diff --git a/DEPS b/DEPS index b9e75de..03ecc72 100644 --- a/DEPS +++ b/DEPS
@@ -276,11 +276,11 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling V8 # and whatever else without interference from each other. - 'src_internal_revision': '7cf9e93279ad7acb591d761935900a49457bbd70', + 'src_internal_revision': '3ce4702c33df16bb3cc2619173d8f80639872707', # 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': 'dba7f46122ba3b1cdb8890eec92aa7b3534781b6', + 'skia_revision': '1398cbd6b7f9af9eca3b0b5277fece4cb33a45ff', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling V8 # and whatever else without interference from each other. @@ -288,7 +288,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': 'c10f5e3fbf61e024ed298700dbc0b5faf888ec5f', + 'angle_revision': 'c289b30f332d55bf0156d7122dac00f1aebe56e0', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling SwiftShader # and whatever else without interference from each other. @@ -352,7 +352,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling chromium_variations # and whatever else without interference from each other. - 'chromium_variations_revision': '9be189a60865bf5fed52f2d6dd76ceb54e231ddb', + 'chromium_variations_revision': '7b74300faa5f3675b06c4f10bfaef33c760918e9', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling CrossBench # and whatever else without interference from each other. @@ -364,7 +364,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling fuzztest # and whatever else without interference from each other. - 'fuzztest_revision': '032f0bdd8c0a3800eb49131d212142a61df81b0c', + 'fuzztest_revision': 'c99c121225fcc175bdc084d83c30f3c806b75afd', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling domato # and whatever else without interference from each other. @@ -372,7 +372,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': 'ed19c1e8985293025be2e812b86ea7619185fcfd', + 'devtools_frontend_revision': '8245ed152847c99e3313079a696637bca1d5bdd7', # 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. @@ -396,7 +396,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': '07cbbfbf05a3b1e9f22b3d0ac3203a5a70c15689', + 'dawn_revision': 'ab9f198d52730b69f4a208c5afd39abb0236f76a', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling feed # and whatever else without interference from each other. @@ -496,7 +496,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling llvm-libc # and whatever else without interference from each other. - 'llvm_libc_revision': '9ee890194fe9d4f39b1d5114c6e291b72e6062dd', + 'llvm_libc_revision': 'cf32ae379c8968df8be7b8b9b1d69115402bccc4', # If you change this, also update the libc++ revision in # //buildtools/deps_revisions.gni. @@ -1439,7 +1439,7 @@ 'src/clank': { 'url': Var('chrome_git') + '/clank/internal/apps.git' + '@' + - 'de102b699470b4d92be37fc98f29f6c668e419f2', + 'dbee4805fa8597c96cfcf0e4f44e77ae9f50901c', 'condition': 'checkout_android and checkout_src_internal', }, @@ -1468,7 +1468,7 @@ }, 'src/ios/third_party/material_components_ios/src': { - 'url': Var('chromium_git') + '/external/github.com/material-components/material-components-ios.git' + '@' + '0ac0dfe5adf41700ef61e3bb0a1ece2362757433', + 'url': Var('chromium_git') + '/external/github.com/material-components/material-components-ios.git' + '@' + '5c9ba055eef03a043b7cf5191de54e1197fee86a', 'condition': 'checkout_ios', }, @@ -1702,7 +1702,7 @@ 'packages': [ { 'package': 'chromium/third_party/android_build_tools/manifest_merger', - 'version': 'T3B_dWqgDISstbC0L7CrQOOf9xe-27KUYK8UCTq6trgC', + 'version': 'UGF3GC1qR9jeALurBm6PNVfQT1TNc-yqDjT4pEpuiYsC', }, ], 'condition': 'checkout_android and non_git_source', @@ -2436,7 +2436,7 @@ Var('pdfium_git') + '/pdfium.git' + '@' + Var('pdfium_revision'), 'src/third_party/perfetto': - Var('android_git') + '/platform/external/perfetto.git' + '@' + '0893e2af69caf8592f6e38f34ccdd4ad6615de9d', + Var('android_git') + '/platform/external/perfetto.git' + '@' + 'e324242074e2e64a65e90a2933afd3ca4413554f', 'src/base/tracing/test/data': { 'bucket': 'perfetto', @@ -2800,13 +2800,13 @@ Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + '450cceb587613ac1469c5a131fac15935c99e0e7', 'src/third_party/webgpu-cts/src': - Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + '18742954f642e134d9080840bdb2a884aad09776', + Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + '973068171048a6ab4c9d0762d5efdae8c2c1c8c4', 'src/third_party/webpagereplay': Var('chromium_git') + '/webpagereplay.git' + '@' + Var('webpagereplay_revision'), 'src/third_party/webrtc': - Var('webrtc_git') + '/src.git' + '@' + '2c96934699b1f75572f1dd6f508e85ab0c96a356', + Var('webrtc_git') + '/src.git' + '@' + '35b67572f28b865e81bdddfc370214c329e2f285', # 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. @@ -4634,7 +4634,7 @@ 'src/ios_internal': { 'url': Var('chrome_git') + '/chrome/ios_internal.git' + '@' + - 'fa89054d303701d661169f5d7cf4e8473dd3fe98', + '5d2fd8e76328068efba5dcac5421f3390b7aa9e7', 'condition': 'checkout_ios and checkout_src_internal', },
diff --git a/ash/accelerators/accelerator_controller_impl.cc b/ash/accelerators/accelerator_controller_impl.cc index 7ba4f5b..5be231a 100644 --- a/ash/accelerators/accelerator_controller_impl.cc +++ b/ash/accelerators/accelerator_controller_impl.cc
@@ -27,6 +27,7 @@ #include "ash/public/cpp/accelerator_actions.h" #include "ash/public/cpp/accelerators.h" #include "ash/public/cpp/debug_delegate.h" +#include "ash/public/mojom/input_device_settings.mojom-shared.h" #include "ash/session/session_controller_impl.h" #include "ash/shell.h" #include "ash/system/input_device_settings/input_device_settings_notification_controller.h" @@ -70,6 +71,8 @@ using ::base::UserMetricsAction; using ::chromeos::WindowStateType; using input_method::InputMethodManager; +using OverviewBasedScreenshotKeyboardType = + AcceleratorControllerImpl::OverviewBasedScreenshotKeyboardType; static_assert(AcceleratorAction::kDesksActivate0 == AcceleratorAction::kDesksActivate1 - 1 && @@ -139,6 +142,49 @@ GetEncodedShortcut(accelerator.modifiers(), accelerator.key_code())); } +void RecordOverviewBasedScreenshotUmaHistogram( + AcceleratorAction action, + const ui::Accelerator& accelerator) { + // Only interested in tracking screenshot related actions. + switch (action) { + case ash::AcceleratorAction::kTakeScreenshot: + case ash::AcceleratorAction::kTakePartialScreenshot: + case ash::AcceleratorAction::kTakeWindowScreenshot: + break; + default: + return; + } + + // Only interested in triggers via the overview key. + if (accelerator.key_code() != ui::VKEY_MEDIA_LAUNCH_APP1) { + return; + } + + const OverviewBasedScreenshotKeyboardType keyboard_type = [&]() { + auto* keyboard_capability = Shell::Get()->keyboard_capability(); + CHECK(keyboard_capability); + + if (!keyboard_capability->IsChromeOSKeyboard( + accelerator.source_device_id())) { + return OverviewBasedScreenshotKeyboardType::kNonChromeOSKeyboard; + } + + if (keyboard_capability->HasTopRowActionKey( + accelerator.source_device_id(), ui::TopRowActionKey::kScreenshot)) { + return OverviewBasedScreenshotKeyboardType:: + kChromeOSKeyboardWithScreenshot; + } else { + return OverviewBasedScreenshotKeyboardType:: + kChromeOSKeyboardWithoutScreenshot; + } + }(); + + base::UmaHistogramEnumeration( + base::StrCat({"Ash.Accelerators.OverviewBasedScreenshot.", + GetAcceleratorActionName(action)}), + keyboard_type); +} + void RecordImeSwitchByAccelerator() { UMA_HISTOGRAM_ENUMERATION("InputMethod.ImeSwitch", ImeSwitchType::kAccelerator); @@ -1707,6 +1753,7 @@ } RecordActionUmaHistogram(action, accelerator); + RecordOverviewBasedScreenshotUmaHistogram(action, accelerator); NotifyActionPerformed(action); // Reset any in progress composition.
diff --git a/ash/accelerators/accelerator_controller_impl.h b/ash/accelerators/accelerator_controller_impl.h index 34652b0..cbea8a5 100644 --- a/ash/accelerators/accelerator_controller_impl.h +++ b/ash/accelerators/accelerator_controller_impl.h
@@ -56,6 +56,16 @@ public AshAcceleratorConfiguration::Observer, public AcceleratorPrefs::Observer { public: + // Used to record the keyboard type which triggers a screenshot action via the + // overview key. Do not reorder values of this enum. + enum class OverviewBasedScreenshotKeyboardType { + kNonChromeOSKeyboard, + kChromeOSKeyboardWithScreenshot, + kChromeOSKeyboardWithoutScreenshot, + kMinValue = kNonChromeOSKeyboard, + kMaxValue = kChromeOSKeyboardWithoutScreenshot, + }; + // TestApi is used for tests to get internal implementation details. class TestApi { public:
diff --git a/ash/accelerators/accelerator_controller_unittest.cc b/ash/accelerators/accelerator_controller_unittest.cc index 27c529c..0bbb840 100644 --- a/ash/accelerators/accelerator_controller_unittest.cc +++ b/ash/accelerators/accelerator_controller_unittest.cc
@@ -105,7 +105,9 @@ #include "ui/display/manager/display_manager.h" #include "ui/display/screen.h" #include "ui/display/test/display_manager_test_api.h" +#include "ui/events/ash/keyboard_capability.h" #include "ui/events/devices/device_data_manager_test_api.h" +#include "ui/events/devices/input_device.h" #include "ui/events/devices/keyboard_device.h" #include "ui/events/event.h" #include "ui/events/event_constants.h" @@ -2646,6 +2648,77 @@ ASSERT_FALSE(message_center()->IsQuietMode()); } +TEST_F(AcceleratorControllerTest, OverviewBasedScreenshotMetric) { + const ui::KeyboardDevice kChromeOSKeyboardWithScreenshot( + 5, ui::INPUT_DEVICE_INTERNAL, "ChromeOSKeyboardWithScreenshot"); + const ui::KeyboardDevice kChromeOSKeyboardWithoutScreenshot( + 10, ui::INPUT_DEVICE_BLUETOOTH, "ChromeOSKeyboardWithoutScreenshot"); + const ui::KeyboardDevice kNonChromeOSKeyboard(15, ui::INPUT_DEVICE_USB, + "NonChromeOSKeyboard"); + + ui::DeviceDataManagerTestApi().SetKeyboardDevices( + {kChromeOSKeyboardWithoutScreenshot, kChromeOSKeyboardWithScreenshot, + kNonChromeOSKeyboard}); + + Shell::Get()->keyboard_capability()->SetKeyboardInfoForTesting( + kChromeOSKeyboardWithScreenshot, + {ui::KeyboardCapability::DeviceType::kDeviceInternalKeyboard, + ui::KeyboardCapability::KeyboardTopRowLayout::kKbdTopRowLayoutCustom, + /*top_row_scan_codes=*/{555}, + /*top_row_action_keys=*/{ui::TopRowActionKey::kScreenshot}}); + Shell::Get()->keyboard_capability()->SetKeyboardInfoForTesting( + kChromeOSKeyboardWithoutScreenshot, + {ui::KeyboardCapability::DeviceType::kDeviceExternalChromeOsKeyboard, + ui::KeyboardCapability::KeyboardTopRowLayout::kKbdTopRowLayoutCustom, + /*top_row_scan_codes=*/{555}, + /*top_row_action_keys=*/{ui::TopRowActionKey::kOverview}}); + Shell::Get()->keyboard_capability()->SetKeyboardInfoForTesting( + kNonChromeOSKeyboard, + {ui::KeyboardCapability::DeviceType::kDeviceExternalGenericKeyboard, + ui::KeyboardCapability::KeyboardTopRowLayout::kKbdTopRowLayout1, + /*top_row_scan_codes=*/{}, + /*top_row_action_keys=*/{ui::TopRowActionKey::kOverview}}); + + ui::KeyEvent partial_screenshot_event( + ui::EventType::kKeyPressed, ui::VKEY_MEDIA_LAUNCH_APP1, + ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN); + + { + base::HistogramTester histogram_tester; + partial_screenshot_event.set_source_device_id( + kChromeOSKeyboardWithScreenshot.id); + controller_->Process(ui::Accelerator(partial_screenshot_event)); + histogram_tester.ExpectUniqueSample( + "Ash.Accelerators.OverviewBasedScreenshot.TakePartialScreenshot", + AcceleratorControllerImpl::OverviewBasedScreenshotKeyboardType:: + kChromeOSKeyboardWithScreenshot, + 1); + } + + { + base::HistogramTester histogram_tester; + partial_screenshot_event.set_source_device_id( + kChromeOSKeyboardWithoutScreenshot.id); + controller_->Process(ui::Accelerator(partial_screenshot_event)); + histogram_tester.ExpectUniqueSample( + "Ash.Accelerators.OverviewBasedScreenshot.TakePartialScreenshot", + AcceleratorControllerImpl::OverviewBasedScreenshotKeyboardType:: + kChromeOSKeyboardWithoutScreenshot, + 1); + } + + { + base::HistogramTester histogram_tester; + partial_screenshot_event.set_source_device_id(kNonChromeOSKeyboard.id); + controller_->Process(ui::Accelerator(partial_screenshot_event)); + histogram_tester.ExpectUniqueSample( + "Ash.Accelerators.OverviewBasedScreenshot.TakePartialScreenshot", + AcceleratorControllerImpl::OverviewBasedScreenshotKeyboardType:: + kNonChromeOSKeyboard, + 1); + } +} + class SystemShortcutBehaviorTest : public AcceleratorControllerTest { void SetUp() override { AcceleratorControllerTest::SetUp();
diff --git a/ash/app_list/views/app_list_folder_view.cc b/ash/app_list/views/app_list_folder_view.cc index 095bd53f..d5aafe2 100644 --- a/ash/app_list/views/app_list_folder_view.cc +++ b/ash/app_list/views/app_list_folder_view.cc
@@ -832,8 +832,6 @@ shown_ = show; UpdateExpandedCollapsedAccessibleState(); if (show) { - // TODO(crbug.com/325137417): Investigate whether this line is necessary. It - // probably isn't. GetViewAccessibility().SetName( folder_item_view_->GetViewAccessibility().GetCachedName(), ax::mojom::NameFrom::kAttribute);
diff --git a/ash/app_list/views/apps_grid_view_unittest.cc b/ash/app_list/views/apps_grid_view_unittest.cc index dd8a5b2b..65d54fa 100644 --- a/ash/app_list/views/apps_grid_view_unittest.cc +++ b/ash/app_list/views/apps_grid_view_unittest.cc
@@ -5178,6 +5178,110 @@ MaybeRunDragAndDropSequenceForAppList(&tasks, /*is_touch =*/false); } +TEST_P(AppsGridViewDragTest, DragPinnedItemToShelf) { + GetTestModel()->PopulateApps(3); + UpdateLayout(); + + auto* const shelf_model = ShelfModel::Get(); + shelf_model->AddAndPinAppWithFactoryConstructedDelegate("Item 1"); + shelf_model->AddAndPinAppWithFactoryConstructedDelegate("Item 2"); + ASSERT_EQ(0, shelf_model->ItemIndexByAppID("Item 1")); + + AppListItemView* const item_view = + GetItemViewInCurrentPageAt(0, 1, apps_grid_view_); + StartDragForViewAndFireTimer(AppsGridView::MOUSE, item_view); + + std::list<base::OnceClosure> tasks; + tasks.push_back(base::BindLambdaForTesting([&]() { + CheckHaptickEventsCount(1); + // Verify that item drag has started. + ASSERT_TRUE(apps_grid_view_->drag_item()); + ASSERT_TRUE(apps_grid_view_->IsDragging()); + ASSERT_EQ(item_view->item(), apps_grid_view_->drag_item()); + })); + tasks.push_back(base::BindLambdaForTesting([&]() { + // 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() + gfx::Vector2d(5, 5), + /*steps=*/1); + + EXPECT_EQ("Item 1", shelf_view->drag_and_drop_shelf_id().app_id); + })); + tasks.push_back(base::BindLambdaForTesting([&]() { + // Move the item towards the end of the shelf, so it becomes the last shelf + // item. + auto* shelf_view = GetPrimaryShelf()->GetShelfViewForTesting(); + const auto& shelf_bounds = shelf_view->GetBoundsInScreen(); + UpdateDragInScreen(AppsGridView::MOUSE, + is_rtl_ + ? shelf_bounds.left_center() + gfx::Vector2d(6, 6) + : shelf_bounds.right_center() - gfx::Vector2d(5, 5), + /*steps=*/2); + + EXPECT_EQ("Item 1", shelf_view->drag_and_drop_shelf_id().app_id); + })); + tasks.push_back(base::BindLambdaForTesting([&]() { EndDrag(); })); + MaybeRunDragAndDropSequenceForAppList(&tasks, /*is_touch =*/false); + + // Releasing drag over shelf should pin the dragged app. + EXPECT_TRUE(shelf_model->IsAppPinned("Item 1")); + EXPECT_EQ(1, shelf_model->ItemIndexByAppID("Item 1")); + EXPECT_EQ(0, shelf_model->ItemIndexByAppID("Item 2")); + CheckHaptickEventsCount(1); +} + +TEST_P(AppsGridViewDragTest, UnpinDraggedItemDuringDragToShelf) { + GetTestModel()->PopulateApps(3); + UpdateLayout(); + + auto* const shelf_model = ShelfModel::Get(); + shelf_model->AddAndPinAppWithFactoryConstructedDelegate("Item 1"); + shelf_model->AddAndPinAppWithFactoryConstructedDelegate("Item 2"); + ASSERT_EQ(0, shelf_model->ItemIndexByAppID("Item 1")); + + AppListItemView* const item_view = + GetItemViewInCurrentPageAt(0, 1, apps_grid_view_); + StartDragForViewAndFireTimer(AppsGridView::MOUSE, item_view); + + std::list<base::OnceClosure> tasks; + tasks.push_back(base::BindLambdaForTesting([&]() { + CheckHaptickEventsCount(1); + // Verify that item drag has started. + ASSERT_TRUE(apps_grid_view_->drag_item()); + ASSERT_TRUE(apps_grid_view_->IsDragging()); + ASSERT_EQ(item_view->item(), apps_grid_view_->drag_item()); + })); + tasks.push_back(base::BindLambdaForTesting([&]() { + // 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() + gfx::Vector2d(5, 5), + /*steps=*/1); + + EXPECT_EQ("Item 1", shelf_view->drag_and_drop_shelf_id().app_id); + })); + tasks.push_back(base::BindLambdaForTesting([&]() { + shelf_model->UnpinAppWithID("Item 1"); + auto* shelf_view = GetPrimaryShelf()->GetShelfViewForTesting(); + UpdateDragInScreen( + AppsGridView::MOUSE, + shelf_view->GetBoundsInScreen().left_center() + gfx::Vector2d(10, 5), + /*steps=*/2); + + EXPECT_EQ("", shelf_view->drag_and_drop_shelf_id().app_id); + EXPECT_FALSE(shelf_model->IsAppPinned("Item 1")); + })); + tasks.push_back(base::BindLambdaForTesting([&]() { EndDrag(); })); + MaybeRunDragAndDropSequenceForAppList(&tasks, /*is_touch =*/false); + + EXPECT_FALSE(shelf_model->IsAppPinned("Item 1")); + EXPECT_EQ(0, shelf_model->ItemIndexByAppID("Item 2")); + CheckHaptickEventsCount(1); +} + TEST_P(AppsGridViewDragTest, MousePointerIsGrabbingDuringDrag) { auto* cursor_manager = Shell::Get()->cursor_manager(); auto previous_cursor_type = cursor_manager->GetCursor().type();
diff --git a/ash/shelf/shelf_view.cc b/ash/shelf/shelf_view.cc index b5f487f..5eaca25 100644 --- a/ash/shelf/shelf_view.cc +++ b/ash/shelf/shelf_view.cc
@@ -1128,12 +1128,13 @@ if (!ShouldHandleDrag(app_id, location_in_screen)) return false; + // If the AppsGridView (which was dispatching this event) was opened by our + // button, ShelfView dragging operations are locked and we have to unlock. + CancelDrag(); + DCHECK(!is_active_drag_and_drop_host_); is_active_drag_and_drop_host_ = true; - // If the AppsGridView (which was dispatching this event) was opened by our - // button, ShelfView dragging operations are locked and we have to unlock. - CancelDrag(std::nullopt); drag_and_drop_item_pinned_ = false; drag_and_drop_shelf_id_ = ShelfID(app_id); // Check if the application is pinned - if not, we have to pin it so @@ -1142,9 +1143,7 @@ if (!model_->IsAppPinned(app_id)) { ShelfModel::ScopedUserTriggeredMutation user_triggered(model_); - if (model_->ItemIndexByAppID(app_id) >= 0) { - model_->PinExistingItemWithID(app_id); - } else { + if (model_->ItemIndexByAppID(app_id) < 0) { model_->AddAndPinAppWithFactoryConstructedDelegate(app_id); drag_and_drop_item_pinned_ = true; } @@ -1157,7 +1156,7 @@ // Since there is already an icon presented by the caller, we hide this item // for now. That has to be done by reducing the size since the visibility will // change once a regrouping animation is performed. - pre_drag_and_drop_size_ = drag_and_drop_view->size(); + // The size will be restored to ideal bounds in `EndDrag()`. drag_and_drop_view->SetSize(gfx::Size()); // First we have to center the mouse cursor over the item. @@ -1185,8 +1184,8 @@ drag_icon_bounds_in_screen_ = drag_icon_bounds_in_screen; gfx::Point pt = location_in_screen; - views::View* drag_and_drop_view = - view_model_->view_at(model_->ItemIndexByID(drag_and_drop_shelf_id_)); + const int item_index = model_->ItemIndexByID(drag_and_drop_shelf_id_); + views::View* drag_and_drop_view = view_model_->view_at(item_index); ConvertPointFromScreen(drag_and_drop_view, &pt); gfx::Point point_in_root = location_in_screen; wm::ConvertPointFromScreen(window_util::GetRootWindowAt(location_in_screen), @@ -1198,24 +1197,23 @@ } void ShelfView::EndDrag(bool cancel) { - drag_scroll_dir_ = 0; - scrolling_timer_.Stop(); - speed_up_drag_scrolling_.Stop(); - if (drag_and_drop_shelf_id_.IsNull()) { - is_active_drag_and_drop_host_ = false; + ClearDragState(); return; } + // `PointerReleasedOnButton()` clears drag state, so cache parts of the state + // needed later on. + const auto item_id = drag_and_drop_shelf_id_; views::View* drag_and_drop_view = - view_model_->view_at(model_->ItemIndexByID(drag_and_drop_shelf_id_)); + view_model_->view_at(model_->ItemIndexByID(item_id)); PointerReleasedOnButton(drag_and_drop_view, DRAG_AND_DROP, cancel); - // Either destroy the temporarily created item - or - make the item visible. - if (drag_and_drop_item_pinned_ && cancel) { - ShelfModel::ScopedUserTriggeredMutation user_triggered(model_); - model_->UnpinAppWithID(drag_and_drop_shelf_id_.app_id); - } else if (drag_and_drop_view) { + // Animate drag and drop view to visible if it hasn't been removed by + // `PointerReleasedOnButton()`. + const int item_index = model_->ItemIndexByID(item_id); + if (item_index >= 0) { + drag_and_drop_view = view_model_->view_at(item_index); std::unique_ptr<gfx::AnimationDelegate> animation_delegate; // Resets the dragged view's opacity at the end of drag. Otherwise, if @@ -1237,12 +1235,12 @@ } } else { - drag_and_drop_view->SetSize(pre_drag_and_drop_size_); + // Restore drag and drop view size, which was cleared in `StartDrag` to + // hide the item view. + const int button_size = GetButtonSize(); + drag_and_drop_view->SetSize(gfx::Size(button_size, button_size)); } } - drag_icon_bounds_in_screen_ = gfx::Rect(); - drag_and_drop_shelf_id_ = ShelfID(); - is_active_drag_and_drop_host_ = false; } void ShelfView::SwapButtons(views::View* button_to_swap, bool with_next) { @@ -1319,31 +1317,31 @@ void ShelfView::PointerReleasedOnButton(const views::View* view, Pointer pointer, bool canceled) { - drag_scroll_dir_ = 0; - scrolling_timer_.Stop(); - speed_up_drag_scrolling_.Stop(); - is_repost_event_on_same_item_ = false; - if (canceled) { - CancelDrag(std::nullopt); + CancelDrag(); } else if (drag_pointer_ == pointer) { + // `dragged_off_shelf_` gets reset in in `FinalizeRipOffDrag()` - cache the + // value so it can be used to decide whether to update dragged view pin + // status. + const bool was_dragged_off = dragged_off_shelf_; FinalizeRipOffDrag(false); drag_pointer_ = NONE; // Check if the pin status of |drag_view_| should be changed when - // |drag_view_| is dragged over the separator. Do nothing if |drag_view_| is - // already handled in FinalizedRipOffDrag. - if (drag_view_) { - if (ShouldUpdateDraggedViewPinStatus( - view_model_->GetIndexOfView(view).value())) { - const std::string drag_app_id = ShelfItemForView(drag_view_)->id.app_id; - ShelfModel::ScopedUserTriggeredMutation user_triggered(model_); - if (model_->IsAppPinned(drag_app_id)) { - model_->UnpinAppWithID(drag_app_id); - } else { - model_->PinExistingItemWithID(drag_app_id); - } + // |drag_view_| is dragged over the separator. Keep the current pin state + // if the view has been dragged off the shelf - `FinalizeRipOffDrag()` + // should have already updated the item pin state appropriately in this + // case. + if (drag_view_ && !was_dragged_off && + ShouldUpdateDraggedViewPinStatus( + view_model_->GetIndexOfView(view).value())) { + const std::string drag_app_id = ShelfItemForView(drag_view_)->id.app_id; + ShelfModel::ScopedUserTriggeredMutation user_triggered(model_); + if (model_->IsAppPinned(drag_app_id)) { + model_->UnpinAppWithID(drag_app_id); + } else { + model_->PinExistingItemWithID(drag_app_id); } } AnimateToIdealBounds(); @@ -1372,9 +1370,7 @@ // If the drag pointer is NONE, no drag operation is going on and the // |drag_view_| can be released. - drag_view_ = nullptr; - drag_view_relative_to_ideal_bounds_ = RelativePosition::kNotAvailable; - RemoveGhostView(); + ClearDragState(); } void ShelfView::AnimateDragImageLayer( @@ -1573,10 +1569,9 @@ DCHECK(drag_view_); drag_pointer_ = pointer; start_drag_index_ = view_model_->GetIndexOfView(drag_view_); - drag_scroll_dir_ = 0; if (!start_drag_index_.has_value()) { - CancelDrag(std::nullopt); + CancelDrag(); return; } @@ -1638,9 +1633,6 @@ HandleRipOffDrag(event); // Check if the item got ripped off the shelf - if it did we are done. if (dragged_off_shelf_) { - drag_scroll_dir_ = 0; - scrolling_timer_.Stop(); - speed_up_drag_scrolling_.Stop(); if (!dragged_off_shelf_before) model_->OnItemRippedOff(); return; @@ -1857,27 +1849,31 @@ drag_view_->SetVisible(false); ShelfModel::ScopedUserTriggeredMutation user_triggered(model_); model_->UnpinAppWithID(model_->items()[current_index.value()].id.app_id); + // Show the view if unpinning the app does not remove it from shelf (e.g. + // if the app ripped of shelf has an open instance). + if (drag_view_) { + drag_view_->SetVisible(true); + } } } + if (cancel || snap_back) { - if (!cancelling_drag_model_changed_) { - // Only do something if the change did not come through a model change. - gfx::Rect drag_bounds = drag_icon_proxy_->GetBoundsInScreen(); - gfx::Point relative_to = GetBoundsInScreen().origin(); - gfx::Rect target( - gfx::PointAtOffsetFromOrigin(drag_bounds.origin() - relative_to), - drag_bounds.size()); - drag_view_->SetBoundsRect(target); - // Hide the status from the active item since we snap it back now. Upon - // animation end the flag gets cleared if |snap_back_from_rip_off_view_| - // is set. - snap_back_from_rip_off_view_ = drag_view_; - drag_view_->AddState(ShelfAppButton::STATE_HIDDEN); - // When a canceling drag model is happening, the view model is diverged - // from the menu model and movements / animations should not be done. - model_->Move(current_index.value(), start_drag_index_.value()); - AnimateToIdealBounds(); - } + // Only do something if the change did not come through a model change. + gfx::Rect drag_bounds = drag_icon_proxy_->GetBoundsInScreen(); + gfx::Point relative_to = GetBoundsInScreen().origin(); + gfx::Rect target( + gfx::PointAtOffsetFromOrigin(drag_bounds.origin() - relative_to), + drag_bounds.size()); + drag_view_->SetBoundsRect(target); + // Hide the status from the active item since we snap it back now. Upon + // animation end the flag gets cleared if |snap_back_from_rip_off_view_| + // is set. + snap_back_from_rip_off_view_ = drag_view_; + drag_view_->AddState(ShelfAppButton::STATE_HIDDEN); + // When a canceling drag model is happening, the view model is diverged + // from the menu model and movements / animations should not be done. + model_->Move(current_index.value(), start_drag_index_.value()); + AnimateToIdealBounds(); drag_view_->layer()->SetOpacity(1.0f); model_->OnItemReturnedFromRipOff(model_->item_count() - 1); } @@ -1980,11 +1976,8 @@ // Only unpinned running apps on shelf can be dragged across the separator to // pin. bool can_change_pin_state = ShelfItemForView(drag_view)->type == TYPE_APP; - - // Note that |drag_and_drop_shelf_id_| is set only when the current drag view - // is from app list, which can not be dragged to the unpinned app side. return !ShelfItemForView(drag_view)->IsPinStateForced() && - drag_and_drop_shelf_id_ == ShelfID() && can_change_pin_state; + can_change_pin_state; } void ShelfView::OnFadeInAnimationEnded() { @@ -2129,45 +2122,42 @@ return bounds; } -std::optional<size_t> ShelfView::CancelDrag( - std::optional<size_t> modified_index) { - drag_scroll_dir_ = 0; - scrolling_timer_.Stop(); - speed_up_drag_scrolling_.Stop(); - +void ShelfView::CancelDrag() { FinalizeRipOffDrag(true); - delegate_->CancelScrollForItemDrag(); - drag_icon_proxy_.reset(); - drag_image_layer_.reset(); + if (drag_view_) { + auto drag_view_index = view_model_->GetIndexOfView(drag_view_); + drag_view_ = nullptr; - if (!drag_view_) - return modified_index; - bool was_dragging = dragging(); - auto drag_view_index = view_model_->GetIndexOfView(drag_view_); + if (drag_and_drop_item_pinned_) { + ShelfModel::ScopedUserTriggeredMutation user_triggered(model_); + model_->UnpinAppWithID(drag_and_drop_shelf_id_.app_id); + } else { + model_->Move(drag_view_index.value(), start_drag_index_.value()); + } + } + + ClearDragState(); +} + +void ShelfView::ClearDragState() { drag_pointer_ = NONE; drag_view_ = nullptr; - if (drag_view_index == modified_index) { - // The view that was being dragged is being modified. Don't do anything. - return modified_index; - } - if (!was_dragging) - return modified_index; + start_drag_index_.reset(); + drag_view_relative_to_ideal_bounds_ = RelativePosition::kNotAvailable; + drag_icon_bounds_in_screen_ = gfx::Rect(); - // Restore previous position, tracking the position of the modified view. - bool at_end = modified_index == view_model_->view_size(); - views::View* modified_view = - (modified_index.has_value() && !at_end) - ? view_model_->view_at(modified_index.value()) - : nullptr; - model_->Move(drag_view_index.value(), start_drag_index_.value()); + drag_icon_proxy_.reset(); + drag_image_layer_.reset(); + dragged_off_shelf_ = false; - // If the modified view will be at the end of the list, return the new end of - // the list. - if (at_end) - return view_model_->view_size(); - return modified_view ? view_model_->GetIndexOfView(modified_view) - : std::nullopt; + delegate_->CancelScrollForItemDrag(); + + drag_and_drop_shelf_id_ = ShelfID(); + drag_and_drop_item_pinned_ = false; + is_active_drag_and_drop_host_ = false; + + RemoveGhostView(); } void ShelfView::OnGestureEvent(ui::GestureEvent* event) { @@ -2220,11 +2210,10 @@ const ShelfItem& item(model_->items()[model_index]); views::View* view = CreateViewForItem(item); - { - base::AutoReset<bool> cancelling_drag(&cancelling_drag_model_changed_, - true); - model_index = static_cast<int>(CancelDrag(model_index).value()); + if (start_drag_index_ >= 0 && model_index <= start_drag_index_) { + start_drag_index_ = *start_drag_index_ + 1; } + view_model_->Add(view, static_cast<size_t>(model_index)); // If |item| is pinned and the mutation is user-triggered, report the pinning @@ -2316,7 +2305,6 @@ // If std::move is not called on |view|, |view| will be deleted once out of // scope. std::unique_ptr<views::View> view(view_model_->view_at(model_index)); - shelf_button_delegate_->OnButtonWillBeRemoved(); if (old_item.is_promise_app) { @@ -2325,18 +2313,20 @@ } view_model_->Remove(model_index); + if (drag_view_ == view.get()) { + ClearDragState(); + } else { + if (start_drag_index_ >= 0 && model_index < start_drag_index_) { + start_drag_index_ = *start_drag_index_ - 1; + } + } + if (old_item.id == context_menu_id_ && shelf_menu_model_adapter_) shelf_menu_model_adapter_->Cancel(); if (old_item.id == item_awaiting_response_) ResetActiveMenuModelRequest(); - { - base::AutoReset<bool> cancelling_drag(&cancelling_drag_model_changed_, - true); - CancelDrag(std::nullopt); - } - if (view.get() == shelf_->tooltip()->GetCurrentAnchorView()) shelf_->tooltip()->Close(); @@ -2408,41 +2398,6 @@ &ShelfView::ShelfItemsUpdatedForDeskChange, base::Unretained(this))); } - if (old_item.type != item.type) { - // Type changed, swap the views. - model_index = static_cast<int>(CancelDrag(model_index).value()); - std::unique_ptr<views::View> old_view(view_model_->view_at(model_index)); - bounds_animator_->StopAnimatingView(old_view.get()); - // Removing and re-inserting a view in our view model will strip the ideal - // bounds from the item. To avoid recalculation of everything the bounds - // get remembered and restored after the insertion to the previous value. - gfx::Rect old_ideal_bounds = view_model_->ideal_bounds(model_index); - view_model_->Remove(model_index); - views::View* new_view = CreateViewForItem(item); - // The view must be added to the |view_model_| before it's added as a child - // so that the model is consistent when UpdateShelfItemViewsVisibility() is - // called as a result the hierarchy changes caused by AddChildView(). See - // ScrollableShelfView::ViewHierarchyChanged(). - view_model_->Add(new_view, model_index); - AddChildView(new_view); - view_model_->set_ideal_bounds(model_index, old_ideal_bounds); - - bounds_animator_->StopAnimatingView(new_view); - new_view->SetBoundsRect(old_view->bounds()); - bounds_animator_->AnimateViewTo(new_view, old_ideal_bounds); - - // If an item is being pinned or unpinned, show the new status of the - // shelf immediately so that the separator gets drawn as needed. - if (old_item.type == TYPE_PINNED_APP || item.type == TYPE_PINNED_APP) { - if (model_->is_current_mutation_user_triggered()) { - AnnouncePinUnpinEvent(old_item, item.type == TYPE_PINNED_APP); - RecordPinUnpinUserAction(item.type == TYPE_PINNED_APP); - } - AnimateToIdealBounds(); - } - return; - } - views::View* view = view_model_->view_at(model_index); switch (item.type) { case TYPE_PINNED_APP: @@ -2458,6 +2413,18 @@ case TYPE_UNDEFINED: break; } + + if (old_item.type != item.type) { + // If an item is being pinned or unpinned, show the new status of the + // shelf immediately so that the separator gets drawn as needed. + if (old_item.type == TYPE_PINNED_APP || item.type == TYPE_PINNED_APP) { + if (model_->is_current_mutation_user_triggered()) { + AnnouncePinUnpinEvent(old_item, item.type == TYPE_PINNED_APP); + RecordPinUnpinUserAction(item.type == TYPE_PINNED_APP); + } + AnimateToIdealBounds(); + } + } } void ShelfView::ShelfItemsUpdatedForDeskChange() { @@ -2475,6 +2442,13 @@ } void ShelfView::ShelfItemMoved(int start_index, int target_index) { + if (start_drag_index_ > start_index && start_drag_index_ < target_index) { + start_drag_index_ = *start_drag_index_ - 1; + } else if (start_drag_index_ > target_index && + start_drag_index_ < start_index) { + start_drag_index_ = *start_drag_index_ + 1; + } + view_model_->Move(start_index, target_index); // Reorder the child view to be in the same order as in the |view_model_|. @@ -2482,12 +2456,7 @@ NotifyAccessibilityEvent(ax::mojom::Event::kChildrenChanged, true /* send_native_event */); - // When cancelling a drag due to a shelf item being added, the currently - // dragged item is moved back to its initial position. AnimateToIdealBounds - // will be called again when the new item is added to the |view_model_| but - // at this time the |view_model_| is inconsistent with the |model_|. - if (!cancelling_drag_model_changed_) - AnimateToIdealBounds(); + AnimateToIdealBounds(); } void ShelfView::ShelfItemDelegateChanged(const ShelfID& id,
diff --git a/ash/shelf/shelf_view.h b/ash/shelf/shelf_view.h index f3ec7af..c16fa689 100644 --- a/ash/shelf/shelf_view.h +++ b/ash/shelf/shelf_view.h
@@ -472,9 +472,12 @@ // exists. bool CanDragAcrossSeparator(views::View* dragged_view) const; - // If there is a drag operation in progress it's canceled. If |modified_index| - // is valid, the new position of the corresponding item is returned. - std::optional<size_t> CancelDrag(std::optional<size_t> modified_index); + // If there is a drag operation in progress it's canceled. + void CancelDrag(); + + // Resets state set for handling drag interactions, including removing ghost + // views. + void ClearDragState(); // Returns rectangle bounds used for drag insertion. gfx::Rect GetBoundsForDragInsertInScreen(); @@ -692,9 +695,6 @@ std::unique_ptr<display::ScopedDisplayForNewWindows> scoped_display_for_new_windows_; - // True when an item being inserted or removed in the model cancels a drag. - bool cancelling_drag_model_changed_ = false; - // The item with an in-flight async request for a context menu or selection // (which shows a shelf item application menu if multiple windows are open). // Used to avoid multiple concurrent menu requests. The value is null if none. @@ -720,9 +720,6 @@ // `ApplicationDragAndDropHost` interface. gfx::Rect drag_icon_bounds_in_screen_; - // The original launcher item's size before the dragging operation. - gfx::Size pre_drag_and_drop_size_; - // True when the icon was dragged off the shelf. bool dragged_off_shelf_ = false; @@ -749,15 +746,6 @@ // alignment or auto-hide state). raw_ptr<views::View> announcement_view_ = nullptr; // Owned by ShelfView - // For dragging: -1 if scrolling back, 1 if scrolling forward, 0 if neither. - int drag_scroll_dir_ = 0; - - // Used to periodically call ScrollForUserDrag. - base::RepeatingTimer scrolling_timer_; - - // Used to call SpeedUpDragScrolling. - base::OneShotTimer speed_up_drag_scrolling_; - // Whether this view should focus its last focusable child (instead of its // first) when focused. bool default_last_focusable_child_ = false;
diff --git a/ash/shelf/shelf_view_unittest.cc b/ash/shelf/shelf_view_unittest.cc index 98bca4f..90b6cf7 100644 --- a/ash/shelf/shelf_view_unittest.cc +++ b/ash/shelf/shelf_view_unittest.cc
@@ -554,15 +554,17 @@ void CheckModelIDs( const std::vector<std::pair<ShelfID, views::View*>>& id_map) { - size_t map_index = 0; - for (size_t model_index = 0; model_index < model_->items().size(); - ++model_index) { - ShelfItem item = model_->items()[model_index]; - ShelfID id = item.id; - EXPECT_EQ(id_map[map_index].first, id); - ++map_index; + std::vector<ShelfID> expected; + for (const auto& item : id_map) { + expected.push_back(item.first); } - ASSERT_EQ(map_index, id_map.size()); + + std::vector<ShelfID> actual; + for (const auto& item : model_->items()) { + actual.push_back(item.id); + } + + EXPECT_EQ(expected, actual); } void ExpectHelpBubbleAnchorBoundsChangedEvent( @@ -933,30 +935,113 @@ ASSERT_NO_FATAL_FAILURE(CheckModelIDs(id_map)); // Deleting an item keeps the remaining intact. - dragged_button = SimulateDrag(ShelfView::MOUSE, 0, 2, false); + dragged_button = SimulateDrag(ShelfView::MOUSE, 3, 2, false); EXPECT_EQ(3, GetHapticTickEventsCount()); // The dragged view has been moved to index 2 during drag. - std::rotate(id_map.begin(), id_map.begin() + 1, id_map.begin() + 3); + std::rotate(id_map.begin() + 2, id_map.begin() + 3, id_map.begin() + 4); ASSERT_NO_FATAL_FAILURE(CheckModelIDs(id_map)); - model_->RemoveItemAt(2); - id_map.erase(id_map.begin() + 2); + model_->RemoveItemAt(0); + id_map.erase(id_map.begin()); ASSERT_NO_FATAL_FAILURE(CheckModelIDs(id_map)); - shelf_view_->PointerReleasedOnButton(dragged_button, ShelfView::MOUSE, false); + // Cancel drag, and verify that the model state has been correctly reset. + shelf_view_->PointerReleasedOnButton(dragged_button, ShelfView::MOUSE, true); + + std::rotate(id_map.begin() + 1, id_map.begin() + 2, id_map.begin() + 3); + ASSERT_NO_FATAL_FAILURE(CheckModelIDs(id_map)); // Waits until app removal animation finishes. test_api_->RunMessageLoopUntilAnimationsDone(); - // Adding a shelf item cancels the drag and respects the order. dragged_button = SimulateDrag(ShelfView::MOUSE, 0, 2, false); EXPECT_EQ(4, GetHapticTickEventsCount()); + std::rotate(id_map.begin(), id_map.begin() + 1, id_map.begin() + 3); ShelfID new_id = AddAppShortcut(); id_map.insert(id_map.begin() + 5, std::make_pair(new_id, GetButtonByID(new_id))); ASSERT_NO_FATAL_FAILURE(CheckModelIDs(id_map)); shelf_view_->PointerReleasedOnButton(dragged_button, ShelfView::MOUSE, false); + ASSERT_NO_FATAL_FAILURE(CheckModelIDs(id_map)); EXPECT_EQ(4, GetHapticTickEventsCount()); + + test_api_->RunMessageLoopUntilAnimationsDone(); + + // Test removing dragged item mid drag. + dragged_button = SimulateDrag(ShelfView::MOUSE, 1, 2, false); + EXPECT_EQ(5, GetHapticTickEventsCount()); + std::rotate(id_map.begin() + 1, id_map.begin() + 2, id_map.begin() + 3); + ASSERT_NO_FATAL_FAILURE(CheckModelIDs(id_map)); + + model_->RemoveItemAt(2); + id_map.erase(id_map.begin() + 2); + ASSERT_NO_FATAL_FAILURE(CheckModelIDs(id_map)); + + shelf_view_->PointerReleasedOnButton(dragged_button, ShelfView::MOUSE, false); + ASSERT_NO_FATAL_FAILURE(CheckModelIDs(id_map)); + EXPECT_EQ(5, GetHapticTickEventsCount()); + + test_api_->RunMessageLoopUntilAnimationsDone(); + + // Test drag cancellation after adding an item before the dragged item. + dragged_button = SimulateDrag(ShelfView::MOUSE, 1, 2, false); + EXPECT_EQ(6, GetHapticTickEventsCount()); + std::rotate(id_map.begin() + 1, id_map.begin() + 2, id_map.begin() + 3); + ASSERT_NO_FATAL_FAILURE(CheckModelIDs(id_map)); + + ShelfItem prepend_item; + new_id = ShelfID("-1"); + prepend_item.id = new_id; + prepend_item.type = TYPE_PINNED_APP; + ShelfModel::Get()->AddAt( + 0, prepend_item, + std::make_unique<TestShelfItemDelegate>(prepend_item.id)); + id_map.insert(id_map.begin(), std::make_pair(new_id, GetButtonByID(new_id))); + ASSERT_NO_FATAL_FAILURE(CheckModelIDs(id_map)); + + shelf_view_->PointerReleasedOnButton(dragged_button, ShelfView::MOUSE, true); + + std::rotate(id_map.begin() + 2, id_map.begin() + 3, id_map.begin() + 4); + ASSERT_NO_FATAL_FAILURE(CheckModelIDs(id_map)); + EXPECT_EQ(6, GetHapticTickEventsCount()); +} + +// Check that model changes are handled correctly while a shelf icon is being +// dragged. +TEST_P(LtrRtlShelfViewTest, MovesInModelWhileDragging) { + std::vector<std::pair<ShelfID, views::View*>> id_map; + SetupForDragTest(&id_map); + + views::View* dragged_button = SimulateDrag(ShelfView::MOUSE, 2, 4, false); + EXPECT_EQ(1, GetHapticTickEventsCount()); + std::rotate(id_map.begin() + 2, id_map.begin() + 3, id_map.begin() + 5); + ASSERT_NO_FATAL_FAILURE(CheckModelIDs(id_map)); + + ShelfModel::Get()->Move(1, 3); + std::rotate(id_map.begin() + 1, id_map.begin() + 2, id_map.begin() + 4); + ASSERT_NO_FATAL_FAILURE(CheckModelIDs(id_map)); + + shelf_view_->PointerReleasedOnButton(dragged_button, ShelfView::MOUSE, true); + EXPECT_EQ(1, GetHapticTickEventsCount()); + + std::rotate(id_map.begin() + 1, id_map.begin() + 4, id_map.begin() + 5); + ASSERT_NO_FATAL_FAILURE(CheckModelIDs(id_map)); + test_api_->RunMessageLoopUntilAnimationsDone(); + + dragged_button = SimulateDrag(ShelfView::MOUSE, 2, 4, false); + EXPECT_EQ(2, GetHapticTickEventsCount()); + std::rotate(id_map.begin() + 2, id_map.begin() + 3, id_map.begin() + 5); + ASSERT_NO_FATAL_FAILURE(CheckModelIDs(id_map)); + + ShelfModel::Get()->Move(3, 1); + std::rotate(id_map.begin() + 1, id_map.begin() + 3, id_map.begin() + 4); + ASSERT_NO_FATAL_FAILURE(CheckModelIDs(id_map)); + + shelf_view_->PointerReleasedOnButton(dragged_button, ShelfView::MOUSE, true); + EXPECT_EQ(2, GetHapticTickEventsCount()); + + std::rotate(id_map.begin() + 3, id_map.begin() + 4, id_map.begin() + 5); + ASSERT_NO_FATAL_FAILURE(CheckModelIDs(id_map)); } // Check that 2nd drag from the other pointer would be ignored.
diff --git a/ash/system/holding_space/pinned_files_section.cc b/ash/system/holding_space/pinned_files_section.cc index 7167c26..c1a9487 100644 --- a/ash/system/holding_space/pinned_files_section.cc +++ b/ash/system/holding_space/pinned_files_section.cc
@@ -147,8 +147,8 @@ // Icon. auto* icon = AddChildView(std::make_unique<views::ImageView>()); - icon->SetImage(gfx::CreateVectorIcon(kFilesAppIcon, kFilesAppChipIconSize, - gfx::kPlaceholderColor)); + icon->SetImage(ui::ImageModel::FromVectorIcon( + kFilesAppIcon, gfx::kPlaceholderColor, kFilesAppChipIconSize)); // Label. auto* label =
diff --git a/ash/system/phonehub/continue_browsing_chip.cc b/ash/system/phonehub/continue_browsing_chip.cc index 1b7dc79..4e06321 100644 --- a/ash/system/phonehub/continue_browsing_chip.cc +++ b/ash/system/phonehub/continue_browsing_chip.cc
@@ -89,12 +89,12 @@ favicon->SetImageSize(kContinueBrowsingChipFaviconSize); if (metadata.favicon.IsEmpty()) { - favicon->SetImage(CreateVectorIcon( + favicon->SetImage(ui::ImageModel::FromVectorIcon( kPhoneHubDefaultFaviconIcon, AshColorProvider::Get()->GetContentLayerColor( AshColorProvider::ContentLayerType::kIconColorPrimary))); } else { - favicon->SetImage(metadata.favicon.AsImageSkia()); + favicon->SetImage(ui::ImageModel::FromImage(metadata.favicon)); } auto* url_label = header_view->AddChildView(
diff --git a/ash/system/phonehub/phone_hub_app_icon.cc b/ash/system/phonehub/phone_hub_app_icon.cc index 41aa3a0..340b63c 100644 --- a/ash/system/phonehub/phone_hub_app_icon.cc +++ b/ash/system/phonehub/phone_hub_app_icon.cc
@@ -10,9 +10,10 @@ namespace ash { AppIcon::AppIcon(const gfx::Image& icon, int size) { - SetImage(gfx::ImageSkiaOperations::CreateResizedImage( - icon.AsImageSkia(), skia::ImageOperations::RESIZE_BEST, - gfx::Size(size, size))); + SetImage(ui::ImageModel::FromImageSkia( + gfx::ImageSkiaOperations::CreateResizedImage( + icon.AsImageSkia(), skia::ImageOperations::RESIZE_BEST, + gfx::Size(size, size)))); } BEGIN_METADATA(AppIcon)
diff --git a/ash/system/phonehub/phone_status_view.cc b/ash/system/phonehub/phone_status_view.cc index 5922f3c..3c9c183 100644 --- a/ash/system/phonehub/phone_status_view.cc +++ b/ash/system/phonehub/phone_status_view.cc
@@ -227,7 +227,7 @@ break; } - signal_icon_->SetImage(signal_image); + signal_icon_->SetImage(ui::ImageModel::FromImageSkia(signal_image)); signal_icon_->SetTooltipText(tooltip_text); } @@ -240,9 +240,10 @@ ? AshColorProvider::ContentLayerType::kIconColorWarning : AshColorProvider::ContentLayerType::kIconColorPrimary); - battery_icon_->SetImage(PowerStatus::GetBatteryImage( - CalculateBatteryInfo(icon_fg_color), kUnifiedTrayBatteryIconSize, - battery_icon_->GetColorProvider())); + battery_icon_->SetImage( + ui::ImageModel::FromImageSkia(PowerStatus::GetBatteryImage( + CalculateBatteryInfo(icon_fg_color), kUnifiedTrayBatteryIconSize, + battery_icon_->GetColorProvider()))); SetBatteryTooltipText(); battery_label_->SetText( base::FormatPercent(phone_status.battery_percentage())); @@ -318,10 +319,10 @@ void PhoneStatusView::ClearExistingStatus() { // Clear mobile status. - signal_icon_->SetImage(gfx::ImageSkia()); + signal_icon_->SetImage(ui::ImageModel()); // Clear battery status. - battery_icon_->SetImage(gfx::ImageSkia()); + battery_icon_->SetImage(ui::ImageModel()); battery_label_->SetText(std::u16string()); // TODO(b/281844561): When the phone is disconnected the |phone_name_label_|
diff --git a/ash/webui/boca_ui/resources/app/client_delegate.ts b/ash/webui/boca_ui/resources/app/client_delegate.ts index 481a7e1..07fdbdb 100644 --- a/ash/webui/boca_ui/resources/app/client_delegate.ts +++ b/ash/webui/boca_ui/resources/app/client_delegate.ts
@@ -40,9 +40,7 @@ return { sessionDurationInMinutes: Number(session.sessionDuration.microseconds / MICRO_SECS_IN_MINUTES), - sessionStartTime: session.sessionStartTime?.msec ? - new Date(session.sessionStartTime.msec) : - undefined, + sessionStartTime: session.sessionStartTime || undefined, teacher: session.teacher ? { id: session.teacher.id, name: session.teacher.name,
diff --git a/ash/webui/focus_mode/resources/app.ts b/ash/webui/focus_mode/resources/app.ts index 10bd1d6f..df323e8 100644 --- a/ash/webui/focus_mode/resources/app.ts +++ b/ash/webui/focus_mode/resources/app.ts
@@ -100,15 +100,13 @@ state: getPlaybackState(newPlaybackStatus.state), title: currentTrack.title, url: currentTrack.mediaUrl, - clientCurrentTime: {msec: clientCurrentTime.getTime()}, + clientCurrentTime: clientCurrentTime, playbackStartOffset: playbackStartOffset, mediaTimeCurrent: newPlaybackStatus.position, mediaStart: start, mediaEnd: end, - clientStartTime: { - msec: (initial ? newPlaybackStatus.loadTime : clientTimeLastReport) - .getTime(), - }, + clientStartTime: initial ? newPlaybackStatus.loadTime : + clientTimeLastReport, initialPlayback: initial, }); playbackStatus = newPlaybackStatus;
diff --git a/base/BUILD.gn b/base/BUILD.gn index 24d7eb9..a6085be0 100644 --- a/base/BUILD.gn +++ b/base/BUILD.gn
@@ -4245,7 +4245,6 @@ "sequence_checker_nocompile.nc", "strings/cstring_view_nocompile.nc", "strings/span_printf_nocompile.nc", - "strings/stringprintf_nocompile.nc", "synchronization/lock_nocompile.nc", "task/bind_post_task_nocompile.nc", "task/task_traits_nocompile.nc",
diff --git a/base/metrics/histogram_macros.h b/base/metrics/histogram_macros.h index c527bbb..5a1d37e 100644 --- a/base/metrics/histogram_macros.h +++ b/base/metrics/histogram_macros.h
@@ -423,7 +423,8 @@ constant_histogram_name, index, constant_maximum, \ histogram_add_method_invocation, histogram_factory_get_invocation) \ do { \ - static std::atomic_uintptr_t atomic_histograms[constant_maximum]; \ + static std::array<std::atomic_uintptr_t, constant_maximum> \ + atomic_histograms; \ DCHECK_LE(0, index); \ DCHECK_LT(index, constant_maximum); \ HISTOGRAM_POINTER_USE( \
diff --git a/base/strings/stringprintf.h b/base/strings/stringprintf.h index 7de6f91..81a70dc 100644 --- a/base/strings/stringprintf.h +++ b/base/strings/stringprintf.h
@@ -26,42 +26,6 @@ const Args&... args) { return absl::StrFormat(format, args...); } -// Returns a C++ string given `printf()`-like input. The format string must be a -// run-time value (like with `std::vformat()`), or this will not compile. -// Because this does not check arguments at compile-time, prefer -// `StringPrintf()` whenever possible. -template <typename... Args> -[[nodiscard]] std::string StringPrintfNonConstexpr(std::string_view format, - const Args&... args) { - std::string output; - CHECK(absl::FormatUntyped(&output, absl::UntypedFormatSpec(format), - {absl::FormatArg(args)...})); - return output; -} - -// If possible, guide users to use `StringPrintf()` instead of -// `StringPrintfNonConstexpr()` when the format string is constexpr. -// -// It would be nice to do this with `std::enable_if`, but I don't know of a way; -// whether a string constant's value is available at compile time is not -// something easily obtained from the type system, and trying to pass various -// forms of string constant to non-type template parameters produces a variety -// of compile errors. -#if HAS_ATTRIBUTE(enable_if) -// Disable calling with a constexpr `std::string_view`. -template <typename... Args> -[[nodiscard]] std::string StringPrintfNonConstexpr(std::string_view format, - const Args&... args) - ENABLE_IF_ATTR( - [](std::string_view s) { return s.empty() || s[0] == s[0]; }(format), - "Use StringPrintf() for constexpr format strings") = delete; -// Disable calling with a constexpr `char[]` or `char*`. -template <typename... Args> -[[nodiscard]] std::string StringPrintfNonConstexpr(const char* format, - const Args&... args) - ENABLE_IF_ATTR([](const char* s) { return !!s; }(format), - "Use StringPrintf() for constexpr format strings") = delete; -#endif // Returns a C++ string given `vprintf()`-like input. [[nodiscard]] PRINTF_FORMAT(1, 0) BASE_EXPORT std::string
diff --git a/base/strings/stringprintf_nocompile.nc b/base/strings/stringprintf_nocompile.nc deleted file mode 100644 index 7166ec9..0000000 --- a/base/strings/stringprintf_nocompile.nc +++ /dev/null
@@ -1,31 +0,0 @@ -// 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/strings/stringprintf.h" - -#include <tuple> - -#include "base/strings/cstring_view.h" - -namespace base { - -void ConstexprStringView() { - static constexpr base::cstring_view kTest = "test %s"; - std::ignore = StringPrintfNonConstexpr(kTest.data(), "123"); // expected-error {{call to deleted function 'StringPrintfNonConstexpr'}} -} - -void ConstexprCharArray() { - static constexpr char kTest[] = "test %s"; - std::ignore = StringPrintfNonConstexpr(kTest, "123"); // expected-error {{call to deleted function 'StringPrintfNonConstexpr'}} -} - -void ConstexprCharPointer() { - static constexpr const char* kTest = "test %s"; - std::ignore = StringPrintfNonConstexpr(kTest, "123"); // expected-error {{call to deleted function 'StringPrintfNonConstexpr'}} -} - -} // namespace base
diff --git a/build/linux/sysroot_scripts/generated_package_lists/bullseye.amd64 b/build/linux/sysroot_scripts/generated_package_lists/bullseye.amd64 index 2526157..f38b6c7 100644 --- a/build/linux/sysroot_scripts/generated_package_lists/bullseye.amd64 +++ b/build/linux/sysroot_scripts/generated_package_lists/bullseye.amd64
@@ -5,7 +5,6 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/a/alsa-lib/libasound2-dev_1.2.4-1.1_amd64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/a/alsa-lib/libasound2_1.2.4-1.1_amd64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/a/aom/libaom0_1.0.0.errata1-3_amd64.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/a/aom/libaom3_3.6.0-1~bpo11+1_amd64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/a/apparmor/libapparmor1_2.13.6-10_amd64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/a/argon2/libargon2-1_0~20171227-0.2_amd64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/a/at-spi2-atk/libatk-bridge2.0-0_2.38.0-4~bpo11+1_amd64.deb @@ -17,7 +16,6 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/a/atk1.0/libatk1.0-0_2.38.0-1~bpo11+1_amd64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/a/atk1.0/libatk1.0-data_2.38.0-1~bpo11+1_all.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/a/atk1.0/libatk1.0-dev_2.38.0-1~bpo11+1_amd64.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/a/attr/libattr1_2.4.48-6_amd64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/a/audit/libaudit-common_3.0-2_all.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/a/audit/libaudit1_3.0-2_amd64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/a/avahi/libavahi-client3_0.8-5+deb11u2_amd64.deb @@ -41,9 +39,7 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/c/cairo/libcairo2-dev_1.16.0-5_amd64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/c/cairo/libcairo2_1.16.0-5_amd64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/c/chromaprint/libchromaprint1_1.5.0-2_amd64.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/c/cjson/libcjson1_1.7.14-1_amd64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/c/codec2/libcodec2-0.9_0.9.2-4_amd64.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/c/codec2/libcodec2-1.0_1.0.5-1_amd64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/c/colord/libcolord2_1.4.5-3_amd64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/c/coreutils/coreutils_8.32-4+b1_amd64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/c/cryptsetup/libcryptsetup12_2.3.7-1+deb11u1_amd64.deb @@ -56,9 +52,7 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/c/cyrus-sasl2/libsasl2-2_2.1.27+dfsg-2.1+deb11u1_amd64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/c/cyrus-sasl2/libsasl2-modules-db_2.1.27+dfsg-2.1+deb11u1_amd64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/d/dav1d/libdav1d4_0.7.1-3_amd64.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/d/dav1d/libdav1d6_1.0.0-2_amd64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/d/db5.3/libdb5.3_5.3.28+dfsg1-0.8_amd64.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/d/dbus-glib/libdbus-glib-1-2_0.110-6_amd64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/d/dbus/dbus-bin_1.14.6-1_amd64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/d/dbus/dbus-daemon_1.14.6-1_amd64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/d/dbus/dbus-session-bus-common_1.14.6-1_all.deb @@ -83,16 +77,12 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/e/expat/libexpat1_2.2.10-2+deb11u5_amd64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/f/ffmpeg/libavcodec-dev_4.3.5-0+deb11u1_amd64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/f/ffmpeg/libavcodec58_4.3.5-0+deb11u1_amd64.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/f/ffmpeg/libavcodec59_5.1.3-1_amd64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/f/ffmpeg/libavformat-dev_4.3.5-0+deb11u1_amd64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/f/ffmpeg/libavformat58_4.3.5-0+deb11u1_amd64.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/f/ffmpeg/libavformat59_5.1.3-1_amd64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/f/ffmpeg/libavutil-dev_4.3.5-0+deb11u1_amd64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/f/ffmpeg/libavutil56_4.3.5-0+deb11u1_amd64.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/f/ffmpeg/libavutil57_5.1.3-1_amd64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/f/ffmpeg/libswresample-dev_4.3.5-0+deb11u1_amd64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/f/ffmpeg/libswresample3_4.3.5-0+deb11u1_amd64.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/f/ffmpeg/libswresample4_5.1.3-1_amd64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/f/flac/libflac-dev_1.3.3-2+deb11u1_amd64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/f/flac/libflac8_1.3.3-2+deb11u1_amd64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/f/fontconfig/fontconfig-config_2.13.1-4.2_all.deb @@ -129,8 +119,6 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/g/gcc-defaults/gcc_10.2.1-1_amd64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/g/gdbm/libgdbm-compat4_1.19-2_amd64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/g/gdbm/libgdbm6_1.19-2_amd64.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/g/gdk-pixbuf-xlib/libgdk-pixbuf-xlib-2.0-0_2.40.2-2_amd64.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/g/gdk-pixbuf-xlib/libgdk-pixbuf2.0-0_2.40.2-2_amd64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/g/gdk-pixbuf/gir1.2-gdkpixbuf-2.0_2.42.2+dfsg-1+deb11u1_amd64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/g/gdk-pixbuf/libgdk-pixbuf-2.0-0_2.42.2+dfsg-1+deb11u1_amd64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/g/gdk-pixbuf/libgdk-pixbuf-2.0-dev_2.42.2+dfsg-1+deb11u1_amd64.deb @@ -187,8 +175,6 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/h/harfbuzz/libharfbuzz-icu0_2.7.4-1_amd64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/h/harfbuzz/libharfbuzz0b_2.7.4-1_amd64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/h/hicolor-icon-theme/hicolor-icon-theme_0.17-2_all.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/h/highway/libhwy1_1.0.3-3_amd64.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/i/icu-le-hb/libicu-le-hb0_1.0.3+git180724-3+b2_amd64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/i/icu/icu-devtools_67.1-7_amd64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/i/icu/libicu-dev_67.1-7_amd64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/i/icu/libicu67_67.1-7_amd64.deb @@ -197,7 +183,6 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/i/isl/libisl23_0.23-1_amd64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/j/jbigkit/libjbig-dev_2.1-3.1+b2_amd64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/j/jbigkit/libjbig0_2.1-3.1+b2_amd64.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/j/jpeg-xl/libjxl0.7_0.7.0-10_amd64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/j/json-c/libjson-c5_0.15-2_amd64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/j/json-glib/libjson-glib-1.0-0_1.6.2-1_amd64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/j/json-glib/libjson-glib-1.0-common_1.6.2-1_all.deb @@ -238,8 +223,6 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libd/libdbusmenu/gir1.2-dbusmenu-glib-0.4_18.10.20180917~bzr492+repack1-2_amd64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libd/libdbusmenu/libdbusmenu-glib-dev_18.10.20180917~bzr492+repack1-2_amd64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libd/libdbusmenu/libdbusmenu-glib4_18.10.20180917~bzr492+repack1-2_amd64.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libd/libdbusmenu/libdbusmenu-gtk3-4_18.10.20180917~bzr492+repack1-2_amd64.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libd/libdbusmenu/libdbusmenu-gtk4_18.10.20180917~bzr492+repack1-2_amd64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libd/libdeflate/libdeflate-dev_1.10-2~bpo11+1_amd64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libd/libdeflate/libdeflate0_1.10-2~bpo11+1_amd64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libd/libdrm/libdrm-amdgpu1_2.4.104-1_amd64.deb @@ -282,8 +265,6 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libg/libgudev/libgudev-1.0-dev_234-1_amd64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libi/libice/libice-dev_1.0.10-1_amd64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libi/libice/libice6_1.0.10-1_amd64.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libi/libidl/libidl-2-0_0.8.14-4+b12_amd64.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libi/libidn/libidn11_1.33-3_amd64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libi/libidn2/libidn2-0_2.3.0-5_amd64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libi/libidn2/libidn2-dev_2.3.0-5_amd64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libi/libinput/libinput-bin_1.16.4-3_amd64.deb @@ -297,7 +278,6 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libm/libmd/libmd0_1.0.3-3_amd64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libn/libnsl/libnsl-dev_1.3.0-2_amd64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libn/libnsl/libnsl2_1.3.0-2_amd64.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libn/libnss-db/libnss-db_2.2.3pre1-6+b10_amd64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libo/libogg/libogg-dev_1.3.4-0.1_amd64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libo/libogg/libogg0_1.3.4-0.1_amd64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libo/libopenmpt/libopenmpt0_0.4.11-1_amd64.deb @@ -310,7 +290,6 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libp/libpthread-stubs/libpthread-stubs0-dev_0.4-1_amd64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libr/librabbitmq/librabbitmq4_0.10.0-1_amd64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libr/librest/librest-0.7-0_0.8.1-1.1_amd64.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libr/librist/librist4_0.2.7+dfsg-1_amd64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libr/librsvg/librsvg2-2_2.50.3+dfsg-1_amd64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libs/libseccomp/libseccomp2_2.5.1-1+deb11u1_amd64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libs/libselinux/libselinux1-dev_3.1-3_amd64.deb @@ -338,7 +317,6 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libt/libtirpc/libtirpc-common_1.3.1-1+deb11u1_all.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libt/libtirpc/libtirpc-dev_1.3.1-1+deb11u1_amd64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libt/libtirpc/libtirpc3_1.3.1-1+deb11u1_amd64.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libt/libtool/libltdl7_2.4.6-15_amd64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libu/libudfread/libudfread0_1.1.1-1_amd64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libu/libunistring/libunistring2_0.9.10-4_amd64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libu/libutempter/libutempter-dev_1.2.1-2_amd64.deb @@ -355,7 +333,6 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libv/libvorbis/libvorbisfile3_1.3.7-1_amd64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libv/libvpx/libvpx-dev_1.9.0-1_amd64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libv/libvpx/libvpx6_1.9.0-1_amd64.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libv/libvpx/libvpx7_1.12.0-1_amd64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libw/libwacom/libwacom-common_1.8-2_all.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libw/libwacom/libwacom-dev_1.8-2_amd64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libw/libwacom/libwacom2_1.8-2_amd64.deb @@ -440,20 +417,16 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/make-dfsg/make_4.3-4.1_amd64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/mako/python3-mako_1.1.3+ds1-2_all.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/markupsafe/python3-markupsafe_2.0.1-2~bpo11+1_amd64.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/mbedtls/libmbedcrypto7_2.28.3-1_amd64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/md4c/libmd4c0_0.4.7-2_amd64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/media-types/media-types_4.0.0_all.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/mesa/libegl-mesa0_20.3.5-1_amd64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/mesa/libegl1-mesa-dev_20.3.5-1_amd64.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/mesa/libegl1-mesa_20.3.5-1_amd64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/mesa/libgbm-dev_20.3.5-1_amd64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/mesa/libgbm1_20.3.5-1_amd64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/mesa/libgl1-mesa-dev_20.3.5-1_amd64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/mesa/libgl1-mesa-dri_20.3.5-1_amd64.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/mesa/libgl1-mesa-glx_20.3.5-1_amd64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/mesa/libglapi-mesa_20.3.5-1_amd64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/mesa/libglx-mesa0_20.3.5-1_amd64.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/mesa/libwayland-egl1-mesa_20.3.5-1_amd64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/mesa/mesa-common-dev_20.3.5-1_amd64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/minizip/libminizip-dev_1.1-8+b1_amd64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/minizip/libminizip1_1.1-8+b1_amd64.deb @@ -498,7 +471,6 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/p/pango1.0/libpangoft2-1.0-0_1.46.2-3_amd64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/p/pango1.0/libpangoxft-1.0-0_1.46.2-3_amd64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/p/pango1.0/pango1.0-tools_1.46.2-3_amd64.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/p/pangox-compat/libpangox-1.0-0_0.0.2-5.1_amd64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/p/patch/patch_2.7.6-7_amd64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/p/pci.ids/pci.ids_0.0~2021.02.08-1_all.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/p/pciutils/libpci-dev_3.7.0-5_amd64.deb @@ -573,7 +545,6 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/r/readline/libreadline8_8.1-1_amd64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/r/readline/readline-common_8.1-1_all.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/r/rtmpdump/librtmp1_2.4+20151223.gitfa8646d.1-2+b2_amd64.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/r/rust-rav1e/librav1e0_0.5.1-6_amd64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/s/sensible-utils/sensible-utils_0.0.14_all.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/s/setuptools/python3-pkg-resources_66.1.1-1~bpo11+1_all.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/s/shadow/login_4.8.1-1_amd64.deb @@ -587,8 +558,6 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/s/speex/libspeex1_1.2~rc1.2-1.1_amd64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/s/sqlite3/libsqlite3-0_3.34.1-3_amd64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/s/srt/libsrt1.4-gnutls_1.4.2-1.3_amd64.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/s/srt/libsrt1.5-gnutls_1.5.1-1_amd64.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/s/svt-av1/libsvtav1enc1_1.4.1+dfsg-1_amd64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/s/systemd/libpam-systemd_252.5-2~bpo11+1_amd64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/s/systemd/libsystemd-dev_252.5-2~bpo11+1_amd64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/s/systemd/libsystemd-shared_252.5-2~bpo11+1_amd64.deb @@ -631,9 +600,7 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/w/wayland/libwayland-server0_1.18.0-2~exp1.1_amd64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/w/webrtc-audio-processing/libwebrtc-audio-processing1_0.3-1+b1_amd64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/x/x264/libx264-160_0.160.3011+gitcde9a93-2.1_amd64.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/x/x264/libx264-164_0.164.3095+gitbaee400-3_amd64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/x/x265/libx265-192_3.4-2_amd64.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/x/x265/libx265-199_3.5-2+b1_amd64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/x/xcb-util-image/libxcb-image0-dev_0.4.0-1+b3_amd64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/x/xcb-util-image/libxcb-image0_0.4.0-1+b3_amd64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/x/xcb-util-keysyms/libxcb-keysyms1_0.4.0-1+b2_amd64.deb
diff --git a/build/linux/sysroot_scripts/generated_package_lists/bullseye.arm64 b/build/linux/sysroot_scripts/generated_package_lists/bullseye.arm64 index 73b565d..82d8486 100644 --- a/build/linux/sysroot_scripts/generated_package_lists/bullseye.arm64 +++ b/build/linux/sysroot_scripts/generated_package_lists/bullseye.arm64
@@ -5,7 +5,6 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/a/alsa-lib/libasound2-dev_1.2.4-1.1_arm64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/a/alsa-lib/libasound2_1.2.4-1.1_arm64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/a/aom/libaom0_1.0.0.errata1-3_arm64.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/a/aom/libaom3_3.6.0-1~bpo11+1_arm64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/a/apparmor/libapparmor1_2.13.6-10_arm64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/a/argon2/libargon2-1_0~20171227-0.2_arm64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/a/at-spi2-atk/libatk-bridge2.0-0_2.38.0-4~bpo11+1_arm64.deb @@ -17,7 +16,6 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/a/atk1.0/libatk1.0-0_2.38.0-1~bpo11+1_arm64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/a/atk1.0/libatk1.0-data_2.38.0-1~bpo11+1_all.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/a/atk1.0/libatk1.0-dev_2.38.0-1~bpo11+1_arm64.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/a/attr/libattr1_2.4.48-6_arm64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/a/audit/libaudit-common_3.0-2_all.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/a/audit/libaudit1_3.0-2_arm64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/a/avahi/libavahi-client3_0.8-5+deb11u2_arm64.deb @@ -41,9 +39,7 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/c/cairo/libcairo2-dev_1.16.0-5_arm64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/c/cairo/libcairo2_1.16.0-5_arm64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/c/chromaprint/libchromaprint1_1.5.0-2_arm64.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/c/cjson/libcjson1_1.7.14-1_arm64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/c/codec2/libcodec2-0.9_0.9.2-4_arm64.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/c/codec2/libcodec2-1.0_1.0.5-1_arm64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/c/colord/libcolord2_1.4.5-3_arm64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/c/coreutils/coreutils_8.32-4_arm64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/c/cryptsetup/libcryptsetup12_2.3.7-1+deb11u1_arm64.deb @@ -56,9 +52,7 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/c/cyrus-sasl2/libsasl2-2_2.1.27+dfsg-2.1+deb11u1_arm64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/c/cyrus-sasl2/libsasl2-modules-db_2.1.27+dfsg-2.1+deb11u1_arm64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/d/dav1d/libdav1d4_0.7.1-3_arm64.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/d/dav1d/libdav1d6_1.0.0-2_arm64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/d/db5.3/libdb5.3_5.3.28+dfsg1-0.8_arm64.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/d/dbus-glib/libdbus-glib-1-2_0.110-6_arm64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/d/dbus/dbus-bin_1.14.6-1_arm64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/d/dbus/dbus-daemon_1.14.6-1_arm64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/d/dbus/dbus-session-bus-common_1.14.6-1_all.deb @@ -83,16 +77,12 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/e/expat/libexpat1_2.2.10-2+deb11u5_arm64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/f/ffmpeg/libavcodec-dev_4.3.5-0+deb11u1_arm64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/f/ffmpeg/libavcodec58_4.3.5-0+deb11u1_arm64.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/f/ffmpeg/libavcodec59_5.1.3-1_arm64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/f/ffmpeg/libavformat-dev_4.3.5-0+deb11u1_arm64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/f/ffmpeg/libavformat58_4.3.5-0+deb11u1_arm64.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/f/ffmpeg/libavformat59_5.1.3-1_arm64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/f/ffmpeg/libavutil-dev_4.3.5-0+deb11u1_arm64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/f/ffmpeg/libavutil56_4.3.5-0+deb11u1_arm64.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/f/ffmpeg/libavutil57_5.1.3-1_arm64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/f/ffmpeg/libswresample-dev_4.3.5-0+deb11u1_arm64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/f/ffmpeg/libswresample3_4.3.5-0+deb11u1_arm64.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/f/ffmpeg/libswresample4_5.1.3-1_arm64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/f/flac/libflac-dev_1.3.3-2+deb11u1_arm64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/f/flac/libflac8_1.3.3-2+deb11u1_arm64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/f/fontconfig/fontconfig-config_2.13.1-4.2_all.deb @@ -128,8 +118,6 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/g/gcc-defaults/gcc_10.2.1-1_arm64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/g/gdbm/libgdbm-compat4_1.19-2_arm64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/g/gdbm/libgdbm6_1.19-2_arm64.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/g/gdk-pixbuf-xlib/libgdk-pixbuf-xlib-2.0-0_2.40.2-2_arm64.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/g/gdk-pixbuf-xlib/libgdk-pixbuf2.0-0_2.40.2-2_arm64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/g/gdk-pixbuf/gir1.2-gdkpixbuf-2.0_2.42.2+dfsg-1+deb11u1_arm64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/g/gdk-pixbuf/libgdk-pixbuf-2.0-0_2.42.2+dfsg-1+deb11u1_arm64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/g/gdk-pixbuf/libgdk-pixbuf-2.0-dev_2.42.2+dfsg-1+deb11u1_arm64.deb @@ -186,8 +174,6 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/h/harfbuzz/libharfbuzz-icu0_2.7.4-1_arm64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/h/harfbuzz/libharfbuzz0b_2.7.4-1_arm64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/h/hicolor-icon-theme/hicolor-icon-theme_0.17-2_all.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/h/highway/libhwy1_1.0.3-3_arm64.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/i/icu-le-hb/libicu-le-hb0_1.0.3+git180724-3+b2_arm64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/i/icu/icu-devtools_67.1-7_arm64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/i/icu/libicu-dev_67.1-7_arm64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/i/icu/libicu67_67.1-7_arm64.deb @@ -195,7 +181,6 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/i/isl/libisl23_0.23-1_arm64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/j/jbigkit/libjbig-dev_2.1-3.1+b2_arm64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/j/jbigkit/libjbig0_2.1-3.1+b2_arm64.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/j/jpeg-xl/libjxl0.7_0.7.0-10_arm64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/j/json-c/libjson-c5_0.15-2_arm64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/j/json-glib/libjson-glib-1.0-0_1.6.2-1_arm64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/j/json-glib/libjson-glib-1.0-common_1.6.2-1_all.deb @@ -236,8 +221,6 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libd/libdbusmenu/gir1.2-dbusmenu-glib-0.4_18.10.20180917~bzr492+repack1-2_arm64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libd/libdbusmenu/libdbusmenu-glib-dev_18.10.20180917~bzr492+repack1-2_arm64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libd/libdbusmenu/libdbusmenu-glib4_18.10.20180917~bzr492+repack1-2_arm64.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libd/libdbusmenu/libdbusmenu-gtk3-4_18.10.20180917~bzr492+repack1-2_arm64.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libd/libdbusmenu/libdbusmenu-gtk4_18.10.20180917~bzr492+repack1-2_arm64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libd/libdeflate/libdeflate-dev_1.10-2~bpo11+1_arm64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libd/libdeflate/libdeflate0_1.10-2~bpo11+1_arm64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libd/libdrm/libdrm-amdgpu1_2.4.104-1_arm64.deb @@ -282,8 +265,6 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libg/libgudev/libgudev-1.0-dev_234-1_arm64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libi/libice/libice-dev_1.0.10-1_arm64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libi/libice/libice6_1.0.10-1_arm64.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libi/libidl/libidl-2-0_0.8.14-4+b12_arm64.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libi/libidn/libidn11_1.33-3_arm64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libi/libidn2/libidn2-0_2.3.0-5_arm64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libi/libidn2/libidn2-dev_2.3.0-5_arm64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libi/libinput/libinput-bin_1.16.4-3_arm64.deb @@ -297,11 +278,9 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libm/libmd/libmd0_1.0.3-3_arm64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libn/libnsl/libnsl-dev_1.3.0-2_arm64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libn/libnsl/libnsl2_1.3.0-2_arm64.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libn/libnss-db/libnss-db_2.2.3pre1-6+b10_arm64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libo/libogg/libogg-dev_1.3.4-0.1_arm64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libo/libogg/libogg0_1.3.4-0.1_arm64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libo/libopenmpt/libopenmpt0_0.4.11-1_arm64.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libp/libpciaccess/libpciaccess0_0.16-1_arm64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libp/libpgm/libpgm-5.3-0_5.3.128~dfsg-2_arm64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libp/libpng1.6/libpng-dev_1.6.37-3_arm64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libp/libpng1.6/libpng16-16_1.6.37-3_arm64.deb @@ -310,7 +289,6 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libp/libpthread-stubs/libpthread-stubs0-dev_0.4-1_arm64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libr/librabbitmq/librabbitmq4_0.10.0-1_arm64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libr/librest/librest-0.7-0_0.8.1-1.1_arm64.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libr/librist/librist4_0.2.7+dfsg-1_arm64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libr/librsvg/librsvg2-2_2.50.3+dfsg-1_arm64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libs/libseccomp/libseccomp2_2.5.1-1+deb11u1_arm64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libs/libselinux/libselinux1-dev_3.1-3_arm64.deb @@ -338,7 +316,6 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libt/libtirpc/libtirpc-common_1.3.1-1+deb11u1_all.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libt/libtirpc/libtirpc-dev_1.3.1-1+deb11u1_arm64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libt/libtirpc/libtirpc3_1.3.1-1+deb11u1_arm64.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libt/libtool/libltdl7_2.4.6-15_arm64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libu/libudfread/libudfread0_1.1.1-1_arm64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libu/libunistring/libunistring2_0.9.10-4_arm64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libu/libutempter/libutempter-dev_1.2.1-2_arm64.deb @@ -355,7 +332,6 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libv/libvorbis/libvorbisfile3_1.3.7-1_arm64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libv/libvpx/libvpx-dev_1.9.0-1_arm64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libv/libvpx/libvpx6_1.9.0-1_arm64.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libv/libvpx/libvpx7_1.12.0-1_arm64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libw/libwacom/libwacom-common_1.8-2_all.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libw/libwacom/libwacom-dev_1.8-2_arm64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libw/libwacom/libwacom2_1.8-2_arm64.deb @@ -440,20 +416,16 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/make-dfsg/make_4.3-4.1_arm64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/mako/python3-mako_1.1.3+ds1-2_all.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/markupsafe/python3-markupsafe_2.0.1-2~bpo11+1_arm64.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/mbedtls/libmbedcrypto7_2.28.3-1_arm64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/md4c/libmd4c0_0.4.7-2_arm64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/media-types/media-types_4.0.0_all.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/mesa/libegl-mesa0_20.3.5-1_arm64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/mesa/libegl1-mesa-dev_20.3.5-1_arm64.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/mesa/libegl1-mesa_20.3.5-1_arm64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/mesa/libgbm-dev_20.3.5-1_arm64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/mesa/libgbm1_20.3.5-1_arm64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/mesa/libgl1-mesa-dev_20.3.5-1_arm64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/mesa/libgl1-mesa-dri_20.3.5-1_arm64.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/mesa/libgl1-mesa-glx_20.3.5-1_arm64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/mesa/libglapi-mesa_20.3.5-1_arm64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/mesa/libglx-mesa0_20.3.5-1_arm64.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/mesa/libwayland-egl1-mesa_20.3.5-1_arm64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/mesa/mesa-common-dev_20.3.5-1_arm64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/minizip/libminizip-dev_1.1-8+b1_arm64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/minizip/libminizip1_1.1-8+b1_arm64.deb @@ -498,7 +470,6 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/p/pango1.0/libpangoft2-1.0-0_1.46.2-3_arm64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/p/pango1.0/libpangoxft-1.0-0_1.46.2-3_arm64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/p/pango1.0/pango1.0-tools_1.46.2-3_arm64.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/p/pangox-compat/libpangox-1.0-0_0.0.2-5.1_arm64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/p/patch/patch_2.7.6-7_arm64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/p/pci.ids/pci.ids_0.0~2021.02.08-1_all.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/p/pciutils/libpci-dev_3.7.0-5_arm64.deb @@ -573,7 +544,6 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/r/readline/libreadline8_8.1-1_arm64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/r/readline/readline-common_8.1-1_all.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/r/rtmpdump/librtmp1_2.4+20151223.gitfa8646d.1-2+b2_arm64.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/r/rust-rav1e/librav1e0_0.5.1-6_arm64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/s/sensible-utils/sensible-utils_0.0.14_all.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/s/setuptools/python3-pkg-resources_66.1.1-1~bpo11+1_all.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/s/shadow/login_4.8.1-1_arm64.deb @@ -587,8 +557,6 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/s/speex/libspeex1_1.2~rc1.2-1.1_arm64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/s/sqlite3/libsqlite3-0_3.34.1-3_arm64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/s/srt/libsrt1.4-gnutls_1.4.2-1.3_arm64.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/s/srt/libsrt1.5-gnutls_1.5.1-1_arm64.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/s/svt-av1/libsvtav1enc1_1.4.1+dfsg-1_arm64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/s/systemd/libpam-systemd_252.5-2~bpo11+1_arm64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/s/systemd/libsystemd-dev_252.5-2~bpo11+1_arm64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/s/systemd/libsystemd-shared_252.5-2~bpo11+1_arm64.deb @@ -631,9 +599,7 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/w/wayland/libwayland-server0_1.18.0-2~exp1.1_arm64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/w/webrtc-audio-processing/libwebrtc-audio-processing1_0.3-1+b1_arm64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/x/x264/libx264-160_0.160.3011+gitcde9a93-2.1_arm64.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/x/x264/libx264-164_0.164.3095+gitbaee400-3_arm64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/x/x265/libx265-192_3.4-2_arm64.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/x/x265/libx265-199_3.5-2+b1_arm64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/x/xcb-util-image/libxcb-image0-dev_0.4.0-1+b3_arm64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/x/xcb-util-image/libxcb-image0_0.4.0-1+b3_arm64.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/x/xcb-util-keysyms/libxcb-keysyms1_0.4.0-1+b2_arm64.deb
diff --git a/build/linux/sysroot_scripts/generated_package_lists/bullseye.armhf b/build/linux/sysroot_scripts/generated_package_lists/bullseye.armhf index d479c216..2fabdd8 100644 --- a/build/linux/sysroot_scripts/generated_package_lists/bullseye.armhf +++ b/build/linux/sysroot_scripts/generated_package_lists/bullseye.armhf
@@ -5,7 +5,6 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/a/alsa-lib/libasound2-dev_1.2.4-1.1_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/a/alsa-lib/libasound2_1.2.4-1.1_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/a/aom/libaom0_1.0.0.errata1-3_armhf.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/a/aom/libaom3_3.6.0-1~bpo11+1_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/a/apparmor/libapparmor1_2.13.6-10_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/a/argon2/libargon2-1_0~20171227-0.2_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/a/at-spi2-atk/libatk-bridge2.0-0_2.38.0-4~bpo11+1_armhf.deb @@ -17,7 +16,6 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/a/atk1.0/libatk1.0-0_2.38.0-1~bpo11+1_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/a/atk1.0/libatk1.0-data_2.38.0-1~bpo11+1_all.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/a/atk1.0/libatk1.0-dev_2.38.0-1~bpo11+1_armhf.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/a/attr/libattr1_2.4.48-6_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/a/audit/libaudit-common_3.0-2_all.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/a/audit/libaudit1_3.0-2_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/a/avahi/libavahi-client3_0.8-5+deb11u2_armhf.deb @@ -41,9 +39,7 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/c/cairo/libcairo2-dev_1.16.0-5_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/c/cairo/libcairo2_1.16.0-5_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/c/chromaprint/libchromaprint1_1.5.0-2_armhf.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/c/cjson/libcjson1_1.7.14-1_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/c/codec2/libcodec2-0.9_0.9.2-4_armhf.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/c/codec2/libcodec2-1.0_1.0.5-1_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/c/colord/libcolord2_1.4.5-3_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/c/coreutils/coreutils_8.32-4_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/c/cryptsetup/libcryptsetup12_2.3.7-1+deb11u1_armhf.deb @@ -56,9 +52,7 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/c/cyrus-sasl2/libsasl2-2_2.1.27+dfsg-2.1+deb11u1_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/c/cyrus-sasl2/libsasl2-modules-db_2.1.27+dfsg-2.1+deb11u1_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/d/dav1d/libdav1d4_0.7.1-3_armhf.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/d/dav1d/libdav1d6_1.0.0-2_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/d/db5.3/libdb5.3_5.3.28+dfsg1-0.8_armhf.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/d/dbus-glib/libdbus-glib-1-2_0.110-6_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/d/dbus/dbus-bin_1.14.6-1_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/d/dbus/dbus-daemon_1.14.6-1_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/d/dbus/dbus-session-bus-common_1.14.6-1_all.deb @@ -83,16 +77,12 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/e/expat/libexpat1_2.2.10-2+deb11u5_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/f/ffmpeg/libavcodec-dev_4.3.5-0+deb11u1_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/f/ffmpeg/libavcodec58_4.3.5-0+deb11u1_armhf.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/f/ffmpeg/libavcodec59_5.1.3-1_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/f/ffmpeg/libavformat-dev_4.3.5-0+deb11u1_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/f/ffmpeg/libavformat58_4.3.5-0+deb11u1_armhf.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/f/ffmpeg/libavformat59_5.1.3-1_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/f/ffmpeg/libavutil-dev_4.3.5-0+deb11u1_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/f/ffmpeg/libavutil56_4.3.5-0+deb11u1_armhf.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/f/ffmpeg/libavutil57_5.1.3-1_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/f/ffmpeg/libswresample-dev_4.3.5-0+deb11u1_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/f/ffmpeg/libswresample3_4.3.5-0+deb11u1_armhf.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/f/ffmpeg/libswresample4_5.1.3-1_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/f/flac/libflac-dev_1.3.3-2+deb11u1_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/f/flac/libflac8_1.3.3-2+deb11u1_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/f/fontconfig/fontconfig-config_2.13.1-4.2_all.deb @@ -125,8 +115,6 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/g/gcc-defaults/gcc_10.2.1-1_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/g/gdbm/libgdbm-compat4_1.19-2_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/g/gdbm/libgdbm6_1.19-2_armhf.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/g/gdk-pixbuf-xlib/libgdk-pixbuf-xlib-2.0-0_2.40.2-2_armhf.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/g/gdk-pixbuf-xlib/libgdk-pixbuf2.0-0_2.40.2-2_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/g/gdk-pixbuf/gir1.2-gdkpixbuf-2.0_2.42.2+dfsg-1+deb11u1_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/g/gdk-pixbuf/libgdk-pixbuf-2.0-0_2.42.2+dfsg-1+deb11u1_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/g/gdk-pixbuf/libgdk-pixbuf-2.0-dev_2.42.2+dfsg-1+deb11u1_armhf.deb @@ -183,8 +171,6 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/h/harfbuzz/libharfbuzz-icu0_2.7.4-1_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/h/harfbuzz/libharfbuzz0b_2.7.4-1_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/h/hicolor-icon-theme/hicolor-icon-theme_0.17-2_all.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/h/highway/libhwy1_1.0.3-3_armhf.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/i/icu-le-hb/libicu-le-hb0_1.0.3+git180724-3+b2_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/i/icu/icu-devtools_67.1-7_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/i/icu/libicu-dev_67.1-7_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/i/icu/libicu67_67.1-7_armhf.deb @@ -192,7 +178,6 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/i/isl/libisl23_0.23-1_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/j/jbigkit/libjbig-dev_2.1-3.1+b2_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/j/jbigkit/libjbig0_2.1-3.1+b2_armhf.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/j/jpeg-xl/libjxl0.7_0.7.0-10_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/j/json-c/libjson-c5_0.15-2_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/j/json-glib/libjson-glib-1.0-0_1.6.2-1_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/j/json-glib/libjson-glib-1.0-common_1.6.2-1_all.deb @@ -233,8 +218,6 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libd/libdbusmenu/gir1.2-dbusmenu-glib-0.4_18.10.20180917~bzr492+repack1-2_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libd/libdbusmenu/libdbusmenu-glib-dev_18.10.20180917~bzr492+repack1-2_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libd/libdbusmenu/libdbusmenu-glib4_18.10.20180917~bzr492+repack1-2_armhf.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libd/libdbusmenu/libdbusmenu-gtk3-4_18.10.20180917~bzr492+repack1-2_armhf.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libd/libdbusmenu/libdbusmenu-gtk4_18.10.20180917~bzr492+repack1-2_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libd/libdeflate/libdeflate-dev_1.10-2~bpo11+1_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libd/libdeflate/libdeflate0_1.10-2~bpo11+1_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libd/libdrm/libdrm-amdgpu1_2.4.104-1_armhf.deb @@ -279,8 +262,6 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libg/libgudev/libgudev-1.0-dev_234-1_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libi/libice/libice-dev_1.0.10-1_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libi/libice/libice6_1.0.10-1_armhf.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libi/libidl/libidl-2-0_0.8.14-4+b12_armhf.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libi/libidn/libidn11_1.33-3_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libi/libidn2/libidn2-0_2.3.0-5_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libi/libidn2/libidn2-dev_2.3.0-5_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libi/libinput/libinput-bin_1.16.4-3_armhf.deb @@ -294,11 +275,9 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libm/libmd/libmd0_1.0.3-3_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libn/libnsl/libnsl-dev_1.3.0-2_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libn/libnsl/libnsl2_1.3.0-2_armhf.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libn/libnss-db/libnss-db_2.2.3pre1-6+b10_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libo/libogg/libogg-dev_1.3.4-0.1_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libo/libogg/libogg0_1.3.4-0.1_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libo/libopenmpt/libopenmpt0_0.4.11-1_armhf.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libp/libpciaccess/libpciaccess0_0.16-1_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libp/libpgm/libpgm-5.3-0_5.3.128~dfsg-2_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libp/libpng1.6/libpng-dev_1.6.37-3_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libp/libpng1.6/libpng16-16_1.6.37-3_armhf.deb @@ -307,7 +286,6 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libp/libpthread-stubs/libpthread-stubs0-dev_0.4-1_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libr/librabbitmq/librabbitmq4_0.10.0-1_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libr/librest/librest-0.7-0_0.8.1-1.1_armhf.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libr/librist/librist4_0.2.7+dfsg-1_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libr/librsvg/librsvg2-2_2.50.3+dfsg-1_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libs/libseccomp/libseccomp2_2.5.1-1+deb11u1_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libs/libselinux/libselinux1-dev_3.1-3_armhf.deb @@ -335,7 +313,6 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libt/libtirpc/libtirpc-common_1.3.1-1+deb11u1_all.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libt/libtirpc/libtirpc-dev_1.3.1-1+deb11u1_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libt/libtirpc/libtirpc3_1.3.1-1+deb11u1_armhf.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libt/libtool/libltdl7_2.4.6-15_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libu/libudfread/libudfread0_1.1.1-1_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libu/libunistring/libunistring2_0.9.10-4_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libu/libutempter/libutempter-dev_1.2.1-2_armhf.deb @@ -352,7 +329,6 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libv/libvorbis/libvorbisfile3_1.3.7-1_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libv/libvpx/libvpx-dev_1.9.0-1_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libv/libvpx/libvpx6_1.9.0-1_armhf.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libv/libvpx/libvpx7_1.12.0-1_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libw/libwacom/libwacom-common_1.8-2_all.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libw/libwacom/libwacom-dev_1.8-2_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libw/libwacom/libwacom2_1.8-2_armhf.deb @@ -437,20 +413,15 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/make-dfsg/make_4.3-4.1_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/mako/python3-mako_1.1.3+ds1-2_all.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/markupsafe/python3-markupsafe_2.0.1-2~bpo11+1_armhf.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/mbedtls/libmbedcrypto7_2.28.3-1_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/md4c/libmd4c0_0.4.7-2_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/media-types/media-types_4.0.0_all.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/mesa/libegl-mesa0_20.3.5-1_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/mesa/libegl1-mesa-dev_20.3.5-1_armhf.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/mesa/libegl1-mesa_20.3.5-1_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/mesa/libgbm-dev_20.3.5-1_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/mesa/libgbm1_20.3.5-1_armhf.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/mesa/libgl1-mesa-dev_20.3.5-1_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/mesa/libgl1-mesa-dri_20.3.5-1_armhf.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/mesa/libgl1-mesa-glx_20.3.5-1_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/mesa/libglapi-mesa_20.3.5-1_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/mesa/libglx-mesa0_20.3.5-1_armhf.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/mesa/libwayland-egl1-mesa_20.3.5-1_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/mesa/mesa-common-dev_20.3.5-1_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/minizip/libminizip-dev_1.1-8+b1_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/minizip/libminizip1_1.1-8+b1_armhf.deb @@ -474,7 +445,6 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/n/nspr/libnspr4_4.29-1_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/n/nss/libnss3-dev_3.61-1+deb11u3_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/n/nss/libnss3_3.61-1+deb11u3_armhf.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/n/numactl/libnuma1_2.0.12-1+b1_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/o/ocl-icd/ocl-icd-libopencl1_2.2.14-2_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/o/openjpeg2/libopenjp2-7_2.4.0-3_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/o/openldap/libldap-2.4-2_2.4.59+dfsg-1~bpo11+1_armhf.deb @@ -495,7 +465,6 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/p/pango1.0/libpangoft2-1.0-0_1.46.2-3_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/p/pango1.0/libpangoxft-1.0-0_1.46.2-3_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/p/pango1.0/pango1.0-tools_1.46.2-3_armhf.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/p/pangox-compat/libpangox-1.0-0_0.0.2-5.1_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/p/patch/patch_2.7.6-7_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/p/pci.ids/pci.ids_0.0~2021.02.08-1_all.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/p/pciutils/libpci-dev_3.7.0-5_armhf.deb @@ -570,7 +539,6 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/r/readline/libreadline8_8.1-1_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/r/readline/readline-common_8.1-1_all.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/r/rtmpdump/librtmp1_2.4+20151223.gitfa8646d.1-2+b2_armhf.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/r/rust-rav1e/librav1e0_0.5.1-6_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/s/sensible-utils/sensible-utils_0.0.14_all.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/s/setuptools/python3-pkg-resources_66.1.1-1~bpo11+1_all.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/s/shadow/login_4.8.1-1_armhf.deb @@ -584,8 +552,6 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/s/speex/libspeex1_1.2~rc1.2-1.1_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/s/sqlite3/libsqlite3-0_3.34.1-3_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/s/srt/libsrt1.4-gnutls_1.4.2-1.3_armhf.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/s/srt/libsrt1.5-gnutls_1.5.1-1_armhf.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/s/svt-av1/libsvtav1enc1_1.4.1+dfsg-1_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/s/systemd/libpam-systemd_252.5-2~bpo11+1_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/s/systemd/libsystemd-dev_252.5-2~bpo11+1_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/s/systemd/libsystemd-shared_252.5-2~bpo11+1_armhf.deb @@ -628,9 +594,7 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/w/wayland/libwayland-server0_1.18.0-2~exp1.1_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/w/webrtc-audio-processing/libwebrtc-audio-processing1_0.3-1+b1_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/x/x264/libx264-160_0.160.3011+gitcde9a93-2.1_armhf.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/x/x264/libx264-164_0.164.3095+gitbaee400-3_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/x/x265/libx265-192_3.4-2_armhf.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/x/x265/libx265-199_3.5-2+b1_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/x/xcb-util-image/libxcb-image0-dev_0.4.0-1+b3_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/x/xcb-util-image/libxcb-image0_0.4.0-1+b3_armhf.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/x/xcb-util-keysyms/libxcb-keysyms1_0.4.0-1+b2_armhf.deb
diff --git a/build/linux/sysroot_scripts/generated_package_lists/bullseye.i386 b/build/linux/sysroot_scripts/generated_package_lists/bullseye.i386 index 9d3591d..00c3b08 100644 --- a/build/linux/sysroot_scripts/generated_package_lists/bullseye.i386 +++ b/build/linux/sysroot_scripts/generated_package_lists/bullseye.i386
@@ -5,7 +5,6 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/a/alsa-lib/libasound2-dev_1.2.4-1.1_i386.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/a/alsa-lib/libasound2_1.2.4-1.1_i386.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/a/aom/libaom0_1.0.0.errata1-3_i386.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/a/aom/libaom3_3.6.0-1~bpo11+1_i386.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/a/apparmor/libapparmor1_2.13.6-10_i386.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/a/argon2/libargon2-1_0~20171227-0.2_i386.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/a/at-spi2-atk/libatk-bridge2.0-0_2.38.0-4~bpo11+1_i386.deb @@ -17,7 +16,6 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/a/atk1.0/libatk1.0-0_2.38.0-1~bpo11+1_i386.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/a/atk1.0/libatk1.0-data_2.38.0-1~bpo11+1_all.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/a/atk1.0/libatk1.0-dev_2.38.0-1~bpo11+1_i386.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/a/attr/libattr1_2.4.48-6_i386.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/a/audit/libaudit-common_3.0-2_all.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/a/audit/libaudit1_3.0-2_i386.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/a/avahi/libavahi-client3_0.8-5+deb11u2_i386.deb @@ -41,9 +39,7 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/c/cairo/libcairo2-dev_1.16.0-5_i386.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/c/cairo/libcairo2_1.16.0-5_i386.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/c/chromaprint/libchromaprint1_1.5.0-2_i386.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/c/cjson/libcjson1_1.7.14-1_i386.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/c/codec2/libcodec2-0.9_0.9.2-4_i386.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/c/codec2/libcodec2-1.0_1.0.5-1_i386.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/c/colord/libcolord2_1.4.5-3_i386.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/c/coreutils/coreutils_8.32-4_i386.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/c/cryptsetup/libcryptsetup12_2.3.7-1+deb11u1_i386.deb @@ -56,9 +52,7 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/c/cyrus-sasl2/libsasl2-2_2.1.27+dfsg-2.1+deb11u1_i386.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/c/cyrus-sasl2/libsasl2-modules-db_2.1.27+dfsg-2.1+deb11u1_i386.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/d/dav1d/libdav1d4_0.7.1-3_i386.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/d/dav1d/libdav1d6_1.0.0-2_i386.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/d/db5.3/libdb5.3_5.3.28+dfsg1-0.8_i386.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/d/dbus-glib/libdbus-glib-1-2_0.110-6_i386.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/d/dbus/dbus-bin_1.14.6-1_i386.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/d/dbus/dbus-daemon_1.14.6-1_i386.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/d/dbus/dbus-session-bus-common_1.14.6-1_all.deb @@ -83,16 +77,12 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/e/expat/libexpat1_2.2.10-2+deb11u5_i386.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/f/ffmpeg/libavcodec-dev_4.3.5-0+deb11u1_i386.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/f/ffmpeg/libavcodec58_4.3.5-0+deb11u1_i386.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/f/ffmpeg/libavcodec59_5.1.3-1_i386.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/f/ffmpeg/libavformat-dev_4.3.5-0+deb11u1_i386.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/f/ffmpeg/libavformat58_4.3.5-0+deb11u1_i386.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/f/ffmpeg/libavformat59_5.1.3-1_i386.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/f/ffmpeg/libavutil-dev_4.3.5-0+deb11u1_i386.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/f/ffmpeg/libavutil56_4.3.5-0+deb11u1_i386.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/f/ffmpeg/libavutil57_5.1.3-1_i386.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/f/ffmpeg/libswresample-dev_4.3.5-0+deb11u1_i386.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/f/ffmpeg/libswresample3_4.3.5-0+deb11u1_i386.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/f/ffmpeg/libswresample4_5.1.3-1_i386.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/f/flac/libflac-dev_1.3.3-2+deb11u1_i386.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/f/flac/libflac8_1.3.3-2+deb11u1_i386.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/f/fontconfig/fontconfig-config_2.13.1-4.2_all.deb @@ -127,8 +117,6 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/g/gcc-defaults/gcc_10.2.1-1_i386.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/g/gdbm/libgdbm-compat4_1.19-2_i386.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/g/gdbm/libgdbm6_1.19-2_i386.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/g/gdk-pixbuf-xlib/libgdk-pixbuf-xlib-2.0-0_2.40.2-2_i386.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/g/gdk-pixbuf-xlib/libgdk-pixbuf2.0-0_2.40.2-2_i386.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/g/gdk-pixbuf/gir1.2-gdkpixbuf-2.0_2.42.2+dfsg-1+deb11u1_i386.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/g/gdk-pixbuf/libgdk-pixbuf-2.0-0_2.42.2+dfsg-1+deb11u1_i386.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/g/gdk-pixbuf/libgdk-pixbuf-2.0-dev_2.42.2+dfsg-1+deb11u1_i386.deb @@ -185,8 +173,6 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/h/harfbuzz/libharfbuzz-icu0_2.7.4-1_i386.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/h/harfbuzz/libharfbuzz0b_2.7.4-1_i386.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/h/hicolor-icon-theme/hicolor-icon-theme_0.17-2_all.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/h/highway/libhwy1_1.0.3-3_i386.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/i/icu-le-hb/libicu-le-hb0_1.0.3+git180724-3+b2_i386.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/i/icu/icu-devtools_67.1-7_i386.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/i/icu/libicu-dev_67.1-7_i386.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/i/icu/libicu67_67.1-7_i386.deb @@ -194,7 +180,6 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/i/isl/libisl23_0.23-1_i386.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/j/jbigkit/libjbig-dev_2.1-3.1+b2_i386.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/j/jbigkit/libjbig0_2.1-3.1+b2_i386.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/j/jpeg-xl/libjxl0.7_0.7.0-10_i386.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/j/json-c/libjson-c5_0.15-2_i386.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/j/json-glib/libjson-glib-1.0-0_1.6.2-1_i386.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/j/json-glib/libjson-glib-1.0-common_1.6.2-1_all.deb @@ -235,8 +220,6 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libd/libdbusmenu/gir1.2-dbusmenu-glib-0.4_18.10.20180917~bzr492+repack1-2_i386.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libd/libdbusmenu/libdbusmenu-glib-dev_18.10.20180917~bzr492+repack1-2_i386.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libd/libdbusmenu/libdbusmenu-glib4_18.10.20180917~bzr492+repack1-2_i386.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libd/libdbusmenu/libdbusmenu-gtk3-4_18.10.20180917~bzr492+repack1-2_i386.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libd/libdbusmenu/libdbusmenu-gtk4_18.10.20180917~bzr492+repack1-2_i386.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libd/libdeflate/libdeflate-dev_1.10-2~bpo11+1_i386.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libd/libdeflate/libdeflate0_1.10-2~bpo11+1_i386.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libd/libdrm/libdrm-amdgpu1_2.4.104-1_i386.deb @@ -279,8 +262,6 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libg/libgudev/libgudev-1.0-dev_234-1_i386.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libi/libice/libice-dev_1.0.10-1_i386.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libi/libice/libice6_1.0.10-1_i386.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libi/libidl/libidl-2-0_0.8.14-4+b12_i386.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libi/libidn/libidn11_1.33-3_i386.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libi/libidn2/libidn2-0_2.3.0-5_i386.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libi/libidn2/libidn2-dev_2.3.0-5_i386.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libi/libinput/libinput-bin_1.16.4-3_i386.deb @@ -294,7 +275,6 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libm/libmd/libmd0_1.0.3-3_i386.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libn/libnsl/libnsl-dev_1.3.0-2_i386.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libn/libnsl/libnsl2_1.3.0-2_i386.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libn/libnss-db/libnss-db_2.2.3pre1-6+b10_i386.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libo/libogg/libogg-dev_1.3.4-0.1_i386.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libo/libogg/libogg0_1.3.4-0.1_i386.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libo/libopenmpt/libopenmpt0_0.4.11-1_i386.deb @@ -307,7 +287,6 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libp/libpthread-stubs/libpthread-stubs0-dev_0.4-1_i386.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libr/librabbitmq/librabbitmq4_0.10.0-1_i386.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libr/librest/librest-0.7-0_0.8.1-1.1_i386.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libr/librist/librist4_0.2.7+dfsg-1_i386.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libr/librsvg/librsvg2-2_2.50.3+dfsg-1_i386.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libs/libseccomp/libseccomp2_2.5.1-1+deb11u1_i386.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libs/libselinux/libselinux1-dev_3.1-3_i386.deb @@ -335,7 +314,6 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libt/libtirpc/libtirpc-common_1.3.1-1+deb11u1_all.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libt/libtirpc/libtirpc-dev_1.3.1-1+deb11u1_i386.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libt/libtirpc/libtirpc3_1.3.1-1+deb11u1_i386.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libt/libtool/libltdl7_2.4.6-15_i386.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libu/libudfread/libudfread0_1.1.1-1_i386.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libu/libunistring/libunistring2_0.9.10-4_i386.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libu/libutempter/libutempter-dev_1.2.1-2_i386.deb @@ -352,7 +330,6 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libv/libvorbis/libvorbisfile3_1.3.7-1_i386.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libv/libvpx/libvpx-dev_1.9.0-1_i386.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libv/libvpx/libvpx6_1.9.0-1_i386.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libv/libvpx/libvpx7_1.12.0-1_i386.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libw/libwacom/libwacom-common_1.8-2_all.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libw/libwacom/libwacom-dev_1.8-2_i386.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libw/libwacom/libwacom2_1.8-2_i386.deb @@ -437,20 +414,16 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/make-dfsg/make_4.3-4.1_i386.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/mako/python3-mako_1.1.3+ds1-2_all.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/markupsafe/python3-markupsafe_2.0.1-2~bpo11+1_i386.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/mbedtls/libmbedcrypto7_2.28.3-1_i386.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/md4c/libmd4c0_0.4.7-2_i386.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/media-types/media-types_4.0.0_all.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/mesa/libegl-mesa0_20.3.5-1_i386.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/mesa/libegl1-mesa-dev_20.3.5-1_i386.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/mesa/libegl1-mesa_20.3.5-1_i386.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/mesa/libgbm-dev_20.3.5-1_i386.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/mesa/libgbm1_20.3.5-1_i386.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/mesa/libgl1-mesa-dev_20.3.5-1_i386.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/mesa/libgl1-mesa-dri_20.3.5-1_i386.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/mesa/libgl1-mesa-glx_20.3.5-1_i386.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/mesa/libglapi-mesa_20.3.5-1_i386.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/mesa/libglx-mesa0_20.3.5-1_i386.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/mesa/libwayland-egl1-mesa_20.3.5-1_i386.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/mesa/mesa-common-dev_20.3.5-1_i386.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/minizip/libminizip-dev_1.1-8+b1_i386.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/minizip/libminizip1_1.1-8+b1_i386.deb @@ -495,7 +468,6 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/p/pango1.0/libpangoft2-1.0-0_1.46.2-3_i386.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/p/pango1.0/libpangoxft-1.0-0_1.46.2-3_i386.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/p/pango1.0/pango1.0-tools_1.46.2-3_i386.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/p/pangox-compat/libpangox-1.0-0_0.0.2-5.1_i386.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/p/patch/patch_2.7.6-7_i386.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/p/pci.ids/pci.ids_0.0~2021.02.08-1_all.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/p/pciutils/libpci-dev_3.7.0-5_i386.deb @@ -570,7 +542,6 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/r/readline/libreadline8_8.1-1_i386.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/r/readline/readline-common_8.1-1_all.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/r/rtmpdump/librtmp1_2.4+20151223.gitfa8646d.1-2+b2_i386.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/r/rust-rav1e/librav1e0_0.5.1-6_i386.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/s/sensible-utils/sensible-utils_0.0.14_all.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/s/setuptools/python3-pkg-resources_66.1.1-1~bpo11+1_all.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/s/shadow/login_4.8.1-1_i386.deb @@ -584,8 +555,6 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/s/speex/libspeex1_1.2~rc1.2-1.1_i386.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/s/sqlite3/libsqlite3-0_3.34.1-3_i386.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/s/srt/libsrt1.4-gnutls_1.4.2-1.3_i386.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/s/srt/libsrt1.5-gnutls_1.5.1-1_i386.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/s/svt-av1/libsvtav1enc1_1.4.1+dfsg-1_i386.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/s/systemd/libpam-systemd_252.5-2~bpo11+1_i386.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/s/systemd/libsystemd-dev_252.5-2~bpo11+1_i386.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/s/systemd/libsystemd-shared_252.5-2~bpo11+1_i386.deb @@ -628,9 +597,7 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/w/wayland/libwayland-server0_1.18.0-2~exp1.1_i386.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/w/webrtc-audio-processing/libwebrtc-audio-processing1_0.3-1+b1_i386.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/x/x264/libx264-160_0.160.3011+gitcde9a93-2.1_i386.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/x/x264/libx264-164_0.164.3095+gitbaee400-3_i386.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/x/x265/libx265-192_3.4-2_i386.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/x/x265/libx265-199_3.5-2+b1_i386.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/x/xcb-util-image/libxcb-image0-dev_0.4.0-1+b3_i386.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/x/xcb-util-image/libxcb-image0_0.4.0-1+b3_i386.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/x/xcb-util-keysyms/libxcb-keysyms1_0.4.0-1+b2_i386.deb
diff --git a/build/linux/sysroot_scripts/generated_package_lists/bullseye.mips64el b/build/linux/sysroot_scripts/generated_package_lists/bullseye.mips64el index db57835..efa39c1 100644 --- a/build/linux/sysroot_scripts/generated_package_lists/bullseye.mips64el +++ b/build/linux/sysroot_scripts/generated_package_lists/bullseye.mips64el
@@ -5,7 +5,6 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/a/alsa-lib/libasound2-dev_1.2.4-1.1_mips64el.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/a/alsa-lib/libasound2_1.2.4-1.1_mips64el.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/a/aom/libaom0_1.0.0.errata1-3_mips64el.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/a/aom/libaom3_3.6.0-1~bpo11+1_mips64el.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/a/apparmor/libapparmor1_2.13.6-10_mips64el.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/a/argon2/libargon2-1_0~20171227-0.2_mips64el.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/a/at-spi2-atk/libatk-bridge2.0-0_2.38.0-4~bpo11+1_mips64el.deb @@ -17,7 +16,6 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/a/atk1.0/libatk1.0-0_2.38.0-1~bpo11+1_mips64el.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/a/atk1.0/libatk1.0-data_2.38.0-1~bpo11+1_all.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/a/atk1.0/libatk1.0-dev_2.38.0-1~bpo11+1_mips64el.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/a/attr/libattr1_2.4.48-6_mips64el.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/a/audit/libaudit-common_3.0-2_all.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/a/audit/libaudit1_3.0-2_mips64el.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/a/avahi/libavahi-client3_0.8-5+deb11u2_mips64el.deb @@ -41,9 +39,7 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/c/cairo/libcairo2-dev_1.16.0-5_mips64el.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/c/cairo/libcairo2_1.16.0-5_mips64el.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/c/chromaprint/libchromaprint1_1.5.0-2_mips64el.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/c/cjson/libcjson1_1.7.14-1_mips64el.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/c/codec2/libcodec2-0.9_0.9.2-4_mips64el.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/c/codec2/libcodec2-1.0_1.0.5-1_mips64el.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/c/colord/libcolord2_1.4.5-3_mips64el.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/c/coreutils/coreutils_8.32-4_mips64el.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/c/cryptsetup/libcryptsetup12_2.3.7-1+deb11u1_mips64el.deb @@ -56,9 +52,7 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/c/cyrus-sasl2/libsasl2-2_2.1.27+dfsg-2.1+deb11u1_mips64el.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/c/cyrus-sasl2/libsasl2-modules-db_2.1.27+dfsg-2.1+deb11u1_mips64el.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/d/dav1d/libdav1d4_0.7.1-3_mips64el.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/d/dav1d/libdav1d6_1.0.0-2_mips64el.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/d/db5.3/libdb5.3_5.3.28+dfsg1-0.8_mips64el.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/d/dbus-glib/libdbus-glib-1-2_0.110-6_mips64el.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/d/dbus/dbus-bin_1.14.6-1_mips64el.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/d/dbus/dbus-daemon_1.14.6-1_mips64el.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/d/dbus/dbus-session-bus-common_1.14.6-1_all.deb @@ -83,16 +77,12 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/e/expat/libexpat1_2.2.10-2+deb11u5_mips64el.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/f/ffmpeg/libavcodec-dev_4.3.5-0+deb11u1_mips64el.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/f/ffmpeg/libavcodec58_4.3.5-0+deb11u1_mips64el.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/f/ffmpeg/libavcodec59_5.1.3-1_mips64el.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/f/ffmpeg/libavformat-dev_4.3.5-0+deb11u1_mips64el.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/f/ffmpeg/libavformat58_4.3.5-0+deb11u1_mips64el.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/f/ffmpeg/libavformat59_5.1.3-1_mips64el.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/f/ffmpeg/libavutil-dev_4.3.5-0+deb11u1_mips64el.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/f/ffmpeg/libavutil56_4.3.5-0+deb11u1_mips64el.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/f/ffmpeg/libavutil57_5.1.3-1_mips64el.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/f/ffmpeg/libswresample-dev_4.3.5-0+deb11u1_mips64el.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/f/ffmpeg/libswresample3_4.3.5-0+deb11u1_mips64el.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/f/ffmpeg/libswresample4_5.1.3-1_mips64el.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/f/flac/libflac-dev_1.3.3-2+deb11u1_mips64el.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/f/flac/libflac8_1.3.3-2+deb11u1_mips64el.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/f/fontconfig/fontconfig-config_2.13.1-4.2_all.deb @@ -123,8 +113,6 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/g/gcc-defaults/gcc_10.2.1-1_mips64el.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/g/gdbm/libgdbm-compat4_1.19-2_mips64el.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/g/gdbm/libgdbm6_1.19-2_mips64el.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/g/gdk-pixbuf-xlib/libgdk-pixbuf-xlib-2.0-0_2.40.2-2_mips64el.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/g/gdk-pixbuf-xlib/libgdk-pixbuf2.0-0_2.40.2-2_mips64el.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/g/gdk-pixbuf/gir1.2-gdkpixbuf-2.0_2.42.2+dfsg-1+deb11u1_mips64el.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/g/gdk-pixbuf/libgdk-pixbuf-2.0-0_2.42.2+dfsg-1+deb11u1_mips64el.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/g/gdk-pixbuf/libgdk-pixbuf-2.0-dev_2.42.2+dfsg-1+deb11u1_mips64el.deb @@ -181,8 +169,6 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/h/harfbuzz/libharfbuzz-icu0_2.7.4-1_mips64el.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/h/harfbuzz/libharfbuzz0b_2.7.4-1_mips64el.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/h/hicolor-icon-theme/hicolor-icon-theme_0.17-2_all.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/h/highway/libhwy1_1.0.3-3_mips64el.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/i/icu-le-hb/libicu-le-hb0_1.0.3+git180724-3+b2_mips64el.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/i/icu/icu-devtools_67.1-7_mips64el.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/i/icu/libicu-dev_67.1-7_mips64el.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/i/icu/libicu67_67.1-7_mips64el.deb @@ -190,7 +176,6 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/i/isl/libisl23_0.23-1_mips64el.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/j/jbigkit/libjbig-dev_2.1-3.1+b2_mips64el.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/j/jbigkit/libjbig0_2.1-3.1+b2_mips64el.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/j/jpeg-xl/libjxl0.7_0.7.0-10_mips64el.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/j/json-c/libjson-c5_0.15-2_mips64el.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/j/json-glib/libjson-glib-1.0-0_1.6.2-1_mips64el.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/j/json-glib/libjson-glib-1.0-common_1.6.2-1_all.deb @@ -231,8 +216,6 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libd/libdbusmenu/gir1.2-dbusmenu-glib-0.4_18.10.20180917~bzr492+repack1-2_mips64el.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libd/libdbusmenu/libdbusmenu-glib-dev_18.10.20180917~bzr492+repack1-2_mips64el.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libd/libdbusmenu/libdbusmenu-glib4_18.10.20180917~bzr492+repack1-2_mips64el.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libd/libdbusmenu/libdbusmenu-gtk3-4_18.10.20180917~bzr492+repack1-2_mips64el.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libd/libdbusmenu/libdbusmenu-gtk4_18.10.20180917~bzr492+repack1-2_mips64el.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libd/libdeflate/libdeflate-dev_1.10-2~bpo11+1_mips64el.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libd/libdeflate/libdeflate0_1.10-2~bpo11+1_mips64el.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libd/libdrm/libdrm-amdgpu1_2.4.104-1_mips64el.deb @@ -274,8 +257,6 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libg/libgudev/libgudev-1.0-dev_234-1_mips64el.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libi/libice/libice-dev_1.0.10-1_mips64el.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libi/libice/libice6_1.0.10-1_mips64el.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libi/libidl/libidl-2-0_0.8.14-4+b12_mips64el.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libi/libidn/libidn11_1.33-3_mips64el.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libi/libidn2/libidn2-0_2.3.0-5_mips64el.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libi/libidn2/libidn2-dev_2.3.0-5_mips64el.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libi/libinput/libinput-bin_1.16.4-3_mips64el.deb @@ -289,11 +270,9 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libm/libmd/libmd0_1.0.3-3_mips64el.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libn/libnsl/libnsl-dev_1.3.0-2_mips64el.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libn/libnsl/libnsl2_1.3.0-2_mips64el.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libn/libnss-db/libnss-db_2.2.3pre1-6+b10_mips64el.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libo/libogg/libogg-dev_1.3.4-0.1_mips64el.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libo/libogg/libogg0_1.3.4-0.1_mips64el.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libo/libopenmpt/libopenmpt0_0.4.11-1_mips64el.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libp/libpciaccess/libpciaccess0_0.16-1_mips64el.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libp/libpgm/libpgm-5.3-0_5.3.128~dfsg-2_mips64el.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libp/libpng1.6/libpng-dev_1.6.37-3_mips64el.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libp/libpng1.6/libpng16-16_1.6.37-3_mips64el.deb @@ -302,7 +281,6 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libp/libpthread-stubs/libpthread-stubs0-dev_0.4-1_mips64el.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libr/librabbitmq/librabbitmq4_0.10.0-1_mips64el.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libr/librest/librest-0.7-0_0.8.1-1.1_mips64el.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libr/librist/librist4_0.2.7+dfsg-1_mips64el.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libr/librsvg/librsvg2-2_2.50.3+dfsg-1_mips64el.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libs/libseccomp/libseccomp2_2.5.1-1+deb11u1_mips64el.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libs/libselinux/libselinux1-dev_3.1-3_mips64el.deb @@ -330,7 +308,6 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libt/libtirpc/libtirpc-common_1.3.1-1+deb11u1_all.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libt/libtirpc/libtirpc-dev_1.3.1-1+deb11u1_mips64el.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libt/libtirpc/libtirpc3_1.3.1-1+deb11u1_mips64el.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libt/libtool/libltdl7_2.4.6-15_mips64el.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libu/libudfread/libudfread0_1.1.1-1_mips64el.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libu/libunistring/libunistring2_0.9.10-4_mips64el.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libu/libutempter/libutempter-dev_1.2.1-2_mips64el.deb @@ -347,7 +324,6 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libv/libvorbis/libvorbisfile3_1.3.7-1_mips64el.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libv/libvpx/libvpx-dev_1.9.0-1_mips64el.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libv/libvpx/libvpx6_1.9.0-1_mips64el.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libv/libvpx/libvpx7_1.12.0-1_mips64el.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libw/libwacom/libwacom-common_1.8-2_all.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libw/libwacom/libwacom-dev_1.8-2_mips64el.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libw/libwacom/libwacom2_1.8-2_mips64el.deb @@ -432,20 +408,16 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/make-dfsg/make_4.3-4.1_mips64el.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/mako/python3-mako_1.1.3+ds1-2_all.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/markupsafe/python3-markupsafe_2.0.1-2~bpo11+1_mips64el.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/mbedtls/libmbedcrypto7_2.28.3-1_mips64el.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/md4c/libmd4c0_0.4.7-2_mips64el.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/media-types/media-types_4.0.0_all.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/mesa/libegl-mesa0_20.3.5-1_mips64el.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/mesa/libegl1-mesa-dev_20.3.5-1_mips64el.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/mesa/libegl1-mesa_20.3.5-1_mips64el.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/mesa/libgbm-dev_20.3.5-1_mips64el.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/mesa/libgbm1_20.3.5-1_mips64el.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/mesa/libgl1-mesa-dev_20.3.5-1_mips64el.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/mesa/libgl1-mesa-dri_20.3.5-1_mips64el.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/mesa/libgl1-mesa-glx_20.3.5-1_mips64el.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/mesa/libglapi-mesa_20.3.5-1_mips64el.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/mesa/libglx-mesa0_20.3.5-1_mips64el.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/mesa/libwayland-egl1-mesa_20.3.5-1_mips64el.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/mesa/mesa-common-dev_20.3.5-1_mips64el.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/minizip/libminizip-dev_1.1-8+b1_mips64el.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/minizip/libminizip1_1.1-8+b1_mips64el.deb @@ -490,7 +462,6 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/p/pango1.0/libpangoft2-1.0-0_1.46.2-3_mips64el.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/p/pango1.0/libpangoxft-1.0-0_1.46.2-3_mips64el.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/p/pango1.0/pango1.0-tools_1.46.2-3_mips64el.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/p/pangox-compat/libpangox-1.0-0_0.0.2-5.1_mips64el.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/p/patch/patch_2.7.6-7_mips64el.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/p/pci.ids/pci.ids_0.0~2021.02.08-1_all.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/p/pciutils/libpci-dev_3.7.0-5_mips64el.deb @@ -565,7 +536,6 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/r/readline/libreadline8_8.1-1_mips64el.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/r/readline/readline-common_8.1-1_all.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/r/rtmpdump/librtmp1_2.4+20151223.gitfa8646d.1-2+b2_mips64el.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/r/rust-rav1e/librav1e0_0.5.1-6_mips64el.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/s/sensible-utils/sensible-utils_0.0.14_all.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/s/setuptools/python3-pkg-resources_66.1.1-1~bpo11+1_all.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/s/shadow/login_4.8.1-1_mips64el.deb @@ -579,8 +549,6 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/s/speex/libspeex1_1.2~rc1.2-1.1_mips64el.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/s/sqlite3/libsqlite3-0_3.34.1-3_mips64el.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/s/srt/libsrt1.4-gnutls_1.4.2-1.3_mips64el.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/s/srt/libsrt1.5-gnutls_1.5.1-1_mips64el.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/s/svt-av1/libsvtav1enc1_1.4.1+dfsg-1_mips64el.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/s/systemd/libpam-systemd_252.5-2~bpo11+1_mips64el.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/s/systemd/libsystemd-dev_252.5-2~bpo11+1_mips64el.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/s/systemd/libsystemd-shared_252.5-2~bpo11+1_mips64el.deb @@ -623,9 +591,7 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/w/wayland/libwayland-server0_1.18.0-2~exp1.1_mips64el.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/w/webrtc-audio-processing/libwebrtc-audio-processing1_0.3-1+b1_mips64el.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/x/x264/libx264-160_0.160.3011+gitcde9a93-2.1_mips64el.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/x/x264/libx264-164_0.164.3095+gitbaee400-3_mips64el.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/x/x265/libx265-192_3.4-2_mips64el.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/x/x265/libx265-199_3.5-2+b1_mips64el.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/x/xcb-util-image/libxcb-image0-dev_0.4.0-1+b3_mips64el.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/x/xcb-util-image/libxcb-image0_0.4.0-1+b3_mips64el.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/x/xcb-util-keysyms/libxcb-keysyms1_0.4.0-1+b2_mips64el.deb
diff --git a/build/linux/sysroot_scripts/generated_package_lists/bullseye.mipsel b/build/linux/sysroot_scripts/generated_package_lists/bullseye.mipsel index 6180a771..bc3aee7 100644 --- a/build/linux/sysroot_scripts/generated_package_lists/bullseye.mipsel +++ b/build/linux/sysroot_scripts/generated_package_lists/bullseye.mipsel
@@ -5,7 +5,6 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/a/alsa-lib/libasound2-dev_1.2.4-1.1_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/a/alsa-lib/libasound2_1.2.4-1.1_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/a/aom/libaom0_1.0.0.errata1-3_mipsel.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/a/aom/libaom3_3.6.0-1~bpo11+1_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/a/apparmor/libapparmor1_2.13.6-10_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/a/argon2/libargon2-1_0~20171227-0.2_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/a/at-spi2-atk/libatk-bridge2.0-0_2.38.0-4~bpo11+1_mipsel.deb @@ -17,7 +16,6 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/a/atk1.0/libatk1.0-0_2.38.0-1~bpo11+1_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/a/atk1.0/libatk1.0-data_2.38.0-1~bpo11+1_all.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/a/atk1.0/libatk1.0-dev_2.38.0-1~bpo11+1_mipsel.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/a/attr/libattr1_2.4.48-6_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/a/audit/libaudit-common_3.0-2_all.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/a/audit/libaudit1_3.0-2_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/a/avahi/libavahi-client3_0.8-5+deb11u2_mipsel.deb @@ -41,9 +39,7 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/c/cairo/libcairo2-dev_1.16.0-5_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/c/cairo/libcairo2_1.16.0-5_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/c/chromaprint/libchromaprint1_1.5.0-2_mipsel.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/c/cjson/libcjson1_1.7.14-1_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/c/codec2/libcodec2-0.9_0.9.2-4_mipsel.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/c/codec2/libcodec2-1.0_1.0.5-1_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/c/colord/libcolord2_1.4.5-3_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/c/coreutils/coreutils_8.32-4_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/c/cryptsetup/libcryptsetup12_2.3.7-1+deb11u1_mipsel.deb @@ -56,9 +52,7 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/c/cyrus-sasl2/libsasl2-2_2.1.27+dfsg-2.1+deb11u1_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/c/cyrus-sasl2/libsasl2-modules-db_2.1.27+dfsg-2.1+deb11u1_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/d/dav1d/libdav1d4_0.7.1-3_mipsel.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/d/dav1d/libdav1d6_1.0.0-2_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/d/db5.3/libdb5.3_5.3.28+dfsg1-0.8_mipsel.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/d/dbus-glib/libdbus-glib-1-2_0.110-6_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/d/dbus/dbus-bin_1.14.6-1_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/d/dbus/dbus-daemon_1.14.6-1_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/d/dbus/dbus-session-bus-common_1.14.6-1_all.deb @@ -83,16 +77,12 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/e/expat/libexpat1_2.2.10-2+deb11u5_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/f/ffmpeg/libavcodec-dev_4.3.5-0+deb11u1_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/f/ffmpeg/libavcodec58_4.3.5-0+deb11u1_mipsel.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/f/ffmpeg/libavcodec59_5.1.3-1_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/f/ffmpeg/libavformat-dev_4.3.5-0+deb11u1_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/f/ffmpeg/libavformat58_4.3.5-0+deb11u1_mipsel.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/f/ffmpeg/libavformat59_5.1.3-1_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/f/ffmpeg/libavutil-dev_4.3.5-0+deb11u1_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/f/ffmpeg/libavutil56_4.3.5-0+deb11u1_mipsel.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/f/ffmpeg/libavutil57_5.1.3-1_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/f/ffmpeg/libswresample-dev_4.3.5-0+deb11u1_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/f/ffmpeg/libswresample3_4.3.5-0+deb11u1_mipsel.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/f/ffmpeg/libswresample4_5.1.3-1_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/f/flac/libflac-dev_1.3.3-2+deb11u1_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/f/flac/libflac8_1.3.3-2+deb11u1_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/f/fontconfig/fontconfig-config_2.13.1-4.2_all.deb @@ -123,8 +113,6 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/g/gcc-defaults/gcc_10.2.1-1_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/g/gdbm/libgdbm-compat4_1.19-2_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/g/gdbm/libgdbm6_1.19-2_mipsel.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/g/gdk-pixbuf-xlib/libgdk-pixbuf-xlib-2.0-0_2.40.2-2_mipsel.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/g/gdk-pixbuf-xlib/libgdk-pixbuf2.0-0_2.40.2-2_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/g/gdk-pixbuf/gir1.2-gdkpixbuf-2.0_2.42.2+dfsg-1+deb11u1_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/g/gdk-pixbuf/libgdk-pixbuf-2.0-0_2.42.2+dfsg-1+deb11u1_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/g/gdk-pixbuf/libgdk-pixbuf-2.0-dev_2.42.2+dfsg-1+deb11u1_mipsel.deb @@ -139,6 +127,7 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/g/glib2.0/libglib2.0-dev-bin_2.66.8-1_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/g/glib2.0/libglib2.0-dev_2.66.8-1_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/g/glibc/libc-dev-bin_2.31-13+deb11u5_mipsel.deb +https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/g/glibc/libc6-dbg_2.31-13+deb11u5_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/g/glibc/libc6-dev_2.31-13+deb11u5_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/g/glibc/libc6-dev_2.31-13+deb11u6_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/g/glibc/libc6_2.31-13+deb11u5_mipsel.deb @@ -180,8 +169,6 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/h/harfbuzz/libharfbuzz-icu0_2.7.4-1_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/h/harfbuzz/libharfbuzz0b_2.7.4-1_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/h/hicolor-icon-theme/hicolor-icon-theme_0.17-2_all.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/h/highway/libhwy1_1.0.3-3_mipsel.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/i/icu-le-hb/libicu-le-hb0_1.0.3+git180724-3+b2_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/i/icu/icu-devtools_67.1-7_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/i/icu/libicu-dev_67.1-7_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/i/icu/libicu67_67.1-7_mipsel.deb @@ -189,7 +176,6 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/i/isl/libisl23_0.23-1_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/j/jbigkit/libjbig-dev_2.1-3.1+b2_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/j/jbigkit/libjbig0_2.1-3.1+b2_mipsel.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/j/jpeg-xl/libjxl0.7_0.7.0-10_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/j/json-c/libjson-c5_0.15-2_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/j/json-glib/libjson-glib-1.0-0_1.6.2-1_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/j/json-glib/libjson-glib-1.0-common_1.6.2-1_all.deb @@ -230,8 +216,6 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libd/libdbusmenu/gir1.2-dbusmenu-glib-0.4_18.10.20180917~bzr492+repack1-2_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libd/libdbusmenu/libdbusmenu-glib-dev_18.10.20180917~bzr492+repack1-2_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libd/libdbusmenu/libdbusmenu-glib4_18.10.20180917~bzr492+repack1-2_mipsel.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libd/libdbusmenu/libdbusmenu-gtk3-4_18.10.20180917~bzr492+repack1-2_mipsel.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libd/libdbusmenu/libdbusmenu-gtk4_18.10.20180917~bzr492+repack1-2_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libd/libdeflate/libdeflate-dev_1.10-2~bpo11+1_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libd/libdeflate/libdeflate0_1.10-2~bpo11+1_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libd/libdrm/libdrm-amdgpu1_2.4.104-1_mipsel.deb @@ -273,8 +257,6 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libg/libgudev/libgudev-1.0-dev_234-1_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libi/libice/libice-dev_1.0.10-1_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libi/libice/libice6_1.0.10-1_mipsel.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libi/libidl/libidl-2-0_0.8.14-4+b12_mipsel.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libi/libidn/libidn11_1.33-3_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libi/libidn2/libidn2-0_2.3.0-5_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libi/libidn2/libidn2-dev_2.3.0-5_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libi/libinput/libinput-bin_1.16.4-3_mipsel.deb @@ -288,11 +270,9 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libm/libmd/libmd0_1.0.3-3_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libn/libnsl/libnsl-dev_1.3.0-2_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libn/libnsl/libnsl2_1.3.0-2_mipsel.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libn/libnss-db/libnss-db_2.2.3pre1-6+b10_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libo/libogg/libogg-dev_1.3.4-0.1_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libo/libogg/libogg0_1.3.4-0.1_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libo/libopenmpt/libopenmpt0_0.4.11-1_mipsel.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libp/libpciaccess/libpciaccess0_0.16-1_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libp/libpgm/libpgm-5.3-0_5.3.128~dfsg-2_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libp/libpng1.6/libpng-dev_1.6.37-3_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libp/libpng1.6/libpng16-16_1.6.37-3_mipsel.deb @@ -301,7 +281,6 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libp/libpthread-stubs/libpthread-stubs0-dev_0.4-1_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libr/librabbitmq/librabbitmq4_0.10.0-1_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libr/librest/librest-0.7-0_0.8.1-1.1_mipsel.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libr/librist/librist4_0.2.7+dfsg-1_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libr/librsvg/librsvg2-2_2.50.3+dfsg-1_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libs/libseccomp/libseccomp2_2.5.1-1+deb11u1_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libs/libselinux/libselinux1-dev_3.1-3_mipsel.deb @@ -329,7 +308,6 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libt/libtirpc/libtirpc-common_1.3.1-1+deb11u1_all.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libt/libtirpc/libtirpc-dev_1.3.1-1+deb11u1_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libt/libtirpc/libtirpc3_1.3.1-1+deb11u1_mipsel.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libt/libtool/libltdl7_2.4.6-15_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libu/libudfread/libudfread0_1.1.1-1_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libu/libunistring/libunistring2_0.9.10-4_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libu/libutempter/libutempter-dev_1.2.1-2_mipsel.deb @@ -346,7 +324,6 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libv/libvorbis/libvorbisfile3_1.3.7-1_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libv/libvpx/libvpx-dev_1.9.0-1_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libv/libvpx/libvpx6_1.9.0-1_mipsel.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libv/libvpx/libvpx7_1.12.0-1_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libw/libwacom/libwacom-common_1.8-2_all.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libw/libwacom/libwacom-dev_1.8-2_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/libw/libwacom/libwacom2_1.8-2_mipsel.deb @@ -431,20 +408,16 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/make-dfsg/make_4.3-4.1_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/mako/python3-mako_1.1.3+ds1-2_all.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/markupsafe/python3-markupsafe_2.0.1-2~bpo11+1_mipsel.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/mbedtls/libmbedcrypto7_2.28.3-1_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/md4c/libmd4c0_0.4.7-2_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/media-types/media-types_4.0.0_all.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/mesa/libegl-mesa0_20.3.5-1_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/mesa/libegl1-mesa-dev_20.3.5-1_mipsel.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/mesa/libegl1-mesa_20.3.5-1_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/mesa/libgbm-dev_20.3.5-1_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/mesa/libgbm1_20.3.5-1_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/mesa/libgl1-mesa-dev_20.3.5-1_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/mesa/libgl1-mesa-dri_20.3.5-1_mipsel.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/mesa/libgl1-mesa-glx_20.3.5-1_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/mesa/libglapi-mesa_20.3.5-1_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/mesa/libglx-mesa0_20.3.5-1_mipsel.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/mesa/libwayland-egl1-mesa_20.3.5-1_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/mesa/mesa-common-dev_20.3.5-1_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/minizip/libminizip-dev_1.1-8+b1_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/m/minizip/libminizip1_1.1-8+b1_mipsel.deb @@ -489,7 +462,6 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/p/pango1.0/libpangoft2-1.0-0_1.46.2-3_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/p/pango1.0/libpangoxft-1.0-0_1.46.2-3_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/p/pango1.0/pango1.0-tools_1.46.2-3_mipsel.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/p/pangox-compat/libpangox-1.0-0_0.0.2-5.1_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/p/patch/patch_2.7.6-7_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/p/pci.ids/pci.ids_0.0~2021.02.08-1_all.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/p/pciutils/libpci-dev_3.7.0-5_mipsel.deb @@ -564,7 +536,6 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/r/readline/libreadline8_8.1-1_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/r/readline/readline-common_8.1-1_all.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/r/rtmpdump/librtmp1_2.4+20151223.gitfa8646d.1-2+b2_mipsel.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/r/rust-rav1e/librav1e0_0.5.1-6_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/s/sensible-utils/sensible-utils_0.0.14_all.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/s/setuptools/python3-pkg-resources_66.1.1-1~bpo11+1_all.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/s/shadow/login_4.8.1-1_mipsel.deb @@ -578,8 +549,6 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/s/speex/libspeex1_1.2~rc1.2-1.1_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/s/sqlite3/libsqlite3-0_3.34.1-3_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/s/srt/libsrt1.4-gnutls_1.4.2-1.3_mipsel.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/s/srt/libsrt1.5-gnutls_1.5.1-1_mipsel.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/s/svt-av1/libsvtav1enc1_1.4.1+dfsg-1_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/s/systemd/libpam-systemd_252.5-2~bpo11+1_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/s/systemd/libsystemd-dev_252.5-2~bpo11+1_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/s/systemd/libsystemd-shared_252.5-2~bpo11+1_mipsel.deb @@ -608,6 +577,7 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/u/util-linux/mount_2.36.1-8+deb11u1_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/u/util-linux/util-linux_2.36.1-8+deb11u1_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/u/util-linux/uuid-dev_2.36.1-8+deb11u1_mipsel.deb +https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/v/valgrind/valgrind_3.16.1-1_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/v/vulkan-loader/libvulkan-dev_1.3.224.0-1~bpo11+1_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/v/vulkan-loader/libvulkan1_1.3.224.0-1~bpo11+1_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/w/wavpack/libwavpack1_5.4.0-1_mipsel.deb @@ -621,9 +591,7 @@ https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/w/wayland/libwayland-server0_1.18.0-2~exp1.1_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/w/webrtc-audio-processing/libwebrtc-audio-processing1_0.3-1+b1_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/x/x264/libx264-160_0.160.3011+gitcde9a93-2.1_mipsel.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/x/x264/libx264-164_0.164.3095+gitbaee400-3_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/x/x265/libx265-192_3.4-2_mipsel.deb -https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/x/x265/libx265-199_3.5-2+b1_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/x/xcb-util-image/libxcb-image0-dev_0.4.0-1+b3_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/x/xcb-util-image/libxcb-image0_0.4.0-1+b3_mipsel.deb https://snapshot.debian.org/archive/debian/20230611T210420Z/pool/main/x/xcb-util-keysyms/libxcb-keysyms1_0.4.0-1+b2_mipsel.deb
diff --git a/build/linux/sysroot_scripts/sysroot_creator.py b/build/linux/sysroot_scripts/sysroot_creator.py index 0b8b9ae..ee4378c 100755 --- a/build/linux/sysroot_scripts/sysroot_creator.py +++ b/build/linux/sysroot_scripts/sysroot_creator.py
@@ -7,6 +7,7 @@ """ import argparse +import collections import hashlib import lzma import os @@ -71,524 +72,69 @@ RELEASE_FILE = "Release" RELEASE_FILE_GPG = "Release.gpg" -# Packages common to all architectures. +# List of development packages. Dependencies are automatically included. DEBIAN_PACKAGES = [ - "comerr-dev", - "krb5-multidev", - "libaom0", - "libaom3", - "libasound2", "libasound2-dev", - "libasyncns0", - "libatk-bridge2.0-0", - "libatk-bridge2.0-dev", - "libatk1.0-0", - "libatk1.0-dev", - "libatomic1", - "libatspi2.0-0", - "libatspi2.0-dev", - "libattr1", - "libaudit1", - "libavahi-client3", - "libavahi-common3", - "libavcodec-dev", - "libavcodec58", - "libavcodec59", "libavformat-dev", - "libavformat59", - "libavutil-dev", - "libavutil56", - "libavutil57", - "libb2-1", - "libblkid-dev", - "libblkid1", "libbluetooth-dev", - "libbluetooth3", - "libbluray2", - "libbrotli-dev", - "libbrotli1", - "libbsd0", - "libbz2-1.0", - "libc6", "libc6-dev", - "libcairo-gobject2", - "libcairo-script-interpreter2", - "libcairo2", - "libcairo2-dev", "libcap-dev", - "libcap-ng0", - "libcap2", - "libchromaprint1", - "libcjson1", - "libcloudproviders0", - "libcodec2-0.9", - "libcodec2-1.0", - "libcolord2", - "libcom-err2", - "libcrypt-dev", - "libcrypt1", - "libcups2", "libcups2-dev", - "libcupsimage2", "libcupsimage2-dev", - "libcurl3-gnutls", "libcurl4-gnutls-dev", - "libdatrie-dev", - "libdatrie1", - "libdav1d4", - "libdav1d6", - "libdb5.3", - "libdbus-1-3", - "libdbus-1-dev", - "libdbus-glib-1-2", "libdbusmenu-glib-dev", - "libdbusmenu-glib4", - "libdbusmenu-gtk3-4", - "libdbusmenu-gtk4", "libdeflate-dev", - "libdeflate0", - "libdouble-conversion3", - "libdrm-amdgpu1", - "libdrm-dev", - "libdrm-nouveau2", - "libdrm-radeon1", - "libdrm2", - "libegl-dev", - "libegl1", - "libegl1-mesa", - "libegl1-mesa-dev", "libelf-dev", - "libelf1", - "libepoxy-dev", - "libepoxy0", - "libevdev-dev", - "libevdev2", - "libevent-2.1-7", - "libexpat1", - "libexpat1-dev", - "libffi-dev", - "libffi7", "libflac-dev", - "libflac8", - "libfontconfig-dev", - "libfontconfig1", - "libfreetype-dev", - "libfreetype6", - "libfribidi-dev", - "libfribidi0", "libgbm-dev", - "libgbm1", - "libgcc-10-dev", - "libgcc-s1", - "libgcrypt20", "libgcrypt20-dev", - "libgdk-pixbuf-2.0-0", - "libgdk-pixbuf-2.0-dev", - "libgl-dev", - "libgl1", - "libgl1-mesa-dev", - "libgl1-mesa-glx", - "libglapi-mesa", - "libgles-dev", - "libgles1", - "libgles2", - "libglib2.0-0", - "libglib2.0-dev", - "libglvnd-dev", - "libglvnd0", - "libglx-dev", - "libglx0", - "libgme0", - "libgmp10", - "libgnutls-dane0", - "libgnutls-openssl27", "libgnutls28-dev", - "libgnutls30", - "libgnutlsxx28", - "libgomp1", - "libgpg-error-dev", - "libgpg-error0", - "libgraphene-1.0-0", - "libgraphene-1.0-dev", - "libgraphite2-3", - "libgraphite2-dev", - "libgsm1", - "libgssapi-krb5-2", - "libgssrpc4", - "libgtk-3-0", "libgtk-3-dev", - "libgtk-4-1", "libgtk-4-dev", - "libgtk2.0-0", - "libgudev-1.0-0", - "libharfbuzz-dev", - "libharfbuzz-gobject0", - "libharfbuzz-icu0", - "libharfbuzz0b", - "libhogweed6", - "libhwy1", - "libice6", - "libicu-le-hb0", - "libicu67", - "libidl-2-0", - "libidn11", - "libidn2-0", "libinput-dev", - "libinput10", "libjbig-dev", - "libjbig0", - "libjpeg62-turbo", - "libjpeg62-turbo-dev", - "libjson-glib-1.0-0", + "libjpeg-dev", "libjsoncpp-dev", - "libjsoncpp24", - "libjxl0.7", - "libjxl0.7", - "libk5crypto3", - "libkadm5clnt-mit12", - "libkadm5srv-mit12", - "libkdb5-10", - "libkeyutils1", - "libkrb5-3", "libkrb5-dev", - "libkrb5support0", - "liblcms2-2", - "libldap-2.4-2", - "liblerc4", - "libltdl7", - "liblz4-1", - "liblzma5", - "liblzo2-2", - "libmbedcrypto7", - "libmd0", - "libmd4c0", + "liblzma-dev", "libminizip-dev", - "libminizip1", - "libmount-dev", - "libmount1", - "libmp3lame0", - "libmpg123-0", - "libmtdev1", "libncurses-dev", - "libncurses6", - "libncursesw6", - "libnettle8", - "libnghttp2-14", - "libnorm1", - "libnsl2", - "libnspr4", - "libnspr4-dev", - "libnss-db", - "libnss3", "libnss3-dev", - "libnuma1", - "libogg-dev", - "libogg0", - "libopengl0", - "libopenjp2-7", - "libopenmpt0", "libopus-dev", - "libopus-dev", - "libopus0", - "libp11-kit0", - "libpam0g", "libpam0g-dev", - "libpango-1.0-0", - "libpango1.0-dev", - "libpangocairo-1.0-0", - "libpangoft2-1.0-0", - "libpangox-1.0-0", - "libpangoxft-1.0-0", "libpci-dev", - "libpci3", - "libpciaccess0", - "libpcre16-3", - "libpcre2-16-0", - "libpcre2-32-0", - "libpcre2-8-0", - "libpcre2-dev", - "libpcre2-posix2", - "libpcre3", - "libpcre3-dev", - "libpcre32-3", - "libpcrecpp0v5", - "libpgm-5.3-0", - "libpipewire-0.3-0", "libpipewire-0.3-dev", - "libpixman-1-0", - "libpixman-1-dev", - "libpng-dev", - "libpng16-16", - "libproxy1v5", - "libpsl5", - "libpthread-stubs0-dev", "libpulse-dev", - "libpulse-mainloop-glib0", - "libpulse0", - "libqt5concurrent5", - "libqt5core5a", - "libqt5dbus5", - "libqt5gui5", - "libqt5network5", - "libqt5printsupport5", - "libqt5sql5", - "libqt5test5", - "libqt5widgets5", - "libqt5xml5", - "libqt6concurrent6", - "libqt6core6", - "libqt6dbus6", - "libqt6gui6", - "libqt6network6", - "libqt6opengl6", - "libqt6openglwidgets6", - "libqt6printsupport6", - "libqt6sql6", - "libqt6test6", - "libqt6widgets6", - "libqt6xml6", - "librabbitmq4", - "librav1e0", - "libre2-9", "libre2-dev", - "librest-0.7-0", - "librist4", - "librsvg2-2", - "librtmp1", - "libsasl2-2", - "libselinux1", - "libselinux1-dev", - "libsepol1", - "libsepol1-dev", - "libshine3", - "libsm6", "libsnappy-dev", - "libsnappy1v5", - "libsndfile1", - "libsodium23", - "libsoup-gnome2.4-1", - "libsoup2.4-1", - "libsoxr0", - "libspa-0.2-dev", "libspeechd-dev", - "libspeechd2", - "libspeex1", - "libsqlite3-0", - "libsrt1.5-gnutls", - "libssh-gcrypt-4", - "libssh2-1", "libssl-dev", - "libssl1.1", - "libstdc++-10-dev", - "libstdc++6", - "libsvtav1enc1", - "libswresample-dev", - "libswresample3", - "libswresample4", "libsystemd-dev", - "libsystemd0", - "libtasn1-6", - "libthai-dev", - "libthai0", - "libtheora0", - "libtheora0", "libtiff-dev", - "libtiff5", - "libtiff6", - "libtiffxx5", - "libtinfo6", - "libtirpc3", - "libts0", - "libtwolame0", - "libudev-dev", - "libudev1", - "libudfread0", - "libunbound8", - "libunistring2", "libutempter-dev", - "libutempter0", - "libuuid1", "libva-dev", - "libva-drm2", - "libva-glx2", - "libva-wayland2", - "libva-x11-2", - "libva2", - "libvdpau1", - "libvorbis0a", - "libvorbisenc2", - "libvorbisfile3", "libvpx-dev", - "libvpx6", - "libvpx7", - "libvulkan-dev", - "libvulkan1", - "libwacom2", - "libwavpack1", - "libwayland-bin", - "libwayland-client0", - "libwayland-cursor0", - "libwayland-dev", "libwayland-egl-backend-dev", - "libwayland-egl1", - "libwayland-egl1-mesa", - "libwayland-server0", "libwebp-dev", - "libwebp6", - "libwebp7", - "libwebpdemux2", - "libwebpmux3", - "libwrap0", - "libx11-6", - "libx11-dev", "libx11-xcb-dev", - "libx11-xcb1", - "libx264-160", - "libx264-164", - "libx265-192", - "libx265-199", - "libxau-dev", - "libxau6", - "libxcb-dri2-0", "libxcb-dri2-0-dev", - "libxcb-dri3-0", "libxcb-dri3-dev", - "libxcb-glx0", "libxcb-glx0-dev", - "libxcb-icccm4", - "libxcb-image0", "libxcb-image0-dev", - "libxcb-keysyms1", "libxcb-present-dev", - "libxcb-present0", - "libxcb-randr0", - "libxcb-randr0-dev", - "libxcb-render-util0", "libxcb-render-util0-dev", - "libxcb-render0", - "libxcb-render0-dev", - "libxcb-shape0", - "libxcb-shape0-dev", - "libxcb-shm0", - "libxcb-shm0-dev", - "libxcb-sync-dev", - "libxcb-sync1", "libxcb-util-dev", - "libxcb-util1", - "libxcb-xfixes0", - "libxcb-xfixes0-dev", - "libxcb-xinerama0", - "libxcb-xinput0", - "libxcb-xkb1", - "libxcb1", - "libxcb1-dev", - "libxcomposite-dev", - "libxcomposite1", - "libxcursor-dev", - "libxcursor1", - "libxdamage-dev", - "libxdamage1", - "libxdmcp-dev", - "libxdmcp6", - "libxext-dev", - "libxext6", - "libxfixes-dev", - "libxfixes3", - "libxft-dev", - "libxft2", - "libxi-dev", - "libxi6", - "libxinerama-dev", - "libxinerama1", - "libxkbcommon-dev", - "libxkbcommon-x11-0", - "libxkbcommon0", - "libxml2", - "libxml2-dev", - "libxrandr-dev", - "libxrandr2", - "libxrender-dev", - "libxrender1", "libxshmfence-dev", - "libxshmfence1", "libxslt1-dev", - "libxslt1.1", "libxss-dev", - "libxss1", "libxt-dev", - "libxt6", - "libxtst-dev", - "libxtst6", - "libxvidcore4", "libxxf86vm-dev", - "libxxf86vm1", - "libzmq5", - "libzstd1", - "libzvbi0", - "linux-libc-dev", "mesa-common-dev", - "ocl-icd-libopencl1", "qt6-base-dev", - "qt6-base-dev-tools", "qtbase5-dev", - "qtbase5-dev-tools", - "shared-mime-info", - "uuid-dev", - "wayland-protocols", - "x11proto-dev", - "zlib1g", - "zlib1g-dev", + "valgrind", ] -DEBIAN_PACKAGES_ARCH = { - "amd64": [ - "libasan6", - "libdrm-intel1", - "libitm1", - "liblsan0", - "libmfx1", - "libquadmath0", - "libtsan0", - "libubsan1", - "valgrind", - ], - "i386": [ - "libasan6", - "libdrm-intel1", - "libitm1", - "libquadmath0", - "libubsan1", - "valgrind", - ], - "armhf": [ - "libasan6", - "libdrm-etnaviv1", - "libdrm-exynos1", - "libdrm-freedreno1", - "libdrm-omap1", - "libdrm-tegra0", - "libubsan1", - "valgrind", - ], - "arm64": [ - "libasan6", - "libdrm-etnaviv1", - "libdrm-freedreno1", - "libdrm-tegra0", - "libgmp10", - "libitm1", - "liblsan0", - "libthai0", - "libtsan0", - "libubsan1", - "valgrind", - ], - "mipsel": [], - "mips64el": [ - "valgrind", - ], -} - def banner(message: str) -> None: print("#" * 70) @@ -775,7 +321,7 @@ # Read the input file and create a dictionary mapping package names to URLs # and checksums. - missing = set(DEBIAN_PACKAGES + DEBIAN_PACKAGES_ARCH[arch]) + missing = set(DEBIAN_PACKAGES) package_dict: dict[str, str] = {} for meta in package_meta.values(): package = meta["Package"] @@ -796,6 +342,11 @@ def hacks_and_patches(install_root: str, script_dir: str, arch: str) -> None: banner("Misc Hacks & Patches") + debian_dir = os.path.join(install_root, "debian") + control_file = os.path.join(debian_dir, "control") + # Create an empty control file + open(control_file, "a").close() + # Remove an unnecessary dependency on qtchooser. qtchooser_conf = os.path.join(install_root, "usr", "lib", TRIPLES[arch], "qt-default/qtchooser/default.conf") @@ -852,10 +403,6 @@ debian_dir = os.path.join(install_root, "debian") os.makedirs(debian_dir, exist_ok=True) - control_file = os.path.join(debian_dir, "control") - # Create an empty control file - open(control_file, "a").close() - for package, sha256sum in packages.items(): package_name = os.path.basename(package) package_path = os.path.join(debian_packages_dir, package_name) @@ -953,6 +500,42 @@ os.symlink(relative_path, full_path) +def removing_unnecessary_files(install_root, arch): + """ + Minimizes the sysroot by removing unnecessary files. + """ + # Preserve these files. + ALLOWLIST = { + "usr/bin/cups-config", + f"usr/lib/gcc/{TRIPLES[arch]}/10/libgcc.a", + f"usr/lib/{TRIPLES[arch]}/libc_nonshared.a", + f"usr/lib/{TRIPLES[arch]}/libffi_pic.a", + } + + # Remove all executables and static libraries, and any symlinks that + # were pointing to them. + reverse_links = collections.defaultdict(list) + remove = [] + for root, _, files in os.walk(install_root): + for filename in files: + filepath = os.path.join(root, filename) + if os.path.relpath(filepath, install_root) in ALLOWLIST: + continue + if os.path.islink(filepath): + target_path = os.readlink(filepath) + if not os.path.isabs(target_path): + target_path = os.path.join(root, target_path) + reverse_links[os.path.realpath(target_path)].append(filepath) + elif "so" in filepath.split(".")[-3:]: + continue + elif os.access(filepath, os.X_OK) or filepath.endswith(".a"): + remove.append(filepath) + for filepath in remove: + os.remove(filepath) + for link in reverse_links[filepath]: + os.remove(link) + + def strip_sections(install_root: str): """ Strips all sections from ELF files except for dynamic linking and @@ -1008,14 +591,63 @@ subprocess.run(objcopy_cmd, check=True, stderr=subprocess.PIPE) +def record_metadata(install_root: str) -> dict[str, tuple[float, float]]: + """ + Recursively walk the install_root directory and record the metadata of all + files. Symlinks are not followed. Returns a dictionary mapping each path + (relative to install_root) to its original metadata. + """ + metadata = {} + for root, dirs, files in os.walk(install_root): + for name in dirs + files: + full_path = os.path.join(root, name) + rel_path = os.path.relpath(full_path, install_root) + st = os.lstat(full_path) + metadata[rel_path] = (st.st_atime, st.st_mtime) + return metadata + + +def restore_metadata(install_root: str, + old_meta: dict[str, tuple[float, float]]) -> None: + """ + 1. Restore the metadata of any file that exists in old_meta. + 2. For all other files, set their timestamp to ARCHIVE_TIMESTAMP. + 3. For all directories (including install_root), set the timestamp + to ARCHIVE_TIMESTAMP. + """ + # Convert the timestamp to a UNIX epoch time. + archive_time = time.mktime( + time.strptime(ARCHIVE_TIMESTAMP, "%Y%m%dT%H%M%SZ")) + + # Walk through the install_root, applying old_meta where available; + # otherwise set times to archive_time. + for root, dirs, files in os.walk(install_root): + # Directories get archive_time. + os.utime(root, (archive_time, archive_time)) + + # Files: old_meta if available, else archive_time. + for file_name in files: + file_path = os.path.join(root, file_name) + if os.path.lexists(file_path): + rel_path = os.path.relpath(file_path, install_root) + if rel_path in old_meta: + restore_time = old_meta[rel_path] + else: + restore_time = (archive_time, archive_time) + os.utime(file_path, restore_time, follow_symlinks=False) + + def build_sysroot(arch: str) -> None: install_root = os.path.join(BUILD_DIR, f"{RELEASE}_{arch}_staging") clear_install_dir(install_root) packages = generate_package_list(arch) install_into_sysroot(BUILD_DIR, install_root, packages) + old_metadata = record_metadata(install_root) hacks_and_patches(install_root, SCRIPT_DIR, arch) cleanup_jail_symlinks(install_root) + removing_unnecessary_files(install_root, arch) strip_sections(install_root) + restore_metadata(install_root, old_metadata) create_tarball(install_root, arch)
diff --git a/cc/BUILD.gn b/cc/BUILD.gn index 8a08fed6..c276f8e 100644 --- a/cc/BUILD.gn +++ b/cc/BUILD.gn
@@ -518,6 +518,8 @@ "test/animation_test_common.h", "test/animation_timelines_test_common.cc", "test/animation_timelines_test_common.h", + "test/fake_compositor_delegate_for_input.cc", + "test/fake_compositor_delegate_for_input.h", "test/fake_compositor_frame_reporting_controller.cc", "test/fake_compositor_frame_reporting_controller.h", "test/fake_content_layer_client.cc", @@ -589,6 +591,8 @@ "test/layer_tree_test.h", "test/lottie_test_data.cc", "test/lottie_test_data.h", + "test/mock_input_handler.cc", + "test/mock_input_handler.h", "test/mock_latency_info_swap_promise_monitor.cc", "test/mock_latency_info_swap_promise_monitor.h", "test/mock_layer_tree_mutator.cc",
diff --git a/cc/layers/heads_up_display_layer_impl.cc b/cc/layers/heads_up_display_layer_impl.cc index 9488375..7677872 100644 --- a/cc/layers/heads_up_display_layer_impl.cc +++ b/cc/layers/heads_up_display_layer_impl.cc
@@ -42,7 +42,6 @@ #include "components/viz/common/gpu/raster_context_provider.h" #include "components/viz/common/quads/solid_color_draw_quad.h" #include "components/viz/common/quads/texture_draw_quad.h" -#include "components/viz/common/resources/bitmap_allocation.h" #include "components/viz/common/resources/platform_color.h" #include "components/viz/common/resources/shared_image_format.h" #include "gpu/GLES2/gl2extchromium.h" @@ -174,14 +173,8 @@ class HudSoftwareBacking : public ResourcePool::SoftwareBacking { public: ~HudSoftwareBacking() override { - if (shared_image) { - auto sii = layer_tree_frame_sink->shared_image_interface(); - if (sii) { - sii->DestroySharedImage(mailbox_sync_token, std::move(shared_image)); - } - } else { - layer_tree_frame_sink->DidDeleteSharedBitmap(shared_bitmap_id); - } + DCHECK(shared_image); + shared_image->UpdateDestructionSyncToken(mailbox_sync_token); } void OnMemoryDump( @@ -338,39 +331,20 @@ DCHECK_EQ(draw_mode, DRAW_MODE_SOFTWARE); auto sii = layer_tree_frame_sink->shared_image_interface(); - if (sii) { - pool_resource = pool_->AcquireResource(internal_content_bounds_, - viz::SinglePlaneFormat::kBGRA_8888, - gfx::ColorSpace()); + DCHECK(sii); + pool_resource = pool_->AcquireResource(internal_content_bounds_, + viz::SinglePlaneFormat::kBGRA_8888, + gfx::ColorSpace()); - if (!pool_resource.software_backing()) { - auto backing = std::make_unique<HudSoftwareBacking>(); - backing->layer_tree_frame_sink = layer_tree_frame_sink; - backing->shared_image = sii->CreateSharedImageForSoftwareCompositor( - {pool_resource.format(), pool_resource.size(), - pool_resource.color_space(), - gpu::SHARED_IMAGE_USAGE_CPU_WRITE_ONLY, "HeadsUpDisplayLayer"}); - CHECK(backing->shared_image); - pool_resource.set_software_backing(std::move(backing)); - } - - } else { - pool_resource = pool_->AcquireResource(internal_content_bounds_, - viz::SinglePlaneFormat::kRGBA_8888, - gfx::ColorSpace()); - if (!pool_resource.software_backing()) { - auto backing = std::make_unique<HudSoftwareBacking>(); - backing->layer_tree_frame_sink = layer_tree_frame_sink; - backing->shared_bitmap_id = viz::SharedBitmap::GenerateId(); - base::MappedReadOnlyRegion shm = - viz::bitmap_allocation::AllocateSharedBitmap( - pool_resource.size(), pool_resource.format()); - backing->shared_mapping = std::move(shm.mapping); - - layer_tree_frame_sink->DidAllocateSharedBitmap( - std::move(shm.region), backing->shared_bitmap_id); - pool_resource.set_software_backing(std::move(backing)); - } + if (!pool_resource.software_backing()) { + auto backing = std::make_unique<HudSoftwareBacking>(); + backing->layer_tree_frame_sink = layer_tree_frame_sink; + backing->shared_image = sii->CreateSharedImageForSoftwareCompositor( + {pool_resource.format(), pool_resource.size(), + pool_resource.color_space(), gpu::SHARED_IMAGE_USAGE_CPU_WRITE_ONLY, + "HeadsUpDisplayLayer"}); + CHECK(backing->shared_image); + pool_resource.set_software_backing(std::move(backing)); } } @@ -462,9 +436,7 @@ DrawHudContents(&canvas); auto sii = layer_tree_frame_sink->shared_image_interface(); - if (backing->shared_image && sii) { - backing->mailbox_sync_token = sii->GenVerifiedSyncToken(); - } + backing->mailbox_sync_token = sii->GenVerifiedSyncToken(); } // Exports the backing to the ResourceProvider, giving it a ResourceId that
diff --git a/cc/layers/texture_layer_unittest.cc b/cc/layers/texture_layer_unittest.cc index c74b6413..3a08a91 100644 --- a/cc/layers/texture_layer_unittest.cc +++ b/cc/layers/texture_layer_unittest.cc
@@ -1500,18 +1500,21 @@ scoped_refptr<viz::RasterContextProvider> compositor_context_provider, scoped_refptr<viz::RasterContextProvider> worker_context_provider) override { + context_provider_sw_ = viz::TestContextProvider::CreateRaster(); + gpu::SharedImageInterface* shared_image_interface_sw = + context_provider_sw_->SharedImageInterface(); + constexpr bool disable_display_vsync = false; bool synchronous_composite = !HasImplThread() && !layer_tree_host()->GetSettings().single_thread_proxy_scheduler; auto sink = std::make_unique<TestLayerTreeFrameSink>( - nullptr, nullptr, /*shared_image_interface=*/nullptr, + nullptr, nullptr, shared_image_interface_sw, gpu_memory_buffer_manager(), renderer_settings, &debug_settings_, task_runner_provider(), synchronous_composite, disable_display_vsync, refresh_rate); frame_sink_ = sink.get(); num_frame_sinks_created_++; - shared_image_interface_ = frame_sink_->GetSharedImageInterface(); return sink; } @@ -1523,7 +1526,7 @@ nullptr; int num_frame_sinks_created_ = 0; - scoped_refptr<gpu::SharedImageInterface> shared_image_interface_; + scoped_refptr<viz::RasterContextProvider> context_provider_sw_; }; class SoftwareTextureLayerSwitchTreesTest : public SoftwareTextureLayerTest { @@ -1542,8 +1545,8 @@ scoped_refptr<gpu::ClientSharedImage> shared_image_; gpu::SyncToken sync_token_; gfx::Size size(1, 1); - AllocateSharedImage(shared_image_interface_.get(), size, shared_image_, - sync_token_); + AllocateSharedImage(frame_sink_->GetSharedImageInterface(), size, + shared_image_, sync_token_); auto transferable_resource = viz::TransferableResource::MakeSoftwareSharedImage( shared_image_, sync_token_, shared_image_->size(), @@ -1612,8 +1615,8 @@ scoped_refptr<gpu::ClientSharedImage> shared_image_; gpu::SyncToken sync_token_; gfx::Size size(1, 1); - AllocateSharedImage(shared_image_interface_.get(), size, shared_image_, - sync_token_); + AllocateSharedImage(frame_sink_->GetSharedImageInterface(), size, + shared_image_, sync_token_); auto transferable_resource = viz::TransferableResource::MakeSoftwareSharedImage( shared_image_, sync_token_, shared_image_->size(), @@ -1688,10 +1691,10 @@ scoped_refptr<gpu::ClientSharedImage> shared_image2_; gpu::SyncToken sync_token2_; gfx::Size size(1, 1); - AllocateSharedImage(shared_image_interface_.get(), size, shared_image1_, - sync_token1_); - AllocateSharedImage(shared_image_interface_.get(), size, shared_image2_, - sync_token2_); + AllocateSharedImage(frame_sink_->GetSharedImageInterface(), size, + shared_image1_, sync_token1_); + AllocateSharedImage(frame_sink_->GetSharedImageInterface(), size, + shared_image2_, sync_token2_); auto transferable_resource1 = viz::TransferableResource::MakeSoftwareSharedImage( shared_image1_, sync_token1_, shared_image1_->size(), @@ -1772,8 +1775,8 @@ scoped_refptr<gpu::ClientSharedImage> shared_image_; gpu::SyncToken sync_token_; gfx::Size size(1, 1); - AllocateSharedImage(shared_image_interface_.get(), size, shared_image_, - sync_token_); + AllocateSharedImage(frame_sink_->GetSharedImageInterface(), size, + shared_image_, sync_token_); auto transferable_resource = viz::TransferableResource::MakeSoftwareSharedImage( shared_image_, sync_token_, shared_image_->size(),
diff --git a/cc/metrics/compositor_frame_reporter.cc b/cc/metrics/compositor_frame_reporter.cc index eb42b36f..46afa59 100644 --- a/cc/metrics/compositor_frame_reporter.cc +++ b/cc/metrics/compositor_frame_reporter.cc
@@ -2,11 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifdef UNSAFE_BUFFERS_BUILD -// TODO(crbug.com/351564777): Remove this and convert code to safer constructs. -#pragma allow_unsafe_buffers -#endif - #include "cc/metrics/compositor_frame_reporter.h" #include <algorithm>
diff --git a/cc/metrics/compositor_frame_reporter.h b/cc/metrics/compositor_frame_reporter.h index be20c1d7..c4bd318 100644 --- a/cc/metrics/compositor_frame_reporter.h +++ b/cc/metrics/compositor_frame_reporter.h
@@ -5,6 +5,7 @@ #ifndef CC_METRICS_COMPOSITOR_FRAME_REPORTER_H_ #define CC_METRICS_COMPOSITOR_FRAME_REPORTER_H_ +#include <array> #include <bitset> #include <deque> #include <memory> @@ -231,7 +232,9 @@ Iterator CreateIterator() const; private: - base::TimeDelta list_[static_cast<int>(BlinkBreakdown::kBreakdownCount)]; + std::array<base::TimeDelta, + static_cast<size_t>(BlinkBreakdown::kBreakdownCount)> + list_; }; // Holds a processed list of Viz breakdowns with an `Iterator` class to easily @@ -279,8 +282,9 @@ base::TimeTicks swap_start() const { return swap_start_; } private: - std::optional<std::pair<base::TimeTicks, base::TimeTicks>> - list_[static_cast<int>(VizBreakdown::kBreakdownCount)]; + std::array<std::optional<std::pair<base::TimeTicks, base::TimeTicks>>, + static_cast<size_t>(VizBreakdown::kBreakdownCount)> + list_; bool buffer_ready_available_ = false; base::TimeTicks swap_start_;
diff --git a/cc/raster/bitmap_raster_buffer_provider.cc b/cc/raster/bitmap_raster_buffer_provider.cc index 6a2f0d6..6d695ef 100644 --- a/cc/raster/bitmap_raster_buffer_provider.cc +++ b/cc/raster/bitmap_raster_buffer_provider.cc
@@ -15,7 +15,6 @@ #include "base/trace_event/traced_value.h" #include "cc/trees/layer_tree_frame_sink.h" #include "components/viz/client/client_resource_provider.h" -#include "components/viz/common/resources/bitmap_allocation.h" #include "components/viz/common/resources/shared_image_format.h" #include "gpu/command_buffer/client/client_shared_image.h" #include "gpu/command_buffer/common/shared_image_usage.h" @@ -28,14 +27,8 @@ class BitmapSoftwareBacking : public ResourcePool::SoftwareBacking { public: ~BitmapSoftwareBacking() override { - if (shared_image) { - auto sii = frame_sink->shared_image_interface(); - if (sii) { - sii->DestroySharedImage(mailbox_sync_token, std::move(shared_image)); - } - } else { - frame_sink->DidDeleteSharedBitmap(shared_bitmap_id); - } + DCHECK(shared_image); + shared_image->UpdateDestructionSyncToken(mailbox_sync_token); } void OnMemoryDump( @@ -111,7 +104,10 @@ BitmapRasterBufferProvider::BitmapRasterBufferProvider( LayerTreeFrameSink* frame_sink) - : frame_sink_(frame_sink) {} + : frame_sink_(frame_sink) { + auto sii = frame_sink_->shared_image_interface(); + CHECK(sii) << "BitmapRasterBufferProvider() SharedImageInterface is null!"; +} BitmapRasterBufferProvider::~BitmapRasterBufferProvider() = default; @@ -133,24 +129,16 @@ auto backing = std::make_unique<BitmapSoftwareBacking>(); backing->frame_sink = frame_sink_; auto sii = frame_sink_->shared_image_interface(); - if (sii) { - auto shared_image_mapping = sii->CreateSharedImage( - {viz::SinglePlaneFormat::kBGRA_8888, size, color_space, - gpu::SHARED_IMAGE_USAGE_CPU_WRITE_ONLY, - "BitmapRasterBufferProvider"}); - backing->shared_image = std::move(shared_image_mapping.shared_image); - CHECK(backing->shared_image); - backing->mapping = std::move(shared_image_mapping.mapping); - backing->mailbox_sync_token = sii->GenVerifiedSyncToken(); - } else { - backing->shared_bitmap_id = viz::SharedBitmap::GenerateId(); - base::MappedReadOnlyRegion shm = - viz::bitmap_allocation::AllocateSharedBitmap( - size, viz::SinglePlaneFormat::kRGBA_8888); - backing->mapping = std::move(shm.mapping); - frame_sink_->DidAllocateSharedBitmap(std::move(shm.region), - backing->shared_bitmap_id); - } + CHECK(sii) << "SharedImageInterface is null!"; + + auto shared_image_mapping = sii->CreateSharedImage( + {viz::SinglePlaneFormat::kBGRA_8888, size, color_space, + gpu::SHARED_IMAGE_USAGE_CPU_WRITE_ONLY, "BitmapRasterBufferProvider"}); + backing->shared_image = std::move(shared_image_mapping.shared_image); + CHECK(backing->shared_image); + + backing->mapping = std::move(shared_image_mapping.mapping); + backing->mailbox_sync_token = sii->GenVerifiedSyncToken(); resource.set_software_backing(std::move(backing)); }
diff --git a/cc/test/fake_compositor_delegate_for_input.cc b/cc/test/fake_compositor_delegate_for_input.cc new file mode 100644 index 0000000..1ae6943 --- /dev/null +++ b/cc/test/fake_compositor_delegate_for_input.cc
@@ -0,0 +1,56 @@ +// Copyright 2025 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "cc/test/fake_compositor_delegate_for_input.h" + +namespace cc { + +FakeCompositorDelegateForInput::FakeCompositorDelegateForInput() + : host_impl_(&task_runner_provider_, &task_graph_runner_) {} + +FakeCompositorDelegateForInput::~FakeCompositorDelegateForInput() = default; + +ScrollTree& FakeCompositorDelegateForInput::GetScrollTree() const { + return scroll_tree_; +} + +bool FakeCompositorDelegateForInput::HasAnimatedScrollbars() const { + return false; +} + +bool FakeCompositorDelegateForInput::IsInHighLatencyMode() const { + return false; +} + +float FakeCompositorDelegateForInput::DeviceScaleFactor() const { + return 0; +} + +float FakeCompositorDelegateForInput::PageScaleFactor() const { + return 0; +} + +gfx::Size FakeCompositorDelegateForInput::VisualDeviceViewportSize() const { + return gfx::Size(); +} + +const LayerTreeSettings& FakeCompositorDelegateForInput::GetSettings() const { + return settings_; +} + +LayerTreeHostImpl& FakeCompositorDelegateForInput::GetImplDeprecated() { + return host_impl_; +} + +const LayerTreeHostImpl& FakeCompositorDelegateForInput::GetImplDeprecated() + const { + return host_impl_; +} + +bool FakeCompositorDelegateForInput::HasScrollLinkedAnimation( + ElementId for_scroller) const { + return false; +} + +} // namespace cc
diff --git a/cc/test/fake_compositor_delegate_for_input.h b/cc/test/fake_compositor_delegate_for_input.h new file mode 100644 index 0000000..d76ac74 --- /dev/null +++ b/cc/test/fake_compositor_delegate_for_input.h
@@ -0,0 +1,70 @@ +// Copyright 2025 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CC_TEST_FAKE_COMPOSITOR_DELEGATE_FOR_INPUT_H_ +#define CC_TEST_FAKE_COMPOSITOR_DELEGATE_FOR_INPUT_H_ + +#include <memory> + +#include "base/types/optional_ref.h" +#include "cc/input/browser_controls_offset_tags_info.h" +#include "cc/input/browser_controls_state.h" +#include "cc/input/compositor_input_interfaces.h" +#include "cc/paint/element_id.h" +#include "cc/test/fake_impl_task_runner_provider.h" +#include "cc/test/fake_layer_tree_host_impl.h" +#include "cc/test/test_task_graph_runner.h" +#include "cc/trees/layer_tree_settings.h" +#include "cc/trees/property_tree.h" +#include "ui/gfx/geometry/size.h" + +namespace cc { + +class FakeCompositorDelegateForInput : public CompositorDelegateForInput { + public: + FakeCompositorDelegateForInput(); + ~FakeCompositorDelegateForInput() override; + void BindToInputHandler( + std::unique_ptr<InputDelegateForCompositor> delegate) override {} + ScrollTree& GetScrollTree() const override; + bool HasAnimatedScrollbars() const override; + void SetNeedsCommit() override {} + void SetNeedsFullViewportRedraw() override {} + void SetDeferBeginMainFrame(bool defer_begin_main_frame) const override {} + void DidUpdateScrollAnimationCurve() override {} + void AccumulateScrollDeltaForTracing(const gfx::Vector2dF& delta) override {} + void DidStartPinchZoom() override {} + void DidUpdatePinchZoom() override {} + void DidEndPinchZoom() override {} + void DidStartScroll() override {} + void DidEndScroll() override {} + void DidMouseLeave() override {} + bool IsInHighLatencyMode() const override; + void WillScrollContent(ElementId element_id) override {} + void DidScrollContent(ElementId element_id, bool animated) override {} + float DeviceScaleFactor() const override; + float PageScaleFactor() const override; + gfx::Size VisualDeviceViewportSize() const override; + const LayerTreeSettings& GetSettings() const override; + LayerTreeHostImpl& GetImplDeprecated() override; + const LayerTreeHostImpl& GetImplDeprecated() const override; + void UpdateBrowserControlsState( + BrowserControlsState constraints, + BrowserControlsState current, + bool animate, + base::optional_ref<const BrowserControlsOffsetTagsInfo> offset_tags_info) + override {} + bool HasScrollLinkedAnimation(ElementId for_scroller) const override; + + private: + mutable ScrollTree scroll_tree_; + LayerTreeSettings settings_; + FakeImplTaskRunnerProvider task_runner_provider_; + TestTaskGraphRunner task_graph_runner_; + FakeLayerTreeHostImpl host_impl_; +}; + +} // namespace cc + +#endif // CC_TEST_FAKE_COMPOSITOR_DELEGATE_FOR_INPUT_H_
diff --git a/cc/test/layer_tree_pixel_test.cc b/cc/test/layer_tree_pixel_test.cc index 7cf5e20..9bd09fd 100644 --- a/cc/test/layer_tree_pixel_test.cc +++ b/cc/test/layer_tree_pixel_test.cc
@@ -165,6 +165,13 @@ void LayerTreePixelTest::InitializeSettings(LayerTreeSettings* settings) { LayerTreeTest::InitializeSettings(settings); + + if (settings->UseLayerContextForDisplay()) { + SkipTest(); + GTEST_SKIP() << "TODO(crbug.com/389148369) TreesInViz: Implement copy " + "output requests"; + } + settings->gpu_rasterization_disabled = !use_accelerated_raster(); settings->use_zero_copy = raster_type() == TestRasterType::kZeroCopy; }
diff --git a/cc/test/layer_tree_test.cc b/cc/test/layer_tree_test.cc index 1fe4e00f..7b80889 100644 --- a/cc/test/layer_tree_test.cc +++ b/cc/test/layer_tree_test.cc
@@ -1184,6 +1184,17 @@ } InitializeSettings(&settings_); + // Tests may be skipped at this point for unsupported setting combinations. + if (skip_test_) { + return; + } + + if (mode_ == CompositorMode::SINGLE_THREADED && + settings_.UseLayerContextForDisplay()) { + GTEST_SKIP() << "TODO(crbug.com/389147356) TreesInViz: Implement single " + "threaded mode"; + } + base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask( FROM_HERE, base::BindOnce(&LayerTreeTest::DoBeginTest, base::Unretained(this)));
diff --git a/cc/test/layer_tree_test.h b/cc/test/layer_tree_test.h index 039ff3f..52611b7 100644 --- a/cc/test/layer_tree_test.h +++ b/cc/test/layer_tree_test.h
@@ -140,6 +140,8 @@ void RealEndTest(); + void SkipTest() { skip_test_ = true; } + std::unique_ptr<LayerTreeFrameSink> ReleaseLayerTreeFrameSinkOnLayerTreeHost(); void SetVisibleOnLayerTreeHost(bool visible); @@ -284,6 +286,8 @@ mutable base::Lock test_ended_lock_; bool ended_ = false; + bool skip_test_ = false; + int timeout_seconds_ = 0; raw_ptr<viz::BeginFrameSource> begin_frame_source_ = nullptr; // NOT OWNED.
diff --git a/cc/test/mock_input_handler.cc b/cc/test/mock_input_handler.cc new file mode 100644 index 0000000..46874c7 --- /dev/null +++ b/cc/test/mock_input_handler.cc
@@ -0,0 +1,23 @@ +// Copyright 2025 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "cc/test/mock_input_handler.h" + +#include "base/lazy_instance.h" + +namespace cc { + +namespace { + +base::LazyInstance<FakeCompositorDelegateForInput>::Leaky + g_fake_compositor_delegate = LAZY_INSTANCE_INITIALIZER; + +} // namespace + +MockInputHandler::MockInputHandler() + : InputHandler(g_fake_compositor_delegate.Get()) {} + +MockInputHandler::~MockInputHandler() = default; + +} // namespace cc
diff --git a/cc/test/mock_input_handler.h b/cc/test/mock_input_handler.h new file mode 100644 index 0000000..4459c095 --- /dev/null +++ b/cc/test/mock_input_handler.h
@@ -0,0 +1,144 @@ +// Copyright 2025 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CC_TEST_MOCK_INPUT_HANDLER_H_ +#define CC_TEST_MOCK_INPUT_HANDLER_H_ + +#include <memory> + +#include "base/memory/weak_ptr.h" +#include "cc/input/browser_controls_offset_tags_info.h" +#include "cc/input/browser_controls_state.h" +#include "cc/input/event_listener_properties.h" +#include "cc/input/input_handler.h" +#include "cc/input/scroll_elasticity_helper.h" +#include "cc/input/scroll_state.h" +#include "cc/metrics/events_metrics_manager.h" +#include "cc/paint/element_id.h" +#include "cc/test/fake_compositor_delegate_for_input.h" +#include "cc/trees/latency_info_swap_promise_monitor.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "ui/events/types/scroll_input_type.h" +#include "ui/gfx/geometry/point.h" +#include "ui/gfx/geometry/vector2d_f.h" + +namespace cc { + +class MockInputHandler : public InputHandler { + public: + MockInputHandler(); + MockInputHandler(const MockInputHandler&) = delete; + MockInputHandler& operator=(const MockInputHandler&) = delete; + + ~MockInputHandler() override; + + base::WeakPtr<InputHandler> AsWeakPtr() override { + return weak_ptr_factory_.GetWeakPtr(); + } + + MOCK_METHOD2(PinchGestureBegin, + void(const gfx::Point& anchor, ui::ScrollInputType type)); + MOCK_METHOD2(PinchGestureUpdate, + void(float magnify_delta, const gfx::Point& anchor)); + MOCK_METHOD1(PinchGestureEnd, void(const gfx::Point& anchor)); + + MOCK_METHOD0(SetNeedsAnimateInput, void()); + + MOCK_METHOD2(ScrollBegin, + ScrollStatus(ScrollState*, ui::ScrollInputType type)); + MOCK_METHOD2(RootScrollBegin, + ScrollStatus(ScrollState*, ui::ScrollInputType type)); + MOCK_METHOD2(ScrollUpdate, + InputHandlerScrollResult(ScrollState, base::TimeDelta)); + MOCK_METHOD1(ScrollEnd, void(bool)); + MOCK_METHOD2(RecordScrollBegin, + void(ui::ScrollInputType type, ScrollBeginThreadState state)); + MOCK_METHOD1(RecordScrollEnd, void(ui::ScrollInputType type)); + MOCK_METHOD1(HitTest, PointerResultType(const gfx::PointF& mouse_position)); + MOCK_METHOD2(MouseDown, + InputHandlerPointerResult(const gfx::PointF& mouse_position, + const bool shift_modifier)); + MOCK_METHOD1(MouseUp, + InputHandlerPointerResult(const gfx::PointF& mouse_position)); + MOCK_METHOD1(SetIsHandlingTouchSequence, void(bool)); + void NotifyInputEvent() override {} + + std::unique_ptr<LatencyInfoSwapPromiseMonitor> + CreateLatencyInfoSwapPromiseMonitor(ui::LatencyInfo* latency) override { + return nullptr; + } + + std::unique_ptr<EventsMetricsManager::ScopedMonitor> + GetScopedEventMetricsMonitor( + EventsMetricsManager::ScopedMonitor::DoneCallback) override { + return nullptr; + } + + ScrollElasticityHelper* CreateScrollElasticityHelper() override { + return nullptr; + } + void DestroyScrollElasticityHelper() override {} + + bool GetScrollOffsetForLayer(ElementId element_id, + gfx::PointF* offset) override { + return false; + } + bool ScrollLayerTo(ElementId element_id, const gfx::PointF& offset) override { + return false; + } + + void BindToClient(InputHandlerClient* client) override {} + + void MouseLeave() override {} + + MOCK_METHOD1(FindFrameElementIdAtPoint, ElementId(const gfx::PointF&)); + + InputHandlerPointerResult MouseMoveAt( + const gfx::Point& mouse_position) override { + return InputHandlerPointerResult(); + } + + MOCK_CONST_METHOD1(GetEventListenerProperties, + EventListenerProperties(EventListenerClass event_class)); + MOCK_METHOD2(EventListenerTypeForTouchStartOrMoveAt, + InputHandler::TouchStartOrMoveEventListenerType( + const gfx::Point& point, + TouchAction* touch_action)); + MOCK_CONST_METHOD1(HasBlockingWheelEventHandlerAt, bool(const gfx::Point&)); + + MOCK_METHOD0(RequestUpdateForSynchronousInputHandler, void()); + MOCK_METHOD1(SetSynchronousInputHandlerRootScrollOffset, + void(const gfx::PointF& root_offset)); + + bool IsCurrentlyScrollingViewport() const override { + return is_scrolling_root_; + } + void set_is_scrolling_root(bool is) { is_scrolling_root_ = is; } + + MOCK_METHOD4(GetSnapFlingInfoAndSetAnimatingSnapTarget, + bool(const gfx::Vector2dF& current_delta, + const gfx::Vector2dF& natural_displacement, + gfx::PointF* initial_offset, + gfx::PointF* target_offset)); + MOCK_METHOD1(ScrollEndForSnapFling, void(bool)); + + bool ScrollbarScrollIsActive() override { return false; } + + void SetDeferBeginMainFrame(bool defer_begin_main_frame) const override {} + + MOCK_METHOD4(UpdateBrowserControlsState, + void(BrowserControlsState constraints, + BrowserControlsState current, + bool animate, + base::optional_ref<const BrowserControlsOffsetTagsInfo> + offset_tags_info)); + + private: + bool is_scrolling_root_ = true; + + base::WeakPtrFactory<MockInputHandler> weak_ptr_factory_{this}; +}; + +} // namespace cc +#endif // CC_TEST_MOCK_INPUT_HANDLER_H_
diff --git a/cc/test/test_client_shared_image_interface.cc b/cc/test/test_client_shared_image_interface.cc index 57a5dd4..55a936b 100644 --- a/cc/test/test_client_shared_image_interface.cc +++ b/cc/test/test_client_shared_image_interface.cc
@@ -14,14 +14,18 @@ gpu::GpuFeatureInfo(), gpu::SharedImageCapabilities(), mojo::ScopedMessagePipeHandle( - mojo::MessagePipeHandle(mojo::kInvalidHandleValue))) {} + mojo::MessagePipeHandle(mojo::kInvalidHandleValue))) { + // There is a "LeakSanitizer: detected memory leaks" on + // mojo::SharedRemoteBase<mojo::AssociatedRemote<gpu::mojom::GpuChannel>> in + // the multithread ASAN test when TestGpuChannelHost is created on the Main + // thread and released on the Compositor thread. Because |remote_| is not + // actually used in the tests, so it's reset here to avoid the memory leak at + // the end. + ResetChannelRemoteForTesting(); +} TestGpuChannelHost::~TestGpuChannelHost() = default; -gpu::mojom::GpuChannel& TestGpuChannelHost::GetGpuChannel() { - return gpu_channel_; -} - TestClientSharedImageInterface::TestClientSharedImageInterface( scoped_refptr<gpu::SharedImageInterface> shared_image_interface) : gpu::ClientSharedImageInterface(
diff --git a/cc/test/test_client_shared_image_interface.h b/cc/test/test_client_shared_image_interface.h index 364899f..e19d6ae 100644 --- a/cc/test/test_client_shared_image_interface.h +++ b/cc/test/test_client_shared_image_interface.h
@@ -9,7 +9,6 @@ #include "gpu/ipc/client/client_shared_image_interface.h" #include "gpu/ipc/client/gpu_channel_host.h" #include "gpu/ipc/common/gpu_channel.mojom.h" -#include "gpu/ipc/common/mock_gpu_channel.h" #include "mojo/public/cpp/system/platform_handle.h" namespace cc { @@ -18,11 +17,8 @@ public: TestGpuChannelHost(); - gpu::mojom::GpuChannel& GetGpuChannel() override; - protected: ~TestGpuChannelHost() override; - gpu::MockGpuChannel gpu_channel_; }; class TestClientSharedImageInterface : public gpu::ClientSharedImageInterface {
diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc index 1075cca..f5aa65cb 100644 --- a/cc/trees/layer_tree_host_impl.cc +++ b/cc/trees/layer_tree_host_impl.cc
@@ -5157,13 +5157,8 @@ gpu::SHARED_IMAGE_USAGE_DISPLAY_READ; // For software compositing, shared memory will be allocated and the // UIResource will be copied into it. - base::MappedReadOnlyRegion shm; base::WritableSharedMemoryMapping shared_mapping; - viz::SharedBitmapId shared_bitmap_id; bool overlay_candidate = false; - // Use sharedImage for software composition; - bool use_shared_image_software = - !!layer_tree_frame_sink_->shared_image_interface(); if (layer_tree_frame_sink_->context_provider()) { viz::RasterContextProvider* context_provider = @@ -5177,7 +5172,8 @@ if (overlay_candidate) { shared_image_usage |= gpu::SHARED_IMAGE_USAGE_SCANOUT; } - } else if (use_shared_image_software) { + } else { + // software composition; DCHECK_EQ(bitmap.GetFormat(), UIResourceBitmap::RGBA8); // Must not include gpu::SHARED_IMAGE_USAGE_DISPLAY_READ here because // DISPLAY_READ means gpu composition. @@ -5191,10 +5187,6 @@ client_shared_image = std::move(shared_image_mapping.shared_image); shared_mapping = std::move(shared_image_mapping.mapping); CHECK(client_shared_image); - } else { - shm = viz::bitmap_allocation::AllocateSharedBitmap(upload_size, format); - shared_mapping = std::move(shm.mapping); - shared_bitmap_id = viz::SharedBitmap::GenerateId(); } if (!scaled) { @@ -5295,18 +5287,12 @@ transferable = viz::TransferableResource::MakeGpu( client_shared_image, texture_target, sync_token, upload_size, format, overlay_candidate, viz::TransferableResource::ResourceSource::kUI); - } else if (use_shared_image_software) { + } else { auto sii = layer_tree_frame_sink_->shared_image_interface(); gpu::SyncToken sync_token = sii->GenVerifiedSyncToken(); transferable = viz::TransferableResource::MakeSoftwareSharedImage( client_shared_image, sync_token, upload_size, format, viz::TransferableResource::ResourceSource::kUI); - } else { - layer_tree_frame_sink_->DidAllocateSharedBitmap(std::move(shm.region), - shared_bitmap_id); - transferable = viz::TransferableResource::MakeSoftwareSharedBitmap( - shared_bitmap_id, gpu::SyncToken(), upload_size, format, - viz::TransferableResource::ResourceSource::kUI); } transferable.color_space = color_space; id = resource_provider_->ImportResource( @@ -5320,10 +5306,6 @@ UIResourceData data; data.opaque = bitmap.GetOpaque(); - if (!use_shared_image_software) { - data.shared_bitmap_id = shared_bitmap_id; - data.shared_mapping = std::move(shared_mapping); - } data.shared_image = std::move(client_shared_image); data.resource_id_for_export = id; ui_resource_map_[uid] = std::move(data); @@ -5352,28 +5334,7 @@ void LayerTreeHostImpl::DeleteUIResourceBacking( UIResourceData data, const gpu::SyncToken& sync_token) { - // Resources are either software or gpu backed, not both. - DCHECK(!(data.shared_mapping.IsValid() && data.shared_image)); - - if (data.shared_mapping.IsValid()) { - layer_tree_frame_sink_->DidDeleteSharedBitmap(data.shared_bitmap_id); - } - - if (data.shared_image) { - if (layer_tree_frame_sink_->context_provider()) { - auto* sii = - layer_tree_frame_sink_->context_provider()->SharedImageInterface(); - if (sii) { - sii->DestroySharedImage(sync_token, std::move(data.shared_image)); - } - } else { - auto sii = layer_tree_frame_sink_->shared_image_interface(); - if (sii) { - sii->DestroySharedImage(sync_token, std::move(data.shared_image)); - } - } - } - // |data| goes out of scope and deletes anything it owned. + data.shared_image->UpdateDestructionSyncToken(sync_token); } void LayerTreeHostImpl::OnUIResourceReleased(UIResourceId uid,
diff --git a/cc/trees/layer_tree_host_impl_unittest.cc b/cc/trees/layer_tree_host_impl_unittest.cc index 21f806d..b6e9ee4 100644 --- a/cc/trees/layer_tree_host_impl_unittest.cc +++ b/cc/trees/layer_tree_host_impl_unittest.cc
@@ -182,6 +182,12 @@ } void SetUp() override { + LayerTreeSettings settings = DefaultSettings(); + if (settings.UseLayerContextForDisplay()) { + GTEST_SKIP() << "TODO(crbug.com/389147356) TreesInViz: Implement single " + "threaded mode"; + } + CreateHostImpl(DefaultSettings(), CreateLayerTreeFrameSink()); // TODO(bokan): Mac wheel scrolls don't cause smooth scrolling in the real @@ -935,7 +941,13 @@ class LayerTreeHostImplTimelinesTest : public LayerTreeHostImplTest { public: void SetUp() override { - CreateHostImpl(DefaultSettings(), CreateLayerTreeFrameSink()); + LayerTreeSettings settings = DefaultSettings(); + if (settings.UseLayerContextForDisplay()) { + GTEST_SKIP() << "TODO(crbug.com/389147356) TreesInViz: Implement single " + "threaded mode"; + } + + CreateHostImpl(settings, CreateLayerTreeFrameSink()); // TODO(bokan): Mac wheel scrolls don't cause smooth scrolling in the real // world. In tests, we force it on for consistency. Can be removed when @@ -957,6 +969,10 @@ settings.scrollbar_fade_delay = base::Milliseconds(500); settings.scrollbar_fade_duration = base::Milliseconds(300); settings.idle_thickness_scale = 0.4f; + if (settings.UseLayerContextForDisplay()) { + GTEST_SKIP() << "TODO(crbug.com/389147356) TreesInViz: Implement single " + "threaded mode"; + } CreateHostImpl(settings, CreateLayerTreeFrameSink()); } @@ -11846,8 +11862,14 @@ class LayerTreeHostImplTestPrepareTiles : public LayerTreeHostImplTest { public: void SetUp() override { + LayerTreeSettings settings = LayerTreeSettings(); + if (settings.UseLayerContextForDisplay()) { + GTEST_SKIP() << "TODO(crbug.com/389147356) TreesInViz: Implement single " + "threaded mode"; + } + fake_host_impl_ = new FakeLayerTreeHostImpl( - LayerTreeSettings(), &task_runner_provider_, &task_graph_runner_); + settings, &task_runner_provider_, &task_graph_runner_); host_impl_.reset(fake_host_impl_); layer_tree_frame_sink_ = CreateLayerTreeFrameSink(); host_impl_->SetVisible(true); @@ -12230,7 +12252,12 @@ class LayerTreeHostImplWithBrowserControlsTest : public LayerTreeHostImplTest { public: void SetUp() override { - CreateHostImpl(DefaultSettings(), CreateLayerTreeFrameSink()); + LayerTreeSettings settings = DefaultSettings(); + if (settings.UseLayerContextForDisplay()) { + GTEST_SKIP() << "TODO(crbug.com/389147356) TreesInViz: Implement single " + "threaded mode"; + } + CreateHostImpl(settings, CreateLayerTreeFrameSink()); host_impl_->active_tree()->SetBrowserControlsParams( {static_cast<float>(top_controls_height_), 0, 0, 0, false, false}); host_impl_->active_tree()->SetCurrentBrowserControlsShownRatio(1.f, 1.f); @@ -17772,6 +17799,11 @@ } void SetUp() override { + LayerTreeSettings settings = DefaultSettings(); + if (settings.UseLayerContextForDisplay()) { + GTEST_SKIP() << "TODO(crbug.com/389147356) TreesInViz: Implement single " + "threaded mode"; + } LayerTreeHostImplTest::SetUp(); cur_time_ = base::TimeTicks() + base::Milliseconds(100); @@ -19124,6 +19156,11 @@ void SetUp() override { scoped_feature_list_.InitAndEnableFeature( features::kMultipleImplOnlyScrollAnimations); + LayerTreeSettings settings = DefaultSettings(); + if (settings.UseLayerContextForDisplay()) { + GTEST_SKIP() << "TODO(crbug.com/389147356) TreesInViz: Implement single " + "threaded mode"; + } LayerTreeHostImplTest::SetUp(); } gfx::PointF CreateAndTickScrollAnimations(); @@ -19276,6 +19313,11 @@ void SetUp() override { scoped_feature_list_.InitAndEnableFeature( features::kMultipleImplOnlyScrollAnimations); + LayerTreeSettings settings = DefaultSettings(); + if (settings.UseLayerContextForDisplay()) { + GTEST_SKIP() << "TODO(crbug.com/389147356) TreesInViz: Implement single " + "threaded mode"; + } LayerTreeHostImplTest::SetUp(); gfx::Size viewport_size(100, 100); gfx::Size content_size(100, 5000);
diff --git a/cc/trees/layer_tree_host_unittest_context.cc b/cc/trees/layer_tree_host_unittest_context.cc index 0a3037a..87ddb63 100644 --- a/cc/trees/layer_tree_host_unittest_context.cc +++ b/cc/trees/layer_tree_host_unittest_context.cc
@@ -1657,11 +1657,7 @@ viz::ResourceId exported_resource_id_ = viz::kInvalidResourceId; }; -// TODO(crbug.com/388228379): Test is failing on ChromeOS ASan LSan builds. -#if !BUILDFLAG(IS_CHROMEOS) || !defined(LEAK_SANITIZER) || \ - !defined(ADDRESS_SANITIZER) SINGLE_AND_MULTI_THREAD_TEST_F(SoftwareTileResourceFreedIfLostWhileExported); -#endif class LayerTreeHostContextTestLoseAfterSendingBeginMainFrame : public LayerTreeHostContextTest {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/BaseCustomTabRootUiCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/BaseCustomTabRootUiCoordinator.java index 8da201b7..15b4dea 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/BaseCustomTabRootUiCoordinator.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/BaseCustomTabRootUiCoordinator.java
@@ -67,6 +67,7 @@ import org.chromium.chrome.browser.privacy_sandbox.ActivityTypeMapper; import org.chromium.chrome.browser.privacy_sandbox.PrivacySandboxBridge; import org.chromium.chrome.browser.privacy_sandbox.PrivacySandboxDialogController; +import org.chromium.chrome.browser.privacy_sandbox.PrivacySandboxSurveyController; import org.chromium.chrome.browser.privacy_sandbox.SurfaceType; import org.chromium.chrome.browser.privacy_sandbox.TrackingProtectionSnackbarController; import org.chromium.chrome.browser.profiles.Profile; @@ -754,12 +755,20 @@ "Startup.Android.PrivacySandbox.ShouldShowAdsNoticeCCT", shouldShowPrivacySandboxDialog); } + PrivacySandboxSurveyController surveyController = + PrivacySandboxSurveyController.initialize( + mTabModelSelectorSupplier.get(), + mActivityLifecycleDispatcher, + mActivity, + mMessageDispatcher, + mActivityTabProvider, + profile); + String appId = mIntentDataProvider.get().getClientPackageName(); if (ChromeFeatureList.isEnabled( ChromeFeatureList.PRIVACY_SANDBOX_ADS_NOTICE_CCT) && shouldShowPrivacySandboxDialog && isCustomTab) { boolean shouldShowPrivacySandboxDialogAppIdCheck = true; - String appId = mIntentDataProvider.get().getClientPackageName(); String paramAdsNoticeAppId = ChromeFeatureList.getFieldTrialParamByFeature( ChromeFeatureList.PRIVACY_SANDBOX_ADS_NOTICE_CCT, @@ -780,7 +789,17 @@ SurfaceType.AGACCT, mWindowAndroid); } + } else if (surveyController != null + && !ChromeFeatureList.isEnabled( + ChromeFeatureList.PRIVACY_SANDBOX_ADS_NOTICE_CCT) + && shouldShowPrivacySandboxDialog + && isCustomTab) { + surveyController.scheduleAdsCctControlSurveyLaunch( + appId, + new PrivacySandboxBridge(currentModelProfile) + .getRequiredPromptType(SurfaceType.AGACCT)); } + if (!didShowPrompt) { didShowPrompt = RequestDesktopUtils
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/dom_distiller/ReaderModeManager.java b/chrome/android/java/src/org/chromium/chrome/browser/dom_distiller/ReaderModeManager.java index 6cd0866..c65281b 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/dom_distiller/ReaderModeManager.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/dom_distiller/ReaderModeManager.java
@@ -367,7 +367,7 @@ // Reader Mode should not pollute the navigation stack. To avoid this, watch for // navigations and prepare to remove any that are "chrome-distiller" urls. - NavigationController controller = mWebContents.get().getNavigationController(); + NavigationController controller = getWebContents().getNavigationController(); int index = controller.getLastCommittedEntryIndex(); NavigationEntry entry = controller.getEntryAtIndex(index); @@ -395,7 +395,7 @@ if (mShouldRemovePreviousNavigation) { mShouldRemovePreviousNavigation = false; - NavigationController controller = mWebContents.get().getNavigationController(); + NavigationController controller = getWebContents().getNavigationController(); if (controller.getEntryAtIndex(mLastDistillerPageIndex) != null) { controller.removeEntryAtIndex(mLastDistillerPageIndex); }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/privacy_sandbox/PrivacySandboxSurveyController.java b/chrome/android/java/src/org/chromium/chrome/browser/privacy_sandbox/PrivacySandboxSurveyController.java index cd94b3d..ed9dc35b 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/privacy_sandbox/PrivacySandboxSurveyController.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/privacy_sandbox/PrivacySandboxSurveyController.java
@@ -128,7 +128,10 @@ private boolean mHasSeenNtp; private boolean mOverrideChannelForTesting; private int mChannelForTesting; + private static long sAdsCctDelayOverrideMilliseconds; + private static boolean sOverrideAdsCctDelay; private static boolean sEnableForTesting; + private static final long DEFAULT_ADS_CCT_DELAY_MS = 20_000L; PrivacySandboxSurveyController( TabModelSelector tabModelSelector, @@ -195,6 +198,10 @@ return true; } + private long getAdsCctDelayMilliseconds() { + return sOverrideAdsCctDelay ? sAdsCctDelayOverrideMilliseconds : DEFAULT_ADS_CCT_DELAY_MS; + } + // Schedules the launch of an Ads CCT Treatment survey. // Should only be invoked after the closure of either the EEA or ROW notice. public void scheduleAdsCctTreatmentSurveyLaunch(String appId) { @@ -204,7 +211,7 @@ PostTask.postDelayedTask( TaskTraits.UI_DEFAULT, () -> maybeLaunchAdsCctTreatmentSurvey(), - /*20 seconds*/ 20000); + getAdsCctDelayMilliseconds()); } // Determines the appropriate survey to launch based on the user interaction with either the EEA @@ -240,7 +247,7 @@ PostTask.postDelayedTask( TaskTraits.UI_DEFAULT, () -> maybeLaunchAdsCctControlSurvey(promptType), - /*20 seconds*/ 20000); + getAdsCctDelayMilliseconds()); } // Determines the appropriate survey to launch based on the prompt type. @@ -458,4 +465,11 @@ mChannelForTesting = channel; ResettersForTesting.register(() -> mChannelForTesting = Channel.DEFAULT); } + + // Overrides the survey delay + public static void overrideAdsCctSurveyDelayForTesting(long delayMilliseconds) { + sAdsCctDelayOverrideMilliseconds = delayMilliseconds; + sOverrideAdsCctDelay = true; + ResettersForTesting.register(() -> sOverrideAdsCctDelay = false); + } }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivity.java index 56ddbc5..5bdfcf95 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivity.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivity.java
@@ -889,6 +889,14 @@ public void onTopResumedActivityChanged(boolean isTopResumedActivity) { super_onTopResumedActivityChanged(isTopResumedActivity); + // For hub search use in split screen and multi window mode, search activity should be + // dismissed when focus is lost to prevent focus from causing the suggestion list to flicker + // on window toggling. + if (!isTopResumedActivity && mIntentOrigin == IntentOrigin.HUB) { + finish(TerminationReason.ACTIVITY_FOCUS_LOST, null); + return; + } + // TODO(crbug.com/329702834): Ensure showing Suggestions when activity resumes. // This may only happen when user enters tab switcher, and immediately returns to the // SearchActivity.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/TabbedRootUiCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/TabbedRootUiCoordinator.java index 84654b3bf..d7a6684 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/TabbedRootUiCoordinator.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/TabbedRootUiCoordinator.java
@@ -1324,11 +1324,6 @@ } @Override - public void openLearnMoreSharedTabGroupsPage(Context context, GURL gurl) { - CustomTabActivity.showInfoPage(context, gurl.getSpec()); - } - - @Override public void openUrlInChromeCustomTab(Context context, GURL gurl) { CustomTabActivity.showInfoPage(context, gurl.getSpec()); }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelRemoverUnitTest.java b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelRemoverUnitTest.java index 657d329..d104833 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelRemoverUnitTest.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelRemoverUnitTest.java
@@ -288,7 +288,8 @@ } @Test - public void testTabRemovalFlow_SingleCollaboration_WithDialog_NoCollborationData_UnknownRole() { + public void + testTabRemovalFlow_SingleCollaboration_WithDialog_NoCollaborationData_UnknownRole() { GroupsPendingDestroy groupsPendingDestroy = new GroupsPendingDestroy(); groupsPendingDestroy.collaborationGroupsDestroyed.add(TAB_GROUP_1); when(mHandler.computeGroupsPendingDestroy()).thenReturn(groupsPendingDestroy);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/privacy_sandbox/PrivacySandboxSurveyControllerIntegrationTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/privacy_sandbox/PrivacySandboxSurveyControllerIntegrationTest.java index 00b898a..9f4a4031 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/privacy_sandbox/PrivacySandboxSurveyControllerIntegrationTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/privacy_sandbox/PrivacySandboxSurveyControllerIntegrationTest.java
@@ -4,6 +4,15 @@ package org.chromium.chrome.browser.privacy_sandbox; +import static androidx.test.espresso.Espresso.onView; +import static androidx.test.espresso.assertion.ViewAssertions.doesNotExist; +import static androidx.test.espresso.matcher.ViewMatchers.withId; +import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; + +import android.content.Context; +import android.content.Intent; + +import androidx.test.core.app.ApplicationProvider; import androidx.test.filters.MediumTest; import org.junit.Assert; @@ -12,16 +21,25 @@ import org.junit.Test; import org.junit.runner.RunWith; +import org.chromium.base.CommandLine; import org.chromium.base.ThreadUtils; import org.chromium.base.test.util.Batch; import org.chromium.base.test.util.CriteriaHelper; import org.chromium.base.test.util.Features; +import org.chromium.base.test.util.Features.DisableFeatures; import org.chromium.base.test.util.RequiresRestart; +import org.chromium.chrome.browser.browserservices.intents.SessionHolder; +import org.chromium.chrome.browser.customtabs.CustomTabActivityTestRule; +import org.chromium.chrome.browser.customtabs.CustomTabsConnection; +import org.chromium.chrome.browser.customtabs.CustomTabsIntentTestUtils; +import org.chromium.chrome.browser.firstrun.FirstRunStatus; import org.chromium.chrome.browser.flags.ChromeFeatureList; +import org.chromium.chrome.browser.flags.ChromeSwitches; import org.chromium.chrome.browser.tab.Tab; import org.chromium.chrome.browser.ui.hats.TestSurveyUtils; import org.chromium.chrome.test.ChromeJUnit4ClassRunner; import org.chromium.chrome.test.ChromeTabbedActivityTestRule; +import org.chromium.chrome.test.R; import org.chromium.components.embedder_support.util.UrlConstants; import org.chromium.components.messages.DismissReason; import org.chromium.components.messages.MessageBannerProperties; @@ -30,6 +48,7 @@ import org.chromium.components.messages.MessageIdentifier; import org.chromium.components.messages.MessageStateHandler; import org.chromium.components.messages.MessagesTestHelper; +import org.chromium.net.test.EmbeddedTestServer; import org.chromium.ui.modelutil.PropertyModel; import java.util.List; @@ -42,16 +61,245 @@ public ChromeTabbedActivityTestRule mActivityTestRule = new ChromeTabbedActivityTestRule(); @Rule + public CustomTabActivityTestRule mCustomTabActivityTestRule = new CustomTabActivityTestRule(); + + @Rule + public ChromeTabbedActivityTestRule mChromeTabbedActivityTestRule = + new ChromeTabbedActivityTestRule(); + + @Rule public TestSurveyUtils.TestSurveyComponentRule mTestSurveyComponentRule = new TestSurveyUtils.TestSurveyComponentRule(); private MessageDispatcher mMessageDispatcher; private PropertyModel mSurveyMessage; + private String mTestPage; + private EmbeddedTestServer mTestServer; + private static final String TEST_PAGE = "/chrome/test/data/android/google.html"; @Before public void setup() { PrivacySandboxSurveyController.setEnableForTesting(); mActivityTestRule.startMainActivityWithURL(UrlConstants.NTP_URL); + + // CCT setup. + ThreadUtils.runOnUiThreadBlocking(() -> FirstRunStatus.setFirstRunFlowComplete(true)); + Context appContext = getInstrumentation().getTargetContext().getApplicationContext(); + mTestServer = EmbeddedTestServer.createAndStartServer(appContext); + mTestPage = mTestServer.getURL(TEST_PAGE); + + // Explicitly remove the `DISABLE_FIRST_RUN_EXPERIENCE` (set via `TestSurveyComponentRule`) + // commandline switch which prevents us from receiving a valid prompt type via + // `PrivacySandboxBridge`. + CommandLine.getInstance().removeSwitch(ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE); + PrivacySandboxSurveyController.overrideAdsCctSurveyDelayForTesting( + /* delayMilliseconds= */ 0); + } + + private Intent createMinimalCustomTabIntent() { + Intent intent = + CustomTabsIntentTestUtils.createMinimalCustomTabIntent( + ApplicationProvider.getApplicationContext(), mTestPage); + var token = SessionHolder.getSessionHolderFromIntent(intent); + // x86 devices will return a null package name unless we explicitly override it. + CustomTabsConnection connection = CustomTabsConnection.getInstance(); + connection.newSession(token.getSessionAsCustomTab()); + connection.overridePackageNameForSessionForTesting(token, "org.chromium.chrome.tests"); + return intent; + } + + @Test + @MediumTest + @Features.EnableFeatures({ + ChromeFeatureList.PRIVACY_SANDBOX_CCT_ADS_NOTICE_SURVEY + + ":app-id/org.chromium.chrome.tests/" + + "row-control-trigger-id/" + + TestSurveyUtils.TEST_TRIGGER_ID_FOO, + ChromeFeatureList.PRIVACY_SANDBOX_SETTINGS_4 + + ":force-show-notice-row-for-testing/true/notice-required/true" + }) + @DisableFeatures({ + ChromeFeatureList.PRIVACY_SANDBOX_ADS_NOTICE_CCT, + }) + public void adsCctSurveyForRowControlAcceptSurvey() { + mCustomTabActivityTestRule.startCustomTabActivityWithIntent(createMinimalCustomTabIntent()); + onView(withId(R.id.privacy_sandbox_dialog)).check(doesNotExist()); + waitForSurveyMessageToShowOnCct(); + ThreadUtils.runOnUiThreadBlocking( + () -> { + mSurveyMessage.get(MessageBannerProperties.ON_PRIMARY_ACTION).get(); + }); + Assert.assertEquals( + "Last shown survey triggerId not match.", + TestSurveyUtils.TEST_TRIGGER_ID_FOO, + mTestSurveyComponentRule.getLastShownTriggerId()); + } + + @Test + @MediumTest + @Features.EnableFeatures({ + ChromeFeatureList.PRIVACY_SANDBOX_CCT_ADS_NOTICE_SURVEY + + ":app-id/org.chromium.chrome.tests/" + + "row-control-trigger-id/" + + TestSurveyUtils.TEST_TRIGGER_ID_FOO, + ChromeFeatureList.PRIVACY_SANDBOX_SETTINGS_4 + + ":force-show-notice-row-for-testing/true/notice-required/true" + }) + @DisableFeatures({ + ChromeFeatureList.PRIVACY_SANDBOX_ADS_NOTICE_CCT, + }) + public void adsCctSurveyForRowControlDismissSurvey() { + mCustomTabActivityTestRule.startCustomTabActivityWithIntent(createMinimalCustomTabIntent()); + onView(withId(R.id.privacy_sandbox_dialog)).check(doesNotExist()); + waitForSurveyMessageToShowOnCct(); + ThreadUtils.runOnUiThreadBlocking( + () -> mMessageDispatcher.dismissMessage(mSurveyMessage, DismissReason.GESTURE)); + Assert.assertTrue( + "Survey displayed not recorded.", + mTestSurveyComponentRule.isPromptShownForTriggerId( + TestSurveyUtils.TEST_TRIGGER_ID_FOO)); + } + + @Test + @MediumTest + @Features.EnableFeatures({ + ChromeFeatureList.PRIVACY_SANDBOX_CCT_ADS_NOTICE_SURVEY + + ":app-id/org.chromium.chrome.tests/", + ChromeFeatureList.PRIVACY_SANDBOX_SETTINGS_4 + + ":force-show-notice-row-for-testing/true/notice-required/true" + }) + @DisableFeatures({ + ChromeFeatureList.PRIVACY_SANDBOX_ADS_NOTICE_CCT, + }) + public void adsCctSurveyForRowControlNotShownWhenNoTriggerIdProvided() { + mCustomTabActivityTestRule.startCustomTabActivityWithIntent(createMinimalCustomTabIntent()); + Assert.assertFalse( + "Survey was displayed.", + mTestSurveyComponentRule.isPromptShownForTriggerId( + TestSurveyUtils.TEST_TRIGGER_ID_FOO)); + } + + @Test + @MediumTest + @Features.EnableFeatures({ + ChromeFeatureList.PRIVACY_SANDBOX_CCT_ADS_NOTICE_SURVEY + + ":app-id/org.chromium.chrome.tests/" + + "eea-control-trigger-id/" + + TestSurveyUtils.TEST_TRIGGER_ID_FOO, + ChromeFeatureList.PRIVACY_SANDBOX_SETTINGS_4 + + ":force-show-consent-for-testing/true/consent-required/true" + }) + @DisableFeatures({ + ChromeFeatureList.PRIVACY_SANDBOX_ADS_NOTICE_CCT, + }) + public void adsCctSurveyForEeaControlAcceptSurvey() { + mCustomTabActivityTestRule.startCustomTabActivityWithIntent(createMinimalCustomTabIntent()); + onView(withId(R.id.privacy_sandbox_dialog)).check(doesNotExist()); + waitForSurveyMessageToShowOnCct(); + ThreadUtils.runOnUiThreadBlocking( + () -> { + mSurveyMessage.get(MessageBannerProperties.ON_PRIMARY_ACTION).get(); + }); + Assert.assertEquals( + "Last shown survey triggerId not match.", + TestSurveyUtils.TEST_TRIGGER_ID_FOO, + mTestSurveyComponentRule.getLastShownTriggerId()); + } + + @Test + @MediumTest + @Features.EnableFeatures({ + ChromeFeatureList.PRIVACY_SANDBOX_CCT_ADS_NOTICE_SURVEY + + ":app-id/org.chromium.chrome.tests/" + + "eea-control-trigger-id/" + + TestSurveyUtils.TEST_TRIGGER_ID_FOO, + ChromeFeatureList.PRIVACY_SANDBOX_SETTINGS_4 + + ":force-show-consent-for-testing/true/consent-required/true" + }) + @DisableFeatures({ + ChromeFeatureList.PRIVACY_SANDBOX_ADS_NOTICE_CCT, + }) + public void adsCctSurveyForEeaControlDismissSurvey() { + mCustomTabActivityTestRule.startCustomTabActivityWithIntent(createMinimalCustomTabIntent()); + onView(withId(R.id.privacy_sandbox_dialog)).check(doesNotExist()); + waitForSurveyMessageToShowOnCct(); + ThreadUtils.runOnUiThreadBlocking( + () -> mMessageDispatcher.dismissMessage(mSurveyMessage, DismissReason.GESTURE)); + Assert.assertTrue( + "Survey displayed not recorded.", + mTestSurveyComponentRule.isPromptShownForTriggerId( + TestSurveyUtils.TEST_TRIGGER_ID_FOO)); + } + + @Test + @MediumTest + @Features.EnableFeatures({ + ChromeFeatureList.PRIVACY_SANDBOX_CCT_ADS_NOTICE_SURVEY + + ":app-id/org.chromium.chrome.tests/", + ChromeFeatureList.PRIVACY_SANDBOX_SETTINGS_4 + + ":force-show-consent-for-testing/true/consent-required/true" + }) + @DisableFeatures({ + ChromeFeatureList.PRIVACY_SANDBOX_ADS_NOTICE_CCT, + }) + public void adsCctSurveyForEeaControlNotShownWhenNoTriggerIdProvided() { + mCustomTabActivityTestRule.startCustomTabActivityWithIntent(createMinimalCustomTabIntent()); + Assert.assertFalse( + "Survey was displayed.", + mTestSurveyComponentRule.isPromptShownForTriggerId( + TestSurveyUtils.TEST_TRIGGER_ID_FOO)); + } + + @Test + @MediumTest + @Features.EnableFeatures({ + ChromeFeatureList.PRIVACY_SANDBOX_CCT_ADS_NOTICE_SURVEY + + ":app-id/org.chromium.chrome.tests/", + ChromeFeatureList.PRIVACY_SANDBOX_SETTINGS_4 + + ":force-show-consent-for-testing/true/consent-required/true" + + "/force-show-notice-row-for-testing/true/notice-required/true", + ChromeFeatureList.PRIVACY_SANDBOX_ADS_NOTICE_CCT + }) + public void adsCctSurveyForControlSurveyNotShownWhenAdsNoticeCctFeatureEnabled() { + mCustomTabActivityTestRule.startCustomTabActivityWithIntent(createMinimalCustomTabIntent()); + Assert.assertFalse( + "Survey was displayed.", + mTestSurveyComponentRule.isPromptShownForTriggerId( + TestSurveyUtils.TEST_TRIGGER_ID_FOO)); + } + + @Test + @MediumTest + @Features.EnableFeatures({ + ChromeFeatureList.PRIVACY_SANDBOX_CCT_ADS_NOTICE_SURVEY + ":app-id/invalid-app-id/", + ChromeFeatureList.PRIVACY_SANDBOX_SETTINGS_4 + + ":force-show-consent-for-testing/true/consent-required/true" + + "/force-show-notice-row-for-testing/true/notice-required/true", + }) + @DisableFeatures({ChromeFeatureList.PRIVACY_SANDBOX_ADS_NOTICE_CCT}) + public void adsCctSurveyForControlSurveyNotShownWithInvalidAppId() { + mCustomTabActivityTestRule.startCustomTabActivityWithIntent(createMinimalCustomTabIntent()); + Assert.assertFalse( + "Survey was displayed.", + mTestSurveyComponentRule.isPromptShownForTriggerId( + TestSurveyUtils.TEST_TRIGGER_ID_FOO)); + } + + @Test + @MediumTest + @Features.EnableFeatures({ + ChromeFeatureList.PRIVACY_SANDBOX_SETTINGS_4 + + ":force-show-consent-for-testing/true/consent-required/true" + + "/force-show-notice-row-for-testing/true/notice-required/true", + }) + @DisableFeatures({ + ChromeFeatureList.PRIVACY_SANDBOX_ADS_NOTICE_CCT, + ChromeFeatureList.PRIVACY_SANDBOX_CCT_ADS_NOTICE_SURVEY + }) + public void adsCctSurveyForControlSurveyNotShownWithSurveyFeatureDisabled() { + mCustomTabActivityTestRule.startCustomTabActivityWithIntent(createMinimalCustomTabIntent()); + Assert.assertEquals( + "Survey was displayed.", mTestSurveyComponentRule.getLastShownTriggerId(), null); } @Test @@ -109,6 +357,22 @@ TestSurveyUtils.TEST_TRIGGER_ID_FOO)); } + private void waitForSurveyMessageToShowOnCct() { + Tab tab = mCustomTabActivityTestRule.getActivity().getActivityTab(); + CriteriaHelper.pollUiThread(() -> !tab.isLoading() && tab.isUserInteractable()); + ThreadUtils.runOnUiThreadBlocking( + () -> { + mMessageDispatcher = + MessageDispatcherProvider.from( + mCustomTabActivityTestRule.getActivity().getWindowAndroid()); + }); + CriteriaHelper.pollUiThread( + () -> { + mSurveyMessage = getSurveyMessage(); + return mSurveyMessage != null; + }); + } + private void waitForSurveyMessageToShow() { Tab tab = mActivityTestRule.getActivity().getActivityTab(); CriteriaHelper.pollUiThread(() -> !tab.isLoading() && tab.isUserInteractable());
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/searchwidget/SearchActivityUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/searchwidget/SearchActivityUnitTest.java index 50a65d99..2f3883a 100644 --- a/chrome/android/junit/src/org/chromium/chrome/browser/searchwidget/SearchActivityUnitTest.java +++ b/chrome/android/junit/src/org/chromium/chrome/browser/searchwidget/SearchActivityUnitTest.java
@@ -1100,6 +1100,7 @@ @Test public void onTopResumedActivityChanged_clearOmniboxFocusIfNotActive() { doNothing().when(mActivity).super_onTopResumedActivityChanged(anyBoolean()); + mActivity.handleNewIntent(buildTestServiceIntent(IntentOrigin.SEARCH_WIDGET), false); mActivity.onTopResumedActivityChanged(false); verify(mLocationBar).clearOmniboxFocus(); verify(mActivity).super_onTopResumedActivityChanged(false); @@ -1108,8 +1109,30 @@ @Test public void onTopResumedActivityChanged_requestOmniboxFocusIfActive() { doNothing().when(mActivity).super_onTopResumedActivityChanged(anyBoolean()); + mActivity.handleNewIntent(buildTestServiceIntent(IntentOrigin.SEARCH_WIDGET), false); mActivity.onTopResumedActivityChanged(true); verify(mLocationBar).requestOmniboxFocus(); verify(mActivity).super_onTopResumedActivityChanged(true); } + + @Test + public void onTopResumedActivityChanged_finishActivityFocusLostHubSearch() { + LocationBarCoordinator locationBarCoordinator = mock(LocationBarCoordinator.class); + StatusCoordinator statusCoordinator = mock(StatusCoordinator.class); + doReturn(statusCoordinator).when(locationBarCoordinator).getStatusCoordinator(); + mActivity.setLocationBarCoordinatorForTesting(locationBarCoordinator); + + doNothing().when(mActivity).super_onTopResumedActivityChanged(anyBoolean()); + var histograms = + HistogramWatcher.newBuilder() + .expectIntRecord( + SearchActivity.HISTOGRAM_SESSION_TERMINATION_REASON + + HISTOGRAM_SUFFIX_HUB, + TerminationReason.ACTIVITY_FOCUS_LOST) + .build(); + + mActivity.handleNewIntent(buildTestServiceIntent(IntentOrigin.HUB), false); + mActivity.onTopResumedActivityChanged(false); + histograms.assertExpected(); + } }
diff --git a/chrome/app/chrome_command_ids.h b/chrome/app/chrome_command_ids.h index 19bb96da..ed36209 100644 --- a/chrome/app/chrome_command_ids.h +++ b/chrome/app/chrome_command_ids.h
@@ -273,6 +273,8 @@ #define IDC_TASK_MANAGER_MAIN_MENU 40288 #define IDC_COMPARE_MENU 40289 #define IDC_SHOW_ALL_COMPARISON_TABLES 40290 +#define IDC_ADD_TO_COMPARISON_TABLE_MENU 40291 +#define IDC_CREATE_NEW_COMPARISON_TABLE_WITH_TAB 40292 // Spell-check // Insert any additional suggestions before _LAST; these have to be consecutive.
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd index 0555d6e..ad0ab60 100644 --- a/chrome/app/generated_resources.grd +++ b/chrome/app/generated_resources.grd
@@ -12275,6 +12275,9 @@ <message name="IDS_DATA_SHARING_LEAVE_DIALOG_BODY" desc="The body for the leave group dialog."> You’ll immediately lose access to the “<ph name="GROUP_NAME">$1<ex>Vacation</ex></ph>” tab group, and it will be deleted from all your devices </message> + <message name="IDS_DATA_SHARING_LEAVE_DIALOG_BODY_NO_GROUP_TITLE" desc="The body for the leave group dialog when the group does not have a title."> + You’ll immediately lose access to this tab group, and it will be deleted from all your devices + </message> <message name="IDS_DATA_SHARING_LEAVE_DIALOG_CONFIRM" desc="The text for the confirmation button for the leave group dialog."> Leave </message>
diff --git a/chrome/app/generated_resources_grd/IDS_DATA_SHARING_LEAVE_DIALOG_BODY_NO_GROUP_TITLE.png.sha1 b/chrome/app/generated_resources_grd/IDS_DATA_SHARING_LEAVE_DIALOG_BODY_NO_GROUP_TITLE.png.sha1 new file mode 100644 index 0000000..962c6993 --- /dev/null +++ b/chrome/app/generated_resources_grd/IDS_DATA_SHARING_LEAVE_DIALOG_BODY_NO_GROUP_TITLE.png.sha1
@@ -0,0 +1 @@ +721c724b2ef391cc3bb5c36a8cc2c304aa3ff547 \ No newline at end of file
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn index 925c234d..398c736 100644 --- a/chrome/browser/BUILD.gn +++ b/chrome/browser/BUILD.gn
@@ -1672,6 +1672,13 @@ "webid/identity_provider_permission_request.h", ] + if (is_chromeos) { + sources += [ + "component_updater/lacros_component_remover.cc", + "component_updater/lacros_component_remover.h", + ] + } + configs += [ "//build/config/compiler:wexit_time_destructors", "//build/config:precompiled_headers", @@ -6715,6 +6722,7 @@ deps += [ "//chrome/common:chrome_features", "//components/dbus", + "//components/os_crypt/async/browser:freedesktop_secret_key_provider", "//components/os_crypt/async/browser:secret_portal_key_provider", ] }
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc index 01608d7..f755f73 100644 --- a/chrome/browser/about_flags.cc +++ b/chrome/browser/about_flags.cc
@@ -7856,6 +7856,11 @@ kOsCrOS, FEATURE_VALUE_TYPE(features::kAccessibilityMagnifierFollowsChromeVox)}, + {"enable-accessibility-manifest-v3-braille-ime", + flag_descriptions::kAccessibilityManifestV3BrailleImeName, + flag_descriptions::kAccessibilityManifestV3BrailleImeDescription, kOsCrOS, + FEATURE_VALUE_TYPE(features::kAccessibilityManifestV3BrailleIme)}, + {"enable-accessibility-manifest-v3-enhanced-network-tts", flag_descriptions::kAccessibilityManifestV3EnhancedNetworkTtsName, flag_descriptions::kAccessibilityManifestV3EnhancedNetworkTtsDescription,
diff --git a/chrome/browser/android/examples/custom_tabs_client/OWNERS b/chrome/browser/android/examples/custom_tabs_client/OWNERS index 8dd5b62..1f2b21d 100644 --- a/chrome/browser/android/examples/custom_tabs_client/OWNERS +++ b/chrome/browser/android/examples/custom_tabs_client/OWNERS
@@ -1,4 +1,3 @@ file://chrome/android/java/src/org/chromium/chrome/browser/customtabs/OWNERS -katzz@google.com jinsukkim@chromium.org sinansahin@google.com
diff --git a/chrome/browser/ash/input_method/component_extension_ime_manager_delegate_impl.cc b/chrome/browser/ash/input_method/component_extension_ime_manager_delegate_impl.cc index ed1d398..c34651c 100644 --- a/chrome/browser/ash/input_method/component_extension_ime_manager_delegate_impl.cc +++ b/chrome/browser/ash/input_method/component_extension_ime_manager_delegate_impl.cc
@@ -39,6 +39,7 @@ #include "extensions/common/extension.h" #include "extensions/common/manifest_constants.h" #include "net/base/url_util.h" +#include "ui/accessibility/accessibility_features.h" #include "ui/base/ime/ash/extension_ime_util.h" #include "ui/base/resource/resource_bundle.h" @@ -417,6 +418,13 @@ std::vector<ComponentExtensionIME>* out_imes) { DCHECK(out_imes); for (auto& extension : allowlisted_component_extensions) { + // TODO(crbug.com/384675323): Remove this check and update + // `allowlisted_component_extensions` when flag is removed. + if (extension.manifest_resource_id == IDR_BRAILLE_MANIFEST && + ::features::IsAccessibilityManifestV3EnabledForBrailleIme()) { + extension.manifest_resource_id = IDR_BRAILLE_MANIFEST_MV3; + } + ComponentExtensionIME component_ime; component_ime.manifest = ui::ResourceBundle::GetSharedInstance().LoadDataResourceString(
diff --git a/chrome/browser/browser_controls/android/java/src/org/chromium/chrome/browser/browser_controls/BottomControlsStacker.java b/chrome/browser/browser_controls/android/java/src/org/chromium/chrome/browser/browser_controls/BottomControlsStacker.java index 2841f68..2bd32089 100644 --- a/chrome/browser/browser_controls/android/java/src/org/chromium/chrome/browser/browser_controls/BottomControlsStacker.java +++ b/chrome/browser/browser_controls/android/java/src/org/chromium/chrome/browser/browser_controls/BottomControlsStacker.java
@@ -199,8 +199,7 @@ public void requestLayerUpdate(boolean animate) { assert isEnabled(); - updateLayerVisibilities(); - recalculateLayerSizes(); + updateLayerVisibilitiesAndSizes(); updateBrowserControlsHeight(animate); if (mBrowserControlsSizer.offsetOverridden() && isDispatchingYOffset()) { repositionLayers( @@ -512,6 +511,19 @@ } } + /** + * Recalculates layer visibilities and sizes without mutating bottom controls height or actually + * repositioning layers. A call to this method must be followed by a call to + * requestLayerUpdate() in the same stack frame to avoid inconsistency between + * BottomControlsStacker's state and the state of individual layers. This is useful if you need + * to mutate browser controls height(s) *before* BottomControlsStacker, e.g. animating a + * simultaneous top and bottom height change. + */ + public void updateLayerVisibilitiesAndSizes() { + updateLayerVisibilities(); + recalculateLayerSizes(); + } + /** Recalculate the browser controls height based on layer sizes. */ private void recalculateLayerSizes() { int height = 0;
diff --git a/chrome/browser/browser_features.cc b/chrome/browser/browser_features.cc index d78fc586..8100ea1 100644 --- a/chrome/browser/browser_features.cc +++ b/chrome/browser/browser_features.cc
@@ -78,6 +78,12 @@ BASE_FEATURE(kDbusSecretPortal, "DbusSecretPortal", base::FEATURE_ENABLED_BY_DEFAULT); + +// Enables usage of os_crypt_async::FreedesktopSecretKeyProvider, which is +// compatible with the synchronous backend. +BASE_FEATURE(kUseFreedesktopSecretKeyProvider, + "UseFreedesktopSecretKeyProvider", + base::FEATURE_DISABLED_BY_DEFAULT); #endif // BUILDFLAG(IS_LINUX) // Destroy profiles when their last browser window is closed, instead of when @@ -234,6 +240,12 @@ BASE_FEATURE(kSecretPortalKeyProviderUseForEncryption, "SecretPortalKeyProviderUseForEncryption", base::FEATURE_DISABLED_BY_DEFAULT); + +// If true, encrypt new data with the key provided by +// FreedesktopSecretKeyProvider. Otherwise, it will only decrypt existing data. +BASE_FEATURE(kUseFreedesktopSecretKeyProviderForEncryption, + "UseFreedesktopSecretKeyProviderForEncryption", + base::FEATURE_DISABLED_BY_DEFAULT); #endif // BUILDFLAG(IS_LINUX) // This flag controls whether to trigger prerendering when the default search
diff --git a/chrome/browser/browser_features.h b/chrome/browser/browser_features.h index 7978bef0e..4acda03 100644 --- a/chrome/browser/browser_features.h +++ b/chrome/browser/browser_features.h
@@ -39,6 +39,7 @@ #if BUILDFLAG(IS_LINUX) BASE_DECLARE_FEATURE(kDbusSecretPortal); +BASE_DECLARE_FEATURE(kUseFreedesktopSecretKeyProvider); #endif BASE_DECLARE_FEATURE(kDestroyProfileOnBrowserClose); @@ -104,6 +105,7 @@ #if BUILDFLAG(IS_LINUX) BASE_DECLARE_FEATURE(kSecretPortalKeyProviderUseForEncryption); +BASE_DECLARE_FEATURE(kUseFreedesktopSecretKeyProviderForEncryption); #endif BASE_DECLARE_FEATURE(kSupportSearchSuggestionForPrerender2);
diff --git a/chrome/browser/browser_process_impl.cc b/chrome/browser/browser_process_impl.cc index 375bf3c9..aefbe00 100644 --- a/chrome/browser/browser_process_impl.cc +++ b/chrome/browser/browser_process_impl.cc
@@ -256,6 +256,8 @@ #if BUILDFLAG(IS_LINUX) #include "chrome/browser/browser_features.h" +#include "components/os_crypt/async/browser/fallback_linux_key_provider.h" +#include "components/os_crypt/async/browser/freedesktop_secret_key_provider.h" #include "components/os_crypt/async/browser/secret_portal_key_provider.h" #endif @@ -1397,6 +1399,21 @@ base::FeatureList::IsEnabled( features::kSecretPortalKeyProviderUseForEncryption))); } + if (base::FeatureList::IsEnabled( + features::kUseFreedesktopSecretKeyProvider)) { + // Use a higher priority than the SecretPortalKeyProvider. + providers.emplace_back( + /*precedence=*/15u, + std::make_unique<os_crypt_async::FreedesktopSecretKeyProvider>( + base::FeatureList::IsEnabled( + features::kUseFreedesktopSecretKeyProviderForEncryption), + l10n_util::GetStringUTF8(IDS_PRODUCT_NAME), nullptr)); + providers.emplace_back( + /*precedence=*/15u, + std::make_unique<os_crypt_async::FallbackLinuxKeyProvider>( + base::FeatureList::IsEnabled( + features::kUseFreedesktopSecretKeyProviderForEncryption))); + } #endif // BUILDFLAG(IS_LINUX) os_crypt_async_ =
diff --git a/chrome/browser/browser_resources.grd b/chrome/browser/browser_resources.grd index 5d02943..9e2507b1b 100644 --- a/chrome/browser/browser_resources.grd +++ b/chrome/browser/browser_resources.grd
@@ -155,6 +155,7 @@ </if> <include name="IDR_BRAILLE_MANIFEST" file="resources\chromeos\accessibility\braille_ime\manifest.json" type="BINDATA" /> + <include name="IDR_BRAILLE_MANIFEST_MV3" file="resources\chromeos\accessibility\braille_ime\mv3\manifest.json" type="BINDATA" /> <include name="IDR_SMB_SHARES_DIALOG_CONTAINER_HTML" file="resources\chromeos\smb_shares\smb_share_dialog_container.html" type="chrome_html" /> <include name="IDR_SMB_SHARES_DIALOG_JS" file="${root_gen_dir}\chrome\browser\resources\chromeos\smb_shares\smb_share_dialog.js" use_base_dir="false" type="chrome_html" /> <include name="IDR_SMB_CREDENTIALS_DIALOG_CONTAINER_HTML" file="resources\chromeos\smb_shares\smb_credentials_dialog_container.html" type="chrome_html" />
diff --git a/chrome/browser/commerce/merchant_viewer/android/java/src/org/chromium/chrome/browser/merchant_viewer/MerchantTrustBottomSheetMediator.java b/chrome/browser/commerce/merchant_viewer/android/java/src/org/chromium/chrome/browser/merchant_viewer/MerchantTrustBottomSheetMediator.java index b4f1cc7..ba35d39 100644 --- a/chrome/browser/commerce/merchant_viewer/android/java/src/org/chromium/chrome/browser/merchant_viewer/MerchantTrustBottomSheetMediator.java +++ b/chrome/browser/commerce/merchant_viewer/android/java/src/org/chromium/chrome/browser/merchant_viewer/MerchantTrustBottomSheetMediator.java
@@ -121,7 +121,7 @@ if (navigation.hasCommitted()) { mToolbarModel.set( BottomSheetToolbarProperties.URL, - mWebContents.get().getVisibleUrl()); + getWebContents().getVisibleUrl()); } } };
diff --git a/chrome/browser/component_updater/lacros_component_remover.cc b/chrome/browser/component_updater/lacros_component_remover.cc new file mode 100644 index 0000000..3662fe80 --- /dev/null +++ b/chrome/browser/component_updater/lacros_component_remover.cc
@@ -0,0 +1,34 @@ +// Copyright 2025 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/component_updater/lacros_component_remover.h" + +#include "base/files/file_path.h" +#include "base/files/file_util.h" +#include "base/functional/bind.h" +#include "base/task/sequenced_task_runner.h" +#include "base/task/thread_pool.h" + +namespace component_updater { + +void DeleteStatefulLacros(const base::FilePath& user_data_dir) { + scoped_refptr<base::SequencedTaskRunner> sequenced_task_runner = + base::ThreadPool::CreateSequencedTaskRunner( + {base::MayBlock(), base::TaskPriority::BEST_EFFORT}); + sequenced_task_runner->PostTask( + FROM_HERE, base::BindOnce( + [](const base::FilePath& user_data_dir) { + base::DeletePathRecursively(user_data_dir.Append( + FILE_PATH_LITERAL("lacros-dogfood-canary"))); + base::DeletePathRecursively(user_data_dir.Append( + FILE_PATH_LITERAL("lacros-dogfood-dev"))); + base::DeletePathRecursively(user_data_dir.Append( + FILE_PATH_LITERAL("lacros-dogfood-beta"))); + base::DeletePathRecursively(user_data_dir.Append( + FILE_PATH_LITERAL("lacros-dogfood-stable"))); + }, + user_data_dir)); +} + +} // namespace component_updater
diff --git a/chrome/browser/component_updater/lacros_component_remover.h b/chrome/browser/component_updater/lacros_component_remover.h new file mode 100644 index 0000000..5581f1e --- /dev/null +++ b/chrome/browser/component_updater/lacros_component_remover.h
@@ -0,0 +1,18 @@ +// Copyright 2025 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_COMPONENT_UPDATER_LACROS_COMPONENT_REMOVER_H_ +#define CHROME_BROWSER_COMPONENT_UPDATER_LACROS_COMPONENT_REMOVER_H_ + +namespace base { +class FilePath; +} // namespace base + +namespace component_updater { + +void DeleteStatefulLacros(const base::FilePath& user_data_dir); + +} // namespace component_updater + +#endif // CHROME_BROWSER_COMPONENT_UPDATER_LACROS_COMPONENT_REMOVER_H_
diff --git a/chrome/browser/component_updater/registration.cc b/chrome/browser/component_updater/registration.cc index a05936c6..672bb01 100644 --- a/chrome/browser/component_updater/registration.cc +++ b/chrome/browser/component_updater/registration.cc
@@ -15,7 +15,6 @@ #include "base/task/thread_pool.h" #include "build/branding_buildflags.h" #include "build/build_config.h" -#include "build/chromeos_buildflags.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/buildflags.h" #include "chrome/browser/component_updater/app_provisioning_component_installer.h" @@ -83,9 +82,9 @@ #include "media/base/media_switches.h" #endif // !BUILDFLAG(IS_ANDROID) -#if BUILDFLAG(IS_CHROMEOS_ASH) +#if BUILDFLAG(IS_CHROMEOS) #include "chrome/browser/component_updater/smart_dim_component_installer.h" -#endif // BUILDFLAG(IS_CHROMEOS_ASH) +#endif // BUILDFLAG(IS_CHROMEOS) #if BUILDFLAG(ENABLE_MEDIA_FOUNDATION_WIDEVINE_CDM) #include "chrome/browser/component_updater/media_foundation_widevine_cdm_component_installer.h" @@ -110,6 +109,10 @@ #include "ui/aura/env.h" #endif +#if BUILDFLAG(IS_CHROMEOS) +#include "chrome/browser/component_updater/lacros_component_remover.h" +#endif // BUILDFLAG(IS_CHROMEOS) + namespace component_updater { void RegisterComponentsForUpdate() { @@ -167,14 +170,13 @@ DeleteHistorySearchStringsComponent(path); } -#if BUILDFLAG(IS_CHROMEOS_LACROS) - if (base::SysInfo::IsRunningOnChromeOS()) { - // PNaCl on Lacros used to be a component, but on real devices this has - // been replaced by a link to the files also used by ash. - // Clean up the component if it is present. - DeletePnaclComponent(path); - } -#endif // BUILDFLAG(IS_CHROMEOS_LACROS) +#if BUILDFLAG(IS_CHROMEOS) + // Lacros is sunsetted. While rootfs Lacros was already taken care of, + // stateful Lacros needs to be cleaned up just like a regular component. + // TODO(crbug.com/380780352): Remove this after the stepping stone. + component_updater::DeleteStatefulLacros(path); +#endif // BUILDFLAG(IS_CHROMEOS) + #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) // NaCl and PNaCl are no longer supported on Windows and Mac, clean up // remaining component. @@ -188,12 +190,12 @@ // policies on Fuchsia. RegisterFileTypePoliciesComponent(cus); -#if !BUILDFLAG(IS_CHROMEOS_ASH) +#if !BUILDFLAG(IS_CHROMEOS) // CRLSetFetcher attempts to load a CRL set from either the local disk or // network. // For Chrome OS this registration is delayed until user login. component_updater::RegisterCRLSetComponent(cus); -#endif // !BUILDFLAG(IS_CHROMEOS_ASH) +#endif // !BUILDFLAG(IS_CHROMEOS) RegisterOriginTrialsComponent(cus); RegisterMediaEngagementPreloadComponent(cus, base::OnceClosure()); @@ -207,10 +209,10 @@ RegisterSafetyTipsComponent(cus); RegisterCrowdDenyComponent(cus); -#if BUILDFLAG(IS_CHROMEOS_ASH) +#if BUILDFLAG(IS_CHROMEOS) RegisterSmartDimComponent(cus); RegisterAppProvisioningComponent(cus); -#endif // !BUILDFLAG(IS_CHROMEOS_ASH) +#endif // !BUILDFLAG(IS_CHROMEOS) #if BUILDFLAG(USE_MINIKIN_HYPHENATION) && !BUILDFLAG(IS_ANDROID) RegisterHyphenationComponent(cus);
diff --git a/chrome/browser/component_updater/widevine_cdm_component_installer.cc b/chrome/browser/component_updater/widevine_cdm_component_installer.cc index 7ae4e6d8..034a5f3 100644 --- a/chrome/browser/component_updater/widevine_cdm_component_installer.cc +++ b/chrome/browser/component_updater/widevine_cdm_component_installer.cc
@@ -52,7 +52,7 @@ #include "chrome/common/media/component_widevine_cdm_hint_file_linux.h" #endif -#if BUILDFLAG(IS_CHROMEOS_ASH) +#if BUILDFLAG(IS_CHROMEOS) #include "chromeos/ash/components/dbus/image_loader/image_loader_client.h" #endif @@ -72,7 +72,7 @@ static_assert(std::size(kWidevineSha2Hash) == crypto::kSHA256Length, "Wrong hash length"); -#if BUILDFLAG(IS_CHROMEOS_ASH) +#if BUILDFLAG(IS_CHROMEOS) // On ChromeOS the component updated CDM comes as a disk image which must be // registered and then mounted in order to access the files. The startup // script that mounts the image (widevine-cdm.conf) also uses this name. @@ -146,7 +146,7 @@ return cdm_path; } -#if BUILDFLAG(IS_CHROMEOS_ASH) +#if BUILDFLAG(IS_CHROMEOS) // This is called when ImageLoaderClient::RegisterComponent() is done. void OnImageRegistered(std::optional<bool> result) { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); @@ -244,7 +244,7 @@ loader->LoadComponent(ImageLoaderComponentName, base::BindOnce(&OnImageLoaded)); } -#endif // BUILDFLAG(IS_CHROMEOS_ASH) +#endif // BUILDFLAG(IS_CHROMEOS) } // namespace @@ -300,12 +300,12 @@ DVLOG(1) << __func__ << ": install_dir=" << install_dir << ", manifest=" << manifest; -#if BUILDFLAG(IS_CHROMEOS_ASH) - // On ASH ChromeOS, anything downloaded by Component Updater is an image - // that needs to be mounted before the files it contains can be used. So - // simply register the image, so that it can be mounted next time the - // device boots. It will also be mounted by UpdateCdmPath() so that the hint - // file can be updated. +#if BUILDFLAG(IS_CHROMEOS) + // On ChromeOS, anything downloaded by Component Updater is an image that + // needs to be mounted before the files it contains can be used. So simply + // register the image, so that it can be mounted next time the device boots. + // It will also be mounted by UpdateCdmPath() so that the hint file can be + // updated. auto* version = manifest.FindString("version"); if (!version) { return update_client::CrxInstaller::Result( @@ -344,7 +344,7 @@ bool WidevineCdmComponentInstallerPolicy::VerifyInstallation( const base::Value::Dict& manifest, const base::FilePath& install_dir) const { -#if !BUILDFLAG(IS_CHROMEOS_ASH) +#if !BUILDFLAG(IS_CHROMEOS) // On ChromeOS, what gets downloaded is an image rather than the directory // structure expected. As a result, we can not check that there is an // library contained until the image is loaded. But on all other systems @@ -401,7 +401,7 @@ return; } -#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS) +#if BUILDFLAG(IS_LINUX) VLOG(1) << "Updating hint file with Widevine CDM " << cdm_version; // This is running on a thread that allows IO, so simply update the hint file. @@ -409,8 +409,8 @@ PLOG(WARNING) << "Failed to update Widevine CDM hint path."; } -#elif BUILDFLAG(IS_CHROMEOS_ASH) - // On ChromeOS ASH, the selected CDM could be the bundled CDM or an image +#elif BUILDFLAG(IS_CHROMEOS) + // On ChromeOS, the selected CDM could be the bundled CDM or an image // containing the CDM downloaded by CU. As the CDM is loaded when Chrome // starts, there is no need to register it as the new version can't be // used until the device restarts. However, we do want to update the hint
diff --git a/chrome/browser/creator/android/java/src/org/chromium/chrome/browser/creator/CreatorTabMediator.java b/chrome/browser/creator/android/java/src/org/chromium/chrome/browser/creator/CreatorTabMediator.java index 8d4fc9a..fadbff1 100644 --- a/chrome/browser/creator/android/java/src/org/chromium/chrome/browser/creator/CreatorTabMediator.java +++ b/chrome/browser/creator/android/java/src/org/chromium/chrome/browser/creator/CreatorTabMediator.java
@@ -126,7 +126,7 @@ public void didFinishNavigationInPrimaryMainFrame(NavigationHandle navigation) { if (navigation.hasCommitted()) { mIsOnErrorPage = navigation.isErrorPage(); - mSheetContent.updateURL(mWebContents.get().getVisibleUrl()); + mSheetContent.updateURL(getWebContents().getVisibleUrl()); } else { // Not viewable contents such as download. Show a toast and close the // tab.
diff --git a/chrome/browser/data_sharing/android/java/src/org/chromium/chrome/browser/data_sharing/DataSharingTabGroupsDelegate.java b/chrome/browser/data_sharing/android/java/src/org/chromium/chrome/browser/data_sharing/DataSharingTabGroupsDelegate.java index ed611cc..c78bf072 100644 --- a/chrome/browser/data_sharing/android/java/src/org/chromium/chrome/browser/data_sharing/DataSharingTabGroupsDelegate.java +++ b/chrome/browser/data_sharing/android/java/src/org/chromium/chrome/browser/data_sharing/DataSharingTabGroupsDelegate.java
@@ -25,14 +25,6 @@ * @param context The context of the current activity. * @param gurl The GURL of the page to be opened in CCT. */ - public void openLearnMoreSharedTabGroupsPage(Context context, GURL gurl); - - /** - * Open url in the Chrome Custom Tab. - * - * @param context The context of the current activity. - * @param gurl The GURL of the page to be opened in CCT. - */ public void openUrlInChromeCustomTab(Context context, GURL gurl); /**
diff --git a/chrome/browser/data_sharing/android/java/src/org/chromium/chrome/browser/data_sharing/DataSharingTabManager.java b/chrome/browser/data_sharing/android/java/src/org/chromium/chrome/browser/data_sharing/DataSharingTabManager.java index ba630a6..53bba183 100644 --- a/chrome/browser/data_sharing/android/java/src/org/chromium/chrome/browser/data_sharing/DataSharingTabManager.java +++ b/chrome/browser/data_sharing/android/java/src/org/chromium/chrome/browser/data_sharing/DataSharingTabManager.java
@@ -84,7 +84,7 @@ private static final String LEARN_MORE_SHARED_TAB_GROUP_PAGE_URL = "https://support.google.com/chrome/?p=chrome_collaboration"; private static final String LEARN_ABOUT_BLOCKED_ACCOUNTS_URL = - "https://support.google.com/chrome/?p=chrome_collaboration"; + "https://support.google.com/accounts/answer/6388749"; private final ObservableSupplier<TabModelSelector> mTabModelSelectorSupplier; private final DataSharingTabGroupsDelegate mDataSharingTabGroupsDelegate; @@ -846,12 +846,6 @@ DataSharingUiConfig.DataSharingCallback dataSharingCallback = new DataSharingUiConfig.DataSharingCallback() { @Override - public void onLearnMoreAboutSharedTabGroupsClicked(Context context, GURL url) { - mDataSharingTabGroupsDelegate.openLearnMoreSharedTabGroupsPage( - context, url); - } - - @Override public void onClickOpenChromeCustomTab(Context context, GURL url) { mDataSharingTabGroupsDelegate.openUrlInChromeCustomTab(context, url); }
diff --git a/chrome/browser/download/chrome_download_manager_delegate.cc b/chrome/browser/download/chrome_download_manager_delegate.cc index ca8026378..46545fe1 100644 --- a/chrome/browser/download/chrome_download_manager_delegate.cc +++ b/chrome/browser/download/chrome_download_manager_delegate.cc
@@ -1936,13 +1936,6 @@ show_download_in_folder); } #endif - if (!download->GetAutoOpened()) { - download::DownloadContent download_content = - download::DownloadContentFromMimeType(download->GetMimeType(), false); - safe_browsing::RecordDownloadOpenedLatency( - download->GetDangerType(), download_content, base::Time::Now(), - download->GetEndTime(), show_download_in_folder); - } } void ChromeDownloadManagerDelegate::MaybeSendDangerousDownloadCanceledReport(
diff --git a/chrome/browser/enterprise/connectors/analysis/content_analysis_dialog.cc b/chrome/browser/enterprise/connectors/analysis/content_analysis_dialog.cc index 003dc79f..00b0aff 100644 --- a/chrome/browser/enterprise/connectors/analysis/content_analysis_dialog.cc +++ b/chrome/browser/enterprise/connectors/analysis/content_analysis_dialog.cc
@@ -18,6 +18,7 @@ #include "components/constrained_window/constrained_window_views.h" #include "components/strings/grit/components_strings.h" #include "components/vector_icons/vector_icons.h" +#include "components/web_modal/web_contents_modal_dialog_manager.h" #include "content/public/browser/browser_task_traits.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/web_contents.h" @@ -284,6 +285,20 @@ return; } + auto* manager = + web_modal::WebContentsModalDialogManager::FromWebContents(web_contents()); + if (!manager) { + // `manager` being null indicates that `web_contents()` doesn't correspond + // to a browser tab (ex: an extension background page reading the + // clipboard). In such a case, we don't show a dialog and instead simply + // accept/cancel the result immediately. See crbug.com/374120523 and + // crbug.com/388049470 for more context. + if (!is_pending()) { + CancelButtonCallback(); + } + return; + } + // If the web contents is still valid when the delay timer goes off and the // dialog has not yet been shown, show it now. if (web_contents() && !contents_view_) {
diff --git a/chrome/browser/enterprise/connectors/analysis/content_analysis_dialog_browsertest.cc b/chrome/browser/enterprise/connectors/analysis/content_analysis_dialog_browsertest.cc index cf23f05d6..a4fa0ac 100644 --- a/chrome/browser/enterprise/connectors/analysis/content_analysis_dialog_browsertest.cc +++ b/chrome/browser/enterprise/connectors/analysis/content_analysis_dialog_browsertest.cc
@@ -22,12 +22,15 @@ #include "chrome/browser/enterprise/connectors/test/fake_content_analysis_delegate.h" #include "chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_utils.h" #include "chrome/browser/ui/browser.h" +#include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/test/test_browser_dialog.h" +#include "chrome/browser/ui/webui/chrome_web_contents_handler.h" #include "chrome/grit/generated_resources.h" #include "chrome/grit/theme_resources.h" #include "components/download/public/common/mock_download_item.h" #include "components/enterprise/common/proto/connectors.pb.h" #include "components/prefs/scoped_user_pref_update.h" +#include "components/web_modal/web_contents_modal_dialog_manager.h" #include "content/public/browser/web_contents_observer.h" #include "content/public/test/browser_test.h" #include "content/public/test/browser_test_utils.h" @@ -39,8 +42,10 @@ #include "ui/views/controls/image_view.h" #include "ui/views/controls/textarea/textarea.h" #include "ui/views/controls/throbber.h" +#include "ui/views/controls/webview/web_dialog_view.h" #include "ui/views/test/ax_event_counter.h" #include "ui/views/test/views_test_utils.h" +#include "ui/web_dialogs/test/test_web_dialog_delegate.h" namespace enterprise_connectors { @@ -275,7 +280,36 @@ base::TimeDelta response_delay() const { return std::get<2>(GetParam()); } + void SetUpOnMainThread() override { + ui::test::TestWebDialogDelegate* delegate = + new ui::test::TestWebDialogDelegate(GURL(url::kAboutBlankURL)); + delegate->SetDeleteOnClosedAndObserve(&web_dialog_delegate_destroyed_); + + auto view = std::make_unique<views::WebDialogView>( + browser()->profile(), delegate, + std::make_unique<ChromeWebContentsHandler>()); + view->SetOwnedByWidget(true); + gfx::NativeView parent_view = + browser()->tab_strip_model()->GetActiveWebContents()->GetNativeView(); + view_ = view.get(); + view_tracker_.SetView(view_); + + auto* widget = + views::Widget::CreateWindowWithParent(std::move(view), parent_view); + widget->Show(); + + EXPECT_TRUE(content::WaitForLoadStop(view_->web_contents())); + } + + content::WebContents* GetWebViewDialogContents() { + return view_->web_contents(); + } + private: + views::ViewTracker view_tracker_; + raw_ptr<views::WebDialogView, DisableDanglingPtrDetection> view_ = nullptr; + bool web_dialog_delegate_destroyed_ = false; + raw_ptr<ContentAnalysisDialog, DanglingUntriaged> dialog_; base::TimeTicks ctor_called_timestamp_; @@ -589,6 +623,43 @@ EXPECT_TRUE(called); } +IN_PROC_BROWSER_TEST_P(ContentAnalysisDialogBehaviorBrowserTest, + NoWebContentsModalDialogManager) { + base::ScopedAllowBlockingForTesting allow_blocking; + + // Setup policies to enable deep scanning, its UI and the responses to be + // simulated. + enterprise_connectors::test::SetAnalysisConnector( + browser()->profile()->GetPrefs(), FILE_ATTACHED, + kBlockingScansForDlpAndMalware); + SetStatusCallbackResponse( + safe_browsing::SimpleContentAnalysisResponseForTesting( + dlp_success(), malware_success(), /*has_custom_rule_message=*/false)); + + // Set up delegate test values. + test::FakeContentAnalysisDelegate::SetResponseDelay(response_delay()); + SetUpDelegate(); + + base::RunLoop run_loop; + ContentAnalysisDelegate::Data data; + CreateFilesForTest({"foo.doc"}, {"content"}, &data); + ASSERT_TRUE(ContentAnalysisDelegate::IsEnabled( + browser()->profile(), GURL(kTestUrl), &data, + enterprise_connectors::AnalysisConnector::FILE_ATTACHED)); + + ContentAnalysisDelegate::CreateForWebContents( + GetWebViewDialogContents(), std::move(data), + base::BindOnce( + [](base::OnceClosure quit_closure, + const ContentAnalysisDelegate::Data& data, + ContentAnalysisDelegate::Result& result) { + std::move(quit_closure).Run(); + }, + run_loop.QuitClosure()), + safe_browsing::DeepScanAccessPoint::UPLOAD); + run_loop.Run(); +} + // The scan type controls if DLP, malware or both are enabled via policies. The // dialog currently behaves identically in all 3 cases, so this parameter // ensures this assumption is not broken by new code.
diff --git a/chrome/browser/extensions/account_extension_tracker.cc b/chrome/browser/extensions/account_extension_tracker.cc index a305b553..77ca943 100644 --- a/chrome/browser/extensions/account_extension_tracker.cc +++ b/chrome/browser/extensions/account_extension_tracker.cc
@@ -127,6 +127,9 @@ const signin::PrimaryAccountChangeEvent& event_details) { ExtensionRegistry* extension_registry = ExtensionRegistry::Get(profile_); + bool observers_notified = false; + + // First look for whether or not the user has signed in or signed out. auto signin_event_type = event_details.GetEventTypeFor(signin::ConsentLevel::kSignin); switch (signin_event_type) { @@ -149,6 +152,10 @@ } extensions_installed_with_signin_promo_.clear(); + + // Don't notify observers that extensions uploadability has changed here, + // since initial sync data has not been received yet. Notifying here now + // may cause UI flickers from events fired in rapid succession. break; } case signin::PrimaryAccountChangeEvent::Type::kCleared: { @@ -161,11 +168,33 @@ for (const auto& extension : extensions) { SetAccountExtensionType(extension->id(), AccountExtensionType::kLocal); } + + NotifyOnExtensionsUploadabilityChanged(); + observers_notified = true; break; } case signin::PrimaryAccountChangeEvent::Type::kNone: break; } + + // Now see if there is any change on whether the user has enabled or disabled + // full sync. If there is, notify observers that the eligibility for uploading + // extensions may have changed. If observers have already been notified, just + // return early. + if (observers_notified) { + return; + } + + auto sync_event_type = + event_details.GetEventTypeFor(signin::ConsentLevel::kSync); + switch (sync_event_type) { + case signin::PrimaryAccountChangeEvent::Type::kCleared: + case signin::PrimaryAccountChangeEvent::Type::kSet: + NotifyOnExtensionsUploadabilityChanged(); + break; + case signin::PrimaryAccountChangeEvent::Type::kNone: + break; + } } void AccountExtensionTracker::OnExtensionSyncDataReceived( @@ -179,9 +208,16 @@ if (type == AccountExtensionType::kLocal) { SetAccountExtensionType(extension_id, AccountExtensionType::kAccountInstalledLocally); + for (auto& observer : observers_) { + observer.OnExtensionUploadabilityChanged(extension_id); + } } } +void AccountExtensionTracker::OnInitialExtensionsSyncDataReceived() { + NotifyOnExtensionsUploadabilityChanged(); +} + AccountExtensionTracker::AccountExtensionType AccountExtensionTracker::GetAccountExtensionType( const ExtensionId& extension_id) const { @@ -221,6 +257,9 @@ // Uploading extensions as "account extensions" aka extensions syncing to the // current signed in user, is only enabled if the user is signed in and // syncing extensions in transport mode. + // If the user is not signed in, then nothing syncs. + // If the user is signed into full sync, then any syncable extensions + // automatically get uploaded so user uploading is disabled. if (!sync_util::IsSyncingExtensionsInTransportMode(profile_)) { return false; } @@ -238,6 +277,14 @@ SetAccountExtensionType(extension_id, type); } +void AccountExtensionTracker::AddObserver(Observer* observer) { + observers_.AddObserver(observer); +} + +void AccountExtensionTracker::RemoveObserver(Observer* observer) { + observers_.RemoveObserver(observer); +} + void AccountExtensionTracker::SetAccountExtensionType( const ExtensionId& extension_id, AccountExtensionTracker::AccountExtensionType type) { @@ -252,4 +299,10 @@ [&extension_id](const ExtensionId& id) { return extension_id == id; }); } +void AccountExtensionTracker::NotifyOnExtensionsUploadabilityChanged() { + for (auto& observer : observers_) { + observer.OnExtensionsUploadabilityChanged(); + } +} + } // namespace extensions
diff --git a/chrome/browser/extensions/account_extension_tracker.h b/chrome/browser/extensions/account_extension_tracker.h index 9da7aa1..ce4c295 100644 --- a/chrome/browser/extensions/account_extension_tracker.h +++ b/chrome/browser/extensions/account_extension_tracker.h
@@ -52,6 +52,18 @@ kLast = 2, }; + class Observer : public base::CheckedObserver { + public: + // Called when an extension's eligibility to be uploaded to the user's + // account may have changed. + virtual void OnExtensionUploadabilityChanged(const ExtensionId& id) = 0; + + // Called when whether extensions can be uploaded to the user's account may + // be changed. Usually emitted when the initial sync download completes or + // when the user is no longer syncing extensions in transport mode. + virtual void OnExtensionsUploadabilityChanged() = 0; + }; + explicit AccountExtensionTracker(Profile* profile); AccountExtensionTracker(const AccountExtensionTracker&) = delete; @@ -78,6 +90,11 @@ // Called when sync data is received for the given `extension_id`. void OnExtensionSyncDataReceived(const ExtensionId& extension_id); + // Called just after the initial set of extension sync data is received. + // i.e. during browser startup (if extensions sync is already enabled), or + // once the initial download completes after extensions sync gets enabled. + void OnInitialExtensionsSyncDataReceived(); + AccountExtensionType GetAccountExtensionType( const ExtensionId& extension_id) const; @@ -92,6 +109,9 @@ void SetAccountExtensionTypeForTesting(const ExtensionId& extension_id, AccountExtensionType type); + void AddObserver(Observer* observer); + void RemoveObserver(Observer* observer); + private: // Sets the extension's AccountExtensionType. Called when the extension is // installed (not updated) or when there is incoming sync data for the @@ -102,12 +122,18 @@ // Removes `extension_id` in `extensions_installed_with_signin_promo_`. void RemoveExpiredExtension(const ExtensionId& extension_id); + // Notifies observers that the eligibility of multiple extensions to be + // uploaded to the user's account may have changed. + void NotifyOnExtensionsUploadabilityChanged(); + const raw_ptr<Profile> profile_; // Keeps track of extensions for which a signin promo was shown after // installation. std::vector<ExtensionId> extensions_installed_with_signin_promo_; + base::ObserverList<Observer> observers_; + // IdentityManager observer. base::ScopedObservation<signin::IdentityManager, signin::IdentityManager::Observer>
diff --git a/chrome/browser/extensions/api/developer_private/developer_private_api.cc b/chrome/browser/extensions/api/developer_private/developer_private_api.cc index cd5d2b04..dada213 100644 --- a/chrome/browser/extensions/api/developer_private/developer_private_api.cc +++ b/chrome/browser/extensions/api/developer_private/developer_private_api.cc
@@ -509,6 +509,7 @@ DependsOn(ExtensionSystemFactory::GetInstance()); DependsOn(PermissionsManager::GetFactory()); DependsOn(ToolbarActionsModelFactory::GetInstance()); + DependsOn(AccountExtensionTracker::GetFactory()); } // static @@ -537,6 +538,12 @@ ExtensionSystem::Get(profile)->extension_service()->allowlist()); permissions_manager_observation_.Observe(PermissionsManager::Get(profile)); toolbar_actions_model_observation_.Observe(ToolbarActionsModel::Get(profile)); + + if (sync_util::IsExtensionsExplicitSigninEnabled()) { + account_extension_tracker_observation_.Observe( + AccountExtensionTracker::Get(profile)); + } + pref_change_registrar_.Init(profile->GetPrefs()); // The unretained is safe, since the PrefChangeRegistrar unregisters the // callback on destruction. @@ -750,6 +757,22 @@ } } +void DeveloperPrivateEventRouter::OnExtensionUploadabilityChanged( + const ExtensionId& id) { + BroadcastItemStateChanged(developer::EventType::kPrefsChanged, id); +} + +void DeveloperPrivateEventRouter::OnExtensionsUploadabilityChanged() { + const ExtensionSet extensions = + ExtensionRegistry::Get(profile_)->GenerateInstalledExtensionsSet(); + for (const auto& extension : extensions) { + if (sync_util::ShouldSync(profile_, extension.get())) { + BroadcastItemStateChanged(developer::EventType::kPrefsChanged, + extension->id()); + } + } +} + void DeveloperPrivateEventRouter::OnProfilePrefChanged() { base::Value::List args; args.Append(DeveloperPrivateAPI::CreateProfileInfo(profile_)->ToValue());
diff --git a/chrome/browser/extensions/api/developer_private/developer_private_api.h b/chrome/browser/extensions/api/developer_private/developer_private_api.h index ead363f..8211ded 100644 --- a/chrome/browser/extensions/api/developer_private/developer_private_api.h +++ b/chrome/browser/extensions/api/developer_private/developer_private_api.h
@@ -13,6 +13,7 @@ #include "base/memory/raw_ptr.h" #include "base/memory/weak_ptr.h" #include "base/scoped_observation.h" +#include "chrome/browser/extensions/account_extension_tracker.h" #include "chrome/browser/extensions/commands/command_service.h" #include "chrome/browser/extensions/error_console/error_console.h" #include "chrome/browser/extensions/extension_allowlist.h" @@ -78,7 +79,8 @@ public ExtensionManagement::Observer, public WarningService::Observer, public PermissionsManager::Observer, - public ToolbarActionsModel::Observer { + public ToolbarActionsModel::Observer, + public AccountExtensionTracker::Observer { public: explicit DeveloperPrivateEventRouter(Profile* profile); @@ -170,6 +172,10 @@ void OnToolbarModelInitialized() override {} void OnToolbarPinnedActionsChanged() override; + // AccountExtensionTracker::Observer: + void OnExtensionUploadabilityChanged(const ExtensionId& id) override; + void OnExtensionsUploadabilityChanged() override; + // Handles a profile preference change. void OnProfilePrefChanged(); @@ -204,6 +210,9 @@ permissions_manager_observation_{this}; base::ScopedObservation<ToolbarActionsModel, ToolbarActionsModel::Observer> toolbar_actions_model_observation_{this}; + base::ScopedObservation<AccountExtensionTracker, + AccountExtensionTracker::Observer> + account_extension_tracker_observation_{this}; raw_ptr<Profile> profile_;
diff --git a/chrome/browser/extensions/api/developer_private/developer_private_api_unittest.cc b/chrome/browser/extensions/api/developer_private/developer_private_api_unittest.cc index 9069a56..29047e1 100644 --- a/chrome/browser/extensions/api/developer_private/developer_private_api_unittest.cc +++ b/chrome/browser/extensions/api/developer_private/developer_private_api_unittest.cc
@@ -5,6 +5,7 @@ #include "chrome/browser/extensions/api/developer_private/developer_private_api.h" #include <memory> +#include <optional> #include <string_view> #include <utility> @@ -33,6 +34,8 @@ #include "chrome/browser/extensions/extension_management_test_util.h" #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/extensions/extension_service_test_with_install.h" +#include "chrome/browser/extensions/extension_sync_data.h" +#include "chrome/browser/extensions/extension_sync_service.h" #include "chrome/browser/extensions/extension_sync_util.h" #include "chrome/browser/extensions/extension_util.h" #include "chrome/browser/extensions/manifest_v2_experiment_manager.h" @@ -56,6 +59,7 @@ #include "components/signin/public/identity_manager/identity_manager.h" #include "components/supervised_user/core/common/features.h" #include "components/sync/base/features.h" +#include "components/sync/test/fake_sync_change_processor.h" #include "components/sync_preferences/testing_pref_service_syncable.h" #include "content/public/test/mock_render_process_host.h" #include "content/public/test/web_contents_tester.h" @@ -78,6 +82,7 @@ #include "extensions/common/extension_features.h" #include "extensions/common/extension_id.h" #include "extensions/common/extension_set.h" +#include "extensions/common/extension_urls.h" #include "extensions/common/manifest_constants.h" #include "extensions/common/mojom/context_type.mojom.h" #include "extensions/common/permissions/permission_set.h" @@ -112,6 +117,31 @@ return has_pref(id, context); } +bool DoesItemChangedEventMatch( + const Event& event, + const ExtensionId& extension_id, + const api::developer_private::EventType event_type, + api::developer_private::ExtensionInfo* info_from_event) { + CHECK_GE(1u, event.event_args.size()); + std::optional<api::developer_private::EventData> event_data = + api::developer_private::EventData::FromValue(event.event_args[0]); + if (!event_data) { + return false; + } + + if (event_data->item_id != extension_id || + event_data->event_type != event_type) { + return false; + } + + if (event_data->extension_info) { + CHECK_EQ(extension_id, event_data->extension_info->id); + *info_from_event = std::move(*event_data->extension_info); + } + + return true; +} + bool WasItemChangedEventDispatched( const TestEventRouterObserver& observer, const ExtensionId& extension_id, @@ -120,22 +150,13 @@ api::developer_private::OnItemStateChanged::kEventName; const auto& event_map = observer.events(); auto iter = event_map.find(kEventName); - if (iter == event_map.end()) - return false; - - const Event& event = *iter->second; - CHECK_GE(1u, event.event_args.size()); - std::optional<api::developer_private::EventData> event_data = - api::developer_private::EventData::FromValue(event.event_args[0]); - if (!event_data) - return false; - - if (event_data->item_id != extension_id || - event_data->event_type != event_type) { + if (iter == event_map.end()) { return false; } - return true; + api::developer_private::ExtensionInfo info; + return DoesItemChangedEventMatch(*iter->second, extension_id, event_type, + &info); } bool WasUserSiteSettingsChangedEventDispatched( @@ -279,6 +300,91 @@ << function->GetError(); } +// A more targeted version of TestEventRouterObserver to pick up a prefs changed +// event for a given extension. +class ItemStatePrefsChangedObserver : public EventRouter::TestObserver { + public: + ItemStatePrefsChangedObserver(EventRouter* event_router, + const ExtensionId& extension_id); + + ItemStatePrefsChangedObserver(const ItemStatePrefsChangedObserver&) = delete; + ItemStatePrefsChangedObserver& operator=( + const ItemStatePrefsChangedObserver&) = delete; + + ~ItemStatePrefsChangedObserver() override; + + // Waits until a matching prefs changed event is dispatched for the + // `extension_id_`. + void WaitForEvent(); + + // Resets the `event_info_` so the observer can wait for another matching + // event. + void Reset(); + + bool WasEventDispatched() { return event_info_.has_value(); } + + api::developer_private::ExtensionInfo event_info() { + return event_info_.has_value() ? event_info_->Clone() + : api::developer_private::ExtensionInfo(); + } + + private: + // EventRouter::TestObserver: + void OnWillDispatchEvent(const Event& event) override; + void OnDidDispatchEventToProcess(const Event& event, + int process_id) override {} + + // The event info from the prefs changed event. Null if a matching event has + // not yet been dispatched. + std::optional<api::developer_private::ExtensionInfo> event_info_; + std::unique_ptr<base::RunLoop> run_loop_; + + raw_ptr<EventRouter> event_router_; + const ExtensionId extension_id_; +}; + +ItemStatePrefsChangedObserver::ItemStatePrefsChangedObserver( + EventRouter* event_router, + const ExtensionId& extension_id) + : event_router_(event_router), extension_id_(extension_id) { + event_router_->AddObserverForTesting(this); +} + +ItemStatePrefsChangedObserver::~ItemStatePrefsChangedObserver() { + // Note: can't use ScopedObserver<> here because the method is + // RemoveObserverForTesting() instead of RemoveObserver(). + event_router_->RemoveObserverForTesting(this); +} + +void ItemStatePrefsChangedObserver::WaitForEvent() { + while (!event_info_.has_value()) { + // Create a new `RunLoop` since reuse is not supported. + run_loop_ = std::make_unique<base::RunLoop>(); + run_loop_->Run(); + run_loop_.reset(); + } +} + +void ItemStatePrefsChangedObserver::Reset() { + event_info_ = std::nullopt; +} + +void ItemStatePrefsChangedObserver::OnWillDispatchEvent(const Event& event) { + CHECK(!event.event_name.empty()); + + api::developer_private::ExtensionInfo info; + bool does_event_match = DoesItemChangedEventMatch( + event, extension_id_, api::developer_private::EventType::kPrefsChanged, + &info); + + if (does_event_match) { + event_info_ = std::move(info); + if (run_loop_) { + run_loop_->Quit(); + } + } +} + } // namespace class DeveloperPrivateApiUnitTest : public ExtensionServiceTestWithInstall { @@ -3351,23 +3457,95 @@ /*disabled_features=*/{}); } + void SetUp() override { + DeveloperPrivateApiUnitTest::SetUp(); + identity_test_env_profile_adaptor_ = + std::make_unique<IdentityTestEnvironmentProfileAdaptor>(profile()); + } + DeveloperPrivateApiTransportModeUnitTest( const DeveloperPrivateApiTransportModeUnitTest&) = delete; DeveloperPrivateApiTransportModeUnitTest& operator=( const DeveloperPrivateApiTransportModeUnitTest&) = delete; + protected: + signin::IdentityTestEnvironment* identity_test_env() { + return identity_test_env_profile_adaptor_->identity_test_env(); + } + + bool CanUploadToAccount(const Extension& extension) { + return AccountExtensionTracker::Get(profile())->CanUploadAsAccountExtension( + extension); + } + + // Loads and returns a syncable extension with the given `name`. + const scoped_refptr<const Extension> LoadSyncableExtension(const char* name) { + const scoped_refptr<const Extension> syncable_extension = + ExtensionBuilder(name) + .SetLocation(mojom::ManifestLocation::kInternal) + .Build(); + EXPECT_TRUE(sync_util::ShouldSync(profile(), syncable_extension.get())); + service()->AddExtension(syncable_extension.get()); + + return syncable_extension; + } + + // Set up a listener for the given `kEventName` and returns the test + // observer. + ItemStatePrefsChangedObserver StartListeningForEvent( + const ExtensionId& extension_id) { + // We need to call DeveloperPrivateAPI::Get() in order to instantiate the + // keyed service, since it's not created by default in unit tests. + DeveloperPrivateAPI::Get(profile()); + EventRouter* event_router = EventRouter::Get(profile()); + + // The DeveloperPrivateEventRouter will only dispatch events if there's at + // least one listener to dispatch to. Create one. + GURL dummy_url("chrome-untrusted://one"); + event_router->AddEventListenerForURL( + api::developer_private::OnItemStateChanged::kEventName, + render_process_host(), dummy_url); + + return ItemStatePrefsChangedObserver(event_router, extension_id); + } + + // Simulates an explicit sign in. This involves both the sign in itself and + // flipping the pref to record an explicit sign in. + void SimulateExplicitSignIn() { + identity_test_env_profile_adaptor_->identity_test_env() + ->MakePrimaryAccountAvailable("testy@mctestface.com", + signin::ConsentLevel::kSignin); + profile()->GetPrefs()->SetBoolean(prefs::kExplicitBrowserSignin, true); + } + + // Simulates an initial download of sync data with the given `extensions` + // present. + void SimulateInitialSync(const std::vector<const Extension*>& extensions) { + syncer::SyncDataList sync_data; + for (const auto* extension : extensions) { + ExtensionSyncData data(*extension, true, + extensions::disable_reason::DISABLE_NONE, false, + false, extension_urls::GetWebstoreUpdateUrl()); + + sync_data.push_back(data.GetSyncData()); + } + + ExtensionSyncService::Get(profile())->MergeDataAndStartSyncing( + syncer::EXTENSIONS, sync_data, + std::make_unique<syncer::FakeSyncChangeProcessor>()); + } + private: base::test::ScopedFeatureList scoped_feature_list_; + + std::unique_ptr<IdentityTestEnvironmentProfileAdaptor> + identity_test_env_profile_adaptor_; }; // Test that extensions cannot be uploaded if the user is signed out. TEST_F(DeveloperPrivateApiTransportModeUnitTest, UploadExtensionToAccount_SignedOut) { - const scoped_refptr<const Extension> extension = - ExtensionBuilder("ext") - .SetLocation(mojom::ManifestLocation::kInternal) - .Build(); - service()->AddExtension(extension.get()); + auto extension = LoadSyncableExtension("ext"); std::string args = base::StringPrintf(R"(["%s"])", extension->id().c_str()); auto upload_function = base::MakeRefCounted< @@ -3390,14 +3568,7 @@ service()->AddExtension(unsyncable_extension.get()); // Sign the user in without full sync. - auto identity_test_env_profile_adaptor = - std::make_unique<IdentityTestEnvironmentProfileAdaptor>(profile()); - identity_test_env_profile_adaptor->identity_test_env() - ->MakePrimaryAccountAvailable("testy@mctestface.com", - signin::ConsentLevel::kSignin); - // Pretend that the user has completed an explicit sign in before. This is - // necessary for extensions to sync in transport mode. - profile()->GetPrefs()->SetBoolean(prefs::kExplicitBrowserSignin, true); + SimulateExplicitSignIn(); std::string args_str = base::StringPrintf(R"(["%s"])", unsyncable_extension->id().c_str()); @@ -3417,22 +3588,10 @@ TEST_F(DeveloperPrivateApiTransportModeUnitTest, UploadExtensionToAccount) { // Add a syncable extension. - const scoped_refptr<const Extension> syncable_extension = - ExtensionBuilder("sync_ext") - .SetLocation(mojom::ManifestLocation::kInternal) - .Build(); - EXPECT_TRUE(sync_util::ShouldSync(profile(), syncable_extension.get())); - service()->AddExtension(syncable_extension.get()); + auto syncable_extension = LoadSyncableExtension("ext"); // Sign the user in without full sync. - auto identity_test_env_profile_adaptor = - std::make_unique<IdentityTestEnvironmentProfileAdaptor>(profile()); - identity_test_env_profile_adaptor->identity_test_env() - ->MakePrimaryAccountAvailable("testy@mctestface.com", - signin::ConsentLevel::kSignin); - // Pretend that the user has completed an explicit sign in before. This is - // necessary for extensions to sync in transport mode. - profile()->GetPrefs()->SetBoolean(prefs::kExplicitBrowserSignin, true); + SimulateExplicitSignIn(); // The syncable extension can be uploaded, but pretend we don't proceed with // the upload by simulating cancelling the dialog. @@ -3466,4 +3625,157 @@ syncable_extension->id())); } +// Test that an extension is uploadable when the user signs into transport mode +// and the extension is not in the user's sync data. +TEST_F(DeveloperPrivateApiTransportModeUnitTest, ExtensionUploadableOnSignIn) { + auto extension = LoadSyncableExtension("ext"); + ItemStatePrefsChangedObserver test_observer = + StartListeningForEvent(extension->id()); + + // Sign the user in without full sync. + SimulateExplicitSignIn(); + + // While the extension technically can be uploaded to the user's account, + // don't dispatch an update event if the initial sync data has not been + // received yet. + EXPECT_TRUE(CanUploadToAccount(*extension)); + EXPECT_FALSE(test_observer.WasEventDispatched()); + test_observer.Reset(); + + // Now simulate an initial sync where no extensions are present in the user's + // sync data. + SimulateInitialSync({}); + test_observer.WaitForEvent(); + + // Upon receiving the sync data, the API's event router should be notified. + auto info = test_observer.event_info(); + + // Verify that the update has alerted observers that the extension can now be + // uploaded. + EXPECT_TRUE(info.can_upload_as_account_extension); + EXPECT_TRUE(CanUploadToAccount(*extension)); +} + +// Test that an extension is not uploadable when it's already present in the +// user's sync data. +TEST_F(DeveloperPrivateApiTransportModeUnitTest, + ExtensionNotUploadableFromInitialSync) { + auto extension = LoadSyncableExtension("ext"); + ItemStatePrefsChangedObserver test_observer = + StartListeningForEvent(extension->id()); + + // Sign the user in without full sync. + SimulateExplicitSignIn(); + EXPECT_FALSE(test_observer.WasEventDispatched()); + test_observer.Reset(); + + // Simulate an initial sync where the extension is already present in the + // user's sync data. + SimulateInitialSync({extension.get()}); + test_observer.WaitForEvent(); + + // An update event should be dispatched but the extension should not be + // uploadable since it's already present in sync data. + auto info = test_observer.event_info(); + EXPECT_FALSE(info.can_upload_as_account_extension); + EXPECT_FALSE(CanUploadToAccount(*extension)); +} + +// Sign outs are not supported for ChromeOS hence this test is not run for +// ChromeOS. +#if !BUILDFLAG(IS_CHROMEOS) +// Test that extensions can no longer be uploaded once the user signs out. +TEST_F(DeveloperPrivateApiTransportModeUnitTest, CannotUploadAfterSignOut) { + // Test setup: Sign in and simulate an empty initial sync so the extension is + // uploadavble. + auto extension = LoadSyncableExtension("ext"); + ItemStatePrefsChangedObserver test_observer = + StartListeningForEvent(extension->id()); + + // Sign the user in without full sync. + SimulateExplicitSignIn(); + + SimulateInitialSync({}); + test_observer.WaitForEvent(); + + auto info = test_observer.event_info(); + EXPECT_TRUE(info.can_upload_as_account_extension); + test_observer.Reset(); + + // Now sign out. An update should be dispatched indicating that the extension + // is no longer syncable. + identity_test_env()->ClearPrimaryAccount(); + test_observer.WaitForEvent(); + info = test_observer.event_info(); + EXPECT_FALSE(info.can_upload_as_account_extension); + EXPECT_FALSE(CanUploadToAccount(*extension)); +} +#endif // !BUILDFLAG(IS_CHROMEOS) + +// Test that extensions can no longer be uploaded by the user if they sign into +// full sync mode. +TEST_F(DeveloperPrivateApiTransportModeUnitTest, CannotUploadWithFullSync) { + // Test setup: Sign in and simulate an empty initial sync so the extension is + // uploadavble. + auto extension = LoadSyncableExtension("ext"); + ItemStatePrefsChangedObserver test_observer = + StartListeningForEvent(extension->id()); + + // Sign the user in without full sync. + SimulateExplicitSignIn(); + + SimulateInitialSync({}); + test_observer.WaitForEvent(); + + auto info = test_observer.event_info(); + EXPECT_TRUE(info.can_upload_as_account_extension); + test_observer.Reset(); + + // Now sign into full sync. Since full sync mode automatically syncs any + // syncable extension, the extension cannot be uploaded anymore. + identity_test_env()->MakePrimaryAccountAvailable("testy@mctestface.com", + signin::ConsentLevel::kSync); + test_observer.WaitForEvent(); + info = test_observer.event_info(); + EXPECT_FALSE(info.can_upload_as_account_extension); + EXPECT_FALSE(CanUploadToAccount(*extension)); +} + +// Test that extensions can no longer be uploaded if an update comes in +// indicating that they're part of the user's sync data. +TEST_F(DeveloperPrivateApiTransportModeUnitTest, + UploadUpdatedAfterIncomingSync) { + // Test setup: Sign in and simulate an empty initial sync so the extension is + // uploadavble. + auto extension = LoadSyncableExtension("ext"); + ItemStatePrefsChangedObserver test_observer = + StartListeningForEvent(extension->id()); + + // Sign the user in without full sync. + SimulateExplicitSignIn(); + + SimulateInitialSync({}); + test_observer.WaitForEvent(); + + auto info = test_observer.event_info(); + EXPECT_TRUE(info.can_upload_as_account_extension); + test_observer.Reset(); + + // Simulate a later sync update where the same extension was installed on + // another device and the change is synced over. + ExtensionSyncData extension_installed_elsewhere( + *extension, true, extensions::disable_reason::DISABLE_NONE, false, false, + extension_urls::GetWebstoreUpdateUrl()); + ExtensionSyncService::Get(profile())->ProcessSyncChanges( + FROM_HERE, {extension_installed_elsewhere.GetSyncChange( + syncer::SyncChange::ACTION_UPDATE)}); + test_observer.WaitForEvent(); + + // The extension should no longer be uploadable since it is now part of the + // user's sync data. + info = test_observer.event_info(); + EXPECT_FALSE(info.can_upload_as_account_extension); + EXPECT_FALSE(CanUploadToAccount(*extension)); +} + } // namespace extensions
diff --git a/chrome/browser/extensions/api/developer_private/extension_info_generator.cc b/chrome/browser/extensions/api/developer_private/extension_info_generator.cc index 5eb5fe6..6cb10f3 100644 --- a/chrome/browser/extensions/api/developer_private/extension_info_generator.cc +++ b/chrome/browser/extensions/api/developer_private/extension_info_generator.cc
@@ -414,10 +414,33 @@ error_console_(ErrorConsole::Get(browser_context)), image_loader_(ImageLoader::Get(browser_context)), pending_image_loads_(0u) { + profile_observation_.Observe(Profile::FromBrowserContext(browser_context)); } ExtensionInfoGenerator::~ExtensionInfoGenerator() = default; +void ExtensionInfoGenerator::OnProfileWillBeDestroyed(Profile* profile) { + // Reset all references for keyed services in case this object outlives the + // profile or browser context. + profile_observation_.Reset(); + browser_context_ = nullptr; + command_service_ = nullptr; + extension_system_ = nullptr; + extension_prefs_ = nullptr; + warning_service_ = nullptr; + error_console_ = nullptr; + image_loader_ = nullptr; + + // Remove any WeakPtr to terminate any async tasks. + weak_factory_.InvalidateWeakPtrs(); + + // Flush the callback if there is one. + if (!callback_.is_null()) { + std::move(callback_).Run({}); + } + // WARNING: `this` is possibly deleted after this line! +} + void ExtensionInfoGenerator::CreateExtensionInfo( const ExtensionId& id, ExtensionInfosCallback callback) {
diff --git a/chrome/browser/extensions/api/developer_private/extension_info_generator.h b/chrome/browser/extensions/api/developer_private/extension_info_generator.h index 77ff756..0089308 100644 --- a/chrome/browser/extensions/api/developer_private/extension_info_generator.h +++ b/chrome/browser/extensions/api/developer_private/extension_info_generator.h
@@ -12,9 +12,13 @@ #include "base/functional/callback.h" #include "base/memory/raw_ptr.h" #include "base/memory/weak_ptr.h" +#include "base/scoped_observation.h" +#include "chrome/browser/profiles/profile_observer.h" #include "chrome/common/extensions/api/developer_private.h" #include "components/supervised_user/core/common/buildflags.h" #include "extensions/browser/blocklist_state.h" +#include "extensions/browser/extension_prefs.h" +#include "extensions/browser/extension_prefs_observer.h" #include "extensions/common/extension_id.h" #include "extensions/common/url_pattern.h" #include "extensions/common/url_pattern_set.h" @@ -38,7 +42,7 @@ // Generates the developerPrivate api's specification for ExtensionInfo. // This class is designed to only have one generation running at a time! -class ExtensionInfoGenerator { +class ExtensionInfoGenerator : public ProfileObserver { public: using ExtensionInfoList = std::vector<api::developer_private::ExtensionInfo>; @@ -49,7 +53,13 @@ ExtensionInfoGenerator(const ExtensionInfoGenerator&) = delete; ExtensionInfoGenerator& operator=(const ExtensionInfoGenerator&) = delete; - ~ExtensionInfoGenerator(); + ~ExtensionInfoGenerator() override; + + // ProfileObserver implementation. + // There's a chance that an instance of this class is owned by a task, which + // means it could outlive some of the systems cached that would be destroyed + // when the profile associated with the `browser_context_` is destroyed. + void OnProfileWillBeDestroyed(Profile* profile) override; // Creates and asynchronously returns an ExtensionInfo for the given // |extension_id|, if the extension can be found. @@ -112,6 +122,8 @@ // The callback to run once all infos have been created. ExtensionInfosCallback callback_; + base::ScopedObservation<Profile, ProfileObserver> profile_observation_{this}; + base::WeakPtrFactory<ExtensionInfoGenerator> weak_factory_{this}; friend class ExtensionInfoGeneratorUnitTest;
diff --git a/chrome/browser/extensions/extension_sync_service.cc b/chrome/browser/extensions/extension_sync_service.cc index 4204eff..b4bf5d4 100644 --- a/chrome/browser/extensions/extension_sync_service.cc +++ b/chrome/browser/extensions/extension_sync_service.cc
@@ -195,6 +195,8 @@ } } + AccountExtensionTracker::Get(profile_)->OnInitialExtensionsSyncDataReceived(); + // Now push the local state to sync. // Note: We'd like to only send out changes for extensions which have // NeedsSync set. However, we can't tell if our changes ever made it to the
diff --git a/chrome/browser/extensions/extension_sync_util.cc b/chrome/browser/extensions/extension_sync_util.cc index 7544667..810fd0c 100644 --- a/chrome/browser/extensions/extension_sync_util.cc +++ b/chrome/browser/extensions/extension_sync_util.cc
@@ -9,6 +9,7 @@ #include "chrome/browser/signin/identity_manager_factory.h" #include "chrome/browser/sync/sync_service_factory.h" #include "chrome/common/extensions/sync_helper.h" +#include "components/signin/public/base/consent_level.h" #include "components/signin/public/base/signin_switches.h" #include "components/signin/public/identity_manager/identity_manager.h" #include "components/sync/base/features.h" @@ -42,6 +43,10 @@ } bool IsSyncingExtensionsEnabled(Profile* profile) { + // TODO(crbug.com/388557898): If this method is called from + // IdentityManagerObserver::OnPrimaryAccountChanged, then it could return the + // wrong value since the sync service also piggybacks on that event to update + // which data types are syncing. syncer::SyncService* sync_service = SyncServiceFactory::GetForProfile(profile); return sync_service && @@ -50,9 +55,14 @@ } bool IsSyncingExtensionsInTransportMode(Profile* profile) { - syncer::SyncService* sync_service = - SyncServiceFactory::GetForProfile(profile); - return IsSyncingExtensionsEnabled(profile) && !sync_service->HasSyncConsent(); + // Prefer querying the IdentityManager for consent levels since it's the base + // source of truth, and something like sync_service->HasSyncConsent() is a bit + // slower to update since it observes IdentityManager. + signin::IdentityManager* identity_manager = + IdentityManagerFactory::GetForProfile(profile); + return IsSyncingExtensionsEnabled(profile) && + identity_manager->HasPrimaryAccount(signin::ConsentLevel::kSignin) && + !identity_manager->HasPrimaryAccount(signin::ConsentLevel::kSync); } bool IsExtensionsExplicitSigninEnabled() {
diff --git a/chrome/browser/extensions/global_shortcut_listener.cc b/chrome/browser/extensions/global_shortcut_listener.cc index 8d574cf..d85ca38 100644 --- a/chrome/browser/extensions/global_shortcut_listener.cc +++ b/chrome/browser/extensions/global_shortcut_listener.cc
@@ -48,12 +48,6 @@ const ui::Accelerator& accelerator, Observer* observer) { CHECK(global_accelerator_listener_); - - if (IsShortcutHandlingSuspended()) { - return false; - } - - accelerators_.push_back(accelerator); return global_accelerator_listener_->RegisterAccelerator(accelerator, observer); } @@ -62,54 +56,23 @@ const ui::Accelerator& accelerator, Observer* observer) { CHECK(global_accelerator_listener_); - - if (IsShortcutHandlingSuspended()) { - return; - } - global_accelerator_listener_->UnregisterAccelerator(accelerator, observer); - RemoveAccelerator(accelerator); } void GlobalShortcutListener::UnregisterAccelerators(Observer* observer) { CHECK(global_accelerator_listener_); - - if (IsShortcutHandlingSuspended()) { - return; - } - - std::vector<ui::Accelerator> removed = - global_accelerator_listener_->UnregisterAccelerators(observer); - for (const ui::Accelerator& accelerator : removed) { - RemoveAccelerator(accelerator); - } + global_accelerator_listener_->UnregisterAccelerators(observer); } void GlobalShortcutListener::SetShortcutHandlingSuspended(bool suspended) { CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); CHECK(global_accelerator_listener_); - - if (shortcut_handling_suspended_ == suspended) { - return; - } - - shortcut_handling_suspended_ = suspended; - for (auto& accelerator : accelerators_) { - // On Linux, when shortcut handling is suspended we cannot simply early - // return in NotifyKeyPressed (similar to what we do for non-global - // shortcuts) because we'd eat the keyboard event thereby preventing the - // user from setting the shortcut. Therefore we must unregister while - // handling is suspended and register when handling resumes. - if (shortcut_handling_suspended_) { - global_accelerator_listener_->StopListeningForAccelerator(accelerator); - } else { - global_accelerator_listener_->StartListeningForAccelerator(accelerator); - } - } + global_accelerator_listener_->SetShortcutHandlingSuspended(suspended); } bool GlobalShortcutListener::IsShortcutHandlingSuspended() const { - return shortcut_handling_suspended_; + CHECK(global_accelerator_listener_); + return global_accelerator_listener_->IsShortcutHandlingSuspended(); } bool GlobalShortcutListener::IsRegistrationHandledExternally() const { @@ -125,9 +88,4 @@ accelerator_group_id, profile_id, commands, observer); } -void GlobalShortcutListener::RemoveAccelerator( - const ui::Accelerator& accelerator) { - std::erase(accelerators_, accelerator); -} - } // namespace extensions
diff --git a/chrome/browser/extensions/global_shortcut_listener.h b/chrome/browser/extensions/global_shortcut_listener.h index 78804b1a..062b08a 100644 --- a/chrome/browser/extensions/global_shortcut_listener.h +++ b/chrome/browser/extensions/global_shortcut_listener.h
@@ -79,19 +79,8 @@ ui::GlobalAcceleratorListener* global_shortcut_listener); private: - // Removes an accelerator from the list of accelerators registered with this - // class. - void RemoveAccelerator(const ui::Accelerator& accelerator); - // GlobalShortcutListener instance to which where most calls are delegated. raw_ptr<ui::GlobalAcceleratorListener> global_accelerator_listener_; - - // Local tracking of accelerators that need to be registered or unregistered - // when shortcut handling is suspended. - std::vector<ui::Accelerator> accelerators_; - - // Keeps track of whether shortcut handling is currently suspended. - bool shortcut_handling_suspended_ = false; }; } // namespace extensions
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json index 1cfdb9e2..aeb682c 100644 --- a/chrome/browser/flag-metadata.json +++ b/chrome/browser/flag-metadata.json
@@ -2202,7 +2202,7 @@ }, { "name": "eche-swa-check-android-network-info", - "owners": [ "dhnishi@chromium.org", "corakwue@google.com", "ftsui@google.com" ], + "owners": [ "corakwue@google.com", "ftsui@google.com" ], "expiry_milestone": 125 }, { @@ -2310,6 +2310,11 @@ "expiry_milestone": 145 }, { + "name": "enable-accessibility-manifest-v3-braille-ime", + "owners": [ "spectranaut@igalia.com", "akihiroota@chromium.org", "//ui/accessibility/OWNERS" ], + "expiry_milestone": 145 + }, + { "name": "enable-accessibility-manifest-v3-enhanced-network-tts", "owners": [ "spectranaut@igalia.com", "akihiroota@chromium.org", "//ui/accessibility/OWNERS" ], "expiry_milestone": 145 @@ -5585,6 +5590,11 @@ "owners": ["cmyang@google.com", "ericappelbaum@google.com"], "expiry_milestone": 135 }, + { + "name": "lens-overlay-alternative-onboarding", + "owners": [ "stkhapugin@chromium.org", "christianxu@chromium.org", "lens-chrome@google.com" ], + "expiry_milestone": 135 + }, { "name": "lens-overlay-disable-price-insights", "owners": ["christianxu@google.com", "stkhapugin@chromium.org"],
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc index 5c952a3e..88a8e999 100644 --- a/chrome/browser/flag_descriptions.cc +++ b/chrome/browser/flag_descriptions.cc
@@ -6577,6 +6577,11 @@ "Experimental migration of accessibility features from extension manifest " "v2 to v3. Likely to break accessibility access while experimental."; +const char kAccessibilityManifestV3BrailleImeName[] = + "Changes accessibility extension Braille IME manifest v2 to v3."; +const char kAccessibilityManifestV3BrailleImeDescription[] = + "Experimental migration of Braille IME from extension manifest v2 to v3."; + const char kAccessibilityManifestV3EnhancedNetworkTtsName[] = "Changes accessibility extension Enhanced Network TTS manifest v2 to v3."; const char kAccessibilityManifestV3EnhancedNetworkTtsDescription[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h index af1be358..97da391 100644 --- a/chrome/browser/flag_descriptions.h +++ b/chrome/browser/flag_descriptions.h
@@ -3851,6 +3851,9 @@ extern const char kExperimentalAccessibilityManifestV3Name[]; extern const char kExperimentalAccessibilityManifestV3Description[]; +extern const char kAccessibilityManifestV3BrailleImeName[]; +extern const char kAccessibilityManifestV3BrailleImeDescription[]; + extern const char kAccessibilityManifestV3EnhancedNetworkTtsName[]; extern const char kAccessibilityManifestV3EnhancedNetworkTtsDescription[];
diff --git a/chrome/browser/glic/glic_keyed_service.cc b/chrome/browser/glic/glic_keyed_service.cc index 86cbb26..30f6027 100644 --- a/chrome/browser/glic/glic_keyed_service.cc +++ b/chrome/browser/glic/glic_keyed_service.cc
@@ -34,6 +34,10 @@ GlicKeyedService::~GlicKeyedService() = default; +void GlicKeyedService::Shutdown() { + window_controller_.Shutdown(); +} + void GlicKeyedService::LaunchUI(views::View* glic_button_view) { // Glic may be disabled for certain user profiles (the user is browsing in // incognito or guest mode, policy, etc). In those cases, the entry points to @@ -93,6 +97,14 @@ SetContextAccessIndicator(false); } +void GlicKeyedService::AttachPanel() { + window_controller_.Attach(); +} + +void GlicKeyedService::DetachPanel() { + window_controller_.Detach(); +} + std::optional<gfx::Size> GlicKeyedService::ResizePanel(const gfx::Size& size) { if (!window_controller_.Resize(size)) { return std::nullopt;
diff --git a/chrome/browser/glic/glic_keyed_service.h b/chrome/browser/glic/glic_keyed_service.h index 3f6f3be..a9ccea6e 100644 --- a/chrome/browser/glic/glic_keyed_service.h +++ b/chrome/browser/glic/glic_keyed_service.h
@@ -39,6 +39,9 @@ GlicKeyedService& operator=(const GlicKeyedService&) = delete; ~GlicKeyedService() override; + // KeyedService + void Shutdown() override; + // Launches the Glic UI anchored at the given View object. When started from // the launcher, no anchor view is provided. void LaunchUI(views::View* glic_button_view); @@ -52,6 +55,8 @@ glic::mojom::WebClientHandler::CreateTabCallback callback); void OpenGlicSettingsPage(); virtual void ClosePanel(); + void AttachPanel(); + void DetachPanel(); std::optional<gfx::Size> ResizePanel(const gfx::Size& size); void SetPanelDraggableAreas(const std::vector<gfx::Rect>& draggable_areas); void SetContextAccessIndicator(bool show);
diff --git a/chrome/browser/glic/glic_profile_manager.cc b/chrome/browser/glic/glic_profile_manager.cc index c016d0e..aca656f 100644 --- a/chrome/browser/glic/glic_profile_manager.cc +++ b/chrome/browser/glic/glic_profile_manager.cc
@@ -13,20 +13,6 @@ namespace glic { -namespace { - -// Ensures that the window is closed early enough (if we don't do this, we -// won't have cleaned up by the time that keyed services are destroyed). -void OnAppTerminating() { - GlicProfileManager* mgr = GlicProfileManager::GetInstance(); - if (!mgr) { - return; - } - mgr->CloseGlicWindow(); -} - -} // namespace - GlicProfileManager* GlicProfileManager::GetInstance() { return g_browser_process->GetFeatures()->glic_profile_manager(); } @@ -52,9 +38,7 @@ active_glic_ = glic->GetWeakPtr(); } -GlicProfileManager::GlicProfileManager() - : termination_subscription_(browser_shutdown::AddAppTerminatingCallback( - base::BindOnce(&OnAppTerminating))) {} +GlicProfileManager::GlicProfileManager() = default; GlicProfileManager::~GlicProfileManager() = default;
diff --git a/chrome/browser/glic/glic_profile_manager.h b/chrome/browser/glic/glic_profile_manager.h index 3439002..b9722a1c 100644 --- a/chrome/browser/glic/glic_profile_manager.h +++ b/chrome/browser/glic/glic_profile_manager.h
@@ -39,7 +39,6 @@ private: base::WeakPtr<GlicKeyedService> active_glic_; - base::CallbackListSubscription termination_subscription_; }; } // namespace glic
diff --git a/chrome/browser/glic/glic_view.cc b/chrome/browser/glic/glic_view.cc index 3ead4ed6..477e0c08 100644 --- a/chrome/browser/glic/glic_view.cc +++ b/chrome/browser/glic/glic_view.cc
@@ -5,9 +5,6 @@ #include "chrome/browser/glic/glic_view.h" #include "base/command_line.h" -#include "chrome/browser/media/webrtc/media_capture_devices_dispatcher.h" -#include "chrome/browser/profiles/keep_alive/profile_keep_alive_types.h" -#include "chrome/browser/profiles/keep_alive/scoped_profile_keep_alive.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/profiles/profile_manager.h" #include "chrome/browser/ui/browser_element_identifiers.h" @@ -28,15 +25,10 @@ namespace glic { GlicView::GlicView(Profile* profile, const gfx::Size& initial_size) { - profile_keep_alive_ = std::make_unique<ScopedProfileKeepAlive>( - profile, ProfileKeepAliveOrigin::kGlicView); SetProperty(views::kElementIdentifierKey, kGlicViewElementId); auto web_view = std::make_unique<views::WebView>(profile); web_view_ = web_view.get(); web_view->SetSize(initial_size); - web_view->LoadInitialURL(GURL("chrome://glic")); - web_view->GetWebContents()->SetPageBaseBackgroundColor(SK_ColorTRANSPARENT); - web_view->GetWebContents()->SetDelegate(this); AddChildView(std::move(web_view)); } @@ -90,17 +82,4 @@ bounds_change_animation_->Start(); } -bool GlicView::HandleKeyboardEvent(content::WebContents* source, - const input::NativeWebKeyboardEvent& event) { - return unhandled_keyboard_event_handler_.HandleKeyboardEvent( - event, web_view()->GetFocusManager()); -} - -void GlicView::RequestMediaAccessPermission( - content::WebContents* web_contents, - const content::MediaStreamRequest& request, - content::MediaResponseCallback callback) { - MediaCaptureDevicesDispatcher::GetInstance()->ProcessMediaAccessRequest( - web_contents, request, std::move(callback), nullptr); -} } // namespace glic
diff --git a/chrome/browser/glic/glic_view.h b/chrome/browser/glic/glic_view.h index 114f005..a641d11 100644 --- a/chrome/browser/glic/glic_view.h +++ b/chrome/browser/glic/glic_view.h
@@ -5,9 +5,7 @@ #ifndef CHROME_BROWSER_GLIC_GLIC_VIEW_H_ #define CHROME_BROWSER_GLIC_GLIC_VIEW_H_ -#include "content/public/browser/web_contents_delegate.h" #include "ui/gfx/geometry/size.h" -#include "ui/views/controls/webview/unhandled_keyboard_event_handler.h" #include "ui/views/view.h" #include "ui/views/widget/unique_widget_ptr.h" @@ -21,11 +19,10 @@ class BrowserFrameBoundsChangeAnimation; class Profile; -class ScopedProfileKeepAlive; namespace glic { -class GlicView : public views::View, public content::WebContentsDelegate { +class GlicView : public views::View { public: GlicView(Profile* profile, const gfx::Size& initial_size); GlicView(const GlicView&) = delete; @@ -51,16 +48,7 @@ void AnimateFrameBounds(const gfx::Rect& bounds); private: - // content::WebContentsDelegate: - bool HandleKeyboardEvent(content::WebContents* source, - const input::NativeWebKeyboardEvent& event) override; - void RequestMediaAccessPermission( - content::WebContents* web_contents, - const content::MediaStreamRequest& request, - content::MediaResponseCallback callback) override; - raw_ptr<views::WebView> web_view_; - views::UnhandledKeyboardEventHandler unhandled_keyboard_event_handler_; // Defines the areas of the view from which it can be dragged. These areas can // be updated by the glic web client. @@ -69,10 +57,6 @@ // Animates programmatic changes to bounds (e.g. via `resizeTo()` // `resizeBy()` and `setContentsSize()` calls). std::unique_ptr<BrowserFrameBoundsChangeAnimation> bounds_change_animation_; - - // Ensures that the profile associated with this view isn't destroyed while - // it is visible. - std::unique_ptr<ScopedProfileKeepAlive> profile_keep_alive_; }; } // namespace glic
diff --git a/chrome/browser/glic/glic_window_controller.cc b/chrome/browser/glic/glic_window_controller.cc index 62394cc..ea5da1ae 100644 --- a/chrome/browser/glic/glic_window_controller.cc +++ b/chrome/browser/glic/glic_window_controller.cc
@@ -7,6 +7,9 @@ #include "base/check.h" #include "chrome/browser/glic/glic_view.h" #include "chrome/browser/media/audio_ducker.h" +#include "chrome/browser/media/webrtc/media_capture_devices_dispatcher.h" +#include "chrome/browser/profiles/keep_alive/profile_keep_alive_types.h" +#include "chrome/browser/profiles/keep_alive/scoped_profile_keep_alive.h" #include "chrome/browser/profiles/profile_manager.h" #include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_list.h" @@ -16,13 +19,17 @@ #include "chrome/browser/ui/views/tabs/glic_button.h" #include "chrome/browser/ui/views/tabs/tab_strip_action_container.h" #include "chrome/browser/ui/webui/glic/glic.mojom.h" +#include "chrome/common/webui_url_constants.h" #include "content/public/browser/web_contents.h" +#include "content/public/browser/web_contents_delegate.h" #include "ui/display/screen.h" #include "ui/events/event_observer.h" +#include "ui/views/controls/webview/unhandled_keyboard_event_handler.h" #include "ui/views/controls/webview/webview.h" #include "ui/views/event_monitor.h" #include "ui/views/widget/widget_observer.h" +namespace glic { namespace { // Default value for how close the top-right corner of the glic window must be // to a browser's glic button to attach to said browser. @@ -32,6 +39,57 @@ constexpr static int kWidgetHeight = 800; constexpr static int kWidgetTopBarHeight = 80; +class ContentsAndProfileKeepAlive : public content::WebContentsDelegate { + public: + ContentsAndProfileKeepAlive(Profile* profile, + GlicWindowController* glic_window_controller) + : profile_keep_alive_(profile, ProfileKeepAliveOrigin::kGlicView), + web_contents_(content::WebContents::Create( + content::WebContents::CreateParams(profile))), + glic_window_controller_(glic_window_controller) { + DCHECK(web_contents_); + web_contents_->SetDelegate(this); + web_contents_->SetPageBaseBackgroundColor(SK_ColorTRANSPARENT); + web_contents_->GetController().LoadURLWithParams( + content::NavigationController::LoadURLParams( + GURL{chrome::kChromeUIGlicURL})); + } + + ~ContentsAndProfileKeepAlive() override { web_contents_->ClosePage(); } + + ContentsAndProfileKeepAlive(const ContentsAndProfileKeepAlive&) = delete; + ContentsAndProfileKeepAlive& operator=(const ContentsAndProfileKeepAlive&) = + delete; + + content::WebContents* web_contents() { return web_contents_.get(); } + + private: + // content::WebContentsDelegate: + bool HandleKeyboardEvent( + content::WebContents* source, + const input::NativeWebKeyboardEvent& event) override { + GlicView* glic_view = glic_window_controller_->GetGlicView(); + if (!glic_view) { + return false; + } + return unhandled_keyboard_event_handler_.HandleKeyboardEvent( + event, glic_view->web_view()->GetFocusManager()); + } + void RequestMediaAccessPermission( + content::WebContents* web_contents, + const content::MediaStreamRequest& request, + content::MediaResponseCallback callback) override { + MediaCaptureDevicesDispatcher::GetInstance()->ProcessMediaAccessRequest( + web_contents, request, std::move(callback), nullptr); + } + + ScopedProfileKeepAlive profile_keep_alive_; + std::unique_ptr<content::WebContents> web_contents_; + views::UnhandledKeyboardEventHandler unhandled_keyboard_event_handler_; + // Unowned + raw_ptr<GlicWindowController> glic_window_controller_; +}; + // Helper class for observing mouse and key events from native window. class WindowEventObserver : public ui::EventObserver { public: @@ -86,8 +144,6 @@ } // namespace -namespace glic { - GlicWindowController::GlicWindowController(Profile* profile) : profile_(profile) {} @@ -124,6 +180,13 @@ glic_window_widget_ = glic::GlicView::CreateWidget( profile_, {top_right_point.x() - kWidgetWidth - padding, top_right_point.y() + padding, kWidgetWidth, kWidgetHeight}); + + GlicView* glic_view = GlicView::FromWidget(*glic_window_widget_); + if (!contents_) { + contents_ = std::make_unique<ContentsAndProfileKeepAlive>(profile_, this); + } + glic_view->web_view()->SetWebContents(contents_->web_contents()); + glic_window_widget_->AddObserver(this); glic_widget_observer_ = std::make_unique<GlicWidgetObserver>(this, glic_window_widget_.get()); @@ -532,6 +595,12 @@ } } +void GlicWindowController::Shutdown() { + // Hide first, then clean up. + Close(); + contents_.reset(); +} + GlicWindowController::GlicWidgetObserver::~GlicWidgetObserver() { if (widget_ && widget_->HasObserver(this)) { widget_->RemoveObserver(this);
diff --git a/chrome/browser/glic/glic_window_controller.h b/chrome/browser/glic/glic_window_controller.h index fd5d0774..4858481 100644 --- a/chrome/browser/glic/glic_window_controller.h +++ b/chrome/browser/glic/glic_window_controller.h
@@ -12,6 +12,7 @@ #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/browser_window/public/browser_window_interface.h" #include "chrome/browser/ui/webui/glic/glic.mojom.h" +#include "content/public/browser/web_contents.h" #include "ui/views/widget/unique_widget_ptr.h" class Browser; @@ -20,12 +21,13 @@ class Point; } // namespace gfx +namespace glic { namespace { +class ContentsAndProfileKeepAlive; class GlicWidgetObserver; class WindowEventObserver; } // namespace -namespace glic { class GlicView; // Class for Glic window controller. Owned by the Glic profile keyed-service. @@ -55,6 +57,9 @@ // display. void Detach(); + // Destroy the glic panel and its web contents. + void Shutdown(); + // Sets the size of the glic window to the specified dimensions. Returns true // if the operation succeeded. bool Resize(const gfx::Size& size); @@ -65,7 +70,7 @@ // Sets the areas of the view from which it should be draggable. void SetDraggableAreas(const std::vector<gfx::Rect>& draggable_areas); - // Called to notify the controller that the window was requested to be closed. + // Close the panel but keep the glic WebContents alive in the background. void Close(); // Sets the audio ducking status. Returns true if the operation succeeded. @@ -191,6 +196,10 @@ std::unique_ptr<views::Widget> holder_widget_; const raw_ptr<Profile> profile_; + // Keep profile alive as long as the glic web contents. This object should be + // destroyed when the profile needs to be destroyed. + std::unique_ptr<ContentsAndProfileKeepAlive> contents_; + std::unique_ptr<views::Widget> glic_window_widget_; bool glic_window_widget_visible_ = false;
diff --git a/chrome/browser/glic/launcher/glic_background_mode_manager_browsertest.cc b/chrome/browser/glic/launcher/glic_background_mode_manager_browsertest.cc index 416f80b..7db475df7 100644 --- a/chrome/browser/glic/launcher/glic_background_mode_manager_browsertest.cc +++ b/chrome/browser/glic/launcher/glic_background_mode_manager_browsertest.cc
@@ -103,7 +103,8 @@ true); GlicBackgroundModeManager* const manager = g_browser_process->GetFeatures()->glic_background_mode_manager(); - EXPECT_EQ(ui::Accelerator(), manager->RegisteredHotkeyForTesting()); + EXPECT_EQ(ui::Accelerator(ui::VKEY_G, ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN), + manager->RegisteredHotkeyForTesting()); ui::Accelerator updated_hotkey(ui::VKEY_A, ui::EF_SHIFT_DOWN); RegisterHotkey(updated_hotkey);
diff --git a/chrome/browser/glic/launcher/glic_launcher_configuration.cc b/chrome/browser/glic/launcher/glic_launcher_configuration.cc index cbece99..5e66fc4e4 100644 --- a/chrome/browser/glic/launcher/glic_launcher_configuration.cc +++ b/chrome/browser/glic/launcher/glic_launcher_configuration.cc
@@ -5,8 +5,10 @@ #include "chrome/browser/glic/launcher/glic_launcher_configuration.h" #include "base/values.h" +#include "base/version_info/channel.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/glic/glic_pref_names.h" +#include "chrome/common/channel_info.h" #include "components/prefs/pref_registry_simple.h" #include "components/prefs/pref_service.h" #include "ui/base/accelerators/accelerator.h" @@ -36,12 +38,16 @@ // static void GlicLauncherConfiguration::RegisterLocalStatePrefs( PrefRegistrySimple* registry) { - registry->RegisterBooleanPref(prefs::kGlicLauncherEnabled, false); + // TODO(crbug.com/379166397): Set the default value to false when FRE is + // implemented. + registry->RegisterBooleanPref( + prefs::kGlicLauncherEnabled, + chrome::GetChannel() == version_info::Channel::CANARY); registry->RegisterDictionaryPref( prefs::kGlicLauncherGlobalHotkey, base::Value::Dict() - .Set(kHotkeyKeyCode, ui::KeyboardCode::VKEY_UNKNOWN) - .Set(kHotkeyModifiers, ui::EF_NONE)); + .Set(kHotkeyKeyCode, ui::KeyboardCode::VKEY_G) + .Set(kHotkeyModifiers, ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN)); } bool GlicLauncherConfiguration::IsEnabled() {
diff --git a/chrome/browser/glic/launcher/glic_status_icon.cc b/chrome/browser/glic/launcher/glic_status_icon.cc index 5918c9e..9da6612 100644 --- a/chrome/browser/glic/launcher/glic_status_icon.cc +++ b/chrome/browser/glic/launcher/glic_status_icon.cc
@@ -31,17 +31,16 @@ : controller_(controller), status_tray_(status_tray) { // TODO(crbug.com/382287104): Use correct icon. // TODO(crbug.com/386839488): Chose color based on system theme. - gfx::ImageSkia status_tray_icon = gfx::CreateVectorIcon(kGlicButtonIcon, -#if BUILDFLAG(IS_LINUX) - SK_ColorWHITE -#else - SK_ColorBLACK -#endif - ); + gfx::ImageSkia status_tray_icon = + gfx::CreateVectorIcon(kGlicButtonIcon, SK_ColorBLACK); status_icon_ = status_tray_->CreateStatusIcon( StatusTray::GLIC_ICON, status_tray_icon, l10n_util::GetStringUTF16(IDS_GLIC_STATUS_ICON_TOOLTIP)); +#if BUILDFLAG(IS_LINUX) + // Set a vector icon for proper themeing on Linux. + status_icon_->SetIcon(kGlicButtonIcon); +#endif #if BUILDFLAG(IS_MAC) if (features::kGlicStatusIconOpenMenuWithSecondaryClick.Get()) { status_icon_->SetOpenMenuWithSecondaryClick(true);
diff --git a/chrome/browser/history/java/OWNERS b/chrome/browser/history/java/OWNERS index 0edc952..932e9e4 100644 --- a/chrome/browser/history/java/OWNERS +++ b/chrome/browser/history/java/OWNERS
@@ -1,2 +1 @@ jinsukkim@chromium.org -katzz@google.com
diff --git a/chrome/browser/metrics/chrome_metrics_service_accessor.h b/chrome/browser/metrics/chrome_metrics_service_accessor.h index 25ae21fa..6ef032b 100644 --- a/chrome/browser/metrics/chrome_metrics_service_accessor.h +++ b/chrome/browser/metrics/chrome_metrics_service_accessor.h
@@ -180,6 +180,7 @@ friend class feed::WebFeedSubscriptionCoordinator; friend class HttpsFirstModeService; friend class ash::DemoSession; + friend class DataSharingUI; // Used to register synthetic trials for ongoing growth experiments. friend class CampaignsManagerClientImpl; friend class tpcd::experiment::ExperimentManagerImpl;
diff --git a/chrome/browser/page_load_metrics/integration_tests/largest_contentful_paint_browsertest.cc b/chrome/browser/page_load_metrics/integration_tests/largest_contentful_paint_browsertest.cc index 48adf1e..c8e29bdb 100644 --- a/chrome/browser/page_load_metrics/integration_tests/largest_contentful_paint_browsertest.cc +++ b/chrome/browser/page_load_metrics/integration_tests/largest_contentful_paint_browsertest.cc
@@ -102,7 +102,8 @@ } // namespace -IN_PROC_BROWSER_TEST_F(MetricIntegrationTest, LargestContentfulPaint) { +// TODO(crbug.com/385580803): Flaky on all platforms +IN_PROC_BROWSER_TEST_F(MetricIntegrationTest, DISABLED_LargestContentfulPaint) { auto waiter = std::make_unique<page_load_metrics::PageLoadMetricsTestWaiter>( web_contents()); Start(); @@ -229,6 +230,7 @@ } // TODO(crbug.com/40936591): This test is flaky on ChromeOS and Linux. +// TODO(crbug.com/382573509): and flaky on Windows. #if BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_LINUX) #define MAYBE_LargestContentfulPaint_SubframeInput \ DISABLED_LargestContentfulPaint_SubframeInput @@ -368,7 +370,8 @@ base::test::ScopedFeatureList feature_list_; }; -IN_PROC_BROWSER_TEST_F(PageViewportInLCPTest, FullSizeImageInIframe) { +// TODO(crbug.com/385580803): flaky on all platforms +IN_PROC_BROWSER_TEST_F(PageViewportInLCPTest, DISABLED_FullSizeImageInIframe) { auto waiter = std::make_unique<page_load_metrics::PageLoadMetricsTestWaiter>( web_contents()); waiter->AddSubFrameExpectation(page_load_metrics::PageLoadMetricsTestWaiter:: @@ -1020,7 +1023,8 @@ double epsilon_ = 8; }; -IN_PROC_BROWSER_TEST_F(LcpBreakdownTimingsTest, Subframe) { +// TODO(crbug.com/385392162): flaky on all platforms +IN_PROC_BROWSER_TEST_F(LcpBreakdownTimingsTest, DISABLED_Subframe) { std::string url = "/lcp_breakdown_timings_with_subframe.html"; auto* resource_name = "lcp-256x256.png"; RunTest(url, resource_name, 0, "addSameSiteSubframe()");
diff --git a/chrome/browser/page_load_metrics/page_load_metrics_browsertest.cc b/chrome/browser/page_load_metrics/page_load_metrics_browsertest.cc index 768c3ec..46225d4 100644 --- a/chrome/browser/page_load_metrics/page_load_metrics_browsertest.cc +++ b/chrome/browser/page_load_metrics/page_load_metrics_browsertest.cc
@@ -3868,8 +3868,8 @@ : public PageLoadMetricsBrowserTestTerminatedPage, public ::testing::WithParamInterface<const char*> {}; -// TODO(crbug.com/40280758): Very flaky on Lacros. -#if BUILDFLAG(IS_CHROMEOS_LACROS) +// TODO(crbug.com/376032466): flaky on Linux/Windows. +#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_WIN) #define MAYBE_UkmIsRecordedForCrashedTabPage \ DISABLED_UkmIsRecordedForCrashedTabPage #else
diff --git a/chrome/browser/privacy_sandbox/android/javatests/src/org/chromium/chrome/browser/privacy_sandbox/PrivacySandboxDialogTest.java b/chrome/browser/privacy_sandbox/android/javatests/src/org/chromium/chrome/browser/privacy_sandbox/PrivacySandboxDialogTest.java index 85d0251..c6747ea 100644 --- a/chrome/browser/privacy_sandbox/android/javatests/src/org/chromium/chrome/browser/privacy_sandbox/PrivacySandboxDialogTest.java +++ b/chrome/browser/privacy_sandbox/android/javatests/src/org/chromium/chrome/browser/privacy_sandbox/PrivacySandboxDialogTest.java
@@ -408,6 +408,7 @@ @SmallTest @Feature({"RenderTest"}) @EnableFeatures(ChromeFeatureList.PRIVACY_SANDBOX_ADS_API_UX_ENHANCEMENTS) + @DisableFeatures(ChromeFeatureList.PRIVACY_SANDBOX_EQUALIZED_PROMPT_BUTTONS) public void testRenderEeaNoticeV2AdMeasurementDropdown() throws IOException { ThreadUtils.runOnUiThreadBlocking( () -> { @@ -457,6 +458,7 @@ @Test @SmallTest @EnableFeatures(ChromeFeatureList.PRIVACY_SANDBOX_ADS_API_UX_ENHANCEMENTS) + @DisableFeatures(ChromeFeatureList.PRIVACY_SANDBOX_EQUALIZED_PROMPT_BUTTONS) public void testEeaNoticeV2AckButton() throws IOException { mFakePrivacySandboxBridge.setRequiredPromptType(PromptType.M1_NOTICE_EEA); launchDialog(); @@ -881,7 +883,10 @@ @Test @SmallTest - @DisableFeatures(ChromeFeatureList.PRIVACY_SANDBOX_ADS_API_UX_ENHANCEMENTS) + @DisableFeatures({ + ChromeFeatureList.PRIVACY_SANDBOX_ADS_API_UX_ENHANCEMENTS, + ChromeFeatureList.PRIVACY_SANDBOX_EQUALIZED_PROMPT_BUTTONS + }) public void testControllerShowsEEANotice() throws IOException { mFakePrivacySandboxBridge.setRequiredPromptType(PromptType.M1_NOTICE_EEA); launchDialog();
diff --git a/chrome/browser/resources/ash/settings/lazy_load.ts b/chrome/browser/resources/ash/settings/lazy_load.ts index ed6a517..804124e3 100644 --- a/chrome/browser/resources/ash/settings/lazy_load.ts +++ b/chrome/browser/resources/ash/settings/lazy_load.ts
@@ -70,6 +70,12 @@ import './os_files_page/office_page.js'; import './os_files_page/one_drive_subpage.js'; import './os_files_page/smb_shares_page.js'; +import './os_languages_page/app_languages_page.js'; +import './os_languages_page/input_method_options_page.js'; +import './os_languages_page/input_page.js'; +import './os_languages_page/os_edit_dictionary_page.js'; +import './os_languages_page/os_japanese_manage_user_dictionary_page.js'; +import './os_languages_page/os_languages_page_v2.js'; import './os_search_page/google_assistant_subpage.js'; import './os_search_page/search_subpage.js'; import './os_people_page/fingerprint_list_subpage.js'; @@ -99,26 +105,11 @@ import './crostini_page/crostini_port_forwarding_add_port_dialog.js'; import './crostini_page/crostini_shared_usb_devices.js'; import './crostini_page/crostini_subpage.js'; -import './date_time_page/timezone_selector.js'; import './guest_os/guest_os_confirmation_dialog.js'; import './guest_os/guest_os_container_select.js'; import './guest_os/guest_os_shared_paths.js'; import './guest_os/guest_os_shared_usb_devices.js'; import './guest_os/guest_os_shared_usb_devices_add_dialog.js'; -import './keyboard_shortcut_banner/keyboard_shortcut_banner.js'; -import './os_languages_page/app_languages_page.js'; -import './os_languages_page/input_method_options_page.js'; -import './os_languages_page/input_page.js'; -import './os_languages_page/os_edit_dictionary_page.js'; -import './os_languages_page/os_japanese_manage_user_dictionary_page.js'; -import './os_languages_page/os_languages_page_v2.js'; -import './os_people_page/os_personalization_options.js'; -import './os_privacy_page/secure_dns.js'; -import './os_privacy_page/secure_dns_input.js'; -import './os_reset_page/os_powerwash_dialog.js'; -import './os_reset_page/os_powerwash_dialog_esim_item.js'; -import './os_reset_page/os_sanitize_dialog.js'; -import './os_search_page/magic_boost_review_terms_banner.js'; export {ScreenAiInstallStatus} from '/shared/settings/a11y_page/ax_annotations_browser_proxy.js'; export {CaptionsBrowserProxy, CaptionsBrowserProxyImpl, LiveCaptionLanguage, LiveCaptionLanguageList} from '/shared/settings/a11y_page/captions_browser_proxy.js';
diff --git a/chrome/browser/resources/chromeos/accessibility/braille_ime/mv3/manifest.json b/chrome/browser/resources/chromeos/accessibility/braille_ime/mv3/manifest.json new file mode 100644 index 0000000..053a37f --- /dev/null +++ b/chrome/browser/resources/chromeos/accessibility/braille_ime/mv3/manifest.json
@@ -0,0 +1,24 @@ +{ + "name": "Braille IME", + "description": "Braille Input Method Extension.", + "version": "1.0", + "background": { + "service_worker": "main.js", + "type": "module" + }, + // chrome-extension://jddehjeebkoimngcbdkaahpobgicbffp + "key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCvDjqqYESDQe3OcI65JctUYLSlQ7RAd902VUw+RO/70fJ7SSkg8+2y+5paD6+g8f6wgFsgVsbTX2UM+tsmGKWR23bgSQxYhfZUZgP7qFdk72hGRUnKnXA+JOJ5maI4v+w18WPTWYOFJt2NOvat+GKKF0CAFQG+z2Ucn/sRZVfnrQIDAQAB", + "manifest_version": 3, + "permissions": [ + "input" + ], + "ime_path": "chromeos/accessibility/braille_ime", + "input_components": [ + { + "name": "Braille Keyboard", + "id": "braille", + "indicator": "\u2803\u2817\u2807", // Unicode of 'brl' in English (and many other) braille codes. + "language": ["None"] + } + ] +}
diff --git a/chrome/browser/resources/chromeos/emoji_picker/store.ts b/chrome/browser/resources/chromeos/emoji_picker/store.ts index 5d6396f..9fef5ac5 100644 --- a/chrome/browser/resources/chromeos/emoji_picker/store.ts +++ b/chrome/browser/resources/chromeos/emoji_picker/store.ts
@@ -94,7 +94,7 @@ const mergedHistory: EmojiHistoryItem[] = prefsHistory.history.map((item) => ({ base: {string: item.emoji}, - timestamp: item.timestamp.msec, + timestamp: item.timestamp.getTime(), alternates: [], })); for (const item of this.store.data.history) { @@ -244,9 +244,7 @@ .map((x) => ({ // Explicit cast here is safe due to filter above. emoji: x.base.string!, - timestamp: { - msec: x.timestamp || 0, - }, + timestamp: new Date(x.timestamp || 0), }))); } }
diff --git a/chrome/browser/resources/component_extension_resources.grd b/chrome/browser/resources/component_extension_resources.grd index 8a234cc..7c8bac0 100644 --- a/chrome/browser/resources/component_extension_resources.grd +++ b/chrome/browser/resources/component_extension_resources.grd
@@ -26,6 +26,8 @@ <!-- Compiled TS --> <include name="IDR_BRAILLE_IME_JS" file="${root_gen_dir}/chrome/browser/resources/chromeos/accessibility/braille_ime/tsc/braille_ime.js" use_base_dir="false" resource_path="chromeos/accessibility/braille_ime/braille_ime.js" type="BINDATA" /> <include name="IDR_BRAILLE_IME_MAIN_JS" file="${root_gen_dir}/chrome/browser/resources/chromeos/accessibility/braille_ime/tsc/main.js" use_base_dir="false" resource_path="chromeos/accessibility/braille_ime/main.js" type="BINDATA" /> + <include name="IDR_BRAILLE_IME_MV3_JS" file="${root_gen_dir}/chrome/browser/resources/chromeos/accessibility/braille_ime/tsc/braille_ime.js" use_base_dir="false" resource_path="chromeos/accessibility/braille_ime/mv3/braille_ime.js" type="BINDATA" /> + <include name="IDR_BRAILLE_IME_MV3_MAIN_JS" file="${root_gen_dir}/chrome/browser/resources/chromeos/accessibility/braille_ime/tsc/main.js" use_base_dir="false" resource_path="chromeos/accessibility/braille_ime/mv3/main.js" type="BINDATA" /> </if> <!-- Hangout Services extension, included in Google Chrome builds only. -->
diff --git a/chrome/browser/resources/data_sharing/data_sharing_api.ts b/chrome/browser/resources/data_sharing/data_sharing_api.ts index 2e19099..07361ce 100644 --- a/chrome/browser/resources/data_sharing/data_sharing_api.ts +++ b/chrome/browser/resources/data_sharing/data_sharing_api.ts
@@ -7,9 +7,12 @@ // </if> // <if expr="not _google_chrome"> import './dummy_data_sharing_sdk.js'; - // </if> +import '/strings.m.js'; + +import {loadTimeData} from 'chrome-untrusted://resources/js/load_time_data.js'; + import type {BrowserProxy} from './browser_proxy.js'; import {BrowserProxyImpl} from './browser_proxy.js'; import type {DataSharingSdk} from './data_sharing_sdk_types.js'; @@ -22,6 +25,8 @@ const browserProxy: BrowserProxy = BrowserProxyImpl.getInstance(); +dataSharingSdk.updateClearcut( + {enabled: loadTimeData.getBoolean('metricsReportingEnabled')}); browserProxy.callbackRouter.onAccessTokenFetched.addListener( (accessToken: string) => { dataSharingSdk.setOauthAccessToken({accessToken});
diff --git a/chrome/browser/resources/data_sharing/data_sharing_app.ts b/chrome/browser/resources/data_sharing/data_sharing_app.ts index 0f2f833e..f058af5a 100644 --- a/chrome/browser/resources/data_sharing/data_sharing_app.ts +++ b/chrome/browser/resources/data_sharing/data_sharing_app.ts
@@ -244,6 +244,8 @@ constructor() { super(); + this.dataSharingSdk_.updateClearcut( + {enabled: loadTimeData.getBoolean('metricsReportingEnabled')}); this.browserProxy_.callbackRouter.onAccessTokenFetched.addListener( (accessToken: string) => { this.dataSharingSdk_.setOauthAccessToken({accessToken});
diff --git a/chrome/browser/resources/glic/glic_api/glic_api.ts b/chrome/browser/resources/glic/glic_api/glic_api.ts index a1df2b11..ef16ee2f 100644 --- a/chrome/browser/resources/glic/glic_api/glic_api.ts +++ b/chrome/browser/resources/glic/glic_api/glic_api.ts
@@ -107,6 +107,14 @@ // Requests the closing of the panel containing the web client. closePanel?(): Promise<void>; + // Requests that the web client's panel be attached/docked to a browser + // window. + attachPanel?(): void; + + // Requests that the web client's panel be detached/undocked from a browser + // window (floats free). + detachPanel?(): void; + // Triggers the change profile flow, which allows the user to switch which // profile is used. If a new profile is chosen, this web client will be // closed in favor of a new one.
diff --git a/chrome/browser/resources/glic/glic_api_impl/glic_api_client.ts b/chrome/browser/resources/glic/glic_api_impl/glic_api_client.ts index 3dac8d7..65e5735 100644 --- a/chrome/browser/resources/glic/glic_api_impl/glic_api_client.ts +++ b/chrome/browser/resources/glic/glic_api_impl/glic_api_client.ts
@@ -170,6 +170,14 @@ return this.sender.requestWithResponse('glicBrowserClosePanel', {}); } + attachPanel(): void { + return this.sender.requestNoResponse('glicBrowserAttachPanel', {}); + } + + detachPanel(): void { + return this.sender.requestNoResponse('glicBrowserDetachPanel', {}); + } + async getContextFromFocusedTab(options: { innerText?: boolean|undefined, viewportScreenshot?: boolean|undefined,
diff --git a/chrome/browser/resources/glic/glic_api_impl/glic_api_host.ts b/chrome/browser/resources/glic/glic_api_impl/glic_api_host.ts index 4b8660a..d60fb190 100644 --- a/chrome/browser/resources/glic/glic_api_impl/glic_api_host.ts +++ b/chrome/browser/resources/glic/glic_api_impl/glic_api_host.ts
@@ -153,6 +153,14 @@ return this.handler.closePanel(); } + glicBrowserAttachPanel(): void { + this.handler.attachPanel(); + } + + glicBrowserDetachPanel(): void { + this.handler.detachPanel(); + } + async glicBrowserGetContextFromFocusedTab( request: { options: {innerText?: boolean, viewportScreenshot?: boolean},
diff --git a/chrome/browser/resources/glic/glic_api_impl/request_types.ts b/chrome/browser/resources/glic/glic_api_impl/request_types.ts index 35b3c013..89a9d8c 100644 --- a/chrome/browser/resources/glic/glic_api_impl/request_types.ts +++ b/chrome/browser/resources/glic/glic_api_impl/request_types.ts
@@ -43,6 +43,9 @@ request: {}, response: void, }; + + // The messages that fullfill the GlicBrowserHost public API follow below. + glicBrowserCreateTab: { request: { url: string, @@ -130,6 +133,14 @@ success: boolean, }, }; + glicBrowserAttachPanel: { + request: {}, + response: void, + }; + glicBrowserDetachPanel: { + request: {}, + response: void, + }; } // Types of requests to the GlicWebClient.
diff --git a/chrome/browser/resources/whats_new/whats_new_app.ts b/chrome/browser/resources/whats_new/whats_new_app.ts index 02ec93d6..764778c 100644 --- a/chrome/browser/resources/whats_new/whats_new_app.ts +++ b/chrome/browser/resources/whats_new/whats_new_app.ts
@@ -11,7 +11,7 @@ import {loadTimeData} from 'chrome://resources/js/load_time_data.js'; import {isChromeOS} from 'chrome://resources/js/platform.js'; import {CrLitElement} from 'chrome://resources/lit/v3_0/lit.rollup.js'; -import type {JSTime, TimeDelta} from 'chrome://resources/mojo/mojo/public/mojom/base/time.mojom-webui.js'; +import type {TimeDelta} from 'chrome://resources/mojo/mojo/public/mojom/base/time.mojom-webui.js'; import type {Url} from 'chrome://resources/mojo/url/mojom/url.mojom-webui.js'; import {ModulePosition, ScrollDepth} from './whats_new.mojom-webui.js'; @@ -204,7 +204,7 @@ function handlePageLoadMetric(data: PageLoadedMetric, isAutoOpen: boolean) { const {handler} = WhatsNewProxyImpl.getInstance(); - const now: JSTime = {msec: Date.now()}; + const now = new Date(); handler.recordTimeToLoadContent(now); // Record initial scroll depth as 0%.
diff --git a/chrome/browser/safe_browsing/download_protection/check_client_download_request_base.cc b/chrome/browser/safe_browsing/download_protection/check_client_download_request_base.cc index 10bcea4d..1d9e200 100644 --- a/chrome/browser/safe_browsing/download_protection/check_client_download_request_base.cc +++ b/chrome/browser/safe_browsing/download_protection/check_client_download_request_base.cc
@@ -464,6 +464,8 @@ resource_request->url = PPAPIDownloadRequest::GetDownloadRequestUrl(); resource_request->method = "POST"; resource_request->load_flags = net::LOAD_DISABLE_CACHE; + resource_request->site_for_cookies = + net::SiteForCookies::FromUrl(resource_request->url); if (!access_token_.empty()) { LogAuthenticatedCookieResets(
diff --git a/chrome/browser/ui/android/ephemeraltab/java/src/org/chromium/chrome/browser/ephemeraltab/EphemeralTabMediator.java b/chrome/browser/ui/android/ephemeraltab/java/src/org/chromium/chrome/browser/ephemeraltab/EphemeralTabMediator.java index b64a431..094bd89 100644 --- a/chrome/browser/ui/android/ephemeraltab/java/src/org/chromium/chrome/browser/ephemeraltab/EphemeralTabMediator.java +++ b/chrome/browser/ui/android/ephemeraltab/java/src/org/chromium/chrome/browser/ephemeraltab/EphemeralTabMediator.java
@@ -173,7 +173,7 @@ public void didFinishNavigationInPrimaryMainFrame(NavigationHandle navigation) { if (navigation.hasCommitted()) { mIsOnErrorPage = navigation.isErrorPage(); - mSheetContent.updateURL(mWebContents.get().getVisibleUrl()); + mSheetContent.updateURL(getWebContents().getVisibleUrl()); } else if (navigation.isDownload()) { // Not viewable contents such as download. Show a toast and close the // tab.
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/voice/VoiceRecognitionHandler.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/voice/VoiceRecognitionHandler.java index 34aa2299..1ab86fa 100644 --- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/voice/VoiceRecognitionHandler.java +++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/voice/VoiceRecognitionHandler.java
@@ -239,7 +239,7 @@ * navigating to is actually a SRP. */ private void setReceivedUserGesture(GURL url) { - WebContents webContents = mWebContents.get(); + WebContents webContents = getWebContents(); if (webContents == null) return; RenderFrameHost renderFrameHost = webContents.getMainFrame();
diff --git a/chrome/browser/ui/browser_command_controller.cc b/chrome/browser/ui/browser_command_controller.cc index f6a03cfe..ec7ec8b 100644 --- a/chrome/browser/ui/browser_command_controller.cc +++ b/chrome/browser/ui/browser_command_controller.cc
@@ -1472,6 +1472,9 @@ // Compare commands. command_updater_.UpdateCommandEnabled(IDC_COMPARE_MENU, true); command_updater_.UpdateCommandEnabled(IDC_SHOW_ALL_COMPARISON_TABLES, true); + command_updater_.UpdateCommandEnabled(IDC_ADD_TO_COMPARISON_TABLE_MENU, true); + command_updater_.UpdateCommandEnabled( + IDC_CREATE_NEW_COMPARISON_TABLE_WITH_TAB, true); // Initialize other commands whose state changes based on various conditions. UpdateCommandsForFullscreenMode(); @@ -1668,6 +1671,12 @@ // Update the zoom commands when an active tab is selected. UpdateCommandsForZoomState(); UpdateCommandsForTabKeyboardFocus(GetKeyboardFocusedTabIndex(browser_)); + + // Disable the add to comparison table menu when the page is not a standard + // webpage. + const GURL& current_url = current_web_contents->GetLastCommittedURL(); + command_updater_.UpdateCommandEnabled(IDC_ADD_TO_COMPARISON_TABLE_MENU, + current_url.SchemeIsHTTPOrHTTPS()); } void BrowserCommandController::UpdateCommandsForZoomState() {
diff --git a/chrome/browser/ui/browser_command_controller_browsertest.cc b/chrome/browser/ui/browser_command_controller_browsertest.cc index d031cb9f..67567d4e 100644 --- a/chrome/browser/ui/browser_command_controller_browsertest.cc +++ b/chrome/browser/ui/browser_command_controller_browsertest.cc
@@ -37,6 +37,7 @@ #include "chrome/browser/ui/views/side_panel/side_panel_ui.h" #include "chrome/test/base/in_process_browser_test.h" #include "chrome/test/base/ui_test_utils.h" +#include "components/commerce/core/commerce_feature_list.h" #include "components/search_engines/template_url_service.h" #include "components/sessions/core/tab_restore_service.h" #include "components/sessions/core/tab_restore_service_observer.h" @@ -613,4 +614,52 @@ #endif // BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_WIN) +// Tests for comparison table submenu. +class BrowserCommandControllerBrowserTestCompare + : public BrowserCommandControllerBrowserTest { + public: + BrowserCommandControllerBrowserTestCompare() { + scoped_feature_list_.InitWithFeatures( + {commerce::kProductSpecifications, + commerce::kCompareManagementInterface}, + {}); + } + + private: + base::test::ScopedFeatureList scoped_feature_list_; +}; + +IN_PROC_BROWSER_TEST_F(BrowserCommandControllerBrowserTestCompare, + AddToTableMenu_UrlSchemeHttp) { + GURL url = GURL("http://example.com"); + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); + + browser()->command_controller()->TabStateChanged(); + + EXPECT_TRUE(browser()->command_controller()->IsCommandEnabled( + IDC_ADD_TO_COMPARISON_TABLE_MENU)); +} + +IN_PROC_BROWSER_TEST_F(BrowserCommandControllerBrowserTestCompare, + AddToTableMenu_UrlSchemeHttps) { + GURL url = GURL("https://example.com"); + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); + + browser()->command_controller()->TabStateChanged(); + + EXPECT_TRUE(browser()->command_controller()->IsCommandEnabled( + IDC_ADD_TO_COMPARISON_TABLE_MENU)); +} + +IN_PROC_BROWSER_TEST_F(BrowserCommandControllerBrowserTestCompare, + AddToTableMenu_UrlSchemeNotHttpOrHttps) { + GURL url = GURL("chrome://history"); + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); + + browser()->command_controller()->TabStateChanged(); + + EXPECT_FALSE(browser()->command_controller()->IsCommandEnabled( + IDC_ADD_TO_COMPARISON_TABLE_MENU)); +} + } // namespace chrome
diff --git a/chrome/browser/ui/browser_commands.cc b/chrome/browser/ui/browser_commands.cc index 09afd18d76..af0e122 100644 --- a/chrome/browser/ui/browser_commands.cc +++ b/chrome/browser/ui/browser_commands.cc
@@ -2310,11 +2310,6 @@ void OpenCommerceProductSpecificationsTab(Browser* browser, const std::vector<GURL>& urls, const int position) { - if (static_cast<int>(urls.size()) < - commerce::kProductSpecificationsMinTabsCount) { - return; - } - auto* prefs = browser->profile()->GetPrefs(); // If user has not accepted the latest disclosure, show the disclosure dialog // first.
diff --git a/chrome/browser/ui/browser_element_identifiers.cc b/chrome/browser/ui/browser_element_identifiers.cc index 687e3cb..e4e45d7 100644 --- a/chrome/browser/ui/browser_element_identifiers.cc +++ b/chrome/browser/ui/browser_element_identifiers.cc
@@ -100,6 +100,7 @@ DEFINE_ELEMENT_IDENTIFIER_VALUE(kTabElementId); DEFINE_ELEMENT_IDENTIFIER_VALUE(kTabGroupEditorBubbleCloseGroupButtonId); DEFINE_ELEMENT_IDENTIFIER_VALUE(kTabGroupEditorBubbleDeleteGroupButtonId); +DEFINE_ELEMENT_IDENTIFIER_VALUE(kTabGroupEditorBubbleLeaveGroupButtonId); DEFINE_ELEMENT_IDENTIFIER_VALUE(kTabGroupEditorBubbleManageSharedGroupButtonId); DEFINE_ELEMENT_IDENTIFIER_VALUE(kTabGroupEditorBubbleShareGroupButtonId); DEFINE_ELEMENT_IDENTIFIER_VALUE(kTabGroupEditorBubbleId);
diff --git a/chrome/browser/ui/browser_element_identifiers.h b/chrome/browser/ui/browser_element_identifiers.h index f850178..abaf9f4 100644 --- a/chrome/browser/ui/browser_element_identifiers.h +++ b/chrome/browser/ui/browser_element_identifiers.h
@@ -111,6 +111,7 @@ DECLARE_ELEMENT_IDENTIFIER_VALUE(kTabElementId); DECLARE_ELEMENT_IDENTIFIER_VALUE(kTabGroupEditorBubbleCloseGroupButtonId); DECLARE_ELEMENT_IDENTIFIER_VALUE(kTabGroupEditorBubbleDeleteGroupButtonId); +DECLARE_ELEMENT_IDENTIFIER_VALUE(kTabGroupEditorBubbleLeaveGroupButtonId); DECLARE_ELEMENT_IDENTIFIER_VALUE( kTabGroupEditorBubbleManageSharedGroupButtonId); DECLARE_ELEMENT_IDENTIFIER_VALUE(kTabGroupEditorBubbleShareGroupButtonId);
diff --git a/chrome/browser/ui/commerce/BUILD.gn b/chrome/browser/ui/commerce/BUILD.gn index cdec15c..8b675bb7 100644 --- a/chrome/browser/ui/commerce/BUILD.gn +++ b/chrome/browser/ui/commerce/BUILD.gn
@@ -6,6 +6,7 @@ source_set("commerce") { sources = [ + "add_to_comparison_table_sub_menu_model.h", "commerce_page_action_controller.h", "commerce_ui_tab_helper.h", "compare_sub_menu_model.h", @@ -35,6 +36,7 @@ source_set("impl") { sources = [ + "add_to_comparison_table_sub_menu_model.cc", "commerce_page_action_controller.cc", "commerce_ui_tab_helper.cc", "compare_sub_menu_model.cc", @@ -50,6 +52,8 @@ ":commerce", "//chrome/browser/profiles:profile", "//chrome/browser/ui/browser_window", + "//chrome/browser/ui/toasts:toasts", + "//chrome/browser/ui/toasts/api:toasts", "//chrome/browser/ui/views/side_panel", "//chrome/common:constants", "//components/bookmarks/browser", @@ -114,10 +118,14 @@ source_set("browser_tests") { testonly = true defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ] - sources = [ "product_specifications_entry_point_controller_browsertest.cc" ] + sources = [ + "add_to_comparison_table_sub_menu_model_browsertest.cc", + "product_specifications_entry_point_controller_browsertest.cc", + ] deps = [ ":commerce", "//chrome/browser/ui/browser_window:browser_window", + "//chrome/browser/ui/toasts:toasts", "//chrome/test:test_support", "//components/commerce/core:account_checker_test_support", "//components/commerce/core:shopping_service_test_support",
diff --git a/chrome/browser/ui/commerce/add_to_comparison_table_sub_menu_model.cc b/chrome/browser/ui/commerce/add_to_comparison_table_sub_menu_model.cc new file mode 100644 index 0000000..0bec0c9 --- /dev/null +++ b/chrome/browser/ui/commerce/add_to_comparison_table_sub_menu_model.cc
@@ -0,0 +1,143 @@ +// Copyright 2025 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/ui/commerce/add_to_comparison_table_sub_menu_model.h" + +#include "base/strings/utf_string_conversions.h" +#include "chrome/app/chrome_command_ids.h" +#include "chrome/browser/commerce/product_specifications/product_specifications_service_factory.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/ui/browser.h" +#include "chrome/browser/ui/browser_commands.h" +#include "chrome/browser/ui/browser_window/public/browser_window_features.h" +#include "chrome/browser/ui/commerce/commerce_ui_tab_helper.h" +#include "chrome/browser/ui/tabs/public/tab_features.h" +#include "chrome/browser/ui/toasts/api/toast_id.h" +#include "chrome/browser/ui/toasts/toast_controller.h" +#include "chrome/browser/ui/toolbar/app_menu_model.h" +#include "chrome/grit/generated_resources.h" +#include "components/commerce/core/commerce_feature_list.h" +#include "components/commerce/core/product_specifications/product_specifications_service.h" +#include "components/strings/grit/components_strings.h" +#include "url/gurl.h" + +namespace commerce { + +AddToComparisonTableSubMenuModel::AddToComparisonTableSubMenuModel( + Browser* browser, + ProductSpecificationsService* product_specs_service) + : SimpleMenuModel(this), + browser_(browser), + product_specs_service_(product_specs_service), + next_menu_id_(AppMenuModel::kMinCompareCommandId) { + AddItemWithStringId(IDC_CREATE_NEW_COMPARISON_TABLE_WITH_TAB, + IDS_COMPARE_NEW_COMPARISON_TABLE); + + auto sets = product_specs_service_->GetAllProductSpecifications(); + if (sets.empty()) { + return; + } + + AddSeparator(ui::NORMAL_SEPARATOR); + + auto& current_url = + browser_->GetActiveTabInterface()->GetContents()->GetLastCommittedURL(); + + for (const auto& set : sets) { + bool contains_current_url = false; + for (const auto& url_info : set.url_infos()) { + if (url_info.url == current_url) { + contains_current_url = true; + break; + } + } + + const int command_id = GetAndIncrementNextMenuId(); + command_id_to_table_info_[command_id] = {set.uuid(), contains_current_url}; + AddItem(command_id, base::UTF8ToUTF16(set.name())); + } +} + +AddToComparisonTableSubMenuModel::~AddToComparisonTableSubMenuModel() = default; + +void AddToComparisonTableSubMenuModel::ExecuteCommand(int command_id, + int event_flags) { + auto& current_url = + browser_->GetActiveTabInterface()->GetContents()->GetLastCommittedURL(); + if (command_id == IDC_CREATE_NEW_COMPARISON_TABLE_WITH_TAB) { + chrome::OpenCommerceProductSpecificationsTab( + browser_, {current_url}, + browser_->GetTabStripModel()->GetIndexOfTab( + browser_->GetActiveTabInterface())); + return; + } + + auto& title = browser_->GetActiveTabInterface()->GetContents()->GetTitle(); + const UrlInfo url_info(current_url, title); + + const auto table_info_for_uuid = command_id_to_table_info_.find(command_id); + AddUrlToSet(url_info, table_info_for_uuid->second.uuid); +} + +bool AddToComparisonTableSubMenuModel::IsCommandIdEnabled( + int command_id) const { + if (command_id == IDC_CREATE_NEW_COMPARISON_TABLE_WITH_TAB) { + return true; + } + + // Disable the table option if it already contains the current URL. + const auto table_info_for_uuid = command_id_to_table_info_.find(command_id); + return !table_info_for_uuid->second.contains_current_url; +} + +int AddToComparisonTableSubMenuModel::GetAndIncrementNextMenuId() { + const int current_id = next_menu_id_; + next_menu_id_ += AppMenuModel::kNumUnboundedMenuTypes; + return current_id; +} + +void AddToComparisonTableSubMenuModel::AddUrlToSet(const UrlInfo& url_info, + base::Uuid set_uuid) { + std::optional<ProductSpecificationsSet> set = + product_specs_service_->GetSetByUuid(set_uuid); + + // If the set was not found, then it was deleted while the menu was open. + if (!set.has_value()) { + return; + } + + const GURL& url = url_info.url; + const std::u16string& title = url_info.title; + + std::vector<UrlInfo> existing_url_infos = set->url_infos(); + auto it = base::ranges::find_if(existing_url_infos, + [&url](const UrlInfo& query_url_info) { + return query_url_info.url == url; + }); + + // Add the URL to the set. If it is already in the set (because it was added + // after the menu was opened), then we still show the toast. + if (it == existing_url_infos.end()) { + existing_url_infos.emplace_back(url, title); + product_specs_service_->SetUrls(set_uuid, std::move(existing_url_infos)); + } + + ShowConfirmationToast(base::UTF8ToUTF16(set->name())); +} + +void AddToComparisonTableSubMenuModel::ShowConfirmationToast( + std::u16string set_name) { + if (base::FeatureList::IsEnabled(kCompareConfirmationToast)) { + ToastController* const toast_controller = + browser_->GetFeatures().toast_controller(); + if (toast_controller) { + ToastParams params = ToastParams(ToastId::kAddedToComparisonTable); + + params.body_string_replacement_params = {set_name}; + toast_controller->MaybeShowToast(ToastParams(std::move(params))); + } + } +} + +} // namespace commerce
diff --git a/chrome/browser/ui/commerce/add_to_comparison_table_sub_menu_model.h b/chrome/browser/ui/commerce/add_to_comparison_table_sub_menu_model.h new file mode 100644 index 0000000..ad4cb9c2 --- /dev/null +++ b/chrome/browser/ui/commerce/add_to_comparison_table_sub_menu_model.h
@@ -0,0 +1,59 @@ +// Copyright 2025 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_UI_COMMERCE_ADD_TO_COMPARISON_TABLE_SUB_MENU_MODEL_H_ +#define CHROME_BROWSER_UI_COMMERCE_ADD_TO_COMPARISON_TABLE_SUB_MENU_MODEL_H_ + +#include "base/uuid.h" +#include "components/commerce/core/product_specifications/product_specifications_service.h" +#include "ui/menus/simple_menu_model.h" +#include "url/gurl.h" + +class Browser; + +namespace commerce { + +class AddToComparisonTableSubMenuModel : public ui::SimpleMenuModel, + public ui::SimpleMenuModel::Delegate { + public: + AddToComparisonTableSubMenuModel( + Browser* browser, + commerce::ProductSpecificationsService* product_specs_service); + + AddToComparisonTableSubMenuModel(const AddToComparisonTableSubMenuModel&) = + delete; + AddToComparisonTableSubMenuModel& operator=( + const AddToComparisonTableSubMenuModel&) = delete; + + ~AddToComparisonTableSubMenuModel() override; + + // ui::SimpleMenuModel::Delegate: + void ExecuteCommand(int command_id, int event_flags) override; + bool IsCommandIdEnabled(int command_id) const override; + + private: + struct TableInfo { + // UUID of the table. + base::Uuid uuid; + + // Whether the current URL is already in the table. + bool contains_current_url; + }; + + int GetAndIncrementNextMenuId(); + + void AddUrlToSet(const UrlInfo& url, base::Uuid set_uuid); + void ShowConfirmationToast(std::u16string set_name); + + raw_ptr<Browser> browser_; + raw_ptr<ProductSpecificationsService> product_specs_service_; + + int next_menu_id_ = 0; + + std::map<int, TableInfo> command_id_to_table_info_; +}; + +} // namespace commerce + +#endif // CHROME_BROWSER_UI_COMMERCE_ADD_TO_COMPARISON_TABLE_SUB_MENU_MODEL_H_
diff --git a/chrome/browser/ui/commerce/add_to_comparison_table_sub_menu_model_browsertest.cc b/chrome/browser/ui/commerce/add_to_comparison_table_sub_menu_model_browsertest.cc new file mode 100644 index 0000000..63bd42a5 --- /dev/null +++ b/chrome/browser/ui/commerce/add_to_comparison_table_sub_menu_model_browsertest.cc
@@ -0,0 +1,166 @@ +// Copyright 2025 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/ui/commerce/add_to_comparison_table_sub_menu_model.h" + +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/ui/browser.h" +#include "chrome/browser/ui/browser_window/public/browser_window_features.h" +#include "chrome/browser/ui/toasts/toast_controller.h" +#include "chrome/test/base/in_process_browser_test.h" +#include "chrome/test/base/ui_test_utils.h" +#include "components/commerce/core/commerce_utils.h" +#include "components/commerce/core/mojom/product_specifications.mojom.h" +#include "components/commerce/core/pref_names.h" +#include "components/commerce/core/product_specifications/mock_product_specifications_service.h" +#include "components/prefs/pref_service.h" +#include "content/public/test/browser_test.h" + +namespace commerce { +namespace { + +const char kTestUrl1[] = "https://example1.com"; +const char kTestUrl2[] = "https://example2.com"; +const char kTestUrl3[] = "https://example3.com"; + +// One item for "New comparison table", one for the separator, and two for the +// existing tables. +const int kFullItemCount = 4; + +} // namespace + +class AddToComparisonTableSubMenuModelBrowserTest + : public InProcessBrowserTest { + public: + void SetUpOnMainThread() override { + InProcessBrowserTest::SetUpOnMainThread(); + + browser()->profile()->GetPrefs()->SetInteger( + commerce::kProductSpecificationsAcceptedDisclosureVersion, + static_cast<int>( + commerce::product_specifications::mojom::DisclosureVersion::kV1)); + + product_specs_service = + std::make_unique<MockProductSpecificationsService>(); + ON_CALL(*product_specs_service, GetAllProductSpecifications()) + .WillByDefault(testing::Return(kProductSpecsSets)); + } + + void NavigateToURL(const GURL& url) { + ui_test_utils::NavigateToURLWithDisposition( + browser(), url, WindowOpenDisposition::CURRENT_TAB, + ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP); + } + + protected: + std::unique_ptr<MockProductSpecificationsService> product_specs_service; + + const std::vector<ProductSpecificationsSet> kProductSpecsSets = { + ProductSpecificationsSet( + base::Uuid::GenerateRandomV4().AsLowercaseString(), + 0, + 0, + { + GURL(kTestUrl1), + }, + "Set 1"), + ProductSpecificationsSet( + base::Uuid::GenerateRandomV4().AsLowercaseString(), + 0, + 0, + { + GURL(kTestUrl1), + GURL(kTestUrl2), + }, + "Set 2")}; +}; + +IN_PROC_BROWSER_TEST_F(AddToComparisonTableSubMenuModelBrowserTest, + ContainsMultipleSets) { + AddToComparisonTableSubMenuModel sub_menu_model(browser(), + product_specs_service.get()); + + ASSERT_TRUE(sub_menu_model.GetItemCount() == kFullItemCount); + + ASSERT_TRUE(sub_menu_model.GetLabelAt(2) == u"Set 1"); + ASSERT_TRUE(sub_menu_model.GetLabelAt(3) == u"Set 2"); + + ASSERT_TRUE( + sub_menu_model.IsCommandIdEnabled(sub_menu_model.GetCommandIdAt(0))); + ASSERT_TRUE( + sub_menu_model.IsCommandIdEnabled(sub_menu_model.GetCommandIdAt(2))); + ASSERT_TRUE( + sub_menu_model.IsCommandIdEnabled(sub_menu_model.GetCommandIdAt(3))); +} + +IN_PROC_BROWSER_TEST_F(AddToComparisonTableSubMenuModelBrowserTest, + ContainsNoSets) { + ON_CALL(*product_specs_service, GetAllProductSpecifications()) + .WillByDefault(testing::Return(std::vector<ProductSpecificationsSet>())); + + AddToComparisonTableSubMenuModel sub_menu_model(browser(), + product_specs_service.get()); + + // One item for "New comparison table". + ASSERT_TRUE(sub_menu_model.GetItemCount() == 1); + + ASSERT_TRUE( + sub_menu_model.IsCommandIdEnabled(sub_menu_model.GetCommandIdAt(0))); +} + +IN_PROC_BROWSER_TEST_F(AddToComparisonTableSubMenuModelBrowserTest, + DisablesTablesContainingCurrentUrl) { + NavigateToURL(GURL(kTestUrl2)); + + AddToComparisonTableSubMenuModel sub_menu_model(browser(), + product_specs_service.get()); + + ASSERT_TRUE(sub_menu_model.GetItemCount() == kFullItemCount); + + // Set 2 should be disabled since it already contains the current URL. + ASSERT_TRUE( + sub_menu_model.IsCommandIdEnabled(sub_menu_model.GetCommandIdAt(2))); + ASSERT_FALSE( + sub_menu_model.IsCommandIdEnabled(sub_menu_model.GetCommandIdAt(3))); +} + +IN_PROC_BROWSER_TEST_F(AddToComparisonTableSubMenuModelBrowserTest, + UrlAddedToNewSet) { + NavigateToURL(GURL(kTestUrl1)); + + AddToComparisonTableSubMenuModel sub_menu_model(browser(), + product_specs_service.get()); + + sub_menu_model.ExecuteCommand(sub_menu_model.GetCommandIdAt(0), 0); + + // Check that a new tab was opened to create the table with the URL. + ASSERT_TRUE(browser()->tab_strip_model()->count() == 2); + ASSERT_TRUE( + browser()->tab_strip_model()->GetActiveWebContents()->GetVisibleURL() == + GetProductSpecsTabUrl({GURL(kTestUrl1)})); +} + +IN_PROC_BROWSER_TEST_F(AddToComparisonTableSubMenuModelBrowserTest, + UrlAddedToExistingSet) { + NavigateToURL(GURL(kTestUrl3)); + const auto& title = + browser()->GetActiveTabInterface()->GetContents()->GetTitle(); + + AddToComparisonTableSubMenuModel sub_menu_model(browser(), + product_specs_service.get()); + + ASSERT_TRUE(sub_menu_model.GetItemCount() == kFullItemCount); + + ON_CALL(*product_specs_service, GetSetByUuid(kProductSpecsSets[0].uuid())) + .WillByDefault(testing::Return(kProductSpecsSets[0])); + EXPECT_CALL(*product_specs_service, + SetUrls(kProductSpecsSets[0].uuid(), + testing::ElementsAre(UrlInfo(GURL(kTestUrl1), u""), + UrlInfo(GURL(kTestUrl3), title)))) + .Times(1); + + sub_menu_model.ExecuteCommand(sub_menu_model.GetCommandIdAt(2), 0); +} + +} // namespace commerce
diff --git a/chrome/browser/ui/commerce/compare_sub_menu_model.cc b/chrome/browser/ui/commerce/compare_sub_menu_model.cc index 3c8eebc..63c3d0a 100644 --- a/chrome/browser/ui/commerce/compare_sub_menu_model.cc +++ b/chrome/browser/ui/commerce/compare_sub_menu_model.cc
@@ -5,15 +5,31 @@ #include "chrome/browser/ui/commerce/compare_sub_menu_model.h" #include "chrome/app/chrome_command_ids.h" +#include "chrome/browser/commerce/product_specifications/product_specifications_service_factory.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/ui/browser.h" +#include "chrome/browser/ui/commerce/add_to_comparison_table_sub_menu_model.h" +#include "chrome/grit/generated_resources.h" #include "components/strings/grit/components_strings.h" namespace commerce { CompareSubMenuModel::CompareSubMenuModel( - ui::SimpleMenuModel::Delegate* delegate) + ui::SimpleMenuModel::Delegate* delegate, + Browser* browser) : SimpleMenuModel(delegate) { + add_to_comparison_table_sub_menu_model_ = + std::make_unique<AddToComparisonTableSubMenuModel>( + browser, ProductSpecificationsServiceFactory::GetForBrowserContext( + browser->profile())); + + AddSubMenuWithStringId(IDC_ADD_TO_COMPARISON_TABLE_MENU, + IDS_COMPARE_ADD_TAB_TO_COMPARISON_TABLE, + add_to_comparison_table_sub_menu_model_.get()); AddItemWithStringId(IDC_SHOW_ALL_COMPARISON_TABLES, IDS_COMPARE_SHOW_ALL_COMPARISON_TABLES); } +CompareSubMenuModel::~CompareSubMenuModel() = default; + } // namespace commerce
diff --git a/chrome/browser/ui/commerce/compare_sub_menu_model.h b/chrome/browser/ui/commerce/compare_sub_menu_model.h index 5b7939ad..a1e758b6 100644 --- a/chrome/browser/ui/commerce/compare_sub_menu_model.h +++ b/chrome/browser/ui/commerce/compare_sub_menu_model.h
@@ -5,15 +5,24 @@ #ifndef CHROME_BROWSER_UI_COMMERCE_COMPARE_SUB_MENU_MODEL_H_ #define CHROME_BROWSER_UI_COMMERCE_COMPARE_SUB_MENU_MODEL_H_ +#include "chrome/browser/ui/commerce/add_to_comparison_table_sub_menu_model.h" #include "ui/menus/simple_menu_model.h" +class Browser; + namespace commerce { class CompareSubMenuModel : public ui::SimpleMenuModel { public: - explicit CompareSubMenuModel(ui::SimpleMenuModel::Delegate* delegate); + CompareSubMenuModel(ui::SimpleMenuModel::Delegate* delegate, + Browser* browser); CompareSubMenuModel(const CompareSubMenuModel&) = delete; CompareSubMenuModel& operator=(const CompareSubMenuModel&) = delete; + ~CompareSubMenuModel() override; + + private: + std::unique_ptr<AddToComparisonTableSubMenuModel> + add_to_comparison_table_sub_menu_model_; }; } // namespace commerce
diff --git a/chrome/browser/ui/commerce/product_specifications_page_action_controller.cc b/chrome/browser/ui/commerce/product_specifications_page_action_controller.cc index 9a2485d..ee23ed9 100644 --- a/chrome/browser/ui/commerce/product_specifications_page_action_controller.cc +++ b/chrome/browser/ui/commerce/product_specifications_page_action_controller.cc
@@ -117,14 +117,19 @@ OnProductSpecificationsSetUpdate( const ProductSpecificationsSet& before_set, const ProductSpecificationsSet& after_set) { + bool is_in_set = base::Contains(after_set.urls(), current_url_); if (!product_group_for_page_.has_value()) { + if (is_in_set) { + most_recent_comparison_table_uuid_for_page_ = after_set.uuid(); + NotifyHost(); + } return; } - bool is_in_set = base::Contains(after_set.urls(), current_url_); // Hide the page action if the page has been added to a set that is not // recommended set. if (product_group_for_page_->uuid != after_set.uuid()) { if (is_in_set) { + most_recent_comparison_table_uuid_for_page_ = after_set.uuid(); product_group_for_page_ = std::nullopt; is_in_recommended_set_ = false; NotifyHost(); @@ -212,9 +217,20 @@ } GURL ProductSpecificationsPageActionController::GetComparisonTableURL() { - CHECK(product_group_for_page_.has_value()); - return commerce::GetProductSpecsTabUrlForID( - product_group_for_page_.value().uuid); + CHECK(product_group_for_page_.has_value() || + most_recent_comparison_table_uuid_for_page_.has_value()); + + if (most_recent_comparison_table_uuid_for_page_.has_value()) { + return commerce::GetProductSpecsTabUrlForID( + most_recent_comparison_table_uuid_for_page_.value()); + } + + if (product_group_for_page_.has_value()) { + return commerce::GetProductSpecsTabUrlForID( + product_group_for_page_.value().uuid); + } + + NOTREACHED(); } void ProductSpecificationsPageActionController::HandleProductInfoResponse(
diff --git a/chrome/browser/ui/commerce/product_specifications_page_action_controller.h b/chrome/browser/ui/commerce/product_specifications_page_action_controller.h index 0af5105f..4b1f8da 100644 --- a/chrome/browser/ui/commerce/product_specifications_page_action_controller.h +++ b/chrome/browser/ui/commerce/product_specifications_page_action_controller.h
@@ -70,6 +70,9 @@ // The product group that current page can be added to if available. std::optional<ProductGroup> product_group_for_page_; + // The UUID of the comparison table the page was most recently added to. + std::optional<base::Uuid> most_recent_comparison_table_uuid_for_page_; + // A bool to indicate whether the product has been added to the recommended // product specifications set. Please note that this will be false for pages // that are already in product specifications set on navigation, as there is
diff --git a/chrome/browser/ui/commerce/product_specifications_page_action_controller_unittest.cc b/chrome/browser/ui/commerce/product_specifications_page_action_controller_unittest.cc index 3613068..e5c66178 100644 --- a/chrome/browser/ui/commerce/product_specifications_page_action_controller_unittest.cc +++ b/chrome/browser/ui/commerce/product_specifications_page_action_controller_unittest.cc
@@ -16,6 +16,7 @@ #include "chrome/browser/ui/toasts/toast_features.h" #include "chrome/test/base/testing_profile.h" #include "components/commerce/core/commerce_feature_list.h" +#include "components/commerce/core/commerce_utils.h" #include "components/commerce/core/mock_account_checker.h" #include "components/commerce/core/mock_cluster_manager.h" #include "components/commerce/core/mock_shopping_service.h" @@ -523,4 +524,32 @@ } } +TEST_F(ProductSpecificationsPageActionControllerUnittest, + OnProductSpecificationsSetUpdated_NoProductGroupOrProductInfo) { + // Page has no product group or product info. + mock_cluster_manager_->SetResponseForGetProductGroupForCandidateProduct( + std::nullopt); + shopping_service_->SetResponseForGetProductInfoForUrl(std::nullopt); + + controller_->ResetForNewNavigation(GURL(kTestUrl1)); + base::RunLoop().RunUntilIdle(); + + // Create a new set to add the page to. + const base::Uuid& uuid = base::Uuid::GenerateRandomV4(); + ProductSpecificationsSet set1 = ProductSpecificationsSet( + uuid.AsLowercaseString(), 0, 0, {GURL(kTestUrl2)}, "Set 1"); + ProductSpecificationsSet set2 = + ProductSpecificationsSet(uuid.AsLowercaseString(), 0, 0, + {GURL(kTestUrl1), GURL(kTestUrl2)}, "Set 2"); + + // Notify the controller that the current page has been added to a set. + controller_->OnProductSpecificationsSetUpdate(set1, set2); + base::RunLoop().RunUntilIdle(); + + // Check that the comparison table URL is the one the page was added to, even + // though there is no recommended product group. + ASSERT_TRUE(controller_->GetComparisonTableURL() == + GetProductSpecsTabUrlForID(uuid)); +} + } // namespace commerce
diff --git a/chrome/browser/ui/lens/lens_overlay_controller.cc b/chrome/browser/ui/lens/lens_overlay_controller.cc index e456a53..28998b0 100644 --- a/chrome/browser/ui/lens/lens_overlay_controller.cc +++ b/chrome/browser/ui/lens/lens_overlay_controller.cc
@@ -1614,7 +1614,22 @@ std::move(callback).Run(bytes, lens::MimeType::kPdf, page_count); } -void LensOverlayController::GetPartialPdfText(uint32_t page_count) { +void LensOverlayController::FetchVisiblePageIndexAndGetPartialPdfText( + uint32_t page_count) { + pdf::PDFDocumentHelper* pdf_helper = + pdf::PDFDocumentHelper::MaybeGetForWebContents(tab_->GetContents()); + if (!pdf_helper) { + return; + } + + pdf_helper->GetMostVisiblePageIndex( + base::BindOnce(&LensOverlayController::GetPartialPdfText, + weak_factory_.GetWeakPtr(), page_count)); +} + +void LensOverlayController::GetPartialPdfText( + uint32_t page_count, + std::optional<uint32_t> visible_page_index) { pdf::PDFDocumentHelper* pdf_helper = pdf::PDFDocumentHelper::MaybeGetForWebContents(tab_->GetContents()); if (!pdf_helper || @@ -1623,6 +1638,8 @@ return; } + // TODO(387306854): Add logic to grab page text form the visible page index. + // Fetch the first page of text which will be then recursively fetch following // pages. initialization_data_->pdf_pages_text_.clear(); @@ -1788,7 +1805,7 @@ // If the new page is a PDF, fetch the text from the page to be used as early // suggest signals. if (content_type == lens::MimeType::kPdf) { - GetPartialPdfText(page_count.value_or(0)); + FetchVisiblePageIndexAndGetPartialPdfText(page_count.value_or(0)); } lens_overlay_query_controller_->SendPageContentUpdateRequest( @@ -2025,9 +2042,11 @@ // If PDF content was extracted from the page, fetch the text from the PDF to // be used as early suggest signals. - if (initialization_data_->page_content_type_ == lens::MimeType::kPdf) { + if (initialization_data_->page_content_type_ == lens::MimeType::kPdf && + !initialization_data_->page_content_bytes_.empty()) { CHECK(initialization_data_->pdf_page_count_.has_value()); - GetPartialPdfText(initialization_data_->pdf_page_count_.value()); + FetchVisiblePageIndexAndGetPartialPdfText( + initialization_data_->pdf_page_count_.value()); } // If the StartQueryFlow optimization is enabled, the page contents will not
diff --git a/chrome/browser/ui/lens/lens_overlay_controller.h b/chrome/browser/ui/lens/lens_overlay_controller.h index 87839a81..ccf9b7e5 100644 --- a/chrome/browser/ui/lens/lens_overlay_controller.h +++ b/chrome/browser/ui/lens/lens_overlay_controller.h
@@ -709,15 +709,21 @@ #if BUILDFLAG(ENABLE_PDF) // Receives the PDF bytes from the IPC call to the PDF renderer and stores - // them in initialization data. + // them in initialization data. `pdf_page_count` is passed to the partial PDF + // text fetch to be used to determine when to stop fetching. void OnPdfBytesReceived(PageContentRetrievedCallback callback, pdf::mojom::PdfListener::GetPdfBytesStatus status, const std::vector<uint8_t>& bytes, uint32_t pdf_page_count); + // Fetches the visible page index from the PDF renderer and then starts the + // process of fetching the text from the PDF to be used for suggest signals. + void FetchVisiblePageIndexAndGetPartialPdfText(uint32_t page_count); + // Starts the process of fetching the text from the PDF to be used for suggest // signals. - void GetPartialPdfText(uint32_t total_page_count); + void GetPartialPdfText(uint32_t page_count, + std::optional<uint32_t> visible_page_index); // Gets the partial text from the PDF to be used for suggest. Schedules for // the next page of text to be fetched, from the PDF in page order until
diff --git a/chrome/browser/ui/lens/lens_overlay_controller_browsertest.cc b/chrome/browser/ui/lens/lens_overlay_controller_browsertest.cc index e32bfe1..65120aed 100644 --- a/chrome/browser/ui/lens/lens_overlay_controller_browsertest.cc +++ b/chrome/browser/ui/lens/lens_overlay_controller_browsertest.cc
@@ -597,6 +597,7 @@ {"use-dynamic-theme-min-chroma", "3.0"}}}, {lens::features::kLensOverlayContextualSearchbox, { + {"send-page-url-for-contextualization", "true"}, {"use-inner-text-as-context", "true"}, {"use-inner-html-as-context", "true"}, }}, @@ -4625,7 +4626,9 @@ const override { auto enabled = PDFExtensionTestBase::GetEnabledFeatures(); enabled.push_back({lens::features::kLensOverlayContextualSearchbox, - {{"use-pdfs-as-context", "true"}, + {{"send-page-url-for-contextualization", "true"}, + {"characters-per-page-heuristic", "1"}, + {"use-pdfs-as-context", "true"}, {"use-inner-html-as-context", "true"}, {"file-upload-limit-bytes", base::NumberToString(kFileSizeLimitBytes)}}}); @@ -4697,10 +4700,12 @@ static_cast<lens::TestLensOverlayQueryController*>( controller->get_lens_overlay_query_controller_for_testing()); ASSERT_TRUE(base::test::RunUntil([&] { - return fake_query_controller->last_sent_partial_content().size() == 1; + return fake_query_controller->last_sent_partial_content().pages_size() == 1; })); - ASSERT_EQ(u"this is some text\r\nsome more text", - fake_query_controller->last_sent_partial_content()[0]); + ASSERT_EQ( + "this is some text\r\nsome more text", + fake_query_controller->last_sent_partial_content().pages(0).text_segments( + 0)); } IN_PROC_BROWSER_TEST_P(LensOverlayControllerBrowserPDFContextualizationTest, @@ -5054,6 +5059,7 @@ auto enabled = PDFExtensionTestBase::GetEnabledFeatures(); enabled.push_back({lens::features::kLensOverlayContextualSearchbox, {{"use-pdfs-as-context", "true"}, + {"characters-per-page-heuristic", "1"}, {"use-inner-html-as-context", "true"}, {"pdf-text-character-limit", "50"}, {"file-upload-limit-bytes", @@ -5093,12 +5099,16 @@ controller->get_lens_overlay_query_controller_for_testing()); ASSERT_TRUE(base::test::RunUntil([&] { - return 2u == fake_query_controller->last_sent_partial_content().size(); + return 2 == fake_query_controller->last_sent_partial_content().pages_size(); })); - ASSERT_EQ(u"1 First Section\r\nThis is the first section.\r\n1", - fake_query_controller->last_sent_partial_content()[0]); - ASSERT_EQ(u"1.1 First Subsection\r\nThis is the first subsection.\r\n2", - fake_query_controller->last_sent_partial_content()[1]); + ASSERT_EQ( + "1 First Section\r\nThis is the first section.\r\n1", + fake_query_controller->last_sent_partial_content().pages(0).text_segments( + 0)); + ASSERT_EQ( + "1.1 First Subsection\r\nThis is the first subsection.\r\n2", + fake_query_controller->last_sent_partial_content().pages(1).text_segments( + 0)); } // TODO(crbug.com/40268279): Stop testing both modes after OOPIF PDF viewer @@ -6170,6 +6180,7 @@ feature_list_.InitAndEnableFeatureWithParameters( lens::features::kLensOverlayContextualSearchbox, { + {"send-page-url-for-contextualization", "true"}, {"use-inner-text-as-context", "false"}, {"use-inner-html-as-context", "true"}, });
diff --git a/chrome/browser/ui/lens/test_lens_overlay_query_controller.cc b/chrome/browser/ui/lens/test_lens_overlay_query_controller.cc index 5aa18aa..6651b47 100644 --- a/chrome/browser/ui/lens/test_lens_overlay_query_controller.cc +++ b/chrome/browser/ui/lens/test_lens_overlay_query_controller.cc
@@ -4,10 +4,28 @@ #include "test_lens_overlay_query_controller.h" +#include "base/containers/span.h" +#include "base/strings/utf_string_conversions.h" +#include "components/lens/lens_overlay_mime_type.h" #include "google_apis/common/api_error_codes.h" namespace lens { +constexpr char kPdfMimeType[] = "application/pdf"; +constexpr char kPlainTextMimeType[] = "text/plain"; +constexpr char kHtmlMimeType[] = "text/html"; + +lens::MimeType StringToContentType(const std::string& content_type) { + if (content_type == kPdfMimeType) { + return lens::MimeType::kPdf; + } else if (content_type == kHtmlMimeType) { + return lens::MimeType::kHtml; + } else if (content_type == kPlainTextMimeType) { + return lens::MimeType::kPlainText; + } + return lens::MimeType::kUnknown; +} + FakeEndpointFetcher::FakeEndpointFetcher(EndpointResponse response) : EndpointFetcher( net::DefineNetworkTrafficAnnotation("lens_overlay_mock_fetcher", @@ -132,34 +150,15 @@ query_text, lens_selection_type, additional_search_query_params); } -void TestLensOverlayQueryController::SendPageContentUpdateRequest( - base::span<const uint8_t> new_content_bytes, - lens::MimeType new_content_type, - GURL new_page_url) { - // TODO(crbug.com/378918804): Update these variables in the endpoint - // fetcher creator. - last_sent_underlying_content_bytes_ = new_content_bytes; - last_sent_underlying_content_type_ = new_content_type; - last_sent_page_url_ = new_page_url; - - LensOverlayQueryController::SendPageContentUpdateRequest( - new_content_bytes, new_content_type, new_page_url); -} - -void TestLensOverlayQueryController::SendPartialPageContentRequest( - base::span<const std::u16string> partial_content) { - last_sent_partial_content_ = partial_content; - LensOverlayQueryController::SendPartialPageContentRequest(partial_content); -} - void TestLensOverlayQueryController::ResetTestingState() { last_lens_selection_type_ = lens::UNKNOWN_SELECTION_TYPE; last_queried_region_.reset(); last_queried_text_.clear(); last_queried_region_bytes_ = std::nullopt; last_sent_underlying_content_bytes_ = base::span<const uint8_t>(); - last_sent_partial_content_ = base::span<const std::u16string>(); + last_sent_partial_content_ = lens::LensOverlayDocument(); last_sent_underlying_content_type_ = lens::MimeType::kUnknown; + last_sent_page_content_data_.clear(); last_sent_page_url_ = GURL(); num_interaction_requests_sent_ = 0; } @@ -195,6 +194,8 @@ // The server doesn't send a response to this request, so no need to set // the response string to something meaningful. fake_server_response_string = ""; + last_sent_partial_content_.CopyFrom( + request->objects_request().payload().partial_pdf_document()); } else if (request->has_objects_request() && !request->objects_request().has_image_data() && request->objects_request().has_payload()) { @@ -206,6 +207,13 @@ fake_server_response_string = ""; sent_page_content_request_id_.CopyFrom( request->objects_request().request_context().request_id()); + last_sent_page_content_data_ = + std::string(request->objects_request().payload().content_data()); + last_sent_underlying_content_bytes_ = + base::as_byte_span(last_sent_page_content_data_); + last_sent_underlying_content_type_ = StringToContentType( + request->objects_request().payload().content_type()); + last_sent_page_url_ = GURL(request->objects_request().payload().page_url()); } else if (request->has_objects_request()) { // Full image request. sent_full_image_objects_request_.CopyFrom(request->objects_request());
diff --git a/chrome/browser/ui/lens/test_lens_overlay_query_controller.h b/chrome/browser/ui/lens/test_lens_overlay_query_controller.h index 58e5f07..b0db74c 100644 --- a/chrome/browser/ui/lens/test_lens_overlay_query_controller.h +++ b/chrome/browser/ui/lens/test_lens_overlay_query_controller.h
@@ -128,7 +128,7 @@ return last_sent_underlying_content_type_; } - base::span<const std::u16string> last_sent_partial_content() const { + const lens::LensOverlayDocument& last_sent_partial_content() const { return last_sent_partial_content_; } @@ -202,13 +202,6 @@ std::map<std::string, std::string> additional_search_query_params) override; - void SendPageContentUpdateRequest(base::span<const uint8_t> new_content_bytes, - lens::MimeType new_content_type, - GURL new_page_url) override; - - void SendPartialPageContentRequest( - base::span<const std::u16string> partial_content) override; - // Resets the test state. void ResetTestingState(); @@ -285,6 +278,10 @@ // The last region bytes sent by the query controller. std::optional<SkBitmap> last_queried_region_bytes_; + // The last page content data sent by the query controller. Used to prevent + // dangling references by the underlying content bytes span. + std::string last_sent_page_content_data_; + // The last underlying content bytes sent by the query controller. base::raw_span<const uint8_t> last_sent_underlying_content_bytes_; @@ -292,7 +289,7 @@ lens::MimeType last_sent_underlying_content_type_; // The last partial content sent by the query controller. - base::raw_span<const std::u16string> last_sent_partial_content_; + lens::LensOverlayDocument last_sent_partial_content_; // The last page url sent by the query controller. GURL last_sent_page_url_;
diff --git a/chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_pref_names.cc b/chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_pref_names.cc index 2780ecd..006ee3b 100644 --- a/chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_pref_names.cc +++ b/chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_pref_names.cc
@@ -14,6 +14,8 @@ registry->RegisterBooleanPref(kTabGroupsDeletionSkipDialogOnUngroup, false); registry->RegisterBooleanPref(kTabGroupsDeletionSkipDialogOnRemoveTab, false); registry->RegisterBooleanPref(kTabGroupsDeletionSkipDialogOnCloseTab, false); + registry->RegisterBooleanPref(kTabGroupsDeletionSkipDialogOnLeaveGroup, + false); registry->RegisterIntegerPref(kTabGroupLearnMoreFooterShownCount, 0); }
diff --git a/chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_pref_names.h b/chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_pref_names.h index 0490020..5235f61 100644 --- a/chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_pref_names.h +++ b/chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_pref_names.h
@@ -32,6 +32,8 @@ "tab_groups.deletion.skip_dialog_on_remove_tab"; inline constexpr char kTabGroupsDeletionSkipDialogOnCloseTab[] = "tab_groups.deletion.skip_dialog_on_close_tab"; +inline constexpr char kTabGroupsDeletionSkipDialogOnLeaveGroup[] = + "tab_groups.deletion.skip_dialog_on_leave_group"; // Integer that keep track of how many times the learn more footer in the // TabGroupEditorBubbleView has been seen by the user. Once this value reaches
diff --git a/chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_utils.cc b/chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_utils.cc index 4420948..dd013d3 100644 --- a/chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_utils.cc +++ b/chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_utils.cc
@@ -7,13 +7,18 @@ #include <numeric> #include <unordered_set> +#include "base/functional/bind.h" +#include "base/functional/callback_helpers.h" #include "base/metrics/user_metrics.h" #include "base/not_fatal_until.h" #include "base/strings/utf_string_conversions.h" #include "base/uuid.h" #include "chrome/app/vector_icons/vector_icons.h" +#include "chrome/browser/collaboration/collaboration_service_factory.h" +#include "chrome/browser/data_sharing/data_sharing_service_factory.h" #include "chrome/browser/favicon/favicon_utils.h" #include "chrome/browser/profiles/profile.h" +#include "chrome/browser/signin/identity_manager_factory.h" #include "chrome/browser/sync/sync_service_factory.h" #include "chrome/browser/tab_group_sync/tab_group_sync_service_factory.h" #include "chrome/browser/tab_group_sync/tab_group_sync_utils.h" @@ -35,11 +40,17 @@ #include "chrome/common/pref_names.h" #include "chrome/common/webui_url_constants.h" #include "chrome/grit/generated_resources.h" +#include "components/collaboration/public/collaboration_service.h" +#include "components/data_sharing/public/data_sharing_service.h" #include "components/data_sharing/public/features.h" +#include "components/data_sharing/public/group_data.h" #include "components/saved_tab_groups/public/features.h" #include "components/saved_tab_groups/public/pref_names.h" #include "components/saved_tab_groups/public/saved_tab_group_tab.h" +#include "components/saved_tab_groups/public/tab_group_sync_service.h" +#include "components/saved_tab_groups/public/types.h" #include "components/saved_tab_groups/public/utils.h" +#include "components/signin/public/identity_manager/identity_manager.h" #include "components/sync/base/user_selectable_type.h" #include "components/sync/service/sync_service.h" #include "components/sync/service/sync_user_settings.h" @@ -208,6 +219,70 @@ } } +// static +void SavedTabGroupUtils::LeaveSharedGroup(const Browser* browser, + const base::Uuid& saved_group_guid) { + TabGroupSyncService* tab_group_service = + SavedTabGroupUtils::GetServiceForProfile(browser->profile()); + if (!tab_group_service) { + return; + } + + const std::optional<SavedTabGroup> saved_group = + tab_group_service->GetGroup(saved_group_guid); + if (!saved_group) { + return; + } + + if (!saved_group->collaboration_id()) { + return; + } + + auto leave_callback = base::BindOnce( + [](const Browser* browser, const base::Uuid& saved_group_guid) { + TabGroupSyncService* tab_group_service = + SavedTabGroupUtils::GetServiceForProfile(browser->profile()); + if (!tab_group_service) { + return; + } + + const std::optional<SavedTabGroup> saved_group = + tab_group_service->GetGroup(saved_group_guid); + if (!saved_group) { + return; + } + + if (!saved_group->collaboration_id()) { + return; + } + + data_sharing::DataSharingService* data_sharing_service = + data_sharing::DataSharingServiceFactory::GetForProfile( + browser->profile()); + if (!data_sharing_service) { + return; + } + + if (saved_group->local_group_id()) { + SavedTabGroupUtils::RemoveGroupFromTabstrip( + nullptr, saved_group->local_group_id().value()); + } + + data_sharing_service->LeaveGroup( + data_sharing::GroupId(saved_group->collaboration_id()->value()), + base::DoNothing()); + }, + browser, saved_group_guid); + DeletionDialogController::DialogMetadata dialog_metadata( + DeletionDialogController::DialogType::LeaveGroup, + /*closing_group_count=*/1, + /*closing_multiple_tabs=*/saved_group->saved_tabs().size() > 1); + dialog_metadata.title_of_closing_group = saved_group->title(); + browser->tab_group_deletion_dialog_controller()->MaybeShowDialog( + dialog_metadata, std::move(leave_callback)); +} + +// static void SavedTabGroupUtils::MaybeShowSavedTabGroupDeletionDialog( Browser* browser, DeletionDialogController::DialogType type, @@ -675,11 +750,13 @@ return elements[0]; } +// static bool SavedTabGroupUtils::ShouldAutoPinNewTabGroups(Profile* profile) { return profile->GetPrefs()->GetBoolean( tab_groups::prefs::kAutoPinNewTabGroups); } +// static bool SavedTabGroupUtils::AreSavedTabGroupsSyncedForProfile(Profile* profile) { const syncer::SyncService* const sync_service = SyncServiceFactory::GetForProfile(profile); @@ -692,6 +769,7 @@ syncer::UserSelectableType::kSavedTabGroups); } +// static bool SavedTabGroupUtils::SupportsSharedTabGroups() { return tab_groups::IsTabGroupsSaveV2Enabled() && tab_groups::IsTabGroupSyncServiceDesktopMigrationEnabled() && @@ -699,4 +777,50 @@ data_sharing::features::kDataSharingFeature); } +// static +bool SavedTabGroupUtils::IsOwnerOfSharedTabGroup(Profile* profile, + const base::Uuid& sync_id) { + // TODO(380515575): Create a function to determine if the user is signed in or + // not instead of checking here. + signin::IdentityManager* identity_manager = + IdentityManagerFactory::GetForProfile(profile); + + CoreAccountInfo account = + identity_manager->GetPrimaryAccountInfo(signin::ConsentLevel::kSignin); + + if (account.IsEmpty()) { + return true; + } + + TabGroupSyncService* tab_group_service = + SavedTabGroupUtils::GetServiceForProfile(profile); + if (!tab_group_service) { + return true; + } + + const std::optional<SavedTabGroup> saved_group = + tab_group_service->GetGroup(sync_id); + if (!saved_group) { + return true; + } + + std::optional<CollaborationId> collaboration_id = + saved_group->collaboration_id(); + if (!collaboration_id) { + return true; + } + + collaboration::CollaborationService* collaboration_service = + collaboration::CollaborationServiceFactory::GetForProfile(profile); + if (!collaboration_service) { + return true; + } + + data_sharing::MemberRole member_role = + collaboration_service->GetCurrentUserRoleForGroup( + data_sharing::GroupId(collaboration_id->value())); + + return data_sharing::MemberRole::kOwner == member_role; +} + } // namespace tab_groups
diff --git a/chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_utils.h b/chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_utils.h index 02e49fe..55a344ff 100644 --- a/chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_utils.h +++ b/chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_utils.h
@@ -35,6 +35,7 @@ class SavedTabGroupUtils { public: DECLARE_CLASS_ELEMENT_IDENTIFIER_VALUE(kDeleteGroupMenuItem); + DECLARE_CLASS_ELEMENT_IDENTIFIER_VALUE(kLeaveGroupMenuItem); DECLARE_CLASS_ELEMENT_IDENTIFIER_VALUE(kMoveGroupToNewWindowMenuItem); DECLARE_CLASS_ELEMENT_IDENTIFIER_VALUE(kToggleGroupPinStateMenuItem); DECLARE_CLASS_ELEMENT_IDENTIFIER_VALUE(kTabsTitleItem); @@ -62,6 +63,8 @@ const base::Uuid& saved_group_guid); static void DeleteSavedGroup(const Browser* browser, const base::Uuid& saved_group_guid); + static void LeaveSharedGroup(const Browser* browser, + const base::Uuid& saved_group_guid); // Open the `url` to the end of `browser` tab strip as a new ungrouped tab. static void OpenUrlInNewUngroupedTab(Browser* browser, const GURL& url); @@ -158,6 +161,10 @@ // Returns true if shared tab groups are supported. static bool SupportsSharedTabGroups(); + + // Returns true if the user is the owner of the shared tab group. + static bool IsOwnerOfSharedTabGroup(Profile* profile, + const base::Uuid& sync_id); }; } // namespace tab_groups
diff --git a/chrome/browser/ui/tabs/tab_group_deletion_dialog_controller.cc b/chrome/browser/ui/tabs/tab_group_deletion_dialog_controller.cc index c8b7180..d33a7ba 100644 --- a/chrome/browser/ui/tabs/tab_group_deletion_dialog_controller.cc +++ b/chrome/browser/ui/tabs/tab_group_deletion_dialog_controller.cc
@@ -145,6 +145,24 @@ base::i18n::MessageFormatter::FormatWithNumberedArgs( l10n_util::GetStringUTF16(kDeleteOkTextId), closing_group_count)}; } + + case DeletionDialogController::DialogType::LeaveGroup: { + const bool title_is_empty = + !dialog_metadata.title_of_closing_group.has_value() || + dialog_metadata.title_of_closing_group->empty(); + std::u16string body_text = + title_is_empty + ? l10n_util::GetStringUTF16( + IDS_DATA_SHARING_LEAVE_DIALOG_BODY_NO_GROUP_TITLE) + : l10n_util::GetStringFUTF16( + IDS_DATA_SHARING_LEAVE_DIALOG_BODY, + dialog_metadata.title_of_closing_group.value()); + + return DialogText{ + l10n_util::GetStringUTF16(IDS_DATA_SHARING_LEAVE_DIALOG_TITLE), + body_text, + l10n_util::GetStringUTF16(IDS_DATA_SHARING_LEAVE_DIALOG_CONFIRM)}; + } } } @@ -172,6 +190,10 @@ return pref_service->GetBoolean( saved_tab_groups::prefs::kTabGroupsDeletionSkipDialogOnCloseTab); } + case DeletionDialogController::DialogType::LeaveGroup: { + return pref_service->GetBoolean( + saved_tab_groups::prefs::kTabGroupsDeletionSkipDialogOnLeaveGroup); + } } } @@ -204,6 +226,11 @@ saved_tab_groups::prefs::kTabGroupsDeletionSkipDialogOnCloseTab, new_value); } + case DeletionDialogController::DialogType::LeaveGroup: { + return pref_service->SetBoolean( + saved_tab_groups::prefs::kTabGroupsDeletionSkipDialogOnLeaveGroup, + new_value); + } } }
diff --git a/chrome/browser/ui/tabs/tab_group_deletion_dialog_controller.h b/chrome/browser/ui/tabs/tab_group_deletion_dialog_controller.h index 8cf9416..85707db 100644 --- a/chrome/browser/ui/tabs/tab_group_deletion_dialog_controller.h +++ b/chrome/browser/ui/tabs/tab_group_deletion_dialog_controller.h
@@ -35,6 +35,7 @@ UngroupSingle, RemoveTabAndDelete, CloseTabAndDelete, + LeaveGroup, }; // Encapsulates metadata required to determine which strings should be @@ -52,6 +53,7 @@ DialogType type; int closing_group_count = 0; bool closing_multiple_tabs = false; + std::optional<std::u16string> title_of_closing_group; }; // State object that represents the current dialog that is being shown.
diff --git a/chrome/browser/ui/toolbar/app_menu_model.h b/chrome/browser/ui/toolbar/app_menu_model.h index 84787f3..d55b5582 100644 --- a/chrome/browser/ui/toolbar/app_menu_model.h +++ b/chrome/browser/ui/toolbar/app_menu_model.h
@@ -194,8 +194,9 @@ // varies depending upon the underlying model. The command IDs for items in // these menus will be staggered and each increment by this value, so they // don't have conflicts. Currently, this accounts for the bookmarks, recent - // tabs menus, the profile submenu and tab groups submenu. - static constexpr int kNumUnboundedMenuTypes = 4; + // tabs menus, the profile submenu, tab groups submenu, and the comparison + // tables submenu. + static constexpr int kNumUnboundedMenuTypes = 5; // First command ID to use for each unbounded menu. These should be staggered, // and there should be kNumUnboundedMenuTypes of them. @@ -203,6 +204,7 @@ static constexpr int kMinRecentTabsCommandId = kMinBookmarksCommandId + 1; static constexpr int kMinOtherProfileCommandId = kMinRecentTabsCommandId + 1; static constexpr int kMinTabGroupsCommandId = kMinOtherProfileCommandId + 1; + static constexpr int kMinCompareCommandId = kMinTabGroupsCommandId + 1; // Creates an app menu model for the given browser. Init() must be called // before passing this to an AppMenu. |app_menu_icon_controller|, if provided,
diff --git a/chrome/browser/ui/toolbar/bookmark_sub_menu_model.cc b/chrome/browser/ui/toolbar/bookmark_sub_menu_model.cc index 8d06cfc1..e14980b1 100644 --- a/chrome/browser/ui/toolbar/bookmark_sub_menu_model.cc +++ b/chrome/browser/ui/toolbar/bookmark_sub_menu_model.cc
@@ -83,7 +83,7 @@ AddSeparator(ui::NORMAL_SEPARATOR); compare_sub_menu_model_ = - std::make_unique<commerce::CompareSubMenuModel>(delegate()); + std::make_unique<commerce::CompareSubMenuModel>(delegate(), browser); AddSubMenuWithStringIdAndIcon( IDC_COMPARE_MENU, IDS_COMPARE_MENU_LABEL, compare_sub_menu_model_.get(), ui::ImageModel::FromVectorIcon(kCompareIcon, ui::kColorMenuIcon, 16));
diff --git a/chrome/browser/ui/views/bookmarks/saved_tab_groups/shared_tab_group_interactive_uitest.cc b/chrome/browser/ui/views/bookmarks/saved_tab_groups/shared_tab_group_interactive_uitest.cc index 19f1c1c..43d46554 100644 --- a/chrome/browser/ui/views/bookmarks/saved_tab_groups/shared_tab_group_interactive_uitest.cc +++ b/chrome/browser/ui/views/bookmarks/saved_tab_groups/shared_tab_group_interactive_uitest.cc
@@ -3,6 +3,8 @@ // found in the LICENSE file. #include "base/test/metrics/histogram_tester.h" +#include "chrome/browser/data_sharing/data_sharing_service_factory.h" +#include "chrome/browser/signin/identity_manager_factory.h" #include "chrome/browser/tab_group_sync/tab_group_sync_service_factory.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_element_identifiers.h" @@ -17,12 +19,19 @@ #include "chrome/browser/ui/views/tabs/tab_group_header.h" #include "chrome/browser/ui/views/tabs/tab_strip.h" #include "chrome/test/interaction/interactive_browser_test.h" +#include "components/data_sharing/public/data_sharing_service.h" #include "components/data_sharing/public/features.h" +#include "components/data_sharing/public/group_data.h" #include "components/saved_tab_groups/internal/tab_group_sync_service_impl.h" #include "components/saved_tab_groups/public/features.h" #include "components/saved_tab_groups/public/tab_group_sync_service.h" +#include "components/signin/public/base/consent_level.h" +#include "components/signin/public/identity_manager/identity_manager.h" +#include "components/signin/public/identity_manager/identity_test_utils.h" #include "components/tab_groups/tab_group_id.h" #include "content/public/test/browser_test.h" +#include "google_apis/gaia/core_account_id.h" +#include "google_apis/gaia/gaia_id.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/base/interaction/interactive_test_internal.h" @@ -95,10 +104,52 @@ return browser()->tab_strip_model()->AddToNewGroup({0}); } - void ShareTabGroup(TabGroupId group_id, std::string collaboration_id) { + void ShareTabGroup(TabGroupId group_id, + std::string collaboration_id, + data_sharing::MemberRole member_role, + bool should_sign_in) { TabGroupSyncServiceImpl* service = static_cast<TabGroupSyncServiceImpl*>( TabGroupSyncServiceFactory::GetForProfile(browser()->profile())); service->MakeTabGroupSharedForTesting(group_id, collaboration_id); + + // Additional Properties. + const std::string display_name = "Display Name"; + const std::string email = "test@mail.com"; + const GURL avatar_url = GURL("chrome://newtab"); + const std::string given_name = "Given Name"; + const std::string access_token = "fake_access_token"; + const GaiaId gaia_id("fake_gaia_id"); + + GaiaId gaia_id_to_use = gaia_id; + if (should_sign_in) { + // Simulate a signed in primary account. + signin::IdentityManager* identity_manager = + IdentityManagerFactory::GetForProfile(browser()->profile()); + signin::MakePrimaryAccountAvailable(identity_manager, email, + signin::ConsentLevel::kSignin); + signin::MakePrimaryAccountAvailable(identity_manager, email, + signin::ConsentLevel::kSync); + CoreAccountInfo account_info = identity_manager->GetPrimaryAccountInfo( + signin::ConsentLevel::kSignin); + + gaia_id_to_use = account_info.gaia; + } + + data_sharing::GroupMember group_member = + data_sharing::GroupMember(gaia_id_to_use, display_name, email, + member_role, avatar_url, given_name); + data_sharing::GroupData group_data = + data_sharing::GroupData(data_sharing::GroupId(collaboration_id), + display_name, {group_member}, access_token); + + data_sharing_service()->AddGroupDataForTesting(std::move(group_data)); + } + + data_sharing::DataSharingService* data_sharing_service() { + data_sharing::DataSharingService* data_sharing_service = + data_sharing::DataSharingServiceFactory::GetForProfile( + browser()->profile()); + return data_sharing_service; } private: @@ -109,7 +160,8 @@ IN_PROC_BROWSER_TEST_F(SharedTabGroupInteractiveUiTest, SharedTabGroupInAppMenu) { TabGroupId group_id = CreateNewTabGroup(); - ShareTabGroup(group_id, "fake_collaboration_id"); + ShareTabGroup(group_id, "fake_collaboration_id", + data_sharing::MemberRole::kOwner, /*should_sign_in=*/false); RunTestSequence(WaitForShow(kTabGroupHeaderElementId), FinishTabstripAnimations(), ShowBookmarksBar(), @@ -130,7 +182,8 @@ IN_PROC_BROWSER_TEST_F(SharedTabGroupInteractiveUiTest, SharedTabGroupInEverythingMenu) { TabGroupId group_id = CreateNewTabGroup(); - ShareTabGroup(group_id, "fake_collaboration_id"); + ShareTabGroup(group_id, "fake_collaboration_id", + data_sharing::MemberRole::kOwner, /*should_sign_in=*/false); RunTestSequence( WaitForShow(kTabGroupHeaderElementId), FinishTabstripAnimations(), @@ -150,7 +203,8 @@ const char kTabGroupHeaderToScreenshot[] = "Tab group header to hover"; TabGroupId group_id = CreateNewTabGroup(); - ShareTabGroup(group_id, "fake_collaboration_id"); + ShareTabGroup(group_id, "fake_collaboration_id", + data_sharing::MemberRole::kOwner, /*should_sign_in=*/false); // TODO(crbug.com/380088920): Manually trigger a layout until we have a way to // know when the entity tracker is initialized. @@ -174,7 +228,8 @@ base::HistogramTester histogram_tester; TabGroupId group_id = CreateNewTabGroup(); - ShareTabGroup(group_id, "fake_collaboration_id"); + ShareTabGroup(group_id, "fake_collaboration_id", + data_sharing::MemberRole::kOwner, /*should_sign_in=*/false); RunTestSequence(WaitForShow(kTabGroupHeaderElementId), FinishTabstripAnimations(), HoverTabGroupHeader(group_id), @@ -196,7 +251,8 @@ ::base::HistogramTester histogram_tester; TabGroupId group_id = CreateNewTabGroup(); - ShareTabGroup(group_id, "fake_collaboration_id"); + ShareTabGroup(group_id, "fake_collaboration_id", + data_sharing::MemberRole::kOwner, /*should_sign_in=*/false); // Close the tab group. browser()->tab_strip_model()->CloseAllTabsInGroup(group_id); @@ -220,7 +276,8 @@ ::base::HistogramTester histogram_tester; TabGroupId group_id = CreateNewTabGroup(); - ShareTabGroup(group_id, "fake_collaboration_id"); + ShareTabGroup(group_id, "fake_collaboration_id", + data_sharing::MemberRole::kOwner, /*should_sign_in=*/false); // Close the tab group. browser()->tab_strip_model()->CloseAllTabsInGroup(group_id); @@ -246,7 +303,8 @@ ::base::HistogramTester histogram_tester; TabGroupId group_id = CreateNewTabGroup(); - ShareTabGroup(group_id, "fake_collaboration_id"); + ShareTabGroup(group_id, "fake_collaboration_id", + data_sharing::MemberRole::kOwner, /*should_sign_in=*/false); // Close the tab group. browser()->tab_strip_model()->CloseAllTabsInGroup(group_id); @@ -276,7 +334,6 @@ ::base::HistogramTester histogram_tester; TabGroupId group_id = CreateNewTabGroup(); - // ShareTabGroup(group_id, "fake_collaboration_id"); RunTestSequence( WaitForShow(kTabGroupHeaderElementId), FinishTabstripAnimations(), @@ -301,7 +358,8 @@ ::base::HistogramTester histogram_tester; TabGroupId group_id = CreateNewTabGroup(); - ShareTabGroup(group_id, "fake_collaboration_id"); + ShareTabGroup(group_id, "fake_collaboration_id", + data_sharing::MemberRole::kOwner, /*should_sign_in=*/false); RunTestSequence( WaitForShow(kTabGroupHeaderElementId), FinishTabstripAnimations(), @@ -326,7 +384,8 @@ ::base::HistogramTester histogram_tester; TabGroupId group_id = CreateNewTabGroup(); - ShareTabGroup(group_id, "fake_collaboration_id"); + ShareTabGroup(group_id, "fake_collaboration_id", + data_sharing::MemberRole::kOwner, /*should_sign_in=*/false); RunTestSequence( WaitForShow(kTabGroupHeaderElementId), FinishTabstripAnimations(), @@ -343,4 +402,22 @@ 1); } +// Verify members see the leave group button instead of the delete button and +// that pressing the leave group buttons deletes the group. +IN_PROC_BROWSER_TEST_F(SharedTabGroupInteractiveUiTest, LeaveGroupPressed) { + TabGroupId group_id = CreateNewTabGroup(); + ShareTabGroup(group_id, "fake_collaboration_id", + data_sharing::MemberRole::kMember, /*should_sign_in=*/true); + + RunTestSequence( + WaitForShow(kTabGroupHeaderElementId), FinishTabstripAnimations(), + HoverTabGroupHeader(group_id), ClickMouse(ui_controls::RIGHT), + WaitForShow(kTabGroupEditorBubbleId), + PressButton(kTabGroupEditorBubbleLeaveGroupButtonId), + WaitForHide(kTabGroupEditorBubbleLeaveGroupButtonId), + WaitForShow(kDeletionDialogOkButtonId), + PressButton(kDeletionDialogOkButtonId), + WaitForHide(kTabGroupHeaderElementId), FinishTabstripAnimations()); +} + } // namespace tab_groups
diff --git a/chrome/browser/ui/views/frame/tab_strip_region_view.cc b/chrome/browser/ui/views/frame/tab_strip_region_view.cc index 515a199..27394f18 100644 --- a/chrome/browser/ui/views/frame/tab_strip_region_view.cc +++ b/chrome/browser/ui/views/frame/tab_strip_region_view.cc
@@ -577,6 +577,9 @@ // account. const auto border_insets = gfx::Insets::TLBR(top_inset, 0, bottom_inset, 0); if (tab_strip_combo_button_) { + if (tab_strip_action_container_) { + tab_strip_action_container_->UpdateButtonBorders(border_insets); + } tab_strip_combo_button_->new_tab_button()->SetBorder( views::CreateEmptyBorder(border_insets)); tab_strip_combo_button_->tab_search_button()->SetBorder(
diff --git a/chrome/browser/ui/views/frame/tab_strip_region_view_interactive_uitest.cc b/chrome/browser/ui/views/frame/tab_strip_region_view_interactive_uitest.cc index 1d13459..129d67fc 100644 --- a/chrome/browser/ui/views/frame/tab_strip_region_view_interactive_uitest.cc +++ b/chrome/browser/ui/views/frame/tab_strip_region_view_interactive_uitest.cc
@@ -113,10 +113,10 @@ EXPECT_TRUE(tab_2->HasFocus()); move_forward_over_tab(tab_2); - EXPECT_TRUE(new_tab_button()->HasFocus()); + EXPECT_TRUE(tab_search_button()->HasFocus()); press_right(); - EXPECT_TRUE(tab_search_button()->HasFocus()); + EXPECT_TRUE(new_tab_button()->HasFocus()); // Focus should cycle back around to tab_0. press_right(); @@ -153,10 +153,10 @@ // Pressing left should immediately cycle back around to the last button. press_left(); - EXPECT_TRUE(tab_search_button()->HasFocus()); + EXPECT_TRUE(new_tab_button()->HasFocus()); press_left(); - EXPECT_TRUE(new_tab_button()->HasFocus()); + EXPECT_TRUE(tab_search_button()->HasFocus()); move_back_to_tab(tab_2); EXPECT_TRUE(tab_2->HasFocus()); @@ -186,12 +186,12 @@ #if !BUILDFLAG(IS_WIN) EXPECT_TRUE(tab_strip_region_view()->AcceleratorPressed( tab_strip_region_view()->end_key())); - EXPECT_TRUE(new_tab_button()->HasFocus()); + EXPECT_TRUE(tab_search_button()->HasFocus()); #endif // !BUILDFLAG(IS_WIN) EXPECT_TRUE(tab_strip_region_view()->AcceleratorPressed( tab_strip_region_view()->home_key())); - EXPECT_TRUE(tab_search_button()->HasFocus()); + EXPECT_TRUE(new_tab_button()->HasFocus()); } else { // The first tab should be active. @@ -200,7 +200,7 @@ #if !BUILDFLAG(IS_WIN) EXPECT_TRUE(tab_strip_region_view()->AcceleratorPressed( tab_strip_region_view()->end_key())); - EXPECT_TRUE(tab_search_button()->HasFocus()); + EXPECT_TRUE(new_tab_button()->HasFocus()); #endif // !BUILDFLAG(IS_WIN) EXPECT_TRUE(tab_strip_region_view()->AcceleratorPressed(
diff --git a/chrome/browser/ui/views/tabs/tab_group_editor_bubble_view.cc b/chrome/browser/ui/views/tabs/tab_group_editor_bubble_view.cc index f8c74f6..1e740ad 100644 --- a/chrome/browser/ui/views/tabs/tab_group_editor_bubble_view.cc +++ b/chrome/browser/ui/views/tabs/tab_group_editor_bubble_view.cc
@@ -345,7 +345,12 @@ menu_items_.push_back(AddChildView(BuildHideGroupButton())); AddChildView(BuildSeparator()); - menu_items_.push_back(AddChildView(BuildDeleteGroupButton())); + + if (OwnsGroup()) { + menu_items_.push_back(AddChildView(BuildDeleteGroupButton())); + } else { + menu_items_.push_back(AddChildView(BuildLeaveGroupButton())); + } if (ShouldShowSavedFooter()) { footer_ = AddChildView(std::make_unique<Footer>(browser_)); @@ -497,6 +502,27 @@ } std::unique_ptr<views::LabelButton> +TabGroupEditorBubbleView::BuildLeaveGroupButton() { + std::unique_ptr<views::LabelButton> leave_group_menu_item = CreateMenuItem( + TAB_GROUP_HEADER_CXMENU_LEAVE_GROUP, + l10n_util::GetStringUTF16( + IDS_DATA_SHARING_MEMBER_DELETE_LAST_TAB_CONFIRM), + base::BindRepeating(&TabGroupEditorBubbleView::LeaveGroupPressed, + base::Unretained(this)), + ui::ImageModel::FromVectorIcon(kTrashCanRefreshIcon, ui::kColorMenuIcon, + kDefaultIconSize)); + + leave_group_menu_item->SetProperty(views::kElementIdentifierKey, + kTabGroupEditorBubbleLeaveGroupButtonId); + if (ShouldShowSavedFooter()) { + leave_group_menu_item->SetProperty( + views::kMarginsKey, gfx::Insets::TLBR(0, 0, kSeparatorPadding, 0)); + } + + return leave_group_menu_item; +} + +std::unique_ptr<views::LabelButton> TabGroupEditorBubbleView::BuildMoveGroupToNewWindowButton() { return CreateMenuItem( TAB_GROUP_HEADER_CXMENU_MOVE_GROUP_TO_NEW_WINDOW, @@ -635,22 +661,8 @@ return true; } - std::optional<tab_groups::CollaborationId> collaboration_id = - maybe_saved_group->collaboration_id(); - if (!collaboration_id) { - return true; - } - - collaboration::CollaborationService* collaboration_service = - collaboration::CollaborationServiceFactory::GetForProfile( - browser_->profile()); - if (!collaboration_service) { - return true; - } - - data_sharing::GroupId group_id(collaboration_id->value()); - return data_sharing::MemberRole::kOwner == - collaboration_service->GetCurrentUserRoleForGroup(group_id); + return tab_groups::SavedTabGroupUtils::IsOwnerOfSharedTabGroup( + browser_->profile(), maybe_saved_group->saved_guid()); } bool TabGroupEditorBubbleView::IsGroupSaved() const { @@ -844,6 +856,23 @@ GetWidget()->Close(); } +void TabGroupEditorBubbleView::LeaveGroupPressed() { + tab_groups::TabGroupSyncService* tab_group_service = + tab_groups::SavedTabGroupUtils::GetServiceForProfile(browser_->profile()); + if (!tab_group_service) { + return; + } + + std::optional<tab_groups::SavedTabGroup> saved_group = + tab_group_service->GetGroup(group_); + if (!saved_group) { + return; + } + + tab_groups::SavedTabGroupUtils::LeaveSharedGroup(browser_, + saved_group->saved_guid()); +} + void TabGroupEditorBubbleView::DeleteGroupFromTabstrip() { TabStripModel* const model = browser_->tab_strip_model(); const int num_tabs_in_group =
diff --git a/chrome/browser/ui/views/tabs/tab_group_editor_bubble_view.h b/chrome/browser/ui/views/tabs/tab_group_editor_bubble_view.h index fcfe03d..7e0bdb9 100644 --- a/chrome/browser/ui/views/tabs/tab_group_editor_bubble_view.h +++ b/chrome/browser/ui/views/tabs/tab_group_editor_bubble_view.h
@@ -47,7 +47,8 @@ static constexpr int TAB_GROUP_HEADER_CXMENU_SHARE = 5; static constexpr int TAB_GROUP_HEADER_CXMENU_CLOSE_GROUP = 6; static constexpr int TAB_GROUP_HEADER_CXMENU_DELETE_GROUP = 7; - static constexpr int TAB_GROUP_HEADER_CXMENU_MOVE_GROUP_TO_NEW_WINDOW = 8; + static constexpr int TAB_GROUP_HEADER_CXMENU_LEAVE_GROUP = 8; + static constexpr int TAB_GROUP_HEADER_CXMENU_MOVE_GROUP_TO_NEW_WINDOW = 9; using Colors = std::vector<std::pair<tab_groups::TabGroupColorId, std::u16string>>; @@ -103,6 +104,7 @@ std::unique_ptr<views::LabelButton> BuildUngroupButton(); std::unique_ptr<views::LabelButton> BuildHideGroupButton(); std::unique_ptr<views::LabelButton> BuildDeleteGroupButton(); + std::unique_ptr<views::LabelButton> BuildLeaveGroupButton(); std::unique_ptr<views::LabelButton> BuildMoveGroupToNewWindowButton(); std::unique_ptr<views::LabelButton> BuildManageSharedGroupButton(); std::unique_ptr<views::LabelButton> BuildShareGroupButton(); @@ -113,6 +115,7 @@ void ShareOrManagePressed(); void HideGroupPressed(); void DeleteGroupPressed(); + void LeaveGroupPressed(); void MoveGroupToNewWindowPressed(); // The action for moving a group to a new window is only enabled when the
diff --git a/chrome/browser/ui/views/tabs/tab_strip_action_container.cc b/chrome/browser/ui/views/tabs/tab_strip_action_container.cc index 995b6c7..cf521fa1 100644 --- a/chrome/browser/ui/views/tabs/tab_strip_action_container.cc +++ b/chrome/browser/ui/views/tabs/tab_strip_action_container.cc
@@ -610,5 +610,21 @@ animation_session_->ApplyAnimationValue(animation); } +void TabStripActionContainer::UpdateButtonBorders( + const gfx::Insets border_insets) { + if (auto_tab_group_button_) { + auto_tab_group_button_->SetBorder(views::CreateEmptyBorder(border_insets)); + } + if (tab_declutter_button_) { + tab_declutter_button_->SetBorder(views::CreateEmptyBorder(border_insets)); + } + if (product_specifications_button_) { + product_specifications_button_->SetBorder( + views::CreateEmptyBorder(border_insets)); + } + if (glic_nudge_button_) { + glic_nudge_button_->SetBorder(views::CreateEmptyBorder(border_insets)); + } +} BEGIN_METADATA(TabStripActionContainer) END_METADATA
diff --git a/chrome/browser/ui/views/tabs/tab_strip_action_container.h b/chrome/browser/ui/views/tabs/tab_strip_action_container.h index ef61dc1..883ad38 100644 --- a/chrome/browser/ui/views/tabs/tab_strip_action_container.h +++ b/chrome/browser/ui/views/tabs/tab_strip_action_container.h
@@ -19,6 +19,9 @@ #include "ui/views/mouse_watcher.h" #include "ui/views/view.h" +namespace gfx { +class Insets; +} namespace glic { class GlicButton; } @@ -114,6 +117,8 @@ // GlicNudgeObserver void OnTriggerGlicNudgeUI(std::string label) override; + void UpdateButtonBorders(gfx::Insets button_insets); + private: friend class TabStripActionContainerBrowserTest;
diff --git a/chrome/browser/ui/views/tabs/tab_strip_combo_button.cc b/chrome/browser/ui/views/tabs/tab_strip_combo_button.cc index 7414003..1cade64 100644 --- a/chrome/browser/ui/views/tabs/tab_strip_combo_button.cc +++ b/chrome/browser/ui/views/tabs/tab_strip_combo_button.cc
@@ -49,7 +49,7 @@ TabStrip* tab_strip) { Edge new_tab_button_flat_edge = Edge::kNone; if (features::HasTabstripComboButtonWithBackground()) { - new_tab_button_flat_edge = base::i18n::IsRTL() ? Edge::kLeft : Edge::kRight; + new_tab_button_flat_edge = base::i18n::IsRTL() ? Edge::kRight : Edge::kLeft; } std::unique_ptr<TabStripControlButton> new_tab_button = std::make_unique<TabStripControlButton>( @@ -70,9 +70,9 @@ new_tab_button->SetBackgroundFrameInactiveColorId( kColorNewTabButtonCRBackgroundFrameInactive); } else { - // Add a gap between the new tab button and tab search container. + // Add a gap between the new tab button and tab search button. new_tab_button->SetProperty( - views::kMarginsKey, gfx::Insets::TLBR(0, 0, 0, kButtonGapNoBackground)); + views::kMarginsKey, gfx::Insets::TLBR(0, kButtonGapNoBackground, 0, 0)); } new_tab_button->SetTooltipText( @@ -106,7 +106,7 @@ Edge tab_search_button_flat_edge = Edge::kNone; if (features::HasTabstripComboButtonWithBackground()) { tab_search_button_flat_edge = - base::i18n::IsRTL() ? Edge::kRight : Edge::kLeft; + base::i18n::IsRTL() ? Edge::kLeft : Edge::kRight; } std::unique_ptr<TabSearchButton> tab_search_button = std::make_unique<TabSearchButton>(tab_strip->controller(), browser, @@ -128,9 +128,9 @@ .SetCrossAxisAlignment(views::LayoutAlignment::kCenter); separator_container->SetCanProcessEventsWithinSubtree(false); - new_tab_button_ = button_container->AddChildView(std::move(new_tab_button)); tab_search_button_ = button_container->AddChildView(std::move(tab_search_button)); + new_tab_button_ = button_container->AddChildView(std::move(new_tab_button)); separator_ = separator_container->AddChildView(std::move(separator)); SetLayoutManager(std::make_unique<views::FillLayout>());
diff --git a/chrome/browser/ui/webui/data_sharing/data_sharing_ui.cc b/chrome/browser/ui/webui/data_sharing/data_sharing_ui.cc index 39872508..4419823 100644 --- a/chrome/browser/ui/webui/data_sharing/data_sharing_ui.cc +++ b/chrome/browser/ui/webui/data_sharing/data_sharing_ui.cc
@@ -4,6 +4,7 @@ #include "chrome/browser/ui/webui/data_sharing/data_sharing_ui.h" +#include "chrome/browser/metrics/chrome_metrics_service_accessor.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/webui/data_sharing/data_sharing_page_handler.h" #include "chrome/common/webui_url_constants.h" @@ -159,6 +160,9 @@ {"ownerCannotShare", IDS_DATA_SHARING_OWNER_CANNOT_SHARE}, }; source->AddLocalizedStrings(kStrings); + source->AddBoolean( + "metricsReportingEnabled", + ChromeMetricsServiceAccessor::IsMetricsAndCrashReportingEnabled()); } DataSharingUI::~DataSharingUI() = default;
diff --git a/chrome/browser/ui/webui/glic/glic.mojom b/chrome/browser/ui/webui/glic/glic.mojom index 85a6785..7a601ee0 100644 --- a/chrome/browser/ui/webui/glic/glic.mojom +++ b/chrome/browser/ui/webui/glic/glic.mojom
@@ -85,6 +85,14 @@ // Closes the Glic panel. ClosePanel(); + // Requests that the web client's panel be attached/docked to a browser + // window. + AttachPanel(); + + // Requests that the web client's panel be detached/undocked from a browser + // window (floats free). + DetachPanel(); + // Returns the context from the currently active tab. // `tab_context_result` is null if tab content could not be captured. // This may fail if the tab is navigated while collecting data, or closed
diff --git a/chrome/browser/ui/webui/glic/glic_page_handler.cc b/chrome/browser/ui/webui/glic/glic_page_handler.cc index 0568f6c..a09597cb 100644 --- a/chrome/browser/ui/webui/glic/glic_page_handler.cc +++ b/chrome/browser/ui/webui/glic/glic_page_handler.cc
@@ -95,6 +95,10 @@ void ClosePanel() override { glic_service_->ClosePanel(); } + void AttachPanel() override { glic_service_->AttachPanel(); } + + void DetachPanel() override { glic_service_->DetachPanel(); } + void ResizeWidget(const gfx::Size& size, ResizeWidgetCallback callback) override { std::optional<gfx::Size> actual_size = glic_service_->ResizePanel(size);
diff --git a/chrome/browser/web_applications/isolated_web_apps/isolated_web_app_update_manager_unittest.cc b/chrome/browser/web_applications/isolated_web_apps/isolated_web_app_update_manager_unittest.cc index 2b244e4..9c09df0 100644 --- a/chrome/browser/web_applications/isolated_web_apps/isolated_web_app_update_manager_unittest.cc +++ b/chrome/browser/web_applications/isolated_web_apps/isolated_web_app_update_manager_unittest.cc
@@ -777,8 +777,16 @@ AssertAppInstalledAtVersion(GetIwa2WebBundleId(), base::Version("2.2.0")); } +// TODO(crbug.com/389137516): Flaky on ChromeOS MSAN. +#if BUILDFLAG(IS_CHROMEOS) && defined(MEMORY_SANITIZER) +#define MAYBE_StopsNonStartedUpdateDiscoveryTasksIfIwaIsUninstalled \ + DISABLED_StopsNonStartedUpdateDiscoveryTasksIfIwaIsUninstalled +#else +#define MAYBE_StopsNonStartedUpdateDiscoveryTasksIfIwaIsUninstalled \ + StopsNonStartedUpdateDiscoveryTasksIfIwaIsUninstalled +#endif TEST_F(IsolatedWebAppUpdateManagerUpdateTest, - StopsNonStartedUpdateDiscoveryTasksIfIwaIsUninstalled) { + MAYBE_StopsNonStartedUpdateDiscoveryTasksIfIwaIsUninstalled) { url_loader_factory().ClearResponses(); InitialIwaBundleForceInstall(CreateIwa1Bundle("1.0.0"));
diff --git a/chrome/build/android-arm32.pgo.txt b/chrome/build/android-arm32.pgo.txt index 390ccf3..c3ace056 100644 --- a/chrome/build/android-arm32.pgo.txt +++ b/chrome/build/android-arm32.pgo.txt
@@ -1 +1 @@ -chrome-android32-main-1736531933-228bc904ebf407e6f5265665948b4adbd3bdf254-ffa032aa746954106635699e59f2cf3b356a49fe.profdata +chrome-android32-main-1736553506-c974b6e7ba1fc9665f6a6bc588a2e87d4e9ba2bd-3ea9782a31e18d48e1d9d373be610264a839a766.profdata
diff --git a/chrome/build/android-arm64.pgo.txt b/chrome/build/android-arm64.pgo.txt index c0c1f871a..3b613b2 100644 --- a/chrome/build/android-arm64.pgo.txt +++ b/chrome/build/android-arm64.pgo.txt
@@ -1 +1 @@ -chrome-android64-main-1736531933-0bb86366eacf9bb8b754fea33cab7b57025fa8d3-ffa032aa746954106635699e59f2cf3b356a49fe.profdata +chrome-android64-main-1736550699-b50c3195a8b0965cd68e8c7742e4092ed8987185-d9898f40a0d24fdeec60134cba73577be45aefd7.profdata
diff --git a/chrome/build/mac-arm.pgo.txt b/chrome/build/mac-arm.pgo.txt index 51114ea..554314d 100644 --- a/chrome/build/mac-arm.pgo.txt +++ b/chrome/build/mac-arm.pgo.txt
@@ -1 +1 @@ -chrome-mac-arm-main-1736531933-6709b71c5ca7f5d7d4cb8ad775a7cd6c6d680236-ffa032aa746954106635699e59f2cf3b356a49fe.profdata +chrome-mac-arm-main-1736553506-3ced6ef36e3d6c833df218c8d49af85bd10cbc41-3ea9782a31e18d48e1d9d373be610264a839a766.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt index cdd1565..63bac15 100644 --- a/chrome/build/win32.pgo.txt +++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@ -chrome-win32-main-1736510306-f81a49cd21b328d70b48b3b859e44eafa159d030-92c2bb99b59079b19c3a40503b593091c01b9b4b.profdata +chrome-win32-main-1736531933-d7160254cfc0d9ca0d91bf9244b84acfbae9319f-ffa032aa746954106635699e59f2cf3b356a49fe.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt index 051db7f..bf3b63a 100644 --- a/chrome/build/win64.pgo.txt +++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@ -chrome-win64-main-1736510306-05734b56a6442882d0cb2bd7c563c12dfe215f87-92c2bb99b59079b19c3a40503b593091c01b9b4b.profdata +chrome-win64-main-1736531933-7b01ca2f5dff6eb381d060e00432958fd420ecc9-ffa032aa746954106635699e59f2cf3b356a49fe.profdata
diff --git a/chrome/common/extensions/api/file_browser_handlers/file_browser_handler_manifest_unittest.cc b/chrome/common/extensions/api/file_browser_handlers/file_browser_handler_manifest_unittest.cc index 4f33388..99393b1 100644 --- a/chrome/common/extensions/api/file_browser_handlers/file_browser_handler_manifest_unittest.cc +++ b/chrome/common/extensions/api/file_browser_handlers/file_browser_handler_manifest_unittest.cc
@@ -62,7 +62,7 @@ } TEST_F(FileBrowserHandlerManifestTest, InvalidFileBrowserHandlers) { - Testcase testcases[] = { + const Testcase testcases[] = { Testcase("filebrowser_invalid_access_permission.json", extensions::ErrorUtils::FormatErrorMessage( errors::kInvalidFileAccessValue, base::NumberToString(1))), @@ -88,7 +88,7 @@ Testcase("filebrowser_invalid_file_filters_url.json", extensions::ErrorUtils::FormatErrorMessage( errors::kInvalidURLPatternError, "http:*.html"))}; - RunTestcases(testcases, std::size(testcases), EXPECT_TYPE_ERROR); + RunTestcases(testcases, EXPECT_TYPE_ERROR); RunTestcase(Testcase("filebrowser_missing_permission.json", errors::kInvalidFileBrowserHandlerMissingPermission), EXPECT_TYPE_WARNING);
diff --git a/chrome/common/extensions/api/speech/tts_engine_manifest_handler_unittest.cc b/chrome/common/extensions/api/speech/tts_engine_manifest_handler_unittest.cc index 1a2b691..6eb5b1d 100644 --- a/chrome/common/extensions/api/speech/tts_engine_manifest_handler_unittest.cc +++ b/chrome/common/extensions/api/speech/tts_engine_manifest_handler_unittest.cc
@@ -23,7 +23,7 @@ base::StringPrintf(errors::kInvalidTtsBufferSizeRange, 1, media::limits::kMaxSamplesPerPacket); - Testcase testcases[] = { + const Testcase testcases[] = { Testcase("tts_engine_invalid_voices_1.json", errors::kInvalidTts), Testcase("tts_engine_invalid_voices_2.json", errors::kInvalidTtsVoices), Testcase("tts_engine_invalid_voices_3.json", errors::kInvalidTtsVoices), @@ -56,7 +56,7 @@ Testcase("tts_engine_invalid_buffer_size_4.json", errors::kInvalidTtsRequiresSampleRateAndBufferSize), }; - RunTestcases(testcases, std::size(testcases), EXPECT_TYPE_ERROR); + RunTestcases(testcases, EXPECT_TYPE_ERROR); LoadAndExpectSuccess("tts_engine_valid_voices.json"); LoadAndExpectSuccess("tts_engine_valid_sample_rate_buffer_size.json");
diff --git a/chrome/common/extensions/manifest_handlers/content_scripts_manifest_unittest.cc b/chrome/common/extensions/manifest_handlers/content_scripts_manifest_unittest.cc index dda4bd5..441ca07 100644 --- a/chrome/common/extensions/manifest_handlers/content_scripts_manifest_unittest.cc +++ b/chrome/common/extensions/manifest_handlers/content_scripts_manifest_unittest.cc
@@ -26,7 +26,7 @@ }; TEST_F(ContentScriptsManifestTest, MatchPattern) { - Testcase testcases[] = { + const Testcase testcases[] = { // chrome:// urls are not allowed. Testcase("content_script_invalid_match_chrome_url.json", ErrorUtils::FormatErrorMessage( @@ -56,7 +56,7 @@ "Error at key 'content_scripts'. Parsing array failed at index " "0: Error at key 'matches': Parsing array failed at index 0: " "expected string, got integer")}; - RunTestcases(testcases, std::size(testcases), EXPECT_TYPE_ERROR); + RunTestcases(testcases, EXPECT_TYPE_ERROR); LoadAndExpectSuccess("ports_in_content_scripts.json"); }
diff --git a/chrome/common/extensions/manifest_handlers/exclude_matches_manifest_unittest.cc b/chrome/common/extensions/manifest_handlers/exclude_matches_manifest_unittest.cc index e293ca5..5236aaf6 100644 --- a/chrome/common/extensions/manifest_handlers/exclude_matches_manifest_unittest.cc +++ b/chrome/common/extensions/manifest_handlers/exclude_matches_manifest_unittest.cc
@@ -12,20 +12,18 @@ }; TEST_F(ExcludeMatchesManifestTest, ExcludeMatchPatterns) { - Testcase testcases[] = { - Testcase("exclude_matches.json"), - Testcase("exclude_matches_empty.json") - }; - RunTestcases(testcases, std::size(testcases), EXPECT_TYPE_SUCCESS); + const Testcase testcases[] = {Testcase("exclude_matches.json"), + Testcase("exclude_matches_empty.json")}; + RunTestcases(testcases, EXPECT_TYPE_SUCCESS); - Testcase testcases2[] = { + const Testcase testcases2[] = { Testcase("exclude_matches_not_list.json", "Error at key 'content_scripts'. Parsing array failed at index " "0: 'exclude_matches': expected list, got string"), Testcase("exclude_matches_invalid_host.json", "Invalid value for 'content_scripts[0].exclude_matches[0]': " "Invalid host wildcard.")}; - RunTestcases(testcases2, std::size(testcases2), EXPECT_TYPE_ERROR); + RunTestcases(testcases2, EXPECT_TYPE_ERROR); } } // namespace extensions
diff --git a/chrome/common/extensions/manifest_tests/extension_manifests_contentsecuritypolicy_unittest.cc b/chrome/common/extensions/manifest_tests/extension_manifests_contentsecuritypolicy_unittest.cc index 966e871..dfeb201 100644 --- a/chrome/common/extensions/manifest_tests/extension_manifests_contentsecuritypolicy_unittest.cc +++ b/chrome/common/extensions/manifest_tests/extension_manifests_contentsecuritypolicy_unittest.cc
@@ -14,7 +14,7 @@ using ContentSecurityPolicyManifestTest = ChromeManifestTest; TEST_F(ContentSecurityPolicyManifestTest, InsecureContentSecurityPolicy) { - Testcase testcases[] = { + const Testcase testcases[] = { Testcase("insecure_contentsecuritypolicy_1.json", ErrorUtils::FormatErrorMessage( errors::kInvalidCSPInsecureValueIgnored, @@ -29,5 +29,5 @@ ErrorUtils::FormatErrorMessage( errors::kInvalidCSPMissingSecureSrc, keys::kContentSecurityPolicy, "object-src"))}; - RunTestcases(testcases, std::size(testcases), EXPECT_TYPE_WARNING); + RunTestcases(testcases, EXPECT_TYPE_WARNING); }
diff --git a/chrome/common/extensions/manifest_tests/extension_manifests_initvalue_unittest.cc b/chrome/common/extensions/manifest_tests/extension_manifests_initvalue_unittest.cc index 6eec22c..c9dfc7d6 100644 --- a/chrome/common/extensions/manifest_tests/extension_manifests_initvalue_unittest.cc +++ b/chrome/common/extensions/manifest_tests/extension_manifests_initvalue_unittest.cc
@@ -35,7 +35,7 @@ TEST_F(InitValueManifestTest, InitFromValueInvalid) { SimpleFeature::ScopedThreadUnsafeAllowlistForTest allowlist(kAllowlistID); - Testcase testcases[] = { + const Testcase testcases[] = { Testcase("init_invalid_version_missing.json", errors::kInvalidVersion), Testcase("init_invalid_version_invalid.json", errors::kInvalidVersion), Testcase("init_invalid_version_name_invalid.json", @@ -109,7 +109,7 @@ Testcase("init_invalid_short_name_type.json", errors::kInvalidShortName), }; - RunTestcases(testcases, std::size(testcases), EXPECT_TYPE_ERROR); + RunTestcases(testcases, EXPECT_TYPE_ERROR); } TEST_F(InitValueManifestTest, InitFromValueValid) { @@ -150,28 +150,27 @@ EXPECT_EQ("1.0.0.0", extension->VersionString()); EXPECT_EQ("1.0 alpha", extension->GetVersionForDisplay()); - Testcase testcases[] = { - // Test with a minimum_chrome_version. - Testcase("init_valid_minimum_chrome.json"), + const Testcase testcases[] = { + // Test with a minimum_chrome_version. + Testcase("init_valid_minimum_chrome.json"), - // Test a hosted app with a minimum_chrome_version. - Testcase("init_valid_app_minimum_chrome.json"), + // Test a hosted app with a minimum_chrome_version. + Testcase("init_valid_app_minimum_chrome.json"), - // Test a hosted app with a requirements section. - Testcase("init_valid_app_requirements.json"), + // Test a hosted app with a requirements section. + Testcase("init_valid_app_requirements.json"), - // Test a theme with a minimum_chrome_version. - Testcase("init_valid_theme_minimum_chrome.json"), + // Test a theme with a minimum_chrome_version. + Testcase("init_valid_theme_minimum_chrome.json"), - // Verify empty permission settings are considered valid. - Testcase("init_valid_permissions_empty.json"), + // Verify empty permission settings are considered valid. + Testcase("init_valid_permissions_empty.json"), - // We allow unknown API permissions, so this will be valid until we better - // distinguish between API and host permissions. - Testcase("init_valid_permissions_unknown.json") - }; + // We allow unknown API permissions, so this will be valid until we better + // distinguish between API and host permissions. + Testcase("init_valid_permissions_unknown.json")}; - RunTestcases(testcases, std::size(testcases), EXPECT_TYPE_SUCCESS); + RunTestcases(testcases, EXPECT_TYPE_SUCCESS); } TEST_F(InitValueManifestTest, InitFromValueValidNameInRTL) {
diff --git a/chrome/common/extensions/manifest_tests/extension_manifests_launch_unittest.cc b/chrome/common/extensions/manifest_tests/extension_manifests_launch_unittest.cc index 806ee69..bd15bda 100644 --- a/chrome/common/extensions/manifest_tests/extension_manifests_launch_unittest.cc +++ b/chrome/common/extensions/manifest_tests/extension_manifests_launch_unittest.cc
@@ -38,70 +38,57 @@ extension = LoadAndExpectSuccess("launch_height.json"); EXPECT_EQ(480, AppLaunchInfo::GetLaunchHeight(extension.get())); - Testcase testcases[] = { - Testcase("launch_window.json", errors::kInvalidLaunchContainer), - Testcase("launch_container_invalid_type.json", - errors::kInvalidLaunchContainer), - Testcase("launch_container_invalid_value.json", - errors::kInvalidLaunchContainer), - Testcase("launch_container_without_launch_url.json", - errors::kLaunchURLRequired), - Testcase("launch_width_invalid.json", - ErrorUtils::FormatErrorMessage( - errors::kInvalidLaunchValueContainer, - keys::kLaunchWidth)), - Testcase("launch_width_negative.json", - ErrorUtils::FormatErrorMessage( - errors::kInvalidLaunchValue, - keys::kLaunchWidth)), - Testcase("launch_height_invalid.json", - ErrorUtils::FormatErrorMessage( - errors::kInvalidLaunchValueContainer, - keys::kLaunchHeight)), - Testcase("launch_height_negative.json", - ErrorUtils::FormatErrorMessage( - errors::kInvalidLaunchValue, - keys::kLaunchHeight)) - }; - RunTestcases(testcases, std::size(testcases), EXPECT_TYPE_ERROR); + const Testcase testcases[] = { + Testcase("launch_window.json", errors::kInvalidLaunchContainer), + Testcase("launch_container_invalid_type.json", + errors::kInvalidLaunchContainer), + Testcase("launch_container_invalid_value.json", + errors::kInvalidLaunchContainer), + Testcase("launch_container_without_launch_url.json", + errors::kLaunchURLRequired), + Testcase("launch_width_invalid.json", + ErrorUtils::FormatErrorMessage( + errors::kInvalidLaunchValueContainer, keys::kLaunchWidth)), + Testcase("launch_width_negative.json", + ErrorUtils::FormatErrorMessage(errors::kInvalidLaunchValue, + keys::kLaunchWidth)), + Testcase("launch_height_invalid.json", + ErrorUtils::FormatErrorMessage( + errors::kInvalidLaunchValueContainer, keys::kLaunchHeight)), + Testcase("launch_height_negative.json", + ErrorUtils::FormatErrorMessage(errors::kInvalidLaunchValue, + keys::kLaunchHeight))}; + RunTestcases(testcases, EXPECT_TYPE_ERROR); } TEST_F(AppLaunchManifestTest, AppLaunchURL) { - Testcase testcases[] = { - Testcase("launch_path_and_url.json", - errors::kLaunchPathAndURLAreExclusive), - Testcase("launch_path_and_extent.json", - errors::kLaunchPathAndExtentAreExclusive), - Testcase("launch_path_invalid_type.json", - ErrorUtils::FormatErrorMessage( - errors::kInvalidLaunchValue, - keys::kLaunchLocalPath)), - Testcase("launch_path_invalid_value.json", - ErrorUtils::FormatErrorMessage( - errors::kInvalidLaunchValue, - keys::kLaunchLocalPath)), - Testcase("launch_path_invalid_localized.json", - ErrorUtils::FormatErrorMessage( - errors::kInvalidLaunchValue, - keys::kLaunchLocalPath)), - Testcase("launch_url_invalid_type_1.json", - ErrorUtils::FormatErrorMessage( - errors::kInvalidLaunchValue, - keys::kLaunchWebURL)), - Testcase("launch_url_invalid_type_2.json", - ErrorUtils::FormatErrorMessage( - errors::kInvalidLaunchValue, - keys::kLaunchWebURL)), - Testcase("launch_url_invalid_type_3.json", - ErrorUtils::FormatErrorMessage( - errors::kInvalidLaunchValue, - keys::kLaunchWebURL)), - Testcase("launch_url_invalid_localized.json", - ErrorUtils::FormatErrorMessage( - errors::kInvalidLaunchValue, - keys::kLaunchWebURL)) - }; - RunTestcases(testcases, std::size(testcases), EXPECT_TYPE_ERROR); + const Testcase testcases[] = { + Testcase("launch_path_and_url.json", + errors::kLaunchPathAndURLAreExclusive), + Testcase("launch_path_and_extent.json", + errors::kLaunchPathAndExtentAreExclusive), + Testcase("launch_path_invalid_type.json", + ErrorUtils::FormatErrorMessage(errors::kInvalidLaunchValue, + keys::kLaunchLocalPath)), + Testcase("launch_path_invalid_value.json", + ErrorUtils::FormatErrorMessage(errors::kInvalidLaunchValue, + keys::kLaunchLocalPath)), + Testcase("launch_path_invalid_localized.json", + ErrorUtils::FormatErrorMessage(errors::kInvalidLaunchValue, + keys::kLaunchLocalPath)), + Testcase("launch_url_invalid_type_1.json", + ErrorUtils::FormatErrorMessage(errors::kInvalidLaunchValue, + keys::kLaunchWebURL)), + Testcase("launch_url_invalid_type_2.json", + ErrorUtils::FormatErrorMessage(errors::kInvalidLaunchValue, + keys::kLaunchWebURL)), + Testcase("launch_url_invalid_type_3.json", + ErrorUtils::FormatErrorMessage(errors::kInvalidLaunchValue, + keys::kLaunchWebURL)), + Testcase("launch_url_invalid_localized.json", + ErrorUtils::FormatErrorMessage(errors::kInvalidLaunchValue, + keys::kLaunchWebURL))}; + RunTestcases(testcases, EXPECT_TYPE_ERROR); scoped_refptr<Extension> extension = LoadAndExpectSuccess("launch_local_path.json");
diff --git a/chrome/common/extensions/manifest_tests/extension_manifests_options_unittest.cc b/chrome/common/extensions/manifest_tests/extension_manifests_options_unittest.cc index 6bed579..c4a28c22 100644 --- a/chrome/common/extensions/manifest_tests/extension_manifests_options_unittest.cc +++ b/chrome/common/extensions/manifest_tests/extension_manifests_options_unittest.cc
@@ -75,7 +75,7 @@ extension = LoadAndExpectSuccess("platform_app_with_options_page.json"); EXPECT_TRUE(!OptionsPageInfo::HasOptionsPage(extension.get())); - Testcase testcases[] = { + const Testcase testcases[] = { // Forbid options page with relative URL in hosted apps. Testcase("hosted_app_relative_options.json", extensions::manifest_errors::kInvalidOptionsPageInHostedApp), @@ -88,7 +88,7 @@ Testcase( "packaged_app_absolute_options.json", extensions::manifest_errors::kInvalidOptionsPageExpectUrlInPackage)}; - RunTestcases(testcases, std::size(testcases), EXPECT_TYPE_ERROR); + RunTestcases(testcases, EXPECT_TYPE_ERROR); } // Tests for the options_ui.page manifest field. @@ -107,9 +107,9 @@ extension->id().c_str()), OptionsPageInfo::GetOptionsPage(extension.get()).spec()); - Testcase testcases[] = {Testcase("options_ui_page_bad_url.json", - "'page': expected page, got null")}; - RunTestcases(testcases, std::size(testcases), EXPECT_TYPE_WARNING); + RunTestcase(Testcase("options_ui_page_bad_url.json", + "'page': expected page, got null"), + EXPECT_TYPE_WARNING); } // Runs TestOptionsUIChromeStyleAndOpenInTab with and without the
diff --git a/chrome/common/extensions/manifest_tests/extension_manifests_platformapp_unittest.cc b/chrome/common/extensions/manifest_tests/extension_manifests_platformapp_unittest.cc index 57e4149..f941be57 100644 --- a/chrome/common/extensions/manifest_tests/extension_manifests_platformapp_unittest.cc +++ b/chrome/common/extensions/manifest_tests/extension_manifests_platformapp_unittest.cc
@@ -57,7 +57,7 @@ extension = LoadAndExpectSuccess("incognito_valid_platform_app.json"); EXPECT_FALSE(IncognitoInfo::IsSplitMode(extension.get())); - Testcase error_testcases[] = { + const Testcase error_testcases[] = { Testcase("init_invalid_platform_app_2.json", errors::kBackgroundRequiredForPlatformApps), Testcase("init_invalid_platform_app_3.json", @@ -65,9 +65,9 @@ errors::kInvalidManifestVersionUnsupported, "either 2 or 3", "apps")), }; - RunTestcases(error_testcases, std::size(error_testcases), EXPECT_TYPE_ERROR); + RunTestcases(error_testcases, EXPECT_TYPE_ERROR); - Testcase warning_testcases[] = { + const Testcase warning_testcases[] = { Testcase( "init_invalid_platform_app_1.json", "'app.launch' is only allowed for legacy packaged apps and hosted " @@ -77,8 +77,7 @@ "apps, " "but this is a packaged app."), }; - RunTestcases(warning_testcases, std::size(warning_testcases), - EXPECT_TYPE_WARNING); + RunTestcases(warning_testcases, EXPECT_TYPE_WARNING); LoadAndExpectWarnings( "init_invalid_platform_app_4.json", @@ -90,19 +89,17 @@ TEST_F(PlatformAppsManifestTest, PlatformAppContentSecurityPolicy) { // Normal platform apps can't specify a CSP value. - Testcase warning_testcases[] = { - Testcase( - "init_platform_app_csp_warning_1.json", - "'content_security_policy' is only allowed for extensions, legacy " - "packaged apps, and login screen extensions, but this is a " - "packaged app."), - Testcase( - "init_platform_app_csp_warning_2.json", - "'app.content_security_policy' is not allowed for specified extension " - "ID.") - }; - RunTestcases(warning_testcases, std::size(warning_testcases), - EXPECT_TYPE_WARNING); + const Testcase warning_testcases[] = { + Testcase( + "init_platform_app_csp_warning_1.json", + "'content_security_policy' is only allowed for extensions, legacy " + "packaged apps, and login screen extensions, but this is a " + "packaged app."), + Testcase("init_platform_app_csp_warning_2.json", + "'app.content_security_policy' is not allowed for specified " + "extension " + "ID.")}; + RunTestcases(warning_testcases, EXPECT_TYPE_WARNING); // Allowlisted ones can (this is the ID corresponding to the base 64 encoded // key in the init_platform_app_csp.json manifest.)
diff --git a/chrome/common/extensions/manifest_tests/extension_manifests_web_unittest.cc b/chrome/common/extensions/manifest_tests/extension_manifests_web_unittest.cc index 8089f0a..419b6f9 100644 --- a/chrome/common/extensions/manifest_tests/extension_manifests_web_unittest.cc +++ b/chrome/common/extensions/manifest_tests/extension_manifests_web_unittest.cc
@@ -15,7 +15,7 @@ namespace errors = extensions::manifest_errors; TEST_F(ChromeManifestTest, AppWebUrls) { - Testcase testcases[] = { + const Testcase testcases[] = { Testcase("web_urls_wrong_type.json", errors::kInvalidWebURLs), Testcase("web_urls_invalid_1.json", ErrorUtils::FormatErrorMessage(errors::kInvalidWebURL, @@ -38,7 +38,7 @@ ErrorUtils::FormatErrorMessage( errors::kInvalidWebURL, base::NumberToString(1), errors::kCannotClaimAllHostsInExtent))}; - RunTestcases(testcases, std::size(testcases), EXPECT_TYPE_ERROR); + RunTestcases(testcases, EXPECT_TYPE_ERROR); LoadAndExpectSuccess("web_urls_has_port.json");
diff --git a/chrome/services/speech/soda/mock_soda_client.h b/chrome/services/speech/soda/mock_soda_client.h index 0cd5c10..ffef5687 100644 --- a/chrome/services/speech/soda/mock_soda_client.h +++ b/chrome/services/speech/soda/mock_soda_client.h
@@ -32,6 +32,10 @@ int sample_rate, int channel_count), (override)); + MOCK_METHOD(void, + UpdateRecognitionContext, + (const RecognitionContext context), + (override)); MOCK_METHOD(bool, IsInitialized, (), (override)); MOCK_METHOD(bool, BinaryLoadedSuccessfully, (), (override)); };
diff --git a/chrome/services/speech/soda/soda_async_impl.h b/chrome/services/speech/soda/soda_async_impl.h index ee2c726..614180a5 100644 --- a/chrome/services/speech/soda/soda_async_impl.h +++ b/chrome/services/speech/soda/soda_async_impl.h
@@ -56,6 +56,14 @@ void* callback_handle; } SerializedSodaConfig; +typedef struct { + // A RecognitionContext serialized as a string. + const char* recognition_context; + + // Length of char* in recognition_context. + int recognition_context_size; +} RecognitionContext; + void* CreateSoda(SerializedSodaConfig config); // Destroys the instance of SODA, called on the destruction of the SodaClient. @@ -65,6 +73,10 @@ void AddAudio(void* soda_async_handle, const char* audio_buffer, int audio_buffer_size); + +// Updates the recognition context. +void UpdateRecognitionContext(void* soda_async_handle, + RecognitionContext context); } #endif // CHROME_SERVICES_SPEECH_SODA_SODA_ASYNC_IMPL_H_
diff --git a/chrome/services/speech/soda/soda_client.h b/chrome/services/speech/soda/soda_client.h index 2bd5d7b..60f8293 100644 --- a/chrome/services/speech/soda/soda_client.h +++ b/chrome/services/speech/soda/soda_client.h
@@ -43,6 +43,9 @@ int sample_rate, int channel_count) = 0; + // Updates the recognition context for the current SODA instance. + virtual void UpdateRecognitionContext(const RecognitionContext context) = 0; + // Returns a flag indicating whether the client has been initialized. virtual bool IsInitialized() = 0;
diff --git a/chrome/services/speech/soda/soda_client_impl.cc b/chrome/services/speech/soda/soda_client_impl.cc index d356525e..cc1f413 100644 --- a/chrome/services/speech/soda/soda_client_impl.cc +++ b/chrome/services/speech/soda/soda_client_impl.cc
@@ -24,7 +24,10 @@ mark_done_func_(reinterpret_cast<MarkDoneFunction>( lib_.GetFunctionPointer("ExtendedSodaMarkDone"))), soda_start_func_(reinterpret_cast<SodaStartFunction>( - lib_.GetFunctionPointer("ExtendedSodaStart"))) { + lib_.GetFunctionPointer("ExtendedSodaStart"))), + update_recognition_context_func_( + reinterpret_cast<UpdateRecognitionContextFunction>( + lib_.GetFunctionPointer("UpdateRecognitionContext"))) { if (!lib_.is_valid()) { LOG(ERROR) << "SODA binary at " << library_path.value() << " could not be loaded."; @@ -32,6 +35,8 @@ DCHECK(false); } + // We do not need to check the |update_recognition_context_func_| since it is + // not available in old SODA versions. DCHECK(create_soda_func_); DCHECK(delete_soda_func_); DCHECK(add_audio_func_); @@ -114,6 +119,16 @@ soda_start_func_(soda_async_handle_); } +NO_SANITIZE("cfi-icall") +void SodaClientImpl::UpdateRecognitionContext( + const RecognitionContext context) { + if (load_soda_result_ != LoadSodaResultValue::kSuccess || + !update_recognition_context_func_) { + return; + } + update_recognition_context_func_(soda_async_handle_, context); +} + bool SodaClientImpl::IsInitialized() { return is_initialized_; }
diff --git a/chrome/services/speech/soda/soda_client_impl.h b/chrome/services/speech/soda/soda_client_impl.h index 054cc8b..d50e212d 100644 --- a/chrome/services/speech/soda/soda_client_impl.h +++ b/chrome/services/speech/soda/soda_client_impl.h
@@ -30,6 +30,7 @@ void Reset(const SerializedSodaConfig config, int sample_rate, int channel_count) override; + void UpdateRecognitionContext(const RecognitionContext context) override; bool IsInitialized() override; bool BinaryLoadedSuccessfully() override; @@ -51,6 +52,9 @@ typedef void (*SodaStartFunction)(void*); SodaStartFunction soda_start_func_; + typedef void (*UpdateRecognitionContextFunction)(void*, RecognitionContext); + UpdateRecognitionContextFunction update_recognition_context_func_; + // An opaque handle to the SODA async instance. While this class owns this // handle, the handle is instantiated and deleted by the SODA library, so the // pointer may dangle after DeleteExtendedSodaAsync is called.
diff --git a/chrome/services/speech/speech_recognition_recognizer_impl.cc b/chrome/services/speech/speech_recognition_recognizer_impl.cc index f97a5d3..3d0e9bcf 100644 --- a/chrome/services/speech/speech_recognition_recognizer_impl.cc +++ b/chrome/services/speech/speech_recognition_recognizer_impl.cc
@@ -350,13 +350,34 @@ soda_client_->MarkDone(); } +void SpeechRecognitionRecognizerImpl::UpdateRecognitionContext( + media::mojom::SpeechRecognitionRecognitionContextPtr recognition_context) { + soda::chrome::RecognitionContext context; + auto* context_input = context.add_context(); + context_input->set_name(kContextInputName); + for (const auto& phrase : recognition_context->phrases) { + auto* p = context_input->mutable_phrases()->add_phrase(); + p->set_phrase(phrase.phrase); + p->set_boost(phrase.boost); + } + + auto serialized = context.SerializeAsString(); + RecognitionContext serialized_recognition_context; + serialized_recognition_context.recognition_context = serialized.c_str(); + serialized_recognition_context.recognition_context_size = serialized.size(); + CHECK(soda_client_); + soda_client_->UpdateRecognitionContext(serialized_recognition_context); +} + void SpeechRecognitionRecognizerImpl::AddAudio( media::mojom::AudioDataS16Ptr buffer) { SendAudioToSpeechRecognitionService(std::move(buffer)); } + void SpeechRecognitionRecognizerImpl::OnAudioCaptureEnd() { MarkDone(); } + void SpeechRecognitionRecognizerImpl::OnAudioCaptureError() { OnSpeechRecognitionError(); }
diff --git a/chrome/services/speech/speech_recognition_recognizer_impl.h b/chrome/services/speech/speech_recognition_recognizer_impl.h index 0c7c35b8..decb734 100644 --- a/chrome/services/speech/speech_recognition_recognizer_impl.h +++ b/chrome/services/speech/speech_recognition_recognizer_impl.h
@@ -102,6 +102,9 @@ void MarkDone() override; + void UpdateRecognitionContext( + media::mojom::SpeechRecognitionRecognitionContextPtr recognition_context); + // AudioSourceConsumer: void AddAudio(media::mojom::AudioDataS16Ptr buffer) override; void OnAudioCaptureEnd() override;
diff --git a/chrome/services/speech/speech_recognition_recognizer_impl_unittest.cc b/chrome/services/speech/speech_recognition_recognizer_impl_unittest.cc index f2fa713..291639d 100644 --- a/chrome/services/speech/speech_recognition_recognizer_impl_unittest.cc +++ b/chrome/services/speech/speech_recognition_recognizer_impl_unittest.cc
@@ -115,4 +115,17 @@ EXPECT_EQ(2.0, phrase.boost()); } +TEST_F(SpeechRecognitionRecognizerImplTest, UpdateRecognitionContextTest) { + auto recognizer = CreateRecognizer(CreateOptions()); + auto soda_client = std::make_unique<NiceMock<::soda::MockSodaClient>>(); + auto* soda_client_ptr = soda_client.get(); + recognizer->SetSodaClientForTesting(std::move(soda_client)); + + media::mojom::SpeechRecognitionRecognitionContextPtr context = + media::mojom::SpeechRecognitionRecognitionContext::New(); + context->phrases.emplace_back("test phrase", 2.0); + EXPECT_CALL(*soda_client_ptr, UpdateRecognitionContext(_)); + recognizer->UpdateRecognitionContext(std::move(context)); +} + } // namespace speech
diff --git a/chrome/test/data/webui/chromeos/boca_ui/client_delegate_impl_test.ts b/chrome/test/data/webui/chromeos/boca_ui/client_delegate_impl_test.ts index b3bcf84..f63dbb1 100644 --- a/chrome/test/data/webui/chromeos/boca_ui/client_delegate_impl_test.ts +++ b/chrome/test/data/webui/chromeos/boca_ui/client_delegate_impl_test.ts
@@ -113,9 +113,7 @@ sessionDuration: { microseconds: 120000000n, }, - sessionStartTime: { - msec: 1000000, - }, + sessionStartTime: new Date(1000000), teacher: { id: '0', name: 'teacher', @@ -410,9 +408,7 @@ sessionDuration: { microseconds: 120000000n, }, - sessionStartTime: { - msec: 1000000, - }, + sessionStartTime: new Date(1000000), students: [], studentsJoinViaCode: [], onTaskConfig: {isLocked: false, tabs: []},
diff --git a/chrome/test/data/webui/data_sharing/BUILD.gn b/chrome/test/data/webui/data_sharing/BUILD.gn index 90cae98..43b9a08 100644 --- a/chrome/test/data/webui/data_sharing/BUILD.gn +++ b/chrome/test/data/webui/data_sharing/BUILD.gn
@@ -16,5 +16,8 @@ [ "chrome-untrusted://data-sharing/*|" + rebase_path("$root_gen_dir/chrome/browser/resources/data_sharing/tsc/*", target_gen_dir) ] - ts_deps = [ "//chrome/browser/resources/data_sharing:build_ts" ] + ts_deps = [ + "//chrome/browser/resources/data_sharing:build_ts", + "//ui/webui/resources/js:build_ts", + ] }
diff --git a/chrome/test/data/webui/data_sharing/data_sharing_test.ts b/chrome/test/data/webui/data_sharing/data_sharing_test.ts index 1b2bef4..3277c54 100644 --- a/chrome/test/data/webui/data_sharing/data_sharing_test.ts +++ b/chrome/test/data/webui/data_sharing/data_sharing_test.ts
@@ -11,6 +11,7 @@ import {DataSharingApp} from 'chrome-untrusted://data-sharing/data_sharing_app.js'; import {Code} from 'chrome-untrusted://data-sharing/data_sharing_sdk_types.js'; import {DataSharingSdkImpl} from 'chrome-untrusted://data-sharing/dummy_data_sharing_sdk.js'; +import {loadTimeData} from 'chrome-untrusted://resources/js/load_time_data.js'; import {assertEquals} from 'chrome-untrusted://webui-test/chai_assert.js'; import {TestBrowserProxy} from 'chrome-untrusted://webui-test/test_browser_proxy.js'; import {TestMock} from 'chrome-untrusted://webui-test/test_mock.js'; @@ -101,4 +102,19 @@ assertEquals(1, testBrowserProxy.getCallCount('closeUi')); assertEquals(Code.OK, testBrowserProxy.getArgs('closeUi')[0]); }); + + test('Metrics reporting', async () => { + loadTimeData.overrideValues({ + metricsReportingEnabled: true, + }); + DataSharingApp.setUrlForTesting( + 'chrome-untrusted://data-sharing?flow=share&tab_group_id=fake_id'); + dataSharingApp = document.createElement('data-sharing-app'); + testBrowserProxy.callbackRouterRemote.onAccessTokenFetched('fake_token'); + document.body.appendChild(dataSharingApp); + await microtasksFinished(); + assertEquals(1, testDataSharingSdk.getCallCount('updateClearcut')); + const arg = testDataSharingSdk.getArgs('updateClearcut')[0]; + assertEquals(true, arg.enabled); + }); });
diff --git a/chrome/updater/policy/service.cc b/chrome/updater/policy/service.cc index 0e0fd00b..85ed18f 100644 --- a/chrome/updater/policy/service.cc +++ b/chrome/updater/policy/service.cc
@@ -87,9 +87,9 @@ } // namespace std::vector<scoped_refptr<PolicyManagerInterface>> CreateManagers( - bool should_take_policy_critical_section, scoped_refptr<ExternalConstants> external_constants, - scoped_refptr<PolicyManagerInterface> dm_policy_manager) { + scoped_refptr<PolicyManagerInterface> dm_policy_manager, + scoped_refptr<PolicyManagerInterface> group_policy) { // The order of the policy managers: // 1) External constants policy manager (if present). // 2) Group policy manager (Windows only). ** @@ -107,9 +107,10 @@ external_constants->GroupPolicies()) : nullptr; #if BUILDFLAG(IS_WIN) - auto group_policy_manager = base::MakeRefCounted<GroupPolicyManager>( - should_take_policy_critical_section, - external_constants->IsMachineManaged()); + auto group_policy_manager = group_policy + ? group_policy + : base::MakeRefCounted<GroupPolicyManager>( + external_constants->IsMachineManaged()); if (CloudPolicyOverridesPlatformPolicy({dm_policy_manager, group_policy_manager, external_constants_policy_manager})) { @@ -144,16 +145,11 @@ persisted_data_(persisted_data), is_ceca_experiment_enabled_(false) {} -// The policy managers are initialized without taking the Group Policy critical -// section here, by passing `false` for `should_take_policy_critical_section`, -// to avoid blocking the main sequence. Later in `FetchPoliciesDone`, the -// policies are reloaded with the critical section lock. PolicyService::PolicyService( scoped_refptr<ExternalConstants> external_constants, scoped_refptr<PersistedData> persisted_data, bool is_ceca_experiment_enabled) : policy_managers_(SortManagers(CreateManagers( - /*should_take_policy_critical_section=*/false, external_constants, CreateDMPolicyManager(external_constants->IsMachineManaged())))), external_constants_(external_constants), @@ -224,15 +220,18 @@ FROM_HERE, {base::MayBlock(), base::WithBaseSyncPrimitives()}, base::BindOnce( [](scoped_refptr<ExternalConstants> external_constants, - scoped_refptr<PolicyManagerInterface> dm_policy_manager) { - return CreateManagers( - /*should_take_policy_critical_section=*/true, - external_constants, dm_policy_manager); + scoped_refptr<PolicyManagerInterface> dm_policy_manager, + scoped_refptr<PolicyManagerInterface> group_policy_manager) { + return CreateManagers(external_constants, dm_policy_manager, + group_policy_manager); }, external_constants_, dm_policy_manager ? dm_policy_manager : policy_managers_.manager_names.contains(kSourceDMPolicyManager) ? policy_managers_.manager_names[kSourceDMPolicyManager] + : nullptr, + policy_managers_.manager_names.contains(kSourceGroupPolicyManager) + ? policy_managers_.manager_names[kSourceGroupPolicyManager] : nullptr), base::BindOnce(&PolicyService::PolicyManagerLoaded, base::WrapRefCounted(this), result));
diff --git a/chrome/updater/policy/service.h b/chrome/updater/policy/service.h index b76cd33..832f7bb 100644 --- a/chrome/updater/policy/service.h +++ b/chrome/updater/policy/service.h
@@ -240,9 +240,9 @@ }; std::vector<scoped_refptr<PolicyManagerInterface>> CreateManagers( - bool should_take_policy_critical_section, scoped_refptr<ExternalConstants> external_constants, - scoped_refptr<PolicyManagerInterface> dm_policy_manager); + scoped_refptr<PolicyManagerInterface> dm_policy_manager, + scoped_refptr<PolicyManagerInterface> group_policy_manager = {}); } // namespace updater
diff --git a/chrome/updater/policy/service_unittest.cc b/chrome/updater/policy/service_unittest.cc index 6fa7f487..274b8ab 100644 --- a/chrome/updater/policy/service_unittest.cc +++ b/chrome/updater/policy/service_unittest.cc
@@ -15,6 +15,7 @@ #include "base/memory/ref_counted.h" #include "base/ranges/algorithm.h" #include "base/strings/stringprintf.h" +#include "base/test/task_environment.h" #include "base/time/time.h" #include "base/values.h" #include "chrome/enterprise_companion/global_constants.h" @@ -771,6 +772,7 @@ #if BUILDFLAG(IS_WIN) TEST(PolicyService, CreateManagers) { + base::test::TaskEnvironment environment; registry_util::RegistryOverrideManager registry_overrides; ASSERT_NO_FATAL_FAILURE( registry_overrides.OverrideRegistry(HKEY_LOCAL_MACHINE)); @@ -780,7 +782,7 @@ OmahaSettingsClientProto>(); auto dm_policy = base::MakeRefCounted<DMPolicyManager>(*omaha_settings, true); PolicyManagers managers = - CreateManagers(false, CreateExternalConstants(), dm_policy); + CreateManagers(CreateExternalConstants(), dm_policy); EXPECT_EQ(managers.size(), size_t{4}); EXPECT_EQ(managers[0]->source(), "DictValuePolicy"); EXPECT_EQ(managers[1]->source(), "Group Policy"); @@ -791,7 +793,7 @@ Wow6432(KEY_WRITE)); EXPECT_EQ(ERROR_SUCCESS, key.WriteValue(L"CloudPolicyOverridesPlatformPolicy", 1)); - managers = CreateManagers(false, CreateExternalConstants(), dm_policy); + managers = CreateManagers(CreateExternalConstants(), dm_policy); EXPECT_EQ(managers.size(), size_t{4}); EXPECT_EQ(managers[0]->source(), "DictValuePolicy"); EXPECT_EQ(managers[1]->source(), "Device Management"); @@ -805,7 +807,7 @@ OmahaSettingsClientProto>(); auto dm_policy = base::MakeRefCounted<DMPolicyManager>(*omaha_settings, true); PolicyManagers managers = - CreateManagers(false, CreateExternalConstants(), dm_policy); + CreateManagers(CreateExternalConstants(), dm_policy); EXPECT_EQ(managers.size(), size_t{4}); EXPECT_EQ(managers[0]->source(), "DictValuePolicy"); EXPECT_EQ(managers[1]->source(), "Device Management"); @@ -819,7 +821,7 @@ OmahaSettingsClientProto>(); auto dm_policy = base::MakeRefCounted<DMPolicyManager>(*omaha_settings, true); PolicyManagers managers = - CreateManagers(false, CreateExternalConstants(), dm_policy); + CreateManagers(CreateExternalConstants(), dm_policy); EXPECT_EQ(managers.size(), size_t{3}); EXPECT_EQ(managers[0]->source(), "DictValuePolicy"); EXPECT_EQ(managers[1]->source(), "Device Management");
diff --git a/chrome/updater/policy/win/group_policy_manager.cc b/chrome/updater/policy/win/group_policy_manager.cc index 69af8e87..4c83efd8 100644 --- a/chrome/updater/policy/win/group_policy_manager.cc +++ b/chrome/updater/policy/win/group_policy_manager.cc
@@ -56,10 +56,10 @@ virtual ~PolicySectionEvents() = default; }; -base::Value::Dict LoadGroupPolicies(bool should_take_policy_critical_section) { +base::Value::Dict LoadGroupPolicies() { base::ScopedClosureRunner leave_policy_section_closure; - if (should_take_policy_critical_section && base::IsManagedDevice()) { + if (base::IsManagedDevice()) { // Only for managed machines, a best effort is made to take the Group Policy // critical section. Lock acquisition can take a long time in the worst case // scenarios, hence a short timed wait is used. @@ -122,9 +122,8 @@ } // namespace GroupPolicyManager::GroupPolicyManager( - bool should_take_policy_critical_section, std::optional<bool> override_is_managed_device) - : PolicyManager(LoadGroupPolicies(should_take_policy_critical_section)), + : PolicyManager(LoadGroupPolicies()), is_managed_device_(override_is_managed_device.value_or( base::IsManagedOrEnterpriseDevice())) {}
diff --git a/chrome/updater/policy/win/group_policy_manager.h b/chrome/updater/policy/win/group_policy_manager.h index ca137e8..3aa1049f 100644 --- a/chrome/updater/policy/win/group_policy_manager.h +++ b/chrome/updater/policy/win/group_policy_manager.h
@@ -15,9 +15,8 @@ // The GroupPolicyManager returns policies for domain-joined machines. class GroupPolicyManager : public PolicyManager { public: - GroupPolicyManager( - bool should_take_policy_critical_section, - std::optional<bool> override_is_managed_device = std::nullopt); + explicit GroupPolicyManager( + std::optional<bool> override_is_managed_device = {}); GroupPolicyManager(const GroupPolicyManager&) = delete; GroupPolicyManager& operator=(const GroupPolicyManager&) = delete;
diff --git a/chrome/updater/policy/win/group_policy_manager_unittest.cc b/chrome/updater/policy/win/group_policy_manager_unittest.cc index 4ae01ecf..abc5591f 100644 --- a/chrome/updater/policy/win/group_policy_manager_unittest.cc +++ b/chrome/updater/policy/win/group_policy_manager_unittest.cc
@@ -53,7 +53,7 @@ } TEST_F(GroupPolicyManagerTests, NoPolicySet) { - auto policy_manager = base::MakeRefCounted<GroupPolicyManager>(true); + auto policy_manager = base::MakeRefCounted<GroupPolicyManager>(); EXPECT_FALSE(policy_manager->HasActiveDevicePolicies()); EXPECT_EQ(policy_manager->source(), "Group Policy"); @@ -125,7 +125,7 @@ EXPECT_EQ(ERROR_SUCCESS, key.WriteValue(L"RollbackToTargetVersion" TEST_APP_ID, 1)); - auto policy_manager = base::MakeRefCounted<GroupPolicyManager>(true); + auto policy_manager = base::MakeRefCounted<GroupPolicyManager>(); EXPECT_EQ(policy_manager->HasActiveDevicePolicies(), base::win::IsEnrolledToDomain()); @@ -199,7 +199,8 @@ EXPECT_EQ(ERROR_SUCCESS, key.WriteValue(L"RollbackToTargetVersion" TEST_APP_ID, L"1")); - auto policy_manager = base::MakeRefCounted<GroupPolicyManager>(true, true); + auto policy_manager = base::MakeRefCounted<GroupPolicyManager>( + /*override_is_managed_device=*/true); EXPECT_TRUE(policy_manager->HasActiveDevicePolicies()); EXPECT_FALSE(policy_manager->CloudPolicyOverridesPlatformPolicy());
diff --git a/chromeos/ash/components/phonehub/OWNERS b/chromeos/ash/components/phonehub/OWNERS index 4746e16..d5036cfe8 100644 --- a/chromeos/ash/components/phonehub/OWNERS +++ b/chromeos/ash/components/phonehub/OWNERS
@@ -5,5 +5,4 @@ pushi@google.com # For Eche -dhnishi@chromium.org nayebi@google.com
diff --git a/clank b/clank index de102b69..dbee480 160000 --- a/clank +++ b/clank
@@ -1 +1 @@ -Subproject commit de102b699470b4d92be37fc98f29f6c668e419f2 +Subproject commit dbee4805fa8597c96cfcf0e4f44e77ae9f50901c
diff --git a/components/browser_ui/bottomsheet/android/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheet.java b/components/browser_ui/bottomsheet/android/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheet.java index 1c02a9a..6413cc3 100644 --- a/components/browser_ui/bottomsheet/android/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheet.java +++ b/components/browser_ui/bottomsheet/android/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheet.java
@@ -316,6 +316,7 @@ mToolbarHolder = (TouchRestrictingFrameLayout) findViewById(R.id.bottom_sheet_toolbar_container); + mToolbarHolder.setBottomSheet(this); mBottomSheetContentContainer = (TouchRestrictingFrameLayout) findViewById(R.id.bottom_sheet_content);
diff --git a/components/browser_ui/bottomsheet/android/internal/java/src/org/chromium/components/browser_ui/bottomsheet/TouchRestrictingFrameLayout.java b/components/browser_ui/bottomsheet/android/internal/java/src/org/chromium/components/browser_ui/bottomsheet/TouchRestrictingFrameLayout.java index bf114fe9..b49ecfa7 100644 --- a/components/browser_ui/bottomsheet/android/internal/java/src/org/chromium/components/browser_ui/bottomsheet/TouchRestrictingFrameLayout.java +++ b/components/browser_ui/bottomsheet/android/internal/java/src/org/chromium/components/browser_ui/bottomsheet/TouchRestrictingFrameLayout.java
@@ -34,13 +34,13 @@ @Override public boolean onInterceptTouchEvent(MotionEvent event) { - if (isTouchDisabled()) return false; + if (isTouchDisabled()) return true; return super.onInterceptTouchEvent(event); } @Override public boolean onTouchEvent(MotionEvent event) { - if (isTouchDisabled()) return false; + if (isTouchDisabled()) return true; return super.onTouchEvent(event); } }
diff --git a/components/browser_ui/display_cutout/android/java/src/org/chromium/components/browser_ui/display_cutout/DisplayCutoutController.java b/components/browser_ui/display_cutout/android/java/src/org/chromium/components/browser_ui/display_cutout/DisplayCutoutController.java index d9962ea8..33ea65fb 100644 --- a/components/browser_ui/display_cutout/android/java/src/org/chromium/components/browser_ui/display_cutout/DisplayCutoutController.java +++ b/components/browser_ui/display_cutout/android/java/src/org/chromium/components/browser_ui/display_cutout/DisplayCutoutController.java
@@ -152,28 +152,15 @@ // Helper implementation to observe fullscreen changes and trigger re-layout. private class FullscreenWebContentsObserver extends WebContentsObserver { - private boolean mIsDestroyed; - FullscreenWebContentsObserver(WebContents webContents) { super(webContents); } - @Nullable - WebContents getWebContents() { - return mIsDestroyed ? null : mWebContents.get(); - } - @Override public void didToggleFullscreenModeForTab( boolean enteredFullscreen, boolean willCauseResize) { maybeUpdateLayout(); } - - @Override - public void destroy() { - mIsDestroyed = true; - super.destroy(); - } } private final Delegate mDelegate; @@ -277,21 +264,19 @@ } private void updateWebContentObserver(@Nullable WebContents webContents) { - if (webContents == null) { - if (mWebContentsObserver != null) { - mWebContentsObserver.destroy(); - mWebContentsObserver = null; - } + if (mWebContentsObserver != null + && webContents != null + && mWebContentsObserver.getWebContents() == webContents) { return; } - if (mWebContentsObserver != null && mWebContentsObserver.mIsDestroyed) { - if (webContents.equals(mWebContentsObserver.getWebContents())) { - return; - } else { - mWebContentsObserver.destroy(); - } + if (mWebContentsObserver != null) { + mWebContentsObserver.destroy(); + mWebContentsObserver = null; } + + if (webContents == null) return; + mWebContentsObserver = new FullscreenWebContentsObserver(webContents); }
diff --git a/components/collaboration/internal/collaboration_service_impl.h b/components/collaboration/internal/collaboration_service_impl.h index 3c6dd7a8..38e06ad 100644 --- a/components/collaboration/internal/collaboration_service_impl.h +++ b/components/collaboration/internal/collaboration_service_impl.h
@@ -29,7 +29,7 @@ namespace collaboration { class CollaborationController; -// The internal implementation of the CollborationService. +// The internal implementation of the CollaborationService. class CollaborationServiceImpl : public CollaborationService, public syncer::SyncServiceObserver, public signin::IdentityManager::Observer {
diff --git a/components/collaboration/public/collaboration_controller_delegate.h b/components/collaboration/public/collaboration_controller_delegate.h index efa073945..1ed255a 100644 --- a/components/collaboration/public/collaboration_controller_delegate.h +++ b/components/collaboration/public/collaboration_controller_delegate.h
@@ -16,7 +16,7 @@ namespace collaboration { // The class responsible for controlling actions on platform specific UI -// elements. This delegate is required by the CollborationController. +// elements. This delegate is required by the CollaborationController. class CollaborationControllerDelegate { public: struct ErrorInfo {
diff --git a/components/commerce_strings.grdp b/components/commerce_strings.grdp index ccb5b27..cdcc437a 100644 --- a/components/commerce_strings.grdp +++ b/components/commerce_strings.grdp
@@ -738,6 +738,12 @@ <message name="IDS_COMPARE_SHOW_ALL_COMPARISON_TABLES" desc="The label for the menu option for showing all of a user's existing comparison tables."> Show All Comparison Tables </message> + <message name="IDS_COMPARE_ADD_TAB_TO_COMPARISON_TABLE" desc="The label for the menu option for adding the current tab to a comparison table."> + Add Tab to Comparison Table + </message> + <message name="IDS_COMPARE_NEW_COMPARISON_TABLE" desc="The label for the menu option for adding the current tab to a new comparison table."> + New Comparison Table + </message> </then> <else> <message name="IDS_COMPARE_MENU_LABEL" desc="The label for the comparison tables submenu item under the bookmarks and lists app menu."> @@ -746,6 +752,12 @@ <message name="IDS_COMPARE_SHOW_ALL_COMPARISON_TABLES" desc="The label for the menu option for showing all of a user's existing comparison tables."> Show all comparison tables </message> + <message name="IDS_COMPARE_ADD_TAB_TO_COMPARISON_TABLE" desc="The label for the menu option for adding the current tab to a comparison table."> + Add tab to comparison table + </message> + <message name="IDS_COMPARE_NEW_COMPARISON_TABLE" desc="The label for the menu option for adding the current tab to a new comparison table."> + New comparison table + </message> </else> </if> <!-- use_titlecase --> </grit-part>
diff --git a/components/commerce_strings_grdp/IDS_COMPARE_ADD_TAB_TO_COMPARISON_TABLE.png.sha1 b/components/commerce_strings_grdp/IDS_COMPARE_ADD_TAB_TO_COMPARISON_TABLE.png.sha1 new file mode 100644 index 0000000..21328611 --- /dev/null +++ b/components/commerce_strings_grdp/IDS_COMPARE_ADD_TAB_TO_COMPARISON_TABLE.png.sha1
@@ -0,0 +1 @@ +ffc443d9c651d08964931f9de0cb885ef57c4c14 \ No newline at end of file
diff --git a/components/commerce_strings_grdp/IDS_COMPARE_NEW_COMPARISON_TABLE.png.sha1 b/components/commerce_strings_grdp/IDS_COMPARE_NEW_COMPARISON_TABLE.png.sha1 new file mode 100644 index 0000000..f467099b --- /dev/null +++ b/components/commerce_strings_grdp/IDS_COMPARE_NEW_COMPARISON_TABLE.png.sha1
@@ -0,0 +1 @@ +cd9af114cd9d4ae67e6587ff52b09c0508521b35 \ No newline at end of file
diff --git a/components/data_sharing/public/android/java/src/org/chromium/components/data_sharing/configs/DataSharingUiConfig.java b/components/data_sharing/public/android/java/src/org/chromium/components/data_sharing/configs/DataSharingUiConfig.java index 9a207d8..5b748b6 100644 --- a/components/data_sharing/public/android/java/src/org/chromium/components/data_sharing/configs/DataSharingUiConfig.java +++ b/components/data_sharing/public/android/java/src/org/chromium/components/data_sharing/configs/DataSharingUiConfig.java
@@ -27,11 +27,6 @@ /** Callback interface for common data sharing UI events. */ public interface DataSharingCallback { - // TODO (ritikagup) : Cleanup this method, once the overloaded method is fully integrated. - default void onLearnMoreAboutSharedTabGroupsClicked(GURL url) {} - - default void onLearnMoreAboutSharedTabGroupsClicked(Context context, GURL url) {} - default void onClickOpenChromeCustomTab(Context context, GURL url) {} }
diff --git a/components/exo/surface.cc b/components/exo/surface.cc index 34f3a0d..4bc215c 100644 --- a/components/exo/surface.cc +++ b/components/exo/surface.cc
@@ -14,6 +14,7 @@ #include "base/functional/callback_helpers.h" #include "base/logging.h" #include "base/memory/raw_ptr.h" +#include "base/metrics/histogram_macros.h" #include "base/not_fatal_until.h" #include "base/notreached.h" #include "base/numerics/safe_conversions.h" @@ -1701,6 +1702,7 @@ bool needs_full_damage, std::optional<float> device_scale_factor, viz::CompositorFrame* frame) { + UMA_HISTOGRAM_BOOLEAN("Graphics.Exo.Surface.AppendContentsToFrame", true); const std::unique_ptr<viz::CompositorRenderPass>& render_pass = frame->render_pass_list.back(); gfx::PointF parent_to_root_dp = gfx::ScalePoint( @@ -1827,6 +1829,7 @@ if (current_resource_.id) { frame->resource_list.push_back(current_resource_); } + UMA_HISTOGRAM_BOOLEAN("Graphics.Exo.Surface.Occluded", true); return; } @@ -1871,6 +1874,7 @@ // Draw quad is only needed if buffer is not fully transparent. if (requires_texture_draw_quad) { + UMA_HISTOGRAM_BOOLEAN("Graphics.Exo.Surface.TextureDrawQuad", true); viz::TextureDrawQuad* texture_quad = render_pass->CreateAndAppendDrawQuad<viz::TextureDrawQuad>(); texture_quad->SetNew(quad_state, quad_rect, quad_rect, @@ -1923,6 +1927,7 @@ damage_rect_px = gfx::RectF(); } } else { + UMA_HISTOGRAM_BOOLEAN("Graphics.Exo.Surface.TileDrawQuad", true); viz::TileDrawQuad* tile_quad = render_pass->CreateAndAppendDrawQuad<viz::TileDrawQuad>(); // TODO(crbug.com/40229946): Support AA quads coming from exo. @@ -1939,6 +1944,7 @@ } frame->resource_list.push_back(current_resource_); } else if (state_.basic_state.alpha != 0.0f) { + UMA_HISTOGRAM_BOOLEAN("Graphics.Exo.Surface.SolidColorDrawQuad", true); const viz::SharedQuadState* quad_state = AppendOrCreateSharedQuadState( viz::DrawQuad::Material::kSolidColor, state_.basic_state.alpha, render_pass, quad_to_target_transform, quad_rect, msk, quad_clip_rect,
diff --git a/components/mirroring/service/openscreen_session_host.cc b/components/mirroring/service/openscreen_session_host.cc index b23629c..94c11fad 100644 --- a/components/mirroring/service/openscreen_session_host.cc +++ b/components/mirroring/service/openscreen_session_host.cc
@@ -546,9 +546,8 @@ weak_factory_.GetWeakPtr()), // This is safe since it is only called synchronously and we own // the video sender instance. - base::BindRepeating(&OpenscreenSessionHost::GetSuggestedVideoBitrate, - base::Unretained(this), video_config->min_bitrate, - video_config->max_bitrate), + base::BindRepeating(&OpenscreenSessionHost::GetVideoNetworkBandwidth, + base::Unretained(this)), gpu_factories); video_stream_ = std::make_unique<VideoRtpStream>( std::move(video_sender), weak_factory_.GetWeakPtr(), @@ -1015,19 +1014,9 @@ } } -int OpenscreenSessionHost::GetSuggestedVideoBitrate(int min_bitrate, - int max_bitrate) const { - // First take the suggested bitrate based on the current bandwidth - // utilization. - int suggested = usable_bandwidth_; - if (audio_stream_) { - suggested -= audio_stream_->GetEncoderBitrate(); - } - - // Then limit it based on the frame sender configuration. - // TODO(crbug.com/40260069): we should also factor in device - // capability when determining which bitrate to use. - return std::clamp(suggested, min_bitrate, max_bitrate); +int OpenscreenSessionHost::GetVideoNetworkBandwidth() const { + return audio_stream_ ? usable_bandwidth_ - audio_stream_->GetEncoderBitrate() + : usable_bandwidth_; } void OpenscreenSessionHost::UpdateBandwidthEstimate() {
diff --git a/components/mirroring/service/openscreen_session_host.h b/components/mirroring/service/openscreen_session_host.h index b87686d1..aae253c7 100644 --- a/components/mirroring/service/openscreen_session_host.h +++ b/components/mirroring/service/openscreen_session_host.h
@@ -184,8 +184,8 @@ // Callback by media::cast::VideoSender to report resource utilization. void ProcessFeedback(const media::VideoCaptureFeedback& feedback); - // Called by OpenscreenFrameSender to determine bitrate. - int GetSuggestedVideoBitrate(int min_bitrate, int max_bitrate) const; + // Called by media::cast::VideoSender to help determine the video bitrate. + int GetVideoNetworkBandwidth() const; // Called periodically to update the `bandwidth_estimate_`. void UpdateBandwidthEstimate();
diff --git a/components/mirroring/service/openscreen_session_host_unittest.cc b/components/mirroring/service/openscreen_session_host_unittest.cc index ffd3df0..c96ca89 100644 --- a/components/mirroring/service/openscreen_session_host_unittest.cc +++ b/components/mirroring/service/openscreen_session_host_unittest.cc
@@ -871,30 +871,25 @@ constexpr int kMinVideoBitrate = 393216; constexpr int kMaxVideoBitrate = 1250000; // Default bitrate should be twice the minimum. - EXPECT_EQ(786432, session_host().GetSuggestedVideoBitrate(kMinVideoBitrate, - kMaxVideoBitrate)); + EXPECT_EQ(786432, session_host().GetVideoNetworkBandwidth()); // If the estimate is below the minimum, it should stay at the minimum. session_host().forced_bandwidth_estimate_for_testing_ = 1000; session_host().UpdateBandwidthEstimate(); - EXPECT_EQ(kMinVideoBitrate, session_host().GetSuggestedVideoBitrate( - kMinVideoBitrate, kMaxVideoBitrate)); + EXPECT_EQ(kMinVideoBitrate, session_host().GetVideoNetworkBandwidth()); // It should gradually reach the max bandwidth estimate when raised. session_host().forced_bandwidth_estimate_for_testing_ = 1000000; session_host().UpdateBandwidthEstimate(); - EXPECT_EQ(432537, session_host().GetSuggestedVideoBitrate(kMinVideoBitrate, - kMaxVideoBitrate)); + EXPECT_EQ(432537, session_host().GetVideoNetworkBandwidth()); session_host().UpdateBandwidthEstimate(); - EXPECT_EQ(475790, session_host().GetSuggestedVideoBitrate(kMinVideoBitrate, - kMaxVideoBitrate)); + EXPECT_EQ(475790, session_host().GetVideoNetworkBandwidth()); for (int i = 0; i < 20; ++i) { session_host().UpdateBandwidthEstimate(); } // The max should be 80% of `forced_bandwidth_estimate_for_testing_`. - EXPECT_EQ(800000, session_host().GetSuggestedVideoBitrate(kMinVideoBitrate, - kMaxVideoBitrate)); + EXPECT_EQ(800000, session_host().GetVideoNetworkBandwidth()); // The video bitrate should stay saturated at the cap when reached. session_host().forced_bandwidth_estimate_for_testing_ = kMaxVideoBitrate + 1; @@ -902,8 +897,7 @@ session_host().UpdateBandwidthEstimate(); } // The max should be 80% of `kMaxVideoBitrate`. - EXPECT_EQ(1000000, session_host().GetSuggestedVideoBitrate(kMinVideoBitrate, - kMaxVideoBitrate)); + EXPECT_EQ(1000000, session_host().GetVideoNetworkBandwidth()); StopSession(); }
diff --git a/components/optimization_guide/core/BUILD.gn b/components/optimization_guide/core/BUILD.gn index 0309dc9..ab1a8421 100644 --- a/components/optimization_guide/core/BUILD.gn +++ b/components/optimization_guide/core/BUILD.gn
@@ -249,6 +249,10 @@ "model_execution/model_execution_manager.h", "model_execution/model_execution_util.cc", "model_execution/model_execution_util.h", + "model_execution/on_device_context.cc", + "model_execution/on_device_context.h", + "model_execution/on_device_execution.cc", + "model_execution/on_device_execution.h", "model_execution/on_device_model_access_controller.cc", "model_execution/on_device_model_access_controller.h", "model_execution/on_device_model_adaptation_controller.cc",
diff --git a/components/optimization_guide/core/model_execution/on_device_context.cc b/components/optimization_guide/core/model_execution/on_device_context.cc new file mode 100644 index 0000000..e2964e3e6 --- /dev/null +++ b/components/optimization_guide/core/model_execution/on_device_context.cc
@@ -0,0 +1,142 @@ +// Copyright 2025 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/optimization_guide/core/model_execution/on_device_context.h" + +#include "base/metrics/histogram_functions.h" +#include "components/optimization_guide/core/optimization_guide_features.h" + +namespace optimization_guide { + +OnDeviceOptions::Client::~Client() = default; + +OnDeviceOptions::OnDeviceOptions() = default; +OnDeviceOptions::OnDeviceOptions(OnDeviceOptions&&) = default; +OnDeviceOptions::~OnDeviceOptions() = default; + +OnDeviceOptions::OnDeviceOptions(const OnDeviceOptions& orig) + : model_client(orig.model_client->Clone()), + model_versions(orig.model_versions), + adapter(orig.adapter), + safety_checker(std::make_unique<SafetyChecker>(*orig.safety_checker)), + token_limits(orig.token_limits), + logger(orig.logger), + log_uploader(orig.log_uploader) {} + +bool OnDeviceOptions::ShouldUse() const { + return model_client->ShouldUse(); +} + +OnDeviceContext::OnDeviceContext(OnDeviceOptions opts, + ModelBasedCapabilityKey feature) + : opts_(std::move(opts)), feature_(feature) {} +OnDeviceContext::~OnDeviceContext() = default; + +bool OnDeviceContext::SetInput(const google::protobuf::MessageLite& request) { + auto input = + opts_.adapter->ConstructInputString(request, /*want_input_context=*/true); + if (!input) { + return false; + } + session_.reset(); + client_.reset(); + input_ = std::move(input->input); + GetOrCreateSession(); // Start processing + return true; +} + +mojo::Remote<on_device_model::mojom::Session>& +OnDeviceContext::GetOrCreateSession() { + DCHECK(opts_.ShouldUse()); + if (session_) { + return session_; + } + opts_.model_client->GetModelRemote()->StartSession( + session_.BindNewPipeAndPassReceiver()); + session_.reset_on_disconnect(); + progress_ = Progress{}; + if (input_) { + AddContext(features::GetOnDeviceModelMinTokensForContext()); + } + return session_; +} + +void OnDeviceContext::CloneSession( + mojo::PendingReceiver<on_device_model::mojom::Session> clone, + proto::OnDeviceModelServiceRequest* logged_request, + bool ignore_context) { + auto& session = GetOrCreateSession(); + CancelOptionalContext(); + if (input_) { + base::UmaHistogramCounts10000( + base::StrCat({"OptimizationGuide.ModelExecution." + "OnDeviceContextTokensProcessed.", + GetStringNameForModelExecutionFeature(feature_)}), + progress_.tokens_processed_); + base::UmaHistogramBoolean( + base::StrCat({"OptimizationGuide.ModelExecution." + "OnDeviceContextFinishedProcessing.", + GetStringNameForModelExecutionFeature(feature_)}), + progress_.finished_processing_); + logged_request->set_input_context_num_tokens_processed( + progress_.tokens_processed_); + logged_request + ->set_time_from_input_context_processed_to_request_initiated_millis( + (progress_.cancelled_ - progress_.start_).InMilliseconds()); + if (!ignore_context) { + logged_request->set_input_context_string(OnDeviceInputToString(*input_)); + } + } + session->Clone(std::move(clone)); +} + +void OnDeviceContext::CancelOptionalContext() { + if (!progress_.cancelled_.is_null()) { + // Already cancelled. + return; + } + progress_.cancelled_ = base::Time::Now(); + if (progress_.can_cancel_) { + client_.reset(); + } +} + +void OnDeviceContext::AddContext(uint32_t num_tokens) { + progress_.expected_tokens_ = num_tokens; + if (num_tokens == 0) { + // This only happens if MinTokens is 0, and we just consider the required + // chunk to be trivially complete, and move on to the next chunk. + OnComplete(0); + return; + } + auto options = on_device_model::mojom::InputOptions::New(); + options->input = input_.Clone(); + options->max_tokens = num_tokens; + options->token_offset = progress_.tokens_processed_; + session_->AddContext(std::move(options), client_.BindNewPipeAndPassRemote()); +} + +void OnDeviceContext::OnComplete(uint32_t tokens_processed) { + client_.reset(); + progress_.tokens_processed_ += tokens_processed; + + if (!progress_.cancelled_.is_null()) { + return; + } + + // This means input has been fully processed. + if (tokens_processed < progress_.expected_tokens_) { + progress_.finished_processing_ = true; + return; + } + + // Once the initial context is complete, we can cancel future context + // processing. + progress_.can_cancel_ = true; + if (progress_.tokens_processed_ < opts_.token_limits.max_context_tokens) { + AddContext(features::GetOnDeviceModelContextTokenChunkSize()); + } +} + +} // namespace optimization_guide
diff --git a/components/optimization_guide/core/model_execution/on_device_context.h b/components/optimization_guide/core/model_execution/on_device_context.h new file mode 100644 index 0000000..2ed7988a --- /dev/null +++ b/components/optimization_guide/core/model_execution/on_device_context.h
@@ -0,0 +1,113 @@ +// Copyright 2025 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_OPTIMIZATION_GUIDE_CORE_MODEL_EXECUTION_ON_DEVICE_CONTEXT_H_ +#define COMPONENTS_OPTIMIZATION_GUIDE_CORE_MODEL_EXECUTION_ON_DEVICE_CONTEXT_H_ + +#include <memory> + +#include "components/optimization_guide/core/model_execution/on_device_model_feature_adapter.h" +#include "components/optimization_guide/core/model_execution/safety_checker.h" +#include "components/optimization_guide/core/optimization_guide_logger.h" +#include "components/optimization_guide/proto/model_quality_metadata.pb.h" +#include "mojo/public/cpp/bindings/remote.h" + +namespace optimization_guide { + +struct OnDeviceOptions final { + OnDeviceOptions(); + OnDeviceOptions(const OnDeviceOptions&); + OnDeviceOptions(OnDeviceOptions&&); + ~OnDeviceOptions(); + + class Client { + public: + virtual ~Client() = 0; + // Create another client for the same model. + virtual std::unique_ptr<Client> Clone() const = 0; + // Called to check whether this client is still usable. + virtual bool ShouldUse() = 0; + // Called to retrieve connection the managed model. + virtual mojo::Remote<on_device_model::mojom::OnDeviceModel>& + GetModelRemote() = 0; + // Called to report a successful execution of the model. + virtual void OnResponseCompleted() = 0; + }; + + std::unique_ptr<Client> model_client; + proto::OnDeviceModelVersions model_versions; + scoped_refptr<const OnDeviceModelFeatureAdapter> adapter; + std::unique_ptr<SafetyChecker> safety_checker; + TokenLimits token_limits; + + base::WeakPtr<OptimizationGuideLogger> logger; + base::WeakPtr<ModelQualityLogsUploaderService> log_uploader; + + // Returns true if the on-device model may be used. + bool ShouldUse() const; +}; + +// Constructs an on-device session and populates it with input context. +// Context is processed incrementally. After the min context size has been +// processed, any pending context processing will be cancelled if an +// CloneSession() call is made. +class OnDeviceContext : public on_device_model::mojom::ContextClient { + public: + OnDeviceContext(OnDeviceOptions opts, ModelBasedCapabilityKey feature); + ~OnDeviceContext() override; + + // Constructs the input context and begins processing it. + bool SetInput(const google::protobuf::MessageLite& request); + + // Get the session that we've sent the input to, creating it if does not + // exist (e.g. due to a disconnect.) + mojo::Remote<on_device_model::mojom::Session>& GetOrCreateSession(); + + // Clones from the session to begin processing a request, terminating any + // optional processing, and logging data about the processing. + void CloneSession( + mojo::PendingReceiver<on_device_model::mojom::Session> clone, + proto::OnDeviceModelServiceRequest* logged_request, + bool ignore_context); + + const OnDeviceOptions& opts() { return opts_; } + + // Whether using this session is still allowed. + // This should be checked before called any other public methods. + bool CanUse() { return opts_.ShouldUse(); } + + private: + void CancelOptionalContext(); + + void AddContext(uint32_t num_tokens); + + // on_device_model::mojom::ContextClient: + void OnComplete(uint32_t tokens_processed) override; + + struct Progress final { + // Whether the required (first) chunk has finished processing. + bool can_cancel_ = false; + // Whether all input has been fully processed. + bool finished_processing_ = false; + // Number of tokens in the chunk currently being processed. + uint32_t expected_tokens_ = 0; + // Total number of tokens processed so far. + uint32_t tokens_processed_ = 0; + // When processing began. + base::Time start_; + // When processing was cancelled to begin the first execution. + base::Time cancelled_; + }; + + OnDeviceOptions opts_; + ModelBasedCapabilityKey feature_; + mojo::Remote<on_device_model::mojom::Session> session_; + on_device_model::mojom::InputPtr input_; + Progress progress_; + mojo::Receiver<on_device_model::mojom::ContextClient> client_{this}; +}; + +} // namespace optimization_guide + +#endif // COMPONENTS_OPTIMIZATION_GUIDE_CORE_MODEL_EXECUTION_ON_DEVICE_CONTEXT_H_
diff --git a/components/optimization_guide/core/model_execution/on_device_execution.cc b/components/optimization_guide/core/model_execution/on_device_execution.cc new file mode 100644 index 0000000..45af30e --- /dev/null +++ b/components/optimization_guide/core/model_execution/on_device_execution.cc
@@ -0,0 +1,629 @@ +// Copyright 2025 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/optimization_guide/core/model_execution/on_device_execution.h" + +#include "base/metrics/histogram_functions.h" +#include "base/strings/stringprintf.h" +#include "components/optimization_guide/core/model_execution/model_execution_util.h" +#include "components/optimization_guide/core/model_execution/repetition_checker.h" +#include "components/optimization_guide/core/optimization_guide_features.h" + +namespace optimization_guide { + +namespace { + +using google::protobuf::RepeatedPtrField; +using ModelExecutionError = + OptimizationGuideModelExecutionError::ModelExecutionError; + +void LogRequest(OptimizationGuideLogger* logger, + const proto::OnDeviceModelServiceRequest& logged_request) { + if (logger && logger->ShouldEnableDebugLogs()) { + OPTIMIZATION_GUIDE_LOGGER( + optimization_guide_common::mojom::LogSource::MODEL_EXECUTION, logger) + << "Executing model " + << (logged_request.input_context_string().empty() + ? "" + : base::StringPrintf( + "with input context of %d tokens:\n%s\n", + logged_request.input_context_num_tokens_processed(), + logged_request.input_context_string().c_str())) + << "with string:\n" + << logged_request.execution_string(); + } +} + +void LogRawResponse(OptimizationGuideLogger* logger, + ModelBasedCapabilityKey feature, + const std::string& raw_response) { + if (logger && logger->ShouldEnableDebugLogs()) { + OPTIMIZATION_GUIDE_LOGGER( + optimization_guide_common::mojom::LogSource::MODEL_EXECUTION, logger) + << "Model generates raw response with " + << std::string(GetStringNameForModelExecutionFeature(feature)) << ":\n" + << raw_response; + } +} + +void LogRepeatedResponse(OptimizationGuideLogger* logger, + ModelBasedCapabilityKey feature, + const std::string& repeated_response) { + if (logger && logger->ShouldEnableDebugLogs()) { + OPTIMIZATION_GUIDE_LOGGER( + optimization_guide_common::mojom::LogSource::MODEL_EXECUTION, logger) + << "Model generates repeated response with " + << std::string(GetStringNameForModelExecutionFeature(feature)) << ":\n" + << repeated_response; + } +} + +void LogResponseHasRepeats(ModelBasedCapabilityKey feature, bool has_repeats) { + base::UmaHistogramBoolean( + base::StrCat( + {"OptimizationGuide.ModelExecution.OnDeviceResponseHasRepeats.", + GetStringNameForModelExecutionFeature(feature)}), + has_repeats); +} + +void LogResponseCompleteTime(ModelBasedCapabilityKey feature, + base::TimeDelta time_to_completion) { + base::UmaHistogramMediumTimes( + base::StrCat( + {"OptimizationGuide.ModelExecution.OnDeviceResponseCompleteTime.", + GetStringNameForModelExecutionFeature(feature)}), + time_to_completion); +} + +void LogResponseCompleteTokens(ModelBasedCapabilityKey feature, + uint32_t tokens) { + base::UmaHistogramCounts10000( + base::StrCat( + {"OptimizationGuide.ModelExecution.OnDeviceResponseCompleteTokens.", + GetStringNameForModelExecutionFeature(feature)}), + tokens); +} + +std::string GenerateExecutionId() { + return "on-device:" + base::Uuid::GenerateRandomV4().AsLowercaseString(); +} + +} // namespace + +void InvokeStreamingCallbackWithRemoteResult( + OptimizationGuideModelExecutionResultStreamingCallback callback, + OptimizationGuideModelExecutionResult result, + std::unique_ptr<ModelQualityLogEntry> log_entry) { + OptimizationGuideModelStreamingExecutionResult streaming_result; + if (log_entry && log_entry->log_ai_data_request() && + log_entry->log_ai_data_request()->has_model_execution_info()) { + streaming_result.execution_info = + std::make_unique<proto::ModelExecutionInfo>( + log_entry->log_ai_data_request()->model_execution_info()); + } + streaming_result.log_entry = std::move(log_entry); + if (result.response.has_value()) { + streaming_result.response = base::ok( + StreamingResponse{.response = *result.response, .is_complete = true}); + } else { + streaming_result.response = base::unexpected(result.response.error()); + } + callback.Run(std::move(streaming_result)); +} + +OnDeviceExecution::OnDeviceExecution( + ModelBasedCapabilityKey feature, + OnDeviceOptions opts, + ExecuteRemoteFn execute_remote_fn, + std::unique_ptr<google::protobuf::MessageLite> message, + std::unique_ptr<ResultLogger> logger, + OptimizationGuideModelExecutionResultStreamingCallback callback, + base::OnceCallback<void(bool)> cleanup_callback) + : feature_(feature), + opts_(std::move(opts)), + execute_remote_fn_(execute_remote_fn), + last_message_(std::move(message)), + histogram_logger_(std::move(logger)), + callback_(std::move(callback)), + cleanup_callback_(std::move(cleanup_callback)), + receiver_(this) { + log_.mutable_model_execution_info() + ->mutable_on_device_model_execution_info() + ->add_execution_infos(); + start_ = base::TimeTicks::Now(); + SetExecutionRequest(feature_, log_, *last_message_); + *(log_.mutable_model_execution_info() + ->mutable_on_device_model_execution_info() + ->mutable_model_versions()) = opts_.model_versions; + // Note: if on-device fails for some reason, the result will be changed. + histogram_logger_->set_result(Result::kUsedOnDevice); +} + +OnDeviceExecution::~OnDeviceExecution() { + if (callback_) { + if (histogram_logger_) { + histogram_logger_->set_result(Result::kDestroyedWhileWaitingForResponse); + } + base::UmaHistogramMediumTimes( + base::StrCat({"OptimizationGuide.ModelExecution." + "OnDeviceDestroyedWhileWaitingForResponseTime.", + GetStringNameForModelExecutionFeature(feature_)}), + base::TimeTicks::Now() - start_); + } +} + +proto::OnDeviceModelServiceRequest* OnDeviceExecution::MutableLoggedRequest() { + CHECK_GT(log_.model_execution_info() + .on_device_model_execution_info() + .execution_infos_size(), + 0); + return log_.mutable_model_execution_info() + ->mutable_on_device_model_execution_info() + ->mutable_execution_infos(0) + ->mutable_request() + ->mutable_on_device_model_service_request(); +} + +proto::OnDeviceModelServiceResponse* +OnDeviceExecution::MutableLoggedResponse() { + CHECK_GT(log_.model_execution_info() + .on_device_model_execution_info() + .execution_infos_size(), + 0); + return log_.mutable_model_execution_info() + ->mutable_on_device_model_execution_info() + ->mutable_execution_infos(0) + ->mutable_response() + ->mutable_on_device_model_service_response(); +} + +void OnDeviceExecution::AddModelExecutionLogs( + google::protobuf::RepeatedPtrField< + proto::InternalOnDeviceModelExecutionInfo> logs) { + log_.mutable_model_execution_info() + ->mutable_on_device_model_execution_info() + ->mutable_execution_infos() + ->MergeFrom(std::move(logs)); +} + +void OnDeviceExecution::Cancel() { + CancelPendingResponse(Result::kCancelled); +} + +void OnDeviceExecution::BeginExecution(OnDeviceContext& context, + const SamplingParams& sampling_params) { + auto input = opts_.adapter->ConstructInputString( + *last_message_, /*want_input_context=*/false); + if (!input) { + FallbackToRemote(Result::kFailedConstructingMessage); + return; + } + + auto* logged_request = MutableLoggedRequest(); + + // Terminate optional context processing and log the context info. + context.CloneSession(session_.BindNewPipeAndPassReceiver(), logged_request, + input->should_ignore_input_context); + + logged_request->set_execution_string(input->ToString()); + // TODO(crbug.com/302327957): Probably do some math to get the accurate number + // here. + logged_request->set_execution_num_tokens_processed( + opts_.token_limits.max_execute_tokens); + LogRequest(opts_.logger.get(), *logged_request); + + auto options = on_device_model::mojom::InputOptions::New(); + options->input = std::move(input->input); + options->max_tokens = opts_.token_limits.max_execute_tokens; + options->ignore_context = input->should_ignore_input_context; + options->max_output_tokens = opts_.token_limits.max_output_tokens; + options->top_k = sampling_params.top_k; + options->temperature = sampling_params.temperature; + + opts_.safety_checker->RunRequestChecks( + *last_message_, + base::BindOnce(&OnDeviceExecution::OnRequestSafetyResult, + weak_ptr_factory_.GetWeakPtr(), std::move(options))); +} + +void OnDeviceExecution::OnRequestSafetyResult( + on_device_model::mojom::InputOptionsPtr options, + SafetyChecker::Result safety_result) { + if (safety_result.failed_to_run) { + FallbackToRemote(Result::kFailedConstructingMessage); + return; + } + // Log the check executions. + AddModelExecutionLogs(std::move(safety_result.logs)); + + // Handle the result. + if (safety_result.is_unsafe || safety_result.is_unsupported_language) { + if (histogram_logger_) { + histogram_logger_->set_result(Result::kRequestUnsafe); + } + if (features::GetOnDeviceModelRetractUnsafeContent()) { + CancelPendingResponse(Result::kRequestUnsafe, + safety_result.is_unsupported_language + ? ModelExecutionError::kUnsupportedLanguage + : ModelExecutionError::kFiltered); + return; + } + } + BeginRequestExecution(std::move(options)); +} + +void OnDeviceExecution::BeginRequestExecution( + on_device_model::mojom::InputOptionsPtr options) { + session_->Execute(std::move(options), receiver_.BindNewPipeAndPassRemote()); + receiver_.set_disconnect_handler(base::BindOnce( + &OnDeviceExecution::OnResponderDisconnect, base::Unretained(this))); +} + +// on_device_model::mojom::StreamingResponder: +void OnDeviceExecution::OnResponse( + on_device_model::mojom::ResponseChunkPtr chunk) { + proto::OnDeviceModelServiceResponse* logged_response = + MutableLoggedResponse(); + + if (current_response_.empty()) { + base::TimeDelta time_to_first_response = base::TimeTicks::Now() - start_; + base::UmaHistogramMediumTimes( + base::StrCat( + {"OptimizationGuide.ModelExecution.OnDeviceFirstResponseTime.", + GetStringNameForModelExecutionFeature(feature_)}), + time_to_first_response); + logged_response->set_time_to_first_response_millis( + time_to_first_response.InMilliseconds()); + } + + current_response_ += chunk->text; + num_unchecked_response_tokens_++; + num_response_tokens_++; + + if (HasRepeatingSuffix(current_response_)) { + // If a repeat is detected, halt the response, and cancel/finish early. + receiver_.reset(); + logged_response->set_has_repeats(true); + if (features::GetOnDeviceModelRetractRepeats()) { + LogRepeatedResponse(opts_.logger.get(), feature_, current_response_); + logged_response->set_status( + proto::ON_DEVICE_MODEL_SERVICE_RESPONSE_STATUS_RETRACTED); + CancelPendingResponse(Result::kResponseHadRepeats, + ModelExecutionError::kFiltered); + return; + } + + // Artificially send the OnComplete event to finish processing. + OnComplete(on_device_model::mojom::ResponseSummary::New()); + return; + } + + if (!opts_.safety_checker->safety_cfg().CanCheckPartialOutput( + num_response_tokens_, num_unchecked_response_tokens_)) { + // Not enough new data to be worth re-evaluating yet. + return; + } + + num_unchecked_response_tokens_ = 0; + RunRawOutputSafetyCheck(ResponseCompleteness::kPartial); +} + +void OnDeviceExecution::OnComplete( + on_device_model::mojom::ResponseSummaryPtr summary) { + receiver_.reset(); // Suppress expected disconnect + + proto::OnDeviceModelServiceResponse* logged_response = + MutableLoggedResponse(); + LogResponseHasRepeats(feature_, logged_response->has_repeats()); + LogResponseCompleteTokens(feature_, num_response_tokens_); + base::TimeDelta time_to_completion = base::TimeTicks::Now() - start_; + LogResponseCompleteTime(feature_, time_to_completion); + logged_response->set_time_to_completion_millis( + time_to_completion.InMilliseconds()); + + opts_.model_client->OnResponseCompleted(); + + RunRawOutputSafetyCheck(ResponseCompleteness::kComplete); +} + +void OnDeviceExecution::OnResponderDisconnect() { + // OnComplete resets the receiver, so this implies that the response is + // incomplete and there was either a service crash or model eviction. + receiver_.reset(); + if (features::GetOnDeviceFallbackToServerOnDisconnect()) { + FallbackToRemote(Result::kDisconnectAndMaybeFallback); + } else { + CancelPendingResponse(Result::kDisconnectAndCancel); + } +} + +void OnDeviceExecution::RunRawOutputSafetyCheck( + ResponseCompleteness completeness) { + opts_.safety_checker->RunRawOutputCheck( + current_response_, completeness, + base::BindOnce(&OnDeviceExecution::OnRawOutputSafetyResult, + weak_ptr_factory_.GetWeakPtr(), current_response_.size(), + completeness)); +} + +void OnDeviceExecution::OnRawOutputSafetyResult( + size_t raw_output_size, + ResponseCompleteness completeness, + SafetyChecker::Result safety_result) { + if (safety_result.failed_to_run) { + FallbackToRemote(Result::kFailedConstructingMessage); + return; + } + if (safety_result.is_unsafe || safety_result.is_unsupported_language) { + if (histogram_logger_) { + histogram_logger_->set_result(Result::kUsedOnDeviceOutputUnsafe); + } + AddModelExecutionLogs(std::move(safety_result.logs)); + if (features::GetOnDeviceModelRetractUnsafeContent()) { + CancelPendingResponse(Result::kUsedOnDeviceOutputUnsafe, + safety_result.is_unsupported_language + ? ModelExecutionError::kUnsupportedLanguage + : ModelExecutionError::kFiltered); + + return; + } + } + if (completeness == ResponseCompleteness::kComplete) { + AddModelExecutionLogs(std::move(safety_result.logs)); + } + latest_safe_raw_output_.length = raw_output_size; + MaybeParseResponse(completeness); +} + +void OnDeviceExecution::MaybeParseResponse(ResponseCompleteness completeness) { + if (completeness == ResponseCompleteness::kPartial && + features::ShouldUseTextSafetyRemoteFallbackForEligibleFeatures()) { + // We don't send streaming responses in this mode. + return; + } + + if (!opts_.adapter->ShouldParseResponse(completeness)) { + return; + } + + std::string safe_response = + current_response_.substr(0, latest_safe_raw_output_.length); + LogRawResponse(opts_.logger.get(), feature_, safe_response); + MutableLoggedResponse()->set_output_string(safe_response); + size_t previous_response_pos = latest_response_pos_; + latest_response_pos_ = latest_safe_raw_output_.length; + opts_.adapter->ParseResponse( + *last_message_, safe_response, previous_response_pos, + base::BindOnce(&OnDeviceExecution::OnParsedResponse, + weak_ptr_factory_.GetWeakPtr(), completeness)); +} + +void OnDeviceExecution::OnParsedResponse( + ResponseCompleteness completeness, + base::expected<proto::Any, ResponseParsingError> output) { + if (!output.has_value()) { + switch (output.error()) { + case ResponseParsingError::kRejectedPii: + MutableLoggedResponse()->set_status( + proto::ON_DEVICE_MODEL_SERVICE_RESPONSE_STATUS_RETRACTED); + CancelPendingResponse(Result::kContainedPII, + ModelExecutionError::kFiltered); + return; + case ResponseParsingError::kFailed: + CancelPendingResponse(Result::kFailedConstructingResponseMessage, + ModelExecutionError::kGenericFailure); + return; + } + } + opts_.safety_checker->RunResponseChecks( + *last_message_, *output, completeness, + base::BindOnce(&OnDeviceExecution::OnResponseSafetyResult, + weak_ptr_factory_.GetWeakPtr(), completeness, *output)); +} + +void OnDeviceExecution::OnResponseSafetyResult( + ResponseCompleteness completeness, + proto::Any output, + SafetyChecker::Result safety_result) { + if (safety_result.failed_to_run) { + FallbackToRemote(Result::kFailedConstructingMessage); + return; + } + if (completeness == ResponseCompleteness::kComplete || + safety_result.is_unsafe || safety_result.is_unsupported_language) { + AddModelExecutionLogs(std::move(safety_result.logs)); + } + if (safety_result.is_unsafe || safety_result.is_unsupported_language) { + if (histogram_logger_) { + histogram_logger_->set_result(Result::kUsedOnDeviceOutputUnsafe); + } + if (features::GetOnDeviceModelRetractUnsafeContent()) { + CancelPendingResponse(Result::kUsedOnDeviceOutputUnsafe, + safety_result.is_unsupported_language + ? ModelExecutionError::kUnsupportedLanguage + : ModelExecutionError::kFiltered); + + return; + } + } + if (completeness == ResponseCompleteness::kPartial) { + SendPartialResponseCallback(output); + return; + } + + if (features::ShouldUseTextSafetyRemoteFallbackForEligibleFeatures()) { + RunTextSafetyRemoteFallback(std::move(output)); + return; + } + + SendSuccessCompletionCallback(output); +} + +void OnDeviceExecution::RunTextSafetyRemoteFallback( + proto::Any success_response_metadata) { + auto ts_request = opts_.adapter->ConstructTextSafetyRequest( + *last_message_, current_response_); + if (!ts_request) { + CancelPendingResponse(Result::kFailedConstructingRemoteTextSafetyRequest, + ModelExecutionError::kGenericFailure); + return; + } + + proto::InternalOnDeviceModelExecutionInfo remote_ts_model_execution_info; + auto* ts_request_log = remote_ts_model_execution_info.mutable_request() + ->mutable_text_safety_model_request(); + ts_request_log->set_text(ts_request->text()); + ts_request_log->set_url(ts_request->url()); + + execute_remote_fn_.Run( + ModelBasedCapabilityKey::kTextSafety, *ts_request, std::nullopt, + /*log_ai_data_request=*/nullptr, + base::BindOnce(&OnDeviceExecution::OnTextSafetyRemoteResponse, + weak_ptr_factory_.GetWeakPtr(), + std::move(remote_ts_model_execution_info), + std::move(success_response_metadata))); +} + +void OnDeviceExecution::OnTextSafetyRemoteResponse( + proto::InternalOnDeviceModelExecutionInfo remote_ts_model_execution_info, + proto::Any success_response_metadata, + OptimizationGuideModelExecutionResult result, + std::unique_ptr<ModelQualityLogEntry> remote_log_entry) { + bool is_unsafe = + !result.response.has_value() && + result.response.error().error() == + OptimizationGuideModelExecutionError::ModelExecutionError::kFiltered; + if (remote_log_entry) { + auto* ts_response_log = remote_ts_model_execution_info.mutable_response() + ->mutable_text_safety_model_response(); + ts_response_log->set_server_execution_id( + remote_log_entry->model_execution_id()); + ts_response_log->set_is_unsafe(is_unsafe); + } + *(log_.mutable_model_execution_info() + ->mutable_on_device_model_execution_info() + ->add_execution_infos()) = remote_ts_model_execution_info; + + if (is_unsafe) { + CancelPendingResponse(Result::kUsedOnDeviceOutputUnsafe, + ModelExecutionError::kFiltered); + return; + } + + if (!result.response.has_value()) { + CancelPendingResponse(Result::kTextSafetyRemoteRequestFailed, + ModelExecutionError::kGenericFailure); + return; + } + + SendSuccessCompletionCallback(success_response_metadata); +} + +void OnDeviceExecution::FallbackToRemote(Result result) { + if (histogram_logger_) { + histogram_logger_->set_result(result); + } + auto self = weak_ptr_factory_.GetWeakPtr(); + execute_remote_fn_.Run( + feature_, *last_message_, std::nullopt, + std::make_unique<proto::LogAiDataRequest>(std::move(log_)), + base::BindOnce(&InvokeStreamingCallbackWithRemoteResult, + std::move(callback_))); + if (self) { + self->Cleanup(/*healthy=*/false); + } +} + +void OnDeviceExecution::CancelPendingResponse(Result result, + ModelExecutionError error) { + if (!callback_) { + return; + } + if (histogram_logger_) { + histogram_logger_->set_result(result); + } + OptimizationGuideModelExecutionError og_error = + OptimizationGuideModelExecutionError::FromModelExecutionError(error); + std::unique_ptr<ModelQualityLogEntry> log_entry; + std::unique_ptr<proto::ModelExecutionInfo> model_execution_info; + if (og_error.ShouldLogModelQuality()) { + log_entry = std::make_unique<ModelQualityLogEntry>(opts_.log_uploader); + log_entry->log_ai_data_request()->MergeFrom(log_); + std::string model_execution_id = GenerateExecutionId(); + log_entry->set_model_execution_id(model_execution_id); + model_execution_info = std::make_unique<proto::ModelExecutionInfo>( + log_entry->log_ai_data_request()->model_execution_info()); + model_execution_info->set_execution_id(model_execution_id); + model_execution_info->set_model_execution_error_enum( + static_cast<uint32_t>(og_error.error())); + } + auto self = weak_ptr_factory_.GetWeakPtr(); + std::move(callback_).Run(OptimizationGuideModelStreamingExecutionResult( + base::unexpected(og_error), /*provided_by_on_device=*/true, + std::move(log_entry), std::move(model_execution_info))); + if (self) { + self->Cleanup(/*healthy=*/true); + } +} + +void OnDeviceExecution::SendPartialResponseCallback( + const proto::Any& success_response_metadata) { + callback_.Run(OptimizationGuideModelStreamingExecutionResult( + base::ok(StreamingResponse{.response = success_response_metadata, + .is_complete = false}), + /*provided_by_on_device=*/true, /*log_entry=*/nullptr)); +} + +void OnDeviceExecution::SendSuccessCompletionCallback( + const proto::Any& success_response_metadata) { + // Complete the log entry and promise it to the ModelQualityUploaderService. + std::unique_ptr<ModelQualityLogEntry> log_entry; + std::unique_ptr<proto::ModelExecutionInfo> model_execution_info; + SetExecutionResponse(feature_, log_, success_response_metadata); + MutableLoggedResponse()->set_status( + proto::ON_DEVICE_MODEL_SERVICE_RESPONSE_STATUS_SUCCESS); + log_entry = std::make_unique<ModelQualityLogEntry>(opts_.log_uploader); + log_entry->log_ai_data_request()->MergeFrom(log_); + std::string model_execution_id = GenerateExecutionId(); + log_entry->set_model_execution_id(model_execution_id); + model_execution_info = + std::make_unique<proto::ModelExecutionInfo>(log_.model_execution_info()); + model_execution_info->set_execution_id(model_execution_id); + log_.Clear(); + + // Return the execution response. + auto self = weak_ptr_factory_.GetWeakPtr(); + std::move(callback_).Run(OptimizationGuideModelStreamingExecutionResult( + base::ok(StreamingResponse{.response = success_response_metadata, + .is_complete = true}), + /*provided_by_on_device=*/true, std::move(log_entry), + std::move(model_execution_info))); + if (self) { + self->Cleanup(/*healthy=*/true); + } +} + +void OnDeviceExecution::Cleanup(bool healthy) { + weak_ptr_factory_.InvalidateWeakPtrs(); + session_.reset(); + receiver_.reset(); + callback_.Reset(); + log_.Clear(); + current_response_.clear(); + histogram_logger_.reset(); + std::move(cleanup_callback_).Run(healthy); +} + +OnDeviceExecution::SafeRawOutput::SafeRawOutput() = default; +OnDeviceExecution::SafeRawOutput::~SafeRawOutput() = default; + +OnDeviceExecution::ResultLogger::~ResultLogger() { + base::UmaHistogramEnumeration( + base::StrCat( + {"OptimizationGuide.ModelExecution.OnDeviceExecuteModelResult.", + GetStringNameForModelExecutionFeature(feature_)}), + result_); +} + +} // namespace optimization_guide
diff --git a/components/optimization_guide/core/model_execution/on_device_execution.h b/components/optimization_guide/core/model_execution/on_device_execution.h new file mode 100644 index 0000000..5be081c2 --- /dev/null +++ b/components/optimization_guide/core/model_execution/on_device_execution.h
@@ -0,0 +1,271 @@ +// Copyright 2025 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_OPTIMIZATION_GUIDE_CORE_MODEL_EXECUTION_ON_DEVICE_EXECUTION_H_ +#define COMPONENTS_OPTIMIZATION_GUIDE_CORE_MODEL_EXECUTION_ON_DEVICE_EXECUTION_H_ + +#include <memory> +#include <optional> +#include <string> +#include <vector> + +#include "base/functional/callback_forward.h" +#include "base/memory/scoped_refptr.h" +#include "base/memory/weak_ptr.h" +#include "base/timer/timer.h" +#include "components/optimization_guide/core/model_execution/feature_keys.h" +#include "components/optimization_guide/core/model_execution/on_device_context.h" +#include "components/optimization_guide/core/model_execution/on_device_model_feature_adapter.h" +#include "components/optimization_guide/core/model_execution/optimization_guide_model_execution_error.h" +#include "components/optimization_guide/core/model_execution/safety_checker.h" +#include "components/optimization_guide/core/model_execution/substitution.h" +#include "components/optimization_guide/core/model_quality/model_quality_logs_uploader_service.h" +#include "components/optimization_guide/core/optimization_guide_model_executor.h" +#include "components/optimization_guide/proto/model_quality_metadata.pb.h" +#include "components/optimization_guide/proto/model_quality_service.pb.h" +#include "components/optimization_guide/proto/text_safety_model_metadata.pb.h" +#include "mojo/public/cpp/bindings/pending_remote.h" +#include "mojo/public/cpp/bindings/receiver.h" +#include "mojo/public/cpp/bindings/remote.h" +#include "services/on_device_model/public/mojom/on_device_model.mojom.h" + +namespace optimization_guide { + +using ExecuteRemoteFn = base::RepeatingCallback<void( + ModelBasedCapabilityKey feature, + const google::protobuf::MessageLite&, + std::optional<base::TimeDelta> timeout, + std::unique_ptr<proto::LogAiDataRequest>, + OptimizationGuideModelExecutionResultCallback)>; + +void InvokeStreamingCallbackWithRemoteResult( + OptimizationGuideModelExecutionResultStreamingCallback callback, + OptimizationGuideModelExecutionResult result, + std::unique_ptr<ModelQualityLogEntry> log_entry); + +// The state for an ongoing ExecuteModel() call. +class OnDeviceExecution final + : public on_device_model::mojom::StreamingResponder { + public: + // Possible outcomes of ExecuteModel(). + // These values are persisted to logs. Entries should not be renumbered and + // numeric values should never be reused. + enum class Result { + // On-device was not used. + kOnDeviceNotUsed = 0, + // On-device was used, and it completed successfully. + kUsedOnDevice = 1, + // Failed constructing message, and used server. + kFailedConstructingMessage = 2, + // Got a response from on-device, but failed constructing the message. + kFailedConstructingResponseMessage = 3, + // Timed out and used server. + kTimedOut = 4, + // Received a disconnect while waiting for response. This may trigger + // fallback to another model, e.g. on the server, if configured. + kDisconnectAndMaybeFallback = 5, + // Received a disconnect while waiting for response and cancelled. + kDisconnectAndCancel = 6, + // Response was cancelled because ExecuteModel() was called while waiting + // for response. + kCancelled = 7, + // SessionImpl was destroyed while waiting for a response. + kDestroyedWhileWaitingForResponse = 8, + // On-device was used, it completed successfully, but the output is + // considered unsafe. + kUsedOnDeviceOutputUnsafe = 9, + // On-device was used, but the output was rejected (because contained PII). + kContainedPII = 10, + // On-device was used, but the output was rejected because it had repeats. + kResponseHadRepeats = 11, + // On-device was used and the output was complete but the output was + // rejected since it did not have the required safety scores. + kResponseCompleteButNoRequiredSafetyScores = 12, + // On-device was used and completed successfully, but the output was not in + // a language that could be reliably evaluated for safety. + kUsedOnDeviceOutputUnsupportedLanguage = 13, + // On-device was used and completed successfully, but failed constructing + // the text safety remote request. + kFailedConstructingRemoteTextSafetyRequest = 14, + // On-device was used and completed successfully, but the text safety remote + // request failed for some reason. + kTextSafetyRemoteRequestFailed = 15, + // On-device was used, but the request was considered unsafe. + kRequestUnsafe = 16, + + // Please update OptimizationGuideOnDeviceResult in + // optimization/enums.xml. + kMaxValue = kRequestUnsafe, + }; + + // Used to log the result of ExecuteModel. + class ResultLogger { + public: + explicit ResultLogger(ModelBasedCapabilityKey feature) + : feature_(feature) {} + ~ResultLogger(); + + void set_result(Result result) { result_ = result; } + + private: + const ModelBasedCapabilityKey feature_; + Result result_ = Result::kOnDeviceNotUsed; + }; + + explicit OnDeviceExecution( + ModelBasedCapabilityKey feature, + OnDeviceOptions opts, + ExecuteRemoteFn execute_remote_fn, + std::unique_ptr<google::protobuf::MessageLite> message, + std::unique_ptr<ResultLogger> logger, + OptimizationGuideModelExecutionResultStreamingCallback callback, + base::OnceCallback<void(bool)> cleanup_callback); + ~OnDeviceExecution() final; + + // Begin processing the request. + void BeginExecution(OnDeviceContext& context, + const SamplingParams& sampling_params); + + // Cancels the execution. + void Cancel(); + + private: + // Returns the mutable on-device model service request for logging. + proto::OnDeviceModelServiceRequest* MutableLoggedRequest(); + + // Returns the mutable on-device model service response for logging. + proto::OnDeviceModelServiceResponse* MutableLoggedResponse(); + + // Adds a collection of model execution logs to the request log. + void AddModelExecutionLogs(google::protobuf::RepeatedPtrField< + proto::InternalOnDeviceModelExecutionInfo> logs); + + // Callback invoked with RequestSafetyCheck result. + // Calls BeginRequestExecution if safety checks pass. + void OnRequestSafetyResult(on_device_model::mojom::InputOptionsPtr options, + SafetyChecker::Result safety_result); + + // Begins request execution (leads to OnResponse/OnComplete, which will + // call RunRawOutputSafetyCheck). + void BeginRequestExecution(on_device_model::mojom::InputOptionsPtr options); + + // on_device_model::mojom::StreamingResponder: + void OnResponse(on_device_model::mojom::ResponseChunkPtr chunk) override; + void OnComplete(on_device_model::mojom::ResponseSummaryPtr summary) override; + void OnResponderDisconnect(); + + // Evaluates raw output safety (leads to OnRawOutputSafetyResult). + void RunRawOutputSafetyCheck(ResponseCompleteness completeness); + + // Called when output safety check completes. + // Calls MaybeParseResponse when there is more safe output. + void OnRawOutputSafetyResult(size_t raw_output_size, + ResponseCompleteness completeness, + SafetyChecker::Result safety_result); + + // Called to parse the latest safe raw output. + // Leads to OnParsedResponse. + void MaybeParseResponse(ResponseCompleteness completeness); + + // Called when a response has finished parsing. + // Begins response safety evaluation, leads to OnResponseSafetyResult. + void OnParsedResponse( + ResponseCompleteness completeness, + base::expected<proto::Any, ResponseParsingError> output); + + // Called when response safety check completes. + // Either fails, sends the result or calls RunTextSafetyRemoteFallback. + void OnResponseSafetyResult(ResponseCompleteness completeness, + proto::Any output, + SafetyChecker::Result safety_result); + + // Called to run the text safety remote fallback. Will invoke + // OnTextSafetyRemoteResponse when done. + void RunTextSafetyRemoteFallback(proto::Any success_response_metadata); + + // Callback invoked when the text safety remote fallback response comes + // back. Will invoke the session's completion callback and destroy state. + void OnTextSafetyRemoteResponse( + proto::InternalOnDeviceModelExecutionInfo remote_ts_model_execution_info, + proto::Any success_response_metadata, + OptimizationGuideModelExecutionResult result, + std::unique_ptr<ModelQualityLogEntry> remote_log_entry); + + // Terminates on-device processing as unhealthy and falls back to remote + // execution to provide the result to the caller. + void FallbackToRemote(Result result); + + // Sends an error result and terminates on-device processing as healthy. + void CancelPendingResponse( + Result result, + OptimizationGuideModelExecutionError::ModelExecutionError error = + OptimizationGuideModelExecutionError::ModelExecutionError:: + kCancelled); + + // Sends the partial response callback, and does NOT terminate processing. + void SendPartialResponseCallback(const proto::Any& success_response_metadata); + + // Sends a successful result and terminates on-device processing as healthy. + void SendSuccessCompletionCallback( + const proto::Any& success_response_metadata); + + // Called after terminating to release all held resources and notify owner + // that this object is safe to destroy. + void Cleanup(bool healthy); + + const ModelBasedCapabilityKey feature_; + const OnDeviceOptions opts_; + ExecuteRemoteFn execute_remote_fn_; + + mojo::Remote<on_device_model::mojom::Session> session_; + + // The request message. + std::unique_ptr<google::protobuf::MessageLite> last_message_; + // Time ExecuteModel() was called. + base::TimeTicks start_; + // Used to log the result of ExecuteModel(). + std::unique_ptr<ResultLogger> histogram_logger_; + // Used to log execution information for the request. + proto::LogAiDataRequest log_; + + // Response received so far. + std::string current_response_; + + // How many tokens (response chunks) have been added. + size_t num_response_tokens_ = 0; + // How many tokens (response chunks) have been added since the last safety + // evaluation was requested. + size_t num_unchecked_response_tokens_ = 0; + + struct SafeRawOutput { + SafeRawOutput(); + ~SafeRawOutput(); + // How much of 'current_response' was checked. + size_t length = 0; + }; + // The longest response that has passed the raw output text safety check. + SafeRawOutput latest_safe_raw_output_; + + // The last position in the response that has been streamed to the + // responder. + size_t latest_response_pos_ = 0; + + // Callback to provide the execution result. + OptimizationGuideModelExecutionResultStreamingCallback callback_; + + // Callback to notify the owning session that on-device execution has + // terminated, and that this object is safe to destroy. + // Should pass true to indicate healthy completion, or false if unhealthy. + base::OnceCallback<void(bool)> cleanup_callback_; + + mojo::Receiver<on_device_model::mojom::StreamingResponder> receiver_; + + // Factory for weak pointers related to this session that are invalidated + // with the request state. + base::WeakPtrFactory<OnDeviceExecution> weak_ptr_factory_{this}; +}; + +} // namespace optimization_guide + +#endif // COMPONENTS_OPTIMIZATION_GUIDE_CORE_MODEL_EXECUTION_ON_DEVICE_EXECUTION_H_
diff --git a/components/optimization_guide/core/model_execution/on_device_model_service_controller.cc b/components/optimization_guide/core/model_execution/on_device_model_service_controller.cc index 2211633..72c0fbf 100644 --- a/components/optimization_guide/core/model_execution/on_device_model_service_controller.cc +++ b/components/optimization_guide/core/model_execution/on_device_model_service_controller.cc
@@ -177,7 +177,7 @@ auto* adaptation_metadata = GetFeatureMetadata(feature); CHECK(adaptation_metadata); - SessionImpl::OnDeviceOptions opts; + OnDeviceOptions opts; opts.model_client = std::make_unique<OnDeviceModelClient>( feature, weak_ptr_factory_.GetWeakPtr(), model_paths, base::OptionalFromPtr(adaptation_metadata->asset_paths())); @@ -445,7 +445,7 @@ OnDeviceModelServiceController::OnDeviceModelClient::~OnDeviceModelClient() = default; -std::unique_ptr<SessionImpl::OnDeviceModelClient> +std::unique_ptr<OnDeviceOptions::Client> OnDeviceModelServiceController::OnDeviceModelClient::Clone() const { return std::make_unique<OnDeviceModelServiceController::OnDeviceModelClient>( feature_, controller_, model_paths_, adaptation_assets_);
diff --git a/components/optimization_guide/core/model_execution/on_device_model_service_controller.h b/components/optimization_guide/core/model_execution/on_device_model_service_controller.h index ee90e780..3dcf0e2 100644 --- a/components/optimization_guide/core/model_execution/on_device_model_service_controller.h +++ b/components/optimization_guide/core/model_execution/on_device_model_service_controller.h
@@ -143,7 +143,7 @@ } private: - class OnDeviceModelClient final : public SessionImpl::OnDeviceModelClient { + class OnDeviceModelClient final : public OnDeviceOptions::Client { public: OnDeviceModelClient( ModelBasedCapabilityKey feature, @@ -152,7 +152,7 @@ base::optional_ref<const on_device_model::AdaptationAssetPaths> adaptation_assets); ~OnDeviceModelClient() override; - std::unique_ptr<SessionImpl::OnDeviceModelClient> Clone() const override; + std::unique_ptr<OnDeviceOptions::Client> Clone() const override; bool ShouldUse() override; mojo::Remote<on_device_model::mojom::OnDeviceModel>& GetModelRemote() override;
diff --git a/components/optimization_guide/core/model_execution/on_device_model_service_controller_unittest.cc b/components/optimization_guide/core/model_execution/on_device_model_service_controller_unittest.cc index 2b6ab58..170ab09d 100644 --- a/components/optimization_guide/core/model_execution/on_device_model_service_controller_unittest.cc +++ b/components/optimization_guide/core/model_execution/on_device_model_service_controller_unittest.cc
@@ -26,6 +26,7 @@ #include "components/optimization_guide/core/model_execution/feature_keys.h" #include "components/optimization_guide/core/model_execution/model_execution_features.h" #include "components/optimization_guide/core/model_execution/model_execution_prefs.h" +#include "components/optimization_guide/core/model_execution/on_device_execution.h" #include "components/optimization_guide/core/model_execution/on_device_model_access_controller.h" #include "components/optimization_guide/core/model_execution/on_device_model_adaptation_loader.h" #include "components/optimization_guide/core/model_execution/on_device_model_execution_proto_value_utils.h" @@ -65,7 +66,7 @@ namespace { using ::on_device_model::mojom::LoadModelResult; -using ExecuteModelResult = SessionImpl::ExecuteModelResult; +using ExecuteModelResult = ::optimization_guide::OnDeviceExecution::Result; using ::testing::AllOf; using ::testing::ElementsAre; @@ -353,7 +354,7 @@ OptimizationGuideLogger logger_; }; -TEST_F(OnDeviceModelServiceControllerTest, ScoreNullBeforeContext) { +TEST_F(OnDeviceModelServiceControllerTest, ScoreBeforeContext) { Initialize(standard_assets_); base::HistogramTester histogram_tester; @@ -361,7 +362,7 @@ EXPECT_TRUE(session); base::test::TestFuture<std::optional<float>> score_future; session->Score("token", score_future.GetCallback()); - EXPECT_EQ(score_future.Get(), std::nullopt); + EXPECT_NE(score_future.Get(), std::nullopt); } TEST_F(OnDeviceModelServiceControllerTest, ScorePresentAfterContext) { @@ -378,7 +379,7 @@ EXPECT_EQ(score_future.Get(), 0.5); } -TEST_F(OnDeviceModelServiceControllerTest, ScoreNullAfterExecute) { +TEST_F(OnDeviceModelServiceControllerTest, ScoreAfterExecute) { Initialize(standard_assets_); base::HistogramTester histogram_tester; @@ -391,7 +392,7 @@ base::test::TestFuture<std::optional<float>> score_future; session->Score("token", score_future.GetCallback()); - EXPECT_EQ(score_future.Get(), std::nullopt); + EXPECT_NE(score_future.Get(), std::nullopt); } TEST_F(OnDeviceModelServiceControllerTest, BaseModelExecutionSuccess) { @@ -2071,9 +2072,7 @@ "OptimizationGuide.ModelExecution.OnDeviceExecuteModelResult.Compose", ExecuteModelResult::kFailedConstructingMessage, 1); EXPECT_TRUE(remote_execute_called_); - // We never actually executed the request on-device so it is expected to not - // have created a log entry. - EXPECT_FALSE(log_ai_data_request_passed_to_remote_); + EXPECT_FALSE(log_ai_data_request_passed_to_remote_->compose().has_response()); } TEST_F(OnDeviceModelServiceControllerTest,
diff --git a/components/optimization_guide/core/model_execution/session_impl.cc b/components/optimization_guide/core/model_execution/session_impl.cc index 5428c4e..efc74cf4 100644 --- a/components/optimization_guide/core/model_execution/session_impl.cc +++ b/components/optimization_guide/core/model_execution/session_impl.cc
@@ -9,14 +9,17 @@ #include <string> #include "base/containers/contains.h" +#include "base/functional/bind.h" #include "base/functional/callback.h" #include "base/metrics/histogram_functions.h" #include "base/strings/stringprintf.h" +#include "base/time/time.h" #include "base/timer/elapsed_timer.h" #include "base/token.h" #include "base/uuid.h" #include "components/optimization_guide/core/model_execution/feature_keys.h" #include "components/optimization_guide/core/model_execution/model_execution_util.h" +#include "components/optimization_guide/core/model_execution/on_device_execution.h" #include "components/optimization_guide/core/model_execution/on_device_model_access_controller.h" #include "components/optimization_guide/core/model_execution/on_device_model_feature_adapter.h" #include "components/optimization_guide/core/model_execution/on_device_model_service_controller.h" @@ -30,6 +33,7 @@ #include "components/optimization_guide/core/optimization_guide_model_executor.h" #include "components/optimization_guide/core/optimization_guide_util.h" #include "components/optimization_guide/proto/model_quality_metadata.pb.h" +#include "components/optimization_guide/proto/model_quality_service.pb.h" #include "components/optimization_guide/proto/string_value.pb.h" #include "components/optimization_guide/proto/text_safety_model_metadata.pb.h" #include "mojo/public/cpp/bindings/callback_helpers.h" @@ -52,101 +56,9 @@ } } -void LogRequest(OptimizationGuideLogger* logger, - const proto::OnDeviceModelServiceRequest& logged_request) { - if (logger && logger->ShouldEnableDebugLogs()) { - OPTIMIZATION_GUIDE_LOGGER( - optimization_guide_common::mojom::LogSource::MODEL_EXECUTION, logger) - << "Executing model " - << (logged_request.input_context_string().empty() - ? "" - : base::StringPrintf( - "with input context of %d tokens:\n%s\n", - logged_request.input_context_num_tokens_processed(), - logged_request.input_context_string().c_str())) - << "with string:\n" - << logged_request.execution_string(); - } -} - -void LogRawResponse(OptimizationGuideLogger* logger, - ModelBasedCapabilityKey feature, - const std::string& raw_response) { - if (logger && logger->ShouldEnableDebugLogs()) { - OPTIMIZATION_GUIDE_LOGGER( - optimization_guide_common::mojom::LogSource::MODEL_EXECUTION, logger) - << "Model generates raw response with " - << std::string(GetStringNameForModelExecutionFeature(feature)) << ":\n" - << raw_response; - } -} - -void LogRepeatedResponse(OptimizationGuideLogger* logger, - ModelBasedCapabilityKey feature, - const std::string& repeated_response) { - if (logger && logger->ShouldEnableDebugLogs()) { - OPTIMIZATION_GUIDE_LOGGER( - optimization_guide_common::mojom::LogSource::MODEL_EXECUTION, logger) - << "Model generates repeated response with " - << std::string(GetStringNameForModelExecutionFeature(feature)) << ":\n" - << repeated_response; - } -} - -void LogResponseHasRepeats(ModelBasedCapabilityKey feature, bool has_repeats) { - base::UmaHistogramBoolean( - base::StrCat( - {"OptimizationGuide.ModelExecution.OnDeviceResponseHasRepeats.", - GetStringNameForModelExecutionFeature(feature)}), - has_repeats); -} - -void LogResponseCompleteTime(ModelBasedCapabilityKey feature, - base::TimeDelta time_to_completion) { - base::UmaHistogramMediumTimes( - base::StrCat( - {"OptimizationGuide.ModelExecution.OnDeviceResponseCompleteTime.", - GetStringNameForModelExecutionFeature(feature)}), - time_to_completion); -} - -void LogResponseCompleteTokens(ModelBasedCapabilityKey feature, - uint32_t tokens) { - base::UmaHistogramCounts10000( - base::StrCat( - {"OptimizationGuide.ModelExecution.OnDeviceResponseCompleteTokens.", - GetStringNameForModelExecutionFeature(feature)}), - tokens); -} - -std::string GenerateExecutionId() { - return "on-device:" + base::Uuid::GenerateRandomV4().AsLowercaseString(); -} - -void InvokeStreamingCallbackWithRemoteResult( - OptimizationGuideModelExecutionResultStreamingCallback callback, - OptimizationGuideModelExecutionResult result, - std::unique_ptr<ModelQualityLogEntry> log_entry) { - OptimizationGuideModelStreamingExecutionResult streaming_result; - if (log_entry && log_entry->log_ai_data_request() && - log_entry->log_ai_data_request()->has_model_execution_info()) { - streaming_result.execution_info = - std::make_unique<proto::ModelExecutionInfo>( - log_entry->log_ai_data_request()->model_execution_info()); - } - streaming_result.log_entry = std::move(log_entry); - if (result.response.has_value()) { - streaming_result.response = base::ok( - StreamingResponse{.response = *result.response, .is_complete = true}); - } else { - streaming_result.response = base::unexpected(result.response.error()); - } - callback.Run(std::move(streaming_result)); -} - SamplingParams ResolveSamplingParams( const std::optional<SessionConfigParams>& config_params, - const std::optional<SessionImpl::OnDeviceOptions>& on_device_opts) { + const std::optional<OnDeviceOptions>& on_device_opts) { if (config_params && config_params->sampling_params) { return config_params->sampling_params.value(); } @@ -168,103 +80,6 @@ } // namespace -// Handles incrementally processing context. After the min context size has been -// processed, any pending context processing will be cancelled if an -// ExecuteModel() call is made. -class SessionImpl::ContextProcessor - : public on_device_model::mojom::ContextClient { - public: - ContextProcessor(SessionImpl& session, on_device_model::mojom::InputPtr input) - : session_(session), input_(std::move(input)) { - int min_context = features::GetOnDeviceModelMinTokensForContext(); - if (min_context > 0) { - AddContext(min_context); - } else { - // If no min context is required, start processing the context as - // optional. - OnComplete(0); - } - } - - // on_device_model::mojom::ContextClient: - void OnComplete(uint32_t tokens_processed) override { - tokens_processed_ += tokens_processed; - - if (has_cancelled_) { - return; - } - - // This means input has been fully processed. - if (tokens_processed < expected_tokens_) { - finished_processing_ = true; - return; - } - - // Once the initial context is complete, we can cancel future context - // processing. - can_cancel_ = true; - if (tokens_processed_ < session_->GetTokenLimits().max_context_tokens) { - AddContext(features::GetOnDeviceModelContextTokenChunkSize()); - } - } - - // Returns whether the full context was processed. - bool MaybeCancelProcessing() { - has_cancelled_ = true; - if (can_cancel_) { - client_.reset(); - } - return finished_processing_; - } - - std::string input() { return OnDeviceInputToString(*input_); } - - uint32_t tokens_processed() const { return tokens_processed_; } - - private: - void AddContext(uint32_t num_tokens) { - expected_tokens_ = num_tokens; - client_.reset(); - if (!session_->ShouldUseOnDeviceModel()) { - return; - } - auto options = on_device_model::mojom::InputOptions::New(); - options->input = input_.Clone(); - options->max_tokens = num_tokens; - options->token_offset = tokens_processed_; - session_->GetOrCreateSession().AddContext( - std::move(options), client_.BindNewPipeAndPassRemote()); - } - - raw_ref<SessionImpl> session_; - on_device_model::mojom::InputPtr input_; - bool finished_processing_ = false; - uint32_t expected_tokens_ = 0; - uint32_t tokens_processed_ = 0; - bool can_cancel_ = false; - bool has_cancelled_ = false; - mojo::Receiver<on_device_model::mojom::ContextClient> client_{this}; -}; - -SessionImpl::OnDeviceModelClient::~OnDeviceModelClient() = default; - -SessionImpl::OnDeviceOptions::OnDeviceOptions() = default; -SessionImpl::OnDeviceOptions::OnDeviceOptions(OnDeviceOptions&&) = default; -SessionImpl::OnDeviceOptions::~OnDeviceOptions() = default; - -SessionImpl::OnDeviceOptions::OnDeviceOptions(const OnDeviceOptions& orig) - : model_client(orig.model_client->Clone()), - model_versions(orig.model_versions), - adapter(orig.adapter), - safety_checker(std::make_unique<SafetyChecker>(*orig.safety_checker)), - token_limits(orig.token_limits), - logger(orig.logger), - log_uploader(orig.log_uploader) {} - -bool SessionImpl::OnDeviceOptions::ShouldUse() const { - return model_client->ShouldUse(); -} - SessionImpl::SessionImpl( ModelBasedCapabilityKey feature, std::optional<OnDeviceOptions> on_device_opts, @@ -274,34 +89,22 @@ execute_remote_fn_(std::move(execute_remote_fn)), sampling_params_(ResolveSamplingParams(config_params, on_device_opts)) { if (on_device_opts && on_device_opts->ShouldUse()) { - on_device_state_.emplace(std::move(*on_device_opts), this); + LogSessionCreation(on_device_opts->logger.get(), feature_); + on_device_context_ = + std::make_unique<OnDeviceContext>(std::move(*on_device_opts), feature_); // Prewarm the initial session to make sure the service is started. - GetOrCreateSession(); - LogSessionCreation(on_device_state_->opts.logger.get(), feature_); + on_device_context_->GetOrCreateSession(); } } -SessionImpl::~SessionImpl() { - if (on_device_state_ && - on_device_state_->did_execute_and_waiting_for_on_complete()) { - if (on_device_state_->histogram_logger) { - on_device_state_->histogram_logger->set_result( - ExecuteModelResult::kDestroyedWhileWaitingForResponse); - } - base::UmaHistogramMediumTimes( - base::StrCat({"OptimizationGuide.ModelExecution." - "OnDeviceDestroyedWhileWaitingForResponseTime.", - GetStringNameForModelExecutionFeature(feature_)}), - base::TimeTicks::Now() - on_device_state_->start); - } -} +SessionImpl::~SessionImpl() {} const TokenLimits& SessionImpl::GetTokenLimits() const { - if (!on_device_state_) { + if (!on_device_context_) { static const TokenLimits null_limits{}; return null_limits; } - return on_device_state_->opts.token_limits; + return on_device_context_->opts().token_limits; } void SessionImpl::AddContext( @@ -321,47 +124,32 @@ context_start_time_ = base::TimeTicks::Now(); // Cancel any pending response. - CancelPendingResponse(ExecuteModelResult::kCancelled); + if (on_device_execution_) { + on_device_execution_->Cancel(); + } if (!ShouldUseOnDeviceModel()) { DestroyOnDeviceState(); return AddContextResult::kUsingServer; } - on_device_state_->add_context_before_execute = false; - auto input = on_device_state_->opts.adapter->ConstructInputString( - *context_, /*want_input_context=*/true); - if (!input) { + if (!on_device_context_->SetInput(*context_)) { // Use server if can't construct input. DestroyOnDeviceState(); return AddContextResult::kFailedConstructingInput; } - // Only the latest context is used, so restart the mojo session here. - on_device_state_->session.reset(); - - // As the session was just destroyed, clear the contextprocessor as - // it will be using the wrong session, and we don't care about old context - // at this point. - on_device_state_->context_processor.reset(); - - on_device_state_->context_processor = - std::make_unique<ContextProcessor>(*this, std::move(input->input)); return AddContextResult::kUsingOnDevice; } void SessionImpl::Score(const std::string& text, OptimizationGuideModelScoreCallback callback) { // Fail if not using on device, or no session was started yet. - if (!on_device_state_ || !on_device_state_->session || - // Fail if context is incomplete - on_device_state_->add_context_before_execute || - // Fail if execute was called - context_start_time_ == base::TimeTicks()) { + if (!on_device_context_ || !on_device_context_->CanUse()) { std::move(callback).Run(std::nullopt); return; } - on_device_state_->session->Score( + on_device_context_->GetOrCreateSession()->Score( text, base::BindOnce([](float score) { return std::optional<float>(score); }) .Then(mojo::WrapCallbackWithDefaultInvokeIfNotRun(std::move(callback), @@ -372,489 +160,63 @@ const google::protobuf::MessageLite& request_metadata, optimization_guide::OptimizationGuideModelExecutionResultStreamingCallback callback) { - std::unique_ptr<ExecuteModelHistogramLogger> logger = - std::make_unique<ExecuteModelHistogramLogger>(feature_); - last_message_ = MergeContext(request_metadata); + auto logger = std::make_unique<OnDeviceExecution::ResultLogger>(feature_); - auto log_ai_data_request = std::make_unique<proto::LogAiDataRequest>(); - SetExecutionRequest(feature_, *log_ai_data_request, *last_message_); - proto::OnDeviceModelServiceRequest* logged_request = - log_ai_data_request->mutable_model_execution_info() - ->mutable_on_device_model_execution_info() - ->add_execution_infos() - ->mutable_request() - ->mutable_on_device_model_service_request(); - + // Compute the amount of time context would have for processing assuming + // on-device execution. + base::TimeDelta context_start_to_execution = base::TimeDelta::Min(); if (context_start_time_ != base::TimeTicks()) { - base::TimeDelta context_start_to_execution = - base::TimeTicks::Now() - context_start_time_; + context_start_to_execution = base::TimeTicks::Now() - context_start_time_; base::UmaHistogramLongTimes( base::StrCat( {"OptimizationGuide.ModelExecution.ContextStartToExecutionTime.", GetStringNameForModelExecutionFeature(feature_)}), context_start_to_execution); - logged_request - ->set_time_from_input_context_processed_to_request_initiated_millis( - context_start_to_execution.InMilliseconds()); // Only interested in logging the first request after adding context. context_start_time_ = base::TimeTicks(); } + std::unique_ptr<google::protobuf::MessageLite> merged_request = + MergeContext(request_metadata); + if (!ShouldUseOnDeviceModel()) { - CancelPendingResponse(ExecuteModelResult::kCancelled); DestroyOnDeviceState(); execute_remote_fn_.Run( - feature_, *last_message_, std::nullopt, + feature_, *merged_request, std::nullopt, /*log_ai_data_request=*/nullptr, base::BindOnce(&InvokeStreamingCallbackWithRemoteResult, std::move(callback))); return; } - *(log_ai_data_request->mutable_model_execution_info() - ->mutable_on_device_model_execution_info() - ->mutable_model_versions()) = on_device_state_->opts.model_versions; - - if (on_device_state_->add_context_before_execute) { - CHECK(context_); - std::unique_ptr<google::protobuf::MessageLite> context = - std::move(context_); - // Note that this will CancelPendingResponse, so it must be called before - // switching to the new pending response below. - AddContext(*context); - CHECK(!on_device_state_->add_context_before_execute); + if (on_device_execution_) { + on_device_execution_->Cancel(); } + on_device_execution_.reset(); - // Make sure to cancel any pending response. - CancelPendingResponse(ExecuteModelResult::kCancelled); // Set new pending response. - on_device_state_->histogram_logger = std::move(logger); - on_device_state_->callback = std::move(callback); + on_device_execution_.emplace( + feature_, on_device_context_->opts(), execute_remote_fn_, + std::move(merged_request), std::move(logger), std::move(callback), + base::BindOnce(&SessionImpl::OnDeviceExecutionTerminated, + weak_ptr_factory_.GetWeakPtr())); - auto input = on_device_state_->opts.adapter->ConstructInputString( - *last_message_, /*want_input_context=*/false); - if (!input) { - // Use server if can't construct input. - DestroyOnDeviceStateAndFallbackToRemote( - ExecuteModelResult::kFailedConstructingMessage); - return; - } - - // Cancel any optional context still processing. - if (on_device_state_->context_processor) { - bool finished_processing = - on_device_state_->context_processor->MaybeCancelProcessing(); - base::UmaHistogramCounts10000( - base::StrCat( - {"OptimizationGuide.ModelExecution.OnDeviceContextTokensProcessed.", - GetStringNameForModelExecutionFeature(feature_)}), - on_device_state_->context_processor->tokens_processed()); - base::UmaHistogramBoolean( - base::StrCat({"OptimizationGuide.ModelExecution." - "OnDeviceContextFinishedProcessing.", - GetStringNameForModelExecutionFeature(feature_)}), - finished_processing); - logged_request->set_input_context_num_tokens_processed( - on_device_state_->context_processor->tokens_processed()); - } - - // Note: if on-device fails for some reason, the result will be changed. - on_device_state_->histogram_logger->set_result( - ExecuteModelResult::kUsedOnDevice); - - if (!input->should_ignore_input_context && - on_device_state_->context_processor) { - logged_request->set_input_context_string( - on_device_state_->context_processor->input()); - } - logged_request->set_execution_string(input->ToString()); - // TODO(b/302327957): Probably do some math to get the accurate number here. - logged_request->set_execution_num_tokens_processed( - on_device_state_->opts.token_limits.max_execute_tokens); - LogRequest(on_device_state_->opts.logger.get(), *logged_request); - - on_device_state_->log_ai_data_request = std::move(log_ai_data_request); - on_device_state_->start = base::TimeTicks::Now(); - - auto options = on_device_model::mojom::InputOptions::New(); - options->input = std::move(input->input); - options->max_tokens = on_device_state_->opts.token_limits.max_execute_tokens; - options->ignore_context = input->should_ignore_input_context; - options->max_output_tokens = - on_device_state_->opts.token_limits.max_output_tokens; - options->top_k = sampling_params_.top_k; - options->temperature = sampling_params_.temperature; - - on_device_state_->opts.safety_checker->RunRequestChecks( - *last_message_, - base::BindOnce(&SessionImpl::OnRequestSafetyResult, - on_device_state_->session_weak_ptr_factory_.GetWeakPtr(), - std::move(options))); + on_device_execution_->BeginExecution(*on_device_context_, sampling_params_); } -void SessionImpl::OnRequestSafetyResult( - on_device_model::mojom::InputOptionsPtr options, - SafetyChecker::Result safety_result) { - if (safety_result.failed_to_run) { - DestroyOnDeviceStateAndFallbackToRemote( - ExecuteModelResult::kFailedConstructingMessage); - return; +void SessionImpl::OnDeviceExecutionTerminated(bool healthy) { + on_device_execution_.reset(); + if (!healthy) { + DestroyOnDeviceState(); } - // Log the check executions. - on_device_state_->AddModelExecutionLogs(std::move(safety_result.logs)); - - // Handle the result. - if (safety_result.is_unsafe || safety_result.is_unsupported_language) { - if (on_device_state_->histogram_logger) { - on_device_state_->histogram_logger->set_result( - ExecuteModelResult::kRequestUnsafe); - } - if (features::GetOnDeviceModelRetractUnsafeContent()) { - CancelPendingResponse(ExecuteModelResult::kRequestUnsafe, - safety_result.is_unsupported_language - ? ModelExecutionError::kUnsupportedLanguage - : ModelExecutionError::kFiltered); - return; - } - } - BeginRequestExecution(std::move(options)); -} - -void SessionImpl::BeginRequestExecution( - on_device_model::mojom::InputOptionsPtr options) { - GetOrCreateSession().Execute( - std::move(options), - on_device_state_->receiver.BindNewPipeAndPassRemote()); - on_device_state_->receiver.set_disconnect_handler(base::BindOnce( - &SessionImpl::OnResponderDisconnect, base::Unretained(this))); -} - -void SessionImpl::OnResponderDisconnect() { - // OnComplete resets the receiver, so this implies that the response is - // incomplete and there was either a service crash or model eviction. - on_device_state_->receiver.reset(); - if (features::GetOnDeviceFallbackToServerOnDisconnect()) { - DestroyOnDeviceStateAndFallbackToRemote( - ExecuteModelResult::kDisconnectAndMaybeFallback); - } else { - CancelPendingResponse(ExecuteModelResult::kDisconnectAndCancel); - } -} - -// on_device_model::mojom::StreamingResponder: -void SessionImpl::OnResponse(on_device_model::mojom::ResponseChunkPtr chunk) { - proto::OnDeviceModelServiceResponse* logged_response = - on_device_state_->MutableLoggedResponse(); - - if (on_device_state_->current_response.empty()) { - base::TimeDelta time_to_first_response = - base::TimeTicks::Now() - on_device_state_->start; - base::UmaHistogramMediumTimes( - base::StrCat( - {"OptimizationGuide.ModelExecution.OnDeviceFirstResponseTime.", - GetStringNameForModelExecutionFeature(feature_)}), - time_to_first_response); - logged_response->set_time_to_first_response_millis( - time_to_first_response.InMilliseconds()); - } - - on_device_state_->current_response += chunk->text; - on_device_state_->num_unchecked_response_tokens++; - on_device_state_->num_response_tokens++; - - if (HasRepeatingSuffix(on_device_state_->current_response)) { - // If a repeat is detected, halt the response, and cancel/finish early. - on_device_state_->receiver.reset(); - logged_response->set_has_repeats(true); - if (features::GetOnDeviceModelRetractRepeats()) { - LogRepeatedResponse(on_device_state_->opts.logger.get(), feature_, - on_device_state_->current_response); - logged_response->set_status( - proto::ON_DEVICE_MODEL_SERVICE_RESPONSE_STATUS_RETRACTED); - CancelPendingResponse(ExecuteModelResult::kResponseHadRepeats, - ModelExecutionError::kFiltered); - return; - } - - // Artificially send the OnComplete event to finish processing. - OnComplete(on_device_model::mojom::ResponseSummary::New()); - return; - } - - if (!on_device_state_->opts.safety_checker->safety_cfg() - .CanCheckPartialOutput( - on_device_state_->num_response_tokens, - on_device_state_->num_unchecked_response_tokens)) { - // Not enough new data to be worth re-evaluating yet. - return; - } - - on_device_state_->num_unchecked_response_tokens = 0; - RunRawOutputSafetyCheck(ResponseCompleteness::kPartial); -} - -void SessionImpl::OnComplete( - on_device_model::mojom::ResponseSummaryPtr summary) { - on_device_state_->receiver.reset(); // Suppress expected disconnect - - proto::OnDeviceModelServiceResponse* logged_response = - on_device_state_->MutableLoggedResponse(); - LogResponseHasRepeats(feature_, logged_response->has_repeats()); - LogResponseCompleteTokens(feature_, on_device_state_->num_response_tokens); - base::TimeDelta time_to_completion = - base::TimeTicks::Now() - on_device_state_->start; - LogResponseCompleteTime(feature_, time_to_completion); - logged_response->set_time_to_completion_millis( - time_to_completion.InMilliseconds()); - - on_device_state_->opts.model_client->OnResponseCompleted(); - - on_device_state_->response_completeness = ResponseCompleteness::kComplete; - RunRawOutputSafetyCheck(ResponseCompleteness::kComplete); -} - -void SessionImpl::RunRawOutputSafetyCheck(ResponseCompleteness completeness) { - on_device_state_->opts.safety_checker->RunRawOutputCheck( - on_device_state_->current_response, completeness, - base::BindOnce(&SessionImpl::OnRawOutputSafetyResult, - on_device_state_->session_weak_ptr_factory_.GetWeakPtr(), - on_device_state_->current_response.size(), completeness)); -} - -void SessionImpl::OnRawOutputSafetyResult(size_t raw_output_size, - ResponseCompleteness completeness, - SafetyChecker::Result safety_result) { - if (safety_result.failed_to_run) { - DestroyOnDeviceStateAndFallbackToRemote( - ExecuteModelResult::kFailedConstructingMessage); - return; - } - if (safety_result.is_unsafe || safety_result.is_unsupported_language) { - if (on_device_state_->histogram_logger) { - on_device_state_->histogram_logger->set_result( - ExecuteModelResult::kUsedOnDeviceOutputUnsafe); - } - on_device_state_->AddModelExecutionLogs(std::move(safety_result.logs)); - if (features::GetOnDeviceModelRetractUnsafeContent()) { - CancelPendingResponse(ExecuteModelResult::kUsedOnDeviceOutputUnsafe, - safety_result.is_unsupported_language - ? ModelExecutionError::kUnsupportedLanguage - : ModelExecutionError::kFiltered); - - return; - } - } - if (completeness == ResponseCompleteness::kComplete) { - on_device_state_->AddModelExecutionLogs(std::move(safety_result.logs)); - } - on_device_state_->latest_safe_raw_output.length = raw_output_size; - SendResponse(completeness); -} - -on_device_model::mojom::Session& SessionImpl::GetOrCreateSession() { - CHECK(ShouldUseOnDeviceModel()); - if (!on_device_state_->session) { - on_device_state_->opts.model_client->GetModelRemote()->StartSession( - on_device_state_->session.BindNewPipeAndPassReceiver()); - on_device_state_->session.set_disconnect_handler(base::BindOnce( - &SessionImpl::OnSessionDisconnect, base::Unretained(this))); - } - return *on_device_state_->session; -} - -void SessionImpl::OnSessionDisconnect() { - if (context_) { - // Persist the current context, so that ExecuteModel() can be called - // without adding the same context. - on_device_state_->add_context_before_execute = true; - } - on_device_state_->session.reset(); -} - -void SessionImpl::CancelPendingResponse(ExecuteModelResult result, - ModelExecutionError error) { - if (!on_device_state_) { - return; - } - if (on_device_state_->histogram_logger) { - on_device_state_->histogram_logger->set_result(result); - } - auto callback = std::move(on_device_state_->callback); - auto log_ai_data_request = std::move(on_device_state_->log_ai_data_request); - on_device_state_->ResetRequestState(); - if (callback) { - OptimizationGuideModelExecutionError og_error = - OptimizationGuideModelExecutionError::FromModelExecutionError(error); - std::unique_ptr<ModelQualityLogEntry> log_entry; - std::unique_ptr<proto::ModelExecutionInfo> model_execution_info; - if (og_error.ShouldLogModelQuality()) { - log_entry = std::make_unique<ModelQualityLogEntry>( - on_device_state_->opts.log_uploader); - log_entry->log_ai_data_request()->MergeFrom(*log_ai_data_request); - std::string model_execution_id = GenerateExecutionId(); - log_entry->set_model_execution_id(model_execution_id); - model_execution_info = std::make_unique<proto::ModelExecutionInfo>( - log_entry->log_ai_data_request()->model_execution_info()); - model_execution_info->set_execution_id(model_execution_id); - model_execution_info->set_model_execution_error_enum( - static_cast<uint32_t>(og_error.error())); - } - callback.Run(OptimizationGuideModelStreamingExecutionResult( - base::unexpected(og_error), /*provided_by_on_device=*/true, - std::move(log_entry), std::move(model_execution_info))); - } -} - -void SessionImpl::SendResponse(ResponseCompleteness completeness) { - if (completeness == ResponseCompleteness::kPartial && - features::ShouldUseTextSafetyRemoteFallbackForEligibleFeatures()) { - // We don't send streaming responses in this mode. - return; - } - - if (!on_device_state_->opts.adapter->ShouldParseResponse(completeness)) { - return; - } - - std::string safe_response = on_device_state_->current_response.substr( - 0, on_device_state_->latest_safe_raw_output.length); - LogRawResponse(on_device_state_->opts.logger.get(), feature_, safe_response); - on_device_state_->MutableLoggedResponse()->set_output_string(safe_response); - size_t previous_response_pos = on_device_state_->latest_response_pos; - on_device_state_->latest_response_pos = - on_device_state_->latest_safe_raw_output.length; - on_device_state_->opts.adapter->ParseResponse( - *last_message_, safe_response, previous_response_pos, - base::BindOnce(&SessionImpl::OnParsedResponse, - on_device_state_->session_weak_ptr_factory_.GetWeakPtr(), - completeness)); -} - -void SessionImpl::OnParsedResponse( - ResponseCompleteness completeness, - base::expected<proto::Any, ResponseParsingError> output) { - if (!output.has_value()) { - switch (output.error()) { - case ResponseParsingError::kRejectedPii: - on_device_state_->MutableLoggedResponse()->set_status( - proto::ON_DEVICE_MODEL_SERVICE_RESPONSE_STATUS_RETRACTED); - CancelPendingResponse(ExecuteModelResult::kContainedPII, - ModelExecutionError::kFiltered); - return; - case ResponseParsingError::kFailed: - CancelPendingResponse( - ExecuteModelResult::kFailedConstructingResponseMessage, - ModelExecutionError::kGenericFailure); - return; - } - } - on_device_state_->opts.safety_checker->RunResponseChecks( - *last_message_, *output, completeness, - base::BindOnce(&SessionImpl::OnResponseSafetyResult, - on_device_state_->session_weak_ptr_factory_.GetWeakPtr(), - completeness, *output)); -} - -void SessionImpl::OnResponseSafetyResult(ResponseCompleteness completeness, - proto::Any output, - SafetyChecker::Result safety_result) { - if (safety_result.failed_to_run) { - DestroyOnDeviceStateAndFallbackToRemote( - ExecuteModelResult::kFailedConstructingMessage); - return; - } - if (completeness == ResponseCompleteness::kComplete || - safety_result.is_unsafe || safety_result.is_unsupported_language) { - on_device_state_->AddModelExecutionLogs(std::move(safety_result.logs)); - } - if (safety_result.is_unsafe || safety_result.is_unsupported_language) { - if (on_device_state_->histogram_logger) { - on_device_state_->histogram_logger->set_result( - ExecuteModelResult::kUsedOnDeviceOutputUnsafe); - } - if (features::GetOnDeviceModelRetractUnsafeContent()) { - CancelPendingResponse(ExecuteModelResult::kUsedOnDeviceOutputUnsafe, - safety_result.is_unsupported_language - ? ModelExecutionError::kUnsupportedLanguage - : ModelExecutionError::kFiltered); - - return; - } - } - if (completeness == ResponseCompleteness::kPartial) { - SendPartialResponseCallback(output); - return; - } - - if (features::ShouldUseTextSafetyRemoteFallbackForEligibleFeatures()) { - RunTextSafetyRemoteFallbackAndCompletionCallback(std::move(output)); - return; - } - - SendSuccessCompletionCallback(output); -} - -void SessionImpl::SendPartialResponseCallback( - const proto::Any& success_response_metadata) { - on_device_state_->callback.Run(OptimizationGuideModelStreamingExecutionResult( - base::ok(StreamingResponse{.response = success_response_metadata, - .is_complete = false}), - /*provided_by_on_device=*/true, /*log_entry=*/nullptr)); -} - -void SessionImpl::SendSuccessCompletionCallback( - const proto::Any& success_response_metadata) { - // Complete the log entry and promise it to the ModelQualityUploaderService. - std::unique_ptr<ModelQualityLogEntry> log_entry; - std::unique_ptr<proto::ModelExecutionInfo> model_execution_info; - if (on_device_state_->log_ai_data_request) { - SetExecutionResponse(feature_, *(on_device_state_->log_ai_data_request), - success_response_metadata); - on_device_state_->MutableLoggedResponse()->set_status( - proto::ON_DEVICE_MODEL_SERVICE_RESPONSE_STATUS_SUCCESS); - log_entry = std::make_unique<ModelQualityLogEntry>( - on_device_state_->opts.log_uploader); - log_entry->log_ai_data_request()->MergeFrom( - *on_device_state_->log_ai_data_request); - std::string model_execution_id = GenerateExecutionId(); - log_entry->set_model_execution_id(model_execution_id); - model_execution_info = std::make_unique<proto::ModelExecutionInfo>( - on_device_state_->log_ai_data_request->model_execution_info()); - model_execution_info->set_execution_id(model_execution_id); - on_device_state_->log_ai_data_request.reset(); - } - - // Return the execution response. - on_device_state_->callback.Run(OptimizationGuideModelStreamingExecutionResult( - base::ok(StreamingResponse{.response = success_response_metadata, - .is_complete = true}), - /*provided_by_on_device=*/true, std::move(log_entry), - std::move(model_execution_info))); - - on_device_state_->ResetRequestState(); } bool SessionImpl::ShouldUseOnDeviceModel() const { - return on_device_state_ && on_device_state_->opts.model_client->ShouldUse(); -} - -void SessionImpl::DestroyOnDeviceStateAndFallbackToRemote( - ExecuteModelResult result) { - if (on_device_state_->histogram_logger) { - on_device_state_->histogram_logger->set_result(result); - } - auto log_ai_data_request = std::move(on_device_state_->log_ai_data_request); - auto callback = std::move(on_device_state_->callback); - DestroyOnDeviceState(); - execute_remote_fn_.Run( - feature_, *last_message_, std::nullopt, std::move(log_ai_data_request), - base::BindOnce(&InvokeStreamingCallbackWithRemoteResult, - std::move(callback))); + return on_device_context_ && on_device_context_->CanUse(); } void SessionImpl::DestroyOnDeviceState() { - DCHECK(!on_device_state_ || !on_device_state_->callback); - on_device_state_.reset(); + on_device_context_.reset(); } std::unique_ptr<google::protobuf::MessageLite> SessionImpl::MergeContext( @@ -870,137 +232,6 @@ return message; } -void SessionImpl::RunTextSafetyRemoteFallbackAndCompletionCallback( - proto::Any success_response_metadata) { - auto ts_request = on_device_state_->opts.adapter->ConstructTextSafetyRequest( - *last_message_, on_device_state_->current_response); - if (!ts_request) { - CancelPendingResponse( - ExecuteModelResult::kFailedConstructingRemoteTextSafetyRequest, - ModelExecutionError::kGenericFailure); - return; - } - - proto::InternalOnDeviceModelExecutionInfo remote_ts_model_execution_info; - auto* ts_request_log = remote_ts_model_execution_info.mutable_request() - ->mutable_text_safety_model_request(); - ts_request_log->set_text(ts_request->text()); - ts_request_log->set_url(ts_request->url()); - - execute_remote_fn_.Run( - ModelBasedCapabilityKey::kTextSafety, *ts_request, std::nullopt, - /*log_ai_data_request=*/nullptr, - base::BindOnce(&SessionImpl::OnTextSafetyRemoteResponse, - on_device_state_->session_weak_ptr_factory_.GetWeakPtr(), - std::move(remote_ts_model_execution_info), - std::move(success_response_metadata))); -} - -void SessionImpl::OnTextSafetyRemoteResponse( - proto::InternalOnDeviceModelExecutionInfo remote_ts_model_execution_info, - proto::Any success_response_metadata, - OptimizationGuideModelExecutionResult result, - std::unique_ptr<ModelQualityLogEntry> remote_log_entry) { - bool is_unsafe = - !result.response.has_value() && - result.response.error().error() == - OptimizationGuideModelExecutionError::ModelExecutionError::kFiltered; - if (on_device_state_->log_ai_data_request) { - if (remote_log_entry) { - auto* ts_response_log = remote_ts_model_execution_info.mutable_response() - ->mutable_text_safety_model_response(); - ts_response_log->set_server_execution_id( - remote_log_entry->model_execution_id()); - ts_response_log->set_is_unsafe(is_unsafe); - } - *(on_device_state_->log_ai_data_request->mutable_model_execution_info() - ->mutable_on_device_model_execution_info() - ->add_execution_infos()) = remote_ts_model_execution_info; - } - - if (is_unsafe) { - CancelPendingResponse(ExecuteModelResult::kUsedOnDeviceOutputUnsafe, - ModelExecutionError::kFiltered); - return; - } - - if (!result.response.has_value()) { - CancelPendingResponse(ExecuteModelResult::kTextSafetyRemoteRequestFailed, - ModelExecutionError::kGenericFailure); - return; - } - - SendSuccessCompletionCallback(success_response_metadata); -} - -SessionImpl::OnDeviceState::OnDeviceState(OnDeviceOptions&& options, - SessionImpl* session) - : opts(std::move(options)), - receiver(session), - session_weak_ptr_factory_(session) {} - -SessionImpl::OnDeviceState::~OnDeviceState() = default; - -proto::OnDeviceModelServiceResponse* -SessionImpl::OnDeviceState::MutableLoggedResponse() { - CHECK(log_ai_data_request); - CHECK_GT(log_ai_data_request->model_execution_info() - .on_device_model_execution_info() - .execution_infos_size(), - 0); - return log_ai_data_request->mutable_model_execution_info() - ->mutable_on_device_model_execution_info() - ->mutable_execution_infos(0) - ->mutable_response() - ->mutable_on_device_model_service_response(); -} - -void SessionImpl::OnDeviceState::AddModelExecutionLog( - const proto::InternalOnDeviceModelExecutionInfo& log) { - CHECK(log_ai_data_request); - - log_ai_data_request->mutable_model_execution_info() - ->mutable_on_device_model_execution_info() - ->add_execution_infos() - ->CopyFrom(log); -} - -void SessionImpl::OnDeviceState::AddModelExecutionLogs( - google::protobuf::RepeatedPtrField< - proto::InternalOnDeviceModelExecutionInfo> logs) { - CHECK(log_ai_data_request); - - log_ai_data_request->mutable_model_execution_info() - ->mutable_on_device_model_execution_info() - ->mutable_execution_infos() - ->MergeFrom(std::move(logs)); -} - -void SessionImpl::OnDeviceState::ResetRequestState() { - receiver.reset(); - callback.Reset(); - current_response.clear(); - latest_response_pos = 0; - start = base::TimeTicks(); - histogram_logger.reset(); - log_ai_data_request.reset(); - num_unchecked_response_tokens = 0; - latest_safe_raw_output.length = 0; - response_completeness = ResponseCompleteness::kPartial; - session_weak_ptr_factory_.InvalidateWeakPtrs(); -} - -SessionImpl::OnDeviceState::SafeRawOutput::SafeRawOutput() = default; -SessionImpl::OnDeviceState::SafeRawOutput::~SafeRawOutput() = default; - -SessionImpl::ExecuteModelHistogramLogger::~ExecuteModelHistogramLogger() { - base::UmaHistogramEnumeration( - base::StrCat( - {"OptimizationGuide.ModelExecution.OnDeviceExecuteModelResult.", - GetStringNameForModelExecutionFeature(feature_)}), - result_); -} - void SessionImpl::GetSizeInTokens( const std::string& text, OptimizationGuideModelSizeInTokenCallback callback) { @@ -1011,7 +242,7 @@ } auto input = on_device_model::mojom::Input::New(); input->pieces.push_back(text); - GetOrCreateSession().GetSizeInTokens( + on_device_context_->GetOrCreateSession()->GetSizeInTokens( std::move(input), mojo::WrapCallbackWithDefaultInvokeIfNotRun(std::move(callback), 0)); } @@ -1031,7 +262,7 @@ } const proto::Any& SessionImpl::GetOnDeviceFeatureMetadata() const { - return on_device_state_->opts.adapter->GetFeatureMetadata(); + return on_device_context_->opts().adapter->GetFeatureMetadata(); } const SamplingParams SessionImpl::GetSamplingParams() const { @@ -1047,13 +278,13 @@ std::move(callback).Run(0); return; } - auto input = on_device_state_->opts.adapter->ConstructInputString( + auto input = on_device_context_->opts().adapter->ConstructInputString( request, want_input_context); if (!input) { std::move(callback).Run(0); return; } - GetOrCreateSession().GetSizeInTokens( + on_device_context_->GetOrCreateSession()->GetSizeInTokens( std::move(input->input), mojo::WrapCallbackWithDefaultInvokeIfNotRun(std::move(callback), 0)); }
diff --git a/components/optimization_guide/core/model_execution/session_impl.h b/components/optimization_guide/core/model_execution/session_impl.h index 398c7df..844415c 100644 --- a/components/optimization_guide/core/model_execution/session_impl.h +++ b/components/optimization_guide/core/model_execution/session_impl.h
@@ -10,10 +10,13 @@ #include <string> #include <vector> +#include "base/functional/callback_forward.h" #include "base/memory/scoped_refptr.h" #include "base/memory/weak_ptr.h" #include "base/timer/timer.h" #include "components/optimization_guide/core/model_execution/feature_keys.h" +#include "components/optimization_guide/core/model_execution/on_device_context.h" +#include "components/optimization_guide/core/model_execution/on_device_execution.h" #include "components/optimization_guide/core/model_execution/on_device_model_feature_adapter.h" #include "components/optimization_guide/core/model_execution/optimization_guide_model_execution_error.h" #include "components/optimization_guide/core/model_execution/safety_checker.h" @@ -23,60 +26,19 @@ #include "components/optimization_guide/proto/model_quality_metadata.pb.h" #include "components/optimization_guide/proto/model_quality_service.pb.h" #include "components/optimization_guide/proto/text_safety_model_metadata.pb.h" +#include "mojo/public/cpp/bindings/pending_remote.h" #include "mojo/public/cpp/bindings/receiver.h" #include "mojo/public/cpp/bindings/remote.h" #include "services/on_device_model/public/mojom/on_device_model.mojom.h" -class OptimizationGuideLogger; - namespace optimization_guide { -class OnDeviceModelFeatureAdapter; -using ExecuteRemoteFn = base::RepeatingCallback<void( - ModelBasedCapabilityKey feature, - const google::protobuf::MessageLite&, - std::optional<base::TimeDelta> timeout, - std::unique_ptr<proto::LogAiDataRequest>, - OptimizationGuideModelExecutionResultCallback)>; +class OnDeviceContext; // Session implementation that uses either the on device model or the server // model. -class SessionImpl : public OptimizationGuideModelExecutor::Session, - public on_device_model::mojom::StreamingResponder { +class SessionImpl : public OptimizationGuideModelExecutor::Session { public: - class OnDeviceModelClient { - public: - virtual ~OnDeviceModelClient() = 0; - // Create another client for the same model. - virtual std::unique_ptr<OnDeviceModelClient> Clone() const = 0; - // Called to check whether this client is still usable. - virtual bool ShouldUse() = 0; - // Called to retrieve connection the managed model. - virtual mojo::Remote<on_device_model::mojom::OnDeviceModel>& - GetModelRemote() = 0; - // Called to report a successful execution of the model. - virtual void OnResponseCompleted() = 0; - }; - - struct OnDeviceOptions final { - OnDeviceOptions(); - OnDeviceOptions(const OnDeviceOptions&); - OnDeviceOptions(OnDeviceOptions&&); - ~OnDeviceOptions(); - - std::unique_ptr<OnDeviceModelClient> model_client; - proto::OnDeviceModelVersions model_versions; - scoped_refptr<const OnDeviceModelFeatureAdapter> adapter; - std::unique_ptr<SafetyChecker> safety_checker; - TokenLimits token_limits; - - base::WeakPtr<OptimizationGuideLogger> logger; - base::WeakPtr<ModelQualityLogsUploaderService> log_uploader; - - // Returns true if the on-device model may be used. - bool ShouldUse() const; - }; - // Possible outcomes of AddContext(). Maps to histogram enum // "OptimizationGuideOnDeviceAddContextResult". // These values are persisted to logs. Entries should not be renumbered @@ -88,57 +50,6 @@ kMaxValue = kFailedConstructingInput, }; - // Possible outcomes of ExecuteModel(). - // These values are persisted to logs. Entries should not be renumbered and - // numeric values should never be reused. - enum class ExecuteModelResult { - // On-device was not used. - kOnDeviceNotUsed = 0, - // On-device was used, and it completed successfully. - kUsedOnDevice = 1, - // Failed constructing message, and used server. - kFailedConstructingMessage = 2, - // Got a response from on-device, but failed constructing the message. - kFailedConstructingResponseMessage = 3, - // Timed out and used server. - kTimedOut = 4, - // Received a disconnect while waiting for response. This may trigger - // fallback to another model, e.g. on the server, if configured. - kDisconnectAndMaybeFallback = 5, - // Received a disconnect while waiting for response and cancelled. - kDisconnectAndCancel = 6, - // Response was cancelled because ExecuteModel() was called while waiting - // for response. - kCancelled = 7, - // SessionImpl was destroyed while waiting for a response. - kDestroyedWhileWaitingForResponse = 8, - // On-device was used, it completed successfully, but the output is - // considered unsafe. - kUsedOnDeviceOutputUnsafe = 9, - // On-device was used, but the output was rejected (because contained PII). - kContainedPII = 10, - // On-device was used, but the output was rejected because it had repeats. - kResponseHadRepeats = 11, - // On-device was used and the output was complete but the output was - // rejected since it did not have the required safety scores. - kResponseCompleteButNoRequiredSafetyScores = 12, - // On-device was used and completed successfully, but the output was not in - // a language that could be reliably evaluated for safety. - kUsedOnDeviceOutputUnsupportedLanguage = 13, - // On-device was used and completed successfully, but failed constructing - // the text safety remote request. - kFailedConstructingRemoteTextSafetyRequest = 14, - // On-device was used and completed successfully, but the text safety remote - // request failed for some reason. - kTextSafetyRemoteRequestFailed = 15, - // On-device was used, but the request was considered unsafe. - kRequestUnsafe = 16, - - // Please update OptimizationGuideOnDeviceExecuteModelResult in - // optimization/enums.xml. - kMaxValue = kRequestUnsafe, - }; - SessionImpl(ModelBasedCapabilityKey feature, std::optional<OnDeviceOptions> on_device_opts, ExecuteRemoteFn execute_remote_fn, @@ -166,180 +77,23 @@ OptimizationGuideModelSizeInTokenCallback callback) override; const SamplingParams GetSamplingParams() const override; - // on_device_model::mojom::StreamingResponder: - void OnResponse(on_device_model::mojom::ResponseChunkPtr chunk) override; - void OnComplete(on_device_model::mojom::ResponseSummaryPtr summary) override; - // Called when the StreamingResponder remote is disconnected. - void OnResponderDisconnect(); - // Returns true if the on-device model should be used. bool ShouldUseOnDeviceModel() const; private: - class ContextProcessor; - - // Used to log the result of ExecuteModel. - class ExecuteModelHistogramLogger { - public: - explicit ExecuteModelHistogramLogger(ModelBasedCapabilityKey feature) - : feature_(feature) {} - ~ExecuteModelHistogramLogger(); - - void set_result(ExecuteModelResult result) { result_ = result; } - - private: - const ModelBasedCapabilityKey feature_; - ExecuteModelResult result_ = ExecuteModelResult::kOnDeviceNotUsed; - }; - - // Captures all state used for the on device model. - struct OnDeviceState { - OnDeviceState(OnDeviceOptions&& opts, SessionImpl* session); - ~OnDeviceState(); - - // Returns true if ExecuteModel() was called and the complete response - // has not been received. - bool did_execute_and_waiting_for_on_complete() const { - return start != base::TimeTicks() && - response_completeness == ResponseCompleteness::kPartial; - } - - // Returns the mutable on-device model service response for logging. - proto::OnDeviceModelServiceResponse* MutableLoggedResponse(); - - // Adds an execution info for the text safety model based on `this`. - void AddModelExecutionLog( - const proto::InternalOnDeviceModelExecutionInfo& log); - // Adds a collection of model execution logs to the request log. - void AddModelExecutionLogs(google::protobuf::RepeatedPtrField< - proto::InternalOnDeviceModelExecutionInfo> logs); - - // Resets all state related to a request. - void ResetRequestState(); - - OnDeviceOptions opts; - mojo::Remote<on_device_model::mojom::Session> session; - std::unique_ptr<ContextProcessor> context_processor; - mojo::Receiver<on_device_model::mojom::StreamingResponder> receiver; - std::string current_response; - OptimizationGuideModelExecutionResultStreamingCallback callback; - // If true, the context is added before execution. This is set to true if - // a disconnect happens. - bool add_context_before_execute = false; - // Time ExecuteModel() was called. - base::TimeTicks start; - // Timer used to detect when no response has been received and fallback - // to remote execution. - base::OneShotTimer timer_for_first_response; - // Used to log the result of ExecuteModel(). - std::unique_ptr<ExecuteModelHistogramLogger> histogram_logger; - // Used to log execution information for the request. - std::unique_ptr<proto::LogAiDataRequest> log_ai_data_request; - - // How many tokens (response chunks) have been added since the last safety - // evaluation was requested. - size_t num_unchecked_response_tokens = 0; - // How many tokens (response chunks) have been added. - size_t num_response_tokens = 0; - - struct SafeRawOutput { - SafeRawOutput(); - ~SafeRawOutput(); - // How much of 'current_response' was checked. - size_t length = 0; - }; - // The longest response that has passed the raw output text safety check. - SafeRawOutput latest_safe_raw_output; - // The last position in the response that has been streamed to the - // responder. - size_t latest_response_pos = 0; - - // Whether the model response is complete. - ResponseCompleteness response_completeness = ResponseCompleteness::kPartial; - - // Factory for weak pointers related to this session that are invalidated - // with the request state. - base::WeakPtrFactory<SessionImpl> session_weak_ptr_factory_; - }; - AddContextResult AddContextImpl( const google::protobuf::MessageLite& request_metadata); - // Gets the active session or restarts a session if the session is reset. - on_device_model::mojom::Session& GetOrCreateSession(); - - // Called when on-device session disconnects. - void OnSessionDisconnect(); - - // Cancels any pending response and resets response state. - void CancelPendingResponse( - ExecuteModelResult result, - OptimizationGuideModelExecutionError::ModelExecutionError error = - OptimizationGuideModelExecutionError::ModelExecutionError:: - kCancelled); - - // Calls SendResponse(kComplete) if we've received the full response and have - // finished checking raw output safety for it. - void MaybeSendCompleteResponse(); - - // Sends `current_response_` to the client. - void SendResponse(ResponseCompleteness completeness); - - void DestroyOnDeviceStateAndFallbackToRemote(ExecuteModelResult result); - void DestroyOnDeviceState(); - // Called to run the text safety remote fallback. Will invoke completion - // callback when done. - void RunTextSafetyRemoteFallbackAndCompletionCallback( - proto::Any success_response_metadata); - - // Callback invoked with RequestSafetyCheck result. - void OnRequestSafetyResult(on_device_model::mojom::InputOptionsPtr options, - SafetyChecker::Result safety_result); - - // Begins request execution (leads to OnResponse/OnComplete). - void BeginRequestExecution(on_device_model::mojom::InputOptionsPtr options); - - // Evaluates raw output safety. - // Will invoke SendResponse if evaluations are successful. - void RunRawOutputSafetyCheck(ResponseCompleteness completeness); - - // Called when output safety check completes. - void OnRawOutputSafetyResult(size_t raw_output_size, - ResponseCompleteness completeness, - SafetyChecker::Result safety_result); - - // Callback invoked when the text safety remote fallback response comes back. - // Will invoke the session's completion callback and destroy state. - void OnTextSafetyRemoteResponse( - proto::InternalOnDeviceModelExecutionInfo remote_ts_model_execution_info, - proto::Any success_response_metadata, - OptimizationGuideModelExecutionResult result, - std::unique_ptr<ModelQualityLogEntry> remote_log_entry); - - // Called when a response has finished parsing. - void OnParsedResponse( - ResponseCompleteness completeness, - base::expected<proto::Any, ResponseParsingError> output); - - // Called when response safety check completes. - void OnResponseSafetyResult(ResponseCompleteness completeness, - proto::Any output, - SafetyChecker::Result safety_result); + // Called when an on-device execution flow terminates, and can be cleaned up. + void OnDeviceExecutionTerminated(bool healthy); // Returns a new message created by merging `request` into `context_`. This // is a bit tricky since we don't know the type of MessageLite. std::unique_ptr<google::protobuf::MessageLite> MergeContext( const google::protobuf::MessageLite& request); - // Sends the partial response callback. - void SendPartialResponseCallback(const proto::Any& success_response_metadata); - - // Sends the success completion callback and destroys any state. - void SendSuccessCompletionCallback( - const proto::Any& success_response_metadata); - // Helper function to get the size of request in tokens with boolean flag to // control if we are extracting the context or the execution text. void GetSizeInTokensInternal( @@ -353,14 +107,17 @@ std::unique_ptr<google::protobuf::MessageLite> context_; base::TimeTicks context_start_time_; - // Last message executed. - std::unique_ptr<google::protobuf::MessageLite> last_message_; + // Manages the on-device session holding the processed context. + // If this is null, on-device executions cannot be started. + std::unique_ptr<OnDeviceContext> on_device_context_; - // Has a value when using the on device model. - std::optional<OnDeviceState> on_device_state_; + // Manages state for an ongoing on-device execution. + std::optional<OnDeviceExecution> on_device_execution_; // Params used to control output sampling for the on device model. const SamplingParams sampling_params_; + + base::WeakPtrFactory<SessionImpl> weak_ptr_factory_{this}; }; } // namespace optimization_guide
diff --git a/components/os_crypt/async/browser/BUILD.gn b/components/os_crypt/async/browser/BUILD.gn index 2c47265..e7b19d7 100644 --- a/components/os_crypt/async/browser/BUILD.gn +++ b/components/os_crypt/async/browser/BUILD.gn
@@ -80,6 +80,26 @@ ] } + source_set("freedesktop_secret_key_provider") { + sources = [ + "fallback_linux_key_provider.cc", + "fallback_linux_key_provider.h", + "freedesktop_secret_key_provider.cc", + "freedesktop_secret_key_provider.h", + ] + + deps = [ + ":key_provider_interface", + "//base", + "//components/dbus", + "//components/os_crypt/async/common", + "//components/os_crypt/sync", + "//crypto", + "//dbus", + ] + public_deps = [ "//build:branding_buildflags" ] + } + source_set("test_secret_portal") { testonly = true @@ -125,8 +145,13 @@ } if (is_linux && use_dbus) { - sources += [ "secret_portal_key_provider_unittest.cc" ] + sources += [ + "freedesktop_secret_key_provider_compat_unittest.cc", + "freedesktop_secret_key_provider_unittest.cc", + "secret_portal_key_provider_unittest.cc", + ] deps += [ + ":freedesktop_secret_key_provider", ":secret_portal_key_provider", "//components/dbus", "//components/prefs:test_support",
diff --git a/components/os_crypt/async/browser/fallback_linux_key_provider.cc b/components/os_crypt/async/browser/fallback_linux_key_provider.cc new file mode 100644 index 0000000..48d39d8 --- /dev/null +++ b/components/os_crypt/async/browser/fallback_linux_key_provider.cc
@@ -0,0 +1,43 @@ +// Copyright 2024 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/os_crypt/async/browser/fallback_linux_key_provider.h" + +#include <utility> + +#include "components/os_crypt/async/common/algorithm.mojom.h" + +namespace os_crypt_async { + +namespace { + +// These constants are duplicated from the sync backend. +constexpr char kEncryptionTag[] = "v10"; + +// PBKDF2-HMAC-SHA1(1 iteration, key = "peanuts", salt = "saltysalt") +constexpr auto kV10Key = + std::to_array<uint8_t>({0xfd, 0x62, 0x1f, 0xe5, 0xa2, 0xb4, 0x02, 0x53, + 0x9d, 0xfa, 0x14, 0x7c, 0xa9, 0x27, 0x27, 0x78}); + +} // namespace + +FallbackLinuxKeyProvider::FallbackLinuxKeyProvider(bool use_for_encryption) + : use_for_encryption_(use_for_encryption) {} + +FallbackLinuxKeyProvider::~FallbackLinuxKeyProvider() = default; + +void FallbackLinuxKeyProvider::GetKey(KeyCallback callback) { + Encryptor::Key key(kV10Key, mojom::Algorithm::kAES128CBC); + std::move(callback).Run(kEncryptionTag, std::move(key)); +} + +bool FallbackLinuxKeyProvider::UseForEncryption() { + return use_for_encryption_; +} + +bool FallbackLinuxKeyProvider::IsCompatibleWithOsCryptSync() { + return false; +} + +} // namespace os_crypt_async
diff --git a/components/os_crypt/async/browser/fallback_linux_key_provider.h b/components/os_crypt/async/browser/fallback_linux_key_provider.h new file mode 100644 index 0000000..eed0cb59 --- /dev/null +++ b/components/os_crypt/async/browser/fallback_linux_key_provider.h
@@ -0,0 +1,32 @@ +// Copyright 2024 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_OS_CRYPT_ASYNC_BROWSER_FALLBACK_LINUX_KEY_PROVIDER_H_ +#define COMPONENTS_OS_CRYPT_ASYNC_BROWSER_FALLBACK_LINUX_KEY_PROVIDER_H_ + +#include "components/os_crypt/async/browser/key_provider.h" + +namespace os_crypt_async { + +class FallbackLinuxKeyProvider : public KeyProvider { + public: + explicit FallbackLinuxKeyProvider(bool use_for_encryption); + + FallbackLinuxKeyProvider(const FallbackLinuxKeyProvider&) = delete; + FallbackLinuxKeyProvider& operator=(const FallbackLinuxKeyProvider&) = delete; + + ~FallbackLinuxKeyProvider() override; + + // KeyProvider: + void GetKey(KeyCallback callback) override; + bool UseForEncryption() override; + bool IsCompatibleWithOsCryptSync() override; + + private: + const bool use_for_encryption_; +}; + +} // namespace os_crypt_async + +#endif // COMPONENTS_OS_CRYPT_ASYNC_BROWSER_FALLBACK_LINUX_KEY_PROVIDER_H_
diff --git a/components/os_crypt/async/browser/freedesktop_secret_key_provider.cc b/components/os_crypt/async/browser/freedesktop_secret_key_provider.cc new file mode 100644 index 0000000..f4d7b48 --- /dev/null +++ b/components/os_crypt/async/browser/freedesktop_secret_key_provider.cc
@@ -0,0 +1,266 @@ +// Copyright 2024 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/os_crypt/async/browser/freedesktop_secret_key_provider.h" + +#include <algorithm> +#include <memory> + +#include "base/base64.h" +#include "base/check.h" +#include "base/containers/span.h" +#include "base/environment.h" +#include "base/functional/bind.h" +#include "base/logging.h" +#include "base/memory/ref_counted_memory.h" +#include "base/memory/scoped_refptr.h" +#include "base/nix/xdg_util.h" +#include "base/no_destructor.h" +#include "base/rand_util.h" +#include "components/dbus/thread_linux/dbus_thread_linux.h" +#include "components/os_crypt/async/common/algorithm.mojom.h" +#include "crypto/encryptor.h" +#include "crypto/kdf.h" +#include "dbus/message.h" +#include "dbus/object_path.h" + +namespace os_crypt_async { + +namespace { + +// These constants are duplicated from the sync backend. +constexpr char kEncryptionTag[] = "v11"; +constexpr char kSalt[] = "saltysalt"; +constexpr size_t kDerivedKeySizeInBits = 128; +constexpr size_t kEncryptionIterations = 1; + +template <typename ReplyArgs> +void CallMethod(dbus::ObjectProxy* object_proxy, + const std::string& interface_name, + const std::string& method_name, + const DbusType& arguments, + base::OnceCallback<void(std::optional<ReplyArgs>)> callback) { + dbus::MethodCall method_call(interface_name, method_name); + dbus::MessageWriter writer(&method_call); + arguments.Write(&writer); + object_proxy->CallMethod( + &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, + base::BindOnce( + [](const std::string& interface_name, const std::string& method_name, + base::OnceCallback<void(std::optional<ReplyArgs>)> callback, + dbus::Response* response) { + if (!response) { + std::move(callback).Run(std::nullopt); + return; + } + dbus::MessageReader reader(response); + ReplyArgs reply; + if (!reply.Read(&reader)) { + LOG(ERROR) << "Failed to read reply for " << interface_name << "." + << method_name << ": expected type " + << ReplyArgs::GetSignature() << " but got type " + << response->GetSignature(); + std::move(callback).Run(std::nullopt); + return; + } + std::move(callback).Run(std::move(reply)); + }, + interface_name, method_name, std::move(callback))); +} + +scoped_refptr<dbus::Bus> CreateBus() { + dbus::Bus::Options options; + options.bus_type = dbus::Bus::SESSION; + options.connection_type = dbus::Bus::PRIVATE; + options.dbus_task_runner = dbus_thread_linux::GetTaskRunner(); + return base::MakeRefCounted<dbus::Bus>(options); +} + +} // namespace + +FreedesktopSecretKeyProvider::FreedesktopSecretKeyProvider( + bool use_for_encryption, + const std::string& product_name, + scoped_refptr<dbus::Bus> bus) + : use_for_encryption_(use_for_encryption), + product_name_(product_name), + bus_(std::move(bus)) { + if (!bus_) { + bus_ = CreateBus(); + } +} + +FreedesktopSecretKeyProvider::~FreedesktopSecretKeyProvider() { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); +} + +void FreedesktopSecretKeyProvider::GetKey(KeyCallback callback) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK(callback); + key_callback_ = std::move(callback); + + if (!secret_for_testing_.empty()) { + DeriveKeyFromSecret(base::as_byte_span(secret_for_testing_)); + return; + } + + dbus_utils::CheckForServiceAndStart( + bus_, kSecretServiceName, + base::BindOnce(&FreedesktopSecretKeyProvider::OnServiceStarted, + weak_ptr_factory_.GetWeakPtr())); +} + +bool FreedesktopSecretKeyProvider::UseForEncryption() { + return use_for_encryption_; +} + +bool FreedesktopSecretKeyProvider::IsCompatibleWithOsCryptSync() { + return true; +} + +void FreedesktopSecretKeyProvider::OnServiceStarted( + std::optional<bool> service_started) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + if (!service_started.value_or(false)) { + FinalizeFailure(); + return; + } + + auto* service_proxy = bus_->GetObjectProxy( + kSecretServiceName, dbus::ObjectPath(kSecretServicePath)); + CallMethod(service_proxy, kSecretServiceInterface, kMethodReadAlias, + DbusString(kDefaultAlias), + base::BindOnce(&FreedesktopSecretKeyProvider::OnReadAliasDefault, + weak_ptr_factory_.GetWeakPtr())); +} + +void FreedesktopSecretKeyProvider::OnReadAliasDefault( + std::optional<DbusObjectPath> collection_path) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + if (!collection_path.has_value()) { + FinalizeFailure(); + return; + } + if (collection_path->value().value() != "/") { + default_collection_proxy_ = + bus_->GetObjectProxy(kSecretServiceName, collection_path->value()); + OpenSession(); + } else { + NOTIMPLEMENTED(); + FinalizeFailure(); + } +} + +void FreedesktopSecretKeyProvider::OpenSession() { + auto* service_proxy = bus_->GetObjectProxy( + kSecretServiceName, dbus::ObjectPath(kSecretServicePath)); + CallMethod(service_proxy, kSecretServiceInterface, kMethodOpenSession, + MakeDbusParameters(DbusString(kAlgorithmPlain), + MakeDbusVariant(DbusString(kInputPlain))), + base::BindOnce(&FreedesktopSecretKeyProvider::OnOpenSession, + weak_ptr_factory_.GetWeakPtr())); +} + +void FreedesktopSecretKeyProvider::OnOpenSession( + std::optional<DbusParameters<DbusVariant, DbusObjectPath>> session_reply) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + if (!session_reply.has_value()) { + FinalizeFailure(); + return; + } + const auto& [_, result] = session_reply->value(); + session_proxy_ = bus_->GetObjectProxy(kSecretServiceName, result.value()); + session_opened_ = true; + + auto search_attrs = MakeDbusArray(MakeDbusDictEntry( + DbusString(kApplicationAttributeKey), DbusString(kAppName))); + + CallMethod(default_collection_proxy_, kSecretCollectionInterface, + kMethodSearchItems, search_attrs, + base::BindOnce(&FreedesktopSecretKeyProvider::OnSearchItems, + weak_ptr_factory_.GetWeakPtr())); +} + +void FreedesktopSecretKeyProvider::OnSearchItems( + std::optional<DbusArray<DbusObjectPath>> results) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + if (!results.has_value()) { + FinalizeFailure(); + return; + } + + if (results->value().empty()) { + NOTIMPLEMENTED(); + FinalizeFailure(); + return; + } + + auto* item_proxy = bus_->GetObjectProxy(kSecretServiceName, + results->value().front().value()); + CallMethod(item_proxy, kSecretItemInterface, kMethodGetSecret, + MakeDbusParameters(DbusObjectPath(session_proxy_->object_path())), + base::BindOnce(&FreedesktopSecretKeyProvider::OnGetSecret, + weak_ptr_factory_.GetWeakPtr())); +} + +void FreedesktopSecretKeyProvider::OnGetSecret( + std::optional<DbusSecret> secret_reply) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + if (!secret_reply.has_value()) { + FinalizeFailure(); + return; + } + + const auto& [session_path, parameters, value, content_type] = + secret_reply->value(); + const auto& secret_bytes = value.value(); + if (!secret_bytes) { + FinalizeFailure(); + return; + } + if (secret_bytes->size() == 0) { + LOG(ERROR) << "GetSecret returned an empty secret."; + FinalizeFailure(); + return; + } + + DeriveKeyFromSecret(base::span(*secret_bytes)); +} + +void FreedesktopSecretKeyProvider::DeriveKeyFromSecret( + base::span<const uint8_t> secret) { + static_assert(kDerivedKeySizeInBits % 8 == 0); + std::array<uint8_t, kDerivedKeySizeInBits / 8> key_bytes; + crypto::kdf::DeriveKeyPbkdf2HmacSha1( + {kEncryptionIterations}, secret, + base::as_byte_span(base::span_from_cstring(kSalt)), key_bytes, + crypto::SubtlePassKey{}); + Encryptor::Key key(key_bytes, mojom::Algorithm::kAES128CBC); + FinalizeSuccess(std::move(key)); +} + +void FreedesktopSecretKeyProvider::FinalizeSuccess(Encryptor::Key key) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + std::move(key_callback_).Run(kEncryptionTag, std::move(key)); + CloseSession(); +} + +void FreedesktopSecretKeyProvider::FinalizeFailure() { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + if (!key_callback_) { + return; + } + std::move(key_callback_).Run(std::string(), std::nullopt); + CloseSession(); +} + +void FreedesktopSecretKeyProvider::CloseSession() { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + if (session_opened_) { + CallMethod(session_proxy_, kSecretSessionInterface, kMethodClose, + DbusVoid(), base::BindOnce([](std::optional<DbusVoid>) {})); + } +} + +} // namespace os_crypt_async
diff --git a/components/os_crypt/async/browser/freedesktop_secret_key_provider.h b/components/os_crypt/async/browser/freedesktop_secret_key_provider.h new file mode 100644 index 0000000..aad3ebc --- /dev/null +++ b/components/os_crypt/async/browser/freedesktop_secret_key_provider.h
@@ -0,0 +1,132 @@ +// Copyright 2024 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_OS_CRYPT_ASYNC_BROWSER_FREEDESKTOP_SECRET_KEY_PROVIDER_H_ +#define COMPONENTS_OS_CRYPT_ASYNC_BROWSER_FREEDESKTOP_SECRET_KEY_PROVIDER_H_ + +#include <map> +#include <memory> +#include <optional> +#include <string> +#include <vector> + +#include "base/files/scoped_file.h" +#include "base/gtest_prod_util.h" +#include "base/memory/raw_ptr.h" +#include "base/memory/weak_ptr.h" +#include "base/sequence_checker.h" +#include "build/branding_buildflags.h" +#include "components/dbus/properties/types.h" +#include "components/dbus/utils/check_for_service_and_start.h" +#include "components/dbus/utils/name_has_owner.h" +#include "components/os_crypt/async/browser/key_provider.h" +#include "crypto/encryptor.h" +#include "dbus/bus.h" +#include "dbus/object_path.h" +#include "dbus/object_proxy.h" + +namespace os_crypt_async { + +// FreedesktopSecretKeyProvider uses the org.freedesktop.secrets interface +// to retrieve a secret from backend (GNOME Keyring, KWallet, KeePassXC), +// which can then be used to encrypt confidential data. +class FreedesktopSecretKeyProvider : public KeyProvider { + public: + FreedesktopSecretKeyProvider(bool use_for_encryption, + const std::string& product_name, + scoped_refptr<dbus::Bus> bus); + ~FreedesktopSecretKeyProvider() override; + + // KeyProvider: + void GetKey(KeyCallback callback) override; + bool UseForEncryption() override; + bool IsCompatibleWithOsCryptSync() override; + + private: + FRIEND_TEST_ALL_PREFIXES(FreedesktopSecretKeyProviderTest, BasicHappyPath); + friend class FreedesktopSecretKeyProviderCompatTest; + + using DbusSecret = DbusStruct</*session=*/DbusObjectPath, + /*parameters=*/DbusByteArray, + /*value=*/DbusByteArray, + /*content_type=*/DbusString>; + + static constexpr char kSecretServiceName[] = "org.freedesktop.secrets"; + static constexpr char kSecretServicePath[] = "/org/freedesktop/secrets"; + static constexpr char kSecretServiceInterface[] = + "org.freedesktop.Secret.Service"; + static constexpr char kSecretCollectionInterface[] = + "org.freedesktop.Secret.Collection"; + static constexpr char kSecretItemInterface[] = "org.freedesktop.Secret.Item"; + static constexpr char kSecretSessionInterface[] = + "org.freedesktop.Secret.Session"; + + static constexpr char kMethodReadAlias[] = "ReadAlias"; + static constexpr char kMethodGetSecret[] = "GetSecret"; + static constexpr char kMethodOpenSession[] = "OpenSession"; + static constexpr char kMethodClose[] = "Close"; + static constexpr char kMethodSearchItems[] = "SearchItems"; + static constexpr char kPropertiesInterface[] = + "org.freedesktop.DBus.Properties"; + static constexpr char kMethodGet[] = "Get"; + + static constexpr char kDefaultAlias[] = "default"; + + // These constants are duplicated from the sync backend. + static constexpr char kApplicationAttributeKey[] = "application"; + static constexpr char kSchemaAttributeKey[] = "xdg:schema"; + static constexpr char kSchemaAttributeValue[] = + "chrome_libsecret_os_crypt_password_v2"; + + static constexpr char kAlgorithmPlain[] = "plain"; + static constexpr char kInputPlain[] = ""; + static constexpr char kMimePlain[] = "text/plain"; + + static constexpr char kSecretCollectionLabelProperty[] = + "org.freedesktop.Secret.Collection.Label"; + static constexpr char kSecretItemAttributesProperty[] = + "org.freedesktop.Secret.Item.Attributes"; + static constexpr char kSecretItemLabelProperty[] = + "org.freedesktop.Secret.Item.Label"; + static constexpr char kDefaultCollectionLabel[] = "Default Keyring"; + static constexpr char kLabelProperty[] = "Label"; + +#if BUILDFLAG(GOOGLE_CHROME_BRANDING) + static constexpr char kAppName[] = "chrome"; +#else + static constexpr char kAppName[] = "chromium"; +#endif + + void OnServiceStarted(std::optional<bool> service_started); + void OnReadAliasDefault(std::optional<DbusObjectPath> collection_path); + void OnOpenSession( + std::optional<DbusParameters<DbusVariant, DbusObjectPath>> session_reply); + void OnSearchItems(std::optional<DbusArray<DbusObjectPath>> results); + void OnGetSecret(std::optional<DbusSecret> secret_reply); + + void OpenSession(); + void DeriveKeyFromSecret(base::span<const uint8_t> secret); + void FinalizeSuccess(Encryptor::Key key); + void FinalizeFailure(); + void CloseSession(); + + raw_ptr<dbus::ObjectProxy> default_collection_proxy_ = nullptr; + raw_ptr<dbus::ObjectProxy> session_proxy_ = nullptr; + bool session_opened_ = false; + + const bool use_for_encryption_; + const std::string product_name_; + scoped_refptr<dbus::Bus> bus_; + KeyCallback key_callback_; + + std::string secret_for_testing_; + + SEQUENCE_CHECKER(sequence_checker_); + + base::WeakPtrFactory<FreedesktopSecretKeyProvider> weak_ptr_factory_{this}; +}; + +} // namespace os_crypt_async + +#endif // COMPONENTS_OS_CRYPT_ASYNC_BROWSER_FREEDESKTOP_SECRET_KEY_PROVIDER_H_
diff --git a/components/os_crypt/async/browser/freedesktop_secret_key_provider_compat_unittest.cc b/components/os_crypt/async/browser/freedesktop_secret_key_provider_compat_unittest.cc new file mode 100644 index 0000000..981a33ba --- /dev/null +++ b/components/os_crypt/async/browser/freedesktop_secret_key_provider_compat_unittest.cc
@@ -0,0 +1,143 @@ +// Copyright 2024 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <memory> +#include <optional> +#include <string> +#include <utility> +#include <vector> + +#include "base/run_loop.h" +#include "base/test/bind.h" +#include "base/test/task_environment.h" +#include "components/os_crypt/async/browser/fallback_linux_key_provider.h" +#include "components/os_crypt/async/browser/freedesktop_secret_key_provider.h" +#include "components/os_crypt/async/browser/os_crypt_async.h" +#include "components/os_crypt/sync/key_storage_linux.h" +#include "components/os_crypt/sync/os_crypt.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace os_crypt_async { + +namespace { + +constexpr char kSecretKey[] = "the_secret_key"; +constexpr char kPlaintext[] = "the_secret_plaintext"; + +class OSCryptMockerLinux : public KeyStorageLinux { + public: + OSCryptMockerLinux() = default; + + OSCryptMockerLinux(const OSCryptMockerLinux&) = delete; + OSCryptMockerLinux& operator=(const OSCryptMockerLinux&) = delete; + + ~OSCryptMockerLinux() override = default; + + bool Init() override { return true; } + std::optional<std::string> GetKeyImpl() override { return std::nullopt; } +}; + +std::unique_ptr<KeyStorageLinux> CreateNewMock() { + return std::make_unique<OSCryptMockerLinux>(); +} + +} // namespace + +// This class tests that FreedesktopSecretKeyProvider is forwards and backwards +// compatible with OSCrypt. +class FreedesktopSecretKeyProviderCompatTest : public ::testing::Test { + protected: + Encryptor GetEncryptorInstance(bool v11) { + std::vector<std::pair<size_t, std::unique_ptr<KeyProvider>>> providers; + if (v11) { + auto provider = std::make_unique<FreedesktopSecretKeyProvider>( + /*use_for_encryption=*/true, "", nullptr); + provider->secret_for_testing_ = kSecretKey; + providers.emplace_back(0, std::move(provider)); + } else { + providers.emplace_back(0, std::make_unique<FallbackLinuxKeyProvider>( + /*use_for_encryption=*/true)); + } + OSCryptAsync factory(std::move(providers)); + + base::RunLoop run_loop; + std::optional<Encryptor> encryptor; + auto subscription = + factory.GetInstance(base::BindLambdaForTesting( + [&](Encryptor encryptor_param, bool success) { + EXPECT_TRUE(success); + encryptor.emplace(std::move(encryptor_param)); + run_loop.Quit(); + }), + Encryptor::Option::kNone); + run_loop.Run(); + return std::move(*encryptor); + } + + void TearDown() override { OSCrypt::ClearCacheForTesting(); } + + base::test::TaskEnvironment task_environment_; +}; + +TEST_F(FreedesktopSecretKeyProviderCompatTest, IsAvailable) { + OSCrypt::SetEncryptionPasswordForTesting(kSecretKey); + Encryptor encryptor = GetEncryptorInstance(/*v11=*/true); + + ASSERT_TRUE(encryptor.IsEncryptionAvailable()); + ASSERT_TRUE(encryptor.IsDecryptionAvailable()); +} + +TEST_F(FreedesktopSecretKeyProviderCompatTest, DecryptOldV11) { + OSCrypt::SetEncryptionPasswordForTesting(kSecretKey); + Encryptor encryptor = GetEncryptorInstance(/*v11=*/true); + + std::string ciphertext; + ASSERT_TRUE(OSCrypt::EncryptString(kPlaintext, &ciphertext)); + EXPECT_TRUE(base::StartsWith(ciphertext, "v11")); + + std::string decrypted; + EXPECT_TRUE(encryptor.DecryptString(ciphertext, &decrypted)); + EXPECT_EQ(kPlaintext, decrypted); +} + +TEST_F(FreedesktopSecretKeyProviderCompatTest, EncryptForOldV11) { + OSCrypt::SetEncryptionPasswordForTesting(kSecretKey); + Encryptor encryptor = GetEncryptorInstance(/*v11=*/true); + + std::string ciphertext; + ASSERT_TRUE(encryptor.EncryptString(kPlaintext, &ciphertext)); + EXPECT_TRUE(base::StartsWith(ciphertext, "v11")); + + std::string decrypted; + EXPECT_TRUE(OSCrypt::DecryptString(ciphertext, &decrypted)); + EXPECT_EQ(kPlaintext, decrypted); +} + +TEST_F(FreedesktopSecretKeyProviderCompatTest, DecryptOldV10) { + OSCrypt::UseMockKeyStorageForTesting(base::BindOnce(&CreateNewMock)); + Encryptor encryptor = GetEncryptorInstance(/*v11=*/false); + + std::string ciphertext; + ASSERT_TRUE(OSCrypt::EncryptString(kPlaintext, &ciphertext)); + EXPECT_TRUE(base::StartsWith(ciphertext, "v10")); + + std::string decrypted; + EXPECT_TRUE(encryptor.DecryptString(ciphertext, &decrypted)); + EXPECT_EQ(kPlaintext, decrypted); +} + +TEST_F(FreedesktopSecretKeyProviderCompatTest, EncryptForOldV10) { + OSCrypt::UseMockKeyStorageForTesting(base::BindOnce(&CreateNewMock)); + Encryptor encryptor = GetEncryptorInstance(/*v11=*/false); + + std::string ciphertext; + ASSERT_TRUE(encryptor.EncryptString(kPlaintext, &ciphertext)); + EXPECT_TRUE(base::StartsWith(ciphertext, "v10")); + + std::string decrypted; + EXPECT_TRUE(OSCrypt::DecryptString(ciphertext, &decrypted)); + EXPECT_EQ(kPlaintext, decrypted); +} + +} // namespace os_crypt_async
diff --git a/components/os_crypt/async/browser/freedesktop_secret_key_provider_unittest.cc b/components/os_crypt/async/browser/freedesktop_secret_key_provider_unittest.cc new file mode 100644 index 0000000..0568d22 --- /dev/null +++ b/components/os_crypt/async/browser/freedesktop_secret_key_provider_unittest.cc
@@ -0,0 +1,225 @@ +// Copyright 2024 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/os_crypt/async/browser/freedesktop_secret_key_provider.h" + +#include <memory> +#include <optional> +#include <string> + +#include "base/files/scoped_file.h" +#include "base/memory/ref_counted_memory.h" +#include "base/memory/scoped_refptr.h" +#include "base/test/bind.h" +#include "build/branding_buildflags.h" +#include "components/dbus/properties/types.h" +#include "components/dbus/utils/name_has_owner.h" +#include "crypto/encryptor.h" +#include "dbus/message.h" +#include "dbus/mock_bus.h" +#include "dbus/mock_object_proxy.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +using ::testing::_; +using ::testing::AtLeast; +using ::testing::Return; + +namespace os_crypt_async { + +namespace { + +constexpr char kProductName[] = "test_product"; +constexpr char kCollectionPath[] = + "/org/freedesktop/secrets/collection/default"; +constexpr char kSessionPath[] = "/org/freedesktop/secrets/session/test_session"; +constexpr char kItemPath[] = + "/org/freedesktop/secrets/collection/default/item0"; + +constexpr char kFakeSecret[] = "c3VwZXJfc2VjcmV0X2tleQ=="; + +template <typename T> +class MatchArgs { + public: + using is_gtest_matcher = void; + + explicit MatchArgs(T&& args) : args_(std::forward<T>(args)) {} + + bool MatchAndExplain(const DbusVariant& match, std::ostream*) const { + const T* match_args = match.GetAs<T>(); + return match_args && *match_args == args_; + } + + void DescribeTo(std::ostream* os) const { *os << "DbusTypes match"; } + + void DescribeNegationTo(std::ostream* os) const { + *os << "DbusTypes mismatch"; + } + + private: + T args_; +}; + +template <typename T> +auto RespondWith(T&& args) { + return [args = std::move(args)]( + const std::string&, const std::string&, DbusVariant, + dbus::ObjectProxy::ResponseCallback* callback) { + auto response = dbus::Response::CreateEmpty(); + dbus::MessageWriter writer(response.get()); + args.Write(&writer); + std::move(*callback).Run(response.get()); + }; +} + +class MockObjectProxyWithTypedCalls : public dbus::MockObjectProxy { + public: + MockObjectProxyWithTypedCalls(dbus::Bus* bus, + const std::string& service_name, + const dbus::ObjectPath& object_path) + : dbus::MockObjectProxy(bus, service_name, object_path) { + // Forward to Call(). + EXPECT_CALL(*this, DoCallMethod(_, _, _)) + .Times(AtLeast(0)) + .WillRepeatedly([this](dbus::MethodCall* method_call, int timeout_ms, + dbus::ObjectProxy::ResponseCallback* callback) { + dbus::MessageReader reader(method_call); + auto args = ReadDbusMessage(&reader); + Call(method_call->GetInterface(), method_call->GetMember(), + std::move(args), callback); + }); + } + + MOCK_METHOD4(Call, + void(const std::string& interface, + const std::string& method_name, + DbusVariant args, + dbus::ObjectProxy::ResponseCallback* callback)); + + protected: + ~MockObjectProxyWithTypedCalls() override = default; +}; + +} // namespace + +TEST(FreedesktopSecretKeyProviderTest, BasicHappyPath) { + auto mock_bus = base::MakeRefCounted<dbus::MockBus>(dbus::Bus::Options()); + + // Initialize object proxies + auto mock_dbus_proxy = base::MakeRefCounted<MockObjectProxyWithTypedCalls>( + mock_bus.get(), DBUS_SERVICE_DBUS, dbus::ObjectPath(DBUS_PATH_DBUS)); + EXPECT_CALL(*mock_bus, GetObjectProxy(DBUS_SERVICE_DBUS, + dbus::ObjectPath(DBUS_PATH_DBUS))) + .WillRepeatedly(Return(mock_dbus_proxy.get())); + auto mock_service_proxy = base::MakeRefCounted<MockObjectProxyWithTypedCalls>( + mock_bus.get(), FreedesktopSecretKeyProvider::kSecretServiceName, + dbus::ObjectPath(FreedesktopSecretKeyProvider::kSecretServicePath)); + EXPECT_CALL( + *mock_bus, + GetObjectProxy( + FreedesktopSecretKeyProvider::kSecretServiceName, + dbus::ObjectPath(FreedesktopSecretKeyProvider::kSecretServicePath))) + .WillRepeatedly(Return(mock_service_proxy.get())); + auto mock_collection_proxy = + base::MakeRefCounted<MockObjectProxyWithTypedCalls>( + mock_bus.get(), FreedesktopSecretKeyProvider::kSecretServiceName, + dbus::ObjectPath(kCollectionPath)); + EXPECT_CALL(*mock_bus, + GetObjectProxy(FreedesktopSecretKeyProvider::kSecretServiceName, + dbus::ObjectPath(kCollectionPath))) + .WillRepeatedly(Return(mock_collection_proxy.get())); + auto mock_item_proxy = base::MakeRefCounted<MockObjectProxyWithTypedCalls>( + mock_bus.get(), FreedesktopSecretKeyProvider::kSecretServiceName, + dbus::ObjectPath(kItemPath)); + EXPECT_CALL(*mock_bus, + GetObjectProxy(FreedesktopSecretKeyProvider::kSecretServiceName, + dbus::ObjectPath(kItemPath))) + .WillRepeatedly(Return(mock_item_proxy.get())); + auto mock_session_proxy = base::MakeRefCounted<MockObjectProxyWithTypedCalls>( + mock_bus.get(), FreedesktopSecretKeyProvider::kSecretServiceName, + dbus::ObjectPath(kSessionPath)); + EXPECT_CALL(*mock_bus, + GetObjectProxy(FreedesktopSecretKeyProvider::kSecretServiceName, + dbus::ObjectPath(kSessionPath))) + .WillRepeatedly(Return(mock_session_proxy.get())); + + // NameHasOwner for Secret Service + EXPECT_CALL(*mock_dbus_proxy, + Call(DBUS_INTERFACE_DBUS, "NameHasOwner", + MatchArgs(DbusString( + FreedesktopSecretKeyProvider::kSecretServiceName)), + _)) + .WillOnce(RespondWith(DbusBoolean(true))); + + // ReadAlias("default") + EXPECT_CALL( + *mock_service_proxy, + Call(FreedesktopSecretKeyProvider::kSecretServiceInterface, + FreedesktopSecretKeyProvider::kMethodReadAlias, + MatchArgs(DbusString(FreedesktopSecretKeyProvider::kDefaultAlias)), + _)) + .WillOnce(RespondWith(DbusObjectPath(dbus::ObjectPath(kCollectionPath)))); + + // OpenSession + EXPECT_CALL( + *mock_service_proxy, + Call(FreedesktopSecretKeyProvider::kSecretServiceInterface, + FreedesktopSecretKeyProvider::kMethodOpenSession, + MatchArgs(MakeDbusParameters( + DbusString(FreedesktopSecretKeyProvider::kAlgorithmPlain), + MakeDbusVariant( + DbusString(FreedesktopSecretKeyProvider::kInputPlain)))), + _)) + .WillOnce(RespondWith( + MakeDbusParameters(MakeDbusVariant(DbusString("")), + DbusObjectPath(dbus::ObjectPath(kSessionPath))))); + + // SearchItems + EXPECT_CALL( + *mock_collection_proxy, + Call(FreedesktopSecretKeyProvider::kSecretCollectionInterface, + FreedesktopSecretKeyProvider::kMethodSearchItems, + MatchArgs(MakeDbusArray(MakeDbusDictEntry( + DbusString( + FreedesktopSecretKeyProvider::kApplicationAttributeKey), + DbusString(FreedesktopSecretKeyProvider::kAppName)))), + _)) + .WillOnce(RespondWith( + MakeDbusArray(DbusObjectPath(dbus::ObjectPath(kItemPath))))); + + // GetSecret + EXPECT_CALL( + *mock_item_proxy, + Call(FreedesktopSecretKeyProvider::kSecretItemInterface, + FreedesktopSecretKeyProvider::kMethodGetSecret, + MatchArgs(DbusObjectPath(dbus::ObjectPath(kSessionPath))), _)) + .WillOnce(RespondWith(MakeDbusStruct( + DbusObjectPath(dbus::ObjectPath(kSessionPath)), + DbusByteArray(base::MakeRefCounted<base::RefCountedString>("")), + DbusByteArray( + base::MakeRefCounted<base::RefCountedString>(kFakeSecret)), + DbusString(FreedesktopSecretKeyProvider::kMimePlain)))); + + // Close + EXPECT_CALL(*mock_session_proxy, + Call(FreedesktopSecretKeyProvider::kSecretSessionInterface, + FreedesktopSecretKeyProvider::kMethodClose, + MatchArgs(DbusVoid()), _)) + .WillOnce(RespondWith(DbusVoid())); + + FreedesktopSecretKeyProvider provider(/*use_for_encryption=*/true, + kProductName, mock_bus); + std::string tag; + std::optional<Encryptor::Key> key; + provider.GetKey(base::BindLambdaForTesting( + [&](const std::string& returned_tag, + std::optional<Encryptor::Key> returned_key) { + tag = returned_tag; + key = std::move(returned_key); + })); + EXPECT_EQ(tag, "v11"); + EXPECT_TRUE(key.has_value()); +} + +} // namespace os_crypt_async
diff --git a/components/os_crypt/async/common/algorithm.mojom b/components/os_crypt/async/common/algorithm.mojom index 528e29d..6a153c5 100644 --- a/components/os_crypt/async/common/algorithm.mojom +++ b/components/os_crypt/async/common/algorithm.mojom
@@ -9,4 +9,7 @@ // Algorithm used on Windows: 256 bit key with 96 bit random nonce at the // start of the data. kAES256GCM, + + // Compatible with "v11" os_crypt_sync encryption on Linux. + kAES128CBC, };
diff --git a/components/os_crypt/async/common/encryptor.cc b/components/os_crypt/async/common/encryptor.cc index 247062f..53c0cb8 100644 --- a/components/os_crypt/async/common/encryptor.cc +++ b/components/os_crypt/async/common/encryptor.cc
@@ -16,6 +16,7 @@ #include "components/os_crypt/async/common/algorithm.mojom.h" #include "components/os_crypt/sync/os_crypt.h" #include "crypto/aead.h" +#include "crypto/aes_cbc.h" #include "crypto/random.h" #include "mojo/public/cpp/bindings/default_construct_tag.h" #include "third_party/abseil-cpp/absl/cleanup/cleanup.h" @@ -34,6 +35,11 @@ constexpr size_t kNonceLength = 96 / 8; // AES_GCM_NONCE_LENGTH +constexpr std::array<uint8_t, crypto::aes_cbc::kBlockSize> kFixedIvForAes128Cbc{ + ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', + ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', +}; + } // namespace Encryptor::Key::Key(base::span<const uint8_t> key, @@ -59,6 +65,9 @@ case mojom::Algorithm::kAES256GCM: CHECK_EQ(key.size(), Key::kAES256GCMKeySize); break; + case mojom::Algorithm::kAES128CBC: + CHECK_EQ(key.size(), Key::kAES128CBCKeySize); + break; } } @@ -141,6 +150,11 @@ ciphertext.insert(ciphertext.begin(), nonce.cbegin(), nonce.cend()); return ciphertext; } + case mojom::Algorithm::kAES128CBC: { + std::vector<uint8_t> ciphertext = crypto::aes_cbc::Encrypt( + key_, base::as_byte_span(kFixedIvForAes128Cbc), plaintext); + return ciphertext; + } } LOG(FATAL) << "Unsupported algorithm" << static_cast<int>(*algorithm_); } @@ -177,6 +191,21 @@ return aead.Open(data, nonce, /*additional_data=*/{}); } + case mojom::Algorithm::kAES128CBC: { + auto plaintext = + crypto::aes_cbc::Decrypt(key_, kFixedIvForAes128Cbc, ciphertext); + if (plaintext.has_value()) { + return plaintext; + } + // Decryption failed - try the empty fallback key. See + // https://crbug.com/40055416. + // PBKDF2-HMAC-SHA1(1 iteration, key = "", salt = "saltysalt") + constexpr auto kEmptyKey = std::to_array<uint8_t>( + {0xd0, 0xd0, 0xec, 0x9c, 0x7d, 0x77, 0xd4, 0x3a, 0xc5, 0x41, 0x87, + 0xfa, 0x48, 0x18, 0xd1, 0x7f}); + return crypto::aes_cbc::Decrypt(kEmptyKey, kFixedIvForAes128Cbc, + ciphertext); + } } LOG(FATAL) << "Unsupported algorithm" << static_cast<int>(*algorithm_); }
diff --git a/components/os_crypt/async/common/encryptor.h b/components/os_crypt/async/common/encryptor.h index ffd939a1..b3809d1 100644 --- a/components/os_crypt/async/common/encryptor.h +++ b/components/os_crypt/async/common/encryptor.h
@@ -51,6 +51,7 @@ ~Key(); static constexpr size_t kAES256GCMKeySize = 256u / 8u; + static constexpr size_t kAES128CBCKeySize = 128u / 8u; // Mojo uses this public constructor for serialization. explicit Key(mojo::DefaultConstruct::Tag);
diff --git a/components/os_crypt/async/common/encryptor_mojom_traits.cc b/components/os_crypt/async/common/encryptor_mojom_traits.cc index afb94b4..6c53660 100644 --- a/components/os_crypt/async/common/encryptor_mojom_traits.cc +++ b/components/os_crypt/async/common/encryptor_mojom_traits.cc
@@ -66,6 +66,10 @@ switch (data.algorithm()) { case os_crypt_async::mojom::Algorithm::kAES256GCM: key_size.emplace(os_crypt_async::Encryptor::Key::kAES256GCMKeySize); + break; + case os_crypt_async::mojom::Algorithm::kAES128CBC: + key_size.emplace(os_crypt_async::Encryptor::Key::kAES128CBCKeySize); + break; } if (!key_size.has_value()) {
diff --git a/components/payments/content/android/java/src/org/chromium/components/payments/PaymentRequestWebContentsData.java b/components/payments/content/android/java/src/org/chromium/components/payments/PaymentRequestWebContentsData.java index 4172a08..9e10b03 100644 --- a/components/payments/content/android/java/src/org/chromium/components/payments/PaymentRequestWebContentsData.java +++ b/components/payments/content/android/java/src/org/chromium/components/payments/PaymentRequestWebContentsData.java
@@ -59,10 +59,8 @@ * @return Whether there has been an activationless PaymentRequest.show() for this WebContents. */ public boolean hadActivationlessShow() { - if (mWebContents == null) return false; - WebContents webContents = mWebContents.get(); + WebContents webContents = getWebContents(); if (webContents == null || webContents.isDestroyed()) return false; - return PaymentRequestWebContentsDataJni.get().hadActivationlessShow(webContents); } @@ -71,10 +69,8 @@ * tracked on the native side in PaymentRequestWebContentsManager. */ public void recordActivationlessShow() { - if (mWebContents == null) return; - WebContents webContents = mWebContents.get(); + WebContents webContents = getWebContents(); if (webContents == null || webContents.isDestroyed()) return; - PaymentRequestWebContentsDataJni.get().recordActivationlessShow(webContents); }
diff --git a/components/pdf/browser/pdf_document_helper.cc b/components/pdf/browser/pdf_document_helper.cc index 0e67647..cfb3c625 100644 --- a/components/pdf/browser/pdf_document_helper.cc +++ b/components/pdf/browser/pdf_document_helper.cc
@@ -253,6 +253,15 @@ remote_pdf_client_->GetPageText(page_index, std::move(callback)); } +void PDFDocumentHelper::GetMostVisiblePageIndex( + pdf::mojom::PdfListener::GetMostVisiblePageIndexCallback callback) { + if (!remote_pdf_client_) { + std::move(callback).Run(std::nullopt); + return; + } + remote_pdf_client_->GetMostVisiblePageIndex(std::move(callback)); +} + void PDFDocumentHelper::OnSelectionEvent(ui::SelectionEventType event) { // Should be handled by `TouchSelectionControllerClientAura`. NOTREACHED();
diff --git a/components/pdf/browser/pdf_document_helper.h b/components/pdf/browser/pdf_document_helper.h index ca3be36..97e3b34c 100644 --- a/components/pdf/browser/pdf_document_helper.h +++ b/components/pdf/browser/pdf_document_helper.h
@@ -99,6 +99,8 @@ void GetPageText(int32_t page_index, pdf::mojom::PdfListener::GetPageTextCallback callback); + void GetMostVisiblePageIndex( + pdf::mojom::PdfListener::GetMostVisiblePageIndexCallback callback); private: friend class content::DocumentUserData<PDFDocumentHelper>;
diff --git a/components/pdf/browser/pdf_document_helper_browsertest.cc b/components/pdf/browser/pdf_document_helper_browsertest.cc index d24e5e9..d56e60a 100644 --- a/components/pdf/browser/pdf_document_helper_browsertest.cc +++ b/components/pdf/browser/pdf_document_helper_browsertest.cc
@@ -49,6 +49,10 @@ GetPageText, (int32_t, GetPageTextCallback callback), (override)); + MOCK_METHOD(void, + GetMostVisiblePageIndex, + (GetMostVisiblePageIndexCallback callback), + (override)); }; class TestPDFDocumentHelperClient : public PDFDocumentHelperClient {
diff --git a/components/safe_browsing/content/browser/download/download_stats.cc b/components/safe_browsing/content/browser/download/download_stats.cc index 9a92066..5504b83e 100644 --- a/components/safe_browsing/content/browser/download/download_stats.cc +++ b/components/safe_browsing/content/browser/download/download_stats.cc
@@ -112,23 +112,6 @@ base::UserMetricsAction("SafeBrowsing.Download.WarningBypassed")); } -void RecordDownloadOpenedLatency(download::DownloadDangerType danger_type, - download::DownloadContent download_content, - base::Time download_opened_time, - base::Time download_end_time, - bool show_download_in_folder) { - if (danger_type != download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS) { - return; - } - std::string metric_suffix = - show_download_in_folder ? ".ShowInFolder" : ".OpenDirectly"; - base::UmaHistogramCustomTimes( - "SBClientDownload.SafeDownloadOpenedLatency2" + metric_suffix, - /* sample */ download_opened_time - download_end_time, - /* min */ base::Seconds(1), - /* max */ base::Days(1), /* buckets */ 50); -} - void RecordDownloadFileTypeAttributes( DownloadFileType::DangerLevel danger_level, bool has_user_gesture,
diff --git a/components/safe_browsing/content/browser/download/download_stats.h b/components/safe_browsing/content/browser/download/download_stats.h index 22de644..50650cf 100644 --- a/components/safe_browsing/content/browser/download/download_stats.h +++ b/components/safe_browsing/content/browser/download/download_stats.h
@@ -56,22 +56,6 @@ bool is_https, bool has_user_gesture); -// Records the latency after completion a download was opened from the download -// shelf/bubble or the chrome://downloads page, or show in folder was clicked. -void RecordDownloadOpenedLatency(download::DownloadDangerType danger_type, - download::DownloadContent download_content, - base::Time download_opened_time, - base::Time download_end_time, - bool show_download_in_folder); - -// Records the latency after completion for when a download was opened (via the -// shelf/bubble or chrome://downloads), or show in folder was clicked, by -// extension type. -void RecordDownloadOpenedLatencyFileType( - download::DownloadContent download_content, - base::Time download_opened_time, - base::Time download_end_time); - // Records the attributes of a download. void RecordDownloadFileTypeAttributes( DownloadFileType::DangerLevel danger_level,
diff --git a/components/safe_browsing/content/browser/download/download_stats_unittest.cc b/components/safe_browsing/content/browser/download/download_stats_unittest.cc index 95a2aa0..58d4795b 100644 --- a/components/safe_browsing/content/browser/download/download_stats_unittest.cc +++ b/components/safe_browsing/content/browser/download/download_stats_unittest.cc
@@ -85,39 +85,6 @@ "SafeBrowsing.Download.WarningBypassed")); } -TEST(SafeBrowsingDownloadStatsTest, RecordDownloadOpened) { - base::HistogramTester histogram_tester; - - base::Time download_end_time = base::Time::Now(); - download::DownloadContent fake_content = - download::DownloadContent::kSpreadSheet; - // Not logged for dangerous downloads. - RecordDownloadOpenedLatency( - download::DownloadDangerType::DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT, - fake_content, download_end_time + base::Days(1), download_end_time, - /*show_download_in_folder=*/false); - histogram_tester.ExpectTotalCount( - "SBClientDownload.SafeDownloadOpenedLatency2.OpenDirectly", 0); - - RecordDownloadOpenedLatency( - download::DownloadDangerType::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS, - fake_content, download_end_time + base::Days(1), download_end_time, - /*show_download_in_folder=*/false); - histogram_tester.ExpectTimeBucketCount( - "SBClientDownload.SafeDownloadOpenedLatency2.OpenDirectly", - /*sample=*/base::Days(1), - /*count=*/1); - - RecordDownloadOpenedLatency( - download::DownloadDangerType::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS, - fake_content, download_end_time + base::Hours(5), download_end_time, - /*show_download_in_folder=*/true); - histogram_tester.ExpectTimeBucketCount( - "SBClientDownload.SafeDownloadOpenedLatency2.ShowInFolder", - /*sample=*/base::Hours(5), - /*count=*/1); -} - TEST(SafeBrowsingDownloadStatsTest, RecordDownloadFileTypeAttributes) { { base::HistogramTester histogram_tester;
diff --git a/content/browser/android/selection/selection_popup_controller.cc b/content/browser/android/selection/selection_popup_controller.cc index 91e6f41..f511d0e 100644 --- a/content/browser/android/selection/selection_popup_controller.cc +++ b/content/browser/android/selection/selection_popup_controller.cc
@@ -9,6 +9,7 @@ #include "base/android/jni_android.h" #include "base/android/jni_string.h" #include "base/android/scoped_java_ref.h" +#include "base/feature_list.h" #include "content/browser/android/selection/composited_touch_handle_drawable.h" #include "content/browser/gpu/gpu_data_manager_impl.h" #include "content/browser/renderer_host/render_widget_host_view_android.h" @@ -63,6 +64,10 @@ return enabled; } +BASE_FEATURE(kDismissMagnifierOnViewSwap, + "DismissMagnifierOnViewSwap", + base::FEATURE_ENABLED_BY_DEFAULT); + } // namespace static jboolean @@ -200,6 +205,17 @@ if (new_rwhva) new_rwhva->set_selection_popup_controller(this); rwhva_ = new_rwhva; + + if (!base::FeatureList::IsEnabled(kDismissMagnifierOnViewSwap)) { + return; + } + JNIEnv* env = AttachCurrentThread(); + ScopedJavaLocalRef<jobject> obj = java_obj_.get(env); + if (obj.is_null()) { + return; + } + + Java_SelectionPopupControllerImpl_renderWidgetHostViewChanged(env, obj); } void SelectionPopupController::OnSelectionEvent(
diff --git a/content/browser/webui/web_ui_mojo_browsertest.cc b/content/browser/webui/web_ui_mojo_browsertest.cc index 6a5858f6..fa265af3 100644 --- a/content/browser/webui/web_ui_mojo_browsertest.cc +++ b/content/browser/webui/web_ui_mojo_browsertest.cc
@@ -48,6 +48,7 @@ #include "mojo/public/cpp/bindings/binder_map.h" #include "mojo/public/cpp/bindings/pending_receiver.h" #include "mojo/public/cpp/bindings/receiver.h" +#include "mojo/public/mojom/base/time.mojom.h" #include "third_party/blink/public/common/chrome_debug_urls.h" namespace content { @@ -113,6 +114,10 @@ dict_ptr ? dict_ptr->Clone() : nullptr); } + void EchoTypemaps(base::Time time, EchoTypemapsCallback cb) override { + std::move(cb).Run(time); + } + private: mojo::Receiver<mojom::WebUITsMojoTestCache> receiver_; std::map<GURL, std::string> cache_;
diff --git a/content/public/android/java/src/org/chromium/content/browser/selection/SelectionPopupControllerImpl.java b/content/public/android/java/src/org/chromium/content/browser/selection/SelectionPopupControllerImpl.java index 1f75933..52bd611 100644 --- a/content/public/android/java/src/org/chromium/content/browser/selection/SelectionPopupControllerImpl.java +++ b/content/public/android/java/src/org/chromium/content/browser/selection/SelectionPopupControllerImpl.java
@@ -1503,6 +1503,13 @@ } } + @CalledByNative + private void renderWidgetHostViewChanged() { + if (getMagnifierAnimator() != null) { + getMagnifierAnimator().handleDragStopped(); + } + } + // All coordinates are in DIP. @VisibleForTesting @CalledByNative
diff --git a/content/public/android/java/src/org/chromium/content_public/browser/WebContentsObserver.java b/content/public/android/java/src/org/chromium/content_public/browser/WebContentsObserver.java index 259cb70..8997018 100644 --- a/content/public/android/java/src/org/chromium/content_public/browser/WebContentsObserver.java +++ b/content/public/android/java/src/org/chromium/content_public/browser/WebContentsObserver.java
@@ -16,7 +16,6 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; -import java.lang.ref.WeakReference; /** * This class receives callbacks that act as hooks for various a native web contents events related @@ -24,16 +23,19 @@ */ @NullMarked public abstract class WebContentsObserver { - // TODO(jdduke): Remove the destroy method and hold observer embedders - // responsible for explicit observer detachment. - // Using a weak reference avoids cycles that might prevent GC of WebView's WebContents. - protected @Nullable WeakReference<WebContents> mWebContents; + private @Nullable WebContents mWebContents; public WebContentsObserver(WebContents webContents) { - mWebContents = new WeakReference<WebContents>(webContents); + mWebContents = webContents; webContents.addObserver(this); } + /** Return the web contents associated with the observer. */ + @Nullable + public WebContents getWebContents() { + return mWebContents; + } + /** * Called when a RenderFrame for renderFrameHost is created in the renderer process. To avoid * creating a RenderFrameHost object without necessity, only its id is passed. Call @@ -244,9 +246,8 @@ /** Stop observing the web contents and clean up associated references. */ public void destroy() { if (mWebContents == null) return; - final WebContents webContents = mWebContents.get(); + final WebContents webContents = mWebContents; mWebContents = null; - if (webContents == null) return; webContents.removeObserver(this); }
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn index fac5ded..e9478ce 100644 --- a/content/test/BUILD.gn +++ b/content/test/BUILD.gn
@@ -1150,7 +1150,10 @@ "data/web_ui_ts_test.test-mojom", "data/web_ui_ts_test_types.test-mojom", ] - public_deps = [ "//url/mojom:url_mojom_gurl" ] + public_deps = [ + "//mojo/public/mojom/base", + "//url/mojom:url_mojom_gurl", + ] webui_module_path = "/content/test/data" deps = [ ":web_ui_ts_test_other_mojo_bindings" ]
diff --git a/content/test/data/web_ui_mojo_ts_test.ts b/content/test/data/web_ui_mojo_ts_test.ts index 1203b95..0eadbbc 100644 --- a/content/test/data/web_ui_mojo_ts_test.ts +++ b/content/test/data/web_ui_mojo_ts_test.ts
@@ -225,6 +225,13 @@ } } + { + const result = await cache.echoTypemaps(new Date(12321)); + assert( + result.time.getTime() === new Date(12321).getTime(), + `unexpected date received ${result.time.getTime()}`); + } + return true; }
diff --git a/content/test/data/web_ui_ts_test.test-mojom b/content/test/data/web_ui_ts_test.test-mojom index de5ef0a..eb88c8c9 100644 --- a/content/test/data/web_ui_ts_test.test-mojom +++ b/content/test/data/web_ui_ts_test.test-mojom
@@ -6,6 +6,7 @@ import "content/test/data/web_ui_ts_test_types.test-mojom"; import "content/test/data/web_ui_ts_test_other_types.test-mojom"; +import "mojo/public/mojom/base/time.mojom"; import "url/mojom/url.mojom"; enum TestEnum { @@ -63,4 +64,8 @@ SimpleMappedType simple_mapped_type, NestedMappedType nested_mapped_type, StringDict? other_mapped_type); + + EchoTypemaps( + mojo_base.mojom.JSTime time + ) => (mojo_base.mojom.JSTime time); };
diff --git a/content/test/gpu/gpu_tests/gpu_integration_test.py b/content/test/gpu/gpu_tests/gpu_integration_test.py index e70cfa3..5f82b32 100644 --- a/content/test/gpu/gpu_tests/gpu_integration_test.py +++ b/content/test/gpu/gpu_tests/gpu_integration_test.py
@@ -556,8 +556,10 @@ if not cls._args_changed_this_browser_start: return - # chrome://gpu does not exist for Webview. - if cls.browser.browser_type == 'android-webview-instrumentation': + # chrome://gpu does not exist for Webview or the Fuchsia cast streaming + # shell. + if cls.browser.browser_type in ('android-webview-instrumentation', + 'cast-streaming-shell'): return # TODO(crbug.com/376498163): Remove this early return once Telemetry's
diff --git a/crypto/subtle_passkey.h b/crypto/subtle_passkey.h index ab28bd2..5143935 100644 --- a/crypto/subtle_passkey.h +++ b/crypto/subtle_passkey.h
@@ -23,6 +23,10 @@ crypto::SubtlePassKey MakeCryptoPassKey(); } +namespace os_crypt_async { +class FreedesktopSecretKeyProvider; +} + class OSCryptImpl; namespace crypto { @@ -57,9 +61,10 @@ // arbitrary (possibly attacker-supplied) PBKDF2 parameters. friend SubtlePassKey chromeos::onc::MakeCryptoPassKey(); - // This class uses custom PBKDF2 parameters and has to keep doing so for + // These classes use custom PBKDF2 parameters and have to keep doing so for // compatibility with existing persisted data. friend class ::OSCryptImpl; + friend class os_crypt_async::FreedesktopSecretKeyProvider; }; } // namespace crypto
diff --git a/docs/README.md b/docs/README.md index 7f36c13..f8ed76d 100644 --- a/docs/README.md +++ b/docs/README.md
@@ -463,7 +463,8 @@ * [What's Up With Processes - Episode 8](transcripts/wuwt-e08-processes.md) * [What's Up With Site Isolation - Episode 9](transcripts/wuwt-e09-site-isolation.md) * [What's Up With Web Platform - Episode 10](transcripts/wuwt-e10-web-platform.md) -* [What's Up With Web Standards - Episode 11](transcriptswuwt-e11-web-standards.md) +* [What's Up With Web Standards - Episode 11](transcripts/wuwt-e11-web-standards.md) +* [What's Up With Base - Episode 12](transcripts/wuwt-e12-base.md) ### Probably Obsolete * [TPM Quick Reference](tpm_quick_ref.md) - Trusted Platform Module notes.
diff --git a/docs/transcripts/wuwt-e12-base.md b/docs/transcripts/wuwt-e12-base.md new file mode 100644 index 0000000..65dcf19 --- /dev/null +++ b/docs/transcripts/wuwt-e12-base.md
@@ -0,0 +1,1008 @@ +# What’s Up With Base + +This is a transcript of [What's Up With +That](https://www.youtube.com/playlist?list=PL9ioqAuyl6ULIdZQys3fwRxi3G3ns39Hq) +Episode 12, a 2024 video discussion between [Sharon (yangsharon@chromium.org) +and Peter +(pkasting@chromium.org)](https://www.youtube.com/watch?v=hXTcG7DJ3Ms). + +The transcript was automatically generated by speech-to-text software. It may +contain minor errors. + +--- + +Base is one of the lowest level directories in Chromium. What's in there? What +should you be using? Why don't we just use the C++ standard library? + +Notes: + +- https://docs.google.com/document/d/1Monua0VRs_JIR-NU9mQ7MO_dw-uiFDK9QAj1Sj_V32g/edit + +Links: + +- [C++ 201] + +--- + +00:00 SHARON: Hello, and welcome to "What's Up With That?," the series that +demystifies all things Chrome. I'm your host, Sharon, and today we're talking +about //base. What is it the base of? What are Chromium-specific types we use? +How does it fit in with C++ at large? Today's special guest answering all of +that and more is Peter. He's our newest //base owner, a longtime team member, +and a driver behind style guide changes, C++ future allowances, and updating to +new versions of C++. He has a series, [C++ 201], on this channel that helped +inspire this series. So welcome, Peter. + +00:33 PETER: Thank you. + +00:33 SHARON: So what is //base? + +00:39 PETER: It's one of our lowest level directories, and it pretty much has +all the low-level stuff everything else depends on. + +00:45 SHARON: Sounds important. So can you tell us a bit more about what those +specific things are and why they're important? + +00:51 PETER: Yeah. So stuff goes in //base if it's broadly useful to lots of +different unrelated places and if it's semantically fundamental, like maybe it +would go in the STL, at least if it were broadly useful to C++, and if you +can't move it to a better, more specific subdirectory. Anything that fits those +is probably a good candidate. + +01:17 SHARON: OK, so what are things that do versus don't belong in //base, +then? + +01:24 PETER: If it's something that is maybe useful for programming in general +but we don't need it in Chrome specifically, then it doesn't go in //base. +We're not trying to make a toolkit for general-purpose use outside Chrome. If +it's just speculative-- you came up with a cool idea and you're hoping somebody +might use it-- then probably don't put it in //base, at least until you can +actually make various things use it. And also, it shouldn't overlap with +anything that's already in the STL or absl or elsewhere in //base, at least, +unless we have some clear guidance on, here's how you pick which alternative to +use, and ideally, some tooling doing that too. + +02:03 SHARON: Yeah, if you look around the Chromium codebase, you see base +types, you see absl types, and you see standard library types. So when do we +use each of these libraries? Because certain things-- if you were around before +we used to use base optional. Now we use absl optional. Or do we use std +optional now? + +02:22 PETER: We do use std optional. + +02:22 SHARON: So that changes, and that has changed over time. So when do we +use which ones? + +02:28 PETER: Yeah, we've used all three of those, actually. Generally, all else +being equal, we will prefer the STL if it's available and absl if it's there +and //base if it's not in either one. So with optional, we had an optional +before the STL had it because it got it in C++ 17, and we had optional long +before that. And then when we allowed absl, we switched to absl optional +because it was there. And then when we allowed C++ 17, we switched over to +that. So that's the most common thing. And then //base, in that case-- just be +used for stuff that supplements the STL. Things that only Chrome needs-- maybe +they're not usable by all C++ plus everywhere in the world, or we're +polyfilling something that we're hoping is in an upcoming standard. So we can +move faster than maybe the upstream library can because we only need to worry +about us. Some stuff supersedes things in the STL or absl. If we have problems +with particular APIs and we need to work around things or ban things-- like +`bit_cast`-- we have our own version of `bit_cast` that warns you if you're +doing something silly. If we're trying to integrate tightly with something in +the library to do more than what's in the spec, like we have our own version of +span, and that's partly because we want to integrate tighter lifetime checks. + +03:59 SHARON: Yeah, we'll get into all the different types we have in //base in +a bit. So what's absl? Because that's something that's newish to Chrome and +didn't always exist there. So why did we start using it? What is it? Tell us +more. + +04:12 PETER: Yep. absl came out of other teams at Google. Some of the internal +code in Google's-- you could think of it as Google's version of //base, +internally. And they said, hey, this is broadly useful. We'd like to make it +available to the open-source world. And they spent a lot of time, and they +released this thing called absl. And we allowed that-- I don't remember-- I +think around 2016 or something-- 2017? And I drove that process, in part +because there was lots of useful stuff there. I think absl had variant at the +time. And that was in an upcoming STL, and a lot of our code could have used +variant. So I allowed us to use absl after getting all the necessary sign-offs. +And you can think of it as the Google bits of Google's //base that they thought +would be useful to the world. + +05:14 SHARON: OK. Can you tell us a bit about the provenance of //base? Where +did it come from? The name, I guess, is fairly self-explanatory, but a bit +about the history of how we got here. + +05:26 PETER: Yeah, Chrome started development in mid 2006, and we built things +internally in Google. So we used a lot of pieces of the same sorts of places +that absl itself came from, like core utilities in //base. And then once we +split off to our own repository-- and this is even before public launch-- we +couldn't depend on the rest of Google anymore. So we copied a few bits over. We +copied over, for example, a type called `string_piece`, which later became +proposed to the STL and became `string_view`. So we had an equivalent to +`string_view` back then. And we copied a few other bits. And then since then, +it's just been added to ad hoc. + +06:06 SHARON: OK. Who owns //base now? Because everyone uses it, but there +isn't a team that's just a dedicated //base team. So who is out here making +sure that the fundamentals still work? + +06:20 PETER: Yeah, there is no formal core //base team. There's currently an +OWNERS file with 11 different people in it. Those folks all have other +particular areas on the team that they nominally are in. Like, Dana is in the +security side of things. And I work on the UI side of things. And to some +degree, we bring those hats to //base, so we contribute to areas where we have +particular expertise. A lot of it is just self-driven. People tend to gravitate +there when they have an interest in mucking with really low-level C++, doing +core tooling and API fixes. And so it's very much like any other directory in +the codebase. Anyone is free to touch it. And if you want to become an owner of +it, then you can get to touch it lots. + +07:19 SHARON: [LAUGHS] Great. Yeah, as someone who is not a hardcore C++ +person, I am very glad there are people who are and keep things running. So +//base exists in the Chromium source directory. Where can you use it? +Presumably everywhere within that. But can you tell us about where you can and +can't use //base? + +07:41 PETER: Yeah, mostly everywhere, although there are a few gotchas. So +there are particular tiny pieces of //base that can't use the rest of //base +for reasons. There's also a few pieces of our core code that we want to make +sure don't depend on //base, like down deep in the installer or the Sandbox, +like code that is extremely low-level, early stuff that needs to run without +anything else being loaded. Otherwise, in Chrome, you can pretty much freely +use things with a few exceptions. As you mentioned, stuff outside Chrome-- so +most stuff in third party-- can't use //base. Interestingly, that sometimes +includes first-party-ish code. The biggest thing that people get caught out by +is probably Blink. Code in Blink can't just use any part of //base it wants. +There's actually an allow-list that a Python script audits. And the main reason +for that is that Blink uses a memory allocation thing called Oilpan, and it +needs to make sure that all the APIs are safe to use with that. So that's why +we have that. But this also means that things that don't live in the Chrome +repository-- so V8 or Crashpad, our crash core utilities-- don't have //base. +So often, they'll have their own forks or small copies or things like that. + +09:06 SHARON: OK. Yeah. I'm sure if you work in any of those areas, you're +pretty familiar with what you can and can't use. So let's get into the fun part +and do a run-through of what exists in //base. So obviously, we're not going to +cover all of it. It is a huge directory. But there are some types you see more +than others, so let's run through some of them. + +09:23 PETER: Yeah, this is the rare case when I actually think, because the +stuff in //base is so broadly used and so useful, it is worth team members' +time to go through the list themselves and just randomly scan down the files in +each directory and be like, oh, what's that, and look into it. I wouldn't +normally say that. Chrome is like millions of lines of code and I don't even +know how many files. But probably worth doing it at some point in base. But +yes, some things that I found when I was doing this, because I don't have all +this committed to memory-- PartitionAlloc. So if people have heard of +MiraclePtr, or even if not, Chrome has its own allocator called PartitionAlloc. +We use it pretty much everywhere. It's actually kind of its own standalone +project at this point. It's one of those pieces in //base that can't depend on +the rest of //base. But it lives inside //base. And if you have ever seen the +`raw_ptr` or `raw_ref` types-- which, at this point, it's hard to have worked +in Chromium and not seen `raw_ptr` somewhere-- then that lives in there. We +have expected. So this is basically a polyfill of the STL's `std::expected` +from C++ 23. So expected is a type that's mostly used as a return type from +functions. And it basically is two things. It's a value type for when your +function worked, which might be void if your function doesn't need to actually +return a value and you just wanted it to do some stuff, or else it's an error +type if the function failed, and then it can hold details. So you can think of +it like a special-purpose variant of two types, with some helpers and stuff +like that. + +11:07 SHARON: What about optional, which seems related to that? + +11:14 PETER: So optional and expected both are useful for functions that can +succeed or fail. Really, the difference is, semantically, that optional is +usually a value if it succeeds or else no value. So it tells you whether it +succeeded, and it tells you what the value is if so. It doesn't tell you +anything on failure, and it's not very useful for the case where success +doesn't mean returning anything. So if it's a getter-- hey, give me this +thing-- and it fails, then obviously, you return nothing. But if it's not a +getter-- it's like, go paint some stuff on the screen, then the only return +value is, yes, I painted it. So the fact that expected can return void in the +success case makes it useful for that. And then expected can return details on +errors. So that makes it more useful for when you want to pass more information +back or handle failure or something. Not necessary if you actually-- there's no +information needed on failure. Yeah, it didn't work. We don't care why. In that +case, maybe optional is still a good idea. + +12:24 SHARON: OK. Up next we have `flat_map`, `flat_set`. That feels like a +//base classic. It's one of the first things, when I was doing code reviews-- I +was like, oh, you could use a `flat_map` here. + +12:32 PETER: Yeah, so this is another polyfill. `flat_map` and `flat_set` are +actually in C++ 23 also. + +12:43 SHARON: OK. Which version of C++ is Chromium using now? + +12:43 PETER: Chrome is currently on C++ 20. Basically, `flat_map` and +`flat_set` are-- let me-- maps and sets or associative containers, I might call +them, generally store things in a tree-like structure. So that gives them all +their properties of you can find or insert things in logarithmic time, and +removal is cheap and lots of other stuff. The problem with trees is that +because, in memory, they're just pointers to different areas of the heap, this +has really bad performance in terms of the constant factors. Because, +basically, every time you traverse to a new element, you're loading a new cache +line. So `flat_map` and `flat_set` store all their data in a contiguous block. +It's basically a sorted vector. And on the face of it, you'd think this would +be bad because sorted vectors, if you remove an element, you have to shift all +the other elements. And now it's linear time. And that's true. And for very +large maps and sets, map and set are way better than `flat_map` and `flat_set` +because big O is important. But for small ones, like the cache-line effects +dominate and `flat_map`-- is actually much more performant. + +14:01 SHARON: What's a good heuristic for "big" or "small" in this case? It's +one of those, like, this is Google. I forgot how to count that low. + +14:07 PETER: We have a doc with more guidance on how to pick which type you +want. So if you look at base/containers/README.md, there's some information +there about, how big are these things, and how do I know what stuff to put in +it. My general mental thought is most code I run into is kind of two buckets. +Either it's like a dozen things or a thousand things. And those fit pretty well +into-- a dozen is a `flat_map` and a thousand is a map. If you're somewhere in +between-- you have a hundred things-- it's probably more iffy. Do some +benchmarking. But the other thing is performance is not always the concern +everywhere. I mean, performance matters, but if your code is not hot, then +binary size might matter more. Or just semantics or not having to change types +at an API boundary or whatever. + +15:04 SHARON: Yeah, you and your code reviewer can go dig that out. Cool. What +about FilePath? + +15:09 PETER: FilePath. So as people who have worked across operating systems +know, operating systems unfortunately choose to do things differently. Windows +has to use a back slash instead of a forward slash because they like CP/M. And +so we need abstractions to do things parse file paths or construct file paths. +More subtly, the different operating systems also use different encodings for +their file paths. Mac is UTF-8, which seems sane to me. Windows is UTF-16, +which is kind of unfortunate, but historically sane. And then Linux is +actually-- you don't know because Linux is literally whatever encoding was used +by whoever wrote the file. And it could be anything, and you just use UTF-8 and +hope for the best, which is what we do. And it mostly works. I don't know. +There's scary comments in there, and I'm like, I don't want to touch this. + +16:10 SHARON: Yeah. Some of the Linux stuff-- it's like, who owns this? And +it's like-- + +16:15 PETER: And this is another one where there are STL utilities. So in this +case, std::filesystem exists. I think that's C++ 17, off the top of my head. +I'm less familiar because we ban it. And the reason we ban it is that the +Google style guide bans it. We didn't actually make this call. And they banned +it over security concerns, some testing concerns. I don't remember. Titus +Winters, who was deeply involved with the C++ working group, had a lot of +internal commentary on why he didn't think it was appropriate for Google use, +and we didn't think Chrome differed enough from Google to make an exception. + +16:58 SHARON: So a brief sidebar about style guides-- so there's a Google-wide +style guide. And then there's a Chromium-specific style guide. So where do +these things differ? + +17:04 PETER: It's interesting. I actually just wrote a formal policy on that we +have approved, but not yet written down in the tree. And basically, the formal +answer is we differ whenever the consensus of the cxx mailing list says we +differ. And cxx, by the way, is a mailing list that anybody in Chrome is +welcome to join. It's a moderated list for non-members, primarily just to +reduce spam. We're not actually trying to keep anybody out. There's no +expertise bar to join it. You don't have to take a test and prove your C++ Foo. +But that's where things get discussed, like, should we allow x, y, and z? For +Googlers, you might be familiar with C-style internally. This is very much the +Chromium version of the C-style mailing list. And mostly, our rule is, yeah, we +do what Google style does, except in cases where there's a good Chrome reason +to differ. And there are sometimes. Google doesn't ship to client machines, so +it doesn't care about the size of updates or how much space it takes on the +hard disk. So they don't care about binary size. The way that we do, especially +on mobile platforms. They have different kinds of security concerns. They're +concerned about the sorts of security holes that you could get on a server-side +app. We're concerned about the sorts of security holes you could get from an +attacker on the web. Those overlap. They're not quite the same. So there's a +lot of subtleties why we might make a different call, but it means that in +general, Chrome style is Google style with this set of changes to it that we +document in our style guide. + +18:43 SHARON: All right. + +18:43 PETER: And yeah, get on the mailing list if you would like to kibitz and +tell us that we're all idiots. + +18:50 SHARON: [LAUGHS] I'm sure, yeah, everyone would love that. Cool. The next +on our list is NoDestructor. + +18:56 PETER: So, yes, this is one of the bazillion "I have a Singleton-ish sort +of thing" types. So we have lazy instance. We have Singleton. We have +NoDestructor. The rule is basically ignore all the others and use NoDestructor. +The other things are old. They're deprecated. Don't use them. NoDestructor +pretty much just tells the compiler, leak this, at program shutdown. Doesn't +try to run it. And that has two nice effects. One is it prevents what you might +call the destruction order fiasco, which is you're in the middle of tearing +things down, and you don't know whether you can rely on other things that are +also getting torn down. So who puts their gun down first, kind of thing. If +nobody has to be destroyed, then you just don't care. And then the other thing +that's nice is it just does less work on shutdown because really, at shutdown, +ideally, what we want to do is two things-- write all the important data to +disk. And then just kill the process. Like, die as fast as possible. Don't do +anything else. Once all your data is written, you don't care. Tell the OS to +wipe you off the system. So NoDestructor sort of helps get closer to that +world. + +20:10 SHARON: On a similar note, RAII Scope? Scoper? + +20:10 PETER: Right. Yeah, anything for scoping stuff. So RAII, for people who +haven't encountered that acronym, stands for Resource-- excuse me. Resource +Allocation Is Initialization. And it really just means using C++'s objects and +lifetimes in order to get stuff guaranteed to happen when something goes out of +scope. So converting some manual calls of, do this on Enter; do this on Exit, +into the lifetime of an object that handles those for you. And so we have +things like AutoLock, which takes and releases a lock for you. Anything with +"scoped" in the name-- Scoped Observation adds and removes an observer for you. +AutoReset is actually trivia, the first thing I ever contributed to //base, at +least that I recall. So I looked it up the other day. It dates back to 2009. It +was called Scoped Bool at that point because it only did bools, and now it does +everything. I was scared of templates in 2009 and did not know how to use them, +so I only made it work with bools because templates. I know more about +templates now, and my feeling then was totally justified. It was completely +correct. But yes. So pretty much, anything you find in //base that has Auto or +Scoped at the front of the name is some kind of scoping helper. + +21:39 SHARON: OK. Yeah, I don't see AutoReset used a ton. Can you briefly +mention how that works? + +21:45 PETER: So AutoReset is just, set a value. And when I go out of scope, +reset back to the old one. So you can do this, for example, if you want-- if +your class has a member that is-- hey, I am inside the blah, blah, blah +function right now, and therefore, you should allow or not allow this +functionality to happen. A few classes have to have-- it's kind of hacky-- but +something like that in their design. So you can use an AutoReset around setting +that member within the particular scope that it's supposed to be set in. You +can use AutoResets in tests, too. Say, OK, within this scope, make this +variable be this. And then just automatically, when it goes out of scope, you +put it back. + +22:28 SHARON: Yeah, that's a handy one. Cool. In terms of template stuff maybe, +span? + +22:37 PETER: Yeah, so span is-- people might see span used a lot more lately +because the security folks have been driving a process called spanification. +And their goal with this is pretty much to get rid of all pointer arithmetic in +the entire project. So span is something we call a view type. And I would +actually to write a talk on view types at some point because I think they're +less understood than they should be because they're very useful. But basically, +a view type is some kind of a window into a block of contiguous objects that it +doesn't own. It just says, here's some memory. Some things live there. You can +think of this like a pointer because since arrays decay to pointers-- for +example, an array that you have decayed to a pointer literally is a view onto +that memory. A span is very similar to that, except that it also carries a size +along with it, so it remembers how big it was. So in that sense, similar to +std::array. But unlike stdarray, it's not managing the memory directly. It's +just saying, no, the memory lives out somewhere else. So maybe it's in a +vector. Maybe it's in an array. Maybe it's somewhere in the binary or it's on +the heap. We don't actually care. Here's the pointer to it. And here's the +size. And that's good because the fact that you have a size means all your APIs +can bound check. So you can either handle things nicely, or you can crash the +process on security holes or something. And you can also-- wow, my brain just +completely died. Oh, yes, You can split spans or do other helpful things with +them. Grab the first [INAUDIBLE] elements of the span, convert to other span +types, things like that. So we provide those APIs. And that's what having a +dedicated type for that gives you. + +24:42 SHARON: This is maybe a bit of a silly question, but how do you write +stuff in that memory, then, if-- + +24:47 PETER: So, since the span is just a window, it can either be a window of +writeable or non-writable types. So if you have a span of int, it just means +here's some ints, and you can read and write them. If you have a span of const +int, that means that it's read only. + +25:00 SHARON: OK, cool. So what about strong alias ID or type? There's a lot of +types and whatnot. So-- + +25:13 PETER: Yeah, I don't see these used a ton, but they're occasionally +useful. Basically, strong alias and ID type, which is a special case of strong +alias, is a generalization of the idea of an enum class. So it's, I have this +type that is implemented in terms of this underlying type. But semantically, +there are different things, and you shouldn't conflate them. So just like you +might have an enum class that it's really an int and its values are ints but +don't just pass it to a function that takes ints. You want the compiler to yell +at you because that's a different meaning. Strong alias is a way of basically +saying, hey, I have an anything. You can do this for even non-numeric types. +You can just say, it's really a this thing over here in the implementation, but +the semantics are different. + +26:10 SHARON: Right. I think you see it a lot for ID types of different +classes, so you don't mix up what you're identifying with those numbers. + +26:17 PETER: Right. Yeah, I mean, any kind of identifier runs this risk of type +confusion. And if you can just get away with using an enum class directly, +that's probably fine, too. But sometimes you can't. + +26:30 SHARON: OK. All right. Next on our list is synchronization types, lock or +available event, some examples of that? + +26:37 PETER: So there's a whole //base synchronization folder. And there's lots +of things in it. Lock is probably the most commonly used thing. This is a mutex +if you need to do thread-safe stuff in Chrome. And actually a word about thread +safety in Chrome, because people have asked this before-- if the code in Chrome +doesn't state otherwise, it is not thread-safe. It's assumed to all be running +on a single thread. It's probably assumed to be running on the primary thread. +The thing we call in the browser process the UI thread. Obviously, in some +directories that doesn't hold, and the whole directory will say something. But +normally, thread-safe code-- code used across threads is the exception, not the +norm. But where it is used-- lock is our mutex type. It's very much like +std::mutex. In the case of //base synchronization APIs, it's really just a case +of, we had all our own stuff and used it all well in advance of C++ 11 adding +it all to the STL. And then we could migrate. It's easy to migrate. And I said +earlier we like to use stuff from the STL when we can. So I should put the +caveats on that. It's easy to do that when either of the following is true. One +is the STL provides the exact same semantics. And the cost is there's no reason +not to do it. And two is there's a huge win. We get way better perf or better +integration with something in the STL, or better safety or something like that. +And in the case of the STL synchronization stuff, migration is scary because +any difference or bug in the implementation of the STL that we use for those is +going to be very bad and hard to track because it's all really low-level, +cryptic cross-thread stuff. And then we don't actually know if there is any +gain. There's an open bug on performance testing some of these things against +each other. But because migration is so scary, no one has bothered. Free +opportunity. If you want, go perf test it. Find a big difference. And then +migrate. And that's probably cool. Of course, if you perf test it and don't +find any difference, then you just wasted your time. Ha-ha. + +29:06 SHARON: [LAUGHS] RE the thread type-- so the most common ones you see, at +least in the browser process, are the I/O and UI threads. So can you give us a +quick rundown of what these two are and how they're different? + +29:17 PETER: Yeah, so in the browser process, there's a whole bunch of +different threads, but the two you mentioned are the two most common ones. The +UI thread is the main thread of the process, and we call it the UI thread, in +part because all of the actual UI interaction-- event handling, painting, et +cetera-- is done on that thread. We don't do most of that off-process. We do do +compositing off thread and things like that. But if you write code in views and +it goes and paints pixels, then that code will be running on the UI thread. The +I/O thread is more confusingly named because at first glance, a lot of +engineers assume that means that's the thread where we do reads and writes to +disk. And it actually doesn't mean that. It's interaction between the different +threads of the browser. So the I/O thread is more like the coordinator thread, +where it's responsible for communicating between browser and renderer +processes, or between the network stack and different things. So the I/O thread +actually never does-- it's not supposed to touch disk at all because disk +blocks, which is why you do it off thread to begin with. And the I/O thread, +since it's coordinating all of the other things, needs to be very responsive. +So in fact, there's other places-- there used to be something called the file +thread. I can't remember if it still exists. We now have the thread pool that +you can use to do some of these longer running blocking tasks. And then they'll +probably communicate their results back directly, but if not, they'll use the +I/O thread to coordinate that. + +30:59 SHARON: OK. All right. Back to our //base walkthrough. So up next is +Time. + +31:04 PETER: Yeah, so another bit of trivia here. Before I was a //base owner, +which-- I've only been a //base owner since March of this year, 2024-- but I +have been a //base time owner for many years. So there are more than 11 //base +OWNERS if you start counting the OWNERS of various subdirectories. And time has +lots of things in it. But the three core classes, which are all in time.h, are +Time, TimeTicks and TimeDelta. TimeDelta is easy because TimeDelta is just the +difference between two Times or two TimeTicks. It's constexpr, and it's +type-safe. And this means that if you want to store a value 100 milliseconds, +even at compile time, even as a constant at the top of your .cc file, do not +do, int blah, blah underscore MS equals 100. That's not type-safe. So it's very +easy to accidentally add that to some value with different units. So yes, +TimeDelta is type-safe. So if you say, auto, blah, blah equals base +milliseconds 100, then that's 100 milliseconds. And it not only says what it is +in the code, but you can't add it to the wrong units. The compiler will check +you. And it doesn't-- It's not expensive. It's compiled into the binary. So +that's great. Time versus TimeTicks is more subtle. These represent two +slightly different versions of What Time is It? So Time is like a wall clock, +human readable time. So a time like 3:57 PM, this time zone, on this date-- +that's a time. And human readable makes it really good for messages to humans +or saying, this happened at this point, but really bad for doing calculations +with because human times are messy. They skip forward in daylight savings +things. They also skip backwards in daylight savings things. There's leap +seconds. There's all sorts of complexities. So trying to find out the +difference between two times is not as easy as it might seem. So then for that, +we have TimeTicks, which is based on a monotonically increasing counter that +runs while the process is running. And that's much more useful for saying, OK, +15 seconds from now, I want this to happen, or something like that. There are +still even gotchas with that because what does that clock do if the user puts +their machine to sleep? Does it keep running or not? And so there's a lot of +commentary in the code about what you do and when, et cetera. But +fundamentally, that's the difference between those. + +33:44 SHARON: So in the stuff I've looked at, I haven't seen too much use of +Time. Where is heavy usage of all this Time stuff? + +33:57 PETER: There is stuff in a number of different places. So for example, +the media code needs heavy usage of Time because it needs to know when to +schedule things. The network code might use Time for computing rates. So if +it's like OK, I'm going to bandwidth-limit this, or I need to know how fast the +user is downloading. A lot of UI code needs to use time to display various +things to users. For example, the scheduled and update-- critical update for +Chrome. You need to restart your machine within 30 minutes type of thing-- +needs to use Times. And then we use Times a lot when time stamping things that +come in from sync or that we save to disk. Cases like that, we often need to +know, is this sufficiently out of date? Do we need to go get a new one? + +34:42 SHARON: OK, sounds good. All right. Next on our exploration is value in +//base. + +34:55 PETER: Yeah, //base value-- I think subject to the longest running +code-health migration thing. We had a code-health thing going for //base value +for. I don't even know how many years to migrate APIs. And actually, +ironically, I have almost never used //base value. So I always thought like, +why are we spending so much time doing this? But //base value is basically a +C++ class that abstracts, what kinds of values can you store in JSON? And JSON +matters because JSON is how we store all of our preferences. It's also a good +abstraction for values that come to and from JavaScript. But all that is +handled differently. Like, V8 and Blink worry more about that. And usually, by +the time you get to stuff in //base, they have dealt with those sorts of things +already. So mostly, where you'll see value is when you're going to and from the +pref store. Preferences are the backing abstraction, also, for sync. So +anything that's synced-- you'll probably go through //base value. That gives it +some things that, to a C++-only programmer, would be odd. For example, that you +can store a double in it, but you can't store an `int64_t`. And that makes +sense if you think in terms of JSON doesn't have the concept of a 64-bit int. +So that's why //base value models that. But this also means that value is not a +good type to use for a member of your class or a general-purpose thing to pass +around in APIs. Most of the time, you have one specific type. Use that type. If +you have multiple things, use a variant. You only really want to use value at +the boundary level of-- you're serializing to or from some kind of storage that +uses values, and then after that, you put it in its own dedicated type. + +36:45 SHARON: OK, cool. All right. Next up, we have numerics. Numbers-- we like +those. + +36:52 PETER: I got my shirt in here. So yes, the numerics library has a number +of useful things. It has some mathematical constants. Actually, C++ now has a +lot of these. We used to have our own constant for pi and square root of 2 and +things like that that you would need to use a lot. And now C++ 20 has those, +and we use those more widely. But we still have others. And we have some basic +conversion functions like, if you're converting between degrees and radians +don't write the code yourself. Just use our code. Not only is calling a +function more readable than doing the math inline, but it prevents you +accidentally going the wrong way or something like that. More interestingly, we +also have a bunch of safe math libraries, so we have math operators and types +that will either clamp out-of-range calculations and values, or they will, in +fact, check fail and crash your process when bad stuff happens. And these are +not only useful in the cases you would expect like, oh, I have some data coming +over the network. I should probably check whether the size they want is sane-- +that sort of thing. But also cases you might not expect, casts to smaller size +within the code. If you're going to use a static cast, the coder should be able +to tell locally that that's provably safe, like you literally just checked that +the size is less than such and such. So of course, it has to fit. Yeah, in that +case, just use a `static_cast`. But if it's coming into some function and +you're not guaranteed, don't make people go read it and find out that 13 other +functions later, the transitive closure of x proves that this can be done. Use +a `checked_cast`. And then even more surprisingly, conversions between integer +and floating point types-- neither one can accurately represent the other. So +you should use the safe math functions. You should not be doing things like +calling `std::round` and then just casting to an int. That doesn't work. + +38:55 SHARON: Right. + +38:55 PETER: I have a screed on this that I wrote at one point. + +39:00 SHARON: OK. Don't roll your own math. All right. What about other ranges +we have in //base? We talked about a couple earlier, I think, but are there +more? + +39:06 PETER: Well, we talked about some-- we talked about span as a view type. +I don't know that we talked about anything with ranges. So //base ranges was a +backport of the range-based algorithms in std::ranges, in C++ 20. So these are +basically-- everyone hated the old algorithms because they're so cumbersome. +You always have to pass, my long vector name dot begin, my long vector name dot +end. And this was even more annoying if you actually had to go to the trouble +of stuffing something into a temporary just so that you could do that because +it was coming from some other function. So the range algorithms provide this +surprisingly nice piece of sugar by just letting you take a range-like object +directly. And there's lots of complexities to, what is a range-like object? So +we had back ported that. They give you a few other nice things. They have +projections on all the algorithms, which lets you do some cool stuff without +having to manually unwrap stuff. But basically, that's what //base ranges was. + +40:16 SHARON: OK, cool. Up next is something we mentioned a bit earlier, but +general string stuff. So you mentioned `string_view` and whatnot. So are there +other string things in //base to know about? + +40:27 PETER: There's conversions between various types of encodings. In +particular, Windows APIs are basically UTF-16. Mac APIs are basically UTF-8. +JavaScript is pretty much UTF-16. POSIX is pretty much UTF-8. There's lots of +disagreement, and therefore, we end up doing this. So //base makes it easy to +do this, although even better than doing an easy conversion is not doing the +conversion. If you can write your APIs or storage such that you actually don't +need to convert, that's better. I have fixed up code where once I trace through +the 10 call chains, I discovered that, actually, we wanted the same type at the +beginning and the end. We just converted back and forth about four times along +the way. So fix that. Don't do that. There's utilities to split and tokenize +strings. Probably not good to write your own HTML parser in this. That's why we +have Blink. But if you're just doing some super trivial thing, we've got that. +And then some of the bigger ones-- we have StringPrintF, which is basically C's +sprintf(), where you want to do a formatted output, but into a string buffer +instead of onto the screen. So we have something like that in StringPrintF, +except that it returns a std::string. It's harder to misuse. It checks a lot of +your format stuff at compile time. I also find it overused. We have cases where +we StringPrintF with a format string that has no substitutions in it at all, +which-- kind of strange. We have things that just concatenate strings, which +could be StrCat. And then in general, lots of stuff is cryptic. And then I just +mentioned StrCat. StrCat is basically a special-purpose function for doing +string concatenation really quickly. You should not just blindly use StrCat for +all concatenation. If you have two strings and you do string plus string, +that's the shortest, most readable, and it turns out, most performant way of +doing it. You should just do that. People have this idea of, oh yeah, string +plus is terribly slow. Don't ever use that. Actually, it's great if you're only +doing it once. If you're doing it over and over and over, it's terrible, but +not because the implementation of plus is bad. It's because that basically is N +squared. You have to potentially resize the string bigger and bigger and +bigger. So StrCat lets you take a whole list of things to concatenate, and it +does it all at once, which means it's linear time to do that. And StrCat also +works very nicely with `string_view`s, so you can mix strings, `string_views`, +C-style strings, et cetera, which is not possible with things like plus. So +very, very useful function. Underused in my opinion. + +43:14 SHARON: OK, go check it out. So you listened to a bunch of all these +different platforms that Chrome runs on and a lot of stuff that lives-- because +of things like //base, you don't have to really worry about what platform +Chrome is running on when you're working on things. For example, in content, we +don't really have to worry about this. There's some Android-specific stuff, but +that's not because of actual-- that's for other reasons. So how much of the +magic that goes into making Chrome run across these different platforms lives +in //base, versus somewhere else? + +43:54 PETER: Certainly more of it. So things like FilePath have to understand, +fairly directly, the differences between operating systems. We also have a file +in //base called `compiler_specific.h`, which is a very low-level-- here's a +bunch of macros. And they differ by platform or by compiler. So C++ 20 gained a +new attribute called-- shoot. Is it `no_tail_padding`? No. It's-- oh, +`no_unique_address`. Yes, C++ 20 gained something called `no_unique_address`. +The only reason I mention it is because on Windows, Clang does something +different to match Microsoft. And so `compiler_specific` abstracts that detail +away and says if you use our macro, then you get the same behavior everywhere. +Things like that. That said, there are still plenty of cases where code outside +//base needs to understand this. Since I work in UI, the examples that come to +mind are in UI, like being a good platform citizen on the different OSes often +means doing different things in terms of what keys do stuff, or how do you +handle different events, or where should the buttons on the OS surfaces be, or +things like that. How do fonts get rendered? Those are all things that wouldn't +be handled in //base because they're higher-level concerns. They're stuff +happening up in a UI layer somewhere. And probably, code in other directories +has to do that kind of thing as well. But certainly, //base will take care of +anything that you might think of as like a Unix versus Windows or POSIX versus +Windows or Mac or something like that. Android versus iOS. API-level +difference. That kind of thing will often be handled more at the //base level. + +45:46 SHARON: OK, cool. So re macros-- a bunch of macros live in //base. We +previously had another Peter on to talk about DCHECK(). Do we have some updates +there, if you want to give us a rundown? + +46:00 PETER: So I think your episode with pbos (Peter) was filmed in 2022. + +46:09 SHARON: It was a while ago. + +46:09 PETER: And then since then, he's been continuing to do hard work. So +anything I mention here is pretty much credit to pbos. But we've changed our +guidance on some of these things. So the guidance used to be, basically-- we +have DCHECK() and CHECK(), and they both kind of mean, this shouldn't happen. +Well, this should be true, and crash if it isn't. And the guidance used to +pretty much be, use DCHECK() for everything. Except, use CHECK() for things +that are security sensitive. And now the guidance is effectively reversed. It's +basically, use CHECK() for everything. Only use DCHECK() if this is provably +performance-disastrous here. And the big reason for that is we're finding +increasingly that a lot of our crashes and security problems in the field come +from violating the code's invariants. So we get to somewhere in the code, and +the invariant that even had a DCHECK() that said, this shouldn't be true here-- +it was violated. So something got goofed up somewhere. And by converting all of +these things into CHECK()s more, initially, it risks making the product more +crashy. But assuming that we do it in a slow enough way and we fix things +quickly as they come up, we eventually get to a state where we're actually +enforcing our invariants and not just saying, well, we're pretty sure this is +true in production, but we don't want to take the perf hit to do it. + +47:35 SHARON: Yeah, and avoiding those weird states is important because that's +what attackers look for, of, once we're in this weird state, all bets are off +kind of thing, and we can just do whatever. So-- + +47:43 PETER: Yes. + +47:43 SHARON: --eliminating those. + +47:48 PETER: C++ 26 has something in this space called Contracts that they're +working on, where you can annotate a function to basically say, these are the +preconditions and postconditions and things. I don't know how that will turn +out, and I don't know whether we'll want that at the time, it ships. The other +thing to mention here is that-- I said that rolling this out can sometimes be +hard. When we say, oh, our guidance is, use CHECK() unless it's +perf-disastrous, a lot of the questions that I get are things like, well, what +if I'm pretty sure this is true, but I mean, I don't know for certain? And I +don't want to ship this thing and crash everyone in the wild. Shouldn't I use a +DCHECK() if that's the case? And we have a couple tools for dealing with that. +And one of them is the feature flag. Pretty much everything should be developed +under a feature flag, unless it really, truly doesn't make sense to do so. And +that's a way that you can say, oh, hey, we noticed everybody under this feature +is crashing. Turn it off. But the other thing is that pbos added something +called NotFatalUntil, which is a way of saying, hey, I'm putting this in. I'm +explicitly going to make it fatal in the future. But for right now, I just want +to collect crash stacks and data on it and not actually crash in the field. And +that's a good tool that people can use to implement things in a cautious way. + +49:11 SHARON: Is that the same as DumpWithoutCrashing()? + +49:11 PETER: So NotFatalUntil, I believe, uses DumpWithoutCrashing() to +implement things. DumpWithoutCrashing() is a way of explicitly just saying. I +don't want this to be fatal. I just want to collect stuff. NotFatalUntil is a +way of marking a CHECK(), as having that behavior, and it will automatically +switch over at a certain milestone. So you say, NotFatalUntil M-136, and then +when M-136 rolls around, bang, that becomes fatal. And everybody-- + +49:40 SHARON: Everyone's crashing. Yeah. + +49:40 PETER: Hopefully not, because hopefully you caught and fixed all the +problems with it before then. But yes. + +49:46 SHARON: So in terms of other macros we see a lot, maybe more as a-- not +something you hopefully see as much in production, but in tests and general +debugging is logging and various logging-adjacent macros. Can you tell us a bit +about those? + +50:00 PETER: Yes. If you're on a team where you know, I have to be able to +debug only from the log output. There's no other way. And I know how to collect +it, and I know what I'm going to do with it, and I'm going to clean it up +eventually when we fix the problem. Then if all those are true, go for it and +log. But otherwise, no. + +50:16 SHARON: If you do want to collect data of, say, certain values, of +certain variables out in the field that you can't get locally, we have better +ways to do that. + +50:27 PETER: Yeah. There's debugging utilities. So you mentioned +DumpWithoutCrashing(), and that's a way to send back a lot of data to us as if +there was a crash. And then when you do that, you can use something called +debug Alias(), where you can force a particular variable's value to get +captured by the crash data, because normally, the crash data will include +things like, well, these variables were on the stack. But in a release build, a +lot of things are optimized away, so you can't guarantee something like that. +So aliasing a variable using that particular //base utility is one way to make +sure that-- we want to capture this, this, this, in this dump, for sure. Do it. + +51:05 SHARON: Is that the thing we also call "crash keys?" + +51:11 PETER: I think it uses crash keys to implement it. I haven't looked at +this very recently. So-- + +51:17 SHARON: OK, I think we'll end our meander through //base at that point. +But there are many types we didn't cover. So how do people find those? Should +they try to remember everything we just mentioned? + +51:28 PETER: Yes, this is the, Mr. Johnson, may I be excused; my brain is full, +moment. So there's no way to remember all the different things in //base, as +far as I can tell. I'm an owner, and I constantly find things that I'm like, +oh, I didn't know we had this. Oh, that would have been useful. So I try to +tell people, like, yeah, you can get better with some of this stuff. The +biggest way to do this is just practice. Practice is much better than just raw, +focused effort on polishing a single thing. Just write more CLs. Look more at +the //base APIs. Use more things. Put them in practice. Send a bunch of stuff. +If it's not perfect, I don't care. If it's a monotonic improvement over what +we've got, I'll stamp that immediately and say, sure, let's move forward, and +just do it more. But in the limit, nobody can remember all this stuff. And I +feel a little bit bad that-- I think the message that a lot of people get in +code review is like, what the heck are you doing? Why didn't you use a base +blah blah, that you've never heard of? And you're like, I'm sorry that I am +not a genius like you. So actually, I feel overwhelmed and incompetent, et +cetera, a lot of the time, too. And it's because this is a hard problem and +it's a big space. //base is huge. Chrome is huge. + +52:54 SHARON: Monotonically increasingly huge. + +52:54 PETER: C++ is huge. The web is huge. Nobody-- no human being is capable +of being an expert in any of these areas. It's too big. And therefore, like if +we can have compassion for each other, that's good. I hope, increasingly, we +give people more encouragement and opportunity to succeed and not just, hey, +avoid failure harder, because that's just a route to everybody getting burned +out and miserable. + +53:20 SHARON: Yeah, I mean, Chrome is fun because a lot of people have been +around for a while, so they have a better grasp of things, I guess, because it +used to be simpler. So it's easier to patch in those incremental changes, +whereas when you come in now, it's so much stuff. It's like, oh, my God. What? +What's happening. And it gets harder and harder to start. And there's not that +many new people at Chrome, relatively, so we kind of don't have that constant +reminder of, oh, this is hard, and this is what people find hard now. So-- + +53:55 PETER: Yes, anybody coming into Chrome-- it's enormous and overwhelming. +And I mean, it overwhelms me, and I've been here since the inception. So it's +very much true. + +54:06 SHARON: I think everyone is overwhelmed, no matter how long they've been +here. It's just what they are overwhelmed by changes as you go. + +54:18 PETER: I have proposed in the past doing more formal training, not just +classes or talks or something, but direct one on one-- here, you watch me step +through this kind of problem and do this. And I'll watch you, and let's give +each other feedback. And very much more apprenticeship model than just like +lecturer model stuff in Chrome. I think that would be good. I think readability +reviews would be useful. I used to be a C++ readability reviewer at Google. And +all of those things-- it's been difficult to get organizational traction to +actually go do those. So shameless plug-- if people think that would be useful +for them and you want to do that with me or somebody else, let me know, and I +will try to make it happen. And if you think that's a terrible idea, don't let +me know. I don't need more discouragement right now. + +55:15 SHARON: Yeah. I mean, everyone in Chrome is incredibly helpful and +friendly. There's people who you think, oh, they must be so busy. But they're +always so willing to help and talk and whatever. + +55:21 PETER: And I think one of the keys to making that happen is finding the +right people for questions and then not turning it into no good deed goes +unpunished. So I've been guilty of this, where someone was helpful to me, and +then I immediately just rammed 500 followups down their throat. And they, +internally, were like, I think I will not be helpful in the future. That might +work better. So I try to tell people, hey, as much as possible, instead of +sending chats or emails to one specific person-- + +56:00 SHARON: Post them to the mailing list. + +56:00 PETER: --post them on a mailing lists. Put them in chat threads. Come +hang out on Slack. There's this weird dichotomy of teams in Chrome that use +Slack, and teams in Chrome that do not use Slack at all, no. I don't really +care, in terms of what your team wants to do. Neither one is wrong. But in +terms of, can everybody else see it and make use of it, if you come over to +Slack, then, yes, that can happen. If you're in your team's chat room, probably +not. So that's my, you should all be in Slack if you would like to have your +conversations visible and possibly helped by other people on the team. + +56:40 SHARON: In Slack, it's much easier to search and use and the other stuff +we use. So if you want to be able to find an answer to something you asked a +long time ago, it's going to be much easier in Slack than-- + +56:51 PETER: Hey, now, you sound dangerously like someone who does not believe +that all Google products are the best for all situations. + +57:00 SHARON: OK, so something you have mentioned a few times, and we've +touched on is updating C++ versions. So you are quite well-acquainted with +that. So what goes into going from, say, C++ 17 to 20? + +57:07 PETER: Yeah, I helped with 14 and 17, and then I pretty much drove 20. So +just so people understand what it means when we say, well, what version of C++, +pretty much, at any given time, Chrome has some version of C++ that it says it +formally supports. So we say, right now, that we formally support C++ 20. And +what that means in practice is really that we pass, like, dash std equals C++ +20 to the compiler and the linker when we build stuff. So this really means +that's the version that's in our build files that we tell the compiler to use. +And then it will complain about stuff outside that. That means you can also use +earlier stuff. Whether you can use later stuff is a matter of whether the +compiler will let you. C++ 20 introduced designated initializers. But in fact, +you could use them before then, in part because compilers would allow that. And +Google style guide, said, yes, and we're OK with that. So it's a function of +that stuff. So we have that. And we have a guide called c++-features.md that +says, here's all the stuff in the different language versions that you are, are +not allowed to use. And when we decide that we want to go to a new version, +like say, if we want to go to C++ 23 right now, the first thing that somebody +will look at-- there's no formal owner of this. There's no timetable that says, +thou shalt pull this new version into Chrome at this time, and this is the team +that will do it. It's very much like, no one owns it, and it will happen if it +happens. And the reason that I did 20 is because I wanted 20. It actually was +even weirder than that. I wanted us to be on 20 in hopes that I could use MSVC +to test something specific at low level in //base. And then that snowballed +because I was just like, well, I'll just roll the C++ version. How hard could +it be? A year later, we rolled. So-- + +59:15 SHARON: That's not bad. That's pretty good. + +59:15 PETER: So what I might try doing first is simply see, does the toolchain +support it? So that means go change the version we passed to the compiler and +see if it compiles. Some stuff gets deprecated in new versions. And you maybe +have to go fix that. And then the next thing is, well, do we want to allow it? +Like, it does no good to say you can use C++ 23 if all of the features aren't +implemented yet, and they're broken, and you don't know that they're broken +because the compiler says that they work, but they don't. This was the case for +a number of things in the past, where people would be like, yeah, you can +theoretically use this. Don't use it. It doesn't work. So once those things are +true, and we know the toolchain pretty much supports most of what we want to +use, not all of-- the rule is, not all of, because toolchains have lagging +things. Actually, Clang slash libc++ just finished C++ 17 within the last +couple of months. They finished the last bits in order to mark it as fully +complete on their thing. Obviously, C++ has been out for a long time, and we've +been using it for a long time, and so has everybody else. So we don't wait for +100% to be complete. We wait for enough. And then we pull in the new thing, fix +everything, and write a bunch of updates to the features doc that says, you can +and can't use these parts. And normally, we're somewhat conservative. We don't +ban everything. We ban anything that doesn't work. But we might also ban some +other stuff just because, yeah, we probably want this, but let's get ourselves +onto the new version before we have to think about, what's the migration plan +to this feature? So let's just, for now, block it. And we'll think of that +after the fact. And that's what happened with C++ 20. We blocked a lot of stuff +initially. And then in the months following that update, we've allowed more +things. The std ranges stuff earlier was an example. I had said, that's what +//base ranges was. And then if anybody out there was asking, well wait a +minute, what do you mean, was? It's still there. I see it in the tree. What do +you mean? It's because std ranges is actually, now, the approved way of going, +and the old thing is deprecated. We will get rid of it at some point. There is +a migration plan. But that's the path that all of the new things follow. So +there's a whole list of stuff in C++ 20 that we want to allow. And I push +aggressively to move forward if we can. And it happens when it happens. + +61:59 SHARON: OK. So if someone wants to be the person who updates to C++ 23 or +generally get more involved in //base kind of things, become another //base +owner, what kind of things can you start out by doing now to go into that +direction? + +62:14 PETER: Yeah, it's funny because in the Notes doc that I wrote myself +where this talk-- all of my notes to myself are at least very negative like, +don't do this; don't do this. And I'm thinking, wow, how discouraging. +Actually, the best answer is, go do it. A lot of these things-- like I said, +with C++ 20, it happened because I was just like, let's do this. How hard can +it be? Many bad events have started with those words. But with a lot of things. +If you want to do stuff in //base or if you want to make major C++ changes in +Chrome, just do it. We have this cultural principle now, think like an owner. +Part of what that means is it actually is OK for you to go make major changes. +You do not need to go get permission. Now, you may want to talk to some +knowledgeable people so that you don't do something silly like, oh, well, I +just spent four months making this possible, only to find that you had blocked +it because-- other reason that I was unaware of or something. But that's very +different than, hey, I don't know whether I'm allowed to do anything. Can I +touch //base? No this is the special, sacred area. It's just like every other +directory. Send CLs to an owner, and we'll do stuff. In fact, for a lot of +people, that's enough. The C++ 20 upgrade I did I was not a //base owner for, +and I still updated the whole C++ version that Chrome uses. And I reviewed all +//base ranges. I did a whole bunch of LSCs to change our string types. All of +that stuff happened without being a //base owner. You probably don't need to be +in an OWNERS file to have special credentials or anything to do a lot of this +stuff. If you're interested and you like doing it, then go do it. If you +actually want to become a //base owner or something like that, then, yeah, like +probably, eventually, you'll need to have some decent C++ skill. I don't like +to use words like, expert, partly because no one knows what they mean, but also +partly because I think the people who are the biggest experts in some area +understand how much they don't know. And therefore, there's this sort of +Dunning-Kruger like-- the people who are not experts are like, I'm an expert. +And then the experts are like, I'm not an expert. So that happens. But know +some stuff. Don't be totally terrified by templates. Maybe feel pretty +comfortable with most of the stuff I covered in [C++ 201]. Some breadth of +experience. If you can have a rough idea that, yeah, this would probably be +useful several places in Chrome; I think we probably do this multiple places, +then that's a useful instinct to be able to have. And you're comfortable +tackling API design-level questions. So not just the implementation, but also, +what direction do we want to move the code //base in? And is this a safe +concept, that people should think at this level? Those kinds of considerations +are things that you want to be comfortable thinking about. And then finally, +fundamentally, you have to have somebody who's established trust over time. +//base OWNERS get an automatic OWNERS override capability. This is not intended +to be like a carrot of, go become a //base owner so you can have this. It's +mostly just like a, hey, if you're touching this, you probably need to make a +lot of changes that touch a lot of directories across the code to fix API usage +or something. So just for convenience sake, we're going to give these people +OWNERS override permission. There's still a request that you not use it on your +own CLs. I'm not supposed to 00 plus 1 my own change after somebody reviews it, +that sort of thing. And because of that level of power, we want to be sure that +people who we're giving this to are people who can be trusted to use that in a +responsible way. So somebody who's only been known to the project for three +weeks-- even if they're the world-- Herb Sutter comes in and is like, hey, I am +the head of the C++ committee. I know lots. Can I be a //base owner? It's like, +probably no, not right away, not because we don't think you're good. It's just +because you don't know Chrome yet, and we don't know you. And so a lot of +building relationship over time. + +66:48 SHARON: Sounds good. Yeah, so if you want to get more into it, cxx is +both a mailing list open to the public, as well as a Slack channel. Lots of +other Slack channels, other related ones, too. + +67:06 PETER: Yeah, there is a //base Slack channel. + +67:06 SHARON: A //base Slack channel. + +67:06 PETER: And it's funny, because many of us are in many Slack channels and +will have conversations simultaneously across several of them, which is a +little odd. But yes, most of our day-to-day work gets discussed on Slack. So +things like, how do I use this space API? Or do you think these things should +change this way? That sort of question is very Slack-level. And then more +formal like, shall this feature be allowed or banned? That's a mailing +list-type question. So people are welcome to come participate in both of those +as they desire to do so. + +67:43 SHARON: OK. Well, thank you so much. This was very fun to see-- get a +glimpse into everything that is in phase, and hopefully, people will find it +interesting and want to get more involved. + +67:56 PETER: All right. + +67:56 SHARON: All right. Thank you very much. You could do both. This is a +fairly low-budget production, as you can tell by the pens holding up-- + +68:02 PETER: And the fact that you-- where is second-- oh, they're-- + +[C++ 201]: https://www.youtube.com/playlist?list=PL9ioqAuyl6UKP9uKZivfIAXwJzfMIQlyo
diff --git a/extensions/common/manifest_handlers/csp_info_unittest.cc b/extensions/common/manifest_handlers/csp_info_unittest.cc index 96ca27f..a669d736 100644 --- a/extensions/common/manifest_handlers/csp_info_unittest.cc +++ b/extensions/common/manifest_handlers/csp_info_unittest.cc
@@ -96,7 +96,7 @@ EXPECT_EQ(kDefaultSandboxedPageCSP, CSPInfo::GetResourceContentSecurityPolicy( extension7.get(), "/test")); - Testcase testcases[] = { + const Testcase testcases[] = { Testcase("sandboxed_pages_invalid_1.json", errors::kInvalidSandboxedPagesList), Testcase("sandboxed_pages_invalid_2.json", errors::kInvalidSandboxedPage), @@ -106,7 +106,7 @@ GetInvalidManifestKeyError(keys::kSandboxedPagesCSP)), Testcase("sandboxed_pages_invalid_5.json", GetInvalidManifestKeyError(keys::kSandboxedPagesCSP))}; - RunTestcases(testcases, std::size(testcases), EXPECT_TYPE_ERROR); + RunTestcases(testcases, EXPECT_TYPE_ERROR); } TEST_F(CSPInfoUnitTest, CSPStringKey) { @@ -128,7 +128,7 @@ } TEST_F(CSPInfoUnitTest, CSPDictionary_ExtensionPages) { - struct { + static constexpr struct { const char* file_name; const char* csp; } cases[] = {{"csp_dictionary_valid_1.json", "default-src 'none'"}, @@ -144,7 +144,7 @@ EXPECT_EQ(test_case.csp, CSPInfo::GetExtensionPagesCSP(extension.get())); } - Testcase testcases[] = { + const Testcase testcases[] = { Testcase("csp_invalid_2.json", GetInvalidManifestKeyError( keys::kContentSecurityPolicy_ExtensionPagesPath)), @@ -162,7 +162,7 @@ keys::kContentSecurityPolicy_ExtensionPagesPath, "'unsafe-eval'", "worker-src")), }; - RunTestcases(testcases, std::size(testcases), EXPECT_TYPE_ERROR); + RunTestcases(testcases, EXPECT_TYPE_ERROR); } // Tests the requirements for object-src specifications. @@ -345,7 +345,7 @@ extension.get(), test_case.resource_path)); } - Testcase testcases[] = { + const Testcase testcases[] = { {"sandbox_both_keys.json", errors::kSandboxPagesCSPKeyNotAllowed}, {"sandbox_csp_with_dictionary.json", errors::kSandboxPagesCSPKeyNotAllowed}, @@ -355,7 +355,7 @@ {"unsandboxed_csp.json", GetInvalidManifestKeyError( keys::kContentSecurityPolicy_SandboxedPagesPath)}}; - RunTestcases(testcases, std::size(testcases), EXPECT_TYPE_ERROR); + RunTestcases(testcases, EXPECT_TYPE_ERROR); } // Ensures that using a dictionary for the keys::kContentSecurityPolicy manifest
diff --git a/extensions/common/manifest_handlers/file_handler_manifest_unittest.cc b/extensions/common/manifest_handlers/file_handler_manifest_unittest.cc index 4d55610..053b5986 100644 --- a/extensions/common/manifest_handlers/file_handler_manifest_unittest.cc +++ b/extensions/common/manifest_handlers/file_handler_manifest_unittest.cc
@@ -24,7 +24,7 @@ using FileHandlersManifestTest = ManifestTest; TEST_F(FileHandlersManifestTest, InvalidFileHandlers) { - Testcase testcases[] = { + const Testcase testcases[] = { Testcase("file_handlers_invalid_handlers.json", errors::kInvalidFileHandlers), Testcase("file_handlers_invalid_type.json", @@ -44,7 +44,7 @@ Testcase("file_handlers_invalid_verb.json", errors::kInvalidFileHandlerVerb), }; - RunTestcases(testcases, std::size(testcases), EXPECT_TYPE_ERROR); + RunTestcases(testcases, EXPECT_TYPE_ERROR); } TEST_F(FileHandlersManifestTest, ValidFileHandlers) {
diff --git a/extensions/common/manifest_handlers/homepage_url_unittest.cc b/extensions/common/manifest_handlers/homepage_url_unittest.cc index ddfc39cf..301af9c 100644 --- a/extensions/common/manifest_handlers/homepage_url_unittest.cc +++ b/extensions/common/manifest_handlers/homepage_url_unittest.cc
@@ -20,15 +20,11 @@ scoped_refptr<Extension> extension( LoadAndExpectSuccess("homepage_url_valid.json")); - Testcase testcases[] = { - Testcase("homepage_url_empty.json", - errors::kInvalidHomepageURL), - Testcase("homepage_url_invalid.json", - errors::kInvalidHomepageURL), - Testcase("homepage_url_bad_schema.json", - errors::kInvalidHomepageURL) - }; - RunTestcases(testcases, std::size(testcases), EXPECT_TYPE_ERROR); + const Testcase testcases[] = { + Testcase("homepage_url_empty.json", errors::kInvalidHomepageURL), + Testcase("homepage_url_invalid.json", errors::kInvalidHomepageURL), + Testcase("homepage_url_bad_schema.json", errors::kInvalidHomepageURL)}; + RunTestcases(testcases, EXPECT_TYPE_ERROR); } TEST_F(HomepageURLManifestTest, GetHomepageURL) {
diff --git a/extensions/common/manifest_handlers/manifest_url_about_unittest.cc b/extensions/common/manifest_handlers/manifest_url_about_unittest.cc index b65bb5f..fde302b 100644 --- a/extensions/common/manifest_handlers/manifest_url_about_unittest.cc +++ b/extensions/common/manifest_handlers/manifest_url_about_unittest.cc
@@ -19,7 +19,7 @@ EXPECT_EQ(GURL("chrome-extension://" + extension->id() + "/about.html"), ManifestURL::GetAboutPage(extension.get())); - Testcase testcases[] = { + const Testcase testcases[] = { // Forbid data types other than strings. Testcase("shared_module_about_invalid_type.json", errors::kInvalidAboutPage), @@ -27,7 +27,7 @@ // Forbid absolute URLs. Testcase("shared_module_about_absolute.json", errors::kInvalidAboutPageExpectRelativePath)}; - RunTestcases(testcases, std::size(testcases), EXPECT_TYPE_ERROR); + RunTestcases(testcases, EXPECT_TYPE_ERROR); } } // namespace extensions
diff --git a/extensions/common/manifest_handlers/requirements_unittest.cc b/extensions/common/manifest_handlers/requirements_unittest.cc index a34cf2f..a20678a 100644 --- a/extensions/common/manifest_handlers/requirements_unittest.cc +++ b/extensions/common/manifest_handlers/requirements_unittest.cc
@@ -15,7 +15,7 @@ using RequirementsManifestTest = ManifestTest; TEST_F(RequirementsManifestTest, RequirementsInvalid) { - Testcase testcases[] = { + const Testcase testcases[] = { Testcase("requirements_invalid_requirements.json", "Error at key 'requirements'. Type is invalid. Expected " "dictionary, found boolean."), @@ -33,7 +33,7 @@ "Error at key 'requirements.3D.features'. Manifest key is required."), }; - RunTestcases(testcases, std::size(testcases), EXPECT_TYPE_ERROR); + RunTestcases(testcases, EXPECT_TYPE_ERROR); } TEST_F(RequirementsManifestTest, RequirementsValid) {
diff --git a/extensions/common/manifest_handlers/shared_module_manifest_unittest.cc b/extensions/common/manifest_handlers/shared_module_manifest_unittest.cc index 53c4f27..1c1a8aa5 100644 --- a/extensions/common/manifest_handlers/shared_module_manifest_unittest.cc +++ b/extensions/common/manifest_handlers/shared_module_manifest_unittest.cc
@@ -82,7 +82,7 @@ } TEST_F(SharedModuleManifestTest, ExportParseErrors) { - Testcase testcases[] = { + const Testcase testcases[] = { Testcase("shared_module_export_and_import.json", "Simultaneous 'import' and 'export' are not allowed."), Testcase("shared_module_export_not_dict.json", @@ -97,7 +97,7 @@ "Error at key 'export.allowlist'. Type is invalid. Expected " "list, found string."), }; - RunTestcases(testcases, std::size(testcases), EXPECT_TYPE_ERROR); + RunTestcases(testcases, EXPECT_TYPE_ERROR); } TEST_F(SharedModuleManifestTest, SharedModuleStaticFunctions) { @@ -166,7 +166,7 @@ } TEST_F(SharedModuleManifestTest, ImportParseErrors) { - Testcase testcases[] = { + const Testcase testcases[] = { Testcase("shared_module_import_not_list.json", "Error at key 'import'. Type is invalid. Expected list, found " "dictionary."), @@ -175,7 +175,7 @@ Testcase("shared_module_import_invalid_version.json", "Invalid value for 'import[0].minimum_version'."), }; - RunTestcases(testcases, std::size(testcases), EXPECT_TYPE_ERROR); + RunTestcases(testcases, EXPECT_TYPE_ERROR); } } // namespace extensions
diff --git a/extensions/common/manifest_handlers/update_url_unittest.cc b/extensions/common/manifest_handlers/update_url_unittest.cc index 9aea5b97..b223231 100644 --- a/extensions/common/manifest_handlers/update_url_unittest.cc +++ b/extensions/common/manifest_handlers/update_url_unittest.cc
@@ -16,7 +16,7 @@ TEST_F(UpdateURLManifestTest, UpdateUrls) { // Test several valid update urls - Testcase testcases[] = { + const Testcase testcases[] = { Testcase("update_url_valid_1.json", ManifestLocation::kInternal, Extension::NO_FLAGS), Testcase("update_url_valid_2.json", ManifestLocation::kInternal, @@ -25,15 +25,15 @@ Extension::NO_FLAGS), Testcase("update_url_valid_4.json", ManifestLocation::kInternal, Extension::NO_FLAGS)}; - RunTestcases(testcases, std::size(testcases), EXPECT_TYPE_SUCCESS); + RunTestcases(testcases, EXPECT_TYPE_SUCCESS); // Test some invalid update urls - Testcase testcases2[] = { + const Testcase testcases2[] = { Testcase("update_url_invalid_1.json", errors::kInvalidUpdateURL, ManifestLocation::kInternal, Extension::NO_FLAGS), Testcase("update_url_invalid_2.json", errors::kInvalidUpdateURL, ManifestLocation::kInternal, Extension::NO_FLAGS), Testcase("update_url_invalid_3.json", errors::kInvalidUpdateURL, ManifestLocation::kInternal, Extension::NO_FLAGS)}; - RunTestcases(testcases2, std::size(testcases2), EXPECT_TYPE_ERROR); + RunTestcases(testcases2, EXPECT_TYPE_ERROR); }
diff --git a/extensions/common/manifest_test.cc b/extensions/common/manifest_test.cc index a9e9959..459accb 100644 --- a/extensions/common/manifest_test.cc +++ b/extensions/common/manifest_test.cc
@@ -2,11 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifdef UNSAFE_BUFFERS_BUILD -// TODO(crbug.com/351564777): Remove this and convert code to safer constructs. -#pragma allow_unsafe_buffers -#endif - #include "extensions/common/manifest_test.h" #include <optional> @@ -327,11 +322,10 @@ location_(location), flags_(flags) {} -void ManifestTest::RunTestcases(const Testcase* testcases, - size_t num_testcases, +void ManifestTest::RunTestcases(base::span<const Testcase> testcases, ExpectType type) { - for (size_t i = 0; i < num_testcases; ++i) { - RunTestcase(testcases[i], type); + for (const auto& testcase : testcases) { + RunTestcase(testcase, type); } }
diff --git a/extensions/common/manifest_test.h b/extensions/common/manifest_test.h index 4c8c1fa..2f5f551 100644 --- a/extensions/common/manifest_test.h +++ b/extensions/common/manifest_test.h
@@ -12,6 +12,7 @@ #include <string> #include <string_view> +#include "base/containers/span.h" #include "base/memory/scoped_refptr.h" #include "base/values.h" #include "extensions/common/extension.h" @@ -181,9 +182,7 @@ int flags); }; - void RunTestcases(const Testcase* testcases, - size_t num_testcases, - ExpectType type); + void RunTestcases(base::span<const Testcase> testcases, ExpectType type); void RunTestcase(const Testcase& testcase, ExpectType type);
diff --git a/gpu/ipc/client/gpu_channel_host.cc b/gpu/ipc/client/gpu_channel_host.cc index 31bc968..ebf94b6 100644 --- a/gpu/ipc/client/gpu_channel_host.cc +++ b/gpu/ipc/client/gpu_channel_host.cc
@@ -264,6 +264,10 @@ base::BindOnce(&Listener::Close, base::Unretained(listener_.get()))); } +void GpuChannelHost::ResetChannelRemoteForTesting() { + gpu_channel_.reset(); +} + int32_t GpuChannelHost::ReserveImageId() { return next_image_id_.GetNext(); }
diff --git a/gpu/ipc/client/gpu_channel_host.h b/gpu/ipc/client/gpu_channel_host.h index 1afb438c..fe7c6da 100644 --- a/gpu/ipc/client/gpu_channel_host.h +++ b/gpu/ipc/client/gpu_channel_host.h
@@ -187,6 +187,9 @@ friend class base::RefCountedThreadSafe<GpuChannelHost>; virtual ~GpuChannelHost(); + // Clears its SharedAssociatedRemote. + void ResetChannelRemoteForTesting(); + private: // Establishes shared memory communication with the GPU process. This memory // is used to keep track of flushed items and avoid unnecessary IPCs.
diff --git a/headless/lib/browser/headless_web_contents_impl.cc b/headless/lib/browser/headless_web_contents_impl.cc index 1253bb1..cb9f320fb 100644 --- a/headless/lib/browser/headless_web_contents_impl.cc +++ b/headless/lib/browser/headless_web_contents_impl.cc
@@ -271,6 +271,11 @@ : blink::mojom::DisplayMode::kBrowser; } + void SetContentsBounds(content::WebContents* source, + const gfx::Rect& bounds) override { + headless_web_contents_->SetBounds(bounds); + } + private: HeadlessBrowserImpl* browser() { return headless_web_contents_->browser(); }
diff --git a/headless/test/data/protocol/sanity/window-resize-to-expected.txt b/headless/test/data/protocol/sanity/window-resize-to-expected.txt new file mode 100644 index 0000000..412f7fd --- /dev/null +++ b/headless/test/data/protocol/sanity/window-resize-to-expected.txt
@@ -0,0 +1,9 @@ +Tests window outer size is properly adjusted by `window.resizeTo()`. +Outer window size (initial): { + outerHeight : 600 + outerWidth : 800 +} +Outer window size (final): { + outerHeight : 500 + outerWidth : 700 +} \ No newline at end of file
diff --git a/headless/test/data/protocol/sanity/window-resize-to.js b/headless/test/data/protocol/sanity/window-resize-to.js new file mode 100644 index 0000000..b934311 --- /dev/null +++ b/headless/test/data/protocol/sanity/window-resize-to.js
@@ -0,0 +1,21 @@ +// Copyright 2025 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +(async function(testRunner) { + const {session} = await testRunner.startBlank('Tests window outer ' + + 'size is properly adjusted by `window.resizeTo()`.'); + const initialSize = await session.evaluate('({outerWidth, outerHeight})'); + testRunner.log(initialSize, 'Outer window size (initial): '); + + const resizePromise = session.evaluateAsync(` + new Promise(resolve => + {window.addEventListener('resize', resolve, {once: true})}) + `); + await session.evaluate('window.resizeTo(700, 500)'); + await resizePromise; + + const finalSize = await session.evaluate('({outerWidth, outerHeight})'); + testRunner.log(finalSize, 'Outer window size (final): '); + testRunner.completeTest(); +})
diff --git a/headless/test/headless_protocol_browsertest.cc b/headless/test/headless_protocol_browsertest.cc index b48ed81..157f32a 100644 --- a/headless/test/headless_protocol_browsertest.cc +++ b/headless/test/headless_protocol_browsertest.cc
@@ -356,9 +356,12 @@ HEADLESS_PROTOCOL_TEST(ShowFilePickerInterception, "sanity/show-file-picker-interception.js") +// The `change-window-*.js` tests cover DevTools methods, while `window-*.js` +// cover `window.*` JS APIs. HEADLESS_PROTOCOL_TEST(ChangeWindowSize, "sanity/change-window-size.js") HEADLESS_PROTOCOL_TEST(ChangeWindowState, "sanity/change-window-state.js") HEADLESS_PROTOCOL_TEST(WindowOuterSize, "sanity/window-outer-size.js") +HEADLESS_PROTOCOL_TEST(WindowResizeTo, "sanity/window-resize-to.js") // https://crbug.com/378531862 #if BUILDFLAG(IS_MAC)
diff --git a/infra/config/generated/builder-owners/bling-engprod@google.com.txt b/infra/config/generated/builder-owners/bling-engprod@google.com.txt index d326e75..ad159a7 100644 --- a/infra/config/generated/builder-owners/bling-engprod@google.com.txt +++ b/infra/config/generated/builder-owners/bling-engprod@google.com.txt
@@ -9,6 +9,7 @@ ci/mac-arm64-on-arm64-rel ci/mac-arm64-rel ci/mac-official +ci/mac-vm ci/mac11-arm64-rel-tests ci/mac12-arm64-rel-tests ci/mac13-arm64-rel-tests @@ -16,6 +17,7 @@ ci/mac14-tests ci/mac14-tests-dbg try/ios-vm +try/mac-vm try/mac14-arm64-rel try/mac14-arm64-rel-compilator try/mac14-tests \ No newline at end of file
diff --git a/infra/config/generated/builders/ci/mac-vm/gn-args.json b/infra/config/generated/builders/ci/mac-vm/gn-args.json new file mode 100644 index 0000000..809d265 --- /dev/null +++ b/infra/config/generated/builders/ci/mac-vm/gn-args.json
@@ -0,0 +1,11 @@ +{ + "gn_args": { + "is_component_build": true, + "is_debug": true, + "symbol_level": 1, + "target_cpu": "arm64", + "target_os": "mac", + "use_remoteexec": true, + "use_siso": true + } +} \ No newline at end of file
diff --git a/infra/config/generated/builders/ci/mac-vm/properties.json b/infra/config/generated/builders/ci/mac-vm/properties.json new file mode 100644 index 0000000..d6916b4 --- /dev/null +++ b/infra/config/generated/builders/ci/mac-vm/properties.json
@@ -0,0 +1,75 @@ +{ + "$build/chromium_tests_builder_config": { + "builder_config": { + "additional_exclusions": [ + "infra/config/generated/builders/ci/mac-vm/gn-args.json" + ], + "builder_db": { + "entries": [ + { + "builder_id": { + "bucket": "ci", + "builder": "mac-vm", + "project": "chromium" + }, + "builder_spec": { + "builder_group": "chromium.fyi", + "execution_mode": "COMPILE_AND_TEST", + "legacy_chromium_config": { + "apply_configs": [ + "mb" + ], + "build_config": "Debug", + "config": "chromium", + "target_bits": 64, + "target_platform": "mac" + }, + "legacy_gclient_config": { + "config": "chromium" + } + } + } + ] + }, + "builder_ids": [ + { + "bucket": "ci", + "builder": "mac-vm", + "project": "chromium" + } + ], + "mirroring_builder_group_and_names": [ + { + "builder": "mac-vm", + "group": "tryserver.chromium.mac" + } + ], + "targets_spec_directory": "src/infra/config/generated/builders/ci/mac-vm/targets" + } + }, + "$build/reclient": { + "instance": "rbe-chromium-trusted", + "metrics_project": "chromium-reclient-metrics", + "scandeps_server": true + }, + "$build/siso": { + "configs": [ + "builder" + ], + "enable_cloud_profiler": true, + "enable_cloud_trace": true, + "experiments": [], + "project": "rbe-chromium-trusted", + "remote_jobs": 250 + }, + "$recipe_engine/resultdb/test_presentation": { + "column_keys": [], + "grouping_keys": [ + "status", + "v.test_suite" + ] + }, + "builder_group": "chromium.fyi", + "recipe": "chromium", + "xcode_build_version": "16c5032a" +} \ No newline at end of file
diff --git a/infra/config/generated/builders/ci/mac-vm/shadow-properties.json b/infra/config/generated/builders/ci/mac-vm/shadow-properties.json new file mode 100644 index 0000000..673c7c0 --- /dev/null +++ b/infra/config/generated/builders/ci/mac-vm/shadow-properties.json
@@ -0,0 +1,17 @@ +{ + "$build/reclient": { + "instance": "rbe-chromium-untrusted", + "metrics_project": "chromium-reclient-metrics", + "scandeps_server": true + }, + "$build/siso": { + "configs": [ + "builder" + ], + "enable_cloud_profiler": true, + "enable_cloud_trace": true, + "experiments": [], + "project": "rbe-chromium-untrusted", + "remote_jobs": 250 + } +} \ No newline at end of file
diff --git a/infra/config/generated/builders/ci/mac-vm/targets/chromium.fyi.json b/infra/config/generated/builders/ci/mac-vm/targets/chromium.fyi.json new file mode 100644 index 0000000..497f301 --- /dev/null +++ b/infra/config/generated/builders/ci/mac-vm/targets/chromium.fyi.json
@@ -0,0 +1,42 @@ +{ + "mac-vm": { + "additional_compile_targets": [ + "all" + ], + "gtest_tests": [ + { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "name": "base_unittests", + "swarming": { + "dimensions": { + "cpu": "Apple_(Virtual)", + "os": "Mac-14", + "pool": "chromium.tests.macvm" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "base_unittests", + "test_id_prefix": "ninja://base:base_unittests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "name": "interactive_ui_tests", + "swarming": { + "dimensions": { + "cpu": "Apple_(Virtual)", + "os": "Mac-14", + "pool": "chromium.tests.macvm" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", + "shards": 6 + }, + "test": "interactive_ui_tests", + "test_id_prefix": "ninja://chrome/test:interactive_ui_tests/" + } + ] + } +} \ No newline at end of file
diff --git a/infra/config/generated/builders/gn_args_locations.json b/infra/config/generated/builders/gn_args_locations.json index ba66699..b11156f 100644 --- a/infra/config/generated/builders/gn_args_locations.json +++ b/infra/config/generated/builders/gn_args_locations.json
@@ -332,6 +332,7 @@ "linux-wpt-chromium-rel": "ci/linux-wpt-chromium-rel/gn-args.json", "mac-osxbeta-rel": "ci/mac-osxbeta-rel/gn-args.json", "mac-perfetto-rel": "ci/mac-perfetto-rel/gn-args.json", + "mac-vm": "ci/mac-vm/gn-args.json", "mac13-wpt-chromium-rel": "ci/mac13-wpt-chromium-rel/gn-args.json", "win-annotator-rel": "ci/win-annotator-rel/gn-args.json", "win-fieldtrial-rel": "ci/win-fieldtrial-rel/gn-args.json", @@ -890,6 +891,7 @@ "mac-perfetto-rel": "try/mac-perfetto-rel/gn-args.json", "mac-rel": "try/mac-rel/gn-args.json", "mac-ubsan-fyi-rel": "try/mac-ubsan-fyi-rel/gn-args.json", + "mac-vm": "try/mac-vm/gn-args.json", "mac11-arm64-rel": "try/mac11-arm64-rel/gn-args.json", "mac12-arm64-rel": "try/mac12-arm64-rel/gn-args.json", "mac12-tests": "try/mac12-tests/gn-args.json",
diff --git a/infra/config/generated/builders/try/mac-vm/gn-args.json b/infra/config/generated/builders/try/mac-vm/gn-args.json new file mode 100644 index 0000000..809d265 --- /dev/null +++ b/infra/config/generated/builders/try/mac-vm/gn-args.json
@@ -0,0 +1,11 @@ +{ + "gn_args": { + "is_component_build": true, + "is_debug": true, + "symbol_level": 1, + "target_cpu": "arm64", + "target_os": "mac", + "use_remoteexec": true, + "use_siso": true + } +} \ No newline at end of file
diff --git a/infra/config/generated/builders/try/mac-vm/properties.json b/infra/config/generated/builders/try/mac-vm/properties.json new file mode 100644 index 0000000..0e00899 --- /dev/null +++ b/infra/config/generated/builders/try/mac-vm/properties.json
@@ -0,0 +1,67 @@ +{ + "$build/chromium_tests_builder_config": { + "builder_config": { + "additional_exclusions": [ + "infra/config/generated/builders/try/mac-vm/gn-args.json" + ], + "builder_db": { + "entries": [ + { + "builder_id": { + "bucket": "ci", + "builder": "mac-vm", + "project": "chromium" + }, + "builder_spec": { + "builder_group": "chromium.fyi", + "execution_mode": "COMPILE_AND_TEST", + "legacy_chromium_config": { + "apply_configs": [ + "mb" + ], + "build_config": "Debug", + "config": "chromium", + "target_bits": 64, + "target_platform": "mac" + }, + "legacy_gclient_config": { + "config": "chromium" + } + } + } + ] + }, + "builder_ids": [ + { + "bucket": "ci", + "builder": "mac-vm", + "project": "chromium" + } + ], + "targets_spec_directory": "src/infra/config/generated/builders/try/mac-vm/targets" + } + }, + "$build/reclient": { + "instance": "rbe-chromium-untrusted", + "metrics_project": "chromium-reclient-metrics", + "scandeps_server": true + }, + "$build/siso": { + "configs": [ + "builder" + ], + "enable_cloud_profiler": true, + "enable_cloud_trace": true, + "experiments": [], + "project": "rbe-chromium-untrusted" + }, + "$recipe_engine/resultdb/test_presentation": { + "column_keys": [], + "grouping_keys": [ + "status", + "v.test_suite" + ] + }, + "builder_group": "tryserver.chromium.mac", + "recipe": "chromium_trybot" +} \ No newline at end of file
diff --git a/infra/config/generated/builders/try/mac-vm/targets/chromium.fyi.json b/infra/config/generated/builders/try/mac-vm/targets/chromium.fyi.json new file mode 100644 index 0000000..497f301 --- /dev/null +++ b/infra/config/generated/builders/try/mac-vm/targets/chromium.fyi.json
@@ -0,0 +1,42 @@ +{ + "mac-vm": { + "additional_compile_targets": [ + "all" + ], + "gtest_tests": [ + { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "name": "base_unittests", + "swarming": { + "dimensions": { + "cpu": "Apple_(Virtual)", + "os": "Mac-14", + "pool": "chromium.tests.macvm" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "base_unittests", + "test_id_prefix": "ninja://base:base_unittests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "name": "interactive_ui_tests", + "swarming": { + "dimensions": { + "cpu": "Apple_(Virtual)", + "os": "Mac-14", + "pool": "chromium.tests.macvm" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", + "shards": 6 + }, + "test": "interactive_ui_tests", + "test_id_prefix": "ninja://chrome/test:interactive_ui_tests/" + } + ] + } +} \ No newline at end of file
diff --git a/infra/config/generated/health-specs/health-specs.json b/infra/config/generated/health-specs/health-specs.json index 41fb4d0b..22e83e1 100644 --- a/infra/config/generated/health-specs/health-specs.json +++ b/infra/config/generated/health-specs/health-specs.json
@@ -11443,6 +11443,27 @@ } ] }, + "mac-vm": { + "contact_team_email": "bling-engprod@google.com", + "problem_specs": [ + { + "name": "Unhealthy", + "period_days": 7, + "score": 5, + "thresholds": { + "_default": "_default" + } + }, + { + "name": "Low Value", + "period_days": 90, + "score": 1, + "thresholds": { + "_default": "_default" + } + } + ] + }, "mac11-arm64-enterprise-companion-tester-dbg": { "contact_team_email": "omaha-client-dev@google.com", "problem_specs": [
diff --git a/infra/config/generated/luci/commit-queue.cfg b/infra/config/generated/luci/commit-queue.cfg index 8ba2982..90dc5f1f 100644 --- a/infra/config/generated/luci/commit-queue.cfg +++ b/infra/config/generated/luci/commit-queue.cfg
@@ -5914,6 +5914,10 @@ mode_allowlist: "FULL_RUN" } builders { + name: "chromium/try/mac-vm" + includable_only: true + } + builders { name: "chromium/try/mac11-arm64-rel" includable_only: true }
diff --git a/infra/config/generated/luci/cr-buildbucket.cfg b/infra/config/generated/luci/cr-buildbucket.cfg index 3967313..7ada85e 100644 --- a/infra/config/generated/luci/cr-buildbucket.cfg +++ b/infra/config/generated/luci/cr-buildbucket.cfg
@@ -64366,6 +64366,120 @@ } } builders { + name: "mac-vm" + swarming_host: "chromium-swarm.appspot.com" + dimensions: "builderless:1" + dimensions: "cpu:arm64" + dimensions: "free_space:standard" + dimensions: "os:Mac-14" + dimensions: "pool:luci.chromium.ci" + exe { + cipd_package: "infra/chromium/bootstrapper/${platform}" + cipd_version: "latest" + cmd: "bootstrapper" + } + properties: + '{' + ' "$bootstrap/exe": {' + ' "exe": {' + ' "cipd_package": "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build",' + ' "cipd_version": "refs/heads/main",' + ' "cmd": [' + ' "luciexe"' + ' ]' + ' }' + ' },' + ' "$bootstrap/properties": {' + ' "properties_file": "infra/config/generated/builders/ci/mac-vm/properties.json",' + ' "shadow_properties_file": "infra/config/generated/builders/ci/mac-vm/shadow-properties.json",' + ' "top_level_project": {' + ' "ref": "refs/heads/main",' + ' "repo": {' + ' "host": "chromium.googlesource.com",' + ' "project": "chromium/src"' + ' }' + ' }' + ' },' + ' "builder_group": "chromium.fyi",' + ' "led_builder_is_bootstrapped": true,' + ' "recipe": "chromium"' + '}' + priority: 35 + execution_timeout_secs: 36000 + caches { + name: "xcode_ios_16c5032a" + path: "xcode_ios_16c5032a.app" + } + build_numbers: YES + service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "chromium.use_per_builder_build_dir_name" + value: 100 + } + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "chrome-luci-data" + dataset: "chromium" + table: "ci_test_results" + test_results {} + } + bq_exports { + project: "chrome-luci-data" + dataset: "chromium" + table: "gpu_ci_test_results" + test_results { + predicate { + test_id_regexp: "ninja://(chrome|content)/test:telemetry_gpu_integration_test[^/]*/.+" + } + } + } + bq_exports { + project: "chrome-luci-data" + dataset: "chromium" + table: "blink_web_tests_ci_test_results" + test_results { + predicate { + test_id_regexp: "(ninja://[^/]*blink_web_tests/.+)|(ninja://[^/]*_wpt_tests/.+)|(ninja://[^/]*headless_shell_wpt/.+)" + } + } + } + history_options { + use_invocation_timestamp: true + } + } + description_html: "Mac builder for running testing targets on Mac Virtual Machines<br/>This builder is mirrored by any of the following try builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/try/mac-vm\">mac-vm</a></li></ul>" + shadow_builder_adjustments { + service_account: "chromium-try-builder@chops-service-accounts.iam.gserviceaccount.com" + pool: "luci.chromium.try" + dimensions: "free_space:" + dimensions: "pool:luci.chromium.try" + } + contact_team_email: "bling-engprod@google.com" + custom_metric_definitions { + name: "/chrome/infra/browser/builds/cached_count" + predicates: "has(build.output.properties.is_cached)" + predicates: "string(build.output.properties.is_cached) == \"true\"" + } + custom_metric_definitions { + name: "/chrome/infra/browser/builds/ran_tests_retry_shard_count" + predicates: "has(build.output.properties.ran_tests_retry_shard)" + } + custom_metric_definitions { + name: "/chrome/infra/browser/builds/ran_tests_without_patch_count" + predicates: "has(build.output.properties.ran_tests_without_patch)" + } + custom_metric_definitions { + name: "/chrome/infra/browser/builds/uncached_count" + predicates: "has(build.output.properties.is_cached)" + predicates: "string(build.output.properties.is_cached) == \"false\"" + } + } + builders { name: "mac11-arm64-enterprise-companion-tester-dbg" swarming_host: "chromium-swarm.appspot.com" dimensions: "builderless:1" @@ -120446,6 +120560,117 @@ } } builders { + name: "mac-vm" + swarming_host: "chromium-swarm.appspot.com" + dimensions: "builderless:1" + dimensions: "cpu:arm64" + dimensions: "free_space:standard" + dimensions: "os:Mac-14" + dimensions: "pool:luci.chromium.try" + dimensions: "ssd:1" + exe { + cipd_package: "infra/chromium/bootstrapper/${platform}" + cipd_version: "latest" + cmd: "bootstrapper" + } + properties: + '{' + ' "$bootstrap/exe": {' + ' "exe": {' + ' "cipd_package": "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build",' + ' "cipd_version": "refs/heads/main",' + ' "cmd": [' + ' "luciexe"' + ' ]' + ' }' + ' },' + ' "$bootstrap/properties": {' + ' "properties_file": "infra/config/generated/builders/try/mac-vm/properties.json",' + ' "top_level_project": {' + ' "ref": "refs/heads/main",' + ' "repo": {' + ' "host": "chromium.googlesource.com",' + ' "project": "chromium/src"' + ' }' + ' }' + ' },' + ' "builder_group": "tryserver.chromium.mac",' + ' "led_builder_is_bootstrapped": true,' + ' "recipe": "chromium_trybot"' + '}' + execution_timeout_secs: 14400 + expiration_secs: 7200 + grace_period { + seconds: 120 + } + build_numbers: YES + service_account: "chromium-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "chromium.use_per_builder_build_dir_name" + value: 100 + } + experiments { + key: "luci.buildbucket.canary_software" + value: 5 + } + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "chrome-luci-data" + dataset: "chromium" + table: "try_test_results" + test_results {} + } + bq_exports { + project: "chrome-luci-data" + dataset: "chromium" + table: "gpu_try_test_results" + test_results { + predicate { + test_id_regexp: "ninja://(chrome|content)/test:telemetry_gpu_integration_test[^/]*/.+" + } + } + } + bq_exports { + project: "chrome-luci-data" + dataset: "chromium" + table: "blink_web_tests_try_test_results" + test_results { + predicate { + test_id_regexp: "(ninja://[^/]*blink_web_tests/.+)|(ninja://[^/]*_wpt_tests/.+)|(ninja://[^/]*headless_shell_wpt/.+)" + } + } + } + history_options { + use_invocation_timestamp: true + } + } + description_html: "<br>Mac builder for running testing targets on Mac Virtual Machines<br/><br/>This builder mirrors the following CI builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/mac-vm\">mac-vm</a></li></ul>" + contact_team_email: "bling-engprod@google.com" + custom_metric_definitions { + name: "/chrome/infra/browser/builds/cached_count" + predicates: "has(build.output.properties.is_cached)" + predicates: "string(build.output.properties.is_cached) == \"true\"" + } + custom_metric_definitions { + name: "/chrome/infra/browser/builds/ran_tests_retry_shard_count" + predicates: "has(build.output.properties.ran_tests_retry_shard)" + } + custom_metric_definitions { + name: "/chrome/infra/browser/builds/ran_tests_without_patch_count" + predicates: "has(build.output.properties.ran_tests_without_patch)" + } + custom_metric_definitions { + name: "/chrome/infra/browser/builds/uncached_count" + predicates: "has(build.output.properties.is_cached)" + predicates: "string(build.output.properties.is_cached) == \"false\"" + } + } + builders { name: "mac11-arm64-rel" swarming_host: "chromium-swarm.appspot.com" dimensions: "builderless:1"
diff --git a/infra/config/generated/luci/luci-milo.cfg b/infra/config/generated/luci/luci-milo.cfg index c45eb25a..d985911 100644 --- a/infra/config/generated/luci/luci-milo.cfg +++ b/infra/config/generated/luci/luci-milo.cfg
@@ -10116,11 +10116,6 @@ short_name: "ios-blk" } builders { - name: "buildbucket/luci.chromium.ci/ios-vm" - category: "iOS" - short_name: "vm" - } - builders { name: "buildbucket/luci.chromium.ci/ios-webkit-tot" category: "iOS" short_name: "wk" @@ -10279,6 +10274,16 @@ short_name: "cmp" } builders { + name: "buildbucket/luci.chromium.ci/ios-vm" + category: "macvm" + short_name: "ios" + } + builders { + name: "buildbucket/luci.chromium.ci/mac-vm" + category: "macvm" + short_name: "mac" + } + builders { name: "buildbucket/luci.chromium.ci/linux-multiscreen-fyi-rel" category: "mulitscreen" } @@ -17935,6 +17940,9 @@ name: "buildbucket/luci.chromium.try/mac-updater-try-builder-rel" } builders { + name: "buildbucket/luci.chromium.try/mac-vm" + } + builders { name: "buildbucket/luci.chromium.try/mac11-arm64-rel" } builders { @@ -19431,6 +19439,9 @@ name: "buildbucket/luci.chromium.try/mac-ubsan-fyi-rel" } builders { + name: "buildbucket/luci.chromium.try/mac-vm" + } + builders { name: "buildbucket/luci.chromium.try/mac11-arm64-rel" } builders {
diff --git a/infra/config/generated/luci/luci-scheduler.cfg b/infra/config/generated/luci/luci-scheduler.cfg index 784ca6a..16adbbf 100644 --- a/infra/config/generated/luci/luci-scheduler.cfg +++ b/infra/config/generated/luci/luci-scheduler.cfg
@@ -5685,6 +5685,16 @@ } } job { + id: "mac-vm" + realm: "ci" + schedule: "0 2-23/4 * * *" + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "ci" + builder: "mac-vm" + } +} +job { id: "mac11-arm64-enterprise-companion-tester-dbg" realm: "ci" buildbucket {
diff --git a/infra/config/subprojects/chromium/ci/chromium.fyi.star b/infra/config/subprojects/chromium/ci/chromium.fyi.star index 351f0a89..d51d111 100644 --- a/infra/config/subprojects/chromium/ci/chromium.fyi.star +++ b/infra/config/subprojects/chromium/ci/chromium.fyi.star
@@ -869,6 +869,53 @@ ), ) +fyi_ios_builder( + name = "mac-vm", + description_html = "Mac builder for running testing targets on Mac Virtual Machines", + # every 4 hours beginning at 2am, deliberately offsetting from ios-vm + schedule = "0 2-23/4 * * *", + triggered_by = [], + builder_spec = builder_config.builder_spec( + gclient_config = builder_config.gclient_config( + config = "chromium", + ), + chromium_config = builder_config.chromium_config( + config = "chromium", + apply_configs = ["mb"], + build_config = builder_config.build_config.DEBUG, + target_bits = 64, + target_platform = builder_config.target_platform.MAC, + ), + ), + gn_args = gn_args.config( + configs = [ + "debug_builder", + "remoteexec", + "mac", + "arm64", + ], + ), + targets = targets.bundle( + targets = [ + "mac_vm_tests", + ], + additional_compile_targets = [ + "all", + ], + mixins = [ + "mac_vm", + ], + ), + builderless = True, + os = os.MAC_DEFAULT, + cpu = cpu.ARM64, + console_view_entry = consoles.console_view_entry( + category = "macvm", + short_name = "mac", + ), + contact_team_email = "bling-engprod@google.com", +) + fyi_mac_builder( name = "mac13-wpt-chromium-rel", description_html = "Runs {} against Chrome.".format( @@ -2017,8 +2064,8 @@ os = os.MAC_DEFAULT, cpu = cpu.ARM64, console_view_entry = consoles.console_view_entry( - category = "iOS", - short_name = "vm", + category = "macvm", + short_name = "ios", ), contact_team_email = "bling-engprod@google.com", xcode = xcode.xcode_default,
diff --git a/infra/config/subprojects/chromium/try/tryserver.chromium.mac.star b/infra/config/subprojects/chromium/try/tryserver.chromium.mac.star index b5bed3f..a7fdfc8 100644 --- a/infra/config/subprojects/chromium/try/tryserver.chromium.mac.star +++ b/infra/config/subprojects/chromium/try/tryserver.chromium.mac.star
@@ -257,6 +257,15 @@ ) try_.builder( + name = "mac-vm", + mirrors = ["ci/mac-vm"], + gn_args = "ci/mac-vm", + builderless = True, + cpu = cpu.ARM64, + contact_team_email = "bling-engprod@google.com", +) + +try_.builder( name = "mac12-arm64-rel", branch_selector = branches.selector.MAC_BRANCHES, mirrors = [
diff --git a/infra/config/targets/bundles.star b/infra/config/targets/bundles.star index a9dd6380..d011783 100644 --- a/infra/config/targets/bundles.star +++ b/infra/config/targets/bundles.star
@@ -5327,6 +5327,21 @@ ) targets.bundle( + name = "mac_vm_tests", + targets = [ + "base_unittests", + "interactive_ui_tests", + ], + per_test_modifications = { + "interactive_ui_tests": targets.mixin( + swarming = targets.swarming( + shards = 6, + ), + ), + }, +) + +targets.bundle( name = "monochrome_public_apk_checker_isolated_script", targets = [ "monochrome_public_apk_checker",
diff --git a/internal b/internal index 7cf9e93..3ce4702c 160000 --- a/internal +++ b/internal
@@ -1 +1 @@ -Subproject commit 7cf9e93279ad7acb591d761935900a49457bbd70 +Subproject commit 3ce4702c33df16bb3cc2619173d8f80639872707
diff --git a/ios/chrome/browser/flags/about_flags.mm b/ios/chrome/browser/flags/about_flags.mm index 8716830..e3b7fa6 100644 --- a/ios/chrome/browser/flags/about_flags.mm +++ b/ios/chrome/browser/flags/about_flags.mm
@@ -137,6 +137,14 @@ {"5000", signin::kWaitThresholdMillisecondsForCapabilitiesApi, "5000"}, }; +const FeatureEntry::Choice kLensOverlayAlternativeOnboardingChoices[] = { + {flags_ui::kGenericExperimentChoiceDefault, "", ""}, + {"A: Speedbump menu", kLensOverlayAlternativeOnboardingType, "1"}, + {"B: Updated Strings", kLensOverlayAlternativeOnboardingType, "2"}, + {"C: Updated Strings and Graphics", kLensOverlayAlternativeOnboardingType, + "3"}, +}; + const FeatureEntry::FeatureParam kOmniboxUIMaxAutocompleteMatches3[] = { {OmniboxFieldTrial::kUIMaxAutocompleteMatchesParam, "3"}}; const FeatureEntry::FeatureParam kOmniboxUIMaxAutocompleteMatches4[] = { @@ -2095,6 +2103,11 @@ flag_descriptions::kLensOverlayForceShowOnboardingScreenDescription, flags_ui::kOsIos, FEATURE_VALUE_TYPE(kLensOverlayForceShowOnboardingScreen)}, + {"lens-overlay-alternative-onboarding", + flag_descriptions::kLensOverlayAlternativeOnboardingName, + flag_descriptions::kLensOverlayAlternativeOnboardingDescription, + flags_ui::kOsIos, + MULTI_VALUE_TYPE(kLensOverlayAlternativeOnboardingChoices)}, {"data-sharing", flag_descriptions::kDataSharingName, flag_descriptions::kDataSharingDescription, flags_ui::kOsIos, FEATURE_VALUE_TYPE(data_sharing::features::kDataSharingFeature)},
diff --git a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc index fdb5793..3192e4c 100644 --- a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc +++ b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
@@ -685,6 +685,12 @@ const char kLensFiltersAblationModeEnabledDescription[] = "Enables the filters ablation mode."; +extern const char kLensOverlayAlternativeOnboardingName[] = + "Lens Overlay Onboarding"; +extern const char kLensOverlayAlternativeOnboardingDescription[] = + "Selects which lens overlay onboarding/entrypoint treatment is active. " + "No-op if lens overlay is off."; + extern const char kLensOverlayDisablePriceInsightsName[] = "Allow Lens overlay to disable price insights"; extern const char kLensOverlayDisablePriceInsightsDescription[] =
diff --git a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h index 029d7db..cf72a1c7d 100644 --- a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h +++ b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
@@ -589,6 +589,11 @@ extern const char kLensOverlayForceShowOnboardingScreenName[]; extern const char kLensOverlayForceShowOnboardingScreenDescription[]; +// Title and description for the flag to switch the onboarding type for lens +// overlay. +extern const char kLensOverlayAlternativeOnboardingName[]; +extern const char kLensOverlayAlternativeOnboardingDescription[]; + // Title and description for the flag to disable price insights because of lens // overlay. extern const char kLensOverlayDisablePriceInsightsName[];
diff --git a/ios/chrome/browser/shared/public/features/features.h b/ios/chrome/browser/shared/public/features/features.h index aca37f0..900a470 100644 --- a/ios/chrome/browser/shared/public/features/features.h +++ b/ios/chrome/browser/shared/public/features/features.h
@@ -341,6 +341,12 @@ // Feature to enable force showing the lens overlay onboarding screen. BASE_DECLARE_FEATURE(kLensOverlayForceShowOnboardingScreen); +// Types of lens overlay onboarding. +extern const char kLensOverlayAlternativeOnboardingType[]; + +// Feature flag to switch between the lens overlay onboarding types. +BASE_DECLARE_FEATURE(kLensOverlayAlternativeOnboarding); + // Feature flag to enable UITraitCollection workaround for fixing incorrect // trait propagation. BASE_DECLARE_FEATURE(kEnableTraitCollectionWorkAround);
diff --git a/ios/chrome/browser/shared/public/features/features.mm b/ios/chrome/browser/shared/public/features/features.mm index 80aee986..1db81f9 100644 --- a/ios/chrome/browser/shared/public/features/features.mm +++ b/ios/chrome/browser/shared/public/features/features.mm
@@ -279,6 +279,13 @@ "EnableLensOverlayForceShowOnboardingScreen", base::FEATURE_DISABLED_BY_DEFAULT); +const char kLensOverlayAlternativeOnboardingType[] = + "kLensOverlayAlternativeOnboardingType"; + +BASE_FEATURE(kLensOverlayAlternativeOnboarding, + "LensOverlayAlternativeOnboarding", + base::FEATURE_ENABLED_BY_DEFAULT); + BASE_FEATURE(kEnableTraitCollectionWorkAround, "EnableTraitCollectionWorkAround", base::FEATURE_ENABLED_BY_DEFAULT);
diff --git a/ios/google_internal/frameworks/ChromeExtensionKeychainInternal.framework.dSYM.ios.zip.sha1 b/ios/google_internal/frameworks/ChromeExtensionKeychainInternal.framework.dSYM.ios.zip.sha1 index ede94dc..1a631eb 100644 --- a/ios/google_internal/frameworks/ChromeExtensionKeychainInternal.framework.dSYM.ios.zip.sha1 +++ b/ios/google_internal/frameworks/ChromeExtensionKeychainInternal.framework.dSYM.ios.zip.sha1
@@ -1 +1 @@ -e1ca9f19b6327cda00dd4cc202472b273a996cfe \ No newline at end of file +f9c276013bc09835ee777f276500a1c0fc3d417b \ No newline at end of file
diff --git a/ios/google_internal/frameworks/ChromeExtensionKeychainInternal.framework.dSYM.ios_asan.zip.sha1 b/ios/google_internal/frameworks/ChromeExtensionKeychainInternal.framework.dSYM.ios_asan.zip.sha1 index a7af0232..da944615 100644 --- a/ios/google_internal/frameworks/ChromeExtensionKeychainInternal.framework.dSYM.ios_asan.zip.sha1 +++ b/ios/google_internal/frameworks/ChromeExtensionKeychainInternal.framework.dSYM.ios_asan.zip.sha1
@@ -1 +1 @@ -6687d24c97104f7518a5490b2278c23480cc7c2f \ No newline at end of file +c7f503874ba3361e987d69532d76f2fd350dbf2a \ No newline at end of file
diff --git a/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios.zip.sha1 b/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios.zip.sha1 index 05b8277..edb108b 100644 --- a/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios.zip.sha1 +++ b/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios.zip.sha1
@@ -1 +1 @@ -572e5c1b9e74615fb0bee9db5f2a32f439cbd06b \ No newline at end of file +4c4b351b3bb6be8d3c8059b7c79a561c4561dd94 \ No newline at end of file
diff --git a/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios_asan.zip.sha1 b/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios_asan.zip.sha1 index 15a5bedbd..dee895e 100644 --- a/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios_asan.zip.sha1 +++ b/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios_asan.zip.sha1
@@ -1 +1 @@ -5471dcfef6ce2458b6f02f1b084aa9ece6f17ed0 \ No newline at end of file +354466def2316c12291dae63299b4b1bd8664d14 \ No newline at end of file
diff --git a/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios.zip.sha1 b/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios.zip.sha1 index 7b06ec8..2a576b3 100644 --- a/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios.zip.sha1 +++ b/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios.zip.sha1
@@ -1 +1 @@ -9fbecf50f813beb326ad465c0f7f18cf10bdc958 \ No newline at end of file +6e5b74c35489209f87ddaddfd6849dcab21b8d46 \ No newline at end of file
diff --git a/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios_asan.zip.sha1 b/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios_asan.zip.sha1 index 89201572..e2b0dd9 100644 --- a/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios_asan.zip.sha1 +++ b/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios_asan.zip.sha1
@@ -1 +1 @@ -460bf83e1ecab35084dad26b6030e0ca4bfde97e \ No newline at end of file +091e9c6335d8cb953360aeeded681cc18a5825b4 \ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.ios.zip.sha1 index 0c3812a..eccc093a 100644 --- a/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.ios.zip.sha1 +++ b/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@ -9dba49ae7e5d4ff5510667be5aedf8f2590244a4 \ No newline at end of file +58a71c8b35b458bc524690b63eb6c1c0e2083875 \ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.ios_asan.zip.sha1 b/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.ios_asan.zip.sha1 index d5119663..abeefab 100644 --- a/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.ios_asan.zip.sha1 +++ b/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.ios_asan.zip.sha1
@@ -1 +1 @@ -dfabad38c6d32633f9fbe5b78be00ec9b2d4f6e7 \ No newline at end of file +8003265b7f1d9c379b54965304f8a66413fe40d6 \ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.iossimulator.zip.sha1 index 3ed7bfd..ec51b1c9 100644 --- a/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.iossimulator.zip.sha1 +++ b/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@ -971c753906e7d6a3012b3ace190bedf2c99e17b1 \ No newline at end of file +fae2cfeb80bdf208eee13184537ee9167b544af7 \ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.iossimulator_asan.zip.sha1 b/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.iossimulator_asan.zip.sha1 index aea5596..7dc1db0b 100644 --- a/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.iossimulator_asan.zip.sha1 +++ b/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.iossimulator_asan.zip.sha1
@@ -1 +1 @@ -52de1a9029b8b0b5f832e7cc7023b941a66f9d9f \ No newline at end of file +a2148282160b76d5786fc7292ac16fdc2ddc82f9 \ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1 index 3a66c3d..42ce12b 100644 --- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1 +++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@ -199991d9951c24ecea45c497ffe48ba7d655a4cb \ No newline at end of file +aea44dfc626dc4d0d5bc0fa8a7ff564bc7a84f87 \ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios_asan.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios_asan.zip.sha1 index a4f15c77..5960e2d 100644 --- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios_asan.zip.sha1 +++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios_asan.zip.sha1
@@ -1 +1 @@ -6205d2142f52248e7819833a0051091d85ceeaef \ No newline at end of file +b1c940f614287f746a1270c8e5f84ebddfb6abb5 \ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1 index 067708c..7b93f624 100644 --- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1 +++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@ -ab748dd653714cf3550844f30a8a9ca3e5450863 \ No newline at end of file +20d36a8fcc917b8316fdacac67127c2e15a36e60 \ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator_asan.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator_asan.zip.sha1 index 63de7b5..6f556b29 100644 --- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator_asan.zip.sha1 +++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator_asan.zip.sha1
@@ -1 +1 @@ -92e6272ba0e6f3da1577dcf1143a035a20df846b \ No newline at end of file +329024a6a1c99035186eb39b476224f5b1410ae6 \ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1 index ba7a328f..97fde3c 100644 --- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1 +++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@ -fdc9736d61bab24b874fdfa201e6ae49062d575e \ No newline at end of file +d547c5757aa6da23dbf31943e8b9c8a6acf8827e \ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios_asan.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios_asan.zip.sha1 index 101ebc3..6486e63a 100644 --- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios_asan.zip.sha1 +++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios_asan.zip.sha1
@@ -1 +1 @@ -5e22cd33277a837e91b84ffd0dfe90ee151598df \ No newline at end of file +f4de64ba9c0ce97079c8cedef2ae9a9145eff136 \ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1 index e2a5020..e629a55 100644 --- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1 +++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@ -2425ca3486ca3d6794d3d3fe40b2c585879c283f \ No newline at end of file +c82402c19a19f285c36d56e0336b4054798cf62e \ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator_asan.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator_asan.zip.sha1 index 7adb934..a7d6c72a 100644 --- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator_asan.zip.sha1 +++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator_asan.zip.sha1
@@ -1 +1 @@ -f33067cb299eb413feecdac9b5ab2bd5ddfda9e4 \ No newline at end of file +3b5af3f669a11d41d994011bc88d6b8feba92e3c \ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.ios.zip.sha1 index 8c94bb6b..a2fc0482 100644 --- a/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.ios.zip.sha1 +++ b/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@ -51b805092a8586e6a15ec2620136e76a81f9c9c0 \ No newline at end of file +8b4a447dd294735093852817c4a46a1801ad37e0 \ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.iossimulator.zip.sha1 index 0833a8a..3a39c11 100644 --- a/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.iossimulator.zip.sha1 +++ b/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@ -141c7a7b1b73e5db613b2b3459143df0f4318535 \ No newline at end of file +794d180e7e49e92ccb5826ebf621115a80cb33de \ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1 index 439bc55d8..a6ffd579 100644 --- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1 +++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@ -92eb9b384365835b2065a381da0b9d1ee076580d \ No newline at end of file +a25df86ef3d523cf0b76cd906fbe18a1606466d5 \ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios_asan.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios_asan.zip.sha1 index 5789dde..17a6baf1 100644 --- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios_asan.zip.sha1 +++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios_asan.zip.sha1
@@ -1 +1 @@ -83610c5dfc9e7c85a32e124f5645d6973071d63c \ No newline at end of file +de3e01355c2aea9737854eda243fa37aa395e386 \ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1 index 4874aa1..6e57ed6 100644 --- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1 +++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@ -09f1f695b635fd7ecc03083361bdf8541c8a2b54 \ No newline at end of file +2e0e63e1802daf772419cf88c433cac71f3bbd32 \ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator_asan.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator_asan.zip.sha1 index ac2e64d..7e292f7 100644 --- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator_asan.zip.sha1 +++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator_asan.zip.sha1
@@ -1 +1 @@ -363dc20dbc4841788c54b3c705f70181795e32ed \ No newline at end of file +eb438bdd0566d4afdd0ad3fa3ddf28d2d19e3f8d \ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1 index a204b8e..1881cc93 100644 --- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1 +++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@ -7e3e3a69a57638df8831bec9de343e13dc9898d9 \ No newline at end of file +8cd584677c8c5fdfcb0bd3663382ddaf756e23fb \ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios_asan.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios_asan.zip.sha1 index 80be1cc1..77e8dd3 100644 --- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios_asan.zip.sha1 +++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios_asan.zip.sha1
@@ -1 +1 @@ -410888b44529570f9c00a4beadfb575df0153b98 \ No newline at end of file +14b7d0ca7388da4855c02635cb9a02acaf5ff43e \ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1 index 962117d0..f8c87a9 100644 --- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1 +++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@ -c2f7d0d7e7db529217db5913e19f231e549c823f \ No newline at end of file +4650bb45c9e15a8da92e061ea6c336dd849874b3 \ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator_asan.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator_asan.zip.sha1 index db07508..8c3e45331 100644 --- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator_asan.zip.sha1 +++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator_asan.zip.sha1
@@ -1 +1 @@ -23ae741873b9da24dcc78c21680e42e2f3d945ad \ No newline at end of file +9fd170e4992a15e10892975eff5b9ffa0f73d108 \ No newline at end of file
diff --git a/ios/third_party/material_components_ios/src b/ios/third_party/material_components_ios/src index 0ac0dfe..5c9ba05 160000 --- a/ios/third_party/material_components_ios/src +++ b/ios/third_party/material_components_ios/src
@@ -1 +1 @@ -Subproject commit 0ac0dfe5adf41700ef61e3bb0a1ece2362757433 +Subproject commit 5c9ba055eef03a043b7cf5191de54e1197fee86a
diff --git a/ios_internal b/ios_internal index fa89054..5d2fd8e 160000 --- a/ios_internal +++ b/ios_internal
@@ -1 +1 @@ -Subproject commit fa89054d303701d661169f5d7cf4e8473dd3fe98 +Subproject commit 5d2fd8e76328068efba5dcac5421f3390b7aa9e7
diff --git a/media/cast/sender/frame_sender.h b/media/cast/sender/frame_sender.h index c6960c31..9a5911b 100644 --- a/media/cast/sender/frame_sender.h +++ b/media/cast/sender/frame_sender.h
@@ -47,18 +47,12 @@ virtual void OnFrameCanceled(FrameId frame_id) {} }; - // NOTE: currently only used by the VideoSender. - // TODO(https://crbug.com/1316434): cleanup bitrate calculations when libcast - // has successfully launched. - using GetSuggestedVideoBitrateCB = base::RepeatingCallback<int()>; - // Method of creating a frame sender using an openscreen::cast::Sender. static std::unique_ptr<FrameSender> Create( scoped_refptr<CastEnvironment> cast_environment, const FrameSenderConfig& config, std::unique_ptr<openscreen::cast::Sender> sender, - Client& client, - GetSuggestedVideoBitrateCB get_bitrate_cb = GetSuggestedVideoBitrateCB()); + Client& client); FrameSender(); FrameSender(FrameSender&&) = delete; @@ -104,10 +98,6 @@ // Returns the number of frames that were sent but not yet acknowledged. virtual int GetUnacknowledgedFrameCount() const = 0; - // Returns the suggested bitrate the next frame should be encoded at. - virtual int GetSuggestedBitrate(base::TimeTicks playout_time, - base::TimeDelta playout_delay) = 0; - // Configuration specific methods. // The maximum frame rate.
diff --git a/media/cast/sender/openscreen_frame_sender.cc b/media/cast/sender/openscreen_frame_sender.cc index fc30382..53a8f4e 100644 --- a/media/cast/sender/openscreen_frame_sender.cc +++ b/media/cast/sender/openscreen_frame_sender.cc
@@ -52,11 +52,9 @@ scoped_refptr<CastEnvironment> cast_environment, const FrameSenderConfig& config, std::unique_ptr<openscreen::cast::Sender> sender, - Client& client, - FrameSender::GetSuggestedVideoBitrateCB get_bitrate_cb) { + Client& client) { return std::make_unique<OpenscreenFrameSender>(cast_environment, config, - std::move(sender), client, - std::move(get_bitrate_cb)); + std::move(sender), client); } // Convenience macro used in logging statements throughout this file. @@ -68,8 +66,7 @@ scoped_refptr<CastEnvironment> cast_environment, const FrameSenderConfig& config, std::unique_ptr<openscreen::cast::Sender> sender, - Client& client, - FrameSender::GetSuggestedVideoBitrateCB get_bitrate_cb) + Client& client) : cast_environment_(cast_environment), sender_(std::move(sender)), client_(client), @@ -78,10 +75,6 @@ min_playout_delay_(config.min_playout_delay), max_playout_delay_(config.max_playout_delay) { CHECK_GT(sender_->config().rtp_timebase, 0); - if (!is_audio_) { - bitrate_suggester_ = std::make_unique<VideoBitrateSuggester>( - config, std::move(get_bitrate_cb)); - } const std::chrono::milliseconds target_playout_delay = sender_->config().target_playout_delay; @@ -168,13 +161,6 @@ return sender_->GetInFlightFrameCount(); } -int OpenscreenFrameSender::GetSuggestedBitrate(base::TimeTicks playout_time, - base::TimeDelta playout_delay) { - // Currently only used by the video sender. - DCHECK(!is_audio_); - return bitrate_suggester_->GetSuggestedBitrate(); -} - double OpenscreenFrameSender::MaxFrameRate() const { return max_frame_rate_; } @@ -294,7 +280,6 @@ const int count_frames_in_flight = GetUnacknowledgedFrameCount() + client_->GetNumberOfFramesInEncoder(); if (count_frames_in_flight >= kMaxUnackedFrames) { - RecordShouldDropNextFrame(/*should_drop=*/true); return CastStreamingFrameDropReason::kTooManyFramesInFlight; } @@ -304,7 +289,6 @@ const double max_frames_in_flight = max_frame_rate_ * duration_in_flight.InSecondsF(); if (count_frames_in_flight >= max_frames_in_flight + kMaxFrameBurst) { - RecordShouldDropNextFrame(/*should_drop=*/true); return CastStreamingFrameDropReason::kBurstThresholdExceeded; } @@ -331,18 +315,11 @@ } } if (duration_would_be_in_flight > allowed_in_flight) { - RecordShouldDropNextFrame(/*should_drop=*/true); return CastStreamingFrameDropReason::kInFlightDurationTooHigh; } // Next frame is accepted. - RecordShouldDropNextFrame(/*should_drop=*/false); return CastStreamingFrameDropReason::kNotDropped; } -void OpenscreenFrameSender::RecordShouldDropNextFrame(bool should_drop) { - if (bitrate_suggester_) { - bitrate_suggester_->RecordShouldDropNextFrame(should_drop); - } -} } // namespace media::cast
diff --git a/media/cast/sender/openscreen_frame_sender.h b/media/cast/sender/openscreen_frame_sender.h index 01c18362..56ab9bc1 100644 --- a/media/cast/sender/openscreen_frame_sender.h +++ b/media/cast/sender/openscreen_frame_sender.h
@@ -46,8 +46,7 @@ OpenscreenFrameSender(scoped_refptr<CastEnvironment> cast_environment, const FrameSenderConfig& config, std::unique_ptr<openscreen::cast::Sender> sender, - Client& client, - FrameSender::GetSuggestedVideoBitrateCB get_bitrate_cb); + Client& client); OpenscreenFrameSender(OpenscreenFrameSender&& other) = delete; OpenscreenFrameSender& operator=(OpenscreenFrameSender&& other) = delete; OpenscreenFrameSender(const OpenscreenFrameSender&) = delete; @@ -64,8 +63,6 @@ base::TimeDelta frame_duration) override; RtpTimeTicks GetRecordedRtpTimestamp(FrameId frame_id) const override; int GetUnacknowledgedFrameCount() const override; - int GetSuggestedBitrate(base::TimeTicks playout_time, - base::TimeDelta playout_delay) override; double MaxFrameRate() const override; void SetMaxFrameRate(double max_frame_rate) override; base::TimeDelta TargetPlayoutDelay() const override; @@ -98,8 +95,6 @@ // fluctuates in response to the currently-measured network latency. base::TimeDelta GetAllowedInFlightMediaDuration() const; - void RecordShouldDropNextFrame(bool should_drop); - // The cast environment. const scoped_refptr<CastEnvironment> cast_environment_; @@ -109,9 +104,6 @@ // The frame sender client. const raw_ref<Client> client_; - // The method for getting the recommended bitrate. - GetSuggestedVideoBitrateCB get_bitrate_cb_; - // Max encoded frames generated per second. double max_frame_rate_; @@ -160,10 +152,6 @@ // buffer is the lower 8 bits of the FrameId. std::array<RtpTimeTicks, 256> frame_rtp_timestamps_; - // TODO(https://crbug.com/1316434): move this property to VideoSender once - // the legacy implementation has been removed. - std::unique_ptr<VideoBitrateSuggester> bitrate_suggester_; - // NOTE: Weak pointers must be invalidated before all other member variables. base::WeakPtrFactory<OpenscreenFrameSender> weak_factory_{this}; };
diff --git a/media/cast/sender/openscreen_frame_sender_unittest.cc b/media/cast/sender/openscreen_frame_sender_unittest.cc index 815fc0a..818f07b9 100644 --- a/media/cast/sender/openscreen_frame_sender_unittest.cc +++ b/media/cast/sender/openscreen_frame_sender_unittest.cc
@@ -82,8 +82,6 @@ base::TimeDelta GetEncoderBacklogDuration() const override { return {}; } void OnFrameCanceled(FrameId frame_id) override {} - int get_suggested_bitrate() { return suggested_bitrate_; } - protected: OpenscreenFrameSenderTest() : task_runner_( @@ -108,23 +106,11 @@ audio_sender_ = std::make_unique<OpenscreenFrameSender>( cast_environment_, kAudioConfig, std::move(openscreen_audio_sender), - *this, - base::BindRepeating(&OpenscreenFrameSenderTest::get_suggested_bitrate, - // Safe because we destroy the audio sender before - // destroying `this`. - base::Unretained(this))); + *this); video_sender_ = std::make_unique<OpenscreenFrameSender>( cast_environment_, kVideoConfig, std::move(openscreen_video_sender), - *this, - base::BindRepeating(&OpenscreenFrameSenderTest::get_suggested_bitrate, - // Safe because we destroy the audio sender before - // destroying `this`. - base::Unretained(this))); - } - - void RecordShouldDropNextFrame(bool should_drop) { - video_sender_->RecordShouldDropNextFrame(should_drop); + *this); } void set_suggested_bitrate(int bitrate) { suggested_bitrate_ = bitrate; } @@ -275,18 +261,4 @@ video_sender().EnqueueFrame(std::move(video_frame_two))); } -TEST_F(OpenscreenFrameSenderTest, HandlesSuggestedBitratesCorrectly) { - // NOTE: the VideoBitrateSuggester tests this workflow more thoroughly. - - // We should start with the maximum video bitrate. - set_suggested_bitrate(5000001); - EXPECT_EQ(5000000, video_sender().GetSuggestedBitrate(base::TimeTicks{}, - base::TimeDelta{})); - - // It should cap at the bitrate suggested by Open Screen. - set_suggested_bitrate(4998374); - EXPECT_EQ(4998374, video_sender().GetSuggestedBitrate(base::TimeTicks{}, - base::TimeDelta{})); -} - } // namespace media::cast
diff --git a/media/cast/sender/video_bitrate_suggester.cc b/media/cast/sender/video_bitrate_suggester.cc index d6bb94d8..7091f749 100644 --- a/media/cast/sender/video_bitrate_suggester.cc +++ b/media/cast/sender/video_bitrate_suggester.cc
@@ -6,26 +6,24 @@ #include <algorithm> #include <limits> -#include <memory> -#include <utility> -#include <vector> +#include "base/check.h" #include "base/feature_list.h" #include "base/logging.h" #include "media/base/media_switches.h" -#include "media/cast/common/openscreen_conversion_helpers.h" -#include "media/cast/common/sender_encoded_frame.h" #include "media/cast/constants.h" namespace media::cast { VideoBitrateSuggester::VideoBitrateSuggester( const FrameSenderConfig& config, - FrameSender::GetSuggestedVideoBitrateCB get_bitrate_cb) - : get_bitrate_cb_(std::move(get_bitrate_cb)), + GetVideoNetworkBandwidthCB get_bitrate_cb) + : get_bandwidth_cb_(std::move(get_bitrate_cb)), min_bitrate_(config.min_bitrate), max_bitrate_(config.max_bitrate), - suggested_max_bitrate_(max_bitrate_) {} + suggested_bitrate_(max_bitrate_) { + CHECK_GE(max_bitrate_, min_bitrate_); +} VideoBitrateSuggester::~VideoBitrateSuggester() = default; @@ -34,16 +32,16 @@ // we also need to consider how well this device is handling encoding at // this bitrate overall. const int suggested_bitrate = - std::min(get_bitrate_cb_.Run(), suggested_max_bitrate_); + std::min(get_bandwidth_cb_.Run(), suggested_bitrate_); // Honor the config boundaries. return std::clamp(suggested_bitrate, min_bitrate_, max_bitrate_); } void VideoBitrateSuggester::RecordShouldDropNextFrame(bool should_drop) { - ++number_of_frames_requested_; + ++frames_requested_; if (should_drop) { - ++number_of_frames_dropped_; + ++frames_dropped_; } if (base::FeatureList::IsEnabled( @@ -55,53 +53,39 @@ } void VideoBitrateSuggester::UpdateSuggestionUsingExponentialAlgorithm() { - // We don't want to change the bitrate too frequently in order to give - // things time to adjust, so only adjust roughly once a second. - constexpr int kWindowSize = 30; - if (number_of_frames_requested_ == kWindowSize) { - DCHECK_GE(max_bitrate_, min_bitrate_); + static constexpr int kWindowSize = 30; + if (frames_requested_ == kWindowSize) { + // Be more conservative about increasing than decreasing the bitrate. + constexpr double kIncreaseFactor = 1.1; + constexpr double kDecreaseFactor = 0.7; - // We want to be more conservative about increasing the frame rate than - // decreasing it. - constexpr double kIncrease = 1.1; - constexpr double kDecrease = 0.8; + // Dropping any frames is a bad sign. + suggested_bitrate_ = + (frames_dropped_ > 0) + ? std::max<int>(min_bitrate_, suggested_bitrate_ * kDecreaseFactor) + : std::min<int>(max_bitrate_, suggested_bitrate_ * kIncreaseFactor); - // Generally speaking we shouldn't be dropping any frames, so even one is - // a bad sign. - suggested_max_bitrate_ = - (number_of_frames_dropped_ > 0) - ? std::max<int>(min_bitrate_, suggested_max_bitrate_ * kDecrease) - : std::min<int>(max_bitrate_, suggested_max_bitrate_ * kIncrease); - - // Reset the recorded frame drops to start a new window. - number_of_frames_requested_ = 0; - number_of_frames_dropped_ = 0; + // Reset the frame counts to start a new window. + frames_requested_ = 0; + frames_dropped_ = 0; } } void VideoBitrateSuggester::UpdateSuggestionUsingLinearAlgorithm() { - // We don't want to change the bitrate too frequently in order to give - // things time to adjust, so only adjust every 100 frames (about 3 seconds - // at 30FPS). - constexpr int kWindowSize = 100; - if (number_of_frames_requested_ == kWindowSize) { - constexpr int kBitrateSteps = 8; - DCHECK_GE(max_bitrate_, min_bitrate_); + static constexpr int kWindowSize = 100; + if (frames_requested_ == kWindowSize) { + static constexpr int kBitrateSteps = 8; const int adjustment = (max_bitrate_ - min_bitrate_) / kBitrateSteps; - // Generally speaking we shouldn't be dropping any frames, so even one is - // a bad sign. - if (number_of_frames_dropped_ > 0) { - suggested_max_bitrate_ = - std::max(min_bitrate_, suggested_max_bitrate_ - adjustment); - } else { - suggested_max_bitrate_ = - std::min(max_bitrate_, suggested_max_bitrate_ + adjustment); - } + // Dropping any frames is a bad sign. + suggested_bitrate_ = + (frames_dropped_ > 0) + ? std::max(min_bitrate_, suggested_bitrate_ - adjustment) + : std::min(max_bitrate_, suggested_bitrate_ + adjustment); - // Reset the recorded frame drops to start a new window. - number_of_frames_requested_ = 0; - number_of_frames_dropped_ = 0; + // Reset the frame counts to start a new window. + frames_requested_ = 0; + frames_dropped_ = 0; } }
diff --git a/media/cast/sender/video_bitrate_suggester.h b/media/cast/sender/video_bitrate_suggester.h index ba8b995..c62f5f3 100644 --- a/media/cast/sender/video_bitrate_suggester.h +++ b/media/cast/sender/video_bitrate_suggester.h
@@ -6,14 +6,15 @@ #define MEDIA_CAST_SENDER_VIDEO_BITRATE_SUGGESTER_H_ #include "media/cast/cast_config.h" -#include "media/cast/sender/frame_sender.h" namespace media::cast { class VideoBitrateSuggester { public: + using GetVideoNetworkBandwidthCB = base::RepeatingCallback<int()>; + VideoBitrateSuggester(const FrameSenderConfig& config, - FrameSender::GetSuggestedVideoBitrateCB get_bitrate_cb); + GetVideoNetworkBandwidthCB get_bitrate_cb); VideoBitrateSuggester(VideoBitrateSuggester&& other) = delete; VideoBitrateSuggester& operator=(VideoBitrateSuggester&& other) = delete; VideoBitrateSuggester(const VideoBitrateSuggester&) = delete; @@ -33,19 +34,19 @@ void UpdateSuggestionUsingLinearAlgorithm(); // The method for getting the recommended bitrate. - FrameSender::GetSuggestedVideoBitrateCB get_bitrate_cb_; + GetVideoNetworkBandwidthCB get_bandwidth_cb_; // The minimum and maximum bitrates set from the config. - int min_bitrate_ = 0; - int max_bitrate_ = 0; + const int min_bitrate_ = 0; + const int max_bitrate_ = 0; - // The suggested maximum bitrate, factoring in frame drops. - int suggested_max_bitrate_ = 0; + // The suggested bitrate, factoring in frame drops. + int suggested_bitrate_ = 0; // We keep track of how many frames get dropped in order to lower the video // bitrate when appropriate. - int number_of_frames_requested_ = 0; - int number_of_frames_dropped_ = 0; + int frames_requested_ = 0; + int frames_dropped_ = 0; }; } // namespace media::cast
diff --git a/media/cast/sender/video_bitrate_suggester_unittest.cc b/media/cast/sender/video_bitrate_suggester_unittest.cc index 569f36d6..969d529 100644 --- a/media/cast/sender/video_bitrate_suggester_unittest.cc +++ b/media/cast/sender/video_bitrate_suggester_unittest.cc
@@ -54,7 +54,7 @@ protected: VideoBitrateSuggesterTest() { - video_bitrate_suggester_ = std::make_unique<VideoBitrateSuggester>( + suggester_ = std::make_unique<VideoBitrateSuggester>( kVideoConfig, base::BindRepeating(&VideoBitrateSuggesterTest::get_suggested_bitrate, // Safe because we destroy the audio sender before @@ -63,14 +63,12 @@ } void RecordShouldDropNextFrame(bool should_drop) { - video_bitrate_suggester_->RecordShouldDropNextFrame(should_drop); + suggester_->RecordShouldDropNextFrame(should_drop); } void set_suggested_bitrate(int bitrate) { suggested_bitrate_ = bitrate; } - VideoBitrateSuggester& video_bitrate_suggester() { - return *video_bitrate_suggester_; - } + VideoBitrateSuggester& suggester() { return *suggester_; } void UseExponentialAlgorithm() { feature_list_.InitAndEnableFeature( @@ -83,18 +81,26 @@ } private: - std::unique_ptr<VideoBitrateSuggester> video_bitrate_suggester_; + std::unique_ptr<VideoBitrateSuggester> suggester_; base::test::ScopedFeatureList feature_list_; int suggested_bitrate_ = 0; }; +TEST_F(VideoBitrateSuggesterTest, StaysWithinBounds) { + set_suggested_bitrate(10000000); + EXPECT_EQ(kDefaultMaxVideoBitrate, suggester().GetSuggestedBitrate()); + + set_suggested_bitrate(1); + EXPECT_EQ(kDefaultMinVideoBitrate, suggester().GetSuggestedBitrate()); +} + TEST_F(VideoBitrateSuggesterTest, SuggestsBitratesCorrectlyWithExponentialAlgorithm) { UseExponentialAlgorithm(); // We should start with the maximum video bitrate. set_suggested_bitrate(5000001); - EXPECT_EQ(5000000, video_bitrate_suggester().GetSuggestedBitrate()); + EXPECT_EQ(kDefaultMaxVideoBitrate, suggester().GetSuggestedBitrate()); // After a period with multiple frame drops, this should go down. RecordShouldDropNextFrame(true); @@ -103,44 +109,42 @@ RecordShouldDropNextFrame(false); } - // It should now go down. - EXPECT_EQ(4000000, video_bitrate_suggester().GetSuggestedBitrate()); - // It should continue to go down to the minimum as long as frames are being // dropped. - int last_suggestion = 4685120; - for (int i = 0; i < 12; ++i) { + int last_suggestion = suggester().GetSuggestedBitrate(); + EXPECT_EQ(3500000, last_suggestion); + while (last_suggestion > kDefaultMinVideoBitrate) { RecordShouldDropNextFrame(true); for (int j = 0; j < 29; ++j) { RecordShouldDropNextFrame(false); } // It should drop every time. - const int suggestion = video_bitrate_suggester().GetSuggestedBitrate(); + const int suggestion = suggester().GetSuggestedBitrate(); EXPECT_LT(suggestion, last_suggestion); last_suggestion = suggestion; } // And then stabilize at the bottom. - EXPECT_EQ(300000, video_bitrate_suggester().GetSuggestedBitrate()); + EXPECT_EQ(kDefaultMinVideoBitrate, suggester().GetSuggestedBitrate()); // It should increase once we stop dropping frames. - last_suggestion = 300000; - for (int i = 0; i < 30; ++i) { + last_suggestion = kDefaultMinVideoBitrate; + while (last_suggestion < kDefaultMaxVideoBitrate) { for (int j = 0; j < 30; ++j) { RecordShouldDropNextFrame(false); } - const int suggestion = video_bitrate_suggester().GetSuggestedBitrate(); + const int suggestion = suggester().GetSuggestedBitrate(); EXPECT_GT(suggestion, last_suggestion); last_suggestion = suggestion; } // And stop at the maximum. - EXPECT_EQ(5000000, video_bitrate_suggester().GetSuggestedBitrate()); + EXPECT_EQ(kDefaultMaxVideoBitrate, suggester().GetSuggestedBitrate()); // Finally, it should cap at the bitrate suggested by Open Screen. set_suggested_bitrate(4998374); - EXPECT_EQ(4998374, video_bitrate_suggester().GetSuggestedBitrate()); + EXPECT_EQ(4998374, suggester().GetSuggestedBitrate()); } TEST_F(VideoBitrateSuggesterTest, @@ -149,7 +153,7 @@ // We should start with the maximum video bitrate. set_suggested_bitrate(5000001); - EXPECT_EQ(5000000, video_bitrate_suggester().GetSuggestedBitrate()); + EXPECT_EQ(kDefaultMaxVideoBitrate, suggester().GetSuggestedBitrate()); // After a period with multiple frame drops, this should go down. RecordShouldDropNextFrame(true); @@ -159,42 +163,40 @@ } // It should now go down. - EXPECT_EQ(4412500, video_bitrate_suggester().GetSuggestedBitrate()); + int last_suggestion = suggester().GetSuggestedBitrate(); + EXPECT_EQ(4412500, last_suggestion); // It should continue to go down to the minimum as long as frames are being // dropped. - int last_suggestion = 4412500; - for (int i = 0; i < 7; ++i) { + while (last_suggestion > kDefaultMinVideoBitrate) { RecordShouldDropNextFrame(true); for (int j = 0; j < 99; ++j) { RecordShouldDropNextFrame(false); } // It should drop every time. - const int suggestion = video_bitrate_suggester().GetSuggestedBitrate(); + const int suggestion = suggester().GetSuggestedBitrate(); EXPECT_LT(suggestion, last_suggestion); last_suggestion = suggestion; } - // And then stabilize at the bottom. - EXPECT_EQ(300000, video_bitrate_suggester().GetSuggestedBitrate()); - // It should increase once we stop dropping frames. - last_suggestion = 300000; - for (int i = 0; i < 8; ++i) { + last_suggestion = suggester().GetSuggestedBitrate(); + EXPECT_EQ(kDefaultMinVideoBitrate, last_suggestion); + while (last_suggestion < kDefaultMaxVideoBitrate) { for (int j = 0; j < 100; ++j) { RecordShouldDropNextFrame(false); } - const int suggestion = video_bitrate_suggester().GetSuggestedBitrate(); + const int suggestion = suggester().GetSuggestedBitrate(); EXPECT_GT(suggestion, last_suggestion); last_suggestion = suggestion; } // And stop at the maximum. - EXPECT_EQ(5000000, video_bitrate_suggester().GetSuggestedBitrate()); + EXPECT_EQ(kDefaultMaxVideoBitrate, suggester().GetSuggestedBitrate()); // Finally, it should cap at the bitrate suggested by Open Screen. set_suggested_bitrate(4998374); - EXPECT_EQ(4998374, video_bitrate_suggester().GetSuggestedBitrate()); + EXPECT_EQ(4998374, suggester().GetSuggestedBitrate()); } } // namespace media::cast
diff --git a/media/cast/sender/video_sender.cc b/media/cast/sender/video_sender.cc index b13caed..56481d1 100644 --- a/media/cast/sender/video_sender.cc +++ b/media/cast/sender/video_sender.cc
@@ -23,6 +23,7 @@ #include "media/cast/encoding/video_encoder.h" #include "media/cast/sender/openscreen_frame_sender.h" #include "media/cast/sender/performance_metrics_overlay.h" +#include "media/cast/sender/video_bitrate_suggester.h" #include "third_party/openscreen/src/cast/streaming/public/encoded_frame.h" #include "third_party/openscreen/src/cast/streaming/public/sender.h" @@ -111,16 +112,18 @@ std::unique_ptr<openscreen::cast::Sender> sender, std::unique_ptr<media::VideoEncoderMetricsProvider> encoder_metrics_provider, - PlayoutDelayChangeCB playout_delay_change_cb, + VideoSender::PlayoutDelayChangeCB playout_delay_change_cb, media::VideoCaptureFeedbackCB feedback_cb, - FrameSender::GetSuggestedVideoBitrateCB get_bitrate_cb, + VideoBitrateSuggester::GetVideoNetworkBandwidthCB get_bandwidth_cb, media::GpuVideoAcceleratorFactories* gpu_factories) : frame_sender_(FrameSender::Create(cast_environment, video_config, std::move(sender), - *this, - std::move(get_bitrate_cb))), + *this)), cast_environment_(cast_environment), + bitrate_suggester_( + std::make_unique<VideoBitrateSuggester>(video_config, + std::move(get_bandwidth_cb))), min_playout_delay_(video_config.min_playout_delay), max_playout_delay_(video_config.max_playout_delay), playout_delay_change_cb_(std::move(playout_delay_change_cb)), @@ -214,7 +217,10 @@ number_of_frames_inserted_++; const CastStreamingFrameDropReason reason = frame_sender_->ShouldDropNextFrame(duration_added_by_next_frame); - if (reason != CastStreamingFrameDropReason::kNotDropped) { + const bool should_drop_frame = + reason != CastStreamingFrameDropReason::kNotDropped; + bitrate_suggester_->RecordShouldDropNextFrame(should_drop_frame); + if (should_drop_frame) { base::TimeDelta new_target_delay = std::min(frame_sender_->CurrentRoundTripTime() * kRoundTripsNeeded + base::Milliseconds(kConstantTimeMs), @@ -250,9 +256,7 @@ return; } - const int bitrate = frame_sender_->GetSuggestedBitrate( - reference_time + frame_sender_->TargetPlayoutDelay(), - frame_sender_->TargetPlayoutDelay()); + const int bitrate = bitrate_suggester_->GetSuggestedBitrate(); if (bitrate != last_bitrate_) { video_encoder_->SetBitRate(bitrate); last_bitrate_ = bitrate;
diff --git a/media/cast/sender/video_sender.h b/media/cast/sender/video_sender.h index 3e19967..81efd5c 100644 --- a/media/cast/sender/video_sender.h +++ b/media/cast/sender/video_sender.h
@@ -17,6 +17,7 @@ #include "media/cast/cast_config.h" #include "media/cast/common/rtp_time.h" #include "media/cast/sender/frame_sender.h" +#include "media/cast/sender/video_bitrate_suggester.h" namespace openscreen::cast { class Sender; @@ -32,7 +33,6 @@ class VideoEncoder; -using PlayoutDelayChangeCB = base::RepeatingCallback<void(base::TimeDelta)>; // Not thread safe. Only called from the main cast thread. // This class owns all objects related to sending video, objects that create RTP @@ -42,19 +42,22 @@ // timeouts. class VideoSender : public FrameSender::Client { public: + using PlayoutDelayChangeCB = base::RepeatingCallback<void(base::TimeDelta)>; + // NOTE: Since the `Sender` instance is destroyed when renegotiation is // complete, `this` is also invalid and should be immediately torn down. - VideoSender(scoped_refptr<CastEnvironment> cast_environment, - const FrameSenderConfig& video_config, - StatusChangeCallback status_change_cb, - const CreateVideoEncodeAcceleratorCallback& create_vea_cb, - std::unique_ptr<openscreen::cast::Sender> sender, - std::unique_ptr<media::VideoEncoderMetricsProvider> - encoder_metrics_provider, - PlayoutDelayChangeCB playout_delay_change_cb, - media::VideoCaptureFeedbackCB feedback_cb, - FrameSender::GetSuggestedVideoBitrateCB get_bitrate_cb, - media::GpuVideoAcceleratorFactories* gpu_factories); + VideoSender( + scoped_refptr<CastEnvironment> cast_environment, + const FrameSenderConfig& video_config, + StatusChangeCallback status_change_cb, + const CreateVideoEncodeAcceleratorCallback& create_vea_cb, + std::unique_ptr<openscreen::cast::Sender> sender, + std::unique_ptr<media::VideoEncoderMetricsProvider> + encoder_metrics_provider, + PlayoutDelayChangeCB playout_delay_change_cb, + media::VideoCaptureFeedbackCB feedback_cb, + VideoBitrateSuggester::GetVideoNetworkBandwidthCB get_bandwidth_cb, + media::GpuVideoAcceleratorFactories* gpu_factories); VideoSender(const VideoSender&) = delete; VideoSender& operator=(const VideoSender&) = delete; @@ -108,6 +111,9 @@ // we get the same value. int last_bitrate_ = 0; + // Keeps track of frame drops and uses that to drive the video bitrate. + std::unique_ptr<VideoBitrateSuggester> bitrate_suggester_; + // The total amount of time between a frame's capture/recording on the sender // and its playback on the receiver (i.e., shown to a user). base::TimeDelta min_playout_delay_;
diff --git a/media/cast/sender/video_sender_unittest.cc b/media/cast/sender/video_sender_unittest.cc index b82f61a..a8298e2 100644 --- a/media/cast/sender/video_sender_unittest.cc +++ b/media/cast/sender/video_sender_unittest.cc
@@ -81,7 +81,7 @@ void IgnorePlayoutDelayChanges(base::TimeDelta unused_playout_delay) {} -int GetSuggestedVideoBitrate() { +int GetVideoNetworkBandwidth() { return openscreen::cast::kDefaultVideoMinBitRate; } @@ -203,7 +203,7 @@ base::BindRepeating(&IgnorePlayoutDelayChanges), base::BindRepeating(&VideoSenderTest::HandleVideoCaptureFeedback, base::Unretained(this)), - base::BindRepeating(&GetSuggestedVideoBitrate), + base::BindRepeating(&GetVideoNetworkBandwidth), mock_gpu_factories_.get()); RunTasksAndAdvanceClock();
diff --git a/mojo/core/ipcz_driver/transport.cc b/mojo/core/ipcz_driver/transport.cc index 0789ea1..96918f8 100644 --- a/mojo/core/ipcz_driver/transport.cc +++ b/mojo/core/ipcz_driver/transport.cc
@@ -186,6 +186,12 @@ // is connected to a known elevated process.) return PlatformHandle(); } + // Verify that this is a handle to a valid object. We do not yet know the + // expected type of the handle (region, file, etc.) so cannot validate that. + DWORD dummy; + if (!::GetHandleInformation(handle, &dummy)) { + return PlatformHandle(); + } return PlatformHandle(base::win::ScopedHandle(handle)); } @@ -208,6 +214,11 @@ } // namespace +// static +size_t Transport::FirstHandleOffsetForTesting() { + return sizeof(ObjectHeader); +} + Transport::Transport(EndpointTypes endpoint_types, PlatformChannelEndpoint endpoint, base::Process remote_process,
diff --git a/mojo/core/ipcz_driver/transport.h b/mojo/core/ipcz_driver/transport.h index ae400f3..6e69734 100644 --- a/mojo/core/ipcz_driver/transport.h +++ b/mojo/core/ipcz_driver/transport.h
@@ -161,6 +161,9 @@ void OnChannelError(Channel::Error error) override; void OnChannelDestroyed() override; + // Allow tests to nerf serialized handles to validate recipient behavior. + static size_t FirstHandleOffsetForTesting(); + private: struct PendingTransmission { PendingTransmission();
diff --git a/mojo/core/ipcz_driver/transport_test.cc b/mojo/core/ipcz_driver/transport_test.cc index 376bf9c..49267b5 100644 --- a/mojo/core/ipcz_driver/transport_test.cc +++ b/mojo/core/ipcz_driver/transport_test.cc
@@ -496,5 +496,59 @@ }); } +#if BUILDFLAG(IS_WIN) +constexpr std::string_view kGotInvalid = "got an invalid handle as expected"; +DEFINE_TEST_CLIENT_TEST_WITH_PIPE(InvalidHandleClient, + MojoIpczTransportTest, + h) { + scoped_refptr<Transport> transport = ReceiveTransport(h); + + TransportListener listener(*transport); + TestMessage message = listener.WaitForNextMessage(); + scoped_refptr<ObjectBase> object; + // We nerfed the handle between serialization and sending so this fails. + const IpczResult result = transport->DeserializeObject( + base::span(message.bytes), base::span(message.handles), object); + EXPECT_EQ(result, IPCZ_RESULT_INVALID_ARGUMENT); + TestMessage(kGotInvalid).Transmit(*transport); + EXPECT_EQ(MOJO_RESULT_OK, MojoClose(h)); +} + +TEST_F(MojoIpczTransportTest, InvalidHandle) { + RunTestClientWithController("InvalidHandleClient", [&](ClientController& c) { + scoped_refptr<Transport> transport = + CreateAndSendTransport(c.pipe(), c.process()); + + TransportListener listener(*transport); + auto region = base::UnsafeSharedMemoryRegion::Create(kGotInvalid.size()); + auto fake_buffer = SharedBuffer::MakeForRegion(std::move(region)); + size_t num_bytes = 0; + size_t num_handles = 0; + TestMessage message; + message.handles.resize(num_handles); + EXPECT_EQ(IPCZ_RESULT_RESOURCE_EXHAUSTED, + transport->SerializeObject(*fake_buffer, message.bytes.data(), + &num_bytes, message.handles.data(), + &num_handles)); + message.bytes.resize(num_bytes); + EXPECT_EQ(IPCZ_RESULT_OK, + transport->SerializeObject(*fake_buffer, message.bytes.data(), + &num_bytes, message.handles.data(), + &num_handles)); + // Nerf the handle. + uint8_t* handle_ptr = + base::span(message.bytes) + .subspan(Transport::FirstHandleOffsetForTesting(), sizeof(uint32_t)) + .data(); + *reinterpret_cast<uint32_t*>(handle_ptr) = 0x12345678u; + // Also close the region in the parent. + ::CloseHandle(fake_buffer->region().GetPlatformHandle()); + message.Transmit(*transport); + EXPECT_EQ(kGotInvalid, listener.WaitForNextMessage().as_string()); + listener.WaitForDisconnect(); + }); +} +#endif // BUILDFLAG(IS_WIN) + } // namespace } // namespace mojo::core::ipcz_driver
diff --git a/mojo/public/mojom/base/BUILD.gn b/mojo/public/mojom/base/BUILD.gn index b87af89..4b72a2e3d 100644 --- a/mojo/public/mojom/base/BUILD.gn +++ b/mojo/public/mojom/base/BUILD.gn
@@ -647,6 +647,19 @@ ] }, ] + + ts_typemaps = [ + { + types = [ + { + mojom = "mojo_base.mojom.JSTime" + ts = "Date" + converter = "JsTimeConverter" + import = "time_converters.js" + }, + ] + }, + ] } mojom_component("protobuf_support") {
diff --git a/mojo/public/mojom/base/time_converters.ts b/mojo/public/mojom/base/time_converters.ts new file mode 100644 index 0000000..e862dfb4 --- /dev/null +++ b/mojo/public/mojom/base/time_converters.ts
@@ -0,0 +1,16 @@ +// Copyright 2025 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +import type {JSTimeDataView, JSTimeTypeMapper} from './time.mojom-converters.js'; + +export class JsTimeConverter implements JSTimeTypeMapper<Date> { + // Encoding + msec(date: Date): number { + return date.valueOf(); + } + + // Decoding + convert(view: JSTimeDataView): Date { + return new Date(view.msec()); + } +}
diff --git a/net/http/http_stream_pool_attempt_manager.cc b/net/http/http_stream_pool_attempt_manager.cc index eef9322f..20b9fea 100644 --- a/net/http/http_stream_pool_attempt_manager.cc +++ b/net/http/http_stream_pool_attempt_manager.cc
@@ -750,6 +750,15 @@ CHECK(!quic_task_result_.has_value()); quic_task_result_ = rv; net_error_details_ = std::move(details); + + // Record completion time only when QuicTask actually attempted QUIC. + if (rv != ERR_DNS_NO_MATCHING_SUPPORTED_ALPN) { + base::UmaHistogramTimes( + base::StrCat({"Net.HttpStreamPool.QuicTaskTime.", + rv == OK ? "Success" : "Failure"}), + base::TimeTicks::Now() - quic_task_->attempt_start_time()); + } + quic_task_.reset(); net_log().AddEvent(
diff --git a/net/http/http_stream_pool_quic_task.cc b/net/http/http_stream_pool_quic_task.cc index 5c9e989b..2d0a756 100644 --- a/net/http/http_stream_pool_quic_task.cc +++ b/net/http/http_stream_pool_quic_task.cc
@@ -99,6 +99,9 @@ net_log_.AddEvent(NetLogEventType::HTTP_STREAM_POOL_QUIC_ATTEMPT_START, [&] { return quic_endpoint->ToValue(); }); + CHECK(attempt_start_time_.is_null()); + attempt_start_time_ = base::TimeTicks::Now(); + session_attempt_ = quic_session_pool()->CreateSessionAttempt( this, GetKey().session_key(), std::move(*quic_endpoint), cert_verify_flags, dns_resolution_start_time, dns_resolution_end_time,
diff --git a/net/http/http_stream_pool_quic_task.h b/net/http/http_stream_pool_quic_task.h index e8f5286..4600b97 100644 --- a/net/http/http_stream_pool_quic_task.h +++ b/net/http/http_stream_pool_quic_task.h
@@ -51,6 +51,10 @@ // any. Never returns ERR_IO_PENDING. std::optional<int> start_result() const { return start_result_; } + // Returns the start time of a session attempt. Maybe null when no attempt is + // made. + base::TimeTicks attempt_start_time() const { return attempt_start_time_; } + private: const HttpStreamKey& stream_key() const; @@ -78,6 +82,7 @@ // TODO(crbug.com/346835898): Support multiple attempts. std::unique_ptr<QuicSessionAttempt> session_attempt_; + base::TimeTicks attempt_start_time_; base::WeakPtrFactory<QuicTask> weak_ptr_factory_{this}; };
diff --git a/net/log/net_log_event_type_list.h b/net/log/net_log_event_type_list.h index b78b476..b25fdbb 100644 --- a/net/log/net_log_event_type_list.h +++ b/net/log/net_log_event_type_list.h
@@ -887,6 +887,15 @@ // } EVENT_TYPE(TRANSPORT_CONNECT_JOB_CONNECT_ATTEMPT) +// This event is logged whenever the SSLConnectJob attempts a +// SSLClientSocket::Connect(). +// +// { +// "ech_enabled": <True when ECH is enabled>, +// "ech_config_list": <The binary representation of ECH config list>, +// } +EVENT_TYPE(SSL_CONNECT_JOB_SSL_CONNECT) + // ------------------------------------------------------------------------ // ClientSocketPoolBaseHelper // ------------------------------------------------------------------------
diff --git a/net/socket/ssl_connect_job.cc b/net/socket/ssl_connect_job.cc index dfce490..90f555f8 100644 --- a/net/socket/ssl_connect_job.cc +++ b/net/socket/ssl_connect_job.cc
@@ -368,6 +368,12 @@ } } + net_log().AddEvent(NetLogEventType::SSL_CONNECT_JOB_SSL_CONNECT, [&] { + base::Value::Dict dict; + dict.Set("ech_enabled", ssl_client_context()->config().ech_enabled); + dict.Set("ech_config_list", NetLogBinaryValue(ssl_config.ech_config_list)); + return dict; + }); ssl_socket_ = client_socket_factory()->CreateSSLClientSocket( ssl_client_context(), std::move(nested_socket_), params_->host_and_port(), ssl_config);
diff --git a/pdf/mojom/pdf.mojom b/pdf/mojom/pdf.mojom index 80f7145b..6ca1cbc2 100644 --- a/pdf/mojom/pdf.mojom +++ b/pdf/mojom/pdf.mojom
@@ -38,6 +38,10 @@ // Get the text contained on the given page of the PDF. `page_index` should be // the range [0, # of pages). GetPageText(int32 page_index) => (mojo_base.mojom.String16 text); + + // Returns the index of the most visible page. If no page is visible, + // returns nullopt. + GetMostVisiblePageIndex() => (uint32? page_index); }; // Browser-side interface used by PDF renderers.
diff --git a/pdf/pdf_view_web_plugin.cc b/pdf/pdf_view_web_plugin.cc index 2c8da49d..b5628eb8 100644 --- a/pdf/pdf_view_web_plugin.cc +++ b/pdf/pdf_view_web_plugin.cc
@@ -1528,6 +1528,16 @@ page_count); } +void PdfViewWebPlugin::GetMostVisiblePageIndex( + GetMostVisiblePageIndexCallback callback) { + auto page_index = engine_->GetMostVisiblePage(); + if (page_index < 0) { + std::move(callback).Run(std::nullopt); + return; + } + std::move(callback).Run(page_index); +} + void PdfViewWebPlugin::GetPageText(int32_t page_index, GetPageTextCallback callback) { if (page_index < 0 || page_index >= engine_->GetNumberOfPages()) {
diff --git a/pdf/pdf_view_web_plugin.h b/pdf/pdf_view_web_plugin.h index 0d01a56..2f1cfea 100644 --- a/pdf/pdf_view_web_plugin.h +++ b/pdf/pdf_view_web_plugin.h
@@ -395,6 +395,7 @@ const gfx::PointF& extent) override; void GetPdfBytes(uint32_t size_limit, GetPdfBytesCallback callback) override; void GetPageText(int32_t page_index, GetPageTextCallback callback) override; + void GetMostVisiblePageIndex(GetMostVisiblePageIndexCallback callback) override; // UrlLoader::Client: bool IsValid() const override;
diff --git a/pdf/pdfium/pdfium_page.cc b/pdf/pdfium/pdfium_page.cc index 3daf6097..72150ac 100644 --- a/pdf/pdfium/pdfium_page.cc +++ b/pdf/pdfium/pdfium_page.cc
@@ -79,6 +79,21 @@ kRotate270 = 3, }; +std::optional<Rotation> GetRotationFromRawValue(int rotation) { + switch (rotation) { + case 0: + return Rotation::kRotate0; + case 1: + return Rotation::kRotate90; + case 2: + return Rotation::kRotate180; + case 3: + return Rotation::kRotate270; + default: + return std::nullopt; + } +} + gfx::RectF FloatPageRectToPixelRect(FPDF_PAGE page, const gfx::RectF& input) { int output_width = FPDF_GetPageWidthF(page); int output_height = FPDF_GetPageHeightF(page); @@ -746,9 +761,14 @@ return gfx::RectF(); } + std::optional<Rotation> rotation = + GetRotationFromRawValue(FPDFPage_GetRotation(page)); + if (!rotation.has_value()) { + return gfx::RectF(); + } + // Page width and height are already swapped based on page rotation. gfx::SizeF page_size(FPDF_GetPageWidthF(page), FPDF_GetPageHeightF(page)); - Rotation rotation = static_cast<Rotation>(FPDFPage_GetRotation(page)); // Start with bounds with the left and bottom values at the max possible // bounds and the right and top values at the min possible bounds. Bounds are @@ -779,10 +799,10 @@ } gfx::RectF bounding_box = - GetRotatedRectF(rotation, page_size, largest_bounds); + GetRotatedRectF(rotation.value(), page_size, largest_bounds); gfx::RectF effective_crop_box = - GetEffectiveCropBox(page, rotation, page_size); + GetEffectiveCropBox(page, rotation.value(), page_size); // If the bounding box is empty, default to the effective crop box. if (bounding_box.IsEmpty()) {
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json index b8dc1a5..fdaa6ee3 100644 --- a/testing/variations/fieldtrial_testing_config.json +++ b/testing/variations/fieldtrial_testing_config.json
@@ -8511,6 +8511,21 @@ ] } ], + "DozeModePowerScheduler": [ + { + "platforms": [ + "chromeos" + ], + "experiments": [ + { + "name": "Enabled", + "enable_features": [ + "EnableDozeModePowerScheduler" + ] + } + ] + } + ], "EagerPrefetchBlockUntilHeadDifferentTimeoutsRetrospective": [ { "platforms": [ @@ -17825,6 +17840,26 @@ ] } ], + "PrivacySandboxEqualizedPromptButtons": [ + { + "platforms": [ + "android", + "chromeos", + "chromeos_lacros", + "linux", + "mac", + "windows" + ], + "experiments": [ + { + "name": "Enabled", + "enable_features": [ + "PrivacySandboxEqualizedPromptButtons" + ] + } + ] + } + ], "PrivacySandboxInternalsDevUI": [ { "platforms": [
diff --git a/third_party/angle b/third_party/angle index c10f5e3..c289b30 160000 --- a/third_party/angle +++ b/third_party/angle
@@ -1 +1 @@ -Subproject commit c10f5e3fbf61e024ed298700dbc0b5faf888ec5f +Subproject commit c289b30f332d55bf0156d7122dac00f1aebe56e0
diff --git a/third_party/blink/common/features.cc b/third_party/blink/common/features.cc index c732cd1..23ddc67 100644 --- a/third_party/blink/common/features.cc +++ b/third_party/blink/common/features.cc
@@ -1106,6 +1106,10 @@ "HiddenSelectionBounds", base::FEATURE_ENABLED_BY_DEFAULT); +BASE_FEATURE(kIgnoreInputWhileHidden, + "IgnoreInputWhileHidden", + base::FEATURE_ENABLED_BY_DEFAULT); + BASE_FEATURE(kImageLoadingPrioritizationFix, "ImageLoadingPrioritizationFix", base::FEATURE_DISABLED_BY_DEFAULT);
diff --git a/third_party/blink/common/storage_key/storage_key.cc b/third_party/blink/common/storage_key/storage_key.cc index a77ff35..2d2a5d3 100644 --- a/third_party/blink/common/storage_key/storage_key.cc +++ b/third_party/blink/common/storage_key/storage_key.cc
@@ -8,7 +8,6 @@ #include <ostream> #include <string> #include <string_view> -#include <tuple> #include "base/feature_list.h" #include "base/ranges/algorithm.h" @@ -852,24 +851,6 @@ other.top_level_site_if_third_party_enabled_; } -bool operator==(const StorageKey& lhs, const StorageKey& rhs) { - return std::tie(lhs.origin_, lhs.top_level_site_, lhs.nonce_, - lhs.ancestor_chain_bit_) == - std::tie(rhs.origin_, rhs.top_level_site_, rhs.nonce_, - rhs.ancestor_chain_bit_); -} - -bool operator!=(const StorageKey& lhs, const StorageKey& rhs) { - return !(lhs == rhs); -} - -bool operator<(const StorageKey& lhs, const StorageKey& rhs) { - return std::tie(lhs.origin_, lhs.top_level_site_, lhs.nonce_, - lhs.ancestor_chain_bit_) < - std::tie(rhs.origin_, rhs.top_level_site_, rhs.nonce_, - rhs.ancestor_chain_bit_); -} - std::ostream& operator<<(std::ostream& ostream, const StorageKey& sk) { return ostream << sk.GetDebugString(); }
diff --git a/third_party/blink/public/common/features.h b/third_party/blink/public/common/features.h index 0286036..e6e8269 100644 --- a/third_party/blink/public/common/features.h +++ b/third_party/blink/public/common/features.h
@@ -640,6 +640,12 @@ // of https://crbug.com/1441243. BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(kHiddenSelectionBounds); +// When enabled all input arriving will be ignored, and the dispatcher will be +// notified that the event was not consumed. With the exception of when there +// is an attached Dev Tools session, during which input will be dispatched even +// if we are hidden. +BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(kIgnoreInputWhileHidden); + // If enabled, a fix for image loading prioritization based on visibility is // applied. See https://crbug.com/1369823. BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(kImageLoadingPrioritizationFix);
diff --git a/third_party/blink/public/common/storage_key/storage_key.h b/third_party/blink/public/common/storage_key/storage_key.h index 6de0d5f..aeb3a37 100644 --- a/third_party/blink/public/common/storage_key/storage_key.h +++ b/third_party/blink/public/common/storage_key/storage_key.h
@@ -9,6 +9,7 @@ #include <optional> #include <string> #include <string_view> +#include <tuple> #include "base/unguessable_token.h" #include "net/base/isolation_info.h" @@ -297,12 +298,18 @@ // (7B) Operators. // Note that not all must be friends, but all are to consolidate the header. - BLINK_COMMON_EXPORT - friend bool operator==(const StorageKey& lhs, const StorageKey& rhs); - BLINK_COMMON_EXPORT - friend bool operator!=(const StorageKey& lhs, const StorageKey& rhs); - BLINK_COMMON_EXPORT - friend bool operator<(const StorageKey& lhs, const StorageKey& rhs); + friend bool operator==(const StorageKey& lhs, const StorageKey& rhs) { + return std::tie(lhs.origin_, lhs.top_level_site_, lhs.nonce_, + lhs.ancestor_chain_bit_) == + std::tie(rhs.origin_, rhs.top_level_site_, rhs.nonce_, + rhs.ancestor_chain_bit_); + } + friend auto operator<=>(const StorageKey& lhs, const StorageKey& rhs) { + return std::tie(lhs.origin_, lhs.top_level_site_, lhs.nonce_, + lhs.ancestor_chain_bit_) <=> + std::tie(rhs.origin_, rhs.top_level_site_, rhs.nonce_, + rhs.ancestor_chain_bit_); + } BLINK_COMMON_EXPORT friend std::ostream& operator<<(std::ostream& ostream, const StorageKey& sk);
diff --git a/third_party/blink/public/mojom/use_counter/metrics/css_property_id.mojom b/third_party/blink/public/mojom/use_counter/metrics/css_property_id.mojom index 943fad0..44033ca 100644 --- a/third_party/blink/public/mojom/use_counter/metrics/css_property_id.mojom +++ b/third_party/blink/public/mojom/use_counter/metrics/css_property_id.mojom
@@ -871,6 +871,7 @@ kMasonryFill = 812, kMasonryDirection = 813, kMasonryFlow = 814, + kMasonryAutoTracks = 815, // 1. Add new features above this line (don't change the assigned numbers of // the existing items).
diff --git a/third_party/blink/renderer/core/css/css_properties.json5 b/third_party/blink/renderer/core/css/css_properties.json5 index 391a5c3..ae9b152 100644 --- a/third_party/blink/renderer/core/css/css_properties.json5 +++ b/third_party/blink/renderer/core/css/css_properties.json5
@@ -3807,6 +3807,21 @@ invalidate: ["paint"], }, { + name: "masonry-auto-tracks", + property_methods: ["ParseSingleValue", "CSSValueFromComputedStyleInternal"], + field_group: "*", + field_template: "external", + include_paths: ["third_party/blink/renderer/core/style/grid_track_list.h"], + default_value: "NGGridTrackList(GridTrackSize(Length::Auto()))", + type_name: "NGGridTrackList", + converter: "ConvertGridTrackSizeList", + keywords: ["auto", "min-content", "max-content"], + typedom_types: ["Keyword", "Length", "Percentage", "Flex"], + separator: " ", + invalidate: ["layout", "paint"], + runtime_flag: "CSSMasonryLayout", + }, + { name: "masonry-direction", property_methods: ["CSSValueFromComputedStyleInternal"], field_group: "*",
diff --git a/third_party/blink/renderer/core/css/css_property_equality.cc b/third_party/blink/renderer/core/css/css_property_equality.cc index afaceac..602bf07 100644 --- a/third_party/blink/renderer/core/css/css_property_equality.cc +++ b/third_party/blink/renderer/core/css/css_property_equality.cc
@@ -517,6 +517,8 @@ return a.MarkerStartResource() == b.MarkerStartResource(); case CSSPropertyID::kMaskType: return a.MaskType() == b.MaskType(); + case CSSPropertyID::kMasonryAutoTracks: + return a.MasonryAutoTracks() == b.MasonryAutoTracks(); case CSSPropertyID::kMasonryDirection: return a.MasonryDirection() == b.MasonryDirection(); case CSSPropertyID::kMasonryFill:
diff --git a/third_party/blink/renderer/core/css/css_value_keywords.json5 b/third_party/blink/renderer/core/css/css_value_keywords.json5 index 9add48d..a5dc42d 100644 --- a/third_party/blink/renderer/core/css/css_value_keywords.json5 +++ b/third_party/blink/renderer/core/css/css_value_keywords.json5
@@ -1236,6 +1236,11 @@ // luminance "match-source", + // masonry-auto-tracks + // min-content + // max-content + // auto + // masonry-direction // row // row-reverse
diff --git a/third_party/blink/renderer/core/css/properties/computed_style_utils.cc b/third_party/blink/renderer/core/css/properties/computed_style_utils.cc index 9b3a4b7..f3a70df 100644 --- a/third_party/blink/renderer/core/css/properties/computed_style_utils.cc +++ b/third_party/blink/renderer/core/css/properties/computed_style_utils.cc
@@ -1819,13 +1819,11 @@ } CSSValue* ComputedStyleUtils::ValueForGridAutoTrackList( - GridTrackSizingDirection track_direction, + const NGGridTrackList& auto_track_list, const LayoutObject* layout_object, const ComputedStyle& style) { CSSValueList* list = CSSValueList::CreateSpaceSeparated(); - const NGGridTrackList& auto_track_list = track_direction == kForColumns - ? style.GridAutoColumns() - : style.GridAutoRows(); + if (auto_track_list.RepeaterCount() == 1) { for (wtf_size_t i = 0; i < auto_track_list.RepeatSize(0); ++i) { list->Append(*SpecifiedValueForGridTrackSize(
diff --git a/third_party/blink/renderer/core/css/properties/computed_style_utils.h b/third_party/blink/renderer/core/css/properties/computed_style_utils.h index dd9ca2ba..afbabfc 100644 --- a/third_party/blink/renderer/core/css/properties/computed_style_utils.h +++ b/third_party/blink/renderer/core/css/properties/computed_style_utils.h
@@ -151,7 +151,7 @@ static CSSValue* ValueForFontPalette(const ComputedStyle&); static CSSValue* SpecifiedValueForGridTrackSize(const GridTrackSize&, const ComputedStyle&); - static CSSValue* ValueForGridAutoTrackList(GridTrackSizingDirection, + static CSSValue* ValueForGridAutoTrackList(const NGGridTrackList&, const LayoutObject*, const ComputedStyle&); static CSSValue* ValueForGridTrackList(GridTrackSizingDirection,
diff --git a/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc b/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc index e6c2c0b..75221109 100644 --- a/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc +++ b/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc
@@ -4342,7 +4342,7 @@ const LayoutObject* layout_object, bool allow_visited_style, CSSValuePhase value_phase) const { - return ComputedStyleUtils::ValueForGridAutoTrackList(kForColumns, + return ComputedStyleUtils::ValueForGridAutoTrackList(style.GridAutoColumns(), layout_object, style); } @@ -4430,8 +4430,8 @@ const LayoutObject* layout_object, bool allow_visited_style, CSSValuePhase value_phase) const { - return ComputedStyleUtils::ValueForGridAutoTrackList(kForRows, layout_object, - style); + return ComputedStyleUtils::ValueForGridAutoTrackList(style.GridAutoRows(), + layout_object, style); } const CSSValue* GridAutoRows::InitialValue() const { @@ -6100,6 +6100,23 @@ return CSSIdentifierValue::Create(style.MaskType()); } +const CSSValue* MasonryAutoTracks::ParseSingleValue( + CSSParserTokenStream& stream, + const CSSParserContext& context, + const CSSParserLocalContext&) const { + return css_parsing_utils::ConsumeGridTrackList( + stream, context, css_parsing_utils::TrackListType::kGridAuto); +} + +const CSSValue* MasonryAutoTracks::CSSValueFromComputedStyleInternal( + const ComputedStyle& style, + const LayoutObject* layout_object, + bool allow_visited_style, + CSSValuePhase value_phase) const { + return ComputedStyleUtils::ValueForGridAutoTrackList( + style.MasonryAutoTracks(), layout_object, style); +} + const CSSValue* MasonryDirection::CSSValueFromComputedStyleInternal( const ComputedStyle& style, const LayoutObject*,
diff --git a/third_party/blink/renderer/core/exported/web_dev_tools_agent_impl.cc b/third_party/blink/renderer/core/exported/web_dev_tools_agent_impl.cc index 404be4b..d9a73c0 100644 --- a/third_party/blink/renderer/core/exported/web_dev_tools_agent_impl.cc +++ b/third_party/blink/renderer/core/exported/web_dev_tools_agent_impl.cc
@@ -306,8 +306,11 @@ void WebDevToolsAgentImpl::AttachSession(DevToolsSession* session, bool restore) { - if (!network_agents_.size()) + if (!network_agents_.size()) { Thread::Current()->AddTaskObserver(this); + web_local_frame_impl_->OnDevToolsSessionConnectionChanged( + /*attached=*/true); + } InspectedFrames* inspected_frames = inspected_frames_.Get(); v8::Isolate* isolate = @@ -460,8 +463,11 @@ network_agents_.erase(session); page_agents_.erase(session); overlay_agents_.erase(session); - if (!network_agents_.size()) + if (!network_agents_.size()) { Thread::Current()->RemoveTaskObserver(this); + web_local_frame_impl_->OnDevToolsSessionConnectionChanged( + /*attached=*/false); + } } void WebDevToolsAgentImpl::InspectElement(
diff --git a/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc b/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc index 1f39c8d..b0c4780 100644 --- a/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc +++ b/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc
@@ -1564,6 +1564,10 @@ LayerTreeHost()->RequestNewLocalSurfaceId(); } +void WebFrameWidgetImpl::OnDevToolsSessionConnectionChanged(bool attached) { + widget_base_->OnDevToolsSessionConnectionChanged(attached); +} + WebInputMethodController* WebFrameWidgetImpl::GetActiveWebInputMethodController() const { WebLocalFrameImpl* local_frame =
diff --git a/third_party/blink/renderer/core/frame/web_frame_widget_impl.h b/third_party/blink/renderer/core/frame/web_frame_widget_impl.h index 07fa5e65..c9a9553 100644 --- a/third_party/blink/renderer/core/frame/web_frame_widget_impl.h +++ b/third_party/blink/renderer/core/frame/web_frame_widget_impl.h
@@ -740,6 +740,8 @@ // Request a new `viz::LocalSurfaceId` on the compositor thread. void RequestNewLocalSurfaceId(); + void OnDevToolsSessionConnectionChanged(bool attached); + protected: // WidgetBaseClient overrides: void ScheduleAnimation() override;
diff --git a/third_party/blink/renderer/core/frame/web_local_frame_impl.cc b/third_party/blink/renderer/core/frame/web_local_frame_impl.cc index d2f653fbf..82bc280 100644 --- a/third_party/blink/renderer/core/frame/web_local_frame_impl.cc +++ b/third_party/blink/renderer/core/frame/web_local_frame_impl.cc
@@ -3204,6 +3204,12 @@ return dev_tools_agent_.Get(); } +void WebLocalFrameImpl::OnDevToolsSessionConnectionChanged(bool attached) { + if (frame_widget_) { + frame_widget_->OnDevToolsSessionConnectionChanged(attached); + } +} + void WebLocalFrameImpl::WasHidden() { if (frame_) frame_->WasHidden();
diff --git a/third_party/blink/renderer/core/frame/web_local_frame_impl.h b/third_party/blink/renderer/core/frame/web_local_frame_impl.h index 237de99..cbbfad6 100644 --- a/third_party/blink/renderer/core/frame/web_local_frame_impl.h +++ b/third_party/blink/renderer/core/frame/web_local_frame_impl.h
@@ -505,6 +505,7 @@ void SendOrientationChangeEvent(); WebDevToolsAgentImpl* DevToolsAgentImpl(bool create_if_necessary); + void OnDevToolsSessionConnectionChanged(bool attached); // Instructs devtools to pause loading of the frame as soon as it's shown // until explicit command from the devtools client. May only be called on a
diff --git a/third_party/blink/renderer/core/html/forms/html_select_element.cc b/third_party/blink/renderer/core/html/forms/html_select_element.cc index bcebda6..0a74f45 100644 --- a/third_party/blink/renderer/core/html/forms/html_select_element.cc +++ b/third_party/blink/renderer/core/html/forms/html_select_element.cc
@@ -730,7 +730,7 @@ // FIXME: ignore for the moment. // } else if (params.name == html_names::kSelectedcontentelementAttr) { - if (RuntimeEnabledFeatures::CustomizableSelectEnabled()) { + if (RuntimeEnabledFeatures::SelectedcontentelementAttributeEnabled()) { HTMLSelectedContentElement* old_selectedcontent = DynamicTo<HTMLSelectedContentElement>( getElementByIdIncludingDisconnected(*this, params.old_value));
diff --git a/third_party/blink/renderer/core/layout/layout_theme.cc b/third_party/blink/renderer/core/layout/layout_theme.cc index c663a9b..5cd36bf 100644 --- a/third_party/blink/renderer/core/layout/layout_theme.cc +++ b/third_party/blink/renderer/core/layout/layout_theme.cc
@@ -441,8 +441,7 @@ return builder.HasAuthorBackground() || builder.HasAuthorBorder(); case AppearanceValue::kMeter: - return RuntimeEnabledFeatures::MeterDevolveAppearanceEnabled() && - (builder.HasAuthorBackground() || builder.HasAuthorBorder()); + return builder.HasAuthorBackground() || builder.HasAuthorBorder(); case AppearanceValue::kMenulist: case AppearanceValue::kSearchField:
diff --git a/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.idl b/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.idl index d405b56..259d8821 100644 --- a/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.idl +++ b/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.idl
@@ -13,7 +13,7 @@ interface mixin BaseRenderingContext2D { // state - void save(); // push state on state stack + [NoAllocDirectCall] void save(); // push state on state stack [NoAllocDirectCall, RaisesException] void restore(); // pop state stack if top state was pushed by save, and restore state [MeasureAs=Canvas2DLayers, RuntimeEnabled=Canvas2dLayers, CallWith=ScriptState] void beginLayer(); // push state on state stack and creates bitmap for subsequent draw ops [MeasureAs=Canvas2DLayers, RuntimeEnabled=Canvas2dLayersWithOptions, CallWith=ScriptState, RaisesException] void beginLayer(BeginLayerOptions options); // push state on state stack and creates bitmap for subsequent draw ops
diff --git a/third_party/blink/renderer/modules/csspaint/paint_rendering_context_2d.idl b/third_party/blink/renderer/modules/csspaint/paint_rendering_context_2d.idl index 1f2692fb..2c3044b 100644 --- a/third_party/blink/renderer/modules/csspaint/paint_rendering_context_2d.idl +++ b/third_party/blink/renderer/modules/csspaint/paint_rendering_context_2d.idl
@@ -8,7 +8,7 @@ Exposed=PaintWorklet ] interface PaintRenderingContext2D { // state - void save(); // push state on state stack + [NoAllocDirectCall] void save(); // push state on state stack [NoAllocDirectCall, RaisesException] void restore(); // pop state stack if top state was pushed by save, and restore state [MeasureAs=Canvas2DLayers, RuntimeEnabled=Canvas2dLayers, CallWith=ScriptState] void beginLayer(); // push state on state stack and creates bitmap for subsequent draw ops [MeasureAs=Canvas2DLayers, RuntimeEnabled=Canvas2dLayersWithOptions, CallWith=ScriptState, RaisesException] void beginLayer(BeginLayerOptions options); // push state on state stack and creates bitmap for subsequent draw ops
diff --git a/third_party/blink/renderer/platform/BUILD.gn b/third_party/blink/renderer/platform/BUILD.gn index 605c8108..d63c8ec 100644 --- a/third_party/blink/renderer/platform/BUILD.gn +++ b/third_party/blink/renderer/platform/BUILD.gn
@@ -2026,6 +2026,7 @@ "testing/video_frame_utils.cc", "testing/video_frame_utils.h", "webrtc/testing/mock_webrtc_video_frame_adapter_shared_resources.h", + "widget/compositing/test/stub_widget_base_client.h", ] # fuzzed_data_provider may not work with a custom toolchain. @@ -2364,8 +2365,11 @@ "widget/input/input_event_prediction_unittest.cc", "widget/input/input_handler_proxy_unittest.cc", "widget/input/main_thread_event_queue_unittest.cc", + "widget/input/mock_input_handler_proxy.h", + "widget/input/mock_input_handler_proxy_client.h", "widget/input/prediction/filter_factory_unittests.cc", "widget/input/scroll_predictor_unittest.cc", + "widget/input/widget_input_handler_manager_unittest.cc", ] if (rtc_use_h265) {
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5 index b838d68..0d0ac60 100644 --- a/third_party/blink/renderer/platform/runtime_enabled_features.json5 +++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -2781,12 +2781,6 @@ name: "MetaRefreshNoFractional", status: "stable", }, - { - // If enabled, the <meter> widget appearance will devolve based on user - // declared styles as defined in the css-ui-4 spec - name: "MeterDevolveAppearance", - status: "stable", - }, // This is enabled by default on Windows only. The only part that's // "experimental" is the support on other platforms. {
diff --git a/third_party/blink/renderer/platform/storage/blink_storage_key.cc b/third_party/blink/renderer/platform/storage/blink_storage_key.cc index 61c03dc9..37af1da5 100644 --- a/third_party/blink/renderer/platform/storage/blink_storage_key.cc +++ b/third_party/blink/renderer/platform/storage/blink_storage_key.cc
@@ -203,20 +203,6 @@ other.top_level_site_if_third_party_enabled_; } -bool operator==(const BlinkStorageKey& lhs, const BlinkStorageKey& rhs) { - DCHECK(lhs.origin_); - DCHECK(rhs.origin_); - - return lhs.origin_->IsSameOriginWith(rhs.origin_.get()) && - lhs.nonce_ == rhs.nonce_ && - lhs.top_level_site_ == rhs.top_level_site_ && - lhs.ancestor_chain_bit_ == rhs.ancestor_chain_bit_; -} - -bool operator!=(const BlinkStorageKey& lhs, const BlinkStorageKey& rhs) { - return !(lhs == rhs); -} - std::ostream& operator<<(std::ostream& ostream, const BlinkStorageKey& key) { return ostream << key.ToDebugString(); }
diff --git a/third_party/blink/renderer/platform/storage/blink_storage_key.h b/third_party/blink/renderer/platform/storage/blink_storage_key.h index c20099c..1f1c566 100644 --- a/third_party/blink/renderer/platform/storage/blink_storage_key.h +++ b/third_party/blink/renderer/platform/storage/blink_storage_key.h
@@ -179,13 +179,17 @@ // (7B) Operators. // Note that not all must be friends, but all are to consolidate the header. - PLATFORM_EXPORT friend bool operator==(const BlinkStorageKey& lhs, - const BlinkStorageKey& rhs); - PLATFORM_EXPORT - friend bool operator!=(const BlinkStorageKey& lhs, - const BlinkStorageKey& rhs); - // If there were a need for an operator< it would go here. + const BlinkStorageKey& rhs) { + DCHECK(lhs.origin_); + DCHECK(rhs.origin_); + + return lhs.origin_->IsSameOriginWith(rhs.origin_.get()) && + lhs.nonce_ == rhs.nonce_ && + lhs.top_level_site_ == rhs.top_level_site_ && + lhs.ancestor_chain_bit_ == rhs.ancestor_chain_bit_; + } + // If there were a need for `operator<=>()` it would go here. PLATFORM_EXPORT friend std::ostream& operator<<(std::ostream& ostream, const BlinkStorageKey& sk);
diff --git a/third_party/blink/renderer/platform/widget/compositing/test/stub_widget_base_client.h b/third_party/blink/renderer/platform/widget/compositing/test/stub_widget_base_client.h new file mode 100644 index 0000000..5e2ef18 --- /dev/null +++ b/third_party/blink/renderer/platform/widget/compositing/test/stub_widget_base_client.h
@@ -0,0 +1,61 @@ +// Copyright 2024 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_PLATFORM_WIDGET_COMPOSITING_TEST_STUB_WIDGET_BASE_CLIENT_H_ +#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WIDGET_COMPOSITING_TEST_STUB_WIDGET_BASE_CLIENT_H_ + +#include "cc/input/overscroll_behavior.h" +#include "cc/trees/layer_tree_frame_sink.h" +#include "components/viz/common/frame_sinks/begin_frame_args.h" +#include "third_party/blink/public/common/input/web_gesture_event.h" +#include "third_party/blink/public/common/input/web_mouse_event.h" +#include "third_party/blink/public/common/widget/visual_properties.h" +#include "third_party/blink/public/mojom/input/input_handler.mojom-blink.h" +#include "third_party/blink/public/platform/web_input_event_result.h" +#include "third_party/blink/public/web/web_lifecycle_update.h" +#include "third_party/blink/renderer/platform/weborigin/kurl.h" +#include "third_party/blink/renderer/platform/widget/widget_base_client.h" +#include "ui/display/screen_infos.h" +#include "ui/gfx/geometry/rect.h" +#include "ui/gfx/geometry/vector2d_f.h" + +namespace blink { +class StubWidgetBaseClient : public WidgetBaseClient { + public: + void OnCommitRequested() override {} + void BeginMainFrame(const viz::BeginFrameArgs& args) override {} + void UpdateLifecycle(WebLifecycleUpdate, DocumentUpdateReason) override {} + std::unique_ptr<cc::LayerTreeFrameSink> AllocateNewLayerTreeFrameSink() + override { + return nullptr; + } + KURL GetURLForDebugTrace() override { return {}; } + WebInputEventResult DispatchBufferedTouchEvents() override { + return WebInputEventResult::kNotHandled; + } + WebInputEventResult HandleInputEvent(const WebCoalescedInputEvent&) override { + return WebInputEventResult::kNotHandled; + } + bool SupportsBufferedTouchEvents() override { return false; } + void WillHandleGestureEvent(const WebGestureEvent&, bool* suppress) override { + } + void WillHandleMouseEvent(const WebMouseEvent&) override {} + void ObserveGestureEventAndResult(const WebGestureEvent&, + const gfx::Vector2dF&, + const cc::OverscrollBehavior&, + bool) override {} + void FocusChanged(mojom::blink::FocusState) override {} + void UpdateVisualProperties( + const VisualProperties& visual_properties) override {} + const display::ScreenInfos& GetOriginalScreenInfos() override { + return screen_infos_; + } + gfx::Rect ViewportVisibleRect() override { return gfx::Rect(); } + + private: + display::ScreenInfos screen_infos_; +}; +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WIDGET_COMPOSITING_TEST_STUB_WIDGET_BASE_CLIENT_H_
diff --git a/third_party/blink/renderer/platform/widget/compositing/widget_compositor_unittest.cc b/third_party/blink/renderer/platform/widget/compositing/widget_compositor_unittest.cc index 8916fdf..2503b04d 100644 --- a/third_party/blink/renderer/platform/widget/compositing/widget_compositor_unittest.cc +++ b/third_party/blink/renderer/platform/widget/compositing/widget_compositor_unittest.cc
@@ -14,48 +14,13 @@ #include "cc/trees/layer_tree_host.h" #include "mojo/public/cpp/bindings/remote.h" #include "third_party/blink/public/platform/scheduler/test/renderer_scheduler_test_support.h" +#include "third_party/blink/renderer/platform/widget/compositing/test/stub_widget_base_client.h" #include "third_party/blink/renderer/platform/widget/widget_base.h" #include "third_party/blink/renderer/platform/widget/widget_base_client.h" #include "third_party/blink/renderer/platform/wtf/thread_safe_ref_counted.h" namespace blink { -class StubWidgetBaseClient : public WidgetBaseClient { - public: - void OnCommitRequested() override {} - void BeginMainFrame(const viz::BeginFrameArgs& args) override {} - void UpdateLifecycle(WebLifecycleUpdate, DocumentUpdateReason) override {} - std::unique_ptr<cc::LayerTreeFrameSink> AllocateNewLayerTreeFrameSink() - override { - return nullptr; - } - KURL GetURLForDebugTrace() override { return {}; } - WebInputEventResult DispatchBufferedTouchEvents() override { - return WebInputEventResult::kNotHandled; - } - WebInputEventResult HandleInputEvent(const WebCoalescedInputEvent&) override { - return WebInputEventResult::kNotHandled; - } - bool SupportsBufferedTouchEvents() override { return false; } - void WillHandleGestureEvent(const WebGestureEvent&, bool* suppress) override { - } - void WillHandleMouseEvent(const WebMouseEvent&) override {} - void ObserveGestureEventAndResult(const WebGestureEvent&, - const gfx::Vector2dF&, - const cc::OverscrollBehavior&, - bool) override {} - void FocusChanged(mojom::blink::FocusState) override {} - void UpdateVisualProperties( - const VisualProperties& visual_properties) override {} - const display::ScreenInfos& GetOriginalScreenInfos() override { - return screen_infos_; - } - gfx::Rect ViewportVisibleRect() override { return gfx::Rect(); } - - private: - display::ScreenInfos screen_infos_; -}; - class FakeWidgetCompositor : public WidgetCompositor { public: static scoped_refptr<FakeWidgetCompositor> Create(
diff --git a/third_party/blink/renderer/platform/widget/input/input_handler_proxy.h b/third_party/blink/renderer/platform/widget/input/input_handler_proxy.h index 7f29233..5f63586 100644 --- a/third_party/blink/renderer/platform/widget/input/input_handler_proxy.h +++ b/third_party/blink/renderer/platform/widget/input/input_handler_proxy.h
@@ -154,7 +154,8 @@ std::unique_ptr<DidOverscrollParams>, const blink::WebInputEventAttribution&, std::unique_ptr<cc::EventMetrics> metrics)>; - void HandleInputEventWithLatencyInfo( + // Virtual for mocking in tests. + virtual void HandleInputEventWithLatencyInfo( std::unique_ptr<blink::WebCoalescedInputEvent> event, std::unique_ptr<cc::EventMetrics> metrics, EventDispositionCallback callback);
diff --git a/third_party/blink/renderer/platform/widget/input/input_handler_proxy_unittest.cc b/third_party/blink/renderer/platform/widget/input/input_handler_proxy_unittest.cc index 7fdda67..69e0414 100644 --- a/third_party/blink/renderer/platform/widget/input/input_handler_proxy_unittest.cc +++ b/third_party/blink/renderer/platform/widget/input/input_handler_proxy_unittest.cc
@@ -9,7 +9,6 @@ #include "base/containers/circular_deque.h" #include "base/functional/bind.h" #include "base/functional/callback_helpers.h" -#include "base/lazy_instance.h" #include "base/test/bind.h" #include "base/test/metrics/histogram_tester.h" #include "base/test/scoped_feature_list.h" @@ -19,13 +18,8 @@ #include "base/types/optional_ref.h" #include "build/build_config.h" #include "cc/base/features.h" -#include "cc/input/browser_controls_offset_tags_info.h" #include "cc/input/main_thread_scrolling_reason.h" -#include "cc/test/fake_impl_task_runner_provider.h" -#include "cc/test/fake_layer_tree_host_impl.h" -#include "cc/test/test_task_graph_runner.h" -#include "cc/trees/latency_info_swap_promise_monitor.h" -#include "cc/trees/layer_tree_settings.h" +#include "cc/test/mock_input_handler.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/blink/public/common/input/web_input_event.h" @@ -39,6 +33,7 @@ #include "third_party/blink/renderer/platform/widget/input/event_with_callback.h" #include "third_party/blink/renderer/platform/widget/input/input_handler_proxy.h" #include "third_party/blink/renderer/platform/widget/input/input_handler_proxy_client.h" +#include "third_party/blink/renderer/platform/widget/input/mock_input_handler_proxy_client.h" #include "third_party/blink/renderer/platform/widget/input/scroll_predictor.h" #include "ui/events/types/scroll_input_type.h" #include "ui/gfx/geometry/size_f.h" @@ -87,179 +82,6 @@ return gesture; } -class FakeCompositorDelegateForInput : public cc::CompositorDelegateForInput { - public: - FakeCompositorDelegateForInput() - : host_impl_(&task_runner_provider_, &task_graph_runner_) {} - void BindToInputHandler( - std::unique_ptr<cc::InputDelegateForCompositor> delegate) override {} - cc::ScrollTree& GetScrollTree() const override { return scroll_tree_; } - bool HasAnimatedScrollbars() const override { return false; } - void SetNeedsCommit() override {} - void SetNeedsFullViewportRedraw() override {} - void SetDeferBeginMainFrame(bool defer_begin_main_frame) const override {} - void DidUpdateScrollAnimationCurve() override {} - void AccumulateScrollDeltaForTracing(const gfx::Vector2dF& delta) override {} - void DidStartPinchZoom() override {} - void DidUpdatePinchZoom() override {} - void DidEndPinchZoom() override {} - void DidStartScroll() override {} - void DidEndScroll() override {} - void DidMouseLeave() override {} - bool IsInHighLatencyMode() const override { return false; } - void WillScrollContent(cc::ElementId element_id) override {} - void DidScrollContent(cc::ElementId element_id, bool animated) override {} - float DeviceScaleFactor() const override { return 0; } - float PageScaleFactor() const override { return 0; } - gfx::Size VisualDeviceViewportSize() const override { return gfx::Size(); } - const cc::LayerTreeSettings& GetSettings() const override { - return settings_; - } - cc::LayerTreeHostImpl& GetImplDeprecated() override { return host_impl_; } - const cc::LayerTreeHostImpl& GetImplDeprecated() const override { - return host_impl_; - } - void UpdateBrowserControlsState( - cc::BrowserControlsState constraints, - cc::BrowserControlsState current, - bool animate, - base::optional_ref<const cc::BrowserControlsOffsetTagsInfo> - offset_tags_info) override {} - bool HasScrollLinkedAnimation(cc::ElementId for_scroller) const override { - return false; - } - - private: - mutable cc::ScrollTree scroll_tree_; - cc::LayerTreeSettings settings_; - cc::FakeImplTaskRunnerProvider task_runner_provider_; - cc::TestTaskGraphRunner task_graph_runner_; - cc::FakeLayerTreeHostImpl host_impl_; -}; - -base::LazyInstance<FakeCompositorDelegateForInput>::Leaky - g_fake_compositor_delegate = LAZY_INSTANCE_INITIALIZER; - -class MockInputHandler : public cc::InputHandler { - public: - MockInputHandler() : cc::InputHandler(g_fake_compositor_delegate.Get()) {} - MockInputHandler(const MockInputHandler&) = delete; - MockInputHandler& operator=(const MockInputHandler&) = delete; - - ~MockInputHandler() override = default; - - base::WeakPtr<InputHandler> AsWeakPtr() override { - return weak_ptr_factory_.GetWeakPtr(); - } - - MOCK_METHOD2(PinchGestureBegin, - void(const gfx::Point& anchor, ui::ScrollInputType type)); - MOCK_METHOD2(PinchGestureUpdate, - void(float magnify_delta, const gfx::Point& anchor)); - MOCK_METHOD1(PinchGestureEnd, void(const gfx::Point& anchor)); - - MOCK_METHOD0(SetNeedsAnimateInput, void()); - - MOCK_METHOD2(ScrollBegin, - ScrollStatus(cc::ScrollState*, ui::ScrollInputType type)); - MOCK_METHOD2(RootScrollBegin, - ScrollStatus(cc::ScrollState*, ui::ScrollInputType type)); - MOCK_METHOD2(ScrollUpdate, - cc::InputHandlerScrollResult(cc::ScrollState, base::TimeDelta)); - MOCK_METHOD1(ScrollEnd, void(bool)); - MOCK_METHOD2(RecordScrollBegin, - void(ui::ScrollInputType type, - cc::ScrollBeginThreadState state)); - MOCK_METHOD1(RecordScrollEnd, void(ui::ScrollInputType type)); - MOCK_METHOD1(HitTest, - cc::PointerResultType(const gfx::PointF& mouse_position)); - MOCK_METHOD2(MouseDown, - cc::InputHandlerPointerResult(const gfx::PointF& mouse_position, - const bool shift_modifier)); - MOCK_METHOD1( - MouseUp, - cc::InputHandlerPointerResult(const gfx::PointF& mouse_position)); - MOCK_METHOD1(SetIsHandlingTouchSequence, void(bool)); - void NotifyInputEvent() override {} - - std::unique_ptr<cc::LatencyInfoSwapPromiseMonitor> - CreateLatencyInfoSwapPromiseMonitor(ui::LatencyInfo* latency) override { - return nullptr; - } - - std::unique_ptr<cc::EventsMetricsManager::ScopedMonitor> - GetScopedEventMetricsMonitor( - cc::EventsMetricsManager::ScopedMonitor::DoneCallback) override { - return nullptr; - } - - cc::ScrollElasticityHelper* CreateScrollElasticityHelper() override { - return nullptr; - } - void DestroyScrollElasticityHelper() override {} - - bool GetScrollOffsetForLayer(cc::ElementId element_id, - gfx::PointF* offset) override { - return false; - } - bool ScrollLayerTo(cc::ElementId element_id, - const gfx::PointF& offset) override { - return false; - } - - void BindToClient(cc::InputHandlerClient* client) override {} - - void MouseLeave() override {} - - MOCK_METHOD1(FindFrameElementIdAtPoint, cc::ElementId(const gfx::PointF&)); - - cc::InputHandlerPointerResult MouseMoveAt( - const gfx::Point& mouse_position) override { - return cc::InputHandlerPointerResult(); - } - - MOCK_CONST_METHOD1( - GetEventListenerProperties, - cc::EventListenerProperties(cc::EventListenerClass event_class)); - MOCK_METHOD2(EventListenerTypeForTouchStartOrMoveAt, - cc::InputHandler::TouchStartOrMoveEventListenerType( - const gfx::Point& point, - cc::TouchAction* touch_action)); - MOCK_CONST_METHOD1(HasBlockingWheelEventHandlerAt, bool(const gfx::Point&)); - - MOCK_METHOD0(RequestUpdateForSynchronousInputHandler, void()); - MOCK_METHOD1(SetSynchronousInputHandlerRootScrollOffset, - void(const gfx::PointF& root_offset)); - - bool IsCurrentlyScrollingViewport() const override { - return is_scrolling_root_; - } - void set_is_scrolling_root(bool is) { is_scrolling_root_ = is; } - - MOCK_METHOD4(GetSnapFlingInfoAndSetAnimatingSnapTarget, - bool(const gfx::Vector2dF& current_delta, - const gfx::Vector2dF& natural_displacement, - gfx::PointF* initial_offset, - gfx::PointF* target_offset)); - MOCK_METHOD1(ScrollEndForSnapFling, void(bool)); - - bool ScrollbarScrollIsActive() override { return false; } - - void SetDeferBeginMainFrame(bool defer_begin_main_frame) const override {} - - MOCK_METHOD4(UpdateBrowserControlsState, - void(cc::BrowserControlsState constraints, - cc::BrowserControlsState current, - bool animate, - base::optional_ref<const cc::BrowserControlsOffsetTagsInfo> - offset_tags_info)); - - private: - bool is_scrolling_root_ = true; - - base::WeakPtrFactory<MockInputHandler> weak_ptr_factory_{this}; -}; - class MockSynchronousInputHandler : public SynchronousInputHandler { public: MOCK_METHOD6(UpdateRootLayerState, @@ -271,33 +93,6 @@ float max_page_scale_factor)); }; -class MockInputHandlerProxyClient : public InputHandlerProxyClient { - public: - MockInputHandlerProxyClient() {} - MockInputHandlerProxyClient(const MockInputHandlerProxyClient&) = delete; - MockInputHandlerProxyClient& operator=(const MockInputHandlerProxyClient&) = - delete; - - ~MockInputHandlerProxyClient() override {} - - void WillShutdown() override {} - - MOCK_METHOD3(GenerateScrollBeginAndSendToMainThread, - void(const WebGestureEvent& update_event, - const WebInputEventAttribution&, - const cc::EventMetrics*)); - - MOCK_METHOD5(DidOverscroll, - void(const gfx::Vector2dF& accumulated_overscroll, - const gfx::Vector2dF& latest_overscroll_delta, - const gfx::Vector2dF& current_fling_velocity, - const gfx::PointF& causal_event_viewport_point, - const cc::OverscrollBehavior& overscroll_behavior)); - void DidStartScrollingViewport() override {} - MOCK_METHOD1(SetAllowedTouchAction, void(cc::TouchAction touch_action)); - bool AllowsScrollResampling() override { return true; } -}; - WebTouchPoint CreateWebTouchPoint(WebTouchPoint::State state, float x, float y) { @@ -425,7 +220,7 @@ void FlingAndSnap(); base::test::SingleThreadTaskEnvironment task_environment_; - testing::StrictMock<MockInputHandler> mock_input_handler_; + testing::StrictMock<cc::MockInputHandler> mock_input_handler_; testing::StrictMock<MockSynchronousInputHandler> mock_synchronous_input_handler_; std::unique_ptr<TestInputHandlerProxy> input_handler_; @@ -468,7 +263,7 @@ // This helper forces the CompositorThreadEventQueue to be flushed. InputHandlerProxy::EventDisposition HandleInputEventAndFlushEventQueue( - testing::StrictMock<MockInputHandler>& mock_input_handler, + testing::StrictMock<cc::MockInputHandler>& mock_input_handler, TestInputHandlerProxy* input_handler, const WebInputEvent& event) { EXPECT_CALL(mock_input_handler, SetNeedsAnimateInput()) @@ -597,7 +392,7 @@ protected: base::test::SingleThreadTaskEnvironment task_environment_; - testing::StrictMock<MockInputHandler> mock_input_handler_; + testing::StrictMock<cc::MockInputHandler> mock_input_handler_; testing::StrictMock<MockInputHandlerProxyClient> mock_client_; TestInputHandlerProxy input_handler_proxy_; std::vector<InputHandlerProxy::EventDisposition> event_disposition_recorder_; @@ -1900,7 +1695,7 @@ protected: base::test::SingleThreadTaskEnvironment task_environment_; - NiceMock<MockInputHandler> mock_input_handler_; + NiceMock<cc::MockInputHandler> mock_input_handler_; NiceMock<MockInputHandlerProxyClient> mock_client_; private: @@ -2291,7 +2086,7 @@ TEST(SynchronousInputHandlerProxyTest, StartupShutdown) { base::test::SingleThreadTaskEnvironment task_environment; - testing::StrictMock<MockInputHandler> mock_input_handler; + testing::StrictMock<cc::MockInputHandler> mock_input_handler; testing::StrictMock<MockInputHandlerProxyClient> mock_client; testing::StrictMock<MockSynchronousInputHandler> mock_synchronous_input_handler; @@ -2318,7 +2113,7 @@ TEST(SynchronousInputHandlerProxyTest, UpdateRootLayerState) { base::test::SingleThreadTaskEnvironment task_environment; - testing::NiceMock<MockInputHandler> mock_input_handler; + testing::NiceMock<cc::MockInputHandler> mock_input_handler; testing::StrictMock<MockInputHandlerProxyClient> mock_client; testing::StrictMock<MockSynchronousInputHandler> mock_synchronous_input_handler; @@ -2342,7 +2137,7 @@ TEST(SynchronousInputHandlerProxyTest, SetOffset) { base::test::SingleThreadTaskEnvironment task_environment; - testing::NiceMock<MockInputHandler> mock_input_handler; + testing::NiceMock<cc::MockInputHandler> mock_input_handler; testing::StrictMock<MockInputHandlerProxyClient> mock_client; testing::StrictMock<MockSynchronousInputHandler> mock_synchronous_input_handler;
diff --git a/third_party/blink/renderer/platform/widget/input/mock_input_handler_proxy.h b/third_party/blink/renderer/platform/widget/input/mock_input_handler_proxy.h new file mode 100644 index 0000000..48b155c4 --- /dev/null +++ b/third_party/blink/renderer/platform/widget/input/mock_input_handler_proxy.h
@@ -0,0 +1,32 @@ +// Copyright 2024 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_PLATFORM_WIDGET_INPUT_MOCK_INPUT_HANDLER_PROXY_H_ +#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WIDGET_INPUT_MOCK_INPUT_HANDLER_PROXY_H_ + +#include <memory> + +#include "cc/metrics/event_metrics.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "third_party/blink/public/common/input/web_coalesced_input_event.h" +#include "third_party/blink/renderer/platform/widget/input/input_handler_proxy.h" + +namespace blink::test { + +class MockInputHandlerProxy : public InputHandlerProxy { + public: + MockInputHandlerProxy(cc::InputHandler& input_handler, + InputHandlerProxyClient* client) + : InputHandlerProxy(input_handler, client) {} + ~MockInputHandlerProxy() override = default; + + MOCK_METHOD3(HandleInputEventWithLatencyInfo, + void(std::unique_ptr<blink::WebCoalescedInputEvent> event, + std::unique_ptr<cc::EventMetrics> metrics, + EventDispositionCallback callback)); +}; + +} // namespace blink::test + +#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WIDGET_INPUT_MOCK_INPUT_HANDLER_PROXY_H_
diff --git a/third_party/blink/renderer/platform/widget/input/mock_input_handler_proxy_client.h b/third_party/blink/renderer/platform/widget/input/mock_input_handler_proxy_client.h new file mode 100644 index 0000000..34908aa --- /dev/null +++ b/third_party/blink/renderer/platform/widget/input/mock_input_handler_proxy_client.h
@@ -0,0 +1,47 @@ +// Copyright 2024 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_PLATFORM_WIDGET_INPUT_MOCK_INPUT_HANDLER_PROXY_CLIENT_H_ +#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WIDGET_INPUT_MOCK_INPUT_HANDLER_PROXY_CLIENT_H_ + +#include "cc/input/overscroll_behavior.h" +#include "cc/input/touch_action.h" +#include "cc/metrics/event_metrics.h" +#include "third_party/blink/public/common/input/web_gesture_event.h" +#include "third_party/blink/public/common/input/web_input_event_attribution.h" +#include "third_party/blink/renderer/platform/widget/input/input_handler_proxy_client.h" +#include "ui/gfx/geometry/point.h" +#include "ui/gfx/geometry/vector2d_f.h" + +namespace blink::test { + +class MockInputHandlerProxyClient : public InputHandlerProxyClient { + public: + MockInputHandlerProxyClient() = default; + MockInputHandlerProxyClient(const MockInputHandlerProxyClient&) = delete; + MockInputHandlerProxyClient& operator=(const MockInputHandlerProxyClient&) = + delete; + + ~MockInputHandlerProxyClient() override = default; + + void WillShutdown() override {} + + MOCK_METHOD3(GenerateScrollBeginAndSendToMainThread, + void(const WebGestureEvent& update_event, + const WebInputEventAttribution&, + const cc::EventMetrics*)); + + MOCK_METHOD5(DidOverscroll, + void(const gfx::Vector2dF& accumulated_overscroll, + const gfx::Vector2dF& latest_overscroll_delta, + const gfx::Vector2dF& current_fling_velocity, + const gfx::PointF& causal_event_viewport_point, + const cc::OverscrollBehavior& overscroll_behavior)); + void DidStartScrollingViewport() override {} + MOCK_METHOD1(SetAllowedTouchAction, void(cc::TouchAction touch_action)); + bool AllowsScrollResampling() override { return true; } +}; + +} // namespace blink::test +#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WIDGET_INPUT_MOCK_INPUT_HANDLER_PROXY_CLIENT_H_
diff --git a/third_party/blink/renderer/platform/widget/input/widget_input_handler_manager.cc b/third_party/blink/renderer/platform/widget/input/widget_input_handler_manager.cc index 5908aec..41d4108 100644 --- a/third_party/blink/renderer/platform/widget/input/widget_input_handler_manager.cc +++ b/third_party/blink/renderer/platform/widget/input/widget_input_handler_manager.cc
@@ -4,9 +4,13 @@ #include "third_party/blink/renderer/platform/widget/input/widget_input_handler_manager.h" +#include <sys/types.h> + +#include <cstdint> #include <utility> #include "base/check_op.h" +#include "base/command_line.h" #include "base/feature_list.h" #include "base/functional/bind.h" #include "base/functional/callback_helpers.h" @@ -16,6 +20,7 @@ #include "base/notreached.h" #include "base/task/single_thread_task_runner.h" #include "base/time/time.h" +#include "base/trace_event/traced_value.h" #include "base/tracing/protos/chrome_track_event.pbzero.h" #include "base/types/optional_ref.h" #include "base/types/pass_key.h" @@ -26,6 +31,7 @@ #include "cc/trees/layer_tree_host.h" #include "cc/trees/paint_holding_reason.h" #include "components/viz/common/features.h" +#include "components/viz/common/switches.h" #include "services/tracing/public/cpp/perfetto/flow_event_utils.h" #include "third_party/blink/public/common/features.h" #include "third_party/blink/public/common/input/web_coalesced_input_event.h" @@ -119,6 +125,60 @@ } } +bool IgnoreHiddenInput() { + return base::FeatureList::IsEnabled(features::kIgnoreInputWhileHidden) && + !base::CommandLine::ForCurrentProcess()->HasSwitch( + ::switches::kRunAllCompositorStagesBeforeDraw); +} + +std::unique_ptr<base::trace_event::TracedValue> SuppressInputToTracedValue( + uint16_t suppress_input) { + auto dict = std::make_unique<base::trace_event::TracedValue>(); + dict->SetBoolean("DeferMainFrameUpdates", + (suppress_input & + static_cast<uint16_t>( + WidgetInputHandlerManager::SuppressingInputEventsBits:: + kDeferMainFrameUpdates))); + dict->SetBoolean( + "DeferCommits", + (suppress_input & + static_cast<uint16_t>(WidgetInputHandlerManager:: + SuppressingInputEventsBits::kDeferCommits))); + dict->SetBoolean( + "HasNotPainted", + (suppress_input & + static_cast<uint16_t>(WidgetInputHandlerManager:: + SuppressingInputEventsBits::kHasNotPainted))); + dict->SetBoolean( + "Hidden", + (suppress_input & + static_cast<uint16_t>( + WidgetInputHandlerManager::SuppressingInputEventsBits::kHidden))); + return dict; +} + +void SuppressEvent( + uint16_t suppress_input, + std::unique_ptr<WebCoalescedInputEvent> event, + mojom::blink::WidgetInputHandler::DispatchEventCallback callback) { + TRACE_EVENT("input", "Input Suppressed", "suppress_input", + SuppressInputToTracedValue(suppress_input)); + + int64_t trace_id = event->latency_info().trace_id(); + TRACE_EVENT("input,benchmark,latencyInfo", "LatencyInfo.Flow", + [&](perfetto::EventContext ctx) { + base::TaskAnnotator::EmitTaskTimingDetails(ctx); + ui::LatencyInfo::FillTraceEvent( + ctx, trace_id, + ChromeLatencyInfo2::Step::STEP_HANDLE_INPUT_EVENT_IMPL); + }); + if (callback) { + std::move(callback).Run( + mojom::blink::InputEventResultSource::kMainThread, ui::LatencyInfo(), + mojom::blink::InputEventResultState::kNotConsumed, nullptr, nullptr); + } +} + } // namespace #if BUILDFLAG(IS_ANDROID) @@ -264,7 +324,8 @@ widget_scheduler_->InputTaskRunner(), widget_scheduler_, /*allow_raf_aligned_input=*/!never_composited)), - allow_scroll_resampling_(allow_scroll_resampling) { + allow_scroll_resampling_(allow_scroll_resampling), + ignore_hidden_input_(IgnoreHiddenInput()) { #if BUILDFLAG(IS_ANDROID) if (compositor_thread_default_task_runner_) { synchronous_compositor_registry_ = @@ -295,6 +356,21 @@ } } +void WidgetInputHandlerManager::SetHidden(bool hidden) { + if (hidden) { + suppressing_input_events_state_ |= + static_cast<uint16_t>(SuppressingInputEventsBits::kHidden); + } else { + suppressing_input_events_state_ &= + ~static_cast<uint16_t>(SuppressingInputEventsBits::kHidden); + } +} + +void WidgetInputHandlerManager::OnDevToolsSessionConnectionChanged( + bool attached) { + dev_tools_session_attached_ = attached; +} + void WidgetInputHandlerManager::InitInputHandler() { bool sync_compositing = false; #if BUILDFLAG(IS_ANDROID) @@ -581,6 +657,12 @@ std::move(event), std::move(callback))); } +void WidgetInputHandlerManager::SetInputHandlerProxyForTesting( + std::unique_ptr<InputHandlerProxy> input_handler_proxy) { + input_handler_proxy_ = std::move(input_handler_proxy); + uses_input_handler_ = input_handler_proxy_.get(); +} + void WidgetInputHandlerManager::DispatchEvent( std::unique_ptr<WebCoalescedInputEvent> event, mojom::blink::WidgetInputHandler::DispatchEventCallback callback) { @@ -649,13 +731,14 @@ ~static_cast<uint16_t>(SuppressingInputEventsBits::kHasNotPainted); } + if (dev_tools_session_attached_ || !ignore_hidden_input_) { + suppress_input &= + ~static_cast<uint16_t>(SuppressingInputEventsBits::kHidden); + } + if (suppress_input && !allow_pre_commit_input_ && !event_is_mouse_or_pointer_move) { - if (callback) { - std::move(callback).Run( - mojom::blink::InputEventResultSource::kMainThread, ui::LatencyInfo(), - mojom::blink::InputEventResultState::kNotConsumed, nullptr, nullptr); - } + SuppressEvent(suppress_input, std::move(event), std::move(callback)); return; }
diff --git a/third_party/blink/renderer/platform/widget/input/widget_input_handler_manager.h b/third_party/blink/renderer/platform/widget/input/widget_input_handler_manager.h index bb5174e..a2ef32b 100644 --- a/third_party/blink/renderer/platform/widget/input/widget_input_handler_manager.h +++ b/third_party/blink/renderer/platform/widget/input/widget_input_handler_manager.h
@@ -66,6 +66,7 @@ kMaxValue = kAfterFirstPaint }; + public: // For use in bitfields to keep track of why we should keep suppressing input // events. Maybe the rendering pipeline is currently deferring something, or // we are still waiting for the user to see some non empty paint. And we use @@ -78,9 +79,10 @@ kDeferCommits = 1 << 1, // if set, we have not painted a main frame from the current navigation yet kHasNotPainted = 1 << 2, + // if set, we are not visible, and should not accept any input + kHidden = 1 << 3, }; - public: // The `widget` and `frame_widget_input_handler` should be invalidated // at the same time. static scoped_refptr<WidgetInputHandlerManager> Create( @@ -124,6 +126,8 @@ bool RequestedMainFramePending() override; void DidFirstVisuallyNonEmptyPaint(const base::TimeTicks& first_paint_time); + void SetHidden(bool hidden); + void OnDevToolsSessionConnectionChanged(bool attached); // InputHandlerProxyClient overrides. void WillShutdown() override; @@ -211,10 +215,17 @@ std::unique_ptr<blink::WebCoalescedInputEvent> event, mojom::blink::WidgetInputHandler::DispatchEventCallback callback); + void SetInputHandlerProxyForTesting( + std::unique_ptr<InputHandlerProxy> input_handler_proxy); + base::WeakPtr<WidgetInputHandlerManager> AsWeakPtr() { return weak_ptr_factory_.GetWeakPtr(); } + uint16_t suppressing_input_events_state() const { + return suppressing_input_events_state_; + } + private: friend class ThreadSafeRefCounted<WidgetInputHandlerManager>; ~WidgetInputHandlerManager() override; @@ -438,6 +449,9 @@ // web_tests to ensure that scroll deltas are not timing-dependent. const bool allow_scroll_resampling_ = true; + std::atomic<bool> dev_tools_session_attached_ = false; + const bool ignore_hidden_input_; + base::WeakPtrFactory<WidgetInputHandlerManager> weak_ptr_factory_{this}; };
diff --git a/third_party/blink/renderer/platform/widget/input/widget_input_handler_manager_unittest.cc b/third_party/blink/renderer/platform/widget/input/widget_input_handler_manager_unittest.cc new file mode 100644 index 0000000..db538d2 --- /dev/null +++ b/third_party/blink/renderer/platform/widget/input/widget_input_handler_manager_unittest.cc
@@ -0,0 +1,230 @@ +// Copyright 2024 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/platform/widget/input/widget_input_handler_manager.h" + +#include <string> + +#include "base/memory/raw_ptr.h" +#include "base/memory/scoped_refptr.h" +#include "base/memory/weak_ptr.h" +#include "base/test/scoped_feature_list.h" +#include "base/test/task_environment.h" +#include "cc/test/fake_impl_task_runner_provider.h" +#include "cc/test/fake_layer_tree_host_impl.h" +#include "cc/test/mock_input_handler.h" +#include "cc/test/test_task_graph_runner.h" +#include "cc/trees/layer_tree_settings.h" +#include "mojo/public/cpp/bindings/self_owned_associated_receiver.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/blink/public/common/features.h" +#include "third_party/blink/public/mojom/widget/platform_widget.mojom-blink.h" +#include "third_party/blink/public/platform/scheduler/test/renderer_scheduler_test_support.h" +#include "third_party/blink/renderer/platform/scheduler/test/fake_widget_scheduler.h" +#include "third_party/blink/renderer/platform/widget/compositing/test/stub_widget_base_client.h" +#include "third_party/blink/renderer/platform/widget/input/frame_widget_input_handler_impl.h" +#include "third_party/blink/renderer/platform/widget/input/input_handler_proxy.h" +#include "third_party/blink/renderer/platform/widget/input/mock_input_handler_proxy.h" +#include "third_party/blink/renderer/platform/widget/input/mock_input_handler_proxy_client.h" +#include "third_party/blink/renderer/platform/widget/widget_base.h" +#include "third_party/blink/renderer/platform/wtf/functional.h" + +namespace blink ::test { + +class WidgetInputHandlerManagerTest : public testing::Test, + public testing::WithParamInterface<bool> { + public: + WidgetInputHandlerManagerTest(); + ~WidgetInputHandlerManagerTest() override = default; + + bool IgnoreInputWhileHidden() const { return GetParam(); } + + // Generates and empty touch start, and invokes `DispatchEvent`. Will validate + // that `HandleInputEventWithLatencyInfo` is called `expected_times_called`. + void DispatchTouchEvent( + int expected_times_called, + mojom::blink::WidgetInputHandler::DispatchEventCallback callback); + + // Validates that `HandleInputEventWithLatencyInfo` is not called, and that + // the `DispatchEventCallback` is notified that the event was not consumed. + void ExpectNotConsumedDispatchEvent(); + + scoped_refptr<WidgetInputHandlerManager> widget_input_handler_manager() { + return widget_input_handler_manager_; + } + + // testing::Test: + void SetUp() override; + + private: + base::test::SingleThreadTaskEnvironment task_environment_; + + scoped_refptr<WidgetInputHandlerManager> widget_input_handler_manager_; + StubWidgetBaseClient client_; + scoped_refptr<scheduler::FakeWidgetScheduler> widget_scheduler_ = + base::MakeRefCounted<scheduler::FakeWidgetScheduler>(); + std::unique_ptr<WidgetBase> widget_base_; + base::WeakPtr<mojom::blink::FrameWidgetInputHandler> + frame_widget_input_handler_; + + testing::StrictMock<cc::MockInputHandler> mock_input_handler_; + raw_ptr<MockInputHandlerProxy> input_handler_proxy_; + testing::StrictMock<MockInputHandlerProxyClient> mock_client_; + + base::test::ScopedFeatureList scoped_feature_list_; +}; + +WidgetInputHandlerManagerTest::WidgetInputHandlerManagerTest() { + if (IgnoreInputWhileHidden()) { + scoped_feature_list_.InitAndEnableFeature( + features::kIgnoreInputWhileHidden); + } else { + scoped_feature_list_.InitAndDisableFeature( + features::kIgnoreInputWhileHidden); + } +} + +void WidgetInputHandlerManagerTest::DispatchTouchEvent( + int expected_times_called, + mojom::blink::WidgetInputHandler::DispatchEventCallback callback) { + EXPECT_CALL(*input_handler_proxy_, HandleInputEventWithLatencyInfo( + testing::_, testing::_, testing::_)) + .Times(expected_times_called); + widget_input_handler_manager_->DispatchEvent( + std::make_unique<WebCoalescedInputEvent>( + WebTouchEvent(WebInputEvent::Type::kTouchStart, + WebInputEvent::kNoModifiers, + WebInputEvent::GetStaticTimeStampForTests()), + ui::LatencyInfo()), + std::move(callback)); + testing::Mock::VerifyAndClearExpectations(input_handler_proxy_); +} + +void WidgetInputHandlerManagerTest::ExpectNotConsumedDispatchEvent() { + DispatchTouchEvent( + /*expected_times_called=*/0, + WTF::BindOnce([](mojom::blink::InputEventResultSource, + const ui::LatencyInfo&, + mojom::blink::InputEventResultState result_state, + mojom::blink::DidOverscrollParamsPtr, + mojom::blink::TouchActionOptionalPtr) { + EXPECT_EQ(result_state, + mojom::blink::InputEventResultState::kNotConsumed); + })); +} + +void WidgetInputHandlerManagerTest::SetUp() { + mojo::AssociatedRemote<mojom::blink::Widget> widget_remote; + mojo::PendingAssociatedReceiver<mojom::blink::Widget> widget_receiver = + widget_remote.BindNewEndpointAndPassDedicatedReceiver(); + + mojo::AssociatedRemote<mojom::blink::WidgetHost> widget_host_remote; + std::ignore = widget_host_remote.BindNewEndpointAndPassDedicatedReceiver(); + + const bool never_composited = false; + + widget_base_ = std::make_unique<WidgetBase>( + /*widget_base_client=*/&client_, widget_host_remote.Unbind(), + std::move(widget_receiver), + scheduler::GetSingleThreadTaskRunnerForTesting(), + /*is_hidden=*/false, never_composited, + /*is_for_child_local_root=*/false, + /*is_for_scalable_page=*/true); + + mojo::AssociatedRemote<mojom::blink::FrameWidgetInputHandler> + frame_widget_input_handler_remote; + mojo::PendingAssociatedReceiver<mojom::blink::FrameWidgetInputHandler> + frame_widget_input_handler_receiver = + frame_widget_input_handler_remote + .BindNewEndpointAndPassDedicatedReceiver(); + + mojo::MakeSelfOwnedAssociatedReceiver( + std::make_unique<FrameWidgetInputHandlerImpl>( + widget_base_->GetWeakPtr(), frame_widget_input_handler_, + /*input_event_queue=*/nullptr), + std::move(frame_widget_input_handler_receiver)); + + // WidgetBase isn't setup with full mocks. Were we to call this with + // `uses_input_hanlder_=true` we'd attempt to access `LayerHostTree` which + // we can't mock in WidgetBase. So we set to false, and use test override + // to enable the desired dispach mode. + widget_input_handler_manager_ = WidgetInputHandlerManager::Create( + widget_base_->GetWeakPtr(), frame_widget_input_handler_, never_composited, + /*compositor_thread_scheduler=*/nullptr, widget_scheduler_, + /*needs_input_handler=*/false, + /*allow_scroll_resampling=*/false, + /*io_thread_id=*/base::kInvalidThreadId, + /*main_thread_id=*/base::PlatformThread::CurrentId()); + + auto unique_input_handler_proxy = std::make_unique<MockInputHandlerProxy>( + mock_input_handler_, &mock_client_); + input_handler_proxy_ = unique_input_handler_proxy.get(); + widget_input_handler_manager_->SetInputHandlerProxyForTesting( + std::move(unique_input_handler_proxy)); +} + +// Tests that while we are hidden, that input is neither dispatched nor +// consumed. Becoming visible should remove this suppression. +TEST_P(WidgetInputHandlerManagerTest, InputWhileHidden) { + auto manager = widget_input_handler_manager(); + EXPECT_EQ( + manager->suppressing_input_events_state(), + static_cast<uint16_t>(WidgetInputHandlerManager:: + SuppressingInputEventsBits::kHasNotPainted)); + manager->DidFirstVisuallyNonEmptyPaint(base::TimeTicks::Now()); + EXPECT_EQ(manager->suppressing_input_events_state(), 0u); + + manager->SetHidden(true); + EXPECT_EQ( + manager->suppressing_input_events_state(), + static_cast<uint16_t>( + WidgetInputHandlerManager::SuppressingInputEventsBits::kHidden)); + + if (GetParam()) { + ExpectNotConsumedDispatchEvent(); + } + + manager->SetHidden(false); + EXPECT_EQ(manager->suppressing_input_events_state(), 0u); + DispatchTouchEvent(/*expected_times_called=*/1, + mojom::blink::WidgetInputHandler::DispatchEventCallback()); +} + +// Tests that while we are hidden, and attached DevTools sessions witll override +// the input suppression. Events should be dispatched. Upon the session +// detaching we should resume neither dispatching nor consuming events. +TEST_P(WidgetInputHandlerManagerTest, DevToolsSessionOverridesSuppression) { + auto manager = widget_input_handler_manager(); + EXPECT_EQ( + manager->suppressing_input_events_state(), + static_cast<uint16_t>(WidgetInputHandlerManager:: + SuppressingInputEventsBits::kHasNotPainted)); + manager->DidFirstVisuallyNonEmptyPaint(base::TimeTicks::Now()); + EXPECT_EQ(manager->suppressing_input_events_state(), 0u); + + manager->SetHidden(true); + EXPECT_EQ( + manager->suppressing_input_events_state(), + static_cast<uint16_t>( + WidgetInputHandlerManager::SuppressingInputEventsBits::kHidden)); + + manager->OnDevToolsSessionConnectionChanged(/*attached=*/true); + DispatchTouchEvent(/*expected_times_called=*/1, + mojom::blink::WidgetInputHandler::DispatchEventCallback()); + + manager->OnDevToolsSessionConnectionChanged(/*attached=*/false); + if (GetParam()) { + ExpectNotConsumedDispatchEvent(); + } +} + +INSTANTIATE_TEST_SUITE_P(, + WidgetInputHandlerManagerTest, + testing::Bool(), + [](auto& info) { + return info.param ? "IgnoreInputWhileHidden" + : "ProcessInputWhileHidden"; + }); +} // namespace blink::test
diff --git a/third_party/blink/renderer/platform/widget/widget_base.cc b/third_party/blink/renderer/platform/widget/widget_base.cc index e9acc8dde..e558cd72 100644 --- a/third_party/blink/renderer/platform/widget/widget_base.cc +++ b/third_party/blink/renderer/platform/widget/widget_base.cc
@@ -1431,6 +1431,10 @@ FlushInputProcessedCallback(); SetCompositorVisible(!is_hidden_); + + if (widget_input_handler_manager_) { + widget_input_handler_manager_->SetHidden(is_hidden_); + } } ui::TextInputType WidgetBase::GetTextInputType() { @@ -1888,4 +1892,10 @@ : max_render_buffer_bounds_gpu_; } +void WidgetBase::OnDevToolsSessionConnectionChanged(bool attached) { + if (widget_input_handler_manager_) { + widget_input_handler_manager_->OnDevToolsSessionConnectionChanged(attached); + } +} + } // namespace blink
diff --git a/third_party/blink/renderer/platform/widget/widget_base.h b/third_party/blink/renderer/platform/widget/widget_base.h index fb0c7d1..dbd53ba9 100644 --- a/third_party/blink/renderer/platform/widget/widget_base.h +++ b/third_party/blink/renderer/platform/widget/widget_base.h
@@ -403,6 +403,8 @@ bool WillBeDestroyed() const { return will_be_destroyed_; } + void OnDevToolsSessionConnectionChanged(bool attached); + private: static void AssertAreCompatible(const WidgetBase& a, const WidgetBase& b);
diff --git a/third_party/blink/renderer/platform/widget/widget_base_client.h b/third_party/blink/renderer/platform/widget/widget_base_client.h index ca38a1f..c0d293f8 100644 --- a/third_party/blink/renderer/platform/widget/widget_base_client.h +++ b/third_party/blink/renderer/platform/widget/widget_base_client.h
@@ -18,6 +18,7 @@ #include "third_party/blink/public/platform/web_input_event_result.h" #include "third_party/blink/public/platform/web_text_input_type.h" #include "third_party/blink/public/web/web_lifecycle_update.h" +#include "third_party/blink/renderer/platform/weborigin/kurl.h" #include "third_party/blink/renderer/platform/widget/input/input_handler_proxy.h" #include "ui/base/mojom/menu_source_type.mojom-blink-forward.h" #include "ui/display/mojom/screen_orientation.mojom-blink.h"
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations index be01deeb..5417191 100644 --- a/third_party/blink/web_tests/TestExpectations +++ b/third_party/blink/web_tests/TestExpectations
@@ -2682,6 +2682,8 @@ crbug.com/383880384 [ Win ] external/wpt/css/css-properties-values-api/registered-property-change-style-002.html [ Failure Pass ] # ====== New tests from wpt-importer added here ====== +crbug.com/388934352 external/wpt/trusted-types/block-eval-function-constructor.html [ Failure ] +crbug.com/388934352 external/wpt/trusted-types/WorkerGlobalScope-importScripts.html [ Failure ] crbug.com/388592307 external/wpt/css/selectors/invalidation/has-nested-pseudo-002-crash.html [ Crash ] crbug.com/388592307 external/wpt/css/selectors/invalidation/has-nested-pseudo-003-crash.html [ Crash ] crbug.com/388580242 [ Mac14 ] external/wpt/html/cross-origin-opener-policy/popup-redirect-same-origin-allow-popups.https.html [ Crash Failure ] @@ -6230,8 +6232,8 @@ crbug.com/40146374 virtual/customizable-select-disabled/external/wpt/html/semantics/forms/the-select-element/customizable-select/border-rendering.tentative.html [ Failure ] crbug.com/40146374 virtual/customizable-select-disabled/external/wpt/html/semantics/forms/the-select-element/customizable-select/select-events.tentative.html [ Failure ] crbug.com/40146374 virtual/select-parser-relaxation/external/wpt/html/semantics/forms/the-select-element/customizable-select/select-events.tentative.html [ Failure ] -crbug.com/40146374 virtual/customizable-select-disabled/external/wpt/html/semantics/forms/the-select-element/select-events.tentative.html [ Failure ] -crbug.com/40146374 virtual/select-parser-relaxation/external/wpt/html/semantics/forms/the-select-element/select-events.tentative.html [ Failure ] +crbug.com/40146374 virtual/customizable-select-disabled/external/wpt/html/semantics/forms/the-select-element/customizable-select/select-events-2.tentative.html [ Failure ] +crbug.com/40146374 virtual/select-parser-relaxation/external/wpt/html/semantics/forms/the-select-element/customizable-select/select-events-2.tentative.html [ Failure ] crbug.com/40146374 virtual/customizable-select-disabled/external/wpt/html/semantics/forms/the-select-element/select-keyboard-behavior.tentative.html [ Failure ] crbug.com/40146374 virtual/select-parser-relaxation/external/wpt/html/semantics/forms/the-select-element/select-keyboard-behavior.tentative.html [ Failure ] crbug.com/40146374 virtual/customizable-select-disabled/external/wpt/html/semantics/forms/the-select-element/select-option-focusable.tentative.html [ Failure ]
diff --git a/third_party/blink/web_tests/TestLists/content_shell.filter b/third_party/blink/web_tests/TestLists/content_shell.filter index e0e55adf..352b7c3 100644 --- a/third_party/blink/web_tests/TestLists/content_shell.filter +++ b/third_party/blink/web_tests/TestLists/content_shell.filter
@@ -422,7 +422,6 @@ external/wpt/css/css-scroll-snap-2/scroll-start/scroll-start-fieldset.tentative.html external/wpt/css/css-scroll-snap-2/scroll-initial-target/scroll-initial-target-rtl.tentative.html external/wpt/css/css-scroll-snap/input/snap-area-overflow-boundary-viewport-covering.tentative.html -external/wpt/css/css-scroll-snap/selection-target.html external/wpt/css/css-scroll-snap/snap-after-relayout/changing-scroll-snap-align-nested.tentative.html external/wpt/css/css-scroll-snap/snap-after-relayout/multiple-aligned-targets/common-to-both-axes-supercedes-first-in-tree-order.html external/wpt/css/css-scroll-snap/snap-after-relayout/multiple-aligned-targets/prefer-common-to-both-axes.html @@ -630,7 +629,6 @@ external/wpt/fledge/tentative/component-ads.https.window.html?11-15 external/wpt/fledge/tentative/reporting-arguments.https.window.html?16-last external/wpt/fs/FileSystemObserver-sync-access-handle.https.tentative.* -external/wpt/gyroscope/Gyroscope.https.html external/wpt/gyroscope/Gyroscope-iframe-access.https.html external/wpt/html-aam/names.html external/wpt/html-aam/roles.html @@ -882,7 +880,6 @@ external/wpt/partitioned-popins/partitioned-popins.cookies-blocked.tentative.sub.https.window.html external/wpt/partitioned-popins/partitioned-popins.partitions.tentative.https.window.html external/wpt/payment-request/onpaymentmethodchange-attribute.https.html -external/wpt/payment-request/payment-request-disallowed-when-hidden.https.html external/wpt/payment-request/show-consume-activation.https.html external/wpt/performance-timeline/navigation-id-reset.tentative.html external/wpt/performance-timeline/not-restored-reasons/abort-block-bfcache.window.html @@ -891,13 +888,11 @@ external/wpt/pointerevents/capturing_boundary_event_handler_at_ua_shadowdom.html* external/wpt/pointerevents/coalesced_events_attributes_under_load.https.optional.html* external/wpt/pointerevents/compat/pointerevent_mouse-pointer-on-scrollbar.html -external/wpt/pointerevents/compat/pointerevent_mouseevent_key_pressed.html external/wpt/pointerevents/compat/pointerevent_touch_target_after_pointerdown_target_removed.tentative.html external/wpt/pointerevents/pointerevent_capture_mouse_and_release_and_capture_again.html external/wpt/pointerevents/pointerevent_contextmenu_is_a_pointerevent.html?touch external/wpt/pointerevents/pointerevent_pointer_boundary_events_after_removing_last_over_element.html external/wpt/pointerevents/pointerevent_pointercapture_in_frame.html?pen -external/wpt/pointerevents/pointerevent_pointermove_after_pointerup_target_removed.html external/wpt/pointerevents/pointerevent_sequence_at_implicit_release_on_drag.html external/wpt/pointerevents/pointerevent_to_slotted_target.html?pen external/wpt/pointerevents/pointerevent_touch-action-modified_touch.html @@ -908,11 +903,6 @@ external/wpt/resize-observer/notify.html external/wpt/resize-observer/scrollbars.html external/wpt/resize-observer/svg.html -external/wpt/screen-orientation/event-before-promise.html -external/wpt/screen-orientation/lock-basic.html -external/wpt/screen-orientation/onchange-event.html -external/wpt/screen-wake-lock/chrome-bug-1348019.https.html -external/wpt/screen-wake-lock/wakelock-document-hidden.https.html external/wpt/screen-wake-lock/wakelock-enabled-by-permissions-policy-attribute.https.html external/wpt/screen-wake-lock/wakelock-enabled-by-permissions-policy.https.html external/wpt/scroll-animations/css/animation-fill-outside-range-test.html @@ -1011,7 +1001,6 @@ external/wpt/websockets/Close-1005.any.worker.html?wpt_flags=h2 external/wpt/websockets/Close-3000-reason.any.worker.html?wpt_flags=h2 external/wpt/websockets/Close-3000-verify-code.any.worker.html?wpt_flags=h2 -external/wpt/websockets/cookies/006.html?wss&wpt_flags=https external/wpt/websockets/Create-extensions-empty.any.html?wpt_flags=h2 external/wpt/websockets/Create-valid-url.any.worker.html?wpt_flags=h2 external/wpt/websockets/Create-valid-url-array-protocols.any.html?wpt_flags=h2 @@ -1090,9 +1079,7 @@ virtual/fractional-scroll-offsets/external/wpt/css/css-position/sticky/position-sticky-inline.html virtual/fractional-scroll-offsets/external/wpt/css/css-position/sticky/position-sticky-large-top* virtual/fractional-scroll-offsets/external/wpt/css/css-position/sticky/position-sticky-nested-inline.html -virtual/generic-sensor-extra-classes/external/wpt/ambient-light/AmbientLightSensor.https.html virtual/generic-sensor-extra-classes/external/wpt/ambient-light/AmbientLightSensor-iframe-access.https.html -virtual/generic-sensor-extra-classes/external/wpt/magnetometer/Magnetometer.https.html virtual/generic-sensor-extra-classes/external/wpt/magnetometer/Magnetometer-iframe-access.https.html virtual/gpu-rasterization/external/wpt/css/css-images/cross-fade-premultiplied-alpha.html virtual/gpu-rasterization/external/wpt/css/css-images/object-fit-* @@ -1206,7 +1193,6 @@ virtual/threaded/external/wpt/css/css-backgrounds/table-cell-background-local-003.html virtual/threaded/external/wpt/css/css-view-transitions/* virtual/threaded/external/wpt/long-animation-frame/tentative/loaf-iframe-self.html -virtual/threaded/external/wpt/long-animation-frame/tentative/loaf-visibility.html virtual/threaded-composited-iframes/external/wpt/is-input-pending/tentative/* virtual/threaded-prefer-compositing/external/wpt/css/cssom-view/getBoundingClientRect-shy.html virtual/threaded-prefer-compositing/external/wpt/css/cssom-view/range-bounding-client-rect-with-display-contents.html
diff --git a/third_party/blink/web_tests/TestLists/highdpi b/third_party/blink/web_tests/TestLists/highdpi index a84ea94..2b3e853 100644 --- a/third_party/blink/web_tests/TestLists/highdpi +++ b/third_party/blink/web_tests/TestLists/highdpi
@@ -849,7 +849,7 @@ external/wpt/html/semantics/forms/the-output-element/output-setcustomvalidity.html external/wpt/html/semantics/forms/the-progress-element/progress.html external/wpt/html/semantics/forms/the-select-element/select-add.html -external/wpt/html/semantics/forms/the-select-element/select-events.tentative.html +external/wpt/html/semantics/forms/the-select-element/customizable-select/select-events-2.tentative.html external/wpt/html/semantics/forms/the-textarea-element/wrap-enumerated-ascii-case-insensitive.html external/wpt/html/semantics/grouping-content/the-dd-element/grouping-dd.html external/wpt/html/semantics/grouping-content/the-div-element/grouping-div.html
diff --git a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json index 8fc44d38..f6eebba 100644 --- a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json +++ b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
@@ -3895,6 +3895,13 @@ {} ] ], + "multicol-loads-indefinitely-001-crash.html": [ + "c00891c14c16485ce88a3bfe56d0432658eb8e61", + [ + null, + {} + ] + ], "nested-balanced-monolithic-multicol-crash.html": [ "082bf70691c955e1ba7fac9e2c077c71a7d52f84", [ @@ -20405,6 +20412,21 @@ }, "print-reftest": { "css": { + "css-anchor-position": { + "anchor-position-005-print.html": [ + "eeae841405a774cfa77012b7f01b304ea8c89632", + [ + null, + [ + [ + "/css/css-anchor-position/anchor-position-005-print-ref.html", + "==" + ] + ], + {} + ] + ] + }, "css-break": { "abspos-in-clipped-overflow-print.html": [ "4510dd3cbc87aa549c0b81fdcfd5ccdafc4fa6b0", @@ -62360,6 +62382,58 @@ {} ] ], + "collapsing-border-model-011.html": [ + "9c03ddeda9051c6e823ca1f31729eed0d8a8ac7b", + [ + null, + [ + [ + "/css/reference/ref-filled-green-100px-square-only.html", + "==" + ] + ], + {} + ] + ], + "collapsing-border-model-012.html": [ + "39b176ce375994ece4ca6c05b7484ef59fcd1c79", + [ + null, + [ + [ + "/css/reference/ref-filled-green-100px-square-only.html", + "==" + ] + ], + {} + ] + ], + "collapsing-border-model-013.html": [ + "5e5f57694c9015c5a74082a9525bb2ce44d39bfc", + [ + null, + [ + [ + "/css/reference/ref-filled-green-100px-square-only.html", + "==" + ] + ], + {} + ] + ], + "collapsing-border-model-014.html": [ + "18177526b824cfebbc95467031c30d04b7b3ff79", + [ + null, + [ + [ + "/css/reference/ref-filled-green-100px-square-only.html", + "==" + ] + ], + {} + ] + ], "column-visibility-004.xht": [ "60d233fa9402f0b57a45f299405dacca5abc8ee6", [ @@ -73517,6 +73591,19 @@ {} ] ], + "anchor-position-multicol-011.html": [ + "eab57bfdb841bc488708ad001f2534cdae2a5188", + [ + null, + [ + [ + "/css/reference/ref-filled-green-100px-square.xht", + "==" + ] + ], + {} + ] + ], "anchor-position-top-layer-001.html": [ "f40cc0dccf3ccbef010629ebc809229ba83c9b45", [ @@ -312992,6 +313079,20 @@ "34edb7b82b9657e0beaf9b1669d0854d365770ed", [] ], + "permissions-policy": { + "ch-ua-high-entropy-values-disabled-by-permissions-policy.https.sub.html.headers": [ + "fcf474880843e220faff943f8946f83b514208a4", + [] + ], + "ch-ua-high-entropy-values-enabled-by-permissions-policy.https.sub.html.headers": [ + "ac74cfd647f07a2e276b5913a139b7728189d7c9", + [] + ], + "ch-ua-high-entropy-values-enabled-on-self-origin-by-permissions-policy.https.sub.html.headers": [ + "9c876c03f3cf59614059e818acab6e39210a5122", + [] + ] + }, "resources": { "2x3-svg-scaled-by-sec-ch-width.py": [ "d53574c361213ddf0949f906a08ebe388f9aa2d0", @@ -323689,6 +323790,10 @@ "5f741b46bb7c2ea65709b61212b0931a9574c7f8", [] ], + "anchor-position-005-print-ref.html": [ + "94a1f8e55d30e2de6607ca741142d4bdbdf87098", + [] + ], "anchor-position-multicol-003-expected.txt": [ "7f03f52a900220153e28834fdbb2cca9f2d99da5", [] @@ -385708,6 +385813,12 @@ "8557441f7e2a9f788475f63c8cb936daaa2afb6d", [] ] + }, + "the-offscreen-canvas": { + "offscreencanvas.transferrable.sw.js": [ + "fc7265a48c393566ad8c9fe7b73281e4f5137b21", + [] + ] } }, "path-objects": { @@ -400751,7 +400862,7 @@ [] ], "WebCryptoAPI.idl": [ - "ae85c1cfe4684fe778038f579223ee7a5606150c", + "ff7a89cd0d51be01760b9bfdb709393f4db5fd31", [] ], "accelerometer.idl": [ @@ -401031,7 +401142,7 @@ [] ], "digital-credentials.idl": [ - "2207b25dd57a0b4d47552faaf0de5cc3cd892021", + "e20079efa14f9a894181d1cafb66129a1634c627", [] ], "digital-goods.idl": [ @@ -401055,7 +401166,7 @@ [] ], "element-timing.idl": [ - "586b5084bb00e902dd3b1a80d09847423bbdc509", + "ef73ca6c0f610ff8cdb14a8bd0859efb8eca743b", [] ], "encoding.idl": [ @@ -401551,7 +401662,7 @@ [] ], "speech-api.idl": [ - "74085481525c943296f023e94a6532e5bc9f5b15", + "f3967b873ffc597380585a0718c4f43406b2f281", [] ], "storage-access.idl": [ @@ -401595,7 +401706,7 @@ [] ], "turtledove.idl": [ - "c416760d5956c4d2b818d8fda9da1384604919b0", + "05072974ec94e3a386aa74e7c0cd6ef98e2b4f04", [] ], "ua-client-hints.idl": [ @@ -401635,7 +401746,7 @@ [] ], "wai-aria.idl": [ - "78083f03f91fd987b6e19da391e640bf9816d701", + "deebc5626e2a925c4b7c7bad92c8492ebb4dcb08", [] ], "wasm-js-api.idl": [ @@ -401687,7 +401798,7 @@ [] ], "webauthn.idl": [ - "46e2418281e4355bd60e493f9b6af20b7ec4b19b", + "a33c85e7bad86753211fa7aa9270abac18b1e54e", [] ], "webcodecs-aac-codec-registration.idl": [ @@ -406975,6 +407086,10 @@ "2265bd01d2bd66cfa079e22a749e03e836e45785", [] ], + "permissions-policy-ch-ua-high-entropy-values.html": [ + "842cf590293dade359df1243b3f3f8b1d804091b", + [] + ], "permissions-policy-clipboard-read.html": [ "10fc45fd933ef0f77e5d53d4fac9ec70d372ce48", [] @@ -407454,12 +407569,8 @@ "98e4d4ad6702f8c847f92fddaa16cdd1b9940632", [] ], - "pointerevent_pointercapture_in_frame_mouse-expected.txt": [ - "be6da2668f5106e74c5e86b211b944e125d71cd7", - [] - ], "pointerevent_pointercapture_in_frame_touch-expected.txt": [ - "e94d02d6b0677b91bbfa65b3204e1ad0277a1bb4", + "a9b817ec4e51ba4b29149ab81d112c8dec732f87", [] ], "pointerevent_pointermove_after_pointerup_target_removed-expected.txt": [ @@ -417784,7 +417895,7 @@ ], "support": { "touch.js": [ - "f4bc0467db8a215fa85fb7e1c483b3d99a458ef7", + "af0fefb67002a02910aee82ea7f02839c967d321", [] ] } @@ -417830,6 +417941,10 @@ "6ea31f1ef8d3838b3b9d84f6dac839b22083ee20", [] ], + "WorkerGlobalScope-worker-constructor-expected.txt": [ + "17ab3eb534a2de51218cd16da334bbf79b47e8e2", + [] + ], "block-string-assignment-to-DOMWindowTimers-setTimeout-setInterval-expected.txt": [ "278967b2138dba63046eb9ae71ffb702f826fd2e", [] @@ -417929,6 +418044,14 @@ "7d574a6a1387a9e7bdaaf596f46e9d616ca94988", [] ], + "ServiceWorkerContainer-register.https.js": [ + "150da428779516deb10176e908772d16a1a27f3b", + [] + ], + "ServiceWorkerContainer-register.https.js.headers": [ + "604e765da46d85fe8ab85d3097fe7c2cbe00a930", + [] + ], "WorkerGlobalScope-eval.https.js": [ "95ac1ff844b3e4e6f01a4abb3cf4846df514bf70", [] @@ -417938,7 +418061,7 @@ [] ], "WorkerGlobalScope-importScripts.https.js": [ - "c40e8550dd659e2656564f5b3b61a0e5cb591710", + "a3ecfc481923b5e92e0eca287498086b4448458c", [] ], "WorkerGlobalScope-importScripts.https.js.headers": [ @@ -417946,13 +418069,21 @@ [] ], "WorkerGlobalScope-worker-constructor.js": [ - "7306b186d61904a60c082d168dc8cd843bcfbf30", + "e45a92d4817c1c81c2b1e893cb4ecd20cc238de1", [] ], "WorkerGlobalScope-worker-constructor.js.headers": [ "af6596b29a8080e5cd8d688d0d6933caf49a2090", [] ], + "block-eval-function-constructor-worker.js": [ + "0a74a1cdef31c93cd854f6abe0220319002d0303", + [] + ], + "block-eval-function-constructor.js": [ + "83bb606388305d9c9e20cb11623f379ef005316f", + [] + ], "block-string-assignment-to-DOMWindowTimers-setTimeout-setInterval-worker.js": [ "456780247f2c329ddd20ebffe5f7b441e5451b28", [] @@ -418005,6 +418136,10 @@ "45053d43e362e223e0ce5e6dffb4da09c0ce3f34", [] ], + "worker.https.js": [ + "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391", + [] + ], "worker.js": [ "4079f7e9c7933cf9ee195fe0e7a54e0f56f184ab", [] @@ -421716,26 +421851,6 @@ "ede50f41555a2b4543d2b8d98207855f22cc0371", [] ], - "audioDecoder-codec-specific.https.any_pcm_f32-expected.txt": [ - "0aa83d1b0ca5fbe42c10ff1148f1eb8988b428c7", - [] - ], - "audioDecoder-codec-specific.https.any_pcm_s16-expected.txt": [ - "2380e74abe8af7bc308723aa43ca3289477478cd", - [] - ], - "audioDecoder-codec-specific.https.any_pcm_s24-expected.txt": [ - "7fe6747c3ee21f2bc6085f47200b0e838a94b7fa", - [] - ], - "audioDecoder-codec-specific.https.any_pcm_s32-expected.txt": [ - "fe4e5430ce379b8f9a265b05fd4d8244828966ce", - [] - ], - "audioDecoder-codec-specific.https.any_pcm_u8-expected.txt": [ - "141d485febff01c84079bb0dd59df850898a5579", - [] - ], "audioDecoder-codec-specific.https.any_vorbis-expected.txt": [ "02d1bda8b892d4fd7e0889ba9af8362a3ac523b3", [] @@ -423099,7 +423214,7 @@ [] ], "fixtures_bidi.py": [ - "241ba528ddfaae95c4474dba578417813a189bcb", + "ec36eb953ff391610d23930dc558d10d11c4b242", [] ], "fixtures_http.py": [ @@ -424912,6 +425027,10 @@ "server-read-then-close.py": [ "7f992e0dcca3ae62277cac0fa39355fce3e57be0", [] + ], + "token-count.py": [ + "8cdfd802b1edb23bf038a2dab82e9913c3654a74", + [] ] }, "idlharness.https.any-expected.txt": [ @@ -436372,38 +436491,462 @@ {} ] ], - "nested-cloning-basic.html": [ - "df4848b69374c89bc7dc372c1adc5365bccc096e", + "nested-cloning-basic.any.js": [ + "91e6630d8545ec5d9b34900dfaa121aff15dfb8c", [ - null, + "IndexedDB/nested-cloning-basic.any.html", { + "script_metadata": [ + [ + "title", + "IndexedDB: basic objects are cloned correctly" + ], + [ + "global", + "window,worker" + ], + [ + "script", + "resources/support-promises.js" + ], + [ + "script", + "resources/nested-cloning-common.js" + ], + [ + "timeout", + "long" + ] + ], + "timeout": "long" + } + ], + [ + "IndexedDB/nested-cloning-basic.any.serviceworker.html", + { + "script_metadata": [ + [ + "title", + "IndexedDB: basic objects are cloned correctly" + ], + [ + "global", + "window,worker" + ], + [ + "script", + "resources/support-promises.js" + ], + [ + "script", + "resources/nested-cloning-common.js" + ], + [ + "timeout", + "long" + ] + ], + "timeout": "long" + } + ], + [ + "IndexedDB/nested-cloning-basic.any.sharedworker.html", + { + "script_metadata": [ + [ + "title", + "IndexedDB: basic objects are cloned correctly" + ], + [ + "global", + "window,worker" + ], + [ + "script", + "resources/support-promises.js" + ], + [ + "script", + "resources/nested-cloning-common.js" + ], + [ + "timeout", + "long" + ] + ], + "timeout": "long" + } + ], + [ + "IndexedDB/nested-cloning-basic.any.worker.html", + { + "script_metadata": [ + [ + "title", + "IndexedDB: basic objects are cloned correctly" + ], + [ + "global", + "window,worker" + ], + [ + "script", + "resources/support-promises.js" + ], + [ + "script", + "resources/nested-cloning-common.js" + ], + [ + "timeout", + "long" + ] + ], "timeout": "long" } ] ], - "nested-cloning-large-multiple.html": [ - "97bcaddfb2d64bfe89aaf0e7df574a654e009153", + "nested-cloning-large-multiple.any.js": [ + "7473d33068da24d2da1e3b439528aca872e482b5", [ - null, + "IndexedDB/nested-cloning-large-multiple.any.html", { + "script_metadata": [ + [ + "title", + "IndexedDB: large nested objects are cloned correctly" + ], + [ + "global", + "window,worker" + ], + [ + "script", + "resources/support-promises.js" + ], + [ + "script", + "resources/nested-cloning-common.js" + ], + [ + "timeout", + "long" + ] + ], + "timeout": "long" + } + ], + [ + "IndexedDB/nested-cloning-large-multiple.any.serviceworker.html", + { + "script_metadata": [ + [ + "title", + "IndexedDB: large nested objects are cloned correctly" + ], + [ + "global", + "window,worker" + ], + [ + "script", + "resources/support-promises.js" + ], + [ + "script", + "resources/nested-cloning-common.js" + ], + [ + "timeout", + "long" + ] + ], + "timeout": "long" + } + ], + [ + "IndexedDB/nested-cloning-large-multiple.any.sharedworker.html", + { + "script_metadata": [ + [ + "title", + "IndexedDB: large nested objects are cloned correctly" + ], + [ + "global", + "window,worker" + ], + [ + "script", + "resources/support-promises.js" + ], + [ + "script", + "resources/nested-cloning-common.js" + ], + [ + "timeout", + "long" + ] + ], + "timeout": "long" + } + ], + [ + "IndexedDB/nested-cloning-large-multiple.any.worker.html", + { + "script_metadata": [ + [ + "title", + "IndexedDB: large nested objects are cloned correctly" + ], + [ + "global", + "window,worker" + ], + [ + "script", + "resources/support-promises.js" + ], + [ + "script", + "resources/nested-cloning-common.js" + ], + [ + "timeout", + "long" + ] + ], "timeout": "long" } ] ], - "nested-cloning-large.html": [ - "0cd8cb48ceec704ef70ea70dae8da76d8928ef12", + "nested-cloning-large.any.js": [ + "07a9e2711390f9ad1f3ddf1e65e3852ad6ddc7a5", [ - null, + "IndexedDB/nested-cloning-large.any.html", { + "script_metadata": [ + [ + "title", + "IndexedDB: large nested objects are cloned correctly" + ], + [ + "global", + "window,worker" + ], + [ + "script", + "resources/support-promises.js" + ], + [ + "script", + "resources/nested-cloning-common.js" + ], + [ + "timeout", + "long" + ] + ], + "timeout": "long" + } + ], + [ + "IndexedDB/nested-cloning-large.any.serviceworker.html", + { + "script_metadata": [ + [ + "title", + "IndexedDB: large nested objects are cloned correctly" + ], + [ + "global", + "window,worker" + ], + [ + "script", + "resources/support-promises.js" + ], + [ + "script", + "resources/nested-cloning-common.js" + ], + [ + "timeout", + "long" + ] + ], + "timeout": "long" + } + ], + [ + "IndexedDB/nested-cloning-large.any.sharedworker.html", + { + "script_metadata": [ + [ + "title", + "IndexedDB: large nested objects are cloned correctly" + ], + [ + "global", + "window,worker" + ], + [ + "script", + "resources/support-promises.js" + ], + [ + "script", + "resources/nested-cloning-common.js" + ], + [ + "timeout", + "long" + ] + ], + "timeout": "long" + } + ], + [ + "IndexedDB/nested-cloning-large.any.worker.html", + { + "script_metadata": [ + [ + "title", + "IndexedDB: large nested objects are cloned correctly" + ], + [ + "global", + "window,worker" + ], + [ + "script", + "resources/support-promises.js" + ], + [ + "script", + "resources/nested-cloning-common.js" + ], + [ + "timeout", + "long" + ] + ], "timeout": "long" } ] ], - "nested-cloning-small.html": [ - "e5105a999f57e6bc49828ce886ab138117c9c8f6", + "nested-cloning-small.any.js": [ + "7cef76275e52512121ebc05ede382cff39b7ae23", [ - null, + "IndexedDB/nested-cloning-small.any.html", { + "script_metadata": [ + [ + "title", + "IndexedDB: small nested objects are cloned correctly" + ], + [ + "global", + "window,worker" + ], + [ + "script", + "resources/support-promises.js" + ], + [ + "script", + "resources/nested-cloning-common.js" + ], + [ + "timeout", + "long" + ] + ], + "timeout": "long" + } + ], + [ + "IndexedDB/nested-cloning-small.any.serviceworker.html", + { + "script_metadata": [ + [ + "title", + "IndexedDB: small nested objects are cloned correctly" + ], + [ + "global", + "window,worker" + ], + [ + "script", + "resources/support-promises.js" + ], + [ + "script", + "resources/nested-cloning-common.js" + ], + [ + "timeout", + "long" + ] + ], + "timeout": "long" + } + ], + [ + "IndexedDB/nested-cloning-small.any.sharedworker.html", + { + "script_metadata": [ + [ + "title", + "IndexedDB: small nested objects are cloned correctly" + ], + [ + "global", + "window,worker" + ], + [ + "script", + "resources/support-promises.js" + ], + [ + "script", + "resources/nested-cloning-common.js" + ], + [ + "timeout", + "long" + ] + ], + "timeout": "long" + } + ], + [ + "IndexedDB/nested-cloning-small.any.worker.html", + { + "script_metadata": [ + [ + "title", + "IndexedDB: small nested objects are cloned correctly" + ], + [ + "global", + "window,worker" + ], + [ + "script", + "resources/support-promises.js" + ], + [ + "script", + "resources/nested-cloning-common.js" + ], + [ + "timeout", + "long" + ] + ], "timeout": "long" } ] @@ -437559,25 +438102,162 @@ {} ] ], - "transaction-lifetime-blocked.htm": [ - "760b6b9bdbe91b987b02495a2cd7af4328520c90", + "transaction-lifetime-empty.any.js": [ + "9ef27d2812dd144e14727b7e122fa22a373b3ea0", [ - null, - {} + "IndexedDB/transaction-lifetime-empty.any.html", + { + "script_metadata": [ + [ + "title", + "IndexedDB: Commit ordering of empty transactions" + ], + [ + "global", + "window,worker" + ], + [ + "script", + "resources/support.js" + ] + ] + } + ], + [ + "IndexedDB/transaction-lifetime-empty.any.serviceworker.html", + { + "script_metadata": [ + [ + "title", + "IndexedDB: Commit ordering of empty transactions" + ], + [ + "global", + "window,worker" + ], + [ + "script", + "resources/support.js" + ] + ] + } + ], + [ + "IndexedDB/transaction-lifetime-empty.any.sharedworker.html", + { + "script_metadata": [ + [ + "title", + "IndexedDB: Commit ordering of empty transactions" + ], + [ + "global", + "window,worker" + ], + [ + "script", + "resources/support.js" + ] + ] + } + ], + [ + "IndexedDB/transaction-lifetime-empty.any.worker.html", + { + "script_metadata": [ + [ + "title", + "IndexedDB: Commit ordering of empty transactions" + ], + [ + "global", + "window,worker" + ], + [ + "script", + "resources/support.js" + ] + ] + } ] ], - "transaction-lifetime-empty.html": [ - "ba299fdcd041802b49e41cb87cf4d52a577bab81", + "transaction-lifetime.any.js": [ + "969960c4aecbe9cbe1248e5dfee756420b273ebb", [ - null, - {} - ] - ], - "transaction-lifetime.htm": [ - "996f62937f76fa2b97fd3b66ead3993d234773f5", + "IndexedDB/transaction-lifetime.any.html", + { + "script_metadata": [ + [ + "title", + "Event order when opening a second database when one connection is open already" + ], + [ + "global", + "window,worker" + ], + [ + "script", + "resources/support.js" + ] + ] + } + ], [ - null, - {} + "IndexedDB/transaction-lifetime.any.serviceworker.html", + { + "script_metadata": [ + [ + "title", + "Event order when opening a second database when one connection is open already" + ], + [ + "global", + "window,worker" + ], + [ + "script", + "resources/support.js" + ] + ] + } + ], + [ + "IndexedDB/transaction-lifetime.any.sharedworker.html", + { + "script_metadata": [ + [ + "title", + "Event order when opening a second database when one connection is open already" + ], + [ + "global", + "window,worker" + ], + [ + "script", + "resources/support.js" + ] + ] + } + ], + [ + "IndexedDB/transaction-lifetime.any.worker.html", + { + "script_metadata": [ + [ + "title", + "Event order when opening a second database when one connection is open already" + ], + [ + "global", + "window,worker" + ], + [ + "script", + "resources/support.js" + ] + ] + } ] ], "transaction-relaxed-durability.any.js": [ @@ -447019,7 +447699,7 @@ "ai": { "language_detection": { "capabilities.tentative.https.any.js": [ - "2a0698695d00ce2f8a50ade3adbcd34212051b8f", + "3eca5119a378741fc48adb44f4bd83ba7a7e06bd", [ "ai/language_detection/capabilities.tentative.https.any.html", { @@ -447027,6 +447707,40 @@ [ "title", "capabilities test" + ], + [ + "global", + "window,worker" + ] + ] + } + ], + [ + "ai/language_detection/capabilities.tentative.https.any.serviceworker.html", + { + "script_metadata": [ + [ + "title", + "capabilities test" + ], + [ + "global", + "window,worker" + ] + ] + } + ], + [ + "ai/language_detection/capabilities.tentative.https.any.sharedworker.html", + { + "script_metadata": [ + [ + "title", + "capabilities test" + ], + [ + "global", + "window,worker" ] ] } @@ -447038,13 +447752,17 @@ [ "title", "capabilities test" + ], + [ + "global", + "window,worker" ] ] } ] ], "detector.https.tentative.any.js": [ - "4f962de8339de0a8eb1b0444dfcf668c05f76b88", + "6bd3a41b8cd8396d9bb890fe17bd753adc4f232e", [ "ai/language_detection/detector.https.tentative.any.html", { @@ -447052,6 +447770,40 @@ [ "title", "Detect english" + ], + [ + "global", + "window,worker" + ] + ] + } + ], + [ + "ai/language_detection/detector.https.tentative.any.serviceworker.html", + { + "script_metadata": [ + [ + "title", + "Detect english" + ], + [ + "global", + "window,worker" + ] + ] + } + ], + [ + "ai/language_detection/detector.https.tentative.any.sharedworker.html", + { + "script_metadata": [ + [ + "title", + "Detect english" + ], + [ + "global", + "window,worker" ] ] } @@ -447063,6 +447815,10 @@ [ "title", "Detect english" + ], + [ + "global", + "window,worker" ] ] } @@ -456184,6 +456940,50 @@ {} ] ], + "permissions-policy": { + "ch-ua-high-entropy-values-allowed-by-permissions-policy-attribute-redirect-on-load.https.sub.html": [ + "da66bd0feb9ea8960aef6e076366b974751ecfbb", + [ + null, + {} + ] + ], + "ch-ua-high-entropy-values-default-permissions-policy.https.sub.html": [ + "d73d61d5ac31e2cb7b4deefb78beb7168599d45a", + [ + null, + {} + ] + ], + "ch-ua-high-entropy-values-disabled-by-permissions-policy.https.sub.html": [ + "c86d3829d5c9fb2613fa16d1757c039aa2d3ce1e", + [ + null, + {} + ] + ], + "ch-ua-high-entropy-values-enabled-by-permissions-policy.https.sub.html": [ + "7e05749816173a90a8e710698993b5df071daf34", + [ + null, + {} + ] + ], + "ch-ua-high-entropy-values-enabled-on-self-origin-by-permissions-policy.https.sub.html": [ + "24161b3cc02540c7c74a3883e11958542d59b3cc", + [ + null, + {} + ] + ], + "ch-ua-high-entropy-values-permissions-policy-attribute.https.sub.html": [ + "a6f9bbf73ee6e0ee511bea827e6ff51591df9d00", + [ + null, + {} + ] + ] + }, "sandbox": { "iframe-csp-same-origin.https.html": [ "a5f094af9d4354527ed4ac2401d0b4ef5075e340", @@ -524396,6 +525196,15 @@ } ] ], + "keep-collapsible-white-space-after-web-app-delete-padding-br.html": [ + "7f38f6514ac685b29e0964044196cc32559779e3", + [ + null, + { + "testdriver": true + } + ] + ], "keep-typed-collapsible-white-space-visible-after-muation.html": [ "9bc02223db5ff5c1b04d93d7cf13562d401bfbf8", [ @@ -588300,7 +589109,7 @@ ] ], "getContextAttributes.html": [ - "47b3d96233acf6b879959f48450be36beff99830", + "9c98863896573541590351b25b716c2c02aa4497", [ null, {} @@ -599560,6 +600369,28 @@ {} ] ], + "offscreencanvas.transferrable.sw.https.window.js": [ + "caa79bafaec50f060e5c4bfa2cc7ba05c9c1436f", + [ + "html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.transferrable.sw.https.window.html", + { + "script_metadata": [ + [ + "script", + "/resources/testdriver.js" + ], + [ + "script", + "/resources/testdriver-vendor.js" + ], + [ + "script", + "/service-workers/service-worker/resources/test-helpers.sub.js" + ] + ] + } + ] + ], "offscreencanvas.transferrable.w.html": [ "38f981e8f09aab76f0ed5131c83e0ee8882f1788", [ @@ -624004,6 +624835,13 @@ {} ] ], + "option-img-alt-text.tentative.html": [ + "0d47b7fe4826cff7d6598d53df1971091f8dd009", + [ + null, + {} + ] + ], "select-accessibility-minimum-target-size.tentative.html": [ "3155849a5863bf1fd205630317bc7319097c765d", [ @@ -654154,6 +654992,17 @@ {} ] ], + "buffered-flag-with-entryTypes-observer.tentative.any.js": [ + "493d1dc7a2d4805e56d5f82800eeef84892ed24c", + [ + "performance-timeline/buffered-flag-with-entryTypes-observer.tentative.any.html", + {} + ], + [ + "performance-timeline/buffered-flag-with-entryTypes-observer.tentative.any.worker.html", + {} + ] + ], "case-sensitivity.any.js": [ "3a98505ae67f7df6f617d6b9fde4af367503278e", [ @@ -657722,7 +658571,7 @@ ] ], "pointerevent_pointercapture_in_frame.html": [ - "9c26c4d5a3869bde8e201653f185a6cb023cb779", + "2d20a055904ec2be238e7b037522867156813772", [ "pointerevents/pointerevent_pointercapture_in_frame.html?mouse", { @@ -677346,7 +678195,7 @@ ] ], "fire-selectionchange-event-on-pressing-backspace.html": [ - "98d2cc6f365512b18a3fdd977d550bec1901e7c8", + "a6b8fd0c9cecbeb082fd796ee72bae64086d9367", [ null, { @@ -704765,7 +705614,7 @@ ] ], "single-touch.html": [ - "57c5cdda7ce3e2f44e4bf5e278a73a04dc6f64a0", + "9e8dc0d7bb6a6ee457274ed648e765c10e1b1e89", [ null, { @@ -704968,6 +705817,13 @@ {} ] ], + "ServiceWorkerContainer-register-from-Worker.https.html": [ + "a808c0c12b50b1c2d06f74ea45310e8c2a5558df", + [ + null, + {} + ] + ], "TrustedType-AttributeNodes.html": [ "f4269a5d40d02d372c57dd09486f3ff8b3b499a9", [ @@ -705148,7 +706004,7 @@ ] ], "WorkerGlobalScope-worker-constructor.html": [ - "86612b9d1d1c3098041963a9b5a66a9c7d415616", + "8964c72780b8fb2a575c73a90dd8230f733da8c6", [ null, {} @@ -705161,6 +706017,13 @@ {} ] ], + "block-eval-function-constructor.html": [ + "a7d61c86dc0e736ef9a7754b80500714bbee93a4", + [ + null, + {} + ] + ], "block-string-assignment-to-DOMParser-parseFromString.html": [ "6dbebd29a43486c7c9affe8f0962c97d34c062b7", [ @@ -705231,6 +706094,13 @@ {} ] ], + "block-string-assignment-to-HTMLIFrameElement-srcdoc.html": [ + "b71d838b8512384ec09b73e74ae0de310e85ba1a", + [ + null, + {} + ] + ], "block-string-assignment-to-Range-createContextualFragment.html": [ "55566589deadc94e62eff817bbefbb90c7574fb7", [ @@ -705460,7 +706330,7 @@ ] ], "trusted-types-createHTMLDocument.html": [ - "cf209cca80060d709bf975dc69ac158eb3e92835", + "38223dac7016eb766d4c3369a4d1c912290bd14b", [ null, {} @@ -705637,7 +706507,7 @@ ] ], "useragentdata.https.any.js": [ - "fa588355181a18f9413f1d13129d1f991837e316", + "73059152bd37dfec6a7d31fce605dd6b460982af", [ "ua-client-hints/useragentdata.https.any.html", { @@ -809758,7 +810628,7 @@ }, "fetch_error": { "fetch_error.py": [ - "872b4877a8e8e229f5c5e049426dd7370a59b721", + "6beb33f7d69a189cd92d9e9ca371002e2b68369c", [ null, {} @@ -810325,6 +811195,13 @@ null, {} ] + ], + "subscription_id.py": [ + "36a57111f6d503aad3467162c2db102a5608a89d", + [ + null, + {} + ] ] }, "unsubscribe": { @@ -810343,7 +811220,7 @@ ] ], "invalid.py": [ - "c286bc09ee04d72aa4048e67078084d985bdcdc3", + "0b13e949673c198f89aba72388b4230cd8bc0a5b", [ null, {}
diff --git a/third_party/blink/web_tests/external/wpt/css/CSS2/tables/collapsing-border-model-011.html b/third_party/blink/web_tests/external/wpt/css/CSS2/tables/collapsing-border-model-011.html new file mode 100644 index 0000000..9c03dde --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/CSS2/tables/collapsing-border-model-011.html
@@ -0,0 +1,17 @@ +<!DOCTYPE html> +<title>CSS Test: Tables under the collapsing borders model don't have padding</title> +<link rel="author" title="Oriol Brufau" href="obrufau@igalia.com"> +<link rel="help" href="http://www.w3.org/TR/CSS21/tables.html#collapsing-borders"> +<link rel="match" href="../../reference/ref-filled-green-100px-square-only.html"> +<style> +table { + border-collapse: collapse; + box-sizing: content-box; + width: 100px; + height: 100px; + padding: 100px; + background: green; +} +</style> +<p>Test passes if there is a filled green square.</p> +<table></table>
diff --git a/third_party/blink/web_tests/external/wpt/css/CSS2/tables/collapsing-border-model-012.html b/third_party/blink/web_tests/external/wpt/css/CSS2/tables/collapsing-border-model-012.html new file mode 100644 index 0000000..39b176ce --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/CSS2/tables/collapsing-border-model-012.html
@@ -0,0 +1,17 @@ +<!DOCTYPE html> +<title>CSS Test: Tables under the collapsing borders model don't have padding</title> +<link rel="author" title="Oriol Brufau" href="obrufau@igalia.com"> +<link rel="help" href="http://www.w3.org/TR/CSS21/tables.html#collapsing-borders"> +<link rel="match" href="../../reference/ref-filled-green-100px-square-only.html"> +<style> +table { + border-collapse: collapse; + box-sizing: border-box; + width: 100px; + height: 100px; + padding: 100px; + background: green; +} +</style> +<p>Test passes if there is a filled green square.</p> +<table></table>
diff --git a/third_party/blink/web_tests/external/wpt/css/CSS2/tables/collapsing-border-model-013.html b/third_party/blink/web_tests/external/wpt/css/CSS2/tables/collapsing-border-model-013.html new file mode 100644 index 0000000..5e5f576 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/CSS2/tables/collapsing-border-model-013.html
@@ -0,0 +1,22 @@ +<!DOCTYPE html> +<title>CSS Test: Tables under the collapsing borders model don't have padding</title> +<link rel="author" title="Oriol Brufau" href="obrufau@igalia.com"> +<link rel="help" href="http://www.w3.org/TR/CSS21/tables.html#collapsing-borders"> +<link rel="match" href="../../reference/ref-filled-green-100px-square-only.html"> +<style> +div { + float: left; + background: green; +} +table { + border-collapse: collapse; + box-sizing: content-box; + width: 100px; + height: 100px; + padding: 100px; +} +</style> +<p>Test passes if there is a filled green square.</p> +<div> + <table></table> +</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/CSS2/tables/collapsing-border-model-014.html b/third_party/blink/web_tests/external/wpt/css/CSS2/tables/collapsing-border-model-014.html new file mode 100644 index 0000000..1817752 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/CSS2/tables/collapsing-border-model-014.html
@@ -0,0 +1,22 @@ +<!DOCTYPE html> +<title>CSS Test: Tables under the collapsing borders model don't have padding</title> +<link rel="author" title="Oriol Brufau" href="obrufau@igalia.com"> +<link rel="help" href="http://www.w3.org/TR/CSS21/tables.html#collapsing-borders"> +<link rel="match" href="../../reference/ref-filled-green-100px-square-only.html"> +<style> +div { + float: left; + background: green; +} +table { + border-collapse: collapse; + box-sizing: border-box; + width: 100px; + height: 100px; + padding: 100px; +} +</style> +<p>Test passes if there is a filled green square.</p> +<div> + <table></table> +</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-masonry/tentative/parsing/masonry-auto-tracks-computed.html b/third_party/blink/web_tests/external/wpt/css/css-masonry/tentative/parsing/masonry-auto-tracks-computed.html new file mode 100644 index 0000000..2581a35 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-masonry/tentative/parsing/masonry-auto-tracks-computed.html
@@ -0,0 +1,58 @@ +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>CSS Masonry: masonry-auto-tracks getComputedStyle()</title> +<link rel="author" title="Celeste Pan" href="mailto:celestepan@microsoft.com"> +<link rel="help" href="https://drafts.csswg.org/css-grid-3"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/computed-testcommon.js"></script> +</head> +<body> +<div id="target"></div> +<style> + #target { + font-size: 40px; + } +</style> +<script> +// <track-breadth> +// <track-breadth> = <length-percentage> | <flex> | min-content | max-content | auto +test_computed_value("masonry-auto-tracks", "1px"); +test_computed_value("masonry-auto-tracks", "calc(10px + 0.5em)", "30px"); +test_computed_value("masonry-auto-tracks", "calc(10px - 0.5em)", "0px"); +test_computed_value("masonry-auto-tracks", "4%"); +test_computed_value("masonry-auto-tracks", "5fr"); +test_computed_value("masonry-auto-tracks", "min-content"); +test_computed_value("masonry-auto-tracks", "max-content"); +test_computed_value("masonry-auto-tracks", "auto"); + +// minmax( <inflexible-breadth> , <track-breadth> ) +// <inflexible-breadth> = <length-percentage> | min-content | max-content | auto +test_computed_value("masonry-auto-tracks", "minmax(1px, 5fr)"); +test_computed_value("masonry-auto-tracks", "minmax(calc(10px + 0.5em), max-content)", "minmax(30px, max-content)"); +test_computed_value("masonry-auto-tracks", "minmax(calc(10px - 0.5em), max-content)", "minmax(0px, max-content)"); +test_computed_value("masonry-auto-tracks", "minmax(4%, auto)"); +test_computed_value("masonry-auto-tracks", "minmax(min-content, calc(10px + 0.5em))", "minmax(min-content, 30px)"); +test_computed_value("masonry-auto-tracks", "minmax(auto, 4%)"); + +// fit-content( <length-percentage> ) +test_computed_value("masonry-auto-tracks", "fit-content(1px)"); +test_computed_value("masonry-auto-tracks", "fit-content(calc(10px + 0.5em))", "fit-content(30px)"); +test_computed_value("masonry-auto-tracks", "fit-content(calc(10px - 0.5em))", "fit-content(0px)"); +test_computed_value("masonry-auto-tracks", "fit-content(4%)"); + +// 0 +test_computed_value("masonry-auto-tracks", "0px"); +test_computed_value("masonry-auto-tracks", "0%"); +test_computed_value("masonry-auto-tracks", "0fr"); +test_computed_value("masonry-auto-tracks", "minmax(auto, 0%)"); +test_computed_value("masonry-auto-tracks", "fit-content(0px)"); + +// <track-size>+ +test_computed_value("masonry-auto-tracks", "1px 2px 3px 0px"); +test_computed_value("masonry-auto-tracks", "fit-content(1px) minmax(2px, 3px) 4px"); +</script> +</body> +</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-masonry/tentative/parsing/masonry-auto-tracks-invalid.html b/third_party/blink/web_tests/external/wpt/css/css-masonry/tentative/parsing/masonry-auto-tracks-invalid.html new file mode 100644 index 0000000..e23933e --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-masonry/tentative/parsing/masonry-auto-tracks-invalid.html
@@ -0,0 +1,39 @@ +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>CSS Masonry: masonry-auto-tracks with invalid values</title> +<link rel="author" title="Celeste Pan" href="mailto:celestepan@microsoft.com"> +<link rel="help" href="https://drafts.csswg.org/css-grid-3"> +<meta name="assert" content="masonry-auto-tracks supports only the grammar '<track-size>+'."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/parsing-testcommon.js"></script> +</head> +<body> +<script> +// <track-breadth> +test_invalid_value("masonry-auto-tracks", "none"); +test_invalid_value("masonry-auto-tracks", "-1px"); +test_invalid_value("masonry-auto-tracks", "-4%"); + +// minmax( <inflexible-breadth> , <track-breadth> ) +test_invalid_value("masonry-auto-tracks", "minmax(1px)"); +test_invalid_value("masonry-auto-tracks", "minmax(1px, 2px, 3px)"); +test_invalid_value("masonry-auto-tracks", "minmax(5fr, 1px)"); +test_invalid_value("masonry-auto-tracks", "minmax(6px, -7%)"); +test_invalid_value("masonry-auto-tracks", "minmax(8px, -9fr)"); + +// fit-content( <length-percentage> ) +test_invalid_value("masonry-auto-tracks", "fit-content(-1px)"); +test_invalid_value("masonry-auto-tracks", "fit-content(1px, 2px)"); +test_invalid_value("masonry-auto-tracks", "fit-content(1px auto)"); + +// <track-size>+ +test_invalid_value("masonry-auto-tracks", "2em / 3em"); +test_invalid_value("masonry-auto-tracks", "auto, 10%"); +test_invalid_value("masonry-auto-tracks", "1px [a] 1px"); +test_invalid_value("masonry-auto-tracks", "[] 1px []"); +</script> +</body> +</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-masonry/tentative/parsing/masonry-auto-tracks-valid.html b/third_party/blink/web_tests/external/wpt/css/css-masonry/tentative/parsing/masonry-auto-tracks-valid.html new file mode 100644 index 0000000..b0c1424 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-masonry/tentative/parsing/masonry-auto-tracks-valid.html
@@ -0,0 +1,57 @@ +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>CSS Masonry: masonry-auto-tracks with valid values</title> +<link rel="author" title="Celeste Pan" href="mailto:celestepan@microsoft.com"> +<link rel="help" href="https://drafts.csswg.org/css-grid-3"> +<meta name="assert" content="masonry-auto-tracks supports the full grammar '<track-size>+'."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/parsing-testcommon.js"></script> +</head> +<body> +<script> +// <track-breadth> +// <track-breadth> = <length-percentage> | <flex> | min-content | max-content | auto +test_valid_value("masonry-auto-tracks", "1px"); +test_valid_value("masonry-auto-tracks", "2em"); +test_valid_value("masonry-auto-tracks", "calc(2em + 3ex)"); +test_valid_value("masonry-auto-tracks", "4%"); +test_valid_value("masonry-auto-tracks", "5fr"); +test_valid_value("masonry-auto-tracks", "min-content"); +test_valid_value("masonry-auto-tracks", "max-content"); +test_valid_value("masonry-auto-tracks", "auto"); +test_valid_value("masonry-auto-tracks", "auto /**/", "auto"); + +// minmax( <inflexible-breadth> , <track-breadth> ) +// <inflexible-breadth> = <length-percentage> | min-content | max-content | auto +test_valid_value("masonry-auto-tracks", "minmax(1px, 5fr)"); +test_valid_value("masonry-auto-tracks", "minmax(2em, min-content)"); +test_valid_value("masonry-auto-tracks", "minmax(calc(2em + 3ex), max-content)"); +test_valid_value("masonry-auto-tracks", "minmax(4%, auto)"); +test_valid_value("masonry-auto-tracks", "minmax(5vmin, 1px)"); +test_valid_value("masonry-auto-tracks", "minmax(min-content, 2em)"); +test_valid_value("masonry-auto-tracks", "minmax(max-content, calc(2em + 3ex))"); +test_valid_value("masonry-auto-tracks", "minmax(auto, 4%)"); + +// fit-content( <length-percentage> ) +test_valid_value("masonry-auto-tracks", "fit-content(1px)"); +test_valid_value("masonry-auto-tracks", "fit-content(2em)"); +test_valid_value("masonry-auto-tracks", "fit-content(calc(2em + 3ex))"); +test_valid_value("masonry-auto-tracks", "fit-content(4%)"); + +test_valid_value("masonry-auto-tracks", "0px"); +test_valid_value("masonry-auto-tracks", "0%"); +test_valid_value("masonry-auto-tracks", "0fr"); +test_valid_value("masonry-auto-tracks", "minmax(auto, 0%)"); +test_valid_value("masonry-auto-tracks", "fit-content(0px)"); + +// <track-size>+ +test_valid_value("masonry-auto-tracks", "auto auto"); +test_valid_value("masonry-auto-tracks", "auto 10px"); +test_valid_value("masonry-auto-tracks", "1px 2px 3px 0px"); +test_valid_value("masonry-auto-tracks", "fit-content(1px) minmax(2px, 3px) 4px"); +</script> +</body> +</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-multicol/multicol-loads-indefinitely-001-crash.html b/third_party/blink/web_tests/external/wpt/css/css-multicol/multicol-loads-indefinitely-001-crash.html new file mode 100644 index 0000000..c00891c1 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-multicol/multicol-loads-indefinitely-001-crash.html
@@ -0,0 +1,20 @@ +<!DOCTYPE html> +<link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com"> +<link rel="author" title="Mozilla" href="https://www.mozilla.org/"> +<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1927270"> + +<style> +* { + max-height: 10vh; + border-top-style: dotted; + word-break: break-all; + columns: 1 0px; +} +.a { + float: right; +} +</style> + +<ol> +<li> +<div class="a">Cj"hWJ_/[8s</dt>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-scroll-snap/selection-target-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-scroll-snap/selection-target-expected.txt deleted file mode 100644 index 15f9d06..0000000 --- a/third_party/blink/web_tests/external/wpt/css/css-scroll-snap/selection-target-expected.txt +++ /dev/null
@@ -1,5 +0,0 @@ -This is a testharness.js-based test. -[FAIL] Test scrolling into view when typing - assert_true: Scroll-padding should be respected expected true got false -Harness: the test ran to completion. -
diff --git a/third_party/blink/web_tests/external/wpt/editing/other/keep-collapsible-white-space-after-web-app-delete-padding-br.html b/third_party/blink/web_tests/external/wpt/editing/other/keep-collapsible-white-space-after-web-app-delete-padding-br.html new file mode 100644 index 0000000..7f38f65 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/editing/other/keep-collapsible-white-space-after-web-app-delete-padding-br.html
@@ -0,0 +1,94 @@ +<!doctype html> +<html> +<head> +<meta charset="utf-8"> +<title>If browsers inserts a br element to make preceding collapsible white-space visible, +it should be maintained even if the web app deletes the br element</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-vendor.js"></script> +<script src="/resources/testdriver-actions.js"></script> +<script src="../include/editor-test-utils.js"></script> +<script> +"use strict"; + +document.addEventListener("DOMContentLoaded", () => { + const editingHost = document.querySelector("div[contenteditable]"); + editingHost.focus(); + const utils = new EditorTestUtils(editingHost); + + promise_test(async t => { + utils.setupEditingHost("abc d[]"); + await utils.sendBackspaceKey(); + assert_in_array( + editingHost.innerHTML, + ["abc <br>", "abc "], + `${t.name}: Deleting the first char of the second word should not make the preceding white-space invisible` + ); + const br = editingHost.querySelector("br"); + if (br) { + br.remove(); + assert_in_array( + editingHost.innerHTML, + ["abc <br>", "abc "], + `${t.name}: Browser should keep the collapsible white-space as visible even if the padding <br> is removed` + ); + } + await utils.sendKey("d"); + assert_equals( + editingHost.innerHTML, + "abc d", + `${t.name}: Typing a character should make the last white-space as an ASCII space and delete the unnecessary <br>` + ); + }, "The last ASCII white-space should be replaced with an NBSP even if <br> is removed by web app"); + + promise_test(async t => { + utils.setupEditingHost("abc d[]<div>ef</div>"); + await utils.sendBackspaceKey(); + assert_in_array( + editingHost.innerHTML, + ["abc <br><div>ef</div>", "abc <div>ef</div>"], + `${t.name}: Deleting the first char of the second word should not make the preceding white-space invisible` + ); + const br = editingHost.querySelector("br"); + if (br) { + br.remove(); + assert_in_array( + editingHost.innerHTML, + ["abc <br><div>ef</div>", "abc <div>ef</div>"], + `${t.name}: Browser should keep the collapsible white-space as visible even if the padding <br> is removed` + ); + } + await utils.sendKey("d"); + assert_equals( + editingHost.innerHTML, + "abc d<div>ef</div>", + `${t.name}: Typing a character should make the last white-space as an ASCII space and delete the unnecessary <br>` + ); + }, "The last ASCII white-space should be replaced with an NBSP even if <br> followed by a child block boundary is removed by web app"); + + promise_test(async t => { + utils.setupEditingHost("abc <br>def"); + editingHost.querySelector("br").remove(); + assert_equals( + editingHost.innerHTML, + "abc def" + ); + }, "The last ASCII white-space should not be replaced with an NBSP if following <br> is not a padding <br>"); + + promise_test(async t => { + utils.setupEditingHost(`<div contenteditable="false">abc <br></div>`); + editingHost.querySelector("br").remove(); + assert_equals( + editingHost.innerHTML, + `<div contenteditable="false">abc </div>` + ); + }, "The last ASCII white-space in non-editable Text node should not be replaced with an NBSP if following <br> is not a padding <br>"); +}, {once: true}); +</script> +</head> +<body> + <div contenteditable></div> +</body> +</html>
diff --git a/third_party/blink/web_tests/external/wpt/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.transferrable.sw.https.window.js b/third_party/blink/web_tests/external/wpt/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.transferrable.sw.https.window.js new file mode 100644 index 0000000..caa79baf --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.transferrable.sw.https.window.js
@@ -0,0 +1,33 @@ +// META: script=/resources/testdriver.js +// META: script=/resources/testdriver-vendor.js +// META: script=/service-workers/service-worker/resources/test-helpers.sub.js + +function unregisterAllServiceWorker() { + return navigator.serviceWorker.getRegistrations().then(registrations => { + return Promise.all(registrations.map(r => r.unregister())); + }); +} + +async function prepareActiveServiceWorker(script) { + await unregisterAllServiceWorker(); + const reg = await navigator.serviceWorker.register(script); + add_completion_callback(() => reg.unregister()); + await navigator.serviceWorker.ready; + return reg; +} + +let registration; + +promise_setup(async () => { + registration = await prepareActiveServiceWorker("offscreencanvas.transferrable.sw.js"); +}); + +promise_test(async () => { + const canvas = new OffscreenCanvas(100, 100); + registration.active.postMessage({ canvas }, { transfer: [canvas] }); + const data = await new Promise(resolve => navigator.serviceWorker.addEventListener("message", ev => { + resolve(ev.data); + }, { once: true })); + assert_equals(data.constructorName, "OffscreenCanvas", "Should get OffscreenCanvas from the window") + assert_true(data.canvas instanceof OffscreenCanvas, "Should get OffscreenCanvas from the service worker"); +}, "Sending and receiving OffscreenCanvas between window and service worker");
diff --git a/third_party/blink/web_tests/external/wpt/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.transferrable.sw.js b/third_party/blink/web_tests/external/wpt/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.transferrable.sw.js new file mode 100644 index 0000000..fc7265a --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.transferrable.sw.js
@@ -0,0 +1,12 @@ +onmessage = (ev) => { + const constructorName = ev.data.canvas?.constructor.name; + const canvas = new OffscreenCanvas(100, 100); + ev.source.postMessage({ + constructorName, + canvas + }, { transfer: [canvas] }); +} + +onmessageerror = (ev) => { + ev.source.postMessage({ constructorName: null }); +}
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-select-element/select-events.tentative.html b/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-select-element/customizable-select/select-events-2.tentative.html similarity index 100% rename from third_party/blink/web_tests/external/wpt/html/semantics/forms/the-select-element/select-events.tentative.html rename to third_party/blink/web_tests/external/wpt/html/semantics/forms/the-select-element/customizable-select/select-events-2.tentative.html
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-select-element/select-pseudo-open-closed.tentative.html b/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-select-element/customizable-select/select-pseudo-open.tentative.html similarity index 100% rename from third_party/blink/web_tests/external/wpt/html/semantics/forms/the-select-element/select-pseudo-open-closed.tentative.html rename to third_party/blink/web_tests/external/wpt/html/semantics/forms/the-select-element/customizable-select/select-pseudo-open.tentative.html
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-select-element/select-value-selectedOption.tentative.html b/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-select-element/customizable-select/select-value-selectedOption.tentative.html similarity index 100% rename from third_party/blink/web_tests/external/wpt/html/semantics/forms/the-select-element/select-value-selectedOption.tentative.html rename to third_party/blink/web_tests/external/wpt/html/semantics/forms/the-select-element/customizable-select/select-value-selectedOption.tentative.html
diff --git a/third_party/blink/web_tests/external/wpt/interfaces/WebCryptoAPI.idl b/third_party/blink/web_tests/external/wpt/interfaces/WebCryptoAPI.idl index ae85c1c..ff7a89c 100644 --- a/third_party/blink/web_tests/external/wpt/interfaces/WebCryptoAPI.idl +++ b/third_party/blink/web_tests/external/wpt/interfaces/WebCryptoAPI.idl
@@ -42,52 +42,77 @@ [SecureContext,Exposed=(Window,Worker)] interface SubtleCrypto { - Promise<any> encrypt(AlgorithmIdentifier algorithm, - CryptoKey key, - BufferSource data); - Promise<any> decrypt(AlgorithmIdentifier algorithm, - CryptoKey key, - BufferSource data); - Promise<any> sign(AlgorithmIdentifier algorithm, - CryptoKey key, - BufferSource data); - Promise<any> verify(AlgorithmIdentifier algorithm, - CryptoKey key, - BufferSource signature, - BufferSource data); - Promise<any> digest(AlgorithmIdentifier algorithm, - BufferSource data); + Promise<ArrayBuffer> encrypt( + AlgorithmIdentifier algorithm, + CryptoKey key, + BufferSource data + ); + Promise<ArrayBuffer> decrypt( + AlgorithmIdentifier algorithm, + CryptoKey key, + BufferSource data + ); + Promise<ArrayBuffer> sign( + AlgorithmIdentifier algorithm, + CryptoKey key, + BufferSource data + ); + Promise<boolean> verify( + AlgorithmIdentifier algorithm, + CryptoKey key, + BufferSource signature, + BufferSource data + ); + Promise<ArrayBuffer> digest( + AlgorithmIdentifier algorithm, + BufferSource data + ); - Promise<any> generateKey(AlgorithmIdentifier algorithm, - boolean extractable, - sequence<KeyUsage> keyUsages ); - Promise<any> deriveKey(AlgorithmIdentifier algorithm, - CryptoKey baseKey, - AlgorithmIdentifier derivedKeyType, - boolean extractable, - sequence<KeyUsage> keyUsages ); - Promise<ArrayBuffer> deriveBits(AlgorithmIdentifier algorithm, - CryptoKey baseKey, - optional unsigned long? length = null); + Promise<(CryptoKey or CryptoKeyPair)> generateKey( + AlgorithmIdentifier algorithm, + boolean extractable, + sequence<KeyUsage> keyUsages + ); + Promise<CryptoKey> deriveKey( + AlgorithmIdentifier algorithm, + CryptoKey baseKey, + AlgorithmIdentifier derivedKeyType, + boolean extractable, + sequence<KeyUsage> keyUsages + ); + Promise<ArrayBuffer> deriveBits( + AlgorithmIdentifier algorithm, + CryptoKey baseKey, + optional unsigned long? length = null + ); - Promise<CryptoKey> importKey(KeyFormat format, - (BufferSource or JsonWebKey) keyData, - AlgorithmIdentifier algorithm, - boolean extractable, - sequence<KeyUsage> keyUsages ); - Promise<any> exportKey(KeyFormat format, CryptoKey key); + Promise<CryptoKey> importKey( + KeyFormat format, + (BufferSource or JsonWebKey) keyData, + AlgorithmIdentifier algorithm, + boolean extractable, + sequence<KeyUsage> keyUsages + ); + Promise<(ArrayBuffer or JsonWebKey)> exportKey( + KeyFormat format, + CryptoKey key + ); - Promise<any> wrapKey(KeyFormat format, - CryptoKey key, - CryptoKey wrappingKey, - AlgorithmIdentifier wrapAlgorithm); - Promise<CryptoKey> unwrapKey(KeyFormat format, - BufferSource wrappedKey, - CryptoKey unwrappingKey, - AlgorithmIdentifier unwrapAlgorithm, - AlgorithmIdentifier unwrappedKeyAlgorithm, - boolean extractable, - sequence<KeyUsage> keyUsages ); + Promise<ArrayBuffer> wrapKey( + KeyFormat format, + CryptoKey key, + CryptoKey wrappingKey, + AlgorithmIdentifier wrapAlgorithm + ); + Promise<CryptoKey> unwrapKey( + KeyFormat format, + BufferSource wrappedKey, + CryptoKey unwrappingKey, + AlgorithmIdentifier unwrapAlgorithm, + AlgorithmIdentifier unwrappedKeyAlgorithm, + boolean extractable, + sequence<KeyUsage> keyUsages + ); }; dictionary RsaOtherPrimesInfo {
diff --git a/third_party/blink/web_tests/external/wpt/interfaces/digital-credentials.idl b/third_party/blink/web_tests/external/wpt/interfaces/digital-credentials.idl index 2207b25..e20079e 100644 --- a/third_party/blink/web_tests/external/wpt/interfaces/digital-credentials.idl +++ b/third_party/blink/web_tests/external/wpt/interfaces/digital-credentials.idl
@@ -8,10 +8,10 @@ }; dictionary DigitalCredentialRequestOptions { - sequence<DigitalCredentialsRequest> requests; + sequence<DigitalCredentialRequest> requests; }; -dictionary DigitalCredentialsRequest { +dictionary DigitalCredentialRequest { required DOMString protocol; required object data; };
diff --git a/third_party/blink/web_tests/external/wpt/interfaces/element-timing.idl b/third_party/blink/web_tests/external/wpt/interfaces/element-timing.idl index 586b508..ef73ca6 100644 --- a/third_party/blink/web_tests/external/wpt/interfaces/element-timing.idl +++ b/third_party/blink/web_tests/external/wpt/interfaces/element-timing.idl
@@ -1,7 +1,7 @@ // GENERATED CONTENT - DO NOT EDIT // Content was automatically extracted by Reffy into webref // (https://github.com/w3c/webref) -// Source: Element Timing API (https://wicg.github.io/element-timing/) +// Source: Element Timing API (https://w3c.github.io/element-timing/) [Exposed=Window] interface PerformanceElementTiming : PerformanceEntry {
diff --git a/third_party/blink/web_tests/external/wpt/interfaces/speech-api.idl b/third_party/blink/web_tests/external/wpt/interfaces/speech-api.idl index 7408548..f3967b8 100644 --- a/third_party/blink/web_tests/external/wpt/interfaces/speech-api.idl +++ b/third_party/blink/web_tests/external/wpt/interfaces/speech-api.idl
@@ -1,7 +1,7 @@ // GENERATED CONTENT - DO NOT EDIT // Content was automatically extracted by Reffy into webref // (https://github.com/w3c/webref) -// Source: Web Speech API (https://wicg.github.io/speech-api/) +// Source: Web Speech API (https://webaudio.github.io/web-speech-api/) [Exposed=Window] interface SpeechRecognition : EventTarget {
diff --git a/third_party/blink/web_tests/external/wpt/interfaces/turtledove.idl b/third_party/blink/web_tests/external/wpt/interfaces/turtledove.idl index c416760..0507297 100644 --- a/third_party/blink/web_tests/external/wpt/interfaces/turtledove.idl +++ b/third_party/blink/web_tests/external/wpt/interfaces/turtledove.idl
@@ -41,6 +41,7 @@ sequence<USVString> trustedBiddingSignalsKeys; DOMString trustedBiddingSignalsSlotSizeMode = "none"; long maxTrustedBiddingSignalsURLLength; + USVString trustedBiddingSignalsCoordinator; any userBiddingSignals; sequence<AuctionAd> ads; sequence<AuctionAd> adComponents; @@ -104,6 +105,7 @@ USVString trustedScoringSignalsURL; long maxTrustedScoringSignalsURLLength; + USVString trustedScoringSignalsCoordinator; sequence<USVString> interestGroupBuyers; Promise<any> auctionSignals; Promise<any> sellerSignals;
diff --git a/third_party/blink/web_tests/external/wpt/interfaces/wai-aria.idl b/third_party/blink/web_tests/external/wpt/interfaces/wai-aria.idl index 78083f0..deebc562 100644 --- a/third_party/blink/web_tests/external/wpt/interfaces/wai-aria.idl +++ b/third_party/blink/web_tests/external/wpt/interfaces/wai-aria.idl
@@ -42,7 +42,7 @@ [CEReactions] attribute DOMString? ariaPosInSet; [CEReactions] attribute DOMString? ariaPressed; [CEReactions] attribute DOMString? ariaReadOnly; - + [CEReactions] attribute DOMString? ariaRelevant; [CEReactions] attribute DOMString? ariaRequired; [CEReactions] attribute DOMString? ariaRoleDescription; [CEReactions] attribute DOMString? ariaRowCount;
diff --git a/third_party/blink/web_tests/external/wpt/interfaces/webauthn.idl b/third_party/blink/web_tests/external/wpt/interfaces/webauthn.idl index 46e2418..a33c85e 100644 --- a/third_party/blink/web_tests/external/wpt/interfaces/webauthn.idl +++ b/third_party/blink/web_tests/external/wpt/interfaces/webauthn.idl
@@ -309,7 +309,7 @@ }; partial dictionary AuthenticationExtensionsClientInputs { - USVString appid; + DOMString appid; }; partial dictionary AuthenticationExtensionsClientOutputs { @@ -317,7 +317,7 @@ }; partial dictionary AuthenticationExtensionsClientInputs { - USVString appidExclude; + DOMString appidExclude; }; partial dictionary AuthenticationExtensionsClientOutputs { @@ -343,7 +343,7 @@ dictionary AuthenticationExtensionsPRFInputs { AuthenticationExtensionsPRFValues eval; - record<USVString, AuthenticationExtensionsPRFValues> evalByCredential; + record<DOMString, AuthenticationExtensionsPRFValues> evalByCredential; }; partial dictionary AuthenticationExtensionsClientInputs {
diff --git a/third_party/blink/web_tests/external/wpt/performance-timeline/buffered-flag-with-entryTypes-observer.tentative.any.js b/third_party/blink/web_tests/external/wpt/performance-timeline/buffered-flag-with-entryTypes-observer.tentative.any.js new file mode 100644 index 0000000..493d1dc7 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/performance-timeline/buffered-flag-with-entryTypes-observer.tentative.any.js
@@ -0,0 +1,13 @@ +async_test(t => { + performance.mark('foo'); + // Use a timeout to ensure the remainder of the test runs after the entry is created. + t.step_timeout(() => { + // `buffered` flag set to true but with entryTypes so that + // the `buffered` flag should be ignored, thus there should be no entry. + new PerformanceObserver(() => { + assert_unreached('Should not have observed any entry!'); + }).observe({entryTypes: ['mark'], buffered: true}); + // Use a timeout to give time to the observer. + t.step_timeout(t.step_func_done(() => {}), 100); + }, 0); +}, 'PerformanceObserver without buffered flag set to false cannot see past entries.');
diff --git a/third_party/blink/web_tests/external/wpt/pointerevents/compat/pointerevent_mouseevent_key_pressed-expected.txt b/third_party/blink/web_tests/external/wpt/pointerevents/compat/pointerevent_mouseevent_key_pressed-expected.txt deleted file mode 100644 index 74c68e81..0000000 --- a/third_party/blink/web_tests/external/wpt/pointerevents/compat/pointerevent_mouseevent_key_pressed-expected.txt +++ /dev/null
@@ -1,13 +0,0 @@ -This is a testharness.js-based test. -[FAIL] Pointer events correctly show Alt pressed - promise_test: Unhandled rejection with value: object "Error: We do not support keydown and keyup mixed with other actions, please use test_driver.send_keys. See crbug.com/893480." -[FAIL] Pointer events correctly show Control pressed - promise_test: Unhandled rejection with value: object "Error: We do not support keydown and keyup mixed with other actions, please use test_driver.send_keys. See crbug.com/893480." -[FAIL] Pointer events correctly show Meta pressed - promise_test: Unhandled rejection with value: object "Error: We do not support keydown and keyup mixed with other actions, please use test_driver.send_keys. See crbug.com/893480." -[FAIL] Pointer events correctly show Shift pressed - promise_test: Unhandled rejection with value: object "Error: We do not support keydown and keyup mixed with other actions, please use test_driver.send_keys. See crbug.com/893480." -[FAIL] Pointer events correctly show Alt,Control,Meta,Shift pressed - promise_test: Unhandled rejection with value: object "Error: We do not support keydown and keyup mixed with other actions, please use test_driver.send_keys. See crbug.com/893480." -Harness: the test ran to completion. -
diff --git a/third_party/blink/web_tests/external/wpt/pointerevents/pointerevent_pointermove_after_pointerup_target_removed-expected.txt b/third_party/blink/web_tests/external/wpt/pointerevents/pointerevent_pointermove_after_pointerup_target_removed-expected.txt deleted file mode 100644 index 8d5fc17..0000000 --- a/third_party/blink/web_tests/external/wpt/pointerevents/pointerevent_pointermove_after_pointerup_target_removed-expected.txt +++ /dev/null
@@ -1,7 +0,0 @@ -This is a testharness.js-based test. -[FAIL] pointermove after pointerup which deletes the overlay should not keep expanding selection - promise_test: Unhandled rejection with value: object "Error: element event-dispatch intercepted error" -[FAIL] pointermove after pointerup which deletes the overlay and move focus to the button should not keep expanding selection - promise_test: Unhandled rejection with value: object "Error: element event-dispatch intercepted error" -Harness: the test ran to completion. -
diff --git a/third_party/blink/web_tests/external/wpt/screen-orientation/event-before-promise-expected.txt b/third_party/blink/web_tests/external/wpt/screen-orientation/event-before-promise-expected.txt deleted file mode 100644 index fe1fb673..0000000 --- a/third_party/blink/web_tests/external/wpt/screen-orientation/event-before-promise-expected.txt +++ /dev/null
@@ -1,5 +0,0 @@ -This is a testharness.js-based test. -[FAIL] The 'change' event must fire before the [[orientationPendingPromise]] is resolved. - assert_true: Expected an instance of Event expected true got false -Harness: the test ran to completion. -
diff --git a/third_party/blink/web_tests/external/wpt/screen-orientation/lock-basic-expected.txt b/third_party/blink/web_tests/external/wpt/screen-orientation/lock-basic-expected.txt deleted file mode 100644 index da1b5878..0000000 --- a/third_party/blink/web_tests/external/wpt/screen-orientation/lock-basic-expected.txt +++ /dev/null
@@ -1,7 +0,0 @@ -This is a testharness.js-based test. -[FAIL] Test that screen.orientation.lock returns a pending promise. - assert_true: Expected landscape orientation for "landscape", got "portrait-primary" expected true got false -[FAIL] Test that screen.orientation.lock() is actually async - assert_true: Expected type to start with landscape, got "portrait-primary" expected true got false -Harness: the test ran to completion. -
diff --git a/third_party/blink/web_tests/external/wpt/screen-orientation/onchange-event-expected.txt b/third_party/blink/web_tests/external/wpt/screen-orientation/onchange-event-expected.txt deleted file mode 100644 index 052fa4e..0000000 --- a/third_party/blink/web_tests/external/wpt/screen-orientation/onchange-event-expected.txt +++ /dev/null
@@ -1,5 +0,0 @@ -This is a testharness.js-based test. -[FAIL] Test that orientationchange event is fired when the orientation changes. - assert_true: The event must be fired first. expected true got false -Harness: the test ran to completion. -
diff --git a/third_party/blink/web_tests/external/wpt/trusted-types/ServiceWorkerContainer-register-from-Worker.https.html b/third_party/blink/web_tests/external/wpt/trusted-types/ServiceWorkerContainer-register-from-Worker.https.html new file mode 100644 index 0000000..a808c0c --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/trusted-types/ServiceWorkerContainer-register-from-Worker.https.html
@@ -0,0 +1,37 @@ +<!doctype html> +<html> +<head> + <meta http-equiv="Content-Security-Policy" content="require-trusted-types-for 'script';"> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> +</head> +<body> +<div id=log></div> + +<script> + +// This test checks ServiceWorkerContainer.register() from Worker scopes and +// follows the same logic as WorkerGlobalScope-importScripts/eval. For the case +// when it's called from Window scope, see worker-constructor.https.html. + +const test_setup_policy = trustedTypes.createPolicy("hurrayanythinggoes", { + createScriptURL: x => x}); +const test_url = + test_setup_policy.createScriptURL("support/ServiceWorkerContainer-register.https.js"); + +fetch_tests_from_worker(new Worker(test_url)); + +fetch_tests_from_worker(new SharedWorker(test_url)); + +if ('serviceWorker' in navigator) { + (async function() { + const scope = 'support/some/scope/for/this/test'; + let reg = await navigator.serviceWorker.getRegistration(scope); + if (reg) await reg.unregister(); + reg = await navigator.serviceWorker.register(test_url, {scope}); + fetch_tests_from_worker(reg.installing); + })(); +} + +</script> +</body>
diff --git a/third_party/blink/web_tests/external/wpt/trusted-types/WorkerGlobalScope-worker-constructor-expected.txt b/third_party/blink/web_tests/external/wpt/trusted-types/WorkerGlobalScope-worker-constructor-expected.txt new file mode 100644 index 0000000..17ab3eb5 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/trusted-types/WorkerGlobalScope-worker-constructor-expected.txt
@@ -0,0 +1,9 @@ +This is a testharness.js-based test. +[FAIL] Creating a Worker from a string should throw (shared worker scope) + assert_throws_js: Creating a Worker threw function "() => { new Worker("w"); }" threw object "ReferenceError: Worker is not defined" ("ReferenceError") expected instance of function "function TypeError() { [native code] }" ("TypeError") +[FAIL] Creating a Worker from a TrustedScriptURL should not throw (shared worker scope) + Worker is not defined +[FAIL] Creating a Worker from a string with a default policy should not throw (shared worker scope) + Worker is not defined +Harness: the test ran to completion. +
diff --git a/third_party/blink/web_tests/external/wpt/trusted-types/WorkerGlobalScope-worker-constructor.html b/third_party/blink/web_tests/external/wpt/trusted-types/WorkerGlobalScope-worker-constructor.html index 86612b9..8964c72 100644 --- a/third_party/blink/web_tests/external/wpt/trusted-types/WorkerGlobalScope-worker-constructor.html +++ b/third_party/blink/web_tests/external/wpt/trusted-types/WorkerGlobalScope-worker-constructor.html
@@ -9,6 +9,9 @@ <script> fetch_tests_from_worker(new Worker( "support/WorkerGlobalScope-worker-constructor.js")); + + fetch_tests_from_worker(new SharedWorker( + "support/WorkerGlobalScope-worker-constructor.js")); </script> </body> </html>
diff --git a/third_party/blink/web_tests/external/wpt/trusted-types/block-eval-function-constructor.html b/third_party/blink/web_tests/external/wpt/trusted-types/block-eval-function-constructor.html new file mode 100644 index 0000000..a7d61c8 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/trusted-types/block-eval-function-constructor.html
@@ -0,0 +1,20 @@ +<!DOCTYPE html> +<link rel="help" href="https://w3c.github.io/webappsec-csp/#can-compile-strings"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="support/helper.sub.js"></script> +<meta http-equiv="Content-Security-Policy" content="require-trusted-types-for 'script'"> +<script src="support/block-eval-function-constructor.js"></script> +<script> + const testSetupPolicy = trustedTypes.createPolicy("p", + { createScriptURL: s => s } + ); + + fetch_tests_from_worker(new Worker( + testSetupPolicy.createScriptURL("support/block-eval-function-constructor-worker.js") + )); + + fetch_tests_from_worker(new SharedWorker( + testSetupPolicy.createScriptURL("support/block-eval-function-constructor-worker.js") + )); +</script>
diff --git a/third_party/blink/web_tests/external/wpt/trusted-types/block-string-assignment-to-HTMLIFrameElement-srcdoc.html b/third_party/blink/web_tests/external/wpt/trusted-types/block-string-assignment-to-HTMLIFrameElement-srcdoc.html new file mode 100644 index 0000000..b71d838 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/trusted-types/block-string-assignment-to-HTMLIFrameElement-srcdoc.html
@@ -0,0 +1,54 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="support/helper.sub.js"></script> + +<meta http-equiv="Content-Security-Policy" content="require-trusted-types-for 'script';"> +<body> +<script> + // TrustedHTML assignments do not throw. + test(t => { + let p = createHTML_policy(window, 1); + let html = p.createHTML(INPUTS.HTML); + let iframe = document.createElement("iframe"); + iframe.srcdoc = html; + assert_equals(iframe.srcdoc, RESULTS.HTML); + }, "iframe.srcdoc assigned via policy (successful HTML transformation)."); + + // String assignments throw. + test(t => { + let iframe = document.createElement("iframe"); + assert_throws_js(TypeError, _ => { + iframe.srcdoc = "A string"; + }); + }, "`iframe.srcdoc = string` throws."); + + // Null assignment throws. + test(t => { + let iframe = document.createElement("iframe"); + assert_throws_js(TypeError, _ => { + iframe.srcdoc = null; + }); + }, "`iframe.srcdoc = null` throws."); + + // After default policy creation string assignment implicitly calls createHTML + test(t => { + let p = window.trustedTypes.createPolicy("default", { createHTML: + (value, _, sink) => { + assert_equals(sink, "HTMLIFrameElement srcdoc"); + return createHTMLJS(value); + } + }); + + let iframe = document.createElement("iframe"); + iframe.srcdoc = INPUTS.HTML; + assert_equals(iframe.srcdoc, RESULTS.HTML); + }, "`iframe.srcdoc = string` assigned via default policy (successful HTML transformation)."); + + // After default policy creation null assignment implicitly calls createHTML. + test(t => { + let iframe = document.createElement("iframe"); + iframe.srcdoc = null; + assert_equals(iframe.srcdoc, "null"); + }, "`iframe.srcdoc = null` assigned via default policy does not throw"); +</script>
diff --git a/third_party/blink/web_tests/external/wpt/trusted-types/support/ServiceWorkerContainer-register.https.js b/third_party/blink/web_tests/external/wpt/trusted-types/support/ServiceWorkerContainer-register.https.js new file mode 100644 index 0000000..150da42 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/trusted-types/support/ServiceWorkerContainer-register.https.js
@@ -0,0 +1,66 @@ +let test_setup_policy = trustedTypes.createPolicy("hurrayanythinggoes", { + createScriptURL: x => x +}); +importScripts(test_setup_policy.createScriptURL("/resources/testharness.js")); + +// Determine worker type (for better logging) +let worker_type = "unknown"; +if (this.DedicatedWorkerGlobalScope !== undefined) { + worker_type = "dedicated worker"; +} else if (this.SharedWorkerGlobalScope !== undefined) { + worker_type = "shared worker"; +} else if (this.ServiceWorkerGlobalScope !== undefined) { + worker_type = "service worker"; +} + +let test_policy = trustedTypes.createPolicy("xxx", { + createScriptURL: url => url.replace("play", "work") +}); + +promise_test(async t => { + assert_true("navigator" in self); + assert_true(self.navigator instanceof WorkerNavigator); +}, `WorkerNavigator exposed in ${worker_type}`); + +if ('serviceWorker' in navigator) { + + // Passing a trusted type to register() should work. + promise_test(async t => { + let trusted_url = test_policy.createScriptURL("player.https.js"); + assert_true(this.trustedTypes.isScriptURL(trusted_url)); + const scope = `scope1/for/${worker_type}`; + let reg = await self.navigator.serviceWorker.getRegistration(scope); + if (reg) await reg.unregister(); + reg = await self.navigator.serviceWorker.register(trusted_url, {scope}); + await new Promise(r => reg.addEventListener("updatefound", r)); + }, `register() with TrustedScriptURL works in ${worker_type}`); + + // Passing a plain string to register() should fail. + promise_test(async t => { + let untrusted_url = "worker.https.js"; + const scope = `scope2/for/${worker_type}`; + let reg = await self.navigator.serviceWorker.getRegistration(scope); + if (reg) await reg.unregister(); + promise_rejects_js(t, TypeError, self.navigator.serviceWorker.register(untrusted_url, {scope})); + }, `register() fails with plain string in ${worker_type}`); + + // Passing a plain string to register() should work after registering a + // default policy. + promise_test(async t => { + trustedTypes.createPolicy("default", { + createScriptURL: (url, _, sink) => { + assert_equals(sink, "ServiceWorkerContainer register"); + return url.replace("play", "work"); + } + }); + + let untrusted_url = "player.https.js"; + const scope = `scope3/for/${worker_type}`; + let reg = await self.navigator.serviceWorker.getRegistration(scope); + if (reg) await reg.unregister(); + reg = await self.navigator.serviceWorker.register(untrusted_url, {scope}); + await new Promise(r => reg.addEventListener("updatefound", r)); + }, `register() fails with plain string in ${worker_type} with a default policy`); +} + +done();
diff --git a/third_party/blink/web_tests/external/wpt/trusted-types/support/ServiceWorkerContainer-register.https.js.headers b/third_party/blink/web_tests/external/wpt/trusted-types/support/ServiceWorkerContainer-register.https.js.headers new file mode 100644 index 0000000..604e765 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/trusted-types/support/ServiceWorkerContainer-register.https.js.headers
@@ -0,0 +1 @@ +Content-Security-Policy: require-trusted-types-for 'script';
diff --git a/third_party/blink/web_tests/external/wpt/trusted-types/support/WorkerGlobalScope-importScripts.https.js b/third_party/blink/web_tests/external/wpt/trusted-types/support/WorkerGlobalScope-importScripts.https.js index c40e8550..a3ecfc48 100644 --- a/third_party/blink/web_tests/external/wpt/trusted-types/support/WorkerGlobalScope-importScripts.https.js +++ b/third_party/blink/web_tests/external/wpt/trusted-types/support/WorkerGlobalScope-importScripts.https.js
@@ -64,7 +64,10 @@ // Test default policy application: trustedTypes.createPolicy("default", { - createScriptURL: url => url.replace("play", "work") + createScriptURL: (url, _, sink) => { + assert_equals(sink, "Worker importScripts"); + return url.replace("play", "work"); + } }, true); test(t => { self.result = "Fail";
diff --git a/third_party/blink/web_tests/external/wpt/trusted-types/support/WorkerGlobalScope-worker-constructor.js b/third_party/blink/web_tests/external/wpt/trusted-types/support/WorkerGlobalScope-worker-constructor.js index 7306b18..e45a92d 100644 --- a/third_party/blink/web_tests/external/wpt/trusted-types/support/WorkerGlobalScope-worker-constructor.js +++ b/third_party/blink/web_tests/external/wpt/trusted-types/support/WorkerGlobalScope-worker-constructor.js
@@ -4,20 +4,33 @@ importScripts(test_setup_policy.createScriptURL("/resources/testharness.js")); +// Determine worker type (for better logging) +let worker_type = "unknown"; +if (this.DedicatedWorkerGlobalScope !== undefined) { + worker_type = "dedicated worker"; +} else if (this.SharedWorkerGlobalScope !== undefined) { + worker_type = "shared worker"; +} + test(() => { assert_throws_js(TypeError, () => { new Worker("w"); }, "Creating a Worker threw"); -}, "Creating a Worker from a string should throw"); +}, `Creating a Worker from a string should throw (${worker_type} scope)`); test(() => { new Worker(test_setup_policy.createScriptURL("u")); -}, "Creating a Worker from a TrustedScriptURL should not throw"); +}, `Creating a Worker from a TrustedScriptURL should not throw (${worker_type} scope)`); test(() => { trustedTypes.createPolicy("default", - { createScriptURL: s => "defaultValue" }); + { createScriptURL: (s, _, sink) => { + assert_equals(sink, 'Worker constructor'); + return "defaultValue"; + } + } + ); new Worker("s"); -}, "Creating a Worker from a string with a default policy should not throw"); +}, `Creating a Worker from a string with a default policy should not throw (${worker_type} scope)`); done();
diff --git a/third_party/blink/web_tests/external/wpt/trusted-types/support/block-eval-function-constructor-worker.js b/third_party/blink/web_tests/external/wpt/trusted-types/support/block-eval-function-constructor-worker.js new file mode 100644 index 0000000..0a74a1cd --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/trusted-types/support/block-eval-function-constructor-worker.js
@@ -0,0 +1,10 @@ +const testSetupPolicy = trustedTypes.createPolicy("p", { createScriptURL: s => s }); + +importScripts(testSetupPolicy.createScriptURL("/resources/testharness.js")); +importScripts(testSetupPolicy.createScriptURL("helper.sub.js")); + +importScripts(testSetupPolicy.createScriptURL( + "block-eval-function-constructor.js" +)); + +done();
diff --git a/third_party/blink/web_tests/external/wpt/trusted-types/support/block-eval-function-constructor.js b/third_party/blink/web_tests/external/wpt/trusted-types/support/block-eval-function-constructor.js new file mode 100644 index 0000000..83bb6063 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/trusted-types/support/block-eval-function-constructor.js
@@ -0,0 +1,33 @@ +const globalThisStr = getGlobalThisStr(); + +let compilationSink = null; +function resetSinkName() { compilationSink = null; } + +trustedTypes.createPolicy("default", { createScript: (s, _, sink) => { + compilationSink = sink; + return `modified '${s}'`; +}}); + +test(t => { + t.add_cleanup(resetSinkName); + assert_throws_js(EvalError, _ => eval("'42'")); + assert_equals(compilationSink, "eval"); +}, `Blocked eval in ${globalThisStr}.`); + +test(t => { + t.add_cleanup(resetSinkName); + assert_throws_js(EvalError, _ => eval?.("'42'")); + assert_equals(compilationSink, "eval"); +}, `Blocked indirect eval in ${globalThisStr}.`); + +const AsyncFunction = async function() {}.constructor; +const GeneratorFunction = function*() {}.constructor; +const AsyncGeneratorFunction = async function*() {}.constructor; + +[Function, AsyncFunction, GeneratorFunction, AsyncGeneratorFunction].forEach(functionConstructor => { + test(t => { + t.add_cleanup(resetSinkName); + assert_throws_js(EvalError, _ => new functionConstructor("return;")); + assert_equals(compilationSink, "Function"); + }, `Blocked ${functionConstructor.name} constructor in ${globalThisStr}.`); +});
diff --git a/third_party/blink/web_tests/external/wpt/trusted-types/support/worker.https.js b/third_party/blink/web_tests/external/wpt/trusted-types/support/worker.https.js new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/trusted-types/support/worker.https.js
diff --git a/third_party/blink/web_tests/external/wpt/trusted-types/trusted-types-createHTMLDocument.html b/third_party/blink/web_tests/external/wpt/trusted-types/trusted-types-createHTMLDocument.html index cf209cc..38223da 100644 --- a/third_party/blink/web_tests/external/wpt/trusted-types/trusted-types-createHTMLDocument.html +++ b/third_party/blink/web_tests/external/wpt/trusted-types/trusted-types-createHTMLDocument.html
@@ -43,10 +43,13 @@ doc_test(doc_type, doc => { const policy = trustedTypes.createPolicy("policy", {createHTML: x => x }); const value = policy.createHTML("hello"); - doc.body.innerHTML = value; - assert_equals(doc.body.textContent, "hello"); + const div = doc.createElement("div"); + doc.body.appendChild(div); + div.innerHTML = value; + assert_equals(div.textContent, "hello"); assert_throws_js(TypeError, - _ => { doc.body.innerHTML = "world"; }); + _ => { div.innerHTML = "world"; }); + div.remove(); }, "Trusted Type instances created in the main doc can be used."); } @@ -67,8 +70,11 @@ for (let doc_type in doc_types) { doc_test(doc_type, doc => { - doc.body.innerHTML = "shouldpass"; - assert_equals(doc.body.textContent, "shouldpass [default]"); + const div = doc.createElement("div"); + doc.body.appendChild(div); + div.innerHTML = "shouldpass"; + assert_equals(div.textContent, "shouldpass [default]"); + div.remove(); }, "Default policy applies."); } </script>
diff --git a/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/network/fetch_error/fetch_error.py b/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/network/fetch_error/fetch_error.py index 872b487..6beb33f 100644 --- a/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/network/fetch_error/fetch_error.py +++ b/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/network/fetch_error/fetch_error.py
@@ -86,9 +86,14 @@ ) on_fetch_error = wait_for_event(FETCH_ERROR_EVENT) asyncio.ensure_future( - fetch(PAGE_INVALID_URL, context=new_tab, timeout_in_seconds=0) + fetch(slow_url, context=new_tab, timeout_in_seconds=0) ) fetch_error_event = await wait_for_future_safe(on_fetch_error) + assert_fetch_error_event( + fetch_error_event, + expected_request={"url": slow_url}, + context=new_tab["context"], + ) @pytest.mark.asyncio
diff --git a/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/session/subscribe/subscription_id.py b/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/session/subscribe/subscription_id.py new file mode 100644 index 0000000..36a5711 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/session/subscribe/subscription_id.py
@@ -0,0 +1,8 @@ +import pytest +import uuid + +@pytest.mark.asyncio +async def test_subscribe_subscription_id(subscribe_events): + result = await subscribe_events(events=["browsingContext"]) + assert isinstance(result['subscription'], str) + uuid.UUID(hex=result['subscription'])
diff --git a/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/session/unsubscribe/invalid.py b/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/session/unsubscribe/invalid.py index c286bc09..0b13e949 100644 --- a/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/session/unsubscribe/invalid.py +++ b/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/session/unsubscribe/invalid.py
@@ -198,3 +198,17 @@ # Try to unsubscribe from one context with pytest.raises(InvalidArgumentException): await bidi_session.session.unsubscribe(events=["log.entryAdded"], contexts=[top_context["context"]]) + + +@pytest.mark.asyncio +@pytest.mark.parametrize("subscriptions", [None, True, 42, {}, "foo"]) +async def test_params_subscriptions_invalid_type(bidi_session, subscriptions): + with pytest.raises(InvalidArgumentException): + await bidi_session.session.unsubscribe(subscriptions=subscriptions) + + +@pytest.mark.asyncio +@pytest.mark.parametrize("subscriptions", [[""], ["12345678-1234-5678-1234-567812345678"]]) +async def test_params_subscriptions_invalid_value(bidi_session, subscriptions): + with pytest.raises(InvalidArgumentException): + await bidi_session.session.unsubscribe(subscriptions=subscriptions)
diff --git a/third_party/blink/web_tests/external/wpt/webdriver/tests/support/fixtures_bidi.py b/third_party/blink/web_tests/external/wpt/webdriver/tests/support/fixtures_bidi.py index 241ba528..ec36eb9 100644 --- a/third_party/blink/web_tests/external/wpt/webdriver/tests/support/fixtures_bidi.py +++ b/third_party/blink/web_tests/external/wpt/webdriver/tests/support/fixtures_bidi.py
@@ -65,8 +65,9 @@ subscriptions = [] async def subscribe_events(events, contexts=None): - await bidi_session.session.subscribe(events=events, contexts=contexts) + result = await bidi_session.session.subscribe(events=events, contexts=contexts) subscriptions.append((events, contexts)) + return result yield subscribe_events
diff --git a/third_party/blink/web_tests/external/wpt/websockets/cookies/006_wss_wpt_flags=https-expected.txt b/third_party/blink/web_tests/external/wpt/websockets/cookies/006_wss_wpt_flags=https-expected.txt deleted file mode 100644 index 07c06008..0000000 --- a/third_party/blink/web_tests/external/wpt/websockets/cookies/006_wss_wpt_flags=https-expected.txt +++ /dev/null
@@ -1,5 +0,0 @@ -This is a testharness.js-based test. -[FAIL] WebSockets: setting Secure cookie with document.cookie, checking ws request - assert_true: cookie should have been visible to wss expected true got false -Harness: the test ran to completion. -
diff --git a/third_party/blink/web_tests/fast/css/getComputedStyle/computed-style-listing-expected.txt b/third_party/blink/web_tests/fast/css/getComputedStyle/computed-style-listing-expected.txt index 6384093..0630ac2 100644 --- a/third_party/blink/web_tests/fast/css/getComputedStyle/computed-style-listing-expected.txt +++ b/third_party/blink/web_tests/fast/css/getComputedStyle/computed-style-listing-expected.txt
@@ -245,6 +245,7 @@ mask-repeat: repeat mask-size: auto mask-type: luminance +masonry-auto-tracks: auto masonry-direction: column masonry-fill: normal masonry-slack: normal
diff --git a/third_party/blink/web_tests/fast/css/getComputedStyle/computed-style-without-renderer-listing-expected.txt b/third_party/blink/web_tests/fast/css/getComputedStyle/computed-style-without-renderer-listing-expected.txt index 050e8c77..cdd797e5 100644 --- a/third_party/blink/web_tests/fast/css/getComputedStyle/computed-style-without-renderer-listing-expected.txt +++ b/third_party/blink/web_tests/fast/css/getComputedStyle/computed-style-without-renderer-listing-expected.txt
@@ -245,6 +245,7 @@ mask-repeat: repeat mask-size: auto mask-type: luminance +masonry-auto-tracks: auto masonry-direction: column masonry-fill: normal masonry-slack: normal
diff --git a/third_party/blink/web_tests/platform/linux/external/wpt/webdriver/tests/bidi/session/subscribe/subscription_id-expected.txt b/third_party/blink/web_tests/platform/linux/external/wpt/webdriver/tests/bidi/session/subscribe/subscription_id-expected.txt new file mode 100644 index 0000000..9982474 --- /dev/null +++ b/third_party/blink/web_tests/platform/linux/external/wpt/webdriver/tests/bidi/session/subscribe/subscription_id-expected.txt
@@ -0,0 +1,5 @@ +This is a wdspec test. +[FAIL] test_subscribe_subscription_id + KeyError: 'subscription' +Harness: the test ran to completion. +
diff --git a/third_party/blink/web_tests/platform/linux/external/wpt/webdriver/tests/bidi/session/unsubscribe/invalid-expected.txt b/third_party/blink/web_tests/platform/linux/external/wpt/webdriver/tests/bidi/session/unsubscribe/invalid-expected.txt new file mode 100644 index 0000000..fcef247 --- /dev/null +++ b/third_party/blink/web_tests/platform/linux/external/wpt/webdriver/tests/bidi/session/unsubscribe/invalid-expected.txt
@@ -0,0 +1,17 @@ +This is a wdspec test. +[FAIL] test_params_subscriptions_invalid_type[None] + TypeError: unsubscribe() got an unexpected keyword argument 'subscriptions' +[FAIL] test_params_subscriptions_invalid_type[True] + TypeError: unsubscribe() got an unexpected keyword argument 'subscriptions' +[FAIL] test_params_subscriptions_invalid_type[42] + TypeError: unsubscribe() got an unexpected keyword argument 'subscriptions' +[FAIL] test_params_subscriptions_invalid_type[subscriptions3] + TypeError: unsubscribe() got an unexpected keyword argument 'subscriptions' +[FAIL] test_params_subscriptions_invalid_type[foo] + TypeError: unsubscribe() got an unexpected keyword argument 'subscriptions' +[FAIL] test_params_subscriptions_invalid_value[subscriptions0] + TypeError: unsubscribe() got an unexpected keyword argument 'subscriptions' +[FAIL] test_params_subscriptions_invalid_value[subscriptions1] + TypeError: unsubscribe() got an unexpected keyword argument 'subscriptions' +Harness: the test ran to completion. +
diff --git a/third_party/blink/web_tests/svg/css/getComputedStyle-listing-expected.txt b/third_party/blink/web_tests/svg/css/getComputedStyle-listing-expected.txt index 7c9711a..6b94d8a 100644 --- a/third_party/blink/web_tests/svg/css/getComputedStyle-listing-expected.txt +++ b/third_party/blink/web_tests/svg/css/getComputedStyle-listing-expected.txt
@@ -245,6 +245,7 @@ mask-repeat: repeat mask-size: auto mask-type: luminance +masonry-auto-tracks: auto masonry-direction: column masonry-fill: normal masonry-slack: normal
diff --git a/third_party/blink/web_tests/virtual/customizable-select-disabled/external/wpt/html/semantics/forms/the-select-element/select-pseudo-open-closed.tentative-expected.txt b/third_party/blink/web_tests/virtual/customizable-select-disabled/external/wpt/html/semantics/forms/the-select-element/customizable-select/select-pseudo-open.tentative-expected.txt similarity index 100% rename from third_party/blink/web_tests/virtual/customizable-select-disabled/external/wpt/html/semantics/forms/the-select-element/select-pseudo-open-closed.tentative-expected.txt rename to third_party/blink/web_tests/virtual/customizable-select-disabled/external/wpt/html/semantics/forms/the-select-element/customizable-select/select-pseudo-open.tentative-expected.txt
diff --git a/third_party/blink/web_tests/virtual/customizable-select-disabled/external/wpt/html/semantics/forms/the-select-element/select-value-selectedOption.tentative-expected.txt b/third_party/blink/web_tests/virtual/customizable-select-disabled/external/wpt/html/semantics/forms/the-select-element/customizable-select/select-value-selectedOption.tentative-expected.txt similarity index 100% rename from third_party/blink/web_tests/virtual/customizable-select-disabled/external/wpt/html/semantics/forms/the-select-element/select-value-selectedOption.tentative-expected.txt rename to third_party/blink/web_tests/virtual/customizable-select-disabled/external/wpt/html/semantics/forms/the-select-element/customizable-select/select-value-selectedOption.tentative-expected.txt
diff --git a/third_party/blink/web_tests/virtual/select-parser-relaxation/external/wpt/html/semantics/forms/the-select-element/select-pseudo-open-closed.tentative-expected.txt b/third_party/blink/web_tests/virtual/select-parser-relaxation/external/wpt/html/semantics/forms/the-select-element/customizable-select/select-pseudo-open.tentative-expected.txt similarity index 100% rename from third_party/blink/web_tests/virtual/select-parser-relaxation/external/wpt/html/semantics/forms/the-select-element/select-pseudo-open-closed.tentative-expected.txt rename to third_party/blink/web_tests/virtual/select-parser-relaxation/external/wpt/html/semantics/forms/the-select-element/customizable-select/select-pseudo-open.tentative-expected.txt
diff --git a/third_party/blink/web_tests/virtual/select-parser-relaxation/external/wpt/html/semantics/forms/the-select-element/select-value-selectedOption.tentative-expected.txt b/third_party/blink/web_tests/virtual/select-parser-relaxation/external/wpt/html/semantics/forms/the-select-element/customizable-select/select-value-selectedOption.tentative-expected.txt similarity index 100% rename from third_party/blink/web_tests/virtual/select-parser-relaxation/external/wpt/html/semantics/forms/the-select-element/select-value-selectedOption.tentative-expected.txt rename to third_party/blink/web_tests/virtual/select-parser-relaxation/external/wpt/html/semantics/forms/the-select-element/customizable-select/select-value-selectedOption.tentative-expected.txt
diff --git a/third_party/blink/web_tests/webexposed/css-properties-as-js-properties-expected.txt b/third_party/blink/web_tests/webexposed/css-properties-as-js-properties-expected.txt index 6ca754d7..132812f26 100644 --- a/third_party/blink/web_tests/webexposed/css-properties-as-js-properties-expected.txt +++ b/third_party/blink/web_tests/webexposed/css-properties-as-js-properties-expected.txt
@@ -287,6 +287,7 @@ maskRepeat maskSize maskType +masonryAutoTracks masonryDirection masonryFill masonryFlow
diff --git a/third_party/blink/web_tests/webexposed/css-property-listing-expected.txt b/third_party/blink/web_tests/webexposed/css-property-listing-expected.txt index 68891c94..0e9a41a 100644 --- a/third_party/blink/web_tests/webexposed/css-property-listing-expected.txt +++ b/third_party/blink/web_tests/webexposed/css-property-listing-expected.txt
@@ -259,6 +259,7 @@ mask-repeat mask-size mask-type + masonry-auto-tracks masonry-direction masonry-fill masonry-slack
diff --git a/third_party/chromium-variations b/third_party/chromium-variations index 9be189a..7b74300 160000 --- a/third_party/chromium-variations +++ b/third_party/chromium-variations
@@ -1 +1 @@ -Subproject commit 9be189a60865bf5fed52f2d6dd76ceb54e231ddb +Subproject commit 7b74300faa5f3675b06c4f10bfaef33c760918e9
diff --git a/third_party/dawn b/third_party/dawn index 07cbbfb..ab9f198 160000 --- a/third_party/dawn +++ b/third_party/dawn
@@ -1 +1 @@ -Subproject commit 07cbbfbf05a3b1e9f22b3d0ac3203a5a70c15689 +Subproject commit ab9f198d52730b69f4a208c5afd39abb0236f76a
diff --git a/third_party/devtools-frontend/src b/third_party/devtools-frontend/src index ed19c1e..8245ed1 160000 --- a/third_party/devtools-frontend/src +++ b/third_party/devtools-frontend/src
@@ -1 +1 @@ -Subproject commit ed19c1e8985293025be2e812b86ea7619185fcfd +Subproject commit 8245ed152847c99e3313079a696637bca1d5bdd7
diff --git a/third_party/fuzztest/src b/third_party/fuzztest/src index 032f0bd..c99c121 160000 --- a/third_party/fuzztest/src +++ b/third_party/fuzztest/src
@@ -1 +1 @@ -Subproject commit 032f0bdd8c0a3800eb49131d212142a61df81b0c +Subproject commit c99c121225fcc175bdc084d83c30f3c806b75afd
diff --git a/third_party/llvm-libc/src b/third_party/llvm-libc/src index 9ee8901..cf32ae3 160000 --- a/third_party/llvm-libc/src +++ b/third_party/llvm-libc/src
@@ -1 +1 @@ -Subproject commit 9ee890194fe9d4f39b1d5114c6e291b72e6062dd +Subproject commit cf32ae379c8968df8be7b8b9b1d69115402bccc4
diff --git a/third_party/perfetto b/third_party/perfetto index 0893e2a..e324242 160000 --- a/third_party/perfetto +++ b/third_party/perfetto
@@ -1 +1 @@ -Subproject commit 0893e2af69caf8592f6e38f34ccdd4ad6615de9d +Subproject commit e324242074e2e64a65e90a2933afd3ca4413554f
diff --git a/third_party/skia b/third_party/skia index dba7f46..1398cbd 160000 --- a/third_party/skia +++ b/third_party/skia
@@ -1 +1 @@ -Subproject commit dba7f46122ba3b1cdb8890eec92aa7b3534781b6 +Subproject commit 1398cbd6b7f9af9eca3b0b5277fece4cb33a45ff
diff --git a/third_party/webgpu-cts/src b/third_party/webgpu-cts/src index 1874295..9730681 160000 --- a/third_party/webgpu-cts/src +++ b/third_party/webgpu-cts/src
@@ -1 +1 @@ -Subproject commit 18742954f642e134d9080840bdb2a884aad09776 +Subproject commit 973068171048a6ab4c9d0762d5efdae8c2c1c8c4
diff --git a/third_party/webrtc b/third_party/webrtc index 2c96934..35b6757 160000 --- a/third_party/webrtc +++ b/third_party/webrtc
@@ -1 +1 @@ -Subproject commit 2c96934699b1f75572f1dd6f508e85ab0c96a356 +Subproject commit 35b67572f28b865e81bdddfc370214c329e2f285
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml index 49d11cc..dabdf19 100644 --- a/tools/metrics/histograms/enums.xml +++ b/tools/metrics/histograms/enums.xml
@@ -17637,6 +17637,7 @@ <int value="-1134420065" label="CriticalPersistedTabData:disabled"/> <int value="-1134412904" label="PrivacySandboxSettings:disabled"/> <int value="-1134307340" label="stop-loading-in-background:enabled"/> + <int value="-1133983181" label="AccessibilityManifestV3BrailleIme:disabled"/> <int value="-1133285806" label="MacLoopbackAudioForScreenShare:disabled"/> <int value="-1132704128" label="AndroidPaymentAppsFilter:disabled"/> <int value="-1132486267" label="ProductSpecificationsMultiSpecifics:enabled"/> @@ -24692,6 +24693,7 @@ <int value="1670161209" label="ClickToOpenPDFPlaceholder:enabled"/> <int value="1670799163" label="ChromeHomeShowGoogleGWhenUrlCleared:enabled"/> <int value="1671021713" label="AutofillImportDynamicForms:disabled"/> + <int value="1671604455" label="AccessibilityManifestV3BrailleIme:enabled"/> <int value="1671985641" label="WebViewVulkan:enabled"/> <int value="1672062546" label="PasswordStrengthIndicator:disabled"/> <int value="1672637465" label="OsIntegrationSubManagers:disabled"/> @@ -26942,6 +26944,7 @@ <int value="812" label="masonry-fill"/> <int value="813" label="masonry-direction"/> <int value="814" label="masonry-flow"/> + <int value="815" label="masonry-auto-tracks"/> </enum> <!-- LINT.ThenChange(//third_party/blink/public/mojom/use_counter/metrics/css_property_id.mojom:CSSSampleId) -->
diff --git a/tools/metrics/histograms/metadata/ash/enums.xml b/tools/metrics/histograms/metadata/ash/enums.xml index 9dd25cc4..f525b26 100644 --- a/tools/metrics/histograms/metadata/ash/enums.xml +++ b/tools/metrics/histograms/metadata/ash/enums.xml
@@ -1610,6 +1610,12 @@ <int value="8" label="Unexpected click"/> </enum> +<enum name="OverviewBasedScreenshotKeyboardType"> + <int value="0" label="Non-ChromeOS Keyboard"/> + <int value="1" label="ChromeOS Keyboard WITH Screenshot key"/> + <int value="2" label="ChromeOS Keyboard WITHOUT Screenshot key"/> +</enum> + <enum name="OverviewDragAction"> <summary> The words clamshell and tablet here refer to the mode at the end of the drag
diff --git a/tools/metrics/histograms/metadata/ash/histograms.xml b/tools/metrics/histograms/metadata/ash/histograms.xml index 429647dc..3ecfb3a6 100644 --- a/tools/metrics/histograms/metadata/ash/histograms.xml +++ b/tools/metrics/histograms/metadata/ash/histograms.xml
@@ -495,6 +495,23 @@ </summary> </histogram> +<histogram name="Ash.Accelerators.OverviewBasedScreenshot.{ActionName}" + enum="OverviewBasedScreenshotKeyboardType" expires_after="2025-05-11"> + <owner>dpad@google.com</owner> + <owner>jimmyxgong@chromium.org</owner> + <owner>cros-device-enablement@google.com</owner> + <summary> + Record which type of keyboard activates a given screenshot action utilizing + the overview key. The options are [Non-ChromeOS Keyboard, ChromeOS Keyboard + with a screenshot key, or ChromeOS Keyboard without a screesnhot key]. + </summary> + <token key="ActionName"> + <variant name="TakePartialScreenshot"/> + <variant name="TakeScreenshot"/> + <variant name="TakeWindowScreenshot"/> + </token> +</histogram> + <histogram name="Ash.Accelerators.Rotation.Usage" enum="ScreenRotationAcceleratorAction" expires_after="2023-10-08"> <owner>jimmyxgong@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/gpu/histograms.xml b/tools/metrics/histograms/metadata/gpu/histograms.xml index 5d0c988..160f8ea 100644 --- a/tools/metrics/histograms/metadata/gpu/histograms.xml +++ b/tools/metrics/histograms/metadata/gpu/histograms.xml
@@ -27,6 +27,14 @@ <variant name="PassthroughProgramCache"/> </variants> +<variants name="ExoSurfaceCodePaths"> + <variant name="AppendContentsToFrame"/> + <variant name="Occluded"/> + <variant name="SolidColorDrawQuad"/> + <variant name="TextureDrawQuad"/> + <variant name="TileDrawQuad"/> +</variants> + <variants name="GPUProtectedVideoType"> <variant name="Clear" summary="Clear"/> <variant name="HardwareProtected" summary="HardwareProtected"/> @@ -1781,6 +1789,19 @@ </token> </histogram> +<histogram name="Graphics.Exo.Surface.{ExoSurfaceCodePaths}" enum="Boolean" + expires_after="2025-06-01"> + <owner>harthuang@google.com</owner> + <owner>fangzhoug@chromium.org</owner> + <owner>petermcneeley@chromium.org</owner> + <owner>chromeos-gfx-display@google.com</owner> + <summary> + Track usage of different code paths in Exo surface code. We should monitor + and remove all deprecated code path if they have never been executed. + </summary> + <token key="ExoSurfaceCodePaths" variants="ExoSurfaceCodePaths"/> +</histogram> + <histogram name="Skia.Graphite.PipelineCreationRace" enum="SkiaPipelineCreationRace" expires_after="2025-11-06"> <owner>robertphillips@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/net/histograms.xml b/tools/metrics/histograms/metadata/net/histograms.xml index efc1113c4..4501f54 100644 --- a/tools/metrics/histograms/metadata/net/histograms.xml +++ b/tools/metrics/histograms/metadata/net/histograms.xml
@@ -2495,6 +2495,21 @@ </summary> </histogram> +<histogram name="Net.HttpStreamPool.QuicTaskTime.{Result}" units="ms" + expires_after="2025-02-27"> + <owner>bashi@chromium.org</owner> + <owner>blink-network-stack@google.com</owner> + <summary> + Time from when a HttpStreamPool::QuicTask attempted a QUIC session to when + the task is completed. Recorded for each QuicTask if the task actually + attempted a session. + </summary> + <token key="Result"> + <variant name="Failure" summary="Attempt failed"/> + <variant name="Success" summary="Attempt succeeded"/> + </token> +</histogram> + <histogram name="Net.HttpStreamPool.StreamAttemptCancelCount.{Reason}" units="attempts" expires_after="2025-02-27"> <owner>bashi@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/network/histograms.xml b/tools/metrics/histograms/metadata/network/histograms.xml index da5c8f1..317c22e 100644 --- a/tools/metrics/histograms/metadata/network/histograms.xml +++ b/tools/metrics/histograms/metadata/network/histograms.xml
@@ -566,7 +566,7 @@ </histogram> <histogram name="Network.Ash.Cellular.SimLock.Policy.Notification.Event" - enum="SimLockNotificationEvent" expires_after="2025-02-20"> + enum="SimLockNotificationEvent" expires_after="2025-11-20"> <owner>hsuregan@chromium.org</owner> <owner>nikhilcn@chromium.org</owner> <owner>cros-device-enablement@google.com</owner> @@ -590,7 +590,7 @@ <histogram name="Network.Ash.Cellular.SimLock.Policy.{PinLockPolicy}.ActiveSIMLockStatus" - enum="SimPinLockType" expires_after="2025-02-20"> + enum="SimPinLockType" expires_after="2025-11-20"> <owner>hsuregan@chromium.org</owner> <owner>nikhilcn@chromium.org</owner> <owner>cros-device-enablement@google.com</owner> @@ -1180,7 +1180,7 @@ </histogram> <histogram name="Network.Cellular.ESim.Installation.NonUserErrorSuccessRate" - enum="HermesResponseStatus" expires_after="2025-02-20"> + enum="HermesResponseStatus" expires_after="2025-11-20"> <owner>nikhilcn@chromium.org</owner> <owner>hsuregan@chromium.org</owner> <owner>cros-device-enablement@google.com</owner> @@ -1395,7 +1395,7 @@ </histogram> <histogram name="Network.Cellular.ESim.RefreshInstalledProfilesLatency" - units="ms" expires_after="2025-02-20"> + units="ms" expires_after="2025-11-20"> <owner>nikhilcn@chromium.org</owner> <owner>cros-device-enablement@google.com</owner> <summary>
diff --git a/tools/metrics/histograms/metadata/safe_browsing/histograms.xml b/tools/metrics/histograms/metadata/safe_browsing/histograms.xml index b86d164..f8b50e5 100644 --- a/tools/metrics/histograms/metadata/safe_browsing/histograms.xml +++ b/tools/metrics/histograms/metadata/safe_browsing/histograms.xml
@@ -1439,7 +1439,7 @@ </histogram> <histogram name="SafeBrowsing.GmsSafeBrowsingApi.ThreatAttribute{Protocol}" - enum="SafeBrowsingApiThreatAttribute" expires_after="2025-01-22"> + enum="SafeBrowsingApiThreatAttribute" expires_after="2026-01-10"> <owner>xinghuilu@chromium.org</owner> <owner>chrome-counter-abuse-alerts@google.com</owner> <summary> @@ -1600,7 +1600,7 @@ </histogram> <histogram name="SafeBrowsing.HPRT.CanGetReputationOfUrl" - enum="BooleanCanCheckUrl" expires_after="2025-01-26"> + enum="BooleanCanCheckUrl" expires_after="2026-01-10"> <owner>xinghuilu@chromium.org</owner> <owner>chrome-counter-abuse-alerts@google.com</owner> <summary> @@ -2131,7 +2131,7 @@ </histogram> <histogram name="SafeBrowsing.PageLoadToken.ClearReason" - enum="SafeBrowsingPageLoadTokenClearReason" expires_after="2025-01-22"> + enum="SafeBrowsingPageLoadTokenClearReason" expires_after="2026-01-10"> <owner>xinghuilu@chromium.org</owner> <owner>chrome-counter-abuse-alerts@google.com</owner> <summary> @@ -3347,7 +3347,7 @@ </histogram> <histogram name="SafeBrowsing.V4LocalDatabaseManager.ThreatInfoSize" - units="verdicts" expires_after="2025-01-22"> + units="verdicts" expires_after="2026-01-10"> <owner>xinghuilu@chromium.org</owner> <owner>chrome-counter-abuse-alerts@google.com</owner> <summary> @@ -3423,7 +3423,7 @@ </histogram> <histogram name="SafeBrowsing.V4ProcessFullUpdate.RemovalsHashesCount" - units="entries" expires_after="2025-01-22"> + units="entries" expires_after="2026-01-10"> <owner>xinghuilu@chromium.org</owner> <owner>chrome-counter-abuse-alerts@google.com</owner> <summary>
diff --git a/tools/metrics/histograms/metadata/sb_client/histograms.xml b/tools/metrics/histograms/metadata/sb_client/histograms.xml index bcc4f25c..a09bcbe 100644 --- a/tools/metrics/histograms/metadata/sb_client/histograms.xml +++ b/tools/metrics/histograms/metadata/sb_client/histograms.xml
@@ -263,23 +263,6 @@ </summary> </histogram> -<histogram name="SBClientDownload.SafeDownloadOpenedLatency2.{ShowAction}" - units="ms" expires_after="2025-02-10"> - <owner>xinghuilu@chromium.org</owner> - <owner>chrome-counter-abuse-alerts@google.com</owner> - <summary> - Records the latency between when a safe download is completed and when the - user {ShowAction}. Users can open the download either from the download - shelf or from the downloads page. Note that if the user opens the same - download for several times, this metric is recorded each time. Excludes - downloads that were auto opened. - </summary> - <token key="ShowAction"> - <variant name="OpenDirectly" summary="opens the download directly"/> - <variant name="ShowInFolder" summary="clicks show in folder"/> - </token> -</histogram> - <histogram name="SBClientDownload.SavePackageFileCount" units="files" expires_after="2023-03-19"> <owner>domfc@chromium.org</owner> @@ -352,7 +335,7 @@ <histogram name="SBClientDownload.UserGestureFileType.Attributes" enum="SBClientDownloadUserGestureFileTypeAttributes" - expires_after="2025-01-22"> + expires_after="2026-01-10"> <owner>xinghuilu@chromium.org</owner> <owner>chrome-counter-abuse-alerts@google.com</owner> <summary>
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json index 7b81669..a2b3165 100644 --- a/tools/perf/core/perfetto_binary_roller/binary_deps.json +++ b/tools/perf/core/perfetto_binary_roller/binary_deps.json
@@ -6,7 +6,7 @@ }, "win": { "hash": "be278d644c8dc049d2c540e3a550d55e5152e702", - "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/1799f295a91ed394890e40071f3f0badfa47622e/trace_processor_shell.exe" + "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/e324242074e2e64a65e90a2933afd3ca4413554f/trace_processor_shell.exe" }, "linux_arm": { "hash": "a15d8362d80cfd7cd8d785cf6afc22586de688cd", @@ -22,7 +22,7 @@ }, "linux": { "hash": "d99ae0124dbfa94f91dd685422c7cb0ffd4052e0", - "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/1799f295a91ed394890e40071f3f0badfa47622e/trace_processor_shell" + "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/e324242074e2e64a65e90a2933afd3ca4413554f/trace_processor_shell" } }, "power_profile.sql": {
diff --git a/ui/accessibility/accessibility_features.cc b/ui/accessibility/accessibility_features.cc index 3758ca6..b8b3f6b6 100644 --- a/ui/accessibility/accessibility_features.cc +++ b/ui/accessibility/accessibility_features.cc
@@ -250,6 +250,14 @@ return base::FeatureList::IsEnabled(::features::kAccessibilityShakeToLocate); } +BASE_FEATURE(kAccessibilityManifestV3BrailleIme, + "AccessibilityManifestV3BrailleIme", + base::FEATURE_DISABLED_BY_DEFAULT); +bool IsAccessibilityManifestV3EnabledForBrailleIme() { + return base::FeatureList::IsEnabled( + ::features::kAccessibilityManifestV3BrailleIme); +} + BASE_FEATURE(kAccessibilityManifestV3EnhancedNetworkTts, "AccessibilityManifestV3EnhancedNetworkTts", base::FEATURE_DISABLED_BY_DEFAULT);
diff --git a/ui/accessibility/accessibility_features.h b/ui/accessibility/accessibility_features.h index 319b5c3..76419da2 100644 --- a/ui/accessibility/accessibility_features.h +++ b/ui/accessibility/accessibility_features.h
@@ -192,6 +192,9 @@ AX_BASE_EXPORT BASE_DECLARE_FEATURE(kAccessibilitySlowKeys); AX_BASE_EXPORT bool IsAccessibilitySlowKeysEnabled(); +AX_BASE_EXPORT BASE_DECLARE_FEATURE(kAccessibilityManifestV3BrailleIme); +AX_BASE_EXPORT bool IsAccessibilityManifestV3EnabledForBrailleIme(); + AX_BASE_EXPORT BASE_DECLARE_FEATURE(kAccessibilityManifestV3EnhancedNetworkTts); AX_BASE_EXPORT bool IsAccessibilityManifestV3EnabledForEnhancedNetworkTts();
diff --git a/ui/base/accelerators/global_accelerator_listener/global_accelerator_listener.cc b/ui/base/accelerators/global_accelerator_listener/global_accelerator_listener.cc index c4bbbfb..cdf35a5 100644 --- a/ui/base/accelerators/global_accelerator_listener/global_accelerator_listener.cc +++ b/ui/base/accelerators/global_accelerator_listener/global_accelerator_listener.cc
@@ -21,6 +21,10 @@ bool GlobalAcceleratorListener::RegisterAccelerator( const ui::Accelerator& accelerator, Observer* observer) { + if (IsShortcutHandlingSuspended()) { + return false; + } + AcceleratorMap::const_iterator it = accelerator_map_.find(accelerator); if (it != accelerator_map_.end()) { // The accelerator has been registered. @@ -44,6 +48,10 @@ void GlobalAcceleratorListener::UnregisterAccelerator( const ui::Accelerator& accelerator, Observer* observer) { + if (IsShortcutHandlingSuspended()) { + return; + } + auto it = accelerator_map_.find(accelerator); // We should never get asked to unregister something that we didn't register. CHECK(it != accelerator_map_.end(), base::NotFatalUntil::M130); @@ -57,8 +65,11 @@ } } -std::vector<ui::Accelerator> GlobalAcceleratorListener::UnregisterAccelerators( - Observer* observer) { +void GlobalAcceleratorListener::UnregisterAccelerators(Observer* observer) { + if (IsShortcutHandlingSuspended()) { + return; + } + std::vector<ui::Accelerator> removed_accelerators; auto it = accelerator_map_.begin(); @@ -71,8 +82,30 @@ ++it; } } +} - return removed_accelerators; +void GlobalAcceleratorListener::SetShortcutHandlingSuspended(bool suspended) { + if (shortcut_handling_suspended_ == suspended) { + return; + } + + shortcut_handling_suspended_ = suspended; + for (auto& it : accelerator_map_) { + // On Linux, when shortcut handling is suspended we cannot simply early + // return in NotifyKeyPressed (similar to what we do for non-global + // shortcuts) because we'd eat the keyboard event thereby preventing the + // user from setting the shortcut. Therefore we must unregister while + // handling is suspended and register when handling resumes. + if (shortcut_handling_suspended_) { + StopListeningForAccelerator(it.first); + } else { + StartListeningForAccelerator(it.first); + } + } +} + +bool GlobalAcceleratorListener::IsShortcutHandlingSuspended() const { + return shortcut_handling_suspended_; } bool GlobalAcceleratorListener::IsRegistrationHandledExternally() const {
diff --git a/ui/base/accelerators/global_accelerator_listener/global_accelerator_listener.h b/ui/base/accelerators/global_accelerator_listener/global_accelerator_listener.h index 70f352b..70180869 100644 --- a/ui/base/accelerators/global_accelerator_listener/global_accelerator_listener.h +++ b/ui/base/accelerators/global_accelerator_listener/global_accelerator_listener.h
@@ -54,16 +54,15 @@ // Unregister and stop listening for all accelerators of the given `observer`. // Returns a vector of the accelerators that were unregistered. - std::vector<ui::Accelerator> UnregisterAccelerators(Observer* observer); + void UnregisterAccelerators(Observer* observer); - // Begin listening to an accelerator that has already been registered by - // calling `RegisterAccelerator`. - virtual bool StartListeningForAccelerator( - const ui::Accelerator& accelerator) = 0; - // Stop listening to an accelerator that has already been registered by - // calling `RegisterAccelerator`. - virtual void StopListeningForAccelerator( - const ui::Accelerator& accelerator) = 0; + // Suspend/Resume global shortcut handling. Note that when suspending, + // RegisterAccelerator/UnregisterAccelerator/UnregisterAccelerators are not + // allowed to be called until shortcut handling has been resumed. + void SetShortcutHandlingSuspended(bool suspended); + + // Returns whether shortcut handling is currently suspended. + bool IsShortcutHandlingSuspended() const; // Called when a group of commands are registered. virtual void OnCommandsChanged(const std::string& accelerator_group_id, @@ -94,11 +93,23 @@ virtual void StartListening() = 0; virtual void StopListening() = 0; + // Begin listening to an accelerator that has already been registered by + // calling `RegisterAccelerator`. + virtual bool StartListeningForAccelerator( + const ui::Accelerator& accelerator) = 0; + // Stop listening to an accelerator that has already been registered by + // calling `RegisterAccelerator`. + virtual void StopListeningForAccelerator( + const ui::Accelerator& accelerator) = 0; + // The map of accelerators that have been successfully registered as global // accelerators and their observer. typedef std::map<ui::Accelerator, raw_ptr<Observer, CtnExperimental>> AcceleratorMap; AcceleratorMap accelerator_map_; + + // Keeps track of whether shortcut handling is currently suspended. + bool shortcut_handling_suspended_ = false; }; } // namespace ui
diff --git a/ui/events/ash/keyboard_capability.cc b/ui/events/ash/keyboard_capability.cc index 4c6836ea..e603e12 100644 --- a/ui/events/ash/keyboard_capability.cc +++ b/ui/events/ash/keyboard_capability.cc
@@ -533,6 +533,16 @@ } KeyboardCapability::KeyboardInfo::KeyboardInfo() = default; +KeyboardCapability::KeyboardInfo::KeyboardInfo( + DeviceType device_type, + KeyboardTopRowLayout top_row_layout, + std::vector<uint32_t> top_row_scan_codes, + std::vector<TopRowActionKey> top_row_action_keys) + : device_type(device_type), + top_row_layout(top_row_layout), + top_row_scan_codes(std::move(top_row_scan_codes)), + top_row_action_keys(std::move(top_row_action_keys)) {} + KeyboardCapability::KeyboardInfo::KeyboardInfo(KeyboardInfo&&) = default; KeyboardCapability::KeyboardInfo& KeyboardCapability::KeyboardInfo::operator=( KeyboardInfo&&) = default; @@ -1308,6 +1318,16 @@ return base::Contains(keyboard_info->top_row_action_keys, action_key); } +bool KeyboardCapability::HasTopRowActionKey(int device_id, + TopRowActionKey action_key) const { + auto keyboard = FindKeyboardWithId(device_id); + if (!keyboard) { + return false; + } + + return HasTopRowActionKey(*keyboard, action_key); +} + bool KeyboardCapability::HasTopRowActionKeyOnAnyKeyboard( TopRowActionKey action_key) const { for (const ui::KeyboardDevice& keyboard :
diff --git a/ui/events/ash/keyboard_capability.h b/ui/events/ash/keyboard_capability.h index 06d355c..f41a881 100644 --- a/ui/events/ash/keyboard_capability.h +++ b/ui/events/ash/keyboard_capability.h
@@ -184,6 +184,10 @@ struct KeyboardInfo { KeyboardInfo(); + KeyboardInfo(DeviceType device_type, + KeyboardTopRowLayout top_row_layout, + std::vector<uint32_t> top_row_scan_codes, + std::vector<TopRowActionKey> top_row_action_keys); KeyboardInfo(KeyboardInfo&&); KeyboardInfo& operator=(KeyboardInfo&&); KeyboardInfo(const KeyboardInfo&) = delete; @@ -289,6 +293,7 @@ // Check if a given `action_key` exists on the given keyboard. bool HasTopRowActionKey(const KeyboardDevice& keyboard, TopRowActionKey action_key) const; + bool HasTopRowActionKey(int device_id, TopRowActionKey action_key) const; bool HasTopRowActionKeyOnAnyKeyboard(TopRowActionKey action_key) const; // Check if the globe key exists on the given keyboard.
diff --git a/ui/events/ozone/evdev/libgestures_glue/gesture_feedback.cc b/ui/events/ozone/evdev/libgestures_glue/gesture_feedback.cc index 2261846..a76ba73 100644 --- a/ui/events/ozone/evdev/libgestures_glue/gesture_feedback.cc +++ b/ui/events/ozone/evdev/libgestures_glue/gesture_feedback.cc
@@ -7,7 +7,10 @@ #include <stddef.h> #include <time.h> +#include <algorithm> +#include <string> #include <utility> +#include <vector> #include "base/command_line.h" #include "base/functional/bind.h" @@ -17,6 +20,7 @@ #include "base/strings/string_number_conversions.h" #include "base/strings/string_util.h" #include "base/strings/stringprintf.h" +#include "base/strings/to_string.h" #include "base/task/thread_pool.h" #include "ui/events/ozone/evdev/libgestures_glue/gesture_property_provider.h" @@ -31,14 +35,11 @@ // Return the values in an array in one string. Used for touch logging. template <typename T> -std::string DumpArrayProperty(const std::vector<T>& value, const char* format) { - std::string ret; - for (size_t i = 0; i < value.size(); ++i) { - if (i > 0) - ret.append(", "); - ret.append(base::StringPrintfNonConstexpr(format, value[i])); - } - return ret; +std::string DumpArrayProperty(const std::vector<T>& value) { + std::vector<std::string> strs; + strs.reserve(value.size()); + std::ranges::transform(value, std::back_inserter(strs), &base::ToString<T>); + return base::JoinString(strs, ", "); } // Return the values in a gesture property in one string. Used for touch @@ -46,15 +47,15 @@ std::string DumpGesturePropertyValue(GesturesProp* property) { switch (property->type()) { case GesturePropertyProvider::PT_INT: - return DumpArrayProperty(property->GetIntValue(), "%d"); + return DumpArrayProperty(property->GetIntValue()); case GesturePropertyProvider::PT_SHORT: - return DumpArrayProperty(property->GetShortValue(), "%d"); + return DumpArrayProperty(property->GetShortValue()); case GesturePropertyProvider::PT_BOOL: - return DumpArrayProperty(property->GetBoolValue(), "%d"); + return DumpArrayProperty(property->GetBoolValue()); case GesturePropertyProvider::PT_STRING: return "\"" + property->GetStringValue() + "\""; case GesturePropertyProvider::PT_REAL: - return DumpArrayProperty(property->GetDoubleValue(), "%lf"); + return DumpArrayProperty(property->GetDoubleValue()); default: NOTREACHED(); }
diff --git a/ui/gl/BUILD.gn b/ui/gl/BUILD.gn index 2a0b5af..93bb1e4 100644 --- a/ui/gl/BUILD.gn +++ b/ui/gl/BUILD.gn
@@ -256,8 +256,14 @@ "swap_chain_presenter.h", "vsync_provider_win.cc", "vsync_provider_win.h", + "vsync_provider_win_dcomp.cc", + "vsync_provider_win_dcomp.h", "vsync_thread_win.cc", "vsync_thread_win.h", + "vsync_thread_win_dcomp.cc", + "vsync_thread_win_dcomp.h", + "vsync_thread_win_dxgi.cc", + "vsync_thread_win_dxgi.h", ] libs = [
diff --git a/ui/gl/direct_composition_support.cc b/ui/gl/direct_composition_support.cc index 424f9fe..d26cfa4b 100644 --- a/ui/gl/direct_composition_support.cc +++ b/ui/gl/direct_composition_support.cc
@@ -618,6 +618,52 @@ } // namespace +// Pointers to DirectComposition functions, dcomp.dll loaded at runtime in +// InitializeDirectComposition when compositor clock vsync interval is enabled. +// DcompositionWaitForCompositorClock function pointer +using PFN_DCOMPOSITION_WAIT = HRESULT(WINAPI*)(UINT count, + const HANDLE* handles, + DWORD timeoutInMs); +PFN_DCOMPOSITION_WAIT g_wait_for_compositor_clock_function = nullptr; + +// DCompositionGetFrameId function pointer +using PFN_DCOMPOSITION_GET_FRAME_ID = + HRESULT(WINAPI*)(COMPOSITION_FRAME_ID_TYPE frameIdType, + COMPOSITION_FRAME_ID* frameId); +PFN_DCOMPOSITION_GET_FRAME_ID g_get_frame_id_function = nullptr; + +// DCompositionGetStatistics function pointer +using PFN_DCOMPOSITION_GET_STATISTICS = + HRESULT(WINAPI*)(COMPOSITION_FRAME_ID frameId, + COMPOSITION_FRAME_STATS* frameStats, + UINT targetIdCount, + COMPOSITION_TARGET_ID* targetIds, + UINT* actualTargetIdCount); +PFN_DCOMPOSITION_GET_STATISTICS g_get_statistics_function = nullptr; + +HRESULT DCompositionWaitForCompositorClock(UINT count, + const HANDLE* handles, + DWORD timeoutInMs) { + DCHECK(g_wait_for_compositor_clock_function); + return g_wait_for_compositor_clock_function(count, handles, timeoutInMs); +} + +HRESULT DCompositionGetFrameId(COMPOSITION_FRAME_ID_TYPE frameIdType, + COMPOSITION_FRAME_ID* frameId) { + DCHECK(g_get_frame_id_function); + return g_get_frame_id_function(frameIdType, frameId); +} + +HRESULT DCompositionGetStatistics(COMPOSITION_FRAME_ID frameId, + COMPOSITION_FRAME_STATS* frameStats, + UINT targetIdCount, + COMPOSITION_TARGET_ID* targetIds, + UINT* actualTargetIdCount) { + DCHECK(g_get_statistics_function); + return g_get_statistics_function(frameId, frameStats, targetIdCount, + targetIds, actualTargetIdCount); +} + void InitializeDirectComposition( Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device) { DCHECK(!g_dcomp_device); @@ -675,6 +721,22 @@ g_d3d11_device = d3d11_device.Detach(); + if (features::UseCompositorClockVSyncInterval()) { + g_get_frame_id_function = reinterpret_cast<PFN_DCOMPOSITION_GET_FRAME_ID>( + ::GetProcAddress(dcomp_module, "DCompositionGetFrameId")); + CHECK(g_get_frame_id_function); + + g_get_statistics_function = + reinterpret_cast<PFN_DCOMPOSITION_GET_STATISTICS>( + ::GetProcAddress(dcomp_module, "DCompositionGetStatistics")); + CHECK(g_get_statistics_function); + + g_wait_for_compositor_clock_function = + reinterpret_cast<PFN_DCOMPOSITION_WAIT>(::GetProcAddress( + dcomp_module, "DCompositionWaitForCompositorClock")); + CHECK(g_wait_for_compositor_clock_function); + } + UpdateVideoProcessorAutoHDRSupport(); }
diff --git a/ui/gl/direct_composition_support.h b/ui/gl/direct_composition_support.h index f07c68f6..60adc67 100644 --- a/ui/gl/direct_composition_support.h +++ b/ui/gl/direct_composition_support.h
@@ -20,6 +20,22 @@ namespace gl { +// Wrapper for DCompositionWaitForCompositorClock Win32 dcomp.h function +HRESULT DCompositionWaitForCompositorClock(UINT count, + const HANDLE* handles, + DWORD timeoutInMs); + +// Wrapper for DcompositionGetFrameId Win32 dcomp.h function +HRESULT DCompositionGetFrameId(COMPOSITION_FRAME_ID_TYPE frameIdType, + COMPOSITION_FRAME_ID* frameId); + +// Wrapper for DCompositionGetStatistics Win32 dcomp.h function +HRESULT DCompositionGetStatistics(COMPOSITION_FRAME_ID frameId, + COMPOSITION_FRAME_STATS* frameStats, + UINT targetIdCount, + COMPOSITION_TARGET_ID* targetIds, + UINT* actualTargetIdCount); + // Initialize direct composition with the given d3d11 device. GL_EXPORT void InitializeDirectComposition( Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device);
diff --git a/ui/gl/gl_features.cc b/ui/gl/gl_features.cc index bb1d50e..70cbbcb 100644 --- a/ui/gl/gl_features.cc +++ b/ui/gl/gl_features.cc
@@ -7,6 +7,7 @@ #include "base/command_line.h" #include "base/feature_list.h" #include "base/strings/string_split.h" +#include "base/win/windows_version.h" #include "build/build_config.h" #include "ui/gl/gl_switches.h" @@ -161,6 +162,18 @@ BASE_FEATURE(kUsePrimaryMonitorVSyncIntervalOnSV3, "UsePrimaryMonitorVSyncIntervalOnSV3", base::FEATURE_ENABLED_BY_DEFAULT); + +// If true, VsyncThreadWin will use the compositor clock +// to determine the vsync interval. +BASE_FEATURE(kUseCompositorClockVSyncInterval, + "UseCompositorClockVSyncInterval", + base::FEATURE_DISABLED_BY_DEFAULT); + +bool UseCompositorClockVSyncInterval() { + return base::win::GetVersion() >= base::win::Version::WIN11_24H2 && + base::FeatureList::IsEnabled( + features::kUseCompositorClockVSyncInterval); +} #endif // BUILDFLAG(IS_WIN) bool UseGpuVsync() {
diff --git a/ui/gl/gl_features.h b/ui/gl/gl_features.h index 40efeaa..83e63d33 100644 --- a/ui/gl/gl_features.h +++ b/ui/gl/gl_features.h
@@ -18,6 +18,9 @@ // Controls if GPU should synchronize presentation with vsync. GL_EXPORT bool UseGpuVsync(); +// Controls if vsync interval should be based on compositor clock. +GL_EXPORT bool UseCompositorClockVSyncInterval(); + #if BUILDFLAG(ENABLE_VALIDATING_COMMAND_DECODER) // All features in alphabetical order. The features should be documented // alongside the definition of their values in the .cc file. @@ -32,6 +35,7 @@ #if BUILDFLAG(IS_WIN) GL_EXPORT BASE_DECLARE_FEATURE(kUsePrimaryMonitorVSyncIntervalOnSV3); +GL_EXPORT BASE_DECLARE_FEATURE(kUseCompositorClockVSyncInterval); #endif // BUILDFLAG(IS_WIN) GL_EXPORT bool IsAndroidFrameDeadlineEnabled();
diff --git a/ui/gl/vsync_provider_win_dcomp.cc b/ui/gl/vsync_provider_win_dcomp.cc new file mode 100644 index 0000000..50cf777 --- /dev/null +++ b/ui/gl/vsync_provider_win_dcomp.cc
@@ -0,0 +1,54 @@ +// Copyright 2024 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/gl/vsync_provider_win_dcomp.h" + +#include "base/trace_event/typed_macros.h" +#include "ui/gl/direct_composition_support.h" + +namespace gl { + +VSyncProviderWinDComp::VSyncProviderWinDComp() = default; + +VSyncProviderWinDComp::~VSyncProviderWinDComp() = default; + +void VSyncProviderWinDComp::GetVSyncParameters(UpdateVSyncCallback callback) { + base::TimeTicks timebase; + base::TimeDelta interval; + if (GetVSyncParametersIfAvailable(&timebase, &interval)) { + std::move(callback).Run(timebase, interval); + } +} + +// Use the compositor clock to determine vsync interval (as opposed +// to algining with primary monitor) to more optimally align vsync +// interval to video FPS in multi-display mixed refresh rate or VRR configs. +bool VSyncProviderWinDComp::GetVSyncParametersIfAvailable( + base::TimeTicks* out_timebase, + base::TimeDelta* out_interval) { + TRACE_EVENT0("gpu", "VSyncProviderWinDComp::GetVSyncIntervalIfAvailable"); + HRESULT hr = S_OK; + + COMPOSITION_FRAME_ID frame_id; + hr = gl::DCompositionGetFrameId(COMPOSITION_FRAME_ID_COMPLETED, &frame_id); + CHECK_EQ(S_OK, hr); + + COMPOSITION_FRAME_STATS stats; + hr = gl::DCompositionGetStatistics(frame_id, &stats, 0, nullptr, nullptr); + CHECK_EQ(S_OK, hr); + + *out_timebase = base::TimeTicks::FromQPCValue(stats.startTime); + *out_interval = base::TimeDelta::FromQPCValue(stats.framePeriod); + return true; +} + +bool VSyncProviderWinDComp::SupportGetVSyncParametersIfAvailable() const { + return true; +} + +bool VSyncProviderWinDComp::IsHWClock() const { + return true; +} + +} // namespace gl
diff --git a/ui/gl/vsync_provider_win_dcomp.h b/ui/gl/vsync_provider_win_dcomp.h new file mode 100644 index 0000000..4072385e --- /dev/null +++ b/ui/gl/vsync_provider_win_dcomp.h
@@ -0,0 +1,33 @@ +// Copyright 2024 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_GL_VSYNC_PROVIDER_WIN_DCOMP_H_ +#define UI_GL_VSYNC_PROVIDER_WIN_DCOMP_H_ + +#include "ui/gl/vsync_provider_win.h" + +namespace gl { +// gfx::VSyncProvider implementation that utilizes the compositor clock to +// determine vsync parameters (as opposed to VSyncProviderWin, where parameters +// are calculated via DWM or QueryDisplayConfig) +class GL_EXPORT VSyncProviderWinDComp : public gfx::VSyncProvider { + public: + VSyncProviderWinDComp(); + + VSyncProviderWinDComp(const VSyncProviderWinDComp&) = delete; + VSyncProviderWinDComp& operator=(const VSyncProviderWinDComp&) = delete; + + ~VSyncProviderWinDComp() override; + + // gfx::VSyncProvider overrides; + void GetVSyncParameters(UpdateVSyncCallback callback) override; + bool GetVSyncParametersIfAvailable(base::TimeTicks* timebase, + base::TimeDelta* interval) override; + bool SupportGetVSyncParametersIfAvailable() const override; + bool IsHWClock() const override; +}; + +} // namespace gl + +#endif // UI_GL_VSYNC_PROVIDER_WIN_DCOMP_H_
diff --git a/ui/gl/vsync_thread_win.cc b/ui/gl/vsync_thread_win.cc index f3e35d77..dd405166 100644 --- a/ui/gl/vsync_thread_win.cc +++ b/ui/gl/vsync_thread_win.cc
@@ -16,129 +16,30 @@ #include "base/win/windows_version.h" #include "ui/gl/direct_composition_support.h" #include "ui/gl/gl_features.h" +#include "ui/gl/vsync_thread_win_dcomp.h" +#include "ui/gl/vsync_thread_win_dxgi.h" namespace gl { namespace { - // Whether the current thread holds the `VSyncThreadWin` lock. thread_local bool g_current_thread_holds_lock = false; - -// Check if a DXGI adapter is stale and needs to be replaced. This can happen -// e.g. when detaching/reattaching remote desktop sessions and causes subsequent -// WaitForVSyncs on the stale adapter/output to return instantly. -bool DXGIFactoryIsCurrent(IDXGIAdapter* dxgi_adapter) { - CHECK(dxgi_adapter); - - HRESULT hr = S_OK; - Microsoft::WRL::ComPtr<IDXGIFactory1> dxgi_factory; - hr = dxgi_adapter->GetParent(IID_PPV_ARGS(&dxgi_factory)); - CHECK_EQ(S_OK, hr); - return dxgi_factory->IsCurrent(); -} - -// Create a new factory and find a DXGI adapter matching a LUID. This is useful -// if we have a previous adapter whose factory has become stale. -Microsoft::WRL::ComPtr<IDXGIAdapter> FindDXGIAdapterOnNewFactory( - const LUID luid) { - HRESULT hr = S_OK; - - Microsoft::WRL::ComPtr<IDXGIFactory1> factory; - hr = CreateDXGIFactory1(IID_PPV_ARGS(&factory)); - CHECK_EQ(S_OK, hr); - - Microsoft::WRL::ComPtr<IDXGIAdapter1> new_adapter; - for (uint32_t i = 0;; i++) { - hr = factory->EnumAdapters1(i, &new_adapter); - if (hr == DXGI_ERROR_NOT_FOUND) { - break; - } - if (FAILED(hr)) { - DLOG(ERROR) << "EnumAdapters1 failed: " - << logging::SystemErrorCodeToString(hr); - return nullptr; - } - - DXGI_ADAPTER_DESC1 new_adapter_desc; - hr = new_adapter->GetDesc1(&new_adapter_desc); - CHECK_EQ(S_OK, hr); - - if (new_adapter_desc.AdapterLuid.HighPart == luid.HighPart && - new_adapter_desc.AdapterLuid.LowPart == luid.LowPart) { - return new_adapter; - } - } - - DLOG(ERROR) << "Failed to find DXGI adapter with matching LUID"; - return nullptr; -} - -// Return true if |output| is on |monitor|. -bool DXGIOutputIsOnMonitor(IDXGIOutput* output, const HMONITOR monitor) { - CHECK(output); - - DXGI_OUTPUT_DESC desc = {}; - HRESULT hr = output->GetDesc(&desc); - CHECK_EQ(S_OK, hr); - return desc.Monitor == monitor; -} - -Microsoft::WRL::ComPtr<IDXGIOutput> DXGIOutputFromMonitor( - HMONITOR monitor, - IDXGIAdapter* dxgi_adapter) { - CHECK(dxgi_adapter); - - HRESULT hr = S_OK; - - Microsoft::WRL::ComPtr<IDXGIOutput> output; - for (uint32_t i = 0;; i++) { - hr = dxgi_adapter->EnumOutputs(i, &output); - if (hr == DXGI_ERROR_NOT_FOUND) { - break; - } - if (FAILED(hr)) { - DLOG(ERROR) << "EnumOutputs failed: " - << logging::SystemErrorCodeToString(hr); - return nullptr; - } - - if (DXGIOutputIsOnMonitor(output.Get(), monitor)) { - return output; - } - } - - DLOG(ERROR) << "Failed to find DXGI output with matching monitor"; - return nullptr; -} - -Microsoft::WRL::ComPtr<IDXGIAdapter> GetAdapter(IDXGIDevice* device) { - CHECK(device); - - Microsoft::WRL::ComPtr<IDXGIAdapter> adapter; - CHECK_EQ(S_OK, device->GetAdapter(&adapter)); - return adapter; -} - -LUID GetLuid(IDXGIAdapter* adapter) { - CHECK(adapter); - - DXGI_ADAPTER_DESC desc; - HRESULT hr = adapter->GetDesc(&desc); - CHECK_EQ(S_OK, hr); - return desc.AdapterLuid; -} } // namespace // static VSyncThreadWin* VSyncThreadWin::GetInstance() { static VSyncThreadWin* vsync_thread = []() -> VSyncThreadWin* { - Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device( - GetDirectCompositionD3D11Device()); - if (!d3d11_device) { - return nullptr; + if (features::UseCompositorClockVSyncInterval()) { + return new VSyncThreadWinDComp(); + } else { + Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device( + GetDirectCompositionD3D11Device()); + if (!d3d11_device) { + return nullptr; + } + Microsoft::WRL::ComPtr<IDXGIDevice> dxgi_device; + CHECK_EQ(d3d11_device.As(&dxgi_device), S_OK); + return new VSyncThreadWinDXGI(std::move(dxgi_device)); } - Microsoft::WRL::ComPtr<IDXGIDevice> dxgi_device; - CHECK_EQ(d3d11_device.As(&dxgi_device), S_OK); - return new VSyncThreadWin(std::move(dxgi_device)); }(); return vsync_thread; } @@ -172,11 +73,7 @@ std::optional<base::AutoLock> auto_lock_; }; -VSyncThreadWin::VSyncThreadWin(Microsoft::WRL::ComPtr<IDXGIDevice> dxgi_device) - : vsync_thread_("GpuVSyncThread"), - vsync_provider_(gfx::kNullAcceleratedWidget), - dxgi_adapter_(GetAdapter(dxgi_device.Get())), - original_adapter_luid_(GetLuid(dxgi_adapter_.Get())) { +VSyncThreadWin::VSyncThreadWin() : vsync_thread_("GpuVSyncThread") { is_suspended_ = base::PowerMonitor::GetInstance() ->AddPowerSuspendObserverAndReturnSuspendedState(this); vsync_thread_.StartWithOptions( @@ -223,71 +120,25 @@ PostTaskIfNeeded(); } -base::TimeDelta VSyncThreadWin::GetVsyncInterval() { - base::TimeTicks vsync_timebase; +void VSyncThreadWin::WaitForVSync() { base::TimeDelta vsync_interval; - // This is a simplified initial approach to fix crbug.com/1456399 - // In Windows SV3 builds DWM will operate with per monitor refresh - // rates. As a result of this, DwmGetCompositionTimingInfo is no longer - // guaranteed to align with the primary monitor but will instead align - // with the current highest refresh rate monitor. This can cause issues - // in clients which may be waiting on the primary monitor's vblank as - // the reported interval may no longer match with the vblank wait. - // To work around this discrepancy get the VSync interval directly from - // monitor associated with window_ or the primary monitor. - static bool use_sv3_workaround = - base::win::GetVersion() > base::win::Version::WIN11_22H2 && - base::FeatureList::IsEnabled( - features::kUsePrimaryMonitorVSyncIntervalOnSV3); + const base::TimeTicks wait_for_vsync_start_time = base::TimeTicks::Now(); + bool wait_succeeded = WaitForVSyncImpl(&vsync_interval); + const base::TimeDelta wait_for_vsync_elapsed_time = + base::TimeTicks::Now() - wait_for_vsync_start_time; - const bool get_vsync_params_succeeded = - use_sv3_workaround - ? vsync_provider_.GetVSyncIntervalIfAvailable(&vsync_interval) - : vsync_provider_.GetVSyncParametersIfAvailable(&vsync_timebase, - &vsync_interval); - DCHECK(get_vsync_params_succeeded); - return vsync_interval; -} - -void VSyncThreadWin::WaitForVSync() { - base::TimeDelta vsync_interval = GetVsyncInterval(); - - if (!dxgi_adapter_ || !DXGIFactoryIsCurrent(dxgi_adapter_.Get())) { - TRACE_EVENT("gpu", "DXGIFactoryIsCurrent non-current factory"); - dxgi_adapter_ = FindDXGIAdapterOnNewFactory(original_adapter_luid_); - primary_output_.Reset(); - } - - // From Raymond Chen's blog "How do I get a handle to the primary monitor?" - // https://devblogs.microsoft.com/oldnewthing/20141106-00/?p=43683 - const HMONITOR monitor = MonitorFromWindow(nullptr, MONITOR_DEFAULTTOPRIMARY); - if (primary_output_ && - !DXGIOutputIsOnMonitor(primary_output_.Get(), monitor)) { - TRACE_EVENT("gpu", "DXGIOutputIsOnMonitor primary monitor changed"); - primary_output_.Reset(); - } - - if (!primary_output_ && dxgi_adapter_) { - primary_output_ = DXGIOutputFromMonitor(monitor, dxgi_adapter_.Get()); - } - - const base::TimeTicks wait_for_vblank_start_time = base::TimeTicks::Now(); - const bool wait_for_vblank_succeeded = - primary_output_ && SUCCEEDED(primary_output_->WaitForVBlank()); - - // WaitForVBlank returns very early instead of waiting until vblank when the - // monitor goes to sleep. We use 1ms as a threshold for the duration of - // WaitForVBlank and fallback to Sleep() if it returns before that. This - // could happen during normal operation for the first call after the vsync - // thread becomes non-idle, but it shouldn't happen often. + // WaitForVBlank and DCompositionWaitForCompositorClock returns very early + // instead of waiting until vblank when the monitor goes to sleep or is + // unplugged (nothing to present due to desktop occlusion). We use 1ms as + // a threshhold for the duration of the wait functions and fallback to + // Sleep() if it returns before that. This could happen during normal + // operation for the first call after the vsync thread becomes non-idle, + // but it shouldn't happen often. constexpr auto kVBlankIntervalThreshold = base::Milliseconds(1); - const base::TimeDelta wait_for_vblank_elapsed_time = - base::TimeTicks::Now() - wait_for_vblank_start_time; - if (!wait_for_vblank_succeeded || - wait_for_vblank_elapsed_time < kVBlankIntervalThreshold) { - TRACE_EVENT2("gpu", "WaitForVSync Sleep", "has adapter", !!dxgi_adapter_, - "has output", !!primary_output_); + if (!wait_succeeded || + wait_for_vsync_elapsed_time < kVBlankIntervalThreshold) { + TRACE_EVENT0("gpu", "WaitForVSync Sleep"); base::Time::ActivateHighResolutionTimer(true); Sleep(static_cast<DWORD>(vsync_interval.InMillisecondsRoundedUp())); base::Time::ActivateHighResolutionTimer(false);
diff --git a/ui/gl/vsync_thread_win.h b/ui/gl/vsync_thread_win.h index b6ff1b0..4e864285 100644 --- a/ui/gl/vsync_thread_win.h +++ b/ui/gl/vsync_thread_win.h
@@ -24,7 +24,7 @@ // for the primary monitor and notifies observers. Observers can be added or // removed from any thread. The vsync thread sleeps when there are no observers. // This is used by ExternalBeginFrameSourceWin. -class GL_EXPORT VSyncThreadWin final : public base::PowerSuspendObserver { +class GL_EXPORT VSyncThreadWin : public base::PowerSuspendObserver { public: static VSyncThreadWin* GetInstance(); @@ -49,31 +49,31 @@ void AddObserver(VSyncObserver* obs); void RemoveObserver(VSyncObserver* obs); - gfx::VSyncProvider* vsync_provider() { return &vsync_provider_; } + virtual gfx::VSyncProvider* vsync_provider() = 0; // Returns the vsync interval via the Vsync provider. - base::TimeDelta GetVsyncInterval(); + virtual base::TimeDelta GetVsyncInterval() = 0; + + protected: + VSyncThreadWin(); + ~VSyncThreadWin() override; + + // Gets vsync interval from vsync_provider and halts thread until the next + // signal from the compositor clock or vblank. Returns true if the wait was + // completed successfully, early if the desktop was occluded and false on any + // other failures. + virtual bool WaitForVSyncImpl(base::TimeDelta* vsync_interval) = 0; private: // Acquires `lock_` in a scope if not already held by the thread. class SCOPED_LOCKABLE AutoVSyncThreadLock; - explicit VSyncThreadWin(Microsoft::WRL::ComPtr<IDXGIDevice> dxgi_device); - ~VSyncThreadWin() final; - - void PostTaskIfNeeded() EXCLUSIVE_LOCKS_REQUIRED(lock_); void WaitForVSync(); + void PostTaskIfNeeded() EXCLUSIVE_LOCKS_REQUIRED(lock_); + base::Thread vsync_thread_; - // Used on vsync thread only after initialization. - VSyncProviderWin vsync_provider_; - Microsoft::WRL::ComPtr<IDXGIAdapter> dxgi_adapter_; - Microsoft::WRL::ComPtr<IDXGIOutput> primary_output_; - - // The LUID of the adapter of the IDXGIDevice this instance was created with. - const LUID original_adapter_luid_; - base::Lock lock_; bool GUARDED_BY(lock_) is_vsync_task_posted_ = false; bool GUARDED_BY(lock_) is_suspended_ = false;
diff --git a/ui/gl/vsync_thread_win_dcomp.cc b/ui/gl/vsync_thread_win_dcomp.cc new file mode 100644 index 0000000..2ff29ff --- /dev/null +++ b/ui/gl/vsync_thread_win_dcomp.cc
@@ -0,0 +1,55 @@ +// Copyright 2024 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/gl/vsync_thread_win_dcomp.h" + +#include "base/logging.h" +#include "base/trace_event/typed_macros.h" +#include "ui/gl/direct_composition_support.h" +#include "ui/gl/gl_features.h" + +namespace gl { + +VSyncThreadWinDComp::VSyncThreadWinDComp() + : VSyncThreadWin(), vsync_provider_() {} + +VSyncThreadWinDComp::~VSyncThreadWinDComp() = default; + +base::TimeDelta VSyncThreadWinDComp::GetVsyncInterval() { + base::TimeTicks vsync_timebase; + base::TimeDelta vsync_interval; + + // Use the compositor clock to determine vsync interval, disabled by default + const bool get_vsync_succeeded = + vsync_provider_.GetVSyncParametersIfAvailable(&vsync_timebase, + &vsync_interval); + DCHECK(get_vsync_succeeded); + + return vsync_interval; +} + +gfx::VSyncProvider* VSyncThreadWinDComp::vsync_provider() { + return &vsync_provider_; +} + +bool VSyncThreadWinDComp::WaitForVSyncImpl(base::TimeDelta* vsync_interval) { + *vsync_interval = GetVsyncInterval(); + + // Using INFINITE timeout as it is expected for the wait to return in a + // timely manner - either at the next vblank or immediately due to desktop + // occlusion. This behavior matches the DXGI case. + // DCompositionWaitForCompositorClock returns an error on desktop occlusion + // and returns early. + DWORD wait_result = + gl::DCompositionWaitForCompositorClock(0, nullptr, INFINITE); + + if (wait_result != WAIT_OBJECT_0) { + TRACE_EVENT1("gpu", "WaitForVSyncImpl", "wait result", wait_result); + return false; + } + + return true; +} + +} // namespace gl
diff --git a/ui/gl/vsync_thread_win_dcomp.h b/ui/gl/vsync_thread_win_dcomp.h new file mode 100644 index 0000000..b081248 --- /dev/null +++ b/ui/gl/vsync_thread_win_dcomp.h
@@ -0,0 +1,35 @@ +// Copyright 2024 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_GL_VSYNC_THREAD_WIN_DCOMP_H_ +#define UI_GL_VSYNC_THREAD_WIN_DCOMP_H_ + +#include "ui/gl/vsync_provider_win_dcomp.h" +#include "ui/gl/vsync_thread_win.h" + +namespace gl { +class GL_EXPORT VSyncThreadWinDComp final : public VSyncThreadWin { + public: + VSyncThreadWinDComp(); + + VSyncThreadWinDComp(const VSyncThreadWinDComp&) = delete; + VSyncThreadWinDComp& operator=(const VSyncThreadWinDComp&) = delete; + + // Returns the vsync interval via the Vsync provider. + base::TimeDelta GetVsyncInterval() final; + + gfx::VSyncProvider* vsync_provider() final; + + protected: + bool WaitForVSyncImpl(base::TimeDelta* vsync_interval) final; + + private: + ~VSyncThreadWinDComp() final; + + // Used on vsync thread only after initialization. + VSyncProviderWinDComp vsync_provider_; +}; +} // namespace gl + +#endif // UI_GL_VSYNC_THREAD_WIN_DCOMP_H_
diff --git a/ui/gl/vsync_thread_win_dxgi.cc b/ui/gl/vsync_thread_win_dxgi.cc new file mode 100644 index 0000000..ca88116 --- /dev/null +++ b/ui/gl/vsync_thread_win_dxgi.cc
@@ -0,0 +1,197 @@ +// Copyright 2024 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/gl/vsync_thread_win_dxgi.h" + +#include "base/logging.h" +#include "base/trace_event/typed_macros.h" +#include "base/win/windows_version.h" +#include "ui/gl/gl_features.h" + +namespace gl { +namespace { + +// Check if a DXGI adapter is stale and needs to be replaced. This can happen +// e.g. when detaching/reattaching remote desktop sessions and causes subsequent +// WaitForVSyncs on the stale adapter/output to return instantly. +bool DXGIFactoryIsCurrent(IDXGIAdapter* dxgi_adapter) { + CHECK(dxgi_adapter); + + HRESULT hr = S_OK; + Microsoft::WRL::ComPtr<IDXGIFactory1> dxgi_factory; + hr = dxgi_adapter->GetParent(IID_PPV_ARGS(&dxgi_factory)); + CHECK_EQ(S_OK, hr); + return dxgi_factory->IsCurrent(); +} + +// Create a new factory and find a DXGI adapter matching a LUID. This is useful +// if we have a previous adapter whose factory has become stale. +Microsoft::WRL::ComPtr<IDXGIAdapter> FindDXGIAdapterOnNewFactory( + const LUID luid) { + HRESULT hr = S_OK; + + Microsoft::WRL::ComPtr<IDXGIFactory1> factory; + hr = CreateDXGIFactory1(IID_PPV_ARGS(&factory)); + CHECK_EQ(S_OK, hr); + + Microsoft::WRL::ComPtr<IDXGIAdapter1> new_adapter; + for (uint32_t i = 0;; i++) { + hr = factory->EnumAdapters1(i, &new_adapter); + if (hr == DXGI_ERROR_NOT_FOUND) { + break; + } + if (FAILED(hr)) { + DLOG(ERROR) << "EnumAdapters1 failed: " + << logging::SystemErrorCodeToString(hr); + return nullptr; + } + + DXGI_ADAPTER_DESC1 new_adapter_desc; + hr = new_adapter->GetDesc1(&new_adapter_desc); + CHECK_EQ(S_OK, hr); + + if (new_adapter_desc.AdapterLuid.HighPart == luid.HighPart && + new_adapter_desc.AdapterLuid.LowPart == luid.LowPart) { + return new_adapter; + } + } + + DLOG(ERROR) << "Failed to find DXGI adapter with matching LUID"; + return nullptr; +} + +// Return true if |output| is on |monitor|. +bool DXGIOutputIsOnMonitor(IDXGIOutput* output, const HMONITOR monitor) { + CHECK(output); + + DXGI_OUTPUT_DESC desc = {}; + HRESULT hr = output->GetDesc(&desc); + CHECK_EQ(S_OK, hr); + return desc.Monitor == monitor; +} + +Microsoft::WRL::ComPtr<IDXGIOutput> DXGIOutputFromMonitor( + HMONITOR monitor, + IDXGIAdapter* dxgi_adapter) { + CHECK(dxgi_adapter); + + HRESULT hr = S_OK; + + Microsoft::WRL::ComPtr<IDXGIOutput> output; + for (uint32_t i = 0;; i++) { + hr = dxgi_adapter->EnumOutputs(i, &output); + if (hr == DXGI_ERROR_NOT_FOUND) { + break; + } + if (FAILED(hr)) { + DLOG(ERROR) << "EnumOutputs failed: " + << logging::SystemErrorCodeToString(hr); + return nullptr; + } + + if (DXGIOutputIsOnMonitor(output.Get(), monitor)) { + return output; + } + } + + DLOG(ERROR) << "Failed to find DXGI output with matching monitor"; + return nullptr; +} + +Microsoft::WRL::ComPtr<IDXGIAdapter> GetAdapter(IDXGIDevice* device) { + CHECK(device); + + Microsoft::WRL::ComPtr<IDXGIAdapter> adapter; + CHECK_EQ(S_OK, device->GetAdapter(&adapter)); + return adapter; +} + +LUID GetLuid(IDXGIAdapter* adapter) { + CHECK(adapter); + + DXGI_ADAPTER_DESC desc; + HRESULT hr = adapter->GetDesc(&desc); + CHECK_EQ(S_OK, hr); + return desc.AdapterLuid; +} +} // namespace + +VSyncThreadWinDXGI::VSyncThreadWinDXGI( + Microsoft::WRL::ComPtr<IDXGIDevice> dxgi_device) + : VSyncThreadWin(), + vsync_provider_(gfx::kNullAcceleratedWidget), + dxgi_adapter_(GetAdapter(dxgi_device.Get())), + original_adapter_luid_(GetLuid(dxgi_adapter_.Get())) {} + +VSyncThreadWinDXGI::~VSyncThreadWinDXGI() { + NOTREACHED(); +} + +gfx::VSyncProvider* VSyncThreadWinDXGI::vsync_provider() { + return &vsync_provider_; +} + +base::TimeDelta VSyncThreadWinDXGI::GetVsyncInterval() { + base::TimeTicks vsync_timebase; + base::TimeDelta vsync_interval; + + // This is a simplified initial approach to fix crbug.com/1456399 + // In Windows SV3 builds DWM will operate with per monitor refresh + // rates. As a result of this, DwmGetCompositionTimingInfo is no longer + // guaranteed to align with the primary monitor but will instead align + // with the current highest refresh rate monitor. This can cause issues + // in clients which may be waiting on the primary monitor's vblank as + // the reported interval may no longer match with the vblank wait. + // To work around this discrepancy get the VSync interval directly from + // monitor associated with window_ or the primary monitor. + static bool use_sv3_workaround = + base::win::GetVersion() > base::win::Version::WIN11_22H2 && + base::FeatureList::IsEnabled( + features::kUsePrimaryMonitorVSyncIntervalOnSV3); + + const bool get_vsync_params_succeeded = + use_sv3_workaround + ? vsync_provider_.GetVSyncIntervalIfAvailable(&vsync_interval) + : vsync_provider_.GetVSyncParametersIfAvailable(&vsync_timebase, + &vsync_interval); + DCHECK(get_vsync_params_succeeded); + + return vsync_interval; +} + +bool VSyncThreadWinDXGI::WaitForVSyncImpl(base::TimeDelta* vsync_interval) { + *vsync_interval = GetVsyncInterval(); + + if (!dxgi_adapter_ || !DXGIFactoryIsCurrent(dxgi_adapter_.Get())) { + TRACE_EVENT("gpu", "DXGIFactoryIsCurrent non-current factory"); + dxgi_adapter_ = FindDXGIAdapterOnNewFactory(original_adapter_luid_); + primary_output_.Reset(); + } + + // From Raymond Chen's blog "How do I get a handle to the primary monitor?" + // https://devblogs.microsoft.com/oldnewthing/20141106-00/?p=43683 + const HMONITOR monitor = MonitorFromWindow(nullptr, MONITOR_DEFAULTTOPRIMARY); + if (primary_output_ && + !DXGIOutputIsOnMonitor(primary_output_.Get(), monitor)) { + TRACE_EVENT("gpu", "DXGIOutputIsOnMonitor primary monitor changed"); + primary_output_.Reset(); + } + + if (!primary_output_ && dxgi_adapter_) { + primary_output_ = DXGIOutputFromMonitor(monitor, dxgi_adapter_.Get()); + } + + // WaitForVBlank returns success on desktop occlusion and returns early + const bool wait_succeeded = + primary_output_ && SUCCEEDED(primary_output_->WaitForVBlank()); + if (!wait_succeeded) { + TRACE_EVENT2("gpu", "WaitForVSyncImpl", "has adapter", !!dxgi_adapter_, + "has output", !!primary_output_); + return false; + } + + return true; +} + +} // namespace gl
diff --git a/ui/gl/vsync_thread_win_dxgi.h b/ui/gl/vsync_thread_win_dxgi.h new file mode 100644 index 0000000..0b0213d --- /dev/null +++ b/ui/gl/vsync_thread_win_dxgi.h
@@ -0,0 +1,41 @@ +// Copyright 2024 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_GL_VSYNC_THREAD_WIN_DXGI_H_ +#define UI_GL_VSYNC_THREAD_WIN_DXGI_H_ + +#include "ui/gl/vsync_thread_win.h" + +namespace gl { +class GL_EXPORT VSyncThreadWinDXGI final : public VSyncThreadWin { + public: + explicit VSyncThreadWinDXGI(Microsoft::WRL::ComPtr<IDXGIDevice> dxgi_device); + + VSyncThreadWinDXGI(const VSyncThreadWinDXGI&) = delete; + VSyncThreadWinDXGI& operator=(const VSyncThreadWinDXGI&) = delete; + + // Returns the vsync interval via the Vsync provider. + base::TimeDelta GetVsyncInterval() final; + + gfx::VSyncProvider* vsync_provider() final; + + protected: + bool WaitForVSyncImpl(base::TimeDelta* vsync_interval) final; + + private: + ~VSyncThreadWinDXGI() final; + + // Used on vsync thread only after initialization. + VSyncProviderWin vsync_provider_; + + // Used on vsync thread only after initialization + Microsoft::WRL::ComPtr<IDXGIAdapter> dxgi_adapter_; + Microsoft::WRL::ComPtr<IDXGIOutput> primary_output_; + + // The LUID of the adapter of the IDXGIDevice this instance was created with. + const LUID original_adapter_luid_; +}; +} // namespace gl + +#endif // UI_GL_VSYNC_THREAD_WIN_DXGI_H_
diff --git a/ui/webui/resources/mojo/BUILD.gn b/ui/webui/resources/mojo/BUILD.gn index e5388e7..3a7cf6f 100644 --- a/ui/webui/resources/mojo/BUILD.gn +++ b/ui/webui/resources/mojo/BUILD.gn
@@ -22,6 +22,7 @@ "mojo/public/mojom/base/safe_base_name.mojom-webui.ts", "mojo/public/mojom/base/string16.mojom-webui.ts", "mojo/public/mojom/base/text_direction.mojom-webui.ts", + "mojo/public/mojom/base/time.mojom-converters.ts", "mojo/public/mojom/base/time.mojom-webui.ts", "mojo/public/mojom/base/token.mojom-webui.ts", "mojo/public/mojom/base/unguessable_token.mojom-webui.ts", @@ -39,6 +40,8 @@ "url/mojom/url.mojom-webui.ts", ] +mojo_converters = [ "mojo/public/mojom/base/time_converters.ts" ] + if (is_ios) { mojo_ts_files += [ "ui/base/mojom/ui_base_types.mojom-webui.ts" ] } @@ -116,17 +119,26 @@ } } +preprocess_if_expr("copy_converters") { + visibility = [ ":build_ts" ] + + in_folder = "//" + out_folder = "$target_gen_dir/preprocessed" + in_files = mojo_converters +} + ts_library("build_ts") { root_dir = "$target_gen_dir/preprocessed" out_dir = tsc_folder composite = true - in_files = mojo_ts_files + in_files = mojo_ts_files + mojo_converters definitions = [ "$tsc_folder/mojo/public/js/bindings.d.ts" ] extra_deps = [ ":copy_bindings_dts", + ":copy_converters", ":copy_mojo_ts", ]