diff --git a/DEPS b/DEPS index 4951357..2fd2e6c6 100644 --- a/DEPS +++ b/DEPS
@@ -297,7 +297,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling Skia # and whatever else without interference from each other. - 'skia_revision': 'ce78d26f61f68080e2dbcf488217f23b0d8c9f38', + 'skia_revision': '687aa00d6924b1b59e509f4e010ec635c45bcc21', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling V8 # and whatever else without interference from each other. @@ -305,7 +305,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': 'b47603e0448d6cedd5d56b8ca4a2658702fcdfb1', + 'angle_revision': 'd0fe12db855d057ecca7fc1f0fa9e37f1d9a40f2', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling SwiftShader # and whatever else without interference from each other. @@ -313,7 +313,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling PDFium # and whatever else without interference from each other. - 'pdfium_revision': '61d8ae861c7576a063422b5cbec9fdae21af58cd', + 'pdfium_revision': 'd14da8e682e244127db32490365d1c094243e5f3', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling BoringSSL # and whatever else without interference from each other. @@ -368,7 +368,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling catapult # and whatever else without interference from each other. - 'catapult_revision': '4c50669a2f87912897af9227846fa5a419560616', + 'catapult_revision': 'dd1a8cd9c4b12f9b40620c80d50675313d5e03c7', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling libFuzzer # and whatever else without interference from each other. @@ -376,7 +376,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': 'c756660b512ce494ce842fa1ff8eed2bef29b87e', + 'devtools_frontend_revision': '8ec1e0e28e54f5deebf62c95315038a0ec415415', # 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. @@ -777,7 +777,7 @@ }, 'src/ios/third_party/earl_grey2/src': { - 'url': Var('chromium_git') + '/external/github.com/google/EarlGrey.git' + '@' + '5bca387a75990c91c693995c87aa0dd1ec09237d', + 'url': Var('chromium_git') + '/external/github.com/google/EarlGrey.git' + '@' + '53a2982c85ac6cf802719603d037ad3be7091ebb', 'condition': 'checkout_ios', }, @@ -1473,7 +1473,7 @@ Var('chromium_git') + '/webm/libwebm.git' + '@' + 'e4fbea0c9751ae8aa86629b197a28d8276a2b0da', 'src/third_party/libwebp/src': - Var('chromium_git') + '/webm/libwebp.git' + '@' + 'a8e366166ab57bb1b4aaf6739fc775515bc71b51', + Var('chromium_git') + '/webm/libwebp.git' + '@' + '3c4a0fbfbcc606193f7e943b7e50af4077ce1a6c', 'src/third_party/libyuv': Var('chromium_git') + '/libyuv/libyuv.git' + '@' + 'd248929c059ff7629a85333699717d7a677d8d96', @@ -1584,7 +1584,7 @@ }, 'src/third_party/perfetto': - Var('android_git') + '/platform/external/perfetto.git' + '@' + 'c2235982559f7008e5384d5145e1febe571573cd', + Var('android_git') + '/platform/external/perfetto.git' + '@' + '361efbf9aab595e4dfa79ec48f242d9e722393c9', 'src/third_party/perl': { 'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3', @@ -1751,7 +1751,7 @@ Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + '44e4c8770158c505b03ee7feafa4859d083b0912', 'src/third_party/webgpu-cts/src': - Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + '72e3a2249135b5d42d7a1c611af497565e9e6f02', + Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + 'c4eb1df3f306c0ee3e43ba2446eb3616e42d6855', 'src/third_party/webrtc': Var('webrtc_git') + '/src.git' + '@' + 'dc5cf31cad576376abd3aa6306169453cfd85ba5', @@ -1827,7 +1827,7 @@ Var('chromium_git') + '/v8/v8.git' + '@' + Var('v8_revision'), 'src-internal': { - 'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@5159c6f0469e394c633fb8b22b2ad56354621518', + 'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@733a35bf21b6aa2214623566eea6e2badaf23fd4', 'condition': 'checkout_src_internal', },
diff --git a/android_webview/browser/enterprise_authentication_app_link_policy_handler.cc b/android_webview/browser/enterprise_authentication_app_link_policy_handler.cc index 449d0f8..1157917 100644 --- a/android_webview/browser/enterprise_authentication_app_link_policy_handler.cc +++ b/android_webview/browser/enterprise_authentication_app_link_policy_handler.cc
@@ -68,14 +68,17 @@ if (!value) return; - std::vector<base::Value> filtered_values; + base::Value::List filtered_values; for (const auto& entry : value->GetList()) { const std::string* url = entry.FindStringKey("url"); if (ValidatePolicyEntry(url)) - filtered_values.emplace_back(*url); + filtered_values.Append(*url); } - if (filtered_values.size() > policy::kMaxUrlFiltersPerPolicy) - filtered_values.resize(policy::kMaxUrlFiltersPerPolicy); + if (filtered_values.size() > policy::kMaxUrlFiltersPerPolicy) { + filtered_values.erase( + filtered_values.begin() + policy::kMaxUrlFiltersPerPolicy, + filtered_values.end()); + } prefs->SetValue(pref_path_, base::Value(std::move(filtered_values))); }
diff --git a/android_webview/nonembedded/java/src/org/chromium/android_webview/nonembedded/AwPureJavaExceptionReporter.java b/android_webview/nonembedded/java/src/org/chromium/android_webview/nonembedded/AwPureJavaExceptionReporter.java index 8010b07..f6242cc 100644 --- a/android_webview/nonembedded/java/src/org/chromium/android_webview/nonembedded/AwPureJavaExceptionReporter.java +++ b/android_webview/nonembedded/java/src/org/chromium/android_webview/nonembedded/AwPureJavaExceptionReporter.java
@@ -35,6 +35,11 @@ @Override protected void uploadMinidump(File minidump) { + // The minidump file will only be ready for upload if PureJavaExceptionReporter attached + // logcat successfully, WebView should upload it even if attaching logcat was failed. + if (!CrashFileManager.isReadyUploadForFirstTime(minidump)) { + CrashFileManager.trySetReadyForUpload(minidump); + } CrashUploadUtil.scheduleNewJob(ContextUtils.getApplicationContext()); }
diff --git a/ash/app_list/app_list_test_api.cc b/ash/app_list/app_list_test_api.cc index bfb0599..18e019a 100644 --- a/ash/app_list/app_list_test_api.cc +++ b/ash/app_list/app_list_test_api.cc
@@ -421,14 +421,24 @@ std::u16string AppListTestApi::GetAppListItemViewName( const std::string& item_id) { + AppListItemView* item_view = GetTopLevelItemViewFromId(item_id); + if (!item_view) + return u""; + + return item_view->title()->GetText(); +} + +AppListItemView* AppListTestApi::GetTopLevelItemViewFromId( + const std::string& item_id) { views::ViewModelT<AppListItemView>* view_model = GetTopLevelAppsGridView()->view_model(); for (size_t i = 0; i < view_model->view_size(); ++i) { AppListItemView* app_list_item_view = view_model->view_at(i); if (app_list_item_view->item()->id() == item_id) - return app_list_item_view->title()->GetText(); + return app_list_item_view; } - return u""; + + return nullptr; } std::vector<std::string> AppListTestApi::GetTopLevelViewIdList() {
diff --git a/ash/app_list/model/app_list_model.cc b/ash/app_list/model/app_list_model.cc index 417dda5..afe2af7 100644 --- a/ash/app_list/model/app_list_model.cc +++ b/ash/app_list/model/app_list_model.cc
@@ -115,8 +115,6 @@ // from chrome side is null. Do not alter `item` default icon in this case. data->icon = item->GetDefaultIcon(); data->icon_color = item->GetDefaultIconColor(); - } else if (data->icon_color != item->GetDefaultIconColor()) { - SetItemDefaultIconAndColor(item, data->icon, data->icon_color); } if (data->folder_id != item->folder_id()) @@ -403,13 +401,4 @@ observer.OnAppListItemUpdated(item); } -void AppListModel::SetItemDefaultIconAndColor(AppListItem* item, - const gfx::ImageSkia& icon, - const IconColor& icon_color) { - DCHECK(FindItem(item->id())); - item->SetDefaultIconAndColor(icon, icon_color); - for (auto& observer : observers_) - observer.OnAppListItemUpdated(item); -} - } // namespace ash
diff --git a/ash/app_list/model/app_list_model.h b/ash/app_list/model/app_list_model.h index ee5710f..4676607 100644 --- a/ash/app_list/model/app_list_model.h +++ b/ash/app_list/model/app_list_model.h
@@ -180,11 +180,6 @@ void SetRootItemPosition(AppListItem* item, const syncer::StringOrdinal& new_position); - // Sets the default icon and the icon's associated color data. - void SetItemDefaultIconAndColor(AppListItem* item, - const gfx::ImageSkia& icon, - const IconColor& icon_color); - // Used to initiate updates on app list items from the ash side. AppListModelDelegate* const delegate_;
diff --git a/ash/app_list/views/app_list_item_view.h b/ash/app_list/views/app_list_item_view.h index 03ec7ea..072da16 100644 --- a/ash/app_list/views/app_list_item_view.h +++ b/ash/app_list/views/app_list_item_view.h
@@ -227,6 +227,7 @@ bool IsNotificationIndicatorShownForTest() const; GridDelegate* grid_delegate_for_test() { return grid_delegate_; } + const gfx::ImageSkia& icon_image_for_test() const { return icon_image_; } AppListMenuModelAdapter* item_menu_model_adapter() const { return item_menu_model_adapter_.get();
diff --git a/ash/app_list/views/apps_grid_view.cc b/ash/app_list/views/apps_grid_view.cc index d70baf8..7cbafd3 100644 --- a/ash/app_list/views/apps_grid_view.cc +++ b/ash/app_list/views/apps_grid_view.cc
@@ -331,6 +331,7 @@ MaybeAbortWholeGridAnimation(); view_model_.Clear(); + pulsing_blocks_model_.Clear(); RemoveAllChildViews(); // `OnBoundsAnimatorDone`, which uses `bounds_animator_`, is called on
diff --git a/ash/app_list/views/apps_grid_view_unittest.cc b/ash/app_list/views/apps_grid_view_unittest.cc index e5740d74..38bc648 100644 --- a/ash/app_list/views/apps_grid_view_unittest.cc +++ b/ash/app_list/views/apps_grid_view_unittest.cc
@@ -5779,6 +5779,87 @@ EXPECT_EQ(0u, GetPulsingBlocksModel().view_size()); } +// Tests that the pulsing blocks animation runs with the productivity launcher. +TEST_P(AppsGridViewClamshellAndTabletTest, + PulsingBlocksAnimationOnFiringAnimationTimer) { + model_->PopulateApps(3); + UpdateLayout(); + EXPECT_EQ(0u, GetPulsingBlocksModel().view_size()); + + ui::ScopedAnimationDurationScaleMode non_zero_duration_mode( + ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION); + + // For scrolling app list, the "page size" is very large, so cap the number of + // pulsing blocks to the size of the tablet mode page (~20 items). + const size_t tiles_per_page = + SharedAppListConfig::instance().GetMaxNumOfItemsPerPage(); + model_->SetStatus(AppListModelStatus::kStatusSyncing); + UpdateLayout(); + ASSERT_EQ(tiles_per_page - 3, GetPulsingBlocksModel().view_size()); + + PulsingBlockView* pulsing_block_view = GetPulsingBlocksModel().view_at(0); + + EXPECT_FALSE(pulsing_block_view->IsAnimating()); + EXPECT_TRUE(pulsing_block_view->FireAnimationTimerForTest()); + EXPECT_TRUE(pulsing_block_view->IsAnimating()); + + // Set the model status as normal to avoid the test hanging due to the + // pulsing blocks animation. + model_->SetStatus(AppListModelStatus::kStatusNormal); + EXPECT_EQ(0u, GetPulsingBlocksModel().view_size()); +} + +// Verify that as new app items get synced into the app list, newer items slowly +// fade in place of a placeholder. +TEST_F(AppsGridViewBubbleTest, AppIconSubtitutesPulsingBlockView) { + model_->PopulateApps(3); + UpdateLayout(); + EXPECT_EQ(0u, GetPulsingBlocksModel().view_size()); + + ui::ScopedAnimationDurationScaleMode non_zero_duration_mode( + ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION); + + // For scrolling app list, the "page size" is very large, so cap the number of + // pulsing blocks to the size of the tablet mode page (~20 items). + const size_t tiles_per_page = + SharedAppListConfig::instance().GetMaxNumOfItemsPerPage(); + model_->SetStatus(AppListModelStatus::kStatusSyncing); + UpdateLayout(); + ASSERT_EQ(tiles_per_page - 3, GetPulsingBlocksModel().view_size()); + + PulsingBlockView* pulsing_block_view = GetPulsingBlocksModel().view_at(0); + + EXPECT_TRUE(pulsing_block_view->FireAnimationTimerForTest()); + EXPECT_TRUE(pulsing_block_view->IsAnimating()); + + gfx::Rect placeholder_bounds = pulsing_block_view->GetBoundsInScreen(); + + // Add another app to simulate a synced app. + model_->PopulateApps(1); + UpdateLayout(); + + // The number of pulsing blocks will be decreased by one in order for the + // incoming app to fade in its place. + ASSERT_EQ(tiles_per_page - 4, GetPulsingBlocksModel().view_size()); + + AppListItemView* item_view = GetItemViewInTopLevelGrid(3); + + ASSERT_TRUE(item_view->layer()); + EXPECT_TRUE(item_view->layer()->GetAnimator()->is_animating()); + EXPECT_EQ(1.0f, item_view->layer()->GetTargetOpacity()); + + LayerAnimationStoppedWaiter animation_waiter; + animation_waiter.Wait(item_view->layer()); + + // The new item should be placed at the first placeholder bounds. + EXPECT_EQ(placeholder_bounds, item_view->GetBoundsInScreen()); + + // Set the model status as normal to avoid the test hanging due to the + // pulsing blocks animation. + model_->SetStatus(AppListModelStatus::kStatusNormal); + EXPECT_EQ(0u, GetPulsingBlocksModel().view_size()); +} + // Tests that right clicking an app will remove focus from other apps within the // apps grid. See https://crbug.com/1146365. TEST_P(AppsGridViewClamshellTest, VerifyFocusRemovedWhenLeftClickingOtherApp) {
diff --git a/ash/app_list/views/pulsing_block_view.cc b/ash/app_list/views/pulsing_block_view.cc index f349cd1..410807d 100644 --- a/ash/app_list/views/pulsing_block_view.cc +++ b/ash/app_list/views/pulsing_block_view.cc
@@ -169,4 +169,22 @@ canvas->FillRect(rect, kBlockColor); } +bool PulsingBlockView::IsAnimating() { + views::View* animating_view = + ash::features::IsLauncherPulsingBlocksRefreshEnabled() + ? background_color_view_ + : this; + return animating_view->layer() + ? animating_view->layer()->GetAnimator()->is_animating() + : false; +} + +bool PulsingBlockView::FireAnimationTimerForTest() { + if (!start_delay_timer_.IsRunning()) + return false; + + start_delay_timer_.FireNow(); + return true; +} + } // namespace ash
diff --git a/ash/app_list/views/pulsing_block_view.h b/ash/app_list/views/pulsing_block_view.h index c257bce..1a94a0a 100644 --- a/ash/app_list/views/pulsing_block_view.h +++ b/ash/app_list/views/pulsing_block_view.h
@@ -15,7 +15,7 @@ namespace ash { -// PulsingBlockView shows a pulsing white block via layer animation. +// PulsingBlockView shows a pulsing white circle via layer animation. class PulsingBlockView : public views::View { public: // Constructs a PulsingBlockView of |size|. Starts the pulsing animation after @@ -31,8 +31,13 @@ const char* GetClassName() const override; void OnThemeChanged() override; - // Schedules the animation again from the beginning. - void ResetAnimation(); + // Returns true if the view has a layer animator attached and is currently + // running. + bool IsAnimating(); + + // Starts the animation by immediately firing `start_delay_timer`. Returns + // false if the timer was not running. + bool FireAnimationTimerForTest(); private: void OnStartDelayTimer();
diff --git a/ash/clipboard/clipboard_history_controller_impl.cc b/ash/clipboard/clipboard_history_controller_impl.cc index 2015244..46213447 100644 --- a/ash/clipboard/clipboard_history_controller_impl.cc +++ b/ash/clipboard/clipboard_history_controller_impl.cc
@@ -664,6 +664,15 @@ confirmed_paste_count = 0; } + if (copy) { + // Record copy actions once they are confirmed, rather than when clipboard + // data first changes, to allow multiple data changes to be debounced into + // a single copy operation. This ensures that each user-initiated copy is + // recorded only once. See `ClipboardHistory::OnDataChanged()` for further + // explanation. + base::RecordAction(base::UserMetricsAction("Ash_Clipboard_CopiedItem")); + } + // Verify that this operation did not interleave with a clipboard history // paste. DCHECK_EQ(pastes_to_be_confirmed_, 0);
diff --git a/ash/constants/ash_features.cc b/ash/constants/ash_features.cc index 2695514..0776995 100644 --- a/ash/constants/ash_features.cc +++ b/ash/constants/ash_features.cc
@@ -1388,7 +1388,7 @@ // Enables Shelf Palm Rejection in tablet mode by defining a pixel offset for // the swipe gesture to show the extended hotseat. Limited to certain apps. const base::Feature kShelfPalmRejectionSwipeOffset{ - "ShelfPalmRejectionSwipeOffset", base::FEATURE_DISABLED_BY_DEFAULT}; + "ShelfPalmRejectionSwipeOffset", base::FEATURE_ENABLED_BY_DEFAULT}; // Enables or disables the new shimless rma flow. const base::Feature kShimlessRMAFlow{"ShimlessRMAFlow",
diff --git a/ash/constants/notifier_catalogs.h b/ash/constants/notifier_catalogs.h index 9afa184..fc1f9d6 100644 --- a/ash/constants/notifier_catalogs.h +++ b/ash/constants/notifier_catalogs.h
@@ -159,7 +159,10 @@ kTPMAutoUpdatePlanned = 145, kTPMAutoUpdateOnReboot = 146, kPrivacyIndicators = 147, - kMaxValue = kPrivacyIndicators + kTailoredSecurityDisabled = 148, + kTailoredSecurityEnabled = 149, + kTailoredSecurityPromotion = 150, + kMaxValue = kTailoredSecurityPromotion }; // A living catalog that registers toasts.
diff --git a/ash/public/cpp/test/app_list_test_api.h b/ash/public/cpp/test/app_list_test_api.h index 1e96efb..8771c04 100644 --- a/ash/public/cpp/test/app_list_test_api.h +++ b/ash/public/cpp/test/app_list_test_api.h
@@ -61,6 +61,9 @@ // Returns the name displayed in the launcher for the provided app list item. std::u16string GetAppListItemViewName(const std::string& item_id); + // Returns the top level item view specified by `item_id`. + AppListItemView* GetTopLevelItemViewFromId(const std::string& item_id); + // Returns ids of the items in top level app list view. std::vector<std::string> GetTopLevelViewIdList();
diff --git a/ash/shelf/shelf_view.cc b/ash/shelf/shelf_view.cc index 35ac465..95ee31a 100644 --- a/ash/shelf/shelf_view.cc +++ b/ash/shelf/shelf_view.cc
@@ -455,22 +455,6 @@ // We'll layout when our bounds change. } -gfx::Rect ShelfView::GetIdealBoundsOfItemIcon(const ShelfID& id) { - int index = model_->ItemIndexByID(id); - if (index < 0 || - !base::Contains(visible_views_indices_, static_cast<size_t>(index))) - return gfx::Rect(); - - const gfx::Rect& ideal_bounds( - view_model_->ideal_bounds(static_cast<size_t>(index))); - ShelfAppButton* button = GetShelfAppButton(id); - gfx::Rect icon_bounds = button->GetIconBounds(); - return gfx::Rect(GetMirroredXWithWidthInView( - ideal_bounds.x() + icon_bounds.x(), icon_bounds.width()), - ideal_bounds.y() + icon_bounds.y(), icon_bounds.width(), - icon_bounds.height()); -} - bool ShelfView::IsShowingMenu() const { return shelf_menu_model_adapter_ && shelf_menu_model_adapter_->IsShowingMenu();
diff --git a/ash/shelf/shelf_view.h b/ash/shelf/shelf_view.h index 1ab8121..24e8eaba 100644 --- a/ash/shelf/shelf_view.h +++ b/ash/shelf/shelf_view.h
@@ -128,11 +128,6 @@ // Initializes shelf view elements. void Init(); - // Returns the ideal bounds of the specified item, or an empty rect if id - // isn't know. If the item is in an overflow shelf, the overflow icon location - // will be returned. - gfx::Rect GetIdealBoundsOfItemIcon(const ShelfID& id); - // Returns true if we're showing a menu. Note the menu could be either the // context menu or the application select menu. bool IsShowingMenu() const;
diff --git a/ash/system/cast/tray_cast.h b/ash/system/cast/tray_cast.h index 7230a0c5..c9509f0 100644 --- a/ash/system/cast/tray_cast.h +++ b/ash/system/cast/tray_cast.h
@@ -32,6 +32,10 @@ // views::View: const char* GetClassName() const override; + views::View* get_add_access_code_device_for_testing() { + return add_access_code_device_; + } + private: void CreateItems();
diff --git a/ash/system/cast/unified_cast_detailed_view_controller.h b/ash/system/cast/unified_cast_detailed_view_controller.h index 2e13b976..6f94fc0 100644 --- a/ash/system/cast/unified_cast_detailed_view_controller.h +++ b/ash/system/cast/unified_cast_detailed_view_controller.h
@@ -31,6 +31,8 @@ views::View* CreateView() override; std::u16string GetAccessibleName() const override; + CastDetailedView* get_cast_detailed_view_for_testing() { return view_; } + private: const std::unique_ptr<DetailedViewDelegate> detailed_view_delegate_;
diff --git a/ash/system/time/calendar_event_list_view_unittest.cc b/ash/system/time/calendar_event_list_view_unittest.cc index 5f75ba8..8e73305 100644 --- a/ash/system/time/calendar_event_list_view_unittest.cc +++ b/ash/system/time/calendar_event_list_view_unittest.cc
@@ -74,8 +74,9 @@ void CreateEventListView(base::Time date) { event_list_view_.reset(); controller_->UpdateMonth(date); - Shell::Get()->system_tray_model()->calendar_model()->InsertEventsForTesting( - CreateMockEventList().get()); + Shell::Get()->system_tray_model()->calendar_model()->OnEventsFetched( + calendar_utils::GetStartOfMonthUTC(date), + google_apis::ApiErrorCode::HTTP_SUCCESS, CreateMockEventList().get()); controller_->selected_date_ = date; event_list_view_ = std::make_unique<CalendarEventListView>(controller_.get());
diff --git a/ash/system/time/calendar_model.cc b/ash/system/time/calendar_model.cc index 24b9b109..b69accc2 100644 --- a/ash/system/time/calendar_model.cc +++ b/ash/system/time/calendar_model.cc
@@ -421,41 +421,6 @@ return GetEndTimeAdjusted(event).UTCMidnight(); } -// TODO(crbug/1330004): Remove function in favor of calling `OnEventsFetched` -// directly from tests. -void CalendarModel::InsertEventsForTesting( - const google_apis::calendar::EventList* events) { - if (!events) - return; - - // Make sure the cache is empty. - event_months_.clear(); - - // Insert, and collect the set of months inserted. - std::set<base::Time> months_inserted; - for (const auto& event : events->items()) { - base::Time start_time_midnight = GetStartTimeMidnightAdjusted(event.get()); - base::Time start_of_month = - calendar_utils::GetStartOfMonthUTC(start_time_midnight); - if (IsMultiDayEvent(event.get())) { - const base::Time end_time_midnight = - GetEndTimeMidnightAdjusted(event.get()); - // If we have multi-day events, we insert the event to every month map the - // event exists in. This to reproduce Google Calendar API requests, which - // are fetched by month, so multi-day events get inserted for every month - // they are active. - while (end_time_midnight >= start_of_month) { - InsertMultiDayEvent(event.get(), start_of_month); - start_of_month = calendar_utils::GetStartOfNextMonthUTC(start_of_month); - months_inserted.emplace(start_of_month); - } - } else { - InsertEventInMonth(event.get(), start_of_month, start_time_midnight); - months_inserted.emplace(start_of_month); - } - } -} - SingleDayEventList CalendarModel::FindEvents(base::Time day) const { SingleDayEventList event_list;
diff --git a/ash/system/time/calendar_model.h b/ash/system/time/calendar_model.h index ec25587..97f2880 100644 --- a/ash/system/time/calendar_model.h +++ b/ash/system/time/calendar_model.h
@@ -169,10 +169,6 @@ base::Time GetEndTimeMidnightAdjusted( const google_apis::calendar::CalendarEvent* event) const; - // Inserts EventList `events` in the EventCache. For testing only, it clears - // out the entire cache and inserts the `events`. - void InsertEventsForTesting(const google_apis::calendar::EventList* events); - // Frees up months of events as needed to keep us within storage limits. void PruneEventCache();
diff --git a/ash/system/time/calendar_month_view_unittest.cc b/ash/system/time/calendar_month_view_unittest.cc index 79e37df..93ea7ff 100644 --- a/ash/system/time/calendar_month_view_unittest.cc +++ b/ash/system/time/calendar_month_view_unittest.cc
@@ -82,9 +82,9 @@ calendar_month_view_->Layout(); } - void UploadEvents() { - calendar_month_view_->calendar_model_->InsertEventsForTesting( - CreateMockEventList().get()); + void MockFetchEvents(base::Time start_of_month) { + calendar_month_view_->calendar_model_->InsertPendingFetchesForTesting( + start_of_month); } void TriggerPaint() { @@ -107,9 +107,10 @@ } void NotifyObservers(base::Time start_of_month) { - for (auto& observer : calendar_month_view_->calendar_model_->observers_) - observer.OnEventsFetched(CalendarModel::kSuccess, start_of_month, - nullptr); + calendar_month_view_->calendar_model_->PruneEventCache(); + calendar_month_view_->calendar_model_->OnEventsFetched( + start_of_month, google_apis::ApiErrorCode::HTTP_SUCCESS, + CreateMockEventList().get()); } CalendarModel::MonthToEventsMap event_months() { @@ -319,7 +320,7 @@ CreateMonthView(date, u"America/Los_Angeles"); // Used to fetch events and notify observers. - base::Time date_midnight = date.UTCMidnight(); + base::Time month_start_midnight = calendar_utils::GetStartOfMonthUTC(today); TriggerPaint(); // Grayed out cell. Sep 2nd is the 33 one in this calendar, which is with @@ -338,7 +339,7 @@ static_cast<CalendarDateCellView*>(month_view()->children()[17]) ->GetTooltipText()); - InsertPendingFetches(date_midnight); + InsertPendingFetches(month_start_midnight); // Grayed out cell. Sep 2nd is the 33 one in this calendar, which is with // index 32. EXPECT_EQ(u"2", @@ -357,8 +358,8 @@ // After events are fetched before the observers are notified the event number // is not updated. - DeletePendingFetches(date_midnight); - UploadEvents(); + DeletePendingFetches(month_start_midnight); + MockFetchEvents(month_start_midnight); // Grayed out cell. Sep 2nd is the 33 one in this calendar, which is with // index 32. EXPECT_EQ(u"2", @@ -377,7 +378,7 @@ // After notifying observers and repainting, the event numbers are updated for // regular cells, not for grayed out cells. - NotifyObservers(date_midnight); + NotifyObservers(month_start_midnight); EXPECT_EQ(u"2", static_cast<CalendarDateCellView*>(month_view()->children()[32]) ->GetText()); @@ -394,10 +395,6 @@ } TEST_F(CalendarMonthViewTest, TimeZone) { - // Create a monthview based on Aug,1st 2021. Today is set to 18th. - base::Time date; - ASSERT_TRUE(base::Time::FromString("1 Aug 2021 10:00 GMT", &date)); - // Set "Now" to a date that is in this month. base::Time today; ASSERT_TRUE(base::Time::FromString("18 Aug 2021 10:00 GMT", &today)); @@ -407,13 +404,13 @@ /*thread_ticks_override=*/nullptr); // Used to fetch events and notify observers. - base::Time date_midnight = date.UTCMidnight(); + base::Time month_start_midnight = calendar_utils::GetStartOfMonthUTC(today); // Sets the timezone to "America/Los_Angeles"; - CreateMonthView(date, u"America/Los_Angeles"); + CreateMonthView(today, u"America/Los_Angeles"); TriggerPaint(); - UploadEvents(); - NotifyObservers(date_midnight); + MockFetchEvents(month_start_midnight); + NotifyObservers(month_start_midnight); EXPECT_EQ(u"18", static_cast<CalendarDateCellView*>(month_view()->children()[17]) @@ -443,10 +440,6 @@ } TEST_F(CalendarMonthViewTest, InactiveUserSession) { - // Create a monthview based on Aug,1st 2021. Today is set to 18th. - base::Time date; - ASSERT_TRUE(base::Time::FromString("1 Aug 2021 10:00 GMT", &date)); - // Set "Now" to a date that is in this month. base::Time today; ASSERT_TRUE(base::Time::FromString("18 Aug 2021 10:00 GMT", &today)); @@ -455,13 +448,13 @@ &CalendarMonthViewTest::FakeTimeNow, /*time_ticks_override=*/nullptr, /*thread_ticks_override=*/nullptr); - CreateMonthView(date, u"America/Los_Angeles"); + CreateMonthView(today, u"America/Los_Angeles"); // Used to fetch events and notify observers. - base::Time date_midnight = date.UTCMidnight(); + base::Time month_start_midnight = calendar_utils::GetStartOfMonthUTC(today); TriggerPaint(); - UploadEvents(); - NotifyObservers(date_midnight); + MockFetchEvents(month_start_midnight); + NotifyObservers(month_start_midnight); EXPECT_EQ(u"18", static_cast<CalendarDateCellView*>(month_view()->children()[17]) ->GetText());
diff --git a/ash/webui/os_feedback_ui/os_feedback_ui.cc b/ash/webui/os_feedback_ui/os_feedback_ui.cc index a0b2d80b..fd5668c 100644 --- a/ash/webui/os_feedback_ui/os_feedback_ui.cc +++ b/ash/webui/os_feedback_ui/os_feedback_ui.cc
@@ -58,6 +58,7 @@ {"attachScreenshotLabel", IDS_FEEDBACK_TOOL_SCREENSHOT_LABEL}, {"attachScreenshotCheckboxAriaLabel", IDS_FEEDBACK_TOOL_ATTACH_SCREENSHOT_CHECKBOX_ARIA_LABEL}, + {"previewImageAriaLabel", IDS_FEEDBACK_TOOL_PREVIEW_IMAGE_ARIA_LABEL}, {"addFileLabel", IDS_FEEDBACK_TOOL_ADD_FILE_LABEL}, {"replaceFileLabel", IDS_FEEDBACK_TOOL_REPLACE_FILE_LABEL}, {"userEmailLabel", IDS_FEEDBACK_TOOL_USER_EMAIL_LABEL},
diff --git a/ash/webui/os_feedback_ui/resources/file_attachment.html b/ash/webui/os_feedback_ui/resources/file_attachment.html index b7bba435..5d1b03865 100644 --- a/ash/webui/os_feedback_ui/resources/file_attachment.html +++ b/ash/webui/os_feedback_ui/resources/file_attachment.html
@@ -91,7 +91,7 @@ <div id="replaceFileContainer" hidden="[[!hasSelectedAFile_]]"> <input type="checkbox" id="selectFileCheckbox"> <div id="replaceFileInfo"> - <div id="selectedFileName" class="overflow-text"></div> + <div id="selectedFileName" class="overflow-text">[[selectedFileName_]]</div> <button id="replaceFileLabel" class="file-input" on-click="handleOpenFileInputClick_"> [[i18n('replaceFileLabel')]] @@ -106,14 +106,13 @@ </iron-icon> <span id="errorMessage">[[i18n('fileTooBigErrorMessage')]]</span> </cr-toast> -<!-- TODO(longbowei): Locolize strings in cr-dialog --> <cr-dialog id="selectedImageDialog"> <div id="modalDialogTitle" slot="title"> <cr-icon-button id="closeDialogButton" class="icon-arrow-back" on-click="handleSelectedImageDialogCloseClick_" - title="Image"> + title="[[selectedFileName_]]"> </cr-icon-button> - <span>Image</span> + <span id="modalDialogTitleText">[[selectedFileName_]]</span> </div> <div slot="body"> <img src="[[selectedImageUrl_]]" class="image-preview">
diff --git a/ash/webui/os_feedback_ui/resources/file_attachment.js b/ash/webui/os_feedback_ui/resources/file_attachment.js index b754640..8ae6fbe 100644 --- a/ash/webui/os_feedback_ui/resources/file_attachment.js +++ b/ash/webui/os_feedback_ui/resources/file_attachment.js
@@ -62,6 +62,13 @@ this.selectedFile_ = null; /** + * The name of the file selected + * @type {string} + * @protected + */ + this.selectedFileName_; + + /** * Url of the selected image. * @type {string} */ @@ -123,6 +130,7 @@ * Get the image url when uploaded file is image type. * @param {!File} file * @return {!Promise<string>} + * @private */ async getImageUrl_(file) { const fileDataBuffer = await file.arrayBuffer(); @@ -179,16 +187,19 @@ return; } this.selectedFile_ = file; - this.getElement_('#selectedFileName').textContent = file.name; + this.selectedFileName_ = file.name; this.getElement_('#selectFileCheckbox').checked = true; // Add a preview image when selected file is image type. if (file.type.startsWith('image/')) { this.getImageUrl_(file).then((imageUrl) => { this.selectedImageUrl_ = imageUrl; + this.$.selectedImageButton.ariaLabel = + this.i18n('previewImageAriaLabel', file.name); }); } else { this.selectedImageUrl_ = ''; + this.$.selectedImageButton.ariaLabel = ''; } }
diff --git a/ash/webui/os_feedback_ui/resources/share_data_page.html b/ash/webui/os_feedback_ui/resources/share_data_page.html index d963101f..f3d621c9 100644 --- a/ash/webui/os_feedback_ui/resources/share_data_page.html +++ b/ash/webui/os_feedback_ui/resources/share_data_page.html
@@ -157,8 +157,7 @@ </cr-checkbox> <label id="screenshotCheckLabel">[[i18n('attachScreenshotLabel')]]</label> <button id="imageButton" on-click="handleScreenshotClick_"> - <img id="screenshotImage" aria-label="$i18n{screenshotA11y}" - src="[[screenshotUrl]]"> + <img id="screenshotImage" src="[[screenshotUrl]]"> </button> </div> <!-- Attach a file -->
diff --git a/ash/webui/os_feedback_ui/resources/share_data_page.js b/ash/webui/os_feedback_ui/resources/share_data_page.js index b2e3a57..4351964 100644 --- a/ash/webui/os_feedback_ui/resources/share_data_page.js +++ b/ash/webui/os_feedback_ui/resources/share_data_page.js
@@ -82,6 +82,8 @@ this.setSysInfoCheckboxLabelAndAttributes_(); this.$.screenshotCheckbox.ariaLabel = this.i18n('attachScreenshotCheckboxAriaLabel'); + this.$.imageButton.ariaLabel = this.i18n( + 'previewImageAriaLabel', this.$.screenshotCheckLabel.textContent); // Set up event listener for email change to retarget |this| to be the // ShareDataPageElement's context.
diff --git a/base/BUILD.gn b/base/BUILD.gn index 7eddfb10..9fc667ea 100644 --- a/base/BUILD.gn +++ b/base/BUILD.gn
@@ -1431,10 +1431,7 @@ configs += [ "//build/config:precompiled_headers", - - # TODO(crbug.com/1292951): Enable. - # "//build/config/compiler:prevent_unsafe_narrowing", - + "//build/config/compiler:prevent_unsafe_narrowing", "//build/config/compiler:wexit_time_destructors", "//build/config/compiler:wglobal_constructors", ]
diff --git a/base/allocator/partition_allocator/page_allocator_constants.h b/base/allocator/partition_allocator/page_allocator_constants.h index 7de7a4c..751c2b3a 100644 --- a/base/allocator/partition_allocator/page_allocator_constants.h +++ b/base/allocator/partition_allocator/page_allocator_constants.h
@@ -42,8 +42,8 @@ // Use PageAllocationGranularity(), PageAllocationGranularityShift() // to initialize and retrieve these values safely. struct PageCharacteristics { - std::atomic<int> size; - std::atomic<int> shift; + std::atomic<size_t> size; + std::atomic<size_t> shift; }; extern PageCharacteristics page_characteristics; @@ -82,9 +82,10 @@ #elif BUILDFLAG(IS_LINUX) && defined(ARCH_CPU_ARM64) // arm64 supports 4kb (shift = 12), 16kb (shift = 14), and 64kb (shift = 16) // page sizes. Retrieve from or initialize cache. - int shift = page_characteristics.shift.load(std::memory_order_relaxed); + size_t shift = page_characteristics.shift.load(std::memory_order_relaxed); if (PA_UNLIKELY(shift == 0)) { - shift = __builtin_ctz((int)PageAllocationGranularity()); + shift = static_cast<size_t>( + __builtin_ctz((unsigned int)PageAllocationGranularity())); page_characteristics.shift.store(shift, std::memory_order_relaxed); } return shift; @@ -102,9 +103,9 @@ #elif BUILDFLAG(IS_LINUX) && defined(ARCH_CPU_ARM64) // arm64 supports 4kb, 16kb, and 64kb page sizes. Retrieve from or // initialize cache. - int size = page_characteristics.size.load(std::memory_order_relaxed); + size_t size = page_characteristics.size.load(std::memory_order_relaxed); if (PA_UNLIKELY(size == 0)) { - size = getpagesize(); + size = static_cast<size_t>(getpagesize()); page_characteristics.size.store(size, std::memory_order_relaxed); } return size;
diff --git a/base/files/file_util.h b/base/files/file_util.h index 79c46ab..d4a3d0d 100644 --- a/base/files/file_util.h +++ b/base/files/file_util.h
@@ -372,8 +372,7 @@ // Creates a directory, as well as creating any parent directories, if they // don't exist. Returns 'true' on successful creation, or if the directory -// already exists. On most systems, the created directories are only readable by -// the current user. But on ChromeOS, they are also world-traversable. +// already exists. The directory is only readable by the current user. // Returns true on success, leaving *error unchanged. // Returns false on failure and sets *error appropriately, if it is non-NULL. BASE_EXPORT bool CreateDirectoryAndGetError(const FilePath& full_path,
diff --git a/base/files/file_util_posix.cc b/base/files/file_util_posix.cc index d13cd46..7c44e03 100644 --- a/base/files/file_util_posix.cc +++ b/base/files/file_util_posix.cc
@@ -715,19 +715,7 @@ for (const FilePath& subpath : base::Reversed(subpaths)) { if (DirectoryExists(subpath)) continue; - -#if BUILDFLAG(IS_CHROMEOS) - // On ChromeOS, create directories that are traversable by group and other - // (rwx--x--x). This is consistent with the creation of files with mode - // (rw-r--r--) on ChromeOS. See also File::DoInitialize() in - // base/files/file_posix.cc and DoCopyDirectory() in - // base/files/file_util_posix.cc. - static constexpr mode_t kMode = S_IRWXU | S_IXGRP | S_IXOTH; // rwx --x --x -#else - static constexpr mode_t kMode = S_IRWXU; // rwx --- --- -#endif - - if (mkdir(subpath.value().c_str(), kMode) == 0) + if (mkdir(subpath.value().c_str(), 0700) == 0) continue; // Mkdir failed, but it might have failed with EEXIST, or some other error // due to the directory appearing out of thin air. This can occur if
diff --git a/base/values.cc b/base/values.cc index 950291bc..83f941d 100644 --- a/base/values.cc +++ b/base/values.cc
@@ -1776,26 +1776,6 @@ ListValue::ListValue() : Value(Type::LIST) {} -bool ListValue::GetDictionary(size_t index, - const DictionaryValue** out_value) const { - const auto& list = GetListDeprecated(); - if (list.size() <= index) - return false; - const base::Value& value = list[index]; - if (!value.is_dict()) - return false; - - if (out_value) - *out_value = static_cast<const DictionaryValue*>(&value); - - return true; -} - -bool ListValue::GetDictionary(size_t index, DictionaryValue** out_value) { - return as_const(*this).GetDictionary( - index, const_cast<const DictionaryValue**>(out_value)); -} - void ListValue::Append(base::Value::Dict in_dict) { list().emplace_back(std::move(in_dict)); }
diff --git a/base/values.h b/base/values.h index 73b91b4..e52f0d9 100644 --- a/base/values.h +++ b/base/values.h
@@ -1416,15 +1416,6 @@ ListValue(); - // Convenience forms of `Get()`. Modifies `out_value` (and returns true) - // only if the index is valid and the Value at that index can be returned - // in the specified form. - // `out_value` is optional and will only be set if non-NULL. - // - // DEPRECATED: prefer `Value::List::operator[]` + `GetIfDict()`. - bool GetDictionary(size_t index, const DictionaryValue** out_value) const; - bool GetDictionary(size_t index, DictionaryValue** out_value); - // Appends a Value to the end of the list. // DEPRECATED: prefer `Value::List::Append()`. using Value::Append;
diff --git a/base/values_unittest.cc b/base/values_unittest.cc index e158ee8..2ed5fcd 100644 --- a/base/values_unittest.cc +++ b/base/values_unittest.cc
@@ -2394,15 +2394,6 @@ EXPECT_FALSE(main_dict.GetListWithoutPathExpansion("dict", nullptr)); EXPECT_TRUE(main_dict.GetListWithoutPathExpansion("list", nullptr)); EXPECT_FALSE(main_dict.GetListWithoutPathExpansion("DNE", nullptr)); - - EXPECT_FALSE(main_list.GetDictionary(0, nullptr)); - EXPECT_FALSE(main_list.GetDictionary(1, nullptr)); - EXPECT_FALSE(main_list.GetDictionary(2, nullptr)); - EXPECT_FALSE(main_list.GetDictionary(3, nullptr)); - EXPECT_FALSE(main_list.GetDictionary(4, nullptr)); - EXPECT_TRUE(main_list.GetDictionary(5, nullptr)); - EXPECT_FALSE(main_list.GetDictionary(6, nullptr)); - EXPECT_FALSE(main_list.GetDictionary(7, nullptr)); } TEST(ValuesTest, SelfSwap) {
diff --git a/build/android/test/incremental_javac_gn/incremental_javac_test_android_library.py b/build/android/test/incremental_javac_gn/incremental_javac_test_android_library.py index e0f7e1f..c84cff0d 100755 --- a/build/android/test/incremental_javac_gn/incremental_javac_test_android_library.py +++ b/build/android/test/incremental_javac_gn/incremental_javac_test_android_library.py
@@ -116,9 +116,8 @@ # GOMA does not work with non-standard output directories. 'use_goma = false', ] - _copy_and_append_gn_args( - options.gn_args_path, out_gn_args_path, - extra_gn_args + ['incremental_javac_test_toggle_gn = false']) + _copy_and_append_gn_args(options.gn_args_path, out_gn_args_path, + extra_gn_args) _run_gn([ '--root-target=' + options.target_name, 'gen', @@ -133,19 +132,19 @@ ninja_args = [_NINJA_PATH, '-C', options.out_dir, gn_path] ninja_output = _run_command(ninja_args, env=ninja_env) if _USING_PARTIAL_JAVAC_MSG in ninja_output: - raise Exception('Incorrectly using partial javac for clean compile.') + raise Exception("Incorrectly using partial javac for clean compile.") _copy_and_append_gn_args( options.gn_args_path, out_gn_args_path, extra_gn_args + ['incremental_javac_test_toggle_gn = true']) ninja_output = _run_command(ninja_args, env=ninja_env) if _USING_PARTIAL_JAVAC_MSG not in ninja_output: - raise Exception('Not using partial javac for incremental compile.') + raise Exception("Not using partial javac for incremental compile.") - expected_output_path = '{}/obj/{}.javac.jar'.format(options.out_dir, - gn_path.replace(':', '/')) + expected_output_path = "{}/lib.java/{}.jar".format(options.out_dir, + gn_path.replace(':', '/')) if not os.path.exists(expected_output_path): - raise Exception('{} not created.'.format(expected_output_path)) + raise Exception("{} not created.".format(expected_output_path)) shutil.copyfile(expected_output_path, options.out_jar)
diff --git a/build/config/android/internal_rules.gni b/build/config/android/internal_rules.gni index 8aca7d2..12be71c 100644 --- a/build/config/android/internal_rules.gni +++ b/build/config/android/internal_rules.gni
@@ -20,9 +20,6 @@ } assert(is_android) -default_android_sdk_dep = "//third_party/android_sdk:android_sdk_java" -_jacoco_dep = "//third_party/jacoco:jacocoagent_java" - # The following _java_*_types variables capture all the existing target types. # If a new type is introduced, please add it to one of these categories, # preferring the more specific resource/library types. @@ -118,8 +115,9 @@ _target_label = get_label_info(":${_parent_invoker.target_name}", "label_no_toolchain") - # Ensure targets match naming patterns so that __assetres, __header, __host, - # and __validate targets work properly. + # Ensure targets match naming patterns so that __assetres, __header, __impl + # targets work properly. Those generated targets allow for effective deps + # filtering. if (filter_exclude([ _type ], _java_resource_types) == []) { if (filter_exclude([ _target_label ], java_resource_patterns) != []) { assert(false, "Invalid java resource target name: $_target_label") @@ -164,6 +162,8 @@ foreach(_possible_dep, invoker.possible_config_deps) { _dep_label = get_label_info(_possible_dep, "label_no_toolchain") if (filter_exclude([ _dep_label ], java_target_patterns) == []) { + # Put the bug number in the target name so that false-positives + # have a hint in the error message about non-existent dependencies. deps += [ "$_dep_label$build_config_target_suffix" ] _dep_gen_dir = get_label_info(_possible_dep, "target_gen_dir") _dep_name = get_label_info(_possible_dep, "name") @@ -1162,7 +1162,12 @@ } template("proguard") { - forward_variables_from(invoker, TESTONLY_AND_VISIBILITY) + forward_variables_from(invoker, + TESTONLY_AND_VISIBILITY + [ + "data", + "data_deps", + "public_deps", + ]) _script = "//build/android/gyp/proguard.py" _deps = invoker.deps @@ -1364,12 +1369,6 @@ _deps += [ ":$_expectations_target" ] } action_with_pydeps(target_name) { - forward_variables_from(invoker, - [ - "data", - "data_deps", - "public_deps", - ]) script = _script deps = _deps inputs = _inputs @@ -1598,9 +1597,6 @@ _r8_path, _custom_d8_path, ] - if (defined(invoker.inputs)) { - inputs += invoker.inputs - } if (!_is_library) { # http://crbug.com/725224. Fix for bots running out of memory. @@ -1893,7 +1889,7 @@ template("bytecode_processor") { action_with_pydeps(target_name) { - forward_variables_from(invoker, TESTONLY_AND_VISIBILITY + [ "data_deps" ]) + forward_variables_from(invoker, TESTONLY_AND_VISIBILITY) _bytecode_checker_script = "$root_build_dir/bin/helper/bytecode_processor" script = "//build/android/gyp/bytecode_processor.py" inputs = [ @@ -2989,7 +2985,7 @@ if (target_name == "chrome_java__header") { # Regression test for: https://crbug.com/1154302 - assert_no_deps = [ "//base:base_java__compile_java" ] + assert_no_deps = [ "//base:base_java__impl" ] } depfile = "$target_gen_dir/$target_name.d" @@ -3130,6 +3126,28 @@ } } + template("java_lib_group") { + forward_variables_from(invoker, [ "testonly" ]) + _group_name = invoker.group_name + not_needed([ "_group_name" ]) + group(target_name) { + if (defined(invoker.deps)) { + deps = [] + foreach(_dep, invoker.deps) { + _target_label = get_label_info(_dep, "label_no_toolchain") + if (filter_exclude([ _target_label ], java_library_patterns) == [] && + filter_exclude([ _target_label ], java_resource_patterns) != []) { + # This is a java library dep, so replace it. + deps += [ "${_target_label}__${_group_name}" ] + } else { + # Transitive java group targets should also include direct deps. + deps += [ _dep ] + } + } + } + } + } + # Create an interface jar from a normal jar. # # Variables @@ -3301,16 +3319,10 @@ _type = invoker.type _is_annotation_processor = _type == "java_annotation_processor" _is_java_binary = _type == "java_binary" || _type == "robolectric_binary" - _is_library = _type == "java_library" _supports_android = defined(invoker.supports_android) && invoker.supports_android _requires_android = defined(invoker.requires_android) && invoker.requires_android - _supports_host = !_requires_android - if (_is_java_binary || _is_annotation_processor) { - assert(!_requires_android && !_supports_android) - } - _bypass_platform_checks = defined(invoker.bypass_platform_checks) && invoker.bypass_platform_checks _is_robolectric = defined(invoker.is_robolectric) && invoker.is_robolectric @@ -3399,9 +3411,6 @@ _jacoco_instrument = !invoker.jacoco_never_instrument && _jacoco_instrument } - if (_jacoco_instrument) { - _invoker_deps += [ _jacoco_dep ] - } if (_build_host_jar) { # Jar files can be needed at runtime (by Robolectric tests or java binaries), @@ -3459,83 +3468,77 @@ } } - _java_assetres_deps = filter_include(_invoker_deps, java_resource_patterns) - - # Cannot use minus operator because it does not work when the operand has - # repeated entries. - _invoker_deps_minus_assetres = - filter_exclude(_invoker_deps, _java_assetres_deps) - _lib_deps = - filter_include(_invoker_deps_minus_assetres, java_library_patterns) - _non_java_deps = filter_exclude(_invoker_deps_minus_assetres, _lib_deps) - - _java_header_deps = [] # Turbine / ijar - - # It would be more ideal to split this into __host and __javac, but we - # combine the two concepts to save on a group() target. - _java_host_deps = [] # Processed host .jar + javac .jar. - _java_validate_deps = [] # Bytecode checker & errorprone. - - foreach(_lib_dep, _lib_deps) { - # Expand //foo/java -> //foo/java:java - _lib_dep = get_label_info(_lib_dep, "label_no_toolchain") - _java_assetres_deps += [ "${_lib_dep}__assetres" ] - _java_header_deps += [ "${_lib_dep}__header" ] - _java_host_deps += [ "${_lib_dep}__host" ] - _java_validate_deps += [ "${_lib_dep}__validate" ] - } - - # APK and base module targets are special because: - # 1) They do not follow java target naming scheme (since they are not - # generally deps, there is no need for them to). - # 2) They do not bother to define a __host target. - # Since __host is used as an indirect dep for the compile_java artifacts, - # add the __compile_java target directly for them. - if (defined(invoker.apk_under_test)) { - _java_assetres_deps += [ "${invoker.apk_under_test}__java__assetres" ] - _java_header_deps += [ "${invoker.apk_under_test}__java__header" ] - _java_validate_deps += [ "${invoker.apk_under_test}__java__validate" ] - _java_host_deps += [ "${invoker.apk_under_test}__compile_java" ] - } - if (defined(invoker.base_module_target)) { - _java_assetres_deps += [ "${invoker.base_module_target}__java__assetres" ] - _java_header_deps += [ "${invoker.base_module_target}__java__header" ] - _java_validate_deps += [ "${invoker.base_module_target}__java__validate" ] - _java_host_deps += [ "${invoker.base_module_target}__compile_java" ] - } - - not_needed([ "_non_java_deps" ]) - if (_is_prebuilt || _has_sources) { - # Classpath deps are used for header and dex targets, they do not need - # __assetres deps. - # _non_java_deps are needed for input_jars_paths that are generated. - _header_classpath_deps = - _java_header_deps + _non_java_deps + [ ":$_build_config_target_name" ] + _java_assetres_deps = + filter_include(_invoker_deps, java_resource_patterns) - _javac_classpath_deps = - _java_host_deps + _non_java_deps + [ ":$_build_config_target_name" ] + # Cannot use minus operator because it does not work when the operand has + # repeated entries. + _invoker_deps_minus_assetres = + filter_exclude(_invoker_deps, _java_assetres_deps) + _lib_deps = + filter_include(_invoker_deps_minus_assetres, java_library_patterns) + _non_java_deps = filter_exclude(_invoker_deps_minus_assetres, _lib_deps) + + _java_header_deps = [] + _java_impl_deps = [] + foreach(_lib_dep, _lib_deps) { + # Expand //foo/java -> //foo/java:java + _lib_dep = get_label_info(_lib_dep, "label_no_toolchain") + + # This is a java library dep, so it has header and impl targets. + _java_header_deps += [ "${_lib_dep}__header" ] + _java_impl_deps += [ "${_lib_dep}__impl" ] + _java_assetres_deps += [ "${_lib_dep}__assetres" ] + } + + # Don't need to depend on the apk-under-test to be packaged. + if (defined(invoker.apk_under_test)) { + _java_header_deps += [ "${invoker.apk_under_test}__java__header" ] + _java_impl_deps += [ "${invoker.apk_under_test}__java__impl" ] + } + + # These deps cannot be passed via invoker.deps since bundle_module targets + # have bundle_module.build_config without the __java suffix, so they are + # special and cannot be passed as regular deps to write_build_config. + if (defined(invoker.base_module_target)) { + _java_header_deps += [ "${invoker.base_module_target}__java__header" ] + _java_impl_deps += [ "${invoker.base_module_target}__java__impl" ] + } + + _extra_java_deps = [] + if (_jacoco_instrument) { + _extra_java_deps += [ "//third_party/jacoco:jacocoagent_java" ] + } _include_android_sdk = _build_device_jar if (defined(invoker.include_android_sdk)) { _include_android_sdk = invoker.include_android_sdk } if (_include_android_sdk) { + _sdk_java_dep = "//third_party/android_sdk:android_sdk_java" if (defined(invoker.alternative_android_sdk_dep)) { - _android_sdk_dep = invoker.alternative_android_sdk_dep - } else { - _android_sdk_dep = default_android_sdk_dep + _sdk_java_dep = invoker.alternative_android_sdk_dep } - _header_classpath_deps += [ "${_android_sdk_dep}__header" ] - _javac_classpath_deps += [ "${_android_sdk_dep}" ] + # This is an android_system_java_prebuilt target, so no headers. + _extra_java_deps += [ _sdk_java_dep ] } + + # Classpath deps is used for header and dex targets, they do not need + # resource deps. + _classpath_deps = _java_header_deps + _non_java_deps + _extra_java_deps + + [ ":$_build_config_target_name" ] + + _full_classpath_deps = + _java_impl_deps + _java_assetres_deps + _non_java_deps + + _extra_java_deps + [ ":$_build_config_target_name" ] } # Often needed, but too hard to figure out when ahead of time. not_needed([ - "_header_classpath_deps", - "_javac_classpath_deps", + "_classpath_deps", + "_full_classpath_deps", ]) if (_java_files != []) { @@ -3619,15 +3622,12 @@ if (defined(invoker.public_deps)) { possible_config_public_deps = invoker.public_deps } + if (defined(_extra_java_deps)) { + possible_config_deps += _extra_java_deps + } if (defined(apk_under_test)) { possible_config_deps += [ apk_under_test ] } - if (defined(_jacoco_instrument) && _jacoco_instrument) { - possible_config_deps += [ _jacoco_dep ] - } - if (defined(_android_sdk_dep)) { - possible_config_deps += [ _android_sdk_dep ] - } supports_android = _supports_android requires_android = _requires_android @@ -3674,6 +3674,9 @@ _header_target_name = "${target_name}__header" } + _public_deps = [] + _analysis_public_deps = [] + if (_has_sources) { if (defined(invoker.enable_errorprone)) { _enable_errorprone = invoker.enable_errorprone @@ -3690,9 +3693,10 @@ "android_library(), or robolectric_library(). " + "Target=$target_name") - # Serves double purpose: Generating R.java, as well as being the - #__assetres target (instead of using a separate group). - _fake_rjava_target = "${target_name}__assetres" + # has _resources at the end so it looks like a resources pattern, since + # it does act like one (and other resources patterns need to depend on + # this before they can read its output R.txt). + _fake_rjava_target = "${target_name}__rjava_resources" generate_r_java(_fake_rjava_target) { deps = [ ":$_build_config_target_name" ] + _java_assetres_deps + _non_java_deps @@ -3719,17 +3723,10 @@ # Filtering out generated files resulted in no files left. group(target_name) { not_needed(invoker, "*") - deps = _header_classpath_deps } } else { compile_java(target_name) { - forward_variables_from(invoker, - "*", - TESTONLY_AND_VISIBILITY + [ "deps" ]) - deps = _header_classpath_deps - if (defined(invoker.deps)) { - deps += invoker.deps - } + forward_variables_from(invoker, "*", TESTONLY_AND_VISIBILITY) output_jar_path = invoker.output_jar_path enable_errorprone = _enable_errorprone use_turbine = defined(invoker.use_turbine) && invoker.use_turbine @@ -3750,6 +3747,10 @@ chromium_code = _chromium_code supports_android = _supports_android include_android_sdk = _is_robolectric || _requires_android + if (!defined(deps)) { + deps = [] + } + deps += _classpath_deps } } } @@ -3773,6 +3774,7 @@ generated_jar_path = _generated_jar_path deps = _annotation_processor_deps } + _public_deps += [ ":$_header_target_name" ] _compile_java_target = "${_main_target_name}__compile_java" compile_java_helper(_compile_java_target) { @@ -3798,16 +3800,14 @@ generated_jar_path = _generated_jar_path output_jar_path = "$target_out_dir/$target_name.errorprone.stamp" } - _java_validate_deps += [ ":$_compile_java_errorprone_target" ] + _analysis_public_deps += [ ":$_compile_java_errorprone_target" ] } } # _has_sources if (_is_prebuilt || _build_device_jar || _build_host_jar) { + _unprocessed_jar_deps = [] if (_has_sources) { - _unprocessed_jar_deps = [ ":$_compile_java_target" ] - } else { - # jars might be generated by a dep. - _unprocessed_jar_deps = _non_java_deps + _unprocessed_jar_deps += [ ":$_compile_java_target" ] } } @@ -3843,7 +3843,7 @@ "--output-jar", rebase_path(_rewritten_jar, root_build_dir), ] - deps = _unprocessed_jar_deps + _javac_classpath_deps + + deps = _unprocessed_jar_deps + _full_classpath_deps + [ invoker.bytecode_rewriter_target ] } @@ -3860,10 +3860,14 @@ input_jar = _unprocessed_jar_path output_jar = _final_ijar_path - # ijar needs only _unprocessed_jar_deps, but this also needs to export - # __header target from deps. - deps = _unprocessed_jar_deps + _java_header_deps + # Normally ijar does not require any deps, but: + # 1 - Some jars are bytecode rewritten by _unprocessed_jar_deps. + # 2 - Other jars need to be unzipped by _non_java_deps. + # 3 - It is expected that depending on a header target implies depending + # on its transitive header target deps via _java_header_deps. + deps = _unprocessed_jar_deps + _non_java_deps + _java_header_deps } + _public_deps += [ ":$_header_target_name" ] } if (_build_host_jar || _build_device_jar) { @@ -3871,15 +3875,11 @@ (!defined(invoker.enable_bytecode_checks) || invoker.enable_bytecode_checks) && android_static_analysis != "off" if (_enable_bytecode_checks) { - _validate_target_name = "${target_name}__validate" - bytecode_processor(_validate_target_name) { + _bytecode_checks_target = "${target_name}__validate_classpath" + bytecode_processor(_bytecode_checks_target) { forward_variables_from(invoker, [ "missing_classes_allowlist" ]) - deps = _unprocessed_jar_deps + _javac_classpath_deps + + deps = _unprocessed_jar_deps + _full_classpath_deps + [ ":$_build_config_target_name" ] - data_deps = _java_validate_deps - if (defined(_compile_java_errorprone_target)) { - data_deps += [ ":$_compile_java_errorprone_target" ] - } include_android_sdk = _requires_android || _is_robolectric target_label = @@ -3888,12 +3888,13 @@ build_config = _build_config is_prebuilt = _is_prebuilt } + _analysis_public_deps += [ ":$_bytecode_checks_target" ] } else { not_needed(invoker, [ "missing_classes_allowlist" ]) } if (_build_host_jar) { - _process_host_jar_target_name = "${target_name}__host" + _process_host_jar_target_name = "${target_name}__process_host" process_java_library(_process_host_jar_target_name) { forward_variables_from(invoker, [ @@ -3904,20 +3905,15 @@ # Robolectric tests require these to be on swarming. data = [ _host_processed_jar_path ] input_jar_path = _unprocessed_jar_path - deps = _unprocessed_jar_deps + _javac_classpath_deps + deps = _unprocessed_jar_deps + _full_classpath_deps output_jar_path = _host_processed_jar_path jacoco_instrument = _jacoco_instrument if (_jacoco_instrument) { java_files = _java_files java_sources_file = _java_sources_file } - - # _java_host_deps isn't necessary for process_java_library(), but is - # necessary so that this target can be used to depend on transitive - # __device targets without the need to create a separate group() - # target. This trade-off works because process_java_library is fast. - deps += _java_host_deps } + _public_deps += [ ":${_process_host_jar_target_name}" ] } if (_build_device_jar) { @@ -3930,8 +3926,7 @@ "jar_included_patterns", ]) input_jar_path = _unprocessed_jar_path - - deps = _unprocessed_jar_deps + _javac_classpath_deps + deps = _unprocessed_jar_deps + _full_classpath_deps output_jar_path = _device_processed_jar_path jacoco_instrument = _jacoco_instrument if (_jacoco_instrument) { @@ -3940,13 +3935,14 @@ } } _process_device_jar_deps = [ ":${_process_device_jar_target_name}" ] + _public_deps += _process_device_jar_deps } else { assert(_unprocessed_jar_path == _device_processed_jar_path) - _process_device_jar_deps = _unprocessed_jar_deps + _process_device_jar_deps = + _unprocessed_jar_deps + _full_classpath_deps } - _dex_target_name = "${target_name}__dex" - dex(_dex_target_name) { + dex("${target_name}__dex") { forward_variables_from(invoker, [ "desugar_jars_paths", @@ -3966,24 +3962,16 @@ # Desugaring with D8 requires full classpath. build_config = _build_config final_ijar_path = _final_ijar_path - deps += _header_classpath_deps + [ ":$_header_target_name" ] + deps += _classpath_deps + [ + ":$_build_config_target_name", + ":$_header_target_name", + ] } enable_multidex = false is_library = true - - # proguard_configs listed on java_library targets need to be marked - # as inputs to at least one target so that "gn analyze" will know - # about them. Although this target doesn't use them, it's a convenient spot - # to list them. - # https://crbug.com/827197 - if (compute_inputs_for_analyze && defined(invoker.proguard_configs)) { - inputs = invoker.proguard_configs - - # For the aapt-generated proguard rules. - deps += _non_java_deps + _srcjar_deps - } } + _public_deps += [ ":${target_name}__dex" ] } } @@ -4008,100 +3996,57 @@ extra_classpath_jars = [ _robolectric_jar_path ] } } + _public_deps += [ ":$_java_binary_script_target_name" ] } - if (!defined(_validate_target_name)) { - _validate_target_name = "${target_name}__validate" + # The __impl target contains all non-analysis steps for this template. + # Having this separated out from the main target (which contains analysis + # steps) allows analysis steps for this target to be run concurrently with + # the non-analysis steps of other targets that depend on this one. + group("${target_name}__impl") { + public_deps = _public_deps - # Allow other targets to depend on this __validate one. - group(_validate_target_name) { - deps = _java_validate_deps + # proguard_configs listed on java_library targets need to be marked + # as inputs to at least one target so that "gn analyze" will know + # about them. Although this target doesn't use them, it's a convenient spot + # to list them. + # https://crbug.com/827197 + if (compute_inputs_for_analyze && defined(invoker.proguard_configs)) { + assert(_build_host_jar || _build_device_jar) + data = invoker.proguard_configs + + # For the aapt-generated proguard rules. + deps = _non_java_deps + _srcjar_deps } } - if (_supports_host && !defined(_process_host_jar_target_name)) { - group("${target_name}__host") { - deps = _java_host_deps + java_lib_group("${target_name}__assetres") { + deps = _invoker_deps + group_name = "assetres" + + if (defined(_fake_rjava_target)) { + deps += [ ":$_fake_rjava_target" ] } } - # robolectric_library can depend on java_library, so java_library must - # define __assetres. - if ((_is_library || _supports_android || _is_robolectric) && - !defined(_fake_rjava_target)) { - group("${target_name}__assetres") { - if (_supports_android || _is_robolectric) { - deps = _java_assetres_deps - } - } - } - - # The top-level group is used: - # 1) To allow building the target explicitly via ninja, - # 2) To trigger all analysis deps, - # 3) By custom action() targets that want to use artifacts as inputs. group(target_name) { forward_variables_from(invoker, [ "assert_no_deps", "data", "data_deps", + "deps", + "public_deps", "visibility", ]) - if (_requires_android || (_supports_android && _is_library)) { - # For non-robolectric targets, depend on other java target's top-level - # groups so that the __dex step gets depended on. - forward_variables_from(invoker, - [ - "deps", - "public_deps", - ]) - if (!defined(deps)) { - deps = [] - } - if (!defined(public_deps)) { - public_deps = [] - } - } else { - # For robolectric targets, depend only on non-java deps and the specific - # subtargets below, which will not include __dex. - deps = _non_java_deps + if (!defined(public_deps)) { public_deps = [] - if (defined(invoker.public_deps)) { - public_deps += - filter_exclude(invoker.public_deps, java_target_patterns) - } } - if (defined(_jacoco_instrument) && _jacoco_instrument) { - deps += [ _jacoco_dep ] - } - if (defined(_process_device_jar_target_name)) { - public_deps += [ ":$_process_device_jar_target_name" ] - } - if (defined(_dex_target_name)) { - public_deps += [ ":$_dex_target_name" ] - } - if (_supports_android && _is_library) { - # Robolectric targets define __assetres, but there's no need to build it - # by default. - public_deps += [ ":${target_name}__assetres" ] - } - if (_supports_host) { - # android_* targets define __host, but there's no need to build it by - # default. - public_deps += [ ":${target_name}__host" ] - } - if (_is_java_binary) { - public_deps += [ ":$_java_binary_script_target_name" ] - } + public_deps += [ ":${target_name}__impl" ] if (!defined(data_deps)) { data_deps = [] } - if (defined(_validate_target_name)) { - data_deps += [ ":$_validate_target_name" ] - } else { - data_deps += _java_validate_deps - } + data_deps += _analysis_public_deps } } }
diff --git a/build/config/android/rules.gni b/build/config/android/rules.gni index 486735c9..15d99775 100644 --- a/build/config/android/rules.gni +++ b/build/config/android/rules.gni
@@ -1031,9 +1031,9 @@ } if (defined(invoker.alternative_android_sdk_dep)) { - _android_sdk_dep = invoker.alternative_android_sdk_dep + _deps += [ invoker.alternative_android_sdk_dep ] } else { - _android_sdk_dep = default_android_sdk_dep + _deps += [ "//third_party/android_sdk:android_sdk_java" ] } _resource_files = [] @@ -1072,7 +1072,7 @@ ]) r_text = _r_text_out_path - possible_config_deps = _deps + [ _android_sdk_dep ] + possible_config_deps = _deps # Always merge manifests from resources. # * Might want to change this at some point for consistency and clarity, @@ -1103,7 +1103,7 @@ } # Depend on non-library deps and on __assetres subtargets of library deps. - deps = filter_exclude(_deps, _lib_deps) + [ _android_sdk_dep ] + deps = filter_exclude(_deps, _lib_deps) foreach(_lib_dep, _lib_deps) { # Expand //foo/java -> //foo/java:java _lib_dep = get_label_info(_lib_dep, "label_no_toolchain") @@ -1237,34 +1237,17 @@ supports_android = true possible_config_deps = _invoker_deps } - - _assetres_deps = filter_include(_invoker_deps, java_resource_patterns) - _invoker_deps_minus_assetres = filter_exclude(_invoker_deps, _assetres_deps) - _lib_deps = - filter_include(_invoker_deps_minus_assetres, java_library_patterns) - - _expanded_lib_deps = [] - foreach(_lib_dep, _lib_deps) { - _expanded_lib_deps += [ get_label_info(_lib_dep, "label_no_toolchain") ] - } foreach(_group_name, [ - "assetres", "header", - "host", - "validate", + "impl", + "assetres", ]) { - group("${target_name}__$_group_name") { - deps = [] - foreach(_lib_dep, _expanded_lib_deps) { - deps += [ "${_lib_dep}__${_group_name}" ] - } - if (_group_name == "assetres") { - deps += _assetres_deps - } + java_lib_group("${target_name}__${_group_name}") { + deps = _invoker_deps + group_name = _group_name } } - group(target_name) { forward_variables_from(invoker, "*", @@ -1380,7 +1363,7 @@ if (defined(invoker.alternative_android_sdk_dep)) { _android_sdk_dep = invoker.alternative_android_sdk_dep } else { - _android_sdk_dep = default_android_sdk_dep + _android_sdk_dep = "//third_party/android_sdk:android_sdk_java" } # A package name or a manifest is required to have resources. This is @@ -1510,13 +1493,8 @@ deps = [ ":$_apkbuilder_target_name", ":$_build_config_target_name", - ":${_java_binary_target_name}__host", - ":${_java_binary_target_name}__java_binary_script", - ":${_java_binary_target_name}__validate", + ":$_java_binary_target_name", ] - - # Add non-libary deps, since the __host target does not depend on them. - deps += filter_exclude(_invoker_deps, java_library_patterns) } } @@ -1760,7 +1738,7 @@ # output = "$root_build_dir/MyLibrary.jar" # } template("dist_dex") { - _deps = [ default_android_sdk_dep ] + _deps = [ "//third_party/android_sdk:android_sdk_java" ] if (defined(invoker.deps)) { _deps += invoker.deps } @@ -1781,6 +1759,8 @@ build_config = _build_config } + _deps += [ ":$_build_config_target_name" ] + dex(target_name) { forward_variables_from(invoker, TESTONLY_AND_VISIBILITY + [ @@ -1791,7 +1771,7 @@ "proguard_enable_obfuscation", "min_sdk_version", ]) - deps = [ ":$_build_config_target_name" ] + _deps + deps = _deps build_config = _build_config enable_multidex = false output = invoker.output @@ -2615,7 +2595,7 @@ } } - _final_deps = [ ":$_java_target_name" ] + _final_deps = [] _enable_main_dex_list = _enable_multidex && _min_sdk_version < 21 if (_enable_main_dex_list) { @@ -2627,7 +2607,7 @@ if (defined(invoker.alternative_android_sdk_dep)) { _android_sdk_dep = invoker.alternative_android_sdk_dep } else { - _android_sdk_dep = default_android_sdk_dep + _android_sdk_dep = "//third_party/android_sdk:android_sdk_java" } if (defined(_shared_resources_allowlist_target)) { @@ -3088,8 +3068,6 @@ output = _dist_ijar_path data = [ _dist_ijar_path ] use_interface_jars = true - - # This will use __header under-the-hood. deps = [ ":$_java_target_name" ] } } @@ -3097,7 +3075,7 @@ if (_uses_static_library_synchronized_proguard) { _final_dex_target_dep = "${invoker.static_library_provider}__dexsplitter" } else if ((_is_bundle_module && _proguard_enabled) || _omit_dex) { - # No library dep needed. + _final_deps += [ ":$_java_target_name" ] } else if (_incremental_apk) { if (defined(invoker.enable_proguard_checks)) { not_needed(invoker, [ "enable_proguard_checks" ]) @@ -3120,24 +3098,18 @@ ":$_java_target_name", ] if (_proguard_enabled) { - # Generates proguard configs - deps += [ ":$_compile_resources_target" ] + deps += _invoker_deps + [ ":$_compile_resources_target" ] proguard_mapping_path = _proguard_mapping_path proguard_sourcefile_suffix = "$android_channel-$_version_code" has_apk_under_test = defined(invoker.apk_under_test) + } else if (_min_sdk_version >= default_min_sdk_version) { + # Enable dex merging only when min_sdk_version is >= what the library + # .dex files were created with. + input_dex_filearg = + "@FileArg(${_rebased_build_config}:final_dex:all_dex_files)" } else { - if (_min_sdk_version >= default_min_sdk_version) { - # Enable dex merging only when min_sdk_version is >= what the library - # .dex files were created with. - input_dex_filearg = - "@FileArg(${_rebased_build_config}:final_dex:all_dex_files)" - - # Pure dex-merge. - enable_desugar = false - } else { - input_classes_filearg = - "@FileArg($_rebased_build_config:deps_info:device_classpath)" - } + input_classes_filearg = + "@FileArg($_rebased_build_config:deps_info:device_classpath)" } if (_is_static_library_provider) { @@ -3159,14 +3131,11 @@ # their respective dex steps. False positives that were suppressed at # per-target dex steps are emitted here since this may use jar files # rather than dex files. - if (!defined(enable_desugar)) { - ignore_desugar_missing_deps = true - } + ignore_desugar_missing_deps = true if (_enable_main_dex_list) { - # Generates main-dex config. - deps += [ ":$_compile_resources_target" ] extra_main_dex_proguard_config = _generated_proguard_main_dex_config + deps += [ ":$_compile_resources_target" ] } } @@ -3303,11 +3272,11 @@ name = "${invoker.name}.apk" build_config = _build_config res_size_info_path = _res_size_info_path - deps = [ - ":$_build_config_target", - ":$_compile_resources_target", - ":$_java_target_name", - ] + deps = _invoker_deps + [ + ":$_build_config_target", + ":$_compile_resources_target", + ":$_java_target_name", + ] } _final_deps += [ ":$_size_info_target" ] } else { @@ -3465,7 +3434,10 @@ args += [ "--native-libs=$_rebased_loadable_modules" ] } } - _final_deps += [ ":$_write_installer_json_rule_name" ] + _final_deps += [ + ":$_java_target_name", + ":$_write_installer_json_rule_name", + ] } # Generate apk operation related script. @@ -3552,8 +3524,6 @@ ]) build_config = _build_config build_config_dep = ":$_build_config_target" - - # This will use library subtargets under-the-hood deps = [ ":$_java_target_name" ] if (defined(invoker.lint_suppressions_dep)) { deps += [ invoker.lint_suppressions_dep ] @@ -3590,7 +3560,8 @@ } # Include unstripped native libraries so tests can symbolize stacks. - data_deps += _all_native_libs_deps + [ ":${_java_target_name}__validate" ] + data_deps += _all_native_libs_deps + if (_enable_lint) { data_deps += [ ":${target_name}__lint" ] } @@ -4978,9 +4949,9 @@ _rebased_build_config = rebase_path(_build_config, root_build_dir) _build_config_target = "$_target_name$build_config_target_suffix" if (defined(invoker.proguard_android_sdk_dep)) { - _android_sdk_dep = invoker.proguard_android_sdk_dep + proguard_android_sdk_dep_ = invoker.proguard_android_sdk_dep } else { - _android_sdk_dep = default_android_sdk_dep + proguard_android_sdk_dep_ = "//third_party/android_sdk:android_sdk_java" } if (_proguard_enabled) { @@ -4994,7 +4965,7 @@ write_build_config(_build_config_target) { type = "android_app_bundle" - possible_config_deps = _module_targets + [ _android_sdk_dep ] + possible_config_deps = _module_targets + [ proguard_android_sdk_dep_ ] build_config = _build_config proguard_enabled = _proguard_enabled module_build_configs = _module_build_configs
diff --git a/build/rust/run_rs_bindings_from_cc.py b/build/rust/run_rs_bindings_from_cc.py index c031f44df..73b78ff 100755 --- a/build/rust/run_rs_bindings_from_cc.py +++ b/build/rust/run_rs_bindings_from_cc.py
@@ -12,7 +12,7 @@ # Set up path to be able to import build_utils. THIS_DIR = os.path.dirname(os.path.abspath(__file__)) -CHROMIUM_SRC_DIR = os.path.join(THIS_DIR, os.pardir, os.pardir) +CHROMIUM_SRC_DIR = os.path.relpath(os.path.join(THIS_DIR, os.pardir, os.pardir)) sys.path.append(THIS_DIR) sys.path.append(os.path.join(CHROMIUM_SRC_DIR, 'build', 'android', 'gyp')) from run_bindgen import filter_clang_args @@ -59,20 +59,17 @@ nargs=argparse.REMAINDER) args = parser.parse_args() - # Start building the cmdline args to pass to the `rs_bindings_from_cc` tool. - genargs = [RS_BINDINGS_FROM_CC_EXE_PATH] - # Output paths - genargs.extend(["--rs_out", args.rs_out]) - genargs.extend(["--cc_out", args.cc_out]) + generator_args = [] + generator_args.append("--rs_out={0}".format(os.path.relpath(args.rs_out))) + generator_args.append("--cc_out={0}".format(os.path.relpath(args.cc_out))) if "CRUBIT_DEBUG" in os.environ: - genargs.extend(["--ir_out", args.rs_out.replace(".rs", ".ir")]) + generator_args.append("--ir_out={0}".format( + os.path.relpath(args.rs_out).replace(".rs", ".ir"))) # Public headers. - genargs.extend([ - "--public_headers", - ",".join([os.path.relpath(hdr) for hdr in args.public_headers.split(",")]) - ]) + generator_args.append("--public_headers={0}".format(",".join( + [os.path.relpath(hdr) for hdr in args.public_headers.split(",")]))) # Targets to headers map. with open(args.targets_and_headers_from_gn, "r") as f: @@ -81,38 +78,52 @@ hdrs = entry["h"] for i in range(len(hdrs)): hdrs[i] = os.path.relpath(hdrs[i]) - genargs.extend(["--targets_and_headers", json.dumps(targets_and_headers)]) + generator_args.append("--targets_and_headers={0}".format( + json.dumps(targets_and_headers))) # All Crubit invocations in Chromium share the following cmdline args. - genargs.extend(["--rustfmt_exe_path", RUSTFMT_EXE_PATH]) - genargs.extend(["--rustfmt_config_path", RUSTFMT_CONFIG_PATH]) - genargs.extend([ - "--crubit_support_path", - "third_party/crubit/src/rs_bindings_from_cc/support" - ]) + generator_args.append(f"--rustfmt_exe_path={RUSTFMT_EXE_PATH}") + generator_args.append(f"--rustfmt_config_path={RUSTFMT_CONFIG_PATH}") + generator_args.append( + "--crubit_support_path=third_party/crubit/src/rs_bindings_from_cc/support" + ) + + # Long cmdlines may not work - work around that by using Abseil's `--flagfile` + # https://abseil.io/docs/python/guides/flags#a-note-about---flagfile + # + # Note that `clang_args` are not written to the flag file, because Abseil's + # flag parsing code is only aware of `ABSL_FLAG`-declared flags and doesn't + # know about Clang args (e.g. `-W...` or `-I...`). + params_file_path = os.path.relpath(args.rs_out).replace(".rs", ".params") + with open(params_file_path, "w") as f: + for line in generator_args: + print(line, file=f) # Clang arguments. # # The call to `filter_clang_args` is needed to avoid the following error: # error: unable to find plugin 'find-bad-constructs' - genargs.extend(filter_clang_args(args.clang_args)) + clang_args = [] + clang_args.extend(filter_clang_args(args.clang_args)) # TODO(crbug.com/1329611): This warning needs to be suppressed, because # otherwise Crubit/Clang complains as follows: # error: .../third_party/rust-toolchain/bin/rs_bindings_from_cc: # 'linker' input unused [-Werror,-Wunused-command-line-argument] # Maybe `build/rust/rs_bindings_from_cc.gni` gives too much in `args`? But # then `{{cflags}}` seems perfectly reasonable... - genargs += ["-Wno-unused-command-line-argument"] + clang_args += ["-Wno-unused-command-line-argument"] # Print a copy&pastable final cmdline when asked for debugging help. + cmdline = [RS_BINDINGS_FROM_CC_EXE_PATH, f"--flagfile={params_file_path}"] + cmdline.extend(clang_args) if "CRUBIT_DEBUG" in os.environ: - pretty_cmdline = format_cmdline(genargs) + pretty_cmdline = format_cmdline(cmdline) print(f"CRUBIT_DEBUG: CMDLINE: {pretty_cmdline}", file=sys.stderr) # TODO(crbug.com/1329611): run_bindgen.py removes the outputs when the tool # fails. Maybe we need to do something similar here? OTOH in most failure # modes Crubit will fail *before* generating its outputs... - return subprocess.run(genargs).returncode + return subprocess.run(cmdline).returncode if __name__ == '__main__':
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 1be97fe..1ecdac0f 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
@@ -4,6 +4,7 @@ package org.chromium.chrome.browser.customtabs; +import android.graphics.Rect; import android.view.View; import androidx.annotation.NonNull; @@ -235,6 +236,21 @@ mCustomTabHeightStrategy.setScrimFraction(scrimFraction); } + @Override + protected Rect getAppRectInWindow() { + // This is necessary if app handler cannot rely on the popup window that ensures the menu + // will not be clipped off the screen, which can happen if Window#FLAGS_LAYOUT_NO_LIMITS + // is set to allow the app to be drawn outside the screen in partial CCT. + if (mIntentDataProvider.get().isPartialHeightCustomTab()) { + View coord = mActivity.findViewById(R.id.coordinator); + int[] location = new int[2]; + coord.getLocationInWindow(location); + return new Rect(location[0], location[1], location[0] + coord.getWidth(), + location[1] + coord.getHeight()); + } + return super.getAppRectInWindow(); + } + /** * Delegates changing the background color to the {@link CustomTabHeightStrategy}. * Returns {@code true} if any action were taken, {@code false} if not.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/PartialCustomTabHeightStrategy.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/PartialCustomTabHeightStrategy.java index d21ed918f..f701e79 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/PartialCustomTabHeightStrategy.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/PartialCustomTabHeightStrategy.java
@@ -131,6 +131,9 @@ @Nullable private Runnable mFinishRunnable; + // Y offset when a dragging gesture starts. + private int mDraggingStartY; + /** A callback to be called once the Custom Tab has been resized. */ interface OnResizedCallback { /** The Custom Tab has been resized. */ @@ -300,6 +303,7 @@ @Override public void onAnimationEnd(Animator animator) { mSpinner.stop(); + mSpinnerView.setVisibility(View.GONE); } @Override public void onAnimationCancel(Animator animator) {} @@ -546,12 +550,20 @@ mActivity.getWindow().setAttributes(attributes); if (mFinishRunnable != null) return; - assert mSpinnerView != null; - centerSpinnerVertically((ViewGroup.LayoutParams) mSpinnerView.getLayoutParams()); + // Show the spinner lazily, only when the tab is dragged _up_, which requires showing + // more area than initial state. + if (mStatus != HeightStatus.TRANSITION + && (mSpinnerView == null || mSpinnerView.getVisibility() != View.VISIBLE) + && y < mDraggingStartY) { + showSpinnerView(); + } + if (mSpinnerView != null) { + centerSpinnerVertically((ViewGroup.LayoutParams) mSpinnerView.getLayoutParams()); + } } private void onMoveStart() { - showSpinnerView(); + mDraggingStartY = mActivity.getWindow().getAttributes().y; updateNavbarVisibility(false); } @@ -564,10 +576,12 @@ // TODO(crbug.com/1328555): Look into observing a view resize event to ensure the fade // animation can always cover the transition artifact. - mSpinnerView.animate() - .alpha(0f) - .setDuration(SPINNER_FADEOUT_DURATION_MS) - .setListener(mSpinnerFadeoutAnimatorListener); + if (mSpinnerView != null && mSpinnerView.getVisibility() == View.VISIBLE) { + mSpinnerView.animate() + .alpha(0f) + .setDuration(SPINNER_FADEOUT_DURATION_MS) + .setListener(mSpinnerFadeoutAnimatorListener); + } updateNavbarVisibility(true); }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbar.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbar.java index ba2bab80..f4d4cda 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbar.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbar.java
@@ -89,9 +89,6 @@ import org.chromium.ui.widget.Toast; import org.chromium.url.GURL; -import java.util.ArrayList; -import java.util.List; - /** * The Toolbar layout to be used for a custom tab. This is used for both phone and tablet UIs. */ @@ -614,9 +611,10 @@ /** * Custom tab-specific implementation of the LocationBar interface. */ - private class CustomTabLocationBar implements LocationBar, UrlBar.UrlBarDelegate, - LocationBarDataProvider.Observer, - View.OnLongClickListener { + @VisibleForTesting + class CustomTabLocationBar + implements LocationBar, UrlBar.UrlBarDelegate, LocationBarDataProvider.Observer, + View.OnLongClickListener { private static final int TITLE_ANIM_DELAY_MS = 800; private static final int BRANDING_DELAY_MS = 1800; @@ -625,6 +623,12 @@ private static final int STATE_DOMAIN_AND_TITLE = 2; private int mState = STATE_DOMAIN_ONLY; + // Used for After branding runnables + private static final int KEY_UPDATE_TITLE_POST_BRANDING = 0; + private static final int KEY_UPDATE_URL_POST_BRANDING = 1; + private static final int KEY_UPDATE_ICON_POST_BRANDING = 2; + private static final int TOTAL_POST_BRANDING_KEYS = 3; + private LocationBarDataProvider mLocationBarDataProvider; private Supplier<EphemeralTabCoordinator> mEphemeralTabCoordinatorSupplier; private Supplier<ModalDialogManager> mModalDialogManagerSupplier; @@ -644,8 +648,9 @@ } }; + private final Runnable[] mAfterBrandingRunnables = new Runnable[TOTAL_POST_BRANDING_KEYS]; private boolean mCurrentlyShowingBranding; - private List<Runnable> mAfterBrandingRunnables; + private boolean mBrandingStarted; private CallbackController mCallbackController = new CallbackController(); public View getLayout() { @@ -661,8 +666,8 @@ } public void showBranding() { + mBrandingStarted = true; mCurrentlyShowingBranding = true; - mAfterBrandingRunnables = new ArrayList<>(); // Store the title and domain setting. final boolean showTitle = mState != STATE_DOMAIN_ONLY; @@ -678,10 +683,7 @@ mCurrentlyShowingBranding = false; setUrlBarHidden(!showUrlBar); setShowTitle(showTitle); - for (Runnable runnable : mAfterBrandingRunnables) { - runnable.run(); - } - mAfterBrandingRunnables.clear(); + runAfterBrandingRunnables(); }); PostTask.postDelayedTask(UiThreadTaskTraits.DEFAULT, hideBranding, BRANDING_DELAY_MS); } @@ -860,10 +862,20 @@ mTitleAnimationStarter.run(); } + private void runAfterBrandingRunnables() { + for (int i = 0; i < mAfterBrandingRunnables.length; i++) { + Runnable runnable = mAfterBrandingRunnables[i]; + if (runnable != null) { + runnable.run(); + mAfterBrandingRunnables[i] = null; + } + } + } + private void updateSecurityIcon() { if (mState == STATE_TITLE_ONLY) return; if (mCurrentlyShowingBranding) { - mAfterBrandingRunnables.add(this::updateSecurityIcon); + mAfterBrandingRunnables[KEY_UPDATE_ICON_POST_BRANDING] = this::updateSecurityIcon; return; } @@ -884,7 +896,7 @@ private void updateTitleBar() { if (mCurrentlyShowingBranding) { - mAfterBrandingRunnables.add(this::updateTitleBar); + mAfterBrandingRunnables[KEY_UPDATE_TITLE_POST_BRANDING] = this::updateTitleBar; return; } String title = mLocationBarDataProvider.getTitle(); @@ -902,9 +914,7 @@ // Delay the title animation until security icon animation finishes. // If this is updated after branding, we don't need to wait. PostTask.postDelayedTask(UiThreadTaskTraits.DEFAULT, mTitleAnimationStarter, - mAfterBrandingRunnables != null && mAfterBrandingRunnables.size() > 0 - ? 0 - : TITLE_ANIM_DELAY_MS); + mBrandingStarted ? 0 : TITLE_ANIM_DELAY_MS); } mTitleBar.setText(title); @@ -912,7 +922,7 @@ private void updateUrlBar() { if (mCurrentlyShowingBranding) { - mAfterBrandingRunnables.add(this::updateUrlBar); + mAfterBrandingRunnables[KEY_UPDATE_URL_POST_BRANDING] = this::updateUrlBar; return; } Tab tab = getCurrentTab(); @@ -1042,5 +1052,10 @@ } return false; } + + @VisibleForTesting + void setAnimDelegateForTesting(CustomTabToolbarAnimationDelegate animDelegate) { + mAnimDelegate = animDelegate; + } } }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ui/RootUiCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/ui/RootUiCoordinator.java index c1752306..69a8e2ab 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ui/RootUiCoordinator.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ui/RootUiCoordinator.java
@@ -1223,13 +1223,7 @@ mActivityLifecycleDispatcher, mToolbarManager, mAppMenuDelegate, mActivity.getWindow().getDecorView(), mActivity.getWindow().getDecorView().findViewById(R.id.menu_anchor_stub), - () -> { - View coord = mActivity.findViewById(R.id.coordinator); - int[] location = new int[2]; - coord.getLocationInWindow(location); - return new Rect(location[0], location[1], location[0] + coord.getWidth(), - location[1] + coord.getHeight()); - }); + this::getAppRectInWindow); AppMenuCoordinatorFactory.setExceptionReporter( (throwable) -> ChromePureJavaExceptionReporter.reportJavaException( @@ -1242,6 +1236,15 @@ } } + /** + * Returns {@link Rect} that represents the app client area the app menu should fit in. + */ + protected Rect getAppRectInWindow() { + Rect appRect = new Rect(); + mActivity.getWindow().getDecorView().getWindowVisibleDisplayFrame(appRect); + return appRect; + } + private void hideAppMenu() { if (mAppMenuCoordinator != null) mAppMenuCoordinator.getAppMenuHandler().hideAppMenu(); }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/PartialCustomTabHeightStrategyTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/PartialCustomTabHeightStrategyTest.java index 408c545c..443aa5c 100644 --- a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/PartialCustomTabHeightStrategyTest.java +++ b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/PartialCustomTabHeightStrategyTest.java
@@ -9,8 +9,10 @@ import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyFloat; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyObject; +import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -479,6 +481,54 @@ assertTrue("Close click handler should be called.", closed[0]); } + @Test + public void showSpinnerOnDragUpOnly() { + PartialCustomTabHeightStrategy strategy = createPcctAtHeight(500); + + verify(mWindow).addFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL); + verify(mWindow).clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND); + + assertEquals(1, mAttributeResults.size()); + assertEquals(MAX_INIT_POS, mAttributeResults.get(0).y); + + when(mSpinnerView.getVisibility()).thenReturn(View.GONE); + + // Pass null because we have a mock Activity and we don't depend on the GestureDetector + // inside as we test MotionEvents directly. + PartialCustomTabHeightStrategy.PartialCustomTabHandleStrategy handleStrategy = + strategy.new PartialCustomTabHandleStrategy(null); + + // action down + handleStrategy.onTouchEvent(MotionEvent.obtain(SystemClock.uptimeMillis(), + SystemClock.uptimeMillis(), MotionEvent.ACTION_DOWN, DEVICE_WIDTH / 2, 1500, 0)); + + // action move + handleStrategy.onTouchEvent(MotionEvent.obtain(SystemClock.uptimeMillis(), + SystemClock.uptimeMillis(), MotionEvent.ACTION_MOVE, DEVICE_WIDTH / 2, 1450, 0)); + + // Verify the spinner is visible. + verify(mSpinnerView, times(1)).setVisibility(View.VISIBLE); + when(mSpinnerView.getVisibility()).thenReturn(View.VISIBLE); + clearInvocations(mSpinnerView); + + // action up + handleStrategy.onTouchEvent(MotionEvent.obtain(SystemClock.uptimeMillis(), + SystemClock.uptimeMillis(), MotionEvent.ACTION_UP, DEVICE_WIDTH / 2, 1400, 0)); + + // Wait animation to finish. + shadowOf(Looper.getMainLooper()).idle(); + when(mSpinnerView.getVisibility()).thenReturn(View.GONE); + + // Now the tab is full-height. Start dragging down. + handleStrategy.onTouchEvent(MotionEvent.obtain(SystemClock.uptimeMillis(), + SystemClock.uptimeMillis(), MotionEvent.ACTION_DOWN, DEVICE_WIDTH / 2, 500, 0)); + handleStrategy.onTouchEvent(MotionEvent.obtain(SystemClock.uptimeMillis(), + SystemClock.uptimeMillis(), MotionEvent.ACTION_MOVE, DEVICE_WIDTH / 2, 650, 0)); + + // Verify the spinner remained invisible. + verify(mSpinnerView, times(0)).setVisibility(anyInt()); + } + private void verifyWindowFlagsSet() { verify(mWindow).addFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL); verify(mWindow).clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbarUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbarUnitTest.java index f4369723..74c07e89 100644 --- a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbarUnitTest.java +++ b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbarUnitTest.java
@@ -5,20 +5,88 @@ package org.chromium.chrome.browser.customtabs.features.toolbar; import static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import android.app.Activity; +import android.os.Handler; +import android.os.Looper; +import android.view.ActionMode; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.TextView; + +import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; +import org.robolectric.Robolectric; import org.robolectric.annotation.Config; +import org.robolectric.annotation.LooperMode; +import org.robolectric.annotation.LooperMode.Mode; +import org.robolectric.shadows.ShadowLooper; +import org.chromium.base.task.TaskTraits; +import org.chromium.base.task.test.ShadowPostTask; +import org.chromium.base.task.test.ShadowPostTask.TestImpl; import org.chromium.base.test.BaseRobolectricTestRunner; +import org.chromium.chrome.R; +import org.chromium.chrome.browser.customtabs.features.toolbar.CustomTabToolbar.CustomTabLocationBar; +import org.chromium.chrome.browser.toolbar.LocationBarModel; +import org.chromium.ui.base.TestActivity; import org.chromium.url.JUnitTestGURLs; /** * Tests AMP url handling in the CustomTab Toolbar. */ @RunWith(BaseRobolectricTestRunner.class) -@Config(manifest = Config.NONE) +@Config(manifest = Config.NONE, shadows = {ShadowLooper.class, ShadowPostTask.class}) +@LooperMode(Mode.PAUSED) public class CustomTabToolbarUnitTest { + @Rule + public MockitoRule mRule = MockitoJUnit.rule(); + + @Mock + LocationBarModel mLocationBarModel; + @Mock + ActionMode.Callback mActionModeCallback; + @Mock + CustomTabToolbarAnimationDelegate mAnimationDelegate; + + private CustomTabLocationBar mLocationBar; + private TextView mTitleBar; + private TextView mUrlBar; + + @Before + public void setup() { + ShadowPostTask.setTestImpl(new TestImpl() { + @Override + public void postDelayedTask(TaskTraits taskTraits, Runnable task, long delay) { + new Handler(Looper.getMainLooper()).postDelayed(task, delay); + } + }); + Mockito.doReturn(R.string.accessibility_security_btn_secure) + .when(mLocationBarModel) + .getSecurityIconContentDescriptionResourceId(); + Mockito.doReturn(R.color.default_icon_color_tint_list) + .when(mLocationBarModel) + .getSecurityIconColorStateList(); + + Activity activity = Robolectric.buildActivity(TestActivity.class).get(); + CustomTabToolbar toolbar = (CustomTabToolbar) LayoutInflater.from(activity).inflate( + R.layout.custom_tabs_toolbar, null, false); + mLocationBar = (CustomTabLocationBar) toolbar.createLocationBar( + mLocationBarModel, mActionModeCallback, () -> null, () -> null); + mUrlBar = toolbar.findViewById(R.id.url_bar); + mTitleBar = toolbar.findViewById(R.id.title_bar); + } + @Test public void testParsesPublisherFromAmp() { assertEquals("www.nyt.com", @@ -31,4 +99,54 @@ CustomTabToolbar.parsePublisherNameFromUrl( JUnitTestGURLs.getGURL(JUnitTestGURLs.EXAMPLE_URL))); } + + @Test + public void testShowBranding_DomainOnly() { + mLocationBar.setAnimDelegateForTesting(mAnimationDelegate); + + mLocationBar.showBranding(); + ShadowLooper.idleMainLooper(); + verify(mLocationBarModel).notifyTitleChanged(); + verify(mAnimationDelegate).prepareTitleAnim(mUrlBar, mTitleBar); + verify(mAnimationDelegate).startTitleAnimation(any()); + assertEquals("URL bar should not be visible during branding.", mUrlBar.getVisibility(), + View.GONE); + + // Run all UI tasks, until the branding is finished. + ShadowLooper.runUiThreadTasksIncludingDelayedTasks(); + verify(mLocationBarModel, times(2)).notifyTitleChanged(); + verify(mLocationBarModel).notifyUrlChanged(); + verify(mLocationBarModel).notifySecurityStateChanged(); + + assertEquals("URL bar is not visible.", mUrlBar.getVisibility(), View.VISIBLE); + } + + @Test + public void testShowBranding_DomainAndTitle() { + mLocationBar.setAnimDelegateForTesting(mAnimationDelegate); + + // Set title before the branding starts, so the state is domain and title. + mLocationBar.setShowTitle(true); + ShadowLooper.idleMainLooper(); + verify(mLocationBarModel).notifyTitleChanged(); + verify(mAnimationDelegate).prepareTitleAnim(mUrlBar, mTitleBar); + // Animation not started since branding is not completed. + verify(mAnimationDelegate, never()).startTitleAnimation(any()); + + mLocationBar.showBranding(); + ShadowLooper.idleMainLooper(); + verify(mLocationBarModel, times(2)).notifyTitleChanged(); + verify(mAnimationDelegate, times(2)).prepareTitleAnim(mUrlBar, mTitleBar); + verify(mAnimationDelegate).startTitleAnimation(any()); + assertEquals("URL bar should not be visible during branding.", mUrlBar.getVisibility(), + View.GONE); + + // Run all UI tasks, until the branding is finished. + ShadowLooper.runUiThreadTasksIncludingDelayedTasks(); + verify(mLocationBarModel, times(3)).notifyTitleChanged(); + verify(mLocationBarModel).notifyUrlChanged(); + verify(mLocationBarModel).notifySecurityStateChanged(); + + assertEquals("URL bar is not visible.", mUrlBar.getVisibility(), View.VISIBLE); + } }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/history_clusters/HistoryClustersCoordinatorTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/history_clusters/HistoryClustersCoordinatorTest.java index bd2746b2..38d7fbb 100644 --- a/chrome/android/junit/src/org/chromium/chrome/browser/history_clusters/HistoryClustersCoordinatorTest.java +++ b/chrome/android/junit/src/org/chromium/chrome/browser/history_clusters/HistoryClustersCoordinatorTest.java
@@ -25,6 +25,7 @@ import androidx.test.core.app.ActivityScenario; import com.google.android.material.tabs.TabLayout; +import com.google.common.collect.ImmutableMap; import org.hamcrest.Matchers; import org.junit.After; @@ -64,6 +65,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.HashSet; +import java.util.LinkedHashMap; import java.util.List; import java.util.concurrent.CopyOnWriteArraySet; @@ -205,7 +207,8 @@ new ArrayList<>(), mGurl2, 123L, new ArrayList<>()); mCluster = new HistoryCluster(Arrays.asList(mVisit1, mVisit2), "\"label\"", "label", Collections.emptyList(), 123L, Arrays.asList("pugs", "terriers")); - mClusterResult = new HistoryClustersResult(Arrays.asList(mCluster), "dogs", false, false); + mClusterResult = new HistoryClustersResult(Arrays.asList(mCluster), + new LinkedHashMap<>(ImmutableMap.of("label", 1)), "dogs", false, false); mActivityScenario = ActivityScenario.launch(ChromeTabbedActivity.class).onActivity(activity -> {
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/history_clusters/HistoryClustersMediatorTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/history_clusters/HistoryClustersMediatorTest.java index ba0ee343..2b971748 100644 --- a/chrome/android/junit/src/org/chromium/chrome/browser/history_clusters/HistoryClustersMediatorTest.java +++ b/chrome/android/junit/src/org/chromium/chrome/browser/history_clusters/HistoryClustersMediatorTest.java
@@ -29,6 +29,8 @@ import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; +import com.google.common.collect.ImmutableMap; + import org.hamcrest.BaseMatcher; import org.hamcrest.Description; import org.hamcrest.Matcher; @@ -71,6 +73,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import java.util.LinkedHashMap; import java.util.List; import java.util.concurrent.TimeUnit; @@ -248,12 +251,17 @@ new ArrayList<>(), 123L, Collections.emptyList()); mCluster3 = new HistoryCluster(Arrays.asList(mVisit4), "\"label3\"", "label3", new ArrayList<>(), 789L, Collections.EMPTY_LIST); - mHistoryClustersResultWithQuery = new HistoryClustersResult( - Arrays.asList(mCluster1, mCluster2), "query", true, false); - mHistoryClustersFollowupResultWithQuery = - new HistoryClustersResult(Arrays.asList(mCluster3), "query", false, true); + mHistoryClustersResultWithQuery = + new HistoryClustersResult(Arrays.asList(mCluster1, mCluster2), + new LinkedHashMap<>(ImmutableMap.of("label", 1)), "query", true, false); + mHistoryClustersFollowupResultWithQuery = new HistoryClustersResult( + Arrays.asList(mCluster3), + new LinkedHashMap<>(ImmutableMap.of("label", 1, "hostname.com", 1, "label3", 1)), + "query", false, true); mHistoryClustersResultEmptyQuery = - new HistoryClustersResult(Arrays.asList(mCluster1, mCluster2), "", false, false); + new HistoryClustersResult(Arrays.asList(mCluster1, mCluster2), + new LinkedHashMap<>(ImmutableMap.of("label", 1, "hostname.com", 1)), "", + false, false); } @Test @@ -318,8 +326,9 @@ ListItem item = mModelList.get(3); PropertyModel model = item.model; - assertTrue(model.getAllSetProperties().containsAll(Arrays.asList( - HistoryClustersItemProperties.CLICK_HANDLER, HistoryClustersItemProperties.TITLE))); + assertTrue(model.getAllSetProperties().containsAll( + Arrays.asList(HistoryClustersItemProperties.CLICK_HANDLER, + HistoryClustersItemProperties.TITLE, HistoryClustersItemProperties.LABEL))); } @Test @@ -496,6 +505,7 @@ doReturn(secondPromise).when(mBridge).loadMoreClusters("query"); doReturn(3).when(mLayoutManager).findLastVisibleItemPosition(); + mMediator.setQueryState(QueryState.forQuery("query", "")); mMediator.startQuery("query"); fulfillPromise(promise, mHistoryClustersResultWithQuery);
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn index b834023..ef07cd4 100644 --- a/chrome/browser/BUILD.gn +++ b/chrome/browser/BUILD.gn
@@ -6687,7 +6687,7 @@ deps += [ "//chrome/common/printing", "//components/printing/browser", - "//components/printing/browser/print_to_pdf", + "//components/printing/browser/headless", "//components/printing/common:mojo_interfaces", "//components/services/print_compositor/public/cpp", "//components/services/print_compositor/public/mojom",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc index e83c29e5..31846374 100644 --- a/chrome/browser/about_flags.cc +++ b/chrome/browser/about_flags.cc
@@ -9001,10 +9001,17 @@ flag_descriptions::kStylusWritingToInputDescription, kOsAndroid, FEATURE_VALUE_TYPE(blink::features::kStylusWritingToInput)}, #endif // BUILDFLAG(IS_ANDROID) - // NOTE: Adding a new flag requires adding a corresponding entry to enum - // "LoginCustomFlags" in tools/metrics/histograms/enums.xml. See "Flag - // Histograms" in tools/metrics/histograms/README.md (run the - // AboutFlagsHistogramTest unit test to verify this process). + +#if BUILDFLAG(IS_CHROMEOS_ASH) + {"enable-media-dynamic-cgroup", flag_descriptions::kMediaDynamicCgroupName, + flag_descriptions::kMediaDynamicCgroupDescription, kOsCrOS, + PLATFORM_FEATURE_NAME_TYPE("CrOSLateBootMediaDynamicCgroup")}, +#endif // BUILDFLAG(IS_CHROMEOS_ASH) + + // NOTE: Adding a new flag requires adding a corresponding entry to enum + // "LoginCustomFlags" in tools/metrics/histograms/enums.xml. See "Flag + // Histograms" in tools/metrics/histograms/README.md (run the + // AboutFlagsHistogramTest unit test to verify this process). }; class FlagsStateSingleton : public flags_ui::FlagsState::Delegate {
diff --git a/chrome/browser/ash/customization/customization_document_browsertest.cc b/chrome/browser/ash/customization/customization_document_browsertest.cc index 2ccac9d..ad9b38f 100644 --- a/chrome/browser/ash/customization/customization_document_browsertest.cc +++ b/chrome/browser/ash/customization/customization_document_browsertest.cc
@@ -240,11 +240,13 @@ << "Test failed for initial_locale='" << GetParam() << "'"; for (size_t i = 0; i < ui_language_list->GetListDeprecated().size(); ++i) { - base::DictionaryValue* language_info = NULL; - ASSERT_TRUE(ui_language_list->GetDictionary(i, &language_info)) + base::Value::Dict* language_info = + ui_language_list->GetList()[i].GetIfDict(); + + ASSERT_TRUE(language_info) << "Test failed for initial_locale='" << GetParam() << "', i=" << i; - const std::string* value = language_info->FindStringKey("value"); + std::string* value = language_info->FindString("value"); ASSERT_TRUE(value) << "Test failed for initial_locale='" << GetParam() << "', i=" << i;
diff --git a/chrome/browser/ash/dbus/org.chromium.ChromeFeaturesService.conf b/chrome/browser/ash/dbus/org.chromium.ChromeFeaturesService.conf index e8a7c17..9cdf90aa 100644 --- a/chrome/browser/ash/dbus/org.chromium.ChromeFeaturesService.conf +++ b/chrome/browser/ash/dbus/org.chromium.ChromeFeaturesService.conf
@@ -38,6 +38,12 @@ send_member="IsFeatureEnabled"/> </policy> + <policy user="resourced"> + <allow send_destination="org.chromium.ChromeFeaturesService" + send_interface="org.chromium.ChromeFeaturesServiceInterface" + send_member="IsFeatureEnabled"/> + </policy> + <!-- limit cras visibility to only IsFeatureEnabled --> <policy user="cras"> <allow send_destination="org.chromium.ChromeFeaturesService"
diff --git a/chrome/browser/ash/login/chrome_restart_request.cc b/chrome/browser/ash/login/chrome_restart_request.cc index c4f9398..bdee04a1 100644 --- a/chrome/browser/ash/login/chrome_restart_request.cc +++ b/chrome/browser/ash/login/chrome_restart_request.cc
@@ -117,6 +117,7 @@ ::switches::kEnableGpuMemoryBufferVideoFrames, ::switches::kEnableGpuRasterization, ::switches::kEnableLogging, + ::switches::kEnableMicrophoneMuteSwitchDeviceSwitch, ::switches::kEnableNativeGpuMemoryBuffers, ::switches::kEnableTouchDragDrop, ::switches::kEnableUnifiedDesktop,
diff --git a/chrome/browser/ash/remote_apps/remote_apps_manager_browsertest.cc b/chrome/browser/ash/remote_apps/remote_apps_manager_browsertest.cc index 73d1716a..ee89303 100644 --- a/chrome/browser/ash/remote_apps/remote_apps_manager_browsertest.cc +++ b/chrome/browser/ash/remote_apps/remote_apps_manager_browsertest.cc
@@ -7,6 +7,7 @@ #include "ash/app_list/app_list_model_provider.h" #include "ash/app_list/model/app_list_item.h" #include "ash/app_list/model/app_list_model.h" +#include "ash/app_list/views/app_list_item_view.h" #include "ash/components/login/auth/public/user_context.h" #include "ash/constants/ash_switches.h" #include "ash/public/cpp/accelerators.h" @@ -56,6 +57,7 @@ #include "content/public/test/browser_test.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" +#include "ui/events/test/event_generator.h" #include "ui/gfx/image/image_skia.h" #include "ui/gfx/image/image_unittest_util.h" @@ -282,15 +284,13 @@ ASSERT_EQ(error, future.Get<1>()); } - void ShowLauncherAppsGrid() { + void ShowLauncherAppsGrid(bool wait_for_opening_animation) { AppListClientImpl* client = AppListClientImpl::GetInstance(); EXPECT_FALSE(client->GetAppListWindow()); ash::AcceleratorController::Get()->PerformActionIfEnabled( ash::TOGGLE_APP_LIST_FULLSCREEN, {}); - if (ash::features::IsProductivityLauncherEnabled()) { - ash::AppListTestApi().WaitForBubbleWindow( - /*wait_for_opening_animation=*/false); - } + if (ash::features::IsProductivityLauncherEnabled()) + ash::AppListTestApi().WaitForBubbleWindow(wait_for_opening_animation); EXPECT_TRUE(client->GetAppListWindow()); } @@ -303,7 +303,7 @@ IN_PROC_BROWSER_TEST_F(RemoteAppsManagerBrowsertest, AddApp) { // Show launcher UI so that app icons are loaded. - ShowLauncherAppsGrid(); + ShowLauncherAppsGrid(/*wait_for_opening_animation=*/false); std::string name = "name"; GURL icon_url("icon_url"); @@ -333,7 +333,7 @@ // default placeholder icon. IN_PROC_BROWSER_TEST_F(RemoteAppsManagerBrowsertest, AddAppPlaceholderIcon) { // Show launcher UI so that app icons are loaded. - ShowLauncherAppsGrid(); + ShowLauncherAppsGrid(/*wait_for_opening_animation=*/true); const std::string name = "name"; @@ -356,7 +356,24 @@ future.GetCallback()); // App's icon is placeholder. - CheckIconsEqual(future.Get()->uncompressed, item->GetDefaultIcon()); + // TODO(https://crbug.com/1345682): add a pixel diff test for this scenario. + ash::AppListTestApi app_list_test_api; + CheckIconsEqual( + future.Get()->uncompressed, + app_list_test_api.GetTopLevelItemViewFromId(kId1)->icon_image_for_test()); + + // App list color sorting should still work for placeholder icons. + ui::test::EventGenerator event_generator(ash::Shell::GetPrimaryRootWindow()); + ash::AppListTestApi::ReorderAnimationEndState actual_state; + + app_list_test_api.ReorderByMouseClickAtToplevelAppsGridMenu( + ash::AppListSortOrder::kColor, + ash::AppListTestApi::MenuType::kAppListNonFolderItemMenu, + &event_generator, + /*target_state=*/ + ash::AppListTestApi::ReorderAnimationEndState::kCompleted, &actual_state); + EXPECT_EQ(ash::AppListTestApi::ReorderAnimationEndState::kCompleted, + actual_state); } IN_PROC_BROWSER_TEST_F(RemoteAppsManagerBrowsertest, AddAppError) {
diff --git a/chrome/browser/browser_features.cc b/chrome/browser/browser_features.cc index cbd5b12..95b5e4c 100644 --- a/chrome/browser/browser_features.cc +++ b/chrome/browser/browser_features.cc
@@ -155,8 +155,13 @@ // Controls whether the static key pinning list can be updated via component // updater. +#if BUILDFLAG(IS_ANDROID) const base::Feature kKeyPinningComponentUpdater{ "KeyPinningComponentUpdater", base::FEATURE_DISABLED_BY_DEFAULT}; +#else +const base::Feature kKeyPinningComponentUpdater{ + "KeyPinningComponentUpdater", base::FEATURE_ENABLED_BY_DEFAULT}; +#endif // When this feature is enabled, the network service will restart unsandboxed if // a previous attempt to launch it sandboxed failed.
diff --git a/chrome/browser/chrome_content_browser_client_receiver_bindings.cc b/chrome/browser/chrome_content_browser_client_receiver_bindings.cc index acfd081..c32733d 100644 --- a/chrome/browser/chrome_content_browser_client_receiver_bindings.cc +++ b/chrome/browser/chrome_content_browser_client_receiver_bindings.cc
@@ -112,7 +112,7 @@ #if BUILDFLAG(ENABLE_PRINTING) #include "chrome/browser/printing/print_view_manager_basic.h" -#include "components/printing/browser/print_to_pdf/pdf_print_manager.h" +#include "components/printing/browser/headless/headless_print_manager.h" #if BUILDFLAG(ENABLE_PRINT_PREVIEW) #include "chrome/browser/printing/print_view_manager.h" #endif // BUILDFLAG(ENABLE_PRINT_PREVIEW) @@ -581,7 +581,7 @@ mojo::PendingAssociatedReceiver<printing::mojom::PrintManagerHost> receiver) { if (headless::IsChromeNativeHeadless()) { - print_to_pdf::PdfPrintManager::BindPrintManagerHost( + headless::HeadlessPrintManager::BindPrintManagerHost( std::move(receiver), render_frame_host); } else { #if BUILDFLAG(ENABLE_PRINT_PREVIEW)
diff --git a/chrome/browser/devtools/BUILD.gn b/chrome/browser/devtools/BUILD.gn index 821d669..ec5a2e2 100644 --- a/chrome/browser/devtools/BUILD.gn +++ b/chrome/browser/devtools/BUILD.gn
@@ -267,7 +267,10 @@ ] } if (enable_basic_printing) { - deps += [ "//components/printing/browser/print_to_pdf" ] + deps += [ + "//components/printing/browser/headless", + "//components/printing/browser/print_to_pdf", + ] } sources += rebase_path(_protocol_generated, ".", target_gen_dir) }
diff --git a/chrome/browser/devtools/protocol/page_handler.cc b/chrome/browser/devtools/protocol/page_handler.cc index a286ee1..52c5f62 100644 --- a/chrome/browser/devtools/protocol/page_handler.cc +++ b/chrome/browser/devtools/protocol/page_handler.cc
@@ -7,7 +7,6 @@ #include "base/strings/utf_string_conversions.h" #include "chrome/browser/web_applications/web_app_helpers.h" #include "components/payments/content/payment_request_web_contents_manager.h" -#include "components/printing/browser/print_to_pdf/pdf_print_result.h" #include "components/subresource_filter/content/browser/devtools_interaction_tracker.h" #include "third_party/blink/public/common/features.h" #include "third_party/blink/public/common/manifest/manifest_util.h" @@ -220,8 +219,8 @@ transfer_mode.fromMaybe("") == protocol::Page::PrintToPDF::TransferModeEnum::ReturnAsStream; - if (auto* print_manager = - print_to_pdf::PdfPrintManager::FromWebContents(web_contents_.get())) { + if (auto* print_manager = headless::HeadlessPrintManager::FromWebContents( + web_contents_.get())) { print_manager->PrintToPdf( web_contents_->GetPrimaryMainFrame(), page_ranges.fromMaybe(""), std::move(absl::get<printing::mojom::PrintPagesParamsPtr>(
diff --git a/chrome/browser/devtools/protocol/page_handler.h b/chrome/browser/devtools/protocol/page_handler.h index 4d7f1d90..6b725f3 100644 --- a/chrome/browser/devtools/protocol/page_handler.h +++ b/chrome/browser/devtools/protocol/page_handler.h
@@ -16,7 +16,8 @@ #include "third_party/blink/public/common/manifest/manifest.h" #if BUILDFLAG(ENABLE_PRINTING) -#include "components/printing/browser/print_to_pdf/pdf_print_manager.h" +#include "components/printing/browser/headless/headless_print_manager.h" +#include "components/printing/browser/print_to_pdf/pdf_print_result.h" #endif // BUILDFLAG(ENABLE_PRINTING) namespace content {
diff --git a/chrome/browser/enterprise/reporting/browser_report_generator_unittest.cc b/chrome/browser/enterprise/reporting/browser_report_generator_unittest.cc index ca91326..884be35 100644 --- a/chrome/browser/enterprise/reporting/browser_report_generator_unittest.cc +++ b/chrome/browser/enterprise/reporting/browser_report_generator_unittest.cc
@@ -26,6 +26,7 @@ #include "content/public/common/webplugininfo.h" #include "content/public/test/browser_task_environment.h" #include "device_management_backend.pb.h" +#include "ppapi/buildflags/buildflags.h" #include "testing/gtest/include/gtest/gtest.h" #if !BUILDFLAG(IS_ANDROID)
diff --git a/chrome/browser/enterprise/reporting/report_generator_unittest.cc b/chrome/browser/enterprise/reporting/report_generator_unittest.cc index c79e19e..18dc544 100644 --- a/chrome/browser/enterprise/reporting/report_generator_unittest.cc +++ b/chrome/browser/enterprise/reporting/report_generator_unittest.cc
@@ -25,6 +25,7 @@ #include "components/policy/core/common/cloud/cloud_policy_util.h" #include "content/public/common/webplugininfo.h" #include "content/public/test/browser_task_environment.h" +#include "ppapi/buildflags/buildflags.h" #include "testing/gtest/include/gtest/gtest.h" #if BUILDFLAG(IS_CHROMEOS_ASH)
diff --git a/chrome/browser/enterprise/reporting/report_request_queue_generator_unittest.cc b/chrome/browser/enterprise/reporting/report_request_queue_generator_unittest.cc index 6f03134..58cebbc 100644 --- a/chrome/browser/enterprise/reporting/report_request_queue_generator_unittest.cc +++ b/chrome/browser/enterprise/reporting/report_request_queue_generator_unittest.cc
@@ -24,6 +24,7 @@ #include "components/policy/core/common/policy_map.h" #include "components/sync_preferences/pref_service_syncable.h" #include "content/public/test/browser_task_environment.h" +#include "ppapi/buildflags/buildflags.h" #include "testing/gtest/include/gtest/gtest.h" #if BUILDFLAG(IS_ANDROID)
diff --git a/chrome/browser/extensions/extension_special_storage_policy.cc b/chrome/browser/extensions/extension_special_storage_policy.cc index fde411df..dbd10c1 100644 --- a/chrome/browser/extensions/extension_special_storage_policy.cc +++ b/chrome/browser/extensions/extension_special_storage_policy.cc
@@ -154,11 +154,9 @@ // Fetch the list of cookies related content_settings and bind it // to CookieSettings::ShouldDeleteCookieOnExit to avoid fetching it on // every call. - ContentSettingsForOneType entries; - cookie_settings_->GetCookieSettings(&entries); return base::BindRepeating( &content_settings::CookieSettings::ShouldDeleteCookieOnExit, - cookie_settings_, std::move(entries)); + cookie_settings_, cookie_settings_->GetCookieSettings()); } bool ExtensionSpecialStoragePolicy::HasSessionOnlyOrigins() { @@ -167,10 +165,9 @@ if (cookie_settings_->GetDefaultCookieSetting(NULL) == CONTENT_SETTING_SESSION_ONLY) return true; - ContentSettingsForOneType entries; - cookie_settings_->GetCookieSettings(&entries); - for (size_t i = 0; i < entries.size(); ++i) { - if (entries[i].GetContentSetting() == CONTENT_SETTING_SESSION_ONLY) + for (const ContentSettingPatternSource& entry : + cookie_settings_->GetCookieSettings()) { + if (entry.GetContentSetting() == CONTENT_SETTING_SESSION_ONLY) return true; } return false;
diff --git a/chrome/browser/feed/android/BUILD.gn b/chrome/browser/feed/android/BUILD.gn index f53234f5..83cef0b 100644 --- a/chrome/browser/feed/android/BUILD.gn +++ b/chrome/browser/feed/android/BUILD.gn
@@ -61,6 +61,7 @@ "java/src/org/chromium/chrome/browser/feed/hooks/FeedHooks.java", "java/src/org/chromium/chrome/browser/feed/hooks/FeedHooksImpl.java", "java/src/org/chromium/chrome/browser/feed/sections/OnSectionHeaderSelectedListener.java", + "java/src/org/chromium/chrome/browser/feed/sections/SectionHeaderBadgeDrawable.java", "java/src/org/chromium/chrome/browser/feed/sections/SectionHeaderListProperties.java", "java/src/org/chromium/chrome/browser/feed/sections/SectionHeaderProperties.java", "java/src/org/chromium/chrome/browser/feed/sections/SectionHeaderView.java", @@ -203,7 +204,6 @@ "java/res/values/dimens.xml", "java/res/values/styles.xml", "java/res/xml/feed_autoplay_preferences.xml", - "java/res/xml/tab_layout_badge.xml", ] deps = [ "//chrome/browser/ui/android/strings:ui_strings_grd",
diff --git a/chrome/browser/feed/android/java/res/values/dimens.xml b/chrome/browser/feed/android/java/res/values/dimens.xml index 5fe6acb0..77b78f76 100644 --- a/chrome/browser/feed/android/java/res/values/dimens.xml +++ b/chrome/browser/feed/android/java/res/values/dimens.xml
@@ -52,6 +52,11 @@ <dimen name="feed_header_tab_extra_margin_right">7dp</dimen> <dimen name="feed_header_background_corner_radius">60dp</dimen> + <!-- Feed unread dot dimens --> + <dimen name="feed_badge_radius">2.5dp</dimen> + <dimen name="feed_badge_hoffset">-1dp</dimen> + <dimen name="feed_badge_voffset">2dp</dimen> + <!-- Dynamic color dimensions. --> <dimen name="feed_header_section_tab_bg_elevation_enabled">@dimen/default_elevation_2</dimen> <dimen name="feed_header_tab_selected_bg_elevation">@dimen/default_elevation_0</dimen>
diff --git a/chrome/browser/feed/android/java/res/xml/tab_layout_badge.xml b/chrome/browser/feed/android/java/res/xml/tab_layout_badge.xml deleted file mode 100644 index d4c8bb5..0000000 --- a/chrome/browser/feed/android/java/res/xml/tab_layout_badge.xml +++ /dev/null
@@ -1,11 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright 2021 The Chromium Authors. All rights reserved. - Use of this source code is governed by a BSD-style license that can be - found in the LICENSE file. --> - -<badge - xmlns:app="http://schemas.android.com/apk/res-auto" - app:backgroundColor="@macro/default_text_color_accent1" - app:badgeRadius="2.5dp" - app:horizontalOffset="-5dp" - app:verticalOffset="2dp"/>
diff --git a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/sections/SectionHeaderBadgeDrawable.java b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/sections/SectionHeaderBadgeDrawable.java new file mode 100644 index 0000000..2e8f64ee --- /dev/null +++ b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/sections/SectionHeaderBadgeDrawable.java
@@ -0,0 +1,130 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chrome.browser.feed.sections; + +import android.content.Context; +import android.content.res.ColorStateList; +import android.graphics.Canvas; +import android.graphics.ColorFilter; +import android.graphics.Paint; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; +import android.view.View; + +import androidx.annotation.Nullable; +import androidx.appcompat.widget.AppCompatTextView; + +import com.google.android.material.shape.MaterialShapeDrawable; + +import org.chromium.chrome.browser.feed.R; +import org.chromium.components.browser_ui.styles.SemanticColorUtils; + +/** + * Drawable representing the unread dot. + * + * Allows for setting of text and animating the width when text changes. + */ +public class SectionHeaderBadgeDrawable extends Drawable { + private Paint mPaint; + // Only used for calculating text width in dps and to easily apply text appearances. + private AppCompatTextView mTextView; + private MaterialShapeDrawable mShapeDrawable; + private String mText; + private Context mContext; + + public SectionHeaderBadgeDrawable(Context context) { + mContext = context; + + // This textview is only used to easily set the text parameters and calculate textual width. + mTextView = new AppCompatTextView(context); + mTextView.setTextAppearance(context, R.style.TextAppearance_Material3_LabelSmall); + mPaint = mTextView.getPaint(); + mPaint.setTextAlign(Paint.Align.CENTER); + + mShapeDrawable = new MaterialShapeDrawable(); + mShapeDrawable.setCornerSize( + mContext.getResources().getDimensionPixelSize(R.dimen.feed_badge_radius)); + mShapeDrawable.setFillColor( + ColorStateList.valueOf(SemanticColorUtils.getDefaultTextColorAccent1(context))); + mText = ""; + } + + public void setText(String text) { + mText = text; + mTextView.setText(text); + } + + @Override + public void draw(Canvas canvas) { + Rect bounds = getBounds(); + + if (bounds.isEmpty() || getAlpha() == 0 || !isVisible()) { + return; + } + + mShapeDrawable.draw(canvas); + + // Draws the full text with the top left corner at (centerX, centerY-(halfheight of text)). + // We define halfheight as the average of ascent and descent to ensure the text does not + // appear lopsided even if the font changes. + canvas.drawText(mText, 0, mText.length(), bounds.centerX(), + bounds.centerY() - ((mPaint.descent() + mPaint.ascent()) / 2), mPaint); + } + + @Override + public void setAlpha(int alpha) { + mPaint.setAlpha(alpha); + invalidateSelf(); + } + + @Override + public void setColorFilter(@Nullable ColorFilter colorFilter) { + mPaint.setColorFilter(colorFilter); + invalidateSelf(); + } + + /** + * Called when we are attaching the drawable to a new overlay. + * + * @param anchorBounds the bounding box for the overlay. + */ + @Override + public void setBounds(Rect anchorBounds) { + // CenterY is the top of the bounding box + a custom vertical offset. + int centerY = anchorBounds.top + + mContext.getResources().getDimensionPixelSize(R.dimen.feed_badge_voffset); + int halfHeight = mContext.getResources().getDimensionPixelSize(R.dimen.feed_badge_radius); + // HalfWidth is the radius if no text, or the text width/2. + int halfWidth = Math.max(halfHeight, mTextView.getMeasuredWidth() / 2); + // CenterX is the right side of the bounding box + radius + offset. + int centerX = anchorBounds.right + halfWidth + - mContext.getResources().getDimensionPixelSize(R.dimen.feed_badge_hoffset); + // The new bounds for the dot + any text to be rendered therein. + Rect newBounds = new Rect(centerX - halfWidth, centerY - halfHeight, centerX + halfWidth, + centerY + halfHeight); + // We don't set bounding box for the textview because + // one does not set bounds for views, and it's not part of the drawing. + mShapeDrawable.setBounds(newBounds); + super.setBounds(newBounds); + } + + @Override + public int getOpacity() { + return mPaint.getAlpha(); + } + + public void attach(View anchor) { + Rect badgeBounds = new Rect(); + anchor.getDrawingRect(badgeBounds); + setBounds(badgeBounds); + anchor.getOverlay().add(this); + invalidateSelf(); + } + + public void detach(View anchor) { + anchor.getOverlay().remove(this); + invalidateSelf(); + } +}
diff --git a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/sections/SectionHeaderView.java b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/sections/SectionHeaderView.java index 7340984f..de5af1c 100644 --- a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/sections/SectionHeaderView.java +++ b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/sections/SectionHeaderView.java
@@ -21,8 +21,6 @@ import androidx.core.content.res.ResourcesCompat; import androidx.core.widget.ImageViewCompat; -import com.google.android.material.badge.BadgeDrawable; -import com.google.android.material.badge.BadgeUtils; import com.google.android.material.tabs.TabLayout; import org.chromium.chrome.browser.feed.FeedUma; @@ -82,21 +80,17 @@ private class UnreadIndicator implements ViewTreeObserver.OnGlobalLayoutListener { private View mAnchor; - private BadgeDrawable mBadge; + private SectionHeaderBadgeDrawable mNewBadge; UnreadIndicator(View anchor) { mAnchor = anchor; - mBadge = BadgeDrawable.createFromResource( - SectionHeaderView.this.getContext(), R.xml.tab_layout_badge); - - mBadge.setVisible(true); + mNewBadge = new SectionHeaderBadgeDrawable(SectionHeaderView.this.getContext()); mAnchor.getViewTreeObserver().addOnGlobalLayoutListener(this); } void destroy() { mAnchor.getViewTreeObserver().removeOnGlobalLayoutListener(this); - mBadge.setVisible(false); - BadgeUtils.detachBadgeDrawable(mBadge, mAnchor); + mNewBadge.detach(mAnchor); } @Override @@ -104,7 +98,7 @@ // attachBadgeDrawable() will crash if mAnchor has no parent. This can happen after the // views are detached from the window. if (mAnchor.getParent() != null) { - BadgeUtils.attachBadgeDrawable(mBadge, mAnchor); + mNewBadge.attach(mAnchor); } } }
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json index 9b7a3c8d..021f101e 100644 --- a/chrome/browser/flag-metadata.json +++ b/chrome/browser/flag-metadata.json
@@ -2504,6 +2504,11 @@ "expiry_milestone": 92 }, { + "name": "enable-media-dynamic-cgroup", + "owners": [ "erin.park@intel.com", "youssefesmat" ], + "expiry_milestone": 120 + }, + { "name": "enable-media-foundation-clear", "owners": [ "wicarr@microsoft.com"
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc index 82d9c61..3dfb2b1b 100644 --- a/chrome/browser/flag_descriptions.cc +++ b/chrome/browser/flag_descriptions.cc
@@ -5599,6 +5599,11 @@ const char kSchedulerConfigurationPerformance[] = "Enables Hyper-Threading on relevant CPUs."; +const char kMediaDynamicCgroupName[] = "Media Dynamic Cgroup"; +const char kMediaDynamicCgroupDescription[] = + "Dynamic Cgroup allows tasks from media workload to be consolidated on " + "limited cpuset"; + const char kShowBluetoothDebugLogToggleName[] = "Show Bluetooth debug log toggle"; const char kShowBluetoothDebugLogToggleDescription[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h index fed7a9a..43a3ab34 100644 --- a/chrome/browser/flag_descriptions.h +++ b/chrome/browser/flag_descriptions.h
@@ -3208,6 +3208,9 @@ extern const char kSchedulerConfigurationConservative[]; extern const char kSchedulerConfigurationPerformance[]; +extern const char kMediaDynamicCgroupName[]; +extern const char kMediaDynamicCgroupDescription[]; + extern const char kShowBluetoothDebugLogToggleName[]; extern const char kShowBluetoothDebugLogToggleDescription[];
diff --git a/chrome/browser/history_clusters/history_clusters_bridge.cc b/chrome/browser/history_clusters/history_clusters_bridge.cc index 13bd5c3f..19ae258 100644 --- a/chrome/browser/history_clusters/history_clusters_bridge.cc +++ b/chrome/browser/history_clusters/history_clusters_bridge.cc
@@ -177,11 +177,23 @@ } ScopedJavaLocalRef<jclass> cluster_type = base::android::GetClass( env, "org/chromium/chrome/browser/history_clusters/HistoryCluster"); + std::vector<std::u16string> unique_raw_labels; + std::vector<int> label_counts; + if (query_clusters_state_->query().empty()) { + for (const auto& label_entry : + query_clusters_state_->raw_label_counts_so_far()) { + unique_raw_labels.push_back(label_entry.first); + label_counts.push_back(label_entry.second); + } + } + const ScopedJavaLocalRef<jobject>& j_result = Java_HistoryClustersBridge_buildClusterResult( env, base::android::ToTypedJavaArrayOfObjects(env, j_clusters, cluster_type), + base::android::ToJavaArrayOfStrings(env, unique_raw_labels), + base::android::ToJavaIntArray(env, label_counts), base::android::ConvertUTF8ToJavaString(env, query), can_load_more, is_continuation); base::android::RunObjectCallbackAndroid(j_callback, j_result);
diff --git a/chrome/browser/history_clusters/java/src/org/chromium/chrome/browser/history_clusters/HistoryClustersBridge.java b/chrome/browser/history_clusters/java/src/org/chromium/chrome/browser/history_clusters/HistoryClustersBridge.java index 6f84036e..3e07fb6 100644 --- a/chrome/browser/history_clusters/java/src/org/chromium/chrome/browser/history_clusters/HistoryClustersBridge.java +++ b/chrome/browser/history_clusters/java/src/org/chromium/chrome/browser/history_clusters/HistoryClustersBridge.java
@@ -17,6 +17,7 @@ import java.util.ArrayList; import java.util.Arrays; +import java.util.LinkedHashMap; import java.util.List; @JNINamespace("history_clusters") @@ -69,10 +70,18 @@ } @CalledByNative - static HistoryClustersResult buildClusterResult( - HistoryCluster[] clusters, String query, boolean canLoadMore, boolean isContinuation) { + static HistoryClustersResult buildClusterResult(HistoryCluster[] clusters, + String[] uniqueRawLabels, int[] labelCounts, String query, boolean canLoadMore, + boolean isContinuation) { + assert uniqueRawLabels.length == labelCounts.length; + LinkedHashMap<String, Integer> labelCountsMap = new LinkedHashMap<>(); + for (int i = 0; i < uniqueRawLabels.length; i++) { + labelCountsMap.put(uniqueRawLabels[i], labelCounts[i]); + } + List<HistoryCluster> clustersList = Arrays.asList(clusters); - return new HistoryClustersResult(clustersList, query, canLoadMore, isContinuation); + return new HistoryClustersResult( + clustersList, labelCountsMap, query, canLoadMore, isContinuation); } @CalledByNative
diff --git a/chrome/browser/history_clusters/java/src/org/chromium/chrome/browser/history_clusters/HistoryClustersMediator.java b/chrome/browser/history_clusters/java/src/org/chromium/chrome/browser/history_clusters/HistoryClustersMediator.java index bb13fbf..e3a202f0 100644 --- a/chrome/browser/history_clusters/java/src/org/chromium/chrome/browser/history_clusters/HistoryClustersMediator.java +++ b/chrome/browser/history_clusters/java/src/org/chromium/chrome/browser/history_clusters/HistoryClustersMediator.java
@@ -47,7 +47,9 @@ import java.util.ArrayList; import java.util.Arrays; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; import java.util.concurrent.TimeUnit; class HistoryClustersMediator extends RecyclerView.OnScrollListener implements SearchDelegate { @@ -79,6 +81,7 @@ private ListItem mClearBrowsingDataItem; private QueryState mQueryState = QueryState.forQueryless(); private final HistoryClustersMetricsLogger mMetricsLogger; + private Map<String, PropertyModel> mLabelToModelMap = new LinkedHashMap<>(); /** * Create a new HistoryClustersMediator. @@ -134,7 +137,7 @@ // SearchDelegate implementation. @Override public void onSearchTextChanged(String query) { - mModelList.clear(); + resetModel(); startQuery(query); } @@ -166,7 +169,7 @@ mQueryState = queryState; mToolbarModel.set(HistoryClustersToolbarProperties.QUERY_STATE, queryState); if (!queryState.isSearching()) { - mModelList.clear(); + resetModel(); startQuery(mQueryState.getQuery()); } } @@ -267,7 +270,7 @@ } mDelegate.removeMarkedItems(); - mModelList.clear(); + resetModel(); startQuery(mQueryState.getQuery()); } @@ -282,6 +285,39 @@ boolean isQueryLess = !mQueryState.isSearching(); if (isQueryLess) { ensureHeaders(); + for (Map.Entry<String, Integer> entry : result.getLabelCounts().entrySet()) { + // Check if label exists in the model already + // If not, create a new entry + String rawLabel = entry.getKey(); + PropertyModel existingModel = mLabelToModelMap.get(rawLabel); + if (existingModel == null) { + existingModel = new PropertyModel(HistoryClustersItemProperties.ALL_KEYS); + mLabelToModelMap.put(rawLabel, existingModel); + Drawable journeysDrawable = + AppCompatResources.getDrawable(mContext, R.drawable.ic_journeys); + existingModel.set( + HistoryClustersItemProperties.ICON_DRAWABLE, journeysDrawable); + existingModel.set(HistoryClustersItemProperties.DIVIDER_VISIBLE, true); + existingModel.set(HistoryClustersItemProperties.TITLE, + getQuotedLabelFromRawLabel(rawLabel, result.getClusters())); + ListItem clusterItem = new ListItem(ItemType.CLUSTER, existingModel); + mModelList.add(clusterItem); + existingModel.set(HistoryClustersItemProperties.CLICK_HANDLER, + (v) + -> setQueryState(QueryState.forQuery( + rawLabel, mDelegate.getSearchEmptyString()))); + existingModel.set(HistoryClustersItemProperties.END_BUTTON_DRAWABLE, null); + } + existingModel.set(HistoryClustersItemProperties.LABEL, + mResources.getQuantityString(R.plurals.history_clusters_n_matches, + entry.getValue(), entry.getValue())); + } + + if (result.canLoadMore() && !result.isContinuation()) { + continueQuery(""); + } + + return; } for (HistoryCluster cluster : result.getClusters()) { @@ -294,15 +330,6 @@ clusterModel.set(HistoryClustersItemProperties.DIVIDER_VISIBLE, isQueryLess); ListItem clusterItem = new ListItem(ItemType.CLUSTER, clusterModel); mModelList.add(clusterItem); - if (isQueryLess) { - clusterModel.set(HistoryClustersItemProperties.CLICK_HANDLER, - (v) - -> setQueryState(QueryState.forQuery( - cluster.getRawLabel(), mDelegate.getSearchEmptyString()))); - clusterModel.set(HistoryClustersItemProperties.END_BUTTON_DRAWABLE, null); - clusterModel.set(HistoryClustersItemProperties.LABEL, null); - continue; - } List<ListItem> visitsAndRelatedSearches = new ArrayList<>(cluster.getVisits().size() + 1); @@ -365,6 +392,22 @@ } } + private void resetModel() { + mModelList.clear(); + mLabelToModelMap.clear(); + } + + private String getQuotedLabelFromRawLabel(String rawLabel, List<HistoryCluster> clusters) { + for (HistoryCluster cluster : clusters) { + if (cluster.getRawLabel().equals(rawLabel)) { + return cluster.getLabel(); + } + } + + // This shouldn't happen, but the unquoted label is a graceful fallback in case it does. + return rawLabel; + } + private void ensureHeaders() { if (mQueryState.isSearching()) { return;
diff --git a/chrome/browser/history_clusters/java/src/org/chromium/chrome/browser/history_clusters/HistoryClustersResult.java b/chrome/browser/history_clusters/java/src/org/chromium/chrome/browser/history_clusters/HistoryClustersResult.java index 06daa27..4a76c13 100644 --- a/chrome/browser/history_clusters/java/src/org/chromium/chrome/browser/history_clusters/HistoryClustersResult.java +++ b/chrome/browser/history_clusters/java/src/org/chromium/chrome/browser/history_clusters/HistoryClustersResult.java
@@ -7,7 +7,9 @@ import androidx.annotation.VisibleForTesting; import java.util.Collections; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE) /** @@ -16,18 +18,25 @@ */ public class HistoryClustersResult { private final List<HistoryCluster> mClusters; + private final LinkedHashMap<String, Integer> mLabelCounts; private final String mQuery; private final boolean mCanLoadMore; private final boolean mIsContinuation; /** Create a new result with no clusters and an empty query. */ public static HistoryClustersResult emptyResult() { - return new HistoryClustersResult(Collections.EMPTY_LIST, "", false, false); + return new HistoryClustersResult( + Collections.EMPTY_LIST, new LinkedHashMap<>(), "", false, false); } - HistoryClustersResult(List<HistoryCluster> clusters, String query, boolean canLoadMore, - boolean isContinuation) { + /** + * Constructs a new HistoryClustersResult. {@code labelCounts} must be a LinkedHashMap so that + * order is stable and preserved. + */ + HistoryClustersResult(List<HistoryCluster> clusters, LinkedHashMap<String, Integer> labelCounts, + String query, boolean canLoadMore, boolean isContinuation) { mClusters = clusters; + mLabelCounts = labelCounts; mQuery = query; mCanLoadMore = canLoadMore; mIsContinuation = isContinuation; @@ -44,4 +53,12 @@ public boolean canLoadMore() { return mCanLoadMore; } + + public Map<String, Integer> getLabelCounts() { + return mLabelCounts; + } + + public boolean isContinuation() { + return mIsContinuation; + } }
diff --git a/chrome/browser/infobars/infobars_browsertest.cc b/chrome/browser/infobars/infobars_browsertest.cc index 00649b9..e0de115 100644 --- a/chrome/browser/infobars/infobars_browsertest.cc +++ b/chrome/browser/infobars/infobars_browsertest.cc
@@ -50,6 +50,7 @@ #include "extensions/browser/sandboxed_unpacker.h" #include "extensions/browser/test_extension_registry_observer.h" #include "net/test/embedded_test_server/embedded_test_server.h" +#include "ppapi/buildflags/buildflags.h" #include "sandbox/policy/switches.h" #include "third_party/abseil-cpp/absl/types/optional.h" #include "ui/base/l10n/l10n_util.h"
diff --git a/chrome/browser/metrics/power/power_metrics_reporter.cc b/chrome/browser/metrics/power/power_metrics_reporter.cc index 16e4352..4b461cb 100644 --- a/chrome/browser/metrics/power/power_metrics_reporter.cc +++ b/chrome/browser/metrics/power/power_metrics_reporter.cc
@@ -262,10 +262,19 @@ auto long_interval_data = long_usage_scenario_data_store_->ResetIntervalData(); - // Report histograms. - auto long_interval_suffixes = GetLongIntervalSuffixes(long_interval_data); + // Get scenario data. + const auto long_interval_scenario_params = + GetLongIntervalScenario(long_interval_data); + // Histograms are recorded without suffix and with a scenario-specific + // suffix. + const std::vector<const char*> long_interval_suffixes{ + "", long_interval_scenario_params.histogram_suffix}; + + // Report process metrics histograms. ReportAggregatedProcessMetricsHistograms(aggregated_process_metrics, long_interval_suffixes); + base::UmaHistogramEnumeration("PerformanceMonitor.UsageScenario.LongInterval", + long_interval_scenario_params.scenario); #if HAS_BATTERY_LEVEL_PROVIDER_IMPL() // Report UKMs. @@ -309,6 +318,10 @@ const ScenarioParams short_interval_scenario_params = GetShortIntervalScenarioParams(short_interval_data, long_interval_data); + base::UmaHistogramEnumeration( + "PerformanceMonitor.UsageScenario.ShortInterval", + short_interval_scenario_params.scenario); + ReportShortIntervalHistograms( short_interval_scenario_params.histogram_suffix, short_interval_resource_usage_rate.value());
diff --git a/chrome/browser/metrics/power/usage_scenario.cc b/chrome/browser/metrics/power/usage_scenario.cc index d4eb2b3..a80602f 100644 --- a/chrome/browser/metrics/power/usage_scenario.cc +++ b/chrome/browser/metrics/power/usage_scenario.cc
@@ -10,87 +10,100 @@ // Canary 7 day aggregation ending on March 15th 2022 from "PerformanceMonitor // .ResourceCoalition.CPUTime2_10sec.*" const ScenarioParams kVideoCaptureParams = { + .scenario = Scenario::kVideoCapture, .histogram_suffix = ".VideoCapture", .short_interval_cpu_threshold = 1.8949, .trace_event_title = "High CPU - Video Capture", }; const ScenarioParams kFullscreenVideoParams = { + .scenario = Scenario::kFullscreenVideo, .histogram_suffix = ".FullscreenVideo", .short_interval_cpu_threshold = 1.4513, .trace_event_title = "High CPU - Fullscreen Video", }; const ScenarioParams kEmbeddedVideoNoNavigationParams = { + .scenario = Scenario::kEmbeddedVideoNoNavigation, .histogram_suffix = ".EmbeddedVideo_NoNavigation", .short_interval_cpu_threshold = 1.5436, .trace_event_title = "High CPU - Embedded Video No Navigation", }; const ScenarioParams kEmbeddedVideoWithNavigationParams = { + .scenario = Scenario::kEmbeddedVideoWithNavigation, .histogram_suffix = ".EmbeddedVideo_WithNavigation", .short_interval_cpu_threshold = 1.9999, .trace_event_title = "High CPU - Embedded Video With Navigation", }; const ScenarioParams kAudioParams = { + .scenario = Scenario::kAudio, .histogram_suffix = ".Audio", .short_interval_cpu_threshold = 1.5110, .trace_event_title = "High CPU - Audio", }; const ScenarioParams kNavigationParams = { + .scenario = Scenario::kNavigation, .histogram_suffix = ".Navigation", .short_interval_cpu_threshold = 1.9999, .trace_event_title = "High CPU - Navigation", }; const ScenarioParams kInteractionParams = { + .scenario = Scenario::kInteraction, .histogram_suffix = ".Interaction", .short_interval_cpu_threshold = 1.2221, .trace_event_title = "High CPU - Interaction", }; const ScenarioParams kPassiveParams = { + .scenario = Scenario::kPassive, .histogram_suffix = ".Passive", .short_interval_cpu_threshold = 0.4736, .trace_event_title = "High CPU - Passive", }; -#if BUILDFLAG(IS_MAC) const ScenarioParams kAllTabsHiddenNoVideoCaptureOrAudioParams = { + .scenario = Scenario::kAllTabsHiddenNoVideoCaptureOrAudio, .histogram_suffix = ".AllTabsHidden_NoVideoCaptureOrAudio", .short_interval_cpu_threshold = 0.2095, .trace_event_title = "High CPU - All Tabs Hidden, No Video Capture or Audio", }; -const ScenarioParams kAllTabsHiddenNoVideoCaptureOrAudioRecentParams = { - .histogram_suffix = ".AllTabsHidden_NoVideoCaptureOrAudio_Recent", - .short_interval_cpu_threshold = 0.3302, - .trace_event_title = - "High CPU - All Tabs Hidden, No Video Capture or Audio (Recent)", -}; - -const ScenarioParams kAllTabsHiddenNoAudioParams = { +const ScenarioParams kAllTabsHiddenAudioParams = { + .scenario = Scenario::kAllTabsHiddenAudio, .histogram_suffix = ".AllTabsHidden_Audio", .short_interval_cpu_threshold = 0.7036, - .trace_event_title = "High CPU - All Tabs Hidden, No Audio", + .trace_event_title = "High CPU - All Tabs Hidden, Audio", }; -const ScenarioParams kAllTabsHiddenNoVideoCapture = { +const ScenarioParams kAllTabsHiddenVideoCaptureParams = { + .scenario = Scenario::kAllTabsHiddenVideoCapture, .histogram_suffix = ".AllTabsHidden_VideoCapture", .short_interval_cpu_threshold = 0.8679, .trace_event_title = "High CPU - All Tabs Hidden, Video Capture", }; -const ScenarioParams kAllTabsHiddenZeroWindowParams = { +const ScenarioParams kZeroWindowParams = { + .scenario = Scenario::kZeroWindow, .histogram_suffix = ".ZeroWindow", .short_interval_cpu_threshold = 0.0500, .trace_event_title = "High CPU - Zero Window", }; +#if BUILDFLAG(IS_MAC) +const ScenarioParams kAllTabsHiddenNoVideoCaptureOrAudioRecentParams = { + .scenario = Scenario::kAllTabsHiddenNoVideoCaptureOrAudioRecent, + .histogram_suffix = ".AllTabsHidden_NoVideoCaptureOrAudio_Recent", + .short_interval_cpu_threshold = 0.3302, + .trace_event_title = + "High CPU - All Tabs Hidden, No Video Capture or Audio (Recent)", +}; const ScenarioParams kAllTabsHiddenZeroWindowRecentParams = { + .scenario = Scenario::kZeroWindowRecent, .histogram_suffix = ".ZeroWindow_Recent", .short_interval_cpu_threshold = 0.0745, .trace_event_title = "High CPU - Zero Window (Recent)", @@ -124,33 +137,22 @@ return kInteractionParams; return kPassiveParams; } +} // namespace -// Helper function for GetLongIntervalSuffixes(). -const char* GetLongIntervalScenarioSuffix( +ScenarioParams GetLongIntervalScenario( const UsageScenarioDataStore::IntervalData& interval_data) { // The order of the conditions is important. See the full description of each // scenario in the histograms.xml file. if (interval_data.max_tab_count == 0) - return ".ZeroWindow"; + return kZeroWindowParams; if (interval_data.max_visible_window_count == 0) { if (!interval_data.time_capturing_video.is_zero()) - return ".AllTabsHidden_VideoCapture"; + return kAllTabsHiddenVideoCaptureParams; if (!interval_data.time_playing_audio.is_zero()) - return ".AllTabsHidden_Audio"; - return ".AllTabsHidden_NoVideoCaptureOrAudio"; + return kAllTabsHiddenAudioParams; + return kAllTabsHiddenNoVideoCaptureOrAudioParams; } - return GetScenarioParamsWithVisibleWindow(interval_data).histogram_suffix; -} - -} // namespace - -// Returns suffixes to use for histograms related to a long interval described -// by `interval_data`. -std::vector<const char*> GetLongIntervalSuffixes( - const UsageScenarioDataStore::IntervalData& interval_data) { - // Histograms are recorded without suffix and with a scenario-specific - // suffix. - return {"", GetLongIntervalScenarioSuffix(interval_data)}; + return GetScenarioParamsWithVisibleWindow(interval_data); } #if BUILDFLAG(IS_MAC) @@ -162,13 +164,13 @@ if (short_interval_data.max_tab_count == 0) { if (pre_interval_data.max_tab_count != 0) return kAllTabsHiddenZeroWindowRecentParams; - return kAllTabsHiddenZeroWindowParams; + return kZeroWindowParams; } if (short_interval_data.max_visible_window_count == 0) { if (!short_interval_data.time_capturing_video.is_zero()) - return kAllTabsHiddenNoVideoCapture; + return kAllTabsHiddenVideoCaptureParams; if (!short_interval_data.time_playing_audio.is_zero()) - return kAllTabsHiddenNoAudioParams; + return kAllTabsHiddenAudioParams; if (pre_interval_data.max_visible_window_count != 0 || !pre_interval_data.time_capturing_video.is_zero() || !pre_interval_data.time_playing_audio.is_zero()) {
diff --git a/chrome/browser/metrics/power/usage_scenario.h b/chrome/browser/metrics/power/usage_scenario.h index 86e50bc..65a2534d 100644 --- a/chrome/browser/metrics/power/usage_scenario.h +++ b/chrome/browser/metrics/power/usage_scenario.h
@@ -5,23 +5,40 @@ #ifndef CHROME_BROWSER_METRICS_POWER_USAGE_SCENARIO_H_ #define CHROME_BROWSER_METRICS_POWER_USAGE_SCENARIO_H_ -#include <vector> - #include "chrome/browser/metrics/usage_scenario/usage_scenario_data_store.h" #include "third_party/abseil-cpp/absl/types/optional.h" +// Describes the different usaage scenarios in Chrome. +enum class Scenario { + kAllTabsHiddenAudio = 1, + kAllTabsHiddenNoVideoCaptureOrAudio = 2, + kAllTabsHiddenNoVideoCaptureOrAudioRecent = 3, // Short scenario only. + kAllTabsHiddenVideoCapture = 4, + kAudio = 5, + kEmbeddedVideoNoNavigation = 6, + kEmbeddedVideoWithNavigation = 7, + kFullscreenVideo = 8, + kInteraction = 9, + kNavigation = 10, + kPassive = 11, + kVideoCapture = 12, + kZeroWindow = 13, + kZeroWindowRecent = 14, // Short scenario only. + kMaxValue = kZeroWindowRecent +}; + // Contains data to determine when and how to generate histograms and trace // events for a usage scenario. struct ScenarioParams { + Scenario scenario; const char* histogram_suffix; // CPU usage threshold to emit a "high CPU" trace event. double short_interval_cpu_threshold; const char* trace_event_title; }; -// Returns the suffixes to use for histograms and UKMs related to a long -// interval described by `interval_data`. -std::vector<const char*> GetLongIntervalSuffixes( +// Returns the scenario params associated with `interval_data`. +ScenarioParams GetLongIntervalScenario( const UsageScenarioDataStore::IntervalData& interval_data); #if BUILDFLAG(IS_MAC)
diff --git a/chrome/browser/metrics/power/usage_scenario_unittest.cc b/chrome/browser/metrics/power/usage_scenario_unittest.cc index 6aabe75..117c28c1 100644 --- a/chrome/browser/metrics/power/usage_scenario_unittest.cc +++ b/chrome/browser/metrics/power/usage_scenario_unittest.cc
@@ -10,15 +10,15 @@ using testing::ElementsAre; using testing::StrEq; -TEST(UsageScenarioTest, GetLongIntervalSuffixes_ZeroWindow) { +TEST(UsageScenarioTest, GetLongIntervalScenario_ZeroWindow) { UsageScenarioDataStore::IntervalData interval_data; interval_data.max_tab_count = 0; - EXPECT_THAT(GetLongIntervalSuffixes(interval_data), - ElementsAre(StrEq(""), StrEq(".ZeroWindow"))); + EXPECT_EQ(GetLongIntervalScenario(interval_data).scenario, + Scenario::kZeroWindow); } -TEST(UsageScenarioTest, GetLongIntervalSuffixes_AllTabsHidden_VideoCapture) { +TEST(UsageScenarioTest, GetLongIntervalScenario_AllTabsHidden_VideoCapture) { UsageScenarioDataStore::IntervalData interval_data; interval_data.max_tab_count = 1; interval_data.max_visible_window_count = 0; @@ -31,11 +31,11 @@ interval_data.top_level_navigation_count = 1; interval_data.user_interaction_count = 1; - EXPECT_THAT(GetLongIntervalSuffixes(interval_data), - ElementsAre(StrEq(""), StrEq(".AllTabsHidden_VideoCapture"))); + EXPECT_EQ(GetLongIntervalScenario(interval_data).scenario, + Scenario::kAllTabsHiddenVideoCapture); } -TEST(UsageScenarioTest, GetLongIntervalSuffixes_AllTabsHidden_Audio) { +TEST(UsageScenarioTest, GetLongIntervalScenario_AllTabsHidden_Audio) { UsageScenarioDataStore::IntervalData interval_data; interval_data.max_tab_count = 1; interval_data.max_visible_window_count = 0; @@ -48,12 +48,12 @@ interval_data.top_level_navigation_count = 1; interval_data.user_interaction_count = 1; - EXPECT_THAT(GetLongIntervalSuffixes(interval_data), - ElementsAre(StrEq(""), StrEq(".AllTabsHidden_Audio"))); + EXPECT_EQ(GetLongIntervalScenario(interval_data).scenario, + Scenario::kAllTabsHiddenAudio); } TEST(UsageScenarioTest, - GetLongIntervalSuffixes_AllTabsHidden_NoVideoCaptureOrAudio) { + GetLongIntervalScenario_AllTabsHidden_NoVideoCaptureOrAudio) { UsageScenarioDataStore::IntervalData interval_data; interval_data.max_tab_count = 1; interval_data.max_visible_window_count = 0; @@ -66,12 +66,11 @@ interval_data.top_level_navigation_count = 1; interval_data.user_interaction_count = 1; - EXPECT_THAT( - GetLongIntervalSuffixes(interval_data), - ElementsAre(StrEq(""), StrEq(".AllTabsHidden_NoVideoCaptureOrAudio"))); + EXPECT_EQ(GetLongIntervalScenario(interval_data).scenario, + Scenario::kAllTabsHiddenNoVideoCaptureOrAudio); } -TEST(UsageScenarioTest, GetLongIntervalSuffixes_VideoCapture) { +TEST(UsageScenarioTest, GetLongIntervalScenario_VideoCapture) { UsageScenarioDataStore::IntervalData interval_data; interval_data.max_tab_count = 1; interval_data.max_visible_window_count = 1; @@ -84,11 +83,11 @@ interval_data.top_level_navigation_count = 1; interval_data.user_interaction_count = 1; - EXPECT_THAT(GetLongIntervalSuffixes(interval_data), - ElementsAre(StrEq(""), StrEq(".VideoCapture"))); + EXPECT_EQ(GetLongIntervalScenario(interval_data).scenario, + Scenario::kVideoCapture); } -TEST(UsageScenarioTest, GetLongIntervalSuffixes_FullscreenVideo) { +TEST(UsageScenarioTest, GetLongIntervalScenario_FullscreenVideo) { UsageScenarioDataStore::IntervalData interval_data; interval_data.max_tab_count = 1; interval_data.max_visible_window_count = 1; @@ -101,11 +100,11 @@ interval_data.top_level_navigation_count = 1; interval_data.user_interaction_count = 1; - EXPECT_THAT(GetLongIntervalSuffixes(interval_data), - ElementsAre(StrEq(""), StrEq(".FullscreenVideo"))); + EXPECT_EQ(GetLongIntervalScenario(interval_data).scenario, + Scenario::kFullscreenVideo); } -TEST(UsageScenarioTest, GetLongIntervalSuffixes_EmbeddedVideo_NoNavigation) { +TEST(UsageScenarioTest, GetLongIntervalScenario_EmbeddedVideo_NoNavigation) { UsageScenarioDataStore::IntervalData interval_data; interval_data.max_tab_count = 1; interval_data.max_visible_window_count = 1; @@ -118,11 +117,11 @@ interval_data.time_playing_audio = base::Seconds(1); interval_data.user_interaction_count = 1; - EXPECT_THAT(GetLongIntervalSuffixes(interval_data), - ElementsAre(StrEq(""), StrEq(".EmbeddedVideo_NoNavigation"))); + EXPECT_EQ(GetLongIntervalScenario(interval_data).scenario, + Scenario::kEmbeddedVideoNoNavigation); } -TEST(UsageScenarioTest, GetLongIntervalSuffixes_EmbeddedVideo_WithNavigation) { +TEST(UsageScenarioTest, GetLongIntervalScenario_EmbeddedVideo_WithNavigation) { UsageScenarioDataStore::IntervalData interval_data; interval_data.max_tab_count = 1; interval_data.max_visible_window_count = 1; @@ -135,11 +134,11 @@ interval_data.time_playing_audio = base::Seconds(1); interval_data.user_interaction_count = 1; - EXPECT_THAT(GetLongIntervalSuffixes(interval_data), - ElementsAre(StrEq(""), StrEq(".EmbeddedVideo_WithNavigation"))); + EXPECT_EQ(GetLongIntervalScenario(interval_data).scenario, + Scenario::kEmbeddedVideoWithNavigation); } -TEST(UsageScenarioTest, GetLongIntervalSuffixes_Audio) { +TEST(UsageScenarioTest, GetLongIntervalScenario_Audio) { UsageScenarioDataStore::IntervalData interval_data; interval_data.max_tab_count = 1; interval_data.max_visible_window_count = 1; @@ -152,11 +151,10 @@ interval_data.user_interaction_count = 1; interval_data.top_level_navigation_count = 1; - EXPECT_THAT(GetLongIntervalSuffixes(interval_data), - ElementsAre(StrEq(""), StrEq(".Audio"))); + EXPECT_EQ(GetLongIntervalScenario(interval_data).scenario, Scenario::kAudio); } -TEST(UsageScenarioTest, GetLongIntervalSuffixes_Navigation) { +TEST(UsageScenarioTest, GetLongIntervalScenario_Navigation) { UsageScenarioDataStore::IntervalData interval_data; interval_data.max_tab_count = 1; interval_data.max_visible_window_count = 1; @@ -169,11 +167,11 @@ // Values below should be ignored. interval_data.user_interaction_count = 1; - EXPECT_THAT(GetLongIntervalSuffixes(interval_data), - ElementsAre(StrEq(""), StrEq(".Navigation"))); + EXPECT_EQ(GetLongIntervalScenario(interval_data).scenario, + Scenario::kNavigation); } -TEST(UsageScenarioTest, GetLongIntervalSuffixes_Interaction) { +TEST(UsageScenarioTest, GetLongIntervalScenario_Interaction) { UsageScenarioDataStore::IntervalData interval_data; interval_data.max_tab_count = 1; interval_data.max_visible_window_count = 1; @@ -185,11 +183,11 @@ interval_data.top_level_navigation_count = 0; interval_data.user_interaction_count = 1; - EXPECT_THAT(GetLongIntervalSuffixes(interval_data), - ElementsAre(StrEq(""), StrEq(".Interaction"))); + EXPECT_EQ(GetLongIntervalScenario(interval_data).scenario, + Scenario::kInteraction); } -TEST(UsageScenarioTest, GetLongIntervalSuffixes_Passive) { +TEST(UsageScenarioTest, GetLongIntervalScenario_Passive) { UsageScenarioDataStore::IntervalData interval_data; interval_data.max_tab_count = 1; interval_data.max_visible_window_count = 1; @@ -201,8 +199,8 @@ interval_data.top_level_navigation_count = 0; interval_data.user_interaction_count = 0; - EXPECT_THAT(GetLongIntervalSuffixes(interval_data), - ElementsAre(StrEq(""), StrEq(".Passive"))); + EXPECT_EQ(GetLongIntervalScenario(interval_data).scenario, + Scenario::kPassive); } #if BUILDFLAG(IS_MAC)
diff --git a/chrome/browser/metrics/variations/variations_safe_mode_end_to_end_browsertest.cc b/chrome/browser/metrics/variations/variations_safe_mode_end_to_end_browsertest.cc index b940c3c9..85998f2 100644 --- a/chrome/browser/metrics/variations/variations_safe_mode_end_to_end_browsertest.cc +++ b/chrome/browser/metrics/variations/variations_safe_mode_end_to_end_browsertest.cc
@@ -185,7 +185,13 @@ base::FilePath local_state_file_; }; -TEST_F(VariationsSafeModeEndToEndBrowserTest, ExtendedSafeModeEndToEnd) { +// TODO(crbug.com/1344852): test is flaky on Mac. +#if BUILDFLAG(IS_MAC) +#define MAYBE_ExtendedSafeModeEndToEnd DISABLED_ExtendedSafeModeEndToEnd +#else +#define MAYBE_ExtendedSafeModeEndToEnd ExtendedSafeModeEndToEnd +#endif +TEST_F(VariationsSafeModeEndToEndBrowserTest, MAYBE_ExtendedSafeModeEndToEnd) { // Reuse the browser_tests binary (i.e., that this test code is in), to // manually run the sub-test. base::CommandLine sub_test =
diff --git a/chrome/browser/net/private_network_access_browsertest.cc b/chrome/browser/net/private_network_access_browsertest.cc index 3c13560..c2415f3d 100644 --- a/chrome/browser/net/private_network_access_browsertest.cc +++ b/chrome/browser/net/private_network_access_browsertest.cc
@@ -1347,8 +1347,8 @@ static constexpr char kPageFile[] = "page.html"; - std::vector<base::Value> resources; - resources.emplace_back(std::string(kPageFile)); + base::Value::List resources; + resources.Append(kPageFile); constexpr char kContents[] = R"( <html> <head>
diff --git a/chrome/browser/password_check/android/password_check_manager.cc b/chrome/browser/password_check/android/password_check_manager.cc index 492a266e..d0cdbdc 100644 --- a/chrome/browser/password_check/android/password_check_manager.cc +++ b/chrome/browser/password_check/android/password_check_manager.cc
@@ -155,7 +155,7 @@ credential.signon_realm, is_using_account_store), &saved_passwords_presenter_, base::BindOnce(&PasswordCheckManager::OnEditUIDismissed, - base::Unretained(this)), + weak_ptr_factory_.GetWeakPtr()), context, settings_launcher); } @@ -359,7 +359,7 @@ } ResetPrecondition(kScriptsCachePrewarmed); password_script_fetcher_->RefreshScriptsIfNecessary(base::BindOnce( - &PasswordCheckManager::OnScriptsFetched, base::Unretained(this))); + &PasswordCheckManager::OnScriptsFetched, weak_ptr_factory_.GetWeakPtr())); } void PasswordCheckManager::OnScriptsFetched() {
diff --git a/chrome/browser/password_check/android/password_check_manager.h b/chrome/browser/password_check/android/password_check_manager.h index eb0b096..57862f1ab 100644 --- a/chrome/browser/password_check/android/password_check_manager.h +++ b/chrome/browser/password_check/android/password_check_manager.h
@@ -7,6 +7,7 @@ #include "base/memory/raw_ptr.h" #include "base/memory/scoped_refptr.h" +#include "base/memory/weak_ptr.h" #include "base/scoped_observation.h" #include "base/strings/string_piece_forward.h" #include "chrome/browser/password_check/android/password_check_ui_status.h" @@ -285,6 +286,9 @@ password_manager::BulkLeakCheckServiceInterface, password_manager::BulkLeakCheckServiceInterface::Observer> observed_bulk_leak_check_service_{this}; + + // Weak pointer factory for callback binding safety. + base::WeakPtrFactory<PasswordCheckManager> weak_ptr_factory_{this}; }; #endif // CHROME_BROWSER_PASSWORD_CHECK_ANDROID_PASSWORD_CHECK_MANAGER_H_
diff --git a/chrome/browser/policy/chrome_browser_cloud_management_register_watcher.cc b/chrome/browser/policy/chrome_browser_cloud_management_register_watcher.cc index e9bb87f..91f146f 100644 --- a/chrome/browser/policy/chrome_browser_cloud_management_register_watcher.cc +++ b/chrome/browser/policy/chrome_browser_cloud_management_register_watcher.cc
@@ -45,6 +45,15 @@ if (token_storage->RetrieveDMToken().is_valid()) return RegisterResult::kEnrollmentSuccessBeforeDialogDisplayed; + // Unretained(this) is safe because `run_loop_` runs in the current scope + // and is not quit until after `callback` executes. Without the run loop it + // would NOT be safe, because `this` is deleted in PostMainMessageLoopRun. If + // execution returned from the current scope, potentially the browser could + // start shutting down and exit the main thread, destroying `this` with + // `callback` scheduled to run on the ThreadPool. (Which sequence runs + // `callback` is an implementation detail of EnterpriseStartupDialog that + // ChromeBrowserCloudManagementRegisterWatcher should not make assumptions + // about.) EnterpriseStartupDialog::DialogResultCallback callback = base::BindOnce( &ChromeBrowserCloudManagementRegisterWatcher::OnDialogClosed, base::Unretained(this));
diff --git a/chrome/browser/printing/printing_init.cc b/chrome/browser/printing/printing_init.cc index f357a062..8255983 100644 --- a/chrome/browser/printing/printing_init.cc +++ b/chrome/browser/printing/printing_init.cc
@@ -6,8 +6,8 @@ #include "chrome/browser/headless/headless_mode_util.h" #include "components/embedder_support/user_agent_utils.h" +#include "components/printing/browser/headless/headless_print_manager.h" #include "components/printing/browser/print_manager_utils.h" -#include "components/printing/browser/print_to_pdf/pdf_print_manager.h" #include "content/public/browser/web_contents.h" #include "printing/buildflags/buildflags.h" @@ -22,7 +22,7 @@ void InitializePrinting(content::WebContents* web_contents) { if (headless::IsChromeNativeHeadless()) { - print_to_pdf::PdfPrintManager::CreateForWebContents(web_contents); + headless::HeadlessPrintManager::CreateForWebContents(web_contents); return; } #if BUILDFLAG(ENABLE_PRINT_PREVIEW)
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/BUILD.gn b/chrome/browser/resources/chromeos/accessibility/accessibility_common/BUILD.gn index 6a92bbc..9cb4ce47 100644 --- a/chrome/browser/resources/chromeos/accessibility/accessibility_common/BUILD.gn +++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/BUILD.gn
@@ -45,6 +45,7 @@ "dictation/editing_util.js", "dictation/focus_handler.js", "dictation/input_controller.js", + "dictation/locale_info.js", "dictation/macros/delete_prev_sent_macro.js", "dictation/macros/hidden_macro_manager.js", "dictation/macros/input_text_view_macro.js", @@ -136,6 +137,7 @@ "dictation/dictation_ui_test.js", "dictation/editing_util_test.js", "dictation/focus_handler_test.js", + "dictation/locale_info_test.js", "dictation/macros/dictation_macros_test.js", "dictation/parse/dictation_parse_test.js", "magnifier/magnifier_test.js", @@ -214,6 +216,7 @@ ":dictation_editing_util", ":dictation_focus_handler", ":dictation_input_controller", + ":dictation_locale_info", ":dictation_metrics", ":dictation_ui_controller", ":speech_parsing", @@ -230,6 +233,7 @@ deps = [ ":dictation_editing_util", ":dictation_focus_handler", + ":dictation_locale_info", ] externs_list = [ "$externs_path/input_method_private.js", @@ -240,6 +244,7 @@ js_library("dictation_ui_controller") { sources = [ "dictation/ui_controller.js" ] + deps = [ ":dictation_locale_info" ] externs_list = [ "$externs_path/accessibility_private.js" ] } @@ -261,6 +266,7 @@ ] deps = [ ":dictation_input_controller", + ":dictation_locale_info", "../common:event_generator", "../common:key_code", ] @@ -281,6 +287,7 @@ } deps = [ ":dictation_input_controller", + ":dictation_locale_info", ":dictation_macros", ] } @@ -305,3 +312,7 @@ js_library("dictation_editing_util") { sources = [ "dictation/editing_util.js" ] } + +js_library("dictation_locale_info") { + sources = [ "dictation/locale_info.js" ] +}
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/dictation.js b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/dictation.js index 3c851a7..4cbb4ee 100644 --- a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/dictation.js +++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/dictation.js
@@ -4,6 +4,7 @@ import {FocusHandler} from './focus_handler.js'; import {InputController} from './input_controller.js'; +import {LocaleInfo} from './locale_info.js'; import {HiddenMacroManager} from './macros/hidden_macro_manager.js'; import {Macro} from './macros/macro.js'; import {MacroName} from './macros/macro_names.js'; @@ -34,9 +35,6 @@ /** @private {HiddenMacroManager} */ this.hiddenMacroManager_ = null; - /** @private {string} */ - this.localePref_ = ''; - /** * Whether or not Dictation is active. * @private {boolean} @@ -86,9 +84,7 @@ () => this.stopDictation_(/*notify=*/ true), this.focusHandler_); this.uiController_ = new UIController(); this.speechParser_ = new SpeechParser(this.inputController_); - if (this.localePref_) { - this.propagateLocale_(this.localePref_); - } + this.speechParser_.refresh(); this.hiddenMacroManager_ = new HiddenMacroManager(this.inputController_); // Set default speech recognition properties. Locale will be updated when @@ -310,7 +306,7 @@ this.clearInterimText_(); // Record metrics. - this.metricsUtils_ = new MetricsUtils(type, this.localePref_); + this.metricsUtils_ = new MetricsUtils(type, LocaleInfo.locale); this.metricsUtils_.recordSpeechRecognitionStarted(); this.uiController_.setState( @@ -356,8 +352,8 @@ if (pref.value) { const locale = /** @type {string} */ (pref.value); this.speechRecognitionOptions_.locale = locale; - this.localePref_ = locale; - this.propagateLocale_(locale); + LocaleInfo.locale = locale; + this.speechParser_.refresh(); } break; case Dictation.SPOKEN_FEEDBACK_PREF: @@ -503,18 +499,6 @@ this.hiddenMacroManager_.runMacroWithTwoStringArgsForTesting( name, arg1, arg2); } - - /** - * @param {string} locale - * @private - */ - propagateLocale_(locale) { - const commandsSupported = - SpeechParser.areCommandsSupported(locale, chrome.i18n.getUILanguage()); - this.speechParser_.initialize(locale, commandsSupported); - this.inputController_.setLocale(locale); - this.uiController_.setHintsSupported(commandsSupported); - } } /**
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/input_controller.js b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/input_controller.js index 38b695f..2b573cde8 100644 --- a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/input_controller.js +++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/input_controller.js
@@ -4,6 +4,7 @@ import {EditingUtil} from './editing_util.js'; import {FocusHandler} from './focus_handler.js'; +import {LocaleInfo} from './locale_info.js'; const AutomationNode = chrome.automation.AutomationNode; const EventType = chrome.automation.EventType; @@ -30,9 +31,6 @@ /** @private {?function():void} */ this.onConnectCallback_ = null; - /** @private {?string} */ - this.locale_ = null; - this.initialize_(); } @@ -103,12 +101,8 @@ return; } - const language = this.locale_.split('-')[0]; - const useSmartSpacingAndCapitalization = - InputController.SMART_SPACING_AND_CAPITALIZATION_LANGUAGES_.includes( - language); const data = this.getEditableNodeData_(); - if (useSmartSpacingAndCapitalization && data) { + if (LocaleInfo.allowSmartCapAndSpacing() && data) { const {value, caretIndex} = data; text = EditingUtil.smartCapitalization(value, caretIndex, text); text = EditingUtil.smartSpacing(value, caretIndex, text); @@ -279,11 +273,6 @@ node.setSelection(newCaretIndex, newCaretIndex); } - /** @param {string} locale */ - setLocale(locale) { - this.locale_ = locale; - } - /** * @param {string} value * @param {number} index @@ -339,12 +328,3 @@ * @const */ InputController.NO_ACTIVE_IME_CONTEXT_ID_ = -1; - - -/** - * The languages that are supported by smart spacing and capitalization. - * @private {!Array<string>} - * @const - */ -InputController.SMART_SPACING_AND_CAPITALIZATION_LANGUAGES_ = - ['en', 'fr', 'it', 'de', 'es'];
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/locale_info.js b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/locale_info.js new file mode 100644 index 0000000..ba925759 --- /dev/null +++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/locale_info.js
@@ -0,0 +1,89 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +/** Contains all locale-related information for Dictation. */ +export class LocaleInfo { + /** @return {boolean} */ + static allowSmartCapAndSpacing() { + const language = LocaleInfo.locale.split('-')[0]; + return LocaleInfo.SMART_CAP_AND_SPACING_LANGUAGES_.has(language); + } + + /** @return {boolean} */ + static allowSmartEditing() { + // Restrict smart editing commands to left-to-right locales. + // TODO(crbug.com/1331351): Add support for RTL locales. + return !LocaleInfo.isRTLLocale(); + } + + /** @return {boolean} */ + static isRTLLocale() { + const locale = LocaleInfo.locale; + return LocaleInfo.RTL_LOCALES_.has(locale); + } + + /** @return {string|undefined} */ + static getUILanguage() { + const locale = LocaleInfo.locale.toLowerCase(); + return LocaleInfo.LOCALE_TO_UI_LANGUAGE_MAP_[locale]; + } + + /** + * Determines whether commands are supported for this Dictation language + * and UI system language. + * @return {boolean} Whether commands are supported. + */ + static areCommandsSupported() { + // Currently Dictation cannot support commands when the UI language + // doesn't match the Dictation language. See crbug.com/1340590. + const systemLocale = chrome.i18n.getUILanguage().toLowerCase(); + const systemLanguage = systemLocale.split('-')[0]; + const dictationLanguage = LocaleInfo.locale.toLowerCase().split('-')[0]; + if (systemLanguage === dictationLanguage) { + return true; + } + + return Boolean(LocaleInfo.getUILanguage()) && + (LocaleInfo.getUILanguage() === systemLanguage || + LocaleInfo.getUILanguage() === systemLocale); + } +} + +/** + * The current Dictation locale. + * @type {string} + */ +LocaleInfo.locale = ''; + +/** + * @const {!Set<string>} + * @private + */ +LocaleInfo.SMART_CAP_AND_SPACING_LANGUAGES_ = + new Set(['en', 'fr', 'it', 'de', 'es']); + +/** + * All RTL locales from Dictation::GetAllSupportedLocales. + * @private {!Set<string>} + * @const + */ +LocaleInfo.RTL_LOCALES_ = new Set([ + 'ar-AE', 'ar-BH', 'ar-DZ', 'ar-EG', 'ar-IL', 'ar-IQ', 'ar-JO', + 'ar-KW', 'ar-LB', 'ar-MA', 'ar-OM', 'ar-PS', 'ar-QA', 'ar-SA', + 'ar-TN', 'ar-YE', 'fa-IR', 'iw-IL', 'ur-IN', 'ur-PK', +]); + +/** + * TODO: get this data from l10n or i18n. + * Hebrew in Dictation is 'iw-IL' but 'he' in UI languages. + * yue-Hant-HK can map to 'zh-TW' because both are written as traditional + * Chinese. Norwegian in Dictation is 'no-NO' but 'nb' in UI languages. + * @private {!Object<string, string>} + * @const + */ +LocaleInfo.LOCALE_TO_UI_LANGUAGE_MAP_ = { + 'iw-il': 'he', + 'yue-hant-hk': 'zh-tw', + 'no-no': 'nb', +};
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/locale_info_test.js b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/locale_info_test.js new file mode 100644 index 0000000..03d46b8 --- /dev/null +++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/locale_info_test.js
@@ -0,0 +1,121 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +GEN_INCLUDE(['dictation_test_base.js']); + +DictationLocaleInfoTest = class extends DictationE2ETestBase { + /** @override */ + async setUpDeferred() { + await super.setUpDeferred(); + await importModule( + 'LocaleInfo', '/accessibility_common/dictation/locale_info.js'); + } +}; + +AX_TEST_F('DictationLocaleInfoTest', 'AllowSmartCapAndSpacing', function() { + // Restrict behavior to English + FIGS (French, Italian, German, Spanish). + LocaleInfo.locale = 'en-US'; + assertTrue(LocaleInfo.allowSmartCapAndSpacing()); + LocaleInfo.locale = 'fr'; + assertTrue(LocaleInfo.allowSmartCapAndSpacing()); + LocaleInfo.locale = 'it-IT'; + assertTrue(LocaleInfo.allowSmartCapAndSpacing()); + LocaleInfo.locale = 'de'; + assertTrue(LocaleInfo.allowSmartCapAndSpacing()); + LocaleInfo.locale = 'es'; + assertTrue(LocaleInfo.allowSmartCapAndSpacing()); + + LocaleInfo.locale = 'ja-JP'; + assertFalse(LocaleInfo.allowSmartCapAndSpacing()); +}); + +AX_TEST_F('DictationLocaleInfoTest', 'AllowSmartEditing', function() { + // Restrict behavior to left-to-right locales. + LocaleInfo.locale = 'en-US'; + assertTrue(LocaleInfo.allowSmartEditing()); + LocaleInfo.locale = 'ja-JP'; + assertTrue(LocaleInfo.allowSmartEditing()); + + LocaleInfo.locale = 'ar-LB'; + assertFalse(LocaleInfo.allowSmartEditing()); +}); + +AX_TEST_F('DictationLocaleInfoTest', 'IsRTLLocale', function() { + LocaleInfo.locale = 'ja-JP'; + assertFalse(LocaleInfo.isRTLLocale()); + LocaleInfo.locale = 'ar-LB'; + assertTrue(LocaleInfo.isRTLLocale()); +}); + +AX_TEST_F('DictationLocaleInfoTest', 'GetUILanguage', function() { + LocaleInfo.locale = 'iw-il'; + assertEquals('he', LocaleInfo.getUILanguage()); + LocaleInfo.locale = 'iw-IL'; + assertEquals('he', LocaleInfo.getUILanguage()); + LocaleInfo.locale = 'yue-hant-hk'; + assertEquals('zh-tw', LocaleInfo.getUILanguage()); + LocaleInfo.locale = 'no-no'; + assertEquals('nb', LocaleInfo.getUILanguage()); + LocaleInfo.locale = 'en-US'; + assertEquals(undefined, LocaleInfo.getUILanguage()); +}); + +AX_TEST_F('DictationLocaleInfoTest', 'AreCommandsSupported', function() { + let systemLocale; + chrome.i18n.getUILanguage = () => { + return systemLocale; + }; + const areCommandsSupported = LocaleInfo.areCommandsSupported; + + // True if the language part of the code matches. + LocaleInfo.locale = 'en-US'; + systemLocale = 'en'; + assertTrue(areCommandsSupported()); + + LocaleInfo.locale = 'EN-US'; + systemLocale = 'en'; + assertTrue(areCommandsSupported()); + + LocaleInfo.locale = 'en-US'; + systemLocale = 'en-GB'; + assertTrue(areCommandsSupported()); + + // False if the language part of the code doesn't match, in most cases. + LocaleInfo.locale = 'en-US'; + systemLocale = 'ja-JP'; + assertFalse(areCommandsSupported()); + + LocaleInfo.locale = 'ja-JP'; + systemLocale = 'en-US'; + assertFalse(areCommandsSupported()); + + // Special cases: these Dictation locales can map to UI languages. + LocaleInfo.locale = 'iw-IL'; + systemLocale = 'he'; + assertTrue(areCommandsSupported()); + + LocaleInfo.locale = 'iw-IL'; + systemLocale = 'he-IL'; + assertTrue(areCommandsSupported()); + + LocaleInfo.locale = 'no-NO'; + systemLocale = 'nb'; + assertTrue(areCommandsSupported()); + + LocaleInfo.locale = 'no-NO'; + systemLocale = 'nb-NB'; + assertTrue(areCommandsSupported()); + + LocaleInfo.locale = 'yue-Hant-HK'; + systemLocale = 'zh-TW'; + assertTrue(areCommandsSupported()); + + LocaleInfo.locale = 'yue-Hant-HK'; + systemLocale = 'zh'; + assertFalse(areCommandsSupported()); + + LocaleInfo.locale = 'yue-Hant-HK'; + systemLocale = 'zh-CN'; + assertFalse(areCommandsSupported()); +});
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/delete_prev_sent_macro.js b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/delete_prev_sent_macro.js index d703ac79..9eb0845 100644 --- a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/delete_prev_sent_macro.js +++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/delete_prev_sent_macro.js
@@ -32,4 +32,9 @@ this.inputController_.deletePrevSentence(); return this.createRunMacroResult_(/*isSuccess=*/ true); } + + /** @override */ + isSmart() { + return true; + } }
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/hidden_macro_manager.js b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/hidden_macro_manager.js index 58f3ca6..5a3ea0d 100644 --- a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/hidden_macro_manager.js +++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/hidden_macro_manager.js
@@ -41,18 +41,16 @@ new DeletePrevSentMacro(this.inputController_).runMacro(); break; case MacroName.NAV_NEXT_WORD: - new NavNextWordMacro(/*isRTLLocale=*/ false).runMacro(); + new NavNextWordMacro().runMacro(); break; case MacroName.NAV_PREV_WORD: - new NavPrevWordMacro(/*isRTLLocale=*/ false).runMacro(); + new NavPrevWordMacro().runMacro(); break; case MacroName.NAV_NEXT_SENT: - new NavNextSentMacro(this.inputController_, /*isRTLLocale=*/ false) - .runMacro(); + new NavNextSentMacro(this.inputController_).runMacro(); break; case MacroName.NAV_PREV_SENT: - new NavPrevSentMacro(this.inputController_, /*isRTLLocale=*/ false) - .runMacro(); + new NavPrevSentMacro(this.inputController_).runMacro(); break; default: throw new Error(`Cannot run macro: ${name} for testing`);
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/macro.js b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/macro.js index dd4b532..9ec23b1 100644 --- a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/macro.js +++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/macro.js
@@ -132,4 +132,9 @@ createRunMacroResult_(isSuccess, error) { return {isSuccess, error}; } + + /** @return {boolean} */ + isSmart() { + return false; + } }
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/nav_sent_macro.js b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/nav_sent_macro.js index 58d30eb5..8bc97ea 100644 --- a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/nav_sent_macro.js +++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/nav_sent_macro.js
@@ -9,16 +9,11 @@ /** Implements a macro that moves the text caret to the next sentence. */ export class NavNextSentMacro extends Macro { - /** - * @param {!InputController} inputController - * @param {boolean} isRTLLocale - */ - constructor(inputController, isRTLLocale) { + /** @param {!InputController} inputController */ + constructor(inputController) { super(MacroName.NAV_NEXT_SENT); /** @private {!InputController} */ this.inputController_ = inputController; - /** @private {boolean} */ - this.isRTLLocale_ = isRTLLocale; } /** @override */ @@ -36,20 +31,20 @@ this.inputController_.navNextSent(); return this.createRunMacroResult_(/*isSuccess=*/ true); } + + /** @override */ + isSmart() { + return true; + } } /** Implements a macro that moves the text caret to the previous sentence. */ export class NavPrevSentMacro extends Macro { - /** - * @param {!InputController} inputController - * @param {boolean} isRTLLocale - */ - constructor(inputController, isRTLLocale) { + /** @param {!InputController} inputController */ + constructor(inputController) { super(MacroName.NAV_PREV_SENT); /** @private {!InputController} */ this.inputController_ = inputController; - /** @private {boolean} */ - this.isRTLLocale_ = isRTLLocale; } /** @override */ @@ -67,4 +62,9 @@ this.inputController_.navPrevSent(); return this.createRunMacroResult_(/*isSuccess=*/ true); } + + /** @override */ + isSmart() { + return true; + } }
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/repeatable_key_press_macro.js b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/repeatable_key_press_macro.js index 65c03c0e..13c5dbc4 100644 --- a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/repeatable_key_press_macro.js +++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/repeatable_key_press_macro.js
@@ -3,6 +3,7 @@ // found in the LICENSE file. import {EventGenerator} from '../../../common/event_generator.js'; +import {LocaleInfo} from '../locale_info.js'; import {Macro, MacroError} from './macro.js'; import {MacroName} from './macro_names.js'; @@ -69,43 +70,29 @@ /** Macro to navigate to the previous character. */ export class NavPreviousCharMacro extends RepeatableKeyPressMacro { - /** - * @param {boolean} isRTLLocale Whether the Dictation speech recognition - * locale is right-to-left. - * @param {number=} repeat The number of characters to move. - */ - constructor(isRTLLocale, repeat = 1) { + /** @param {number=} repeat The number of characters to move. */ + constructor(repeat = 1) { super(MacroName.NAV_PREV_CHAR, repeat); - - /** @private {boolean} */ - this.isRTLLocale_ = isRTLLocale; } /** @override */ doKeyPress() { EventGenerator.sendKeyPress( - this.isRTLLocale_ ? KeyCode.RIGHT : KeyCode.LEFT); + LocaleInfo.isRTLLocale() ? KeyCode.RIGHT : KeyCode.LEFT); } } /** Macro to navigate to the next character. */ export class NavNextCharMacro extends RepeatableKeyPressMacro { - /** - * @param {boolean} isRTLLocale Whether the Dictation speech recognition - * locale is right-to-left. - * @param {number=} repeat The number of characters to move. - */ - constructor(isRTLLocale, repeat = 1) { + /** @param {number=} repeat The number of characters to move. */ + constructor(repeat = 1) { super(MacroName.NAV_NEXT_CHAR, repeat); - - /** @private {boolean} */ - this.isRTLLocale_ = isRTLLocale; } /** @override */ doKeyPress() { EventGenerator.sendKeyPress( - this.isRTLLocale_ ? KeyCode.LEFT : KeyCode.RIGHT); + LocaleInfo.isRTLLocale() ? KeyCode.LEFT : KeyCode.RIGHT); } } @@ -209,20 +196,14 @@ /** Macro to unselect text. */ export class UnselectTextMacro extends RepeatableKeyPressMacro { - /** - * @param {boolean} isRTLLocale Whether the Dictation speech recognition - * locale is right-to-left. - */ - constructor(isRTLLocale) { + constructor() { super(MacroName.UNSELECT_TEXT, /*repeat=*/ 1); - /** @private {boolean} */ - this.isRTLLocale_ = isRTLLocale; } /** @override */ doKeyPress() { EventGenerator.sendKeyPress( - this.isRTLLocale_ ? KeyCode.LEFT : KeyCode.RIGHT); + LocaleInfo.isRTLLocale() ? KeyCode.LEFT : KeyCode.RIGHT); } } @@ -240,42 +221,28 @@ /** Macro to navigate to the next word. */ export class NavNextWordMacro extends RepeatableKeyPressMacro { - /** - * @param {boolean} isRTLLocale Whether the Dictation speech recognition - * locale is right-to-left. - * @param {number=} repeat The number of words to move. - */ - constructor(isRTLLocale, repeat = 1) { + /** @param {number=} repeat The number of words to move. */ + constructor(repeat = 1) { super(MacroName.NAV_NEXT_WORD, repeat); - - /** @private {boolean} */ - this.isRTLLocale_ = isRTLLocale; } /** @override */ doKeyPress() { EventGenerator.sendKeyPress( - this.isRTLLocale_ ? KeyCode.LEFT : KeyCode.RIGHT, {ctrl: true}); + LocaleInfo.isRTLLocale() ? KeyCode.LEFT : KeyCode.RIGHT, {ctrl: true}); } } /** Macro to navigate to the previous word. */ export class NavPrevWordMacro extends RepeatableKeyPressMacro { - /** - * @param {boolean} isRTLLocale Whether the Dictation speech recognition - * locale is right-to-left. - * @param {number=} repeat The number of words to move. - */ - constructor(isRTLLocale, repeat = 1) { + /** @param {number=} repeat The number of words to move. */ + constructor(repeat = 1) { super(MacroName.NAV_PREV_WORD, repeat); - - /** @private {boolean} */ - this.isRTLLocale_ = isRTLLocale; } /** @override */ doKeyPress() { EventGenerator.sendKeyPress( - this.isRTLLocale_ ? KeyCode.RIGHT : KeyCode.LEFT, {ctrl: true}); + LocaleInfo.isRTLLocale() ? KeyCode.RIGHT : KeyCode.LEFT, {ctrl: true}); } }
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/smart_delete_phrase_macro.js b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/smart_delete_phrase_macro.js index a6474394..ec34a092 100644 --- a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/smart_delete_phrase_macro.js +++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/smart_delete_phrase_macro.js
@@ -36,4 +36,9 @@ this.inputController_.deletePhrase(this.phrase_); return this.createRunMacroResult_(/*isSuccess=*/ true); } + + /** @override */ + isSmart() { + return true; + } }
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/smart_insert_before_macro.js b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/smart_insert_before_macro.js index 0da4923..8439a71 100644 --- a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/smart_insert_before_macro.js +++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/smart_insert_before_macro.js
@@ -42,4 +42,9 @@ this.inputController_.insertBefore(this.insertPhrase_, this.beforePhrase_); return this.createRunMacroResult_(/*isSuccess=*/ true); } + + /** @override */ + isSmart() { + return true; + } }
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/smart_replace_phrase_macro.js b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/smart_replace_phrase_macro.js index 1370fea..400d421 100644 --- a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/smart_replace_phrase_macro.js +++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/smart_replace_phrase_macro.js
@@ -40,4 +40,9 @@ this.inputController_.replacePhrase(this.deletePhrase_, this.insertPhrase_); return this.createRunMacroResult_(/*isSuccess=*/ true); } + + /** @override */ + isSmart() { + return true; + } }
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/smart_select_between_macro.js b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/smart_select_between_macro.js index e5c98a2..6bcac17 100644 --- a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/smart_select_between_macro.js +++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/smart_select_between_macro.js
@@ -39,4 +39,9 @@ this.inputController_.selectBetween(this.startPhrase_, this.endPhrase_); return this.createRunMacroResult_(/*isSuccess=*/ true); } + + /** @override */ + isSmart() { + return true; + } }
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/parse/dictation_parse_test.js b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/parse/dictation_parse_test.js index 15a9df35..6612d01 100644 --- a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/parse/dictation_parse_test.js +++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/parse/dictation_parse_test.js
@@ -35,58 +35,87 @@ assertNotNullNorUndefined(strategy); let macro = await strategy.parse('Hello world'); assertEquals('INPUT_TEXT_VIEW', macro.getMacroNameString()); + assertFalse(macro.isSmart()); macro = await strategy.parse('type delete'); assertEquals('INPUT_TEXT_VIEW', macro.getMacroNameString()); + assertFalse(macro.isSmart()); macro = await strategy.parse('delete'); assertEquals('DELETE_PREV_CHAR', macro.getMacroNameString()); + assertFalse(macro.isSmart()); macro = await strategy.parse('move to the previous character'); assertEquals('NAV_PREV_CHAR', macro.getMacroNameString()); + assertFalse(macro.isSmart()); macro = await strategy.parse('move to the next character'); assertEquals('NAV_NEXT_CHAR', macro.getMacroNameString()); + assertFalse(macro.isSmart()); macro = await strategy.parse('move to the previous line'); assertEquals('NAV_PREV_LINE', macro.getMacroNameString()); + assertFalse(macro.isSmart()); macro = await strategy.parse('move to the next line'); assertEquals('NAV_NEXT_LINE', macro.getMacroNameString()); + assertFalse(macro.isSmart()); macro = await strategy.parse('copy'); assertEquals('COPY_SELECTED_TEXT', macro.getMacroNameString()); + assertFalse(macro.isSmart()); macro = await strategy.parse('paste'); assertEquals('PASTE_TEXT', macro.getMacroNameString()); + assertFalse(macro.isSmart()); macro = await strategy.parse('cut'); assertEquals('CUT_SELECTED_TEXT', macro.getMacroNameString()); + assertFalse(macro.isSmart()); macro = await strategy.parse('undo'); assertEquals('UNDO_TEXT_EDIT', macro.getMacroNameString()); + assertFalse(macro.isSmart()); macro = await strategy.parse('redo'); assertEquals('REDO_ACTION', macro.getMacroNameString()); + assertFalse(macro.isSmart()); macro = await strategy.parse('select all'); assertEquals('SELECT_ALL_TEXT', macro.getMacroNameString()); + assertFalse(macro.isSmart()); macro = await strategy.parse('unselect'); assertEquals('UNSELECT_TEXT', macro.getMacroNameString()); + assertFalse(macro.isSmart()); macro = await strategy.parse('help'); assertEquals('LIST_COMMANDS', macro.getMacroNameString()); + assertFalse(macro.isSmart()); macro = await strategy.parse('new line'); assertEquals('NEW_LINE', macro.getMacroNameString()); + assertFalse(macro.isSmart()); macro = await strategy.parse('cancel'); assertEquals('STOP_LISTENING', macro.getMacroNameString()); + assertFalse(macro.isSmart()); macro = await strategy.parse('delete the previous word'); assertEquals('DELETE_PREV_WORD', macro.getMacroNameString()); - macro = await strategy.parse('delete the previous sentence'); - assertEquals('DELETE_PREV_SENT', macro.getMacroNameString()); + assertFalse(macro.isSmart()); macro = await strategy.parse('move to the next word'); assertEquals('NAV_NEXT_WORD', macro.getMacroNameString()); + assertFalse(macro.isSmart()); macro = await strategy.parse('move to the previous word'); assertEquals('NAV_PREV_WORD', macro.getMacroNameString()); + assertFalse(macro.isSmart()); + + // Smart macros. + macro = await strategy.parse('delete the previous sentence'); + assertEquals('DELETE_PREV_SENT', macro.getMacroNameString()); + assertTrue(macro.isSmart()); macro = await strategy.parse('delete hello world'); assertEquals('SMART_DELETE_PHRASE', macro.getMacroNameString()); + assertTrue(macro.isSmart()); macro = await strategy.parse('replace hello world with goodnight world'); assertEquals('SMART_REPLACE_PHRASE', macro.getMacroNameString()); + assertTrue(macro.isSmart()); macro = await strategy.parse('insert hello world before goodnight world'); assertEquals('SMART_INSERT_BEFORE', macro.getMacroNameString()); + assertTrue(macro.isSmart()); macro = await strategy.parse('select from hello world to goodnight world'); assertEquals('SMART_SELECT_BTWN_INCL', macro.getMacroNameString()); + assertTrue(macro.isSmart()); macro = await strategy.parse('move to the next sentence'); assertEquals('NAV_NEXT_SENT', macro.getMacroNameString()); + assertTrue(macro.isSmart()); macro = await strategy.parse('move to the previous sentence'); assertEquals('NAV_PREV_SENT', macro.getMacroNameString()); + assertTrue(macro.isSmart()); }); // TODO(crbug.com/1264544): This test fails because of a memory issues @@ -105,30 +134,25 @@ assertEquals('DELETE_PREV_CHAR', macro.getMacroNameString()); }); -AX_TEST_F( - 'DictationParseTest', 'AreCommandsSupportedForLocales', async function() { - // True if the language part of the code matches. - assertTrue(SpeechParser.areCommandsSupported('en-US', 'en')); - assertTrue(SpeechParser.areCommandsSupported('EN-US', 'en')); - assertTrue(SpeechParser.areCommandsSupported('en-US', 'en-US')); - assertTrue(SpeechParser.areCommandsSupported('en-GB', 'en-US')); - assertTrue(SpeechParser.areCommandsSupported('es-CR', 'es')); - assertTrue(SpeechParser.areCommandsSupported('es-CR', 'es-ES')); +AX_TEST_F('DictationParseTest', 'NoSmartMacrosForRTLLocales', async function() { + const strategy = this.getSimpleParseStrategy(); + assertNotNullNorUndefined(strategy); + await this.setPref(Dictation.DICTATION_LOCALE_PREF, 'en-US'); + await this.getPref(Dictation.DICTATION_LOCALE_PREF); - // False if the language part of the code doesn't match, in most cases. - assertFalse(SpeechParser.areCommandsSupported('en-US', 'ja-JP')); - assertFalse(SpeechParser.areCommandsSupported('ja-JP', 'en-US')); - assertFalse(SpeechParser.areCommandsSupported('iw-IL', 'en-US')); - assertFalse(SpeechParser.areCommandsSupported('no-NO', 'es-ES')); - assertFalse(SpeechParser.areCommandsSupported('yue-Hant-HK', 'en-US')); + let macro = await strategy.parse('insert hello world before goodnight world'); + assertNotNullNorUndefined(macro); + assertTrue(macro.isSmart()); + assertEquals('SMART_INSERT_BEFORE', macro.getMacroNameString()); - // Special cases: these Dictation locales can map to - // existing UI languages. - assertTrue(SpeechParser.areCommandsSupported('iw-IL', 'he')); - assertTrue(SpeechParser.areCommandsSupported('iw-IL', 'he-IL')); - assertTrue(SpeechParser.areCommandsSupported('no-NO', 'nb')); - assertTrue(SpeechParser.areCommandsSupported('no-NO', 'nb-NB')); - assertFalse(SpeechParser.areCommandsSupported('yue-Hant-HK', 'zh')); - assertFalse(SpeechParser.areCommandsSupported('yue-Hant-HK', 'zh-CN')); - assertTrue(SpeechParser.areCommandsSupported('yue-Hant-HK', 'zh-TW')); - }); + // Change Dictation locale to a right-to-left locale. + await this.setPref(Dictation.DICTATION_LOCALE_PREF, 'ar-LB'); + await this.getPref(Dictation.DICTATION_LOCALE_PREF); + + // Smart macros are not supported in right-to-left locales. In these cases, + // we fall back to INPUT_TEXT_VIEW macros. + macro = await strategy.parse('insert hello world before goodnight world'); + assertNotNullNorUndefined(macro); + assertFalse(macro.isSmart()); + assertEquals('INPUT_TEXT_VIEW', macro.getMacroNameString()); +});
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/parse/input_text_strategy.js b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/parse/input_text_strategy.js index f73b25b..c43e67a1 100644 --- a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/parse/input_text_strategy.js +++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/parse/input_text_strategy.js
@@ -11,7 +11,7 @@ export class InputTextStrategy extends ParseStrategy { /** @param {!InputController} inputController */ constructor(inputController) { - super(inputController, false); + super(inputController); } /** @override */
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/parse/parse_strategy.js b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/parse/parse_strategy.js index 35022b61..301dc75 100644 --- a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/parse/parse_strategy.js +++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/parse/parse_strategy.js
@@ -15,15 +15,10 @@ * Macro. */ export class ParseStrategy { - /** - * @param {!InputController} inputController - * @param {boolean} isRTLLocale - */ - constructor(inputController, isRTLLocale) { + /** @param {!InputController} inputController */ + constructor(inputController) { /** @private {!InputController} */ this.inputController_ = inputController; - /** @private {boolean} */ - this.isRTLLocale_ = isRTLLocale; } /** @return {!InputController} */ @@ -31,11 +26,6 @@ return this.inputController_; } - /** @return {boolean} */ - getIsRTLLocale() { - return this.isRTLLocale_; - } - /** * Accepts text, parses it, and returns a Macro. * @param {string} text
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/parse/pumpkin_parse_strategy.js b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/parse/pumpkin_parse_strategy.js index f3b59b9..07e7ecd 100644 --- a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/parse/pumpkin_parse_strategy.js +++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/parse/pumpkin_parse_strategy.js
@@ -8,6 +8,7 @@ */ import {InputController} from '../input_controller.js'; +import {LocaleInfo} from '../locale_info.js'; import {InputTextViewMacro} from '../macros/input_text_view_macro.js'; import {ListCommandsMacro} from '../macros/list_commands_macro.js'; import {Macro} from '../macros/macro.js'; @@ -24,12 +25,11 @@ export class PumpkinParseStrategy extends ParseStrategy { /** * @param {!InputController} inputController - * @param {boolean} isRTLLocale - * @param {string} locale * @return {!Promise<!PumpkinParseStrategy>} */ - static async create(inputController, isRTLLocale, locale) { - const instance = new PumpkinParseStrategy(inputController, isRTLLocale); + static async create(inputController) { + const locale = LocaleInfo.locale; + const instance = new PumpkinParseStrategy(inputController); if (PumpkinAvailability.usePumpkin(locale)) { await instance.initPumpkin_(PumpkinAvailability.LOCALES[locale]); } @@ -39,11 +39,10 @@ /** * @param {!InputController} inputController - * @param {boolean} isRTLLocale * @private */ - constructor(inputController, isRTLLocale) { - super(inputController, isRTLLocale); + constructor(inputController) { + super(inputController); /** @private {speech.pumpkin.api.js.PumpkinTagger.PumpkinTagger} */ this.pumpkinTagger_ = null; @@ -180,11 +179,9 @@ case MacroName.DELETE_PREV_CHAR: return new RepeatableKeyPressMacro.DeletePreviousCharacterMacro(repeat); case MacroName.NAV_PREV_CHAR: - return new RepeatableKeyPressMacro.NavPreviousCharMacro( - this.getIsRTLLocale(), repeat); + return new RepeatableKeyPressMacro.NavPreviousCharMacro(repeat); case MacroName.NAV_NEXT_CHAR: - return new RepeatableKeyPressMacro.NavNextCharMacro( - this.getIsRTLLocale(), repeat); + return new RepeatableKeyPressMacro.NavNextCharMacro(repeat); case MacroName.NAV_PREV_LINE: return new RepeatableKeyPressMacro.NavPreviousLineMacro(repeat); case MacroName.NAV_NEXT_LINE: @@ -202,8 +199,7 @@ case MacroName.SELECT_ALL_TEXT: return new RepeatableKeyPressMacro.SelectAllTextMacro(); case MacroName.UNSELECT_TEXT: - return new RepeatableKeyPressMacro.UnselectTextMacro( - this.getIsRTLLocale()); + return new RepeatableKeyPressMacro.UnselectTextMacro(); case MacroName.LIST_COMMANDS: return new ListCommandsMacro(); default:
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/parse/simple_parse_strategy.js b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/parse/simple_parse_strategy.js index d43a05e..8515fd84 100644 --- a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/parse/simple_parse_strategy.js +++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/parse/simple_parse_strategy.js
@@ -8,6 +8,7 @@ */ import {InputController} from '../input_controller.js'; +import {LocaleInfo} from '../locale_info.js'; import {DeletePrevSentMacro} from '../macros/delete_prev_sent_macro.js'; import {HiddenMacroManager} from '../macros/hidden_macro_manager.js'; import {InputTextViewMacro, NewLineMacro} from '../macros/input_text_view_macro.js'; @@ -39,9 +40,8 @@ /** * @param {!MacroName} macroName * @param {!InputController} inputController - * @param {boolean} isRTLLocale */ - constructor(macroName, inputController, isRTLLocale) { + constructor(macroName, inputController) { if (!SimpleMacroFactory.getData_()[macroName]) { throw new Error( 'Macro is not supported by SimpleMacroFactory: ' + macroName); @@ -51,8 +51,6 @@ this.macroName_ = macroName; /** @private {!InputController} */ this.inputController_ = inputController; - /** @private {boolean} */ - this.isRTLLocale_ = isRTLLocale; /** @private {RegExp} */ this.commandRegex_ = null; @@ -100,25 +98,16 @@ const initialArgs = []; switch (this.macroName_) { - case MacroName.NAV_PREV_CHAR: - case MacroName.NAV_NEXT_CHAR: - case MacroName.UNSELECT_TEXT: - case MacroName.NAV_NEXT_WORD: - case MacroName.NAV_PREV_WORD: - initialArgs.push(this.isRTLLocale_); - break; case MacroName.NEW_LINE: case MacroName.DELETE_PREV_SENT: + case MacroName.NAV_NEXT_SENT: + case MacroName.NAV_PREV_SENT: case MacroName.SMART_DELETE_PHRASE: case MacroName.SMART_REPLACE_PHRASE: case MacroName.SMART_INSERT_BEFORE: case MacroName.SMART_SELECT_BTWN_INCL: initialArgs.push(this.inputController_); break; - case MacroName.NAV_NEXT_SENT: - case MacroName.NAV_PREV_SENT: - initialArgs.push(this.inputController_, this.isRTLLocale_); - break; } const result = this.commandRegex_.exec(text); @@ -128,7 +117,12 @@ const extractedArgs = result.slice(1); const finalArgs = initialArgs.concat(extractedArgs); const data = SimpleMacroFactory.getData_(); - return new data[this.macroName_].build(...finalArgs); + const macro = new data[this.macroName_].build(...finalArgs); + if (macro.isSmart() && !LocaleInfo.allowSmartEditing()) { + return null; + } + + return macro; } /** @@ -244,12 +238,9 @@ /** A parsing strategy that utilizes SimpleMacroFactory. */ export class SimpleParseStrategy extends ParseStrategy { - /** - * @param {!InputController} inputController - * @param {boolean} isRTLLocale - */ - constructor(inputController, isRTLLocale) { - super(inputController, isRTLLocale); + /** @param {!InputController} inputController */ + constructor(inputController) { + super(inputController); /** * Map of macro names to a factory for that macro. @@ -271,9 +262,7 @@ } this.macroFactoryMap_.set( - name, - new SimpleMacroFactory( - name, this.getInputController(), this.getIsRTLLocale())); + name, new SimpleMacroFactory(name, this.getInputController())); } } @@ -300,8 +289,7 @@ macros[1] : macros[0]; } else if (macros.length > 2) { - // Unexpected, log a warning. This will cause tests to fail. - console.warn('Unexpected ambiguous macros found for text: ${text}.'); + console.warn(`Unexpected ambiguous macros found for text: ${text}.`); return macros[0]; }
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/parse/speech_parser.js b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/parse/speech_parser.js index ad6b36d..9b61d75 100644 --- a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/parse/speech_parser.js +++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/parse/speech_parser.js
@@ -7,6 +7,7 @@ */ import {InputController} from '../input_controller.js'; +import {LocaleInfo} from '../locale_info.js'; import {Macro} from '../macros/macro.js'; import {InputTextStrategy} from './input_text_strategy.js'; @@ -18,9 +19,6 @@ export class SpeechParser { /** @param {!InputController} inputController to interact with the IME. */ constructor(inputController) { - /** @private {boolean} */ - this.isRTLLocale_ = false; - /** @private {!InputController} */ this.inputController_ = inputController; @@ -34,25 +32,18 @@ this.pumpkinParseStrategy_ = null; } - /** - * @param {string} locale The Dictation recognition locale. Only some locales - * are supported by Pumpkin. - * @return {!Promise} - */ - async initialize(locale, commandsSupported) { - this.isRTLLocale_ = SpeechParser.RTLLocales.has(locale); - - if (!commandsSupported) { + /** @return {!Promise} */ + async refresh() { + if (!LocaleInfo.areCommandsSupported()) { this.simpleParseStrategy_ = null; this.pumpkinParseStrategy_ = null; return; } - // Initialize additional parsing strategies. - this.simpleParseStrategy_ = - new SimpleParseStrategy(this.inputController_, this.isRTLLocale_); - this.pumpkinParseStrategy_ = await PumpkinParseStrategy.create( - this.inputController_, this.isRTLLocale_, locale); + // Initialize additional parsing strategies. + this.simpleParseStrategy_ = new SimpleParseStrategy(this.inputController_); + this.pumpkinParseStrategy_ = + await PumpkinParseStrategy.create(this.inputController_); } /** @@ -80,43 +71,4 @@ return await /** @type {!Promise<!Macro>} */ ( this.inputTextStrategy_.parse(text)); } - - /** - * Determines whether commands are supported for this Dictation language - * and UI system language. - * @param {string} locale The Dictation locale code, like 'en-US'. - * @param {string} systemLocale The system language, may be like 'en' or - * 'en-US'. - * @return boolean Whether commands are supported. - */ - static areCommandsSupported(locale, systemLocale) { - // Currently Dictation cannot support commands when the UI language - // doesn't match the Dictation language. See crbug.com/1340590. - locale = locale.toLowerCase(); - systemLocale = systemLocale.toLowerCase(); - const uiLanguage = systemLocale.split('-')[0]; - if (uiLanguage !== (locale.split('-')[0])) { - if (SpeechParser.LocaleToUILanguagesMap.has(locale) && - (SpeechParser.LocaleToUILanguagesMap.get(locale) === uiLanguage || - SpeechParser.LocaleToUILanguagesMap.get(locale) === systemLocale)) { - return true; - } - return false; - } - return true; - } } - -// All RTL locales from Dictation::GetAllSupportedLocales. -SpeechParser.RTLLocales = new Set([ - 'ar-AE', 'ar-BH', 'ar-DZ', 'ar-EG', 'ar-IL', 'ar-IQ', 'ar-JO', - 'ar-KW', 'ar-LB', 'ar-MA', 'ar-OM', 'ar-PS', 'ar-QA', 'ar-SA', - 'ar-TN', 'ar-YE', 'fa-IR', 'iw-IL', 'ur-IN', 'ur-PK', -]); - -// Hebrew in Dictation is 'iw-IL' but 'he' in UI languages. -// yue-Hant-HK can map to 'zh-TW' because both are written as traditional -// Chinese. check this Norwegian in Dictation is 'no-NO' but 'nb' in UI -// languages. -SpeechParser.LocaleToUILanguagesMap = - new Map([['iw-il', 'he'], ['yue-hant-hk', 'zh-tw'], ['no-no', 'nb']]);
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/ui_controller.js b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/ui_controller.js index 654bdf8..e78f744b 100644 --- a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/ui_controller.js +++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/ui_controller.js
@@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import {LocaleInfo} from './locale_info.js'; + const HintType = chrome.accessibilityPrivate.DictationBubbleHintType; const IconType = chrome.accessibilityPrivate.DictationBubbleIconType; @@ -36,18 +38,6 @@ constructor() { /** @private {?number} */ this.showHintsTimeoutId_ = null; - - /** @private {boolean} */ - this.hintsSupported_ = true; - } - - /** - * Sets whether hints are supported. If hints are not - * supported, they will not be shown. - * @param {boolean} supported - */ - setHintsSupported(supported) { - this.hintsSupported_ = supported; } /** @@ -85,7 +75,8 @@ break; } - if (!context || !this.hintsSupported_) { + if (!context || !LocaleInfo.areCommandsSupported()) { + // Do not show hints if commands are not supported. return; }
diff --git a/chrome/browser/resources/chromeos/login/test_api/test_api.js b/chrome/browser/resources/chromeos/login/test_api/test_api.js index 85a87cec..c83bbed 100644 --- a/chrome/browser/resources/chromeos/login/test_api/test_api.js +++ b/chrome/browser/resources/chromeos/login/test_api/test_api.js
@@ -161,6 +161,8 @@ class WelcomeScreenTester extends ScreenElementApi { constructor() { super('connect'); + this.demoModeConfirmationDialog = + new PolymerElementApi(this, '#demoModeConfirmationDialog'); } /** @override */ @@ -173,6 +175,9 @@ assert(this.nextButton); this.nextButton.click(); } + getDemoModeOkButtonName() { + return loadTimeData.getString('enableDemoModeDialogConfirm'); + } } class NetworkScreenTester extends ScreenElementApi { @@ -542,6 +547,29 @@ } } +class DemoPreferencesScreenTester extends ScreenElementApi { + constructor() { + super('demo-preferences'); + } + + getDemoPreferencesNextButtonName() { + return loadTimeData.getString('demoPreferencesNextButtonLabel'); + } +} + +class ArcTosScreenTester extends ScreenElementApi { + constructor() { + super('arc-tos'); + } + + // Note that the Accept Button text key is different depending on whether + // the device in Demo Mode setup. Key for non-demo setup is + // "arcTermsOfServiceAcceptButton" + getArcTosDemoModeAcceptButtonName() { + return loadTimeData.getString('arcTermsOfServiceAcceptAndContinueButton'); + } +} + class OobeApiProvider { constructor() { this.screens = { @@ -563,6 +591,8 @@ GuestTosScreen: new GuestTosScreenTester(), ErrorScreen: new ErrorScreenTester(), OfflineLoginScreen: new OfflineLoginScreenTester(), + DemoPreferencesScreen: new DemoPreferencesScreenTester(), + ArcTosScreen: new ArcTosScreenTester(), }; this.loginWithPin = function(username, pin) {
diff --git a/chrome/browser/safe_browsing/BUILD.gn b/chrome/browser/safe_browsing/BUILD.gn index 9d43a724..b047622 100644 --- a/chrome/browser/safe_browsing/BUILD.gn +++ b/chrome/browser/safe_browsing/BUILD.gn
@@ -2,6 +2,7 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. +import("//build/config/chromeos/ui_mode.gni") import("//components/safe_browsing/buildflags.gni") import("//extensions/buildflags/buildflags.gni") @@ -389,6 +390,9 @@ "//chrome/services/file_util/public/mojom", ] } + if (is_chromeos_ash) { + deps += [ "//ash/constants:constants" ] + } } else if (safe_browsing_mode == 2) { if (is_android) { sources += [
diff --git a/chrome/browser/safe_browsing/tailored_security/chrome_tailored_security_service_unittest.cc b/chrome/browser/safe_browsing/tailored_security/chrome_tailored_security_service_unittest.cc index f0d9a8f..5a3403f 100644 --- a/chrome/browser/safe_browsing/tailored_security/chrome_tailored_security_service_unittest.cc +++ b/chrome/browser/safe_browsing/tailored_security/chrome_tailored_security_service_unittest.cc
@@ -39,7 +39,7 @@ namespace safe_browsing { -#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_CHROMEOS_ASH) +#if !BUILDFLAG(IS_ANDROID) // Names for Tailored Security status to make the test cases clearer. const bool kTailoredSecurityEnabled = true; const bool kTailoredSecurityDisabled = false; @@ -203,10 +203,7 @@ chrome_tailored_security_service_; }; -#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_CHROMEOS_ASH) -// TODO(crbug/1344604): Tests will fail until we add `catalog_name` to notifier -// id creation. - +#if !BUILDFLAG(IS_ANDROID) // Some of the test names are shorted using "Ts" for Tailored Security, "Ep" // for Enhanced Protection and "Sb" for Safe Browsing.
diff --git a/chrome/browser/safe_browsing/tailored_security/notification_handler_desktop.cc b/chrome/browser/safe_browsing/tailored_security/notification_handler_desktop.cc index 2a27e61..af2214c 100644 --- a/chrome/browser/safe_browsing/tailored_security/notification_handler_desktop.cc +++ b/chrome/browser/safe_browsing/tailored_security/notification_handler_desktop.cc
@@ -32,6 +32,10 @@ #include "ui/native_theme/common_theme.h" #include "ui/native_theme/native_theme.h" +#if BUILDFLAG(IS_CHROMEOS_ASH) +#include "ash/constants/notifier_catalogs.h" +#endif + namespace safe_browsing { namespace { @@ -73,11 +77,33 @@ outcome); } +#if BUILDFLAG(IS_CHROMEOS_ASH) + +message_center::NotifierId GetDisabledNotifierId() { + return message_center::NotifierId( + message_center::NotifierType::SYSTEM_COMPONENT, + kTailoredSecurityNotifierId, + ash::NotificationCatalogName::kTailoredSecurityDisabled); +} +message_center::NotifierId GetEnabledNotifierId() { + return message_center::NotifierId( + message_center::NotifierType::SYSTEM_COMPONENT, + kTailoredSecurityNotifierId, + ash::NotificationCatalogName::kTailoredSecurityEnabled); +} +message_center::NotifierId GetPromotionNotifierId() { + return message_center::NotifierId( + message_center::NotifierType::SYSTEM_COMPONENT, + kTailoredSecurityNotifierId, + ash::NotificationCatalogName::kTailoredSecurityPromotion); +} +#else message_center::NotifierId GetNotifierId() { return message_center::NotifierId( message_center::NotifierType::SYSTEM_COMPONENT, kTailoredSecurityNotifierId); } +#endif } // namespace @@ -152,6 +178,12 @@ std::u16string title, description, primary_button, secondary_button; ui::ImageModel icon; std::string notification_id; +#if BUILDFLAG(IS_CHROMEOS_ASH) + const message_center::NotifierId notifier_id = + enable ? GetEnabledNotifierId() : GetDisabledNotifierId(); +#else + const message_center::NotifierId notifier_id = GetNotifierId(); +#endif if (enable) { notification_id = kTailoredSecurityEnableNotificationId; title = l10n_util::GetStringUTF16( @@ -184,7 +216,7 @@ message_center::NOTIFICATION_TYPE_SIMPLE, notification_id, title, description, icon, l10n_util::GetStringUTF16(IDS_TAILORED_SECURITY_DISPLAY_SOURCE), - GURL(kTailoredSecurityNotificationOrigin), GetNotifierId(), + GURL(kTailoredSecurityNotificationOrigin), notifier_id, message_center::RichNotificationData(), /*delegate=*/nullptr); notification.set_buttons({message_center::ButtonInfo(primary_button), @@ -205,6 +237,12 @@ IDS_TAILORED_SECURITY_UNCONSENTED_PROMOTION_NOTIFICATION_ACCEPT); const std::u16string& secondary_button = l10n_util::GetStringUTF16(IDS_NO_THANKS); +#if BUILDFLAG(IS_CHROMEOS_ASH) + const message_center::NotifierId notifier_id = GetPromotionNotifierId(); +#else + const message_center::NotifierId notifier_id = GetNotifierId(); +#endif + // TODO(crbug/1257622): Confirm with UX that it's appropriate to use the // blue color here. auto icon = @@ -215,7 +253,7 @@ message_center::NOTIFICATION_TYPE_SIMPLE, notification_id, title, description, icon, l10n_util::GetStringUTF16(IDS_TAILORED_SECURITY_DISPLAY_SOURCE), - GURL(kTailoredSecurityNotificationOrigin), GetNotifierId(), + GURL(kTailoredSecurityNotificationOrigin), notifier_id, message_center::RichNotificationData(), /*delegate=*/nullptr); notification.set_buttons({message_center::ButtonInfo(primary_button),
diff --git a/chrome/browser/safety_check/android/java/src/org/chromium/chrome/browser/safety_check/SafetyCheckMediator.java b/chrome/browser/safety_check/android/java/src/org/chromium/chrome/browser/safety_check/SafetyCheckMediator.java index 5cb94be..2792292c 100644 --- a/chrome/browser/safety_check/android/java/src/org/chromium/chrome/browser/safety_check/SafetyCheckMediator.java +++ b/chrome/browser/safety_check/android/java/src/org/chromium/chrome/browser/safety_check/SafetyCheckMediator.java
@@ -26,8 +26,11 @@ import org.chromium.chrome.browser.password_check.PasswordCheck; import org.chromium.chrome.browser.password_check.PasswordCheckFactory; import org.chromium.chrome.browser.password_check.PasswordCheckUIStatus; +import org.chromium.chrome.browser.password_manager.CredentialManagerLauncher.CredentialManagerError; import org.chromium.chrome.browser.password_manager.ManagePasswordsReferrer; import org.chromium.chrome.browser.password_manager.PasswordCheckReferrer; +import org.chromium.chrome.browser.password_manager.PasswordCheckupClientHelper.PasswordCheckBackendException; +import org.chromium.chrome.browser.password_manager.PasswordManagerBackendSupportHelper; import org.chromium.chrome.browser.password_manager.PasswordManagerHelper; import org.chromium.chrome.browser.password_manager.PasswordStoreBridge; import org.chromium.chrome.browser.password_manager.PasswordStoreCredential; @@ -245,6 +248,17 @@ mShowSafePasswordState = false; mModel.set(SafetyCheckProperties.SAFE_BROWSING_STATE, SafeBrowsingState.UNCHECKED); mModel.set(SafetyCheckProperties.UPDATES_STATE, UpdatesState.UNCHECKED); + + // If the new Password Manager backend is out of date, attempting to fetch breached + // credentials will expectedly fail and display an error message. This error is + // designed to be only shown when user explicitly runs the check (or it was ran + // recently). For this case, breached credential fetch is skipped. + if (PasswordManagerHelper.canUseUpm() + && PasswordManagerBackendSupportHelper.getInstance().isUpdateNeeded()) { + mLoadStage = PasswordCheckLoadStage.IDLE; + mModel.set(SafetyCheckProperties.PASSWORDS_STATE, PasswordsState.UNCHECKED); + return; + } } mModel.set(SafetyCheckProperties.PASSWORDS_STATE, PasswordsState.CHECKING); mLoadStage = PasswordCheckLoadStage.INITIAL_WAIT_FOR_LOAD; @@ -257,6 +271,7 @@ PasswordsStatus.SIGNED_OUT, PasswordsStatus.MAX_VALUE + 1); updatePasswordElementClickDestination(); } + fetchPasswordsAndBreachedCredentials(); if (mPasswordsLoaded && mLeaksLoaded) { determinePasswordStateOnLoadComplete(); @@ -543,6 +558,11 @@ } return true; }; + } else if (state == PasswordsState.BACKEND_VERSION_NOT_SUPPORTED) { + listener = (p) -> { + PasswordManagerHelper.launchGmsUpdate(p.getContext()); + return true; + }; } else { listener = (p) -> { PasswordManagerHelper.showPasswordSettings(p.getContext(), @@ -668,13 +688,21 @@ if (mModel == null) return; setRunnablePasswords(() -> { - if (mModel != null) { - RecordHistogram.recordEnumeratedHistogram("Settings.SafetyCheck.PasswordsResult", - SafetyCheckProperties.passwordsStateToNative(PasswordsState.ERROR), - PasswordsStatus.MAX_VALUE + 1); + if (mModel == null) return; + + RecordHistogram.recordEnumeratedHistogram("Settings.SafetyCheck.PasswordsResult", + SafetyCheckProperties.passwordsStateToNative(PasswordsState.ERROR), + PasswordsStatus.MAX_VALUE + 1); + if (error instanceof PasswordCheckBackendException + && ((PasswordCheckBackendException) error).errorCode + == CredentialManagerError.BACKEND_VERSION_NOT_SUPPORTED) { + mModel.set(SafetyCheckProperties.PASSWORDS_STATE, + PasswordsState.BACKEND_VERSION_NOT_SUPPORTED); + } else { mModel.set(SafetyCheckProperties.PASSWORDS_STATE, PasswordsState.ERROR); - updatePasswordElementClickDestination(); } + + updatePasswordElementClickDestination(); }); }
diff --git a/chrome/browser/safety_check/android/java/src/org/chromium/chrome/browser/safety_check/SafetyCheckProperties.java b/chrome/browser/safety_check/android/java/src/org/chromium/chrome/browser/safety_check/SafetyCheckProperties.java index cca700e..339841a 100644 --- a/chrome/browser/safety_check/android/java/src/org/chromium/chrome/browser/safety_check/SafetyCheckProperties.java +++ b/chrome/browser/safety_check/android/java/src/org/chromium/chrome/browser/safety_check/SafetyCheckProperties.java
@@ -41,7 +41,8 @@ @IntDef({PasswordsState.UNCHECKED, PasswordsState.CHECKING, PasswordsState.SAFE, PasswordsState.COMPROMISED_EXIST, PasswordsState.OFFLINE, PasswordsState.NO_PASSWORDS, - PasswordsState.SIGNED_OUT, PasswordsState.QUOTA_LIMIT, PasswordsState.ERROR}) + PasswordsState.SIGNED_OUT, PasswordsState.QUOTA_LIMIT, PasswordsState.ERROR, + PasswordsState.BACKEND_VERSION_NOT_SUPPORTED}) @Retention(RetentionPolicy.SOURCE) public @interface PasswordsState { int UNCHECKED = 0; @@ -53,6 +54,7 @@ int SIGNED_OUT = 6; int QUOTA_LIMIT = 7; int ERROR = 8; + int BACKEND_VERSION_NOT_SUPPORTED = 9; } static @PasswordsState int passwordsStatefromErrorState(@PasswordCheckUIStatus int state) { @@ -82,6 +84,11 @@ // This is not used. assert false : "PasswordsState.UNCHECKED has no native equivalent."; return PasswordsStatus.ERROR; + case PasswordsState.BACKEND_VERSION_NOT_SUPPORTED: + // This is not used. + assert false + : "PasswordsState.BACKEND_VERSION_NOT_SUPPORTED has no native equivalent."; + return PasswordsStatus.ERROR; case PasswordsState.CHECKING: return PasswordsStatus.CHECKING; case PasswordsState.SAFE:
diff --git a/chrome/browser/safety_check/android/java/src/org/chromium/chrome/browser/safety_check/SafetyCheckViewBinder.java b/chrome/browser/safety_check/android/java/src/org/chromium/chrome/browser/safety_check/SafetyCheckViewBinder.java index da41040..01b6f97 100644 --- a/chrome/browser/safety_check/android/java/src/org/chromium/chrome/browser/safety_check/SafetyCheckViewBinder.java +++ b/chrome/browser/safety_check/android/java/src/org/chromium/chrome/browser/safety_check/SafetyCheckViewBinder.java
@@ -47,6 +47,8 @@ return context.getResources().getQuantityString( R.plurals.safety_check_passwords_compromised_exist, compromised, compromised); + case PasswordsState.BACKEND_VERSION_NOT_SUPPORTED: + return context.getString(R.string.safety_check_passwords_update_play_services); default: assert false : "Unknown PasswordsState value."; } @@ -68,6 +70,7 @@ case PasswordsState.QUOTA_LIMIT: case PasswordsState.OFFLINE: case PasswordsState.ERROR: + case PasswordsState.BACKEND_VERSION_NOT_SUPPORTED: return R.drawable.ic_info_outline_grey_24dp; default: assert false : "Unknown PasswordsState value.";
diff --git a/chrome/browser/safety_check/android/javatests/src/org/chromium/chrome/browser/safety_check/SafetyCheckMediatorTest.java b/chrome/browser/safety_check/android/javatests/src/org/chromium/chrome/browser/safety_check/SafetyCheckMediatorTest.java index 7336570..c2c0273 100644 --- a/chrome/browser/safety_check/android/javatests/src/org/chromium/chrome/browser/safety_check/SafetyCheckMediatorTest.java +++ b/chrome/browser/safety_check/android/javatests/src/org/chromium/chrome/browser/safety_check/SafetyCheckMediatorTest.java
@@ -47,6 +47,7 @@ import org.chromium.chrome.browser.password_check.PasswordCheck; import org.chromium.chrome.browser.password_check.PasswordCheckFactory; import org.chromium.chrome.browser.password_check.PasswordCheckUIStatus; +import org.chromium.chrome.browser.password_manager.CredentialManagerLauncher.CredentialManagerError; import org.chromium.chrome.browser.password_manager.PasswordCheckupClientHelper; import org.chromium.chrome.browser.password_manager.PasswordCheckupClientHelper.PasswordCheckBackendException; import org.chromium.chrome.browser.password_manager.PasswordCheckupClientHelperFactory; @@ -113,6 +114,8 @@ private Handler mHandler; @Mock private PasswordCheck mPasswordCheck; + + // TODO(crbug.com/1346235): Use existing fake instead of mocking @Mock private PasswordCheckupClientHelper mPasswordCheckupHelper; @Mock @@ -121,6 +124,8 @@ private PrefService mPrefService; @Mock private UserPrefs.Natives mUserPrefsJniMock; + + // TODO(crbug.com/1346235): Use fake instead of mocking @Mock private PasswordManagerBackendSupportHelper mBackendSupportHelperMock; @@ -279,6 +284,7 @@ mModel = SafetyCheckProperties.createSafetyCheckModel(); if (mUseNewApi) { + // TODO(crbug.com/1346235): Use existing fake instead of mocking PasswordCheckupClientHelperFactory mockPasswordCheckFactory = mock(PasswordCheckupClientHelperFactory.class); when(mockPasswordCheckFactory.createHelper()).thenReturn(mPasswordCheckupHelper); @@ -388,6 +394,19 @@ } @Test + public void testPasswordsCheckBackendOutdated() { + if (!mUseNewApi) return; + captureRunPasswordCheckCallback(); + mMediator.performSafetyCheck(); + mRunPasswordCheckFailedCallback.onResult(new PasswordCheckBackendException( + "test", CredentialManagerError.BACKEND_VERSION_NOT_SUPPORTED)); + assertEquals(PasswordsState.BACKEND_VERSION_NOT_SUPPORTED, mModel.get(PASSWORDS_STATE)); + assertEquals(1, + RecordHistogram.getHistogramValueCountForTesting( + SAFETY_CHECK_PASSWORDS_RESULT_HISTOGRAM, PasswordsStatus.ERROR)); + } + + @Test public void testPasswordsCheckNoPasswords() { captureRunPasswordCheckCallback(); mMediator.performSafetyCheck();
diff --git a/chrome/browser/search_resumption/BUILD.gn b/chrome/browser/search_resumption/BUILD.gn index e5608aa2..79d81e1 100644 --- a/chrome/browser/search_resumption/BUILD.gn +++ b/chrome/browser/search_resumption/BUILD.gn
@@ -97,5 +97,6 @@ "//third_party/mockito:mockito_java", "//ui/android:ui_no_recycler_view_java", "//url:gurl_java", + "//url:gurl_junit_test_support", ] }
diff --git a/chrome/browser/search_resumption/java/src/org/chromium/chrome/browser/search_resumption/SearchResumptionModuleCoordinator.java b/chrome/browser/search_resumption/java/src/org/chromium/chrome/browser/search_resumption/SearchResumptionModuleCoordinator.java index 9c8b6db..58ac8fb 100644 --- a/chrome/browser/search_resumption/java/src/org/chromium/chrome/browser/search_resumption/SearchResumptionModuleCoordinator.java +++ b/chrome/browser/search_resumption/java/src/org/chromium/chrome/browser/search_resumption/SearchResumptionModuleCoordinator.java
@@ -22,8 +22,8 @@ public SearchResumptionModuleCoordinator(ViewGroup parent, Tab tabToTrack, Tab currentTab, Profile profile, int moduleContainerStbuId) { - OnSuggestionClickCallback callback = tile -> { - currentTab.loadUrl(new LoadUrlParams(tile.getUrl())); + OnSuggestionClickCallback callback = (gurl) -> { + currentTab.loadUrl(new LoadUrlParams(gurl)); RecordUserAction.record(SearchResumptionModuleMediator.ACTION_CLICK); }; mTileBuilder = new SearchResumptionTileBuilder(callback);
diff --git a/chrome/browser/search_resumption/java/src/org/chromium/chrome/browser/search_resumption/SearchResumptionModuleMediator.java b/chrome/browser/search_resumption/java/src/org/chromium/chrome/browser/search_resumption/SearchResumptionModuleMediator.java index 7d3159e0..e72466aa 100644 --- a/chrome/browser/search_resumption/java/src/org/chromium/chrome/browser/search_resumption/SearchResumptionModuleMediator.java +++ b/chrome/browser/search_resumption/java/src/org/chromium/chrome/browser/search_resumption/SearchResumptionModuleMediator.java
@@ -26,6 +26,7 @@ import org.chromium.components.omnibox.AutocompleteResult; import org.chromium.ui.modelutil.PropertyModel; import org.chromium.ui.modelutil.PropertyModelChangeProcessor; +import org.chromium.url.GURL; import java.util.List; @@ -45,6 +46,7 @@ private PropertyModel mModel; private @Nullable SearchResumptionModuleView mModuleLayoutView; + private @Nullable SearchResumptionModuleBridge mSearchResumptionModuleBridge; SearchResumptionModuleMediator(ViewStub moduleStub, Tab tabToTrack, Profile profile, SearchResumptionTileBuilder tileBuilder) { @@ -78,22 +80,34 @@ } /** + * Called when the search suggestions are available using the new service API. + * @param suggestionTexts The display texts of the suggestions. + * @param suggestionUrls The URLs of the suggestions. + */ + void onSuggestionsAvailable(String[] suggestionTexts, GURL[] suggestionUrls) { + if (mModel != null || !shouldShowSuggestionModule(suggestionUrls, suggestionTexts)) { + return; + } + showSearchSuggestionModule(suggestionTexts, suggestionUrls); + } + + /** * Inflates the search_resumption_layout and shows the suggestions on the module. * @param autocompleteResult The suggestions to show on the module. */ void showSearchSuggestionModule(AutocompleteResult autocompleteResult) { - if (mModel != null) return; - - mModuleLayoutView = (SearchResumptionModuleView) mStub.inflate(); - mModel = new PropertyModel(SearchResumptionModuleProperties.ALL_KEYS); - PropertyModelChangeProcessor.create( - mModel, mModuleLayoutView, new SearchResumptionModuleViewBinder()); - + if (!initializeModule()) return; mTileBuilder.buildSuggestionTile(autocompleteResult.getSuggestionsList(), mModuleLayoutView.findViewById(R.id.search_resumption_module_tiles_container)); - mModel.set(SearchResumptionModuleProperties.EXPAND_COLLAPSE_CLICK_CALLBACK, - this::onExpandedOrCollapsed); - RecordUserAction.record(ACTION_SHOW); + } + + /** + * Inflates the search_resumption_layout and shows the suggestions on the module. + */ + void showSearchSuggestionModule(String[] texts, GURL[] urls) { + if (!initializeModule()) return; + mTileBuilder.buildSuggestionTile(texts, urls, + mModuleLayoutView.findViewById(R.id.search_resumption_module_tiles_container)); } void destroy() { @@ -103,6 +117,9 @@ if (mModuleLayoutView != null) { mModuleLayoutView.destroy(); } + if (mSearchResumptionModuleBridge != null) { + mSearchResumptionModuleBridge.destroy(); + } TemplateUrlServiceFactory.get().removeObserver(this::onTemplateURLServiceChanged); mSignInManager.removeSignInStateObserver(this); } @@ -120,7 +137,9 @@ mAutoComplete.startZeroSuggest("", mTabToTrackSuggestion.getUrl().getSpec(), pageClassification, mTabToTrackSuggestion.getTitle()); } else { - // TODO(hanxi): Switches to use a new KeyedService instead of Autocomplete API. + mSearchResumptionModuleBridge = new SearchResumptionModuleBridge(profile); + mSearchResumptionModuleBridge.fetchSuggestions( + mTabToTrackSuggestion.getUrl().getSpec(), this::onSuggestionsAvailable); } } @@ -156,6 +175,45 @@ return false; } + /** + * Returns whether to show the search resumption module. Only showing the module if at least + * {@link SearchResumptionTileBuilder#MAX_TILES_NUMBER} -1 suggestions are given. + */ + private boolean shouldShowSuggestionModule(GURL[] urls, String[] texts) { + if (urls.length != texts.length + || urls.length < SearchResumptionTileBuilder.MAX_TILES_NUMBER - 1) { + return false; + } + + int count = 0; + for (int i = 0; i < urls.length; i++) { + if (SearchResumptionTileBuilder.isSuggestionValid(texts[i])) { + count++; + } + if (count >= SearchResumptionTileBuilder.MAX_TILES_NUMBER - 1) { + return true; + } + } + return false; + } + + /** + * Inflates the module and initializes the property model. + * @return Whether the module is inflated. + */ + private boolean initializeModule() { + if (mModel != null) return false; + + mModuleLayoutView = (SearchResumptionModuleView) mStub.inflate(); + mModel = new PropertyModel(SearchResumptionModuleProperties.ALL_KEYS); + PropertyModelChangeProcessor.create( + mModel, mModuleLayoutView, new SearchResumptionModuleViewBinder()); + mModel.set(SearchResumptionModuleProperties.EXPAND_COLLAPSE_CLICK_CALLBACK, + this::onExpandedOrCollapsed); + RecordUserAction.record(ACTION_SHOW); + return true; + } + private void setVisibility(boolean isVisible) { if (mModel != null) { mModel.set(SearchResumptionModuleProperties.IS_VISIBLE, isVisible);
diff --git a/chrome/browser/search_resumption/java/src/org/chromium/chrome/browser/search_resumption/SearchResumptionTileBuilder.java b/chrome/browser/search_resumption/java/src/org/chromium/chrome/browser/search_resumption/SearchResumptionTileBuilder.java index 953c1a7a..6feca4a 100644 --- a/chrome/browser/search_resumption/java/src/org/chromium/chrome/browser/search_resumption/SearchResumptionTileBuilder.java +++ b/chrome/browser/search_resumption/java/src/org/chromium/chrome/browser/search_resumption/SearchResumptionTileBuilder.java
@@ -10,6 +10,7 @@ import org.chromium.base.TraceEvent; import org.chromium.chrome.browser.omnibox.OmniboxSuggestionType; import org.chromium.components.omnibox.AutocompleteMatch; +import org.chromium.url.GURL; import java.util.List; @@ -26,7 +27,7 @@ * The callback when a {@link SearchResumptionTileView} is clicked. */ interface OnSuggestionClickCallback { - void onSuggestionClick(AutocompleteMatch tile); + void onSuggestionClick(GURL gurl); } public SearchResumptionTileBuilder(OnSuggestionClickCallback callback) { @@ -38,11 +39,18 @@ * OmniboxSuggestionType.SEARCH_SUGGEST}. */ static boolean isSearchSuggestion(AutocompleteMatch suggestion) { - return !TextUtils.isEmpty(suggestion.getDisplayText()) + return isSuggestionValid(suggestion.getDisplayText()) && suggestion.getType() == OmniboxSuggestionType.SEARCH_SUGGEST; } /** + * Returns Whether the given suggestion is a qualified suggestion. + */ + static boolean isSuggestionValid(String text) { + return !TextUtils.isEmpty(text); + } + + /** * Iterators the suggestions and chooses the top MAX_TILES_NUMBER ones or less depending on the * number of available suggestions to build on the parent ViewGroup. */ @@ -75,12 +83,52 @@ } /** + * Iterators the suggestions and chooses the top MAX_TILES_NUMBER ones or less depending on the + * number of available suggestions to build on the parent ViewGroup. + */ + void buildSuggestionTile( + String[] texts, GURL[] urls, SearchResumptionTileContainerView parent) { + try (TraceEvent e = TraceEvent.scoped("SearchSuggestionTileProvider.addTileSection")) { + assert parent.getChildCount() == 0; + + int suggestionCount = urls.length; + int visibleTilesCount = Math.min(suggestionCount, MAX_TILES_NUMBER); + int tileIndex = 0; + int suggestionIndex = 0; + while (tileIndex < visibleTilesCount && suggestionIndex < urls.length) { + if (!isSuggestionValid(texts[suggestionIndex])) { + suggestionIndex++; + continue; + } + SearchResumptionTileView tileView = + buildTileView(texts[suggestionIndex], urls[suggestionIndex], parent); + parent.addView(tileView); + tileIndex++; + suggestionIndex++; + } + + int childSize = parent.getChildCount(); + for (int i = 0; i < childSize; i++) { + ((SearchResumptionTileView) parent.getChildAt(i)).mayUpdateBackground(i, childSize); + } + } + } + + /** * Builds a {@link SearchResumptionTileView} based on the given suggestion. */ SearchResumptionTileView buildTileView( AutocompleteMatch suggestion, SearchResumptionTileContainerView parent) { + return buildTileView(suggestion.getDisplayText(), suggestion.getUrl(), parent); + } + + /** + * Builds a {@link SearchResumptionTileView} based on the given suggestion. + */ + SearchResumptionTileView buildTileView( + String text, GURL url, SearchResumptionTileContainerView parent) { SearchResumptionTileView tileView = parent.buildTileView(); - tileView.updateSuggestionData(suggestion); + tileView.updateSuggestionData(url, text); tileView.addOnSuggestionClickCallback(mCallback); return tileView; }
diff --git a/chrome/browser/search_resumption/java/src/org/chromium/chrome/browser/search_resumption/SearchResumptionTileView.java b/chrome/browser/search_resumption/java/src/org/chromium/chrome/browser/search_resumption/SearchResumptionTileView.java index 649286a7..63a5cec 100644 --- a/chrome/browser/search_resumption/java/src/org/chromium/chrome/browser/search_resumption/SearchResumptionTileView.java +++ b/chrome/browser/search_resumption/java/src/org/chromium/chrome/browser/search_resumption/SearchResumptionTileView.java
@@ -13,13 +13,13 @@ import androidx.core.content.ContextCompat; import org.chromium.chrome.browser.search_resumption.SearchResumptionTileBuilder.OnSuggestionClickCallback; -import org.chromium.components.omnibox.AutocompleteMatch; +import org.chromium.url.GURL; /** * The view for a search suggestion tile. */ public class SearchResumptionTileView extends RelativeLayout { - private AutocompleteMatch mSearchTile; + private GURL mGurl; private TextView mTileContent; public SearchResumptionTileView(Context context, @Nullable AttributeSet attrs) { @@ -35,14 +35,14 @@ /** * Updates the content of the tile. */ - void updateSuggestionData(AutocompleteMatch suggestion) { - mSearchTile = suggestion; - mTileContent.setText(suggestion.getDisplayText()); + void updateSuggestionData(GURL gUrl, String displayText) { + mGurl = gUrl; + mTileContent.setText(displayText); setContentDescription(mTileContent.getText()); } void addOnSuggestionClickCallback(OnSuggestionClickCallback callback) { - setOnClickListener(v -> callback.onSuggestionClick(mSearchTile)); + setOnClickListener(v -> callback.onSuggestionClick(mGurl)); } /**
diff --git a/chrome/browser/search_resumption/junit/src/org/chromium/chrome/browser/search_resumption/SearchResumptionModuleMediatorUnitTest.java b/chrome/browser/search_resumption/junit/src/org/chromium/chrome/browser/search_resumption/SearchResumptionModuleMediatorUnitTest.java index 9c977162..fe46238 100644 --- a/chrome/browser/search_resumption/junit/src/org/chromium/chrome/browser/search_resumption/SearchResumptionModuleMediatorUnitTest.java +++ b/chrome/browser/search_resumption/junit/src/org/chromium/chrome/browser/search_resumption/SearchResumptionModuleMediatorUnitTest.java
@@ -197,6 +197,26 @@ @Test @MediumTest + public void testDoNotBuildModuleWithoutEnoughSuggestions_newServiceAPI() { + String[] texts = {"suggestion 1"}; + GURL[] gUrls = {createMockGurl("foo.com")}; + + mMediator.onSuggestionsAvailable(texts, gUrls); + verify(mParent, times(0)).inflate(); + } + @Test + @MediumTest + public void testShowModuleWithEnoughResults_newServiceAPI() { + String[] texts = {"suggestion 1", "suggestion2"}; + GURL[] gUrls = {createMockGurl("foo.com"), createMockGurl("bar.com")}; + + mMediator.onSuggestionsAvailable(texts, gUrls); + verify(mParent, times(1)).inflate(); + Assert.assertEquals(View.VISIBLE, mSuggestionTilesContainerView.getVisibility()); + } + + @Test + @MediumTest public void testModuleVisibility() { testShowModuleWithEnoughResults(); mMediator.onSignedOut();
diff --git a/chrome/browser/search_resumption/junit/src/org/chromium/chrome/browser/search_resumption/SearchResumptionModuleViewUnitTest.java b/chrome/browser/search_resumption/junit/src/org/chromium/chrome/browser/search_resumption/SearchResumptionModuleViewUnitTest.java index 85801671..a2c481b 100644 --- a/chrome/browser/search_resumption/junit/src/org/chromium/chrome/browser/search_resumption/SearchResumptionModuleViewUnitTest.java +++ b/chrome/browser/search_resumption/junit/src/org/chromium/chrome/browser/search_resumption/SearchResumptionModuleViewUnitTest.java
@@ -4,7 +4,6 @@ package org.chromium.chrome.browser.search_resumption; -import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -29,9 +28,10 @@ import org.chromium.chrome.browser.preferences.ChromePreferenceKeys; import org.chromium.chrome.browser.preferences.SharedPreferencesManager; import org.chromium.chrome.browser.search_resumption.SearchResumptionTileBuilder.OnSuggestionClickCallback; -import org.chromium.components.omnibox.AutocompleteMatch; import org.chromium.ui.modelutil.PropertyModel; import org.chromium.ui.modelutil.PropertyModelChangeProcessor; +import org.chromium.url.GURL; +import org.chromium.url.JUnitTestGURLs; /** Tests for {@link SearchResumptionModuleView}. */ @RunWith(BaseRobolectricTestRunner.class) @@ -138,10 +138,9 @@ public void testTileView() { SearchResumptionTileView tileView = inflateTileView(); String text = "foo"; - AutocompleteMatch match = Mockito.mock(AutocompleteMatch.class); - doReturn(text).when(match).getDisplayText(); + GURL gUrl = JUnitTestGURLs.getGURL(JUnitTestGURLs.EXAMPLE_URL); - tileView.updateSuggestionData(match); + tileView.updateSuggestionData(gUrl, text); Assert.assertEquals(text, tileView.getTextForTesting()); Assert.assertEquals(text, tileView.getContentDescription());
diff --git a/chrome/browser/search_resumption/junit/src/org/chromium/chrome/browser/search_resumption/SearchResumptionTileBuilderUnitTest.java b/chrome/browser/search_resumption/junit/src/org/chromium/chrome/browser/search_resumption/SearchResumptionTileBuilderUnitTest.java index d35f3438..ddb11d9 100644 --- a/chrome/browser/search_resumption/junit/src/org/chromium/chrome/browser/search_resumption/SearchResumptionTileBuilderUnitTest.java +++ b/chrome/browser/search_resumption/junit/src/org/chromium/chrome/browser/search_resumption/SearchResumptionTileBuilderUnitTest.java
@@ -118,8 +118,8 @@ } private void createTileBuilder() { - OnSuggestionClickCallback callback = tile -> { - mTab.loadUrl(new LoadUrlParams(tile.getUrl())); + OnSuggestionClickCallback callback = (gUrl) -> { + mTab.loadUrl(new LoadUrlParams(gUrl)); RecordUserAction.record(SearchResumptionModuleMediator.ACTION_CLICK); }; mTileBuilder = new SearchResumptionTileBuilder(callback);
diff --git a/chrome/browser/ui/android/autofill/autofill_popup_view_android.cc b/chrome/browser/ui/android/autofill/autofill_popup_view_android.cc index 7633478..b2762ad 100644 --- a/chrome/browser/ui/android/autofill/autofill_popup_view_android.cc +++ b/chrome/browser/ui/android/autofill/autofill_popup_view_android.cc
@@ -107,7 +107,7 @@ base::android::ConvertUTF16ToJavaString(env, suggestion.offer_label); Java_AutofillPopupBridge_addToAutofillSuggestionArray( env, data_array, i, value, label, item_tag, android_icon_id, - /*icon_at_start=*/false, suggestion.frontend_id, is_deletable, + suggestion.is_icon_at_start, suggestion.frontend_id, is_deletable, is_label_multiline, /*isLabelBold*/ false, url::GURLAndroid::FromNativeGURL(env, suggestion.custom_icon_url)); }
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings.grd b/chrome/browser/ui/android/strings/android_chrome_strings.grd index 809cac6..be6a443 100644 --- a/chrome/browser/ui/android/strings/android_chrome_strings.grd +++ b/chrome/browser/ui/android/strings/android_chrome_strings.grd
@@ -1447,6 +1447,9 @@ <message name="IDS_SAFETY_CHECK_PASSWORDS_ERROR_QUOTA_LIMIT" desc="Text to display when the password check hits the daily quota limit."> Chrome couldn’t check all passwords </message> + <message name="IDS_SAFETY_CHECK_PASSWORDS_UPDATE_PLAY_SERVICES" desc="Text to display when the password check is unable to run because Google Play services version is not supported."> + Update Google Play services to check your passwords + </message> <message name="IDS_SAFETY_CHECK_UPDATES_UPDATED" desc="Text to display when the updates check confirms that the latest version is already installed."> Chrome is up to date </message>
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_SAFETY_CHECK_PASSWORDS_UPDATE_PLAY_SERVICES.png.sha1 b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_SAFETY_CHECK_PASSWORDS_UPDATE_PLAY_SERVICES.png.sha1 new file mode 100644 index 0000000..1776f4a --- /dev/null +++ b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_SAFETY_CHECK_PASSWORDS_UPDATE_PLAY_SERVICES.png.sha1
@@ -0,0 +1 @@ +409c88ac939613e3995340b7d3b8518103009465 \ No newline at end of file
diff --git a/chrome/browser/ui/app_list/chrome_app_list_model_updater.cc b/chrome/browser/ui/app_list/chrome_app_list_model_updater.cc index f40a43ac..c2b7b8714 100644 --- a/chrome/browser/ui/app_list/chrome_app_list_model_updater.cc +++ b/chrome/browser/ui/app_list/chrome_app_list_model_updater.cc
@@ -365,15 +365,33 @@ const std::string& id, const gfx::ImageSkia& icon, const ash::IconColor& icon_color) { + if (icon.isNull()) + return; + + // TODO(https://crbug.com/1346386): it is awkward to check both `chrome_item` + // and `item`. Maybe remove one of them or both after investigation. ChromeAppListItem* chrome_item = FindItem(id); if (!chrome_item) return; + ash::AppListItem* item = model_.FindItem(id); + if (!item) + return; + base::AutoReset auto_reset(&item_with_icon_update_, chrome_item->id()); - std::unique_ptr<ash::AppListItemMetadata> data = chrome_item->CloneMetadata(); - data->icon = icon; - data->icon_color = icon_color; + const bool color_change = (icon_color != item->GetDefaultIconColor()); + + // Two similar icons may generate the same extracted icon color value. + // Therefore, always update the app list item icon. + item->SetDefaultIconAndColor(icon, icon_color); + + // Sync the icon color if the color changes. Note that the icon is not synced. + // Therefore, we only check whether the color changes here. + if (color_change) + OnAppListItemUpdated(item); + + std::unique_ptr<ash::AppListItemMetadata> data = item->CloneMetadata(); MaybeUpdatePositionWhenIconColorChange(data.get()); model_.SetItemMetadata(id, std::move(data));
diff --git a/chrome/browser/ui/ash/projector/projector_client_impl.cc b/chrome/browser/ui/ash/projector/projector_client_impl.cc index 10cb8cc2..952a2467 100644 --- a/chrome/browser/ui/ash/projector/projector_client_impl.cc +++ b/chrome/browser/ui/ash/projector/projector_client_impl.cc
@@ -295,7 +295,15 @@ if (!is_enabled) CloseProjectorApp(); - auto* web_app_provider = web_app::WebAppProvider::GetForWebApps(profile); - web_app_provider->sync_bridge().SetAppIsDisabled( - ash::kChromeUITrustedProjectorSwaAppId, !is_enabled); + auto* web_app_provider = ash::SystemWebAppManager::GetWebAppProvider(profile); + web_app_provider->on_registry_ready().Post( + FROM_HERE, base::BindOnce(&ProjectorClientImpl::SetAppIsDisabled, + weak_ptr_factory_.GetWeakPtr(), + web_app_provider, !is_enabled)); +} + +void ProjectorClientImpl::SetAppIsDisabled(web_app::WebAppProvider* provider, + bool disabled) { + provider->sync_bridge().SetAppIsDisabled( + ash::kChromeUITrustedProjectorSwaAppId, disabled); }
diff --git a/chrome/browser/ui/ash/projector/projector_client_impl.h b/chrome/browser/ui/ash/projector/projector_client_impl.h index 66bc785..a47666a7 100644 --- a/chrome/browser/ui/ash/projector/projector_client_impl.h +++ b/chrome/browser/ui/ash/projector/projector_client_impl.h
@@ -26,6 +26,10 @@ class WebView; } // namespace views +namespace web_app { +class WebAppProvider; +} // namespace web_app + class OnDeviceSpeechRecognizer; // The client implementation for the ProjectorController in ash/. This client is @@ -103,6 +107,9 @@ // app is enabled. void OnEnablementPolicyChanged(); + // Called when app registry becomes ready. + void SetAppIsDisabled(web_app::WebAppProvider* provider, bool disabled); + ash::ProjectorController* const controller_; ash::AnnotatorMessageHandler* message_handler_ = nullptr; SpeechRecognizerStatus recognizer_status_ =
diff --git a/chrome/browser/ui/ash/projector/projector_client_impl_browsertest.cc b/chrome/browser/ui/ash/projector/projector_client_impl_browsertest.cc index e32c7e20..27b6500 100644 --- a/chrome/browser/ui/ash/projector/projector_client_impl_browsertest.cc +++ b/chrome/browser/ui/ash/projector/projector_client_impl_browsertest.cc
@@ -384,6 +384,12 @@ profile->GetPrefs()->SetBoolean(GetPolicy(), false); // The Projector app immediately closes to prevent further access. EXPECT_TRUE(app_browser->IsAttemptingToCloseBrowser()); + + auto* web_app_provider = web_app::WebAppProvider::GetForTest(profile); + base::RunLoop loop; + web_app_provider->on_registry_ready().Post(FROM_HERE, loop.QuitClosure()); + loop.Run(); + // We can't uninstall the Projector SWA until the next session, but the icon // is greyed out and disabled. EXPECT_EQ(apps::Readiness::kDisabledByPolicy, @@ -394,6 +400,11 @@ // The app can re-enable too if it's already installed and the policy flips to // true. profile->GetPrefs()->SetBoolean(GetPolicy(), true); + + base::RunLoop loop2; + web_app_provider->on_registry_ready().Post(FROM_HERE, loop2.QuitClosure()); + loop2.Run(); + EXPECT_EQ(apps::Readiness::kReady, GetAppReadiness(kChromeUITrustedProjectorSwaAppId)); EXPECT_FALSE(apps::IconEffects::kBlocked &
diff --git a/chrome/browser/ui/ash/system_tray_tray_cast_browsertest_media_router_chromeos.cc b/chrome/browser/ui/ash/system_tray_tray_cast_browsertest_media_router_chromeos.cc index 08de5f2..54bfc54 100644 --- a/chrome/browser/ui/ash/system_tray_tray_cast_browsertest_media_router_chromeos.cc +++ b/chrome/browser/ui/ash/system_tray_tray_cast_browsertest_media_router_chromeos.cc
@@ -5,19 +5,38 @@ #include <memory> #include <vector> +#include "ash/components/login/auth/public/key.h" +#include "ash/components/login/auth/public/user_context.h" #include "ash/public/cpp/ash_view_ids.h" #include "ash/public/cpp/cast_config_controller.h" +#include "ash/public/cpp/system_tray_client.h" #include "ash/public/cpp/system_tray_test_api.h" +#include "ash/shelf/shelf.h" +#include "ash/shelf/shelf_widget.h" +#include "ash/shell.h" +#include "ash/system/cast/tray_cast.h" +#include "ash/system/cast/unified_cast_detailed_view_controller.h" +#include "ash/system/model/system_tray_model.h" +#include "ash/system/status_area_widget.h" +#include "ash/system/unified/unified_system_tray.h" +#include "ash/system/unified/unified_system_tray_bubble.h" +#include "ash/system/unified/unified_system_tray_view.h" #include "base/strings/utf_string_conversions.h" #include "chrome/browser/ash/login/login_manager_test.h" +#include "chrome/browser/ash/login/session/user_session_manager.h" +#include "chrome/browser/ash/login/session/user_session_manager_test_api.h" #include "chrome/browser/ash/login/test/login_manager_mixin.h" +#include "chrome/browser/ash/login/ui/login_display_host_webui.h" #include "chrome/browser/ash/profiles/profile_helper.h" #include "chrome/browser/media/router/discovery/access_code/access_code_cast_feature.h" #include "chrome/browser/media/router/media_router_feature.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/ash/cast_config_controller_media_router.h" #include "chrome/browser/ui/ui_features.h" +#include "chrome/test/base/chrome_test_utils.h" #include "chrome/test/base/in_process_browser_test.h" +#include "chrome/test/media_router/access_code_cast/access_code_cast_integration_browsertest.h" +#include "components/access_code_cast/common/access_code_cast_metrics.h" #include "components/account_id/account_id.h" #include "components/media_router/browser/media_routes_observer.h" #include "components/media_router/browser/media_sinks_observer.h" @@ -26,8 +45,12 @@ #include "components/media_router/common/test/test_helper.h" #include "components/prefs/pref_service.h" #include "components/user_manager/user_manager.h" +#include "components/vector_icons/vector_icons.h" #include "content/public/test/browser_test.h" +#include "content/public/test/test_host_resolver.h" #include "content/public/test/test_utils.h" +#include "ui/base/l10n/l10n_util.h" +#include "ui/events/test/event_generator.h" #include "ui/message_center/message_center.h" #include "url/gurl.h" @@ -219,11 +242,10 @@ EXPECT_FALSE(IsCastingNotificationVisible()); } -class SystemTrayTrayCastAccessCodeChromeOSTest : public ash::LoginManagerTest { +class SystemTrayTrayCastAccessCodeChromeOSTest + : public media_router::AccessCodeCastIntegrationBrowserTest { public: - SystemTrayTrayCastAccessCodeChromeOSTest() : LoginManagerTest() { - scoped_feature_list_.InitAndEnableFeature(::features::kAccessCodeCastUI); - + SystemTrayTrayCastAccessCodeChromeOSTest() { // Use consumer emails to avoid having to fake a policy fetch. login_mixin_.AppendRegularUsers(2); account_id1_ = login_mixin_.users()[0].account_id; @@ -237,20 +259,55 @@ ~SystemTrayTrayCastAccessCodeChromeOSTest() override = default; + void PreRunTestOnMainThread() override { + CastConfigControllerMediaRouter::SetMediaRouterForTest(media_router_); + InProcessBrowserTest::PreRunTestOnMainThread(); + } + void SetUpOnMainThread() override { - LoginManagerTest::SetUpOnMainThread(); + ash::LoginDisplayHostWebUI::DisableRestrictiveProxyCheckForTest(); + + ash::test::UserSessionManagerTestApi session_manager_test_api( + ash::UserSessionManager::GetInstance()); + session_manager_test_api.SetShouldLaunchBrowserInTests(false); + session_manager_test_api.SetShouldObtainTokenHandleInTests(false); + + AccessCodeCastIntegrationBrowserTest::SetUpOnMainThread(); + MixinBasedInProcessBrowserTest::SetUpOnMainThread(); tray_test_api_ = ash::SystemTrayTestApi::Create(); + + event_generator_ = std::make_unique<ui::test::EventGenerator>( // IN-TEST + ash::Shell::GetPrimaryRootWindow()); + } + + ui::test::EventGenerator* GetEventGenerator() { + return event_generator_.get(); } protected: void SetupUserProfile(const AccountId& account_id, bool allow_access_code) { - const user_manager::User* user = UserManager::Get()->FindUser(account_id); - Profile* profile = ProfileHelper::Get()->GetProfileByUser(user); + user_ = UserManager::Get()->FindUser(account_id); + Profile* profile = ProfileHelper::Get()->GetProfileByUser(user_); profile->GetPrefs()->SetBoolean(media_router::prefs::kAccessCodeCastEnabled, allow_access_code); content::RunAllTasksUntilIdle(); } + ash::UserContext CreateUserContext(const AccountId& account_id, + const std::string& password) { + ash::UserContext user_context(user_manager::UserType::USER_TYPE_REGULAR, + account_id); + user_context.SetKey(ash::Key(password)); + user_context.SetPasswordKey(ash::Key(password)); + if (account_id.GetUserEmail() == ash::FakeGaiaMixin::kEnterpriseUser1) { + user_context.SetRefreshToken(ash::FakeGaiaMixin::kTestRefreshToken1); + } else if (account_id.GetUserEmail() == + ash::FakeGaiaMixin::kEnterpriseUser2) { + user_context.SetRefreshToken(ash::FakeGaiaMixin::kTestRefreshToken2); + } + return user_context; + } + void ShowBubble() { tray_test_api_->ShowBubble(); } void CloseBubble() { tray_test_api_->CloseBubble(); } @@ -263,19 +320,57 @@ bool IsTrayVisible() { return IsViewDrawn(ash::VIEW_ID_CAST_MAIN_VIEW); } + // Returns the status area widget. + ash::StatusAreaWidget* FindStatusAreaWidget() { + return ash::Shelf::ForWindow(ash::Shell::GetRootWindowForNewWindows()) + ->shelf_widget() + ->status_area_widget(); + } + + // Performs a tap of the specified |view|. + void TapOn(const views::View* view) { + GetEventGenerator()->MoveTouch(view->GetBoundsInScreen().CenterPoint()); + GetEventGenerator()->ClickLeftButton(); + } + + ash::CastDetailedView* GetCastDetailedView() { + return GetUnifiedCastDetailedViewController() + ->get_cast_detailed_view_for_testing(); // IN-TEST + } + + ash::UnifiedSystemTrayController* GetUnifiedSystemTrayController() { + return FindStatusAreaWidget() + ->unified_system_tray() + ->bubble() + ->unified_system_tray_controller(); + } + + ash::UnifiedCastDetailedViewController* + GetUnifiedCastDetailedViewController() { + return static_cast<ash::UnifiedCastDetailedViewController*>( + GetUnifiedSystemTrayController()->detailed_view_controller()); + } + AccountId account_id1_; AccountId account_id2_; - private: ash::LoginManagerMixin login_mixin_{&mixin_host_}; + + std::unique_ptr<ui::test::EventGenerator> event_generator_; + const user_manager::User* user_; + + private: std::unique_ptr<ash::SystemTrayTestApi> tray_test_api_; base::test::ScopedFeatureList scoped_feature_list_; }; IN_PROC_BROWSER_TEST_F(SystemTrayTrayCastAccessCodeChromeOSTest, PolicyOffNoSinksNoVisibleTray) { + const ash::UserContext user_context = + CreateUserContext(account_id1_, "password"); + // Login a user that does not have access code casting enabled - LoginUser(account_id1_); + ASSERT_TRUE(login_mixin_.LoginAndWaitForActiveSession(user_context)); SetupUserProfile(account_id1_, /* allow_access_code */ false); ShowBubble(); @@ -287,13 +382,88 @@ IN_PROC_BROWSER_TEST_F(SystemTrayTrayCastAccessCodeChromeOSTest, PolicyOnNoSinksVisibleTray) { - // Login a user that does not have access code casting enabled - LoginUser(account_id2_); + const ash::UserContext user_context = + CreateUserContext(account_id2_, "password"); + + // Login a user that does have access code casting enabled + ASSERT_TRUE(login_mixin_.LoginAndWaitForActiveSession(user_context)); SetupUserProfile(account_id2_, /* allow_access_code */ true); ShowBubble(); // Even though there are no sinks, since this user does have access code - // casting enabled, the tray should not be visible. + // casting enabled, the tray should be visible. EXPECT_TRUE(IsTrayVisible()); } + +IN_PROC_BROWSER_TEST_F(SystemTrayTrayCastAccessCodeChromeOSTest, + SimulateValidCastingWorkflow) { + const ash::UserContext user_context = + CreateUserContext(account_id2_, "password"); + + // Login a user that does have access code casting enabled + ASSERT_TRUE(login_mixin_.LoginAndWaitForActiveSession(user_context)); + SetupUserProfile(account_id2_, /* allow_access_code */ true); + + ShowBubble(); + + // Show the Cast detailed view menu. + GetUnifiedSystemTrayController()->ShowCastDetailedView(); + + auto* detailed_cast_view = GetCastDetailedView(); + ASSERT_TRUE(detailed_cast_view); + + auto* access_code_cast_button = + detailed_cast_view->get_add_access_code_device_for_testing(); // IN-TEST + ASSERT_TRUE(access_code_cast_button); + ASSERT_TRUE(access_code_cast_button->GetEnabled()); + + const char kEndpointResponseSuccess[] = + R"({ + "device": { + "displayName": "test_device", + "id": "1234", + "deviceCapabilities": { + "videoOut": true, + "videoIn": true, + "audioOut": true, + "audioIn": true, + "devMode": true + }, + "networkInfo": { + "hostName": "GoogleNet", + "port": "666", + "ipV4Address": "192.0.2.146", + "ipV6Address": "2001:0db8:85a3:0000:0000:8a2e:0370:7334" + } + } + })"; + + // Mock a successful fetch from our server. + SetEndpointFetcherMockResponse(kEndpointResponseSuccess, net::HTTP_OK, + net::OK); + + // Simulate a successful opening of the channel. + SetMockOpenChannelCallbackResponse(true); + + SetUpPrimaryAccountWithHostedDomain( + signin::ConsentLevel::kSync, + ProfileHelper::Get()->GetProfileByUser(user_)); + + content::WebContentsAddedObserver observer; + TapOn(access_code_cast_button); + + content::WebContents* dialog_contents = observer.GetWebContents(); + ASSERT_TRUE(content::WaitForLoadStop(dialog_contents)); + + SetAccessCode("abcdef", dialog_contents); + + // TODO(crbug.com/1291738): There is a validation process with desktop media + // requests which are unnecessary for the complexity of this browsertest. We + // are just passing in a hardcoded magic string instead. + ExpectStartRouteCallFromTabMirroring( + "cast:<1234>", "urn:x-org.chromium.media:source:desktop", nullptr, + base::Seconds(120), media_router_); + + PressSubmitAndWaitForClose(dialog_contents); +}
diff --git a/chrome/browser/ui/views/accessibility/caption_bubble_controller_views_browsertest.cc b/chrome/browser/ui/views/accessibility/caption_bubble_controller_views_browsertest.cc index f466ec3..6afe1fc 100644 --- a/chrome/browser/ui/views/accessibility/caption_bubble_controller_views_browsertest.cc +++ b/chrome/browser/ui/views/accessibility/caption_bubble_controller_views_browsertest.cc
@@ -29,6 +29,7 @@ #include "ui/views/controls/button/image_button.h" #include "ui/views/controls/image_view.h" #include "ui/views/controls/label.h" +#include "ui/views/test/widget_test.h" #include "ui/views/widget/widget.h" #if defined(USE_AURA) @@ -1059,8 +1060,12 @@ test_task_runner->FastForwardBy(base::Seconds(4)); EXPECT_TRUE(IsWidgetVisible()); - // TODO(crbug.com/1055150): Test that widget doesn't hide when focused. It - // works in app but the tests aren't working. + // Test that widget doesn't hide when focused. + views::test::WidgetActivationWaiter waiter(GetCaptionWidget(), true); + GetCaptionWidget()->Activate(); + waiter.Wait(); + test_task_runner->FastForwardBy(base::Seconds(10)); + EXPECT_TRUE(IsWidgetVisible()); } // TODO(https://crbug.com/1207312): Flaky test.
diff --git a/chrome/browser/ui/views/omnibox/omnibox_popup_contents_view.cc b/chrome/browser/ui/views/omnibox/omnibox_popup_contents_view.cc index 9cc06e2..c105c05 100644 --- a/chrome/browser/ui/views/omnibox/omnibox_popup_contents_view.cc +++ b/chrome/browser/ui/views/omnibox/omnibox_popup_contents_view.cc
@@ -355,8 +355,10 @@ // Show the header if it's distinct from the previous match's header. const AutocompleteMatch& match = GetMatchAtIndex(i); std::u16string current_row_header = - edit_model_->result().GetHeaderForSuggestionGroup( - match.suggestion_group_id.value_or(kInvalidSuggestionGroupId)); + match.suggestion_group_id.has_value() + ? edit_model_->result().GetHeaderForSuggestionGroup( + match.suggestion_group_id.value()) + : u""; if (!current_row_header.empty() && current_row_header != previous_row_header) { row_view->ShowHeader(match.suggestion_group_id.value(),
diff --git a/chrome/browser/ui/views/omnibox/omnibox_row_view.cc b/chrome/browser/ui/views/omnibox/omnibox_row_view.cc index e8c872af..9d4b2e3 100644 --- a/chrome/browser/ui/views/omnibox/omnibox_row_view.cc +++ b/chrome/browser/ui/views/omnibox/omnibox_row_view.cc
@@ -17,6 +17,7 @@ #include "components/omnibox/browser/omnibox_edit_model.h" #include "components/omnibox/browser/omnibox_popup_selection.h" #include "components/omnibox/browser/omnibox_prefs.h" +#include "components/omnibox/browser/suggestion_group.h" #include "components/omnibox/browser/vector_icons.h" #include "components/prefs/pref_change_registrar.h" #include "components/prefs/pref_service.h" @@ -86,7 +87,8 @@ } } - void SetHeader(int suggestion_group_id, const std::u16string& header_text) { + void SetHeader(SuggestionGroupId suggestion_group_id, + const std::u16string& header_text) { suggestion_group_id_ = suggestion_group_id; header_text_ = header_text; @@ -248,7 +250,7 @@ raw_ptr<views::ToggleImageButton> header_toggle_button_; // The group ID associated with this header. - int suggestion_group_id_ = 0; + SuggestionGroupId suggestion_group_id_ = SuggestionGroupId::kInvalid; // The unmodified header text for this header. std::u16string header_text_; @@ -332,7 +334,7 @@ result_view_ = AddChildView(std::move(result_view)); } -void OmniboxRowView::ShowHeader(int suggestion_group_id, +void OmniboxRowView::ShowHeader(SuggestionGroupId suggestion_group_id, const std::u16string& header_text) { // Create the header (at index 0) if it doesn't exist. if (header_view_ == nullptr)
diff --git a/chrome/browser/ui/views/omnibox/omnibox_row_view.h b/chrome/browser/ui/views/omnibox/omnibox_row_view.h index dce7b82..189c46234 100644 --- a/chrome/browser/ui/views/omnibox/omnibox_row_view.h +++ b/chrome/browser/ui/views/omnibox/omnibox_row_view.h
@@ -14,6 +14,7 @@ class OmniboxEditModel; class OmniboxResultView; class PrefService; +enum class SuggestionGroupId; // The View that's a direct child of the OmniboxPopupContentsView, one per row. // This, in turn, has a child OmniboxResultView and an optional header that is @@ -31,7 +32,8 @@ PrefService* pref_service); // Sets the header that appears above this row. Also shows the header. - void ShowHeader(int suggestion_group_id, const std::u16string& header_text); + void ShowHeader(SuggestionGroupId suggestion_group_id, + const std::u16string& header_text); // Hides the header. void HideHeader();
diff --git a/chrome/browser/ui/views/side_search/side_search_icon_view.cc b/chrome/browser/ui/views/side_search/side_search_icon_view.cc index 9ec7fb9..d82f0976 100644 --- a/chrome/browser/ui/views/side_search/side_search_icon_view.cc +++ b/chrome/browser/ui/views/side_search/side_search_icon_view.cc
@@ -146,10 +146,9 @@ return; if (base::FeatureList::IsEnabled(features::kUnifiedSidePanel)) { - content::WebContents* active_contents = - browser_view->GetActiveWebContents(); - UnifiedSideSearchController::FromWebContents(active_contents) - ->OpenSidePanel(); + browser_view->side_panel_coordinator()->Show( + SidePanelEntry::Id::kSideSearch, + SidePanelUtil::SidePanelOpenTrigger::kSideSearchPageAction); } else { browser_view->side_search_controller()->ToggleSidePanel(); }
diff --git a/chrome/browser/ui/views/side_search/unified_side_search_controller.cc b/chrome/browser/ui/views/side_search/unified_side_search_controller.cc index dffdbe91..0acb2ace 100644 --- a/chrome/browser/ui/views/side_search/unified_side_search_controller.cc +++ b/chrome/browser/ui/views/side_search/unified_side_search_controller.cc
@@ -89,6 +89,13 @@ void UnifiedSideSearchController::OnEntryShown(SidePanelEntry* entry) { UpdateSidePanel(); + auto* active_contents = GetBrowserView()->GetActiveWebContents(); + if (active_contents) { + auto* helper = + SideSearchTabContentsHelper::FromWebContents(active_contents); + if (helper) + helper->MaybeRecordDurationSidePanelAvailableToFirstOpen(); + } } void UnifiedSideSearchController::OnEntryHidden(SidePanelEntry* entry) { @@ -151,13 +158,6 @@ if (browser_view) { browser_view->side_panel_coordinator()->Show( SidePanelEntry::Id::kSideSearch); - auto* active_contents = browser_view->GetActiveWebContents(); - if (active_contents) { - auto* helper = - SideSearchTabContentsHelper::FromWebContents(active_contents); - if (helper) - helper->MaybeRecordDurationSidePanelAvailableToFirstOpen(); - } } }
diff --git a/chrome/browser/ui/views/status_bubble_views.cc b/chrome/browser/ui/views/status_bubble_views.cc index 8c3a7cba..a48193c6 100644 --- a/chrome/browser/ui/views/status_bubble_views.cc +++ b/chrome/browser/ui/views/status_bubble_views.cc
@@ -706,7 +706,11 @@ DCHECK(!expand_view_); popup_ = std::make_unique<views::Widget>(); +#if BUILDFLAG(IS_MAC) + views::Widget::InitParams params(views::Widget::InitParams::TYPE_TOOLTIP); +#else views::Widget::InitParams params(views::Widget::InitParams::TYPE_POPUP); +#endif #if BUILDFLAG(IS_WIN) // On Windows use the software compositor to ensure that we don't block // the UI thread blocking issue during command buffer creation. We can
diff --git a/chrome/browser/ui/views/tabs/tab_drag_controller.cc b/chrome/browser/ui/views/tabs/tab_drag_controller.cc index 9336243b..22325cc 100644 --- a/chrome/browser/ui/views/tabs/tab_drag_controller.cc +++ b/chrome/browser/ui/views/tabs/tab_drag_controller.cc
@@ -2338,28 +2338,18 @@ gfx::Point TabDragController::GetCursorScreenPoint() { #if BUILDFLAG(IS_CHROMEOS_ASH) - if (event_source_ == EVENT_SOURCE_TOUCH && - aura::Env::GetInstance()->is_touch_down()) { - views::Widget* widget = GetAttachedBrowserWidget(); - DCHECK(widget); - aura::Window* widget_window = widget->GetNativeWindow(); - DCHECK(widget_window->GetRootWindow()); - gfx::PointF touch_point_f; - bool got_touch_point = - widget->GetGestureRecognizer()->GetLastTouchPointForTarget( - widget_window, &touch_point_f); - if (got_touch_point) { - gfx::Point touch_point = gfx::ToFlooredPoint(touch_point_f); - wm::ConvertPointToScreen(widget_window->GetRootWindow(), &touch_point); - return touch_point; - } - - // Fallback when touch state is lost. See http://crbug.com/1162541 - return last_point_in_screen_; - } -#endif - + views::Widget* widget = GetAttachedBrowserWidget(); + DCHECK(widget); + aura::Window* widget_window = widget->GetNativeWindow(); + DCHECK(widget_window->GetRootWindow()); + auto event_source = event_source_ == EVENT_SOURCE_MOUSE + ? ui::mojom::DragEventSource::kMouse + : ui::mojom::DragEventSource::kTouch; + return aura::Env::GetInstance()->GetLastPointerPoint( + event_source, widget_window, /*fallback=*/last_point_in_screen_); +#else return display::Screen::GetScreen()->GetCursorScreenPoint(); +#endif } gfx::Vector2d TabDragController::GetWindowOffset(
diff --git a/chrome/browser/ui/webui/access_code_cast/access_code_cast_handler_browsertest.cc b/chrome/browser/ui/webui/access_code_cast/access_code_cast_handler_browsertest.cc index 84b8a1a4..e62f725 100644 --- a/chrome/browser/ui/webui/access_code_cast/access_code_cast_handler_browsertest.cc +++ b/chrome/browser/ui/webui/access_code_cast/access_code_cast_handler_browsertest.cc
@@ -8,6 +8,7 @@ #include "base/win/windows_version.h" #endif #include "chrome/browser/media/router/discovery/access_code/access_code_cast_constants.h" +#include "components/sessions/content/session_tab_helper.h" #include "testing/gtest/include/gtest/gtest.h" namespace media_router { @@ -29,7 +30,8 @@ // This tests that if the network is not present (we are not connected to the // internet), we will see a server error in the access code dialog box. - SetUpPrimaryAccountWithHostedDomain(signin::ConsentLevel::kSync); + SetUpPrimaryAccountWithHostedDomain(signin::ConsentLevel::kSync, + browser()->profile()); auto* dialog_contents = ShowDialog(); SetAccessCode("abcdef", dialog_contents); @@ -79,11 +81,17 @@ EnableAccessCodeCasting(); - SetUpPrimaryAccountWithHostedDomain(signin::ConsentLevel::kSync); + SetUpPrimaryAccountWithHostedDomain(signin::ConsentLevel::kSync, + browser()->profile()); auto* dialog_contents = ShowDialog(); SetAccessCode("abcdef", dialog_contents); - ExpectStartRouteCallFromTabMirroring("cast:<1234>"); + ExpectStartRouteCallFromTabMirroring( + "cast:<1234>", + MediaSource::ForTab( + sessions::SessionTabHelper::IdForTab(web_contents()).id()) + .id(), + web_contents()); PressSubmitAndWaitForClose(dialog_contents); } @@ -100,7 +108,8 @@ // This tests that an account that does not have Sync enabled will throw a // generic error. - SetUpPrimaryAccountWithHostedDomain(signin::ConsentLevel::kSignin); + SetUpPrimaryAccountWithHostedDomain(signin::ConsentLevel::kSignin, + browser()->profile()); auto* dialog_contents = ShowDialog(); SetAccessCode("abcdef", dialog_contents);
diff --git a/chrome/browser/ui/webui/realbox/realbox_handler.cc b/chrome/browser/ui/webui/realbox/realbox_handler.cc index f7b1d0c2..515b20f 100644 --- a/chrome/browser/ui/webui/realbox/realbox_handler.cc +++ b/chrome/browser/ui/webui/realbox/realbox_handler.cc
@@ -137,7 +137,8 @@ suggestion_group->hide_group_a11y_label = l10n_util::GetStringFUTF16( IDS_ACC_HEADER_HIDE_SUGGESTIONS_BUTTON, suggestion_group->header); - result_map.emplace(pair.first, std::move(suggestion_group)); + result_map.emplace(static_cast<int>(pair.first), + std::move(suggestion_group)); } return result_map; } @@ -212,8 +213,8 @@ description_class.style)); } mojom_match->destination_url = match.destination_url; - mojom_match->suggestion_group_id = - match.suggestion_group_id.value_or(kInvalidSuggestionGroupId); + mojom_match->suggestion_group_id = static_cast<int>( + match.suggestion_group_id.value_or(SuggestionGroupId::kInvalid)); const bool is_bookmarked = bookmark_model->IsBookmarked(match.destination_url); mojom_match->icon_url = @@ -689,9 +690,14 @@ if (!autocomplete_controller_) return; + // It should be safe to cast |suggestion_group_id| to SuggestionGroupId type, + // since the group ID was originally passed to the page by the browser. + // TODO(crbug.com/1343512): Investigate migrating this enum to a proto enum + // to take advantage of its safe built-in conversion logic. omnibox::SuggestionGroupVisibility new_value = autocomplete_controller_->result().IsSuggestionGroupHidden( - profile_->GetPrefs(), suggestion_group_id) + profile_->GetPrefs(), + static_cast<SuggestionGroupId>(suggestion_group_id)) ? omnibox::SuggestionGroupVisibility::SHOWN : omnibox::SuggestionGroupVisibility::HIDDEN; omnibox::SetSuggestionGroupVisibility(profile_->GetPrefs(),
diff --git a/chrome/browser/web_applications/commands/install_isolated_app_command.cc b/chrome/browser/web_applications/commands/install_isolated_app_command.cc index 3d014737..96a6ced 100644 --- a/chrome/browser/web_applications/commands/install_isolated_app_command.cc +++ b/chrome/browser/web_applications/commands/install_isolated_app_command.cc
@@ -16,6 +16,9 @@ #include "chrome/browser/web_applications/commands/web_app_command.h" #include "chrome/browser/web_applications/web_app_data_retriever.h" #include "chrome/browser/web_applications/web_app_url_loader.h" +#include "third_party/blink/public/common/manifest/manifest_util.h" +#include "third_party/blink/public/mojom/manifest/manifest.mojom.h" +#include "url/gurl.h" namespace web_app { @@ -63,14 +66,58 @@ url_loader_.LoadUrl( url, shared_web_contents(), WebAppUrlLoader::UrlComparison::kIgnoreQueryParamsAndRef, + base::BindOnce(&InstallIsolatedAppCommand::OnLoadUrl, weak_this_)); +} + +void InstallIsolatedAppCommand::OnLoadUrl(WebAppUrlLoaderResult result) { + if (!IsUrlLoadingResultSuccess(result)) { + ReportFailure(); + return; + } + + data_retriever_->CheckInstallabilityAndRetrieveManifest( + shared_web_contents(), + /*bypass_service_worker_check=*/false, base::BindOnce( - [](base::WeakPtr<InstallIsolatedAppCommand> self, - WebAppUrlLoader::Result url_loading_result) { - self->Report(IsUrlLoadingResultSuccess(url_loading_result)); - }, + &InstallIsolatedAppCommand::OnCheckInstallabilityAndRetrieveManifest, weak_this_)); } +void InstallIsolatedAppCommand::OnCheckInstallabilityAndRetrieveManifest( + blink::mojom::ManifestPtr opt_manifest, + const GURL& manifest_url, + bool valid_manifest_for_web_app, + bool is_installable) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + if (!is_installable) { + ReportFailure(); + return; + } + + // See |WebAppDataRetriever::CheckInstallabilityCallback| documentation for + // details. + DCHECK(valid_manifest_for_web_app) + << "must be true when |is_installable| is true."; + + if (!opt_manifest) { + ReportFailure(); + return; + } + + // See |WebAppDataRetriever::CheckInstallabilityCallback| documentation for + // details. + DCHECK(!blink::IsEmptyManifest(opt_manifest)) + << "must not be empty when manifest is present."; + + // See |WebAppDataRetriever::CheckInstallabilityCallback| documentation for + // details. + DCHECK(!manifest_url.is_empty()) + << "must not be empty if manifest is not empty."; + + Report(/*success=*/true); +} + void InstallIsolatedAppCommand::OnSyncSourceRemoved() { ReportFailure(); }
diff --git a/chrome/browser/web_applications/commands/install_isolated_app_command.h b/chrome/browser/web_applications/commands/install_isolated_app_command.h index 526aa27..67f9880 100644 --- a/chrome/browser/web_applications/commands/install_isolated_app_command.h +++ b/chrome/browser/web_applications/commands/install_isolated_app_command.h
@@ -13,11 +13,16 @@ #include "base/strings/string_piece_forward.h" #include "base/values.h" #include "chrome/browser/web_applications/commands/web_app_command.h" +#include "third_party/blink/public/mojom/manifest/manifest.mojom-forward.h" + +class GURL; namespace web_app { class WebAppUrlLoader; class WebAppDataRetriever; +enum class WebAppUrlLoaderResult; + enum class InstallIsolatedAppCommandResult { kOk, kUnknownError, @@ -44,6 +49,13 @@ void ReportFailure(); void Report(bool success); + void OnLoadUrl(WebAppUrlLoaderResult result); + void OnCheckInstallabilityAndRetrieveManifest( + blink::mojom::ManifestPtr opt_manifest, + const GURL& manifest_url, + bool valid_manifest_for_web_app, + bool is_installable); + SEQUENCE_CHECKER(sequence_checker_); std::string url_;
diff --git a/chrome/browser/web_applications/commands/install_isolated_app_command_unittest.cc b/chrome/browser/web_applications/commands/install_isolated_app_command_unittest.cc index ea93c70..f9dd3e5 100644 --- a/chrome/browser/web_applications/commands/install_isolated_app_command_unittest.cc +++ b/chrome/browser/web_applications/commands/install_isolated_app_command_unittest.cc
@@ -84,13 +84,36 @@ std::unique_ptr<WebAppDataRetriever> data_retriever = nullptr) { base::test::TestFuture<InstallIsolatedAppCommandResult> test_future; auto command = CreateCommand(url, test_future.GetCallback()); - if (data_retriever != nullptr) { - command->SetDataRetrieverForTesting(std::move(data_retriever)); - } + command->SetDataRetrieverForTesting(data_retriever != nullptr + ? std::move(data_retriever) + : CreateDefaultDataRetriever()); ScheduleCommand(std::move(command)); return test_future.Get(); } + static std::unique_ptr<FakeDataRetriever> CreateDefaultDataRetriever() { + std::unique_ptr<FakeDataRetriever> fake_data_retriever = + std::make_unique<FakeDataRetriever>(); + + fake_data_retriever->SetManifest( + /*manifest=*/CreateDefaultManifest(), /*is_installable=*/true, + /*manifest_url=*/CreateDefaultManifestURL()); + return fake_data_retriever; + } + + static blink::mojom::ManifestPtr CreateDefaultManifest() { + auto manifest = blink::mojom::Manifest::New(); + manifest->start_url = GURL{"http://test.com/"}, + manifest->scope = GURL{"http://test.com/scope"}, + manifest->display = DisplayMode::kStandalone; + manifest->short_name = u"Manifest Name"; + return manifest; + } + + static GURL CreateDefaultManifestURL() { + return GURL{"http://defaul-non-empty-url.com/manifest.json"}; + } + TestingProfile* profile() const { return profile_.get(); } private: @@ -171,11 +194,54 @@ std::unique_ptr<FakeDataRetriever> fake_data_retriever = std::make_unique<FakeDataRetriever>(); + fake_data_retriever->SetManifest( + /*manifest=*/CreateDefaultManifest(), /*is_installable=*/true, + /*manifest_url=*/CreateDefaultManifestURL()); EXPECT_THAT(ExecuteCommand("http://test-url-example.com", std::move(fake_data_retriever)), IsInstallationOk()); } +TEST_F(InstallIsolatedAppCommandTest, + InstallationFailsWhenAppIsNotInstallable) { + SetPrepareForLoadResultLoaded(); + + ExpectLoadedForURL("http://test-url-example.com"); + + std::unique_ptr<FakeDataRetriever> fake_data_retriever = + std::make_unique<FakeDataRetriever>(); + + auto manifest = blink::mojom::Manifest::New(); + fake_data_retriever->SetManifest( + std::move(manifest), + /*is_installable=*/false, + /*manifest_url=*/ + GURL{"http://test-url-example.com/manifest.json"}); + + EXPECT_THAT(ExecuteCommand("http://test-url-example.com", + std::move(fake_data_retriever)), + Not(IsInstallationOk())); +} + +TEST_F(InstallIsolatedAppCommandTest, + InstallationFailsWhenAppIsInstallableButManifestIsNull) { + SetPrepareForLoadResultLoaded(); + + ExpectLoadedForURL("http://test-url-example.com"); + + std::unique_ptr<FakeDataRetriever> fake_data_retriever = + std::make_unique<FakeDataRetriever>(); + + fake_data_retriever->SetManifest( + /*manifest=*/nullptr, + /*is_installable=*/true, + /*manifest_url=*/CreateDefaultManifestURL()); + + EXPECT_THAT(ExecuteCommand("http://test-url-example.com", + std::move(fake_data_retriever)), + Not(IsInstallationOk())); +} + } // namespace } // namespace web_app
diff --git a/chrome/browser/webauthn/chrome_webauthn_browsertest.cc b/chrome/browser/webauthn/chrome_webauthn_browsertest.cc index 0f90f1e..82f8194 100644 --- a/chrome/browser/webauthn/chrome_webauthn_browsertest.cc +++ b/chrome/browser/webauthn/chrome_webauthn_browsertest.cc
@@ -98,8 +98,8 @@ static constexpr char kPageFile[] = "page.html"; - std::vector<base::Value> resources; - resources.emplace_back(std::string(kPageFile)); + base::Value::List resources; + resources.Append(std::string(kPageFile)); static constexpr char kContents[] = R"( <html> <head>
diff --git a/chrome/browser/xsurface/android/java/src/org/chromium/chrome/browser/xsurface/SurfaceActionsHandler.java b/chrome/browser/xsurface/android/java/src/org/chromium/chrome/browser/xsurface/SurfaceActionsHandler.java index 4089466..eb85d98 100644 --- a/chrome/browser/xsurface/android/java/src/org/chromium/chrome/browser/xsurface/SurfaceActionsHandler.java +++ b/chrome/browser/xsurface/android/java/src/org/chromium/chrome/browser/xsurface/SurfaceActionsHandler.java
@@ -114,4 +114,11 @@ * Attempts to follow or unfollow a WebFeed. */ default void updateWebFeedFollowState(WebFeedFollowUpdate update) {} + + /** + * Navigates a new tab in group to a particular URL. + * @param url The url for which to navigate. + * @param actionSourceView The View from which the user tap originated. May be null. + */ + default void navigateNewTabInGroup(String url, View actionSourceView) {} }
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt index 7f18884..a471cb2 100644 --- a/chrome/build/linux.pgo.txt +++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@ -chrome-linux-main-1658404777-1ea1be63e174d409a7b616996078d592f388c70f.profdata +chrome-linux-main-1658425924-e2c6280a51ccc9e0ee83ef2933893b13ff7fcb7c.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt index 086ccbbc..5b73041 100644 --- a/chrome/build/win32.pgo.txt +++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@ -chrome-win32-main-1658404777-d39fb39b600e72bc0bcac09f6cf18a5f1419f1af.profdata +chrome-win32-main-1658425924-778ab27c2d5aa269a6113ae799d48237a5b5d381.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt index 3090acc..384962c 100644 --- a/chrome/build/win64.pgo.txt +++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@ -chrome-win64-main-1658415573-cbf63ca3eb3683d159c0865bd1049a6ee0f2a2f1.profdata +chrome-win64-main-1658425924-23d20021b0a4b09689ed0da2e2eccc4accf2fc53.profdata
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn index fdce773..3bb624d1 100644 --- a/chrome/test/BUILD.gn +++ b/chrome/test/BUILD.gn
@@ -4183,6 +4183,7 @@ "//chromeos/ui/frame", "//chromeos/ui/frame:test_support", "//chromeos/ui/wm", + "//components/access_code_cast/common", "//components/app_constants", "//components/app_restore", "//components/arc:arc_test_support", @@ -9285,6 +9286,7 @@ "//ui/base:test_support", "//ui/base/clipboard:clipboard_test_support", "//ui/compositor:test_support", + "//ui/display:display_interactive_ui_tests", "//ui/display:test_support", "//ui/events:events_interactive_ui_tests", "//ui/events:gesture_detection",
diff --git a/chrome/test/chromedriver/session_commands_unittest.cc b/chrome/test/chromedriver/session_commands_unittest.cc index 2f000d5..e4525a6 100644 --- a/chrome/test/chromedriver/session_commands_unittest.cc +++ b/chrome/test/chromedriver/session_commands_unittest.cc
@@ -192,14 +192,14 @@ ASSERT_TRUE(result.DictEmpty()); // Invalid entry - base::DictionaryValue* entry_ptr; - ASSERT_TRUE(list_ptr->GetDictionary(0, &entry_ptr)); - entry_ptr->GetDict().Set("pageLoadStrategy", "invalid"); + base::Value::Dict* entry_ptr = list_ptr->GetList()[0].GetIfDict(); + ASSERT_TRUE(entry_ptr); + entry_ptr->Set("pageLoadStrategy", "invalid"); status = ProcessCapabilities(params, &result); ASSERT_EQ(kInvalidArgument, status.code()); // Valid entry - entry_ptr->GetDict().Set("pageLoadStrategy", "eager"); + entry_ptr->Set("pageLoadStrategy", "eager"); status = ProcessCapabilities(params, &result); ASSERT_EQ(kOk, status.code()) << status.message(); ASSERT_EQ(result.DictSize(), 1u); @@ -209,9 +209,10 @@ // Multiple entries, the first one should be selected. list_ptr->Append(base::DictionaryValue()); - ASSERT_TRUE(list_ptr->GetDictionary(1, &entry_ptr)); - entry_ptr->GetDict().Set("pageLoadStrategy", "normal"); - entry_ptr->GetDict().Set("browserName", "chrome"); + entry_ptr = list_ptr->GetList()[1].GetIfDict(); + ASSERT_TRUE(entry_ptr); + entry_ptr->Set("pageLoadStrategy", "normal"); + entry_ptr->Set("browserName", "chrome"); status = ProcessCapabilities(params, &result); ASSERT_EQ(kOk, status.code()) << status.message(); ASSERT_EQ(result.DictSize(), 1u);
diff --git a/chrome/test/data/webui/chromeos/os_feedback_ui/file_attachment_test.js b/chrome/test/data/webui/chromeos/os_feedback_ui/file_attachment_test.js index 725f1633..35d4b8b 100644 --- a/chrome/test/data/webui/chromeos/os_feedback_ui/file_attachment_test.js +++ b/chrome/test/data/webui/chromeos/os_feedback_ui/file_attachment_test.js
@@ -266,21 +266,42 @@ return new Uint8Array(fakeData).buffer; }, }); - // getImageUrl_ should return an url when file is image type. - const imageUrl = await page.getImageUrl_(fakeImageFile); + + page.setSelectedFileForTesting(fakeImageFile); + await flushTasks(); + + // The selected file name is set properly. + assertEquals('fake.png', getElementContent('#selectedFileName')); + + // The selectedFileImage should have an url when file is image type. + const imageUrl = getElement('#selectedFileImage').src; assertTrue(imageUrl.length > 0); // There should be a preview image. page.selectedImageUrl_ = imageUrl; const selectedImage = getElement('#selectedFileImage'); assertTrue(!!selectedImage.src); assertEquals(imageUrl, selectedImage.src); + assertEquals( + 'Preview fake.png', getElement('#selectedImageButton').ariaLabel); }); - // Test that clicking the image will open preview dialog and set the // focus on the close dialog icon button. test('selectedImagePreviewDialog', async () => { await initializePage(); + const fakeData = [12, 11, 99]; + + /** @type {!File} */ + const fakeImageFile = /** @type {!File} */ ({ + name: 'fake.png', + type: 'image/png', + size: MAX_ATTACH_FILE_SIZE, + arrayBuffer: async () => { + return new Uint8Array(fakeData).buffer; + }, + }); + + page.setSelectedFileForTesting(fakeImageFile); page.selectedImageUrl_ = fakeImageUrl; assertEquals(fakeImageUrl, getElement('#selectedFileImage').src); @@ -295,6 +316,9 @@ imageButton.click(); await imageClickPromise; + // The preview dialog's title should be set properly. + assertEquals('fake.png', getElementContent('#modalDialogTitleText')); + // The preview dialog's close icon button is visible now. assertTrue(isVisible(closeDialogButton)); // The preview dialog's close icon button is focused.
diff --git a/chrome/test/data/webui/chromeos/os_feedback_ui/share_data_page_test.js b/chrome/test/data/webui/chromeos/os_feedback_ui/share_data_page_test.js index e3164829..c4c33dce 100644 --- a/chrome/test/data/webui/chromeos/os_feedback_ui/share_data_page_test.js +++ b/chrome/test/data/webui/chromeos/os_feedback_ui/share_data_page_test.js
@@ -134,7 +134,10 @@ assertTrue(page.i18nExists('attachScreenshotLabel')); assertEquals('Screenshot', getElementContent('#screenshotCheckLabel')); + assertTrue(!!getElement('#screenshotImage')); + assertEquals('Preview Screenshot', getElement('#imageButton').ariaLabel); + assertTrue(page.i18nExists('previewImageAriaLabel')); // Add file attachment element. assertTrue(!!getElement('file-attachment'));
diff --git a/chrome/test/media_router/access_code_cast/access_code_cast_integration_browsertest.cc b/chrome/test/media_router/access_code_cast/access_code_cast_integration_browsertest.cc index 51c8c6e..8ee6bf1c 100644 --- a/chrome/test/media_router/access_code_cast/access_code_cast_integration_browsertest.cc +++ b/chrome/test/media_router/access_code_cast/access_code_cast_integration_browsertest.cc
@@ -183,24 +183,17 @@ } void AccessCodeCastIntegrationBrowserTest::SetUpPrimaryAccountWithHostedDomain( - signin::ConsentLevel consent_level) { + signin::ConsentLevel consent_level, + Profile* profile) { + ASSERT_TRUE(identity_test_environment_); // Ensure that the stub user is signed in. - CoreAccountInfo account_info = - identity_test_environment_->MakePrimaryAccountAvailable( - user_manager::kStubUserEmail, consent_level); + identity_test_environment_->MakePrimaryAccountAvailable( + user_manager::kStubUserEmail, consent_level); identity_test_environment_->SetAutomaticIssueOfAccessTokens(true); - ASSERT_EQ(account_info.email, user_manager::kStubUserEmail); - - identity_test_environment_->SimulateSuccessfulFetchOfAccountInfo( - account_info.account_id, account_info.email, account_info.gaia, - "foo_school.com", "full_name", "given_name", "locale", - "http://picture.example.com/picture.jpg"); - - ASSERT_TRUE( - AccessCodeCastSinkServiceFactory::GetForProfile(browser()->profile())); - AccessCodeCastSinkServiceFactory::GetForProfile(browser()->profile()) + ASSERT_TRUE(AccessCodeCastSinkServiceFactory::GetForProfile(profile)); + AccessCodeCastSinkServiceFactory::GetForProfile(profile) ->SetIdentityManagerForTesting( identity_test_environment_->identity_manager()); @@ -423,15 +416,19 @@ } void AccessCodeCastIntegrationBrowserTest::ExpectStartRouteCallFromTabMirroring( - const std::string& sink_name) { - MediaSource media_source = MediaSource::ForTab( - sessions::SessionTabHelper::IdForTab(web_contents()).id()); - auto* router = static_cast<TestMediaRouter*>( - media_router::MediaRouterFactory::GetInstance()->GetApiForBrowserContext( - browser()->profile())); - EXPECT_CALL(*router, - CreateRouteInternal(media_source.id(), sink_name, _, - web_contents(), _, base::Seconds(60), false)); + const std::string& sink_name, + const std::string& media_source_id, + content::WebContents* web_contents, + base::TimeDelta timeout, + media_router::MockMediaRouter* media_router) { + if (!media_router) { + media_router = static_cast<TestMediaRouter*>( + media_router::MediaRouterFactory::GetInstance() + ->GetApiForBrowserContext(browser()->profile())); + } + EXPECT_CALL(*media_router, + CreateRouteInternal(media_source_id, sink_name, _, web_contents, + _, timeout, false)); } } // namespace media_router
diff --git a/chrome/test/media_router/access_code_cast/access_code_cast_integration_browsertest.h b/chrome/test/media_router/access_code_cast/access_code_cast_integration_browsertest.h index 88141f4..7d9b6b46 100644 --- a/chrome/test/media_router/access_code_cast/access_code_cast_integration_browsertest.h +++ b/chrome/test/media_router/access_code_cast/access_code_cast_integration_browsertest.h
@@ -20,6 +20,7 @@ #include "chrome/common/webui_url_constants.h" #include "chrome/test/base/chrome_test_utils.h" #include "chrome/test/base/in_process_browser_test.h" +#include "chrome/test/base/mixin_based_in_process_browser_test.h" #include "chrome/test/base/mojo_web_ui_browser_test.h" #include "components/keyed_service/content/browser_context_dependency_manager.h" #include "components/media_router/browser/media_router.h" @@ -39,7 +40,8 @@ // Base class that generates an access code cast dialog and all objects that are // required for interacting with the dialog. -class AccessCodeCastIntegrationBrowserTest : public InProcessBrowserTest { +class AccessCodeCastIntegrationBrowserTest + : public MixinBasedInProcessBrowserTest { public: AccessCodeCastIntegrationBrowserTest(); ~AccessCodeCastIntegrationBrowserTest() override; @@ -51,7 +53,8 @@ // Makes user signed-in with the stub account's email and sets the // |consent_level| for that account. - void SetUpPrimaryAccountWithHostedDomain(signin::ConsentLevel consent_level); + void SetUpPrimaryAccountWithHostedDomain(signin::ConsentLevel consent_level, + Profile* profile); void EnableAccessCodeCasting(); @@ -96,7 +99,12 @@ void UpdateSinks(const std::vector<MediaSink>& sinks, const std::vector<url::Origin>& origins); - void ExpectStartRouteCallFromTabMirroring(const std::string& sink_name); + void ExpectStartRouteCallFromTabMirroring( + const std::string& sink_name, + const std::string& media_source_id, + content::WebContents* web_contents, + base::TimeDelta timeout = base::Seconds(60), + media_router::MockMediaRouter* media_router = nullptr); void ValidateAndReleaseCastMediaSinkServiceImpl(); protected:
diff --git a/chromecast/media/cma/backend/volume_map.cc b/chromecast/media/cma/backend/volume_map.cc index da361cd..70e6227 100644 --- a/chromecast/media/cma/backend/volume_map.cc +++ b/chromecast/media/cma/backend/volume_map.cc
@@ -66,18 +66,18 @@ double prev_level = -1.0; std::vector<LevelToDb> new_map; - for (size_t i = 0; i < volume_map_list->GetListDeprecated().size(); ++i) { - const base::DictionaryValue* volume_map_entry; - CHECK(volume_map_list->GetDictionary(i, &volume_map_entry)); - absl::optional<double> level = volume_map_entry->FindDoubleKey(kKeyLevel); + for (const auto& value : volume_map_list->GetList()) { + const base::Value::Dict& volume_map_entry = value.GetDict(); + + absl::optional<double> level = volume_map_entry.FindDouble(kKeyLevel); CHECK(level); CHECK_GE(*level, 0.0); CHECK_LE(*level, 1.0); CHECK_GT(*level, prev_level); prev_level = *level; - absl::optional<double> db = volume_map_entry->FindDoubleKey(kKeyDb); + absl::optional<double> db = volume_map_entry.FindDouble(kKeyDb); CHECK(db); CHECK_LE(*db, 0.0);
diff --git a/chromeos/chromeos_strings.grd b/chromeos/chromeos_strings.grd index b8b512f..465a6c2 100644 --- a/chromeos/chromeos_strings.grd +++ b/chromeos/chromeos_strings.grd
@@ -3449,6 +3449,9 @@ <message name="IDS_FEEDBACK_TOOL_ATTACH_SCREENSHOT_CHECKBOX_ARIA_LABEL" desc="Aria Label for the checkbox to select the screenshot"> Attach screenshot </message> + <message name="IDS_FEEDBACK_TOOL_PREVIEW_IMAGE_ARIA_LABEL" desc="Aria Label for the thumbnail of a screenshot or an image file"> + Preview <ph name="PREVIEW_OBJECT">$1<ex>Screenshot</ex></ph> + </message> <message name="IDS_FEEDBACK_TOOL_USER_CONSENT_LABEL" desc="Label for checkbox indicating whether the user agrees to be contacted back."> We may email you for more information or updates </message>
diff --git a/chromeos/chromeos_strings_grd/IDS_FEEDBACK_TOOL_PREVIEW_IMAGE_ARIA_LABEL.png.sha1 b/chromeos/chromeos_strings_grd/IDS_FEEDBACK_TOOL_PREVIEW_IMAGE_ARIA_LABEL.png.sha1 new file mode 100644 index 0000000..1bdd032 --- /dev/null +++ b/chromeos/chromeos_strings_grd/IDS_FEEDBACK_TOOL_PREVIEW_IMAGE_ARIA_LABEL.png.sha1
@@ -0,0 +1 @@ +c3ac7a6f7875ab6f9705b1a46fbd7a3fd47e97ad \ No newline at end of file
diff --git a/chromeos/components/quick_answers/BUILD.gn b/chromeos/components/quick_answers/BUILD.gn index f8680da..4a85be8 100644 --- a/chromeos/components/quick_answers/BUILD.gn +++ b/chromeos/components/quick_answers/BUILD.gn
@@ -59,6 +59,7 @@ "//chromeos/services/machine_learning/public/mojom", "//chromeos/strings:strings_grit", "//components/account_id", + "//components/language/core/browser", "//components/prefs:prefs", "//components/signin/public/base", "//components/signin/public/identity_manager",
diff --git a/chromeos/components/quick_answers/understanding/intent_generator.cc b/chromeos/components/quick_answers/understanding/intent_generator.cc index f113f0d..c64cd105 100644 --- a/chromeos/components/quick_answers/understanding/intent_generator.cc +++ b/chromeos/components/quick_answers/understanding/intent_generator.cc
@@ -41,6 +41,8 @@ // Set of invalid characters for definition annonations. constexpr char kInvalidCharactersSet[] = "()[]{}<>_&|!"; +constexpr char kEnglishLanguage[] = "en"; + const std::map<std::string, IntentType>& GetIntentTypeMap() { static base::NoDestructor<std::map<std::string, IntentType>> kIntentTypeMap( {{"unit", IntentType::kUnit}, {"dictionary", IntentType::kDictionary}}); @@ -93,10 +95,31 @@ return intent; } +bool IsPreferredLanguage(const std::string& detected_language) { + auto preferred_languages_list = + base::SplitString(QuickAnswersState::Get()->preferred_languages(), ",", + base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY); + + for (const std::string& locale : preferred_languages_list) { + if (l10n_util::GetLanguage(locale) == detected_language) + return true; + } + return false; +} + // TODO(b/169370175): There is an issue with text classifier that // concatenated words are annotated as definitions. Before we switch to v2 // model, skip such kind of queries for definition annotation for now. bool ShouldSkipDefinition(const std::string& text) { + // Skip definition annotations if English is not device language or user + // preferred language (Currently the text classifier only works with English + // words). + auto device_language = + l10n_util::GetLanguage(QuickAnswersState::Get()->application_locale()); + if (device_language != kEnglishLanguage && + !IsPreferredLanguage(kEnglishLanguage)) + return true; + DCHECK(text.length()); // Skip the query for definition annotation if the selected text contains // capitalized characters in the middle and not all capitalized. @@ -115,18 +138,6 @@ return false; } -bool IsPreferredLanguage(const std::string& detected_language) { - auto preferred_languages_list = - base::SplitString(QuickAnswersState::Get()->preferred_languages(), ",", - base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY); - - for (const std::string& locale : preferred_languages_list) { - if (l10n_util::GetLanguage(locale) == detected_language) - return true; - } - return false; -} - bool HasDigits(const std::string& word) { for (const auto& character : word) { if (std::isdigit(character)) @@ -214,8 +225,9 @@ QuickAnswersState::Get()->application_locale(), language)); - // Record intent source type for dictionary intent. + // Record intent source type and language for dictionary intent. RecordDictionaryIntentSource(DictionaryIntentSource::kHunspell); + RecordDictionaryIntentLanguage(language); return; } @@ -283,9 +295,13 @@ RewriteIntent(request.selected_text, entity_str, it->second), QuickAnswersState::Get()->application_locale())); - // Record intent source type for dictionary intent. - if (it->second == IntentType::kDictionary) + // Record intent source type and language for dictionary intent. + if (it->second == IntentType::kDictionary) { RecordDictionaryIntentSource(DictionaryIntentSource::kTextClassifier); + // Record the English language since currently the text classifier only + // works with English words. + RecordDictionaryIntentLanguage(kEnglishLanguage); + } return; } }
diff --git a/chromeos/components/quick_answers/understanding/intent_generator_unittest.cc b/chromeos/components/quick_answers/understanding/intent_generator_unittest.cc index 581090b..00fc9c5 100644 --- a/chromeos/components/quick_answers/understanding/intent_generator_unittest.cc +++ b/chromeos/components/quick_answers/understanding/intent_generator_unittest.cc
@@ -38,6 +38,12 @@ return TextLanguage::New("en", /* confidence */ 1); } +std::vector<TextLanguagePtr> DefaultLanguages() { + std::vector<TextLanguagePtr> languages; + languages.push_back(DefaultLanguage()); + return languages; +} + class FakeSpellChecker : public SpellChecker { public: FakeSpellChecker( @@ -83,7 +89,9 @@ base::BindOnce(&IntentGeneratorTest::IntentGeneratorTestCallback, base::Unretained(this))); - QuickAnswersState::Get()->set_use_text_annotator_for_testing(); + fake_quick_answers_state()->set_use_text_annotator_for_testing(); + fake_quick_answers_state()->set_application_locale("en"); + fake_quick_answers_state()->set_preferred_languages("en"); } void TearDown() override { @@ -106,8 +114,7 @@ void UseFakeServiceConnection( const std::vector<TextAnnotationPtr>& annotations = std::vector<TextAnnotationPtr>(), - const std::vector<TextLanguagePtr>& languages = - std::vector<TextLanguagePtr>()) { + const std::vector<TextLanguagePtr>& languages = DefaultLanguages()) { chromeos::machine_learning::ServiceConnection:: UseFakeServiceConnectionForTesting(&fake_service_connection_); chromeos::machine_learning::ServiceConnection::GetInstance()->Initialize(); @@ -253,9 +260,7 @@ TEST_F(IntentGeneratorTest, TranslationIntentWithAnnotation) { QuickAnswersRequest request; - request.selected_text = "unfathomable"; - fake_quick_answers_state()->set_application_locale("es"); - fake_quick_answers_state()->set_preferred_languages("es"); + request.selected_text = "prueba"; // Create the test annotations. std::vector<TextEntityPtr> entities; @@ -264,14 +269,14 @@ 1.0, // Confidence score. TextEntityData::NewNumericValue(0.0))); // Data extracted. - auto dictionary_annotation = TextAnnotation::New(0, // Start offset. - 12, // End offset. + auto dictionary_annotation = TextAnnotation::New(0, // Start offset. + 6, // End offset. std::move(entities)); std::vector<TextAnnotationPtr> annotations; annotations.push_back(dictionary_annotation->Clone()); std::vector<TextLanguagePtr> languages; - languages.push_back(DefaultLanguage()); + languages.push_back(TextLanguage::New("es", /* confidence */ 1)); UseFakeServiceConnection(annotations, languages); intent_generator_->GenerateIntent(request); @@ -281,7 +286,7 @@ // Should generate dictionary intent which is prioritized against // translation. EXPECT_EQ(IntentType::kDictionary, intent_info_.intent_type); - EXPECT_EQ("unfathomable", intent_info_.intent_text); + EXPECT_EQ("prueba", intent_info_.intent_text); } TEST_F(IntentGeneratorTest, TranslationIntentDeviceLanguageNotSet) { @@ -486,6 +491,41 @@ EXPECT_EQ("23 cm", intent_info_.intent_text); } +TEST_F(IntentGeneratorTest, TextAnnotationNonEnglishLanguage) { + fake_quick_answers_state()->set_application_locale("es"); + fake_quick_answers_state()->set_preferred_languages("es"); + + std::unique_ptr<QuickAnswersRequest> quick_answers_request = + std::make_unique<QuickAnswersRequest>(); + quick_answers_request->selected_text = "unfathomable"; + + // Create the test annotations. + std::vector<TextEntityPtr> entities; + entities.emplace_back(TextEntity::New( + "dictionary", // Entity name. + 1.0, // Confidence score. + TextEntityData::NewNumericValue(0.0))); // Data extracted. + + auto dictionary_annotation = TextAnnotation::New(0, // Start offset. + 12, // End offset. + std::move(entities)); + + std::vector<TextAnnotationPtr> annotations; + annotations.push_back(dictionary_annotation->Clone()); + std::vector<TextLanguagePtr> languages; + languages.push_back(TextLanguage::New("en", /* confidence */ 1)); + UseFakeServiceConnection(annotations, languages); + + intent_generator_->GenerateIntent(*quick_answers_request); + + FlushForTesting(); + + // Should not generate dictionary intent since English is not device language + // or preferred language. Should fallback to translation intent. + EXPECT_EQ(IntentType::kTranslation, intent_info_.intent_type); + EXPECT_EQ("unfathomable", intent_info_.intent_text); +} + TEST_F(IntentGeneratorTest, TextAnnotationIntentNoAnnotation) { std::unique_ptr<QuickAnswersRequest> quick_answers_request = std::make_unique<QuickAnswersRequest>();
diff --git a/chromeos/components/quick_answers/utils/quick_answers_metrics.cc b/chromeos/components/quick_answers/utils/quick_answers_metrics.cc index a96bf2e..0c6ebbe3 100644 --- a/chromeos/components/quick_answers/utils/quick_answers_metrics.cc +++ b/chromeos/components/quick_answers/utils/quick_answers_metrics.cc
@@ -8,6 +8,7 @@ #include "base/notreached.h" #include "base/strings/stringprintf.h" #include "base/time/time.h" +#include "components/language/core/browser/language_usage_metrics.h" namespace quick_answers { @@ -26,6 +27,8 @@ "QuickAnswers.TextToSpeech.EngineEvent"; const char kQuickAnswersDictionaryIntentSource[] = "QuickAnswers.DictionaryIntent.Source"; +const char kQuickAnswersDictionaryIntentLanguage[] = + "QuickAnswers.DictionaryIntent.Language"; const char kQuickAnswersNetworkError[] = "QuickAnswers.NetworkError.IntentType"; const char kQuickAnswersNetworkResponseCode[] = "QuickAnswers.NetworkError.ResponseCode"; @@ -153,4 +156,10 @@ base::UmaHistogramEnumeration(kQuickAnswersDictionaryIntentSource, source); } +void RecordDictionaryIntentLanguage(const std::string& language) { + base::UmaHistogramSparse( + kQuickAnswersDictionaryIntentLanguage, + language::LanguageUsageMetrics::ToLanguageCodeHash(language)); +} + } // namespace quick_answers
diff --git a/chromeos/components/quick_answers/utils/quick_answers_metrics.h b/chromeos/components/quick_answers/utils/quick_answers_metrics.h index eb65090..bcbfe80 100644 --- a/chromeos/components/quick_answers/utils/quick_answers_metrics.h +++ b/chromeos/components/quick_answers/utils/quick_answers_metrics.h
@@ -75,6 +75,9 @@ // Record the source type of dictionary intent. void RecordDictionaryIntentSource(DictionaryIntentSource source); +// Record the query language of dictionary intent. +void RecordDictionaryIntentLanguage(const std::string& language); + } // namespace quick_answers #endif // CHROMEOS_COMPONENTS_QUICK_ANSWERS_UTILS_QUICK_ANSWERS_METRICS_H_
diff --git a/chromeos/dbus/BUILD.gn b/chromeos/dbus/BUILD.gn index 9a158c3..e606507 100644 --- a/chromeos/dbus/BUILD.gn +++ b/chromeos/dbus/BUILD.gn
@@ -12,6 +12,8 @@ public_deps = [ "//chromeos/dbus/common", "//chromeos/dbus/constants", + + # TODO(jamescook): Remove this and fix all build targets that need it. "//chromeos/dbus/debug_daemon", "//chromeos/dbus/init", "//chromeos/dbus/shill", @@ -19,18 +21,9 @@ ] deps = [ "//base", - "//chromeos/dbus/easy_unlock", - "//chromeos/dbus/fwupd", "//chromeos/dbus/util", - "//components/account_id", - "//components/device_event_log", - "//components/policy/proto", - "//net", - "//url", ] sources = [ - "dbus_clients_browser.cc", - "dbus_clients_browser.h", "dbus_thread_manager.cc", "dbus_thread_manager.h", ]
diff --git a/chromeos/dbus/dbus_clients_browser.cc b/chromeos/dbus/dbus_clients_browser.cc deleted file mode 100644 index afbf65b..0000000 --- a/chromeos/dbus/dbus_clients_browser.cc +++ /dev/null
@@ -1,20 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "chromeos/dbus/dbus_clients_browser.h" - -#include "base/check.h" -#include "chromeos/dbus/dbus_thread_manager.h" - -namespace chromeos { - -DBusClientsBrowser::DBusClientsBrowser(bool use_real_clients) {} - -DBusClientsBrowser::~DBusClientsBrowser() = default; - -void DBusClientsBrowser::Initialize(dbus::Bus* system_bus) { - DCHECK(DBusThreadManager::IsInitialized()); -} - -} // namespace chromeos
diff --git a/chromeos/dbus/dbus_clients_browser.h b/chromeos/dbus/dbus_clients_browser.h deleted file mode 100644 index ffc1c00..0000000 --- a/chromeos/dbus/dbus_clients_browser.h +++ /dev/null
@@ -1,33 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CHROMEOS_DBUS_DBUS_CLIENTS_BROWSER_H_ -#define CHROMEOS_DBUS_DBUS_CLIENTS_BROWSER_H_ - -#include "base/component_export.h" - -namespace dbus { -class Bus; -} - -namespace chromeos { - -// TODO(jamescook): Delete this class. http://crbug.com/647367 -class COMPONENT_EXPORT(CHROMEOS_DBUS) DBusClientsBrowser { - public: - // Creates real implementations if |use_real_clients| is true and fakes - // otherwise. Fakes are used when running on Linux desktop and in tests. - explicit DBusClientsBrowser(bool use_real_clients); - - DBusClientsBrowser(const DBusClientsBrowser&) = delete; - DBusClientsBrowser& operator=(const DBusClientsBrowser&) = delete; - - ~DBusClientsBrowser(); - - void Initialize(dbus::Bus* system_bus); -}; - -} // namespace chromeos - -#endif // CHROMEOS_DBUS_DBUS_CLIENTS_BROWSER_H_
diff --git a/chromeos/dbus/dbus_thread_manager.cc b/chromeos/dbus/dbus_thread_manager.cc index 5725aa7..48c56b0b 100644 --- a/chromeos/dbus/dbus_thread_manager.cc +++ b/chromeos/dbus/dbus_thread_manager.cc
@@ -8,24 +8,16 @@ #include <utility> #include "base/logging.h" -#include "base/memory/ptr_util.h" -#include "base/message_loop/message_pump_type.h" #include "chromeos/dbus/common/dbus_client.h" -#include "chromeos/dbus/dbus_clients_browser.h" #include "chromeos/dbus/shill/shill_clients.h" namespace chromeos { static DBusThreadManager* g_dbus_thread_manager = nullptr; -DBusThreadManager::DBusThreadManager() - : clients_browser_( - std::make_unique<DBusClientsBrowser>(use_real_clients_)) {} +DBusThreadManager::DBusThreadManager() = default; -DBusThreadManager::~DBusThreadManager() { - // Delete all D-Bus clients before shutting down the system bus. - clients_browser_.reset(); -} +DBusThreadManager::~DBusThreadManager() = default; void DBusThreadManager::InitializeClients() { // Some clients call DBusThreadManager::Get() during initialization. @@ -35,10 +27,7 @@ // that require Shill clients. https://crbug.com/948390. shill_clients::Initialize(GetSystemBus()); - if (clients_browser_) - clients_browser_->Initialize(GetSystemBus()); - - if (use_real_clients_) + if (!IsUsingFakes()) VLOG(1) << "DBusThreadManager initialized for ChromeOS"; else VLOG(1) << "DBusThreadManager created for testing";
diff --git a/chromeos/dbus/dbus_thread_manager.h b/chromeos/dbus/dbus_thread_manager.h index 45f5315..0c90633c 100644 --- a/chromeos/dbus/dbus_thread_manager.h +++ b/chromeos/dbus/dbus_thread_manager.h
@@ -5,21 +5,11 @@ #ifndef CHROMEOS_DBUS_DBUS_THREAD_MANAGER_H_ #define CHROMEOS_DBUS_DBUS_THREAD_MANAGER_H_ -#include <memory> -#include <string> - -#include "base/callback.h" #include "base/component_export.h" #include "chromeos/dbus/init/dbus_thread_manager_base.h" namespace chromeos { -// Style Note: Clients are sorted by names. -class DBusClientsBrowser; - -// THIS CLASS IS BEING DEPRECATED. See README.md for guidelines and -// https://crbug.com/647367 for details. -// // Ash implementation of DBusThreadManagerBase. class COMPONENT_EXPORT(CHROMEOS_DBUS) DBusThreadManager : public DBusThreadManagerBase { @@ -27,8 +17,6 @@ // Sets the global instance. Must be called before any calls to Get(). // We explicitly initialize and shut down the global object, rather than // making it a Singleton, to ensure clean startup and shutdown. - // This will initialize real or fake DBusClients depending on command-line - // arguments and whether this process runs in a ChromeOS environment. static void Initialize(); // Returns true if DBusThreadManager has been initialized. Call this to @@ -47,12 +35,10 @@ const DBusThreadManager& operator=(const DBusThreadManager&) = delete; ~DBusThreadManager() override; - // Initializes all currently stored DBusClients with the system bus and - // performs additional setup. + // Performs additional setup. + // TODO(https://crbug.com/948390): Remove after shill client initialization + // moves into chrome/browser. void InitializeClients(); - - // Owns the clients. - std::unique_ptr<DBusClientsBrowser> clients_browser_; }; } // namespace chromeos
diff --git a/chromeos/dbus/init/dbus_thread_manager_base.h b/chromeos/dbus/init/dbus_thread_manager_base.h index 7819aa2..462663e 100644 --- a/chromeos/dbus/init/dbus_thread_manager_base.h +++ b/chromeos/dbus/init/dbus_thread_manager_base.h
@@ -41,6 +41,7 @@ const DBusThreadManagerBase& operator=(const DBusThreadManagerBase&) = delete; virtual ~DBusThreadManagerBase(); + private: // Whether to use real or fake dbus clients. const bool use_real_clients_;
diff --git a/chromeos/tast_control.gni b/chromeos/tast_control.gni index 7b8e1902..fe74a2c 100644 --- a/chromeos/tast_control.gni +++ b/chromeos/tast_control.gni
@@ -257,9 +257,6 @@ # http://crbug.com/1335213 "arc.WindowState.clamshell", - # http://crbug.com/1338201 - "ui.ChromeCrashLoggedIn.gpu_process_breakpad", - # http://crbug.com/1309278 "policy.ChromeOsLockOnIdleSuspend",
diff --git a/components/autofill/android/java/res/layout/autofill_dropdown_item_refresh.xml b/components/autofill/android/java/res/layout/autofill_dropdown_item_refresh.xml index 64457f5..9acf6959 100644 --- a/components/autofill/android/java/res/layout/autofill_dropdown_item_refresh.xml +++ b/components/autofill/android/java/res/layout/autofill_dropdown_item_refresh.xml
@@ -16,6 +16,15 @@ android:orientation="horizontal" tools:ignore="UnusedResources" > + <ImageView + android:id="@+id/start_dropdown_icon" + android:layout_width="@dimen/autofill_dropdown_refresh_icon_width" + android:layout_height="@dimen/autofill_dropdown_refresh_icon_height" + android:scaleType="centerInside" + android:layout_marginStart="0dp" + android:layout_marginEnd="@dimen/autofill_dropdown_refresh_icon_margin" + tools:ignore="ContentDescription" /> + <!-- TODO(crbug.com/903554): Remove the fixed layout_height in favor of an equivalent line height and padding. --> <LinearLayout
diff --git a/components/autofill/android/java/src/org/chromium/components/autofill/AutofillDropdownAdapter.java b/components/autofill/android/java/src/org/chromium/components/autofill/AutofillDropdownAdapter.java index 2ff01d38..59f8a93 100644 --- a/components/autofill/android/java/src/org/chromium/components/autofill/AutofillDropdownAdapter.java +++ b/components/autofill/android/java/src/org/chromium/components/autofill/AutofillDropdownAdapter.java
@@ -90,8 +90,16 @@ // For refreshed layout, ignore the return value as we don't need to adjust the height // of the view. populateItemTagView(item, layout); + // Set the visibility of the start/end icons. + layout.findViewById(R.id.start_dropdown_icon) + .setVisibility(item.isIconAtStart() ? View.VISIBLE : View.GONE); + layout.findViewById(R.id.end_dropdown_icon) + .setVisibility(item.isIconAtStart() ? View.GONE : View.VISIBLE); ImageView iconView = - populateIconView((ImageView) layout.findViewById(R.id.end_dropdown_icon), item); + populateIconView((ImageView) (item.isIconAtStart() + ? layout.findViewById(R.id.start_dropdown_icon) + : layout.findViewById(R.id.end_dropdown_icon)), + item); if (iconView != null) { iconView.setLayoutParams(getSizeParamsForIconView(iconView, item)); } @@ -188,8 +196,10 @@ ImageView iconViewEnd = (ImageView) layout.findViewById(R.id.end_dropdown_icon); if (item.isIconAtStart()) { iconViewEnd.setVisibility(View.GONE); + iconViewStart.setVisibility(View.VISIBLE); } else { iconViewStart.setVisibility(View.GONE); + iconViewEnd.setVisibility(View.VISIBLE); } ImageView iconView =
diff --git a/components/autofill/core/browser/autofill_form_test_utils.cc b/components/autofill/core/browser/autofill_form_test_utils.cc index 3acab8e9..c58f17d 100644 --- a/components/autofill/core/browser/autofill_form_test_utils.cc +++ b/components/autofill/core/browser/autofill_form_test_utils.cc
@@ -115,6 +115,7 @@ ff.is_autofilled = dd.is_autofilled.value_or(false); ff.origin = dd.origin.value_or(f.main_frame_origin); ff.should_autocomplete = dd.should_autocomplete; + ff.properties_mask = dd.properties_mask; f.fields.push_back(ff); } return f;
diff --git a/components/autofill/core/browser/autofill_form_test_utils.h b/components/autofill/core/browser/autofill_form_test_utils.h index 8da4714..498bfdf 100644 --- a/components/autofill/core/browser/autofill_form_test_utils.h +++ b/components/autofill/core/browser/autofill_form_test_utils.h
@@ -47,6 +47,7 @@ absl::optional<bool> is_autofilled; absl::optional<url::Origin> origin; std::vector<SelectOption> select_options = {}; + FieldPropertiesMask properties_mask = 0; }; // Attributes provided to the test form.
diff --git a/components/autofill/core/browser/autofill_suggestion_generator.cc b/components/autofill/core/browser/autofill_suggestion_generator.cc index 248118f..d409d25 100644 --- a/components/autofill/core/browser/autofill_suggestion_generator.cc +++ b/components/autofill/core/browser/autofill_suggestion_generator.cc
@@ -409,7 +409,10 @@ if (image) suggestion.custom_icon = *image; } - +#if BUILDFLAG(IS_ANDROID) + // The card art icon should always be shown at the start of the suggestion. + suggestion.is_icon_at_start = true; +#endif // BUILDFLAG(IS_ANDROID) return suggestion; }
diff --git a/components/autofill/core/browser/browser_autofill_manager.cc b/components/autofill/core/browser/browser_autofill_manager.cc index 1df3392..8d9e0641 100644 --- a/components/autofill/core/browser/browser_autofill_manager.cc +++ b/components/autofill/core/browser/browser_autofill_manager.cc
@@ -695,8 +695,12 @@ form_for_autocomplete.fields[i].should_autocomplete = false; } + // If the field was edited by the user and there existed an autofillable + // value for the field, log whether the value on submission is same as the + // autofillable value. if (submitted_form->field(i) - ->value_not_autofilled_over_existing_value_hash()) { + ->value_not_autofilled_over_existing_value_hash() && + (submitted_form->field(i)->properties_mask & kUserTyped)) { // Compare and record if the currently filled value is same as the // non-empty value that was to be autofilled in the field. AutofillMetrics:: @@ -1922,6 +1926,8 @@ // case. if (base::FeatureList::IsEnabled( features::kAutofillPreventOverridingPrefilledValues)) { + form_structure->field(i) + ->set_value_not_autofilled_over_existing_value_hash(absl::nullopt); if (form.fields[i].form_control_type != "select-one" && !form.fields[i].value.empty() && !has_override && !FormFieldData::DeepEqual(form.fields[i], field)) { @@ -1933,18 +1939,16 @@ optional_cvc ? *optional_cvc : kEmptyCvc, action, &unused_failure_to_fill); if (action == mojom::RendererFormDataAction::kFill && - !fill_value.empty() && fill_value != form.fields[i].value) { - // Save the value that was supposed to be autofilled for this field. + !fill_value.empty() && fill_value != form.fields[i].value && + !form_structure->field(i)->value.empty()) { + // Save the value that was supposed to be autofilled for this field if + // the field contained an initial value. form_structure->field(i) ->set_value_not_autofilled_over_existing_value_hash( base::FastHash(base::UTF16ToUTF8(fill_value))); } continue; } - - // Clear out the value in case the autofill happens for the field. - form_structure->field(i) - ->set_value_not_autofilled_over_existing_value_hash(absl::nullopt); } // Do not fill fields that have been edited by the user, except if the field
diff --git a/components/autofill/core/browser/data_model/autofill_profile_comparator_unittest.cc b/components/autofill/core/browser/data_model/autofill_profile_comparator_unittest.cc index 129254b..5f547ac 100644 --- a/components/autofill/core/browser/data_model/autofill_profile_comparator_unittest.cc +++ b/components/autofill/core/browser/data_model/autofill_profile_comparator_unittest.cc
@@ -1386,20 +1386,30 @@ // Checks for various scenarios for determining mergeability of profiles w.r.t. // the state. TEST_P(AutofillProfileComparatorTest, CheckStatesMergeability) { + // |kAutofillEnableSupportForMoreStructureInAddresses| is not compatible with + // AlternativeStateNameMap merging logic. + if (structured_addresses_enabled_) + return; + base::test::ScopedFeatureList feature; feature.InitAndEnableFeature( autofill::features::kAutofillUseAlternativeStateNameMap); autofill::test::ClearAlternativeStateNameMapForTesting(); - autofill::test::PopulateAlternativeStateNameMapForTesting(); + autofill::test::PopulateAlternativeStateNameMapForTesting( + "DE", "RandomState", + {{.canonical_name = "RandomState", + .abbreviations = {"RS"}, + .alternative_names = {"AlternateRandomState"}}}); AutofillProfile empty = CreateProfileWithAddress("", "", "", "", "", "DE"); - AutofillProfile p1 = CreateProfileWithAddress("", "", "", "Bayern", "", "DE"); + AutofillProfile p1 = + CreateProfileWithAddress("", "", "", "RandomState", "", "DE"); AutofillProfile p2 = CreateProfileWithAddress("", "", "", "Random", "", "DE"); - AutofillProfile p3 = - CreateProfileWithAddress("", "", "", "Bayern - BY - Bavaria", "", "DE"); + AutofillProfile p3 = CreateProfileWithAddress( + "", "", "", "RandomState - RS - AlternateRandomState", "", "DE"); AutofillProfile p4 = - CreateProfileWithAddress("", "", "", "Bavaria", "", "DE"); + CreateProfileWithAddress("", "", "", "AlternateRandomState", "", "DE"); EXPECT_TRUE(comparator_.HaveMergeableAddresses(empty, empty)); EXPECT_TRUE(comparator_.HaveMergeableAddresses(p1, empty));
diff --git a/components/autofill/core/browser/metrics/autofill_metrics.cc b/components/autofill/core/browser/metrics/autofill_metrics.cc index a02cffe..ef58aa9 100644 --- a/components/autofill/core/browser/metrics/autofill_metrics.cc +++ b/components/autofill/core/browser/metrics/autofill_metrics.cc
@@ -3598,7 +3598,7 @@ void AutofillMetrics:: LogIsValueNotAutofilledOverExistingValueSameAsSubmittedValue(bool is_same) { base::UmaHistogramBoolean( - "Autofill.IsValueNotAutofilledOverExistingValueSameAsSubmittedValue", + "Autofill.IsValueNotAutofilledOverExistingValueSameAsSubmittedValue2", is_same); }
diff --git a/components/autofill/core/browser/metrics/autofill_metrics_unittest.cc b/components/autofill/core/browser/metrics/autofill_metrics_unittest.cc index 0ec29043..d4e0825 100644 --- a/components/autofill/core/browser/metrics/autofill_metrics_unittest.cc +++ b/components/autofill/core/browser/metrics/autofill_metrics_unittest.cc
@@ -11863,22 +11863,22 @@ // Tests the following 4 cases when |kAutofillPreventOverridingPrefilledValues| // is enabled: -// 1. The field is not autofilled since it has a prefilled value but the value +// 1. The field is not autofilled since it has an initial value but the value // is edited before the form submission and is same as the value that was // to be autofilled in the field. -// |Autofill.IsValueNotAutofilledOverExistingValueSameAsSubmittedValue| +// |Autofill.IsValueNotAutofilledOverExistingValueSameAsSubmittedValue2| // should emit true for this case. -// 2. The field is not autofilled since it has a prefilled value but the value +// 2. The field is not autofilled since it has an initial value but the value // is edited before the form submission and is different than the value that // was to be autofilled in the field. -// |Autofill.IsValueNotAutofilledOverExistingValueSameAsSubmittedValue| +// |Autofill.IsValueNotAutofilledOverExistingValueSameAsSubmittedValue2| // should emit false for this case. -// 3. The field had a prefilled value that was similar to the value to be +// 3. The field had an initial value that was similar to the value to be // autofilled in the field. -// |Autofill.IsValueNotAutofilledOverExistingValueSameAsSubmittedValue| +// |Autofill.IsValueNotAutofilledOverExistingValueSameAsSubmittedValue2| // should not record anything in this case. // 4. Selection fields are always overridden by Autofill. -// |Autofill.IsValueNotAutofilledOverExistingValueSameAsSubmittedValue| +// |Autofill.IsValueNotAutofilledOverExistingValueSameAsSubmittedValue2| // should not record anything in this case. TEST_F(AutofillMetricsTest, IsValueNotAutofilledOverExistingValueSameAsSubmittedValue) { @@ -11889,20 +11889,23 @@ FormData form = test::GetFormData( {.description_for_logging = "AutofilledStateFieldSource", - .fields = {{.role = ServerFieldType::NAME_FULL}, - {.role = ServerFieldType::ADDRESS_HOME_CITY, - .value = u"Sacremento"}, // Case #1 - {.role = ServerFieldType::ADDRESS_HOME_STATE, - .value = u"CA", - .form_control_type = "select-one", - .select_options = {{u"TN", u"Tennesse"}, - {u"CA", u"California"}, - {u"WA", u"Washington DC"}}}, // Case #4 - {.role = ServerFieldType::ADDRESS_HOME_ZIP, - .value = u"00000"}, // Case #2 - {.role = ServerFieldType::PHONE_HOME_WHOLE_NUMBER, - .value = u"12345678901"}, // Case #3 - {.role = ServerFieldType::ADDRESS_HOME_COUNTRY}}}); + .fields = { + {.role = ServerFieldType::NAME_FULL}, + {.role = ServerFieldType::ADDRESS_HOME_CITY, + .value = u"Sacremento", + .properties_mask = FieldPropertiesFlags::kUserTyped}, // Case #1 + {.role = ServerFieldType::ADDRESS_HOME_STATE, + .value = u"CA", + .form_control_type = "select-one", + .select_options = {{u"TN", u"Tennesse"}, + {u"CA", u"California"}, + {u"WA", u"Washington DC"}}}, // Case #4 + {.role = ServerFieldType::ADDRESS_HOME_ZIP, + .value = u"00000", + .properties_mask = FieldPropertiesFlags::kUserTyped}, // Case #2 + {.role = ServerFieldType::PHONE_HOME_WHOLE_NUMBER, + .value = u"12345678901"}, // Case #3 + {.role = ServerFieldType::ADDRESS_HOME_COUNTRY}}}); std::vector<ServerFieldType> heuristic_types = { NAME_FULL, ADDRESS_HOME_CITY, ADDRESS_HOME_STATE, @@ -11912,7 +11915,8 @@ ADDRESS_HOME_ZIP, PHONE_HOME_WHOLE_NUMBER, ADDRESS_HOME_COUNTRY}; // Simulate having seen this form on page load. - autofill_manager().AddSeenForm(form, heuristic_types, server_types); + autofill_manager().AddSeenForm(form, heuristic_types, server_types, + /*preserve_values_in_form_structure=*/true); autofill_manager().OnAskForValuesToFillTest(form, form.fields[0]); autofill_manager().DidShowSuggestions( @@ -11942,10 +11946,10 @@ SubmissionSource::FORM_SUBMISSION); histogram_tester.ExpectBucketCount( - "Autofill.IsValueNotAutofilledOverExistingValueSameAsSubmittedValue", + "Autofill.IsValueNotAutofilledOverExistingValueSameAsSubmittedValue2", true, 1); histogram_tester.ExpectBucketCount( - "Autofill.IsValueNotAutofilledOverExistingValueSameAsSubmittedValue", + "Autofill.IsValueNotAutofilledOverExistingValueSameAsSubmittedValue2", false, 1); }
diff --git a/components/autofill/core/browser/test_browser_autofill_manager.cc b/components/autofill/core/browser/test_browser_autofill_manager.cc index 92cf2871..da912c1 100644 --- a/components/autofill/core/browser/test_browser_autofill_manager.cc +++ b/components/autofill/core/browser/test_browser_autofill_manager.cc
@@ -107,7 +107,8 @@ void TestBrowserAutofillManager::AddSeenForm( const FormData& form, const std::vector<ServerFieldType>& heuristic_types, - const std::vector<ServerFieldType>& server_types) { + const std::vector<ServerFieldType>& server_types, + bool preserve_values_in_form_structure) { std::vector<std::vector<std::pair<PatternSource, ServerFieldType>>> all_heuristic_types; @@ -118,21 +119,24 @@ return {{GetActivePatternSource(), type}}; }); - AddSeenForm(form, all_heuristic_types, server_types); + AddSeenForm(form, all_heuristic_types, server_types, + preserve_values_in_form_structure); } void TestBrowserAutofillManager::AddSeenForm( const FormData& form, const std::vector<std::vector<std::pair<PatternSource, ServerFieldType>>>& heuristic_types, - const std::vector<ServerFieldType>& server_types) { - FormData empty_form = form; - for (auto& field : empty_form.fields) { + const std::vector<ServerFieldType>& server_types, + bool preserve_values_in_form_structure) { + FormData form_with_empty_fields = form; + for (auto& field : form_with_empty_fields.fields) { field.value = std::u16string(); } std::unique_ptr<TestFormStructure> form_structure = - std::make_unique<TestFormStructure>(empty_form); + std::make_unique<TestFormStructure>( + preserve_values_in_form_structure ? form : form_with_empty_fields); form_structure->SetFieldTypes(heuristic_types, server_types); form_structure->identify_sections_for_testing(); AddSeenFormStructure(std::move(form_structure));
diff --git a/components/autofill/core/browser/test_browser_autofill_manager.h b/components/autofill/core/browser/test_browser_autofill_manager.h index 422bf0e..d9078bf 100644 --- a/components/autofill/core/browser/test_browser_autofill_manager.h +++ b/components/autofill/core/browser/test_browser_autofill_manager.h
@@ -58,13 +58,15 @@ void AddSeenForm(const FormData& form, const std::vector<ServerFieldType>& heuristic_types, - const std::vector<ServerFieldType>& server_types); + const std::vector<ServerFieldType>& server_types, + bool preserve_values_in_form_structure = false); void AddSeenForm( const FormData& form, const std::vector<std::vector<std::pair<PatternSource, ServerFieldType>>>& heuristic_types, - const std::vector<ServerFieldType>& server_types); + const std::vector<ServerFieldType>& server_types, + bool preserve_values_in_form_structure); void AddSeenFormStructure(std::unique_ptr<FormStructure> form_structure);
diff --git a/components/autofill/core/browser/ui/suggestion.h b/components/autofill/core/browser/ui/suggestion.h index 7216790b..1368f10a 100644 --- a/components/autofill/core/browser/ui/suggestion.h +++ b/components/autofill/core/browser/ui/suggestion.h
@@ -131,6 +131,10 @@ // The url for the custom icon. This is used by android to fetch the image as // android does not support gfx::Image directly. GURL custom_icon_url; + + // On Android, the icon can be at the start of the suggestion before the label + // or at the end of the label. + bool is_icon_at_start = false; #endif // BUILDFLAG(IS_ANDROID) // TODO(crbug.com/1019660): Identify icons with enum instead of strings.
diff --git a/components/autofill_assistant/browser/BUILD.gn b/components/autofill_assistant/browser/BUILD.gn index ef62dac..a378876 100644 --- a/components/autofill_assistant/browser/BUILD.gn +++ b/components/autofill_assistant/browser/BUILD.gn
@@ -266,6 +266,11 @@ "starter.h", "starter_heuristic.cc", "starter_heuristic.h", + "starter_heuristic_configs/finch_starter_heuristic_config.cc", + "starter_heuristic_configs/finch_starter_heuristic_config.h", + "starter_heuristic_configs/legacy_starter_heuristic_config.cc", + "starter_heuristic_configs/legacy_starter_heuristic_config.h", + "starter_heuristic_configs/starter_heuristic_config.h", "starter_platform_delegate.h", "startup_util.cc", "startup_util.h", @@ -550,6 +555,8 @@ "service/service_impl_unittest.cc", "service/service_request_sender_impl_unittest.cc", "service/service_request_sender_local_impl_unittest.cc", + "starter_heuristic_configs/finch_starter_heuristic_config_unittest.cc", + "starter_heuristic_configs/legacy_starter_heuristic_config_unittest.cc", "starter_heuristic_unittest.cc", "starter_unittest.cc", "startup_util_unittest.cc",
diff --git a/components/autofill_assistant/browser/features.cc b/components/autofill_assistant/browser/features.cc index 22ef1476..48dafce9 100644 --- a/components/autofill_assistant/browser/features.cc +++ b/components/autofill_assistant/browser/features.cc
@@ -105,7 +105,19 @@ const base::Feature kAutofillAssistantProactiveHelp{ "AutofillAssistantProactiveHelp", base::FEATURE_ENABLED_BY_DEFAULT}; -// Used to configure the start heuristics for +// Used to configure URL heuristics for upcoming new features. +extern const base::Feature kAutofillAssistantUrlHeuristic1{ + "AutofillAssistantUrlHeuristic1", base::FEATURE_DISABLED_BY_DEFAULT}; +extern const base::Feature kAutofillAssistantUrlHeuristic2{ + "AutofillAssistantUrlHeuristic2", base::FEATURE_DISABLED_BY_DEFAULT}; +extern const base::Feature kAutofillAssistantUrlHeuristic3{ + "AutofillAssistantUrlHeuristic3", base::FEATURE_DISABLED_BY_DEFAULT}; +extern const base::Feature kAutofillAssistantUrlHeuristic4{ + "AutofillAssistantUrlHeuristic4", base::FEATURE_DISABLED_BY_DEFAULT}; +extern const base::Feature kAutofillAssistantUrlHeuristic5{ + "AutofillAssistantUrlHeuristic5", base::FEATURE_DISABLED_BY_DEFAULT}; + +// Legacy URL heuristics. Used to configure the start heuristics for // |kAutofillAssistantInCctTriggering| and/or // |kAutofillAssistantInTabTriggering|. const base::Feature kAutofillAssistantUrlHeuristics{
diff --git a/components/autofill_assistant/browser/features.h b/components/autofill_assistant/browser/features.h index 6ae675f..1a5f159 100644 --- a/components/autofill_assistant/browser/features.h +++ b/components/autofill_assistant/browser/features.h
@@ -30,6 +30,11 @@ extern const base::Feature kAutofillAssistantLoadDFMForTriggerScripts; extern const base::Feature kAutofillAssistantProactiveHelp; extern const base::Feature kAutofillAssistantSignGetActionsRequests; +extern const base::Feature kAutofillAssistantUrlHeuristic1; +extern const base::Feature kAutofillAssistantUrlHeuristic2; +extern const base::Feature kAutofillAssistantUrlHeuristic3; +extern const base::Feature kAutofillAssistantUrlHeuristic4; +extern const base::Feature kAutofillAssistantUrlHeuristic5; extern const base::Feature kAutofillAssistantUrlHeuristics; extern const base::Feature kAutofillAssistantVerifyGetActionsResponses;
diff --git a/components/autofill_assistant/browser/script_parameters.cc b/components/autofill_assistant/browser/script_parameters.cc index e2c9baf..c9272204 100644 --- a/components/autofill_assistant/browser/script_parameters.cc +++ b/components/autofill_assistant/browser/script_parameters.cc
@@ -115,6 +115,7 @@ ScriptParameters::ScriptParameters() = default; ScriptParameters::~ScriptParameters() = default; +ScriptParameters& ScriptParameters::operator=(ScriptParameters&&) = default; void ScriptParameters::MergeWith(const ScriptParameters& another) { for (const auto& param : another.parameters_) {
diff --git a/components/autofill_assistant/browser/script_parameters.h b/components/autofill_assistant/browser/script_parameters.h index a6112be..9d357adf 100644 --- a/components/autofill_assistant/browser/script_parameters.h +++ b/components/autofill_assistant/browser/script_parameters.h
@@ -24,6 +24,7 @@ ~ScriptParameters(); ScriptParameters(const ScriptParameters&) = delete; ScriptParameters& operator=(const ScriptParameters&) = delete; + ScriptParameters& operator=(ScriptParameters&&); // Merges |another| into this. Does not overwrite existing values. void MergeWith(const ScriptParameters& another);
diff --git a/components/autofill_assistant/browser/starter.cc b/components/autofill_assistant/browser/starter.cc index 10bc943..2b97e5a 100644 --- a/components/autofill_assistant/browser/starter.cc +++ b/components/autofill_assistant/browser/starter.cc
@@ -26,6 +26,7 @@ #include "components/autofill_assistant/browser/service/service_request_sender_impl.h" #include "components/autofill_assistant/browser/service/service_request_sender_local_impl.h" #include "components/autofill_assistant/browser/service/simple_url_loader_factory.h" +#include "components/autofill_assistant/browser/starter_heuristic_configs/finch_starter_heuristic_config.h" #include "components/autofill_assistant/browser/switches.h" #include "components/autofill_assistant/browser/trigger_scripts/dynamic_trigger_conditions.h" #include "components/autofill_assistant/browser/trigger_scripts/static_trigger_conditions.h" @@ -65,6 +66,20 @@ const char kEnabledGroupName[] = "Enabled"; const char kExperimentsSyntheticTrial[] = "AutofillAssistantExperimentsTrial"; +// String parameter containing the JSON-encoded parameter dictionary. +const char kUrlHeuristicParametersKey[] = "json_parameters"; +// The 5 URL heuristics features that are defined for future use cases. +constexpr base::FeatureParam<std::string> kUrlHeuristicParams1{ + &features::kAutofillAssistantUrlHeuristic1, kUrlHeuristicParametersKey, ""}; +constexpr base::FeatureParam<std::string> kUrlHeuristicParams2{ + &features::kAutofillAssistantUrlHeuristic2, kUrlHeuristicParametersKey, ""}; +constexpr base::FeatureParam<std::string> kUrlHeuristicParams3{ + &features::kAutofillAssistantUrlHeuristic3, kUrlHeuristicParametersKey, ""}; +constexpr base::FeatureParam<std::string> kUrlHeuristicParams4{ + &features::kAutofillAssistantUrlHeuristic4, kUrlHeuristicParametersKey, ""}; +constexpr base::FeatureParam<std::string> kUrlHeuristicParams5{ + &features::kAutofillAssistantUrlHeuristic5, kUrlHeuristicParametersKey, ""}; + // Creates a service request sender that communicates with a remote endpoint. std::unique_ptr<ServiceRequestSender> CreateRpcTriggerScriptRequestSender( content::BrowserContext* browser_context, @@ -84,15 +99,6 @@ .value_or(false); } -// The heuristic is shared across all instances and initialized on first use. As -// such, we do not support updating the heuristic while Chrome is running. -const scoped_refptr<StarterHeuristic> GetOrCreateStarterHeuristic() { - static const base::NoDestructor<scoped_refptr<StarterHeuristic>> - starter_heuristic( - [] { return base::MakeRefCounted<StarterHeuristic>(); }()); - return *starter_heuristic; -} - // The cache of failed trigger script fetches is shared across all instances and // initialized on first use. base::HashingLRUCache<std::string, base::TimeTicks>* @@ -174,8 +180,21 @@ platform_delegate_(platform_delegate), ukm_recorder_(ukm_recorder), runtime_manager_(runtime_manager), - starter_heuristic_(GetOrCreateStarterHeuristic()), - tick_clock_(tick_clock) {} + starter_heuristic_(base::MakeRefCounted<StarterHeuristic>()), + tick_clock_(tick_clock) { + heuristic_configs_.emplace_back( + std::make_unique<LegacyStarterHeuristicConfig>()); + heuristic_configs_.emplace_back( + std::make_unique<FinchStarterHeuristicConfig>(kUrlHeuristicParams1)); + heuristic_configs_.emplace_back( + std::make_unique<FinchStarterHeuristicConfig>(kUrlHeuristicParams2)); + heuristic_configs_.emplace_back( + std::make_unique<FinchStarterHeuristicConfig>(kUrlHeuristicParams3)); + heuristic_configs_.emplace_back( + std::make_unique<FinchStarterHeuristicConfig>(kUrlHeuristicParams4)); + heuristic_configs_.emplace_back( + std::make_unique<FinchStarterHeuristicConfig>(kUrlHeuristicParams5)); +} Starter::~Starter() = default; @@ -372,18 +391,10 @@ platform_delegate_->GetFeatureModuleInstalled(); bool prev_fetch_trigger_scripts_on_navigation = fetch_trigger_scripts_on_navigation_; - // Note: the feature flag must be the last thing tested in this if-statement, - // to avoid tagging tabs that otherwise don't qualify for in-cct triggering, - // which leads to pollution of our metrics. - fetch_trigger_scripts_on_navigation_ = - proactive_help_setting_enabled && msbb_setting_enabled && - (!platform_delegate_->GetIsWebLayer() || - platform_delegate_->GetIsLoggedIn()) && - ((is_custom_tab_ && platform_delegate_->GetIsTabCreatedByGSA() && - base::FeatureList::IsEnabled( - features::kAutofillAssistantInCCTTriggering)) || - (!is_custom_tab_ && base::FeatureList::IsEnabled( - features::kAutofillAssistantInTabTriggering))); + + starter_heuristic_->InitFromHeuristicConfigs(heuristic_configs_, + platform_delegate_.get()); + fetch_trigger_scripts_on_navigation_ = starter_heuristic_->HasConditionSets(); // If there is a pending startup, re-check that the settings are still // allowing the startup to proceed. If not, cancel the startup.
diff --git a/components/autofill_assistant/browser/starter.h b/components/autofill_assistant/browser/starter.h index 795821e..7926515e 100644 --- a/components/autofill_assistant/browser/starter.h +++ b/components/autofill_assistant/browser/starter.h
@@ -6,6 +6,7 @@ #define COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_STARTER_H_ #include <memory> +#include <vector> #include "base/containers/flat_set.h" #include "base/containers/lru_cache.h" @@ -21,6 +22,8 @@ #include "components/autofill_assistant/browser/public/runtime_manager.h" #include "components/autofill_assistant/browser/service.pb.h" #include "components/autofill_assistant/browser/starter_heuristic.h" +#include "components/autofill_assistant/browser/starter_heuristic_configs/legacy_starter_heuristic_config.h" +#include "components/autofill_assistant/browser/starter_heuristic_configs/starter_heuristic_config.h" #include "components/autofill_assistant/browser/starter_platform_delegate.h" #include "components/autofill_assistant/browser/startup_util.h" #include "components/autofill_assistant/browser/trigger_scripts/trigger_script_coordinator.h" @@ -197,6 +200,7 @@ // from the command line and intended only for debugging and testing. ImplicitTriggeringDebugParametersProto implicit_triggering_debug_parameters_; + std::vector<std::unique_ptr<StarterHeuristicConfig>> heuristic_configs_; bool waiting_for_onboarding_ = false; bool waiting_for_deeplink_navigation_ = false; bool is_custom_tab_ = false; @@ -206,7 +210,7 @@ bool fetch_trigger_scripts_on_navigation_ = false; std::unique_ptr<TriggerContext> pending_trigger_context_; std::unique_ptr<TriggerScriptCoordinator> trigger_script_coordinator_; - const scoped_refptr<StarterHeuristic> starter_heuristic_; + scoped_refptr<StarterHeuristic> starter_heuristic_; const raw_ptr<const base::TickClock> tick_clock_; base::OnceCallback<void(bool success, absl::optional<GURL> url,
diff --git a/components/autofill_assistant/browser/starter_heuristic.cc b/components/autofill_assistant/browser/starter_heuristic.cc index d848a68..1166ce0 100644 --- a/components/autofill_assistant/browser/starter_heuristic.cc +++ b/components/autofill_assistant/browser/starter_heuristic.cc
@@ -24,124 +24,87 @@ namespace autofill_assistant { -// String parameter containing the JSON-encoded parameter dictionary. -const char kJsonParameterDictKey[] = "json_parameters"; - -constexpr base::FeatureParam<std::string> kFieldTrialParams{ - &features::kAutofillAssistantUrlHeuristics, kJsonParameterDictKey, ""}; - -// Array of strings, containing the list of globally denylisted domains. -const char kDenylistedDomainsKey[] = "denylistedDomains"; -// Array of heuristics, each with its own intent and conditions. -const char kHeuristicsKey[] = "heuristics"; -// String. The intent associated with a specific heuristic. -const char kHeuristicIntentKey[] = "intent"; // UrlFilter dictionary. The URL condition set defining a specific intent's -// heuristic. See also components/url_matcher/url_matcher_factory.h +// URL filter. See also components/url_matcher/url_matcher_factory.h const char kHeuristicUrlConditionSetKey[] = "conditionSet"; -StarterHeuristic::StarterHeuristic() { - InitFromTrialParams(); -} - +StarterHeuristic::StarterHeuristic() = default; StarterHeuristic::~StarterHeuristic() = default; -void StarterHeuristic::InitFromTrialParams() { - DCHECK(matcher_id_to_intent_map_.empty()) - << "Called after already initialized"; +void StarterHeuristic::InitFromHeuristicConfigs( + const std::vector<std::unique_ptr<StarterHeuristicConfig>>& configs, + StarterPlatformDelegate* platform_delegate) { + url_matcher_ = std::make_unique<url_matcher::URLMatcher>(); + matcher_id_to_config_map_.clear(); - std::string parameters = kFieldTrialParams.Get(); - if (parameters.empty()) { - VLOG(2) << "Field trial parameter not set"; - return; - } - auto dict = base::JSONReader::ReadAndReturnValueWithError(parameters); - if (!dict.has_value() || !dict->is_dict()) { - VLOG(1) << "Failed to parse field trial params as JSON object: " - << parameters; - if (VLOG_IS_ON(1)) { - if (dict.has_value()) - VLOG(1) << "Expecting a dictionary"; - else - VLOG(1) << dict.error().message << ", line: " << dict.error().line - << ", col: " << dict.error().column; - } - return; - } - - // Read mandatory list of heuristics. - const base::Value::List* heuristics = - dict->GetDict().FindList(kHeuristicsKey); - if (heuristics == nullptr) { - VLOG(1) << "Field trial params did not contain heuristics"; - return; - } url_matcher::URLMatcherConditionSet::Vector condition_sets; - base::flat_map<base::MatcherStringPattern::ID, std::string> mapping; + base::flat_map<base::MatcherStringPattern::ID, HeuristicConfigEntry> mapping; base::MatcherStringPattern::ID next_condition_set_id = 0; - for (const auto& heuristic : *heuristics) { - auto* intent = - heuristic.FindKeyOfType(kHeuristicIntentKey, base::Value::Type::STRING); - auto* url_conditions = heuristic.FindKeyOfType( - kHeuristicUrlConditionSetKey, base::Value::Type::DICTIONARY); - if (!intent || !url_conditions) { - VLOG(1) << "Heuristic did not contain intent or url_conditions"; - return; - } - - std::string error; - condition_sets.emplace_back( - url_matcher::URLMatcherFactory::CreateFromURLFilterDictionary( - url_matcher_.condition_factory(), url_conditions->GetDict(), - next_condition_set_id, &error)); - if (!error.empty()) { - VLOG(1) << "Error pasing url conditions: " << error; - return; - } - mapping[next_condition_set_id++] = *intent->GetIfString(); - } - - // Read optional list of denylisted domains. - const base::Value::List* denylisted_domains_value = - dict->GetDict().FindList(kDenylistedDomainsKey); - base::flat_set<std::string> denylisted_domains; - if (denylisted_domains_value != nullptr) { - for (const auto& domain : *denylisted_domains_value) { - if (!domain.is_string()) { - VLOG(1) << "Invalid type for denylisted domain"; + for (const auto& config : configs) { + for (const auto& condition_set : + config->GetConditionSetsForClientState(platform_delegate)) { + if (!condition_set.is_dict()) { + LOG(ERROR) << "Invalid heuristic config: expected a dictionary for " + "each condition set, but got " + << base::Value::GetTypeName(condition_set.type()); return; } - denylisted_domains.insert(*domain.GetIfString()); + auto* url_conditions = condition_set.FindKeyOfType( + kHeuristicUrlConditionSetKey, base::Value::Type::DICTIONARY); + if (!url_conditions) { + VLOG(1) << "Condition dict did not contain a value for 'conditionSet'"; + return; + } + + std::string error; + condition_sets.emplace_back( + url_matcher::URLMatcherFactory::CreateFromURLFilterDictionary( + url_matcher_->condition_factory(), url_conditions->GetDict(), + next_condition_set_id, &error)); + if (!error.empty()) { + VLOG(1) << "Error pasing url conditions: " << error; + return; + } + mapping.insert( + std::make_pair(next_condition_set_id++, + HeuristicConfigEntry(config->GetIntent(), + config->GetDenylistedDomains()))); } } - VLOG(2) << "Read " << condition_sets.size() << " condition sets and " - << denylisted_domains.size() << " denylisted domains."; - denylisted_domains_ = std::move(denylisted_domains); - url_matcher_.AddConditionSets(condition_sets); - matcher_id_to_intent_map_ = std::move(mapping); + VLOG(2) << "Read " << condition_sets.size() << " condition sets from " + << configs.size() << " configs."; + url_matcher_->AddConditionSets(condition_sets); + matcher_id_to_config_map_ = std::move(mapping); +} + +bool StarterHeuristic::HasConditionSets() const { + return !matcher_id_to_config_map_.empty(); } base::flat_set<std::string> StarterHeuristic::IsHeuristicMatch( - const GURL& url) const { + const GURL& url, + base::flat_map<base::MatcherStringPattern::ID, HeuristicConfigEntry> + copied_matcher_id_to_config_map) const { base::flat_set<std::string> matching_intents; - if (matcher_id_to_intent_map_.empty() || !url.is_valid()) { + if (copied_matcher_id_to_config_map.empty() || !url.is_valid()) { return matching_intents; } - if (denylisted_domains_.count( - url_utils::GetOrganizationIdentifyingDomain(url)) > 0) { - return matching_intents; - } - - std::set<base::MatcherStringPattern::ID> matches = url_matcher_.MatchURL(url); + std::set<base::MatcherStringPattern::ID> matches = + url_matcher_->MatchURL(url); for (const auto& match : matches) { - auto intent = matcher_id_to_intent_map_.find(match); - if (intent == matcher_id_to_intent_map_.end()) { + auto config = copied_matcher_id_to_config_map.find(match); + if (config == copied_matcher_id_to_config_map.end()) { DCHECK(false); continue; } - matching_intents.emplace(intent->second); + // Skip matches if they are in the denylist of that config. + if (config->second.denylisted_domains.count( + url_utils::GetOrganizationIdentifyingDomain(url)) > 0) { + continue; + } + matching_intents.emplace(config->second.intent); } return matching_intents; } @@ -153,8 +116,16 @@ base::ThreadPool::PostTaskAndReplyWithResult( FROM_HERE, {base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN}, base::BindOnce(&StarterHeuristic::IsHeuristicMatch, - base::RetainedRef(this), url), + base::RetainedRef(this), url, matcher_id_to_config_map_), std::move(callback)); } +StarterHeuristic::HeuristicConfigEntry::HeuristicConfigEntry( + const std::string& _intent, + const base::flat_set<std::string>& _denylisted_domains) + : intent(_intent), denylisted_domains(_denylisted_domains) {} +StarterHeuristic::HeuristicConfigEntry::HeuristicConfigEntry( + const HeuristicConfigEntry&) = default; +StarterHeuristic::HeuristicConfigEntry::~HeuristicConfigEntry() = default; + } // namespace autofill_assistant
diff --git a/components/autofill_assistant/browser/starter_heuristic.h b/components/autofill_assistant/browser/starter_heuristic.h index aa0e602..cacf68c 100644 --- a/components/autofill_assistant/browser/starter_heuristic.h +++ b/components/autofill_assistant/browser/starter_heuristic.h
@@ -11,6 +11,8 @@ #include "base/containers/flat_map.h" #include "base/containers/flat_set.h" #include "base/memory/ref_counted.h" +#include "components/autofill_assistant/browser/starter_heuristic_configs/starter_heuristic_config.h" +#include "components/autofill_assistant/browser/starter_platform_delegate.h" #include "components/url_matcher/url_matcher.h" #include "components/url_matcher/url_matcher_factory.h" #include "url/gurl.h" @@ -27,6 +29,16 @@ StarterHeuristic(const StarterHeuristic&) = delete; StarterHeuristic& operator=(const StarterHeuristic&) = delete; + // (Re-)initializes this starter heuristic from the given set of configs and + // the current client state. + void InitFromHeuristicConfigs( + const std::vector<std::unique_ptr<StarterHeuristicConfig>>& configs, + StarterPlatformDelegate* platform_delegate); + + // Returns true if at least one condition set is available. There is no point + // in running the heuristic otherwise. + bool HasConditionSets() const; + // Runs the heuristic against |url| and invokes the callback with all matching // intents. // @@ -40,6 +52,19 @@ private: friend class base::RefCountedThreadSafe<StarterHeuristic>; friend class StarterHeuristicTest; + + // Corresponds to a particular heuristic config. Used to map URL matcher IDs + // to the originating heuristic config without having to take ownership of + // or otherwise directly interacting with those configs. + struct HeuristicConfigEntry { + HeuristicConfigEntry(const std::string& intent, + const base::flat_set<std::string>& denylisted_domains); + HeuristicConfigEntry(const HeuristicConfigEntry&); + ~HeuristicConfigEntry(); + std::string intent; + base::flat_set<std::string> denylisted_domains; + }; + ~StarterHeuristic(); // Initializes the heuristic from the heuristic trial parameters. If there is @@ -50,20 +75,17 @@ void InitFromTrialParams(); // Runs the heuristic against |url|. Returns all matching intents. - base::flat_set<std::string> IsHeuristicMatch(const GURL& url) const; - - // The set of denylisted domains that will always return false before - // considering any of the intent heuristics. - base::flat_set<std::string> denylisted_domains_; + base::flat_set<std::string> IsHeuristicMatch( + const GURL& url, + base::flat_map<base::MatcherStringPattern::ID, HeuristicConfigEntry> + copied_matcher_id_to_config_map) const; // The URL matcher containing one URLMatcherConditionSet per supported intent. - url_matcher::URLMatcher url_matcher_; + std::unique_ptr<url_matcher::URLMatcher> url_matcher_; - // Arbitrary mapping of matcher IDs to intent strings. This mapping is built - // dynamically to allow the heuristic to work on intents that are otherwise - // unknown to the client. - base::flat_map<base::MatcherStringPattern::ID, std::string> - matcher_id_to_intent_map_; + // Arbitrary mapping of matcher IDs to heuristic configs. + base::flat_map<base::MatcherStringPattern::ID, HeuristicConfigEntry> + matcher_id_to_config_map_; }; } // namespace autofill_assistant
diff --git a/components/autofill_assistant/browser/starter_heuristic_configs/finch_starter_heuristic_config.cc b/components/autofill_assistant/browser/starter_heuristic_configs/finch_starter_heuristic_config.cc new file mode 100644 index 0000000..3e6998105 --- /dev/null +++ b/components/autofill_assistant/browser/starter_heuristic_configs/finch_starter_heuristic_config.cc
@@ -0,0 +1,143 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/autofill_assistant/browser/starter_heuristic_configs/finch_starter_heuristic_config.h" +#include "base/json/json_reader.h" + +namespace autofill_assistant { + +FinchStarterHeuristicConfig::FinchStarterHeuristicConfig( + const base::FeatureParam<std::string>& trial_parameter) { + InitFromTrialParams(trial_parameter); +} + +FinchStarterHeuristicConfig::~FinchStarterHeuristicConfig() = default; + +const std::string& FinchStarterHeuristicConfig::GetIntent() const { + return intent_; +} + +const base::Value::List& +FinchStarterHeuristicConfig::GetConditionSetsForClientState( + StarterPlatformDelegate* platform_delegate) const { + if (platform_delegate->GetIsSupervisedUser()) { + return empty_condition_sets_.GetList(); + } + + if (!platform_delegate->GetProactiveHelpSettingEnabled()) { + return empty_condition_sets_.GetList(); + } + + if (platform_delegate->GetIsCustomTab() && + (!platform_delegate->GetIsTabCreatedByGSA() || + !enabled_in_custom_tabs_)) { + return empty_condition_sets_.GetList(); + } + + if (!platform_delegate->GetIsCustomTab() && + !platform_delegate->GetIsWebLayer() && !enabled_in_regular_tabs_) { + return empty_condition_sets_.GetList(); + } + + if (platform_delegate->GetIsWebLayer() && !enabled_in_weblayer_) { + return empty_condition_sets_.GetList(); + } + + if (!platform_delegate->GetIsLoggedIn() && !enabled_for_signed_out_users_) { + return empty_condition_sets_.GetList(); + } + + if (!platform_delegate->GetMakeSearchesAndBrowsingBetterEnabled() && + !enabled_without_msbb_) { + return empty_condition_sets_.GetList(); + } + + return condition_sets_.GetList(); +} + +const base::flat_set<std::string>& +FinchStarterHeuristicConfig::GetDenylistedDomains() const { + return denylisted_domains_; +} + +absl::optional<base::flat_set<std::string>> +FinchStarterHeuristicConfig::ReadDenylistedDomains( + const base::Value::Dict& dict) const { + const base::Value::List* denylisted_domains_value = + dict.FindList(kDenylistedDomainsKey); + if (!denylisted_domains_value) { + return base::flat_set<std::string>{}; + } + + base::flat_set<std::string> denylisted_domains; + for (const auto& domain : *denylisted_domains_value) { + if (!domain.is_string()) { + VLOG(1) << "Invalid type for denylisted domain"; + return absl::nullopt; + } + denylisted_domains.insert(*domain.GetIfString()); + } + return denylisted_domains; +} + +void FinchStarterHeuristicConfig::InitFromTrialParams( + const base::FeatureParam<std::string>& trial_parameter) { + std::string parameters = trial_parameter.Get(); + if (parameters.empty()) { + VLOG(2) << "Field trial parameter not set"; + return; + } + auto dict = base::JSONReader::ReadAndReturnValueWithError(parameters); + if (!dict.has_value() || !dict->is_dict()) { + VLOG(1) << "Failed to parse field trial params as JSON object: " + << parameters; + if (VLOG_IS_ON(1)) { + if (dict.has_value()) + VLOG(1) << "Expecting a dictionary"; + else + VLOG(1) << dict.error().message << ", line: " << dict.error().line + << ", col: " << dict.error().column; + } + return; + } + + // Read the mandatory intent. + auto* intent = dict->GetDict().FindString(kIntentKey); + if (!intent) { + VLOG(1) << "Dictionary did not contain the intent parameter"; + return; + } + + // Read optional list of denylisted domains. + absl::optional<base::flat_set<std::string>> denylisted_domains = + ReadDenylistedDomains(dict->GetDict()); + if (!denylisted_domains) { + return; + } + + // Read condition sets. + base::Value::List* heuristics = dict->GetDict().FindList(kHeuristicsKey); + if (heuristics == nullptr) { + VLOG(1) << "Field trial params did not contain heuristics"; + return; + } + + // Read optional additional filters. + enabled_in_custom_tabs_ = + dict->GetDict().FindBool(kEnabledInCustomTabsKey).value_or(false); + enabled_in_regular_tabs_ = + dict->GetDict().FindBool(kEnabledInRegularTabsKey).value_or(false); + enabled_in_weblayer_ = + dict->GetDict().FindBool(kEnabledInWeblayerKey).value_or(false); + enabled_for_signed_out_users_ = + dict->GetDict().FindBool(kEnabledForSignedOutUsers).value_or(false); + enabled_without_msbb_ = + dict->GetDict().FindBool(kEnabledWithoutMsbb).value_or(false); + + denylisted_domains_ = std::move(*denylisted_domains); + condition_sets_ = base::Value(std::move(*heuristics)); + intent_.assign(*intent); +} + +} // namespace autofill_assistant
diff --git a/components/autofill_assistant/browser/starter_heuristic_configs/finch_starter_heuristic_config.h b/components/autofill_assistant/browser/starter_heuristic_configs/finch_starter_heuristic_config.h new file mode 100644 index 0000000..c714a60 --- /dev/null +++ b/components/autofill_assistant/browser/starter_heuristic_configs/finch_starter_heuristic_config.h
@@ -0,0 +1,90 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_STARTER_HEURISTIC_CONFIGS_FINCH_STARTER_HEURISTIC_CONFIG_H_ +#define COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_STARTER_HEURISTIC_CONFIGS_FINCH_STARTER_HEURISTIC_CONFIG_H_ + +#include "base/metrics/field_trial_params.h" +#include "components/autofill_assistant/browser/starter_heuristic_configs/starter_heuristic_config.h" + +namespace autofill_assistant { + +// Dictionary key for the list of denylisted domains. +const char kDenylistedDomainsKey[] = "denylistedDomains"; +// Dictionary key for the list of heuristics. +const char kHeuristicsKey[] = "heuristics"; +// Dictionary key for the intent. +const char kIntentKey[] = "intent"; +// Dictionary keys for filters that can't be directly enforced via finch. If not +// specified, these default to false, so at least some of them must be set. In +// addition to the conditions here, supervised accounts are never supported, and +// the proactive setting must be enabled as well. +// Note that only custom tabs created by GSA are supported. +const char kEnabledInCustomTabsKey[] = "enabledInCustomTabs"; +const char kEnabledInRegularTabsKey[] = "enabledInRegularTabs"; +const char kEnabledInWeblayerKey[] = "enabledInWeblayer"; +// Note: signed-in users default to true and need not be configured. +const char kEnabledForSignedOutUsers[] = "enabledForSignedOutUsers"; +// Whether 'make searches and browsing better' is required or not. By default, +// MSBB must be enabled. +const char kEnabledWithoutMsbb[] = "enabledWithoutMsbb"; + +// A heuristic config that is originating from a finch feature parameter. +// The trial parameter must be a JSON object of the following format: +/* + { + "intent": "SOME_INTENT", + "denylistedDomains": ["example.com", ...], + "heuristics": [ + {"conditionSet": {...}}, + {"conditionSet": {...}}, + ... + ], + "enabledInCustomTabs":true, + "enabledInRegularTabs":false, + "enabledInWeblayer":false, + "enabledForSignedOutUsers":true + "enabledWithoutMsbb":false + } +*/ +// The 'intent' parameter is mandatory. All other parameters are optional, but +// at least one conditionSet and one enabled* flag must be set for the config +// to be meaningful. +class FinchStarterHeuristicConfig : public StarterHeuristicConfig { + public: + explicit FinchStarterHeuristicConfig( + const base::FeatureParam<std::string>& trial_parameter); + ~FinchStarterHeuristicConfig() override; + + // Overrides HeuristicConfig: + const std::string& GetIntent() const override; + const base::Value::List& GetConditionSetsForClientState( + StarterPlatformDelegate* platform_delegate) const override; + const base::flat_set<std::string>& GetDenylistedDomains() const override; + + private: + void InitFromTrialParams( + const base::FeatureParam<std::string>& trial_parameter); + + // Returns the list of denylisted domains in |dict|. Returns the empty list + // if the relevant key does not exist in |dict|. Returns absl::nullopt if the + // format of the encountered denylist was invalid. + absl::optional<base::flat_set<std::string>> ReadDenylistedDomains( + const base::Value::Dict& dict) const; + + bool enabled_in_custom_tabs_ = false; + bool enabled_in_regular_tabs_ = false; + bool enabled_in_weblayer_ = false; + bool enabled_for_signed_out_users_ = false; + bool enabled_without_msbb_ = false; + const base::Value empty_condition_sets_ = + base::Value(base::Value::Type::LIST); + std::string intent_; + base::Value condition_sets_ = base::Value(base::Value::Type::LIST); + base::flat_set<std::string> denylisted_domains_; +}; + +} // namespace autofill_assistant + +#endif // COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_STARTER_HEURISTIC_CONFIGS_FINCH_STARTER_HEURISTIC_CONFIG_H_
diff --git a/components/autofill_assistant/browser/starter_heuristic_configs/finch_starter_heuristic_config_unittest.cc b/components/autofill_assistant/browser/starter_heuristic_configs/finch_starter_heuristic_config_unittest.cc new file mode 100644 index 0000000..57eb20a5 --- /dev/null +++ b/components/autofill_assistant/browser/starter_heuristic_configs/finch_starter_heuristic_config_unittest.cc
@@ -0,0 +1,388 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/autofill_assistant/browser/starter_heuristic_configs/finch_starter_heuristic_config.h" +#include "base/json/json_reader.h" +#include "base/test/scoped_feature_list.h" +#include "components/autofill_assistant/browser/fake_starter_platform_delegate.h" +#include "components/autofill_assistant/browser/features.h" + +#include "testing/gmock/include/gmock/gmock.h" + +namespace autofill_assistant { + +using ::testing::ElementsAre; +using ::testing::Eq; +using ::testing::IsEmpty; +using ::testing::SizeIs; +using ::testing::UnorderedElementsAreArray; + +class FinchStarterHeuristicConfigTest : public testing::Test { + public: + FinchStarterHeuristicConfigTest() = default; + ~FinchStarterHeuristicConfigTest() override = default; + + protected: + // Convenience for tests where the contents of the heuristic don't matter. + void InitDefaultHeuristic(const base::Feature& feature, + const std::string& parameter_key) { + scoped_feature_list_ = std::make_unique<base::test::ScopedFeatureList>(); + scoped_feature_list_->InitWithFeaturesAndParameters( + {{feature, {{parameter_key, R"( + { + "intent":"FAKE_INTENT", + "denylistedDomains":["example.com"], + "enabledInCustomTabs":true, + "enabledInRegularTabs":true, + "enabledInWeblayer": true, + "enabledForSignedOutUsers": true, + "heuristics":[ + { + "conditionSet":{ + "urlContains":"something" + } + } + ] + } + )"}}}}, + /* disabled_features = */ {}); + } + + FakeStarterPlatformDelegate fake_platform_delegate_; + + private: + std::unique_ptr<base::test::ScopedFeatureList> scoped_feature_list_; +}; + +TEST_F(FinchStarterHeuristicConfigTest, SmokeTest) { + InitDefaultHeuristic(features::kAutofillAssistantUrlHeuristic1, "some_key"); + + FinchStarterHeuristicConfig config_enabled(base::FeatureParam<std::string>{ + &features::kAutofillAssistantUrlHeuristic1, "some_key", ""}); + EXPECT_THAT( + config_enabled.GetConditionSetsForClientState(&fake_platform_delegate_), + SizeIs(1)); + + // UrlHeuristic2 was not enabled, so this should return the empty list. + FinchStarterHeuristicConfig config_default_disabled( + base::FeatureParam<std::string>{ + &features::kAutofillAssistantUrlHeuristic2, "some_key", ""}); + EXPECT_THAT(config_default_disabled.GetConditionSetsForClientState( + &fake_platform_delegate_), + IsEmpty()); +} + +TEST_F(FinchStarterHeuristicConfigTest, DefaultHeuristicParsedCorrectly) { + InitDefaultHeuristic(features::kAutofillAssistantUrlHeuristic1, "some_key"); + FinchStarterHeuristicConfig config(base::FeatureParam<std::string>{ + &features::kAutofillAssistantUrlHeuristic1, "some_key", ""}); + + EXPECT_THAT(config.GetIntent(), Eq("FAKE_INTENT")); + EXPECT_THAT( + config.GetDenylistedDomains(), + UnorderedElementsAreArray(std::vector<std::string>{"example.com"})); + EXPECT_EQ(config.GetConditionSetsForClientState(&fake_platform_delegate_), + base::JSONReader::Read(R"([ + { + "conditionSet":{ + "urlContains":"something" + } + } + ])") + ->GetList()); +} + +TEST_F(FinchStarterHeuristicConfigTest, DisabledForSupervisedUsers) { + InitDefaultHeuristic(features::kAutofillAssistantUrlHeuristic1, "some_key"); + FinchStarterHeuristicConfig config(base::FeatureParam<std::string>{ + &features::kAutofillAssistantUrlHeuristic1, "some_key", ""}); + + fake_platform_delegate_.is_supervised_user_ = true; + EXPECT_THAT(config.GetConditionSetsForClientState(&fake_platform_delegate_), + IsEmpty()); + + fake_platform_delegate_.is_supervised_user_ = false; + EXPECT_THAT(config.GetConditionSetsForClientState(&fake_platform_delegate_), + SizeIs(1)); +} + +TEST_F(FinchStarterHeuristicConfigTest, DisabledIfProactiveHelpSettingOff) { + InitDefaultHeuristic(features::kAutofillAssistantUrlHeuristic1, "some_key"); + FinchStarterHeuristicConfig config(base::FeatureParam<std::string>{ + &features::kAutofillAssistantUrlHeuristic1, "some_key", ""}); + + fake_platform_delegate_.proactive_help_enabled_ = false; + EXPECT_THAT(config.GetConditionSetsForClientState(&fake_platform_delegate_), + IsEmpty()); + + fake_platform_delegate_.proactive_help_enabled_ = true; + EXPECT_THAT(config.GetConditionSetsForClientState(&fake_platform_delegate_), + SizeIs(1)); +} + +TEST_F(FinchStarterHeuristicConfigTest, FlagsDefaultToFalse) { + auto scoped_feature_list = std::make_unique<base::test::ScopedFeatureList>(); + scoped_feature_list->InitWithFeaturesAndParameters( + {{features::kAutofillAssistantUrlHeuristic1, {{"some_key", R"( + { + "intent":"FAKE_INTENT", + "denylistedDomains":["example.com"], + "heuristics":[ + { + "conditionSet":{ + "urlContains":"something" + } + } + ] + } + )"}}}}, + /* disabled_features = */ {}); + + FinchStarterHeuristicConfig config(base::FeatureParam<std::string>{ + &features::kAutofillAssistantUrlHeuristic1, "some_key", ""}); + EXPECT_THAT(config.GetConditionSetsForClientState(&fake_platform_delegate_), + IsEmpty()); +} + +TEST_F(FinchStarterHeuristicConfigTest, EnabledInCustomTabsOnly) { + auto scoped_feature_list = std::make_unique<base::test::ScopedFeatureList>(); + scoped_feature_list->InitWithFeaturesAndParameters( + {{features::kAutofillAssistantUrlHeuristic1, {{"some_key", R"( + { + "intent":"FAKE_INTENT", + "denylistedDomains":["example.com"], + "heuristics":[ + { + "conditionSet":{ + "urlContains":"something" + } + } + ], + "enabledInCustomTabs":true + } + )"}}}}, + /* disabled_features = */ {}); + + FinchStarterHeuristicConfig config(base::FeatureParam<std::string>{ + &features::kAutofillAssistantUrlHeuristic1, "some_key", ""}); + + fake_platform_delegate_.is_web_layer_ = false; + fake_platform_delegate_.is_tab_created_by_gsa_ = false; + fake_platform_delegate_.is_custom_tab_ = true; + EXPECT_THAT(config.GetConditionSetsForClientState(&fake_platform_delegate_), + IsEmpty()); + + fake_platform_delegate_.is_tab_created_by_gsa_ = true; + fake_platform_delegate_.is_custom_tab_ = true; + EXPECT_THAT(config.GetConditionSetsForClientState(&fake_platform_delegate_), + SizeIs(1)); + + fake_platform_delegate_.is_custom_tab_ = false; + EXPECT_THAT(config.GetConditionSetsForClientState(&fake_platform_delegate_), + IsEmpty()); + + // In reality, these two flags should be mutually exclusive. + fake_platform_delegate_.is_custom_tab_ = true; + fake_platform_delegate_.is_web_layer_ = true; + EXPECT_THAT(config.GetConditionSetsForClientState(&fake_platform_delegate_), + IsEmpty()); +} + +TEST_F(FinchStarterHeuristicConfigTest, EnabledInRegularTabsOnly) { + auto scoped_feature_list = std::make_unique<base::test::ScopedFeatureList>(); + scoped_feature_list->InitWithFeaturesAndParameters( + {{features::kAutofillAssistantUrlHeuristic1, {{"some_key", R"( + { + "intent":"FAKE_INTENT", + "denylistedDomains":["example.com"], + "heuristics":[ + { + "conditionSet":{ + "urlContains":"something" + } + } + ], + "enabledInRegularTabs":true + } + )"}}}}, + /* disabled_features = */ {}); + + FinchStarterHeuristicConfig config(base::FeatureParam<std::string>{ + &features::kAutofillAssistantUrlHeuristic1, "some_key", ""}); + + fake_platform_delegate_.is_custom_tab_ = false; + fake_platform_delegate_.is_web_layer_ = false; + EXPECT_THAT(config.GetConditionSetsForClientState(&fake_platform_delegate_), + SizeIs(1)); + + fake_platform_delegate_.is_custom_tab_ = true; + EXPECT_THAT(config.GetConditionSetsForClientState(&fake_platform_delegate_), + IsEmpty()); + + fake_platform_delegate_.is_custom_tab_ = false; + fake_platform_delegate_.is_web_layer_ = true; + EXPECT_THAT(config.GetConditionSetsForClientState(&fake_platform_delegate_), + IsEmpty()); +} + +TEST_F(FinchStarterHeuristicConfigTest, EnabledInWeblayerOnly) { + auto scoped_feature_list = std::make_unique<base::test::ScopedFeatureList>(); + scoped_feature_list->InitWithFeaturesAndParameters( + {{features::kAutofillAssistantUrlHeuristic1, {{"some_key", R"( + { + "intent":"FAKE_INTENT", + "denylistedDomains":["example.com"], + "heuristics":[ + { + "conditionSet":{ + "urlContains":"something" + } + } + ], + "enabledInWeblayer":true + } + )"}}}}, + /* disabled_features = */ {}); + + FinchStarterHeuristicConfig config(base::FeatureParam<std::string>{ + &features::kAutofillAssistantUrlHeuristic1, "some_key", ""}); + + fake_platform_delegate_.is_custom_tab_ = false; + fake_platform_delegate_.is_web_layer_ = true; + EXPECT_THAT(config.GetConditionSetsForClientState(&fake_platform_delegate_), + SizeIs(1)); + + fake_platform_delegate_.is_web_layer_ = false; + EXPECT_THAT(config.GetConditionSetsForClientState(&fake_platform_delegate_), + IsEmpty()); +} + +TEST_F(FinchStarterHeuristicConfigTest, EnabledForSignedOutUsers) { + auto scoped_feature_list = std::make_unique<base::test::ScopedFeatureList>(); + scoped_feature_list->InitWithFeaturesAndParameters( + {{features::kAutofillAssistantUrlHeuristic1, {{"some_key", R"( + { + "intent":"FAKE_INTENT", + "denylistedDomains":["example.com"], + "heuristics":[ + { + "conditionSet":{ + "urlContains":"something" + } + } + ], + "enabledForSignedOutUsers":true, + "enabledInCustomTabs":true + } + )"}}}}, + /* disabled_features = */ {}); + + FinchStarterHeuristicConfig config(base::FeatureParam<std::string>{ + &features::kAutofillAssistantUrlHeuristic1, "some_key", ""}); + + fake_platform_delegate_.is_web_layer_ = false; + fake_platform_delegate_.is_custom_tab_ = true; + fake_platform_delegate_.is_logged_in_ = true; + EXPECT_THAT(config.GetConditionSetsForClientState(&fake_platform_delegate_), + SizeIs(1)); + + fake_platform_delegate_.is_custom_tab_ = true; + fake_platform_delegate_.is_logged_in_ = false; + EXPECT_THAT(config.GetConditionSetsForClientState(&fake_platform_delegate_), + SizeIs(1)); + + fake_platform_delegate_.is_custom_tab_ = false; + fake_platform_delegate_.is_logged_in_ = true; + EXPECT_THAT(config.GetConditionSetsForClientState(&fake_platform_delegate_), + IsEmpty()); + + fake_platform_delegate_.is_custom_tab_ = false; + fake_platform_delegate_.is_logged_in_ = false; + EXPECT_THAT(config.GetConditionSetsForClientState(&fake_platform_delegate_), + IsEmpty()); +} + +TEST_F(FinchStarterHeuristicConfigTest, EnabledWithoutMsbb) { + auto scoped_feature_list = std::make_unique<base::test::ScopedFeatureList>(); + scoped_feature_list->InitWithFeaturesAndParameters( + {{features::kAutofillAssistantUrlHeuristic1, {{"some_key", R"( + { + "intent":"FAKE_INTENT", + "denylistedDomains":["example.com"], + "heuristics":[ + { + "conditionSet":{ + "urlContains":"something" + } + } + ], + "enabledWithoutMsbb":true, + "enabledInCustomTabs":true + } + )"}}}}, + /* disabled_features = */ {}); + + FinchStarterHeuristicConfig config(base::FeatureParam<std::string>{ + &features::kAutofillAssistantUrlHeuristic1, "some_key", ""}); + + fake_platform_delegate_.is_web_layer_ = false; + fake_platform_delegate_.is_custom_tab_ = true; + fake_platform_delegate_.msbb_enabled_ = false; + EXPECT_THAT(config.GetConditionSetsForClientState(&fake_platform_delegate_), + SizeIs(1)); + + fake_platform_delegate_.is_custom_tab_ = true; + fake_platform_delegate_.msbb_enabled_ = true; + EXPECT_THAT(config.GetConditionSetsForClientState(&fake_platform_delegate_), + SizeIs(1)); + + fake_platform_delegate_.is_custom_tab_ = false; + fake_platform_delegate_.msbb_enabled_ = true; + EXPECT_THAT(config.GetConditionSetsForClientState(&fake_platform_delegate_), + IsEmpty()); + + fake_platform_delegate_.is_custom_tab_ = false; + fake_platform_delegate_.msbb_enabled_ = false; + EXPECT_THAT(config.GetConditionSetsForClientState(&fake_platform_delegate_), + IsEmpty()); +} + +TEST_F(FinchStarterHeuristicConfigTest, MultipleConditionSets) { + auto scoped_feature_list = std::make_unique<base::test::ScopedFeatureList>(); + scoped_feature_list->InitWithFeaturesAndParameters( + {{features::kAutofillAssistantUrlHeuristic1, {{"some_key", R"( + { + "intent":"FAKE_INTENT", + "denylistedDomains":["example.com"], + "heuristics":[ + { + "conditionSet":{ + "urlContains":"something" + } + }, + { + "conditionSet":{ + "urlContains":"different" + } + } + ], + "enabledInCustomTabs":true + } + )"}}}}, + /* disabled_features = */ {}); + + FinchStarterHeuristicConfig config(base::FeatureParam<std::string>{ + &features::kAutofillAssistantUrlHeuristic1, "some_key", ""}); + + fake_platform_delegate_.is_web_layer_ = false; + fake_platform_delegate_.is_custom_tab_ = true; + EXPECT_THAT(config.GetConditionSetsForClientState(&fake_platform_delegate_), + SizeIs(2)); + + fake_platform_delegate_.is_custom_tab_ = false; + EXPECT_THAT(config.GetConditionSetsForClientState(&fake_platform_delegate_), + IsEmpty()); +} + +} // namespace autofill_assistant
diff --git a/components/autofill_assistant/browser/starter_heuristic_configs/legacy_starter_heuristic_config.cc b/components/autofill_assistant/browser/starter_heuristic_configs/legacy_starter_heuristic_config.cc new file mode 100644 index 0000000..562ae9f --- /dev/null +++ b/components/autofill_assistant/browser/starter_heuristic_configs/legacy_starter_heuristic_config.cc
@@ -0,0 +1,160 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/autofill_assistant/browser/starter_heuristic_configs/legacy_starter_heuristic_config.h" +#include "base/json/json_reader.h" +#include "base/metrics/field_trial_params.h" +#include "components/autofill_assistant/browser/features.h" +#include "components/autofill_assistant/browser/starter_heuristic_configs/finch_starter_heuristic_config.h" + +namespace autofill_assistant { + +const char kJsonParameterDictKey[] = "json_parameters"; +constexpr base::FeatureParam<std::string> kLegacyFieldTrialParams{ + &features::kAutofillAssistantUrlHeuristics, kJsonParameterDictKey, ""}; + +LegacyStarterHeuristicConfig::LegacyStarterHeuristicConfig() { + InitFromTrialParams(); +} + +LegacyStarterHeuristicConfig::~LegacyStarterHeuristicConfig() = default; + +const std::string& LegacyStarterHeuristicConfig::GetIntent() const { + return intent_; +} + +const base::Value::List& +LegacyStarterHeuristicConfig::GetConditionSetsForClientState( + StarterPlatformDelegate* platform_delegate) const { + if (platform_delegate->GetIsSupervisedUser()) { + return empty_condition_sets_.GetList(); + } + + if (!platform_delegate->GetProactiveHelpSettingEnabled()) { + return empty_condition_sets_.GetList(); + } + + if (platform_delegate->GetIsCustomTab() && + !platform_delegate->GetIsTabCreatedByGSA()) { + return empty_condition_sets_.GetList(); + } + + // The legacy config used a separate finch feature to gate CCT vs. non-CCT + // support. In new configs, these can be specified directly in the params. + if (platform_delegate->GetIsCustomTab() && + !base::FeatureList::IsEnabled( + features::kAutofillAssistantInCCTTriggering)) { + return empty_condition_sets_.GetList(); + } + + if (!platform_delegate->GetIsCustomTab() && + !base::FeatureList::IsEnabled( + features::kAutofillAssistantInTabTriggering)) { + return empty_condition_sets_.GetList(); + } + + // The legacy config used to only be available for signed-in users in + // weblayer. + if (platform_delegate->GetIsWebLayer() && + !platform_delegate->GetIsLoggedIn()) { + return empty_condition_sets_.GetList(); + } + + return condition_sets_.GetList(); +} + +const base::flat_set<std::string>& +LegacyStarterHeuristicConfig::GetDenylistedDomains() const { + return denylisted_domains_; +} + +absl::optional<base::flat_set<std::string>> +LegacyStarterHeuristicConfig::ReadDenylistedDomains( + const base::Value::Dict& dict) const { + const base::Value::List* denylisted_domains_value = + dict.FindList(kDenylistedDomainsKey); + if (!denylisted_domains_value) { + return base::flat_set<std::string>{}; + } + + base::flat_set<std::string> denylisted_domains; + for (const auto& domain : *denylisted_domains_value) { + if (!domain.is_string()) { + VLOG(1) << "Invalid type for denylisted domain"; + return absl::nullopt; + } + denylisted_domains.insert(*domain.GetIfString()); + } + return denylisted_domains; +} + +absl::optional<std::pair<base::Value, std::string>> +LegacyStarterHeuristicConfig::ReadConditionSetsAndIntent( + const base::Value::Dict& dict) const { + const base::Value::List* condition_sets_list = dict.FindList(kHeuristicsKey); + if (!condition_sets_list) { + VLOG(1) << "Field trial params did not contain condition sets"; + return absl::nullopt; + } + + // In this legacy config, the INTENT script parameter was specified as part of + // each individual heuristic entry (and not one overall). Thus, it was + // technically possible to supply different INTENTS per heuristic. This was + // never actually used. For legacy treatment, we simply take the first + // specified INTENT here. + std::string intent; + if (!condition_sets_list->empty()) { + auto* intent_param = condition_sets_list->front().FindKeyOfType( + kIntentKey, base::Value::Type::STRING); + if (!intent_param) { + VLOG(1) << "Heuristic did not contain the intent parameter"; + return absl::nullopt; + } + intent.assign(*intent_param->GetIfString()); + } + + return std::make_pair(base::Value(condition_sets_list->Clone()), intent); +} + +void LegacyStarterHeuristicConfig::InitFromTrialParams() { + std::string parameters = kLegacyFieldTrialParams.Get(); + if (parameters.empty()) { + VLOG(2) << "Field trial parameter not set"; + return; + } + auto dict = base::JSONReader::ReadAndReturnValueWithError(parameters); + if (!dict.has_value() || !dict->is_dict()) { + VLOG(1) << "Failed to parse field trial params as JSON object: " + << parameters; + if (VLOG_IS_ON(1)) { + if (dict.has_value()) { + VLOG(1) << "Expecting a dictionary"; + } else { + VLOG(1) << dict.error().message << ", line: " << dict.error().line + << ", col: " << dict.error().column; + } + } + return; + } + + // Read optional list of denylisted domains. + absl::optional<base::flat_set<std::string>> denylisted_domains = + ReadDenylistedDomains(dict->GetDict()); + if (!denylisted_domains) { + return; + } + + // Read condition sets and intent. + absl::optional<std::pair<base::Value, std::string>> + condition_sets_and_intent = ReadConditionSetsAndIntent(dict->GetDict()); + if (!condition_sets_and_intent) { + return; + } + + denylisted_domains_ = std::move(*denylisted_domains); + condition_sets_ = base::Value(std::move(condition_sets_and_intent->first)); + intent_.assign(condition_sets_and_intent->second); +} + +} // namespace autofill_assistant
diff --git a/components/autofill_assistant/browser/starter_heuristic_configs/legacy_starter_heuristic_config.h b/components/autofill_assistant/browser/starter_heuristic_configs/legacy_starter_heuristic_config.h new file mode 100644 index 0000000..aaa7390 --- /dev/null +++ b/components/autofill_assistant/browser/starter_heuristic_configs/legacy_starter_heuristic_config.h
@@ -0,0 +1,54 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_STARTER_HEURISTIC_CONFIGS_LEGACY_STARTER_HEURISTIC_CONFIG_H_ +#define COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_STARTER_HEURISTIC_CONFIGS_LEGACY_STARTER_HEURISTIC_CONFIG_H_ + +#include <string> + +#include "base/containers/flat_map.h" +#include "components/autofill_assistant/browser/script_parameters.h" +#include "components/autofill_assistant/browser/starter_heuristic_configs/starter_heuristic_config.h" +#include "third_party/abseil-cpp/absl/types/optional.h" + +namespace autofill_assistant { + +// The legacy config. Some smaller changes have been made to the format of field +// trial parameters since then, so this class provides a legacy layer for the +// old trial until we can phase it out. +class LegacyStarterHeuristicConfig : public StarterHeuristicConfig { + public: + LegacyStarterHeuristicConfig(); + ~LegacyStarterHeuristicConfig() override; + + // Overrides HeuristicConfig: + const std::string& GetIntent() const override; + const base::Value::List& GetConditionSetsForClientState( + StarterPlatformDelegate* platform_delegate) const override; + const base::flat_set<std::string>& GetDenylistedDomains() const override; + + private: + void InitFromTrialParams(); + + // Returns the list of denylisted domains in |dict|. Returns the empty list + // if the relevant key does not exist in |dict|. Returns absl::nullopt if the + // format of the encountered denylist was invalid. + absl::optional<base::flat_set<std::string>> ReadDenylistedDomains( + const base::Value::Dict& dict) const; + + // Reads the condition sets and intent in |dict|. Returns absl::nullopt if + // either of these parameters is invalid. + absl::optional<std::pair<base::Value, std::string>> + ReadConditionSetsAndIntent(const base::Value::Dict& dict) const; + + const base::Value empty_condition_sets_ = + base::Value(base::Value::Type::LIST); + std::string intent_; + base::Value condition_sets_ = base::Value(base::Value::Type::LIST); + base::flat_set<std::string> denylisted_domains_; +}; + +} // namespace autofill_assistant + +#endif // COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_STARTER_HEURISTIC_CONFIGS_LEGACY_STARTER_HEURISTIC_CONFIG_H_
diff --git a/components/autofill_assistant/browser/starter_heuristic_configs/legacy_starter_heuristic_config_unittest.cc b/components/autofill_assistant/browser/starter_heuristic_configs/legacy_starter_heuristic_config_unittest.cc new file mode 100644 index 0000000..3924864c --- /dev/null +++ b/components/autofill_assistant/browser/starter_heuristic_configs/legacy_starter_heuristic_config_unittest.cc
@@ -0,0 +1,379 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/autofill_assistant/browser/starter_heuristic_configs/legacy_starter_heuristic_config.h" +#include "base/json/json_reader.h" +#include "base/test/scoped_feature_list.h" +#include "components/autofill_assistant/browser/fake_starter_platform_delegate.h" +#include "components/autofill_assistant/browser/features.h" + +#include "testing/gmock/include/gmock/gmock.h" + +namespace autofill_assistant { + +using ::testing::ElementsAre; +using ::testing::Eq; +using ::testing::IsEmpty; +using ::testing::SizeIs; +using ::testing::UnorderedElementsAreArray; + +class LegacyStarterHeuristicConfigTest : public testing::Test { + public: + LegacyStarterHeuristicConfigTest() = default; + ~LegacyStarterHeuristicConfigTest() override = default; + + protected: + // Convenience for tests where the contents of the heuristic don't matter. + void InitDefaultHeuristic() { + scoped_feature_list_ = std::make_unique<base::test::ScopedFeatureList>(); + scoped_feature_list_->InitWithFeaturesAndParameters( + {{features::kAutofillAssistantUrlHeuristics, {{"json_parameters", R"( + { + "heuristics":[ + { + "intent":"FAKE_INTENT", + "conditionSet":{ + "urlContains":"something" + } + } + ] + } + )"}}}}, + /* disabled_features = */ {}); + } + + FakeStarterPlatformDelegate fake_platform_delegate_; + + private: + std::unique_ptr<base::test::ScopedFeatureList> scoped_feature_list_; +}; + +TEST_F(LegacyStarterHeuristicConfigTest, DisabledIfNoFeatureParam) { + LegacyStarterHeuristicConfig config; + EXPECT_THAT(config.GetDenylistedDomains(), IsEmpty()); + EXPECT_THAT(config.GetIntent(), IsEmpty()); + EXPECT_THAT(config.GetConditionSetsForClientState(&fake_platform_delegate_), + IsEmpty()); +} + +TEST_F(LegacyStarterHeuristicConfigTest, DenylistedDomains) { + auto scoped_feature_list = std::make_unique<base::test::ScopedFeatureList>(); + scoped_feature_list->InitWithFeaturesAndParameters( + {{features::kAutofillAssistantUrlHeuristics, {{"json_parameters", R"( + { + "denylistedDomains":["example.com", "different.com"], + "heuristics":[ + { + "intent":"FAKE_INTENT_A", + "conditionSet":{ + "urlContains":"something" + } + } + ] + } + )"}}}, + {features::kAutofillAssistantInCCTTriggering, {}}}, + /* disabled_features = */ {}); + + LegacyStarterHeuristicConfig config; + EXPECT_THAT(config.GetDenylistedDomains(), + UnorderedElementsAreArray( + std::vector<std::string>{"example.com", "different.com"})); +} + +TEST_F(LegacyStarterHeuristicConfigTest, MultipleConditionSets) { + auto scoped_feature_list = std::make_unique<base::test::ScopedFeatureList>(); + scoped_feature_list->InitWithFeaturesAndParameters( + {{features::kAutofillAssistantUrlHeuristics, {{"json_parameters", R"( + { + "heuristics":[ + { + "intent":"FAKE_INTENT_A", + "conditionSet":{ + "urlContains":"something" + } + }, + { + "intent":"FAKE_INTENT_B", + "conditionSet":{ + "urlContains":"different" + } + } + ] + } + )"}}}, + {features::kAutofillAssistantInCCTTriggering, {}}}, + /* disabled_features = */ {}); + + LegacyStarterHeuristicConfig config; + fake_platform_delegate_.is_custom_tab_ = true; + fake_platform_delegate_.is_tab_created_by_gsa_ = true; + fake_platform_delegate_.is_web_layer_ = false; + EXPECT_EQ(config.GetConditionSetsForClientState(&fake_platform_delegate_), + base::JSONReader::Read(R"([ + { + "intent":"FAKE_INTENT_A", + "conditionSet":{ + "urlContains":"something" + } + }, + { + "intent":"FAKE_INTENT_B", + "conditionSet":{ + "urlContains":"different" + } + } + ])") + ->GetList()); +} + +TEST_F(LegacyStarterHeuristicConfigTest, UseFirstIntentIfMultipleSpecified) { + auto scoped_feature_list = std::make_unique<base::test::ScopedFeatureList>(); + scoped_feature_list->InitWithFeaturesAndParameters( + {{features::kAutofillAssistantUrlHeuristics, {{"json_parameters", R"( + { + "heuristics":[ + { + "intent":"FAKE_INTENT_A", + "conditionSet":{ + "urlContains":"something" + } + }, + { + "intent":"FAKE_INTENT_B", + "conditionSet":{ + "urlContains":"different" + } + } + ] + } + )"}}}, + {features::kAutofillAssistantInCCTTriggering, {}}}, + /* disabled_features = */ {}); + + LegacyStarterHeuristicConfig config; + EXPECT_THAT(config.GetIntent(), Eq("FAKE_INTENT_A")); +} + +TEST_F(LegacyStarterHeuristicConfigTest, OnlyEnabledInCustomTab) { + InitDefaultHeuristic(); + auto scoped_feature_list = std::make_unique<base::test::ScopedFeatureList>(); + scoped_feature_list->InitWithFeatures( + /* enabled_features = */ {features::kAutofillAssistantInCCTTriggering}, + /* disabled_features = */ {features::kAutofillAssistantInTabTriggering}); + LegacyStarterHeuristicConfig config; + + // Allowed: custom tabs created by GSA. + fake_platform_delegate_.is_custom_tab_ = true; + fake_platform_delegate_.is_tab_created_by_gsa_ = true; + EXPECT_THAT(config.GetConditionSetsForClientState(&fake_platform_delegate_), + SizeIs(1)); + + // Not a custom tab. + fake_platform_delegate_.is_custom_tab_ = false; + fake_platform_delegate_.is_tab_created_by_gsa_ = true; + EXPECT_THAT(config.GetConditionSetsForClientState(&fake_platform_delegate_), + IsEmpty()); + + // CCT not created by GSA. + fake_platform_delegate_.is_custom_tab_ = true; + fake_platform_delegate_.is_tab_created_by_gsa_ = false; + EXPECT_THAT(config.GetConditionSetsForClientState(&fake_platform_delegate_), + IsEmpty()); +} + +TEST_F(LegacyStarterHeuristicConfigTest, OnlyEnabledInRegularTab) { + InitDefaultHeuristic(); + auto scoped_feature_list = std::make_unique<base::test::ScopedFeatureList>(); + scoped_feature_list->InitWithFeatures( + /* enabled_features = */ {features::kAutofillAssistantInTabTriggering}, + /* disabled_features = */ {features::kAutofillAssistantInCCTTriggering}); + LegacyStarterHeuristicConfig config; + + // Allowed: regular tabs. + fake_platform_delegate_.is_custom_tab_ = false; + EXPECT_THAT(config.GetConditionSetsForClientState(&fake_platform_delegate_), + SizeIs(1)); + + // Not a regular tab. + fake_platform_delegate_.is_custom_tab_ = true; + EXPECT_THAT(config.GetConditionSetsForClientState(&fake_platform_delegate_), + IsEmpty()); +} + +TEST_F(LegacyStarterHeuristicConfigTest, EnabledInAllTabs) { + InitDefaultHeuristic(); + auto scoped_feature_list = std::make_unique<base::test::ScopedFeatureList>(); + scoped_feature_list->InitWithFeatures( + /* enabled_features = */ {features::kAutofillAssistantInTabTriggering, + features::kAutofillAssistantInCCTTriggering}, + /* disabled_features = */ {}); + LegacyStarterHeuristicConfig config; + + // Allowed: regular tabs. + fake_platform_delegate_.is_custom_tab_ = false; + EXPECT_THAT(config.GetConditionSetsForClientState(&fake_platform_delegate_), + SizeIs(1)); + + // Allowed: custom tabs. + fake_platform_delegate_.is_custom_tab_ = true; + EXPECT_THAT(config.GetConditionSetsForClientState(&fake_platform_delegate_), + SizeIs(1)); +} + +TEST_F(LegacyStarterHeuristicConfigTest, OnlySignedInUsersInWeblayer) { + InitDefaultHeuristic(); + auto scoped_feature_list = std::make_unique<base::test::ScopedFeatureList>(); + scoped_feature_list->InitWithFeatures( + /* enabled_features = */ {features::kAutofillAssistantInCCTTriggering}, + /* disabled_features = */ {features::kAutofillAssistantInTabTriggering}); + LegacyStarterHeuristicConfig config; + + // For weblayer, only signed in users are allowed. + fake_platform_delegate_.is_custom_tab_ = true; + fake_platform_delegate_.is_tab_created_by_gsa_ = true; + fake_platform_delegate_.is_web_layer_ = true; + fake_platform_delegate_.is_logged_in_ = true; + EXPECT_THAT(config.GetConditionSetsForClientState(&fake_platform_delegate_), + SizeIs(1)); + + fake_platform_delegate_.is_custom_tab_ = false; + fake_platform_delegate_.is_tab_created_by_gsa_ = true; + fake_platform_delegate_.is_web_layer_ = true; + fake_platform_delegate_.is_logged_in_ = false; + EXPECT_THAT(config.GetConditionSetsForClientState(&fake_platform_delegate_), + IsEmpty()); +} + +TEST_F(LegacyStarterHeuristicConfigTest, InvalidDenylistedDomains) { + auto scoped_feature_list = std::make_unique<base::test::ScopedFeatureList>(); + scoped_feature_list->InitWithFeaturesAndParameters( + {{features::kAutofillAssistantUrlHeuristics, {{"json_parameters", R"( + { + "denylistedDomains":[{"invalid_nested_in_object"}], + "heuristics":[ + { + "intent":"FAKE_INTENT_A", + "conditionSet":{ + "urlContains":"something" + } + } + ] + } + )"}}}, + {features::kAutofillAssistantInTabTriggering, {}}, + {features::kAutofillAssistantInCCTTriggering, {}}}, + /* disabled_features = */ {}); + + LegacyStarterHeuristicConfig config; + EXPECT_THAT(config.GetConditionSetsForClientState(&fake_platform_delegate_), + IsEmpty()); + EXPECT_THAT(config.GetDenylistedDomains(), IsEmpty()); +} + +TEST_F(LegacyStarterHeuristicConfigTest, NoIntent) { + auto scoped_feature_list = std::make_unique<base::test::ScopedFeatureList>(); + scoped_feature_list->InitWithFeaturesAndParameters( + {{features::kAutofillAssistantUrlHeuristics, {{"json_parameters", R"( + { + "heuristics":[ + { + "conditionSet":{ + "urlContains":"something" + } + } + ] + } + )"}}}, + {features::kAutofillAssistantInTabTriggering, {}}, + {features::kAutofillAssistantInCCTTriggering, {}}}, + /* disabled_features = */ {}); + + LegacyStarterHeuristicConfig config; + EXPECT_THAT(config.GetConditionSetsForClientState(&fake_platform_delegate_), + IsEmpty()); + EXPECT_THAT(config.GetDenylistedDomains(), IsEmpty()); + EXPECT_THAT(config.GetIntent(), IsEmpty()); +} + +TEST_F(LegacyStarterHeuristicConfigTest, NoConditionSet) { + auto scoped_feature_list = std::make_unique<base::test::ScopedFeatureList>(); + scoped_feature_list->InitWithFeaturesAndParameters( + {{features::kAutofillAssistantUrlHeuristics, {{"json_parameters", R"( + { + "heuristics":[ + { + "intent":"FAKE_INTENT_A", + } + ] + } + )"}}}, + {features::kAutofillAssistantInTabTriggering, {}}, + {features::kAutofillAssistantInCCTTriggering, {}}}, + /* disabled_features = */ {}); + + LegacyStarterHeuristicConfig config; + EXPECT_THAT(config.GetConditionSetsForClientState(&fake_platform_delegate_), + IsEmpty()); + EXPECT_THAT(config.GetDenylistedDomains(), IsEmpty()); + EXPECT_THAT(config.GetIntent(), IsEmpty()); +} + +TEST_F(LegacyStarterHeuristicConfigTest, EmptyConditionSets) { + // Technically allowed, but pointless. + auto scoped_feature_list = std::make_unique<base::test::ScopedFeatureList>(); + scoped_feature_list->InitWithFeaturesAndParameters( + {{features::kAutofillAssistantUrlHeuristics, {{"json_parameters", R"( + { + "heuristics":[] + } + )"}}}, + {features::kAutofillAssistantInTabTriggering, {}}, + {features::kAutofillAssistantInCCTTriggering, {}}}, + /* disabled_features = */ {}); + + LegacyStarterHeuristicConfig config; + EXPECT_THAT(config.GetConditionSetsForClientState(&fake_platform_delegate_), + IsEmpty()); + EXPECT_THAT(config.GetDenylistedDomains(), IsEmpty()); + EXPECT_THAT(config.GetIntent(), IsEmpty()); +} + +TEST_F(LegacyStarterHeuristicConfigTest, DisabledForSupervisedUsers) { + InitDefaultHeuristic(); + auto scoped_feature_list = std::make_unique<base::test::ScopedFeatureList>(); + scoped_feature_list->InitWithFeatures( + /* enabled_features = */ {features::kAutofillAssistantInTabTriggering, + features::kAutofillAssistantInCCTTriggering}, + /* disabled_features = */ {}); + LegacyStarterHeuristicConfig config; + + fake_platform_delegate_.is_supervised_user_ = true; + EXPECT_THAT(config.GetConditionSetsForClientState(&fake_platform_delegate_), + IsEmpty()); + + fake_platform_delegate_.is_supervised_user_ = false; + EXPECT_THAT(config.GetConditionSetsForClientState(&fake_platform_delegate_), + SizeIs(1)); +} + +TEST_F(LegacyStarterHeuristicConfigTest, DisabledIfProactiveHelpSettingOff) { + InitDefaultHeuristic(); + auto scoped_feature_list = std::make_unique<base::test::ScopedFeatureList>(); + scoped_feature_list->InitWithFeatures( + /* enabled_features = */ {features::kAutofillAssistantInTabTriggering, + features::kAutofillAssistantInCCTTriggering}, + /* disabled_features = */ {}); + LegacyStarterHeuristicConfig config; + + fake_platform_delegate_.proactive_help_enabled_ = false; + EXPECT_THAT(config.GetConditionSetsForClientState(&fake_platform_delegate_), + IsEmpty()); + + fake_platform_delegate_.proactive_help_enabled_ = true; + EXPECT_THAT(config.GetConditionSetsForClientState(&fake_platform_delegate_), + SizeIs(1)); +} + +} // namespace autofill_assistant
diff --git a/components/autofill_assistant/browser/starter_heuristic_configs/starter_heuristic_config.h b/components/autofill_assistant/browser/starter_heuristic_configs/starter_heuristic_config.h new file mode 100644 index 0000000..1ce654d --- /dev/null +++ b/components/autofill_assistant/browser/starter_heuristic_configs/starter_heuristic_config.h
@@ -0,0 +1,43 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_STARTER_HEURISTIC_CONFIGS_STARTER_HEURISTIC_CONFIG_H_ +#define COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_STARTER_HEURISTIC_CONFIGS_STARTER_HEURISTIC_CONFIG_H_ + +#include <string> + +#include "base/containers/flat_set.h" +#include "base/values.h" +#include "components/autofill_assistant/browser/script_parameters.h" +#include "components/autofill_assistant/browser/starter_platform_delegate.h" + +namespace autofill_assistant { + +// Base class for starter heuristic configs. Instances of this class define +// heurists that are used by the starter to determine when to start. Configs +// are usually either hard-coded into Chrome, or supplied via finch parameters. +class StarterHeuristicConfig { + public: + StarterHeuristicConfig() = default; + virtual ~StarterHeuristicConfig() = default; + StarterHeuristicConfig(const StarterHeuristicConfig&) = delete; + StarterHeuristicConfig& operator=(const StarterHeuristicConfig&) = delete; + + // Returns the intent script parameter for this starter heuristic. + virtual const std::string& GetIntent() const = 0; + + // Returns a list containing the condition sets to use for the + // current client state (can be empty). Each conditionSet is a + // URLMatcherConditionSet dictionary as defined by the URLMatcherFactory + // (components/url_matcher/url_matcher_factory.h). + virtual const base::Value::List& GetConditionSetsForClientState( + StarterPlatformDelegate* platform_delegate) const = 0; + + // Returns the list of denylisted domains for this config. + virtual const base::flat_set<std::string>& GetDenylistedDomains() const = 0; +}; + +} // namespace autofill_assistant + +#endif // COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_STARTER_HEURISTIC_CONFIGS_STARTER_HEURISTIC_CONFIG_H_
diff --git a/components/autofill_assistant/browser/starter_heuristic_unittest.cc b/components/autofill_assistant/browser/starter_heuristic_unittest.cc index 1421a42..2d6e0ea 100644 --- a/components/autofill_assistant/browser/starter_heuristic_unittest.cc +++ b/components/autofill_assistant/browser/starter_heuristic_unittest.cc
@@ -11,7 +11,9 @@ #include "base/test/mock_callback.h" #include "base/test/scoped_feature_list.h" #include "base/test/task_environment.h" +#include "components/autofill_assistant/browser/fake_starter_platform_delegate.h" #include "components/autofill_assistant/browser/features.h" +#include "components/autofill_assistant/browser/starter_heuristic_configs/legacy_starter_heuristic_config.h" #include "testing/gmock/include/gmock/gmock.h" namespace autofill_assistant { @@ -28,14 +30,37 @@ base::flat_set<std::string> IsHeuristicMatchForTest( const StarterHeuristic& starter_heuristic, const GURL& url) { - return starter_heuristic.IsHeuristicMatch(url); + return starter_heuristic.IsHeuristicMatch( + url, starter_heuristic.matcher_id_to_config_map_); } + + // Enables in-cct triggering with the specified parameters for + // |starter_heuristic|. + void InitDefaultHeuristic(StarterHeuristic& starter_heuristic, + const std::string& json_parameters) { + scoped_feature_list_ = std::make_unique<base::test::ScopedFeatureList>(); + scoped_feature_list_->InitWithFeaturesAndParameters( + {{features::kAutofillAssistantUrlHeuristics, + {{"json_parameters", json_parameters}}}, + {features::kAutofillAssistantInCCTTriggering, {}}}, + /* disabled_features = */ {}); + + std::vector<std::unique_ptr<StarterHeuristicConfig>> configs; + configs.emplace_back(std::make_unique<LegacyStarterHeuristicConfig>()); + starter_heuristic.InitFromHeuristicConfigs(configs, + &fake_platform_delegate_); + } + + protected: + FakeStarterPlatformDelegate fake_platform_delegate_; + + private: + std::unique_ptr<base::test::ScopedFeatureList> scoped_feature_list_; }; TEST_F(StarterHeuristicTest, SmokeTest) { - auto scoped_feature_list = std::make_unique<base::test::ScopedFeatureList>(); - scoped_feature_list->InitAndEnableFeatureWithParameters( - features::kAutofillAssistantUrlHeuristics, {{"json_parameters", R"( + auto starter_heuristic = base::MakeRefCounted<StarterHeuristic>(); + InitDefaultHeuristic(*starter_heuristic, R"( { "heuristics":[ { @@ -46,9 +71,8 @@ } ] } - )"}}); + )"); - auto starter_heuristic = base::MakeRefCounted<StarterHeuristic>(); EXPECT_THAT(IsHeuristicMatchForTest(*starter_heuristic, GURL("https://www.example.com/cart")), ElementsAre("FAKE_INTENT_CART")); @@ -60,10 +84,8 @@ } TEST_F(StarterHeuristicTest, RunHeuristicAsync) { - auto scoped_feature_list = std::make_unique<base::test::ScopedFeatureList>(); - scoped_feature_list->InitAndEnableFeatureWithParameters( - features::kAutofillAssistantUrlHeuristics, {{"json_parameters", - R"( + auto starter_heuristic = base::MakeRefCounted<StarterHeuristic>(); + InitDefaultHeuristic(*starter_heuristic, R"( { "heuristics":[ { @@ -74,63 +96,21 @@ } ] } - )"}}); + )"); base::test::TaskEnvironment task_environment; base::MockCallback< base::OnceCallback<void(const base::flat_set<std::string>&)>> callback; EXPECT_CALL(callback, Run(base::flat_set<std::string>{"FAKE_INTENT_CART"})); - auto starter_heuristic = base::MakeRefCounted<StarterHeuristic>(); starter_heuristic->RunHeuristicAsync(GURL("https://www.example.com/cart"), callback.Get()); task_environment.RunUntilIdle(); } -TEST_F(StarterHeuristicTest, MultipleIntentHeuristics) { - auto scoped_feature_list = std::make_unique<base::test::ScopedFeatureList>(); - scoped_feature_list->InitAndEnableFeatureWithParameters( - features::kAutofillAssistantUrlHeuristics, {{"json_parameters", - R"( - { - "heuristics":[ - { - "intent":"FAKE_INTENT_CART", - "conditionSet":{ - "urlContains":"cart" - } - }, - { - "intent":"FAKE_INTENT_OTHER", - "conditionSet":{ - "urlMatches":".*other.*" - } - } - ] - } - )"}}); - - auto starter_heuristic = base::MakeRefCounted<StarterHeuristic>(); - EXPECT_THAT(IsHeuristicMatchForTest(*starter_heuristic, - GURL("https://www.example.com/cart")), - ElementsAre("FAKE_INTENT_CART")); - EXPECT_THAT(IsHeuristicMatchForTest(*starter_heuristic, - GURL("https://www.example.com/other")), - ElementsAre("FAKE_INTENT_OTHER")); - EXPECT_THAT(IsHeuristicMatchForTest(*starter_heuristic, - GURL("https://www.example.com")), - IsEmpty()); - EXPECT_THAT( - IsHeuristicMatchForTest(*starter_heuristic, - GURL("https://www.example.com/cart/other")), - ElementsAre("FAKE_INTENT_CART", "FAKE_INTENT_OTHER")); -} - TEST_F(StarterHeuristicTest, DenylistedDomains) { - auto scoped_feature_list = std::make_unique<base::test::ScopedFeatureList>(); - scoped_feature_list->InitAndEnableFeatureWithParameters( - features::kAutofillAssistantUrlHeuristics, {{"json_parameters", - R"( + auto starter_heuristic = base::MakeRefCounted<StarterHeuristic>(); + InitDefaultHeuristic(*starter_heuristic, R"( { "denylistedDomains": ["example.com", "other-example.com"], "heuristics":[ @@ -142,11 +122,10 @@ } ] } - )"}}); + )"); // URLs on denylisted domains or subdomains thereof will always fail the // heuristic even if they would otherwise match. - auto starter_heuristic = base::MakeRefCounted<StarterHeuristic>(); EXPECT_THAT(IsHeuristicMatchForTest(*starter_heuristic, GURL("https://www.example.com/cart")), IsEmpty()); @@ -172,10 +151,8 @@ } TEST_F(StarterHeuristicTest, MultipleConditionSetsForSameIntent) { - auto scoped_feature_list = std::make_unique<base::test::ScopedFeatureList>(); - scoped_feature_list->InitAndEnableFeatureWithParameters( - features::kAutofillAssistantUrlHeuristics, {{"json_parameters", - R"( + auto starter_heuristic = base::MakeRefCounted<StarterHeuristic>(); + InitDefaultHeuristic(*starter_heuristic, R"( { "heuristics":[ { @@ -192,9 +169,8 @@ } ] } - )"}}); + )"); - auto starter_heuristic = base::MakeRefCounted<StarterHeuristic>(); EXPECT_THAT(IsHeuristicMatchForTest(*starter_heuristic, GURL("https://example.com/cart")), ElementsAre("FAKE_INTENT_CART")); @@ -206,56 +182,6 @@ IsEmpty()); } -TEST_F(StarterHeuristicTest, MultipleConditionSetsForMultipleIntents) { - auto scoped_feature_list = std::make_unique<base::test::ScopedFeatureList>(); - scoped_feature_list->InitAndEnableFeatureWithParameters( - features::kAutofillAssistantUrlHeuristics, {{"json_parameters", - R"( - { - "heuristics":[ - { - "intent":"FAKE_INTENT_A", - "conditionSet":{ - "urlContains":"a_and_b" - } - }, - { - "intent":"FAKE_INTENT_A", - "conditionSet":{ - "urlContains":"only_a" - } - }, - { - "intent":"FAKE_INTENT_B", - "conditionSet":{ - "urlContains":"a_and_b" - } - }, - { - "intent":"FAKE_INTENT_B", - "conditionSet":{ - "urlContains":"only_b" - } - } - ] - } - )"}}); - - auto starter_heuristic = base::MakeRefCounted<StarterHeuristic>(); - EXPECT_THAT(IsHeuristicMatchForTest(*starter_heuristic, - GURL("https://example.com/a_and_b")), - ElementsAre("FAKE_INTENT_A", "FAKE_INTENT_B")); - EXPECT_THAT(IsHeuristicMatchForTest(*starter_heuristic, - GURL("https://example.com/only_a")), - ElementsAre("FAKE_INTENT_A")); - EXPECT_THAT(IsHeuristicMatchForTest(*starter_heuristic, - GURL("https://example.com/only_b")), - ElementsAre("FAKE_INTENT_B")); - EXPECT_THAT(IsHeuristicMatchForTest(*starter_heuristic, - GURL("https://www.example.com")), - IsEmpty()); -} - TEST_F(StarterHeuristicTest, FieldTrialNotSet) { // Just a check that this does not crash. auto starter_heuristic = base::MakeRefCounted<StarterHeuristic>(); @@ -266,11 +192,9 @@ TEST_F(StarterHeuristicTest, FieldTrialInvalid) { // Just a check that this does not crash. - auto scoped_feature_list = std::make_unique<base::test::ScopedFeatureList>(); - scoped_feature_list->InitAndEnableFeatureWithParameters( - features::kAutofillAssistantUrlHeuristics, - {{"json_parameters", "invalid"}}); auto starter_heuristic = base::MakeRefCounted<StarterHeuristic>(); + InitDefaultHeuristic(*starter_heuristic, "invalid"); + EXPECT_THAT(IsHeuristicMatchForTest(*starter_heuristic, GURL("https://www.example.com/cart")), IsEmpty()); @@ -279,10 +203,8 @@ TEST_F(StarterHeuristicTest, PartiallyInvalidFieldTrialsAreCompletelyIgnored) { // |denylistedDomains| expects an array of strings. If specified but invalid, // the entire configuration should be ignored. - auto scoped_feature_list = std::make_unique<base::test::ScopedFeatureList>(); - scoped_feature_list->InitAndEnableFeatureWithParameters( - features::kAutofillAssistantUrlHeuristics, {{"json_parameters", - R"( + auto starter_heuristic = base::MakeRefCounted<StarterHeuristic>(); + InitDefaultHeuristic(*starter_heuristic, R"( { "denylistedDomains": [-1], "heuristics":[ @@ -294,8 +216,8 @@ } ] } - )"}}); - auto starter_heuristic = base::MakeRefCounted<StarterHeuristic>(); + )"); + EXPECT_THAT(IsHeuristicMatchForTest(*starter_heuristic, GURL("https://www.example.com/cart")), IsEmpty());
diff --git a/components/autofill_assistant/browser/starter_unittest.cc b/components/autofill_assistant/browser/starter_unittest.cc index 2de8455..19a1097c 100644 --- a/components/autofill_assistant/browser/starter_unittest.cc +++ b/components/autofill_assistant/browser/starter_unittest.cc
@@ -2013,7 +2013,7 @@ task_environment()->RunUntilIdle(); } -TEST(MultipleIntentStarterTest, ImplicitTriggeringSendsAllMatchingIntents) { +TEST(MultipleIntentStarterTest, ImplicitTriggeringSendsFirstLegacyIntent) { content::BrowserTaskEnvironment task_environment( base::test::TaskEnvironment::TimeSource::MOCK_TIME); content::RenderViewHostTestEnabler rvh_test_enabler; @@ -2079,8 +2079,7 @@ auto actual_intents = base::SplitString(actual_intent_param->value(), ",", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL); - EXPECT_THAT(actual_intents, - UnorderedElementsAre("FAKE_INTENT_A", "FAKE_INTENT_B")); + EXPECT_THAT(actual_intents, UnorderedElementsAre("FAKE_INTENT_A")); })); content::WebContentsTester::For(web_contents.get())
diff --git a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/RadioButtonWithEditTextTest.java b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/RadioButtonWithEditTextTest.java index fe2885d..d0bfc04 100644 --- a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/RadioButtonWithEditTextTest.java +++ b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/RadioButtonWithEditTextTest.java
@@ -35,6 +35,7 @@ import org.chromium.base.test.util.Batch; import org.chromium.base.test.util.Criteria; import org.chromium.base.test.util.CriteriaHelper; +import org.chromium.base.test.util.DisabledTest; import org.chromium.components.browser_ui.widget.test.R; import org.chromium.content_public.browser.test.util.TestThreadUtils; import org.chromium.ui.KeyboardVisibilityDelegate; @@ -236,6 +237,7 @@ @Test @SmallTest + @DisabledTest(message = "Test is flaky: https://crbug.com/1344713") public void testFocusChange() { Assert.assertFalse(mRadioButtonWithEditText.hasFocus()); TestThreadUtils.runOnUiThreadBlocking(() -> { mRadioButtonWithEditText.setChecked(true); });
diff --git a/components/content_settings/core/browser/cookie_settings.cc b/components/content_settings/core/browser/cookie_settings.cc index 77862163..ccb9867 100644 --- a/components/content_settings/core/browser/cookie_settings.cc +++ b/components/content_settings/core/browser/cookie_settings.cc
@@ -55,10 +55,11 @@ ContentSettingsType::COOKIES, provider_id); } -void CookieSettings::GetCookieSettings( - ContentSettingsForOneType* settings) const { +ContentSettingsForOneType CookieSettings::GetCookieSettings() const { + ContentSettingsForOneType settings; host_content_settings_map_->GetSettingsForOneType( - ContentSettingsType::COOKIES, settings); + ContentSettingsType::COOKIES, &settings); + return settings; } void CookieSettings::RegisterProfilePrefs(
diff --git a/components/content_settings/core/browser/cookie_settings.h b/components/content_settings/core/browser/cookie_settings.h index 1103a05..57bba8ae 100644 --- a/components/content_settings/core/browser/cookie_settings.h +++ b/components/content_settings/core/browser/cookie_settings.h
@@ -80,11 +80,10 @@ ContentSetting GetDefaultCookieSetting(std::string* provider_id) const; // Returns all patterns with a non-default cookie setting, mapped to their - // actual settings, in the precedence order of the setting rules. |settings| - // must be a non-nullptr outparam. + // actual settings, in the precedence order of the setting rules. // // This may be called on any thread. - void GetCookieSettings(ContentSettingsForOneType* settings) const; + ContentSettingsForOneType GetCookieSettings() const; // Sets the default content setting (CONTENT_SETTING_ALLOW, // CONTENT_SETTING_BLOCK, or CONTENT_SETTING_SESSION_ONLY) for cookies.
diff --git a/components/content_settings/core/browser/cookie_settings_unittest.cc b/components/content_settings/core/browser/cookie_settings_unittest.cc index ee5f639..533ad6a6 100644 --- a/components/content_settings/core/browser/cookie_settings_unittest.cc +++ b/components/content_settings/core/browser/cookie_settings_unittest.cc
@@ -115,10 +115,8 @@ protected: bool ShouldDeleteCookieOnExit(const std::string& domain, bool is_https) { - ContentSettingsForOneType settings; - cookie_settings_->GetCookieSettings(&settings); - return cookie_settings_->ShouldDeleteCookieOnExit(settings, domain, - is_https); + return cookie_settings_->ShouldDeleteCookieOnExit( + cookie_settings_->GetCookieSettings(), domain, is_https); } // There must be a valid ThreadTaskRunnerHandle in HostContentSettingsMap's
diff --git a/components/cronet/android/test/javatests/src/org/chromium/net/PkpTest.java b/components/cronet/android/test/javatests/src/org/chromium/net/PkpTest.java index 230059b..05d7736f 100644 --- a/components/cronet/android/test/javatests/src/org/chromium/net/PkpTest.java +++ b/components/cronet/android/test/javatests/src/org/chromium/net/PkpTest.java
@@ -66,8 +66,8 @@ if (mTestRule.testingJavaImpl()) { return; } - // Start HTTP2 Test Server System.loadLibrary("cronet_tests"); + TestFilesInstaller.installIfNeeded(getContext()); assertTrue(Http2TestServer.startHttp2TestServer( getContext(), SERVER_CERT_PEM, SERVER_KEY_PKCS8_PEM)); mServerHost = "test.example.com";
diff --git a/components/embedder_support/user_agent_utils_unittest.cc b/components/embedder_support/user_agent_utils_unittest.cc index ff91aaa6..dcc5af1 100644 --- a/components/embedder_support/user_agent_utils_unittest.cc +++ b/components/embedder_support/user_agent_utils_unittest.cc
@@ -643,7 +643,7 @@ EXPECT_NE(base::StringPrintf( kAndroid, version_info::GetMajorVersionNumber().c_str(), ""), GetUserAgent()); - EXPECT_NE(content::GetUnifiedPlatform().c_str(), + EXPECT_NE(content::GetUnifiedPlatformForTesting().c_str(), GetUserAgentPlatformOsCpu(GetUserAgent())); } @@ -675,11 +675,12 @@ blink::features::kForceMajorVersionInMinorPositionInUserAgent}, {}); { - EXPECT_EQ(base::StringPrintf("Mozilla/5.0 (%s) AppleWebKit/537.36 (KHTML, " - "like Gecko) Chrome/%s.%s.0.0 Safari/537.36", - content::GetUnifiedPlatform().c_str(), "99", - version_info::GetMajorVersionNumber().c_str()), - GetUserAgent()); + EXPECT_EQ( + base::StringPrintf("Mozilla/5.0 (%s) AppleWebKit/537.36 (KHTML, " + "like Gecko) Chrome/%s.%s.0.0 Safari/537.36", + content::GetUnifiedPlatformForTesting().c_str(), + "99", version_info::GetMajorVersionNumber().c_str()), + GetUserAgent()); } // Ensure that the ForceMajorVersionToMinorPosition policy is applied even @@ -706,7 +707,7 @@ // phase 5. #if BUILDFLAG(IS_ANDROID) EXPECT_NE(GetUserAgent(), GetReducedUserAgent()); - EXPECT_NE(content::GetUnifiedPlatform().c_str(), + EXPECT_NE(content::GetUnifiedPlatformForTesting().c_str(), GetUserAgentPlatformOsCpu(GetUserAgent())); #else EXPECT_EQ(GetUserAgent(), GetReducedUserAgent());
diff --git a/components/exo/seat.cc b/components/exo/seat.cc index 82f2ddc..4296e18b 100644 --- a/components/exo/seat.cc +++ b/components/exo/seat.cc
@@ -39,36 +39,9 @@ #if BUILDFLAG(IS_CHROMEOS_ASH) #include "ash/shell.h" #include "ui/aura/env.h" -#include "ui/events/gestures/gesture_recognizer.h" -#include "ui/wm/core/coordinate_conversion.h" #endif // BUILDFLAG(IS_CHROMEOS_ASH) namespace exo { -namespace { - -gfx::PointF GetCursorScreenPoint(ui::mojom::DragEventSource event_source, - aura::Window* window) { -#if BUILDFLAG(IS_CHROMEOS_ASH) - if (event_source == ui::mojom::DragEventSource::kTouch && - aura::Env::GetInstance()->is_touch_down()) { - DCHECK(window); - DCHECK(window->GetRootWindow()); - gfx::PointF touch_point_f; - bool got_touch_point = - aura::Env::GetInstance() - ->gesture_recognizer() - ->GetLastTouchPointForTarget(window, &touch_point_f); - if (got_touch_point) { - gfx::Point touch_point = gfx::ToFlooredPoint(touch_point_f); - wm::ConvertPointToScreen(window->GetRootWindow(), &touch_point); - return gfx::PointF(touch_point); - } - } -#endif // BUILDFLAG(IS_CHROMEOS_ASH) - return gfx::PointF(display::Screen::GetScreen()->GetCursorScreenPoint()); -} - -} // namespace Seat::Seat(std::unique_ptr<DataExchangeDelegate> delegate) : changing_clipboard_data_to_selection_source_(false), @@ -155,12 +128,12 @@ Surface* origin, Surface* icon, ui::mojom::DragEventSource event_source) { - gfx::PointF cursor_location = - GetCursorScreenPoint(event_source, origin->window()); + gfx::Point cursor_location = aura::Env::GetInstance()->GetLastPointerPoint( + event_source, origin->window(), /*fallback=*/absl::nullopt); // DragDropOperation manages its own lifetime. - drag_drop_operation_ = - DragDropOperation::Create(data_exchange_delegate_.get(), source, origin, - icon, cursor_location, event_source); + drag_drop_operation_ = DragDropOperation::Create( + data_exchange_delegate_.get(), source, origin, icon, + gfx::PointF(cursor_location), event_source); } void Seat::AbortPendingDragOperation() {
diff --git a/components/history_clusters/core/query_clusters_state.cc b/components/history_clusters/core/query_clusters_state.cc index cc4a3ed..3bc58d9 100644 --- a/components/history_clusters/core/query_clusters_state.cc +++ b/components/history_clusters/core/query_clusters_state.cc
@@ -166,14 +166,19 @@ return; const auto& raw_label_value = cluster.raw_label.value(); - - // Subtle code below: If we've NEVER encountered the label before, this [] - // operator initializes the count to 0, and then post-increments it to 1. - // If it already exists, the post-increment will up the count, but will - // return false. - if (raw_label_counts_[raw_label_value]++ == 0) { - unique_raw_labels_.push_back(raw_label_value); + // Warning: N^2 algorithm below. If this ends up scaling poorly, it can be + // optimized by adding a map that tracks which labels have been seen + // already. + auto it = std::find_if(raw_label_counts_so_far_.begin(), + raw_label_counts_so_far_.end(), + [&raw_label_value](const LabelCount& label_count) { + return label_count.first == raw_label_value; + }); + if (it == raw_label_counts_so_far_.end()) { + it = raw_label_counts_so_far_.insert(it, + std::make_pair(raw_label_value, 0)); } + it->second++; } }
diff --git a/components/history_clusters/core/query_clusters_state.h b/components/history_clusters/core/query_clusters_state.h index 7d5c5d6..511c512 100644 --- a/components/history_clusters/core/query_clusters_state.h +++ b/components/history_clusters/core/query_clusters_state.h
@@ -24,6 +24,8 @@ class HistoryClustersService; +using LabelCount = std::pair<std::u16string, size_t>; + // This object encapsulates the results of a query to HistoryClustersService. // It manages fetching more pages from the clustering backend as the user // scrolls down. @@ -54,15 +56,12 @@ // Used to request another batch of clusters of the same query. void LoadNextBatchOfClusters(ResultCallback callback); - // Use these together to iterate through the list of raw labels in the same - // order as the clusters are ordered. The counts can be fetched by inputting - // the labels into the map as keys - but note, this only counts the number - // of label instances seen SO FAR, not necessarily in all of History. - const std::vector<std::u16string>& unique_raw_labels() { - return unique_raw_labels_; - } - const std::map<std::u16string, size_t>& raw_label_counts() { - return raw_label_counts_; + // The list of raw labels in the same order as the clusters are ordered + // alongside the number of occurrences so far. The counts can be fetched by + // inputting the labels into the map as keys - but note, this only counts the + // number of label instances seen SO FAR, not necessarily in all of History. + const std::vector<LabelCount>& raw_label_counts_so_far() { + return raw_label_counts_so_far_; } private: @@ -97,16 +96,11 @@ // The string query the user entered into the searchbox. const std::string query_; - // The de-duplicated list of raw labels we've seen so far, in the same order - // as the clusters themselves were provided. This is only computed if `query` - // is empty. For non-empty `query`, this will be an empty list. - std::vector<std::u16string> unique_raw_labels_; - // Counts the number of instances of each raw label we've seen. Note that - // the value will always be an integer 1 or above. This is also used for our - // internal uniqueness test. Note: this only counts the number of raw labels - // of this string seen SO FAR, so unless we iterate through ALL the clusters, - // this number may be smaller than the total number in History. - std::map<std::u16string, size_t> raw_label_counts_; + // The de-duplicated list of raw labels we've seen so far and their number of + // occurrences, in the same order as the clusters themselves were provided. + // This is only computed if `query` is empty. For non-empty `query`, this will + // be an empty list. + std::vector<LabelCount> raw_label_counts_so_far_; // The continuation params used to track where the last query left off and // query for the "next page".
diff --git a/components/history_clusters/core/query_clusters_state_unittest.cc b/components/history_clusters/core/query_clusters_state_unittest.cc index 0941a51c..3b5d1ce 100644 --- a/components/history_clusters/core/query_clusters_state_unittest.cc +++ b/components/history_clusters/core/query_clusters_state_unittest.cc
@@ -285,20 +285,18 @@ auto result = InjectRawClustersAndAwaitPostProcessing( &state, {cluster1, cluster2, cluster4}, {}); ASSERT_EQ(result.cluster_batch.size(), 3U); - EXPECT_THAT(state.unique_raw_labels(), - ElementsAre(u"rawlabel1", u"rawlabel2")); - EXPECT_EQ(state.raw_label_counts().at(u"rawlabel1"), 2U); - EXPECT_EQ(state.raw_label_counts().at(u"rawlabel2"), 1U); + EXPECT_THAT(state.raw_label_counts_so_far(), + ElementsAre(std::make_pair(u"rawlabel1", 2), + std::make_pair(u"rawlabel2", 1))); // Test updating an existing count, and adding new ones after that. result = InjectRawClustersAndAwaitPostProcessing(&state, {cluster5, cluster3}, {}); ASSERT_EQ(result.cluster_batch.size(), 2U); - EXPECT_THAT(state.unique_raw_labels(), - ElementsAre(u"rawlabel1", u"rawlabel2", u"rawlabel3")); - EXPECT_EQ(state.raw_label_counts().at(u"rawlabel1"), 2U); - EXPECT_EQ(state.raw_label_counts().at(u"rawlabel2"), 2U); - EXPECT_EQ(state.raw_label_counts().at(u"rawlabel3"), 1U); + EXPECT_THAT(state.raw_label_counts_so_far(), + ElementsAre(std::make_pair(u"rawlabel1", 2), + std::make_pair(u"rawlabel2", 2), + std::make_pair(u"rawlabel3", 1))); } } // namespace history_clusters
diff --git a/components/history_clusters_strings.grdp b/components/history_clusters_strings.grdp index 30670a4..fe94d0a 100644 --- a/components/history_clusters_strings.grdp +++ b/components/history_clusters_strings.grdp
@@ -70,4 +70,8 @@ <message name="IDS_HISTORY_CLUSTERS_SEARCH_YOUR_JOURNEYS" desc="A label for the hint text for the search box in Chrome Journeys." formatter_data="android_java"> Search your Journeys </message> + <message name="IDS_HISTORY_CLUSTERS_N_MATCHES" desc="A label for the number of matching clusters for a given label." formatter_data="android_java"> + {NUM_MATCHES, plural, + =1 {# match} other {# matches}} + </message> </grit-part>
diff --git a/components/history_clusters_strings_grdp/IDS_HISTORY_CLUSTERS_N_MATCHES.png.sha1 b/components/history_clusters_strings_grdp/IDS_HISTORY_CLUSTERS_N_MATCHES.png.sha1 new file mode 100644 index 0000000..160e9ad --- /dev/null +++ b/components/history_clusters_strings_grdp/IDS_HISTORY_CLUSTERS_N_MATCHES.png.sha1
@@ -0,0 +1 @@ +e5b8707aa16ed376ba83f2258f78b489b928d069 \ No newline at end of file
diff --git a/components/metrics/metrics_service.cc b/components/metrics/metrics_service.cc index 5963fd95..fda6c7e2 100644 --- a/components/metrics/metrics_service.cc +++ b/components/metrics/metrics_service.cc
@@ -937,11 +937,6 @@ state_ = SENDING_LOGS; } -void MetricsService::IncrementLongPrefsValue(const char* path) { - int64_t value = local_state_->GetInt64(path); - local_state_->SetInt64(path, value + 1); -} - void MetricsService::RegisterMetricsProvider( std::unique_ptr<MetricsProvider> provider) { DCHECK_EQ(CONSTRUCTED, state_);
diff --git a/components/metrics/metrics_service.h b/components/metrics/metrics_service.h index 0963e20..1a6b61d 100644 --- a/components/metrics/metrics_service.h +++ b/components/metrics/metrics_service.h
@@ -367,10 +367,6 @@ // profiler data, as well as incremental stability-related metrics. void PrepareInitialMetricsLog(); - // Reads, increments and then sets the specified long preference that is - // stored as a string. - void IncrementLongPrefsValue(const char* path); - // Creates a new MetricsLog instance with the given |log_type|. std::unique_ptr<MetricsLog> CreateLog(MetricsLog::LogType log_type);
diff --git a/components/minidump_uploader/android/java/src/org/chromium/components/minidump_uploader/CrashFileManager.java b/components/minidump_uploader/android/java/src/org/chromium/components/minidump_uploader/CrashFileManager.java index 2062076..eeace51 100644 --- a/components/minidump_uploader/android/java/src/org/chromium/components/minidump_uploader/CrashFileManager.java +++ b/components/minidump_uploader/android/java/src/org/chromium/components/minidump_uploader/CrashFileManager.java
@@ -214,6 +214,13 @@ } /** + * @return True iff the provided File was ready be uploaded for the first time. + */ + public static boolean isReadyUploadForFirstTime(File fileToUpload) { + return fileToUpload.getName().contains(READY_FOR_UPLOAD_SUFFIX); + } + + /** * Attempts to rename the given file to mark it as a forced upload. This is useful for allowing * users to manually initiate previously skipped uploads. *
diff --git a/components/minidump_uploader/android/javatests/src/org/chromium/components/minidump_uploader/CrashFileManagerTest.java b/components/minidump_uploader/android/javatests/src/org/chromium/components/minidump_uploader/CrashFileManagerTest.java index 10b06056..34d2b13f 100644 --- a/components/minidump_uploader/android/javatests/src/org/chromium/components/minidump_uploader/CrashFileManagerTest.java +++ b/components/minidump_uploader/android/javatests/src/org/chromium/components/minidump_uploader/CrashFileManagerTest.java
@@ -162,6 +162,18 @@ @Test @SmallTest @Feature({"Android-AppBase"}) + public void testReadyForUploadForFirstTime() { + Assert.assertTrue(CrashFileManager.isReadyUploadForFirstTime( + new File(mTestRule.getCrashDir(), "foo.try0"))); + Assert.assertFalse(CrashFileManager.isReadyUploadForFirstTime( + new File(mTestRule.getCrashDir(), "foo.try1"))); + Assert.assertFalse(CrashFileManager.isReadyUploadForFirstTime( + new File(mTestRule.getCrashDir(), "foo"))); + } + + @Test + @SmallTest + @Feature({"Android-AppBase"}) public void testCrashFileManagerWithNull() { try { new CrashFileManager(null);
diff --git a/components/net_log/net_export_file_writer_unittest.cc b/components/net_log/net_export_file_writer_unittest.cc index 23244ff..15817d36e 100644 --- a/components/net_log/net_export_file_writer_unittest.cc +++ b/components/net_log/net_export_file_writer_unittest.cc
@@ -804,13 +804,12 @@ ASSERT_GE(events->GetListDeprecated().size(), 1u); // Check the URL in the params of the first event. - base::DictionaryValue* event; - EXPECT_TRUE(events->GetDictionary(0, &event)); - base::DictionaryValue* event_params; - EXPECT_TRUE(event->GetDictionary("params", &event_params)); - std::string event_url; - EXPECT_TRUE(event_params->GetString("url", &event_url)); - EXPECT_EQ(test_server.GetURL(kRedirectURL), event_url); + base::Value::Dict* event = events->GetList()[0].GetIfDict(); + EXPECT_TRUE(event); + base::Value::Dict* event_params = event->FindDict("params"); + EXPECT_TRUE(event_params); + EXPECT_EQ(test_server.GetURL(kRedirectURL), + *(event_params->FindString("url"))); block_fetch.Signal(); run_loop2.Run();
diff --git a/components/omnibox/browser/autocomplete_controller.cc b/components/omnibox/browser/autocomplete_controller.cc index 7ad7ce3f..f6a85f4 100644 --- a/components/omnibox/browser/autocomplete_controller.cc +++ b/components/omnibox/browser/autocomplete_controller.cc
@@ -296,6 +296,47 @@ search_service_worker_signal_sent_(false), template_url_service_(provider_client_->GetTemplateURLService()) { provider_types &= ~OmniboxFieldTrial::GetDisabledProviderTypes(); + + if (OmniboxFieldTrial::kAutocompleteStabilityAsyncProvidersFirst.Get()) { + // Providers run in the order they're added. Run these async providers 1st + // so their async requests can be kicked off before waiting a few + // milliseconds for the other sync providers to complete. + + if (provider_types & AutocompleteProvider::TYPE_SEARCH) { + search_provider_ = new SearchProvider(provider_client_.get(), this); + providers_.push_back(search_provider_.get()); + } + // Providers run in the order they're added. Add `HistoryURLProvider` after + // `SearchProvider` because: + // - `SearchProvider` synchronously queries the history database's + // keyword_search_terms and url table. + // - `HistoryUrlProvider` schedules a background task that also accesses the + // history database. + // If both db accesses happen concurrently, TSan complains. So put + // `HistoryURLProvider` later to make sure that `SearchProvider` is done + // doing its thing by the time the `HistoryURLProvider` task runs. (And hope + // that it completes before `AutocompleteController::Start()` is called the + // next time.) + if (provider_types & AutocompleteProvider::TYPE_HISTORY_URL) { + history_url_provider_ = + new HistoryURLProvider(provider_client_.get(), this); + if (provider_types & AutocompleteProvider::TYPE_HISTORY_URL) + providers_.push_back(history_url_provider_.get()); + } + if (provider_types & AutocompleteProvider::TYPE_DOCUMENT) { + document_provider_ = + DocumentProvider::Create(provider_client_.get(), this); + providers_.push_back(document_provider_.get()); + } + if (provider_types & AutocompleteProvider::TYPE_ON_DEVICE_HEAD) { + on_device_head_provider_ = + OnDeviceHeadProvider::Create(provider_client_.get(), this); + if (on_device_head_provider_) { + providers_.push_back(on_device_head_provider_.get()); + } + } + } + if (provider_types & AutocompleteProvider::TYPE_BOOKMARK) { bookmark_provider_ = new BookmarkProvider(provider_client_.get()); providers_.push_back(bookmark_provider_.get()); @@ -310,27 +351,28 @@ keyword_provider_ = new KeywordProvider(provider_client_.get(), this); providers_.push_back(keyword_provider_.get()); } - if (provider_types & AutocompleteProvider::TYPE_SEARCH) { - search_provider_ = new SearchProvider(provider_client_.get(), this); - providers_.push_back(search_provider_.get()); - } - // It's important that the HistoryURLProvider gets added after SearchProvider: - // AutocompleteController::Start() calls each providers' Start() function - // synchronously in the order they're in providers_. - // - SearchProvider::Start() synchronously queries the history database's - // keyword_search_terms and url table. - // - HistoryUrlProvider::Start schedules a background task that also accesses - // the history database. - // If both db accesses happen concurrently, TSan complains. - // So put HistoryURLProvider later to make sure that SearchProvider is done - // doing its thing by the time the HistoryURLProvider task runs. - // (And hope that it completes before AutocompleteController::Start() is - // called the next time.) - if (provider_types & AutocompleteProvider::TYPE_HISTORY_URL) { - history_url_provider_ = - new HistoryURLProvider(provider_client_.get(), this); - if (provider_types & AutocompleteProvider::TYPE_HISTORY_URL) - providers_.push_back(history_url_provider_.get()); + if (!OmniboxFieldTrial::kAutocompleteStabilityAsyncProvidersFirst.Get()) { + if (provider_types & AutocompleteProvider::TYPE_SEARCH) { + search_provider_ = new SearchProvider(provider_client_.get(), this); + providers_.push_back(search_provider_.get()); + } + // Providers run in the order they're added. Add `HistoryURLProvider` after + // `SearchProvider` because: + // - `SearchProvider` synchronously queries the history database's + // keyword_search_terms and url table. + // - `HistoryUrlProvider` schedules a background task that also accesses the + // history database. + // If both db accesses happen concurrently, TSan complains. So put + // `HistoryURLProvider` later to make sure that `SearchProvider` is done + // doing its thing by the time the `HistoryURLProvider` task runs. (And hope + // that it completes before `AutocompleteController::Start()` is called the + // next time.) + if (provider_types & AutocompleteProvider::TYPE_HISTORY_URL) { + history_url_provider_ = + new HistoryURLProvider(provider_client_.get(), this); + if (provider_types & AutocompleteProvider::TYPE_HISTORY_URL) + providers_.push_back(history_url_provider_.get()); + } } if (provider_types & AutocompleteProvider::TYPE_SHORTCUTS) providers_.push_back(new ShortcutsProvider(provider_client_.get())); @@ -353,15 +395,18 @@ providers_.push_back( new ZeroSuggestVerbatimMatchProvider(provider_client_.get())); } - if (provider_types & AutocompleteProvider::TYPE_DOCUMENT) { - document_provider_ = DocumentProvider::Create(provider_client_.get(), this); - providers_.push_back(document_provider_.get()); - } - if (provider_types & AutocompleteProvider::TYPE_ON_DEVICE_HEAD) { - on_device_head_provider_ = - OnDeviceHeadProvider::Create(provider_client_.get(), this); - if (on_device_head_provider_) { - providers_.push_back(on_device_head_provider_.get()); + if (!OmniboxFieldTrial::kAutocompleteStabilityAsyncProvidersFirst.Get()) { + if (provider_types & AutocompleteProvider::TYPE_DOCUMENT) { + document_provider_ = + DocumentProvider::Create(provider_client_.get(), this); + providers_.push_back(document_provider_.get()); + } + if (provider_types & AutocompleteProvider::TYPE_ON_DEVICE_HEAD) { + on_device_head_provider_ = + OnDeviceHeadProvider::Create(provider_client_.get(), this); + if (on_device_head_provider_) { + providers_.push_back(on_device_head_provider_.get()); + } } } if (provider_types & AutocompleteProvider::TYPE_CLIPBOARD) { @@ -396,7 +441,8 @@ provider_client_.get(), history_quick_provider_, bookmark_provider_)); } if (provider_types & AutocompleteProvider::TYPE_OPEN_TAB) { - providers_.push_back(new OpenTabProvider(provider_client_.get())); + open_tab_provider_ = new OpenTabProvider(provider_client_.get()); + providers_.push_back(open_tab_provider_.get()); } // Ideally, we'd check `IsApplicationLocaleSupportedByJourneys()` when // constructing `provider_types`. But that's usually constructed in @@ -870,8 +916,7 @@ if (!done_) { // This conditional needs to match the conditional in Start that invokes // StartExpireTimer. - result_.TransferOldMatches(input_, &old_matches_to_reuse, - template_url_service_); + result_.TransferOldMatches(input_, &old_matches_to_reuse); if (OmniboxFieldTrial::kAutocompleteStabilityPreserveDefaultAfterTransfer .Get()) { result_.SortAndCull(input_, template_url_service_,
diff --git a/components/omnibox/browser/autocomplete_controller.h b/components/omnibox/browser/autocomplete_controller.h index e6290e1b..2f685a0 100644 --- a/components/omnibox/browser/autocomplete_controller.h +++ b/components/omnibox/browser/autocomplete_controller.h
@@ -27,6 +27,7 @@ #include "components/omnibox/browser/autocomplete_result.h" #include "components/omnibox/browser/bookmark_provider.h" #include "components/omnibox/browser/omnibox_log.h" +#include "components/omnibox/browser/open_tab_provider.h" class ClipboardProvider; class DocumentProvider; @@ -196,6 +197,7 @@ VoiceSuggestProvider* voice_suggest_provider() const { return voice_suggest_provider_; } + OpenTabProvider* open_tab_provider() const { return open_tab_provider_; } const AutocompleteInput& input() const { return input_; } const AutocompleteResult& result() const { return result_; } @@ -353,6 +355,8 @@ raw_ptr<VoiceSuggestProvider> voice_suggest_provider_; + raw_ptr<OpenTabProvider> open_tab_provider_; + // Input passed to Start. AutocompleteInput input_;
diff --git a/components/omnibox/browser/autocomplete_match.h b/components/omnibox/browser/autocomplete_match.h index 3e5c26a..2cca96b 100644 --- a/components/omnibox/browser/autocomplete_match.h +++ b/components/omnibox/browser/autocomplete_match.h
@@ -41,6 +41,8 @@ class TemplateURL; class TemplateURLService; +enum class SuggestionGroupId; + namespace base { class Time; } // namespace base @@ -677,9 +679,9 @@ // The optional suggestion group Id. Used to look up the suggestion group info // such as the header text this match must appear under from ACResult. // - // If this value exists, it should always be positive and nonzero. In Java and - // JavaScript, -1 is used as a sentinel value, but should never occur in C++. - absl::optional<int> suggestion_group_id; + // This is converted to a primitive int type in Java and JavaScript; with -1 + // (SuggestionGroupId::kInvalid) used as a sentinel value. + absl::optional<SuggestionGroupId> suggestion_group_id; // If true, UI-level code should swap the contents and description fields // before displaying.
diff --git a/components/omnibox/browser/autocomplete_match_android.cc b/components/omnibox/browser/autocomplete_match_android.cc index be46479..81f8112 100644 --- a/components/omnibox/browser/autocomplete_match_android.cc +++ b/components/omnibox/browser/autocomplete_match_android.cc
@@ -113,7 +113,8 @@ url::GURLAndroid::FromNativeGURL(env, image_url), j_image_dominant_color, SupportsDeletion(), j_post_content_type, j_post_content, - suggestion_group_id.value_or(kInvalidSuggestionGroupId), + static_cast<int>( + suggestion_group_id.value_or(SuggestionGroupId::kInvalid)), j_query_tiles, ToJavaByteArray(env, clipboard_image_data), has_tab_match.value_or(false), ToJavaArrayOfStrings(env, suggest_titles),
diff --git a/components/omnibox/browser/autocomplete_provider_unittest.cc b/components/omnibox/browser/autocomplete_provider_unittest.cc index 0a0cdcc..c4881c57 100644 --- a/components/omnibox/browser/autocomplete_provider_unittest.cc +++ b/components/omnibox/browser/autocomplete_provider_unittest.cc
@@ -326,7 +326,7 @@ struct SuggestionGroupsTestData { SuggestionGroupsMap suggestion_groups_map; - std::vector<absl::optional<int>> suggestion_group_ids; + std::vector<absl::optional<SuggestionGroupId>> suggestion_group_ids; }; struct AssistedQueryStatsTestData { @@ -608,7 +608,9 @@ for (auto suggestion_group_id : test_data.suggestion_group_ids) { AutocompleteMatch match(nullptr, relevance--, false, AutocompleteMatchType::SEARCH_SUGGEST_PERSONALIZED); - match.suggestion_group_id = suggestion_group_id; + if (suggestion_group_id.has_value()) { + match.suggestion_group_id = suggestion_group_id.value(); + } matches.push_back(match); } result_.Reset(); @@ -853,24 +855,34 @@ // Tests that the AutocompleteResult is updated with the suggestion group // information and matches with group IDs are grouped and demoted correctly. -// Also verifies that 1) suggestion group IDs without associated suggestion -// group information are stripped away. and 2) headers are optional for groups. -TEST_F(AutocompleteProviderTest, Headers) { +// Also verifies that: +// 1) headers are optional for suggestion groups. +// 2) suggestion groups are ordered based on their priories. +// 3) suggestion group IDs without associated suggestion group information are +// stripped away. +TEST_F(AutocompleteProviderTest, SuggestionGroups) { ResetControllerWithKeywordAndSearchProviders(); - const int kRecommendedGroupId = 1; + const auto kRecommendedGroupId = + SuggestionGroupId::kNonPersonalizedZeroSuggest1; const std::u16string kRecommended = u"Recommended for you"; - const int kRecentSearchesGroupId = 2; + const auto kRecentSearchesGroupId = + SuggestionGroupId::kNonPersonalizedZeroSuggest2; const std::u16string kRecentSearches = u"Recent Searches"; // This exists to verify that suggestion group IDs without associated // suggestion groups information are stripped away. - const int kBadSuggestionGroupId = 99; + const auto kBadSuggestionGroupId = SuggestionGroupId::kInvalid; { + // Headers are optional for suggestion groups. SuggestionGroupsMap suggestion_groups_map; suggestion_groups_map[kRecommendedGroupId].header = kRecommended; + suggestion_groups_map[kRecommendedGroupId].priority = + SuggestionGroupPriority::kRemoteZeroSuggest4; suggestion_groups_map[kRecentSearchesGroupId].header = u""; + suggestion_groups_map[kRecentSearchesGroupId].priority = + SuggestionGroupPriority::kRemoteZeroSuggest3; UpdateResultsWithSuggestionGroupsTestData({std::move(suggestion_groups_map), { {}, @@ -896,9 +908,14 @@ result_.GetHeaderForSuggestionGroup(kRecommendedGroupId)); } { + // Suggestion groups are ordered based on their priories. SuggestionGroupsMap suggestion_groups_map; suggestion_groups_map[kRecommendedGroupId].header = kRecommended; + suggestion_groups_map[kRecommendedGroupId].priority = + SuggestionGroupPriority::kRemoteZeroSuggest3; suggestion_groups_map[kRecentSearchesGroupId].header = kRecentSearches; + suggestion_groups_map[kRecentSearchesGroupId].priority = + SuggestionGroupPriority::kRemoteZeroSuggest4; UpdateResultsWithSuggestionGroupsTestData({std::move(suggestion_groups_map), { {}, @@ -912,22 +929,24 @@ EXPECT_FALSE(result_.match_at(1)->suggestion_group_id.has_value()); - EXPECT_EQ(kRecentSearchesGroupId, + EXPECT_EQ(kRecommendedGroupId, result_.match_at(2)->suggestion_group_id.value()); - EXPECT_EQ(kRecentSearches, - result_.GetHeaderForSuggestionGroup(kRecentSearchesGroupId)); + EXPECT_EQ(kRecommended, + result_.GetHeaderForSuggestionGroup(kRecommendedGroupId)); EXPECT_EQ(kRecentSearchesGroupId, result_.match_at(3)->suggestion_group_id.value()); EXPECT_EQ(kRecentSearches, result_.GetHeaderForSuggestionGroup(kRecentSearchesGroupId)); - EXPECT_EQ(kRecommendedGroupId, + EXPECT_EQ(kRecentSearchesGroupId, result_.match_at(4)->suggestion_group_id.value()); - EXPECT_EQ(kRecommended, - result_.GetHeaderForSuggestionGroup(kRecommendedGroupId)); + EXPECT_EQ(kRecentSearches, + result_.GetHeaderForSuggestionGroup(kRecentSearchesGroupId)); } { + // suggestion group IDs without associated suggestion group information are + // stripped away. SuggestionGroupsMap suggestion_groups_map; suggestion_groups_map[kRecommendedGroupId].header = kRecommended; suggestion_groups_map[kRecentSearchesGroupId].header = kRecentSearches;
diff --git a/components/omnibox/browser/autocomplete_result.cc b/components/omnibox/browser/autocomplete_result.cc index b157ce0..67ff2127 100644 --- a/components/omnibox/browser/autocomplete_result.cc +++ b/components/omnibox/browser/autocomplete_result.cc
@@ -158,10 +158,8 @@ AutocompleteResult::~AutocompleteResult() = default; -void AutocompleteResult::TransferOldMatches( - const AutocompleteInput& input, - AutocompleteResult* old_matches, - TemplateURLService* template_url_service) { +void AutocompleteResult::TransferOldMatches(const AutocompleteInput& input, + AutocompleteResult* old_matches) { // Don't transfer matches from done providers. If the match is still // relevant, it'll already be in `result_`, potentially with updated fields // that shouldn't be deduped with the out-of-date match. Otherwise, the @@ -331,19 +329,7 @@ const size_t num_matches = CalculateNumMatches(is_zero_suggest, matches_, comparing_object); - if (base::FeatureList::IsEnabled(omnibox::kRetainSuggestionsWithHeaders)) { - size_t num_regular_suggestions = 0; - base::EraseIf(matches_, - [&num_regular_suggestions, num_matches](const auto& match) { - // Trim suggestions without group IDs to the given limit. - if (!match.suggestion_group_id.has_value()) { - num_regular_suggestions++; - return num_regular_suggestions > num_matches; - } - // Do not trim suggestions with group IDs. - return false; - }); - } else { + if (!is_zero_suggest) { matches_.resize(num_matches); } @@ -359,6 +345,24 @@ GroupAndDemoteMatchesInGroups(); + if (is_zero_suggest) { + if (base::FeatureList::IsEnabled(omnibox::kRetainSuggestionsWithHeaders)) { + size_t num_regular_suggestions = 0; + base::EraseIf(matches_, + [&num_regular_suggestions, num_matches](const auto& match) { + // Trim suggestions without group IDs to the given limit. + if (!match.suggestion_group_id.has_value()) { + num_regular_suggestions++; + return num_regular_suggestions > num_matches; + } + // Do not trim suggestions with group IDs. + return false; + }); + } else { + matches_.resize(num_matches); + } + } + // Run sanity checks on the default match to make sure all the suggestions // are congruent with the user's input. Skip checks in these cases: // - If the default match has no |destination_url|. An example of this is the @@ -395,52 +399,50 @@ } void AutocompleteResult::GroupAndDemoteMatchesInGroups() { - // Create a map from suggestion group ID to the index it first appears. - // Reserve the first spot for matches without group IDs. - std::map<int, int> group_id_index_map = {{kInvalidSuggestionGroupId, 0}}; - for (auto it = matches_.begin(); it != matches_.end(); ++it) { - if (it->suggestion_group_id.has_value()) { - const int group_id = it->suggestion_group_id.value(); - - if (suggestion_groups_map_.find(group_id) != - suggestion_groups_map_.end()) { - // Record suggestion group information into the additional_info field - // for chrome://omnibox. - it->RecordAdditionalInfo("group id", group_id); - it->RecordAdditionalInfo("group header", - GetHeaderForSuggestionGroup(group_id)); - } else { - // Strip group IDs from the matches for which there is no suggestion - // group information. These matches should instead be treated as - // ordinary matches with no group IDs. - it->suggestion_group_id.reset(); - } + bool any_matches_in_groups = false; + for (auto& match : *this) { + if (!match.suggestion_group_id.has_value()) { + continue; } + any_matches_in_groups = true; - int group_id = it->suggestion_group_id.value_or(kInvalidSuggestionGroupId); - // Use the 1-based index of the match to record the first appearance of its - // group ID since 0 is reserved for matches without group IDs. We are - // interested in the relative values of these indices only and their - // absolute values hardly matter. - int index = std::distance(matches_.begin(), it) + 1; - // map::insert doesn't insert the value if the map already contains the key. - group_id_index_map.insert(std::pair<int, int>(group_id, index)); + const SuggestionGroupId group_id = match.suggestion_group_id.value(); + if (suggestion_groups_map_.find(group_id) != suggestion_groups_map_.end()) { + // Record suggestion group information into the additional_info field + // for chrome://omnibox. + match.RecordAdditionalInfo("group id", static_cast<int>(group_id)); + match.RecordAdditionalInfo("group header", + GetHeaderForSuggestionGroup(group_id)); + match.RecordAdditionalInfo( + "group priority", + static_cast<int>(GetPriorityForSuggestionGroup(group_id))); + } else { + // Strip group IDs from the matches for which there is no suggestion + // group information. These matches should instead be treated as + // ordinary matches with no group IDs. + match.suggestion_group_id.reset(); + } } - // No need to group and demote matches with group IDs if none exists. - if (group_id_index_map.size() == 1) + // No need to group and demote matches in groups if none exists. + if (!any_matches_in_groups) { return; + } - // Sort the matches based on the order in which their group IDs first appear - // while preserving the existing order of matches with the same group ID. + // Sort the matches based on the order in which their groups should appear + // while preserving the existing order of matches within the same group. std::stable_sort( - matches_.begin(), matches_.end(), - [&group_id_index_map](const auto& a, const auto& b) { - const int a_group_id = - a.suggestion_group_id.value_or(kInvalidSuggestionGroupId); - const int b_group_id = - b.suggestion_group_id.value_or(kInvalidSuggestionGroupId); - return group_id_index_map[a_group_id] < group_id_index_map[b_group_id]; + matches_.begin(), matches_.end(), [this](const auto& a, const auto& b) { + // Note that matches not in a group must appear before the matches in + // one; thus the order of the following two early checks is important. + if (!b.suggestion_group_id.has_value()) { + return false; + } + if (!a.suggestion_group_id.has_value()) { + return true; + } + return GetPriorityForSuggestionGroup(a.suggestion_group_id.value()) < + GetPriorityForSuggestionGroup(b.suggestion_group_id.value()); }); } @@ -956,19 +958,18 @@ } std::u16string AutocompleteResult::GetHeaderForSuggestionGroup( - int suggestion_group_id) const { + SuggestionGroupId suggestion_group_id) const { const auto& it = suggestion_groups_map_.find(suggestion_group_id); - if (it != suggestion_groups_map_.end()) - return it->second.header; - return std::u16string(); + DCHECK(it != suggestion_groups_map_.end()); + return it->second.header; } bool AutocompleteResult::IsSuggestionGroupHidden( PrefService* prefs, - int suggestion_group_id) const { + SuggestionGroupId suggestion_group_id) const { omnibox::SuggestionGroupVisibility user_preference = omnibox::GetUserPreferenceForSuggestionGroupVisibility( - prefs, suggestion_group_id); + prefs, static_cast<int>(suggestion_group_id)); if (user_preference == omnibox::SuggestionGroupVisibility::HIDDEN) return true; @@ -977,9 +978,15 @@ DCHECK_EQ(user_preference, omnibox::SuggestionGroupVisibility::DEFAULT); const auto& it = suggestion_groups_map_.find(suggestion_group_id); - if (it != suggestion_groups_map_.end()) - return it->second.hidden; - return false; + DCHECK(it != suggestion_groups_map_.end()); + return it->second.hidden; +} + +SuggestionGroupPriority AutocompleteResult::GetPriorityForSuggestionGroup( + SuggestionGroupId suggestion_group_id) const { + const auto& it = suggestion_groups_map_.find(suggestion_group_id); + DCHECK(it != suggestion_groups_map_.end()); + return it->second.priority; } void AutocompleteResult::MergeSuggestionGroupsMap(
diff --git a/components/omnibox/browser/autocomplete_result.h b/components/omnibox/browser/autocomplete_result.h index 60391fc..f71907e 100644 --- a/components/omnibox/browser/autocomplete_result.h +++ b/components/omnibox/browser/autocomplete_result.h
@@ -92,8 +92,7 @@ // Moves matches from |old_matches| to provide a consistent result set. // |old_matches| is mutated during this, and should not be used afterwards. void TransferOldMatches(const AutocompleteInput& input, - AutocompleteResult* old_matches, - TemplateURLService* template_url_service); + AutocompleteResult* old_matches); // Adds a new set of matches to the result set. Does not re-sort. void AppendMatches(const ACMatches& matches); @@ -113,24 +112,26 @@ TemplateURLService* template_url_service, const AutocompleteMatch* preserve_default_match = nullptr); - // Ensures that matches with a suggestion_group_id value, are grouped together - // at the bottom of result set based on the order in which their group IDs - // first appear in the result set. This is done for two reasons: + // Ensures that matches belonging to suggestion groups, i.e., those with a + // suggestion_group_id value and a corresponding suggestion group info, are + // grouped together at the bottom of result set based on the order in which + // the groups should appear in the result set. This is done for two reasons: // // 1) Certain groups of remote zero-prefix matches need to appear under a - // header for transparency reasons. These optional headers are uniquely - // identified by the group IDs. Also it is possible for zero-prefix matches - // from different providers (e.g., local and remote) to mix and match. Hence, - // after mixing and sorting the matches, we group the ones with the same - // group ID and demote them to the bottom of the result set to ensure, one, - // matches without group IDs (and thus headers) appear at the top of the - // result set, and two, there are no interleaving headers; whether caused by - // bad server data or by mixing of local and remote zero-prefix suggestions. + // header as specified in SuggestionGroup. SuggestionGroups are uniquely + // identified by the group IDs in |suggestion_groups_map_|. It is also + // possible for zero-prefix matches to mix and match while belonging to the + // same groups (e.g., bad server data or mixing of local and remote + // suggestions from different providers). Hence, after mixing, deduping, and + // sorting the matches, we group the ones with the same group ID and demote + // them to the bottom of the result set based on a predetermined order. This + // ensures matches without group IDs or SuggestionGroup to appear at the top + // of the result set, and two, there are no interleaving of groups or headers; // // 2) Certain groups of non-zero-prefix matches, such as those produced by the // HistoryClusterProvider, must appear at the bottom of the result set. - // Setting a group ID on those matches ensures they sink to the bottom of the - // result set. + // Specifying a group ID (and a corresponding suggestion group info) for those + // matches ensures that would happen. // // Called after matches are deduped and sorted and before they are culled. void GroupAndDemoteMatchesInGroups(); @@ -232,19 +233,26 @@ // This is only used for logging. std::vector<MatchDedupComparator> GetMatchDedupComparators() const; - // Gets the header string associated with |suggestion_group_id|. Returns an - // empty string if no suggestion group is found. - std::u16string GetHeaderForSuggestionGroup(int suggestion_group_id) const; + // Returns the header string associated with |suggestion_group_id|. + // DCHECKs whether |suggestion_group_id| is found in |suggestion_groups_map_|. + std::u16string GetHeaderForSuggestionGroup( + SuggestionGroupId suggestion_group_id) const; // Returns whether or not |suggestion_group_id| should be collapsed in the UI. // This method takes into account both the user's stored |prefs| as well as // the server-provided visibility hint for |suggestion_group_id|. + // DCHECKs whether |suggestion_group_id| is found in |suggestion_groups_map_|. bool IsSuggestionGroupHidden(PrefService* prefs, - int suggestion_group_id) const; + SuggestionGroupId suggestion_group_id) const; + + // Returns the priority associated with |suggestion_group_id|. + // DCHECKs whether |suggestion_group_id| is found in |suggestion_groups_map_|. + SuggestionGroupPriority GetPriorityForSuggestionGroup( + SuggestionGroupId suggestion_group_id) const; // Updates |suggestion_groups_map_| with the suggestion groups information // from |suggeston_groups_map|. Followed by GroupAndDemoteMatchesInGroups() - // which sorts the matches based on the order in which their groups first + // which sorts the matches based on the order in which their groups should // appear while preserving the existing order of matches within the same // group. void MergeSuggestionGroupsMap(
diff --git a/components/omnibox/browser/autocomplete_result_android.cc b/components/omnibox/browser/autocomplete_result_android.cc index 91049f41..818e528 100644 --- a/components/omnibox/browser/autocomplete_result_android.cc +++ b/components/omnibox/browser/autocomplete_result_android.cc
@@ -99,7 +99,7 @@ size_t index = 0; for (const auto& suggestion_group : suggestion_groups_map_) { - group_ids[index] = suggestion_group.first; + group_ids[index] = static_cast<int>(suggestion_group.first); group_names[index] = suggestion_group.second.header; group_collapsed_states[index] = suggestion_group.second.hidden; ++index;
diff --git a/components/omnibox/browser/autocomplete_result_unittest.cc b/components/omnibox/browser/autocomplete_result_unittest.cc index 671e78b..b30a292f 100644 --- a/components/omnibox/browser/autocomplete_result_unittest.cc +++ b/components/omnibox/browser/autocomplete_result_unittest.cc
@@ -106,7 +106,7 @@ AutocompleteMatchType::Type type{AutocompleteMatchType::SEARCH_SUGGEST}; // Suggestion Group ID for this suggestion - absl::optional<int> suggestion_group_id; + absl::optional<SuggestionGroupId> suggestion_group_id; // Inline autocompletion. std::string inline_autocompletion; @@ -208,7 +208,9 @@ match->relevance = data.relevance; match->allowed_to_be_default_match = data.allowed_to_be_default_match; match->duplicate_matches = data.duplicate_matches; - match->suggestion_group_id = data.suggestion_group_id; + if (data.suggestion_group_id.has_value()) { + match->suggestion_group_id = data.suggestion_group_id.value(); + } match->inline_autocompletion = base::UTF8ToUTF16(data.inline_autocompletion); } @@ -280,8 +282,7 @@ AutocompleteResult current_result; current_result.AppendMatches(current_matches); current_result.SortAndCull(input, template_url_service_.get()); - current_result.TransferOldMatches(input, &last_result, - template_url_service_.get()); + current_result.TransferOldMatches(input, &last_result); current_result.SortAndCull(input, template_url_service_.get()); AssertResultMatches(current_result, expected, expected_size); @@ -1819,7 +1820,8 @@ result.AppendMatches(matches); result.SortAndCull(input, template_url_service_.get()); - TestData expected_data[] = { + ASSERT_EQ(6U, AutocompleteResult::GetMaxMatches(/*is_zero_suggest=*/false)); + const std::array<TestData, 6> expected_data{{ // default match unmoved {3, 2, 800, true, {}, AutocompleteMatchType::HISTORY_TITLE}, // search types @@ -1829,207 +1831,300 @@ {6, 3, 1100, false, {}, AutocompleteMatchType::BOOKMARK_TITLE}, {5, 2, 1000, false, {}, AutocompleteMatchType::HISTORY_BODY}, {1, 2, 600, false, {}, AutocompleteMatchType::HISTORY_URL}, - }; - - AssertResultMatches(result, expected_data, - AutocompleteResult::GetMaxMatches()); + }}; + AssertResultMatches(result, expected_data.begin(), expected_data.size()); } #endif -TEST_F(AutocompleteResultTest, SortAndCullKeepGroupedSuggestionsLast) { - base::test::ScopedFeatureList feature_list; - feature_list.InitAndEnableFeatureWithParameters( - omnibox::kUIExperimentMaxAutocompleteMatches, - {{OmniboxFieldTrial::kUIMaxAutocompleteMatchesParam, "6"}}); - TestData data[] = { - {0, 1, 500, false, {}, AutocompleteMatchType::SEARCH_SUGGEST, 1}, - {1, 2, 600, false, {}, AutocompleteMatchType::HISTORY_URL}, - {2, 1, 700, false, {}, AutocompleteMatchType::SEARCH_SUGGEST, 1}, - {3, 2, 800, true, {}, AutocompleteMatchType::HISTORY_TITLE}, - {4, 1, 900, false, {}, AutocompleteMatchType::SEARCH_SUGGEST, 2}, - {5, 2, 1000, false, {}, AutocompleteMatchType::SEARCH_SUGGEST, 2}, - {6, 3, 1100, false, {}, AutocompleteMatchType::BOOKMARK_TITLE}, - }; - ACMatches matches; - PopulateAutocompleteMatches(data, std::size(data), &matches); - - AutocompleteInput input(u"a", metrics::OmniboxEventProto::OTHER, - TestSchemeClassifier()); - AutocompleteResult result; - - SuggestionGroupsMap suggestion_groups_map; - suggestion_groups_map[1].header = u"1"; - suggestion_groups_map[2].header = u"2"; - result.MergeSuggestionGroupsMap(suggestion_groups_map); - result.AppendMatches(matches); - result.SortAndCull(input, template_url_service_.get()); - - TestData expected_data[] = { - // default match unmoved - {3, 2, 800, true, {}, AutocompleteMatchType::HISTORY_TITLE}, - // search types - {6, 3, 1100, false, {}, AutocompleteMatchType::BOOKMARK_TITLE}, - {1, 2, 600, false, {}, AutocompleteMatchType::HISTORY_URL}, - // Group <2> is scored higher - {5, 2, 1000, false, {}, AutocompleteMatchType::SEARCH_SUGGEST}, - {4, 1, 900, false, {}, AutocompleteMatchType::SEARCH_SUGGEST}, - // Group <1> is scored lower - {2, 1, 700, false, {}, AutocompleteMatchType::SEARCH_SUGGEST}, - }; - - AssertResultMatches(result, expected_data, - AutocompleteResult::GetMaxMatches()); -} - -TEST_F(AutocompleteResultTest, SortAndCull_NoRetainSuggestionsWithHeaders) { +TEST_F(AutocompleteResultTest, SortAndCull_DemoteSuggestionGroups_ExceedLimit) { base::test::ScopedFeatureList feature_list; feature_list.InitWithFeaturesAndParameters( - { - /* Enabled list */ - {omnibox::kUIExperimentMaxAutocompleteMatches, - {{OmniboxFieldTrial::kUIMaxAutocompleteMatchesParam, "2"}}}, // - }, + {{omnibox::kUIExperimentMaxAutocompleteMatches, + {{OmniboxFieldTrial::kUIMaxAutocompleteMatchesParam, "6"}}}, + {omnibox::kMaxZeroSuggestMatches, + {{OmniboxFieldTrial::kMaxZeroSuggestMatchesParam, "5"}}}}, {{omnibox::kDynamicMaxAutocomplete, omnibox::kRetainSuggestionsWithHeaders}}); + + const auto group_1 = SuggestionGroupId::kNonPersonalizedZeroSuggest1; + const auto group_2 = SuggestionGroupId::kNonPersonalizedZeroSuggest2; TestData data[] = { - {1, 1, 1100, true, {}, AutocompleteMatchType::SEARCH_SUGGEST}, - {2, 1, 1099, true, {}, AutocompleteMatchType::SEARCH_SUGGEST}, - {3, 1, 1098, true, {}, AutocompleteMatchType::SEARCH_SUGGEST}, - {4, 1, 1097, true, {}, AutocompleteMatchType::SEARCH_SUGGEST}, - {5, 1, 1096, true, {}, AutocompleteMatchType::SEARCH_SUGGEST}, - // Do not trip the Dynamic Max Autocomplete - {5, 2, 1097, false, {}, AutocompleteMatchType::HISTORY_URL}, - {6, 1, 1095, true, {}, AutocompleteMatchType::SEARCH_SUGGEST, 1}, - {7, 1, 1094, true, {}, AutocompleteMatchType::SEARCH_SUGGEST, 1}, - {8, 1, 1093, true, {}, AutocompleteMatchType::SEARCH_SUGGEST, 1}, - {9, 1, 1092, true, {}, AutocompleteMatchType::SEARCH_SUGGEST, 1}, + {0, 4, 500, false, {}, AutocompleteMatchType::SEARCH_SUGGEST, group_1}, + {1, 2, 600, false, {}, AutocompleteMatchType::HISTORY_URL}, + {2, 1, 700, false, {}, AutocompleteMatchType::SEARCH_SUGGEST, group_1}, + {3, 2, 800, true, {}, AutocompleteMatchType::HISTORY_TITLE}, + {4, 1, 900, false, {}, AutocompleteMatchType::SEARCH_SUGGEST, group_2}, + {5, 2, 1000, false, {}, AutocompleteMatchType::SEARCH_SUGGEST, group_2}, + {6, 3, 1100, false, {}, AutocompleteMatchType::BOOKMARK_TITLE}, }; ACMatches matches; PopulateAutocompleteMatches(data, std::size(data), &matches); - AutocompleteInput input(u"a", metrics::OmniboxEventProto::OTHER, - TestSchemeClassifier()); - AutocompleteResult result; - + // Suggestion groups have SuggestionGroupPriority::kDefault priority by + // default. SuggestionGroupsMap suggestion_groups_map; - suggestion_groups_map[1].header = u"1"; - suggestion_groups_map[2].header = u"2"; - result.MergeSuggestionGroupsMap(suggestion_groups_map); - result.AppendMatches(matches); - result.SortAndCull(input, template_url_service_.get()); + suggestion_groups_map[group_1].header = u"1"; + suggestion_groups_map[group_2].header = u"2"; - std::array<TestData, 2> expected_data{{ - {1, 1, 1100, true, {}, AutocompleteMatchType::SEARCH_SUGGEST}, - {2, 1, 1099, true, {}, AutocompleteMatchType::SEARCH_SUGGEST}, - }}; + { + AutocompleteInput typed_input(u"a", metrics::OmniboxEventProto::OTHER, + TestSchemeClassifier()); + AutocompleteResult result; + result.MergeSuggestionGroupsMap(suggestion_groups_map); + result.AppendMatches(matches); + result.SortAndCull(typed_input, template_url_service_.get()); - AssertResultMatches(result, expected_data.begin(), expected_data.size()); + ASSERT_EQ(6U, AutocompleteResult::GetMaxMatches(/*is_zero_suggest=*/false)); + const std::array<TestData, 6> expected_data{{ + // default match unmoved + {3, 2, 800, true, {}, AutocompleteMatchType::HISTORY_TITLE}, + // other types + {6, 3, 1100, false, {}, AutocompleteMatchType::BOOKMARK_TITLE}, + {1, 2, 600, false, {}, AutocompleteMatchType::HISTORY_URL}, + // Group one is scored higher + {5, 2, 1000, false, {}, AutocompleteMatchType::SEARCH_SUGGEST}, + {4, 1, 900, false, {}, AutocompleteMatchType::SEARCH_SUGGEST}, + // Group two is scored lower + {2, 1, 700, false, {}, AutocompleteMatchType::SEARCH_SUGGEST}, + }}; + AssertResultMatches(result, expected_data.begin(), expected_data.size()); + } + { + AutocompleteInput zero_prefix_input(u"", metrics::OmniboxEventProto::NTP, + TestSchemeClassifier()); + zero_prefix_input.set_focus_type(OmniboxFocusType::ON_FOCUS); + AutocompleteResult result; + result.MergeSuggestionGroupsMap(suggestion_groups_map); + result.AppendMatches(matches); + result.SortAndCull(zero_prefix_input, template_url_service_.get()); + + ASSERT_EQ(5U, AutocompleteResult::GetMaxMatches(/*is_zero_suggest=*/true)); + const std::array<TestData, 5> expected_data{{ + // default match unmoved + {3, 2, 800, true, {}, AutocompleteMatchType::HISTORY_TITLE}, + // other types + {6, 3, 1100, false, {}, AutocompleteMatchType::BOOKMARK_TITLE}, + {1, 2, 600, false, {}, AutocompleteMatchType::HISTORY_URL}, + // Group two is scored higher + {5, 2, 1000, false, {}, AutocompleteMatchType::SEARCH_SUGGEST}, + {4, 1, 900, false, {}, AutocompleteMatchType::SEARCH_SUGGEST}, + }}; + AssertResultMatches(result, expected_data.begin(), expected_data.size()); + } + + // Set priorities that contradict the scores of the matches in groups. + suggestion_groups_map[group_1].priority = + SuggestionGroupPriority::kRemoteZeroSuggest1; + suggestion_groups_map[group_2].priority = + SuggestionGroupPriority::kRemoteZeroSuggest2; + + { + AutocompleteInput typed_input(u"a", metrics::OmniboxEventProto::OTHER, + TestSchemeClassifier()); + AutocompleteResult result; + result.MergeSuggestionGroupsMap(suggestion_groups_map); + result.AppendMatches(matches); + result.SortAndCull(typed_input, template_url_service_.get()); + + // typed matches are first culled, then grouped based on group IDs, and + // then ordered based on group priorities. + ASSERT_EQ(6U, AutocompleteResult::GetMaxMatches(/*is_zero_suggest=*/false)); + const std::array<TestData, 6> expected_data{{ + // default match unmoved + {3, 2, 800, true, {}, AutocompleteMatchType::HISTORY_TITLE}, + // other types + {6, 3, 1100, false, {}, AutocompleteMatchType::BOOKMARK_TITLE}, + {1, 2, 600, false, {}, AutocompleteMatchType::HISTORY_URL}, + // Group one has a higher priority + {2, 1, 700, false, {}, AutocompleteMatchType::SEARCH_SUGGEST}, + // Group two has a lower priority + {5, 2, 1000, false, {}, AutocompleteMatchType::SEARCH_SUGGEST}, + {4, 1, 900, false, {}, AutocompleteMatchType::SEARCH_SUGGEST}, + }}; + AssertResultMatches(result, expected_data.begin(), expected_data.size()); + } + { + AutocompleteInput zero_prefix_input(u"", metrics::OmniboxEventProto::NTP, + TestSchemeClassifier()); + zero_prefix_input.set_focus_type(OmniboxFocusType::ON_FOCUS); + AutocompleteResult result; + result.MergeSuggestionGroupsMap(suggestion_groups_map); + result.AppendMatches(matches); + result.SortAndCull(zero_prefix_input, template_url_service_.get()); + + // zero-prefix matches are first grouped basd on group IDs, then ordered + // based on group priorities, then culled. + ASSERT_EQ(5U, AutocompleteResult::GetMaxMatches(/*is_zero_suggest=*/true)); + const std::array<TestData, 5> expected_data{{ + // default match unmoved + {3, 2, 800, true, {}, AutocompleteMatchType::HISTORY_TITLE}, + // other types + {6, 3, 1100, false, {}, AutocompleteMatchType::BOOKMARK_TITLE}, + {1, 2, 600, false, {}, AutocompleteMatchType::HISTORY_URL}, + // Group <1> has a higher priority + {2, 1, 700, false, {}, AutocompleteMatchType::SEARCH_SUGGEST}, + {0, 4, 500, false, {}, AutocompleteMatchType::SEARCH_SUGGEST}, + }}; + AssertResultMatches(result, expected_data.begin(), expected_data.size()); + } } TEST_F(AutocompleteResultTest, - SortAndCull_RetainSuggestionsWithHeaders_NoDynamicMaxAutocomplete) { + SortAndCull_RetainSuggestionGroups_NoDynamicMaxAutocomplete) { base::test::ScopedFeatureList feature_list; feature_list.InitWithFeaturesAndParameters( - { - /* Enabled list */ - {omnibox::kUIExperimentMaxAutocompleteMatches, - {{OmniboxFieldTrial::kUIMaxAutocompleteMatchesParam, "2"}}}, // - {omnibox::kRetainSuggestionsWithHeaders, {}} // - }, + {{omnibox::kUIExperimentMaxAutocompleteMatches, + {{OmniboxFieldTrial::kUIMaxAutocompleteMatchesParam, "2"}}}, + {omnibox::kMaxZeroSuggestMatches, + {{OmniboxFieldTrial::kMaxZeroSuggestMatchesParam, "2"}}}, + {omnibox::kRetainSuggestionsWithHeaders, {}}}, {{omnibox::kDynamicMaxAutocomplete}}); + + const auto group_1 = SuggestionGroupId::kNonPersonalizedZeroSuggest1; + const auto group_2 = SuggestionGroupId::kNonPersonalizedZeroSuggest2; TestData data[] = { {1, 1, 1100, true, {}, AutocompleteMatchType::SEARCH_SUGGEST}, {2, 1, 1099, true, {}, AutocompleteMatchType::SEARCH_SUGGEST}, {3, 1, 1098, true, {}, AutocompleteMatchType::SEARCH_SUGGEST}, {4, 1, 1097, true, {}, AutocompleteMatchType::SEARCH_SUGGEST}, {5, 1, 1096, true, {}, AutocompleteMatchType::SEARCH_SUGGEST}, - // Do not trip the Dynamic Max Autocomplete {5, 2, 1097, false, {}, AutocompleteMatchType::HISTORY_URL}, - {6, 1, 1095, true, {}, AutocompleteMatchType::SEARCH_SUGGEST, 1}, - {7, 1, 1094, true, {}, AutocompleteMatchType::SEARCH_SUGGEST, 1}, - {8, 1, 1093, true, {}, AutocompleteMatchType::SEARCH_SUGGEST, 1}, - {9, 1, 1092, true, {}, AutocompleteMatchType::SEARCH_SUGGEST, 1}, + {6, 1, 1095, true, {}, AutocompleteMatchType::SEARCH_SUGGEST, group_1}, + {7, 1, 1094, true, {}, AutocompleteMatchType::SEARCH_SUGGEST, group_2}, + {8, 1, 1093, true, {}, AutocompleteMatchType::SEARCH_SUGGEST, group_2}, + {9, 1, 1092, true, {}, AutocompleteMatchType::SEARCH_SUGGEST, group_2}, }; ACMatches matches; PopulateAutocompleteMatches(data, std::size(data), &matches); - AutocompleteInput input(u"a", metrics::OmniboxEventProto::OTHER, - TestSchemeClassifier()); - AutocompleteResult result; - + // Suggestion groups have SuggestionGroupPriority::kDefault priority by + // default. SuggestionGroupsMap suggestion_groups_map; - suggestion_groups_map[1].header = u"1"; - suggestion_groups_map[2].header = u"2"; - result.MergeSuggestionGroupsMap(suggestion_groups_map); - result.AppendMatches(matches); - result.SortAndCull(input, template_url_service_.get()); + suggestion_groups_map[group_1].header = u"1"; + suggestion_groups_map[group_2].header = u"2"; - std::array<TestData, 6> expected_data{{ - {1, 1, 1100, true, {}, AutocompleteMatchType::SEARCH_SUGGEST}, - {2, 1, 1099, true, {}, AutocompleteMatchType::SEARCH_SUGGEST}, - {6, 1, 1095, true, {}, AutocompleteMatchType::SEARCH_SUGGEST, 1}, - {7, 1, 1094, true, {}, AutocompleteMatchType::SEARCH_SUGGEST, 1}, - {8, 1, 1093, true, {}, AutocompleteMatchType::SEARCH_SUGGEST, 1}, - {9, 1, 1092, true, {}, AutocompleteMatchType::SEARCH_SUGGEST, 1}, - }}; + { + AutocompleteInput typed_input(u"a", metrics::OmniboxEventProto::OTHER, + TestSchemeClassifier()); + AutocompleteResult result; + result.MergeSuggestionGroupsMap(suggestion_groups_map); + result.AppendMatches(matches); + result.SortAndCull(typed_input, template_url_service_.get()); - AssertResultMatches(result, expected_data.begin(), expected_data.size()); + // Retaining suggestion groups has no effect on typed matched. + ASSERT_EQ(2U, AutocompleteResult::GetMaxMatches(/*is_zero_suggest=*/false)); + const std::array<TestData, 2> expected_data{{ + {1, 1, 1100, true, {}, AutocompleteMatchType::SEARCH_SUGGEST}, + {2, 1, 1099, true, {}, AutocompleteMatchType::SEARCH_SUGGEST}, + }}; + AssertResultMatches(result, expected_data.begin(), expected_data.size()); + } + { + AutocompleteInput zero_prefix_input(u"", metrics::OmniboxEventProto::NTP, + TestSchemeClassifier()); + zero_prefix_input.set_focus_type(OmniboxFocusType::ON_FOCUS); + AutocompleteResult result; + + result.MergeSuggestionGroupsMap(suggestion_groups_map); + result.AppendMatches(matches); + result.SortAndCull(zero_prefix_input, template_url_service_.get()); + + ASSERT_EQ(2U, AutocompleteResult::GetMaxMatches(/*is_zero_suggest=*/true)); + const std::array<TestData, 6> expected_data{{ + {1, 1, 1100, true, {}, AutocompleteMatchType::SEARCH_SUGGEST}, + {2, 1, 1099, true, {}, AutocompleteMatchType::SEARCH_SUGGEST}, + // Group one is scored higher + {6, 1, 1095, true, {}, AutocompleteMatchType::SEARCH_SUGGEST, group_1}, + // Group two is scored lower + {7, 1, 1094, true, {}, AutocompleteMatchType::SEARCH_SUGGEST, group_2}, + {8, 1, 1093, true, {}, AutocompleteMatchType::SEARCH_SUGGEST, group_2}, + {9, 1, 1092, true, {}, AutocompleteMatchType::SEARCH_SUGGEST, group_2}, + }}; + AssertResultMatches(result, expected_data.begin(), expected_data.size()); + } } TEST_F(AutocompleteResultTest, - SortAndCull_RetainSuggestionsWithHeaders_WithDynamicMaxAutocomplete) { + SortAndCull_RetainSuggestionGroups_WithDynamicMaxAutocomplete) { base::test::ScopedFeatureList feature_list; feature_list.InitWithFeaturesAndParameters( - { - /* Enabled list */ - {omnibox::kUIExperimentMaxAutocompleteMatches, - {{OmniboxFieldTrial::kUIMaxAutocompleteMatchesParam, "2"}}}, // - {omnibox::kDynamicMaxAutocomplete, - {{OmniboxFieldTrial::kDynamicMaxAutocompleteIncreasedLimitParam, - "3"}}}, - {omnibox::kRetainSuggestionsWithHeaders, {}} // - }, - {/* Disabled list */}); + {{omnibox::kUIExperimentMaxAutocompleteMatches, + {{OmniboxFieldTrial::kUIMaxAutocompleteMatchesParam, "2"}}}, + {omnibox::kMaxZeroSuggestMatches, + {{OmniboxFieldTrial::kMaxZeroSuggestMatchesParam, "2"}}}, + {omnibox::kDynamicMaxAutocomplete, + {{OmniboxFieldTrial::kDynamicMaxAutocompleteIncreasedLimitParam, "3"}}}, + {omnibox::kRetainSuggestionsWithHeaders, {}}}, + {}); + + const auto group_1 = SuggestionGroupId::kNonPersonalizedZeroSuggest1; + const auto group_2 = SuggestionGroupId::kNonPersonalizedZeroSuggest2; TestData data[] = { {1, 1, 1100, true, {}, AutocompleteMatchType::SEARCH_SUGGEST}, {2, 1, 1099, true, {}, AutocompleteMatchType::SEARCH_SUGGEST}, {3, 1, 1098, true, {}, AutocompleteMatchType::SEARCH_SUGGEST}, {4, 1, 1097, true, {}, AutocompleteMatchType::SEARCH_SUGGEST}, {5, 1, 1096, true, {}, AutocompleteMatchType::SEARCH_SUGGEST}, - // Do not trip the Dynamic Max Autocomplete {5, 2, 1097, false, {}, AutocompleteMatchType::HISTORY_URL}, - {6, 1, 1095, true, {}, AutocompleteMatchType::SEARCH_SUGGEST, 1}, - {7, 1, 1094, true, {}, AutocompleteMatchType::SEARCH_SUGGEST, 2}, - {8, 1, 1093, true, {}, AutocompleteMatchType::SEARCH_SUGGEST, 2}, - {9, 1, 1092, true, {}, AutocompleteMatchType::SEARCH_SUGGEST, 2}, + {6, 1, 1095, true, {}, AutocompleteMatchType::SEARCH_SUGGEST, group_1}, + {7, 1, 1094, true, {}, AutocompleteMatchType::SEARCH_SUGGEST, group_2}, + {8, 1, 1093, true, {}, AutocompleteMatchType::SEARCH_SUGGEST, group_2}, + {9, 1, 1092, true, {}, AutocompleteMatchType::SEARCH_SUGGEST, group_2}, }; ACMatches matches; PopulateAutocompleteMatches(data, std::size(data), &matches); - AutocompleteInput input(u"a", metrics::OmniboxEventProto::OTHER, - TestSchemeClassifier()); - AutocompleteResult result; - + // Set priorities that contradict the scores of the matches in groups. SuggestionGroupsMap suggestion_groups_map; - suggestion_groups_map[1].header = u"1"; - suggestion_groups_map[2].header = u"2"; - result.MergeSuggestionGroupsMap(suggestion_groups_map); - result.AppendMatches(matches); - result.SortAndCull(input, template_url_service_.get()); + suggestion_groups_map[group_1].header = u"1"; + suggestion_groups_map[group_1].priority = + SuggestionGroupPriority::kRemoteZeroSuggest2; + suggestion_groups_map[group_2].header = u"2"; + suggestion_groups_map[group_2].priority = + SuggestionGroupPriority::kRemoteZeroSuggest1; - std::array<TestData, 7> expected_data{{ - {1, 1, 1100, true, {}, AutocompleteMatchType::SEARCH_SUGGEST}, - {2, 1, 1099, true, {}, AutocompleteMatchType::SEARCH_SUGGEST}, - {3, 1, 1098, true, {}, AutocompleteMatchType::SEARCH_SUGGEST}, - // Group <1> is scored higher - {6, 1, 1095, true, {}, AutocompleteMatchType::SEARCH_SUGGEST, 1}, - // Group <2> is scored lower - {7, 1, 1094, true, {}, AutocompleteMatchType::SEARCH_SUGGEST, 2}, - {8, 1, 1093, true, {}, AutocompleteMatchType::SEARCH_SUGGEST, 2}, - {9, 1, 1092, true, {}, AutocompleteMatchType::SEARCH_SUGGEST, 2}, - }}; + { + AutocompleteInput typed_input(u"a", metrics::OmniboxEventProto::OTHER, + TestSchemeClassifier()); + AutocompleteResult result; + result.MergeSuggestionGroupsMap(suggestion_groups_map); + result.AppendMatches(matches); + result.SortAndCull(typed_input, template_url_service_.get()); - AssertResultMatches(result, expected_data.begin(), expected_data.size()); + // Retaining suggestion groups has no effect on typed matched. + ASSERT_EQ(2U, AutocompleteResult::GetMaxMatches(/*is_zero_suggest=*/false)); + const std::array<TestData, 3> expected_data{{ + {1, 1, 1100, true, {}, AutocompleteMatchType::SEARCH_SUGGEST}, + {2, 1, 1099, true, {}, AutocompleteMatchType::SEARCH_SUGGEST}, + {3, 1, 1098, true, {}, AutocompleteMatchType::SEARCH_SUGGEST}, + }}; + AssertResultMatches(result, expected_data.begin(), expected_data.size()); + } + { + AutocompleteInput zero_prefix_input(u"", metrics::OmniboxEventProto::NTP, + TestSchemeClassifier()); + zero_prefix_input.set_focus_type(OmniboxFocusType::ON_FOCUS); + AutocompleteResult result; + + result.MergeSuggestionGroupsMap(suggestion_groups_map); + result.AppendMatches(matches); + result.SortAndCull(zero_prefix_input, template_url_service_.get()); + + // DynamicMaxAutocomplete has no effect on zero-prefix matched. The number + // of results not in groups is determined by the zero-suggest limit. + ASSERT_EQ(2U, AutocompleteResult::GetMaxMatches(/*is_zero_suggest=*/true)); + const std::array<TestData, 6> expected_data{{ + {1, 1, 1100, true, {}, AutocompleteMatchType::SEARCH_SUGGEST}, + {2, 1, 1099, true, {}, AutocompleteMatchType::SEARCH_SUGGEST}, + // Group two has a higher priority + {7, 1, 1094, true, {}, AutocompleteMatchType::SEARCH_SUGGEST, group_2}, + {8, 1, 1093, true, {}, AutocompleteMatchType::SEARCH_SUGGEST, group_2}, + {9, 1, 1092, true, {}, AutocompleteMatchType::SEARCH_SUGGEST, group_2}, + // Group one has a lower priority + {6, 1, 1095, true, {}, AutocompleteMatchType::SEARCH_SUGGEST, group_1}, + }}; + AssertResultMatches(result, expected_data.begin(), expected_data.size()); + } } TEST_F(AutocompleteResultTest,
diff --git a/components/omnibox/browser/history_cluster_provider.cc b/components/omnibox/browser/history_cluster_provider.cc index a7df8e0..8b79f85 100644 --- a/components/omnibox/browser/history_cluster_provider.cc +++ b/components/omnibox/browser/history_cluster_provider.cc
@@ -108,8 +108,10 @@ match.contents_class.push_back( ACMatchClassification(0, ACMatchClassification::URL)); - match.suggestion_group_id = 1; - suggestion_groups_map_[1].MergeFrom({}); + match.suggestion_group_id = SuggestionGroupId::kHistoryCluster; + // Insert a corresponding SuggestionGroup with default values in the + // suggestion groups map; otherwise the group ID will get dropped. + suggestion_groups_map_[SuggestionGroupId::kHistoryCluster]; return match; }
diff --git a/components/omnibox/browser/history_url_provider.cc b/components/omnibox/browser/history_url_provider.cc index fcc0554..99941c6 100644 --- a/components/omnibox/browser/history_url_provider.cc +++ b/components/omnibox/browser/history_url_provider.cc
@@ -112,7 +112,7 @@ // precalculate the ending position. for (size_t i = 0; i < matches->size(); ++i) { for (history::HistoryMatches::iterator j(matches->begin() + i + 1); - j != matches->end(); ) { + j != matches->end();) { if ((*matches)[i].url_info.url() == j->url_info.url()) j = matches->erase(j); else @@ -121,9 +121,9 @@ } } -// Calculates a new relevance score applying half-life time decaying to |count| -// using |time_since_last_visit| and |score_buckets|. This function will never -// return a score higher than |undecayed_relevance|; in other words, it can only +// Calculates a new relevance score applying half-life time decaying to `count` +// using `time_since_last_visit` and `score_buckets`. This function will never +// return a score higher than `undecayed_relevance`; in other words, it can only // demote the old score. double CalculateRelevanceUsingScoreBuckets( const HUPScoringParams::ScoreBuckets& score_buckets, @@ -143,22 +143,23 @@ const HUPScoringParams::ScoreBuckets::CountMaxRelevance* score_bucket = nullptr; - const double factor = (score_buckets.use_decay_factor() ? - decay_factor : decayed_count); + const double factor = + (score_buckets.use_decay_factor() ? decay_factor : decayed_count); for (size_t i = 0; i < score_buckets.buckets().size(); ++i) { score_bucket = &score_buckets.buckets()[i]; if (factor >= score_bucket->first) break; } - return (score_bucket && (undecayed_relevance > score_bucket->second)) ? - score_bucket->second : undecayed_relevance; + return (score_bucket && (undecayed_relevance > score_bucket->second)) + ? score_bucket->second + : undecayed_relevance; } -// Returns a new relevance score for the given |match| based on the -// |old_relevance| score and |scoring_params|. The new relevance score is -// guaranteed to be less than or equal to |old_relevance|. In other words, this -// function can only demote a score, never boost it. Returns |old_relevance| if +// Returns a new relevance score for the given `match` based on the +// `old_relevance` score and `scoring_params`. The new relevance score is +// guaranteed to be less than or equal to `old_relevance`. In other words, this +// function can only demote a score, never boost it. Returns `old_relevance` if // experimental scoring is disabled. int CalculateRelevanceScoreUsingScoringParams( const history::HistoryMatch& match, @@ -184,7 +185,7 @@ } // Extracts typed_count, visit_count, and last_visited time from the URLRow and -// puts them in the additional info field of the |match| for display in +// puts them in the additional info field of the `match` for display in // about:omnibox. void RecordAdditionalInfoFromUrlRow(const history::URLRow& info, AutocompleteMatch* match) { @@ -193,16 +194,16 @@ match->RecordAdditionalInfo("last visit", info.last_visit()); } -// If |create_if_necessary| is true, ensures that |matches| contains an entry -// for |info|, creating a new such entry if necessary (using |match_template| +// If `create_if_necessary` is true, ensures that `matches` contains an entry +// for `info`, creating a new such entry if necessary (using `match_template` // to get all the other match data). // -// If |promote| is true, this also ensures the entry is the first element in -// |matches|, moving or adding it to the front as appropriate. When |promote| +// If `promote` is true, this also ensures the entry is the first element in +// `matches`, moving or adding it to the front as appropriate. When `promote` // is false, existing matches are left in place, and newly added matches are // placed at the back. // -// It's OK to call this function with both |create_if_necessary| and |promote| +// It's OK to call this function with both `create_if_necessary` and `promote` // false, in which case we'll do nothing. // // Returns whether the match exists regardless if it was promoted/created. @@ -211,7 +212,7 @@ history::HistoryMatches* matches, bool create_if_necessary, bool promote) { - // |matches| may already have an entry for this. + // `matches` may already have an entry for this. for (history::HistoryMatches::iterator i(matches->begin()); i != matches->end(); ++i) { if (i->url_info.url() == info.url()) { @@ -225,7 +226,7 @@ if (!create_if_necessary) return false; - // No entry, so create one using |match_template| as a basis. + // No entry, so create one using `match_template` as a basis. history::HistoryMatch match = match_template; match.url_info = info; if (promote) @@ -236,7 +237,7 @@ return true; } -// Returns whether |match| is suitable for inline autocompletion. +// Returns whether `match` is suitable for inline autocompletion. bool CanPromoteMatchForInlineAutocomplete(const history::HistoryMatch& match) { // We can promote this match if it's been typed at least n times, where n == 1 // for "simple" (host-only) URLs and n == 2 for others. We set a higher bar @@ -245,10 +246,10 @@ // URLs, if the user manually edits the URL or types some long thing in by // hand, we wouldn't want to immediately start autocompleting it. return match.url_info.typed_count() && - ((match.url_info.typed_count() > 1) || match.IsHostOnly()); + ((match.url_info.typed_count() > 1) || match.IsHostOnly()); } -// Given the user's |input| and a |match| created from it, reduce the match's +// Given the user's `input` and a `match` created from it, reduce the match's // URL to just a host. If this host still matches the user input, return it. // Returns the empty string on failure. GURL ConvertToHostOnly(const history::HistoryMatch& match, @@ -314,7 +315,7 @@ private: raw_ptr<HistoryURLProvider> provider_; raw_ptr<history::URLDatabase> db_; - Type type_; + Type type_ = Type::kInvalid; history::URLRow url_row_; }; @@ -322,7 +323,7 @@ HistoryURLProvider* provider, const AutocompleteInput& input, history::URLDatabase* db) - : provider_(provider), db_(db), type_(Type::kInvalid) { + : provider_(provider), db_(db) { // Detect email addresses. These cases will look like "http://user@site/", // and because the history backend strips auth creds, we'll get a bogus exact // match below if the user has visited "site". @@ -354,7 +355,7 @@ // If the input does not correspond to a visited URL, we check if the // canonical URL has an intranet hostname that the user visited (albeit with a - // different port and/or path) before. If this is true, |url_row_| will be + // different port and/or path) before. If this is true, `url_row_` will be // mostly empty: the URL field will be set to an unvisited URL with the same // scheme and host as some visited URL in the db. const GURL as_known_intranet_url = provider_->AsKnownIntranetURL(db_, input); @@ -387,8 +388,7 @@ search_terms_data(SearchTermsData::MakeSnapshot(search_terms_data)), allow_deleting_browser_history(allow_deleting_browser_history) {} -HistoryURLProviderParams::~HistoryURLProviderParams() { -} +HistoryURLProviderParams::~HistoryURLProviderParams() = default; size_t HistoryURLProviderParams::EstimateMemoryUsage() const { size_t res = 0; @@ -417,14 +417,14 @@ void HistoryURLProvider::Start(const AutocompleteInput& input, bool minimal_changes) { TRACE_EVENT0("omnibox", "HistoryURLProvider::Start"); - // NOTE: We could try hard to do less work in the |minimal_changes| case + // NOTE: We could try hard to do less work in the `minimal_changes` case // here; some clever caching would let us reuse the raw matches from the // history DB without re-querying. However, we'd still have to go back to // the history thread to mark these up properly, and if pass 2 is currently // running, we'd need to wait for it to return to the main thread before // doing this (we can't just write new data for it to read due to thread // safety issues). At that point it's just as fast, and easier, to simply - // re-run the query from scratch and ignore |minimal_changes|. + // re-run the query from scratch and ignore `minimal_changes`. // Cancel any in-progress query. Stop(true, false); @@ -491,16 +491,17 @@ // Get the default search provider and search terms data now since we have to // retrieve these on the UI thread, and the second pass runs on the history - // thread. |template_url_service| can be null when testing. + // thread. `template_url_service` can be null when testing. TemplateURLService* template_url_service = client()->GetTemplateURLService(); - const TemplateURL* default_search_provider = template_url_service ? - template_url_service->GetDefaultSearchProvider() : nullptr; + const TemplateURL* default_search_provider = + template_url_service ? template_url_service->GetDefaultSearchProvider() + : nullptr; const SearchTermsData* search_terms_data = template_url_service ? &template_url_service->search_terms_data() : nullptr; // Create the data structure for the autocomplete passes. We'll save this off - // onto the |params_| member for later deletion below if we need to run pass + // onto the `params_` member for later deletion below if we need to run pass // 2. std::unique_ptr<HistoryURLProviderParams> params(new HistoryURLProviderParams( fixed_up_input, autocomplete_input, trim_http, what_you_typed_match, @@ -520,7 +521,7 @@ DoAutocomplete(nullptr, url_db, params.get()); matches_.clear(); PromoteMatchesIfNecessary(*params); - // NOTE: We don't reset |params| here since at least the |promote_type| + // NOTE: We don't reset `params` here since at least the `promote_type` // field on it will be read by the second pass -- see comments in // DoAutocomplete(). } @@ -576,15 +577,14 @@ base::BindOnce(&HistoryURLProvider::QueryComplete, this, params)); } -HistoryURLProvider::~HistoryURLProvider() { - // Note: This object can get leaked on shutdown if there are pending - // requests on the database (which hold a reference to us). Normally, these - // messages get flushed for each thread. We do a round trip from main, to - // history, back to main while holding a reference. If the main thread - // completes before the history thread, the message to delegate back to the - // main thread will not run and the reference will leak. Therefore, don't do - // anything on destruction. -} +// Note: This object can get leaked on shutdown if there are pending +// requests on the database (which hold a reference to us). Normally, these +// messages get flushed for each thread. We do a round trip from main, to +// history, back to main while holding a reference. If the main thread +// completes before the history thread, the message to delegate back to the +// main thread will not run and the reference will leak. Therefore, don't do +// anything on destruction. +HistoryURLProvider::~HistoryURLProvider() = default; // static int HistoryURLProvider::CalculateRelevance(MatchType match_type, @@ -670,7 +670,7 @@ } // Try to create a shorter suggestion from the best match. - // We consider the what you typed match eligible for display when it's + // We consider the what-you-typed match eligible for display when it's // navigable and there's a reasonable chance the user intended to do // something other than search. We use a variety of heuristics to determine // this, e.g. whether the user explicitly typed a scheme, or if omnibox @@ -706,10 +706,11 @@ // FRONT_HISTORY_MATCH during the first pass, the second pass will not // consider the exact suggestion to be in history and therefore will not // suggest the exact input as a better match. (Note that during the first - // pass, this conditional will always succeed since |promote_type| is + // pass, this conditional will always succeed since `promote_type` is // initialized to NEITHER.) (params->promote_type != HistoryURLProviderParams::FRONT_HISTORY_MATCH); - params->exact_suggestion_is_in_history = can_check_history_for_exact_match && + params->exact_suggestion_is_in_history = + can_check_history_for_exact_match && FixupExactSuggestion(db, classifier, params); // If we succeeded in fixing up the exact match based on the user's history, @@ -736,9 +737,9 @@ params->promote_type = HistoryURLProviderParams::FRONT_HISTORY_MATCH; } else { // Failed to promote any URLs. Use the What You Typed match, if we have it. - params->promote_type = params->have_what_you_typed_match ? - HistoryURLProviderParams::WHAT_YOU_TYPED_MATCH : - HistoryURLProviderParams::NEITHER; + params->promote_type = params->have_what_you_typed_match + ? HistoryURLProviderParams::WHAT_YOU_TYPED_MATCH + : HistoryURLProviderParams::NEITHER; } const size_t max_results = @@ -759,9 +760,8 @@ if (params.promote_type == HistoryURLProviderParams::NEITHER) return; if (params.promote_type == HistoryURLProviderParams::FRONT_HISTORY_MATCH) { - matches_.push_back( - HistoryMatchToACMatch(params, 0, - CalculateRelevance(INLINE_AUTOCOMPLETE, 0))); + matches_.push_back(HistoryMatchToACMatch( + params, 0, CalculateRelevance(INLINE_AUTOCOMPLETE, 0))); } // There are two cases where we need to add the what-you-typed-match: // * If params.promote_type is WHAT_YOU_TYPED_MATCH, we're being explicitly @@ -786,7 +786,7 @@ void HistoryURLProvider::QueryComplete( HistoryURLProviderParams* params_gets_deleted) { TRACE_EVENT0("omnibox", "HistoryURLProvider::QueryComplete"); - // Ensure |params_gets_deleted| gets deleted on exit. + // Ensure `params_gets_deleted` gets deleted on exit. std::unique_ptr<HistoryURLProviderParams> params(params_gets_deleted); // If the user hasn't already started another query, clear our member pointer @@ -798,24 +798,26 @@ if (params->cancel_flag.IsSet()) return; // Already set done_ when we canceled, no need to set it again. - // Don't modify |matches_| if the query failed, since it might have a default - // match in it, whereas |params->matches| will be empty. + // Don't modify `matches_` if the query failed, since it might have a default + // match in it, whereas `params->matches` will be empty. if (!params->failed) { matches_.clear(); PromoteMatchesIfNecessary(*params); // Determine relevance of highest scoring match, if any. - int relevance = matches_.empty() ? - CalculateRelevance(NORMAL, - static_cast<int>(params->matches.size() - 1)) : - matches_[0].relevance; + int relevance = + matches_.empty() + ? CalculateRelevance(NORMAL, + static_cast<int>(params->matches.size() - 1)) + : matches_[0].relevance; // Convert the history matches to autocomplete matches. If we promoted the // first match, skip over it. - const size_t first_match = - (params->exact_suggestion_is_in_history || - (params->promote_type == - HistoryURLProviderParams::FRONT_HISTORY_MATCH)) ? 1 : 0; + const size_t first_match = (params->exact_suggestion_is_in_history || + (params->promote_type == + HistoryURLProviderParams::FRONT_HISTORY_MATCH)) + ? 1 + : 0; for (size_t i = first_match; i < params->matches.size(); ++i) { // All matches score one less than the previous match. --relevance; @@ -975,14 +977,14 @@ bool can_add_search_base_to_matches = !params->have_what_you_typed_match; if (search_base.is_empty()) { // Search from what the user typed when we couldn't reduce the best match - // to a host. Careful: use a substring of |match| here, rather than the - // first match in |params|, because they might have different prefixes. If + // to a host. Careful: use a substring of `match` here, rather than the + // first match in `params`, because they might have different prefixes. If // the user typed "google.com", params->what_you_typed_match will hold - // "http://google.com/", but |match| might begin with + // "http://google.com/", but `match` might begin with // "http://www.google.com/". // TODO: this should be cleaned up, and is probably incorrect for IDN. - std::string new_match = match.url_info.url().possibly_invalid_spec(). - substr(0, match.input_location + params->input.text().length()); + std::string new_match = match.url_info.url().possibly_invalid_spec().substr( + 0, match.input_location + params->input.text().length()); search_base = GURL(new_match); if (search_base.is_empty()) return false; // Can't construct a URL from which to start a search. @@ -991,9 +993,9 @@ (search_base != params->what_you_typed_match.destination_url); } if (search_base == match.url_info.url()) - return false; // Couldn't shorten |match|, so no URLs to search over. + return false; // Couldn't shorten `match`, so no URLs to search over. - // Search the DB for short URLs between our base and |match|. + // Search the DB for short URLs between our base and `match`. history::URLRow info(search_base); bool promote = true; // A short URL is only worth suggesting if it's been visited at least a third @@ -1007,8 +1009,9 @@ // autocomplete, unstable. const int min_typed_count = match.url_info.typed_count() ? 1 : 0; if (!db->FindShortestURLFromBase(search_base.possibly_invalid_spec(), - match.url_info.url().possibly_invalid_spec(), min_visit_count, - min_typed_count, can_add_search_base_to_matches, &info)) { + match.url_info.url().possibly_invalid_spec(), + min_visit_count, min_typed_count, + can_add_search_base_to_matches, &info)) { if (!can_add_search_base_to_matches) return false; // Couldn't find anything and can't add the search base. @@ -1029,7 +1032,7 @@ HistoryURLProviderParams* params) const { const base::Time& threshold(history::AutocompleteAgeThreshold()); for (history::HistoryMatches::iterator i(params->matches.begin()); - i != params->matches.end(); ) { + i != params->matches.end();) { if (RowQualifiesAsSignificant(i->url_info, threshold) && (!params->default_search_provider || !params->default_search_provider->IsSearchURL( @@ -1045,14 +1048,14 @@ history::HistoryMatches* matches, size_t max_results) const { for (size_t source = 0; - (source < matches->size()) && (source < max_results); ) { + (source < matches->size()) && (source < max_results);) { const GURL& url = (*matches)[source].url_info.url(); // TODO(brettw) this should go away when everything uses GURL. history::RedirectList redirects = backend->QueryRedirectsFrom(url); if (!redirects.empty()) { - // Remove all but the first occurrence of any of these redirects in the - // search results. We also must add the URL we queried for, since it may - // not be the first match and we'd want to remove it. + // Remove all but the first occurrence of these redirects in the search + // results. We also must add the URL we queried for, since it may not be + // the first match and we'd want to remove it. // // For example, when A redirects to B and our matches are [A, X, B], // we'll get B as the redirects from, and we want to remove the second @@ -1078,19 +1081,21 @@ // Find the first occurrence of any URL in the redirect chain. We want to // keep this one since it is rated the highest. - history::HistoryMatches::iterator first(std::find_first_of( - matches->begin(), matches->end(), remove.begin(), remove.end(), - history::HistoryMatch::EqualsGURL)); + history::HistoryMatches::iterator first( + std::find_first_of(matches->begin(), matches->end(), remove.begin(), + remove.end(), history::HistoryMatch::EqualsGURL)); DCHECK(first != matches->end()) << "We should have always found at least the " - "original URL."; + "original URL."; // Find any following occurrences of any URL in the redirect chain, these // should be deleted. - for (history::HistoryMatches::iterator next(std::find_first_of(first + 1, - matches->end(), remove.begin(), remove.end(), - history::HistoryMatch::EqualsGURL)); - next != matches->end(); next = std::find_first_of(next, matches->end(), - remove.begin(), remove.end(), history::HistoryMatch::EqualsGURL)) { + for (history::HistoryMatches::iterator next( + std::find_first_of(first + 1, matches->end(), remove.begin(), + remove.end(), history::HistoryMatch::EqualsGURL)); + next != matches->end(); + next = std::find_first_of(next, matches->end(), remove.begin(), + remove.end(), + history::HistoryMatch::EqualsGURL)) { // Remove this item. When we remove an item before the source index, we // need to shift it to the right and remember that so we can return it. next = matches->erase(next); @@ -1148,7 +1153,7 @@ match.description_class = ClassifyDescription(params.input.text(), match.description); - // |inline_autocomplete_offset| was guaranteed not to be npos before the call + // `inline_autocomplete_offset` was guaranteed not to be npos before the call // to FormatUrl(). If it is npos now, that means the represented location no // longer exists as such in the formatted string, e.g. if the offset pointed // into the middle of a punycode sequence fixed up to Unicode. In this case,
diff --git a/components/omnibox/browser/history_url_provider.h b/components/omnibox/browser/history_url_provider.h index b3e480be..e07e3cfd 100644 --- a/components/omnibox/browser/history_url_provider.h +++ b/components/omnibox/browser/history_url_provider.h
@@ -33,7 +33,7 @@ namespace history { class HistoryBackend; class URLDatabase; -} +} // namespace history // How history autocomplete works // ============================== @@ -95,7 +95,7 @@ // Used to communicate autocomplete parameters between threads via the history // service. struct HistoryURLProviderParams { - // See comments on |promote_type| below. + // See comments on `promote_type` below. enum PromoteType { WHAT_YOU_TYPED_MATCH, FRONT_HISTORY_MATCH, @@ -122,9 +122,9 @@ // A copy of the autocomplete input. We need the copy since this object will // live beyond the original query while it runs on the history thread. AutocompleteInput input; - // |input_before_fixup| is needed for invoking - // |AutocompleteMatch::SetAllowedToBeDefault| which considers - // trailing input whitespaces which the fixed up |input| will have trimmed. + // `input_before_fixup` is needed for invoking + // `AutocompleteMatch::SetAllowedToBeDefault` which considers + // trailing input whitespaces which the fixed up `input` will have trimmed. AutocompleteInput input_before_fixup; // Set when "http://" should be trimmed from the beginning of the URLs. @@ -142,16 +142,16 @@ // Set by ExecuteWithDB() on the history thread when the query could not be // performed because the history system failed to properly init the database. // If this is set when the main thread is called back, it avoids changing - // |matches_| at all, so it won't delete the default match Start() creates. + // `matches_` at all, so it won't delete the default match Start() creates. bool failed; // List of matches written by DoAutocomplete(). Upon its return the provider - // converts this list to ACMatches and places them in |matches_|. + // converts this list to ACMatches and places them in `matches_`. history::HistoryMatches matches; // True if the suggestion for exactly what the user typed appears as a known // URL in the user's history. In this case, this will also be the first match - // in |matches|. + // in `matches`. // // NOTE: There are some complications related to keeping things consistent // between passes and how we deal with intranet URLs, which are too complex to @@ -159,19 +159,19 @@ // FixupExactSuggestion() for specific comments. bool exact_suggestion_is_in_history; - // Tells the provider whether to promote the what you typed match, the first - // element of |matches|, or neither as the first AutocompleteMatch. If - // |exact_suggestion_is_in_history| is true (and thus "the what you typed - // match" and "the first element of |matches|" represent the same thing), this + // Tells the provider whether to promote the what-you-typed match, the first + // element of `matches`, or neither as the first AutocompleteMatch. If + // `exact_suggestion_is_in_history` is true (and thus the what-you-typed + // match and the first element of `matches` represent the same thing), this // will be set to WHAT_YOU_TYPED_MATCH. // // NOTE: The second pass of DoAutocomplete() checks what the first pass set // this to. See comments in DoAutocomplete(). PromoteType promote_type; - // True if |what_you_typed_match| is eligible for display. If this is true, - // PromoteMatchesIfNecessary() may choose to place |what_you_typed_match| on - // |matches_| even when |promote_type| is not WHAT_YOU_TYPED_MATCH. + // True if `what_you_typed_match` is eligible for display. If this is true, + // PromoteMatchesIfNecessary() may choose to place `what_you_typed_match` on + // `matches_` even when `promote_type` is not WHAT_YOU_TYPED_MATCH. bool have_what_you_typed_match; // The default search provider and search terms data necessary to cull results @@ -243,15 +243,15 @@ ~HistoryURLProvider() override; - // Determines the relevance for a match, given its type. If |match_type| is - // NORMAL, |match_number| is a number indicating the relevance of the match - // (higher == more relevant). For other values of |match_type|, - // |match_number| is ignored. Only called some of the time; for some matches, + // Determines the relevance for a match, given its type. If `match_type` is + // NORMAL, `match_number` is a number indicating the relevance of the match + // (higher == more relevant). For other values of `match_type`, + // `match_number` is ignored. Only called sometimes; for some matches, // relevancy scores are assigned consecutively decreasing (1416, 1415, ...). static int CalculateRelevance(MatchType match_type, int match_number); // Returns a set of classifications that highlight all the occurrences of - // |input_text| at word breaks in |description|. + // `input_text` at word breaks in `description`. static ACMatchClassifications ClassifyDescription( const std::u16string& input_text, const std::u16string& description); @@ -263,8 +263,8 @@ history::URLDatabase* db, HistoryURLProviderParams* params); - // May promote the what you typed match, the first history match in - // params->matches, or both to the front of |matches_|, depending on the + // May promote the what-you-typed match, the first history match in + // params->matches, or both to the front of `matches_`, depending on the // values of params->promote_type, params->have_what_you_typed_match, and // params->prevent_inline_autocomplete. void PromoteMatchesIfNecessary(const HistoryURLProviderParams& params); @@ -286,7 +286,7 @@ // Helper function for FixupExactSuggestion. If a URL with the same host name // has been visited by the user in the past, the function returns a valid URL. // The return value is built from the canonicalized version of the - // autocomplete input in |params|. The scheme and host format (e.g. prefixed + // autocomplete input in `params`. The scheme and host format (e.g. prefixed // with "www.") of the return value is the same as one of the corresponding // entries in the database. GURL AsKnownIntranetURL(history::URLDatabase* db, @@ -300,9 +300,8 @@ // once, we'll suggest http://example.com/ even if they've never been to it. // Returns true if a match was successfully created/promoted that we're // willing to inline autocomplete. - bool PromoteOrCreateShorterSuggestion( - history::URLDatabase* db, - HistoryURLProviderParams* params); + bool PromoteOrCreateShorterSuggestion(history::URLDatabase* db, + HistoryURLProviderParams* params); // Removes results that have been rarely typed or visited, and not any time // recently. The exact parameters for this heuristic can be found in the @@ -312,27 +311,27 @@ // anyway. void CullPoorMatches(HistoryURLProviderParams* params) const; - // Removes results that redirect to each other, leaving at most |max_results| + // Removes results that redirect to each other, leaving at most `max_results` // results. void CullRedirects(history::HistoryBackend* backend, history::HistoryMatches* matches, size_t max_results) const; // Helper function for CullRedirects, this removes all but the first - // occurance of [any of the set of strings in |remove|] from the |matches| + // occurrence of [any of the set of strings in `remove`] from the `matches` // list. // // The return value is the index of the item that is after the item in the - // input identified by |source_index|. If |source_index| or an item before + // input identified by `source_index`. If `source_index` or an item before // is removed, the next item will be shifted, and this allows the caller to // pick up on the next one when this happens. size_t RemoveSubsequentMatchesOf(history::HistoryMatches* matches, size_t source_index, const std::vector<GURL>& remove) const; - // Converts a specified |match_number| from params.matches into an + // Converts a specified `match_number` from params.matches into an // autocomplete match for display. If experimental scoring is enabled, the - // final relevance score might be different from the given |relevance|. + // final relevance score might be different from the given `relevance`. // NOTE: This function should only be called on the UI thread. AutocompleteMatch HistoryMatchToACMatch( const HistoryURLProviderParams& params,
diff --git a/components/omnibox/browser/local_history_zero_suggest_provider.cc b/components/omnibox/browser/local_history_zero_suggest_provider.cc index 165a4a92..a16db69 100644 --- a/components/omnibox/browser/local_history_zero_suggest_provider.cc +++ b/components/omnibox/browser/local_history_zero_suggest_provider.cc
@@ -235,6 +235,13 @@ /*relevance_from_server=*/false, /*input_text=*/base::ASCIIToUTF16(std::string())); + // Only provide a group ID, as the client does not know the header or the + // priority for SuggestionGroupId::kPersonalizedZeroSuggest. The suggestion + // group info will either be provided by the server (i.e., on SRP/Web) or + // this group ID will be dropped (i.e., on NTP). + suggestion.set_suggestion_group_id( + SuggestionGroupId::kPersonalizedZeroSuggest); + AutocompleteMatch match = BaseSearchProvider::CreateSearchSuggestion( this, input, /*in_keyword_mode=*/false, suggestion, template_url_service->GetDefaultSearchProvider(),
diff --git a/components/omnibox/browser/omnibox_edit_model.cc b/components/omnibox/browser/omnibox_edit_model.cc index 03c930d3..a7a3da6 100644 --- a/components/omnibox/browser/omnibox_edit_model.cc +++ b/components/omnibox/browser/omnibox_edit_model.cc
@@ -802,6 +802,18 @@ const std::u16string& pasted_text, size_t index, base::TimeTicks match_selection_timestamp) { + // Switch the window disposition to SWITCH_TO_TAB for open tab matches that + // originated while in keyword mode. This is to support the keyword mode + // starter pack's tab search (@tabs) feature, which should open all + // suggestions in the existing open tab. + bool is_open_tab_match = + OmniboxFieldTrial::IsSiteSearchStarterPackEnabled() && + match.from_keyword && + match.provider->type() == AutocompleteProvider::TYPE_OPEN_TAB; + if (is_open_tab_match) { + disposition = WindowOpenDisposition::SWITCH_TO_TAB; + } + TRACE_EVENT("omnibox", "OmniboxEditModel::OpenMatch", "match", match, "disposition", disposition, "altenate_nav_url", alternate_nav_url, "pasted_text", pasted_text); @@ -1961,7 +1973,8 @@ ? omnibox::SuggestionGroupVisibility::SHOWN : omnibox::SuggestionGroupVisibility::HIDDEN; omnibox::SetSuggestionGroupVisibility( - GetPrefService(), match.suggestion_group_id.value(), new_value); + GetPrefService(), static_cast<int>(match.suggestion_group_id.value()), + new_value); break; } case OmniboxPopupSelection::FOCUSED_BUTTON_TAB_SWITCH:
diff --git a/components/omnibox/browser/omnibox_edit_model_unittest.cc b/components/omnibox/browser/omnibox_edit_model_unittest.cc index f2c99688..0aaee64 100644 --- a/components/omnibox/browser/omnibox_edit_model_unittest.cc +++ b/components/omnibox/browser/omnibox_edit_model_unittest.cc
@@ -34,6 +34,7 @@ #include "components/url_formatter/url_fixer.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/metrics_proto/omnibox_event.pb.h" +#include "ui/base/window_open_disposition.h" #include "ui/gfx/geometry/rect.h" using metrics::OmniboxEventProto; @@ -57,6 +58,12 @@ class OmniboxEditModelTest : public testing::Test { public: void SetUp() override { + // The #omnibox-site-search-starter-pack feature flag has to be enabled + // before set up in order for the OpenTabProvider to be initialized (needed + // for OpenTabMatch test). + base::test::ScopedFeatureList feature_list; + feature_list.InitAndEnableFeature(omnibox::kSiteSearchStarterPack); + controller_ = std::make_unique<TestOmniboxEditController>(); view_ = std::make_unique<TestOmniboxView>(controller_.get()); view_->SetModel(std::make_unique<TestOmniboxEditModel>( @@ -734,14 +741,15 @@ matches[3].has_tab_match = true; matches[3].deletable = true; // Make match index 4 have a suggestion_group_id to test header behavior. - matches[4].suggestion_group_id = 7; + const auto kNewGroupId = SuggestionGroupId::kNonPersonalizedZeroSuggest1; + matches[4].suggestion_group_id = kNewGroupId; auto* result = &model()->autocomplete_controller()->result_; AutocompleteInput input(u"match", metrics::OmniboxEventProto::NTP, TestSchemeClassifier()); result->AppendMatches(matches); SuggestionGroupsMap suggestion_groups_map; - suggestion_groups_map[7].header = u"header"; + suggestion_groups_map[kNewGroupId].header = u"header"; result->MergeSuggestionGroupsMap(suggestion_groups_map); result->SortAndCull(input, nullptr); model()->OnPopupResultChanged(); @@ -815,17 +823,19 @@ } // Hide the second two matches. - matches[2].suggestion_group_id = 7; - matches[3].suggestion_group_id = 7; + const auto kNewGroupId = SuggestionGroupId::kNonPersonalizedZeroSuggest1; + matches[2].suggestion_group_id = kNewGroupId; + matches[3].suggestion_group_id = kNewGroupId; omnibox::SetSuggestionGroupVisibility( - pref_service(), 7, omnibox::SuggestionGroupVisibility::HIDDEN); + pref_service(), static_cast<int>(kNewGroupId), + omnibox::SuggestionGroupVisibility::HIDDEN); auto* result = &model()->autocomplete_controller()->result_; AutocompleteInput input(u"match", metrics::OmniboxEventProto::NTP, TestSchemeClassifier()); result->AppendMatches(matches); SuggestionGroupsMap suggestion_groups_map; - suggestion_groups_map[7].header = u"header"; + suggestion_groups_map[kNewGroupId].header = u"header"; result->MergeSuggestionGroupsMap(suggestion_groups_map); result->SortAndCull(input, nullptr); model()->OnPopupResultChanged(); @@ -886,14 +896,14 @@ matches[0].inline_autocompletion = u"1"; matches[1].fill_into_edit = u"a2"; matches[2].fill_into_edit = u"a3"; - matches[2].suggestion_group_id = 7; - + const auto kNewGroupId = SuggestionGroupId::kNonPersonalizedZeroSuggest1; + matches[2].suggestion_group_id = kNewGroupId; auto* result = &model()->autocomplete_controller()->result_; AutocompleteInput input(u"a", metrics::OmniboxEventProto::NTP, TestSchemeClassifier()); result->AppendMatches(matches); SuggestionGroupsMap suggestion_groups_map; - suggestion_groups_map[7].header = u"header"; + suggestion_groups_map[kNewGroupId].header = u"header"; result->MergeSuggestionGroupsMap(suggestion_groups_map); result->SortAndCull(input, nullptr); model()->OnPopupResultChanged(); @@ -1174,3 +1184,38 @@ } } } + +#if !(BUILDFLAG(IS_IOS) || BUILDFLAG(IS_ANDROID)) +// The keyword mode feature is only available on Desktop. Do not test on mobile. +TEST_F(OmniboxEditModelTest, OpenTabMatch) { + base::test::ScopedFeatureList feature_list; + feature_list.InitAndEnableFeature(omnibox::kSiteSearchStarterPack); + + // When the match comes from the Open Tab Provider while in keyword mode, + // the disposition should be set to SWITCH_TO_TAB. + AutocompleteMatch match( + model()->autocomplete_controller()->open_tab_provider(), 0, false, + AutocompleteMatchType::OPEN_TAB); + match.destination_url = GURL("https://foo/"); + match.from_keyword = true; + + model()->OnSetFocus(false); // Avoids DCHECK in OpenMatch(). + model()->SetUserText(u"http://abcd"); + model()->OpenMatch(match, WindowOpenDisposition::CURRENT_TAB, GURL(), + std::u16string(), 0); + EXPECT_EQ(controller_->disposition(), WindowOpenDisposition::SWITCH_TO_TAB); + + // Suggestions not from the Open Tab Provider or not from keyword mode should + // not change the disposition. + match.from_keyword = false; + model()->OpenMatch(match, WindowOpenDisposition::CURRENT_TAB, GURL(), + std::u16string(), 0); + EXPECT_EQ(controller_->disposition(), WindowOpenDisposition::CURRENT_TAB); + + match.provider = model()->autocomplete_controller()->search_provider(); + match.from_keyword = true; + model()->OpenMatch(match, WindowOpenDisposition::CURRENT_TAB, GURL(), + std::u16string(), 0); + EXPECT_EQ(controller_->disposition(), WindowOpenDisposition::CURRENT_TAB); +} +#endif // !(BUILDFLAG(IS_IOS) || BUILDFLAG(IS_ANDROID))
diff --git a/components/omnibox/browser/omnibox_field_trial.cc b/components/omnibox/browser/omnibox_field_trial.cc index 7ae0dc89..94b1eb57 100644 --- a/components/omnibox/browser/omnibox_field_trial.cc +++ b/components/omnibox/browser/omnibox_field_trial.cc
@@ -739,6 +739,10 @@ &omnibox::kAutocompleteStability, "AutocompleteStabilityDontCopyDoneProviders", false); +const base::FeatureParam<bool> kAutocompleteStabilityAsyncProvidersFirst( + &omnibox::kAutocompleteStability, + "AutocompleteStabilityAsyncProvidersFirst", + false); // Local history zero-prefix (aka zero-suggest) and prefix suggestions:
diff --git a/components/omnibox/browser/omnibox_field_trial.h b/components/omnibox/browser/omnibox_field_trial.h index 3e76b32..7b15a6a 100644 --- a/components/omnibox/browser/omnibox_field_trial.h +++ b/components/omnibox/browser/omnibox_field_trial.h
@@ -490,6 +490,10 @@ // providers whose suggestions are pending have their old matches copied over. extern const base::FeatureParam<bool> kAutocompleteStabilityDontCopyDoneProviders; +// Begin async providers before sync providers so their async requests can +// happen in parallel. This effects only the search, history_url, document, and +// on device head providers. +extern const base::FeatureParam<bool> kAutocompleteStabilityAsyncProvidersFirst; // Local history zero-prefix (aka zero-suggest) and prefix suggestions.
diff --git a/components/omnibox/browser/omnibox_popup_selection.cc b/components/omnibox/browser/omnibox_popup_selection.cc index 59c630d..9f4d979 100644 --- a/components/omnibox/browser/omnibox_popup_selection.cc +++ b/components/omnibox/browser/omnibox_popup_selection.cc
@@ -67,6 +67,12 @@ case KEYWORD_MODE: return match.associated_keyword != nullptr; case FOCUSED_BUTTON_TAB_SWITCH: + // The default action for suggestions from the open tab provider in + // keyword mode is to switch to the open tab so no button is necessary. + if (match.from_keyword && + match.provider->type() == AutocompleteProvider::TYPE_OPEN_TAB) { + return false; + } return match.has_tab_match.value_or(false); case FOCUSED_BUTTON_ACTION: return match.action != nullptr;
diff --git a/components/omnibox/browser/search_suggestion_parser.cc b/components/omnibox/browser/search_suggestion_parser.cc index fa022b19..5c53f316 100644 --- a/components/omnibox/browser/search_suggestion_parser.cc +++ b/components/omnibox/browser/search_suggestion_parser.cc
@@ -10,6 +10,8 @@ #include <memory> #include "base/check.h" +#include "base/containers/contains.h" +#include "base/containers/fixed_flat_map.h" #include "base/i18n/icu_string_conversions.h" #include "base/json/json_reader.h" #include "base/json/json_string_value_serializer.h" @@ -113,6 +115,38 @@ // The field number for the string value in ExperimentStatsV2. constexpr char kStringValueFieldNumber[] = "2"; +// Used to dynamically convert the server-provided group IDs to those known to +// Chrome based on the 0-based index of the suggestion groups in the server +// response. +constexpr auto kReservedGroupIdsMap = + base::MakeFixedFlatMap<int, SuggestionGroupId>( + {{0, SuggestionGroupId::kNonPersonalizedZeroSuggest1}, + {1, SuggestionGroupId::kNonPersonalizedZeroSuggest2}, + {2, SuggestionGroupId::kNonPersonalizedZeroSuggest3}, + {3, SuggestionGroupId::kNonPersonalizedZeroSuggest4}, + {4, SuggestionGroupId::kNonPersonalizedZeroSuggest5}, + {5, SuggestionGroupId::kNonPersonalizedZeroSuggest6}, + {6, SuggestionGroupId::kNonPersonalizedZeroSuggest7}, + {7, SuggestionGroupId::kNonPersonalizedZeroSuggest8}, + {8, SuggestionGroupId::kNonPersonalizedZeroSuggest9}, + {9, SuggestionGroupId::kNonPersonalizedZeroSuggest10}}); + +// Used to dynamically convert the order of suggestion groups in the server +// response to the group priorities known to Chrome based on the 0-based index +// of the suggestion groups in the server response. +constexpr auto kReservedGroupPrioritiesMap = + base::MakeFixedFlatMap<int, SuggestionGroupPriority>( + {{0, SuggestionGroupPriority::kRemoteZeroSuggest1}, + {1, SuggestionGroupPriority::kRemoteZeroSuggest2}, + {2, SuggestionGroupPriority::kRemoteZeroSuggest3}, + {3, SuggestionGroupPriority::kRemoteZeroSuggest4}, + {4, SuggestionGroupPriority::kRemoteZeroSuggest5}, + {5, SuggestionGroupPriority::kRemoteZeroSuggest6}, + {6, SuggestionGroupPriority::kRemoteZeroSuggest7}, + {7, SuggestionGroupPriority::kRemoteZeroSuggest8}, + {8, SuggestionGroupPriority::kRemoteZeroSuggest9}, + {9, SuggestionGroupPriority::kRemoteZeroSuggest10}}); + } // namespace // SearchSuggestionParser::Result ---------------------------------------------- @@ -493,6 +527,7 @@ const base::Value* subtype_identifiers = nullptr; int prefetch_index = -1; int prerender_index = -1; + std::unordered_map<int, SuggestionGroup> parsed_suggestion_groups_map; if (root_list.size() > 4u && root_list[4].is_dict()) { const base::Value& extras = root_list[4]; @@ -554,7 +589,7 @@ for (auto it : headers->DictItems()) { int suggestion_group_id; base::StringToInt(it.first, &suggestion_group_id); - results->suggestion_groups_map[suggestion_group_id].header = + parsed_suggestion_groups_map[suggestion_group_id].header = base::UTF8ToUTF16(it.second.GetString()); } } @@ -562,8 +597,9 @@ const base::Value* hidden_group_ids = header_texts->FindListKey("h"); if (hidden_group_ids) { for (const auto& value : hidden_group_ids->GetListDeprecated()) { - if (value.is_int()) - results->suggestion_groups_map[value.GetInt()].hidden = true; + if (value.is_int()) { + parsed_suggestion_groups_map[value.GetInt()].hidden = true; + } } } } @@ -610,6 +646,9 @@ int relevance = default_result_relevance; const std::u16string& trimmed_input = base::CollapseWhitespace(input.text(), false); + int last_suggestion_group_id = static_cast<int>(SuggestionGroupId::kInvalid); + int last_suggestion_group_index = -1; + for (size_t index = 0; index < results_list.size() && results_list[index].is_string(); ++index) { @@ -752,11 +791,51 @@ trimmed_input)); if (suggestion_group_id) { + if (last_suggestion_group_id != *suggestion_group_id) { + // Remember the ID and the 0-based index of the last seen group. + last_suggestion_group_id = *suggestion_group_id; + last_suggestion_group_index++; + } + + // Map the group ID to one known to Chrome. With an exception of the + // personalized suggestions whose group ID is known to Chrome, the + // mapping is dynamically done based on the 0-based index of the group. + SuggestionGroupId mapped_suggestion_group_id = + SuggestionGroupId::kInvalid; + if (*suggestion_group_id == + static_cast<int>(SuggestionGroupId::kPersonalizedZeroSuggest)) { + mapped_suggestion_group_id = + SuggestionGroupId::kPersonalizedZeroSuggest; + } else if (base::Contains(kReservedGroupIdsMap, + last_suggestion_group_index)) { + mapped_suggestion_group_id = + kReservedGroupIdsMap.at(last_suggestion_group_index); + } else { + continue; + } + + // Use the mapped group ID in the result. results->suggest_results.back().set_suggestion_group_id( - *suggestion_group_id); + mapped_suggestion_group_id); + + // Use the mapped group ID to update the suggestion group info, if any. + // It is possible for a suggestion to specify a group ID for which there + // are no header or default visibility information available. Such group + // IDs are generally stripped away later on. + if (base::Contains(parsed_suggestion_groups_map, + *suggestion_group_id)) { + results->suggestion_groups_map[mapped_suggestion_group_id].MergeFrom( + parsed_suggestion_groups_map[*suggestion_group_id]); + results->suggestion_groups_map[mapped_suggestion_group_id] + .original_group_id = *suggestion_group_id; + results->suggestion_groups_map[mapped_suggestion_group_id].priority = + kReservedGroupPrioritiesMap.at(last_suggestion_group_index); + } } - if (answer_parsed_successfully) + + if (answer_parsed_successfully) { results->suggest_results.back().SetAnswer(answer); + } } } results->relevances_from_server = relevances != nullptr;
diff --git a/components/omnibox/browser/search_suggestion_parser.h b/components/omnibox/browser/search_suggestion_parser.h index 3beb8b2..78c377f 100644 --- a/components/omnibox/browser/search_suggestion_parser.h +++ b/components/omnibox/browser/search_suggestion_parser.h
@@ -163,10 +163,10 @@ return additional_query_params_; } - void set_suggestion_group_id(int suggestion_group_id) { + void set_suggestion_group_id(SuggestionGroupId suggestion_group_id) { suggestion_group_id_ = suggestion_group_id; } - absl::optional<int> suggestion_group_id() const { + absl::optional<SuggestionGroupId> suggestion_group_id() const { return suggestion_group_id_; } @@ -214,9 +214,9 @@ // suggestion_config.proto. Used to look up the suggestion group info this // suggestion belong to such as the header text this suggestion must appear // under. - // Note: Use kInvalidSuggestionGroupId in place of a missing suggestion + // Note: Use SuggestionGroupId::kInvalid in place of a missing suggestion // group Id when this is to be converted to a primitive type. - absl::optional<int> suggestion_group_id_; + absl::optional<SuggestionGroupId> suggestion_group_id_; // Optional short answer to the input that produced this suggestion. absl::optional<SuggestionAnswer> answer_;
diff --git a/components/omnibox/browser/search_suggestion_parser_unittest.cc b/components/omnibox/browser/search_suggestion_parser_unittest.cc index a9a6923..cd1fd09 100644 --- a/components/omnibox/browser/search_suggestion_parser_unittest.cc +++ b/components/omnibox/browser/search_suggestion_parser_unittest.cc
@@ -323,11 +323,16 @@ EXPECT_EQ(kNone, result.match_contents_class()); } -TEST(SearchSuggestionParserTest, ParseHeaderInfo) { - std::string json_data = R"([ +TEST(SearchSuggestionParserTest, ParseSuggestionGroupInfo) { + TestSchemeClassifier scheme_classifier; + AutocompleteInput input(u"", metrics::OmniboxEventProto::NTP_REALBOX, + scheme_classifier); + + { + std::string json_data = R"([ "", ["los angeles", "san diego", "las vegas", "san francisco"], - ["history", "", "", ""], + ["", "history", "", ""], [], { "google:clientdata": { @@ -336,8 +341,214 @@ }, "google:headertexts":{ "a":{ - "40007":"Not recommended for you", - "40008":"Recommended for you" + "40000":"Recent Searches", + "40008":"Recommended for you", + "garbage_non_int":"NOT RECOMMENDED FOR YOU" + }, + "h":[40000, "40008", "garbage_non_int"] + }, + "google:suggestdetail":[ + { + }, + { + "zl":40000 + }, + { + "zl":40008 + }, + { + "zl":40009 + } + ], + "google:suggestrelevance": [607, 606, 605, 604], + "google:suggesttype": ["QUERY", "PERSONALIZED_QUERY", "QUERY", "QUERY"] + }])"; + absl::optional<base::Value> root_val = base::JSONReader::Read(json_data); + ASSERT_TRUE(root_val); + + SearchSuggestionParser::Results results; + ASSERT_TRUE(SearchSuggestionParser::ParseSuggestResults( + *root_val, input, scheme_classifier, /*default_result_relevance=*/400, + /*is_keyword_result=*/false, &results)); + + // Suggestion group headers, original group ids, priorities, and default + // visibilities are correctly parsed and populated. + ASSERT_EQ(2U, results.suggestion_groups_map.size()); + + ASSERT_EQ( + u"Recent Searches", + results + .suggestion_groups_map[SuggestionGroupId::kPersonalizedZeroSuggest] + .header); + ASSERT_EQ( + 40000, + results + .suggestion_groups_map[SuggestionGroupId::kPersonalizedZeroSuggest] + .original_group_id.value()); + ASSERT_EQ( + SuggestionGroupPriority::kRemoteZeroSuggest1, + results + .suggestion_groups_map[SuggestionGroupId::kPersonalizedZeroSuggest] + .priority); + ASSERT_TRUE( + results + .suggestion_groups_map[SuggestionGroupId::kPersonalizedZeroSuggest] + .hidden); + + ASSERT_EQ(u"Recommended for you", + results + .suggestion_groups_map + [SuggestionGroupId::kNonPersonalizedZeroSuggest2] + .header); + ASSERT_EQ(40008, results + .suggestion_groups_map + [SuggestionGroupId::kNonPersonalizedZeroSuggest2] + .original_group_id.value()); + ASSERT_FALSE(results + .suggestion_groups_map + [SuggestionGroupId::kNonPersonalizedZeroSuggest2] + .hidden); + ASSERT_EQ(SuggestionGroupPriority::kRemoteZeroSuggest2, + results + .suggestion_groups_map + [SuggestionGroupId::kNonPersonalizedZeroSuggest2] + .priority); + + ASSERT_EQ(u"los angeles", results.suggest_results[0].suggestion()); + // This suggestion does not belong to a group. + ASSERT_EQ(absl::nullopt, results.suggest_results[0].suggestion_group_id()); + + ASSERT_EQ(u"san diego", results.suggest_results[1].suggestion()); + ASSERT_EQ(SuggestionGroupId::kPersonalizedZeroSuggest, + *results.suggest_results[1].suggestion_group_id()); + + ASSERT_EQ(u"las vegas", results.suggest_results[2].suggestion()); + ASSERT_EQ(SuggestionGroupId::kNonPersonalizedZeroSuggest2, + *results.suggest_results[2].suggestion_group_id()); + + ASSERT_EQ(u"san francisco", results.suggest_results[3].suggestion()); + ASSERT_EQ(SuggestionGroupId::kNonPersonalizedZeroSuggest3, + results.suggest_results[3].suggestion_group_id()); + } + { + std::string json_data = R"([ + "", + ["los angeles", "san diego", "las vegas", "san francisco"], + ["", "", "history", ""], + [], + { + "google:clientdata": { + "bpc": false, + "tlw": false + }, + "google:headertexts":{ + "a":{ + "40000":"Recent Searches", + "40008":"Recommended for you", + "garbage_non_int":"NOT RECOMMENDED FOR YOU" + }, + "h":[40000, "40008", "garbage_non_int"] + }, + "google:suggestdetail":[ + { + "zl":40008 + }, + { + "zl":40008 + }, + { + "zl":40000 + }, + { + "zl":40009 + } + ], + "google:suggestrelevance": [607, 606, 605, 604], + "google:suggesttype": ["QUERY", "QUERY", "PERSONALIZED_QUERY", "QUERY"] + }])"; + absl::optional<base::Value> root_val = base::JSONReader::Read(json_data); + ASSERT_TRUE(root_val); + + SearchSuggestionParser::Results results; + ASSERT_TRUE(SearchSuggestionParser::ParseSuggestResults( + *root_val, input, scheme_classifier, /*default_result_relevance=*/400, + /*is_keyword_result=*/false, &results)); + + // Suggestion group headers, original group ids, priorities, and default + // visibilities are correctly parsed and populated. + ASSERT_EQ(2U, results.suggestion_groups_map.size()); + + ASSERT_EQ(u"Recommended for you", + results + .suggestion_groups_map + [SuggestionGroupId::kNonPersonalizedZeroSuggest1] + .header); + ASSERT_EQ(40008, results + .suggestion_groups_map + [SuggestionGroupId::kNonPersonalizedZeroSuggest1] + .original_group_id.value()); + ASSERT_FALSE(results + .suggestion_groups_map + [SuggestionGroupId::kNonPersonalizedZeroSuggest1] + .hidden); + ASSERT_EQ(SuggestionGroupPriority::kRemoteZeroSuggest1, + results + .suggestion_groups_map + [SuggestionGroupId::kNonPersonalizedZeroSuggest1] + .priority); + + ASSERT_EQ( + u"Recent Searches", + results + .suggestion_groups_map[SuggestionGroupId::kPersonalizedZeroSuggest] + .header); + ASSERT_EQ( + 40000, + results + .suggestion_groups_map[SuggestionGroupId::kPersonalizedZeroSuggest] + .original_group_id.value()); + ASSERT_EQ( + SuggestionGroupPriority::kRemoteZeroSuggest2, + results + .suggestion_groups_map[SuggestionGroupId::kPersonalizedZeroSuggest] + .priority); + ASSERT_TRUE( + results + .suggestion_groups_map[SuggestionGroupId::kPersonalizedZeroSuggest] + .hidden); + + ASSERT_EQ(u"los angeles", results.suggest_results[0].suggestion()); + ASSERT_EQ(SuggestionGroupId::kNonPersonalizedZeroSuggest1, + *results.suggest_results[0].suggestion_group_id()); + + ASSERT_EQ(u"san diego", results.suggest_results[1].suggestion()); + ASSERT_EQ(SuggestionGroupId::kNonPersonalizedZeroSuggest1, + *results.suggest_results[1].suggestion_group_id()); + + ASSERT_EQ(u"las vegas", results.suggest_results[2].suggestion()); + ASSERT_EQ(SuggestionGroupId::kPersonalizedZeroSuggest, + *results.suggest_results[2].suggestion_group_id()); + + ASSERT_EQ(u"san francisco", results.suggest_results[3].suggestion()); + ASSERT_EQ(SuggestionGroupId::kNonPersonalizedZeroSuggest3, + results.suggest_results[3].suggestion_group_id()); + } + { + std::string json_data = R"([ + "", + ["los angeles", "san diego", "las vegas", "san francisco"], + ["", "", "", "history"], + [], + { + "google:clientdata": { + "bpc": false, + "tlw": false + }, + "google:headertexts":{ + "a":{ + "40007":"Related Searches", + "40008":"Recommended for you", + "garbage_non_int":"NOT RECOMMENDED FOR YOU" }, "h":[40007, "40008", "garbage_non_int"] }, @@ -351,45 +562,77 @@ "zl":40008 }, { - "zl":40009 + "zl":40000 } ], "google:suggestrelevance": [607, 606, 605, 604], - "google:suggesttype": ["PERSONALIZED_QUERY", "QUERY", "QUERY", "QUERY"] + "google:suggesttype": ["QUERY", "QUERY", "QUERY", "PERSONALIZED_QUERY"] }])"; - absl::optional<base::Value> root_val = base::JSONReader::Read(json_data); - ASSERT_TRUE(root_val); - TestSchemeClassifier scheme_classifier; - AutocompleteInput input(u"", metrics::OmniboxEventProto::NTP_REALBOX, - scheme_classifier); - SearchSuggestionParser::Results results; - ASSERT_TRUE(SearchSuggestionParser::ParseSuggestResults( - *root_val, input, scheme_classifier, /*default_result_relevance=*/400, - /*is_keyword_result=*/false, &results)); + absl::optional<base::Value> root_val = base::JSONReader::Read(json_data); + ASSERT_TRUE(root_val); - // Parse integers, and only integers, out of the "h" metadata list. - ASSERT_TRUE(results.suggestion_groups_map[40007].hidden); + SearchSuggestionParser::Results results; + ASSERT_TRUE(SearchSuggestionParser::ParseSuggestResults( + *root_val, input, scheme_classifier, /*default_result_relevance=*/400, + /*is_keyword_result=*/false, &results)); - { - const auto& suggestion_result = results.suggest_results[0]; - ASSERT_EQ(u"los angeles", suggestion_result.suggestion()); + // Suggestion group headers, original group ids, priorities, and default + // visibilities are correctly parsed and populated. + ASSERT_EQ(2U, results.suggestion_groups_map.size()); + + ASSERT_EQ(u"Related Searches", + results + .suggestion_groups_map + [SuggestionGroupId::kNonPersonalizedZeroSuggest1] + .header); + ASSERT_EQ(40007, results + .suggestion_groups_map + [SuggestionGroupId::kNonPersonalizedZeroSuggest1] + .original_group_id.value()); + ASSERT_EQ(SuggestionGroupPriority::kRemoteZeroSuggest1, + results + .suggestion_groups_map + [SuggestionGroupId::kNonPersonalizedZeroSuggest1] + .priority); + ASSERT_TRUE(results + .suggestion_groups_map + [SuggestionGroupId::kNonPersonalizedZeroSuggest1] + .hidden); + + ASSERT_EQ(u"Recommended for you", + results + .suggestion_groups_map + [SuggestionGroupId::kNonPersonalizedZeroSuggest2] + .header); + ASSERT_EQ(40008, results + .suggestion_groups_map + [SuggestionGroupId::kNonPersonalizedZeroSuggest2] + .original_group_id.value()); + ASSERT_FALSE(results + .suggestion_groups_map + [SuggestionGroupId::kNonPersonalizedZeroSuggest2] + .hidden); + ASSERT_EQ(SuggestionGroupPriority::kRemoteZeroSuggest2, + results + .suggestion_groups_map + [SuggestionGroupId::kNonPersonalizedZeroSuggest2] + .priority); + + ASSERT_EQ(u"los angeles", results.suggest_results[0].suggestion()); // This suggestion does not belong to a group. - ASSERT_EQ(absl::nullopt, suggestion_result.suggestion_group_id()); - } - { - const auto& suggestion_result = results.suggest_results[1]; - ASSERT_EQ(u"san diego", suggestion_result.suggestion()); - ASSERT_EQ(40007, *suggestion_result.suggestion_group_id()); - } - { - const auto& suggestion_result = results.suggest_results[2]; - ASSERT_EQ(u"las vegas", suggestion_result.suggestion()); - ASSERT_EQ(40008, *suggestion_result.suggestion_group_id()); - } - { - const auto& suggestion_result = results.suggest_results[3]; - ASSERT_EQ(u"san francisco", suggestion_result.suggestion()); - ASSERT_EQ(40009, *suggestion_result.suggestion_group_id()); + ASSERT_EQ(absl::nullopt, results.suggest_results[0].suggestion_group_id()); + + ASSERT_EQ(u"san diego", results.suggest_results[1].suggestion()); + ASSERT_EQ(SuggestionGroupId::kNonPersonalizedZeroSuggest1, + *results.suggest_results[1].suggestion_group_id()); + + ASSERT_EQ(u"las vegas", results.suggest_results[2].suggestion()); + ASSERT_EQ(SuggestionGroupId::kNonPersonalizedZeroSuggest2, + *results.suggest_results[2].suggestion_group_id()); + + ASSERT_EQ(u"san francisco", results.suggest_results[3].suggestion()); + ASSERT_EQ(SuggestionGroupId::kPersonalizedZeroSuggest, + results.suggest_results[3].suggestion_group_id()); } }
diff --git a/components/omnibox/browser/suggestion_group.cc b/components/omnibox/browser/suggestion_group.cc index 7090588..0f16404 100644 --- a/components/omnibox/browser/suggestion_group.cc +++ b/components/omnibox/browser/suggestion_group.cc
@@ -4,18 +4,27 @@ #include "components/omnibox/browser/suggestion_group.h" -// Value chosen based on SuggestionGroupIds::INVALID in suggestion_config.proto. -const int kInvalidSuggestionGroupId = -1; - void SuggestionGroup::MergeFrom(const SuggestionGroup& suggestion_group) { + // Only update the priority if not previously set. + if (priority == SuggestionGroupPriority::kDefault) { + priority = suggestion_group.priority; + } // Only update the header if not previously set. if (header.empty()) { header = suggestion_group.header; } + // Only update the server group ID if not previously set and given group has + // a value. + if (!original_group_id.has_value() && + suggestion_group.original_group_id.has_value()) { + original_group_id = *suggestion_group.original_group_id; + } hidden = suggestion_group.hidden; } void SuggestionGroup::Clear() { + priority = SuggestionGroupPriority::kDefault; header.clear(); + original_group_id.reset(); hidden = false; }
diff --git a/components/omnibox/browser/suggestion_group.h b/components/omnibox/browser/suggestion_group.h index 36be1b0..2c2954d 100644 --- a/components/omnibox/browser/suggestion_group.h +++ b/components/omnibox/browser/suggestion_group.h
@@ -5,11 +5,73 @@ #ifndef COMPONENTS_OMNIBOX_BROWSER_SUGGESTION_GROUP_H_ #define COMPONENTS_OMNIBOX_BROWSER_SUGGESTION_GROUP_H_ -#include <map> #include <string> +#include <unordered_map> -// Indicates an invalid suggestion group Id. -extern const int kInvalidSuggestionGroupId; +#include "third_party/abseil-cpp/absl/types/optional.h" + +// Determines the order in which suggestion groups appear in the final displayed +// list relative to one another. A higher numeric value places a given group +// towards the bottom of the suggestion list relative to the other groups with +// lower priority numeric values. +// +// Use a fixed underlying int type for this enum to ensure that the 0-based +// integer indices of the remote zero-prefix suggestions can be safely converted +// to this enum type. +enum class SuggestionGroupPriority : int { + // The default suggestion group priority. Any suggestion with this priority is + // placed above the remote zero-prefix suggestions (see below). + kDefault = 0, + // Reserved for remote zero-prefix suggestions. The priorities are dynamically + // assigned to the groups found in the server response based on the 0-based + // index of the first zero-prefix suggestion in the group. + kRemoteZeroSuggest1 = 1, + kRemoteZeroSuggest2 = 2, + kRemoteZeroSuggest3 = 3, + kRemoteZeroSuggest4 = 4, + kRemoteZeroSuggest5 = 5, + kRemoteZeroSuggest6 = 6, + kRemoteZeroSuggest7 = 7, + kRemoteZeroSuggest8 = 8, + kRemoteZeroSuggest9 = 9, + kRemoteZeroSuggest10 = 10, +}; + +// These values uniquely identify the suggestion groups in SuggestionGroupsMap. +// +// Use a fixed underlying int type for this enum to ensure its values can be +// safely converted to the equivalent Java and Mojom types in Android and WebUI. +enum class SuggestionGroupId : int { + // SuggestionGroupIds::INVALID in suggestion_config.proto. + kInvalid = -1, + // Reserved for non-personalized zero-prefix suggestions. These values don't + // match the reserved range for these suggestions in suggestion_config.proto. + // Produced by SearchSuggestionParser. + kNonPersonalizedZeroSuggest1 = 10000, + kNonPersonalizedZeroSuggest2 = 10001, + kNonPersonalizedZeroSuggest3 = 10002, + kNonPersonalizedZeroSuggest4 = 10003, + kNonPersonalizedZeroSuggest5 = 10004, + kNonPersonalizedZeroSuggest6 = 10005, + kNonPersonalizedZeroSuggest7 = 10006, + kNonPersonalizedZeroSuggest8 = 10007, + kNonPersonalizedZeroSuggest9 = 10008, + kNonPersonalizedZeroSuggest10 = 10009, + // SuggestionGroupIds::PERSONALIZED_HISTORY_GROUP in suggestion_config.proto. + // Found in server response. Also Produced by LocalHistoryZeroSuggestProvider. + kPersonalizedZeroSuggest = 40000, + + // Produced by HistoryClusterProvider. + kHistoryCluster = 100000, +}; + +// This allows using SuggestionGroupId as the key in SuggestionGroupsMap. +struct SuggestionGroupIdHash { + template <typename T> + int operator()(T t) const { + return static_cast<int>(t); + } +}; // Contains the information about the suggestion groups. struct SuggestionGroup { @@ -21,12 +83,20 @@ void MergeFrom(const SuggestionGroup& suggestion_group); void Clear(); - // Group header provided by the server. + // Determines how this group is placed in the final list of suggestions with + // relative to the other groups. + // Inferred from the server response for remote zero-prefix suggestions. + SuggestionGroupPriority priority{SuggestionGroupPriority::kDefault}; + // The original group ID provided by the server, if applicable. + absl::optional<int> original_group_id; + // Group header provided by the server, if applicable. std::u16string header{u""}; - // Whether the group should be hidden by default. + // Default visibility provided by the server, if applicable. bool hidden{false}; }; -using SuggestionGroupsMap = std::map<int, SuggestionGroup>; +// A map of SuggestionGroupId to SuggestionGroup. +using SuggestionGroupsMap = std:: + unordered_map<SuggestionGroupId, SuggestionGroup, SuggestionGroupIdHash>; #endif // COMPONENTS_OMNIBOX_BROWSER_SUGGESTION_GROUP_H_
diff --git a/components/omnibox/browser/test_omnibox_edit_controller.cc b/components/omnibox/browser/test_omnibox_edit_controller.cc index 3b6c414..4597e7d 100644 --- a/components/omnibox/browser/test_omnibox_edit_controller.cc +++ b/components/omnibox/browser/test_omnibox_edit_controller.cc
@@ -30,4 +30,5 @@ match, alternative_nav_match); alternate_nav_match_ = alternative_nav_match; + disposition_ = disposition; }
diff --git a/components/omnibox/browser/test_omnibox_edit_controller.h b/components/omnibox/browser/test_omnibox_edit_controller.h index ab5cc30..54b0ee4 100644 --- a/components/omnibox/browser/test_omnibox_edit_controller.h +++ b/components/omnibox/browser/test_omnibox_edit_controller.h
@@ -8,6 +8,7 @@ #include "components/omnibox/browser/autocomplete_match.h" #include "components/omnibox/browser/omnibox_edit_controller.h" #include "components/omnibox/browser/test_location_bar_model.h" +#include "ui/base/window_open_disposition.h" class TestOmniboxEditController : public OmniboxEditController { public: @@ -35,11 +36,14 @@ return alternate_nav_match_; } + const WindowOpenDisposition& disposition() const { return disposition_; } + using OmniboxEditController::destination_url; private: TestLocationBarModel location_bar_model_; AutocompleteMatch alternate_nav_match_; + WindowOpenDisposition disposition_; }; #endif // COMPONENTS_OMNIBOX_BROWSER_TEST_OMNIBOX_EDIT_CONTROLLER_H_
diff --git a/components/omnibox/browser/zero_suggest_provider.cc b/components/omnibox/browser/zero_suggest_provider.cc index 5f8b621..670b69994 100644 --- a/components/omnibox/browser/zero_suggest_provider.cc +++ b/components/omnibox/browser/zero_suggest_provider.cc
@@ -175,6 +175,23 @@ (!check_authentication_state || client->IsAuthenticated()); } +// Returns a sanitized copy of |input|. For zero-suggest, input is expected to +// empty, as it is checked against the suggest response which always has an +// empty query. If those don't match, the response is dropped. Ensures the input +// text is empty. However copies over the URL. Zero-suggest on Web/SRP on Mobile +// relies on the URL to be set. +// TODO(crbug.com/1344004): Find out if the other fields also need to be set. +AutocompleteInput GetSanitizedInput(const AutocompleteInput& input, + const AutocompleteProviderClient* client) { + AutocompleteInput sanitized_input(u"", input.current_page_classification(), + client->GetSchemeClassifier()); + sanitized_input.set_current_url(input.current_url()); + sanitized_input.set_current_title(input.current_title()); + sanitized_input.set_prevent_inline_autocomplete(true); + sanitized_input.set_allow_exact_keyword_match(false); + return sanitized_input; +} + } // namespace // static @@ -274,18 +291,44 @@ std::string()); } +void ZeroSuggestProvider::StartPrefetch(const AutocompleteInput& input) { + TRACE_EVENT0("omnibox", "ZeroSuggestProvider::StartPrefetch"); + + ResultType result_type; + if (!AllowZeroPrefixSuggestions(client(), input, &result_type)) { + return; + } + + // Do not start a request if async requests are disallowed. + if (input.omit_asynchronous_matches()) { + return; + } + + if (prefetch_loader_) { + LogEvent(Event::kRequestInvalidated, result_type, /*is_prefetch=*/true); + } + + // Create a loader for the request and take ownership of it. + TemplateURLRef::SearchTermsArgs search_terms_args; + search_terms_args.page_classification = input.current_page_classification(); + search_terms_args.focus_type = input.focus_type(); + search_terms_args.current_page_url = result_type == REMOTE_SEND_URL + ? input.current_url().spec() + : std::string(); + prefetch_loader_ = + client() + ->GetRemoteSuggestionsService(/*create_if_necessary=*/true) + ->StartSuggestionsRequest( + search_terms_args, client()->GetTemplateURLService(), + base::BindOnce(&ZeroSuggestProvider::OnPrefetchURLLoadComplete, + weak_ptr_factory_.GetWeakPtr(), + GetSanitizedInput(input, client()), result_type)); + + LogEvent(Event::kRequestSent, result_type, /*is_prefetch=*/true); +} + void ZeroSuggestProvider::Start(const AutocompleteInput& input, bool minimal_changes) { - Start(input, minimal_changes, /*is_prefetch=*/false); -} - -void ZeroSuggestProvider::StartPrefetch(const AutocompleteInput& input) { - Start(input, /*minimal_changes=*/false, /*is_prefetch=*/true); -} - -void ZeroSuggestProvider::Start(const AutocompleteInput& input, - bool minimal_changes, - bool is_prefetch) { TRACE_EVENT0("omnibox", "ZeroSuggestProvider::Start"); Stop(true, false); @@ -298,52 +341,38 @@ set_field_trial_triggered(false); set_field_trial_triggered_in_session(false); - TemplateURLRef::SearchTermsArgs search_terms_args; - search_terms_args.page_classification = input.current_page_classification(); - search_terms_args.focus_type = input.focus_type(); - GURL suggest_url = RemoteSuggestionsService::EndpointUrl( - search_terms_args, client()->GetTemplateURLService()); - if (!suggest_url.is_valid()) - return; - - if (is_prefetch) { - prefetch_done_ = false; - } else { - done_ = false; - - // Prefetching is only meant to update the stored response with a fresh - // response. It is unnecessary to convert it to the displayed matches when - // prefetching; as they will get cleared on the next call to `Start()`. - auto response_data = ReadStoredResponse(); - if (response_data) { - if (ConvertResponseToAutocompleteMatches(std::move(response_data))) { - LogEvent(Event::KCachedResponseConvertedToMatches, result_type_running_, - is_prefetch); - } + // Convert the stored response to |matches_|, if applicable. + auto response_data = ReadStoredResponse(result_type_running_); + if (response_data) { + if (ConvertResponseToAutocompleteMatches(std::move(response_data))) { + LogEvent(Event::KCachedResponseConvertedToMatches, result_type_running_, + /*is_prefetch=*/false); } } + // Do not start a request if async requests are disallowed. if (input.omit_asynchronous_matches()) { - // Asynchronous provider logic should only be omitted during non-prefetch - // ZPS requests. - DCHECK(!is_prefetch); - Stop(true, false); return; } + done_ = false; + + // Create a loader for the request and take ownership of it. + TemplateURLRef::SearchTermsArgs search_terms_args; + search_terms_args.page_classification = input.current_page_classification(); + search_terms_args.focus_type = input.focus_type(); search_terms_args.current_page_url = result_type_running_ == REMOTE_SEND_URL ? input.current_url().spec() : std::string(); - loader_ = - client() - ->GetRemoteSuggestionsService(/*create_if_necessary=*/true) - ->StartSuggestionsRequest( - search_terms_args, client()->GetTemplateURLService(), - base::BindOnce(&ZeroSuggestProvider::OnURLLoadComplete, - weak_ptr_factory_.GetWeakPtr(), is_prefetch)); + loader_ = client() + ->GetRemoteSuggestionsService(/*create_if_necessary=*/true) + ->StartSuggestionsRequest( + search_terms_args, client()->GetTemplateURLService(), + base::BindOnce(&ZeroSuggestProvider::OnURLLoadComplete, + weak_ptr_factory_.GetWeakPtr(), + result_type_running_)); - LogEvent(Event::kRequestSent, result_type_running_, - /*is_prefetch=*/!prefetch_done_); + LogEvent(Event::kRequestSent, result_type_running_, /*is_prefetch=*/false); } void ZeroSuggestProvider::Stop(bool clear_cached_results, @@ -352,11 +381,9 @@ if (loader_) { LogEvent(Event::kRequestInvalidated, result_type_running_, - /*is_prefetch=*/!prefetch_done_); + /*is_prefetch=*/false); + loader_.reset(); } - loader_.reset(); - prefetch_done_ = true; - result_type_running_ = NONE; if (clear_cached_results) { experiment_stats_v2s_.clear(); @@ -389,8 +416,7 @@ ZeroSuggestProvider::ZeroSuggestProvider(AutocompleteProviderClient* client, AutocompleteProviderListener* listener) - : BaseSearchProvider(AutocompleteProvider::TYPE_ZERO_SUGGEST, client), - result_type_running_(NONE) { + : BaseSearchProvider(AutocompleteProvider::TYPE_ZERO_SUGGEST, client) { AddListener(listener); } @@ -403,19 +429,7 @@ } const AutocompleteInput ZeroSuggestProvider::GetInput(bool is_keyword) const { - // In zero-suggest, input is expected to empty, as it is checked against the - // suggest response which always has an empty query. If those don't match, - // the response is dropped. Ensure the input text is empty. However copy - // over the URL. on-focus zero-suggest on Web/SRP on Mobile relies on the - // URL to be set. - AutocompleteInput input(std::u16string(), - input_.current_page_classification(), - client()->GetSchemeClassifier()); - input.set_current_url(input_.current_url()); - input.set_current_title(input_.current_title()); - input.set_prevent_inline_autocomplete(true); - input.set_allow_exact_keyword_match(false); - return input; + return GetSanitizedInput(input_, client()); } bool ZeroSuggestProvider::ShouldAppendExtraParams( @@ -435,10 +449,10 @@ } void ZeroSuggestProvider::OnURLLoadComplete( - bool is_prefetch, + ResultType result_type, const network::SimpleURLLoader* source, std::unique_ptr<std::string> response_body) { - DCHECK(!done_ || !prefetch_done_); + DCHECK(!done_); DCHECK_EQ(loader_.get(), source); std::unique_ptr<base::Value> response_data = nullptr; @@ -448,35 +462,56 @@ (source->ResponseInfo() && source->ResponseInfo()->headers && source->ResponseInfo()->headers->response_code() == 200); if (response_received) { - LogEvent(Event::kRemoteResponseReceived, result_type_running_, is_prefetch); - response_data = StoreRemoteResponse(SearchSuggestionParser::ExtractJsonData( - source, std::move(response_body)), - is_prefetch); + LogEvent(Event::kRemoteResponseReceived, result_type, + /*is_prefetch=*/false); + response_data = + StoreRemoteResponse(SearchSuggestionParser::ExtractJsonData( + source, std::move(response_body)), + GetInput(/*is_keyword=*/false), result_type, + /*is_prefetch=*/false); } - // Prefetching is only meant to update the stored response with a fresh - // response. It is unnecessary to convert the prefetched response to the - // displayed matches; as they will get cleared on the next call to `Start()`. - const bool matches_updated = response_data && !is_prefetch; - if (matches_updated) { + // Convert the response to |matches_|, if applicable. + if (response_data) { if (ConvertResponseToAutocompleteMatches(std::move(response_data))) { - LogEvent(Event::kRemoteResponseConvertedToMatches, result_type_running_, - is_prefetch); + LogEvent(Event::kRemoteResponseConvertedToMatches, result_type, + /*is_prefetch=*/false); } } loader_.reset(); - prefetch_done_ = true; done_ = true; - result_type_running_ = NONE; // Do not notify the provider listener for prefetch requests. - if (!is_prefetch) - NotifyListeners(matches_updated); + NotifyListeners(!!response_data); +} + +void ZeroSuggestProvider::OnPrefetchURLLoadComplete( + const AutocompleteInput& input, + ResultType result_type, + const network::SimpleURLLoader* source, + std::unique_ptr<std::string> response_body) { + DCHECK_EQ(prefetch_loader_.get(), source); + + const bool response_received = + response_body && source->NetError() == net::OK && + (source->ResponseInfo() && source->ResponseInfo()->headers && + source->ResponseInfo()->headers->response_code() == 200); + if (response_received) { + LogEvent(Event::kRemoteResponseReceived, result_type, /*is_prefetch=*/true); + StoreRemoteResponse(SearchSuggestionParser::ExtractJsonData( + source, std::move(response_body)), + input, result_type, + /*is_prefetch=*/true); + } + + prefetch_loader_.reset(); } std::unique_ptr<base::Value> ZeroSuggestProvider::StoreRemoteResponse( const std::string& response_json, + const AutocompleteInput& input, + ResultType result_type, bool is_prefetch) { if (response_json.empty()) { return nullptr; @@ -489,16 +524,18 @@ } SearchSuggestionParser::Results results; - if (!ParseSuggestResults(*response_data, kDefaultZeroSuggestRelevance, - /*is_keyword_result=*/false, &results)) { + if (!SearchSuggestionParser::ParseSuggestResults( + *response_data, GetSanitizedInput(input, client()), + client()->GetSchemeClassifier(), kDefaultZeroSuggestRelevance, + /*is_keyword_result=*/false, &results)) { return nullptr; } // Store the valid response only if running the REMOTE_NO_URL variant. - if (result_type_running_ == REMOTE_NO_URL) { + if (result_type == REMOTE_NO_URL) { client()->GetPrefs()->SetString(omnibox::kZeroSuggestCachedResults, response_json); - LogEvent(Event::kRemoteResponseCached, result_type_running_, is_prefetch); + LogEvent(Event::kRemoteResponseCached, result_type, is_prefetch); } // For display stability reasons, update the displayed results with the remote @@ -514,9 +551,10 @@ return nullptr; } -std::unique_ptr<base::Value> ZeroSuggestProvider::ReadStoredResponse() { +std::unique_ptr<base::Value> ZeroSuggestProvider::ReadStoredResponse( + ResultType result_type) { // Use the stored response only if running the REMOTE_NO_URL variant. - if (result_type_running_ != REMOTE_NO_URL) { + if (result_type != REMOTE_NO_URL) { return nullptr; }
diff --git a/components/omnibox/browser/zero_suggest_provider.h b/components/omnibox/browser/zero_suggest_provider.h index 5e078ce..2f779c28 100644 --- a/components/omnibox/browser/zero_suggest_provider.h +++ b/components/omnibox/browser/zero_suggest_provider.h
@@ -87,8 +87,8 @@ static void RegisterProfilePrefs(PrefRegistrySimple* registry); // AutocompleteProvider: - void Start(const AutocompleteInput& input, bool minimal_changes) override; void StartPrefetch(const AutocompleteInput& input) override; + void Start(const AutocompleteInput& input, bool minimal_changes) override; void Stop(bool clear_cached_results, bool due_to_user_inactivity) override; void DeleteMatch(const AutocompleteMatch& match) override; @@ -117,12 +117,6 @@ ZeroSuggestProvider(const ZeroSuggestProvider&) = delete; ZeroSuggestProvider& operator=(const ZeroSuggestProvider&) = delete; - // Called by Start() or StartPrefetch() with the appropriate arguments. - // Contains the implementation to start a request for suggestions. - void Start(const AutocompleteInput& input, - bool minimal_changes, - bool is_prefetch); - // BaseSearchProvider: const TemplateURL* GetTemplateURL(bool is_keyword) const override; const AutocompleteInput GetInput(bool is_keyword) const override; @@ -130,28 +124,38 @@ const SearchSuggestionParser::SuggestResult& result) const override; void RecordDeletionResult(bool success) override; - // Called when the network request for suggestions has completed. - // `is_prefetch` is bound to this callback and indicates if the request is a - // prefetch one. - void OnURLLoadComplete(bool is_prefetch, + // Called when the non-prefetch network request has completed. + // `result_type` is bound to this callback and indicate the result type being + // received in this callback. + void OnURLLoadComplete(ResultType result_type, const network::SimpleURLLoader* source, std::unique_ptr<std::string> response_body); + // Called when the prefetch network request has completed. + // `input` and `result_type` are bound to this callback. The former is the + // input the request was made for and the latter indicates the result type + // being received in this callback. + void OnPrefetchURLLoadComplete(const AutocompleteInput& input, + ResultType result_type, + const network::SimpleURLLoader* source, + std::unique_ptr<std::string> response_body); // Called when the remote response is received. Stores the response json in // the user prefs, if successfully parsed and if applicable based on - // |result_type_running_|. + // |result_type|. // // Returns the successfully parsed response if it is eligible to be converted // to |matches_| or nullptr otherwise. std::unique_ptr<base::Value> StoreRemoteResponse( const std::string& response_json, + const AutocompleteInput& input, + ResultType result_type, bool is_prefetch); // Called on Start(). // // Returns the response stored in the user prefs, if applicable based on - // |result_type_running_| or nullptr otherwise. - std::unique_ptr<base::Value> ReadStoredResponse(); + // |result_type| or nullptr otherwise. + std::unique_ptr<base::Value> ReadStoredResponse(ResultType result_type); // Returns an AutocompleteMatch for a navigational suggestion |navigation|. AutocompleteMatch NavigationToMatch( @@ -171,20 +175,22 @@ bool ConvertResponseToAutocompleteMatches( std::unique_ptr<base::Value> response); - // The result type that is currently being processed by provider. - // When the provider is not running, the result type is set to NONE. - ResultType result_type_running_; + // The result type that is currently being retrieved and processed for + // non-prefetch requests. + // Set in Start() and used in Stop() for logging purposes. + ResultType result_type_running_{NONE}; - // The user's input for which a suggestion fetch is pending. + // The input for which suggestions are being retrieved and processed for both + // prefetch and non-prefetch requests. + // Set in Start() and StartPrefetch() and used in GetInput() for parsing the + // response. AutocompleteInput input_; - // Loader used to retrieve results. + // Loader used to retrieve results for non-prefetch requests. std::unique_ptr<network::SimpleURLLoader> loader_; - // Like `AutocompleteProvider::done_`, but for prefetch requests. Used for - // metrics when the provider is stopped. `done_` and `prefetch_done_` should - // never both be true, a `Start()` request stops ongoing requests. - bool prefetch_done_; + // Loader used to retrieve results for prefetch requests. + std::unique_ptr<network::SimpleURLLoader> prefetch_loader_; // The list of experiment stats corresponding to |matches_|. SearchSuggestionParser::ExperimentStatsV2s experiment_stats_v2s_;
diff --git a/components/omnibox/browser/zero_suggest_provider_unittest.cc b/components/omnibox/browser/zero_suggest_provider_unittest.cc index f8bc182..381d1bf6 100644 --- a/components/omnibox/browser/zero_suggest_provider_unittest.cc +++ b/components/omnibox/browser/zero_suggest_provider_unittest.cc
@@ -693,22 +693,17 @@ GURL suggest_url = GetSuggestURL(metrics::OmniboxEventProto::NTP_REALBOX); provider_->Start(input, false); - // Given that this is a purely synchronous provider run, the running result - // type should have been set to NONE. - ASSERT_EQ(ZeroSuggestProvider::NONE, + ASSERT_EQ(ZeroSuggestProvider::REMOTE_NO_URL, provider_->GetResultTypeRunningForTesting()); + EXPECT_TRUE(provider_->done()); + EXPECT_TRUE(provider_->matches().empty()); // There should be no pending network requests, given that asynchronous logic // has been explicitly disabled (`omit_asynchronous_matches_ == true`). ASSERT_FALSE(test_loader_factory()->IsPending(suggest_url.spec())); - EXPECT_TRUE(provider_->done()); - EXPECT_TRUE(provider_->matches().empty()); - // Expect the provider to have not populated the results cache, given that - // no asynchronous network requests should have been sent to the suggestions - // service. - PrefService* prefs = client_->GetPrefs(); - EXPECT_EQ("", prefs->GetString(omnibox::kZeroSuggestCachedResults)); + // Expect the provider not to have notified the provider listener. + EXPECT_FALSE(provider_did_notify_); } TEST_F(ZeroSuggestProviderTest, TestPsuggestZeroSuggestHasCachedResults) { @@ -867,8 +862,6 @@ AutocompleteInput input = PrefetchingInputForNTP(); provider_->StartPrefetch(input); EXPECT_TRUE(provider_->done()); - ASSERT_EQ(ZeroSuggestProvider::REMOTE_NO_URL, - provider_->GetResultTypeRunningForTesting()); // Expect the results to be empty. ASSERT_EQ(0U, provider_->matches().size());
diff --git a/components/omnibox/common/omnibox_features.cc b/components/omnibox/common/omnibox_features.cc index 098b1e8..adfd095 100644 --- a/components/omnibox/common/omnibox_features.cc +++ b/components/omnibox/common/omnibox_features.cc
@@ -305,7 +305,7 @@ base::FEATURE_DISABLED_BY_DEFAULT}; const base::Feature kBlurWithEscape{"OmniboxBlurWithEscape", - base::FEATURE_DISABLED_BY_DEFAULT}; + base::FEATURE_ENABLED_BY_DEFAULT}; // When enabled, adds a "starter pack" of @history, @bookmarks, and @settings // scopes to Site Search/Keyword Mode.
diff --git a/components/printing/browser/headless/BUILD.gn b/components/printing/browser/headless/BUILD.gn new file mode 100644 index 0000000..04709c5 --- /dev/null +++ b/components/printing/browser/headless/BUILD.gn
@@ -0,0 +1,25 @@ +# Copyright 2022 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import("//printing/buildflags/buildflags.gni") + +assert(enable_basic_printing) + +static_library("headless") { + sources = [ + "headless_print_manager.cc", + "headless_print_manager.h", + ] + + deps = [ + "//base", + "//components/printing/browser", + "//components/printing/browser/print_to_pdf", + "//printing", + "//printing/buildflags", + "//printing/mojom", + ] + + public_deps = [ "//components/printing/common:mojo_interfaces" ] +}
diff --git a/components/printing/browser/headless/OWNERS b/components/printing/browser/headless/OWNERS new file mode 100644 index 0000000..d299b1ba --- /dev/null +++ b/components/printing/browser/headless/OWNERS
@@ -0,0 +1 @@ +file://headless/OWNERS
diff --git a/components/printing/browser/print_to_pdf/pdf_print_manager.cc b/components/printing/browser/headless/headless_print_manager.cc similarity index 64% rename from components/printing/browser/print_to_pdf/pdf_print_manager.cc rename to components/printing/browser/headless/headless_print_manager.cc index 3975b60..8d7914a3 100644 --- a/components/printing/browser/print_to_pdf/pdf_print_manager.cc +++ b/components/printing/browser/headless/headless_print_manager.cc
@@ -2,13 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "components/printing/browser/print_to_pdf/pdf_print_manager.h" +#include "components/printing/browser/headless/headless_print_manager.h" #include <utility> #include "base/bind.h" #include "build/build_config.h" -#include "components/printing/browser/print_to_pdf/pdf_print_result.h" #include "components/printing/browser/print_to_pdf/pdf_print_utils.h" #include "printing/mojom/print.mojom.h" #include "printing/page_range.h" @@ -18,51 +17,41 @@ #include "mojo/public/cpp/bindings/message.h" #endif -namespace print_to_pdf { +using print_to_pdf::PageRangeError; +using print_to_pdf::PdfPrintResult; + +namespace headless { namespace { #if BUILDFLAG(ENABLE_PRINT_PREVIEW) -constexpr char kInvalidUpdatePrintSettingsCall[] = - "Invalid UpdatePrintSettings Call"; -constexpr char kInvalidSetupScriptedPrintPreviewCall[] = - "Invalid SetupScriptedPrintPreview Call"; -constexpr char kInvalidShowScriptedPrintPreviewCall[] = - "Invalid ShowScriptedPrintPreview Call"; -constexpr char kInvalidRequestPrintPreviewCall[] = - "Invalid RequestPrintPreview Call"; -constexpr char kInvalidCheckForCancelCall[] = "Invalid CheckForCancel Call"; -#endif - -#if BUILDFLAG(ENABLE_TAGGED_PDF) -constexpr char kInvalidSetAccessibilityTreeCall[] = - "Invalid SetAccessibilityTree Call"; +constexpr char kUnexpectedPrintManagerCall[] = "Unexpected Print Manager call"; #endif } // namespace -PdfPrintManager::PdfPrintManager(content::WebContents* web_contents) +HeadlessPrintManager::HeadlessPrintManager(content::WebContents* web_contents) : printing::PrintManager(web_contents), - content::WebContentsUserData<PdfPrintManager>(*web_contents) {} + content::WebContentsUserData<HeadlessPrintManager>(*web_contents) {} -PdfPrintManager::~PdfPrintManager() = default; +HeadlessPrintManager::~HeadlessPrintManager() = default; // static -void PdfPrintManager::BindPrintManagerHost( +void HeadlessPrintManager::BindPrintManagerHost( mojo::PendingAssociatedReceiver<printing::mojom::PrintManagerHost> receiver, content::RenderFrameHost* rfh) { auto* web_contents = content::WebContents::FromRenderFrameHost(rfh); if (!web_contents) return; - auto* print_manager = PdfPrintManager::FromWebContents(web_contents); + auto* print_manager = HeadlessPrintManager::FromWebContents(web_contents); if (!print_manager) return; print_manager->BindReceiver(std::move(receiver), rfh); } -void PdfPrintManager::PrintToPdf( +void HeadlessPrintManager::PrintToPdf( content::RenderFrameHost* rfh, const std::string& page_ranges, printing::mojom::PrintPagesParamsPtr print_pages_params, @@ -82,7 +71,7 @@ } absl::variant<printing::PageRanges, PageRangeError> parsed_ranges = - TextPageRangesToPageRanges(page_ranges); + print_to_pdf::TextPageRangesToPageRanges(page_ranges); if (absl::holds_alternative<PageRangeError>(parsed_ranges)) { PdfPrintResult print_result; switch (absl::get<PageRangeError>(parsed_ranges)) { @@ -107,11 +96,11 @@ // in the base class. If we're gone, mojo will discard the callback. GetPrintRenderFrame(rfh)->PrintWithParams( std::move(print_pages_params), - base::BindOnce(&PdfPrintManager::OnDidPrintWithParams, + base::BindOnce(&HeadlessPrintManager::OnDidPrintWithParams, base::Unretained(this))); } -void PdfPrintManager::OnDidPrintWithParams( +void HeadlessPrintManager::OnDidPrintWithParams( printing::mojom::PrintWithParamsResultPtr result) { if (result->is_failure_reason()) { switch (result->get_failure_reason()) { @@ -138,13 +127,13 @@ ReleaseJob(PdfPrintResult::PRINT_SUCCESS); } -void PdfPrintManager::GetDefaultPrintSettings( +void HeadlessPrintManager::GetDefaultPrintSettings( GetDefaultPrintSettingsCallback callback) { DLOG(ERROR) << "Scripted print is not supported"; std::move(callback).Run(printing::mojom::PrintParams::New()); } -void PdfPrintManager::ScriptedPrint( +void HeadlessPrintManager::ScriptedPrint( printing::mojom::ScriptedPrintParamsPtr params, ScriptedPrintCallback callback) { auto default_param = printing::mojom::PrintPagesParams::New(); @@ -153,64 +142,52 @@ std::move(callback).Run(std::move(default_param)); } -void PdfPrintManager::ShowInvalidPrinterSettingsError() { +void HeadlessPrintManager::ShowInvalidPrinterSettingsError() { ReleaseJob(PdfPrintResult::INVALID_PRINTER_SETTINGS); } #if BUILDFLAG(ENABLE_PRINT_PREVIEW) -void PdfPrintManager::UpdatePrintSettings( +void HeadlessPrintManager::UpdatePrintSettings( int32_t cookie, base::Value::Dict job_settings, UpdatePrintSettingsCallback callback) { - // UpdatePrintSettingsCallback() should never be called on - // PdfPrintManager, since it is only triggered by Print Preview. - mojo::ReportBadMessage(kInvalidUpdatePrintSettingsCall); + mojo::ReportBadMessage(kUnexpectedPrintManagerCall); } -void PdfPrintManager::SetupScriptedPrintPreview( +void HeadlessPrintManager::SetupScriptedPrintPreview( SetupScriptedPrintPreviewCallback callback) { - // SetupScriptedPrintPreview() should never be called on - // PdfPrintManager, since it is only triggered by Print Preview. - mojo::ReportBadMessage(kInvalidSetupScriptedPrintPreviewCall); + mojo::ReportBadMessage(kUnexpectedPrintManagerCall); } -void PdfPrintManager::ShowScriptedPrintPreview(bool source_is_modifiable) { - // ShowScriptedPrintPreview() should never be called on - // PdfPrintManager, since it is only triggered by Print Preview. - mojo::ReportBadMessage(kInvalidShowScriptedPrintPreviewCall); +void HeadlessPrintManager::ShowScriptedPrintPreview(bool source_is_modifiable) { + mojo::ReportBadMessage(kUnexpectedPrintManagerCall); } -void PdfPrintManager::RequestPrintPreview( +void HeadlessPrintManager::RequestPrintPreview( printing::mojom::RequestPrintPreviewParamsPtr params) { - // RequestPrintPreview() should never be called on PdfPrintManager, - // since it is only triggered by Print Preview. - mojo::ReportBadMessage(kInvalidRequestPrintPreviewCall); + mojo::ReportBadMessage(kUnexpectedPrintManagerCall); } -void PdfPrintManager::CheckForCancel(int32_t preview_ui_id, - int32_t request_id, - CheckForCancelCallback callback) { - // CheckForCancel() should never be called on PdfPrintManager, since it - // is only triggered by Print Preview. - mojo::ReportBadMessage(kInvalidCheckForCancelCall); +void HeadlessPrintManager::CheckForCancel(int32_t preview_ui_id, + int32_t request_id, + CheckForCancelCallback callback) { + mojo::ReportBadMessage(kUnexpectedPrintManagerCall); } #endif // BUILDFLAG(ENABLE_PRINT_PREVIEW) #if BUILDFLAG(ENABLE_TAGGED_PDF) -void PdfPrintManager::SetAccessibilityTree( +void HeadlessPrintManager::SetAccessibilityTree( int32_t cookie, const ui::AXTreeUpdate& accessibility_tree) { - // SetAccessibilityTree() should never be called on PdfPrintManager, - // since it is only triggered by Print Preview. - mojo::ReportBadMessage(kInvalidSetAccessibilityTreeCall); + mojo::ReportBadMessage(kUnexpectedPrintManagerCall); } #endif #if BUILDFLAG(IS_ANDROID) -void PdfPrintManager::PdfWritingDone(int page_count) {} +void HeadlessPrintManager::PdfWritingDone(int page_count) {} #endif -void PdfPrintManager::RenderFrameDeleted( +void HeadlessPrintManager::RenderFrameDeleted( content::RenderFrameHost* render_frame_host) { PrintManager::RenderFrameDeleted(render_frame_host); @@ -226,13 +203,13 @@ Reset(); } -void PdfPrintManager::Reset() { +void HeadlessPrintManager::Reset() { printing_rfh_ = nullptr; callback_.Reset(); data_.clear(); } -void PdfPrintManager::ReleaseJob(PdfPrintResult result) { +void HeadlessPrintManager::ReleaseJob(PdfPrintResult result) { if (!callback_) { DLOG(ERROR) << "ReleaseJob is called when callback_ is null. Check whether " "ReleaseJob is called more than once."; @@ -254,6 +231,6 @@ Reset(); } -WEB_CONTENTS_USER_DATA_KEY_IMPL(PdfPrintManager); +WEB_CONTENTS_USER_DATA_KEY_IMPL(HeadlessPrintManager); -} // namespace print_to_pdf +} // namespace headless
diff --git a/components/printing/browser/print_to_pdf/pdf_print_manager.h b/components/printing/browser/headless/headless_print_manager.h similarity index 73% rename from components/printing/browser/print_to_pdf/pdf_print_manager.h rename to components/printing/browser/headless/headless_print_manager.h index b2dcff28..bab23c2 100644 --- a/components/printing/browser/print_to_pdf/pdf_print_manager.h +++ b/components/printing/browser/headless/headless_print_manager.h
@@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef COMPONENTS_PRINTING_BROWSER_PRINT_TO_PDF_PDF_PRINT_MANAGER_H_ -#define COMPONENTS_PRINTING_BROWSER_PRINT_TO_PDF_PDF_PRINT_MANAGER_H_ +#ifndef COMPONENTS_PRINTING_BROWSER_HEADLESS_HEADLESS_PRINT_MANAGER_H_ +#define COMPONENTS_PRINTING_BROWSER_HEADLESS_HEADLESS_PRINT_MANAGER_H_ #include <memory> #include <string> @@ -20,19 +20,23 @@ #include "content/public/browser/web_contents_user_data.h" #include "printing/print_settings.h" -namespace print_to_pdf { +namespace headless { -class PdfPrintManager : public printing::PrintManager, - public content::WebContentsUserData<PdfPrintManager> { +// Minimalistic PrintManager implemementation intended for use with Headless +// Chrome. It shortcuts most of the methods exposing only PrintToPdf() +// functionality. +class HeadlessPrintManager + : public printing::PrintManager, + public content::WebContentsUserData<HeadlessPrintManager> { public: using PrintToPdfCallback = - base::OnceCallback<void(PdfPrintResult, + base::OnceCallback<void(print_to_pdf::PdfPrintResult, scoped_refptr<base::RefCountedMemory>)>; - ~PdfPrintManager() override; + ~HeadlessPrintManager() override; - PdfPrintManager(const PdfPrintManager&) = delete; - PdfPrintManager& operator=(const PdfPrintManager&) = delete; + HeadlessPrintManager(const HeadlessPrintManager&) = delete; + HeadlessPrintManager& operator=(const HeadlessPrintManager&) = delete; static void BindPrintManagerHost( mojo::PendingAssociatedReceiver<printing::mojom::PrintManagerHost> @@ -45,9 +49,9 @@ PrintToPdfCallback callback); private: - friend class content::WebContentsUserData<PdfPrintManager>; + friend class content::WebContentsUserData<HeadlessPrintManager>; - explicit PdfPrintManager(content::WebContents* web_contents); + explicit HeadlessPrintManager(content::WebContents* web_contents); void OnDidPrintWithParams(printing::mojom::PrintWithParamsResultPtr result); @@ -83,7 +87,7 @@ #endif void Reset(); - void ReleaseJob(PdfPrintResult result); + void ReleaseJob(print_to_pdf::PdfPrintResult result); raw_ptr<content::RenderFrameHost> printing_rfh_ = nullptr; PrintToPdfCallback callback_; @@ -92,6 +96,6 @@ WEB_CONTENTS_USER_DATA_KEY_DECL(); }; -} // namespace print_to_pdf +} // namespace headless -#endif // COMPONENTS_PRINTING_BROWSER_PRINT_TO_PDF_PDF_PRINT_MANAGER_H_ +#endif // COMPONENTS_PRINTING_BROWSER_HEADLESS_HEADLESS_PRINT_MANAGER_H_
diff --git a/components/printing/browser/print_to_pdf/BUILD.gn b/components/printing/browser/print_to_pdf/BUILD.gn index abe92fd9..394f173 100644 --- a/components/printing/browser/print_to_pdf/BUILD.gn +++ b/components/printing/browser/print_to_pdf/BUILD.gn
@@ -9,8 +9,6 @@ static_library("print_to_pdf") { sources = [ - "pdf_print_manager.cc", - "pdf_print_manager.h", "pdf_print_result.cc", "pdf_print_result.h", "pdf_print_utils.cc", @@ -23,6 +21,7 @@ "//printing", "//printing/buildflags", "//printing/mojom", + "//url", ] public_deps = [ "//components/printing/common:mojo_interfaces" ]
diff --git a/components/printing/browser/print_to_pdf/pdf_print_result.cc b/components/printing/browser/print_to_pdf/pdf_print_result.cc index 836cfb1..11b3a61e 100644 --- a/components/printing/browser/print_to_pdf/pdf_print_result.cc +++ b/components/printing/browser/print_to_pdf/pdf_print_result.cc
@@ -4,8 +4,6 @@ #include "components/printing/browser/print_to_pdf/pdf_print_result.h" -#include "base/notreached.h" - namespace print_to_pdf { std::string PdfPrintResultToString(PdfPrintResult result) {
diff --git a/components/privacy_sandbox/privacy_sandbox_settings.cc b/components/privacy_sandbox/privacy_sandbox_settings.cc index 66615fa..02e2b76 100644 --- a/components/privacy_sandbox/privacy_sandbox_settings.cc +++ b/components/privacy_sandbox/privacy_sandbox_settings.cc
@@ -310,8 +310,6 @@ if (!IsPrivacySandboxEnabled()) return {}; - ContentSettingsForOneType cookie_settings; - cookie_settings_->GetCookieSettings(&cookie_settings); std::vector<GURL> allowed_parties; for (const auto& party : auction_parties) { if (IsPrivacySandboxEnabledForContext(party, top_frame_origin)) {
diff --git a/components/remote_cocoa/app_shim/native_widget_mac_nswindow.h b/components/remote_cocoa/app_shim/native_widget_mac_nswindow.h index 67ebc56..77662e8 100644 --- a/components/remote_cocoa/app_shim/native_widget_mac_nswindow.h +++ b/components/remote_cocoa/app_shim/native_widget_mac_nswindow.h
@@ -69,6 +69,9 @@ // The NativeWidgetNSWindowBridge that this will use to call back to the host. @property(assign, nonatomic) remote_cocoa::NativeWidgetNSWindowBridge* bridge; + +// Whether this window functions as a tooltip. +@property(assign, nonatomic) BOOL isTooltip; @end #endif // COMPONENTS_REMOTE_COCOA_APP_SHIM_NATIVE_WIDGET_MAC_NSWINDOW_H_
diff --git a/components/remote_cocoa/app_shim/native_widget_mac_nswindow.mm b/components/remote_cocoa/app_shim/native_widget_mac_nswindow.mm index 48c86f5a..75546f5 100644 --- a/components/remote_cocoa/app_shim/native_widget_mac_nswindow.mm +++ b/components/remote_cocoa/app_shim/native_widget_mac_nswindow.mm
@@ -146,9 +146,11 @@ BOOL _isEnforcingNeverMadeVisible; BOOL _preventKeyWindow; BOOL _isAddingChildWindow; + BOOL _isTooltip; } @synthesize bridgedNativeWidgetId = _bridgedNativeWidgetId; @synthesize bridge = _bridge; +@synthesize isTooltip = _isTooltip; - (instancetype)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)windowStyle @@ -279,6 +281,10 @@ return obj; } +- (NSAccessibilityRole)accessibilityRole { + return _isTooltip ? NSAccessibilityHelpTagRole : [super accessibilityRole]; +} + // NSWindow overrides. + (Class)frameViewClassForStyleMask:(NSWindowStyleMask)windowStyle {
diff --git a/components/remote_cocoa/app_shim/native_widget_ns_window_bridge.mm b/components/remote_cocoa/app_shim/native_widget_ns_window_bridge.mm index 34721e5..c8a108c 100644 --- a/components/remote_cocoa/app_shim/native_widget_ns_window_bridge.mm +++ b/components/remote_cocoa/app_shim/native_widget_ns_window_bridge.mm
@@ -482,6 +482,7 @@ // Don't allow dragging sheets. if (params->modal_type == ui::MODAL_TYPE_WINDOW) [window_ setMovable:NO]; + [window_ setIsTooltip:params->is_tooltip]; } void NativeWidgetNSWindowBridge::SetInitialBounds(
diff --git a/components/remote_cocoa/common/native_widget_ns_window.mojom b/components/remote_cocoa/common/native_widget_ns_window.mojom index 98292d6..6bb6315 100644 --- a/components/remote_cocoa/common/native_widget_ns_window.mojom +++ b/components/remote_cocoa/common/native_widget_ns_window.mojom
@@ -81,6 +81,9 @@ // window's workspace and fullscreen state, and can be retrieved from or // applied to a window. array<uint8> state_restoration_data; + // If true, this window is functionally a tooltip, and shouldn't be presented + // as a new window to the accessibility system. + bool is_tooltip; }; enum WindowControlsOverlayNSViewType {
diff --git a/components/signin/core/browser/cookie_settings_util.cc b/components/signin/core/browser/cookie_settings_util.cc index 2f9cebf..9bfe742 100644 --- a/components/signin/core/browser/cookie_settings_util.cc +++ b/components/signin/core/browser/cookie_settings_util.cc
@@ -23,8 +23,7 @@ const content_settings::CookieSettings* cookie_settings) { GURL gaia_url = GaiaUrls::GetInstance()->gaia_url(); GURL google_url = GaiaUrls::GetInstance()->google_url(); - ContentSettingsForOneType settings; - cookie_settings->GetCookieSettings(&settings); + ContentSettingsForOneType settings = cookie_settings->GetCookieSettings(); return !cookie_settings || cookie_settings->ShouldDeleteCookieOnExit(
diff --git a/components/tab_groups/public/mojom/tab_group_types.mojom b/components/tab_groups/public/mojom/tab_group_types.mojom index 010513de..7b78dcef 100644 --- a/components/tab_groups/public/mojom/tab_group_types.mojom +++ b/components/tab_groups/public/mojom/tab_group_types.mojom
@@ -6,8 +6,9 @@ // The color Id associated with a given Tab Group. Maps to // tab_groups::TabGroupColorId in components/tab_groups/tab_group_color.h. +[Stable, Extensible] enum Color { - kGrey, + [Default] kGrey, kBlue, kRed, kYellow,
diff --git a/components/viz/service/display/display.cc b/components/viz/service/display/display.cc index a8092b91..0101429e 100644 --- a/components/viz/service/display/display.cc +++ b/components/viz/service/display/display.cc
@@ -972,6 +972,11 @@ "viz,benchmark", "Graphics.Pipeline.DrawAndSwap", last_swap_ack_trace_id_, "WaitForPresentation", timings.swap_end); + if (overlay_processor_) + overlay_processor_->OverlayPresentationComplete(); + if (renderer_) + renderer_->SwapBuffersComplete(std::move(release_fence)); + DCHECK_GT(pending_swaps_, 0); pending_swaps_--; if (scheduler_) { @@ -981,11 +986,6 @@ if (no_pending_swaps_callback_ && pending_swaps_ == 0) std::move(no_pending_swaps_callback_).Run(); - if (overlay_processor_) - overlay_processor_->OverlayPresentationComplete(); - if (renderer_) - renderer_->SwapBuffersComplete(std::move(release_fence)); - // It's possible to receive multiple calls to DidReceiveSwapBuffersAck() // before DidReceivePresentationFeedback(). Ensure that we're not setting // |swap_timings_| for the same PresentationGroupTiming multiple times.
diff --git a/components/viz/service/display/overlay_processor_android.cc b/components/viz/service/display/overlay_processor_android.cc index 11de2ab..fc108b61 100644 --- a/components/viz/service/display/overlay_processor_android.cc +++ b/components/viz/service/display/overlay_processor_android.cc
@@ -22,6 +22,15 @@ DisplayCompositorMemoryAndTaskController* display_controller) : OverlayProcessorUsingStrategy(), gpu_task_scheduler_(display_controller->gpu_task_scheduler()) { + // Promoting video to overlay with SurfaceView overlays requires recreation of + // main SurfaceView and Display. This leads to the situation when we + // consider video overlay not being efficient for the first frame after we + // updated SurfaceView and video gets demoted back to composition. To avoid + // this, we disable heuristics that filter out not efficient quads but still + // sort them by potential power savings. + prioritization_config_.changing_threshold = false; + prioritization_config_.damage_rate_threshold = false; + // In unittests, we don't have the gpu_task_scheduler_ set up, but still want // to test ProcessForOverlays functionalities where we are making overlay // candidates correctly.
diff --git a/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc b/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc index 263909c..00631df 100644 --- a/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc +++ b/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc
@@ -1607,20 +1607,7 @@ void SkiaOutputSurfaceImplOnGpu::ScheduleOverlays( SkiaOutputSurface::OverlayList overlays) { - TRACE_EVENT1("viz", "SkiaOutputSurfaceImplOnGpu::ScheduleOverlays", - "num_overlays", overlays.size()); - - constexpr base::TimeDelta kHistogramMinTime = base::Microseconds(5); - constexpr base::TimeDelta kHistogramMaxTime = base::Milliseconds(16); - constexpr int kHistogramTimeBuckets = 50; - base::TimeTicks start_time = base::TimeTicks::Now(); - - output_device_->ScheduleOverlays(std::move(overlays)); - - UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES( - "Gpu.OutputSurface.ScheduleOverlaysUs", - base::TimeTicks::Now() - start_time, kHistogramMinTime, kHistogramMaxTime, - kHistogramTimeBuckets); + overlays_ = std::move(overlays); } void SkiaOutputSurfaceImplOnGpu::SetEnableDCLayers(bool enable) { @@ -2015,6 +2002,23 @@ output_surface_plane_->damage_rect = frame->sub_buffer_rect; } + if (overlays_.size()) { + TRACE_EVENT1("viz", "SkiaOutputDevice->ScheduleOverlays()", + "num_overlays", overlays_.size()); + + constexpr base::TimeDelta kHistogramMinTime = base::Microseconds(5); + constexpr base::TimeDelta kHistogramMaxTime = base::Milliseconds(16); + constexpr int kHistogramTimeBuckets = 50; + base::TimeTicks start_time = base::TimeTicks::Now(); + + output_device_->ScheduleOverlays(std::move(overlays_)); + + UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES( + "Gpu.OutputSurface.ScheduleOverlaysUs", + base::TimeTicks::Now() - start_time, kHistogramMinTime, + kHistogramMaxTime, kHistogramTimeBuckets); + } + output_device_->SetViewportSize(frame->size); output_device_->SchedulePrimaryPlane(output_surface_plane_); @@ -2038,8 +2042,9 @@ } } - // Reset the primary plane information even on skipped swap. + // Reset the overlay plane information even on skipped swap. output_surface_plane_.reset(); + overlays_.clear(); destroy_after_swap_.clear(); context_state_->UpdateSkiaOwnedMemorySize();
diff --git a/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.h b/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.h index 710b875e..4c123e7 100644 --- a/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.h +++ b/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.h
@@ -493,6 +493,9 @@ absl::optional<OverlayProcessorInterface::OutputSurfaceOverlayPlane> output_surface_plane_; + // Overlays are saved when ScheduleOverlays() is called, then passed to + // |output_device_| in PostSubmit(). + SkiaOutputSurface::OverlayList overlays_; // Micro-optimization to get to issuing GPU SwapBuffers as soon as possible. std::vector<sk_sp<SkDeferredDisplayList>> destroy_after_swap_;
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn index 1a6ad373..9089c28 100644 --- a/content/browser/BUILD.gn +++ b/content/browser/BUILD.gn
@@ -1446,8 +1446,11 @@ "private_aggregation/private_aggregation_budget_storage.h", "private_aggregation/private_aggregation_budgeter.cc", "private_aggregation/private_aggregation_budgeter.h", + "private_aggregation/private_aggregation_features.cc", + "private_aggregation/private_aggregation_features.h", "private_aggregation/private_aggregation_host.cc", "private_aggregation/private_aggregation_host.h", + "private_aggregation/private_aggregation_manager.cc", "private_aggregation/private_aggregation_manager.h", "private_aggregation/private_aggregation_manager_impl.cc", "private_aggregation/private_aggregation_manager_impl.h",
diff --git a/content/browser/aggregation_service/aggregatable_report_scheduler.h b/content/browser/aggregation_service/aggregatable_report_scheduler.h index 5ebe9e3..850a5de 100644 --- a/content/browser/aggregation_service/aggregatable_report_scheduler.h +++ b/content/browser/aggregation_service/aggregatable_report_scheduler.h
@@ -47,22 +47,24 @@ delete; AggregatableReportScheduler& operator=( const AggregatableReportScheduler& other) = delete; - ~AggregatableReportScheduler(); + virtual ~AggregatableReportScheduler(); + + // Methods are virtual for testing. // Schedules the `request` to be assembled and sent at // `request.shared_info().scheduled_report_time`. - void ScheduleRequest(AggregatableReportRequest request); + virtual void ScheduleRequest(AggregatableReportRequest request); // Notifies that the request to assemble and send the report with `request_id` // was successfully completed. There must be an in-progress request stored // with that `request_id`. - void NotifyInProgressRequestSucceeded( + virtual void NotifyInProgressRequestSucceeded( AggregationServiceStorage::RequestId request_id); // Notifies that the request to assemble and send the report with `request_id` // completed unsuccessfully. There must be an in-progress request stored with // that `request_id`. - void NotifyInProgressRequestFailed( + virtual void NotifyInProgressRequestFailed( AggregationServiceStorage::RequestId request_id); // TODO(crbug.com/1340042): Implement offline and startup handling
diff --git a/content/browser/aggregation_service/aggregation_service.h b/content/browser/aggregation_service/aggregation_service.h index 550dded3..8c42ab3 100644 --- a/content/browser/aggregation_service/aggregation_service.h +++ b/content/browser/aggregation_service/aggregation_service.h
@@ -7,7 +7,7 @@ #include "content/browser/aggregation_service/aggregatable_report_assembler.h" #include "content/browser/aggregation_service/aggregatable_report_sender.h" -#include "third_party/abseil-cpp/absl/types/optional.h" +#include "content/public/browser/storage_partition.h" class GURL; @@ -56,12 +56,20 @@ const base::Value& contents, SendCallback callback) = 0; - // Deletes all data in storage that were fetched between `delete_begin` and - // `delete_end` time (inclusive). Null times are treated as unbounded lower or - // upper range. + // Deletes all data in storage that were fetched/stored between `delete_begin` + // and `delete_end` time (inclusive). Null times are treated as unbounded + // lower or upper range. If `!filter.is_null()`, requests with a reporting + // origin that does *not* match the `filter` are retained (i.e. not cleared); + // `filter` does not affect public key deletion. virtual void ClearData(base::Time delete_begin, base::Time delete_end, + StoragePartition::StorageKeyMatcherFunction filter, base::OnceClosure done) = 0; + + // Schedules `report_request` to be assembled and sent at its scheduled report + // time. It is stored on disk (unless in incognito) until then. See the + // `AggregatableReportScheduler` for details. + virtual void ScheduleReport(AggregatableReportRequest report_request) = 0; }; } // namespace content
diff --git a/content/browser/aggregation_service/aggregation_service_impl.cc b/content/browser/aggregation_service/aggregation_service_impl.cc index 900206b..2ef5d003 100644 --- a/content/browser/aggregation_service/aggregation_service_impl.cc +++ b/content/browser/aggregation_service/aggregation_service_impl.cc
@@ -8,8 +8,11 @@ #include <memory> #include <utility> +#include <vector> +#include "base/bind.h" #include "base/callback.h" +#include "base/check_op.h" #include "base/files/file_path.h" #include "base/memory/ptr_util.h" #include "base/task/lazy_thread_pool_task_runner.h" @@ -18,10 +21,12 @@ #include "base/time/time.h" #include "base/values.h" #include "content/browser/aggregation_service/aggregatable_report_assembler.h" +#include "content/browser/aggregation_service/aggregatable_report_scheduler.h" +#include "content/browser/aggregation_service/aggregatable_report_sender.h" #include "content/browser/aggregation_service/aggregation_service_storage_sql.h" #include "content/browser/aggregation_service/public_key.h" #include "content/browser/storage_partition_impl.h" -#include "third_party/abseil-cpp/absl/types/optional.h" +#include "content/public/browser/storage_partition.h" #include "url/gurl.h" namespace content { @@ -50,6 +55,12 @@ run_in_memory, user_data_directory, base::DefaultClock::GetInstance(), + std::make_unique<AggregatableReportScheduler>( + /*storage_context=*/this, + // `base::Unretained` is safe as the scheduler is owned by `this`. + base::BindRepeating( + &AggregationServiceImpl::OnScheduledReportTimeReached, + base::Unretained(this))), std::make_unique<AggregatableReportAssembler>(this, storage_partition), std::make_unique<AggregatableReportSender>(storage_partition)) {} @@ -62,20 +73,23 @@ bool run_in_memory, const base::FilePath& user_data_directory, const base::Clock* clock, + std::unique_ptr<AggregatableReportScheduler> scheduler, std::unique_ptr<AggregatableReportAssembler> assembler, std::unique_ptr<AggregatableReportSender> sender) { - return base::WrapUnique<AggregationServiceImpl>( - new AggregationServiceImpl(run_in_memory, user_data_directory, clock, - std::move(assembler), std::move(sender))); + return base::WrapUnique<AggregationServiceImpl>(new AggregationServiceImpl( + run_in_memory, user_data_directory, clock, std::move(scheduler), + std::move(assembler), std::move(sender))); } AggregationServiceImpl::AggregationServiceImpl( bool run_in_memory, const base::FilePath& user_data_directory, const base::Clock* clock, + std::unique_ptr<AggregatableReportScheduler> scheduler, std::unique_ptr<AggregatableReportAssembler> assembler, std::unique_ptr<AggregatableReportSender> sender) - : storage_(base::SequenceBound<AggregationServiceStorageSql>( + : scheduler_(std::move(scheduler)), + storage_(base::SequenceBound<AggregationServiceStorageSql>( g_storage_task_runner.Get(), run_in_memory, user_data_directory, @@ -106,13 +120,62 @@ return storage_; } -void AggregationServiceImpl::ClearData(base::Time delete_begin, - base::Time delete_end, - base::OnceClosure done) { - storage_.AsyncCall(&AggregationServiceStorage::ClearPublicKeysFetchedBetween) - .WithArgs(delete_begin, delete_end) +void AggregationServiceImpl::ClearData( + base::Time delete_begin, + base::Time delete_end, + StoragePartition::StorageKeyMatcherFunction filter, + base::OnceClosure done) { + storage_.AsyncCall(&AggregationServiceStorage::ClearDataBetween) + .WithArgs(delete_begin, delete_end, std::move(filter)) .Then(std::move(done)); - // TODO(crbug.com/1340053): Clear stored report requests as well. +} + +void AggregationServiceImpl::ScheduleReport( + AggregatableReportRequest report_request) { + scheduler_->ScheduleRequest(std::move(report_request)); +} + +void AggregationServiceImpl::OnScheduledReportTimeReached( + std::vector<AggregationServiceStorage::RequestAndId> requests_and_ids) { + for (AggregationServiceStorage::RequestAndId& elem : requests_and_ids) { + GURL reporting_url = elem.request.GetReportingUrl(); + AssembleReport( + std::move(elem.request), + base::BindOnce( + &AggregationServiceImpl::OnReportAssemblyComplete, + // `base::Unretained` is safe as the assembler is owned by `this`. + base::Unretained(this), elem.id, std::move(reporting_url))); + } +} + +void AggregationServiceImpl::OnReportAssemblyComplete( + AggregationServiceStorage::RequestId request_id, + GURL reporting_url, + absl::optional<AggregatableReport> report, + AggregatableReportAssembler::AssemblyStatus status) { + DCHECK_EQ(report.has_value(), + status == AggregatableReportAssembler::AssemblyStatus::kOk); + if (!report.has_value()) { + scheduler_->NotifyInProgressRequestFailed(request_id); + return; + } + + SendReport(reporting_url, report.value(), + /*callback=*/ + base::BindOnce( + &AggregationServiceImpl::OnReportSendingComplete, + // `base::Unretained` is safe as the sender is owned by `this`. + base::Unretained(this), request_id)); +} + +void AggregationServiceImpl::OnReportSendingComplete( + AggregationServiceStorage::RequestId request_id, + AggregatableReportSender::RequestStatus status) { + if (status == AggregatableReportSender::RequestStatus::kOk) { + scheduler_->NotifyInProgressRequestSucceeded(request_id); + } else { + scheduler_->NotifyInProgressRequestFailed(request_id); + } } void AggregationServiceImpl::SetPublicKeysForTesting(
diff --git a/content/browser/aggregation_service/aggregation_service_impl.h b/content/browser/aggregation_service/aggregation_service_impl.h index 100eecd5..da14798 100644 --- a/content/browser/aggregation_service/aggregation_service_impl.h +++ b/content/browser/aggregation_service/aggregation_service_impl.h
@@ -8,14 +8,17 @@ #include <stdint.h> #include <memory> +#include <vector> #include "base/containers/flat_map.h" #include "base/threading/sequence_bound.h" #include "content/browser/aggregation_service/aggregatable_report_assembler.h" +#include "content/browser/aggregation_service/aggregatable_report_scheduler.h" #include "content/browser/aggregation_service/aggregatable_report_sender.h" #include "content/browser/aggregation_service/aggregation_service.h" #include "content/browser/aggregation_service/aggregation_service_storage_context.h" #include "content/common/content_export.h" +#include "content/public/browser/storage_partition.h" #include "third_party/abseil-cpp/absl/types/optional.h" class GURL; @@ -28,7 +31,9 @@ namespace content { struct PublicKeyset; +class AggregatableReport; class AggregationServiceStorage; +class AggregatableReportScheduler; class StoragePartitionImpl; // UI thread class that manages the lifetime of the underlying storage. Owned by @@ -42,6 +47,7 @@ bool run_in_memory, const base::FilePath& user_data_directory, const base::Clock* clock, + std::unique_ptr<AggregatableReportScheduler> scheduler, std::unique_ptr<AggregatableReportAssembler> assembler, std::unique_ptr<AggregatableReportSender> sender); @@ -66,7 +72,9 @@ SendCallback callback) override; void ClearData(base::Time delete_begin, base::Time delete_end, + StoragePartition::StorageKeyMatcherFunction filter, base::OnceClosure done) override; + void ScheduleReport(AggregatableReportRequest report_request) override; // AggregationServiceStorageContext: const base::SequenceBound<AggregationServiceStorage>& GetStorage() override; @@ -75,14 +83,29 @@ void SetPublicKeysForTesting(const GURL& url, const PublicKeyset& keyset); private: + // Allows access to `OnScheduledReportTimeReached()`. + friend class AggregationServiceImplTest; + AggregationServiceImpl(bool run_in_memory, const base::FilePath& user_data_directory, const base::Clock* clock, + std::unique_ptr<AggregatableReportScheduler> scheduler, std::unique_ptr<AggregatableReportAssembler> assembler, std::unique_ptr<AggregatableReportSender> sender); - // TODO(crbug.com/1340050): Hook scheduler up to service and sender. + void OnScheduledReportTimeReached( + std::vector<AggregationServiceStorage::RequestAndId> requests_and_ids); + void OnReportAssemblyComplete( + AggregationServiceStorage::RequestId request_id, + GURL reporting_url, + absl::optional<AggregatableReport> report, + AggregatableReportAssembler::AssemblyStatus status); + + void OnReportSendingComplete(AggregationServiceStorage::RequestId request_id, + AggregatableReportSender::RequestStatus status); + + std::unique_ptr<AggregatableReportScheduler> scheduler_; base::SequenceBound<AggregationServiceStorage> storage_; std::unique_ptr<AggregatableReportAssembler> assembler_; std::unique_ptr<AggregatableReportSender> sender_;
diff --git a/content/browser/aggregation_service/aggregation_service_impl_unittest.cc b/content/browser/aggregation_service/aggregation_service_impl_unittest.cc index 0206bda..4db2819 100644 --- a/content/browser/aggregation_service/aggregation_service_impl_unittest.cc +++ b/content/browser/aggregation_service/aggregation_service_impl_unittest.cc
@@ -11,7 +11,10 @@ #include <utility> #include <vector> +#include "base/callback.h" +#include "base/callback_helpers.h" #include "base/containers/contains.h" +#include "base/containers/flat_map.h" #include "base/files/scoped_temp_dir.h" #include "base/memory/raw_ptr.h" #include "base/memory/scoped_refptr.h" @@ -19,6 +22,9 @@ #include "base/time/time.h" #include "content/browser/aggregation_service/aggregatable_report.h" #include "content/browser/aggregation_service/aggregatable_report_assembler.h" +#include "content/browser/aggregation_service/aggregatable_report_scheduler.h" +#include "content/browser/aggregation_service/aggregatable_report_sender.h" +#include "content/browser/aggregation_service/aggregation_service_storage.h" #include "content/browser/aggregation_service/aggregation_service_test_utils.h" #include "content/public/test/browser_task_environment.h" #include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h" @@ -29,6 +35,8 @@ namespace content { +// TODO(alexmt): Consider rewriting these tests using gmock. + class TestAggregatableReportAssembler : public AggregatableReportAssembler { public: explicit TestAggregatableReportAssembler( @@ -82,10 +90,75 @@ std::map<int64_t, ReportSentCallback> callbacks_; }; +class TestAggregatableReportScheduler : public AggregatableReportScheduler { + public: + TestAggregatableReportScheduler( + AggregationServiceStorageContext* storage_context, + base::RepeatingCallback< + void(std::vector<AggregationServiceStorage::RequestAndId>)> + on_scheduled_report_time_reached) + : AggregatableReportScheduler(storage_context, base::DoNothing()), + on_scheduled_report_time_reached_( + std::move(on_scheduled_report_time_reached)) {} + ~TestAggregatableReportScheduler() override = default; + + void ScheduleRequest(AggregatableReportRequest request) override { + scheduled_reports_.emplace(unique_id_counter_++, std::move(request)); + } + + void NotifyInProgressRequestSucceeded( + AggregationServiceStorage::RequestId request_id) override { + completed_requests_status_[request_id] = true; + } + + void NotifyInProgressRequestFailed( + AggregationServiceStorage::RequestId request_id) override { + completed_requests_status_[request_id] = false; + } + + void TriggerReportingTime( + std::vector<AggregationServiceStorage::RequestId> request_ids) { + std::vector<AggregationServiceStorage::RequestAndId> return_value; + for (AggregationServiceStorage::RequestId request_id : request_ids) { + ASSERT_TRUE(base::Contains(scheduled_reports_, request_id)); + return_value.push_back(AggregationServiceStorage::RequestAndId{ + .request = std::move(scheduled_reports_.at(request_id)), + .id = request_id}); + scheduled_reports_.erase(request_id); + } + on_scheduled_report_time_reached_.Run(std::move(return_value)); + } + + // Returns a boolean representing whether the request was successfully + // completed. Returns absl::nullopt if the request has not yet completed. + absl::optional<bool> WasRequestSuccessful( + AggregationServiceStorage::RequestId request_id) { + if (!base::Contains(completed_requests_status_, request_id)) { + return absl::nullopt; + } + return completed_requests_status_[request_id]; + } + + private: + base::RepeatingCallback<void( + std::vector<AggregationServiceStorage::RequestAndId>)> + on_scheduled_report_time_reached_; + int64_t unique_id_counter_ = 1; + base::flat_map<AggregationServiceStorage::RequestId, + AggregatableReportRequest> + scheduled_reports_; + + // Each completed request's ID is the key, with value whether it was completed + // successfully. + base::flat_map<AggregationServiceStorage::RequestId, bool> + completed_requests_status_; +}; + class AggregationServiceImplTest : public testing::Test { public: AggregationServiceImplTest() - : task_environment_(base::test::TaskEnvironment::TimeSource::MOCK_TIME) { + : task_environment_(base::test::TaskEnvironment::TimeSource::MOCK_TIME), + storage_context_(task_environment_.GetMockClock()) { EXPECT_TRUE(dir_.CreateUniqueTempDir()); scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory = @@ -100,10 +173,20 @@ std::make_unique<TestAggregatableReportSender>(url_loader_factory); test_sender_ = sender.get(); + auto scheduler = std::make_unique<TestAggregatableReportScheduler>( + &storage_context_, + base::BindLambdaForTesting( + [this](std::vector<AggregationServiceStorage::RequestAndId> + requests_and_ids) { + service_impl_->OnScheduledReportTimeReached( + std::move(requests_and_ids)); + })); + test_scheduler_ = scheduler.get(); + service_impl_ = AggregationServiceImpl::CreateForTesting( /*run_in_memory=*/true, dir_.GetPath(), - task_environment_.GetMockClock(), std::move(assembler), - std::move(sender)); + task_environment_.GetMockClock(), std::move(scheduler), + std::move(assembler), std::move(sender)); } void AssembleReport(AggregatableReportRequest request) { @@ -124,9 +207,14 @@ })); } + void ScheduleReport(AggregatableReportRequest request) { + service()->ScheduleReport(std::move(request)); + } + AggregationServiceImpl* service() { return service_impl_.get(); } TestAggregatableReportAssembler* assembler() { return test_assembler_; } TestAggregatableReportSender* sender() { return test_sender_; } + TestAggregatableReportScheduler* scheduler() { return test_scheduler_; } // Returns `absl::nullopt` if no report callback has been run or if the last // assembly had an error. @@ -151,8 +239,10 @@ BrowserTaskEnvironment task_environment_; network::TestURLLoaderFactory test_url_loader_factory_; std::unique_ptr<AggregationServiceImpl> service_impl_; + TestAggregationServiceStorageContext storage_context_; raw_ptr<TestAggregatableReportAssembler> test_assembler_ = nullptr; raw_ptr<TestAggregatableReportSender> test_sender_ = nullptr; + raw_ptr<TestAggregatableReportScheduler> test_scheduler_ = nullptr; absl::optional<AggregatableReport> last_assembled_report_; absl::optional<AggregationService::AssemblyStatus> last_assembly_status_; @@ -215,4 +305,152 @@ EXPECT_EQ(last_send_status().value(), AggregationService::SendStatus::kOk); } -} // namespace content \ No newline at end of file +TEST_F(AggregationServiceImplTest, ScheduleReport_Success) { + AggregatableReportRequest request = + aggregation_service::CreateExampleRequest(); + + ScheduleReport(std::move(request)); + + // Request IDs begin at 1. + scheduler()->TriggerReportingTime( + /*request_ids=*/{AggregationServiceStorage::RequestId(1)}); + + std::vector<AggregatableReport::AggregationServicePayload> payloads; + payloads.emplace_back(/*payload=*/kABCD1234AsBytes, + /*key_id=*/"key_1", + /*debug_cleartext_payload=*/absl::nullopt); + AggregatableReport report(std::move(payloads), "example_shared_info"); + + assembler()->TriggerResponse( + /*report_id=*/0, std::move(report), + AggregatableReportAssembler::AssemblyStatus::kOk); + + sender()->TriggerResponse(/*report_id=*/0, + AggregatableReportSender::RequestStatus::kOk); + + ASSERT_TRUE( + scheduler() + ->WasRequestSuccessful(AggregationServiceStorage::RequestId(1)) + .has_value()); + EXPECT_TRUE( + scheduler() + ->WasRequestSuccessful(AggregationServiceStorage::RequestId(1)) + .value()); +} + +TEST_F(AggregationServiceImplTest, ScheduleReport_FailedAssembly) { + AggregatableReportRequest request = + aggregation_service::CreateExampleRequest(); + + ScheduleReport(std::move(request)); + + // Request IDs begin at 1. + scheduler()->TriggerReportingTime( + /*request_ids=*/{AggregationServiceStorage::RequestId(1)}); + + std::vector<AggregatableReport::AggregationServicePayload> payloads; + payloads.emplace_back(/*payload=*/kABCD1234AsBytes, + /*key_id=*/"key_1", + /*debug_cleartext_payload=*/absl::nullopt); + AggregatableReport report(std::move(payloads), "example_shared_info"); + + assembler()->TriggerResponse( + /*report_id=*/0, absl::nullopt, + AggregatableReportAssembler::AssemblyStatus::kAssemblyFailed); + + ASSERT_TRUE( + scheduler() + ->WasRequestSuccessful(AggregationServiceStorage::RequestId(1)) + .has_value()); + EXPECT_FALSE( + scheduler() + ->WasRequestSuccessful(AggregationServiceStorage::RequestId(1)) + .value()); +} + +TEST_F(AggregationServiceImplTest, ScheduleReport_FailedSending) { + AggregatableReportRequest request = + aggregation_service::CreateExampleRequest(); + + ScheduleReport(std::move(request)); + + scheduler()->TriggerReportingTime( + /*request_ids=*/{AggregationServiceStorage::RequestId(1)}); + + std::vector<AggregatableReport::AggregationServicePayload> payloads; + payloads.emplace_back(/*payload=*/kABCD1234AsBytes, + /*key_id=*/"key_1", + /*debug_cleartext_payload=*/absl::nullopt); + AggregatableReport report(std::move(payloads), "example_shared_info"); + + assembler()->TriggerResponse( + /*report_id=*/0, std::move(report), + AggregatableReportAssembler::AssemblyStatus::kOk); + + sender()->TriggerResponse( + /*report_id=*/0, AggregatableReportSender::RequestStatus::kNetworkError); + + ASSERT_TRUE( + scheduler() + ->WasRequestSuccessful(AggregationServiceStorage::RequestId(1)) + .has_value()); + EXPECT_FALSE( + scheduler() + ->WasRequestSuccessful(AggregationServiceStorage::RequestId(1)) + .value()); +} + +TEST_F(AggregationServiceImplTest, + MultipleReportsReturnedFromScheduler_Success) { + AggregatableReportRequest request_1 = + aggregation_service::CreateExampleRequest(); + AggregatableReportRequest request_2 = + aggregation_service::CreateExampleRequest(); + + ScheduleReport(std::move(request_1)); + ScheduleReport(std::move(request_2)); + + // Request IDs begin at 1. + scheduler()->TriggerReportingTime( + /*request_ids=*/{AggregationServiceStorage::RequestId(1), + AggregationServiceStorage::RequestId(2)}); + + std::vector<AggregatableReport::AggregationServicePayload> payloads; + payloads.emplace_back(/*payload=*/kABCD1234AsBytes, + /*key_id=*/"key_1", + /*debug_cleartext_payload=*/absl::nullopt); + AggregatableReport report_1(payloads, "example_shared_info"); + AggregatableReport report_2(payloads, "example_shared_info"); + + assembler()->TriggerResponse( + /*report_id=*/0, std::move(report_1), + AggregatableReportAssembler::AssemblyStatus::kOk); + assembler()->TriggerResponse( + /*report_id=*/1, std::move(report_2), + AggregatableReportAssembler::AssemblyStatus::kOk); + + sender()->TriggerResponse(/*report_id=*/0, + AggregatableReportSender::RequestStatus::kOk); + sender()->TriggerResponse(/*report_id=*/1, + AggregatableReportSender::RequestStatus::kOk); + + ASSERT_TRUE( + scheduler() + ->WasRequestSuccessful(AggregationServiceStorage::RequestId(1)) + .has_value()); + EXPECT_TRUE( + scheduler() + ->WasRequestSuccessful(AggregationServiceStorage::RequestId(1)) + .value()); + + ASSERT_TRUE( + scheduler() + ->WasRequestSuccessful(AggregationServiceStorage::RequestId(2)) + .has_value()); + EXPECT_TRUE( + scheduler() + ->WasRequestSuccessful(AggregationServiceStorage::RequestId(2)) + .value()); +} + +} // namespace content
diff --git a/content/browser/aggregation_service/aggregation_service_storage.h b/content/browser/aggregation_service/aggregation_service_storage.h index 88fc140..b01a244 100644 --- a/content/browser/aggregation_service/aggregation_service_storage.h +++ b/content/browser/aggregation_service/aggregation_service_storage.h
@@ -11,6 +11,7 @@ #include "base/types/strong_alias.h" #include "content/browser/aggregation_service/aggregatable_report.h" +#include "content/public/browser/storage_partition.h" #include "third_party/abseil-cpp/absl/types/optional.h" class GURL; @@ -50,12 +51,6 @@ // Clears the stored public keys for `url`. virtual void ClearPublicKeys(const GURL& url) = 0; - // Clears the stored public keys that were fetched between `delete_begin` and - // `delete_end` time (inclusive). Null times are treated as unbounded lower or - // upper range. - virtual void ClearPublicKeysFetchedBetween(base::Time delete_begin, - base::Time delete_end) = 0; - // Clears the stored public keys that expire no later than `delete_end` // (inclusive). virtual void ClearPublicKeysExpiredBy(base::Time delete_end) = 0; @@ -84,6 +79,19 @@ // TODO(crbug.com/1340042): Add a method to randomly delay all reports in the // past (for startup and coming online). + + // == Joint methods ===== + + // Clears the stored public keys that were fetched between and the report + // requests that were stored between `delete_begin` and `delete_end` time + // (inclusive). Null times are treated as unbounded lower or upper range. If + // `!filter.is_null()`, requests with a reporting origin that does *not* match + // the `filter` are retained (i.e. not cleared); `filter` does not affect + // public key deletion. + virtual void ClearDataBetween( + base::Time delete_begin, + base::Time delete_end, + StoragePartition::StorageKeyMatcherFunction filter) = 0; }; } // namespace content
diff --git a/content/browser/aggregation_service/aggregation_service_storage_sql.cc b/content/browser/aggregation_service/aggregation_service_storage_sql.cc index 70c64e8..fc63592 100644 --- a/content/browser/aggregation_service/aggregation_service_storage_sql.cc +++ b/content/browser/aggregation_service/aggregation_service_storage_sql.cc
@@ -26,6 +26,7 @@ #include "content/browser/aggregation_service/aggregation_service_storage.h" #include "content/browser/aggregation_service/proto/aggregatable_report.pb.h" #include "content/browser/aggregation_service/public_key.h" +#include "content/public/browser/storage_partition.h" #include "services/network/public/cpp/is_potentially_trustworthy.h" #include "sql/database.h" #include "sql/meta_table.h" @@ -33,6 +34,7 @@ #include "sql/transaction.h" #include "third_party/abseil-cpp/absl/numeric/int128.h" #include "third_party/abseil-cpp/absl/types/optional.h" +#include "third_party/blink/public/common/storage_key/storage_key.h" #include "url/gurl.h" #include "url/origin.h" @@ -248,23 +250,9 @@ void AggregationServiceStorageSql::ClearPublicKeysFetchedBetween( base::Time delete_begin, base::Time delete_end) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - - if (!EnsureDatabaseOpen(DbCreationPolicy::kFailIfAbsent)) - return; - - // Treat null times as unbounded lower or upper range. This is used by - // browsing data remover. - if (delete_begin.is_null()) - delete_begin = base::Time::Min(); - - if (delete_end.is_null()) - delete_end = base::Time::Max(); - - if (delete_begin.is_min() && delete_end.is_max()) { - ClearAllPublicKeys(); - return; - } + DCHECK(!delete_begin.is_null()); + DCHECK(!delete_end.is_null()); + DCHECK(!delete_begin.is_min() || !delete_end.is_max()); sql::Transaction transaction(&db_); if (!transaction.Begin()) @@ -456,6 +444,10 @@ if (!EnsureDatabaseOpen(DbCreationPolicy::kFailIfAbsent)) return; + DeleteRequestImpl(request_id); +} + +bool AggregationServiceStorageSql::DeleteRequestImpl(RequestId request_id) { static constexpr char kDeleteRequestSql[] = "DELETE FROM report_requests WHERE request_id=?"; @@ -464,7 +456,7 @@ delete_request_statement.BindInt64(0, request_id.value()); - delete_request_statement.Run(); + return delete_request_statement.Run(); } absl::optional<base::Time> AggregationServiceStorageSql::NextReportTimeAfter( @@ -528,6 +520,82 @@ return result; } +void AggregationServiceStorageSql::ClearDataBetween( + base::Time delete_begin, + base::Time delete_end, + StoragePartition::StorageKeyMatcherFunction filter) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + if (!EnsureDatabaseOpen(DbCreationPolicy::kFailIfAbsent)) + return; + + // Treat null times as unbounded lower or upper range. This is used by + // browsing data remover. + if (delete_begin.is_null()) + delete_begin = base::Time::Min(); + + if (delete_end.is_null()) + delete_end = base::Time::Max(); + + if (delete_begin.is_min() && delete_end.is_max()) { + ClearAllPublicKeys(); + + if (filter.is_null()) { + ClearAllRequests(); + return; + } + } else { + ClearPublicKeysFetchedBetween(delete_begin, delete_end); + } + + ClearRequestsStoredBetween(delete_begin, delete_end, filter); +} + +void AggregationServiceStorageSql::ClearRequestsStoredBetween( + base::Time delete_begin, + base::Time delete_end, + StoragePartition::StorageKeyMatcherFunction filter) { + DCHECK(!delete_begin.is_null()); + DCHECK(!delete_end.is_null()); + DCHECK(!delete_begin.is_min() || !delete_end.is_max() || !filter.is_null()); + + sql::Transaction transaction(&db_); + if (!transaction.Begin()) + return; + + static constexpr char kSelectRequestsToDeleteSql[] = + "SELECT request_id,reporting_origin FROM report_requests " + "WHERE creation_time BETWEEN ? AND ?"; + sql::Statement select_requests_to_delete_statement( + db_.GetCachedStatement(SQL_FROM_HERE, kSelectRequestsToDeleteSql)); + select_requests_to_delete_statement.BindTime(0, delete_begin); + select_requests_to_delete_statement.BindTime(1, delete_end); + + while (select_requests_to_delete_statement.Step()) { + url::Origin reporting_origin = url::Origin::Create( + GURL(select_requests_to_delete_statement.ColumnString(1))); + if (filter.is_null() || filter.Run(blink::StorageKey(reporting_origin))) { + if (!DeleteRequestImpl( + RequestId(select_requests_to_delete_statement.ColumnInt64(0)))) { + return; + } + } + } + + if (!select_requests_to_delete_statement.Succeeded()) + return; + + transaction.Commit(); +} + +void AggregationServiceStorageSql::ClearAllRequests() { + static constexpr char kClearAllRequests[] = "DELETE FROM report_requests"; + sql::Statement select_requests_to_delete_statement( + db_.GetCachedStatement(SQL_FROM_HERE, kClearAllRequests)); + + select_requests_to_delete_statement.Run(); +} + void AggregationServiceStorageSql::HandleInitializationFailure( const InitStatus status) { RecordInitializationStatus(status);
diff --git a/content/browser/aggregation_service/aggregation_service_storage_sql.h b/content/browser/aggregation_service/aggregation_service_storage_sql.h index 9eb10e8..ede193c3 100644 --- a/content/browser/aggregation_service/aggregation_service_storage_sql.h +++ b/content/browser/aggregation_service/aggregation_service_storage_sql.h
@@ -14,6 +14,7 @@ #include "base/thread_annotations.h" #include "content/browser/aggregation_service/aggregation_service_storage.h" #include "content/common/content_export.h" +#include "content/public/browser/storage_partition.h" #include "sql/database.h" #include "sql/meta_table.h" #include "third_party/abseil-cpp/absl/types/optional.h" @@ -57,8 +58,6 @@ std::vector<PublicKey> GetPublicKeys(const GURL& url) override; void SetPublicKeys(const GURL& url, const PublicKeyset& keyset) override; void ClearPublicKeys(const GURL& url) override; - void ClearPublicKeysFetchedBetween(base::Time delete_begin, - base::Time delete_end) override; void ClearPublicKeysExpiredBy(base::Time delete_end) override; void StoreRequest(AggregatableReportRequest request) override; void DeleteRequest(AggregationServiceStorage::RequestId request_id) override; @@ -66,6 +65,10 @@ base::Time strictly_after_time) override; std::vector<AggregationServiceStorage::RequestAndId> GetRequestsReportingOnOrBefore(base::Time not_after_time) override; + void ClearDataBetween( + base::Time delete_begin, + base::Time delete_end, + StoragePartition::StorageKeyMatcherFunction filter) override; void set_ignore_errors_for_testing(bool ignore_for_testing) VALID_CONTEXT_REQUIRED(sequence_checker_) { @@ -115,9 +118,33 @@ bool ClearPublicKeysByUrlId(int64_t url_id) VALID_CONTEXT_REQUIRED(sequence_checker_); + // Clears the stored public keys that were fetched between `delete_begin` and + // `delete_end` time (inclusive). Null times are treated as unbounded lower or + // upper range. + void ClearPublicKeysFetchedBetween(base::Time delete_begin, + base::Time delete_end) + VALID_CONTEXT_REQUIRED(sequence_checker_); + // Clears all stored public keys. void ClearAllPublicKeys() VALID_CONTEXT_REQUIRED(sequence_checker_); + // Deletes the stored request with the given report ID. + bool DeleteRequestImpl(RequestId request_id) + VALID_CONTEXT_REQUIRED(sequence_checker_); + + // Clears the report requests that were stored between `delete_begin` and + // `delete_end` time (inclusive). Null times are treated as unbounded lower or + // upper range. If `!filter.is_null()`, only requests with reporting origins + // matching the `filter` are cleared. + void ClearRequestsStoredBetween( + base::Time delete_begin, + base::Time delete_end, + StoragePartition::StorageKeyMatcherFunction filter) + VALID_CONTEXT_REQUIRED(sequence_checker_); + + // Clears all stored report requests; + void ClearAllRequests() VALID_CONTEXT_REQUIRED(sequence_checker_); + // Initializes the database if necessary, and returns whether the database is // open. `creation_policy` indicates whether the database should be created if // it is not already.
diff --git a/content/browser/aggregation_service/aggregation_service_storage_sql_unittest.cc b/content/browser/aggregation_service/aggregation_service_storage_sql_unittest.cc index 7a65a47..c7d091cf 100644 --- a/content/browser/aggregation_service/aggregation_service_storage_sql_unittest.cc +++ b/content/browser/aggregation_service/aggregation_service_storage_sql_unittest.cc
@@ -8,10 +8,12 @@ #include <utility> #include <vector> +#include "base/callback_helpers.h" #include "base/containers/flat_set.h" #include "base/files/file_path.h" #include "base/files/file_util.h" #include "base/files/scoped_temp_dir.h" +#include "base/test/bind.h" #include "base/test/metrics/histogram_tester.h" #include "base/test/simple_test_clock.h" #include "base/time/time.h" @@ -24,6 +26,7 @@ #include "sql/test/test_helpers.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/abseil-cpp/absl/types/optional.h" +#include "third_party/blink/public/common/storage_key/storage_key.h" #include "url/gurl.h" namespace content { @@ -248,7 +251,7 @@ } TEST_F(AggregationServiceStorageSqlTest, - ClearPublicKeysFetchedBetween_RangeDeleted) { + ClearDataBetween_PublicKeyRangeDeleted) { OpenDatabase(); GURL url_1("https://a.com/keys"); @@ -275,15 +278,19 @@ keys_2, storage_->GetPublicKeys(url_2))); base::Time now = clock_.Now(); - storage_->ClearPublicKeysFetchedBetween(now - base::Days(5), - now - base::Days(1)); + storage_->ClearDataBetween( + now - base::Days(5), now - base::Days(1), + // The filter should be ignored. + base::BindLambdaForTesting( + [](const blink::StorageKey& storage_key) { return false; })); EXPECT_TRUE(storage_->GetPublicKeys(url_1).empty()); EXPECT_TRUE(aggregation_service::PublicKeysEqual( keys_2, storage_->GetPublicKeys(url_2))); } -TEST_F(AggregationServiceStorageSqlTest, ClearAllPublicKeys_AllDeleted) { +TEST_F(AggregationServiceStorageSqlTest, + ClearAllDataWithFilter_PublicKeysAllDeleted) { OpenDatabase(); GURL url_1("https://a.com/keys"); @@ -309,7 +316,45 @@ EXPECT_TRUE(aggregation_service::PublicKeysEqual( keys_2, storage_->GetPublicKeys(url_2))); - storage_->ClearPublicKeysFetchedBetween(base::Time(), base::Time::Max()); + storage_->ClearDataBetween( + base::Time(), base::Time::Max(), + // The filter should be ignored. + base::BindLambdaForTesting( + [](const blink::StorageKey& storage_key) { return false; })); + + EXPECT_TRUE(storage_->GetPublicKeys(url_1).empty()); + EXPECT_TRUE(storage_->GetPublicKeys(url_2).empty()); +} + +TEST_F(AggregationServiceStorageSqlTest, + ClearAllDataWithoutFilter_AllPublicKeysDeleted) { + OpenDatabase(); + + GURL url_1("https://a.com/keys"); + std::vector<PublicKey> keys_1{ + aggregation_service::GenerateKey("abcd").public_key, + aggregation_service::GenerateKey("bcde").public_key}; + storage_->SetPublicKeys(url_1, + PublicKeyset(keys_1, /*fetch_time=*/clock_.Now(), + /*expiry_time=*/base::Time::Max())); + + clock_.Advance(base::Days(1)); + + GURL url_2("https://b.com/keys"); + std::vector<PublicKey> keys_2{ + aggregation_service::GenerateKey("abcd").public_key, + aggregation_service::GenerateKey("efgh").public_key}; + storage_->SetPublicKeys(url_2, + PublicKeyset(keys_2, /*fetch_time=*/clock_.Now(), + /*expiry_time=*/base::Time::Max())); + + EXPECT_TRUE(aggregation_service::PublicKeysEqual( + keys_1, storage_->GetPublicKeys(url_1))); + EXPECT_TRUE(aggregation_service::PublicKeysEqual( + keys_2, storage_->GetPublicKeys(url_2))); + + storage_->ClearDataBetween(base::Time(), base::Time::Max(), + base::NullCallback()); EXPECT_TRUE(storage_->GetPublicKeys(url_1).empty()); EXPECT_TRUE(storage_->GetPublicKeys(url_2).empty()); @@ -610,6 +655,91 @@ 3u); } +TEST_F(AggregationServiceStorageSqlTest, + ClearAllDataWithoutFilter_AllRequestsDeleted) { + OpenDatabase(); + + storage_->StoreRequest(aggregation_service::CreateExampleRequest()); + storage_->StoreRequest(aggregation_service::CreateExampleRequest()); + + EXPECT_EQ(storage_->GetRequestsReportingOnOrBefore(base::Time::Max()).size(), + 2u); + + storage_->ClearDataBetween(base::Time(), base::Time(), base::NullCallback()); + + EXPECT_EQ(storage_->GetRequestsReportingOnOrBefore(base::Time::Max()).size(), + 0u); +} + +TEST_F(AggregationServiceStorageSqlTest, + ClearDataBetween_RequestsTimeRangeDeleted) { + OpenDatabase(); + + const base::Time kExampleTime = base::Time::FromJavaTime(1652984901234); + + clock_.SetNow(kExampleTime); + storage_->StoreRequest(aggregation_service::CreateExampleRequest()); + + clock_.Advance(base::Hours(1)); + storage_->StoreRequest(aggregation_service::CreateExampleRequest()); + + clock_.Advance(base::Hours(1)); + storage_->StoreRequest(aggregation_service::CreateExampleRequest()); + + EXPECT_EQ(storage_->GetRequestsReportingOnOrBefore(base::Time::Max()).size(), + 3u); + + // As the times are inclusive, this should delete the first two requests. + storage_->ClearDataBetween(kExampleTime, kExampleTime + base::Hours(1), + base::NullCallback()); + + std::vector<AggregationServiceStorage::RequestAndId> stored_reports = + storage_->GetRequestsReportingOnOrBefore(base::Time::Max()); + ASSERT_EQ(stored_reports.size(), 1u); + + // Only the last request should be left. Request IDs start from 1. + EXPECT_EQ(stored_reports[0].id, AggregationServiceStorage::RequestId(3)); +} + +TEST_F(AggregationServiceStorageSqlTest, + ClearDataAllTimesWithFilter_OnlyRequestsSpecifiedAreDeleted) { + const url::Origin reporting_origins[] = { + url::Origin::Create(GURL("https://a.example")), + url::Origin::Create(GURL("https://b.example")), + url::Origin::Create(GURL("https://c.example"))}; + + OpenDatabase(); + + for (const url::Origin& reporting_origin : reporting_origins) { + AggregatableReportRequest example_request = + aggregation_service::CreateExampleRequest(); + AggregatableReportSharedInfo shared_info = + example_request.shared_info().Clone(); + shared_info.reporting_origin = reporting_origin; + storage_->StoreRequest( + AggregatableReportRequest::Create(example_request.payload_contents(), + std::move(shared_info)) + .value()); + } + + EXPECT_EQ(storage_->GetRequestsReportingOnOrBefore(base::Time::Max()).size(), + 3u); + + storage_->ClearDataBetween( + base::Time::Min(), base::Time::Max(), + base::BindLambdaForTesting( + [&reporting_origins](const blink::StorageKey& storage_key) { + return storage_key != blink::StorageKey(reporting_origins[2]); + })); + + std::vector<AggregationServiceStorage::RequestAndId> stored_reports = + storage_->GetRequestsReportingOnOrBefore(base::Time::Max()); + ASSERT_EQ(stored_reports.size(), 1u); + + // Only the last request should be left. Request IDs start from 1. + EXPECT_EQ(stored_reports[0].id, AggregationServiceStorage::RequestId(3)); +} + TEST_F(AggregationServiceStorageSqlInMemoryTest, DatabaseInMemoryReopened_RequestsNotPersisted) { OpenDatabase();
diff --git a/content/browser/back_forward_cache_features_browsertest.cc b/content/browser/back_forward_cache_features_browsertest.cc index c3c3bae..9eaefee 100644 --- a/content/browser/back_forward_cache_features_browsertest.cc +++ b/content/browser/back_forward_cache_features_browsertest.cc
@@ -3535,7 +3535,7 @@ // alpha=1. While on the b-page it captures 3 more events. If the a-page is // still receiving events it should receive one or more of these. Finally it // resets the reasing back to have alpha=0 and navigates back to the a-page and -// catpures 3 more events and verifies that all events on the a-page have +// captures 3 more events and verifies that all events on the a-page have // alpha=1. IN_PROC_BROWSER_TEST_F(SensorBackForwardCacheBrowserTest, SensorPausedWhileCached) {
diff --git a/content/browser/devtools/protocol/storage_handler.cc b/content/browser/devtools/protocol/storage_handler.cc index 32a6df4..466ea42 100644 --- a/content/browser/devtools/protocol/storage_handler.cc +++ b/content/browser/devtools/protocol/storage_handler.cc
@@ -378,13 +378,8 @@ return Response::Success(); } -void StorageHandler::ClearDataForOrigin( - const std::string& origin, - const std::string& storage_types, - std::unique_ptr<ClearDataForOriginCallback> callback) { - if (!storage_partition_) - return callback->sendFailure(Response::InternalError()); - +namespace { +uint32_t GetRemoveDataMask(const std::string& storage_types) { std::vector<std::string> types = base::SplitString( storage_types, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY); std::unordered_set<std::string> set(types.begin(), types.end()); @@ -409,6 +404,18 @@ remove_mask |= StoragePartition::REMOVE_DATA_MASK_INTEREST_GROUPS; if (set.count(Storage::StorageTypeEnum::All)) remove_mask |= StoragePartition::REMOVE_DATA_MASK_ALL; + return remove_mask; +} +} // namespace + +void StorageHandler::ClearDataForOrigin( + const std::string& origin, + const std::string& storage_types, + std::unique_ptr<ClearDataForOriginCallback> callback) { + if (!storage_partition_) + return callback->sendFailure(Response::InternalError()); + + uint32_t remove_mask = GetRemoveDataMask(storage_types); if (!remove_mask) { return callback->sendFailure( @@ -423,6 +430,29 @@ std::move(callback))); } +void StorageHandler::ClearDataForStorageKey( + const std::string& storage_key, + const std::string& storage_types, + std::unique_ptr<ClearDataForStorageKeyCallback> callback) { + if (!storage_partition_) + return callback->sendFailure(Response::InternalError()); + + uint32_t remove_mask = GetRemoveDataMask(storage_types); + + if (!remove_mask) { + return callback->sendFailure( + Response::InvalidParams("No valid storage type specified")); + } + + absl::optional<blink::StorageKey> key = + blink::StorageKey::Deserialize(storage_key); + storage_partition_->ClearData( + remove_mask, StoragePartition::QUOTA_MANAGED_STORAGE_MASK_ALL, *key, + base::Time(), base::Time::Max(), + base::BindOnce(&ClearDataForStorageKeyCallback::sendSuccess, + std::move(callback))); +} + void StorageHandler::GetUsageAndQuota( const String& origin_string, std::unique_ptr<GetUsageAndQuotaCallback> callback) {
diff --git a/content/browser/devtools/protocol/storage_handler.h b/content/browser/devtools/protocol/storage_handler.h index c54feda..203bbb55 100644 --- a/content/browser/devtools/protocol/storage_handler.h +++ b/content/browser/devtools/protocol/storage_handler.h
@@ -49,6 +49,10 @@ const std::string& origin, const std::string& storage_types, std::unique_ptr<ClearDataForOriginCallback> callback) override; + void ClearDataForStorageKey( + const std::string& storage_key, + const std::string& storage_types, + std::unique_ptr<ClearDataForStorageKeyCallback> callback) override; void GetUsageAndQuota( const String& origin, std::unique_ptr<GetUsageAndQuotaCallback> callback) override;
diff --git a/content/browser/devtools/protocol_config.json b/content/browser/devtools/protocol_config.json index c2d36723..9c0af74 100644 --- a/content/browser/devtools/protocol_config.json +++ b/content/browser/devtools/protocol_config.json
@@ -91,7 +91,7 @@ }, { "domain": "Storage", - "async": ["getUsageAndQuota", "clearDataForOrigin", "getCookies", "setCookies", "clearCookies", "overrideQuotaForOrigin", "getTrustTokens", "clearTrustTokens", "getInterestGroupDetails"] + "async": ["getUsageAndQuota", "clearDataForOrigin", "clearDataForStorageKey", "getCookies", "setCookies", "clearCookies", "overrideQuotaForOrigin", "getTrustTokens", "clearTrustTokens", "getInterestGroupDetails"] }, { "domain": "SystemInfo",
diff --git a/content/browser/first_party_sets/database/first_party_sets_database.cc b/content/browser/first_party_sets/database/first_party_sets_database.cc index 18a9034..ed0c4c3 100644 --- a/content/browser/first_party_sets/database/first_party_sets_database.cc +++ b/content/browser/first_party_sets/database/first_party_sets_database.cc
@@ -48,18 +48,18 @@ if (!db.Execute(kMarkedAtRunSitesSql)) return false; - static constexpr char kProfilesClearedSql[] = - "CREATE TABLE IF NOT EXISTS profiles_cleared(" - "profile TEXT PRIMARY KEY NOT NULL," + static constexpr char kBrowserContextsClearedSql[] = + "CREATE TABLE IF NOT EXISTS browser_contexts_cleared(" + "browser_context_id TEXT PRIMARY KEY NOT NULL," "cleared_at_run INTEGER NOT NULL" ")WITHOUT ROWID"; - if (!db.Execute(kProfilesClearedSql)) + if (!db.Execute(kBrowserContextsClearedSql)) return false; - static constexpr char kClearedAtRunProfilesSql[] = - "CREATE INDEX IF NOT EXISTS idx_cleared_at_run_profiles " - "ON profiles_cleared(cleared_at_run)"; - if (!db.Execute(kClearedAtRunProfilesSql)) + static constexpr char kClearedAtRunBrowserContextsSql[] = + "CREATE INDEX IF NOT EXISTS idx_cleared_at_run_browser_contexts " + "ON browser_contexts_cleared(cleared_at_run)"; + if (!db.Execute(kClearedAtRunBrowserContextsSql)) return false; return true; @@ -94,8 +94,10 @@ for (const auto& site : sites) { DCHECK(!site.opaque()); static constexpr char kInsertSitesToClearSql[] = + // clang-format off "INSERT OR REPLACE INTO sites_to_clear(site,marked_at_run) " "VALUES(?,?)"; + // clang-format on sql::Statement statement( db_->GetCachedStatement(SQL_FROM_HERE, kInsertSitesToClearSql)); statement.BindString(0, site.Serialize()); @@ -107,34 +109,37 @@ return transaction.Commit(); } -bool FirstPartySetsDatabase::InsertProfileCleared(const std::string& profile) { +bool FirstPartySetsDatabase::InsertBrowserContextCleared( + const std::string& browser_context_id) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - DCHECK(!profile.empty()); + DCHECK(!browser_context_id.empty()); if (!LazyInit()) return false; - static constexpr char kInsertProfilesClearedSql[] = - "INSERT OR REPLACE INTO profiles_cleared(profile,cleared_at_run) " + static constexpr char kInsertBrowserContextsClearedSql[] = + // clang-format off + "INSERT OR REPLACE INTO browser_contexts_cleared(browser_context_id,cleared_at_run) " "VALUES(?,?)"; + // clang-format on sql::Statement statement( - db_->GetCachedStatement(SQL_FROM_HERE, kInsertProfilesClearedSql)); - statement.BindString(0, profile); + db_->GetCachedStatement(SQL_FROM_HERE, kInsertBrowserContextsClearedSql)); + statement.BindString(0, browser_context_id); statement.BindInt64(1, run_count_); return statement.Run(); } std::vector<net::SchemefulSite> FirstPartySetsDatabase::FetchSitesToClear( - const std::string& profile) { + const std::string& browser_context_id) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - DCHECK(!profile.empty()); + DCHECK(!browser_context_id.empty()); if (!LazyInit()) return {}; - // No-op if the `profile` does not exist before. - if (!HasEntryFor(profile)) + // No-op if the `browser_context_id` does not exist before. + if (!HasEntryFor(browser_context_id)) return {}; std::vector<net::SchemefulSite> results; @@ -142,13 +147,13 @@ // clang-format off "SELECT site FROM sites_to_clear " "WHERE marked_at_run>" - "(SELECT cleared_at_run FROM profiles_cleared " - "WHERE profile=?)"; + "(SELECT cleared_at_run FROM browser_contexts_cleared " + "WHERE browser_context_id=?)"; // clang-format on sql::Statement statement( db_->GetCachedStatement(SQL_FROM_HERE, kSelectSitesToClearSql)); - statement.BindString(0, profile); + statement.BindString(0, browser_context_id); while (statement.Step()) { absl::optional<net::SchemefulSite> site = @@ -299,18 +304,19 @@ } } -bool FirstPartySetsDatabase::HasEntryFor(const std::string& profile) const { +bool FirstPartySetsDatabase::HasEntryFor( + const std::string& browser_context_id) const { DCHECK_EQ(db_status_, InitStatus::kSuccess); - DCHECK(!profile.empty()); + DCHECK(!browser_context_id.empty()); - static constexpr char kSelectProfileSql[] = - "SELECT 1 FROM profiles_cleared " - "WHERE profile=?" + static constexpr char kSelectBrowserContextSql[] = + "SELECT 1 FROM browser_contexts_cleared " + "WHERE browser_context_id=?" "LIMIT 1"; sql::Statement statement( - db_->GetCachedStatement(SQL_FROM_HERE, kSelectProfileSql)); - statement.BindString(0, profile); + db_->GetCachedStatement(SQL_FROM_HERE, kSelectBrowserContextSql)); + statement.BindString(0, browser_context_id); return statement.Step(); }
diff --git a/content/browser/first_party_sets/database/first_party_sets_database.h b/content/browser/first_party_sets/database/first_party_sets_database.h index a7f4aa54..a56dc2b 100644 --- a/content/browser/first_party_sets/database/first_party_sets_database.h +++ b/content/browser/first_party_sets/database/first_party_sets_database.h
@@ -60,18 +60,20 @@ FirstPartySetsDatabase& operator=(const FirstPartySetsDatabase&&) = delete; ~FirstPartySetsDatabase(); - // Stores the `sites` to be cleared into database, and returns true on + // Stores the `sites` into sites_to_clear table, and returns true on // success. [[nodiscard]] bool InsertSitesToClear( const std::vector<net::SchemefulSite>& sites); - // Stores the `profile` into database, and returns true on success. - [[nodiscard]] bool InsertProfileCleared(const std::string& profile); + // Stores the `browser_context_id` that has performed clearing into + // browser_contexts_cleared table, and returns true on success. + [[nodiscard]] bool InsertBrowserContextCleared( + const std::string& browser_context_id); - // Gets the list of sites to clear for `profile`. Returns an empty vector if - // `profile` does not exist in the database before. + // Gets the list of sites to clear for the `browser_context_id`. Returns an + // empty vector if `browser_context_id` does not exist in the database before. [[nodiscard]] std::vector<net::SchemefulSite> FetchSitesToClear( - const std::string& profile); + const std::string& browser_context_id); private: // Called at the start of each public operation, and initializes the database @@ -95,8 +97,8 @@ // never be negative. void IncreaseRunCount() VALID_CONTEXT_REQUIRED(sequence_checker_); - // Returns whether an entry exists for `profile`. - [[nodiscard]] bool HasEntryFor(const std::string& profile) const + // Returns whether an entry exists for the `browser_context_id`. + [[nodiscard]] bool HasEntryFor(const std::string& browser_context_id) const VALID_CONTEXT_REQUIRED(sequence_checker_); // Deletes the database and returns whether the operation was successful.
diff --git a/content/browser/first_party_sets/database/first_party_sets_database_unittest.cc b/content/browser/first_party_sets/database/first_party_sets_database_unittest.cc index f91923f..50b95e20e 100644 --- a/content/browser/first_party_sets/database/first_party_sets_database_unittest.cc +++ b/content/browser/first_party_sets/database/first_party_sets_database_unittest.cc
@@ -66,9 +66,10 @@ return size; } - size_t CountProfilesClearedEntries(sql::Database* db) { + size_t CountBrowserContextsClearedEntries(sql::Database* db) { size_t size = 0; - EXPECT_TRUE(sql::test::CountTableRows(db, "profiles_cleared", &size)); + EXPECT_TRUE( + sql::test::CountTableRows(db, "browser_contexts_cleared", &size)); return size; } @@ -105,18 +106,18 @@ // Create a db handle to the existing db file to verify schemas. sql::Database db; EXPECT_TRUE(db.Open(db_path())); - // [sites_to_clear], [profiles_cleared], and [meta]. + // [sites_to_clear], [browser_contexts_cleared], and [meta]. EXPECT_EQ(3u, sql::test::CountSQLTables(&db)); EXPECT_EQ(1, VersionFromMetaTable(db)); - // [idx_marked_at_run_sites], [idx_cleared_at_run_profiles], and + // [idx_marked_at_run_sites], [idx_cleared_at_run_browser_contexts], and // [sqlite_autoindex_meta_1]. EXPECT_EQ(3u, sql::test::CountSQLIndices(&db)); // `site`, `marked_at_run`. EXPECT_EQ(2u, sql::test::CountTableColumns(&db, "sites_to_clear")); - // `profile`, `cleared_at_run`. - EXPECT_EQ(2u, sql::test::CountTableColumns(&db, "profiles_cleared")); + // `browser_context_id`, `cleared_at_run`. + EXPECT_EQ(2u, sql::test::CountTableColumns(&db, "browser_contexts_cleared")); EXPECT_EQ(0u, CountSitesToClearEntries(&db)); - EXPECT_EQ(0u, CountProfilesClearedEntries(&db)); + EXPECT_EQ(0u, CountBrowserContextsClearedEntries(&db)); } TEST_F(FirstPartySetsDatabaseTest, LoadDBFile_CurrentVersion_Success) { @@ -134,7 +135,7 @@ EXPECT_EQ(3u, sql::test::CountSQLTables(&db)); EXPECT_EQ(1, VersionFromMetaTable(db)); EXPECT_EQ(1u, CountSitesToClearEntries(&db)); - EXPECT_EQ(1u, CountProfilesClearedEntries(&db)); + EXPECT_EQ(1u, CountBrowserContextsClearedEntries(&db)); histograms.ExpectUniqueSample("FirstPartySets.Database.InitStatus", FirstPartySetsDatabase::InitStatus::kSuccess, @@ -158,7 +159,7 @@ EXPECT_EQ(3u, sql::test::CountSQLTables(&db)); EXPECT_EQ(0, VersionFromMetaTable(db)); EXPECT_EQ(1u, CountSitesToClearEntries(&db)); - EXPECT_EQ(1u, CountProfilesClearedEntries(&db)); + EXPECT_EQ(1u, CountBrowserContextsClearedEntries(&db)); histograms.ExpectUniqueSample("FirstPartySets.Database.InitStatus", FirstPartySetsDatabase::InitStatus::kTooOld, 1); @@ -181,7 +182,7 @@ EXPECT_EQ(3u, sql::test::CountSQLTables(&db)); EXPECT_EQ(2, VersionFromMetaTable(db)); EXPECT_EQ(1u, CountSitesToClearEntries(&db)); - EXPECT_EQ(1u, CountProfilesClearedEntries(&db)); + EXPECT_EQ(1u, CountBrowserContextsClearedEntries(&db)); histograms.ExpectUniqueSample("FirstPartySets.Database.InitStatus", FirstPartySetsDatabase::InitStatus::kTooNew, 1); @@ -290,29 +291,30 @@ EXPECT_FALSE(s.Step()); } -TEST_F(FirstPartySetsDatabaseTest, InsertProfileCleared_NoPreExistingDB) { - const std::string profile = "p"; +TEST_F(FirstPartySetsDatabaseTest, + InsertBrowserContextCleared_NoPreExistingDB) { + const std::string browser_context_id = "p"; int64_t expected_run_count = 1; OpenDatabase(); // Trigger the lazy-initialization. - EXPECT_TRUE(db()->InsertProfileCleared(profile)); + EXPECT_TRUE(db()->InsertBrowserContextCleared(browser_context_id)); CloseDatabase(); sql::Database db; EXPECT_TRUE(db.Open(db_path())); - EXPECT_EQ(1u, CountProfilesClearedEntries(&db)); + EXPECT_EQ(1u, CountBrowserContextsClearedEntries(&db)); const char kSelectSql[] = - "SELECT profile, cleared_at_run FROM profiles_cleared"; + "SELECT browser_context_id, cleared_at_run FROM browser_contexts_cleared"; sql::Statement s(db.GetUniqueStatement(kSelectSql)); EXPECT_TRUE(s.Step()); - EXPECT_EQ(profile, s.ColumnString(0)); + EXPECT_EQ(browser_context_id, s.ColumnString(0)); EXPECT_EQ(expected_run_count, s.ColumnInt64(1)); EXPECT_FALSE(s.Step()); } -TEST_F(FirstPartySetsDatabaseTest, InsertProfileCleared_PreExistingDB) { +TEST_F(FirstPartySetsDatabaseTest, InsertBrowserContextCleared_PreExistingDB) { ASSERT_TRUE( sql::test::CreateDatabaseFromSQL(db_path(), GetSqlFilePath("v1.sql"))); @@ -322,10 +324,11 @@ sql::Database db; EXPECT_TRUE(db.Open(db_path())); EXPECT_EQ(3u, sql::test::CountSQLTables(&db)); - EXPECT_EQ(1u, CountProfilesClearedEntries(&db)); + EXPECT_EQ(1u, CountBrowserContextsClearedEntries(&db)); const char kSelectSql[] = - "SELECT profile, cleared_at_run FROM profiles_cleared"; + "SELECT browser_context_id, cleared_at_run FROM " + "browser_contexts_cleared"; sql::Statement s(db.GetUniqueStatement(kSelectSql)); EXPECT_TRUE(s.Step()); EXPECT_EQ("p", s.ColumnString(0)); @@ -333,36 +336,35 @@ pre_run_count = s.ColumnInt64(1); } - std::string profile = "p1"; + std::string browser_context_id = "p1"; OpenDatabase(); // Trigger the lazy-initialization. - EXPECT_TRUE(db()->InsertProfileCleared(profile)); + EXPECT_TRUE(db()->InsertBrowserContextCleared(browser_context_id)); CloseDatabase(); // Verify the inserted data. sql::Database db; EXPECT_TRUE(db.Open(db_path())); - EXPECT_EQ(2u, CountProfilesClearedEntries(&db)); + EXPECT_EQ(2u, CountBrowserContextsClearedEntries(&db)); const char kSelectSql[] = - "SELECT profile FROM profiles_cleared " + "SELECT browser_context_id FROM browser_contexts_cleared " "WHERE cleared_at_run>?"; sql::Statement s(db.GetUniqueStatement(kSelectSql)); s.BindInt64(0, pre_run_count); EXPECT_TRUE(s.Step()); - EXPECT_EQ(profile, s.ColumnString(0)); + EXPECT_EQ(browser_context_id, s.ColumnString(0)); EXPECT_FALSE(s.Step()); } TEST_F(FirstPartySetsDatabaseTest, FetchSitesToClear_NoPreExistingDB) { OpenDatabase(); - EXPECT_EQ(std::vector<net::SchemefulSite>(), - db()->FetchSitesToClear("profile")); + EXPECT_EQ(std::vector<net::SchemefulSite>(), db()->FetchSitesToClear("id")); } -TEST_F(FirstPartySetsDatabaseTest, FetchSitesToClear_ProfileNotExist) { +TEST_F(FirstPartySetsDatabaseTest, FetchSitesToClear_BrowserContextNotExist) { ASSERT_TRUE( sql::test::CreateDatabaseFromSQL(db_path(), GetSqlFilePath("v1.sql"))); @@ -372,9 +374,10 @@ EXPECT_TRUE(db.Open(db_path())); EXPECT_EQ(3u, sql::test::CountSQLTables(&db)); EXPECT_EQ(1u, CountSitesToClearEntries(&db)); - EXPECT_EQ(1u, CountProfilesClearedEntries(&db)); + EXPECT_EQ(1u, CountBrowserContextsClearedEntries(&db)); - const char kSelectSql[] = "SELECT profile FROM profiles_cleared"; + const char kSelectSql[] = + "SELECT browser_context_id FROM browser_contexts_cleared"; sql::Statement s(db.GetUniqueStatement(kSelectSql)); EXPECT_TRUE(s.Step()); EXPECT_EQ("p", s.ColumnString(0)); @@ -395,9 +398,10 @@ EXPECT_TRUE(db.Open(db_path())); EXPECT_EQ(3u, sql::test::CountSQLTables(&db)); EXPECT_EQ(1u, CountSitesToClearEntries(&db)); - EXPECT_EQ(1u, CountProfilesClearedEntries(&db)); + EXPECT_EQ(1u, CountBrowserContextsClearedEntries(&db)); - const char kSelectSql[] = "SELECT profile FROM profiles_cleared"; + const char kSelectSql[] = + "SELECT browser_context_id FROM browser_contexts_cleared"; sql::Statement s(db.GetUniqueStatement(kSelectSql)); EXPECT_TRUE(s.Step()); EXPECT_EQ("p", s.ColumnString(0));
diff --git a/content/browser/gpu/gpu_data_manager_impl_private.cc b/content/browser/gpu/gpu_data_manager_impl_private.cc index 5100caa..855bd71 100644 --- a/content/browser/gpu/gpu_data_manager_impl_private.cc +++ b/content/browser/gpu/gpu_data_manager_impl_private.cc
@@ -1118,9 +1118,12 @@ void GpuDataManagerImplPrivate::UpdateDXGIInfo( gfx::mojom::DXGIInfoPtr dxgi_info) { - // This is running on the main thread; - DCHECK_CURRENTLY_ON(BrowserThread::UI); - HDRProxy::GotResult(std::move(dxgi_info)); + // Calling out into HDRProxy::GotResult may end up re-entering us via + // GpuDataManagerImpl::OnDisplayRemoved/OnDisplayAdded. Both of these + // take the owner's lock. To avoid recursive locks, we PostTask + // HDRProxy::GotResult so that it runs outside of the lock. + GetUIThreadTaskRunner({})->PostTask( + FROM_HERE, base::BindOnce(&HDRProxy::GotResult, std::move(dxgi_info))); } void GpuDataManagerImplPrivate::UpdateDxDiagNodeRequestStatus(
diff --git a/content/browser/loader/navigation_url_loader_impl.h b/content/browser/loader/navigation_url_loader_impl.h index 64fb28a..b3a44d6 100644 --- a/content/browser/loader/navigation_url_loader_impl.h +++ b/content/browser/loader/navigation_url_loader_impl.h
@@ -19,6 +19,7 @@ #include "mojo/public/cpp/bindings/pending_receiver.h" #include "mojo/public/cpp/bindings/pending_remote.h" #include "net/url_request/url_request.h" +#include "ppapi/buildflags/buildflags.h" #include "services/metrics/public/cpp/ukm_source_id.h" #include "services/network/public/mojom/accept_ch_frame_observer.mojom.h" #include "services/network/public/mojom/url_loader.mojom.h"
diff --git a/content/browser/media/capture/web_contents_video_capture_device_browsertest.cc b/content/browser/media/capture/web_contents_video_capture_device_browsertest.cc index 7d6580a8..cf3a9a0 100644 --- a/content/browser/media/capture/web_contents_video_capture_device_browsertest.cc +++ b/content/browser/media/capture/web_contents_video_capture_device_browsertest.cc
@@ -320,16 +320,8 @@ // Tests that the device starts, captures a frame, and then gracefully // errors-out because the WebContents is destroyed before the device is stopped. -// TODO the test is flaky on Mac. See https://crbug.com/1345663. -#if BUILDFLAG(IS_MAC) -#define MAYBE_ErrorsOutWhenWebContentsIsDestroyed \ - DISABLED_ErrorsOutWhenWebContentsIsDestroyed -#else -#define MAYBE_ErrorsOutWhenWebContentsIsDestroyed \ - ErrorsOutWhenWebContentsIsDestroyed -#endif IN_PROC_BROWSER_TEST_F(WebContentsVideoCaptureDeviceBrowserTest, - MAYBE_ErrorsOutWhenWebContentsIsDestroyed) { + ErrorsOutWhenWebContentsIsDestroyed) { NavigateToInitialDocument(); AllocateAndStartAndWaitForFirstFrame(); EXPECT_TRUE(shell()->web_contents()->IsBeingCaptured()); @@ -350,14 +342,8 @@ // Tests that capture is re-targetted when the render view of a WebContents // changes. -// TODO the test is flaky on Mac. See https://crbug.com/1345663. -#if BUILDFLAG(IS_MAC) -#define MAYBE_ChangesTargettedRenderView DISABLED_ChangesTargettedRenderView -#else -#define MAYBE_ChangesTargettedRenderView ChangesTargettedRenderView -#endif IN_PROC_BROWSER_TEST_F(WebContentsVideoCaptureDeviceBrowserTest, - MAYBE_ChangesTargettedRenderView) { + ChangesTargettedRenderView) { NavigateToInitialDocument(); AllocateAndStartAndWaitForFirstFrame(); EXPECT_TRUE(shell()->web_contents()->IsBeingCaptured()); @@ -429,14 +415,8 @@ // Tests that capture is re-targetted when a renderer crash is followed by a // reload. Regression test for http://crbug.com/916332. -// TODO the test is flaky on Mac. See https://crbug.com/1345663. -#if BUILDFLAG(IS_MAC) -#define MAYBE_RecoversAfterRendererCrash DISABLED_RecoversAfterRendererCrash -#else -#define MAYBE_RecoversAfterRendererCrash RecoversAfterRendererCrash -#endif IN_PROC_BROWSER_TEST_F(WebContentsVideoCaptureDeviceBrowserTest, - MAYBE_RecoversAfterRendererCrash) { + RecoversAfterRendererCrash) { NavigateToInitialDocument(); AllocateAndStartAndWaitForFirstFrame(); EXPECT_TRUE(shell()->web_contents()->IsBeingCaptured()); @@ -465,14 +445,8 @@ // Tests that the device stops delivering frames while suspended. When resumed, // any content changes that occurred during the suspend should cause a new frame // to be delivered, to ensure the client is up-to-date. -// TODO the test is flaky on Mac. See https://crbug.com/1345663. -#if BUILDFLAG(IS_MAC) -#define MAYBE_SuspendsAndResumes DISABLED_SuspendsAndResumes -#else -#define MAYBE_SuspendsAndResumes SuspendsAndResumes -#endif IN_PROC_BROWSER_TEST_F(WebContentsVideoCaptureDeviceBrowserTest, - MAYBE_SuspendsAndResumes) { + SuspendsAndResumes) { NavigateToInitialDocument(); AllocateAndStartAndWaitForFirstFrame(); EXPECT_TRUE(shell()->web_contents()->IsBeingCaptured()); @@ -506,15 +480,8 @@ // Tests that the device delivers refresh frames when asked, while the source // content is not changing. -// TODO the test is flaky on Mac. See https://crbug.com/1345663. -#if BUILDFLAG(IS_MAC) -#define MAYBE_DeliversRefreshFramesUponRequest \ - DISABLED_DeliversRefreshFramesUponRequest -#else -#define MAYBE_DeliversRefreshFramesUponRequest DeliversRefreshFramesUponRequest -#endif IN_PROC_BROWSER_TEST_F(WebContentsVideoCaptureDeviceBrowserTest, - MAYBE_DeliversRefreshFramesUponRequest) { + DeliversRefreshFramesUponRequest) { NavigateToInitialDocument(); AllocateAndStartAndWaitForFirstFrame(); EXPECT_TRUE(shell()->web_contents()->IsBeingCaptured());
diff --git a/content/browser/plugin_service_impl.cc b/content/browser/plugin_service_impl.cc index edb9122..602c95af 100644 --- a/content/browser/plugin_service_impl.cc +++ b/content/browser/plugin_service_impl.cc
@@ -123,7 +123,6 @@ PpapiPluginProcessHost* PluginServiceImpl::FindOrStartPpapiPluginProcess( int render_process_id, - const url::Origin& embedder_origin, const base::FilePath& plugin_path, const base::FilePath& profile_data_directory, const absl::optional<url::Origin>& origin_lock) { @@ -142,12 +141,6 @@ return nullptr; } - // Validate that |embedder_origin| is allowed to embed the plugin. - if (!GetContentClient()->browser()->ShouldAllowPluginCreation(embedder_origin, - *info)) { - return nullptr; - } - PpapiPluginProcessHost* plugin_host = FindPpapiPluginProcess(plugin_path, profile_data_directory, origin_lock); if (plugin_host) @@ -173,14 +166,12 @@ void PluginServiceImpl::OpenChannelToPpapiPlugin( int render_process_id, - const url::Origin& embedder_origin, const base::FilePath& plugin_path, const base::FilePath& profile_data_directory, const absl::optional<url::Origin>& origin_lock, PpapiPluginProcessHost::PluginClient* client) { PpapiPluginProcessHost* plugin_host = FindOrStartPpapiPluginProcess( - render_process_id, embedder_origin, plugin_path, profile_data_directory, - origin_lock); + render_process_id, plugin_path, profile_data_directory, origin_lock); if (plugin_host) { plugin_host->OpenChannelToPlugin(client); } else {
diff --git a/content/browser/plugin_service_impl.h b/content/browser/plugin_service_impl.h index 9f256a7f..f0e36ba 100644 --- a/content/browser/plugin_service_impl.h +++ b/content/browser/plugin_service_impl.h
@@ -87,7 +87,6 @@ // is NULL. Must be called on the IO thread. PpapiPluginProcessHost* FindOrStartPpapiPluginProcess( int render_process_id, - const url::Origin& embedder_origin, const base::FilePath& plugin_path, const base::FilePath& profile_data_directory, const absl::optional<url::Origin>& origin_lock); @@ -96,7 +95,6 @@ // a new plugin process if necessary. This must be called on the IO thread // or else a deadlock can occur. void OpenChannelToPpapiPlugin(int render_process_id, - const url::Origin& embedder_origin, const base::FilePath& plugin_path, const base::FilePath& profile_data_directory, const absl::optional<url::Origin>& origin_lock,
diff --git a/content/browser/plugin_service_impl_browsertest.cc b/content/browser/plugin_service_impl_browsertest.cc index 2b2ea99e..25d7001 100644 --- a/content/browser/plugin_service_impl_browsertest.cc +++ b/content/browser/plugin_service_impl_browsertest.cc
@@ -86,8 +86,7 @@ PluginServiceImpl* service = PluginServiceImpl::GetInstance(); service->OpenChannelToPpapiPlugin( - /*render_process_id=*/0, /*embedder_origin=*/url::Origin(), - plugin_path_, profile_dir_, origin, client); + /*render_process_id=*/0, plugin_path_, profile_dir_, origin, client); client->WaitForQuit(); client->SetRunLoop(nullptr); }
diff --git a/content/browser/private_aggregation/private_aggregation_features.cc b/content/browser/private_aggregation/private_aggregation_features.cc new file mode 100644 index 0000000..9913b09 --- /dev/null +++ b/content/browser/private_aggregation/private_aggregation_features.cc
@@ -0,0 +1,12 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/browser/private_aggregation/private_aggregation_features.h" + +namespace content { + +const base::Feature kPrivateAggregationApi = { + "PrivateAggregationApi", base::FEATURE_DISABLED_BY_DEFAULT}; + +} // namespace content
diff --git a/content/browser/private_aggregation/private_aggregation_features.h b/content/browser/private_aggregation/private_aggregation_features.h new file mode 100644 index 0000000..5c0b2d4 --- /dev/null +++ b/content/browser/private_aggregation/private_aggregation_features.h
@@ -0,0 +1,19 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_BROWSER_PRIVATE_AGGREGATION_PRIVATE_AGGREGATION_FEATURES_H_ +#define CONTENT_BROWSER_PRIVATE_AGGREGATION_PRIVATE_AGGREGATION_FEATURES_H_ + +#include "base/feature_list.h" + +namespace content { + +// Enables the Private Aggregation API. Note that this API also requires the +// `kPrivacySandboxAggregationService` to be enabled to successfully send +// reports. +extern const base::Feature kPrivateAggregationApi; + +} // namespace content + +#endif // CONTENT_BROWSER_PRIVATE_AGGREGATION_PRIVATE_AGGREGATION_FEATURES_H_
diff --git a/content/browser/private_aggregation/private_aggregation_manager.cc b/content/browser/private_aggregation/private_aggregation_manager.cc new file mode 100644 index 0000000..1597b0dc --- /dev/null +++ b/content/browser/private_aggregation/private_aggregation_manager.cc
@@ -0,0 +1,20 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/browser/private_aggregation/private_aggregation_manager.h" + +#include "content/browser/private_aggregation/private_aggregation_manager_impl.h" +#include "content/browser/storage_partition_impl.h" +#include "content/public/browser/browser_context.h" + +namespace content { + +PrivateAggregationManager* PrivateAggregationManager::GetManager( + BrowserContext* browser_context) { + return static_cast<StoragePartitionImpl*>( + browser_context->GetDefaultStoragePartition()) + ->GetPrivateAggregationManager(); +} + +} // namespace content
diff --git a/content/browser/private_aggregation/private_aggregation_manager.h b/content/browser/private_aggregation/private_aggregation_manager.h index f3154ad..c50766c 100644 --- a/content/browser/private_aggregation/private_aggregation_manager.h +++ b/content/browser/private_aggregation/private_aggregation_manager.h
@@ -15,12 +15,16 @@ namespace content { +class BrowserContext; + // Interface that mediates data flow between the Private Aggregation API // component and other APIs using it. class PrivateAggregationManager { public: virtual ~PrivateAggregationManager() = default; + static PrivateAggregationManager* GetManager(BrowserContext* browser_context); + // Binds a new pending receiver for a worklet, allowing messages to be sent // and processed. However, the receiver is not bound if the `worklet_origin` // is not potentially trustworthy. The return value indicates whether the
diff --git a/content/browser/private_aggregation/private_aggregation_manager_impl.h b/content/browser/private_aggregation/private_aggregation_manager_impl.h index 8c2210bb..48a9977 100644 --- a/content/browser/private_aggregation/private_aggregation_manager_impl.h +++ b/content/browser/private_aggregation/private_aggregation_manager_impl.h
@@ -28,7 +28,8 @@ class PrivateAggregationHost; // UI thread class that manages the lifetime of the other classes, -// coordinates report requests, and interfaces with other directories. +// coordinates report requests, and interfaces with other directories. Lifetime +// is bound to lifetime of the `StoragePartitionImpl`. // TODO(crbug.com/1323325): Integrate with aggregation service. class CONTENT_EXPORT PrivateAggregationManagerImpl : public PrivateAggregationManager {
diff --git a/content/browser/renderer_host/navigation_controller_impl.cc b/content/browser/renderer_host/navigation_controller_impl.cc index 29b9642..40b1947 100644 --- a/content/browser/renderer_host/navigation_controller_impl.cc +++ b/content/browser/renderer_host/navigation_controller_impl.cc
@@ -354,7 +354,8 @@ const FrameNavigationEntry& frame_entry, bool has_pending_cross_document_commit, bool is_currently_error_page, - bool is_same_document_history_load) { + bool is_same_document_history_load, + bool is_unfenced_top_navigation) { // Reload navigations switch (reload_type) { case ReloadType::NORMAL: @@ -381,7 +382,11 @@ // If the current document is an error page, we should always treat it as // a different-document navigation so that we'll attempt to load the // document we're navigating to (and not stay in the current error page). - !is_currently_error_page; + !is_currently_error_page && + // If the navigation is to _unfencedTop (i.e. escapes a fenced frame), + // same-document navigations should be disabled because we want to force + // the creation of a new browsing context group. + !is_unfenced_top_navigation; // History navigations. if (frame_entry.page_state().IsValid()) { @@ -2633,7 +2638,8 @@ bool is_form_submission, const absl::optional<blink::Impression>& impression, base::TimeTicks navigation_start_time, - bool is_embedder_initiated_fenced_frame_navigation) { + bool is_embedder_initiated_fenced_frame_navigation, + bool is_unfenced_top_navigation) { if (is_renderer_initiated) DCHECK(initiator_origin.has_value()); @@ -2769,7 +2775,8 @@ node, params, override_user_agent, should_replace_current_entry, false /* has_user_gesture */, std::move(source_location), ReloadType::NONE, entry.get(), frame_entry.get(), - navigation_start_time, is_embedder_initiated_fenced_frame_navigation); + navigation_start_time, is_embedder_initiated_fenced_frame_navigation, + is_unfenced_top_navigation); if (!request) return; @@ -3659,7 +3666,8 @@ NavigationEntryImpl* entry, FrameNavigationEntry* frame_entry, base::TimeTicks navigation_start_time, - bool is_embedder_initiated_fenced_frame_navigation) { + bool is_embedder_initiated_fenced_frame_navigation, + bool is_unfenced_top_navigation) { DCHECK_EQ(-1, GetIndexOfEntry(entry)); DCHECK(frame_entry); // All renderer-initiated navigations must have an initiator_origin. @@ -3735,7 +3743,7 @@ /*old_url=*/node->current_url(), /*new_url=*/url_to_load, reload_type, entry, *frame_entry, has_pending_cross_document_commit, is_currently_error_page, - /*is_same_document_history_load=*/false); + /*is_same_document_history_load=*/false, is_unfenced_top_navigation); // Create the NavigationParams based on |params|. @@ -3912,7 +3920,8 @@ /*old_url=*/frame_tree_node->current_url(), /*new_url=*/dest_url, reload_type, entry, *frame_entry, has_pending_cross_document_commit, is_currently_error_page, - is_same_document_history_load); + is_same_document_history_load, + /*is_unfenced_top_navigation=*/false); // A form submission may happen here if the navigation is a // back/forward/reload navigation that does a form resubmission.
diff --git a/content/browser/renderer_host/navigation_controller_impl.h b/content/browser/renderer_host/navigation_controller_impl.h index e7f492d..b6392e7c 100644 --- a/content/browser/renderer_host/navigation_controller_impl.h +++ b/content/browser/renderer_host/navigation_controller_impl.h
@@ -220,7 +220,8 @@ bool is_form_submission, const absl::optional<blink::Impression>& impression, base::TimeTicks navigation_start_time, - bool is_embedder_initiated_fenced_frame_navigation = false); + bool is_embedder_initiated_fenced_frame_navigation = false, + bool is_unfenced_top_navigation = false); // Navigates to the history entry associated with the given navigation API // |key|. Searches |entries_| for a FrameNavigationEntry associated with @@ -578,7 +579,8 @@ NavigationEntryImpl* entry, FrameNavigationEntry* frame_entry, base::TimeTicks navigation_start_time, - bool is_embedder_initiated_fenced_frame_navigation = false); + bool is_embedder_initiated_fenced_frame_navigation = false, + bool is_unfenced_top_navigation = false); // Creates and returns a NavigationRequest for a navigation to |entry|. Will // return nullptr if the parameters are invalid and the navigation cannot
diff --git a/content/browser/renderer_host/navigator.cc b/content/browser/renderer_host/navigator.cc index 6db78d7..8caea1c 100644 --- a/content/browser/renderer_host/navigator.cc +++ b/content/browser/renderer_host/navigator.cc
@@ -837,7 +837,8 @@ bool is_form_submission, const absl::optional<blink::Impression>& impression, base::TimeTicks navigation_start_time, - bool is_embedder_initiated_fenced_frame_navigation) { + bool is_embedder_initiated_fenced_frame_navigation, + bool is_unfenced_top_navigation) { // |method != "POST"| should imply absence of |post_body|. if (method != "POST" && post_body) { NOTREACHED(); @@ -880,7 +881,8 @@ download_policy, method, post_body, extra_headers, std::move(source_location), std::move(blob_url_loader_factory), is_form_submission, impression, navigation_start_time, - is_embedder_initiated_fenced_frame_navigation); + is_embedder_initiated_fenced_frame_navigation, + is_unfenced_top_navigation); } void Navigator::BeforeUnloadCompleted(FrameTreeNode* frame_tree_node,
diff --git a/content/browser/renderer_host/navigator.h b/content/browser/renderer_host/navigator.h index 301b7fb..52b8b0e 100644 --- a/content/browser/renderer_host/navigator.h +++ b/content/browser/renderer_host/navigator.h
@@ -163,7 +163,8 @@ bool is_form_submission, const absl::optional<blink::Impression>& impression, base::TimeTicks navigation_start_time, - bool is_embedder_initiated_fenced_frame_navigation = false); + bool is_embedder_initiated_fenced_frame_navigation = false, + bool is_unfenced_top_navigation = false); // Called after BeforeUnloadCompleted callback is invoked from the renderer. // If |frame_tree_node| has a NavigationRequest waiting for the renderer
diff --git a/content/browser/renderer_host/pepper/pepper_renderer_connection.cc b/content/browser/renderer_host/pepper/pepper_renderer_connection.cc index 3e1c2ae1..1123a29 100644 --- a/content/browser/renderer_host/pepper/pepper_renderer_connection.cc +++ b/content/browser/renderer_host/pepper/pepper_renderer_connection.cc
@@ -353,8 +353,7 @@ } plugin_service_->OpenChannelToPpapiPlugin( - render_process_id_, embedder_origin, path, profile_data_directory_, - origin_lock, + render_process_id_, path, profile_data_directory_, origin_lock, new OpenChannelToPpapiPluginCallback(this, std::move(callback))); }
diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc index 586f85a..cb9e54b 100644 --- a/content/browser/renderer_host/render_frame_host_impl.cc +++ b/content/browser/renderer_host/render_frame_host_impl.cc
@@ -7166,7 +7166,9 @@ /*post_body=*/nullptr, params->extra_headers, /*blob_url_loader_factory=*/nullptr, network::mojom::SourceLocation::New(), /*has_user_gesture=*/false, - params->is_form_submission, params->impression, base::TimeTicks::Now()); + params->is_form_submission, params->impression, base::TimeTicks::Now(), + /*is_embedder_initiated_fenced_frame_navigation=*/false, + /*is_unfenced_top_navigation=*/true); return; }
diff --git a/content/browser/storage_partition_impl.cc b/content/browser/storage_partition_impl.cc index ab92124..4aeaaf7 100644 --- a/content/browser/storage_partition_impl.cc +++ b/content/browser/storage_partition_impl.cc
@@ -78,6 +78,8 @@ #include "content/browser/notifications/platform_notification_context_impl.h" #include "content/browser/payments/payment_app_context_impl.h" #include "content/browser/preloading/prerender/prerender_host_registry.h" +#include "content/browser/private_aggregation/private_aggregation_features.h" +#include "content/browser/private_aggregation/private_aggregation_manager_impl.h" #include "content/browser/push_messaging/push_messaging_context.h" #include "content/browser/quota/quota_context.h" #include "content/browser/renderer_host/frame_tree_node.h" @@ -1388,6 +1390,11 @@ shared_storage_manager_ = std::make_unique<storage::SharedStorageManager>( shared_storage_path, special_storage_policy_); } + + if (base::FeatureList::IsEnabled(kPrivateAggregationApi)) { + private_aggregation_manager_ = + std::make_unique<PrivateAggregationManagerImpl>(is_in_memory(), path); + } } void StoragePartitionImpl::OnStorageServiceDisconnected() { @@ -1726,6 +1733,12 @@ return shared_storage_manager_.get(); } +PrivateAggregationManagerImpl* +StoragePartitionImpl::GetPrivateAggregationManager() { + DCHECK(initialized_); + return private_aggregation_manager_.get(); +} + void StoragePartitionImpl::OpenLocalStorage( const blink::StorageKey& storage_key, const blink::LocalFrameToken& local_frame_token, @@ -2534,7 +2547,7 @@ // TODO(crbug.com/1286173): Consider adding aggregation service origins to // `CookiesTreeModel`. aggregation_service->ClearData( - begin, end, + begin, end, filter, CreateTaskCompletionClosure(TracingDataType::kAggregationService)); }
diff --git a/content/browser/storage_partition_impl.h b/content/browser/storage_partition_impl.h index 3eaa89f..46da9dc2b 100644 --- a/content/browser/storage_partition_impl.h +++ b/content/browser/storage_partition_impl.h
@@ -88,6 +88,7 @@ class NativeIOContextImpl; class PaymentAppContextImpl; class PrefetchURLLoaderService; +class PrivateAggregationManagerImpl; class PushMessagingContext; class QuotaContext; class SharedStorageWorkletHostManager; @@ -263,6 +264,7 @@ // Gets the SharedStorageManager for the StoragePartition, or nullptr if it // doesn't exist because the feature is disabled. storage::SharedStorageManager* GetSharedStorageManager(); + PrivateAggregationManagerImpl* GetPrivateAggregationManager(); // blink::mojom::DomStorage interface. void OpenLocalStorage( @@ -665,6 +667,8 @@ std::unique_ptr<SharedStorageWorkletHostManager> shared_storage_worklet_host_manager_; + std::unique_ptr<PrivateAggregationManagerImpl> private_aggregation_manager_; + // ReceiverSet for DomStorage, using the // ChildProcessSecurityPolicyImpl::Handle as the binding context type. The // handle can subsequently be used during interface method calls to
diff --git a/content/browser/storage_partition_impl_unittest.cc b/content/browser/storage_partition_impl_unittest.cc index a0865f3..d1c2f28 100644 --- a/content/browser/storage_partition_impl_unittest.cc +++ b/content/browser/storage_partition_impl_unittest.cc
@@ -550,6 +550,7 @@ ClearData, (base::Time delete_begin, base::Time delete_end, + StoragePartition::StorageKeyMatcherFunction filter, base::OnceClosure done), (override)); }; @@ -2074,51 +2075,98 @@ const uint32_t kTestQuotaClearMask = StoragePartition::QUOTA_MANAGED_STORAGE_MASK_ALL; const auto kTestOrigin = GURL("https://example.com"); + const auto kOtherOrigin = GURL("https://example.net"); const auto kBeginTime = base::Time() + base::Hours(1); const auto kEndTime = base::Time() + base::Hours(2); const auto invoke_callback = [](base::Time delete_begin, base::Time delete_end, + StoragePartition::StorageKeyMatcherFunction filter, base::OnceClosure done) { std::move(done).Run(); }; + const auto is_test_origin_valid = + [&kTestOrigin]( + content::StoragePartition::StorageKeyMatcherFunction filter) { + return filter.Run(blink::StorageKey(url::Origin::Create(kTestOrigin))); + }; + const auto is_other_origin_valid = + [&kOtherOrigin]( + content::StoragePartition::StorageKeyMatcherFunction filter) { + return filter.Run(blink::StorageKey(url::Origin::Create(kOtherOrigin))); + }; + const auto is_filter_null = + [&](content::StoragePartition::StorageKeyMatcherFunction filter) { + return filter.is_null(); + }; // Verify that each of the StoragePartition interfaces for clearing origin // based data calls aggregation service appropriately. + EXPECT_CALL( + *aggregation_service_ptr, + ClearData( + base::Time(), base::Time::Max(), + testing::AllOf(testing::Truly(is_test_origin_valid), + testing::Not(testing::Truly(is_other_origin_valid))), + testing::_)) + .WillOnce(invoke_callback); + { + base::RunLoop run_loop; + partition->ClearDataForOrigin(kTestClearMask, kTestQuotaClearMask, + kTestOrigin, run_loop.QuitClosure()); + run_loop.Run(); + testing::Mock::VerifyAndClearExpectations(aggregation_service_ptr); + } + + EXPECT_CALL( + *aggregation_service_ptr, + ClearData( + kBeginTime, kEndTime, + testing::AllOf(testing::Truly(is_test_origin_valid), + testing::Not(testing::Truly(is_other_origin_valid))), + testing::_)) + .WillOnce(testing::Invoke(invoke_callback)); + { + base::RunLoop run_loop; + partition->ClearData(kTestClearMask, kTestQuotaClearMask, + blink::StorageKey(url::Origin::Create(kTestOrigin)), + kBeginTime, kEndTime, run_loop.QuitClosure()); + run_loop.Run(); + testing::Mock::VerifyAndClearExpectations(aggregation_service_ptr); + } + + EXPECT_CALL( + *aggregation_service_ptr, + ClearData( + kBeginTime, kEndTime, + testing::AllOf(testing::Truly(is_test_origin_valid), + testing::Not(testing::Truly(is_other_origin_valid))), + testing::_)) + .WillOnce(testing::Invoke(invoke_callback)); + { + base::RunLoop run_loop; + partition->ClearData( + kTestClearMask, kTestQuotaClearMask, + base::BindLambdaForTesting([&](const blink::StorageKey& storage_key, + storage::SpecialStoragePolicy* policy) { + return storage_key == + blink::StorageKey(url::Origin::Create(kTestOrigin)); + }), + /*cookie_deletion_filter=*/nullptr, + /*perform_storage_cleanup=*/false, kBeginTime, kEndTime, + run_loop.QuitClosure()); + run_loop.Run(); + testing::Mock::VerifyAndClearExpectations(aggregation_service_ptr); + } EXPECT_CALL(*aggregation_service_ptr, - ClearData(base::Time(), base::Time::Max(), testing::_)) + ClearData(kBeginTime, kEndTime, testing::Truly(is_filter_null), + testing::_)) .WillOnce(testing::Invoke(invoke_callback)); - base::RunLoop run_loop; - partition->ClearDataForOrigin(kTestClearMask, kTestQuotaClearMask, - kTestOrigin, run_loop.QuitClosure()); - run_loop.Run(); - testing::Mock::VerifyAndClearExpectations(aggregation_service_ptr); - - EXPECT_CALL(*aggregation_service_ptr, - ClearData(kBeginTime, kEndTime, testing::_)) - .WillOnce(testing::Invoke(invoke_callback)); - partition->ClearData(kTestClearMask, kTestQuotaClearMask, - blink::StorageKey(url::Origin::Create(kTestOrigin)), - kBeginTime, kEndTime, base::DoNothing()); - testing::Mock::VerifyAndClearExpectations(aggregation_service_ptr); - - EXPECT_CALL(*aggregation_service_ptr, - ClearData(kBeginTime, kEndTime, testing::_)) - .WillOnce(testing::Invoke(invoke_callback)); - partition->ClearData( - kTestClearMask, kTestQuotaClearMask, - base::BindLambdaForTesting([&](const blink::StorageKey& storage_key, - storage::SpecialStoragePolicy* policy) { - return storage_key == - blink::StorageKey(url::Origin::Create(kTestOrigin)); - }), - /*cookie_deletion_filter=*/nullptr, /*perform_storage_cleanup=*/false, - kBeginTime, kEndTime, base::DoNothing()); - testing::Mock::VerifyAndClearExpectations(aggregation_service_ptr); - - EXPECT_CALL(*aggregation_service_ptr, - ClearData(kBeginTime, kEndTime, testing::_)) - .WillOnce(testing::Invoke(invoke_callback)); - partition->ClearData(kTestClearMask, kTestQuotaClearMask, blink::StorageKey(), - kBeginTime, kEndTime, base::DoNothing()); + { + base::RunLoop run_loop; + partition->ClearData(kTestClearMask, kTestQuotaClearMask, + blink::StorageKey(), kBeginTime, kEndTime, + run_loop.QuitClosure()); + run_loop.Run(); + } } // https://crbug.com/1221382
diff --git a/content/child/runtime_features.cc b/content/child/runtime_features.cc index 1f91a77..a34397c8 100644 --- a/content/child/runtime_features.cc +++ b/content/child/runtime_features.cc
@@ -376,6 +376,8 @@ {"PendingBeaconAPI", blink::features::kPendingBeaconAPI}, {"PrefersColorSchemeClientHintHeader", blink::features::kPrefersColorSchemeClientHintHeader}, + {"PrivateNetworkAccessPermissionPrompt", + blink::features::kPrivateNetworkAccessPermissionPrompt}, {"FirstPartySets", features::kFirstPartySets}, {"QuickIntensiveWakeUpThrottlingAfterLoading", blink::features::kQuickIntensiveWakeUpThrottlingAfterLoading},
diff --git a/content/common/user_agent.cc b/content/common/user_agent.cc index 8745028..cdfbe98a 100644 --- a/content/common/user_agent.cc +++ b/content/common/user_agent.cc
@@ -29,6 +29,13 @@ namespace { +const char kFrozenUserAgentTemplate[] = + "Mozilla/5.0 (%s) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/%s.0.0.0 " +#if BUILDFLAG(IS_ANDROID) + "%s" +#endif + "Safari/537.36"; + std::string GetUserAgentPlatform() { #if BUILDFLAG(IS_WIN) return ""; @@ -45,26 +52,30 @@ #endif } -} // namespace - std::string GetUnifiedPlatform() { #if BUILDFLAG(IS_ANDROID) - return frozen_user_agent_strings::kUnifiedPlatformAndroid; + return "Linux; Android 10; K"; #elif BUILDFLAG(IS_CHROMEOS) - return frozen_user_agent_strings::kUnifiedPlatformCrOS; + return "X11; CrOS x86_64 14541.0.0"; #elif BUILDFLAG(IS_MAC) - return frozen_user_agent_strings::kUnifiedPlatformMacOS; + return "Macintosh; Intel Mac OS X 10_15_7"; #elif BUILDFLAG(IS_WIN) - return frozen_user_agent_strings::kUnifiedPlatformWindows; + return "Windows NT 10.0; Win64; x64"; #elif BUILDFLAG(IS_FUCHSIA) - return frozen_user_agent_strings::kUnifiedPlatformFuchsia; + return "Fuchsia"; #elif BUILDFLAG(IS_LINUX) - return frozen_user_agent_strings::kUnifiedPlatformLinux; + return "X11; Linux x86_64"; #else #error Unsupported platform #endif } +} // namespace + +std::string GetUnifiedPlatformForTesting() { + return GetUnifiedPlatform(); +} + // Inaccurately named for historical reasons std::string GetWebKitVersion() { return base::StringPrintf("537.36 (%s)", CHROMIUM_GIT_REVISION); @@ -284,10 +295,10 @@ device_compat = mobile ? "Mobile " : ""; #endif std::string user_agent = - base::StringPrintf(frozen_user_agent_strings::kTemplate, - GetUnifiedPlatform().c_str(), major_version.c_str() + base::StringPrintf(kFrozenUserAgentTemplate, GetUnifiedPlatform().c_str(), + major_version.c_str() #if BUILDFLAG(IS_ANDROID) - , + , device_compat.c_str() #endif );
diff --git a/content/public/browser/content_browser_client.cc b/content/public/browser/content_browser_client.cc index 6f443c2..5f50e84 100644 --- a/content/public/browser/content_browser_client.cc +++ b/content/public/browser/content_browser_client.cc
@@ -1260,14 +1260,6 @@ return false; } -#if BUILDFLAG(ENABLE_PLUGINS) -bool ContentBrowserClient::ShouldAllowPluginCreation( - const url::Origin& embedder_origin, - const content::PepperPluginInfo& plugin_info) { - return true; -} -#endif - #if BUILDFLAG(ENABLE_VR) XrIntegrationClient* ContentBrowserClient::GetXrIntegrationClient() { return nullptr;
diff --git a/content/public/browser/content_browser_client.h b/content/public/browser/content_browser_client.h index cadbd44..614766cd 100644 --- a/content/public/browser/content_browser_client.h +++ b/content/public/browser/content_browser_client.h
@@ -45,7 +45,6 @@ #include "mojo/public/cpp/bindings/pending_remote.h" #include "mojo/public/cpp/bindings/remote.h" #include "mojo/public/cpp/system/message_pipe.h" -#include "ppapi/buildflags/buildflags.h" #include "services/cert_verifier/public/mojom/cert_verifier_service_factory.mojom-forward.h" #include "services/metrics/public/cpp/ukm_source_id.h" #include "services/network/public/mojom/ip_address_space.mojom-forward.h" @@ -240,7 +239,6 @@ struct GlobalRenderFrameHostId; struct GlobalRequestID; struct OpenURLParams; -struct PepperPluginInfo; struct Referrer; struct ServiceWorkerVersionBaseInfo; struct SocketPermissionRequest; @@ -2150,14 +2148,6 @@ // fullscreen when mock screen orientation changes. virtual bool CanEnterFullscreenWithoutUserActivation(); -#if BUILDFLAG(ENABLE_PLUGINS) - // Returns true if |embedder_origin| is allowed to embed a plugin described by - // |plugin_info|. This method allows restricting some internal plugins (like - // Chrome's PDF plugin) to specific origins. - virtual bool ShouldAllowPluginCreation(const url::Origin& embedder_origin, - const PepperPluginInfo& plugin_info); -#endif - #if BUILDFLAG(ENABLE_VR) // Allows the embedder to provide mechanisms to integrate with WebXR // functionality.
diff --git a/content/public/common/content_switch_dependent_feature_overrides.cc b/content/public/common/content_switch_dependent_feature_overrides.cc index 8edde5fc..96c1204 100644 --- a/content/public/common/content_switch_dependent_feature_overrides.cc +++ b/content/public/common/content_switch_dependent_feature_overrides.cc
@@ -66,9 +66,6 @@ std::cref(features::kBlockInsecurePrivateNetworkRequestsFromUnknown), base::FeatureList::OVERRIDE_ENABLE_FEATURE}, {switches::kEnableExperimentalWebPlatformFeatures, - std::cref(features::kPrivateNetworkAccessPermissionPrompt), - base::FeatureList::OVERRIDE_ENABLE_FEATURE}, - {switches::kEnableExperimentalWebPlatformFeatures, std::cref(features::kPrivateNetworkAccessForWorkers), base::FeatureList::OVERRIDE_ENABLE_FEATURE}, {switches::kEnableExperimentalWebPlatformFeatures,
diff --git a/content/public/common/user_agent.h b/content/public/common/user_agent.h index bf6f9af..ead4bc4 100644 --- a/content/public/common/user_agent.h +++ b/content/public/common/user_agent.h
@@ -13,24 +13,6 @@ namespace content { -namespace frozen_user_agent_strings { - -const char kTemplate[] = - "Mozilla/5.0 (%s) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/%s.0.0.0 " -#if BUILDFLAG(IS_ANDROID) - "%s" -#endif - "Safari/537.36"; - -const char kUnifiedPlatformAndroid[] = "Linux; Android 10; K"; -const char kUnifiedPlatformCrOS[] = "X11; CrOS x86_64 14541.0.0"; -const char kUnifiedPlatformFuchsia[] = "Fuchsia"; -const char kUnifiedPlatformLinux[] = "X11; Linux x86_64"; -const char kUnifiedPlatformMacOS[] = "Macintosh; Intel Mac OS X 10_15_7"; -const char kUnifiedPlatformWindows[] = "Windows NT 10.0; Win64; x64"; - -} // namespace frozen_user_agent_strings - enum class IncludeAndroidBuildNumber { Include, Exclude }; enum class IncludeAndroidModel { Include, Exclude }; @@ -77,9 +59,10 @@ CONTENT_EXPORT std::string GetReducedUserAgent(bool mobile, std::string major_version); -// Helper function to return the <unifiedPlatform> token of a reduced -// User-Agent header -CONTENT_EXPORT std::string GetUnifiedPlatform(); +// TODO(crbug.com/1257310): Remove this after user agent reduction phase 5 and +// --force-major-version-to-minor is removed. +// Return the <unifiedPlatform> token of a reduced User-Agent header. +CONTENT_EXPORT std::string GetUnifiedPlatformForTesting(); // Helper function to generate a full user agent string from a short // product name.
diff --git a/content/renderer/v8_value_converter_impl_unittest.cc b/content/renderer/v8_value_converter_impl_unittest.cc index 64980ac..b62c2452 100644 --- a/content/renderer/v8_value_converter_impl_unittest.cc +++ b/content/renderer/v8_value_converter_impl_unittest.cc
@@ -1021,9 +1021,10 @@ ASSERT_EQ(2u, list_result->GetListDeprecated().size()); for (size_t i = 0; i < list_result->GetListDeprecated().size(); ++i) { ASSERT_FALSE(IsNull(list_result.get(), i)); - base::DictionaryValue* dict_value = nullptr; - ASSERT_TRUE(list_result->GetDictionary(0u, &dict_value)); - EXPECT_EQ("same value", GetString(dict_value, "key")); + base::Value::Dict* dict_value = list_result->GetList()[0].GetIfDict(); + ; + ASSERT_TRUE(dict_value); + EXPECT_STREQ("same value", dict_value->FindString("key")->c_str()); } } }
diff --git a/content/test/data/first_party_sets/v0.init_too_old.sql b/content/test/data/first_party_sets/v0.init_too_old.sql index bf8a5ef..e4ffcdb 100644 --- a/content/test/data/first_party_sets/v0.init_too_old.sql +++ b/content/test/data/first_party_sets/v0.init_too_old.sql
@@ -9,12 +9,12 @@ CREATE INDEX idx_marked_at_run_sites ON sites_to_clear (marked_at_run); -CREATE TABLE IF NOT EXISTS profiles_cleared ( - profile TEXT PRIMARY KEY NOT NULL, +CREATE TABLE IF NOT EXISTS browser_contexts_cleared ( + browser_context_id TEXT PRIMARY KEY NOT NULL, cleared_at_run Integer NOT NULL ) WITHOUT ROWID; -CREATE INDEX idx_cleared_at_run_profiles ON profiles_cleared (cleared_at_run); +CREATE INDEX idx_cleared_at_run_browser_contexts ON browser_contexts_cleared (cleared_at_run); CREATE TABLE meta(key LONGVARCHAR NOT NULL UNIQUE PRIMARY KEY, value LONGVARCHAR); @@ -23,6 +23,6 @@ INSERT INTO meta VALUES('run_count','1'); INSERT INTO sites_to_clear VALUES('https://example.test', 1); -INSERT INTO profiles_cleared VALUES('p', 1); +INSERT INTO browser_contexts_cleared VALUES('p', 1); COMMIT; \ No newline at end of file
diff --git a/content/test/data/first_party_sets/v1.init_invalid_run_count.sql b/content/test/data/first_party_sets/v1.init_invalid_run_count.sql index e501123..af26f23 100644 --- a/content/test/data/first_party_sets/v1.init_invalid_run_count.sql +++ b/content/test/data/first_party_sets/v1.init_invalid_run_count.sql
@@ -9,12 +9,12 @@ CREATE INDEX idx_marked_at_run_sites ON sites_to_clear (marked_at_run); -CREATE TABLE IF NOT EXISTS profiles_cleared ( - profile TEXT PRIMARY KEY NOT NULL, +CREATE TABLE IF NOT EXISTS browser_contexts_cleared ( + browser_context_id TEXT PRIMARY KEY NOT NULL, cleared_at_run Integer NOT NULL ) WITHOUT ROWID; -CREATE INDEX idx_cleared_at_run_profiles ON profiles_cleared (cleared_at_run); +CREATE INDEX idx_cleared_at_run_browser_contexts ON browser_contexts_cleared (cleared_at_run); CREATE TABLE meta(key LONGVARCHAR NOT NULL UNIQUE PRIMARY KEY, value LONGVARCHAR); @@ -23,6 +23,6 @@ INSERT INTO meta VALUES('run_count','0'); INSERT INTO sites_to_clear VALUES('https://example.test', 2); -INSERT INTO profiles_cleared VALUES('p', 2); +INSERT INTO browser_contexts_cleared VALUES('p', 2); COMMIT; \ No newline at end of file
diff --git a/content/test/data/first_party_sets/v1.init_too_new.sql b/content/test/data/first_party_sets/v1.init_too_new.sql index 8a7048f..ef31021 100644 --- a/content/test/data/first_party_sets/v1.init_too_new.sql +++ b/content/test/data/first_party_sets/v1.init_too_new.sql
@@ -9,12 +9,12 @@ CREATE INDEX idx_marked_at_run_sites ON sites_to_clear (marked_at_run); -CREATE TABLE IF NOT EXISTS profiles_cleared ( - profile TEXT PRIMARY KEY NOT NULL, +CREATE TABLE IF NOT EXISTS browser_contexts_cleared ( + browser_context_id TEXT PRIMARY KEY NOT NULL, cleared_at_run Integer NOT NULL ) WITHOUT ROWID; -CREATE INDEX idx_cleared_at_run_profiles ON profiles_cleared (cleared_at_run); +CREATE INDEX idx_cleared_at_run_browser_contexts ON browser_contexts_cleared (cleared_at_run); CREATE TABLE meta(key LONGVARCHAR NOT NULL UNIQUE PRIMARY KEY, value LONGVARCHAR); @@ -23,6 +23,6 @@ INSERT INTO meta VALUES('run_count','2'); INSERT INTO sites_to_clear VALUES('https://example.test', 2); -INSERT INTO profiles_cleared VALUES('p', 2); +INSERT INTO browser_contexts_cleared VALUES('p', 2); COMMIT; \ No newline at end of file
diff --git a/content/test/data/first_party_sets/v1.sql b/content/test/data/first_party_sets/v1.sql index 444d75d..4a246f2d 100644 --- a/content/test/data/first_party_sets/v1.sql +++ b/content/test/data/first_party_sets/v1.sql
@@ -9,12 +9,12 @@ CREATE INDEX idx_marked_at_run_sites ON sites_to_clear (marked_at_run); -CREATE TABLE IF NOT EXISTS profiles_cleared ( - profile TEXT PRIMARY KEY NOT NULL, +CREATE TABLE IF NOT EXISTS browser_contexts_cleared ( + browser_context_id TEXT PRIMARY KEY NOT NULL, cleared_at_run Integer NOT NULL ) WITHOUT ROWID; -CREATE INDEX idx_cleared_at_run_profiles ON profiles_cleared (cleared_at_run); +CREATE INDEX idx_cleared_at_run_browser_contexts ON browser_contexts_cleared (cleared_at_run); CREATE TABLE meta(key LONGVARCHAR NOT NULL UNIQUE PRIMARY KEY, value LONGVARCHAR); @@ -23,6 +23,6 @@ INSERT INTO meta VALUES('run_count','1'); INSERT INTO sites_to_clear VALUES('https://example.test', 1); -INSERT INTO profiles_cleared VALUES('p', 1); +INSERT INTO browser_contexts_cleared VALUES('p', 1); COMMIT; \ No newline at end of file
diff --git a/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt index 8bc8c6e..765a75f 100644 --- a/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt +++ b/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt
@@ -303,6 +303,7 @@ # Still fails on Nexus 5 after all other Pixel_CanvasLowLatency* pass. crbug.com/1097752 [ android android-nexus-5 ] Pixel_CanvasLowLatencyWebGLAlphaFalse [ Failure ] +crbug.com/1097752 [ android android-nexus-5 ] Pixel_VideoStreamFromWebGLCanvas [ Failure ] # Fails on Fuchsia emulators crbug.com/1302427 [ fuchsia-board-qemu-x64 ] Pixel_CanvasLowLatencyWebGLAlphaFalse [ Failure ]
diff --git a/content/test/gpu/gpu_tests/webgl_conformance_integration_test.py b/content/test/gpu/gpu_tests/webgl_conformance_integration_test.py index df7720c..0e4e9c6 100644 --- a/content/test/gpu/gpu_tests/webgl_conformance_integration_test.py +++ b/content/test/gpu/gpu_tests/webgl_conformance_integration_test.py
@@ -69,8 +69,11 @@ SERIAL_TESTS = {} SERIAL_TEST_GLOBS = { - # crbug.com/1345466. + # crbug.com/1345466. Can be removed once OpenGL is no longer used on Mac. 'deqp/functional/gles3/transformfeedback/*', + # crbug.com/1345782. Can be removed once OpenGL is no longer used on Mac. + 'deqp/functional/gles3/texturefiltering/*', + 'deqp/functional/gles3/texturespecification/*', }
diff --git a/device/bluetooth/dbus/fake_bluetooth_device_client.cc b/device/bluetooth/dbus/fake_bluetooth_device_client.cc index 2647b85..9ee22add 100644 --- a/device/bluetooth/dbus/fake_bluetooth_device_client.cc +++ b/device/bluetooth/dbus/fake_bluetooth_device_client.cc
@@ -325,7 +325,8 @@ &FakeBluetoothDeviceClient::OnPropertyChanged, base::Unretained(this), dbus::ObjectPath(kPairedDevicePath))); properties->address.ReplaceValue(kPairedDeviceAddress); - properties->bluetooth_class.ReplaceValue(kPairedDeviceClass); + properties->bluetooth_class.ReplaceValue( + static_cast<int>(kPairedDeviceClass)); properties->name.ReplaceValue(kPairedDeviceName); properties->name.set_valid(true); properties->alias.ReplaceValue(kPairedDeviceAlias); @@ -909,238 +910,239 @@ base::Value FakeBluetoothDeviceClient::GetBluetoothDevicesAsDictionaries() const { - base::Value::ListStorage predefined_devices; + base::Value::List predefined_devices; - base::Value paired_device(base::Value::Type::DICTIONARY); - paired_device.SetStringKey("path", kPairedDevicePath); - paired_device.SetStringKey("address", kPairedDeviceAddress); - paired_device.SetStringKey("name", kPairedDeviceName); - paired_device.SetStringKey("alias", kPairedDeviceName); - paired_device.SetStringKey("pairingMethod", ""); - paired_device.SetStringKey("pairingAuthToken", ""); - paired_device.SetStringKey("pairingAction", ""); - paired_device.SetIntKey("classValue", kPairedDeviceClass); - paired_device.SetBoolKey("discoverable", true); - paired_device.SetBoolKey("isTrusted", true); - paired_device.SetBoolKey("paired", true); - paired_device.SetBoolKey("incoming", false); - predefined_devices.push_back(std::move(paired_device)); + base::Value::Dict paired_device; + paired_device.Set("path", kPairedDevicePath); + paired_device.Set("address", kPairedDeviceAddress); + paired_device.Set("name", kPairedDeviceName); + paired_device.Set("alias", kPairedDeviceName); + paired_device.Set("pairingMethod", ""); + paired_device.Set("pairingAuthToken", ""); + paired_device.Set("pairingAction", ""); + paired_device.Set("classValue", static_cast<int>(kPairedDeviceClass)); + paired_device.Set("discoverable", true); + paired_device.Set("isTrusted", true); + paired_device.Set("paired", true); + paired_device.Set("incoming", false); + predefined_devices.Append(std::move(paired_device)); - base::Value legacy_device(base::Value::Type::DICTIONARY); - legacy_device.SetStringKey("path", kLegacyAutopairPath); - legacy_device.SetStringKey("address", kLegacyAutopairAddress); - legacy_device.SetStringKey("name", kLegacyAutopairName); - legacy_device.SetStringKey("alias", kLegacyAutopairName); - legacy_device.SetStringKey("pairingMethod", ""); - legacy_device.SetStringKey("pairingAuthToken", ""); - legacy_device.SetStringKey("pairingAction", ""); - legacy_device.SetIntKey("classValue", kLegacyAutopairClass); - legacy_device.SetBoolKey("isTrusted", true); - legacy_device.SetBoolKey("discoverable", false); - legacy_device.SetBoolKey("paired", false); - legacy_device.SetBoolKey("incoming", false); - predefined_devices.push_back(std::move(legacy_device)); + base::Value::Dict legacy_device; + legacy_device.Set("path", kLegacyAutopairPath); + legacy_device.Set("address", kLegacyAutopairAddress); + legacy_device.Set("name", kLegacyAutopairName); + legacy_device.Set("alias", kLegacyAutopairName); + legacy_device.Set("pairingMethod", ""); + legacy_device.Set("pairingAuthToken", ""); + legacy_device.Set("pairingAction", ""); + legacy_device.Set("classValue", static_cast<int>(kLegacyAutopairClass)); + legacy_device.Set("isTrusted", true); + legacy_device.Set("discoverable", false); + legacy_device.Set("paired", false); + legacy_device.Set("incoming", false); + predefined_devices.Append(std::move(legacy_device)); - base::Value pin(base::Value::Type::DICTIONARY); - pin.SetStringKey("path", kDisplayPinCodePath); - pin.SetStringKey("address", kDisplayPinCodeAddress); - pin.SetStringKey("name", kDisplayPinCodeName); - pin.SetStringKey("alias", kDisplayPinCodeName); - pin.SetStringKey("pairingMethod", kPairingMethodPinCode); - pin.SetStringKey("pairingAuthToken", kTestPinCode); - pin.SetStringKey("pairingAction", kPairingActionDisplay); - pin.SetIntKey("classValue", kDisplayPinCodeClass); - pin.SetBoolKey("isTrusted", false); - pin.SetBoolKey("discoverable", false); - pin.SetBoolKey("paired", false); - pin.SetBoolKey("incoming", false); - predefined_devices.push_back(std::move(pin)); + base::Value::Dict pin; + pin.Set("path", kDisplayPinCodePath); + pin.Set("address", kDisplayPinCodeAddress); + pin.Set("name", kDisplayPinCodeName); + pin.Set("alias", kDisplayPinCodeName); + pin.Set("pairingMethod", kPairingMethodPinCode); + pin.Set("pairingAuthToken", kTestPinCode); + pin.Set("pairingAction", kPairingActionDisplay); + pin.Set("classValue", static_cast<int>(kDisplayPinCodeClass)); + pin.Set("isTrusted", false); + pin.Set("discoverable", false); + pin.Set("paired", false); + pin.Set("incoming", false); + predefined_devices.Append(std::move(pin)); - base::Value vanishing(base::Value::Type::DICTIONARY); - vanishing.SetStringKey("path", kVanishingDevicePath); - vanishing.SetStringKey("address", kVanishingDeviceAddress); - vanishing.SetStringKey("name", kVanishingDeviceName); - vanishing.SetStringKey("alias", kVanishingDeviceName); - vanishing.SetStringKey("pairingMethod", ""); - vanishing.SetStringKey("pairingAuthToken", ""); - vanishing.SetStringKey("pairingAction", ""); - vanishing.SetIntKey("classValue", kVanishingDeviceClass); - vanishing.SetBoolKey("isTrusted", false); - vanishing.SetBoolKey("discoverable", false); - vanishing.SetBoolKey("paired", false); - vanishing.SetBoolKey("incoming", false); - predefined_devices.push_back(std::move(vanishing)); + base::Value::Dict vanishing; + vanishing.Set("path", kVanishingDevicePath); + vanishing.Set("address", kVanishingDeviceAddress); + vanishing.Set("name", kVanishingDeviceName); + vanishing.Set("alias", kVanishingDeviceName); + vanishing.Set("pairingMethod", ""); + vanishing.Set("pairingAuthToken", ""); + vanishing.Set("pairingAction", ""); + vanishing.Set("classValue", static_cast<int>(kVanishingDeviceClass)); + vanishing.Set("isTrusted", false); + vanishing.Set("discoverable", false); + vanishing.Set("paired", false); + vanishing.Set("incoming", false); + predefined_devices.Append(std::move(vanishing)); - base::Value connect_unpairable(base::Value::Type::DICTIONARY); - connect_unpairable.SetStringKey("path", kConnectUnpairablePath); - connect_unpairable.SetStringKey("address", kConnectUnpairableAddress); - connect_unpairable.SetStringKey("name", kConnectUnpairableName); - connect_unpairable.SetStringKey("pairingMethod", ""); - connect_unpairable.SetStringKey("pairingAuthToken", ""); - connect_unpairable.SetStringKey("pairingAction", ""); - connect_unpairable.SetStringKey("alias", kConnectUnpairableName); - connect_unpairable.SetIntKey("classValue", kConnectUnpairableClass); - connect_unpairable.SetBoolKey("isTrusted", false); - connect_unpairable.SetBoolKey("discoverable", false); - connect_unpairable.SetBoolKey("paired", false); - connect_unpairable.SetBoolKey("incoming", false); - predefined_devices.push_back(std::move(connect_unpairable)); + base::Value::Dict connect_unpairable; + connect_unpairable.Set("path", kConnectUnpairablePath); + connect_unpairable.Set("address", kConnectUnpairableAddress); + connect_unpairable.Set("name", kConnectUnpairableName); + connect_unpairable.Set("pairingMethod", ""); + connect_unpairable.Set("pairingAuthToken", ""); + connect_unpairable.Set("pairingAction", ""); + connect_unpairable.Set("alias", kConnectUnpairableName); + connect_unpairable.Set("classValue", + static_cast<int>(kConnectUnpairableClass)); + connect_unpairable.Set("isTrusted", false); + connect_unpairable.Set("discoverable", false); + connect_unpairable.Set("paired", false); + connect_unpairable.Set("incoming", false); + predefined_devices.Append(std::move(connect_unpairable)); - base::Value passkey(base::Value::Type::DICTIONARY); - passkey.SetStringKey("path", kDisplayPasskeyPath); - passkey.SetStringKey("address", kDisplayPasskeyAddress); - passkey.SetStringKey("name", kDisplayPasskeyName); - passkey.SetStringKey("alias", kDisplayPasskeyName); - passkey.SetStringKey("pairingMethod", kPairingMethodPassKey); - passkey.SetIntKey("pairingAuthToken", kTestPassKey); - passkey.SetStringKey("pairingAction", kPairingActionDisplay); - passkey.SetIntKey("classValue", kDisplayPasskeyClass); - passkey.SetBoolKey("isTrusted", false); - passkey.SetBoolKey("discoverable", false); - passkey.SetBoolKey("paired", false); - passkey.SetBoolKey("incoming", false); - predefined_devices.push_back(std::move(passkey)); + base::Value::Dict passkey; + passkey.Set("path", kDisplayPasskeyPath); + passkey.Set("address", kDisplayPasskeyAddress); + passkey.Set("name", kDisplayPasskeyName); + passkey.Set("alias", kDisplayPasskeyName); + passkey.Set("pairingMethod", kPairingMethodPassKey); + passkey.Set("pairingAuthToken", kTestPassKey); + passkey.Set("pairingAction", kPairingActionDisplay); + passkey.Set("classValue", static_cast<int>(kDisplayPasskeyClass)); + passkey.Set("isTrusted", false); + passkey.Set("discoverable", false); + passkey.Set("paired", false); + passkey.Set("incoming", false); + predefined_devices.Append(std::move(passkey)); - base::Value request_pin(base::Value::Type::DICTIONARY); - request_pin.SetStringKey("path", kRequestPinCodePath); - request_pin.SetStringKey("address", kRequestPinCodeAddress); - request_pin.SetStringKey("name", kRequestPinCodeName); - request_pin.SetStringKey("alias", kRequestPinCodeName); - request_pin.SetStringKey("pairingMethod", ""); - request_pin.SetStringKey("pairingAuthToken", ""); - request_pin.SetStringKey("pairingAction", kPairingActionRequest); - request_pin.SetIntKey("classValue", kRequestPinCodeClass); - request_pin.SetBoolKey("isTrusted", false); - request_pin.SetBoolKey("discoverable", false); - request_pin.SetBoolKey("paired", false); - request_pin.SetBoolKey("incoming", false); - predefined_devices.push_back(std::move(request_pin)); + base::Value::Dict request_pin; + request_pin.Set("path", kRequestPinCodePath); + request_pin.Set("address", kRequestPinCodeAddress); + request_pin.Set("name", kRequestPinCodeName); + request_pin.Set("alias", kRequestPinCodeName); + request_pin.Set("pairingMethod", ""); + request_pin.Set("pairingAuthToken", ""); + request_pin.Set("pairingAction", kPairingActionRequest); + request_pin.Set("classValue", static_cast<int>(kRequestPinCodeClass)); + request_pin.Set("isTrusted", false); + request_pin.Set("discoverable", false); + request_pin.Set("paired", false); + request_pin.Set("incoming", false); + predefined_devices.Append(std::move(request_pin)); - base::Value confirm(base::Value::Type::DICTIONARY); - confirm.SetStringKey("path", kConfirmPasskeyPath); - confirm.SetStringKey("address", kConfirmPasskeyAddress); - confirm.SetStringKey("name", kConfirmPasskeyName); - confirm.SetStringKey("alias", kConfirmPasskeyName); - confirm.SetStringKey("pairingMethod", ""); - confirm.SetIntKey("pairingAuthToken", kTestPassKey); - confirm.SetStringKey("pairingAction", kPairingActionConfirmation); - confirm.SetIntKey("classValue", kConfirmPasskeyClass); - confirm.SetBoolKey("isTrusted", false); - confirm.SetBoolKey("discoverable", false); - confirm.SetBoolKey("paired", false); - confirm.SetBoolKey("incoming", false); - predefined_devices.push_back(std::move(confirm)); + base::Value::Dict confirm; + confirm.Set("path", kConfirmPasskeyPath); + confirm.Set("address", kConfirmPasskeyAddress); + confirm.Set("name", kConfirmPasskeyName); + confirm.Set("alias", kConfirmPasskeyName); + confirm.Set("pairingMethod", ""); + confirm.Set("pairingAuthToken", kTestPassKey); + confirm.Set("pairingAction", kPairingActionConfirmation); + confirm.Set("classValue", static_cast<int>(kConfirmPasskeyClass)); + confirm.Set("isTrusted", false); + confirm.Set("discoverable", false); + confirm.Set("paired", false); + confirm.Set("incoming", false); + predefined_devices.Append(std::move(confirm)); - base::Value request_passkey(base::Value::Type::DICTIONARY); - request_passkey.SetStringKey("path", kRequestPasskeyPath); - request_passkey.SetStringKey("address", kRequestPasskeyAddress); - request_passkey.SetStringKey("name", kRequestPasskeyName); - request_passkey.SetStringKey("alias", kRequestPasskeyName); - request_passkey.SetStringKey("pairingMethod", kPairingMethodPassKey); - request_passkey.SetStringKey("pairingAction", kPairingActionRequest); - request_passkey.SetIntKey("pairingAuthToken", kTestPassKey); - request_passkey.SetIntKey("classValue", kRequestPasskeyClass); - request_passkey.SetBoolKey("isTrusted", false); - request_passkey.SetBoolKey("discoverable", false); - request_passkey.SetBoolKey("paired", false); - request_passkey.SetBoolKey("incoming", false); - predefined_devices.push_back(std::move(request_passkey)); + base::Value::Dict request_passkey; + request_passkey.Set("path", kRequestPasskeyPath); + request_passkey.Set("address", kRequestPasskeyAddress); + request_passkey.Set("name", kRequestPasskeyName); + request_passkey.Set("alias", kRequestPasskeyName); + request_passkey.Set("pairingMethod", kPairingMethodPassKey); + request_passkey.Set("pairingAction", kPairingActionRequest); + request_passkey.Set("pairingAuthToken", kTestPassKey); + request_passkey.Set("classValue", static_cast<int>(kRequestPasskeyClass)); + request_passkey.Set("isTrusted", false); + request_passkey.Set("discoverable", false); + request_passkey.Set("paired", false); + request_passkey.Set("incoming", false); + predefined_devices.Append(std::move(request_passkey)); - base::Value unconnectable(base::Value::Type::DICTIONARY); - unconnectable.SetStringKey("path", kUnconnectableDevicePath); - unconnectable.SetStringKey("address", kUnconnectableDeviceAddress); - unconnectable.SetStringKey("name", kUnconnectableDeviceName); - unconnectable.SetStringKey("alias", kUnconnectableDeviceName); - unconnectable.SetStringKey("pairingMethod", ""); - unconnectable.SetStringKey("pairingAuthToken", ""); - unconnectable.SetStringKey("pairingAction", ""); - unconnectable.SetIntKey("classValue", kUnconnectableDeviceClass); - unconnectable.SetBoolKey("isTrusted", true); - unconnectable.SetBoolKey("discoverable", false); - unconnectable.SetBoolKey("paired", false); - unconnectable.SetBoolKey("incoming", false); - predefined_devices.push_back(std::move(unconnectable)); + base::Value::Dict unconnectable; + unconnectable.Set("path", kUnconnectableDevicePath); + unconnectable.Set("address", kUnconnectableDeviceAddress); + unconnectable.Set("name", kUnconnectableDeviceName); + unconnectable.Set("alias", kUnconnectableDeviceName); + unconnectable.Set("pairingMethod", ""); + unconnectable.Set("pairingAuthToken", ""); + unconnectable.Set("pairingAction", ""); + unconnectable.Set("classValue", static_cast<int>(kUnconnectableDeviceClass)); + unconnectable.Set("isTrusted", true); + unconnectable.Set("discoverable", false); + unconnectable.Set("paired", false); + unconnectable.Set("incoming", false); + predefined_devices.Append(std::move(unconnectable)); - base::Value unpairable(base::Value::Type::DICTIONARY); - unpairable.SetStringKey("path", kUnpairableDevicePath); - unpairable.SetStringKey("address", kUnpairableDeviceAddress); - unpairable.SetStringKey("name", kUnpairableDeviceName); - unpairable.SetStringKey("alias", kUnpairableDeviceName); - unpairable.SetStringKey("pairingMethod", ""); - unpairable.SetStringKey("pairingAuthToken", ""); - unpairable.SetStringKey("pairingAction", kPairingActionFail); - unpairable.SetIntKey("classValue", kUnpairableDeviceClass); - unpairable.SetBoolKey("isTrusted", false); - unpairable.SetBoolKey("discoverable", false); - unpairable.SetBoolKey("paired", false); - unpairable.SetBoolKey("incoming", false); - predefined_devices.push_back(std::move(unpairable)); + base::Value::Dict unpairable; + unpairable.Set("path", kUnpairableDevicePath); + unpairable.Set("address", kUnpairableDeviceAddress); + unpairable.Set("name", kUnpairableDeviceName); + unpairable.Set("alias", kUnpairableDeviceName); + unpairable.Set("pairingMethod", ""); + unpairable.Set("pairingAuthToken", ""); + unpairable.Set("pairingAction", kPairingActionFail); + unpairable.Set("classValue", static_cast<int>(kUnpairableDeviceClass)); + unpairable.Set("isTrusted", false); + unpairable.Set("discoverable", false); + unpairable.Set("paired", false); + unpairable.Set("incoming", false); + predefined_devices.Append(std::move(unpairable)); - base::Value just_works(base::Value::Type::DICTIONARY); - just_works.SetStringKey("path", kJustWorksPath); - just_works.SetStringKey("address", kJustWorksAddress); - just_works.SetStringKey("name", kJustWorksName); - just_works.SetStringKey("alias", kJustWorksName); - just_works.SetStringKey("pairingMethod", ""); - just_works.SetStringKey("pairingAuthToken", ""); - just_works.SetStringKey("pairingAction", ""); - just_works.SetIntKey("classValue", kJustWorksClass); - just_works.SetBoolKey("isTrusted", false); - just_works.SetBoolKey("discoverable", false); - just_works.SetBoolKey("paired", false); - just_works.SetBoolKey("incoming", false); - predefined_devices.push_back(std::move(just_works)); + base::Value::Dict just_works; + just_works.Set("path", kJustWorksPath); + just_works.Set("address", kJustWorksAddress); + just_works.Set("name", kJustWorksName); + just_works.Set("alias", kJustWorksName); + just_works.Set("pairingMethod", ""); + just_works.Set("pairingAuthToken", ""); + just_works.Set("pairingAction", ""); + just_works.Set("classValue", static_cast<int>(kJustWorksClass)); + just_works.Set("isTrusted", false); + just_works.Set("discoverable", false); + just_works.Set("paired", false); + just_works.Set("incoming", false); + predefined_devices.Append(std::move(just_works)); - base::Value low_energy(base::Value::Type::DICTIONARY); - low_energy.SetStringKey("path", kLowEnergyPath); - low_energy.SetStringKey("address", kLowEnergyAddress); - low_energy.SetStringKey("name", kLowEnergyName); - low_energy.SetStringKey("alias", kLowEnergyName); - low_energy.SetStringKey("pairingMethod", ""); - low_energy.SetStringKey("pairingAuthToken", ""); - low_energy.SetStringKey("pairingAction", ""); - low_energy.SetIntKey("classValue", kLowEnergyClass); - low_energy.SetBoolKey("isTrusted", false); - low_energy.SetBoolKey("discoverable", false); - low_energy.SetBoolKey("paireed", false); - low_energy.SetBoolKey("incoming", false); - predefined_devices.push_back(std::move(low_energy)); + base::Value::Dict low_energy; + low_energy.Set("path", kLowEnergyPath); + low_energy.Set("address", kLowEnergyAddress); + low_energy.Set("name", kLowEnergyName); + low_energy.Set("alias", kLowEnergyName); + low_energy.Set("pairingMethod", ""); + low_energy.Set("pairingAuthToken", ""); + low_energy.Set("pairingAction", ""); + low_energy.Set("classValue", static_cast<int>(kLowEnergyClass)); + low_energy.Set("isTrusted", false); + low_energy.Set("discoverable", false); + low_energy.Set("paireed", false); + low_energy.Set("incoming", false); + predefined_devices.Append(std::move(low_energy)); - base::Value paired_unconnectable(base::Value::Type::DICTIONARY); - paired_unconnectable.SetStringKey("path", kPairedUnconnectableDevicePath); - paired_unconnectable.SetStringKey("address", - kPairedUnconnectableDeviceAddress); - paired_unconnectable.SetStringKey("name", kPairedUnconnectableDeviceName); - paired_unconnectable.SetStringKey("pairingMethod", ""); - paired_unconnectable.SetStringKey("pairingAuthToken", ""); - paired_unconnectable.SetStringKey("pairingAction", ""); - paired_unconnectable.SetStringKey("alias", kPairedUnconnectableDeviceName); - paired_unconnectable.SetIntKey("classValue", kPairedUnconnectableDeviceClass); - paired_unconnectable.SetBoolKey("isTrusted", false); - paired_unconnectable.SetBoolKey("discoverable", true); - paired_unconnectable.SetBoolKey("paired", true); - paired_unconnectable.SetBoolKey("incoming", false); - predefined_devices.push_back(std::move(paired_unconnectable)); + base::Value::Dict paired_unconnectable; + paired_unconnectable.Set("path", kPairedUnconnectableDevicePath); + paired_unconnectable.Set("address", kPairedUnconnectableDeviceAddress); + paired_unconnectable.Set("name", kPairedUnconnectableDeviceName); + paired_unconnectable.Set("pairingMethod", ""); + paired_unconnectable.Set("pairingAuthToken", ""); + paired_unconnectable.Set("pairingAction", ""); + paired_unconnectable.Set("alias", kPairedUnconnectableDeviceName); + paired_unconnectable.Set("classValue", + static_cast<int>(kPairedUnconnectableDeviceClass)); + paired_unconnectable.Set("isTrusted", false); + paired_unconnectable.Set("discoverable", true); + paired_unconnectable.Set("paired", true); + paired_unconnectable.Set("incoming", false); + predefined_devices.Append(std::move(paired_unconnectable)); - base::Value connected_trusted_not_paired(base::Value::Type::DICTIONARY); - connected_trusted_not_paired.SetStringKey( - "path", kConnectedTrustedNotPairedDevicePath); - connected_trusted_not_paired.SetStringKey( - "address", kConnectedTrustedNotPairedDeviceAddress); - connected_trusted_not_paired.SetStringKey( - "name", kConnectedTrustedNotPairedDeviceName); - connected_trusted_not_paired.SetStringKey("pairingMethod", ""); - connected_trusted_not_paired.SetStringKey("pairingAuthToken", ""); - connected_trusted_not_paired.SetStringKey("pairingAction", ""); - connected_trusted_not_paired.SetStringKey( - "alias", kConnectedTrustedNotPairedDeviceName); - connected_trusted_not_paired.SetIntKey("classValue", - kConnectedTrustedNotPairedDeviceClass); - connected_trusted_not_paired.SetBoolKey("isTrusted", true); - connected_trusted_not_paired.SetBoolKey("discoverable", true); - connected_trusted_not_paired.SetBoolKey("paired", false); - connected_trusted_not_paired.SetBoolKey("incoming", false); - predefined_devices.push_back(std::move(connected_trusted_not_paired)); + base::Value::Dict connected_trusted_not_paired; + connected_trusted_not_paired.Set("path", + kConnectedTrustedNotPairedDevicePath); + connected_trusted_not_paired.Set("address", + kConnectedTrustedNotPairedDeviceAddress); + connected_trusted_not_paired.Set("name", + kConnectedTrustedNotPairedDeviceName); + connected_trusted_not_paired.Set("pairingMethod", ""); + connected_trusted_not_paired.Set("pairingAuthToken", ""); + connected_trusted_not_paired.Set("pairingAction", ""); + connected_trusted_not_paired.Set("alias", + kConnectedTrustedNotPairedDeviceName); + connected_trusted_not_paired.Set( + "classValue", static_cast<int>(kConnectedTrustedNotPairedDeviceClass)); + connected_trusted_not_paired.Set("isTrusted", true); + connected_trusted_not_paired.Set("discoverable", true); + connected_trusted_not_paired.Set("paired", false); + connected_trusted_not_paired.Set("incoming", false); + predefined_devices.Append(std::move(connected_trusted_not_paired)); return base::Value(std::move(predefined_devices)); }
diff --git a/device/bluetooth/dbus/fake_bluetooth_device_client.h b/device/bluetooth/dbus/fake_bluetooth_device_client.h index 8b4ef62..1d84f4cdb 100644 --- a/device/bluetooth/dbus/fake_bluetooth_device_client.h +++ b/device/bluetooth/dbus/fake_bluetooth_device_client.h
@@ -146,9 +146,8 @@ void CreateDeviceWithProperties(const dbus::ObjectPath& adapter_path, const IncomingDeviceProperties& props); - // Creates and returns a list of std::unique_ptr<base::DictionaryValue> - // objects, which contain all the data from the constants for devices with - // predefined behavior. + // Creates and returns a list of dictionary objects as a Value, which contain + // all the data from the constants for devices with predefined behavior. base::Value GetBluetoothDevicesAsDictionaries() const; SimulatedPairingOptions* GetPairingOptions(
diff --git a/extensions/common/extension_builder.h b/extensions/common/extension_builder.h index bbe2596..227e631b 100644 --- a/extensions/common/extension_builder.h +++ b/extensions/common/extension_builder.h
@@ -8,6 +8,7 @@ #include <initializer_list> #include <memory> #include <string> +#include <utility> #include "base/files/file_path.h" #include "base/memory/ref_counted.h" @@ -113,15 +114,15 @@ // Can be used in conjuction with ListBuilder and DictionaryBuilder for more // complex types. template <typename T> - ExtensionBuilder& SetManifestKey(base::StringPiece key, T value) { - SetManifestKeyImpl(key, base::Value(value)); + ExtensionBuilder& SetManifestKey(base::StringPiece key, T&& value) { + SetManifestKeyImpl(key, base::Value(std::forward<T>(value))); return *this; } template <typename T> ExtensionBuilder& SetManifestPath( std::initializer_list<base::StringPiece> path, - T value) { - SetManifestPathImpl(path, base::Value(value)); + T&& value) { + SetManifestPathImpl(path, base::Value(std::forward<T>(value))); return *this; } // Specializations for unique_ptr<> to allow passing unique_ptr<base::Value>.
diff --git a/fuchsia_web/webengine/BUILD.gn b/fuchsia_web/webengine/BUILD.gn index f39a256..76607982 100644 --- a/fuchsia_web/webengine/BUILD.gn +++ b/fuchsia_web/webengine/BUILD.gn
@@ -17,6 +17,11 @@ defines = [ "WEB_ENGINE_IMPLEMENTATION" ] } +source_set("web_engine_export_from_implementation") { + public = [ "web_engine_export.h" ] + public_configs = [ ":web_engine_implementation" ] +} + fidl_library("fidl") { library_name = "chromium.internal" sources = [ "fidl/dev_tools.fidl" ] @@ -79,8 +84,10 @@ component("web_engine_core") { deps = [ + ":context_provider", ":fidl", ":switches", + ":web_engine_export_from_implementation", "//base", "//base:base_static", "//components/cast/message_port:message_port_fuchsia", @@ -120,10 +127,6 @@ "//content/public/renderer", "//fuchsia_web/common", "//fuchsia_web/webengine/mojom", - - # TODO(crbug.com/1081525): Move context_provider to its own target and move - # this deps there. - "//fuchsia_web/webinstance_host", "//google_apis", "//gpu/command_buffer/service", "//media", @@ -234,8 +237,6 @@ "//third_party/fuchsia-sdk/sdk/pkg/inspect", ] - configs += [ ":web_engine_implementation" ] - sources = [ "browser/accessibility_bridge.cc", "browser/accessibility_bridge.h", @@ -301,10 +302,6 @@ "common/cors_exempt_headers.h", "common/web_engine_content_client.cc", "common/web_engine_content_client.h", - "context_provider_impl.cc", - "context_provider_impl.h", - "context_provider_main.cc", - "context_provider_main.h", "renderer/web_engine_audio_device_factory.cc", "renderer/web_engine_audio_device_factory.h", "renderer/web_engine_audio_renderer.cc", @@ -317,12 +314,30 @@ "renderer/web_engine_render_frame_observer.h", "renderer/web_engine_url_loader_throttle_provider.cc", "renderer/web_engine_url_loader_throttle_provider.h", - "web_engine_export.h", "web_engine_main_delegate.cc", "web_engine_main_delegate.h", ] } +source_set("context_provider") { + sources = [ + "context_provider_impl.cc", + "context_provider_impl.h", + "context_provider_main.cc", + "context_provider_main.h", + ] + deps = [ + ":fidl", + ":web_engine_export_from_implementation", + "//base", + "//components/fuchsia_component_support", + "//fuchsia_web/common", + "//fuchsia_web/webinstance_host", + "//third_party/fuchsia-sdk/sdk/pkg/sys_cpp", + "//third_party/fuchsia-sdk/sdk/pkg/sys_inspect_cpp", + ] +} + # TODO(crbug.com/1081525): Rename to features_and_switches or collapse into # common. Consider moving these and other files in engine/ to common/ or # elsewhere. @@ -338,6 +353,7 @@ executable("web_engine_exe") { deps = [ + ":context_provider", ":switches", ":web_engine_core", "//base", @@ -589,6 +605,7 @@ "test/run_all_unittests.cc", ] deps = [ + ":context_provider", ":switches", ":web_engine_core", ":web_engine_unittests_fake_instance_manifest",
diff --git a/fuchsia_web/webinstance_host/BUILD.gn b/fuchsia_web/webinstance_host/BUILD.gn index d0e156a..4ca22bcfd 100644 --- a/fuchsia_web/webinstance_host/BUILD.gn +++ b/fuchsia_web/webinstance_host/BUILD.gn
@@ -12,9 +12,7 @@ # WebEngine clients that instantiate WebInstances directly. "//fuchsia_web/runners/*", "//fuchsia_web/shell:web_engine_shell_exec", - - # TODO(crbug.com/1081525): Change to the context_provider target when created. - "//fuchsia_web/webengine:web_engine_core", + "//fuchsia_web/webengine:context_provider", # TODO(crbug.com/1081525): Move dependent tests into this directory and # source_sets that have these deps.
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.cc b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.cc index b23732b..ff99584 100644 --- a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.cc +++ b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.cc
@@ -134,18 +134,35 @@ GLfloat clear_color_[4]; }; -class ScopedColorMaskReset { +// Reset the color mask for buffer zero only. +class ScopedColorMaskZeroReset { public: - explicit ScopedColorMaskReset(gl::GLApi* api) : api_(api) { - api_->glGetBooleanvFn(GL_COLOR_WRITEMASK, color_mask_); + explicit ScopedColorMaskZeroReset(gl::GLApi* api, + bool oes_draw_buffers_indexed) + : api_(api), oes_draw_buffers_indexed_(oes_draw_buffers_indexed) { + if (oes_draw_buffers_indexed_) { + GLsizei length = 0; + api_->glGetBooleani_vRobustANGLEFn( + GL_COLOR_WRITEMASK, 0, sizeof(color_mask_), &length, color_mask_); + } else { + api_->glGetBooleanvFn(GL_COLOR_WRITEMASK, color_mask_); + } } - ~ScopedColorMaskReset() { - api_->glColorMaskFn(color_mask_[0], color_mask_[1], color_mask_[2], - color_mask_[3]); + ~ScopedColorMaskZeroReset() { + if (oes_draw_buffers_indexed_) { + api_->glColorMaskiOESFn(0, color_mask_[0], color_mask_[1], color_mask_[2], + color_mask_[3]); + } else { + api_->glColorMaskFn(color_mask_[0], color_mask_[1], color_mask_[2], + color_mask_[3]); + } } private: raw_ptr<gl::GLApi> api_; + const bool oes_draw_buffers_indexed_; + // The color mask, or the color mask of buffer zero, if + // OES_draw_buffers_indexed is enabled. GLboolean color_mask_[4]; }; @@ -399,13 +416,14 @@ PassthroughResources::SharedImageData::SharedImageData() = default; PassthroughResources::SharedImageData::SharedImageData( std::unique_ptr<GLTexturePassthroughImageRepresentation> representation, - gl::GLApi* api) + gl::GLApi* api, + const FeatureInfo* feature_info) : representation_(std::move(representation)) { DCHECK(representation_); // Note, that ideally we could defer clear till BeginAccess, but there is no // enforcement that will require clients to call Begin/End access. - EnsureClear(api); + EnsureClear(api, feature_info); } PassthroughResources::SharedImageData::SharedImageData( SharedImageData&& other) = default; @@ -418,7 +436,9 @@ return *this; } -void PassthroughResources::SharedImageData::EnsureClear(gl::GLApi* api) { +void PassthroughResources::SharedImageData::EnsureClear( + gl::GLApi* api, + const FeatureInfo* feature_info) { // To avoid unnessary overhead we don't enable robust initialization on shared // gl context where all shared images are created, so we clear image here if // necessary. @@ -432,13 +452,16 @@ return; auto texture = representation_->GetTexturePassthrough(); + const bool use_oes_draw_buffers_indexed = + feature_info->feature_flags().oes_draw_buffers_indexed; // Back up all state we are about to change. ScopedFramebufferBindingReset fbo_reset( api, false /* supports_seperate_fbo_bindings */); ScopedTextureBindingReset texture_reset(api, texture->target()); ScopedClearColorReset clear_color_reset(api); - ScopedColorMaskReset color_mask_reset(api); + ScopedColorMaskZeroReset color_mask_reset(api, + use_oes_draw_buffers_indexed); ScopedScissorTestReset scissor_test_reset(api); // Generate a new framebuffer and bind the shared image's uncleared texture @@ -452,7 +475,10 @@ 0); // Clear the bound framebuffer. api->glClearColorFn(0, 0, 0, 0); - api->glColorMaskFn(true, true, true, true); + if (use_oes_draw_buffers_indexed) + api->glColorMaskiOESFn(0, true, true, true, true); + else + api->glColorMaskFn(true, true, true, true); api->glDisableFn(GL_SCISSOR_TEST); api->glClearFn(GL_COLOR_BUFFER_BIT);
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.h b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.h index c09f1a4..6d48a0d 100644 --- a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.h +++ b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.h
@@ -99,7 +99,8 @@ SharedImageData(); explicit SharedImageData( std::unique_ptr<GLTexturePassthroughImageRepresentation> representation, - gl::GLApi* api); + gl::GLApi* api, + const FeatureInfo* feature_info); SharedImageData(SharedImageData&& other); SharedImageData(const SharedImageData&) = delete; @@ -112,7 +113,7 @@ return representation_.get(); } - void EnsureClear(gl::GLApi* api); + void EnsureClear(gl::GLApi* api, const FeatureInfo* feature_info); bool BeginAccess(GLenum mode, gl::GLApi* api);
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doers.cc b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doers.cc index 7b23f76..d962cbb 100644 --- a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doers.cc +++ b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doers.cc
@@ -4885,7 +4885,8 @@ resources_->texture_object_map.RemoveClientID(texture_client_id); resources_->texture_object_map.SetIDMapping(texture_client_id, texture); resources_->texture_shared_image_map[texture_client_id] = - PassthroughResources::SharedImageData(std::move(shared_image), api()); + PassthroughResources::SharedImageData(std::move(shared_image), api(), + feature_info_.get()); return error::kNoError; }
diff --git a/headless/BUILD.gn b/headless/BUILD.gn index cb2a88ebc..0bf719b 100644 --- a/headless/BUILD.gn +++ b/headless/BUILD.gn
@@ -490,7 +490,7 @@ if (enable_basic_printing) { deps += [ "//components/printing/browser", - "//components/printing/browser/print_to_pdf", + "//components/printing/browser/headless", "//components/printing/common:mojo_interfaces", "//printing", "//printing/mojom", @@ -747,7 +747,7 @@ if (enable_basic_printing) { deps += [ "//components/printing/browser", - "//components/printing/browser/print_to_pdf", + "//components/printing/browser/headless", "//printing", "//printing/buildflags", "//third_party/blink/public:blink",
diff --git a/headless/lib/browser/headless_content_browser_client.cc b/headless/lib/browser/headless_content_browser_client.cc index e2b2cd34..9bc8593 100644 --- a/headless/lib/browser/headless_content_browser_client.cc +++ b/headless/lib/browser/headless_content_browser_client.cc
@@ -61,7 +61,7 @@ #endif // defined(HEADLESS_USE_POLICY) #if BUILDFLAG(ENABLE_PRINTING) -#include "components/printing/browser/print_to_pdf/pdf_print_manager.h" +#include "components/printing/browser/headless/headless_print_manager.h" #endif // defined(ENABLE_PRINTING) namespace headless { @@ -153,8 +153,8 @@ [](content::RenderFrameHost* render_frame_host, mojo::PendingAssociatedReceiver<printing::mojom::PrintManagerHost> receiver) { - print_to_pdf::PdfPrintManager::BindPrintManagerHost(std::move(receiver), - render_frame_host); + HeadlessPrintManager::BindPrintManagerHost(std::move(receiver), + render_frame_host); }, &render_frame_host)); #endif
diff --git a/headless/lib/browser/headless_web_contents_impl.cc b/headless/lib/browser/headless_web_contents_impl.cc index 807879aa..aa48857 100644 --- a/headless/lib/browser/headless_web_contents_impl.cc +++ b/headless/lib/browser/headless_web_contents_impl.cc
@@ -51,7 +51,7 @@ #include "ui/gfx/switches.h" #if BUILDFLAG(ENABLE_PRINTING) -#include "components/printing/browser/print_to_pdf/pdf_print_manager.h" +#include "components/printing/browser/headless/headless_print_manager.h" #endif namespace headless { @@ -314,8 +314,7 @@ agent_host_( content::DevToolsAgentHost::GetOrCreateFor(web_contents_.get())) { #if BUILDFLAG(ENABLE_PRINTING) - print_to_pdf::PdfPrintManager::CreateForWebContents(web_contents_.get()); -// TODO(weili): Add support for printing OOPIFs. + HeadlessPrintManager::CreateForWebContents(web_contents_.get()); #endif UpdatePrefsFromSystemSettings(web_contents_->GetMutableRendererPrefs()); web_contents_->GetMutableRendererPrefs()->accept_languages =
diff --git a/headless/lib/browser/protocol/page_handler.cc b/headless/lib/browser/protocol/page_handler.cc index edcbbcd..aff24baee 100644 --- a/headless/lib/browser/protocol/page_handler.cc +++ b/headless/lib/browser/protocol/page_handler.cc
@@ -8,7 +8,6 @@ #include "content/public/browser/web_contents.h" #if BUILDFLAG(ENABLE_PRINTING) -#include "components/printing/browser/print_to_pdf/pdf_print_result.h" #include "components/printing/browser/print_to_pdf/pdf_print_utils.h" #include "third_party/abseil-cpp/absl/types/optional.h" #endif @@ -90,7 +89,7 @@ bool return_as_stream = transfer_mode.fromMaybe("") == Page::PrintToPDF::TransferModeEnum::ReturnAsStream; - print_to_pdf::PdfPrintManager::FromWebContents(web_contents_.get()) + HeadlessPrintManager::FromWebContents(web_contents_.get()) ->PrintToPdf( web_contents_->GetPrimaryMainFrame(), page_ranges.fromMaybe(""), std::move(absl::get<printing::mojom::PrintPagesParamsPtr>(
diff --git a/headless/lib/browser/protocol/page_handler.h b/headless/lib/browser/protocol/page_handler.h index 59c47475..99172c7 100644 --- a/headless/lib/browser/protocol/page_handler.h +++ b/headless/lib/browser/protocol/page_handler.h
@@ -12,7 +12,7 @@ #include "printing/buildflags/buildflags.h" #if BUILDFLAG(ENABLE_PRINTING) -#include "components/printing/browser/print_to_pdf/pdf_print_manager.h" +#include "components/printing/browser/headless/headless_print_manager.h" #include "components/printing/browser/print_to_pdf/pdf_print_result.h" #include "headless/public/headless_export.h" #endif
diff --git "a/infra/config/generated/builders/ci/mac-arm64-rel \050reclient shadow\051/properties.json" "b/infra/config/generated/builders/ci/mac-arm64-rel \050reclient shadow\051/properties.json" new file mode 100644 index 0000000..da5d62c --- /dev/null +++ "b/infra/config/generated/builders/ci/mac-arm64-rel \050reclient shadow\051/properties.json"
@@ -0,0 +1,55 @@ +{ + "$build/chromium_tests_builder_config": { + "builder_config": { + "builder_db": { + "entries": [ + { + "builder_id": { + "bucket": "ci", + "builder": "mac-arm64-rel (reclient shadow)", + "project": "chromium" + }, + "builder_spec": { + "builder_group": "chromium.mac", + "execution_mode": "COMPILE_AND_TEST", + "legacy_chromium_config": { + "apply_configs": [ + "mb" + ], + "build_config": "Release", + "config": "chromium", + "target_arch": "arm", + "target_bits": 64, + "target_platform": "mac" + }, + "legacy_gclient_config": { + "config": "chromium" + } + } + } + ] + }, + "builder_ids": [ + { + "bucket": "ci", + "builder": "mac-arm64-rel (reclient shadow)", + "project": "chromium" + } + ] + } + }, + "$build/reclient": { + "instance": "rbe-chromium-trusted", + "jobs": 40, + "metrics_project": "chromium-reclient-metrics" + }, + "$recipe_engine/resultdb/test_presentation": { + "column_keys": [], + "grouping_keys": [ + "status", + "v.test_suite" + ] + }, + "builder_group": "chromium.mac", + "recipe": "chromium" +} \ No newline at end of file
diff --git a/infra/config/generated/luci/cr-buildbucket.cfg b/infra/config/generated/luci/cr-buildbucket.cfg index ce2d9ee..87799dce 100644 --- a/infra/config/generated/luci/cr-buildbucket.cfg +++ b/infra/config/generated/luci/cr-buildbucket.cfg
@@ -15866,6 +15866,75 @@ } } builders { + name: "Mac deterministic (reclient shadow)" + swarming_host: "chromium-swarm.appspot.com" + dimensions: "builder:Mac deterministic (reclient shadow)" + dimensions: "cpu:x86-64" + dimensions: "os:Mac-12" + dimensions: "pool:luci.chromium.ci" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/reclient": {' + ' "instance": "rbe-chromium-trusted",' + ' "jobs": 40,' + ' "metrics_project": "chromium-reclient-metrics"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "chromium.fyi",' + ' "recipe": "swarming/deterministic_build"' + '}' + execution_timeout_secs: 21600 + build_numbers: YES + service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "chrome-luci-data" + dataset: "chromium" + table: "ci_test_results" + test_results {} + } + bq_exports { + project: "chrome-luci-data" + dataset: "chromium" + table: "gpu_ci_test_results" + test_results { + predicate { + test_id_regexp: "ninja://chrome/test:telemetry_gpu_integration_test[^/]*/.+" + } + } + } + bq_exports { + project: "chrome-luci-data" + dataset: "chromium" + table: "blink_web_tests_ci_test_results" + test_results { + predicate { + test_id_regexp: "ninja://[^/]*blink_web_tests/.+" + } + } + } + history_options { + use_invocation_timestamp: true + } + } + } + builders { name: "Mac10.13 Tests" swarming_host: "chromium-swarm.appspot.com" dimensions: "builderless:1" @@ -39487,6 +39556,83 @@ } } builders { + name: "mac-arm64-rel (reclient shadow)" + swarming_host: "chromium-swarm.appspot.com" + dimensions: "builder:mac-arm64-rel (reclient shadow)" + dimensions: "cpu:x86-64" + dimensions: "os:Mac-12" + 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-arm64-rel (reclient shadow)/properties.json",' + ' "top_level_project": {' + ' "ref": "refs/heads/main",' + ' "repo": {' + ' "host": "chromium.googlesource.com",' + ' "project": "chromium/src"' + ' }' + ' }' + ' },' + ' "builder_group": "chromium.mac",' + ' "led_builder_is_bootstrapped": true,' + ' "recipe": "chromium"' + '}' + execution_timeout_secs: 10800 + build_numbers: YES + service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "chrome-luci-data" + dataset: "chromium" + table: "ci_test_results" + test_results {} + } + bq_exports { + project: "chrome-luci-data" + dataset: "chromium" + table: "gpu_ci_test_results" + test_results { + predicate { + test_id_regexp: "ninja://chrome/test:telemetry_gpu_integration_test[^/]*/.+" + } + } + } + bq_exports { + project: "chrome-luci-data" + dataset: "chromium" + table: "blink_web_tests_ci_test_results" + test_results { + predicate { + test_id_regexp: "ninja://[^/]*blink_web_tests/.+" + } + } + } + history_options { + use_invocation_timestamp: true + } + } + } + builders { name: "mac-arm64-updater-tester-dbg" 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 c58384ac..5464462 100644 --- a/infra/config/generated/luci/luci-milo.cfg +++ b/infra/config/generated/luci/luci-milo.cfg
@@ -211,6 +211,11 @@ short_name: "bld" } builders { + name: "buildbucket/luci.chromium.ci/mac-arm64-rel (reclient shadow)" + category: "chromium.mac|release|arm64" + short_name: "rec" + } + builders { name: "buildbucket/luci.chromium.ci/Mac Builder (dbg)" category: "chromium.mac|debug" short_name: "bld" @@ -7803,6 +7808,11 @@ short_name: "dbg" } builders { + name: "buildbucket/luci.chromium.ci/Mac deterministic (reclient shadow)" + category: "deterministic|mac" + short_name: "rec" + } + builders { name: "buildbucket/luci.chromium.ci/fuchsia-fyi-arm64-cfv2-script" category: "fuchsia|a64" short_name: "cfv2" @@ -10843,6 +10853,11 @@ short_name: "bld" } builders { + name: "buildbucket/luci.chromium.ci/mac-arm64-rel (reclient shadow)" + category: "release|arm64" + short_name: "rec" + } + builders { name: "buildbucket/luci.chromium.ci/Mac Builder (dbg)" category: "debug" short_name: "bld"
diff --git a/infra/config/generated/luci/luci-notify.cfg b/infra/config/generated/luci/luci-notify.cfg index f5add8b..1c32a17 100644 --- a/infra/config/generated/luci/luci-notify.cfg +++ b/infra/config/generated/luci/luci-notify.cfg
@@ -3636,6 +3636,25 @@ } notifiers { notifications { + on_occurrence: FAILURE + failed_step_regexp: "\\b(bot_update|compile|gclient runhooks|runhooks|update|\\w*nocompile_test)\\b" + email { + rotation_urls: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-build-sheriff" + } + template: "tree_closure_email_template" + } + builders { + bucket: "ci" + name: "mac-arm64-rel (reclient shadow)" + repository: "https://chromium.googlesource.com/chromium/src" + } + tree_closers { + tree_status_host: "chromium-status.appspot.com" + failed_step_regexp: "\\b(bot_update|compile|gclient runhooks|runhooks|update|\\w*nocompile_test)\\b" + } +} +notifiers { + notifications { on_change: true email { recipients: "chrome-memory-safety+bots@google.com"
diff --git a/infra/config/generated/luci/luci-scheduler.cfg b/infra/config/generated/luci/luci-scheduler.cfg index 68d223af..02cf342 100644 --- a/infra/config/generated/luci/luci-scheduler.cfg +++ b/infra/config/generated/luci/luci-scheduler.cfg
@@ -2685,6 +2685,16 @@ } } job { + id: "Mac deterministic (reclient shadow)" + realm: "ci" + acl_sets: "ci" + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "ci" + builder: "Mac deterministic (reclient shadow)" + } +} +job { id: "Mac10.13 Tests" realm: "ci" acls { @@ -6469,6 +6479,16 @@ } } job { + id: "mac-arm64-rel (reclient shadow)" + realm: "ci" + acl_sets: "ci" + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "ci" + builder: "mac-arm64-rel (reclient shadow)" + } +} +job { id: "mac-arm64-rel reclient staging" realm: "reclient" acl_sets: "reclient" @@ -7374,6 +7394,7 @@ triggers: "Mac Builder Next" triggers: "Mac deterministic" triggers: "Mac deterministic (dbg)" + triggers: "Mac deterministic (reclient shadow)" triggers: "Network Service Linux" triggers: "Site Isolation Android" triggers: "TSAN Debug" @@ -7589,6 +7610,7 @@ triggers: "mac-arm64-on-arm64-rel" triggers: "mac-arm64-on-arm64-rel-reclient" triggers: "mac-arm64-rel" + triggers: "mac-arm64-rel (reclient shadow)" triggers: "mac-code-coverage" triggers: "mac-fieldtrial-rel" triggers: "mac-hermetic-upgrade-rel"
diff --git a/infra/config/subprojects/chromium/ci/chromium.fyi.star b/infra/config/subprojects/chromium/ci/chromium.fyi.star index d553acd..5133aac7 100644 --- a/infra/config/subprojects/chromium/ci/chromium.fyi.star +++ b/infra/config/subprojects/chromium/ci/chromium.fyi.star
@@ -1964,6 +1964,20 @@ ) fyi_mac_builder( + name = "Mac deterministic (reclient shadow)", + console_view_entry = consoles.console_view_entry( + category = "deterministic|mac", + short_name = "rec", + ), + cores = None, + executable = "recipe:swarming/deterministic_build", + execution_timeout = 6 * time.hour, + goma_backend = None, + reclient_instance = rbe_instance.DEFAULT, + reclient_jobs = 40, +) + +fyi_mac_builder( name = "mac-hermetic-upgrade-rel", console_view_entry = consoles.console_view_entry( category = "mac",
diff --git a/infra/config/subprojects/chromium/ci/chromium.mac.star b/infra/config/subprojects/chromium/ci/chromium.mac.star index a36e8491..4943025 100644 --- a/infra/config/subprojects/chromium/ci/chromium.mac.star +++ b/infra/config/subprojects/chromium/ci/chromium.mac.star
@@ -160,6 +160,22 @@ os = os.MAC_DEFAULT, ) +ci.builder( + name = "mac-arm64-rel (reclient shadow)", + builder_spec = builder_config.copy_from( + "ci/mac-arm64-rel", + ), + console_view_entry = consoles.console_view_entry( + category = "release|arm64", + short_name = "rec", + ), + os = os.MAC_DEFAULT, + sheriff_rotations = args.ignore_default(None), + goma_backend = None, + reclient_instance = rbe_instance.DEFAULT, + reclient_jobs = 40, +) + ci.thin_tester( name = "mac11-arm64-rel-tests", branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
diff --git a/ios/chrome/browser/follow/BUILD.gn b/ios/chrome/browser/follow/BUILD.gn index 8f7a15b..932eeb3 100644 --- a/ios/chrome/browser/follow/BUILD.gn +++ b/ios/chrome/browser/follow/BUILD.gn
@@ -2,7 +2,7 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -import("//ios/web/public/js_messaging/optimize_js.gni") +import("//ios/web/public/js_messaging/optimize_ts.gni") source_set("follow") { sources = [ @@ -70,9 +70,9 @@ configs += [ "//build/config/compiler:enable_arc" ] } -optimize_js("rss_link_js") { +optimize_ts("rss_link_js") { visibility = [ ":follow" ] - primary_script = "resources/rss_link.js" - sources = [ "resources/rss_link.js" ] + sources = [ "resources/rss_link.ts" ] + deps = [ "//ios/web/public/js_messaging:gcrweb" ] }
diff --git a/ios/chrome/browser/follow/resources/rss_link.js b/ios/chrome/browser/follow/resources/rss_link.ts similarity index 81% rename from ios/chrome/browser/follow/resources/rss_link.js rename to ios/chrome/browser/follow/resources/rss_link.ts index a84ef51..c4b21ea4 100644 --- a/ios/chrome/browser/follow/resources/rss_link.js +++ b/ios/chrome/browser/follow/resources/rss_link.ts
@@ -2,16 +2,18 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import {gCrWeb} from "//ios/web/public/js_messaging/resources/gcrweb.js"; + /** * @fileoverview Functions used to parse RSS links from a web page. */ -__gCrWeb['rssLink'] = {}; /* Gets RSS links. */ -__gCrWeb.rssLink.getRSSLinks = function() { +gCrWeb.rssLink.getRSSLinks = function(): string[] { const linkTags = document.head.getElementsByTagName('link'); - const rssLinks = []; + const rssLinks: string[] = []; + for (const linkTag of linkTags) { if (linkTag.rel === 'alternate' || linkTag.rel === 'service.feed') {
diff --git a/ios/chrome/browser/ui/bubble/bubble_presenter.mm b/ios/chrome/browser/ui/bubble/bubble_presenter.mm index ef4f80f5..1163192f 100644 --- a/ios/chrome/browser/ui/bubble/bubble_presenter.mm +++ b/ios/chrome/browser/ui/bubble/bubble_presenter.mm
@@ -535,10 +535,12 @@ }; BubbleViewControllerPresenter* bubbleViewControllerPresenter = - [[BubbleViewControllerPresenter alloc] initWithText:text - arrowDirection:direction - alignment:alignment - dismissalCallback:dismissalCallback]; + [[BubbleViewControllerPresenter alloc] + initDefaultBubbleWithText:text + arrowDirection:direction + alignment:alignment + isLongDurationBubble:[self isLongDurationBubble:feature] + dismissalCallback:dismissalCallback]; return bubbleViewControllerPresenter; } @@ -552,4 +554,11 @@ ->DismissedWithSnooze(feature, snoozeAction); } +// Returns YES if the bubble for `feature` has a long duration. +- (BOOL)isLongDurationBubble:(const base::Feature&)feature { + // Display follow iph bubble with long duration. + return feature.name == + feature_engagement::kIPHFollowWhileBrowsingFeature.name; +} + @end
diff --git a/ios/chrome/browser/ui/bubble/bubble_view_controller_presenter.h b/ios/chrome/browser/ui/bubble/bubble_view_controller_presenter.h index cb4c15e..07ff9a2 100644 --- a/ios/chrome/browser/ui/bubble/bubble_view_controller_presenter.h +++ b/ios/chrome/browser/ui/bubble/bubble_view_controller_presenter.h
@@ -65,12 +65,15 @@ // Initializes the presenter with a Default BubbleViewType. `text` is the text // displayed by the bubble. `arrowDirection` is the direction the bubble's arrow // is pointing. `alignment` is the position of the arrow on the bubble. +// `isLongDurationBubble` is YES if the bubble presenting time is longer. // `dismissalCallback` is a block invoked when the bubble is dismissed (manual // and automatic dismissal). `dismissalCallback` is optional. -- (instancetype)initWithText:(NSString*)text - arrowDirection:(BubbleArrowDirection)arrowDirection - alignment:(BubbleAlignment)alignment - dismissalCallback:(ProceduralBlockWithSnoozeAction)dismissalCallback; +- (instancetype)initDefaultBubbleWithText:(NSString*)text + arrowDirection:(BubbleArrowDirection)arrowDirection + alignment:(BubbleAlignment)alignment + isLongDurationBubble:(BOOL)isLongDurationBubble + dismissalCallback: + (ProceduralBlockWithSnoozeAction)dismissalCallback; - (instancetype)init NS_UNAVAILABLE;
diff --git a/ios/chrome/browser/ui/bubble/bubble_view_controller_presenter.mm b/ios/chrome/browser/ui/bubble/bubble_view_controller_presenter.mm index ab46e175..8efe706 100644 --- a/ios/chrome/browser/ui/bubble/bubble_view_controller_presenter.mm +++ b/ios/chrome/browser/ui/bubble/bubble_view_controller_presenter.mm
@@ -18,6 +18,9 @@ // How long, in seconds, the bubble is visible on the screen. const NSTimeInterval kBubbleVisibilityDuration = 5.0; +// How long, in seconds, the long duration bubble is visible on the screen. Ex. +// Follow in-product help(IPH) bubble. +const NSTimeInterval kBubbleVisibilityLongDuration = 8.0; // How long, in seconds, the user should be considered engaged with the bubble // after the bubble first becomes visible. const NSTimeInterval kBubbleEngagementDuration = 30.0; @@ -68,6 +71,8 @@ @property(nonatomic, assign) BubbleAlignment alignment; // The type of the bubble view's content. @property(nonatomic, assign, readonly) BubbleViewType bubbleType; +// YES if the bubble should present longer. +@property(nonatomic, assign) BOOL isLongDurationBubble; // Whether the bubble view controller is presented or dismissed. @property(nonatomic, assign, getter=isPresenting) BOOL presenting; // The block invoked when the bubble is dismissed (both via timer and via tap). @@ -138,11 +143,13 @@ return self; } -- (instancetype)initWithText:(NSString*)text - arrowDirection:(BubbleArrowDirection)arrowDirection - alignment:(BubbleAlignment)alignment - dismissalCallback: - (ProceduralBlockWithSnoozeAction)dismissalCallback { +- (instancetype)initDefaultBubbleWithText:(NSString*)text + arrowDirection:(BubbleArrowDirection)arrowDirection + alignment:(BubbleAlignment)alignment + isLongDurationBubble:(BOOL)isLongDurationBubble + dismissalCallback: + (ProceduralBlockWithSnoozeAction)dismissalCallback { + self.isLongDurationBubble = isLongDurationBubble; return [self initWithText:text title:nil image:nil @@ -184,7 +191,9 @@ [parentView addGestureRecognizer:self.swipeRecognizer]; self.bubbleDismissalTimer = [NSTimer - scheduledTimerWithTimeInterval:kBubbleVisibilityDuration + scheduledTimerWithTimeInterval:self.isLongDurationBubble + ? kBubbleVisibilityLongDuration + : kBubbleVisibilityDuration target:self selector:@selector(bubbleDismissalTimerFired:) userInfo:nil
diff --git a/ios/chrome/browser/ui/elements/text_view_selection_disabled.mm b/ios/chrome/browser/ui/elements/text_view_selection_disabled.mm index 7989489a..624b4ac 100644 --- a/ios/chrome/browser/ui/elements/text_view_selection_disabled.mm +++ b/ios/chrome/browser/ui/elements/text_view_selection_disabled.mm
@@ -8,16 +8,22 @@ #error "This file requires ARC support." #endif +#if !defined(__IPHONE_16_0) || __IPHONE_OS_VERSION_MAX_ALLOWED < __IPHONE_16_0 +@interface UITextView (TextKit) +// Forward declare iOS 16 `+textViewUsingTextLayoutManager` on iOS 15 SDK +// builds. ++ (instancetype)textViewUsingTextLayoutManager:(BOOL)usingTextLayoutManager; +@end +#endif + @implementation TextViewSelectionDisabled + (TextViewSelectionDisabled*)textView { -// TODO(crbug.com/1335912): On iOS 16, EG is unable to tap links in -// TextKit2-based UITextViews. Fall back to TextKit1 until this issue -// is resolved. -#if defined(__IPHONE_16_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_16_0 + // TODO(crbug.com/1335912): On iOS 16, EG is unable to tap links in + // TextKit2-based UITextViews. Fall back to TextKit1 until this issue + // is resolved. if (@available(iOS 16, *)) return [TextViewSelectionDisabled textViewUsingTextLayoutManager:NO]; -#endif return [[TextViewSelectionDisabled alloc] init]; }
diff --git a/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_egtest.mm b/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_egtest.mm index 0d504eb8b..5630ee3 100644 --- a/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_egtest.mm +++ b/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_egtest.mm
@@ -138,7 +138,8 @@ // Tests that tapping the switch to open tab button, switch to the open tab, // doesn't close the tab. -- (void)testSwitchToOpenTab { +// TOOD(crbug.com/1346362): Test failing regularly. +- (void)DISABLED_testSwitchToOpenTab { // TODO(crbug.com/1067817): Test won't pass on iPad devices. #if !TARGET_IPHONE_SIMULATOR if ([ChromeEarlGrey isIPadIdiom]) {
diff --git a/ios/chrome/browser/ui/toolbar/BUILD.gn b/ios/chrome/browser/ui/toolbar/BUILD.gn index 8fdfbf09..681d7dd 100644 --- a/ios/chrome/browser/ui/toolbar/BUILD.gn +++ b/ios/chrome/browser/ui/toolbar/BUILD.gn
@@ -53,6 +53,8 @@ "//ios/chrome/browser/ui/icons:infobar_icons", "//ios/chrome/browser/ui/icons:symbols", "//ios/chrome/browser/ui/location_bar", + "//ios/chrome/browser/ui/main:layout_guide_scene_agent", + "//ios/chrome/browser/ui/main:scene_state_header", "//ios/chrome/browser/ui/menu", "//ios/chrome/browser/ui/ntp", "//ios/chrome/browser/ui/ntp:util",
diff --git a/ios/chrome/browser/ui/toolbar/adaptive_toolbar_coordinator.mm b/ios/chrome/browser/ui/toolbar/adaptive_toolbar_coordinator.mm index 376adf7..4c50a55 100644 --- a/ios/chrome/browser/ui/toolbar/adaptive_toolbar_coordinator.mm +++ b/ios/chrome/browser/ui/toolbar/adaptive_toolbar_coordinator.mm
@@ -15,6 +15,9 @@ #import "ios/chrome/browser/ui/commands/find_in_page_commands.h" #import "ios/chrome/browser/ui/commands/omnibox_commands.h" #import "ios/chrome/browser/ui/commands/popup_menu_commands.h" +#import "ios/chrome/browser/ui/main/layout_guide_scene_agent.h" +#import "ios/chrome/browser/ui/main/scene_state.h" +#import "ios/chrome/browser/ui/main/scene_state_browser_agent.h" #import "ios/chrome/browser/ui/menu/browser_action_factory.h" #import "ios/chrome/browser/ui/ntp/ntp_util.h" #import "ios/chrome/browser/ui/toolbar/adaptive_toolbar_coordinator+subclassing.h" @@ -41,6 +44,8 @@ @property(nonatomic, strong) ToolbarMediator* mediator; // Actions handler for the toolbar buttons. @property(nonatomic, strong) ToolbarButtonActionsHandler* actionHandler; +// The layout guide center to use to coordinate views. +@property(nonatomic, readonly) LayoutGuideCenter* layoutGuideCenter; @end @@ -64,6 +69,7 @@ self.browser->GetBrowserState()->IsOffTheRecord() ? UIUserInterfaceStyleDark : UIUserInterfaceStyleUnspecified; + self.viewController.layoutGuideCenter = self.layoutGuideCenter; self.mediator = [[ToolbarMediator alloc] init]; self.mediator.incognito = self.browser->GetBrowserState()->IsOffTheRecord(); @@ -89,6 +95,18 @@ #pragma mark - Properties +- (LayoutGuideCenter*)layoutGuideCenter { + SceneState* sceneState = + SceneStateBrowserAgent::FromBrowser(self.browser)->GetSceneState(); + LayoutGuideSceneAgent* layoutGuideSceneAgent = + [LayoutGuideSceneAgent agentFromScene:sceneState]; + if (self.browser->GetBrowserState()->IsOffTheRecord()) { + return layoutGuideSceneAgent.incognitoLayoutGuideCenter; + } else { + return layoutGuideSceneAgent.layoutGuideCenter; + } +} + - (void)setLongPressDelegate:(id<PopupMenuLongPressDelegate>)longPressDelegate { _longPressDelegate = longPressDelegate; self.viewController.longPressDelegate = longPressDelegate;
diff --git a/ios/chrome/browser/ui/toolbar/adaptive_toolbar_view_controller.h b/ios/chrome/browser/ui/toolbar/adaptive_toolbar_view_controller.h index dd56d02..639a4e4 100644 --- a/ios/chrome/browser/ui/toolbar/adaptive_toolbar_view_controller.h +++ b/ios/chrome/browser/ui/toolbar/adaptive_toolbar_view_controller.h
@@ -15,6 +15,7 @@ @protocol AdaptiveToolbarMenusProvider; @class AdaptiveToolbarViewController; @protocol BrowserCommands; +@class LayoutGuideCenter; @protocol OmniboxCommands; @protocol PopupMenuCommands; @protocol PopupMenuLongPressDelegate; @@ -33,6 +34,8 @@ // Button factory. @property(nonatomic, strong) ToolbarButtonFactory* buttonFactory; +// Layout Guide Center. +@property(nonatomic, strong) LayoutGuideCenter* layoutGuideCenter; // Omnibox commands handler for the ViewController. @property(nonatomic, weak) id<OmniboxCommands> omniboxCommandsHandler; // Popup menu commands handler for the ViewController.
diff --git a/ios/chrome/browser/ui/toolbar/adaptive_toolbar_view_controller.mm b/ios/chrome/browser/ui/toolbar/adaptive_toolbar_view_controller.mm index d87333c..06ed171 100644 --- a/ios/chrome/browser/ui/toolbar/adaptive_toolbar_view_controller.mm +++ b/ios/chrome/browser/ui/toolbar/adaptive_toolbar_view_controller.mm
@@ -85,13 +85,15 @@ object:nil]; [self makeViewAccessibilityTraitsContainer]; - // Adds the layout guide to the buttons. + // Add the layout guide names to the buttons. self.view.toolsMenuButton.guideName = kToolsMenuGuide; self.view.tabGridButton.guideName = kTabSwitcherGuide; self.view.openNewTabButton.guideName = kNewTabButtonGuide; self.view.forwardButton.guideName = kForwardButtonGuide; self.view.backButton.guideName = kBackButtonGuide; + [self addLayoutGuideCenterToButtons]; + // Add navigation popup menu triggers. if (UseSymbols()) { [self configureMenuProviderForButton:self.view.backButton @@ -136,6 +138,14 @@ return self.view.toolsMenuButton; } +- (void)setLayoutGuideCenter:(LayoutGuideCenter*)layoutGuideCenter { + _layoutGuideCenter = layoutGuideCenter; + + if (self.isViewLoaded) { + [self addLayoutGuideCenterToButtons]; + } +} + #pragma mark - ToolbarConsumer - (void)setCanGoForward:(BOOL)canGoForward { @@ -456,4 +466,12 @@ [button addAction:action forControlEvents:UIControlEventMenuActionTriggered]; } +- (void)addLayoutGuideCenterToButtons { + self.view.toolsMenuButton.layoutGuideCenter = self.layoutGuideCenter; + self.view.tabGridButton.layoutGuideCenter = self.layoutGuideCenter; + self.view.openNewTabButton.layoutGuideCenter = self.layoutGuideCenter; + self.view.forwardButton.layoutGuideCenter = self.layoutGuideCenter; + self.view.backButton.layoutGuideCenter = self.layoutGuideCenter; +} + @end
diff --git a/ios/chrome/browser/ui/toolbar/buttons/toolbar_button.h b/ios/chrome/browser/ui/toolbar/buttons/toolbar_button.h index bcb2903..6f8e1272 100644 --- a/ios/chrome/browser/ui/toolbar/buttons/toolbar_button.h +++ b/ios/chrome/browser/ui/toolbar/buttons/toolbar_button.h
@@ -10,6 +10,7 @@ #import "ios/chrome/browser/ui/toolbar/buttons/toolbar_component_options.h" #import "ios/chrome/browser/ui/util/named_guide.h" +@class LayoutGuideCenter; @class ToolbarConfiguration; // UIButton subclass used as a Toolbar component. @@ -30,6 +31,8 @@ // rotations. Any view constrained to them is expected to be dismissed on such // events. @property(nonatomic, strong) GuideName* guideName; +// The layout guide center for this button. +@property(nonatomic, strong) LayoutGuideCenter* layoutGuideCenter; // Whether this button is spotlighted, having a light gray background. This // state should not be used in the same time as the selected state. @property(nonatomic, assign) BOOL spotlighted;
diff --git a/ios/chrome/browser/ui/toolbar/buttons/toolbar_button.mm b/ios/chrome/browser/ui/toolbar/buttons/toolbar_button.mm index b9cf7045..e8acd16 100644 --- a/ios/chrome/browser/ui/toolbar/buttons/toolbar_button.mm +++ b/ios/chrome/browser/ui/toolbar/buttons/toolbar_button.mm
@@ -8,6 +8,7 @@ #import "ios/chrome/browser/ui/toolbar/buttons/toolbar_configuration.h" #import "ios/chrome/browser/ui/toolbar/public/toolbar_constants.h" #import "ios/chrome/browser/ui/util/uikit_ui_util.h" +#import "ios/chrome/browser/ui/util/util_swift.h" #import "ios/chrome/common/ui/util/constraints_ui_util.h" #if !defined(__has_feature) || !__has_feature(objc_arc) @@ -158,6 +159,8 @@ NamedGuide* guide = [NamedGuide guideWithName:self.guideName view:self]; if (guide.constrainedView != self) guide.constrainedView = self; + + [self.layoutGuideCenter referenceView:self underName:self.guideName]; } }
diff --git a/ios/chrome/browser/ui/util/layout_guide_center.swift b/ios/chrome/browser/ui/util/layout_guide_center.swift index e31f87f7..f1d4e8c 100644 --- a/ios/chrome/browser/ui/util/layout_guide_center.swift +++ b/ios/chrome/browser/ui/util/layout_guide_center.swift
@@ -22,9 +22,12 @@ /// References a view under a specific `name`. @objc(referenceView:underName:) func reference(view referenceView: UIView?, under name: String) { - if let oldReferenceView = referenceViews.object(forKey: name as NSString) { - oldReferenceView.cr_onWindowCoordinatesChanged = nil + let oldReferenceView = referenceViews.object(forKey: name as NSString) + // Early return if `referenceView` is already set. + if referenceView == oldReferenceView { + return } + oldReferenceView?.cr_onWindowCoordinatesChanged = nil referenceViews.setObject(referenceView, forKey: name as NSString) updateGuides(named: name) // Schedule updates to the matching layout guides when the reference view
diff --git a/ios/chrome/browser/ui/util/layout_guide_center_unittest.mm b/ios/chrome/browser/ui/util/layout_guide_center_unittest.mm index ee75055..729d766d 100644 --- a/ios/chrome/browser/ui/util/layout_guide_center_unittest.mm +++ b/ios/chrome/browser/ui/util/layout_guide_center_unittest.mm
@@ -153,3 +153,29 @@ // the elements are released. EXPECT_EQ(weak_layout_guide, nil); } + +// Checks that if `referenceView:underName:` is called twice with the same +// arguments, there are no changes +TEST_F(LayoutGuideCenterTest, TestReferenceViewNoChangesIfSameView) { + CGRect rect = CGRectMake(10, 20, 30, 40); + UIView* reference_view = [[UIView alloc] initWithFrame:rect]; + [center_ referenceView:reference_view underName:@"view"]; + + // Override reference_view's cr_onWindowCoordinatesChanged to later verify + // that it hasn't changed. + __block BOOL windowCoordinatesChangedCalled = NO; + reference_view.cr_onWindowCoordinatesChanged = ^(UIView* view) { + windowCoordinatesChangedCalled = YES; + }; + + UIView* view = [[UIView alloc] init]; + reference_view.cr_onWindowCoordinatesChanged(view); + EXPECT_TRUE(windowCoordinatesChangedCalled); + windowCoordinatesChangedCalled = NO; + + // Re-reference reference_view. This should not change the view's + // cr_onWindowCoordinatesChanged block. + [center_ referenceView:reference_view underName:@"view"]; + reference_view.cr_onWindowCoordinatesChanged(view); + EXPECT_TRUE(windowCoordinatesChangedCalled); +}
diff --git a/ios/web/js_features/window_error/BUILD.gn b/ios/web/js_features/window_error/BUILD.gn index be6f29b..53dc308 100644 --- a/ios/web/js_features/window_error/BUILD.gn +++ b/ios/web/js_features/window_error/BUILD.gn
@@ -3,7 +3,7 @@ # found in the LICENSE file. import("//ios/build/config.gni") -import("//ios/web/public/js_messaging/optimize_js.gni") +import("//ios/web/public/js_messaging/optimize_ts.gni") source_set("window_error") { configs += [ "//build/config/compiler:enable_arc" ] @@ -21,11 +21,11 @@ ] } -optimize_js("error_js") { +optimize_ts("error_js") { visibility = [ ":window_error" ] - primary_script = "resources/error.js" - sources = [ "resources/error.js" ] + sources = [ "resources/error.ts" ] + deps = [ "//ios/web/public/js_messaging:util_scripts" ] } source_set("unittests") {
diff --git a/ios/web/js_features/window_error/resources/error.js b/ios/web/js_features/window_error/resources/error.ts similarity index 67% rename from ios/web/js_features/window_error/resources/error.js rename to ios/web/js_features/window_error/resources/error.ts index 83c50cb..85a122ea 100644 --- a/ios/web/js_features/window_error/resources/error.js +++ b/ios/web/js_features/window_error/resources/error.ts
@@ -6,16 +6,19 @@ * @fileoverview Error listener to report error details to the native app. */ -// Requires functions from common.js +import {sendWebKitMessage} from '//ios/web/public/js_messaging/resources/utils.js' /** * JavaScript errors are logged on the main application side. The handler is * added ASAP to catch any errors in startup. */ -window.addEventListener('error', function(event) { - __gCrWeb.common.sendWebKitMessage('WindowErrorResultHandler', +function errorEventHandler(event: ErrorEvent): void { + sendWebKitMessage('WindowErrorResultHandler', {'filename' : event.filename, 'line_number' : event.lineno, 'message': event.message.toString() }); -}); +} + + +window.addEventListener('error', errorEventHandler);
diff --git a/media/gpu/v4l2/test/av1_decoder.cc b/media/gpu/v4l2/test/av1_decoder.cc index b0e0710..036a1b6 100644 --- a/media/gpu/v4l2/test/av1_decoder.cc +++ b/media/gpu/v4l2/test/av1_decoder.cc
@@ -617,8 +617,8 @@ ANALYZER_ALLOW_UNUSED(reference_id); } - // TODO(b/228534730): add changes to prepare parameters for V4L2 AV1 stateless - // decoding and make VIDIOC_S_EXT_CTRLS v4l2 ioctl call + // TODO(b/239618516): add ext_ctrl for V4L2_CID_STATELESS_AV1_SEQUENCE + struct v4l2_ctrl_av1_frame_header v4l2_frame_params = {}; FillLoopFilterParams(&v4l2_frame_params.loop_filter, @@ -648,11 +648,15 @@ FillGlobalMotionParams(&v4l2_frame_params.global_motion, current_frame_header.global_motion); + // TODO(stevecho): V4L2_CID_STATELESS_AV1_FRAME_HEADER is trending to be + // changed to V4L2_CID_STATELESS_AV1_FRAME struct v4l2_ext_control ext_ctrl = {.id = V4L2_CID_STATELESS_AV1_FRAME_HEADER, .size = sizeof(v4l2_frame_params), .ptr = &v4l2_frame_params}; - if (!v4l2_ioctl_->SetExtCtrls(OUTPUT_queue_, ext_ctrl)) + struct v4l2_ext_controls ext_ctrls = {.count = 1, .controls = &ext_ctrl}; + + if (!v4l2_ioctl_->SetExtCtrls(OUTPUT_queue_, &ext_ctrls)) LOG(FATAL) << "VIDIOC_S_EXT_CTRLS failed."; if (!v4l2_ioctl_->MediaRequestIocQueue(OUTPUT_queue_))
diff --git a/media/gpu/v4l2/test/v4l2_ioctl_shim.cc b/media/gpu/v4l2/test/v4l2_ioctl_shim.cc index 555dbe6..b96167d0 100644 --- a/media/gpu/v4l2/test/v4l2_ioctl_shim.cc +++ b/media/gpu/v4l2/test/v4l2_ioctl_shim.cc
@@ -482,7 +482,7 @@ } bool V4L2IoctlShim::SetExtCtrls(const std::unique_ptr<V4L2Queue>& queue, - v4l2_ext_control& ext_ctrl) const { + v4l2_ext_controls* ext_ctrls) const { // TODO(b/230021497): add compressed header probability related change // when V4L2_CID_STATELESS_VP9_COMPRESSED_HDR is supported @@ -492,12 +492,10 @@ // instead are applied by the driver for the buffer associated with // the same request.", see: // https://www.kernel.org/doc/html/v5.10/userspace-api/media/v4l/vidioc-g-ext-ctrls.html#description - struct v4l2_ext_controls ctrls = {.which = V4L2_CTRL_WHICH_REQUEST_VAL, - .count = 1, - .request_fd = queue->media_request_fd(), - .controls = &ext_ctrl}; + ext_ctrls->which = V4L2_CTRL_WHICH_REQUEST_VAL; + ext_ctrls->request_fd = queue->media_request_fd(); - const bool ret = Ioctl(VIDIOC_S_EXT_CTRLS, &ctrls); + const bool ret = Ioctl(VIDIOC_S_EXT_CTRLS, ext_ctrls); return ret; }
diff --git a/media/gpu/v4l2/test/v4l2_ioctl_shim.h b/media/gpu/v4l2/test/v4l2_ioctl_shim.h index d9b83cb..0f0de65 100644 --- a/media/gpu/v4l2/test/v4l2_ioctl_shim.h +++ b/media/gpu/v4l2/test/v4l2_ioctl_shim.h
@@ -186,10 +186,10 @@ // Starts streaming |queue| (via VIDIOC_STREAMON). [[nodiscard]] bool StreamOn(const enum v4l2_buf_type type) const; - // Sets the value of a control which specifies decoding parameters + // Sets the value of controls which specify decoding parameters // for each frame. [[nodiscard]] bool SetExtCtrls(const std::unique_ptr<V4L2Queue>& queue, - v4l2_ext_control& ext_ctrl) const; + v4l2_ext_controls* ext_ctrls) const; // Allocates requests (likely one per OUTPUT buffer) via // MEDIA_IOC_REQUEST_ALLOC on the media device.
diff --git a/media/gpu/v4l2/test/vp9_decoder.cc b/media/gpu/v4l2/test/vp9_decoder.cc index 1841104..bd2f35f0 100644 --- a/media/gpu/v4l2/test/vp9_decoder.cc +++ b/media/gpu/v4l2/test/vp9_decoder.cc
@@ -463,7 +463,9 @@ .size = sizeof(v4l2_frame_params), .ptr = &v4l2_frame_params}; - if (!v4l2_ioctl_->SetExtCtrls(OUTPUT_queue_, ext_ctrl)) + struct v4l2_ext_controls ext_ctrls = {.count = 1, .controls = &ext_ctrl}; + + if (!v4l2_ioctl_->SetExtCtrls(OUTPUT_queue_, &ext_ctrls)) LOG(FATAL) << "VIDIOC_S_EXT_CTRLS failed."; if (!v4l2_ioctl_->MediaRequestIocQueue(OUTPUT_queue_))
diff --git a/services/network/network_context.cc b/services/network/network_context.cc index 5fcfc9df..cb42918 100644 --- a/services/network/network_context.cc +++ b/services/network/network_context.cc
@@ -746,13 +746,15 @@ void NetworkContext::CreateURLLoaderFactory( mojo::PendingReceiver<mojom::URLLoaderFactory> receiver, mojom::URLLoaderFactoryParamsPtr params) { - scoped_refptr<ResourceSchedulerClient> resource_scheduler_client = - base::MakeRefCounted<ResourceSchedulerClient>( - current_resource_scheduler_client_id_, - IsBrowserInitiated(params->process_id == mojom::kBrowserProcessId), - resource_scheduler_.get(), - url_request_context_->network_quality_estimator()); - current_resource_scheduler_client_id_.Increment(); + scoped_refptr<ResourceSchedulerClient> resource_scheduler_client; + if (!base::FeatureList::IsEnabled(features::kDisableResourceScheduler)) { + resource_scheduler_client = base::MakeRefCounted<ResourceSchedulerClient>( + current_resource_scheduler_client_id_, + IsBrowserInitiated(params->process_id == mojom::kBrowserProcessId), + resource_scheduler_.get(), + url_request_context_->network_quality_estimator()); + current_resource_scheduler_client_id_.Increment(); + } CreateURLLoaderFactory(std::move(receiver), std::move(params), std::move(resource_scheduler_client)); }
diff --git a/services/network/public/cpp/features.cc b/services/network/public/cpp/features.cc index 8912c7d6..b0d56aa 100644 --- a/services/network/public/cpp/features.cc +++ b/services/network/public/cpp/features.cc
@@ -295,5 +295,9 @@ const base::Feature kReduceAcceptLanguage{"ReduceAcceptLanguage", base::FEATURE_DISABLED_BY_DEFAULT}; +// Disable ResourceScheduler. +const base::Feature kDisableResourceScheduler{ + "DisableResourceScheduler", base::FEATURE_DISABLED_BY_DEFAULT}; + } // namespace features } // namespace network
diff --git a/services/network/public/cpp/features.h b/services/network/public/cpp/features.h index 0bded2a..6f89731c 100644 --- a/services/network/public/cpp/features.h +++ b/services/network/public/cpp/features.h
@@ -125,6 +125,9 @@ COMPONENT_EXPORT(NETWORK_CPP) extern const base::Feature kReduceAcceptLanguage; +COMPONENT_EXPORT(NETWORK_CPP) +extern const base::Feature kDisableResourceScheduler; + } // namespace features } // namespace network
diff --git a/testing/buildbot/chromium.mac.json b/testing/buildbot/chromium.mac.json index c9008f8..2269a72 100644 --- a/testing/buildbot/chromium.mac.json +++ b/testing/buildbot/chromium.mac.json
@@ -20278,6 +20278,11 @@ "all" ] }, + "mac-arm64-rel (reclient shadow)": { + "additional_compile_targets": [ + "all" + ] + }, "mac11-arm64-rel-tests": { "gtest_tests": [ {
diff --git a/testing/buildbot/waterfalls.pyl b/testing/buildbot/waterfalls.pyl index e7d6773b..9ccab1fb 100644 --- a/testing/buildbot/waterfalls.pyl +++ b/testing/buildbot/waterfalls.pyl
@@ -5211,6 +5211,11 @@ 'all', ], }, + 'mac-arm64-rel (reclient shadow)': { + 'additional_compile_targets': [ + 'all', + ], + }, 'mac11-arm64-rel-tests': { 'mixins': [ # Only run selected test suites on CQ. https://crbug.com/1234525.
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json index 5a0ab1d..4f89b44 100644 --- a/testing/variations/fieldtrial_testing_config.json +++ b/testing/variations/fieldtrial_testing_config.json
@@ -2903,6 +2903,21 @@ ] } ], + "CrOSLateBootMediaDynamicCgroup": [ + { + "platforms": [ + "chromeos" + ], + "experiments": [ + { + "name": "Enabled", + "enable_features": [ + "CrOSLateBootMediaDynamicCgroup" + ] + } + ] + } + ], "CrOSLateBootSchedTrace": [ { "platforms": [ @@ -3820,6 +3835,26 @@ ] } ], + "EnablePDPMetricsUSDesktopIOS": [ + { + "platforms": [ + "chromeos", + "chromeos_lacros", + "ios", + "linux", + "mac", + "windows" + ], + "experiments": [ + { + "name": "Enabled", + "enable_features": [ + "ShoppingPDPMetrics" + ] + } + ] + } + ], "EnableSsePathForCopyLCharsX86": [ { "platforms": [ @@ -5432,25 +5467,6 @@ ] } ], - "KeyPinningComponentUpdater": [ - { - "platforms": [ - "chromeos", - "chromeos_lacros", - "linux", - "mac", - "windows" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "KeyPinningComponentUpdater" - ] - } - ] - } - ], "KeyboardAccessoryAddressIPH": [ { "platforms": [
diff --git a/third_party/blink/public/devtools_protocol/browser_protocol.pdl b/third_party/blink/public/devtools_protocol/browser_protocol.pdl index 0d2cacc..73feffd 100644 --- a/third_party/blink/public/devtools_protocol/browser_protocol.pdl +++ b/third_party/blink/public/devtools_protocol/browser_protocol.pdl
@@ -774,6 +774,7 @@ NotificationPermissionRequestedIframe ObsoleteWebRtcCipherSuite OpenWebDatabaseInsecureContext + OverflowVisibleOnReplacedElement PictureSourceSrc PrefixedCancelAnimationFrame PrefixedRequestAnimationFrame @@ -2684,7 +2685,7 @@ array of NodeId nodeIds # Returns NodeIds of current top layer elements. - # Top layer is rendered closest to the user within a viewport, therefore its elements always + # Top layer is rendered closest to the user within a viewport, therefore its elements always # appear on top of all other content. experimental command getTopLayerElements returns @@ -8992,6 +8993,14 @@ # Comma separated list of StorageType to clear. string storageTypes + # Clears storage for storage key. + command clearDataForStorageKey + parameters + # Storage key. + string storageKey + # Comma separated list of StorageType to clear. + string storageTypes + # Returns all browser cookies. command getCookies parameters
diff --git a/third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom b/third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom index 8cf860b..b0cc28d 100644 --- a/third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom +++ b/third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom
@@ -3629,6 +3629,7 @@ kSendBeaconWithFormData = 4308, kSendBeaconWithURLSearchParams = 4309, kSendBeaconWithUSVString = 4310, + kReplacedElementPaintedWithOverflow = 4311, // Add new features immediately above this line. Don't change assigned // numbers of any item, and don't reuse removed slots.
diff --git a/third_party/blink/renderer/core/frame/deprecation/deprecation.cc b/third_party/blink/renderer/core/frame/deprecation/deprecation.cc index 9904e81..24a89961 100644 --- a/third_party/blink/renderer/core/frame/deprecation/deprecation.cc +++ b/third_party/blink/renderer/core/frame/deprecation/deprecation.cc
@@ -247,6 +247,9 @@ case WebFeature::kNavigateEventRestoreScroll: return DeprecationInfo::WithTranslation( feature, DeprecationIssueType::kNavigateEventRestoreScroll); + case WebFeature::kExplicitOverflowVisibleOnReplacedElement: + return DeprecationInfo::WithTranslation( + feature, DeprecationIssueType::kOverflowVisibleOnReplacedElement); default: return DeprecationInfo::NotDeprecated(feature); }
diff --git a/third_party/blink/renderer/core/inspector/inspector_audits_issue.cc b/third_party/blink/renderer/core/inspector/inspector_audits_issue.cc index 2fc40fd..6a339d6 100644 --- a/third_party/blink/renderer/core/inspector/inspector_audits_issue.cc +++ b/third_party/blink/renderer/core/inspector/inspector_audits_issue.cc
@@ -536,6 +536,10 @@ type = protocol::Audits::DeprecationIssueTypeEnum:: OpenWebDatabaseInsecureContext; break; + case DeprecationIssueType::kOverflowVisibleOnReplacedElement: + type = protocol::Audits::DeprecationIssueTypeEnum:: + OverflowVisibleOnReplacedElement; + break; case DeprecationIssueType::kPictureSourceSrc: type = protocol::Audits::DeprecationIssueTypeEnum::PictureSourceSrc; break;
diff --git a/third_party/blink/renderer/core/inspector/inspector_audits_issue.h b/third_party/blink/renderer/core/inspector/inspector_audits_issue.h index 6ac5eec..65f11a48 100644 --- a/third_party/blink/renderer/core/inspector/inspector_audits_issue.h +++ b/third_party/blink/renderer/core/inspector/inspector_audits_issue.h
@@ -70,6 +70,7 @@ kNotificationPermissionRequestedIframe, kObsoleteWebRtcCipherSuite, kOpenWebDatabaseInsecureContext, + kOverflowVisibleOnReplacedElement, kPictureSourceSrc, kPrefixedCancelAnimationFrame, kPrefixedRequestAnimationFrame,
diff --git a/third_party/blink/renderer/core/layout/layout_object.cc b/third_party/blink/renderer/core/layout/layout_object.cc index d8cca14..c2fdf0b 100644 --- a/third_party/blink/renderer/core/layout/layout_object.cc +++ b/third_party/blink/renderer/core/layout/layout_object.cc
@@ -2804,6 +2804,15 @@ } } +bool LayoutObject::BelongsToElementChangingOverflowBehaviour() const { + auto* element = DynamicTo<Element>(GetNode()); + if (!element) + return false; + + return IsA<HTMLVideoElement>(element) || IsA<HTMLCanvasElement>(element) || + IsA<HTMLImageElement>(element); +} + void LayoutObject::StyleDidChange(StyleDifference diff, const ComputedStyle* old_style) { NOT_DESTROYED(); @@ -2855,13 +2864,17 @@ // changing the behavior regardless of the counts. Likewise, embedded content // will remain clipped regardless of the overflow: visible behvaior change. // Note for this reason we exclude SVG and embedded content from the counts. - if (IsLayoutReplaced() && !IsSVG() && !IsLayoutEmbeddedContent()) { + if (BelongsToElementChangingOverflowBehaviour()) { if ((StyleRef().HasExplicitOverflowXVisible() && StyleRef().OverflowX() == EOverflow::kVisible) || (StyleRef().HasExplicitOverflowYVisible() && StyleRef().OverflowY() == EOverflow::kVisible)) { UseCounter::Count(GetDocument(), WebFeature::kExplicitOverflowVisibleOnReplacedElement); + + Deprecation::CountDeprecation( + GetDocument().GetExecutionContext(), + WebFeature::kExplicitOverflowVisibleOnReplacedElement); if (!StyleRef().ObjectPropertiesPreventReplacedOverflow()) { UseCounter::Count( GetDocument(),
diff --git a/third_party/blink/renderer/core/layout/layout_object.h b/third_party/blink/renderer/core/layout/layout_object.h index 7fac6dd..97cf44e1 100644 --- a/third_party/blink/renderer/core/layout/layout_object.h +++ b/third_party/blink/renderer/core/layout/layout_object.h
@@ -3569,6 +3569,13 @@ bitfields_.SetShouldAssumePaintOffsetTranslationForLayoutShiftTracking(b); } + // Returns true if this layout object is created for an element which will be + // changing behaviour for overflow: visible. + // See + // https://groups.google.com/a/chromium.org/g/blink-dev/c/MuTeW_AFgxA/m/IlT4QVEfAgAJ + // for details. + bool BelongsToElementChangingOverflowBehaviour() const; + protected: // Identifiers for each of LayoutObject subclasses. // The identifier name for blink::LayoutFoo should be kLayoutObjectFoo.
diff --git a/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc b/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc index 8f6e4aa..b4d0269 100644 --- a/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc +++ b/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc
@@ -679,6 +679,9 @@ const auto& box_model = To<LayoutBoxModelObject>(object_); TransformPaintPropertyNode::State state{ gfx::Vector2dF(box_model.StickyPositionOffset())}; + state.direct_compositing_reasons = + full_context_.direct_compositing_reasons & + CompositingReason::kStickyPosition; // TODO(wangxianzhu): Not using GetCompositorElementId() here because // sticky elements don't work properly under multicol for now, to keep // consistency with CompositorElementIdFromUniqueObjectId() below. @@ -690,55 +693,59 @@ state.flags.flattens_inherited_transform = context_.should_flatten_inherited_transform; - const auto* layout_constraint = box_model.StickyConstraints(); - DCHECK(layout_constraint); - const auto* scroll_container_properties = - layout_constraint->containing_scroll_container_layer - ->GetLayoutObject() - .FirstFragment() - .PaintProperties(); - // A scroll node is only created if an object can be scrolled manually, - // while sticky position attaches to anything that clips overflow. - // No need to (actually can't) setup composited sticky constraint if - // the clipping ancestor we attach to doesn't have a scroll node. - bool scroll_container_scrolls = - scroll_container_properties && - scroll_container_properties->Scroll() == context_.current.scroll; - if (scroll_container_scrolls) { - auto constraint = std::make_unique<CompositorStickyConstraint>(); - constraint->is_anchored_left = layout_constraint->is_anchored_left; - constraint->is_anchored_right = layout_constraint->is_anchored_right; - constraint->is_anchored_top = layout_constraint->is_anchored_top; - constraint->is_anchored_bottom = layout_constraint->is_anchored_bottom; + if (state.direct_compositing_reasons) { + const auto* layout_constraint = box_model.StickyConstraints(); + DCHECK(layout_constraint); + const auto* scroll_container_properties = + layout_constraint->containing_scroll_container_layer + ->GetLayoutObject() + .FirstFragment() + .PaintProperties(); + // A scroll node is only created if an object can be scrolled manually, + // while sticky position attaches to anything that clips overflow. + // No need to (actually can't) setup composited sticky constraint if + // the clipping ancestor we attach to doesn't have a scroll node. + bool scroll_container_scrolls = + scroll_container_properties && + scroll_container_properties->Scroll() == context_.current.scroll; + if (scroll_container_scrolls) { + auto constraint = std::make_unique<CompositorStickyConstraint>(); + constraint->is_anchored_left = layout_constraint->is_anchored_left; + constraint->is_anchored_right = layout_constraint->is_anchored_right; + constraint->is_anchored_top = layout_constraint->is_anchored_top; + constraint->is_anchored_bottom = + layout_constraint->is_anchored_bottom; - constraint->left_offset = layout_constraint->left_offset.ToFloat(); - constraint->right_offset = layout_constraint->right_offset.ToFloat(); - constraint->top_offset = layout_constraint->top_offset.ToFloat(); - constraint->bottom_offset = layout_constraint->bottom_offset.ToFloat(); - constraint->constraint_box_rect = - gfx::RectF(layout_constraint->constraining_rect); - constraint->scroll_container_relative_sticky_box_rect = gfx::RectF( - layout_constraint->scroll_container_relative_sticky_box_rect); - constraint->scroll_container_relative_containing_block_rect = - gfx::RectF(layout_constraint - ->scroll_container_relative_containing_block_rect); - if (const PaintLayer* sticky_box_shifting_ancestor = - layout_constraint->nearest_sticky_layer_shifting_sticky_box) { - constraint->nearest_element_shifting_sticky_box = - CompositorElementIdFromUniqueObjectId( - sticky_box_shifting_ancestor->GetLayoutObject().UniqueId(), - CompositorElementIdNamespace::kStickyTranslation); + constraint->left_offset = layout_constraint->left_offset.ToFloat(); + constraint->right_offset = layout_constraint->right_offset.ToFloat(); + constraint->top_offset = layout_constraint->top_offset.ToFloat(); + constraint->bottom_offset = + layout_constraint->bottom_offset.ToFloat(); + constraint->constraint_box_rect = + gfx::RectF(layout_constraint->constraining_rect); + constraint->scroll_container_relative_sticky_box_rect = gfx::RectF( + layout_constraint->scroll_container_relative_sticky_box_rect); + constraint->scroll_container_relative_containing_block_rect = + gfx::RectF(layout_constraint + ->scroll_container_relative_containing_block_rect); + if (const PaintLayer* sticky_box_shifting_ancestor = + layout_constraint->nearest_sticky_layer_shifting_sticky_box) { + constraint->nearest_element_shifting_sticky_box = + CompositorElementIdFromUniqueObjectId( + sticky_box_shifting_ancestor->GetLayoutObject().UniqueId(), + CompositorElementIdNamespace::kStickyTranslation); + } + if (const PaintLayer* containing_block_shifting_ancestor = + layout_constraint + ->nearest_sticky_layer_shifting_containing_block) { + constraint->nearest_element_shifting_containing_block = + CompositorElementIdFromUniqueObjectId( + containing_block_shifting_ancestor->GetLayoutObject() + .UniqueId(), + CompositorElementIdNamespace::kStickyTranslation); + } + state.sticky_constraint = std::move(constraint); } - if (const PaintLayer* containing_block_shifting_ancestor = - layout_constraint - ->nearest_sticky_layer_shifting_containing_block) { - constraint->nearest_element_shifting_containing_block = - CompositorElementIdFromUniqueObjectId( - containing_block_shifting_ancestor->GetLayoutObject() - .UniqueId(), - CompositorElementIdNamespace::kStickyTranslation); - } - state.sticky_constraint = std::move(constraint); } // TODO(crbug.com/1117658): Implement direct update of StickyTranslation @@ -2976,9 +2983,11 @@ if (properties->TransformIsolationNode()) return true; if (auto* offset_translation = properties->PaintOffsetTranslation()) { - if (offset_translation->RequiresCompositingForScrollDependentPosition()) + if (offset_translation->RequiresCompositingForFixedPosition()) return true; } + if (auto* sticky_translation = properties->StickyTranslation()) + return true; if (properties->OverflowClip()) return true; return false;
diff --git a/third_party/blink/renderer/core/paint/paint_property_tree_builder_test.cc b/third_party/blink/renderer/core/paint/paint_property_tree_builder_test.cc index 3634243..5840de6 100644 --- a/third_party/blink/renderer/core/paint/paint_property_tree_builder_test.cc +++ b/third_party/blink/renderer/core/paint/paint_property_tree_builder_test.cc
@@ -6415,6 +6415,8 @@ const auto* outer_properties = PaintPropertiesForElement("outer"); ASSERT_TRUE(outer_properties && outer_properties->StickyTranslation()); + EXPECT_TRUE(outer_properties->StickyTranslation() + ->RequiresCompositingForStickyPosition()); EXPECT_EQ(gfx::Vector2dF(0, 60), outer_properties->StickyTranslation()->Translation2D()); ASSERT_NE(nullptr, @@ -6429,6 +6431,8 @@ const auto* middle_properties = PaintPropertiesForElement("middle"); ASSERT_TRUE(middle_properties && middle_properties->StickyTranslation()); + EXPECT_TRUE(middle_properties->StickyTranslation() + ->RequiresCompositingForStickyPosition()); EXPECT_EQ(gfx::Vector2dF(0, 15), middle_properties->StickyTranslation()->Translation2D()); ASSERT_NE(nullptr, @@ -6443,6 +6447,8 @@ const auto* inner_properties = PaintPropertiesForElement("inner"); ASSERT_TRUE(inner_properties && inner_properties->StickyTranslation()); + EXPECT_TRUE(inner_properties->StickyTranslation() + ->RequiresCompositingForStickyPosition()); EXPECT_EQ(gfx::Vector2dF(0, 20), inner_properties->StickyTranslation()->Translation2D()); ASSERT_NE(nullptr, @@ -6457,9 +6463,9 @@ ->nearest_element_shifting_containing_block); } -TEST_P(PaintPropertyTreeBuilderTest, NonScrollableSticky) { +TEST_P(PaintPropertyTreeBuilderTest, StickyUnderOverflowHidden) { // This test verifies the property tree builder applies sticky offset - // correctly when the clipping container cannot be scrolled, and + // correctly when the scroll container cannot be manually scrolled, and // does not emit sticky constraints. SetBodyInnerHTML(R"HTML( <div id="scroller" style="overflow:hidden; width:300px; height:200px;"> @@ -6478,6 +6484,10 @@ const auto* outer_properties = PaintPropertiesForElement("outer"); ASSERT_TRUE(outer_properties && outer_properties->StickyTranslation()); + // We still composite the element for better performance programmatic scroll + // offset animation. + EXPECT_TRUE(outer_properties->StickyTranslation() + ->RequiresCompositingForStickyPosition()); EXPECT_EQ(gfx::Vector2dF(0, 60), outer_properties->StickyTranslation()->Translation2D()); EXPECT_EQ(nullptr, @@ -6485,6 +6495,8 @@ const auto* middle_properties = PaintPropertiesForElement("middle"); ASSERT_TRUE(middle_properties && middle_properties->StickyTranslation()); + EXPECT_TRUE(middle_properties->StickyTranslation() + ->RequiresCompositingForStickyPosition()); EXPECT_EQ(gfx::Vector2dF(0, 15), middle_properties->StickyTranslation()->Translation2D()); EXPECT_EQ(nullptr, @@ -6492,6 +6504,52 @@ const auto* inner_properties = PaintPropertiesForElement("inner"); ASSERT_TRUE(inner_properties && inner_properties->StickyTranslation()); + EXPECT_TRUE(inner_properties->StickyTranslation() + ->RequiresCompositingForStickyPosition()); + EXPECT_EQ(gfx::Vector2dF(0, 20), + inner_properties->StickyTranslation()->Translation2D()); + EXPECT_EQ(nullptr, + inner_properties->StickyTranslation()->GetStickyConstraint()); +} + +TEST_P(PaintPropertyTreeBuilderTest, StickyUnderScrollerWithoutOverflow) { + // This test verifies the property tree builder applies sticky offset + // correctly when the scroll container doesn't have overflow, and does not + // emit compositing reasons or sticky constraints. + SetBodyInnerHTML(R"HTML( + <div id="scroller" style="overflow:scroll; width:300px; height:400px;"> + <div id="outer" style="position:sticky; top:10px;"> + <div style="height:300px;"> + <span id="middle" style="position:sticky; top:25px;"> + <span id="inner" style="position:sticky; top:45px;"></span> + </span> + </div> + </div> + </div> + )HTML"); + + const auto* outer_properties = PaintPropertiesForElement("outer"); + ASSERT_TRUE(outer_properties && outer_properties->StickyTranslation()); + EXPECT_FALSE(outer_properties->StickyTranslation() + ->RequiresCompositingForStickyPosition()); + EXPECT_EQ(gfx::Vector2dF(0, 10), + outer_properties->StickyTranslation()->Translation2D()); + EXPECT_EQ(nullptr, + outer_properties->StickyTranslation()->GetStickyConstraint()); + + const auto* middle_properties = PaintPropertiesForElement("middle"); + ASSERT_TRUE(middle_properties && middle_properties->StickyTranslation()); + EXPECT_FALSE(middle_properties->StickyTranslation() + ->RequiresCompositingForStickyPosition()); + EXPECT_EQ(gfx::Vector2dF(0, 15), + middle_properties->StickyTranslation()->Translation2D()); + EXPECT_EQ(nullptr, + middle_properties->StickyTranslation()->GetStickyConstraint()); + + const auto* inner_properties = PaintPropertiesForElement("inner"); + ASSERT_TRUE(inner_properties && inner_properties->StickyTranslation()); + EXPECT_FALSE(inner_properties->StickyTranslation() + ->RequiresCompositingForStickyPosition()); EXPECT_EQ(gfx::Vector2dF(0, 20), inner_properties->StickyTranslation()->Translation2D()); EXPECT_EQ(nullptr,
diff --git a/third_party/blink/renderer/core/paint/replaced_painter.cc b/third_party/blink/renderer/core/paint/replaced_painter.cc index 7a23c537..0f1cc456 100644 --- a/third_party/blink/renderer/core/paint/replaced_painter.cc +++ b/third_party/blink/renderer/core/paint/replaced_painter.cc
@@ -4,8 +4,10 @@ #include "third_party/blink/renderer/core/paint/replaced_painter.h" +#include "base/metrics/histogram_macros.h" #include "third_party/abseil-cpp/absl/types/optional.h" #include "third_party/blink/renderer/core/frame/local_frame.h" +#include "third_party/blink/renderer/core/frame/web_feature.h" #include "third_party/blink/renderer/core/layout/layout_replaced.h" #include "third_party/blink/renderer/core/layout/svg/layout_svg_root.h" #include "third_party/blink/renderer/core/paint/box_painter.h" @@ -21,6 +23,7 @@ #include "third_party/blink/renderer/platform/graphics/paint/display_item_cache_skipper.h" #include "third_party/blink/renderer/platform/graphics/paint/drawing_recorder.h" #include "third_party/blink/renderer/platform/graphics/paint/scoped_paint_chunk_properties.h" +#include "third_party/blink/renderer/platform/instrumentation/use_counter.h" namespace blink { @@ -163,6 +166,24 @@ layout_replaced_); layout_replaced_.PaintReplaced(content_paint_state.GetPaintInfo(), content_paint_state.PaintOffset()); + + if (layout_replaced_.BelongsToElementChangingOverflowBehaviour() && + !layout_replaced_.ClipsToContentBox() && + layout_replaced_.HasVisualOverflow()) { + UseCounter::Count(layout_replaced_.GetDocument(), + WebFeature::kReplacedElementPaintedWithOverflow); + + auto overflow_size = layout_replaced_.PhysicalVisualOverflowRect().size; + auto overflow_area = overflow_size.width * overflow_size.height; + + auto content_size = layout_replaced_.Size(); + auto content_area = content_size.Width() * content_size.Height(); + + DCHECK_GT(overflow_area, content_area); + UMA_HISTOGRAM_COUNTS_100000( + "Blink.Overflow.ReplacedElementAreaOutsideContentRect", + (overflow_area - content_area).ToInt()); + } } if (layout_replaced_.StyleRef().Visibility() == EVisibility::kVisible &&
diff --git a/third_party/blink/renderer/platform/graphics/compositing/pending_layer.cc b/third_party/blink/renderer/platform/graphics/compositing/pending_layer.cc index 5d103e7..b91fbc0 100644 --- a/third_party/blink/renderer/platform/graphics/compositing/pending_layer.cc +++ b/third_party/blink/renderer/platform/graphics/compositing/pending_layer.cc
@@ -424,14 +424,14 @@ !node->IsRoot() && !can_be_decomposited.Contains(node); node = &node->Parent()->Unalias()) { if (!node->IsIdentityOr2DTranslation() || node->ScrollNode() || - node->GetStickyConstraint() || - node->IsAffectedByOuterViewportBoundsDelta() || node->HasDirectCompositingReasonsOtherThan3dTransform() || !node->FlattensInheritedTransformSameAsParent() || !node->BackfaceVisibilitySameAsParent()) { mark_not_decompositable(*node); break; } + DCHECK(!node->GetStickyConstraint()); + DCHECK(!node->IsAffectedByOuterViewportBoundsDelta()); can_be_decomposited.insert(node, true); }
diff --git a/third_party/blink/renderer/platform/graphics/compositing_reasons.h b/third_party/blink/renderer/platform/graphics/compositing_reasons.h index cff06d6..54668f2 100644 --- a/third_party/blink/renderer/platform/graphics/compositing_reasons.h +++ b/third_party/blink/renderer/platform/graphics/compositing_reasons.h
@@ -118,13 +118,13 @@ // Various combinations of compositing reasons are defined here also, for // more intuitive and faster bitwise logic. - kComboScrollDependentPosition = kFixedPosition | kStickyPosition, + // Note that translate is not included, because we care about transforms // that are not IsIdentityOrTranslation(). kPreventingSubpixelAccumulationReasons = kWillChangeTransform | kWillChangeScale | kWillChangeRotate, kDirectReasonsForPaintOffsetTranslationProperty = - kComboScrollDependentPosition | kAffectedByOuterViewportBoundsDelta | + kFixedPosition | kAffectedByOuterViewportBoundsDelta | kFixedToViewport | kVideo | kCanvas | kPlugin | kIFrame, // TODO(dbaron): kWillChangeOther probably shouldn't be in this list. kDirectReasonsForTransformProperty =
diff --git a/third_party/blink/renderer/platform/graphics/paint/transform_paint_property_node.h b/third_party/blink/renderer/platform/graphics/paint/transform_paint_property_node.h index fc78cdf..16270ae 100644 --- a/third_party/blink/renderer/platform/graphics/paint/transform_paint_property_node.h +++ b/third_party/blink/renderer/platform/graphics/paint/transform_paint_property_node.h
@@ -364,9 +364,8 @@ return DirectCompositingReasons() & CompositingReason::kFixedToViewport; } - bool RequiresCompositingForScrollDependentPosition() const { - return DirectCompositingReasons() & - CompositingReason::kComboScrollDependentPosition; + bool RequiresCompositingForStickyPosition() const { + return DirectCompositingReasons() & CompositingReason::kStickyPosition; } CompositingReasons DirectCompositingReasonsForDebugging() const {
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5 index cf1c788..f160a1e 100644 --- a/third_party/blink/renderer/platform/runtime_enabled_features.json5 +++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -1896,7 +1896,6 @@ }, { name: "PrivateNetworkAccessPermissionPrompt", - status: "test", }, { name: "PushMessaging",
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations index 35d7cb1..0d3aaaae 100644 --- a/third_party/blink/web_tests/TestExpectations +++ b/third_party/blink/web_tests/TestExpectations
@@ -1788,8 +1788,6 @@ crbug.com/1078927 virtual/layout_ng_table_frag/fragmentation/border-spacing-break-before-unbreakable-row.html [ Failure ] crbug.com/1078927 virtual/layout_ng_table_frag/fragmentation/fragmented-rowspan-alignment.html [ Failure ] crbug.com/1078927 virtual/layout_ng_table_frag/fragmentation/fragmented-rowspan.html [ Failure ] -crbug.com/1078927 virtual/layout_ng_table_frag/fragmentation/nested-repeating-thead-2.html [ Failure ] -crbug.com/1078927 virtual/layout_ng_table_frag/fragmentation/nested-repeating-thead-3.html [ Failure ] crbug.com/1078927 virtual/layout_ng_table_frag/fragmentation/no-repeating-table-header-after-sections.html [ Failure ] crbug.com/1078927 virtual/layout_ng_table_frag/fragmentation/single-line-cells-multiple-tables-caption-repeating-thead-tfoot-with-border-spacing-at-top-of-row-2.html [ Failure ] crbug.com/1078927 virtual/layout_ng_table_frag/fragmentation/single-line-cells-multiple-tables-caption-repeating-thead-tfoot-with-border-spacing-at-top-of-row-3.html [ Failure ]
diff --git a/third_party/blink/web_tests/VirtualTestSuites b/third_party/blink/web_tests/VirtualTestSuites index d467078..e557b009 100644 --- a/third_party/blink/web_tests/VirtualTestSuites +++ b/third_party/blink/web_tests/VirtualTestSuites
@@ -8,6 +8,12 @@ ] }, { + "prefix": "pna-permission", + "platforms": ["Linux", "Mac", "Win", "Fuchsia"], + "bases": ["external/wpt/fetch/private-network-access"], + "args": ["--enable-features=PrivateNetworkAccessPermissionPrompt"] + }, + { "prefix": "disable-accept-language-header", "platforms": ["Linux", "Mac", "Win"], "bases": ["http/tests/navigation/language"],
diff --git a/third_party/blink/web_tests/fragmentation/nested-repeating-thead-2.html b/third_party/blink/web_tests/fragmentation/nested-repeating-thead-2.html index 41c504b..197efbb 100644 --- a/third_party/blink/web_tests/fragmentation/nested-repeating-thead-2.html +++ b/third_party/blink/web_tests/fragmentation/nested-repeating-thead-2.html
@@ -3,7 +3,7 @@ .header { line-height: 80px; } -thead, tfoot { +thead, tfoot, tr { break-inside: avoid; } </style>
diff --git a/third_party/blink/web_tests/fragmentation/nested-repeating-thead-3.html b/third_party/blink/web_tests/fragmentation/nested-repeating-thead-3.html index 89b836e..9ff97166 100644 --- a/third_party/blink/web_tests/fragmentation/nested-repeating-thead-3.html +++ b/third_party/blink/web_tests/fragmentation/nested-repeating-thead-3.html
@@ -3,7 +3,7 @@ .barcode { line-height: 40px; } -thead, tfoot { +thead, tfoot, tr { break-inside: avoid; } </style> @@ -31,7 +31,7 @@ </tr> <tr> <td> - <div style="height: 445px;"></div> + <div style="height: 400px;"></div> </td> </tr> <tr>
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/network/same-site-issue-warn-cookie-strict-subresource-context-downgrade.js b/third_party/blink/web_tests/http/tests/inspector-protocol/network/same-site-issue-warn-cookie-strict-subresource-context-downgrade.js index 41937c62d..69e827b 100644 --- a/third_party/blink/web_tests/http/tests/inspector-protocol/network/same-site-issue-warn-cookie-strict-subresource-context-downgrade.js +++ b/third_party/blink/web_tests/http/tests/inspector-protocol/network/same-site-issue-warn-cookie-strict-subresource-context-downgrade.js
@@ -9,7 +9,7 @@ const setCookieUrl = 'https://cookie.test:8443/inspector-protocol/network/resources/set-cookie.php?cookie=' + encodeURIComponent('name=value; SameSite=Strict'); - await session.evaluate(`fetch('${setCookieUrl}', {method: 'POST', credentials: 'include'})`); + session.evaluate(`fetch('${setCookieUrl}', {method: 'POST', credentials: 'include'})`); const issue = await dp.Audits.onceIssueAdded(); testRunner.log(issue.params, 'Inspector issue:');
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/storage/dom-storage-storage-key-clear.js b/third_party/blink/web_tests/http/tests/inspector-protocol/storage/dom-storage-storage-key-clear.js new file mode 100644 index 0000000..9fde068 --- /dev/null +++ b/third_party/blink/web_tests/http/tests/inspector-protocol/storage/dom-storage-storage-key-clear.js
@@ -0,0 +1,24 @@ +(async function(testRunner) { + const {dp, session} = await testRunner.startBlank( + `Tests that clearing data works for DOMStorage with storageKey\n`); + + await dp.DOMStorage.enable(); + await dp.Page.enable(); + + const addedPromise = dp.DOMStorage.onceDomStorageItemAdded(); + session.evaluate('window.localStorage.setItem("testKey", "testItem")'); + const event = await addedPromise; + const storageId = event.params.storageId; + const storageKey = storageId.storageKey; + + testRunner.log(`Set item: ${await session.evaluate('window.localStorage.getItem("testKey")')}`) + testRunner.log(`storageId.storageKey: ${typeof storageKey}`) + testRunner.log(storageKey ? "not empty\n" : "empty\n"); + + await dp.Storage.clearDataForStorageKey({storageKey: storageKey, storageTypes: 'local_storage'}); + + testRunner.log("Clear data"); + testRunner.log(`Get item: ${await session.evaluate('window.localStorage.getItem("testKey")')}`); + + testRunner.completeTest(); +})
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/storage/dom-storage-storage-key-functionality.js b/third_party/blink/web_tests/http/tests/inspector-protocol/storage/dom-storage-storage-key-functionality.js index 43b12f5..744ec6a 100644 --- a/third_party/blink/web_tests/http/tests/inspector-protocol/storage/dom-storage-storage-key-functionality.js +++ b/third_party/blink/web_tests/http/tests/inspector-protocol/storage/dom-storage-storage-key-functionality.js
@@ -1,12 +1,12 @@ (async function(testRunner) { - const {dp} = await testRunner.startBlank( + const {dp, session} = await testRunner.startBlank( `Tests DOMStorage functionality with storageKey\n`); await dp.DOMStorage.enable(); await dp.Page.enable(); const addedPromise = dp.DOMStorage.onceDomStorageItemAdded(); - window.localStorage.setItem("testKey", "testItem"); + session.evaluate('window.localStorage.setItem("testKey", "testItem")'); const event = await addedPromise; const storageId = event.params.storageId;
diff --git a/third_party/blink/web_tests/platform/generic/http/tests/inspector-protocol/storage/dom-storage-storage-key-clear-expected.txt b/third_party/blink/web_tests/platform/generic/http/tests/inspector-protocol/storage/dom-storage-storage-key-clear-expected.txt new file mode 100644 index 0000000..a2233fb8 --- /dev/null +++ b/third_party/blink/web_tests/platform/generic/http/tests/inspector-protocol/storage/dom-storage-storage-key-clear-expected.txt
@@ -0,0 +1,9 @@ +Tests that clearing data works for DOMStorage with storageKey + +Set item: testItem +storageId.storageKey: string +not empty + +Clear data +Get item: null +
diff --git a/third_party/blink/web_tests/platform/generic/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt b/third_party/blink/web_tests/platform/generic/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt index f11be43..3855262 100644 --- a/third_party/blink/web_tests/platform/generic/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt +++ b/third_party/blink/web_tests/platform/generic/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt
@@ -1323,7 +1323,6 @@ getter referrer getter referrerPolicy getter signal - getter targetAddressSpace getter url method arrayBuffer method blob
diff --git a/third_party/blink/web_tests/platform/generic/webexposed/global-interface-listing-dedicated-worker-expected.txt b/third_party/blink/web_tests/platform/generic/webexposed/global-interface-listing-dedicated-worker-expected.txt index 08c8d1a3..d3f0dde 100644 --- a/third_party/blink/web_tests/platform/generic/webexposed/global-interface-listing-dedicated-worker-expected.txt +++ b/third_party/blink/web_tests/platform/generic/webexposed/global-interface-listing-dedicated-worker-expected.txt
@@ -1376,7 +1376,6 @@ [Worker] getter referrer [Worker] getter referrerPolicy [Worker] getter signal -[Worker] getter targetAddressSpace [Worker] getter url [Worker] method arrayBuffer [Worker] method blob
diff --git a/third_party/blink/web_tests/platform/generic/webexposed/global-interface-listing-expected.txt b/third_party/blink/web_tests/platform/generic/webexposed/global-interface-listing-expected.txt index de7bd25..6df9e2e 100644 --- a/third_party/blink/web_tests/platform/generic/webexposed/global-interface-listing-expected.txt +++ b/third_party/blink/web_tests/platform/generic/webexposed/global-interface-listing-expected.txt
@@ -7160,7 +7160,6 @@ getter referrer getter referrerPolicy getter signal - getter targetAddressSpace getter url method arrayBuffer method blob
diff --git a/third_party/blink/web_tests/platform/generic/webexposed/global-interface-listing-shared-worker-expected.txt b/third_party/blink/web_tests/platform/generic/webexposed/global-interface-listing-shared-worker-expected.txt index 5c557a45..16c7be69 100644 --- a/third_party/blink/web_tests/platform/generic/webexposed/global-interface-listing-shared-worker-expected.txt +++ b/third_party/blink/web_tests/platform/generic/webexposed/global-interface-listing-shared-worker-expected.txt
@@ -1215,7 +1215,6 @@ [Worker] getter referrer [Worker] getter referrerPolicy [Worker] getter signal -[Worker] getter targetAddressSpace [Worker] getter url [Worker] method arrayBuffer [Worker] method blob
diff --git a/third_party/blink/web_tests/virtual/pna-permission/README.md b/third_party/blink/web_tests/virtual/pna-permission/README.md new file mode 100644 index 0000000..39867dc --- /dev/null +++ b/third_party/blink/web_tests/virtual/pna-permission/README.md
@@ -0,0 +1 @@ +This directory is for testing PrivateNetworkAccessPermissionPrompt (https://crbug.com/1338438).
diff --git a/third_party/blink/web_tests/wpt_internal/fenced_frame/unfenced-top.https.html b/third_party/blink/web_tests/wpt_internal/fenced_frame/unfenced-top.https.html index 5c8bf79a..139f334 100644 --- a/third_party/blink/web_tests/wpt_internal/fenced_frame/unfenced-top.https.html +++ b/third_party/blink/web_tests/wpt_internal/fenced_frame/unfenced-top.https.html
@@ -250,6 +250,30 @@ await new_window.execute(() => {}); }, '_unfencedTop :blob URL failure'); +// Test that fragment navigations out of a fenced frame using _unfencedTop +// do not act as same-document navigations. +promise_test(async() => { + // Create a new window. + const new_window = await createAndValidateWindow(); + + await new_window.execute(async () => { + // Save some state in the document. + window.foo = "foo"; + + // Navigate _unfencedTop using a fragment URL. + window.executor.suspend(() => { + window.fenced_frame.execute((parent_url) => { + const result = window.open(parent_url+'#foo', '_unfencedTop'); + }, [location.href]); + }); + }); + + // Observe that there is a new document (the old state is lost). + await new_window.execute(async () => { + assert_equals(window.foo, undefined); + }); +}, '_unfencedTop fragment navigation'); + async function click(frame) { var actions = new test_driver.Actions(); await actions.pointerMove(0, 0, {origin: frame})
diff --git a/third_party/wpt_tools/README.chromium b/third_party/wpt_tools/README.chromium index c373a3b..2b5c1fe 100644 --- a/third_party/wpt_tools/README.chromium +++ b/third_party/wpt_tools/README.chromium
@@ -1,7 +1,7 @@ Name: web-platform-tests - Test Suites for Web Platform specifications Short Name: wpt URL: https://github.com/web-platform-tests/wpt/ -Version: 8ee81d9c52f7c47292ba8ae731b52de03a078352 +Version: 6cc880d5283965ca9c9b6eb2a8af4ba0c024f872 License: LICENSES FOR W3C TEST SUITES (https://www.w3.org/Consortium/Legal/2008/03-bsd-license.html) License File: NOT_SHIPPED Security Critical: no
diff --git a/third_party/wpt_tools/wpt/tools/wpt/browser.py b/third_party/wpt_tools/wpt/tools/wpt/browser.py index cf8336b7..cc3e793 100644 --- a/third_party/wpt_tools/wpt/tools/wpt/browser.py +++ b/third_party/wpt_tools/wpt/tools/wpt/browser.py
@@ -6,7 +6,6 @@ import stat import subprocess import tempfile -import xml.etree.ElementTree as etree # noqa: N813 from abc import ABCMeta, abstractmethod from datetime import datetime, timedelta from distutils.spawn import find_executable @@ -1452,8 +1451,26 @@ requirements = "requirements_safari.txt" def _find_downloads(self): - def text_content(e): - return etree.tostring(e, encoding="unicode", method="text") + def text_content(e, __output=None): + # this doesn't use etree.tostring so that we can add spaces for p and br + if __output is None: + __output = [] + + if e.tag == "p": + __output.append("\n\n") + + if e.tag == "br": + __output.append("\n") + + if e.text is not None: + __output.append(e.text) + + for child in e: + text_content(child, __output) + if child.tail is not None: + __output.append(child.tail) + + return "".join(__output) self.logger.info("Finding STP download URLs") resp = get("https://developer.apple.com/safari/download/") @@ -1472,38 +1489,50 @@ if {"download", "dmg", "zip"} & class_names: downloads.append(candidate) - """ - When converting to string with text_content, it converts <br> to no space. - For example: - Input: Safari Technology Preview<br>for macOS Ventura - Output: Safari Technology Previewfor macOS Ventura - """ - stp_link_text = re.compile(r"^\s*Safari\s+Technology\s+Preview\s*(?:[0-9]+\s+)?for\s+macOS") + # Note we use \s throughout for space as we don't care what form the whitespace takes + stp_link_text = re.compile( + r"^\s*Safari\s+Technology\s+Preview\s+(?:[0-9]+\s+)?for\s+macOS" + ) requirement = re.compile( - r"Requires\s+macOS\s+([0-9\.]+)\s+(?:or\s+later|beta)." + r"""(?x) # (extended regexp syntax for comments) + ^\s*Requires\s+macOS\s+ # Starting with the magic string + ([0-9\.]+) # A macOS version number of numbers and dots + (?:\s+beta(?:\s+[0-9]+)?)? # Optionally a beta, itself optionally with a number (no dots!) + (?:\s+or\s+later)? # Optionally an 'or later' + \.\s*$ # Ending with a literal dot + """ ) stp_downloads = [] for download in downloads: for link in download.iterfind(".//a[@href]"): - if stp_link_text.match(text_content(link)): + if stp_link_text.search(text_content(link)): break + else: + self.logger.debug("non-matching anchor: " + text_content(link)) else: continue - m = requirement.search(text_content(download)) - if m: - version = m.group(1) + for el in download.iter(): + # avoid assuming any given element here, just assume it is a single element + m = requirement.search(text_content(el)) + if m: + version = m.group(1) - # This assumes the current macOS numbering, whereby X.Y is compatible - # with X.(Y+1), e.g. 12.4 is compatible with 12.3, but 13.0 isn't - # compatible with 12.3. This doesn't handle the former 10.* numbering. - if "." in version: - spec = SpecifierSet(f"~={version}") - else: - spec = SpecifierSet(f"=={version}.*") + # This assumes the current macOS numbering, whereby X.Y is compatible + # with X.(Y+1), e.g. 12.4 is compatible with 12.3, but 13.0 isn't + # compatible with 12.3. + if version.count(".") >= (2 if version.startswith("10.") else 1): + spec = SpecifierSet(f"~={version}") + else: + spec = SpecifierSet(f"=={version}.*") - stp_downloads.append((spec, link.attrib["href"])) + stp_downloads.append((spec, link.attrib["href"])) + break + else: + self.logger.debug( + "Found a link but no requirement: " + text_content(download) + ) if stp_downloads: self.logger.info(
diff --git a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/executors/actions.py b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/executors/actions.py index 07ba761..596576b0 100644 --- a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/executors/actions.py +++ b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/executors/actions.py
@@ -70,9 +70,13 @@ def __init__(self, logger, protocol): self.logger = logger self.protocol = protocol + self.requires_state_reset = False def __call__(self, payload): # TODO: some sort of shallow error checking + if self.requires_state_reset: + self.reset() + self.requires_state_reset = True actions = payload["actions"] for actionSequence in actions: if actionSequence["type"] == "pointer": @@ -85,6 +89,10 @@ def get_element(self, element_selector): return self.protocol.select.element_by_selector(element_selector) + def reset(self): + self.protocol.action_sequence.release() + self.requires_state_reset = False + class GenerateTestReportAction: name = "generate_test_report"
diff --git a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/executors/executormarionette.py b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/executors/executormarionette.py index 051821c0..fedb7e959 100644 --- a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/executors/executormarionette.py +++ b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/executors/executormarionette.py
@@ -481,6 +481,9 @@ self.logger.info(actions) self.marionette._send_message("WebDriver:PerformActions", actions) + def release(self): + self.marionette._send_message("WebDriver:ReleaseActions", {}) + class MarionetteTestDriverProtocolPart(TestDriverProtocolPart): def setup(self):
diff --git a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/executors/executorselenium.py b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/executors/executorselenium.py index 22842e9..5dc5505 100644 --- a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/executors/executorselenium.py +++ b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/executors/executorselenium.py
@@ -222,6 +222,9 @@ def send_actions(self, actions): self.webdriver.execute(Command.W3C_ACTIONS, {"actions": actions}) + def release(self): + self.webdriver.execute(Command.W3C_CLEAR_ACTIONS, {}) + class SeleniumTestDriverProtocolPart(TestDriverProtocolPart): def setup(self):
diff --git a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/executors/executorwebdriver.py b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/executors/executorwebdriver.py index 4ba78b2..a5e5e57 100644 --- a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/executors/executorwebdriver.py +++ b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/executors/executorwebdriver.py
@@ -237,6 +237,9 @@ def send_actions(self, actions): self.webdriver.actions.perform(actions['actions']) + def release(self): + self.webdriver.actions.release() + class WebDriverTestDriverProtocolPart(TestDriverProtocolPart): def setup(self): @@ -627,6 +630,8 @@ self.protocol.base.execute_script(self.wait_script, True) screenshot = self.protocol.webdriver.screenshot() + if screenshot is None: + raise ValueError('screenshot is None') # strip off the data:img/png, part of the url if screenshot.startswith("data:image/png;base64,"):
diff --git a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/executors/protocol.py b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/executors/protocol.py index 86cfce8..23fa797 100644 --- a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/executors/protocol.py +++ b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/executors/protocol.py
@@ -384,6 +384,9 @@ :param actions: A protocol-specific handle to an array of actions.""" pass + def release(self): + pass + class TestDriverProtocolPart(ProtocolPart): """Protocol part that implements the basic functionality required for
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl index b48dbe6..41dd635 100644 --- a/tools/mb/mb_config.pyl +++ b/tools/mb/mb_config.pyl
@@ -369,6 +369,7 @@ 'Mac Builder Next': 'mac_arm64_gpu_tests_release_bot_minimal_symbols_no_nacl', 'Mac deterministic': 'release_bot_mac_strip_minimal_symbols', 'Mac deterministic (dbg)': 'debug_bot', + 'Mac deterministic (reclient shadow)': 'release_bot_mac_strip_minimal_symbols_reclient', 'Site Isolation Android': 'android_release_bot_minimal_symbols_arm64_reclient', 'VR Linux': 'vr_release_bot_reclient', 'Win 10 Fast Ring': 'release_trybot_minimal_symbols_reclient', @@ -616,6 +617,7 @@ 'ios-simulator-noncq': 'ios_simulator_debug_static_bot_xctest', 'mac-arm64-on-arm64-rel': 'mac_arm64_release_bot', 'mac-arm64-rel': 'mac_arm64_gpu_tests_release_bot_minimal_symbols_no_nacl', + 'mac-arm64-rel (reclient shadow)': 'mac_arm64_gpu_tests_release_bot_minimal_symbols_no_nacl_reclient', }, 'chromium.memory': { @@ -3374,6 +3376,10 @@ 'release_bot', 'mac_strip', 'minimal_symbols', 'arm64', ], + 'release_bot_mac_strip_minimal_symbols_reclient': [ + 'release_bot_reclient', 'mac_strip', 'minimal_symbols', + ], + 'release_bot_minimal_symbols': [ 'release_bot', 'minimal_symbols', ],
diff --git a/tools/mb/mb_config_expectations/chromium.fyi.json b/tools/mb/mb_config_expectations/chromium.fyi.json index f78e174..25cf058 100644 --- a/tools/mb/mb_config_expectations/chromium.fyi.json +++ b/tools/mb/mb_config_expectations/chromium.fyi.json
@@ -487,6 +487,16 @@ "use_goma": true } }, + "Mac deterministic (reclient shadow)": { + "gn_args": { + "dcheck_always_on": false, + "enable_stripping": true, + "is_component_build": false, + "is_debug": false, + "symbol_level": 1, + "use_remoteexec": true + } + }, "Site Isolation Android": { "gn_args": { "dcheck_always_on": false,
diff --git a/tools/mb/mb_config_expectations/chromium.mac.json b/tools/mb/mb_config_expectations/chromium.mac.json index 2d52b89a..eca6e79 100644 --- a/tools/mb/mb_config_expectations/chromium.mac.json +++ b/tools/mb/mb_config_expectations/chromium.mac.json
@@ -127,5 +127,18 @@ "target_cpu": "arm64", "use_goma": true } + }, + "mac-arm64-rel (reclient shadow)": { + "gn_args": { + "dcheck_always_on": false, + "enable_nacl": false, + "ffmpeg_branding": "Chrome", + "is_component_build": false, + "is_debug": false, + "proprietary_codecs": true, + "symbol_level": 1, + "target_cpu": "arm64", + "use_remoteexec": true + } } } \ No newline at end of file
diff --git a/tools/metrics/actions/actions.xml b/tools/metrics/actions/actions.xml index ff1170c9..7bfcce7 100644 --- a/tools/metrics/actions/actions.xml +++ b/tools/metrics/actions/actions.xml
@@ -3715,6 +3715,12 @@ </description> </action> +<action name="Ash_Clipboard_CopiedItem"> + <owner>ckincaid@chromium.org</owner> + <owner>multipaste@google.com</owner> + <description>Emitted when a user copies data to their clipboard.</description> +</action> + <action name="Ash_ClipboardHistory_PastedItem1"> <owner>ckincaid@chromium.org</owner> <owner>multipaste@google.com</owner>
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml index f25ffe7b..a541261 100644 --- a/tools/metrics/histograms/enums.xml +++ b/tools/metrics/histograms/enums.xml
@@ -40441,8 +40441,8 @@ <int value="3896" label="WebBluetoothManufacturerDataFilter"/> <int value="3897" label="SanitizerAPIGetConfig"/> <int value="3898" label="SanitizerAPIGetDefaultConfig"/> - <int value="3899" label="ComputePressureObserver_Constructor"/> - <int value="3900" label="ComputePressureObserver_Observe"/> + <int value="3899" label="PressureObserver_Constructor"/> + <int value="3900" label="PressureObserver_Observe"/> <int value="3901" label="OBSOLETE_ComputePressureObserver_Stop"/> <int value="3902" label="WebAppWindowControlsOverlay"/> <int value="3903" label="PaymentRequestShowWithoutGestureOrToken"/> @@ -40739,9 +40739,9 @@ <int value="4181" label="BluetoothDeviceForget"/> <int value="4182" label="TopicsAPI_BrowsingTopics_Method"/> <int value="4183" label="BlockingAttributeRenderToken"/> - <int value="4184" label="ComputePressureObserver_Unobserve"/> - <int value="4185" label="ComputePressureObserver_Disconnect"/> - <int value="4186" label="ComputePressureObserver_TakeRecords"/> + <int value="4184" label="PressureObserver_Unobserve"/> + <int value="4185" label="PressureObserver_Disconnect"/> + <int value="4186" label="PressureObserver_TakeRecords"/> <int value="4187" label="PrivacySandboxAdsAPIs"/> <int value="4188" label="Fledge"/> <int value="4189" label="ElementShowPopup"/> @@ -40871,6 +40871,7 @@ <int value="4308" label="SendBeaconWithFormData"/> <int value="4309" label="SendBeaconWithURLSearchParams"/> <int value="4310" label="SendBeaconWithUSVString"/> + <int value="4311" label="ReplacedElementPaintedWithOverflow"/> </enum> <enum name="FeaturePolicyAllowlistType"> @@ -60231,6 +60232,7 @@ <int value="934236781" label="OmniboxSuggestionsRecyclerView:enabled"/> <int value="934292666" label="DownloadRename:enabled"/> <int value="934805020" label="CornerShortcuts:disabled"/> + <int value="935653796" label="CrOSLateBootMediaDynamicCgroup:enabled"/> <int value="935655516" label="password-import-export:disabled"/> <int value="936341613" label="OfflinePagesCT:disabled"/> <int value="936919953" label="bypass-app-banner-engagement-checks"/> @@ -60805,6 +60807,7 @@ <int value="1293697921" label="RequestDesktopSiteExceptions:enabled"/> <int value="1294131571" label="disable-winrt-midi-api"/> <int value="1294306055" label="FeedInteractiveRefresh:disabled"/> + <int value="1296661698" label="CrOSLateBootMediaDynamicCgroup:disabled"/> <int value="1296878388" label="PermissionPredictions:disabled"/> <int value="1296958520" label="hide-active-apps-from-shelf"/> <int value="1298734793" label="OsSettingsAppNotificationsPage:disabled"/> @@ -76365,6 +76368,38 @@ <int value="25" label="DOM_LOADING"/> </enum> +<enum name="PerformanceMonitor.UsageScenario.LongInterval"> + <int value="1" label="All Tabs Hidden Audio"/> + <int value="2" label="All Tabs Hidden No Video Capture or Audio"/> + <int value="4" label="All Tabs Hidden Video Capture"/> + <int value="5" label="Audio"/> + <int value="6" label="Embedded Video No Navigation"/> + <int value="7" label="Embedded Video With Navigation"/> + <int value="8" label="Fullscreen Video"/> + <int value="9" label="Interaction"/> + <int value="10" label="Navigation"/> + <int value="11" label="Passive"/> + <int value="12" label="Video capture"/> + <int value="13" label="Zero Window"/> +</enum> + +<enum name="PerformanceMonitor.UsageScenario.ShortInterval"> + <int value="1" label="All Tabs Hidden Audio"/> + <int value="2" label="All Tabs Hidden No Video Capture or Audio"/> + <int value="3" label="All Tabs Hidden No Video Capture or Audio (Recent)"/> + <int value="4" label="All Tabs Hidden Video Capture"/> + <int value="5" label="Audio"/> + <int value="6" label="Embedded Video No Navigation"/> + <int value="7" label="Embedded Video With Navigation"/> + <int value="8" label="Fullscreen Video"/> + <int value="9" label="Interaction"/> + <int value="10" label="Navigation"/> + <int value="11" label="Passive"/> + <int value="12" label="Video capture"/> + <int value="13" label="Zero Window"/> + <int value="14" label="Zero Window (Recent)"/> +</enum> + <enum name="PeripheralConnectivityResult"> <!-- This must be kept current with PeripheralConnectivityResults located in ash/components/peripheral_notification/peripheral_notification_manager.h -->
diff --git a/tools/metrics/histograms/metadata/autofill/histograms.xml b/tools/metrics/histograms/metadata/autofill/histograms.xml index 6a993f5..4298cec 100644 --- a/tools/metrics/histograms/metadata/autofill/histograms.xml +++ b/tools/metrics/histograms/metadata/autofill/histograms.xml
@@ -1730,6 +1730,10 @@ <histogram name="Autofill.IsValueNotAutofilledOverExistingValueSameAsSubmittedValue" enum="Boolean" expires_after="2022-12-25"> + <obsolete> + Deprecated in July 2022. Subsumed by + Autofill.IsValueNotAutofilledOverExistingValueSameAsSubmittedValue2. + </obsolete> <owner>vidhanj@google.com</owner> <owner>koerber@google.com</owner> <owner>chrome-autofill-alerts@google.com</owner> @@ -1741,6 +1745,20 @@ </summary> </histogram> +<histogram + name="Autofill.IsValueNotAutofilledOverExistingValueSameAsSubmittedValue2" + enum="Boolean" expires_after="2022-12-25"> + <owner>vidhanj@google.com</owner> + <owner>koerber@google.com</owner> + <owner>chrome-autofill-alerts@google.com</owner> + <summary> + This metric is recorderd on form submission for each field that had an + initial value on page load and was edited by the user later on. The result + of the equality comparison between the submitted field value and supposedly + autofillable value is emitted by this metric. + </summary> +</histogram> + <histogram name="Autofill.KeyMetrics.FillingAcceptance{AutofillFormType}" enum="BooleanAutofillFillingAcceptance" expires_after="2022-12-12"> <owner>battre@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/blink/histograms.xml b/tools/metrics/histograms/metadata/blink/histograms.xml index 4fd0d39..af984c0 100644 --- a/tools/metrics/histograms/metadata/blink/histograms.xml +++ b/tools/metrics/histograms/metadata/blink/histograms.xml
@@ -2126,6 +2126,16 @@ </summary> </histogram> +<histogram name="Blink.Overflow.ReplacedElementAreaOutsideContentRect" + units="pixels" expires_after="2022-12-01"> + <owner>khushalsagar@chromium.org</owner> + <owner>vmpstr@chromium.org</owner> + <summary> + Computes the area of img, video or canvas elements which is painted outside + the content rect if 'overflow' property is respected on these elements. + </summary> +</histogram> + <histogram name="Blink.Paint.PaintArtifactCompositorUpdateReason" enum="PaintArtifactCompositorUpdateReason" expires_after="2022-12-01"> <owner>pdr@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/cryptohome/histograms.xml b/tools/metrics/histograms/metadata/cryptohome/histograms.xml index 4bc5df44..ba0cdbd 100644 --- a/tools/metrics/histograms/metadata/cryptohome/histograms.xml +++ b/tools/metrics/histograms/metadata/cryptohome/histograms.xml
@@ -786,6 +786,32 @@ </summary> </histogram> +<histogram name="Cryptohome.TimeToUSSLoadPersisted" units="ms" + expires_after="2023-04-01"> + <owner>thomascedeno@google.com</owner> + <owner>cros-hwsec+uma@chromium.org</owner> + <summary> + The amount of time cryptohome spends reading persistent storage to populate + an encrypted USS container flatbuffer, or the time + UserSecretStash::LoadPersisted() takes to complete. The metric is emitted + during user authentication in cryptohome, when an AuthFactor must be + authenticated and UserSecretStash must read the corresponding USS file. + </summary> +</histogram> + +<histogram name="Cryptohome.TimeToUSSPersist" units="ms" + expires_after="2023-04-01"> + <owner>thomascedeno@google.com</owner> + <owner>cros-hwsec+uma@chromium.org</owner> + <summary> + The amount of time cryptohome spends writing the USS container flatbuffer to + persistent storage. This metric records the time UserSecretStash::Persist() + takes to complete. The metric is emitted when a user adds, removes or + updates a means of authentication or AuthFactor. Manipulating an AuthFactor + requires UserSecretStash to write to the corresponding USS file. + </summary> +</histogram> + <histogram name="Cryptohome.TpmResults" enum="CryptohomeTpmResults" expires_after="2022-10-30"> <owner>afakhry@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/power/histograms.xml b/tools/metrics/histograms/metadata/power/histograms.xml index ad57c57ad..48269e7 100644 --- a/tools/metrics/histograms/metadata/power/histograms.xml +++ b/tools/metrics/histograms/metadata/power/histograms.xml
@@ -496,6 +496,36 @@ <token key="UsageScenario" variants="UsageScenario"/> </histogram> +<histogram name="PerformanceMonitor.UsageScenario.LongInterval" + enum="PerformanceMonitor.UsageScenario.LongInterval" + expires_after="2023-06-18"> + <owner>olivierli@chromium.org</owner> + <owner>catan-team@chromium.org</owner> + <summary> + Record the usage scenario that applies to the last 2 mins of Chrome use. + Recorded every two minutes on a timer. For more information on user + scenarios see go/chrome_power_use_per_scenario. + + Values in the enum match the variants present in the + "UsageScenario" variants. + </summary> +</histogram> + +<histogram name="PerformanceMonitor.UsageScenario.ShortInterval" + enum="PerformanceMonitor.UsageScenario.ShortInterval" + expires_after="2023-06-18"> + <owner>olivierli@chromium.org</owner> + <owner>catan-team@chromium.org</owner> + <summary> + Record the usage scenario that applies to the last 10 seconds of Chrome use. + Recorded every two minutes on a timer. For more information on user + scenarios see go/chrome_power_use_per_scenario. + + Values in the enum match the variants present in the + "UsageScenario10Sec" variants. + </summary> +</histogram> + <histogram name="Power.AdaptiveChargingBatteryPercentageOnUnplug" units="%" expires_after="2023-03-19"> <owner>dbasehore@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/quick_answers/histograms.xml b/tools/metrics/histograms/metadata/quick_answers/histograms.xml index 6c447a9..ee5f2bf 100644 --- a/tools/metrics/histograms/metadata/quick_answers/histograms.xml +++ b/tools/metrics/histograms/metadata/quick_answers/histograms.xml
@@ -116,6 +116,16 @@ </token> </histogram> +<histogram name="QuickAnswers.DictionaryIntent.Language" enum="LanguageName" + expires_after="2023-07-01"> + <owner>updowndota@chromium.org</owner> + <owner>croissant-eng@chromium.org</owner> + <summary> + For Quick answers fetch, records the query text language of dictionary + intent generated on device. ChromeOS only. + </summary> +</histogram> + <histogram name="QuickAnswers.DictionaryIntent.Source" enum="QuickAnswersDictionaryIntentSource" expires_after="2023-07-01"> <owner>updowndota@chromium.org</owner>
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json index 54ca75b7..0486f75c 100644 --- a/tools/perf/core/perfetto_binary_roller/binary_deps.json +++ b/tools/perf/core/perfetto_binary_roller/binary_deps.json
@@ -5,8 +5,8 @@ "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux_arm64/49b4b5dcbc312d8d2c3751cf29238b8efeb4e494/trace_processor_shell" }, "win": { - "hash": "224082e63ee0725d55c44bf7b585ebad9dd44997", - "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/c2235982559f7008e5384d5145e1febe571573cd/trace_processor_shell.exe" + "hash": "f438948d0367b33e3ce4a580cb00f9b1540ae548", + "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/361efbf9aab595e4dfa79ec48f242d9e722393c9/trace_processor_shell.exe" }, "linux_arm": { "hash": "58893933be305d3bfe0a72ebebcacde2ac3ca893", @@ -21,8 +21,8 @@ "full_remote_path": "perfetto-luci-artifacts/v25.0/mac-arm64/trace_processor_shell" }, "linux": { - "hash": "ac75be567c3531560e0b72f4e15081bf587e172d", - "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/c2235982559f7008e5384d5145e1febe571573cd/trace_processor_shell" + "hash": "c21d40b89142cf06456f64df5b23a7d00a274961", + "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/361efbf9aab595e4dfa79ec48f242d9e722393c9/trace_processor_shell" } }, "power_profile.sql": {
diff --git a/ui/aura/env.cc b/ui/aura/env.cc index 219ffda5..0f7b3b3 100644 --- a/ui/aura/env.cc +++ b/ui/aura/env.cc
@@ -14,12 +14,14 @@ #include "base/observer_list_types.h" #include "build/build_config.h" #include "ui/aura/client/aura_constants.h" +#include "ui/aura/client/screen_position_client.h" #include "ui/aura/env_input_state_controller.h" #include "ui/aura/env_observer.h" #include "ui/aura/input_state_lookup.h" #include "ui/aura/window.h" #include "ui/aura/window_event_dispatcher_observer.h" #include "ui/aura/window_occlusion_tracker.h" +#include "ui/display/screen.h" #include "ui/events/event_observer.h" #include "ui/events/event_target_iterator.h" #include "ui/events/gestures/gesture_recognizer_impl.h" @@ -152,6 +154,33 @@ gesture_recognizer_ = std::move(gesture_recognizer); } +gfx::Point Env::GetLastPointerPoint(ui::mojom::DragEventSource event_source, + Window* window, + absl::optional<gfx::Point> fallback) { + if (event_source == ui::mojom::DragEventSource::kTouch && is_touch_down()) { + DCHECK(window); + DCHECK(window->GetRootWindow()); + gfx::PointF touch_point_f; + bool got_touch_point = gesture_recognizer()->GetLastTouchPointForTarget( + window, &touch_point_f); + if (got_touch_point) { + Window* root = window->GetRootWindow(); + DCHECK(root); + DCHECK(root->GetRootWindow()); + DCHECK(aura::client::GetScreenPositionClient(root->GetRootWindow())); + client::GetScreenPositionClient(root->GetRootWindow()) + ->ConvertPointToScreen(root, &touch_point_f); + return gfx::ToFlooredPoint(touch_point_f); + } + // Fallback when touch state is lost. See http://crbug.com/1162541. + if (fallback) + return *fallback; + } + + // TODO(https://crbug.com/1338746): Use last_mouse_location_. + return display::Screen::GetScreen()->GetCursorScreenPoint(); +} + WindowOcclusionTracker* Env::GetWindowOcclusionTracker() { if (!window_occlusion_tracker_) { // Use base::WrapUnique + new because of the constructor is private.
diff --git a/ui/aura/env.h b/ui/aura/env.h index fc2c99d..0b947f2 100644 --- a/ui/aura/env.h +++ b/ui/aura/env.h
@@ -14,6 +14,7 @@ #include "build/build_config.h" #include "mojo/public/cpp/system/buffer.h" #include "ui/aura/aura_export.h" +#include "ui/base/dragdrop/mojom/drag_drop_types.mojom.h" #include "ui/events/event_target.h" #include "ui/events/types/event_type.h" #include "ui/gfx/geometry/point.h" @@ -113,10 +114,15 @@ ui::GestureRecognizer* gesture_recognizer() { return gesture_recognizer_.get(); } - void SetGestureRecognizer( std::unique_ptr<ui::GestureRecognizer> gesture_recognizer); + // The `fallback` parameter allows callers of this API to specify a + // value to be returned in the case of a missing touch state. + gfx::Point GetLastPointerPoint(ui::mojom::DragEventSource event_source, + aura::Window* window, + absl::optional<gfx::Point> fallback); + // Get WindowOcclusionTracker instance. Create one if not yet created. WindowOcclusionTracker* GetWindowOcclusionTracker();
diff --git a/ui/display/BUILD.gn b/ui/display/BUILD.gn index 18cce0c..c18d939 100644 --- a/ui/display/BUILD.gn +++ b/ui/display/BUILD.gn
@@ -184,6 +184,8 @@ sources += [ "mac/test/test_screen_mac.h", "mac/test/test_screen_mac.mm", + "mac/test/virtual_display_mac_util.h", + "mac/test/virtual_display_mac_util.mm", ] } @@ -279,3 +281,19 @@ ] } } + +# This target is added as a dependency of browser interactive_ui_tests. It must +# be source_set, otherwise the linker will drop the tests as dead code. +source_set("display_interactive_ui_tests") { + testonly = true + if (is_mac) { + sources = [ "mac/test/virtual_display_mac_util_interactive_uitest.mm" ] + + deps = [ + ":display", + ":test_support", + "//base/test:test_support", + "//testing/gtest", + ] + } +}
diff --git a/ui/display/mac/test/virtual_display_mac_util.h b/ui/display/mac/test/virtual_display_mac_util.h new file mode 100644 index 0000000..846d8f3 --- /dev/null +++ b/ui/display/mac/test/virtual_display_mac_util.h
@@ -0,0 +1,113 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_DISPLAY_MAC_TEST_VIRTUAL_DISPLAY_MAC_UTIL_H_ +#define UI_DISPLAY_MAC_TEST_VIRTUAL_DISPLAY_MAC_UTIL_H_ + +#import <IOKit/pwr_mgt/IOPMLib.h> +#include <stdint.h> + +#include "base/containers/flat_set.h" +#include "base/run_loop.h" +#include "ui/display/display_observer.h" +#include "ui/display/types/display_constants.h" + +namespace display { +class Display; + +namespace test { +struct DisplayParams; + +// This interface creates system-level virtual displays to support the automated +// integration testing of display information and window placement APIs in +// multi-screen device environments. It updates the displays that the normal mac +// screen impl sees, but not `TestScreenMac`. +class VirtualDisplayMacUtil : public display::DisplayObserver { + public: + VirtualDisplayMacUtil(); + ~VirtualDisplayMacUtil() override; + + VirtualDisplayMacUtil(const VirtualDisplayMacUtil&) = delete; + VirtualDisplayMacUtil& operator=(const VirtualDisplayMacUtil&) = delete; + + void WarmUp(); + + // `display_id` is only used to label the virtual display. This function + // returns the generated display::Display id, which can be used with the + // Screen instance or passed to `RemoveDisplay`. + int64_t AddDisplay(int64_t display_id, const DisplayParams& display_params); + void RemoveDisplay(int64_t display_id); + + // Check whether the related CoreGraphics APIs are available in the current + // system version. + static bool IsAPIAvailable(); + + // Preset display configuration parameters. + static const DisplayParams k6016x3384; + static const DisplayParams k5120x2880; + static const DisplayParams k4096x2304; + static const DisplayParams k3840x2400; + static const DisplayParams k3840x2160; + static const DisplayParams k3840x1600; + static const DisplayParams k3840x1080; + static const DisplayParams k3072x1920; + static const DisplayParams k2880x1800; + static const DisplayParams k2560x1600; + static const DisplayParams k2560x1440; + static const DisplayParams k2304x1440; + static const DisplayParams k2048x1536; + static const DisplayParams k2048x1152; + static const DisplayParams k1920x1200; + static const DisplayParams k1600x1200; + static const DisplayParams k1920x1080; + static const DisplayParams k1680x1050; + static const DisplayParams k1440x900; + static const DisplayParams k1400x1050; + static const DisplayParams k1366x768; + static const DisplayParams k1280x1024; + static const DisplayParams k1280x1800; + + private: + class DisplaySleepBlocker { + public: + DisplaySleepBlocker(); + ~DisplaySleepBlocker(); + + DisplaySleepBlocker(const DisplaySleepBlocker&) = delete; + DisplaySleepBlocker& operator=(const DisplaySleepBlocker&) = delete; + + private: + // Track the AssertionID argument to IOPMAssertionCreateWithProperties and + // IOPMAssertionRelease. + IOPMAssertionID assertion_id_ = kIOPMNullAssertionID; + }; + + // display::DisplayObserver: + void OnDisplayMetricsChanged(const display::Display& display, + uint32_t changed_metrics) override; + void OnDisplayAdded(const display::Display& new_display) override; + void OnDisplayRemoved(const display::Display& old_display) override; + + void OnDisplayAddedOrRemoved(int64_t id); + + // Remove all virtual displays before leaving the scope of + // VirtualDisplayMacUtil. + void RemoveAllDisplays(); + + // Wait for the display with the given `id` to be added. + // Return immediately if the display is already available. + void WaitForDisplay(int64_t id, bool added); + + bool NeedWarmUp(); + + base::flat_set<int64_t> waiting_for_ids_; + std::unique_ptr<base::RunLoop> run_loop_; + + DisplaySleepBlocker display_sleep_blocker_; +}; + +} // namespace test +} // namespace display + +#endif // UI_DISPLAY_MAC_TEST_VIRTUAL_DISPLAY_MAC_UTIL_H_
diff --git a/ui/display/mac/test/virtual_display_mac_util.mm b/ui/display/mac/test/virtual_display_mac_util.mm new file mode 100644 index 0000000..99147bd4 --- /dev/null +++ b/ui/display/mac/test/virtual_display_mac_util.mm
@@ -0,0 +1,602 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/display/mac/test/virtual_display_mac_util.h" + +#include <CoreGraphics/CoreGraphics.h> +#import <Foundation/Foundation.h> + +#include <map> + +#include "base/auto_reset.h" +#include "base/check.h" +#include "base/check_op.h" +#include "base/mac/scoped_nsobject.h" +#include "build/build_config.h" +#include "ui/display/display.h" +#include "ui/display/screen.h" +#include "ui/gfx/geometry/size.h" + +// These interfaces were generated from CoreGraphics binaries. +API_AVAILABLE(macos(10.14)) +@interface CGVirtualDisplay : NSObject { + unsigned int _vendorID; + unsigned int _productID; + unsigned int _serialNum; + NSString* _name; + struct CGSize _sizeInMillimeters; + unsigned int _maxPixelsWide; + unsigned int _maxPixelsHigh; + struct CGPoint _redPrimary; + struct CGPoint _greenPrimary; + struct CGPoint _bluePrimary; + struct CGPoint _whitePoint; + id _queue; + id _terminationHandler; + unsigned int _displayID; + unsigned int _hiDPI; + NSArray* _modes; +} + +@property(readonly, nonatomic) + unsigned int vendorID; // @synthesize vendorID=_vendorID; +@property(readonly, nonatomic) + unsigned int productID; // @synthesize productID=_productID; +@property(readonly, nonatomic) + unsigned int serialNum; // @synthesize serialNum=_serialNum; +@property(readonly, nonatomic) NSString* name; // @synthesize name=_name; +@property(readonly, nonatomic) struct CGSize + sizeInMillimeters; // @synthesize sizeInMillimeters=_sizeInMillimeters; +@property(readonly, nonatomic) + unsigned int maxPixelsWide; // @synthesize maxPixelsWide=_maxPixelsWide; +@property(readonly, nonatomic) + unsigned int maxPixelsHigh; // @synthesize maxPixelsHigh=_maxPixelsHigh; +@property(readonly, nonatomic) + struct CGPoint redPrimary; // @synthesize redPrimary=_redPrimary; +@property(readonly, nonatomic) + struct CGPoint greenPrimary; // @synthesize greenPrimary=_greenPrimary; +@property(readonly, nonatomic) + struct CGPoint bluePrimary; // @synthesize bluePrimary=_bluePrimary; +@property(readonly, nonatomic) + struct CGPoint whitePoint; // @synthesize whitePoint=_whitePoint; +@property(readonly, nonatomic) id queue; // @synthesize queue=_queue; +@property(readonly, nonatomic) id + terminationHandler; // @synthesize terminationHandler=_terminationHandler; +@property(readonly, nonatomic) + unsigned int displayID; // @synthesize displayID=_displayID; +@property(readonly, nonatomic) unsigned int hiDPI; // @synthesize hiDPI=_hiDPI; +@property(readonly, nonatomic) NSArray* modes; // @synthesize modes=_modes; +- (BOOL)applySettings:(id)arg1; +- (void)dealloc; +- (id)initWithDescriptor:(id)arg1; + +@end + +// These interfaces were generated from CoreGraphics binaries. +API_AVAILABLE(macos(10.14)) +@interface CGVirtualDisplayDescriptor : NSObject { + unsigned int _vendorID; + unsigned int _productID; + unsigned int _serialNum; + NSString* _name; + struct CGSize _sizeInMillimeters; + unsigned int _maxPixelsWide; + unsigned int _maxPixelsHigh; + struct CGPoint _redPrimary; + struct CGPoint _greenPrimary; + struct CGPoint _bluePrimary; + struct CGPoint _whitePoint; + id _queue; + id _terminationHandler; +} + +@property(nonatomic) unsigned int vendorID; // @synthesize vendorID=_vendorID; +@property(nonatomic) + unsigned int productID; // @synthesize productID=_productID; +@property(nonatomic) + unsigned int serialNum; // @synthesize serialNum=_serialNum; +@property(strong, nonatomic) NSString* name; // @synthesize name=_name; +@property(nonatomic) struct CGSize + sizeInMillimeters; // @synthesize sizeInMillimeters=_sizeInMillimeters; +@property(nonatomic) + unsigned int maxPixelsWide; // @synthesize maxPixelsWide=_maxPixelsWide; +@property(nonatomic) + unsigned int maxPixelsHigh; // @synthesize maxPixelsHigh=_maxPixelsHigh; +@property(nonatomic) + struct CGPoint redPrimary; // @synthesize redPrimary=_redPrimary; +@property(nonatomic) + struct CGPoint greenPrimary; // @synthesize greenPrimary=_greenPrimary; +@property(nonatomic) + struct CGPoint bluePrimary; // @synthesize bluePrimary=_bluePrimary; +@property(nonatomic) + struct CGPoint whitePoint; // @synthesize whitePoint=_whitePoint; +@property(retain, nonatomic) id queue; // @synthesize queue=_queue; +@property(copy, nonatomic) id + terminationHandler; // @synthesize terminationHandler=_terminationHandler; +- (void)dealloc; +- (id)init; +- (id)dispatchQueue; +- (void)setDispatchQueue:(id)arg1; + +@end + +// These interfaces were generated from CoreGraphics binaries. +API_AVAILABLE(macos(10.14)) +@interface CGVirtualDisplayMode : NSObject { + unsigned int _width; + unsigned int _height; + double _refreshRate; +} + +@property(readonly, nonatomic) unsigned int width; // @synthesize width=_width; +@property(readonly, nonatomic) + unsigned int height; // @synthesize height=_height; +@property(readonly, nonatomic) + double refreshRate; // @synthesize refreshRate=_refreshRate; +- (id)initWithWidth:(unsigned int)arg1 + height:(unsigned int)arg2 + refreshRate:(double)arg3; + +@end + +// These interfaces were generated from CoreGraphics binaries. +API_AVAILABLE(macos(10.14)) +@interface CGVirtualDisplaySettings : NSObject { + NSArray* _modes; + unsigned int _hiDPI; +} + +@property(strong, nonatomic) NSArray* modes; // @synthesize modes=_modes; +@property(nonatomic) unsigned int hiDPI; // @synthesize hiDPI=_hiDPI; +- (void)dealloc; +- (id)init; + +@end + +namespace { + +static bool g_during_warm_up = false; + +base::flat_set<int64_t> g_unexpected_display_ids; + +// A global singleton that tracks the current set of mocked displays. +std::map<int64_t, base::scoped_nsobject<CGVirtualDisplay>> g_display_map + API_AVAILABLE(macos(10.14)); + +// A helper function for creating virtual display and return CGVirtualDisplay +// object. +base::scoped_nsobject<CGVirtualDisplay> CreateVirtualDisplay(int width, + int height, + int ppi, + BOOL hiDPI, + NSString* name) + API_AVAILABLE(macos(10.14)) { + base::scoped_nsobject<CGVirtualDisplaySettings> settings( + [[CGVirtualDisplaySettings alloc] init]); + [settings setHiDPI:hiDPI]; + + base::scoped_nsobject<CGVirtualDisplayDescriptor> descriptor( + [[CGVirtualDisplayDescriptor alloc] init]); + [descriptor + setQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)]; + [descriptor setName:name]; + + // See System Preferences > Displays > Color > Open Profile > Apple display + // native information + [descriptor setWhitePoint:CGPointMake(0.3125, 0.3291)]; + [descriptor setBluePrimary:CGPointMake(0.1494, 0.0557)]; + [descriptor setGreenPrimary:CGPointMake(0.2559, 0.6983)]; + [descriptor setRedPrimary:CGPointMake(0.6797, 0.3203)]; + [descriptor setMaxPixelsHigh:height]; + [descriptor setMaxPixelsWide:width]; + [descriptor + setSizeInMillimeters:CGSizeMake(25.4 * width / ppi, 25.4 * height / ppi)]; + [descriptor setSerialNum:0]; + [descriptor setProductID:0]; + [descriptor setVendorID:0]; + + base::scoped_nsobject<CGVirtualDisplay> display( + [[CGVirtualDisplay alloc] initWithDescriptor:descriptor]); + + if ([settings hiDPI]) { + width /= 2; + height /= 2; + } + base::scoped_nsobject<CGVirtualDisplayMode> mode([[CGVirtualDisplayMode alloc] + initWithWidth:width + height:height + refreshRate:60]); + [settings setModes:@[ mode ]]; + + if (![display applySettings:settings]) + return base::scoped_nsobject<CGVirtualDisplay>(); + + return display; +} + +void DealWithUnexpectedDisplay(int64_t id) { + if (g_unexpected_display_ids.count(id)) { + g_unexpected_display_ids.erase(id); + } else { + g_unexpected_display_ids.insert(id); + } +} + +// This method detects whether the local machine is running headless. +// Typically returns true when the session is curtained or if there are no +// physical monitors attached. In those two scenarios, the online display will +// be marked as virtual. +bool IsRunningHeadless() { + // Most machines will have < 4 displays but a larger upper bound won't hurt. + constexpr UInt32 kMaxDisplaysToQuery = 32; + // 0x76697274 is a 4CC value for 'virt' which indicates the display is + // virtual. + constexpr CGDirectDisplayID kVirtualDisplayID = 0x76697274; + + CGDirectDisplayID online_displays[kMaxDisplaysToQuery]; + UInt32 online_display_count = 0; + CGError return_code = CGGetOnlineDisplayList( + kMaxDisplaysToQuery, online_displays, &online_display_count); + if (return_code != kCGErrorSuccess) { + LOG(ERROR) << __func__ + << " - CGGetOnlineDisplayList() failed: " << return_code << "."; + // If this fails, assume machine is headless to err on the side of caution. + return true; + } + + bool is_running_headless = true; + for (UInt32 i = 0; i < online_display_count; i++) { + if (CGDisplayModelNumber(online_displays[i]) != kVirtualDisplayID) { + // At least one monitor is attached so the machine is not headless. + is_running_headless = false; + break; + } + } + + // TODO(crbug.com/1126278): Please remove this log or replace it with + // [D]CHECK() ASAP when the TEST is stable. + LOG(INFO) << __func__ << " - Is running headless: " << is_running_headless + << ". Online display count: " << online_display_count << "."; + + return is_running_headless; +} + +} // namespace + +namespace display::test { + +struct DisplayParams { + DisplayParams(int width, + int height, + int ppi, + bool hiDPI, + std::string description) + : width(width), + height(height), + ppi(ppi), + hiDPI(hiDPI), + description([NSString + stringWithCString:description.c_str() + encoding:[NSString defaultCStringEncoding]]) {} + + bool IsValid() const { + return width > 0 && height > 0 && ppi > 0 && [description length] > 0; + } + + int width; + int height; + int ppi; + BOOL hiDPI; + base::scoped_nsobject<NSString> description; +}; + +VirtualDisplayMacUtil::VirtualDisplayMacUtil() { + display::Screen::GetScreen()->AddObserver(this); +} + +VirtualDisplayMacUtil::~VirtualDisplayMacUtil() { + RemoveAllDisplays(); + display::Screen::GetScreen()->RemoveObserver(this); +} + +void VirtualDisplayMacUtil::WarmUp() { + constexpr int kMaxRetryCount = 4; + + // TODO(crbug.com/1126278): Please remove this log or replace it with + // [D]CHECK() ASAP when the TEST is stable. + LOG(INFO) << "VirtualDisplayMacUtil::" << __func__ << " - start."; + + base::AutoReset<bool> auto_reset(&g_during_warm_up, true); + + int retry_count = 0; + do { + for (int64_t display_id : {1, 2}) { + AddDisplay(display_id, k1920x1080); + } + RemoveAllDisplays(); + + retry_count++; + } while (!g_unexpected_display_ids.empty() && retry_count <= kMaxRetryCount); + + // TODO(crbug.com/1126278): Please remove this log or replace it with + // [D]CHECK() ASAP when the TEST is stable. + LOG(INFO) << "VirtualDisplayMacUtil::" << __func__ << " - end."; +} + +int64_t VirtualDisplayMacUtil::AddDisplay(int64_t display_id, + const DisplayParams& display_params) { + if (@available(macos 10.14, *)) { + bool need_warm_up = NeedWarmUp(); + + DCHECK(display_params.IsValid()); + + NSString* display_name = + [NSString stringWithFormat:@"Virtual Display #%lld", display_id]; + base::scoped_nsobject<CGVirtualDisplay> display = CreateVirtualDisplay( + display_params.width, display_params.height, display_params.ppi, + display_params.hiDPI, display_name); + DCHECK(display.get()); + + // TODO(crbug.com/1126278): Please remove this log or replace it with + // [D]CHECK() ASAP when the TEST is stable. + LOG(INFO) << "VirtualDisplayMacUtil::" << __func__ + << " - display id: " << display_id + << ". CreateVirtualDisplay success."; + + int64_t id = [display displayID]; + DCHECK_NE(id, 0u); + + WaitForDisplay(id, /*added=*/true); + + // TODO(crbug.com/1126278): Please remove this log or replace it with + // [D]CHECK() ASAP when the TEST is stable. + LOG(INFO) << "VirtualDisplayMacUtil::" << __func__ + << " - display id: " << display_id << "(" << id + << "). WaitForDisplay success."; + + DCHECK(!g_display_map[id]); + g_display_map[id] = display; + + if (need_warm_up) { + int64_t tmp_id = AddDisplay(0, display_params); + RemoveDisplay(tmp_id); + } + + return id; + } + return display::kInvalidDisplayId; +} + +void VirtualDisplayMacUtil::RemoveDisplay(int64_t display_id) { + if (@available(macos 10.14, *)) { + auto it = g_display_map.find(display_id); + DCHECK(it != g_display_map.end()); + + g_display_map.erase(it); + + // TODO(crbug.com/1126278): Please remove this log or replace it with + // [D]CHECK() ASAP when the TEST is stable. + LOG(INFO) << "VirtualDisplayMacUtil::" << __func__ + << " - display id: " << display_id << ". Erase success."; + + WaitForDisplay(display_id, /*added=*/false); + + // TODO(crbug.com/1126278): Please remove this log or replace it with + // [D]CHECK() ASAP when the TEST is stable. + LOG(INFO) << "VirtualDisplayMacUtil::" << __func__ + << " - display id: " << display_id << ". WaitForDisplay success."; + } +} + +// static +bool VirtualDisplayMacUtil::IsAPIAvailable() { + bool is_arch_cpu_arm_family = false; +#if defined(ARCH_CPU_ARM_FAMILY) + is_arch_cpu_arm_family = true; +#endif // defined(ARCH_CPU_ARM_FAMILY) + if (is_arch_cpu_arm_family) { + return false; + } + + bool is_api_available = false; + if (@available(macos 12.0, *)) { + is_api_available = true; + } + if (!is_api_available) { + return false; + } + + // Only support non-headless bots now. Some odd behavior happened when + // enable on headless bots. See + // https://ci.chromium.org/ui/p/chromium/builders/try/mac-rel/1058024/test-results + // for details. + // TODO(crbug.com/1126278): Remove this restriction when support headless + // environment. + if (IsRunningHeadless()) { + return false; + } + + return true; +} + +// Predefined display configurations from +// https://en.wikipedia.org/wiki/Graphics_display_resolution and +// https://www.theverge.com/tldr/2016/3/21/11278192/apple-iphone-ipad-screen-sizes-pixels-density-so-many-choices. +const DisplayParams VirtualDisplayMacUtil::k6016x3384 = + DisplayParams(6016, 3384, 218, true, "Apple Pro Display XDR"); +const DisplayParams VirtualDisplayMacUtil::k5120x2880 = + DisplayParams(5120, 2880, 218, true, "27-inch iMac with Retina 5K display"); +const DisplayParams VirtualDisplayMacUtil::k4096x2304 = + DisplayParams(4096, + 2304, + 219, + true, + "21.5-inch iMac with Retina 4K display"); +const DisplayParams VirtualDisplayMacUtil::k3840x2400 = + DisplayParams(3840, 2400, 200, true, "WQUXGA"); +const DisplayParams VirtualDisplayMacUtil::k3840x2160 = + DisplayParams(3840, 2160, 200, true, "UHD"); +const DisplayParams VirtualDisplayMacUtil::k3840x1600 = + DisplayParams(3840, 1600, 200, true, "WQHD+, UW-QHD+"); +const DisplayParams VirtualDisplayMacUtil::k3840x1080 = + DisplayParams(3840, 1080, 200, true, "DFHD"); +const DisplayParams VirtualDisplayMacUtil::k3072x1920 = + DisplayParams(3072, + 1920, + 226, + true, + "16-inch MacBook Pro with Retina display"); +const DisplayParams VirtualDisplayMacUtil::k2880x1800 = + DisplayParams(2880, + 1800, + 220, + true, + "15.4-inch MacBook Pro with Retina display"); +const DisplayParams VirtualDisplayMacUtil::k2560x1600 = + DisplayParams(2560, + 1600, + 227, + true, + "WQXGA, 13.3-inch MacBook Pro with Retina display"); +const DisplayParams VirtualDisplayMacUtil::k2560x1440 = + DisplayParams(2560, 1440, 109, false, "27-inch Apple Thunderbolt display"); +const DisplayParams VirtualDisplayMacUtil::k2304x1440 = + DisplayParams(2304, 1440, 226, true, "12-inch MacBook with Retina display"); +const DisplayParams VirtualDisplayMacUtil::k2048x1536 = + DisplayParams(2048, 1536, 150, false, "QXGA"); +const DisplayParams VirtualDisplayMacUtil::k2048x1152 = + DisplayParams(2048, 1152, 150, false, "QWXGA"); +const DisplayParams VirtualDisplayMacUtil::k1920x1200 = + DisplayParams(1920, 1200, 150, false, "WUXGA"); +const DisplayParams VirtualDisplayMacUtil::k1600x1200 = + DisplayParams(1600, 1200, 125, false, "UXGA"); +const DisplayParams VirtualDisplayMacUtil::k1920x1080 = + DisplayParams(1920, 1080, 102, false, "HD, 21.5-inch iMac"); +const DisplayParams VirtualDisplayMacUtil::k1680x1050 = + DisplayParams(1680, + 1050, + 99, + false, + "WSXGA+, Apple Cinema Display (20-inch), 20-inch iMac"); +const DisplayParams VirtualDisplayMacUtil::k1440x900 = + DisplayParams(1440, 900, 127, false, "WXGA+, 13.3-inch MacBook Air"); +const DisplayParams VirtualDisplayMacUtil::k1400x1050 = + DisplayParams(1400, 1050, 125, false, "SXGA+"); +const DisplayParams VirtualDisplayMacUtil::k1366x768 = + DisplayParams(1366, 768, 135, false, "11.6-inch MacBook Air"); +const DisplayParams VirtualDisplayMacUtil::k1280x1024 = + DisplayParams(1280, 1024, 100, false, "SXGA"); +const DisplayParams VirtualDisplayMacUtil::k1280x1800 = + DisplayParams(1280, 800, 113, false, "13.3-inch MacBook Pro"); + +VirtualDisplayMacUtil::DisplaySleepBlocker::DisplaySleepBlocker() { + IOReturn result = IOPMAssertionCreateWithName( + kIOPMAssertionTypeNoDisplaySleep, kIOPMAssertionLevelOn, + CFSTR("DisplaySleepBlocker"), &assertion_id_); + DCHECK_EQ(result, kIOReturnSuccess); +} + +VirtualDisplayMacUtil::DisplaySleepBlocker::~DisplaySleepBlocker() { + IOReturn result = IOPMAssertionRelease(assertion_id_); + DCHECK_EQ(result, kIOReturnSuccess); +} + +void VirtualDisplayMacUtil::OnDisplayMetricsChanged( + const display::Display& display, + uint32_t changed_metrics) { + LOG(INFO) << "VirtualDisplayMacUtil::" << __func__ + << " - display id: " << display.id() + << ", changed_metrics: " << changed_metrics << "."; +} + +void VirtualDisplayMacUtil::OnDisplayAdded( + const display::Display& new_display) { + // TODO(crbug.com/1126278): Please remove this log or replace it with + // [D]CHECK() ASAP when the TEST is stable. + LOG(INFO) << "VirtualDisplayMacUtil::" << __func__ + << " - display id: " << new_display.id() << "."; + + OnDisplayAddedOrRemoved(new_display.id()); +} + +void VirtualDisplayMacUtil::OnDisplayRemoved( + const display::Display& old_display) { + // TODO(crbug.com/1126278): Please remove this log or replace it with + // [D]CHECK() ASAP when the TEST is stable. + LOG(INFO) << "VirtualDisplayMacUtil::" << __func__ + << " - display id: " << old_display.id() << "."; + + OnDisplayAddedOrRemoved(old_display.id()); +} + +void VirtualDisplayMacUtil::OnDisplayAddedOrRemoved(int64_t id) { + if (!waiting_for_ids_.count(id)) { + DealWithUnexpectedDisplay(id); + return; + } + + waiting_for_ids_.erase(id); + if (!waiting_for_ids_.empty()) { + return; + } + + run_loop_->Quit(); +} + +void VirtualDisplayMacUtil::RemoveAllDisplays() { + if (@available(macos 10.14, *)) { + int display_count = g_display_map.size(); + + // TODO(crbug.com/1126278): Please remove this log or replace it with + // [D]CHECK() ASAP when the TEST is stable. + LOG(INFO) << "VirtualDisplayMacUtil::" << __func__ + << " - display count: " << display_count << "."; + + if (!display_count) { + return; + } + + auto iter = g_display_map.begin(); + while (iter != g_display_map.end()) { + waiting_for_ids_.insert(iter->first); + iter++; + } + + g_display_map.clear(); + + run_loop_ = std::make_unique<base::RunLoop>(); + run_loop_->Run(); + } +} + +void VirtualDisplayMacUtil::WaitForDisplay(int64_t id, bool added) { + display::Display d; + if (display::Screen::GetScreen()->GetDisplayWithDisplayId(id, &d) == added) { + return; + } + + // TODO(crbug.com/1126278): Please remove this log or replace it with + // [D]CHECK() ASAP when the TEST is stable. + LOG(INFO) << "VirtualDisplayMacUtil::" << __func__ << " - display id: " << id + << "(added: " << added << "). Start waiting."; + + waiting_for_ids_.insert(id); + + run_loop_ = std::make_unique<base::RunLoop>(); + run_loop_->Run(); +} + +bool VirtualDisplayMacUtil::NeedWarmUp() { + if (g_during_warm_up) { + return false; + } + + if (@available(macos 10.14, *)) { + return g_display_map.empty(); + } + + return false; +} + +} // namespace display::test
diff --git a/ui/display/mac/test/virtual_display_mac_util_interactive_uitest.mm b/ui/display/mac/test/virtual_display_mac_util_interactive_uitest.mm new file mode 100644 index 0000000..04b24fe --- /dev/null +++ b/ui/display/mac/test/virtual_display_mac_util_interactive_uitest.mm
@@ -0,0 +1,95 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/test/task_environment.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/display/mac/test/virtual_display_mac_util.h" +#include "ui/display/screen.h" +#include "ui/display/types/display_constants.h" + +class VirtualDisplayMacUtilInteractiveUitest : public testing::Test { + protected: + VirtualDisplayMacUtilInteractiveUitest() = default; + + VirtualDisplayMacUtilInteractiveUitest( + const VirtualDisplayMacUtilInteractiveUitest&) = delete; + VirtualDisplayMacUtilInteractiveUitest& operator=( + const VirtualDisplayMacUtilInteractiveUitest&) = delete; + + void SetUp() override { + if (!display::test::VirtualDisplayMacUtil::IsAPIAvailable()) { + GTEST_SKIP() << "Skipping test for MacOS 11.0 and older or Arm Macs."; + } + + testing::Test::SetUp(); + } + + private: + display::ScopedNativeScreen screen_; + base::test::TaskEnvironment task_environment_{ + base::test::TaskEnvironment::MainThreadType::UI}; +}; + +TEST_F(VirtualDisplayMacUtilInteractiveUitest, WarmUp) { + int display_count = display::Screen::GetScreen()->GetNumDisplays(); + + display::test::VirtualDisplayMacUtil virtual_display_mac_util; + virtual_display_mac_util.WarmUp(); + + EXPECT_EQ(display::Screen::GetScreen()->GetNumDisplays(), display_count); +} + +TEST_F(VirtualDisplayMacUtilInteractiveUitest, AddDisplay) { + display::test::VirtualDisplayMacUtil virtual_display_mac_util; + virtual_display_mac_util.WarmUp(); + + int64_t id = virtual_display_mac_util.AddDisplay( + 1, display::test::VirtualDisplayMacUtil::k1920x1080); + EXPECT_NE(id, display::kInvalidDisplayId); + + display::Display d; + bool found = display::Screen::GetScreen()->GetDisplayWithDisplayId(id, &d); + EXPECT_TRUE(found); +} + +TEST_F(VirtualDisplayMacUtilInteractiveUitest, RemoveDisplay) { + display::test::VirtualDisplayMacUtil virtual_display_mac_util; + virtual_display_mac_util.WarmUp(); + + int64_t id = virtual_display_mac_util.AddDisplay( + 1, display::test::VirtualDisplayMacUtil::k1920x1080); + int display_count = display::Screen::GetScreen()->GetNumDisplays(); + EXPECT_GT(display_count, 1); + + virtual_display_mac_util.RemoveDisplay(id); + EXPECT_EQ(display::Screen::GetScreen()->GetNumDisplays(), display_count - 1); + + display::Display d; + bool found = display::Screen::GetScreen()->GetDisplayWithDisplayId(id, &d); + EXPECT_FALSE(found); +} + +TEST_F(VirtualDisplayMacUtilInteractiveUitest, IsAPIAvailable) { + EXPECT_TRUE(display::test::VirtualDisplayMacUtil::IsAPIAvailable()); +} + +TEST_F(VirtualDisplayMacUtilInteractiveUitest, HotPlug) { + int display_count = display::Screen::GetScreen()->GetNumDisplays(); + + std::unique_ptr<display::test::VirtualDisplayMacUtil> + virtual_display_mac_util = + std::make_unique<display::test::VirtualDisplayMacUtil>(); + virtual_display_mac_util->WarmUp(); + + virtual_display_mac_util->AddDisplay( + 1, display::test::VirtualDisplayMacUtil::k1920x1080); + EXPECT_EQ(display::Screen::GetScreen()->GetNumDisplays(), display_count + 1); + + virtual_display_mac_util->AddDisplay( + 2, display::test::VirtualDisplayMacUtil::k1920x1080); + EXPECT_EQ(display::Screen::GetScreen()->GetNumDisplays(), display_count + 2); + + virtual_display_mac_util.reset(); + EXPECT_EQ(display::Screen::GetScreen()->GetNumDisplays(), display_count); +}
diff --git a/ui/display/manager/configure_displays_task.cc b/ui/display/manager/configure_displays_task.cc index 37a2dbec..98653ce 100644 --- a/ui/display/manager/configure_displays_task.cc +++ b/ui/display/manager/configure_displays_task.cc
@@ -231,9 +231,11 @@ ConfigureDisplaysTask::ConfigureDisplaysTask( NativeDisplayDelegate* delegate, const std::vector<DisplayConfigureRequest>& requests, - ResponseCallback callback) + ResponseCallback callback, + ConfigurationType configuration_type) : delegate_(delegate), requests_(requests), + configuration_type_(configuration_type), callback_(std::move(callback)), task_status_(SUCCESS) { delegate_->AddObserver(this); @@ -270,10 +272,13 @@ is_first_attempt ? &ConfigureDisplaysTask::OnFirstAttemptConfigured : &ConfigureDisplaysTask::OnRetryConfigured; + uint32_t modeset_flags = display::kTestModeset | display::kCommitModeset; + if (configuration_type_ == kConfigurationTypeSeamless) + modeset_flags |= display::kSeamlessModeset; delegate_->Configure( config_requests, base::BindOnce(on_configured, weak_ptr_factory_.GetWeakPtr()), - display::kTestModeset | display::kCommitModeset); + modeset_flags); } void ConfigureDisplaysTask::OnConfigurationChanged() {}
diff --git a/ui/display/manager/configure_displays_task.h b/ui/display/manager/configure_displays_task.h index 50ebdb9..77dd865 100644 --- a/ui/display/manager/configure_displays_task.h +++ b/ui/display/manager/configure_displays_task.h
@@ -14,6 +14,7 @@ #include "base/containers/queue.h" #include "base/memory/weak_ptr.h" #include "ui/display/manager/display_manager_export.h" +#include "ui/display/types/display_constants.h" #include "ui/display/types/native_display_observer.h" #include "ui/gfx/geometry/point.h" @@ -69,9 +70,11 @@ using ResponseCallback = base::OnceCallback<void(Status)>; - ConfigureDisplaysTask(NativeDisplayDelegate* delegate, - const std::vector<DisplayConfigureRequest>& requests, - ResponseCallback callback); + ConfigureDisplaysTask( + NativeDisplayDelegate* delegate, + const std::vector<DisplayConfigureRequest>& requests, + ResponseCallback callback, + ConfigurationType configuration_type = kConfigurationTypeFull); ConfigureDisplaysTask(const ConfigureDisplaysTask&) = delete; ConfigureDisplaysTask& operator=(const ConfigureDisplaysTask&) = delete; @@ -130,6 +133,10 @@ // Holds the next configuration request to attempt modeset. std::vector<DisplayConfigureRequest> requests_; + // Whether this request should be seamless or not (i.e. should a full modeset + // be permitted or not). + const ConfigurationType configuration_type_; + // A queue of display requests grouped by their // |requests_[index]->display->base_connector_id()|. These request groups are // used to downgrade displays' modes stored in |requests_| when the original
diff --git a/ui/display/manager/display_configurator.cc b/ui/display/manager/display_configurator.cc index af9f38e..76e8df0 100644 --- a/ui/display/manager/display_configurator.cc +++ b/ui/display/manager/display_configurator.cc
@@ -765,7 +765,7 @@ native_display_delegate_.get(), layout_manager_.get(), requested_display_state_, GetRequestedPowerState(), kSetDisplayPowerForceProbe, kRefreshRateThrottleDisabled, - /*force_configure=*/true, + /*force_configure=*/true, kConfigurationTypeFull, base::BindOnce(&DisplayConfigurator::OnConfigured, weak_ptr_factory_.GetWeakPtr())); configuration_task_->Run(); @@ -1026,13 +1026,18 @@ CallAndClearQueuedCallbacks(true); return; } + ConfigurationType configuration_type = kConfigurationTypeFull; + if (!HasPendingFullConfiguration()) { + DCHECK(HasPendingSeamlessConfiguration()); + configuration_type = kConfigurationTypeSeamless; + } configuration_task_ = std::make_unique<UpdateDisplayConfigurationTask>( native_display_delegate_.get(), layout_manager_.get(), requested_display_state_, pending_power_state_, pending_power_flags_, pending_refresh_rate_throttle_state_.value_or( kRefreshRateThrottleDisabled), - force_configure_, + force_configure_, configuration_type, base::BindOnce(&DisplayConfigurator::OnConfigured, weak_ptr_factory_.GetWeakPtr())); @@ -1105,6 +1110,10 @@ } bool DisplayConfigurator::ShouldRunConfigurationTask() const { + return HasPendingSeamlessConfiguration() || HasPendingFullConfiguration(); +} + +bool DisplayConfigurator::HasPendingFullConfiguration() const { if (force_configure_) return true; @@ -1117,13 +1126,14 @@ if (has_pending_power_state_) return true; - // Schedule if there is a pending request to change the refresh rate. - if (pending_refresh_rate_throttle_state_) - return true; - return false; } +bool DisplayConfigurator::HasPendingSeamlessConfiguration() const { + // Schedule if there is a pending request to change the refresh rate. + return pending_refresh_rate_throttle_state_.has_value(); +} + void DisplayConfigurator::CallAndClearInProgressCallbacks(bool success) { for (auto& callback : in_progress_configuration_callbacks_) std::move(callback).Run(success);
diff --git a/ui/display/manager/display_configurator.h b/ui/display/manager/display_configurator.h index 57a0e0c..a904aa3 100644 --- a/ui/display/manager/display_configurator.h +++ b/ui/display/manager/display_configurator.h
@@ -349,6 +349,14 @@ // otherwise. bool ShouldRunConfigurationTask() const; + // Returns true if there are pending configuration changes that should be done + // seamlessly. + bool HasPendingSeamlessConfiguration() const; + + // Returns true if there are pending configuration changes that require a full + // modeset. + bool HasPendingFullConfiguration() const; + // Helper functions which will call the callbacks in // |in_progress_configuration_callbacks_| and // |queued_configuration_callbacks_| and clear the lists after. |success| is
diff --git a/ui/display/manager/update_display_configuration_task.cc b/ui/display/manager/update_display_configuration_task.cc index 7182fd1..21cf3d1 100644 --- a/ui/display/manager/update_display_configuration_task.cc +++ b/ui/display/manager/update_display_configuration_task.cc
@@ -47,6 +47,7 @@ int power_flags, RefreshRateThrottleState refresh_rate_throttle_state, bool force_configure, + ConfigurationType configuration_type, ResponseCallback callback) : delegate_(delegate), layout_manager_(layout_manager), @@ -55,6 +56,7 @@ power_flags_(power_flags), refresh_rate_throttle_state_(refresh_rate_throttle_state), force_configure_(force_configure), + configuration_type_(configuration_type), callback_(std::move(callback)), requesting_displays_(false) { delegate_->AddObserver(this); @@ -123,10 +125,8 @@ return; } if (!requests.empty()) { - // TODO(b:238361145) Plumb seamless modeset for refresh rate throttle - // changes. configure_task_ = std::make_unique<ConfigureDisplaysTask>( - delegate_, requests, std::move(callback)); + delegate_, requests, std::move(callback), configuration_type_); configure_task_->Run(); } else { VLOG(2) << "No displays";
diff --git a/ui/display/manager/update_display_configuration_task.h b/ui/display/manager/update_display_configuration_task.h index c9d1b2ce..deff9c6c 100644 --- a/ui/display/manager/update_display_configuration_task.h +++ b/ui/display/manager/update_display_configuration_task.h
@@ -40,6 +40,7 @@ int power_flags, RefreshRateThrottleState refresh_rate_throttle_state, bool force_configure, + ConfigurationType configuration_type, ResponseCallback callback); UpdateDisplayConfigurationTask(const UpdateDisplayConfigurationTask&) = @@ -104,6 +105,10 @@ bool force_configure_; + // Whether the configuration task should be done without blanking the + // displays. + const ConfigurationType configuration_type_; + // Used to signal that the task has finished. ResponseCallback callback_;
diff --git a/ui/display/manager/update_display_configuration_task_unittest.cc b/ui/display/manager/update_display_configuration_task_unittest.cc index 474fbf1..d546295 100644 --- a/ui/display/manager/update_display_configuration_task_unittest.cc +++ b/ui/display/manager/update_display_configuration_task_unittest.cc
@@ -235,6 +235,7 @@ UpdateDisplayConfigurationTask task( &delegate_, &layout_manager_, MULTIPLE_DISPLAY_STATE_HEADLESS, chromeos::DISPLAY_POWER_ALL_ON, 0, kRefreshRateThrottleEnabled, false, + kConfigurationTypeFull, base::BindOnce(&UpdateDisplayConfigurationTaskTest::ResponseCallback, base::Unretained(this))); task.Run(); @@ -254,6 +255,7 @@ UpdateDisplayConfigurationTask task( &delegate_, &layout_manager_, MULTIPLE_DISPLAY_STATE_SINGLE, chromeos::DISPLAY_POWER_ALL_ON, 0, kRefreshRateThrottleEnabled, false, + kConfigurationTypeFull, base::BindOnce(&UpdateDisplayConfigurationTaskTest::ResponseCallback, base::Unretained(this))); task.Run(); @@ -277,6 +279,7 @@ UpdateDisplayConfigurationTask task( &delegate_, &layout_manager_, MULTIPLE_DISPLAY_STATE_MULTI_EXTENDED, chromeos::DISPLAY_POWER_ALL_ON, 0, kRefreshRateThrottleEnabled, false, + kConfigurationTypeFull, base::BindOnce(&UpdateDisplayConfigurationTaskTest::ResponseCallback, base::Unretained(this))); task.Run(); @@ -305,6 +308,7 @@ UpdateDisplayConfigurationTask task( &delegate_, &layout_manager_, MULTIPLE_DISPLAY_STATE_MULTI_MIRROR, chromeos::DISPLAY_POWER_ALL_ON, 0, kRefreshRateThrottleEnabled, false, + kConfigurationTypeFull, base::BindOnce(&UpdateDisplayConfigurationTaskTest::ResponseCallback, base::Unretained(this))); task.Run(); @@ -332,6 +336,7 @@ UpdateDisplayConfigurationTask task( &delegate_, &layout_manager_, MULTIPLE_DISPLAY_STATE_MULTI_MIRROR, chromeos::DISPLAY_POWER_ALL_ON, 0, kRefreshRateThrottleEnabled, false, + kConfigurationTypeFull, base::BindOnce(&UpdateDisplayConfigurationTaskTest::ResponseCallback, base::Unretained(this))); task.Run(); @@ -350,6 +355,7 @@ UpdateDisplayConfigurationTask task( &delegate_, &layout_manager_, MULTIPLE_DISPLAY_STATE_MULTI_EXTENDED, chromeos::DISPLAY_POWER_ALL_ON, 0, kRefreshRateThrottleEnabled, false, + kConfigurationTypeFull, base::BindOnce(&UpdateDisplayConfigurationTaskTest::ResponseCallback, base::Unretained(this))); task.Run(); @@ -404,6 +410,7 @@ UpdateDisplayConfigurationTask task( &delegate_, &layout_manager_, MULTIPLE_DISPLAY_STATE_SINGLE, chromeos::DISPLAY_POWER_ALL_ON, 0, kRefreshRateThrottleEnabled, false, + kConfigurationTypeFull, base::BindOnce(&UpdateDisplayConfigurationTaskTest::ResponseCallback, base::Unretained(this))); task.Run(); @@ -424,6 +431,7 @@ UpdateDisplayConfigurationTask task( &delegate_, &layout_manager_, MULTIPLE_DISPLAY_STATE_SINGLE, chromeos::DISPLAY_POWER_ALL_OFF, 0, kRefreshRateThrottleEnabled, false, + kConfigurationTypeFull, base::BindOnce(&UpdateDisplayConfigurationTaskTest::ResponseCallback, base::Unretained(this))); task.Run(); @@ -449,6 +457,7 @@ UpdateDisplayConfigurationTask task( &delegate_, &layout_manager_, MULTIPLE_DISPLAY_STATE_MULTI_EXTENDED, chromeos::DISPLAY_POWER_ALL_ON, 0, kRefreshRateThrottleEnabled, false, + kConfigurationTypeFull, base::BindOnce(&UpdateDisplayConfigurationTaskTest::ResponseCallback, base::Unretained(this))); task.Run(); @@ -460,6 +469,7 @@ UpdateDisplayConfigurationTask task( &delegate_, &layout_manager_, MULTIPLE_DISPLAY_STATE_MULTI_MIRROR, chromeos::DISPLAY_POWER_ALL_ON, 0, kRefreshRateThrottleEnabled, false, + kConfigurationTypeFull, base::BindOnce(&UpdateDisplayConfigurationTaskTest::ResponseCallback, base::Unretained(this))); task.Run(); @@ -483,6 +493,7 @@ UpdateDisplayConfigurationTask task( &delegate_, &layout_manager_, MULTIPLE_DISPLAY_STATE_MULTI_EXTENDED, chromeos::DISPLAY_POWER_ALL_ON, 0, kRefreshRateThrottleEnabled, false, + kConfigurationTypeFull, base::BindOnce(&UpdateDisplayConfigurationTaskTest::ResponseCallback, base::Unretained(this))); task.Run(); @@ -494,7 +505,7 @@ UpdateDisplayConfigurationTask task( &delegate_, &layout_manager_, MULTIPLE_DISPLAY_STATE_MULTI_MIRROR, chromeos::DISPLAY_POWER_ALL_ON, 0, kRefreshRateThrottleEnabled, - true /* force_configure */, + true /* force_configure */, kConfigurationTypeFull, base::BindOnce(&UpdateDisplayConfigurationTaskTest::ResponseCallback, base::Unretained(this))); task.Run();
diff --git a/ui/display/types/display_constants.h b/ui/display/types/display_constants.h index f13516c..abc5be90 100644 --- a/ui/display/types/display_constants.h +++ b/ui/display/types/display_constants.h
@@ -123,11 +123,25 @@ kRefreshRateThrottleDisabled, }; +// Whether a configuration should be seamless or full. Full configuration may +// result in visible artifacts such as blanking to achieve the specified +// configuration. Seamless configuration requests will fail if the system cannot +// achieve it without visible artifacts. +enum ConfigurationType { + kConfigurationTypeFull, + kConfigurationTypeSeamless, +}; + // A flag to allow ui/display and ozone to adjust the behavior of display // configurations. enum ModesetFlag { + // At least one of kTestModeset and kCommitModeset must be set. kTestModeset = 1 << 0, kCommitModeset = 1 << 1, + // When |kSeamlessModeset| is set, the commit (or test) will succeed only if + // the submitted configuration can be completed without visual artifacts such + // as blanking. + kSeamlessModeset = 1 << 2, }; // Defines the float values closest to repeating decimal scale factors.
diff --git a/ui/events/event_switches.cc b/ui/events/event_switches.cc index a28b059..1dbd4511 100644 --- a/ui/events/event_switches.cc +++ b/ui/events/event_switches.cc
@@ -40,6 +40,12 @@ // Disable CancelAllTouches() function for the implementation on cancel single // touches. const char kDisableCancelAllTouches[] = "disable-cancel-all-touches"; + +// Enables logic to detect microphone mute switch device state, which disables +// internal audio input when toggled. +constexpr char kEnableMicrophoneMuteSwitchDeviceSwitch[] = + "enable-microphone-mute-switch-device"; + #endif } // namespace switches
diff --git a/ui/events/event_switches.h b/ui/events/event_switches.h index 08a45c3..8347fd3 100644 --- a/ui/events/event_switches.h +++ b/ui/events/event_switches.h
@@ -21,6 +21,9 @@ #if defined(USE_OZONE) EVENTS_BASE_EXPORT extern const char kEdgeTouchFiltering[]; EVENTS_BASE_EXPORT extern const char kDisableCancelAllTouches[]; +EVENTS_BASE_EXPORT +extern const char kEnableMicrophoneMuteSwitchDeviceSwitch[]; + #endif } // namespace switches
diff --git a/ui/events/ozone/evdev/BUILD.gn b/ui/events/ozone/evdev/BUILD.gn index c5ef119..8b49af5 100644 --- a/ui/events/ozone/evdev/BUILD.gn +++ b/ui/events/ozone/evdev/BUILD.gn
@@ -82,8 +82,6 @@ "mouse_button_map_evdev.h", "stylus_button_event_converter_evdev.cc", "stylus_button_event_converter_evdev.h", - "switches.cc", - "switches.h", "tablet_event_converter_evdev.cc", "tablet_event_converter_evdev.h", "touch_evdev_debug_buffer.cc",
diff --git a/ui/events/ozone/evdev/input_device_factory_evdev.cc b/ui/events/ozone/evdev/input_device_factory_evdev.cc index 59ceb06..764b405d 100644 --- a/ui/events/ozone/evdev/input_device_factory_evdev.cc +++ b/ui/events/ozone/evdev/input_device_factory_evdev.cc
@@ -21,6 +21,7 @@ #include "ui/events/devices/device_data_manager.h" #include "ui/events/devices/device_util_linux.h" #include "ui/events/devices/stylus_state.h" +#include "ui/events/event_switches.h" #include "ui/events/ozone/evdev/device_event_dispatcher_evdev.h" #include "ui/events/ozone/evdev/event_converter_evdev_impl.h" #include "ui/events/ozone/evdev/event_device_info.h" @@ -28,7 +29,6 @@ #include "ui/events/ozone/evdev/keyboard_imposter_checker_evdev.h" #include "ui/events/ozone/evdev/microphone_mute_switch_event_converter_evdev.h" #include "ui/events/ozone/evdev/stylus_button_event_converter_evdev.h" -#include "ui/events/ozone/evdev/switches.h" #include "ui/events/ozone/evdev/tablet_event_converter_evdev.h" #include "ui/events/ozone/evdev/touch_evdev_types.h" #include "ui/events/ozone/evdev/touch_event_converter_evdev.h"
diff --git a/ui/events/ozone/evdev/input_device_opener_evdev.cc b/ui/events/ozone/evdev/input_device_opener_evdev.cc index e91a330..b1d477b 100644 --- a/ui/events/ozone/evdev/input_device_opener_evdev.cc +++ b/ui/events/ozone/evdev/input_device_opener_evdev.cc
@@ -11,11 +11,11 @@ #include "base/files/scoped_file.h" #include "base/memory/ptr_util.h" #include "base/trace_event/trace_event.h" +#include "ui/events/event_switches.h" #include "ui/events/ozone/evdev/event_converter_evdev_impl.h" #include "ui/events/ozone/evdev/gamepad_event_converter_evdev.h" #include "ui/events/ozone/evdev/microphone_mute_switch_event_converter_evdev.h" #include "ui/events/ozone/evdev/stylus_button_event_converter_evdev.h" -#include "ui/events/ozone/evdev/switches.h" #include "ui/events/ozone/evdev/tablet_event_converter_evdev.h" #include "ui/events/ozone/evdev/touch_event_converter_evdev.h" @@ -89,7 +89,7 @@ } if (base::CommandLine::ForCurrentProcess()->HasSwitch( - kEnableMicrophoneMuteSwitchDeviceSwitch) && + switches::kEnableMicrophoneMuteSwitchDeviceSwitch) && devinfo.IsMicrophoneMuteSwitchDevice()) { return base::WrapUnique<EventConverterEvdev>( new MicrophoneMuteSwitchEventConverterEvdev(
diff --git a/ui/events/ozone/evdev/switches.cc b/ui/events/ozone/evdev/switches.cc deleted file mode 100644 index a83cb0c7..0000000 --- a/ui/events/ozone/evdev/switches.cc +++ /dev/null
@@ -1,14 +0,0 @@ -// Copyright 2021 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "ui/events/ozone/evdev/switches.h" - -namespace ui { - -// Enables logic to detect microphone mute switch device state, which disables -// internal audio input when toggled. -constexpr char kEnableMicrophoneMuteSwitchDeviceSwitch[] = - "enable-microphone-mute-switch-device"; - -} // namespace ui
diff --git a/ui/events/ozone/evdev/switches.h b/ui/events/ozone/evdev/switches.h deleted file mode 100644 index 40965055..0000000 --- a/ui/events/ozone/evdev/switches.h +++ /dev/null
@@ -1,17 +0,0 @@ -// Copyright 2021 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef UI_EVENTS_OZONE_EVDEV_SWITCHES_H_ -#define UI_EVENTS_OZONE_EVDEV_SWITCHES_H_ - -#include "base/component_export.h" - -namespace ui { - -COMPONENT_EXPORT(EVDEV) -extern const char kEnableMicrophoneMuteSwitchDeviceSwitch[]; - -} // namespace ui - -#endif // UI_EVENTS_OZONE_EVDEV_SWITCHES_H_
diff --git a/ui/gfx/BUILD.gn b/ui/gfx/BUILD.gn index c492e236..9fd5b696 100644 --- a/ui/gfx/BUILD.gn +++ b/ui/gfx/BUILD.gn
@@ -306,8 +306,6 @@ sources += [ "platform_font_skia.cc", "platform_font_skia.h", - "skia_font_delegate.cc", - "skia_font_delegate.h", ] } @@ -382,6 +380,9 @@ } # Linux. + if (is_linux) { + deps += [ "//ui/linux:linux_ui" ] + } if (is_linux || is_chromeos) { deps += [ "//third_party/fontconfig" ] } @@ -690,11 +691,11 @@ "text_elider_unittest.cc", "text_utils_unittest.cc", ] + if (is_linux) { + sources += [ "font_render_params_linux_unittest.cc" ] + } if (is_linux || is_chromeos) { - sources += [ - "font_fallback_linux_unittest.cc", - "font_render_params_linux_unittest.cc", - ] + sources += [ "font_fallback_linux_unittest.cc" ] } if (is_mac) { sources += [ @@ -879,6 +880,10 @@ ] } + if (is_linux) { + deps += [ "//ui/linux:test_support" ] + } + if (is_linux || is_chromeos) { sources += [ "linux/fontconfig_util_unittest.cc",
diff --git a/ui/gfx/DEPS b/ui/gfx/DEPS index bff94453..73262fa7 100644 --- a/ui/gfx/DEPS +++ b/ui/gfx/DEPS
@@ -8,6 +8,7 @@ "+third_party/skia", "+third_party/test_fonts", "+ui/ios", + "+ui/linux", "+ui/ozone/buildflags.h", "-testing/gmock",
diff --git a/ui/gfx/font_render_params_linux.cc b/ui/gfx/font_render_params_linux.cc index 42bb770b..816846787 100644 --- a/ui/gfx/font_render_params_linux.cc +++ b/ui/gfx/font_render_params_linux.cc
@@ -23,9 +23,12 @@ #include "ui/gfx/font.h" #include "ui/gfx/font_render_params_linux.h" #include "ui/gfx/linux/fontconfig_util.h" -#include "ui/gfx/skia_font_delegate.h" #include "ui/gfx/switches.h" +#if BUILDFLAG(IS_LINUX) +#include "ui/linux/linux_ui.h" +#endif + namespace gfx { namespace { @@ -209,9 +212,10 @@ // Start with the delegate's settings, but let Fontconfig have the final say. FontRenderParams params; - const SkiaFontDelegate* delegate = SkiaFontDelegate::instance(); - if (delegate) - params = delegate->GetDefaultFontRenderParams(); +#if BUILDFLAG(IS_LINUX) + if (const auto* linux_ui = ui::LinuxUi::instance()) + params = linux_ui->GetDefaultFontRenderParams(); +#endif QueryFontconfig(actual_query, ¶ms, family_out); if (!params.antialiasing) { // Cairo forces full hinting when antialiasing is disabled, since anything
diff --git a/ui/gfx/font_render_params_linux_unittest.cc b/ui/gfx/font_render_params_linux_unittest.cc index 57314f8..50e80353 100644 --- a/ui/gfx/font_render_params_linux_unittest.cc +++ b/ui/gfx/font_render_params_linux_unittest.cc
@@ -18,7 +18,7 @@ #include "third_party/test_fonts/fontconfig/fontconfig_util_linux.h" #include "ui/gfx/font.h" #include "ui/gfx/linux/fontconfig_util.h" -#include "ui/gfx/skia_font_delegate.h" +#include "ui/linux/fake_linux_ui.h" namespace gfx { @@ -36,16 +36,16 @@ const char kFontconfigMatchPatternHeader[] = " <match target=\"pattern\">\n"; const char kFontconfigMatchFooter[] = " </match>\n"; -// Implementation of SkiaFontDelegate that returns a canned FontRenderParams +// Implementation of LinuxUi that returns a canned FontRenderParams // struct. This is used to isolate tests from the system's local configuration. -class TestFontDelegate : public SkiaFontDelegate { +class TestFontDelegate : public ui::FakeLinuxUi { public: - TestFontDelegate() {} + TestFontDelegate() = default; TestFontDelegate(const TestFontDelegate&) = delete; TestFontDelegate& operator=(const TestFontDelegate&) = delete; - ~TestFontDelegate() override {} + ~TestFontDelegate() override = default; void set_params(const FontRenderParams& params) { params_ = params; } @@ -55,7 +55,7 @@ void GetDefaultFontDescription(std::string* family_out, int* size_pixels_out, int* style_out, - Font::Weight* weight_out, + int* weight_out, FontRenderParams* params_out) const override { NOTIMPLEMENTED(); } @@ -111,8 +111,9 @@ class FontRenderParamsTest : public testing::Test { public: FontRenderParamsTest() { - original_font_delegate_ = SkiaFontDelegate::instance(); - SkiaFontDelegate::SetInstance(&test_font_delegate_); + auto test_font_delegate = std::make_unique<TestFontDelegate>(); + test_font_delegate_ = test_font_delegate.get(); + ui::LinuxUi::SetInstance(std::move(test_font_delegate)); ClearFontRenderParamsCacheForTest(); // Create a new fontconfig configuration and load the default fonts @@ -137,13 +138,12 @@ OverrideGlobalFontConfigForTesting(original_config_); FcConfigDestroy(override_config_); - SkiaFontDelegate::SetInstance( - const_cast<SkiaFontDelegate*>(original_font_delegate_.get())); + ui::LinuxUi::SetInstance(nullptr); + test_font_delegate_ = nullptr; } protected: - raw_ptr<const SkiaFontDelegate> original_font_delegate_; - TestFontDelegate test_font_delegate_; + raw_ptr<TestFontDelegate> test_font_delegate_; raw_ptr<FcConfig> override_config_ = nullptr; raw_ptr<FcConfig> original_config_ = nullptr; @@ -152,42 +152,40 @@ TEST_F(FontRenderParamsTest, Default) { ASSERT_TRUE(LoadConfigDataIntoFontconfig( std::string(kFontconfigFileHeader) + - // Specify the desired defaults via a font match rather than a pattern - // match (since this is the style generally used in - // /etc/fonts/conf.d). - kFontconfigMatchFontHeader + - CreateFontconfigEditStanza("antialias", "bool", "true") + - CreateFontconfigEditStanza("autohint", "bool", "true") + - CreateFontconfigEditStanza("hinting", "bool", "true") + - CreateFontconfigEditStanza("hintstyle", "const", "hintslight") + - CreateFontconfigEditStanza("rgba", "const", "rgb") + - kFontconfigMatchFooter + - // Add a font match for Arimo. Since it specifies a family, it - // shouldn't take effect when querying default settings. - kFontconfigMatchFontHeader + - CreateFontconfigTestStanza("family", "eq", "string", "Arimo") + - CreateFontconfigEditStanza("antialias", "bool", "true") + - CreateFontconfigEditStanza("autohint", "bool", "false") + - CreateFontconfigEditStanza("hinting", "bool", "true") + - CreateFontconfigEditStanza("hintstyle", "const", "hintfull") + - CreateFontconfigEditStanza("rgba", "const", "none") + - kFontconfigMatchFooter + - // Add font matches for fonts between 10 and 20 points or pixels. - // Since they specify sizes, they also should not affect the defaults. - kFontconfigMatchFontHeader + - CreateFontconfigTestStanza("size", "more_eq", "double", "10.0") + - CreateFontconfigTestStanza("size", "less_eq", "double", "20.0") + - CreateFontconfigEditStanza("antialias", "bool", "false") + - kFontconfigMatchFooter + kFontconfigMatchFontHeader + - CreateFontconfigTestStanza("pixel_size", "more_eq", "double", - "10.0") + - CreateFontconfigTestStanza("pixel_size", "less_eq", "double", - "20.0") + - CreateFontconfigEditStanza("antialias", "bool", "false") + - kFontconfigMatchFooter + kFontconfigFileFooter)); + // Specify the desired defaults via a font match rather than a pattern + // match (since this is the style generally used in + // /etc/fonts/conf.d). + kFontconfigMatchFontHeader + + CreateFontconfigEditStanza("antialias", "bool", "true") + + CreateFontconfigEditStanza("autohint", "bool", "true") + + CreateFontconfigEditStanza("hinting", "bool", "true") + + CreateFontconfigEditStanza("hintstyle", "const", "hintslight") + + CreateFontconfigEditStanza("rgba", "const", "rgb") + + kFontconfigMatchFooter + + // Add a font match for Arimo. Since it specifies a family, it + // shouldn't take effect when querying default settings. + kFontconfigMatchFontHeader + + CreateFontconfigTestStanza("family", "eq", "string", "Arimo") + + CreateFontconfigEditStanza("antialias", "bool", "true") + + CreateFontconfigEditStanza("autohint", "bool", "false") + + CreateFontconfigEditStanza("hinting", "bool", "true") + + CreateFontconfigEditStanza("hintstyle", "const", "hintfull") + + CreateFontconfigEditStanza("rgba", "const", "none") + + kFontconfigMatchFooter + + // Add font matches for fonts between 10 and 20 points or pixels. + // Since they specify sizes, they also should not affect the defaults. + kFontconfigMatchFontHeader + + CreateFontconfigTestStanza("size", "more_eq", "double", "10.0") + + CreateFontconfigTestStanza("size", "less_eq", "double", "20.0") + + CreateFontconfigEditStanza("antialias", "bool", "false") + + kFontconfigMatchFooter + kFontconfigMatchFontHeader + + CreateFontconfigTestStanza("pixel_size", "more_eq", "double", "10.0") + + CreateFontconfigTestStanza("pixel_size", "less_eq", "double", "20.0") + + CreateFontconfigEditStanza("antialias", "bool", "false") + + kFontconfigMatchFooter + kFontconfigFileFooter)); - FontRenderParams params = GetFontRenderParams( - FontRenderParamsQuery(), NULL); + FontRenderParams params = + GetFontRenderParams(FontRenderParamsQuery(), nullptr); EXPECT_TRUE(params.antialiasing); EXPECT_TRUE(params.autohinter); EXPECT_TRUE(params.use_bitmaps); @@ -200,31 +198,31 @@ TEST_F(FontRenderParamsTest, Size) { ASSERT_TRUE(LoadConfigDataIntoFontconfig( std::string(kFontconfigFileHeader) + kFontconfigMatchPatternHeader + - CreateFontconfigEditStanza("antialias", "bool", "true") + - CreateFontconfigEditStanza("hinting", "bool", "true") + - CreateFontconfigEditStanza("hintstyle", "const", "hintfull") + - CreateFontconfigEditStanza("rgba", "const", "none") + - kFontconfigMatchFooter + kFontconfigMatchPatternHeader + - CreateFontconfigTestStanza("pixelsize", "less_eq", "double", "10") + - CreateFontconfigEditStanza("antialias", "bool", "false") + - kFontconfigMatchFooter + kFontconfigMatchPatternHeader + - CreateFontconfigTestStanza("size", "more_eq", "double", "20") + - CreateFontconfigEditStanza("hintstyle", "const", "hintslight") + - CreateFontconfigEditStanza("rgba", "const", "rgb") + - kFontconfigMatchFooter + kFontconfigFileFooter)); + CreateFontconfigEditStanza("antialias", "bool", "true") + + CreateFontconfigEditStanza("hinting", "bool", "true") + + CreateFontconfigEditStanza("hintstyle", "const", "hintfull") + + CreateFontconfigEditStanza("rgba", "const", "none") + + kFontconfigMatchFooter + kFontconfigMatchPatternHeader + + CreateFontconfigTestStanza("pixelsize", "less_eq", "double", "10") + + CreateFontconfigEditStanza("antialias", "bool", "false") + + kFontconfigMatchFooter + kFontconfigMatchPatternHeader + + CreateFontconfigTestStanza("size", "more_eq", "double", "20") + + CreateFontconfigEditStanza("hintstyle", "const", "hintslight") + + CreateFontconfigEditStanza("rgba", "const", "rgb") + + kFontconfigMatchFooter + kFontconfigFileFooter)); // The defaults should be used when the supplied size isn't matched by the // second or third blocks. FontRenderParamsQuery query; query.pixel_size = 12; - FontRenderParams params = GetFontRenderParams(query, NULL); + FontRenderParams params = GetFontRenderParams(query, nullptr); EXPECT_TRUE(params.antialiasing); EXPECT_EQ(FontRenderParams::HINTING_FULL, params.hinting); EXPECT_EQ(FontRenderParams::SUBPIXEL_RENDERING_NONE, params.subpixel_rendering); query.pixel_size = 10; - params = GetFontRenderParams(query, NULL); + params = GetFontRenderParams(query, nullptr); EXPECT_FALSE(params.antialiasing); EXPECT_EQ(FontRenderParams::HINTING_FULL, params.hinting); EXPECT_EQ(FontRenderParams::SUBPIXEL_RENDERING_NONE, @@ -232,7 +230,7 @@ query.pixel_size = 0; query.point_size = 20; - params = GetFontRenderParams(query, NULL); + params = GetFontRenderParams(query, nullptr); EXPECT_TRUE(params.antialiasing); EXPECT_EQ(FontRenderParams::HINTING_SLIGHT, params.hinting); EXPECT_EQ(FontRenderParams::SUBPIXEL_RENDERING_RGB, @@ -244,41 +242,41 @@ // hinting for italic text. ASSERT_TRUE(LoadConfigDataIntoFontconfig( std::string(kFontconfigFileHeader) + kFontconfigMatchPatternHeader + - CreateFontconfigEditStanza("antialias", "bool", "true") + - CreateFontconfigEditStanza("hinting", "bool", "true") + - CreateFontconfigEditStanza("hintstyle", "const", "hintslight") + - CreateFontconfigEditStanza("rgba", "const", "rgb") + - kFontconfigMatchFooter + kFontconfigMatchPatternHeader + - CreateFontconfigTestStanza("weight", "eq", "const", "bold") + - CreateFontconfigEditStanza("rgba", "const", "none") + - kFontconfigMatchFooter + kFontconfigMatchPatternHeader + - CreateFontconfigTestStanza("slant", "eq", "const", "italic") + - CreateFontconfigEditStanza("hinting", "bool", "false") + - kFontconfigMatchFooter + kFontconfigFileFooter)); + CreateFontconfigEditStanza("antialias", "bool", "true") + + CreateFontconfigEditStanza("hinting", "bool", "true") + + CreateFontconfigEditStanza("hintstyle", "const", "hintslight") + + CreateFontconfigEditStanza("rgba", "const", "rgb") + + kFontconfigMatchFooter + kFontconfigMatchPatternHeader + + CreateFontconfigTestStanza("weight", "eq", "const", "bold") + + CreateFontconfigEditStanza("rgba", "const", "none") + + kFontconfigMatchFooter + kFontconfigMatchPatternHeader + + CreateFontconfigTestStanza("slant", "eq", "const", "italic") + + CreateFontconfigEditStanza("hinting", "bool", "false") + + kFontconfigMatchFooter + kFontconfigFileFooter)); FontRenderParamsQuery query; query.style = Font::NORMAL; - FontRenderParams params = GetFontRenderParams(query, NULL); + FontRenderParams params = GetFontRenderParams(query, nullptr); EXPECT_EQ(FontRenderParams::HINTING_SLIGHT, params.hinting); EXPECT_EQ(FontRenderParams::SUBPIXEL_RENDERING_RGB, params.subpixel_rendering); query.weight = Font::Weight::BOLD; - params = GetFontRenderParams(query, NULL); + params = GetFontRenderParams(query, nullptr); EXPECT_EQ(FontRenderParams::HINTING_SLIGHT, params.hinting); EXPECT_EQ(FontRenderParams::SUBPIXEL_RENDERING_NONE, params.subpixel_rendering); query.weight = Font::Weight::NORMAL; query.style = Font::ITALIC; - params = GetFontRenderParams(query, NULL); + params = GetFontRenderParams(query, nullptr); EXPECT_EQ(FontRenderParams::HINTING_NONE, params.hinting); EXPECT_EQ(FontRenderParams::SUBPIXEL_RENDERING_RGB, params.subpixel_rendering); query.weight = Font::Weight::BOLD; query.style = Font::ITALIC; - params = GetFontRenderParams(query, NULL); + params = GetFontRenderParams(query, nullptr); EXPECT_EQ(FontRenderParams::HINTING_NONE, params.hinting); EXPECT_EQ(FontRenderParams::SUBPIXEL_RENDERING_NONE, params.subpixel_rendering); @@ -288,15 +286,15 @@ // Load a config that only enables antialiasing for scalable fonts. ASSERT_TRUE(LoadConfigDataIntoFontconfig( std::string(kFontconfigFileHeader) + kFontconfigMatchPatternHeader + - CreateFontconfigEditStanza("antialias", "bool", "false") + - kFontconfigMatchFooter + kFontconfigMatchPatternHeader + - CreateFontconfigTestStanza("scalable", "eq", "bool", "true") + - CreateFontconfigEditStanza("antialias", "bool", "true") + - kFontconfigMatchFooter + kFontconfigFileFooter)); + CreateFontconfigEditStanza("antialias", "bool", "false") + + kFontconfigMatchFooter + kFontconfigMatchPatternHeader + + CreateFontconfigTestStanza("scalable", "eq", "bool", "true") + + CreateFontconfigEditStanza("antialias", "bool", "true") + + kFontconfigMatchFooter + kFontconfigFileFooter)); // Check that we specifically ask how scalable fonts should be rendered. - FontRenderParams params = GetFontRenderParams( - FontRenderParamsQuery(), NULL); + FontRenderParams params = + GetFontRenderParams(FontRenderParamsQuery(), nullptr); EXPECT_TRUE(params.antialiasing); } @@ -304,18 +302,18 @@ // Load a config that enables embedded bitmaps for fonts <= 10 pixels. ASSERT_TRUE(LoadConfigDataIntoFontconfig( std::string(kFontconfigFileHeader) + kFontconfigMatchPatternHeader + - CreateFontconfigEditStanza("embeddedbitmap", "bool", "false") + - kFontconfigMatchFooter + kFontconfigMatchPatternHeader + - CreateFontconfigTestStanza("pixelsize", "less_eq", "double", "10") + - CreateFontconfigEditStanza("embeddedbitmap", "bool", "true") + - kFontconfigMatchFooter + kFontconfigFileFooter)); + CreateFontconfigEditStanza("embeddedbitmap", "bool", "false") + + kFontconfigMatchFooter + kFontconfigMatchPatternHeader + + CreateFontconfigTestStanza("pixelsize", "less_eq", "double", "10") + + CreateFontconfigEditStanza("embeddedbitmap", "bool", "true") + + kFontconfigMatchFooter + kFontconfigFileFooter)); FontRenderParamsQuery query; - FontRenderParams params = GetFontRenderParams(query, NULL); + FontRenderParams params = GetFontRenderParams(query, nullptr); EXPECT_FALSE(params.use_bitmaps); query.pixel_size = 5; - params = GetFontRenderParams(query, NULL); + params = GetFontRenderParams(query, nullptr); EXPECT_TRUE(params.use_bitmaps); } @@ -324,16 +322,16 @@ // subpixel rendering. ASSERT_TRUE(LoadConfigDataIntoFontconfig( std::string(kFontconfigFileHeader) + kFontconfigMatchPatternHeader + - CreateFontconfigEditStanza("antialias", "bool", "false") + - CreateFontconfigEditStanza("hinting", "bool", "false") + - CreateFontconfigEditStanza("hintstyle", "const", "hintnone") + - CreateFontconfigEditStanza("rgba", "const", "rgb") + - kFontconfigMatchFooter + kFontconfigFileFooter)); + CreateFontconfigEditStanza("antialias", "bool", "false") + + CreateFontconfigEditStanza("hinting", "bool", "false") + + CreateFontconfigEditStanza("hintstyle", "const", "hintnone") + + CreateFontconfigEditStanza("rgba", "const", "rgb") + + kFontconfigMatchFooter + kFontconfigFileFooter)); // Full hinting should be forced. See the comment in GetFontRenderParams() for // more information. - FontRenderParams params = GetFontRenderParams( - FontRenderParamsQuery(), NULL); + FontRenderParams params = + GetFontRenderParams(FontRenderParamsQuery(), nullptr); EXPECT_FALSE(params.antialiasing); EXPECT_EQ(FontRenderParams::HINTING_FULL, params.hinting); EXPECT_EQ(FontRenderParams::SUBPIXEL_RENDERING_NONE, @@ -344,7 +342,7 @@ TEST_F(FontRenderParamsTest, ForceSubpixelPositioning) { { FontRenderParams params = - GetFontRenderParams(FontRenderParamsQuery(), NULL); + GetFontRenderParams(FontRenderParamsQuery(), nullptr); EXPECT_TRUE(params.antialiasing); EXPECT_FALSE(params.subpixel_positioning); SetFontRenderParamsDeviceScaleFactor(1.0f); @@ -354,7 +352,7 @@ // Subpixel positioning should be forced. { FontRenderParams params = - GetFontRenderParams(FontRenderParamsQuery(), NULL); + GetFontRenderParams(FontRenderParamsQuery(), nullptr); EXPECT_TRUE(params.antialiasing); EXPECT_TRUE(params.subpixel_positioning); SetFontRenderParamsDeviceScaleFactor(1.0f); @@ -377,22 +375,21 @@ } TEST_F(FontRenderParamsTest, OnlySetConfiguredValues) { - // Configure the SkiaFontDelegate (which queries GtkSettings on desktop - // Linux) to request subpixel rendering. + // Configure the LinuxUi to request subpixel rendering. FontRenderParams system_params; system_params.subpixel_rendering = FontRenderParams::SUBPIXEL_RENDERING_RGB; - test_font_delegate_.set_params(system_params); + test_font_delegate_->set_params(system_params); // Load a Fontconfig config that enables antialiasing but doesn't say anything // about subpixel rendering. ASSERT_TRUE(LoadConfigDataIntoFontconfig( std::string(kFontconfigFileHeader) + kFontconfigMatchPatternHeader + - CreateFontconfigEditStanza("antialias", "bool", "true") + - kFontconfigMatchFooter + kFontconfigFileFooter)); + CreateFontconfigEditStanza("antialias", "bool", "true") + + kFontconfigMatchFooter + kFontconfigFileFooter)); // The subpixel rendering setting from the delegate should make it through. - FontRenderParams params = GetFontRenderParams( - FontRenderParamsQuery(), NULL); + FontRenderParams params = + GetFontRenderParams(FontRenderParamsQuery(), nullptr); EXPECT_EQ(system_params.subpixel_rendering, params.subpixel_rendering); } @@ -405,7 +402,7 @@ system_params.antialiasing = true; system_params.hinting = FontRenderParams::HINTING_MEDIUM; system_params.subpixel_rendering = FontRenderParams::SUBPIXEL_RENDERING_RGB; - test_font_delegate_.set_params(system_params); + test_font_delegate_->set_params(system_params); FontRenderParamsQuery query; query.families.push_back("Arimo"); @@ -440,11 +437,11 @@ // Configure Fontconfig to use Tinos for both Helvetica and Arimo. ASSERT_TRUE(LoadConfigDataIntoFontconfig( std::string(kFontconfigFileHeader) + - CreateFontconfigAliasStanza("Helvetica", "Tinos") + - kFontconfigMatchPatternHeader + - CreateFontconfigTestStanza("family", "eq", "string", "Arimo") + - CreateFontconfigEditStanza("family", "string", "Tinos") + - kFontconfigMatchFooter + kFontconfigFileFooter)); + CreateFontconfigAliasStanza("Helvetica", "Tinos") + + kFontconfigMatchPatternHeader + + CreateFontconfigTestStanza("family", "eq", "string", "Arimo") + + CreateFontconfigEditStanza("family", "string", "Tinos") + + kFontconfigMatchFooter + kFontconfigFileFooter)); FontRenderParamsQuery query; query.families.push_back("Helvetica");
diff --git a/ui/gfx/platform_font_skia.cc b/ui/gfx/platform_font_skia.cc index 4a829602b..cefc3fe 100644 --- a/ui/gfx/platform_font_skia.cc +++ b/ui/gfx/platform_font_skia.cc
@@ -22,13 +22,16 @@ #include "ui/gfx/font.h" #include "ui/gfx/font_list.h" #include "ui/gfx/font_render_params.h" -#include "ui/gfx/skia_font_delegate.h" #include "ui/gfx/text_utils.h" #if BUILDFLAG(IS_WIN) #include "ui/gfx/system_fonts_win.h" #endif +#if BUILDFLAG(IS_LINUX) +#include "ui/linux/linux_ui.h" +#endif + namespace gfx { namespace { @@ -163,13 +166,17 @@ weight = system_font.GetWeight(); #endif // BUILDFLAG(IS_WIN) - // On Linux, SkiaFontDelegate is used to query the native toolkit (e.g. - // GTK+) for the default UI font. - const SkiaFontDelegate* delegate = SkiaFontDelegate::instance(); - if (delegate) { - delegate->GetDefaultFontDescription(&family, &size_pixels, &style, &weight, - ¶ms); - } else if (default_font_description_) { +#if BUILDFLAG(IS_LINUX) + // On Linux, LinuxUi is used to query the native toolkit (e.g. + // GTK) for the default UI font. + if (const auto* linux_ui = ui::LinuxUi::instance()) { + int weight_int; + linux_ui->GetDefaultFontDescription( + &family, &size_pixels, &style, static_cast<int*>(&weight_int), ¶ms); + weight = static_cast<Font::Weight>(weight_int); + } else +#endif + if (default_font_description_) { #if BUILDFLAG(IS_CHROMEOS) // On ChromeOS, a FontList font description string is stored as a // translatable resource and passed in via SetDefaultFontDescription().
diff --git a/ui/gfx/platform_font_skia_unittest.cc b/ui/gfx/platform_font_skia_unittest.cc index d2ad3c7..6060900a 100644 --- a/ui/gfx/platform_font_skia_unittest.cc +++ b/ui/gfx/platform_font_skia_unittest.cc
@@ -15,17 +15,20 @@ #include "ui/gfx/font.h" #include "ui/gfx/font_names_testing.h" #include "ui/gfx/font_render_params.h" -#include "ui/gfx/skia_font_delegate.h" #if BUILDFLAG(IS_WIN) #include "ui/gfx/system_fonts_win.h" #endif +#if BUILDFLAG(IS_LINUX) +#include "ui/linux/fake_linux_ui.h" +#endif + namespace gfx { -// Implementation of SkiaFontDelegate used to control the default font -// description. -class TestFontDelegate : public SkiaFontDelegate { +#if BUILDFLAG(IS_LINUX) +// Implementation of LinuxUi used to control the default font description. +class TestFontDelegate : public ui::FakeLinuxUi { public: TestFontDelegate() = default; @@ -48,12 +51,12 @@ void GetDefaultFontDescription(std::string* family_out, int* size_pixels_out, int* style_out, - Font::Weight* weight_out, + int* weight_out, FontRenderParams* params_out) const override { *family_out = family_; *size_pixels_out = size_pixels_; *style_out = style_; - *weight_out = weight_; + *weight_out = static_cast<int>(weight_); *params_out = params_; } @@ -76,36 +79,33 @@ ~PlatformFontSkiaTest() override = default; void SetUp() override { - original_font_delegate_ = SkiaFontDelegate::instance(); - SkiaFontDelegate::SetInstance(&test_font_delegate_); + DCHECK_EQ(ui::LinuxUi::instance(), nullptr); + auto test_font_delegate = std::make_unique<TestFontDelegate>(); + test_font_delegate_ = test_font_delegate.get(); + ui::LinuxUi::SetInstance(std::move(test_font_delegate)); PlatformFontSkia::ReloadDefaultFont(); } void TearDown() override { - DCHECK_EQ(&test_font_delegate_, SkiaFontDelegate::instance()); - SkiaFontDelegate::SetInstance( - const_cast<SkiaFontDelegate*>(original_font_delegate_.get())); + DCHECK_EQ(test_font_delegate_, ui::LinuxUi::instance()); + ui::LinuxUi::SetInstance(nullptr); PlatformFontSkia::ReloadDefaultFont(); } protected: - TestFontDelegate test_font_delegate_; - - private: - // Originally-registered delegate. - raw_ptr<const SkiaFontDelegate> original_font_delegate_; + TestFontDelegate* test_font_delegate_ = nullptr; }; // Test that PlatformFontSkia's default constructor initializes the instance // with the correct parameters. TEST_F(PlatformFontSkiaTest, DefaultFont) { - test_font_delegate_.set_family(kTestFontName); - test_font_delegate_.set_size_pixels(13); - test_font_delegate_.set_style(Font::NORMAL); + test_font_delegate_->set_family(kTestFontName); + test_font_delegate_->set_size_pixels(13); + test_font_delegate_->set_style(Font::NORMAL); FontRenderParams params; params.antialiasing = false; params.hinting = FontRenderParams::HINTING_FULL; - test_font_delegate_.set_params(params); + test_font_delegate_->set_params(params); scoped_refptr<gfx::PlatformFontSkia> font(new gfx::PlatformFontSkia()); EXPECT_EQ(kTestFontName, font->GetFontName()); EXPECT_EQ(13, font->GetFontSize()); @@ -115,10 +115,10 @@ EXPECT_EQ(params.hinting, font->GetFontRenderParams().hinting); // Drop the old default font and check that new settings are loaded. - test_font_delegate_.set_family(kSymbolFontName); - test_font_delegate_.set_size_pixels(15); - test_font_delegate_.set_style(gfx::Font::ITALIC); - test_font_delegate_.set_weight(gfx::Font::Weight::BOLD); + test_font_delegate_->set_family(kSymbolFontName); + test_font_delegate_->set_size_pixels(15); + test_font_delegate_->set_style(gfx::Font::ITALIC); + test_font_delegate_->set_weight(gfx::Font::Weight::BOLD); PlatformFontSkia::ReloadDefaultFont(); scoped_refptr<gfx::PlatformFontSkia> font2(new gfx::PlatformFontSkia()); EXPECT_EQ(kSymbolFontName, font2->GetFontName()); @@ -126,6 +126,7 @@ EXPECT_NE(font2->GetStyle() & Font::ITALIC, 0); EXPECT_EQ(gfx::Font::Weight::BOLD, font2->GetWeight()); } +#endif // BUILDFLAG(IS_LINUX) TEST(PlatformFontSkiaRenderParamsTest, DefaultFontRenderParams) { scoped_refptr<PlatformFontSkia> default_font(new PlatformFontSkia());
diff --git a/ui/gfx/skia_font_delegate.cc b/ui/gfx/skia_font_delegate.cc deleted file mode 100644 index f7c24c5f..0000000 --- a/ui/gfx/skia_font_delegate.cc +++ /dev/null
@@ -1,23 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "ui/gfx/skia_font_delegate.h" - -namespace { - -gfx::SkiaFontDelegate* g_skia_font_delegate = 0; - -} // namespace - -namespace gfx { - -void SkiaFontDelegate::SetInstance(SkiaFontDelegate* instance) { - g_skia_font_delegate = instance; -} - -const SkiaFontDelegate* SkiaFontDelegate::instance() { - return g_skia_font_delegate; -} - -} // namespace gfx
diff --git a/ui/gfx/skia_font_delegate.h b/ui/gfx/skia_font_delegate.h deleted file mode 100644 index 05636e9..0000000 --- a/ui/gfx/skia_font_delegate.h +++ /dev/null
@@ -1,48 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef UI_GFX_SKIA_FONT_DELEGATE_H_ -#define UI_GFX_SKIA_FONT_DELEGATE_H_ - -#include <memory> -#include <string> - -#include "ui/gfx/font_render_params.h" -#include "ui/gfx/gfx_export.h" - -namespace gfx { - -// Allows a Linux platform-specific overriding of font preferences. -class GFX_EXPORT SkiaFontDelegate { - public: - virtual ~SkiaFontDelegate() {} - - // Sets the dynamically loaded singleton that provides font preferences. - // This pointer is not owned, and if this method is called a second time, - // the first instance is not deleted. - static void SetInstance(SkiaFontDelegate* instance); - - // Returns a SkiaFontDelegate instance for the toolkit used in - // the user's desktop environment. - // - // Can return NULL, in case no toolkit has been set. (For example, if we're - // running with the "--ash" flag.) - static const SkiaFontDelegate* instance(); - - // Returns the default font rendering settings. - virtual FontRenderParams GetDefaultFontRenderParams() const = 0; - - // Returns details about the default UI font. |style_out| holds a bitfield of - // gfx::Font::Style values. - virtual void GetDefaultFontDescription( - std::string* family_out, - int* size_pixels_out, - int* style_out, - Font::Weight* weight_out, - FontRenderParams* params_out) const = 0; -}; - -} // namespace gfx - -#endif // UI_GFX_SKIA_FONT_DELEGATE_H_
diff --git a/ui/gtk/gtk_ui.cc b/ui/gtk/gtk_ui.cc index b410534..1fbb5815 100644 --- a/ui/gtk/gtk_ui.cc +++ b/ui/gtk/gtk_ui.cc
@@ -458,12 +458,12 @@ void GtkUi::GetDefaultFontDescription(std::string* family_out, int* size_pixels_out, int* style_out, - gfx::Font::Weight* weight_out, + int* weight_out, gfx::FontRenderParams* params_out) const { *family_out = default_font_family_; *size_pixels_out = default_font_size_pixels_; *style_out = default_font_style_; - *weight_out = default_font_weight_; + *weight_out = static_cast<int>(default_font_weight_); *params_out = default_font_render_params_; }
diff --git a/ui/gtk/gtk_ui.h b/ui/gtk/gtk_ui.h index 1137f17..e73bbdf 100644 --- a/ui/gtk/gtk_ui.h +++ b/ui/gtk/gtk_ui.h
@@ -14,6 +14,7 @@ #include "printing/buildflags/buildflags.h" #include "ui/base/glib/glib_signal.h" #include "ui/gfx/color_utils.h" +#include "ui/gfx/font_render_params.h" #include "ui/gtk/gtk_ui_platform.h" #include "ui/linux/linux_ui_base.h" #include "ui/linux/window_frame_provider.h" @@ -66,7 +67,7 @@ std::string* family_out, int* size_pixels_out, int* style_out, - gfx::Font::Weight* weight_out, + int* weight_out, gfx::FontRenderParams* params_out) const override; // ui::ShellDialogLinux:
diff --git a/ui/linux/BUILD.gn b/ui/linux/BUILD.gn index fa3e8d33..48b78fe 100644 --- a/ui/linux/BUILD.gn +++ b/ui/linux/BUILD.gn
@@ -29,7 +29,6 @@ deps = [ "//base", "//build:chromecast_buildflags", - "//ui/gfx", "//ui/gfx/animation", ] public_deps = [ @@ -46,6 +45,7 @@ "//base", "//ui/gfx", "//ui/native_theme", + "//ui/shell_dialogs", ] }
diff --git a/ui/linux/fake_linux_ui.cc b/ui/linux/fake_linux_ui.cc index 5dfb564..211b9093 100644 --- a/ui/linux/fake_linux_ui.cc +++ b/ui/linux/fake_linux_ui.cc
@@ -6,6 +6,7 @@ #include "base/time/time.h" #include "ui/gfx/color_palette.h" +#include "ui/gfx/font_render_params.h" #include "ui/gfx/geometry/size.h" #include "ui/gfx/image/image.h" #include "ui/shell_dialogs/select_file_policy.h" @@ -30,7 +31,7 @@ std::string* family_out, int* size_pixels_out, int* style_out, - gfx::Font::Weight* weight_out, + int* weight_out, gfx::FontRenderParams* params_out) const {} ui::SelectFileDialog* FakeLinuxUi::CreateSelectFileDialog(
diff --git a/ui/linux/fake_linux_ui.h b/ui/linux/fake_linux_ui.h index 7e3228c..17ab87be 100644 --- a/ui/linux/fake_linux_ui.h +++ b/ui/linux/fake_linux_ui.h
@@ -24,7 +24,7 @@ std::string* family_out, int* size_pixels_out, int* style_out, - gfx::Font::Weight* weight_out, + int* weight_out, gfx::FontRenderParams* params_out) const override; ui::SelectFileDialog* CreateSelectFileDialog( void* listener,
diff --git a/ui/linux/linux_ui.cc b/ui/linux/linux_ui.cc index 5e9ec12..bae51ee 100644 --- a/ui/linux/linux_ui.cc +++ b/ui/linux/linux_ui.cc
@@ -28,7 +28,6 @@ // static std::unique_ptr<LinuxUi> LinuxUi::SetInstance( std::unique_ptr<LinuxUi> instance) { - SkiaFontDelegate::SetInstance(instance.get()); gfx::AnimationSettingsProviderLinux::SetInstance(instance.get()); return std::exchange(GetLinuxUiInstance(), std::move(instance));
diff --git a/ui/linux/linux_ui.h b/ui/linux/linux_ui.h index 41b2bb0..e8be3f2e 100644 --- a/ui/linux/linux_ui.h +++ b/ui/linux/linux_ui.h
@@ -19,8 +19,6 @@ #include "printing/buildflags/buildflags.h" #include "third_party/skia/include/core/SkColor.h" #include "ui/gfx/animation/animation_settings_provider_linux.h" -#include "ui/gfx/geometry/size.h" -#include "ui/gfx/skia_font_delegate.h" // The main entrypoint into Linux toolkit specific code. GTK/QT code should only // be executed behind this interface. @@ -34,6 +32,7 @@ } namespace gfx { +struct FontRenderParams; class Image; class Size; } // namespace gfx @@ -61,8 +60,7 @@ // Adapter class with targets to render like different toolkits. Set by any // project that wants to do linux desktop native rendering. class COMPONENT_EXPORT(LINUX_UI) LinuxUi - : public gfx::SkiaFontDelegate, - public gfx::AnimationSettingsProviderLinux { + : public gfx::AnimationSettingsProviderLinux { public: using UseSystemThemeCallback = base::RepeatingCallback<bool(aura::Window* window)>; @@ -218,6 +216,18 @@ const ui::Event& event, std::vector<TextEditCommandAuraLinux>* commands) = 0; + // Returns the default font rendering settings. + virtual gfx::FontRenderParams GetDefaultFontRenderParams() const = 0; + + // Returns details about the default UI font. |style_out| holds a bitfield of + // gfx::Font::Style values. + virtual void GetDefaultFontDescription( + std::string* family_out, + int* size_pixels_out, + int* style_out, + int* weight_out, + gfx::FontRenderParams* params_out) const = 0; + protected: struct CmdLineArgs { CmdLineArgs();
diff --git a/ui/ozone/platform/drm/gpu/mock_drm_device.cc b/ui/ozone/platform/drm/gpu/mock_drm_device.cc index c23fe32..dc1a784e 100644 --- a/ui/ozone/platform/drm/gpu/mock_drm_device.cc +++ b/ui/ozone/platform/drm/gpu/mock_drm_device.cc
@@ -37,7 +37,6 @@ }; namespace ui { - namespace { constexpr uint32_t kTestModesetFlags = @@ -45,6 +44,11 @@ constexpr uint32_t kCommitModesetFlags = DRM_MODE_ATOMIC_ALLOW_MODESET; +// Seamless modeset is defined by the lack of DRM_MODE_ATOMIC_ALLOW_MODESET. +// This also happens to be the same set of flags as would be used for a +// pageflip, or other atomic property changes that do not require modesetting. +constexpr uint32_t kSeamlessModesetFlags = 0; + template <class Object> Object* DrmAllocator() { return static_cast<Object*>(drmMalloc(sizeof(Object))); @@ -478,12 +482,20 @@ uint32_t crtc_count, scoped_refptr<PageFlipRequest> page_flip_request) { commit_count_++; - if (flags == kTestModesetFlags) - ++test_modeset_count_; - else if (flags == kCommitModesetFlags) - ++commit_modeset_count_; + const bool test_only = flags & DRM_MODE_ATOMIC_TEST_ONLY; + switch (flags) { + case kTestModesetFlags: + ++test_modeset_count_; + break; + case kCommitModesetFlags: + ++commit_modeset_count_; + break; + case kSeamlessModesetFlags: + ++seamless_modeset_count_; + break; + } - if ((flags & kCommitModesetFlags && !set_crtc_expectation_) || + if ((!test_only && !set_crtc_expectation_) || (flags & DRM_MODE_ATOMIC_NONBLOCK && !commit_expectation_)) { return false; } @@ -517,7 +529,7 @@ if (page_flip_request) callbacks_.push(page_flip_request->AddPageFlip()); - if (flags & DRM_MODE_ATOMIC_TEST_ONLY) + if (test_only) return true; // Only update values if not testing.
diff --git a/ui/ozone/platform/drm/gpu/mock_drm_device.h b/ui/ozone/platform/drm/gpu/mock_drm_device.h index 05269d403..2c5456c 100644 --- a/ui/ozone/platform/drm/gpu/mock_drm_device.h +++ b/ui/ozone/platform/drm/gpu/mock_drm_device.h
@@ -77,6 +77,7 @@ int get_overlay_clear_call_count() const { return overlay_clear_call_count_; } int get_test_modeset_count() const { return test_modeset_count_; } int get_commit_modeset_count() const { return commit_modeset_count_; } + int get_seamless_modeset_count() const { return seamless_modeset_count_; } int get_commit_count() const { return commit_count_; } int get_set_object_property_count() const { return set_object_property_count_; @@ -232,6 +233,7 @@ int allocate_buffer_count_; int test_modeset_count_ = 0; int commit_modeset_count_ = 0; + int seamless_modeset_count_ = 0; int commit_count_ = 0; int set_object_property_count_ = 0; int set_gamma_ramp_count_ = 0;
diff --git a/ui/ozone/platform/drm/gpu/screen_manager.cc b/ui/ozone/platform/drm/gpu/screen_manager.cc index 2b3cd7d..f6c4d2cd 100644 --- a/ui/ozone/platform/drm/gpu/screen_manager.cc +++ b/ui/ozone/platform/drm/gpu/screen_manager.cc
@@ -323,6 +323,9 @@ ParamsToTracedValue(controllers_params, modeset_flag), "before", base::trace_event::ToTracedValue(this)); + // At least one of these flags must be set. + DCHECK(modeset_flag & (display::kCommitModeset | display::kTestModeset)); + // Split them to different lists unique to each DRM Device. base::flat_map<scoped_refptr<DrmDevice>, ControllerConfigsList> displays_for_drm_devices; @@ -337,6 +340,7 @@ } const bool commit_modeset = modeset_flag & display::kCommitModeset; + const bool is_seamless_modeset = modeset_flag & display::kSeamlessModeset; bool config_success = true; // Perform display configurations together for the same DRM only. for (const auto& configs_on_drm : displays_for_drm_devices) { @@ -346,8 +350,9 @@ if (modeset_flag & display::kTestModeset) { bool test_modeset = - TestAndSetPreferredModifiers(drm_controllers_params) || - TestAndSetLinearModifier(drm_controllers_params); + TestAndSetPreferredModifiers(drm_controllers_params, + is_seamless_modeset) || + TestAndSetLinearModifier(drm_controllers_params, is_seamless_modeset); config_success &= test_modeset; VLOG(1) << "Test-modeset " << (test_modeset ? "succeeded." : "failed."); if (!test_modeset) @@ -356,9 +361,10 @@ if (commit_modeset) { bool can_modeset_with_overlays = - TestModesetWithOverlays(drm_controllers_params); + TestModesetWithOverlays(drm_controllers_params, is_seamless_modeset); bool modeset_commit_result = - Modeset(drm_controllers_params, can_modeset_with_overlays); + Modeset(drm_controllers_params, can_modeset_with_overlays, + is_seamless_modeset); config_success &= modeset_commit_result; if (modeset_commit_result) { VLOG(1) << "Modeset succeeded."; @@ -378,7 +384,8 @@ } bool ScreenManager::TestAndSetPreferredModifiers( - const ControllerConfigsList& controllers_params) { + const ControllerConfigsList& controllers_params, + bool is_seamless_modeset) { TRACE_EVENT1("drm", "ScreenManager::TestAndSetPreferredModifiers", "display_count", controllers_params.size()); @@ -416,9 +423,10 @@ } } - if (!drm->plane_manager()->Commit( - std::move(commit_request), - DRM_MODE_ATOMIC_TEST_ONLY | DRM_MODE_ATOMIC_ALLOW_MODESET)) { + uint32_t flags = DRM_MODE_ATOMIC_TEST_ONLY; + if (!is_seamless_modeset) + flags |= DRM_MODE_ATOMIC_ALLOW_MODESET; + if (!drm->plane_manager()->Commit(std::move(commit_request), flags)) { return false; } @@ -427,7 +435,8 @@ } bool ScreenManager::TestAndSetLinearModifier( - const ControllerConfigsList& controllers_params) { + const ControllerConfigsList& controllers_params, + bool is_seamless_modeset) { TRACE_EVENT1("drm", "ScreenManager::TestAndSetLinearModifier", "display_count", controllers_params.size()); @@ -468,9 +477,10 @@ } } - if (!drm->plane_manager()->Commit( - std::move(commit_request), - DRM_MODE_ATOMIC_TEST_ONLY | DRM_MODE_ATOMIC_ALLOW_MODESET)) { + uint32_t flags = DRM_MODE_ATOMIC_TEST_ONLY; + if (!is_seamless_modeset) + flags |= DRM_MODE_ATOMIC_ALLOW_MODESET; + if (!drm->plane_manager()->Commit(std::move(commit_request), flags)) { return false; } @@ -501,7 +511,8 @@ } bool ScreenManager::TestModesetWithOverlays( - const ControllerConfigsList& controllers_params) { + const ControllerConfigsList& controllers_params, + bool is_seamless_modeset) { TRACE_EVENT1("drm", "ScreenManager::TestModesetWithOverlays", "display_count", controllers_params.size()); @@ -537,13 +548,15 @@ if (!does_an_overlay_exist) return false; - return drm->plane_manager()->Commit( - std::move(commit_request), - DRM_MODE_ATOMIC_TEST_ONLY | DRM_MODE_ATOMIC_ALLOW_MODESET); + uint32_t flags = DRM_MODE_ATOMIC_TEST_ONLY; + if (!is_seamless_modeset) + flags |= DRM_MODE_ATOMIC_ALLOW_MODESET; + return drm->plane_manager()->Commit(std::move(commit_request), flags); } bool ScreenManager::Modeset(const ControllerConfigsList& controllers_params, - bool can_modeset_with_overlays) { + bool can_modeset_with_overlays, + bool is_seamless_modeset) { TRACE_EVENT2("drm", "ScreenManager::Modeset", "display_count", controllers_params.size(), "modeset_with_overlays", can_modeset_with_overlays); @@ -579,8 +592,8 @@ } } - bool commit_status = drm->plane_manager()->Commit( - commit_request, DRM_MODE_ATOMIC_ALLOW_MODESET); + uint32_t flags = is_seamless_modeset ? 0 : DRM_MODE_ATOMIC_ALLOW_MODESET; + bool commit_status = drm->plane_manager()->Commit(commit_request, flags); UpdateControllerStateAfterModeset(drm, commit_request, commit_status);
diff --git a/ui/ozone/platform/drm/gpu/screen_manager.h b/ui/ozone/platform/drm/gpu/screen_manager.h index ada15d9..97ae879 100644 --- a/ui/ozone/platform/drm/gpu/screen_manager.h +++ b/ui/ozone/platform/drm/gpu/screen_manager.h
@@ -119,9 +119,10 @@ uint32_t crtc); bool TestAndSetPreferredModifiers( - const ControllerConfigsList& controllers_params); - bool TestAndSetLinearModifier( - const ControllerConfigsList& controllers_params); + const ControllerConfigsList& controllers_params, + bool is_seamless_modeset); + bool TestAndSetLinearModifier(const ControllerConfigsList& controllers_params, + bool is_seamless_modeset); // Setting the Preferred modifiers that passed from one of the Modeset Test // functions. The preferred modifiers are used in Modeset. void SetPreferredModifiers( @@ -130,9 +131,11 @@ // The planes used for modesetting can have overlays beside the primary, test // if we can modeset with them. If not, return false to indicate that we must // only use the primary plane. - bool TestModesetWithOverlays(const ControllerConfigsList& controllers_params); + bool TestModesetWithOverlays(const ControllerConfigsList& controllers_params, + bool is_seamless_modeset); bool Modeset(const ControllerConfigsList& controllers_params, - bool can_modeset_with_overlays); + bool can_modeset_with_overlays, + bool is_seamless_modeset); // Configures a display controller to be enabled. The display controller is // identified by (|crtc|, |connector|) and the controller is to be modeset
diff --git a/ui/ozone/platform/drm/gpu/screen_manager_unittest.cc b/ui/ozone/platform/drm/gpu/screen_manager_unittest.cc index 3d0258b8..e91a722 100644 --- a/ui/ozone/platform/drm/gpu/screen_manager_unittest.cc +++ b/ui/ozone/platform/drm/gpu/screen_manager_unittest.cc
@@ -265,6 +265,24 @@ EXPECT_TRUE(controller->HasCrtc(drm_, kPrimaryCrtc)); } +TEST_F(ScreenManagerTest, CheckWithSeamlessModeset) { + InitializeDrmStateWithDefault(drm_.get(), /*is_atomic=*/true); + + screen_manager_->AddDisplayController(drm_, kPrimaryCrtc, kPrimaryConnector); + + ScreenManager::ControllerConfigsList controllers_to_enable; + controllers_to_enable.emplace_back( + kPrimaryDisplayId, drm_, kPrimaryCrtc, kPrimaryConnector, + GetPrimaryBounds().origin(), + std::make_unique<drmModeModeInfo>(kDefaultMode)); + screen_manager_->ConfigureDisplayControllers( + controllers_to_enable, + display::kCommitModeset | display::kSeamlessModeset); + + EXPECT_EQ(drm_->get_commit_modeset_count(), 0); + EXPECT_EQ(drm_->get_seamless_modeset_count(), 1); +} + TEST_F(ScreenManagerTest, CheckWithInvalidBounds) { InitializeDrmStateWithDefault(drm_.get(), /*is_atomic=*/true);
diff --git a/ui/ozone/platform/wayland/host/wayland_popup.cc b/ui/ozone/platform/wayland/host/wayland_popup.cc index 68d8044..ef51aa8 100644 --- a/ui/ozone/platform/wayland/host/wayland_popup.cc +++ b/ui/ozone/platform/wayland/host/wayland_popup.cc
@@ -50,11 +50,12 @@ params.bounds = bounds_dip; params.menu_type = delegate()->GetMenuType().value_or(MenuType::kRootContextMenu); - params.anchor = delegate()->GetOwnedWindowAnchorAndRectInDIP(); + params.anchor = delegate()->GetOwnedWindowAnchorAndRectInPx(); if (params.anchor.has_value()) { + // TODO(crbug.com/1306688): Change anchor_rect to DIP. params.anchor->anchor_rect = delegate()->ConvertRectToDIP(wl::TranslateBoundsToParentCoordinates( - params.anchor->anchor_rect, parent_window()->GetBoundsInDIP())); + params.anchor->anchor_rect, parent_window()->GetBoundsInPixels())); // If size is empty, set 1x1. if (params.anchor->anchor_rect.size().IsEmpty()) params.anchor->anchor_rect.set_size({1, 1});
diff --git a/ui/ozone/platform/wayland/host/wayland_window_unittest.cc b/ui/ozone/platform/wayland/host/wayland_window_unittest.cc index 6dfa4580..89a37a2a 100644 --- a/ui/ozone/platform/wayland/host/wayland_window_unittest.cc +++ b/ui/ozone/platform/wayland/host/wayland_window_unittest.cc
@@ -2302,7 +2302,7 @@ MockWaylandPlatformWindowDelegate menu_window_delegate; EXPECT_CALL(menu_window_delegate, GetMenuType()) .WillOnce(Return(MenuType::kRootMenu)); - EXPECT_CALL(menu_window_delegate, GetOwnedWindowAnchorAndRectInDIP()) + EXPECT_CALL(menu_window_delegate, GetOwnedWindowAnchorAndRectInPx()) .WillOnce(Return(absl::nullopt)); gfx::Rect menu_window_bounds(gfx::Point(439, 46), menu_window_positioner.size); @@ -2382,7 +2382,7 @@ OwnedWindowAnchorPosition::kBottomRight, OwnedWindowAnchorGravity::kBottomLeft, OwnedWindowConstraintAdjustment::kAdjustmentFlipY}; - EXPECT_CALL(menu_window_delegate, GetOwnedWindowAnchorAndRectInDIP()) + EXPECT_CALL(menu_window_delegate, GetOwnedWindowAnchorAndRectInPx()) .WillOnce(Return(anchor)); gfx::Rect menu_window_bounds(gfx::Point(176, 74), menu_window_positioner.size); @@ -2405,7 +2405,7 @@ OwnedWindowAnchorGravity::kBottomRight, OwnedWindowConstraintAdjustment::kAdjustmentFlipY | OwnedWindowConstraintAdjustment::kAdjustmentFlipX}; - EXPECT_CALL(nested_menu_window_delegate, GetOwnedWindowAnchorAndRectInDIP()) + EXPECT_CALL(nested_menu_window_delegate, GetOwnedWindowAnchorAndRectInPx()) .WillOnce(Return(anchor)); gfx::Rect nested_menu_window_bounds(gfx::Point(492, 157), nested_menu_window_positioner.size);
diff --git a/ui/ozone/platform/x11/gl_ozone_glx.cc b/ui/ozone/platform/x11/gl_ozone_glx.cc index 0e19b76..06dc614 100644 --- a/ui/ozone/platform/x11/gl_ozone_glx.cc +++ b/ui/ozone/platform/x11/gl_ozone_glx.cc
@@ -90,7 +90,7 @@ } bool GLOzoneGLX::CanImportNativePixmap() { - return gl::GLImageGLXNativePixmap::CanImportNativePixmap(); + return false; } std::unique_ptr<NativePixmapGLBinding> GLOzoneGLX::ImportNativePixmap(
diff --git a/ui/ozone/platform/x11/x11_surface_factory.cc b/ui/ozone/platform/x11/x11_surface_factory.cc index 9a122d1..e7affbd 100644 --- a/ui/ozone/platform/x11/x11_surface_factory.cc +++ b/ui/ozone/platform/x11/x11_surface_factory.cc
@@ -45,10 +45,7 @@ return GLOzoneEGL::InitializeStaticGLBindings(implementation); } - bool CanImportNativePixmap() override { - return gl::GLSurfaceEGL::GetGLDisplayEGL() - ->ext->b_EGL_NOK_texture_from_pixmap; - } + bool CanImportNativePixmap() override { return false; } // This implementation is used when ANGLE supports pixmaps through // eglCreatePixmapSurface and exposes it through EGL extension
diff --git a/ui/ozone/test/mock_platform_window_delegate.h b/ui/ozone/test/mock_platform_window_delegate.h index c63540a..9da8666 100644 --- a/ui/ozone/test/mock_platform_window_delegate.h +++ b/ui/ozone/test/mock_platform_window_delegate.h
@@ -40,7 +40,7 @@ MOCK_METHOD0(GetMinimumSizeForWindow, absl::optional<gfx::Size>()); MOCK_METHOD0(GetMaximumSizeForWindow, absl::optional<gfx::Size>()); MOCK_METHOD0(GetMenuType, absl::optional<MenuType>()); - MOCK_METHOD0(GetOwnedWindowAnchorAndRectInDIP, + MOCK_METHOD0(GetOwnedWindowAnchorAndRectInPx, absl::optional<OwnedWindowAnchor>()); MOCK_METHOD0(OnMouseEnter, void()); };
diff --git a/ui/platform_window/platform_window_delegate.cc b/ui/platform_window/platform_window_delegate.cc index 5b88c11..ea10ab5 100644 --- a/ui/platform_window/platform_window_delegate.cc +++ b/ui/platform_window/platform_window_delegate.cc
@@ -44,7 +44,7 @@ PlatformWindowOcclusionState occlusion_state) {} absl::optional<OwnedWindowAnchor> -PlatformWindowDelegate::GetOwnedWindowAnchorAndRectInDIP() { +PlatformWindowDelegate::GetOwnedWindowAnchorAndRectInPx() { return absl::nullopt; }
diff --git a/ui/platform_window/platform_window_delegate.h b/ui/platform_window/platform_window_delegate.h index ea72df25..1bae07b 100644 --- a/ui/platform_window/platform_window_delegate.h +++ b/ui/platform_window/platform_window_delegate.h
@@ -135,7 +135,7 @@ // positioning. Useful for such backends as Wayland as it provides flexibility // in positioning child windows, which must be repositioned if the originally // intended position caused the surface to be constrained. - virtual absl::optional<OwnedWindowAnchor> GetOwnedWindowAnchorAndRectInDIP(); + virtual absl::optional<OwnedWindowAnchor> GetOwnedWindowAnchorAndRectInPx(); // Enables or disables frame rate throttling. virtual void SetFrameRateThrottleEnabled(bool enabled);
diff --git a/ui/qt/qt_ui.cc b/ui/qt/qt_ui.cc index a44ed93b..5cb5798 100644 --- a/ui/qt/qt_ui.cc +++ b/ui/qt/qt_ui.cc
@@ -137,7 +137,7 @@ void QtUi::GetDefaultFontDescription(std::string* family_out, int* size_pixels_out, int* style_out, - gfx::Font::Weight* weight_out, + int* weight_out, gfx::FontRenderParams* params_out) const { if (family_out) *family_out = font_family_; @@ -335,15 +335,14 @@ font_size_pixels_ = font_size_points_ * GetDeviceScaleFactor(); } font_style_ = desc.is_italic ? gfx::Font::ITALIC : gfx::Font::NORMAL; - font_weight_ = - static_cast<gfx::Font::Weight>(QtWeightToCssWeight(desc.weight)); + font_weight_ = QtWeightToCssWeight(desc.weight); gfx::FontRenderParamsQuery query; query.families = {font_family_}; query.pixel_size = font_size_pixels_; query.point_size = font_size_points_; query.style = font_style_; - query.weight = font_weight_; + query.weight = static_cast<gfx::Font::Weight>(font_weight_); gfx::FontRenderParams fc_params; gfx::QueryFontconfig(query, &fc_params, nullptr);
diff --git a/ui/qt/qt_ui.h b/ui/qt/qt_ui.h index 179b0b3..2735e26 100644 --- a/ui/qt/qt_ui.h +++ b/ui/qt/qt_ui.h
@@ -12,6 +12,7 @@ #include "third_party/abseil-cpp/absl/types/optional.h" #include "ui/color/color_provider.h" #include "ui/color/color_provider_manager.h" +#include "ui/gfx/font_render_params.h" #include "ui/linux/linux_ui_base.h" #include "ui/qt/qt_interface.h" @@ -43,7 +44,7 @@ std::string* family_out, int* size_pixels_out, int* style_out, - gfx::Font::Weight* weight_out, + int* weight_out, gfx::FontRenderParams* params_out) const override; // ui::ShellDialogLinux: @@ -111,7 +112,7 @@ int font_size_pixels_ = 0; int font_size_points_ = 0; gfx::Font::FontStyle font_style_ = gfx::Font::NORMAL; - gfx::Font::Weight font_weight_; + int font_weight_; gfx::FontRenderParams font_params_; std::unique_ptr<QtInterface> shim_;
diff --git a/ui/views/cocoa/native_widget_mac_ns_window_host.mm b/ui/views/cocoa/native_widget_mac_ns_window_host.mm index 0ca869c..84aa682 100644 --- a/ui/views/cocoa/native_widget_mac_ns_window_host.mm +++ b/ui/views/cocoa/native_widget_mac_ns_window_host.mm
@@ -373,11 +373,10 @@ in_process_ns_window_bridge_.get(), GetNSWindowMojo()); Widget* widget = native_widget_mac_->GetWidget(); - // Tooltip Widgets shouldn't have their own tooltip manager, but tooltips are - // native on Mac, so nothing should ever want one in Widget form. - DCHECK_NE(params.type, Widget::InitParams::TYPE_TOOLTIP); widget_type_ = params.type; - tooltip_manager_ = std::make_unique<TooltipManagerMac>(GetNSWindowMojo()); + bool is_tooltip = params.type == Widget::InitParams::TYPE_TOOLTIP; + if (!is_tooltip) + tooltip_manager_ = std::make_unique<TooltipManagerMac>(GetNSWindowMojo()); if (params.workspace.length()) { std::string restoration_data; @@ -396,6 +395,7 @@ window_params->is_translucent = params.opacity == Widget::InitParams::WindowOpacity::kTranslucent; window_params->is_headless_mode_window = params.headless_mode; + window_params->is_tooltip = is_tooltip; is_headless_mode_window_ = params.headless_mode; // OSX likes to put shadows on most things. However, frameless windows (with
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.cc b/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.cc index a24ea472..fdead2a 100644 --- a/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.cc +++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.cc
@@ -904,12 +904,17 @@ } absl::optional<ui::OwnedWindowAnchor> -DesktopWindowTreeHostPlatform::GetOwnedWindowAnchorAndRectInDIP() { - const auto* anchor = +DesktopWindowTreeHostPlatform::GetOwnedWindowAnchorAndRectInPx() { + auto* anchor = GetContentWindow()->GetProperty(aura::client::kOwnedWindowAnchor); if (!anchor) return absl::nullopt; - return *anchor; + // Make a copy of the structure. Otherwise, conversion will result in + // overriding the stored property's value. + ui::OwnedWindowAnchor window_anchor = *anchor; + // Anchor rect must be translated from DIP to px. + window_anchor.anchor_rect = ToPixelRect(window_anchor.anchor_rect); + return window_anchor; } gfx::Rect DesktopWindowTreeHostPlatform::ConvertRectToPixels(
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.h b/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.h index cbc3e81..cbf0abe 100644 --- a/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.h +++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.h
@@ -151,7 +151,7 @@ absl::optional<gfx::Size> GetMaximumSizeForWindow() override; SkPath GetWindowMaskForWindowShapeInPixels() override; absl::optional<ui::MenuType> GetMenuType() override; - absl::optional<ui::OwnedWindowAnchor> GetOwnedWindowAnchorAndRectInDIP() + absl::optional<ui::OwnedWindowAnchor> GetOwnedWindowAnchorAndRectInPx() override; gfx::Rect ConvertRectToPixels(const gfx::Rect& rect_in_dip) const override; gfx::Rect ConvertRectToDIP(const gfx::Rect& rect_in_pixels) const override;
diff --git a/ui/views/widget/native_widget_mac_unittest.mm b/ui/views/widget/native_widget_mac_unittest.mm index 7eab8980..32d9648 100644 --- a/ui/views/widget/native_widget_mac_unittest.mm +++ b/ui/views/widget/native_widget_mac_unittest.mm
@@ -2082,6 +2082,31 @@ parent->CloseNow(); } +// Tests that tooltip widgets get the correct accessibilty role so that they're +// not announced as windows by VoiceOver. +TEST_F(NativeWidgetMacTest, AccessibilityRole) { + { + NativeWidgetMacTestWindow* window; + + Widget::InitParams init_params = + CreateParams(Widget::InitParams::TYPE_WINDOW); + Widget* widget = + CreateWidgetWithTestWindow(std::move(init_params), &window); + ASSERT_EQ([window accessibilityRole], NSAccessibilityWindowRole); + widget->CloseNow(); + } + { + NativeWidgetMacTestWindow* window; + + Widget::InitParams init_params = + CreateParams(Widget::InitParams::TYPE_TOOLTIP); + Widget* widget = + CreateWidgetWithTestWindow(std::move(init_params), &window); + ASSERT_EQ([window accessibilityRole], NSAccessibilityHelpTagRole); + widget->CloseNow(); + } +} + // Test that updateFullKeyboardAccess method on BridgedContentView correctly // sets the keyboard accessibility mode on the associated focus manager. TEST_F(NativeWidgetMacFullKeyboardAccessTest, FullKeyboardToggle) {
diff --git a/ui/views/widget/widget_interactive_uitest.cc b/ui/views/widget/widget_interactive_uitest.cc index 04791dca..f20021d 100644 --- a/ui/views/widget/widget_interactive_uitest.cc +++ b/ui/views/widget/widget_interactive_uitest.cc
@@ -42,6 +42,7 @@ #include "ui/views/test/widget_test.h" #include "ui/views/touchui/touch_selection_controller_impl.h" #include "ui/views/widget/root_view.h" +#include "ui/views/widget/unique_widget_ptr.h" #include "ui/views/widget/widget.h" #include "ui/views/widget/widget_utils.h" #include "ui/views/window/dialog_delegate.h" @@ -62,6 +63,25 @@ namespace { +template <class T> +class UniqueWidgetPtrT : public views::UniqueWidgetPtr { + public: + UniqueWidgetPtrT() = default; + UniqueWidgetPtrT(std::unique_ptr<T> widget) // NOLINT + : views::UniqueWidgetPtr(std::move(widget)) {} + UniqueWidgetPtrT(UniqueWidgetPtrT&&) = default; + UniqueWidgetPtrT& operator=(UniqueWidgetPtrT&&) = default; + ~UniqueWidgetPtrT() = default; + + T& operator*() const { + return static_cast<T&>(views::UniqueWidgetPtr::operator*()); + } + T* operator->() const { + return static_cast<T*>(views::UniqueWidgetPtr::operator->()); + } + T* get() const { return static_cast<T*>(views::UniqueWidgetPtr::get()); } +}; + // A View that closes the Widget and exits the current message-loop when it // receives a mouse-release event. class ExitLoopOnRelease : public View { @@ -517,9 +537,9 @@ // Test view focus restoration when a widget is deactivated and re-activated. TEST_F(WidgetTestInteractive, ViewFocusOnWidgetActivationChanges) { WidgetAutoclosePtr widget1(CreateTopLevelPlatformWidget()); - View* view1 = new View; + View* view1 = + widget1->GetContentsView()->AddChildView(std::make_unique<View>()); view1->SetFocusBehavior(View::FocusBehavior::ALWAYS); - widget1->GetContentsView()->AddChildView(view1); WidgetAutoclosePtr widget2(CreateTopLevelPlatformWidget()); View* view2a = new View; @@ -765,33 +785,33 @@ // Tests whether the widget only becomes active when the underlying window // is really active. TEST_F(WidgetTestInteractive, WidgetNotActivatedOnFakeActivationMessages) { - WidgetActivationTest widget1; + UniqueWidgetPtrT widget1 = std::make_unique<WidgetActivationTest>(); Widget::InitParams init_params = CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS); - init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; - init_params.native_widget = new DesktopNativeWidgetAura(&widget1); + init_params.native_widget = new DesktopNativeWidgetAura(widget1.get()); init_params.bounds = gfx::Rect(0, 0, 200, 200); - widget1.Init(std::move(init_params)); - widget1.Show(); - EXPECT_EQ(true, widget1.active()); + widget1->Init(std::move(init_params)); + widget1->Show(); + EXPECT_EQ(true, widget1->active()); - WidgetActivationTest widget2; - init_params.native_widget = new DesktopNativeWidgetAura(&widget2); - widget2.Init(std::move(init_params)); - widget2.Show(); - EXPECT_EQ(true, widget2.active()); - EXPECT_EQ(false, widget1.active()); + UniqueWidgetPtrT widget2 = std::make_unique<WidgetActivationTest>(); + init_params = CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS); + init_params.native_widget = new DesktopNativeWidgetAura(widget2.get()); + widget2->Init(std::move(init_params)); + widget2->Show(); + EXPECT_EQ(true, widget2->active()); + EXPECT_EQ(false, widget1->active()); - HWND win32_native_window1 = HWNDForWidget(&widget1); + HWND win32_native_window1 = HWNDForWidget(widget1.get()); EXPECT_TRUE(::IsWindow(win32_native_window1)); ::SendMessage(win32_native_window1, WM_NCACTIVATE, 1, 0); - EXPECT_EQ(false, widget1.active()); - EXPECT_EQ(true, widget2.active()); + EXPECT_EQ(false, widget1->active()); + EXPECT_EQ(true, widget2->active()); ::SetActiveWindow(win32_native_window1); - EXPECT_EQ(true, widget1.active()); - EXPECT_EQ(false, widget2.active()); + EXPECT_EQ(true, widget1->active()); + EXPECT_EQ(false, widget2->active()); } // On Windows if we create a fullscreen window on a thread, then it affects the @@ -799,42 +819,41 @@ // this we reduce the bounds of a fullscreen window by 1px when it loses // activation. This test verifies the same. TEST_F(WidgetTestInteractive, FullscreenBoundsReducedOnActivationLoss) { - Widget widget1; + UniqueWidgetPtr widget1 = std::make_unique<Widget>(); Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW); - params.native_widget = new DesktopNativeWidgetAura(&widget1); - params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; - widget1.Init(std::move(params)); - widget1.SetBounds(gfx::Rect(0, 0, 200, 200)); - widget1.Show(); + params.native_widget = new DesktopNativeWidgetAura(widget1.get()); + widget1->Init(std::move(params)); + widget1->SetBounds(gfx::Rect(0, 0, 200, 200)); + widget1->Show(); - widget1.Activate(); + widget1->Activate(); RunPendingMessages(); EXPECT_EQ(::GetActiveWindow(), - widget1.GetNativeWindow()->GetHost()->GetAcceleratedWidget()); + widget1->GetNativeWindow()->GetHost()->GetAcceleratedWidget()); - widget1.SetFullscreen(true); - EXPECT_TRUE(widget1.IsFullscreen()); + widget1->SetFullscreen(true); + EXPECT_TRUE(widget1->IsFullscreen()); // Ensure that the StopIgnoringPosChanges task in HWNDMessageHandler runs. // This task is queued when a widget becomes fullscreen. RunPendingMessages(); EXPECT_EQ(::GetActiveWindow(), - widget1.GetNativeWindow()->GetHost()->GetAcceleratedWidget()); - gfx::Rect fullscreen_bounds = widget1.GetWindowBoundsInScreen(); + widget1->GetNativeWindow()->GetHost()->GetAcceleratedWidget()); + gfx::Rect fullscreen_bounds = widget1->GetWindowBoundsInScreen(); - Widget widget2; - params.native_widget = new DesktopNativeWidgetAura(&widget2); - params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; - widget2.Init(std::move(params)); - widget2.SetBounds(gfx::Rect(0, 0, 200, 200)); - widget2.Show(); + UniqueWidgetPtr widget2 = std::make_unique<Widget>(); + params = CreateParams(Widget::InitParams::TYPE_WINDOW); + params.native_widget = new DesktopNativeWidgetAura(widget2.get()); + widget2->Init(std::move(params)); + widget2->SetBounds(gfx::Rect(0, 0, 200, 200)); + widget2->Show(); - widget2.Activate(); + widget2->Activate(); RunPendingMessages(); EXPECT_EQ(::GetActiveWindow(), - widget2.GetNativeWindow()->GetHost()->GetAcceleratedWidget()); + widget2->GetNativeWindow()->GetHost()->GetAcceleratedWidget()); gfx::Rect fullscreen_bounds_after_activation_loss = - widget1.GetWindowBoundsInScreen(); + widget1->GetWindowBoundsInScreen(); // After deactivation loss the bounds of the fullscreen widget should be // reduced by 1px. @@ -842,44 +861,40 @@ fullscreen_bounds_after_activation_loss.height(), 1); - widget1.Activate(); + widget1->Activate(); RunPendingMessages(); EXPECT_EQ(::GetActiveWindow(), - widget1.GetNativeWindow()->GetHost()->GetAcceleratedWidget()); + widget1->GetNativeWindow()->GetHost()->GetAcceleratedWidget()); gfx::Rect fullscreen_bounds_after_activate = - widget1.GetWindowBoundsInScreen(); + widget1->GetWindowBoundsInScreen(); // After activation the bounds of the fullscreen widget should be restored. EXPECT_EQ(fullscreen_bounds, fullscreen_bounds_after_activate); - - widget1.CloseNow(); - widget2.CloseNow(); } // Ensure the window rect and client rects are correct with a window that was // maximized. TEST_F(WidgetTestInteractive, FullscreenMaximizedWindowBounds) { - Widget widget; + UniqueWidgetPtr widget = std::make_unique<Widget>(); Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW); - params.native_widget = new DesktopNativeWidgetAura(&widget); - params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; - widget.set_frame_type(Widget::FrameType::kForceCustom); - widget.Init(std::move(params)); - widget.SetBounds(gfx::Rect(0, 0, 200, 200)); - widget.Show(); + params.native_widget = new DesktopNativeWidgetAura(widget.get()); + widget->set_frame_type(Widget::FrameType::kForceCustom); + widget->Init(std::move(params)); + widget->SetBounds(gfx::Rect(0, 0, 200, 200)); + widget->Show(); - widget.Maximize(); - EXPECT_TRUE(widget.IsMaximized()); + widget->Maximize(); + EXPECT_TRUE(widget->IsMaximized()); - widget.SetFullscreen(true); - EXPECT_TRUE(widget.IsFullscreen()); - EXPECT_FALSE(widget.IsMaximized()); + widget->SetFullscreen(true); + EXPECT_TRUE(widget->IsFullscreen()); + EXPECT_FALSE(widget->IsMaximized()); // Ensure that the StopIgnoringPosChanges task in HWNDMessageHandler runs. // This task is queued when a widget becomes fullscreen. RunPendingMessages(); - aura::WindowTreeHost* host = widget.GetNativeWindow()->GetHost(); + aura::WindowTreeHost* host = widget->GetNativeWindow()->GetHost(); HWND hwnd = host->GetAcceleratedWidget(); HMONITOR monitor = ::MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST); @@ -889,22 +904,20 @@ ASSERT_TRUE(::GetMonitorInfo(monitor, &monitor_info)); gfx::Rect monitor_bounds(monitor_info.rcMonitor); - gfx::Rect window_bounds = widget.GetWindowBoundsInScreen(); + gfx::Rect window_bounds = widget->GetWindowBoundsInScreen(); gfx::Rect client_area_bounds = host->GetBoundsInPixels(); EXPECT_EQ(window_bounds, monitor_bounds); EXPECT_EQ(monitor_bounds, client_area_bounds); // Setting not fullscreen should return it to maximized. - widget.SetFullscreen(false); - EXPECT_FALSE(widget.IsFullscreen()); - EXPECT_TRUE(widget.IsMaximized()); + widget->SetFullscreen(false); + EXPECT_FALSE(widget->IsFullscreen()); + EXPECT_TRUE(widget->IsMaximized()); client_area_bounds = host->GetBoundsInPixels(); EXPECT_TRUE(monitor_bounds.Contains(client_area_bounds)); EXPECT_NE(monitor_bounds, client_area_bounds); - - widget.CloseNow(); } #endif // BUILDFLAG(IS_WIN) @@ -918,17 +931,16 @@ focus_listener.focus_changes(); // Create a top level widget. - Widget top_level_widget; + UniqueWidgetPtr top_level_widget = std::make_unique<Widget>(); Widget::InitParams init_params = CreateParams(Widget::InitParams::TYPE_WINDOW); init_params.show_state = ui::SHOW_STATE_NORMAL; gfx::Rect initial_bounds(0, 0, 500, 500); init_params.bounds = initial_bounds; - init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; - top_level_widget.Init(std::move(init_params)); - ShowSync(&top_level_widget); + top_level_widget->Init(std::move(init_params)); + ShowSync(top_level_widget.get()); - gfx::NativeView top_level_native_view = top_level_widget.GetNativeView(); + gfx::NativeView top_level_native_view = top_level_widget->GetNativeView(); ASSERT_FALSE(focus_listener.focus_changes().empty()); EXPECT_EQ(1u, focus_changes.size()); EXPECT_EQ(top_level_native_view, focus_changes[0]); @@ -938,7 +950,7 @@ dialog_delegate->SetModalType(ui::MODAL_TYPE_WINDOW); Widget* modal_dialog_widget = views::DialogDelegate::CreateDialogWidget( - dialog_delegate.release(), nullptr, top_level_widget.GetNativeView()); + dialog_delegate.release(), nullptr, top_level_widget->GetNativeView()); modal_dialog_widget->SetBounds(gfx::Rect(100, 100, 200, 200)); // Note the dialog widget doesn't need a ShowSync. Since it is modal, it gains @@ -953,33 +965,34 @@ #if BUILDFLAG(IS_MAC) // Window modal dialogs on Mac are "sheets", which animate to close before // activating their parent widget. - views::test::WidgetActivationWaiter waiter(&top_level_widget, true); + views::test::WidgetActivationWaiter waiter(top_level_widget.get(), true); modal_dialog_widget->Close(); waiter.Wait(); #else - modal_dialog_widget->CloseNow(); + views::test::WidgetDestroyedWaiter waiter(modal_dialog_widget); + modal_dialog_widget->Close(); + waiter.Wait(); #endif ASSERT_EQ(5u, focus_changes.size()); EXPECT_EQ(gfx::kNullNativeView, focus_changes[3]); EXPECT_EQ(top_level_native_view, focus_changes[4]); - top_level_widget.CloseNow(); + top_level_widget->Close(); WidgetFocusManager::GetInstance()->RemoveFocusChangeListener(&focus_listener); } #endif TEST_F(DesktopWidgetTestInteractive, CanActivateFlagIsHonored) { - Widget widget; + UniqueWidgetPtr widget = std::make_unique<Widget>(); Widget::InitParams init_params = CreateParams(Widget::InitParams::TYPE_WINDOW); init_params.bounds = gfx::Rect(0, 0, 200, 200); - init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; init_params.activatable = Widget::InitParams::Activatable::kNo; - widget.Init(std::move(init_params)); + widget->Init(std::move(init_params)); - widget.Show(); - EXPECT_FALSE(widget.IsActive()); + widget->Show(); + EXPECT_FALSE(widget->IsActive()); } #if defined(USE_AURA) @@ -1022,41 +1035,37 @@ #endif // !BUILDFLAG(IS_WIN) // Create first widget and view, activate the widget, and focus the view. - Widget widget1; + UniqueWidgetPtr widget1 = std::make_unique<Widget>(); Widget::InitParams params1 = CreateParams(Widget::InitParams::TYPE_POPUP); - params1.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; params1.activatable = Widget::InitParams::Activatable::kYes; - widget1.Init(std::move(params1)); + widget1->Init(std::move(params1)); - View* view1 = new View(); + View* view1 = widget1->GetRootView()->AddChildView(std::make_unique<View>()); view1->SetFocusBehavior(View::FocusBehavior::ALWAYS); - widget1.GetRootView()->AddChildView(view1); - widget1.Show(); - ActivateSync(&widget1); + widget1->Show(); + ActivateSync(widget1.get()); - FocusManager* focus_manager1 = widget1.GetFocusManager(); + FocusManager* focus_manager1 = widget1->GetFocusManager(); ASSERT_TRUE(focus_manager1); focus_manager1->SetFocusedView(view1); EXPECT_EQ(view1, focus_manager1->GetFocusedView()); // Create second widget and view, activate the widget, and focus the view. - Widget widget2; + UniqueWidgetPtr widget2 = std::make_unique<Widget>(); Widget::InitParams params2 = CreateParams(Widget::InitParams::TYPE_POPUP); - params2.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; params2.activatable = Widget::InitParams::Activatable::kYes; - widget2.Init(std::move(params2)); + widget2->Init(std::move(params2)); - View* view2 = new View(); + View* view2 = widget2->GetRootView()->AddChildView(std::make_unique<View>()); view2->SetFocusBehavior(View::FocusBehavior::ALWAYS); - widget2.GetRootView()->AddChildView(view2); - widget2.Show(); - ActivateSync(&widget2); - EXPECT_TRUE(widget2.IsActive()); - EXPECT_FALSE(widget1.IsActive()); + widget2->Show(); + ActivateSync(widget2.get()); + EXPECT_TRUE(widget2->IsActive()); + EXPECT_FALSE(widget1->IsActive()); - FocusManager* focus_manager2 = widget2.GetFocusManager(); + FocusManager* focus_manager2 = widget2->GetFocusManager(); ASSERT_TRUE(focus_manager2); focus_manager2->SetFocusedView(view2); EXPECT_EQ(view2, focus_manager2->GetFocusedView()); @@ -1065,8 +1074,8 @@ // activated. view1->SetEnabled(false); EXPECT_NE(view1, focus_manager1->GetFocusedView()); - EXPECT_FALSE(widget1.IsActive()); - EXPECT_TRUE(widget2.IsActive()); + EXPECT_FALSE(widget1->IsActive()); + EXPECT_TRUE(widget2->IsActive()); } TEST_F(WidgetTestInteractive, ShowCreatesActiveWindow) { @@ -1140,13 +1149,12 @@ #if BUILDFLAG(ENABLE_DESKTOP_AURA) || BUILDFLAG(IS_MAC) TEST_F(WidgetTestInteractive, InactiveWidgetDoesNotGrabActivation) { - WidgetAutoclosePtr widget(CreateTopLevelPlatformWidget()); + UniqueWidgetPtr widget = base::WrapUnique(CreateTopLevelPlatformWidget()); ShowSync(widget.get()); EXPECT_EQ(GetWidgetShowState(widget.get()), ui::SHOW_STATE_NORMAL); - WidgetAutoclosePtr widget2(new Widget()); + UniqueWidgetPtr widget2 = std::make_unique<Widget>(); Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); - params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; widget2->Init(std::move(params)); widget2->Show(); RunPendingMessagesForActiveStatusChange(); @@ -1459,36 +1467,36 @@ // Verifies Widget::SetCapture() results in updating native capture along with // invoking the right Widget function. void TestCapture(bool use_desktop_native_widget) { - CaptureLostState capture_state1; - CaptureLostTrackingWidget widget1(&capture_state1); - InitPlatformWidget(&widget1, use_desktop_native_widget); - widget1.Show(); + UniqueWidgetPtrT widget1 = + std::make_unique<CaptureLostTrackingWidget>(capture_state1_.get()); + InitPlatformWidget(widget1.get(), use_desktop_native_widget); + widget1->Show(); - CaptureLostState capture_state2; - CaptureLostTrackingWidget widget2(&capture_state2); - InitPlatformWidget(&widget2, use_desktop_native_widget); - widget2.Show(); + UniqueWidgetPtrT widget2 = + std::make_unique<CaptureLostTrackingWidget>(capture_state2_.get()); + InitPlatformWidget(widget2.get(), use_desktop_native_widget); + widget2->Show(); // Set capture to widget2 and verity it gets it. - widget2.SetCapture(widget2.GetRootView()); - EXPECT_FALSE(widget1.HasCapture()); - EXPECT_TRUE(widget2.HasCapture()); - EXPECT_FALSE(capture_state1.GetAndClearGotCaptureLost()); - EXPECT_FALSE(capture_state2.GetAndClearGotCaptureLost()); + widget2->SetCapture(widget2->GetRootView()); + EXPECT_FALSE(widget1->HasCapture()); + EXPECT_TRUE(widget2->HasCapture()); + EXPECT_FALSE(capture_state1_->GetAndClearGotCaptureLost()); + EXPECT_FALSE(capture_state2_->GetAndClearGotCaptureLost()); // Set capture to widget1 and verify it gets it. - widget1.SetCapture(widget1.GetRootView()); - EXPECT_TRUE(widget1.HasCapture()); - EXPECT_FALSE(widget2.HasCapture()); - EXPECT_FALSE(capture_state1.GetAndClearGotCaptureLost()); - EXPECT_TRUE(capture_state2.GetAndClearGotCaptureLost()); + widget1->SetCapture(widget1->GetRootView()); + EXPECT_TRUE(widget1->HasCapture()); + EXPECT_FALSE(widget2->HasCapture()); + EXPECT_FALSE(capture_state1_->GetAndClearGotCaptureLost()); + EXPECT_TRUE(capture_state2_->GetAndClearGotCaptureLost()); // Release and verify no one has it. - widget1.ReleaseCapture(); - EXPECT_FALSE(widget1.HasCapture()); - EXPECT_FALSE(widget2.HasCapture()); - EXPECT_TRUE(capture_state1.GetAndClearGotCaptureLost()); - EXPECT_FALSE(capture_state2.GetAndClearGotCaptureLost()); + widget1->ReleaseCapture(); + EXPECT_FALSE(widget1->HasCapture()); + EXPECT_FALSE(widget2->HasCapture()); + EXPECT_TRUE(capture_state1_->GetAndClearGotCaptureLost()); + EXPECT_FALSE(capture_state2_->GetAndClearGotCaptureLost()); } void InitPlatformWidget(Widget* widget, bool use_desktop_native_widget) { @@ -1499,9 +1507,25 @@ use_desktop_native_widget ? nullptr : CreatePlatformNativeWidgetImpl(widget, kDefault, nullptr); - params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; widget->Init(std::move(params)); } + + protected: + void SetUp() override { + DesktopWidgetTestInteractive::SetUp(); + capture_state1_ = std::make_unique<CaptureLostState>(); + capture_state2_ = std::make_unique<CaptureLostState>(); + } + + void TearDown() override { + capture_state1_.reset(); + capture_state2_.reset(); + DesktopWidgetTestInteractive::TearDown(); + } + + private: + std::unique_ptr<CaptureLostState> capture_state1_; + std::unique_ptr<CaptureLostState> capture_state2_; }; // See description in TestCapture(). @@ -1549,6 +1573,7 @@ EXPECT_TRUE(capture_state.GetAndClearGotCaptureLost()); } +// TODO(kylixrd): Remove this test once Widget ownership is normalized. TEST_F(WidgetCaptureTest, DestroyWithCapture_WidgetOwnsNativeWidget) { Widget widget; Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW); @@ -1562,32 +1587,31 @@ // Test that no state is set if capture fails. TEST_F(WidgetCaptureTest, FailedCaptureRequestIsNoop) { - Widget widget; + UniqueWidgetPtr widget = std::make_unique<Widget>(); Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS); - params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; params.bounds = gfx::Rect(400, 400); - widget.Init(std::move(params)); + widget->Init(std::move(params)); - MouseView* mouse_view1 = new MouseView; - MouseView* mouse_view2 = new MouseView; auto contents_view = std::make_unique<View>(); - contents_view->AddChildView(mouse_view1); - contents_view->AddChildView(mouse_view2); - widget.SetContentsView(std::move(contents_view)); + MouseView* mouse_view1 = + contents_view->AddChildView(std::make_unique<MouseView>()); + MouseView* mouse_view2 = + contents_view->AddChildView(std::make_unique<MouseView>()); + widget->SetContentsView(std::move(contents_view)); mouse_view1->SetBounds(0, 0, 200, 400); mouse_view2->SetBounds(200, 0, 200, 400); // Setting capture should fail because |widget| is not visible. - widget.SetCapture(mouse_view1); - EXPECT_FALSE(widget.HasCapture()); + widget->SetCapture(mouse_view1); + EXPECT_FALSE(widget->HasCapture()); - widget.Show(); - ui::test::EventGenerator generator(GetRootWindow(&widget), - widget.GetNativeWindow()); + widget->Show(); + ui::test::EventGenerator generator(GetRootWindow(widget.get()), + widget->GetNativeWindow()); generator.set_current_screen_location( - widget.GetClientAreaBoundsInScreen().CenterPoint()); + widget->GetClientAreaBoundsInScreen().CenterPoint()); generator.PressLeftButton(); EXPECT_FALSE(mouse_view1->pressed()); @@ -1799,37 +1823,33 @@ WidgetFocusManager::GetInstance()->AddFocusChangeListener(&focus_listener); // Create a top level widget. - Widget top_level_widget; + UniqueWidgetPtr top_level_widget = std::make_unique<Widget>(); Widget::InitParams init_params = CreateParams(Widget::InitParams::TYPE_WINDOW); init_params.show_state = ui::SHOW_STATE_NORMAL; gfx::Rect initial_bounds(0, 0, 500, 500); init_params.bounds = initial_bounds; - init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; - top_level_widget.Init(std::move(init_params)); - ShowSync(&top_level_widget); + top_level_widget->Init(std::move(init_params)); + ShowSync(top_level_widget.get()); ASSERT_FALSE(focus_listener.focus_changes().empty()); - EXPECT_EQ(top_level_widget.GetNativeView(), + EXPECT_EQ(top_level_widget->GetNativeView(), focus_listener.focus_changes().back()); - EXPECT_FALSE(top_level_widget.HasCapture()); - top_level_widget.SetCapture(nullptr); - EXPECT_TRUE(top_level_widget.HasCapture()); + EXPECT_FALSE(top_level_widget->HasCapture()); + top_level_widget->SetCapture(nullptr); + EXPECT_TRUE(top_level_widget->HasCapture()); // Create a modal dialog. auto dialog_delegate = std::make_unique<DialogDelegateView>(); dialog_delegate->SetModalType(ui::MODAL_TYPE_SYSTEM); Widget* modal_dialog_widget = views::DialogDelegate::CreateDialogWidget( - dialog_delegate.release(), nullptr, top_level_widget.GetNativeView()); + dialog_delegate.release(), nullptr, top_level_widget->GetNativeView()); modal_dialog_widget->SetBounds(gfx::Rect(100, 100, 200, 200)); ShowSync(modal_dialog_widget); - EXPECT_FALSE(top_level_widget.HasCapture()); - - modal_dialog_widget->CloseNow(); - top_level_widget.CloseNow(); + EXPECT_FALSE(top_level_widget->HasCapture()); WidgetFocusManager::GetInstance()->RemoveFocusChangeListener(&focus_listener); } @@ -1846,32 +1866,30 @@ // mouse events when a different widget grabs capture. Except for Windows, // which does not send a synthetic mouse exit. TEST_F(WidgetCaptureTest, MAYBE_MouseExitOnCaptureGrab) { - Widget widget1; + UniqueWidgetPtr widget1 = std::make_unique<Widget>(); Widget::InitParams params1 = CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS); - params1.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; - widget1.Init(std::move(params1)); + widget1->Init(std::move(params1)); MouseView* mouse_view1 = - widget1.SetContentsView(std::make_unique<MouseView>()); - widget1.Show(); - widget1.SetBounds(gfx::Rect(300, 300)); + widget1->SetContentsView(std::make_unique<MouseView>()); + widget1->Show(); + widget1->SetBounds(gfx::Rect(300, 300)); - Widget widget2; + UniqueWidgetPtr widget2 = std::make_unique<Widget>(); Widget::InitParams params2 = CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS); - params2.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; - widget2.Init(std::move(params2)); - widget2.Show(); - widget2.SetBounds(gfx::Rect(400, 0, 300, 300)); + widget2->Init(std::move(params2)); + widget2->Show(); + widget2->SetBounds(gfx::Rect(400, 0, 300, 300)); - ui::test::EventGenerator generator(GetRootWindow(&widget1)); + ui::test::EventGenerator generator(GetRootWindow(widget1.get())); generator.set_current_screen_location(gfx::Point(100, 100)); generator.MoveMouseBy(0, 0); EXPECT_EQ(1, mouse_view1->EnteredCalls()); EXPECT_EQ(0, mouse_view1->ExitedCalls()); - widget2.SetCapture(nullptr); + widget2->SetCapture(nullptr); EXPECT_EQ(0, mouse_view1->EnteredCalls()); // On Windows, Chrome doesn't synthesize a separate mouse exited event. // Instead, it uses ::TrackMouseEvent to get notified of the mouse leaving. @@ -1916,18 +1934,17 @@ // Test that setting capture on widget activation of a non-toplevel widget // (e.g. a bubble on Linux) succeeds. TEST_F(WidgetCaptureTest, SetCaptureToNonToplevel) { - Widget toplevel; + UniqueWidgetPtr toplevel = std::make_unique<Widget>(); Widget::InitParams toplevel_params = CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS); - toplevel_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; - toplevel.Init(std::move(toplevel_params)); - toplevel.Show(); + toplevel->Init(std::move(toplevel_params)); + toplevel->Show(); - Widget* child = new Widget; + UniqueWidgetPtr child = std::make_unique<Widget>(); Widget::InitParams child_params = CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS); - child_params.parent = toplevel.GetNativeView(); - child_params.context = toplevel.GetNativeWindow(); + child_params.parent = toplevel->GetNativeView(); + child_params.context = toplevel->GetNativeWindow(); child->Init(std::move(child_params)); CaptureOnActivationObserver observer; @@ -1983,39 +2000,37 @@ // on Windows that it is correctly processed by the widget that doesn't have // capture. This behavior is not desired on OSes other than Windows. TEST_F(WidgetCaptureTest, MouseEventDispatchedToRightWindow) { - MouseEventTrackingWidget widget1; + UniqueWidgetPtrT widget1 = std::make_unique<MouseEventTrackingWidget>(); Widget::InitParams params1 = CreateParams(views::Widget::InitParams::TYPE_WINDOW); - params1.native_widget = new DesktopNativeWidgetAura(&widget1); - params1.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; - widget1.Init(std::move(params1)); - widget1.Show(); + params1.native_widget = new DesktopNativeWidgetAura(widget1.get()); + widget1->Init(std::move(params1)); + widget1->Show(); - MouseEventTrackingWidget widget2; + UniqueWidgetPtrT widget2 = std::make_unique<MouseEventTrackingWidget>(); Widget::InitParams params2 = CreateParams(views::Widget::InitParams::TYPE_WINDOW); - params2.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; - params2.native_widget = new DesktopNativeWidgetAura(&widget2); - widget2.Init(std::move(params2)); - widget2.Show(); + params2.native_widget = new DesktopNativeWidgetAura(widget2.get()); + widget2->Init(std::move(params2)); + widget2->Show(); // Set capture to widget2 and verity it gets it. - widget2.SetCapture(widget2.GetRootView()); - EXPECT_FALSE(widget1.HasCapture()); - EXPECT_TRUE(widget2.HasCapture()); + widget2->SetCapture(widget2->GetRootView()); + EXPECT_FALSE(widget1->HasCapture()); + EXPECT_TRUE(widget2->HasCapture()); - widget1.GetAndClearGotMouseEvent(); - widget2.GetAndClearGotMouseEvent(); + widget1->GetAndClearGotMouseEvent(); + widget2->GetAndClearGotMouseEvent(); // Send a mouse event to the RootWindow associated with |widget1|. Even though // |widget2| has capture, |widget1| should still get the event. ui::MouseEvent mouse_event(ui::ET_MOUSE_EXITED, gfx::Point(), gfx::Point(), ui::EventTimeForNow(), ui::EF_NONE, ui::EF_NONE); ui::EventDispatchDetails details = - widget1.GetNativeWindow()->GetHost()->GetEventSink()->OnEventFromSource( + widget1->GetNativeWindow()->GetHost()->GetEventSink()->OnEventFromSource( &mouse_event); ASSERT_FALSE(details.dispatcher_destroyed); - EXPECT_TRUE(widget1.GetAndClearGotMouseEvent()); - EXPECT_FALSE(widget2.GetAndClearGotMouseEvent()); + EXPECT_TRUE(widget1->GetAndClearGotMouseEvent()); + EXPECT_FALSE(widget2->GetAndClearGotMouseEvent()); } #endif // BUILDFLAG(IS_WIN)
diff --git a/ui/webui/resources/BUILD.gn b/ui/webui/resources/BUILD.gn index 2b70d13..483defef 100644 --- a/ui/webui/resources/BUILD.gn +++ b/ui/webui/resources/BUILD.gn
@@ -232,8 +232,6 @@ "js/cr/event_target.m.js", "js/cr.m.js", "js/cr/ui.m.js", - "js/cr/ui/drag_wrapper.js", - "js/cr/ui/focus_grid.js", "js/cr/ui/focus_outline_manager.m.js", "js/cr/ui/focus_row.m.js", "js/cr/ui/keyboard_shortcut_list.m.js", @@ -334,6 +332,8 @@ "cr_elements/cr_tree/cr_tree_item.html.ts", "js/assert_ts.ts", "js/custom_element.ts", + "js/cr/ui/focus_grid.ts", + "js/cr/ui/drag_wrapper.ts", ] if (include_polymer) {
diff --git a/ui/webui/resources/js/cr/ui/BUILD.gn b/ui/webui/resources/js/cr/ui/BUILD.gn index fb79712..21226194 100644 --- a/ui/webui/resources/js/cr/ui/BUILD.gn +++ b/ui/webui/resources/js/cr/ui/BUILD.gn
@@ -28,6 +28,18 @@ public_deps = [ ":preprocess_generated", ":preprocess_src", + ":preprocess_src_ts", + ] +} + +# TS files are passed to a separate target so that they are not listed in the +# |out_manifest|. +preprocess_if_expr("preprocess_src_ts") { + in_folder = "." + out_folder = preprocess_folder + in_files = [ + "drag_wrapper.ts", + "focus_grid.ts", ] } @@ -35,11 +47,7 @@ in_folder = "./" out_folder = "$preprocess_folder" out_manifest = "$target_gen_dir/$preprocess_src_manifest" - in_files = [ - "drag_wrapper.js", - "focus_grid.js", - "store.js", - ] + in_files = [ "store.js" ] if (is_chromeos_ash) { in_files += [ @@ -304,8 +312,6 @@ js_type_check("ui_resources_modules") { is_polymer3 = true deps = [ - ":drag_wrapper", - ":focus_grid", ":focus_outline_manager.m", ":focus_row.m", ":focus_row_behavior.m", @@ -377,16 +383,6 @@ } } -js_library("drag_wrapper") { -} - -js_library("focus_grid") { - deps = [ - ":focus_row.m", - "../..:assert.m", - ] -} - js_library("focus_outline_manager.m") { sources = [ "$root_gen_dir/ui/webui/resources/js/cr/ui/focus_outline_manager.m.js" ]
diff --git a/ui/webui/resources/js/cr/ui/drag_wrapper.js b/ui/webui/resources/js/cr/ui/drag_wrapper.js deleted file mode 100644 index 3a586e37..0000000 --- a/ui/webui/resources/js/cr/ui/drag_wrapper.js +++ /dev/null
@@ -1,139 +0,0 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -/** - * @fileoverview DragWrapper - * A class for simplifying HTML5 drag and drop. Classes should use this to - * handle the details of nested drag enters and leaves. - */ -/** @interface */ -export class DragWrapperDelegate { - // TODO(devlin): The only method this "delegate" actually needs is - // shouldAcceptDrag(); the rest can be events emitted by the DragWrapper. - /** - * @param {MouseEvent} e The event for the drag. - * @return {boolean} Whether the drag should be accepted. If false, - * subsequent methods (doDrag*) will not be called. - */ - shouldAcceptDrag(e) {} - - /** @param {MouseEvent} e */ - doDragEnter(e) {} - - /** @param {MouseEvent} e */ - doDragLeave(e) {} - - /** @param {MouseEvent} e */ - doDragOver(e) {} - - /** @param {MouseEvent} e */ - doDrop(e) {} -} - - /** - * Creates a DragWrapper which listens for drag target events on |target| and - * delegates event handling to |delegate|. - */ -export class DragWrapper { - /** - * @param {!Element} target - * @param {!DragWrapperDelegate} delegate - */ - constructor(target, delegate) { - /** - * The number of un-paired dragenter events that have fired on |this|. - * This is incremented by |onDragEnter_| and decremented by - * |onDragLeave_|. This is necessary because dragging over child widgets - * will fire additional enter and leave events on |this|. A non-zero value - * does not necessarily indicate that |isCurrentDragTarget()| is true. - * @private {number} - */ - this.dragEnters_ = 0; - - /** @private {!Element} */ - this.target_ = target; - - /** @private {!DragWrapperDelegate} */ - this.delegate_ = delegate; - - target.addEventListener( - 'dragenter', e => this.onDragEnter_(/** @type {!MouseEvent} */ (e))); - target.addEventListener( - 'dragover', e => this.onDragOver_(/** @type {!MouseEvent} */ (e))); - target.addEventListener( - 'drop', e => this.onDrop_(/** @type {!MouseEvent} */ (e))); - target.addEventListener( - 'dragleave', e => this.onDragLeave_(/** @type {!MouseEvent} */ (e))); - } - - /** - * Whether the tile page is currently being dragged over with data it can - * accept. - * @return {boolean} - */ - get isCurrentDragTarget() { - return this.target_.classList.contains('drag-target'); - } - - /** - * Delegate for dragenter events fired on |target_|. - * @param {!MouseEvent} e A MouseEvent for the drag. - * @private - */ - onDragEnter_(e) { - if (++this.dragEnters_ === 1) { - if (this.delegate_.shouldAcceptDrag(e)) { - this.target_.classList.add('drag-target'); - this.delegate_.doDragEnter(e); - } - } else { - // Sometimes we'll get an enter event over a child element without an - // over event following it. In this case we have to still call the - // drag over delegate so that we make the necessary updates (one visible - // symptom of not doing this is that the cursor's drag state will - // flicker during drags). - this.onDragOver_(e); - } - } - - /** - * Thunk for dragover events fired on |target_|. - * @param {!MouseEvent} e A MouseEvent for the drag. - * @private - */ - onDragOver_(e) { - if (!this.target_.classList.contains('drag-target')) { - return; - } - this.delegate_.doDragOver(e); - } - - /** - * Thunk for drop events fired on |target_|. - * @param {!MouseEvent} e A MouseEvent for the drag. - * @private - */ - onDrop_(e) { - this.dragEnters_ = 0; - if (!this.target_.classList.contains('drag-target')) { - return; - } - this.target_.classList.remove('drag-target'); - this.delegate_.doDrop(e); - } - - /** - * Thunk for dragleave events fired on |target_|. - * @param {!MouseEvent} e A MouseEvent for the drag. - * @private - */ - onDragLeave_(e) { - if (--this.dragEnters_ > 0) { - return; - } - - this.target_.classList.remove('drag-target'); - this.delegate_.doDragLeave(e); - } -}
diff --git a/ui/webui/resources/js/cr/ui/drag_wrapper.ts b/ui/webui/resources/js/cr/ui/drag_wrapper.ts new file mode 100644 index 0000000..b6fd31d --- /dev/null +++ b/ui/webui/resources/js/cr/ui/drag_wrapper.ts
@@ -0,0 +1,114 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +/** + * @fileoverview DragWrapper + * A class for simplifying HTML5 drag and drop. Classes should use this to + * handle the details of nested drag enters and leaves. + */ +export interface DragWrapperDelegate { + // TODO(devlin): The only method this "delegate" actually needs is + // shouldAcceptDrag(); the rest can be events emitted by the DragWrapper. + /** + * @return Whether the drag should be accepted. If false, + * subsequent methods (doDrag*) will not be called. + */ + shouldAcceptDrag(e: MouseEvent): boolean; + + doDragEnter(e: MouseEvent): void; + + doDragLeave(e: MouseEvent): void; + + doDragOver(e: MouseEvent): void; + + doDrop(e: MouseEvent): void; +} + +/** + * Creates a DragWrapper which listens for drag target events on |target| and + * delegates event handling to |delegate|. + */ +export class DragWrapper { + /** + * The number of un-paired dragenter events that have fired on |this|. + * This is incremented by |onDragEnter_| and decremented by + * |onDragLeave_|. This is necessary because dragging over child widgets + * will fire additional enter and leave events on |this|. A non-zero value + * does not necessarily indicate that |isCurrentDragTarget()| is true. + */ + private dragEnters_: number = 0; + private target_: HTMLElement; + private delegate_: DragWrapperDelegate; + + constructor(target: HTMLElement, delegate: DragWrapperDelegate) { + this.target_ = target; + this.delegate_ = delegate; + + target.addEventListener('dragenter', e => this.onDragEnter_(e)); + target.addEventListener('dragover', e => this.onDragOver_(e)); + target.addEventListener('drop', e => this.onDrop_(e)); + target.addEventListener('dragleave', e => this.onDragLeave_(e)); + } + + /** + * Whether the tile page is currently being dragged over with data it can + * accept. + */ + get isCurrentDragTarget(): boolean { + return this.target_.classList.contains('drag-target'); + } + + /** + * Delegate for dragenter events fired on |target_|. + */ + private onDragEnter_(e: MouseEvent) { + if (++this.dragEnters_ === 1) { + if (this.delegate_.shouldAcceptDrag(e)) { + this.target_.classList.add('drag-target'); + this.delegate_.doDragEnter(e); + } + } else { + // Sometimes we'll get an enter event over a child element without an + // over event following it. In this case we have to still call the + // drag over delegate so that we make the necessary updates (one visible + // symptom of not doing this is that the cursor's drag state will + // flicker during drags). + this.onDragOver_(e); + } + } + + /** + * Thunk for dragover events fired on |target_|. + */ + private onDragOver_(e: MouseEvent) { + if (!this.target_.classList.contains('drag-target')) { + return; + } + this.delegate_.doDragOver(e); + } + + /** + * Thunk for drop events fired on |target_|. + */ + private onDrop_(e: MouseEvent) { + this.dragEnters_ = 0; + if (!this.target_.classList.contains('drag-target')) { + return; + } + this.target_.classList.remove('drag-target'); + this.delegate_.doDrop(e); + } + + /** + * Thunk for dragleave events fired on |target_|. + */ + private onDragLeave_(e: MouseEvent) { + if (--this.dragEnters_ > 0) { + return; + } + + this.target_.classList.remove('drag-target'); + this.delegate_.doDragLeave(e); + } +}
diff --git a/ui/webui/resources/js/cr/ui/focus_grid.js b/ui/webui/resources/js/cr/ui/focus_grid.ts similarity index 67% rename from ui/webui/resources/js/cr/ui/focus_grid.js rename to ui/webui/resources/js/cr/ui/focus_grid.ts index 770121d..a765f72 100644 --- a/ui/webui/resources/js/cr/ui/focus_grid.js +++ b/ui/webui/resources/js/cr/ui/focus_grid.ts
@@ -4,6 +4,7 @@ // clang-format off import {assert} from '../../assert.m.js'; + import {FocusRow, FocusRowDelegate} from './focus_row.m.js'; // clang-format on @@ -27,24 +28,13 @@ * focusable focusable focusable * focusable focusable [focused] (row: 1, col: 2) * focusable focusable focusable - * - * @implements {FocusRowDelegate} */ -export class FocusGrid { - constructor() { - /** @type {!Array<!FocusRow>} */ - this.rows = []; +export class FocusGrid implements FocusRowDelegate { + rows: FocusRow[] = []; + private ignoreFocusChange_: boolean = false; + private lastFocused_: EventTarget|null = null; - /** @private {boolean} */ - this.ignoreFocusChange_ = false; - - /** @private {?EventTarget} */ - this.lastFocused_ = null; - } - - // override - // Note: Not using @override because it breaks TypeScript. - onFocus(row, e) { + onFocus(row: FocusRow, e: Event) { if (this.ignoreFocusChange_) { this.ignoreFocusChange_ = false; } else { @@ -56,8 +46,7 @@ }); } - // override - onKeydown(row, e) { + onKeydown(row: FocusRow, e: KeyboardEvent) { const rowIndex = this.rows.indexOf(row); assert(rowIndex >= 0); @@ -76,10 +65,7 @@ const rowToFocus = this.rows[newRow]; if (rowToFocus) { this.ignoreFocusChange_ = true; - rowToFocus - .getEquivalentElement( - /** @type {!Element} */ (this.lastFocused_)) - .focus(); + rowToFocus.getEquivalentElement(this.lastFocused_ as HTMLElement).focus(); e.preventDefault(); return true; } @@ -87,8 +73,7 @@ return false; } - // override - getCustomEquivalent(sampleElement) { + getCustomEquivalent(_sampleElement: HTMLElement) { return null; } @@ -103,12 +88,12 @@ } /** - * @param {!Element} target A target item to find in this grid. - * @return {number} The row index. -1 if not found. + * @param target A target item to find in this grid. + * @return The row index. -1 if not found. */ - getRowIndexForTarget(target) { + getRowIndexForTarget(target: HTMLElement): number { for (let i = 0; i < this.rows.length; ++i) { - if (this.rows[i].getElements().indexOf(target) >= 0) { + if (this.rows[i]!.getElements().indexOf(target) >= 0) { return i; } } @@ -116,13 +101,13 @@ } /** - * @param {Element} root An element to search for. - * @return {?FocusRow} The row with root of |root| or null. + * @param root An element to search for. + * @return The row with root of |root| or null. */ - getRowForRoot(root) { + getRowForRoot(root: HTMLElement): FocusRow|null { for (let i = 0; i < this.rows.length; ++i) { - if (this.rows[i].root === root) { - return this.rows[i]; + if (this.rows[i]!.root === root) { + return this.rows[i]!; } } return null; @@ -130,19 +115,19 @@ /** * Adds |row| to the end of this list. - * @param {!FocusRow} row The row that needs to be added to this grid. + * @param row The row that needs to be added to this grid. */ - addRow(row) { + addRow(row: FocusRow) { this.addRowBefore(row, null); } /** * Adds |row| before |nextRow|. If |nextRow| is not in the list or it's * null, |row| is added to the end. - * @param {!FocusRow} row The row that needs to be added to this grid. - * @param {FocusRow} nextRow The row that should follow |row|. + * @param row The row that needs to be added to this grid. + * @param nextRow The row that should follow |row|. */ - addRowBefore(row, nextRow) { + addRowBefore(row: FocusRow, nextRow: FocusRow|null) { row.delegate = row.delegate || this; const nextRowIndex = nextRow ? this.rows.indexOf(nextRow) : -1; @@ -155,9 +140,9 @@ /** * Removes a row from the focus row. No-op if row is not in the grid. - * @param {FocusRow} row The row that needs to be removed. + * @param row The row that needs to be removed. */ - removeRow(row) { + removeRow(row: FocusRow|null) { const nextRowIndex = row ? this.rows.indexOf(row) : -1; if (nextRowIndex > -1) { this.rows.splice(nextRowIndex, 1); @@ -167,21 +152,21 @@ /** * Makes sure that at least one row is active. Should be called once, after * adding all rows to FocusGrid. - * @param {number=} preferredRow The row to select if no other row is + * @param preferredRow The row to select if no other row is * active. Selects the first item if this is beyond the range of the * grid. */ - ensureRowActive(preferredRow) { + ensureRowActive(preferredRow?: number) { if (this.rows.length === 0) { return; } for (let i = 0; i < this.rows.length; ++i) { - if (this.rows[i].isActive()) { + if (this.rows[i]!.isActive()) { return; } } - (this.rows[preferredRow || 0] || this.rows[0]).makeActive(true); + (this.rows[preferredRow || 0] || this.rows[0]!).makeActive(true); } }
diff --git a/ui/webui/resources/js/cr/ui/focus_row.js b/ui/webui/resources/js/cr/ui/focus_row.js index 213ceda..99abe3f8 100644 --- a/ui/webui/resources/js/cr/ui/focus_row.js +++ b/ui/webui/resources/js/cr/ui/focus_row.js
@@ -85,8 +85,8 @@ * that can gain focus is in a shadow DOM. Allowing an override via a * function leaves the details of how the element is retrieved to the * component. - * @param {!Element} element - * @return {!Element} + * @param {!HTMLElement} element + * @return {!HTMLElement} */ static getFocusableElement(element) { if (element.getFocusableElement) { @@ -141,16 +141,18 @@ } /** - * @param {!Element} sampleElement An element for to find an equivalent for. - * @return {!Element} An equivalent element to focus for |sampleElement|. + * @param {!HTMLElement} sampleElement An element for to find an equivalent + * for. + * @return {!HTMLElement} An equivalent element to focus for + * |sampleElement|. * @protected */ getCustomEquivalent(sampleElement) { - return /** @type {!Element} */ (assert(this.getFirstFocusable())); + return /** @type {!HTMLElement} */ (assert(this.getFirstFocusable())); } /** - * @return {!Array<!Element>} All registered elements (regardless of + * @return {!Array<!HTMLElement>} All registered elements (regardless of * focusability). */ getElements() { @@ -160,9 +162,9 @@ /** * Find the element that best matches |sampleElement|. - * @param {!Element} sampleElement An element from a row of the same type - * which previously held focus. - * @return {!Element} The element that best matches sampleElement. + * @param {!HTMLElement} sampleElement An element from a row of the same + * type which previously held focus. + * @return {!HTMLElement} The element that best matches sampleElement. */ getEquivalentElement(sampleElement) { if (this.getFocusableElements().indexOf(sampleElement) >= 0) { @@ -182,7 +184,7 @@ /** * @param {string=} opt_type An optional type to search for. - * @return {?Element} The first focusable element with |type|. + * @return {?HTMLElement} The first focusable element with |type|. */ getFirstFocusable(opt_type) { const element = this.getFocusableElements().find( @@ -190,7 +192,7 @@ return element || null; } - /** @return {!Array<!Element>} Registered, focusable elements. */ + /** @return {!Array<!HTMLElement>} Registered, focusable elements. */ getFocusableElements() { return this.getElements().filter(cr.ui.FocusRow.isFocusable); } @@ -234,7 +236,7 @@ return; } - const currentTarget = /** @type {!Element} */ (e.currentTarget); + const currentTarget = /** @type {!HTMLElement} */ (e.currentTarget); if (this.getFocusableElements().indexOf(currentTarget) >= 0) { this.makeActive(false); } @@ -273,7 +275,7 @@ onKeydown_(e) { const elements = this.getFocusableElements(); const currentElement = cr.ui.FocusRow.getFocusableElement( - /** @type {!Element} */ (e.currentTarget)); + /** @type {!HTMLElement} */ (e.currentTarget)); const elementIndex = elements.indexOf(currentElement); assert(elementIndex >= 0); @@ -343,8 +345,8 @@ onFocus(row, e) {} /** - * @param {!Element} sampleElement An element to find an equivalent for. - * @return {?Element} An equivalent element to focus, or null to use the + * @param {!HTMLElement} sampleElement An element to find an equivalent for. + * @return {?HTMLElement} An equivalent element to focus, or null to use the * default FocusRow element. */ getCustomEquivalent(sampleElement) {}
diff --git a/ui/webui/resources/js/cr/ui/focus_row_behavior.js b/ui/webui/resources/js/cr/ui/focus_row_behavior.js index 0ee9a91..be59eb9 100644 --- a/ui/webui/resources/js/cr/ui/focus_row_behavior.js +++ b/ui/webui/resources/js/cr/ui/focus_row_behavior.js
@@ -30,7 +30,7 @@ * @param {!Event} e */ onFocus(row, e) { - const element = /** @type {!Element} */ (e.composedPath()[0]); + const element = /** @type {!HTMLElement} */ (e.composedPath()[0]); const focusableElement = cr.ui.FocusRow.getFocusableElement(element); if (element !== focusableElement) { focusableElement.focus(); @@ -119,7 +119,7 @@ observer: 'focusRowIndexChanged', }, - /** @type {Element} */ + /** @type {HTMLElement} */ lastFocused: { type: Object, notify: true, @@ -394,7 +394,7 @@ /** @type {number} */ this.focusRowIndex; - /** @type {?Element} */ + /** @type {?HTMLElement} */ this.lastFocused; /** @type {number} */
diff --git a/weblayer/browser/overlay_popup_ad_intervention_browsertest.cc b/weblayer/browser/overlay_popup_ad_intervention_browsertest.cc index 0424fe8..4455d2ba 100644 --- a/weblayer/browser/overlay_popup_ad_intervention_browsertest.cc +++ b/weblayer/browser/overlay_popup_ad_intervention_browsertest.cc
@@ -134,8 +134,9 @@ base::test::ScopedFeatureList feature_list_; }; +// TODO(https://crbug.com/1344280): Test is flaky. IN_PROC_BROWSER_TEST_F(OverlayPopupAdViolationBrowserTestWithoutEnforcement, - OverlayPopupAd_NoAdInterventionTriggered) { + DISABLED_OverlayPopupAd_NoAdInterventionTriggered) { base::HistogramTester histogram_tester; GURL url = embedded_test_server()->GetURL(