diff --git a/DEPS b/DEPS index 8740f1f0..4468b0f 100644 --- a/DEPS +++ b/DEPS
@@ -207,7 +207,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': 'b09f16ee7aeb7f2063c29fdbe600644d0ffe07b8', + 'angle_revision': '1fd3e5d89a8f840f859fb9c379e58defe9c399ab', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling SwiftShader # and whatever else without interference from each other. @@ -258,7 +258,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': '6b79223314a55ddee356ff91f56dd7f676824e87', + 'catapult_revision': '8c88c750bd4516075f6879b4aed5e3bf22ef4424', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling libFuzzer # and whatever else without interference from each other. @@ -322,7 +322,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling feed # and whatever else without interference from each other. - 'quiche_revision': '3c3e8905ad7ef0dded8d6231087fcc6e3b6a1051', + 'quiche_revision': '89ec3381ca3daf5d6aa9b151a8da6bf03edbe3be', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling ios_webkit # and whatever else without interference from each other. @@ -896,12 +896,12 @@ # For Linux and Chromium OS. 'src/third_party/cros_system_api': { - 'url': Var('chromium_git') + '/chromiumos/platform2/system_api.git' + '@' + 'd5ad6a50d168252d431f99cd227e327a86c567ff', + 'url': Var('chromium_git') + '/chromiumos/platform2/system_api.git' + '@' + '5df72efed1de3c81f7590dbc09b240cb6edc4cd6', 'condition': 'checkout_linux', }, 'src/third_party/depot_tools': - Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'b677e985297d9c668afa697cb773b9eee5154ab0', + Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '69e30b23c45f22df585dc12f464479ca3deafb69', 'src/third_party/devtools-frontend/src': Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'), @@ -1332,7 +1332,7 @@ 'packages': [ { 'package': 'fuchsia/third_party/aemu/linux-amd64', - 'version': 'UABC8VAzZj56bPNLe3ou7AIlBjwHbiXOu6R9f5RbZWcC' + 'version': 'KmGtEkDfwHxQu0H8Vjg__Xqkj_7wy2sK8nS-v1WZhSwC' }, ], 'condition': 'host_os == "linux" and checkout_fuchsia', @@ -1486,7 +1486,7 @@ }, 'src/third_party/webrtc': - Var('webrtc_git') + '/src.git' + '@' + 'ab4a49259c0ea3ab7e7dc28c7bda8f443181cf92', + Var('webrtc_git') + '/src.git' + '@' + '5b5abd79106a1770bb6fdab9e40f7270c7d4259a', 'src/third_party/libgifcodec': Var('skia_git') + '/libgifcodec' + '@'+ Var('libgifcodec_revision'), @@ -1558,7 +1558,7 @@ Var('chromium_git') + '/v8/v8.git' + '@' + Var('v8_revision'), 'src-internal': { - 'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@110e287f7da023e2e7b26285a5cbb648869c51b5', + 'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@fbeee8fe0c0bc1ee5a70f7375dd0cb048ea7bafe', 'condition': 'checkout_src_internal', },
diff --git a/PRESUBMIT.py b/PRESUBMIT.py index 1af75a3..ab30915e 100644 --- a/PRESUBMIT.py +++ b/PRESUBMIT.py
@@ -328,13 +328,11 @@ '^chrome/browser/download/', '^chrome/browser/extensions/', '^chrome/browser/history/', - '^chrome/browser/installable/installable_manager_browsertest.cc', '^chrome/browser/lifetime/', '^chrome/browser/media_galleries/', '^chrome/browser/media/', '^chrome/browser/metrics/', '^chrome/browser/nacl_host/test/gdb_debug_stub_browsertest.cc', - '^chrome/browser/nearby_sharing/client/nearby_share_api_call_flow_impl_unittest.cc', # pylint: disable=line-too-long '^chrome/browser/net/', '^chrome/browser/notifications/', '^chrome/browser/ntp_tiles/ntp_tiles_browsertest.cc',
diff --git a/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java b/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java index 6a7d804..8ade01e 100644 --- a/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java +++ b/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java
@@ -7,6 +7,7 @@ import org.chromium.base.BaseSwitches; import org.chromium.cc.base.CcSwitches; import org.chromium.components.metrics.MetricsSwitches; +import org.chromium.gpu.config.GpuFeatures; import org.chromium.gpu.config.GpuSwitches; /** @@ -64,7 +65,8 @@ Flag.commandLine(GpuSwitches.IGNORE_GPU_BLOCKLIST, "Overrides the built-in software rendering list and enables " + "GPU acceleration on unsupported device configurations."), - Flag.baseFeature("EnableSharedImageForWebview", "Enables shared images for WebView."), + Flag.baseFeature(GpuFeatures.ENABLE_SHARED_IMAGE_FOR_WEBVIEW, + "Enables shared images for WebView."), Flag.baseFeature("VizForWebView", "Enables Viz for WebView."), Flag.baseFeature(AwFeatures.WEBVIEW_CONNECTIONLESS_SAFE_BROWSING, "Uses GooglePlayService's 'connectionless' APIs for Safe Browsing "
diff --git a/ash/BUILD.gn b/ash/BUILD.gn index a88d144..d94683d1 100644 --- a/ash/BUILD.gn +++ b/ash/BUILD.gn
@@ -275,8 +275,6 @@ "capture_mode/capture_mode_type_view.cc", "capture_mode/capture_mode_type_view.h", "capture_mode/capture_mode_types.h", - "capture_mode/capture_window_observer.cc", - "capture_mode/capture_window_observer.h", "capture_mode/stop_recording_button_tray.cc", "capture_mode/stop_recording_button_tray.h", "capture_mode/view_with_ink_drop.h",
diff --git a/ash/ambient/ui/ambient_background_image_view.cc b/ash/ambient/ui/ambient_background_image_view.cc index 1bf423e..eac7be9 100644 --- a/ash/ambient/ui/ambient_background_image_view.cc +++ b/ash/ambient/ui/ambient_background_image_view.cc
@@ -8,6 +8,7 @@ #include "ash/ambient/ambient_constants.h" #include "ash/ambient/ui/glanceable_info_view.h" +#include "ash/ambient/ui/media_string_view.h" #include "ash/ambient/util/ambient_util.h" #include "ash/assistant/ui/assistant_view_ids.h" #include "base/rand_util.h" @@ -30,6 +31,7 @@ // Appearance. constexpr int kMarginDip = 16; constexpr int kSpacingDip = 8; +constexpr int kMediaStringMarginDip = 32; // Typography. constexpr SkColor kTextColor = SK_ColorWHITE; @@ -228,6 +230,25 @@ details_label_->SetShadows(ambient::util::GetTextShadowValues()); details_label_->SetPaintToLayer(); details_label_->layer()->SetFillsBoundsOpaquely(false); + + // Inits the media string view. The media string view is positioned on the + // right-top corner of the container. + views::View* media_string_view_container_ = + AddChildView(std::make_unique<views::View>()); + views::BoxLayout* media_string_layout = + media_string_view_container_->SetLayoutManager( + std::make_unique<views::BoxLayout>( + views::BoxLayout::Orientation::kVertical)); + media_string_layout->set_main_axis_alignment( + views::BoxLayout::MainAxisAlignment::kStart); + media_string_layout->set_cross_axis_alignment( + views::BoxLayout::CrossAxisAlignment::kEnd); + media_string_layout->set_inside_border_insets( + gfx::Insets(kMediaStringMarginDip + shadow_insets.top(), 0, 0, + kMediaStringMarginDip + shadow_insets.right())); + media_string_view_ = media_string_view_container_->AddChildView( + std::make_unique<MediaStringView>()); + media_string_view_->SetVisible(false); } void AmbientBackgroundImageView::UpdateGlanceableInfoPosition() { @@ -262,6 +283,13 @@ transform.Translate(current_x_translation, current_y_translation); glanceable_info_view_->layer()->SetTransform(transform); details_label_->layer()->SetTransform(transform); + + if (media_string_view_->GetVisible()) { + gfx::Transform media_string_transform; + media_string_transform.Translate(-current_x_translation, + -current_y_translation); + media_string_view_->layer()->SetTransform(media_string_transform); + } } bool AmbientBackgroundImageView::UpdateRelatedImageViewVisibility() {
diff --git a/ash/ambient/ui/ambient_background_image_view.h b/ash/ambient/ui/ambient_background_image_view.h index efffebe..deb9f83 100644 --- a/ash/ambient/ui/ambient_background_image_view.h +++ b/ash/ambient/ui/ambient_background_image_view.h
@@ -22,6 +22,7 @@ namespace ash { class GlanceableInfoView; +class MediaStringView; // AmbientBackgroundImageView-------------------------------------------------- // A custom ImageView to display photo image and details information on ambient. @@ -90,6 +91,8 @@ // current image. Owned by the view hierarchy. views::Label* details_label_ = nullptr; + MediaStringView* media_string_view_ = nullptr; + ScopedObserver<views::View, views::ViewObserver> observed_views_{this}; }; } // namespace ash
diff --git a/ash/ambient/ui/ambient_container_view.cc b/ash/ambient/ui/ambient_container_view.cc index aec77ea..d7cd087 100644 --- a/ash/ambient/ui/ambient_container_view.cc +++ b/ash/ambient/ui/ambient_container_view.cc
@@ -9,7 +9,6 @@ #include "ash/ambient/ui/ambient_assistant_container_view.h" #include "ash/ambient/ui/ambient_view_delegate.h" -#include "ash/ambient/ui/media_string_view.h" #include "ash/ambient/ui/photo_view.h" #include "ash/ambient/util/ambient_util.h" #include "ash/assistant/ui/assistant_view_ids.h" @@ -38,7 +37,6 @@ // Appearance. constexpr int kAssistantPreferredHeightDip = 128; -constexpr int kMediaStringMarginDip = 32; // A tolerance threshold used to ignore spurious mouse move. constexpr int kMouseMoveErrorTolerancePx = 3; @@ -135,7 +133,7 @@ void AmbientContainerView::Layout() { // Layout child views first to have proper bounds set for children. LayoutPhotoView(); - LayoutMediaStringView(); + // The assistant view may not exist if |kAmbientAssistant| feature is // disabled. if (ambient_assistant_container_view_) @@ -156,9 +154,6 @@ photo_view_ = AddChildView(std::make_unique<PhotoView>(delegate_)); - media_string_view_ = AddChildView(std::make_unique<MediaStringView>()); - media_string_view_->SetVisible(false); - if (IsAmbientAssistantEnabled()) { ambient_assistant_container_view_ = AddChildView(std::make_unique<AmbientAssistantContainerView>()); @@ -178,19 +173,6 @@ gfx::Rect(0, 0, preferred_width, preferred_height)); } -void AmbientContainerView::LayoutMediaStringView() { - const gfx::Size container_size = GetLocalBounds().size(); - const gfx::Size preferred_size = media_string_view_->GetPreferredSize(); - - // The media string view is positioned on the right-top corner of the - // container. - int x = - container_size.width() - kMediaStringMarginDip - preferred_size.width(); - int y = kMediaStringMarginDip; - media_string_view_->SetBoundsRect( - gfx::Rect(x, y, preferred_size.width(), preferred_size.height())); -} - void AmbientContainerView::HandleEvent() { delegate_->OnBackgroundPhotoEvents(); }
diff --git a/ash/ambient/ui/ambient_container_view.h b/ash/ambient/ui/ambient_container_view.h index 2cd56ea6..cd44646 100644 --- a/ash/ambient/ui/ambient_container_view.h +++ b/ash/ambient/ui/ambient_container_view.h
@@ -16,7 +16,6 @@ class AmbientAssistantContainerView; class AmbientViewDelegate; class PhotoView; -class MediaStringView; // Container view to display all Ambient Mode related views, i.e. photo frame, // weather info. @@ -42,7 +41,6 @@ // Layout(). See b/163170162. void LayoutPhotoView(); void LayoutAssistantView(); - void LayoutMediaStringView(); // Invoked on specific types of events. void HandleEvent(); @@ -52,7 +50,6 @@ // Owned by view hierarchy. PhotoView* photo_view_ = nullptr; AmbientAssistantContainerView* ambient_assistant_container_view_ = nullptr; - MediaStringView* media_string_view_ = nullptr; // Observes events from its host widget. std::unique_ptr<HostWidgetEventObserver> event_observer_;
diff --git a/ash/ambient/ui/media_string_view_unittest.cc b/ash/ambient/ui/media_string_view_unittest.cc index 2a4ffaa..c6435481 100644 --- a/ash/ambient/ui/media_string_view_unittest.cc +++ b/ash/ambient/ui/media_string_view_unittest.cc
@@ -28,6 +28,11 @@ GetSessionControllerClient()->set_show_lock_screen_views(true); } + void TearDown() override { + CloseAmbientScreen(); + AmbientAshTestBase::TearDown(); + } + const base::string16& GetText() { return GetMediaStringViewTextLabel()->GetText(); } @@ -260,6 +265,8 @@ metadata.title = base::ASCIIToUTF16("title"); metadata.artist = base::ASCIIToUTF16("artist"); + SimulateMediaPlaybackStateChanged( + media_session::mojom::MediaPlaybackState::kPlaying); SimulateMediaMetadataChanged(metadata); // Force re-layout. container_view()->Layout(); @@ -277,6 +284,8 @@ metadata.title = base::ASCIIToUTF16("A super duper long title"); metadata.artist = base::ASCIIToUTF16("A super duper long artist name"); + SimulateMediaPlaybackStateChanged( + media_session::mojom::MediaPlaybackState::kPlaying); SimulateMediaMetadataChanged(metadata); // Force re-layout. container_view()->Layout(); @@ -294,6 +303,8 @@ metadata.title = base::ASCIIToUTF16("title"); metadata.artist = base::ASCIIToUTF16("artist"); + SimulateMediaPlaybackStateChanged( + media_session::mojom::MediaPlaybackState::kPlaying); SimulateMediaMetadataChanged(metadata); // Force re-layout. container_view()->Layout(); @@ -357,7 +368,6 @@ PrefService* pref = Shell::Get()->session_controller()->GetPrimaryUserPrefService(); pref->SetBoolean(prefs::kLockScreenMediaControlsEnabled, false); - // Simulates Ambient Mode shown on lock-screen. LockScreen(); FastForwardToInactivity(); @@ -370,4 +380,28 @@ EXPECT_FALSE(GetMediaStringView()->GetVisible()); } +TEST_F(MediaStringViewTest, ShouldHasDifferentTransform) { + ShowAmbientScreen(); + + // Sets metadata for current session. + media_session::MediaMetadata metadata; + metadata.title = base::ASCIIToUTF16("title"); + metadata.artist = base::ASCIIToUTF16("artist"); + + SimulateMediaPlaybackStateChanged( + media_session::mojom::MediaPlaybackState::kPlaying); + SimulateMediaMetadataChanged(metadata); + EXPECT_TRUE(GetMediaStringView()->GetVisible()); + + // It is theoretically that the transforms could be the same in two + // consecutive updates, therefore we test with two updates. + gfx::Transform transform1 = + GetMediaStringView()->layer()->GetTargetTransform(); + FastForwardToNextImage(); + FastForwardToNextImage(); + gfx::Transform transform2 = + GetMediaStringView()->layer()->GetTargetTransform(); + EXPECT_NE(transform1, transform2); +} + } // namespace ash
diff --git a/ash/ash_strings.grd b/ash/ash_strings.grd index f257ea6..78eff98 100644 --- a/ash/ash_strings.grd +++ b/ash/ash_strings.grd
@@ -2590,6 +2590,10 @@ Pin to shelf </message> + <message name="IDS_ASH_GLOBAL_MEDIA_CONTROLS_NO_MEDIA_TEXT" desc="Text indicating no media is currently playing."> + No media playing + </message> + <!-- Power off menu --> <message name="IDS_ASH_POWER_BUTTON_MENU_POWER_OFF_BUTTON" desc="Text shown on power off button in power button menu."> Power off
diff --git a/ash/ash_strings_grd/IDS_ASH_GLOBAL_MEDIA_CONTROLS_NO_MEDIA_TEXT.png.sha1 b/ash/ash_strings_grd/IDS_ASH_GLOBAL_MEDIA_CONTROLS_NO_MEDIA_TEXT.png.sha1 new file mode 100644 index 0000000..524c2c4 --- /dev/null +++ b/ash/ash_strings_grd/IDS_ASH_GLOBAL_MEDIA_CONTROLS_NO_MEDIA_TEXT.png.sha1
@@ -0,0 +1 @@ +d8a52632861b5b591d9b4bf354b24c426ec91bf4 \ No newline at end of file
diff --git a/ash/capture_mode/capture_mode_session.cc b/ash/capture_mode/capture_mode_session.cc index 8bb0a2a..6ea2bfa 100644 --- a/ash/capture_mode/capture_mode_session.cc +++ b/ash/capture_mode/capture_mode_session.cc
@@ -6,7 +6,6 @@ #include "ash/capture_mode/capture_mode_bar_view.h" #include "ash/capture_mode/capture_mode_controller.h" -#include "ash/capture_mode/capture_window_observer.h" #include "ash/display/mouse_cursor_event_filter.h" #include "ash/public/cpp/shell_window_ids.h" #include "ash/resources/vector_icons/vector_icons.h" @@ -32,7 +31,6 @@ #include "ui/views/background.h" #include "ui/views/controls/button/label_button.h" #include "ui/views/controls/label.h" -#include "ui/wm/core/coordinate_conversion.h" namespace ash { @@ -170,11 +168,6 @@ capture_mode_bar_widget_.Show(); RefreshStackingOrder(parent); - - if (controller_->source() == CaptureModeSource::kWindow) { - capture_window_observer_ = - std::make_unique<CaptureWindowObserver>(this, controller_->type()); - } } CaptureModeSession::~CaptureModeSession() { @@ -183,18 +176,15 @@ } aura::Window* CaptureModeSession::GetSelectedWindow() const { - return capture_window_observer_ ? capture_window_observer_->window() - : nullptr; + // Note that the capture bar widget is activatable, so we can't use + // window_util::GetActiveWindow(). Instead, we use the MRU window tracker and + // get the top-most window if any. + auto mru_windows = + Shell::Get()->mru_window_tracker()->BuildMruWindowList(kActiveDesk); + return mru_windows.empty() ? nullptr : mru_windows[0]; } void CaptureModeSession::OnCaptureSourceChanged(CaptureModeSource new_source) { - if (new_source == CaptureModeSource::kWindow) { - capture_window_observer_ = - std::make_unique<CaptureWindowObserver>(this, controller_->type()); - } else { - capture_window_observer_.reset(); - } - capture_mode_bar_view_->OnCaptureSourceChanged(new_source); SetMouseWarpEnabled(new_source != CaptureModeSource::kRegion); UpdateCaptureRegionWidgets(); @@ -202,8 +192,6 @@ } void CaptureModeSession::OnCaptureTypeChanged(CaptureModeType new_type) { - if (controller_->source() == CaptureModeSource::kWindow) - capture_window_observer_->OnCaptureTypeChanged(new_type); capture_mode_bar_view_->OnCaptureTypeChanged(new_type); } @@ -342,45 +330,13 @@ void CaptureModeSession::OnLocatedEvent(ui::LocatedEvent* event, bool is_touch) { - // No need to handle events if the current source is kFullscreen. - const CaptureModeSource capture_source = controller_->source(); - if (capture_source == CaptureModeSource::kFullscreen) + // No need to handle events if the current source is not region. + if (controller_->source() != CaptureModeSource::kRegion) return; gfx::Point location = event->location(); aura::Window* source = static_cast<aura::Window*>(event->target()); aura::Window::ConvertPointToTarget(source, current_root_, &location); - const bool is_event_on_capture_bar = - CaptureModeBarView::GetBounds(current_root_).Contains(location); - - if (capture_source == CaptureModeSource::kWindow) { - // Do not handle any event located on the capture mode bar. - if (is_event_on_capture_bar) - return; - - event->SetHandled(); - event->StopPropagation(); - - switch (event->type()) { - case ui::ET_MOUSE_MOVED: - case ui::ET_TOUCH_PRESSED: - case ui::ET_TOUCH_MOVED: { - gfx::Point screen_location(event->location()); - ::wm::ConvertPointToScreen(source, &screen_location); - capture_window_observer_->UpdateSelectedWindowAtPosition( - screen_location); - break; - } - case ui::ET_MOUSE_RELEASED: - case ui::ET_TOUCH_RELEASED: - if (GetSelectedWindow()) - controller_->PerformCapture(); - break; - default: - break; - } - return; - } // Let the capture button handle any events within its bounds. if (capture_button_widget_ && @@ -390,7 +346,7 @@ // Allow events that are located on the capture mode bar to pass through so we // can click the buttons. - if (!is_event_on_capture_bar) { + if (!CaptureModeBarView::GetBounds(current_root_).Contains(location)) { event->SetHandled(); event->StopPropagation(); }
diff --git a/ash/capture_mode/capture_mode_session.h b/ash/capture_mode/capture_mode_session.h index 46f72e0a..b3b1263 100644 --- a/ash/capture_mode/capture_mode_session.h +++ b/ash/capture_mode/capture_mode_session.h
@@ -24,7 +24,6 @@ class CaptureModeBarView; class CaptureModeController; -class CaptureWindowObserver; // Encapsulates an active capture mode session (i.e. an instance of this class // lives as long as capture mode is active). It creates and owns the capture @@ -171,9 +170,6 @@ // Caches the old status of mouse warping before the session started to be // restored at the end. bool old_mouse_warp_status_; - - // Observer to observe the current selected to-be-captured window. - std::unique_ptr<CaptureWindowObserver> capture_window_observer_; }; } // namespace ash
diff --git a/ash/capture_mode/capture_mode_unittests.cc b/ash/capture_mode/capture_mode_unittests.cc index c03b5a5..68ad8bc 100644 --- a/ash/capture_mode/capture_mode_unittests.cc +++ b/ash/capture_mode/capture_mode_unittests.cc
@@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include <memory> - #include "ash/capture_mode/capture_mode_bar_view.h" #include "ash/capture_mode/capture_mode_close_button.h" #include "ash/capture_mode/capture_mode_controller.h" @@ -20,7 +18,6 @@ #include "ash/shell.h" #include "ash/system/status_area_widget.h" #include "ash/test/ash_test_base.h" -#include "ash/wm/window_state.h" #include "base/test/scoped_feature_list.h" #include "ui/events/keycodes/keyboard_codes_posix.h" #include "ui/gfx/geometry/insets.h" @@ -500,38 +497,4 @@ dimensions_label_window->bounds().bottom()); } -TEST_F(CaptureModeTest, WindowCapture) { - // Create 2 windows that overlap with each other. - const gfx::Rect bounds1(0, 0, 200, 200); - std::unique_ptr<aura::Window> window1(CreateTestWindow(bounds1)); - const gfx::Rect bounds2(150, 150, 200, 200); - std::unique_ptr<aura::Window> window2(CreateTestWindow(bounds2)); - - auto* controller = CaptureModeController::Get(); - controller->SetSource(CaptureModeSource::kWindow); - controller->SetType(CaptureModeType::kImage); - controller->Start(); - EXPECT_TRUE(controller->IsActive()); - - auto* event_generator = GetEventGenerator(); - event_generator->MoveMouseToCenterOf(window1.get()); - auto* capture_mode_session = controller->capture_mode_session(); - EXPECT_EQ(capture_mode_session->GetSelectedWindow(), window1.get()); - event_generator->MoveMouseToCenterOf(window2.get()); - EXPECT_EQ(capture_mode_session->GetSelectedWindow(), window2.get()); - - // Now move the mouse to the overlapped area. - event_generator->MoveMouseTo(gfx::Point(175, 175)); - EXPECT_EQ(capture_mode_session->GetSelectedWindow(), window2.get()); - // Close the current selected window should automatically focus to next one. - window2.reset(); - EXPECT_EQ(capture_mode_session->GetSelectedWindow(), window1.get()); - // Open another one on top also change the selected window. - std::unique_ptr<aura::Window> window3(CreateTestWindow(bounds2)); - EXPECT_EQ(capture_mode_session->GetSelectedWindow(), window3.get()); - // Minimize the window should also automatically change the selected window. - WindowState::Get(window3.get())->Minimize(); - EXPECT_EQ(capture_mode_session->GetSelectedWindow(), window1.get()); -} - } // namespace ash
diff --git a/ash/capture_mode/capture_window_observer.cc b/ash/capture_mode/capture_window_observer.cc deleted file mode 100644 index b9dedc1b8..0000000 --- a/ash/capture_mode/capture_window_observer.cc +++ /dev/null
@@ -1,178 +0,0 @@ -// Copyright 2020 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 "ash/capture_mode/capture_window_observer.h" - -#include "ash/capture_mode/capture_mode_controller.h" -#include "ash/capture_mode/capture_mode_session.h" -#include "ash/public/cpp/shell_window_ids.h" -#include "ash/public/cpp/window_finder.h" -#include "ash/resources/vector_icons/vector_icons.h" -#include "ash/shell.h" -#include "ui/base/cursor/cursor_factory.h" -#include "ui/base/cursor/cursor_util.h" -#include "ui/display/screen.h" -#include "ui/gfx/paint_vector_icon.h" -#include "ui/wm/core/window_util.h" -#include "ui/wm/public/activation_client.h" - -namespace ash { - -CaptureWindowObserver::CaptureWindowObserver( - CaptureModeSession* capture_mode_session, - CaptureModeType type) - : capture_type_(type), - original_cursor_(Shell::Get()->cursor_manager()->GetCursor()), - capture_mode_session_(capture_mode_session) { - Shell::Get()->activation_client()->AddObserver(this); -} - -CaptureWindowObserver::~CaptureWindowObserver() { - auto* shell = Shell::Get(); - shell->activation_client()->RemoveObserver(this); - StopObserving(); - ::wm::CursorManager* cursor_manager = shell->cursor_manager(); - if (is_cursor_locked_) { - cursor_manager->UnlockCursor(); - cursor_manager->SetCursor(original_cursor_); - is_cursor_locked_ = false; - } -} - -void CaptureWindowObserver::UpdateSelectedWindowAtPosition( - const gfx::Point& location_in_screen) { - UpdateSelectedWindowAtPosition(location_in_screen, /*ignore_windows=*/{}); -} - -void CaptureWindowObserver::OnCaptureTypeChanged(CaptureModeType new_type) { - capture_type_ = new_type; - UpdateMouseCursor(); -} - -void CaptureWindowObserver::OnWindowBoundsChanged( - aura::Window* window, - const gfx::Rect& old_bounds, - const gfx::Rect& new_bounds, - ui::PropertyChangeReason reason) { - DCHECK_EQ(window, window_); - RepaintCaptureRegion(); -} - -void CaptureWindowObserver::OnWindowVisibilityChanging(aura::Window* window, - bool visible) { - DCHECK_EQ(window, window_); - DCHECK(!visible); - StopObserving(); - UpdateSelectedWindowAtPosition(location_in_screen_, - /*ignore_windows=*/{window}); -} - -void CaptureWindowObserver::OnWindowDestroying(aura::Window* window) { - DCHECK_EQ(window, window_); - StopObserving(); - UpdateSelectedWindowAtPosition(location_in_screen_, - /*ignore_windows=*/{window}); -} - -void CaptureWindowObserver::OnWindowActivated(ActivationReason reason, - aura::Window* gained_active, - aura::Window* lost_active) { - // If another window is activated on top of the current selected window, we - // may change the selected window to the activated window if it's under the - // current event location. If there is no selected window at the moment, we - // also want to check if new activated window should be focused. - UpdateSelectedWindowAtPosition(location_in_screen_, /*ignore_windows=*/{}); -} - -void CaptureWindowObserver::StartObserving(aura::Window* window) { - window_ = window; - window_->AddObserver(this); -} - -void CaptureWindowObserver::StopObserving() { - if (window_) { - window_->RemoveObserver(this); - window_ = nullptr; - } -} - -void CaptureWindowObserver::UpdateSelectedWindowAtPosition( - const gfx::Point& location_in_screen, - const std::set<aura::Window*>& ignore_windows) { - location_in_screen_ = location_in_screen; - // Find the toplevel window under the mouse/touch position. - aura::Window* window = - GetTopmostWindowAtPoint(location_in_screen_, ignore_windows); - if (window_ == window) - return; - - // Don't capture wallpaper window. - if (window && window->parent() && - window->parent()->id() == kShellWindowId_WallpaperContainer) { - window = nullptr; - } - - // Stop observing the current selected window if there is one. - aura::Window* previous_selected_window = window_; - StopObserving(); - if (window) - StartObserving(window); - RepaintCaptureRegion(); - - // Change mouse cursor depending on capture type and capture window if - // applicable. - const bool should_update_cursor = - !previous_selected_window != !window_ && - Shell::Get()->cursor_manager()->IsCursorVisible(); - if (should_update_cursor) - UpdateMouseCursor(); -} - -void CaptureWindowObserver::RepaintCaptureRegion() { - ui::Layer* layer = capture_mode_session_->layer(); - layer->SchedulePaint(layer->bounds()); -} - -void CaptureWindowObserver::UpdateMouseCursor() { - ::wm::CursorManager* cursor_manager = Shell::Get()->cursor_manager(); - if (window_) { - // Change the mouse cursor to a capture icon or a recording icon. - ui::Cursor cursor(ui::mojom::CursorType::kCustom); - const display::Display display = - display::Screen::GetScreen()->GetDisplayNearestWindow(window_); - const float device_scale_factor = display.device_scale_factor(); - // TODO: Adjust the icon color after spec is updated. - const gfx::ImageSkia icon = gfx::CreateVectorIcon( - capture_type_ == CaptureModeType::kImage ? kCaptureModeImageIcon - : kCaptureModeVideoIcon, - SK_ColorBLACK); - SkBitmap bitmap = *icon.bitmap(); - gfx::Point hotspot(bitmap.width() / 2, bitmap.height() / 2); - ui::ScaleAndRotateCursorBitmapAndHotpoint( - device_scale_factor, display.panel_rotation(), &bitmap, &hotspot); - auto* cursor_factory = ui::CursorFactory::GetInstance(); - ui::PlatformCursor platform_cursor = - cursor_factory->CreateImageCursor(bitmap, hotspot); - cursor.SetPlatformCursor(platform_cursor); - cursor.set_custom_bitmap(bitmap); - cursor.set_custom_hotspot(hotspot); - cursor_factory->UnrefImageCursor(platform_cursor); - - // Unlock the cursor first so that it can be changed. - if (is_cursor_locked_) - cursor_manager->UnlockCursor(); - cursor_manager->SetCursor(cursor); - cursor_manager->LockCursor(); - is_cursor_locked_ = true; - } else { - // Revert back to its previous mouse cursor setting. - if (is_cursor_locked_) { - cursor_manager->UnlockCursor(); - is_cursor_locked_ = false; - } - cursor_manager->SetCursor(original_cursor_); - } -} - -} // namespace ash
diff --git a/ash/capture_mode/capture_window_observer.h b/ash/capture_mode/capture_window_observer.h deleted file mode 100644 index f02ade3..0000000 --- a/ash/capture_mode/capture_window_observer.h +++ /dev/null
@@ -1,98 +0,0 @@ -// Copyright 2020 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 ASH_CAPTURE_MODE_CAPTURE_WINDOW_OBSERVER_H_ -#define ASH_CAPTURE_MODE_CAPTURE_WINDOW_OBSERVER_H_ - -#include <set> - -#include "ash/ash_export.h" -#include "ash/capture_mode/capture_mode_types.h" -#include "ui/aura/window_observer.h" -#include "ui/base/cursor/cursor.h" -#include "ui/gfx/geometry/point.h" -#include "ui/gfx/native_widget_types.h" -#include "ui/wm/public/activation_change_observer.h" - -namespace aura { -class Window; -} // namespace aura - -namespace ash { - -class CaptureModeSession; - -// Class to observe the current selected to-be-captured window and update the -// capture region if applicable. -class ASH_EXPORT CaptureWindowObserver : public aura::WindowObserver, - public ::wm::ActivationChangeObserver { - public: - CaptureWindowObserver(CaptureModeSession* capture_mode_session, - CaptureModeType type); - CaptureWindowObserver(const CaptureWindowObserver&) = delete; - CaptureWindowObserver& operator=(const CaptureWindowObserver&) = delete; - - ~CaptureWindowObserver() override; - - // Updates selected window depending on the mouse/touch event location. If - // there is an eligible window under the current mouse/touch event location, - // its bounds will be highlighted. - void UpdateSelectedWindowAtPosition(const gfx::Point& location_in_screen); - - // Called when capture type changes. The mouse cursor image may update - // accordingly. - void OnCaptureTypeChanged(CaptureModeType new_type); - - // aura::WindowObserver: - void OnWindowBoundsChanged(aura::Window* window, - const gfx::Rect& old_bounds, - const gfx::Rect& new_bounds, - ui::PropertyChangeReason reason) override; - void OnWindowVisibilityChanging(aura::Window* window, bool visible) override; - void OnWindowDestroying(aura::Window* window) override; - - // ::wm::ActivationChangeObserver: - void OnWindowActivated(ActivationReason reason, - aura::Window* gained_active, - aura::Window* lost_active) override; - - aura::Window* window() { return window_; } - - private: - void StartObserving(aura::Window* window); - void StopObserving(); - - // Updates selected window depending on the mouse/touch event location with - // ignoring |ignore_windows|. - void UpdateSelectedWindowAtPosition( - const gfx::Point& location_in_screen, - const std::set<aura::Window*>& ignore_windows); - - // Repaints the window capture region. - void RepaintCaptureRegion(); - - // Updates the mouse cursor to change it to a capture or record icon when the - // mouse hovers over an eligible window. - void UpdateMouseCursor(); - - // Current observed window. - aura::Window* window_ = nullptr; - - // Stores current mouse or touch location in screen coordinate. - gfx::Point location_in_screen_; - - // Current capture type. - CaptureModeType capture_type_; - - // True if the current cursor is locked by this. - bool is_cursor_locked_ = false; - const gfx::NativeCursor original_cursor_; - - // Pointer to current capture session. Not nullptr during this lifecycle. - CaptureModeSession* const capture_mode_session_; -}; - -} // namespace ash - -#endif // ASH_CAPTURE_MODE_CAPTURE_WINDOW_OBSERVER_H_
diff --git a/ash/resources/vector_icons/BUILD.gn b/ash/resources/vector_icons/BUILD.gn index ecc8a62..b711c67 100644 --- a/ash/resources/vector_icons/BUILD.gn +++ b/ash/resources/vector_icons/BUILD.gn
@@ -77,6 +77,7 @@ "login_screen_enterprise.icon", "login_screen_menu_dropdown.icon", "mic.icon", + "music_note.icon", "muted_microphone.icon", "network_badge_captive_portal.icon", "network_badge_off.icon",
diff --git a/ash/resources/vector_icons/music_note.icon b/ash/resources/vector_icons/music_note.icon new file mode 100644 index 0000000..e2f88b4 --- /dev/null +++ b/ash/resources/vector_icons/music_note.icon
@@ -0,0 +1,15 @@ +// Copyright 2020 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. + +CANVAS_DIMENSIONS, 20, +MOVE_TO, 10, 3, +R_LINE_TO, 0.01f, 7.79f, +R_ARC_TO, 3.33f, 3.33f, 0, 0, 0, -1.67f, -0.46f, +R_ARC_TO, 3.33f, 3.33f, 0, 1, 0, 0, 6.67f, +CUBIC_TO, 10.19f, 17, 12, 15.51f, 12, 13.67f, +V_LINE_TO, 6, +R_H_LINE_TO, 3, +V_LINE_TO, 3, +R_H_LINE_TO, -5, +CLOSE
diff --git a/ash/style/ash_color_provider.cc b/ash/style/ash_color_provider.cc index 6d0feaad..0c5bed9 100644 --- a/ash/style/ash_color_provider.cc +++ b/ash/style/ash_color_provider.cc
@@ -7,6 +7,7 @@ #include <math.h> #include "ash/public/cpp/ash_constants.h" +#include "ash/public/cpp/ash_features.h" #include "ash/public/cpp/ash_pref_names.h" #include "ash/public/cpp/login_constants.h" #include "ash/session/session_controller_impl.h" @@ -290,6 +291,38 @@ // added separately so its easier to monitor performance. } +void AshColorProvider::DecorateIconButton(views::ImageButton* button, + ButtonType type, + const gfx::VectorIcon& icon, + bool toggled, + int icon_size) { + DCHECK(!icon.is_empty()); + const SkColor normal_color = + GetContentLayerColor(ContentLayerType::kButtonIconColor); + const SkColor toggled_icon_color = + GetContentLayerColor(ContentLayerType::kButtonIconColorPrimary); + const SkColor icon_color = toggled ? toggled_icon_color : normal_color; + + // Skip repainting if the incoming icon is the same as the current icon. If + // the icon has been painted before, |gfx::CreateVectorIcon()| will simply + // grab the ImageSkia from a cache, so it will be cheap. Note that this + // assumes that toggled/disabled images changes at the same time as the normal + // image, which it currently does. + const gfx::ImageSkia new_normal_image = + gfx::CreateVectorIcon(icon, icon_size, icon_color); + const gfx::ImageSkia& old_normal_image = + button->GetImage(views::Button::STATE_NORMAL); + if (!new_normal_image.isNull() && !old_normal_image.isNull() && + new_normal_image.BackedBySameObjectAs(old_normal_image)) { + return; + } + + button->SetImage(views::Button::STATE_NORMAL, new_normal_image); + button->SetImage( + views::Button::STATE_DISABLED, + gfx::CreateVectorIcon(icon, icon_size, GetDisabledColor(normal_color))); +} + void AshColorProvider::AddObserver(ColorModeObserver* observer) { observers_.AddObserver(observer); } @@ -299,7 +332,7 @@ } bool AshColorProvider::IsDarkModeEnabled() const { - if (override_light_mode_as_default_) + if (!features::IsDarkLightModeEnabled() && override_light_mode_as_default_) return false; if (!active_user_pref_service_)
diff --git a/ash/style/ash_color_provider.h b/ash/style/ash_color_provider.h index d5d4c02..6af3166b 100644 --- a/ash/style/ash_color_provider.h +++ b/ash/style/ash_color_provider.h
@@ -121,6 +121,7 @@ enum class ButtonType { kPillButtonWithIcon, kCloseButtonWithSmallBase, + kIconButtonSmallOrMedium, }; // Attributes of ripple, includes the base color, opacity of inkdrop and @@ -185,6 +186,11 @@ ButtonType type, int button_size, const gfx::VectorIcon& icon); + void DecorateIconButton(views::ImageButton* button, + ButtonType type, + const gfx::VectorIcon& icon, + bool toggled, + int icon_size); void AddObserver(ColorModeObserver* observer); void RemoveObserver(ColorModeObserver* observer); @@ -227,8 +233,9 @@ // Default color mode is dark, which is controlled by pref |kDarkModeEnabled| // currently. But we can also override it to light through // ScopedLightModeAsDefault. This is done to help keeping some of the UI - // elements as light by default before launching dark/light mode. This will be - // removed once enabling dark/light mode. + // elements as light by default before launching dark/light mode. Overriding + // only if the kDarkLightMode feature is disabled. This variable will be + // removed once enabled dark/light mode. bool override_light_mode_as_default_ = false; base::ObserverList<ColorModeObserver> observers_;
diff --git a/ash/style/scoped_light_mode_as_default.h b/ash/style/scoped_light_mode_as_default.h index 52e902d..d2f91f1 100644 --- a/ash/style/scoped_light_mode_as_default.h +++ b/ash/style/scoped_light_mode_as_default.h
@@ -10,7 +10,8 @@ // A helper class to set default color mode to light. Color mode is dark by // default, which is controlled by pref |kDarkModeEnabled| currently. Some of // the components need to be kept as LIGHT by default before launching -// dark/light mode. E.g. power button menu and system toast. +// dark/light mode. E.g. power button menu and system toast. Overriding only if +// the kDarkLightMode feature is disabled. class ScopedLightModeAsDefault { public: ScopedLightModeAsDefault();
diff --git a/ash/system/media/unified_media_controls_controller.cc b/ash/system/media/unified_media_controls_controller.cc index a25bf344..e98683a6 100644 --- a/ash/system/media/unified_media_controls_controller.cc +++ b/ash/system/media/unified_media_controls_controller.cc
@@ -109,9 +109,12 @@ // Start hide controls timer if there is no active session, wait to // see if we will receive a new session. if (!request_id.has_value()) { + if (hide_artwork_timer_->IsRunning()) + hide_artwork_timer_->Stop(); + hide_controls_timer_->Start( FROM_HERE, kHideControlsDelay, - base::BindOnce(&UnifiedMediaControlsController::HideControls, + base::BindOnce(&UnifiedMediaControlsController::ShowEmptyState, base::Unretained(this))); return; } @@ -119,6 +122,7 @@ if (!media_session_id_.has_value()) delegate_->ShowMediaControls(); media_session_id_ = request_id; + media_controls_->OnNewMediaSession(); } void UnifiedMediaControlsController::MediaControllerImageChanged( @@ -175,9 +179,9 @@ media_session::PerformMediaSessionAction(action, media_controller_remote_); } -void UnifiedMediaControlsController::HideControls() { +void UnifiedMediaControlsController::ShowEmptyState() { media_session_id_ = base::nullopt; - delegate_->HideMediaControls(); + media_controls_->ShowEmptyState(); } void UnifiedMediaControlsController::FlushForTesting() {
diff --git a/ash/system/media/unified_media_controls_controller.h b/ash/system/media/unified_media_controls_controller.h index 1944089..d6d6cff 100644 --- a/ash/system/media/unified_media_controls_controller.h +++ b/ash/system/media/unified_media_controls_controller.h
@@ -30,7 +30,6 @@ public: virtual ~Delegate() = default; virtual void ShowMediaControls() = 0; - virtual void HideMediaControls() = 0; virtual void OnMediaControlsViewClicked() = 0; }; @@ -70,7 +69,7 @@ } private: - void HideControls(); + void ShowEmptyState(); // Weak ptr, owned by view hierarchy. UnifiedMediaControlsView* media_controls_ = nullptr;
diff --git a/ash/system/media/unified_media_controls_controller_unittest.cc b/ash/system/media/unified_media_controls_controller_unittest.cc index dd0e5b81..88051bc 100644 --- a/ash/system/media/unified_media_controls_controller_unittest.cc +++ b/ash/system/media/unified_media_controls_controller_unittest.cc
@@ -36,7 +36,6 @@ ~MockMediaControlsDelegate() override = default; void ShowMediaControls() override { visible_ = true; } - void HideMediaControls() override { visible_ = false; } MOCK_METHOD0(OnMediaControlsViewClicked, void()); bool IsControlsVisible() { return visible_; } @@ -130,6 +129,10 @@ return static_cast<views::Button*>(*it); } + bool IsMediaControlsInEmptyState() const { + return media_controls_->is_in_empty_state_; + } + SkPath GetArtworkClipPath() { return media_controls_->GetArtworkClipPath(); } views::View* button_row() { return media_controls_->button_row_; } @@ -364,31 +367,123 @@ } TEST_F(UnifiedMediaControlsControllerTest, - ShowHideControlsOnMediaSessionChanged) { + UpdateControlsStateOnMediaSessionChanged) { auto request_id = base::UnguessableToken::Create(); EXPECT_FALSE(delegate()->IsControlsVisible()); controller()->MediaSessionChanged(request_id); EXPECT_TRUE(delegate()->IsControlsVisible()); + EXPECT_FALSE(IsMediaControlsInEmptyState()); controller()->MediaSessionChanged(base::nullopt); - EXPECT_TRUE(delegate()->IsControlsVisible()); + EXPECT_FALSE(IsMediaControlsInEmptyState()); - // Still visible since we are within waiting delay time frame. + // Still in normal state since we are within waiting delay time frame. task_environment()->FastForwardBy( base::TimeDelta::FromMilliseconds(kHideControlsDelay - 1)); - EXPECT_TRUE(delegate()->IsControlsVisible()); + EXPECT_FALSE(IsMediaControlsInEmptyState()); - // Session resumes, controls should still be visible. + // Session resumes, controls should still be in normal state. controller()->MediaSessionChanged(request_id); task_environment()->FastForwardBy(base::TimeDelta::FromMilliseconds(1)); - EXPECT_TRUE(delegate()->IsControlsVisible()); + EXPECT_FALSE(IsMediaControlsInEmptyState()); - // Hide controls timer expired, controls should be hidden. + // Hide controls timer expired, controls should be in empty state. controller()->MediaSessionChanged(base::nullopt); task_environment()->FastForwardBy( base::TimeDelta::FromMilliseconds(kHideControlsDelay)); + EXPECT_TRUE(IsMediaControlsInEmptyState()); + EXPECT_TRUE(delegate()->IsControlsVisible()); +} + +TEST_F(UnifiedMediaControlsControllerTest, MediaControlsEmptyState) { + CreateWidget(); + + // Show media controls. + auto request_id = base::UnguessableToken::Create(); + controller()->MediaSessionChanged(request_id); + EXPECT_TRUE(delegate()->IsControlsVisible()); + EXPECT_FALSE(IsMediaControlsInEmptyState()); + + EnableAction(MediaSessionAction::kPlay); + EnableAction(MediaSessionAction::kPause); + EnableAction(MediaSessionAction::kPreviousTrack); + EnableAction(MediaSessionAction::kNextTrack); + + EXPECT_TRUE(artist_label()->GetVisible()); + EXPECT_FALSE(artwork_view()->GetVisible()); + for (views::View* button : button_row()->children()) + EXPECT_TRUE(button->GetEnabled()); + + // Media controls should be in empty state after getting empty session. + controller()->MediaSessionChanged(base::nullopt); + task_environment()->FastForwardBy( + base::TimeDelta::FromMilliseconds(kHideControlsDelay)); + + EXPECT_TRUE(IsMediaControlsInEmptyState()); + + // When in empty state, artist label should be hidden; artwork view + // should be hidden since it was hidden before getting into empty + // state; all action buttons should be disabled. + EXPECT_FALSE(artist_label()->GetVisible()); + EXPECT_FALSE(artwork_view()->GetVisible()); + for (views::View* button : button_row()->children()) + EXPECT_FALSE(button->GetEnabled()); + + // Tapping on the media controls when we are in empty state should not + // notify delegate. + EXPECT_CALL(*delegate(), OnMediaControlsViewClicked).Times(0); + ui::test::EventGenerator* generator = GetEventGenerator(); + generator->MoveMouseTo( + media_controls_view()->GetBoundsInScreen().CenterPoint()); + generator->ClickLeftButton(); + + // Media controls should get back to normal state. + controller()->MediaSessionChanged(request_id); + EXPECT_FALSE(IsMediaControlsInEmptyState()); + + EXPECT_TRUE(artist_label()->GetVisible()); + EXPECT_FALSE(artwork_view()->GetVisible()); + for (views::View* button : button_row()->children()) + EXPECT_TRUE(button->GetEnabled()); + + // User should be able to tap the controls for detailed view again. + EXPECT_CALL(*delegate(), OnMediaControlsViewClicked).Times(1); + generator->ClickLeftButton(); +} + +TEST_F(UnifiedMediaControlsControllerTest, MediaControlsEmptyStateWithArtwork) { + auto request_id = base::UnguessableToken::Create(); + EXPECT_FALSE(delegate()->IsControlsVisible()); + controller()->MediaSessionChanged(request_id); + EXPECT_TRUE(delegate()->IsControlsVisible()); + EXPECT_FALSE(IsMediaControlsInEmptyState()); + + // Artwork changed, and artwork view should have an empty background in normal + // state. + SkBitmap artwork; + artwork.allocN32Pixels(40, 40); + controller()->MediaControllerImageChanged( + media_session::mojom::MediaSessionImageType::kArtwork, artwork); + EXPECT_TRUE(artwork_view()->GetVisible()); + EXPECT_EQ(artwork_view()->background(), nullptr); + + controller()->MediaSessionChanged(base::nullopt); + task_environment()->FastForwardBy( + base::TimeDelta::FromMilliseconds(kHideControlsDelay)); + + // Artwork view should still be visible and have an background in empty state. + EXPECT_TRUE(IsMediaControlsInEmptyState()); + EXPECT_TRUE(artwork_view()->GetVisible()); + EXPECT_NE(artwork_view()->background(), nullptr); + + // Session and artwork updated, artwotk view should be back in normal state. + controller()->MediaSessionChanged(request_id); + controller()->MediaControllerImageChanged( + media_session::mojom::MediaSessionImageType::kArtwork, artwork); + EXPECT_TRUE(artwork_view()->GetVisible()); + EXPECT_EQ(artwork_view()->background(), nullptr); } TEST_F(UnifiedMediaControlsControllerTest, FreezeControlsWhenUpdateSession) {
diff --git a/ash/system/media/unified_media_controls_view.cc b/ash/system/media/unified_media_controls_view.cc index d725157..602f8c5 100644 --- a/ash/system/media/unified_media_controls_view.cc +++ b/ash/system/media/unified_media_controls_view.cc
@@ -41,6 +41,7 @@ constexpr gfx::Insets kTrackColumnInsets = gfx::Insets(1, 0, 1, 0); constexpr gfx::Insets kMediaControlsViewInsets = gfx::Insets(8, 8, 8, 12); +constexpr gfx::Size kEmptyArtworkIconSize = gfx::Size(20, 20); constexpr gfx::Size kArtworkSize = gfx::Size(40, 40); constexpr gfx::Size kMediaButtonSize = gfx::Size(32, 32); @@ -116,6 +117,12 @@ GetVectorIconForMediaAction(action), kMediaButtonIconSize, AshColorProvider::Get()->GetContentLayerColor( AshColorProvider::ContentLayerType::kIconColorPrimary))); + + SetImage(views::Button::STATE_DISABLED, + CreateVectorIcon( + GetVectorIconForMediaAction(action), kMediaButtonIconSize, + AshColorProvider::Get()->GetContentLayerColor( + AshColorProvider::ContentLayerType::kIconColorSecondary))); } std::unique_ptr<views::InkDrop> @@ -186,7 +193,7 @@ kUnifiedMenuMoreIcon, AshColorProvider::Get()->GetContentLayerColor( AshColorProvider::ContentLayerType::kIconColorPrimary))); - title_row->AddChildView(std::move(drop_down_icon)); + drop_down_icon_ = title_row->AddChildView(std::move(drop_down_icon)); title_row_layout->SetFlexForView(title_label_, 1); track_column->AddChildView(std::move(title_row)); @@ -223,6 +230,9 @@ void UnifiedMediaControlsView::ButtonPressed(views::Button* sender, const ui::Event& event) { + if (is_in_empty_state_) + return; + if (sender == this) { controller_->OnMediaControlsViewClicked(); return; @@ -286,15 +296,66 @@ button_row_->InvalidateLayout(); } -SkPath UnifiedMediaControlsView::GetArtworkClipPath() { +void UnifiedMediaControlsView::ShowEmptyState() { + is_in_empty_state_ = true; + + title_label_->SetText( + l10n_util::GetStringUTF16(IDS_ASH_GLOBAL_MEDIA_CONTROLS_NO_MEDIA_TEXT)); + title_label_->SetEnabledColor(AshColorProvider::Get()->GetContentLayerColor( + AshColorProvider::ContentLayerType::kTextColorSecondary)); + artist_label_->SetVisible(false); + drop_down_icon_->SetVisible(false); + + for (views::View* button : button_row_->children()) + button->SetEnabled(false); + InvalidateLayout(); + + if (!artwork_view_->GetVisible()) + return; + + artwork_view_->SetBackground(views::CreateSolidBackground( + AshColorProvider::Get()->GetControlsLayerColor( + AshColorProvider::ControlsLayerType:: + kControlBackgroundColorInactive))); + artwork_view_->SetImageSize(kEmptyArtworkIconSize); + artwork_view_->SetImage(CreateVectorIcon( + kMusicNoteIcon, kEmptyArtworkIconSize.width(), + AshColorProvider::Get()->GetContentLayerColor( + AshColorProvider::ContentLayerType::kIconColorSecondary))); + + artwork_view_->SetClipPath(GetArtworkClipPath(kArtworkSize)); +} + +void UnifiedMediaControlsView::OnNewMediaSession() { + if (!is_in_empty_state_) + return; + + is_in_empty_state_ = false; + title_label_->SetEnabledColor(AshColorProvider::Get()->GetContentLayerColor( + AshColorProvider::ContentLayerType::kTextColorPrimary)); + artist_label_->SetVisible(true); + drop_down_icon_->SetVisible(true); + + for (views::View* button : button_row_->children()) + button->SetEnabled(true); + InvalidateLayout(); + + if (!artwork_view_->GetVisible()) + return; + artwork_view_->SetBackground(nullptr); +} + +SkPath UnifiedMediaControlsView::GetArtworkClipPath( + base::Optional<gfx::Size> image_size) { // Calculate image bounds since we might need to draw this when image is // not visible (i.e. when quick setting bubble is collapsed). - gfx::Size image_size = artwork_view_->GetImageBounds().size(); - int x = (kArtworkSize.width() - image_size.width()) / 2; - int y = (kArtworkSize.height() - image_size.height()) / 2; + if (!image_size.has_value()) + image_size = artwork_view_->GetImageBounds().size(); + int x = (kArtworkSize.width() - image_size->width()) / 2; + int y = (kArtworkSize.height() - image_size->height()) / 2; SkPath path; - path.addRoundRect(gfx::RectToSkRect(gfx::Rect(x, y, image_size.width(), - image_size.height())), + path.addRoundRect(gfx::RectToSkRect(gfx::Rect(x, y, image_size->width(), + image_size->height())), kArtworkCornerRadius, kArtworkCornerRadius); return path; }
diff --git a/ash/system/media/unified_media_controls_view.h b/ash/system/media/unified_media_controls_view.h index 91575e5..215745f 100644 --- a/ash/system/media/unified_media_controls_view.h +++ b/ash/system/media/unified_media_controls_view.h
@@ -43,6 +43,13 @@ const base::flat_set<media_session::mojom::MediaSessionAction>& enabled_actions); + // Show an empty state representing no media is playing. + void ShowEmptyState(); + + // Called when receiving new media session, update controls to normal state + // if necessary. + void OnNewMediaSession(); + views::ImageView* artwork_view() { return artwork_view_; } private: @@ -65,15 +72,19 @@ std::unique_ptr<views::InkDropRipple> CreateInkDropRipple() const override; }; - SkPath GetArtworkClipPath(); + SkPath GetArtworkClipPath( + base::Optional<gfx::Size> image_size = base::nullopt); UnifiedMediaControlsController* const controller_ = nullptr; views::ImageView* artwork_view_ = nullptr; + views::ImageView* drop_down_icon_ = nullptr; views::Label* title_label_ = nullptr; views::Label* artist_label_ = nullptr; MediaActionButton* play_pause_button_ = nullptr; views::View* button_row_ = nullptr; + + bool is_in_empty_state_ = false; }; } // namespace ash
diff --git a/ash/system/unified/feature_pod_button.cc b/ash/system/unified/feature_pod_button.cc index e0c09d2..7394707 100644 --- a/ash/system/unified/feature_pod_button.cc +++ b/ash/system/unified/feature_pod_button.cc
@@ -144,32 +144,9 @@ if (!icon_) return; - const auto* color_provider = AshColorProvider::Get(); - const SkColor normal_color = - color_provider->GetContentLayerColor(ContentLayerType::kButtonIconColor); - const SkColor toggled_icon_color = color_provider->GetContentLayerColor( - ContentLayerType::kButtonIconColorPrimary); - const SkColor icon_color = toggled_ ? toggled_icon_color : normal_color; - - // Skip repainting if the incoming icon is the same as the current icon. If - // the icon has been painted before, |gfx::CreateVectorIcon()| will simply - // grab the ImageSkia from a cache, so it will be cheap. Note that this - // assumes that toggled/disabled images changes at the same time as the normal - // image, which it currently does. - const gfx::ImageSkia new_normal_image = gfx::CreateVectorIcon( - *icon_, kUnifiedFeaturePodVectorIconSize, icon_color); - const gfx::ImageSkia& old_normal_image = - GetImage(views::Button::STATE_NORMAL); - if (!new_normal_image.isNull() && !old_normal_image.isNull() && - new_normal_image.BackedBySameObjectAs(old_normal_image)) { - return; - } - - SetImage(views::Button::STATE_NORMAL, new_normal_image); - SetImage( - views::Button::STATE_DISABLED, - gfx::CreateVectorIcon(*icon_, kUnifiedFeaturePodVectorIconSize, - AshColorProvider::GetDisabledColor(normal_color))); + AshColorProvider::Get()->DecorateIconButton( + this, AshColorProvider::ButtonType::kIconButtonSmallOrMedium, *icon_, + toggled_, kUnifiedFeaturePodVectorIconSize); } FeaturePodLabelButton::FeaturePodLabelButton(views::ButtonListener* listener)
diff --git a/ash/system/unified/unified_slider_view.cc b/ash/system/unified/unified_slider_view.cc index 54b7a7c..90be5b0 100644 --- a/ash/system/unified/unified_slider_view.cc +++ b/ash/system/unified/unified_slider_view.cc
@@ -74,7 +74,7 @@ UnifiedSliderButton::UnifiedSliderButton(views::ButtonListener* listener, const gfx::VectorIcon& icon, int accessible_name_id) - : views::ToggleImageButton(listener) { + : views::ImageButton(listener) { SetImageHorizontalAlignment(ALIGN_CENTER); SetImageVerticalAlignment(ALIGN_MIDDLE); if (accessible_name_id) @@ -105,24 +105,13 @@ } void UnifiedSliderButton::SetVectorIcon(const gfx::VectorIcon& icon) { - const SkColor toggled_color = AshColorProvider::Get()->GetContentLayerColor( - ContentLayerType::kButtonIconColorPrimary); - const SkColor icon_color = AshColorProvider::Get()->GetContentLayerColor( - ContentLayerType::kButtonIconColor); - - SetImage(views::Button::STATE_NORMAL, - gfx::CreateVectorIcon(icon, icon_color)); - - toggled_icon_ = gfx::CreateVectorIcon(icon, toggled_color); - SetToggledImage(views::Button::STATE_NORMAL, &toggled_icon_); - - SetImage(views::Button::STATE_DISABLED, - gfx::CreateVectorIcon(icon, icon_color)); + icon_ = &icon; + UpdateVectorIcon(); } void UnifiedSliderButton::SetToggled(bool toggled) { toggled_ = toggled; - views::ToggleImageButton::SetToggled(toggled); + UpdateVectorIcon(); } void UnifiedSliderButton::PaintButtonContents(gfx::Canvas* canvas) { @@ -160,12 +149,21 @@ void UnifiedSliderButton::GetAccessibleNodeData(ui::AXNodeData* node_data) { if (!GetEnabled()) return; - views::ToggleImageButton::GetAccessibleNodeData(node_data); + views::ImageButton::GetAccessibleNodeData(node_data); node_data->role = ax::mojom::Role::kToggleButton; node_data->SetCheckedState(toggled_ ? ax::mojom::CheckedState::kTrue : ax::mojom::CheckedState::kFalse); } +void UnifiedSliderButton::UpdateVectorIcon() { + if (!icon_) + return; + + AshColorProvider::Get()->DecorateIconButton( + this, AshColorProvider::ButtonType::kIconButtonSmallOrMedium, *icon_, + toggled_, GetDefaultSizeOfVectorIcon(*icon_)); +} + UnifiedSliderView::UnifiedSliderView(UnifiedSliderListener* listener, const gfx::VectorIcon& icon, int accessible_name_id,
diff --git a/ash/system/unified/unified_slider_view.h b/ash/system/unified/unified_slider_view.h index c6a929c..49e1fa83 100644 --- a/ash/system/unified/unified_slider_view.h +++ b/ash/system/unified/unified_slider_view.h
@@ -54,7 +54,7 @@ }; // A button used in a slider row of UnifiedSystemTray. The button is togglable. -class UnifiedSliderButton : public views::ToggleImageButton { +class UnifiedSliderButton : public views::ImageButton { public: UnifiedSliderButton(views::ButtonListener* listener, const gfx::VectorIcon& icon, @@ -73,7 +73,7 @@ // views::Button: const char* GetClassName() const override; - // views::ToggleImageButton: + // views::ImageButton: std::unique_ptr<views::InkDrop> CreateInkDrop() override; std::unique_ptr<views::InkDropRipple> CreateInkDropRipple() const override; std::unique_ptr<views::InkDropHighlight> CreateInkDropHighlight() @@ -83,11 +83,12 @@ void GetAccessibleNodeData(ui::AXNodeData* node_data) override; private: + void UpdateVectorIcon(); + // True if the button is currently toggled. bool toggled_ = false; - // Icon used when the button is toggled. - gfx::ImageSkia toggled_icon_; + const gfx::VectorIcon* icon_ = nullptr; DISALLOW_COPY_AND_ASSIGN(UnifiedSliderButton); };
diff --git a/ash/system/unified/unified_system_tray_controller.cc b/ash/system/unified/unified_system_tray_controller.cc index 17547310..35b66c4 100644 --- a/ash/system/unified/unified_system_tray_controller.cc +++ b/ash/system/unified/unified_system_tray_controller.cc
@@ -430,10 +430,6 @@ unified_view_->ShowMediaControls(); } -void UnifiedSystemTrayController::HideMediaControls() { - unified_view_->HideMediaControls(); -} - void UnifiedSystemTrayController::OnMediaControlsViewClicked() { ShowMediaControlsDetailedView(); }
diff --git a/ash/system/unified/unified_system_tray_controller.h b/ash/system/unified/unified_system_tray_controller.h index b633f32..68c99b7a 100644 --- a/ash/system/unified/unified_system_tray_controller.h +++ b/ash/system/unified/unified_system_tray_controller.h
@@ -134,7 +134,6 @@ // UnifedMediaControlsController::Delegate; void ShowMediaControls() override; - void HideMediaControls() override; void OnMediaControlsViewClicked() override; UnifiedSystemTrayModel* model() { return model_; }
diff --git a/ash/system/unified/unified_system_tray_view.cc b/ash/system/unified/unified_system_tray_view.cc index e909239..a064283 100644 --- a/ash/system/unified/unified_system_tray_view.cc +++ b/ash/system/unified/unified_system_tray_view.cc
@@ -292,11 +292,6 @@ PreferredSizeChanged(); } -void UnifiedSystemTrayView::HideMediaControls() { - media_controls_container_->SetShouldShowMediaControls(false); - PreferredSizeChanged(); -} - void UnifiedSystemTrayView::SetDetailedView(views::View* detailed_view) { auto system_tray_size = system_tray_container_->GetPreferredSize(); system_tray_container_->SetVisible(false);
diff --git a/ash/system/unified/unified_system_tray_view.h b/ash/system/unified/unified_system_tray_view.h index 00fe842..f838c30 100644 --- a/ash/system/unified/unified_system_tray_view.h +++ b/ash/system/unified/unified_system_tray_view.h
@@ -130,9 +130,8 @@ // Settings). bool IsDetailedViewShown() const; - // Show and hide media controls view. + // Show media controls view. void ShowMediaControls(); - void HideMediaControls(); // views::View: gfx::Size CalculatePreferredSize() const override;
diff --git a/base/allocator/BUILD.gn b/base/allocator/BUILD.gn index fbf0e7a..2ca6714 100644 --- a/base/allocator/BUILD.gn +++ b/base/allocator/BUILD.gn
@@ -6,13 +6,6 @@ import("//build/buildflag_header.gni") import("//build/config/compiler/compiler.gni") -# This file depends on the legacy global sources assignment filter. It should -# be converted to check target platform before assigning source files to the -# sources variable. Remove this import and set_sources_assignment_filter call -# when the file has been converted. See https://crbug.com/1018739 for details. -import("//build/config/deprecated_default_sources_assignment_filter.gni") -set_sources_assignment_filter(deprecated_default_sources_assignment_filter) - declare_args() { # Provide a way to force disable debugallocation in Debug builds, # e.g. for profiling (it's more rare to profile Debug builds, @@ -95,9 +88,6 @@ # Generated for our configuration from tcmalloc's build # and checked in. "$tcmalloc_dir/src/config.h", - "$tcmalloc_dir/src/config_android.h", - "$tcmalloc_dir/src/config_linux.h", - "$tcmalloc_dir/src/config_win.h", # tcmalloc native and forked files. "$tcmalloc_dir/src/base/abort.cc", @@ -186,6 +176,18 @@ #"win_allocator.cc", ] + if (is_android) { + sources += [ "$tcmalloc_dir/src/config_android.h" ] + } + + if (is_linux || is_chromeos) { + sources += [ "$tcmalloc_dir/src/config_linux.h" ] + } + + if (is_win) { + sources += [ "$tcmalloc_dir/src/config_win.h" ] + } + # Not included on mips64el. if (current_cpu == "mips64el") { sources -= [
diff --git a/base/values.cc b/base/values.cc index c06458bc..4928e830 100644 --- a/base/values.cc +++ b/base/values.cc
@@ -1442,10 +1442,6 @@ list().clear(); } -void ListValue::Reserve(size_t n) { - list().reserve(n); -} - bool ListValue::Set(size_t index, std::unique_ptr<Value> in_value) { if (!in_value) return false;
diff --git a/base/values.h b/base/values.h index a7fba47..00ba152 100644 --- a/base/values.h +++ b/base/values.h
@@ -621,10 +621,10 @@ Value* SetString(StringPiece path, StringPiece in_value); // DEPRECATED, use Value::SetStringPath(). Value* SetString(StringPiece path, const string16& in_value); - // DEPRECATED, use Value::SetPath() or Value::SetDictPath() + // DEPRECATED, use Value::SetPath(). DictionaryValue* SetDictionary(StringPiece path, std::unique_ptr<DictionaryValue> in_value); - // DEPRECATED, use Value::SetPath() or Value::SetListPath() + // DEPRECATED, use Value::SetPath(). ListValue* SetList(StringPiece path, std::unique_ptr<ListValue> in_value); // Like Set(), but without special treatment of '.'. This allows e.g. URLs to @@ -801,11 +801,6 @@ // DEPRECATED, use GetList()::empty() instead. bool empty() const { return list().empty(); } - // Reserves storage for at least |n| values. - // DEPRECATED, first construct a base::Value::ListStorage and use - // base::Value::ListStorage::reserve() instead. - void Reserve(size_t n); - // Sets the list item at the given index to be the Value specified by // the value given. If the index beyond the current end of the list, null // Values will be used to pad out the list.
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1 index 3e92f4f..900c75e9 100644 --- a/build/fuchsia/linux.sdk.sha1 +++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@ -0.20201001.2.1 +0.20201001.3.1
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1 index 3e92f4f..900c75e9 100644 --- a/build/fuchsia/mac.sdk.sha1 +++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@ -0.20201001.2.1 +0.20201001.3.1
diff --git a/cc/benchmarks/rasterize_and_record_benchmark.cc b/cc/benchmarks/rasterize_and_record_benchmark.cc index 3f15604a..5c23cd8 100644 --- a/cc/benchmarks/rasterize_and_record_benchmark.cc +++ b/cc/benchmarks/rasterize_and_record_benchmark.cc
@@ -8,12 +8,12 @@ #include <algorithm> #include <limits> +#include <memory> #include <string> #include "base/bind.h" #include "base/memory/ptr_util.h" #include "base/strings/stringprintf.h" -#include "base/timer/lap_timer.h" #include "base/values.h" #include "cc/benchmarks/rasterize_and_record_benchmark_impl.h" #include "cc/layers/content_layer_client.h" @@ -30,29 +30,6 @@ const int kDefaultRecordRepeatCount = 100; -const char* kModeSuffixes[RecordingSource::RECORDING_MODE_COUNT] = { - "", - "_caching_disabled", - "_subsequence_caching_disabled", - "_partial_invalidation"}; - -ContentLayerClient::PaintingControlSetting -RecordingModeToPaintingControlSetting(RecordingSource::RecordingMode mode) { - switch (mode) { - case RecordingSource::RECORD_NORMALLY: - return ContentLayerClient::PAINTING_BEHAVIOR_NORMAL_FOR_TEST; - case RecordingSource::RECORD_WITH_CACHING_DISABLED: - return ContentLayerClient::DISPLAY_LIST_CACHING_DISABLED; - case RecordingSource::RECORD_WITH_SUBSEQUENCE_CACHING_DISABLED: - return ContentLayerClient::SUBSEQUENCE_CACHING_DISABLED; - case RecordingSource::RECORD_WITH_PARTIAL_INVALIDATION: - return ContentLayerClient::PARTIAL_INVALIDATION; - case RecordingSource::RECORDING_MODE_COUNT: - NOTREACHED(); - } - return ContentLayerClient::PAINTING_BEHAVIOR_NORMAL_FOR_TEST; -} - } // namespace RasterizeAndRecordBenchmark::RasterizeAndRecordBenchmark( @@ -88,6 +65,10 @@ for (auto* layer : *layer_tree_host) layer->RunMicroBenchmark(this); + PaintBenchmarkResult paint_benchmark_result; + layer_tree_host->client()->RunPaintBenchmark(record_repeat_count_, + paint_benchmark_result); + DCHECK(!results_.get()); results_ = base::WrapUnique(new base::DictionaryValue); results_->SetInteger("pixels_recorded", record_results_.pixels_recorded); @@ -97,12 +78,21 @@ static_cast<int>(record_results_.paint_op_memory_usage)); results_->SetInteger("paint_op_count", static_cast<int>(record_results_.paint_op_count)); - - for (int i = 0; i < RecordingSource::RECORDING_MODE_COUNT; i++) { - std::string name = base::StringPrintf("record_time%s_ms", kModeSuffixes[i]); - results_->SetDouble(name, - record_results_.total_best_time[i].InMillisecondsF()); - } + results_->SetDouble("record_time_ms", paint_benchmark_result.record_time_ms); + results_->SetDouble("record_time_caching_disabled_ms", + paint_benchmark_result.record_time_caching_disabled_ms); + results_->SetDouble( + "record_time_subsequence_caching_disabled_ms", + paint_benchmark_result.record_time_subsequence_caching_disabled_ms); + results_->SetDouble( + "record_time_partial_invalidation_ms", + paint_benchmark_result.record_time_partial_invalidation_ms); + results_->SetDouble( + "raster_invalidation_and_convert_time_ms", + paint_benchmark_result.raster_invalidation_and_convert_time_ms); + results_->SetDouble( + "paint_artifact_compositor_update_time_ms", + paint_benchmark_result.paint_artifact_compositor_update_time_ms); main_thread_benchmark_done_ = true; } @@ -134,64 +124,21 @@ if (!layer->DrawsContent()) return; - const int kTimeCheckInterval = 1; - const int kWarmupRuns = 0; - const int kTimeLimitMillis = 1; ContentLayerClient* painter = layer->client(); RecordingSource recording_source; - for (int mode_index = 0; mode_index < RecordingSource::RECORDING_MODE_COUNT; - mode_index++) { - ContentLayerClient::PaintingControlSetting painting_control = - RecordingModeToPaintingControlSetting( - static_cast<RecordingSource::RecordingMode>(mode_index)); - base::TimeDelta min_time = base::TimeDelta::Max(); - size_t paint_op_memory_usage = 0; - size_t paint_op_count = 0; + scoped_refptr<DisplayItemList> display_list; + display_list = painter->PaintContentsToDisplayList(); + recording_source.UpdateDisplayItemList( + display_list, painter->GetApproximateUnsharedMemoryUsage(), + layer_tree_host_->recording_scale_factor()); - scoped_refptr<DisplayItemList> display_list; - for (int i = 0; i < record_repeat_count_; ++i) { - // Run for a minimum amount of time to avoid problems with timer - // quantization when the layer is very small. - base::LapTimer timer(kWarmupRuns, - base::TimeDelta::FromMilliseconds(kTimeLimitMillis), - kTimeCheckInterval); - - do { - display_list = painter->PaintContentsToDisplayList(painting_control); - recording_source.UpdateDisplayItemList( - display_list, painter->GetApproximateUnsharedMemoryUsage(), - layer_tree_host_->recording_scale_factor()); - - if (paint_op_memory_usage) { - // Verify we are recording the same thing each time. - DCHECK_EQ(paint_op_memory_usage, display_list->BytesUsed()); - DCHECK_EQ(paint_op_count, display_list->TotalOpCount()); - } else { - paint_op_memory_usage = display_list->BytesUsed(); - paint_op_count = display_list->TotalOpCount(); - } - - timer.NextLap(); - } while (!timer.HasTimeLimitExpired()); - base::TimeDelta duration = timer.TimePerLap(); - if (duration < min_time) - min_time = duration; - } - - if (mode_index == RecordingSource::RECORD_NORMALLY) { - record_results_.painter_memory_usage += - painter->GetApproximateUnsharedMemoryUsage(); - record_results_.paint_op_memory_usage += paint_op_memory_usage; - record_results_.paint_op_count += paint_op_count; - record_results_.pixels_recorded += painter->PaintableRegion().width() * - painter->PaintableRegion().height(); - } - record_results_.total_best_time[mode_index] += min_time; - } + record_results_.painter_memory_usage += + painter->GetApproximateUnsharedMemoryUsage(); + record_results_.paint_op_memory_usage += display_list->BytesUsed(); + record_results_.paint_op_count += display_list->TotalOpCount(); + record_results_.pixels_recorded += + painter->PaintableRegion().width() * painter->PaintableRegion().height(); } -RasterizeAndRecordBenchmark::RecordResults::RecordResults() = default; -RasterizeAndRecordBenchmark::RecordResults::~RecordResults() = default; - } // namespace cc
diff --git a/cc/benchmarks/rasterize_and_record_benchmark.h b/cc/benchmarks/rasterize_and_record_benchmark.h index 8ca7c588..f66314f3 100644 --- a/cc/benchmarks/rasterize_and_record_benchmark.h +++ b/cc/benchmarks/rasterize_and_record_benchmark.h
@@ -8,6 +8,7 @@ #include <stddef.h> #include <map> +#include <memory> #include <utility> #include <vector> @@ -42,14 +43,10 @@ void RecordRasterResults(std::unique_ptr<base::Value> results); struct RecordResults { - RecordResults(); - ~RecordResults(); - int pixels_recorded = 0; size_t painter_memory_usage = 0; size_t paint_op_memory_usage = 0; size_t paint_op_count = 0; - base::TimeDelta total_best_time[RecordingSource::RECORDING_MODE_COUNT]; }; RecordResults record_results_;
diff --git a/cc/layers/content_layer_client.h b/cc/layers/content_layer_client.h index 285816c..e782c5b 100644 --- a/cc/layers/content_layer_client.h +++ b/cc/layers/content_layer_client.h
@@ -18,14 +18,6 @@ class CC_EXPORT ContentLayerClient { public: - enum PaintingControlSetting { - PAINTING_BEHAVIOR_NORMAL, - PAINTING_BEHAVIOR_NORMAL_FOR_TEST, - DISPLAY_LIST_CACHING_DISABLED, - SUBSEQUENCE_CACHING_DISABLED, - PARTIAL_INVALIDATION, - }; - // The paintable region is the rectangular region, within the bounds of the // layer this client paints, that the client is capable of painting via // paintContents(). Calling paintContents() will return a DisplayItemList @@ -36,8 +28,7 @@ // to the layer itself, into a DisplayItemList that it returns. The // PaintingControlSetting enum controls painting to isolate different // components in performance tests. - virtual scoped_refptr<DisplayItemList> PaintContentsToDisplayList( - PaintingControlSetting painting_control) = 0; + virtual scoped_refptr<DisplayItemList> PaintContentsToDisplayList() = 0; // If true the layer may skip clearing the background before rasterizing, // because it will cover any uncleared data with content.
diff --git a/cc/layers/picture_layer.cc b/cc/layers/picture_layer.cc index 852c77d..e4a53b29 100644 --- a/cc/layers/picture_layer.cc +++ b/cc/layers/picture_layer.cc
@@ -141,8 +141,7 @@ { auto old_display_list = std::move(picture_layer_inputs_.display_list); picture_layer_inputs_.display_list = - picture_layer_inputs_.client->PaintContentsToDisplayList( - ContentLayerClient::PAINTING_BEHAVIOR_NORMAL); + picture_layer_inputs_.client->PaintContentsToDisplayList(); if (old_display_list && picture_layer_inputs_.display_list ->NeedsAdditionalInvalidationForLCDText(*old_display_list)) { @@ -190,8 +189,7 @@ return nullptr; scoped_refptr<DisplayItemList> display_list = - picture_layer_inputs_.client->PaintContentsToDisplayList( - ContentLayerClient::PAINTING_BEHAVIOR_NORMAL); + picture_layer_inputs_.client->PaintContentsToDisplayList(); SkPictureRecorder recorder; SkCanvas* canvas = recorder.beginRecording(bounds().width(), bounds().height());
diff --git a/cc/layers/picture_layer_impl_unittest.cc b/cc/layers/picture_layer_impl_unittest.cc index 627e929..3b000ca9 100644 --- a/cc/layers/picture_layer_impl_unittest.cc +++ b/cc/layers/picture_layer_impl_unittest.cc
@@ -4724,8 +4724,7 @@ gfx::Size layer_bounds) { gfx::Rect new_recorded_viewport = client->PaintableRegion(); scoped_refptr<DisplayItemList> display_list = - client->PaintContentsToDisplayList( - ContentLayerClient::PAINTING_BEHAVIOR_NORMAL); + client->PaintContentsToDisplayList(); size_t painter_reported_memory_usage = client->GetApproximateUnsharedMemoryUsage();
diff --git a/cc/metrics/frame_sequence_metrics.cc b/cc/metrics/frame_sequence_metrics.cc index d6154c69..dd025972 100644 --- a/cc/metrics/frame_sequence_metrics.cc +++ b/cc/metrics/frame_sequence_metrics.cc
@@ -354,37 +354,13 @@ void FrameSequenceMetrics::ComputeJank( FrameSequenceMetrics::ThreadType thread_type, - uint32_t frame_token, base::TimeTicks presentation_time, base::TimeDelta frame_interval) { if (!jank_reporter_) return; if (thread_type == jank_reporter_->thread_type()) - jank_reporter_->AddPresentedFrame(frame_token, presentation_time, - frame_interval); -} - -void FrameSequenceMetrics::NotifySubmitForJankReporter( - FrameSequenceMetrics::ThreadType thread_type, - uint32_t frame_token, - uint32_t sequence_number) { - if (!jank_reporter_) - return; - - if (thread_type == jank_reporter_->thread_type()) - jank_reporter_->AddSubmitFrame(frame_token, sequence_number); -} - -void FrameSequenceMetrics::NotifyNoUpdateForJankReporter( - FrameSequenceMetrics::ThreadType thread_type, - uint32_t sequence_number, - base::TimeDelta frame_interval) { - if (!jank_reporter_) - return; - - if (thread_type == jank_reporter_->thread_type()) - jank_reporter_->AddFrameWithNoUpdate(sequence_number, frame_interval); + jank_reporter_->AddPresentedFrame(presentation_time, frame_interval); } bool FrameSequenceMetrics::ThroughputData::CanReportHistogram(
diff --git a/cc/metrics/frame_sequence_metrics.h b/cc/metrics/frame_sequence_metrics.h index b7a3313..13ab2823 100644 --- a/cc/metrics/frame_sequence_metrics.h +++ b/cc/metrics/frame_sequence_metrics.h
@@ -150,19 +150,9 @@ void AdvanceTrace(base::TimeTicks timestamp); void ComputeJank(FrameSequenceMetrics::ThreadType thread_type, - uint32_t frame_token, base::TimeTicks presentation_time, base::TimeDelta frame_interval); - void NotifySubmitForJankReporter(FrameSequenceMetrics::ThreadType thread_type, - uint32_t frame_token, - uint32_t sequence_number); - - void NotifyNoUpdateForJankReporter( - FrameSequenceMetrics::ThreadType thread_type, - uint32_t sequence_number, - base::TimeDelta frame_interval); - private: const FrameSequenceTrackerType type_;
diff --git a/cc/metrics/frame_sequence_tracker.cc b/cc/metrics/frame_sequence_tracker.cc index 03b59ce..b05d5a2 100644 --- a/cc/metrics/frame_sequence_tracker.cc +++ b/cc/metrics/frame_sequence_tracker.cc
@@ -276,9 +276,6 @@ TRACKER_TRACE_STREAM << "s(" << frame_token % kDebugStrMod << ")"; had_impl_frame_submitted_between_commits_ = true; - metrics()->NotifySubmitForJankReporter( - FrameSequenceMetrics::ThreadType::kCompositor, frame_token, - ack.frame_id.sequence_number); const bool main_changes_after_sequence_started = first_received_main_sequence_ && @@ -301,9 +298,6 @@ << origin_args.frame_id.sequence_number % kDebugStrMod << ")"; - metrics()->NotifySubmitForJankReporter( - FrameSequenceMetrics::ThreadType::kMain, frame_token, - origin_args.frame_id.sequence_number); last_submitted_main_sequence_ = origin_args.frame_id.sequence_number; main_frames_.push_back(frame_token); @@ -364,9 +358,6 @@ impl_throughput().frames_ontime) << TRACKER_DCHECK_MSG; --impl_throughput().frames_expected; - metrics()->NotifyNoUpdateForJankReporter( - FrameSequenceMetrics::ThreadType::kCompositor, - args.frame_id.sequence_number, args.interval); #if DCHECK_IS_ON() ++impl_throughput().frames_processed; // If these two are the same, it means that each impl frame is either @@ -470,7 +461,7 @@ } metrics()->ComputeJank(FrameSequenceMetrics::ThreadType::kCompositor, - frame_token, feedback.timestamp, feedback.interval); + feedback.timestamp, feedback.interval); } if (was_presented) { @@ -492,8 +483,7 @@ } metrics()->ComputeJank(FrameSequenceMetrics::ThreadType::kMain, - frame_token, feedback.timestamp, - feedback.interval); + feedback.timestamp, feedback.interval); } if (main_frames_.size() < size_before_erase) { if (!last_frame_presentation_timestamp_.is_null() && @@ -608,10 +598,6 @@ << TRACKER_DCHECK_MSG; last_no_main_damage_sequence_ = args.frame_id.sequence_number; --main_throughput().frames_expected; - metrics()->NotifyNoUpdateForJankReporter( - FrameSequenceMetrics::ThreadType::kMain, args.frame_id.sequence_number, - args.interval); - DCHECK_GE(main_throughput().frames_expected, main_frames_.size()) << TRACKER_DCHECK_MSG;
diff --git a/cc/metrics/jank_metrics.cc b/cc/metrics/jank_metrics.cc index 02fb65d..1164269c 100644 --- a/cc/metrics/jank_metrics.cc +++ b/cc/metrics/jank_metrics.cc
@@ -67,90 +67,19 @@ } JankMetrics::~JankMetrics() = default; -void JankMetrics::AddSubmitFrame(uint32_t frame_token, - uint32_t sequence_number) { - // When a frame is submitted, record its |frame_token| and its associated - // |sequence_number|. This pushed item will be removed when this frame is - // presented. - queue_frame_token_and_id_.push({frame_token, sequence_number}); -} - -void JankMetrics::AddFrameWithNoUpdate(uint32_t sequence_number, - base::TimeDelta frame_interval) { - // If a frame does not cause an increase in expected frames, it will be - // recorded here and later subtracted from the presentation interval that - // includes this frame. - queue_frame_id_and_interval_.push({sequence_number, frame_interval}); -} - void JankMetrics::AddPresentedFrame( - uint32_t presented_frame_token, base::TimeTicks current_presentation_timestamp, base::TimeDelta frame_interval) { - uint32_t presented_frame_id = 0; + base::TimeDelta current_frame_delta = + current_presentation_timestamp - last_presentation_timestamp_; - // Find the main_sequence_number of the presented_frame_token - while (!queue_frame_token_and_id_.empty()) { - auto token_and_id = queue_frame_token_and_id_.front(); - - if (token_and_id.first > presented_frame_token) { - // The submitting of this presented frame was not recorded (e.g. the - // submitting might have occurred before JankMetrics starts recording). - // In that case, do not use this frame presentation for jank detection. - return; - } - queue_frame_token_and_id_.pop(); - - if (token_and_id.first == presented_frame_token) { - // Found information about the submit of this presented frame; - // retrieve the frame's sequence number. - presented_frame_id = token_and_id.second; - break; - } - } - // If for any reason the sequence number associated with the - // presented_frame_token cannot be identified, then ignore this frame - // presentation. - if (presented_frame_id == 0) - return; - - base::TimeDelta no_update_time; // The frame time spanned by the frames that - // have no updates - - // Compute the presentation delay contributed by no-update frames that began - // BEFORE (i.e. have smaller sequence number than) the current presented - // frame. - while (!queue_frame_id_and_interval_.empty() && - queue_frame_id_and_interval_.front().first < presented_frame_id) { - auto id_and_interval = queue_frame_id_and_interval_.front(); - if (id_and_interval.first >= last_presentation_frame_id_) { - // Only count no-update frames that began SINCE (i.e. have a greater [or - // equal] sequence number than) the beginning of previous presented frame. - // If, in rare cases, there are still no-update frames that began BEFORE - // the beginning of previous presented frame left in the queue, those - // frames will simply be discarded and not counted into |no_update_time|. - no_update_time += id_and_interval.second; - } - queue_frame_id_and_interval_.pop(); - } - - // Exclude the presentation delay introduced by no-update frames. If this - // exclusion results in negative frame delta, treat the frame delta as 0. - base::TimeDelta current_frame_delta = current_presentation_timestamp - - last_presentation_timestamp_ - - no_update_time; - if (current_frame_delta < base::TimeDelta::FromMilliseconds(0)) - current_frame_delta = base::TimeDelta::FromMilliseconds(0); - - // Only start tracking jank if this function has already been - // called at least once (so that |last_presentation_timestamp_| - // and |prev_frame_delta_| have been set). + // Only start tracking jank if this function has been called (so that + // |last_presentation_timestamp_| and |prev_frame_delta_| have been set). // - // The presentation interval is typically a multiple of VSync - // intervals (i.e. 16.67ms, 33.33ms, 50ms ... on a 60Hz display) - // with small fluctuations. The 0.5 * |frame_interval| criterion - // is chosen so that the jank detection is robust to those - // fluctuations. + // The presentation interval is typically a multiple of VSync intervals (i.e. + // 16.67ms, 33.33ms, 50ms ... on a 60Hz display) with small fluctuations. The + // 0.5 * |frame_interval| criterion is chosen so that the jank detection is + // robust to those fluctuations. if (!last_presentation_timestamp_.is_null() && !prev_frame_delta_.is_zero() && current_frame_delta > prev_frame_delta_ + 0.5 * frame_interval) { jank_count_++; @@ -165,7 +94,7 @@ FrameSequenceTracker::GetFrameSequenceTrackerTypeName(tracker_type_)); } last_presentation_timestamp_ = current_presentation_timestamp; - last_presentation_frame_id_ = presented_frame_id; + prev_frame_delta_ = current_frame_delta; }
diff --git a/cc/metrics/jank_metrics.h b/cc/metrics/jank_metrics.h index 09c6cff..71aca6b 100644 --- a/cc/metrics/jank_metrics.h +++ b/cc/metrics/jank_metrics.h
@@ -6,8 +6,6 @@ #define CC_METRICS_JANK_METRICS_H_ #include <memory> -#include <queue> -#include <utility> #include "cc/metrics/frame_sequence_metrics.h" @@ -23,8 +21,7 @@ // Check if a jank occurs based on the timestamps of recent presentations. // If there is a jank, increment |jank_count_| and log a trace event. - void AddPresentedFrame(uint32_t presented_frame_token, - base::TimeTicks current_presentation_timestamp, + void AddPresentedFrame(base::TimeTicks current_presentation_timestamp, base::TimeDelta frame_interval); // Report the occurrence rate of janks as a UMA metric. @@ -33,10 +30,6 @@ // Merge the current jank count with a previously unreported jank metrics. void Merge(std::unique_ptr<JankMetrics> jank_metrics); - void AddSubmitFrame(uint32_t frame_token, uint32_t sequence_number); - - void AddFrameWithNoUpdate(uint32_t sequence_number, - base::TimeDelta frame_interval); FrameSequenceMetrics::ThreadType thread_type() const { return effective_thread_; } @@ -55,19 +48,8 @@ // The time when the last presentation occurs base::TimeTicks last_presentation_timestamp_; - // The sequence number associated with the last presented frame - uint32_t last_presentation_frame_id_; - // The interval before the previous frame presentation. base::TimeDelta prev_frame_delta_; - - // A queue storing {frame token, sequence number} for all submitted - // frames, in ascending order of frame token. - std::queue<std::pair<uint32_t, uint32_t>> queue_frame_token_and_id_; - - // A queue storing {sequence number, frame interval} of unprocessed no-update - // frames, in ascending order of sequence number. - std::queue<std::pair<uint32_t, base::TimeDelta>> queue_frame_id_and_interval_; }; } // namespace cc
diff --git a/cc/metrics/jank_metrics_unittest.cc b/cc/metrics/jank_metrics_unittest.cc index 18c23ec..e96e7dd 100644 --- a/cc/metrics/jank_metrics_unittest.cc +++ b/cc/metrics/jank_metrics_unittest.cc
@@ -6,7 +6,6 @@ #include <memory> #include <string> -#include <unordered_map> #include <utility> #include <vector> @@ -20,16 +19,6 @@ #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" -namespace { -const base::TimeDelta kDefaultFrameInterval = - base::TimeDelta::FromMillisecondsD(16.67); - -// All sequence numbers for simulated frame events will start at this number. -// This makes it easier to numerically distinguish sequence numbers versus -// frame tokens, which always start at 1. -const uint32_t kSequenceNumberStartsAt = 100u; -} // namespace - namespace cc { class JankMetricsTest : public testing::Test { @@ -37,99 +26,42 @@ JankMetricsTest() = default; ~JankMetricsTest() override = default; - // Simulate a series of Submit, NoUpdate, and Presentation events and notify - // |jank_reporter|, as specified by |frame_sequences|. The exact presentation - // time of frames can be slightly manipulated by |presentation_time_shifts|. - void SimulateFrameSequence( - JankMetrics* jank_reporter, - const std::array<std::string, 3>& frame_sequences, - const std::unordered_map<char, double>& presentation_time_shifts = {}) { - // |frame_sequences| is an array of 3 strings of EQUAL LENGTH, representing - // the (S)UBMIT, (N)O-UPDATE, (P)RESENTATION events, respectively. In all 3 - // strings: - // any char == a vsync interval (16.67ms) with a unique sequence number. - // '-' == no event in this vsync interval. - // In SUBMIT string: - // [a-zA-Z] == A SUBMIT occurs at this vsync interval. Each symbol in this - // string must be unique. - // In NO-UPDATE string: - // Any non '-' letter == A NO-UPDATE frame is reported at this vsync - // interval. - // In PRESENTATION string: - // [a-zA-Z] == A PRESENTATION occurs at this vsync interval. Each - // symbol must be unique and MUST HAVE APPEARED in the SUBMIT string. - // - // NOTE this test file stylistically denotes the frames that should jank - // with uppercases (although this is not a strict). - // - // Each item in |presentation_time_shifts| maps a presentation frame letter - // (must have appeared in string P) to how much time (in ms) this - // presentation deviates from expected. For example: {'a': -3.2} means frame - // 'a' is presented 3.2ms before expected. - // - // e.g. - // S = "a-b--c--D--" - // N = "---**------" - // P = "-a-b---c-D-" - // presentation_time_shifts = {'c':-8.4, 'D':8.4} - // - // means submit at vsync 0, 2, 5, 8, presentation at 1, 3, 7, 9. Due to the - // no-update frames 3 and 4, no janks will be reported for 'c'. However, the - // large fluctuation of presentation time of 'c' and 'D', there is a jank - // at 'D'. - // - // Without the no-update frames and presentation_time_shifts, one jank would - // have been reported at 'c'. - auto& submits = frame_sequences[0]; - auto& ignores = frame_sequences[1]; - auto& presnts = frame_sequences[2]; + // Create a sequence of PresentationFeedback for testing based on the provided + // sequence of actual frame intervals and the expected frame interval. The + // size of the returned sequence is |actual_intervals_ms|.size() + 1 + static std::vector<gfx::PresentationFeedback> CreateFeedbackSequence( + const std::vector<double>& actual_intervals_ms, + double expected_interval_ms) { + std::vector<gfx::PresentationFeedback> feedbacks; - // All three sequences must have the same size. - EXPECT_EQ(submits.size(), ignores.size()); - EXPECT_EQ(submits.size(), presnts.size()); - - // Map submitted frame to their tokens - std::unordered_map<char, uint32_t> submit_to_token; - + // The timestamp of the first presentation. base::TimeTicks start_time = base::TimeTicks::Now(); + double accum_interval = 0.0; + base::TimeDelta expected_interval = + base::TimeDelta::FromMillisecondsD(expected_interval_ms); - // Scan S to collect all symbols - for (uint32_t frame_token = 1, i = 0; i < submits.size(); ++i) { - uint32_t sequence_number = kSequenceNumberStartsAt + i; - if (submits[i] != '-') { - submit_to_token[submits[i]] = frame_token; - jank_reporter->AddSubmitFrame(/*frame_token=*/frame_token, - /*sequence_number=*/sequence_number); - frame_token++; - } + feedbacks.emplace_back( + gfx::PresentationFeedback(start_time, expected_interval, 0)); + for (auto interval : actual_intervals_ms) { + accum_interval += interval; + feedbacks.emplace_back(gfx::PresentationFeedback{ + start_time + base::TimeDelta::FromMillisecondsD(accum_interval), + expected_interval, 0}); + } + return feedbacks; + } - if (ignores[i] != '-') { - jank_reporter->AddFrameWithNoUpdate( - /*sequence_number=*/sequence_number, - /*frame_interval=*/kDefaultFrameInterval); - } - - if (presnts[i] != '-') { - // The present frame must have been previously submitted - EXPECT_EQ(submit_to_token.count(presnts[i]), 1u); - - double presentation_offset = 0.0; // ms - if (presentation_time_shifts.count(presnts[i])) - presentation_offset = presentation_time_shifts.at(presnts[i]); - - jank_reporter->AddPresentedFrame( - /*presented_frame_token=*/submit_to_token[presnts[i]], - /*current_presentation_timestamp=*/start_time + - i * kDefaultFrameInterval + - base::TimeDelta::FromMillisecondsD(presentation_offset), - /*frame_interval=*/kDefaultFrameInterval); - submit_to_token.erase(presnts[i]); - } + // Notify |jank_reporter| of all presentations in |feedbacks|. + void AddPresentedFramesToJankReporter( + JankMetrics* jank_reporter, + const std::vector<gfx::PresentationFeedback>& feedbacks) { + for (auto feedback : feedbacks) { + jank_reporter->AddPresentedFrame(feedback.timestamp, feedback.interval); } } }; -TEST_F(JankMetricsTest, CompositorAnimationOneJankWithMildFluctuation) { +TEST_F(JankMetricsTest, CompositorAnimationMildFluctuationNoJank) { base::HistogramTester histogram_tester; FrameSequenceTrackerType tracker_type = FrameSequenceTrackerType::kCompositorAnimation; @@ -137,18 +69,14 @@ FrameSequenceMetrics::ThreadType::kCompositor; JankMetrics jank_reporter{tracker_type, thread_type}; - // One Jank; there are no no-update frames. The fluctuation in presentation of - // 'd' is not big enough to cause another jank. - SimulateFrameSequence(&jank_reporter, - { - /*submit */ "ab-C-d", - /*noupdate */ "------", - /*present */ "ab-C-d", - }, - {{'d', +8.0 /*ms*/}}); + // No jank. Small upticks such as 15->17 or 14->18 do not qualify as janks. + auto feedbacks = + CreateFeedbackSequence({16.67, 16.67, 15, 17, 14, 18, 15, 16.67}, 16.67); + + AddPresentedFramesToJankReporter(&jank_reporter, feedbacks); jank_reporter.ReportJankMetrics(100u); - // One sample of 1 janks reported for "Compositor". + // One sample of 0 janks reported for "Compositor". const char* metric = "Graphics.Smoothness.Jank.Compositor.CompositorAnimation"; const char* invalid_metric = @@ -156,13 +84,13 @@ histogram_tester.ExpectTotalCount(metric, 1u); EXPECT_THAT(histogram_tester.GetAllSamples(metric), - testing::ElementsAre(base::Bucket(1, 1))); + testing::ElementsAre(base::Bucket(0, 1))); // No reporting for "Main". histogram_tester.ExpectTotalCount(invalid_metric, 0u); } -TEST_F(JankMetricsTest, MainThreadAnimationOneJankWithNoUpdate) { +TEST_F(JankMetricsTest, MainThreadAnimationOneJank) { base::HistogramTester histogram_tester; FrameSequenceTrackerType tracker_type = FrameSequenceTrackerType::kMainThreadAnimation; @@ -170,13 +98,13 @@ FrameSequenceMetrics::ThreadType::kMain; JankMetrics jank_reporter{tracker_type, thread_type}; - // There are only 1 jank because of a no-update frame. + // One Main thread jank from 15 to 24, since 24 - 15 = 9, which is greater + // then 0.5 * frame_interval = 8.33. The jank occurrence is visually marked + // with a "+" sign. + auto feedbacks = + CreateFeedbackSequence({48, 15, +24, 14, 18, 15, 16.67}, 16.67); - SimulateFrameSequence(&jank_reporter, { - /*submit */ "ab-c--D", - /*noupdate */ "--*----", - /*present */ "ab-c--D", - }); + AddPresentedFramesToJankReporter(&jank_reporter, feedbacks); jank_reporter.ReportJankMetrics(100u); // One jank is reported for "Main". @@ -200,13 +128,10 @@ JankMetrics jank_reporter{tracker_type, thread_type}; // 7 janks. - SimulateFrameSequence(&jank_reporter, - { - /*submit */ "ab-C--DeFGh-IJk---L---------", - /*noupdate */ "----------------------------", - /*present */ "---ab-C--De-F--Gh-I---Jk---L", - }); + auto feedbacks = CreateFeedbackSequence( + {15, +33, +50, 33, 16, +33, +50, +100, +120, +180}, 16.67); + AddPresentedFramesToJankReporter(&jank_reporter, feedbacks); jank_reporter.ReportJankMetrics(300u); // Report in the 7/300 ~= 2% bucket for "Compositor" @@ -221,20 +146,17 @@ histogram_tester.ExpectTotalCount(invalid_metric, 0u); } -TEST_F(JankMetricsTest, WheelScrollMainThreadNoJanksWithNoUpdates) { +TEST_F(JankMetricsTest, WheelScrollMainThreadTwoJanks) { base::HistogramTester histogram_tester; FrameSequenceTrackerType tracker_type = FrameSequenceTrackerType::kWheelScroll; FrameSequenceMetrics::ThreadType thread_type = FrameSequenceMetrics::ThreadType::kMain; + JankMetrics jank_reporter{tracker_type, thread_type}; - SimulateFrameSequence(&jank_reporter, - { - /*submit */ "ab-c--d------e---------f-", - /*noupdate */ "--*-**-******-*********--", - /*present */ "---ab-c-d-----e---------f", - }); + auto feedbacks = CreateFeedbackSequence({33, 16, +33, +48, 33}, 16.67); + AddPresentedFramesToJankReporter(&jank_reporter, feedbacks); jank_reporter.ReportJankMetrics(100u); // Expect 2 janks for "Main" and no jank for "Compositor" @@ -244,43 +166,12 @@ histogram_tester.ExpectTotalCount(metric, 1u); EXPECT_THAT(histogram_tester.GetAllSamples(metric), - testing::ElementsAre(base::Bucket(0, 1))); - - histogram_tester.ExpectTotalCount(invalid_metric, 0u); -} - -TEST_F(JankMetricsTest, WheelScrollCompositorTwoJanksWithLargeFluctuation) { - base::HistogramTester histogram_tester; - FrameSequenceTrackerType tracker_type = - FrameSequenceTrackerType::kWheelScroll; - FrameSequenceMetrics::ThreadType thread_type = - FrameSequenceMetrics::ThreadType::kCompositor; - JankMetrics jank_reporter{tracker_type, thread_type}; - - // Two janks; there are no no-update frames. The fluctuations in presentation - // of 'C' and 'D' are just big enough to cause another jank. - SimulateFrameSequence(&jank_reporter, - { - /*submit */ "ab-C-D", - /*noupdate */ "------", - /*present */ "ab-C-D", - }, - {{'C', -2.0 /*ms*/}, {'D', +7.0 /*ms*/}}); - jank_reporter.ReportJankMetrics(100u); - - // One sample of 2 janks reported for "Compositor". - const char* metric = "Graphics.Smoothness.Jank.Compositor.WheelScroll"; - const char* invalid_metric = "Graphics.Smoothness.Jank.Main.WheelScroll"; - - histogram_tester.ExpectTotalCount(metric, 1u); - EXPECT_THAT(histogram_tester.GetAllSamples(metric), testing::ElementsAre(base::Bucket(2, 1))); - // No reporting for "Main". histogram_tester.ExpectTotalCount(invalid_metric, 0u); } -TEST_F(JankMetricsTest, TouchScrollCompositorThreadManyJanksLongLatency) { +TEST_F(JankMetricsTest, TouchScrollCompositorThreadManyJanks) { base::HistogramTester histogram_tester; FrameSequenceTrackerType tracker_type = FrameSequenceTrackerType::kTouchScroll; @@ -289,17 +180,14 @@ JankMetrics jank_reporter{tracker_type, thread_type}; - // There are long delays from submit to presentations. - SimulateFrameSequence( - &jank_reporter, { - /*submit */ "abB-c--D--EFgH----------------------", - /*noupdate */ "---*--------------------------------", - /*present */ "----------ab-B--c---D----E-----Fg--H", - }); + auto feedbacks = + CreateFeedbackSequence({33, 16, +33, +48, +100, 16, +48, +100}, 16.67); + + AddPresentedFramesToJankReporter(&jank_reporter, feedbacks); jank_reporter.ReportJankMetrics(120u); - // Expect janks in the 5/120 ~= 4% bucket for "Compositor", and no jank - // for "Main" + // Expect janks in the 5/120 ~= 4% bucket for "Compositor", and no jank for + // "Main" const char* metric = "Graphics.Smoothness.Jank.Compositor.TouchScroll"; const char* invalid_metric = "Graphics.Smoothness.Jank.Main.TouchScroll"; @@ -322,13 +210,9 @@ std::unique_ptr<JankMetrics> other_reporter = std::make_unique<JankMetrics>(tracker_type, thread_type); - std::array<std::string, 3> seqs = { - /*submit */ "a-b-Cd-e-F--D-", - /*noupdate */ "-*----*-------", - /*present */ "-a-b-Cd-e-F--D", - }; - SimulateFrameSequence(&jank_reporter, seqs); - SimulateFrameSequence(other_reporter.get(), seqs); + auto feedbacks = CreateFeedbackSequence({33, +50, 16, +33, 33, +48}, 16.67); + AddPresentedFramesToJankReporter(other_reporter.get(), feedbacks); + AddPresentedFramesToJankReporter(&jank_reporter, feedbacks); jank_reporter.Merge(std::move(other_reporter)); jank_reporter.ReportJankMetrics(100u); @@ -354,11 +238,9 @@ // There should be 4 janks, but the jank reporter does not track or report // them. - SimulateFrameSequence(&jank_reporter, { - /*submit */ "ab-C--D---E----F", - /*noupdate */ "----------------", - /*present */ "ab-C--D---E----F", - }); + auto feedbacks = CreateFeedbackSequence({16, +33, +48, 16, +33, +48}, 16.67); + + AddPresentedFramesToJankReporter(&jank_reporter, feedbacks); jank_reporter.ReportJankMetrics(100u); // Expect no jank reports even though the sequence contains jank
diff --git a/cc/paint/discardable_image_map_unittest.cc b/cc/paint/discardable_image_map_unittest.cc index a334098..4675f29 100644 --- a/cc/paint/discardable_image_map_unittest.cc +++ b/cc/paint/discardable_image_map_unittest.cc
@@ -131,8 +131,7 @@ } scoped_refptr<DisplayItemList> display_list = - content_layer_client.PaintContentsToDisplayList( - ContentLayerClient::PAINTING_BEHAVIOR_NORMAL); + content_layer_client.PaintContentsToDisplayList(); display_list->GenerateDiscardableImagesMetadata(); const DiscardableImageMap& image_map = display_list->discardable_image_map(); @@ -204,8 +203,7 @@ } scoped_refptr<DisplayItemList> display_list = - content_layer_client.PaintContentsToDisplayList( - ContentLayerClient::PAINTING_BEHAVIOR_NORMAL); + content_layer_client.PaintContentsToDisplayList(); display_list->GenerateDiscardableImagesMetadata(); const DiscardableImageMap& image_map = display_list->discardable_image_map(); @@ -306,8 +304,7 @@ } scoped_refptr<DisplayItemList> display_list = - content_layer_client.PaintContentsToDisplayList( - ContentLayerClient::PAINTING_BEHAVIOR_NORMAL); + content_layer_client.PaintContentsToDisplayList(); display_list->GenerateDiscardableImagesMetadata(); const DiscardableImageMap& image_map = display_list->discardable_image_map(); @@ -342,8 +339,7 @@ flags); scoped_refptr<DisplayItemList> display_list = - content_layer_client.PaintContentsToDisplayList( - ContentLayerClient::PAINTING_BEHAVIOR_NORMAL); + content_layer_client.PaintContentsToDisplayList(); display_list->GenerateDiscardableImagesMetadata(); const DiscardableImageMap& image_map = display_list->discardable_image_map(); @@ -480,8 +476,7 @@ flags); scoped_refptr<DisplayItemList> display_list = - content_layer_client.PaintContentsToDisplayList( - ContentLayerClient::PAINTING_BEHAVIOR_NORMAL); + content_layer_client.PaintContentsToDisplayList(); display_list->GenerateDiscardableImagesMetadata(); const DiscardableImageMap& image_map = display_list->discardable_image_map(); @@ -525,8 +520,7 @@ gfx::Point(-10000, 500), flags); scoped_refptr<DisplayItemList> display_list = - content_layer_client.PaintContentsToDisplayList( - ContentLayerClient::PAINTING_BEHAVIOR_NORMAL); + content_layer_client.PaintContentsToDisplayList(); display_list->GenerateDiscardableImagesMetadata(); const DiscardableImageMap& image_map = display_list->discardable_image_map(); @@ -575,8 +569,7 @@ gfx::Point(-100, 500), flags); scoped_refptr<DisplayItemList> display_list = - content_layer_client.PaintContentsToDisplayList( - ContentLayerClient::PAINTING_BEHAVIOR_NORMAL); + content_layer_client.PaintContentsToDisplayList(); display_list->GenerateDiscardableImagesMetadata(); const DiscardableImageMap& image_map = display_list->discardable_image_map(); @@ -641,8 +634,7 @@ } scoped_refptr<DisplayItemList> display_list = - content_layer_client.PaintContentsToDisplayList( - ContentLayerClient::PAINTING_BEHAVIOR_NORMAL); + content_layer_client.PaintContentsToDisplayList(); display_list->GenerateDiscardableImagesMetadata(); const DiscardableImageMap& image_map = display_list->discardable_image_map(); @@ -766,8 +758,7 @@ gfx::Point(200, 200), flags); scoped_refptr<DisplayItemList> display_list = - content_layer_client.PaintContentsToDisplayList( - ContentLayerClient::PAINTING_BEHAVIOR_NORMAL); + content_layer_client.PaintContentsToDisplayList(); display_list->GenerateDiscardableImagesMetadata(); const auto& animated_images_metadata = display_list->discardable_image_map().animated_images_metadata(); @@ -811,8 +802,7 @@ flags); scoped_refptr<DisplayItemList> display_list = - content_layer_client.PaintContentsToDisplayList( - ContentLayerClient::PAINTING_BEHAVIOR_NORMAL); + content_layer_client.PaintContentsToDisplayList(); display_list->GenerateDiscardableImagesMetadata(); const auto& paint_worklet_inputs = @@ -1010,8 +1000,7 @@ content_layer_client.add_draw_image(sync_image, gfx::Point(20, 20), PaintFlags()); scoped_refptr<DisplayItemList> display_list = - content_layer_client.PaintContentsToDisplayList( - ContentLayerClient::PAINTING_BEHAVIOR_NORMAL); + content_layer_client.PaintContentsToDisplayList(); display_list->GenerateDiscardableImagesMetadata(); auto decode_hints = display_list->TakeDecodingModeMap(); ASSERT_EQ(decode_hints.size(), 3u); @@ -1082,8 +1071,7 @@ content_layer_client.add_draw_image(sync_image3, gfx::Point(50, 50), PaintFlags()); scoped_refptr<DisplayItemList> display_list = - content_layer_client.PaintContentsToDisplayList( - ContentLayerClient::PAINTING_BEHAVIOR_NORMAL); + content_layer_client.PaintContentsToDisplayList(); display_list->GenerateDiscardableImagesMetadata(); auto decode_hints = display_list->TakeDecodingModeMap(); @@ -1117,8 +1105,7 @@ content_layer_client.add_draw_image(image, gfx::Point(400, 400), flags); scoped_refptr<DisplayItemList> display_list = - content_layer_client.PaintContentsToDisplayList( - ContentLayerClient::PAINTING_BEHAVIOR_NORMAL); + content_layer_client.PaintContentsToDisplayList(); display_list->GenerateDiscardableImagesMetadata(); const auto& image_map = display_list->discardable_image_map(); @@ -1154,16 +1141,14 @@ content_layer_client.set_bounds(visible_rect.size()); scoped_refptr<DisplayItemList> display_list = - content_layer_client.PaintContentsToDisplayList( - ContentLayerClient::PAINTING_BEHAVIOR_NORMAL); + content_layer_client.PaintContentsToDisplayList(); display_list->GenerateDiscardableImagesMetadata(); const DiscardableImageMap& image_map = display_list->discardable_image_map(); EXPECT_FALSE(image_map.contains_hbd_images()); content_layer_client.add_draw_image(discardable_image, gfx::Point(0, 0), PaintFlags()); - display_list = content_layer_client.PaintContentsToDisplayList( - ContentLayerClient::PAINTING_BEHAVIOR_NORMAL); + display_list = content_layer_client.PaintContentsToDisplayList(); display_list->GenerateDiscardableImagesMetadata(); const DiscardableImageMap& image_map2 = display_list->discardable_image_map(); EXPECT_TRUE(image_map2.contains_hbd_images()); @@ -1176,8 +1161,7 @@ content_layer_client.set_bounds(kVisibleRect.size()); // Empty map should report a color usage of SRGB. - auto display_list = content_layer_client.PaintContentsToDisplayList( - ContentLayerClient::PAINTING_BEHAVIOR_NORMAL); + auto display_list = content_layer_client.PaintContentsToDisplayList(); display_list->GenerateDiscardableImagesMetadata(); EXPECT_EQ(display_list->discardable_image_map().content_color_usage(), gfx::ContentColorUsage::kSRGB); @@ -1187,8 +1171,7 @@ kSize, gfx::ColorSpace::CreateSRGB().ToSkColorSpace()); content_layer_client.add_draw_image(discardable_image_srgb, gfx::Point(0, 0), PaintFlags()); - display_list = content_layer_client.PaintContentsToDisplayList( - ContentLayerClient::PAINTING_BEHAVIOR_NORMAL); + display_list = content_layer_client.PaintContentsToDisplayList(); display_list->GenerateDiscardableImagesMetadata(); EXPECT_EQ(display_list->discardable_image_map().content_color_usage(), gfx::ContentColorUsage::kSRGB); @@ -1198,8 +1181,7 @@ kSize, gfx::ColorSpace::CreateDisplayP3D65().ToSkColorSpace()); content_layer_client.add_draw_image(discardable_image_wcg, gfx::Point(0, 0), PaintFlags()); - display_list = content_layer_client.PaintContentsToDisplayList( - ContentLayerClient::PAINTING_BEHAVIOR_NORMAL); + display_list = content_layer_client.PaintContentsToDisplayList(); display_list->GenerateDiscardableImagesMetadata(); EXPECT_EQ(display_list->discardable_image_map().content_color_usage(), gfx::ContentColorUsage::kWideColorGamut); @@ -1209,8 +1191,7 @@ kSize, gfx::ColorSpace::CreateHDR10().ToSkColorSpace()); content_layer_client.add_draw_image(discardable_image_hdr, gfx::Point(0, 0), PaintFlags()); - display_list = content_layer_client.PaintContentsToDisplayList( - ContentLayerClient::PAINTING_BEHAVIOR_NORMAL); + display_list = content_layer_client.PaintContentsToDisplayList(); display_list->GenerateDiscardableImagesMetadata(); EXPECT_EQ(display_list->discardable_image_map().content_color_usage(), gfx::ContentColorUsage::kHDR); @@ -1230,8 +1211,7 @@ content_layer_client.set_bounds(visible_rect.size()); scoped_refptr<DisplayItemList> display_list = - content_layer_client.PaintContentsToDisplayList( - ContentLayerClient::PAINTING_BEHAVIOR_NORMAL); + content_layer_client.PaintContentsToDisplayList(); display_list->GenerateDiscardableImagesMetadata(); const DiscardableImageMap& image_map = display_list->discardable_image_map(); @@ -1240,8 +1220,7 @@ content_layer_client.add_draw_image(discardable_image, gfx::Point(0, 0), PaintFlags()); - display_list = content_layer_client.PaintContentsToDisplayList( - ContentLayerClient::PAINTING_BEHAVIOR_NORMAL); + display_list = content_layer_client.PaintContentsToDisplayList(); display_list->GenerateDiscardableImagesMetadata(); const DiscardableImageMap& image_map2 = display_list->discardable_image_map();
diff --git a/cc/test/fake_content_layer_client.cc b/cc/test/fake_content_layer_client.cc index 90f5339..f4fb8e4f 100644 --- a/cc/test/fake_content_layer_client.cc +++ b/cc/test/fake_content_layer_client.cc
@@ -37,8 +37,7 @@ } scoped_refptr<DisplayItemList> -FakeContentLayerClient::PaintContentsToDisplayList( - PaintingControlSetting painting_control) { +FakeContentLayerClient::PaintContentsToDisplayList() { auto display_list = base::MakeRefCounted<DisplayItemList>(); for (RectPaintVector::const_iterator it = draw_rects_.begin();
diff --git a/cc/test/fake_content_layer_client.h b/cc/test/fake_content_layer_client.h index 5105306..eaf1b12 100644 --- a/cc/test/fake_content_layer_client.h +++ b/cc/test/fake_content_layer_client.h
@@ -43,8 +43,7 @@ ~FakeContentLayerClient() override; gfx::Rect PaintableRegion() override; - scoped_refptr<DisplayItemList> PaintContentsToDisplayList( - PaintingControlSetting painting_control) override; + scoped_refptr<DisplayItemList> PaintContentsToDisplayList() override; bool FillsBoundsCompletely() const override; size_t GetApproximateUnsharedMemoryUsage() const override; @@ -96,10 +95,6 @@ SkCanvas* last_canvas() const { return last_canvas_; } - PaintingControlSetting last_painting_control() const { - return last_painting_control_; - } - void set_reported_memory_usage(size_t reported_memory_usage) { reported_memory_usage_ = reported_memory_usage; } @@ -117,7 +112,6 @@ RectPaintVector draw_rects_; ImageVector draw_images_; SkCanvas* last_canvas_ = nullptr; - PaintingControlSetting last_painting_control_ = PAINTING_BEHAVIOR_NORMAL; size_t reported_memory_usage_ = 0; gfx::Size bounds_; bool bounds_set_ = false;
diff --git a/cc/test/fake_recording_source.h b/cc/test/fake_recording_source.h index 5883215..dbf5ceae 100644 --- a/cc/test/fake_recording_source.h +++ b/cc/test/fake_recording_source.h
@@ -81,8 +81,7 @@ Region invalidation; gfx::Rect new_recorded_viewport = client_.PaintableRegion(); scoped_refptr<DisplayItemList> display_list = - client_.PaintContentsToDisplayList( - ContentLayerClient::PAINTING_BEHAVIOR_NORMAL); + client_.PaintContentsToDisplayList(); size_t painter_reported_memory_usage = client_.GetApproximateUnsharedMemoryUsage(); UpdateAndExpandInvalidation(&invalidation, size_, new_recorded_viewport);
diff --git a/cc/test/solid_color_content_layer_client.cc b/cc/test/solid_color_content_layer_client.cc index 7ac7c96..0741e89 100644 --- a/cc/test/solid_color_content_layer_client.cc +++ b/cc/test/solid_color_content_layer_client.cc
@@ -19,8 +19,7 @@ } scoped_refptr<DisplayItemList> -SolidColorContentLayerClient::PaintContentsToDisplayList( - PaintingControlSetting painting_control) { +SolidColorContentLayerClient::PaintContentsToDisplayList() { auto display_list = base::MakeRefCounted<DisplayItemList>(); display_list->StartPaint(); display_list->push<SaveOp>();
diff --git a/cc/test/solid_color_content_layer_client.h b/cc/test/solid_color_content_layer_client.h index d8fcd70c..e70d6ffc 100644 --- a/cc/test/solid_color_content_layer_client.h +++ b/cc/test/solid_color_content_layer_client.h
@@ -29,8 +29,7 @@ gfx::Rect PaintableRegion() override; // ContentLayerClient implementation. - scoped_refptr<DisplayItemList> PaintContentsToDisplayList( - PaintingControlSetting painting_control) override; + scoped_refptr<DisplayItemList> PaintContentsToDisplayList() override; bool FillsBoundsCompletely() const override; size_t GetApproximateUnsharedMemoryUsage() const override;
diff --git a/cc/trees/layer_tree_host_client.h b/cc/trees/layer_tree_host_client.h index a97a408..ab13254 100644 --- a/cc/trees/layer_tree_host_client.h +++ b/cc/trees/layer_tree_host_client.h
@@ -66,6 +66,15 @@ constexpr ManipulationInfo kManipulationInfoPrecisionTouchPad = 1 << 2; constexpr ManipulationInfo kManipulationInfoPinchZoom = 1 << 3; +struct PaintBenchmarkResult { + double record_time_ms = 0; + double record_time_caching_disabled_ms = 0; + double record_time_subsequence_caching_disabled_ms = 0; + double record_time_partial_invalidation_ms = 0; + double raster_invalidation_and_convert_time_ms = 0; + double paint_artifact_compositor_update_time_ms = 0; +}; + // A LayerTreeHost is bound to a LayerTreeHostClient. The main rendering // loop (in ProxyMain or SingleThreadProxy) calls methods on the // LayerTreeHost, which then handles them and also calls into the equivalent @@ -172,6 +181,9 @@ virtual std::unique_ptr<BeginMainFrameMetrics> GetBeginMainFrameMetrics() = 0; virtual void NotifyThroughputTrackerResults(CustomTrackerResults results) = 0; + virtual void RunPaintBenchmark(int repeat_count, + PaintBenchmarkResult& result) {} + protected: virtual ~LayerTreeHostClient() = default; };
diff --git a/cc/trees/layer_tree_host_pixeltest_masks.cc b/cc/trees/layer_tree_host_pixeltest_masks.cc index 95c904c..39e253292 100644 --- a/cc/trees/layer_tree_host_pixeltest_masks.cc +++ b/cc/trees/layer_tree_host_pixeltest_masks.cc
@@ -62,8 +62,7 @@ gfx::Rect PaintableRegion() override { return gfx::Rect(bounds_); } - scoped_refptr<DisplayItemList> PaintContentsToDisplayList( - PaintingControlSetting picture_control) override { + scoped_refptr<DisplayItemList> PaintContentsToDisplayList() override { auto display_list = base::MakeRefCounted<DisplayItemList>(); display_list->StartPaint(); @@ -197,8 +196,7 @@ gfx::Rect PaintableRegion() override { return gfx::Rect(bounds_); } - scoped_refptr<DisplayItemList> PaintContentsToDisplayList( - PaintingControlSetting picture_control) override { + scoped_refptr<DisplayItemList> PaintContentsToDisplayList() override { // Intentionally return a solid color, empty mask display list. This // is a situation where all content should be masked out. auto display_list = base::MakeRefCounted<DisplayItemList>(); @@ -332,8 +330,7 @@ sk_sp<SkSurface> surface = SkSurface::MakeRasterN32Premul(50, 50); SkCanvas* canvas = surface->getCanvas(); scoped_refptr<DisplayItemList> mask_display_list = - mask_client.PaintContentsToDisplayList( - ContentLayerClient::PAINTING_BEHAVIOR_NORMAL); + mask_client.PaintContentsToDisplayList(); mask_display_list->Raster(canvas); FakeContentLayerClient layer_client; @@ -361,8 +358,7 @@ SkCanvas* canvas = surface->getCanvas(); MaskContentLayerClient client(mask_bounds); scoped_refptr<DisplayItemList> mask_display_list = - client.PaintContentsToDisplayList( - ContentLayerClient::PAINTING_BEHAVIOR_NORMAL); + client.PaintContentsToDisplayList(); mask_display_list->Raster(canvas); FakeContentLayerClient mask_client; @@ -449,8 +445,7 @@ bool FillsBoundsCompletely() const override { return false; } size_t GetApproximateUnsharedMemoryUsage() const override { return 0; } gfx::Rect PaintableRegion() override { return gfx::Rect(bounds_); } - scoped_refptr<DisplayItemList> PaintContentsToDisplayList( - PaintingControlSetting picture_control) override { + scoped_refptr<DisplayItemList> PaintContentsToDisplayList() override { auto display_list = base::MakeRefCounted<DisplayItemList>(); display_list->StartPaint(); @@ -498,8 +493,7 @@ bool FillsBoundsCompletely() const override { return false; } size_t GetApproximateUnsharedMemoryUsage() const override { return 0; } gfx::Rect PaintableRegion() override { return gfx::Rect(bounds_); } - scoped_refptr<DisplayItemList> PaintContentsToDisplayList( - PaintingControlSetting picture_control) override { + scoped_refptr<DisplayItemList> PaintContentsToDisplayList() override { auto display_list = base::MakeRefCounted<DisplayItemList>(); display_list->StartPaint(); @@ -724,8 +718,7 @@ } gfx::Rect PaintableRegion() override { return gfx::Rect(bounds()); } - scoped_refptr<DisplayItemList> PaintContentsToDisplayList( - PaintingControlSetting) override { + scoped_refptr<DisplayItemList> PaintContentsToDisplayList() override { return display_list_; } bool FillsBoundsCompletely() const override { return false; }
diff --git a/cc/trees/layer_tree_host_pixeltest_tiles.cc b/cc/trees/layer_tree_host_pixeltest_tiles.cc index 0cc4f38..c6a7abf 100644 --- a/cc/trees/layer_tree_host_pixeltest_tiles.cc +++ b/cc/trees/layer_tree_host_pixeltest_tiles.cc
@@ -59,8 +59,7 @@ : size_(size), blue_top_(true) {} gfx::Rect PaintableRegion() override { return gfx::Rect(size_); } - scoped_refptr<DisplayItemList> PaintContentsToDisplayList( - PaintingControlSetting painting_status) override { + scoped_refptr<DisplayItemList> PaintContentsToDisplayList() override { auto display_list = base::MakeRefCounted<DisplayItemList>(); display_list->StartPaint();
diff --git a/cc/trees/layer_tree_host_unittest_capture_content.cc b/cc/trees/layer_tree_host_unittest_capture_content.cc index 785d826..c7bf7b5b 100644 --- a/cc/trees/layer_tree_host_unittest_capture_content.cc +++ b/cc/trees/layer_tree_host_unittest_capture_content.cc
@@ -32,8 +32,7 @@ holders_.push_back(holder); } - scoped_refptr<DisplayItemList> PaintContentsToDisplayList( - PaintingControlSetting painting_control) override { + scoped_refptr<DisplayItemList> PaintContentsToDisplayList() override { auto display_list = base::MakeRefCounted<DisplayItemList>(); for (auto& holder : holders_) { display_list->StartPaint();
diff --git a/chrome/MAJOR_BRANCH_DATE b/chrome/MAJOR_BRANCH_DATE index 895cc24..38106ca 100644 --- a/chrome/MAJOR_BRANCH_DATE +++ b/chrome/MAJOR_BRANCH_DATE
@@ -1 +1 @@ -MAJOR_BRANCH_DATE=2020-08-21 +MAJOR_BRANCH_DATE=2020-10-02
diff --git a/chrome/VERSION b/chrome/VERSION index 174b420..cf24f5c 100644 --- a/chrome/VERSION +++ b/chrome/VERSION
@@ -1,4 +1,4 @@ -MAJOR=87 +MAJOR=88 MINOR=0 -BUILD=4280 +BUILD=4281 PATCH=0
diff --git a/chrome/android/chrome_java_sources.gni b/chrome/android/chrome_java_sources.gni index 9caba09..0df0263 100644 --- a/chrome/android/chrome_java_sources.gni +++ b/chrome/android/chrome_java_sources.gni
@@ -1183,6 +1183,7 @@ "java/src/org/chromium/chrome/browser/omnibox/suggestions/header/HeaderView.java", "java/src/org/chromium/chrome/browser/omnibox/suggestions/header/HeaderViewBinder.java", "java/src/org/chromium/chrome/browser/omnibox/suggestions/header/HeaderViewProperties.java", + "java/src/org/chromium/chrome/browser/omnibox/suggestions/mostvisited/MostVisitedTilesProcessor.java", "java/src/org/chromium/chrome/browser/omnibox/suggestions/tail/AlignmentManager.java", "java/src/org/chromium/chrome/browser/omnibox/suggestions/tail/TailSuggestionProcessor.java", "java/src/org/chromium/chrome/browser/omnibox/suggestions/tail/TailSuggestionView.java", @@ -1584,6 +1585,7 @@ "java/src/org/chromium/chrome/browser/toolbar/menu_button/MenuButtonMediator.java", "java/src/org/chromium/chrome/browser/toolbar/menu_button/MenuButtonProperties.java", "java/src/org/chromium/chrome/browser/toolbar/menu_button/MenuButtonViewBinder.java", + "java/src/org/chromium/chrome/browser/toolbar/menu_button/PropertyModelAnimatorFactory.java", "java/src/org/chromium/chrome/browser/toolbar/top/ActionModeController.java", "java/src/org/chromium/chrome/browser/toolbar/top/IncognitoSwitchCoordinator.java", "java/src/org/chromium/chrome/browser/toolbar/top/IncognitoSwitchProperties.java",
diff --git a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/v2/FeedListContentManager.java b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/v2/FeedListContentManager.java index 743d750..6c51e57 100644 --- a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/v2/FeedListContentManager.java +++ b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/v2/FeedListContentManager.java
@@ -119,13 +119,19 @@ UiUtils.removeViewFromParent(mNativeView); FrameLayout enclosingLayout = new FrameLayout(parent.getContext()); - // Set the left and right margins. FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams( new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)); - layoutParams.leftMargin = context.getResources().getDimensionPixelSize( - R.dimen.ntp_header_lateral_margins_v2); - layoutParams.rightMargin = layoutParams.leftMargin; enclosingLayout.setLayoutParams(layoutParams); + + // Set the left and right paddings. + int horizontalPadding = context.getResources().getDimensionPixelSize( + R.dimen.ntp_header_lateral_margins_v2); + enclosingLayout.setPadding(/* left */ horizontalPadding, /* top */ 0, + /* right */ horizontalPadding, /* bottom */ 0); + // Do not clip children. This ensures that the negative margin use in the feed header + // does not subsequently cause the IPH bubble to be clipped. + enclosingLayout.setClipToPadding(false); + enclosingLayout.setClipChildren(false); enclosingLayout.addView(mNativeView); return enclosingLayout; }
diff --git a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/v2/FeedStreamSurface.java b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/v2/FeedStreamSurface.java index d23a583a..8ab3c16 100644 --- a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/v2/FeedStreamSurface.java +++ b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/v2/FeedStreamSurface.java
@@ -770,6 +770,11 @@ @Override public void processThereAndBackAgainData(byte[] data) { + processThereAndBackAgainData(data, null); + } + + @Override + public void processThereAndBackAgainData(byte[] data, @Nullable View actionSourceView) { assert ThreadUtils.runningOnUiThread(); FeedStreamSurfaceJni.get().processThereAndBackAgain( mNativeFeedStreamSurface, FeedStreamSurface.this, data);
diff --git a/chrome/android/java/res/values/dimens.xml b/chrome/android/java/res/values/dimens.xml index d0b63b15..1e60d80 100644 --- a/chrome/android/java/res/values/dimens.xml +++ b/chrome/android/java/res/values/dimens.xml
@@ -225,6 +225,7 @@ <dimen name="toolbar_button_width">48dp</dimen> <dimen name="toolbar_identity_disc_size">24dp</dimen> <dimen name="toolbar_identity_disc_size_duet">32dp</dimen> + <dimen name="toolbar_url_focus_translation_x">10dp</dimen> <!-- Bottom Toolbar --> <dimen name="split_toolbar_button_height">56dp</dimen> @@ -317,6 +318,7 @@ <dimen name="tile_view_bg_corner_radius">2dp</dimen> <dimen name="tile_view_width">80dp</dimen> <dimen name="tile_view_width_condensed">64dp</dimen> + <dimen name="tile_view_min_height">86dp</dimen> <dimen name="tile_view_icon_size">48dp</dimen> <dimen name="tile_view_icon_size_modern">24dp</dimen> <!-- TODO(crbug.com/900912): Fix and remove lint ignore -->
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 bed1fbc..f838fa5 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
@@ -49,6 +49,7 @@ import org.chromium.chrome.browser.ActivityTabProvider; import org.chromium.chrome.browser.WindowDelegate; import org.chromium.chrome.browser.app.ChromeActivity; +import org.chromium.chrome.browser.compositor.layouts.OverviewModeBehavior; import org.chromium.chrome.browser.native_page.NativePageFactory; import org.chromium.chrome.browser.ntp.NewTabPage; import org.chromium.chrome.browser.offlinepages.OfflinePageUtils; @@ -829,6 +830,9 @@ @Override public void setUnfocusedWidth(int unfocusedWidth) {} + @Override + public void setOverviewModeBehavior(OverviewModeBehavior overviewModeBehavior) {} + // Implements FakeBoxDelegate. @Override public boolean isUrlBarFocused() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBar.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBar.java index 83ce837d..2550d83 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBar.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBar.java
@@ -15,6 +15,7 @@ import org.chromium.base.supplier.Supplier; import org.chromium.chrome.browser.ActivityTabProvider; import org.chromium.chrome.browser.WindowDelegate; +import org.chromium.chrome.browser.compositor.layouts.OverviewModeBehavior; import org.chromium.chrome.browser.ntp.FakeboxDelegate; import org.chromium.chrome.browser.ntp.NewTabPage; import org.chromium.chrome.browser.omnibox.UrlBar.UrlBarDelegate; @@ -129,6 +130,11 @@ void setToolbarDataProvider(ToolbarDataProvider model); /** + * Sets the {@link OverviewModeBehavior}. + */ + void setOverviewModeBehavior(OverviewModeBehavior overviewModeBehavior); + + /** * Gets the {@link ToolbarDataProvider} to be used for accessing {@link Toolbar} state. */ ToolbarDataProvider getToolbarDataProvider();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java index 05676d0b..e44f1cc 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java
@@ -43,6 +43,7 @@ import org.chromium.chrome.browser.ActivityTabProvider; import org.chromium.chrome.browser.AppHooks; import org.chromium.chrome.browser.WindowDelegate; +import org.chromium.chrome.browser.compositor.layouts.OverviewModeBehavior; import org.chromium.chrome.browser.flags.ChromeSwitches; import org.chromium.chrome.browser.gsa.GSAState; import org.chromium.chrome.browser.locale.LocaleManager; @@ -356,6 +357,11 @@ } @Override + public void setOverviewModeBehavior(OverviewModeBehavior overviewModeBehavior) { + mAutocompleteCoordinator.setOverviewModeBehavior(overviewModeBehavior); + } + + @Override public void onDeferredStartup() { mAutocompleteCoordinator.prefetchZeroSuggestResults(); }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarPhone.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarPhone.java index dbba147..2ce8fd7 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarPhone.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarPhone.java
@@ -15,7 +15,6 @@ import org.chromium.base.TraceEvent; import org.chromium.chrome.R; -import org.chromium.chrome.browser.compositor.layouts.OverviewModeBehavior; import org.chromium.chrome.browser.ntp.NewTabPage; import org.chromium.ui.interpolators.BakedBezierInterpolator; @@ -329,10 +328,6 @@ } } - public void setOverviewModeBehavior(OverviewModeBehavior overviewModeBehavior) { - mAutocompleteCoordinator.setOverviewModeBehavior(overviewModeBehavior); - } - /** Update the status visibility according to the current state held in LocationBar. */ private void updateStatusVisibility() { boolean incognito = getToolbarDataProvider().isIncognito();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteCoordinatorImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteCoordinatorImpl.java index a7ca8e2..b0d88cf 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteCoordinatorImpl.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteCoordinatorImpl.java
@@ -28,11 +28,13 @@ import org.chromium.chrome.browser.omnibox.suggestions.base.BaseSuggestionView; import org.chromium.chrome.browser.omnibox.suggestions.base.BaseSuggestionViewBinder; import org.chromium.chrome.browser.omnibox.suggestions.basic.SuggestionViewViewBinder; +import org.chromium.chrome.browser.omnibox.suggestions.carousel.BaseCarouselSuggestionViewBinder; import org.chromium.chrome.browser.omnibox.suggestions.editurl.EditUrlSuggestionView; import org.chromium.chrome.browser.omnibox.suggestions.editurl.EditUrlSuggestionViewBinder; import org.chromium.chrome.browser.omnibox.suggestions.entity.EntitySuggestionViewBinder; import org.chromium.chrome.browser.omnibox.suggestions.header.HeaderView; import org.chromium.chrome.browser.omnibox.suggestions.header.HeaderViewBinder; +import org.chromium.chrome.browser.omnibox.suggestions.mostvisited.MostVisitedTilesProcessor; import org.chromium.chrome.browser.omnibox.suggestions.tail.TailSuggestionView; import org.chromium.chrome.browser.omnibox.suggestions.tail.TailSuggestionViewBinder; import org.chromium.chrome.browser.omnibox.voice.VoiceRecognitionHandler; @@ -173,6 +175,11 @@ mQueryTileCoordinator::bind); adapter.registerType( + OmniboxSuggestionUiType.TILE_NAVSUGGEST, + MostVisitedTilesProcessor::createView, + BaseCarouselSuggestionViewBinder::bind); + + adapter.registerType( OmniboxSuggestionUiType.HEADER, parent -> new HeaderView(parent.getContext()), HeaderViewBinder::bind); @@ -287,12 +294,11 @@ public boolean handleKeyEvent(int keyCode, KeyEvent event) { boolean isShowingList = mDropdown != null && mDropdown.getViewGroup().isShown(); - boolean isUpOrDown = KeyNavigationUtil.isGoUpOrDown(event); - if (isShowingList && mMediator.getSuggestionCount() > 0 && isUpOrDown) { + boolean isAnyDirection = KeyNavigationUtil.isGoAnyDirection(event); + if (isShowingList && mMediator.getSuggestionCount() > 0 && isAnyDirection) { mMediator.allowPendingItemSelection(); } - boolean isValidListKey = isUpOrDown || KeyNavigationUtil.isGoRight(event) - || KeyNavigationUtil.isGoLeft(event) || KeyNavigationUtil.isEnter(event); + boolean isValidListKey = isAnyDirection || KeyNavigationUtil.isEnter(event); if (isShowingList && isValidListKey && mDropdown.getViewGroup().onKeyDown(keyCode, event)) { return true; }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediator.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediator.java index a719c8b..1188743 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediator.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediator.java
@@ -479,13 +479,6 @@ PropertyModel model, OmniboxSuggestion suggestion, int position) { return new SuggestionViewDelegate() { @Override - public void onSetUrlToSuggestion() { - if (mIgnoreOmniboxItemSelection) return; - mIgnoreOmniboxItemSelection = true; - AutocompleteMediator.this.onSetUrlToSuggestion(suggestion); - } - - @Override public void onSelection() { processor.recordItemUsed(model); AutocompleteMediator.this.onSelection(suggestion, position); @@ -662,10 +655,13 @@ /** * Triggered when the user navigates to one of the suggestions without clicking on it. - * @param suggestion The suggestion that was selected. + * @param text The text to be displayed in the Omnibox. */ - void onSetUrlToSuggestion(OmniboxSuggestion suggestion) { - mDelegate.setOmniboxEditingText(suggestion.getFillIntoEdit()); + @Override + public void setOmniboxEditingText(String text) { + if (mIgnoreOmniboxItemSelection) return; + mIgnoreOmniboxItemSelection = true; + mDelegate.setOmniboxEditingText(text); } /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/DropdownItemViewInfoListBuilder.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/DropdownItemViewInfoListBuilder.java index 5623cc5..b5b0cf9 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/DropdownItemViewInfoListBuilder.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/DropdownItemViewInfoListBuilder.java
@@ -27,6 +27,7 @@ import org.chromium.chrome.browser.omnibox.suggestions.editurl.EditUrlSuggestionProcessor; import org.chromium.chrome.browser.omnibox.suggestions.entity.EntitySuggestionProcessor; import org.chromium.chrome.browser.omnibox.suggestions.header.HeaderProcessor; +import org.chromium.chrome.browser.omnibox.suggestions.mostvisited.MostVisitedTilesProcessor; import org.chromium.chrome.browser.omnibox.suggestions.tail.TailSuggestionProcessor; import org.chromium.chrome.browser.omnibox.suggestions.tiles.TileSuggestionProcessor; import org.chromium.chrome.browser.profiles.Profile; @@ -101,6 +102,8 @@ registerSuggestionProcessor( new TileSuggestionProcessor(context, queryTileSuggestionCallback)); registerSuggestionProcessor( + new MostVisitedTilesProcessor(context, host, iconBridgeSupplier)); + registerSuggestionProcessor( new BasicSuggestionProcessor(context, host, textProvider, iconBridgeSupplier)); }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxSuggestionUiType.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxSuggestionUiType.java index 8ba5857..77db27b0 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxSuggestionUiType.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxSuggestionUiType.java
@@ -13,7 +13,7 @@ @IntDef({OmniboxSuggestionUiType.DEFAULT, OmniboxSuggestionUiType.EDIT_URL_SUGGESTION, OmniboxSuggestionUiType.ANSWER_SUGGESTION, OmniboxSuggestionUiType.ENTITY_SUGGESTION, OmniboxSuggestionUiType.TAIL_SUGGESTION, OmniboxSuggestionUiType.CLIPBOARD_SUGGESTION, - OmniboxSuggestionUiType.TILE_SUGGESTION}) + OmniboxSuggestionUiType.TILE_SUGGESTION, OmniboxSuggestionUiType.TILE_NAVSUGGEST}) @Retention(RetentionPolicy.SOURCE) public @interface OmniboxSuggestionUiType { int DEFAULT = 0; @@ -24,4 +24,5 @@ int CLIPBOARD_SUGGESTION = 5; int TILE_SUGGESTION = 6; int HEADER = 7; + int TILE_NAVSUGGEST = 8; }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/SuggestionHost.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/SuggestionHost.java index 3bc4d8d..28c804c9 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/SuggestionHost.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/SuggestionHost.java
@@ -40,4 +40,11 @@ * @param isCollapsed True if group should appear collapsed, otherwise false. */ void setGroupCollapsedState(int groupId, boolean isCollapsed); + + /** + * Update the content of the Omnibox without triggering the Navigation. + * + * @param text The text to be displayed in the Omnibox. + */ + void setOmniboxEditingText(String text); }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/SuggestionViewDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/SuggestionViewDelegate.java index d0247ed0..d0f3dca93 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/SuggestionViewDelegate.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/SuggestionViewDelegate.java
@@ -13,7 +13,4 @@ /** Triggered when the user long presses the omnibox suggestion. */ void onLongPress(); - - /** Triggered when the user navigates to one of the suggestions without clicking on it. */ - void onSetUrlToSuggestion(); }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionView.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionView.java index 1544751..ab50838 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionView.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionView.java
@@ -11,6 +11,7 @@ import android.widget.ImageView; import androidx.annotation.LayoutRes; +import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import androidx.appcompat.widget.AppCompatImageView; @@ -32,6 +33,7 @@ private final List<ImageView> mActionButtons; private final DecoratedSuggestionView<T> mDecoratedView; private SuggestionViewDelegate mDelegate; + private @Nullable Runnable mOnFocusViaSelectionListener; /** * Constructs a new suggestion view. @@ -136,8 +138,8 @@ @Override public void setSelected(boolean selected) { mDecoratedView.setSelected(selected); - if (selected) { - mDelegate.onSetUrlToSuggestion(); + if (selected && mOnFocusViaSelectionListener != null) { + mOnFocusViaSelectionListener.run(); } } @@ -170,6 +172,15 @@ mDelegate = delegate; } + /** + * Specify the listener receiving a call when the user highlights this Suggestion. + * + * @param listener The listener to be notified about selection. + */ + void setOnFocusViaSelectionListener(@Nullable Runnable listener) { + mOnFocusViaSelectionListener = listener; + } + /** @return Widget holding suggestion decoration icon. */ RoundedCornerImageView getSuggestionImageView() { return mDecoratedView.getImageView();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionViewBinder.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionViewBinder.java index 7143f15..deeaca4 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionViewBinder.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionViewBinder.java
@@ -63,6 +63,9 @@ updateColorScheme(model, view); } else if (BaseSuggestionViewProperties.ACTIONS == propertyKey) { bindActionButtons(model, view, model.get(BaseSuggestionViewProperties.ACTIONS)); + } else if (BaseSuggestionViewProperties.ON_FOCUS_VIA_SELECTION == propertyKey) { + view.setOnFocusViaSelectionListener( + model.get(BaseSuggestionViewProperties.ON_FOCUS_VIA_SELECTION)); } }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionViewProcessor.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionViewProcessor.java index 676ef82..510db42 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionViewProcessor.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionViewProcessor.java
@@ -152,6 +152,8 @@ mSuggestionHost.createSuggestionViewDelegate(this, model, suggestion, position); model.set(BaseSuggestionViewProperties.SUGGESTION_DELEGATE, delegate); + model.set(BaseSuggestionViewProperties.ON_FOCUS_VIA_SELECTION, + () -> { mSuggestionHost.setOmniboxEditingText(suggestion.getFillIntoEdit()); }); model.set(BaseSuggestionViewProperties.DENSITY, mDensity); setCustomActions(model, null); }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionViewProperties.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionViewProperties.java index c0d3f7e..40b86b1b 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionViewProperties.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionViewProperties.java
@@ -74,6 +74,10 @@ public static final WritableObjectPropertyKey<List<Action>> ACTIONS = new WritableObjectPropertyKey(); + /** Callback invoked when the Suggestion view is highlighted. */ + public static final WritableObjectPropertyKey<Runnable> ON_FOCUS_VIA_SELECTION = + new WritableObjectPropertyKey<>(); + /** Delegate receiving user events. */ public static final WritableObjectPropertyKey<SuggestionViewDelegate> SUGGESTION_DELEGATE = new WritableObjectPropertyKey<>(); @@ -82,7 +86,7 @@ public static final WritableIntPropertyKey DENSITY = new WritableIntPropertyKey(); public static final PropertyKey[] ALL_UNIQUE_KEYS = - new PropertyKey[] {ACTIONS, ICON, DENSITY, SUGGESTION_DELEGATE}; + new PropertyKey[] {ACTIONS, ICON, DENSITY, SUGGESTION_DELEGATE, ON_FOCUS_VIA_SELECTION}; public static final PropertyKey[] ALL_KEYS = PropertyModel.concatKeys(ALL_UNIQUE_KEYS, SuggestionCommonProperties.ALL_KEYS);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/mostvisited/MostVisitedTilesProcessor.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/mostvisited/MostVisitedTilesProcessor.java new file mode 100644 index 0000000..47ba247a --- /dev/null +++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/mostvisited/MostVisitedTilesProcessor.java
@@ -0,0 +1,149 @@ +// Copyright 2020 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.omnibox.suggestions.mostvisited; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.view.LayoutInflater; +import android.view.ViewGroup; + +import androidx.annotation.NonNull; + +import org.chromium.base.supplier.Supplier; +import org.chromium.chrome.R; +import org.chromium.chrome.browser.omnibox.OmniboxSuggestionType; +import org.chromium.chrome.browser.omnibox.styles.OmniboxResourceProvider; +import org.chromium.chrome.browser.omnibox.styles.OmniboxTheme; +import org.chromium.chrome.browser.omnibox.suggestions.OmniboxSuggestion; +import org.chromium.chrome.browser.omnibox.suggestions.OmniboxSuggestionUiType; +import org.chromium.chrome.browser.omnibox.suggestions.SuggestionHost; +import org.chromium.chrome.browser.omnibox.suggestions.carousel.BaseCarouselSuggestionProcessor; +import org.chromium.chrome.browser.omnibox.suggestions.carousel.BaseCarouselSuggestionView; +import org.chromium.chrome.browser.omnibox.suggestions.carousel.BaseCarouselSuggestionViewProperties; +import org.chromium.chrome.browser.suggestions.tile.TileView; +import org.chromium.chrome.browser.suggestions.tile.TileViewBinder; +import org.chromium.chrome.browser.suggestions.tile.TileViewProperties; +import org.chromium.chrome.browser.ui.favicon.LargeIconBridge; +import org.chromium.ui.modelutil.MVCListAdapter.ListItem; +import org.chromium.ui.modelutil.MVCListAdapter.ModelList; +import org.chromium.ui.modelutil.PropertyModel; +import org.chromium.ui.modelutil.SimpleRecyclerViewAdapter; +import org.chromium.url.GURL; + +import java.util.ArrayList; +import java.util.List; + +/** + * SuggestionProcessor for Most Visited URL tiles. + * TODO(crbug.com/1106109): Write integration tests. + */ +public class MostVisitedTilesProcessor extends BaseCarouselSuggestionProcessor { + private final @NonNull Context mContext; + private final @NonNull SuggestionHost mSuggestionHost; + private final @NonNull Supplier<LargeIconBridge> mIconBridgeSupplier; + private final int mMinCarouselItemViewHeight; + private static final int DEFAULT_TILE_TYPE = 0; + + /** + * Constructor. + * + * @param context An Android context. + * @param host SuggestionHost receiving notifications about user actions. + * @param iconBridgeSupplier Supplier of the LargeIconBridge used to fetch site favicons. + */ + public MostVisitedTilesProcessor(@NonNull Context context, @NonNull SuggestionHost host, + @NonNull Supplier<LargeIconBridge> iconBridgeSupplier) { + super(context); + mContext = context; + mSuggestionHost = host; + mIconBridgeSupplier = iconBridgeSupplier; + mMinCarouselItemViewHeight = + context.getResources().getDimensionPixelSize(R.dimen.tile_view_min_height); + } + + @Override + public boolean doesProcessSuggestion(OmniboxSuggestion suggestion, int position) { + return suggestion.getType() == OmniboxSuggestionType.TILE_NAVSUGGEST; + } + + @Override + public int getViewTypeId() { + return OmniboxSuggestionUiType.TILE_NAVSUGGEST; + } + + @Override + public PropertyModel createModel() { + return new PropertyModel(BaseCarouselSuggestionViewProperties.ALL_KEYS); + } + + @Override + public int getMinimumCarouselItemViewHeight() { + return mMinCarouselItemViewHeight; + } + + @Override + public void populateModel(OmniboxSuggestion suggestion, PropertyModel model, int position) { + final List<OmniboxSuggestion.NavsuggestTile> tiles = suggestion.getNavsuggestTiles(); + final int tilesCount = tiles.size(); + final List<ListItem> tileList = new ArrayList<>(tilesCount); + final LargeIconBridge iconBridge = mIconBridgeSupplier.get(); + + for (int index = 0; index < tilesCount; index++) { + final PropertyModel tileModel = new PropertyModel(TileViewProperties.ALL_KEYS); + final GURL url = tiles.get(index).url; + tileModel.set(TileViewProperties.TITLE, tiles.get(index).title); + tileModel.set(TileViewProperties.TITLE_LINES, 1); + tileModel.set(TileViewProperties.ON_FOCUS_VIA_SELECTION, + () -> { mSuggestionHost.setOmniboxEditingText(url.getSpec()); }); + + if (iconBridge != null) { + iconBridge.getLargeIconForUrl(tiles.get(index).url, /* size */ 32, + (Bitmap icon, int fallbackColor, boolean isFallbackColorDefault, + int iconType) -> { + if (icon == null) return; + tileModel.set(TileViewProperties.ICON, new BitmapDrawable(icon)); + }); + } + + tileList.add(new ListItem(DEFAULT_TILE_TYPE, tileModel)); + } + + model.set(BaseCarouselSuggestionViewProperties.TILES, tileList); + model.set(BaseCarouselSuggestionViewProperties.TITLE, + mContext.getResources().getString(R.string.most_visited_tiles_header)); + } + + /** + * Create Carousel Suggestion View presenting the Most Visited URL tiles. + * + * @param parent ViewGroup that will host the Carousel view. + * @return BaseCarouselSuggestionView for the Most Visited URL suggestions. + */ + public static BaseCarouselSuggestionView createView(ViewGroup parent) { + SimpleRecyclerViewAdapter adapter = new SimpleRecyclerViewAdapter(new ModelList()); + adapter.registerType( + DEFAULT_TILE_TYPE, MostVisitedTilesProcessor::buildTile, TileViewBinder::bind); + return new BaseCarouselSuggestionView(parent.getContext(), adapter); + } + + /** + * Create a Tile element for the Most Visited URL suggestions. + * + * @param parent ViewGroup that will host the Tile. + * @return A TileView element for the individual URL suggestion. + */ + private static TileView buildTile(ViewGroup parent) { + TileView tile = (TileView) LayoutInflater.from(parent.getContext()) + .inflate(R.layout.suggestions_tile_view, parent, false); + tile.setClickable(true); + + Drawable background = OmniboxResourceProvider.resolveAttributeToDrawable( + parent.getContext(), OmniboxTheme.LIGHT_THEME, R.attr.selectableItemBackground); + tile.setBackgroundDrawable(background); + return tile; + } +}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/menu_button/MenuButtonCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/menu_button/MenuButtonCoordinator.java index de113bf..1e30186e 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/menu_button/MenuButtonCoordinator.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/menu_button/MenuButtonCoordinator.java
@@ -4,7 +4,12 @@ package org.chromium.chrome.browser.toolbar.menu_button; +import static android.view.View.LAYOUT_DIRECTION_RTL; + +import android.animation.Animator; import android.app.Activity; +import android.graphics.Canvas; +import android.view.View; import android.view.View.OnKeyListener; import androidx.annotation.IdRes; @@ -20,6 +25,7 @@ import org.chromium.chrome.browser.ui.appmenu.AppMenuButtonHelper; import org.chromium.chrome.browser.ui.appmenu.AppMenuCoordinator; import org.chromium.ui.UiUtils; +import org.chromium.ui.base.ViewUtils; import org.chromium.ui.modelutil.PropertyModel; import org.chromium.ui.modelutil.PropertyModelChangeProcessor; @@ -201,9 +207,34 @@ * Set the visibility of the MenuButton controlled by this coordinator. * @param visible Visibility state, true for visible and false for hidden. */ - public void setVisibility(boolean visible) { if (mMediator == null) return; mMediator.setVisibility(visible); } + + /** + * Draws the current visual state of this component for the purposes of rendering the tab + * switcher animation, setting the alpha to fade the view by the appropriate amount. + * @param root Root view for the menu button; used to position the canvas that's drawn on. + * @param canvas Canvas to draw to. + * @param alpha Integer (0-255) alpha level to draw at. + */ + public void drawTabSwitcherAnimationOverlay(View root, Canvas canvas, int alpha) { + canvas.save(); + ViewUtils.translateCanvasToView(root, mMenuButton, canvas); + mMenuButton.drawTabSwitcherAnimationOverlay(canvas, alpha); + canvas.restore(); + } + + /** + * Creates an animator for the MenuButton during the process offocusing or unfocusing the + * UrlBar. The animation translate and fades the button into/out of view. + * @return The Animator object for the MenuButton. + * @param isFocusingUrl Whether the animation is for focusing the URL, meaning the button is + * fading out of view, or un-focusing, meaning it's fading into view. + */ + public Animator getUrlFocusingAnimator(boolean isFocusingUrl) { + return mMediator.getUrlFocusingAnimator(isFocusingUrl, + mMenuButton != null && mMenuButton.getLayoutDirection() == LAYOUT_DIRECTION_RTL); + } }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/menu_button/MenuButtonMediator.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/menu_button/MenuButtonMediator.java index 40f8ceb..d2928ae 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/menu_button/MenuButtonMediator.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/menu_button/MenuButtonMediator.java
@@ -4,17 +4,21 @@ package org.chromium.chrome.browser.toolbar.menu_button; +import android.animation.Animator; +import android.animation.AnimatorSet; import android.content.res.ColorStateList; import android.content.res.Resources; import androidx.annotation.Nullable; import org.chromium.base.Callback; +import org.chromium.base.MathUtils; import org.chromium.base.metrics.RecordUserAction; import org.chromium.base.supplier.ObservableSupplier; import org.chromium.base.supplier.ObservableSupplierImpl; import org.chromium.base.supplier.OneshotSupplier; import org.chromium.base.supplier.Supplier; +import org.chromium.chrome.R; import org.chromium.chrome.browser.browser_controls.BrowserStateBrowserControlsVisibilityDelegate; import org.chromium.chrome.browser.omaha.UpdateMenuItemHelper; import org.chromium.chrome.browser.omaha.UpdateMenuItemHelper.MenuButtonState; @@ -36,7 +40,6 @@ * changes to the property model that backs the MenuButton view. */ class MenuButtonMediator implements AppMenuObserver { - private OneshotSupplier<AppMenuCoordinator> mAppMenuCoordinatorSupplier; private Callback<AppMenuCoordinator> mAppMenuCoordinatorSupplierObserver; private @Nullable AppMenuPropertiesDelegate mAppMenuPropertiesDelegate; private AppMenuButtonHelper mAppMenuButtonHelper; @@ -55,6 +58,9 @@ private Supplier<Boolean> mIsInOverviewModeSupplier; private boolean mSuppressAppMenuUpdateBadge; private Resources mResources; + private OneshotSupplier<AppMenuCoordinator> mAppMenuCoordinatorSupplier; + + private final int mUrlFocusTranslationX; /** * @param appMenuCoordinatorSupplier Supplier for the AppMenuCoordinator, which owns all other @@ -94,6 +100,9 @@ mAppMenuCoordinatorSupplier.onAvailable(mAppMenuCoordinatorSupplierObserver); mResources = resources; mAppMenuButtonHelperSupplier = new ObservableSupplierImpl<>(); + + mUrlFocusTranslationX = + mResources.getDimensionPixelSize(R.dimen.toolbar_url_focus_translation_x); } @Override @@ -269,4 +278,25 @@ private boolean isUpdateAvailable() { return UpdateMenuItemHelper.getInstance().getUiState().buttonState != null; } + + public Animator getUrlFocusingAnimator(boolean isFocusingUrl, boolean isRtl) { + float translationX; + float alpha; + if (isFocusingUrl) { + float density = mResources.getDisplayMetrics().density; + translationX = MathUtils.flipSignIf(mUrlFocusTranslationX, isRtl) * density; + alpha = 0.0f; + } else { + translationX = 0.0f; + alpha = 1.0f; + } + + AnimatorSet animatorSet = new AnimatorSet(); + Animator translationAnimator = PropertyModelAnimatorFactory.ofFloat( + mPropertyModel, MenuButtonProperties.TRANSLATION_X, translationX); + Animator alphaAnimator = PropertyModelAnimatorFactory.ofFloat( + mPropertyModel, MenuButtonProperties.ALPHA, alpha); + animatorSet.playTogether(translationAnimator, alphaAnimator); + return animatorSet; + } }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/menu_button/MenuButtonProperties.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/menu_button/MenuButtonProperties.java index b03b11d..8d37e9f 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/menu_button/MenuButtonProperties.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/menu_button/MenuButtonProperties.java
@@ -11,6 +11,7 @@ import org.chromium.chrome.browser.ui.appmenu.AppMenuButtonHelper; import org.chromium.ui.modelutil.PropertyKey; import org.chromium.ui.modelutil.PropertyModel.WritableBooleanPropertyKey; +import org.chromium.ui.modelutil.PropertyModel.WritableFloatPropertyKey; import org.chromium.ui.modelutil.PropertyModel.WritableObjectPropertyKey; class MenuButtonProperties { @@ -35,6 +36,7 @@ } } + public static final WritableFloatPropertyKey ALPHA = new WritableFloatPropertyKey(); public static final WritableObjectPropertyKey<AppMenuButtonHelper> APP_MENU_BUTTON_HELPER = new WritableObjectPropertyKey<>(); public static final WritableObjectPropertyKey<String> CONTENT_DESCRIPTION = @@ -47,8 +49,9 @@ new WritableObjectPropertyKey(true); public static final WritableObjectPropertyKey<ThemeProperty> THEME = new WritableObjectPropertyKey<>(true); + public static final WritableFloatPropertyKey TRANSLATION_X = new WritableFloatPropertyKey(); public static final PropertyKey[] ALL_KEYS = - new PropertyKey[] {APP_MENU_BUTTON_HELPER, CONTENT_DESCRIPTION, IS_CLICKABLE, - IS_HIGHLIGHTING, IS_VISIBLE, SHOW_UPDATE_BADGE, THEME}; + new PropertyKey[] {ALPHA, APP_MENU_BUTTON_HELPER, CONTENT_DESCRIPTION, IS_CLICKABLE, + IS_HIGHLIGHTING, IS_VISIBLE, SHOW_UPDATE_BADGE, THEME, TRANSLATION_X}; }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/menu_button/MenuButtonViewBinder.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/menu_button/MenuButtonViewBinder.java index 0693f206..eb423b57 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/menu_button/MenuButtonViewBinder.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/menu_button/MenuButtonViewBinder.java
@@ -15,7 +15,9 @@ class MenuButtonViewBinder implements ViewBinder<PropertyModel, MenuButton, PropertyKey> { @Override public void bind(PropertyModel model, MenuButton view, PropertyKey propertyKey) { - if (propertyKey == MenuButtonProperties.APP_MENU_BUTTON_HELPER) { + if (propertyKey == MenuButtonProperties.ALPHA) { + view.setAlpha(model.get(MenuButtonProperties.ALPHA)); + } else if (propertyKey == MenuButtonProperties.APP_MENU_BUTTON_HELPER) { view.setAppMenuButtonHelper(model.get(MenuButtonProperties.APP_MENU_BUTTON_HELPER)); } else if (propertyKey == MenuButtonProperties.CONTENT_DESCRIPTION) { view.updateContentDescription(model.get(MenuButtonProperties.CONTENT_DESCRIPTION)); @@ -36,6 +38,8 @@ } else if (propertyKey == MenuButtonProperties.THEME) { ThemeProperty themeProperty = model.get(MenuButtonProperties.THEME); view.onTintChanged(themeProperty.mColorStateList, themeProperty.mUseLightColors); + } else if (propertyKey == MenuButtonProperties.TRANSLATION_X) { + view.setTranslationX(model.get(MenuButtonProperties.TRANSLATION_X)); } } }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/menu_button/PropertyModelAnimatorFactory.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/menu_button/PropertyModelAnimatorFactory.java new file mode 100644 index 0000000..093b96b --- /dev/null +++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/menu_button/PropertyModelAnimatorFactory.java
@@ -0,0 +1,53 @@ +// Copyright 2020 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.toolbar.menu_button; + +import android.animation.ObjectAnimator; +import android.util.Property; + +import org.chromium.ui.modelutil.PropertyModel; +import org.chromium.ui.modelutil.PropertyModel.WritableFloatPropertyKey; + +/** + * Static factory class that creates Animators for MVC properties by providing implementations of + * android.util.Property that mutate a given property in a given model. + */ +class PropertyModelAnimatorFactory { + /** + * Builds an Animator for the given model, key, and target value. + * @param model PropertyModel object to write changes to the given key to. + * @param key Key of the property to change. + * @param targetValue Target end value of the property. + * @return An Animator that when run, will animate the property from its current value to the + * given target. + */ + static ObjectAnimator ofFloat( + PropertyModel model, WritableFloatPropertyKey key, float targetValue) { + PropertyModelFloatProp customProperty = new PropertyModelFloatProp(key); + return ObjectAnimator.ofFloat(model, customProperty, targetValue); + } + + private static class PropertyModelFloatProp extends Property<PropertyModel, Float> { + final WritableFloatPropertyKey mKey; + + public PropertyModelFloatProp(WritableFloatPropertyKey key) { + super(Float.class, key.toString()); + mKey = key; + } + + @Override + public Float get(PropertyModel model) { + return model.get(mKey); + } + + @Override + public void set(PropertyModel model, Float value) { + model.set(mKey, value); + } + } + + // TODO(https://crbug.com/1086676, pnoland): Extract this from toolbar.menu_button and implement + // factory methods for other types, e.g. int and aRGB. +}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhone.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhone.java index 2611d14..a8c1682 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhone.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhone.java
@@ -68,7 +68,6 @@ import org.chromium.chrome.browser.toolbar.TabCountProvider.TabCountObserver; import org.chromium.chrome.browser.toolbar.TabSwitcherDrawable; import org.chromium.chrome.browser.toolbar.ToolbarColors; -import org.chromium.chrome.browser.toolbar.menu_button.MenuButton; import org.chromium.chrome.browser.toolbar.menu_button.MenuButtonCoordinator; import org.chromium.chrome.browser.toolbar.top.TopToolbarCoordinator.UrlExpansionObserver; import org.chromium.components.browser_ui.styles.ChromeColors; @@ -94,7 +93,6 @@ public static final long THEME_COLOR_TRANSITION_DURATION = 250; public static final int URL_FOCUS_CHANGE_ANIMATION_DURATION_MS = 225; - private static final int URL_FOCUS_TOOLBAR_BUTTONS_TRANSLATION_X_DP = 10; private static final int URL_FOCUS_TOOLBAR_BUTTONS_DURATION_MS = 100; private static final int URL_CLEAR_FOCUS_EXPERIMENTAL_BUTTON_DELAY_MS = 150; private static final int URL_CLEAR_FOCUS_TABSTACK_DELAY_MS = 200; @@ -265,6 +263,7 @@ private AnimatorSet mOptionalButtonAnimator; private boolean mOptionalButtonAnimationRunning; private int mOptionalButtonTranslation; + private int mUrlFocusTranslationX; /** * The progress fraction for the location bar width change animation that is run when the @@ -363,6 +362,8 @@ inflateTabSwitchingResources(); setWillNotDraw(false); + mUrlFocusTranslationX = + getResources().getDimensionPixelSize(R.dimen.toolbar_url_focus_translation_x); } } @@ -1242,14 +1243,14 @@ mLocationBar.setAlpha(previousAlpha); // Translate to draw end toolbar buttons. - translateCanvasToView(this, mToolbarButtonsContainer, canvas); + ViewUtils.translateCanvasToView(this, mToolbarButtonsContainer, canvas); // Draw the optional button if necessary. if (mOptionalButton != null && mOptionalButton.getVisibility() != View.GONE) { canvas.save(); Drawable optionalButtonDrawable = mOptionalButton.getDrawable(); - translateCanvasToView(mToolbarButtonsContainer, mOptionalButton, canvas); + ViewUtils.translateCanvasToView(mToolbarButtonsContainer, mOptionalButton, canvas); int backgroundWidth = mOptionalButton.getDrawable().getIntrinsicWidth(); int backgroundHeight = mOptionalButton.getDrawable().getIntrinsicHeight(); @@ -1274,7 +1275,8 @@ && mUrlExpansionFraction != 1f) { // Draw the tab stack button image. canvas.save(); - translateCanvasToView(mToolbarButtonsContainer, mToggleTabStackButton, canvas); + ViewUtils.translateCanvasToView( + mToolbarButtonsContainer, mToggleTabStackButton, canvas); int backgroundWidth = mToggleTabStackButton.getDrawable().getIntrinsicWidth(); int backgroundHeight = mToggleTabStackButton.getDrawable().getIntrinsicHeight(); @@ -1298,12 +1300,10 @@ } // Draw the menu button if necessary. - final MenuButton menuButton = getMenuButtonCoordinator().getMenuButton(); - if (menuButton != null) { - canvas.save(); - translateCanvasToView(mToolbarButtonsContainer, menuButton, canvas); - menuButton.drawTabSwitcherAnimationOverlay(canvas, rgbAlpha); - canvas.restore(); + final MenuButtonCoordinator menuButtonCoordinator = getMenuButtonCoordinator(); + if (menuButtonCoordinator != null) { + menuButtonCoordinator.drawTabSwitcherAnimationOverlay( + mToolbarButtonsContainer, canvas, rgbAlpha); } mLightDrawablesUsedForLastTextureCapture = useLight(); @@ -1315,28 +1315,6 @@ canvas.restore(); } - /** - * Translates the canvas to ensure the specified view's coordinates are at 0, 0. - * - * @param from The view the canvas is currently translated to. - * @param to The view to translate to. - * @param canvas The canvas to be translated. - * - * @throws IllegalArgumentException if {@code from} is not an ancestor of {@code to}. - */ - private static void translateCanvasToView(View from, View to, Canvas canvas) - throws IllegalArgumentException { - assert from != null; - assert to != null; - while (to != from) { - canvas.translate(to.getLeft(), to.getTop()); - if (!(to.getParent() instanceof View)) { - throw new IllegalArgumentException("View 'to' was not a desendent of 'from'."); - } - to = (View) to.getParent(); - } - } - @Override protected boolean drawChild(Canvas canvas, View child, long drawingTime) { if (child == mLocationBar) return drawLocationBar(canvas, drawingTime); @@ -1984,21 +1962,12 @@ float density = getContext().getResources().getDisplayMetrics().density; boolean isRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL; float toolbarButtonTranslationX = - MathUtils.flipSignIf(URL_FOCUS_TOOLBAR_BUTTONS_TRANSLATION_X_DP, isRtl) * density; + MathUtils.flipSignIf(mUrlFocusTranslationX, isRtl) * density; - final View menuButtonWrapper = getMenuButtonCoordinator().getMenuButton(); - if (menuButtonWrapper != null) { - animator = ObjectAnimator.ofFloat( - menuButtonWrapper, TRANSLATION_X, toolbarButtonTranslationX); - animator.setDuration(URL_FOCUS_TOOLBAR_BUTTONS_DURATION_MS); - animator.setInterpolator(BakedBezierInterpolator.FADE_OUT_CURVE); - animators.add(animator); - - animator = ObjectAnimator.ofFloat(menuButtonWrapper, ALPHA, 0); - animator.setDuration(URL_FOCUS_TOOLBAR_BUTTONS_DURATION_MS); - animator.setInterpolator(BakedBezierInterpolator.FADE_OUT_CURVE); - animators.add(animator); - } + animator = getMenuButtonCoordinator().getUrlFocusingAnimator(true); + animator.setDuration(URL_FOCUS_TOOLBAR_BUTTONS_DURATION_MS); + animator.setInterpolator(BakedBezierInterpolator.FADE_OUT_CURVE); + animators.add(animator); if (mToggleTabStackButton != null) { animator = ObjectAnimator.ofFloat( @@ -2041,20 +2010,10 @@ animator.setInterpolator(BakedBezierInterpolator.TRANSFORM_CURVE); animators.add(animator); - final View menuButtonWrapper = getMenuButtonCoordinator().getMenuButton(); - if (menuButtonWrapper != null) { - animator = ObjectAnimator.ofFloat(menuButtonWrapper, TRANSLATION_X, 0); - animator.setDuration(URL_FOCUS_TOOLBAR_BUTTONS_DURATION_MS); - animator.setStartDelay(URL_CLEAR_FOCUS_MENU_DELAY_MS); - animator.setInterpolator(BakedBezierInterpolator.TRANSFORM_CURVE); - animators.add(animator); - - animator = ObjectAnimator.ofFloat(menuButtonWrapper, ALPHA, 1); - animator.setDuration(URL_FOCUS_TOOLBAR_BUTTONS_DURATION_MS); - animator.setStartDelay(URL_CLEAR_FOCUS_MENU_DELAY_MS); - animator.setInterpolator(BakedBezierInterpolator.TRANSFORM_CURVE); - animators.add(animator); - } + animator = getMenuButtonCoordinator().getUrlFocusingAnimator(false); + animator.setDuration(URL_FOCUS_TOOLBAR_BUTTONS_DURATION_MS); + animator.setInterpolator(BakedBezierInterpolator.FADE_OUT_CURVE); + animators.add(animator); if (mToggleTabStackButton != null) { animator = ObjectAnimator.ofFloat(mToggleTabStackButton, TRANSLATION_X, 0);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/tasks/ReturnToChromeTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/tasks/ReturnToChromeTest.java index 5f735c9..f3797a6 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/tasks/ReturnToChromeTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/tasks/ReturnToChromeTest.java
@@ -413,6 +413,8 @@ // clang-format off @CommandLineFlags.Add({BASE_PARAMS + "/" + TAB_SWITCHER_ON_RETURN_MS_PARAM + "/0" + "/start_surface_variation/omniboxonly"}) + @DisableIf.Build(sdk_is_less_than = VERSION_CODES.Q, sdk_is_greater_than = VERSION_CODES.O, + message = "crbug.com/1134361") public void testTabSwitcherModeTriggeredBeyondThreshold_WarmStart() throws Exception { // clang-format on testTabSwitcherModeTriggeredBeyondThreshold();
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhoneTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhoneTest.java index cdd9aa723..73bd82af 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhoneTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhoneTest.java
@@ -4,12 +4,19 @@ package org.chromium.chrome.browser.toolbar.top; +import static androidx.test.espresso.Espresso.onView; +import static androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility; +import static androidx.test.espresso.matcher.ViewMatchers.withId; + +import static org.hamcrest.Matchers.allOf; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import android.graphics.Canvas; +import android.view.View; +import androidx.test.espresso.matcher.ViewMatchers.Visibility; import androidx.test.filters.MediumTest; import org.junit.Before; @@ -46,6 +53,7 @@ private Canvas mCanvas = new Canvas(); private ToolbarPhone mToolbar; + private View mToolbarButtonsContainer; private MenuButton mMenuButton; @Before @@ -54,22 +62,37 @@ mActivityTestRule.startMainActivityOnBlankPage(); mToolbar = mActivityTestRule.getActivity().findViewById(R.id.toolbar); - mMenuButton = Mockito.spy(mToolbar.findViewById(R.id.menu_button_wrapper)); - mToolbar.setMenuButtonCoordinatorForTesting(mMenuButtonCoordinator); - doReturn(mMenuButton).when(mMenuButtonCoordinator).getMenuButton(); + mToolbarButtonsContainer = mToolbar.findViewById(R.id.toolbar_buttons); } @Test @MediumTest public void testDrawTabSwitcherAnimation_menuButtonDrawn() { + mMenuButton = Mockito.spy(mToolbar.findViewById(R.id.menu_button_wrapper)); + mToolbar.setMenuButtonCoordinatorForTesting(mMenuButtonCoordinator); + doReturn(mMenuButton).when(mMenuButtonCoordinator).getMenuButton(); + TestThreadUtils.runOnUiThreadBlocking(() -> { mToolbar.drawTabSwitcherAnimationOverlay(mCanvas, 0); - verify(mMenuButton).drawTabSwitcherAnimationOverlay(mCanvas, 255); + verify(mMenuButtonCoordinator) + .drawTabSwitcherAnimationOverlay(mToolbarButtonsContainer, mCanvas, 255); mToolbar.setTextureCaptureMode(true); mToolbar.draw(mCanvas); - verify(mMenuButton, times(2)).drawTabSwitcherAnimationOverlay(mCanvas, 255); + verify(mMenuButtonCoordinator, times(2)) + .drawTabSwitcherAnimationOverlay(mToolbarButtonsContainer, mCanvas, 255); mToolbar.setTextureCaptureMode(false); }); } + + @Test + @MediumTest + public void testFocusAnimation_menuButtonHidesAndShows() { + TestThreadUtils.runOnUiThreadBlocking(() -> { mToolbar.onUrlFocusChange(true); }); + onView(allOf(withId(R.id.menu_button_wrapper), withEffectiveVisibility(Visibility.GONE))); + + TestThreadUtils.runOnUiThreadBlocking(() -> { mToolbar.onUrlFocusChange(false); }); + onView(allOf( + withId(R.id.menu_button_wrapper), withEffectiveVisibility(Visibility.VISIBLE))); + } }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionViewTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionViewTest.java index a2a1ee05..1712d1fd 100644 --- a/chrome/android/junit/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionViewTest.java +++ b/chrome/android/junit/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionViewTest.java
@@ -41,6 +41,9 @@ private View mContentView; @Mock + private Runnable mOnFocusListener; + + @Mock SuggestionViewDelegate mMockDelegate; // IMPORTANT: We need to extend the tested class here to support functionality currently @@ -87,6 +90,7 @@ mContentView = new View(mActivity); mView = new BaseSuggestionViewForTest(mContentView); mView.setDelegate(mMockDelegate); + mView.setOnFocusViaSelectionListener(mOnFocusListener); mActionIconWidthPx = mActivity.getResources().getDimensionPixelSize( R.dimen.omnibox_suggestion_action_icon_width); @@ -333,12 +337,12 @@ @Test public void setSelected_emitsOmniboxUpdateWhenSelected() { mView.setSelected(true); - verify(mMockDelegate, times(1)).onSetUrlToSuggestion(); + verify(mOnFocusListener, times(1)).run(); } @Test public void setSelected_noOmniboxUpdateWhenDeselected() { mView.setSelected(false); - verify(mMockDelegate, never()).onSetUrlToSuggestion(); + verify(mOnFocusListener, never()).run(); } }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/toolbar/menu_button/MenuButtonCoordinatorTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/toolbar/menu_button/MenuButtonCoordinatorTest.java index 826e2b9..e1d47669 100644 --- a/chrome/android/junit/src/org/chromium/chrome/browser/toolbar/menu_button/MenuButtonCoordinatorTest.java +++ b/chrome/android/junit/src/org/chromium/chrome/browser/toolbar/menu_button/MenuButtonCoordinatorTest.java
@@ -9,6 +9,7 @@ import static org.mockito.Mockito.verify; import android.app.Activity; +import android.content.res.Resources; import android.widget.ImageButton; import org.junit.Before; @@ -56,6 +57,8 @@ private Runnable mRequestRenderRunnable; @Mock ThemeColorProvider mThemeColorProvider; + @Mock + Resources mResources; private UpdateMenuItemHelper.MenuUiState mMenuUiState; private OneshotSupplierImpl<AppMenuCoordinator> mAppMenuSupplier; @@ -77,6 +80,10 @@ .when(mActivity) .findViewById(org.chromium.chrome.R.id.menu_button_wrapper); doReturn(mImageButton).when(mMenuButton).getImageButton(); + doReturn(mResources).when(mActivity).getResources(); + doReturn(10) + .when(mResources) + .getDimensionPixelSize(org.chromium.chrome.R.dimen.toolbar_url_focus_translation_x); mMenuButtonCoordinator = new MenuButtonCoordinator(mAppMenuSupplier, mControlsVisibilityDelegate, mActivity, mFocusFunction, mRequestRenderRunnable,
diff --git a/chrome/app/os_settings_strings.grdp b/chrome/app/os_settings_strings.grdp index fc23709..9a1ee42e 100644 --- a/chrome/app/os_settings_strings.grdp +++ b/chrome/app/os_settings_strings.grdp
@@ -1637,6 +1637,12 @@ <message name="IDS_SETTINGS_MOUSE_SWAP_BUTTONS_LABEL" desc="In Device Settings, the text next to the checkbox to set the primary mouse button to the right button instead of the left button."> Swap primary mouse button </message> + <message name="IDS_SETTINGS_PRIMARY_MOUSE_BUTTON_LEFT_LABEL" desc="In Device Settings, the text labelling the dropdown menu item for the left mouse button."> + Left button + </message> + <message name="IDS_SETTINGS_PRIMARY_MOUSE_BUTTON_RIGHT_LABEL" desc="In Device Settings, the text labelling the dropdown menu item for the right mouse button."> + Right button + </message> <message name="IDS_SETTINGS_MOUSE_REVERSE_SCROLL_LABEL" desc="In Device Settings, the text next to the checkbox to set reverse scrolling."> Enable reverse scrolling. <ph name="LINK_BEGIN"><a></ph>Learn more<ph name="LINK_END"></a></ph> </message>
diff --git a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_PRIMARY_MOUSE_BUTTON_LEFT_LABEL.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_PRIMARY_MOUSE_BUTTON_LEFT_LABEL.png.sha1 new file mode 100644 index 0000000..843d8b2 --- /dev/null +++ b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_PRIMARY_MOUSE_BUTTON_LEFT_LABEL.png.sha1
@@ -0,0 +1 @@ +0de32bdc0a9e5c95680629877c323c11d242cf03 \ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_PRIMARY_MOUSE_BUTTON_RIGHT_LABEL.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_PRIMARY_MOUSE_BUTTON_RIGHT_LABEL.png.sha1 new file mode 100644 index 0000000..d0f4c974 --- /dev/null +++ b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_PRIMARY_MOUSE_BUTTON_RIGHT_LABEL.png.sha1
@@ -0,0 +1 @@ +037c4cf7a5c655f6e15a4ca3c906230359440d50 \ No newline at end of file
diff --git a/chrome/browser/android/vr/BUILD.gn b/chrome/browser/android/vr/BUILD.gn index 4292683..90ca4dfa 100644 --- a/chrome/browser/android/vr/BUILD.gn +++ b/chrome/browser/android/vr/BUILD.gn
@@ -40,8 +40,6 @@ "gvr_scheduler_delegate.h", "gvr_util.cc", "gvr_util.h", - "mailbox_to_surface_bridge.cc", - "mailbox_to_surface_bridge.h", "metrics_util_android.cc", "metrics_util_android.h", "register_gvr_jni.cc", @@ -63,29 +61,16 @@ "vr_shell_delegate.h", "vrcore_install_helper.cc", "vrcore_install_helper.h", - "web_xr_presentation_state.cc", - "web_xr_presentation_state.h", ] if (enable_arcore) { sources += [ - "arcore_device/ar_image_transport.cc", - "arcore_device/ar_image_transport.h", - "arcore_device/ar_renderer.cc", - "arcore_device/ar_renderer.h", - "arcore_device/arcore_device.cc", - "arcore_device/arcore_device.h", "arcore_device/arcore_device_provider.cc", "arcore_device/arcore_device_provider.h", - "arcore_device/arcore_gl.cc", - "arcore_device/arcore_gl.h", - "arcore_device/arcore_gl_thread.cc", - "arcore_device/arcore_gl_thread.h", "arcore_device/arcore_install_helper.cc", "arcore_device/arcore_install_helper.h", "arcore_device/arcore_java_utils.cc", "arcore_device/arcore_java_utils.h", - "arcore_device/arcore_session_utils.h", ] } @@ -106,10 +91,12 @@ "//components/permissions", "//components/rappor", "//components/search_engines:search_engines", + "//components/webxr:webxr", "//content/public/browser", "//content/public/common", "//device/vr", "//device/vr:vr_base", + "//device/vr/android:vr_android", "//device/vr/buildflags:buildflags", "//services/device/public/mojom", "//services/metrics/public/cpp:ukm_builders", @@ -238,6 +225,7 @@ "//base/test:test_support", "//chrome/browser", "//components/translate/core/language_detection:language_detection", + "//components/webxr:webxr", "//device/vr:vr_fakes", "//device/vr/android/arcore", "//device/vr/public/mojom",
diff --git a/chrome/browser/android/vr/DEPS b/chrome/browser/android/vr/DEPS index 36776e6..eb5f1416 100644 --- a/chrome/browser/android/vr/DEPS +++ b/chrome/browser/android/vr/DEPS
@@ -3,6 +3,7 @@ "+cc/layers", "+cc/test", "+chrome/android/features/vr/jni_headers", + "+components/webxr", "+device/vr", "+services/metrics/public/cpp/ukm_builders.h", "+third_party/gvr-android-keyboard/src",
diff --git a/chrome/browser/android/vr/arcore_device/arcore_device_provider.cc b/chrome/browser/android/vr/arcore_device/arcore_device_provider.cc index 9c559d1..c553e99 100644 --- a/chrome/browser/android/vr/arcore_device/arcore_device_provider.cc +++ b/chrome/browser/android/vr/arcore_device/arcore_device_provider.cc
@@ -4,7 +4,11 @@ #include "chrome/browser/android/vr/arcore_device/arcore_device_provider.h" -#include "chrome/browser/android/vr/arcore_device/arcore_device.h" +#include "chrome/browser/android/vr/arcore_device/arcore_java_utils.h" +#include "components/webxr/mailbox_to_surface_bridge_impl.h" +#include "device/vr/android/arcore/ar_image_transport.h" +#include "device/vr/android/arcore/arcore_device.h" +#include "device/vr/android/arcore/arcore_impl.h" #include "device/vr/android/arcore/arcore_shim.h" namespace device { @@ -23,7 +27,11 @@ base::OnceClosure initialization_complete) { if (vr::IsArCoreSupported()) { DVLOG(2) << __func__ << ": ARCore is supported, creating device"; - arcore_device_ = std::make_unique<ArCoreDevice>(); + arcore_device_ = std::make_unique<ArCoreDevice>( + std::make_unique<ArCoreImplFactory>(), + std::make_unique<ArImageTransportFactory>(), + std::make_unique<webxr::MailboxToSurfaceBridgeFactoryImpl>(), + std::make_unique<vr::ArCoreJavaUtils>()); add_device_callback.Run( arcore_device_->GetId(), arcore_device_->GetVRDisplayInfo(),
diff --git a/chrome/browser/android/vr/arcore_device/arcore_device_unittest.cc b/chrome/browser/android/vr/arcore_device/arcore_device_unittest.cc index efab547..4837ebc 100644 --- a/chrome/browser/android/vr/arcore_device/arcore_device_unittest.cc +++ b/chrome/browser/android/vr/arcore_device/arcore_device_unittest.cc
@@ -2,21 +2,19 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "chrome/browser/android/vr/arcore_device/arcore_device.h" +#include "device/vr/android/arcore/arcore_device.h" #include <memory> #include "base/bind.h" #include "base/memory/ptr_util.h" #include "base/run_loop.h" -#include "chrome/browser/android/vr/arcore_device/ar_image_transport.h" -#include "chrome/browser/android/vr/arcore_device/arcore_gl.h" -#include "chrome/browser/android/vr/arcore_device/arcore_session_utils.h" #include "chrome/browser/android/vr/arcore_device/fake_arcore.h" -#include "chrome/browser/android/vr/mailbox_to_surface_bridge.h" +#include "components/webxr/mailbox_to_surface_bridge_impl.h" +#include "device/vr/android/arcore/ar_image_transport.h" +#include "device/vr/android/arcore/arcore_gl.h" +#include "device/vr/android/arcore/arcore_session_utils.h" #include "device/vr/public/mojom/vr_service.mojom.h" -#include "device/vr/test/fake_vr_device.h" -#include "device/vr/test/fake_vr_service_client.h" #include "mojo/public/cpp/bindings/associated_remote.h" #include "mojo/public/cpp/bindings/pending_remote.h" #include "mojo/public/cpp/bindings/remote.h" @@ -28,7 +26,7 @@ class StubArImageTransport : public ArImageTransport { public: explicit StubArImageTransport( - std::unique_ptr<vr::MailboxToSurfaceBridge> mailbox_bridge) + std::unique_ptr<MailboxToSurfaceBridge> mailbox_bridge) : ArImageTransport(std::move(mailbox_bridge)) {} void Initialize(vr::WebXrPresentationState*, @@ -49,7 +47,7 @@ return gpu::MailboxHolder(); } - std::unique_ptr<vr::MailboxToSurfaceBridge> mailbox_bridge_; + std::unique_ptr<MailboxToSurfaceBridge> mailbox_bridge_; const GLuint CAMERA_TEXTURE_ID = 10; }; @@ -58,12 +56,12 @@ ~StubArImageTransportFactory() override = default; std::unique_ptr<ArImageTransport> Create( - std::unique_ptr<vr::MailboxToSurfaceBridge> mailbox_bridge) override { + std::unique_ptr<MailboxToSurfaceBridge> mailbox_bridge) override { return std::make_unique<StubArImageTransport>(std::move(mailbox_bridge)); } }; -class StubMailboxToSurfaceBridge : public vr::MailboxToSurfaceBridge { +class StubMailboxToSurfaceBridge : public webxr::MailboxToSurfaceBridgeImpl { public: StubMailboxToSurfaceBridge() = default; @@ -73,14 +71,19 @@ bool IsConnected() override { return true; } - void CallCallback() { std::move(callback_).Run(); } - const uint32_t TEXTURE_ID = 1; private: base::OnceClosure callback_; }; +class StubMailboxToSurfaceBridgeFactory : public MailboxToSurfaceBridgeFactory { + public: + std::unique_ptr<device::MailboxToSurfaceBridge> Create() const override { + return std::make_unique<StubMailboxToSurfaceBridge>(); + } +}; + class StubArCoreSessionUtils : public vr::ArCoreSessionUtils { public: StubArCoreSessionUtils() = default; @@ -140,7 +143,6 @@ std::move(quit_closure).Run(); } - StubMailboxToSurfaceBridge* bridge; StubArCoreSessionUtils* session_utils; mojo::Remote<mojom::XRFrameDataProvider> frame_provider; mojo::AssociatedRemote<mojom::XREnvironmentIntegrationProvider> @@ -150,15 +152,13 @@ protected: void SetUp() override { - std::unique_ptr<StubMailboxToSurfaceBridge> bridge_ptr = - std::make_unique<StubMailboxToSurfaceBridge>(); - bridge = bridge_ptr.get(); std::unique_ptr<StubArCoreSessionUtils> session_utils_ptr = std::make_unique<StubArCoreSessionUtils>(); session_utils = session_utils_ptr.get(); device_ = std::make_unique<ArCoreDevice>( std::make_unique<FakeArCoreFactory>(), - std::make_unique<StubArImageTransportFactory>(), std::move(bridge_ptr), + std::make_unique<StubArImageTransportFactory>(), + std::make_unique<StubMailboxToSurfaceBridgeFactory>(), std::move(session_utils_ptr)); }
diff --git a/chrome/browser/android/vr/arcore_device/arcore_java_utils.h b/chrome/browser/android/vr/arcore_device/arcore_java_utils.h index bfc2fb4a..f5ccf57 100644 --- a/chrome/browser/android/vr/arcore_device/arcore_java_utils.h +++ b/chrome/browser/android/vr/arcore_device/arcore_java_utils.h
@@ -11,7 +11,7 @@ #include "base/android/scoped_java_ref.h" #include "base/callback.h" #include "base/memory/weak_ptr.h" -#include "chrome/browser/android/vr/arcore_device/arcore_session_utils.h" +#include "device/vr/android/arcore/arcore_session_utils.h" namespace vr {
diff --git a/chrome/browser/android/vr/arcore_device/fake_arcore.cc b/chrome/browser/android/vr/arcore_device/fake_arcore.cc index fff7829..d6476c13 100644 --- a/chrome/browser/android/vr/arcore_device/fake_arcore.cc +++ b/chrome/browser/android/vr/arcore_device/fake_arcore.cc
@@ -7,9 +7,6 @@ #include "base/android/android_hardware_buffer_compat.h" #include "base/numerics/math_constants.h" #include "base/single_thread_task_runner.h" -#include "ui/display/display.h" -#include "ui/gfx/buffer_types.h" -#include "ui/gl/gl_image_ahardwarebuffer.h" namespace {}
diff --git a/chrome/browser/android/vr/gvr_graphics_delegate.h b/chrome/browser/android/vr/gvr_graphics_delegate.h index 243ba4b..9f26abb 100644 --- a/chrome/browser/android/vr/gvr_graphics_delegate.h +++ b/chrome/browser/android/vr/gvr_graphics_delegate.h
@@ -14,9 +14,9 @@ #include "base/containers/queue.h" #include "base/macros.h" #include "base/memory/weak_ptr.h" -#include "chrome/browser/android/vr/web_xr_presentation_state.h" #include "chrome/browser/vr/base_graphics_delegate.h" #include "chrome/browser/vr/render_info.h" +#include "device/vr/android/web_xr_presentation_state.h" #include "device/vr/util/sliding_average.h" #include "third_party/gvr-android-sdk/src/libraries/headers/vr/gvr/capi/include/gvr.h" #include "third_party/gvr-android-sdk/src/libraries/headers/vr/gvr/capi/include/gvr_types.h"
diff --git a/chrome/browser/android/vr/gvr_install_helper.cc b/chrome/browser/android/vr/gvr_install_helper.cc index 2cb22a21..943ee51 100644 --- a/chrome/browser/android/vr/gvr_install_helper.cc +++ b/chrome/browser/android/vr/gvr_install_helper.cc
@@ -8,7 +8,7 @@ #include "base/bind.h" #include "base/logging.h" -#include "chrome/browser/android/vr/android_vr_utils.h" +#include "chrome/browser/android/vr/vr_module_provider.h" #include "chrome/browser/android/vr/vrcore_install_helper.h" using base::android::AttachCurrentThread;
diff --git a/chrome/browser/android/vr/gvr_scheduler_delegate.cc b/chrome/browser/android/vr/gvr_scheduler_delegate.cc index 1f911cf6..dd2f613 100644 --- a/chrome/browser/android/vr/gvr_scheduler_delegate.cc +++ b/chrome/browser/android/vr/gvr_scheduler_delegate.cc
@@ -14,11 +14,11 @@ #include "base/trace_event/trace_event.h" #include "base/trace_event/traced_value.h" #include "chrome/browser/android/vr/gl_browser_interface.h" -#include "chrome/browser/android/vr/mailbox_to_surface_bridge.h" #include "chrome/browser/android/vr/metrics_util_android.h" #include "chrome/browser/android/vr/scoped_gpu_trace.h" #include "chrome/browser/vr/scheduler_browser_renderer_interface.h" #include "chrome/browser/vr/scheduler_ui_interface.h" +#include "components/webxr/mailbox_to_surface_bridge_impl.h" #include "content/public/common/content_features.h" #include "device/vr/android/gvr/gvr_delegate.h" #include "gpu/command_buffer/common/shared_image_usage.h" @@ -304,7 +304,7 @@ gl::SurfaceTexture* surface_texture) { DCHECK(!mailbox_bridge_); DCHECK(!webxr_.mailbox_bridge_ready()); - mailbox_bridge_ = std::make_unique<MailboxToSurfaceBridge>(); + mailbox_bridge_ = std::make_unique<webxr::MailboxToSurfaceBridgeImpl>(); if (surface_texture) mailbox_bridge_->CreateSurface(surface_texture); mailbox_bridge_->CreateAndBindContextProvider(
diff --git a/chrome/browser/android/vr/gvr_scheduler_delegate.h b/chrome/browser/android/vr/gvr_scheduler_delegate.h index 4313f84..7fde4a6 100644 --- a/chrome/browser/android/vr/gvr_scheduler_delegate.h +++ b/chrome/browser/android/vr/gvr_scheduler_delegate.h
@@ -16,8 +16,8 @@ #include "base/memory/ref_counted.h" #include "chrome/browser/android/vr/android_vsync_helper.h" #include "chrome/browser/android/vr/gvr_graphics_delegate.h" -#include "chrome/browser/android/vr/web_xr_presentation_state.h" #include "chrome/browser/vr/base_scheduler_delegate.h" +#include "device/vr/android/web_xr_presentation_state.h" #include "device/vr/public/mojom/vr_service.mojom.h" #include "device/vr/util/sliding_average.h" #include "mojo/public/cpp/bindings/pending_associated_receiver.h" @@ -40,10 +40,13 @@ class GvrApi; } +namespace device { +class MailboxToSurfaceBridge; +} + namespace vr { class GlBrowserInterface; -class MailboxToSurfaceBridge; class SchedulerUiInterface; class ScopedGpuTrace; class SlidingTimeDeltaAverage; @@ -235,7 +238,7 @@ void(FrameType, const gfx::Transform&, std::unique_ptr<gl::GLFenceEGL>)> webxr_delayed_gvr_submit_; - std::unique_ptr<MailboxToSurfaceBridge> mailbox_bridge_; + std::unique_ptr<device::MailboxToSurfaceBridge> mailbox_bridge_; std::unique_ptr<ScopedGpuTrace> gpu_trace_; device::FPSMeter vr_ui_fps_meter_;
diff --git a/chrome/browser/android/vr/mailbox_to_surface_bridge.h b/chrome/browser/android/vr/mailbox_to_surface_bridge.h deleted file mode 100644 index 6b58838..0000000 --- a/chrome/browser/android/vr/mailbox_to_surface_bridge.h +++ /dev/null
@@ -1,147 +0,0 @@ -// Copyright 2017 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 CHROME_BROWSER_ANDROID_VR_MAILBOX_TO_SURFACE_BRIDGE_H_ -#define CHROME_BROWSER_ANDROID_VR_MAILBOX_TO_SURFACE_BRIDGE_H_ - -#include "base/callback.h" -#include "base/macros.h" -#include "base/memory/weak_ptr.h" -#include "base/single_thread_task_runner.h" -#include "gpu/command_buffer/common/sync_token.h" -#include "gpu/ipc/common/surface_handle.h" -#include "ui/gfx/buffer_format_util.h" -#include "ui/gfx/gpu_fence.h" -#include "ui/gl/android/scoped_java_surface.h" - -namespace gl { -class SurfaceTexture; -} // namespace gl - -namespace gfx { -class ColorSpace; -} - -namespace gpu { -class ContextSupport; -class GpuMemoryBufferImplAndroidHardwareBuffer; -struct MailboxHolder; -struct SyncToken; -namespace gles2 { -class GLES2Interface; -} -} // namespace gpu - -namespace viz { -class ContextProvider; -} - -namespace vr { - -class MailboxToSurfaceBridge { - public: - // It's OK to create an object instance and pass it to a different thread, - // i.e. to enable dependency injection for a unit test, but all methods on it - // must be called consistently on a single GL thread. This is verified by - // DCHECKs. - MailboxToSurfaceBridge(); - virtual ~MailboxToSurfaceBridge(); - - // Returns true if the GPU process connection is established and ready to use. - // Equivalent to waiting for on_initialized to be called. - virtual bool IsConnected(); - - // Checks if a workaround from "gpu/config/gpu_driver_bug_workaround_type.h" - // is active. Requires initialization to be complete. - bool IsGpuWorkaroundEnabled(int32_t workaround); - - // This call is needed for Surface transport, in that case it must be called - // on the GL thread with a valid local native GL context. If it's not used, - // only the SharedBuffer transport methods are available. - void CreateSurface(gl::SurfaceTexture*); - - // Asynchronously create the context using the surface provided by an earlier - // CreateSurface call, or an offscreen context if that wasn't called. Also - // binds the context provider to the current thread (making it the GL thread), - // and calls the callback on the GL thread. - virtual void CreateAndBindContextProvider(base::OnceClosure callback); - - // All other public methods below must be called on the GL thread - // (except when marked otherwise). - - void ResizeSurface(int width, int height); - - // Returns true if swapped successfully. This can fail if the GL - // context isn't ready for use yet, in that case the caller - // won't get a new frame on the SurfaceTexture. - bool CopyMailboxToSurfaceAndSwap(const gpu::MailboxHolder& mailbox); - - void GenSyncToken(gpu::SyncToken* out_sync_token); - - void WaitSyncToken(const gpu::SyncToken& sync_token); - - // Copies a GpuFence from the local context to the GPU process, - // and issues a server wait for it. - void WaitForClientGpuFence(gfx::GpuFence*); - - // Creates a GpuFence in the GPU process after the supplied sync_token - // completes, and copies it for use in the local context. This is - // asynchronous, the callback receives the GpuFence once it's available. - void CreateGpuFence( - const gpu::SyncToken& sync_token, - base::OnceCallback<void(std::unique_ptr<gfx::GpuFence>)> callback); - - // Creates a shared image bound to |buffer|. Returns a mailbox holder that - // references the shared image with a sync token representing a point after - // the creation. Caller must call DestroySharedImage to free the shared image. - // Does not take ownership of |buffer| or retain any references to it. - gpu::MailboxHolder CreateSharedImage( - gpu::GpuMemoryBufferImplAndroidHardwareBuffer* buffer, - const gfx::ColorSpace& color_space, - uint32_t usage); - - // Destroys a shared image created by CreateSharedImage. The mailbox_holder's - // sync_token must have been updated to a sync token after the last use of the - // shared image. - void DestroySharedImage(const gpu::MailboxHolder& mailbox_holder); - - private: - void BindContextProviderToCurrentThread(); - void OnContextAvailableOnUiThread( - scoped_refptr<viz::ContextProvider> provider); - void InitializeRenderer(); - void DestroyContext(); - void DrawQuad(unsigned int textureHandle); - - scoped_refptr<viz::ContextProvider> context_provider_; - std::unique_ptr<gl::ScopedJavaSurface> surface_; - gpu::gles2::GLES2Interface* gl_ = nullptr; - gpu::ContextSupport* context_support_ = nullptr; - int surface_handle_ = gpu::kNullSurfaceHandle; - // TODO(https://crbug.com/836524): shouldn't have both of these closures - // in the same class like this. - base::OnceClosure on_context_bound_; - - int surface_width_ = 0; - int surface_height_ = 0; - - // If true, surface width/height is the intended size that should be applied - // to the surface once it's ready for use. - bool needs_resize_ = false; - - // A swap ID which is passed to GL swap. Incremented each call. - uint64_t swap_id_ = 0; - - // A task runner for the GL thread - scoped_refptr<base::SingleThreadTaskRunner> gl_thread_task_runner_; - - // Must be last. - base::WeakPtrFactory<MailboxToSurfaceBridge> weak_ptr_factory_{this}; - - DISALLOW_COPY_AND_ASSIGN(MailboxToSurfaceBridge); -}; - -} // namespace vr - -#endif // CHROME_BROWSER_ANDROID_VR_MAILBOX_TO_SURFACE_BRIDGE_H_
diff --git a/chrome/browser/android/vr/vr_shell_delegate.cc b/chrome/browser/android/vr/vr_shell_delegate.cc index 3caee0e..f2904d8 100644 --- a/chrome/browser/android/vr/vr_shell_delegate.cc +++ b/chrome/browser/android/vr/vr_shell_delegate.cc
@@ -9,7 +9,6 @@ #include "base/android/jni_android.h" #include "base/bind.h" #include "chrome/android/features/vr/jni_headers/VrShellDelegate_jni.h" -#include "chrome/browser/android/vr/arcore_device/arcore_device_provider.h" #include "chrome/browser/android/vr/vr_shell.h" #include "chrome/browser/android/vr/vrcore_install_helper.h" #include "chrome/browser/browser_process.h"
diff --git a/chrome/browser/apps/app_service/app_icon_factory_unittest.cc b/chrome/browser/apps/app_service/app_icon_factory_unittest.cc index c37eea8..318aaac 100644 --- a/chrome/browser/apps/app_service/app_icon_factory_unittest.cc +++ b/chrome/browser/apps/app_service/app_icon_factory_unittest.cc
@@ -44,6 +44,7 @@ #if defined(OS_CHROMEOS) #include "chrome/browser/chromeos/arc/icon_decode_request.h" #include "chrome/browser/ui/app_list/icon_standardizer.h" +#include "chrome/browser/ui/app_list/md_icon_normalizer.h" #include "chrome/grit/chrome_unscaled_resources.h" #include "components/arc/mojom/intent_helper.mojom.h" #endif @@ -612,6 +613,7 @@ run_loop.QuitClosure())); run_loop.Run(); + extensions::ChromeAppIcon::ResizeFunction resize_function; #if defined(OS_CHROMEOS) if (base::FeatureList::IsEnabled(features::kAppServiceAdaptiveIcon)) { if (purpose == IconPurpose::ANY) { @@ -621,13 +623,16 @@ if (purpose == IconPurpose::MASKABLE) { output_image_skia = apps::ApplyBackgroundAndMask(output_image_skia); } + } else { + resize_function = + base::BindRepeating(&app_list::MaybeResizeAndPadIconForMd); } #endif extensions::ChromeAppIcon::ApplyEffects( - kSizeInDip, extensions::ChromeAppIcon::ResizeFunction(), - true /* app_launchable */, true /* from_bookmark */, - extensions::ChromeAppIcon::Badge::kNone, &output_image_skia); + kSizeInDip, resize_function, true /* app_launchable */, + true /* from_bookmark */, extensions::ChromeAppIcon::Badge::kNone, + &output_image_skia); EnsureRepresentationsLoaded(output_image_skia); } @@ -729,10 +734,17 @@ {{1.0, kIconSize1}, {2.0, kIconSize2}}, src_image_skia); gfx::ImageSkia dst_image_skia; - LoadIconFromWebApp( - app_id, - apps::IconEffects::kRoundCorners | apps::IconEffects::kCrOsStandardIcon, - dst_image_skia); + apps::IconEffects icon_effect = apps::IconEffects::kRoundCorners; + +#if defined(OS_CHROMEOS) + if (base::FeatureList::IsEnabled(features::kAppServiceAdaptiveIcon)) { + icon_effect |= apps::IconEffects::kCrOsStandardIcon; + } else { + icon_effect |= apps::IconEffects::kResizeAndPad; + } +#endif + + LoadIconFromWebApp(app_id, icon_effect, dst_image_skia); VerifyIcon(src_image_skia, dst_image_skia); } @@ -758,10 +770,17 @@ src_data); apps::mojom::IconValuePtr icon; - LoadCompressedIconBlockingFromWebApp( - app_id, - apps::IconEffects::kRoundCorners | apps::IconEffects::kCrOsStandardIcon, - icon); + apps::IconEffects icon_effect = apps::IconEffects::kRoundCorners; + +#if defined(OS_CHROMEOS) + if (base::FeatureList::IsEnabled(features::kAppServiceAdaptiveIcon)) { + icon_effect |= apps::IconEffects::kCrOsStandardIcon; + } else { + icon_effect |= apps::IconEffects::kResizeAndPad; + } +#endif + + LoadCompressedIconBlockingFromWebApp(app_id, icon_effect, icon); VerifyCompressedIcon(src_data, icon); } @@ -783,20 +802,25 @@ RegisterApp(std::move(web_app)); #if defined(OS_CHROMEOS) - ASSERT_TRUE( - icon_manager().HasIcons(app_id, IconPurpose::MASKABLE, {kIconSize2})); + if (base::FeatureList::IsEnabled(features::kAppServiceAdaptiveIcon)) { + ASSERT_TRUE( + icon_manager().HasIcons(app_id, IconPurpose::MASKABLE, {kIconSize2})); - gfx::ImageSkia src_image_skia; - GenerateWebAppIcon(app_id, IconPurpose::MASKABLE, {kIconSize2}, - {{1.0, kIconSize2}, {2.0, kIconSize2}}, src_image_skia); + gfx::ImageSkia src_image_skia; + GenerateWebAppIcon(app_id, IconPurpose::MASKABLE, {kIconSize2}, + {{1.0, kIconSize2}, {2.0, kIconSize2}}, src_image_skia); - gfx::ImageSkia dst_image_skia; - LoadIconFromWebApp(app_id, - apps::IconEffects::kRoundCorners | - apps::IconEffects::kCrOsStandardBackground | - apps::IconEffects::kCrOsStandardMask, - dst_image_skia); -#else + gfx::ImageSkia dst_image_skia; + LoadIconFromWebApp(app_id, + apps::IconEffects::kRoundCorners | + apps::IconEffects::kCrOsStandardBackground | + apps::IconEffects::kCrOsStandardMask, + dst_image_skia); + VerifyIcon(src_image_skia, dst_image_skia); + return; + } +#endif + ASSERT_TRUE(icon_manager().HasIcons(app_id, IconPurpose::ANY, {kIconSize1})); gfx::ImageSkia src_image_skia; @@ -805,7 +829,6 @@ gfx::ImageSkia dst_image_skia; LoadIconFromWebApp(app_id, apps::IconEffects::kRoundCorners, dst_image_skia); -#endif VerifyIcon(src_image_skia, dst_image_skia); } @@ -828,30 +851,33 @@ std::vector<uint8_t> src_data; apps::mojom::IconValuePtr icon; + apps::IconEffects icon_effect = apps::IconEffects::kRoundCorners; #if defined(OS_CHROMEOS) - ASSERT_TRUE( - icon_manager().HasIcons(app_id, IconPurpose::MASKABLE, {kIconSize2})); + if (base::FeatureList::IsEnabled(features::kAppServiceAdaptiveIcon)) { + icon_effect |= apps::IconEffects::kCrOsStandardBackground | + apps::IconEffects::kCrOsStandardMask; + ASSERT_TRUE( + icon_manager().HasIcons(app_id, IconPurpose::MASKABLE, {kIconSize2})); - GenerateWebAppCompressedIcon(app_id, IconPurpose::MASKABLE, {kIconSize2}, - {{1.0, kIconSize2}, {2.0, kIconSize2}}, - src_data); + GenerateWebAppCompressedIcon(app_id, IconPurpose::MASKABLE, {kIconSize2}, + {{1.0, kIconSize2}, {2.0, kIconSize2}}, + src_data); - LoadCompressedIconBlockingFromWebApp( - app_id, - apps::IconEffects::kRoundCorners | - apps::IconEffects::kCrOsStandardBackground | - apps::IconEffects::kCrOsStandardMask, - icon); -#else + LoadCompressedIconBlockingFromWebApp(app_id, icon_effect, icon); + VerifyCompressedIcon(src_data, icon); + return; + } + + icon_effect |= apps::IconEffects::kResizeAndPad; +#endif + ASSERT_TRUE(icon_manager().HasIcons(app_id, IconPurpose::ANY, {kIconSize1})); GenerateWebAppCompressedIcon(app_id, IconPurpose::ANY, {kIconSize1}, {{1.0, kIconSize1}, {2.0, kIconSize1}}, src_data); - LoadCompressedIconBlockingFromWebApp(app_id, apps::IconEffects::kRoundCorners, - icon); -#endif + LoadCompressedIconBlockingFromWebApp(app_id, icon_effect, icon); VerifyCompressedIcon(src_data, icon); } @@ -879,10 +905,17 @@ {{1.0, kIconSize2}, {2.0, kIconSize2}}, src_image_skia); gfx::ImageSkia dst_image_skia; - LoadIconFromWebApp( - app_id, - apps::IconEffects::kRoundCorners | apps::IconEffects::kCrOsStandardIcon, - dst_image_skia); + apps::IconEffects icon_effect = apps::IconEffects::kRoundCorners; + +#if defined(OS_CHROMEOS) + if (base::FeatureList::IsEnabled(features::kAppServiceAdaptiveIcon)) { + icon_effect |= apps::IconEffects::kCrOsStandardIcon; + } else { + icon_effect |= apps::IconEffects::kResizeAndPad; + } +#endif + + LoadIconFromWebApp(app_id, icon_effect, dst_image_skia); VerifyIcon(src_image_skia, dst_image_skia); } @@ -944,10 +977,17 @@ {{1.0, kIconSize2}, {2.0, kIconSize4}}, src_image_skia); gfx::ImageSkia dst_image_skia; - LoadIconFromWebApp( - app_id, - apps::IconEffects::kRoundCorners | apps::IconEffects::kCrOsStandardIcon, - dst_image_skia); + apps::IconEffects icon_effect = apps::IconEffects::kRoundCorners; + +#if defined(OS_CHROMEOS) + if (base::FeatureList::IsEnabled(features::kAppServiceAdaptiveIcon)) { + icon_effect |= apps::IconEffects::kCrOsStandardIcon; + } else { + icon_effect |= apps::IconEffects::kResizeAndPad; + } +#endif + + LoadIconFromWebApp(app_id, icon_effect, dst_image_skia); VerifyIcon(src_image_skia, dst_image_skia); }
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc index c5c7a66..26a0847 100644 --- a/chrome/browser/chrome_content_browser_client.cc +++ b/chrome/browser/chrome_content_browser_client.cc
@@ -3373,22 +3373,6 @@ web_prefs->immersive_mode_enabled = vr::VrTabHelper::IsInVr(contents); } - if (base::FeatureList::IsEnabled(features::kLowPriorityIframes)) { - // Obtain the maximum effective connection type at which the feature is - // enabled. - std::string effective_connection_type_param = - base::GetFieldTrialParamValueByFeature( - features::kLowPriorityIframes, - "max_effective_connection_type_threshold"); - - base::Optional<net::EffectiveConnectionType> effective_connection_type = - net::GetEffectiveConnectionTypeForName(effective_connection_type_param); - if (effective_connection_type) { - web_prefs->low_priority_iframes_threshold = - effective_connection_type.value(); - } - } - web_prefs->lazy_load_enabled = !contents || !contents->GetDelegate() || contents->GetDelegate()->ShouldAllowLazyLoad();
diff --git a/chrome/browser/chromeos/accessibility/accessibility_manager.cc b/chrome/browser/chromeos/accessibility/accessibility_manager.cc index b2be0a10..79adcbd6 100644 --- a/chrome/browser/chromeos/accessibility/accessibility_manager.cc +++ b/chrome/browser/chromeos/accessibility/accessibility_manager.cc
@@ -47,6 +47,7 @@ #include "chrome/browser/extensions/api/braille_display_private/stub_braille_controller.h" #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/profiles/profile_manager.h" +#include "chrome/browser/ui/ash/keyboard/chrome_keyboard_controller_client.h" #include "chrome/browser/ui/ash/multi_user/multi_user_util.h" #include "chrome/browser/ui/singleton_tabs.h" #include "chrome/common/chrome_paths.h" @@ -1436,6 +1437,12 @@ void AccessibilityManager::PostLoadSwitchAccess() { InitializeFocusRings(extension_misc::kSwitchAccessExtensionId); + + was_vk_enabled_before_switch_access_ = + ChromeKeyboardControllerClient::Get()->IsEnableFlagSet( + keyboard::KeyboardEnableFlag::kExtensionEnabled); + ChromeKeyboardControllerClient::Get()->SetEnableFlag( + keyboard::KeyboardEnableFlag::kExtensionEnabled); } void AccessibilityManager::PostUnloadSwitchAccess() { @@ -1444,6 +1451,13 @@ // Clear the accessibility focus ring. RemoveFocusRings(extension_misc::kSwitchAccessExtensionId); + + if (!was_vk_enabled_before_switch_access_) { + ChromeKeyboardControllerClient::Get()->ClearEnableFlag( + keyboard::KeyboardEnableFlag::kExtensionEnabled); + } else { + was_vk_enabled_before_switch_access_ = false; + } } void AccessibilityManager::PostLoadAccessibilityCommon() {
diff --git a/chrome/browser/chromeos/accessibility/accessibility_manager.h b/chrome/browser/chromeos/accessibility/accessibility_manager.h index 5772f19f..45432f3 100644 --- a/chrome/browser/chromeos/accessibility/accessibility_manager.h +++ b/chrome/browser/chromeos/accessibility/accessibility_manager.h
@@ -489,10 +489,14 @@ // Used to set the audio focus enforcement type for ChromeVox. mojo::Remote<media_session::mojom::AudioFocusManager> audio_focus_manager_; + // Whether the virtual keyboard was enabled before Switch Access loaded. + bool was_vk_enabled_before_switch_access_ = false; + base::WeakPtrFactory<AccessibilityManager> weak_ptr_factory_{this}; friend class DictationTest; friend class SwitchAccessTest; + DISALLOW_COPY_AND_ASSIGN(AccessibilityManager); };
diff --git a/chrome/browser/chromeos/arc/bluetooth/arc_bluetooth_bridge.cc b/chrome/browser/chromeos/arc/bluetooth/arc_bluetooth_bridge.cc index 771a0cf1..68c52b28 100644 --- a/chrome/browser/chromeos/arc/bluetooth/arc_bluetooth_bridge.cc +++ b/chrome/browser/chromeos/arc/bluetooth/arc_bluetooth_bridge.cc
@@ -5,6 +5,7 @@ #include "chrome/browser/chromeos/arc/bluetooth/arc_bluetooth_bridge.h" #include <bluetooth/bluetooth.h> +#include <bluetooth/l2cap.h> #include <bluetooth/rfcomm.h> #include <fcntl.h> #include <stddef.h> @@ -1916,8 +1917,8 @@ } } -void ArcBluetoothBridge::OpenBluetoothSocket( - OpenBluetoothSocketCallback callback) { +void ArcBluetoothBridge::OpenBluetoothSocketDeprecated( + OpenBluetoothSocketDeprecatedCallback callback) { base::ScopedFD sock(socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM)); if (!sock.is_valid()) { LOG(ERROR) << "Failed to open socket."; @@ -2930,17 +2931,79 @@ namespace { -constexpr int kMinRfcommChannelNum = 1; -constexpr int kMaxRfcommChannelNum = 30; +constexpr int kAutoSockPort = 0; +constexpr int kMinRfcommChannel = 1; +constexpr int kMaxRfcommChannel = 30; -base::ScopedFD OpenRfcommSocket(int32_t optval) { - base::ScopedFD sock(socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM)); +// Copied from the values of L2CAP_PSM_LE_DYN_START and L2CAP_PSM_LE_DYN_END +// in /include/net/bluetooth/l2cap.h +constexpr int kMinL2capLePsm = 0x0080; +constexpr int kMaxL2capLePsm = 0x00FF; + +union BluetoothSocketAddress { + sockaddr sock; + sockaddr_rc rfcomm; + sockaddr_l2 l2cap; +}; + +bool IsValidPort(mojom::BluetoothSocketType sock_type, int port) { + switch (sock_type) { + case mojom::BluetoothSocketType::TYPE_RFCOMM: + return port <= kMaxRfcommChannel && port >= kMinRfcommChannel; + case mojom::BluetoothSocketType::TYPE_L2CAP_LE: + return port <= kMaxL2capLePsm && port >= kMinL2capLePsm; + } +} + +int32_t GetSockOptvalFromFlags(mojom::BluetoothSocketType sock_type, + mojom::BluetoothSocketFlagsPtr sock_flags) { + int optval = 0; + switch (sock_type) { + case mojom::BluetoothSocketType::TYPE_RFCOMM: + optval |= sock_flags->encrypt ? RFCOMM_LM_ENCRYPT : 0; + optval |= sock_flags->auth ? RFCOMM_LM_AUTH : 0; + optval |= sock_flags->auth_mitm ? RFCOMM_LM_SECURE : 0; + optval |= sock_flags->auth_16_digit ? RFCOMM_LM_SECURE : 0; + return optval; + case mojom::BluetoothSocketType::TYPE_L2CAP_LE: + optval |= sock_flags->encrypt ? L2CAP_LM_ENCRYPT : 0; + optval |= sock_flags->auth ? L2CAP_LM_AUTH : 0; + optval |= sock_flags->auth_mitm ? L2CAP_LM_SECURE : 0; + optval |= sock_flags->auth_16_digit ? L2CAP_LM_SECURE : 0; + return optval; + } +} + +// Opens an AF_BLUETOOTH socket with |sock_type|, sets L2CAP_LM or RFCOMM_LM +// with |optval|, and binds the socket to address with |port|. +base::ScopedFD OpenBluetoothSocketImpl(mojom::BluetoothSocketType sock_type, + int32_t optval, + uint16_t port) { + int protocol; + int level; + int optname; + switch (sock_type) { + case mojom::BluetoothSocketType::TYPE_RFCOMM: + protocol = BTPROTO_RFCOMM; + level = SOL_RFCOMM; + optname = RFCOMM_LM; + break; + case mojom::BluetoothSocketType::TYPE_L2CAP_LE: + protocol = BTPROTO_L2CAP; + level = SOL_L2CAP; + optname = L2CAP_LM; + break; + default: + LOG(ERROR) << "Unknown socket type " << sock_type; + return {}; + } + + base::ScopedFD sock(socket(AF_BLUETOOTH, SOCK_STREAM, protocol)); if (!sock.is_valid()) { PLOG(ERROR) << "Failed to open bluetooth socket."; return {}; } - if (setsockopt(sock.get(), SOL_RFCOMM, RFCOMM_LM, &optval, sizeof(optval)) == - -1) { + if (setsockopt(sock.get(), level, optname, &optval, sizeof(optval)) == -1) { PLOG(ERROR) << "Failed to setopt() on socket."; return {}; } @@ -2949,127 +3012,240 @@ PLOG(ERROR) << "Failed to fcntl() on socket."; return {}; } + + BluetoothSocketAddress sa = {}; + switch (sock_type) { + case mojom::BluetoothSocketType::TYPE_RFCOMM: + sa.rfcomm.rc_family = AF_BLUETOOTH; + sa.rfcomm.rc_channel = port; + break; + case mojom::BluetoothSocketType::TYPE_L2CAP_LE: + sa.l2cap.l2_family = AF_BLUETOOTH; + sa.l2cap.l2_psm = htobs(port); + sa.l2cap.l2_bdaddr_type = BDADDR_LE_PUBLIC; + break; + default: + LOG(ERROR) << "Unknown socket type " << sock_type; + return {}; + } + + if (bind(sock.get(), &sa.sock, sizeof(sa)) == -1) { + PLOG(ERROR) << "Failed to bind()"; + return {}; + } + return sock; } } // namespace -void ArcBluetoothBridge::RfcommListen(int32_t channel, - int32_t optval, - RfcommListenCallback callback) { - // |channel|=0 means selecting a available channel automatically. - if (channel != 0 && - (channel < kMinRfcommChannelNum || channel > kMaxRfcommChannelNum)) { +void ArcBluetoothBridge::RfcommListenDeprecated( + int32_t channel, + int32_t optval, + RfcommListenDeprecatedCallback callback) { + if (channel != kAutoSockPort && + !IsValidPort(mojom::BluetoothSocketType::TYPE_RFCOMM, channel)) { LOG(ERROR) << "Invalid channel number"; std::move(callback).Run( mojom::BluetoothStatus::FAIL, /*channel=*/0, mojo::PendingReceiver<mojom::RfcommListeningSocketClient>()); return; } - uint8_t listen_channel = static_cast<uint8_t>(channel); - auto sock_wrapper = RfcommCreateListenSocket(optval, &listen_channel); + + uint16_t listen_channel = static_cast<uint16_t>(channel); + auto sock_wrapper = CreateBluetoothListenSocket( + mojom::BluetoothSocketType::TYPE_RFCOMM, optval, &listen_channel); if (!sock_wrapper) { std::move(callback).Run( mojom::BluetoothStatus::FAIL, /*channel=*/0, mojo::PendingReceiver<mojom::RfcommListeningSocketClient>()); return; } - std::move(callback).Run(mojom::BluetoothStatus::SUCCESS, listen_channel, - sock_wrapper->remote.BindNewPipeAndPassReceiver()); - sock_wrapper->remote.set_disconnect_handler( - base::BindOnce(&ArcBluetoothBridge::RfcommCloseListeningSocket, + sock_wrapper->created_by_deprecated_method = true; + std::move(callback).Run( + mojom::BluetoothStatus::SUCCESS, listen_channel, + sock_wrapper->deprecated_remote.BindNewPipeAndPassReceiver()); + + sock_wrapper->deprecated_remote.set_disconnect_handler( + base::BindOnce(&ArcBluetoothBridge::CloseBluetoothListeningSocket, weak_factory_.GetWeakPtr(), sock_wrapper.get())); listening_sockets_.insert(std::move(sock_wrapper)); } -void ArcBluetoothBridge::RfcommCloseListeningSocket( - RfcommListeningSocket* ptr) { +void ArcBluetoothBridge::BluetoothSocketListen( + mojom::BluetoothSocketType sock_type, + mojom::BluetoothSocketFlagsPtr sock_flags, + int32_t port, + BluetoothSocketListenCallback callback) { + if (!mojom::IsKnownEnumValue(sock_type)) { + LOG(ERROR) << "Unsupported sock type " << sock_type; + std::move(callback).Run( + mojom::BluetoothStatus::UNSUPPORTED, /*port=*/0, + mojo::PendingReceiver<mojom::BluetoothListenSocketClient>()); + return; + } + + if (port != kAutoSockPort && !IsValidPort(sock_type, port)) { + LOG(ERROR) << "Invalid port number " << port; + std::move(callback).Run( + mojom::BluetoothStatus::FAIL, /*port=*/0, + mojo::PendingReceiver<mojom::BluetoothListenSocketClient>()); + return; + } + + int32_t optval = GetSockOptvalFromFlags(sock_type, std::move(sock_flags)); + uint16_t listen_port = static_cast<uint16_t>(port); + auto sock_wrapper = + CreateBluetoothListenSocket(sock_type, optval, &listen_port); + if (!sock_wrapper) { + std::move(callback).Run( + mojom::BluetoothStatus::FAIL, /*port=*/0, + mojo::PendingReceiver<mojom::BluetoothListenSocketClient>()); + return; + } + + std::move(callback).Run(mojom::BluetoothStatus::SUCCESS, listen_port, + sock_wrapper->remote.BindNewPipeAndPassReceiver()); + sock_wrapper->remote.set_disconnect_handler( + base::BindOnce(&ArcBluetoothBridge::CloseBluetoothListeningSocket, + weak_factory_.GetWeakPtr(), sock_wrapper.get())); + listening_sockets_.insert(std::move(sock_wrapper)); +} + +void ArcBluetoothBridge::CloseBluetoothListeningSocket( + BluetoothListeningSocket* ptr) { auto itr = listening_sockets_.find(ptr); listening_sockets_.erase(itr); } -void ArcBluetoothBridge::RfcommConnect(mojom::BluetoothAddressPtr remote_addr, - int32_t channel, - int32_t optval, - RfcommConnectCallback callback) { - if (channel < kMinRfcommChannelNum || channel > kMaxRfcommChannelNum) { - LOG(ERROR) << "Invalid channel number"; +void ArcBluetoothBridge::RfcommConnectDeprecated( + mojom::BluetoothAddressPtr remote_addr, + int32_t channel, + int32_t optval, + RfcommConnectDeprecatedCallback callback) { + if (!IsValidPort(mojom::BluetoothSocketType::TYPE_RFCOMM, channel)) { + LOG(ERROR) << "Invalid channel number " << channel; std::move(callback).Run(mojom::BluetoothStatus::FAIL, mojom::RfcommConnectingSocketClientRequest()); return; } - auto sock_wrapper = RfcommCreateConnectSocket( - std::move(remote_addr), static_cast<uint8_t>(channel), optval); + auto sock_wrapper = CreateBluetoothConnectSocket( + mojom::BluetoothSocketType::TYPE_RFCOMM, optval, std::move(remote_addr), + static_cast<uint16_t>(channel)); if (!sock_wrapper) { std::move(callback).Run(mojom::BluetoothStatus::FAIL, mojom::RfcommConnectingSocketClientRequest()); return; } + sock_wrapper->created_by_deprecated_method = true; + std::move(callback).Run( + mojom::BluetoothStatus::SUCCESS, + sock_wrapper->deprecated_remote.BindNewPipeAndPassReceiver()); + sock_wrapper->deprecated_remote.set_disconnect_handler( + base::BindOnce(&ArcBluetoothBridge::CloseBluetoothConnectingSocket, + weak_factory_.GetWeakPtr(), sock_wrapper.get())); + connecting_sockets_.insert(std::move(sock_wrapper)); +} + +void ArcBluetoothBridge::BluetoothSocketConnect( + mojom::BluetoothSocketType sock_type, + mojom::BluetoothSocketFlagsPtr sock_flags, + mojom::BluetoothAddressPtr remote_addr, + int32_t port, + BluetoothSocketConnectCallback callback) { + if (!mojom::IsKnownEnumValue(sock_type)) { + LOG(ERROR) << "Unsupported sock type " << sock_type; + std::move(callback).Run(mojom::BluetoothStatus::UNSUPPORTED, + mojom::BluetoothConnectSocketClientRequest()); + return; + } + + if (!IsValidPort(sock_type, port)) { + LOG(ERROR) << "Invalid port number " << port; + std::move(callback).Run(mojom::BluetoothStatus::FAIL, + mojom::BluetoothConnectSocketClientRequest()); + return; + } + + int32_t optval = GetSockOptvalFromFlags(sock_type, std::move(sock_flags)); + auto sock_wrapper = CreateBluetoothConnectSocket( + sock_type, optval, std::move(remote_addr), static_cast<uint16_t>(port)); + if (!sock_wrapper) { + std::move(callback).Run(mojom::BluetoothStatus::FAIL, + mojom::BluetoothConnectSocketClientRequest()); + return; + } + std::move(callback).Run(mojom::BluetoothStatus::SUCCESS, sock_wrapper->remote.BindNewPipeAndPassReceiver()); sock_wrapper->remote.set_disconnect_handler( - base::BindOnce(&ArcBluetoothBridge::RfcommCloseConnectingSocket, + base::BindOnce(&ArcBluetoothBridge::CloseBluetoothConnectingSocket, weak_factory_.GetWeakPtr(), sock_wrapper.get())); connecting_sockets_.insert(std::move(sock_wrapper)); } -void ArcBluetoothBridge::RfcommCloseConnectingSocket( - RfcommConnectingSocket* ptr) { +void ArcBluetoothBridge::CloseBluetoothConnectingSocket( + BluetoothConnectingSocket* ptr) { auto itr = connecting_sockets_.find(ptr); connecting_sockets_.erase(itr); } -std::unique_ptr<ArcBluetoothBridge::RfcommListeningSocket> -ArcBluetoothBridge::RfcommCreateListenSocket(int32_t optval, uint8_t* channel) { - base::ScopedFD sock = OpenRfcommSocket(optval); +std::unique_ptr<ArcBluetoothBridge::BluetoothListeningSocket> +ArcBluetoothBridge::CreateBluetoothListenSocket( + mojom::BluetoothSocketType sock_type, + int32_t optval, + uint16_t* port) { + DCHECK(port); + base::ScopedFD sock = OpenBluetoothSocketImpl(sock_type, optval, *port); if (!sock.is_valid()) { LOG(ERROR) << "Failed to open listen socket."; return nullptr; } - DCHECK(channel); - struct sockaddr_rc my_addr = {}; - my_addr.rc_family = AF_BLUETOOTH; - my_addr.rc_channel = *channel; - - if (bind(sock.get(), reinterpret_cast<const struct sockaddr*>(&my_addr), - sizeof(my_addr)) == -1) { - PLOG(ERROR) << "Failed to bind()"; - return nullptr; - } if (listen(sock.get(), /*backlog=*/1) == -1) { PLOG(ERROR) << "Failed to listen()"; return nullptr; } - socklen_t addr_len = sizeof(my_addr); - if (getsockname(sock.get(), reinterpret_cast<struct sockaddr*>(&my_addr), - &addr_len) == -1) { + BluetoothSocketAddress local_addr; + socklen_t addr_len = sizeof(local_addr); + if (getsockname(sock.get(), &local_addr.sock, &addr_len) == -1) { PLOG(ERROR) << "Failed to getsockname()"; return nullptr; } - auto sock_wrapper = std::make_unique<RfcommListeningSocket>(); + auto sock_wrapper = std::make_unique<BluetoothListeningSocket>(); + sock_wrapper->sock_type = sock_type; sock_wrapper->controller = base::FileDescriptorWatcher::WatchReadable( sock.get(), - base::BindRepeating(&ArcBluetoothBridge::OnRfcommListeningSocketReady, + base::BindRepeating(&ArcBluetoothBridge::OnBluetoothListeningSocketReady, weak_factory_.GetWeakPtr(), sock_wrapper.get())); sock_wrapper->file = std::move(sock); - *channel = my_addr.rc_channel; + switch (sock_type) { + case mojom::BluetoothSocketType::TYPE_RFCOMM: + *port = local_addr.rfcomm.rc_channel; + break; + case mojom::BluetoothSocketType::TYPE_L2CAP_LE: + *port = btohs(local_addr.l2cap.l2_psm); + break; + default: + LOG(ERROR) << "Unknown socket type " << sock_type; + return nullptr; + } + return sock_wrapper; } -void ArcBluetoothBridge::OnRfcommListeningSocketReady( - ArcBluetoothBridge::RfcommListeningSocket* sock_wrapper) { - struct sockaddr_rc sa; +void ArcBluetoothBridge::OnBluetoothListeningSocketReady( + ArcBluetoothBridge::BluetoothListeningSocket* sock_wrapper) { + BluetoothSocketAddress sa; socklen_t addr_len = sizeof(sa); - base::ScopedFD accept_fd(accept(sock_wrapper->file.get(), - reinterpret_cast<struct sockaddr*>(&sa), - &addr_len)); + base::ScopedFD accept_fd( + accept(sock_wrapper->file.get(), &sa.sock, &addr_len)); if (!accept_fd.is_valid()) { PLOG(ERROR) << "Failed to accept()"; return; @@ -3084,38 +3260,91 @@ mojo::WrapPlatformHandle(mojo::PlatformHandle(std::move(accept_fd))); // Tells Android we successfully accept() a new connection. - auto connection = mojom::BluetoothRfcommConnection::New(); - connection->sock = std::move(handle); - connection->addr = mojom::BluetoothAddress::From<bdaddr_t>(sa.rc_bdaddr); - connection->channel = sa.rc_channel; - sock_wrapper->remote->OnAccepted(std::move(connection)); + if (sock_wrapper->created_by_deprecated_method) { + auto connection = mojom::BluetoothRfcommConnection::New(); + connection->sock = std::move(handle); + connection->addr = + mojom::BluetoothAddress::From<bdaddr_t>(sa.rfcomm.rc_bdaddr); + connection->channel = sa.rfcomm.rc_channel; + sock_wrapper->deprecated_remote->OnAccepted(std::move(connection)); + } else { + auto connection = mojom::BluetoothSocketConnection::New(); + connection->sock = std::move(handle); + switch (sock_wrapper->sock_type) { + case mojom::BluetoothSocketType::TYPE_RFCOMM: + connection->addr = + mojom::BluetoothAddress::From<bdaddr_t>(sa.rfcomm.rc_bdaddr); + connection->port = sa.rfcomm.rc_channel; + break; + case mojom::BluetoothSocketType::TYPE_L2CAP_LE: + connection->addr = + mojom::BluetoothAddress::From<bdaddr_t>(sa.l2cap.l2_bdaddr); + connection->port = btohs(sa.l2cap.l2_psm); + break; + default: + LOG(ERROR) << "Unknown socket type " << sock_wrapper->sock_type; + return; + } + sock_wrapper->remote->OnAccepted(std::move(connection)); + } } -std::unique_ptr<ArcBluetoothBridge::RfcommConnectingSocket> -ArcBluetoothBridge::RfcommCreateConnectSocket(mojom::BluetoothAddressPtr addr, - uint8_t channel, - int32_t optval) { - base::ScopedFD sock = OpenRfcommSocket(optval); +std::unique_ptr<ArcBluetoothBridge::BluetoothConnectingSocket> +ArcBluetoothBridge::CreateBluetoothConnectSocket( + mojom::BluetoothSocketType sock_type, + int32_t optval, + mojom::BluetoothAddressPtr addr, + uint16_t port) { + base::ScopedFD sock = + OpenBluetoothSocketImpl(sock_type, optval, kAutoSockPort); if (!sock.is_valid()) { LOG(ERROR) << "Failed to open connect socket."; return nullptr; } - struct sockaddr_rc sa = {}; - sa.rc_family = AF_BLUETOOTH; - sa.rc_bdaddr = addr->To<bdaddr_t>(); - sa.rc_channel = channel; + std::string addr_str = addr->To<std::string>(); + BluetoothDevice* device = bluetooth_adapter_->GetDevice(addr_str); + if (!device) + return nullptr; + + const auto addr_type = device->GetAddressType(); + if (addr_type == BluetoothDevice::ADDR_TYPE_UNKNOWN) { + LOG(ERROR) << "Unknown address type."; + return nullptr; + } + + BluetoothSocketAddress sa = {}; + switch (sock_type) { + case mojom::BluetoothSocketType::TYPE_RFCOMM: + sa.rfcomm.rc_family = AF_BLUETOOTH; + sa.rfcomm.rc_bdaddr = addr->To<bdaddr_t>(); + sa.rfcomm.rc_channel = static_cast<uint8_t>(port); + break; + case mojom::BluetoothSocketType::TYPE_L2CAP_LE: + sa.l2cap.l2_family = AF_BLUETOOTH; + sa.l2cap.l2_bdaddr = addr->To<bdaddr_t>(); + sa.l2cap.l2_psm = htobs(port); + sa.l2cap.l2_bdaddr_type = addr_type == BluetoothDevice::ADDR_TYPE_PUBLIC + ? BDADDR_LE_PUBLIC + : BDADDR_LE_RANDOM; + break; + default: + LOG(ERROR) << "Unknown socket type " << sock_type; + return nullptr; + } + int ret = HANDLE_EINTR(connect( sock.get(), reinterpret_cast<const struct sockaddr*>(&sa), sizeof(sa))); auto sock_wrapper = - std::make_unique<ArcBluetoothBridge::RfcommConnectingSocket>(); + std::make_unique<ArcBluetoothBridge::BluetoothConnectingSocket>(); + sock_wrapper->sock_type = sock_type; if (ret == 0) { // connect() returns success immediately. sock_wrapper->file = std::move(sock); base::ThreadPool::PostTask( FROM_HERE, - base::BindOnce(&ArcBluetoothBridge::OnRfcommConnectingSocketReady, + base::BindOnce(&ArcBluetoothBridge::OnBluetoothConnectingSocketReady, weak_factory_.GetWeakPtr(), sock_wrapper.get())); return sock_wrapper; } @@ -3126,14 +3355,14 @@ sock_wrapper->controller = base::FileDescriptorWatcher::WatchWritable( sock.get(), - base::BindRepeating(&ArcBluetoothBridge::OnRfcommConnectingSocketReady, + base::BindRepeating(&ArcBluetoothBridge::OnBluetoothConnectingSocketReady, weak_factory_.GetWeakPtr(), sock_wrapper.get())); sock_wrapper->file = std::move(sock); return sock_wrapper; } -void ArcBluetoothBridge::OnRfcommConnectingSocketReady( - ArcBluetoothBridge::RfcommConnectingSocket* sock_wrapper) { +void ArcBluetoothBridge::OnBluetoothConnectingSocketReady( + ArcBluetoothBridge::BluetoothConnectingSocket* sock_wrapper) { // When connect() is ready, we will transfer this fd to Android, and Android // is responsible for closing it. base::ScopedFD fd = std::move(sock_wrapper->file); @@ -3143,27 +3372,35 @@ socklen_t len = sizeof(err); int ret = getsockopt(fd.get(), SOL_SOCKET, SO_ERROR, &err, &len); if (ret != 0 || err != 0) { - PLOG(ERROR) << "Failed to connect. err=" << err; - sock_wrapper->remote->OnConnectFailed(); + LOG(ERROR) << "Failed to connect. err=" << err; + if (sock_wrapper->created_by_deprecated_method) + sock_wrapper->deprecated_remote->OnConnectFailed(); + else + sock_wrapper->remote->OnConnectFailed(); return; } // Gets peer address. - struct sockaddr_rc sa; - socklen_t sa_len = sizeof(sa); - if (getpeername(fd.get(), reinterpret_cast<sockaddr*>(&sa), &sa_len) == -1) { + BluetoothSocketAddress peer_sa; + socklen_t peer_sa_len = sizeof(peer_sa); + if (getpeername(fd.get(), &peer_sa.sock, &peer_sa_len) == -1) { PLOG(ERROR) << "Failed to getpeername()."; - sock_wrapper->remote->OnConnectFailed(); + if (sock_wrapper->created_by_deprecated_method) + sock_wrapper->deprecated_remote->OnConnectFailed(); + else + sock_wrapper->remote->OnConnectFailed(); return; } - // Gets our channel. - struct sockaddr_rc our_sa; - socklen_t our_sa_len = sizeof(sa); - if (getsockname(fd.get(), reinterpret_cast<sockaddr*>(&our_sa), - &our_sa_len) == -1) { + // Gets our port. + BluetoothSocketAddress local_sa; + socklen_t local_sa_len = sizeof(local_sa); + if (getsockname(fd.get(), &local_sa.sock, &local_sa_len) == -1) { PLOG(ERROR) << "Failed to getsockname()"; - sock_wrapper->remote->OnConnectFailed(); + if (sock_wrapper->created_by_deprecated_method) + sock_wrapper->deprecated_remote->OnConnectFailed(); + else + sock_wrapper->remote->OnConnectFailed(); return; } @@ -3171,17 +3408,43 @@ mojo::WrapPlatformHandle(mojo::PlatformHandle(std::move(fd))); // Notifies Android. - auto connection = mojom::BluetoothRfcommConnection::New(); - connection->sock = std::move(handle); - connection->addr = mojom::BluetoothAddress::From<bdaddr_t>(sa.rc_bdaddr); - connection->channel = our_sa.rc_channel; - sock_wrapper->remote->OnConnected(std::move(connection)); + if (sock_wrapper->created_by_deprecated_method) { + auto connection = mojom::BluetoothRfcommConnection::New(); + connection->sock = std::move(handle); + connection->addr = + mojom::BluetoothAddress::From<bdaddr_t>(peer_sa.rfcomm.rc_bdaddr); + connection->channel = local_sa.rfcomm.rc_channel; + sock_wrapper->deprecated_remote->OnConnected(std::move(connection)); + } else { + auto connection = mojom::BluetoothSocketConnection::New(); + connection->sock = std::move(handle); + switch (sock_wrapper->sock_type) { + case mojom::BluetoothSocketType::TYPE_RFCOMM: + connection->addr = + mojom::BluetoothAddress::From<bdaddr_t>(peer_sa.rfcomm.rc_bdaddr); + connection->port = local_sa.rfcomm.rc_channel; + break; + case mojom::BluetoothSocketType::TYPE_L2CAP_LE: + connection->addr = + mojom::BluetoothAddress::From<bdaddr_t>(peer_sa.l2cap.l2_bdaddr); + connection->port = btohs(local_sa.l2cap.l2_psm); + break; + default: + LOG(ERROR) << "Unknown socket type " << sock_wrapper->sock_type; + return; + } + sock_wrapper->remote->OnConnected(std::move(connection)); + } } -ArcBluetoothBridge::RfcommListeningSocket::RfcommListeningSocket() = default; -ArcBluetoothBridge::RfcommListeningSocket::~RfcommListeningSocket() = default; -ArcBluetoothBridge::RfcommConnectingSocket::RfcommConnectingSocket() = default; -ArcBluetoothBridge::RfcommConnectingSocket::~RfcommConnectingSocket() = default; +ArcBluetoothBridge::BluetoothListeningSocket::BluetoothListeningSocket() = + default; +ArcBluetoothBridge::BluetoothListeningSocket::~BluetoothListeningSocket() = + default; +ArcBluetoothBridge::BluetoothConnectingSocket::BluetoothConnectingSocket() = + default; +ArcBluetoothBridge::BluetoothConnectingSocket::~BluetoothConnectingSocket() = + default; ArcBluetoothBridge::BluetoothArcConnectionObserver:: BluetoothArcConnectionObserver(ArcBluetoothBridge* arc_bluetooth_bridge)
diff --git a/chrome/browser/chromeos/arc/bluetooth/arc_bluetooth_bridge.h b/chrome/browser/chromeos/arc/bluetooth/arc_bluetooth_bridge.h index f7cf99d7..ce80fec 100644 --- a/chrome/browser/chromeos/arc/bluetooth/arc_bluetooth_bridge.h +++ b/chrome/browser/chromeos/arc/bluetooth/arc_bluetooth_bridge.h
@@ -267,7 +267,8 @@ void ReadRemoteRssi(mojom::BluetoothAddressPtr remote_addr, ReadRemoteRssiCallback callback) override; - void OpenBluetoothSocket(OpenBluetoothSocketCallback callback) override; + void OpenBluetoothSocketDeprecated( + OpenBluetoothSocketDeprecatedCallback callback) override; // Bluetooth Mojo host interface - Bluetooth Gatt Server functions // Android counterpart link: @@ -316,13 +317,25 @@ RemoveSdpRecordCallback callback) override; // Bluetooth Mojo host interface - Bluetooth RFCOMM functions - void RfcommListen(int32_t channel, - int32_t optval, - RfcommListenCallback callback) override; - void RfcommConnect(mojom::BluetoothAddressPtr remote_addr, - int32_t channel, - int32_t optval, - RfcommConnectCallback callback) override; + void RfcommListenDeprecated(int32_t channel, + int32_t optval, + RfcommListenDeprecatedCallback callback) override; + void RfcommConnectDeprecated( + mojom::BluetoothAddressPtr remote_addr, + int32_t channel, + int32_t optval, + RfcommConnectDeprecatedCallback callback) override; + + // Bluetooth Mojo host interface - Bluetooth socket functions + void BluetoothSocketListen(mojom::BluetoothSocketType sock_type, + mojom::BluetoothSocketFlagsPtr sock_flags, + int32_t port, + BluetoothSocketListenCallback callback) override; + void BluetoothSocketConnect(mojom::BluetoothSocketType sock_type, + mojom::BluetoothSocketFlagsPtr sock_flags, + mojom::BluetoothAddressPtr remote_addr, + int32_t port, + BluetoothSocketConnectCallback callback) override; // Set up or disable multiple advertising. void ReserveAdvertisementHandle( @@ -548,48 +561,60 @@ // DevicePairedChange() but not in this function. void TrackPairingState(const device::BluetoothDevice* device); - // Data structures for RFCOMM listening/connecting sockets that live in + // Data structures for Bluetooth listening/connecting sockets that live in // Chrome. - struct RfcommListeningSocket { - mojo::Remote<mojom::RfcommListeningSocketClient> remote; + struct BluetoothListeningSocket { + mojom::BluetoothSocketType sock_type; + // TODO(b/163099156): Remove the following two fields when + // RfcommListenDeprecated()/RfcommConnectDeprecated() are removed. + bool created_by_deprecated_method = false; + mojo::Remote<mojom::RfcommListeningSocketClient> deprecated_remote; + mojo::Remote<mojom::BluetoothListenSocketClient> remote; base::ScopedFD file; std::unique_ptr<base::FileDescriptorWatcher::Controller> controller; - RfcommListeningSocket(); - ~RfcommListeningSocket(); + BluetoothListeningSocket(); + ~BluetoothListeningSocket(); }; - struct RfcommConnectingSocket { - mojo::Remote<mojom::RfcommConnectingSocketClient> remote; + struct BluetoothConnectingSocket { + mojom::BluetoothSocketType sock_type; + // TODO(b/163099156): Remove the following two fields when + // RfcommListenDeprecated()/RfcommConnectDeprecated() are removed. + bool created_by_deprecated_method = false; + mojo::Remote<mojom::RfcommConnectingSocketClient> deprecated_remote; + mojo::Remote<mojom::BluetoothConnectSocketClient> remote; base::ScopedFD file; std::unique_ptr<base::FileDescriptorWatcher::Controller> controller; - RfcommConnectingSocket(); - ~RfcommConnectingSocket(); + BluetoothConnectingSocket(); + ~BluetoothConnectingSocket(); }; - // Creates a bluetooth socket with socket option |optval|, and then bind() - // and listen() with requested RFCOMM |channel| number. The actual channel - // number will be filled in |channel| as the return value. Returns a - // RfcommListeningSocket that holds the socket. - std::unique_ptr<RfcommListeningSocket> RfcommCreateListenSocket( + // Creates a Bluetooth socket with socket option |optval|, and then bind() and + // listen() with requested |port| number. The actual port number will be + // filled in |port| as the return value. Returns a BluetoothListeningSocket + // that holds the socket. + std::unique_ptr<BluetoothListeningSocket> CreateBluetoothListenSocket( + mojom::BluetoothSocketType type, int32_t optval, - uint8_t* channel); - // Creates a bluetooth socket with socket option |optval|, and then calls - // connect() to (|addr|, |channel|). This connect() call is non-blocking. - // Returns a RfcommConnectingSocket that holds the socket. - std::unique_ptr<RfcommConnectingSocket> RfcommCreateConnectSocket( + uint16_t* port); + // Creates a Bluetooth socket with socket option |optval|, and then calls + // connect() to (|addr|, |port|). This connect() call is non-blocking. + // Returns a BluetoothConnectingSocket that holds the socket. + std::unique_ptr<BluetoothConnectingSocket> CreateBluetoothConnectSocket( + mojom::BluetoothSocketType type, + int32_t optval, mojom::BluetoothAddressPtr addr, - uint8_t channel, - int32_t optval); + uint16_t port); - // Closes RFCOMM sockets. Releases the corresponding resources. - void RfcommCloseListeningSocket(RfcommListeningSocket* socket); - void RfcommCloseConnectingSocket(RfcommConnectingSocket* socket); + // Closes Bluetooth sockets. Releases the corresponding resources. + void CloseBluetoothListeningSocket(BluetoothListeningSocket* socket); + void CloseBluetoothConnectingSocket(BluetoothConnectingSocket* socket); // Called when the listening socket is ready to accept(). - void OnRfcommListeningSocketReady( - ArcBluetoothBridge::RfcommListeningSocket* socket); + void OnBluetoothListeningSocketReady( + ArcBluetoothBridge::BluetoothListeningSocket* socket); // Called when the connecting socket is ready. - void OnRfcommConnectingSocketReady( - ArcBluetoothBridge::RfcommConnectingSocket* socket); + void OnBluetoothConnectingSocketReady( + ArcBluetoothBridge::BluetoothConnectingSocket* socket); ArcBridgeService* const arc_bridge_service_; // Owned by ArcServiceManager. @@ -699,10 +724,11 @@ // Start/StopLEScan(). ArcBluetoothTaskQueue discovery_queue_; - // Rfcomm sockets that live in Chrome. - std::set<std::unique_ptr<RfcommListeningSocket>, base::UniquePtrComparator> + // Bluetooth sockets that live in Chrome. + std::set<std::unique_ptr<BluetoothListeningSocket>, base::UniquePtrComparator> listening_sockets_; - std::set<std::unique_ptr<RfcommConnectingSocket>, base::UniquePtrComparator> + std::set<std::unique_ptr<BluetoothConnectingSocket>, + base::UniquePtrComparator> connecting_sockets_; // Observes the ARC connection to Bluetooth service in Android. We need to do
diff --git a/chrome/browser/chromeos/arc/tracing/arc_app_performance_tracing.cc b/chrome/browser/chromeos/arc/tracing/arc_app_performance_tracing.cc index 4a12721..d5f4a3d 100644 --- a/chrome/browser/chromeos/arc/tracing/arc_app_performance_tracing.cc +++ b/chrome/browser/chromeos/arc/tracing/arc_app_performance_tracing.cc
@@ -5,15 +5,19 @@ #include "chrome/browser/chromeos/arc/tracing/arc_app_performance_tracing.h" #include "base/memory/singleton.h" +#include "base/metrics/histogram_functions.h" #include "base/no_destructor.h" #include "base/strings/string_util.h" +#include "base/timer/timer.h" #include "chrome/browser/chromeos/arc/tracing/arc_app_performance_tracing_custom_session.h" #include "chrome/browser/chromeos/arc/tracing/arc_app_performance_tracing_session.h" #include "chrome/browser/chromeos/arc/tracing/arc_app_performance_tracing_uma_session.h" #include "chrome/browser/ui/app_list/arc/arc_app_list_prefs_factory.h" #include "components/arc/arc_browser_context_keyed_service_factory_base.h" #include "components/arc/arc_features.h" +#include "components/arc/arc_service_manager.h" #include "components/arc/arc_util.h" +#include "components/arc/session/arc_bridge_service.h" #include "components/exo/wm_helper.h" #include "ui/aura/window.h" @@ -21,6 +25,13 @@ namespace { +// Tracing delay for jankinees. +constexpr base::TimeDelta kJankinessTracingTime = + base::TimeDelta::FromMinutes(5); + +// Minimum number of frames for a jankiness tracing result to be valid. +constexpr int kMinTotalFramesJankiness = 1000; + // Singleton factory for ArcAppPerformanceTracing. class ArcAppPerformanceTracingFactory : public internal::ArcBrowserContextKeyedServiceFactoryBase< @@ -124,6 +135,8 @@ } void ArcAppPerformanceTracing::Shutdown() { + CancelJankinessTracing(); + MaybeStopTracing(); // |session_|. Make sure that |arc_active_window_| is detached. @@ -160,6 +173,9 @@ // Discard any active tracing if any. MaybeStopTracing(); + // Stop and report previous active window's jankiness tracing so far. + FinalizeJankinessTracing(true /* stopped_early */); + // Detach previous active window if it is set. DetachActiveWindow(); @@ -170,6 +186,8 @@ // Observe active ARC++ window. AttachActiveWindow(gained_active); + StartJankinessTracing(); + MaybeStartTracing(); } @@ -177,6 +195,8 @@ // ARC++ window will be destroyed. DCHECK_EQ(arc_active_window_, window); + CancelJankinessTracing(); + MaybeStopTracing(); DetachActiveWindow(); @@ -187,7 +207,7 @@ const std::string& activity, const std::string& intent) { const std::string app_id = ArcAppListPrefs::GetAppId(package_name, activity); - task_id_to_app_id_[task_id] = app_id; + task_id_to_app_id_[task_id] = std::make_pair(app_id, package_name); MaybeStartTracing(); } @@ -195,6 +215,109 @@ task_id_to_app_id_.erase(task_id); } +void ArcAppPerformanceTracing::StartJankinessTracing() { + DCHECK(!jankiness_timer_.IsRunning()); + jankiness_timer_.Start( + FROM_HERE, kJankinessTracingTime, + base::BindOnce(&ArcAppPerformanceTracing::FinalizeJankinessTracing, + base::Unretained(this), false /* stopped_early */)); +} + +void ArcAppPerformanceTracing::CancelJankinessTracing() { + jankiness_timer_.Stop(); +} + +void ArcAppPerformanceTracing::FinalizeJankinessTracing(bool stopped_early) { + // Never started. Nothing to do. + if (!jankiness_timer_.IsRunning() && stopped_early) + return; + + jankiness_timer_.Stop(); + + // Check if we have all conditions met, ARC++ window is active and information + // is available for associated task. + if (!arc_active_window_) + return; + + const int32_t task_id = arc::GetWindowTaskId(arc_active_window_); + DCHECK_GT(task_id, 0); + + const auto it = task_id_to_app_id_.find(task_id); + if (it == task_id_to_app_id_.end()) + // It is normal that information might not be available at this time. + return; + + // Test instances might not have Service Manager running. + auto* arc_service_manager = ArcServiceManager::Get(); + if (!arc_service_manager) + return; + + auto* instance = ARC_GET_INSTANCE_FOR_METHOD( + arc_service_manager->arc_bridge_service()->metrics(), GetGfxMetrics); + if (!instance) + return; + + const std::string package_name = it->second.second; + auto callback = base::BindOnce(&ArcAppPerformanceTracing::OnGfxMetrics, + base::Unretained(this), package_name); + instance->GetGfxMetrics(package_name, std::move(callback)); + + // Finalized normally, safe to restart. + if (!stopped_early) + StartJankinessTracing(); +} + +void ArcAppPerformanceTracing::OnGfxMetrics(const std::string& package_name, + mojom::GfxMetricsPtr metrics) { + if (!metrics) { + LOG(ERROR) << "Failed to resolve GFX metrics"; + return; + } + + uint64_t framesTotal = metrics->framesTotal; + uint64_t framesJanky = metrics->framesJanky; + const uint32_t frameTime95 = metrics->frameTimePercentile95; // in ms. + + const auto it = package_name_to_gfx_metrics_.find(package_name); + const bool first_time = it == package_name_to_gfx_metrics_.end(); + + // Cached data exists and not outdated. Calculate delta. + if (!first_time && it->second.framesTotal <= framesTotal) { + framesTotal -= it->second.framesTotal; + framesJanky -= it->second.framesJanky; + } + + // Update cache. + package_name_to_gfx_metrics_[package_name] = *metrics; + + // Not enough data. + if (framesTotal < kMinTotalFramesJankiness) { + VLOG(1) << "Not enough GFX metrics data collected to report."; + return; + } + + // We can only calculate real numbers for initial data. Only report if first + // time. + if (first_time) { + const base::TimeDelta frameTime = + base::TimeDelta::FromMilliseconds(frameTime95); + base::UmaHistogramTimes("Arc.Runtime.Performance.Generic.FrameTime", + frameTime); + VLOG(1) << "Total Frames: " << framesTotal << " | " + << "Janky Frames: " << framesJanky << " | " + << "95 Percentile Frame Time: " << frameTime.InMilliseconds() + << "ms"; + } else { + VLOG(1) << "Total Frames: " << framesTotal << " | " + << "Janky Frames: " << framesJanky; + } + + const int jankiness = (framesJanky * 100) / framesTotal; + + base::UmaHistogramPercentage("Arc.Runtime.Performance.Generic.Jankiness", + jankiness); +} + bool ArcAppPerformanceTracing::WasReported(const std::string& category) const { DCHECK(!category.empty()); return reported_categories_.count(category); @@ -226,8 +349,8 @@ return; } - const std::string& category = - AppToCategoryMapper::GetInstance().GetCategory(it->second /* app_id */); + const std::string& category = AppToCategoryMapper::GetInstance().GetCategory( + it->second.first /* app_id */); if (category.empty()) { // App is not recognized as app for tracing, ignore it.
diff --git a/chrome/browser/chromeos/arc/tracing/arc_app_performance_tracing.h b/chrome/browser/chromeos/arc/tracing/arc_app_performance_tracing.h index 687768ea..ec6706c 100644 --- a/chrome/browser/chromeos/arc/tracing/arc_app_performance_tracing.h +++ b/chrome/browser/chromeos/arc/tracing/arc_app_performance_tracing.h
@@ -15,6 +15,7 @@ #include "base/callback.h" #include "base/macros.h" #include "chrome/browser/ui/app_list/arc/arc_app_list_prefs.h" +#include "components/arc/mojom/metrics.mojom.h" #include "components/keyed_service/core/keyed_service.h" #include "ui/aura/window_observer.h" #include "ui/wm/public/activation_change_observer.h" @@ -33,7 +34,7 @@ class ArcBridgeService; // Service that monitors ARC++ apps, measures and reports performance metrics -// for the set of predefined apps. +// for the set of predefined apps. Also report GFX metrics jankiness results. class ArcAppPerformanceTracing : public KeyedService, public wm::ActivationChangeObserver, public aura::WindowObserver, @@ -121,13 +122,35 @@ // |arc_active_window_|. void DetachActiveWindow(); + // Starts timer for jankiness tracing. Called by OnWindowActivation() and + // FinalizeJankinessTracing(). + void StartJankinessTracing(); + + // Cancels jankiness tracing without reporting partial results. + void CancelJankinessTracing(); + + // Retrieves and reports jankiness metrics and restarts timer. May be called + // early by OnWindowActivation() and OnWindowDestroying(). + // In this case, |stopped_early| is set to true. + void FinalizeJankinessTracing(bool stopped_early); + + // Callback for jankiness results. Reports results to UMA. + // Note: Results are cumulative. Uses task_id_to_gfx_metrics_ + // values for delta calculations. + void OnGfxMetrics(const std::string& package_name, + mojom::GfxMetricsPtr metrics_ptr); + // Unowned pointers. content::BrowserContext* const context_; // Currently active ARC++ app window. aura::Window* arc_active_window_ = nullptr; - // Maps active tasks to app id. - std::map<int, std::string> task_id_to_app_id_; + // Maps active tasks to app id and package name. + std::map<int, std::pair<std::string, std::string>> task_id_to_app_id_; + + // Maps tasks to most recent GFX jankiness results. Used for delta + // calculation. + std::map<std::string, mojom::GfxMetrics> package_name_to_gfx_metrics_; // Set of already reported ARC++ apps for the current session. Used to prevent // capturing too frequently. @@ -139,6 +162,9 @@ // Callback to call when custom session is ready for testing. CustomSessionReadyCallback custom_session_ready_callback_; + // Timer for jankiness tracing. + base::OneShotTimer jankiness_timer_; + DISALLOW_COPY_AND_ASSIGN(ArcAppPerformanceTracing); };
diff --git a/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc b/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc index d8fb996..869e69b4 100644 --- a/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc +++ b/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
@@ -103,6 +103,11 @@ return *this; } + TestCase& EnableSinglePartitionFormat() { + options.single_partition_format = true; + return *this; + } + // Show the startup browser. Some tests invoke the file picker dialog during // the test. Requesting a file picker from a background page is forbidden by // the apps platform, and it's a bug that these tests do so. @@ -149,6 +154,9 @@ if (options.zip_no_nacl) full_name += "_ZipNoNaCl"; + if (options.single_partition_format) + full_name += "_SinglePartitionFormat"; + return full_name; } @@ -272,6 +280,7 @@ TestCase("fileDisplayMtp"), TestCase("fileDisplayUsb"), TestCase("fileDisplayUsbPartition"), + TestCase("fileDisplayUsbPartition").EnableSinglePartitionFormat(), TestCase("fileDisplayUsbPartitionSort"), TestCase("fileDisplayPartitionFileTable"), TestCase("fileSearch"), @@ -613,9 +622,11 @@ TestCase("dirContextMenuCrostini"), TestCase("dirContextMenuPlayFiles"), TestCase("dirContextMenuUsbs"), + TestCase("dirContextMenuUsbs").EnableSinglePartitionFormat(), TestCase("dirContextMenuFsp"), TestCase("dirContextMenuDocumentsProvider").EnableDocumentsProvider(), TestCase("dirContextMenuUsbDcim"), + TestCase("dirContextMenuUsbDcim").EnableSinglePartitionFormat(), TestCase("dirContextMenuMtp"), TestCase("dirContextMenuMediaView").EnableArc(), TestCase("dirContextMenuMyDrive"), @@ -629,24 +640,26 @@ WRAPPED_INSTANTIATE_TEST_SUITE_P( DriveSpecific, /* drive_specific.js */ FilesAppBrowserTest, - ::testing::Values(TestCase("driveOpenSidebarOffline"), - TestCase("driveOpenSidebarSharedWithMe"), - TestCase("driveAutoCompleteQuery"), - TestCase("drivePinMultiple"), - TestCase("drivePinHosted"), - TestCase("drivePinFileMobileNetwork"), - TestCase("driveClickFirstSearchResult"), - TestCase("drivePressEnterToSearch"), - TestCase("drivePressClearSearch"), - TestCase("drivePressCtrlAFromSearch"), - TestCase("driveBackupPhotos"), - TestCase("driveAvailableOfflineGearMenu"), - TestCase("driveAvailableOfflineDirectoryGearMenu"), - TestCase("driveAvailableOfflineActionBar"), - TestCase("driveLinkToDirectory"), - TestCase("driveLinkOpenFileThroughLinkedDirectory"), - TestCase("driveLinkOpenFileThroughTransitiveLink"), - TestCase("driveWelcomeBanner"))); + ::testing::Values( + TestCase("driveOpenSidebarOffline"), + TestCase("driveOpenSidebarSharedWithMe"), + TestCase("driveAutoCompleteQuery"), + TestCase("drivePinMultiple"), + TestCase("drivePinHosted"), + TestCase("drivePinFileMobileNetwork"), + TestCase("driveClickFirstSearchResult"), + TestCase("drivePressEnterToSearch"), + TestCase("drivePressClearSearch"), + TestCase("drivePressCtrlAFromSearch"), + TestCase("driveBackupPhotos"), + TestCase("driveBackupPhotos").EnableSinglePartitionFormat(), + TestCase("driveAvailableOfflineGearMenu"), + TestCase("driveAvailableOfflineDirectoryGearMenu"), + TestCase("driveAvailableOfflineActionBar"), + TestCase("driveLinkToDirectory"), + TestCase("driveLinkOpenFileThroughLinkedDirectory"), + TestCase("driveLinkOpenFileThroughTransitiveLink"), + TestCase("driveWelcomeBanner"))); WRAPPED_INSTANTIATE_TEST_SUITE_P( Transfer, /* transfer.js */ @@ -674,6 +687,8 @@ TestCase("transferDragDropTreeItemDenies").FilesNg(), TestCase("transferDragAndHoverTreeItemEntryList"), TestCase("transferDragAndHoverTreeItemFakeEntry"), + TestCase("transferDragAndHoverTreeItemFakeEntry") + .EnableSinglePartitionFormat(), TestCase("transferDragFileListItemSelects"), TestCase("transferDragAndDrop"), TestCase("transferDragAndHover"), @@ -1031,11 +1046,18 @@ WRAPPED_INSTANTIATE_TEST_SUITE_P( FormatDialog, /* format_dialog.js */ FilesAppBrowserTest, - ::testing::Values(TestCase("formatDialog"), - TestCase("formatDialogEmpty"), - TestCase("formatDialogCancel"), - TestCase("formatDialogNameLength"), - TestCase("formatDialogNameInvalid"), - TestCase("formatDialogGearMenu"))); + ::testing::Values( + TestCase("formatDialog"), + TestCase("formatDialogEmpty"), + TestCase("formatDialogCancel"), + TestCase("formatDialogNameLength"), + TestCase("formatDialogNameInvalid"), + TestCase("formatDialogGearMenu"), + TestCase("formatDialog").EnableSinglePartitionFormat(), + TestCase("formatDialogEmpty").EnableSinglePartitionFormat(), + TestCase("formatDialogCancel").EnableSinglePartitionFormat(), + TestCase("formatDialogNameLength").EnableSinglePartitionFormat(), + TestCase("formatDialogNameInvalid").EnableSinglePartitionFormat(), + TestCase("formatDialogGearMenu").EnableSinglePartitionFormat())); } // namespace file_manager
diff --git a/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.cc b/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.cc index afe1918..b67977c2 100644 --- a/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.cc +++ b/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.cc
@@ -718,6 +718,7 @@ PRINT_IF_NOT_DEFAULT(tablet_mode) PRINT_IF_NOT_DEFAULT(zip) PRINT_IF_NOT_DEFAULT(zip_no_nacl) + PRINT_IF_NOT_DEFAULT(single_partition_format) #undef PRINT_IF_NOT_DEFAULT @@ -1583,6 +1584,10 @@ enabled_features.push_back(features::kSharesheet); } + if (options.single_partition_format) { + enabled_features.push_back(chromeos::features::kFilesSinglePartitionFormat); + } + // This is destroyed in |TearDown()|. We cannot initialize this in the // constructor due to this feature values' above dependence on virtual // method calls, but by convention subclasses of this fixture may initialize
diff --git a/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.h b/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.h index 63ed339..1e09001b 100644 --- a/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.h +++ b/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.h
@@ -92,6 +92,9 @@ // Whether test should enable sharesheet. bool enable_sharesheet = false; + + // Whether test needs the single partition format feature. + bool single_partition_format = false; }; protected:
diff --git a/chrome/browser/chromeos/release_notes/release_notes_storage.cc b/chrome/browser/chromeos/release_notes/release_notes_storage.cc index 13853f1..3f480295 100644 --- a/chrome/browser/chromeos/release_notes/release_notes_storage.cc +++ b/chrome/browser/chromeos/release_notes/release_notes_storage.cc
@@ -111,4 +111,9 @@ times_left_to_show - 1); } +void ReleaseNotesStorage::StopShowingSuggestionChip() { + profile_->GetPrefs()->SetInteger( + prefs::kReleaseNotesSuggestionChipTimesLeftToShow, 0); +} + } // namespace chromeos
diff --git a/chrome/browser/chromeos/release_notes/release_notes_storage.h b/chrome/browser/chromeos/release_notes/release_notes_storage.h index e40e09c..7f70d0d4 100644 --- a/chrome/browser/chromeos/release_notes/release_notes_storage.h +++ b/chrome/browser/chromeos/release_notes/release_notes_storage.h
@@ -35,6 +35,9 @@ // Decreases the amount of times left to show the suggestion chip. void DecreaseTimesLeftToShowSuggestionChip(); + // Sets the number of times left to show the suggestion chip to 0. + void StopShowingSuggestionChip(); + private: Profile* const profile_;
diff --git a/chrome/browser/chromeos/scanning/scanning_type_converters.cc b/chrome/browser/chromeos/scanning/scanning_type_converters.cc index 3cea91c..dd1f382f 100644 --- a/chrome/browser/chromeos/scanning/scanning_type_converters.cc +++ b/chrome/browser/chromeos/scanning/scanning_type_converters.cc
@@ -44,11 +44,13 @@ return mojo_ipc::SourceType::kAdfSimplex; case lorgnette::SOURCE_ADF_DUPLEX: return mojo_ipc::SourceType::kAdfDuplex; + case lorgnette::SOURCE_DEFAULT: + return mojo_ipc::SourceType::kDefault; case lorgnette::SOURCE_UNSPECIFIED: case lorgnette::SourceType_INT_MIN_SENTINEL_DO_NOT_USE_: case lorgnette::SourceType_INT_MAX_SENTINEL_DO_NOT_USE_: NOTREACHED(); - return mojo_ipc::SourceType::kFlatbed; + return mojo_ipc::SourceType::kUnknown; } } };
diff --git a/chrome/browser/chromeos/scanning/scanning_type_converters_unittest.cc b/chrome/browser/chromeos/scanning/scanning_type_converters_unittest.cc index e6f7e34..d9803b7 100644 --- a/chrome/browser/chromeos/scanning/scanning_type_converters_unittest.cc +++ b/chrome/browser/chromeos/scanning/scanning_type_converters_unittest.cc
@@ -100,6 +100,9 @@ mojo_ipc::SourceType::kAdfSimplex, mojo_ipc::ColorMode::kGrayscale}, ScanningTypeConvertersTestParams{ lorgnette::SOURCE_ADF_DUPLEX, lorgnette::MODE_COLOR, - mojo_ipc::SourceType::kAdfDuplex, mojo_ipc::ColorMode::kColor})); + mojo_ipc::SourceType::kAdfDuplex, mojo_ipc::ColorMode::kColor}, + ScanningTypeConvertersTestParams{ + lorgnette::SOURCE_DEFAULT, lorgnette::MODE_COLOR, + mojo_ipc::SourceType::kDefault, mojo_ipc::ColorMode::kColor})); } // namespace chromeos
diff --git a/chrome/browser/chromeos/web_applications/help_app_integration_browsertest.cc b/chrome/browser/chromeos/web_applications/help_app_integration_browsertest.cc index 8b64461..f2ad7db 100644 --- a/chrome/browser/chromeos/web_applications/help_app_integration_browsertest.cc +++ b/chrome/browser/chromeos/web_applications/help_app_integration_browsertest.cc
@@ -47,6 +47,7 @@ HelpAppIntegrationTest() { scoped_feature_list_.InitWithFeatures( {chromeos::features::kHelpAppReleaseNotes, + chromeos::features::kHelpAppSearchServiceIntegration, chromeos::features::kReleaseNotesNotificationAllChannels}, {}); } @@ -354,6 +355,45 @@ EXPECT_EQ(expected_url, GetActiveWebContents()->GetVisibleURL()); } +// Test that the Help App delegate uses the local search service methods. +IN_PROC_BROWSER_TEST_P(HelpAppIntegrationTest, + HelpAppV2UsesLocalSearchServiceMethods) { + WaitForTestSystemAppInstall(); + content::WebContents* web_contents = LaunchApp(web_app::SystemAppType::HELP); + + // Script that adds a data item to the search index, then tries to find that + // data item. + constexpr char kScript[] = R"( + (async () => { + const delegate = document.querySelector('showoff-app').getDelegate(); + + await delegate.addOrUpdateSearchIndex([{ + id: 'test-id', + title: 'foobar', + body: 'foo bar baz', + mainCategoryName: 'Help', + locale: 'en-US', + }]); + + // Polling is required as addOrUpdateSearchIndex resolves before the + // search index is actually updated. + setInterval(async () => { + // Note that the LSS will fuzzy match into foobar. + const {results} = await delegate.findInSearchIndex('foober'); + if (results && results.length === 1) { + window.domAutomationController.send(results[0].id); + } + }, 10); + })(); + )"; + std::string result; + // Use ExecuteScript instead of EvalJsInAppFrame because the script needs to + // run in the same world as the page's code. + EXPECT_TRUE(content::ExecuteScriptAndExtractString( + SandboxedWebUiAppTestBase::GetAppFrame(web_contents), kScript, &result)); + EXPECT_EQ(result, "test-id"); +} + // Test that the Help App opens when Gesture help requested. IN_PROC_BROWSER_TEST_P(HelpAppAllProfilesIntegrationTest, HelpAppOpenGestures) { WaitForTestSystemAppInstall();
diff --git a/chrome/browser/component_updater/floc_component_installer.cc b/chrome/browser/component_updater/floc_component_installer.cc index f5e06a2d..4f2d35d 100644 --- a/chrome/browser/component_updater/floc_component_installer.cc +++ b/chrome/browser/component_updater/floc_component_installer.cc
@@ -59,15 +59,12 @@ std::unique_ptr<base::DictionaryValue> manifest) { DCHECK(!install_dir.empty()); - // TODO(yaoxia): Pass along the |version| to each service. At the end of each - // floc computation cycle, it should verify the two versions match. This is - // not needed currently as the floc_sorting_lsh_clusters_service is not set up - // yet. floc_blocklist_service_->OnBlocklistFileReady( - install_dir.Append(federated_learning::kBlocklistFileName)); + install_dir.Append(federated_learning::kBlocklistFileName), version); floc_sorting_lsh_clusters_service_->OnSortingLshClustersFileReady( - install_dir.Append(federated_learning::kSortingLshClustersFileName)); + install_dir.Append(federated_learning::kSortingLshClustersFileName), + version); } // Called during startup and installation before ComponentReady().
diff --git a/chrome/browser/component_updater/floc_component_installer_unittest.cc b/chrome/browser/component_updater/floc_component_installer_unittest.cc index 4e91736..124077f 100644 --- a/chrome/browser/component_updater/floc_component_installer_unittest.cc +++ b/chrome/browser/component_updater/floc_component_installer_unittest.cc
@@ -49,14 +49,18 @@ ~MockFlocBlocklistService() override = default; - void OnBlocklistFileReady(const base::FilePath& file_path) override { + void OnBlocklistFileReady(const base::FilePath& file_path, + const base::Version& version) override { file_paths_.push_back(file_path); + versions_.push_back(version); } const std::vector<base::FilePath>& file_paths() const { return file_paths_; } + const std::vector<base::Version>& versions() const { return versions_; } private: std::vector<base::FilePath> file_paths_; + std::vector<base::Version> versions_; }; // This class monitors the OnSortingLshClustersFileReady method calls. @@ -72,14 +76,18 @@ ~MockFlocSortingLshClustersService() override = default; - void OnSortingLshClustersFileReady(const base::FilePath& file_path) override { + void OnSortingLshClustersFileReady(const base::FilePath& file_path, + const base::Version& version) override { file_paths_.push_back(file_path); + versions_.push_back(version); } const std::vector<base::FilePath>& file_paths() const { return file_paths_; } + const std::vector<base::Version>& versions() const { return versions_; } private: std::vector<base::FilePath> file_paths_; + std::vector<base::Version> versions_; }; } // namespace @@ -203,10 +211,12 @@ std::string contents = "abcd"; ASSERT_NO_FATAL_FAILURE(CreateTestFlocComponentFiles(contents, contents)); ASSERT_NO_FATAL_FAILURE(LoadFlocComponent( - "1.0.0", federated_learning::kCurrentFlocComponentFormatVersion)); + "1.0.1", federated_learning::kCurrentFlocComponentFormatVersion)); ASSERT_EQ(blocklist_service()->file_paths().size(), 1u); + ASSERT_EQ(blocklist_service()->versions().size(), 1u); ASSERT_EQ(sorting_lsh_clusters_service()->file_paths().size(), 1u); + ASSERT_EQ(sorting_lsh_clusters_service()->versions().size(), 1u); // Assert that the file path is the concatenation of |component_install_dir_| // and the corresponding file name, which implies that the |version| argument @@ -222,6 +232,9 @@ .Append(federated_learning::kSortingLshClustersFileName) .AsUTF8Unsafe()); + EXPECT_EQ(blocklist_service()->versions()[0].GetString(), "1.0.1"); + EXPECT_EQ(sorting_lsh_clusters_service()->versions()[0].GetString(), "1.0.1"); + std::string actual_contents; ASSERT_TRUE(base::ReadFileToString(blocklist_service()->file_paths()[0], &actual_contents));
diff --git a/chrome/browser/extensions/BUILD.gn b/chrome/browser/extensions/BUILD.gn index d02c734..18315b38 100644 --- a/chrome/browser/extensions/BUILD.gn +++ b/chrome/browser/extensions/BUILD.gn
@@ -176,6 +176,8 @@ "api/identity/gaia_web_auth_flow.h", "api/identity/identity_api.cc", "api/identity/identity_api.h", + "api/identity/identity_clear_all_cached_auth_tokens_function.cc", + "api/identity/identity_clear_all_cached_auth_tokens_function.h", "api/identity/identity_constants.cc", "api/identity/identity_constants.h", "api/identity/identity_get_accounts_function.cc",
diff --git a/chrome/browser/extensions/api/declarative_net_request/declarative_net_request_browsertest.cc b/chrome/browser/extensions/api/declarative_net_request/declarative_net_request_browsertest.cc index daf006fc..a11c0768 100644 --- a/chrome/browser/extensions/api/declarative_net_request/declarative_net_request_browsertest.cc +++ b/chrome/browser/extensions/api/declarative_net_request/declarative_net_request_browsertest.cc
@@ -450,14 +450,15 @@ void SetActionsAsBadgeText(const ExtensionId& extension_id, bool pref) { const char* pref_string = pref ? "true" : "false"; - static constexpr char kSetActionCountAsBadgeTextScript[] = R"( - chrome.declarativeNetRequest.setActionCountAsBadgeText(%s); + static constexpr char kSetExtensionActionOptionsScript[] = R"( + chrome.declarativeNetRequest.setExtensionActionOptions( + {displayActionCountAsBadgeText: %s}); window.domAutomationController.send("done"); )"; ExecuteScriptInBackgroundPage( extension_id, - base::StringPrintf(kSetActionCountAsBadgeTextScript, pref_string)); + base::StringPrintf(kSetExtensionActionOptionsScript, pref_string)); } // Navigates frame with name |frame_name| to |url|. @@ -3091,8 +3092,9 @@ EXPECT_FALSE(action->HasDNRActionCount(first_tab_id)); } -// Test that enabling the setActionCountAsBadgeText preference will update -// all browsers sharing the same browser context. +// Test that enabling the "displayActionCountAsBadgeText" preference using +// setExtensionActionOptions will update all browsers sharing the same browser +// context. IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest, ActionCountPreferenceMultipleWindows) { // Load the extension with a background script so scripts can be run from its
diff --git a/chrome/browser/extensions/api/extension_action/extension_action_api.cc b/chrome/browser/extensions/api/extension_action/extension_action_api.cc index b55acbd..ac64e70 100644 --- a/chrome/browser/extensions/api/extension_action/extension_action_api.cc +++ b/chrome/browser/extensions/api/extension_action/extension_action_api.cc
@@ -513,16 +513,16 @@ ExtensionFunction::ResponseAction ExtensionActionGetBadgeTextFunction::RunExtensionAction() { - // Return a placeholder value if the extension has called - // setActionCountAsBadgeText(true) and the badge count shown for this tab is - // the number of actions matched. + // Return a placeholder value if the extension has enabled using + // declarativeNetRequest action count as badge text and the badge count shown + // for this tab is the number of actions matched. std::string badge_text = extension_action_->UseDNRActionCountAsBadgeText(tab_id_) ? declarative_net_request::kActionCountPlaceholderBadgeText : extension_action_->GetExplicitlySetBadgeText(tab_id_); // TODO(crbug.com/990224): Document this behavior once - // chrome.declarativeNetRequest.setActionCountAsBadgeText is promoted to beta + // chrome.declarativeNetRequest.setExtensionActionOptions is promoted to beta // from trunk. return RespondNow( OneArgument(std::make_unique<base::Value>(std::move(badge_text))));
diff --git a/chrome/browser/extensions/api/identity/identity_api.h b/chrome/browser/extensions/api/identity/identity_api.h index 1b9db68..02b1162 100644 --- a/chrome/browser/extensions/api/identity/identity_api.h +++ b/chrome/browser/extensions/api/identity/identity_api.h
@@ -18,6 +18,7 @@ #include "build/build_config.h" #include "build/buildflag.h" #include "chrome/browser/extensions/api/identity/gaia_web_auth_flow.h" +#include "chrome/browser/extensions/api/identity/identity_clear_all_cached_auth_tokens_function.h" #include "chrome/browser/extensions/api/identity/identity_get_accounts_function.h" #include "chrome/browser/extensions/api/identity/identity_get_auth_token_function.h" #include "chrome/browser/extensions/api/identity/identity_get_profile_user_info_function.h"
diff --git a/chrome/browser/extensions/api/identity/identity_apitest.cc b/chrome/browser/extensions/api/identity/identity_apitest.cc index 35829f5..d3ec2b0 100644 --- a/chrome/browser/extensions/api/identity/identity_apitest.cc +++ b/chrome/browser/extensions/api/identity/identity_apitest.cc
@@ -11,9 +11,11 @@ #include "base/bind.h" #include "base/command_line.h" #include "base/memory/ptr_util.h" +#include "base/memory/scoped_refptr.h" #include "base/run_loop.h" #include "base/strings/string_util.h" #include "base/strings/stringprintf.h" +#include "base/test/bind_test_util.h" #include "base/test/metrics/histogram_tester.h" #include "base/test/scoped_feature_list.h" #include "base/values.h" @@ -60,12 +62,14 @@ #include "components/signin/public/identity_manager/identity_test_utils.h" #include "content/public/browser/notification_service.h" #include "content/public/browser/notification_source.h" +#include "content/public/browser/storage_partition.h" #include "content/public/test/browser_test.h" #include "content/public/test/test_utils.h" #include "extensions/browser/api_test_utils.h" #include "extensions/common/extension_builder.h" #include "extensions/common/extension_features.h" #include "google_apis/gaia/oauth2_mint_token_flow.h" +#include "net/cookies/cookie_util.h" #include "net/test/embedded_test_server/embedded_test_server.h" #include "services/network/public/cpp/shared_url_loader_factory.h" #include "services/network/test/test_url_loader_factory.h" @@ -3583,6 +3587,111 @@ url); } +class ClearAllCachedAuthTokensFunctionTest : public AsyncExtensionBrowserTest { + public: + void SetUpOnMainThread() override { + AsyncExtensionBrowserTest::SetUpOnMainThread(); + base::FilePath manifest_path = + test_data_dir_.AppendASCII("platform_apps/oauth2"); + extension_ = LoadExtension(manifest_path); + } + + const Extension* extension() { return extension_; } + + bool RunClearAllCachedAuthTokensFunction() { + auto function = + base::MakeRefCounted<IdentityClearAllCachedAuthTokensFunction>(); + function->set_extension(extension_); + return utils::RunFunction(function.get(), "[]", browser(), + api_test_utils::NONE); + } + + IdentityAPI* id_api() { + return IdentityAPI::GetFactoryInstance()->Get(browser()->profile()); + } + + private: + base::test::ScopedFeatureList feature_list_; + const Extension* extension_ = nullptr; +}; + +IN_PROC_BROWSER_TEST_F(ClearAllCachedAuthTokensFunctionTest, + EraseCachedGaiaId) { + id_api()->SetGaiaIdForExtension(extension()->id(), "test_gaia"); + EXPECT_EQ("test_gaia", id_api()->GetGaiaIdForExtension(extension()->id())); + ASSERT_TRUE(RunClearAllCachedAuthTokensFunction()); + EXPECT_FALSE(id_api()->GetGaiaIdForExtension(extension()->id()).has_value()); +} + +IN_PROC_BROWSER_TEST_F(ClearAllCachedAuthTokensFunctionTest, + EraseCachedTokens) { + ExtensionTokenKey token_key(extension()->id(), CoreAccountInfo(), {"foo"}); + id_api()->token_cache()->SetToken( + token_key, + IdentityTokenCacheValue::CreateToken("access_token", {"foo"}, + base::TimeDelta::FromSeconds(3600))); + EXPECT_NE(IdentityTokenCacheValue::CACHE_STATUS_NOTFOUND, + id_api()->token_cache()->GetToken(token_key).status()); + ASSERT_TRUE(RunClearAllCachedAuthTokensFunction()); + EXPECT_EQ(IdentityTokenCacheValue::CACHE_STATUS_NOTFOUND, + id_api()->token_cache()->GetToken(token_key).status()); +} + +class ClearAllCachedAuthTokensFunctionTestWithPartitionParam + : public ClearAllCachedAuthTokensFunctionTest, + public testing::WithParamInterface<WebAuthFlow::Partition> { + public: + network::mojom::CookieManager* GetCookieManager() { + Profile* profile = browser()->profile(); + return content::BrowserContext::GetStoragePartition( + profile, + WebAuthFlow::GetWebViewPartitionConfig(GetParam(), profile)) + ->GetCookieManagerForBrowserProcess(); + } + + // Returns the list of cookies in the cookie manager. + net::CookieList GetCookies() { + net::CookieList result; + base::RunLoop get_all_cookies_loop; + GetCookieManager()->GetAllCookies(base::BindLambdaForTesting( + [&get_all_cookies_loop, &result](const net::CookieList& cookie_list) { + result = cookie_list; + get_all_cookies_loop.Quit(); + })); + get_all_cookies_loop.Run(); + return result; + } +}; + +IN_PROC_BROWSER_TEST_P(ClearAllCachedAuthTokensFunctionTestWithPartitionParam, + CleanWebAuthFlowCookies) { + net::CanonicalCookie test_cookie( + "test_name", "test_value", "test.com", "/", base::Time(), base::Time(), + base::Time(), true, false, net::CookieSameSite::NO_RESTRICTION, + net::COOKIE_PRIORITY_DEFAULT); + base::RunLoop set_cookie_loop; + GetCookieManager()->SetCanonicalCookie( + test_cookie, + net::cookie_util::SimulatedCookieSource(test_cookie, url::kHttpsScheme), + net::CookieOptions(), + net::cookie_util::AdaptCookieAccessResultToBool( + base::BindLambdaForTesting([&](bool include) { + set_cookie_loop.Quit(); + EXPECT_TRUE(include); + }))); + set_cookie_loop.Run(); + + EXPECT_FALSE(GetCookies().empty()); + ASSERT_TRUE(RunClearAllCachedAuthTokensFunction()); + EXPECT_TRUE(GetCookies().empty()); +} + +INSTANTIATE_TEST_SUITE_P( + All, + ClearAllCachedAuthTokensFunctionTestWithPartitionParam, + ::testing::Values(WebAuthFlow::Partition::LAUNCH_WEB_AUTH_FLOW, + WebAuthFlow::Partition::GET_AUTH_TOKEN)); + class OnSignInChangedEventTest : public IdentityTestWithSignin { protected: void SetUpOnMainThread() override { @@ -3751,7 +3860,7 @@ EXPECT_FALSE(HasExpectedEvent()); } -// Tests the chrome.identity API implemented by custom JS bindings . +// Tests the chrome.identity API implemented by custom JS bindings. IN_PROC_BROWSER_TEST_F(ExtensionApiTest, ChromeIdentityJsBindings) { ASSERT_TRUE(RunExtensionTest("identity/js_bindings")) << message_; }
diff --git a/chrome/browser/extensions/api/identity/identity_clear_all_cached_auth_tokens_function.cc b/chrome/browser/extensions/api/identity/identity_clear_all_cached_auth_tokens_function.cc new file mode 100644 index 0000000..a4deb13 --- /dev/null +++ b/chrome/browser/extensions/api/identity/identity_clear_all_cached_auth_tokens_function.cc
@@ -0,0 +1,72 @@ +// Copyright 2020 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 "chrome/browser/extensions/api/identity/identity_clear_all_cached_auth_tokens_function.h" + +#include "base/bind.h" +#include "base/location.h" +#include "base/stl_util.h" +#include "base/threading/thread_task_runner_handle.h" +#include "chrome/browser/extensions/api/identity/identity_api.h" +#include "chrome/browser/extensions/api/identity/identity_constants.h" +#include "chrome/browser/extensions/api/identity/web_auth_flow.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/common/extensions/api/identity.h" +#include "content/public/browser/storage_partition.h" + +namespace extensions { + +namespace { + +constexpr WebAuthFlow::Partition kPartitionsToClean[] = { + WebAuthFlow::GET_AUTH_TOKEN, WebAuthFlow::LAUNCH_WEB_AUTH_FLOW}; + +} + +IdentityClearAllCachedAuthTokensFunction:: + IdentityClearAllCachedAuthTokensFunction() = default; +IdentityClearAllCachedAuthTokensFunction:: + ~IdentityClearAllCachedAuthTokensFunction() = default; + +ExtensionFunction::ResponseAction +IdentityClearAllCachedAuthTokensFunction::Run() { + Profile* profile = Profile::FromBrowserContext(browser_context()); + if (profile->IsOffTheRecord()) + return RespondNow(Error(identity_constants::kOffTheRecord)); + + IdentityAPI* id_api = IdentityAPI::GetFactoryInstance()->Get(profile); + id_api->EraseGaiaIdForExtension(extension()->id()); + id_api->token_cache()->EraseAllTokensForExtension(extension()->id()); + + for (WebAuthFlow::Partition partition : kPartitionsToClean) { + content::BrowserContext::GetStoragePartition( + profile, WebAuthFlow::GetWebViewPartitionConfig(partition, profile)) + ->GetCookieManagerForBrowserProcess() + ->DeleteCookies( + network::mojom::CookieDeletionFilter::New(), + base::BindOnce( + &IdentityClearAllCachedAuthTokensFunction::OnCookiesDeleted, + this)); + } + + // This object is retained by the DeleteCookies callbacks. + return RespondLater(); +} + +void IdentityClearAllCachedAuthTokensFunction::OnCookiesDeleted( + uint32_t num_deleted) { + ++cleaned_partitions_; + + if (cleaned_partitions_ < base::size(kPartitionsToClean)) + return; + + // Post a task to ensure Respond() is not synchronously called from Run(). The + // object is retained by this task. + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, + base::BindOnce(&IdentityClearAllCachedAuthTokensFunction::Respond, this, + NoArguments())); +} + +} // namespace extensions
diff --git a/chrome/browser/extensions/api/identity/identity_clear_all_cached_auth_tokens_function.h b/chrome/browser/extensions/api/identity/identity_clear_all_cached_auth_tokens_function.h new file mode 100644 index 0000000..ef2302d --- /dev/null +++ b/chrome/browser/extensions/api/identity/identity_clear_all_cached_auth_tokens_function.h
@@ -0,0 +1,32 @@ +// Copyright 2020 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 CHROME_BROWSER_EXTENSIONS_API_IDENTITY_IDENTITY_CLEAR_ALL_CACHED_AUTH_TOKENS_FUNCTION_H_ +#define CHROME_BROWSER_EXTENSIONS_API_IDENTITY_IDENTITY_CLEAR_ALL_CACHED_AUTH_TOKENS_FUNCTION_H_ + +#include "extensions/browser/extension_function.h" +#include "extensions/browser/extension_function_histogram_value.h" + +namespace extensions { + +class IdentityClearAllCachedAuthTokensFunction : public ExtensionFunction { + public: + DECLARE_EXTENSION_FUNCTION("identity.clearAllCachedAuthTokens", + IDENTITY_CLEARALLCACHEDAUTHTOKENS) + IdentityClearAllCachedAuthTokensFunction(); + + private: + ~IdentityClearAllCachedAuthTokensFunction() override; + + // ExtensionFunction: + ResponseAction Run() override; + + void OnCookiesDeleted(uint32_t num_deleted); + + size_t cleaned_partitions_ = 0; +}; + +} // namespace extensions + +#endif // CHROME_BROWSER_EXTENSIONS_API_IDENTITY_IDENTITY_CLEAR_ALL_CACHED_AUTH_TOKENS_FUNCTION_H_
diff --git a/chrome/browser/extensions/api/identity/identity_token_cache.cc b/chrome/browser/extensions/api/identity/identity_token_cache.cc index 8b3276b..aad54f1 100644 --- a/chrome/browser/extensions/api/identity/identity_token_cache.cc +++ b/chrome/browser/extensions/api/identity/identity_token_cache.cc
@@ -192,6 +192,20 @@ } } +void IdentityTokenCache::EraseAllTokensForExtension( + const std::string& extension_id) { + base::EraseIf(access_tokens_cache_, + [&extension_id](const auto& key_value_pair) { + const AccessTokensKey& key = key_value_pair.first; + return key.extension_id == extension_id; + }); + base::EraseIf(intermediate_value_cache_, + [&extension_id](const auto& key_value_pair) { + const ExtensionTokenKey& key = key_value_pair.first; + return key.extension_id == extension_id; + }); +} + void IdentityTokenCache::EraseAllTokens() { intermediate_value_cache_.clear(); access_tokens_cache_.clear();
diff --git a/chrome/browser/extensions/api/identity/identity_token_cache.h b/chrome/browser/extensions/api/identity/identity_token_cache.h index 9fb8e47..1662baee 100644 --- a/chrome/browser/extensions/api/identity/identity_token_cache.h +++ b/chrome/browser/extensions/api/identity/identity_token_cache.h
@@ -106,6 +106,7 @@ const IdentityTokenCacheValue& token_data); void EraseAccessToken(const std::string& extension_id, const std::string& token); + void EraseAllTokensForExtension(const std::string& extension_id); void EraseAllTokens(); const IdentityTokenCacheValue& GetToken(const ExtensionTokenKey& key);
diff --git a/chrome/browser/extensions/api/identity/identity_token_cache_unittest.cc b/chrome/browser/extensions/api/identity/identity_token_cache_unittest.cc index 2ea53e9..a35343a 100644 --- a/chrome/browser/extensions/api/identity/identity_token_cache_unittest.cc +++ b/chrome/browser/extensions/api/identity/identity_token_cache_unittest.cc
@@ -213,6 +213,32 @@ GetToken(ext_2, scopes_2).status()); } +TEST_F(IdentityTokenCacheTest, EraseAllTokensForExtension) { + std::string token_string = "token"; + std::set<std::string> scopes_1 = {"foo", "bar"}; + SetAccessToken(kDefaultExtensionId, token_string, scopes_1); + + std::string remote_consent = "approved"; + std::set<std::string> scopes_2 = {"foo", "foobar"}; + SetRemoteConsentApprovedToken(kDefaultExtensionId, remote_consent, scopes_2); + + std::string unrelated_extension = "ext_unrelated"; + SetAccessToken(unrelated_extension, token_string, scopes_1); + SetRemoteConsentApprovedToken(unrelated_extension, remote_consent, scopes_2); + + cache().EraseAllTokensForExtension(kDefaultExtensionId); + + EXPECT_EQ(IdentityTokenCacheValue::CACHE_STATUS_NOTFOUND, + GetToken(kDefaultExtensionId, scopes_1).status()); + EXPECT_EQ(IdentityTokenCacheValue::CACHE_STATUS_NOTFOUND, + GetToken(kDefaultExtensionId, scopes_2).status()); + + EXPECT_EQ(IdentityTokenCacheValue::CACHE_STATUS_TOKEN, + GetToken(unrelated_extension, scopes_1).status()); + EXPECT_EQ(IdentityTokenCacheValue::CACHE_STATUS_REMOTE_CONSENT_APPROVED, + GetToken(unrelated_extension, scopes_2).status()); +} + TEST_F(IdentityTokenCacheTest, GetAccessTokens) { std::string ext_1 = "ext_1"; std::string token_string_1 = "token_1";
diff --git a/chrome/browser/extensions/api/tab_capture/tab_capture_apitest.cc b/chrome/browser/extensions/api/tab_capture/tab_capture_apitest.cc index b4bb0d3515..27208378 100644 --- a/chrome/browser/extensions/api/tab_capture/tab_capture_apitest.cc +++ b/chrome/browser/extensions/api/tab_capture/tab_capture_apitest.cc
@@ -148,7 +148,8 @@ } // Tests API behaviors, including info queries, and constraints violations. -IN_PROC_BROWSER_TEST_F(TabCaptureApiTest, ApiTests) { +// Disabled due to high flake rate; see https://crbug.com/764464. +IN_PROC_BROWSER_TEST_F(TabCaptureApiTest, DISABLED_ApiTests) { AddExtensionToCommandLineAllowlist(); ASSERT_TRUE(RunExtensionSubtest("tab_capture", "api_tests.html")) << message_; }
diff --git a/chrome/browser/extensions/menu_manager.cc b/chrome/browser/extensions/menu_manager.cc index 55d56730..497b0dc 100644 --- a/chrome/browser/extensions/menu_manager.cc +++ b/chrome/browser/extensions/menu_manager.cc
@@ -669,14 +669,8 @@ webview_guest->view_instance_id()); } - auto args = std::make_unique<base::ListValue>(); - args->Reserve(2); - args->Append(std::move(properties)); - // |properties| is invalidated at this time, which is why |args| needs to be - // queried for the pointer. The obtained pointer is guaranteed to stay valid - // even after further Appends, because enough storage was reserved above. - base::DictionaryValue* raw_properties = nullptr; - args->GetDictionary(0, &raw_properties); + base::Value::ListStorage args; + args.push_back(base::Value::FromUniquePtrValue(std::move(properties))); // Add the tab info to the argument list. // No tab info in a platform app. @@ -685,7 +679,7 @@ if (web_contents) { int frame_id = ExtensionApiFrameIdMap::GetFrameId(render_frame_host); if (frame_id != ExtensionApiFrameIdMap::kInvalidFrameId) - raw_properties->SetInteger("frameId", frame_id); + args[0].SetIntKey("frameId", frame_id); // We intentionally don't scrub the tab data here, since the user chose to // invoke the extension on the page. @@ -693,26 +687,26 @@ // on permissions. ExtensionTabUtil::ScrubTabBehavior scrub_tab_behavior = { ExtensionTabUtil::kDontScrubTab, ExtensionTabUtil::kDontScrubTab}; - args->Append(ExtensionTabUtil::CreateTabObject( - web_contents, scrub_tab_behavior, extension) - ->ToValue()); + args.push_back(base::Value::FromUniquePtrValue( + ExtensionTabUtil::CreateTabObject(web_contents, scrub_tab_behavior, + extension) + ->ToValue())); } else { - args->Append(std::make_unique<base::DictionaryValue>()); + args.push_back(base::DictionaryValue()); } } if (item->type() == MenuItem::CHECKBOX || item->type() == MenuItem::RADIO) { bool was_checked = item->checked(); - raw_properties->SetBoolean("wasChecked", was_checked); + args[0].SetBoolKey("wasChecked", was_checked); // RADIO items always get set to true when you click on them, but CHECKBOX // items get their state toggled. - bool checked = - (item->type() == MenuItem::RADIO) ? true : !was_checked; + bool checked = item->type() == MenuItem::RADIO || !was_checked; item->SetChecked(checked); - raw_properties->SetBoolean("checked", item->checked()); + args[0].SetBoolKey("checked", item->checked()); if (extension) WriteToStorage(extension, item->id().extension_key); @@ -732,7 +726,7 @@ webview_guest ? events::WEB_VIEW_INTERNAL_CONTEXT_MENUS : events::CONTEXT_MENUS, webview_guest ? kOnWebviewContextMenus : kOnContextMenus, - std::unique_ptr<base::ListValue>(args->DeepCopy()), context); + std::make_unique<base::ListValue>(args), context); event->user_gesture = EventRouter::USER_GESTURE_ENABLED; event_router->DispatchEventToExtension(item->extension_id(), std::move(event)); @@ -744,7 +738,7 @@ : events::CONTEXT_MENUS_ON_CLICKED, webview_guest ? api::chrome_web_view_internal::OnClicked::kEventName : api::context_menus::OnClicked::kEventName, - std::move(args), context); + std::make_unique<base::ListValue>(std::move(args)), context); event->user_gesture = EventRouter::USER_GESTURE_ENABLED; if (webview_guest) event->filter_info.instance_id = webview_guest->view_instance_id();
diff --git a/chrome/browser/federated_learning/floc_id_provider_browsertest.cc b/chrome/browser/federated_learning/floc_id_provider_browsertest.cc index 21fd9801..babffe4 100644 --- a/chrome/browser/federated_learning/floc_id_provider_browsertest.cc +++ b/chrome/browser/federated_learning/floc_id_provider_browsertest.cc
@@ -25,6 +25,7 @@ #include "chrome/test/base/in_process_browser_test.h" #include "chrome/test/base/ui_test_utils.h" #include "components/content_settings/core/browser/cookie_settings.h" +#include "components/federated_learning/floc_constants.h" #include "components/history/core/test/fake_web_history_service.h" #include "components/keyed_service/content/browser_context_dependency_manager.h" #include "components/sync/driver/test_sync_service.h" @@ -237,11 +238,25 @@ run_loop.Run(); } + void FinishOutstandingSortingLshQueries() { + base::RunLoop run_loop; + FlocId dummy_floc = FlocId(0u); + g_browser_process->floc_sorting_lsh_clusters_service()->ApplySortingLsh( + dummy_floc, + base::BindLambdaForTesting( + [&](FlocId floc, base::Optional<base::Version> version) { + run_loop.Quit(); + })); + run_loop.Run(); + } + void FinishOutstandingBlocklistQueries() { base::RunLoop run_loop; FlocId dummy_unfiltered_floc = FlocId(0u); + base::Optional<base::Version> no_need_to_verify_version = base::nullopt; + g_browser_process->floc_blocklist_service()->FilterByBlocklist( - dummy_unfiltered_floc, + dummy_unfiltered_floc, no_need_to_verify_version, base::BindLambdaForTesting( [&](FlocId filtered_floc) { run_loop.Quit(); })); run_loop.Run(); @@ -264,6 +279,30 @@ base::NumberToString(next_unique_file_suffix_++)); } + base::FilePath CreateSortingLshFile( + const std::vector<uint32_t>& sorting_lsh_entries) { + base::ScopedAllowBlockingForTesting allow_blocking; + + base::FilePath file_path = GetUniqueTemporaryPath(); + base::File file(file_path, base::File::FLAG_CREATE | base::File::FLAG_READ | + base::File::FLAG_WRITE); + CHECK(file.IsValid()); + + CopyingFileOutputStream copying_stream(std::move(file)); + google::protobuf::io::CopyingOutputStreamAdaptor zero_copy_stream_adaptor( + ©ing_stream); + + google::protobuf::io::CodedOutputStream output_stream( + &zero_copy_stream_adaptor); + + for (uint32_t next : sorting_lsh_entries) + output_stream.WriteVarint32(next); + + CHECK(!output_stream.HadError()); + + return file_path; + } + base::FilePath CreateBlocklistFile( const std::vector<uint64_t>& blocklist_entries) { base::ScopedAllowBlockingForTesting allow_blocking; @@ -292,21 +331,36 @@ void FinishOutstandingAsyncQueries() { FinishOutstandingRemotePermissionQueries(); FinishOutstandingHistoryQueries(); + FinishOutstandingSortingLshQueries(); FinishOutstandingBlocklistQueries(); } - // Turn on sync-history, set up the blocklist file, and trigger the blocklist - // file-ready event. - void InitializeBlocklist(const std::vector<uint64_t>& blocklist_entries) { + // Turn on sync-history, set up the blocklist and sorting-lsh file, and + // trigger the blocklist file-ready event. + void InitializeBlocklistAndSortingLsh( + const std::vector<uint64_t>& blocklist_entries, + base::Version blocklist_version, + const std::vector<uint32_t>& sorting_lsh_entries, + base::Version sorting_lsh_version) { sync_service()->SetActiveDataTypes(syncer::ModelTypeSet::All()); sync_service()->FireStateChanged(); g_browser_process->floc_blocklist_service()->OnBlocklistFileReady( - CreateBlocklistFile(blocklist_entries)); + CreateBlocklistFile(blocklist_entries), blocklist_version); + + g_browser_process->floc_sorting_lsh_clusters_service() + ->OnSortingLshClustersFileReady( + CreateSortingLshFile(sorting_lsh_entries), sorting_lsh_version); FinishOutstandingAsyncQueries(); } + void InitializeBlocklist(const std::vector<uint64_t>& blocklist_entries) { + base::Version kDummyVersion("1.0.0"); + InitializeBlocklistAndSortingLsh(blocklist_entries, kDummyVersion, {}, + kDummyVersion); + } + history::HistoryService* history_service() { return HistoryServiceFactory::GetForProfile( browser()->profile(), ServiceAccessType::IMPLICIT_ACCESS); @@ -658,4 +712,143 @@ InvokeInterestCohortJsApi(web_contents())); } +class FlocIdProviderSortingLshEnabledBrowserTest + : public FlocIdProviderWithCustomizedServicesBrowserTest { + public: + FlocIdProviderSortingLshEnabledBrowserTest() { + scoped_feature_list_.Reset(); + scoped_feature_list_.InitWithFeatures( + {features::kFlocIdComputedEventLogging, + features::kFlocIdSortingLshBasedComputation, + features::kFlocIdBlocklistFiltering}, + {}); + } +}; + +IN_PROC_BROWSER_TEST_F(FlocIdProviderSortingLshEnabledBrowserTest, + SingleSortingLshCluster) { + net::IPAddress::ConsiderLoopbackIPToBePubliclyRoutableForTesting(); + + ConfigureReplacementHostAndPortForRemotePermissionService(); + + std::string cookies_to_set = "/set-cookie?user_id=123"; + ui_test_utils::NavigateToURL( + browser(), https_server_.GetURL(test_host(), cookies_to_set)); + + EXPECT_EQ(1u, GetHistoryUrls().size()); + + EXPECT_FALSE(GetFlocId().IsValid()); + + // All sim_hash will be encoded as 0 during sorting-lsh + std::vector<uint32_t> single_cluster_representation = { + kMaxNumberOfBitsInFloc}; + + InitializeBlocklistAndSortingLsh({}, base::Version("1.0.0"), + single_cluster_representation, + base::Version("1.0.0")); + + // Expect that the FlocIdComputed user event is recorded. + ASSERT_EQ(1u, user_event_service()->GetRecordedUserEvents().size()); + + // Check that the original sim_hash is not 0. + EXPECT_NE(FlocId(0), FlocId::CreateFromHistory({test_host()})); + + // Expect that the final id is 0 because the sorting-lsh was applied. + EXPECT_EQ(FlocId(0), GetFlocId()); +} + +IN_PROC_BROWSER_TEST_F(FlocIdProviderSortingLshEnabledBrowserTest, + MismatchedBlocklistAndSortingLshVersion) { + net::IPAddress::ConsiderLoopbackIPToBePubliclyRoutableForTesting(); + + ConfigureReplacementHostAndPortForRemotePermissionService(); + + std::string cookies_to_set = "/set-cookie?user_id=123"; + ui_test_utils::NavigateToURL( + browser(), https_server_.GetURL(test_host(), cookies_to_set)); + + EXPECT_EQ(1u, GetHistoryUrls().size()); + + EXPECT_FALSE(GetFlocId().IsValid()); + + // All sim_hash will be encoded as 0 during sorting-lsh + std::vector<uint32_t> single_cluster_representation = { + kMaxNumberOfBitsInFloc}; + + InitializeBlocklistAndSortingLsh({}, base::Version("1.0.1"), + single_cluster_representation, + base::Version("1.0.0")); + + // Expect that the FlocIdComputed user event is recorded. + ASSERT_EQ(1u, user_event_service()->GetRecordedUserEvents().size()); + + // Check that the original sim_hash is not 0. + EXPECT_NE(FlocId(0), FlocId::CreateFromHistory({test_host()})); + + // Expect that the final id is invalid because of version mismatch. + EXPECT_FALSE(GetFlocId().IsValid()); +} + +IN_PROC_BROWSER_TEST_F(FlocIdProviderSortingLshEnabledBrowserTest, + SortingLshAndThenBlocked) { + net::IPAddress::ConsiderLoopbackIPToBePubliclyRoutableForTesting(); + + ConfigureReplacementHostAndPortForRemotePermissionService(); + + std::string cookies_to_set = "/set-cookie?user_id=123"; + ui_test_utils::NavigateToURL( + browser(), https_server_.GetURL(test_host(), cookies_to_set)); + + EXPECT_EQ(1u, GetHistoryUrls().size()); + + EXPECT_FALSE(GetFlocId().IsValid()); + + // All sim_hash will be encoded as 0 during sorting-lsh + std::vector<uint32_t> single_cluster_representation = { + kMaxNumberOfBitsInFloc}; + + // Configure a blocklist that would block 0. + InitializeBlocklistAndSortingLsh({0}, base::Version("1.0.0"), + single_cluster_representation, + base::Version("1.0.0")); + + // Expect that the FlocIdComputed user event is recorded. + ASSERT_EQ(1u, user_event_service()->GetRecordedUserEvents().size()); + + // Check that the original sim_hash is not 0. + EXPECT_NE(FlocId(0), FlocId::CreateFromHistory({test_host()})); + + // Expect that the final id is invalid because it was blocked. + EXPECT_FALSE(GetFlocId().IsValid()); +} + +IN_PROC_BROWSER_TEST_F(FlocIdProviderSortingLshEnabledBrowserTest, + CorruptedSortingLSH) { + net::IPAddress::ConsiderLoopbackIPToBePubliclyRoutableForTesting(); + + ConfigureReplacementHostAndPortForRemotePermissionService(); + + std::string cookies_to_set = "/set-cookie?user_id=123"; + ui_test_utils::NavigateToURL( + browser(), https_server_.GetURL(test_host(), cookies_to_set)); + + EXPECT_EQ(1u, GetHistoryUrls().size()); + + EXPECT_FALSE(GetFlocId().IsValid()); + + // All sim_hash will be encoded as an invalid id. + std::vector<uint32_t> corrupted_sorting_lsh = {}; + + InitializeBlocklistAndSortingLsh({}, base::Version("1.0.0"), + corrupted_sorting_lsh, + base::Version("1.0.0")); + + // Expect that the FlocIdComputed user event is recorded. + ASSERT_EQ(1u, user_event_service()->GetRecordedUserEvents().size()); + + // Expect that the final id is invalid due to unexpected sorting-lsh file + // format. + EXPECT_FALSE(GetFlocId().IsValid()); +} + } // namespace federated_learning
diff --git a/chrome/browser/federated_learning/floc_id_provider_impl.cc b/chrome/browser/federated_learning/floc_id_provider_impl.cc index e8a9acb..3fd3468 100644 --- a/chrome/browser/federated_learning/floc_id_provider_impl.cc +++ b/chrome/browser/federated_learning/floc_id_provider_impl.cc
@@ -48,10 +48,16 @@ user_event_service_(user_event_service) { history_service->AddObserver(this); sync_service_->AddObserver(this); + g_browser_process->floc_sorting_lsh_clusters_service()->AddObserver(this); g_browser_process->floc_blocklist_service()->AddObserver(this); OnStateChanged(sync_service); + if (g_browser_process->floc_sorting_lsh_clusters_service() + ->IsSortingLshClustersFileReady()) { + OnSortingLshClustersFileReady(); + } + if (g_browser_process->floc_blocklist_service()->IsBlocklistFileReady()) OnBlocklistFileReady(); } @@ -179,6 +185,15 @@ ComputeFloc(ComputeFlocTrigger::kHistoryDelete); } +void FlocIdProviderImpl::OnSortingLshClustersFileReady() { + if (first_sorting_lsh_file_ready_seen_) + return; + + first_sorting_lsh_file_ready_seen_ = true; + + MaybeTriggerFirstFlocComputation(); +} + void FlocIdProviderImpl::OnBlocklistFileReady() { if (first_blocklist_file_ready_seen_) return; @@ -204,9 +219,17 @@ if (first_floc_computation_triggered_) return; - if (!first_sync_history_enabled_seen_ || - (base::FeatureList::IsEnabled(features::kFlocIdBlocklistFiltering) && - !first_blocklist_file_ready_seen_)) { + bool sorting_lsh_ready_or_not_required = + !base::FeatureList::IsEnabled( + features::kFlocIdSortingLshBasedComputation) || + first_sorting_lsh_file_ready_seen_; + + bool blocklist_ready_or_not_required = + !base::FeatureList::IsEnabled(features::kFlocIdBlocklistFiltering) || + first_blocklist_file_ready_seen_; + + if (!first_sync_history_enabled_seen_ || !sorting_lsh_ready_or_not_required || + !blocklist_ready_or_not_required) { return; } @@ -369,15 +392,39 @@ const FlocId& sim_hash) { DCHECK(sim_hash.IsValid()); - if (!base::FeatureList::IsEnabled(features::kFlocIdBlocklistFiltering)) { - std::move(callback).Run(ComputeFlocResult(sim_hash, sim_hash)); + if (!base::FeatureList::IsEnabled( + features::kFlocIdSortingLshBasedComputation)) { + SkippedOrAppliedSortingLsh(std::move(callback), sim_hash, + /*sim_hash_or_sorting_lsh=*/sim_hash, + /*version_to_validate=*/base::nullopt); + return; + } + + g_browser_process->floc_sorting_lsh_clusters_service()->ApplySortingLsh( + sim_hash, base::BindOnce(&FlocIdProviderImpl::SkippedOrAppliedSortingLsh, + weak_ptr_factory_.GetWeakPtr(), + std::move(callback), sim_hash)); +} + +void FlocIdProviderImpl::SkippedOrAppliedSortingLsh( + ComputeFlocCompletedCallback callback, + const FlocId& sim_hash, + FlocId sim_hash_or_sorting_lsh, + base::Optional<base::Version> version_to_validate) { + // |!sim_hash_or_sorting_lsh.IsValid()| indicates a missing or corrupted + // sorting-lsh file. + if (!base::FeatureList::IsEnabled(features::kFlocIdBlocklistFiltering) || + !sim_hash_or_sorting_lsh.IsValid()) { + std::move(callback).Run( + ComputeFlocResult(sim_hash, sim_hash_or_sorting_lsh)); return; } g_browser_process->floc_blocklist_service()->FilterByBlocklist( - sim_hash, base::BindOnce(&FlocIdProviderImpl::DidApplyAdditionalFiltering, - weak_ptr_factory_.GetWeakPtr(), - std::move(callback), sim_hash)); + sim_hash_or_sorting_lsh, version_to_validate, + base::BindOnce(&FlocIdProviderImpl::DidApplyAdditionalFiltering, + weak_ptr_factory_.GetWeakPtr(), std::move(callback), + sim_hash)); } void FlocIdProviderImpl::DidApplyAdditionalFiltering(
diff --git a/chrome/browser/federated_learning/floc_id_provider_impl.h b/chrome/browser/federated_learning/floc_id_provider_impl.h index b7bccec..268f443 100644 --- a/chrome/browser/federated_learning/floc_id_provider_impl.h +++ b/chrome/browser/federated_learning/floc_id_provider_impl.h
@@ -10,6 +10,7 @@ #include "base/timer/timer.h" #include "chrome/browser/federated_learning/floc_id_provider.h" #include "components/federated_learning/floc_blocklist_service.h" +#include "components/federated_learning/floc_sorting_lsh_clusters_service.h" #include "components/history/core/browser/history_service.h" #include "components/history/core/browser/history_service_observer.h" #include "components/sync/driver/sync_service_observer.h" @@ -44,6 +45,7 @@ // the event of history deletion, the floc will be recomputed immediately and // reset the timer of any currently scheduled computation to be 24 hours later. class FlocIdProviderImpl : public FlocIdProvider, + public FlocSortingLshClustersService::Observer, public FlocBlocklistService::Observer, public history::HistoryServiceObserver, public syncer::SyncServiceObserver { @@ -112,6 +114,9 @@ void OnURLsDeleted(history::HistoryService* history_service, const history::DeletionInfo& deletion_info) override; + // FlocSortingLshClustersService::Observer + void OnSortingLshClustersFileReady() override; + // FlocBlocklistService::Observer void OnBlocklistFileReady() override; @@ -143,6 +148,11 @@ // history. For example, invalidate it if it's in the blocklist. void ApplyAdditionalFiltering(ComputeFlocCompletedCallback callback, const FlocId& sim_hash); + void SkippedOrAppliedSortingLsh( + ComputeFlocCompletedCallback callback, + const FlocId& sim_hash, + FlocId sim_hash_or_sorting_lsh, + base::Optional<base::Version> version_to_validate); void DidApplyAdditionalFiltering(ComputeFlocCompletedCallback callback, FlocId sim_hash, FlocId final_hash); @@ -158,6 +168,7 @@ // loggings, updates, etc.), and compute again. base::Optional<ComputeFlocTrigger> pending_recompute_event_; + bool first_sorting_lsh_file_ready_seen_ = false; bool first_blocklist_file_ready_seen_ = false; bool first_sync_history_enabled_seen_ = false;
diff --git a/chrome/browser/federated_learning/floc_id_provider_unittest.cc b/chrome/browser/federated_learning/floc_id_provider_unittest.cc index 548efd88..9a45d07 100644 --- a/chrome/browser/federated_learning/floc_id_provider_unittest.cc +++ b/chrome/browser/federated_learning/floc_id_provider_unittest.cc
@@ -32,17 +32,22 @@ using ComputeFlocTrigger = FlocIdProviderImpl::ComputeFlocTrigger; using ComputeFlocResult = FlocIdProviderImpl::ComputeFlocResult; +using ComputeFlocCompletedCallback = + FlocIdProviderImpl::ComputeFlocCompletedCallback; +using CanComputeFlocCallback = FlocIdProviderImpl::CanComputeFlocCallback; class MockFlocBlocklistService : public FlocBlocklistService { public: using FlocBlocklistService::FlocBlocklistService; - void ConfigureFlocToBlock(const FlocId& floc_to_block) { + void ConfigureBlocklist(const FlocId& floc_to_block) { floc_to_block_ = floc_to_block; } - void FilterByBlocklist(const FlocId& unfiltered_floc, - FilterByBlocklistCallback callback) override { + void FilterByBlocklist( + const FlocId& unfiltered_floc, + const base::Optional<base::Version>& version_to_validate, + FilterByBlocklistCallback callback) override { if (floc_to_block_ == unfiltered_floc) { std::move(callback).Run(FlocId()); return; @@ -54,6 +59,33 @@ FlocId floc_to_block_; }; +class MockFlocSortingLshService : public FlocSortingLshClustersService { + public: + using FlocSortingLshClustersService::FlocSortingLshClustersService; + + void ConfigureSortingLsh( + const std::unordered_map<uint64_t, FlocId>& sorting_lsh_map, + const base::Version& version) { + sorting_lsh_map_ = sorting_lsh_map; + version_ = version; + } + + void ApplySortingLsh(const FlocId& raw_floc_id, + ApplySortingLshCallback callback) override { + if (sorting_lsh_map_.count(raw_floc_id.ToUint64())) { + std::move(callback).Run(sorting_lsh_map_.at(raw_floc_id.ToUint64()), + version_); + return; + } + + std::move(callback).Run(FlocId(), version_); + } + + private: + std::unordered_map<uint64_t, FlocId> sorting_lsh_map_; + base::Version version_; +}; + class FakeFlocRemotePermissionService : public FlocRemotePermissionService { public: using FlocRemotePermissionService::FlocRemotePermissionService; @@ -203,6 +235,11 @@ &prefs_, /*is_off_the_record=*/false, /*store_last_modified=*/false, /*restore_session=*/false); + auto sorting_lsh_service = std::make_unique<MockFlocSortingLshService>(); + sorting_lsh_service_ = sorting_lsh_service.get(); + TestingBrowserProcess::GetGlobal()->SetFlocSortingLshClustersService( + std::move(sorting_lsh_service)); + auto blocklist_service = std::make_unique<MockFlocBlocklistService>(); blocklist_service_ = blocklist_service.get(); TestingBrowserProcess::GetGlobal()->SetFlocBlocklistService( @@ -238,13 +275,16 @@ history_service_->RemoveObserver(floc_id_provider_.get()); } - void CheckCanComputeFloc( - FlocIdProviderImpl::CanComputeFlocCallback callback) { + void ApplyAdditionalFiltering(ComputeFlocCompletedCallback callback, + const FlocId& sim_hash) { + floc_id_provider_->ApplyAdditionalFiltering(std::move(callback), sim_hash); + } + + void CheckCanComputeFloc(CanComputeFlocCallback callback) { floc_id_provider_->CheckCanComputeFloc(std::move(callback)); } - void IsSwaaNacAccountEnabled( - FlocIdProviderImpl::CanComputeFlocCallback callback) { + void IsSwaaNacAccountEnabled(CanComputeFlocCallback callback) { floc_id_provider_->IsSwaaNacAccountEnabled(std::move(callback)); } @@ -324,6 +364,7 @@ scoped_refptr<FakeCookieSettings> fake_cookie_settings_; std::unique_ptr<MockFlocIdProvider> floc_id_provider_; + MockFlocSortingLshService* sorting_lsh_service_; MockFlocBlocklistService* blocklist_service_; base::ScopedTempDir temp_dir_; @@ -833,7 +874,7 @@ // Trigger the blocklist ready event. The 1st floc computation should be // triggered now as sync & sync-history are enabled the blocklist is ready. - blocklist_service_->OnBlocklistFileReady(base::FilePath()); + blocklist_service_->OnBlocklistFileReady(base::FilePath(), base::Version()); EXPECT_TRUE(first_floc_computation_triggered()); } @@ -845,7 +886,7 @@ // Trigger the blocklist ready event. The 1st floc computation should not be // triggered as sync & sync-history are not enabled yet. - blocklist_service_->OnBlocklistFileReady(base::FilePath()); + blocklist_service_->OnBlocklistFileReady(base::FilePath(), base::Version()); EXPECT_FALSE(first_floc_computation_triggered()); @@ -876,7 +917,7 @@ // Trigger the blocklist ready event, and turn on sync & sync-history to // trigger the 1st floc computation. - blocklist_service_->OnBlocklistFileReady(base::FilePath()); + blocklist_service_->OnBlocklistFileReady(base::FilePath(), base::Version()); test_sync_service_->SetTransportState( syncer::SyncService::TransportState::ACTIVE); @@ -895,7 +936,7 @@ EXPECT_EQ(floc_from_history, floc_id()); // Set the blocklist to block |floc_from_history|. - blocklist_service_->ConfigureFlocToBlock(floc_from_history); + blocklist_service_->ConfigureBlocklist(floc_from_history); task_environment_.FastForwardBy(base::TimeDelta::FromDays(1)); @@ -927,7 +968,7 @@ EXPECT_EQ(floc_from_history.ToUint64(), event.floc_id()); // Reset the blocklist to block nothing. - blocklist_service_->ConfigureFlocToBlock(FlocId()); + blocklist_service_->ConfigureBlocklist(FlocId()); task_environment_.FastForwardBy(base::TimeDelta::FromDays(1)); @@ -1148,4 +1189,52 @@ EXPECT_EQ(FlocId::CreateFromHistory({domain1}), floc_id()); } +TEST_F(FlocIdProviderUnitTest, ApplyAdditionalFiltering_SortingLsh) { + base::test::ScopedFeatureList feature_list; + feature_list.InitWithFeatures({features::kFlocIdSortingLshBasedComputation}, + {}); + + bool callback_called = false; + auto callback = base::BindLambdaForTesting([&](ComputeFlocResult result) { + EXPECT_FALSE(callback_called); + EXPECT_EQ(result.sim_hash, FlocId(3)); + EXPECT_EQ(result.final_hash, FlocId(2)); + callback_called = true; + }); + + // Map 3 to 2 + sorting_lsh_service_->OnSortingLshClustersFileReady(base::FilePath(), + base::Version()); + sorting_lsh_service_->ConfigureSortingLsh({{3, FlocId(2)}}, + base::Version("3.4.5")); + + FlocId sim_hash(3); + ApplyAdditionalFiltering(std::move(callback), sim_hash); + task_environment_.RunUntilIdle(); + EXPECT_TRUE(callback_called); +} + +TEST_F(FlocIdProviderUnitTest, ApplyAdditionalFiltering_SortingLshCorrupted) { + base::test::ScopedFeatureList feature_list; + feature_list.InitWithFeatures({features::kFlocIdSortingLshBasedComputation}, + {}); + + bool callback_called = false; + auto callback = base::BindLambdaForTesting([&](ComputeFlocResult result) { + EXPECT_FALSE(callback_called); + EXPECT_EQ(result.sim_hash, FlocId(3)); + EXPECT_EQ(result.final_hash, FlocId()); + callback_called = true; + }); + + sorting_lsh_service_->OnSortingLshClustersFileReady(base::FilePath(), + base::Version()); + sorting_lsh_service_->ConfigureSortingLsh({}, base::Version("3.4.5")); + + FlocId sim_hash(3); + ApplyAdditionalFiltering(std::move(callback), sim_hash); + task_environment_.RunUntilIdle(); + EXPECT_TRUE(callback_called); +} + } // namespace federated_learning
diff --git a/chrome/browser/installable/installable_manager_browsertest.cc b/chrome/browser/installable/installable_manager_browsertest.cc index a175bb3..720aafe6 100644 --- a/chrome/browser/installable/installable_manager_browsertest.cc +++ b/chrome/browser/installable/installable_manager_browsertest.cc
@@ -95,15 +95,16 @@ class LazyWorkerInstallableManager : public InstallableManager { public: LazyWorkerInstallableManager(content::WebContents* web_contents, - base::Closure quit_closure) - : InstallableManager(web_contents), quit_closure_(quit_closure) {} - ~LazyWorkerInstallableManager() override {} + base::OnceClosure quit_closure) + : InstallableManager(web_contents), + quit_closure_(std::move(quit_closure)) {} + ~LazyWorkerInstallableManager() override = default; protected: - void OnWaitingForServiceWorker() override { quit_closure_.Run(); } + void OnWaitingForServiceWorker() override { std::move(quit_closure_).Run(); } private: - base::Closure quit_closure_; + base::OnceClosure quit_closure_; }; // Used only for testing pages where the manifest URL is changed. This class @@ -114,7 +115,7 @@ : InstallableManager(web_contents) {} ~ResetDataInstallableManager() override {} - void SetQuitClosure(base::Closure quit_closure) { + void SetQuitClosure(base::RepeatingClosure quit_closure) { quit_closure_ = quit_closure; } @@ -125,12 +126,12 @@ } private: - base::Closure quit_closure_; + base::RepeatingClosure quit_closure_; }; class CallbackTester { public: - explicit CallbackTester(base::Closure quit_closure) + explicit CallbackTester(base::RepeatingClosure quit_closure) : quit_closure_(quit_closure) {} void OnDidFinishInstallableCheck(const InstallableData& data) { @@ -162,7 +163,7 @@ bool has_worker() const { return has_worker_; } private: - base::Closure quit_closure_; + base::RepeatingClosure quit_closure_; std::vector<InstallableStatusCode> errors_; GURL manifest_url_; blink::Manifest manifest_; @@ -179,8 +180,10 @@ public: NestedCallbackTester(InstallableManager* manager, const InstallableParams& params, - base::Closure quit_closure) - : manager_(manager), params_(params), quit_closure_(quit_closure) {} + base::OnceClosure quit_closure) + : manager_(manager), + params_(params), + quit_closure_(std::move(quit_closure)) {} void Run() { manager_->GetData( @@ -217,13 +220,13 @@ EXPECT_EQ(manifest_.short_name, data.manifest->short_name); EXPECT_EQ(manifest_.display_override, data.manifest->display_override); - quit_closure_.Run(); + std::move(quit_closure_).Run(); } private: InstallableManager* manager_; InstallableParams params_; - base::Closure quit_closure_; + base::OnceClosure quit_closure_; std::vector<InstallableStatusCode> errors_; GURL manifest_url_; blink::Manifest manifest_;
diff --git a/chrome/browser/metrics/BUILD.gn b/chrome/browser/metrics/BUILD.gn index b4a3aad..5691a2d 100644 --- a/chrome/browser/metrics/BUILD.gn +++ b/chrome/browser/metrics/BUILD.gn
@@ -29,6 +29,123 @@ } generate_expired_histograms_array("expired_histograms_array") { + inputs = [ + "//tools/metrics/histograms/histograms.xml", + "//tools/metrics/histograms/histograms_xml/accessibility/histograms.xml", + "//tools/metrics/histograms/histograms_xml/android/histograms.xml", + "//tools/metrics/histograms/histograms_xml/apps/histograms.xml", + "//tools/metrics/histograms/histograms_xml/arc/histograms.xml", + "//tools/metrics/histograms/histograms_xml/ash/histograms.xml", + "//tools/metrics/histograms/histograms_xml/assistant/histograms.xml", + "//tools/metrics/histograms/histograms_xml/auth/histograms.xml", + "//tools/metrics/histograms/histograms_xml/auto/histograms.xml", + "//tools/metrics/histograms/histograms_xml/autofill/histograms.xml", + "//tools/metrics/histograms/histograms_xml/back_forward_cache/histograms.xml", + "//tools/metrics/histograms/histograms_xml/background/histograms.xml", + "//tools/metrics/histograms/histograms_xml/blink/histograms.xml", + "//tools/metrics/histograms/histograms_xml/bluetooth/histograms.xml", + "//tools/metrics/histograms/histograms_xml/browser/histograms.xml", + "//tools/metrics/histograms/histograms_xml/chrome/histograms.xml", + "//tools/metrics/histograms/histograms_xml/cloud/histograms.xml", + "//tools/metrics/histograms/histograms_xml/compositing/histograms.xml", + "//tools/metrics/histograms/histograms_xml/content/histograms.xml", + "//tools/metrics/histograms/histograms_xml/cookie/histograms.xml", + "//tools/metrics/histograms/histograms_xml/cras/histograms.xml", + "//tools/metrics/histograms/histograms_xml/cros/histograms.xml", + "//tools/metrics/histograms/histograms_xml/crostini/histograms.xml", + "//tools/metrics/histograms/histograms_xml/crypt/histograms.xml", + "//tools/metrics/histograms/histograms_xml/cryptohome/histograms.xml", + "//tools/metrics/histograms/histograms_xml/custom_tabs/histograms.xml", + "//tools/metrics/histograms/histograms_xml/data_reduction_proxy/histograms.xml", + "//tools/metrics/histograms/histograms_xml/dev/histograms.xml", + "//tools/metrics/histograms/histograms_xml/diagnostics/histograms.xml", + "//tools/metrics/histograms/histograms_xml/direct/histograms.xml", + "//tools/metrics/histograms/histograms_xml/disk/histograms.xml", + "//tools/metrics/histograms/histograms_xml/dom/histograms.xml", + "//tools/metrics/histograms/histograms_xml/download/histograms.xml", + "//tools/metrics/histograms/histograms_xml/enterprise/histograms.xml", + "//tools/metrics/histograms/histograms_xml/event/histograms.xml", + "//tools/metrics/histograms/histograms_xml/extension/histograms.xml", + "//tools/metrics/histograms/histograms_xml/extensions/histograms.xml", + "//tools/metrics/histograms/histograms_xml/file/histograms.xml", + "//tools/metrics/histograms/histograms_xml/fingerprint/histograms.xml", + "//tools/metrics/histograms/histograms_xml/gcm/histograms.xml", + "//tools/metrics/histograms/histograms_xml/geolocation/histograms.xml", + "//tools/metrics/histograms/histograms_xml/google/histograms.xml", + "//tools/metrics/histograms/histograms_xml/gpu/histograms.xml", + "//tools/metrics/histograms/histograms_xml/histogram_suffixes_list.xml", + "//tools/metrics/histograms/histograms_xml/history/histograms.xml", + "//tools/metrics/histograms/histograms_xml/holding_space/histograms.xml", + "//tools/metrics/histograms/histograms_xml/image/histograms.xml", + "//tools/metrics/histograms/histograms_xml/input/histograms.xml", + "//tools/metrics/histograms/histograms_xml/installer/histograms.xml", + "//tools/metrics/histograms/histograms_xml/instant/histograms.xml", + "//tools/metrics/histograms/histograms_xml/interstitial/histograms.xml", + "//tools/metrics/histograms/histograms_xml/ios/histograms.xml", + "//tools/metrics/histograms/histograms_xml/local/histograms.xml", + "//tools/metrics/histograms/histograms_xml/login/histograms.xml", + "//tools/metrics/histograms/histograms_xml/media/histograms.xml", + "//tools/metrics/histograms/histograms_xml/memory/histograms.xml", + "//tools/metrics/histograms/histograms_xml/mobile/histograms.xml", + "//tools/metrics/histograms/histograms_xml/multi_device/histograms.xml", + "//tools/metrics/histograms/histograms_xml/na_cl/histograms.xml", + "//tools/metrics/histograms/histograms_xml/navigation/histograms.xml", + "//tools/metrics/histograms/histograms_xml/net/histograms.xml", + "//tools/metrics/histograms/histograms_xml/network/histograms.xml", + "//tools/metrics/histograms/histograms_xml/new_tab_page/histograms.xml", + "//tools/metrics/histograms/histograms_xml/notifications/histograms.xml", + "//tools/metrics/histograms/histograms_xml/offline/histograms.xml", + "//tools/metrics/histograms/histograms_xml/omnibox/histograms.xml", + "//tools/metrics/histograms/histograms_xml/oobe/histograms.xml", + "//tools/metrics/histograms/histograms_xml/optimization/histograms.xml", + "//tools/metrics/histograms/histograms_xml/others/histograms.xml", + "//tools/metrics/histograms/histograms_xml/page/histograms.xml", + "//tools/metrics/histograms/histograms_xml/password/histograms.xml", + "//tools/metrics/histograms/histograms_xml/payment/histograms.xml", + "//tools/metrics/histograms/histograms_xml/permissions/histograms.xml", + "//tools/metrics/histograms/histograms_xml/platform/histograms.xml", + "//tools/metrics/histograms/histograms_xml/plugin/histograms.xml", + "//tools/metrics/histograms/histograms_xml/power/histograms.xml", + "//tools/metrics/histograms/histograms_xml/print/histograms.xml", + "//tools/metrics/histograms/histograms_xml/printing/histograms.xml", + "//tools/metrics/histograms/histograms_xml/profile/histograms.xml", + "//tools/metrics/histograms/histograms_xml/quickoffice/histograms.xml", + "//tools/metrics/histograms/histograms_xml/quota/histograms.xml", + "//tools/metrics/histograms/histograms_xml/renderer/histograms.xml", + "//tools/metrics/histograms/histograms_xml/renderer4/histograms.xml", + "//tools/metrics/histograms/histograms_xml/safe_browsing/histograms.xml", + "//tools/metrics/histograms/histograms_xml/sb_client/histograms.xml", + "//tools/metrics/histograms/histograms_xml/search/histograms.xml", + "//tools/metrics/histograms/histograms_xml/security/histograms.xml", + "//tools/metrics/histograms/histograms_xml/service/histograms.xml", + "//tools/metrics/histograms/histograms_xml/session/histograms.xml", + "//tools/metrics/histograms/histograms_xml/settings/histograms.xml", + "//tools/metrics/histograms/histograms_xml/sharing/histograms.xml", + "//tools/metrics/histograms/histograms_xml/signin/histograms.xml", + "//tools/metrics/histograms/histograms_xml/simple/histograms.xml", + "//tools/metrics/histograms/histograms_xml/smart/histograms.xml", + "//tools/metrics/histograms/histograms_xml/software/histograms.xml", + "//tools/metrics/histograms/histograms_xml/stability/histograms.xml", + "//tools/metrics/histograms/histograms_xml/startup/histograms.xml", + "//tools/metrics/histograms/histograms_xml/storage/histograms.xml", + "//tools/metrics/histograms/histograms_xml/subresource/histograms.xml", + "//tools/metrics/histograms/histograms_xml/sync/histograms.xml", + "//tools/metrics/histograms/histograms_xml/tab/histograms.xml", + "//tools/metrics/histograms/histograms_xml/translate/histograms.xml", + "//tools/metrics/histograms/histograms_xml/ukm/histograms.xml", + "//tools/metrics/histograms/histograms_xml/uma/histograms.xml", + "//tools/metrics/histograms/histograms_xml/update_engine/histograms.xml", + "//tools/metrics/histograms/histograms_xml/v8/histograms.xml", + "//tools/metrics/histograms/histograms_xml/variations/histograms.xml", + "//tools/metrics/histograms/histograms_xml/video_tutorials/histograms.xml", + "//tools/metrics/histograms/histograms_xml/web_apk/histograms.xml", + "//tools/metrics/histograms/histograms_xml/web_audio/histograms.xml", + "//tools/metrics/histograms/histograms_xml/web_core/histograms.xml", + "//tools/metrics/histograms/histograms_xml/web_rtc/histograms.xml", + "//tools/metrics/histograms/histograms_xml/windows/histograms.xml", + "//tools/metrics/histograms/histograms_xml/obsolete_histograms.xml", + "//tools/metrics/histograms/enums.xml", + ] namespace = "chrome_metrics" header_filename = "expired_histograms_array.h" major_branch_date_filepath = "//chrome/MAJOR_BRANCH_DATE"
diff --git a/chrome/browser/nearby_sharing/client/nearby_share_api_call_flow_impl_unittest.cc b/chrome/browser/nearby_sharing/client/nearby_share_api_call_flow_impl_unittest.cc index af865f4..c92f282 100644 --- a/chrome/browser/nearby_sharing/client/nearby_share_api_call_flow_impl_unittest.cc +++ b/chrome/browser/nearby_sharing/client/nearby_share_api_call_flow_impl_unittest.cc
@@ -82,12 +82,12 @@ void StartPostRequestApiCallFlowWithSerializedRequest( const std::string& serialized_request) { - flow_.StartPostRequest(GURL(kRequestUrl), serialized_request, - shared_factory_, kAccessToken, - base::Bind(&NearbyShareApiCallFlowImplTest::OnResult, - base::Unretained(this)), - base::Bind(&NearbyShareApiCallFlowImplTest::OnError, - base::Unretained(this))); + flow_.StartPostRequest( + GURL(kRequestUrl), serialized_request, shared_factory_, kAccessToken, + base::BindOnce(&NearbyShareApiCallFlowImplTest::OnResult, + base::Unretained(this)), + base::BindOnce(&NearbyShareApiCallFlowImplTest::OnError, + base::Unretained(this))); // A pending fetch for the API request should be created. CheckNearbySharingClientHttpPostRequest(serialized_request); } @@ -100,10 +100,10 @@ const std::string& serialized_request) { flow_.StartPatchRequest( GURL(kRequestUrl), serialized_request, shared_factory_, kAccessToken, - base::Bind(&NearbyShareApiCallFlowImplTest::OnResult, - base::Unretained(this)), - base::Bind(&NearbyShareApiCallFlowImplTest::OnError, - base::Unretained(this))); + base::BindOnce(&NearbyShareApiCallFlowImplTest::OnResult, + base::Unretained(this)), + base::BindOnce(&NearbyShareApiCallFlowImplTest::OnError, + base::Unretained(this))); // A pending fetch for the API request should be created. CheckNearbySharingClientHttpPatchRequest(serialized_request); } @@ -116,12 +116,13 @@ void StartGetRequestApiCallFlowWithRequestAsQueryParameters( const NearbyShareApiCallFlow::QueryParameters& request_as_query_parameters) { - flow_.StartGetRequest(GURL(kRequestUrl), request_as_query_parameters, - shared_factory_, kAccessToken, - base::Bind(&NearbyShareApiCallFlowImplTest::OnResult, - base::Unretained(this)), - base::Bind(&NearbyShareApiCallFlowImplTest::OnError, - base::Unretained(this))); + flow_.StartGetRequest( + GURL(kRequestUrl), request_as_query_parameters, shared_factory_, + kAccessToken, + base::BindOnce(&NearbyShareApiCallFlowImplTest::OnResult, + base::Unretained(this)), + base::BindOnce(&NearbyShareApiCallFlowImplTest::OnError, + base::Unretained(this))); // A pending fetch for the API request should be created. CheckNearbySharingClientHttpGetRequest(request_as_query_parameters); }
diff --git a/chrome/browser/password_check/android/password_check_manager.cc b/chrome/browser/password_check/android/password_check_manager.cc index da527fd..1afa9fc 100644 --- a/chrome/browser/password_check/android/password_check_manager.cc +++ b/chrome/browser/password_check/android/password_check_manager.cc
@@ -91,7 +91,6 @@ // The request is being handled, so reset the boolean. was_start_requested_ = false; is_check_running_ = true; - progress_ = std::make_unique<PasswordCheckProgress>(); for (const auto& password : saved_passwords_presenter_.GetSavedPasswords()) progress_->IncrementCounts(password); @@ -197,6 +196,11 @@ if (state != State::kRunning) { progress_.reset(); is_check_running_ = false; + if (saved_passwords_presenter_.GetSavedPasswords().empty()) { + observer_->OnPasswordCheckStatusChanged( + PasswordCheckUIStatus::kErrorNoPasswords); + return; + } } observer_->OnPasswordCheckStatusChanged(GetUIStatus(state));
diff --git a/chrome/browser/password_check/android/password_check_manager_unittest.cc b/chrome/browser/password_check/android/password_check_manager_unittest.cc index 21f753fb..8a3d42d2 100644 --- a/chrome/browser/password_check/android/password_check_manager_unittest.cc +++ b/chrome/browser/password_check/android/password_check_manager_unittest.cc
@@ -593,3 +593,20 @@ base::ASCIIToUTF16(kPassword1)), password_manager::IsLeaked(false)); } + +TEST_F(PasswordCheckManagerTest, TurnsIdleIntoNoPasswords) { + InitializeManager(); + RunUntilIdle(); + + EXPECT_CALL(mock_observer(), + OnPasswordCheckStatusChanged(PasswordCheckUIStatus::kRunning)) + .Times(1); + EXPECT_CALL(mock_observer(), + OnPasswordCheckStatusChanged(PasswordCheckUIStatus::kIdle)) + .Times(0); + EXPECT_CALL(mock_observer(), OnPasswordCheckStatusChanged( + PasswordCheckUIStatus::kErrorNoPasswords)) + .Times(1); + + manager().StartCheck(); +}
diff --git a/chrome/browser/prerender/isolated/isolated_prerender_browsertest.cc b/chrome/browser/prerender/isolated/isolated_prerender_browsertest.cc index 8951900..f13eefe 100644 --- a/chrome/browser/prerender/isolated/isolated_prerender_browsertest.cc +++ b/chrome/browser/prerender/isolated/isolated_prerender_browsertest.cc
@@ -2573,6 +2573,50 @@ "IsolatedPrerender.AfterClick.Subresources.UsedCache", true, 2); } +IN_PROC_BROWSER_TEST_F(IsolatedPrerenderWithNSPBrowserTest, + DISABLE_ON_WIN_MAC_CHROMEOS(StartsSpareRenderer)) { + // Enable low-end device mode to turn off automatic spare renderers. Note that + // this will also prevent NSPs from triggering, but the logic under test + // happens before that anyways. + base::CommandLine::ForCurrentProcess()->AppendSwitch( + switches::kEnableLowEndDeviceMode); + base::CommandLine::ForCurrentProcess()->AppendSwitch( + "isolated-prerender-start-spare-renderer"); + + SetDataSaverEnabled(true); + GURL starting_page = GetOriginServerURL("/simple.html"); + ui_test_utils::NavigateToURL(browser(), starting_page); + WaitForUpdatedCustomProxyConfig(); + + IsolatedPrerenderTabHelper* tab_helper = + IsolatedPrerenderTabHelper::FromWebContents(GetWebContents()); + + GURL eligible_link = + GetOriginServerURL("/prerender/isolated/prefetch_page.html"); + + TestTabHelperObserver tab_helper_observer(tab_helper); + tab_helper_observer.SetExpectedSuccessfulURLs({eligible_link}); + + base::RunLoop prefetch_run_loop; + tab_helper_observer.SetOnPrefetchSuccessfulClosure( + prefetch_run_loop.QuitClosure()); + + GURL doc_url("https://www.google.com/search?q=test"); + MakeNavigationPrediction(doc_url, {eligible_link}); + + base::HistogramTester histogram_tester; + + // This run loop will quit when all the prefetch responses have been + // successfully done and processed. + prefetch_run_loop.Run(); + + // Navigate to trigger the histogram recording. + ui_test_utils::NavigateToURL(browser(), GURL("about:blank")); + + histogram_tester.ExpectUniqueSample( + "IsolatedPrerender.SpareRenderer.CountStartedOnSRP", 1, 1); +} + namespace { std::unique_ptr<net::test_server::HttpResponse> HandleNonEligibleOrigin( const net::test_server::HttpRequest& request) {
diff --git a/chrome/browser/prerender/isolated/isolated_prerender_params.cc b/chrome/browser/prerender/isolated/isolated_prerender_params.cc index 953b8fa..4f91bc9a 100644 --- a/chrome/browser/prerender/isolated/isolated_prerender_params.cc +++ b/chrome/browser/prerender/isolated/isolated_prerender_params.cc
@@ -156,3 +156,10 @@ return base::GetFieldTrialParamByFeatureAsInt( features::kIsolatePrerenders, "max_subresource_count_per_prerender", 50); } + +bool IsolatedPrerenderStartsSpareRenderer() { + return base::CommandLine::ForCurrentProcess()->HasSwitch( + "isolated-prerender-start-spare-renderer") || + base::GetFieldTrialParamByFeatureAsBool(features::kIsolatePrerenders, + "start_spare_renderer", false); +}
diff --git a/chrome/browser/prerender/isolated/isolated_prerender_params.h b/chrome/browser/prerender/isolated/isolated_prerender_params.h index caeadac5..1017a58d 100644 --- a/chrome/browser/prerender/isolated/isolated_prerender_params.h +++ b/chrome/browser/prerender/isolated/isolated_prerender_params.h
@@ -73,4 +73,8 @@ // The maximum number of subresources that will be fetched per prefetched page. size_t IsolatedPrerenderMaxSubresourcesPerPrerender(); +// Whether a spare renderer should be started after all prefetching and NSP is +// complete. +bool IsolatedPrerenderStartsSpareRenderer(); + #endif // CHROME_BROWSER_PRERENDER_ISOLATED_ISOLATED_PRERENDER_PARAMS_H_
diff --git a/chrome/browser/prerender/isolated/isolated_prerender_tab_helper.cc b/chrome/browser/prerender/isolated/isolated_prerender_tab_helper.cc index 69ea90a..19ec6efa 100644 --- a/chrome/browser/prerender/isolated/isolated_prerender_tab_helper.cc +++ b/chrome/browser/prerender/isolated/isolated_prerender_tab_helper.cc
@@ -38,6 +38,7 @@ #include "content/public/browser/navigation_handle.h" #include "content/public/browser/network_service_instance.h" #include "content/public/browser/render_frame_host.h" +#include "content/public/browser/render_process_host.h" #include "content/public/browser/service_worker_context.h" #include "content/public/browser/storage_partition.h" #include "content/public/browser/web_contents.h" @@ -147,6 +148,23 @@ run_me.Run(); } +bool ShouldStartSpareRenderer() { + if (!IsolatedPrerenderStartsSpareRenderer()) { + return false; + } + + for (content::RenderProcessHost::iterator iter( + content::RenderProcessHost::AllHostsIterator()); + !iter.IsAtEnd(); iter.Advance()) { + if (iter.GetCurrentValue()->IsUnused()) { + // There is already a spare renderer. + return false; + } + } + + return true; +} + } // namespace IsolatedPrerenderTabHelper::PrefetchMetrics::PrefetchMetrics() = default; @@ -168,6 +186,12 @@ } IsolatedPrerenderTabHelper::CurrentPageLoad::~CurrentPageLoad() { + if (IsolatedPrerenderStartsSpareRenderer()) { + UMA_HISTOGRAM_COUNTS_100( + "IsolatedPrerender.SpareRenderer.CountStartedOnSRP", + number_of_spare_renderers_started_); + } + if (!profile_) return; @@ -680,6 +704,12 @@ IsolatedPrerenderMainframeBodyLengthLimit()); page_->url_loaders_.emplace(std::move(loader)); + + // Start a spare renderer now so that it will be ready by the time it is + // useful to have. + if (ShouldStartSpareRenderer()) { + StartSpareRenderer(); + } } void IsolatedPrerenderTabHelper::OnPrefetchRedirect( @@ -922,6 +952,12 @@ void IsolatedPrerenderTabHelper::OnPrerenderDone(const GURL& url) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + // The completed NSP probably consumed a previously started spare renderer, so + // kick off another one if needed. + if (ShouldStartSpareRenderer()) { + StartSpareRenderer(); + } + // It is possible that this is run as a callback after a navigation has // already happened and |page_| is now a different instance than when the // prerender was started. In this case, just return. @@ -943,6 +979,11 @@ DoNoStatePrefetch(); } +void IsolatedPrerenderTabHelper::StartSpareRenderer() { + page_->number_of_spare_renderers_started_++; + content::RenderProcessHost::WarmupSpareRenderProcessHost(profile_); +} + void IsolatedPrerenderTabHelper::OnPredictionUpdated( const base::Optional<NavigationPredictorKeyedService::Prediction> prediction) {
diff --git a/chrome/browser/prerender/isolated/isolated_prerender_tab_helper.h b/chrome/browser/prerender/isolated/isolated_prerender_tab_helper.h index 443b7a9..be234bf 100644 --- a/chrome/browser/prerender/isolated/isolated_prerender_tab_helper.h +++ b/chrome/browser/prerender/isolated/isolated_prerender_tab_helper.h
@@ -362,6 +362,9 @@ // The number of no state prefetch requests that have been made. size_t number_of_no_state_prefetch_attempts_ = 0; + // The number of spare renderers that were started during this page load. + size_t number_of_spare_renderers_started_ = 0; + // All urls that are eligible to be no state prefetched. Once a no state // prefetch finishes, in success or in error, it is removed from this list. // If there is an active no state prefetch, its url will always be the first @@ -445,6 +448,10 @@ // Starts a new no state prefetch for the next eligible url. void DoNoStatePrefetch(); + // Starts a spare renderer. Should only be called when all NSPs and + // prefetching is complete. + void StartSpareRenderer(); + // Makes a clone of |this|'s prefetch response so that it can be used for // NoStatePrefetch now and later reused if the user navigates to that page. std::unique_ptr<PrefetchedMainframeResponseContainer>
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/i_tutorial/components/i_tutorial.js b/chrome/browser/resources/chromeos/accessibility/chromevox/i_tutorial/components/i_tutorial.js index 7148dea..b0f8a23 100644 --- a/chrome/browser/resources/chromeos/accessibility/chromevox/i_tutorial/components/i_tutorial.js +++ b/chrome/browser/resources/chromeos/accessibility/chromevox/i_tutorial/components/i_tutorial.js
@@ -90,6 +90,8 @@ numLessons: {type: Number, value: 0}, + numLoadedLessons: {type: Number, value: 0}, + activeScreen: {type: String, observer: 'onActiveScreenChanged'}, interactiveMode: {type: Boolean, value: false}, @@ -436,14 +438,13 @@ { title: 'Sounds', - content: [ - 'ChromeVox uses sounds to give you essential and additional ' + - 'information. You can use these sounds to navigate more ' + - 'quickly by learning what each sound means. Once you get ' + - 'more comfortable, you can turn off verbose descriptions in ' + - 'speech and rely on them for essential information about the ' + - 'page. Here is a complete list of sounds and what they mean', - ], + content: + ['ChromeVox uses sounds to give you essential and additional ' + + 'information. You can use these sounds to navigate more ' + + 'quickly by learning what each sound means. Once you get ' + + 'more comfortable, you can turn off verbose descriptions in ' + + 'speech and rely on them for essential information about the ' + + 'page. Here is a complete list of sounds and what they mean'], medium: InteractionMedium.KEYBOARD, curriculums: [Curriculum.SOUNDS_AND_SETTINGS] }, @@ -488,8 +489,16 @@ /** @override */ ready() { - document.addEventListener('keydown', this.onKeyDown.bind(this)); this.hideAllScreens(); + document.addEventListener('keydown', this.onKeyDown.bind(this)); + this.addEventListener('lessonready', () => { + this.numLoadedLessons += 1; + if (this.numLoadedLessons === this.lessonData.length) { + this.buildEarconLesson(); + this.dispatchEvent( + new CustomEvent('readyfortesting', {composed: true})); + } + }); this.$.lessonTemplate.addEventListener('dom-change', (evt) => { // Executes once all lessons have been added to the dom. this.show(); @@ -968,5 +977,45 @@ // Queue lesson content so it is read after the lesson title. this.requestSpeech(text, QueueMode.QUEUE); } + }, + + /** + * @private + * @suppress {undefinedVars|missingProperties} For referencing + * EarconDescription and Msgs, which are defined on the Panel window. + */ + buildEarconLesson() { + // Find earcon lesson. + let earconLesson; + const elements = this.$.lessonContainer.children; + for (const element of elements) { + if (element.is === 'tutorial-lesson' && element.title === 'Sounds') { + earconLesson = element; + } + } + + if (!earconLesson) { + throw new Error('Could not find the earcon lesson.'); + } + + // Add text and listeners. + for (const earconId in EarconDescription) { + const msgid = EarconDescription[earconId]; + const earconElement = document.createElement('p'); + earconElement.innerText = Msgs.getMsg(msgid); + earconElement.setAttribute('tabindex', -1); + earconElement.addEventListener( + 'focus', this.requestEarcon.bind(this, earconId)); + earconLesson.contentDiv.appendChild(earconElement); + } + }, + + /** + * @param {string} earconId + * @private + */ + requestEarcon(earconId) { + this.dispatchEvent( + new CustomEvent('requestearcon', {composed: true, detail: {earconId}})); } });
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/i_tutorial/components/tutorial_lesson.html b/chrome/browser/resources/chromeos/accessibility/chromevox/i_tutorial/components/tutorial_lesson.html index 9e37df8..a9caeba 100644 --- a/chrome/browser/resources/chromeos/accessibility/chromevox/i_tutorial/components/tutorial_lesson.html +++ b/chrome/browser/resources/chromeos/accessibility/chromevox/i_tutorial/components/tutorial_lesson.html
@@ -97,7 +97,8 @@ <h1 id="title" tabindex="-1">[[ title ]]</h1> </template> <div id="content"> - <template is="dom-repeat" items="[[ content ]]" as="text"> + <template id="contentTemplate" is="dom-repeat" items="[[ content ]]" + as="text"> <p tabindex="-1">[[ text ]]</p> </template> </div>
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/i_tutorial/components/tutorial_lesson.js b/chrome/browser/resources/chromeos/accessibility/chromevox/i_tutorial/components/tutorial_lesson.js index aa9d5820..28d1988 100644 --- a/chrome/browser/resources/chromeos/accessibility/chromevox/i_tutorial/components/tutorial_lesson.js +++ b/chrome/browser/resources/chromeos/accessibility/chromevox/i_tutorial/components/tutorial_lesson.js
@@ -51,6 +51,11 @@ /** @override */ ready() { + this.$.contentTemplate.addEventListener('dom-change', (evt) => { + this.dispatchEvent(new CustomEvent('lessonready', {composed: true})); + }); + + if (this.practiceFile) { this.populatePracticeContent(); for (const evt of this.events) { @@ -258,4 +263,9 @@ return false; }, + + /** @return {Element} */ + get contentDiv() { + return this.$.content; + } }); \ No newline at end of file
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/panel/panel.js b/chrome/browser/resources/chromeos/accessibility/chromevox/panel/panel.js index de3f75f..48d6d22 100644 --- a/chrome/browser/resources/chromeos/accessibility/chromevox/panel/panel.js +++ b/chrome/browser/resources/chromeos/accessibility/chromevox/panel/panel.js
@@ -8,6 +8,7 @@ goog.provide('Panel'); +goog.require('AbstractEarcons'); goog.require('AnnotationsUI'); goog.require('BackgroundKeyboardHandler'); goog.require('BrailleCommandData'); @@ -187,6 +188,9 @@ 'enable-experimental-accessibility-chromevox-tutorial', (enabled) => { Panel.iTutorialEnabled_ = enabled; }); + + /** @private {boolean} */ + Panel.iTutorialReadyForTesting_ = false; } /** @@ -1230,6 +1234,14 @@ chrome.extension.getBackgroundPage()['CommandHandler']; commandHandler.onCommand('fullyDescribe'); }); + $('i-tutorial').addEventListener('requestearcon', (evt) => { + const earconId = evt.detail.earconId; + chrome.extension + .getBackgroundPage()['ChromeVox']['earcons']['playEarcon'](earconId); + }); + $('i-tutorial').addEventListener('readyfortesting', () => { + Panel.iTutorialReadyForTesting_ = true; + }); } /**
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/panel/tutorial_test.js b/chrome/browser/resources/chromeos/accessibility/chromevox/panel/tutorial_test.js index 17e205a..1e254804 100644 --- a/chrome/browser/resources/chromeos/accessibility/chromevox/panel/tutorial_test.js +++ b/chrome/browser/resources/chromeos/accessibility/chromevox/panel/tutorial_test.js
@@ -82,8 +82,16 @@ if (mutation.type === 'childList') { for (const node of mutation.addedNodes) { if (node.id === 'i-tutorial-container') { - // Resolve once the tutorial has been added to the document. - resolve(); + // Once the tutorial has been added to the document, we need + // to wait for the lesson templates to load. + const panel = this.getPanel(); + if (panel.iTutorialReadyForTesting_) { + resolve(); + } else { + panel.iTutorial.addEventListener('readyfortesting', () => { + resolve(); + }); + } observer.disconnect(); } } @@ -445,3 +453,33 @@ .replay(); }); }); + +// Tests for correct speech and earcons on the earcons lesson. +TEST_F('ChromeVoxTutorialTest', 'EarconLesson', function() { + const mockFeedback = this.createMockFeedback(); + this.runWithLoadedTree(this.simpleDoc, async function(root) { + await this.launchAndWaitForTutorial(); + const tutorial = this.getPanel().iTutorial; + const nextObjectAndExpectSpeechAndEarcon = (speech, earcon) => { + mockFeedback.call(doCmd('nextObject')) + .expectSpeech(speech) + .expectEarcon(earcon); + }; + mockFeedback.expectSpeech('Choose your tutorial experience') + .call(() => { + // Show the lesson. + tutorial.curriculum = 'sounds_and_settings'; + tutorial.showLesson(0); + }) + .expectSpeech('Sounds') + .call(doCmd('nextObject')) + .expectSpeech(new RegExp( + 'ChromeVox uses sounds to give you essential and additional ' + + 'information.')); + nextObjectAndExpectSpeechAndEarcon('A modal alert', Earcon.ALERT_MODAL); + nextObjectAndExpectSpeechAndEarcon( + 'A non modal alert', Earcon.ALERT_NONMODAL); + nextObjectAndExpectSpeechAndEarcon('A button', Earcon.BUTTON); + mockFeedback.replay(); + }); +});
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/switch_access.js b/chrome/browser/resources/chromeos/accessibility/switch_access/switch_access.js index 97e751d..778535cc 100644 --- a/chrome/browser/resources/chromeos/accessibility/switch_access/switch_access.js +++ b/chrome/browser/resources/chromeos/accessibility/switch_access/switch_access.js
@@ -10,8 +10,6 @@ class SwitchAccess { static initialize() { SwitchAccess.instance = new SwitchAccess(); - chrome.virtualKeyboardPrivate.setKeyboardState( - chrome.virtualKeyboardPrivate.KeyboardState.ENABLED); chrome.automation.getDesktop((desktop) => { // NavigationManager must be initialized first.
diff --git a/chrome/browser/resources/settings/chromeos/device_page/pointers.html b/chrome/browser/resources/settings/chromeos/device_page/pointers.html index 20b7b39..a58258f 100644 --- a/chrome/browser/resources/settings/chromeos/device_page/pointers.html +++ b/chrome/browser/resources/settings/chromeos/device_page/pointers.html
@@ -5,6 +5,7 @@ <link rel="import" href="../deep_linking_behavior.html"> <link rel="import" href="../localized_link/localized_link.html"> <link rel="import" href="../../controls/settings_radio_group.html"> +<link rel="import" href="../../controls/settings_dropdown_menu.html"> <link rel="import" href="../../controls/settings_slider.html"> <link rel="import" href="../../controls/settings_toggle_button.html"> <link rel="import" href="../../prefs/prefs_behavior.html"> @@ -40,14 +41,16 @@ <div class$="[[getSubsectionClass_(showMouseSection_, hasTouchpad)]]"> <!-- Do not change the mouse button pref before the mouse is released. See crbug.com/686949 --> - <settings-toggle-button id="mouseSwapButton" - pref="{{prefs.settings.mouse.primary_right}}" - label="$i18n{mouseSwapButtons}" - on-settings-boolean-control-change="onMouseSwapButtonsChange_" - on-down="onMouseSwapButtonsDown_" on-up="onMouseSwapButtonsUp_" - deep-link-focus-id$="[[Setting.kMouseSwapPrimaryButtons]]" - no-set-pref> - </settings-toggle-button> + <div class="settings-box"> + <div class="start settings-box-text" id="mouseSwapButtonLabel"> + $i18n{mouseSwapButtons} + </div> + <settings-dropdown-menu aria-labeledby="mouseSwapButtonLabel" + pref="{{prefs.settings.mouse.primary_right}}" + menu-options="[[swapPrimaryOptions]]" + deep-link-focus-id$="[[Setting.kMouseSwapPrimaryButtons]]"> + </settings-dropdown-menu> + </div> <template is="dom-if" if="[[allowDisableAcceleration_]]"> <settings-toggle-button id="mouseAcceleration" class="hr"
diff --git a/chrome/browser/resources/settings/chromeos/device_page/pointers.js b/chrome/browser/resources/settings/chromeos/device_page/pointers.js index be6fbda0..dbe4c81 100644 --- a/chrome/browser/resources/settings/chromeos/device_page/pointers.js +++ b/chrome/browser/resources/settings/chromeos/device_page/pointers.js
@@ -27,6 +27,23 @@ hasTouchpad: Boolean, + swapPrimaryOptions: { + readOnly: true, + type: Array, + value() { + return [ + { + value: false, + name: loadTimeData.getString('primaryMouseButtonLeft') + }, + { + value: true, + name: loadTimeData.getString('primaryMouseButtonRight') + }, + ]; + }, + }, + /** * Interim property for use until we have a separate subsection for pointing * sticks. (See crbug.com/1114828) @@ -101,10 +118,6 @@ return hasMouse || hasPointingStick; }, - // Used to correctly identify when the mouse button has been released. - // crbug.com/686949. - receivedMouseSwapButtonsDown_: false, - /** * @param {!settings.Route} route * @param {settings.Route} oldRoute @@ -129,26 +142,6 @@ return showMouseSection && hasTouchpad ? 'subsection' : ''; }, - /** @private */ - onMouseSwapButtonsDown_() { - this.receivedMouseSwapButtonsDown_ = true; - }, - - /** @private */ - onMouseSwapButtonsUp_() { - this.receivedMouseSwapButtonsDown_ = false; - /** @type {!SettingsToggleButtonElement} */ (this.$.mouseSwapButton) - .sendPrefChange(); - }, - - /** @private */ - onMouseSwapButtonsChange_() { - if (!this.receivedMouseSwapButtonsDown_) { - /** @type {!SettingsToggleButtonElement} */ (this.$.mouseSwapButton) - .sendPrefChange(); - } - }, - /** * @param {!Event} event * @private
diff --git a/chrome/browser/sharing/click_to_call/phone_number_regex.cc b/chrome/browser/sharing/click_to_call/phone_number_regex.cc index a589248..a3e8dbb 100644 --- a/chrome/browser/sharing/click_to_call/phone_number_regex.cc +++ b/chrome/browser/sharing/click_to_call/phone_number_regex.cc
@@ -8,7 +8,6 @@ #include "base/bind.h" #include "base/feature_list.h" -#include "base/metrics/histogram_macros.h" #include "base/task/post_task.h" #include "base/task/thread_pool.h" #include "base/time/time.h" @@ -26,7 +25,6 @@ R"((?:^|\p{Z})((?:\(?\+[0-9]+\)?)?(?:[.\p{Z}\-(]?[0-9][\p{Z}\-)]?){8,}))"; void PrecompilePhoneNumberRegexes() { - SCOPED_UMA_HISTOGRAM_TIMER("Sharing.ClickToCallPhoneNumberPrecompileTime"); static const char kExampleInput[] = "+01(2)34-5678 9012"; std::string parsed;
diff --git a/chrome/browser/speech/speech_recognition_service_browsertest.cc b/chrome/browser/speech/speech_recognition_service_browsertest.cc new file mode 100644 index 0000000..0fc448e --- /dev/null +++ b/chrome/browser/speech/speech_recognition_service_browsertest.cc
@@ -0,0 +1,179 @@ +// Copyright 2020 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/files/file_util.h" +#include "base/test/scoped_feature_list.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/profiles/profile_manager.h" +#include "chrome/browser/speech/speech_recognition_service.h" +#include "chrome/browser/ui/browser.h" +#include "chrome/common/pref_names.h" +#include "chrome/test/base/in_process_browser_test.h" +#include "components/prefs/pref_service.h" +#include "content/public/test/browser_test.h" +#include "media/audio/wav_audio_handler.h" +#include "media/base/media_switches.h" +#include "media/mojo/mojom/media_types.mojom.h" +#include "media/mojo/mojom/speech_recognition_service.mojom.h" +#include "sandbox/policy/switches.h" + +namespace speech { + +constexpr base::FilePath::CharType kSodaResourcesDir[] = + FILE_PATH_LITERAL("third_party/soda/resources"); + +constexpr base::FilePath::CharType kSodaLanguagePackRelativePath[] = + FILE_PATH_LITERAL("en_us"); + +constexpr base::FilePath::CharType kSodaTestAudioRelativePath[] = + FILE_PATH_LITERAL("hey_google.wav"); + +constexpr int kExpectedChannelCount = 1; + +constexpr base::FilePath::CharType kSodaBinaryRelativePath[] = + FILE_PATH_LITERAL("libsoda_for_testing.so"); + +class SpeechRecognitionServiceTest + : public InProcessBrowserTest, + public media::mojom::SpeechRecognitionRecognizerClient { + public: + SpeechRecognitionServiceTest() { + scoped_feature_list_.InitWithFeatures( + {media::kLiveCaption, media::kUseSodaForLiveCaption}, {}); + } + ~SpeechRecognitionServiceTest() override = default; + + // media::mojom::SpeechRecognitionRecognizerClient + void OnSpeechRecognitionRecognitionEvent( + media::mojom::SpeechRecognitionResultPtr result) override; + + void SetUpCommandLine(base::CommandLine* command_line) override { + // Required for the utility process to access the directory containing the + // test files. + command_line->AppendSwitch(sandbox::policy::switches::kNoSandbox); + } + + protected: + void LaunchService(); + base::test::ScopedFeatureList scoped_feature_list_; + mojo::Remote<media::mojom::SpeechRecognitionContext> + speech_recognition_context_; + + mojo::Remote<media::mojom::SpeechRecognitionRecognizer> + speech_recognition_recognizer_; + + mojo::Receiver<media::mojom::SpeechRecognitionRecognizerClient> + speech_recognition_client_receiver_{this}; + + std::vector<std::string> recognition_results_; + + DISALLOW_COPY_AND_ASSIGN(SpeechRecognitionServiceTest); +}; + +void SpeechRecognitionServiceTest::OnSpeechRecognitionRecognitionEvent( + media::mojom::SpeechRecognitionResultPtr result) { + recognition_results_.push_back(std::move(result->transcription)); +} + +void SpeechRecognitionServiceTest::LaunchService() { + // Launch the Speech Recognition service. + auto* browser_context = + static_cast<content::BrowserContext*>(browser()->profile()); + auto* service = new SpeechRecognitionService(browser_context); + + mojo::PendingReceiver<media::mojom::SpeechRecognitionContext> + speech_recognition_context_receiver = + speech_recognition_context_.BindNewPipeAndPassReceiver(); + service->Create(std::move(speech_recognition_context_receiver)); + + mojo::PendingReceiver<media::mojom::SpeechRecognitionRecognizer> + pending_recognizer_receiver = + speech_recognition_recognizer_.BindNewPipeAndPassReceiver(); + + bool is_multichannel_supported = true; + auto run_loop = std::make_unique<base::RunLoop>(); + // Bind the recognizer pipes used to send audio and receive results. + speech_recognition_context_->BindRecognizer( + std::move(pending_recognizer_receiver), + speech_recognition_client_receiver_.BindNewPipeAndPassRemote(), + base::BindOnce( + [](bool* p_is_multichannel_supported, base::RunLoop* run_loop, + bool is_multichannel_supported) { + *p_is_multichannel_supported = is_multichannel_supported; + run_loop->Quit(); + }, + &is_multichannel_supported, run_loop.get())); + + run_loop->Run(); + ASSERT_TRUE(is_multichannel_supported); +} + +IN_PROC_BROWSER_TEST_F(SpeechRecognitionServiceTest, RecognizePhrase) { + ProfileManager::GetActiveUserProfile()->GetPrefs()->SetFilePath( + prefs::kSodaBinaryPath, + base::FilePath(kSodaResourcesDir).Append(kSodaBinaryRelativePath)); + ProfileManager::GetActiveUserProfile()->GetPrefs()->SetFilePath( + prefs::kSodaEnUsConfigPath, + base::FilePath(kSodaResourcesDir).Append(kSodaLanguagePackRelativePath)); + LaunchService(); + + std::string buffer; + auto audio_file = base::FilePath(kSodaResourcesDir) + .Append(base::FilePath(kSodaTestAudioRelativePath)); + { + base::ScopedAllowBlockingForTesting allow_blocking; + ASSERT_TRUE(base::PathExists(audio_file)); + ASSERT_TRUE(base::ReadFileToString(audio_file, &buffer)); + } + + auto handler = media::WavAudioHandler::Create(buffer); + ASSERT_TRUE(handler.get()); + ASSERT_EQ(handler->num_channels(), kExpectedChannelCount); + + auto bus = + media::AudioBus::Create(kExpectedChannelCount, handler->total_frames()); + + size_t bytes_written = 0u; + ASSERT_TRUE(handler->CopyTo(bus.get(), 0, &bytes_written)); + + std::vector<int16_t> audio_data(bus->frames()); + bus->ToInterleaved<media::SignedInt16SampleTypeTraits>(bus->frames(), + audio_data.data()); + + constexpr size_t kMaxChunkSize = 1024; + constexpr int kReplayAudioCount = 2; + for (int i = 0; i < kReplayAudioCount; i++) { + int chunk_start = 0; + // Upload chunks of 1024 frames at a time. + while (chunk_start < static_cast<int>(audio_data.size())) { + int chunk_size = kMaxChunkSize < audio_data.size() - chunk_start + ? kMaxChunkSize + : audio_data.size() - chunk_start; + + auto signed_buffer = media::mojom::AudioDataS16::New(); + signed_buffer->channel_count = kExpectedChannelCount; + signed_buffer->frame_count = chunk_size; + signed_buffer->sample_rate = handler->sample_rate(); + for (int i = 0; i < chunk_size; i++) { + signed_buffer->data.push_back(audio_data[chunk_start + i]); + } + + speech_recognition_recognizer_->SendAudioToSpeechRecognitionService( + std::move(signed_buffer)); + chunk_start += chunk_size; + + // Sleep for 20ms to simulate real-time audio. SODA requires audio + // streaming in order to return events. + usleep(20000); + } + } + + base::RunLoop().RunUntilIdle(); + // Sleep for 50ms to ensure SODA has returned real-time results. + usleep(50000); + ASSERT_GT(static_cast<int>(recognition_results_.size()), kReplayAudioCount); + ASSERT_EQ(recognition_results_.back(), "Hey Google Hey Google"); +} + +} // namespace speech
diff --git a/chrome/browser/ssl/sct_reporting_service_browsertest.cc b/chrome/browser/ssl/sct_reporting_service_browsertest.cc index 16ce472e..1030c0a 100644 --- a/chrome/browser/ssl/sct_reporting_service_browsertest.cc +++ b/chrome/browser/ssl/sct_reporting_service_browsertest.cc
@@ -230,8 +230,9 @@ // Tests that reports are still sent for opted-in profiles after the network // service crashes and is restarted. +// Disabled due to high flake rate; see https://crbug.com/1131803. IN_PROC_BROWSER_TEST_F(SCTReportingServiceBrowserTest, - ReportsSentAfterNetworkServiceRestart) { + DISABLED_ReportsSentAfterNetworkServiceRestart) { // This test is only applicable to out-of-process network service because it // tests what happens when the network service crashes and restarts. if (content::IsInProcessNetworkService()) {
diff --git a/chrome/browser/ui/app_list/app_service/app_service_app_model_builder_unittest.cc b/chrome/browser/ui/app_list/app_service/app_service_app_model_builder_unittest.cc index beeb50f..38021d5 100644 --- a/chrome/browser/ui/app_list/app_service/app_service_app_model_builder_unittest.cc +++ b/chrome/browser/ui/app_list/app_service/app_service_app_model_builder_unittest.cc
@@ -35,6 +35,7 @@ #include "chrome/browser/ui/app_list/chrome_app_list_item.h" #include "chrome/browser/ui/app_list/icon_standardizer.h" #include "chrome/browser/ui/app_list/internal_app/internal_app_metadata.h" +#include "chrome/browser/ui/app_list/md_icon_normalizer.h" #include "chrome/browser/ui/app_list/test/fake_app_list_model_updater.h" #include "chrome/browser/ui/app_list/test/test_app_list_controller_delegate.h" #include "chrome/browser/ui/web_applications/test/web_app_browsertest_util.h" @@ -281,8 +282,15 @@ &output_image_skia, run_loop.QuitClosure())); run_loop.Run(); - if (base::FeatureList::IsEnabled(features::kAppServiceAdaptiveIcon)) + if (base::FeatureList::IsEnabled(features::kAppServiceAdaptiveIcon)) { output_image_skia = app_list::CreateStandardIconImage(output_image_skia); + } else { + extensions::ChromeAppIcon::ApplyEffects( + size_in_dip, + base::BindRepeating(&app_list::MaybeResizeAndPadIconForMd), + true /* app_launchable */, false /* from_bookmark */, + extensions::ChromeAppIcon::Badge::kNone, &output_image_skia); + } } void GenerateExtensionAppCompressedIcon(const std::string app_id, @@ -602,7 +610,7 @@ apps::IconEffects icon_effects = (base::FeatureList::IsEnabled(features::kAppServiceAdaptiveIcon)) ? apps::IconEffects::kCrOsStandardIcon - : apps::IconEffects::kNone; + : apps::IconEffects::kResizeAndPad; base::RunLoop run_loop; apps::mojom::IconValuePtr dst_icon;
diff --git a/chrome/browser/ui/app_list/search/app_service_app_result.cc b/chrome/browser/ui/app_list/search/app_service_app_result.cc index 5c2e9cd8..1fd00ad 100644 --- a/chrome/browser/ui/app_list/search/app_service_app_result.cc +++ b/chrome/browser/ui/app_list/search/app_service_app_result.cc
@@ -172,6 +172,7 @@ base::UserMetricsAction("ReleaseNotes.SuggestionChipLaunched")); proxy->LaunchAppWithUrl(app_id(), event_flags, query_url().value(), launch_source, controller()->GetAppListDisplayId()); + chromeos::ReleaseNotesStorage(profile()).StopShowingSuggestionChip(); return; }
diff --git a/chrome/browser/ui/ash/holding_space/holding_space_ui_browsertest.cc b/chrome/browser/ui/ash/holding_space/holding_space_ui_browsertest.cc index 2d07299..b3c7bf5 100644 --- a/chrome/browser/ui/ash/holding_space/holding_space_ui_browsertest.cc +++ b/chrome/browser/ui/ash/holding_space/holding_space_ui_browsertest.cc
@@ -11,7 +11,6 @@ #include "ash/public/cpp/holding_space/holding_space_model_observer.h" #include "base/scoped_observer.h" #include "base/test/bind_test_util.h" -#include "chrome/browser/ui/browser_window.h" #include "content/public/test/browser_test.h" #include "testing/gmock/include/gmock/gmock.h" #include "ui/aura/window.h" @@ -267,14 +266,6 @@ // Otherwise the screenshot will be taken using the `ChromeScreenshotGrabber`. PressAndReleaseKey(ui::VKEY_MEDIA_LAUNCH_APP1, ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN); - // Move the mouse over to the browser window. The reason for that is with - // `features::kCaptureMode` enabled, the new capture mode implementation will - // not automatically capture the topmost window unless the mouse is hovered - // above it. - aura::Window* browser_window = browser()->window()->GetNativeWindow(); - ui::test::EventGenerator event_generator(browser_window->GetRootWindow()); - event_generator.MoveMouseTo( - browser_window->GetBoundsInScreen().CenterPoint()); PressAndReleaseKey(ui::VKEY_RETURN); // Bind an observer to watch for updates to the holding space model.
diff --git a/chrome/browser/ui/content_settings/content_setting_image_model.cc b/chrome/browser/ui/content_settings/content_setting_image_model.cc index b40309b..0c230099 100644 --- a/chrome/browser/ui/content_settings/content_setting_image_model.cc +++ b/chrome/browser/ui/content_settings/content_setting_image_model.cc
@@ -941,16 +941,11 @@ // Show promo the first time a quiet prompt is shown to the user. set_should_show_promo( QuietNotificationPermissionUiState::ShouldShowPromo(profile)); - using QuietUiReason = permissions::PermissionRequestManager::QuietUiReason; - switch (manager->ReasonForUsingQuietUi()) { - case QuietUiReason::kEnabledInPrefs: - set_explanatory_string_id(IDS_NOTIFICATIONS_OFF_EXPLANATORY_TEXT); - break; - case QuietUiReason::kTriggeredByCrowdDeny: - case QuietUiReason::kTriggeredDueToAbusiveRequests: - case QuietUiReason::kTriggeredDueToAbusiveContent: - set_explanatory_string_id(0); - break; + if (permissions::NotificationPermissionUiSelector::ShouldSuppressAnimation( + manager->ReasonForUsingQuietUi())) { + set_explanatory_string_id(0); + } else { + set_explanatory_string_id(IDS_NOTIFICATIONS_OFF_EXPLANATORY_TEXT); } return true; }
diff --git a/chrome/browser/ui/views/permission_bubble/permission_prompt_impl.cc b/chrome/browser/ui/views/permission_bubble/permission_prompt_impl.cc index 999223a..7d5afdb7 100644 --- a/chrome/browser/ui/views/permission_bubble/permission_prompt_impl.cc +++ b/chrome/browser/ui/views/permission_bubble/permission_prompt_impl.cc
@@ -13,8 +13,10 @@ #include "chrome/browser/ui/ui_features.h" #include "chrome/browser/ui/views/frame/browser_view.h" #include "chrome/browser/ui/views/permission_bubble/permission_prompt_bubble_view.h" +#include "components/permissions/notification_permission_ui_selector.h" #include "components/permissions/permission_request.h" #include "components/permissions/permission_request_manager.h" +#include "components/permissions/permission_uma_util.h" #include "content/public/browser/web_contents.h" #include "ui/views/bubble/bubble_frame_view.h" @@ -111,3 +113,23 @@ return permissions::PermissionPrompt::TabSwitchingBehavior:: kDestroyPromptButKeepRequestPending; } + +permissions::PermissionPromptDisposition +PermissionPromptImpl::GetPromptDisposition() const { + switch (prompt_style_) { + case PromptStyle::kBubble: + return permissions::PermissionPromptDisposition::ANCHORED_BUBBLE; + case PromptStyle::kChip: + return permissions::PermissionPromptDisposition::LOCATION_BAR_LEFT_CHIP; + case PromptStyle::kQuiet: { + permissions::PermissionRequestManager* manager = + permissions::PermissionRequestManager::FromWebContents(web_contents_); + return permissions::NotificationPermissionUiSelector:: + ShouldSuppressAnimation(manager->ReasonForUsingQuietUi()) + ? permissions::PermissionPromptDisposition:: + LOCATION_BAR_RIGHT_STATIC_ICON + : permissions::PermissionPromptDisposition:: + LOCATION_BAR_RIGHT_ANIMATED_ICON; + } + } +}
diff --git a/chrome/browser/ui/views/permission_bubble/permission_prompt_impl.h b/chrome/browser/ui/views/permission_bubble/permission_prompt_impl.h index 9b8926c..fdebbb51 100644 --- a/chrome/browser/ui/views/permission_bubble/permission_prompt_impl.h +++ b/chrome/browser/ui/views/permission_bubble/permission_prompt_impl.h
@@ -30,6 +30,8 @@ // permissions::PermissionPrompt: void UpdateAnchorPosition() override; TabSwitchingBehavior GetTabSwitchingBehavior() override; + permissions::PermissionPromptDisposition GetPromptDisposition() + const override; PermissionPromptBubbleView* prompt_bubble_for_testing() { return prompt_bubble_;
diff --git a/chrome/browser/ui/views/sharing/click_to_call_browsertest.cc b/chrome/browser/ui/views/sharing/click_to_call_browsertest.cc index 001b21a..e895d15 100644 --- a/chrome/browser/ui/views/sharing/click_to_call_browsertest.cc +++ b/chrome/browser/ui/views/sharing/click_to_call_browsertest.cc
@@ -86,13 +86,7 @@ base::HistogramTester::CountsMap GetTotalHistogramCounts( const base::HistogramTester& histograms) { - base::HistogramTester::CountsMap counts = - histograms.GetTotalCountsForPrefix(HistogramName("")); - // PhoneNumberPrecompileTime will be logged 15 seconds after startup but - // we want to ignore it in these browser tests as we don't know if the - // test takes more or less time than that. - counts.erase(HistogramName("PhoneNumberPrecompileTime")); - return counts; + return histograms.GetTotalCountsForPrefix(HistogramName("")); } };
diff --git a/chrome/browser/ui/views/tabs/tab_drag_controller.cc b/chrome/browser/ui/views/tabs/tab_drag_controller.cc index 9f394def..1b6caf4 100644 --- a/chrome/browser/ui/views/tabs/tab_drag_controller.cc +++ b/chrome/browser/ui/views/tabs/tab_drag_controller.cc
@@ -1216,6 +1216,9 @@ if (drag_data_[i].pinned) add_types |= TabStripModel::ADD_PINNED; + // We should have owned_contents here, this CHECK is used to gather data + // for https://crbug.com/677806. + CHECK(drag_data_[i].owned_contents); attached_context_->GetTabStripModel()->InsertWebContentsAt( index + i - first_tab_index(), std::move(drag_data_[i].owned_contents), add_types, group_); @@ -1755,6 +1758,9 @@ std::vector<TabStripModelDelegate::NewStripContents> contentses; for (size_t i = 0; i < drag_data_.size(); ++i) { TabStripModelDelegate::NewStripContents item; + // We should have owned_contents here, this CHECK is used to gather data + // for https://crbug.com/677806. + CHECK(drag_data_[i].owned_contents); item.web_contents = std::move(drag_data_[i].owned_contents); item.add_types = drag_data_[i].pinned ? TabStripModel::ADD_PINNED : TabStripModel::ADD_NONE;
diff --git a/chrome/browser/ui/webui/chromeos/multidevice_internals/multidevice_internals_phone_hub_handler.cc b/chrome/browser/ui/webui/chromeos/multidevice_internals/multidevice_internals_phone_hub_handler.cc index 2ebe801..f1a662bd 100644 --- a/chrome/browser/ui/webui/chromeos/multidevice_internals/multidevice_internals_phone_hub_handler.cc +++ b/chrome/browser/ui/webui/chromeos/multidevice_internals/multidevice_internals_phone_hub_handler.cc
@@ -247,12 +247,12 @@ } void MultidevicePhoneHubHandler::OnPhoneRingingStateChanged() { - // TODO(jimmyxgong): Change to casting the enum TBA to int. - bool is_ringing = fake_phone_hub_manager_->fake_find_my_device_controller() - ->IsPhoneRinging(); - int status_as_int = is_ringing ? 2 : 1; + phonehub::FindMyDeviceController::Status ringing_status = + fake_phone_hub_manager_->fake_find_my_device_controller() + ->GetPhoneRingingStatus(); + FireWebUIListener("find-my-device-status-changed", - base::Value(status_as_int)); + base::Value(static_cast<int>(ringing_status))); } void MultidevicePhoneHubHandler::OnTetherStatusChanged() {
diff --git a/chrome/browser/ui/webui/settings/chromeos/device_section.cc b/chrome/browser/ui/webui/settings/chromeos/device_section.cc index 6e180aae..b9688248 100644 --- a/chrome/browser/ui/webui/settings/chromeos/device_section.cc +++ b/chrome/browser/ui/webui/settings/chromeos/device_section.cc
@@ -1118,6 +1118,9 @@ {"mouseScrollSpeed", IDS_SETTINGS_MOUSE_SCROLL_SPEED_LABEL}, {"mouseSpeed", IDS_SETTINGS_MOUSE_SPEED_LABEL}, {"mouseSwapButtons", IDS_SETTINGS_MOUSE_SWAP_BUTTONS_LABEL}, + {"primaryMouseButtonLeft", IDS_SETTINGS_PRIMARY_MOUSE_BUTTON_LEFT_LABEL}, + {"primaryMouseButtonRight", + IDS_SETTINGS_PRIMARY_MOUSE_BUTTON_RIGHT_LABEL}, {"mouseReverseScroll", IDS_SETTINGS_MOUSE_REVERSE_SCROLL_LABEL}, {"mouseAccelerationLabel", IDS_SETTINGS_MOUSE_ACCELERATION_LABEL}, {"mouseScrollAccelerationLabel",
diff --git a/chrome/browser/util/android/java/src/org/chromium/chrome/browser/util/KeyNavigationUtil.java b/chrome/browser/util/android/java/src/org/chromium/chrome/browser/util/KeyNavigationUtil.java index 5fb8057..d119325 100644 --- a/chrome/browser/util/android/java/src/org/chromium/chrome/browser/util/KeyNavigationUtil.java +++ b/chrome/browser/util/android/java/src/org/chromium/chrome/browser/util/KeyNavigationUtil.java
@@ -73,6 +73,15 @@ } /** + * Checks whether the given event is any DPAD or NUMPAD direction. + * @param event Event to be checked. + * @return Whether the event should be processed as any of navigation direction. + */ + public static boolean isGoAnyDirection(KeyEvent event) { + return isGoDown(event) || isGoUp(event) || isGoLeft(event) || isGoRight(event); + } + + /** * Checks whether the given event is any of ENTER or NUMPAD ENTER. * @param event Event to be checked. * @return Whether the event should be processed as ENTER.
diff --git a/chrome/browser/web_applications/BUILD.gn b/chrome/browser/web_applications/BUILD.gn index 3b105a6..4cb5243 100644 --- a/chrome/browser/web_applications/BUILD.gn +++ b/chrome/browser/web_applications/BUILD.gn
@@ -18,6 +18,7 @@ sources = [ "daily_metrics_helper.cc", "daily_metrics_helper.h", + "extension_status_utils.h", "manifest_update_manager.cc", "manifest_update_manager.h", "manifest_update_task.cc",
diff --git a/chrome/browser/web_applications/extension_status_utils.h b/chrome/browser/web_applications/extension_status_utils.h new file mode 100644 index 0000000..4b73cff2 --- /dev/null +++ b/chrome/browser/web_applications/extension_status_utils.h
@@ -0,0 +1,31 @@ +// Copyright 2020 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 CHROME_BROWSER_WEB_APPLICATIONS_EXTENSION_STATUS_UTILS_H_ +#define CHROME_BROWSER_WEB_APPLICATIONS_EXTENSION_STATUS_UTILS_H_ + +#include <string> + +namespace content { +class BrowserContext; +} + +namespace extensions { + +bool IsExtensionBlockedByPolicy(content::BrowserContext* context, + const std::string& extension_id); + +// Returns whether the extension with |extension_id| is installed regardless of +// disabled/blocked/terminated status. +bool IsExtensionInstalled(content::BrowserContext* context, + const std::string& extension_id); + +// Returns whether the user has uninstalled an externally installed extension +// with |extension_id|. +bool IsExternalExtensionUninstalled(content::BrowserContext* context, + const std::string& extension_id); + +} // namespace extensions + +#endif // CHROME_BROWSER_WEB_APPLICATIONS_EXTENSION_STATUS_UTILS_H_
diff --git a/chrome/browser/web_applications/extensions/BUILD.gn b/chrome/browser/web_applications/extensions/BUILD.gn index 83aaf79..12f0ccb 100644 --- a/chrome/browser/web_applications/extensions/BUILD.gn +++ b/chrome/browser/web_applications/extensions/BUILD.gn
@@ -31,6 +31,7 @@ "bookmark_app_shortcut_manager.h", "bookmark_app_util.cc", "bookmark_app_util.h", + "extension_status_utils.cc", "web_app_extension_shortcut.cc", "web_app_extension_shortcut.h", "web_app_extension_shortcut_mac.h",
diff --git a/chrome/browser/web_applications/extensions/extension_status_utils.cc b/chrome/browser/web_applications/extensions/extension_status_utils.cc new file mode 100644 index 0000000..bd2a37e --- /dev/null +++ b/chrome/browser/web_applications/extensions/extension_status_utils.cc
@@ -0,0 +1,45 @@ +// Copyright 2020 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 "chrome/browser/web_applications/extension_status_utils.h" +#include "chrome/browser/extensions/extension_management.h" +#include "extensions/browser/extension_prefs.h" +#include "extensions/browser/extension_registry.h" +#include "extensions/browser/extension_system.h" + +namespace extensions { + +bool IsExtensionBlockedByPolicy(content::BrowserContext* context, + const std::string& extension_id) { + auto* registry = ExtensionRegistry::Get(context); + // May be nullptr in unit tests. + if (!registry) + return false; + + const Extension* extension = registry->GetInstalledExtension(extension_id); + ExtensionManagement* management = + ExtensionManagementFactory::GetForBrowserContext(context); + ExtensionManagement::InstallationMode mode = + extension ? management->GetInstallationMode(extension) + : management->GetInstallationMode(extension_id, + /*update_url=*/std::string()); + return mode == ExtensionManagement::INSTALLATION_BLOCKED || + mode == ExtensionManagement::INSTALLATION_REMOVED; +} + +bool IsExtensionInstalled(content::BrowserContext* context, + const std::string& extension_id) { + auto* registry = ExtensionRegistry::Get(context); + // May be nullptr in unit tests. + return registry && registry->GetInstalledExtension(extension_id); +} + +bool IsExternalExtensionUninstalled(content::BrowserContext* context, + const std::string& extension_id) { + auto* prefs = ExtensionPrefs::Get(context); + // May be nullptr in unit tests. + return prefs && prefs->IsExternalExtensionUninstalled(extension_id); +} + +} // namespace extensions
diff --git a/chrome/browser/web_applications/external_web_app_manager.cc b/chrome/browser/web_applications/external_web_app_manager.cc index bcb899a..7c43a3e 100644 --- a/chrome/browser/web_applications/external_web_app_manager.cc +++ b/chrome/browser/web_applications/external_web_app_manager.cc
@@ -31,6 +31,7 @@ #include "chrome/browser/web_applications/components/pending_app_manager.h" #include "chrome/browser/web_applications/components/web_app_constants.h" #include "chrome/browser/web_applications/components/web_app_install_utils.h" +#include "chrome/browser/web_applications/extension_status_utils.h" #include "chrome/browser/web_applications/external_web_app_utils.h" #include "chrome/browser/web_applications/preinstalled_web_apps.h" #include "chrome/common/chrome_features.h" @@ -246,31 +247,35 @@ parsed_configs.options_list.push_back(std::move(options)); parsed_configs.disabled_count += preinstalled_web_apps.disabled_count; - // Save this as we may remove apps due to user uninstall (not the same as - // being disabled). + // Track this separately as we may remove apps due to user uninstall (not the + // same as being disabled). int enabled_count = parsed_configs.options_list.size(); - // Remove web apps whose replace target was uninstalled. - if (extensions::ExtensionSystem::Get(profile_)) { - auto* extension_prefs = extensions::ExtensionPrefs::Get(profile_); - auto* extension_registry = extensions::ExtensionRegistry::Get(profile_); - - base::EraseIf( - parsed_configs.options_list, - [&](const ExternalInstallOptions& options) { - for (const AppId& app_id : options.uninstall_and_replace) { - if (extension_registry->GetInstalledExtension(app_id)) - return false; + base::EraseIf( + parsed_configs.options_list, [&](const ExternalInstallOptions& options) { + // Remove if any blocked by admin policy. + for (const AppId& app_id : options.uninstall_and_replace) { + if (extensions::IsExtensionBlockedByPolicy(profile_, app_id)) { + ++parsed_configs.disabled_count; + --enabled_count; + return true; } + } - for (const AppId& app_id : options.uninstall_and_replace) { - if (extension_prefs->IsExternalExtensionUninstalled(app_id)) - return true; - } + // Keep if any installed. + for (const AppId& app_id : options.uninstall_and_replace) { + if (extensions::IsExtensionInstalled(profile_, app_id)) + return false; + } - return false; - }); - } + // Remove if any previously uninstalled. + for (const AppId& app_id : options.uninstall_and_replace) { + if (extensions::IsExternalExtensionUninstalled(profile_, app_id)) + return true; + } + + return false; + }); base::UmaHistogramCounts100(ExternalWebAppManager::kHistogramEnabledCount, enabled_count);
diff --git a/chrome/browser/web_applications/external_web_app_manager_unittest.cc b/chrome/browser/web_applications/external_web_app_manager_unittest.cc index 9486376..a062612 100644 --- a/chrome/browser/web_applications/external_web_app_manager_unittest.cc +++ b/chrome/browser/web_applications/external_web_app_manager_unittest.cc
@@ -19,13 +19,16 @@ #include "base/test/metrics/histogram_tester.h" #include "base/test/scoped_feature_list.h" #include "base/test/scoped_path_override.h" +#include "chrome/browser/extensions/extension_management_test_util.h" #include "chrome/browser/supervised_user/supervised_user_constants.h" #include "chrome/browser/web_applications/components/external_app_install_features.h" #include "chrome/browser/web_applications/components/web_app_constants.h" +#include "chrome/browser/web_applications/preinstalled_web_apps.h" #include "chrome/common/chrome_features.h" #include "chrome/common/chrome_paths.h" #include "chrome/test/base/testing_profile.h" #include "components/account_id/account_id.h" +#include "components/sync_preferences/testing_pref_service_syncable.h" #include "content/public/test/browser_task_environment.h" #include "testing/gtest/include/gtest/gtest.h" #include "url/gurl.h" @@ -186,6 +189,53 @@ DISALLOW_COPY_AND_ASSIGN(ExternalWebAppManagerTest); }; +TEST_F(ExternalWebAppManagerTest, ReplacementExtensionBlockedByPolicy) { + using PolicyUpdater = extensions::ExtensionManagementPrefUpdater< + sync_preferences::TestingPrefServiceSyncable>; + auto test_profile = std::make_unique<TestingProfile>(); + sync_preferences::TestingPrefServiceSyncable* prefs = + test_profile->GetTestingPrefService(); + + ScopedTestingPreinstalledAppData scoped_preinstalled_apps; + GURL install_url("https://test.app"); + constexpr char kExtensionId[] = "abcdefghijklmnopabcdefghijklmnop"; + scoped_preinstalled_apps.apps.push_back({ + .install_url = install_url, + .feature_name = nullptr, + .app_id_to_replace = kExtensionId, + }); + + auto expect_present = [&]() { + std::vector<ExternalInstallOptions> options_list = + LoadApps(/*test_dir=*/"", test_profile.get()); + ASSERT_EQ(options_list.size(), 1u); + EXPECT_EQ(options_list[0].install_url, install_url); + }; + + auto expect_not_present = [&]() { + std::vector<ExternalInstallOptions> options_list = + LoadApps(/*test_dir=*/"", test_profile.get()); + ASSERT_EQ(options_list.size(), 0u); + }; + + expect_present(); + + PolicyUpdater(prefs).SetBlocklistedByDefault(false); + expect_present(); + + PolicyUpdater(prefs).SetBlocklistedByDefault(true); + expect_not_present(); + + PolicyUpdater(prefs).SetIndividualExtensionInstallationAllowed(kExtensionId, + true); + expect_present(); + + PolicyUpdater(prefs).SetBlocklistedByDefault(false); + PolicyUpdater(prefs).SetIndividualExtensionInstallationAllowed(kExtensionId, + false); + expect_not_present(); +} + // Only Chrome OS parses config files. #if defined(OS_CHROMEOS) TEST_F(ExternalWebAppManagerTest, GoodJson) {
diff --git a/chrome/browser/web_applications/preinstalled_web_apps.cc b/chrome/browser/web_applications/preinstalled_web_apps.cc index 5296f0f..ab8d068 100644 --- a/chrome/browser/web_applications/preinstalled_web_apps.cc +++ b/chrome/browser/web_applications/preinstalled_web_apps.cc
@@ -46,7 +46,8 @@ PreinstalledWebApps result; for (const PreinstalledAppData& app_data : GetPreinstalledAppData()) { - if (!IsExternalAppInstallFeatureEnabled(app_data.feature_name)) { + if (app_data.feature_name && + !IsExternalAppInstallFeatureEnabled(app_data.feature_name)) { ++result.disabled_count; continue; }
diff --git a/chrome/browser/xsurface/android/java/src/org/chromium/chrome/browser/xsurface/FeedActionsHandler.java b/chrome/browser/xsurface/android/java/src/org/chromium/chrome/browser/xsurface/FeedActionsHandler.java index 713f2a1..90f980a3 100644 --- a/chrome/browser/xsurface/android/java/src/org/chromium/chrome/browser/xsurface/FeedActionsHandler.java +++ b/chrome/browser/xsurface/android/java/src/org/chromium/chrome/browser/xsurface/FeedActionsHandler.java
@@ -4,6 +4,10 @@ package org.chromium.chrome.browser.xsurface; +import android.view.View; + +import androidx.annotation.Nullable; + import java.util.Map; /** @@ -24,6 +28,13 @@ default void processThereAndBackAgainData(byte[] data) {} /** + * Sends data back to the server when content is clicked and provides the corresponding view + * through |actionSourceView| which can be null. + */ + @Deprecated + default void processThereAndBackAgainData(byte[] data, @Nullable View actionSourceView) {} + + /** * Stores a view FeedAction for eventual upload. 'data' is a serialized FeedAction protobuf * message. */
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt index 047ef7f..b762e601 100644 --- a/chrome/build/linux.pgo.txt +++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@ -chrome-linux-master-1601575120-46ddcdbe4da420985f324196a05692689b215d89.profdata +chrome-linux-master-1601596804-53a4ffa238a2dd27e204a4d6f101c3543de8d4a0.profdata
diff --git a/chrome/common/chrome_features.cc b/chrome/common/chrome_features.cc index 84f07d2..6a4afa2f 100644 --- a/chrome/common/chrome_features.cc +++ b/chrome/common/chrome_features.cc
@@ -66,7 +66,7 @@ #if !defined(OS_ANDROID) // App Service related flags. See components/services/app_service/README.md. const base::Feature kAppServiceAdaptiveIcon{"AppServiceAdaptiveIcon", - base::FEATURE_ENABLED_BY_DEFAULT}; + base::FEATURE_DISABLED_BY_DEFAULT}; const base::Feature kAppServiceExternalProtocol{ "AppServiceExternalProtocol", base::FEATURE_DISABLED_BY_DEFAULT}; const base::Feature kAppServiceIntentHandling{"AppServiceIntentHandling", @@ -379,12 +379,17 @@ // floc id is first computed for a browsing session or is refreshed due to a // long period of time has passed since the last computation. const base::Feature kFlocIdComputedEventLogging{ - "FlocIdComputedEventLogging", base::FEATURE_DISABLED_BY_DEFAULT}; + "FlocIdComputedEventLogging", base::FEATURE_ENABLED_BY_DEFAULT}; // If enabled, a computed floc will be invalidated if it appears in a blocklist. const base::Feature kFlocIdBlocklistFiltering{ "FlocIdBlocklistFiltering", base::FEATURE_DISABLED_BY_DEFAULT}; +// If enabled, the sim-hash floc computed from history will be further encoded +// based on the sorting-lsh. +const base::Feature kFlocIdSortingLshBasedComputation{ + "FlocIdSortingLshBasedComputation", base::FEATURE_DISABLED_BY_DEFAULT}; + // Enables Focus Mode which brings up a PWA-like window look. const base::Feature kFocusMode{"FocusMode", base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/chrome/common/chrome_features.h b/chrome/common/chrome_features.h index 02a63426..4f22f07 100644 --- a/chrome/common/chrome_features.h +++ b/chrome/common/chrome_features.h
@@ -249,6 +249,9 @@ extern const base::Feature kFlocIdBlocklistFiltering; COMPONENT_EXPORT(CHROME_FEATURES) +extern const base::Feature kFlocIdSortingLshBasedComputation; + +COMPONENT_EXPORT(CHROME_FEATURES) extern const base::Feature kFocusMode; #if defined(OS_WIN)
diff --git a/chrome/common/extensions/api/identity.idl b/chrome/common/extensions/api/identity.idl index d8d7e548..e05f91fe 100644 --- a/chrome/common/extensions/api/identity.idl +++ b/chrome/common/extensions/api/identity.idl
@@ -94,6 +94,7 @@ callback GetAccountsCallback = void (AccountInfo[] accounts); callback GetProfileUserInfoCallback = void (ProfileUserInfo userInfo); callback InvalidateAuthTokenCallback = void (); + callback ClearAllCachedAuthTokensCallback = void (); callback LaunchWebAuthFlowCallback = void (optional DOMString responseUrl); interface Functions { @@ -160,6 +161,15 @@ InvalidTokenDetails details, optional InvalidateAuthTokenCallback callback); + // Resets the state of the Identity API: + // - Removes all OAuth2 access tokens from the token cache + // - Removes user's account preferences + // - De-authorizes the user from all auth flows + // + // |callback| : Called when the state has been cleared. + static void clearAllCachedAuthTokens( + ClearAllCachedAuthTokensCallback callback); + // Starts an auth flow at the specified URL. // // This method enables auth flows with non-Google identity
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn index ba2f8309..9771f28 100644 --- a/chrome/test/BUILD.gn +++ b/chrome/test/BUILD.gn
@@ -16,6 +16,7 @@ import("//chrome/browser/page_load_metrics/integration_tests/sources.gni") import("//chrome/common/features.gni") import("//chrome/services/machine_learning/features.gni") +import("//chrome/services/speech/buildflags.gni") import("//chrome/test/base/js2gtest.gni") import("//chrome/test/include_js_tests.gni") import("//chromeos/assistant/assistant.gni") @@ -1590,6 +1591,11 @@ deps += [ "//chrome/app:chrome_dll_resources" ] } + if (enable_soda) { + sources += + [ "../browser/speech/speech_recognition_service_browsertest.cc" ] + } + if (use_ozone) { deps += [ "//ui/ozone" ] }
diff --git a/chrome/test/data/policy/policy_test_cases.json b/chrome/test/data/policy/policy_test_cases.json index ce9809f..586966d 100644 --- a/chrome/test/data/policy/policy_test_cases.json +++ b/chrome/test/data/policy/policy_test_cases.json
@@ -4721,8 +4721,7 @@ }, "EnableDeprecatedWebPlatformFeatures": { - "os": ["win", "linux", "mac", "chromeos"], - "policy_pref_mapping_test": [] + "note": "This policy has been removed, as of M88." }, "TouchVirtualKeyboardEnabled": {
diff --git a/chrome/test/data/webui/settings/chromeos/device_page_tests.js b/chrome/test/data/webui/settings/chromeos/device_page_tests.js index 4f0dd422..4995bea 100644 --- a/chrome/test/data/webui/settings/chromeos/device_page_tests.js +++ b/chrome/test/data/webui/settings/chromeos/device_page_tests.js
@@ -667,8 +667,6 @@ test('mouse', function() { expectLT(0, pointersPage.$$('#mouse').offsetHeight); - expectFalse(pointersPage.$$('#mouse settings-toggle-button').checked); - const slider = assert(pointersPage.$$('#mouse settings-slider')); expectEquals(4, slider.pref.value); MockInteractions.pressAndReleaseKeyOn(
diff --git a/chromeos/components/diagnostics_ui/backend/system_data_provider.cc b/chromeos/components/diagnostics_ui/backend/system_data_provider.cc index ea2f0c46..20e36701 100644 --- a/chromeos/components/diagnostics_ui/backend/system_data_provider.cc +++ b/chromeos/components/diagnostics_ui/backend/system_data_provider.cc
@@ -31,6 +31,7 @@ constexpr int kBatteryHealthRefreshIntervalInSeconds = 60; constexpr int kChargeStatusRefreshIntervalInSeconds = 15; +constexpr int kMemoryUsageRefreshIntervalInSeconds = 10; constexpr int kMilliampsInAnAmp = 1000; void PopulateBoardName(const healthd::SystemInfo& system_info, @@ -128,6 +129,12 @@ out_battery_health.charge_full_design_milliamp_hours; } +void PopulateMemoryUsage(const healthd::MemoryInfo& memory_info, + mojom::MemoryUsage& out_memory_usage) { + out_memory_usage.total_memory_kib = memory_info.total_memory_kib; + out_memory_usage.free_memory_kib = memory_info.free_memory_kib; + out_memory_usage.available_memory_kib = memory_info.available_memory_kib; +} } // namespace SystemDataProvider::SystemDataProvider() { @@ -187,6 +194,20 @@ UpdateBatteryHealth(); } +void SystemDataProvider::ObserveMemoryUsage( + mojo::PendingRemote<mojom::MemoryUsageObserver> observer) { + memory_usage_observers_.Add(std::move(observer)); + + if (!memory_usage_timer_->IsRunning()) { + memory_usage_timer_->Start( + FROM_HERE, + base::TimeDelta::FromSeconds(kMemoryUsageRefreshIntervalInSeconds), + base::BindRepeating(&SystemDataProvider::UpdateMemoryUsage, + base::Unretained(this))); + } + UpdateMemoryUsage(); +} + void SystemDataProvider::PowerChanged( const power_manager::PowerSupplyProperties& proto) { if (battery_charge_status_observers_.empty()) { @@ -211,6 +232,11 @@ battery_health_timer_ = std::move(timer); } +void SystemDataProvider::SetMemoryUsageTimerForTesting( + std::unique_ptr<base::RepeatingTimer> timer) { + memory_usage_timer_ = std::move(timer); +} + void SystemDataProvider::OnSystemInfoProbeResponse( GetSystemInfoCallback callback, healthd::TelemetryInfoPtr info_ptr) { @@ -301,6 +327,15 @@ base::Unretained(this))); } +void SystemDataProvider::UpdateMemoryUsage() { + BindCrosHealthdProbeServiceIfNeccessary(); + + probe_service_->ProbeTelemetryInfo( + {ProbeCategories::kMemory}, + base::BindOnce(&SystemDataProvider::OnMemoryUsageUpdated, + base::Unretained(this))); +} + void SystemDataProvider::OnBatteryChargeStatusUpdated( const base::Optional<PowerSupplyProperties>& power_supply_properties, healthd::TelemetryInfoPtr info_ptr) { @@ -359,6 +394,29 @@ NotifyBatteryHealthObservers(battery_health); } +void SystemDataProvider::OnMemoryUsageUpdated( + healthd::TelemetryInfoPtr info_ptr) { + mojom::MemoryUsagePtr memory_usage = mojom::MemoryUsage::New(); + + if (info_ptr.is_null()) { + LOG(ERROR) << "Null response from croshealthd::ProbeTelemetryInfo."; + NotifyMemoryUsageObservers(memory_usage); + memory_usage_timer_.reset(); + return; + } + + const healthd::MemoryInfo* memory_info = GetMemoryInfo(*info_ptr); + if (memory_info == nullptr) { + LOG(ERROR) << "No MemoryInfo in response from cros_healthd."; + NotifyMemoryUsageObservers(memory_usage); + memory_usage_timer_.reset(); + return; + } + + PopulateMemoryUsage(*memory_info, *memory_usage.get()); + NotifyMemoryUsageObservers(memory_usage); +} + void SystemDataProvider::NotifyBatteryChargeStatusObservers( const mojom::BatteryChargeStatusPtr& battery_charge_status) { for (auto& observer : battery_charge_status_observers_) { @@ -373,6 +431,13 @@ } } +void SystemDataProvider::NotifyMemoryUsageObservers( + const mojom::MemoryUsagePtr& memory_usage) { + for (auto& observer : memory_usage_observers_) { + observer->OnMemoryUsageUpdated(memory_usage.Clone()); + } +} + void SystemDataProvider::BindCrosHealthdProbeServiceIfNeccessary() { if (!probe_service_ || !probe_service_.is_connected()) { cros_healthd::ServiceConnection::GetInstance()->GetProbeService(
diff --git a/chromeos/components/diagnostics_ui/backend/system_data_provider.h b/chromeos/components/diagnostics_ui/backend/system_data_provider.h index 70959957..3b58ba92 100644 --- a/chromeos/components/diagnostics_ui/backend/system_data_provider.h +++ b/chromeos/components/diagnostics_ui/backend/system_data_provider.h
@@ -39,6 +39,8 @@ override; void ObserveBatteryHealth( mojo::PendingRemote<mojom::BatteryHealthObserver> observer) override; + void ObserveMemoryUsage( + mojo::PendingRemote<mojom::MemoryUsageObserver> observer) override; // PowerManagerClient::Observer: void PowerChanged(const power_manager::PowerSupplyProperties& proto) override; @@ -49,6 +51,9 @@ void SetBatteryHealthTimerForTesting( std::unique_ptr<base::RepeatingTimer> timer); + void SetMemoryUsageTimerForTesting( + std::unique_ptr<base::RepeatingTimer> timer); + private: void BindCrosHealthdProbeServiceIfNeccessary(); @@ -66,12 +71,16 @@ void UpdateBatteryHealth(); + void UpdateMemoryUsage(); + void NotifyBatteryChargeStatusObservers( const mojom::BatteryChargeStatusPtr& battery_charge_status); void NotifyBatteryHealthObservers( const mojom::BatteryHealthPtr& battery_health); + void NotifyMemoryUsageObservers(const mojom::MemoryUsagePtr& memory_usage); + void OnBatteryChargeStatusUpdated( const base::Optional<power_manager::PowerSupplyProperties>& power_supply_properties, @@ -79,13 +88,17 @@ void OnBatteryHealthUpdated(cros_healthd::mojom::TelemetryInfoPtr info_ptr); + void OnMemoryUsageUpdated(cros_healthd::mojom::TelemetryInfoPtr info_ptr); + mojo::Remote<cros_healthd::mojom::CrosHealthdProbeService> probe_service_; mojo::RemoteSet<mojom::BatteryChargeStatusObserver> battery_charge_status_observers_; mojo::RemoteSet<mojom::BatteryHealthObserver> battery_health_observers_; + mojo::RemoteSet<mojom::MemoryUsageObserver> memory_usage_observers_; std::unique_ptr<base::RepeatingTimer> battery_charge_status_timer_; std::unique_ptr<base::RepeatingTimer> battery_health_timer_; + std::unique_ptr<base::RepeatingTimer> memory_usage_timer_; }; } // namespace diagnostics
diff --git a/chromeos/components/diagnostics_ui/backend/system_data_provider_unittest.cc b/chromeos/components/diagnostics_ui/backend/system_data_provider_unittest.cc index feef02b..9db7f89 100644 --- a/chromeos/components/diagnostics_ui/backend/system_data_provider_unittest.cc +++ b/chromeos/components/diagnostics_ui/backend/system_data_provider_unittest.cc
@@ -183,7 +183,7 @@ CreateCrosHealthdBatteryInfoResponse(vendor, charge_full_design); SetProbeTelemetryInfoResponse(std::move(battery_info), /*cpu_info=*/nullptr, /*memory_info=*/nullptr, - /*memory_info=*/nullptr); + /*system_info=*/nullptr); } void SetCrosHealthdBatteryChargeStatusResponse(double charge_now, @@ -192,7 +192,7 @@ CreateCrosHealthdBatteryChargeStatusResponse(charge_now, current_now); SetProbeTelemetryInfoResponse(std::move(battery_info), /*cpu_info=*/nullptr, /*memory_info=*/nullptr, - /*memory_info=*/nullptr); + /*system_info=*/nullptr); } void SetCrosHealthdBatteryHealthResponse(double charge_full_now, @@ -203,7 +203,19 @@ charge_full_design, cycle_count); SetProbeTelemetryInfoResponse(std::move(battery_info), /*cpu_info=*/nullptr, /*memory_info=*/nullptr, - /*memory_info=*/nullptr); + /*system_info=*/nullptr); +} + +void SetCrosHealthdMemoryUsageResponse(uint32_t total_memory_kib, + uint32_t free_memory_kib, + uint32_t available_memory_kib) { + cros_healthd::mojom::MemoryInfoPtr memory_info = + cros_healthd::mojom::MemoryInfo::New(total_memory_kib, free_memory_kib, + available_memory_kib, + /*page_faults_since_last_boot=*/0); + SetProbeTelemetryInfoResponse(/*battery_info=*/nullptr, /*cpu_info=*/nullptr, + /*memory_info=*/std::move(memory_info), + /*system_info=*/nullptr); } bool AreValidPowerTimes(int64_t time_to_full, int64_t time_to_empty) { @@ -315,6 +327,15 @@ EXPECT_EQ(expected_battery_wear_percentage, update->battery_wear_percentage); } +void VerifyMemoryUsageResult(const mojom::MemoryUsagePtr& update, + uint32_t expected_total_memory_kib, + uint32_t expected_free_memory_kib, + uint32_t expected_available_memory_kib) { + EXPECT_EQ(expected_total_memory_kib, update->total_memory_kib); + EXPECT_EQ(expected_free_memory_kib, update->free_memory_kib); + EXPECT_EQ(expected_available_memory_kib, update->available_memory_kib); +} + } // namespace struct FakeBatteryChargeStatusObserver @@ -345,6 +366,19 @@ mojo::Receiver<mojom::BatteryHealthObserver> receiver{this}; }; +struct FakeMemoryUsageObserver : public mojom::MemoryUsageObserver { + // mojom::MemoryUsageObserver + void OnMemoryUsageUpdated(mojom::MemoryUsagePtr status_ptr) override { + updates.emplace_back(std::move(status_ptr)); + } + + // Tracks calls to OnMemoryUsageUpdated. Each call adds an element to + // the vector. + std::vector<mojom::MemoryUsagePtr> updates; + + mojo::Receiver<mojom::MemoryUsageObserver> receiver{this}; +}; + class SystemDataProviderTest : public testing::Test { public: SystemDataProviderTest() { @@ -559,5 +593,53 @@ charge_full_design, new_cycle_count); } +TEST_F(SystemDataProviderTest, MemoryUsageObserver) { + // Setup Timer + auto timer = std::make_unique<base::MockRepeatingTimer>(); + auto* timer_ptr = timer.get(); + system_data_provider_->SetMemoryUsageTimerForTesting(std::move(timer)); + + // Setup initial data + const uint32_t total_memory_kib = 10000; + const uint32_t free_memory_kib = 2000; + const uint32_t available_memory_kib = 4000; + + SetCrosHealthdMemoryUsageResponse(total_memory_kib, free_memory_kib, + available_memory_kib); + + // Registering as an observer should trigger one update. + FakeMemoryUsageObserver memory_usage_observer; + system_data_provider_->ObserveMemoryUsage( + memory_usage_observer.receiver.BindNewPipeAndPassRemote()); + base::RunLoop().RunUntilIdle(); + + EXPECT_EQ(1u, memory_usage_observer.updates.size()); + VerifyMemoryUsageResult(memory_usage_observer.updates[0], total_memory_kib, + free_memory_kib, available_memory_kib); + + // Firing the timer should trigger another. + timer_ptr->Fire(); + base::RunLoop().RunUntilIdle(); + + EXPECT_EQ(2u, memory_usage_observer.updates.size()); + VerifyMemoryUsageResult(memory_usage_observer.updates[1], total_memory_kib, + free_memory_kib, available_memory_kib); + + // Updating the information in Croshealthd does not trigger an update until + // the timer fires + const uint32_t new_available_memory_kib = available_memory_kib + 1000; + SetCrosHealthdMemoryUsageResponse(total_memory_kib, free_memory_kib, + new_available_memory_kib); + + EXPECT_EQ(2u, memory_usage_observer.updates.size()); + + timer_ptr->Fire(); + base::RunLoop().RunUntilIdle(); + + EXPECT_EQ(3u, memory_usage_observer.updates.size()); + VerifyMemoryUsageResult(memory_usage_observer.updates[2], total_memory_kib, + free_memory_kib, new_available_memory_kib); +} + } // namespace diagnostics } // namespace chromeos
diff --git a/chromeos/components/diagnostics_ui/mojom/system_data_provider.mojom b/chromeos/components/diagnostics_ui/mojom/system_data_provider.mojom index 1bba4fe5..d31d87c8 100644 --- a/chromeos/components/diagnostics_ui/mojom/system_data_provider.mojom +++ b/chromeos/components/diagnostics_ui/mojom/system_data_provider.mojom
@@ -63,6 +63,13 @@ int8 battery_wear_percentage; }; +// Contains information about the usage of memory on the device. +struct MemoryUsage { + uint32 total_memory_kib; + uint32 available_memory_kib; + uint32 free_memory_kib; +}; + // Implemented by clients that wish to be updated periodically about changes to // the battery charge status. interface BatteryChargeStatusObserver { @@ -82,6 +89,15 @@ OnBatteryHealthUpdated(BatteryHealth battery_health); }; +// Implemented by clients that wish to be updated periodically about the +// memory usage of the device. +interface MemoryUsageObserver { + // OnMemoryUsageUpdated calls can be triggered by either of 2 conditions: + // 1) A MemoryUsageObserver is registered with SystemDataProvider + // 2) A periodic update is sent by SystemDataProvider + OnMemoryUsageUpdated(MemoryUsage memory_usage); +}; + // Provides telemetric information about the system. This API is exposed to the // Diagnostics SWA. interface SystemDataProvider { @@ -98,6 +114,9 @@ ObserveBatteryChargeStatus( pending_remote<BatteryChargeStatusObserver> observer); - // Registers an observer of BatteryHealth inforamation. + // Registers an observer of BatteryHealth information. ObserveBatteryHealth(pending_remote<BatteryHealthObserver> observer); + + // Registers an observer of MemoryUsage information. + ObserveMemoryUsage(pending_remote<MemoryUsageObserver> observer); };
diff --git a/chromeos/components/help_app_ui/help_app_page_handler.cc b/chromeos/components/help_app_ui/help_app_page_handler.cc index 3d6ca46c..8f5adce 100644 --- a/chromeos/components/help_app_ui/help_app_page_handler.cc +++ b/chromeos/components/help_app_ui/help_app_page_handler.cc
@@ -6,13 +6,18 @@ #include <utility> +#include "base/feature_list.h" #include "chromeos/components/help_app_ui/help_app_ui.h" #include "chromeos/components/help_app_ui/help_app_ui_delegate.h" +#include "chromeos/constants/chromeos_features.h" HelpAppPageHandler::HelpAppPageHandler( chromeos::HelpAppUI* help_app_ui, mojo::PendingReceiver<help_app_ui::mojom::PageHandler> receiver) - : receiver_(this, std::move(receiver)), help_app_ui_(help_app_ui) {} + : receiver_(this, std::move(receiver)), + help_app_ui_(help_app_ui), + is_lss_enabled_(base::FeatureList::IsEnabled( + chromeos::features::kHelpAppSearchServiceIntegration)) {} HelpAppPageHandler::~HelpAppPageHandler() = default; @@ -25,3 +30,7 @@ void HelpAppPageHandler::ShowParentalControls() { help_app_ui_->delegate()->ShowParentalControls(); } + +void HelpAppPageHandler::IsLssEnabled(IsLssEnabledCallback callback) { + std::move(callback).Run(is_lss_enabled_); +}
diff --git a/chromeos/components/help_app_ui/help_app_page_handler.h b/chromeos/components/help_app_ui/help_app_page_handler.h index 1eeec71..c71a1b4 100644 --- a/chromeos/components/help_app_ui/help_app_page_handler.h +++ b/chromeos/components/help_app_ui/help_app_page_handler.h
@@ -28,10 +28,12 @@ // help_app_ui::mojom::PageHandler: void OpenFeedbackDialog(OpenFeedbackDialogCallback callback) override; void ShowParentalControls() override; + void IsLssEnabled(IsLssEnabledCallback callback) override; private: mojo::Receiver<help_app_ui::mojom::PageHandler> receiver_; chromeos::HelpAppUI* help_app_ui_; // Owns |this|. + bool is_lss_enabled_; }; #endif // CHROMEOS_COMPONENTS_HELP_APP_UI_HELP_APP_PAGE_HANDLER_H_
diff --git a/chromeos/components/help_app_ui/help_app_ui.mojom b/chromeos/components/help_app_ui/help_app_ui.mojom index 0fa3ecf..95b8e92f5 100644 --- a/chromeos/components/help_app_ui/help_app_ui.mojom +++ b/chromeos/components/help_app_ui/help_app_ui.mojom
@@ -19,4 +19,7 @@ // Opens the parental controls part of OS settings. ShowParentalControls(); + + // Returns true if Local Search Service integration is enabled. + IsLssEnabled() => (bool enabled); };
diff --git a/chromeos/components/help_app_ui/resources/browser_proxy.js b/chromeos/components/help_app_ui/resources/browser_proxy.js index 95ef49c..aa9743bb 100644 --- a/chromeos/components/help_app_ui/resources/browser_proxy.js +++ b/chromeos/components/help_app_ui/resources/browser_proxy.js
@@ -11,16 +11,32 @@ help_app.handler.$.bindNewPipeAndPassReceiver()); // Set up an index remote to talk to Local Search Service. -// TODO(b/166047521): Define and use API to make calls to this from untrusted -// context. +/** @type {!chromeos.localSearchService.mojom.IndexProxyRemote} */ const indexRemote = chromeos.localSearchService.mojom.IndexProxy.getRemote(); const GUEST_ORIGIN = 'chrome-untrusted://help-app'; -const guestFrame = /** @type {!HTMLIFrameElement} */ ( - document.createElement('iframe')); +const guestFrame = + /** @type {!HTMLIFrameElement} */ (document.createElement('iframe')); guestFrame.src = `${GUEST_ORIGIN}${location.pathname}`; document.body.appendChild(guestFrame); +// Cached result whether Local Search Service is enabled. +/** @type {Promise<boolean>} */ +const isLssEnabled = + help_app.handler.isLssEnabled().then(result => result.enabled); + +/** + * @param {string} s + * @return {!mojoBase.mojom.String16Spec} + */ +function toString16(s) { + return /** @type {!mojoBase.mojom.String16Spec} */ ( + {data: Array.from(s, (/** @type {string} */ c) => c.charCodeAt())}); +} +const TITLE_ID = 'title'; +const BODY_ID = 'body'; +const CATEGORY_ID = 'main-category'; + /** * A pipe through which we can send messages to the guest frame. * Use an undefined `target` to find the <iframe> automatically. @@ -28,9 +44,9 @@ * throw exceptions that are handled on the other side of the pipe (in the guest * frame), not on this side. */ -const guestMessagePipe = - new MessagePipe('chrome-untrusted://help-app', /*target=*/ undefined, - /*rethrowErrors=*/ false); +const guestMessagePipe = new MessagePipe( + 'chrome-untrusted://help-app', /*target=*/ undefined, + /*rethrowErrors=*/ false); guestMessagePipe.registerHandler(Message.OPEN_FEEDBACK_DIALOG, () => { return help_app.handler.openFeedbackDialog(); @@ -39,3 +55,89 @@ guestMessagePipe.registerHandler(Message.SHOW_PARENTAL_CONTROLS, () => { help_app.handler.showParentalControls(); }); + +guestMessagePipe.registerHandler( + Message.ADD_OR_UPDATE_SEARCH_INDEX, async (message) => { + if (!(await isLssEnabled)) { + return; + } + const data_from_app = + /** @type {!Array<!helpApp.SearchableItem>} */ (message); + const data_to_send = data_from_app.map(searchable_item => { + const contents = [ + { + id: TITLE_ID, + content: toString16(searchable_item.title), + weight: 1.0, + }, + { + id: BODY_ID, + content: toString16(searchable_item.body), + weight: 0.2, + }, + { + id: CATEGORY_ID, + content: toString16(searchable_item.mainCategoryName), + weight: 0.1, + }, + ]; + return { + id: searchable_item.id, + contents, + locale: searchable_item.locale, + }; + }); + indexRemote.addOrUpdate(data_to_send); + }); + +guestMessagePipe.registerHandler(Message.CLEAR_SEARCH_INDEX, async () => { + if (!(await isLssEnabled)) { + return; + } + // TODO(b/166047521): Clear the index when that method is available. +}); + +guestMessagePipe.registerHandler( + Message.FIND_IN_SEARCH_INDEX, async (message) => { + if (!(await isLssEnabled)) { + return {results: null}; + } + const response = await indexRemote.find( + toString16((/** @type {{query: string}} */ (message)).query), + /*max_results=*/ 100); + if (response.status !== + chromeos.localSearchService.mojom.ResponseStatus.kSuccess || + !response.results) { + return {results: null}; + } + const search_results = + /** @type {!Array<!chromeos.localSearchService.mojom.Result>} */ ( + response.results); + // Sort results by decreasing score. + search_results.sort((a, b) => b.score - a.score); + /** @type {!Array<!helpApp.SearchResult>} */ + const results = search_results.map(result => { + /** @type {!Array<!helpApp.Position>} */ + const titlePositions = []; + /** @type {!Array<!helpApp.Position>} */ + const bodyPositions = []; + for (const position of result.positions) { + if (position.contentId === TITLE_ID) { + titlePositions.push( + {length: position.length, start: position.start}); + } else if (position.contentId === BODY_ID) { + bodyPositions.push( + {length: position.length, start: position.start}); + } + } + // Sort positions by start index. + titlePositions.sort((a, b) => a.start - b.start); + bodyPositions.sort((a, b) => a.start - b.start); + return { + id: result.id, + titlePositions, + bodyPositions, + }; + }); + return {results}; + });
diff --git a/chromeos/components/help_app_ui/resources/help_app.externs.js b/chromeos/components/help_app_ui/resources/help_app.externs.js index 39cb0c0..d740058 100644 --- a/chromeos/components/help_app_ui/resources/help_app.externs.js +++ b/chromeos/components/help_app_ui/resources/help_app.externs.js
@@ -77,12 +77,14 @@ /** @type {string} */ helpApp.SearchResult.prototype.id; /** - * List of positions corresponding to the title. Used in snippet. + * List of positions corresponding to the title, sorted by start index. Used in + * snippet. * @type {?Array<!helpApp.Position>} */ helpApp.SearchResult.prototype.titlePositions; /** - * List of positions corresponding to the body. Used in snippet. + * List of positions corresponding to the body sorted by start index. Used in + * snippet. * @type {?Array<!helpApp.Position>} */ helpApp.SearchResult.prototype.bodyPositions;
diff --git a/chromeos/components/help_app_ui/resources/message_types.js b/chromeos/components/help_app_ui/resources/message_types.js index 3dfb4207..b97f3fc 100644 --- a/chromeos/components/help_app_ui/resources/message_types.js +++ b/chromeos/components/help_app_ui/resources/message_types.js
@@ -13,5 +13,8 @@ */ const Message = { OPEN_FEEDBACK_DIALOG: 'open-feedback-dialog', - SHOW_PARENTAL_CONTROLS: 'show-parental-controls' + SHOW_PARENTAL_CONTROLS: 'show-parental-controls', + ADD_OR_UPDATE_SEARCH_INDEX: 'add-or-update-search-index', + CLEAR_SEARCH_INDEX: 'clear-search-index', + FIND_IN_SEARCH_INDEX: 'find-in-search-index' };
diff --git a/chromeos/components/help_app_ui/resources/receiver.js b/chromeos/components/help_app_ui/resources/receiver.js index c49136f6..bf54dcb 100644 --- a/chromeos/components/help_app_ui/resources/receiver.js +++ b/chromeos/components/help_app_ui/resources/receiver.js
@@ -24,15 +24,27 @@ async showParentalControls() { await parentMessagePipe.sendMessage(Message.SHOW_PARENTAL_CONTROLS); }, - // TODO(b/166047521): Complete the implementation of these. - async addOrUpdateSearchIndex() { - return; + /** + * @override + * @param {!Array<!helpApp.SearchableItem>} data + */ + async addOrUpdateSearchIndex(data) { + await parentMessagePipe.sendMessage( + Message.ADD_OR_UPDATE_SEARCH_INDEX, data); }, async clearSearchIndex() { + // TODO(b/166047521): Send the message when clear search index has been + // implemented. the index when that method is available. return; }, - async findInSearchIndex() { - return {results: null}; + /** + * @override + * @param {string} query + * @return {!Promise<!helpApp.FindResponse>} + */ + findInSearchIndex(query) { + return /** @type {!Promise<!helpApp.FindResponse>} */ ( + parentMessagePipe.sendMessage(Message.FIND_IN_SEARCH_INDEX, {query})); }, };
diff --git a/chromeos/components/help_app_ui/test/help_app_ui_browsertest.js b/chromeos/components/help_app_ui/test/help_app_ui_browsertest.js index a36e84ce..27188f1a 100644 --- a/chromeos/components/help_app_ui/test/help_app_ui_browsertest.js +++ b/chromeos/components/help_app_ui/test/help_app_ui_browsertest.js
@@ -44,8 +44,6 @@ } }; -const toString16 = s => ({data: Array.from(s, c => c.charCodeAt())}); - // Tests that chrome://help-app goes somewhere instead of 404ing or crashing. TEST_F('HelpAppUIBrowserTest', 'HasChromeSchemeURL', () => { const guest = document.querySelector('iframe'); @@ -64,6 +62,7 @@ // Tests that we can make calls to the LSS to search. TEST_F('HelpAppUIBrowserTest', 'CanSearchViaLSSIndex', async () => { + const toString16 = s => ({data: Array.from(s, c => c.charCodeAt())}); const result = await indexRemote.find(toString16('search string!'), 100); // Status 3 corresponds to kEmptyIndex.
diff --git a/chromeos/components/phonehub/fake_find_my_device_controller.cc b/chromeos/components/phonehub/fake_find_my_device_controller.cc index a85c009a..9781641 100644 --- a/chromeos/components/phonehub/fake_find_my_device_controller.cc +++ b/chromeos/components/phonehub/fake_find_my_device_controller.cc
@@ -11,22 +11,33 @@ FakeFindMyDeviceController::~FakeFindMyDeviceController() = default; -bool FakeFindMyDeviceController::IsPhoneRinging() const { - return is_phone_ringing_; +void FakeFindMyDeviceController::SetPhoneRingingState( + Status phone_ringing_status) { + if (phone_ringing_status_ == phone_ringing_status) + return; + phone_ringing_status_ = phone_ringing_status; + NotifyPhoneRingingStateChanged(); } void FakeFindMyDeviceController::SetIsPhoneRingingInternal( bool is_phone_ringing) { - if (is_phone_ringing_ == is_phone_ringing) + Status phone_ringing_status = + is_phone_ringing ? Status::kRingingOn : Status::kRingingOff; + + if (phone_ringing_status_ == Status::kRingingNotAvailable) return; - is_phone_ringing_ = is_phone_ringing; - NotifyPhoneRingingStateChanged(); + SetPhoneRingingState(phone_ringing_status); } void FakeFindMyDeviceController::RequestNewPhoneRingingState(bool ringing) { SetIsPhoneRingingInternal(ringing); } +FindMyDeviceController::Status +FakeFindMyDeviceController::GetPhoneRingingStatus() { + return phone_ringing_status_; +} + } // namespace phonehub } // namespace chromeos
diff --git a/chromeos/components/phonehub/fake_find_my_device_controller.h b/chromeos/components/phonehub/fake_find_my_device_controller.h index 60a57e97..514f1f7 100644 --- a/chromeos/components/phonehub/fake_find_my_device_controller.h +++ b/chromeos/components/phonehub/fake_find_my_device_controller.h
@@ -15,13 +15,15 @@ FakeFindMyDeviceController(); ~FakeFindMyDeviceController() override; + void SetPhoneRingingState(Status status); + // FindMyDeviceController: - bool IsPhoneRinging() const override; void SetIsPhoneRingingInternal(bool is_phone_ringing) override; void RequestNewPhoneRingingState(bool ringing) override; + Status GetPhoneRingingStatus() override; private: - bool is_phone_ringing_ = false; + Status phone_ringing_status_ = Status::kRingingOff; }; } // namespace phonehub
diff --git a/chromeos/components/phonehub/find_my_device_controller.h b/chromeos/components/phonehub/find_my_device_controller.h index cd95a2b..e455dffd 100644 --- a/chromeos/components/phonehub/find_my_device_controller.h +++ b/chromeos/components/phonehub/find_my_device_controller.h
@@ -22,20 +22,31 @@ virtual void OnPhoneRingingStateChanged() = 0; }; + enum class Status { + // The connected phone is not currently ringing. + kRingingOff = 0, + // The connected phone is currently ringing. + kRingingOn = 1, + // Ringing is not available if the phone's DoNotDisturb mode is enabled. + // To re-enable ringing, DoNotDisturb mode must be disabled. + kRingingNotAvailable = 2, + }; + FindMyDeviceController(const FindMyDeviceController&) = delete; FindMyDeviceController& operator=(const FindMyDeviceController&) = delete; virtual ~FindMyDeviceController(); - // Returns whether the phone is ringing as a result of Find My Device - // functionality. Note that this function does not return true if the phone is - // ringing for another reason (e.g., a normal phone call). - virtual bool IsPhoneRinging() const = 0; - // Note: Ringing the phone via Find My Device is not a synchronous operation, // since it requires sending a message to the connected phone. Use the // observer interface to be notified of when the state changes. virtual void RequestNewPhoneRingingState(bool ringing) = 0; + // Returns the current ringing state of the connected phone. There are three + // possible states (on, off, disabled). The status is a result of Find My + // Device Functionality. Note that this function does not return true if the + // phone is ringing for another reason (e.g., a normal phone call) + virtual Status GetPhoneRingingStatus() = 0; + void AddObserver(Observer* observer); void RemoveObserver(Observer* observer);
diff --git a/chromeos/components/phonehub/find_my_device_controller_impl.cc b/chromeos/components/phonehub/find_my_device_controller_impl.cc index 28cc5d9..17cda6a 100644 --- a/chromeos/components/phonehub/find_my_device_controller_impl.cc +++ b/chromeos/components/phonehub/find_my_device_controller_impl.cc
@@ -5,26 +5,70 @@ #include "chromeos/components/phonehub/find_my_device_controller_impl.h" #include "chromeos/components/multidevice/logging/logging.h" +#include "chromeos/components/phonehub/message_sender.h" namespace chromeos { namespace phonehub { -FindMyDeviceControllerImpl::FindMyDeviceControllerImpl() = default; +FindMyDeviceControllerImpl::FindMyDeviceControllerImpl( + DoNotDisturbController* do_not_disturb_controller, + MessageSender* message_sender) + : do_not_disturb_controller_(do_not_disturb_controller), + message_sender_(message_sender) { + DCHECK(do_not_disturb_controller_); + DCHECK(message_sender_); -FindMyDeviceControllerImpl::~FindMyDeviceControllerImpl() = default; + do_not_disturb_controller_->AddObserver(this); +} -bool FindMyDeviceControllerImpl::IsPhoneRinging() const { - return is_phone_ringing_; +FindMyDeviceControllerImpl::~FindMyDeviceControllerImpl() { + do_not_disturb_controller_->RemoveObserver(this); } void FindMyDeviceControllerImpl::SetIsPhoneRingingInternal( bool is_phone_ringing) { is_phone_ringing_ = is_phone_ringing; + UpdateStatus(); +} + +FindMyDeviceController::Status +FindMyDeviceControllerImpl::GetPhoneRingingStatus() { + return phone_ringing_status_; } void FindMyDeviceControllerImpl::RequestNewPhoneRingingState(bool ringing) { + if (phone_ringing_status_ == Status::kRingingNotAvailable) { + PA_LOG(WARNING) << "Cannot request new ringing status because DoNotDisturb " + << "mode is enabled."; + return; + } + PA_LOG(INFO) << "Attempting to set Find My Device phone ring state; new " << "value: " << ringing; + message_sender_->SendRingDeviceRequest(ringing); +} + +void FindMyDeviceControllerImpl::OnDndStateChanged() { + UpdateStatus(); +} + +FindMyDeviceController::Status FindMyDeviceControllerImpl::ComputeStatus() + const { + if (do_not_disturb_controller_->IsDndEnabled()) { + PA_LOG(WARNING) << "Cannot set ringing status because DoNotDisturb mode is " + << "enabled."; + return Status::kRingingNotAvailable; + } + return is_phone_ringing_ ? Status::kRingingOn : Status::kRingingOff; +} + +void FindMyDeviceControllerImpl::UpdateStatus() { + Status status = ComputeStatus(); + if (phone_ringing_status_ == status) + return; + + phone_ringing_status_ = status; + NotifyPhoneRingingStateChanged(); } } // namespace phonehub
diff --git a/chromeos/components/phonehub/find_my_device_controller_impl.h b/chromeos/components/phonehub/find_my_device_controller_impl.h index c76b15b..f84937c 100644 --- a/chromeos/components/phonehub/find_my_device_controller_impl.h +++ b/chromeos/components/phonehub/find_my_device_controller_impl.h
@@ -5,24 +5,42 @@ #ifndef CHROMEOS_COMPONENTS_PHONEHUB_FIND_MY_DEVICE_CONTROLLER_IMPL_H_ #define CHROMEOS_COMPONENTS_PHONEHUB_FIND_MY_DEVICE_CONTROLLER_IMPL_H_ +#include "chromeos/components/phonehub/do_not_disturb_controller.h" #include "chromeos/components/phonehub/find_my_device_controller.h" namespace chromeos { namespace phonehub { -// TODO(https://crbug.com/1106937): Add real implementation. -class FindMyDeviceControllerImpl : public FindMyDeviceController { +class MessageSender; + +// Responsible for sending and receiving updates in regards to the Find My +// Device feature which involves ringing the user's remote phone. +class FindMyDeviceControllerImpl : public FindMyDeviceController, + public DoNotDisturbController::Observer { public: - FindMyDeviceControllerImpl(); + FindMyDeviceControllerImpl(DoNotDisturbController* do_not_disturb_controller, + MessageSender* message_sender); ~FindMyDeviceControllerImpl() override; private: + friend class FindMyDeviceControllerImplTest; + + Status ComputeStatus() const; + void UpdateStatus(); + // FindMyDeviceController: - bool IsPhoneRinging() const override; void SetIsPhoneRingingInternal(bool is_phone_ringing) override; void RequestNewPhoneRingingState(bool ringing) override; + Status GetPhoneRingingStatus() override; + + // DoNotDisturbController::Observer: + void OnDndStateChanged() override; bool is_phone_ringing_ = false; + Status phone_ringing_status_ = Status::kRingingOff; + + DoNotDisturbController* do_not_disturb_controller_; + MessageSender* message_sender_; }; } // namespace phonehub
diff --git a/chromeos/components/phonehub/find_my_device_controller_impl_unittest.cc b/chromeos/components/phonehub/find_my_device_controller_impl_unittest.cc index 7c904bc..1519a18 100644 --- a/chromeos/components/phonehub/find_my_device_controller_impl_unittest.cc +++ b/chromeos/components/phonehub/find_my_device_controller_impl_unittest.cc
@@ -6,6 +6,9 @@ #include <memory> +#include "chromeos/components/phonehub/fake_do_not_disturb_controller.h" +#include "chromeos/components/phonehub/fake_message_sender.h" +#include "chromeos/components/phonehub/find_my_device_controller.h" #include "testing/gtest/include/gtest/gtest.h" namespace chromeos { @@ -39,25 +42,101 @@ // testing::Test: void SetUp() override { - controller_ = std::make_unique<FindMyDeviceControllerImpl>(); + fake_do_not_disturb_controller_ = + std::make_unique<FakeDoNotDisturbController>(); + fake_message_sender_ = std::make_unique<FakeMessageSender>(); + controller_ = std::make_unique<FindMyDeviceControllerImpl>( + fake_do_not_disturb_controller_.get(), fake_message_sender_.get()); controller_->AddObserver(&fake_observer_); } void TearDown() override { controller_->RemoveObserver(&fake_observer_); } - bool IsPhoneRinging() const { return controller_->IsPhoneRinging(); } + FindMyDeviceController::Status GetPhoneRingingStatus() const { + return controller_->GetPhoneRingingStatus(); + } + + void SetIsPhoneRingingInternal(bool is_phone_ringing) { + controller_->SetIsPhoneRingingInternal(is_phone_ringing); + } + + void RequestNewPhoneRingingState(bool ringing) { + controller_->RequestNewPhoneRingingState(ringing); + } size_t GetNumObserverCalls() const { return fake_observer_.num_calls(); } + protected: + std::unique_ptr<FakeDoNotDisturbController> fake_do_not_disturb_controller_; + std::unique_ptr<FakeMessageSender> fake_message_sender_; + private: + std::unique_ptr<FindMyDeviceControllerImpl> controller_; FakeObserver fake_observer_; - std::unique_ptr<FindMyDeviceController> controller_; }; -// TODO(https://crbug.com/1106937): Remove this test once we have real -// functionality to test. -TEST_F(FindMyDeviceControllerImplTest, Initialize) { - EXPECT_FALSE(IsPhoneRinging()); +TEST_F(FindMyDeviceControllerImplTest, RingingStateChanges) { + EXPECT_EQ(FindMyDeviceController::Status::kRingingOff, + GetPhoneRingingStatus()); + + // Simulate flipping DoNotDisturb mode to enabled, this should set the + // FindMyPhone status to kRingingNotAvailable. + fake_do_not_disturb_controller_->SetDoNotDisturbStateInternal( + /*is_dnd_enabled=*/true); + EXPECT_EQ(FindMyDeviceController::Status::kRingingNotAvailable, + GetPhoneRingingStatus()); + // Simulate initiating phone ringing when DoNotDisturb mode is enabled. This + // will not update the internal status. + SetIsPhoneRingingInternal(/*is_phone_ringing=*/true); + EXPECT_EQ(FindMyDeviceController::Status::kRingingNotAvailable, + GetPhoneRingingStatus()); + EXPECT_EQ(1u, GetNumObserverCalls()); + + // Flip DoNotDisturb back to disabled, expect status to reset back to its + // previous state. + fake_do_not_disturb_controller_->SetDoNotDisturbStateInternal( + /*is_dnd_enabled=*/false); + // Since we previously recorded that the phone should be ringing during + // DoNotDisturb mode was enabled, we return to that state once DoNotDisturb + // is disabled. + EXPECT_EQ(FindMyDeviceController::Status::kRingingOn, + GetPhoneRingingStatus()); + EXPECT_EQ(2u, GetNumObserverCalls()); + + // Attempt to set ringing status with the same previous state. Expect that no + // observer calls were made. + SetIsPhoneRingingInternal(/*is_phone_ringing=*/true); + EXPECT_EQ(2u, GetNumObserverCalls()); +} + +TEST_F(FindMyDeviceControllerImplTest, RequestNewRingStatus) { + RequestNewPhoneRingingState(/*ringing=*/true); + EXPECT_EQ(1u, fake_message_sender_->GetRingDeviceRequestCallCount()); + EXPECT_TRUE(fake_message_sender_->GetRecentRingDeviceRequest()); + + // Simulate flipping DoNotDisturb mode to enabled, this should set the + // FindMyPhone status to kRingingNotAvailable and not send any new messages. + fake_do_not_disturb_controller_->SetDoNotDisturbStateInternal( + /*is_dnd_enabled=*/true); + EXPECT_EQ(FindMyDeviceController::Status::kRingingNotAvailable, + GetPhoneRingingStatus()); + + RequestNewPhoneRingingState(/*ringing=*/false); + EXPECT_EQ(1u, fake_message_sender_->GetRingDeviceRequestCallCount()); + // No new messages were sent, expect that the last request was still the + // previous "true" value. + EXPECT_TRUE(fake_message_sender_->GetRecentRingDeviceRequest()); + + // Flip DoNotDisturb mode to disabled, expect that messages are able to be + // sent again. + fake_do_not_disturb_controller_->SetDoNotDisturbStateInternal( + /*is_dnd_enabled=*/false); + EXPECT_EQ(FindMyDeviceController::Status::kRingingOff, + GetPhoneRingingStatus()); + + RequestNewPhoneRingingState(/*ringing=*/false); + EXPECT_EQ(2u, fake_message_sender_->GetRingDeviceRequestCallCount()); + EXPECT_FALSE(fake_message_sender_->GetRecentRingDeviceRequest()); } } // namespace phonehub
diff --git a/chromeos/components/phonehub/phone_hub_manager_impl.cc b/chromeos/components/phonehub/phone_hub_manager_impl.cc index e6b6350..65dc98e 100644 --- a/chromeos/components/phonehub/phone_hub_manager_impl.cc +++ b/chromeos/components/phonehub/phone_hub_manager_impl.cc
@@ -45,8 +45,9 @@ connection_scheduler_(std::make_unique<ConnectionSchedulerImpl>( connection_manager_.get(), feature_status_provider_.get())), - find_my_device_controller_( - std::make_unique<FindMyDeviceControllerImpl>()), + find_my_device_controller_(std::make_unique<FindMyDeviceControllerImpl>( + do_not_disturb_controller_.get(), + message_sender_.get())), notification_access_manager_( std::make_unique<NotificationAccessManagerImpl>(pref_service)), notification_manager_(std::make_unique<NotificationManagerImpl>()),
diff --git a/chromeos/components/phonehub/phone_status_processor_unittest.cc b/chromeos/components/phonehub/phone_status_processor_unittest.cc index c356fdc6..57a4bfe 100644 --- a/chromeos/components/phonehub/phone_status_processor_unittest.cc +++ b/chromeos/components/phonehub/phone_status_processor_unittest.cc
@@ -137,7 +137,8 @@ EXPECT_EQ(base::UTF8ToUTF16(test_remote_device_.name()), *mutable_phone_model_->phone_name()); EXPECT_TRUE(fake_do_not_disturb_controller_->IsDndEnabled()); - EXPECT_TRUE(fake_find_my_device_controller_->IsPhoneRinging()); + EXPECT_EQ(FindMyDeviceController::Status::kRingingOn, + fake_find_my_device_controller_->GetPhoneRingingStatus()); EXPECT_TRUE(fake_notification_access_manager_->HasAccessBeenGranted()); base::Optional<PhoneStatusModel> phone_status_model = @@ -198,7 +199,8 @@ EXPECT_EQ(base::UTF8ToUTF16(test_remote_device_.name()), *mutable_phone_model_->phone_name()); EXPECT_TRUE(fake_do_not_disturb_controller_->IsDndEnabled()); - EXPECT_TRUE(fake_find_my_device_controller_->IsPhoneRinging()); + EXPECT_EQ(FindMyDeviceController::Status::kRingingOn, + fake_find_my_device_controller_->GetPhoneRingingStatus()); EXPECT_TRUE(fake_notification_access_manager_->HasAccessBeenGranted()); base::Optional<PhoneStatusModel> phone_status_model = @@ -221,7 +223,8 @@ EXPECT_EQ(base::UTF8ToUTF16(test_remote_device_.name()), *mutable_phone_model_->phone_name()); EXPECT_TRUE(fake_do_not_disturb_controller_->IsDndEnabled()); - EXPECT_TRUE(fake_find_my_device_controller_->IsPhoneRinging()); + EXPECT_EQ(FindMyDeviceController::Status::kRingingOn, + fake_find_my_device_controller_->GetPhoneRingingStatus()); EXPECT_TRUE(fake_notification_access_manager_->HasAccessBeenGranted()); phone_status_model = mutable_phone_model_->phone_status_model();
diff --git a/chromeos/components/scanning/mojom/scanning.mojom b/chromeos/components/scanning/mojom/scanning.mojom index 0cb5685..dddf0a6 100644 --- a/chromeos/components/scanning/mojom/scanning.mojom +++ b/chromeos/components/scanning/mojom/scanning.mojom
@@ -16,12 +16,17 @@ // The source types from which a scan can be obtained. enum SourceType { + // An unknown source type. + kUnknown, // A flatbed that scans a single page. kFlatbed, // An automatic document feeder that scans a single side of each page. kAdfSimplex, // An automatic document feeder that scans both sides of each page. kAdfDuplex, + // The implicit source type used for scanners that do not report any source + // options. + kDefault, }; // The source from which a scan can be obtained.
diff --git a/components/arc/mojom/bluetooth.mojom b/components/arc/mojom/bluetooth.mojom index 64e3f6d..1851507 100644 --- a/components/arc/mojom/bluetooth.mojom +++ b/components/arc/mojom/bluetooth.mojom
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Next MinVersion: 18 +// Next MinVersion: 19 module arc.mojom; @@ -316,8 +316,56 @@ OnConnectFailed@1(); }; -// Next Method ID: 47 -// Deprecated Method ID: 4, 5, 6, 7, 20, 21 +// Copied from Android. See android.bluetooth.BluetoothSocket. +// Currently we only support RFCOMM and LE CoC (L2CAP_LE) sockets. +[Extensible] +enum BluetoothSocketType { + TYPE_RFCOMM = 1, + TYPE_L2CAP_LE = 4, +}; + +// Bluetooth socket security flags in Android, used in open socket requests. +// Chrome should set the socket option based on these flags. +struct BluetoothSocketFlags { + bool encrypt; + bool auth; + bool auth_mitm; + bool auth_16_digit; +}; + +// BluetoothSocketConnection contains the information for a new Bluetooth socket +// connection. Note that |port| is either the channel number for an RFCOMM +// socket or PSM for an L2CAP socket. Since we cannot get socket (or peer) name +// on the transferred socket on Android side, we also need to pass the peer +// address and port number. +struct BluetoothSocketConnection { + handle sock; + BluetoothAddress addr; + int32 port; +}; + +// The mojo connection represents a listening socket. +// Next Method ID: 1 +interface BluetoothListenSocketClient { + // Called when accept() succeeds. |port| in |connection| is the peer port + // number. + OnAccepted@0(BluetoothSocketConnection connection); +}; + +// The mojo connection represents a connecting (not connected yet) socket. +// After connect() either succeeds or fails, Android is responsible for closing +// this mojo connection. +// Next Method ID: 2 +interface BluetoothConnectSocketClient { + // Called when connect() succeeds. |port| in |connection| is the port number + // on our side. + OnConnected@0(BluetoothSocketConnection connection); + // Called when connect() fails. + OnConnectFailed@1(); +}; + +// Next Method ID: 49 +// Deprecated Method ID: 4, 5, 6, 7, 20, 21, 29, 45, 46 interface BluetoothHost { EnableAdapter@0() => (BluetoothAdapterState state); DisableAdapter@1() => (BluetoothAdapterState state); @@ -375,7 +423,9 @@ [MinVersion=1] ReadRemoteRssi@28(BluetoothAddress remote_addr) => (int32 rssi); - [MinVersion=2] OpenBluetoothSocket@29() + // DEPRECATED: Use BluetoothSocketListen@47 or BluetoothSocketConnect@48 + // instead. + [MinVersion=2] OpenBluetoothSocketDeprecated@29() => (handle sock); // Bluetooth Gatt Server functions @@ -430,7 +480,7 @@ // we will select a channel number automatically. If this process succeeds, // returns SUCCESS in |status|, the actual listening channel in |channel|, // and a new mojo connection which represents the listening socket. - [MinVersion=15] RfcommListen@45(int32 channel, int32 optval) + [MinVersion=15] RfcommListenDeprecated@45(int32 channel, int32 optval) => (BluetoothStatus status, int32 channel, RfcommListeningSocketClient&? client); // Opens a bluetooth socket with socket option |optval|, and then connect() @@ -438,9 +488,27 @@ // in |status| and a new mojo connection which holds the connecting socket. // Unlike in RfcommListen(), |channel| here could not be 0, since this is the // peer channel number. - [MinVersion=15] RfcommConnect@46(BluetoothAddress remote_addr, - int32 channel, int32 optval) + [MinVersion=15] RfcommConnectDeprecated@46(BluetoothAddress remote_addr, + int32 channel, int32 optval) => (BluetoothStatus status, RfcommConnectingSocketClient&? client); + + // Bluetooth socket (RFCOMM and L2CAP LE) functions + // Opens a |sock_type| socket with security options in |sock_flags|, and + // listens on |port| (RFCOMM channel or L2CAP PSM). When |port| = 0, we will + // select a port number automatically. If this process succeeds, the actual + // listening port will be returned in |port|. + [MinVersion=18] BluetoothSocketListen@47(BluetoothSocketType sock_type, + BluetoothSocketFlags sock_flags, + int32 port) + => (BluetoothStatus status, int32 port, + BluetoothListenSocketClient&? client); + // Opens a |sock_type| socket with security options in |sock_flags|, and + // connects to |remote_addr| which is listening on |remote_port|. + [MinVersion=18] BluetoothSocketConnect@48(BluetoothSocketType sock_type, + BluetoothSocketFlags sock_flags, + BluetoothAddress remote_addr, + int32 remote_port) + => (BluetoothStatus status, BluetoothConnectSocketClient&? client); }; // Next Method ID: 24
diff --git a/components/autofill/core/browser/BUILD.gn b/components/autofill/core/browser/BUILD.gn index 3447ad7..cbc8883 100644 --- a/components/autofill/core/browser/BUILD.gn +++ b/components/autofill/core/browser/BUILD.gn
@@ -183,6 +183,8 @@ "metrics/form_event_logger_base.cc", "metrics/form_event_logger_base.h", "metrics/form_events.h", + "pattern_provider/pattern_configuration_parser.cc", + "pattern_provider/pattern_configuration_parser.h", "pattern_provider/pattern_provider.cc", "pattern_provider/pattern_provider.h", "payments/account_info_getter.h", @@ -390,6 +392,7 @@ "//crypto", "//google_apis", "//net", + "//services/data_decoder/public/cpp:cpp", "//services/metrics/public/cpp:metrics_cpp", "//services/metrics/public/cpp:ukm_builders", "//services/network/public/cpp", @@ -446,6 +449,8 @@ "logging/stub_log_manager.h", "mock_autocomplete_history_manager.cc", "mock_autocomplete_history_manager.h", + "pattern_provider/test_pattern_provider.cc", + "pattern_provider/test_pattern_provider.h", "payments/test_authentication_requester.cc", "payments/test_authentication_requester.h", "payments/test_credit_card_save_manager.cc", @@ -628,6 +633,7 @@ "logging/log_buffer_submitter_unittest.cc", "logging/log_manager_unittest.cc", "logging/log_router_unittest.cc", + "pattern_provider/pattern_configuration_parser_unittest.cc", "pattern_provider/pattern_provider_unittest.cc", "payments/autofill_offer_manager_unittest.cc", "payments/credit_card_access_manager_unittest.cc", @@ -727,6 +733,7 @@ "//google_apis", "//google_apis:test_support", "//net:test_support", + "//services/data_decoder/public/cpp:test_support", "//services/metrics/public/cpp:ukm_builders", "//services/network:test_support", "//services/network/public/cpp",
diff --git a/components/autofill/core/browser/pattern_provider/DEPS b/components/autofill/core/browser/pattern_provider/DEPS new file mode 100644 index 0000000..89e4cd8 --- /dev/null +++ b/components/autofill/core/browser/pattern_provider/DEPS
@@ -0,0 +1,5 @@ +include_rules = [ + "+components/grit/components_resources.h", + "+services/data_decoder/public/cpp:cpp", + "+services/data_decoder/public", +]
diff --git a/components/autofill/core/browser/pattern_provider/pattern_configuration_parser.cc b/components/autofill/core/browser/pattern_provider/pattern_configuration_parser.cc new file mode 100644 index 0000000..f6e4f7fa --- /dev/null +++ b/components/autofill/core/browser/pattern_provider/pattern_configuration_parser.cc
@@ -0,0 +1,220 @@ +// Copyright 2020 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/core/browser/pattern_provider/pattern_configuration_parser.h" + +#include "base/bind.h" +#include "base/task/task_traits.h" +#include "base/task/thread_pool.h" +#include "base/values.h" +#include "components/autofill/core/browser/autofill_type.h" +#include "components/autofill/core/browser/field_types.h" +#include "components/grit/components_resources.h" +#include "ui/base/resource/resource_bundle.h" + +namespace autofill { + +namespace field_type_parsing { + +namespace { + +const char kPatternIdentifierKey[] = "pattern_identifier"; +const char kPositivePatternKey[] = "positive_pattern"; +const char kNegativePatternKey[] = "negative_pattern"; +const char kPositiveScoreKey[] = "positive_score"; +const char kMatchFieldAttributesKey[] = "match_field_attributes"; +const char kMatchFieldInputTypesKey[] = "match_field_input_types"; +const char kVersionKey[] = "version"; + +bool ParseMatchingPattern(PatternProvider::Map& patterns, + const std::string& field_type, + const std::string& language, + const base::Value& value) { + if (!value.is_dict()) + return false; + + const std::string* pattern_identifier = + value.FindStringKey(kPatternIdentifierKey); + const std::string* positive_pattern = + value.FindStringKey(kPositivePatternKey); + const std::string* negative_pattern = + value.FindStringKey(kNegativePatternKey); + base::Optional<double> positive_score = + value.FindDoubleKey(kPositiveScoreKey); + base::Optional<int> match_field_attributes = + value.FindIntKey(kMatchFieldAttributesKey); + base::Optional<int> match_field_input_types = + value.FindIntKey(kMatchFieldInputTypesKey); + + if (!pattern_identifier || !positive_pattern || !negative_pattern || + !positive_score || !match_field_attributes || !match_field_input_types) + return false; + + autofill::MatchingPattern new_pattern; + new_pattern.pattern_identifier = *pattern_identifier; + new_pattern.positive_pattern = *positive_pattern; + new_pattern.positive_score = *positive_score; + new_pattern.negative_pattern = *negative_pattern; + new_pattern.match_field_attributes = match_field_attributes.value(); + new_pattern.match_field_input_types = match_field_input_types.value(); + new_pattern.language = language; + + std::vector<MatchingPattern>* pattern_list = &patterns[field_type][language]; + pattern_list->push_back(new_pattern); + + DVLOG(2) << "Correctly parsed MatchingPattern with identifier |" + << new_pattern.pattern_identifier << "|."; + + return true; +} + +// Callback which is used once the JSON is parsed. +// |overwrite_equal_version| should be true when loading a remote +// configuration. If the configuration versions are equal or +// both unspecified (i.e. set to 0) this prioritizes the remote +// configuration over the local one. +void OnJsonParsed(bool overwrite_equal_version, + base::OnceClosure done_callback, + data_decoder::DataDecoder::ValueOrError result) { + // Skip any processing in case of an error. + if (!result.value) { + DVLOG(1) << "Failed to parse PatternProvider configuration JSON string."; + std::move(done_callback).Run(); + return; + } + + base::Version version = ExtractVersionFromJsonObject(result.value.value()); + base::Optional<PatternProvider::Map> patterns = + GetConfigurationFromJsonObject(result.value.value()); + + if (patterns && version.IsValid()) { + DVLOG(1) << "Successfully parsed PatternProvider configuration."; + + PatternProvider& pattern_provider = PatternProvider::GetInstance(); + pattern_provider.SetPatterns(std::move(patterns.value()), + std::move(version), overwrite_equal_version); + } else { + DVLOG(1) << "Failed to parse PatternProvider configuration JSON object."; + } + + std::move(done_callback).Run(); +} + +} // namespace + +base::Optional<PatternProvider::Map> GetConfigurationFromJsonObject( + const base::Value& root) { + PatternProvider::Map patterns; + + if (!root.is_dict()) { + DVLOG(1) << "JSON object is not a dictionary."; + return base::nullopt; + } + + for (const auto& kv : root.DictItems()) { + const std::string& field_type = kv.first; + const base::Value* field_type_dict = &kv.second; + + if (!field_type_dict->is_dict()) { + DVLOG(1) << "|" << field_type << "| does not contain a dictionary."; + return base::nullopt; + } + + for (const auto& value : field_type_dict->DictItems()) { + const std::string& language = value.first; + const base::Value* inner_list = &value.second; + + if (!inner_list->is_list()) { + DVLOG(1) << "Language |" << language << "| in |" << field_type + << "| does not contain a list."; + return base::nullopt; + } + + for (const auto& matchingPatternObj : inner_list->GetList()) { + bool success = ParseMatchingPattern(patterns, field_type, language, + matchingPatternObj); + if (!success) { + DVLOG(1) << "Found incorrect |MatchingPattern| object in list |" + << field_type << "|, language |" << language << "|."; + return base::nullopt; + } + } + } + } + + return base::make_optional(patterns); +} + +base::Version ExtractVersionFromJsonObject(base::Value& root) { + if (!root.is_dict()) + return base::Version("0"); + + base::Optional<base::Value> version_str = root.ExtractKey(kVersionKey); + if (!version_str || !version_str.value().is_string()) + return base::Version("0"); + + base::Version version = base::Version(version_str.value().GetString()); + if (!version.IsValid()) + return base::Version("0"); + + return version; +} + +void PopulateFromJsonString(std::string json_string) { + data_decoder::DataDecoder::ParseJsonIsolated( + std::move(json_string), + base::BindOnce(&OnJsonParsed, true, base::DoNothing::Once())); +} + +void PopulateFromResourceBundle(base::OnceClosure done_callback) { + if (!ui::ResourceBundle::HasSharedInstance()) { + VLOG(1) << "Resource Bundle unavailable to load Autofill Matching Pattern " + "definitions."; + std::move(done_callback).Run(); + return; + } + + ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance(); + + // Load the string from the Resource Bundle on a worker thread, then + // securely parse the JSON in a separate process and call |OnJsonParsed| + // with the result. + base::ThreadPool::PostTaskAndReplyWithResult( + FROM_HERE, {base::MayBlock()}, + base::BindOnce(&ui::ResourceBundle::LoadDataResourceString, + base::Unretained(&bundle), IDR_AUTOFILL_REGEX_JSON), + base::BindOnce( + [](base::OnceClosure done_callback, std::string resource_string) { + data_decoder::DataDecoder::ParseJsonIsolated( + std::move(resource_string), + base::BindOnce(&OnJsonParsed, false, std::move(done_callback))); + }, + std::move(done_callback))); +} + +base::Optional<PatternProvider::Map> +GetPatternsFromResourceBundleSynchronously() { + if (!ui::ResourceBundle::HasSharedInstance()) { + VLOG(1) << "Resource Bundle unavailable to load Autofill Matching Pattern " + "definitions."; + return base::nullopt; + } + + ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance(); + std::string resource_string = + bundle.LoadDataResourceString(IDR_AUTOFILL_REGEX_JSON); + base::Optional<base::Value> json_object = + base::JSONReader::Read(resource_string); + + // Discard version, since this is the only getter used in unit tests. + base::Version version = ExtractVersionFromJsonObject(json_object.value()); + base::Optional<PatternProvider::Map> configuration_map = + GetConfigurationFromJsonObject(json_object.value()); + + return configuration_map; +} + +} // namespace field_type_parsing + +} // namespace autofill
diff --git a/components/autofill/core/browser/pattern_provider/pattern_configuration_parser.h b/components/autofill/core/browser/pattern_provider/pattern_configuration_parser.h new file mode 100644 index 0000000..8f72b18 --- /dev/null +++ b/components/autofill/core/browser/pattern_provider/pattern_configuration_parser.h
@@ -0,0 +1,60 @@ +// Copyright 2020 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_CORE_BROWSER_PATTERN_PROVIDER_PATTERN_CONFIGURATION_PARSER_H_ +#define COMPONENTS_AUTOFILL_CORE_BROWSER_PATTERN_PROVIDER_PATTERN_CONFIGURATION_PARSER_H_ + +#include <string> + +#include "base/json/json_reader.h" +#include "base/version.h" +#include "components/autofill/core/browser/form_parsing/autofill_parsing_utils.h" +#include "components/autofill/core/browser/pattern_provider/pattern_provider.h" +#include "services/data_decoder/public/cpp/data_decoder.h" + +namespace autofill { + +namespace field_type_parsing { + +// Tries to extract the configuration version from the JSON base::Value tree. +// This removes the key if found, so that validation is easier later on. +// If not found, default to version 0. +base::Version ExtractVersionFromJsonObject(base::Value& root); + +// Transforms the parsed JSON base::Value tree into the map used in +// |PatternProvider|. Requires the version key to already be extracted. +// The root is expected to be a dictionary with keys corresponding to +// strings representing |ServerFieldType|. Then there should be +// second level dictionaries with keys describing the language. These +// should point to a list of objects representing |MatchingPattern|. +// { +// "FIELD_NAME": { +// "language":[ +// {MatchingPatternFields} +// ] +// } +// } +// An example can be found in the relative resources folder. +base::Optional<PatternProvider::Map> GetConfigurationFromJsonObject( + const base::Value& root); + +// Tries to get and parse the default configuration in the resource bundle +// into a valid map used in |PatternProvider| and swap it in for further use. +// The callback is used as a signal for testing. +void PopulateFromResourceBundle( + base::OnceClosure done_callback = base::DoNothing::Once()); + +// Tries to parse the given JSON string into a valid map used in the +// |PatternProvider| and swap it in for further use. +void PopulateFromJsonString(std::string json_string); + +// Synchronous getter used to set up a test fixture. +base::Optional<PatternProvider::Map> +GetPatternsFromResourceBundleSynchronously(); + +} // namespace field_type_parsing + +} // namespace autofill + +#endif
diff --git a/components/autofill/core/browser/pattern_provider/pattern_configuration_parser_unittest.cc b/components/autofill/core/browser/pattern_provider/pattern_configuration_parser_unittest.cc new file mode 100644 index 0000000..d945187 --- /dev/null +++ b/components/autofill/core/browser/pattern_provider/pattern_configuration_parser_unittest.cc
@@ -0,0 +1,196 @@ +// Copyright 2020 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/core/browser/pattern_provider/pattern_configuration_parser.h" + +#include <stddef.h> + +#include "base/json/json_reader.h" +#include "base/test/gtest_util.h" +#include "base/version.h" +#include "components/grit/components_resources.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/base/resource/resource_bundle.h" + +namespace autofill { + +namespace field_type_parsing { + +// Test that the |base::Value| object of the configuration is +// parsed to the map structure used by |PatternProvider| as +// expected, given the input is valid. +TEST(PatternConfigurationParserTest, WellFormedParsedCorrectly) { + std::string JSON_message = R"( + { + "version": "1.0", + "FULL_NAME": { + "en_us": [ + { + "pattern_identifier": "Name_en", + "positive_pattern": "name|full name", + "positive_score": 2.0, + "negative_pattern": "company", + "match_field_attributes": 2, + "match_field_input_types": 3 + } + ], + "fr": [ + { + "pattern_identifier": "Name_fr", + "positive_pattern": "nom|prenom", + "positive_score": 2.0, + "negative_pattern": "compagne", + "match_field_attributes": 2, + "match_field_input_types": 3 + } + ] + }, + "ADDRESS": { + "en_us": [ + { + "pattern_identifier": "Address", + "positive_pattern": "address", + "positive_score": 2.0, + "negative_pattern": "email", + "match_field_attributes": 4, + "match_field_input_types": 3 + } + ] + } + })"; + base::Optional<base::Value> JSON_object = + base::JSONReader::Read(JSON_message); + + ASSERT_TRUE(JSON_object) << "Incorrectly formatted JSON string."; + + base::Version version = ExtractVersionFromJsonObject(JSON_object.value()); + base::Optional<PatternProvider::Map> optional_patterns = + GetConfigurationFromJsonObject(JSON_object.value()); + + ASSERT_TRUE(version.IsValid()); + ASSERT_TRUE(optional_patterns); + + ASSERT_EQ(base::Version("1.0"), version); + + PatternProvider::Map patterns = optional_patterns.value(); + + ASSERT_EQ(2U, patterns.size()); + ASSERT_TRUE(patterns.count("FULL_NAME")); + ASSERT_EQ(2U, patterns["FULL_NAME"].size()); + ASSERT_TRUE(patterns["FULL_NAME"].count("en_us")); + ASSERT_TRUE(patterns["FULL_NAME"].count("fr")); + + ASSERT_TRUE(patterns.count("ADDRESS")); + ASSERT_EQ(1U, patterns["ADDRESS"].size()); + ASSERT_TRUE(patterns["ADDRESS"].count("en_us")); + + // Test one |MatchingPattern| to check that they are parsed correctly. + MatchingPattern* pattern = &patterns["FULL_NAME"]["fr"][0]; + + ASSERT_EQ("Name_fr", pattern->pattern_identifier); + ASSERT_EQ("nom|prenom", pattern->positive_pattern); + ASSERT_EQ("compagne", pattern->negative_pattern); + ASSERT_EQ("fr", pattern->language); + ASSERT_NEAR(2.0, pattern->positive_score, 1e-6); + ASSERT_EQ(2, pattern->match_field_attributes); + ASSERT_EQ(3, pattern->match_field_input_types); +} + +// Test that the parser does not return anything if some |MatchingPattern| +// object is missing a property. +TEST(PatternConfigurationParserTest, MalformedMissingProperty) { + std::string JSON_message = R"( + { + "version": "1.0", + "FULL_NAME": { + "en_us": [ + { + "pattern_identifier": "Name_en", + "positive_pattern": "name|full name", + "positive_score": 2.0, + "negative_pattern": "company", + "match_field_attributes": 2, + "match_field_input_types": 3 + } + ], + "fr": [ + { + "pattern_identifier": "Name_fr", + "positive_pattern": "nom|prenom", + "negative_pattern": "compagne", + "match_field_attributes": 2, + "match_field_input_types": 3 + } + ] + } + })"; + base::Optional<base::Value> JSON_object = + base::JSONReader::Read(JSON_message); + + ASSERT_TRUE(JSON_object) << "Incorrectly formatted JSON string."; + + base::Optional<PatternProvider::Map> optional_patterns = + GetConfigurationFromJsonObject(JSON_object.value()); + + ASSERT_FALSE(optional_patterns); +} + +// Test that the parser correctly sets the default version if +// it is not present in the configuration. +TEST(PatternConfigurationParserTest, MalformedMissingVersion) { + std::string JSON_message = R"( + { + "FULL_NAME": { + "en_us": [ + { + "pattern_identifier": "Name_en", + "positive_pattern": "name|full name", + "positive_score": 2.0, + "negative_pattern": "company", + "match_field_attributes": 2, + "match_field_input_types": 3 + } + ] + } + })"; + base::Optional<base::Value> JSON_object = + base::JSONReader::Read(JSON_message); + + ASSERT_TRUE(JSON_object) << "Incorrectly formatted JSON string."; + + base::Version version = ExtractVersionFromJsonObject(JSON_object.value()); + + ASSERT_EQ(base::Version("0"), version); +} + +// Test that the parser does not return anything if the inner key points +// to a single object instead of a list. +TEST(PatternConfigurationParserTest, MalformedNotList) { + std::string JSON_message = R"( + { + "FULL_NAME": { + "en_us": { + "pattern_identifier": "Name_en", + "positive_pattern": "name|full name", + "positive_score": 2.0, + "negative_pattern": "company", + "match_field_attributes": 2, + "match_field_input_types": 3 + } + } + })"; + base::Optional<base::Value> JSON_object = + base::JSONReader::Read(JSON_message); + + ASSERT_TRUE(JSON_object) << "Incorrectly formatted JSON string."; + + base::Optional<PatternProvider::Map> optional_patterns = + GetConfigurationFromJsonObject(JSON_object.value()); + + ASSERT_FALSE(optional_patterns); +} + +} // namespace field_type_parsing + +} // namespace autofill
diff --git a/components/autofill/core/browser/pattern_provider/pattern_provider.cc b/components/autofill/core/browser/pattern_provider/pattern_provider.cc index 04a2d48..a2eb5e74f 100644 --- a/components/autofill/core/browser/pattern_provider/pattern_provider.cc +++ b/components/autofill/core/browser/pattern_provider/pattern_provider.cc
@@ -8,29 +8,36 @@ #include <iostream> #include <string> +#include "base/bind.h" +#include "base/no_destructor.h" #include "components/autofill/core/browser/autofill_type.h" +#include "components/autofill/core/browser/pattern_provider/pattern_configuration_parser.h" namespace autofill { -PatternProvider::PatternProvider() { - auto& company_patterns = patterns_[AutofillType(COMPANY_NAME).ToString()]; - company_patterns["EN"].push_back(GetCompanyPatternEn()); - company_patterns["DE"].push_back(GetCompanyPatternDe()); +namespace { +PatternProvider* g_pattern_provider = nullptr; } -PatternProvider::~PatternProvider() { - patterns_.clear(); -} +PatternProvider::PatternProvider() = default; +PatternProvider::~PatternProvider() = default; -void PatternProvider::SetPatterns( - const std::map<std::string, - std::map<std::string, std::vector<MatchingPattern>>>& - patterns) { - patterns_ = patterns; +void PatternProvider::SetPatterns(PatternProvider::Map patterns, + const base::Version version, + const bool overwrite_equal_version) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + if (!pattern_version_.IsValid() || pattern_version_ < version || + (overwrite_equal_version && pattern_version_ == version)) { + patterns_ = patterns; + pattern_version_ = version; + } } const std::vector<MatchingPattern>& PatternProvider::GetMatchPatterns( const std::string& pattern_name, const std::string& page_language) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + return patterns_[pattern_name][page_language]; } @@ -41,9 +48,26 @@ return GetMatchPatterns(pattern_name, page_language); } -PatternProvider* PatternProvider::getInstance() { - static base::NoDestructor<PatternProvider> instance; - return instance.get(); +// static. +PatternProvider& PatternProvider::GetInstance() { + if (!g_pattern_provider) { + static base::NoDestructor<PatternProvider> instance; + g_pattern_provider = instance.get(); + field_type_parsing::PopulateFromResourceBundle(); + } + return *g_pattern_provider; +} + +// static. +void PatternProvider::SetPatternProviderForTesting( + PatternProvider* pattern_provider) { + DCHECK(pattern_provider); + g_pattern_provider = pattern_provider; +} + +// static. +void PatternProvider::ResetPatternProvider() { + g_pattern_provider = nullptr; } } // namespace autofill
diff --git a/components/autofill/core/browser/pattern_provider/pattern_provider.h b/components/autofill/core/browser/pattern_provider/pattern_provider.h index 2cb7422..c182e8bce4 100644 --- a/components/autofill/core/browser/pattern_provider/pattern_provider.h +++ b/components/autofill/core/browser/pattern_provider/pattern_provider.h
@@ -7,24 +7,33 @@ #include <string> +#include "base/gtest_prod_util.h" #include "base/macros.h" #include "base/no_destructor.h" +#include "base/sequence_checker.h" +#include "base/version.h" #include "components/autofill/core/browser/field_types.h" #include "components/autofill/core/browser/form_parsing/autofill_parsing_utils.h" #include "components/autofill/core/common/autofill_regex_constants.h" -#include "third_party/re2/src/re2/re2.h" namespace autofill { +// Base class for the Pattern Provider. This class contains the implementation +// for providing the matching patterns. Different subclasses provide different +// ways to load the data in for further use. class PatternProvider { public: - static PatternProvider* getInstance(); + // Shorthand for the map structure used to store patterns. + using Map = std::map<std::string, + std::map<std::string, std::vector<MatchingPattern>>>; - // Setter for loaded patterns from external storage. - void SetPatterns( - const std::map<std::string, - std::map<std::string, std::vector<MatchingPattern>>>& - patterns); + // Returns a reference to the global Pattern Provider. + static PatternProvider& GetInstance(); + + // Setter for loading patterns from external storage. + void SetPatterns(const Map patterns, + const base::Version version, + const bool overwrite_equal_version); // Provides us with all patterns that can match our field type and page // language. @@ -40,19 +49,38 @@ const std::vector<MatchingPattern>& GetAllPatternsBaseOnType( ServerFieldType type); - private: + protected: PatternProvider(); ~PatternProvider(); + // Local map to store a vector of patterns keyed by field type and + // page language. + Map patterns_; + + // Version for keeping track which pattern set is in use. + base::Version pattern_version_; + + // Sets a provider to be used for tests. + static void SetPatternProviderForTesting(PatternProvider* pattern_provider); + + // Resets the provider pointer if the object behind it gets deleted. + static void ResetPatternProvider(); + + private: // Func to sort the incoming map by score. void SortPatternsByScore(std::vector<MatchingPattern>& patterns); - // Local map to store a vector of patterns keyed by field type and - // page language. - std::map<std::string, std::map<std::string, std::vector<MatchingPattern>>> - patterns_; + // Sequence checker to ensure thread-safety for pattern swapping. + // All functions accessing the |patterns_| member variable are + // expected to be called from the UI thread. + SEQUENCE_CHECKER(sequence_checker_); + + FRIEND_TEST_ALL_PREFIXES(AutofillPatternProviderPipelineTest, + TestParsingEquivalent); + FRIEND_TEST_ALL_PREFIXES(AutofillPatternProviderPipelineTest, + DefaultPatternProviderLoads); friend class base::NoDestructor<PatternProvider>; }; } // namespace autofill -#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_PATTERN_PROVIDER_PATTERN_PROVIDER_H_ \ No newline at end of file +#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_PATTERN_PROVIDER_PATTERN_PROVIDER_H_
diff --git a/components/autofill/core/browser/pattern_provider/pattern_provider_unittest.cc b/components/autofill/core/browser/pattern_provider/pattern_provider_unittest.cc index 7259a75d..c9a3140 100644 --- a/components/autofill/core/browser/pattern_provider/pattern_provider_unittest.cc +++ b/components/autofill/core/browser/pattern_provider/pattern_provider_unittest.cc
@@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "components/autofill/core/browser/pattern_provider/pattern_provider.h" - #include <stddef.h> #include <map> @@ -11,10 +9,46 @@ #include <vector> #include "base/test/gtest_util.h" +#include "base/test/task_environment.h" +#include "components/autofill/core/browser/autofill_type.h" +#include "components/autofill/core/browser/pattern_provider/pattern_configuration_parser.h" +#include "components/autofill/core/browser/pattern_provider/pattern_provider.h" +#include "components/autofill/core/browser/pattern_provider/test_pattern_provider.h" +#include "services/data_decoder/public/cpp/test_support/in_process_data_decoder.h" #include "testing/gtest/include/gtest/gtest.h" namespace autofill { +namespace { + +// Pattern Provider with custom values set for testing. +class UnitTestPatternProvider : public PatternProvider { + public: + UnitTestPatternProvider(); + ~UnitTestPatternProvider(); +}; + +UnitTestPatternProvider::UnitTestPatternProvider() { + auto& company_patterns = patterns_[AutofillType(COMPANY_NAME).ToString()]; + company_patterns["EN"].push_back(GetCompanyPatternEn()); + company_patterns["DE"].push_back(GetCompanyPatternDe()); + + PatternProvider::SetPatternProviderForTesting(this); +} + +UnitTestPatternProvider::~UnitTestPatternProvider() { + PatternProvider::ResetPatternProvider(); +} + +} // namespace + +class AutofillPatternProviderTest : public testing::Test { + protected: + UnitTestPatternProvider pattern_provider_; + + ~AutofillPatternProviderTest() override = default; +}; + bool operator==(const MatchingPattern& mp1, const MatchingPattern& mp2) { return (mp1.language == mp2.language && mp1.match_field_attributes == mp2.match_field_attributes && @@ -25,15 +59,50 @@ mp1.positive_score == mp2.positive_score); } -TEST(AutofillPatternProvider, Single_Match) { +TEST_F(AutofillPatternProviderTest, Single_Match) { MatchingPattern kCompanyPatternEn = GetCompanyPatternEn(); MatchingPattern kCompanyPatternDe = GetCompanyPatternDe(); - PatternProvider* pattern_provider = PatternProvider::getInstance(); + PatternProvider& pattern_provider = PatternProvider::GetInstance(); - ASSERT_TRUE(pattern_provider->GetMatchPatterns("COMPANY_NAME", "EN").size() > + ASSERT_TRUE(pattern_provider.GetMatchPatterns("COMPANY_NAME", "EN").size() > 0); - EXPECT_EQ(pattern_provider->GetMatchPatterns("COMPANY_NAME", "EN")[0], + EXPECT_EQ(pattern_provider.GetMatchPatterns("COMPANY_NAME", "EN")[0], kCompanyPatternEn); } -} // namespace autofill \ No newline at end of file +// Test that the default pattern provider loads without crashing. +TEST(AutofillPatternProviderPipelineTest, DefaultPatternProviderLoads) { + base::test::TaskEnvironment task_environment_; + data_decoder::test::InProcessDataDecoder in_process_data_decoder_; + + base::RunLoop run_loop; + field_type_parsing::PopulateFromResourceBundle(run_loop.QuitClosure()); + run_loop.Run(); + PatternProvider& default_pattern_provider = PatternProvider::GetInstance(); + + EXPECT_FALSE(default_pattern_provider.patterns_.empty()); + + // Call the getter to ensure sequence checks work correctly. + default_pattern_provider.GetMatchPatterns("EMAIL_ADDRESS", "en"); +} + +// Test that the TestPatternProvider class uses a PatternProvider::Map +// equivalent to the DefaultPatternProvider. This is also an example of what is +// needed to test the DefaultPatternProvider. Warning: If this crashes, check +// that no state carried over from other tests using the singleton. +TEST(AutofillPatternProviderPipelineTest, TestParsingEquivalent) { + base::test::TaskEnvironment task_environment_; + data_decoder::test::InProcessDataDecoder in_process_data_decoder_; + + base::RunLoop run_loop; + field_type_parsing::PopulateFromResourceBundle(run_loop.QuitClosure()); + run_loop.Run(); + PatternProvider& default_pattern_provider = PatternProvider::GetInstance(); + + TestPatternProvider test_pattern_provider; + + EXPECT_EQ(default_pattern_provider.patterns_, + test_pattern_provider.patterns_); +} + +} // namespace autofill
diff --git a/components/autofill/core/browser/pattern_provider/resources/regex_patterns.json b/components/autofill/core/browser/pattern_provider/resources/regex_patterns.json new file mode 100644 index 0000000..95ca87f70 --- /dev/null +++ b/components/autofill/core/browser/pattern_provider/resources/regex_patterns.json
@@ -0,0 +1,2866 @@ +{ + "ATTENTION_IGNORED": { + "en": [ + { + "pattern_identifier": "en_attention_ignored_preserving", + "positive_pattern": "attention|attn", + "positive_score": 1.1, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ] + }, + "REGION_IGNORED": { + "en": [ + { + "pattern_identifier": "en_region_ignored_preserving", + "positive_pattern": "province|region|other", + "positive_score": 1.1, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ], + "es": [ + { + "pattern_identifier": "es_region_ignored_preserving", + "positive_pattern": "provincia", + "positive_score": 1.1, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ], + "pt": [ + { + "pattern_identifier": "pt_region_ignored_preserving", + "positive_pattern": "bairro|suburb", + "positive_score": 1.1, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ] + }, + "ADDRESS_NAME_IGNORED": { + "en": [ + { + "pattern_identifier": "en_address_name_ignored_preserving", + "positive_pattern": "address.*nickname|address.*label", + "positive_score": 1.1, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ] + }, + "COMPANY": { + "en": [ + { + "pattern_identifier": "en_company_preserving", + "positive_pattern": "company|business|organization|organisation", + "positive_score": 1.1, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ], + "de": [ + { + "pattern_identifier": "de_company_preserving", + "positive_pattern": "(?<!con)firma|firmenname", + "positive_score": 1.1, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ], + "es": [ + { + "pattern_identifier": "es_company_preserving", + "positive_pattern": "empresa", + "positive_score": 1.1, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ], + "fr": [ + { + "pattern_identifier": "fr_company_preserving", + "positive_pattern": "societe|société", + "positive_score": 1.1, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ], + "it": [ + { + "pattern_identifier": "it_company_preserving", + "positive_pattern": "ragione.?sociale", + "positive_score": 1.1, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ], + "ja": [ + { + "pattern_identifier": "ja_company_preserving", + "positive_pattern": "会社", + "positive_score": 1.1, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ], + "ru": [ + { + "pattern_identifier": "ru_company_preserving", + "positive_pattern": "название.?компании", + "positive_score": 1.1, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ], + "zh": [ + { + "pattern_identifier": "zh_company_preserving", + "positive_pattern": "单位|公司", + "positive_score": 1.1, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ], + "fa": [ + { + "pattern_identifier": "fa_company_preserving", + "positive_pattern": "شرکت", + "positive_score": 1.1, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ], + "ko": [ + { + "pattern_identifier": "ko_company_preserving", + "positive_pattern": "회사|직장", + "positive_score": 1.1, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ] + }, + "ADDRESS_LINE_1": { + "en": [ + { + "pattern_identifier": "en_address_line_1_preserving", + "positive_pattern": "^address$|address[_-]?line(one)?|address1|addr1|street|(?:shipping|billing)address$|house.?name", + "positive_score": 1.1, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 68 + } + ], + "de": [ + { + "pattern_identifier": "de_address_line_1_preserving", + "positive_pattern": "strasse|straße", + "positive_score": 1.1, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 68 + } + ], + "es": [ + { + "pattern_identifier": "es_address_line_1_preserving", + "positive_pattern": "direccion|dirección", + "positive_score": 1.1, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 68 + } + ], + "fr": [ + { + "pattern_identifier": "fr_address_line_1_preserving", + "positive_pattern": "adresse", + "positive_score": 1.1, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 68 + } + ], + "it": [ + { + "pattern_identifier": "it_address_line_1_preserving", + "positive_pattern": "indirizzo", + "positive_score": 1.1, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 68 + } + ], + "ja": [ + { + "pattern_identifier": "ja_address_line_1_preserving", + "positive_pattern": "^住所$|住所1", + "positive_score": 1.1, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 68 + } + ], + "pt": [ + { + "pattern_identifier": "pt_address_line_1_preserving", + "positive_pattern": "morada|((?<!identificação do )endereço)", + "positive_score": 1.1, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 68 + } + ], + "ru": [ + { + "pattern_identifier": "ru_address_line_1_preserving", + "positive_pattern": "Адрес", + "positive_score": 1.1, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 68 + } + ], + "zh": [ + { + "pattern_identifier": "zh_address_line_1_preserving", + "positive_pattern": "地址", + "positive_score": 1.1, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 68 + } + ], + "tr": [ + { + "pattern_identifier": "tr_address_line_1_preserving", + "positive_pattern": "(\\b|_)adres(?! (başlığı(nız)?|tarifi))(\\b|_)", + "positive_score": 1.1, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 68 + } + ], + "ko": [ + { + "pattern_identifier": "ko_address_line_1_preserving", + "positive_pattern": "^주소.?$|주소.?1", + "positive_score": 1.1, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 68 + } + ] + }, + "ADDRESS_LINE_1_LABEL": { + "en": [ + { + "pattern_identifier": "en_address_line_1_label_preserving", + "positive_pattern": "(^\\W*address)|(address\\W*$)|(?:shipping|billing|mailing|pick.?up|drop.?off|delivery|sender|postal|recipient|home|work|office|school|business|mail)[\\s\\-]+address|address\\s+(of|for|to|from)", + "positive_score": 1.1, + "negative_pattern": "", + "match_field_attributes": 1, + "match_field_input_types": 68 + } + ], + "fr": [ + { + "pattern_identifier": "fr_address_line_1_label_preserving", + "positive_pattern": "adresse", + "positive_score": 1.1, + "negative_pattern": "", + "match_field_attributes": 1, + "match_field_input_types": 68 + } + ], + "it": [ + { + "pattern_identifier": "it_address_line_1_label_preserving", + "positive_pattern": "indirizzo", + "positive_score": 1.1, + "negative_pattern": "", + "match_field_attributes": 1, + "match_field_input_types": 68 + } + ], + "ja": [ + { + "pattern_identifier": "ja_address_line_1_label_preserving", + "positive_pattern": "住所", + "positive_score": 1.1, + "negative_pattern": "", + "match_field_attributes": 1, + "match_field_input_types": 68 + } + ], + "zh": [ + { + "pattern_identifier": "zh_address_line_1_label_preserving", + "positive_pattern": "地址", + "positive_score": 1.1, + "negative_pattern": "", + "match_field_attributes": 1, + "match_field_input_types": 68 + } + ], + "tr": [ + { + "pattern_identifier": "tr_address_line_1_label_preserving", + "positive_pattern": "(\\b|_)adres(?! (başlığı(nız)?|tarifi))(\\b|_)", + "positive_score": 1.1, + "negative_pattern": "", + "match_field_attributes": 1, + "match_field_input_types": 68 + } + ], + "ko": [ + { + "pattern_identifier": "ko_address_line_1_label_preserving", + "positive_pattern": "주소", + "positive_score": 1.1, + "negative_pattern": "", + "match_field_attributes": 1, + "match_field_input_types": 68 + } + ] + }, + "ADDRESS_LINE_2": { + "en": [ + { + "pattern_identifier": "en_address_line_2_preserving", + "positive_pattern": "address[_-]?line(2|two)|address2|addr2|street|suite|unit", + "positive_score": 1.1, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ], + "de": [ + { + "pattern_identifier": "de_address_line_2_preserving", + "positive_pattern": "adresszusatz|ergänzende.?angaben", + "positive_score": 1.1, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ], + "es": [ + { + "pattern_identifier": "es_address_line_2_preserving", + "positive_pattern": "direccion2|colonia|adicional", + "positive_score": 1.1, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ], + "fr": [ + { + "pattern_identifier": "fr_address_line_2_preserving", + "positive_pattern": "addresssuppl|complementnom|appartement", + "positive_score": 1.1, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ], + "it": [ + { + "pattern_identifier": "it_address_line_2_preserving", + "positive_pattern": "indirizzo2", + "positive_score": 1.1, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ], + "ja": [ + { + "pattern_identifier": "ja_address_line_2_preserving", + "positive_pattern": "住所2", + "positive_score": 1.1, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ], + "pt": [ + { + "pattern_identifier": "pt_address_line_2_preserving", + "positive_pattern": "complemento|addrcomplement", + "positive_score": 1.1, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ], + "ru": [ + { + "pattern_identifier": "ru_address_line_2_preserving", + "positive_pattern": "Улица", + "positive_score": 1.1, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ], + "zh": [ + { + "pattern_identifier": "zh_address_line_2_preserving", + "positive_pattern": "地址2", + "positive_score": 1.1, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ], + "ko": [ + { + "pattern_identifier": "ko_address_line_2_preserving", + "positive_pattern": "주소.?2", + "positive_score": 1.1, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ] + }, + "ADDRESS_LINE_2_LABEL": { + "en": [ + { + "pattern_identifier": "en_address_line_2_label_preserving", + "positive_pattern": "address|line", + "positive_score": 1.1, + "negative_pattern": "", + "match_field_attributes": 1, + "match_field_input_types": 4 + } + ], + "fr": [ + { + "pattern_identifier": "fr_address_line_2_label_preserving", + "positive_pattern": "adresse", + "positive_score": 1.1, + "negative_pattern": "", + "match_field_attributes": 1, + "match_field_input_types": 4 + } + ], + "it": [ + { + "pattern_identifier": "it_address_line_2_label_preserving", + "positive_pattern": "indirizzo", + "positive_score": 1.1, + "negative_pattern": "", + "match_field_attributes": 1, + "match_field_input_types": 4 + } + ], + "zh": [ + { + "pattern_identifier": "zh_address_line_2_label_preserving", + "positive_pattern": "地址", + "positive_score": 1.1, + "negative_pattern": "", + "match_field_attributes": 1, + "match_field_input_types": 4 + } + ], + "ko": [ + { + "pattern_identifier": "ko_address_line_2_label_preserving", + "positive_pattern": "주소", + "positive_score": 1.1, + "negative_pattern": "", + "match_field_attributes": 1, + "match_field_input_types": 4 + } + ] + }, + "ADDRESS_LINE_EXTRA": { + "en": [ + { + "pattern_identifier": "en_address_line_extra_preserving", + "positive_pattern": "address.*line[3-9]|address[3-9]|addr[3-9]|street|line[3-9]", + "positive_score": 1.1, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ], + "es": [ + { + "pattern_identifier": "es_address_line_extra_preserving", + "positive_pattern": "municipio", + "positive_score": 1.1, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ], + "fr": [ + { + "pattern_identifier": "fr_address_line_extra_preserving", + "positive_pattern": "batiment|residence", + "positive_score": 1.1, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ], + "it": [ + { + "pattern_identifier": "it_address_line_extra_preserving", + "positive_pattern": "indirizzo[3-9]", + "positive_score": 1.1, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ] + }, + "ADDRESS_LOOKUP": { + "en": [ + { + "pattern_identifier": "en_address_lookup_preserving", + "positive_pattern": "lookup", + "positive_score": 1.1, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ] + }, + "COUNTRY": { + "en": [ + { + "pattern_identifier": "en_country_preserving", + "positive_pattern": "country|countries", + "positive_score": 1.1, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 548 + } + ], + "es": [ + { + "pattern_identifier": "es_country_preserving", + "positive_pattern": "país|pais", + "positive_score": 1.1, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 548 + } + ], + "de": [ + { + "pattern_identifier": "de_country_preserving", + "positive_pattern": "(\\b|_)land(\\b|_)(?!.*(mark.*))", + "positive_score": 1.1, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 548 + } + ], + "ja": [ + { + "pattern_identifier": "ja_country_preserving", + "positive_pattern": "(?<!(入|出))国", + "positive_score": 1.1, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 548 + } + ], + "zh": [ + { + "pattern_identifier": "zh_country_preserving", + "positive_pattern": "国家", + "positive_score": 1.1, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 548 + } + ], + "ko": [ + { + "pattern_identifier": "ko_country_preserving", + "positive_pattern": "국가|나라", + "positive_score": 1.1, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 548 + } + ], + "tr": [ + { + "pattern_identifier": "tr_country_preserving", + "positive_pattern": "(\\b|_)(ülke|ulce|ulke)(\\b|_)", + "positive_score": 1.1, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 548 + } + ], + "fa": [ + { + "pattern_identifier": "fa_country_preserving", + "positive_pattern": "کشور", + "positive_score": 1.1, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 548 + } + ] + }, + "COUNTRY_LOCATION": { + "en": [ + { + "pattern_identifier": "en_country_location_preserving", + "positive_pattern": "location", + "positive_score": 1.1, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 544 + } + ] + }, + "ZIP_CODE": { + "en": [ + { + "pattern_identifier": "en_zip_code_preserving", + "positive_pattern": "zip|postal|post.*code|pcode|pin.?code", + "positive_score": 1.1, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 276 + } + ], + "de": [ + { + "pattern_identifier": "de_zip_code_preserving", + "positive_pattern": "postleitzahl", + "positive_score": 1.1, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 276 + } + ], + "es": [ + { + "pattern_identifier": "es_zip_code_preserving", + "positive_pattern": "\\bcp\\b", + "positive_score": 1.1, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 276 + } + ], + "fr": [ + { + "pattern_identifier": "fr_zip_code_preserving", + "positive_pattern": "\\bcdp\\b", + "positive_score": 1.1, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 276 + } + ], + "it": [ + { + "pattern_identifier": "it_zip_code_preserving", + "positive_pattern": "\\bcap\\b", + "positive_score": 1.1, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 276 + } + ], + "ja": [ + { + "pattern_identifier": "ja_zip_code_preserving", + "positive_pattern": "郵便番号", + "positive_score": 1.1, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 276 + } + ], + "pt": [ + { + "pattern_identifier": "pt_zip_code_preserving", + "positive_pattern": "codigo|codpos|\\bcep\\b", + "positive_score": 1.1, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 276 + } + ], + "ru": [ + { + "pattern_identifier": "ru_zip_code_preserving", + "positive_pattern": "Почтовый.?Индекс", + "positive_score": 1.1, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 276 + } + ], + "hi": [ + { + "pattern_identifier": "hi_zip_code_preserving", + "positive_pattern": "पिन.?कोड", + "positive_score": 1.1, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 276 + } + ], + "ml": [ + { + "pattern_identifier": "ml_zip_code_preserving", + "positive_pattern": "പിന്കോഡ്", + "positive_score": 1.1, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 276 + } + ], + "zh": [ + { + "pattern_identifier": "zh_zip_code_preserving", + "positive_pattern": "邮政编码|邮编|郵遞區號", + "positive_score": 1.1, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 276 + } + ], + "tr": [ + { + "pattern_identifier": "tr_zip_code_preserving", + "positive_pattern": "(\\b|_)posta kodu(\\b|_)", + "positive_score": 1.1, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 276 + } + ], + "ko": [ + { + "pattern_identifier": "ko_zip_code_preserving", + "positive_pattern": "우편.?번호", + "positive_score": 1.1, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 276 + } + ] + }, + "ZIP_4": { + "en": [ + { + "pattern_identifier": "en_zip_4_preserving", + "positive_pattern": "zip|^-$|post2", + "positive_score": 1.1, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 276 + } + ], + "pt": [ + { + "pattern_identifier": "pt_zip_4_preserving", + "positive_pattern": "codpos2", + "positive_score": 1.1, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 276 + } + ] + }, + "CITY": { + "en": [ + { + "pattern_identifier": "en_city_preserving", + "positive_pattern": "city|town|suburb", + "positive_score": 1.1, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 548 + } + ], + "de": [ + { + "pattern_identifier": "de_city_preserving", + "positive_pattern": "\\bort\\b|stadt", + "positive_score": 1.1, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 548 + } + ], + "es": [ + { + "pattern_identifier": "es_city_preserving", + "positive_pattern": "ciudad|provincia|localidad|poblacion", + "positive_score": 1.1, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 548 + } + ], + "fr": [ + { + "pattern_identifier": "fr_city_preserving", + "positive_pattern": "ville|commune", + "positive_score": 1.1, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 548 + } + ], + "it": [ + { + "pattern_identifier": "it_city_preserving", + "positive_pattern": "localita", + "positive_score": 1.1, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 548 + } + ], + "ja": [ + { + "pattern_identifier": "ja_city_preserving", + "positive_pattern": "市区町村", + "positive_score": 1.1, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 548 + } + ], + "pt": [ + { + "pattern_identifier": "pt_city_preserving", + "positive_pattern": "cidade", + "positive_score": 1.1, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 548 + } + ], + "ru": [ + { + "pattern_identifier": "ru_city_preserving", + "positive_pattern": "Город", + "positive_score": 1.1, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 548 + } + ], + "zh": [ + { + "pattern_identifier": "zh_city_preserving", + "positive_pattern": "市|分區", + "positive_score": 1.1, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 548 + } + ], + "fa": [ + { + "pattern_identifier": "fa_city_preserving", + "positive_pattern": "شهر", + "positive_score": 1.1, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 548 + } + ], + "hi": [ + { + "pattern_identifier": "hi_city_preserving", + "positive_pattern": "शहर|ग्राम|गाँव", + "positive_score": 1.1, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 548 + } + ], + "ml": [ + { + "pattern_identifier": "ml_city_preserving", + "positive_pattern": "നഗരം|ഗ്രാമം", + "positive_score": 1.1, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 548 + } + ], + "tr": [ + { + "pattern_identifier": "tr_city_preserving", + "positive_pattern": "((\\b|_|\\*)([İii̇]l[cç]e(miz|niz)?)(\\b|_|\\*))", + "positive_score": 1.1, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 548 + } + ], + "ko": [ + { + "pattern_identifier": "ko_city_preserving", + "positive_pattern": "^시[^도·・]|시[·・]?군[·・]?구", + "positive_score": 1.1, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 548 + } + ] + }, + "STATE": { + "en": [ + { + "pattern_identifier": "en_state_preserving", + "positive_pattern": "(?<!(united|hist|history).?)state|county|region|province|county|principality", + "positive_score": 1.1, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 548 + } + ], + "ja": [ + { + "pattern_identifier": "ja_state_preserving", + "positive_pattern": "都道府県", + "positive_score": 1.1, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 548 + } + ], + "pt": [ + { + "pattern_identifier": "pt_state_preserving", + "positive_pattern": "estado|provincia", + "positive_score": 1.1, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 548 + } + ], + "ru": [ + { + "pattern_identifier": "ru_state_preserving", + "positive_pattern": "область", + "positive_score": 1.1, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 548 + } + ], + "zh": [ + { + "pattern_identifier": "zh_state_preserving", + "positive_pattern": "省|地區", + "positive_score": 1.1, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 548 + } + ], + "ml": [ + { + "pattern_identifier": "ml_state_preserving", + "positive_pattern": "സംസ്ഥാനം", + "positive_score": 1.1, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 548 + } + ], + "fa": [ + { + "pattern_identifier": "fa_state_preserving", + "positive_pattern": "استان", + "positive_score": 1.1, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 548 + } + ], + "hi": [ + { + "pattern_identifier": "hi_state_preserving", + "positive_pattern": "राज्य", + "positive_score": 1.1, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 548 + } + ], + "tr": [ + { + "pattern_identifier": "tr_state_preserving", + "positive_pattern": "((\\b|_|\\*)(eyalet|[şs]ehir|[İii̇]l(imiz)?|kent)(\\b|_|\\*))", + "positive_score": 1.1, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 548 + } + ], + "ko": [ + { + "pattern_identifier": "ko_state_preserving", + "positive_pattern": "^시[·・]?도", + "positive_score": 1.1, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 548 + } + ] + }, + "SEARCH_TERM": { + "en": [ + { + "pattern_identifier": "en_search_term_preserving", + "positive_pattern": "^q$|search|query|qry", + "positive_score": 0.8, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 580 + } + ], + "de": [ + { + "pattern_identifier": "de_search_term_preserving", + "positive_pattern": "suche.*", + "positive_score": 0.8, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 580 + } + ], + "zh": [ + { + "pattern_identifier": "zh_search_term_preserving", + "positive_pattern": "搜索", + "positive_score": 0.8, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 580 + } + ], + "ja": [ + { + "pattern_identifier": "ja_search_term_preserving", + "positive_pattern": "探す|検索", + "positive_score": 0.8, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 580 + } + ], + "fr": [ + { + "pattern_identifier": "fr_search_term_preserving", + "positive_pattern": "recherch.*", + "positive_score": 0.8, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 580 + } + ], + "pt": [ + { + "pattern_identifier": "pt_search_term_preserving", + "positive_pattern": "busca", + "positive_score": 0.8, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 580 + } + ], + "fa": [ + { + "pattern_identifier": "fa_search_term_preserving", + "positive_pattern": "جستجو", + "positive_score": 0.8, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 580 + } + ], + "ru": [ + { + "pattern_identifier": "ru_search_term_preserving", + "positive_pattern": "искать|найти|поиск", + "positive_score": 0.8, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 580 + } + ] + }, + "PRICE": { + "en": [ + { + "pattern_identifier": "en_price_preserving", + "positive_pattern": "\\bprice\\b|\\brate\\b|\\bcost\\b", + "positive_score": 0.95, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 868 + } + ], + "ar": [ + { + "pattern_identifier": "ar_price_preserving", + "positive_pattern": "قیمة|سعر", + "positive_score": 0.95, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 868 + } + ], + "fa": [ + { + "pattern_identifier": "fa_price_preserving", + "positive_pattern": "قیمت", + "positive_score": 0.95, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 868 + } + ], + "fr": [ + { + "pattern_identifier": "fr_price_preserving", + "positive_pattern": "\\bprix\\b|\\bcoût\\b|\\bcout\\b|\\btarif\\b", + "positive_score": 0.95, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 868 + } + ] + }, + "NAME_ON_CARD": { + "en": [ + { + "pattern_identifier": "en_name_on_card_preserving", + "positive_pattern": "card.?(?:holder|owner)|name.*(\\b)?on(\\b)?.*card|(?:card|cc).?name|cc.?full.?name", + "positive_score": 1.0, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ], + "de": [ + { + "pattern_identifier": "de_name_on_card_preserving", + "positive_pattern": "karteninhaber", + "positive_score": 1.0, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ], + "es": [ + { + "pattern_identifier": "es_name_on_card_preserving", + "positive_pattern": "nombre.*tarjeta", + "positive_score": 1.0, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ], + "fr": [ + { + "pattern_identifier": "fr_name_on_card_preserving", + "positive_pattern": "nom.*carte", + "positive_score": 1.0, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ], + "it": [ + { + "pattern_identifier": "it_name_on_card_preserving", + "positive_pattern": "nome.*cart", + "positive_score": 1.0, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ], + "ja": [ + { + "pattern_identifier": "ja_name_on_card_preserving", + "positive_pattern": "名前", + "positive_score": 1.0, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ], + "ru": [ + { + "pattern_identifier": "ru_name_on_card_preserving", + "positive_pattern": "Имя.*карты", + "positive_score": 1.0, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ], + "zh": [ + { + "pattern_identifier": "zh_name_on_card_preserving", + "positive_pattern": "信用卡开户名|开户名|持卡人姓名|持卡人姓名", + "positive_score": 1.0, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ] + }, + "NAME_ON_CARD_CONTEXTUAL": { + "en": [ + { + "pattern_identifier": "en_name_on_card_contextual_preserving", + "positive_pattern": "name", + "positive_score": 1.0, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ] + }, + "CARD_NUMBER": { + "en": [ + { + "pattern_identifier": "en_card_number_preserving", + "positive_pattern": "(add)?(?:card|cc|acct).?(?:number|#|no|num|field)", + "positive_score": 1.0, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 404 + } + ], + "de": [ + { + "pattern_identifier": "de_card_number_preserving", + "positive_pattern": "(?<!telefon|haus|person|fødsels)nummer", + "positive_score": 1.0, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 404 + } + ], + "ja": [ + { + "pattern_identifier": "ja_card_number_preserving", + "positive_pattern": "カード番号", + "positive_score": 1.0, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 404 + } + ], + "ru": [ + { + "pattern_identifier": "ru_card_number_preserving", + "positive_pattern": "Номер.*карты", + "positive_score": 1.0, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 404 + } + ], + "zh": [ + { + "pattern_identifier": "zh_card_number_preserving", + "positive_pattern": "信用卡号|信用卡号码|信用卡卡號", + "positive_score": 1.0, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 404 + } + ], + "ko": [ + { + "pattern_identifier": "ko_card_number_preserving", + "positive_pattern": "카드", + "positive_score": 1.0, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 404 + } + ], + "es": [ + { + "pattern_identifier": "es_card_number_preserving", + "positive_pattern": "(numero|número|numéro)(?!.*(document|fono|phone|réservation))", + "positive_score": 1.0, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 404 + } + ], + "pt": [ + { + "pattern_identifier": "pt_card_number_preserving", + "positive_pattern": "(numero|número|numéro)(?!.*(document|fono|phone|réservation))", + "positive_score": 1.0, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 404 + } + ], + "fr": [ + { + "pattern_identifier": "fr_card_number_preserving", + "positive_pattern": "(numero|número|numéro)(?!.*(document|fono|phone|réservation))", + "positive_score": 1.0, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 404 + } + ] + }, + "CARD_CVC": { + "en": [ + { + "pattern_identifier": "en_card_cvc_preserving", + "positive_pattern": "verification|card.?identification|security.?code|card.?code|security.?value|security.?number|card.?pin|c-v-v|(cvn|cvv|cvc|csc|cvd|cid|ccv)(field)?|\\bcid\\b", + "positive_score": 1.0, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 404 + } + ] + }, + "CARD_EXP_MONTH": { + "en": [ + { + "pattern_identifier": "en_card_exp_month_preserving", + "positive_pattern": "expir|exp.*mo|exp.*date|ccmonth|cardmonth|addmonth", + "positive_score": 1.0, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 948 + } + ], + "de": [ + { + "pattern_identifier": "de_card_exp_month_preserving", + "positive_pattern": "gueltig|gültig|monat", + "positive_score": 1.0, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 948 + } + ], + "es": [ + { + "pattern_identifier": "es_card_exp_month_preserving", + "positive_pattern": "fecha", + "positive_score": 1.0, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 948 + } + ], + "fr": [ + { + "pattern_identifier": "fr_card_exp_month_preserving", + "positive_pattern": "date.*exp", + "positive_score": 1.0, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 948 + } + ], + "it": [ + { + "pattern_identifier": "it_card_exp_month_preserving", + "positive_pattern": "scadenza", + "positive_score": 1.0, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 948 + } + ], + "ja": [ + { + "pattern_identifier": "ja_card_exp_month_preserving", + "positive_pattern": "有効期限", + "positive_score": 1.0, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 948 + } + ], + "pt": [ + { + "pattern_identifier": "pt_card_exp_month_preserving", + "positive_pattern": "validade", + "positive_score": 1.0, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 948 + } + ], + "ru": [ + { + "pattern_identifier": "ru_card_exp_month_preserving", + "positive_pattern": "Срок действия карты", + "positive_score": 1.0, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 948 + } + ], + "zh": [ + { + "pattern_identifier": "zh_card_exp_month_preserving", + "positive_pattern": "月", + "positive_score": 1.0, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 948 + } + ] + }, + "CARD_EXP_YEAR": { + "en": [ + { + "pattern_identifier": "en_card_exp_year_preserving", + "positive_pattern": "exp|^/|(add)?year", + "positive_score": 1.0, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 948 + } + ], + "de": [ + { + "pattern_identifier": "de_card_exp_year_preserving", + "positive_pattern": "ablaufdatum|gueltig|gültig|jahr", + "positive_score": 1.0, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 948 + } + ], + "es": [ + { + "pattern_identifier": "es_card_exp_year_preserving", + "positive_pattern": "fecha", + "positive_score": 1.0, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 948 + } + ], + "it": [ + { + "pattern_identifier": "it_card_exp_year_preserving", + "positive_pattern": "scadenza", + "positive_score": 1.0, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 948 + } + ], + "ja": [ + { + "pattern_identifier": "ja_card_exp_year_preserving", + "positive_pattern": "有効期限", + "positive_score": 1.0, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 948 + } + ], + "pt": [ + { + "pattern_identifier": "pt_card_exp_year_preserving", + "positive_pattern": "validade", + "positive_score": 1.0, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 948 + } + ], + "ru": [ + { + "pattern_identifier": "ru_card_exp_year_preserving", + "positive_pattern": "Срок действия карты", + "positive_score": 1.0, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 948 + } + ], + "zh": [ + { + "pattern_identifier": "zh_card_exp_year_preserving", + "positive_pattern": "年|有效期", + "positive_score": 1.0, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 948 + } + ] + }, + "CARD_EXP_DATE_2_DIGIT_YEAR": { + "en": [ + { + "pattern_identifier": "en_card_exp_date_2_digit_year_preserving", + "positive_pattern": "(?:exp.*date[^y\\n\\r]*|mm\\s*[-/]?\\s*)yy(?:[^y]|$)", + "positive_score": 1.0, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 948 + } + ] + }, + "CARD_EXP_DATE_4_DIGIT_YEAR": { + "en": [ + { + "pattern_identifier": "en_card_exp_date_4_digit_year_preserving", + "positive_pattern": "(?:exp.*date[^y\\n\\r]*|mm\\s*[-/]?\\s*)yyyy(?:[^y]|$)", + "positive_score": 1.0, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 948 + } + ] + }, + "CARD_EXP_DATE": { + "en": [ + { + "pattern_identifier": "en_card_exp_date_preserving", + "positive_pattern": "expir|exp.*date|^expfield$", + "positive_score": 1.0, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 948 + } + ], + "de": [ + { + "pattern_identifier": "de_card_exp_date_preserving", + "positive_pattern": "gueltig|gültig", + "positive_score": 1.0, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 948 + } + ], + "es": [ + { + "pattern_identifier": "es_card_exp_date_preserving", + "positive_pattern": "fecha", + "positive_score": 1.0, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 948 + } + ], + "fr": [ + { + "pattern_identifier": "fr_card_exp_date_preserving", + "positive_pattern": "date.*exp", + "positive_score": 1.0, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 948 + } + ], + "it": [ + { + "pattern_identifier": "it_card_exp_date_preserving", + "positive_pattern": "scadenza", + "positive_score": 1.0, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 948 + } + ], + "ja": [ + { + "pattern_identifier": "ja_card_exp_date_preserving", + "positive_pattern": "有効期限", + "positive_score": 1.0, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 948 + } + ], + "pt": [ + { + "pattern_identifier": "pt_card_exp_date_preserving", + "positive_pattern": "validade", + "positive_score": 1.0, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 948 + } + ], + "ru": [ + { + "pattern_identifier": "ru_card_exp_date_preserving", + "positive_pattern": "Срок действия карты", + "positive_score": 1.0, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 948 + } + ] + }, + "GIFT_CARD": { + "en": [ + { + "pattern_identifier": "en_gift_card_preserving", + "positive_pattern": "gift.?(card|cert)", + "positive_score": 1.0, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 788 + } + ] + }, + "DEBIT_GIFT_CARD": { + "en": [ + { + "pattern_identifier": "en_debit_gift_card_preserving", + "positive_pattern": "(?:visa|mastercard|discover|amex|american express).*gift.?card", + "positive_score": 1.0, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 788 + } + ] + }, + "DEBIT_CARD": { + "en": [ + { + "pattern_identifier": "en_debit_card_preserving", + "positive_pattern": "debit.*card", + "positive_score": 1.0, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 788 + } + ] + }, + "DAY": { + "en": [ + { + "pattern_identifier": "en_day_preserving", + "positive_pattern": "day", + "positive_score": 1.0, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 36 + } + ] + }, + "EMAIL_ADDRESS": { + "en": [ + { + "pattern_identifier": "en_email_preserving", + "positive_pattern": "e.?mail", + "positive_score": 1.4, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 12 + } + ], + "fr": [ + { + "pattern_identifier": "fr_email_preserving", + "positive_pattern": "courriel", + "positive_score": 1.4, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 12 + } + ], + "es": [ + { + "pattern_identifier": "es_email_preserving", + "positive_pattern": "correo.*electr(o|ó)nico", + "positive_score": 1.4, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 12 + } + ], + "ja": [ + { + "pattern_identifier": "ja_email_preserving", + "positive_pattern": "メールアドレス", + "positive_score": 1.4, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 12 + } + ], + "ru": [ + { + "pattern_identifier": "ru_email_preserving", + "positive_pattern": "Электронной.?Почты", + "positive_score": 1.4, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 12 + } + ], + "zh": [ + { + "pattern_identifier": "zh_email_preserving", + "positive_pattern": "邮件|邮箱|電郵地址", + "positive_score": 1.4, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 12 + } + ], + "ml": [ + { + "pattern_identifier": "ml_email_preserving", + "positive_pattern": "ഇ-മെയില്|ഇലക്ട്രോണിക്.?മെയിൽ", + "positive_score": 1.4, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 12 + } + ], + "fa": [ + { + "pattern_identifier": "fa_email_preserving", + "positive_pattern": "ایمیل|پست.*الکترونیک", + "positive_score": 1.4, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 12 + } + ], + "hi": [ + { + "pattern_identifier": "hi_email_preserving", + "positive_pattern": "ईमेल|इलॅक्ट्रॉनिक.?मेल", + "positive_score": 1.4, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 12 + } + ], + "tr": [ + { + "pattern_identifier": "tr_email_preserving", + "positive_pattern": "(\\b|_)eposta(\\b|_)", + "positive_score": 1.4, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 12 + } + ], + "ko": [ + { + "pattern_identifier": "ko_email_preserving", + "positive_pattern": "(?:이메일|전자.?우편|[Ee]-?mail)(.?주소)?", + "positive_score": 1.4, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 12 + } + ] + }, + "NAME_IGNORED": { + "en": [ + { + "pattern_identifier": "en_name_ignored_preserving", + "positive_pattern": "user.?name|user.?id|nickname|maiden name|title|prefix|suffix", + "positive_score": 0.9, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ], + "tr": [ + { + "pattern_identifier": "tr_name_ignored_preserving", + "positive_pattern": "adres başlığınız", + "positive_score": 0.9, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ], + "de": [ + { + "pattern_identifier": "de_name_ignored_preserving", + "positive_pattern": "vollständiger.?name", + "positive_score": 0.9, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ], + "zh": [ + { + "pattern_identifier": "zh_name_ignored_preserving", + "positive_pattern": "用户名", + "positive_score": 0.9, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ], + "ko": [ + { + "pattern_identifier": "ko_name_ignored_preserving", + "positive_pattern": "(?:사용자.?)?아이디|사용자.?ID", + "positive_score": 0.9, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ] + }, + "FULL_NAME": { + "en": [ + { + "pattern_identifier": "en_full_name_preserving", + "positive_pattern": "^name|full.?name|your.?name|customer.?name|bill.?name|ship.?name|name.*first.*last|firstandlastname", + "positive_score": 0.9, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ], + "es": [ + { + "pattern_identifier": "es_full_name_preserving", + "positive_pattern": "nombre.*y.*apellidos", + "positive_score": 0.9, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ], + "fr": [ + { + "pattern_identifier": "fr_full_name_preserving", + "positive_pattern": "^nom(?!bre)", + "positive_score": 0.9, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ], + "ja": [ + { + "pattern_identifier": "ja_full_name_preserving", + "positive_pattern": "お名前|氏名", + "positive_score": 0.9, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ], + "pt": [ + { + "pattern_identifier": "pt_full_name_preserving", + "positive_pattern": "^nome", + "positive_score": 0.9, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ], + "fa": [ + { + "pattern_identifier": "fa_full_name_preserving", + "positive_pattern": "نام.*نام.*خانوادگی", + "positive_score": 0.9, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ], + "zh": [ + { + "pattern_identifier": "zh_full_name_preserving", + "positive_pattern": "姓名", + "positive_score": 0.9, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ], + "tr": [ + { + "pattern_identifier": "tr_full_name_preserving", + "positive_pattern": "(\\b|_|\\*)ad[ı]? soyad[ı]?(\\b|_|\\*)", + "positive_score": 0.9, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ], + "ko": [ + { + "pattern_identifier": "ko_full_name_preserving", + "positive_pattern": "성명", + "positive_score": 0.9, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ] + }, + "NAME_SPECIFIC": { + "en": [ + { + "pattern_identifier": "en_name_specific_preserving", + "positive_pattern": "^name", + "positive_score": 0.9, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ], + "fr": [ + { + "pattern_identifier": "fr_name_specific_preserving", + "positive_pattern": "^nom", + "positive_score": 0.9, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ], + "pt": [ + { + "pattern_identifier": "pt_name_specific_preserving", + "positive_pattern": "^nome", + "positive_score": 0.9, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ] + }, + "FIRST_NAME": { + "en": [ + { + "pattern_identifier": "en_first_name_preserving", + "positive_pattern": "first.*name|initials|fname|first$|given.*name", + "positive_score": 0.9, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ], + "de": [ + { + "pattern_identifier": "de_first_name_preserving", + "positive_pattern": "vorname", + "positive_score": 0.9, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ], + "es": [ + { + "pattern_identifier": "es_first_name_preserving", + "positive_pattern": "nombre", + "positive_score": 0.9, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ], + "fr": [ + { + "pattern_identifier": "fr_first_name_preserving", + "positive_pattern": "forename|prénom|prenom", + "positive_score": 0.9, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ], + "ja": [ + { + "pattern_identifier": "ja_first_name_preserving", + "positive_pattern": "名", + "positive_score": 0.9, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ], + "pt": [ + { + "pattern_identifier": "pt_first_name_preserving", + "positive_pattern": "nome", + "positive_score": 0.9, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ], + "ru": [ + { + "pattern_identifier": "ru_first_name_preserving", + "positive_pattern": "Имя", + "positive_score": 0.9, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ], + "fa": [ + { + "pattern_identifier": "fa_first_name_preserving", + "positive_pattern": "نام", + "positive_score": 0.9, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ], + "ko": [ + { + "pattern_identifier": "ko_first_name_preserving", + "positive_pattern": "이름", + "positive_score": 0.9, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ], + "ml": [ + { + "pattern_identifier": "ml_first_name_preserving", + "positive_pattern": "പേര്", + "positive_score": 0.9, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ], + "tr": [ + { + "pattern_identifier": "tr_first_name_preserving", + "positive_pattern": "(\\b|_|\\*)(isim|ad|ad(i|ı|iniz|ınız)?)(\\b|_|\\*)", + "positive_score": 0.9, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ], + "hi": [ + { + "pattern_identifier": "hi_first_name_preserving", + "positive_pattern": "नाम", + "positive_score": 0.9, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ] + }, + "MIDDLE_INITIAL": { + "en": [ + { + "pattern_identifier": "en_middle_initial_preserving", + "positive_pattern": "middle.*initial|m\\.i\\.|mi$|\\bmi\\b", + "positive_score": 0.9, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ] + }, + "MIDDLE_NAME": { + "en": [ + { + "pattern_identifier": "en_middle_name_preserving", + "positive_pattern": "middle.*name|mname|middle$", + "positive_score": 0.9, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ] + }, + "LAST_NAME": { + "en": [ + { + "pattern_identifier": "en_last_name_preserving", + "positive_pattern": "last.*name|lname|surname(?!\\d)|last$|secondname|family.*name", + "positive_score": 0.9, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ], + "de": [ + { + "pattern_identifier": "de_last_name_preserving", + "positive_pattern": "nachname", + "positive_score": 0.9, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ], + "es": [ + { + "pattern_identifier": "es_last_name_preserving", + "positive_pattern": "apellidos?", + "positive_score": 0.9, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ], + "fr": [ + { + "pattern_identifier": "fr_last_name_preserving", + "positive_pattern": "famille|^nom(?!bre)", + "positive_score": 0.9, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ], + "it": [ + { + "pattern_identifier": "it_last_name_preserving", + "positive_pattern": "cognome", + "positive_score": 0.9, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ], + "ja": [ + { + "pattern_identifier": "ja_last_name_preserving", + "positive_pattern": "姓", + "positive_score": 0.9, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ], + "pt": [ + { + "pattern_identifier": "pt_last_name_preserving", + "positive_pattern": "apelidos|surename|sobrenome", + "positive_score": 0.9, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ], + "ru": [ + { + "pattern_identifier": "ru_last_name_preserving", + "positive_pattern": "Фамилия", + "positive_score": 0.9, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ], + "fa": [ + { + "pattern_identifier": "fa_last_name_preserving", + "positive_pattern": "نام.*خانوادگی", + "positive_score": 0.9, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ], + "hi": [ + { + "pattern_identifier": "hi_last_name_preserving", + "positive_pattern": "उपनाम", + "positive_score": 0.9, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ], + "ml": [ + { + "pattern_identifier": "ml_last_name_preserving", + "positive_pattern": "മറുപേര്", + "positive_score": 0.9, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ], + "tr": [ + { + "pattern_identifier": "tr_last_name_preserving", + "positive_pattern": "(\\b|_|\\*)(soyisim|soyad(i|ı|iniz|ınız)?)(\\b|_|\\*)", + "positive_score": 0.9, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ], + "ko": [ + { + "pattern_identifier": "ko_last_name_preserving", + "positive_pattern": "\\b성(?:[^명]|\\b)", + "positive_score": 0.9, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ] + }, + "LAST_NAME_FIRST": { + "es": [ + { + "pattern_identifier": "es_last_name_first_preserving", + "positive_pattern": "(primer.*apellido)|(apellido1)|(apellido.*paterno)|surname_?1|first(\\s|_)?surname", + "positive_score": 0.9, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ] + }, + "LAST_NAME_SECOND": { + "es": [ + { + "pattern_identifier": "es_last_name_second_preserving", + "positive_pattern": "(segund.*apellido)|(apellido2)|(apellido.*materno)|surname_?2|second(\\s|_)?surname", + "positive_score": 0.9, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ] + }, + "HONORIFIC_PREFIX": { + "en": [ + { + "pattern_identifier": "en_honorific_prefix_preserving", + "positive_pattern": "^title:?$|(salutation(?! and given name))", + "positive_score": 0.9, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ], + "de": [ + { + "pattern_identifier": "de_honorific_prefix_preserving", + "positive_pattern": "anrede|titel", + "positive_score": 0.9, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ], + "es": [ + { + "pattern_identifier": "es_honorific_prefix_preserving", + "positive_pattern": "tratamiento|encabezamiento", + "positive_score": 0.9, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ], + "it": [ + { + "pattern_identifier": "it_honorific_prefix_preserving", + "positive_pattern": "titolo", + "positive_score": 0.9, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ], + "fr": [ + { + "pattern_identifier": "fr_honorific_prefix_preserving", + "positive_pattern": "titre", + "positive_score": 0.9, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ], + "ru": [ + { + "pattern_identifier": "ru_honorific_prefix_preserving", + "positive_pattern": "обраще́ние|зва́ние", + "positive_score": 0.9, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ], + "el": [ + { + "pattern_identifier": "el_honorific_prefix_preserving", + "positive_pattern": "προσφώνηση", + "positive_score": 0.9, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ], + "tr": [ + { + "pattern_identifier": "tr_honorific_prefix_preserving", + "positive_pattern": "hitap", + "positive_score": 0.9, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ] + }, + "PHONE": { + "en": [ + { + "pattern_identifier": "en_phone_preserving", + "positive_pattern": "phone|mobile|contact.?number", + "positive_score": 1.2, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 276 + } + ], + "de": [ + { + "pattern_identifier": "de_phone_preserving", + "positive_pattern": "telefonnummer", + "positive_score": 1.2, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 276 + } + ], + "es": [ + { + "pattern_identifier": "es_phone_preserving", + "positive_pattern": "telefono|teléfono", + "positive_score": 1.2, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 276 + } + ], + "fr": [ + { + "pattern_identifier": "fr_phone_preserving", + "positive_pattern": "telfixe", + "positive_score": 1.2, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 276 + } + ], + "ja": [ + { + "pattern_identifier": "ja_phone_preserving", + "positive_pattern": "電話", + "positive_score": 1.2, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 276 + } + ], + "pt": [ + { + "pattern_identifier": "pt_phone_preserving", + "positive_pattern": "telefone|telemovel", + "positive_score": 1.2, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 276 + } + ], + "ru": [ + { + "pattern_identifier": "ru_phone_preserving", + "positive_pattern": "телефон", + "positive_score": 1.2, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 276 + } + ], + "hi": [ + { + "pattern_identifier": "hi_phone_preserving", + "positive_pattern": "मोबाइल", + "positive_score": 1.2, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 276 + } + ], + "tr": [ + { + "pattern_identifier": "tr_phone_preserving", + "positive_pattern": "(\\b|_|\\*)telefon(\\b|_|\\*)", + "positive_score": 1.2, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 276 + } + ], + "zh": [ + { + "pattern_identifier": "zh_phone_preserving", + "positive_pattern": "电话", + "positive_score": 1.2, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 276 + } + ], + "ml": [ + { + "pattern_identifier": "ml_phone_preserving", + "positive_pattern": "മൊബൈല്", + "positive_score": 1.2, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 276 + } + ], + "ko": [ + { + "pattern_identifier": "ko_phone_preserving", + "positive_pattern": "(?:전화|핸드폰|휴대폰|휴대전화)(?:.?번호)?", + "positive_score": 1.2, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 276 + } + ] + }, + "AUGMENTED_PHONE_COUNTRY_CODE": { + "en": [ + { + "pattern_identifier": "en_augmented_phone_country_code_preserving", + "positive_pattern": "^[^0-9+]*(?:\\+|00)\\s*([1-9]\\d{0,3})\\D*$", + "positive_score": 1.3, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ] + }, + "PHONE_COUNTRY_CODE": { + "en": [ + { + "pattern_identifier": "en_phone_country_code_preserving", + "positive_pattern": "country.*code|ccode|_cc|phone.*code|user.*phone.*code", + "positive_score": 1.3, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 308 + } + ] + }, + "PHONE_AREA_CODE_NO_TEXT": { + "en": [ + { + "pattern_identifier": "en_phone_area_code_no_text_preserving", + "positive_pattern": "^\\($", + "positive_score": 1.3, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 276 + } + ] + }, + "PHONE_AREA_CODE": { + "en": [ + { + "pattern_identifier": "en_phone_area_code_preserving", + "positive_pattern": "area.*code|acode|area", + "positive_score": 1.3, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 276 + } + ], + "ko": [ + { + "pattern_identifier": "ko_phone_area_code_preserving", + "positive_pattern": "지역.?번호", + "positive_score": 1.3, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 276 + } + ] + }, + "PHONE_PREFIX_SEPARATOR": { + "en": [ + { + "pattern_identifier": "en_phone_prefix_separator_preserving", + "positive_pattern": "^-$|^\\)$", + "positive_score": 1.3, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 276 + } + ] + }, + "PHONE_SUFFIX_SEPARATOR": { + "en": [ + { + "pattern_identifier": "en_phone_suffix_separator_preserving", + "positive_pattern": "^-$", + "positive_score": 1.3, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 276 + } + ] + }, + "PHONE_PREFIX": { + "en": [ + { + "pattern_identifier": "en_phone_prefix_preserving", + "positive_pattern": "prefix|exchange", + "positive_score": 1.3, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 276 + } + ], + "fr": [ + { + "pattern_identifier": "fr_phone_prefix_preserving", + "positive_pattern": "preselection", + "positive_score": 1.3, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 276 + } + ], + "pt": [ + { + "pattern_identifier": "pt_phone_prefix_preserving", + "positive_pattern": "ddd", + "positive_score": 1.3, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 276 + } + ] + }, + "PHONE_SUFFIX": { + "en": [ + { + "pattern_identifier": "en_phone_suffix_preserving", + "positive_pattern": "suffix", + "positive_score": 1.3, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 276 + } + ] + }, + "PHONE_EXTENSION": { + "en": [ + { + "pattern_identifier": "en_phone_extension_preserving", + "positive_pattern": "\\bext|ext\\b|extension", + "positive_score": 1.3, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 276 + } + ], + "pt": [ + { + "pattern_identifier": "pt_phone_extension_preserving", + "positive_pattern": "ramal", + "positive_score": 1.3, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 276 + } + ] + }, + "PASSPORT": { + "en": [ + { + "pattern_identifier": "en_passport_preserving", + "positive_pattern": "document.*number|passport", + "positive_score": 1.2, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ], + "fr": [ + { + "pattern_identifier": "fr_passport_preserving", + "positive_pattern": "passeport", + "positive_score": 1.2, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ], + "es": [ + { + "pattern_identifier": "es_passport_preserving", + "positive_pattern": "numero.*documento|pasaporte", + "positive_score": 1.2, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ], + "ja": [ + { + "pattern_identifier": "ja_passport_preserving", + "positive_pattern": "書類", + "positive_score": 1.2, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ] + }, + "TRAVEL_ORIGIN": { + "en": [ + { + "pattern_identifier": "en_travel_origin_preserving", + "positive_pattern": "point.*of.*entry|arrival", + "positive_score": 1.2, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ], + "es": [ + { + "pattern_identifier": "es_travel_origin_preserving", + "positive_pattern": "punto.*internaci(o|ó)n|fecha.*llegada", + "positive_score": 1.2, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ], + "ja": [ + { + "pattern_identifier": "ja_travel_origin_preserving", + "positive_pattern": "入国", + "positive_score": 1.2, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ] + }, + "TRAVEL_DESTINATION": { + "en": [ + { + "pattern_identifier": "en_travel_destination_preserving", + "positive_pattern": "departure", + "positive_score": 1.2, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ], + "es": [ + { + "pattern_identifier": "es_travel_destination_preserving", + "positive_pattern": "fecha.*salida|destino", + "positive_score": 1.2, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ], + "ja": [ + { + "pattern_identifier": "ja_travel_destination_preserving", + "positive_pattern": "出国", + "positive_score": 1.2, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ] + }, + "FLIGHT": { + "en": [ + { + "pattern_identifier": "en_flight_preserving", + "positive_pattern": "airline|flight", + "positive_score": 1.2, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ], + "es": [ + { + "pattern_identifier": "es_flight_preserving", + "positive_pattern": "aerol(i|í)nea|n(u|ú)mero.*vuelo", + "positive_score": 1.2, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ], + "ja": [ + { + "pattern_identifier": "ja_flight_preserving", + "positive_pattern": "便名|航空会社", + "positive_score": 1.2, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ] + }, + "UPI_VIRTUAL_PAYMENT_ADDRESS": { + "en": [ + { + "pattern_identifier": "en_upi_virtual_payment_address_user@(IFSC/Aadhaar/Mobile/RuPay)_preserving", + "positive_pattern": "^[\\w.+-_]+@(\\w+\\.ifsc\\.npci|aadhaar\\.npci|mobile\\.npci|rupay\\.npci)$", + "positive_score": 0, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + }, + { + "pattern_identifier": "en_upi_virtual_payment_address_user@(bank_list)_preserving", + "positive_pattern": "^[\\w.+-_]+@(airtel|airtelpaymentsbank|albk|allahabadbank|allbank|andb|apb|apl|axis|axisbank|axisgo|bandhan|barodampay|birla|boi|cbin|cboi|centralbank|cmsidfc|cnrb|csbcash|csbpay|cub|dbs|dcb|dcbbank|denabank|dlb|eazypay|equitas|ezeepay|fbl|federal|finobank|hdfcbank|hsbc|icici|idbi|idbibank|idfc|idfcbank|idfcnetc|ikwik|imobile|indbank|indianbank|indianbk|indus|iob|jkb|jsb|jsbp|karb|karurvysyabank|kaypay|kbl|kbl052|kmb|kmbl|kotak|kvb|kvbank|lime|lvb|lvbank|mahb|obc|okaxis|okbizaxis|okhdfcbank|okicici|oksbi|paytm|payzapp|pingpay|pnb|pockets|psb|purz|rajgovhdfcbank|rbl|sbi|sc|scb|scbl|scmobile|sib|srcb|synd|syndbank|syndicate|tjsb|tjsp|ubi|uboi|uco|unionbank|unionbankofindia|united|upi|utbi|vijayabank|vijb|vjb|ybl|yesbank|yesbankltd)$", + "positive_score": 0, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ] + }, + "INTERNATIONAL_BANK_ACCOUNT_NUMBER": { + "en": [ + { + "pattern_identifier": "en_international_bank_account_number_preserving", + "positive_pattern": "^[a-zA-Z]{2}[0-9]{2}[a-zA-Z0-9]{4}[0-9]{7}([a-zA-Z0-9]?){0,16}$", + "positive_score": 0, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ] + }, + "CREDIT_CARD_CVC": { + "en": [ + { + "pattern_identifier": "en_credit_card_cvc_preserving", + "positive_pattern": "^\\d{3,4}$", + "positive_score": 0, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ] + }, + "CREDIT_CARD_EXP_YEAR": { + "en": [ + { + "pattern_identifier": "en_credit_card_exp_year_preserving", + "positive_pattern": "^[2][0][1-9][0-9]$", + "positive_score": 0, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ] + }, + "URL_SEARCH_ACTION": { + "en": [ + { + "pattern_identifier": "en_url_search_action_preserving", + "positive_pattern": "/search(/|((\\w*\\.\\w+)?$))", + "positive_score": 0, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ] + }, + "SOCIAL_SECURITY": { + "en": [ + { + "pattern_identifier": "en_social_security_preserving", + "positive_pattern": "ssn|social.?security.?(num(ber)?|#)*", + "positive_score": 0, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ] + }, + "ONE_TIME_PASSWORD": { + "en": [ + { + "pattern_identifier": "en_one_time_password_preserving", + "positive_pattern": "one.?time|sms.?(code|token|password|pwd|pass)", + "positive_score": 0, + "negative_pattern": "", + "match_field_attributes": 3, + "match_field_input_types": 4 + } + ] + } +} \ No newline at end of file
diff --git a/components/autofill/core/browser/pattern_provider/test_pattern_provider.cc b/components/autofill/core/browser/pattern_provider/test_pattern_provider.cc new file mode 100644 index 0000000..1a15523 --- /dev/null +++ b/components/autofill/core/browser/pattern_provider/test_pattern_provider.cc
@@ -0,0 +1,24 @@ +// Copyright 2020 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/core/browser/pattern_provider/test_pattern_provider.h" + +#include "components/autofill/core/browser/pattern_provider/pattern_configuration_parser.h" + +namespace autofill { + +TestPatternProvider::TestPatternProvider() { + base::Optional<PatternProvider::Map> patterns = + field_type_parsing::GetPatternsFromResourceBundleSynchronously(); + if (patterns) + patterns_ = patterns.value(); + + PatternProvider::SetPatternProviderForTesting(this); +} + +TestPatternProvider::~TestPatternProvider() { + PatternProvider::ResetPatternProvider(); +} + +} // namespace autofill
diff --git a/components/autofill/core/browser/pattern_provider/test_pattern_provider.h b/components/autofill/core/browser/pattern_provider/test_pattern_provider.h new file mode 100644 index 0000000..dc92323 --- /dev/null +++ b/components/autofill/core/browser/pattern_provider/test_pattern_provider.h
@@ -0,0 +1,23 @@ +// Copyright 2020 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_CORE_BROWSER_PATTERN_PROVIDER_TEST_PATTERN_PROVIDER_H_ +#define COMPONENTS_AUTOFILL_CORE_BROWSER_PATTERN_PROVIDER_TEST_PATTERN_PROVIDER_H_ + +#include "components/autofill/core/browser/pattern_provider/pattern_provider.h" + +namespace autofill { + +// The pattern provider to be used in tests. Loads the MatchingPattern +// configuration synchronously from the Resource Bundle and sets itself as the +// global PatternProvider. +class TestPatternProvider : public PatternProvider { + public: + TestPatternProvider(); + ~TestPatternProvider(); +}; + +} // namespace autofill + +#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_PATTERN_PROVIDER_TEST_PATTERN_PROVIDER_H_
diff --git a/components/client_hints/browser/client_hints.cc b/components/client_hints/browser/client_hints.cc index b3766ce..42e16bc5 100644 --- a/components/client_hints/browser/client_hints.cc +++ b/components/client_hints/browser/client_hints.cc
@@ -110,9 +110,8 @@ if (expiration_duration <= base::TimeDelta::FromSeconds(0)) return; - std::unique_ptr<base::ListValue> expiration_times_list = - std::make_unique<base::ListValue>(); - expiration_times_list->Reserve(client_hints.size()); + base::Value::ListStorage expiration_times_list; + expiration_times_list.reserve(client_hints.size()); // Use wall clock since the expiration time would be persisted across embedder // restarts. @@ -120,12 +119,12 @@ (base::Time::Now() + expiration_duration).ToDoubleT(); for (const auto& entry : client_hints) - expiration_times_list->AppendInteger(static_cast<int>(entry)); + expiration_times_list.push_back(base::Value(static_cast<int>(entry))); auto expiration_times_dictionary = std::make_unique<base::DictionaryValue>(); - expiration_times_dictionary->SetList("client_hints", - std::move(expiration_times_list)); - expiration_times_dictionary->SetDouble("expiration_time", expiration_time); + expiration_times_dictionary->SetKey( + "client_hints", base::Value(std::move(expiration_times_list))); + expiration_times_dictionary->SetDoubleKey("expiration_time", expiration_time); // TODO(tbansal): crbug.com/735518. Disable updates to client hints settings // when cookies are disabled for |primary_origin|.
diff --git a/components/domain_reliability/quic_error_mapping.cc b/components/domain_reliability/quic_error_mapping.cc index 7d104df..bf31314 100644 --- a/components/domain_reliability/quic_error_mapping.cc +++ b/components/domain_reliability/quic_error_mapping.cc
@@ -384,6 +384,7 @@ {quic::QUIC_SILENT_IDLE_TIMEOUT, "quic.silent_idle_timeout"}, {quic::QUIC_HTTP_RECEIVE_SPDY_SETTING, "quic.http_receive_spdy_setting"}, {quic::QUIC_MISSING_WRITE_KEYS, "quic.missing_write_keys"}, + {quic::QUIC_HTTP_RECEIVE_SPDY_FRAME, "quic.http_receive_spdy_frame"}, // QUIC_INVALID_APPLICATION_CLOSE_DATA was code 101. The code has been // deprecated, but to keep the assert below happy, there needs to be
diff --git a/components/federated_learning/floc_blocklist_service.cc b/components/federated_learning/floc_blocklist_service.cc index 21a7f26..f33713f9 100644 --- a/components/federated_learning/floc_blocklist_service.cc +++ b/components/federated_learning/floc_blocklist_service.cc
@@ -90,12 +90,14 @@ } bool FlocBlocklistService::IsBlocklistFileReady() const { - return blocklist_file_path_.has_value(); + return first_file_ready_seen_; } -void FlocBlocklistService::OnBlocklistFileReady( - const base::FilePath& file_path) { +void FlocBlocklistService::OnBlocklistFileReady(const base::FilePath& file_path, + const base::Version& version) { blocklist_file_path_ = file_path; + blocklist_version_ = version; + first_file_ready_seen_ = true; for (auto& observer : observers_) observer.OnBlocklistFileReady(); @@ -103,13 +105,20 @@ void FlocBlocklistService::FilterByBlocklist( const FlocId& unfiltered_floc, + const base::Optional<base::Version>& version_to_validate, FilterByBlocklistCallback callback) { DCHECK(unfiltered_floc.IsValid()); - DCHECK(blocklist_file_path_.has_value()); + DCHECK(first_file_ready_seen_); + if (version_to_validate && + version_to_validate.value().CompareTo(blocklist_version_) != 0) { + std::move(callback).Run(FlocId()); + return; + } + base::PostTaskAndReplyWithResult( background_task_runner_.get(), FROM_HERE, base::BindOnce(&FilterByBlocklistOnBackgroundThread, unfiltered_floc, - blocklist_file_path_.value()), + blocklist_file_path_), std::move(callback)); }
diff --git a/components/federated_learning/floc_blocklist_service.h b/components/federated_learning/floc_blocklist_service.h index b7305d41..9cd4ae4 100644 --- a/components/federated_learning/floc_blocklist_service.h +++ b/components/federated_learning/floc_blocklist_service.h
@@ -13,6 +13,7 @@ #include "base/memory/weak_ptr.h" #include "base/observer_list.h" #include "base/optional.h" +#include "base/version.h" #include "components/federated_learning/floc_id.h" namespace base { @@ -47,11 +48,14 @@ bool IsBlocklistFileReady() const; // Virtual for testing. - virtual void OnBlocklistFileReady(const base::FilePath& file_path); + virtual void OnBlocklistFileReady(const base::FilePath& file_path, + const base::Version& version); // Virtual for testing. - virtual void FilterByBlocklist(const FlocId& unfiltered_floc, - FilterByBlocklistCallback callback); + virtual void FilterByBlocklist( + const FlocId& unfiltered_floc, + const base::Optional<base::Version>& version_to_validate, + FilterByBlocklistCallback callback); void SetBackgroundTaskRunnerForTesting( scoped_refptr<base::SequencedTaskRunner> background_task_runner); @@ -66,7 +70,9 @@ base::ObserverList<Observer>::Unchecked observers_; - base::Optional<base::FilePath> blocklist_file_path_; + bool first_file_ready_seen_ = false; + base::FilePath blocklist_file_path_; + base::Version blocklist_version_; base::WeakPtrFactory<FlocBlocklistService> weak_ptr_factory_; };
diff --git a/components/federated_learning/floc_blocklist_service_unittest.cc b/components/federated_learning/floc_blocklist_service_unittest.cc index 054b0e4..c2dc6549 100644 --- a/components/federated_learning/floc_blocklist_service_unittest.cc +++ b/components/federated_learning/floc_blocklist_service_unittest.cc
@@ -21,6 +21,10 @@ namespace federated_learning { +namespace { + +base::Version kDummyVersion = base::Version("1.0.0"); + class CopyingFileOutputStream : public google::protobuf::io::CopyingOutputStream { public: @@ -41,6 +45,8 @@ base::File file_; }; +} // namespace + class FlocBlocklistServiceTest : public ::testing::Test { public: FlocBlocklistServiceTest() @@ -86,7 +92,7 @@ base::FilePath InitializeBlocklistFile( const std::vector<uint64_t>& blocklist) { base::FilePath file_path = CreateBlocklistFile(blocklist); - service()->OnBlocklistFileReady(file_path); + service()->OnBlocklistFileReady(file_path, kDummyVersion); EXPECT_TRUE(blocklist_file_path().has_value()); return file_path; } @@ -95,7 +101,10 @@ FlocBlocklistService* service() { return service_.get(); } - const base::Optional<base::FilePath>& blocklist_file_path() { + base::Optional<base::FilePath> blocklist_file_path() { + if (!service()->first_file_ready_seen_) + return base::nullopt; + return service()->blocklist_file_path_; } @@ -108,7 +117,7 @@ run_loop.Quit(); }); - service()->FilterByBlocklist(unfiltered_floc, std::move(cb)); + service()->FilterByBlocklist(unfiltered_floc, kDummyVersion, std::move(cb)); background_task_runner_->RunPendingTasks(); run_loop.Run(); @@ -167,7 +176,7 @@ TEST_F(FlocBlocklistServiceTest, NonExistentBlocklist_Blocked) { base::FilePath file_path = GetUniqueTemporaryPath(); - service()->OnBlocklistFileReady(file_path); + service()->OnBlocklistFileReady(file_path, kDummyVersion); EXPECT_EQ(FlocId(), FilterByBlocklist(FlocId(3))); }
diff --git a/components/federated_learning/floc_sorting_lsh_clusters_service.cc b/components/federated_learning/floc_sorting_lsh_clusters_service.cc index e9bc065..4d93529 100644 --- a/components/federated_learning/floc_sorting_lsh_clusters_service.cc +++ b/components/federated_learning/floc_sorting_lsh_clusters_service.cc
@@ -116,29 +116,46 @@ observers_.RemoveObserver(observer); } -void FlocSortingLshClustersService::SetBackgroundTaskRunnerForTesting( - scoped_refptr<base::SequencedTaskRunner> background_task_runner) { - background_task_runner_ = background_task_runner; +bool FlocSortingLshClustersService::IsSortingLshClustersFileReady() const { + return first_file_ready_seen_; +} + +void FlocSortingLshClustersService::OnSortingLshClustersFileReady( + const base::FilePath& file_path, + const base::Version& version) { + sorting_lsh_clusters_file_path_ = file_path; + sorting_lsh_clusters_version_ = version; + first_file_ready_seen_ = true; + + for (auto& observer : observers_) + observer.OnSortingLshClustersFileReady(); } void FlocSortingLshClustersService::ApplySortingLsh( const FlocId& raw_floc_id, ApplySortingLshCallback callback) { DCHECK(raw_floc_id.IsValid()); - DCHECK(sorting_lsh_clusters_file_path_.has_value()); + DCHECK(first_file_ready_seen_); + base::PostTaskAndReplyWithResult( background_task_runner_.get(), FROM_HERE, base::BindOnce(&ApplySortingLshOnBackgroundThread, raw_floc_id, - sorting_lsh_clusters_file_path_.value()), - std::move(callback)); + sorting_lsh_clusters_file_path_), + base::BindOnce(&FlocSortingLshClustersService::DidApplySortingLsh, + weak_ptr_factory_.GetWeakPtr(), std::move(callback), + sorting_lsh_clusters_version_)); } -void FlocSortingLshClustersService::OnSortingLshClustersFileReady( - const base::FilePath& file_path) { - sorting_lsh_clusters_file_path_ = file_path; +void FlocSortingLshClustersService::SetBackgroundTaskRunnerForTesting( + scoped_refptr<base::SequencedTaskRunner> background_task_runner) { + background_task_runner_ = background_task_runner; +} - for (auto& observer : observers_) - observer.OnSortingLshClustersFileReady(); +void FlocSortingLshClustersService::DidApplySortingLsh( + ApplySortingLshCallback callback, + base::Version version, + FlocId floc_id) { + std::move(callback).Run(std::move(floc_id), std::move(version)); } } // namespace federated_learning
diff --git a/components/federated_learning/floc_sorting_lsh_clusters_service.h b/components/federated_learning/floc_sorting_lsh_clusters_service.h index 8db8863..57e6d3f 100644 --- a/components/federated_learning/floc_sorting_lsh_clusters_service.h +++ b/components/federated_learning/floc_sorting_lsh_clusters_service.h
@@ -13,6 +13,7 @@ #include "base/memory/weak_ptr.h" #include "base/observer_list.h" #include "base/optional.h" +#include "base/version.h" #include "components/federated_learning/floc_id.h" namespace base { @@ -28,7 +29,8 @@ // File reading and parsing is posted to |background_task_runner_|. class FlocSortingLshClustersService { public: - using ApplySortingLshCallback = base::OnceCallback<void(FlocId)>; + using ApplySortingLshCallback = + base::OnceCallback<void(FlocId, base::Optional<base::Version>)>; class Observer { public: @@ -46,24 +48,34 @@ void AddObserver(Observer* observer); void RemoveObserver(Observer* observer); + bool IsSortingLshClustersFileReady() const; + + // Virtual for testing. + virtual void OnSortingLshClustersFileReady(const base::FilePath& file_path, + const base::Version& version); + + // Virtual for testing. + virtual void ApplySortingLsh(const FlocId& raw_floc_id, + ApplySortingLshCallback callback); + void SetBackgroundTaskRunnerForTesting( scoped_refptr<base::SequencedTaskRunner> background_task_runner); - void ApplySortingLsh(const FlocId& raw_floc_id, - ApplySortingLshCallback callback); - - // Virtual for testing. - virtual void OnSortingLshClustersFileReady(const base::FilePath& file_path); - private: friend class FlocSortingLshClustersServiceTest; + void DidApplySortingLsh(ApplySortingLshCallback callback, + base::Version version, + FlocId floc_id); + // Runner for tasks that do not influence user experience. scoped_refptr<base::SequencedTaskRunner> background_task_runner_; base::ObserverList<Observer>::Unchecked observers_; - base::Optional<base::FilePath> sorting_lsh_clusters_file_path_; + bool first_file_ready_seen_ = false; + base::FilePath sorting_lsh_clusters_file_path_; + base::Version sorting_lsh_clusters_version_; base::WeakPtrFactory<FlocSortingLshClustersService> weak_ptr_factory_; };
diff --git a/components/federated_learning/floc_sorting_lsh_clusters_service_unittest.cc b/components/federated_learning/floc_sorting_lsh_clusters_service_unittest.cc index 82b6640..f7d94e5f1 100644 --- a/components/federated_learning/floc_sorting_lsh_clusters_service_unittest.cc +++ b/components/federated_learning/floc_sorting_lsh_clusters_service_unittest.cc
@@ -23,6 +23,8 @@ namespace { +base::Version kDummyVersion = base::Version("1.2.3"); + class CopyingFileOutputStream : public google::protobuf::io::CopyingOutputStream { public: @@ -94,7 +96,7 @@ const std::vector<uint32_t>& sorting_lsh_clusters) { base::FilePath file_path = CreateTestSortingLshClustersFile(sorting_lsh_clusters); - service()->OnSortingLshClustersFileReady(file_path); + service()->OnSortingLshClustersFileReady(file_path, kDummyVersion); EXPECT_TRUE(sorting_lsh_clusters_file_path().has_value()); return file_path; } @@ -103,7 +105,10 @@ FlocSortingLshClustersService* service() { return service_.get(); } - const base::Optional<base::FilePath>& sorting_lsh_clusters_file_path() { + base::Optional<base::FilePath> sorting_lsh_clusters_file_path() { + if (!service()->first_file_ready_seen_) + return base::nullopt; + return service()->sorting_lsh_clusters_file_path_; } @@ -111,10 +116,11 @@ FlocId result; base::RunLoop run_loop; - auto cb = base::BindLambdaForTesting([&](FlocId floc_id) { - result = floc_id; - run_loop.Quit(); - }); + auto cb = base::BindLambdaForTesting( + [&](FlocId floc_id, base::Optional<base::Version> version) { + result = floc_id; + run_loop.Quit(); + }); service()->ApplySortingLsh(floc_id, std::move(cb)); background_task_runner_->RunPendingTasks(); @@ -216,11 +222,12 @@ base::FilePath file_path = InitializeSortingLshClustersFile({0}); base::RunLoop run_loop; - auto cb = base::BindLambdaForTesting([&](FlocId floc_id) { - // Since the file has been deleted, expect an invalid floc id. - EXPECT_EQ(FlocId(), floc_id); - run_loop.Quit(); - }); + auto cb = base::BindLambdaForTesting( + [&](FlocId floc_id, base::Optional<base::Version> version) { + // Since the file has been deleted, expect an invalid floc id. + EXPECT_EQ(FlocId(), floc_id); + run_loop.Quit(); + }); service()->ApplySortingLsh(FlocId(0), std::move(cb)); base::DeleteFile(file_path);
diff --git a/components/feedback/BUILD.gn b/components/feedback/BUILD.gn index 3fa9e8bf..1def69187 100644 --- a/components/feedback/BUILD.gn +++ b/components/feedback/BUILD.gn
@@ -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("//testing/libfuzzer/fuzzer_test.gni") + static_library("feedback") { sources = [ "feedback_common.cc", @@ -76,3 +78,9 @@ "//testing/gtest", ] } + +fuzzer_test("redaction_tool_fuzzer") { + sources = [ "redaction_tool_fuzzer.cc" ] + deps = [ ":feedback" ] + dict = "redaction_tool_fuzzer.dict" +}
diff --git a/components/feedback/redaction_tool_fuzzer.cc b/components/feedback/redaction_tool_fuzzer.cc new file mode 100644 index 0000000..16e5858 --- /dev/null +++ b/components/feedback/redaction_tool_fuzzer.cc
@@ -0,0 +1,46 @@ +// Copyright 2020 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 <stddef.h> +#include <stdint.h> + +#include <memory> +#include <string> +#include <vector> + +#include <fuzzer/FuzzedDataProvider.h> + +#include "components/feedback/redaction_tool.h" + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + FuzzedDataProvider provider(data, size); + + int first_party_extension_id_count = provider.ConsumeIntegralInRange(-1, 50); + // This is the storage for the strings inside first_party_extension_ids. This + // is to make sure the char *'s we pass to the RedactionTool constructor are + // deleted correctly -- they must be deleted after redactor is destructed, but + // not leaked. + std::vector<std::string> first_party_extension_id_store; + // The first_party_extension_ids we pass to the RedactionTool constructor. + // This owns the array but not the pointed-to strings. Note that if + // first_party_extension_id_count is -1, this is not set so we pass nullptr to + // the constructor; that's deliberate. + std::unique_ptr<const char*[]> first_party_extension_ids; + if (first_party_extension_id_count >= 0) { + first_party_extension_id_store.reserve(first_party_extension_id_count); + first_party_extension_ids = + std::make_unique<const char*[]>(first_party_extension_id_count + 1); + for (int i = 0; i < first_party_extension_id_count; ++i) { + constexpr int kArbitraryMaxNameLength = 4096; + first_party_extension_id_store.emplace_back( + provider.ConsumeRandomLengthString(kArbitraryMaxNameLength)); + first_party_extension_ids[i] = first_party_extension_id_store[i].c_str(); + } + first_party_extension_ids[first_party_extension_id_count] = nullptr; + } + + feedback::RedactionTool redactor(first_party_extension_ids.get()); + redactor.Redact(provider.ConsumeRemainingBytesAsString()); + return 0; +}
diff --git a/components/feedback/redaction_tool_fuzzer.dict b/components/feedback/redaction_tool_fuzzer.dict new file mode 100644 index 0000000..3810445 --- /dev/null +++ b/components/feedback/redaction_tool_fuzzer.dict
@@ -0,0 +1,42 @@ +# Copyright 2020 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. + +# Literals from kCustomPatternsWithContext strings +CellID="Cell ID:" +LocAC="Location area code:" +SSID1="ssid" +SSIDHex="SSID - hexdump" +SSID1="SSID" +Serial1="serial number" +Serial2="Serial Number" +GAIA="gaia_id" +GAIAId="id:" +GAIAEmail=", email" +UUID="UUID=" +UUIDEnd="xxx" +VolumeLabel="LABEL=" +VolumeLabel2="/media/removable/" + +# Literals from kCustomPatternsWithoutContext, and other things that look like +# URLS and Emails +URL1="http://" +URL2="https://" +URL3="ftp://" +URL4="chrome://" +URL5="chrome-extension://" +URL6="android://" +URL7="rtsp://" +URLHost="foo.com" +URLPort=":80" +URLQuery="?" +URLFragment="#" + +# Email Symbols +EMailAt="@" +EMailDot="." +EMailExample="a@b.c" + +# MAC Symbols +MACColon=":" +MACExample="10:fd:b5:ec:b1:3e"
diff --git a/components/metrics/generate_expired_histograms_array.gni b/components/metrics/generate_expired_histograms_array.gni index a1f7435..47fef83 100644 --- a/components/metrics/generate_expired_histograms_array.gni +++ b/components/metrics/generate_expired_histograms_array.gni
@@ -6,6 +6,9 @@ # produce an output file and a source_set to build it. # # Parameters: +# inputs: +# List of file name to read. Each file should be a .xml file with +# histogram descriptions. # # namespace (optional): # Namespace in which the generated code should be scoped. If left empty, @@ -27,6 +30,8 @@ script = "//tools/metrics/histograms/generate_expired_histograms_array.py" outputs = [ header_filename ] + inputs = invoker.inputs + major_branch_date_filepath = invoker.major_branch_date_filepath milestone_filepath = invoker.milestone_filepath @@ -37,10 +42,10 @@ } args += [ - "-o" + rebase_path(root_gen_dir, root_build_dir), - "-H" + rebase_path(header_filename, root_gen_dir), - "-d" + rebase_path(major_branch_date_filepath, root_build_dir), - "-m" + rebase_path(milestone_filepath, root_build_dir), - ] + "-o" + rebase_path(root_gen_dir, root_build_dir), + "-H" + rebase_path(header_filename, root_gen_dir), + "-d" + rebase_path(major_branch_date_filepath, root_build_dir), + "-m" + rebase_path(milestone_filepath, root_build_dir), + ] + rebase_path(inputs, root_build_dir) } }
diff --git a/components/performance_manager/performance_manager.cc b/components/performance_manager/performance_manager.cc index e71417f..b12e130 100644 --- a/components/performance_manager/performance_manager.cc +++ b/components/performance_manager/performance_manager.cc
@@ -75,8 +75,14 @@ PerformanceManagerTabHelper::FromWebContents(wc); if (!helper) return nullptr; - - return helper->GetFrameNode(rfh)->GetWeakPtr(); + auto* frame_node = helper->GetFrameNode(rfh); + if (!frame_node) { + // This should only happen if GetFrameNodeForRenderFrameHost is called + // before the RenderFrameCreate notification is dispatched. + DCHECK(!rfh->IsRenderFrameCreated()); + return nullptr; + } + return frame_node->GetWeakPtr(); } // static
diff --git a/components/performance_manager/performance_manager_unittest.cc b/components/performance_manager/performance_manager_unittest.cc index af8ac21..762a319 100644 --- a/components/performance_manager/performance_manager_unittest.cc +++ b/components/performance_manager/performance_manager_unittest.cc
@@ -8,13 +8,17 @@ #include "base/run_loop.h" #include "base/test/bind_test_util.h" +#include "components/performance_manager/public/graph/frame_node.h" #include "components/performance_manager/public/graph/page_node.h" +#include "components/performance_manager/public/render_frame_host_proxy.h" #include "components/performance_manager/public/web_contents_proxy.h" #include "components/performance_manager/test_support/performance_manager_test_harness.h" #include "content/public/browser/browser_task_traits.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/web_contents.h" +#include "content/public/test/navigation_simulator.h" #include "testing/gtest/include/gtest/gtest.h" +#include "url/gurl.h" namespace performance_manager { @@ -41,42 +45,60 @@ DISALLOW_COPY_AND_ASSIGN(PerformanceManagerTest); }; -TEST_F(PerformanceManagerTest, GetPageNodeForWebContents) { +TEST_F(PerformanceManagerTest, NodeAccessors) { auto contents = CreateTestWebContents(); + content::RenderFrameHost* rfh = contents->GetMainFrame(); + ASSERT_TRUE(rfh); base::WeakPtr<PageNode> page_node = PerformanceManager::GetPageNodeForWebContents(contents.get()); + // FrameNode's and ProcessNode's don't exist until an observer fires on + // navigation. Verify that looking them up before that returns null instead + // of crashing. + EXPECT_FALSE(PerformanceManager::GetFrameNodeForRenderFrameHost(rfh)); + + // Simulate a committed navigation to create the nodes. + content::NavigationSimulator::NavigateAndCommitFromBrowser( + contents.get(), GURL("https://www.example.com/")); + base::WeakPtr<FrameNode> frame_node = + PerformanceManager::GetFrameNodeForRenderFrameHost(rfh); + // Post a task to the Graph and make it call a function on the UI thread that - // will ensure that |page_node| is really associated with |contents|. + // will ensure that the nodes are really associated with the content objects. base::RunLoop run_loop; - auto check_wc_on_main_thread = - base::BindLambdaForTesting([&](const WebContentsProxy& wc_proxy) { + auto check_proxies_on_main_thread = + base::BindLambdaForTesting([&](const WebContentsProxy& wc_proxy, + const RenderFrameHostProxy& rfh_proxy) { EXPECT_EQ(contents.get(), wc_proxy.Get()); + EXPECT_EQ(rfh, rfh_proxy.Get()); run_loop.Quit(); }); auto call_on_graph_cb = base::BindLambdaForTesting([&]() { EXPECT_TRUE(page_node.get()); + EXPECT_TRUE(frame_node.get()); content::GetUIThreadTaskRunner({})->PostTask( - FROM_HERE, base::BindOnce(std::move(check_wc_on_main_thread), - page_node->GetContentsProxy())); + FROM_HERE, base::BindOnce(std::move(check_proxies_on_main_thread), + page_node->GetContentsProxy(), + frame_node->GetRenderFrameHostProxy())); }); PerformanceManager::CallOnGraph(FROM_HERE, call_on_graph_cb); - // Wait for |check_wc_on_main_thread| to be called. + // Wait for |check_proxies_on_main_thread| to be called. run_loop.Run(); contents.reset(); - // After deleting |contents| the corresponding PageNode WeakPtr should be + // After deleting |contents| the corresponding WeakPtr's should be // invalid. base::RunLoop run_loop_after_contents_reset; auto quit_closure = run_loop_after_contents_reset.QuitClosure(); auto call_on_graph_cb_2 = base::BindLambdaForTesting([&]() { EXPECT_FALSE(page_node.get()); + EXPECT_FALSE(frame_node.get()); std::move(quit_closure).Run(); });
diff --git a/components/permissions/android/permission_prompt_android.cc b/components/permissions/android/permission_prompt_android.cc index e7e561c..2bacb24 100644 --- a/components/permissions/android/permission_prompt_android.cc +++ b/components/permissions/android/permission_prompt_android.cc
@@ -10,6 +10,7 @@ #include "components/infobars/core/infobar_manager.h" #include "components/permissions/android/permission_dialog_delegate.h" #include "components/permissions/permission_request.h" +#include "components/permissions/permission_uma_util.h" #include "components/permissions/permissions_client.h" #include "components/resources/android/theme_resources.h" #include "components/strings/grit/components_strings.h" @@ -64,6 +65,13 @@ return TabSwitchingBehavior::kKeepPromptAlive; } +permissions::PermissionPromptDisposition +PermissionPromptAndroid::GetPromptDisposition() const { + return permission_infobar_ + ? permissions::PermissionPromptDisposition::MINI_INFOBAR + : permissions::PermissionPromptDisposition::MODAL_DIALOG; +} + void PermissionPromptAndroid::Closing() { delegate_->Closing(); }
diff --git a/components/permissions/android/permission_prompt_android.h b/components/permissions/android/permission_prompt_android.h index e1ee71e..a2069f2 100644 --- a/components/permissions/android/permission_prompt_android.h +++ b/components/permissions/android/permission_prompt_android.h
@@ -33,6 +33,8 @@ // permissions::PermissionPrompt: void UpdateAnchorPosition() override; TabSwitchingBehavior GetTabSwitchingBehavior() override; + permissions::PermissionPromptDisposition GetPromptDisposition() + const override; void Closing(); void Accept();
diff --git a/components/permissions/notification_permission_ui_selector.cc b/components/permissions/notification_permission_ui_selector.cc index 46bdd77..5261001 100644 --- a/components/permissions/notification_permission_ui_selector.cc +++ b/components/permissions/notification_permission_ui_selector.cc
@@ -6,6 +6,19 @@ namespace permissions { +// static +bool NotificationPermissionUiSelector::ShouldSuppressAnimation( + QuietUiReason reason) { + switch (reason) { + case QuietUiReason::kEnabledInPrefs: + return false; + case QuietUiReason::kTriggeredByCrowdDeny: + case QuietUiReason::kTriggeredDueToAbusiveRequests: + case QuietUiReason::kTriggeredDueToAbusiveContent: + return true; + } +} + NotificationPermissionUiSelector::Decision::Decision( base::Optional<QuietUiReason> quiet_ui_reason, base::Optional<WarningReason> warning_reason)
diff --git a/components/permissions/notification_permission_ui_selector.h b/components/permissions/notification_permission_ui_selector.h index 444dec5..91e62fb 100644 --- a/components/permissions/notification_permission_ui_selector.h +++ b/components/permissions/notification_permission_ui_selector.h
@@ -62,6 +62,10 @@ virtual ~NotificationPermissionUiSelector() {} + // Determines whether animations should be suppressed because we're very + // confident the user does not want notifications (e.g. they're abusive). + static bool ShouldSuppressAnimation(QuietUiReason reason); + // Determines the UI to use for the given |request|, and invokes |callback| // when done, either synchronously or asynchronously. The |callback| is // guaranteed never to be invoked after |this| goes out of scope. Only one
diff --git a/components/permissions/permission_prompt.h b/components/permissions/permission_prompt.h index de0be84..f327492d 100644 --- a/components/permissions/permission_prompt.h +++ b/components/permissions/permission_prompt.h
@@ -17,6 +17,8 @@ } namespace permissions { +enum class PermissionPromptDisposition; + class PermissionRequest; // This class is the platform-independent interface through which the permission @@ -74,6 +76,9 @@ // Get the behavior of this prompt when the user switches away from the // associated tab. virtual TabSwitchingBehavior GetTabSwitchingBehavior() = 0; + + // Get the type of prompt UI shown for metrics. + virtual PermissionPromptDisposition GetPromptDisposition() const = 0; }; } // namespace permissions
diff --git a/components/permissions/permission_request_manager.cc b/components/permissions/permission_request_manager.cc index c61e7fd..9b12bf4 100644 --- a/components/permissions/permission_request_manager.cc +++ b/components/permissions/permission_request_manager.cc
@@ -732,18 +732,9 @@ PermissionPromptDisposition PermissionRequestManager::DetermineCurrentRequestUIDispositionForUMA() { -#if defined(OS_ANDROID) - return ShouldCurrentRequestUseQuietUI() - ? PermissionPromptDisposition::MINI_INFOBAR - : PermissionPromptDisposition::MODAL_DIALOG; -#else - return !ShouldCurrentRequestUseQuietUI() - ? PermissionPromptDisposition::ANCHORED_BUBBLE - : ReasonForUsingQuietUi() == QuietUiReason::kTriggeredByCrowdDeny - ? PermissionPromptDisposition::LOCATION_BAR_RIGHT_STATIC_ICON - : PermissionPromptDisposition:: - LOCATION_BAR_RIGHT_ANIMATED_ICON; -#endif + if (view_) + return view_->GetPromptDisposition(); + return PermissionPromptDisposition::NONE_VISIBLE; } void PermissionRequestManager::LogWarningToConsole(const char* message) {
diff --git a/components/permissions/permission_uma_util.cc b/components/permissions/permission_uma_util.cc index 45cc8445..422f18b 100644 --- a/components/permissions/permission_uma_util.cc +++ b/components/permissions/permission_uma_util.cc
@@ -161,6 +161,8 @@ switch (ui_disposition) { case PermissionPromptDisposition::ANCHORED_BUBBLE: return "AnchoredBubble"; + case PermissionPromptDisposition::LOCATION_BAR_LEFT_CHIP: + return "LocationBarLeftChip"; case PermissionPromptDisposition::LOCATION_BAR_RIGHT_ANIMATED_ICON: return "LocationBarRightAnimatedIcon"; case PermissionPromptDisposition::LOCATION_BAR_RIGHT_STATIC_ICON: @@ -169,6 +171,8 @@ return "MiniInfobar"; case PermissionPromptDisposition::MODAL_DIALOG: return "ModalDialog"; + case PermissionPromptDisposition::NONE_VISIBLE: + return "NoneVisible"; case PermissionPromptDisposition::NOT_APPLICABLE: return "NotApplicable"; }
diff --git a/components/permissions/permission_uma_util.h b/components/permissions/permission_uma_util.h index a8a4040e..964033b 100644 --- a/components/permissions/permission_uma_util.h +++ b/components/permissions/permission_uma_util.h
@@ -69,7 +69,7 @@ // Enum used in UKMs and UMAs, do not re-order or change values. Deprecated // items should only be commented out. New items should be added at the end, // and the "PermissionPromptDisposition" histogram suffix needs to be updated to -// match (tools/metrics/histograms/histograms.xml). +// match (tools/metrics/histograms/histograms_xml/histogram_suffixes_list.xml). enum class PermissionPromptDisposition { // Not all permission actions will have an associated permission prompt (e.g. // changing permission via the settings page). @@ -92,6 +92,14 @@ // Only used on Android, an initially-collapsed infobar at the bottom of the // page. MINI_INFOBAR = 5, + + // Only used on desktop, a chip on the left-hand side of the location bar that + // shows a bubble when clicked. + LOCATION_BAR_LEFT_CHIP = 6, + + // There was no UI being shown. This is usually because the user closed an + // inactive tab that had a pending permission request. + NONE_VISIBLE = 7, }; enum class AdaptiveTriggers {
diff --git a/components/permissions/test/mock_permission_prompt.cc b/components/permissions/test/mock_permission_prompt.cc index b87746ea..2af4bfbc 100644 --- a/components/permissions/test/mock_permission_prompt.cc +++ b/components/permissions/test/mock_permission_prompt.cc
@@ -7,6 +7,7 @@ #include "base/bind.h" #include "base/run_loop.h" #include "build/build_config.h" +#include "components/permissions/permission_uma_util.h" #include "components/permissions/test/mock_permission_prompt_factory.h" #include "testing/gtest/include/gtest/gtest.h" @@ -32,6 +33,14 @@ #endif } +PermissionPromptDisposition MockPermissionPrompt::GetPromptDisposition() const { +#if defined(OS_ANDROID) + return PermissionPromptDisposition::MODAL_DIALOG; +#else + return PermissionPromptDisposition::ANCHORED_BUBBLE; +#endif +} + MockPermissionPrompt::MockPermissionPrompt(MockPermissionPromptFactory* factory, Delegate* delegate) : factory_(factory), delegate_(delegate) {
diff --git a/components/permissions/test/mock_permission_prompt.h b/components/permissions/test/mock_permission_prompt.h index 2ae804b..220dd9a4 100644 --- a/components/permissions/test/mock_permission_prompt.h +++ b/components/permissions/test/mock_permission_prompt.h
@@ -21,6 +21,7 @@ // PermissionPrompt: void UpdateAnchorPosition() override; TabSwitchingBehavior GetTabSwitchingBehavior() override; + PermissionPromptDisposition GetPromptDisposition() const override; bool IsVisible();
diff --git a/components/resources/autofill_regex_resources.grdp b/components/resources/autofill_regex_resources.grdp new file mode 100644 index 0000000..eb901de --- /dev/null +++ b/components/resources/autofill_regex_resources.grdp
@@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<grit-part> + <include name="IDR_AUTOFILL_REGEX_JSON" file="../autofill/core/browser/pattern_provider/resources/regex_patterns.json" type="BINDATA" compress="gzip" /> +</grit-part>
diff --git a/components/resources/components_resources.grd b/components/resources/components_resources.grd index 1d62db6..be607c11 100644 --- a/components/resources/components_resources.grd +++ b/components/resources/components_resources.grd
@@ -14,6 +14,7 @@ <release seq="1"> <includes> <part file="about_ui_resources.grdp" /> + <part file="autofill_regex_resources.grdp" /> <part file="dom_distiller_resources.grdp" /> <part file="flags_ui_resources.grdp" /> <part file="management_resources.grdp" />
diff --git a/components/system_media_controls/linux/system_media_controls_linux.cc b/components/system_media_controls/linux/system_media_controls_linux.cc index c07a782..14d5a0b9 100644 --- a/components/system_media_controls/linux/system_media_controls_linux.cc +++ b/components/system_media_controls/linux/system_media_controls_linux.cc
@@ -153,7 +153,7 @@ // org.mpris.MediaPlayer2 interface properties. auto set_property = [&](const std::string& property_name, auto&& value) { properties_->SetProperty(kMprisAPIInterfaceName, property_name, - std::move(value), false); + std::forward<decltype(value)>(value), false); }; set_property("CanQuit", DbusBoolean(false)); set_property("CanRaise", DbusBoolean(false)); @@ -171,7 +171,7 @@ auto set_player_property = [&](const std::string& property_name, auto&& value) { properties_->SetProperty(kMprisAPIPlayerInterfaceName, property_name, - std::move(value), false); + std::forward<decltype(value)>(value), false); }; set_player_property("PlaybackStatus", DbusString("Stopped")); set_player_property("Rate", DbusDouble(1.0));
diff --git a/components/viz/common/features.cc b/components/viz/common/features.cc index 956b3b48c..53957212 100644 --- a/components/viz/common/features.cc +++ b/components/viz/common/features.cc
@@ -42,6 +42,10 @@ base::FEATURE_ENABLED_BY_DEFAULT}; #endif +// Uses glClear to composite solid color quads whenever possible. +const base::Feature kFastSolidColorDraw{"FastSolidColorDraw", + base::FEATURE_DISABLED_BY_DEFAULT}; + // Viz for WebView architecture. const base::Feature kVizForWebView{"VizForWebView", base::FEATURE_DISABLED_BY_DEFAULT}; @@ -127,6 +131,10 @@ } #endif +bool IsUsingFastPathForSolidColorQuad() { + return base::FeatureList::IsEnabled(kFastSolidColorDraw); +} + bool IsUsingVizForWebView() { // Viz for WebView requires shared images to be enabled. if (!base::FeatureList::IsEnabled(kEnableSharedImageForWebview))
diff --git a/components/viz/common/features.h b/components/viz/common/features.h index 72491fe..b7c7376 100644 --- a/components/viz/common/features.h +++ b/components/viz/common/features.h
@@ -20,6 +20,7 @@ #if defined(OS_ANDROID) VIZ_COMMON_EXPORT extern const base::Feature kDynamicColorGamut; #endif +VIZ_COMMON_EXPORT extern const base::Feature kFastSolidColorDraw; VIZ_COMMON_EXPORT extern const base::Feature kVizForWebView; VIZ_COMMON_EXPORT extern const base::Feature kVizFrameSubmissionForWebView; VIZ_COMMON_EXPORT extern const base::Feature kUsePreferredIntervalForVideo; @@ -38,6 +39,7 @@ #if defined(OS_ANDROID) VIZ_COMMON_EXPORT bool IsDynamicColorGamutEnabled(); #endif +VIZ_COMMON_EXPORT bool IsUsingFastPathForSolidColorQuad(); VIZ_COMMON_EXPORT bool IsUsingVizForWebView(); VIZ_COMMON_EXPORT bool IsUsingVizFrameSubmissionForWebView(); VIZ_COMMON_EXPORT bool IsUsingPreferredIntervalForVideo();
diff --git a/components/viz/service/display/dc_layer_overlay.cc b/components/viz/service/display/dc_layer_overlay.cc index 740c080..b294b34b 100644 --- a/components/viz/service/display/dc_layer_overlay.cc +++ b/components/viz/service/display/dc_layer_overlay.cc
@@ -6,6 +6,7 @@ #include <limits> +#include "base/metrics/histogram_functions.h" #include "base/metrics/histogram_macros.h" #include "base/threading/thread_task_runner_handle.h" #include "build/build_config.h" @@ -19,6 +20,7 @@ #include "components/viz/service/display/display_resource_provider.h" #include "components/viz/service/display/overlay_processor_interface.h" #include "gpu/GLES2/gl2extchromium.h" +#include "gpu/config/gpu_finch_features.h" #include "ui/gfx/geometry/insets.h" #include "ui/gfx/geometry/rect.h" #include "ui/gfx/geometry/rect_conversions.h" @@ -71,7 +73,8 @@ const YUVVideoDrawQuad* quad, const std::vector<gfx::Rect>& backdrop_filter_rects, bool has_overlay_support, - int current_frame_processed_overlay_count, + int allowed_yuv_overlay_count, + int processed_yuv_overlay_count, DisplayResourceProvider* resource_provider) { // Note: Do not override this value based on base::Feature values. It is the // result after the GPU blocklist has been consulted. @@ -97,7 +100,7 @@ return DC_LAYER_FAILED_COMPLEX_TRANSFORM; } - if (current_frame_processed_overlay_count > 0) + if (processed_yuv_overlay_count >= allowed_yuv_overlay_count) return DC_LAYER_FAILED_TOO_MANY_OVERLAYS; // Rounded corner on overlays are not supported. @@ -320,8 +323,13 @@ DCLayerOverlayProcessor::DCLayerOverlayProcessor( const DebugRendererSettings* debug_settings, + int allowed_yuv_overlay_count, bool skip_initialization_for_testing) : has_overlay_support_(skip_initialization_for_testing), + use_overlay_damage_list_(base::FeatureList::IsEnabled( + features::kDirectCompositionUseOverlayDamageList)), + allowed_yuv_overlay_count_( + use_overlay_damage_list_ ? allowed_yuv_overlay_count : 1), debug_settings_(debug_settings), viz_task_runner_(skip_initialization_for_testing ? nullptr @@ -406,6 +414,7 @@ gfx::Rect* damage_rect, DCLayerOverlayList* dc_layer_overlays) { gfx::Rect this_frame_underlay_rect; + processed_yuv_overlay_count_ = 0; // Which render passes have backdrop filters. base::flat_set<AggregatedRenderPassId> render_pass_has_backdrop_filters; @@ -451,11 +460,13 @@ DCLayerResult result; switch (it->material) { case DrawQuad::Material::kYuvVideoContent: - result = - ValidateYUVQuad(YUVVideoDrawQuad::MaterialCast(*it), - backdrop_filter_rects, has_overlay_support_, - candidate_index_list.size(), resource_provider); + result = ValidateYUVQuad( + YUVVideoDrawQuad::MaterialCast(*it), backdrop_filter_rects, + has_overlay_support_, allowed_yuv_overlay_count_, + processed_yuv_overlay_count_, resource_provider); yuv_quads_in_quad_list++; + if (result == DC_LAYER_SUCCESS) + processed_yuv_overlay_count_++; break; case DrawQuad::Material::kTextureContent: result = ValidateTextureQuad(TextureDrawQuad::MaterialCast(*it), @@ -483,11 +494,17 @@ candidate_index_list.push_back(index); } + // A YUV quad might be rejected later due to not allowed as an underlay. + // Recount the YUV overlays when they are added to the overlay list + // successfully. + processed_yuv_overlay_count_ = 0; + // TODO(magchen@): Revisit this code if allowed_yuv_overlay_count_ > 1. // We might not save power if there are more than one videos and only one is // promoted to overlay. Skip overlays for this frame unless there are // protected video or texture overlays. - if (candidate_index_list.size() > 0 && yuv_quads_in_quad_list > 1 && + if (candidate_index_list.size() > 0 && + yuv_quads_in_quad_list > allowed_yuv_overlay_count_ && !has_protected_video_or_texture_overlays) { candidate_index_list.clear(); // In this case, there is only one candidate in the list. @@ -516,6 +533,12 @@ // skipped if they're not underlay compatible. const bool requires_overlay = IsProtectedVideo(it); + // TODO(magchen@): Since we reject underlays here, the max number of YUV + // overlays we can promote might not be accurate. We should allow all YUV + // quads to be put into candidate_index_list, but only + // |allowed_yuv_overlay_count_| YUV quads should be promoted to + // overlays/underlays from that list. + // Skip quad if it's an underlay and underlays are not allowed. if (!is_overlay && !requires_overlay) { DCLayerResult result = IsUnderlayAllowed(it); @@ -539,16 +562,23 @@ // found in this frame, the previous overlay rects would have been handled // above and |previous_frame_overlay_rect_union_| becomes empty. damage_rect->Union(previous_frame_overlay_rect_union_); + damage_rect->Intersect(gfx::ToEnclosingRect(display_rect)); + previous_frame_overlay_rect_union_ = current_frame_overlay_rect_union_; current_frame_overlay_rect_union_ = gfx::Rect(); previous_frame_processed_overlay_count_ = current_frame_processed_overlay_count_; current_frame_processed_overlay_count_ = 0; - - damage_rect->Intersect(gfx::ToEnclosingRect(display_rect)); previous_display_rect_ = display_rect; previous_frame_underlay_rect_ = this_frame_underlay_rect; + if (!dc_layer_overlays->empty()) { + base::UmaHistogramExactLinear( + "GPU.DirectComposition.DCLayer.YUVOverlayCount", + /*sample=*/processed_yuv_overlay_count_, + /*value_max=*/10); + } + if (debug_settings_->show_dc_layer_debug_borders && dc_layer_overlays->size() > 0) { InsertDebugBorderDrawQuad(dc_layer_overlays, root_render_pass, display_rect, @@ -576,6 +606,7 @@ case DrawQuad::Material::kYuvVideoContent: FromYUVQuad(YUVVideoDrawQuad::MaterialCast(*it), render_pass->transform_to_root_target, &dc_layer); + processed_yuv_overlay_count_++; break; case DrawQuad::Material::kTextureContent: FromTextureQuad(TextureDrawQuad::MaterialCast(*it), @@ -624,7 +655,6 @@ dc_layer_overlays->push_back(dc_layer); - // Only allow one overlay unless it's hardware protected video. current_frame_processed_overlay_count_++; }
diff --git a/components/viz/service/display/dc_layer_overlay.h b/components/viz/service/display/dc_layer_overlay.h index 0db594e..bc35e03 100644 --- a/components/viz/service/display/dc_layer_overlay.h +++ b/components/viz/service/display/dc_layer_overlay.h
@@ -79,8 +79,14 @@ public: // When |skip_initialization_for_testing| is true, object will be isolated // for unit tests. + // allowed_yuv_overlay_count will be limited to 1 if + // |use_overlay_damage_list_| is not supported. This new method produces an + // empty root damage rect when the overlay quads are the only damages in the + // frames. If |use_overlay_damage_list_| is false, we should not allowed more + // than one YUV overlays since non-empty damage rect won't save any power. explicit DCLayerOverlayProcessor( const DebugRendererSettings* debug_settings, + int allowed_yuv_overlay_count, bool skip_initialization_for_testing = false); virtual ~DCLayerOverlayProcessor(); @@ -137,6 +143,11 @@ gfx::Rect* damage_rect); bool has_overlay_support_; + const bool use_overlay_damage_list_; + // TODO(magchen@): We are going to support more than one YUV overlay. + const int allowed_yuv_overlay_count_; + int processed_yuv_overlay_count_ = 0; + // Reference to the global viz singleton. const DebugRendererSettings* const debug_settings_;
diff --git a/components/viz/service/display/gl_renderer.cc b/components/viz/service/display/gl_renderer.cc index e5179c0..31fa3d2 100644 --- a/components/viz/service/display/gl_renderer.cc +++ b/components/viz/service/display/gl_renderer.cc
@@ -32,6 +32,7 @@ #include "cc/paint/render_surface_filters.h" #include "cc/raster/scoped_gpu_raster.h" #include "components/viz/common/display/renderer_settings.h" +#include "components/viz/common/features.h" #include "components/viz/common/frame_sinks/copy_output_request.h" #include "components/viz/common/gpu/context_provider.h" #include "components/viz/common/quads/compositor_frame.h" @@ -77,6 +78,7 @@ #include "ui/gfx/geometry/rect.h" #include "ui/gfx/geometry/rect_conversions.h" #include "ui/gfx/geometry/size_conversions.h" +#include "ui/gfx/geometry/vector2d.h" #include "ui/gfx/rrect_f.h" #include "ui/gfx/skia_util.h" @@ -218,6 +220,37 @@ gpu::gles2::GLES2Interface* gl_; }; +void AccumulateDrawRects(const gfx::Rect& quad_rect, + const gfx::Transform& target_transform, + std::vector<gfx::Rect>* drawn_rects) { + gfx::RectF quad_rect_f(quad_rect); + + // If the transform is not axis aligned then assume the largest possible + // bounds the quad can take in the render target. In this case, we take the + // sum of 2 sides. + if (!target_transform.Preserves2dAxisAlignment()) { + // Increase the length of each side to |width + height|. + const int total_length = quad_rect.width() + quad_rect.height(); + quad_rect_f.set_height(total_length); + quad_rect_f.set_width(total_length); + + // Ensure that the increase is equally distributed on either sides of the + // quad such that the position of the center of the quad does not change. + const float delta_x = -(quad_rect.height() / 2.f); + const float delta_y = -(quad_rect.width() / 2.f); + quad_rect_f.Offset(gfx::Vector2d(delta_x, delta_y)); + + // Apply only the scale and translation component. + const gfx::Vector2dF& translate = target_transform.To2dTranslation(); + const gfx::Vector2dF& scale = target_transform.Scale2d(); + quad_rect_f.Scale(scale.x(), scale.y()); + quad_rect_f.Offset(translate.x(), translate.y()); + } else { + target_transform.TransformRect(&quad_rect_f); + } + drawn_rects->push_back(gfx::ToRoundedRect(quad_rect_f)); +} + // Smallest unit that impact anti-aliasing output. We use this to // determine when anti-aliasing is unnecessary. const float kAntiAliasingEpsilon = 1.0f / 1024.0f; @@ -420,6 +453,8 @@ prefer_draw_to_copy_ = output_surface_->context_provider() ->GetGpuFeatureInfo() .IsWorkaroundEnabled(gpu::PREFER_DRAW_TO_COPY); + use_fast_path_solid_color_quad_ = + features::IsUsingFastPathForSolidColorQuad(); InitializeSharedObjects(); } @@ -509,6 +544,9 @@ gl_->GenQueriesEXT(1, &occlusion_query_); gl_->BeginQueryEXT(GL_SAMPLES_PASSED_ARB, occlusion_query_); } + + // For each render pass, reset the drawn region. + drawn_rects_.clear(); } void GLRenderer::ClearFramebuffer() { @@ -1286,6 +1324,10 @@ ChooseRPDQProgram(params, CurrentRenderPassColorSpace()); UpdateRPDQUniforms(params); DrawRPDQ(*params); + + AccumulateDrawRects(params->quad->visible_rect, + params->quad->shared_quad_state->quad_to_target_transform, + &drawn_rects_); } bool GLRenderer::InitializeRPDQParameters( @@ -2120,41 +2162,84 @@ color = color_f.toSkColor(); } - SetShaderColor(color, opacity); - if (current_program_->rounded_corner_rect_location() != -1) { - SetShaderRoundedCorner( - quad->shared_quad_state->rounded_corner_bounds, - current_frame()->window_matrix * current_frame()->projection_matrix); - } + // Try using glClear to draw the solid color quad if possible. This is much + // more performant than executing the shader pipeline. + if (CanUseFastSolidColorDraw(quad) && !use_aa) { + // Pre-multiply the alpha and opacity to get the correct blending in case of + // transparent buffers. glClear does not have any alpha blending stage. + Float4 result = PremultipliedColor(color, opacity); + SkRGBA4f<kPremul_SkAlphaType> color_f_premul; + std::copy(result.data, result.data + 4, color_f_premul.vec()); - if (current_program_->tint_color_matrix_location() != -1) { - auto matrix = cc::DebugColors::TintCompositedContentColorTransformMatrix(); - gl_->UniformMatrix4fv(current_program_->tint_color_matrix_location(), 1, - false, matrix.data()); - } + gfx::RectF quad_rect_in_target_f(quad->visible_rect); - if (use_aa) { - gl_->Uniform3fv(current_program_->edge_location(), 8, edge); - } + device_transform.TransformRect(&quad_rect_in_target_f); + gfx::Rect quad_rect_in_target = gfx::ToRoundedRect(quad_rect_in_target_f); - // Enable blending when the quad properties require it or if we decided - // to use antialiasing. - SetBlendEnabled(quad->ShouldDrawWithBlending() || use_aa); - ApplyBlendModeUsingBlendFunc(quad->shared_quad_state->blend_mode); + // If we are using partial swap, make sure the new scissor rect is within + // the partial swap bounds. + if (!scissor_rect_.IsEmpty() && is_scissor_enabled_) + quad_rect_in_target.Intersect(scissor_rect_); - // Antialising requires a normalized quad, but this could lead to floating - // point precision errors, so only normalize when antialising is on. - if (use_aa) { - DrawQuadGeometryWithAA(quad, &local_quad, tile_rect); + gl_->Enable(GL_SCISSOR_TEST); + gl_->Scissor(quad_rect_in_target.x(), quad_rect_in_target.y(), + quad_rect_in_target.width(), quad_rect_in_target.height()); + + gl_->ClearColor(color_f_premul.fR, color_f_premul.fG, color_f_premul.fB, + color_f_premul.fA); + gl_->Clear(GL_COLOR_BUFFER_BIT); + + // Restore GL scissor state. + if (is_scissor_enabled_) + gl_->Enable(GL_SCISSOR_TEST); + else + gl_->Disable(GL_SCISSOR_TEST); + + gl_->Scissor(scissor_rect_.x(), scissor_rect_.y(), scissor_rect_.width(), + scissor_rect_.height()); } else { - PrepareGeometry(SHARED_BINDING); - SetShaderQuadF(local_quad); - SetShaderMatrix(current_frame()->projection_matrix * - quad->shared_quad_state->quad_to_target_transform); - gl_->DrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, nullptr); - num_triangles_drawn_ += 2; + SetShaderColor(color, opacity); + if (current_program_->rounded_corner_rect_location() != -1) { + SetShaderRoundedCorner( + quad->shared_quad_state->rounded_corner_bounds, + current_frame()->window_matrix * current_frame()->projection_matrix); + } + + if (current_program_->tint_color_matrix_location() != -1) { + auto matrix = + cc::DebugColors::TintCompositedContentColorTransformMatrix(); + gl_->UniformMatrix4fv(current_program_->tint_color_matrix_location(), 1, + false, matrix.data()); + } + + if (use_aa) { + gl_->Uniform3fv(current_program_->edge_location(), 8, edge); + } + + // Enable blending when the quad properties require it or if we decided + // to use antialiasing. + SetBlendEnabled(quad->ShouldDrawWithBlending() || use_aa); + ApplyBlendModeUsingBlendFunc(quad->shared_quad_state->blend_mode); + + // Antialiasing requires a normalized quad, but this could lead to floating + // point precision errors, so only normalize when antialiasing is on. + if (use_aa) { + DrawQuadGeometryWithAA(quad, &local_quad, tile_rect); + } else { + PrepareGeometry(SHARED_BINDING); + SetShaderQuadF(local_quad); + SetShaderMatrix(current_frame()->projection_matrix * + quad->shared_quad_state->quad_to_target_transform); + gl_->DrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, nullptr); + num_triangles_drawn_ += 2; + } + RestoreBlendFuncToDefault(quad->shared_quad_state->blend_mode); } - RestoreBlendFuncToDefault(quad->shared_quad_state->blend_mode); + + // Add the quad to the region that has been drawn. + AccumulateDrawRects(quad->visible_rect, + quad->shared_quad_state->quad_to_target_transform, + &drawn_rects_); } void GLRenderer::DrawTileQuad(const TileDrawQuad* quad, @@ -2193,6 +2278,10 @@ clip_region); else DrawContentQuadNoAA(quad, resource_id, clip_region); + + AccumulateDrawRects(quad->visible_rect, + quad->shared_quad_state->quad_to_target_transform, + &drawn_rects_); } void GLRenderer::DrawContentQuadAA(const ContentDrawQuadBase* quad, @@ -2633,6 +2722,11 @@ quad->shared_quad_state->quad_to_target_transform, tile_rect, region_quad, uvs); } + + // Track the region in the current target surface that has been drawn to. + AccumulateDrawRects(quad->visible_rect, + quad->shared_quad_state->quad_to_target_transform, + &drawn_rects_); } void GLRenderer::DrawStreamVideoQuad(const StreamVideoDrawQuad* quad, @@ -2708,6 +2802,10 @@ quad->shared_quad_state->quad_to_target_transform, tile_rect, region_quad, uvs); } + + AccumulateDrawRects(quad->visible_rect, + quad->shared_quad_state->quad_to_target_transform, + &drawn_rects_); } void GLRenderer::FlushTextureQuadCache(BoundGeometry flush_binding) { @@ -2905,6 +3003,11 @@ quad_rect_matrix.matrix().asColMajorf(m.data); draw_cache_.matrix_data.push_back(m); + // Track the region in the current target surface that has been drawn to. + AccumulateDrawRects(quad->visible_rect, + quad->shared_quad_state->quad_to_target_transform, + &drawn_rects_); + if (clip_region) { DCHECK_EQ(quad->rect, quad->visible_rect); gfx::QuadF scaled_region; @@ -4244,6 +4347,67 @@ return is_root_render_pass && !output_color_matrix.isIdentity(); } +bool GLRenderer::CanUseFastSolidColorDraw( + const SolidColorDrawQuad* quad) const { + const SharedQuadState* sqs = quad->shared_quad_state; + + if (!use_fast_path_solid_color_quad_) + return false; + + // Rounded corners require blending with the background, which is not possible + // with the glClear draw method. + if (!sqs->rounded_corner_bounds.IsEmpty()) + return false; + + // 3D transforms need vertex computation in 3D and cannot be handled using + // glClear(). + if (!sqs->quad_to_target_transform.IsFlat()) + return false; + + // glClear ignores stencil buffer. + if (stencil_shadow_) + return false; + + // Any non axis aligned transform cannot be handled by glClear. + if (!sqs->quad_to_target_transform.Preserves2dAxisAlignment()) + return false; + + // If no blending is needed for the quad, then fast draw can be safely used. + if (!quad->ShouldDrawWithBlending() && SkColorGetA(quad->color) == 255) + return true; + + // It is safe to use glClearColor with alpha blending when the render + // pass has transparent background because the blending happens against + // (0, 0, 0, 0) which is the same as replacing the destination color & alpha. + // However, if the render pass does not have a transparent background, using + // glClear with a color that has alpha or opacity, would end up punching an + // unwanted hole in the frame buffer. + if (!current_frame()->current_render_pass->has_transparent_background) + return false; + + // If the color has any alpha and blending is needed, ensure the blend mode + // allows replacing destination color & alpha. + const bool is_translucent = + SkColorGetA(quad->color) != 255 || quad->shared_quad_state->opacity < 1.f; + if (is_translucent && + !(quad->shared_quad_state->blend_mode == SkBlendMode::kSrc || + quad->shared_quad_state->blend_mode == SkBlendMode::kSrcOver)) { + return false; + } + + gfx::RectF quad_rect_in_target(quad->visible_rect); + sqs->quad_to_target_transform.TransformRect(&quad_rect_in_target); + const gfx::Rect quad_rect_in_target_rounded = + gfx::ToRoundedRect(quad_rect_in_target); + + // If the quad does not intersect with any region that has already been drawn + // to, then blending is not an issue and fast draw path can be used. + for (const auto& rect : drawn_rects_) + if (quad_rect_in_target_rounded.Intersects(rect)) + return false; + return true; +} + void GLRenderer::AllocateRenderPassResourceIfNeeded( const AggregatedRenderPassId& render_pass_id, const RenderPassRequirements& requirements) {
diff --git a/components/viz/service/display/gl_renderer.h b/components/viz/service/display/gl_renderer.h index 7e3373fe..ed6efb8 100644 --- a/components/viz/service/display/gl_renderer.h +++ b/components/viz/service/display/gl_renderer.h
@@ -372,6 +372,10 @@ bool HasOutputColorMatrix() const; + // Returns true if the given solid color draw quad can be safely drawn using + // the glClear function call. + bool CanUseFastSolidColorDraw(const SolidColorDrawQuad* quad) const; + // A map from RenderPass id to the texture used to draw the RenderPass from. base::flat_map<AggregatedRenderPassId, ScopedRenderPassTexture> render_pass_textures_; @@ -454,6 +458,7 @@ bool use_timer_query_ = false; bool use_occlusion_query_ = false; bool use_swap_with_bounds_ = false; + bool use_fast_path_solid_color_quad_ = false; // If true, tints all the composited content to red. bool tint_gl_composited_content_ = true; @@ -477,6 +482,9 @@ // quad type as string. base::queue<std::pair<unsigned, std::string>> timer_queries_; + // Keeps track of areas that have been drawn to in the current render pass. + std::vector<gfx::Rect> drawn_rects_; + // This may be null if the compositor is run on a thread without a // MessageLoop. scoped_refptr<base::SingleThreadTaskRunner> current_task_runner_;
diff --git a/components/viz/service/display/gl_renderer_unittest.cc b/components/viz/service/display/gl_renderer_unittest.cc index 3dfb5b542..d01c4ec2 100644 --- a/components/viz/service/display/gl_renderer_unittest.cc +++ b/components/viz/service/display/gl_renderer_unittest.cc
@@ -28,6 +28,7 @@ #include "cc/test/resource_provider_test_utils.h" #include "components/viz/client/client_resource_provider.h" #include "components/viz/common/display/renderer_settings.h" +#include "components/viz/common/features.h" #include "components/viz/common/frame_sinks/copy_output_request.h" #include "components/viz/common/frame_sinks/copy_output_result.h" #include "components/viz/common/quads/texture_draw_quad.h" @@ -1376,8 +1377,13 @@ EXPECT_FILTER_CALL(GL_LINEAR); EXPECT_CALL(*gl, DrawElements(_, _, _, _)); - // stream video, solid color and debug draw quads - EXPECT_CALL(*gl, DrawElements(_, _, _, _)).Times(3); + if (features::IsUsingFastPathForSolidColorQuad()) { + // stream video and debug draw quads + EXPECT_CALL(*gl, DrawElements(_, _, _, _)).Times(2); + } else { + // stream video, solid color, and debug draw quads + EXPECT_CALL(*gl, DrawElements(_, _, _, _)).Times(3); + } } gfx::Size viewport_size(100, 100); @@ -1534,8 +1540,15 @@ Expectation first_render_pass = EXPECT_CALL(*mock_gl, DrawElements(_, _, _, _)).Times(1); - // The second render pass is the root one, clearing should be prevented. - EXPECT_CALL(*mock_gl, Clear(clear_bits)).Times(0).After(first_render_pass); + if (features::IsUsingFastPathForSolidColorQuad()) { + // The second render pass is the root one, clearing should be prevented. The + // one call is expected due to the solid color draw quad which uses glClear + // to draw the quad. + EXPECT_CALL(*mock_gl, Clear(clear_bits)).Times(1).After(first_render_pass); + } else { + // The second render pass is the root one, clearing should be prevented. + EXPECT_CALL(*mock_gl, Clear(clear_bits)).Times(0).After(first_render_pass); + } EXPECT_CALL(*mock_gl, DrawElements(_, _, _, _)) .Times(AnyNumber()) @@ -1553,7 +1566,20 @@ public: ScissorTestOnClearCheckingGLES2Interface() = default; - void Clear(GLbitfield) override { EXPECT_FALSE(scissor_enabled_); } + void ClearColor(GLfloat r, GLfloat g, GLfloat b, GLfloat a) override { + // RGBA - {0, 0, 0, 0} is used to clear the buffer before drawing onto the + // render target. Any other color means a solid color draw quad is being + // drawn. + if (features::IsUsingFastPathForSolidColorQuad()) + is_drawing_solid_color_quad_ = !(r == 0 && g == 0 && b == 0 && a == 0); + } + + void Clear(GLbitfield bits) override { + // GL clear is also used to draw solid color draw quads. + if ((bits & GL_COLOR_BUFFER_BIT) && is_drawing_solid_color_quad_) + return; + EXPECT_FALSE(scissor_enabled_); + } void Enable(GLenum cap) override { if (cap == GL_SCISSOR_TEST) @@ -1567,6 +1593,7 @@ private: bool scissor_enabled_ = false; + bool is_drawing_solid_color_quad_ = false; }; TEST_F(GLRendererTest, ScissorTestWhenClearing) { @@ -1930,7 +1957,10 @@ } void DrawBlackFrame(const gfx::Size& viewport_size) { - EXPECT_CALL(*gl_, DrawElements(_, _, _, _)).Times(1); + // The feature enables a faster path to draw solid color quads that does not + // use GL draw calls but instead uses glClear. + if (!features::IsUsingFastPathForSolidColorQuad()) + EXPECT_CALL(*gl_, DrawElements(_, _, _, _)).Times(1); AggregatedRenderPassId root_pass_id{1}; AggregatedRenderPass* root_pass = cc::AddRenderPass( @@ -1962,6 +1992,7 @@ DrawBlackFrame(viewport_size); EXPECT_CALL(*gl_, DrawElements(_, _, _, _)).Times(1); + AggregatedRenderPassId root_pass_id{1}; AggregatedRenderPass* root_pass = cc::AddRenderPass( &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size), @@ -1969,6 +2000,11 @@ root_pass->damage_rect = gfx::Rect(0, 0, 25, 25); cc::AddQuad(root_pass, quad_rect, SK_ColorGREEN); + // Add rounded corners to the solid color draw quad so that the fast path + // of drawing using glClear is not used. + root_pass->shared_quad_state_list.front()->rounded_corner_bounds = + gfx::RRectF(gfx::RectF(quad_rect), 2.f); + renderer_->DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_); DrawFrame(renderer_.get(), viewport_size); } @@ -1992,6 +2028,11 @@ gfx::Rect(0, 0, 40, 40); root_pass->quad_list.front()->visible_rect = gfx::Rect(20, 20, 20, 20); + // Add rounded corners to the solid color draw quad so that the fast path + // of drawing using glClear is not used. + root_pass->shared_quad_state_list.front()->rounded_corner_bounds = + gfx::RRectF(gfx::RectF(quad_rect), 1.f); + renderer_->DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_); DrawFrame(renderer_.get(), viewport_size); // DrawElements should not be called because the visible rect is outside the @@ -2482,7 +2523,9 @@ class MockDCLayerOverlayProcessor : public DCLayerOverlayProcessor { public: MockDCLayerOverlayProcessor() - : DCLayerOverlayProcessor(&debug_settings_, true) {} + : DCLayerOverlayProcessor(&debug_settings_, + /*allowed_yuv_overlay_count=*/1, + true) {} ~MockDCLayerOverlayProcessor() override = default; MOCK_METHOD5(Process, void(DisplayResourceProvider* resource_provider, @@ -3046,6 +3089,358 @@ DrawFrame(&renderer, viewport_size); } +class FastSolidColorMockGLES2Interface : public TestGLES2Interface { + public: + FastSolidColorMockGLES2Interface() = default; + + MOCK_METHOD1(Enable, void(GLenum cap)); + MOCK_METHOD1(Disable, void(GLenum cap)); + MOCK_METHOD4(ClearColor, + void(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha)); + MOCK_METHOD4(Scissor, void(GLint x, GLint y, GLsizei width, GLsizei height)); +}; + +class GLRendererFastSolidColorTest : public GLRendererTest { + public: + void SetUp() override { + feature_list_.InitAndEnableFeature(features::kFastSolidColorDraw); + GLRendererTest::SetUp(); + + auto gl_owned = std::make_unique<FastSolidColorMockGLES2Interface>(); + gl_owned->set_have_post_sub_buffer(true); + gl_ = gl_owned.get(); + + auto provider = TestContextProvider::Create(std::move(gl_owned)); + provider->BindToCurrentThread(); + + output_surface_ = FakeOutputSurface::Create3d(std::move(provider)); + output_surface_->BindToClient(&output_surface_client_); + + resource_provider_ = std::make_unique<DisplayResourceProvider>( + DisplayResourceProvider::kGpu, output_surface_->context_provider(), + nullptr); + + settings_.partial_swap_enabled = true; + settings_.slow_down_compositing_scale_factor = 1; + settings_.allow_antialiasing = true; + + fake_renderer_ = std::make_unique<FakeRendererGL>( + &settings_, &debug_settings_, output_surface_.get(), + resource_provider_.get()); + fake_renderer_->Initialize(); + EXPECT_TRUE(fake_renderer_->use_partial_swap()); + fake_renderer_->SetVisible(true); + } + + void TearDown() override { + resource_provider_.reset(); + fake_renderer_.reset(); + output_surface_.reset(); + gl_ = nullptr; + + GLRendererTest::TearDown(); + } + + FastSolidColorMockGLES2Interface* gl_ptr() { return gl_; } + + FakeOutputSurface* output_surface() { return output_surface_.get(); } + + protected: + void AddExpectations(bool use_fast_path, + const gfx::Rect& scissor_rect, + SkColor color = SK_ColorBLACK, + bool enable_stencil = false) { + auto* gl = gl_ptr(); + + InSequence seq; + + // Restore GL state method calls + EXPECT_CALL(*gl, Disable(GL_DEPTH_TEST)); + EXPECT_CALL(*gl, Disable(GL_CULL_FACE)); + EXPECT_CALL(*gl, Disable(GL_STENCIL_TEST)); + EXPECT_CALL(*gl, Enable(GL_BLEND)); + EXPECT_CALL(*gl, Disable(GL_SCISSOR_TEST)); + EXPECT_CALL(*gl, Scissor(0, 0, 0, 0)); + + if (!enable_stencil) + EXPECT_CALL(*gl, ClearColor(0, 0, 0, 0)); + + if (use_fast_path) { + EXPECT_CALL(*gl, Enable(GL_SCISSOR_TEST)); + EXPECT_CALL(*gl, Scissor(scissor_rect.x(), scissor_rect.y(), + scissor_rect.width(), scissor_rect.height())); + + SkColor4f color_f = SkColor4f::FromColor(color); + EXPECT_CALL(*gl, + ClearColor(color_f.fR, color_f.fG, color_f.fB, color_f.fA)); + + EXPECT_CALL(*gl, Disable(GL_SCISSOR_TEST)); + EXPECT_CALL(*gl, Scissor(0, 0, 0, 0)); + } + + if (enable_stencil) { + EXPECT_CALL(*gl, Enable(GL_STENCIL_TEST)); + EXPECT_CALL(*gl, Disable(GL_BLEND)); + } + + EXPECT_CALL(*gl, Disable(GL_BLEND)); + } + + void RunTest(const gfx::Size& viewport_size) { + fake_renderer_->DecideRenderPassAllocationsForFrame( + render_passes_in_draw_order_); + DrawFrame(fake_renderer_.get(), viewport_size); + + auto* gl = gl_ptr(); + ASSERT_TRUE(gl); + Mock::VerifyAndClearExpectations(gl); + } + + private: + FastSolidColorMockGLES2Interface* gl_ = nullptr; + std::unique_ptr<FakeRendererGL> fake_renderer_; + std::unique_ptr<FakeOutputSurface> output_surface_; + std::unique_ptr<DisplayResourceProvider> resource_provider_; + cc::FakeOutputSurfaceClient output_surface_client_; + RendererSettings settings_; + base::test::ScopedFeatureList feature_list_; +}; + +TEST_F(GLRendererFastSolidColorTest, RoundedCorners) { + gfx::Size viewport_size(500, 500); + gfx::Rect root_pass_output_rect(400, 400); + gfx::Rect root_pass_damage_rect(10, 20, 300, 200); + gfx::Rect quad_rect(0, 50, 100, 100); + + AggregatedRenderPassId root_pass_id{1}; + AggregatedRenderPass* root_pass = cc::AddRenderPassWithDamage( + &render_passes_in_draw_order_, root_pass_id, root_pass_output_rect, + root_pass_damage_rect, gfx::Transform(), cc::FilterOperations()); + root_pass->damage_rect = root_pass_damage_rect; + cc::AddQuad(root_pass, quad_rect, SK_ColorRED); + + root_pass->shared_quad_state_list.front()->rounded_corner_bounds = + gfx::RRectF(gfx::RectF(quad_rect), 5.f); + + // Fast Solid color draw quads should not be executed. + AddExpectations(false /*use_fast_path*/, gfx::Rect()); + + RunTest(viewport_size); +} + +TEST_F(GLRendererFastSolidColorTest, Transform3DSlowPath) { + gfx::Size viewport_size(500, 500); + gfx::Rect root_pass_damage_rect(10, 20, 300, 200); + gfx::Rect quad_rect(0, 50, 100, 100); + + AggregatedRenderPassId root_pass_id{1}; + AggregatedRenderPass* root_pass = cc::AddRenderPass( + &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size), + gfx::Transform(), cc::FilterOperations()); + root_pass->damage_rect = root_pass_damage_rect; + cc::AddQuad(root_pass, quad_rect, SK_ColorRED); + + gfx::Transform tm_3d; + tm_3d.RotateAboutYAxis(30.0); + ASSERT_FALSE(tm_3d.IsFlat()); + + root_pass->shared_quad_state_list.front()->quad_to_target_transform = tm_3d; + + AddExpectations(false /*use_fast_path*/, gfx::Rect()); + + RunTest(viewport_size); +} + +TEST_F(GLRendererFastSolidColorTest, NonTransform3DFastPath) { + gfx::Size viewport_size(500, 500); + gfx::Rect root_pass_damage_rect(10, 20, 300, 200); + gfx::Rect quad_rect(0, 0, 200, 200); + + AggregatedRenderPassId root_pass_id{1}; + AggregatedRenderPass* root_pass = cc::AddRenderPass( + &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size), + gfx::Transform(), cc::FilterOperations()); + root_pass->damage_rect = root_pass_damage_rect; + cc::AddQuad(root_pass, quad_rect, SK_ColorRED); + + gfx::Transform tm_non_3d; + tm_non_3d.Translate(10.f, 10.f); + ASSERT_TRUE(tm_non_3d.IsFlat()); + + root_pass->shared_quad_state_list.front()->quad_to_target_transform = + tm_non_3d; + + AddExpectations(true /*use_fast_path*/, gfx::Rect(10, 290, 200, 200), + SK_ColorRED); + + RunTest(viewport_size); +} + +TEST_F(GLRendererFastSolidColorTest, NonAxisAlignSlowPath) { + gfx::Size viewport_size(500, 500); + gfx::Rect root_pass_damage_rect(10, 20, 300, 200); + gfx::Rect quad_rect(0, 0, 200, 200); + + AggregatedRenderPassId root_pass_id{1}; + AggregatedRenderPass* root_pass = cc::AddRenderPass( + &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size), + gfx::Transform(), cc::FilterOperations()); + root_pass->damage_rect = root_pass_damage_rect; + cc::AddQuad(root_pass, quad_rect, SK_ColorRED); + + gfx::Transform tm_non_axis_align; + tm_non_axis_align.RotateAboutZAxis(45.0); + ASSERT_TRUE(tm_non_axis_align.IsFlat()); + + root_pass->shared_quad_state_list.front()->quad_to_target_transform = + tm_non_axis_align; + + AddExpectations(false /*use_fast_path*/, gfx::Rect()); + + RunTest(viewport_size); +} + +TEST_F(GLRendererFastSolidColorTest, StencilSlowPath) { + gfx::Size viewport_size(500, 500); + gfx::Rect root_pass_damage_rect(10, 20, 300, 200); + gfx::Rect quad_rect(0, 0, 200, 200); + + AggregatedRenderPassId root_pass_id{1}; + AggregatedRenderPass* root_pass = cc::AddRenderPass( + &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size), + gfx::Transform(), cc::FilterOperations()); + root_pass->damage_rect = root_pass_damage_rect; + root_pass->has_transparent_background = false; + + cc::AddQuad(root_pass, quad_rect, SK_ColorRED); + + AddExpectations(false /*use_fast_path*/, gfx::Rect(), SK_ColorRED, + true /*enable_stencil*/); + output_surface()->set_has_external_stencil_test(true); + + RunTest(viewport_size); +} + +TEST_F(GLRendererFastSolidColorTest, NeedsBlendingSlowPath) { + gfx::Size viewport_size(500, 500); + gfx::Rect root_pass_damage_rect(2, 3, 300, 200); + gfx::Rect full_quad_rect(0, 0, 50, 50); + gfx::Rect quad_rect_1(0, 0, 20, 20); + gfx::Rect quad_rect_2(20, 0, 20, 20); + gfx::Rect quad_rect_3(0, 20, 20, 20); + + AggregatedRenderPassId root_pass_id{1}; + AggregatedRenderPass* root_pass = cc::AddRenderPass( + &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size), + gfx::Transform(), cc::FilterOperations()); + root_pass->damage_rect = root_pass_damage_rect; + + cc::AddQuad(root_pass, quad_rect_1, SK_ColorRED); + root_pass->quad_list.back()->needs_blending = true; + + cc::AddQuad(root_pass, quad_rect_2, SK_ColorBLUE); + root_pass->shared_quad_state_list.back()->opacity = 0.5f; + + cc::AddQuad(root_pass, quad_rect_3, SK_ColorGREEN); + root_pass->shared_quad_state_list.back()->blend_mode = SkBlendMode::kDstIn; + + cc::AddQuad(root_pass, full_quad_rect, SK_ColorBLACK); + + // The first solid color quad would use a fast path, but the other quads that + // require blending will use the slower method. + AddExpectations(true /*use_fast_path*/, gfx::Rect(0, 450, 50, 50), + SK_ColorBLACK, false /*enable_stencil*/); + + RunTest(viewport_size); +} + +TEST_F(GLRendererFastSolidColorTest, NeedsBlendingFastPath) { + gfx::Size viewport_size(500, 500); + gfx::Rect root_pass_damage_rect(2, 3, 300, 200); + gfx::Rect quad_rect_1(0, 0, 20, 20); + gfx::Rect quad_rect_2(20, 0, 20, 20); + gfx::Rect quad_rect_3(0, 20, 20, 20); + + AggregatedRenderPassId root_pass_id{1}; + AggregatedRenderPass* root_pass = cc::AddRenderPass( + &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size), + gfx::Transform(), cc::FilterOperations()); + root_pass->damage_rect = root_pass_damage_rect; + + cc::AddQuad(root_pass, quad_rect_1, SK_ColorRED); + root_pass->quad_list.back()->needs_blending = true; + + cc::AddQuad(root_pass, quad_rect_2, SK_ColorBLUE); + root_pass->shared_quad_state_list.back()->opacity = 0.5f; + + cc::AddQuad(root_pass, quad_rect_3, SK_ColorGREEN); + root_pass->shared_quad_state_list.back()->blend_mode = SkBlendMode::kDstIn; + + auto* gl = gl_ptr(); + + // The quads here despite having blend requirements can still use fast path + // because they do not intersect with any other quad that has already been + // drawn onto the render target. + InSequence seq; + + // // Restore GL state method calls + EXPECT_CALL(*gl, Disable(GL_DEPTH_TEST)); + EXPECT_CALL(*gl, Disable(GL_CULL_FACE)); + EXPECT_CALL(*gl, Disable(GL_STENCIL_TEST)); + EXPECT_CALL(*gl, Enable(GL_BLEND)); + EXPECT_CALL(*gl, Disable(GL_SCISSOR_TEST)); + EXPECT_CALL(*gl, Scissor(0, 0, 0, 0)); + EXPECT_CALL(*gl, ClearColor(0, 0, 0, 0)); + + // Fast path draw used for green quad. + EXPECT_CALL(*gl, Enable(GL_SCISSOR_TEST)); + EXPECT_CALL(*gl, Scissor(0, 460, 20, 20)); + EXPECT_CALL(*gl, ClearColor(0, 1, 0, 1)); + EXPECT_CALL(*gl, Disable(GL_SCISSOR_TEST)); + EXPECT_CALL(*gl, Scissor(0, 0, 0, 0)); + + // Fast path draw used for blue quad. + EXPECT_CALL(*gl, Enable(GL_SCISSOR_TEST)); + EXPECT_CALL(*gl, Scissor(20, 480, 20, 20)); + EXPECT_CALL(*gl, ClearColor(0, 0, 0.5f, 0.5f)); + EXPECT_CALL(*gl, Disable(GL_SCISSOR_TEST)); + EXPECT_CALL(*gl, Scissor(0, 0, 0, 0)); + + // Fast path draw used for red quad. + EXPECT_CALL(*gl, Enable(GL_SCISSOR_TEST)); + EXPECT_CALL(*gl, Scissor(0, 480, 20, 20)); + EXPECT_CALL(*gl, ClearColor(1, 0, 0, 1)); + EXPECT_CALL(*gl, Disable(GL_SCISSOR_TEST)); + EXPECT_CALL(*gl, Scissor(0, 0, 0, 0)); + + EXPECT_CALL(*gl, Disable(GL_BLEND)); + + RunTest(viewport_size); +} + +TEST_F(GLRendererFastSolidColorTest, AntiAliasSlowPath) { + gfx::Size viewport_size(500, 500); + gfx::Rect root_pass_damage_rect(10, 20, 300, 200); + gfx::Rect quad_rect(0, 0, 200, 200); + + AggregatedRenderPassId root_pass_id{1}; + AggregatedRenderPass* root_pass = cc::AddRenderPass( + &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size), + gfx::Transform(), cc::FilterOperations()); + root_pass->damage_rect = root_pass_damage_rect; + cc::AddQuad(root_pass, quad_rect, SK_ColorRED); + + gfx::Transform tm_aa; + tm_aa.Translate(0.1f, 0.1f); + ASSERT_TRUE(tm_aa.IsFlat()); + + root_pass->shared_quad_state_list.front()->quad_to_target_transform = tm_aa; + + AddExpectations(false /*use_fast_path*/, gfx::Rect()); + + RunTest(viewport_size); +} + class PartialSwapMockGLES2Interface : public TestGLES2Interface { public: PartialSwapMockGLES2Interface() = default; @@ -3057,6 +3452,13 @@ }; class GLRendererPartialSwapTest : public GLRendererTest { + public: + void SetUp() override { + // Force enable fast solid color draw path. + scoped_feature_list_.InitAndEnableFeature(features::kFastSolidColorDraw); + GLRendererTest::SetUp(); + } + protected: void RunTest(bool partial_swap, bool set_draw_rectangle) { auto gl_owned = std::make_unique<PartialSwapMockGLES2Interface>(); @@ -3095,15 +3497,30 @@ EXPECT_CALL(*gl, Disable(GL_DEPTH_TEST)).Times(1); EXPECT_CALL(*gl, Disable(GL_CULL_FACE)).Times(1); EXPECT_CALL(*gl, Disable(GL_STENCIL_TEST)).Times(1); - EXPECT_CALL(*gl, Disable(GL_BLEND)).Times(2); EXPECT_CALL(*gl, Enable(GL_BLEND)).Times(1); - EXPECT_CALL(*gl, Disable(GL_SCISSOR_TEST)).Times(1); - EXPECT_CALL(*gl, Scissor(0, 0, 0, 0)).Times(1); - if (set_draw_rectangle) { - EXPECT_CALL(*gl, Enable(GL_SCISSOR_TEST)).Times(1); - EXPECT_CALL(*gl, Scissor(0, 0, 100, 100)).Times(1); + + if (output_surface->capabilities().supports_dc_layers) { + EXPECT_CALL(*gl, Disable(GL_SCISSOR_TEST)).Times(1); + EXPECT_CALL(*gl, Scissor(0, 0, 0, 0)).Times(1); + + // Root render pass requires a scissor if the output surface supports + // dc layers. + EXPECT_CALL(*gl, Enable(GL_SCISSOR_TEST)).Times(3); + EXPECT_CALL(*gl, Scissor(0, 0, 100, 100)).Times(3); + } else { + EXPECT_CALL(*gl, Disable(GL_SCISSOR_TEST)).Times(2); + EXPECT_CALL(*gl, Scissor(0, 0, 0, 0)).Times(2); + if (set_draw_rectangle) { + EXPECT_CALL(*gl, Enable(GL_SCISSOR_TEST)).Times(2); + EXPECT_CALL(*gl, Scissor(0, 0, 100, 100)).Times(2); + } else { + EXPECT_CALL(*gl, Enable(GL_SCISSOR_TEST)).Times(1); + EXPECT_CALL(*gl, Scissor(0, 0, 100, 100)).Times(1); + } } + EXPECT_CALL(*gl, Disable(GL_BLEND)).Times(1); + AggregatedRenderPassId root_pass_id{1}; AggregatedRenderPass* root_pass = cc::AddRenderPass( &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size), @@ -3136,19 +3553,34 @@ gfx::Rect output_rectangle = partial_swap ? root_pass_damage_rect : gfx::Rect(viewport_size); + // The scissor is flipped, so subtract the y coord and height from the + // bottom of the GL viewport. + gfx::Rect scissor_rect(output_rectangle.x(), + viewport_size.height() - output_rectangle.y() - + output_rectangle.height(), + output_rectangle.width(), + output_rectangle.height()); + + // Drawing the solid color quad using glClear and scissor rect. + EXPECT_CALL(*gl, Enable(GL_SCISSOR_TEST)); + EXPECT_CALL(*gl, Scissor(scissor_rect.x(), scissor_rect.y(), + scissor_rect.width(), scissor_rect.height())); + if (partial_swap || set_draw_rectangle) { EXPECT_CALL(*gl, Enable(GL_SCISSOR_TEST)); - // The scissor is flipped, so subtract the y coord and height from the - // bottom of the GL viewport. - EXPECT_CALL( - *gl, Scissor(output_rectangle.x(), - viewport_size.height() - output_rectangle.y() - - output_rectangle.height(), - output_rectangle.width(), output_rectangle.height())); + EXPECT_CALL(*gl, Scissor(scissor_rect.x(), scissor_rect.y(), + scissor_rect.width(), scissor_rect.height())); } - // The quad doesn't need blending. - EXPECT_CALL(*gl, Disable(GL_BLEND)); + // Restore GL state after solid color draw quad. + if (partial_swap || set_draw_rectangle) { + EXPECT_CALL(*gl, Enable(GL_SCISSOR_TEST)); + EXPECT_CALL(*gl, Scissor(scissor_rect.x(), scissor_rect.y(), + scissor_rect.width(), scissor_rect.height())); + } else { + EXPECT_CALL(*gl, Disable(GL_SCISSOR_TEST)); + EXPECT_CALL(*gl, Scissor(0, 0, 0, 0)); + } // Blending is disabled at the end of the frame. EXPECT_CALL(*gl, Disable(GL_BLEND)); @@ -3164,6 +3596,9 @@ Mock::VerifyAndClearExpectations(gl); } } + + private: + base::test::ScopedFeatureList scoped_feature_list_; }; TEST_F(GLRendererPartialSwapTest, PartialSwap) { @@ -3236,7 +3671,8 @@ auto processor = std::make_unique<OverlayProcessorWin>( output_surface.get(), - std::make_unique<DCLayerOverlayProcessor>(&debug_settings_, true)); + std::make_unique<DCLayerOverlayProcessor>( + &debug_settings_, /*allowed_yuv_overlay_count=*/1, true)); RendererSettings settings; settings.partial_swap_enabled = true;
diff --git a/components/viz/service/display/overlay_dc_unittest.cc b/components/viz/service/display/overlay_dc_unittest.cc index c466c78..0a2c6d11 100644 --- a/components/viz/service/display/overlay_dc_unittest.cc +++ b/components/viz/service/display/overlay_dc_unittest.cc
@@ -101,10 +101,11 @@ class DCTestOverlayProcessor : public OverlayProcessorWin { public: explicit DCTestOverlayProcessor(OutputSurface* output_surface) - : OverlayProcessorWin( - output_surface, - std::make_unique<DCLayerOverlayProcessor>(&debug_settings_, true)) { - } + : OverlayProcessorWin(output_surface, + std::make_unique<DCLayerOverlayProcessor>( + &debug_settings_, + /*allowed_yuv_overlay_count=*/1, + true)) {} DebugRendererSettings debug_settings_; };
diff --git a/components/viz/service/display/overlay_processor_interface.cc b/components/viz/service/display/overlay_processor_interface.cc index 659dfb7..d111caf9b 100644 --- a/components/viz/service/display/overlay_processor_interface.cc +++ b/components/viz/service/display/overlay_processor_interface.cc
@@ -92,8 +92,8 @@ enable_ca_overlay); #elif defined(OS_WIN) return std::make_unique<OverlayProcessorWin>( - output_surface, - std::make_unique<DCLayerOverlayProcessor>(debug_settings)); + output_surface, std::make_unique<DCLayerOverlayProcessor>( + debug_settings, /*allowed_yuv_overlay_count=*/1)); #elif defined(USE_OZONE) if (!features::IsUsingOzonePlatform()) return std::make_unique<OverlayProcessorStub>();
diff --git a/components/webxr/BUILD.gn b/components/webxr/BUILD.gn new file mode 100644 index 0000000..719ac8a --- /dev/null +++ b/components/webxr/BUILD.gn
@@ -0,0 +1,17 @@ +source_set("webxr") { + defines = [] + sources = [ + "mailbox_to_surface_bridge_impl.cc", + "mailbox_to_surface_bridge_impl.h", + ] + + deps = [ + "//components/viz/common:common", + "//content/public/browser:browser", + "//device/vr/android:vr_android", + "//gpu/command_buffer/common:common", + "//gpu/ipc/common:common", + "//services/viz/public/cpp/gpu:gpu", + "//ui/gl:gl", + ] +}
diff --git a/components/webxr/DEPS b/components/webxr/DEPS new file mode 100644 index 0000000..c717a6fc --- /dev/null +++ b/components/webxr/DEPS
@@ -0,0 +1,12 @@ +include_rules = [ + "+components/viz/common/gpu/context_provider.h", + "+content/public/browser", + "+device/vr/android/mailbox_to_surface_bridge.h", + "+gpu/GLES2/gl2extchromium.h", + "+gpu/command_buffer/client", + "+gpu/command_buffer/common", + "+gpu/ipc", + "+services/viz/public/cpp/gpu/context_provider_command_buffer.h", + "+ui/gfx", + "+ui/gl/android", +] \ No newline at end of file
diff --git a/components/webxr/OWNERS b/components/webxr/OWNERS new file mode 100644 index 0000000..0890a7e8 --- /dev/null +++ b/components/webxr/OWNERS
@@ -0,0 +1,5 @@ +alcooper@chromium.org +bialpio@chromium.org + +# TEAM: xr-dev@chromium.org +# COMPONENT: Blink>WebXR
diff --git a/chrome/browser/android/vr/mailbox_to_surface_bridge.cc b/components/webxr/mailbox_to_surface_bridge_impl.cc similarity index 84% rename from chrome/browser/android/vr/mailbox_to_surface_bridge.cc rename to components/webxr/mailbox_to_surface_bridge_impl.cc index 8c1e0f2..6cdd4c5d 100644 --- a/chrome/browser/android/vr/mailbox_to_surface_bridge.cc +++ b/components/webxr/mailbox_to_surface_bridge_impl.cc
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "chrome/browser/android/vr/mailbox_to_surface_bridge.h" +#include "components/webxr/mailbox_to_surface_bridge_impl.h" #include <memory> #include <string> @@ -28,6 +28,7 @@ #include "gpu/ipc/common/gpu_surface_tracker.h" #include "services/viz/public/cpp/gpu/context_provider_command_buffer.h" #include "ui/gfx/color_space.h" +#include "ui/gfx/transform.h" #include "ui/gl/android/surface_texture.h" #include <android/native_window_jni.h> @@ -43,8 +44,10 @@ attribute vec4 a_Position; attribute vec2 a_TexCoordinate; varying highp vec2 v_TexCoordinate; + uniform mat4 u_UvTransform; void main() { - v_TexCoordinate = a_TexCoordinate; + highp vec4 uv_in = vec4(a_TexCoordinate.x, a_TexCoordinate.y, 0, 1); + v_TexCoordinate = (u_UvTransform * uv_in).xy; gl_Position = a_Position; } ); @@ -134,7 +137,7 @@ GLuint ConsumeTexture(gpu::gles2::GLES2Interface* gl, const gpu::MailboxHolder& mailbox) { - TRACE_EVENT0("gpu", "MailboxToSurfaceBridge::ConsumeTexture"); + TRACE_EVENT0("gpu", "MailboxToSurfaceBridgeImpl::ConsumeTexture"); gl->WaitSyncTokenCHROMIUM(mailbox.sync_token.GetConstData()); return gl->CreateAndTexStorage2DSharedImageCHROMIUM(mailbox.mailbox.name); @@ -142,13 +145,13 @@ } // namespace -namespace vr { +namespace webxr { -MailboxToSurfaceBridge::MailboxToSurfaceBridge() { +MailboxToSurfaceBridgeImpl::MailboxToSurfaceBridgeImpl() { DVLOG(1) << __FUNCTION__; } -MailboxToSurfaceBridge::~MailboxToSurfaceBridge() { +MailboxToSurfaceBridgeImpl::~MailboxToSurfaceBridgeImpl() { if (surface_handle_) { // Unregister from the surface tracker to avoid a resource leak. gpu::GpuSurfaceTracker* tracker = gpu::GpuSurfaceTracker::Get(); @@ -158,17 +161,17 @@ DVLOG(1) << __FUNCTION__; } -bool MailboxToSurfaceBridge::IsConnected() { +bool MailboxToSurfaceBridgeImpl::IsConnected() { return context_provider_ && gl_ && context_support_; } -bool MailboxToSurfaceBridge::IsGpuWorkaroundEnabled(int32_t workaround) { +bool MailboxToSurfaceBridgeImpl::IsGpuWorkaroundEnabled(int32_t workaround) { DCHECK(IsConnected()); return context_provider_->GetGpuFeatureInfo().IsWorkaroundEnabled(workaround); } -void MailboxToSurfaceBridge::OnContextAvailableOnUiThread( +void MailboxToSurfaceBridgeImpl::OnContextAvailableOnUiThread( scoped_refptr<viz::ContextProvider> provider) { DVLOG(1) << __FUNCTION__; // Must save a reference to the viz::ContextProvider to keep it alive, @@ -180,11 +183,11 @@ gl_thread_task_runner_->PostTask( FROM_HERE, base::BindOnce( - &MailboxToSurfaceBridge::BindContextProviderToCurrentThread, + &MailboxToSurfaceBridgeImpl::BindContextProviderToCurrentThread, base::Unretained(this))); } -void MailboxToSurfaceBridge::BindContextProviderToCurrentThread() { +void MailboxToSurfaceBridgeImpl::BindContextProviderToCurrentThread() { auto result = context_provider_->BindToCurrentThread(); if (result != gpu::ContextResult::kSuccess) { DLOG(ERROR) << "Failed to init viz::ContextProvider"; @@ -210,7 +213,7 @@ } } -void MailboxToSurfaceBridge::CreateSurface( +void MailboxToSurfaceBridgeImpl::CreateSurface( gl::SurfaceTexture* surface_texture) { ANativeWindow* window = surface_texture->CreateSurface(); gpu::GpuSurfaceTracker* tracker = gpu::GpuSurfaceTracker::Get(); @@ -225,7 +228,7 @@ ANativeWindow_release(window); } -void MailboxToSurfaceBridge::CreateAndBindContextProvider( +void MailboxToSurfaceBridgeImpl::CreateAndBindContextProvider( base::OnceClosure on_bound_callback) { gl_thread_task_runner_ = base::ThreadTaskRunnerHandle::Get(); on_context_bound_ = std::move(on_bound_callback); @@ -234,7 +237,7 @@ // until the context becomes available. So pass it on to the callback, so that // it stays alive, and is destroyed on the same thread once done. auto callback = - base::BindOnce(&MailboxToSurfaceBridge::OnContextAvailableOnUiThread, + base::BindOnce(&MailboxToSurfaceBridgeImpl::OnContextAvailableOnUiThread, weak_ptr_factory_.GetWeakPtr()); content::GetUIThreadTaskRunner({})->PostTask( @@ -272,7 +275,7 @@ surface_handle_, std::move(callback))); } -void MailboxToSurfaceBridge::ResizeSurface(int width, int height) { +void MailboxToSurfaceBridgeImpl::ResizeSurface(int width, int height) { surface_width_ = width; surface_height_ = height; @@ -289,8 +292,14 @@ gl_->Viewport(0, 0, surface_width_, surface_height_); } -bool MailboxToSurfaceBridge::CopyMailboxToSurfaceAndSwap( +bool MailboxToSurfaceBridgeImpl::CopyMailboxToSurfaceAndSwap( const gpu::MailboxHolder& mailbox) { + return CopyMailboxToSurfaceAndSwap(mailbox, gfx::Transform()); +} + +bool MailboxToSurfaceBridgeImpl::CopyMailboxToSurfaceAndSwap( + const gpu::MailboxHolder& mailbox, + const gfx::Transform& uv_transform) { if (!IsConnected()) { // We may not have a context yet, i.e. due to surface initialization // being incomplete. This is not an error, but we obviously can't draw @@ -316,26 +325,28 @@ GLuint sourceTexture = ConsumeTexture(gl_, mailbox); gl_->BeginSharedImageAccessDirectCHROMIUM( sourceTexture, GL_SHARED_IMAGE_ACCESS_MODE_READ_CHROMIUM); - DrawQuad(sourceTexture); + DrawQuad(sourceTexture, uv_transform); gl_->EndSharedImageAccessDirectCHROMIUM(sourceTexture); gl_->DeleteTextures(1, &sourceTexture); gl_->SwapBuffers(swap_id_++); return true; } -void MailboxToSurfaceBridge::GenSyncToken(gpu::SyncToken* out_sync_token) { +void MailboxToSurfaceBridgeImpl::GenSyncToken(gpu::SyncToken* out_sync_token) { TRACE_EVENT0("gpu", __FUNCTION__); DCHECK(IsConnected()); gl_->GenSyncTokenCHROMIUM(out_sync_token->GetData()); } -void MailboxToSurfaceBridge::WaitSyncToken(const gpu::SyncToken& sync_token) { +void MailboxToSurfaceBridgeImpl::WaitSyncToken( + const gpu::SyncToken& sync_token) { TRACE_EVENT0("gpu", __FUNCTION__); DCHECK(IsConnected()); gl_->WaitSyncTokenCHROMIUM(sync_token.GetConstData()); } -void MailboxToSurfaceBridge::WaitForClientGpuFence(gfx::GpuFence* gpu_fence) { +void MailboxToSurfaceBridgeImpl::WaitForClientGpuFence( + gfx::GpuFence* gpu_fence) { TRACE_EVENT0("gpu", __FUNCTION__); DCHECK(IsConnected()); GLuint id = gl_->CreateClientGpuFenceCHROMIUM(gpu_fence->AsClientGpuFence()); @@ -343,7 +354,7 @@ gl_->DestroyGpuFenceCHROMIUM(id); } -void MailboxToSurfaceBridge::CreateGpuFence( +void MailboxToSurfaceBridgeImpl::CreateGpuFence( const gpu::SyncToken& sync_token, base::OnceCallback<void(std::unique_ptr<gfx::GpuFence>)> callback) { TRACE_EVENT0("gpu", __FUNCTION__); @@ -354,7 +365,7 @@ gl_->DestroyGpuFenceCHROMIUM(id); } -gpu::MailboxHolder MailboxToSurfaceBridge::CreateSharedImage( +gpu::MailboxHolder MailboxToSurfaceBridgeImpl::CreateSharedImage( gpu::GpuMemoryBufferImplAndroidHardwareBuffer* buffer, const gfx::ColorSpace& color_space, uint32_t usage) { @@ -375,7 +386,7 @@ return mailbox_holder; } -void MailboxToSurfaceBridge::DestroySharedImage( +void MailboxToSurfaceBridgeImpl::DestroySharedImage( const gpu::MailboxHolder& mailbox_holder) { TRACE_EVENT0("gpu", __FUNCTION__); DCHECK(IsConnected()); @@ -385,12 +396,12 @@ sii->DestroySharedImage(mailbox_holder.sync_token, mailbox_holder.mailbox); } -void MailboxToSurfaceBridge::DestroyContext() { +void MailboxToSurfaceBridgeImpl::DestroyContext() { gl_ = nullptr; context_provider_ = nullptr; } -void MailboxToSurfaceBridge::InitializeRenderer() { +void MailboxToSurfaceBridgeImpl::InitializeRenderer() { GLuint vertex_shader_handle = CompileShader(gl_, GL_VERTEX_SHADER, kQuadCopyVertex); if (!vertex_shader_handle) { @@ -421,6 +432,8 @@ gl_->GetAttribLocation(program_handle, "a_TexCoordinate"); GLuint texUniform_handle = gl_->GetUniformLocation(program_handle, "u_Texture"); + uniform_uv_transform_handle_ = + gl_->GetUniformLocation(program_handle, "u_UvTransform"); GLuint vertexBuffer = 0; gl_->GenBuffers(1, &vertexBuffer); @@ -464,7 +477,8 @@ gl_->Uniform1i(texUniform_handle, 0); } -void MailboxToSurfaceBridge::DrawQuad(unsigned int texture_handle) { +void MailboxToSurfaceBridgeImpl::DrawQuad(unsigned int texture_handle, + const gfx::Transform& uv_transform) { DCHECK(IsConnected()); // We're redrawing over the entire viewport, but it's generally more @@ -475,6 +489,11 @@ // it's not supported on older devices such as Nexus 5X. gl_->Clear(GL_COLOR_BUFFER_BIT); + float uv_transform_floats[16]; + uv_transform.matrix().asColMajorf(uv_transform_floats); + gl_->UniformMatrix4fv(uniform_uv_transform_handle_, 1, GL_FALSE, + &uv_transform_floats[0]); + // Configure texture. This is a 1:1 pixel copy since the surface // size is resized to match the source canvas, so we can use // GL_NEAREST. @@ -482,8 +501,17 @@ gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + if (uv_transform.IsIdentity()) { + gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + } else { + gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + } gl_->DrawArrays(GL_TRIANGLE_FAN, 0, 4); } -} // namespace vr +std::unique_ptr<device::MailboxToSurfaceBridge> +MailboxToSurfaceBridgeFactoryImpl::Create() const { + return std::make_unique<MailboxToSurfaceBridgeImpl>(); +} + +} // namespace webxr
diff --git a/components/webxr/mailbox_to_surface_bridge_impl.h b/components/webxr/mailbox_to_surface_bridge_impl.h new file mode 100644 index 0000000..d611c65 --- /dev/null +++ b/components/webxr/mailbox_to_surface_bridge_impl.h
@@ -0,0 +1,124 @@ +// Copyright 2017 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_WEBXR_MAILBOX_TO_SURFACE_BRIDGE_IMPL_H_ +#define COMPONENTS_WEBXR_MAILBOX_TO_SURFACE_BRIDGE_IMPL_H_ + +#include "base/callback.h" +#include "base/macros.h" +#include "base/memory/weak_ptr.h" +#include "base/single_thread_task_runner.h" +#include "device/vr/android/mailbox_to_surface_bridge.h" +#include "gpu/command_buffer/common/sync_token.h" +#include "gpu/ipc/common/surface_handle.h" +#include "ui/gfx/buffer_format_util.h" +#include "ui/gfx/gpu_fence.h" +#include "ui/gl/android/scoped_java_surface.h" + +namespace gpu { +class ContextSupport; + +namespace gles2 { +class GLES2Interface; +} +} // namespace gpu + +namespace viz { +class ContextProvider; +} + +namespace webxr { + +class MailboxToSurfaceBridgeImpl : public device::MailboxToSurfaceBridge { + public: + // It's OK to create an object instance and pass it to a different thread, + // i.e. to enable dependency injection for a unit test, but all methods on it + // must be called consistently on a single GL thread. This is verified by + // DCHECKs. + MailboxToSurfaceBridgeImpl(); + ~MailboxToSurfaceBridgeImpl() override; + + bool IsConnected() override; + + bool IsGpuWorkaroundEnabled(int32_t workaround) override; + + void CreateSurface(gl::SurfaceTexture*) override; + + void CreateAndBindContextProvider(base::OnceClosure callback) override; + + // All other public methods below must be called on the GL thread + // (except when marked otherwise). + + void ResizeSurface(int width, int height) override; + + bool CopyMailboxToSurfaceAndSwap(const gpu::MailboxHolder& mailbox) override; + + bool CopyMailboxToSurfaceAndSwap(const gpu::MailboxHolder& mailbox, + const gfx::Transform& uv_transform) override; + + void GenSyncToken(gpu::SyncToken* out_sync_token) override; + + void WaitSyncToken(const gpu::SyncToken& sync_token) override; + + void WaitForClientGpuFence(gfx::GpuFence*) override; + + void CreateGpuFence(const gpu::SyncToken& sync_token, + base::OnceCallback<void(std::unique_ptr<gfx::GpuFence>)> + callback) override; + + gpu::MailboxHolder CreateSharedImage( + gpu::GpuMemoryBufferImplAndroidHardwareBuffer* buffer, + const gfx::ColorSpace& color_space, + uint32_t usage) override; + + void DestroySharedImage(const gpu::MailboxHolder& mailbox_holder) override; + + private: + void BindContextProviderToCurrentThread(); + void OnContextAvailableOnUiThread( + scoped_refptr<viz::ContextProvider> provider); + void InitializeRenderer(); + void DestroyContext(); + void DrawQuad(unsigned int textureHandle, const gfx::Transform& uv_transform); + + scoped_refptr<viz::ContextProvider> context_provider_; + std::unique_ptr<gl::ScopedJavaSurface> surface_; + gpu::gles2::GLES2Interface* gl_ = nullptr; + gpu::ContextSupport* context_support_ = nullptr; + int surface_handle_ = gpu::kNullSurfaceHandle; + // TODO(https://crbug.com/836524): shouldn't have both of these closures + // in the same class like this. + base::OnceClosure on_context_bound_; + + int surface_width_ = 0; + int surface_height_ = 0; + + // If true, surface width/height is the intended size that should be applied + // to the surface once it's ready for use. + bool needs_resize_ = false; + + // A swap ID which is passed to GL swap. Incremented each call. + uint64_t swap_id_ = 0; + + // Uniform handle for the UV transform used by DrawQuad. + uint32_t uniform_uv_transform_handle_ = 0; + + // A task runner for the GL thread + scoped_refptr<base::SingleThreadTaskRunner> gl_thread_task_runner_; + + // Must be last. + base::WeakPtrFactory<MailboxToSurfaceBridgeImpl> weak_ptr_factory_{this}; + + DISALLOW_COPY_AND_ASSIGN(MailboxToSurfaceBridgeImpl); +}; + +class MailboxToSurfaceBridgeFactoryImpl + : public device::MailboxToSurfaceBridgeFactory { + public: + std::unique_ptr<device::MailboxToSurfaceBridge> Create() const override; +}; + +} // namespace webxr + +#endif // COMPONENTS_WEBXR_MAILBOX_TO_SURFACE_BRIDGE_H_
diff --git a/content/browser/DEPS b/content/browser/DEPS index ddf5db3..a0177bb 100644 --- a/content/browser/DEPS +++ b/content/browser/DEPS
@@ -50,7 +50,8 @@ "+device/fido/hid", "+device/gamepad", # For gamepad API "+device/nfc", - "+device/vr", # For WebVR API + "+device/vr/public", # For WebXR API + "+device/vr/buildflags/buildflags.h", # For WebXR API # This can only be used on POSIX, in particular it mustn't be used on Windows # in the browser DLL. "+gin/v8_initializer.h",
diff --git a/content/browser/back_forward_cache_browsertest.cc b/content/browser/back_forward_cache_browsertest.cc index 7569387..057747f 100644 --- a/content/browser/back_forward_cache_browsertest.cc +++ b/content/browser/back_forward_cache_browsertest.cc
@@ -2044,10 +2044,10 @@ IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest, DoesNotCacheIfAcquiredWakeLock) { - ASSERT_TRUE(embedded_test_server()->Start()); + ASSERT_TRUE(CreateHttpsServer()->Start()); // 1) Navigate to a page with WakeLock usage. - GURL url(embedded_test_server()->GetURL("/back_forward_cache/empty.html")); + GURL url(https_server()->GetURL("a.com", "/back_forward_cache/empty.html")); EXPECT_TRUE(NavigateToURL(shell(), url)); RenderFrameHostImpl* rfh_a = current_frame_host(); @@ -2055,18 +2055,18 @@ // Acquire WakeLock. EXPECT_EQ("DONE", EvalJs(rfh_a, R"( - new Promise(async resolve => { - try { - await navigator.wakeLock.request('screen'); - resolve('DONE'); - } catch (error) { - resolve('error: request failed'); - } - }); + new Promise(async resolve => { + try { + await navigator.wakeLock.request('screen'); + resolve('DONE'); + } catch (error) { + resolve('error: request failed'); + } + }); )")); // 2) Navigate away. - shell()->LoadURL(embedded_test_server()->GetURL("b.com", "/title1.html")); + shell()->LoadURL(https_server()->GetURL("b.com", "/title1.html")); // The page uses WakeLock so it should be deleted. deleted.WaitUntilDeleted(); @@ -2081,55 +2081,78 @@ blink::scheduler::WebSchedulerTrackedFeature::kWakeLock, FROM_HERE); } -// TODO(yuzus): By releasing wakelock, the page should become cacheable again. -// Fix and re-enable the rest of this test. -IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest, - DISABLED_CacheIfReleasedWakeLock) { - ASSERT_TRUE(embedded_test_server()->Start()); +IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest, CacheIfReleasedWakeLock) { + ASSERT_TRUE(CreateHttpsServer()->Start()); // 1) Navigate to a page with WakeLock usage. - GURL url(embedded_test_server()->GetURL( - "/back_forward_cache/page_with_wakelock.html")); + GURL url(https_server()->GetURL("a.com", "/back_forward_cache/empty.html")); EXPECT_TRUE(NavigateToURL(shell(), url)); - + EXPECT_TRUE(WaitForLoadStop(shell()->web_contents())); RenderFrameHostImpl* rfh_a = current_frame_host(); - RenderFrameDeletedObserver deleted(current_frame_host()); + // Acquire and release WakeLock. + EXPECT_EQ("DONE", EvalJs(rfh_a, R"( + new Promise(async resolve => { + try { + const lock = await navigator.wakeLock.request('screen'); + await lock.release(); + resolve('DONE'); + } catch (error) { + resolve('error: request failed'); + } + }); + )")); - // Acquire WakeLock. - EXPECT_EQ("DONE", EvalJs(rfh_a, "requestWakeLock()")); // 2) Navigate away. - shell()->LoadURL(embedded_test_server()->GetURL("b.com", "/title1.html")); - - // The page uses WakeLock so it should be deleted. - deleted.WaitUntilDeleted(); + shell()->LoadURL(https_server()->GetURL("b.com", "/title1.html")); + EXPECT_TRUE(WaitForLoadStop(shell()->web_contents())); + EXPECT_TRUE(rfh_a->IsInBackForwardCache()); // 3) Go back to the page with WakeLock. web_contents()->GetController().GoBack(); EXPECT_TRUE(WaitForLoadStop(shell()->web_contents())); + EXPECT_EQ(current_frame_host(), rfh_a); + + // This time the page is restored from cache because WakeLock is released. + ExpectOutcome(BackForwardCacheMetrics::HistoryNavigationOutcome::kRestored, + FROM_HERE); +} + +IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest, + DoesNotCacheIfAnyWakeLockHeld) { + ASSERT_TRUE(CreateHttpsServer()->Start()); + + // 1) Navigate to a page with WakeLock usage. + GURL url(https_server()->GetURL("/back_forward_cache/empty.html")); + EXPECT_TRUE(NavigateToURL(shell(), url)); + EXPECT_TRUE(WaitForLoadStop(shell()->web_contents())); + RenderFrameHostImpl* rfh_a = current_frame_host(); + // Acquire and release WakeLock. + EXPECT_EQ("DONE", EvalJs(rfh_a, R"( + new Promise(async resolve => { + try { + const lock1 = await navigator.wakeLock.request('screen'); + const lock2 = await navigator.wakeLock.request('screen'); + await lock1.release(); + resolve('DONE'); + } catch (error) { + resolve('error: request failed'); + } + }); + )")); + + // 2) Navigate away. + shell()->LoadURL(https_server()->GetURL("b.com", "/title1.html")); + EXPECT_TRUE(WaitForLoadStop(shell()->web_contents())); + + // 3) Go back to the page with WakeLock. + web_contents()->GetController().GoBack(); + EXPECT_TRUE(WaitForLoadStop(shell()->web_contents())); + ExpectNotRestored( {BackForwardCacheMetrics::NotRestoredReason::kBlocklistedFeatures}, FROM_HERE); ExpectBlocklistedFeature( blink::scheduler::WebSchedulerTrackedFeature::kWakeLock, FROM_HERE); - - // Release WakeLock. - EXPECT_EQ("DONE", EvalJs(current_frame_host(), "releaseWakeLock()")); - - // 4) Navigate away. - web_contents()->GetController().GoBack(); - EXPECT_TRUE(WaitForLoadStop(shell()->web_contents())); - - EXPECT_TRUE(rfh_a->IsInBackForwardCache()); - - // 5) Go back to the page with WakeLock. - web_contents()->GetController().GoBack(); - EXPECT_TRUE(WaitForLoadStop(shell()->web_contents())); - EXPECT_EQ(current_frame_host(), rfh_a); - EXPECT_TRUE(WaitForLoadStop(shell()->web_contents())); - - // This time the page is restored from cache because WakeLock is released. - ExpectOutcome(BackForwardCacheMetrics::HistoryNavigationOutcome::kRestored, - FROM_HERE); } IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
diff --git a/content/browser/loader/navigation_url_loader_impl.cc b/content/browser/loader/navigation_url_loader_impl.cc index 3a0f7d0..b2d3918 100644 --- a/content/browser/loader/navigation_url_loader_impl.cc +++ b/content/browser/loader/navigation_url_loader_impl.cc
@@ -206,16 +206,8 @@ new_request->trusted_params->client_security_state = request_info->client_security_state.Clone(); new_request->is_main_frame = request_info->is_main_frame; - - net::RequestPriority net_priority = net::HIGHEST; - if (!request_info->is_main_frame && - base::FeatureList::IsEnabled(features::kLowPriorityIframes)) { - net_priority = net::LOWEST; - } - new_request->priority = net_priority; - + new_request->priority = net::HIGHEST; new_request->render_frame_id = frame_tree_node_id; - new_request->request_initiator = request_info->common_params->initiator_origin; new_request->referrer = request_info->common_params->referrer->url;
diff --git a/content/browser/loader/navigation_url_loader_impl_unittest.cc b/content/browser/loader/navigation_url_loader_impl_unittest.cc index 0393112..0af4d37 100644 --- a/content/browser/loader/navigation_url_loader_impl_unittest.cc +++ b/content/browser/loader/navigation_url_loader_impl_unittest.cc
@@ -280,25 +280,6 @@ } } - net::RequestPriority NavigateAndReturnRequestPriority(const GURL& url, - bool is_main_frame) { - TestNavigationURLLoaderDelegate delegate; - base::test::ScopedFeatureList scoped_feature_list_; - - scoped_feature_list_.InitAndEnableFeature(features::kLowPriorityIframes); - - std::unique_ptr<NavigationURLLoader> loader = CreateTestLoader( - url, - base::StringPrintf("%s: %s", net::HttpRequestHeaders::kOrigin, - url.GetOrigin().spec().c_str()), - "GET", &delegate, NavigationDownloadPolicy(), is_main_frame); - delegate.WaitForRequestRedirected(); - loader->FollowRedirect({}, {}, {}, blink::PreviewsTypes::PREVIEWS_OFF); - delegate.WaitForResponseStarted(); - - return most_recent_resource_request_.value().priority; - } - net::RedirectInfo NavigateAndReturnRedirectInfo(const GURL& url, bool upgrade_if_insecure, bool expect_request_fail) { @@ -328,16 +309,6 @@ base::Optional<network::ResourceRequest> most_recent_resource_request_; }; -TEST_F(NavigationURLLoaderImplTest, RequestPriority) { - ASSERT_TRUE(http_test_server_.Start()); - const GURL url = http_test_server_.GetURL("/redirect301-to-echo"); - - EXPECT_EQ(net::HIGHEST, - NavigateAndReturnRequestPriority(url, true /* is_main_frame */)); - EXPECT_EQ(net::LOWEST, - NavigateAndReturnRequestPriority(url, false /* is_main_frame */)); -} - TEST_F(NavigationURLLoaderImplTest, IsolationInfoOfMainFrameNavigation) { ASSERT_TRUE(http_test_server_.Start());
diff --git a/content/browser/loader/quic_transport_browsertest.cc b/content/browser/loader/quic_transport_browsertest.cc index a99a891..dc494a2 100644 --- a/content/browser/loader/quic_transport_browsertest.cc +++ b/content/browser/loader/quic_transport_browsertest.cc
@@ -276,8 +276,7 @@ ASSERT_TRUE(WaitForTitle(ASCIIToUTF16("PASS"), {ASCIIToUTF16("FAIL")})); } -// Flaky on many platforms (see crbug/1064434). -IN_PROC_BROWSER_TEST_F(QuicTransportBrowserTest, DISABLED_ReceiveStream) { +IN_PROC_BROWSER_TEST_F(QuicTransportBrowserTest, ReceiveStream) { ASSERT_TRUE(embedded_test_server()->Start()); ASSERT_TRUE( NavigateToURL(shell(), embedded_test_server()->GetURL("/title2.html")));
diff --git a/content/browser/renderer_host/agent_scheduling_group_host.cc b/content/browser/renderer_host/agent_scheduling_group_host.cc index e773a359..da6b9f8 100644 --- a/content/browser/renderer_host/agent_scheduling_group_host.cc +++ b/content/browser/renderer_host/agent_scheduling_group_host.cc
@@ -240,12 +240,14 @@ } void AgentSchedulingGroupHost::CreateFrame(mojom::CreateFrameParamsPtr params) { - SetUpMojoIfNeeded(); + DCHECK(process_.IsInitializedAndNotDead()); + DCHECK(mojo_remote_.is_bound()); mojo_remote_.get()->CreateFrame(std::move(params)); } void AgentSchedulingGroupHost::CreateView(mojom::CreateViewParamsPtr params) { - SetUpMojoIfNeeded(); + DCHECK(process_.IsInitializedAndNotDead()); + DCHECK(mojo_remote_.is_bound()); mojo_remote_.get()->CreateView(std::move(params)); }
diff --git a/content/browser/renderer_host/clipboard_host_impl.cc b/content/browser/renderer_host/clipboard_host_impl.cc index 29887892..ec20fc6 100644 --- a/content/browser/renderer_host/clipboard_host_impl.cc +++ b/content/browser/renderer_host/clipboard_host_impl.cc
@@ -88,13 +88,16 @@ clipboard_(ui::Clipboard::GetForCurrentThread()) { // |render_frame_host| may be null in unit tests. if (render_frame_host) { - render_frame_routing_id_ = render_frame_host->GetRoutingID(); - render_process_id_ = render_frame_host->GetProcess()->GetID(); + render_frame_routing_id_ = + GlobalFrameRoutingId(render_frame_host->GetProcess()->GetID(), + render_frame_host->GetRoutingID()); clipboard_writer_ = std::make_unique<ui::ScopedClipboardWriter>( ui::ClipboardBuffer::kCopyPaste, std::make_unique<ui::ClipboardDataEndpoint>( render_frame_host->GetLastCommittedOrigin())); } else { + render_frame_routing_id_ = GlobalFrameRoutingId( + ChildProcessHost::kInvalidUniqueID, MSG_ROUTING_NONE); clipboard_writer_ = std::make_unique<ui::ScopedClipboardWriter>( ui::ClipboardBuffer::kCopyPaste); } @@ -384,7 +387,7 @@ std::string data) { // May not have a RenderFrameHost in tests. RenderFrameHostImpl* render_frame_host = - RenderFrameHostImpl::FromID(render_process_id_, render_frame_routing_id_); + RenderFrameHostImpl::FromID(render_frame_routing_id_); if (render_frame_host) { render_frame_host->IsClipboardPasteAllowed( data_type, data, @@ -416,7 +419,7 @@ std::unique_ptr<ui::ClipboardDataEndpoint> ClipboardHostImpl::CreateDataEndpoint() { RenderFrameHostImpl* render_frame_host = - RenderFrameHostImpl::FromID(render_process_id_, render_frame_routing_id_); + RenderFrameHostImpl::FromID(render_frame_routing_id_); if (render_frame_host) { return std::make_unique<ui::ClipboardDataEndpoint>( render_frame_host->GetLastCommittedOrigin());
diff --git a/content/browser/renderer_host/clipboard_host_impl.h b/content/browser/renderer_host/clipboard_host_impl.h index 8ef374e..df05d9a5 100644 --- a/content/browser/renderer_host/clipboard_host_impl.h +++ b/content/browser/renderer_host/clipboard_host_impl.h
@@ -182,8 +182,7 @@ mojo::Receiver<blink::mojom::ClipboardHost> receiver_; ui::Clipboard* const clipboard_; // Not owned - int render_frame_routing_id_ = MSG_ROUTING_NONE; - int render_process_id_ = ChildProcessHost::kInvalidUniqueID; + GlobalFrameRoutingId render_frame_routing_id_; std::unique_ptr<ui::ScopedClipboardWriter> clipboard_writer_; // Outstanding is allowed requests per clipboard contents. Maps a clipboard
diff --git a/content/browser/service_worker/service_worker_storage.cc b/content/browser/service_worker/service_worker_storage.cc index 3526c3bb..2750b392 100644 --- a/content/browser/service_worker/service_worker_storage.cc +++ b/content/browser/service_worker/service_worker_storage.cc
@@ -1064,12 +1064,6 @@ disk_cache_->Disable(); } -void ServiceWorkerStorage::PurgeResources(const ResourceList& resources) { - if (!has_checked_for_stale_resources_) - DeleteStaleResources(); - StartPurgingResources(resources); -} - void ServiceWorkerStorage::PurgeResources( const std::vector<int64_t>& resource_ids) { if (!has_checked_for_stale_resources_)
diff --git a/content/browser/service_worker/service_worker_storage.h b/content/browser/service_worker/service_worker_storage.h index 5d2093bb..b109a02b 100644 --- a/content/browser/service_worker/service_worker_storage.h +++ b/content/browser/service_worker/service_worker_storage.h
@@ -278,12 +278,11 @@ void Disable(); - // Schedules deleting |resources| from the disk cache and removing their keys - // as purgeable resources from the service worker database. It's OK to call - // this for resources that don't have purgeable resource keys, like + // Schedules deleting `resource_ids` from the disk cache and removing their + // keys as purgeable resources from the service worker database. It's OK to + // call this for resources that don't have purgeable resource keys, like // uncommitted resources, as long as the caller does its own cleanup to remove // the uncommitted resource keys. - void PurgeResources(const ResourceList& resources); void PurgeResources(const std::vector<int64_t>& resource_ids); // Applies |policy_updates|.
diff --git a/content/browser/site_per_process_browsertest.cc b/content/browser/site_per_process_browsertest.cc index cecc4ef..7888dca 100644 --- a/content/browser/site_per_process_browsertest.cc +++ b/content/browser/site_per_process_browsertest.cc
@@ -13722,34 +13722,24 @@ features::kReloadHiddenTabsWithCrashedSubframes); } - private: - base::test::ScopedFeatureList feature_list_; -}; - -// Verify the feature where hidden tabs with crashed subframes are marked for -// reload. This avoids showing crashed subframes if a hidden tab is eventually -// shown. See https://crbug.com/841572. -// crbug.com/1010119, fails on Win. crbug.com/1015971, fails on Linux. -// crbug.com/1049885, fails on Android and Mac. -#if defined(OS_WIN) || defined(OS_LINUX) || defined(OS_CHROMEOS) || \ - defined(OS_ANDROID) || defined(OS_MACOSX) -#define MAYBE_ReloadHiddenTabWithCrashedSubframe \ - DISABLED_ReloadHiddenTabWithCrashedSubframe -#else -#define MAYBE_ReloadHiddenTabWithCrashedSubframe \ - ReloadHiddenTabWithCrashedSubframe -#endif -IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTestWithSadFrameTabReload, - MAYBE_ReloadHiddenTabWithCrashedSubframe) { - auto crash_process = [](FrameTreeNode* ftn) { + void CrashProcess(FrameTreeNode* ftn) { RenderProcessHost* process = ftn->current_frame_host()->GetProcess(); RenderProcessHostWatcher crash_observer( process, RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT); process->Shutdown(0); crash_observer.Wait(); EXPECT_FALSE(ftn->current_frame_host()->IsRenderFrameLive()); - }; + } + private: + base::test::ScopedFeatureList feature_list_; +}; + +// Verify the feature where hidden tabs with a visible crashed subframe are +// marked for reload. This avoids showing crashed subframes if a hidden tab is +// eventually shown. See https://crbug.com/841572. +IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTestWithSadFrameTabReload, + ReloadHiddenTabWithCrashedSubframeInViewport) { GURL main_url(embedded_test_server()->GetURL( "a.com", "/cross_site_iframe_factory.html?a(b)")); EXPECT_TRUE(NavigateToURL(shell(), main_url)); @@ -13764,16 +13754,18 @@ // Kill the b.com subframe's process. This should mark the hidden // WebContents for reload. { - SCOPED_TRACE("In-viewport sad frame on a hidden tab"); base::HistogramTester histograms; - crash_process(root->child_at(0)); + CrashProcess(root->child_at(0)); histograms.ExpectUniqueSample( "Stability.ChildFrameCrash.TabMarkedForReload", true, 1); histograms.ExpectUniqueSample( "Stability.ChildFrameCrash.TabMarkedForReload.Visibility", blink::mojom::FrameVisibility::kRenderedInViewport, 1); + } - // Show the WebContents. This should trigger a reload of the main frame. + // Show the WebContents. This should trigger a reload of the main frame. + { + base::HistogramTester histograms; web_contents()->UpdateWebContentsVisibility(Visibility::VISIBLE); EXPECT_TRUE(WaitForLoadStop(web_contents())); histograms.ExpectUniqueSample( @@ -13784,76 +13776,124 @@ // Both frames should now have live renderer processes. EXPECT_TRUE(root->current_frame_host()->IsRenderFrameLive()); EXPECT_TRUE(root->child_at(0)->current_frame_host()->IsRenderFrameLive()); +} - // Next, try the same with a crashed subframe that's scrolled out of view. - // This should also trigger a reload. +// Verify the feature where hidden tabs with crashed subframes are marked for +// reload. This avoids showing crashed subframes if a hidden tab is eventually +// shown. Similar to the test above, except that the crashed subframe is +// scrolled out of view. +IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTestWithSadFrameTabReload, + ReloadHiddenTabWithCrashedSubframeOutOfView) { + // Set WebContents to VISIBLE to avoid hitting the |!did_first_set_visible_| + // case when we hide it later. + web_contents()->UpdateWebContentsVisibility(Visibility::VISIBLE); + + // Navigate to a page with an OOPIF that's scrolled out of view. GURL out_of_view_url( embedded_test_server()->GetURL("a.com", "/iframe_out_of_view.html")); EXPECT_TRUE(NavigateToURL(shell(), out_of_view_url)); EXPECT_EQ("LOADED", EvalJsWithManualReply(shell(), "notifyWhenLoaded();")); NavigateIframeToURL(web_contents(), "test_iframe", embedded_test_server()->GetURL("b.com", "/title1.html")); - web_contents()->UpdateWebContentsVisibility(Visibility::HIDDEN); + + // Verify the OOPIF isn't visible at the moment. + FrameTreeNode* root = web_contents()->GetFrameTree()->root(); + RenderFrameProxyHost* proxy_to_parent = + root->child_at(0)->render_manager()->GetProxyToParent(); + CrossProcessFrameConnector* connector = + proxy_to_parent->cross_process_frame_connector(); + EXPECT_FALSE(connector->IsVisible()); + EXPECT_EQ(blink::mojom::FrameVisibility::kRenderedOutOfViewport, + connector->visibility()); + + // Hide the WebContents and crash the OOPIF. { - SCOPED_TRACE("Out-of-viewport sad frame on a hidden tab"); base::HistogramTester histograms; - crash_process(root->child_at(0)); + web_contents()->UpdateWebContentsVisibility(Visibility::HIDDEN); + CrashProcess(root->child_at(0)); histograms.ExpectUniqueSample( "Stability.ChildFrameCrash.TabMarkedForReload", true, 1); histograms.ExpectUniqueSample( "Stability.ChildFrameCrash.TabMarkedForReload.Visibility", blink::mojom::FrameVisibility::kRenderedOutOfViewport, 1); + } + EXPECT_TRUE(root->current_frame_host()->IsRenderFrameLive()); + EXPECT_FALSE(root->child_at(0)->current_frame_host()->IsRenderFrameLive()); + + // Show the tab and ensure that it reloads. + { + base::HistogramTester histograms; web_contents()->UpdateWebContentsVisibility(Visibility::VISIBLE); EXPECT_TRUE(WaitForLoadStop(web_contents())); histograms.ExpectUniqueSample( "Navigation.LoadIfNecessaryType", NavigationControllerImpl::NeedsReloadType::kCrashedSubframe, 1); } + EXPECT_TRUE(root->current_frame_host()->IsRenderFrameLive()); EXPECT_TRUE(root->child_at(0)->current_frame_host()->IsRenderFrameLive()); +} - // Now, load a page with where an iframe is hidden with "display:none". - // Ensure that we do not mark the tab for reload in that case. +// Verify that hidden tabs with a crashed subframe are not marked for reload +// when the crashed subframe is hidden with "display:none". +IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTestWithSadFrameTabReload, + DoNotReloadHiddenTabWithHiddenCrashedSubframe) { + // Set WebContents to VISIBLE to avoid hitting the |!did_first_set_visible_| + // case when we hide it later. + web_contents()->UpdateWebContentsVisibility(Visibility::VISIBLE); + GURL hidden_iframe_url( embedded_test_server()->GetURL("a.com", "/page_with_hidden_iframe.html")); EXPECT_TRUE(NavigateToURL(shell(), hidden_iframe_url)); NavigateIframeToURL(web_contents(), "test_iframe", embedded_test_server()->GetURL("b.com", "/title1.html")); + FrameTreeNode* root = web_contents()->GetFrameTree()->root(); RenderFrameProxyHost* proxy_to_parent = root->child_at(0)->render_manager()->GetProxyToParent(); EXPECT_TRUE(proxy_to_parent->cross_process_frame_connector()->IsHidden()); + // Crashing a hidden OOPIF shouldn't mark the tab for reload. web_contents()->UpdateWebContentsVisibility(Visibility::HIDDEN); - { - SCOPED_TRACE("display:none sad frame on a hidden tab"); - base::HistogramTester histograms; - crash_process(root->child_at(0)); - histograms.ExpectUniqueSample( - "Stability.ChildFrameCrash.TabMarkedForReload", false, 1); - } - web_contents()->UpdateWebContentsVisibility(Visibility::VISIBLE); - EXPECT_TRUE(WaitForLoadStop(web_contents())); - EXPECT_TRUE(root->current_frame_host()->IsRenderFrameLive()); - EXPECT_FALSE(root->child_at(0)->current_frame_host()->IsRenderFrameLive()); + base::HistogramTester histograms; + CrashProcess(root->child_at(0)); + histograms.ExpectUniqueSample("Stability.ChildFrameCrash.TabMarkedForReload", + false, 1); - // Finally, ensure that the reload policy doesn't trigger for a visible tab, - // even if it becomes hidden and then visible again. + // Making the WebContents visible again should keep the sad frame and should + // not load anything new. + web_contents()->UpdateWebContentsVisibility(Visibility::VISIBLE); + EXPECT_TRUE(WaitForLoadStop(web_contents())); + EXPECT_TRUE(root->current_frame_host()->IsRenderFrameLive()); + EXPECT_FALSE(root->child_at(0)->current_frame_host()->IsRenderFrameLive()); +} + +// Ensure that the sad frame reload policy doesn't trigger for a visible tab, +// even if it becomes hidden and then visible again. +IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTestWithSadFrameTabReload, + DoNotReloadVisibleTabWithCrashedSubframe) { + // Set WebContents to VISIBLE to avoid hitting the |!did_first_set_visible_| + // case when we hide it later. + web_contents()->UpdateWebContentsVisibility(Visibility::VISIBLE); EXPECT_EQ(Visibility::VISIBLE, web_contents()->GetVisibility()); + + GURL main_url(embedded_test_server()->GetURL( + "a.com", "/cross_site_iframe_factory.html?a(b)")); EXPECT_TRUE(NavigateToURL(shell(), main_url)); - { - SCOPED_TRACE("Visible sad frame on a visible tab"); - base::HistogramTester histograms; - crash_process(root->child_at(0)); - histograms.ExpectUniqueSample( - "Stability.ChildFrameCrash.TabMarkedForReload", false, 1); - } + FrameTreeNode* root = web_contents()->GetFrameTree()->root(); + base::HistogramTester histograms; + CrashProcess(root->child_at(0)); + histograms.ExpectUniqueSample("Stability.ChildFrameCrash.TabMarkedForReload", + false, 1); + EXPECT_EQ(Visibility::VISIBLE, web_contents()->GetVisibility()); web_contents()->UpdateWebContentsVisibility(Visibility::HIDDEN); web_contents()->UpdateWebContentsVisibility(Visibility::VISIBLE); EXPECT_TRUE(WaitForLoadStop(web_contents())); EXPECT_TRUE(root->current_frame_host()->IsRenderFrameLive()); EXPECT_FALSE(root->child_at(0)->current_frame_host()->IsRenderFrameLive()); + histograms.ExpectUniqueSample("Stability.ChildFrameCrash.TabMarkedForReload", + false, 1); } // Check that when a frame changes a subframe's size twice and then sends a
diff --git a/content/browser/sms/sms_browsertest.cc b/content/browser/sms/sms_browsertest.cc index 94f9689..3ae6e8f4 100644 --- a/content/browser/sms/sms_browsertest.cc +++ b/content/browser/sms/sms_browsertest.cc
@@ -771,7 +771,8 @@ EXPECT_TRUE(render_frame_host->DocumentUsedWebOTP()); } -IN_PROC_BROWSER_TEST_F(SmsBrowserTest, RecordPendingOriginCount) { +// Disabled test: https://crbug.com/1134455 +IN_PROC_BROWSER_TEST_F(SmsBrowserTest, DISABLED_RecordPendingOriginCount) { base::HistogramTester histogram_tester; auto provider = std::make_unique<MockSmsProvider>(); MockSmsProvider* mock_provider_ptr = provider.get();
diff --git a/content/browser/xr/DEPS b/content/browser/xr/DEPS index 974b375c..ef170b7f44c 100644 --- a/content/browser/xr/DEPS +++ b/content/browser/xr/DEPS
@@ -1,4 +1,5 @@ include_rules = [ "+device/base", - "+device/vr", + "+device/vr/orientation", + "+device/vr/test", ]
diff --git a/content/browser/xr/service/vr_service_impl.cc b/content/browser/xr/service/vr_service_impl.cc index c985059e..cee6ad2 100644 --- a/content/browser/xr/service/vr_service_impl.cc +++ b/content/browser/xr/service/vr_service_impl.cc
@@ -504,6 +504,9 @@ "id", request.runtime_id); auto runtime_options = GetRuntimeOptions(request.options.get()); + // Make the resolved enabled features available to the runtime. + runtime_options->enabled_features.assign(request.enabled_features.begin(), + request.enabled_features.end()); #if defined(OS_ANDROID) && BUILDFLAG(ENABLE_ARCORE) if (request.runtime_id == device::mojom::XRDeviceId::ARCORE_DEVICE_ID) { @@ -512,10 +515,15 @@ runtime_options->render_frame_id = render_frame_host_->GetRoutingID(); } #endif - // Make the resolved enabled features available to the runtime. - runtime_options->enabled_features.reserve(request.enabled_features.size()); - for (const auto& feature : request.enabled_features) { - runtime_options->enabled_features.push_back(feature); + + bool use_dom_overlay = + base::Contains(runtime_options->enabled_features, + device::mojom::XRSessionFeature::DOM_OVERLAY); + + if (use_dom_overlay) { + // Tell RenderFrameHostImpl that we're setting up the WebXR DOM Overlay, + // it checks for this in EnterFullscreen via HasSeenRecentXrOverlaySetup(). + render_frame_host_->SetIsXrOverlaySetup(); } if (device::XRSessionModeUtils::IsImmersive(runtime_options->mode)) {
diff --git a/content/public/common/content_features.cc b/content/public/common/content_features.cc index 450c516..ce6dd47f 100644 --- a/content/public/common/content_features.cc +++ b/content/public/common/content_features.cc
@@ -356,10 +356,6 @@ #endif }; -// Enables lowering the priority of the resources in iframes. -const base::Feature kLowPriorityIframes{"LowPriorityIframes", - base::FEATURE_DISABLED_BY_DEFAULT}; - // Removes the association between the `AgentSchedulingGroup` interfaces and the // IPC Channel. This will break ordering guarantees between different agent // scheduling groups (ordering withing a group is still preserved).
diff --git a/content/public/common/content_features.h b/content/public/common/content_features.h index 579f33f..8a6d4cb 100644 --- a/content/public/common/content_features.h +++ b/content/public/common/content_features.h
@@ -82,7 +82,6 @@ CONTENT_EXPORT extern const base::Feature kLazyInitializeMediaControls; CONTENT_EXPORT extern const base::Feature kLegacyWindowsDWriteFontFallback; CONTENT_EXPORT extern const base::Feature kLogJsConsoleMessages; -CONTENT_EXPORT extern const base::Feature kLowPriorityIframes; CONTENT_EXPORT extern const base::Feature kMbiDetachAgentSchedulingGroupFromChannel; CONTENT_EXPORT extern const base::Feature kMediaDevicesSystemMonitorCache;
diff --git a/content/renderer/child_frame_compositing_helper.cc b/content/renderer/child_frame_compositing_helper.cc index 9c016e3..43097a2 100644 --- a/content/renderer/child_frame_compositing_helper.cc +++ b/content/renderer/child_frame_compositing_helper.cc
@@ -90,8 +90,7 @@ } scoped_refptr<cc::DisplayItemList> -ChildFrameCompositingHelper::PaintContentsToDisplayList( - PaintingControlSetting) { +ChildFrameCompositingHelper::PaintContentsToDisplayList() { DCHECK(crash_ui_layer_); auto layer_size = crash_ui_layer_->bounds(); auto display_list = base::MakeRefCounted<cc::DisplayItemList>();
diff --git a/content/renderer/child_frame_compositing_helper.h b/content/renderer/child_frame_compositing_helper.h index 2615b9b..6ff2d6a 100644 --- a/content/renderer/child_frame_compositing_helper.h +++ b/content/renderer/child_frame_compositing_helper.h
@@ -53,8 +53,7 @@ // cc::ContentLayerClient implementation. Called from the cc::PictureLayer // created for the crashed child frame to display the sad image. gfx::Rect PaintableRegion() override; - scoped_refptr<cc::DisplayItemList> PaintContentsToDisplayList( - PaintingControlSetting) override; + scoped_refptr<cc::DisplayItemList> PaintContentsToDisplayList() override; bool FillsBoundsCompletely() const override; size_t GetApproximateUnsharedMemoryUsage() const override;
diff --git a/content/test/data/back_forward_cache/page_with_wakelock.html b/content/test/data/back_forward_cache/page_with_wakelock.html deleted file mode 100644 index 1586440668..0000000 --- a/content/test/data/back_forward_cache/page_with_wakelock.html +++ /dev/null
@@ -1,21 +0,0 @@ -<html> - <title>wakelock</title> -</html> -<script> -let lock; - -async function requestWakeLock() { - try { - lock = await navigator.wakeLock.request('screen'); - return 'DONE'; - } catch (error) { - return `${error}`; - } -} - -async function releaseWakeLock() { - await lock.release(); - return 'DONE'; -} - -</script>
diff --git a/device/vr/android/BUILD.gn b/device/vr/android/BUILD.gn new file mode 100644 index 0000000..37f5894 --- /dev/null +++ b/device/vr/android/BUILD.gn
@@ -0,0 +1,17 @@ +# Copyright 2020 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. + +static_library("vr_android") { + defines = [] + sources = [ + "mailbox_to_surface_bridge.h", + "web_xr_presentation_state.cc", + "web_xr_presentation_state.h", + ] + + deps = [ + "//gpu/ipc/common:common", + "//ui/gl:gl", + ] +}
diff --git a/device/vr/android/DEPS b/device/vr/android/DEPS new file mode 100644 index 0000000..fefc58b --- /dev/null +++ b/device/vr/android/DEPS
@@ -0,0 +1,5 @@ +include_rules = [ + "+gpu/ipc/common", + "+gpu/command_buffer/common/shared_image_usage.h", + "+ui/gl", +]
diff --git a/device/vr/android/arcore/BUILD.gn b/device/vr/android/arcore/BUILD.gn index 85f76776..e733c1c 100644 --- a/device/vr/android/arcore/BUILD.gn +++ b/device/vr/android/arcore/BUILD.gn
@@ -11,12 +11,22 @@ defines = [ "IS_VR_ARCORE_IMPL" ] sources = [ "address_to_id_map.h", + "ar_image_transport.cc", + "ar_image_transport.h", + "ar_renderer.cc", + "ar_renderer.h", "arcore.cc", "arcore.h", "arcore_anchor_manager.cc", "arcore_anchor_manager.h", + "arcore_device.cc", + "arcore_device.h", "arcore_device_provider_factory.cc", "arcore_device_provider_factory.h", + "arcore_gl.cc", + "arcore_gl.h", + "arcore_gl_thread.cc", + "arcore_gl_thread.h", "arcore_impl.cc", "arcore_impl.h", "arcore_math_utils.cc", @@ -24,6 +34,7 @@ "arcore_plane_manager.cc", "arcore_plane_manager.h", "arcore_sdk.h", + "arcore_session_utils.h", "arcore_shim.cc", "arcore_shim.h", "scoped_arcore_objects.h", @@ -35,9 +46,12 @@ deps = [ "//base", + "//device/vr:vr", "//device/vr:vr_base", + "//device/vr/android:vr_android", "//mojo/public/cpp/bindings", "//ui/gfx", + "//ui/gl/init", ] configs += [ "//third_party/arcore-android-sdk:libarcore_config" ]
diff --git a/device/vr/android/arcore/DEPS b/device/vr/android/arcore/DEPS index efd700c..5e9ba5929 100644 --- a/device/vr/android/arcore/DEPS +++ b/device/vr/android/arcore/DEPS
@@ -1 +1,3 @@ -include_rules = [ "+third_party/arcore-android-sdk/src", ] \ No newline at end of file +include_rules = [ + "+third_party/arcore-android-sdk/src", + ] \ No newline at end of file
diff --git a/chrome/browser/android/vr/arcore_device/ar_image_transport.cc b/device/vr/android/arcore/ar_image_transport.cc similarity index 96% rename from chrome/browser/android/vr/arcore_device/ar_image_transport.cc rename to device/vr/android/arcore/ar_image_transport.cc index 3fe8fc2..c2ef9322 100644 --- a/chrome/browser/android/vr/arcore_device/ar_image_transport.cc +++ b/device/vr/android/arcore/ar_image_transport.cc
@@ -2,15 +2,15 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "chrome/browser/android/vr/arcore_device/ar_image_transport.h" +#include "device/vr/android/arcore/ar_image_transport.h" #include "base/android/android_hardware_buffer_compat.h" #include "base/android/scoped_hardware_buffer_handle.h" #include "base/containers/queue.h" #include "base/trace_event/trace_event.h" #include "base/trace_event/traced_value.h" -#include "chrome/browser/android/vr/mailbox_to_surface_bridge.h" -#include "chrome/browser/android/vr/web_xr_presentation_state.h" +#include "device/vr/android/mailbox_to_surface_bridge.h" +#include "device/vr/android/web_xr_presentation_state.h" #include "gpu/command_buffer/common/shared_image_usage.h" #include "gpu/ipc/common/gpu_memory_buffer_impl_android_hardware_buffer.h" #include "ui/gfx/gpu_fence.h" @@ -26,7 +26,7 @@ namespace device { ArImageTransport::ArImageTransport( - std::unique_ptr<vr::MailboxToSurfaceBridge> mailbox_bridge) + std::unique_ptr<MailboxToSurfaceBridge> mailbox_bridge) : gl_thread_task_runner_(base::ThreadTaskRunnerHandle::Get()), mailbox_bridge_(std::move(mailbox_bridge)) { DVLOG(2) << __func__; @@ -378,7 +378,8 @@ void ArImageTransport::CopyMailboxToSurfaceAndSwap( const gfx::Size& frame_size, - const gpu::MailboxHolder& mailbox) { + const gpu::MailboxHolder& mailbox, + const gfx::Transform& uv_transform) { DVLOG(2) << __func__; if (frame_size != surface_size_) { DVLOG(2) << __func__ << " resize from " << surface_size_.ToString() @@ -392,7 +393,8 @@ // Draw the image to the surface in the GPU process's command buffer context. // This will trigger an OnFrameAvailable event once the corresponding // SurfaceTexture in the local GL context is ready for updating. - bool swapped = mailbox_bridge_->CopyMailboxToSurfaceAndSwap(mailbox); + bool swapped = + mailbox_bridge_->CopyMailboxToSurfaceAndSwap(mailbox, uv_transform); DCHECK(swapped); } @@ -401,7 +403,7 @@ } std::unique_ptr<ArImageTransport> ArImageTransportFactory::Create( - std::unique_ptr<vr::MailboxToSurfaceBridge> mailbox_bridge) { + std::unique_ptr<MailboxToSurfaceBridge> mailbox_bridge) { return std::make_unique<ArImageTransport>(std::move(mailbox_bridge)); }
diff --git a/chrome/browser/android/vr/arcore_device/ar_image_transport.h b/device/vr/android/arcore/ar_image_transport.h similarity index 89% rename from chrome/browser/android/vr/arcore_device/ar_image_transport.h rename to device/vr/android/arcore/ar_image_transport.h index 0daa910..a7fca4d 100644 --- a/chrome/browser/android/vr/arcore_device/ar_image_transport.h +++ b/device/vr/android/arcore/ar_image_transport.h
@@ -2,14 +2,14 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef CHROME_BROWSER_ANDROID_VR_ARCORE_DEVICE_AR_IMAGE_TRANSPORT_H_ -#define CHROME_BROWSER_ANDROID_VR_ARCORE_DEVICE_AR_IMAGE_TRANSPORT_H_ +#ifndef DEVICE_VR_ANDROID_ARCORE_AR_IMAGE_TRANSPORT_H_ +#define DEVICE_VR_ANDROID_ARCORE_AR_IMAGE_TRANSPORT_H_ #include "base/macros.h" #include "base/memory/ref_counted.h" #include "base/memory/weak_ptr.h" #include "base/single_thread_task_runner.h" -#include "chrome/browser/android/vr/arcore_device/ar_renderer.h" +#include "device/vr/android/arcore/ar_renderer.h" #include "device/vr/public/mojom/vr_service.mojom.h" #include "ui/gfx/geometry/size_f.h" @@ -27,22 +27,23 @@ } // namespace gpu namespace vr { -class MailboxToSurfaceBridge; class WebXrPresentationState; struct WebXrSharedBuffer; } // namespace vr namespace device { +class MailboxToSurfaceBridge; + using XrFrameCallback = base::RepeatingCallback<void(const gfx::Transform&)>; // This class handles transporting WebGL rendered output from the GPU process's // command buffer GL context to the local GL context, and compositing WebGL // output onto the camera image using the local GL context. -class ArImageTransport { +class COMPONENT_EXPORT(VR_ARCORE) ArImageTransport { public: explicit ArImageTransport( - std::unique_ptr<vr::MailboxToSurfaceBridge> mailbox_bridge); + std::unique_ptr<MailboxToSurfaceBridge> mailbox_bridge); virtual ~ArImageTransport(); virtual void DestroySharedBuffers(vr::WebXrPresentationState* webxr); @@ -85,7 +86,8 @@ const gfx::Transform& uv_transform); virtual void WaitSyncToken(const gpu::SyncToken& sync_token); virtual void CopyMailboxToSurfaceAndSwap(const gfx::Size& frame_size, - const gpu::MailboxHolder& mailbox); + const gpu::MailboxHolder& mailbox, + const gfx::Transform& uv_transform); bool UseSharedBuffer() { return shared_buffer_draw_; } void SetFrameAvailableCallback(XrFrameCallback on_frame_available); @@ -109,7 +111,7 @@ scoped_refptr<base::SingleThreadTaskRunner> gl_thread_task_runner_; - std::unique_ptr<vr::MailboxToSurfaceBridge> mailbox_bridge_; + std::unique_ptr<MailboxToSurfaceBridge> mailbox_bridge_; // If true, use shared buffer transport aka DRAW_INTO_TEXTURE_MAILBOX. // If false, use Surface transport aka SUBMIT_AS_MAILBOX_HOLDER. @@ -134,13 +136,13 @@ DISALLOW_COPY_AND_ASSIGN(ArImageTransport); }; -class ArImageTransportFactory { +class COMPONENT_EXPORT(VR_ARCORE) ArImageTransportFactory { public: virtual ~ArImageTransportFactory() = default; virtual std::unique_ptr<ArImageTransport> Create( - std::unique_ptr<vr::MailboxToSurfaceBridge> mailbox_bridge); + std::unique_ptr<MailboxToSurfaceBridge> mailbox_bridge); }; } // namespace device -#endif // CHROME_BROWSER_ANDROID_VR_ARCORE_DEVICE_AR_IMAGE_TRANSPORT_H_ +#endif // DEVICE_VR_ANDROID_ARCORE_AR_IMAGE_TRANSPORT_H_
diff --git a/chrome/browser/android/vr/arcore_device/ar_renderer.cc b/device/vr/android/arcore/ar_renderer.cc similarity index 87% rename from chrome/browser/android/vr/arcore_device/ar_renderer.cc rename to device/vr/android/arcore/ar_renderer.cc index 68ab235..51f721a 100644 --- a/chrome/browser/android/vr/arcore_device/ar_renderer.cc +++ b/device/vr/android/arcore/ar_renderer.cc
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "chrome/browser/android/vr/arcore_device/ar_renderer.h" +#include "device/vr/android/arcore/ar_renderer.h" #include "base/stl_util.h" #include "device/vr/vr_gl_util.h" @@ -100,12 +100,15 @@ glVertexAttribPointer(position_handle_, 2, GL_FLOAT, false, 0, 0); glEnableVertexAttribArray(position_handle_); - // Bind texture. This is a 1:1 pixel copy since the source surface - // and renderbuffer destination size are resized to match, so use - // GL_NEAREST. + // Bind texture. This is not necessarily a 1:1 pixel copy since the + // size is modified by framebufferScaleFactor and requestViewportScale, + // so use GL_LINEAR. glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_EXTERNAL_OES, texture_handle); - vr::SetTexParameters(GL_TEXTURE_EXTERNAL_OES); + glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glUniform1i(texture_handle_, 0); glUniformMatrix4fv(uv_transform_, 1, GL_FALSE, &uv_transform[0]);
diff --git a/chrome/browser/android/vr/arcore_device/ar_renderer.h b/device/vr/android/arcore/ar_renderer.h similarity index 79% rename from chrome/browser/android/vr/arcore_device/ar_renderer.h rename to device/vr/android/arcore/ar_renderer.h index 5fc7d8d..b1365bc 100644 --- a/chrome/browser/android/vr/arcore_device/ar_renderer.h +++ b/device/vr/android/arcore/ar_renderer.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 CHROME_BROWSER_ANDROID_VR_ARCORE_DEVICE_AR_RENDERER_H_ -#define CHROME_BROWSER_ANDROID_VR_ARCORE_DEVICE_AR_RENDERER_H_ +#ifndef DEVICE_VR_ANDROID_ARCORE_AR_RENDERER_H_ +#define DEVICE_VR_ANDROID_ARCORE_AR_RENDERER_H_ #include "base/macros.h" #include "ui/gl/gl_bindings.h" @@ -33,4 +33,4 @@ } // namespace device -#endif // CHROME_BROWSER_ANDROID_VR_ARCORE_DEVICE_AR_RENDERER_H_ +#endif // DEVICE_VR_ANDROID_ARCORE_AR_RENDERER_H_
diff --git a/chrome/browser/android/vr/arcore_device/arcore_device.cc b/device/vr/android/arcore/arcore_device.cc similarity index 88% rename from chrome/browser/android/vr/arcore_device/arcore_device.cc rename to device/vr/android/arcore/arcore_device.cc index cab362d..37ab0d6 100644 --- a/chrome/browser/android/vr/arcore_device/arcore_device.cc +++ b/device/vr/android/arcore/arcore_device.cc
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "chrome/browser/android/vr/arcore_device/arcore_device.h" +#include "device/vr/android/arcore/arcore_device.h" #include <algorithm> @@ -11,16 +11,12 @@ #include "base/optional.h" #include "base/task/post_task.h" #include "base/trace_event/trace_event.h" -#include "chrome/browser/android/tab_android.h" -#include "chrome/browser/android/vr/arcore_device/ar_image_transport.h" -#include "chrome/browser/android/vr/arcore_device/arcore_gl.h" -#include "chrome/browser/android/vr/arcore_device/arcore_gl_thread.h" -#include "chrome/browser/android/vr/arcore_device/arcore_java_utils.h" -#include "chrome/browser/android/vr/arcore_device/arcore_session_utils.h" -#include "chrome/browser/android/vr/mailbox_to_surface_bridge.h" -#include "chrome/browser/permissions/permission_update_infobar_delegate_android.h" -#include "content/public/browser/render_frame_host.h" +#include "device/vr/android/arcore/ar_image_transport.h" +#include "device/vr/android/arcore/arcore_gl.h" +#include "device/vr/android/arcore/arcore_gl_thread.h" #include "device/vr/android/arcore/arcore_impl.h" +#include "device/vr/android/arcore/arcore_session_utils.h" +#include "device/vr/android/mailbox_to_surface_bridge.h" #include "ui/display/display.h" using base::android::JavaRef; @@ -64,14 +60,16 @@ ArCoreDevice::ArCoreDevice( std::unique_ptr<ArCoreFactory> arcore_factory, std::unique_ptr<ArImageTransportFactory> ar_image_transport_factory, - std::unique_ptr<vr::MailboxToSurfaceBridge> mailbox_to_surface_bridge, + std::unique_ptr<MailboxToSurfaceBridgeFactory> + mailbox_to_surface_bridge_factory, std::unique_ptr<vr::ArCoreSessionUtils> arcore_session_utils) : VRDeviceBase(mojom::XRDeviceId::ARCORE_DEVICE_ID), main_thread_task_runner_(base::ThreadTaskRunnerHandle::Get()), arcore_factory_(std::move(arcore_factory)), ar_image_transport_factory_(std::move(ar_image_transport_factory)), - mailbox_bridge_(std::move(mailbox_to_surface_bridge)), + mailbox_bridge_factory_(std::move(mailbox_to_surface_bridge_factory)), arcore_session_utils_(std::move(arcore_session_utils)), + mailbox_bridge_(mailbox_bridge_factory_->Create()), session_state_(std::make_unique<ArCoreDevice::SessionState>()) { // Ensure display_info_ is set to avoid crash in CallDeferredSessionCallback // if initialization fails. Use an arbitrary but really low resolution to make @@ -80,12 +78,6 @@ SetVRDisplayInfo(CreateVRDisplayInfo({16, 16})); } -ArCoreDevice::ArCoreDevice() - : ArCoreDevice(std::make_unique<ArCoreImplFactory>(), - std::make_unique<ArImageTransportFactory>(), - std::make_unique<vr::MailboxToSurfaceBridge>(), - std::make_unique<vr::ArCoreJavaUtils>()) {} - ArCoreDevice::~ArCoreDevice() { // If there's still a pending session request, reject it. CallDeferredRequestSessionCallback(/*success=*/false); @@ -128,16 +120,6 @@ bool use_dom_overlay = base::Contains( options->enabled_features, device::mojom::XRSessionFeature::DOM_OVERLAY); - if (use_dom_overlay) { - // Tell RenderFrameHostImpl that we're setting up the WebXR DOM Overlay, - // it checks for this in EnterFullscreen via HasSeenRecentXrOverlaySetup(). - content::RenderFrameHost* render_frame_host = - content::RenderFrameHost::FromID(options->render_process_id, - options->render_frame_id); - DCHECK(render_frame_host); - render_frame_host->SetIsXrOverlaySetup(); - } - // mailbox_bridge_ is either supplied from the constructor, or recreated in // OnSessionEnded(). DCHECK(mailbox_bridge_); @@ -242,7 +224,7 @@ // Create a new mailbox bridge for use in the next session. (This is cheap, // the constructor doesn't establish a GL context.) - mailbox_bridge_ = std::make_unique<vr::MailboxToSurfaceBridge>(); + mailbox_bridge_ = mailbox_bridge_factory_->Create(); // This sets HasExclusiveSession status to false. OnExitPresent(); @@ -294,6 +276,7 @@ session->data_provider = std::move(frame_data_provider); session->display_info = std::move(display_info); session->submit_frame_sink = std::move(presentation_connection); + session->supports_viewport_scaling = true; std::move(deferred_callback) .Run(std::move(session), std::move(session_controller));
diff --git a/chrome/browser/android/vr/arcore_device/arcore_device.h b/device/vr/android/arcore/arcore_device.h similarity index 90% rename from chrome/browser/android/vr/arcore_device/arcore_device.h rename to device/vr/android/arcore/arcore_device.h index 4576de3..1e9858e1 100644 --- a/chrome/browser/android/vr/arcore_device/arcore_device.h +++ b/device/vr/android/arcore/arcore_device.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 CHROME_BROWSER_ANDROID_VR_ARCORE_DEVICE_ARCORE_DEVICE_H_ -#define CHROME_BROWSER_ANDROID_VR_ARCORE_DEVICE_ARCORE_DEVICE_H_ +#ifndef DEVICE_VR_ANDROID_ARCORE_ARCORE_DEVICE_H_ +#define DEVICE_VR_ANDROID_ARCORE_ARCORE_DEVICE_H_ #include <jni.h> #include <memory> @@ -22,7 +22,6 @@ #include "ui/gfx/native_widget_types.h" namespace vr { -class MailboxToSurfaceBridge; class ArCoreSessionUtils; } // namespace vr @@ -31,15 +30,17 @@ class ArImageTransportFactory; class ArCoreFactory; class ArCoreGlThread; +class MailboxToSurfaceBridge; +class MailboxToSurfaceBridgeFactory; -class ArCoreDevice : public VRDeviceBase { +class COMPONENT_EXPORT(VR_ARCORE) ArCoreDevice : public VRDeviceBase { public: ArCoreDevice( std::unique_ptr<ArCoreFactory> arcore_factory, std::unique_ptr<ArImageTransportFactory> ar_image_transport_factory, - std::unique_ptr<vr::MailboxToSurfaceBridge> mailbox_to_surface_bridge, + std::unique_ptr<MailboxToSurfaceBridgeFactory> + mailbox_to_surface_bridge_factory, std::unique_ptr<vr::ArCoreSessionUtils> arcore_session_utils); - ArCoreDevice(); ~ArCoreDevice() override; // VRDeviceBase implementation. @@ -112,9 +113,11 @@ scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner_; std::unique_ptr<ArCoreFactory> arcore_factory_; std::unique_ptr<ArImageTransportFactory> ar_image_transport_factory_; - std::unique_ptr<vr::MailboxToSurfaceBridge> mailbox_bridge_; + std::unique_ptr<MailboxToSurfaceBridgeFactory> mailbox_bridge_factory_; std::unique_ptr<vr::ArCoreSessionUtils> arcore_session_utils_; + std::unique_ptr<MailboxToSurfaceBridge> mailbox_bridge_; + // Encapsulates data with session lifetime. struct SessionState { SessionState(); @@ -149,4 +152,4 @@ } // namespace device -#endif // CHROME_BROWSER_ANDROID_VR_ARCORE_DEVICE_ARCORE_DEVICE_H_ +#endif // DEVICE_VR_ANDROID_ARCORE_ARCORE_DEVICE_H_
diff --git a/chrome/browser/android/vr/arcore_device/arcore_gl.cc b/device/vr/android/arcore/arcore_gl.cc similarity index 94% rename from chrome/browser/android/vr/arcore_device/arcore_gl.cc rename to device/vr/android/arcore/arcore_gl.cc index 2c1a5c8..0002139 100644 --- a/chrome/browser/android/vr/arcore_device/arcore_gl.cc +++ b/device/vr/android/arcore/arcore_gl.cc
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "chrome/browser/android/vr/arcore_device/arcore_gl.h" +#include "device/vr/android/arcore/arcore_gl.h" #include <algorithm> #include <iomanip> @@ -20,12 +20,12 @@ #include "base/time/time.h" #include "base/trace_event/trace_event.h" #include "base/trace_event/traced_value.h" -#include "chrome/browser/android/vr/arcore_device/ar_image_transport.h" -#include "chrome/browser/android/vr/arcore_device/arcore_session_utils.h" -#include "chrome/browser/android/vr/web_xr_presentation_state.h" +#include "device/vr/android/arcore/ar_image_transport.h" #include "device/vr/android/arcore/arcore.h" #include "device/vr/android/arcore/arcore_math_utils.h" +#include "device/vr/android/arcore/arcore_session_utils.h" #include "device/vr/android/arcore/type_converters.h" +#include "device/vr/android/web_xr_presentation_state.h" #include "device/vr/public/mojom/pose.h" #include "device/vr/public/mojom/vr_service.mojom.h" #include "gpu/ipc/common/gpu_memory_buffer_impl_android_hardware_buffer.h" @@ -62,6 +62,22 @@ const gfx::Size kDefaultFrameSize = {1, 1}; const display::Display::Rotation kDefaultRotation = display::Display::ROTATE_0; +gfx::Transform GetContentTransform(const gfx::RectF& bounds) { + // Calculate the transform matrix from quad coordinates (range 0..1 with + // origin at bottom left of the quad) to texture lookup UV coordinates (also + // range 0..1 with origin at bottom left), where the active viewport uses a + // subset of the texture range that needs to be magnified to fill the quad. + // The bounds as used by the UpdateLayerBounds mojo messages appear to use an + // old WebVR convention with origin at top left, so the Y range needs to be + // mirrored. + gfx::Transform transform; + transform.matrix().set(0, 0, bounds.width()); + transform.matrix().set(1, 1, bounds.height()); + transform.matrix().set(0, 3, bounds.x()); + transform.matrix().set(1, 3, 1.f - bounds.y() - bounds.height()); + return transform; +} + } // namespace namespace device { @@ -400,6 +416,7 @@ vr::WebXrFrame* xrframe = webxr_->GetAnimatingFrame(); xrframe->time_pose = now; + xrframe->bounds_left = viewport_bounds_; if (display_info_changed_) { frame_data->left_eye = display_info_->left_eye.Clone(); @@ -573,7 +590,13 @@ DCHECK(webxr_->HaveProcessingFrame()); DCHECK(!ar_image_transport_->UseSharedBuffer()); - ar_image_transport_->CopyMailboxToSurfaceAndSwap(transfer_size_, mailbox); + // Use only the active bounds of the viewport, converting the + // bounds UV boundaries to a transform. See also OnWebXrTokenSignaled(). + gfx::Transform transform = + GetContentTransform(webxr_->GetProcessingFrame()->bounds_left); + ar_image_transport_->CopyMailboxToSurfaceAndSwap(transfer_size_, mailbox, + transform); + // Notify the client that we're done with the mailbox so that the underlying // image is eligible for destruction. submit_client_->OnSubmitFrameTransferred(true); @@ -661,9 +684,13 @@ webxr_->GetProcessingFrame()->time_copied = base::TimeTicks::Now(); webxr_->TransitionFrameProcessingToRendering(); + // Use only the active bounds of the viewport, converting the + // bounds UV boundaries to a transform. See also ProcessFrameFromMailbox(). + gfx::Transform transform = + GetContentTransform(webxr_->GetRenderingFrame()->bounds_left); glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER, 0); ar_image_transport_->CopyDrawnImageToFramebuffer( - webxr_.get(), camera_image_size_, shared_buffer_transform_); + webxr_.get(), camera_image_size_, transform); FinishFrame(frame_index); @@ -684,8 +711,22 @@ const gfx::RectF& left_bounds, const gfx::RectF& right_bounds, const gfx::Size& source_size) { - DVLOG(2) << __func__ << " source_size=" << source_size.ToString(); + DVLOG(2) << __func__ << " source_size=" << source_size.ToString() + << " left_bounds=" << left_bounds.ToString(); + // The first UpdateLayerBounds may arrive early, when there's + // no animating frame yet. In that case, just save it in viewport_bounds_ + // so that it's applied to the next animating frame. + if (webxr_->HaveAnimatingFrame()) { + // Handheld AR mode is monoscopic and only uses the left bounds. + webxr_->GetAnimatingFrame()->bounds_left = left_bounds; + (void)right_bounds; + } + viewport_bounds_ = left_bounds; + + // Early setting of transfer_size_ is OK since that's only used by the + // animating frame. Processing/rendering frames use the bounds from + // WebXRPresentationState. transfer_size_ = source_size; }
diff --git a/chrome/browser/android/vr/arcore_device/arcore_gl.h b/device/vr/android/arcore/arcore_gl.h similarity index 96% rename from chrome/browser/android/vr/arcore_device/arcore_gl.h rename to device/vr/android/arcore/arcore_gl.h index 6704d70..71cd3af5 100644 --- a/chrome/browser/android/vr/arcore_device/arcore_gl.h +++ b/device/vr/android/arcore/arcore_gl.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 CHROME_BROWSER_ANDROID_VR_ARCORE_DEVICE_ARCORE_GL_H_ -#define CHROME_BROWSER_ANDROID_VR_ARCORE_DEVICE_ARCORE_GL_H_ +#ifndef DEVICE_VR_ANDROID_ARCORE_ARCORE_GL_H_ +#define DEVICE_VR_ANDROID_ARCORE_ARCORE_GL_H_ #include <memory> #include <unordered_set> @@ -225,6 +225,11 @@ // smaller than the camera image if framebufferScaleFactor is < 1.0. gfx::Size transfer_size_ = gfx::Size(0, 0); + // Viewport size to use for new animating frames. Currently in-flight + // processing/rendering frames continue using the viewport size stored + // in their WebXrFrame state. + gfx::RectF viewport_bounds_ = gfx::RectF(0.f, 0.f, 1.f, 1.f); + // The camera image size stays locked to the screen size even if // framebufferScaleFactor changes. gfx::Size camera_image_size_ = gfx::Size(0, 0); @@ -235,10 +240,6 @@ // and can include 90 degree rotations or other nontrivial transforms. gfx::Transform uv_transform_; - // UV transform for drawing received WebGL content from a shared buffer's - // texture, this is simply an identity. - gfx::Transform shared_buffer_transform_; - gfx::Transform projection_; gfx::Transform inverse_projection_; // The first run of ProduceFrame should set uv_transform_ and projection_ @@ -324,4 +325,4 @@ } // namespace device -#endif // CHROME_BROWSER_ANDROID_VR_ARCORE_DEVICE_ARCORE_GL_H_ +#endif // DEVICE_VR_ANDROID_ARCORE_ARCORE_GL_H_
diff --git a/chrome/browser/android/vr/arcore_device/arcore_gl_thread.cc b/device/vr/android/arcore/arcore_gl_thread.cc similarity index 80% rename from chrome/browser/android/vr/arcore_device/arcore_gl_thread.cc rename to device/vr/android/arcore/arcore_gl_thread.cc index 99c60201..cd04fcb 100644 --- a/chrome/browser/android/vr/arcore_device/arcore_gl_thread.cc +++ b/device/vr/android/arcore/arcore_gl_thread.cc
@@ -2,18 +2,18 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "chrome/browser/android/vr/arcore_device/arcore_gl_thread.h" +#include "device/vr/android/arcore/arcore_gl_thread.h" #include <utility> #include "base/version.h" -#include "chrome/browser/android/vr/arcore_device/ar_image_transport.h" -#include "chrome/browser/android/vr/arcore_device/arcore_gl.h" +#include "device/vr/android/arcore/ar_image_transport.h" +#include "device/vr/android/arcore/arcore_gl.h" namespace device { ArCoreGlThread::ArCoreGlThread( std::unique_ptr<ArImageTransportFactory> ar_image_transport_factory, - std::unique_ptr<vr::MailboxToSurfaceBridge> mailbox_bridge, + std::unique_ptr<MailboxToSurfaceBridge> mailbox_bridge, base::OnceCallback<void()> initialized_callback) : base::android::JavaHandlerThread("ArCoreGL"), ar_image_transport_factory_(std::move(ar_image_transport_factory)),
diff --git a/chrome/browser/android/vr/arcore_device/arcore_gl_thread.h b/device/vr/android/arcore/arcore_gl_thread.h similarity index 71% rename from chrome/browser/android/vr/arcore_device/arcore_gl_thread.h rename to device/vr/android/arcore/arcore_gl_thread.h index 7e74423..a8a8578 100644 --- a/chrome/browser/android/vr/arcore_device/arcore_gl_thread.h +++ b/device/vr/android/arcore/arcore_gl_thread.h
@@ -2,26 +2,27 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef CHROME_BROWSER_ANDROID_VR_ARCORE_DEVICE_ARCORE_GL_THREAD_H_ -#define CHROME_BROWSER_ANDROID_VR_ARCORE_DEVICE_ARCORE_GL_THREAD_H_ +#ifndef DEVICE_VR_ANDROID_ARCORE_ARCORE_GL_THREAD_H_ +#define DEVICE_VR_ANDROID_ARCORE_ARCORE_GL_THREAD_H_ #include <memory> #include "base/android/java_handler_thread.h" #include "base/macros.h" #include "base/memory/weak_ptr.h" #include "base/single_thread_task_runner.h" -#include "chrome/browser/android/vr/mailbox_to_surface_bridge.h" +#include "device/vr/android/mailbox_to_surface_bridge.h" namespace device { class ArCoreGl; class ArImageTransportFactory; +class MailboxToSurfaceBridge; class ArCoreGlThread : public base::android::JavaHandlerThread { public: ArCoreGlThread( std::unique_ptr<ArImageTransportFactory> ar_image_transport_factory, - std::unique_ptr<vr::MailboxToSurfaceBridge> mailbox_bridge, + std::unique_ptr<MailboxToSurfaceBridge> mailbox_bridge, base::OnceCallback<void()> initialized_callback); ~ArCoreGlThread() override; ArCoreGl* GetArCoreGl(); @@ -32,7 +33,7 @@ private: std::unique_ptr<ArImageTransportFactory> ar_image_transport_factory_; - std::unique_ptr<vr::MailboxToSurfaceBridge> mailbox_bridge_; + std::unique_ptr<MailboxToSurfaceBridge> mailbox_bridge_; base::OnceCallback<void()> initialized_callback_; // Created on GL thread. @@ -43,4 +44,4 @@ } // namespace device -#endif // CHROME_BROWSER_ANDROID_VR_ARCORE_DEVICE_ARCORE_GL_THREAD_H_ +#endif // DEVICE_VR_ANDROID_ARCORE_ARCORE_GL_THREAD_H_
diff --git a/chrome/browser/android/vr/arcore_device/arcore_session_utils.h b/device/vr/android/arcore/arcore_session_utils.h similarity index 88% rename from chrome/browser/android/vr/arcore_device/arcore_session_utils.h rename to device/vr/android/arcore/arcore_session_utils.h index e48c9080..d3c3f4d 100644 --- a/chrome/browser/android/vr/arcore_device/arcore_session_utils.h +++ b/device/vr/android/arcore/arcore_session_utils.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 CHROME_BROWSER_ANDROID_VR_ARCORE_DEVICE_ARCORE_SESSION_UTILS_H_ -#define CHROME_BROWSER_ANDROID_VR_ARCORE_DEVICE_ARCORE_SESSION_UTILS_H_ +#ifndef DEVICE_VR_ANDROID_ARCORE_ARCORE_SESSION_UTILS_H_ +#define DEVICE_VR_ANDROID_ARCORE_ARCORE_SESSION_UTILS_H_ #include "base/android/scoped_java_ref.h" #include "base/memory/weak_ptr.h" @@ -51,4 +51,4 @@ } // namespace vr -#endif // CHROME_BROWSER_ANDROID_VR_ARCORE_DEVICE_ARCORE_SESSION_UTILS_H_ +#endif // DEVICE_VR_ANDROID_ARCORE_ARCORE_SESSION_UTILS_H_
diff --git a/device/vr/android/mailbox_to_surface_bridge.h b/device/vr/android/mailbox_to_surface_bridge.h new file mode 100644 index 0000000..c7b123f --- /dev/null +++ b/device/vr/android/mailbox_to_surface_bridge.h
@@ -0,0 +1,101 @@ +// Copyright 2020 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 DEVICE_VR_ANDROID_MAILBOX_TO_SURFACE_BRIDGE_H_ +#define DEVICE_VR_ANDROID_MAILBOX_TO_SURFACE_BRIDGE_H_ + +namespace gfx { +class ColorSpace; +class GpuFence; +class Transform; +} // namespace gfx + +namespace gl { +class SurfaceTexture; +} // namespace gl + +namespace gpu { +class GpuMemoryBufferImplAndroidHardwareBuffer; +struct MailboxHolder; +struct SyncToken; +} // namespace gpu + +namespace device { +class MailboxToSurfaceBridge { + public: + virtual ~MailboxToSurfaceBridge() {} + + // Returns true if the GPU process connection is established and ready to use. + // Equivalent to waiting for on_initialized to be called. + virtual bool IsConnected() = 0; + + // Checks if a workaround from "gpu/config/gpu_driver_bug_workaround_type.h" + // is active. Requires initialization to be complete. + virtual bool IsGpuWorkaroundEnabled(int32_t workaround) = 0; + + // This call is needed for Surface transport, in that case it must be called + // on the GL thread with a valid local native GL context. If it's not used, + // only the SharedBuffer transport methods are available. + virtual void CreateSurface(gl::SurfaceTexture*) = 0; + + // Asynchronously create the context using the surface provided by an earlier + // CreateSurface call, or an offscreen context if that wasn't called. Also + // binds the context provider to the current thread (making it the GL thread), + // and calls the callback on the GL thread. + virtual void CreateAndBindContextProvider(base::OnceClosure callback) = 0; + + // All other public methods below must be called on the GL thread + // (except when marked otherwise). + + virtual void ResizeSurface(int width, int height) = 0; + + // Returns true if swapped successfully. This can fail if the GL + // context isn't ready for use yet, in that case the caller + // won't get a new frame on the SurfaceTexture. + virtual bool CopyMailboxToSurfaceAndSwap( + const gpu::MailboxHolder& mailbox) = 0; + virtual bool CopyMailboxToSurfaceAndSwap( + const gpu::MailboxHolder& mailbox, + const gfx::Transform& uv_transform) = 0; + + virtual void GenSyncToken(gpu::SyncToken* out_sync_token) = 0; + + virtual void WaitSyncToken(const gpu::SyncToken& sync_token) = 0; + + // Copies a GpuFence from the local context to the GPU process, + // and issues a server wait for it. + virtual void WaitForClientGpuFence(gfx::GpuFence*) = 0; + + // Creates a GpuFence in the GPU process after the supplied sync_token + // completes, and copies it for use in the local context. This is + // asynchronous, the callback receives the GpuFence once it's available. + virtual void CreateGpuFence( + const gpu::SyncToken& sync_token, + base::OnceCallback<void(std::unique_ptr<gfx::GpuFence>)> callback) = 0; + + // Creates a shared image bound to |buffer|. Returns a mailbox holder that + // references the shared image with a sync token representing a point after + // the creation. Caller must call DestroySharedImage to free the shared image. + // Does not take ownership of |buffer| or retain any references to it. + virtual gpu::MailboxHolder CreateSharedImage( + gpu::GpuMemoryBufferImplAndroidHardwareBuffer* buffer, + const gfx::ColorSpace& color_space, + uint32_t usage) = 0; + + // Destroys a shared image created by CreateSharedImage. The mailbox_holder's + // sync_token must have been updated to a sync token after the last use of the + // shared image. + virtual void DestroySharedImage(const gpu::MailboxHolder& mailbox_holder) = 0; +}; + +class MailboxToSurfaceBridgeFactory { + public: + virtual ~MailboxToSurfaceBridgeFactory() {} + + virtual std::unique_ptr<device::MailboxToSurfaceBridge> Create() const = 0; +}; + +} // namespace device + +#endif // DEVICE_VR_ANDROID_MAILBOX_TO_SURFACE_BRIDGE_H_
diff --git a/chrome/browser/android/vr/web_xr_presentation_state.cc b/device/vr/android/web_xr_presentation_state.cc similarity index 98% rename from chrome/browser/android/vr/web_xr_presentation_state.cc rename to device/vr/android/web_xr_presentation_state.cc index 52caa35..7c42a6d8 100644 --- a/chrome/browser/android/vr/web_xr_presentation_state.cc +++ b/device/vr/android/web_xr_presentation_state.cc
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "chrome/browser/android/vr/web_xr_presentation_state.h" +#include "device/vr/android/web_xr_presentation_state.h" #include "base/logging.h" #include "base/trace_event/trace_event.h"
diff --git a/chrome/browser/android/vr/web_xr_presentation_state.h b/device/vr/android/web_xr_presentation_state.h similarity index 90% rename from chrome/browser/android/vr/web_xr_presentation_state.h rename to device/vr/android/web_xr_presentation_state.h index c65fecb..dce959a 100644 --- a/chrome/browser/android/vr/web_xr_presentation_state.h +++ b/device/vr/android/web_xr_presentation_state.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 CHROME_BROWSER_ANDROID_VR_WEB_XR_PRESENTATION_STATE_H_ -#define CHROME_BROWSER_ANDROID_VR_WEB_XR_PRESENTATION_STATE_H_ +#ifndef DEVICE_VR_ANDROID_WEB_XR_PRESENTATION_STATE_H_ +#define DEVICE_VR_ANDROID_WEB_XR_PRESENTATION_STATE_H_ #include <memory> #include <utility> @@ -13,6 +13,7 @@ #include "base/macros.h" #include "base/memory/ref_counted.h" #include "gpu/command_buffer/common/mailbox_holder.h" +#include "ui/gfx/geometry/rect_f.h" #include "ui/gfx/geometry/size.h" #include "ui/gfx/transform.h" @@ -127,6 +128,16 @@ std::unique_ptr<WebXrSharedBuffer> camera_image_shared_buffer; + // Viewport bounds used for rendering, in texture coordinates with uv=(0, 1) + // corresponding to viewport pixel (0, 0) as set by UpdateLayerBounds. + // + // Currently this is only used by the ARCore handheld AR mode which is + // monoscopic and uses the left viewport. TODO(https://crbug.com/1134203): The + // GVR device currently has its own separate bounds tracking implementation. + // That should be updated to use this implementation, at that time a matching + // bounds_right would need to be added. + gfx::RectF bounds_left; + DISALLOW_COPY_AND_ASSIGN(WebXrFrame); }; @@ -214,4 +225,4 @@ } // namespace vr -#endif // CHROME_BROWSER_ANDROID_VR_WEB_XR_PRESENTATION_STATE_H_ +#endif // DEVICE_VR_ANDROID_WEB_XR_PRESENTATION_STATE_H_
diff --git a/docs/accessibility/ia2_to_uia.md b/docs/accessibility/ia2_to_uia.md new file mode 100644 index 0000000..5dffbad --- /dev/null +++ b/docs/accessibility/ia2_to_uia.md
@@ -0,0 +1,139 @@ +# Runtime two way IAccessible2 to UI Automation elements look up via unique id +Assistive technologies (ATs) who currently rely on IAccessible2 (IA2) that want +to take advantage of the UI Automation (UIA) features at runtime can convert an +IA2 element to an UIA element via a unique id and directly access UIA's API. +This enables ATs who want to gradually transition from IA2 to UIA to experiment +with individual UIA elements at runtime without switching entirely to UIA. + + +To look up an UIA element through a unique id, an AT can utilize +`IUIAutomationItemContainerPattern::FindItemByProperty()` with the custom UIA +unique id property and the element's unique id as parameters. +To look up an IA2 element from an UIA element, an AT can simply +utilize IUIAutomationLegacyIAccessiblePattern::GetIAccessible() and +then query for IAccessible2 interface. The unique id is not needed to +look up the IA2 element from UIA element. + +## Convert an IA2 element to UIA element via unique id +An IA2 element can be converted to an UIA element at runtime via a unique id +that is shared between the two APIs. + +*Note: For the purpose of brevity and clarity, the code snippets below do not +include clean-up of COM references neither does it have error handling.* + +~~~c++ + #include <uiautomation.h> + #include <uiautomationclient.h> + + // Consider the following HTML: + // <html> + // <button>button</button> + // </html> + + // Register custom UIA property for retrieving the unique id of IA2 object. + // {cc7eeb32-4b62-4f4c-aff6-1c2e5752ad8e} + GUID UiaPropertyUniqueIdGuid = { + 0xcc7eeb32, + 0x4b62, + 0x4f4c, + {0xaf, 0xf6, 0x1c, 0x2e, 0x57, 0x52, 0xad, 0x8e}}; + + // Create the registrar object and get the IUIAutomationRegistrar + // interface pointer. + IUIAutomationRegistrar* registrar; + CoCreateInstance(CLSID_CUIAutomationRegistrar, nullptr, CLSCTX_INPROC_SERVER, + IID_PPV_ARGS(®istrar)); + + // Custom UIA property id used to retrieve the unique id between UIA/IA2. + PROPERTYID uia_unique_id_property_id; + + // Register the custom UIA property that represents the unique id of an UIA + // element which also matches its corresponding IA2 element's unique id. + // Custom property registration only needs to be done once per process + // lifetime. + UIAutomationPropertyInfo unique_id_property_info = { + UiaPropertyUniqueIdGuid, L"UniqueId", UIAutomationType_String}; + registrar->RegisterProperty(&unique_id_property_info, + &uia_unique_id_property_id); + + // Assume we are given the IAccessible2 element for button, and we want to + // retrieve its corresponding UIA element for the final result. + IAccessible2* button_ia2; /* Initialized */ + + // Retrieve button IA2 element's unique id, which will be used to look up the + // corresponding UIA element later. + LONG unique_id_long; + button_ia2->get_uniqueID(&unique_id_long); + + // Assume we are given the Window Handle hwnd for the root. + UIA_HWND hwnd; /* Initialized */ + + // Instantiating an IUIAutomation object. + IUIAutomation* ui_automation; + CoCreateInstance(CLSID_CUIAutomation, NULL, CLSCTX_INPROC_SERVER, + IID_PPV_ARGS(&ui_automation)); + + // Retrieve the root element from the window handle. + IUIAutomationElement* root_element; + ui_automation->ElementFromHandle(hwnd, &root_element); + + // Retrieve the ItemContainerPattern of the root element. + IUIAutomationItemContainerPattern* item_container_pattern; + root_element->GetCurrentPatternAs(UIA_ItemContainerPatternId, + IID_PPV_ARGS(&item_container_pattern)); + + // We also need to convert the retrieved IA2 element unique id from long to + // VARIANT.VT_BSTR to be consumed by UIA. For demo purpose, I utilize + // std::string here as an intermediary step to convert to VARIANT.VT_BSTR. + std::string unique_id_str = std::to_string(unique_id_long); + + VARIANT unique_id_variant; + unique_id_variant.vt = VT_BSTR; + unique_id_variant.bstrVal = SysAllocString(unique_id_str.c_str()); + + // Retrieving the corresponding UIAutomation element from the unique id of IA2 + // object. + IUIAutomationElement* button_uia; + item_container_pattern->FindItemByProperty(nullptr, uia_unique_id_property_id, + unique_id_variant, + &button_uia /* final result */); +~~~ + +## Convert an UIA element to IA2 element. +Converting an UIA element to an IA2 element is a lot more straightforward and +does not require the shared unique id. Consider the same example above. +~~~c++ + #include <uiautomationclient.h> + + // Assume we are given the UIAutomation element for button, and we want to + // retrieve its corresponding IAccessible2 element for the final result. + IUIAutomationElement* button_uia; /* Initialized */ + + // Retrieve the LegacyIAccessiblePattern of the button UIA element. + IUIAutomationLegacyIAccessiblePattern* legacy_iaccessible_pattern; + legacy_iaccessible_pattern->GetCurrentPatternAs( + UIA_LegacyIAccessiblePatternId, + IID_PPV_ARGS(&legacy_iaccessible_pattern)); + + // Retrieve the IAccessible element from button UIA element. + IAccessible* button_iaccessible; + legacy_iaccessible_pattern->GetIAccessible(&iaccessible); + + // Use QueryService to retrieve button's IAccessible2 element from IAccessible + // element. + IServiceProvider* service_provider; + IAccessible2* button_ia2; + if (SUCCEEDED(button_iaccessible->QueryInterface( + IID_PPV_ARGS(&service_provider)))) { + service_provider->QueryService( + IID_PPV_ARGS(&button_ia2 /* final result */)); + } +~~~ + +## Docs & References: +[Custom UIA Property and Pattern registration in Chromium](https://chromium.googlesource.com/chromium/src/+/master/ui/accessibility/platform/uia_registrar_win.h) + +[UI Automation IItemContainerPattern. It is used to look up IAccessible2 element +via a unique id](https://docs.microsoft.com/en-us/windows/win32/api/uiautomationclient/nn-uiautomationclient-iuiautomationitemcontainerpattern) + +[UI Automation Client](https://docs.microsoft.com/en-us/windows/win32/api/uiautomationclient/)
diff --git a/extensions/browser/api/declarative_net_request/action_tracker.h b/extensions/browser/api/declarative_net_request/action_tracker.h index aeb7e06..4c310ca 100644 --- a/extensions/browser/api/declarative_net_request/action_tracker.h +++ b/extensions/browser/api/declarative_net_request/action_tracker.h
@@ -61,8 +61,9 @@ const WebRequestInfo& request_info); // Updates the action count for all tabs for the specified |extension_id|'s - // extension action. Called when chrome.setActionCountAsBadgeText(true) is - // called by an extension. + // extension action. Called when the extension calls setExtensionActionOptions + // to enable setting the action count as badge text. + // TODO(karandeepb): Rename to OnActionCountAsBadgeTextPreferenceEnabled. void OnPreferenceEnabled(const ExtensionId& extension_id) const; // Clears the TrackedInfo for the specified |extension_id| for all tabs.
diff --git a/extensions/browser/api/declarative_net_request/declarative_net_request_api.cc b/extensions/browser/api/declarative_net_request/declarative_net_request_api.cc index 69069b9..a8f7caf 100644 --- a/extensions/browser/api/declarative_net_request/declarative_net_request_api.cc +++ b/extensions/browser/api/declarative_net_request/declarative_net_request_api.cc
@@ -332,31 +332,35 @@ return user_gesture() || disable_throttling_for_test_; } -DeclarativeNetRequestSetActionCountAsBadgeTextFunction:: - DeclarativeNetRequestSetActionCountAsBadgeTextFunction() = default; -DeclarativeNetRequestSetActionCountAsBadgeTextFunction:: - ~DeclarativeNetRequestSetActionCountAsBadgeTextFunction() = default; +DeclarativeNetRequestSetExtensionActionOptionsFunction:: + DeclarativeNetRequestSetExtensionActionOptionsFunction() = default; +DeclarativeNetRequestSetExtensionActionOptionsFunction:: + ~DeclarativeNetRequestSetExtensionActionOptionsFunction() = default; ExtensionFunction::ResponseAction -DeclarativeNetRequestSetActionCountAsBadgeTextFunction::Run() { - using Params = dnr_api::SetActionCountAsBadgeText::Params; +DeclarativeNetRequestSetExtensionActionOptionsFunction::Run() { + using Params = dnr_api::SetExtensionActionOptions::Params; base::string16 error; std::unique_ptr<Params> params(Params::Create(*args_, &error)); EXTENSION_FUNCTION_VALIDATE(params); EXTENSION_FUNCTION_VALIDATE(error.empty()); + bool use_action_count_as_badge_text = + params->options.display_action_count_as_badge_text; ExtensionPrefs* prefs = ExtensionPrefs::Get(browser_context()); - if (params->enable == prefs->GetDNRUseActionCountAsBadgeText(extension_id())) + if (use_action_count_as_badge_text == + prefs->GetDNRUseActionCountAsBadgeText(extension_id())) return RespondNow(NoArguments()); - prefs->SetDNRUseActionCountAsBadgeText(extension_id(), params->enable); + prefs->SetDNRUseActionCountAsBadgeText(extension_id(), + use_action_count_as_badge_text); // If the preference is switched on, update the extension's badge text with // the number of actions matched for this extension. Otherwise, clear the // action count for the extension's icon and show the default badge text if // set. - if (params->enable) { + if (use_action_count_as_badge_text) { declarative_net_request::RulesMonitorService* rules_monitor_service = declarative_net_request::RulesMonitorService::Get(browser_context()); DCHECK(rules_monitor_service);
diff --git a/extensions/browser/api/declarative_net_request/declarative_net_request_api.h b/extensions/browser/api/declarative_net_request/declarative_net_request_api.h index b63b5b2..496d9dc 100644 --- a/extensions/browser/api/declarative_net_request/declarative_net_request_api.h +++ b/extensions/browser/api/declarative_net_request/declarative_net_request_api.h
@@ -105,15 +105,15 @@ static bool disable_throttling_for_test_; }; -class DeclarativeNetRequestSetActionCountAsBadgeTextFunction +class DeclarativeNetRequestSetExtensionActionOptionsFunction : public ExtensionFunction { public: - DeclarativeNetRequestSetActionCountAsBadgeTextFunction(); - DECLARE_EXTENSION_FUNCTION("declarativeNetRequest.setActionCountAsBadgeText", + DeclarativeNetRequestSetExtensionActionOptionsFunction(); + DECLARE_EXTENSION_FUNCTION("declarativeNetRequest.setExtensionActionOptions", DECLARATIVENETREQUEST_SETACTIONCOUNTASBADGETEXT) protected: - ~DeclarativeNetRequestSetActionCountAsBadgeTextFunction() override; + ~DeclarativeNetRequestSetExtensionActionOptionsFunction() override; ExtensionFunction::ResponseAction Run() override; };
diff --git a/extensions/browser/extension_action.h b/extensions/browser/extension_action.h index cda369b8..1e38d63f 100644 --- a/extensions/browser/extension_action.h +++ b/extensions/browser/extension_action.h
@@ -308,7 +308,7 @@ // Maps tab_id to the number of actions taken based on declarative net request // rule matches on incoming requests. Overrides the default |badge_text_| for - // this extension if it has called chrome.setActionCountAsBadgeText(true). + // this extension if it has opted into setting the action count as badge text. std::map<int, int> dnr_action_count_; // ExtensionIconSet containing paths to bitmaps from which default icon's
diff --git a/extensions/browser/extension_function_histogram_value.h b/extensions/browser/extension_function_histogram_value.h index 9cbd5bdd..5f3e5a5 100644 --- a/extensions/browser/extension_function_histogram_value.h +++ b/extensions/browser/extension_function_histogram_value.h
@@ -1573,6 +1573,7 @@ ACCESSIBILITY_PRIVATE_MOVEMAGNIFIERTORECT = 1510, FILEMANAGERPRIVATE_SINGLEPARTITIONFORMAT = 1511, TABS_REMOVECSS = 1512, + IDENTITY_CLEARALLCACHEDAUTHTOKENS = 1513, // Last entry: Add new entries above, then run: // python tools/metrics/histograms/update_extension_histograms.py ENUM_BOUNDARY
diff --git a/extensions/browser/extension_prefs.h b/extensions/browser/extension_prefs.h index a708acc5..abb2ab6 100644 --- a/extensions/browser/extension_prefs.h +++ b/extensions/browser/extension_prefs.h
@@ -671,7 +671,7 @@ // Whether the extension with the given |extension_id| is using its ruleset's // matched action count for the badge text. This is set via the - // setActionCountAsBadgeText API call. + // setExtensionActionOptions API call. bool GetDNRUseActionCountAsBadgeText(const ExtensionId& extension_id) const; void SetDNRUseActionCountAsBadgeText(const ExtensionId& extension_id, bool use_action_count_as_badge_text);
diff --git a/extensions/common/api/declarative_net_request.idl b/extensions/common/api/declarative_net_request.idl index 76fe5b61..b736b68 100644 --- a/extensions/common/api/declarative_net_request.idl +++ b/extensions/common/api/declarative_net_request.idl
@@ -428,6 +428,12 @@ DOMString[]? enableRulesetIds; }; + dictionary ExtensionActionOptions { + // Whether to automatically display the action count for a page as the + // extension's badge text. False by default. + boolean displayActionCountAsBadgeText; + }; + callback EmptyCallback = void(); callback GetAllowedPagesCallback = void(DOMString[] result); callback GetRulesCallback = void(Rule[] rules); @@ -500,10 +506,10 @@ static void getMatchedRules(optional MatchedRulesFilter filter, GetMatchedRulesCallback callback); - // Sets whether to automatically badge extension's icon to the matched - // action count for a tab. This preference is persisted across sessions and - // is false by default. - static void setActionCountAsBadgeText(boolean enable); + // Configures how matched actions will be displayed on the extension action. + // This preference is persisted across sessions. + static void setExtensionActionOptions( + ExtensionActionOptions options); // Checks if the given regular expression will be supported as a // <code>regexFilter</code> rule condition.
diff --git a/extensions/renderer/api_activity_logger.cc b/extensions/renderer/api_activity_logger.cc index e4c7fc1..d7616ff2 100644 --- a/extensions/renderer/api_activity_logger.cc +++ b/extensions/renderer/api_activity_logger.cc
@@ -56,24 +56,28 @@ ScriptContext* script_context = ScriptContextSet::GetContextByV8Context(context); - auto value_args = std::make_unique<base::ListValue>(); std::unique_ptr<content::V8ValueConverter> converter = content::V8ValueConverter::Create(); ActivityLogConverterStrategy strategy; converter->SetFunctionAllowed(true); converter->SetStrategy(&strategy); - value_args->Reserve(arguments.size()); + + base::Value::ListStorage value_args; + value_args.reserve(arguments.size()); // TODO(devlin): This doesn't protect against custom properties, so it might // not perfectly reflect the passed arguments. for (const auto& arg : arguments) { std::unique_ptr<base::Value> converted_arg = converter->FromV8Value(arg, context); - value_args->Append(converted_arg ? std::move(converted_arg) - : std::make_unique<base::Value>()); + if (!converted_arg) + converted_arg = std::make_unique<base::Value>(); + value_args.push_back( + base::Value::FromUniquePtrValue(std::move(converted_arg))); } LogInternal(APICALL, script_context->GetExtensionID(), call_name, - std::move(value_args), std::string()); + std::make_unique<base::ListValue>(std::move(value_args)), + std::string()); } void APIActivityLogger::LogEvent(ScriptContext* script_context, @@ -114,10 +118,10 @@ } // Get the array of call arguments. - auto arguments = std::make_unique<base::ListValue>(); + base::Value::ListStorage arguments; v8::Local<v8::Array> arg_array = v8::Local<v8::Array>::Cast(args[2]); if (arg_array->Length() > 0) { - arguments->Reserve(arg_array->Length()); + arguments.reserve(arg_array->Length()); std::unique_ptr<content::V8ValueConverter> converter = content::V8ValueConverter::Create(); ActivityLogConverterStrategy strategy; @@ -128,12 +132,15 @@ // actual error handling. std::unique_ptr<base::Value> converted_arg = converter->FromV8Value( arg_array->Get(context, i).ToLocalChecked(), context); - arguments->Append(converted_arg ? std::move(converted_arg) - : std::make_unique<base::Value>()); + if (!converted_arg) + converted_arg = std::make_unique<base::Value>(); + arguments.push_back( + base::Value::FromUniquePtrValue(std::move(converted_arg))); } } - LogInternal(call_type, extension_id, call_name, std::move(arguments), extra); + LogInternal(call_type, extension_id, call_name, + std::make_unique<base::ListValue>(std::move(arguments)), extra); } // static
diff --git a/extensions/renderer/bindings/api_signature.cc b/extensions/renderer/bindings/api_signature.cc index 900215b..9d23e3cd 100644 --- a/extensions/renderer/bindings/api_signature.cc +++ b/extensions/renderer/bindings/api_signature.cc
@@ -500,8 +500,8 @@ callback = value.As<v8::Function>(); } - auto json = std::make_unique<base::ListValue>(); - json->Reserve(size); + base::Value::ListStorage json; + json.reserve(size); std::unique_ptr<content::V8ValueConverter> converter = content::V8ValueConverter::Create(); @@ -520,11 +520,11 @@ // null). Duplicate that behavior here. converted = std::make_unique<base::Value>(); } - json->Append(std::move(converted)); + json.push_back(base::Value::FromUniquePtrValue(std::move(converted))); } JSONParseResult result; - result.arguments = std::move(json); + result.arguments = std::make_unique<base::ListValue>(std::move(json)); result.callback = callback; result.async_type = callback.IsEmpty() ? binding::AsyncResponseType::kNone
diff --git a/gpu/command_buffer/service/shared_image_backing_factory_gl_texture.h b/gpu/command_buffer/service/shared_image_backing_factory_gl_texture.h index 0d10395..c66613b 100644 --- a/gpu/command_buffer/service/shared_image_backing_factory_gl_texture.h +++ b/gpu/command_buffer/service/shared_image_backing_factory_gl_texture.h
@@ -22,6 +22,10 @@ class ColorSpace; } // namespace gfx +namespace gl { +class ProgressReporter; +} // namespace gl + namespace gpu { class SharedImageBacking; class SharedImageBatchAccessManager; @@ -43,7 +47,8 @@ const GpuDriverBugWorkarounds& workarounds, const GpuFeatureInfo& gpu_feature_info, ImageFactory* image_factory, - SharedImageBatchAccessManager* batch_access_manager); + SharedImageBatchAccessManager* batch_access_manager, + gl::ProgressReporter* progress_reporter); ~SharedImageBackingFactoryGLTexture() override; // SharedImageBackingFactory implementation. @@ -170,6 +175,10 @@ SharedImageBackingGLCommon::UnpackStateAttribs attribs; GpuDriverBugWorkarounds workarounds_; + // Used to notify the watchdog before a buffer allocation in case it takes + // long. + gl::ProgressReporter* const progress_reporter_ = nullptr; + #if defined(OS_ANDROID) SharedImageBatchAccessManager* batch_access_manager_ = nullptr; #endif
diff --git a/gpu/command_buffer/service/shared_image_backing_factory_gl_texture_unittest.cc b/gpu/command_buffer/service/shared_image_backing_factory_gl_texture_unittest.cc index 0fbb0f60..276317a3f 100644 --- a/gpu/command_buffer/service/shared_image_backing_factory_gl_texture_unittest.cc +++ b/gpu/command_buffer/service/shared_image_backing_factory_gl_texture_unittest.cc
@@ -29,6 +29,7 @@ #include "gpu/config/gpu_feature_info.h" #include "gpu/config/gpu_preferences.h" #include "gpu/config/gpu_test_config.h" +#include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/skia/include/core/SkPromiseImageTexture.h" #include "third_party/skia/include/core/SkSurface.h" @@ -44,6 +45,9 @@ #include "ui/gl/gl_image_stub.h" #include "ui/gl/gl_surface.h" #include "ui/gl/init/gl_factory.h" +#include "ui/gl/progress_reporter.h" + +using testing::AtLeast; namespace gpu { namespace { @@ -79,6 +83,15 @@ #endif } +class MockProgressReporter : public gl::ProgressReporter { + public: + MockProgressReporter() = default; + ~MockProgressReporter() override = default; + + // gl::ProgressReporter implementation. + MOCK_METHOD0(ReportProgress, void()); +}; + class SharedImageBackingFactoryGLTextureTestBase : public testing::TestWithParam<std::tuple<bool, viz::ResourceFormat>> { public: @@ -105,7 +118,7 @@ preferences.use_passthrough_cmd_decoder = use_passthrough(); backing_factory_ = std::make_unique<SharedImageBackingFactoryGLTexture>( preferences, workarounds, GpuFeatureInfo(), factory, - shared_image_manager_->batch_access_manager()); + shared_image_manager_->batch_access_manager(), &progress_reporter_); memory_type_tracker_ = std::make_unique<MemoryTypeTracker>(nullptr); shared_image_representation_factory_ = @@ -118,11 +131,31 @@ gles2::PassthroughCommandDecoderSupported(); } + bool can_create_non_scanout_shared_image(viz::ResourceFormat format) const { + if (format == viz::ResourceFormat::BGRA_1010102 || + format == viz::ResourceFormat::RGBA_1010102) { + return supports_ar30_ || supports_ab30_; + } else if (format == viz::ResourceFormat::ETC1) { + return supports_etc1_; + } + return true; + } + + bool can_create_scanout_or_gmb_shared_image( + viz::ResourceFormat format) const { + if (format == viz::ResourceFormat::BGRA_1010102) + return supports_ar30_; + else if (format == viz::ResourceFormat::RGBA_1010102) + return supports_ab30_; + return true; + } + viz::ResourceFormat get_format() { return std::get<1>(GetParam()); } GrDirectContext* gr_context() { return context_state_->gr_context(); } protected: + ::testing::NiceMock<MockProgressReporter> progress_reporter_; scoped_refptr<gl::GLSurface> surface_; scoped_refptr<gl::GLContext> context_; scoped_refptr<SharedContextState> context_state_; @@ -213,6 +246,9 @@ return; } + const bool should_succeed = can_create_non_scanout_shared_image(get_format()); + if (should_succeed) + EXPECT_CALL(progress_reporter_, ReportProgress).Times(AtLeast(1)); auto mailbox = Mailbox::GenerateForSharedImage(); auto format = get_format(); gfx::Size size(256, 256); @@ -225,12 +261,7 @@ mailbox, format, surface_handle, size, color_space, surface_origin, alpha_type, usage, false /* is_thread_safe */); - // As long as either |chromium_image_ar30| or |chromium_image_ab30| is - // enabled, we can create a non-scanout SharedImage with format - // viz::ResourceFormat::{BGRA,RGBA}_1010102. - if ((format == viz::ResourceFormat::BGRA_1010102 || - format == viz::ResourceFormat::RGBA_1010102) && - !supports_ar30_ && !supports_ab30_) { + if (!should_succeed) { EXPECT_FALSE(backing); return; } @@ -347,6 +378,11 @@ bot_config.Matches("mac passthrough")) { return; } + + const bool should_succeed = + can_create_scanout_or_gmb_shared_image(get_format()); + if (should_succeed) + EXPECT_CALL(progress_reporter_, ReportProgress).Times(AtLeast(1)); auto mailbox = Mailbox::GenerateForSharedImage(); auto format = get_format(); gfx::Size size(256, 256); @@ -359,15 +395,12 @@ mailbox, format, surface_handle, size, color_space, surface_origin, alpha_type, usage, false /* is_thread_safe */); - // We can only create a scanout SharedImage with format - // viz::ResourceFormat::{BGRA,RGBA}_1010102 if the corresponding - // |chromium_image_ar30| or |chromium_image_ab30| is enabled. - if ((format == viz::ResourceFormat::BGRA_1010102 && !supports_ar30_) || - (format == viz::ResourceFormat::RGBA_1010102 && !supports_ab30_)) { + if (!should_succeed) { EXPECT_FALSE(backing); return; } ASSERT_TRUE(backing); + ::testing::Mock::VerifyAndClearExpectations(&progress_reporter_); // Check clearing. if (!backing->IsCleared()) { @@ -472,6 +505,7 @@ if (!use_passthrough() && context_state_->feature_info()->feature_flags().ext_texture_rg) { + EXPECT_CALL(progress_reporter_, ReportProgress).Times(AtLeast(1)); // Create a R-8 image texture, and check that the internal_format is that // of the image (GL_RGBA for TextureImageFactory). This only matters for // the validating decoder. @@ -503,6 +537,9 @@ for (auto format : {viz::ResourceFormat::RGBA_8888, viz::ResourceFormat::ETC1, viz::ResourceFormat::BGRA_1010102, viz::ResourceFormat::RGBA_1010102}) { + const bool should_succeed = can_create_non_scanout_shared_image(format); + if (should_succeed) + EXPECT_CALL(progress_reporter_, ReportProgress).Times(AtLeast(1)); auto mailbox = Mailbox::GenerateForSharedImage(); gfx::Size size(256, 256); auto color_space = gfx::ColorSpace::CreateSRGB(); @@ -514,22 +551,11 @@ auto backing = backing_factory_->CreateSharedImage( mailbox, format, size, color_space, surface_origin, alpha_type, usage, initial_data); - - if (format == viz::ResourceFormat::ETC1 && !supports_etc1_) { + ::testing::Mock::VerifyAndClearExpectations(&progress_reporter_); + if (!should_succeed) { EXPECT_FALSE(backing); continue; } - - // As long as either |chromium_image_ar30| or |chromium_image_ab30| is - // enabled, we can create a non-scanout SharedImage with format - // viz::ResourceFormat::{BGRA,RGBA}_1010102. - if ((format == viz::ResourceFormat::BGRA_1010102 || - format == viz::ResourceFormat::RGBA_1010102) && - !supports_ar30_ && !supports_ab30_) { - EXPECT_FALSE(backing); - continue; - } - ASSERT_TRUE(backing); EXPECT_TRUE(backing->IsCleared()); @@ -571,6 +597,10 @@ } TEST_P(SharedImageBackingFactoryGLTextureTest, InitialDataImage) { + const bool should_succeed = + can_create_scanout_or_gmb_shared_image(get_format()); + if (should_succeed) + EXPECT_CALL(progress_reporter_, ReportProgress).Times(AtLeast(1)); auto mailbox = Mailbox::GenerateForSharedImage(); auto format = get_format(); gfx::Size size(256, 256); @@ -582,12 +612,7 @@ auto backing = backing_factory_->CreateSharedImage( mailbox, format, size, color_space, surface_origin, alpha_type, usage, initial_data); - - // We can only create a scanout SharedImage with format - // viz::ResourceFormat::{BGRA,RGBA}_1010102 if the corresponding - // |chromium_image_ar30| or |chromium_image_ab30| is enabled. - if ((format == viz::ResourceFormat::BGRA_1010102 && !supports_ar30_) || - (format == viz::ResourceFormat::RGBA_1010102 && !supports_ab30_)) { + if (!should_succeed) { EXPECT_FALSE(backing); return; } @@ -679,6 +704,9 @@ } TEST_P(SharedImageBackingFactoryGLTextureTest, EstimatedSize) { + const bool should_succeed = can_create_non_scanout_shared_image(get_format()); + if (should_succeed) + EXPECT_CALL(progress_reporter_, ReportProgress).Times(AtLeast(1)); auto mailbox = Mailbox::GenerateForSharedImage(); auto format = get_format(); gfx::Size size(256, 256); @@ -691,12 +719,7 @@ mailbox, format, surface_handle, size, color_space, surface_origin, alpha_type, usage, false /* is_thread_safe */); - // As long as either |chromium_image_ar30| or |chromium_image_ab30| is - // enabled, we can create a non-scanout SharedImage with format - // viz::ResourceFormat::{BGRA,RGBA}_1010102. - if ((format == viz::ResourceFormat::BGRA_1010102 || - format == viz::ResourceFormat::RGBA_1010102) && - !supports_ar30_ && !supports_ab30_) { + if (!should_succeed) { EXPECT_FALSE(backing); return; } @@ -891,12 +914,7 @@ auto backing = backing_factory_->CreateSharedImage( mailbox, kClientId, std::move(handle), format, kNullSurfaceHandle, size, color_space, surface_origin, alpha_type, usage); - - // We can only create a GMB SharedImage with format - // viz::ResourceFormat::{BGRA,RGBA}_1010102 if the corresponding - // |chromium_image_ar30| or |chromium_image_ab30| is enabled. - if ((get_format() == viz::ResourceFormat::BGRA_1010102 && !supports_ar30_) || - (get_format() == viz::ResourceFormat::RGBA_1010102 && !supports_ab30_)) { + if (!can_create_scanout_or_gmb_shared_image(get_format())) { EXPECT_FALSE(backing); return; } @@ -952,12 +970,7 @@ auto backing = backing_factory_->CreateSharedImage( mailbox, kClientId, std::move(handle), format, kNullSurfaceHandle, size, color_space, surface_origin, alpha_type, usage); - - // We can only create a GMB SharedImage with format - // viz::ResourceFormat::{BGRA,RGBA}_1010102 if the corresponding - // |chromium_image_ar30| or |chromium_image_ab30| is enabled. - if ((get_format() == viz::ResourceFormat::BGRA_1010102 && !supports_ar30_) || - (get_format() == viz::ResourceFormat::RGBA_1010102 && !supports_ab30_)) { + if (!can_create_scanout_or_gmb_shared_image(get_format())) { EXPECT_FALSE(backing); return; } @@ -990,12 +1003,7 @@ auto backing = backing_factory_->CreateSharedImage( mailbox, kClientId, std::move(handle), format, kNullSurfaceHandle, size, color_space, surface_origin, alpha_type, usage); - - // We can only create a GMB SharedImage with format - // viz::ResourceFormat::{BGRA,RGBA}_1010102 if the corresponding - // |chromium_image_ar30| or |chromium_image_ab30| is enabled. - if ((get_format() == viz::ResourceFormat::BGRA_1010102 && !supports_ar30_) || - (get_format() == viz::ResourceFormat::RGBA_1010102 && !supports_ab30_)) { + if (!can_create_scanout_or_gmb_shared_image(get_format())) { EXPECT_FALSE(backing); return; }
diff --git a/gpu/command_buffer/service/shared_image_backing_factory_iosurface_unittest.cc b/gpu/command_buffer/service/shared_image_backing_factory_iosurface_unittest.cc index 5bf507c..8fc0c73f8 100644 --- a/gpu/command_buffer/service/shared_image_backing_factory_iosurface_unittest.cc +++ b/gpu/command_buffer/service/shared_image_backing_factory_iosurface_unittest.cc
@@ -67,7 +67,8 @@ backing_factory_ = std::make_unique<SharedImageBackingFactoryGLTexture>( preferences, workarounds, GpuFeatureInfo(), &image_factory_, - shared_image_manager_.batch_access_manager()); + shared_image_manager_.batch_access_manager(), + /*progress_reporter=*/nullptr); memory_type_tracker_ = std::make_unique<MemoryTypeTracker>(nullptr); shared_image_representation_factory_ =
diff --git a/gpu/command_buffer/service/shared_image_backing_gl_texture.cc b/gpu/command_buffer/service/shared_image_backing_gl_texture.cc index 308d6d7..354d494 100644 --- a/gpu/command_buffer/service/shared_image_backing_gl_texture.cc +++ b/gpu/command_buffer/service/shared_image_backing_gl_texture.cc
@@ -44,6 +44,7 @@ #include "ui/gl/gl_image_shared_memory.h" #include "ui/gl/gl_implementation.h" #include "ui/gl/gl_version_info.h" +#include "ui/gl/progress_reporter.h" #include "ui/gl/scoped_binders.h" #include "ui/gl/shared_gl_fence_egl.h" #include "ui/gl/trace_util.h" @@ -75,11 +76,13 @@ const GpuDriverBugWorkarounds& workarounds, const GpuFeatureInfo& gpu_feature_info, ImageFactory* image_factory, - SharedImageBatchAccessManager* batch_access_manager) + SharedImageBatchAccessManager* batch_access_manager, + gl::ProgressReporter* progress_reporter) : use_passthrough_(gpu_preferences.use_passthrough_cmd_decoder && gles2::PassthroughCommandDecoderSupported()), image_factory_(image_factory), - workarounds_(workarounds) { + workarounds_(workarounds), + progress_reporter_(progress_reporter) { #if defined(OS_ANDROID) batch_access_manager_ = batch_access_manager; #endif @@ -505,13 +508,22 @@ // the internal format in the LevelInfo. https://crbug.com/628064 GLuint level_info_internal_format = format_info.gl_format; bool is_cleared = false; + + // |scoped_progress_reporter| will notify |progress_reporter_| upon + // construction and destruction. We limit the scope so that progress is + // reported immediately after allocation/upload and before other GL + // operations. if (use_buffer) { - image = image_factory_->CreateAnonymousImage( - size, format_info.buffer_format, gfx::BufferUsage::SCANOUT, - surface_handle, &is_cleared); + { + gl::ScopedProgressReporter scoped_progress_reporter(progress_reporter_); + image = image_factory_->CreateAnonymousImage( + size, format_info.buffer_format, gfx::BufferUsage::SCANOUT, + surface_handle, &is_cleared); + } // Scanout images have different constraints than GL images and might fail // to allocate even if GL images can be created. if (!image) { + gl::ScopedProgressReporter scoped_progress_reporter(progress_reporter_); // TODO(dcastagna): Use BufferUsage::GPU_READ_WRITE instead // BufferUsage::GPU_READ once we add it. image = image_factory_->CreateAnonymousImage( @@ -546,6 +558,7 @@ image, mailbox, format, size, color_space, surface_origin, alpha_type, usage, params, attribs, use_passthrough_); if (!pixel_data.empty()) { + gl::ScopedProgressReporter scoped_progress_reporter(progress_reporter_); result->InitializePixels(format_info.adjusted_format, format_info.gl_type, pixel_data.data()); } @@ -561,12 +574,16 @@ api->glBindTextureFn(target, result->GetGLServiceId()); if (format_info.supports_storage) { - api->glTexStorage2DEXTFn(target, 1, format_info.storage_internal_format, - size.width(), size.height()); + { + gl::ScopedProgressReporter scoped_progress_reporter(progress_reporter_); + api->glTexStorage2DEXTFn(target, 1, format_info.storage_internal_format, + size.width(), size.height()); + } if (!pixel_data.empty()) { ScopedResetAndRestoreUnpackState scoped_unpack_state( api, attribs, true /* uploading_data */); + gl::ScopedProgressReporter scoped_progress_reporter(progress_reporter_); api->glTexSubImage2DFn(target, 0, 0, 0, size.width(), size.height(), format_info.adjusted_format, format_info.gl_type, pixel_data.data()); @@ -574,12 +591,14 @@ } else if (format_info.is_compressed) { ScopedResetAndRestoreUnpackState scoped_unpack_state(api, attribs, !pixel_data.empty()); + gl::ScopedProgressReporter scoped_progress_reporter(progress_reporter_); api->glCompressedTexImage2DFn( target, 0, format_info.image_internal_format, size.width(), size.height(), 0, pixel_data.size(), pixel_data.data()); } else { ScopedResetAndRestoreUnpackState scoped_unpack_state(api, attribs, !pixel_data.empty()); + gl::ScopedProgressReporter scoped_progress_reporter(progress_reporter_); api->glTexImage2DFn(target, 0, format_info.image_internal_format, size.width(), size.height(), 0, format_info.adjusted_format, format_info.gl_type,
diff --git a/gpu/command_buffer/service/shared_image_factory.cc b/gpu/command_buffer/service/shared_image_factory.cc index d8e3bb5..c671ee2f 100644 --- a/gpu/command_buffer/service/shared_image_factory.cc +++ b/gpu/command_buffer/service/shared_image_factory.cc
@@ -100,7 +100,9 @@ if (use_gl) { gl_backing_factory_ = std::make_unique<SharedImageBackingFactoryGLTexture>( gpu_preferences, workarounds, gpu_feature_info, image_factory, - shared_image_manager->batch_access_manager()); + shared_image_manager->batch_access_manager(), + shared_context_state_ ? shared_context_state_->progress_reporter() + : nullptr); } // TODO(ccameron): This block of code should be changed to a switch on
diff --git a/gpu/config/BUILD.gn b/gpu/config/BUILD.gn index 569657ae..134306e 100644 --- a/gpu/config/BUILD.gn +++ b/gpu/config/BUILD.gn
@@ -245,9 +245,19 @@ template = "android/java/src/org/chromium/gpu/config/GpuSwitches.java.tmpl" } + java_cpp_features("java_features_srcjar") { + # External code should depend on ":config_java" instead. + visibility = [ ":*" ] + sources = [ "gpu_finch_features.cc" ] + template = "android/java/src/org/chromium/gpu/config/GpuFeatures.java.tmpl" + } + android_library("config_java") { - # Right now, this only includes the Java switches. But if we need more Java - # files, they should be added here as necessary. - srcjar_deps = [ ":java_switches_srcjar" ] + # Right now, this only includes the Java switches/features. But if we need + # more Java files, they should be added here as necessary. + srcjar_deps = [ + ":java_features_srcjar", + ":java_switches_srcjar", + ] } }
diff --git a/gpu/config/android/java/src/org/chromium/gpu/config/GpuFeatures.java.tmpl b/gpu/config/android/java/src/org/chromium/gpu/config/GpuFeatures.java.tmpl new file mode 100644 index 0000000..9ef9708 --- /dev/null +++ b/gpu/config/android/java/src/org/chromium/gpu/config/GpuFeatures.java.tmpl
@@ -0,0 +1,16 @@ +// Copyright 2020 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.gpu.config; + +/** + * Constants for the names of GPU Features. + */ +public final class GpuFeatures {{ + +{NATIVE_FEATURES} + + // Prevent instantiation. + private GpuFeatures() {{}} +}}
diff --git a/gpu/config/gpu_finch_features.cc b/gpu/config/gpu_finch_features.cc index b81cf575..dd4d77b 100644 --- a/gpu/config/gpu_finch_features.cc +++ b/gpu/config/gpu_finch_features.cc
@@ -31,16 +31,17 @@ // Enable GPU Rasterization by default. This can still be overridden by // --force-gpu-rasterization or --disable-gpu-rasterization. -#if defined(OS_MAC) || defined(OS_WIN) || defined(OS_CHROMEOS) || \ - defined(OS_ANDROID) || defined(OS_FUCHSIA) // DefaultEnableGpuRasterization has launched on Mac, Windows, ChromeOS, and // Android. const base::Feature kDefaultEnableGpuRasterization{ - "DefaultEnableGpuRasterization", base::FEATURE_ENABLED_BY_DEFAULT}; + "DefaultEnableGpuRasterization", +#if defined(OS_MAC) || defined(OS_WIN) || defined(OS_CHROMEOS) || \ + defined(OS_ANDROID) || defined(OS_FUCHSIA) + base::FEATURE_ENABLED_BY_DEFAULT #else -const base::Feature kDefaultEnableGpuRasterization{ - "DefaultEnableGpuRasterization", base::FEATURE_DISABLED_BY_DEFAULT}; + base::FEATURE_DISABLED_BY_DEFAULT #endif +}; // Enable out of process rasterization by default. This can still be overridden // by --disable-oop-rasterization. @@ -51,16 +52,22 @@ // Use a high priority for GPU process on Windows. const base::Feature kGpuProcessHighPriorityWin{ "GpuProcessHighPriorityWin", base::FEATURE_ENABLED_BY_DEFAULT}; + +// Compute the root damage rect from the surface damage list for overlays on +// Windows. +const base::Feature kDirectCompositionUseOverlayDamageList{ + "DirectCompositionUseOverlayDamageList", base::FEATURE_ENABLED_BY_DEFAULT}; #endif // Use ThreadPriority::DISPLAY for GPU main, viz compositor and IO threads. +const base::Feature kGpuUseDisplayThreadPriority{ + "GpuUseDisplayThreadPriority", #if defined(OS_ANDROID) || defined(OS_CHROMEOS) || defined(OS_WIN) -const base::Feature kGpuUseDisplayThreadPriority{ - "GpuUseDisplayThreadPriority", base::FEATURE_ENABLED_BY_DEFAULT}; + base::FEATURE_ENABLED_BY_DEFAULT #else -const base::Feature kGpuUseDisplayThreadPriority{ - "GpuUseDisplayThreadPriority", base::FEATURE_DISABLED_BY_DEFAULT}; + base::FEATURE_DISABLED_BY_DEFAULT #endif +}; // Gpu watchdog V2 to simplify the logic and reduce GPU hangs const base::Feature kGpuWatchdogV2{"GpuWatchdogV2",
diff --git a/gpu/config/gpu_finch_features.h b/gpu/config/gpu_finch_features.h index 1fac460..e8a4f367 100644 --- a/gpu/config/gpu_finch_features.h +++ b/gpu/config/gpu_finch_features.h
@@ -28,6 +28,8 @@ #if defined(OS_WIN) GPU_EXPORT extern const base::Feature kGpuProcessHighPriorityWin; + +GPU_EXPORT extern const base::Feature kDirectCompositionUseOverlayDamageList; #endif GPU_EXPORT extern const base::Feature kGpuUseDisplayThreadPriority;
diff --git a/ios/chrome/app/main_controller.mm b/ios/chrome/app/main_controller.mm index 5ef4941..ca2f6c3 100644 --- a/ios/chrome/app/main_controller.mm +++ b/ios/chrome/app/main_controller.mm
@@ -138,6 +138,9 @@ // Constants for deferred deletion of leftover temporary passwords files. NSString* const kDeleteTempPasswords = @"DeleteTempPasswords"; +// Constants for deferred UMA logging of existing Siri User shortcuts. +NSString* const kLogSiriShortcuts = @"LogSiriShortcuts"; + // Constants for deferred sending of queued feedback. NSString* const kSendQueuedFeedback = @"SendQueuedFeedback"; @@ -984,6 +987,7 @@ [self scheduleSpotlightResync]; [self scheduleDeleteTempDownloadsDirectory]; [self scheduleDeleteTempPasswordsDirectory]; + [self scheduleLogSiriShortcuts]; [self scheduleStartupAttemptReset]; [self startFreeMemoryMonitoring]; [self scheduleAppDistributionPings]; @@ -1020,6 +1024,15 @@ }]; } +- (void)scheduleLogSiriShortcuts { + __weak StartupTasks* startupTasks = _startupTasks; + [[DeferredInitializationRunner sharedInstance] + enqueueBlockNamed:kLogSiriShortcuts + block:^{ + [startupTasks logSiriShortcuts]; + }]; +} + - (void)scheduleSpotlightResync { if (!_spotlightManager) { return;
diff --git a/ios/chrome/app/startup_tasks.h b/ios/chrome/app/startup_tasks.h index 2664398..c4146d63 100644 --- a/ios/chrome/app/startup_tasks.h +++ b/ios/chrome/app/startup_tasks.h
@@ -20,6 +20,8 @@ - (void)initializeOmaha; // Registers to receive UIApplicationWillResignActiveNotification. - (void)registerForApplicationWillResignActiveNotification; +// Logs the number of Chrome Siri Shortcuts to UMA. +- (void)logSiriShortcuts; @end
diff --git a/ios/chrome/app/startup_tasks.mm b/ios/chrome/app/startup_tasks.mm index 7e030fe0..c5899e80 100644 --- a/ios/chrome/app/startup_tasks.mm +++ b/ios/chrome/app/startup_tasks.mm
@@ -7,6 +7,8 @@ #import <MediaPlayer/MediaPlayer.h> #include "base/bind.h" +#include "base/metrics/histogram_functions.h" +#include "base/task/post_task.h" #import "ios/chrome/app/deferred_initialization_runner.h" #include "ios/chrome/app/intents/SearchInChromeIntent.h" #include "ios/chrome/browser/application_context.h" @@ -77,6 +79,26 @@ object:nil]; } +- (void)logSiriShortcuts { + base::ThreadPool::PostTask( + FROM_HERE, {base::MayBlock(), base::TaskPriority::BEST_EFFORT}, + base::BindOnce(^{ + [[INVoiceShortcutCenter sharedCenter] + getAllVoiceShortcutsWithCompletion:^( + NSArray<INVoiceShortcut*>* voiceShortcuts, NSError* error) { + if (error || !voiceShortcuts) { + return; + } + + // The 20 shortcuts cap is arbitrary but seems like a reasonable + // limit. + base::UmaHistogramExactLinear( + "IOS.SiriShortcuts.Count", + base::saturated_cast<int>([voiceShortcuts count]), 20); + }]; + })); +} + #pragma mark - Private methods. + (void)performDeferredInitializationForBrowserState:
diff --git a/ios/chrome/browser/metrics/BUILD.gn b/ios/chrome/browser/metrics/BUILD.gn index bf8347f..1d45088 100644 --- a/ios/chrome/browser/metrics/BUILD.gn +++ b/ios/chrome/browser/metrics/BUILD.gn
@@ -282,6 +282,123 @@ import("//components/metrics/generate_expired_histograms_array.gni") generate_expired_histograms_array("expired_histograms_array") { + inputs = [ + "//tools/metrics/histograms/histograms.xml", + "//tools/metrics/histograms/histograms_xml/accessibility/histograms.xml", + "//tools/metrics/histograms/histograms_xml/android/histograms.xml", + "//tools/metrics/histograms/histograms_xml/apps/histograms.xml", + "//tools/metrics/histograms/histograms_xml/arc/histograms.xml", + "//tools/metrics/histograms/histograms_xml/ash/histograms.xml", + "//tools/metrics/histograms/histograms_xml/assistant/histograms.xml", + "//tools/metrics/histograms/histograms_xml/auth/histograms.xml", + "//tools/metrics/histograms/histograms_xml/auto/histograms.xml", + "//tools/metrics/histograms/histograms_xml/autofill/histograms.xml", + "//tools/metrics/histograms/histograms_xml/back_forward_cache/histograms.xml", + "//tools/metrics/histograms/histograms_xml/background/histograms.xml", + "//tools/metrics/histograms/histograms_xml/blink/histograms.xml", + "//tools/metrics/histograms/histograms_xml/bluetooth/histograms.xml", + "//tools/metrics/histograms/histograms_xml/browser/histograms.xml", + "//tools/metrics/histograms/histograms_xml/chrome/histograms.xml", + "//tools/metrics/histograms/histograms_xml/cloud/histograms.xml", + "//tools/metrics/histograms/histograms_xml/compositing/histograms.xml", + "//tools/metrics/histograms/histograms_xml/content/histograms.xml", + "//tools/metrics/histograms/histograms_xml/cookie/histograms.xml", + "//tools/metrics/histograms/histograms_xml/cras/histograms.xml", + "//tools/metrics/histograms/histograms_xml/cros/histograms.xml", + "//tools/metrics/histograms/histograms_xml/crostini/histograms.xml", + "//tools/metrics/histograms/histograms_xml/crypt/histograms.xml", + "//tools/metrics/histograms/histograms_xml/cryptohome/histograms.xml", + "//tools/metrics/histograms/histograms_xml/custom_tabs/histograms.xml", + "//tools/metrics/histograms/histograms_xml/data_reduction_proxy/histograms.xml", + "//tools/metrics/histograms/histograms_xml/dev/histograms.xml", + "//tools/metrics/histograms/histograms_xml/diagnostics/histograms.xml", + "//tools/metrics/histograms/histograms_xml/direct/histograms.xml", + "//tools/metrics/histograms/histograms_xml/disk/histograms.xml", + "//tools/metrics/histograms/histograms_xml/dom/histograms.xml", + "//tools/metrics/histograms/histograms_xml/download/histograms.xml", + "//tools/metrics/histograms/histograms_xml/enterprise/histograms.xml", + "//tools/metrics/histograms/histograms_xml/event/histograms.xml", + "//tools/metrics/histograms/histograms_xml/extension/histograms.xml", + "//tools/metrics/histograms/histograms_xml/extensions/histograms.xml", + "//tools/metrics/histograms/histograms_xml/file/histograms.xml", + "//tools/metrics/histograms/histograms_xml/fingerprint/histograms.xml", + "//tools/metrics/histograms/histograms_xml/gcm/histograms.xml", + "//tools/metrics/histograms/histograms_xml/geolocation/histograms.xml", + "//tools/metrics/histograms/histograms_xml/google/histograms.xml", + "//tools/metrics/histograms/histograms_xml/gpu/histograms.xml", + "//tools/metrics/histograms/histograms_xml/histogram_suffixes_list.xml", + "//tools/metrics/histograms/histograms_xml/history/histograms.xml", + "//tools/metrics/histograms/histograms_xml/holding_space/histograms.xml", + "//tools/metrics/histograms/histograms_xml/image/histograms.xml", + "//tools/metrics/histograms/histograms_xml/input/histograms.xml", + "//tools/metrics/histograms/histograms_xml/installer/histograms.xml", + "//tools/metrics/histograms/histograms_xml/instant/histograms.xml", + "//tools/metrics/histograms/histograms_xml/interstitial/histograms.xml", + "//tools/metrics/histograms/histograms_xml/ios/histograms.xml", + "//tools/metrics/histograms/histograms_xml/local/histograms.xml", + "//tools/metrics/histograms/histograms_xml/login/histograms.xml", + "//tools/metrics/histograms/histograms_xml/media/histograms.xml", + "//tools/metrics/histograms/histograms_xml/memory/histograms.xml", + "//tools/metrics/histograms/histograms_xml/mobile/histograms.xml", + "//tools/metrics/histograms/histograms_xml/multi_device/histograms.xml", + "//tools/metrics/histograms/histograms_xml/na_cl/histograms.xml", + "//tools/metrics/histograms/histograms_xml/navigation/histograms.xml", + "//tools/metrics/histograms/histograms_xml/net/histograms.xml", + "//tools/metrics/histograms/histograms_xml/network/histograms.xml", + "//tools/metrics/histograms/histograms_xml/new_tab_page/histograms.xml", + "//tools/metrics/histograms/histograms_xml/notifications/histograms.xml", + "//tools/metrics/histograms/histograms_xml/offline/histograms.xml", + "//tools/metrics/histograms/histograms_xml/omnibox/histograms.xml", + "//tools/metrics/histograms/histograms_xml/oobe/histograms.xml", + "//tools/metrics/histograms/histograms_xml/optimization/histograms.xml", + "//tools/metrics/histograms/histograms_xml/others/histograms.xml", + "//tools/metrics/histograms/histograms_xml/page/histograms.xml", + "//tools/metrics/histograms/histograms_xml/password/histograms.xml", + "//tools/metrics/histograms/histograms_xml/payment/histograms.xml", + "//tools/metrics/histograms/histograms_xml/permissions/histograms.xml", + "//tools/metrics/histograms/histograms_xml/platform/histograms.xml", + "//tools/metrics/histograms/histograms_xml/plugin/histograms.xml", + "//tools/metrics/histograms/histograms_xml/power/histograms.xml", + "//tools/metrics/histograms/histograms_xml/print/histograms.xml", + "//tools/metrics/histograms/histograms_xml/printing/histograms.xml", + "//tools/metrics/histograms/histograms_xml/profile/histograms.xml", + "//tools/metrics/histograms/histograms_xml/quickoffice/histograms.xml", + "//tools/metrics/histograms/histograms_xml/quota/histograms.xml", + "//tools/metrics/histograms/histograms_xml/renderer/histograms.xml", + "//tools/metrics/histograms/histograms_xml/renderer4/histograms.xml", + "//tools/metrics/histograms/histograms_xml/safe_browsing/histograms.xml", + "//tools/metrics/histograms/histograms_xml/sb_client/histograms.xml", + "//tools/metrics/histograms/histograms_xml/search/histograms.xml", + "//tools/metrics/histograms/histograms_xml/security/histograms.xml", + "//tools/metrics/histograms/histograms_xml/service/histograms.xml", + "//tools/metrics/histograms/histograms_xml/session/histograms.xml", + "//tools/metrics/histograms/histograms_xml/settings/histograms.xml", + "//tools/metrics/histograms/histograms_xml/sharing/histograms.xml", + "//tools/metrics/histograms/histograms_xml/signin/histograms.xml", + "//tools/metrics/histograms/histograms_xml/simple/histograms.xml", + "//tools/metrics/histograms/histograms_xml/smart/histograms.xml", + "//tools/metrics/histograms/histograms_xml/software/histograms.xml", + "//tools/metrics/histograms/histograms_xml/stability/histograms.xml", + "//tools/metrics/histograms/histograms_xml/startup/histograms.xml", + "//tools/metrics/histograms/histograms_xml/storage/histograms.xml", + "//tools/metrics/histograms/histograms_xml/subresource/histograms.xml", + "//tools/metrics/histograms/histograms_xml/sync/histograms.xml", + "//tools/metrics/histograms/histograms_xml/tab/histograms.xml", + "//tools/metrics/histograms/histograms_xml/translate/histograms.xml", + "//tools/metrics/histograms/histograms_xml/ukm/histograms.xml", + "//tools/metrics/histograms/histograms_xml/uma/histograms.xml", + "//tools/metrics/histograms/histograms_xml/update_engine/histograms.xml", + "//tools/metrics/histograms/histograms_xml/v8/histograms.xml", + "//tools/metrics/histograms/histograms_xml/variations/histograms.xml", + "//tools/metrics/histograms/histograms_xml/video_tutorials/histograms.xml", + "//tools/metrics/histograms/histograms_xml/web_apk/histograms.xml", + "//tools/metrics/histograms/histograms_xml/web_audio/histograms.xml", + "//tools/metrics/histograms/histograms_xml/web_core/histograms.xml", + "//tools/metrics/histograms/histograms_xml/web_rtc/histograms.xml", + "//tools/metrics/histograms/histograms_xml/windows/histograms.xml", + "//tools/metrics/histograms/histograms_xml/obsolete_histograms.xml", + "//tools/metrics/histograms/enums.xml", + ] namespace = "" header_filename = "ios_expired_histograms_array.h" major_branch_date_filepath = "//chrome/MAJOR_BRANCH_DATE"
diff --git a/ios/chrome/browser/ui/tab_grid/grid/grid_view_controller_unittest.mm b/ios/chrome/browser/ui/tab_grid/grid/grid_view_controller_unittest.mm index cc66ddd..9049b28 100644 --- a/ios/chrome/browser/ui/tab_grid/grid/grid_view_controller_unittest.mm +++ b/ios/chrome/browser/ui/tab_grid/grid/grid_view_controller_unittest.mm
@@ -183,7 +183,7 @@ // Tests that |-replaceItemID:withItem:| does not crash when updating an item // that is scrolled offscreen. -TEST_F(GridViewControllerTest, ReplaceScrolledOffScreenCell) { +TEST_F(GridViewControllerTest, DISABLED_ReplaceScrolledOffScreenCell) { // TODO(crbug.com/1104872): On iOS 14 iPhone X, visibleCellsCount is always // equal to the total number of cells, so the while loop below never // terminates.
diff --git a/media/capture/video/video_capture_device_unittest.cc b/media/capture/video/video_capture_device_unittest.cc index a878a553..38f5617 100644 --- a/media/capture/video/video_capture_device_unittest.cc +++ b/media/capture/video/video_capture_device_unittest.cc
@@ -55,7 +55,7 @@ #include "media/capture/video/chromeos/public/cros_features.h" #include "media/capture/video/chromeos/video_capture_device_chromeos_halv3.h" #include "media/capture/video/chromeos/video_capture_device_factory_chromeos.h" -#include "media/gpu/test/local_gpu_memory_buffer_manager.h" +#include "media/gpu/test/local_gpu_memory_buffer_manager.h" // nogncheck #include "mojo/public/cpp/bindings/pending_receiver.h" #endif
diff --git a/media/gpu/test/BUILD.gn b/media/gpu/test/BUILD.gn index 92f16e16..675e565 100644 --- a/media/gpu/test/BUILD.gn +++ b/media/gpu/test/BUILD.gn
@@ -214,7 +214,7 @@ } } -if (is_chromeos || use_vaapi) { +if (is_chromeos) { static_library("local_gpu_memory_buffer_manager") { testonly = true sources = [
diff --git a/media/gpu/v4l2/BUILD.gn b/media/gpu/v4l2/BUILD.gn index 1307adc..9a999e4 100644 --- a/media/gpu/v4l2/BUILD.gn +++ b/media/gpu/v4l2/BUILD.gn
@@ -23,6 +23,8 @@ source_set("v4l2") { defines = [ "MEDIA_GPU_IMPLEMENTATION" ] sources = [ + "buffer_affinity_tracker.cc", + "buffer_affinity_tracker.h", "generic_v4l2_device.cc", "generic_v4l2_device.h", "v4l2_decode_surface.cc",
diff --git a/media/gpu/v4l2/buffer_affinity_tracker.cc b/media/gpu/v4l2/buffer_affinity_tracker.cc new file mode 100644 index 0000000..2e1524b --- /dev/null +++ b/media/gpu/v4l2/buffer_affinity_tracker.cc
@@ -0,0 +1,50 @@ +// Copyright 2020 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 "media/gpu/v4l2/buffer_affinity_tracker.h" +#include "base/synchronization/lock.h" +#include "media/gpu/macros.h" +#include "ui/gfx/generic_shared_memory_id.h" + +namespace media { + +BufferAffinityTracker::BufferAffinityTracker(size_t nb_buffers) { + resize(0); +} + +void BufferAffinityTracker::resize(size_t nb_buffers) { + base::AutoLock lock(lock_); + + id_to_buffer_map_.clear(); + nb_buffers_ = nb_buffers; + DVLOGF(4) << this << " resize: " << nb_buffers; +} + +base::Optional<size_t> BufferAffinityTracker::get_buffer_for_id( + gfx::GenericSharedMemoryId id) { + base::AutoLock lock(lock_); + + auto it = id_to_buffer_map_.find(id); + // If the handle is already bound to a buffer, return it. + if (it != id_to_buffer_map_.end()) { + DVLOGF(4) << this << " match for " << it->second; + return it->second; + } + + // Try to assign a new buffer for this handle... + + // No buffer available? No luck then. + if (id_to_buffer_map_.size() == nb_buffers()) { + DVLOGF(4) << this << " tracker is full!"; + return base::nullopt; + } + + const size_t v4l2_id = id_to_buffer_map_.size(); + id_to_buffer_map_.emplace(id, v4l2_id); + + DVLOGF(4) << this << " add " << v4l2_id; + return v4l2_id; +} + +} // namespace media \ No newline at end of file
diff --git a/media/gpu/v4l2/buffer_affinity_tracker.h b/media/gpu/v4l2/buffer_affinity_tracker.h new file mode 100644 index 0000000..3b3c572 --- /dev/null +++ b/media/gpu/v4l2/buffer_affinity_tracker.h
@@ -0,0 +1,55 @@ +// Copyright 2020 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 MEDIA_GPU_V4L2_V4L2_BUFFER_AFFINITY_TRACKER_H_ +#define MEDIA_GPU_V4L2_V4L2_BUFFER_AFFINITY_TRACKER_H_ + +#include <cstddef> +#include <map> + +#include "base/optional.h" +#include "base/synchronization/lock.h" +#include "ui/gfx/generic_shared_memory_id.h" + +namespace media { + +/** + * Maintains affinity between native handles and V4L2 buffers. + * + * Give a handle ID, `get_buffer_for_id()` will attempt to always return the + * same V4L2 buffer ID so handles are always used with the same buffer. This + * is both beneficial for performance, and necessary in some cases like the + * stateful decoder. + * + * All the methods of this class are thread-safe. + */ +class BufferAffinityTracker { + public: + explicit BufferAffinityTracker(size_t nb_buffers); + size_t nb_buffers() const { return nb_buffers_; } + // Resize this tracker and reset its state. + void resize(size_t nb_buffers); + + /** + * Return the V4L2 buffer index suitable for this buffer ID. + * + * If it is the first time this method is called with a given id, return the + * first available buffer it can find, and memorize the association between + * the id and the V4L2 buffer. + * + * On subsequent calls with the same id, that same V4L2 buffer will be + * returned. + */ + base::Optional<size_t> get_buffer_for_id(gfx::GenericSharedMemoryId id); + + private: + base::Lock lock_; + std::map<gfx::GenericSharedMemoryId, size_t> id_to_buffer_map_; + // Maximum number of buffers we are allowed to track. + size_t nb_buffers_; +}; + +} // namespace media + +#endif // MEDIA_GPU_V4L2_V4L2_BUFFER_AFFINITY_TRACKER_H_
diff --git a/media/gpu/v4l2/v4l2_device.cc b/media/gpu/v4l2/v4l2_device.cc index 7386a0bd..9dd1c87 100644 --- a/media/gpu/v4l2/v4l2_device.cc +++ b/media/gpu/v4l2/v4l2_device.cc
@@ -29,7 +29,9 @@ #include "media/gpu/chromeos/fourcc.h" #include "media/gpu/chromeos/platform_video_frame_utils.h" #include "media/gpu/macros.h" +#include "media/gpu/v4l2/buffer_affinity_tracker.h" #include "media/gpu/v4l2/generic_v4l2_device.h" +#include "ui/gfx/generic_shared_memory_id.h" #include "ui/gfx/native_pixmap_handle.h" #if defined(ARCH_CPU_ARMEL) @@ -908,6 +910,7 @@ enum v4l2_buf_type type, base::OnceClosure destroy_cb) : type_(type), + affinity_tracker_(0), device_(dev), destroy_cb_(std::move(destroy_cb)), weak_this_factory_(this) { @@ -1097,6 +1100,8 @@ free_buffers_->ReturnBuffer(i); } + affinity_tracker_.resize(buffers_.size()); + DCHECK(free_buffers_); DCHECK_EQ(free_buffers_->size(), buffers_.size()); DCHECK_EQ(queued_buffers_.size(), 0u); @@ -1117,6 +1122,7 @@ weak_this_factory_.InvalidateWeakPtrs(); buffers_.clear(); + affinity_tracker_.resize(0); free_buffers_ = nullptr; // Free all buffers. @@ -1184,6 +1190,37 @@ weak_this_factory_.GetWeakPtr()); } +base::Optional<V4L2WritableBufferRef> V4L2Queue::GetFreeBufferForFrame( + const VideoFrame& frame) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + // No buffers allocated at the moment? + if (!free_buffers_) + return base::nullopt; + + if (memory_ != V4L2_MEMORY_DMABUF) { + DVLOGF(1) << "Queue is not DMABUF"; + return base::nullopt; + } + + gfx::GenericSharedMemoryId id; + if (auto gmb = frame.GetGpuMemoryBuffer()) { + id = gmb->GetId(); + } else if (frame.HasDmaBufs()) { + id = gfx::GenericSharedMemoryId(frame.DmabufFds()[0].get()); + } else { + DVLOGF(1) << "Unsupported frame provided"; + return base::nullopt; + } + + const auto v4l2_id = affinity_tracker_.get_buffer_for_id(id); + if (!v4l2_id) { + return base::nullopt; + } + + return GetFreeBuffer(*v4l2_id); +} + bool V4L2Queue::QueueBuffer(struct v4l2_buffer* v4l2_buffer, scoped_refptr<VideoFrame> video_frame) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
diff --git a/media/gpu/v4l2/v4l2_device.h b/media/gpu/v4l2/v4l2_device.h index 30653ec..849d1cd 100644 --- a/media/gpu/v4l2/v4l2_device.h +++ b/media/gpu/v4l2/v4l2_device.h
@@ -28,6 +28,7 @@ #include "media/base/video_frame_layout.h" #include "media/gpu/chromeos/fourcc.h" #include "media/gpu/media_gpu_export.h" +#include "media/gpu/v4l2/buffer_affinity_tracker.h" #include "media/gpu/v4l2/v4l2_device_poller.h" #include "media/video/video_decode_accelerator.h" #include "media/video/video_encode_accelerator.h" @@ -374,6 +375,23 @@ // return |base::nullopt|. base::Optional<V4L2WritableBufferRef> GetFreeBuffer( size_t requested_buffer_id); + // Return a V4L2 buffer suitable for the passed VideoFrame. + // + // This method will try as much as possible to always return the same V4L2 + // buffer when the same frame is passed again, to avoid memory unmap + // operations in the kernel driver. + // + // The operating mode of the queue must be DMABUF, and the VideoFrame must + // be backed either by a GpuMemoryBuffer, or by DMABUFs. In the case of + // DMABUFs, this method will only work correctly if the same DMABUFs are + // passed with each call, i.e. no dup shall be performed. + // + // This should be the preferred way to obtain buffers when using DMABUF mode, + // since it will maximize performance in that case provided the number of + // different VideoFrames passed to this method does not exceed the number of + // V4L2 buffers allocated on the queue. + base::Optional<V4L2WritableBufferRef> GetFreeBufferForFrame( + const VideoFrame& frame); // Attempt to dequeue a buffer, and return a reference to it if one was // available. @@ -443,6 +461,9 @@ // value will be set to the VideoFrame that has been passed when we queued // the buffer, if any. std::map<size_t, scoped_refptr<VideoFrame>> queued_buffers_; + // Keep track of which buffer was assigned to which frame by + // |GetFreeBufferForFrame()| so we reuse the same buffer in subsequent calls. + BufferAffinityTracker affinity_tracker_; scoped_refptr<V4L2Device> device_; // Callback to call in this queue's destructor.
diff --git a/media/gpu/v4l2/v4l2_image_processor_backend.cc b/media/gpu/v4l2/v4l2_image_processor_backend.cc index 00b6119c..064b893 100644 --- a/media/gpu/v4l2/v4l2_image_processor_backend.cc +++ b/media/gpu/v4l2/v4l2_image_processor_backend.cc
@@ -96,13 +96,23 @@ } bool AllocateV4L2Buffers(V4L2Queue* queue, - size_t num_buffers, + const size_t num_buffers, v4l2_memory memory_type) { DCHECK(queue); - if (queue->AllocateBuffers(num_buffers, memory_type) == 0u) + + size_t requested_buffers = num_buffers; + + // If we are using DMABUFs, then we will try to keep using the same V4L2 + // buffer for a given input or output frame. In that case, allocate as many + // V4L2 buffers as we can to avoid running out of them. Unused buffers won't + // use backed memory and are thus virtually free. + if (memory_type == V4L2_MEMORY_DMABUF) + requested_buffers = VIDEO_MAX_FRAME; + + if (queue->AllocateBuffers(requested_buffers, memory_type) == 0u) return false; - if (queue->AllocatedBuffersCount() != num_buffers) { + if (queue->AllocatedBuffersCount() < num_buffers) { VLOGF(1) << "Failed to allocate buffers. Allocated number=" << queue->AllocatedBuffersCount() << ", Requested number=" << num_buffers; @@ -613,8 +623,26 @@ } // We need one input and one output buffer to schedule the job - auto input_buffer = input_queue_->GetFreeBuffer(); - auto output_buffer = output_queue_->GetFreeBuffer(); + base::Optional<V4L2WritableBufferRef> input_buffer; + // If we are using DMABUF frames, try to always obtain the same V4L2 buffer. + if (input_memory_type_ == V4L2_MEMORY_DMABUF) { + const VideoFrame& input_frame = + *(input_job_queue_.front()->input_frame.get()); + input_buffer = input_queue_->GetFreeBufferForFrame(input_frame); + } + if (!input_buffer) + input_buffer = input_queue_->GetFreeBuffer(); + + base::Optional<V4L2WritableBufferRef> output_buffer; + // If we are using DMABUF frames, try to always obtain the same V4L2 buffer. + if (output_memory_type_ == V4L2_MEMORY_DMABUF) { + const VideoFrame& output_frame = + *(input_job_queue_.front()->output_frame.get()); + output_buffer = output_queue_->GetFreeBufferForFrame(output_frame); + } + if (!output_buffer) + output_buffer = output_queue_->GetFreeBuffer(); + if (!input_buffer || !output_buffer) break;
diff --git a/media/gpu/vaapi/vaapi_wrapper.h b/media/gpu/vaapi/vaapi_wrapper.h index 6f3387cd..d67b497 100644 --- a/media/gpu/vaapi/vaapi_wrapper.h +++ b/media/gpu/vaapi/vaapi_wrapper.h
@@ -34,7 +34,7 @@ #include "ui/gfx/geometry/size.h" #if defined(USE_X11) -#include "ui/gfx/x/x11.h" +#include "ui/gfx/x/x11.h" // nogncheck #endif // USE_X11 namespace gfx {
diff --git a/net/quic/bidirectional_stream_quic_impl_unittest.cc b/net/quic/bidirectional_stream_quic_impl_unittest.cc index 9aabe53..8d0a5a20 100644 --- a/net/quic/bidirectional_stream_quic_impl_unittest.cc +++ b/net/quic/bidirectional_stream_quic_impl_unittest.cc
@@ -530,8 +530,8 @@ new QuicChromiumConnectionHelper(&clock_, &random_generator_)); alarm_factory_.reset(new QuicChromiumAlarmFactory(runner_.get(), &clock_)); connection_ = new quic::QuicConnection( - connection_id_, ToQuicSocketAddress(peer_addr_), helper_.get(), - alarm_factory_.get(), + connection_id_, quic::QuicSocketAddress(), + ToQuicSocketAddress(peer_addr_), helper_.get(), alarm_factory_.get(), new QuicChromiumPacketWriter(socket.get(), runner_.get()), true /* owns_writer */, quic::Perspective::IS_CLIENT, quic::test::SupportedVersions(version_));
diff --git a/net/quic/quic_chromium_client_session_test.cc b/net/quic/quic_chromium_client_session_test.cc index 17b0895..b0b9d11 100644 --- a/net/quic/quic_chromium_client_session_test.cc +++ b/net/quic/quic_chromium_client_session_test.cc
@@ -181,8 +181,8 @@ socket.get(), base::ThreadTaskRunnerHandle::Get().get()); quic::QuicConnection* connection = new quic::QuicConnection( quic::QuicUtils::CreateRandomConnectionId(&random_), - ToQuicSocketAddress(kIpEndPoint), &helper_, &alarm_factory_, writer, - true, quic::Perspective::IS_CLIENT, + quic::QuicSocketAddress(), ToQuicSocketAddress(kIpEndPoint), &helper_, + &alarm_factory_, writer, true, quic::Perspective::IS_CLIENT, quic::test::SupportedVersions(version_)); session_.reset(new TestingQuicChromiumClientSession( connection, std::move(socket),
diff --git a/net/quic/quic_connection_logger.cc b/net/quic/quic_connection_logger.cc index a8d3875..d74eb20 100644 --- a/net/quic/quic_connection_logger.cc +++ b/net/quic/quic_connection_logger.cc
@@ -290,8 +290,9 @@ // TODO(rtenneti): Add logging. } -void QuicConnectionLogger::OnPacketHeader( - const quic::QuicPacketHeader& header) { +void QuicConnectionLogger::OnPacketHeader(const quic::QuicPacketHeader& header, + quic::QuicTime receive_time, + quic::EncryptionLevel level) { if (!first_received_packet_number_.IsInitialized()) { first_received_packet_number_ = header.packet_number; } else if (header.packet_number < first_received_packet_number_) { @@ -338,7 +339,7 @@ no_packet_received_after_ping_ = false; } last_received_packet_number_ = header.packet_number; - event_logger_.OnPacketHeader(header); + event_logger_.OnPacketHeader(header, receive_time, level); } void QuicConnectionLogger::OnStreamFrame(const quic::QuicStreamFrame& frame) {
diff --git a/net/quic/quic_connection_logger.h b/net/quic/quic_connection_logger.h index 9c5e351..a9def71 100644 --- a/net/quic/quic_connection_logger.h +++ b/net/quic/quic_connection_logger.h
@@ -73,7 +73,9 @@ quic::EncryptionLevel decryption_level) override; void OnDuplicatePacket(quic::QuicPacketNumber packet_number) override; void OnProtocolVersionMismatch(quic::ParsedQuicVersion version) override; - void OnPacketHeader(const quic::QuicPacketHeader& header) override; + void OnPacketHeader(const quic::QuicPacketHeader& header, + quic::QuicTime receive_time, + quic::EncryptionLevel level) override; void OnPathChallengeFrame(const quic::QuicPathChallengeFrame& frame) override; void OnPathResponseFrame(const quic::QuicPathResponseFrame& frame) override; void OnCryptoFrame(const quic::QuicCryptoFrame& frame) override;
diff --git a/net/quic/quic_event_logger.cc b/net/quic/quic_event_logger.cc index e1023fa7..e8807eee 100644 --- a/net/quic/quic_event_logger.cc +++ b/net/quic/quic_event_logger.cc
@@ -642,7 +642,9 @@ [&] { return NetLogQuicDuplicatePacketParams(packet_number); }); } -void QuicEventLogger::OnPacketHeader(const quic::QuicPacketHeader& header) { +void QuicEventLogger::OnPacketHeader(const quic::QuicPacketHeader& header, + quic::QuicTime /*receive_time*/, + quic::EncryptionLevel /*level*/) { if (!net_log_.IsCapturing()) return; net_log_.AddEvent(NetLogEventType::QUIC_SESSION_PACKET_AUTHENTICATED);
diff --git a/net/quic/quic_event_logger.h b/net/quic/quic_event_logger.h index d25a8f6..8e8eb4d 100644 --- a/net/quic/quic_event_logger.h +++ b/net/quic/quic_event_logger.h
@@ -52,7 +52,9 @@ void OnAttemptingToProcessUndecryptablePacket( quic::EncryptionLevel decryption_level) override; void OnDuplicatePacket(quic::QuicPacketNumber packet_number) override; - void OnPacketHeader(const quic::QuicPacketHeader& header) override; + void OnPacketHeader(const quic::QuicPacketHeader& header, + quic::QuicTime receive_time, + quic::EncryptionLevel level) override; void OnPathChallengeFrame(const quic::QuicPathChallengeFrame& frame) override; void OnPathResponseFrame(const quic::QuicPathResponseFrame& frame) override; void OnCryptoFrame(const quic::QuicCryptoFrame& frame) override;
diff --git a/net/quic/quic_flags_list.h b/net/quic/quic_flags_list.h index bf264bd..2413624 100644 --- a/net/quic/quic_flags_list.h +++ b/net/quic/quic_flags_list.h
@@ -456,3 +456,14 @@ // If true, check for NULL before sending a fallback config. QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_check_fallback_null, true) + +// If true, HTTP/3 sesions will report error and close connection upon receiving +// HTTP/2 only frames. +QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_reject_spdy_frames, false) + +// If true, QuicConnection will initialize its self address to the self address +// of the first received packet, for all server connections and client +// connections that know its own address. +QUIC_FLAG(bool, + FLAGS_quic_reloadable_flag_quic_connection_set_initial_self_address, + false)
diff --git a/net/quic/quic_http_stream_test.cc b/net/quic/quic_http_stream_test.cc index cfec1ae1..6115b11 100644 --- a/net/quic/quic_http_stream_test.cc +++ b/net/quic/quic_http_stream_test.cc
@@ -165,6 +165,7 @@ QuicChromiumAlarmFactory* alarm_factory, quic::QuicPacketWriter* writer) : quic::QuicConnection(connection_id, + quic::QuicSocketAddress(), ToQuicSocketAddress(address), helper, alarm_factory,
diff --git a/net/quic/quic_proxy_client_socket_unittest.cc b/net/quic/quic_proxy_client_socket_unittest.cc index 2876669..751eecf 100644 --- a/net/quic/quic_proxy_client_socket_unittest.cc +++ b/net/quic/quic_proxy_client_socket_unittest.cc
@@ -227,7 +227,8 @@ QuicChromiumPacketWriter* writer = new QuicChromiumPacketWriter( socket.get(), base::ThreadTaskRunnerHandle::Get().get()); quic::QuicConnection* connection = new quic::QuicConnection( - connection_id_, net::ToQuicSocketAddress(peer_addr_), helper_.get(), + connection_id_, quic::QuicSocketAddress(), + net::ToQuicSocketAddress(peer_addr_), helper_.get(), alarm_factory_.get(), writer, true /* owns_writer */, quic::Perspective::IS_CLIENT, quic::test::SupportedVersions(version_)); connection->set_visitor(&visitor_);
diff --git a/net/quic/quic_stream_factory.cc b/net/quic/quic_stream_factory.cc index ad845cb..dabd78c 100644 --- a/net/quic/quic_stream_factory.cc +++ b/net/quic/quic_stream_factory.cc
@@ -1774,8 +1774,8 @@ QuicChromiumPacketWriter* writer = new QuicChromiumPacketWriter(socket.get(), task_runner_); quic::QuicConnection* connection = new quic::QuicConnection( - connection_id, ToQuicSocketAddress(addr), helper_.get(), - alarm_factory_.get(), writer, true /* owns_writer */, + connection_id, quic::QuicSocketAddress(), ToQuicSocketAddress(addr), + helper_.get(), alarm_factory_.get(), writer, true /* owns_writer */, quic::Perspective::IS_CLIENT, {quic_version}); connection->set_ping_timeout(ping_timeout_); connection->SetMaxPacketLength(params_.max_packet_length);
diff --git a/net/quic/quic_transport_client.cc b/net/quic/quic_transport_client.cc index 6f2138d..e45116b9 100644 --- a/net/quic/quic_transport_client.cc +++ b/net/quic/quic_transport_client.cc
@@ -275,8 +275,9 @@ quic::QuicUtils::CreateRandomConnectionId( quic_context_->random_generator()); connection_ = std::make_unique<quic::QuicConnection>( - connection_id, ToQuicSocketAddress(server_address), - quic_context_->helper(), alarm_factory_.get(), + connection_id, quic::QuicSocketAddress(), + ToQuicSocketAddress(server_address), quic_context_->helper(), + alarm_factory_.get(), new QuicChromiumPacketWriter(socket_.get(), task_runner_), /* owns_writer */ true, quic::Perspective::IS_CLIENT, supported_versions_);
diff --git a/sandbox/linux/BUILD.gn b/sandbox/linux/BUILD.gn index 4fd639f..e99b5e5 100644 --- a/sandbox/linux/BUILD.gn +++ b/sandbox/linux/BUILD.gn
@@ -434,6 +434,7 @@ "system_headers/linux_filter.h", "system_headers/linux_futex.h", "system_headers/linux_prctl.h", + "system_headers/linux_ptrace.h", "system_headers/linux_seccomp.h", "system_headers/linux_signal.h", "system_headers/linux_syscalls.h",
diff --git a/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.cc b/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.cc index a100248..45c7eda 100644 --- a/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.cc +++ b/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.cc
@@ -29,6 +29,7 @@ #include "sandbox/linux/seccomp-bpf/sandbox_bpf.h" #include "sandbox/linux/system_headers/linux_futex.h" #include "sandbox/linux/system_headers/linux_prctl.h" +#include "sandbox/linux/system_headers/linux_ptrace.h" #include "sandbox/linux/system_headers/linux_syscalls.h" #include "sandbox/linux/system_headers/linux_time.h" @@ -405,20 +406,26 @@ #if !defined(OS_NACL_NONSFI) ResultExpr RestrictPtrace() { const Arg<int> request(0); - return Switch(request).CASES(( +#if defined(__aarch64__) + const Arg<uintptr_t> addr(2); +#endif + return Switch(request) + .CASES(( #if !defined(__aarch64__) - PTRACE_GETREGS, - PTRACE_GETFPREGS, - PTRACE_GET_THREAD_AREA, + PTRACE_GETREGS, PTRACE_GETFPREGS, PTRACE_GET_THREAD_AREA, + PTRACE_GETREGSET, #endif #if defined(__arm__) - PTRACE_GETVFPREGS, + PTRACE_GETVFPREGS, #endif - PTRACE_GETREGSET, - PTRACE_PEEKDATA, - PTRACE_ATTACH, - PTRACE_DETACH), - Allow()) + PTRACE_PEEKDATA, PTRACE_ATTACH, PTRACE_DETACH), + Allow()) +#if defined(__aarch64__) + .Case( + PTRACE_GETREGSET, + If(AllOf(addr != NT_ARM_PACA_KEYS, addr != NT_ARM_PACG_KEYS), Allow()) + .Else(CrashSIGSYSPtrace())) +#endif .Default(CrashSIGSYSPtrace()); } #endif // defined(OS_NACL_NONSFI)
diff --git a/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions_unittests.cc b/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions_unittests.cc index b6c8c63..4bbfc7e 100644 --- a/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions_unittests.cc +++ b/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions_unittests.cc
@@ -32,6 +32,7 @@ #include "sandbox/linux/seccomp-bpf/sandbox_bpf.h" #include "sandbox/linux/seccomp-bpf/syscall.h" #include "sandbox/linux/services/syscall_wrappers.h" +#include "sandbox/linux/system_headers/linux_ptrace.h" #include "sandbox/linux/system_headers/linux_syscalls.h" #include "sandbox/linux/system_headers/linux_time.h" #include "sandbox/linux/tests/unit_tests.h" @@ -341,6 +342,36 @@ &iov); } +#if defined(__aarch64__) +BPF_DEATH_TEST_C( + ParameterRestrictions, + ptrace_getregs_nt_arm_paca_keys_blocked, + DEATH_SEGV_MESSAGE(sandbox::GetPtraceErrorMessageContentForTests()), + RestrictPtracePolicy) { + user_regs_struct regs{}; + iovec iov; + iov.iov_base = ®s; + iov.iov_len = sizeof(regs); + errno = 0; + ptrace(PTRACE_GETREGSET, getpid(), reinterpret_cast<void*>(NT_ARM_PACA_KEYS), + &iov); +} + +BPF_DEATH_TEST_C( + ParameterRestrictions, + ptrace_getregs_nt_arm_pacg_keys_blocked, + DEATH_SEGV_MESSAGE(sandbox::GetPtraceErrorMessageContentForTests()), + RestrictPtracePolicy) { + user_regs_struct regs{}; + iovec iov; + iov.iov_base = ®s; + iov.iov_len = sizeof(regs); + errno = 0; + ptrace(PTRACE_GETREGSET, getpid(), reinterpret_cast<void*>(NT_ARM_PACG_KEYS), + &iov); +} +#endif + } // namespace } // namespace sandbox
diff --git a/sandbox/linux/system_headers/linux_ptrace.h b/sandbox/linux/system_headers/linux_ptrace.h new file mode 100644 index 0000000..c7f47ac --- /dev/null +++ b/sandbox/linux/system_headers/linux_ptrace.h
@@ -0,0 +1,13 @@ +// Copyright 2020 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 SANDBOX_LINUX_SYSTEM_HEADERS_LINUX_PTRACE_H_ +#define SANDBOX_LINUX_SYSTEM_HEADERS_LINUX_PTRACE_H_ + +#if !defined(NT_ARM_PACA_KEYS) +#define NT_ARM_PACA_KEYS 0x407 /* Arm pointer authentication address keys */ +#define NT_ARM_PACG_KEYS 0x408 /* Arm pointer authentication generic key */ +#endif + +#endif // SANDBOX_LINUX_SYSTEM_HEADERS_LINUX_PTRACE_H_
diff --git a/services/network/resource_scheduler/resource_scheduler.cc b/services/network/resource_scheduler/resource_scheduler.cc index e738be0..e898448 100644 --- a/services/network/resource_scheduler/resource_scheduler.cc +++ b/services/network/resource_scheduler/resource_scheduler.cc
@@ -1270,10 +1270,6 @@ ideal_duration_to_wait); } - // Tracks if the main HTML parser has reached the body which marks the end of - // layout-blocking resources. - // This is disabled and the is always true when kRendererSideResourceScheduler - // is enabled. RequestQueue pending_requests_; RequestSet in_flight_requests_;
diff --git a/testing/buildbot/generate_buildbot_json.py b/testing/buildbot/generate_buildbot_json.py index 5975cdd..af61bc4 100755 --- a/testing/buildbot/generate_buildbot_json.py +++ b/testing/buildbot/generate_buildbot_json.py
@@ -727,8 +727,9 @@ self.initialize_args_for_test( result, tester_config, additional_arg_keys=['gtest_args']) - if self.is_android(tester_config) and tester_config.get('use_swarming', - True): + if self.is_android(tester_config) and tester_config.get( + 'use_swarming', + True) and not test_config.get('use_isolated_scripts_api', False): self.add_android_presentation_args(tester_config, test_name, result) result['args'] = result.get('args', []) + ['--recover-devices'] @@ -740,9 +741,14 @@ if not result.get('merge'): # TODO(https://crbug.com/958376): Consider adding the ability to not have # this default. + if test_config.get('use_isolated_scripts_api', False): + merge_script = 'standard_isolated_script_merge' + else: + merge_script = 'standard_gtest_merge' + result['merge'] = { - 'script': '//testing/merge_scripts/standard_gtest_merge.py', - 'args': [], + 'script': '//testing/merge_scripts/%s.py' % merge_script, + 'args': [], } return result
diff --git a/testing/buildbot/generate_buildbot_json_unittest.py b/testing/buildbot/generate_buildbot_json_unittest.py index 1c5601a9..266674f 100755 --- a/testing/buildbot/generate_buildbot_json_unittest.py +++ b/testing/buildbot/generate_buildbot_json_unittest.py
@@ -892,6 +892,19 @@ } """ +GTEST_AS_ISOLATED_SCRIPT_SUITE = """\ +{ + 'basic_suites': { + 'foo_tests': { + 'foo_test': { + 'script': 'foo.py', + 'use_isolated_scripts_api': True, + }, + }, + }, +} +""" + SCRIPT_WITH_ARGS_EXCEPTIONS = """\ { 'foo_test': { @@ -1176,6 +1189,35 @@ } """ +FOO_WATERFALL_GTEST_ISOLATED_SCRIPT_OUTPUT = """\ +{ + "AAAAA1 AUTOGENERATED FILE DO NOT EDIT": {}, + "AAAAA2 See generate_buildbot_json.py to make changes": {}, + "Fake Tester": { + "gtest_tests": [ + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "script": "foo.py", + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "kvm": "1" + } + ] + }, + "test": "foo_test", + "test_id_prefix": "ninja://chrome/test:foo_test/", + "use_isolated_scripts_api": true + } + ] + } +} +""" + COMPOSITION_WATERFALL_FILTERED_OUTPUT = """\ { "AAAAA1 AUTOGENERATED FILE DO NOT EDIT": {}, @@ -2425,6 +2467,19 @@ fbb.check_output_file_consistency(verbose=True) self.assertFalse(fbb.printed_lines) + def test_gtest_as_isolated_Script(self): + fbb = FakeBBGen(self.args, + FOO_GTESTS_WATERFALL, + GTEST_AS_ISOLATED_SCRIPT_SUITE, + LUCI_MILO_CFG, + gn_isolate_map=GN_ISOLATE_MAP) + self.create_testing_buildbot_json_file( + 'chromium.test.json', FOO_WATERFALL_GTEST_ISOLATED_SCRIPT_OUTPUT) + self.create_testing_buildbot_json_file( + 'chromium.ci.json', FOO_WATERFALL_GTEST_ISOLATED_SCRIPT_OUTPUT) + fbb.check_output_file_consistency(verbose=True) + self.assertFalse(fbb.printed_lines) + def test_ungenerated_output_files_are_caught(self): fbb = FakeBBGen(self.args, COMPOSITION_GTEST_SUITE_WATERFALL,
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json index 2aeb8b36..b4cee57 100644 --- a/testing/variations/fieldtrial_testing_config.json +++ b/testing/variations/fieldtrial_testing_config.json
@@ -1846,6 +1846,25 @@ ] } ], + "CrosSchedulerCore": [ + { + "platforms": [ + "chromeos" + ], + "experiments": [ + { + "name": "Enabled", + "params": { + "config": "core-scheduling" + }, + "enable_features": [ + "CoreSchedulingEnabled", + "SchedulerConfiguration" + ] + } + ] + } + ], "CrostiniWebUIUpgrader": [ { "platforms": [ @@ -2821,27 +2840,6 @@ ] } ], - "FlocIdComputedEventLogging": [ - { - "platforms": [ - "android", - "android_webview", - "chromeos", - "ios", - "linux", - "mac", - "windows" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "FlocIdComputedEventLogging" - ] - } - ] - } - ], "FormControlsDarkMode": [ { "platforms": [ @@ -4109,29 +4107,6 @@ ] } ], - "LowPriorityIframes2": [ - { - "platforms": [ - "linux", - "mac", - "windows", - "android", - "android_weblayer", - "chromeos" - ], - "experiments": [ - { - "name": "Enabled", - "params": { - "max_effective_connection_type_threshold": "4G" - }, - "enable_features": [ - "LowPriorityIframes" - ] - } - ] - } - ], "LowerJavaScriptPriorityWhenForceDeferred": [ { "platforms": [
diff --git a/third_party/blink/common/OWNERS b/third_party/blink/common/OWNERS index 9b94b90..f5f8884e 100644 --- a/third_party/blink/common/OWNERS +++ b/third_party/blink/common/OWNERS
@@ -1,11 +1,12 @@ -jam@chromium.org -haraken@chromium.org -kinuko@chromium.org -tkent@chromium.org -jbroman@chromium.org -pfeldman@chromium.org -dgozman@chromium.org dcheng@chromium.org +dgozman@chromium.org +dtapuska@chromium.org falken@chromium.org +haraken@chromium.org +jam@chromium.org +jbroman@chromium.org +kinuko@chromium.org +pfeldman@chromium.org +tkent@chromium.org # COMPONENT: Blink
diff --git a/third_party/blink/renderer/bindings/generated_in_modules.gni b/third_party/blink/renderer/bindings/generated_in_modules.gni index 6cf406ae..9875903 100644 --- a/third_party/blink/renderer/bindings/generated_in_modules.gni +++ b/third_party/blink/renderer/bindings/generated_in_modules.gni
@@ -399,12 +399,18 @@ "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_gpu_vertex_buffer_layout_descriptor.h", "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_gpu_vertex_state_descriptor.cc", "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_gpu_vertex_state_descriptor.h", + "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_hid_collection_info.cc", + "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_hid_collection_info.h", "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_hid_connection_event_init.cc", "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_hid_connection_event_init.h", "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_hid_device_filter.cc", "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_hid_device_filter.h", "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_hid_device_request_options.cc", "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_hid_device_request_options.h", + "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_hid_report_info.cc", + "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_hid_report_info.h", + "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_hid_report_item.cc", + "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_hid_report_item.h", "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_hit_region_options.cc", "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_hit_region_options.h", "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_idb_database_info.cc",
diff --git a/third_party/blink/renderer/core/exported/web_view_impl.cc b/third_party/blink/renderer/core/exported/web_view_impl.cc index 8a07d74..2d07ca8 100644 --- a/third_party/blink/renderer/core/exported/web_view_impl.cc +++ b/third_party/blink/renderer/core/exported/web_view_impl.cc
@@ -4249,4 +4249,11 @@ web_widget_->SetDeviceColorSpaceForTesting(color_space); } +void WebViewImpl::RunPaintBenchmark(int repeat_count, + cc::PaintBenchmarkResult& result) { + DCHECK(MainFrameImpl()); + if (auto* frame_view = MainFrameImpl()->GetFrameView()) + frame_view->RunPaintBenchmark(repeat_count, result); +} + } // namespace blink
diff --git a/third_party/blink/renderer/core/exported/web_view_impl.h b/third_party/blink/renderer/core/exported/web_view_impl.h index e5dc418..03a2e10 100644 --- a/third_party/blink/renderer/core/exported/web_view_impl.h +++ b/third_party/blink/renderer/core/exported/web_view_impl.h
@@ -463,6 +463,8 @@ // Called when keyboard focus switches to an anchor with the given URL. void SetKeyboardFocusURL(const KURL&); + void RunPaintBenchmark(int repeat_count, cc::PaintBenchmarkResult& result); + private: FRIEND_TEST_ALL_PREFIXES(WebFrameTest, DivScrollIntoEditableTest); FRIEND_TEST_ALL_PREFIXES(WebFrameTest,
diff --git a/third_party/blink/renderer/core/frame/DEPS b/third_party/blink/renderer/core/frame/DEPS index bce2d2b..66e05bf7 100644 --- a/third_party/blink/renderer/core/frame/DEPS +++ b/third_party/blink/renderer/core/frame/DEPS
@@ -21,6 +21,7 @@ "+ui/gfx/transform.h" ], "local_frame_view.cc": [ + "+base/timer/lap_timer.h", "+cc/tiles/frame_viewer_instrumentation.h", "+components/paint_preview/common/paint_preview_tracker.h", ],
diff --git a/third_party/blink/renderer/core/frame/local_frame_view.cc b/third_party/blink/renderer/core/frame/local_frame_view.cc index dc779bb..6cfbe3e 100644 --- a/third_party/blink/renderer/core/frame/local_frame_view.cc +++ b/third_party/blink/renderer/core/frame/local_frame_view.cc
@@ -35,6 +35,7 @@ #include "base/memory/ptr_util.h" #include "base/metrics/field_trial_params.h" #include "base/numerics/safe_conversions.h" +#include "base/timer/lap_timer.h" #include "cc/input/main_thread_scrolling_reason.h" #include "cc/layers/picture_layer.h" #include "cc/tiles/frame_viewer_instrumentation.h" @@ -2738,7 +2739,7 @@ return target_state > DocumentLifecycle::kPrePaintClean; } -void LocalFrameView::RunPaintLifecyclePhase() { +void LocalFrameView::RunPaintLifecyclePhase(PaintBenchmarkMode benchmark_mode) { TRACE_EVENT0("blink,benchmark", "LocalFrameView::RunPaintLifecyclePhase"); // While printing or capturing a paint preview of a document, the paint walk // is done into a special canvas. There is no point doing a normal paint step @@ -2746,7 +2747,7 @@ bool is_capturing_layout = frame_->GetDocument()->IsCapturingLayout(); HashSet<const GraphicsLayer*> repainted_layers; if (!is_capturing_layout) - PaintTree(repainted_layers); + PaintTree(repainted_layers, benchmark_mode); if (!RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) { if (GetLayoutView()->Compositor()->InCompositingMode()) { @@ -2754,6 +2755,15 @@ } } + if (benchmark_mode == + PaintBenchmarkMode::kForcePaintArtifactCompositorUpdate || + // TODO(paint-dev): Separate requirement for update for repaint and full + // PaintArtifactCompositor update. + (RuntimeEnabledFeatures::CompositeAfterPaintEnabled() && + benchmark_mode != PaintBenchmarkMode::kNormal)) { + paint_artifact_compositor_->SetNeedsUpdate(); + } + if (!is_capturing_layout) { bool needed_update = !paint_artifact_compositor_ || paint_artifact_compositor_->NeedsUpdate(); @@ -2901,8 +2911,8 @@ }); } -void LocalFrameView::PaintTree( - HashSet<const GraphicsLayer*>& repainted_layers) { +void LocalFrameView::PaintTree(HashSet<const GraphicsLayer*>& repainted_layers, + PaintBenchmarkMode benchmark_mode) { SCOPED_UMA_AND_UKM_TIMER(EnsureUkmAggregator(), LocalFrameUkmAggregator::kPaint); @@ -2924,12 +2934,16 @@ if (!paint_controller_) paint_controller_ = std::make_unique<PaintController>(); + PaintController::ScopedBenchmarkMode scoped_benchmark(*paint_controller_, + benchmark_mode); + // TODO(crbug.com/917911): Painting of overlays should not force repainting // of the frame contents. auto* web_local_frame_impl = WebLocalFrameImpl::FromFrame(frame_); bool has_dev_tools_overlays = web_local_frame_impl && web_local_frame_impl->HasDevToolsOverlays(); - if (!GetLayoutView()->Layer()->SelfOrDescendantNeedsRepaint() && + if (!paint_controller_->ShouldForcePaintForBenchmark() && + !GetLayoutView()->Layer()->SelfOrDescendantNeedsRepaint() && !visual_viewport_needs_repaint_ && !has_dev_tools_overlays) { paint_controller_->UpdateUMACountsOnFullyCached(); } else { @@ -2988,7 +3002,7 @@ // the host page and will be painted during painting of the host page. if (GraphicsLayer* root_graphics_layer = layout_view->Compositor()->PaintRootGraphicsLayer()) { - root_graphics_layer->PaintRecursively(repainted_layers); + root_graphics_layer->PaintRecursively(repainted_layers, benchmark_mode); if (!repainted_layers.IsEmpty()) { // If the painted result changed, the recorded hit test data may have // changed which will affect the mapped hit test geometry. @@ -3047,7 +3061,7 @@ // Skip updating property trees, pushing cc::Layers, and issuing raster // invalidations if possible. - // TODO(paint-team): In CompositeAfterPaint mode, repainted_layers will always + // TODO(paint-dev): In CompositeAfterPaint mode, repainted_layers will always // be empty, even if painted output has changed. We need an equivalent signal // to indicate that PAC doesn't need to run the layerization algorithm, but it // does need to update properties on layers that depend on painted output. @@ -4804,4 +4818,43 @@ return GetFullScreenOverlayVideoLayer(*doc); } +void LocalFrameView::RunPaintBenchmark(int repeat_count, + cc::PaintBenchmarkResult& result) { + DCHECK_EQ(Lifecycle().GetState(), DocumentLifecycle::kPaintClean); + + auto run_benchmark = [&](PaintBenchmarkMode mode) -> double { + constexpr int kTimeCheckInterval = 1; + constexpr int kWarmupRuns = 0; + constexpr base::TimeDelta kTimeLimit = base::TimeDelta::FromMilliseconds(1); + + base::TimeDelta min_time = base::TimeDelta::Max(); + for (int i = 0; i < repeat_count; i++) { + // Run for a minimum amount of time to avoid problems with timer + // quantization when the time is very small. + base::LapTimer timer(kWarmupRuns, kTimeLimit, kTimeCheckInterval); + do { + RunPaintLifecyclePhase(mode); + timer.NextLap(); + } while (!timer.HasTimeLimitExpired()); + + base::TimeDelta duration = timer.TimePerLap(); + if (duration < min_time) + min_time = duration; + } + return min_time.InMillisecondsF(); + }; + + result.record_time_ms = run_benchmark(PaintBenchmarkMode::kForcePaint); + result.record_time_caching_disabled_ms = + run_benchmark(PaintBenchmarkMode::kCachingDisabled); + result.record_time_subsequence_caching_disabled_ms = + run_benchmark(PaintBenchmarkMode::kSubsequenceCachingDisabled); + result.record_time_partial_invalidation_ms = + run_benchmark(PaintBenchmarkMode::kPartialInvalidation); + result.raster_invalidation_and_convert_time_ms = + run_benchmark(PaintBenchmarkMode::kForceRasterInvalidationAndConvert); + result.paint_artifact_compositor_update_time_ms = + run_benchmark(PaintBenchmarkMode::kForcePaintArtifactCompositorUpdate); +} + } // namespace blink
diff --git a/third_party/blink/renderer/core/frame/local_frame_view.h b/third_party/blink/renderer/core/frame/local_frame_view.h index 4ca0577..520dde8 100644 --- a/third_party/blink/renderer/core/frame/local_frame_view.h +++ b/third_party/blink/renderer/core/frame/local_frame_view.h
@@ -46,6 +46,7 @@ #include "third_party/blink/renderer/platform/graphics/color.h" #include "third_party/blink/renderer/platform/graphics/compositor_element_id.h" #include "third_party/blink/renderer/platform/graphics/paint/cull_rect.h" +#include "third_party/blink/renderer/platform/graphics/paint/paint_controller.h" #include "third_party/blink/renderer/platform/graphics/paint_invalidation_reason.h" #include "third_party/blink/renderer/platform/graphics/subtree_paint_property_update_reason.h" #include "third_party/blink/renderer/platform/timer.h" @@ -57,8 +58,8 @@ class Layer; class PaintOpBuffer; enum class PaintHoldingCommitTrigger; - using PaintRecord = PaintOpBuffer; +struct PaintBenchmarkResult; } namespace ui { @@ -729,6 +730,8 @@ PaintLayer* GetFullScreenOverlayLayer() const; + void RunPaintBenchmark(int repeat_count, cc::PaintBenchmarkResult& result); + protected: void FrameRectsChanged(const IntRect&) override; void SelfVisibleChanged() override; @@ -803,9 +806,10 @@ DocumentLifecycle::LifecycleState target_state); bool RunPrePaintLifecyclePhase( DocumentLifecycle::LifecycleState target_state); - void RunPaintLifecyclePhase(); + void RunPaintLifecyclePhase(PaintBenchmarkMode = PaintBenchmarkMode::kNormal); - void PaintTree(HashSet<const GraphicsLayer*>& repainted_layers); + void PaintTree(HashSet<const GraphicsLayer*>& repainted_layers, + PaintBenchmarkMode); void UpdateStyleAndLayoutIfNeededRecursive(); void PushPaintArtifactToCompositor(
diff --git a/third_party/blink/renderer/core/frame/web_view_frame_widget.cc b/third_party/blink/renderer/core/frame/web_view_frame_widget.cc index 892b34da..cfd1be3 100644 --- a/third_party/blink/renderer/core/frame/web_view_frame_widget.cc +++ b/third_party/blink/renderer/core/frame/web_view_frame_widget.cc
@@ -416,6 +416,11 @@ return true; } +void WebViewFrameWidget::RunPaintBenchmark(int repeat_count, + cc::PaintBenchmarkResult& result) { + web_view_->RunPaintBenchmark(repeat_count, result); +} + const ScreenInfo& WebViewFrameWidget::GetOriginalScreenInfo() { if (device_emulator_) return device_emulator_->original_screen_info();
diff --git a/third_party/blink/renderer/core/frame/web_view_frame_widget.h b/third_party/blink/renderer/core/frame/web_view_frame_widget.h index e5d3b46..1d98679 100644 --- a/third_party/blink/renderer/core/frame/web_view_frame_widget.h +++ b/third_party/blink/renderer/core/frame/web_view_frame_widget.h
@@ -135,6 +135,8 @@ gfx::Rect ViewportVisibleRect() override; bool UpdateScreenRects(const gfx::Rect& widget_screen_rect, const gfx::Rect& window_screen_rect) override; + void RunPaintBenchmark(int repeat_count, + cc::PaintBenchmarkResult& result) override; void SetScreenMetricsEmulationParameters( bool enabled,
diff --git a/third_party/blink/renderer/core/inspector/inspector_overlay_agent.cc b/third_party/blink/renderer/core/inspector/inspector_overlay_agent.cc index 29402d2..9f9dc0e 100644 --- a/third_party/blink/renderer/core/inspector/inspector_overlay_agent.cc +++ b/third_party/blink/renderer/core/inspector/inspector_overlay_agent.cc
@@ -296,8 +296,7 @@ bool FillsBoundsCompletely() const override { return false; } size_t GetApproximateUnsharedMemoryUsage() const override { return 0; } - scoped_refptr<cc::DisplayItemList> PaintContentsToDisplayList( - PaintingControlSetting) override { + scoped_refptr<cc::DisplayItemList> PaintContentsToDisplayList() override { auto display_list = base::MakeRefCounted<cc::DisplayItemList>(); display_list->StartPaint(); display_list->push<cc::DrawRecordOp>(
diff --git a/third_party/blink/renderer/core/layout/layout_button.h b/third_party/blink/renderer/core/layout/layout_button.h index 796df53..279451d0 100644 --- a/third_party/blink/renderer/core/layout/layout_button.h +++ b/third_party/blink/renderer/core/layout/layout_button.h
@@ -42,8 +42,7 @@ } bool IsOfType(LayoutObjectType type) const override { NOT_DESTROYED(); - return type == kLayoutObjectLayoutButton || - LayoutFlexibleBox::IsOfType(type); + return type == kLayoutObjectButton || LayoutFlexibleBox::IsOfType(type); } void AddChild(LayoutObject* new_child,
diff --git a/third_party/blink/renderer/core/layout/layout_custom_scrollbar_part.h b/third_party/blink/renderer/core/layout/layout_custom_scrollbar_part.h index 1c8d465..58aae26 100644 --- a/third_party/blink/renderer/core/layout/layout_custom_scrollbar_part.h +++ b/third_party/blink/renderer/core/layout/layout_custom_scrollbar_part.h
@@ -71,7 +71,7 @@ bool IsOfType(LayoutObjectType type) const override { NOT_DESTROYED(); - return type == kLayoutObjectLayoutCustomScrollbarPart || + return type == kLayoutObjectCustomScrollbarPart || LayoutReplaced::IsOfType(type); } ScrollableArea* GetScrollableArea() const {
diff --git a/third_party/blink/renderer/core/layout/layout_grid.h b/third_party/blink/renderer/core/layout/layout_grid.h index c5f349b..63e16523 100644 --- a/third_party/blink/renderer/core/layout/layout_grid.h +++ b/third_party/blink/renderer/core/layout/layout_grid.h
@@ -146,7 +146,7 @@ private: bool IsOfType(LayoutObjectType type) const override { NOT_DESTROYED(); - return type == kLayoutObjectLayoutGrid || LayoutBlock::IsOfType(type); + return type == kLayoutObjectGrid || LayoutBlock::IsOfType(type); } MinMaxSizes ComputeIntrinsicLogicalWidths() const override;
diff --git a/third_party/blink/renderer/core/layout/layout_iframe.h b/third_party/blink/renderer/core/layout/layout_iframe.h index bc22555..45f3f76b 100644 --- a/third_party/blink/renderer/core/layout/layout_iframe.h +++ b/third_party/blink/renderer/core/layout/layout_iframe.h
@@ -47,8 +47,7 @@ bool IsOfType(LayoutObjectType type) const override { NOT_DESTROYED(); - return type == kLayoutObjectLayoutIFrame || - LayoutEmbeddedContent::IsOfType(type); + return type == kLayoutObjectIFrame || LayoutEmbeddedContent::IsOfType(type); } PaintLayerType LayerTypeRequired() const override;
diff --git a/third_party/blink/renderer/core/layout/layout_image.h b/third_party/blink/renderer/core/layout/layout_image.h index 94db77be..4ac30e6c 100644 --- a/third_party/blink/renderer/core/layout/layout_image.h +++ b/third_party/blink/renderer/core/layout/layout_image.h
@@ -117,7 +117,7 @@ bool IsOfType(LayoutObjectType type) const override { NOT_DESTROYED(); - return type == kLayoutObjectLayoutImage || LayoutReplaced::IsOfType(type); + return type == kLayoutObjectImage || LayoutReplaced::IsOfType(type); } void WillBeDestroyed() override;
diff --git a/third_party/blink/renderer/core/layout/layout_multi_column_set.h b/third_party/blink/renderer/core/layout/layout_multi_column_set.h index 03f086e..85782690 100644 --- a/third_party/blink/renderer/core/layout/layout_multi_column_set.h +++ b/third_party/blink/renderer/core/layout/layout_multi_column_set.h
@@ -102,7 +102,7 @@ bool IsOfType(LayoutObjectType type) const override { NOT_DESTROYED(); - return type == kLayoutObjectLayoutMultiColumnSet || + return type == kLayoutObjectMultiColumnSet || LayoutBlockFlow::IsOfType(type); } bool CanHaveChildren() const final {
diff --git a/third_party/blink/renderer/core/layout/layout_multi_column_spanner_placeholder.h b/third_party/blink/renderer/core/layout/layout_multi_column_spanner_placeholder.h index 074ae5d..94d6224cf 100644 --- a/third_party/blink/renderer/core/layout/layout_multi_column_spanner_placeholder.h +++ b/third_party/blink/renderer/core/layout/layout_multi_column_spanner_placeholder.h
@@ -19,7 +19,7 @@ public: bool IsOfType(LayoutObjectType type) const override { NOT_DESTROYED(); - return type == kLayoutObjectLayoutMultiColumnSpannerPlaceholder || + return type == kLayoutObjectMultiColumnSpannerPlaceholder || LayoutBox::IsOfType(type); }
diff --git a/third_party/blink/renderer/core/layout/layout_object.h b/third_party/blink/renderer/core/layout/layout_object.h index af644aa6..630ef00d 100644 --- a/third_party/blink/renderer/core/layout/layout_object.h +++ b/third_party/blink/renderer/core/layout/layout_object.h
@@ -838,11 +838,11 @@ } bool IsLayoutTableCol() const { NOT_DESTROYED(); - return IsOfType(kLayoutObjectLayoutTableCol); + return IsOfType(kLayoutObjectTableCol); } bool IsLayoutNGTableCol() const { NOT_DESTROYED(); - return IsOfType(kLayoutObjectLayoutNGTableCol); + return IsOfType(kLayoutObjectNGTableCol); } bool IsListItem() const { NOT_DESTROYED(); @@ -882,8 +882,7 @@ } bool IsButtonIncludingNG() const { NOT_DESTROYED(); - return IsOfType(kLayoutObjectLayoutButton) || - IsOfType(kLayoutObjectNGButton); + return IsOfType(kLayoutObjectButton) || IsOfType(kLayoutObjectNGButton); } bool IsLayoutNGButton() const { NOT_DESTROYED(); @@ -891,39 +890,39 @@ } bool IsLayoutNGCustom() const { NOT_DESTROYED(); - return IsOfType(kLayoutObjectLayoutNGCustom); + return IsOfType(kLayoutObjectNGCustom); } bool IsLayoutGrid() const { NOT_DESTROYED(); - return IsOfType(kLayoutObjectLayoutGrid); + return IsOfType(kLayoutObjectGrid); } bool IsLayoutIFrame() const { NOT_DESTROYED(); - return IsOfType(kLayoutObjectLayoutIFrame); + return IsOfType(kLayoutObjectIFrame); } bool IsLayoutImage() const { NOT_DESTROYED(); - return IsOfType(kLayoutObjectLayoutImage); + return IsOfType(kLayoutObjectImage); } bool IsLayoutMultiColumnSet() const { NOT_DESTROYED(); - return IsOfType(kLayoutObjectLayoutMultiColumnSet); + return IsOfType(kLayoutObjectMultiColumnSet); } bool IsLayoutMultiColumnSpannerPlaceholder() const { NOT_DESTROYED(); - return IsOfType(kLayoutObjectLayoutMultiColumnSpannerPlaceholder); + return IsOfType(kLayoutObjectMultiColumnSpannerPlaceholder); } bool IsLayoutReplaced() const { NOT_DESTROYED(); - return IsOfType(kLayoutObjectLayoutReplaced); + return IsOfType(kLayoutObjectReplaced); } bool IsLayoutCustomScrollbarPart() const { NOT_DESTROYED(); - return IsOfType(kLayoutObjectLayoutCustomScrollbarPart); + return IsOfType(kLayoutObjectCustomScrollbarPart); } bool IsLayoutView() const { NOT_DESTROYED(); - return IsOfType(kLayoutObjectLayoutView); + return IsOfType(kLayoutObjectView); } bool IsRuby() const { NOT_DESTROYED(); @@ -967,7 +966,7 @@ } bool IsTextAreaIncludingNG() const { NOT_DESTROYED(); - return IsOfType(kLayoutObjectTextArea) || + return IsOfType(kLayoutObjectTextControlMultiLine) || IsOfType(kLayoutObjectNGTextControlMultiLine); } bool IsTextControlIncludingNG() const { @@ -978,7 +977,7 @@ } bool IsTextFieldIncludingNG() const { NOT_DESTROYED(); - return IsOfType(kLayoutObjectTextField) || + return IsOfType(kLayoutObjectTextControlSingleLine) || IsOfType(kLayoutObjectNGTextControlSingleLine); } bool IsVideo() const { @@ -3273,6 +3272,8 @@ } protected: + // Identifiers for each of LayoutObject subclasses. + // The identifier name for blink::LayoutFoo should be kLayoutObjectFoo. enum LayoutObjectType { kLayoutObjectBr, kLayoutObjectCanvas, @@ -3284,8 +3285,8 @@ kLayoutObjectFrame, kLayoutObjectFrameSet, kLayoutObjectInsideListMarker, - kLayoutObjectLayoutTableCol, - kLayoutObjectLayoutNGTableCol, + kLayoutObjectTableCol, + kLayoutObjectNGTableCol, kLayoutObjectListItem, kLayoutObjectListMarker, kLayoutObjectListMarkerImage, @@ -3308,19 +3309,16 @@ kLayoutObjectOutsideListMarker, kLayoutObjectProgress, kLayoutObjectQuote, - kLayoutObjectLayoutButton, - kLayoutObjectLayoutNGCustom, - kLayoutObjectLayoutFlowThread, - kLayoutObjectLayoutGrid, - kLayoutObjectLayoutIFrame, - kLayoutObjectLayoutImage, - kLayoutObjectLayoutInline, - kLayoutObjectLayoutMultiColumnSet, - kLayoutObjectLayoutMultiColumnSpannerPlaceholder, - kLayoutObjectLayoutEmbeddedContent, - kLayoutObjectLayoutReplaced, - kLayoutObjectLayoutCustomScrollbarPart, - kLayoutObjectLayoutView, + kLayoutObjectButton, + kLayoutObjectNGCustom, + kLayoutObjectGrid, + kLayoutObjectIFrame, + kLayoutObjectImage, + kLayoutObjectMultiColumnSet, + kLayoutObjectMultiColumnSpannerPlaceholder, + kLayoutObjectReplaced, + kLayoutObjectCustomScrollbarPart, + kLayoutObjectView, kLayoutObjectRuby, kLayoutObjectRubyBase, kLayoutObjectRubyRun, @@ -3331,9 +3329,9 @@ kLayoutObjectTableCellLegacy, kLayoutObjectTableRow, kLayoutObjectTableSection, - kLayoutObjectTextArea, + kLayoutObjectTextControlMultiLine, kLayoutObjectTextControl, - kLayoutObjectTextField, + kLayoutObjectTextControlSingleLine, kLayoutObjectVideo, kLayoutObjectWidget,
diff --git a/third_party/blink/renderer/core/layout/layout_replaced.h b/third_party/blink/renderer/core/layout/layout_replaced.h index ffeec050..7549fd9 100644 --- a/third_party/blink/renderer/core/layout/layout_replaced.h +++ b/third_party/blink/renderer/core/layout/layout_replaced.h
@@ -169,7 +169,7 @@ bool IsOfType(LayoutObjectType type) const override { NOT_DESTROYED(); - return type == kLayoutObjectLayoutReplaced || LayoutBox::IsOfType(type); + return type == kLayoutObjectReplaced || LayoutBox::IsOfType(type); } private:
diff --git a/third_party/blink/renderer/core/layout/layout_table_col.h b/third_party/blink/renderer/core/layout/layout_table_col.h index b6fd653..f43776f 100644 --- a/third_party/blink/renderer/core/layout/layout_table_col.h +++ b/third_party/blink/renderer/core/layout/layout_table_col.h
@@ -95,7 +95,7 @@ private: bool IsOfType(LayoutObjectType type) const override { NOT_DESTROYED(); - return type == kLayoutObjectLayoutTableCol || LayoutBox::IsOfType(type); + return type == kLayoutObjectTableCol || LayoutBox::IsOfType(type); } void UpdateFromElement() override;
diff --git a/third_party/blink/renderer/core/layout/layout_text_control_multi_line.h b/third_party/blink/renderer/core/layout/layout_text_control_multi_line.h index 5828d9c..2df8ed5 100644 --- a/third_party/blink/renderer/core/layout/layout_text_control_multi_line.h +++ b/third_party/blink/renderer/core/layout/layout_text_control_multi_line.h
@@ -35,7 +35,8 @@ private: bool IsOfType(LayoutObjectType type) const override { NOT_DESTROYED(); - return type == kLayoutObjectTextArea || LayoutTextControl::IsOfType(type); + return type == kLayoutObjectTextControlMultiLine || + LayoutTextControl::IsOfType(type); } bool NodeAtPoint(HitTestResult&,
diff --git a/third_party/blink/renderer/core/layout/layout_text_control_single_line.h b/third_party/blink/renderer/core/layout/layout_text_control_single_line.h index e03d33b..7a74105b 100644 --- a/third_party/blink/renderer/core/layout/layout_text_control_single_line.h +++ b/third_party/blink/renderer/core/layout/layout_text_control_single_line.h
@@ -47,7 +47,8 @@ private: bool IsOfType(LayoutObjectType type) const override { NOT_DESTROYED(); - return type == kLayoutObjectTextField || LayoutTextControl::IsOfType(type); + return type == kLayoutObjectTextControlSingleLine || + LayoutTextControl::IsOfType(type); } void Paint(const PaintInfo&) const override;
diff --git a/third_party/blink/renderer/core/layout/layout_view.h b/third_party/blink/renderer/core/layout/layout_view.h index 028ea58..cc899b6 100644 --- a/third_party/blink/renderer/core/layout/layout_view.h +++ b/third_party/blink/renderer/core/layout/layout_view.h
@@ -94,7 +94,7 @@ bool IsOfType(LayoutObjectType type) const override { NOT_DESTROYED(); - return type == kLayoutObjectLayoutView || LayoutBlockFlow::IsOfType(type); + return type == kLayoutObjectView || LayoutBlockFlow::IsOfType(type); } PaintLayerType LayerTypeRequired() const override {
diff --git a/third_party/blink/renderer/core/layout/ng/custom/layout_ng_custom.h b/third_party/blink/renderer/core/layout/ng/custom/layout_ng_custom.h index 95f0f3d..720fad9 100644 --- a/third_party/blink/renderer/core/layout/ng/custom/layout_ng_custom.h +++ b/third_party/blink/renderer/core/layout/ng/custom/layout_ng_custom.h
@@ -37,8 +37,7 @@ private: bool IsOfType(LayoutObjectType type) const override { - return type == kLayoutObjectLayoutNGCustom || - LayoutNGBlockFlow::IsOfType(type); + return type == kLayoutObjectNGCustom || LayoutNGBlockFlow::IsOfType(type); } LayoutNGCustomState state_;
diff --git a/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_column.h b/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_column.h index 20a342e..87adf3b 100644 --- a/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_column.h +++ b/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_column.h
@@ -67,7 +67,7 @@ bool IsOfType(LayoutObjectType type) const override { NOT_DESTROYED(); - return type == kLayoutObjectLayoutTableCol || LayoutBox::IsOfType(type); + return type == kLayoutObjectTableCol || LayoutBox::IsOfType(type); } private:
diff --git a/third_party/blink/renderer/core/paint/css_mask_painter.cc b/third_party/blink/renderer/core/paint/css_mask_painter.cc index 6a26d45..cef7d06 100644 --- a/third_party/blink/renderer/core/paint/css_mask_painter.cc +++ b/third_party/blink/renderer/core/paint/css_mask_painter.cc
@@ -27,6 +27,7 @@ SVGResources::ReferenceBoxForEffects(object); const float reference_box_zoom = object.IsSVGForeignObject() ? object.StyleRef().EffectiveZoom() : 1; + masker->ClearInvalidationMask(); return EnclosingIntRect( masker->ResourceBoundingBox(reference_box, reference_box_zoom)); }
diff --git a/third_party/blink/renderer/core/paint/link_highlight_impl.cc b/third_party/blink/renderer/core/paint/link_highlight_impl.cc index 53eb7f7f..8529965e 100644 --- a/third_party/blink/renderer/core/paint/link_highlight_impl.cc +++ b/third_party/blink/renderer/core/paint/link_highlight_impl.cc
@@ -159,8 +159,7 @@ } scoped_refptr<cc::DisplayItemList> -LinkHighlightImpl::LinkHighlightFragment::PaintContentsToDisplayList( - PaintingControlSetting painting_control) { +LinkHighlightImpl::LinkHighlightFragment::PaintContentsToDisplayList() { auto display_list = base::MakeRefCounted<cc::DisplayItemList>(); PaintRecorder recorder;
diff --git a/third_party/blink/renderer/core/paint/link_highlight_impl.h b/third_party/blink/renderer/core/paint/link_highlight_impl.h index d1acded..def05d0 100644 --- a/third_party/blink/renderer/core/paint/link_highlight_impl.h +++ b/third_party/blink/renderer/core/paint/link_highlight_impl.h
@@ -102,8 +102,7 @@ private: // cc::ContentLayerClient implementation. gfx::Rect PaintableRegion() override; - scoped_refptr<cc::DisplayItemList> PaintContentsToDisplayList( - PaintingControlSetting painting_control) override; + scoped_refptr<cc::DisplayItemList> PaintContentsToDisplayList() override; bool FillsBoundsCompletely() const override { return false; } size_t GetApproximateUnsharedMemoryUsage() const override { return 0; }
diff --git a/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_impl.cc b/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_impl.cc index 5cc8e0d..54053d6a 100644 --- a/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_impl.cc +++ b/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_impl.cc
@@ -163,8 +163,8 @@ dummy_connection_id = quic::QuicConnectionId(connection_id_bytes, sizeof(connection_id_bytes)); return std::make_unique<quic::QuicConnection>( - dummy_connection_id, dummy_address, helper, alarm_factory, packet_writer, - /* owns_writer */ true, perspective, + dummy_connection_id, quic::QuicSocketAddress(), dummy_address, helper, + alarm_factory, packet_writer, /* owns_writer */ true, perspective, quic::ParsedQuicVersionVector{quic::CurrentSupportedVersions()[0]}); }
diff --git a/third_party/blink/renderer/modules/wake_lock/wake_lock.cc b/third_party/blink/renderer/modules/wake_lock/wake_lock.cc index cb0c5f1..8b3983f1 100644 --- a/third_party/blink/renderer/modules/wake_lock/wake_lock.cc +++ b/third_party/blink/renderer/modules/wake_lock/wake_lock.cc
@@ -18,7 +18,6 @@ #include "third_party/blink/renderer/modules/wake_lock/wake_lock_type.h" #include "third_party/blink/renderer/platform/bindings/exception_state.h" #include "third_party/blink/renderer/platform/instrumentation/use_counter.h" -#include "third_party/blink/renderer/platform/scheduler/public/frame_or_worker_scheduler.h" #include "third_party/blink/renderer/platform/wtf/functional.h" #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h" @@ -34,11 +33,7 @@ managers_{ MakeGarbageCollected<WakeLockManager>(&window, WakeLockType::kScreen), MakeGarbageCollected<WakeLockManager>(&window, - WakeLockType::kSystem)} { - window.GetScheduler()->RegisterStickyFeature( - SchedulingPolicy::Feature::kWakeLock, - {SchedulingPolicy::RecordMetricsForBackForwardCache()}); -} + WakeLockType::kSystem)} {} WakeLock::WakeLock(DedicatedWorkerGlobalScope& worker_scope) : ExecutionContextLifecycleObserver(&worker_scope),
diff --git a/third_party/blink/renderer/modules/wake_lock/wake_lock_manager.cc b/third_party/blink/renderer/modules/wake_lock/wake_lock_manager.cc index c8cd404b..324f555d 100644 --- a/third_party/blink/renderer/modules/wake_lock/wake_lock_manager.cc +++ b/third_party/blink/renderer/modules/wake_lock/wake_lock_manager.cc
@@ -51,6 +51,11 @@ wake_lock_.set_disconnect_handler(WTF::Bind( &WakeLockManager::OnWakeLockConnectionError, WrapWeakPersistent(this))); wake_lock_->RequestWakeLock(); + + feature_handle_for_scheduler_ = + execution_context_->GetScheduler()->RegisterFeature( + SchedulingPolicy::Feature::kWakeLock, + {SchedulingPolicy::RecordMetricsForBackForwardCache()}); } // https://w3c.github.io/screen-wake-lock/#the-request-method // 5.2. Let lock be a new WakeLockSentinel object with its type attribute set @@ -86,6 +91,9 @@ // 5.2. If success is true and type is "screen" run the following: // 5.2.1. Reset the platform-specific inactivity timer after which the // screen is actually turned off. + + // Make the page bfcache-eligible if there is no WakeLock held. + feature_handle_for_scheduler_.reset(); } }
diff --git a/third_party/blink/renderer/modules/wake_lock/wake_lock_manager.h b/third_party/blink/renderer/modules/wake_lock/wake_lock_manager.h index ad23acd..d8a5d52 100644 --- a/third_party/blink/renderer/modules/wake_lock/wake_lock_manager.h +++ b/third_party/blink/renderer/modules/wake_lock/wake_lock_manager.h
@@ -12,6 +12,7 @@ #include "third_party/blink/renderer/platform/heap/handle.h" #include "third_party/blink/renderer/platform/mojo/heap_mojo_remote.h" #include "third_party/blink/renderer/platform/mojo/heap_mojo_wrapper_mode.h" +#include "third_party/blink/renderer/platform/scheduler/public/frame_or_worker_scheduler.h" namespace blink { @@ -51,6 +52,11 @@ // ExecutionContext from which we will connect to |wake_lock_service_|. Member<ExecutionContext> execution_context_; + // Do not put a page into BackForwardCache if a page has acquired WakeLock. + // The page becomes cache-able when all locks are released. + FrameOrWorkerScheduler::SchedulingAffectingFeatureHandle + feature_handle_for_scheduler_; + FRIEND_TEST_ALL_PREFIXES(WakeLockManagerTest, AcquireWakeLock); FRIEND_TEST_ALL_PREFIXES(WakeLockManagerTest, ReleaseAllWakeLocks); FRIEND_TEST_ALL_PREFIXES(WakeLockManagerTest, ReleaseOneWakeLock);
diff --git a/third_party/blink/renderer/modules/webcodecs/fuzzer_utils.cc b/third_party/blink/renderer/modules/webcodecs/fuzzer_utils.cc index c9c72b2..c259ce9 100644 --- a/third_party/blink/renderer/modules/webcodecs/fuzzer_utils.cc +++ b/third_party/blink/renderer/modules/webcodecs/fuzzer_utils.cc
@@ -9,7 +9,9 @@ #include "third_party/blink/renderer/bindings/core/v8/script_function.h" #include "third_party/blink/renderer/bindings/core/v8/script_value.h" #include "third_party/blink/renderer/bindings/core/v8/v8_image_bitmap_options.h" +#include "third_party/blink/renderer/bindings/modules/v8/v8_encoded_audio_chunk_init.h" #include "third_party/blink/renderer/bindings/modules/v8/v8_encoded_audio_config.h" +#include "third_party/blink/renderer/bindings/modules/v8/v8_encoded_video_chunk_init.h" #include "third_party/blink/renderer/bindings/modules/v8/v8_video_decoder_config.h" #include "third_party/blink/renderer/bindings/modules/v8/v8_video_decoder_init.h" #include "third_party/blink/renderer/bindings/modules/v8/v8_video_encoder_config.h" @@ -17,8 +19,6 @@ #include "third_party/blink/renderer/core/html/canvas/image_data.h" #include "third_party/blink/renderer/core/imagebitmap/image_bitmap.h" #include "third_party/blink/renderer/core/typed_arrays/dom_array_buffer.h" -#include "third_party/blink/renderer/modules/webcodecs/encoded_audio_chunk_init.h" -#include "third_party/blink/renderer/modules/webcodecs/encoded_video_chunk_init.h" #include "third_party/blink/renderer/modules/webcodecs/fuzzer_inputs.pb.h" #include "third_party/blink/renderer/modules/webcodecs/video_frame.h" #include "third_party/blink/renderer/platform/bindings/exception_state.h"
diff --git a/third_party/blink/renderer/platform/graphics/compositing/content_layer_client_impl.h b/third_party/blink/renderer/platform/graphics/compositing/content_layer_client_impl.h index a9b13f4..5a1164a0 100644 --- a/third_party/blink/renderer/platform/graphics/compositing/content_layer_client_impl.h +++ b/third_party/blink/renderer/platform/graphics/compositing/content_layer_client_impl.h
@@ -34,8 +34,7 @@ gfx::Rect PaintableRegion() override { return gfx::Rect(raster_invalidator_.LayerBounds().size()); } - scoped_refptr<cc::DisplayItemList> PaintContentsToDisplayList( - PaintingControlSetting) override { + scoped_refptr<cc::DisplayItemList> PaintContentsToDisplayList() override { return cc_display_item_list_; } bool FillsBoundsCompletely() const override { return false; }
diff --git a/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.cc b/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.cc index 8a422bc5..1edd815 100644 --- a/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.cc +++ b/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.cc
@@ -1016,8 +1016,8 @@ path_ = path; } -scoped_refptr<cc::DisplayItemList> SynthesizedClip::PaintContentsToDisplayList( - PaintingControlSetting) { +scoped_refptr<cc::DisplayItemList> +SynthesizedClip::PaintContentsToDisplayList() { auto cc_list = base::MakeRefCounted<cc::DisplayItemList>( cc::DisplayItemList::kTopLevelDisplayItemList); PaintFlags flags;
diff --git a/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.h b/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.h index 147c8a5f..884a5f3 100644 --- a/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.h +++ b/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.h
@@ -99,8 +99,7 @@ bool FillsBoundsCompletely() const final { return false; } size_t GetApproximateUnsharedMemoryUsage() const final { return 0; } - scoped_refptr<cc::DisplayItemList> PaintContentsToDisplayList( - PaintingControlSetting) final; + scoped_refptr<cc::DisplayItemList> PaintContentsToDisplayList() final; private: scoped_refptr<cc::PictureLayer> layer_;
diff --git a/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor_test.cc b/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor_test.cc index ad71c43..f7e0a0ed 100644 --- a/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor_test.cc +++ b/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor_test.cc
@@ -4876,8 +4876,7 @@ .Build()); ASSERT_EQ(1u, LayerCount()); auto* layer = static_cast<cc::PictureLayer*>(LayerAt(0)); - auto display_item_list = layer->client()->PaintContentsToDisplayList( - cc::ContentLayerClient::PAINTING_BEHAVIOR_NORMAL); + auto display_item_list = layer->client()->PaintContentsToDisplayList(); // Change t1 but not t2. layer->ClearSubtreePropertyChangedForTesting(); @@ -4895,10 +4894,7 @@ ASSERT_EQ(1u, LayerCount()); ASSERT_EQ(layer, LayerAt(0)); EXPECT_EQ(display_item_list.get(), - layer->client() - ->PaintContentsToDisplayList( - cc::ContentLayerClient::PAINTING_BEHAVIOR_NORMAL) - .get()); + layer->client()->PaintContentsToDisplayList().get()); // TODO(wangxianzhu): Probably avoid setting this flag on transform change. EXPECT_TRUE(layer->subtree_property_changed()); // This is set by cc when propagating ancestor change flag to descendants. @@ -4925,10 +4921,7 @@ ASSERT_EQ(1u, LayerCount()); ASSERT_EQ(layer, LayerAt(0)); EXPECT_EQ(display_item_list.get(), - layer->client() - ->PaintContentsToDisplayList( - cc::ContentLayerClient::PAINTING_BEHAVIOR_NORMAL) - .get()); + layer->client()->PaintContentsToDisplayList().get()); // TODO(wangxianzhu): Probably avoid setting this flag on transform change. EXPECT_TRUE(layer->subtree_property_changed()); EXPECT_TRUE(GetTransformNode(layer).transform_changed); @@ -4952,10 +4945,7 @@ ASSERT_EQ(1u, LayerCount()); ASSERT_EQ(layer, LayerAt(0)); EXPECT_EQ(display_item_list.get(), - layer->client() - ->PaintContentsToDisplayList( - cc::ContentLayerClient::PAINTING_BEHAVIOR_NORMAL) - .get()); + layer->client()->PaintContentsToDisplayList().get()); // The new transform is decomposited, so there is no transform_changed, but // we set subtree_property_changed because offset_from_transform_parent // (calculated from the decomposited transforms) changed. @@ -4975,10 +4965,7 @@ ASSERT_EQ(1u, LayerCount()); ASSERT_EQ(layer, LayerAt(0)); EXPECT_NE(display_item_list.get(), - layer->client() - ->PaintContentsToDisplayList( - cc::ContentLayerClient::PAINTING_BEHAVIOR_NORMAL) - .get()); + layer->client()->PaintContentsToDisplayList().get()); } TEST_P(PaintArtifactCompositorTest, EffectChange) {
diff --git a/third_party/blink/renderer/platform/graphics/graphics_layer.cc b/third_party/blink/renderer/platform/graphics/graphics_layer.cc index dd12b90..eb4435b 100644 --- a/third_party/blink/renderer/platform/graphics/graphics_layer.cc +++ b/third_party/blink/renderer/platform/graphics/graphics_layer.cc
@@ -265,12 +265,15 @@ } void GraphicsLayer::PaintRecursively( - HashSet<const GraphicsLayer*>& repainted_layers) { - ForAllPaintingGraphicsLayers(*this, - [&repainted_layers](GraphicsLayer& layer) { - if (layer.Paint()) - repainted_layers.insert(&layer); - }); + HashSet<const GraphicsLayer*>& repainted_layers, + PaintBenchmarkMode benchmark_mode) { + ForAllPaintingGraphicsLayers( + *this, [benchmark_mode, &repainted_layers](GraphicsLayer& layer) { + PaintController::ScopedBenchmarkMode scoped_benchmark_mode( + layer.GetPaintController(), benchmark_mode); + if (layer.Paint()) + repainted_layers.insert(&layer); + }); #if DCHECK_IS_ON() if (!repainted_layers.IsEmpty()) { @@ -297,7 +300,9 @@ if (PaintWithoutCommit()) { GetPaintController().CommitNewDisplayItems(); UpdateShouldCreateLayersAfterPaint(); - } else if (!needs_check_raster_invalidation_) { + } else if (!needs_check_raster_invalidation_ && + GetPaintController().GetBenchmarkMode() != + PaintBenchmarkMode::kForceRasterInvalidationAndConvert) { return false; } @@ -312,6 +317,22 @@ raster_invalidation_function_, GetPaintController().GetPaintArtifactShared(), layer_bounds, layer_state_->state.Unalias(), this); + + base::Optional<RasterUnderInvalidationCheckingParams> + raster_under_invalidation_params; + if (RuntimeEnabledFeatures::PaintUnderInvalidationCheckingEnabled() && + PaintsContentOrHitTest()) { + raster_under_invalidation_params.emplace( + EnsureRasterInvalidator().EnsureTracking(), InterestRect(), + DebugName()); + } + + cc_display_item_list_ = PaintChunksToCcLayer::Convert( + GetPaintController().PaintChunks(), layer_state_->state.Unalias(), + gfx::Vector2dF(layer_state_->offset.X(), layer_state_->offset.Y()), + GetPaintController().GetPaintArtifact().GetDisplayItemList(), + cc::DisplayItemList::kTopLevelDisplayItemList, + base::OptionalOrNullptr(raster_under_invalidation_params)); } needs_check_raster_invalidation_ = false; @@ -374,9 +395,9 @@ interest_rect = &new_interest_rect; } - if (!GetPaintController().ShouldForcePaintForBenchmark() && - !client_.NeedsRepaint(*this) && - !GetPaintController().CacheIsAllInvalid() && + PaintController& paint_controller = GetPaintController(); + if (!paint_controller.ShouldForcePaintForBenchmark() && + !client_.NeedsRepaint(*this) && !paint_controller.CacheIsAllInvalid() && previous_interest_rect_ == *interest_rect) { GetPaintController().UpdateUMACountsOnFullyCached(); return false; @@ -384,8 +405,8 @@ GraphicsContext context(GetPaintController()); DCHECK(layer_state_) << "No layer state for GraphicsLayer: " << DebugName(); - GetPaintController().UpdateCurrentPaintChunkProperties(nullptr, - layer_state_->state); + paint_controller.UpdateCurrentPaintChunkProperties(nullptr, + layer_state_->state); previous_interest_rect_ = *interest_rect; client_.PaintContents(this, context, painting_phase_, *interest_rect); @@ -706,48 +727,9 @@ client_.GraphicsLayersDidChange(); } -scoped_refptr<cc::DisplayItemList> GraphicsLayer::PaintContentsToDisplayList( - PaintingControlSetting painting_control) { +scoped_refptr<cc::DisplayItemList> GraphicsLayer::PaintContentsToDisplayList() { DCHECK(!ShouldCreateLayersAfterPaint()); - TRACE_EVENT0("blink,benchmark", "GraphicsLayer::PaintContents"); - - if (painting_control == SUBSEQUENCE_CACHING_DISABLED) - PaintController::SetSubsequenceCachingDisabledForBenchmark(); - else if (painting_control == PARTIAL_INVALIDATION) - PaintController::SetPartialInvalidationForBenchmark(); - - PaintController& paint_controller = GetPaintController(); - // We also disable caching when Painting or Construction are disabled. In both - // cases we would like to compare assuming the full cost of recording, not the - // cost of re-using cached content. - if (painting_control == DISPLAY_LIST_CACHING_DISABLED) - paint_controller.InvalidateAll(); - - // Anything other than PAINTING_BEHAVIOR_NORMAL is for testing. In non-testing - // scenarios, it is an error to call GraphicsLayer::Paint. Actual painting - // occurs in LocalFrameView::PaintTree() which calls GraphicsLayer::Paint(); - // this method merely copies the painted output to the cc::DisplayItemList. - if (painting_control != PAINTING_BEHAVIOR_NORMAL) - Paint(); - - base::Optional<RasterUnderInvalidationCheckingParams> - raster_under_invalidation_params; - if (RuntimeEnabledFeatures::PaintUnderInvalidationCheckingEnabled() && - PaintsContentOrHitTest()) { - raster_under_invalidation_params.emplace( - EnsureRasterInvalidator().EnsureTracking(), InterestRect(), - DebugName()); - } - - PaintController::ClearFlagsForBenchmark(); - - DCHECK(layer_state_) << "No layer state for GraphicsLayer: " << DebugName(); - return PaintChunksToCcLayer::Convert( - GetPaintController().PaintChunks(), layer_state_->state.Unalias(), - gfx::Vector2dF(layer_state_->offset.X(), layer_state_->offset.Y()), - paint_controller.GetPaintArtifact().GetDisplayItemList(), - cc::DisplayItemList::kTopLevelDisplayItemList, - base::OptionalOrNullptr(raster_under_invalidation_params)); + return cc_display_item_list_; } size_t GraphicsLayer::GetApproximateUnsharedMemoryUsage() const {
diff --git a/third_party/blink/renderer/platform/graphics/graphics_layer.h b/third_party/blink/renderer/platform/graphics/graphics_layer.h index c441191..a5eb8db 100644 --- a/third_party/blink/renderer/platform/graphics/graphics_layer.h +++ b/third_party/blink/renderer/platform/graphics/graphics_layer.h
@@ -58,6 +58,7 @@ #include "third_party/skia/include/core/SkRefCnt.h" namespace cc { +class DisplayItemList; class PictureLayer; } // namespace cc @@ -180,7 +181,8 @@ PaintInvalidationReason); IntRect InterestRect(); - void PaintRecursively(HashSet<const GraphicsLayer*>& repainted_layers); + void PaintRecursively(HashSet<const GraphicsLayer*>& repainted_layers, + PaintBenchmarkMode = PaintBenchmarkMode::kNormal); // Returns true if this layer is repainted. bool Paint(); @@ -240,8 +242,7 @@ // cc::ContentLayerClient implementation. gfx::Rect PaintableRegion() final { return InterestRect(); } - scoped_refptr<cc::DisplayItemList> PaintContentsToDisplayList( - PaintingControlSetting painting_control) final; + scoped_refptr<cc::DisplayItemList> PaintContentsToDisplayList() final; bool FillsBoundsCompletely() const override { return false; } size_t GetApproximateUnsharedMemoryUsage() const final; @@ -297,6 +298,7 @@ scoped_refptr<cc::PictureLayer> layer_; scoped_refptr<cc::Layer> contents_layer_; + scoped_refptr<cc::DisplayItemList> cc_display_item_list_; SquashingDisallowedReasons squashing_disallowed_reasons_ = SquashingDisallowedReason::kNone;
diff --git a/third_party/blink/renderer/platform/graphics/paint/paint_controller.cc b/third_party/blink/renderer/platform/graphics/paint/paint_controller.cc index 353434f..700d609 100644 --- a/third_party/blink/renderer/platform/graphics/paint/paint_controller.cc +++ b/third_party/blink/renderer/platform/graphics/paint/paint_controller.cc
@@ -35,57 +35,15 @@ } } -// For micro benchmarks of record time. -static bool g_subsequence_caching_disabled = false; -static bool g_partial_invalidation = false; -static int g_partial_invalidation_display_item_count = 0; -static int g_partial_invalidation_subsequence_count = 0; - -// This is used to invalidate one out of every |kInvalidateDisplayItemInterval| -// display items for the micro benchmark of record time with partial -// invalidation. -static bool ShouldInvalidateDisplayItemForBenchmark() { - constexpr int kInvalidateDisplayItemInterval = 8; - return g_partial_invalidation && - !(g_partial_invalidation_display_item_count++ % - kInvalidateDisplayItemInterval); -} -// Similar to the above, but for subsequences. -static bool ShouldInvalidateSubsequenceForBenchmark() { - constexpr int kInvalidateSubsequenceInterval = 2; - return g_partial_invalidation && - !(g_partial_invalidation_subsequence_count++ % - kInvalidateSubsequenceInterval); -} - -void PaintController::SetSubsequenceCachingDisabledForBenchmark() { - g_subsequence_caching_disabled = true; -} - -void PaintController::SetPartialInvalidationForBenchmark() { - g_partial_invalidation = true; - g_partial_invalidation_display_item_count = 0; - g_partial_invalidation_subsequence_count = 0; -} - -bool PaintController::ShouldForcePaintForBenchmark() { - return g_subsequence_caching_disabled || g_partial_invalidation; -} - -void PaintController::ClearFlagsForBenchmark() { - g_subsequence_caching_disabled = false; - g_partial_invalidation = false; -} - bool PaintController::UseCachedItemIfPossible(const DisplayItemClient& client, DisplayItem::Type type) { if (usage_ == kTransient) return false; - if (!ClientCacheIsValid(client)) + if (ShouldInvalidateDisplayItemForBenchmark()) return false; - if (ShouldInvalidateDisplayItemForBenchmark()) + if (!ClientCacheIsValid(client)) return false; if (RuntimeEnabledFeatures::PaintUnderInvalidationCheckingEnabled() && @@ -132,9 +90,6 @@ if (usage_ == kTransient) return false; - if (g_subsequence_caching_disabled) - return false; - if (ShouldInvalidateSubsequenceForBenchmark()) return false; @@ -848,4 +803,38 @@ sum_num_cached_subsequences_ = 0; } +bool PaintController::ShouldInvalidateDisplayItemForBenchmark() { + if (benchmark_mode_ == PaintBenchmarkMode::kCachingDisabled) + return true; + + // For kPartialInvalidation, invalidate one out of every + // |kInvalidateDisplayItemInterval| display items for the micro benchmark of + // record time with partial invalidation. + constexpr int kInvalidateDisplayItemInterval = 8; + return benchmark_mode_ == PaintBenchmarkMode::kPartialInvalidation && + !(partial_invalidation_display_item_count_++ % + kInvalidateDisplayItemInterval); +} + +bool PaintController::ShouldInvalidateSubsequenceForBenchmark() { + if (benchmark_mode_ == PaintBenchmarkMode::kCachingDisabled || + benchmark_mode_ == PaintBenchmarkMode::kSubsequenceCachingDisabled) + return true; + + // Similar to the ShouldInvalidateDisplayItemsForBenchmark(), but for + // subsequences. + constexpr int kInvalidateSubsequenceInterval = 2; + return benchmark_mode_ == PaintBenchmarkMode::kPartialInvalidation && + !(partial_invalidation_subsequence_count_++ % + kInvalidateSubsequenceInterval); +} + +void PaintController::SetBenchmarkMode(PaintBenchmarkMode mode) { + benchmark_mode_ = mode; + if (mode == PaintBenchmarkMode::kPartialInvalidation) { + partial_invalidation_display_item_count_ = 0; + partial_invalidation_subsequence_count_ = 0; + } +} + } // namespace blink
diff --git a/third_party/blink/renderer/platform/graphics/paint/paint_controller.h b/third_party/blink/renderer/platform/graphics/paint/paint_controller.h index 5a80c63..0a718a8 100644 --- a/third_party/blink/renderer/platform/graphics/paint/paint_controller.h +++ b/third_party/blink/renderer/platform/graphics/paint/paint_controller.h
@@ -31,6 +31,18 @@ static constexpr wtf_size_t kInitialDisplayItemListCapacityBytes = 512; +enum class PaintBenchmarkMode { + kNormal, + kForceRasterInvalidationAndConvert, + kForcePaintArtifactCompositorUpdate, + kForcePaint, + // The above modes don't additionally invalidate paintings, i.e. during + // repeated benchmarking, the PaintController is fully cached. + kPartialInvalidation, + kSubsequenceCachingDisabled, + kCachingDisabled, +}; + // FrameFirstPaint stores first-paint, text or image painted for the // corresponding frame. They are never reset to false. First-paint is defined in // https://github.com/WICG/paint-timing. It excludes default background paint. @@ -218,11 +230,29 @@ return GetPaintArtifact().PaintChunks(); } - // For micro benchmarks of record time. - static void SetSubsequenceCachingDisabledForBenchmark(); - static void SetPartialInvalidationForBenchmark(); - static bool ShouldForcePaintForBenchmark(); - static void ClearFlagsForBenchmark(); + class ScopedBenchmarkMode { + STACK_ALLOCATED(); + + public: + ScopedBenchmarkMode(PaintController& paint_controller, + PaintBenchmarkMode mode) + : paint_controller_(paint_controller) { + // Nesting is not allowed. + DCHECK_EQ(PaintBenchmarkMode::kNormal, paint_controller_.benchmark_mode_); + paint_controller.SetBenchmarkMode(mode); + } + ~ScopedBenchmarkMode() { + paint_controller_.SetBenchmarkMode(PaintBenchmarkMode::kNormal); + } + + private: + PaintController& paint_controller_; + }; + + PaintBenchmarkMode GetBenchmarkMode() const { return benchmark_mode_; } + bool ShouldForcePaintForBenchmark() { + return benchmark_mode_ >= PaintBenchmarkMode::kForcePaint; + } void SetFirstPainted(); void SetTextPainted(); @@ -383,6 +413,10 @@ void UpdateUMACounts(); + void SetBenchmarkMode(PaintBenchmarkMode); + bool ShouldInvalidateDisplayItemForBenchmark(); + bool ShouldInvalidateSubsequenceForBenchmark(); + Usage usage_; // The last paint artifact after CommitNewDisplayItems(). @@ -452,6 +486,10 @@ wtf_size_t current_fragment_ = 0; + PaintBenchmarkMode benchmark_mode_ = PaintBenchmarkMode::kNormal; + int partial_invalidation_display_item_count_ = 0; + int partial_invalidation_subsequence_count_ = 0; + // Accumulated counts for UMA metrics. Updated by UpdateUMACounts() and // UpdateUMACountsOnFullyCached(), and reported as UMA metrics and reset by // ReportUMACounts(). The accumulation is mainly for pre-CompositeAfterPaint
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5 index f7734f43..f7c4a557 100644 --- a/third_party/blink/renderer/platform/runtime_enabled_features.json5 +++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -1006,7 +1006,7 @@ { name: "LayoutNGForControls", depends_on: ["LayoutNG"], - status: "experimental", + status: "stable", }, { name: "LayoutNGFragmentItem", @@ -1637,7 +1637,7 @@ // Enables the use of |RTCRtpTransceiver::stop()| { name: "RTCRtpTransceiverStop", - status: "experimental", + status: "stable", }, { name: "RTCStatsRelativePacketArrivalDelay",
diff --git a/third_party/blink/renderer/platform/scheduler/common/scheduling_policy.cc b/third_party/blink/renderer/platform/scheduler/common/scheduling_policy.cc index bd68c2f7..11758a5 100644 --- a/third_party/blink/renderer/platform/scheduler/common/scheduling_policy.cc +++ b/third_party/blink/renderer/platform/scheduler/common/scheduling_policy.cc
@@ -29,6 +29,7 @@ case Feature::kPortal: case Feature::kSpeechRecognizer: case Feature::kSpeechSynthesis: + case Feature::kWakeLock: return false; case Feature::kMainResourceHasCacheControlNoStore: case Feature::kMainResourceHasCacheControlNoCache: @@ -50,7 +51,6 @@ case Feature::kRequestedBackForwardCacheBlockedSensors: case Feature::kRequestedBackgroundWorkPermission: case Feature::kWebLocks: - case Feature::kWakeLock: case Feature::kRequestedStorageAccessGrant: case Feature::kWebNfc: case Feature::kWebFileSystem:
diff --git a/third_party/blink/renderer/platform/widget/compositing/layer_tree_view.cc b/third_party/blink/renderer/platform/widget/compositing/layer_tree_view.cc index 96c4c11..1c02cf2c 100644 --- a/third_party/blink/renderer/platform/widget/compositing/layer_tree_view.cc +++ b/third_party/blink/renderer/platform/widget/compositing/layer_tree_view.cc
@@ -40,10 +40,6 @@ #include "third_party/blink/public/platform/scheduler/web_thread_scheduler.h" #include "ui/gfx/presentation_feedback.h" -namespace base { -class Value; -} - namespace cc { class Layer; } @@ -344,6 +340,12 @@ first_scroll_timestamp); } +void LayerTreeView::RunPaintBenchmark(int repeat_count, + cc::PaintBenchmarkResult& result) { + if (delegate_) + delegate_->RunPaintBenchmark(repeat_count, result); +} + void LayerTreeView::DidScheduleBeginMainFrame() { if (!delegate_ || !web_main_thread_scheduler_) return;
diff --git a/third_party/blink/renderer/platform/widget/compositing/layer_tree_view.h b/third_party/blink/renderer/platform/widget/compositing/layer_tree_view.h index 85c7510..9438140 100644 --- a/third_party/blink/renderer/platform/widget/compositing/layer_tree_view.h +++ b/third_party/blink/renderer/platform/widget/compositing/layer_tree_view.h
@@ -12,7 +12,6 @@ #include "base/memory/weak_ptr.h" #include "base/single_thread_task_runner.h" #include "base/time/time.h" -#include "base/values.h" #include "cc/input/browser_controls_state.h" #include "cc/trees/layer_tree_host_client.h" #include "cc/trees/layer_tree_host_single_thread_client.h" @@ -107,6 +106,8 @@ void DidObserveFirstScrollDelay( base::TimeDelta first_scroll_delay, base::TimeTicks first_scroll_timestamp) override; + void RunPaintBenchmark(int repeat_count, + cc::PaintBenchmarkResult& result) override; // cc::LayerTreeHostSingleThreadClient implementation. void DidSubmitCompositorFrame() override;
diff --git a/third_party/blink/renderer/platform/widget/compositing/layer_tree_view_delegate.h b/third_party/blink/renderer/platform/widget/compositing/layer_tree_view_delegate.h index 680604a..52e3cdb 100644 --- a/third_party/blink/renderer/platform/widget/compositing/layer_tree_view_delegate.h +++ b/third_party/blink/renderer/platform/widget/compositing/layer_tree_view_delegate.h
@@ -115,6 +115,9 @@ // perform actual painting work. virtual void WillBeginMainFrame() = 0; + virtual void RunPaintBenchmark(int repeat_count, + cc::PaintBenchmarkResult& result) {} + protected: virtual ~LayerTreeViewDelegate() {} };
diff --git a/third_party/blink/renderer/platform/widget/widget_base.cc b/third_party/blink/renderer/platform/widget/widget_base.cc index 048460f..f7b9702 100644 --- a/third_party/blink/renderer/platform/widget/widget_base.cc +++ b/third_party/blink/renderer/platform/widget/widget_base.cc
@@ -512,6 +512,11 @@ UpdateTextInputState(); } +void WidgetBase::RunPaintBenchmark(int repeat_count, + cc::PaintBenchmarkResult& result) { + client_->RunPaintBenchmark(repeat_count, result); +} + void WidgetBase::SetCompositorVisible(bool visible) { if (never_composited_) return;
diff --git a/third_party/blink/renderer/platform/widget/widget_base.h b/third_party/blink/renderer/platform/widget/widget_base.h index 6514596..cb841555 100644 --- a/third_party/blink/renderer/platform/widget/widget_base.h +++ b/third_party/blink/renderer/platform/widget/widget_base.h
@@ -141,6 +141,8 @@ void EndUpdateLayers() override; void UpdateVisualState() override; void WillBeginMainFrame() override; + void RunPaintBenchmark(int repeat_count, + cc::PaintBenchmarkResult& result) override; cc::AnimationHost* AnimationHost() const; cc::LayerTreeHost* LayerTreeHost() const;
diff --git a/third_party/blink/renderer/platform/widget/widget_base_client.h b/third_party/blink/renderer/platform/widget/widget_base_client.h index 8de569c..2b462c49 100644 --- a/third_party/blink/renderer/platform/widget/widget_base_client.h +++ b/third_party/blink/renderer/platform/widget/widget_base_client.h
@@ -204,6 +204,9 @@ // Inform the widget that it was shown. virtual void WasShown(bool was_evicted) {} + + virtual void RunPaintBenchmark(int repeat_count, + cc::PaintBenchmarkResult& result) {} }; } // namespace blink
diff --git a/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py b/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py index e9fa69e7..db0f24ef 100755 --- a/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py +++ b/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py
@@ -599,6 +599,7 @@ 'cc::ActiveFrameSequenceTrackers', 'cc::ApplyViewportChangesArgs', 'cc::LayerTreeSettings', + 'cc::PaintBenchmarkResult', 'cc::TaskGraphRunner', 'gfx::DisplayColorSpaces', 'ui::ImeTextSpan', @@ -1172,8 +1173,10 @@ }, { 'paths': ['third_party/blink/renderer/core/frame/local_frame_view.cc'], - 'allowed': - ['cc::frame_viewer_instrumentation::IsTracingLayerTreeSnapshots'], + 'allowed': [ + 'base::LapTimer', + 'cc::frame_viewer_instrumentation::IsTracingLayerTreeSnapshots', + ], }, { 'paths': [
diff --git a/third_party/blink/web_tests/SlowTests b/third_party/blink/web_tests/SlowTests index b00ea4d..5c579df 100644 --- a/third_party/blink/web_tests/SlowTests +++ b/third_party/blink/web_tests/SlowTests
@@ -538,3 +538,5 @@ crbug.com/1104091 [ Linux ] webcodecs/videoframe-imagebitmap.html [ Slow ] crbug.com/1093478 external/wpt/quirks/unitless-length/limited-quirks.html [ Slow ] + +crbug.com/1133836 external/wpt/scroll-to-text-fragment/redirects.html [ Slow ]
diff --git a/third_party/blink/web_tests/external/wpt/svg/painting/mask-containing-image-with-clip-path.svg b/third_party/blink/web_tests/external/wpt/svg/painting/mask-containing-image-with-clip-path.svg new file mode 100644 index 0000000..faeec28 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/svg/painting/mask-containing-image-with-clip-path.svg
@@ -0,0 +1,31 @@ +<svg class="reftest-wait" xmlns="http://www.w3.org/2000/svg" + xmlns:html="http://www.w3.org/1999/xhtml"> + <html:script src="/common/reftest-wait.js"/> + <html:script src="/common/rendering-utils.js"/> + <html:link rel="match" href="../embedded/reference/green-rect-100x100.svg"/> + <script> +<![CDATA[ + function loadImage() { + waitForAtLeastOneFrame().then(() => { + var clip = document.getElementById('clip'); + clip.setAttribute('width', '100'); + clip.setAttribute('height', '100'); + waitForAtLeastOneFrame().then(takeScreenshot); + }); + } +]]> + </script> + <defs> + <mask id="mask"> + <g clip-path="url(#clip_path)"> + <image onload="loadImage()" width="100px" height="100px" href="support/white-rect-100x100.svg"/> + </g> + </mask> + <clipPath id="clip_path"> + <rect id="clip"/> + </clipPath> + </defs> + <g mask="url(#mask)"> + <rect width="100" height="100" fill="green"/> + </g> +</svg>
diff --git a/third_party/blink/web_tests/external/wpt/svg/painting/support/white-rect-100x100.svg b/third_party/blink/web_tests/external/wpt/svg/painting/support/white-rect-100x100.svg new file mode 100644 index 0000000..6ee3841 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/svg/painting/support/white-rect-100x100.svg
@@ -0,0 +1,3 @@ +<svg xmlns="http://www.w3.org/2000/svg"> + <rect width="100" height="100" fill="white"/> +</svg>
diff --git a/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt b/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt index c858492b..90b779b 100644 --- a/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt +++ b/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt
@@ -5583,6 +5583,7 @@ getter stopped method constructor method setCodecPreferences + method stop setter direction interface RTCSctpTransport : EventTarget attribute @@toStringTag
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml index 4fc9e54..91a366d 100644 --- a/tools/metrics/histograms/enums.xml +++ b/tools/metrics/histograms/enums.xml
@@ -24685,6 +24685,7 @@ <int value="1510" label="ACCESSIBILITY_PRIVATE_MOVEMAGNIFIERTORECT"/> <int value="1511" label="FILEMANAGERPRIVATE_SINGLEPARTITIONFORMAT"/> <int value="1512" label="TABS_REMOVECSS"/> + <int value="1513" label="IDENTITY_CLEARALLCACHEDAUTHTOKENS"/> </enum> <enum name="ExtensionIconState"> @@ -30822,6 +30823,9 @@ <int value="11" label="FORMAT_SUCCESS"/> <int value="12" label="FORMAT_FAIL"/> <int value="13" label="RENAME_FAIL"/> + <int value="14" label="PARTITION_START"/> + <int value="15" label="PARTITION_SUCCESS"/> + <int value="16" label="PARTITION_FAIL"/> </enum> <enum name="FileManagerNotificationUserAction">
diff --git a/tools/metrics/histograms/generate_expired_histograms_array.py b/tools/metrics/histograms/generate_expired_histograms_array.py index bd0b321..c6591c3 100755 --- a/tools/metrics/histograms/generate_expired_histograms_array.py +++ b/tools/metrics/histograms/generate_expired_histograms_array.py
@@ -233,7 +233,15 @@ arguments.major_branch_date_filepath: File path for base date. arguments.milestone_filepath: File path for milestone information. """ - descriptions = merge_xml.MergeFiles(histogram_paths.ALL_XMLS) + # TODO(sweilun): Assert that the |--inputs| is the same as + # |histogram_paths.ALL_XMLS| to make sure we have the most updated list of + # histogram descriptions. Otherwise, inform the cl owner to update the + # --inputs. + # assert histogram_paths.ALL_XMLS == arguments.inputs, "The --inputs is not " + # "sync with the most updated list of xmls. Please update the inputs in " + # "chrome/browser/metrics/BUILD.gn and ios/chrome/browser/metrics/BUILD.gn." + + descriptions = merge_xml.MergeFiles(arguments.inputs) with open(arguments.major_branch_date_filepath, "r") as date_file: branch_file_content = date_file.read() with open(arguments.milestone_filepath, "r") as milestone_file: @@ -278,6 +286,10 @@ "-m", required=True, help="A path to the file with the milestone information.") + arg_parser.add_argument( + "inputs", + nargs="+", + help="Paths to .xml files with histogram descriptions.") return arg_parser.parse_args()
diff --git a/tools/metrics/histograms/histograms_xml/arc/histograms.xml b/tools/metrics/histograms/histograms_xml/arc/histograms.xml index 3399fb0f..9c12b18 100644 --- a/tools/metrics/histograms/histograms_xml/arc/histograms.xml +++ b/tools/metrics/histograms/histograms_xml/arc/histograms.xml
@@ -874,7 +874,7 @@ <!-- Name completed by histogram_suffixes name="ArcPerformanceAppCategories" --> <owner>khmel@google.com</owner> - <owner>skuhne@google.com</owner> + <owner>camurcu@google.com</owner> <summary>Standard deviation for commit time delta from ideal time.</summary> </histogram> @@ -883,16 +883,36 @@ <!-- Name completed by histogram_suffixes name="ArcPerformanceAppCategories" --> <owner>khmel@google.com</owner> - <owner>skuhne@google.com</owner> + <owner>camurcu@google.com</owner> <summary>Render frames per second.</summary> </histogram> +<histogram name="Arc.Runtime.Performance.Generic.FrameTime" units="ms" + expires_after="2021-09-28"> + <owner>camurcu@google.com</owner> + <owner>khmel@google.com</owner> + <summary> + 95 percent of the frames in the first 5 minutes after app launch took + shorter time (in ms) than this value. + </summary> +</histogram> + +<histogram name="Arc.Runtime.Performance.Generic.Jankiness" units="%" + expires_after="2021-09-28"> + <owner>camurcu@google.com</owner> + <owner>khmel@google.com</owner> + <summary> + Percentage ratio of janky frames to total frames recorded in a 5 minute + interval. + </summary> +</histogram> + <histogram name="Arc.Runtime.Performance.RenderQuality" units="%" expires_after="2021-03-11"> <!-- Name completed by histogram_suffixes name="ArcPerformanceAppCategories" --> <owner>khmel@google.com</owner> - <owner>skuhne@google.com</owner> + <owner>camurcu@google.com</owner> <summary>Render quality with maximum 100%.</summary> </histogram>
diff --git a/tools/metrics/histograms/histograms_xml/gpu/histograms.xml b/tools/metrics/histograms/histograms_xml/gpu/histograms.xml index 3b26b772..1dd7dbf 100644 --- a/tools/metrics/histograms/histograms_xml/gpu/histograms.xml +++ b/tools/metrics/histograms/histograms_xml/gpu/histograms.xml
@@ -366,6 +366,17 @@ </summary> </histogram> +<histogram name="GPU.DirectComposition.DCLayer.YUVOverlayCount" + units="overlays" expires_after="2021-03-15"> + <owner>magchen@chromium.org</owner> + <owner>zmo@chromium.org</owner> + <summary> + The number of YUV overlays we are going to present in each frame if the + number is not 0. Recorded when the overlay processor is called for drawing a + frame. + </summary> +</histogram> + <histogram name="GPU.DirectComposition.DCLayerResult.Texture" enum="DCLayerResult" expires_after="2020-12-31"> <owner>sunnyps@chromium.org</owner>
diff --git a/tools/metrics/histograms/histograms_xml/histogram_suffixes_list.xml b/tools/metrics/histograms/histograms_xml/histogram_suffixes_list.xml index 709ae1e..87c4339 100644 --- a/tools/metrics/histograms/histograms_xml/histogram_suffixes_list.xml +++ b/tools/metrics/histograms/histograms_xml/histogram_suffixes_list.xml
@@ -13698,6 +13698,9 @@ <histogram_suffixes name="PermissionPromptDisposition" separator="."> <suffix name="AnchoredBubble" label="A bubble under the site settings padlock"/> + <suffix name="LocationBarLeftChip" + label="A chip on the left-hand side of the location bar that shows a + bubble when clicked"/> <suffix name="LocationBarRightAnimatedIcon" label="An animated indicator on the right-hand side of the location bar"/> <suffix name="LocationBarRightStaticIcon" @@ -13705,7 +13708,8 @@ <suffix name="MiniInfobar" label="An initially-collapsed infobar at the bottom of the page"/> <suffix name="ModalDialog" label="A modal dialog"/> - <suffix name="NotApplicable" label="No permission prompt"/> + <suffix name="NoneVisible" label="There was no UI being shown"/> + <suffix name="NotApplicable" label="No permission prompt at all"/> <affected-histogram name="Permissions.Action.WithDisposition"/> </histogram_suffixes>
diff --git a/tools/metrics/histograms/histograms_xml/ios/histograms.xml b/tools/metrics/histograms/histograms_xml/ios/histograms.xml index 8ca504d..ddaa28a 100644 --- a/tools/metrics/histograms/histograms_xml/ios/histograms.xml +++ b/tools/metrics/histograms/histograms_xml/ios/histograms.xml
@@ -700,6 +700,22 @@ </summary> </histogram> +<histogram name="IOS.SiriShortcuts.Count" units="shortcuts" + expires_after="2021-02-28"> + <owner>gujen@google.com</owner> + <owner>sebsg@chromium.org</owner> + <summary> + Counts the number of Chrome Siri Shortcuts that the user has created in the + Siri Shortcuts app. This is recorded once during startup. The histogram caps + at 20 shortcuts, which is an arbitrary but reasonable limit. Note that + shortcuts that have multiple actions are not counted if at least one action + isn't a Chrome-provided one. For example, a shortcut that opens URLs in + Chrome and then opens URLs in another app won't be counted. As such, this + metric undercounts the true number of Chrome shortcuts. This is a + restriction of the native Shortcuts API. + </summary> +</histogram> + <histogram name="IOS.Spotlight.Action" enum="IOSSpotlightAction" expires_after="2021-02-28"> <owner>eugenebut@chromium.org</owner>
diff --git a/tools/metrics/histograms/histograms_xml/others/histograms.xml b/tools/metrics/histograms/histograms_xml/others/histograms.xml index ee0e719d..e96d50f 100644 --- a/tools/metrics/histograms/histograms_xml/others/histograms.xml +++ b/tools/metrics/histograms/histograms_xml/others/histograms.xml
@@ -6862,6 +6862,17 @@ </summary> </histogram> +<histogram name="IsolatedPrerender.SpareRenderer.CountStartedOnSRP" + units="count" expires_after="M90"> + <owner>robertogden@chromium.org</owner> + <owner>ryansturm@chromium.org</owner> + <owner>tbansal@chromium.org</owner> + <summary> + Records the number of spare renderers that were attempted to be started on + the SRP, when the feature param is enabled. + </summary> +</histogram> + <histogram name="JSDialogs.OnBeforeUnloadStayVsLeave" enum="StayVsLeave" expires_after="M77"> <owner>avi@chromium.org</owner>
diff --git a/tools/metrics/histograms/histograms_xml/sharing/histograms.xml b/tools/metrics/histograms/histograms_xml/sharing/histograms.xml index 5a7059b0..222342c 100644 --- a/tools/metrics/histograms/histograms_xml/sharing/histograms.xml +++ b/tools/metrics/histograms/histograms_xml/sharing/histograms.xml
@@ -22,7 +22,7 @@ <histograms> <histogram name="Sharing.ClickToCallAppsToShow" units="apps" - expires_after="M88"> + expires_after="M91"> <!-- Name completed by histogram_suffixes name="SharingClickToCallUi" --> <owner>mvanouwerkerk@chromium.org</owner> @@ -34,7 +34,7 @@ </histogram> <histogram name="Sharing.ClickToCallDevicesToShow" units="devices" - expires_after="M88"> + expires_after="M91"> <!-- Name completed by histogram_suffixes name="SharingClickToCallUi" --> <owner>mvanouwerkerk@chromium.org</owner> @@ -46,7 +46,7 @@ </histogram> <histogram name="Sharing.ClickToCallDialerPresent" enum="BooleanPresent" - expires_after="M87"> + expires_after="M91"> <owner>mvanouwerkerk@chromium.org</owner> <owner>knollr@chromium.org</owner> <summary> @@ -57,7 +57,7 @@ </histogram> <histogram name="Sharing.ClickToCallDialogShown" enum="SharingDialogType" - expires_after="M88"> + expires_after="M91"> <owner>mvanouwerkerk@chromium.org</owner> <owner>knollr@chromium.org</owner> <summary> @@ -68,6 +68,9 @@ <histogram name="Sharing.ClickToCallPhoneNumberPrecompileTime" units="ms" expires_after="M87"> + <obsolete> + Removed in M87. + </obsolete> <owner>knollr@chromium.org</owner> <owner>mvanouwerkerk@chromium.org</owner> <summary> @@ -77,7 +80,7 @@ </histogram> <histogram name="Sharing.ClickToCallSelectedAppIndex" units="index" - expires_after="M88"> + expires_after="M91"> <!-- Name completed by histogram_suffixes name="SharingClickToCallUi" --> <owner>mvanouwerkerk@chromium.org</owner> @@ -89,7 +92,7 @@ </histogram> <histogram name="Sharing.ClickToCallSelectedDeviceIndex" units="index" - expires_after="M88"> + expires_after="M91"> <!-- Name completed by histogram_suffixes name="SharingClickToCallUi" --> <owner>mvanouwerkerk@chromium.org</owner>
diff --git a/tools/perf/measurements/rasterize_and_record_micro.py b/tools/perf/measurements/rasterize_and_record_micro.py index 75ab939..399404a 100644 --- a/tools/perf/measurements/rasterize_and_record_micro.py +++ b/tools/perf/measurements/rasterize_and_record_micro.py
@@ -64,34 +64,26 @@ data = tab.EvaluateJavaScript('window.benchmark_results.results') pixels_recorded = data['pixels_recorded'] - record_time = data['record_time_ms'] pixels_rasterized = data['pixels_rasterized'] - rasterize_time = data['rasterize_time_ms'] painter_memory_usage = data.get('painter_memory_usage', 0) paint_op_memory_usage = data.get('paint_op_memory_usage', 0) paint_op_count = data.get('paint_op_count', 0) results.AddMeasurement('pixels_recorded', 'count', pixels_recorded) results.AddMeasurement('pixels_rasterized', 'count', pixels_rasterized) - results.AddMeasurement('rasterize_time', 'ms', rasterize_time) - results.AddMeasurement('record_time', 'ms', record_time) results.AddMeasurement('painter_memory_usage', 'bytes', painter_memory_usage) results.AddMeasurement('paint_op_memory_usage', 'bytes', paint_op_memory_usage) results.AddMeasurement('paint_op_count', 'count', paint_op_count) - record_time_caching_disabled = data['record_time_caching_disabled_ms'] - record_time_subsequence_caching_disabled = \ - data['record_time_subsequence_caching_disabled_ms'] - record_time_partial_invalidation = \ - data['record_time_partial_invalidation_ms'] - results.AddMeasurement('record_time_caching_disabled', 'ms', - record_time_caching_disabled) - results.AddMeasurement('record_time_subsequence_caching_disabled', 'ms', - record_time_subsequence_caching_disabled) - results.AddMeasurement('record_time_partial_invalidation', 'ms', - record_time_partial_invalidation) + for metric in ('rasterize_time', 'record_time', + 'record_time_caching_disabled', + 'record_time_subsequence_caching_disabled', + 'record_time_partial_invalidation', + 'raster_invalidation_and_convert_time', + 'paint_artifact_compositor_update_time'): + results.AddMeasurement(metric, 'ms', data.get(metric + '_ms', 0)) if self._report_detailed_results: for metric in ('pixels_rasterized_with_non_solid_color',
diff --git a/ui/android/java/src/org/chromium/ui/base/ViewUtils.java b/ui/android/java/src/org/chromium/ui/base/ViewUtils.java index 3a5dc3c..3ddce06 100644 --- a/ui/android/java/src/org/chromium/ui/base/ViewUtils.java +++ b/ui/android/java/src/org/chromium/ui/base/ViewUtils.java
@@ -193,4 +193,26 @@ roundedIcon.setCornerRadius(cornerRadius); return roundedIcon; } + + /** + * Translates the canvas to ensure the specified view's coordinates are at 0, 0. + * + * @param from The view the canvas is currently translated to. + * @param to The view to translate to. + * @param canvas The canvas to be translated. + * + * @throws IllegalArgumentException if {@code from} is not an ancestor of {@code to}. + */ + public static void translateCanvasToView(View from, View to, Canvas canvas) + throws IllegalArgumentException { + assert from != null; + assert to != null; + while (to != from) { + canvas.translate(to.getLeft(), to.getTop()); + if (!(to.getParent() instanceof View)) { + throw new IllegalArgumentException("View 'to' was not a desendent of 'from'."); + } + to = (View) to.getParent(); + } + } }
diff --git a/ui/base/ui_base_features.cc b/ui/base/ui_base_features.cc index d35c5b6..feb7606d 100644 --- a/ui/base/ui_base_features.cc +++ b/ui/base/ui_base_features.cc
@@ -100,7 +100,8 @@ // native apps on Windows. const base::Feature kExperimentalFlingAnimation { "ExperimentalFlingAnimation", -#if defined(OS_WIN) || (defined(OS_LINUX) && !defined(OS_CHROMEOS)) +#if defined(OS_WIN) || \ + (defined(OS_LINUX) && !defined(OS_CHROMEOS) && !BUILDFLAG(IS_LACROS)) base::FEATURE_ENABLED_BY_DEFAULT #else base::FEATURE_DISABLED_BY_DEFAULT
diff --git a/ui/compositor/layer.cc b/ui/compositor/layer.cc index cbba087..6031c51 100644 --- a/ui/compositor/layer.cc +++ b/ui/compositor/layer.cc
@@ -1300,8 +1300,7 @@ return gfx::Rect(size()); } -scoped_refptr<cc::DisplayItemList> Layer::PaintContentsToDisplayList( - ContentLayerClient::PaintingControlSetting painting_control) { +scoped_refptr<cc::DisplayItemList> Layer::PaintContentsToDisplayList() { TRACE_EVENT1("ui", "Layer::PaintContentsToDisplayList", "name", name_); gfx::Rect local_bounds(bounds().size()); gfx::Rect invalidation(
diff --git a/ui/compositor/layer.h b/ui/compositor/layer.h index 8290f3f..805c313 100644 --- a/ui/compositor/layer.h +++ b/ui/compositor/layer.h
@@ -441,8 +441,7 @@ // ContentLayerClient implementation. gfx::Rect PaintableRegion() override; - scoped_refptr<cc::DisplayItemList> PaintContentsToDisplayList( - ContentLayerClient::PaintingControlSetting painting_control) override; + scoped_refptr<cc::DisplayItemList> PaintContentsToDisplayList() override; bool FillsBoundsCompletely() const override; size_t GetApproximateUnsharedMemoryUsage() const override;
diff --git a/ui/compositor/layer_unittest.cc b/ui/compositor/layer_unittest.cc index fc21b32..0de22be7 100644 --- a/ui/compositor/layer_unittest.cc +++ b/ui/compositor/layer_unittest.cc
@@ -1431,8 +1431,7 @@ EXPECT_EQ(bound1, root->damaged_region_for_testing()); root->SendDamagedRects(); EXPECT_EQ(gfx::Rect(), root->cc_layer_for_testing()->update_rect()); - root->PaintContentsToDisplayList( - cc::ContentLayerClient::PAINTING_BEHAVIOR_NORMAL); + root->PaintContentsToDisplayList(); EXPECT_EQ(gfx::Rect(), LastInvalidation()); // During deferring paint request, a new invalid_rect will be accumulated. @@ -1443,8 +1442,7 @@ EXPECT_EQ(bound_union, root->damaged_region_for_testing().bounds()); root->SendDamagedRects(); EXPECT_EQ(gfx::Rect(), root->cc_layer_for_testing()->update_rect()); - root->PaintContentsToDisplayList( - cc::ContentLayerClient::PAINTING_BEHAVIOR_NORMAL); + root->PaintContentsToDisplayList(); EXPECT_EQ(gfx::Rect(), LastInvalidation()); // Remove deferring paint request. @@ -1454,8 +1452,7 @@ // paint, i.e. union of bound1 and bound2. root->SendDamagedRects(); EXPECT_EQ(bound_union, root->cc_layer_for_testing()->update_rect()); - root->PaintContentsToDisplayList( - cc::ContentLayerClient::PAINTING_BEHAVIOR_NORMAL); + root->PaintContentsToDisplayList(); EXPECT_EQ(bound_union, LastInvalidation()); }
diff --git a/ui/file_manager/file_manager/background/js/device_handler.js b/ui/file_manager/file_manager/background/js/device_handler.js index ccec60c..778e55f 100644 --- a/ui/file_manager/file_manager/background/js/device_handler.js +++ b/ui/file_manager/file_manager/background/js/device_handler.js
@@ -67,6 +67,11 @@ case 'format_fail': this.handleFormatEvent_(event); break; + case 'partition_start': + case 'partition_success': + case 'partition_fail': + this.handlePartitionEvent_(event); + break; case 'rename_fail': DeviceHandler.Notification.RENAME_FAIL.show(event.devicePath); break; @@ -122,6 +127,46 @@ } /** + * Handles partition events and displays a notification in the progress + * center. As the partitioning is the first part of SinglePartitionFormat + * operation, just show errors that would stop the operation. Other part + * handled in format event flow. + * @param {chrome.fileManagerPrivate.DeviceEvent} event Device event. + * @private + */ + handlePartitionEvent_(event) { + const item = new ProgressCenterItem(); + item.id = 'partition:' + event.devicePath; + item.type = ProgressItemType.PARTITION; + item.itemCount = 1; + item.progressMax = 1; + + let notificationType; + switch (event.type) { + case 'partition_start': + case 'partition_success': + // No op for start/success. + return; + case 'partition_fail': + item.state = ProgressItemState.ERROR; + item.message = strf('FORMAT_FAILURE_MESSAGE', event.deviceLabel); + item.progressValue = 0; + notificationType = DeviceHandler.Notification.Type.PARTITION_FAIL; + break; + default: + console.error('Unknown partition event type: ' + event.type); + break; + } + + this.progressCenter_.updateItem(item); + + requestIdleCallback( + () => metrics.recordEnum( + 'Notification.Show', notificationType, + DeviceHandler.Notification.TypesForUMA)); + } + + /** * Handles mount completed events to show notifications for removable devices. * @param {chrome.fileManagerPrivate.MountCompletedEvent} event Mount * completed event. @@ -632,6 +677,9 @@ FORMAT_SUCCESS: 'format_success', FORMAT_FAIL: 'format_fail', RENAME_FAIL: 'rename_fail', + PARTITION_START: 'partition_start', + PARTITION_SUCCESS: 'partition_success', + PARTITION_FAIL: 'partition_fail', }; /** @@ -656,6 +704,9 @@ DeviceHandler.Notification.Type.FORMAT_SUCCESS, DeviceHandler.Notification.Type.FORMAT_FAIL, DeviceHandler.Notification.Type.RENAME_FAIL, + DeviceHandler.Notification.Type.PARTITION_START, + DeviceHandler.Notification.Type.PARTITION_SUCCESS, + DeviceHandler.Notification.Type.PARTITION_FAIL, ]); console.assert( Object.keys(DeviceHandler.Notification.Type).length ===
diff --git a/ui/file_manager/file_manager/background/js/device_handler_unittest.js b/ui/file_manager/file_manager/background/js/device_handler_unittest.js index c8fb211..5a3dd57 100644 --- a/ui/file_manager/file_manager/background/js/device_handler_unittest.js +++ b/ui/file_manager/file_manager/background/js/device_handler_unittest.js
@@ -607,6 +607,41 @@ progressCenter.getItemById('format:/device/path').message); } +function testPartitionSucceeded() { + mockChrome.fileManagerPrivate.onDeviceChanged.dispatch({ + type: 'partition_start', + devicePath: '/device/path', + deviceLabel: 'label' + }); + assertEquals(0, progressCenter.getItemCount()); + + mockChrome.fileManagerPrivate.onDeviceChanged.dispatch({ + type: 'partition_success', + devicePath: '/device/path', + deviceLabel: 'label' + }); + assertEquals(0, progressCenter.getItemCount()); +} + +function testPartitionFailed() { + mockChrome.fileManagerPrivate.onDeviceChanged.dispatch({ + type: 'partition_start', + devicePath: '/device/path', + deviceLabel: 'label' + }); + assertEquals(0, progressCenter.getItemCount()); + + mockChrome.fileManagerPrivate.onDeviceChanged.dispatch({ + type: 'partition_fail', + devicePath: '/device/path', + deviceLabel: 'label' + }); + assertEquals(1, progressCenter.getItemCount()); + assertEquals( + 'FORMAT_FAILURE_MESSAGE: label', + progressCenter.getItemById('partition:/device/path').message); +} + function testRenameSucceeded() { mockChrome.fileManagerPrivate.onDeviceChanged.dispatch( {type: 'rename_start', devicePath: '/device/path'});
diff --git a/ui/file_manager/file_manager/common/js/progress_center_common.js b/ui/file_manager/file_manager/common/js/progress_center_common.js index 4e77d7d..0b88481 100644 --- a/ui/file_manager/file_manager/common/js/progress_center_common.js +++ b/ui/file_manager/file_manager/common/js/progress_center_common.js
@@ -52,7 +52,9 @@ // The item is external drive format operation. FORMAT: 'format', // The item is archive operation. - MOUNT_ARCHIVE: 'mount_archive' + MOUNT_ARCHIVE: 'mount_archive', + // The item is external drive partitioning operation. + PARTITION: 'partition' }; Object.freeze(ProgressItemType);
diff --git a/ui/file_manager/file_manager/foreground/elements/files_format_dialog.html b/ui/file_manager/file_manager/foreground/elements/files_format_dialog.html index f009fd4..d4655970 100644 --- a/ui/file_manager/file_manager/foreground/elements/files_format_dialog.html +++ b/ui/file_manager/file_manager/foreground/elements/files_format_dialog.html
@@ -54,12 +54,13 @@ } </style> - <cr-dialog id="dialog" close-text="[[i18n('CLOSE_LABEL')]]"> + <cr-dialog id="dialog" close-text="[[i18n('CLOSE_LABEL')]]" + single-partition-format$="[[getSinglePartitionFormat()]]"> <div slot="title"> - [[i18n('FORMAT_DIALOG_TITLE', volumeInfo_.label)]] + [[i18n('FORMAT_DIALOG_TITLE', title)]] </div> <div slot="body"> - <div>[[i18n('FORMAT_DIALOG_MESSAGE')]]</div> + <div>[[getDialogMessage_(isErase_)]]</div> <div id="warning-container" hidden="[[!space_used_]]" role="alert"> <iron-icon id="warning-icon" icon="cr:warning"></iron-icon> <div id="warning-message"> @@ -87,7 +88,7 @@ </cr-button> <cr-button class="action-button" on-click="format_" id="format-button"> - [[i18n('FORMAT_DIALOG_CONFIRM_LABEL')]] + [[getConfirmLabel_(isErase_)]] </cr-button> </div> </cr-dialog>
diff --git a/ui/file_manager/file_manager/foreground/elements/files_format_dialog.js b/ui/file_manager/file_manager/foreground/elements/files_format_dialog.js index 5786a6f..56e7f12 100644 --- a/ui/file_manager/file_manager/foreground/elements/files_format_dialog.js +++ b/ui/file_manager/file_manager/foreground/elements/files_format_dialog.js
@@ -22,6 +22,11 @@ space_used_: { type: String, value: '', + }, + + isErase_: { + type: Boolean, + value: false, } }, @@ -42,21 +47,79 @@ this.$.label.invalid = true; return; } - chrome.fileManagerPrivate.formatVolume( - this.volumeInfo_.volumeId, this.formatType_, this.label_); + + if (this.isErase_) { + chrome.fileManagerPrivate.singlePartitionFormat( + this.root_.devicePath_, this.formatType_, this.label_); + } else { + chrome.fileManagerPrivate.formatVolume( + this.volumeInfo_.volumeId, this.formatType_, this.label_); + } this.$.dialog.close(); }, /** + * Used to set "single-partition-format" attribute on element. + * It is used to check flag status in the tests. + * @return {string} + * + * @private + */ + getSinglePartitionFormat() { + if (util.isSinglePartitionFormatEnabled()) { + return 'single-partition-format'; + } + return ''; + }, + + /** + * @param {!boolean} is_erase + * @return {string} + * + * @private + */ + getConfirmLabel_: function(is_erase) { + if (util.isSinglePartitionFormatEnabled()) { + if (is_erase) { + return this.i18n('REPARTITION_DIALOG_CONFIRM_LABEL'); + } else { + return this.i18n('FORMAT_DIALOG_CONFIRM_SHORT_LABEL'); + } + } else { + return this.i18n('FORMAT_DIALOG_CONFIRM_LABEL'); + } + }, + + /** + * @param {!boolean} is_erase + * @return {string} + * + * @private + */ + getDialogMessage_: function(is_erase) { + if (util.isSinglePartitionFormatEnabled()) { + if (is_erase) { + return this.i18n('REPARTITION_DIALOG_MESSAGE'); + } else { + return this.i18n('FORMAT_PARTITION_DIALOG_MESSAGE'); + } + } else { + return this.i18n('FORMAT_DIALOG_MESSAGE'); + } + }, + + /** * Shows the dialog for drive represented by |volumeInfo|. * @param {!VolumeInfo} volumeInfo */ showModal: function(volumeInfo) { + this.isErase_ = false; this.label_ = ''; this.formatType_ = chrome.fileManagerPrivate.FormatFileSystemType.VFAT; this.space_used_ = ''; this.volumeInfo_ = volumeInfo; + this.title = this.volumeInfo_.label; if (volumeInfo.displayRoot) { chrome.fileManagerPrivate.getDirectorySize( volumeInfo.displayRoot, space_used_ => { @@ -71,4 +134,44 @@ this.$.dialog.showModal(); }, + + /** + * Shows the dialog for erasing device. + * @param {!EntryList} root + */ + showEraseModal: function(root) { + this.isErase_ = true; + this.label_ = ''; + this.formatType_ = chrome.fileManagerPrivate.FormatFileSystemType.VFAT; + this.space_used_ = ''; + + this.root_ = root; + this.title = root.label; + const childVolumes = + /** @type {Array<VolumeEntry>} */ (this.root_.getUIChildren()); + let totalSpaceUsed = 0; + + const getSpaceUsedRequests = childVolumes.map((childVolume) => { + return new Promise((resolve) => { + const volumeInfo = childVolume.volumeInfo; + if (volumeInfo.displayRoot) { + chrome.fileManagerPrivate.getDirectorySize( + volumeInfo.displayRoot, space_used_ => { + totalSpaceUsed += space_used_; + if (totalSpaceUsed > 0) { + this.space_used_ = util.bytesToString(totalSpaceUsed); + } + resolve(); + }); + } + }); + }); + + Promise.all(getSpaceUsedRequests).then(() => { + if (window.IN_TEST) { + this.$['warning-container'].setAttribute('fully-initialized', ''); + } + }); + this.$.dialog.showModal(); + }, });
diff --git a/ui/file_manager/file_manager/foreground/js/file_manager_commands.js b/ui/file_manager/file_manager/foreground/js/file_manager_commands.js index 8a67c26..4948ece 100644 --- a/ui/file_manager/file_manager/foreground/js/file_manager_commands.js +++ b/ui/file_manager/file_manager/foreground/js/file_manager_commands.js
@@ -384,6 +384,28 @@ return false; }; + +/** + * Extracts entry on which command event was dispatched. + * + * @param {!Event} event Command event to mark. + * @param {!CommandHandlerDeps} fileManager CommandHandlerDeps to use. + * @return {Entry|FilesAppDirEntry} Entry of the event node. + */ +CommandUtil.getEventEntry = (event, fileManager) => { + let entry; + if (fileManager.ui.directoryTree.contains( + /** @type {Node} */ (event.target))) { + // The command is executed from the directory tree context menu. + entry = CommandUtil.getCommandEntry(fileManager, event.target); + } else { + // The command is executed from the gear menu. + entry = fileManager.directoryModel.getCurrentDirEntry(); + } + return entry; +}; + + /** * Handle of the command events. */ @@ -737,7 +759,59 @@ const removableRoot = location && isRoot && location.rootType === VolumeManagerCommon.RootType.REMOVABLE; event.canExecute = removableRoot && (isUnrecognizedVolume || writable); - event.command.setHidden(!removableRoot); + + if (util.isSinglePartitionFormatEnabled()) { + let isDevice = false; + if (root && root instanceof EntryList) { + // root entry is device node if it has child (partition). + isDevice = !!removableRoot && root.getUIChildren().length > 0; + } + // Disable format command on device when SinglePartitionFormat on, + // erase command will be available. + event.command.setHidden(!removableRoot || isDevice); + } else { + event.command.setHidden(!removableRoot); + } + } +}; + +/** + * Deletes removable device partition, creates single partition and formats it. + */ +CommandHandler.COMMANDS_['erase-device'] = new class extends Command { + execute(event, fileManager) { + const root = CommandUtil.getEventEntry(event, fileManager); + + if (root && root instanceof EntryList) { + /** @type {FilesFormatDialogElement} */ (fileManager.ui.formatDialog) + .showEraseModal(root); + } + } + + /** @override */ + canExecute(event, fileManager) { + if (!util.isSinglePartitionFormatEnabled()) { + event.canExecute = false; + event.command.setHidden(true); + return; + } + const root = CommandUtil.getEventEntry(event, fileManager); + const location = root && fileManager.volumeManager.getLocationInfo(root); + const writable = location && !location.isReadOnly; + const isRoot = location && location.isRootEntry; + + const removableRoot = location && isRoot && + location.rootType === VolumeManagerCommon.RootType.REMOVABLE; + + let isDevice = false; + if (root && root instanceof EntryList) { + // root entry is device node if it has child (partition). + isDevice = !!removableRoot && root.getUIChildren().length > 0; + } + + event.canExecute = removableRoot && !writable; + // Enable the command if this is a removable and device node. + event.command.setHidden(!removableRoot || !isDevice); } };
diff --git a/ui/file_manager/file_manager/foreground/js/navigation_list_model.js b/ui/file_manager/file_manager/foreground/js/navigation_list_model.js index d7d6b9fe..21175a1d 100644 --- a/ui/file_manager/file_manager/foreground/js/navigation_list_model.js +++ b/ui/file_manager/file_manager/foreground/js/navigation_list_model.js
@@ -717,7 +717,8 @@ removableGroup[0].volumeInfo.driveLabel : /*default*/ 'External Drive'; removableEntry = new EntryList( - rootLabel, VolumeManagerCommon.RootType.REMOVABLE, devicePath); + rootLabel, VolumeManagerCommon.RootType.REMOVABLE, + removableGroup[0].volumeInfo.devicePath); removableModel = new NavigationModelFakeItem( removableEntry.label, NavigationModelItemType.ENTRY_LIST, removableEntry);
diff --git a/ui/file_manager/file_manager/foreground/js/ui/BUILD.gn b/ui/file_manager/file_manager/foreground/js/ui/BUILD.gn index d983cab..b2dc391 100644 --- a/ui/file_manager/file_manager/foreground/js/ui/BUILD.gn +++ b/ui/file_manager/file_manager/foreground/js/ui/BUILD.gn
@@ -285,6 +285,7 @@ ":search_box", ":suggest_apps_dialog", "//ui/file_manager/file_manager/common/js:util", + "//ui/file_manager/file_manager/foreground/elements:files_format_dialog", "//ui/file_manager/file_manager/foreground/elements:files_message", "//ui/file_manager/file_manager/foreground/elements:files_password_dialog", "//ui/file_manager/file_manager/foreground/elements:files_spinner",
diff --git a/ui/file_manager/file_manager/main.html b/ui/file_manager/file_manager/main.html index 9d27675c3..830284fd 100644 --- a/ui/file_manager/file_manager/main.html +++ b/ui/file_manager/file_manager/main.html
@@ -111,6 +111,7 @@ <command id="unmount" label="$i18n{UNMOUNT_DEVICE_BUTTON_LABEL}" shortcut="E|Shift|Ctrl"> <command id="format" label="$i18n{FORMAT_DEVICE_BUTTON_LABEL}"> + <command id="erase-device" label="$i18n{REPARTITION_DEVICE_BUTTON_LABEL}"> <command id="configure" label="$i18n{CONFIGURE_VOLUME_BUTTON_LABEL}"> <command id="volume-help" label="$i18n{DRIVE_MENU_HELP}"> @@ -223,6 +224,7 @@ <cr-menu-item command="#configure"></cr-menu-item> <cr-menu-item command="#unmount"></cr-menu-item> <cr-menu-item command="#format"></cr-menu-item> + <cr-menu-item command="#erase-device"></cr-menu-item> <cr-menu-item command="#rename"></cr-menu-item> <cr-menu-item command="#unpin-folder"></cr-menu-item> <cr-menu-item command="#share-with-linux"></cr-menu-item> @@ -297,6 +299,7 @@ <cr-menu-item id="gear-menu-newservice" command="#new-service" sub-menu="#add-new-services-menu"></cr-menu-item> <cr-menu-item id="gear-menu-format" command="#format"></cr-menu-item> + <cr-menu-item id="gear-menu-erase" command="#erase-device"></cr-menu-item> <cr-menu-item id="volume-space-info" command="#volume-storage"> <div id="volume-space-info-contents"> <span id="volume-space-info-label"></span>
diff --git a/ui/file_manager/image_loader/piex/tests.js b/ui/file_manager/image_loader/piex/tests.js index dc61d48f..5e0f23b 100644 --- a/ui/file_manager/image_loader/piex/tests.js +++ b/ui/file_manager/image_loader/piex/tests.js
@@ -43,8 +43,14 @@ console.log(puppeteer.defaultArgs()); } + let args = []; + if (process.platform === 'linux') { + args = ['--no-sandbox']; + } + const browser = await puppeteer.launch({ - headless: !program.debug + headless: !program.debug, + args: [...args] }); const page = await browser.newPage();
diff --git a/ui/file_manager/integration_tests/file_manager/background.js b/ui/file_manager/integration_tests/file_manager/background.js index d98bc9a..becb357 100644 --- a/ui/file_manager/integration_tests/file_manager/background.js +++ b/ui/file_manager/integration_tests/file_manager/background.js
@@ -649,3 +649,14 @@ const cssClass = body.attributes['class'] || ''; return cssClass.includes('files-ng'); } + +/** + * Returns true if the SinglePartitionFormat flag is on. + * @param {string} appId Files app windowId. + */ +async function isSinglePartitionFormat(appId) { + const dialog = await remoteCall.waitForElement( + appId, ['files-format-dialog', 'cr-dialog']); + const flag = dialog.attributes['single-partition-format'] || ''; + return !!flag; +}
diff --git a/ui/file_manager/integration_tests/file_manager/directory_tree_context_menu.js b/ui/file_manager/integration_tests/file_manager/directory_tree_context_menu.js index 197ac3c..923c77a 100644 --- a/ui/file_manager/integration_tests/file_manager/directory_tree_context_menu.js +++ b/ui/file_manager/integration_tests/file_manager/directory_tree_context_menu.js
@@ -1278,6 +1278,33 @@ ['#delete', true], ['#new-folder', true], ]; + const ext4DeviceMenus = [ + ['#unmount', true], + ['#erase-device', true], + ['#share-with-linux', true], + ]; + const ext4PartitionMenus = [ + ['#share-with-linux', true], + ['#format', true], + ['#rename', false], + ['#new-folder', true], + ]; + const ntfsDeviceMenus = [ + ['#unmount', true], + ['#erase-device', true], + ['#share-with-linux', true], + ]; + const ntfsPartitionMenus = [ + ['#share-with-linux', true], + ['#format', true], + ['#rename', true], + ['#new-folder', true], + ]; + const deviceMenus = [ + ['#unmount', true], + ['#erase-device', true], + ['#share-with-linux', true], + ]; // Mount removable volumes. await sendTestMessage({name: 'mountUsbWithPartitions'}); @@ -1287,34 +1314,75 @@ const appId = await setupAndWaitUntilReady( RootPath.DOWNLOADS, [ENTRIES.beautiful], []); - // Check the context menu for single partition ext4 USB. - await checkContextMenu( - appId, '/fake-usb', ext4UsbMenus, true /* rootMenu */); + if (await isSinglePartitionFormat(appId)) { + // Check the context menu for single partition drive. + await checkContextMenu( + appId, '/FAKEUSB', ext4DeviceMenus, true /* rootMenu */); - // Check the context menu for a folder inside a single USB partition. - await checkContextMenu( - appId, '/fake-usb/A', folderMenus, false /* rootMenu */); + // Check the context menu for single partition ext4 USB. + await checkContextMenu( + appId, '/FAKEUSB/fake-usb', ext4PartitionMenus, false /* rootMenu */); - // Check the context menu for multiple partitions USB (root). - await checkContextMenu( - appId, '/Drive Label', partitionsRootMenus, true /* rootMenu */); + // Check the context menu for a folder inside a single USB partition. + await checkContextMenu( + appId, '/FAKEUSB/fake-usb/A', folderMenus, false /* rootMenu */); - // Check the context menu for multiple partitions USB (actual partition). - await checkContextMenu( - appId, '/Drive Label/partition-1', partition1Menus, - false /* rootMenu */); + // Check the context menu for multiple partitions USB (root). + await checkContextMenu( + appId, '/Drive Label', deviceMenus, true /* rootMenu */); - // Check the context menu for a folder inside a partition1. - await checkContextMenu( - appId, '/Drive Label/partition-1/A', folderMenus, false /* rootMenu */); + // Check the context menu for multiple partitions USB (actual partition). + await checkContextMenu( + appId, '/Drive Label/partition-1', partition1Menus, + false /* rootMenu */); - // Remount the single partition ext4 USB as NTFS - await sendTestMessage({name: 'unmountUsb'}); - await sendTestMessage({name: 'mountFakeUsb', filesystem: 'ntfs'}); + // Check the context menu for a folder inside a partition1. + await checkContextMenu( + appId, '/Drive Label/partition-1/A', folderMenus, + false /* rootMenu */); - // Check the context menu for a single partition NTFS USB. - await checkContextMenu( - appId, '/fake-usb', ntfsUsbMenus, true /* rootMenu */); + // Remount the single partition ext4 USB as NTFS + await sendTestMessage({name: 'unmountUsb'}); + await sendTestMessage({name: 'mountFakeUsb', filesystem: 'ntfs'}); + + // Check the context menu for a single partition NTFS USB. + await checkContextMenu( + appId, '/FAKEUSB', ntfsDeviceMenus, true /* rootMenu */); + + // Check the context menu for a single partition NTFS USB. + await checkContextMenu( + appId, '/FAKEUSB/fake-usb', ntfsPartitionMenus, false /* rootMenu */); + } else { + // Check the context menu for single partition ext4 USB. + await checkContextMenu( + appId, '/fake-usb', ext4UsbMenus, true /* rootMenu */); + + // Check the context menu for a folder inside a single USB partition. + await checkContextMenu( + appId, '/fake-usb/A', folderMenus, false /* rootMenu */); + + // Check the context menu for multiple partitions USB (root). + await checkContextMenu( + appId, '/Drive Label', partitionsRootMenus, true /* rootMenu */); + + // Check the context menu for multiple partitions USB (actual partition). + await checkContextMenu( + appId, '/Drive Label/partition-1', partition1Menus, + false /* rootMenu */); + + // Check the context menu for a folder inside a partition1. + await checkContextMenu( + appId, '/Drive Label/partition-1/A', folderMenus, + false /* rootMenu */); + + // Remount the single partition ext4 USB as NTFS + await sendTestMessage({name: 'unmountUsb'}); + await sendTestMessage({name: 'mountFakeUsb', filesystem: 'ntfs'}); + + // Check the context menu for a single partition NTFS USB. + await checkContextMenu( + appId, '/fake-usb', ntfsUsbMenus, true /* rootMenu */); + } }; /** @@ -1336,6 +1404,12 @@ ['#delete', true], ['#new-folder', true], ]; + const deviceUsbMenus = [ + ['#share-with-linux', true], + ['#format', true], + ['#rename', false], + ['#new-folder', true], + ]; // Mount removable volumes. await sendTestMessage({name: 'mountFakeUsbDcim'}); @@ -1344,12 +1418,23 @@ const appId = await setupAndWaitUntilReady( RootPath.DOWNLOADS, [ENTRIES.beautiful], []); - // Check the context menu for single partition USB. - await checkContextMenu(appId, '/fake-usb', usbMenus, true /* rootMenu */); + if (await isSinglePartitionFormat(appId)) { + // Check the context menu for single partition USB. + await checkContextMenu( + appId, '/FAKEUSB/fake-usb', deviceUsbMenus, false /* rootMenu */); - // Check the context menu for the DCIM folder inside USB. - await checkContextMenu( - appId, '/fake-usb/DCIM', dcimFolderMenus, false /* rootMenu */); + // Check the context menu for the DCIM folder inside USB. + await checkContextMenu( + appId, '/FAKEUSB/fake-usb/DCIM', dcimFolderMenus, + false /* rootMenu */); + } else { + // Check the context menu for single partition USB. + await checkContextMenu(appId, '/fake-usb', usbMenus, true /* rootMenu */); + + // Check the context menu for the DCIM folder inside USB. + await checkContextMenu( + appId, '/fake-usb/DCIM', dcimFolderMenus, false /* rootMenu */); + } }; /*
diff --git a/ui/file_manager/integration_tests/file_manager/drive_specific.js b/ui/file_manager/integration_tests/file_manager/drive_specific.js index 556912b..3f62497 100644 --- a/ui/file_manager/integration_tests/file_manager/drive_specific.js +++ b/ui/file_manager/integration_tests/file_manager/drive_specific.js
@@ -438,9 +438,14 @@ // Wait for the USB mount. await remoteCall.waitForElement(appId, USB_VOLUME_QUERY); - // Navigate to the DCIM directory. - await remoteCall.navigateWithDirectoryTree( - appId, '/DCIM', 'fake-usb', 'removable'); + if (await isSinglePartitionFormat(appId)) { + // Navigate to the DCIM directory. + await navigateWithDirectoryTree(appId, '/FAKEUSB/fake-usb/DCIM'); + } else { + // Navigate to the DCIM directory. + await remoteCall.navigateWithDirectoryTree( + appId, '/DCIM', 'fake-usb', 'removable'); + } // Wait for the import button to be ready. await remoteCall.waitForElement(
diff --git a/ui/file_manager/integration_tests/file_manager/file_display.js b/ui/file_manager/integration_tests/file_manager/file_display.js index cc576be..cd18cdd9 100644 --- a/ui/file_manager/integration_tests/file_manager/file_display.js +++ b/ui/file_manager/integration_tests/file_manager/file_display.js
@@ -268,14 +268,28 @@ chrome.test.assertEq( 'removable', fakeUsb.attributes['volume-type-for-testing']); - // Check unpartitioned USB does not have partitions as tree children. - const itemEntriesQuery = - ['[entry-label="fake-usb"] .tree-children .tree-item']; - const itemEntries = await remoteCall.callRemoteTestUtil( - 'queryAllElements', appId, itemEntriesQuery); - chrome.test.assertEq(1, itemEntries.length); - const childVolumeType = itemEntries[0].attributes['volume-type-for-testing']; - chrome.test.assertTrue('removable' !== childVolumeType); + if (await isSinglePartitionFormat(appId)) { + // Check unpartitioned USB has single partition as tree child. + const itemEntriesQuery = + ['[entry-label="FAKEUSB"] .tree-children .tree-item']; + const itemEntries = await remoteCall.callRemoteTestUtil( + 'queryAllElements', appId, itemEntriesQuery); + chrome.test.assertEq(1, itemEntries.length); + const childVolumeType = + itemEntries[0].attributes['volume-type-for-testing']; + + chrome.test.assertTrue('removable' == childVolumeType); + } else { + // Check unpartitioned USB does not have partitions as tree children. + const itemEntriesQuery = + ['[entry-label="fake-usb"] .tree-children .tree-item']; + const itemEntries = await remoteCall.callRemoteTestUtil( + 'queryAllElements', appId, itemEntriesQuery); + chrome.test.assertEq(1, itemEntries.length); + const childVolumeType = + itemEntries[0].attributes['volume-type-for-testing']; + chrome.test.assertTrue('removable' !== childVolumeType); + } }; /**
diff --git a/ui/file_manager/integration_tests/file_manager/format_dialog.js b/ui/file_manager/integration_tests/file_manager/format_dialog.js index fa13759c..42a67ef1 100644 --- a/ui/file_manager/integration_tests/file_manager/format_dialog.js +++ b/ui/file_manager/integration_tests/file_manager/format_dialog.js
@@ -21,6 +21,11 @@ * @param {string} usbLabel Label of usb to format. */ async function openFormatDialog(appId, usbLabel) { + if (await isSinglePartitionFormat(appId)) { + await openFormatDialogWithSinglePartitionFormat(appId, usbLabel, 'FAKEUSB'); + return; + } + // Focus the directory tree. chrome.test.assertTrue( !!await remoteCall.callRemoteTestUtil( @@ -46,6 +51,44 @@ } /** + * Opens a format dialog for the USB with label |usbLabel| and device with + * label |deviceLabel|. + * + * @param {string} appId Files app window ID. + * @param {string} usbLabel Label of usb to format. + * @param {string} deviceLabel Label of the parent device of usb. + */ +async function openFormatDialogWithSinglePartitionFormat( + appId, usbLabel, deviceLabel) { + // Focus the directory tree. + chrome.test.assertTrue( + !!await remoteCall.callRemoteTestUtil( + 'focus', appId, ['#directory-tree']), + 'focus failed: #directory-tree'); + + // Expand device tree entry to access partition entry. + await remoteCall.expandTreeItemInDirectoryTree( + appId, `#directory-tree [entry-label="${deviceLabel}"]`); + + // Right click on the USB's directory tree entry. + const treeQuery = `#directory-tree [entry-label="${usbLabel}"]`; + await remoteCall.waitForElement(appId, treeQuery); + chrome.test.assertTrue( + !!await remoteCall.callRemoteTestUtil( + 'fakeMouseRightClick', appId, [treeQuery]), + 'fakeMouseRightClick failed'); + + // Click on the format menu item. + const formatItemQuery = '#directory-tree-context-menu:not([hidden])' + + ' cr-menu-item[command="#format"]:not([hidden]):not([disabled])'; + await remoteCall.waitAndClickElement(appId, formatItemQuery); + + // Check the dialog is open. + await remoteCall.waitForElement( + appId, ['files-format-dialog', 'cr-dialog[open]']); +} + +/** * Tests the format dialog for a sample USB with files on it. */ testcase.formatDialog = async () => { @@ -261,8 +304,12 @@ 'focus', appId, ['#directory-tree']), 'focus failed: #directory-tree'); + let usbNavigationPath = '/fake-usb'; + if (await isSinglePartitionFormat(appId)) { + usbNavigationPath = '/FAKEUSB/fake-usb'; + } // Navigate to the USB via the directory tree. - await navigateWithDirectoryTree(appId, '/fake-usb'); + await navigateWithDirectoryTree(appId, usbNavigationPath); // Click on the gear menu button. await remoteCall.waitAndClickElement(appId, '#gear-button:not([hidden])');
diff --git a/ui/file_manager/integration_tests/file_manager/transfer.js b/ui/file_manager/integration_tests/file_manager/transfer.js index 52b6eb6..7b9f014e 100644 --- a/ui/file_manager/integration_tests/file_manager/transfer.js +++ b/ui/file_manager/integration_tests/file_manager/transfer.js
@@ -759,8 +759,12 @@ 'fakeDragAndDrop', appId, [source, target, skipDrop]), 'fakeDragAndDrop failed'); + let navigationPath = '/fake-usb'; + if (await isSinglePartitionFormat(appId)) { + navigationPath = '/FAKEUSB/fake-usb'; + } // Check: drag hovering should navigate the file list. - await remoteCall.waitUntilCurrentDirectoryIsChanged(appId, '/fake-usb'); + await remoteCall.waitUntilCurrentDirectoryIsChanged(appId, navigationPath); }; /** @@ -984,8 +988,12 @@ chrome.test.assertTrue( await remoteCall.callRemoteTestUtil('execCommand', appId, ['copy'])); + let navigationPath = '/fake-usb'; + if (await isSinglePartitionFormat(appId)) { + navigationPath = '/FAKEUSB/fake-usb'; + } // Select USB volume. - await navigateWithDirectoryTree(appId, '/fake-usb'); + await navigateWithDirectoryTree(appId, navigationPath); // Tell the background page to never finish the file copy. await remoteCall.callRemoteTestUtil(
diff --git a/ui/ozone/platform_selection.cc b/ui/ozone/platform_selection.cc index eb8e85ed..1b49a08 100644 --- a/ui/ozone/platform_selection.cc +++ b/ui/ozone/platform_selection.cc
@@ -33,9 +33,6 @@ return g_selected_platform; std::string platform_name = GetPlatformName(); - // TODO(b/169115289) remove once all Tast tests use "drm". - if (platform_name == "gbm") - platform_name = "drm"; // Search for a matching platform in the list. for (int platform_id = 0; platform_id < kPlatformCount; ++platform_id) {
diff --git a/ui/webui/resources/cr_elements/chromeos/cr_picture/BUILD.gn b/ui/webui/resources/cr_elements/chromeos/cr_picture/BUILD.gn index ed62aab..fb06449 100644 --- a/ui/webui/resources/cr_elements/chromeos/cr_picture/BUILD.gn +++ b/ui/webui/resources/cr_elements/chromeos/cr_picture/BUILD.gn
@@ -7,13 +7,6 @@ import("//ui/webui/resources/tools/js_modulizer.gni") import("../os_cr_elements.gni") -# This file depends on the legacy global sources assignment filter. It should -# be converted to check target platform before assigning source files to the -# sources variable. Remove this import and set_sources_assignment_filter call -# when the file has been converted. See https://crbug.com/1018739 for details. -import("//build/config/deprecated_default_sources_assignment_filter.gni") -set_sources_assignment_filter(deprecated_default_sources_assignment_filter) - js_type_check("closure_compile") { deps = [ ":cr_camera", @@ -70,19 +63,20 @@ } js_library("png.m") { - # Need to turn off default sources filtering by GN, otherwise |sources| is - # filtered on non-CrOS, because it contains the term "chromeos". This - # js_library() target is needed in non-CrOS builds as well (see - # chrome/browser/resources/settings/people_page:people_page.m) - set_sources_assignment_filter([]) sources = [ "$root_gen_dir/ui/webui/resources/cr_elements/chromeos/cr_picture/png.m.js", ] extra_deps = [ ":modulize" ] } +# TODO(crbug.com/1134204): This target should probably only be defined if +# target_os is "chromos" as it does not include any source files on other +# platforms. js_library("cr_picture_types.m") { - sources = [ "$root_gen_dir/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_picture_types.m.js" ] + sources = [] + if (is_chromeos) { + sources += [ "$root_gen_dir/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_picture_types.m.js" ] + } extra_deps = [ ":modulize" ] } @@ -96,8 +90,14 @@ ] } +# TODO(crbug.com/1134204): This target should probably only be defined if +# target_os is "chromos" as it does not include any source files on other +# platforms. js_library("cr_picture_pane.m") { - sources = [ "$root_gen_dir/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_picture_pane.m.js" ] + sources = [] + if (is_chromeos) { + sources += [ "$root_gen_dir/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_picture_pane.m.js" ] + } deps = [ ":cr_camera.m", ":cr_picture_types.m", @@ -106,14 +106,26 @@ extra_deps = [ ":cr_picture_pane_module" ] } +# TODO(crbug.com/1134204): This target should probably only be defined if +# target_os is "chromos" as it does not include any source files on other +# platforms. js_library("cr_camera.m") { - sources = [ "$root_gen_dir/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_camera.m.js" ] + sources = [] + if (is_chromeos) { + sources += [ "$root_gen_dir/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_camera.m.js" ] + } deps = [ ":png.m" ] extra_deps = [ ":cr_camera_module" ] } +# TODO(crbug.com/1134204): This target should probably only be defined if +# target_os is "chromos" as it does not include any source files on other +# platforms. js_library("cr_picture_list.m") { - sources = [ "$root_gen_dir/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_picture_list.m.js" ] + sources = [] + if (is_chromeos) { + sources += [ "$root_gen_dir/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_picture_list.m.js" ] + } deps = [ ":cr_picture_types.m", ":png.m",
diff --git a/weblayer/browser/android/javatests/BUILD.gn b/weblayer/browser/android/javatests/BUILD.gn index a322694..fe48352e 100644 --- a/weblayer/browser/android/javatests/BUILD.gn +++ b/weblayer/browser/android/javatests/BUILD.gn
@@ -10,6 +10,7 @@ testonly = true sources = [ "src/org/chromium/weblayer/test/BrowserFragmentLifecycleTest.java", + "src/org/chromium/weblayer/test/BrowserTest.java", "src/org/chromium/weblayer/test/CookieManagerTest.java", "src/org/chromium/weblayer/test/CrashReporterTest.java", "src/org/chromium/weblayer/test/DataClearingTest.java",
diff --git a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/BrowserFragmentLifecycleTest.java b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/BrowserFragmentLifecycleTest.java index 2cfb648..21e51fe 100644 --- a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/BrowserFragmentLifecycleTest.java +++ b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/BrowserFragmentLifecycleTest.java
@@ -18,6 +18,7 @@ import org.chromium.base.test.util.CallbackHelper; import org.chromium.content_public.browser.test.util.TestThreadUtils; +import org.chromium.weblayer.Browser; import org.chromium.weblayer.Navigation; import org.chromium.weblayer.NavigationCallback; import org.chromium.weblayer.NavigationController; @@ -282,4 +283,25 @@ }); helper.waitForCallback(callCount, 1); } + + @Test + @SmallTest + public void browserAndTabIsDestroyedWhenFragmentDestroyed() throws Throwable { + mActivityTestRule.launchShellWithUrl(mActivityTestRule.getTestDataURL("simple_page.html")); + + CallbackHelper helper = new CallbackHelper(); + Browser browser = TestThreadUtils.runOnUiThreadBlocking( + () -> { return mActivityTestRule.getActivity().getBrowser(); }); + Tab tab = TestThreadUtils.runOnUiThreadBlocking(() -> { return browser.getActiveTab(); }); + TestThreadUtils.runOnUiThreadBlocking(() -> { + Assert.assertFalse(browser.isDestroyed()); + Assert.assertFalse(tab.isDestroyed()); + destroyFragment(helper); + }); + helper.waitForFirst(); + TestThreadUtils.runOnUiThreadBlocking(() -> { + Assert.assertTrue(browser.isDestroyed()); + Assert.assertTrue(tab.isDestroyed()); + }); + } }
diff --git a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/BrowserTest.java b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/BrowserTest.java new file mode 100644 index 0000000..39c8f4b --- /dev/null +++ b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/BrowserTest.java
@@ -0,0 +1,44 @@ +// Copyright 2020 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.weblayer.test; + +import androidx.test.filters.SmallTest; + +import org.junit.Assert; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.chromium.content_public.browser.test.util.TestThreadUtils; +import org.chromium.weblayer.Browser; +import org.chromium.weblayer.Tab; +import org.chromium.weblayer.shell.InstrumentationActivity; + +/** + * Tests for Browser. + */ +@RunWith(WebLayerJUnit4ClassRunner.class) +public class BrowserTest { + @Rule + public InstrumentationActivityTestRule mActivityTestRule = + new InstrumentationActivityTestRule(); + + private InstrumentationActivity mActivity; + + @Test + @SmallTest + public void testDestroyTab() { + String url = mActivityTestRule.getTestDataURL("before_unload.html"); + mActivity = mActivityTestRule.launchShellWithUrl(url); + Assert.assertNotNull(mActivity); + TestThreadUtils.runOnUiThreadBlocking(() -> { + Browser browser = mActivity.getBrowser(); + Tab tab = browser.getActiveTab(); + Assert.assertFalse(tab.isDestroyed()); + browser.destroyTab(tab); + Assert.assertTrue(tab.isDestroyed()); + }); + } +}
diff --git a/weblayer/public/java/org/chromium/weblayer/Browser.java b/weblayer/public/java/org/chromium/weblayer/Browser.java index d568ed02..cc3718d 100644 --- a/weblayer/public/java/org/chromium/weblayer/Browser.java +++ b/weblayer/public/java/org/chromium/weblayer/Browser.java
@@ -73,6 +73,14 @@ : null; } + /** + * Returns true if this Browser has been destroyed. + */ + public boolean isDestroyed() { + ThreadCheck.ensureOnUiThread(); + return mImpl == null; + } + // Called prior to notifying IBrowser of destroy(). void prepareForDestroy() { mFragment = null;
diff --git a/weblayer/public/java/org/chromium/weblayer/Tab.java b/weblayer/public/java/org/chromium/weblayer/Tab.java index 32495e0..b9cb6ad 100644 --- a/weblayer/public/java/org/chromium/weblayer/Tab.java +++ b/weblayer/public/java/org/chromium/weblayer/Tab.java
@@ -128,6 +128,14 @@ mBrowser = browser; } + /** + * Returns true if this Tab has been destroyed. + */ + public boolean isDestroyed() { + ThreadCheck.ensureOnUiThread(); + return mImpl == null; + } + @NonNull public Browser getBrowser() { ThreadCheck.ensureOnUiThread();