diff --git a/BUILD.gn b/BUILD.gn index 08609dd..57478e5 100644 --- a/BUILD.gn +++ b/BUILD.gn
@@ -727,15 +727,19 @@ deps += [ "//third_party/sqlite:sqlite_shell" ] } + if ((is_linux && !is_chromecast) || is_chromeos_lacros || is_fuchsia) { + # TODO(https://crbug.com/1329673): Figure out if this should be in gn_all and how cross-platform this is. + deps += [ "//components/services/filesystem:filesystem_service_unittests" ] + } + if ((is_linux && !is_chromecast) || is_chromeos_lacros) { - # TODO(GYP): Figure out if any of these should be in gn_all + # TODO(https://crbug.com/1329673): Figure out if any of these should be in gn_all # and figure out how cross-platform they are deps += [ "//chrome/installer/util:strings", "//chrome/tools/convert_dict", "//components/constrained_window:unit_tests", "//components/metrics:serialization", - "//components/services/filesystem:filesystem_service_unittests", "//components/sessions:unit_tests", "//media/cast:udp_proxy", "//storage/browser:dump_file_system",
diff --git a/DEPS b/DEPS index cf1d83a..8954accc0 100644 --- a/DEPS +++ b/DEPS
@@ -279,11 +279,11 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling V8 # and whatever else without interference from each other. - 'v8_revision': '13b836bc34632aa4662240953059f4faca930e29', + 'v8_revision': '14c5ef09fc64e934f0b859b8e9323f7c907166f3', # 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': '86fce7a77eb2780e51712fc023189bac49e5ea2e', + 'angle_revision': 'd96cee6685099f6bcc392a4d20d28c8ec484673a', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling SwiftShader # and whatever else without interference from each other. @@ -291,7 +291,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling PDFium # and whatever else without interference from each other. - 'pdfium_revision': 'c85f8943e9cccbfbd7ee0661c112ee9b5ba94d1b', + 'pdfium_revision': '73c09241bb432ca957bd66555304c5c2b4c9d47c', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling BoringSSL # and whatever else without interference from each other. @@ -747,11 +747,11 @@ Var('chromium_git') + '/external/github.com/toji/webvr.info.git' + '@' + 'c58ae99b9ff9e2aa4c524633519570bf33536248', 'src/docs/website': { - 'url': Var('chromium_git') + '/website.git' + '@' + '0df96724d5586995354039ff746b43b5bfe9cdcd', + 'url': Var('chromium_git') + '/website.git' + '@' + 'a1a7b0cb5eae86ac111698335ece12790121be09', }, 'src/ios/third_party/earl_grey2/src': { - 'url': Var('chromium_git') + '/external/github.com/google/EarlGrey.git' + '@' + 'bea0718748608826bcbf45351059f12cba6ac0fa', + 'url': Var('chromium_git') + '/external/github.com/google/EarlGrey.git' + '@' + 'd423acd46f8938bd66a15f999259d8977a8d9361', 'condition': 'checkout_ios', }, @@ -841,7 +841,7 @@ 'packages': [ { 'package': 'chromium/rts/model/linux-amd64', - 'version': 'mBk-JxbI_dvdb-NUDVixeOf97tA1vOg6H8aTfv4Z5ZYC', + 'version': 'h3Fw4WWTDL-AIwlPuqOBmpc2Od7Ha52OSHPv4Kb5TXIC', }, ], 'dep_type': 'cipd', @@ -1113,7 +1113,7 @@ # Tools used when building Chrome for Chrome OS. This affects both the Simple # Chrome workflow, as well as the chromeos-chrome ebuild. 'src/third_party/chromite': { - 'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'e2bf3a6f503a80623e848902461b45e64c973bb0', + 'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '97c3857f4712c249543d99f627312a5b8bc3cf70', 'condition': 'checkout_chromeos', }, @@ -1131,12 +1131,12 @@ # For Linux and Chromium OS. 'src/third_party/cros_system_api': { - 'url': Var('chromium_git') + '/chromiumos/platform2/system_api.git' + '@' + 'fcfe475ad52efaeda2f78c4a6aad4bda140f57ee', + 'url': Var('chromium_git') + '/chromiumos/platform2/system_api.git' + '@' + '315a946dfdf2b0d850e35ec2dbca7ad45525c6ff', 'condition': 'checkout_linux', }, 'src/third_party/depot_tools': - Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '10a8286824639fe34a8c14e8d4075cdf56c5a69d', + Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '41691abe86bc361799737a63e06eb0f8dfb05e0a', 'src/third_party/devtools-frontend/src': Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'), @@ -1533,7 +1533,7 @@ }, 'src/third_party/perfetto': - Var('android_git') + '/platform/external/perfetto.git' + '@' + '40eee45831024f09bd55fdbe57a0e13718b9f7e7', + Var('android_git') + '/platform/external/perfetto.git' + '@' + '989b580fdb73a90d3c659b3b4ff2bcdfa7039d55', 'src/third_party/perl': { 'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3', @@ -1651,7 +1651,7 @@ Var('chromium_git') + '/external/github.com/GoogleChromeLabs/text-fragments-polyfill.git' + '@' + 'c036420683f672d685e27415de0a5f5e85bdc23f', 'src/third_party/tflite/src': - Var('chromium_git') + '/external/github.com/tensorflow/tensorflow.git' + '@' + '6959967708cc680fe0d17381b5a421413783bda1', + Var('chromium_git') + '/external/github.com/tensorflow/tensorflow.git' + '@' + '5ddefa9f8d1455c8ca694ba1a511ba82e3a88960', 'src/third_party/turbine': { 'packages': [ @@ -1708,7 +1708,7 @@ Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + '24c2a787daefb05e27c1689f5ef6cc491eeadb1c', 'src/third_party/webrtc': - Var('webrtc_git') + '/src.git' + '@' + '6ef0a3816e9c959a84f7de3e0fe983dbe3b114ca', + Var('webrtc_git') + '/src.git' + '@' + 'ee6ad1403ae6c2c853097b8928253e5d29228046', 'src/third_party/libgifcodec': Var('skia_git') + '/libgifcodec' + '@'+ Var('libgifcodec_revision'), @@ -1781,7 +1781,7 @@ Var('chromium_git') + '/v8/v8.git' + '@' + Var('v8_revision'), 'src-internal': { - 'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@3dd1bb99d9c97d7cc257814803166367131bf8ca', + 'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@05e5ce91a8144e97bfce8e2cddbca99d2cfa99cc', 'condition': 'checkout_src_internal', }, @@ -1822,7 +1822,7 @@ 'packages': [ { 'package': 'chromeos_internal/apps/media_app/app', - 'version': 'fAwvEhy-AStpxksGoC7k7vU4v19rjtfrnu-c8ElEg0wC', + 'version': 'n_-s6AHGojfZHBSb-a6B7uW3aAIRgNqEB6mpNU6He4QC', }, ], 'condition': 'checkout_chromeos and checkout_src_internal', @@ -1833,7 +1833,7 @@ 'packages': [ { 'package': 'chromeos_internal/apps/projector_app/app', - 'version': '2WQHIccAp2RigVJJDLtugL6zbmz1Y7UcNCmsFLrVecMC', + 'version': 'Ngb5o5UWP8i8lD1N151CemqExqW0n-qsynp-NXtckmwC', }, ], 'condition': 'checkout_chromeos and checkout_src_internal',
diff --git a/WATCHLISTS b/WATCHLISTS index 3c6d47f0..df0073b 100644 --- a/WATCHLISTS +++ b/WATCHLISTS
@@ -2664,7 +2664,6 @@ 'minidump_tests': ['bsheedy@chromium.org'], 'multidevice': ['danlee+watch-multidevice@google.com', 'hansberry+watch-multidevice@chromium.org', - 'nohle+watch-multidevice@chromium.org', 'themaxli+watch-multidevice@chromium.org'], 'multipaste': ['andrewxu+watch-multipaste@google.com', 'dmblack+watch-multipaste@google.com', @@ -2676,12 +2675,13 @@ 'creis+watch@chromium.org', 'navigation-cc+reviews@chromium.org'], 'nearby': ['cclem+watch-nearby@google.com', + 'crisrael+watch-nearby@google.com', 'danlee+watch-nearby@google.com', 'hais+watch-nearby@google.com', 'hansberry+watch-nearby@chromium.org', 'hansenmichael+watch-nearby@google.com', 'knollr+watch-nearby@chromium.org', - 'nohle+watch-nearby@chromium.org', + 'pushi+watch-nearby@google.com', 'xlythe+watch-nearby@google.com'], 'net': ['net-reviews@chromium.org'], 'net_base': ['bnc+watch@chromium.org'],
diff --git a/android_webview/browser/gfx/gpu_service_webview.cc b/android_webview/browser/gfx/gpu_service_webview.cc index ffe31384..c488be26 100644 --- a/android_webview/browser/gfx/gpu_service_webview.cc +++ b/android_webview/browser/gfx/gpu_service_webview.cc
@@ -29,9 +29,9 @@ gpu::GpuPreferences gpu_preferences = content::GetGpuPreferencesFromCommandLine(); auto* command_line = base::CommandLine::ForCurrentProcess(); - bool success = gpu::InitializeGLThreadSafe(command_line, gpu_preferences, - &gpu_info, &gpu_feature_info); - if (!success) { + gl::GLDisplay* display = gpu::InitializeGLThreadSafe( + command_line, gpu_preferences, &gpu_info, &gpu_feature_info); + if (!display) { LOG(FATAL) << "gpu::InitializeGLThreadSafe() failed."; } auto sync_point_manager = std::make_unique<gpu::SyncPointManager>();
diff --git a/ash/BUILD.gn b/ash/BUILD.gn index 94bf469..e4f41c5 100644 --- a/ash/BUILD.gn +++ b/ash/BUILD.gn
@@ -3167,6 +3167,8 @@ ":test_support", "//ash/public/cpp", "//base/test:test_support", + "//chromeos/dbus/power:power", + "//chromeos/dbus/power:power_manager_proto", "//components/viz/test:test_support", "//mojo/core/embedder:embedder", ]
diff --git a/ash/app_list/app_list_bubble_presenter.cc b/ash/app_list/app_list_bubble_presenter.cc index 6ecaa5a..800ddf6 100644 --- a/ash/app_list/app_list_bubble_presenter.cc +++ b/ash/app_list/app_list_bubble_presenter.cc
@@ -192,6 +192,8 @@ is_target_visibility_show_ = true; target_page_ = AppListBubblePage::kApps; + controller_->OnVisibilityWillChange(/*visible=*/true, display_id); + // Refresh the continue tasks before opening the launcher. If a file doesn't // exist on disk anymore then the launcher should not create or animate the // continue task view for that suggestion. @@ -268,7 +270,7 @@ // time the bubble is shown to make sure it tracks the right display. aura::client::GetFocusClient(bubble_widget_->GetNativeWindow()) ->AddObserver(this); - controller_->OnVisibilityWillChange(/*visible=*/true, display_id); + bubble_widget_->Show(); // The page must be set before triggering the show animation so the correct // animations are triggered.
diff --git a/ash/app_list/app_list_bubble_presenter.h b/ash/app_list/app_list_bubble_presenter.h index 6087c18..dc046bf 100644 --- a/ash/app_list/app_list_bubble_presenter.h +++ b/ash/app_list/app_list_bubble_presenter.h
@@ -104,7 +104,7 @@ // outside the bubble. void OnPressOutsideBubble(); - // Gets the display id for the display `bubble_widget_` is shown on, returns + // Gets the display id for the display `bubble_widget_` is shown on. Returns // kInvalidDisplayId if not shown. int64_t GetDisplayId() const; @@ -113,8 +113,9 @@ AppListControllerImpl* const controller_; - // Whether the view is showing or animating to shown. If true, - // `bubble_widget_` is not null. + // Whether the view is showing or animating to show. Note that the + // `bubble_widget_` may be null during the zero state search called in + // `Show()`. bool is_target_visibility_show_ = false; // Owned by native widget.
diff --git a/ash/app_list/app_list_controller_impl.cc b/ash/app_list/app_list_controller_impl.cc index 7a6a713..e6a06c1 100644 --- a/ash/app_list/app_list_controller_impl.cc +++ b/ash/app_list/app_list_controller_impl.cc
@@ -520,7 +520,7 @@ const absl::optional<int64_t>& display_id) const { return last_target_visible_ && (!display_id.has_value() || - display_id.value() == last_visible_display_id_); + display_id.value() == last_target_visible_display_id_); } void AppListControllerImpl::Show(int64_t display_id,
diff --git a/ash/components/multidevice/OWNERS b/ash/components/multidevice/OWNERS index 1d57183..b44251a0 100644 --- a/ash/components/multidevice/OWNERS +++ b/ash/components/multidevice/OWNERS
@@ -1,4 +1,3 @@ hansberry@chromium.org jonmann@chromium.org khorimoto@chromium.org -nohle@chromium.org
diff --git a/ash/components/phonehub/OWNERS b/ash/components/phonehub/OWNERS index 1d57183..b44251a0 100644 --- a/ash/components/phonehub/OWNERS +++ b/ash/components/phonehub/OWNERS
@@ -1,4 +1,3 @@ hansberry@chromium.org jonmann@chromium.org khorimoto@chromium.org -nohle@chromium.org
diff --git a/ash/quick_pair/OWNERS b/ash/quick_pair/OWNERS index ef9bf69..ab242bd 100644 --- a/ash/quick_pair/OWNERS +++ b/ash/quick_pair/OWNERS
@@ -1,4 +1,3 @@ -shanefitz@google.com jonmann@chromium.org dclasson@google.com julietlevesque@google.com
diff --git a/ash/shelf/hotseat_transition_animator.cc b/ash/shelf/hotseat_transition_animator.cc index fc225a43..a6a7a64 100644 --- a/ash/shelf/hotseat_transition_animator.cc +++ b/ash/shelf/hotseat_transition_animator.cc
@@ -7,6 +7,7 @@ #include "ash/public/cpp/metrics_util.h" #include "ash/public/cpp/shelf_config.h" #include "ash/shelf/drag_handle.h" +#include "ash/shelf/shelf_layout_manager.h" #include "ash/shelf/shelf_widget.h" #include "ash/shell.h" #include "ash/wm/tablet_mode/tablet_mode_controller.h" @@ -155,6 +156,11 @@ if (!animations_enabled_for_current_session_state_) return false; + // The shelf should be directly hidden without animation if the auto hide + // state is auto hidden. + if (shelf_widget_->shelf_layout_manager()->is_shelf_auto_hidden()) + return false; + return (new_state == HotseatState::kShownHomeLauncher || old_state == HotseatState::kShownHomeLauncher) && !(new_state == HotseatState::kShownClamshell ||
diff --git a/ash/shelf/shelf_layout_manager.cc b/ash/shelf/shelf_layout_manager.cc index 1143944..5a8838e 100644 --- a/ash/shelf/shelf_layout_manager.cc +++ b/ash/shelf/shelf_layout_manager.cc
@@ -1963,8 +1963,11 @@ return SHELF_AUTO_HIDE_SHOWN; } - if (!in_tablet_mode && shelf_widget_->IsShowingAppList()) + if (auto* app_list_controller = Shell::Get()->app_list_controller(); + !in_tablet_mode && + app_list_controller->GetTargetVisibility(display_.id())) { return SHELF_AUTO_HIDE_SHOWN; + } if (shelf_widget_->status_area_widget() && shelf_widget_->status_area_widget()->ShouldShowShelf()) { @@ -2152,11 +2155,11 @@ float ShelfLayoutManager::ComputeTargetOpacity(const State& state) const { if (Shell::Get()->IsInTabletMode()) { - // The shelf should not become transparent during the animation to or from + // The shelf should not become transparent during the animation to // HomeLauncher. auto* app_list_controller = Shell::Get()->app_list_controller(); - if (app_list_controller->GetTargetVisibility(display_.id()) != - app_list_controller->IsVisible(display_.id())) { + if (app_list_controller->GetTargetVisibility(display_.id()) && + !app_list_controller->IsVisible(display_.id())) { return 1.0f; } }
diff --git a/ash/shelf/shelf_layout_manager_unittest.cc b/ash/shelf/shelf_layout_manager_unittest.cc index df07e6b..7479df9 100644 --- a/ash/shelf/shelf_layout_manager_unittest.cc +++ b/ash/shelf/shelf_layout_manager_unittest.cc
@@ -211,6 +211,38 @@ base::RunLoop run_loop_; }; +// This class detects the auto hide state change in shelf layout manager within +// its lifetime. +class AutoHideStateDetector : public ShelfLayoutManagerObserver { + public: + AutoHideStateDetector() { + Shell::GetPrimaryRootWindowController() + ->shelf() + ->shelf_layout_manager() + ->AddObserver(this); + } + + AutoHideStateDetector(const AutoHideStateDetector&) = delete; + AutoHideStateDetector& operator=(const AutoHideStateDetector&) = delete; + + ~AutoHideStateDetector() override { + Shell::GetPrimaryRootWindowController() + ->shelf() + ->shelf_layout_manager() + ->RemoveObserver(this); + } + + void OnAutoHideStateChanged(ShelfAutoHideState new_state) override { + if (new_state == SHELF_AUTO_HIDE_HIDDEN) + was_shelf_auto_hidden = true; + } + + bool WasShelfAutoHidden() const { return was_shelf_auto_hidden; } + + private: + bool was_shelf_auto_hidden = false; +}; + } // namespace class ShelfLayoutManagerTest : public ShelfLayoutManagerTestBase { @@ -3309,6 +3341,51 @@ EXPECT_FALSE(Shell::Get()->app_list_controller()->IsVisible()); } +// Tests that tapping the home button is successful on the autohidden shelf. +TEST_F(AppListBubbleShelfLayoutManagerTest, + NoTemporaryAutoHideStateWhileOpeningLauncher) { + // Enable animations and simulate the zero state search called when showing + // the launcher. + ui::ScopedAnimationDurationScaleMode duration( + ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION); + GetTestAppListClient()->set_run_zero_state_callback_immediately(false); + + Shelf* shelf = GetPrimaryShelf(); + shelf->SetAutoHideBehavior(ShelfAutoHideBehavior::kAlways); + const display::Display display = + display::Screen::GetScreen()->GetPrimaryDisplay(); + + // Create a window to hide the shelf in auto-hide mode. + std::unique_ptr<aura::Window> window = + AshTestBase::CreateTestWindow(gfx::Rect(0, 0, 400, 400)); + wm::ActivateWindow(window.get()); + + EXPECT_EQ(SHELF_AUTO_HIDE, shelf->GetVisibilityState()); + EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf->GetAutoHideState()); + GetAppListTestHelper()->CheckVisibility(false); + + SwipeUpOnShelf(); + EXPECT_EQ(SHELF_AUTO_HIDE_SHOWN, shelf->GetAutoHideState()); + GetAppListTestHelper()->CheckVisibility(false); + + { + AutoHideStateDetector detector; + + // Open the launcher by tapping the home button. + GestureTapOn(GetPrimaryShelf()->navigation_widget()->GetHomeButton()); + + // Wait until the zero state callback is called. + base::RunLoop().RunUntilIdle(); + EXPECT_EQ(1, GetTestAppListClient()->zero_state_search_done_count()); + + // No `SHELF_AUTO_HIDE_HIDDEN` should be set when launcher is showing. + EXPECT_FALSE(detector.WasShelfAutoHidden()); + } + + // The app list should now be visible. + GetAppListTestHelper()->CheckVisibility(true); +} + class ShelfLayoutManagerWindowDraggingTest : public ShelfLayoutManagerTestBase { public: ShelfLayoutManagerWindowDraggingTest() = default;
diff --git a/ash/system/palette/palette_welcome_bubble.cc b/ash/system/palette/palette_welcome_bubble.cc index bc7e558..3c456b7 100644 --- a/ash/system/palette/palette_welcome_bubble.cc +++ b/ash/system/palette/palette_welcome_bubble.cc
@@ -64,7 +64,9 @@ .AddChild( views::Builder<views::Label>() .SetText(l10n_util::GetStringUTF16( - assistant::util::IsGoogleDevice() + assistant::util::IsGoogleDevice() && + !ash::features:: + IsDeprecateAssistantStylusFeaturesEnabled() ? IDS_ASH_STYLUS_WARM_WELCOME_BUBBLE_WITH_ASSISTANT_DESCRIPTION : IDS_ASH_STYLUS_WARM_WELCOME_BUBBLE_DESCRIPTION)) .SetHorizontalAlignment(gfx::ALIGN_LEFT)
diff --git a/ash/test/ash_pixel_diff_test_base.cc b/ash/test/ash_pixel_diff_test_base.cc index 23a8c72..7cfb8146 100644 --- a/ash/test/ash_pixel_diff_test_base.cc +++ b/ash/test/ash_pixel_diff_test_base.cc
@@ -5,8 +5,12 @@ #include "ash/test/ash_pixel_diff_test_base.h" #include "ash/shell.h" +#include "ash/system/power/power_status.h" #include "ash/wallpaper/wallpaper_controller_impl.h" #include "base/command_line.h" +#include "base/time/time_override.h" +#include "chromeos/dbus/power/fake_power_manager_client.h" +#include "chromeos/dbus/power_manager/power_supply_properties.pb.h" #include "ui/compositor/compositor_switches.h" #include "ui/display/display.h" @@ -23,6 +27,12 @@ constexpr SkColor kWallPaperColor = SK_ColorMAGENTA; +constexpr char kLocale[] = "en_US"; +constexpr char kTimeZone[] = "America/Chicago"; + +// The string that represents the current time. +constexpr char kFakeNowTimeString[] = "Sun, 6 May 2018 14:30:00 CDT"; + // Creates a pure color image of the specified size. gfx::ImageSkia CreateImage(int width, int height, SkColor color) { SkBitmap bitmap; @@ -32,12 +42,25 @@ return image; } +// TimeOverrideHelper ---------------------------------------------------------- + +struct TimeOverrideHelper { + static base::Time TimeNow() { return current_time; } + + // Used as the current time in ash pixel diff tests. + static base::Time current_time; +}; + +base::Time TimeOverrideHelper::current_time; + } // namespace AshPixelDiffTestBase::AshPixelDiffTestBase( std::unique_ptr<base::test::TaskEnvironment> task_environment) : AshTestBase(std::move(task_environment)), - kAccountId_(AccountId::FromUserEmailGaiaId(kUser, "test-hash")) {} + kAccountId_(AccountId::FromUserEmailGaiaId(kUser, "test-hash")), + scoped_locale_(kLocale), + time_zone_(kTimeZone) {} AshPixelDiffTestBase::~AshPixelDiffTestBase() = default; @@ -47,6 +70,10 @@ base::CommandLine::ForCurrentProcess()->AppendSwitch( switches::kEnablePixelOutputInTests); + // Override the current time before setting up `AshTestBase` so that the views + // relying on the current time, like the tray time view, show as expected. + OverrideTime(); + // Do not start the user session in `AshTestBase::SetUp()`. Instead, perform // user login with `kAccountId_`. set_start_session(false); @@ -57,6 +84,7 @@ // Set variable UI components in explicit ways to stabilize screenshots. SetWallPaper(); + SetBatteryState(); } void AshPixelDiffTestBase::SetWallPaper() { @@ -80,4 +108,22 @@ /*preview_mode=*/false); } +void AshPixelDiffTestBase::OverrideTime() { + ASSERT_TRUE(base::Time::FromString(kFakeNowTimeString, + &TimeOverrideHelper::current_time)); + time_override_ = std::make_unique<base::subtle::ScopedTimeClockOverrides>( + &TimeOverrideHelper::TimeNow, /*time_ticks_override=*/nullptr, + /*thread_ticks_override=*/nullptr); +} + +void AshPixelDiffTestBase::SetBatteryState() { + power_manager::PowerSupplyProperties proto; + proto.set_external_power( + power_manager::PowerSupplyProperties_ExternalPower_DISCONNECTED); + proto.set_battery_state( + power_manager::PowerSupplyProperties_BatteryState_DISCHARGING); + proto.set_battery_percent(50.0); + chromeos::FakePowerManagerClient::Get()->UpdatePowerProperties(proto); +} + } // namespace ash
diff --git a/ash/test/ash_pixel_diff_test_base.h b/ash/test/ash_pixel_diff_test_base.h index b9b6003a..455f90a 100644 --- a/ash/test/ash_pixel_diff_test_base.h +++ b/ash/test/ash_pixel_diff_test_base.h
@@ -8,6 +8,7 @@ #include "ash/test/ash_test_base.h" #include "ash/wallpaper/test_wallpaper_controller_client.h" #include "base/files/scoped_temp_dir.h" +#include "base/test/icu_test_util.h" namespace ash { @@ -41,14 +42,28 @@ // Sets a pure color wallpaper. void SetWallPaper(); + // Overrides the current time. + void OverrideTime(); + + // Sets the battery state. It ensures that the tray battery icon does not + // change during pixel tests. + void SetBatteryState(); + const AccountId kAccountId_; + // Used for setting the locale and the time zone. + const base::test::ScopedRestoreICUDefaultLocale scoped_locale_; + const base::test::ScopedRestoreDefaultTimezone time_zone_; + // The temporary data directories for wallpaper setting. base::ScopedTempDir user_data_dir_; base::ScopedTempDir online_wallpaper_dir_; base::ScopedTempDir custom_wallpaper_dir_; TestWallpaperControllerClient client_; + + // Overrides the current time. + std::unique_ptr<base::subtle::ScopedTimeClockOverrides> time_override_; }; } // namespace ash
diff --git a/ash/webui/shimless_rma/resources/onboarding_network_page.js b/ash/webui/shimless_rma/resources/onboarding_network_page.js index 077237b..0db96251 100644 --- a/ash/webui/shimless_rma/resources/onboarding_network_page.js +++ b/ash/webui/shimless_rma/resources/onboarding_network_page.js
@@ -266,6 +266,11 @@ if (dialog.open) { dialog.close(); } + + // Reset the network state properties. + this.networkType_ = ''; + this.networkName_ = ''; + this.guid_ = ''; } /** @protected */
diff --git a/ash/webui/shimless_rma/resources/wrapup_repair_complete_page.html b/ash/webui/shimless_rma/resources/wrapup_repair_complete_page.html index 658fd952..b07871e5 100644 --- a/ash/webui/shimless_rma/resources/wrapup_repair_complete_page.html +++ b/ash/webui/shimless_rma/resources/wrapup_repair_complete_page.html
@@ -110,8 +110,8 @@ #navigationButtonWrapper { bottom: 6px; - margin-inline-start: calc(-1 * var(--content-container-padding) + 6px); - position: absolute; + left: 6px; + position: fixed; } .navigation-button {
diff --git a/ash/webui/shimless_rma/resources/wrapup_restock_page.html b/ash/webui/shimless_rma/resources/wrapup_restock_page.html index 3418c51..29536a02 100644 --- a/ash/webui/shimless_rma/resources/wrapup_restock_page.html +++ b/ash/webui/shimless_rma/resources/wrapup_restock_page.html
@@ -31,21 +31,3 @@ </div> </div> </base-page> - -<cr-dialog id="powerwashDialog" close-text="close"> - <div slot="title"> - [[i18n('powerwashDialogTitle')]] - </div> - <div slot="body"> - [[i18n('powerwashDialogShutdownDescription')]] - </div> - <div class="dialog-footer" slot="button-container"> - <cr-button id="closePowerwashDialogButton" on-click="onCancelClick_"> - [[i18n('cancelButtonLabel')]] - </cr-button> - <cr-button id="powerwashButton" class="text-button action-button" - on-click="onPowerwashButtonClick_"> - [[i18n('powerwashDialogPowerwashButton')]] - </cr-button> - </div> -</cr-dialog>
diff --git a/ash/webui/shimless_rma/resources/wrapup_restock_page.js b/ash/webui/shimless_rma/resources/wrapup_restock_page.js index b6f4e8dd..21861094 100644 --- a/ash/webui/shimless_rma/resources/wrapup_restock_page.js +++ b/ash/webui/shimless_rma/resources/wrapup_restock_page.js
@@ -62,28 +62,7 @@ } /** @protected */ - onShutdownButtonClicked_(e) { - e.preventDefault(); - const dialog = /** @type {!CrDialogElement} */ ( - this.shadowRoot.querySelector('#powerwashDialog')); - if (!dialog.open) { - dialog.showModal(); - } - } - - /** @protected */ - onCancelClick_() { - const dialog = /** @type {!CrDialogElement} */ ( - this.shadowRoot.querySelector('#powerwashDialog')); - dialog.close(); - } - - /** @protected */ - onPowerwashButtonClick_(e) { - e.preventDefault(); - const dialog = /** @type {!CrDialogElement} */ ( - this.shadowRoot.querySelector('#powerwashDialog')); - dialog.close(); + onShutdownButtonClicked_() { executeThenTransitionState( this, () => this.shimlessRmaService_.shutdownForRestock()); }
diff --git a/base/allocator/partition_alloc_features.cc b/base/allocator/partition_alloc_features.cc index cc2809c..621f767b 100644 --- a/base/allocator/partition_alloc_features.cc +++ b/base/allocator/partition_alloc_features.cc
@@ -51,6 +51,7 @@ const BASE_EXPORT Feature kPartitionAllocLargeEmptySlotSpanRing{ "PartitionAllocLargeEmptySlotSpanRing", FEATURE_DISABLED_BY_DEFAULT}; +#endif // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) const Feature kPartitionAllocBackupRefPtr { "PartitionAllocBackupRefPtr", @@ -88,7 +89,13 @@ &kPartitionAllocBackupRefPtr, "brp-mode", BackupRefPtrMode::kEnabled, &kBackupRefPtrModeOptions}; -#endif // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) +const base::FeatureParam<bool> kBackupRefPtrAsanEnableDereferenceCheckParam{ + &kPartitionAllocBackupRefPtr, "asan-enable-dereference-check", true}; +const base::FeatureParam<bool> kBackupRefPtrAsanEnableExtractionCheckParam{ + &kPartitionAllocBackupRefPtr, "asan-enable-extraction-check", + false}; // Not much noise at the moment to enable by default. +const base::FeatureParam<bool> kBackupRefPtrAsanEnableInstantiationCheckParam{ + &kPartitionAllocBackupRefPtr, "asan-enable-instantiation-check", true}; // If enabled, switches the bucket distribution to an alternate one. The // alternate distribution must have buckets that are a subset of the default
diff --git a/base/allocator/partition_alloc_features.h b/base/allocator/partition_alloc_features.h index 6b34d01..6aac406 100644 --- a/base/allocator/partition_alloc_features.h +++ b/base/allocator/partition_alloc_features.h
@@ -27,6 +27,7 @@ extern const BASE_EXPORT Feature kPartitionAllocBackupRefPtrControl; extern const BASE_EXPORT Feature kPartitionAllocLargeThreadCacheSize; extern const BASE_EXPORT Feature kPartitionAllocLargeEmptySlotSpanRing; +#endif // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) enum class BackupRefPtrEnabledProcesses { // BRP enabled only in the browser process. @@ -63,7 +64,12 @@ kBackupRefPtrEnabledProcessesParam; extern const BASE_EXPORT base::FeatureParam<BackupRefPtrMode> kBackupRefPtrModeParam; -#endif // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) +extern const BASE_EXPORT base::FeatureParam<bool> + kBackupRefPtrAsanEnableDereferenceCheckParam; +extern const BASE_EXPORT base::FeatureParam<bool> + kBackupRefPtrAsanEnableExtractionCheckParam; +extern const BASE_EXPORT base::FeatureParam<bool> + kBackupRefPtrAsanEnableInstantiationCheckParam; extern const BASE_EXPORT Feature kPartitionAllocPCScanMUAwareScheduler; extern const BASE_EXPORT Feature kPartitionAllocPCScanStackScanning;
diff --git a/base/allocator/partition_allocator/partition_alloc_base/bits.h b/base/allocator/partition_allocator/partition_alloc_base/bits.h index 82cb152..f047afa 100644 --- a/base/allocator/partition_allocator/partition_alloc_base/bits.h +++ b/base/allocator/partition_allocator/partition_alloc_base/bits.h
@@ -80,10 +80,10 @@ // constexpr. #if defined(COMPILER_MSVC) && !defined(__clang__) -template <typename T, unsigned bits = sizeof(T) * 8> +template <typename T, int bits = sizeof(T) * 8> PA_ALWAYS_INLINE typename std::enable_if<std::is_unsigned<T>::value && sizeof(T) <= 4, - unsigned>::type + int>::type CountLeadingZeroBits(T x) { static_assert(bits > 0, "invalid instantiation"); unsigned long index; @@ -92,10 +92,10 @@ : bits; } -template <typename T, unsigned bits = sizeof(T) * 8> +template <typename T, int bits = sizeof(T) * 8> PA_ALWAYS_INLINE typename std::enable_if<std::is_unsigned<T>::value && sizeof(T) == 8, - unsigned>::type + int>::type CountLeadingZeroBits(T x) { static_assert(bits > 0, "invalid instantiation"); unsigned long index; @@ -117,10 +117,10 @@ #endif } -template <typename T, unsigned bits = sizeof(T) * 8> +template <typename T, int bits = sizeof(T) * 8> PA_ALWAYS_INLINE typename std::enable_if<std::is_unsigned<T>::value && sizeof(T) <= 4, - unsigned>::type + int>::type CountTrailingZeroBits(T x) { static_assert(bits > 0, "invalid instantiation"); unsigned long index; @@ -128,10 +128,10 @@ : bits; } -template <typename T, unsigned bits = sizeof(T) * 8> +template <typename T, int bits = sizeof(T) * 8> PA_ALWAYS_INLINE typename std::enable_if<std::is_unsigned<T>::value && sizeof(T) == 8, - unsigned>::type + int>::type CountTrailingZeroBits(T x) { static_assert(bits > 0, "invalid instantiation"); unsigned long index; @@ -152,24 +152,16 @@ #endif } -PA_ALWAYS_INLINE uint32_t CountLeadingZeroBits32(uint32_t x) { - return CountLeadingZeroBits(x); -} - -PA_ALWAYS_INLINE uint64_t CountLeadingZeroBits64(uint64_t x) { - return CountLeadingZeroBits(x); -} - #elif defined(COMPILER_GCC) || defined(__clang__) // __builtin_clz has undefined behaviour for an input of 0, even though there's // clearly a return value that makes sense, and even though some processor clz // instructions have defined behaviour for 0. We could drop to raw __asm__ to // do better, but we'll avoid doing that unless we see proof that we need to. -template <typename T, unsigned bits = sizeof(T) * 8> +template <typename T, int bits = sizeof(T) * 8> PA_ALWAYS_INLINE constexpr typename std::enable_if<std::is_unsigned<T>::value && sizeof(T) <= 8, - unsigned>::type + int>::type CountLeadingZeroBits(T value) { static_assert(bits > 0, "invalid instantiation"); return PA_LIKELY(value) @@ -179,10 +171,10 @@ : bits; } -template <typename T, unsigned bits = sizeof(T) * 8> +template <typename T, int bits = sizeof(T) * 8> PA_ALWAYS_INLINE constexpr typename std::enable_if<std::is_unsigned<T>::value && sizeof(T) <= 8, - unsigned>::type + int>::type CountTrailingZeroBits(T value) { return PA_LIKELY(value) ? bits == 64 ? __builtin_ctzll(static_cast<uint64_t>(value)) @@ -190,24 +182,8 @@ : bits; } -PA_ALWAYS_INLINE constexpr uint32_t CountLeadingZeroBits32(uint32_t x) { - return CountLeadingZeroBits(x); -} - -PA_ALWAYS_INLINE constexpr uint64_t CountLeadingZeroBits64(uint64_t x) { - return CountLeadingZeroBits(x); -} - #endif -PA_ALWAYS_INLINE constexpr size_t CountLeadingZeroBitsSizeT(size_t x) { - return CountLeadingZeroBits(x); -} - -PA_ALWAYS_INLINE constexpr size_t CountTrailingZeroBitsSizeT(size_t x) { - return CountTrailingZeroBits(x); -} - // Returns the integer i such as 2^i <= n < 2^(i+1). // // There is a common `BitLength` function, which returns the number of bits
diff --git a/base/allocator/partition_allocator/partition_alloc_base/bits_pa_unittest.cc b/base/allocator/partition_allocator/partition_alloc_base/bits_pa_unittest.cc index 4d8bb75..9496eec 100644 --- a/base/allocator/partition_allocator/partition_alloc_base/bits_pa_unittest.cc +++ b/base/allocator/partition_allocator/partition_alloc_base/bits_pa_unittest.cc
@@ -118,106 +118,106 @@ } TEST(BitsTest, CountLeadingZeroBits8) { - EXPECT_EQ(8u, CountLeadingZeroBits(uint8_t{0})); - EXPECT_EQ(7u, CountLeadingZeroBits(uint8_t{1})); - for (uint8_t shift = 0; shift <= 7; shift++) { - EXPECT_EQ(7u - shift, + EXPECT_EQ(8, CountLeadingZeroBits(uint8_t{0})); + EXPECT_EQ(7, CountLeadingZeroBits(uint8_t{1})); + for (int shift = 0; shift <= 7; ++shift) { + EXPECT_EQ(7 - shift, CountLeadingZeroBits(static_cast<uint8_t>(1 << shift))); } - EXPECT_EQ(4u, CountLeadingZeroBits(uint8_t{0x0f})); + EXPECT_EQ(4, CountLeadingZeroBits(uint8_t{0x0f})); } TEST(BitsTest, CountLeadingZeroBits16) { - EXPECT_EQ(16u, CountLeadingZeroBits(uint16_t{0})); - EXPECT_EQ(15u, CountLeadingZeroBits(uint16_t{1})); - for (uint16_t shift = 0; shift <= 15; shift++) { - EXPECT_EQ(15u - shift, + EXPECT_EQ(16, CountLeadingZeroBits(uint16_t{0})); + EXPECT_EQ(15, CountLeadingZeroBits(uint16_t{1})); + for (int shift = 0; shift <= 15; ++shift) { + EXPECT_EQ(15 - shift, CountLeadingZeroBits(static_cast<uint16_t>(1 << shift))); } - EXPECT_EQ(4u, CountLeadingZeroBits(uint16_t{0x0f0f})); + EXPECT_EQ(4, CountLeadingZeroBits(uint16_t{0x0f0f})); } TEST(BitsTest, CountLeadingZeroBits32) { - EXPECT_EQ(32u, CountLeadingZeroBits(uint32_t{0})); - EXPECT_EQ(31u, CountLeadingZeroBits(uint32_t{1})); - for (uint32_t shift = 0; shift <= 31; shift++) { - EXPECT_EQ(31u - shift, CountLeadingZeroBits(uint32_t{1} << shift)); + EXPECT_EQ(32, CountLeadingZeroBits(uint32_t{0})); + EXPECT_EQ(31, CountLeadingZeroBits(uint32_t{1})); + for (int shift = 0; shift <= 31; ++shift) { + EXPECT_EQ(31 - shift, CountLeadingZeroBits(uint32_t{1} << shift)); } - EXPECT_EQ(4u, CountLeadingZeroBits(uint32_t{0x0f0f0f0f})); + EXPECT_EQ(4, CountLeadingZeroBits(uint32_t{0x0f0f0f0f})); } -TEST(BitsTest, CountTrailingeZeroBits8) { - EXPECT_EQ(8u, CountTrailingZeroBits(uint8_t{0})); - EXPECT_EQ(7u, CountTrailingZeroBits(uint8_t{128})); - for (uint8_t shift = 0; shift <= 7; shift++) { +TEST(BitsTest, CountTrailingZeroBits8) { + EXPECT_EQ(8, CountTrailingZeroBits(uint8_t{0})); + EXPECT_EQ(7, CountTrailingZeroBits(uint8_t{128})); + for (int shift = 0; shift <= 7; ++shift) { EXPECT_EQ(shift, CountTrailingZeroBits(static_cast<uint8_t>(1 << shift))); } - EXPECT_EQ(4u, CountTrailingZeroBits(uint8_t{0xf0})); + EXPECT_EQ(4, CountTrailingZeroBits(uint8_t{0xf0})); } -TEST(BitsTest, CountTrailingeZeroBits16) { - EXPECT_EQ(16u, CountTrailingZeroBits(uint16_t{0})); - EXPECT_EQ(15u, CountTrailingZeroBits(uint16_t{32768})); - for (uint16_t shift = 0; shift <= 15; shift++) { +TEST(BitsTest, CountTrailingZeroBits16) { + EXPECT_EQ(16, CountTrailingZeroBits(uint16_t{0})); + EXPECT_EQ(15, CountTrailingZeroBits(uint16_t{32768})); + for (int shift = 0; shift <= 15; ++shift) { EXPECT_EQ(shift, CountTrailingZeroBits(static_cast<uint16_t>(1 << shift))); } - EXPECT_EQ(4u, CountTrailingZeroBits(uint16_t{0xf0f0})); + EXPECT_EQ(4, CountTrailingZeroBits(uint16_t{0xf0f0})); } -TEST(BitsTest, CountTrailingeZeroBits32) { - EXPECT_EQ(32u, CountTrailingZeroBits(uint32_t{0})); - EXPECT_EQ(31u, CountTrailingZeroBits(uint32_t{1} << 31)); - for (uint32_t shift = 0; shift <= 31; shift++) { +TEST(BitsTest, CountTrailingZeroBits32) { + EXPECT_EQ(32, CountTrailingZeroBits(uint32_t{0})); + EXPECT_EQ(31, CountTrailingZeroBits(uint32_t{1} << 31)); + for (int shift = 0; shift <= 31; ++shift) { EXPECT_EQ(shift, CountTrailingZeroBits(uint32_t{1} << shift)); } - EXPECT_EQ(4u, CountTrailingZeroBits(uint32_t{0xf0f0f0f0})); + EXPECT_EQ(4, CountTrailingZeroBits(uint32_t{0xf0f0f0f0})); } TEST(BitsTest, CountLeadingZeroBits64) { - EXPECT_EQ(64u, CountLeadingZeroBits(uint64_t{0})); - EXPECT_EQ(63u, CountLeadingZeroBits(uint64_t{1})); - for (uint64_t shift = 0; shift <= 63; shift++) { - EXPECT_EQ(63u - shift, CountLeadingZeroBits(uint64_t{1} << shift)); + EXPECT_EQ(64, CountLeadingZeroBits(uint64_t{0})); + EXPECT_EQ(63, CountLeadingZeroBits(uint64_t{1})); + for (int shift = 0; shift <= 63; ++shift) { + EXPECT_EQ(63 - shift, CountLeadingZeroBits(uint64_t{1} << shift)); } - EXPECT_EQ(4u, CountLeadingZeroBits(uint64_t{0x0f0f0f0f0f0f0f0f})); + EXPECT_EQ(4, CountLeadingZeroBits(uint64_t{0x0f0f0f0f0f0f0f0f})); } -TEST(BitsTest, CountTrailingeZeroBits64) { - EXPECT_EQ(64u, CountTrailingZeroBits(uint64_t{0})); - EXPECT_EQ(63u, CountTrailingZeroBits(uint64_t{1} << 63)); - for (uint64_t shift = 0; shift <= 31; shift++) { +TEST(BitsTest, CountTrailingZeroBits64) { + EXPECT_EQ(64, CountTrailingZeroBits(uint64_t{0})); + EXPECT_EQ(63, CountTrailingZeroBits(uint64_t{1} << 63)); + for (int shift = 0; shift <= 31; ++shift) { EXPECT_EQ(shift, CountTrailingZeroBits(uint64_t{1} << shift)); } - EXPECT_EQ(4u, CountTrailingZeroBits(uint64_t{0xf0f0f0f0f0f0f0f0})); + EXPECT_EQ(4, CountTrailingZeroBits(uint64_t{0xf0f0f0f0f0f0f0f0})); } TEST(BitsTest, CountLeadingZeroBitsSizeT) { #if defined(ARCH_CPU_64_BITS) - EXPECT_EQ(64u, CountLeadingZeroBitsSizeT(size_t{0})); - EXPECT_EQ(63u, CountLeadingZeroBitsSizeT(size_t{1})); - EXPECT_EQ(32u, CountLeadingZeroBitsSizeT(size_t{1} << 31)); - EXPECT_EQ(1u, CountLeadingZeroBitsSizeT(size_t{1} << 62)); - EXPECT_EQ(0u, CountLeadingZeroBitsSizeT(size_t{1} << 63)); + EXPECT_EQ(64, CountLeadingZeroBits(size_t{0})); + EXPECT_EQ(63, CountLeadingZeroBits(size_t{1})); + EXPECT_EQ(32, CountLeadingZeroBits(size_t{1} << 31)); + EXPECT_EQ(1, CountLeadingZeroBits(size_t{1} << 62)); + EXPECT_EQ(0, CountLeadingZeroBits(size_t{1} << 63)); #else - EXPECT_EQ(32u, CountLeadingZeroBitsSizeT(size_t{0})); - EXPECT_EQ(31u, CountLeadingZeroBitsSizeT(size_t{1})); - EXPECT_EQ(1u, CountLeadingZeroBitsSizeT(size_t{1} << 30)); - EXPECT_EQ(0u, CountLeadingZeroBitsSizeT(size_t{1} << 31)); + EXPECT_EQ(32, CountLeadingZeroBits(size_t{0})); + EXPECT_EQ(31, CountLeadingZeroBits(size_t{1})); + EXPECT_EQ(1, CountLeadingZeroBits(size_t{1} << 30)); + EXPECT_EQ(0, CountLeadingZeroBits(size_t{1} << 31)); #endif // ARCH_CPU_64_BITS } TEST(BitsTest, CountTrailingZeroBitsSizeT) { #if defined(ARCH_CPU_64_BITS) - EXPECT_EQ(64u, CountTrailingZeroBitsSizeT(size_t{0})); - EXPECT_EQ(63u, CountTrailingZeroBitsSizeT(size_t{1} << 63)); - EXPECT_EQ(31u, CountTrailingZeroBitsSizeT(size_t{1} << 31)); - EXPECT_EQ(1u, CountTrailingZeroBitsSizeT(size_t{2})); - EXPECT_EQ(0u, CountTrailingZeroBitsSizeT(size_t{1})); + EXPECT_EQ(64, CountTrailingZeroBits(size_t{0})); + EXPECT_EQ(63, CountTrailingZeroBits(size_t{1} << 63)); + EXPECT_EQ(31, CountTrailingZeroBits(size_t{1} << 31)); + EXPECT_EQ(1, CountTrailingZeroBits(size_t{2})); + EXPECT_EQ(0, CountTrailingZeroBits(size_t{1})); #else - EXPECT_EQ(32u, CountTrailingZeroBitsSizeT(size_t{0})); - EXPECT_EQ(31u, CountTrailingZeroBitsSizeT(size_t{1} << 31)); - EXPECT_EQ(1u, CountTrailingZeroBitsSizeT(size_t{2})); - EXPECT_EQ(0u, CountTrailingZeroBitsSizeT(size_t{1})); + EXPECT_EQ(32, CountTrailingZeroBits(size_t{0})); + EXPECT_EQ(31, CountTrailingZeroBits(size_t{1} << 31)); + EXPECT_EQ(1, CountTrailingZeroBits(size_t{2})); + EXPECT_EQ(0, CountTrailingZeroBits(size_t{1})); #endif // ARCH_CPU_64_BITS }
diff --git a/base/allocator/partition_allocator/partition_bucket_lookup.h b/base/allocator/partition_allocator/partition_bucket_lookup.h index ad1bc30..75fb57eb 100644 --- a/base/allocator/partition_allocator/partition_bucket_lookup.h +++ b/base/allocator/partition_allocator/partition_bucket_lookup.h
@@ -104,9 +104,9 @@ // The class used to generate the bucket lookup table at compile-time. class BucketIndexLookup final { public: - PA_ALWAYS_INLINE constexpr static size_t GetIndexForDenserBuckets( + PA_ALWAYS_INLINE constexpr static uint16_t GetIndexForDenserBuckets( size_t size); - PA_ALWAYS_INLINE constexpr static size_t GetIndex(size_t size); + PA_ALWAYS_INLINE constexpr static uint16_t GetIndex(size_t size); constexpr BucketIndexLookup() { constexpr uint16_t sentinel_bucket_index = kNumBuckets; @@ -222,7 +222,7 @@ } // static -PA_ALWAYS_INLINE constexpr size_t BucketIndexLookup::GetIndex(size_t size) { +PA_ALWAYS_INLINE constexpr uint16_t BucketIndexLookup::GetIndex(size_t size) { // For any order 2^N, under the denser bucket distribution ("Distribution A"), // we have 4 evenly distributed buckets: 2^N, 1.25*2^N, 1.5*2^N, and 1.75*2^N. // These numbers represent the maximum size of an allocation that can go into @@ -243,18 +243,18 @@ // Distribution A, but to the 2^11 bucket under Distribution B. if (1 << 8 < size && size < 1 << 19) return BucketIndexLookup::GetIndexForDenserBuckets(RoundUpSize(size)); - else - return BucketIndexLookup::GetIndexForDenserBuckets(size); + return BucketIndexLookup::GetIndexForDenserBuckets(size); } // static -PA_ALWAYS_INLINE constexpr size_t BucketIndexLookup::GetIndexForDenserBuckets( +PA_ALWAYS_INLINE constexpr uint16_t BucketIndexLookup::GetIndexForDenserBuckets( size_t size) { // This forces the bucket table to be constant-initialized and immediately // materialized in the binary. constexpr BucketIndexLookup lookup{}; - const uint8_t order = - kBitsPerSizeT - base::bits::CountLeadingZeroBitsSizeT(size); + const size_t order = + kBitsPerSizeT - + static_cast<size_t>(base::bits::CountLeadingZeroBits(size)); // The order index is simply the next few bits after the most significant // bit. const size_t order_index =
diff --git a/base/allocator/partition_allocator/partition_root.h b/base/allocator/partition_allocator/partition_root.h index 0f9173d..615153f 100644 --- a/base/allocator/partition_allocator/partition_root.h +++ b/base/allocator/partition_allocator/partition_root.h
@@ -1390,7 +1390,8 @@ // direct-mapped allocations are uncommon. if (PA_LIKELY(flags.with_thread_cache && !IsDirectMappedBucket(slot_span->bucket))) { - size_t bucket_index = slot_span->bucket - this->buckets; + size_t bucket_index = + static_cast<size_t>(slot_span->bucket - this->buckets); auto* thread_cache = ThreadCache::Get(); if (PA_LIKELY(ThreadCache::IsValid(thread_cache) && thread_cache->MaybePutInCache(slot_start, bucket_index))) { @@ -1614,8 +1615,7 @@ bool with_denser_bucket_distribution) { if (with_denser_bucket_distribution) return internal::BucketIndexLookup::GetIndexForDenserBuckets(size); - else - return internal::BucketIndexLookup::GetIndex(size); + return internal::BucketIndexLookup::GetIndex(size); } template <bool thread_safe> @@ -1922,7 +1922,7 @@ // size. raw_size = static_cast<size_t>(1) - << (sizeof(size_t) * 8 - + << (int{sizeof(size_t) * 8} - partition_alloc::internal::base::bits::CountLeadingZeroBits( raw_size - 1)); }
diff --git a/base/allocator/partition_allocator/starscan/state_bitmap.h b/base/allocator/partition_allocator/starscan/state_bitmap.h index 6c7ef29..31c75db2 100644 --- a/base/allocator/partition_allocator/starscan/state_bitmap.h +++ b/base/allocator/partition_allocator/starscan/state_bitmap.h
@@ -404,7 +404,7 @@ CellType value = LoadCell(cell_index); while (value) { const size_t trailing_zeroes = - (base::bits::CountTrailingZeroBits(value) & ~0b1); + static_cast<size_t>(base::bits::CountTrailingZeroBits(value) & ~0b1); const size_t clear_value_mask = ~(static_cast<CellType>(kStateMask) << trailing_zeroes); const CellType bits = (value >> trailing_zeroes) & kStateMask;
diff --git a/base/bits.h b/base/bits.h index c22a8cd..51c8e894 100644 --- a/base/bits.h +++ b/base/bits.h
@@ -86,10 +86,10 @@ // similar. #if defined(COMPILER_MSVC) && !defined(__clang__) -template <typename T, unsigned bits = sizeof(T) * 8> +template <typename T, int bits = sizeof(T) * 8> ALWAYS_INLINE typename std::enable_if<std::is_unsigned<T>::value && sizeof(T) <= 4, - unsigned>::type + int>::type CountLeadingZeroBits(T x) { static_assert(bits > 0, "invalid instantiation"); unsigned long index; @@ -98,10 +98,10 @@ : bits; } -template <typename T, unsigned bits = sizeof(T) * 8> +template <typename T, int bits = sizeof(T) * 8> ALWAYS_INLINE typename std::enable_if<std::is_unsigned<T>::value && sizeof(T) == 8, - unsigned>::type + int>::type CountLeadingZeroBits(T x) { static_assert(bits > 0, "invalid instantiation"); unsigned long index; @@ -123,10 +123,10 @@ #endif } -template <typename T, unsigned bits = sizeof(T) * 8> +template <typename T, int bits = sizeof(T) * 8> ALWAYS_INLINE typename std::enable_if<std::is_unsigned<T>::value && sizeof(T) <= 4, - unsigned>::type + int>::type CountTrailingZeroBits(T x) { static_assert(bits > 0, "invalid instantiation"); unsigned long index; @@ -134,10 +134,10 @@ : bits; } -template <typename T, unsigned bits = sizeof(T) * 8> +template <typename T, int bits = sizeof(T) * 8> ALWAYS_INLINE typename std::enable_if<std::is_unsigned<T>::value && sizeof(T) == 8, - unsigned>::type + int>::type CountTrailingZeroBits(T x) { static_assert(bits > 0, "invalid instantiation"); unsigned long index; @@ -158,20 +158,16 @@ #endif } -// Used in place of "constexpr" below for things which are conditionally -// constexpr depending on whether the functions above are constexpr. -#define BASE_BITOPS_CONSTEXPR - #elif defined(COMPILER_GCC) || defined(__clang__) // __builtin_clz has undefined behaviour for an input of 0, even though there's // clearly a return value that makes sense, and even though some processor clz // instructions have defined behaviour for 0. We could drop to raw __asm__ to // do better, but we'll avoid doing that unless we see proof that we need to. -template <typename T, unsigned bits = sizeof(T) * 8> +template <typename T, int bits = sizeof(T) * 8> ALWAYS_INLINE constexpr typename std::enable_if<std::is_unsigned<T>::value && sizeof(T) <= 8, - unsigned>::type + int>::type CountLeadingZeroBits(T value) { static_assert(bits > 0, "invalid instantiation"); return LIKELY(value) @@ -181,10 +177,10 @@ : bits; } -template <typename T, unsigned bits = sizeof(T) * 8> +template <typename T, int bits = sizeof(T) * 8> ALWAYS_INLINE constexpr typename std::enable_if<std::is_unsigned<T>::value && sizeof(T) <= 8, - unsigned>::type + int>::type CountTrailingZeroBits(T value) { return LIKELY(value) ? bits == 64 ? __builtin_ctzll(static_cast<uint64_t>(value)) @@ -192,31 +188,8 @@ : bits; } -#define BASE_BITOPS_CONSTEXPR constexpr - #endif -ALWAYS_INLINE BASE_BITOPS_CONSTEXPR uint32_t -CountLeadingZeroBits32(uint32_t x) { - return CountLeadingZeroBits(x); -} - -ALWAYS_INLINE BASE_BITOPS_CONSTEXPR uint64_t -CountLeadingZeroBits64(uint64_t x) { - return CountLeadingZeroBits(x); -} - -ALWAYS_INLINE BASE_BITOPS_CONSTEXPR size_t CountLeadingZeroBitsSizeT(size_t x) { - return CountLeadingZeroBits(x); -} - -ALWAYS_INLINE BASE_BITOPS_CONSTEXPR size_t -CountTrailingZeroBitsSizeT(size_t x) { - return CountTrailingZeroBits(x); -} - -#undef BASE_BITOPS_CONSTEXPR - // Returns the integer i such as 2^i <= n < 2^(i+1). // // There is a common `BitLength` function, which returns the number of bits
diff --git a/base/bits_unittest.cc b/base/bits_unittest.cc index b4eca60..854ee26 100644 --- a/base/bits_unittest.cc +++ b/base/bits_unittest.cc
@@ -120,106 +120,106 @@ } TEST(BitsTest, CountLeadingZeroBits8) { - EXPECT_EQ(8u, CountLeadingZeroBits(uint8_t{0})); - EXPECT_EQ(7u, CountLeadingZeroBits(uint8_t{1})); - for (uint8_t shift = 0; shift <= 7; shift++) { - EXPECT_EQ(7u - shift, + EXPECT_EQ(8, CountLeadingZeroBits(uint8_t{0})); + EXPECT_EQ(7, CountLeadingZeroBits(uint8_t{1})); + for (int shift = 0; shift <= 7; ++shift) { + EXPECT_EQ(7 - shift, CountLeadingZeroBits(static_cast<uint8_t>(1 << shift))); } - EXPECT_EQ(4u, CountLeadingZeroBits(uint8_t{0x0f})); + EXPECT_EQ(4, CountLeadingZeroBits(uint8_t{0x0f})); } TEST(BitsTest, CountLeadingZeroBits16) { - EXPECT_EQ(16u, CountLeadingZeroBits(uint16_t{0})); - EXPECT_EQ(15u, CountLeadingZeroBits(uint16_t{1})); - for (uint16_t shift = 0; shift <= 15; shift++) { - EXPECT_EQ(15u - shift, + EXPECT_EQ(16, CountLeadingZeroBits(uint16_t{0})); + EXPECT_EQ(15, CountLeadingZeroBits(uint16_t{1})); + for (int shift = 0; shift <= 15; ++shift) { + EXPECT_EQ(15 - shift, CountLeadingZeroBits(static_cast<uint16_t>(1 << shift))); } - EXPECT_EQ(4u, CountLeadingZeroBits(uint16_t{0x0f0f})); + EXPECT_EQ(4, CountLeadingZeroBits(uint16_t{0x0f0f})); } TEST(BitsTest, CountLeadingZeroBits32) { - EXPECT_EQ(32u, CountLeadingZeroBits(uint32_t{0})); - EXPECT_EQ(31u, CountLeadingZeroBits(uint32_t{1})); - for (uint32_t shift = 0; shift <= 31; shift++) { - EXPECT_EQ(31u - shift, CountLeadingZeroBits(uint32_t{1} << shift)); + EXPECT_EQ(32, CountLeadingZeroBits(uint32_t{0})); + EXPECT_EQ(31, CountLeadingZeroBits(uint32_t{1})); + for (int shift = 0; shift <= 31; ++shift) { + EXPECT_EQ(31 - shift, CountLeadingZeroBits(uint32_t{1} << shift)); } - EXPECT_EQ(4u, CountLeadingZeroBits(uint32_t{0x0f0f0f0f})); + EXPECT_EQ(4, CountLeadingZeroBits(uint32_t{0x0f0f0f0f})); } -TEST(BitsTest, CountTrailingeZeroBits8) { - EXPECT_EQ(8u, CountTrailingZeroBits(uint8_t{0})); - EXPECT_EQ(7u, CountTrailingZeroBits(uint8_t{128})); - for (uint8_t shift = 0; shift <= 7; shift++) { +TEST(BitsTest, CountTrailingZeroBits8) { + EXPECT_EQ(8, CountTrailingZeroBits(uint8_t{0})); + EXPECT_EQ(7, CountTrailingZeroBits(uint8_t{128})); + for (int shift = 0; shift <= 7; ++shift) { EXPECT_EQ(shift, CountTrailingZeroBits(static_cast<uint8_t>(1 << shift))); } - EXPECT_EQ(4u, CountTrailingZeroBits(uint8_t{0xf0})); + EXPECT_EQ(4, CountTrailingZeroBits(uint8_t{0xf0})); } -TEST(BitsTest, CountTrailingeZeroBits16) { - EXPECT_EQ(16u, CountTrailingZeroBits(uint16_t{0})); - EXPECT_EQ(15u, CountTrailingZeroBits(uint16_t{32768})); - for (uint16_t shift = 0; shift <= 15; shift++) { +TEST(BitsTest, CountTrailingZeroBits16) { + EXPECT_EQ(16, CountTrailingZeroBits(uint16_t{0})); + EXPECT_EQ(15, CountTrailingZeroBits(uint16_t{32768})); + for (int shift = 0; shift <= 15; ++shift) { EXPECT_EQ(shift, CountTrailingZeroBits(static_cast<uint16_t>(1 << shift))); } - EXPECT_EQ(4u, CountTrailingZeroBits(uint16_t{0xf0f0})); + EXPECT_EQ(4, CountTrailingZeroBits(uint16_t{0xf0f0})); } -TEST(BitsTest, CountTrailingeZeroBits32) { - EXPECT_EQ(32u, CountTrailingZeroBits(uint32_t{0})); - EXPECT_EQ(31u, CountTrailingZeroBits(uint32_t{1} << 31)); - for (uint32_t shift = 0; shift <= 31; shift++) { +TEST(BitsTest, CountTrailingZeroBits32) { + EXPECT_EQ(32, CountTrailingZeroBits(uint32_t{0})); + EXPECT_EQ(31, CountTrailingZeroBits(uint32_t{1} << 31)); + for (int shift = 0; shift <= 31; ++shift) { EXPECT_EQ(shift, CountTrailingZeroBits(uint32_t{1} << shift)); } - EXPECT_EQ(4u, CountTrailingZeroBits(uint32_t{0xf0f0f0f0})); + EXPECT_EQ(4, CountTrailingZeroBits(uint32_t{0xf0f0f0f0})); } TEST(BitsTest, CountLeadingZeroBits64) { - EXPECT_EQ(64u, CountLeadingZeroBits(uint64_t{0})); - EXPECT_EQ(63u, CountLeadingZeroBits(uint64_t{1})); - for (uint64_t shift = 0; shift <= 63; shift++) { - EXPECT_EQ(63u - shift, CountLeadingZeroBits(uint64_t{1} << shift)); + EXPECT_EQ(64, CountLeadingZeroBits(uint64_t{0})); + EXPECT_EQ(63, CountLeadingZeroBits(uint64_t{1})); + for (int shift = 0; shift <= 63; ++shift) { + EXPECT_EQ(63 - shift, CountLeadingZeroBits(uint64_t{1} << shift)); } - EXPECT_EQ(4u, CountLeadingZeroBits(uint64_t{0x0f0f0f0f0f0f0f0f})); + EXPECT_EQ(4, CountLeadingZeroBits(uint64_t{0x0f0f0f0f0f0f0f0f})); } -TEST(BitsTest, CountTrailingeZeroBits64) { - EXPECT_EQ(64u, CountTrailingZeroBits(uint64_t{0})); - EXPECT_EQ(63u, CountTrailingZeroBits(uint64_t{1} << 63)); - for (uint64_t shift = 0; shift <= 31; shift++) { +TEST(BitsTest, CountTrailingZeroBits64) { + EXPECT_EQ(64, CountTrailingZeroBits(uint64_t{0})); + EXPECT_EQ(63, CountTrailingZeroBits(uint64_t{1} << 63)); + for (int shift = 0; shift <= 31; ++shift) { EXPECT_EQ(shift, CountTrailingZeroBits(uint64_t{1} << shift)); } - EXPECT_EQ(4u, CountTrailingZeroBits(uint64_t{0xf0f0f0f0f0f0f0f0})); + EXPECT_EQ(4, CountTrailingZeroBits(uint64_t{0xf0f0f0f0f0f0f0f0})); } TEST(BitsTest, CountLeadingZeroBitsSizeT) { #if defined(ARCH_CPU_64_BITS) - EXPECT_EQ(64u, CountLeadingZeroBitsSizeT(size_t{0})); - EXPECT_EQ(63u, CountLeadingZeroBitsSizeT(size_t{1})); - EXPECT_EQ(32u, CountLeadingZeroBitsSizeT(size_t{1} << 31)); - EXPECT_EQ(1u, CountLeadingZeroBitsSizeT(size_t{1} << 62)); - EXPECT_EQ(0u, CountLeadingZeroBitsSizeT(size_t{1} << 63)); + EXPECT_EQ(64, CountLeadingZeroBits(size_t{0})); + EXPECT_EQ(63, CountLeadingZeroBits(size_t{1})); + EXPECT_EQ(32, CountLeadingZeroBits(size_t{1} << 31)); + EXPECT_EQ(1, CountLeadingZeroBits(size_t{1} << 62)); + EXPECT_EQ(0, CountLeadingZeroBits(size_t{1} << 63)); #else - EXPECT_EQ(32u, CountLeadingZeroBitsSizeT(size_t{0})); - EXPECT_EQ(31u, CountLeadingZeroBitsSizeT(size_t{1})); - EXPECT_EQ(1u, CountLeadingZeroBitsSizeT(size_t{1} << 30)); - EXPECT_EQ(0u, CountLeadingZeroBitsSizeT(size_t{1} << 31)); + EXPECT_EQ(32, CountLeadingZeroBits(size_t{0})); + EXPECT_EQ(31, CountLeadingZeroBits(size_t{1})); + EXPECT_EQ(1, CountLeadingZeroBits(size_t{1} << 30)); + EXPECT_EQ(0, CountLeadingZeroBits(size_t{1} << 31)); #endif // ARCH_CPU_64_BITS } TEST(BitsTest, CountTrailingZeroBitsSizeT) { #if defined(ARCH_CPU_64_BITS) - EXPECT_EQ(64u, CountTrailingZeroBitsSizeT(size_t{0})); - EXPECT_EQ(63u, CountTrailingZeroBitsSizeT(size_t{1} << 63)); - EXPECT_EQ(31u, CountTrailingZeroBitsSizeT(size_t{1} << 31)); - EXPECT_EQ(1u, CountTrailingZeroBitsSizeT(size_t{2})); - EXPECT_EQ(0u, CountTrailingZeroBitsSizeT(size_t{1})); + EXPECT_EQ(64, CountTrailingZeroBits(size_t{0})); + EXPECT_EQ(63, CountTrailingZeroBits(size_t{1} << 63)); + EXPECT_EQ(31, CountTrailingZeroBits(size_t{1} << 31)); + EXPECT_EQ(1, CountTrailingZeroBits(size_t{2})); + EXPECT_EQ(0, CountTrailingZeroBits(size_t{1})); #else - EXPECT_EQ(32u, CountTrailingZeroBitsSizeT(size_t{0})); - EXPECT_EQ(31u, CountTrailingZeroBitsSizeT(size_t{1} << 31)); - EXPECT_EQ(1u, CountTrailingZeroBitsSizeT(size_t{2})); - EXPECT_EQ(0u, CountTrailingZeroBitsSizeT(size_t{1})); + EXPECT_EQ(32, CountTrailingZeroBits(size_t{0})); + EXPECT_EQ(31, CountTrailingZeroBits(size_t{1} << 31)); + EXPECT_EQ(1, CountTrailingZeroBits(size_t{2})); + EXPECT_EQ(0, CountTrailingZeroBits(size_t{1})); #endif // ARCH_CPU_64_BITS }
diff --git a/base/files/file_proxy.cc b/base/files/file_proxy.cc index 56ef007..432761f 100644 --- a/base/files/file_proxy.cc +++ b/base/files/file_proxy.cc
@@ -239,7 +239,7 @@ } bool FileProxy::CreateOrOpen(const FilePath& file_path, - uint32_t file_flags, + int file_flags, StatusCallback callback) { DCHECK(!file_.IsValid()); CreateOrOpenHelper* helper = new CreateOrOpenHelper(this, File());
diff --git a/base/files/file_proxy.h b/base/files/file_proxy.h index 14b0791a..ae92a4b 100644 --- a/base/files/file_proxy.h +++ b/base/files/file_proxy.h
@@ -59,7 +59,7 @@ // // This returns false if task posting to |task_runner| has failed. bool CreateOrOpen(const FilePath& file_path, - uint32_t file_flags, + int file_flags, StatusCallback callback); // Creates a temporary file for writing. The path and an open file are
diff --git a/base/i18n/streaming_utf8_validator_unittest.cc b/base/i18n/streaming_utf8_validator_unittest.cc index 058ce548..e308882 100644 --- a/base/i18n/streaming_utf8_validator_unittest.cc +++ b/base/i18n/streaming_utf8_validator_unittest.cc
@@ -48,7 +48,7 @@ int32_t char_index = 0; while (char_index < src_len) { - int32_t code_point; + base_icu::UChar32 code_point; U8_NEXT(src, char_index, src_len, code_point); if (!base::IsValidCodepoint(code_point)) return false;
diff --git a/base/json/json_parser.cc b/base/json/json_parser.cc index ee60d9df..468ea2d 100644 --- a/base/json/json_parser.cc +++ b/base/json/json_parser.cc
@@ -204,7 +204,7 @@ StringBuilder&& other) = default; void JSONParser::StringBuilder::Append(uint32_t point) { - DCHECK(IsValidCodepoint(point)); + DCHECK(IsValidCodepoint(static_cast<base_icu::UChar32>(point))); if (point < kExtendedASCIIStart && !string_) { DCHECK_EQ(static_cast<char>(point), pos_[length_]); @@ -538,7 +538,7 @@ StringBuilder string(pos()); while (PeekChar()) { - uint32_t next_char = 0; + base_icu::UChar32 next_char = 0; if (!ReadUnicodeCharacter(input_.data(), static_cast<int32_t>(input_.length()), &index_, &next_char) ||
diff --git a/base/json/string_escape.cc b/base/json/string_escape.cc index 24de682..4c1dbeb5 100644 --- a/base/json/string_escape.cc +++ b/base/json/string_escape.cc
@@ -25,7 +25,7 @@ const char kU16EscapeFormat[] = "\\u%04X"; // The code point to output for an invalid input code unit. -const uint32_t kReplacementCodePoint = 0xFFFD; +const base_icu::UChar32 kReplacementCodePoint = 0xFFFD; // Used below in EscapeSpecialCodePoint(). static_assert('<' == 0x3C, "less than sign must be 0x3c"); @@ -91,9 +91,9 @@ const int32_t length = static_cast<int32_t>(str.length()); for (int32_t i = 0; i < length; ++i) { - uint32_t code_point; + base_icu::UChar32 code_point; if (!ReadUnicodeCharacter(str.data(), length, &i, &code_point) || - code_point == static_cast<decltype(code_point)>(CBU_SENTINEL)) { + code_point == CBU_SENTINEL) { code_point = kReplacementCodePoint; did_replacement = true; }
diff --git a/base/memory/raw_ptr.cc b/base/memory/raw_ptr.cc index 720adb8..62a4fc5 100644 --- a/base/memory/raw_ptr.cc +++ b/base/memory/raw_ptr.cc
@@ -86,6 +86,7 @@ #include <sanitizer/asan_interface.h> #include "base/logging.h" +#include "base/memory/raw_ptr_asan_service.h" namespace base::internal { @@ -113,19 +114,10 @@ } } // namespace -void AsanBackupRefPtrImpl::AsanCheckIfValidInstantiation( - void const volatile* ptr) { - if (IsFreedHeapPointer(ptr)) { - LOG(ERROR) << "BackupRefPtr: Constructing a raw_ptr from a pointer " - "to an already freed allocation at " - << const_cast<void*>(ptr) << " leads to memory corruption."; - ForceRead(ptr); - } -} - void AsanBackupRefPtrImpl::AsanCheckIfValidDereference( void const volatile* ptr) { - if (IsFreedHeapPointer(ptr)) { + if (RawPtrAsanService::GetInstance().is_dereference_check_enabled() && + IsFreedHeapPointer(ptr)) { LOG(ERROR) << "BackupRefPtr: Dereferencing a raw_ptr to an already " "freed allocation at " @@ -137,7 +129,8 @@ void AsanBackupRefPtrImpl::AsanCheckIfValidExtraction( void const volatile* ptr) { - if (IsFreedHeapPointer(ptr)) { + if (RawPtrAsanService::GetInstance().is_extraction_check_enabled() && + IsFreedHeapPointer(ptr)) { LOG(ERROR) << "BackupRefPtr: Extracting from a raw_ptr to an already " "freed allocation at " @@ -150,6 +143,17 @@ } } +void AsanBackupRefPtrImpl::AsanCheckIfValidInstantiation( + void const volatile* ptr) { + if (RawPtrAsanService::GetInstance().is_instantiation_check_enabled() && + IsFreedHeapPointer(ptr)) { + LOG(ERROR) << "BackupRefPtr: Constructing a raw_ptr from a pointer " + "to an already freed allocation at " + << const_cast<void*>(ptr) << " leads to memory corruption."; + ForceRead(ptr); + } +} + } // namespace base::internal #endif // BUILDFLAG(USE_ASAN_BACKUP_REF_PTR)
diff --git a/base/memory/raw_ptr_asan_service.cc b/base/memory/raw_ptr_asan_service.cc index 8c502f6..b5f8d817 100644 --- a/base/memory/raw_ptr_asan_service.cc +++ b/base/memory/raw_ptr_asan_service.cc
@@ -11,7 +11,7 @@ #include "base/check_op.h" #include "base/compiler_specific.h" -namespace base::internal { +namespace base { RawPtrAsanService RawPtrAsanService::instance_; @@ -46,10 +46,17 @@ } NO_SANITIZE("address") -void RawPtrAsanService::Configure(Mode mode) { +void RawPtrAsanService::Configure( + EnableDereferenceCheck enable_dereference_check, + EnableExtractionCheck enable_extraction_check, + EnableInstantiationCheck enable_instantiation_check) { CHECK_EQ(mode_, Mode::kUninitialized); - if (mode == Mode::kEnabled) { + Mode new_mode = enable_dereference_check || enable_extraction_check || + enable_instantiation_check + ? Mode::kEnabled + : Mode::kDisabled; + if (new_mode == Mode::kEnabled) { // The constants we use aren't directly exposed by the API, so // validate them at runtime as carefully as possible. size_t shadow_scale; @@ -65,9 +72,13 @@ delete dummy_alloc; __sanitizer_install_malloc_and_free_hooks(MallocHook, FreeHook); + + is_dereference_check_enabled_ = !!enable_dereference_check; + is_extraction_check_enabled_ = !!enable_extraction_check; + is_instantiation_check_enabled_ = !!enable_instantiation_check; } - mode_ = mode; + mode_ = new_mode; } uint8_t* RawPtrAsanService::GetShadow(void* ptr) const { @@ -75,5 +86,5 @@ (reinterpret_cast<uintptr_t>(ptr) >> kShadowScale) + shadow_offset_); } -} // namespace base::internal +} // namespace base #endif // BUILDFLAG(USE_ASAN_BACKUP_REF_PTR)
diff --git a/base/memory/raw_ptr_asan_service.h b/base/memory/raw_ptr_asan_service.h index 01b9435..eb4abec5 100644 --- a/base/memory/raw_ptr_asan_service.h +++ b/base/memory/raw_ptr_asan_service.h
@@ -12,11 +12,18 @@ #include <cstdint> #include "base/base_export.h" +#include "base/types/strong_alias.h" -namespace base::internal { +namespace base { -BASE_EXPORT -class RawPtrAsanService { +using EnableDereferenceCheck = + base::StrongAlias<class EnableDereferenceCheckTag, bool>; +using EnableExtractionCheck = + base::StrongAlias<class EnableExtractionCheckTag, bool>; +using EnableInstantiationCheck = + base::StrongAlias<class EnableInstantiationCheckTag, bool>; + +class BASE_EXPORT RawPtrAsanService { public: enum class Mode { kUninitialized, @@ -24,11 +31,23 @@ kEnabled, }; - void Configure(Mode mode); + void Configure(EnableDereferenceCheck, + EnableExtractionCheck, + EnableInstantiationCheck); Mode mode() const { return mode_; } bool IsSupportedAllocation(void*) const; + bool is_dereference_check_enabled() const { + return is_dereference_check_enabled_; + } + bool is_extraction_check_enabled() const { + return is_extraction_check_enabled_; + } + bool is_instantiation_check_enabled() const { + return is_instantiation_check_enabled_; + } + static RawPtrAsanService& GetInstance() { return instance_; } private: @@ -38,13 +57,17 @@ static void FreeHook(const volatile void*) {} Mode mode_ = Mode::kUninitialized; + bool is_dereference_check_enabled_ = false; + bool is_extraction_check_enabled_ = false; + bool is_instantiation_check_enabled_ = false; + size_t shadow_offset_ = 0; static RawPtrAsanService instance_; // Not a static local variable because // `GetInstance()` is used in hot paths. }; -} // namespace base::internal +} // namespace base #endif // BUILDFLAG(USE_ASAN_BACKUP_REF_PTR) #endif // BASE_MEMORY_RAW_PTR_ASAN_SERVICE_H_
diff --git a/base/memory/raw_ptr_unittest.cc b/base/memory/raw_ptr_unittest.cc index 94f1b605..e9bcd48c 100644 --- a/base/memory/raw_ptr_unittest.cc +++ b/base/memory/raw_ptr_unittest.cc
@@ -1357,6 +1357,16 @@ }; TEST(AsanBackupRefPtrImpl, Dereference) { + if (RawPtrAsanService::GetInstance().mode() != + RawPtrAsanService::Mode::kEnabled) { + base::RawPtrAsanService::GetInstance().Configure( + base::EnableDereferenceCheck(true), base::EnableExtractionCheck(true), + base::EnableInstantiationCheck(true)); + } else { + ASSERT_TRUE( + base::RawPtrAsanService::GetInstance().is_dereference_check_enabled()); + } + raw_ptr<AsanStruct> protected_ptr = new AsanStruct; // The four statements below should succeed. @@ -1378,6 +1388,16 @@ } TEST(AsanBackupRefPtrImpl, Extraction) { + if (RawPtrAsanService::GetInstance().mode() != + RawPtrAsanService::Mode::kEnabled) { + base::RawPtrAsanService::GetInstance().Configure( + base::EnableDereferenceCheck(true), base::EnableExtractionCheck(true), + base::EnableInstantiationCheck(true)); + } else { + ASSERT_TRUE( + base::RawPtrAsanService::GetInstance().is_extraction_check_enabled()); + } + raw_ptr<AsanStruct> protected_ptr = new AsanStruct; AsanStruct* ptr1 = protected_ptr; // Shouldn't crash. @@ -1394,6 +1414,16 @@ } TEST(AsanBackupRefPtrImpl, Instantiation) { + if (RawPtrAsanService::GetInstance().mode() != + RawPtrAsanService::Mode::kEnabled) { + base::RawPtrAsanService::GetInstance().Configure( + base::EnableDereferenceCheck(true), base::EnableExtractionCheck(true), + base::EnableInstantiationCheck(true)); + } else { + ASSERT_TRUE(base::RawPtrAsanService::GetInstance() + .is_instantiation_check_enabled()); + } + AsanStruct* ptr = new AsanStruct; raw_ptr<AsanStruct> protected_ptr1 = ptr; // Shouldn't crash. @@ -1416,7 +1446,9 @@ AsanStruct* ptr1 = new AsanStruct; - RawPtrAsanService::GetInstance().Configure(RawPtrAsanService::Mode::kEnabled); + base::RawPtrAsanService::GetInstance().Configure( + base::EnableDereferenceCheck(true), base::EnableExtractionCheck(true), + base::EnableInstantiationCheck(true)); AsanStruct* ptr2 = new AsanStruct;
diff --git a/base/sampling_heap_profiler/lock_free_address_hash_set.h b/base/sampling_heap_profiler/lock_free_address_hash_set.h index 3050c5f..bfa8a27 100644 --- a/base/sampling_heap_profiler/lock_free_address_hash_set.h +++ b/base/sampling_heap_profiler/lock_free_address_hash_set.h
@@ -84,7 +84,7 @@ ALWAYS_INLINE Node* FindNode(void* key) const; std::vector<std::atomic<Node*>> buckets_; - int size_ = 0; + size_t size_ = 0; const size_t bucket_mask_; };
diff --git a/base/strings/escape.cc b/base/strings/escape.cc index 8b98c04..e9b9afc 100644 --- a/base/strings/escape.cc +++ b/base/strings/escape.cc
@@ -212,7 +212,7 @@ // |escaped_text| that corresponds to the unescaped character. bool UnescapeUTF8CharacterAtIndex(StringPiece escaped_text, size_t index, - uint32_t* code_point_out, + base_icu::UChar32* code_point_out, std::string* unescaped_out) { DCHECK(unescaped_out->empty()); @@ -406,7 +406,7 @@ // Locations of adjusted text. for (size_t i = 0, max = escaped_text.size(); i < max;) { // Try to unescape the character. - uint32_t code_point; + base_icu::UChar32 code_point; std::string unescaped; if (!UnescapeUTF8CharacterAtIndex(escaped_text, i, &code_point, &unescaped)) {
diff --git a/base/strings/safe_sprintf.h b/base/strings/safe_sprintf.h index bc1b2b1..5323f8b9 100644 --- a/base/strings/safe_sprintf.h +++ b/base/strings/safe_sprintf.h
@@ -175,7 +175,7 @@ integer.width = sizeof(long long); } Arg(unsigned long long j) : type(UINT) { - integer.i = j; + integer.i = static_cast<int64_t>(j); integer.width = sizeof(long long); }
diff --git a/base/strings/string_number_conversions.cc b/base/strings/string_number_conversions.cc index 892f0c69..c97c1755 100644 --- a/base/strings/string_number_conversions.cc +++ b/base/strings/string_number_conversions.cc
@@ -155,19 +155,21 @@ bool HexStringToBytes(StringPiece input, std::vector<uint8_t>* output) { DCHECK(output->empty()); - return internal::HexStringToByteContainer(input, std::back_inserter(*output)); + return internal::HexStringToByteContainer<uint8_t>( + input, std::back_inserter(*output)); } bool HexStringToString(StringPiece input, std::string* output) { DCHECK(output->empty()); - return internal::HexStringToByteContainer(input, std::back_inserter(*output)); + return internal::HexStringToByteContainer<char>(input, + std::back_inserter(*output)); } bool HexStringToSpan(StringPiece input, base::span<uint8_t> output) { if (input.size() / 2 != output.size()) return false; - return internal::HexStringToByteContainer(input, output.begin()); + return internal::HexStringToByteContainer<uint8_t>(input, output.begin()); } } // namespace base
diff --git a/base/strings/string_number_conversions_internal.h b/base/strings/string_number_conversions_internal.h index 0a7f0f07..ac9985e 100644 --- a/base/strings/string_number_conversions_internal.h +++ b/base/strings/string_number_conversions_internal.h
@@ -252,7 +252,7 @@ char buffer[32]; double_conversion::StringBuilder builder(buffer, sizeof(buffer)); GetDoubleToStringConverter()->ToShortest(value, &builder); - return ToString<StringT>(buffer, builder.position()); + return ToString<StringT>(buffer, static_cast<size_t>(builder.position())); } template <typename STRING, typename CHAR> @@ -263,8 +263,8 @@ 0.0, 0, nullptr, nullptr); int processed_characters_count; - output = - converter.StringToDouble(data, input.size(), &processed_characters_count); + output = converter.StringToDouble(data, checked_cast<int>(input.size()), + &processed_characters_count); // Cases to return false: // - If the input string is empty, there was nothing to parse. @@ -278,7 +278,7 @@ !IsUnicodeWhitespace(input[0]); } -template <typename OutIter> +template <typename Char, typename OutIter> static bool HexStringToByteContainer(StringPiece input, OutIter output) { size_t count = input.size(); if (count == 0 || (count % 2) != 0) @@ -291,7 +291,7 @@ if (!msb || !lsb) { return false; } - *(output++) = (*msb << 4) | *lsb; + *(output++) = static_cast<Char>((*msb << 4) | *lsb); } return true; }
diff --git a/base/strings/string_piece.h b/base/strings/string_piece.h index 1554cfd..3be3d792 100644 --- a/base/strings/string_piece.h +++ b/base/strings/string_piece.h
@@ -607,10 +607,10 @@ // This is a custom hash function. We don't use the ones already defined for // string and std::u16string directly because it would require the string // constructors to be called, which we don't want. - std::size_t operator()(StringPieceType sp) const { - std::size_t result = 0; + size_t operator()(StringPieceType sp) const { + size_t result = 0; for (auto c : sp) - result = (result * 131) + c; + result = (result * 131) + static_cast<size_t>(c); return result; } };
diff --git a/base/strings/string_util.cc b/base/strings/string_util.cc index 7dd66abc..7c8cf62 100644 --- a/base/strings/string_util.cc +++ b/base/strings/string_util.cc
@@ -175,8 +175,7 @@ int32_t prev = char_index; base_icu::UChar32 code_point = 0; CBU8_NEXT(data, char_index, truncation_length, code_point); - if (!IsValidCharacter(code_point) || - !IsValidCodepoint(code_point)) { + if (!IsValidCharacter(code_point)) { char_index = prev - 1; } else { break; @@ -291,24 +290,12 @@ return internal::EndsWithT(str, search_for, case_sensitivity); } -char HexDigitToInt(wchar_t c) { +char HexDigitToInt(char c) { DCHECK(IsHexDigit(c)); if (c >= '0' && c <= '9') return static_cast<char>(c - '0'); - if (c >= 'A' && c <= 'F') - return static_cast<char>(c - 'A' + 10); - if (c >= 'a' && c <= 'f') - return static_cast<char>(c - 'a' + 10); - return 0; -} - -bool IsUnicodeWhitespace(wchar_t c) { - // kWhitespaceWide is a NULL-terminated string - for (const wchar_t* cur = kWhitespaceWide; *cur; ++cur) { - if (*cur == c) - return true; - } - return false; + return (c >= 'A' && c <= 'F') ? static_cast<char>(c - 'A' + 10) + : static_cast<char>(c - 'a' + 10); } static const char* const kByteStringsUnlocalized[] = {
diff --git a/base/strings/string_util.h b/base/strings/string_util.h index 9c101bb..87009d7 100644 --- a/base/strings/string_util.h +++ b/base/strings/string_util.h
@@ -123,7 +123,7 @@ template <typename CharT, typename = std::enable_if_t<std::is_integral<CharT>::value>> CharT ToUpperASCII(CharT c) { - return (c >= 'a' && c <= 'z') ? (c + ('A' - 'a')) : c; + return (c >= 'a' && c <= 'z') ? static_cast<CharT>(c + 'A' - 'a') : c; } // Converts the given string to it's ASCII-lowercase equivalent. @@ -417,11 +417,24 @@ // '4' -> 4 // 'a' -> 10 // 'B' -> 11 -// Assumes the input is a valid hex character. DCHECKs in debug builds if not. -BASE_EXPORT char HexDigitToInt(wchar_t c); +// Assumes the input is a valid hex character. +BASE_EXPORT char HexDigitToInt(char c); +inline char HexDigitToInt(char16_t c) { + DCHECK(IsHexDigit(c)); + return HexDigitToInt(static_cast<char>(c)); +} // Returns true if it's a Unicode whitespace character. -BASE_EXPORT bool IsUnicodeWhitespace(wchar_t c); +template <typename Char> +inline bool IsUnicodeWhitespace(Char c) { + // kWhitespaceWide is a NUL-terminated string + for (const auto* cur = kWhitespaceWide; *cur; ++cur) { + if (static_cast<typename std::make_unsigned_t<wchar_t>>(*cur) == + static_cast<typename std::make_unsigned_t<Char>>(c)) + return true; + } + return false; +}; // Return a byte string in human-readable format with a unit suffix. Not // appropriate for use in any UI; use of FormatBytes and friends in ui/base is
diff --git a/base/strings/string_util_internal.h b/base/strings/string_util_internal.h index eb2ca21c..d5fc93a 100644 --- a/base/strings/string_util_internal.h +++ b/base/strings/string_util_internal.h
@@ -26,7 +26,7 @@ : parameter(parameter), offset(offset) {} // Index of the parameter. - uintptr_t parameter; + size_t parameter; // Starting position in the string. size_t offset; @@ -147,7 +147,7 @@ bool in_whitespace = true; bool already_trimmed = true; - int chars_written = 0; + size_t chars_written = 0; for (auto c : text) { if (IsUnicodeWhitespace(c)) { if (!in_whitespace) { @@ -196,7 +196,7 @@ // Prologue: align the input. while (!IsMachineWordAligned(characters) && characters < end) - all_char_bits |= *characters++; + all_char_bits |= static_cast<MachineWord>(*characters++); if (all_char_bits & non_ascii_bit_mask) return false; @@ -222,19 +222,19 @@ // Process the remaining bytes. while (characters < end) - all_char_bits |= *characters++; + all_char_bits |= static_cast<MachineWord>(*characters++); return !(all_char_bits & non_ascii_bit_mask); } -template <bool (*Validator)(uint32_t)> +template <bool (*Validator)(base_icu::UChar32)> inline bool DoIsStringUTF8(StringPiece str) { - const char* src = str.data(); - int32_t src_len = static_cast<int32_t>(str.length()); - int32_t char_index = 0; + const uint8_t* src = reinterpret_cast<const uint8_t*>(str.data()); + size_t src_len = str.length(); + size_t char_index = 0; while (char_index < src_len) { - int32_t code_point; + base_icu::UChar32 code_point; CBU8_NEXT(src, char_index, src_len, code_point); if (!Validator(code_point)) return false; @@ -583,10 +583,9 @@ DLOG(ERROR) << "Invalid placeholder: $" << *i; continue; } - uintptr_t index = *i - '1'; + size_t index = static_cast<size_t>(*i - '1'); if (offsets) { - ReplacementOffset r_offset(index, - static_cast<int>(formatted.size())); + ReplacementOffset r_offset(index, formatted.size()); r_offsets.insert( ranges::upper_bound(r_offsets, r_offset, &CompareParameter), r_offset);
diff --git a/base/strings/utf_offset_string_conversions.cc b/base/strings/utf_offset_string_conversions.cc index 903a9fdc..f243e58 100644 --- a/base/strings/utf_offset_string_conversions.cc +++ b/base/strings/utf_offset_string_conversions.cc
@@ -192,7 +192,7 @@ bool success = true; int32_t src_len32 = static_cast<int32_t>(src_len); for (int32_t i = 0; i < src_len32; i++) { - uint32_t code_point; + base_icu::UChar32 code_point; size_t original_i = i; size_t chars_written = 0; if (ReadUnicodeCharacter(src, src_len32, &i, &code_point)) {
diff --git a/base/strings/utf_string_conversion_utils.cc b/base/strings/utf_string_conversion_utils.cc index f0dfc78..c530637 100644 --- a/base/strings/utf_string_conversion_utils.cc +++ b/base/strings/utf_string_conversion_utils.cc
@@ -14,13 +14,10 @@ bool ReadUnicodeCharacter(const char* src, int32_t src_len, int32_t* char_index, - uint32_t* code_point_out) { - // U8_NEXT expects to be able to use -1 to signal an error, so we must - // use a signed type for code_point. But this function returns false - // on error anyway, so code_point_out is unsigned. - int32_t code_point; + base_icu::UChar32* code_point_out) { + base_icu::UChar32 code_point; CBU8_NEXT(src, *char_index, src_len, code_point); - *code_point_out = static_cast<uint32_t>(code_point); + *code_point_out = code_point; // The ICU macro above moves to the next char, we want to point to the last // char consumed. @@ -33,7 +30,7 @@ bool ReadUnicodeCharacter(const char16_t* src, int32_t src_len, int32_t* char_index, - uint32_t* code_point) { + base_icu::UChar32* code_point) { if (CBU16_IS_SURROGATE(src[*char_index])) { if (!CBU16_IS_SURROGATE_LEAD(src[*char_index]) || *char_index + 1 >= src_len || @@ -58,7 +55,7 @@ bool ReadUnicodeCharacter(const wchar_t* src, int32_t src_len, int32_t* char_index, - uint32_t* code_point) { + base_icu::UChar32* code_point) { // Conversion is easy since the source is 32-bit. *code_point = src[*char_index];
diff --git a/base/strings/utf_string_conversion_utils.h b/base/strings/utf_string_conversion_utils.h index 775089a4..2ca9262 100644 --- a/base/strings/utf_string_conversion_utils.h +++ b/base/strings/utf_string_conversion_utils.h
@@ -14,26 +14,29 @@ #include <string> #include "base/base_export.h" +#include "base/third_party/icu/icu_utf.h" +#include "build/build_config.h" namespace base { -inline bool IsValidCodepoint(uint32_t code_point) { +inline bool IsValidCodepoint(base_icu::UChar32 code_point) { // Excludes code points that are not Unicode scalar values, i.e. // surrogate code points ([0xD800, 0xDFFF]). Additionally, excludes // code points larger than 0x10FFFF (the highest codepoint allowed). // Non-characters and unassigned code points are allowed. // https://unicode.org/glossary/#unicode_scalar_value - return code_point < 0xD800u || - (code_point >= 0xE000u && code_point <= 0x10FFFFu); + return (code_point >= 0 && code_point < 0xD800) || + (code_point >= 0xE000 && code_point <= 0x10FFFF); } -inline bool IsValidCharacter(uint32_t code_point) { +inline bool IsValidCharacter(base_icu::UChar32 code_point) { // Excludes non-characters (U+FDD0..U+FDEF, and all code points // ending in 0xFFFE or 0xFFFF) from the set of valid code points. // https://unicode.org/faq/private_use.html#nonchar1 - return code_point < 0xD800u || (code_point >= 0xE000u && - code_point < 0xFDD0u) || (code_point > 0xFDEFu && - code_point <= 0x10FFFFu && (code_point & 0xFFFEu) != 0xFFFEu); + return (code_point >= 0 && code_point < 0xD800) || + (code_point >= 0xE000 && code_point < 0xFDD0) || + (code_point > 0xFDEF && code_point <= 0x10FFFF && + (code_point & 0xFFFE) != 0xFFFE); } // ReadUnicodeCharacter -------------------------------------------------------- @@ -48,20 +51,20 @@ BASE_EXPORT bool ReadUnicodeCharacter(const char* src, int32_t src_len, int32_t* char_index, - uint32_t* code_point_out); + base_icu::UChar32* code_point_out); // Reads a UTF-16 character. The usage is the same as the 8-bit version above. BASE_EXPORT bool ReadUnicodeCharacter(const char16_t* src, int32_t src_len, int32_t* char_index, - uint32_t* code_point); + base_icu::UChar32* code_point); #if defined(WCHAR_T_IS_UTF32) // Reads UTF-32 character. The usage is the same as the 8-bit version above. BASE_EXPORT bool ReadUnicodeCharacter(const wchar_t* src, int32_t src_len, int32_t* char_index, - uint32_t* code_point); + base_icu::UChar32* code_point); #endif // defined(WCHAR_T_IS_UTF32) // WriteUnicodeCharacter -------------------------------------------------------
diff --git a/base/strings/utf_string_conversions.cc b/base/strings/utf_string_conversions.cc index 8269b8ef..bf0d322 100644 --- a/base/strings/utf_string_conversions.cc +++ b/base/strings/utf_string_conversions.cc
@@ -20,7 +20,7 @@ namespace { -constexpr int32_t kErrorCodePoint = 0xFFFD; +constexpr base_icu::UChar32 kErrorCodePoint = 0xFFFD; // Size coefficient ---------------------------------------------------------- // The maximum number of codeunits in the destination encoding corresponding to @@ -71,17 +71,23 @@ bool>; template <typename Char, EnableIfBitsAre<Char, 8> = true> -void UnicodeAppendUnsafe(Char* out, int32_t* size, uint32_t code_point) { - CBU8_APPEND_UNSAFE(out, *size, code_point); +void UnicodeAppendUnsafe(Char* out, + size_t* size, + base_icu::UChar32 code_point) { + CBU8_APPEND_UNSAFE(reinterpret_cast<uint8_t*>(out), *size, code_point); } template <typename Char, EnableIfBitsAre<Char, 16> = true> -void UnicodeAppendUnsafe(Char* out, int32_t* size, uint32_t code_point) { +void UnicodeAppendUnsafe(Char* out, + size_t* size, + base_icu::UChar32 code_point) { CBU16_APPEND_UNSAFE(out, *size, code_point); } template <typename Char, EnableIfBitsAre<Char, 32> = true> -void UnicodeAppendUnsafe(Char* out, int32_t* size, uint32_t code_point) { +void UnicodeAppendUnsafe(Char* out, + size_t* size, + base_icu::UChar32 code_point) { out[(*size)++] = code_point; } @@ -91,14 +97,14 @@ template <typename DestChar> bool DoUTFConversion(const char* src, - int32_t src_len, + size_t src_len, DestChar* dest, - int32_t* dest_len) { + size_t* dest_len) { bool success = true; - for (int32_t i = 0; i < src_len;) { - int32_t code_point; - CBU8_NEXT(src, i, src_len, code_point); + for (size_t i = 0; i < src_len;) { + base_icu::UChar32 code_point; + CBU8_NEXT(reinterpret_cast<const uint8_t*>(src), i, src_len, code_point); if (!IsValidCodepoint(code_point)) { success = false; @@ -113,12 +119,12 @@ template <typename DestChar> bool DoUTFConversion(const char16_t* src, - int32_t src_len, + size_t src_len, DestChar* dest, - int32_t* dest_len) { + size_t* dest_len) { bool success = true; - auto ConvertSingleChar = [&success](char16_t in) -> int32_t { + auto ConvertSingleChar = [&success](char16_t in) -> base_icu::UChar32 { if (!CBU16_IS_SINGLE(in) || !IsValidCodepoint(in)) { success = false; return kErrorCodePoint; @@ -126,12 +132,12 @@ return in; }; - int32_t i = 0; + size_t i = 0; // Always have another symbol in order to avoid checking boundaries in the // middle of the surrogate pair. - while (i < src_len - 1) { - int32_t code_point; + while (i + 1 < src_len) { + base_icu::UChar32 code_point; if (CBU16_IS_LEAD(src[i]) && CBU16_IS_TRAIL(src[i + 1])) { code_point = CBU16_GET_SUPPLEMENTARY(src[i], src[i + 1]); @@ -148,8 +154,9 @@ UnicodeAppendUnsafe(dest, dest_len, code_point); } - if (i < src_len) + if (i < src_len) { UnicodeAppendUnsafe(dest, dest_len, ConvertSingleChar(src[i])); + } return success; } @@ -158,13 +165,13 @@ template <typename DestChar> bool DoUTFConversion(const wchar_t* src, - int32_t src_len, + size_t src_len, DestChar* dest, - int32_t* dest_len) { + size_t* dest_len) { bool success = true; - for (int32_t i = 0; i < src_len; ++i) { - int32_t code_point = src[i]; + for (size_t i = 0; i < src_len; ++i) { + base_icu::UChar32 code_point = src[i]; if (!IsValidCodepoint(code_point)) { success = false; @@ -197,12 +204,12 @@ auto* dest = &(*dest_str)[0]; // ICU requires 32 bit numbers. - int32_t src_len32 = static_cast<int32_t>(src_str.length()); - int32_t dest_len32 = 0; + size_t src_len = src_str.length(); + size_t dest_len = 0; - bool res = DoUTFConversion(src_str.data(), src_len32, dest, &dest_len32); + bool res = DoUTFConversion(src_str.data(), src_len, dest, &dest_len); - dest_str->resize(dest_len32); + dest_str->resize(dest_len); dest_str->shrink_to_fit(); return res;
diff --git a/base/substring_set_matcher/matcher_string_pattern.h b/base/substring_set_matcher/matcher_string_pattern.h index 935ac601..ee7be64 100644 --- a/base/substring_set_matcher/matcher_string_pattern.h +++ b/base/substring_set_matcher/matcher_string_pattern.h
@@ -20,10 +20,10 @@ // need to contain unique IDs. class BASE_EXPORT MatcherStringPattern { public: - typedef int ID; + using ID = size_t; // An invalid ID value. Clients must not use this as the id. - static constexpr ID kInvalidId = -1; + static constexpr ID kInvalidId = static_cast<ID>(-1); MatcherStringPattern(std::string pattern, ID id);
diff --git a/base/substring_set_matcher/string_pattern_unittest.cc b/base/substring_set_matcher/string_pattern_unittest.cc index e597a04..993a504 100644 --- a/base/substring_set_matcher/string_pattern_unittest.cc +++ b/base/substring_set_matcher/string_pattern_unittest.cc
@@ -13,7 +13,7 @@ TEST(MatcherStringPatternTest, MatcherStringPattern) { MatcherStringPattern r1("Test", 2); EXPECT_EQ("Test", r1.pattern()); - EXPECT_EQ(2, r1.id()); + EXPECT_EQ(2u, r1.id()); EXPECT_FALSE(r1 < r1); MatcherStringPattern r2("Test", 3);
diff --git a/base/substring_set_matcher/substring_set_matcher.cc b/base/substring_set_matcher/substring_set_matcher.cc index b3496da..5e1bfee 100644 --- a/base/substring_set_matcher/substring_set_matcher.cc +++ b/base/substring_set_matcher/substring_set_matcher.cc
@@ -66,8 +66,7 @@ // Check that all the match labels fit into an edge. for (const MatcherStringPattern* pattern : patterns) { - if (pattern->id() < 0 || - base::checked_cast<NodeID>(pattern->id()) >= kInvalidNodeID) { + if (pattern->id() >= kInvalidNodeID) { return false; } }
diff --git a/base/substring_set_matcher/substring_set_matcher.h b/base/substring_set_matcher/substring_set_matcher.h index 520a5e6f..fcb5076 100644 --- a/base/substring_set_matcher/substring_set_matcher.h +++ b/base/substring_set_matcher/substring_set_matcher.h
@@ -201,7 +201,8 @@ void SetMatchID(MatcherStringPattern::ID id) { DCHECK(!IsEndOfPattern()); - SetEdge(kMatchIDLabel, id); + DCHECK(id < kInvalidNodeID); // This is enforced by Build(). + SetEdge(kMatchIDLabel, static_cast<NodeID>(id)); has_outputs_ = true; }
diff --git a/base/substring_set_matcher/substring_set_matcher_unittest.cc b/base/substring_set_matcher/substring_set_matcher_unittest.cc index a9cbb65..42cb260 100644 --- a/base/substring_set_matcher/substring_set_matcher_unittest.cc +++ b/base/substring_set_matcher/substring_set_matcher_unittest.cc
@@ -26,7 +26,7 @@ patterns.emplace_back(pattern, 1); SubstringSetMatcher matcher; ASSERT_TRUE(matcher.Build(patterns)); - std::set<int> matches; + std::set<MatcherStringPattern::ID> matches; matcher.Match(test_string, &matches); size_t expected_matches = (is_match ? 1 : 0); @@ -58,7 +58,7 @@ } SubstringSetMatcher matcher; ASSERT_TRUE(matcher.Build(patterns)); - std::set<int> matches; + std::set<MatcherStringPattern::ID> matches; matcher.Match(test_string, &matches); size_t expected_matches = (is_match_1 ? 1 : 0) + (is_match_2 ? 1 : 0); @@ -143,7 +143,7 @@ auto matcher = std::make_unique<SubstringSetMatcher>(); ASSERT_TRUE(matcher->Build(patterns)); - std::set<int> matches; + std::set<MatcherStringPattern::ID> matches; matcher->Match("abd", &matches); EXPECT_EQ(2u, matches.size()); EXPECT_TRUE(matches.end() != matches.find(1)); @@ -179,7 +179,7 @@ SubstringSetMatcher matcher; matcher.Build(patterns); - std::set<int> matches; + std::set<MatcherStringPattern::ID> matches; matcher.Match(text, &matches); EXPECT_EQ(patterns.size(), matches.size()); for (const MatcherStringPattern& pattern : patterns) { @@ -192,7 +192,7 @@ std::vector<MatcherStringPattern> patterns; SubstringSetMatcher matcher; matcher.Build(patterns); - std::set<int> matches; + std::set<MatcherStringPattern::ID> matches; matcher.Match("abd", &matches); EXPECT_TRUE(matches.empty()); EXPECT_TRUE(matcher.IsEmpty());
diff --git a/base/task/thread_pool/priority_queue.h b/base/task/thread_pool/priority_queue.h index 5ab5980..1edb5f4 100644 --- a/base/task/thread_pool/priority_queue.h +++ b/base/task/thread_pool/priority_queue.h
@@ -7,6 +7,7 @@ #include <functional> #include <memory> +#include <type_traits> #include "base/base_export.h" #include "base/containers/intrusive_heap.h" @@ -66,7 +67,8 @@ // Returns the number of TaskSources with |priority|. size_t GetNumTaskSourcesWithPriority(TaskPriority priority) const { - return num_task_sources_per_priority_[static_cast<int>(priority)]; + return num_task_sources_per_priority_ + [static_cast<std::underlying_type_t<TaskPriority>>(priority)]; } // Set the PriorityQueue to empty all its TaskSources of Tasks when it is
diff --git a/base/trace_event/interned_args_helper.h b/base/trace_event/interned_args_helper.h index f3ec1975..ce3c4c3 100644 --- a/base/trace_event/interned_args_helper.h +++ b/base/trace_event/interned_args_helper.h
@@ -23,12 +23,12 @@ struct BASE_EXPORT TraceSourceLocation { const char* function_name = nullptr; const char* file_name = nullptr; - size_t line_number = 0; + int line_number = 0; TraceSourceLocation() = default; TraceSourceLocation(const char* function_name, const char* file_name, - size_t line_number) + int line_number) : function_name(function_name), file_name(file_name), line_number(line_number) {}
diff --git a/base/trace_event/traced_value_support.h b/base/trace_event/traced_value_support.h index 15e9f92..b30914e 100644 --- a/base/trace_event/traced_value_support.h +++ b/base/trace_event/traced_value_support.h
@@ -96,7 +96,7 @@ struct TraceFormatTraits<::base::TimeDelta> { static void WriteIntoTrace(perfetto::TracedValue context, const ::base::TimeDelta& value) { - std::move(context).WriteUInt64(value.InMicroseconds()); + std::move(context).WriteInt64(value.InMicroseconds()); } };
diff --git a/build/toolchain/win/ml.py b/build/toolchain/win/ml.py new file mode 100755 index 0000000..6a1b6e57 --- /dev/null +++ b/build/toolchain/win/ml.py
@@ -0,0 +1,290 @@ +#!/usr/bin/env python +# Copyright 2018 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. + +"""Wraps ml.exe or ml64.exe and postprocesses the output to be deterministic. +Sets timestamp in .obj file to 0, hence incompatible with link.exe /incremental. + +Use by prefixing the ml(64).exe invocation with this script: + python ml.py ml.exe [args...]""" + +import array +import collections +import struct +import subprocess +import sys + + +class Struct(object): + """A thin wrapper around the struct module that returns a namedtuple""" + def __init__(self, name, *args): + """Pass the name of the return type, and then an interleaved list of + format strings as used by the struct module and of field names.""" + self.fmt = '<' + ''.join(args[0::2]) + self.type = collections.namedtuple(name, args[1::2]) + + def pack_into(self, buffer, offset, data): + return struct.pack_into(self.fmt, buffer, offset, *data) + + def unpack_from(self, buffer, offset=0): + return self.type(*struct.unpack_from(self.fmt, buffer, offset)) + + def size(self): + return struct.calcsize(self.fmt) + + +def Subtract(nt, **kwargs): + """Subtract(nt, f=2) returns a new namedtuple with 2 subtracted from nt.f""" + return nt._replace(**{k: getattr(nt, k) - v for k, v in kwargs.items()}) + + +def MakeDeterministic(objdata): + # Takes data produced by ml(64).exe (without any special flags) and + # 1. Sets the timestamp to 0 + # 2. Strips the .debug$S section (which contains an unwanted absolute path) + + # This makes several assumptions about ml's output: + # - Section data is in the same order as the corresponding section headers: + # section headers preceding the .debug$S section header have their data + # preceding the .debug$S section data; likewise for section headers + # following the .debug$S section. + # - The .debug$S section contains only the absolute path to the obj file and + # nothing else, in particular there's only a single entry in the symbol + # table referring to the .debug$S section. + # - There are no COFF line number entries. + # - There's no IMAGE_SYM_CLASS_CLR_TOKEN symbol. + # These seem to hold in practice; if they stop holding this script needs to + # become smarter. + + objdata = array.array('b', objdata) # Writable, e.g. via struct.pack_into. + + # Read coff header. + COFFHEADER = Struct('COFFHEADER', + 'H', 'Machine', + 'H', 'NumberOfSections', + 'I', 'TimeDateStamp', + 'I', 'PointerToSymbolTable', + 'I', 'NumberOfSymbols', + + 'H', 'SizeOfOptionalHeader', + 'H', 'Characteristics') + coff_header = COFFHEADER.unpack_from(objdata) + assert coff_header.SizeOfOptionalHeader == 0 # Only set for binaries. + + # Read section headers following coff header. + SECTIONHEADER = Struct('SECTIONHEADER', + '8s', 'Name', + 'I', 'VirtualSize', + 'I', 'VirtualAddress', + + 'I', 'SizeOfRawData', + 'I', 'PointerToRawData', + 'I', 'PointerToRelocations', + 'I', 'PointerToLineNumbers', + + 'H', 'NumberOfRelocations', + 'H', 'NumberOfLineNumbers', + 'I', 'Characteristics') + section_headers = [] + debug_section_index = -1 + for i in range(0, coff_header.NumberOfSections): + section_header = SECTIONHEADER.unpack_from( + objdata, offset=COFFHEADER.size() + i * SECTIONHEADER.size()) + assert not section_header[0].startswith(b'/') # Support short names only. + section_headers.append(section_header) + + if section_header.Name == b'.debug$S': + assert debug_section_index == -1 + debug_section_index = i + assert debug_section_index != -1 + + data_start = COFFHEADER.size() + len(section_headers) * SECTIONHEADER.size() + + # Verify the .debug$S section looks like we expect. + assert section_headers[debug_section_index].Name == b'.debug$S' + assert section_headers[debug_section_index].VirtualSize == 0 + assert section_headers[debug_section_index].VirtualAddress == 0 + debug_size = section_headers[debug_section_index].SizeOfRawData + debug_offset = section_headers[debug_section_index].PointerToRawData + assert section_headers[debug_section_index].PointerToRelocations == 0 + assert section_headers[debug_section_index].PointerToLineNumbers == 0 + assert section_headers[debug_section_index].NumberOfRelocations == 0 + assert section_headers[debug_section_index].NumberOfLineNumbers == 0 + + # Make sure sections in front of .debug$S have their data preceding it. + for header in section_headers[:debug_section_index]: + assert header.PointerToRawData < debug_offset + assert header.PointerToRelocations < debug_offset + assert header.PointerToLineNumbers < debug_offset + + # Make sure sections after of .debug$S have their data following it. + for header in section_headers[debug_section_index + 1:]: + # Make sure the .debug$S data is at the very end of section data: + assert header.PointerToRawData > debug_offset + assert header.PointerToRelocations == 0 + assert header.PointerToLineNumbers == 0 + + # Make sure the first non-empty section's data starts right after the section + # headers. + for section_header in section_headers: + if section_header.PointerToRawData == 0: + assert section_header.PointerToRelocations == 0 + assert section_header.PointerToLineNumbers == 0 + continue + assert section_header.PointerToRawData == data_start + break + + # Make sure the symbol table (and hence, string table) appear after the last + # section: + assert (coff_header.PointerToSymbolTable >= + section_headers[-1].PointerToRawData + section_headers[-1].SizeOfRawData) + + # The symbol table contains a symbol for the no-longer-present .debug$S + # section. If we leave it there, lld-link will complain: + # + # lld-link: error: .debug$S should not refer to non-existent section 5 + # + # so we need to remove that symbol table entry as well. This shifts symbol + # entries around and we need to update symbol table indices in: + # - relocations + # - line number records (never present) + # - one aux symbol entry (IMAGE_SYM_CLASS_CLR_TOKEN; not present in ml output) + SYM = Struct('SYM', + '8s', 'Name', + 'I', 'Value', + 'h', 'SectionNumber', # Note: Signed! + 'H', 'Type', + + 'B', 'StorageClass', + 'B', 'NumberOfAuxSymbols') + i = 0 + debug_sym = -1 + while i < coff_header.NumberOfSymbols: + sym_offset = coff_header.PointerToSymbolTable + i * SYM.size() + sym = SYM.unpack_from(objdata, sym_offset) + + # 107 is IMAGE_SYM_CLASS_CLR_TOKEN, which has aux entry "CLR Token + # Definition", which contains a symbol index. Check it's never present. + assert sym.StorageClass != 107 + + # Note: sym.SectionNumber is 1-based, debug_section_index is 0-based. + if sym.SectionNumber - 1 == debug_section_index: + assert debug_sym == -1, 'more than one .debug$S symbol found' + debug_sym = i + # Make sure the .debug$S symbol looks like we expect. + # In particular, it should have exactly one aux symbol. + assert sym.Name == b'.debug$S' + assert sym.Value == 0 + assert sym.Type == 0 + assert sym.StorageClass == 3 + assert sym.NumberOfAuxSymbols == 1 + elif sym.SectionNumber > debug_section_index: + sym = Subtract(sym, SectionNumber=1) + SYM.pack_into(objdata, sym_offset, sym) + i += 1 + sym.NumberOfAuxSymbols + assert debug_sym != -1, '.debug$S symbol not found' + + # Note: Usually the .debug$S section is the last, but for files saying + # `includelib foo.lib`, like safe_terminate_process.asm in 32-bit builds, + # this isn't true: .drectve is after .debug$S. + + # Update symbol table indices in relocations. + # There are a few processor types that have one or two relocation types + # where SymbolTableIndex has a different meaning, but not for x86. + REL = Struct('REL', + 'I', 'VirtualAddress', + 'I', 'SymbolTableIndex', + 'H', 'Type') + for header in section_headers[0:debug_section_index]: + for j in range(0, header.NumberOfRelocations): + rel_offset = header.PointerToRelocations + j * REL.size() + rel = REL.unpack_from(objdata, rel_offset) + assert rel.SymbolTableIndex != debug_sym + if rel.SymbolTableIndex > debug_sym: + rel = Subtract(rel, SymbolTableIndex=2) + REL.pack_into(objdata, rel_offset, rel) + + # Update symbol table indices in line numbers -- just check they don't exist. + for header in section_headers: + assert header.NumberOfLineNumbers == 0 + + # Now that all indices are updated, remove the symbol table entry referring to + # .debug$S and its aux entry. + del objdata[coff_header.PointerToSymbolTable + debug_sym * SYM.size(): + coff_header.PointerToSymbolTable + (debug_sym + 2) * SYM.size()] + + # Now we know that it's safe to write out the input data, with just the + # timestamp overwritten to 0, the last section header cut out (and the + # offsets of all other section headers decremented by the size of that + # one section header), and the last section's data cut out. The symbol + # table offset needs to be reduced by one section header and the size of + # the missing section. + # (The COFF spec only requires on-disk sections to be aligned in image files, + # for obj files it's not required. If that wasn't the case, deleting slices + # if data would not generally be safe.) + + # Update section offsets and remove .debug$S section data. + for i in range(0, debug_section_index): + header = section_headers[i] + if header.SizeOfRawData: + header = Subtract(header, PointerToRawData=SECTIONHEADER.size()) + if header.NumberOfRelocations: + header = Subtract(header, PointerToRelocations=SECTIONHEADER.size()) + if header.NumberOfLineNumbers: + header = Subtract(header, PointerToLineNumbers=SECTIONHEADER.size()) + SECTIONHEADER.pack_into( + objdata, COFFHEADER.size() + i * SECTIONHEADER.size(), header) + for i in range(debug_section_index + 1, len(section_headers)): + header = section_headers[i] + shift = SECTIONHEADER.size() + debug_size + if header.SizeOfRawData: + header = Subtract(header, PointerToRawData=shift) + if header.NumberOfRelocations: + header = Subtract(header, PointerToRelocations=shift) + if header.NumberOfLineNumbers: + header = Subtract(header, PointerToLineNumbers=shift) + SECTIONHEADER.pack_into( + objdata, COFFHEADER.size() + i * SECTIONHEADER.size(), header) + + del objdata[debug_offset:debug_offset + debug_size] + + # Finally, remove .debug$S section header and update coff header. + coff_header = coff_header._replace(TimeDateStamp=0) + coff_header = Subtract(coff_header, + NumberOfSections=1, + PointerToSymbolTable=SECTIONHEADER.size() + debug_size, + NumberOfSymbols=2) + COFFHEADER.pack_into(objdata, 0, coff_header) + + del objdata[ + COFFHEADER.size() + debug_section_index * SECTIONHEADER.size(): + COFFHEADER.size() + (debug_section_index + 1) * SECTIONHEADER.size()] + + # All done! + if sys.version_info.major == 2: + return objdata.tostring() + else: + return objdata.tobytes() + + +def main(): + ml_result = subprocess.call(sys.argv[1:]) + if ml_result != 0: + return ml_result + + objfile = None + for i in range(1, len(sys.argv)): + if sys.argv[i].startswith('/Fo'): + objfile = sys.argv[i][len('/Fo'):] + assert objfile, 'failed to find ml output' + + with open(objfile, 'rb') as f: + objdata = f.read() + objdata = MakeDeterministic(objdata) + with open(objfile, 'wb') as f: + f.write(objdata) + + +if __name__ == '__main__': + sys.exit(main())
diff --git a/build/toolchain/win/toolchain.gni b/build/toolchain/win/toolchain.gni index 9cdcc6bf..e7fd6209 100644 --- a/build/toolchain/win/toolchain.gni +++ b/build/toolchain/win/toolchain.gni
@@ -254,12 +254,11 @@ ml = "armasm64.exe" } } else { - prefix = rebase_path("$clang_base_path/bin", root_build_dir) - ml = "$prefix/llvm-ml.exe" + # x86/x64 builds always use the MSVC assembler. if (toolchain_args.current_cpu == "x64") { - ml += " --m64" + ml = "ml64.exe" } else { - ml += " --m32" + ml = "ml.exe" } } @@ -271,6 +270,16 @@ if (toolchain_args.current_cpu != "arm64") { ml += " /c" } + if (use_lld) { + # Wrap ml(64).exe with a script that makes its output deterministic. + # It's lld only because the script zaps obj Timestamp which + # link.exe /incremental looks at. + # TODO(https://crbug.com/762167): If we end up writing an llvm-ml64, + # make sure it has deterministic output (maybe with /Brepro or + # something) and remove this wrapper. + ml_py = rebase_path("//build/toolchain/win/ml.py", root_build_dir) + ml = "$python_path $ml_py $ml" + } } if (toolchain_args.current_cpu != "arm64" || toolchain_is_clang) { command = "$python_path $_tool_wrapper_path asm-wrapper $env $ml {{defines}} {{include_dirs}} {{asmflags}} {{source}}"
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java index e3922316..0e9ebb4 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
@@ -656,7 +656,10 @@ } private void openTabletTabSwitcherIfNoTabs() { - if (!isTablet()) return; + if (!isTablet() || mLayoutManager == null + || !mTabModelOrchestrator.areTabModelsInitialized()) { + return; + } boolean gridTabSwitcherEnabled = TabUiFeatureUtilities.isTabletGridTabSwitcherEnabled(this); boolean overviewVisible = mLayoutManager.isLayoutVisible(LayoutType.TAB_SWITCHER);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/ReadingListFeatures.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/ReadingListFeatures.java index ee384f0..8744b4f3 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/ReadingListFeatures.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/ReadingListFeatures.java
@@ -20,7 +20,7 @@ * discards the "last bookmark location". Default: {@link #DEFAULT_SESSION_LENGTH_SECONDS}</li> * <li>{@code use_cct}: boolean; open Reading list items in CCT. Default: {@code true}</li> * <li>{@code use_root_bookmark_as_default}: boolean; use the root folder rather than "Mobile - * bookmarks" as the default bookmark folder. Default: {@code false}</li> + * bookmarks" as the default bookmark folder. Default: {@code true}</li> * <li>{@code read_later_min_version}: boolean; see {@link BookmarkFeatures#VERSION}.</li> * <li>{@code allow_bookmark_type_swapping}: boolean; Allow type swapping between bookmarks and * reading list types. Default: {@code false}</li> @@ -53,7 +53,7 @@ public static boolean shouldUseRootFolderAsDefaultForReadLater() { return isReadingListEnabled() && ChromeFeatureList.getFieldTrialParamByFeatureAsBoolean( - ChromeFeatureList.READ_LATER, "use_root_bookmark_as_default", false); + ChromeFeatureList.READ_LATER, "use_root_bookmark_as_default", true); } /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OWNERS b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OWNERS index 634d052..008e644 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OWNERS +++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OWNERS
@@ -1,3 +1,8 @@ -donnd@chromium.org +gangwu@chromium.org per-file Overlay*=mdjones@chromium.org + +# Secondary +bttk@chromium.org +donnd@chromium.org +twellington@chromium.org \ No newline at end of file
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/OWNERS b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/OWNERS index 03faf07..c303a4f 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/OWNERS +++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/OWNERS
@@ -1,2 +1,7 @@ +gangwu@chromium.org + +# Secondary +bttk@chromium.org donnd@chromium.org +mdjones@chromium.org twellington@chromium.org
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/OWNERS b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/OWNERS index 03faf07..a4aa196 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/OWNERS +++ b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/OWNERS
@@ -1,2 +1,5 @@ +gangwu@chromium.org +# Secondary +bttk@chromium.org donnd@chromium.org twellington@chromium.org
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/init/ChromeBrowserInitializer.java b/chrome/android/java/src/org/chromium/chrome/browser/init/ChromeBrowserInitializer.java index 06dda766..2ca0e6b 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/init/ChromeBrowserInitializer.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/init/ChromeBrowserInitializer.java
@@ -36,6 +36,7 @@ import org.chromium.chrome.browser.download.DownloadManagerService; import org.chromium.chrome.browser.flags.ChromeSwitches; import org.chromium.chrome.browser.language.GlobalAppLocaleController; +import org.chromium.chrome.browser.metrics.UmaUtils; import org.chromium.chrome.browser.signin.SigninCheckerProvider; import org.chromium.chrome.browser.webapps.ChromeWebApkHost; import org.chromium.components.background_task_scheduler.BackgroundTaskSchedulerFactory; @@ -431,6 +432,7 @@ }); MemoryPressureUma.initializeForBrowser(); + UmaUtils.recordBackgroundRestrictions(); // Needed for field trial metrics to be properly collected in minimal browser mode. ChromeCachedFlags.getInstance().cacheMinimalBrowserFlags();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/metrics/UmaUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/metrics/UmaUtils.java index 2cab87e..77868ad 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/metrics/UmaUtils.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/metrics/UmaUtils.java
@@ -4,15 +4,22 @@ package org.chromium.chrome.browser.metrics; +import android.app.ActivityManager; +import android.app.usage.UsageStatsManager; +import android.content.Context; import android.os.Build; import android.os.SystemClock; +import androidx.annotation.IntDef; + +import org.chromium.base.ContextUtils; import org.chromium.base.ObserverList; import org.chromium.base.ThreadUtils; import org.chromium.base.annotations.CalledByNative; import org.chromium.base.annotations.JNINamespace; import org.chromium.base.annotations.NativeMethods; import org.chromium.base.compat.ApiHelperForN; +import org.chromium.base.metrics.RecordHistogram; /** * Utilities to support startup metrics - Android version. @@ -56,6 +63,24 @@ } /** + * App standby bucket status, used for UMA reporting. Enum values correspond to the return + * values of {@link UsageStatsManager#getAppStandbyBucket}. + * These values are persisted to logs. Entries should not be renumbered and + * numeric values should never be reused. + */ + @IntDef({StandbyBucketStatus.ACTIVE, StandbyBucketStatus.WORKING_SET, + StandbyBucketStatus.FREQUENT, StandbyBucketStatus.RARE, StandbyBucketStatus.RESTRICTED, + StandbyBucketStatus.COUNT}) + private @interface StandbyBucketStatus { + int ACTIVE = 0; + int WORKING_SET = 1; + int FREQUENT = 2; + int RARE = 3; + int RESTRICTED = 4; + int COUNT = 5; + } + + /** * Record the time in the application lifecycle at which Chrome code first runs * (Application.attachBaseContext()). */ @@ -120,6 +145,46 @@ } /** + * Records various levels of background restrictions imposed by android on chrome. + */ + public static void recordBackgroundRestrictions() { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) return; + Context context = ContextUtils.getApplicationContext(); + ActivityManager activityManager = + (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); + boolean isBackgroundRestricted = activityManager.isBackgroundRestricted(); + RecordHistogram.recordBooleanHistogram( + "Android.BackgroundRestrictions.IsBackgroundRestricted", isBackgroundRestricted); + + UsageStatsManager usageStatsManager = + (UsageStatsManager) context.getSystemService(Context.USAGE_STATS_SERVICE); + int standbyBucket = usageStatsManager.getAppStandbyBucket(); + int standbyBucketUma = -1; + switch (standbyBucket) { + case UsageStatsManager.STANDBY_BUCKET_ACTIVE: + standbyBucketUma = StandbyBucketStatus.ACTIVE; + break; + case UsageStatsManager.STANDBY_BUCKET_WORKING_SET: + standbyBucketUma = StandbyBucketStatus.WORKING_SET; + break; + case UsageStatsManager.STANDBY_BUCKET_FREQUENT: + standbyBucketUma = StandbyBucketStatus.FREQUENT; + break; + case UsageStatsManager.STANDBY_BUCKET_RARE: + standbyBucketUma = StandbyBucketStatus.RARE; + break; + case UsageStatsManager.STANDBY_BUCKET_RESTRICTED: + standbyBucketUma = StandbyBucketStatus.RESTRICTED; + break; + default: + assert false : "Unexpected standby bucket " + standbyBucket; + } + + RecordHistogram.recordEnumeratedHistogram("Android.BackgroundRestrictions.StandbyBucket", + standbyBucketUma, StandbyBucketStatus.COUNT); + } + + /** * Sets whether metrics reporting was opt-in or not. If it was opt-in, then the enable checkbox * on first-run was default unchecked. If it was opt-out, then the checkbox was default checked. * This should only be set once, and only during first-run.
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkTest.java index 821f521..59011f89 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkTest.java
@@ -118,8 +118,7 @@ // clang-format off @RunWith(ParameterizedRunner.class) @ParameterAnnotations.UseRunnerDelegate(ChromeJUnit4RunnerDelegate.class) -@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE, "force-fieldtrials=Study/Group", - "force-fieldtrial-params=Study.Group:use_root_bookmark_as_default/false"}) +@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE}) public class BookmarkTest { // clang-format on @Rule @@ -380,6 +379,7 @@ @Test @SmallTest + @Features.EnableFeatures({ChromeFeatureList.READ_LATER + ":use_root_bookmark_as_default/false"}) public void testOpenBookmark() throws InterruptedException, ExecutionException { addBookmark(TEST_PAGE_TITLE_GOOGLE, mTestPage); openBookmarkManager(); @@ -415,6 +415,7 @@ @Test @SmallTest + @Features.EnableFeatures({ChromeFeatureList.READ_LATER + ":use_root_bookmark_as_default/false"}) public void testOpenBookmarkManager() throws InterruptedException { openBookmarkManager(); BookmarkTestUtil.waitForBookmarkModelLoaded(); @@ -427,12 +428,9 @@ @Test @MediumTest - @Features.EnableFeatures({ChromeFeatureList.READ_LATER + "<Study"}) - @CommandLineFlags.Add({"force-fieldtrials=Study/Group", - "force-fieldtrial-params=Study.Group:use_root_bookmark_as_default/true"}) + @Features.EnableFeatures({ChromeFeatureList.READ_LATER}) @Restriction({UiRestriction.RESTRICTION_TYPE_PHONE}) - public void - testOpenBookmarkManagerWhenDefaultToRootEnabled() + public void testOpenBookmarkManagerWhenDefaultToRootEnabled() throws InterruptedException, ExecutionException { openBookmarkManager(); BookmarkTestUtil.waitForBookmarkModelLoaded(); @@ -643,6 +641,7 @@ @Test @MediumTest @Feature({"RenderTest"}) + @Features.EnableFeatures({ChromeFeatureList.READ_LATER + ":use_root_bookmark_as_default/false"}) @ParameterAnnotations.UseMethodParameter(NightModeTestUtils.NightModeParams.class) public void testBookmarkFolderIcon(boolean nightModeEnabled) throws Exception { BookmarkPromoHeader.forcePromoStateForTests(SyncPromoState.NO_PROMO); @@ -702,6 +701,7 @@ @Test @MediumTest + @Features.EnableFeatures({ChromeFeatureList.READ_LATER + ":use_root_bookmark_as_default/false"}) public void testEndIconVisibilityInSelectionMode() throws Exception { BookmarkId testId = addFolder(TEST_FOLDER_TITLE); addBookmark(TEST_TITLE_A, mTestUrlA); @@ -800,6 +800,7 @@ @Test @MediumTest + @Features.EnableFeatures({ChromeFeatureList.READ_LATER + ":use_root_bookmark_as_default/false"}) public void testSmallDrag_Up_BookmarksOnly() throws Exception { List<BookmarkId> initial = new ArrayList<>(); List<BookmarkId> expected = new ArrayList<>(); @@ -863,6 +864,7 @@ @Test @MediumTest + @Features.EnableFeatures({ChromeFeatureList.READ_LATER + ":use_root_bookmark_as_default/false"}) public void testSmallDrag_Down_FoldersOnly() throws Exception { List<BookmarkId> initial = new ArrayList<>(); List<BookmarkId> expected = new ArrayList<>(); @@ -928,6 +930,7 @@ @Test @MediumTest + @Features.EnableFeatures({ChromeFeatureList.READ_LATER + ":use_root_bookmark_as_default/false"}) public void testSmallDrag_Down_MixedFoldersAndBookmarks() throws Exception { List<BookmarkId> initial = new ArrayList<>(); List<BookmarkId> expected = new ArrayList<>(); @@ -989,6 +992,7 @@ @Test @MediumTest + @Features.EnableFeatures({ChromeFeatureList.READ_LATER + ":use_root_bookmark_as_default/false"}) public void testPromoDraggability() throws Exception { BookmarkId testId = addFolder(TEST_FOLDER_TITLE); @@ -1009,6 +1013,7 @@ @Test @MediumTest + @Features.EnableFeatures({ChromeFeatureList.READ_LATER + ":use_root_bookmark_as_default/false"}) public void testPartnerFolderDraggability() throws Exception { BookmarkId testId = addFolderWithPartner(TEST_FOLDER_TITLE); BookmarkPromoHeader.forcePromoStateForTests(SyncPromoState.PROMO_FOR_SYNC_TURNED_OFF_STATE); @@ -1028,6 +1033,7 @@ @Test @MediumTest + @Features.EnableFeatures({ChromeFeatureList.READ_LATER + ":use_root_bookmark_as_default/false"}) public void testUnselectedItemDraggability() throws Exception { BookmarkId aId = addBookmark("a", mTestUrlA); addFolder(TEST_FOLDER_TITLE); @@ -1257,9 +1263,7 @@ @Test @SmallTest - @Features.EnableFeatures({ChromeFeatureList.READ_LATER + "<Study"}) - @CommandLineFlags. - Add({"force-fieldtrials=Study/Group", "force-fieldtrial-params=Study.Group:use_cct/false"}) + @Features.EnableFeatures({ChromeFeatureList.READ_LATER + ":use_cct/false"}) @Restriction({UiRestriction.RESTRICTION_TYPE_PHONE}) public void testReadingListOpenInRegularTab() throws Exception { addReadingListBookmark(TEST_PAGE_TITLE_GOOGLE, mTestUrlA); @@ -1291,9 +1295,7 @@ @Test @SmallTest - @Features.EnableFeatures({ChromeFeatureList.READ_LATER + "<Study"}) - @CommandLineFlags. - Add({"force-fieldtrials=Study/Group", "force-fieldtrial-params=Study.Group:use_cct/false"}) + @Features.EnableFeatures({ChromeFeatureList.READ_LATER + ":use_cct/false"}) @Restriction({UiRestriction.RESTRICTION_TYPE_PHONE}) public void testReadingListOpenInIncognitoTab() throws Exception { addReadingListBookmark(TEST_PAGE_TITLE_GOOGLE, mTestUrlA); @@ -1326,6 +1328,7 @@ @Test @MediumTest + @Features.EnableFeatures({ChromeFeatureList.READ_LATER + ":use_root_bookmark_as_default/false"}) public void testMoveUpMenuItem() throws Exception { addBookmark(TEST_PAGE_TITLE_GOOGLE, mTestUrlA); addFolder(TEST_FOLDER_TITLE); @@ -1352,6 +1355,7 @@ @Test @MediumTest + @Features.EnableFeatures({ChromeFeatureList.READ_LATER + ":use_root_bookmark_as_default/false"}) public void testMoveDownMenuItem() throws Exception { addBookmark(TEST_PAGE_TITLE_GOOGLE, mTestUrlA); addFolder(TEST_FOLDER_TITLE); @@ -1378,6 +1382,7 @@ @Test @MediumTest + @Features.EnableFeatures({ChromeFeatureList.READ_LATER + ":use_root_bookmark_as_default/false"}) public void testMoveDownGoneForBottomElement() throws Exception { addBookmarkWithPartner(TEST_PAGE_TITLE_GOOGLE, mTestUrlA); addFolderWithPartner(TEST_FOLDER_TITLE); @@ -1394,6 +1399,7 @@ @Test @MediumTest + @Features.EnableFeatures({ChromeFeatureList.READ_LATER + ":use_root_bookmark_as_default/false"}) public void testMoveUpGoneForTopElement() throws Exception { addBookmark(TEST_PAGE_TITLE_GOOGLE, mTestUrlA); addFolder(TEST_FOLDER_TITLE); @@ -1434,6 +1440,7 @@ @Test @MediumTest + @Features.EnableFeatures({ChromeFeatureList.READ_LATER + ":use_root_bookmark_as_default/false"}) public void testMoveButtonsGoneWithOneBookmark() throws Exception { addFolder(TEST_FOLDER_TITLE); BookmarkPromoHeader.forcePromoStateForTests(SyncPromoState.PROMO_FOR_SYNC_TURNED_OFF_STATE); @@ -1533,6 +1540,7 @@ @Test @MediumTest + @Features.EnableFeatures({ChromeFeatureList.READ_LATER + ":use_root_bookmark_as_default/false"}) public void testShowInFolder_NoScroll() throws Exception { addFolder(TEST_FOLDER_TITLE); BookmarkPromoHeader.forcePromoStateForTests(SyncPromoState.PROMO_FOR_SYNC_TURNED_OFF_STATE); @@ -1885,11 +1893,9 @@ @Test @MediumTest - @Features.EnableFeatures({ChromeFeatureList.READ_LATER + "<Study"}) - @CommandLineFlags.Add({"force-fieldtrials=Study/Group", - "force-fieldtrial-params=Study.Group:add_to_reading_list_in_app_menu/true"}) - public void - testAddToReadingListFromAppMenu() throws Exception { + @Features. + EnableFeatures({ChromeFeatureList.READ_LATER + ":add_to_reading_list_in_app_menu/true"}) + public void testAddToReadingListFromAppMenu() throws Exception { mActivityTestRule.loadUrl(mTestPage); // Click "Add to Reading List" to add the current tab. @@ -1912,6 +1918,7 @@ @Test @MediumTest + @Features.EnableFeatures({ChromeFeatureList.READ_LATER + ":use_root_bookmark_as_default/false"}) public void testBookmarksDoesNotRecordLaunchMetrics() throws Throwable { Assert.assertEquals(1, RecordHistogram.getHistogramTotalCountForTesting( @@ -1999,9 +2006,9 @@ @Test @MediumTest @Feature({"RenderTest"}) - @Features.EnableFeatures({ChromeFeatureList.BOOKMARKS_REFRESH + "<Study"}) - @CommandLineFlags.Add({"force-fieldtrials=Study/Group", - "force-fieldtrial-params=Study.Group:bookmark_visuals_enabled/true"}) + @Features. + EnableFeatures({ChromeFeatureList.BOOKMARKS_REFRESH + ":bookmark_visuals_enabled/true", + ChromeFeatureList.READ_LATER + ":use_root_bookmark_as_default/false"}) public void testBookmarksVisualRefreshFolders() throws Exception { BookmarkPromoHeader.forcePromoStateForTests(SyncPromoState.NO_PROMO); @@ -2029,11 +2036,9 @@ @Test @MediumTest @Feature({"RenderTest"}) - @Features.EnableFeatures({ChromeFeatureList.BOOKMARKS_REFRESH + "<Study"}) - @CommandLineFlags.Add({"force-fieldtrials=Study/Group", - "force-fieldtrial-params=" - + "Study.Group:bookmark_visuals_enabled/true" - + "/bookmark_compact_visuals_enabled/false"}) + @Features.EnableFeatures({ChromeFeatureList.BOOKMARKS_REFRESH + ":bookmark_visuals_enabled/true" + + "/bookmark_compact_visuals_enabled/false", + ChromeFeatureList.READ_LATER + ":use_root_bookmark_as_default/false"}) public void testBookmarksVisualRefreshBookmarks() throws Exception { BookmarkPromoHeader.forcePromoStateForTests(SyncPromoState.NO_PROMO); @@ -2061,11 +2066,9 @@ @Test @MediumTest @Feature({"RenderTest"}) - @Features.EnableFeatures({ChromeFeatureList.BOOKMARKS_REFRESH + "<Study"}) - @CommandLineFlags.Add({"force-fieldtrials=Study/Group", - "force-fieldtrial-params=" - + "Study.Group:bookmark_visuals_enabled/true" - + "/bookmark_compact_visuals_enabled/true"}) + @Features.EnableFeatures({ChromeFeatureList.BOOKMARKS_REFRESH + ":bookmark_visuals_enabled/true" + + "/bookmark_compact_visuals_enabled/true", + ChromeFeatureList.READ_LATER + ":use_root_bookmark_as_default/false"}) public void testBookmarksCompactVisualRefreshBookmarks() throws Exception { BookmarkPromoHeader.forcePromoStateForTests(SyncPromoState.NO_PROMO); @@ -2094,11 +2097,9 @@ @Test @MediumTest @Feature({"RenderTest"}) - @Features.EnableFeatures({ChromeFeatureList.BOOKMARKS_REFRESH + "<Study"}) - @CommandLineFlags.Add({"force-fieldtrials=Study/Group", - "force-fieldtrial-params=" - + "Study.Group:bookmark_visuals_enabled/true" - + "/bookmark_compact_visuals_enabled/false"}) + @Features.EnableFeatures({ChromeFeatureList.BOOKMARKS_REFRESH + ":bookmark_visuals_enabled/true" + + "/bookmark_compact_visuals_enabled/false", + ChromeFeatureList.READ_LATER + ":use_root_bookmark_as_default/false"}) public void testBookmarksVisualRefreshBookmarksAndFolder() throws Exception { BookmarkPromoHeader.forcePromoStateForTests(SyncPromoState.NO_PROMO); @@ -2135,11 +2136,9 @@ @Test @MediumTest @Feature({"RenderTest"}) - @Features.EnableFeatures({ChromeFeatureList.BOOKMARKS_REFRESH + "<Study"}) - @CommandLineFlags.Add({"force-fieldtrials=Study/Group", - "force-fieldtrial-params=" - + "Study.Group:bookmark_visuals_enabled/true" - + "/bookmark_compact_visuals_enabled/true"}) + @Features.EnableFeatures({ChromeFeatureList.BOOKMARKS_REFRESH + ":bookmark_visuals_enabled/true" + + "/bookmark_compact_visuals_enabled/true", + ChromeFeatureList.READ_LATER + ":use_root_bookmark_as_default/false"}) public void testBookmarksCompactVisualRefreshBookmarksAndFolder() throws Exception { BookmarkPromoHeader.forcePromoStateForTests(SyncPromoState.NO_PROMO);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/OWNERS b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/OWNERS index 03faf07..7e9a0c0 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/OWNERS +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/OWNERS
@@ -1,2 +1,6 @@ +gangwu@chromium.org + +# Secondary +bttk@chromium.org donnd@chromium.org twellington@chromium.org
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrArCameraAccessTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrArCameraAccessTest.java index f64e10f..50eb0aa2 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrArCameraAccessTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrArCameraAccessTest.java
@@ -73,7 +73,7 @@ testCameraAccessImageTextureNotNull() { mWebXrArTestFramework.loadFileAndAwaitInitialization( "webxr_test_camera_access", PAGE_LOAD_TIMEOUT_S); - mWebXrArTestFramework.enterSessionWithUserGestureOrFail(); + mWebXrArTestFramework.enterSessionWithUserGestureOrFail(/*needsCameraPermission=*/true); mWebXrArTestFramework.runJavaScriptOrFail( "stepStartStoringCameraTexture(1)", POLL_TIMEOUT_SHORT_MS); mWebXrArTestFramework.waitOnJavaScriptStep(); @@ -93,7 +93,7 @@ testConsecutiveCameraAccessImageTexturesNotNull() { mWebXrArTestFramework.loadFileAndAwaitInitialization( "webxr_test_camera_access", PAGE_LOAD_TIMEOUT_S); - mWebXrArTestFramework.enterSessionWithUserGestureOrFail(); + mWebXrArTestFramework.enterSessionWithUserGestureOrFail(/*needsCameraPermission=*/true); mWebXrArTestFramework.runJavaScriptOrFail( "stepStartStoringCameraTexture(3)", POLL_TIMEOUT_SHORT_MS); mWebXrArTestFramework.waitOnJavaScriptStep(); @@ -114,7 +114,7 @@ testCameraAccessImageTextureCanBeDeleted() { mWebXrArTestFramework.loadFileAndAwaitInitialization( "webxr_test_camera_access", PAGE_LOAD_TIMEOUT_S); - mWebXrArTestFramework.enterSessionWithUserGestureOrFail(); + mWebXrArTestFramework.enterSessionWithUserGestureOrFail(/*needsCameraPermission=*/true); mWebXrArTestFramework.runJavaScriptOrFail( "stepStartStoreAndDeleteCameraTexture()", POLL_TIMEOUT_SHORT_MS); mWebXrArTestFramework.waitOnJavaScriptStep(); @@ -134,7 +134,7 @@ testCameraAccessImageTextureLifetime() { mWebXrArTestFramework.loadFileAndAwaitInitialization( "webxr_test_camera_access", PAGE_LOAD_TIMEOUT_S); - mWebXrArTestFramework.enterSessionWithUserGestureOrFail(); + mWebXrArTestFramework.enterSessionWithUserGestureOrFail(/*needsCameraPermission=*/true); mWebXrArTestFramework.runJavaScriptOrFail( "stepCheckCameraTextureLifetimeLimitedToOneFrame()", POLL_TIMEOUT_SHORT_MS); mWebXrArTestFramework.waitOnJavaScriptStep();
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrArSanityTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrArSanityTest.java index e70e0f3..bb155691 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrArSanityTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrArSanityTest.java
@@ -43,6 +43,9 @@ "enable-features=WebXRIncubations,LogJsConsoleMessages"}) @MinAndroidSdkLevel(Build.VERSION_CODES.N) // WebXR for AR is only supported on N+ public class WebXrArSanityTest { + public static final boolean ENABLE_CAMERA_ACCESS = + Build.VERSION.SDK_INT >= Build.VERSION_CODES.O; + @ClassParameter private static List<ParameterSet> sClassParams = ArTestRuleUtils.generateDefaultTestRuleParameters(); @@ -73,12 +76,14 @@ mWebXrArTestFramework.loadFileAndAwaitInitialization( "webxr_test_basic_all_ar_features", PAGE_LOAD_TIMEOUT_S); - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) { + if (!ENABLE_CAMERA_ACCESS) { mWebXrArTestFramework.runJavaScriptOrFail( "disableCameraAccess()", POLL_TIMEOUT_SHORT_MS); } - mWebXrArTestFramework.enterSessionWithUserGestureOrFail(); + mWebXrArTestFramework.enterSessionWithUserGestureOrFail( + /*needsCameraPermission=*/ENABLE_CAMERA_ACCESS); + // The recording is 12 seconds long, let's tell the test to run for 10 seconds and wait for // a bit more than that before timing out. mWebXrArTestFramework.executeStepAndWait("stepStartTest(10)", 15 * 1000); @@ -98,12 +103,14 @@ mWebXrArTestFramework.loadFileAndAwaitInitialization( "webxr_test_basic_all_ar_features", PAGE_LOAD_TIMEOUT_S); - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) { + if (!ENABLE_CAMERA_ACCESS) { mWebXrArTestFramework.runJavaScriptOrFail( "disableCameraAccess()", POLL_TIMEOUT_SHORT_MS); } - mWebXrArTestFramework.enterSessionWithUserGestureOrFail(); + mWebXrArTestFramework.enterSessionWithUserGestureOrFail( + /*needsCameraPermission=*/ENABLE_CAMERA_ACCESS); + // The recording is 37 seconds long, let's tell the test to run for 30 seconds and wait for // a bit more than that before timing out. mWebXrArTestFramework.executeStepAndWait("stepStartTest(30)", 40 * 1000);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrArSessionTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrArSessionTest.java index 06709cc..3c3f10d 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrArSessionTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrArSessionTest.java
@@ -89,10 +89,9 @@ public void testArPermissionPersistance() { mWebXrArTestFramework.loadFileAndAwaitInitialization( "test_ar_request_session_succeeds", PAGE_LOAD_TIMEOUT_S); - WebContents contents = mWebXrArTestFramework.getCurrentWebContents(); // Start new session, accepting the consent prompt - mWebXrArTestFramework.enterSessionWithUserGestureOrFail(contents); + mWebXrArTestFramework.enterSessionWithUserGestureOrFail(/*needsCameraPermission=*/false); mWebXrArTestFramework.endSession(); mWebXrArTestFramework.assertNoJavaScriptErrors(); mWebXrArTestFramework.pollJavaScriptBooleanOrFail( @@ -136,7 +135,7 @@ WebContents contents = mWebXrArTestFramework.getCurrentWebContents(); // Start new session, accepting the consent prompt - mWebXrArTestFramework.enterSessionWithUserGestureOrFail(contents); + mWebXrArTestFramework.enterSessionWithUserGestureOrFail(/*needsCameraPermission=*/false); mWebXrArTestFramework.assertNoJavaScriptErrors(); // Trigger a new permission prompt to show when tapping on the canvas, then tap on it.
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrArTestFramework.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrArTestFramework.java index cc2d548..17a01b6 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrArTestFramework.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrArTestFramework.java
@@ -25,21 +25,30 @@ * Causes a test failure if it is unable to do so, or if the permission prompt is missing. * * @param webContents The Webcontents to start the AR session in. + * @param needsCameraPermission True if the session requires Camera permission. */ @Override - public void enterSessionWithUserGestureOrFail(WebContents webContents) { + public void enterSessionWithUserGestureOrFail( + WebContents webContents, boolean needsCameraPermission) { runJavaScriptOrFail( "sessionTypeToRequest = sessionTypes.AR", POLL_TIMEOUT_LONG_MS, webContents); + boolean willPromptForCamera = + needsCameraPermission && permissionRequestWouldTriggerPrompt("camera"); + enterSessionWithUserGesture(webContents); - // We expect a session permissiom prompt (in this case the AR-specific one), but should not - // get prompted for page camera permission. + // We expect a session permissiom prompt (in this case the AR-specific one): if (shouldExpectPermissionPrompt()) { PermissionUtils.waitForPermissionPrompt(); PermissionUtils.acceptPermissionPrompt(); } + if (willPromptForCamera) { + PermissionUtils.waitForPermissionPrompt(); + PermissionUtils.acceptPermissionPrompt(); + } + pollJavaScriptBooleanOrFail("sessionInfos[sessionTypes.AR].currentSession != null", POLL_TIMEOUT_LONG_MS, webContents); }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrTestFramework.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrTestFramework.java index 81907705..78c08bd2 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrTestFramework.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrTestFramework.java
@@ -152,14 +152,25 @@ * Attempts to enter a WebXR or WebVR session of some kind, failing if it is unable to. * * @param webContents The WebContents for the tab to enter the session in. + * @param needsCameraPermission True if the session requires Camera permission. */ - public abstract void enterSessionWithUserGestureOrFail(WebContents webContents); + public abstract void enterSessionWithUserGestureOrFail( + WebContents webContents, boolean needsCameraPermission); /** * Helper function to run enterSessionWithUserGestureOrFail with the current tab's WebContents. + * Session will be treated as not requiring Camera permission. */ public void enterSessionWithUserGestureOrFail() { - enterSessionWithUserGestureOrFail(getCurrentWebContents()); + enterSessionWithUserGestureOrFail(false); + } + + /** + * Helper function to run enterSessionWithUserGestureOrFail with the current tab's WebContents. + * @param needsCameraPermission True if the session requires Camera permission. + */ + public void enterSessionWithUserGestureOrFail(boolean needsCameraPermission) { + enterSessionWithUserGestureOrFail(getCurrentWebContents(), needsCameraPermission); } /**
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrVrInputTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrVrInputTest.java index f372bbb..bef138f 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrVrInputTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrVrInputTest.java
@@ -493,7 +493,8 @@ } WebXrVrTestFramework.waitOnJavaScriptStep(mTestRule.getWebContents()); - mWebXrVrTestFramework.enterSessionWithUserGestureOrFail(mTestRule.getWebContents()); + mWebXrVrTestFramework.enterSessionWithUserGestureOrFail( + mTestRule.getWebContents(), /*needsCameraPermission=*/false); // The permission toasts automatically show for ~5 seconds when entering an immersive // session, so wait for that to disappear NativeUiUtils.performActionAndWaitForVisibilityStatus(
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrVrTestFramework.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrVrTestFramework.java index afc88d5..0178906e 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrVrTestFramework.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrVrTestFramework.java
@@ -94,11 +94,21 @@ * @param webContents The WebContents of the tab to enter the immersive session in. */ @Override - public void enterSessionWithUserGestureOrFail(WebContents webContents) { + public void enterSessionWithUserGestureOrFail( + WebContents webContents, boolean needsCameraPermission) { runJavaScriptOrFail( "sessionTypeToRequest = sessionTypes.IMMERSIVE", POLL_TIMEOUT_LONG_MS, webContents); + + boolean willPromptForCamera = + needsCameraPermission && permissionRequestWouldTriggerPrompt("camera"); + enterSessionWithUserGesture(webContents); + if (willPromptForCamera) { + PermissionUtils.waitForPermissionPrompt(); + PermissionUtils.acceptPermissionPrompt(); + } + pollJavaScriptBooleanOrFail("sessionInfos[sessionTypes.IMMERSIVE].currentSession != null", POLL_TIMEOUT_LONG_MS, webContents); Assert.assertTrue("Immersive session started, but VR Shell not in presentation mode",
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrVrTransitionTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrVrTransitionTest.java index 8e786d8..8ba4bb5 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrVrTransitionTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrVrTransitionTest.java
@@ -36,7 +36,6 @@ import org.chromium.base.test.params.ParameterizedRunner; import org.chromium.base.test.util.CommandLineFlags; import org.chromium.base.test.util.CriteriaHelper; -import org.chromium.base.test.util.DisabledTest; import org.chromium.base.test.util.FlakyTest; import org.chromium.base.test.util.Restriction; import org.chromium.base.test.util.UrlUtils; @@ -48,6 +47,7 @@ import org.chromium.chrome.browser.vr.util.VrTransitionUtils; import org.chromium.chrome.test.ChromeActivityTestRule; import org.chromium.chrome.test.ChromeJUnit4RunnerDelegate; +import org.chromium.content_public.browser.test.util.TestThreadUtils; import java.io.File; import java.util.List; @@ -161,7 +161,6 @@ @Restriction({RESTRICTION_TYPE_DEVICE_DAYDREAM, RESTRICTION_TYPE_VR_DON_ENABLED}) @CommandLineFlags.Add({"enable-features=WebXR"}) @XrActivityRestriction({XrActivityRestriction.SupportedActivity.ALL}) - @DisabledTest(message = "https://crbug.com/1329550") public void testPresentationPromiseUnresolvedDuringDon_WebXr() { presentationPromiseUnresolvedDuringDonImpl( @@ -170,9 +169,27 @@ private void presentationPromiseUnresolvedDuringDonImpl( String url, WebXrVrTestFramework framework) { - framework.loadFileAndAwaitInitialization(url, PAGE_LOAD_TIMEOUT_S); - framework.enterSessionWithUserGestureAndWait(); - framework.endTest(); + try { + framework.loadFileAndAwaitInitialization(url, PAGE_LOAD_TIMEOUT_S); + framework.enterSessionWithUserGestureAndWait(); + framework.endTest(); + } finally { + // The DON flow still being open at test end can cause issues with Chrome activity + // shutdown, which in turn causes the test to fail. So, back out of the flow if + // necessary. + final UiDevice uiDevice = + UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()); + String currentPackageName = TestThreadUtils.runOnUiThreadBlockingNoException( + () -> { return uiDevice.getCurrentPackageName(); }); + if (currentPackageName == null || !currentPackageName.equals("com.google.vr.vrcore")) { + return; + } + uiDevice.pressBack(); + CriteriaHelper.pollUiThread(() -> { + String packageName = uiDevice.getCurrentPackageName(); + return packageName == null || !packageName.equals("com.google.vr.vrcore"); + }); + } } /**
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/XrTestFramework.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/XrTestFramework.java index 149b3bb..22ae5862 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/XrTestFramework.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/XrTestFramework.java
@@ -128,7 +128,8 @@ /** * Checks whether a request for the given permission would trigger a permission prompt. * - * @param permission The name of the permission to check. + * @param permission The name of the permission to check. Must come from PermissionName enum + * (see third_party/blink/renderer/modules/permissions/permission_descriptor.idl). * @param webContents The WebContents to run the JavaScript in. * @return True if the permission request would trigger a prompt, false otherwise. */ @@ -498,7 +499,8 @@ /** * Helper method to run permissionRequestWouldTriggerPrompt with the current tab's WebContents. * - * @param permission The name of the permission to check. + * @param permission The name of the permission to check. Must come from PermissionName enum + * (see third_party/blink/renderer/modules/permissions/permission_descriptor.idl). * @return True if the permission request would trigger a prompt, false otherwise. */ public boolean permissionRequestWouldTriggerPrompt(String permission) {
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/contextualsearch/OWNERS b/chrome/android/junit/src/org/chromium/chrome/browser/contextualsearch/OWNERS index 0b65700..eb14167 100644 --- a/chrome/android/junit/src/org/chromium/chrome/browser/contextualsearch/OWNERS +++ b/chrome/android/junit/src/org/chromium/chrome/browser/contextualsearch/OWNERS
@@ -1,3 +1,7 @@ +gangwu@chromium.org + +# Secondary +bttk@chromium.org donnd@chromium.org twellington@chromium.org
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn index 47efba5..24b2d5a 100644 --- a/chrome/browser/BUILD.gn +++ b/chrome/browser/BUILD.gn
@@ -4367,6 +4367,7 @@ "//chrome/app/theme:chrome_unscaled_resources_grit", "//chrome/app/vector_icons", "//chrome/browser/ash/system_web_apps/types:types", + "//chrome/browser/autofill_assistant/password_change/proto:proto", "//chrome/browser/cart:mojo_bindings", "//chrome/browser/enterprise/signals:utils", "//chrome/browser/first_party_sets", @@ -7627,6 +7628,10 @@ } } + if (is_win) { + deps += [ "//chrome/browser/resources/conflicts:build_ts" ] + } + if (is_win || is_mac || is_linux || is_chromeos || is_fuchsia) { deps += [ "//chrome/browser/resources/gaia_auth_host:modulize",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc index 663df8b..6b44941 100644 --- a/chrome/browser/about_flags.cc +++ b/chrome/browser/about_flags.cc
@@ -4361,7 +4361,7 @@ {"enable-openscreen-cast-streaming-session", flag_descriptions::kOpenscreenCastStreamingSessionName, flag_descriptions::kOpenscreenCastStreamingSessionDescription, kOsDesktop, - FEATURE_VALUE_TYPE(mirroring::features::kOpenscreenCastStreamingSession)}, + FEATURE_VALUE_TYPE(media::kOpenscreenCastStreamingSession)}, {"enable-cast-streaming-av1", flag_descriptions::kCastStreamingAv1Name, flag_descriptions::kCastStreamingAv1Description, kOsDesktop,
diff --git a/chrome/browser/android/compositor/OWNERS b/chrome/browser/android/compositor/OWNERS index f628bfa..57fa9d4 100644 --- a/chrome/browser/android/compositor/OWNERS +++ b/chrome/browser/android/compositor/OWNERS
@@ -1,5 +1,9 @@ dtrainor@chromium.org mdjones@chromium.org +per-file *contextual_search*=gangwu@chromium.org + +# Secondary +per-file *contextual_search*=bttk@chromium.org per-file *contextual_search*=donnd@chromium.org per-file *contextual_search*=twellington@chromium.org
diff --git a/chrome/browser/android/contextualsearch/OWNERS b/chrome/browser/android/contextualsearch/OWNERS index 0b65700..7e9a0c0 100644 --- a/chrome/browser/android/contextualsearch/OWNERS +++ b/chrome/browser/android/contextualsearch/OWNERS
@@ -1,3 +1,6 @@ +gangwu@chromium.org + +# Secondary +bttk@chromium.org donnd@chromium.org twellington@chromium.org -
diff --git a/chrome/browser/ash/arc/input_overlay/ui/action_label.cc b/chrome/browser/ash/arc/input_overlay/ui/action_label.cc index 450c9cb..e2e2fde 100644 --- a/chrome/browser/ash/arc/input_overlay/ui/action_label.cc +++ b/chrome/browser/ash/arc/input_overlay/ui/action_label.cc
@@ -42,9 +42,9 @@ // About focus ring. // Gap between focus ring outer edge to label. -constexpr float kHaloInset = -3; +constexpr float kHaloInset = -6; // Thickness of focus ring. -constexpr float kHaloThickness = 2; +constexpr float kHaloThickness = 4; // UI strings. // TODO(cuicuiruan): move the strings to chrome/app/generated_resources.grd
diff --git a/chrome/browser/ash/arc/nearby_share/OWNERS b/chrome/browser/ash/arc/nearby_share/OWNERS index a65cd53..a1934c0 100644 --- a/chrome/browser/ash/arc/nearby_share/OWNERS +++ b/chrome/browser/ash/arc/nearby_share/OWNERS
@@ -4,7 +4,7 @@ melzhang@chromium.org # For Nearby Share integration: -nohle@chromium.org +hansenmichael@google.com # For backup: tsergeant@chromium.org
diff --git a/chrome/browser/ash/arc/session/arc_session_manager.cc b/chrome/browser/ash/arc/session/arc_session_manager.cc index 33731ec2..2be0498 100644 --- a/chrome/browser/ash/arc/session/arc_session_manager.cc +++ b/chrome/browser/ash/arc/session/arc_session_manager.cc
@@ -1812,10 +1812,7 @@ std::deque<JobDesc> jobs = { JobDesc{kArcPrepareHostGeneratedDirJobName, UpstartOperation::JOB_START, - {std::string("IS_ARCVM=") + (is_arcvm ? "1" : "0"), - // TODO(b/233107740) Remove this once crrev.com/c/3656121 has - // landed. - std::string("ADD_NATIVE_BRIDGE_64BIT_SUPPORT=0")}}, + {std::string("IS_ARCVM=") + (is_arcvm ? "1" : "0")}}, }; ConfigureUpstartJobs(std::move(jobs), base::BindOnce(&ArcSessionManager::OnExpandPropertyFiles,
diff --git a/chrome/browser/ash/child_accounts/time_limits/web_time_limit_enforcer.cc b/chrome/browser/ash/child_accounts/time_limits/web_time_limit_enforcer.cc index 0b08b989..93f7be1 100644 --- a/chrome/browser/ash/child_accounts/time_limits/web_time_limit_enforcer.cc +++ b/chrome/browser/ash/child_accounts/time_limits/web_time_limit_enforcer.cc
@@ -63,7 +63,7 @@ url_matcher::URLMatcherConditionSet::Vector condition_set_vector; auto* condition_factory = url_matcher_->condition_factory(); - int id = 0; + base::MatcherStringPattern::ID id = 0; for (const auto& url : allowlisted_urls) { url_matcher::URLMatcherCondition condition = condition_factory->CreateURLMatchesCondition(url);
diff --git a/chrome/browser/ash/crosapi/BUILD.gn b/chrome/browser/ash/crosapi/BUILD.gn index 9f99764..d5df81d 100644 --- a/chrome/browser/ash/crosapi/BUILD.gn +++ b/chrome/browser/ash/crosapi/BUILD.gn
@@ -311,6 +311,7 @@ "//ash", "//chrome/test:test_support", "//chromeos/crosapi/mojom", + "//chromeos/dbus/shill", "//testing/gmock", ] }
diff --git a/chrome/browser/ash/crosapi/test_controller_ash.cc b/chrome/browser/ash/crosapi/test_controller_ash.cc index 6ec5d3e0..f962128a 100644 --- a/chrome/browser/ash/crosapi/test_controller_ash.cc +++ b/chrome/browser/ash/crosapi/test_controller_ash.cc
@@ -4,6 +4,8 @@ #include "chrome/browser/ash/crosapi/test_controller_ash.h" +#include <utility> + #include "ash/public/cpp/shelf_item_delegate.h" #include "ash/public/cpp/shelf_model.h" #include "ash/public/cpp/tablet_mode.h" @@ -13,17 +15,25 @@ #include "ash/wm/overview/overview_observer.h" #include "ash/wm/tablet_mode/tablet_mode_controller.h" #include "base/callback_helpers.h" +#include "base/strings/string_number_conversions.h" #include "base/strings/utf_string_conversions.h" #include "base/version.h" #include "build/chromeos_buildflags.h" #include "chrome/browser/ash/crosapi/browser_manager.h" +#include "chrome/browser/ash/crosapi/vpn_service_ash.h" #include "chrome/browser/ash/crosapi/window_util.h" +#include "chrome/browser/ash/profiles/profile_helper.h" +#include "chrome/browser/profiles/profile_manager.h" #include "chrome/browser/sharesheet/sharesheet_service.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_list.h" #include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/views/tabs/tab_scrubber_chromeos.h" +#include "chromeos/dbus/shill/shill_profile_client.h" +#include "chromeos/dbus/shill/shill_third_party_vpn_driver_client.h" #include "components/version_info/version_info.h" +#include "crypto/sha2.h" +#include "mojo/public/cpp/bindings/self_owned_receiver.h" #include "third_party/abseil-cpp/absl/types/optional.h" #include "ui/aura/window.h" #include "ui/aura/window_event_dispatcher.h" @@ -429,6 +439,14 @@ std::move(callback).Run(version_info::GetVersion().GetString()); } +void TestControllerAsh::BindTestShillController( + mojo::PendingReceiver<crosapi::mojom::TestShillController> receiver, + BindTestShillControllerCallback callback) { + mojo::MakeSelfOwnedReceiver<crosapi::mojom::TestShillController>( + std::make_unique<crosapi::TestShillControllerAsh>(), std::move(receiver)); + std::move(callback).Run(); +} + // This class waits for overview mode to either enter or exit and fires a // callback. This class will fire the callback at most once. class TestControllerAsh::OverviewWaiter : public ash::OverviewObserver { @@ -481,4 +499,43 @@ TestControllerAsh* test_controller_; }; +TestShillControllerAsh::TestShillControllerAsh() { + chromeos::ShillProfileClient::Get()->GetTestInterface()->AddProfile( + "/network/test", ash::ProfileHelper::GetUserIdHashFromProfile( + ProfileManager::GetPrimaryUserProfile())); +} + +TestShillControllerAsh::~TestShillControllerAsh() = default; + +void TestShillControllerAsh::OnPacketReceived( + const std::string& extension_id, + const std::string& configuration_name, + const std::vector<uint8_t>& data) { + const std::string key = crosapi::VpnServiceForExtensionAsh::GetKey( + extension_id, configuration_name); + const std::string shill_key = shill::kObjectPathBase + key; + // On linux ShillThirdPartyVpnDriverClient is initialized as Fake and + // therefore exposes a testing interface. + auto* client = + chromeos::ShillThirdPartyVpnDriverClient::Get()->GetTestInterface(); + CHECK(client); + client->OnPacketReceived(shill_key, + std::vector<char>(data.begin(), data.end())); +} + +void TestShillControllerAsh::OnPlatformMessage( + const std::string& extension_id, + const std::string& configuration_name, + uint32_t message) { + const std::string key = crosapi::VpnServiceForExtensionAsh::GetKey( + extension_id, configuration_name); + const std::string shill_key = shill::kObjectPathBase + key; + // On linux ShillThirdPartyVpnDriverClient is initialized as Fake and + // therefore exposes a testing interface. + auto* client = + chromeos::ShillThirdPartyVpnDriverClient::Get()->GetTestInterface(); + CHECK(client); + client->OnPlatformMessage(shill_key, message); +} + } // namespace crosapi
diff --git a/chrome/browser/ash/crosapi/test_controller_ash.h b/chrome/browser/ash/crosapi/test_controller_ash.h index 39d0b6b..3b84125f 100644 --- a/chrome/browser/ash/crosapi/test_controller_ash.h +++ b/chrome/browser/ash/crosapi/test_controller_ash.h
@@ -6,6 +6,8 @@ #define CHROME_BROWSER_ASH_CROSAPI_TEST_CONTROLLER_ASH_H_ #include <memory> +#include <string> +#include <vector> #include "chrome/browser/ash/crosapi/crosapi_ash.h" #include "chromeos/crosapi/mojom/test_controller.mojom.h" @@ -77,6 +79,10 @@ SetSelectedSharesheetAppCallback callback) override; void GetAshVersion(GetAshVersionCallback callback) override; + void BindTestShillController( + mojo::PendingReceiver<crosapi::mojom::TestShillController> receiver, + BindTestShillControllerCallback callback) override; + mojo::Remote<mojom::StandaloneBrowserTestController>& GetStandaloneBrowserTestController() { DCHECK(standalone_browser_test_controller_.is_bound()); @@ -119,6 +125,20 @@ standalone_browser_test_controller_; }; +class TestShillControllerAsh : public crosapi::mojom::TestShillController { + public: + TestShillControllerAsh(); + ~TestShillControllerAsh() override; + + // crosapi::mojom::TestShillController: + void OnPacketReceived(const std::string& extension_id, + const std::string& configuration_name, + const std::vector<uint8_t>& data) override; + void OnPlatformMessage(const std::string& extension_id, + const std::string& configuration_name, + uint32_t message) override; +}; + } // namespace crosapi #endif // CHROME_BROWSER_ASH_CROSAPI_TEST_CONTROLLER_ASH_H_
diff --git a/chrome/browser/ash/crosapi/vpn_service_ash.h b/chrome/browser/ash/crosapi/vpn_service_ash.h index 01aa9e74..8fab7f4 100644 --- a/chrome/browser/ash/crosapi/vpn_service_ash.h +++ b/chrome/browser/ash/crosapi/vpn_service_ash.h
@@ -40,7 +40,7 @@ namespace chromeos { // Fwd for friend declaration in VpnServiceAsh. -class VpnProviderApiTest; +class VpnProviderApiTestAsh; } // namespace chromeos @@ -172,7 +172,8 @@ private: friend class VpnConfigurationImpl; - friend class chromeos::VpnProviderApiTest; + friend class chromeos::VpnProviderApiTestAsh; + friend class TestShillControllerAsh; using StringToOwnedConfigurationMap = std::map<std::string, std::unique_ptr<VpnConfiguration>>; @@ -264,7 +265,7 @@ base::flat_set<std::string> vpn_extensions) override; private: - friend class chromeos::VpnProviderApiTest; + friend class chromeos::VpnProviderApiTestAsh; friend class VpnServiceForExtensionAsh; // Callback for
diff --git a/chrome/browser/ash/input_method/native_input_method_engine_observer.cc b/chrome/browser/ash/input_method/native_input_method_engine_observer.cc index a52dcd2..cb2690d0 100644 --- a/chrome/browser/ash/input_method/native_input_method_engine_observer.cc +++ b/chrome/browser/ash/input_method/native_input_method_engine_observer.cc
@@ -405,7 +405,7 @@ uint32_t Utf16ToCodepoint(const std::u16string& str) { int32_t index = 0; - uint32_t codepoint = 0; + base_icu::UChar32 codepoint = 0; base::ReadUnicodeCharacter(str.data(), str.length(), &index, &codepoint); // Should only contain a single codepoint.
diff --git a/chrome/browser/ash/login/reporting/login_logout_reporter_unittest.cc b/chrome/browser/ash/login/reporting/login_logout_reporter_unittest.cc index 39131b1..b8dfc68 100644 --- a/chrome/browser/ash/login/reporting/login_logout_reporter_unittest.cc +++ b/chrome/browser/ash/login/reporting/login_logout_reporter_unittest.cc
@@ -143,7 +143,7 @@ report_count_ = 0; auto mock_queue = std::unique_ptr<::reporting::MockReportQueue, base::OnTaskRunnerDeleter>( - new testing::NiceMock<::reporting::MockReportQueue>(), + new ::reporting::MockReportQueue(), base::OnTaskRunnerDeleter(base::SequencedTaskRunnerHandle::Get())); ON_CALL(*mock_queue, AddRecord(_, ::reporting::Priority::SECURITY, _))
diff --git a/chrome/browser/ash/login/screens/recommend_apps_screen.cc b/chrome/browser/ash/login/screens/recommend_apps_screen.cc index 2bee80fa..c2190d7 100644 --- a/chrome/browser/ash/login/screens/recommend_apps_screen.cc +++ b/chrome/browser/ash/login/screens/recommend_apps_screen.cc
@@ -16,6 +16,11 @@ #include "components/user_manager/user_manager.h" namespace ash { +namespace { +// Maximum number of recommended apps that we going to show. +const int kMaxNumberOfRecommendedApps = 20; + +} // namespace // static std::string RecommendAppsScreen::GetResultString(Result result) { @@ -150,6 +155,8 @@ app_info.Set("optimized_for_chrome", base::Value(play_extras->GetOptimizedForChrome())); app_list.Append(std::move(app_info)); + if (app_list.size() == kMaxNumberOfRecommendedApps) + break; } view_->OnLoadSuccess(base::Value(std::move(app_list))); }
diff --git a/chrome/browser/ash/policy/reporting/user_added_removed/user_added_removed_reporter_unittest.cc b/chrome/browser/ash/policy/reporting/user_added_removed/user_added_removed_reporter_unittest.cc index c4d2e75d..e1b649d 100644 --- a/chrome/browser/ash/policy/reporting/user_added_removed/user_added_removed_reporter_unittest.cc +++ b/chrome/browser/ash/policy/reporting/user_added_removed/user_added_removed_reporter_unittest.cc
@@ -32,8 +32,7 @@ public: TestHelper(std::unique_ptr<::reporting::ReportQueue, base::OnTaskRunnerDeleter> report_queue, - base::WeakPtr<testing::StrictMock<::reporting::MockReportQueue>> - mock_queue, + base::WeakPtr<::reporting::MockReportQueueStrict> mock_queue, bool should_report_event, bool should_report_user, bool is_user_new) @@ -61,7 +60,7 @@ mock_queue_->Enqueue(record, priority, std::move(enqueue_cb)); } - base::WeakPtr<testing::StrictMock<::reporting::MockReportQueue>> mock_queue_; + base::WeakPtr<::reporting::MockReportQueueStrict> mock_queue_; bool should_report_event_; @@ -82,9 +81,9 @@ user_manager_enabler_ = std::make_unique<user_manager::ScopedUserManager>( std::move(user_manager)); - mock_queue_ = new testing::StrictMock<::reporting::MockReportQueue>(); - weak_mock_queue_factory_ = std::make_unique<base::WeakPtrFactory< - testing::StrictMock<::reporting::MockReportQueue>>>(mock_queue_); + mock_queue_ = new ::reporting::MockReportQueueStrict(); + weak_mock_queue_factory_ = std::make_unique< + base::WeakPtrFactory<::reporting::MockReportQueueStrict>>(mock_queue_); } void TearDown() override { @@ -135,10 +134,9 @@ return profile; } - testing::StrictMock<::reporting::MockReportQueue>* mock_queue_; + ::reporting::MockReportQueueStrict* mock_queue_; - std::unique_ptr< - base::WeakPtrFactory<testing::StrictMock<::reporting::MockReportQueue>>> + std::unique_ptr<base::WeakPtrFactory<::reporting::MockReportQueueStrict>> weak_mock_queue_factory_; private:
diff --git a/chrome/browser/ash/policy/reporting/user_event_reporter_helper_unittest.cc b/chrome/browser/ash/policy/reporting/user_event_reporter_helper_unittest.cc index 1ed0145..3ab4fff 100644 --- a/chrome/browser/ash/policy/reporting/user_event_reporter_helper_unittest.cc +++ b/chrome/browser/ash/policy/reporting/user_event_reporter_helper_unittest.cc
@@ -23,9 +23,10 @@ UserEventReporterTestingRecord input_record; input_record.set_field1(100); - auto mock_queue = std::unique_ptr<MockReportQueue, base::OnTaskRunnerDeleter>( - new testing::StrictMock<MockReportQueue>(), - base::OnTaskRunnerDeleter(base::SequencedTaskRunnerHandle::Get())); + auto mock_queue = + std::unique_ptr<MockReportQueueStrict, base::OnTaskRunnerDeleter>( + new MockReportQueueStrict(), + base::OnTaskRunnerDeleter(base::SequencedTaskRunnerHandle::Get())); UserEventReporterTestingRecord enqueued_record; ::reporting::Priority priority; @@ -53,9 +54,10 @@ UserEventReporterTestingRecord input_record; input_record.set_field1(100); - auto mock_queue = std::unique_ptr<MockReportQueue, base::OnTaskRunnerDeleter>( - new testing::StrictMock<MockReportQueue>(), - base::OnTaskRunnerDeleter(base::SequencedTaskRunnerHandle::Get())); + auto mock_queue = + std::unique_ptr<MockReportQueueStrict, base::OnTaskRunnerDeleter>( + new MockReportQueueStrict(), + base::OnTaskRunnerDeleter(base::SequencedTaskRunnerHandle::Get())); UserEventReporterTestingRecord enqueued_record; ::reporting::Priority priority;
diff --git a/chrome/browser/autofill_assistant/password_change/apc_external_action_delegate.cc b/chrome/browser/autofill_assistant/password_change/apc_external_action_delegate.cc index 184e4de..91380aa6 100644 --- a/chrome/browser/autofill_assistant/password_change/apc_external_action_delegate.cc +++ b/chrome/browser/autofill_assistant/password_change/apc_external_action_delegate.cc
@@ -3,10 +3,17 @@ // found in the LICENSE file. #include "chrome/browser/autofill_assistant/password_change/apc_external_action_delegate.h" +#include "chrome/browser/ui/autofill_assistant/password_change/password_change_run_display.h" -// TODO(crbug.comgit /1324089): Implement once the side panel and +// TODO(crbug.com/1324089): Implement once the side panel and // UpdateDesktopSideAction are available. -ApcExternalActionDelegate::ApcExternalActionDelegate() = default; +ApcExternalActionDelegate::ApcExternalActionDelegate( + AssistantDisplayDelegate* display_delegate) + : display_delegate_(display_delegate) { + DCHECK(display_delegate_); +} + +ApcExternalActionDelegate::~ApcExternalActionDelegate() = default; void ApcExternalActionDelegate::OnActionRequested( const autofill_assistant::external::Action& action_info, @@ -18,6 +25,47 @@ std::move(end_action_callback).Run(result); } -void ApcExternalActionDelegate::OnInterruptStarted() {} +void ApcExternalActionDelegate::SetupDisplay() { + Show(PasswordChangeRunDisplay::Create(GetWeakPtr(), display_delegate_)); +} +void ApcExternalActionDelegate::OnInterruptStarted() {} void ApcExternalActionDelegate::OnInterruptFinished() {} + +// PasswordChangeRunController +void ApcExternalActionDelegate::Show( + base::WeakPtr<PasswordChangeRunDisplay> password_change_run_display) { + password_change_run_display_ = password_change_run_display; + password_change_run_display_->Show(); +} + +void ApcExternalActionDelegate::SetTopIcon( + autofill_assistant::password_change::TopIcon top_icon) { + DCHECK(password_change_run_display_); + model_.top_icon = top_icon; + password_change_run_display_->SetTopIcon(top_icon); +} + +void ApcExternalActionDelegate::SetTitle(std::u16string title) { + DCHECK(password_change_run_display_); + model_.title = title; + password_change_run_display_->SetTitle(title); +} + +void ApcExternalActionDelegate::SetDescription(std::u16string description) { + DCHECK(password_change_run_display_); + model_.description = description; + password_change_run_display_->SetDescription(description); +} + +void ApcExternalActionDelegate::SetProgressBarStep( + autofill_assistant::password_change::ProgressStep progress_step) { + DCHECK(password_change_run_display_); + model_.progress_step = progress_step; + password_change_run_display_->SetProgressBarStep(progress_step); +} + +base::WeakPtr<PasswordChangeRunController> +ApcExternalActionDelegate::GetWeakPtr() { + return weak_ptr_factory_.GetWeakPtr(); +}
diff --git a/chrome/browser/autofill_assistant/password_change/apc_external_action_delegate.h b/chrome/browser/autofill_assistant/password_change/apc_external_action_delegate.h index 3615032..e3dda7b6 100644 --- a/chrome/browser/autofill_assistant/password_change/apc_external_action_delegate.h +++ b/chrome/browser/autofill_assistant/password_change/apc_external_action_delegate.h
@@ -5,17 +5,29 @@ #ifndef CHROME_BROWSER_AUTOFILL_ASSISTANT_PASSWORD_CHANGE_APC_EXTERNAL_ACTION_DELEGATE_H_ #define CHROME_BROWSER_AUTOFILL_ASSISTANT_PASSWORD_CHANGE_APC_EXTERNAL_ACTION_DELEGATE_H_ +#include <memory> + +#include "chrome/browser/autofill_assistant/password_change/proto/extensions.pb.h" #include "chrome/browser/ui/autofill_assistant/password_change/password_change_run_controller.h" #include "components/autofill_assistant/browser/public/external_action_delegate.h" +class PasswordChangeRunDisplay; +class AssistantDisplayDelegate; + class ApcExternalActionDelegate - : public autofill_assistant::ExternalActionDelegate { + : public autofill_assistant::ExternalActionDelegate, + public PasswordChangeRunController { public: - ApcExternalActionDelegate(); + explicit ApcExternalActionDelegate( + AssistantDisplayDelegate* display_delegate); ApcExternalActionDelegate(const ApcExternalActionDelegate&) = delete; ApcExternalActionDelegate& operator=(const ApcExternalActionDelegate&) = delete; - ~ApcExternalActionDelegate() = default; + ~ApcExternalActionDelegate() override; + + // Sets up the display to render a password change run UI, + // needs to be called BEFORE starting a script. + void SetupDisplay(); // ExternalActionDelegate void OnActionRequested( @@ -26,9 +38,31 @@ void OnInterruptStarted() override; void OnInterruptFinished() override; + // PasswordChangeRunController + void SetTopIcon( + autofill_assistant::password_change::TopIcon top_icon) override; + void SetTitle(std::u16string title) override; + void SetDescription(std::u16string description) override; + void SetProgressBarStep( + autofill_assistant::password_change::ProgressStep progress_step) override; + base::WeakPtr<PasswordChangeRunController> GetWeakPtr() override; + private: + // PasswordChangeRunController + void Show(base::WeakPtr<PasswordChangeRunDisplay> password_change_run_display) + override; // Stores the UI state of a password change run. PasswordChangeRunController::Model model_; + + // The view that renders a password change run flow. + base::WeakPtr<PasswordChangeRunDisplay> password_change_run_display_ = + nullptr; + + // The display where we render the UI for a password change run. + AssistantDisplayDelegate* display_delegate_ = nullptr; + + // Factory for weak pointers to this class. + base::WeakPtrFactory<PasswordChangeRunController> weak_ptr_factory_{this}; }; #endif // CHROME_BROWSER_AUTOFILL_ASSISTANT_PASSWORD_CHANGE_APC_EXTERNAL_ACTION_DELEGATE_H_
diff --git a/chrome/browser/back_press/android/java/src/org/chromium/chrome/browser/back_press/BackPressManager.java b/chrome/browser/back_press/android/java/src/org/chromium/chrome/browser/back_press/BackPressManager.java index 4c6db3db..df270afa 100644 --- a/chrome/browser/back_press/android/java/src/org/chromium/chrome/browser/back_press/BackPressManager.java +++ b/chrome/browser/back_press/android/java/src/org/chromium/chrome/browser/back_press/BackPressManager.java
@@ -33,7 +33,9 @@ private final BackPressHandler[] mHandlers = new BackPressHandler[Type.NUM_TYPES]; - private final Callback<Boolean> mObserverCallback = (t) -> backPressStateChanged(); + private final Callback<Boolean>[] mObserverCallbacks = new Callback[Type.NUM_TYPES]; + private final boolean[] mStates = new boolean[Type.NUM_TYPES]; + private int mEnabledCount; /** * @return True if the back gesture refactor is enabled. @@ -50,7 +52,8 @@ public void addHandler(BackPressHandler handler, @Type int type) { assert mHandlers[type] == null : "Each type can have at most one handler"; mHandlers[type] = handler; - handler.getHandleBackPressChangedSupplier().addObserver(mObserverCallback); + mObserverCallbacks[type] = (t) -> backPressStateChanged(type); + handler.getHandleBackPressChangedSupplier().addObserver(mObserverCallbacks[type]); } /** @@ -60,8 +63,8 @@ public void removeHandler(@NonNull BackPressHandler handler) { for (int i = 0; i < mHandlers.length; i++) { if (mHandlers[i] == handler) { - handler.getHandleBackPressChangedSupplier().removeObserver(mObserverCallback); - mHandlers[i] = null; + removeHandler(i); + return; } } } @@ -71,6 +74,15 @@ * @param type {@link Type} to be removed. */ public void removeHandler(@Type int type) { + BackPressHandler handler = mHandlers[type]; + Boolean enabled = mHandlers[type].getHandleBackPressChangedSupplier().get(); + if (enabled != null && enabled) { + mEnabledCount--; + mStates[type] = false; + mCallback.setEnabled(mEnabledCount != 0); + } + handler.getHandleBackPressChangedSupplier().removeObserver(mObserverCallbacks[type]); + mObserverCallbacks[type] = null; mHandlers[type] = null; } @@ -90,17 +102,16 @@ return mCallback; } - private void backPressStateChanged() { - boolean isEnabled = false; - for (BackPressHandler handler : mHandlers) { - if (handler == null) continue; - Boolean enabled = handler.getHandleBackPressChangedSupplier().get(); - if (enabled != null && enabled) { - isEnabled = true; - break; - } + private void backPressStateChanged(@Type int type) { + Boolean enabled = mHandlers[type].getHandleBackPressChangedSupplier().get(); + if (enabled == null || enabled == mStates[type]) return; + if (enabled) { + mEnabledCount++; + } else { + mEnabledCount--; } - mCallback.setEnabled(isEnabled); + mStates[type] = enabled; + mCallback.setEnabled(mEnabledCount != 0); } private void handleBackPress() {
diff --git a/chrome/browser/back_press/android/java/src/org/chromium/chrome/browser/back_press/BackPressManagerUnitTest.java b/chrome/browser/back_press/android/java/src/org/chromium/chrome/browser/back_press/BackPressManagerUnitTest.java index 2d9de6e..0d12b6e 100644 --- a/chrome/browser/back_press/android/java/src/org/chromium/chrome/browser/back_press/BackPressManagerUnitTest.java +++ b/chrome/browser/back_press/android/java/src/org/chromium/chrome/browser/back_press/BackPressManagerUnitTest.java
@@ -142,6 +142,31 @@ } @Test + public void testRemoveEnabledHandler() { + BackPressManager manager = new BackPressManager(); + EmptyBackPressHandler h1 = new EmptyBackPressHandler(); + EmptyBackPressHandler h2 = new EmptyBackPressHandler(); + manager.addHandler(h1, 0); + manager.addHandler(h2, 1); + Assert.assertEquals("Two handlers should be registered", 2, getHandlerCount(manager)); + Assert.assertFalse("Callback should be disabled if no handler is enabled.", manager.getCallback().isEnabled()); + + h1.getHandleBackPressChangedSupplier().set(true); + h2.getHandleBackPressChangedSupplier().set(true); + Assert.assertTrue("Callback should be enabled if any handler is enabled.", manager.getCallback().isEnabled()); + + manager.removeHandler(h2); + Assert.assertEquals("One handler should be removed", 1, getHandlerCount(manager)); + Assert.assertFalse("One handler should be removed", manager.has(2)); + Assert.assertTrue("Callback should be enabled if any handler is enabled.", manager.getCallback().isEnabled()); + + manager.removeHandler(h1); + Assert.assertEquals("All handlers should have been removed", 0, getHandlerCount(manager)); + Assert.assertFalse("All handlers should have been removed", manager.has(1)); + Assert.assertFalse("Callback should be disabled if no handler is registered.", manager.getCallback().isEnabled()); + } + + @Test public void testNoHandlerRegistered() { BackPressManager manager = new BackPressManager(); Assert.assertFalse("Callback should be disabled if no handler is registered",
diff --git a/chrome/browser/browser_resources.grd b/chrome/browser/browser_resources.grd index 1bf2c65..d964e88 100644 --- a/chrome/browser/browser_resources.grd +++ b/chrome/browser/browser_resources.grd
@@ -24,7 +24,7 @@ <includes> <if expr="is_win"> <include name="IDR_ABOUT_CONFLICTS_HTML" file="resources\conflicts\about_conflicts.html" type="BINDATA" /> - <include name="IDR_ABOUT_CONFLICTS_JS" file="resources\conflicts\about_conflicts.js" type="BINDATA" /> + <include name="IDR_ABOUT_CONFLICTS_JS" file="${root_gen_dir}\chrome\browser\resources\conflicts\tsc\about_conflicts.js" use_base_dir="false" type="BINDATA" /> <include name="IDR_ABOUT_CONFLICTS_WARNING_SVG" file="resources\conflicts\warning.svg" type="BINDATA" /> </if> <if expr="enable_nacl or is_macosx"> @@ -341,6 +341,7 @@ <include name="IDR_SYS_INTERNALS_IMAGE_CPU_SVG" file="resources\chromeos\sys_internals\img\cpu.svg" type="BINDATA" /> <include name="IDR_SYS_INTERNALS_IMAGE_MEMORY_SVG" file="resources\chromeos\sys_internals\img\memory.svg" type="BINDATA" /> <include name="IDR_SYS_INTERNALS_IMAGE_ZRAM_SVG" file="resources\chromeos\sys_internals\img\zram.svg" type="BINDATA" /> + <include name="IDR_NOTIFICATION_TESTER_HTML" file="resources\chromeos\notification_tester\index.html" type="BINDATA" /> <include name="IDR_ADD_SUPERVISION_HTML" file="resources\chromeos\add_supervision\add_supervision.html" type="chrome_html" /> <include name="IDR_ADD_SUPERVISION_NETWORK_UNAVAILABLE_SVG" file="resources\chromeos\add_supervision\images\network_unavailable.svg" type="BINDATA" /> <include name="IDR_ADD_SUPERVISION_UI_JS" file="${root_gen_dir}\chrome\browser\resources\chromeos\add_supervision\add_supervision_ui.js" use_base_dir="false" type="chrome_html" />
diff --git a/chrome/browser/chromeos/extensions/vpn_provider/BUILD.gn b/chrome/browser/chromeos/extensions/vpn_provider/BUILD.gn index b1c1456..1985e4e3 100644 --- a/chrome/browser/chromeos/extensions/vpn_provider/BUILD.gn +++ b/chrome/browser/chromeos/extensions/vpn_provider/BUILD.gn
@@ -8,7 +8,7 @@ assert(enable_extensions, "Cannot depend on extensions because enable_extensions=false.") -assert(is_chromeos_ash) +assert(is_chromeos) source_set("vpn_provider") { sources = [ @@ -23,12 +23,22 @@ deps = [ "//base", - "//chrome/browser/ash/crosapi", + "//chrome/browser/profiles:profile", "//chrome/common/extensions/api", "//chromeos/crosapi/mojom", - "//chromeos/login/login_state", "//components/keyed_service/content", "//content/public/browser", "//extensions/browser", ] + + if (is_chromeos_ash) { + deps += [ + "//chrome/browser/ash/crosapi", + "//chromeos/login/login_state", + ] + } + + if (is_chromeos_lacros) { + deps += [ "//chromeos/lacros:lacros" ] + } }
diff --git a/chrome/browser/chromeos/extensions/vpn_provider/vpn_service.cc b/chrome/browser/chromeos/extensions/vpn_provider/vpn_service.cc index e9b7956..853f633 100644 --- a/chrome/browser/chromeos/extensions/vpn_provider/vpn_service.cc +++ b/chrome/browser/chromeos/extensions/vpn_provider/vpn_service.cc
@@ -19,9 +19,15 @@ #include "mojo/public/cpp/bindings/self_owned_receiver.h" #include "third_party/abseil-cpp/absl/types/optional.h" +#if BUILDFLAG(IS_CHROMEOS_ASH) #include "chrome/browser/ash/crosapi/crosapi_ash.h" #include "chrome/browser/ash/crosapi/crosapi_manager.h" #include "chrome/browser/ash/crosapi/vpn_service_ash.h" +#endif + +#if BUILDFLAG(IS_CHROMEOS_LACROS) +#include "chromeos/lacros/lacros_service.h" +#endif namespace chromeos { @@ -286,8 +292,17 @@ // static crosapi::mojom::VpnService* VpnService::GetVpnService() { +#if BUILDFLAG(IS_CHROMEOS_ASH) DCHECK(crosapi::CrosapiManager::IsInitialized()); return crosapi::CrosapiManager::Get()->crosapi_ash()->vpn_service_ash(); +#else + auto* service = chromeos::LacrosService::Get(); + if (!service->IsAvailable<crosapi::mojom::VpnService>()) { + LOG(ERROR) << "chrome.vpnProvider is not available in Lacros"; + return nullptr; + } + return service->GetRemote<crosapi::mojom::VpnService>().get(); +#endif } mojo::Remote<crosapi::mojom::VpnServiceForExtension>& @@ -309,12 +324,12 @@ FailureCallback failure, std::unique_ptr<content::PepperVpnProviderResourceHostProxy> pepper_vpn_provider_proxy) { - // Here we create a PepperVpnProxyAdapter that will forward everything to the - // underlying PepperVpnProviderResourceHostProxy and bind it via crosapi. Note - // that the crosapi call might be unsuccessful if the active vpn configuration - // is not owned by the given extension; therefore we don't create the - // SelfOwnedReceiver right away, but instead do it in the callback on success - // or reset the entangled pipe on failure. + // Here we create a PepperVpnProxyAdapter that will forward everything to + // the underlying PepperVpnProviderResourceHostProxy and bind it via + // crosapi. Note that the crosapi call might be unsuccessful if the active + // vpn configuration is not owned by the given extension; therefore we don't + // create the SelfOwnedReceiver right away, but instead do it in the + // callback on success or reset the entangled pipe on failure. auto pepper_adapter = std::make_unique<PepperVpnProxyAdapter>( std::move(pepper_vpn_provider_proxy)); mojo::PendingRemote<crosapi::mojom::PepperVpnProxyObserver> pepper_client;
diff --git a/chrome/browser/chromeos/extensions/vpn_provider/vpn_service_factory.cc b/chrome/browser/chromeos/extensions/vpn_provider/vpn_service_factory.cc index 0424a574..5149b72 100644 --- a/chrome/browser/chromeos/extensions/vpn_provider/vpn_service_factory.cc +++ b/chrome/browser/chromeos/extensions/vpn_provider/vpn_service_factory.cc
@@ -6,10 +6,40 @@ #include "base/memory/singleton.h" #include "chrome/browser/chromeos/extensions/vpn_provider/vpn_service.h" -#include "chromeos/login/login_state/login_state.h" +#include "chrome/browser/profiles/profile.h" #include "components/keyed_service/content/browser_context_dependency_manager.h" +#include "content/public/browser/browser_context.h" #include "extensions/browser/extensions_browser_client.h" +#if BUILDFLAG(IS_CHROMEOS_ASH) +#include "chromeos/login/login_state/login_state.h" +#endif + +namespace { + +// Only main profile should be allowed to access the API. +bool IsContextForMainProfile(content::BrowserContext* context) { +#if BUILDFLAG(IS_CHROMEOS_LACROS) + if (!Profile::FromBrowserContext(context)->IsMainProfile()) { + return false; + } +#endif + +#if BUILDFLAG(IS_CHROMEOS_ASH) + std::string user_hash = + extensions::ExtensionsBrowserClient::Get()->GetUserIdHashFromContext( + context); + if (!chromeos::LoginState::IsInitialized() || + user_hash != chromeos::LoginState::Get()->primary_user_hash()) { + return false; + } +#endif + + return true; +} + +} // namespace + namespace chromeos { // static @@ -41,19 +71,9 @@ KeyedService* VpnServiceFactory::BuildServiceInstanceFor( content::BrowserContext* context) const { - if (!VpnService::GetVpnService()) { + if (!VpnService::GetVpnService() || !IsContextForMainProfile(context)) { return nullptr; } - - std::string context_user_hash = - extensions::ExtensionsBrowserClient::Get()->GetUserIdHashFromContext( - context); - - if (!LoginState::IsInitialized() || - context_user_hash != LoginState::Get()->primary_user_hash()) { - return nullptr; - } - return new VpnService(context); }
diff --git a/chrome/browser/chromeos/policy/dlp/dlp_rules_manager_impl.h b/chrome/browser/chromeos/policy/dlp/dlp_rules_manager_impl.h index d4ec987..3ed1257 100644 --- a/chrome/browser/chromeos/policy/dlp/dlp_rules_manager_impl.h +++ b/chrome/browser/chromeos/policy/dlp/dlp_rules_manager_impl.h
@@ -24,7 +24,7 @@ class DlpRulesManagerImpl : public DlpRulesManager { public: using RuleId = int; - using UrlConditionId = url_matcher::URLMatcherConditionSet::ID; + using UrlConditionId = base::MatcherStringPattern::ID; ~DlpRulesManagerImpl() override;
diff --git a/chrome/browser/enterprise/connectors/analysis/analysis_service_settings.cc b/chrome/browser/enterprise/connectors/analysis/analysis_service_settings.cc index 8502e737..22d2943 100644 --- a/chrome/browser/enterprise/connectors/analysis/analysis_service_settings.cc +++ b/chrome/browser/enterprise/connectors/analysis/analysis_service_settings.cc
@@ -34,7 +34,7 @@ // settings.*_pattern_settings. No enable patterns implies the settings are // invalid. matcher_ = std::make_unique<url_matcher::URLMatcher>(); - url_matcher::URLMatcherConditionSet::ID id(0); + base::MatcherStringPattern::ID id(0); const base::Value* enable = settings_value.FindListKey(kKeyEnable); if (enable && enable->is_list() && !enable->GetListDeprecated().empty()) { for (const base::Value& value : enable->GetListDeprecated()) @@ -109,7 +109,7 @@ absl::optional<AnalysisServiceSettings::URLPatternSettings> AnalysisServiceSettings::GetPatternSettings( const PatternSettings& patterns, - url_matcher::URLMatcherConditionSet::ID match) { + base::MatcherStringPattern::ID match) { // If the pattern exists directly in the map, return its settings. if (patterns.count(match) == 1) return patterns.at(match); @@ -195,7 +195,7 @@ void AnalysisServiceSettings::AddUrlPatternSettings( const base::Value& url_settings_value, bool enabled, - url_matcher::URLMatcherConditionSet::ID* id) { + base::MatcherStringPattern::ID* id) { DCHECK(id); DCHECK(service_provider_); if (enabled) @@ -231,10 +231,10 @@ } std::set<std::string> AnalysisServiceSettings::GetTags( - const std::set<url_matcher::URLMatcherConditionSet::ID>& matches) const { + const std::set<base::MatcherStringPattern::ID>& matches) const { std::set<std::string> enable_tags; std::set<std::string> disable_tags; - for (const url_matcher::URLMatcherConditionSet::ID match : matches) { + for (const base::MatcherStringPattern::ID match : matches) { // Enabled patterns need to be checked first, otherwise they always match // the first disabled pattern. bool enable = true;
diff --git a/chrome/browser/enterprise/connectors/analysis/analysis_service_settings.h b/chrome/browser/enterprise/connectors/analysis/analysis_service_settings.h index 2acaf99..a572a38 100644 --- a/chrome/browser/enterprise/connectors/analysis/analysis_service_settings.h +++ b/chrome/browser/enterprise/connectors/analysis/analysis_service_settings.h
@@ -57,12 +57,12 @@ // Map from an ID representing a specific matched pattern to its settings. using PatternSettings = - std::map<url_matcher::URLMatcherConditionSet::ID, URLPatternSettings>; + std::map<base::MatcherStringPattern::ID, URLPatternSettings>; // Accessors for the pattern setting maps. static absl::optional<URLPatternSettings> GetPatternSettings( const PatternSettings& patterns, - url_matcher::URLMatcherConditionSet::ID match); + base::MatcherStringPattern::ID match); // Returns true if the settings were initialized correctly. If this returns // false, then GetAnalysisSettings will always return absl::nullopt. @@ -72,12 +72,12 @@ // |disabled_patterns_settings_| from a policy value. void AddUrlPatternSettings(const base::Value& url_settings_value, bool enabled, - url_matcher::URLMatcherConditionSet::ID* id); + base::MatcherStringPattern::ID* id); // Return tags found in |enabled_patterns_settings| corresponding to the // matches while excluding the ones in |disable_patterns_settings|. std::set<std::string> GetTags( - const std::set<url_matcher::URLMatcherConditionSet::ID>& matches) const; + const std::set<base::MatcherStringPattern::ID>& matches) const; // The service provider matching the name given in a Connector policy. nullptr // implies that a corresponding service provider doesn't exist and that these
diff --git a/chrome/browser/enterprise/connectors/file_system/service_settings.h b/chrome/browser/enterprise/connectors/file_system/service_settings.h index b1c4f6a..ce27060d 100644 --- a/chrome/browser/enterprise/connectors/file_system/service_settings.h +++ b/chrome/browser/enterprise/connectors/file_system/service_settings.h
@@ -48,7 +48,7 @@ std::set<std::string> mime_types; }; - using URLMatchingID = url_matcher::URLMatcherConditionSet::ID; + using URLMatchingID = base::MatcherStringPattern::ID; // Map from a url matching ID (representing a specific url matching pattern) // to its URLPatternSettings.
diff --git a/chrome/browser/extensions/BUILD.gn b/chrome/browser/extensions/BUILD.gn index 69d8725..365d49f 100644 --- a/chrome/browser/extensions/BUILD.gn +++ b/chrome/browser/extensions/BUILD.gn
@@ -1004,6 +1004,7 @@ deps += [ "//chrome/browser/chromeos/extensions:constants", "//chrome/browser/chromeos/extensions/login_screen", + "//chrome/browser/chromeos/extensions/vpn_provider", "//chromeos/components/disks:prefs", "//chromeos/components/quick_answers/public/cpp:prefs", "//chromeos/constants", @@ -1131,7 +1132,6 @@ "//ash/webui/resources:media_app_bundle_resources_grit", "//chrome/browser/ash/crosapi", "//chrome/browser/ash/crostini:crostini_installer_types_mojom", - "//chrome/browser/chromeos/extensions/vpn_provider", "//chrome/browser/devtools", "//chrome/browser/nearby_sharing/common", "//chrome/browser/ui/webui/settings/chromeos/constants:mojom",
diff --git a/chrome/browser/extensions/api/braille_display_private/brlapi_keycode_map.cc b/chrome/browser/extensions/api/braille_display_private/brlapi_keycode_map.cc index 7787874a..fa2c8d7 100644 --- a/chrome/browser/extensions/api/braille_display_private/brlapi_keycode_map.cc +++ b/chrome/browser/extensions/api/braille_display_private/brlapi_keycode_map.cc
@@ -50,7 +50,7 @@ brlapi_keyCode_t key_sym = code & BRLAPI_KEY_CODE_MASK; if (key_sym < kMaxLatin1KeySym || (key_sym & BRLAPI_KEY_SYM_UNICODE) != 0) { - uint32_t code_point = key_sym & ~BRLAPI_KEY_SYM_UNICODE; + base_icu::UChar32 code_point = key_sym & ~BRLAPI_KEY_SYM_UNICODE; if (!base::IsValidCharacter(code_point)) return; event->standard_key_char = std::make_unique<std::string>();
diff --git a/chrome/browser/extensions/api/declarative_content/declarative_content_page_url_condition_tracker.cc b/chrome/browser/extensions/api/declarative_content/declarative_content_page_url_condition_tracker.cc index edf18c4d..12ee95a 100644 --- a/chrome/browser/extensions/api/declarative_content/declarative_content_page_url_condition_tracker.cc +++ b/chrome/browser/extensions/api/declarative_content/declarative_content_page_url_condition_tracker.cc
@@ -20,7 +20,7 @@ const char kPageUrlInvalidTypeOfParameter[] = "Attribute '%s' has an invalid type"; -static url_matcher::URLMatcherConditionSet::ID g_next_id = 0; +static base::MatcherStringPattern::ID g_next_id = 0; } // namespace @@ -88,7 +88,7 @@ void DeclarativeContentPageUrlConditionTracker::PerWebContentsTracker:: UpdateMatchesForCurrentUrl(bool request_evaluation_if_unchanged) { - std::set<url_matcher::URLMatcherConditionSet::ID> new_matches = + std::set<base::MatcherStringPattern::ID> new_matches = url_matcher_->MatchURL(web_contents()->GetVisibleURL()); matches_.swap(new_matches); if (matches_ != new_matches || request_evaluation_if_unchanged) @@ -155,8 +155,7 @@ void DeclarativeContentPageUrlConditionTracker::StopTrackingPredicates( const std::vector<const void*>& predicate_groups) { // Condition set ids to be removed from |url_matcher_|. - std::vector<url_matcher::URLMatcherConditionSet::ID> - condition_set_ids_to_remove; + std::vector<base::MatcherStringPattern::ID> condition_set_ids_to_remove; for (const void* group : predicate_groups) { auto loc = tracked_predicates_.find(group); if (loc == tracked_predicates_.end()) @@ -205,8 +204,8 @@ static_cast<const DeclarativeContentPageUrlPredicate*>(predicate); auto loc = per_web_contents_tracker_.find(tab); DCHECK(loc != per_web_contents_tracker_.end()); - const std::set<url_matcher::URLMatcherConditionSet::ID>& - web_contents_id_matches = loc->second->matches(); + const std::set<base::MatcherStringPattern::ID>& web_contents_id_matches = + loc->second->matches(); return base::Contains(web_contents_id_matches, typed_predicate->url_matcher_condition_set()->id()); }
diff --git a/chrome/browser/extensions/api/declarative_content/declarative_content_page_url_condition_tracker.h b/chrome/browser/extensions/api/declarative_content/declarative_content_page_url_condition_tracker.h index 0a28791..b699964 100644 --- a/chrome/browser/extensions/api/declarative_content/declarative_content_page_url_condition_tracker.h +++ b/chrome/browser/extensions/api/declarative_content/declarative_content_page_url_condition_tracker.h
@@ -117,7 +117,7 @@ void UpdateMatchesForCurrentUrl(bool request_evaluation_if_unchanged); - const std::set<url_matcher::URLMatcherConditionSet::ID>& matches() { + const std::set<base::MatcherStringPattern::ID>& matches() { return matches_; } @@ -129,7 +129,7 @@ const RequestEvaluationCallback request_evaluation_; WebContentsDestroyedCallback web_contents_destroyed_; - std::set<url_matcher::URLMatcherConditionSet::ID> matches_; + std::set<base::MatcherStringPattern::ID> matches_; }; // Called by PerWebContentsTracker on web contents destruction.
diff --git a/chrome/browser/extensions/api/declarative_content/declarative_content_page_url_condition_tracker_unittest.cc b/chrome/browser/extensions/api/declarative_content/declarative_content_page_url_condition_tracker_unittest.cc index a5c8217..dcfd28c 100644 --- a/chrome/browser/extensions/api/declarative_content/declarative_content_page_url_condition_tracker_unittest.cc +++ b/chrome/browser/extensions/api/declarative_content/declarative_content_page_url_condition_tracker_unittest.cc
@@ -123,7 +123,7 @@ EXPECT_THAT(matcher.MatchURL(GURL("http://google.com/")), ElementsAre(/*empty*/)); - std::set<url_matcher::URLMatcherConditionSet::ID> page_url_matches = + std::set<base::MatcherStringPattern::ID> page_url_matches = matcher.MatchURL(GURL("http://www.example.com/foobar")); EXPECT_THAT( page_url_matches,
diff --git a/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router.cc b/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router.cc index 5289819..13a99129 100644 --- a/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router.cc +++ b/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router.cc
@@ -161,7 +161,7 @@ std::unique_ptr<url_matcher::URLMatcher> matcher = std::make_unique<url_matcher::URLMatcher>(); - url_matcher::URLMatcherConditionSet::ID unused_id(0); + base::MatcherStringPattern::ID unused_id(0); url_matcher::util::AddFilters(matcher.get(), true, &unused_id, it->second); return matcher;
diff --git a/chrome/browser/extensions/api/vpn_provider/vpn_provider_apitest.cc b/chrome/browser/extensions/api/vpn_provider/vpn_provider_apitest.cc index 04aed984..d8b5f53 100644 --- a/chrome/browser/extensions/api/vpn_provider/vpn_provider_apitest.cc +++ b/chrome/browser/extensions/api/vpn_provider/vpn_provider_apitest.cc
@@ -8,22 +8,13 @@ #include "base/bind.h" #include "base/callback_helpers.h" #include "base/containers/contains.h" -#include "base/memory/ptr_util.h" -#include "base/strings/string_number_conversions.h" +#include "base/memory/raw_ptr.h" #include "base/test/test_future.h" -#include "chrome/browser/ash/profiles/profile_helper.h" #include "chrome/browser/chromeos/extensions/vpn_provider/vpn_provider_api.h" -#include "chrome/browser/chromeos/extensions/vpn_provider/vpn_service.h" #include "chrome/browser/chromeos/extensions/vpn_provider/vpn_service_factory.h" -#include "chrome/browser/chromeos/extensions/vpn_provider/vpn_service_interface.h" #include "chrome/browser/extensions/extension_apitest.h" #include "chrome/browser/extensions/extension_service.h" -#include "chromeos/dbus/shill/fake_shill_third_party_vpn_driver_client.h" -#include "chromeos/dbus/shill/shill_clients.h" -#include "chromeos/dbus/shill/shill_profile_client.h" -#include "chromeos/dbus/shill/shill_service_client.h" -#include "chromeos/network/network_configuration_handler.h" -#include "chromeos/network/network_profile_handler.h" +#include "chromeos/network/shill_property_handler.h" #include "content/public/browser/browser_context.h" #include "content/public/browser/pepper_vpn_provider_resource_host_proxy.h" #include "content/public/browser/vpn_service_proxy.h" @@ -34,11 +25,24 @@ #include "extensions/test/result_catcher.h" #include "testing/gmock/include/gmock/gmock.h" #include "third_party/abseil-cpp/absl/types/optional.h" -#include "third_party/cros_system_api/dbus/service_constants.h" +#if BUILDFLAG(IS_CHROMEOS_ASH) #include "chrome/browser/ash/crosapi/crosapi_ash.h" #include "chrome/browser/ash/crosapi/crosapi_manager.h" #include "chrome/browser/ash/crosapi/vpn_service_ash.h" +#include "chrome/browser/ash/profiles/profile_helper.h" +#include "chromeos/dbus/shill/fake_shill_third_party_vpn_driver_client.h" +#include "chromeos/dbus/shill/shill_manager_client.h" +#include "chromeos/dbus/shill/shill_profile_client.h" +#include "chromeos/network/network_profile_handler.h" +#include "third_party/cros_system_api/dbus/service_constants.h" +#endif + +#if BUILDFLAG(IS_CHROMEOS_LACROS) +#include "chromeos/crosapi/mojom/test_controller.mojom-test-utils.h" +#include "chromeos/crosapi/mojom/test_controller.mojom.h" +#include "chromeos/lacros/lacros_service.h" +#endif namespace chromeos { @@ -46,9 +50,11 @@ namespace api_vpn = extensions::api::vpn_provider; -const char kNetworkProfilePath[] = "/network/test"; const char kTestConfig[] = "testconfig"; const char kPacket[] = "feebdaed"; + +#if BUILDFLAG(IS_CHROMEOS_ASH) +const char kNetworkProfilePath[] = "/network/test"; const char* kParameterValues[] = {"10.10.10.10", "24", "63.145.213.129/32 63.145.212.0/24", @@ -73,12 +79,11 @@ void DoNothingSuccessCallback(const std::string& service_path, const std::string& guid) {} -crosapi::VpnServiceAsh* GetVpnServiceAsh() { - return crosapi::CrosapiManager::Get()->crosapi_ash()->vpn_service_ash(); -} +#endif } // namespace +#if BUILDFLAG(IS_CHROMEOS_ASH) // Records the number of calls and their parameters. Always replies successfully // to calls. class TestShillThirdPartyVpnDriverClient @@ -124,65 +129,135 @@ int send_packet_counter_ = 0; std::vector<char> ip_packet_; }; +#endif -class VpnProviderApiTest : public extensions::ExtensionApiTest { +class VpnProviderApiTestBase : public extensions::ExtensionApiTest { public: - VpnProviderApiTest() = default; - ~VpnProviderApiTest() override = default; - - void SetUpInProcessBrowserTestFixture() override { - extensions::ExtensionApiTest::SetUpInProcessBrowserTestFixture(); - // Destroy the existing client and create a test specific fake client. It - // will be destroyed in ChromeBrowserMain. - test_client_ = new TestShillThirdPartyVpnDriverClient(); - } - - void AddNetworkProfileForUser() { - ShillProfileClient::Get()->GetTestInterface()->AddProfile( - kNetworkProfilePath, - ash::ProfileHelper::GetUserIdHashFromProfile(profile())); - content::RunAllPendingInMessageLoop(); - } - - void LoadVpnExtension() { - extension_ = LoadExtension(test_data_dir_.AppendASCII("vpn_provider")); - extension_id_ = extension_->id(); - service_ = VpnServiceFactory::GetForBrowserContext(profile()); - content::RunAllPendingInMessageLoop(); + // extensions::ExtensionApiTest + void SetUpOnMainThread() override { + extensions::ExtensionApiTest::SetUpOnMainThread(); + LoadVpnExtension(); } bool RunTest(const std::string& test_name) { + DCHECK(extension_); GURL url = extension_->GetResourceURL("basic.html?#" + test_name); return RunExtensionTest("vpn_provider", {.page_url = url.spec().c_str()}); } + const std::string& extension_id() const { + DCHECK(extension_id_); + return *extension_id_; + } + + chromeos::VpnServiceInterface* service() { + return chromeos::VpnServiceFactory::GetForBrowserContext(profile()); + } + + virtual void OnPlatformMessage(const std::string& configuration_name, + api_vpn::PlatformMessage) = 0; + virtual void OnPacketReceived(const std::string& configuration_name, + const std::vector<char>& data) = 0; + + protected: + void LoadVpnExtension() { + DCHECK(!extension_); + extension_ = LoadExtension(test_data_dir_.AppendASCII("vpn_provider")); + extension_id_ = extension_->id(); + } + + raw_ptr<const extensions::Extension> extension_ = nullptr; + absl::optional<std::string> extension_id_; +}; + +#if BUILDFLAG(IS_CHROMEOS_LACROS) +class VpnProviderApiTestLacros : public VpnProviderApiTestBase { + public: + // VpnProviderApiTestBase: + void TearDownOnMainThread() override { + UnloadExtension(extension_id()); + VpnProviderApiTestBase::TearDownOnMainThread(); + } + void OnPlatformMessage(const std::string& configuration_name, + api_vpn::PlatformMessage message) override { + controller_->OnPlatformMessage(extension_id(), configuration_name, message); + } + void OnPacketReceived(const std::string& configuration_name, + const std::vector<char>& data) override { + controller_->OnPacketReceived( + extension_id(), configuration_name, + std::vector<uint8_t>(data.begin(), data.end())); + } + + bool InitTestShillController() { + auto* service = chromeos::LacrosService::Get(); + if (!service->IsAvailable<crosapi::mojom::TestController>() || + service->GetInterfaceVersion(crosapi::mojom::TestController::Uuid_) < + static_cast<int>(crosapi::mojom::TestController::MethodMinVersions:: + kBindTestShillControllerMinVersion)) { + LOG(ERROR) << "Unsupported ash version."; + return false; + } + crosapi::mojom::TestControllerAsyncWaiter waiter{ + service->GetRemote<crosapi::mojom::TestController>().get()}; + waiter.BindTestShillController(controller_.BindNewPipeAndPassReceiver()); + return true; + } + + protected: + mojo::Remote<crosapi::mojom::TestShillController> controller_; +}; +#else +class VpnProviderApiTestAsh : public VpnProviderApiTestBase { + public: + // VpnProviderApiTestBase: + void SetUpInProcessBrowserTestFixture() override { + VpnProviderApiTestBase::SetUpInProcessBrowserTestFixture(); + // Destroy the existing client and create a test specific fake client. It + // will be destroyed in ChromeBrowserMain. + test_client_ = new TestShillThirdPartyVpnDriverClient(); + } + void SetUpOnMainThread() override { + VpnProviderApiTestBase::SetUpOnMainThread(); + AddNetworkProfileForUser(); + } + void OnPlatformMessage(const std::string& configuration_name, + api_vpn::PlatformMessage message) override { + test_client_->OnPlatformMessage( + shill::kObjectPathBase + GetKey(configuration_name), message); + } + void OnPacketReceived(const std::string& configuration_name, + const std::vector<char>& data) override { + test_client_->OnPacketReceived( + shill::kObjectPathBase + GetKey(configuration_name), data); + } + std::string GetKey(const std::string& configuration_name) const { - return crosapi::VpnServiceForExtensionAsh::GetKey(extension_id_, + return crosapi::VpnServiceForExtensionAsh::GetKey(extension_id(), configuration_name); } bool DoesConfigExist(const std::string& configuration_name) const { const auto& mapping = GetVpnServiceAsh()->extension_id_to_service_; - if (!base::Contains(mapping, extension_id_)) { + if (!base::Contains(mapping, extension_id())) { return false; } - return base::Contains(mapping.at(extension_id_)->key_to_configuration_map_, + return base::Contains(mapping.at(extension_id())->key_to_configuration_map_, GetKey(configuration_name)); } bool IsConfigConnected() const { const auto& mapping = GetVpnServiceAsh()->extension_id_to_service_; - if (!base::Contains(mapping, extension_id_)) { + if (!base::Contains(mapping, extension_id())) { return false; } - return mapping.at(extension_id_)->OwnsActiveConfiguration(); + return mapping.at(extension_id())->OwnsActiveConfiguration(); } std::string GetSingleServicePath() { - auto* vpn_service_ash = GetVpnServiceAsh(); std::vector<std::string> service_paths; for (const auto& [extension_id, service] : - vpn_service_ash->extension_id_to_service_) { + GetVpnServiceAsh()->extension_id_to_service_) { const auto& service_path_map = service->service_path_to_configuration_map_; if (service_path_map.empty()) { @@ -237,36 +312,53 @@ configuration_name, api_vpn::PLATFORM_MESSAGE_ERROR, error_message); } + void ClearNetworkProfiles() { + ShillProfileClient::Get()->GetTestInterface()->ClearProfiles(); + // ShillProfileClient doesn't notify NetworkProfileHandler that profiles got + // cleared, therefore we have to call ShillManagerClient explicitly. + ShillManagerClient::Get()->GetTestInterface()->ClearProfiles(); + } + protected: - TestShillThirdPartyVpnDriverClient* test_client_ = nullptr; // Unowned - extensions::api::VpnServiceInterface* service_ = nullptr; - std::string extension_id_; - std::string service_path_; - const extensions::Extension* extension_ = nullptr; + void AddNetworkProfileForUser() { + ShillProfileClient::Get()->GetTestInterface()->AddProfile( + kNetworkProfilePath, + ash::ProfileHelper::GetUserIdHashFromProfile(profile())); + content::RunAllPendingInMessageLoop(); + } + + static crosapi::VpnServiceAsh* GetVpnServiceAsh() { + return crosapi::CrosapiManager::Get()->crosapi_ash()->vpn_service_ash(); + } + + raw_ptr<TestShillThirdPartyVpnDriverClient> test_client_ = + nullptr; // Unowned }; +#endif -IN_PROC_BROWSER_TEST_F(VpnProviderApiTest, ComboSuite) { - LoadVpnExtension(); - AddNetworkProfileForUser(); - EXPECT_TRUE(RunTest("comboSuite")); -} +#if BUILDFLAG(IS_CHROMEOS_LACROS) +using VpnProviderApiTest = VpnProviderApiTestLacros; +#else +using VpnProviderApiTest = VpnProviderApiTestAsh; +#endif +//////////////////////////// +// Ash-specific tests. +//////////////////////////// + +#if BUILDFLAG(IS_CHROMEOS_ASH) IN_PROC_BROWSER_TEST_F(VpnProviderApiTest, CreateConfigWithoutNetworkProfile) { - LoadVpnExtension(); + ClearNetworkProfiles(); EXPECT_TRUE(RunTest("createConfigWithoutNetworkProfile")); } IN_PROC_BROWSER_TEST_F(VpnProviderApiTest, CreateConfig) { - LoadVpnExtension(); - AddNetworkProfileForUser(); EXPECT_TRUE(RunTest("createConfigSuccess")); EXPECT_TRUE(DoesConfigExist(kTestConfig)); EXPECT_TRUE(HasService(GetSingleServicePath())); } IN_PROC_BROWSER_TEST_F(VpnProviderApiTest, DestroyConfig) { - LoadVpnExtension(); - AddNetworkProfileForUser(); EXPECT_TRUE(CreateConfigForTest(kTestConfig)); EXPECT_TRUE(DoesConfigExist(kTestConfig)); const std::string service_path = GetSingleServicePath(); @@ -278,18 +370,13 @@ } IN_PROC_BROWSER_TEST_F(VpnProviderApiTest, DestroyConnectedConfig) { - LoadVpnExtension(); - AddNetworkProfileForUser(); - EXPECT_TRUE(CreateConfigForTest(kTestConfig)); EXPECT_TRUE(DoesConfigExist(kTestConfig)); const std::string service_path = GetSingleServicePath(); EXPECT_TRUE(HasService(service_path)); EXPECT_FALSE(IsConfigConnected()); - const std::string object_path = shill::kObjectPathBase + GetKey(kTestConfig); - test_client_->OnPlatformMessage(object_path, - api_vpn::PLATFORM_MESSAGE_CONNECTED); + OnPlatformMessage(kTestConfig, api_vpn::PLATFORM_MESSAGE_CONNECTED); EXPECT_TRUE(IsConfigConnected()); EXPECT_TRUE(RunTest("destroyConnectedConfigSetup")); @@ -303,52 +390,7 @@ ASSERT_TRUE(catcher.GetNextResult()); } -IN_PROC_BROWSER_TEST_F(VpnProviderApiTest, VpnSuccess) { - LoadVpnExtension(); - AddNetworkProfileForUser(); - EXPECT_TRUE(RunTest("createConfigConnectAndDisconnect")); - EXPECT_TRUE(DoesConfigExist(kTestConfig)); - EXPECT_TRUE(HasService(GetSingleServicePath())); - EXPECT_FALSE(IsConfigConnected()); - - const std::string object_path = shill::kObjectPathBase + GetKey(kTestConfig); - - extensions::ResultCatcher catcher; - EXPECT_EQ(0, test_client_->set_parameters_counter_); - EXPECT_EQ(0, test_client_->update_connection_state_counter_); - EXPECT_EQ(0, test_client_->send_packet_counter_); - test_client_->OnPlatformMessage(object_path, - api_vpn::PLATFORM_MESSAGE_CONNECTED); - EXPECT_TRUE(IsConfigConnected()); - ASSERT_TRUE(catcher.GetNextResult()); - EXPECT_EQ(1, test_client_->set_parameters_counter_); - EXPECT_EQ(1, test_client_->update_connection_state_counter_); - EXPECT_EQ(1, test_client_->send_packet_counter_); - EXPECT_EQ(api_vpn::VPN_CONNECTION_STATE_CONNECTED, - test_client_->update_connection_state_counter_); - for (size_t i = 0; i < std::size(kParameterValues); ++i) { - const std::string* value = - test_client_->parameters_.FindStringKey(kParameterKeys[i]); - ASSERT_TRUE(value); - EXPECT_EQ(kParameterValues[i], *value); - } - std::vector<char> packet(std::begin(kPacket), std::prev(std::end(kPacket))); - EXPECT_EQ(packet, test_client_->ip_packet_); - - packet.assign(test_client_->ip_packet_.rbegin(), - test_client_->ip_packet_.rend()); - test_client_->OnPacketReceived(object_path, packet); - ASSERT_TRUE(catcher.GetNextResult()); - - test_client_->OnPlatformMessage(object_path, - api_vpn::PLATFORM_MESSAGE_DISCONNECTED); - ASSERT_TRUE(catcher.GetNextResult()); - EXPECT_FALSE(IsConfigConnected()); -} - IN_PROC_BROWSER_TEST_F(VpnProviderApiTest, ConfigInternalRemove) { - LoadVpnExtension(); - AddNetworkProfileForUser(); EXPECT_TRUE(RunTest("configInternalRemove")); EXPECT_TRUE(DoesConfigExist(kTestConfig)); @@ -359,27 +401,23 @@ } IN_PROC_BROWSER_TEST_F(VpnProviderApiTest, CheckEvents) { - LoadVpnExtension(); - AddNetworkProfileForUser(); EXPECT_TRUE(RunTest("expectEvents")); EXPECT_TRUE(DoesConfigExist(kTestConfig)); extensions::ResultCatcher catcher; - SendPlatformError(extension_id_, kTestConfig, "error_message"); - service_->SendShowAddDialogToExtension(extension_id_); - service_->SendShowConfigureDialogToExtension(extension_id_, kTestConfig); + SendPlatformError(extension_id(), kTestConfig, "error_message"); + service()->SendShowAddDialogToExtension(extension_id()); + service()->SendShowConfigureDialogToExtension(extension_id(), kTestConfig); EXPECT_TRUE(catcher.GetNextResult()); } IN_PROC_BROWSER_TEST_F(VpnProviderApiTest, ConfigPersistence) { - LoadVpnExtension(); - AddNetworkProfileForUser(); EXPECT_FALSE(DoesConfigExist(kTestConfig)); base::Value::Dict properties; properties.Set(shill::kTypeProperty, shill::kTypeVPN); properties.Set(shill::kNameProperty, kTestConfig); - properties.Set(shill::kProviderHostProperty, extension_id_); + properties.Set(shill::kProviderHostProperty, extension_id()); properties.Set(shill::kObjectPathSuffixProperty, GetKey(kTestConfig)); properties.Set(shill::kProviderTypeProperty, shill::kProviderThirdPartyVpn); properties.Set(shill::kProfileProperty, kNetworkProfilePath); @@ -394,23 +432,19 @@ } IN_PROC_BROWSER_TEST_F(VpnProviderApiTest, CreateUninstall) { - LoadVpnExtension(); - AddNetworkProfileForUser(); EXPECT_TRUE(RunTest("createConfigSuccess")); EXPECT_TRUE(DoesConfigExist(kTestConfig)); const std::string service_path = GetSingleServicePath(); EXPECT_TRUE(HasService(service_path)); - UninstallExtension(extension_id_); + UninstallExtension(extension_id()); content::RunAllPendingInMessageLoop(); EXPECT_FALSE(DoesConfigExist(kTestConfig)); EXPECT_FALSE(HasService(service_path)); } IN_PROC_BROWSER_TEST_F(VpnProviderApiTest, CreateDisable) { - LoadVpnExtension(); - AddNetworkProfileForUser(); EXPECT_TRUE(RunTest("createConfigSuccess")); EXPECT_TRUE(DoesConfigExist(kTestConfig)); @@ -420,15 +454,13 @@ extensions::ExtensionService* extension_service = extensions::ExtensionSystem::Get(profile())->extension_service(); extension_service->DisableExtension( - extension_id_, extensions::disable_reason::DISABLE_USER_ACTION); + extension_id(), extensions::disable_reason::DISABLE_USER_ACTION); content::RunAllPendingInMessageLoop(); EXPECT_FALSE(DoesConfigExist(kTestConfig)); EXPECT_FALSE(HasService(service_path)); } IN_PROC_BROWSER_TEST_F(VpnProviderApiTest, CreateBlocklist) { - LoadVpnExtension(); - AddNetworkProfileForUser(); EXPECT_TRUE(RunTest("createConfigSuccess")); EXPECT_TRUE(DoesConfigExist(kTestConfig)); @@ -437,11 +469,78 @@ extensions::ExtensionService* extension_service = extensions::ExtensionSystem::Get(profile())->extension_service(); - extension_service->BlocklistExtensionForTest(extension_id_); + extension_service->BlocklistExtensionForTest(extension_id()); content::RunAllPendingInMessageLoop(); EXPECT_FALSE(DoesConfigExist(kTestConfig)); EXPECT_FALSE(HasService(service_path)); } +#endif + +//////////////////////////// +// Ash/lacros shared tests. +//////////////////////////// + +IN_PROC_BROWSER_TEST_F(VpnProviderApiTest, ComboSuite) { +#if BUILDFLAG(IS_CHROMEOS_LACROS) + if (!InitTestShillController()) { + GTEST_SKIP() << "Unsupported ash version."; + } +#endif + + EXPECT_TRUE(RunTest("comboSuite")); +} + +IN_PROC_BROWSER_TEST_F(VpnProviderApiTest, VpnSuccess) { +#if BUILDFLAG(IS_CHROMEOS_LACROS) + if (!InitTestShillController()) { + GTEST_SKIP() << "Unsupported ash version."; + } +#endif + + EXPECT_TRUE(RunTest("createConfigConnectAndDisconnect")); + +#if BUILDFLAG(IS_CHROMEOS_ASH) + EXPECT_TRUE(DoesConfigExist(kTestConfig)); + EXPECT_TRUE(HasService(GetSingleServicePath())); + EXPECT_FALSE(IsConfigConnected()); + EXPECT_EQ(0, test_client_->set_parameters_counter_); + EXPECT_EQ(0, test_client_->update_connection_state_counter_); + EXPECT_EQ(0, test_client_->send_packet_counter_); +#endif + + extensions::ResultCatcher catcher; + OnPlatformMessage(kTestConfig, api_vpn::PLATFORM_MESSAGE_CONNECTED); + ASSERT_TRUE(catcher.GetNextResult()); + +#if BUILDFLAG(IS_CHROMEOS_ASH) + EXPECT_TRUE(IsConfigConnected()); + EXPECT_EQ(1, test_client_->set_parameters_counter_); + EXPECT_EQ(1, test_client_->update_connection_state_counter_); + EXPECT_EQ(1, test_client_->send_packet_counter_); + EXPECT_EQ(api_vpn::VPN_CONNECTION_STATE_CONNECTED, + test_client_->update_connection_state_counter_); + for (size_t i = 0; i < std::size(kParameterValues); ++i) { + const std::string* value = + test_client_->parameters_.FindStringKey(kParameterKeys[i]); + ASSERT_TRUE(value); + EXPECT_EQ(kParameterValues[i], *value); + } + std::vector<char> received_packet(std::begin(kPacket), + std::prev(std::end(kPacket))); + EXPECT_EQ(received_packet, test_client_->ip_packet_); +#endif + + std::vector<char> packet(++std::rbegin(kPacket), std::rend(kPacket)); + OnPacketReceived(kTestConfig, packet); + ASSERT_TRUE(catcher.GetNextResult()); + + OnPlatformMessage(kTestConfig, api_vpn::PLATFORM_MESSAGE_DISCONNECTED); + ASSERT_TRUE(catcher.GetNextResult()); + +#if BUILDFLAG(IS_CHROMEOS_ASH) + EXPECT_FALSE(IsConfigConnected()); +#endif +} class FakePepperVpnProviderResourceHostProxy : public content::PepperVpnProviderResourceHostProxy { @@ -463,8 +562,11 @@ }; IN_PROC_BROWSER_TEST_F(VpnProviderApiTest, PepperProxy) { - LoadVpnExtension(); - AddNetworkProfileForUser(); +#if BUILDFLAG(IS_CHROMEOS_LACROS) + if (!InitTestShillController()) { + GTEST_SKIP() << "Unsupported ash version."; + } +#endif base::test::TestFuture<bool> unbind; base::test::TestFuture<std::vector<char>> data; @@ -480,27 +582,24 @@ // PLATFORM_MESSAGE_CONNECTED. EXPECT_TRUE(RunTest("createConfigConnectForBind")); ASSERT_TRUE(catcher.GetNextResult()); - const std::string object_path = shill::kObjectPathBase + GetKey(kTestConfig); - test_client_->OnPlatformMessage(object_path, - api_vpn::PLATFORM_MESSAGE_CONNECTED); + OnPlatformMessage(kTestConfig, api_vpn::PLATFORM_MESSAGE_CONNECTED); ASSERT_TRUE(catcher.GetNextResult()); // Synchronously bind the fake pepper proxy. base::RunLoop run_loop; - service_->GetVpnServiceProxy()->Bind( - extension_id_, {}, kTestConfig, run_loop.QuitClosure(), base::DoNothing(), - std::move(pepper_proxy)); + service()->GetVpnServiceProxy()->Bind( + extension_id(), {}, kTestConfig, run_loop.QuitClosure(), + base::DoNothing(), std::move(pepper_proxy)); run_loop.Run(); // Assert that packets are routed through the proxy. - test_client_->OnPacketReceived( - object_path, std::vector<char>{std::begin(kPacket), std::end(kPacket)}); + OnPacketReceived(kTestConfig, + std::vector<char>{std::begin(kPacket), std::end(kPacket)}); ASSERT_TRUE(data.Wait()); // Assert that pepper proxy receives an OnUnbind event on // PLATFORM_MESSAGE_DISCONNECTED. - test_client_->OnPlatformMessage(object_path, - api_vpn::PLATFORM_MESSAGE_DISCONNECTED); + OnPlatformMessage(kTestConfig, api_vpn::PLATFORM_MESSAGE_DISCONNECTED); ASSERT_TRUE(catcher.GetNextResult()); ASSERT_TRUE(unbind.Wait()); }
diff --git a/chrome/browser/extensions/chrome_content_browser_client_extensions_part.cc b/chrome/browser/extensions/chrome_content_browser_client_extensions_part.cc index 3f40faa6..5716f76 100644 --- a/chrome/browser/extensions/chrome_content_browser_client_extensions_part.cc +++ b/chrome/browser/extensions/chrome_content_browser_client_extensions_part.cc
@@ -74,9 +74,9 @@ #include "third_party/blink/public/common/associated_interfaces/associated_interface_registry.h" #include "url/origin.h" -#if BUILDFLAG(IS_CHROMEOS_ASH) +#if BUILDFLAG(IS_CHROMEOS) #include "chrome/browser/chromeos/extensions/vpn_provider/vpn_service_factory.h" -#endif // BUILDFLAG(IS_CHROMEOS_ASH) +#endif // BUILDFLAG(IS_CHROMEOS) using blink::web_pref::WebPreferences; using content::BrowserContext; @@ -570,7 +570,7 @@ std::unique_ptr<content::VpnServiceProxy> ChromeContentBrowserClientExtensionsPart::GetVpnServiceProxy( content::BrowserContext* browser_context) { -#if BUILDFLAG(IS_CHROMEOS_ASH) +#if BUILDFLAG(IS_CHROMEOS) chromeos::VpnServiceInterface* vpn_service = chromeos::VpnServiceFactory::GetForBrowserContext(browser_context); if (!vpn_service)
diff --git a/chrome/browser/external_protocol/external_protocol_handler.cc b/chrome/browser/external_protocol/external_protocol_handler.cc index 380a73e..29eba47d 100644 --- a/chrome/browser/external_protocol/external_protocol_handler.cc +++ b/chrome/browser/external_protocol/external_protocol_handler.cc
@@ -281,7 +281,7 @@ return false; url_matcher::URLMatcher matcher; - url_matcher::URLMatcherConditionSet::ID id(0); + base::MatcherStringPattern::ID id(0); url_matcher::util::AddFilters(&matcher, true /* allowed */, &id, &base::Value::AsListValue(*origin_patterns));
diff --git a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedStream.java b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedStream.java index 9cb651c..3c4f192f 100644 --- a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedStream.java +++ b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedStream.java
@@ -283,8 +283,6 @@ @Override public void processThereAndBackAgainData(byte[] data, LoggingParameters loggingParameters) { assert ThreadUtils.runningOnUiThread(); - // TODO(crbug.com/1268575): Forward loggingParameters to FeedApi, and check that they - // match the current state. FeedStreamJni.get().processThereAndBackAgain(mNativeFeedStream, FeedStream.this, data, FeedLoggingParameters.convertToProto(loggingParameters).toByteArray()); }
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json index 8f1e7a572..d31a8b4 100644 --- a/chrome/browser/flag-metadata.json +++ b/chrome/browser/flag-metadata.json
@@ -1124,6 +1124,11 @@ "expiry_milestone": 93 }, { + "name": "default-browser-intents-show-settings", + "owners": [ "olivierrobin", "bling-flags@google.com" ], + "expiry_milestone": 120 + }, + { "name": "default-chrome-apps-migration", "owners": ["vkovalova"], "expiry_milestone": 104
diff --git a/chrome/browser/media/webrtc/media_stream_device_permission_context.cc b/chrome/browser/media/webrtc/media_stream_device_permission_context.cc index 2798adc..36bc6eb 100644 --- a/chrome/browser/media/webrtc/media_stream_device_permission_context.cc +++ b/chrome/browser/media/webrtc/media_stream_device_permission_context.cc
@@ -8,11 +8,20 @@ #include "chrome/common/pref_names.h" #include "components/content_settings/core/browser/host_content_settings_map.h" #include "components/content_settings/core/common/content_settings.h" +#include "components/permissions/permission_context_base.h" #include "content/public/common/content_features.h" #include "content/public/common/url_constants.h" #include "extensions/common/constants.h" #include "third_party/blink/public/mojom/permissions_policy/permissions_policy.mojom.h" +#if BUILDFLAG(IS_ANDROID) +#include "base/stl_util.h" +#include "components/permissions/android/android_permission_util.h" +#include "components/permissions/permission_request_id.h" +#include "components/permissions/permissions_client.h" +#include "content/public/browser/web_contents.h" +#endif + namespace { blink::mojom::PermissionsPolicyFeature GetPermissionsPolicyFeature( @@ -81,6 +90,144 @@ return setting; } +#if BUILDFLAG(IS_ANDROID) +// There are two other permissions that need to check corresponding OS-level +// permissions, and they take two different approaches to this. Geolocation only +// stores the permission ContentSetting if both requests are granted (or if the +// site permission is "Block"). WebXR permissions are following the approach +// found here. +void MediaStreamDevicePermissionContext::NotifyPermissionSet( + const permissions::PermissionRequestID& id, + const GURL& requesting_origin, + const GURL& embedding_origin, + permissions::BrowserPermissionCallback callback, + bool persist, + ContentSetting content_setting, + bool is_one_time) { + // For Android, we need to customize the PermissionContextBase's behavior if + // the permission was granted. We will: + // 1. Check if the permission was granted by the user - if not, we'll fall + // back to the implementation in the base class. + // 2. Handle persisting the permission if needed (this is the same as base + // class implementation). + // 3. Check if the OS permissions need to be re-prompted. + // a) If no, we'll call base class impl. & say that the permission was granted + // by the user, but skip persisting it (because we already did in step 2). + // b) If yes but we cannot show the info bar, we will call base class impl. & + // say that the permission was rejected by the user, and we won't persist + // it (because we already did in step 2 and the user didn't actually reject + // the permission). + // c) If yes and we can show the info bar, we show it and propagate the answer + // to the base class impl., skipping persisting it (because we already + // persisted it and the user didn't actually reject it). + // + // Note that base class implementation will call into `UpdateTabContext()` + // virtual method when we invoke `NotifyPermissionSet()` from the base class. + // This is fine, even in 3b) and 3c), where we call it with a parameter that + // does not correspond to user's answer to Chrome-level permission, because + // `MediaStreamDevicePermissionContext` does *not* have a custom + // implementation for `UpdateTabContext()` - if it did, we'd need to stop + // calling into base class with the parameter not matching user's answer. + + DCHECK(content_settings_type_ == ContentSettingsType::MEDIASTREAM_CAMERA || + content_settings_type_ == ContentSettingsType::MEDIASTREAM_MIC); + + // Camera and Microphone need to check for additional permissions, but only if + // they were actually allowed: + if (content_setting != ContentSetting::CONTENT_SETTING_ALLOW) { + PermissionContextBase::NotifyPermissionSet( + id, requesting_origin, embedding_origin, std::move(callback), persist, + content_setting, is_one_time); + return; + } + + // Whether or not the user will ultimately accept the OS permissions, we want + // to save the content_setting here if we should. This is done here because we + // won't set `persist=true` when calling + // `PermissionContextBase::NotifyPermissionSet()` after this point. + if (persist) { + UpdateContentSetting(requesting_origin, embedding_origin, content_setting, + is_one_time); + } + + content::WebContents* web_contents = + content::WebContents::FromRenderFrameHost( + content::RenderFrameHost::FromID(id.render_process_id(), + id.render_frame_id())); + if (!web_contents) { + // If we can't get the web contents, we don't know the state of the OS + // permission, so assume we don't have it. + OnAndroidPermissionDecided(id, requesting_origin, embedding_origin, + std::move(callback), + false /*permission_granted*/); + return; + } + + // Otherwise, the user granted permission to use `content_settings_type_`, so + // now we need to check if we need to prompt for Android system permissions. + std::vector<ContentSettingsType> permission_type = {content_settings_type_}; + permissions::PermissionRepromptState reprompt_state = + permissions::ShouldRepromptUserForPermissions(web_contents, + permission_type); + switch (reprompt_state) { + case permissions::PermissionRepromptState::kNoNeed: + // We would have already returned if permission was denied by the user, + // and this result indicates that we have all the OS permissions we need. + OnAndroidPermissionDecided(id, requesting_origin, embedding_origin, + std::move(callback), + true /*permission_granted*/); + return; + + case permissions::PermissionRepromptState::kCannotShow: + // If we cannot show the info bar, then we have to assume we don't have + // the permissions we need. + OnAndroidPermissionDecided(id, requesting_origin, embedding_origin, + std::move(callback), + false /*permission_granted*/); + return; + + case permissions::PermissionRepromptState::kShow: + // Otherwise, prompt the user that we need additional permissions. + permissions::PermissionsClient::Get()->RepromptForAndroidPermissions( + web_contents, permission_type, + base::BindOnce( + &MediaStreamDevicePermissionContext::OnAndroidPermissionDecided, + weak_ptr_factory_.GetWeakPtr(), id, requesting_origin, + embedding_origin, std::move(callback))); + return; + } +} + +void MediaStreamDevicePermissionContext::OnAndroidPermissionDecided( + const permissions::PermissionRequestID& id, + const GURL& requesting_origin, + const GURL& embedding_origin, + permissions::BrowserPermissionCallback callback, + bool permission_granted) { + // If we were supposed to persist the setting we've already done so in the + // initial override of |NotifyPermissionSet|. At this point, if the user + // has denied the OS level permission, we want to notify the requestor that + // the permission has been blocked. + ContentSetting setting = permission_granted + ? ContentSetting::CONTENT_SETTING_ALLOW + : ContentSetting::CONTENT_SETTING_BLOCK; + // `persist=false` because the user's response to Chrome-level permission is + // already persisted, and `is_one_time=false` because it is only relevant when + // persisting permission. + PermissionContextBase::NotifyPermissionSet( + id, requesting_origin, embedding_origin, std::move(callback), + false /*persist*/, setting, /*is_one_time=*/false); +} + +void MediaStreamDevicePermissionContext::UpdateTabContext( + const permissions::PermissionRequestID& id, + const GURL& requesting_origin, + bool allowed) { + // See the comment in `NotifyPermissionSet()` for context on why this method + // should be empty. +} +#endif + void MediaStreamDevicePermissionContext::ResetPermission( const GURL& requesting_origin, const GURL& embedding_origin) {
diff --git a/chrome/browser/media/webrtc/media_stream_device_permission_context.h b/chrome/browser/media/webrtc/media_stream_device_permission_context.h index ac8d83f..abe199c 100644 --- a/chrome/browser/media/webrtc/media_stream_device_permission_context.h +++ b/chrome/browser/media/webrtc/media_stream_device_permission_context.h
@@ -5,6 +5,7 @@ #ifndef CHROME_BROWSER_MEDIA_WEBRTC_MEDIA_STREAM_DEVICE_PERMISSION_CONTEXT_H_ #define CHROME_BROWSER_MEDIA_WEBRTC_MEDIA_STREAM_DEVICE_PERMISSION_CONTEXT_H_ +#include "base/memory/weak_ptr.h" #include "components/content_settings/core/common/content_settings_types.h" #include "components/permissions/permission_context_base.h" @@ -22,6 +23,17 @@ ~MediaStreamDevicePermissionContext() override; +#if BUILDFLAG(IS_ANDROID) + // PermissionContextBase: + void NotifyPermissionSet(const permissions::PermissionRequestID& id, + const GURL& requesting_origin, + const GURL& embedding_origin, + permissions::BrowserPermissionCallback callback, + bool persist, + ContentSetting content_setting, + bool is_one_time) override; +#endif + // TODO(xhwang): GURL.DeprecatedGetOriginAsURL() shouldn't be used as the // origin. Need to refactor to use url::Origin. crbug.com/527149 is filed for // this. @@ -37,7 +49,25 @@ // PermissionContextBase: bool IsRestrictedToSecureOrigins() const override; +#if BUILDFLAG(IS_ANDROID) + // PermissionContextBase: + void UpdateTabContext(const permissions::PermissionRequestID& id, + const GURL& requesting_origin, + bool allowed) override; + + void OnAndroidPermissionDecided( + const permissions::PermissionRequestID& id, + const GURL& requesting_origin, + const GURL& embedding_origin, + permissions::BrowserPermissionCallback callback, + bool permission_granted); +#endif + ContentSettingsType content_settings_type_; + + // Must be the last member. + base::WeakPtrFactory<MediaStreamDevicePermissionContext> weak_ptr_factory_{ + this}; }; #endif // CHROME_BROWSER_MEDIA_WEBRTC_MEDIA_STREAM_DEVICE_PERMISSION_CONTEXT_H_
diff --git a/chrome/browser/nearby_sharing/OWNERS b/chrome/browser/nearby_sharing/OWNERS index e8a8f52..2763694 100644 --- a/chrome/browser/nearby_sharing/OWNERS +++ b/chrome/browser/nearby_sharing/OWNERS
@@ -1,3 +1,2 @@ hansberry@chromium.org hansenmichael@google.com -nohle@chromium.org
diff --git a/chrome/browser/optimization_guide/optimization_guide_keyed_service_factory.cc b/chrome/browser/optimization_guide/optimization_guide_keyed_service_factory.cc index 90eb382c..27ba6896 100644 --- a/chrome/browser/optimization_guide/optimization_guide_keyed_service_factory.cc +++ b/chrome/browser/optimization_guide/optimization_guide_keyed_service_factory.cc
@@ -46,14 +46,11 @@ KeyedService* OptimizationGuideKeyedServiceFactory::BuildServiceInstanceFor( content::BrowserContext* context) const { #if BUILDFLAG(IS_CHROMEOS_ASH) - // Do not build the OptimizationGuideKeyedService if it's a sign-in profile - // since it basically is an ephemeral profile anyway and we cannot provide - // hints or models to it anyway. Additionally, sign in profiles do not go - // through the standard profile initialization flow, so a lot of things that - // are required are not available when the browser context for the signin - // profile is created. + // Do not build the OptimizationGuideKeyedService if it's a sign-in or + // lockscreen profile since it basically is an ephemeral profile anyway and we + // cannot provide hints or models to it anyway. Profile* profile = Profile::FromBrowserContext(context); - if (ash::ProfileHelper::IsSigninProfile(profile)) + if (!ash::ProfileHelper::IsRegularProfile(profile)) return nullptr; #endif return new OptimizationGuideKeyedService(context);
diff --git a/chrome/browser/optimization_guide/page_content_annotations_service_browsertest.cc b/chrome/browser/optimization_guide/page_content_annotations_service_browsertest.cc index 8a3649d..a6cd3d87 100644 --- a/chrome/browser/optimization_guide/page_content_annotations_service_browsertest.cc +++ b/chrome/browser/optimization_guide/page_content_annotations_service_browsertest.cc
@@ -13,6 +13,7 @@ #include "chrome/browser/optimization_guide/optimization_guide_keyed_service_factory.h" #include "chrome/browser/optimization_guide/page_content_annotations_service_factory.h" #include "chrome/browser/ui/browser.h" +#include "chrome/common/chrome_switches.h" #include "chrome/test/base/in_process_browser_test.h" #include "chrome/test/base/ui_test_utils.h" #include "components/history/core/browser/history_database.h" @@ -37,6 +38,12 @@ #include "testing/gmock/include/gmock/gmock.h" #include "third_party/abseil-cpp/absl/types/optional.h" +#if BUILDFLAG(IS_CHROMEOS_ASH) +#include "chrome/browser/ash/login/test/device_state_mixin.h" +#include "chrome/browser/ash/login/test/scoped_policy_update.h" +#include "chrome/test/base/mixin_based_in_process_browser_test.h" +#endif + namespace optimization_guide { namespace { @@ -123,6 +130,65 @@ browser()->profile())); } +class PageContentAnnotationsServiceKioskModeBrowserTest + : public InProcessBrowserTest { + public: + PageContentAnnotationsServiceKioskModeBrowserTest() { + scoped_feature_list_.InitWithFeatures( + {features::kOptimizationHints, features::kPageContentAnnotations}, + /*disabled_features=*/{}); + } + + void SetUpCommandLine(base::CommandLine* command_line) override { + command_line->AppendSwitch(::switches::kKioskMode); + } + + private: + base::test::ScopedFeatureList scoped_feature_list_; +}; + +IN_PROC_BROWSER_TEST_F(PageContentAnnotationsServiceKioskModeBrowserTest, + DisabledInKioskMode) { + EXPECT_EQ(nullptr, PageContentAnnotationsServiceFactory::GetForProfile( + browser()->profile())); +} + +#if BUILDFLAG(IS_CHROMEOS_ASH) +class PageContentAnnotationsServiceEphemeralProfileBrowserTest + : public MixinBasedInProcessBrowserTest { + public: + PageContentAnnotationsServiceEphemeralProfileBrowserTest() { + scoped_feature_list_.InitWithFeatures( + {features::kOptimizationHints, features::kPageContentAnnotations}, + /*disabled_features=*/{}); + } + + void SetUpInProcessBrowserTestFixture() override { + MixinBasedInProcessBrowserTest::SetUpInProcessBrowserTestFixture(); + + std::unique_ptr<ash::ScopedDevicePolicyUpdate> device_policy_update = + device_state_.RequestDevicePolicyUpdate(); + device_policy_update->policy_payload() + ->mutable_ephemeral_users_enabled() + ->set_ephemeral_users_enabled(true); + } + + protected: + ash::DeviceStateMixin device_state_{ + &mixin_host_, + ash::DeviceStateMixin::State::OOBE_COMPLETED_CLOUD_ENROLLED}; + + private: + base::test::ScopedFeatureList scoped_feature_list_; +}; + +IN_PROC_BROWSER_TEST_F(PageContentAnnotationsServiceEphemeralProfileBrowserTest, + EphemeralProfileDoesNotInstantiateService) { + EXPECT_EQ(nullptr, PageContentAnnotationsServiceFactory::GetForProfile( + browser()->profile())); +} +#endif + class PageContentAnnotationsServiceValidationBrowserTest : public InProcessBrowserTest { public:
diff --git a/chrome/browser/optimization_guide/page_content_annotations_service_factory.cc b/chrome/browser/optimization_guide/page_content_annotations_service_factory.cc index a0de82b..dc47a83 100644 --- a/chrome/browser/optimization_guide/page_content_annotations_service_factory.cc +++ b/chrome/browser/optimization_guide/page_content_annotations_service_factory.cc
@@ -9,11 +9,15 @@ #include "base/task/sequenced_task_runner.h" #include "base/task/task_traits.h" #include "base/task/thread_pool.h" +#include "chrome/browser/app_mode/app_mode_utils.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/history/history_service_factory.h" #include "chrome/browser/optimization_guide/optimization_guide_keyed_service.h" #include "chrome/browser/optimization_guide/optimization_guide_keyed_service_factory.h" #include "chrome/browser/profiles/profile.h" +#include "chrome/browser/profiles/profile_attributes_entry.h" +#include "chrome/browser/profiles/profile_attributes_storage.h" +#include "chrome/browser/profiles/profile_manager.h" #include "components/history/core/browser/history_service.h" #include "components/keyed_service/content/browser_context_dependency_manager.h" #include "components/leveldb_proto/public/proto_database_provider.h" @@ -23,9 +27,39 @@ #include "content/public/browser/storage_partition.h" #include "third_party/blink/public/common/features.h" +#if BUILDFLAG(IS_CHROMEOS_ASH) +#include "chrome/browser/ash/profiles/profile_helper.h" +#endif + namespace { -bool ShouldEnablePageContentAnnotations() { +bool IsEphemeralProfile(Profile* profile) { +#if BUILDFLAG(IS_CHROMEOS_ASH) + if (ash::ProfileHelper::IsEphemeralUserProfile(profile)) + return true; +#endif + + // Catch additional logic that may not be caught by the existing Ash check. + ProfileAttributesStorage& storage = + g_browser_process->profile_manager()->GetProfileAttributesStorage(); + ProfileAttributesEntry* entry = + storage.GetProfileAttributesWithPath(profile->GetPath()); + return entry && entry->IsEphemeral(); +} + +bool ShouldEnablePageContentAnnotations(Profile* profile) { + if (chrome::IsRunningInAppMode()) { + // The annotations we provide cannot provide any benefit to users in kiosk + // mode, so we can skip. + return false; + } + + if (IsEphemeralProfile(profile)) { + // The annotations we provide won't have lasting effect if profile is + // ephemeral, so we can skip. + return false; + } + // Allow for the validation experiment, remote page metadata, or the Topics // experiment to enable the PCAService without need to enable both features. return optimization_guide::features::IsPageContentAnnotationEnabled() || @@ -65,11 +99,11 @@ KeyedService* PageContentAnnotationsServiceFactory::BuildServiceInstanceFor( content::BrowserContext* context) const { - if (!ShouldEnablePageContentAnnotations()) - return nullptr; - Profile* profile = Profile::FromBrowserContext(context); + if (!ShouldEnablePageContentAnnotations(profile)) + return nullptr; + auto* proto_db_provider = profile->GetOriginalProfile() ->GetDefaultStoragePartition() ->GetProtoDatabaseProvider(); @@ -95,7 +129,7 @@ bool PageContentAnnotationsServiceFactory::ServiceIsCreatedWithBrowserContext() const { - return ShouldEnablePageContentAnnotations(); + return true; } bool PageContentAnnotationsServiceFactory::ServiceIsNULLWhileTesting() const {
diff --git a/chrome/browser/resources/BUILD.gn b/chrome/browser/resources/BUILD.gn index 13e2db0..3e916d91 100644 --- a/chrome/browser/resources/BUILD.gn +++ b/chrome/browser/resources/BUILD.gn
@@ -155,13 +155,6 @@ ] } - # Note: The following targets should only be type-checked on Windows builds, - # but because Window bots don't depend on Java, |closure_compile| is off on - # Windows. Adding |is_linux| so that these targets are type-checked on at - # least one platform. - if (is_win || is_linux || is_chromeos) { - deps += [ "conflicts:closure_compile" ] - } if (is_linux || is_chromeos) { deps += [ "certificate_viewer:closure_compile" ] }
diff --git a/chrome/browser/resources/certificate_viewer/BUILD.gn b/chrome/browser/resources/certificate_viewer/BUILD.gn index 17418b9a..e1c7921 100644 --- a/chrome/browser/resources/certificate_viewer/BUILD.gn +++ b/chrome/browser/resources/certificate_viewer/BUILD.gn
@@ -13,8 +13,5 @@ deps = [ "//ui/webui/resources/js:cr.m", "//ui/webui/resources/js:util.m", - "//ui/webui/resources/js/cr:ui.m", - "//ui/webui/resources/js/cr/ui:tabs", - "//ui/webui/resources/js/cr/ui:tree", ] }
diff --git a/chrome/browser/resources/certificate_viewer/certificate_viewer.css b/chrome/browser/resources/certificate_viewer/certificate_viewer.css index 034c87ab..6bb0e2d 100644 --- a/chrome/browser/resources/certificate_viewer/certificate_viewer.css +++ b/chrome/browser/resources/certificate_viewer/certificate_viewer.css
@@ -22,11 +22,8 @@ #tabbox { height: 100%; - width: 100%; -} - -#tabpanels { overflow: auto; + width: 100%; } #cert-field-value { @@ -64,7 +61,8 @@ } /* Used so that 100% width within tabpanel will correspond to usable space. */ -tabpanel { +div[slot='panel'] { + height: 100%; position: relative; } @@ -104,15 +102,12 @@ overflow: auto; } -.tree-row[selected] { - background-color: #f0f0f0; - background-image: none; -} - -.tree-item > .tree-row { - border: 0; - border-radius: 0; - line-height: 29px; +html { + --cr-tree-row-selected-color: #f0f0f0; + --cr-tree-row-selected-image: none; + --cr-tree-row-border: 0; + --cr-tree-row-border-radius: 0; + --cr-tree-row-line-height: 29px; } #export {
diff --git a/chrome/browser/resources/certificate_viewer/certificate_viewer.html b/chrome/browser/resources/certificate_viewer/certificate_viewer.html index 686195f..f873d6ec 100644 --- a/chrome/browser/resources/certificate_viewer/certificate_viewer.html +++ b/chrome/browser/resources/certificate_viewer/certificate_viewer.html
@@ -4,114 +4,109 @@ <meta charset="utf-8"> <title id="title"></title> <link rel="stylesheet" href="chrome://resources/css/chrome_shared.css"> - <link rel="stylesheet" href="chrome://resources/css/tabs.css"> - <link rel="stylesheet" href="chrome://resources/css/tree.css"> <link rel="stylesheet" href="certificate_viewer.css"> <script type="module" src="certificate_viewer.js"></script> </head> <body> - <tabbox id="tabbox"> - <tabs id="tabs" class="new-style-tabs" role="tablist"> - <tab id="general-tab" role="tab" aria-controls="general" tabindex="0"> - $i18n{general} - </tab> - <tab id="details-tab" role="tab" aria-controls="details"> - $i18n{details} - </tab> - </tabs> - <tabpanels id="tabpanels" class="new-style-tabs"> - <!-- General --> - <tabpanel id="general" aria-labelledby="general-tab"> - <div class="groups"> - <!-- Issued to --> - <div> - <h3 role="heading">$i18n{issuedTo}</h3> - </div> - <div> - <div class="attribute">$i18n{cn}</div> - <div id="issued-cn" class="value"></div> - </div> - <div> - <div class="attribute">$i18n{o}</div> - <div id="issued-o" class="value"></div> - </div> - <div> - <div class="attribute">$i18n{ou}</div> - <div id="issued-ou" class="value"></div> - </div> - - <!-- Issued by --> - <div> - <h3 role="heading">$i18n{issuedBy}</h3> - </div> - <div> - <div class="attribute">$i18n{cn}</div> - <div id="issuer-cn" class="value"></div> - </div> - <div> - <div class="attribute">$i18n{o}</div> - <div id="issuer-o" class="value"></div> - </div> - <div> - <div class="attribute">$i18n{ou}</div> - <div id="issuer-ou" class="value"></div> - </div> - - <!-- Validity --> - <div> - <h3 role="heading">$i18n{validity}</h3> - </div> - <div> - <div class="attribute">$i18n{issuedOn}</div> - <div id="issue-date" class="value"></div> - </div> - <div> - <div class="attribute">$i18n{expiresOn}</div> - <div id="expiry-date" class="value"></div> - </div> - - <!-- Fingerprints --> - <div> - <h3 role="heading">$i18n{fingerprints}</h3> - </div> - <div> - <div class="attribute">$i18n{sha256}</div> - <div id="sha256" class="value"></div> - </div> - <div> - <div class="attribute">$i18n{sha1}</div> - <div id="sha1" class="value"></div> - </div> + <cr-tab-box id="tabbox" hidden> + <div slot="tab" id="general-tab" role="tab" aria-controls="general" + tabindex="0"> + $i18n{general} + </div> + <div slot="tab" id="details-tab" role="tab" aria-controls="details"> + $i18n{details} + </div> + <!-- General --> + <div slot="panel" id="general" aria-labelledby="general-tab"> + <div class="groups"> + <!-- Issued to --> + <div> + <h3 role="heading">$i18n{issuedTo}</h3> </div> - </tabpanel> + <div> + <div class="attribute">$i18n{cn}</div> + <div id="issued-cn" class="value"></div> + </div> + <div> + <div class="attribute">$i18n{o}</div> + <div id="issued-o" class="value"></div> + </div> + <div> + <div class="attribute">$i18n{ou}</div> + <div id="issued-ou" class="value"></div> + </div> - <!-- Details --> - <tabpanel id="details" aria-labelledby="details-tab"> - <div id="hierarchy-section" class="vertical-box"> - <h3 id="hierarchy-label" role="heading">$i18n{hierarchy}</h3> - <tree id="hierarchy" class="section-contents" - aria-labelledby="hierarchy-label" - icon-visibility='hidden'></tree> + <!-- Issued by --> + <div> + <h3 role="heading">$i18n{issuedBy}</h3> </div> - <div id="cert-fields-section" class="vertical-box"> - <h3 id="cert-fields-label" role="heading">$i18n{certFields}</h3> - <tree id="cert-fields" class="section-contents" - aria-labelledby="cert-fields-label" - icon-visibility='hidden'></tree> + <div> + <div class="attribute">$i18n{cn}</div> + <div id="issuer-cn" class="value"></div> </div> - <div id="cert-field-value-section" class="vertical-box"> - <h3 id="cert-field-value-label" role="heading"> - $i18n{certFieldVal} - </h3> - <div id="cert-field-value" class="section-contents" tabindex="0" - aria-live="polite" aria-atomic="true" aria-readonly="true" - aria-labelledby="cert-field-value-label" role="textbox"></div> - <div> - <button id="export">$i18n{export}</button> - </div> + <div> + <div class="attribute">$i18n{o}</div> + <div id="issuer-o" class="value"></div> </div> - </tabpanel> - </tabpanels> - </tabbox> + <div> + <div class="attribute">$i18n{ou}</div> + <div id="issuer-ou" class="value"></div> + </div> + + <!-- Validity --> + <div> + <h3 role="heading">$i18n{validity}</h3> + </div> + <div> + <div class="attribute">$i18n{issuedOn}</div> + <div id="issue-date" class="value"></div> + </div> + <div> + <div class="attribute">$i18n{expiresOn}</div> + <div id="expiry-date" class="value"></div> + </div> + + <!-- Fingerprints --> + <div> + <h3 role="heading">$i18n{fingerprints}</h3> + </div> + <div> + <div class="attribute">$i18n{sha256}</div> + <div id="sha256" class="value"></div> + </div> + <div> + <div class="attribute">$i18n{sha1}</div> + <div id="sha1" class="value"></div> + </div> + </div> + </div> + + <!-- Details --> + <div slot="panel" id="details" aria-labelledby="details-tab"> + <div id="hierarchy-section" class="vertical-box"> + <h3 id="hierarchy-label" role="heading">$i18n{hierarchy}</h3> + <cr-tree id="hierarchy" class="section-contents" + aria-labelledby="hierarchy-label" + icon-visibility='hidden'></cr-tree> + </div> + <div id="cert-fields-section" class="vertical-box"> + <h3 id="cert-fields-label" role="heading">$i18n{certFields}</h3> + <cr-tree id="cert-fields" class="section-contents" + aria-labelledby="cert-fields-label" + icon-visibility='hidden'></cr-tree> + </div> + <div id="cert-field-value-section" class="vertical-box"> + <h3 id="cert-field-value-label" role="heading"> + $i18n{certFieldVal} + </h3> + <div id="cert-field-value" class="section-contents" tabindex="0" + aria-live="polite" aria-atomic="true" aria-readonly="true" + aria-labelledby="cert-field-value-label" role="textbox"></div> + <div> + <button id="export">$i18n{export}</button> + </div> + </div> + </div> + </cr-tab-box> </body> </html>
diff --git a/chrome/browser/resources/certificate_viewer/certificate_viewer.js b/chrome/browser/resources/certificate_viewer/certificate_viewer.js index cd5322a..2ed2e8b 100644 --- a/chrome/browser/resources/certificate_viewer/certificate_viewer.js +++ b/chrome/browser/resources/certificate_viewer/certificate_viewer.js
@@ -3,13 +3,34 @@ // found in the LICENSE file. import './strings.m.js'; +import 'chrome://resources/cr_elements/cr_tab_box/cr_tab_box.js'; +import 'chrome://resources/cr_elements/cr_tree/cr_tree.js'; +import 'chrome://resources/cr_elements/cr_tree/cr_tree_item.js'; import {assert} from 'chrome://resources/js/assert.m.js'; import {sendWithPromise} from 'chrome://resources/js/cr.m.js'; -import {decorate} from 'chrome://resources/js/cr/ui.m.js'; -import {TabBox} from 'chrome://resources/js/cr/ui/tabs.js'; -import {Tree, TreeItem} from 'chrome://resources/js/cr/ui/tree.js'; -import {$} from 'chrome://resources/js/util.m.js'; + +// TODO (rbpotter): Remove these temporary definitions after migrating to TS. +/** + * @typedef {{ + * detail: { children: Object, payload: (Object|undefined) }, + * expanded: boolean, + * add: function(CrTreeItemElement): void, + * }} + */ +let CrTreeItemElement; + +/** + * @typedef {{ + * detail: { children: Object, payload: Object }, + * add: function(CrTreeItemElement): void, + * removeTreeItem: function(CrTreeItemElement): void, + * expanded: boolean, + * items: Array<CrTreeItemElement>, + * selectedItem: CrTreeItemElement, + * }} + */ +let CrTreeElement; /** * @typedef {{ @@ -24,7 +45,8 @@ * substituting in translated strings and requesting certificate details. */ function initialize() { - decorate('tabbox', TabBox); + const tabBox = document.querySelector('cr-tab-box'); + tabBox.hidden = false; const args = JSON.parse(chrome.getVariableValue('dialogArguments')); getCertificateInfo(/** @type {!CertificateInfo} */ (args)); @@ -37,8 +59,14 @@ * fires. */ const initializeDetailTab = oneShot(function() { - initializeTree(assert($('hierarchy')), showCertificateFields); - initializeTree(assert($('cert-fields')), showCertificateFieldValue); + const hierarchy = + /** @type {CrTreeElement} */ (document.querySelector('#hierarchy')); + assert(hierarchy); + initializeTree(hierarchy, showCertificateFields); + const certFields = + /** @type {CrTreeElement} */ (document.querySelector('#cert-fields')); + assert(certFields); + initializeTree(certFields, showCertificateFieldValue); createCertificateHierarchy(args.hierarchy); }); @@ -50,14 +78,14 @@ // even the slowest machine. setTimeout(initializeDetailTab, 200); - $('tabbox').addEventListener('selectedChange', function f(e) { - $('tabbox').removeEventListener('selectedChange', f); + tabBox.addEventListener('selected-index-change', function f() { + tabBox.removeEventListener('selected-index-change', f); initializeDetailTab(); }, true); stripGtkAccessorKeys(); - $('export').onclick = exportCertificate; + document.querySelector('#export').onclick = exportCertificate; } /** @@ -77,13 +105,12 @@ /** * Initialize a Tree object from a given element using the specified * change handler. - * @param {!HTMLElement} tree The HTMLElement to style as a tree. + * @param {!CrTreeElement} tree The HTMLElement to style as a tree. * @param {function()} handler Function to call when a node is selected. */ function initializeTree(tree, handler) { - decorate(tree, Tree); - tree['detail'] = {payload: {}, children: {}}; - tree.addEventListener('change', handler); + tree.detail = {payload: {}, children: {}}; + /** @type {HTMLElement} */ (tree).addEventListener('cr-tree-change', handler); } /** @@ -94,8 +121,10 @@ */ function stripGtkAccessorKeys() { // Copy all the tab labels into an array. - const nodes = Array.prototype.slice.call($('tabs').childNodes, 0); - nodes.push($('export')); + const tabs = document.querySelectorAll('div[slot=\'tab\']'); + const nodes = Array.prototype.slice.call(tabs, 0); + const exportButton = document.querySelector('#export'); + nodes.push(exportButton); for (let i = 0; i < nodes.length; i++) { nodes[i].textContent = nodes[i].textContent.replace('&', ''); } @@ -103,12 +132,13 @@ /** * Expand all nodes of the given tree object. - * @param {!Tree} tree The tree object to expand all nodes on. + * @param {!CrTreeElement|CrTreeItemElement} tree The tree object to expand all + * nodes on. */ function revealTree(tree) { tree.expanded = true; - for (const key in tree['detail'].children) { - revealTree(tree['detail'].children[key]); + for (const key in tree.detail.children) { + revealTree(tree.detail.children[key]); } } @@ -119,7 +149,7 @@ */ function getCertificateInfo(certInfo) { for (const key in certInfo.general) { - $(key).textContent = certInfo.general[key]; + document.querySelector(`#${key}`).textContent = certInfo.general[key]; } } @@ -128,35 +158,37 @@ * @param {Object} hierarchy A dictionary containing the hierarchy. */ function createCertificateHierarchy(hierarchy) { - const treeItem = /** @type {!Tree} */ ($('hierarchy')); + const tree = + /** @type {CrTreeElement} */ (document.querySelector('#hierarchy')); const root = constructTree(hierarchy[0]); - treeItem['detail'].children['root'] = root; - treeItem.add(root); + tree.detail['children']['root'] = root; + tree.add(root); // Select the last item in the hierarchy (really we have a list here - each // node has at most one child). This will reveal the parent nodes and // populate the fields view. let last = root; - while (last.detail.children && last.detail.children[0]) { - last = last.detail.children[0]; + while (last.detail['children'] && last.detail['children'][0]) { + last = last.detail['children'][0]; } - last.selected = true; + tree.selectedItem = last; } /** * Constructs a TreeItem corresponding to the passed in tree * @param {Object} tree Dictionary describing the tree structure. - * @return {!TreeItem} Tree node corresponding to the input dictionary. + * @return {!CrTreeItemElement} Tree node corresponding to the input dictionary. */ function constructTree(tree) { - const treeItem = new TreeItem({ - label: tree.label, - detail: {payload: tree.payload ? tree.payload : {}, children: {}} - }); + const treeItem = + /** @type {CrTreeItemElement} */ (document.createElement('cr-tree-item')); + treeItem.label = tree.label; + treeItem.detail = {payload: tree.payload ? tree.payload : {}, children: {}}; if (tree.children) { for (let i = 0; i < tree.children.length; i++) { - treeItem.add( - treeItem['detail'].children[i] = constructTree(tree.children[i])); + const child = constructTree(tree.children[i]); + treeItem.add(child); + treeItem.detail.children[i] = child; } } return treeItem; @@ -166,10 +198,11 @@ * Clear any previous certificate fields in the tree. */ function clearCertificateFields() { - const treeItem = /** @type {!Tree} */ ($('cert-fields')); - for (const key in treeItem['detail'].children) { - treeItem.remove(treeItem['detail'].children[key]); - delete treeItem['detail'].children[key]; + const treeItem = + /** @type {!CrTreeElement} */ (document.querySelector('#cert-fields')); + for (const key in treeItem.detail.children) { + treeItem.removeTreeItem(treeItem.detail.children[key]); + delete treeItem.detail.children[key]; } } @@ -178,7 +211,9 @@ */ function showCertificateFields() { clearCertificateFields(); - const item = $('hierarchy').selectedItem; + const hierarchy = + /** @type {!CrTreeElement} */ (document.querySelector('#hierarchy')); + const item = hierarchy.selectedItem; if (item && item.detail.payload.index !== undefined) { sendWithPromise('requestCertificateFields', item.detail.payload.index) .then(onCertificateFields); @@ -192,12 +227,14 @@ */ function onCertificateFields(certFields) { clearCertificateFields(); - const treeItem = /** @type {!Tree} */ ($('cert-fields')); - treeItem.add( - treeItem['detail'].children['root'] = constructTree(certFields[0])); - revealTree(treeItem); + const tree = + /** @type {!CrTreeElement} */ (document.querySelector('#cert-fields')); + const root = constructTree(certFields[0]); + tree.detail.children['root'] = root; + tree.add(root); + revealTree(tree); // Ensure the list is scrolled to the top by selecting the first item. - treeItem.children[0].selected = true; + tree.selectedItem = tree.items[0]; document.body.dispatchEvent( new CustomEvent('certificate-fields-updated-for-testing')); } @@ -206,11 +243,14 @@ * Show certificate field value for a selected certificate field. */ function showCertificateFieldValue() { - const item = $('cert-fields').selectedItem; + const certFields = + /** @type {CrTreeElement} */ (document.querySelector('#cert-fields')); + const item = certFields.selectedItem; + const fieldValue = document.querySelector('#cert-field-value'); if (item && item.detail.payload.val) { - $('cert-field-value').textContent = item.detail.payload.val; + fieldValue.textContent = item.detail.payload.val; } else { - $('cert-field-value').textContent = ''; + fieldValue.textContent = ''; } } @@ -218,7 +258,9 @@ * Export the selected certificate. */ function exportCertificate() { - const item = $('hierarchy').selectedItem; + const hierarchy = + /** @type {CrTreeElement} */ (document.querySelector('#hierarchy')); + const item = hierarchy.selectedItem; if (item && item.detail.payload.index !== undefined) { chrome.send('exportCertificate', [item.detail.payload.index]); }
diff --git a/chrome/browser/resources/chromeos/login/BUILD.gn b/chrome/browser/resources/chromeos/login/BUILD.gn index 482f737..6d6f407b 100644 --- a/chrome/browser/resources/chromeos/login/BUILD.gn +++ b/chrome/browser/resources/chromeos/login/BUILD.gn
@@ -190,6 +190,8 @@ "components/dialogs/oobe_modal_dialog.html", "components/dialogs/oobe_modal_dialog.js", "components/display_manager_types.html", + "components/oobe_apps_list.html", + "components/oobe_apps_list.js", "components/oobe_vars/oobe_shared_vars_css.html", "components/oobe_carousel.html", "components/oobe_carousel.js", @@ -396,6 +398,7 @@ "components/common_styles/cr_card_radio_group_styles.m.js", "components/common_styles/oobe_dialog_host_styles.m.js", "components/common_styles/oobe_flex_layout_styles.m.js", + "components/oobe_apps_list.m.js", "components/oobe_carousel.m.js", "components/oobe_slide.m.js", "components/oobe_vars/oobe_shared_vars_css.m.js",
diff --git a/chrome/browser/resources/chromeos/login/components/BUILD.gn b/chrome/browser/resources/chromeos/login/components/BUILD.gn index dc817e7..c06f5c74 100644 --- a/chrome/browser/resources/chromeos/login/components/BUILD.gn +++ b/chrome/browser/resources/chromeos/login/components/BUILD.gn
@@ -30,6 +30,7 @@ ":network_select_login.m", ":notification_card.m", ":oobe_a11y_option.m", + ":oobe_apps_list.m", ":oobe_carousel.m", ":oobe_cr_lottie.m", ":oobe_i18n_dropdown.m", @@ -65,6 +66,7 @@ ":network_select_login_module", ":notification_card_module", ":oobe_a11y_option_module", + ":oobe_apps_list_module", ":oobe_carousel_module", ":oobe_cr_lottie_module", ":oobe_icons_module", @@ -171,6 +173,12 @@ extra_deps = [ ":oobe_a11y_option_module" ] } +js_library("oobe_apps_list.m") { + sources = [ "$root_gen_dir/chrome/browser/resources/chromeos/login/components/oobe_apps_list.m.js" ] + deps = [] + extra_deps = [ ":oobe_apps_list_module" ] +} + js_library("oobe_icons.m") { sources = [ "$root_gen_dir/chrome/browser/resources/chromeos/login/components/oobe_icons.m.js" ] deps = [] @@ -337,6 +345,14 @@ namespace_rewrites = oobe_namespace_rewrites } +polymer_modulizer("oobe_apps_list") { + js_file = "oobe_apps_list.js" + html_file = "oobe_apps_list.html" + html_type = "dom-module" + auto_imports = oobe_auto_imports + namespace_rewrites = oobe_namespace_rewrites +} + polymer_modulizer("oobe_icons") { js_file = "oobe_icons.m.js" html_file = "oobe_icons.html"
diff --git a/chrome/browser/resources/chromeos/login/components/oobe_apps_list.html b/chrome/browser/resources/chromeos/login/components/oobe_apps_list.html new file mode 100644 index 0000000..48cd654 --- /dev/null +++ b/chrome/browser/resources/chromeos/login/components/oobe_apps_list.html
@@ -0,0 +1,197 @@ +<!-- Copyright 2022 The Chromium Authors. All rights reserved. + Use of this source code is governed by a BSD-style license that can be + found in the LICENSE file. --> + +<link rel="import" href="chrome://resources/html/polymer.html"> +<link rel="import" href="chrome://resources/cr_elements/chromeos/cros_color_overrides.html"> +<link rel="import" href="chrome://resources/cr_elements/cr_checkbox/cr_checkbox.html"> +<link rel="import" href="chrome://resources/cr_elements/cr_icon_button/cr_icon_button.html"> +<link rel="import" href="chrome://resources/cr_elements/cr_expand_button/cr_expand_button.html"> +<link rel="import" href="chrome://resources/cr_elements/cr_scrollable_behavior.html"> +<link rel="import" href="chrome://resources/cr_elements/icons.html"> + +<!-- + OOBE polymer element which is used to show a list of recommended apps. +--> + +<dom-module id="oobe-apps-list"> + <template> + <style include="cros-color-overrides"> + :host { + --checkbox-column-width: 40px; + --checkbox-size: 18px; + } + + #appsListContainer { + display: grid; + gap: 8px 0; + grid-auto-flow: row; + grid-template-rows: 40px 1fr; + height: 100%; + } + + #appsList { + height: 100%; + overflow-y: auto; + } + + #appsListContainer .app-item { + display: grid; + gap: 4px 16px; + grid-template-areas: + '. . title . ' + 'checkbox icon category . ' + '. . description expand'; + grid-auto-flow: column; + grid-template-columns: var(--checkbox-column-width) 48px 1fr 26px; + grid-template-rows: 22px 22px auto; + padding-bottom: 16px; + padding-top: 16px; + } + + #appsListContainer .app-item:not(:first-child) { + border-top: 1px solid var(--cros-color-primary); + } + + cr-checkbox { + --cr-checkbox-size: var(--checkbox-size); + } + + #selectAll { + margin-inline-start: + calc((var(--checkbox-column-width) - var(--checkbox-size)) / 2); + } + + #unselectAll { + border-bottom: solid 2px var(--cr-checked-color); + height: 19px; + margin-inline-start: calc((var(--checkbox-column-width) - + var(--checkbox-size)) / 2 + 4px); + position: absolute; + width: 10px; + } + + cr-checkbox.some-selected { + --cr-checkbox-unchecked-box-color: var(--cr-checked-color); + } + + .app-item cr-checkbox { + align-self: center; + grid-area: checkbox; + height: var(--checkbox-size); + justify-self: center; + width: var(--checkbox-size); + } + + webview.app-icon { + align-items: center; + align-self: center; + grid-area: icon; + height: 48px; + user-select: none; + width: 48px; + } + + .app-title { + color: var(--oobe-header-text-color); + font-size: var(--oobe-modal-dialog-header-font-size); + font-weight: var(--oobe-modal-dialog-header-font-weight); + grid-area: title; + line-height: 22px; + } + + .secondary-text { + color: var(--oobe-text-color); + font-family: var(--oobe-default-font-family); + font-size: 12px; + font-weight: var(--oobe-default-font-weight); + line-height: 18px; + } + + .app-tags { + grid-area: category; + } + + .app-tags ul { + list-style: none; + margin: 0; + padding: 0; + } + + .app-tags li { + display: inline-block; + } + + .app-tags ul>li::before { + content: '|'; + margin-inline-end: 6px; + margin-inline-start: 6px; + } + + .app-tags ul>li:first-of-type::before { + content: ''; + margin: 0; + } + + .app-description { + grid-area: description; + } + + .truncated { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + + .expand-button { + align-items: flex-start; + display: flex; + grid-area: expand; + } + + cr-expand-button { + --cr-icon-button-margin-start: 0; + --cr-icon-button-margin-end: 0; + --cr-expand-button-icon-size: 20px; + --cr-expand-button-size: 20px; + --cr-section-vertical-padding: 0; + height: 20px; + width: 20px; + } + </style> + <div id="appsListContainer"> + <div id="unselectAll" hidden="[[unselectSymbolHidden_(appsSelected)]]"> + </div> + <cr-checkbox id="selectAll" checked="{{allSelected_}}" + on-change="updateSelection_"> + <slot name="selectAllTitle"></slot> + </cr-checkbox> + <div id="appsList"> + <template is="dom-repeat" items="[[appList]]"> + <div class="app-item"> + <cr-checkbox checked="{{item.checked}}" + on-change="updateCount_"></cr-checkbox> + <webview class="app-icon" src="[[getWrappedIcon_(item.icon_url)]]" + tabindex="-1" on-contentload="onImageLoaded_"> + </webview> + <div class="app-title">[[item.title]]</div> + <div class="app-tags secondary-text truncated"> + <ul> + <template is="dom-repeat" items="[[item.tags]]"> + <li>[[item]]</li> + </template> + </ul> + </div> + <div class="app-description secondary-text truncated"> + [[item.description]] + </div> + <div class="expand-button"> + <cr-expand-button on-click="onExpandClicked_"></cr-expand-button> + </div> + </div> + </template> + </div> + </div> + </template> + <script src="oobe_apps_list.js"></script> +</dom-module>
diff --git a/chrome/browser/resources/chromeos/login/components/oobe_apps_list.js b/chrome/browser/resources/chromeos/login/components/oobe_apps_list.js new file mode 100644 index 0000000..6c13e0c --- /dev/null +++ b/chrome/browser/resources/chromeos/login/components/oobe_apps_list.js
@@ -0,0 +1,206 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +/* #js_imports_placeholder */ + +const MAX_IMG_LOADING_TIME_SEC = 7; + +/** + * @polymer + */ +/* #export */ class OobeAppsList extends Polymer.Element { + static get is() { + return 'oobe-apps-list'; + } + + /* #html_template_placeholder */ + + static get properties() { + return { + /** + * Apps list. + */ + appList: { + type: Array, + value: [], + observer: 'onAppListSet_', + }, + + appsSelected: { + type: Number, + value: 0, + notify: true, + }, + }; + } + + constructor() { + super(); + /** + * Timer id of pending load. + * @type {number|undefined} + * @private + */ + this.loadingTimer_ = undefined; + this.allSelected_ = false; + this.loadedImagesCount_ = 0; + } + + /** + * Clears loading timer. + * @private + */ + clearLoadingTimer_() { + if (this.loadingTimer_) { + clearTimeout(this.loadingTimer_); + this.loadingTimer_ = undefined; + } + } + + /** + * Called when app list is changed. We assume that non-empty list is set only + * once. + * @private + */ + onAppListSet_() { + if (this.appList.length === 0) { + return; + } + this.clearLoadingTimer_(); + // Wait a few seconds before at least some icons are downloaded. If it + // happens faster we will exit waiting timer. + this.loadingTimer_ = setTimeout( + this.onLoadingTimeOut_.bind(this), MAX_IMG_LOADING_TIME_SEC * 1000); + } + + /** + * Handler for icons loading timeout. + * @private + */ + onLoadingTimeOut_() { + this.loadingTimer_ = undefined; + this.dispatchEvent( + new CustomEvent('apps-list-loaded', {bubbles: true, composed: true})); + } + + /** + * Wrap the icon as a image into a html snippet. + * + * @param {string} iconUri the icon uri to be wrapped. + * @return {string} wrapped html snippet. + * + * @private + */ + getWrappedIcon_(iconUri) { + return 'data:text/html;charset=utf-8,' + encodeURIComponent(String.raw` + <html> + <style> + body { + margin: 0; + } + #icon { + width: 48px; + height: 48px; + user-select: none; + } + </style> + <body><img id="icon" src="` + iconUri + '"></body></html>'); + } + + /** + * After any change in selection update current counter. + * @private + */ + updateCount_() { + let appsSelected = 0; + this.appList.forEach(app => { + appsSelected += app.checked; + }); + this.appsSelected = appsSelected; + this.allSelected_ = this.appsSelected === this.appList.length; + } + + /** + * Set all checkboxes to a new value. + * @param {boolean} value new state for all checkboxes + * @private + */ + updateSelectionTo_(value) { + this.appList.forEach((_, index) => { + this.set('appList.' + index + '.checked', value); + }); + } + + /** + * Called when select all checkbox is clicked. It has 3 states: + * 1) all are selected; + * 2) some are selected; + * 3) nothing is selected. + * Clicking the button in states 1 and 2 leads to a state 3; from state 3 to + * state 1. + * @private + */ + updateSelection_() { + if (this.allSelected_ && this.appsSelected === 0) { + this.updateSelectionTo_(true); + } else { + this.updateSelectionTo_(false); + } + // When there are selected apps clicking on this checkbox should unselect + // them. Keep checkbox unchecked. + if (this.allSelected_ && this.appsSelected > 0) { + this.allSelected_ = false; + } + this.updateCount_(); + } + + /** + * @param {!Event} e + * @private + */ + onExpandClicked_(e) { + const appItem = e.currentTarget.parentElement.parentElement; + appItem.querySelector('.app-description').classList.toggle('truncated'); + } + + /** + * Called when single webview loads app icon. + * @private + */ + onImageLoaded_() { + this.loadedImagesCount_ += 1; + if (this.loadedImagesCount_ === this.appList.length) { + this.clearLoadingTimer_(); + + this.dispatchEvent( + new CustomEvent('apps-list-loaded', {bubbles: true, composed: true})); + } + } + + /** + * Return a list of selected apps. + * @returns {!Array<String>} + */ + getSelectedApps() { + const packageNames = []; + this.appList.forEach(app => { + if (app.checked) { + packageNames.push(app.package_name); + } + }); + return packageNames; + } + + unselectSymbolHidden_(appsSelected) { + if (appsSelected > 0 && appsSelected < this.appList.length) { + this.$.selectAll.classList.add('some-selected'); + return false; + } else { + this.$.selectAll.classList.remove('some-selected'); + return true; + } + } +} + +customElements.define(OobeAppsList.is, OobeAppsList);
diff --git a/chrome/browser/resources/chromeos/login/oobe_auto_imports.gni b/chrome/browser/resources/chromeos/login/oobe_auto_imports.gni index ff2c62e..3ff82d7 100644 --- a/chrome/browser/resources/chromeos/login/oobe_auto_imports.gni +++ b/chrome/browser/resources/chromeos/login/oobe_auto_imports.gni
@@ -49,6 +49,7 @@ "chrome/browser/resources/chromeos/login/components/long_touch_detector.html|LongTouchDetector", "chrome/browser/resources/chromeos/login/components/network_select_login.html|NetworkSelectLogin", "chrome/browser/resources/chromeos/login/components/oobe_select.html|getSelectedValue,getSelectedTitle,SelectListType,setupSelect", + "chrome/browser/resources/chromeos/login/components/oobe_apps_list.html|OobeAppsList", "chrome/browser/resources/chromeos/login/components/oobe_types.html|OobeTypes", "chrome/browser/resources/chromeos/login/components/oobe_a11y_option.html|OobeA11yOption", "chrome/browser/resources/chromeos/login/components/web_view_helper.html|WebViewHelper",
diff --git a/chrome/browser/resources/chromeos/login/screens/common/BUILD.gn b/chrome/browser/resources/chromeos/login/screens/common/BUILD.gn index 76a0ed2..b581bd82 100644 --- a/chrome/browser/resources/chromeos/login/screens/common/BUILD.gn +++ b/chrome/browser/resources/chromeos/login/screens/common/BUILD.gn
@@ -519,6 +519,7 @@ js_library("recommend_apps.m") { sources = [ "$root_gen_dir/chrome/browser/resources/chromeos/login/screens/common/recommend_apps.m.js" ] deps = [ + "../../components:oobe_apps_list.m", "../../components:oobe_types.m", "../../components/behaviors:login_screen_behavior.m", "../../components/behaviors:multi_step_behavior.m",
diff --git a/chrome/browser/resources/chromeos/login/screens/common/recommend_apps.html b/chrome/browser/resources/chromeos/login/screens/common/recommend_apps.html index ccbbebf..3d377cb8 100644 --- a/chrome/browser/resources/chromeos/login/screens/common/recommend_apps.html +++ b/chrome/browser/resources/chromeos/login/screens/common/recommend_apps.html
@@ -16,6 +16,7 @@ <link rel="import" href="../../components/buttons/oobe_text_button.html"> <link rel="import" href="../../components/common_styles/oobe_dialog_host_styles.html"> <link rel="import" href="../../components/dialogs/oobe_adaptive_dialog.html"> +<link rel="import" href="../../components/oobe_apps_list.html"> <link rel="import" href="../../components/oobe_types.html"> <dom-module id="recommend-apps-element"> @@ -36,6 +37,10 @@ justify-content: flex-end; margin-bottom: 16px; } + + #app-list-view-container-new { + overflow-y: auto; + } </style> <oobe-loading-dialog id="loadingDialog" for-step="loading" title-key="recommendAppsLoading"> @@ -52,16 +57,28 @@ <div slot="subtitle"> [[i18nDynamic(locale, 'recommendAppsScreenDescription')]] </div> - <div id="app-list-view-container" slot="content" - hidden="[[isOobeNewRecommendAppsEnabled_]]"> - <div id="selectAllButton"> - <a class="oobe-local-link focus-on-show" is="action-link" - on-click="onSelectAll_"> - [[i18nDynamic(locale, 'recommendAppsSelectAll')]] - </a> + <template is="dom-if" if="[[!isOobeNewRecommendAppsEnabled_]]"> + <div id="app-list-view-container" slot="content"> + <div id="selectAllButton"> + <a class="oobe-local-link focus-on-show" is="action-link" + on-click="onSelectAll_"> + [[i18nDynamic(locale, 'recommendAppsSelectAll')]] + </a> + </div> + <webview id="appView" on-message="onMessage_"></webview> </div> - <webview id="appView" on-message="onMessage_"></webview> - </div> + </template> + <template is="dom-if" if="[[isOobeNewRecommendAppsEnabled_]]"> + <div id="app-list-view-container-new" slot="content"> + <oobe-apps-list id="appsList" app-list="[[appList_]]" + apps-selected="{{appsSelected_}}" + on-apps-list-loaded="onFullyLoaded_"> + <div slot="selectAllTitle"> + [[i18nDynamic(locale, 'recommendAppsSelectAll')]] + </div> + </oobe-apps-list> + </div> + </template> <div slot="bottom-buttons"> <oobe-text-button id="skipButton" text-key="recommendAppsSkip" on-click="onSkip_" border>
diff --git a/chrome/browser/resources/chromeos/login/screens/common/recommend_apps.js b/chrome/browser/resources/chromeos/login/screens/common/recommend_apps.js index 7c502ad..d158f24 100644 --- a/chrome/browser/resources/chromeos/login/screens/common/recommend_apps.js +++ b/chrome/browser/resources/chromeos/login/screens/common/recommend_apps.js
@@ -33,10 +33,11 @@ * @typedef {{ * appsDialog: OobeAdaptiveDialogElement, * appView: WebView, + * appsList: OobeAppsList, * installButton: OobeTextButton, * }} */ - RecommendAppsElementBase.$; +RecommendAppsElementBase.$; /** * @polymer @@ -61,6 +62,11 @@ value: 0, }, + appList_: { + type: Array, + value: [], + }, + /** * If new version of screen available. * @private @@ -125,7 +131,9 @@ setWebview(contents) { cr.ui.login.invokePolymerMethod(this.$.appsDialog, 'onBeforeShow'); - this.$.appView.src = + // Can't use this.$.appView here as the element is in a <dom-if>. + const appListView = this.shadowRoot.querySelector('#appView'); + appListView.src = 'data:text/html;charset=utf-8,' + encodeURIComponent(contents); } @@ -135,13 +143,40 @@ */ loadAppList(appList) { if (this.isOobeNewRecommendAppsEnabled_) { - // TODO(dkuzmin): finish UI changes of a new layout. - this.setUIStep(RecommendAppsUiState.LIST); + const recommendAppsContainsAdsStr = this.i18n('recommendAppsContainsAds'); + const recommendAppsInAppPurchasesStr = + this.i18n('recommendAppsInAppPurchases'); + const recommendAppsWasInstalledStr = + this.i18n('recommendAppsWasInstalled'); + this.appList_ = appList.map(app => { + const tagList = [app.category]; + if (app.contains_ads) { + tagList.push(recommendAppsContainsAdsStr); + } + if (app.in_app_purchases) { + tagList.push(recommendAppsInAppPurchasesStr); + } + if (app.was_installed) { + tagList.push(recommendAppsWasInstalledStr); + } + if (app.content_rating) { + tagList.push(app.content_rating); + } + return { + title: app.title, + icon_url: app.icon_url, + tags: tagList, + description: app.description, + package_name: app.package_name, + checked: false, + }; + }); return; } this.appCount_ = appList.length; - const appListView = this.$.appView; + // Can't use this.$.appView here as the element is in a <dom-if>. + const appListView = this.shadowRoot.querySelector('#appView'); appListView.addEventListener('contentload', () => { appListView.executeScript( {file: 'recommend_app_old_list_view.js'}, () => { @@ -169,7 +204,12 @@ * Handles event when contents in the webview is generated. */ onFullyLoaded_() { - const appListView = this.$.appView; + if (this.isOobeNewRecommendAppsEnabled_) { + this.setUIStep(RecommendAppsUiState.LIST); + return; + } + // Can't use this.$.appView here as the element is in a <dom-if>. + const appListView = this.shadowRoot.querySelector('#appView'); appListView.executeScript({code: 'getHeight();'}, function(result) { appListView.setAttribute('style', 'height: ' + result + 'px'); }); @@ -190,7 +230,15 @@ onInstall_() { // Only start installation if there are apps to install. if (this.appsSelected_ > 0) { - const appListView = this.$.appView; + if (this.isOobeNewRecommendAppsEnabled_) { + // Can't use this.$.appsList here as the element is in a <dom-if>. + const appsList = this.shadowRoot.querySelector('#appsList'); + const packageNames = appsList.getSelectedApps(); + chrome.send('recommendAppsInstall', [packageNames]); + return; + } + // Can't use this.$.appView here as the element is in a <dom-if>. + const appListView = this.shadowRoot.querySelector('#appView'); appListView.executeScript( {code: 'getSelectedPackages();'}, function(result) { chrome.send('recommendAppsInstall', [result[0]]); @@ -214,7 +262,8 @@ * Handles Select all button click. */ onSelectAll_() { - const appListView = this.$.appView; + // Can't use this.$.appView here as the element is in a <dom-if>. + const appListView = this.shadowRoot.querySelector('#appView'); if (!this.isOobeNewRecommendAppsEnabled_) { appListView.executeScript({code: 'selectAll();'}); return;
diff --git a/chrome/browser/resources/chromeos/login/screens/common/sync_consent.js b/chrome/browser/resources/chromeos/login/screens/common/sync_consent.js index 564d8ceb..1a18a9e 100644 --- a/chrome/browser/resources/chromeos/login/screens/common/sync_consent.js +++ b/chrome/browser/resources/chromeos/login/screens/common/sync_consent.js
@@ -154,10 +154,11 @@ * @private */ onSettingsSaveAndContinue_(e, opted_in) { - assert(e.path); + assert(e.composedPath()); chrome.send('login.SyncConsentScreen.continue', [ opted_in, this.$.reviewSettingsBox.checked, this.getConsentDescription_(), - this.getConsentConfirmation_(e.path) + this.getConsentConfirmation_( + /** @type {!Array<!HTMLElement>} */ (e.composedPath())) ]); }
diff --git a/chrome/browser/resources/chromeos/notification_tester/OWNERS b/chrome/browser/resources/chromeos/notification_tester/OWNERS new file mode 100644 index 0000000..0fa385f --- /dev/null +++ b/chrome/browser/resources/chromeos/notification_tester/OWNERS
@@ -0,0 +1 @@ +leandre@chromium.org \ No newline at end of file
diff --git a/chrome/browser/resources/chromeos/notification_tester/index.html b/chrome/browser/resources/chromeos/notification_tester/index.html new file mode 100644 index 0000000..a300eae --- /dev/null +++ b/chrome/browser/resources/chromeos/notification_tester/index.html
@@ -0,0 +1,24 @@ +<!-- Copyright 2022 The Chromium Authors. All rights reserved. +Use of this source code is governed by a BSD-style license that can be +found in the LICENSE file. --> + +<!DOCTYPE HTML> +<html i18n-values="dir:textdirection"> + +<head> + <meta charset="utf-8"> + <title>Notification Tester</title> + <script type="module" src="index.js"></script> + <script src="chrome://resources/js/cr.js"></script> + <script src="chrome://resources/js/load_time_data.js"></script> + <script src="chrome://resources/js/util.js"></script> + <script src="strings.js"></script> +</head> + +<body> + <h1>Hello World</h1> + <p id="p1">test words</p> + <button type="button">Click Me!</button> +</body> + +</html> \ No newline at end of file
diff --git a/chrome/browser/resources/conflicts/BUILD.gn b/chrome/browser/resources/conflicts/BUILD.gn index 0f1e454..d005f23 100644 --- a/chrome/browser/resources/conflicts/BUILD.gn +++ b/chrome/browser/resources/conflicts/BUILD.gn
@@ -2,15 +2,13 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -import("//third_party/closure_compiler/compile_js.gni") +import("//tools/typescript/ts_library.gni") -js_type_check("closure_compile") { - deps = [ ":about_conflicts" ] -} +assert(is_win) -js_library("about_conflicts") { - deps = [ - "//ui/webui/resources/js:cr.m", - "//ui/webui/resources/js:util.m", - ] +ts_library("build_ts") { + root_dir = "." + out_dir = "$target_gen_dir/tsc" + in_files = [ "about_conflicts.ts" ] + deps = [ "//ui/webui/resources:library" ] }
diff --git a/chrome/browser/resources/conflicts/about_conflicts.js b/chrome/browser/resources/conflicts/about_conflicts.js deleted file mode 100644 index 08df95c9..0000000 --- a/chrome/browser/resources/conflicts/about_conflicts.js +++ /dev/null
@@ -1,82 +0,0 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js'; - -import {sendWithPromise} from 'chrome://resources/js/cr.m.js'; -import {$} from 'chrome://resources/js/util.m.js'; - -/** - * This variable structure is here to document the structure that the template - * expects to correctly populate the page. - */ -const moduleListDataFormat = { - 'moduleList': [{ - 'type_description': - 'The type of module (string), defaults to blank for regular modules', - 'location': 'The module path, not including filename', - 'name': 'The name of the module', - 'product_name': 'The name of the product the module belongs to', - 'description': 'The module description', - 'version': 'The module version', - 'digital_signer': 'The signer of the digital certificate for the module', - 'code_id': 'The code id of the module', - 'third_party_module_status': 'The module status' - }] -}; - -/** - * Takes the |moduleListData| input argument which represents data about - * the currently available modules and populates the HTML template - * with that data. It expects an object structure like the above. - * @param {Object} moduleListData Information about available modules. - */ -function renderTemplate(moduleListData) { - const bind = document.body.querySelector('dom-bind'); - - moduleListData.hasModules = moduleListData.moduleList.length > 0; - bind.data = moduleListData; -} - -/** - * Asks the C++ ConflictsHandler to get details about the available modules - * and return detailed data about the configuration. - */ -function requestModuleListData() { - sendWithPromise('requestModuleList').then(returnModuleList); -} - -/** - * Filters list of displayed modules to those listed in the process types - * specified in the url fragment. For instance, chrome://conflicts/#r will show - * only those modules that have loaded into a renderer. - */ -function filterModuleListData() { - const filter = window.location.hash.substr(1).toLowerCase(); - const modules = document.getElementsByClassName('module'); - - // Loop through all modules, and hide all that don't match the filter. - for (const module of modules) { - module.style.display = - module.dataset['process'].toLowerCase().includes(filter) ? '' : 'none'; - } -} - -/** - * Called by the WebUI to re-populate the page with data representing the - * current state of installed modules. - * @param {Object} moduleListData Information about available modules. - */ -function returnModuleList(moduleListData) { - renderTemplate(moduleListData); - if (window.location.hash.length > 1) { - filterModuleListData(); - } - $('loading-message').style.visibility = 'hidden'; - $('body-container').style.visibility = 'visible'; -} - -// Get data and have it displayed upon loading. -document.addEventListener('DOMContentLoaded', requestModuleListData); -window.addEventListener('hashchange', filterModuleListData, false);
diff --git a/chrome/browser/resources/conflicts/about_conflicts.ts b/chrome/browser/resources/conflicts/about_conflicts.ts new file mode 100644 index 0000000..e714696 --- /dev/null +++ b/chrome/browser/resources/conflicts/about_conflicts.ts
@@ -0,0 +1,81 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js'; + +import {assert} from 'chrome://resources/js/assert_ts.js'; +import {sendWithPromise} from 'chrome://resources/js/cr.m.js'; +import {$} from 'chrome://resources/js/util.m.js'; + +type ModuleData = { + code_id: string, + description: string, + digital_signer: string, + location: string, + name: string, + process_types: string, + type_description: string, + version: string, + third_party_module_status: string, +}; + +type ModuleListData = { + moduleCount: number, + moduleList: ModuleData[], + thirdPartyFeatureStatus: string, + thirdPartyFeatureEnabled: boolean, + hasModules: boolean, +}; + +type DomBindElement = HTMLElement&{data: ModuleListData}; + +/** + * Takes the |moduleListData| input argument which represents data about + * the currently available modules and populates the HTML template + * with that data. It expects an object structure like the above. + */ +function renderTemplate(moduleListData: ModuleListData) { + const bind = document.body.querySelector<DomBindElement>('dom-bind'); + assert(bind); + + moduleListData.hasModules = moduleListData.moduleList.length > 0; + bind.data = moduleListData; +} + +/** + * Filters list of displayed modules to those listed in the process types + * specified in the url fragment. For instance, chrome://conflicts/#r will show + * only those modules that have loaded into a renderer. + */ +function filterModuleListData() { + const filter = window.location.hash.substr(1).toLowerCase(); + const modules = document.body.querySelectorAll<HTMLElement>('.module'); + + // Loop through all modules, and hide all that don't match the filter. + for (const module of modules) { + module.style.display = + module.dataset['process']!.toLowerCase().includes(filter) ? '' : 'none'; + } +} + +/** + * Called by the WebUI to re-populate the page with data representing the + * current state of installed modules. + */ +function returnModuleList(moduleListData: ModuleListData) { + renderTemplate(moduleListData); + if (window.location.hash.length > 1) { + filterModuleListData(); + } + $('loading-message').style.visibility = 'hidden'; + $('body-container').style.visibility = 'visible'; +} + +// Get data and have it displayed upon loading. +document.addEventListener('DOMContentLoaded', () => { + // Ask the C++ ConflictsHandler to get details about the available modules + // and return detailed data about the configuration. + sendWithPromise('requestModuleList').then(returnModuleList); + window.addEventListener('hashchange', filterModuleListData, false); +});
diff --git a/chrome/browser/resources/settings/chromeos/device_page/pointers.js b/chrome/browser/resources/settings/chromeos/device_page/pointers.js index eb18a84..07a2a6e6 100644 --- a/chrome/browser/resources/settings/chromeos/device_page/pointers.js +++ b/chrome/browser/resources/settings/chromeos/device_page/pointers.js
@@ -230,11 +230,12 @@ * @private */ onLearnMoreLinkClicked_(event) { - if (!Array.isArray(event.path) || !event.path.length) { + const path = event.composedPath(); + if (!Array.isArray(path) || !path.length) { return; } - if (event.path[0].tagName === 'A') { + if (path[0].tagName === 'A') { // Do not toggle reverse scrolling if the contained link is clicked. event.stopPropagation(); }
diff --git a/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_feature_item.js b/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_feature_item.js index 27e063ac..f30976f 100644 --- a/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_feature_item.js +++ b/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_feature_item.js
@@ -128,7 +128,7 @@ /** @private */ handleItemClick_(event) { // We do not navigate away if the click was on a link. - if (event.path[0].tagName === 'A') { + if (event.composedPath()[0].tagName === 'A') { event.stopPropagation(); return; }
diff --git a/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_page.js b/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_page.js index 6bc6ebd8..8f98caf 100644 --- a/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_page.js +++ b/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_page.js
@@ -325,7 +325,7 @@ /** @private */ handleItemClick_(event) { // We do not open the subpage if the click was on a link. - if (event.path[0].tagName === 'A') { + if (event.composedPath()[0].tagName === 'A') { event.stopPropagation(); return; } @@ -360,7 +360,7 @@ onDialogClose_(event) { event.stopPropagation(); - if (event.path.some( + if (event.composedPath().some( element => element.id === 'multidevicePasswordPrompt')) { this.onPasswordPromptDialogClose_(); }
diff --git a/chrome/browser/resources/settings/chromeos/settings_scheduler_slider/settings_scheduler_slider.js b/chrome/browser/resources/settings/chromeos/settings_scheduler_slider/settings_scheduler_slider.js index 0b61786..ad380c9 100644 --- a/chrome/browser/resources/settings/chromeos/settings_scheduler_slider/settings_scheduler_slider.js +++ b/chrome/browser/resources/settings/chromeos/settings_scheduler_slider/settings_scheduler_slider.js
@@ -657,7 +657,8 @@ */ handleKnobEvent_(event, overrideElement) { const knob = overrideElement || - event.path.find(el => el.classList && el.classList.contains('knob')); + event.composedPath().find( + el => el.classList && el.classList.contains('knob')); if (!knob) { event.preventDefault(); return;
diff --git a/chrome/browser/sync/test/integration/single_client_passwords_sync_test.cc b/chrome/browser/sync/test/integration/single_client_passwords_sync_test.cc index d91667e..64ffcd9 100644 --- a/chrome/browser/sync/test/integration/single_client_passwords_sync_test.cc +++ b/chrome/browser/sync/test/integration/single_client_passwords_sync_test.cc
@@ -106,6 +106,23 @@ base::test::ScopedFeatureList feature_list_; }; +class SingleClientPasswordsSyncTestWithBaseSpecificsInMetadataAndNotes + : public SyncTest { + public: + SingleClientPasswordsSyncTestWithBaseSpecificsInMetadataAndNotes() + : SyncTest(SINGLE_CLIENT) { + feature_list_.InitWithFeatures( + /*enabled_features=*/{syncer::kCacheBaseEntitySpecificsInMetadata, + password_manager::features::kPasswordNotes}, + /*disabled_features=*/{}); + } + ~SingleClientPasswordsSyncTestWithBaseSpecificsInMetadataAndNotes() override = + default; + + private: + base::test::ScopedFeatureList feature_list_; +}; + IN_PROC_BROWSER_TEST_F(SingleClientPasswordsSyncTestWithVerifier, Sanity) { ASSERT_TRUE(SetupSync()) << "SetupSync() failed."; @@ -675,4 +692,84 @@ cryptographer.get(), "new_password", kUnsupportedField))); } +IN_PROC_BROWSER_TEST_F( + SingleClientPasswordsSyncTestWithBaseSpecificsInMetadataAndNotes, + PreservesUnsupportedNotesFieldsDataOnCommits) { + // Create an unsupported field in the PasswordSpecificsData_Notes with an + // unused tag. + const std::string kUnsupportedNotesField = + CreateSerializedProtoField(/*field_number=*/999999, "unknown_field 1"); + // Create an unsupported field in the PasswordSpecificsData_Notes_Note with an + // unused tag. Since they are different protos, they can use the same + // field_number. + const std::string kUnsupportedNoteField = + CreateSerializedProtoField(/*field_number=*/999999, "unknown_field 2"); + + // Create a password on the server with an unsupported field in the notes + // proto as well as the individual notes. + sync_pb::PasswordSpecificsData password_data; + password_data.set_origin("http://fake-site.com/"); + password_data.set_signon_realm("http://fake-site.com/"); + password_data.set_username_value("username-with-note"); + password_data.set_password_value("password"); + + *password_data.mutable_notes()->mutable_unknown_fields() = + kUnsupportedNotesField; + + sync_pb::PasswordSpecificsData_Notes_Note* note = + password_data.mutable_notes()->add_note(); + note->set_value("note value"); + *note->mutable_unknown_fields() = kUnsupportedNoteField; + + passwords_helper::InjectKeystoreEncryptedServerPassword(password_data, + GetFakeServer()); + + // Sign in and enable Sync. + ASSERT_TRUE(SetupSync()) << "SetupSync() failed."; + ASSERT_TRUE(GetSyncService(0)->IsSyncFeatureEnabled()); + ASSERT_TRUE(GetSyncService(0)->GetActiveDataTypes().Has(syncer::PASSWORDS)); + + // Make a local update to the password note. + PasswordForm form; + form.signon_realm = "http://fake-site.com/"; + form.url = GURL("http://fake-site.com/"); + form.username_value = u"username-with-note"; + form.password_value = u"password"; + form.notes.emplace_back(u"new note value", + /*date_created=*/base::Time::Now()); + GetProfilePasswordStoreInterface(0)->UpdateLogin(form); + + // Add an obsolete password to make sure that the server has received the + // update. Otherwise, calling count match could finish before the local update + // actually goes through (as there is already 1 password entity on the + // server). + GetProfilePasswordStoreInterface(0)->AddLogin(CreateTestPasswordForm(2)); + ASSERT_TRUE(ServerCountMatchStatusChecker(syncer::PASSWORDS, 2).Wait()); + + // Check that the password note was updated and the commit preserved the data + // for an unsupported field. + std::unique_ptr<syncer::CryptographerImpl> cryptographer = + syncer::CryptographerImpl::FromSingleKeyForTesting( + base::Base64Encode(fake_server_->GetKeystoreKeys().back()), + syncer::KeyDerivationParams::CreateForPbkdf2()); + + const std::vector<sync_pb::SyncEntity> entities = + fake_server_->GetSyncEntitiesByModelType(syncer::PASSWORDS); + for (const sync_pb::SyncEntity& entity : entities) { + // Find the password with the notes. + sync_pb::PasswordSpecificsData decrypted; + cryptographer->Decrypt(entity.specifics().password().encrypted(), + &decrypted); + if (decrypted.username_value() != "username-with-note") { + continue; + } + EXPECT_EQ(kUnsupportedNotesField, decrypted.notes().unknown_fields()); + ASSERT_EQ(1, decrypted.notes().note_size()); + sync_pb::PasswordSpecificsData_Notes_Note decrypted_note = + decrypted.notes().note(0); + EXPECT_EQ("new note value", decrypted_note.value()); + EXPECT_EQ(kUnsupportedNoteField, decrypted_note.unknown_fields()); + } +} + } // namespace
diff --git a/chrome/browser/sync/test/integration/two_client_passwords_sync_test.cc b/chrome/browser/sync/test/integration/two_client_passwords_sync_test.cc index 6182cf3..bfeea2c 100644 --- a/chrome/browser/sync/test/integration/two_client_passwords_sync_test.cc +++ b/chrome/browser/sync/test/integration/two_client_passwords_sync_test.cc
@@ -12,6 +12,7 @@ #include "base/rand_util.h" #include "base/strings/utf_string_conversions.h" #include "base/test/metrics/histogram_tester.h" +#include "base/test/scoped_feature_list.h" #include "build/build_config.h" #include "chrome/browser/sync/test/integration/passwords_helper.h" #include "chrome/browser/sync/test/integration/sync_integration_test_util.h" @@ -19,6 +20,7 @@ #include "chrome/browser/sync/test/integration/sync_test.h" #include "chrome/browser/sync/test/integration/updated_progress_marker_checker.h" #include "components/password_manager/core/browser/password_store_interface.h" +#include "components/password_manager/core/common/password_manager_features.h" #include "components/sync/engine/cycle/entity_change_metric_recording.h" #include "components/sync/engine/cycle/sync_cycle_snapshot.h" #include "content/public/test/browser_test.h" @@ -497,3 +499,51 @@ EXPECT_THAT(GetAllLogins(GetProfilePasswordStoreInterface(1)), ElementsAre(Pointee(form))); } + +class TwoClientPasswordsSyncTestWithNotes : public SyncTest { + public: + TwoClientPasswordsSyncTestWithNotes() : SyncTest(TWO_CLIENT) { + feature_list_.InitAndEnableFeature( + password_manager::features::kPasswordNotes); + } + ~TwoClientPasswordsSyncTestWithNotes() override = default; + + private: + base::test::ScopedFeatureList feature_list_; +}; + +IN_PROC_BROWSER_TEST_F(TwoClientPasswordsSyncTestWithNotes, + SyncPasswordNotesBetweenDevices) { + ASSERT_TRUE(SetupSync()) << "SetupSync() failed."; + ASSERT_TRUE(AllProfilesContainSamePasswordForms()); + + // Add a password with note to Client 0. + PasswordForm form = CreateTestPasswordForm(0); + form.notes.emplace_back( + /*unique_display_name=*/u"My Phone Pin", /*value=*/u"123456", + /*date_created=*/base::Time::Now(), /*hide_by_default=*/true); + GetProfilePasswordStoreInterface(0)->AddLogin(form); + + // Wait until Client 1 picks up changes. + ASSERT_TRUE(SamePasswordFormsChecker().Wait()); + EXPECT_THAT(GetAllLogins(GetProfilePasswordStoreInterface(1)), + ElementsAre(Pointee(form))); + + // Update the note in Client 1. + form.notes[0].value = u"78910"; + GetProfilePasswordStoreInterface(1)->UpdateLogin(form); + + // Wait until Client 0 picks up changes. + ASSERT_TRUE(SamePasswordFormsChecker().Wait()); + EXPECT_THAT(GetAllLogins(GetProfilePasswordStoreInterface(0)), + ElementsAre(Pointee(form))); + + // Remove all notes on Client 0. + form.notes.clear(); + GetProfilePasswordStoreInterface(0)->UpdateLogin(form); + + // Wait until Client 1 picks up changes. + ASSERT_TRUE(SamePasswordFormsChecker().Wait()); + EXPECT_THAT(GetAllLogins(GetProfilePasswordStoreInterface(1)), + ElementsAre(Pointee(form))); +}
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn index 05968a07..191b348 100644 --- a/chrome/browser/ui/BUILD.gn +++ b/chrome/browser/ui/BUILD.gn
@@ -300,6 +300,12 @@ "webui/policy/policy_ui.h", "webui/policy/policy_ui_handler.cc", "webui/policy/policy_ui_handler.h", + "webui/policy/status_provider/cloud_policy_core_status_provider.cc", + "webui/policy/status_provider/cloud_policy_core_status_provider.h", + "webui/policy/status_provider/status_provider_util.cc", + "webui/policy/status_provider/status_provider_util.h", + "webui/policy/status_provider/user_cloud_policy_status_provider.cc", + "webui/policy/status_provider/user_cloud_policy_status_provider.h", "webui/predictors/predictors_handler.cc", "webui/predictors/predictors_handler.h", "webui/predictors/predictors_ui.cc", @@ -2769,6 +2775,8 @@ "webui/chromeos/network_logs_message_handler.h", "webui/chromeos/network_ui.cc", "webui/chromeos/network_ui.h", + "webui/chromeos/notification_tester/notification_tester_ui.cc", + "webui/chromeos/notification_tester/notification_tester_ui.h", "webui/chromeos/onc_import_message_handler.cc", "webui/chromeos/onc_import_message_handler.h", "webui/chromeos/parent_access/parent_access_dialog.cc", @@ -2835,6 +2843,8 @@ "webui/nearby_share/nearby_share_dialog_ui.h", "webui/nearby_share/shared_resources.cc", "webui/nearby_share/shared_resources.h", + "webui/policy/status_provider/device_cloud_policy_status_provider_chromeos.cc", + "webui/policy/status_provider/device_cloud_policy_status_provider_chromeos.h", "webui/settings/ash/app_management/app_management_uma.h", "webui/settings/ash/calculator/size_calculator.cc", "webui/settings/ash/calculator/size_calculator.h",
diff --git a/chrome/browser/ui/ash/shelf/app_service/exo_app_type_resolver.cc b/chrome/browser/ui/ash/shelf/app_service/exo_app_type_resolver.cc index a0f7f10f..49ef60f 100644 --- a/chrome/browser/ui/ash/shelf/app_service/exo_app_type_resolver.cc +++ b/chrome/browser/ui/ash/shelf/app_service/exo_app_type_resolver.cc
@@ -48,11 +48,6 @@ true); out_properties_container.SetProperty(app_restore::kLacrosWindowId, params.app_id); - const int32_t restore_window_id = - app_restore::GetLacrosRestoreWindowId(params.app_id); - out_properties_container.SetProperty( - app_restore::kParentToHiddenContainerKey, - restore_window_id == app_restore::kParentToHiddenContainer); out_properties_container.SetProperty(ash::kWebAuthnRequestId, new std::string(params.app_id));
diff --git a/chrome/browser/ui/autofill/payments/save_card_bubble_controller_impl.cc b/chrome/browser/ui/autofill/payments/save_card_bubble_controller_impl.cc index b3cda321..b9adfeb 100644 --- a/chrome/browser/ui/autofill/payments/save_card_bubble_controller_impl.cc +++ b/chrome/browser/ui/autofill/payments/save_card_bubble_controller_impl.cc
@@ -164,11 +164,29 @@ } std::u16string SaveCardBubbleControllerImpl::GetWindowTitle() const { + int save_card_ui_experiment_selected = + features::kAutofillSaveCardUiExperimentSelectorInNumber.Get(); + // Check if the save card offer ui experiment is enabled. + bool save_card_ui_experiment_enabled = + (base::FeatureList::IsEnabled(features::kAutofillSaveCardUiExperiment) && + save_card_ui_experiment_selected); + switch (current_bubble_type_) { case BubbleType::LOCAL_SAVE: return l10n_util::GetStringUTF16( IDS_AUTOFILL_SAVE_CARD_PROMPT_TITLE_LOCAL); case BubbleType::UPLOAD_SAVE: + if (save_card_ui_experiment_enabled) { + switch (save_card_ui_experiment_selected) { + case SaveCardUiExperiment::FASTER_AND_PROTECTED: + return l10n_util::GetStringUTF16( + IDS_AUTOFILL_SAVE_CARD_PROMPT_TITLE_EXPERIMENT_FASTER_AND_PROTECTED); + case SaveCardUiExperiment::ENCRYPTED_AND_SECURE: + return l10n_util::GetStringUTF16( + IDS_AUTOFILL_SAVE_CARD_PROMPT_TITLE_EXPERIMENT_ENCRYPTED_AND_SECURE); + } + } + return features::ShouldShowImprovedUserConsentForCreditCardSave() ? l10n_util::GetStringUTF16( IDS_AUTOFILL_SAVE_CARD_PROMPT_TITLE_TO_CLOUD_V4) @@ -197,6 +215,23 @@ IDS_AUTOFILL_SAVE_CARD_PROMPT_UPLOAD_EXPLANATION_V3_WITH_NAME); } + int save_card_ui_experiment_selected = + features::kAutofillSaveCardUiExperimentSelectorInNumber.Get(); + // Check if the save card offer ui experiment is enabled. + bool save_card_ui_experiment_enabled = + (base::FeatureList::IsEnabled(features::kAutofillSaveCardUiExperiment) && + save_card_ui_experiment_selected); + if (save_card_ui_experiment_enabled) { + switch (save_card_ui_experiment_selected) { + case SaveCardUiExperiment::FASTER_AND_PROTECTED: + return l10n_util::GetStringUTF16( + IDS_AUTOFILL_SAVE_CARD_PROMPT_UPLOAD_EXPLANATION_EXPERIMENT_FASTER_AND_PROTECTED); + case SaveCardUiExperiment::ENCRYPTED_AND_SECURE: + return l10n_util::GetStringUTF16( + IDS_AUTOFILL_SAVE_CARD_PROMPT_UPLOAD_EXPLANATION_EXPERIMENT_ENCRYPTED_AND_SECURE); + } + } + return l10n_util::GetStringUTF16( IDS_AUTOFILL_SAVE_CARD_PROMPT_UPLOAD_EXPLANATION_V3); }
diff --git a/chrome/browser/ui/autofill/payments/save_card_ui.h b/chrome/browser/ui/autofill/payments/save_card_ui.h index e54f914..c5a71b7 100644 --- a/chrome/browser/ui/autofill/payments/save_card_ui.h +++ b/chrome/browser/ui/autofill/payments/save_card_ui.h
@@ -31,6 +31,15 @@ INACTIVE }; +// The type of experiment running for the save card ui. +enum SaveCardUiExperiment { + // Show the text for faster and protected image. + FASTER_AND_PROTECTED = 1, + + // Show the text for encrypted and secure image. + ENCRYPTED_AND_SECURE = 2 +}; + } // namespace autofill #endif // CHROME_BROWSER_UI_AUTOFILL_PAYMENTS_SAVE_CARD_UI_H_
diff --git a/chrome/browser/ui/autofill_assistant/password_change/mock_password_change_run_controller.h b/chrome/browser/ui/autofill_assistant/password_change/mock_password_change_run_controller.h index cdd234a..7278e59 100644 --- a/chrome/browser/ui/autofill_assistant/password_change/mock_password_change_run_controller.h +++ b/chrome/browser/ui/autofill_assistant/password_change/mock_password_change_run_controller.h
@@ -9,6 +9,8 @@ #include "chrome/browser/ui/autofill_assistant/password_change/password_change_run_controller.h" #include "testing/gmock/include/gmock/gmock.h" +class PasswordChangeRunDisplay; + // Mocked PasswordChangeRunController used in unit tests. class MockPasswordChangeRunController : public PasswordChangeRunController { public:
diff --git a/chrome/browser/ui/autofill_assistant/password_change/password_change_run_controller.h b/chrome/browser/ui/autofill_assistant/password_change/password_change_run_controller.h index f7b57e9..0a8eb12 100644 --- a/chrome/browser/ui/autofill_assistant/password_change/password_change_run_controller.h +++ b/chrome/browser/ui/autofill_assistant/password_change/password_change_run_controller.h
@@ -22,7 +22,7 @@ struct Model { std::u16string title; autofill_assistant::password_change::TopIcon top_icon; - std::u16string progress_description; + std::u16string description; autofill_assistant::password_change::ProgressStep progress_step; }; @@ -41,7 +41,7 @@ virtual void SetTopIcon( autofill_assistant::password_change::TopIcon top_icon) = 0; virtual void SetTitle(std::u16string title) = 0; - virtual void SetDescription(std::u16string progress_description) = 0; + virtual void SetDescription(std::u16string description) = 0; virtual void SetProgressBarStep( autofill_assistant::password_change::ProgressStep progress_step) = 0;
diff --git a/chrome/browser/ui/browser_dialogs.h b/chrome/browser/ui/browser_dialogs.h index af39e4e4..fd71eb5a 100644 --- a/chrome/browser/ui/browser_dialogs.h +++ b/chrome/browser/ui/browser_dialogs.h
@@ -20,6 +20,7 @@ #include "content/public/browser/bluetooth_delegate.h" #include "content/public/browser/login_delegate.h" #include "extensions/buildflags/buildflags.h" +#include "extensions/common/extension_id.h" #include "third_party/abseil-cpp/absl/types/optional.h" #include "third_party/skia/include/core/SkColor.h" #include "ui/base/interaction/element_identifier.h" @@ -305,7 +306,7 @@ // blocked due to policy. It also show additional information from administrator // if it exists. void ShowExtensionInstallBlockedDialog( - const std::string& extension_id, + const extensions::ExtensionId& extension_id, const std::string& extension_name, const std::u16string& custom_error_message, const gfx::ImageSkia& icon,
diff --git a/chrome/browser/ui/signin/dice_web_signin_interceptor_delegate.cc b/chrome/browser/ui/signin/dice_web_signin_interceptor_delegate.cc index 614e80e..0b81699 100644 --- a/chrome/browser/ui/signin/dice_web_signin_interceptor_delegate.cc +++ b/chrome/browser/ui/signin/dice_web_signin_interceptor_delegate.cc
@@ -8,7 +8,7 @@ #include "base/callback.h" #include "base/feature_list.h" -#include "base/memory/raw_ptr.h" +#include "base/memory/weak_ptr.h" #include "base/threading/thread_task_runner_handle.h" #include "chrome/browser/signin/identity_manager_factory.h" #include "chrome/browser/signin/signin_features.h" @@ -46,7 +46,7 @@ const DiceWebSigninInterceptor::Delegate::BubbleParameters& bubble_parameters, base::OnceCallback<void(SigninInterceptionResult)> callback) - : browser_(browser), + : browser_(browser->AsWeakPtr()), profile_creation_required_by_policy_( bubble_parameters.interception_type == DiceWebSigninInterceptor::SigninInterceptionType:: @@ -55,28 +55,23 @@ callback_(std::move(callback)) { DCHECK(browser_); DCHECK(callback_); - ShowEnterpriseProfileInterceptionDialog( - bubble_parameters.intercepted_account, - bubble_parameters.profile_highlight_color); - } - - ~ForcedEnterpriseSigninInterceptionHandle() override { - browser_->signin_view_controller()->CloseModalSignin(); - if (callback_) - std::move(callback_).Run(SigninInterceptionResult::kDeclined); - } - - private: - void ShowEnterpriseProfileInterceptionDialog(const AccountInfo& account_info, - SkColor profile_color) { browser_->signin_view_controller()->ShowModalEnterpriseConfirmationDialog( - account_info, profile_creation_required_by_policy_, - show_link_data_option_, profile_color, + bubble_parameters.intercepted_account, + profile_creation_required_by_policy_, show_link_data_option_, + bubble_parameters.profile_highlight_color, base::BindOnce(&ForcedEnterpriseSigninInterceptionHandle:: OnEnterpriseInterceptionDialogClosed, base::Unretained(this))); } + ~ForcedEnterpriseSigninInterceptionHandle() override { + if (browser_) + browser_->signin_view_controller()->CloseModalSignin(); + if (callback_) + std::move(callback_).Run(SigninInterceptionResult::kDeclined); + } + + private: void OnEnterpriseInterceptionDialogClosed(signin::SigninChoice result) { switch (result) { case signin::SIGNIN_CHOICE_NEW_PROFILE: @@ -97,7 +92,7 @@ } } - raw_ptr<Browser> browser_; + base::WeakPtr<Browser> browser_; const bool profile_creation_required_by_policy_; const bool show_link_data_option_; base::OnceCallback<void(SigninInterceptionResult)> callback_;
diff --git a/chrome/browser/ui/views/apps/chrome_native_app_window_views_aura.cc b/chrome/browser/ui/views/apps/chrome_native_app_window_views_aura.cc index 24c25d24..ff7713df 100644 --- a/chrome/browser/ui/views/apps/chrome_native_app_window_views_aura.cc +++ b/chrome/browser/ui/views/apps/chrome_native_app_window_views_aura.cc
@@ -23,6 +23,11 @@ #include "chrome/browser/shell_integration_linux.h" #endif +#if BUILDFLAG(IS_CHROMEOS_LACROS) +#include "chrome/browser/lacros/lacros_extensions_util.h" +#include "chrome/browser/profiles/profile.h" +#endif + using extensions::AppWindow; ui::WindowShowState @@ -62,6 +67,18 @@ init_params->wm_role_name = std::string(kX11WindowRoleApp); #endif // BUILDFLAG(IS_LINUX) +#if BUILDFLAG(IS_CHROMEOS_LACROS) + init_params->restore_session_id = app_window()->session_id().id(); + // `MuxId` could return a non-empty string even if extension is null, so don't + // save `restore_window_id_source` to avoid that. + if (app_window()->GetExtension()) { + Profile* profile = + Profile::FromBrowserContext(app_window()->browser_context()); + init_params->restore_window_id_source = + lacros_extensions_util::MuxId(profile, app_window()->GetExtension()); + } +#endif + ChromeNativeAppWindowViews::OnBeforeWidgetInit(create_params, init_params, widget); }
diff --git a/chrome/browser/ui/views/extensions/blocked_action_dialog_view.cc b/chrome/browser/ui/views/extensions/blocked_action_dialog_view.cc index 9b6e7fc..708f0e14 100644 --- a/chrome/browser/ui/views/extensions/blocked_action_dialog_view.cc +++ b/chrome/browser/ui/views/extensions/blocked_action_dialog_view.cc
@@ -8,12 +8,11 @@ #include "base/callback_forward.h" #include "base/feature_list.h" #include "chrome/browser/extensions/site_permissions_helper.h" +#include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/toolbar/toolbar_action_view_controller.h" #include "chrome/browser/ui/ui_features.h" #include "chrome/browser/ui/views/extensions/extensions_dialogs_utils.h" -#include "chrome/browser/ui/views/extensions/extensions_toolbar_button.h" #include "chrome/browser/ui/views/extensions/extensions_toolbar_container.h" -#include "chrome/browser/ui/views/frame/browser_view.h" #include "chrome/browser/ui/views/frame/toolbar_button_provider.h" #include "chrome/grit/generated_resources.h" #include "ui/base/interaction/element_identifier.h" @@ -46,16 +45,9 @@ const extensions::ExtensionId& extension_id, bool show_checkbox, base::OnceCallback<void(bool)> callback) { - // TODO(crbug.com/1322796): Multiple classes use this. We should pull getting - // an anchor view and showing a BubbleDialogDelegate into a common location. - BrowserView* const browser_view = - BrowserView::GetBrowserViewForBrowser(browser); ExtensionsToolbarContainer* const container = - browser_view ? browser_view->toolbar_button_provider() - ->GetExtensionsToolbarContainer() - : nullptr; + GetExtensionsToolbarContainer(browser); DCHECK(container); - auto* extension = container->GetActionForId(extension_id); auto on_ok_button_clicked = [](ui::DialogModel* dialog_model, bool did_show_checkbox, @@ -67,6 +59,8 @@ ui::DialogModel::Builder dialog_builder; if (base::FeatureList::IsEnabled(features::kExtensionsMenuAccessControl)) { + ToolbarActionViewController* extension = + container->GetActionForId(extension_id); content::WebContents* web_contents = browser->tab_strip_model()->GetActiveWebContents(); dialog_builder @@ -76,9 +70,11 @@ .SetIcon(GetIcon(extension, web_contents)) .AddBodyText(ui::DialogModelLabel(l10n_util::GetStringUTF16( IDS_EXTENSION_BLOCKED_ACTION_BUBBLE_BODY_TEXT))) - .AddOkButton(base::BindOnce(on_ok_button_clicked, - dialog_builder.model(), show_checkbox, - std::move(callback))); + .AddOkButton( + base::BindOnce(on_ok_button_clicked, dialog_builder.model(), + show_checkbox, std::move(callback)), + l10n_util::GetStringUTF16( + IDS_EXTENSION_BLOCKED_ACTION_BUBBLE_OK_BUTTON_LABEL)); if (show_checkbox) { dialog_builder.AddCheckbox( kCheckboxId, @@ -94,13 +90,5 @@ IDS_EXTENSION_BLOCKED_ACTION_BUBBLE_OK_BUTTON)); } - views::View* const anchor_view = container->GetViewForId(extension_id); - auto bubble = std::make_unique<views::BubbleDialogModelHost>( - dialog_builder.Build(), - anchor_view ? anchor_view : container->GetExtensionsButton(), - views::BubbleBorder::TOP_RIGHT); - - container->ShowWidgetForExtension( - views::BubbleDialogDelegate::CreateBubble(std::move(bubble)), - extension_id); + ShowDialog(container, extension_id, dialog_builder.Build()); }
diff --git a/chrome/browser/ui/views/extensions/extension_uninstall_dialog_view.cc b/chrome/browser/ui/views/extensions/extension_uninstall_dialog_view.cc index 169b0b3..a41b76e 100644 --- a/chrome/browser/ui/views/extensions/extension_uninstall_dialog_view.cc +++ b/chrome/browser/ui/views/extensions/extension_uninstall_dialog_view.cc
@@ -13,11 +13,9 @@ #include "chrome/browser/extensions/extension_uninstall_dialog.h" #include "chrome/browser/ui/views/chrome_layout_provider.h" #include "chrome/browser/ui/views/chrome_typography.h" +#include "chrome/browser/ui/views/extensions/extensions_dialogs_utils.h" #include "chrome/browser/ui/views/extensions/extensions_toolbar_container.h" -#include "chrome/browser/ui/views/frame/browser_view.h" -#include "chrome/browser/ui/views/frame/toolbar_button_provider.h" #include "chrome/grit/generated_resources.h" -#include "components/constrained_window/constrained_window_views.h" #include "components/strings/grit/components_strings.h" #include "extensions/common/constants.h" #include "extensions/common/extension.h" @@ -128,28 +126,7 @@ std::unique_ptr<ui::DialogModel> dialog_model = dialog_builder.Build(); dialog_model_ = dialog_model.get(); - // TODO(crbug.com/1322796): Multiple classes use this. We should pull getting - // an anchor view and showing a BubbleDialogDelegate into a common location. - BrowserView* const browser_view = - parent() ? BrowserView::GetBrowserViewForNativeWindow(parent()) : nullptr; - ExtensionsToolbarContainer* const container = - browser_view ? browser_view->toolbar_button_provider() - ->GetExtensionsToolbarContainer() - : nullptr; - ToolbarActionView* anchor_view = - container ? container->GetViewForId(extension()->id()) : nullptr; - - if (anchor_view) { - DCHECK(container); - auto bubble = std::make_unique<views::BubbleDialogModelHost>( - std::move(dialog_model), anchor_view, views::BubbleBorder::TOP_RIGHT); - - container->ShowWidgetForExtension( - views::BubbleDialogDelegate::CreateBubble(std::move(bubble)), - extension()->id()); - } else { - constrained_window::ShowBrowserModal(std::move(dialog_model), parent()); - } + ShowDialog(parent(), extension()->id(), std::move(dialog_model)); } void ExtensionUninstallDialogViews::Close() {
diff --git a/chrome/browser/ui/views/extensions/extensions_dialogs_utils.cc b/chrome/browser/ui/views/extensions/extensions_dialogs_utils.cc index f4805dbb..1bb95b50 100644 --- a/chrome/browser/ui/views/extensions/extensions_dialogs_utils.cc +++ b/chrome/browser/ui/views/extensions/extensions_dialogs_utils.cc
@@ -7,10 +7,27 @@ #include "chrome/browser/ui/toolbar/toolbar_action_view_controller.h" #include "chrome/browser/ui/views/chrome_layout_provider.h" #include "chrome/browser/ui/views/extensions/extensions_menu_item_view.h" +#include "chrome/browser/ui/views/extensions/extensions_toolbar_button.h" +#include "chrome/browser/ui/views/extensions/extensions_toolbar_container.h" +#include "chrome/browser/ui/views/frame/browser_view.h" +#include "chrome/browser/ui/views/frame/toolbar_button_provider.h" +#include "components/constrained_window/constrained_window_views.h" #include "components/url_formatter/url_formatter.h" +#include "ui/gfx/native_widget_types.h" #include "ui/views/controls/image_view.h" #include "ui/views/layout/flex_layout_view.h" +namespace { + +ExtensionsToolbarContainer* GetExtensionsToolbarContainer( + BrowserView* browser_view) { + return browser_view ? browser_view->toolbar_button_provider() + ->GetExtensionsToolbarContainer() + : nullptr; +} + +} // namespace + std::unique_ptr<views::BubbleDialogModelHost::CustomView> CreateExtensionItem( const std::u16string& name, const ui::ImageModel& icon) { @@ -31,6 +48,19 @@ views::BubbleDialogModelHost::FieldType::kMenuItem); } +ExtensionsToolbarContainer* GetExtensionsToolbarContainer(Browser* browser) { + BrowserView* const browser_view = + BrowserView::GetBrowserViewForBrowser(browser); + return GetExtensionsToolbarContainer(browser_view); +} + +ExtensionsToolbarContainer* GetExtensionsToolbarContainer( + gfx::NativeWindow parent) { + BrowserView* const browser_view = + BrowserView::GetBrowserViewForNativeWindow(parent); + return GetExtensionsToolbarContainer(browser_view); +} + // TODO(crbug.com/1325171): Use extensions::IconImage instead of getting the // action's image. The icon displayed should be the "product" icon and not the // "action" action based on the web contents. @@ -54,3 +84,33 @@ url_formatter::kFormatUrlTrimAfterHost, base::UnescapeRule::NORMAL, nullptr, nullptr, nullptr); } + +views::View* GetDialogAnchorView(ExtensionsToolbarContainer* container, + const extensions::ExtensionId& extension_id) { + DCHECK(container); + views::View* const action_view = container->GetViewForId(extension_id); + return action_view ? action_view : container->GetExtensionsButton(); +} + +void ShowDialog(gfx::NativeWindow parent, + const extensions::ExtensionId& extension_id, + std::unique_ptr<ui::DialogModel> dialog_model) { + ExtensionsToolbarContainer* container = GetExtensionsToolbarContainer(parent); + if (container) + ShowDialog(container, extension_id, std::move(dialog_model)); + else + constrained_window::ShowBrowserModal(std::move(dialog_model), parent); +} + +void ShowDialog(ExtensionsToolbarContainer* container, + const extensions::ExtensionId& extension_id, + std::unique_ptr<ui::DialogModel> dialog_model) { + DCHECK(container); + views::View* const anchor_view = GetDialogAnchorView(container, extension_id); + auto bubble = std::make_unique<views::BubbleDialogModelHost>( + std::move(dialog_model), anchor_view, views::BubbleBorder::TOP_RIGHT); + + container->ShowWidgetForExtension( + views::BubbleDialogDelegate::CreateBubble(std::move(bubble)), + extension_id); +}
diff --git a/chrome/browser/ui/views/extensions/extensions_dialogs_utils.h b/chrome/browser/ui/views/extensions/extensions_dialogs_utils.h index 39b394e..8552cf08 100644 --- a/chrome/browser/ui/views/extensions/extensions_dialogs_utils.h +++ b/chrome/browser/ui/views/extensions/extensions_dialogs_utils.h
@@ -6,18 +6,27 @@ #define CHROME_BROWSER_UI_VIEWS_EXTENSIONS_EXTENSIONS_DIALOGS_UTILS_H_ #include "content/public/browser/web_contents.h" +#include "extensions/common/extension_id.h" #include "ui/views/bubble/bubble_dialog_model_host.h" namespace content { class WebContents; } // namespace content +class Browser; class ToolbarActionViewController; +class ExtensionsToolbarContainer; std::unique_ptr<views::BubbleDialogModelHost::CustomView> CreateExtensionItem( const std::u16string& name, const ui::ImageModel& icon); +// Returns the extensions toolbar container in `browser` or `parent`, if +// existent. +ExtensionsToolbarContainer* GetExtensionsToolbarContainer(Browser* browser); +ExtensionsToolbarContainer* GetExtensionsToolbarContainer( + gfx::NativeWindow parent); + // Returns the icon corresponding to `action` for the given `web_contents`. ui::ImageModel GetIcon(ToolbarActionViewController* action, content::WebContents* web_contents); @@ -26,4 +35,23 @@ // web contents are present. std::u16string GetCurrentHost(content::WebContents* web_contents); +// Returns the action view corresponding to `extension_id` in `container`. If it +// doesn't exist, it defaults to the extensions menu button. +views::View* GetDialogAnchorView(ExtensionsToolbarContainer* container, + const extensions::ExtensionId& extension_id); + +// Shows the dialog constructed from `dialog_model` anchored to the view +// corresponding to `extension_id` in the extensions container. If parent does +// not have an extensions container, it will display a browser-modal dialog +// instead. +void ShowDialog(gfx::NativeWindow parent, + const extensions::ExtensionId& extension_id, + std::unique_ptr<ui::DialogModel> dialog_model); + +// Shows the dialog constructed from `dialog_model` anchored to the view +// corresponding to `extension_id` in `container`. +void ShowDialog(ExtensionsToolbarContainer* container, + const extensions::ExtensionId& extension_id, + std::unique_ptr<ui::DialogModel> dialog_model); + #endif // CHROME_BROWSER_UI_VIEWS_EXTENSIONS_EXTENSIONS_DIALOGS_UTILS_H_
diff --git a/chrome/browser/ui/views/extensions/extensions_request_access_button.cc b/chrome/browser/ui/views/extensions/extensions_request_access_button.cc index 8eaf1e1..587e824b 100644 --- a/chrome/browser/ui/views/extensions/extensions_request_access_button.cc +++ b/chrome/browser/ui/views/extensions/extensions_request_access_button.cc
@@ -8,12 +8,11 @@ #include "base/bind.h" #include "base/check_op.h" #include "chrome/browser/ui/browser.h" +#include "chrome/browser/ui/views/extensions/extensions_dialogs_utils.h" #include "chrome/browser/ui/views/extensions/extensions_request_access_button_hover_card.h" #include "chrome/browser/ui/views/extensions/extensions_request_access_dialog_view.h" #include "chrome/browser/ui/views/extensions/extensions_toolbar_button.h" #include "chrome/browser/ui/views/extensions/extensions_toolbar_container.h" -#include "chrome/browser/ui/views/frame/browser_view.h" -#include "chrome/browser/ui/views/frame/toolbar_button_provider.h" #include "chrome/grit/generated_resources.h" #include "content/public/browser/web_contents.h" #include "ui/base/l10n/l10n_util.h" @@ -57,14 +56,8 @@ } void ExtensionsRequestAccessButton::OnButtonPressed() { - // TODO(crbug.com/1322796): Multiple classes use this. We should pull getting - // an anchor view and showing a BubbleDialogDelegate into a common location. - BrowserView* const browser_view = - BrowserView::GetBrowserViewForBrowser(browser_); ExtensionsToolbarContainer* const container = - browser_view ? browser_view->toolbar_button_provider() - ->GetExtensionsToolbarContainer() - : nullptr; + GetExtensionsToolbarContainer(browser_); DCHECK(container); views::View* const anchor_view = container->GetExtensionsButton();
diff --git a/chrome/browser/ui/views/extensions/print_job_confirmation_dialog_view.cc b/chrome/browser/ui/views/extensions/print_job_confirmation_dialog_view.cc index 2f32c519..fa95c73 100644 --- a/chrome/browser/ui/views/extensions/print_job_confirmation_dialog_view.cc +++ b/chrome/browser/ui/views/extensions/print_job_confirmation_dialog_view.cc
@@ -7,10 +7,8 @@ #include "base/strings/utf_string_conversions.h" #include "chrome/browser/ui/views/chrome_layout_provider.h" #include "chrome/browser/ui/views/chrome_typography.h" +#include "chrome/browser/ui/views/extensions/extensions_dialogs_utils.h" #include "chrome/browser/ui/views/extensions/extensions_toolbar_container.h" -#include "chrome/browser/ui/views/frame/browser_view.h" -#include "chrome/browser/ui/views/frame/toolbar_button_provider.h" -#include "chrome/browser/ui/views/toolbar/toolbar_action_view.h" #include "chrome/grit/generated_resources.h" #include "components/constrained_window/constrained_window_views.h" #include "components/strings/grit/components_strings.h" @@ -29,44 +27,36 @@ // static void PrintJobConfirmationDialogView::Show( gfx::NativeWindow parent, - const std::string& extension_id, + const extensions::ExtensionId& extension_id, const std::u16string& extension_name, const gfx::ImageSkia& extension_icon, const std::u16string& print_job_title, const std::u16string& printer_name, base::OnceCallback<void(bool)> callback) { - // TODO(crbug.com/1322796): Multiple classes use this. We should pull getting - // an anchor view and showing a BubbleDialogDelegate into a common location. - - // We may want to show dialog even if there is no appropriate browser view, - // i.e. |parent| is null or kNullNativeWindow. In that case we use - // constrained_window::CreateBrowserModalDialogViews() (see below). - BrowserView* const browser_view = - parent ? BrowserView::GetBrowserViewForNativeWindow(parent) : nullptr; - ExtensionsToolbarContainer* const container = - browser_view ? browser_view->toolbar_button_provider() - ->GetExtensionsToolbarContainer() - : nullptr; - ToolbarActionView* anchor_view = - container ? container->GetViewForId(extension_id) : nullptr; - - auto* print_job_confirmation_dialog_view = new PrintJobConfirmationDialogView( + // TODO(crbug.com/1330637): Migrate PrintJobConfirmationDialogView to use + // DialogModel. Then, we can use ShowDialog(container, extension_id, + // dialog_model) helper method. + ExtensionsToolbarContainer* container = GetExtensionsToolbarContainer(parent); + views::View* anchor_view = + container ? GetDialogAnchorView(container, extension_id) : nullptr; + auto* dialog_view = new PrintJobConfirmationDialogView( anchor_view, extension_name, extension_icon, print_job_title, printer_name, std::move(callback)); - if (anchor_view) { - DCHECK(container); - views::Widget* const widget = views::BubbleDialogDelegateView::CreateBubble( - print_job_confirmation_dialog_view); + + if (container) { + views::Widget* const widget = + views::BubbleDialogDelegateView::CreateBubble(dialog_view); container->ShowWidgetForExtension(widget, extension_id); } else { - constrained_window::CreateBrowserModalDialogViews( - print_job_confirmation_dialog_view, parent) + // We may want to show dialog even if there is no appropriate browser view + // or container, i.e. `parent` is null or kNullNativeWindow. + constrained_window::CreateBrowserModalDialogViews(dialog_view, parent) ->Show(); } } PrintJobConfirmationDialogView::PrintJobConfirmationDialogView( - ToolbarActionView* anchor_view, + views::View* anchor_view, const std::u16string& extension_name, const gfx::ImageSkia& extension_icon, const std::u16string& print_job_title, @@ -134,7 +124,7 @@ namespace chrome { void ShowPrintJobConfirmationDialog(gfx::NativeWindow parent, - const std::string& extension_id, + const extensions::ExtensionId& extension_id, const std::u16string& extension_name, const gfx::ImageSkia& extension_icon, const std::u16string& print_job_title,
diff --git a/chrome/browser/ui/views/extensions/print_job_confirmation_dialog_view.h b/chrome/browser/ui/views/extensions/print_job_confirmation_dialog_view.h index a052f4a..ded18bf 100644 --- a/chrome/browser/ui/views/extensions/print_job_confirmation_dialog_view.h +++ b/chrome/browser/ui/views/extensions/print_job_confirmation_dialog_view.h
@@ -9,28 +9,27 @@ #include <string> #include "base/callback_forward.h" +#include "extensions/common/extension_id.h" #include "ui/base/metadata/metadata_header_macros.h" #include "ui/base/ui_base_types.h" #include "ui/gfx/image/image_skia.h" #include "ui/gfx/native_widget_types.h" #include "ui/views/bubble/bubble_dialog_delegate_view.h" -class ToolbarActionView; - // The dialog's view, owned by the views framework. class PrintJobConfirmationDialogView : public views::BubbleDialogDelegateView { public: METADATA_HEADER(PrintJobConfirmationDialogView); static void Show(gfx::NativeWindow parent, - const std::string& extension_id, + const extensions::ExtensionId& extension_id, const std::u16string& extension_name, const gfx::ImageSkia& extension_icon, const std::u16string& print_job_title, const std::u16string& printer_name, base::OnceCallback<void(bool)> callback); - PrintJobConfirmationDialogView(ToolbarActionView* anchor_view, + PrintJobConfirmationDialogView(views::View* anchor_view, const std::u16string& extension_name, const gfx::ImageSkia& extension_icon, const std::u16string& print_job_title,
diff --git a/chrome/browser/ui/views/frame/browser_desktop_window_tree_host_linux.cc b/chrome/browser/ui/views/frame/browser_desktop_window_tree_host_linux.cc index 63a4f4e..696e49d 100644 --- a/chrome/browser/ui/views/frame/browser_desktop_window_tree_host_linux.cc +++ b/chrome/browser/ui/views/frame/browser_desktop_window_tree_host_linux.cc
@@ -149,7 +149,6 @@ } void BrowserDesktopWindowTreeHostLinux::UpdateFrameHints() { -#if BUILDFLAG(IS_LINUX) auto* view = static_cast<BrowserFrameViewLinux*>( native_frame_->browser_frame()->GetFrameView()); auto* layout = view->layout(); @@ -235,7 +234,6 @@ } SizeConstraintsChanged(); -#endif } ////////////////////////////////////////////////////////////////////////////////
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_chromeos.cc b/chrome/browser/ui/views/frame/browser_non_client_frame_view_chromeos.cc index dd2fdf1..6c0fd65 100644 --- a/chrome/browser/ui/views/frame/browser_non_client_frame_view_chromeos.cc +++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_chromeos.cc
@@ -341,14 +341,15 @@ // window header by redirecting paints from its background to // BrowserNonClientFrameViewChromeOS. gfx::Rect client_bounds(bounds()); - if (browser_view()->IsFullscreen()) + if (chromeos::TabletState::Get()->InTabletMode() || + browser_view()->IsFullscreen() || browser_view()->IsMaximized()) { return client_bounds; + } // If there is a border, inset the client bounds to show the border when the - // browser window is not in fullscreen. - if (auto* border = GetBorder()) { + // browser window is not fullscreened, maximized or in tablet mode. + if (auto* border = GetBorder()) client_bounds.Inset(border->GetInsets()); - } return client_bounds; }
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_chromeos_browsertest.cc b/chrome/browser/ui/views/frame/browser_non_client_frame_view_chromeos_browsertest.cc index c187010b..1dc3986f 100644 --- a/chrome/browser/ui/views/frame/browser_non_client_frame_view_chromeos_browsertest.cc +++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_chromeos_browsertest.cc
@@ -396,7 +396,9 @@ BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser()); BrowserNonClientFrameViewChromeOS* frame_view = GetFrameViewChromeOS(browser_view); - EXPECT_EQ(0, frame_view->GetBoundsForClientView().y()); + const int expect_y = + frame_view->GetBorder() ? frame_view->GetBorder()->GetInsets().top() : 0; + EXPECT_EQ(expect_y, frame_view->GetBoundsForClientView().y()); Widget* widget = browser_view->GetWidget(); ASSERT_NO_FATAL_FAILURE( @@ -415,7 +417,9 @@ BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser()); BrowserNonClientFrameViewChromeOS* frame_view = GetFrameViewChromeOS(browser_view); - EXPECT_EQ(0, frame_view->GetBoundsForClientView().y()); + const int expect_y = + frame_view->GetBorder() ? frame_view->GetBorder()->GetInsets().top() : 0; + EXPECT_EQ(expect_y, frame_view->GetBoundsForClientView().y()); Widget* widget = browser_view->GetWidget(); ASSERT_NO_FATAL_FAILURE(
diff --git a/chrome/browser/ui/views/location_bar/permission_request_chip.cc b/chrome/browser/ui/views/location_bar/permission_request_chip.cc index 45c95d8a..84575e7 100644 --- a/chrome/browser/ui/views/location_bar/permission_request_chip.cc +++ b/chrome/browser/ui/views/location_bar/permission_request_chip.cc
@@ -113,9 +113,6 @@ views::View* PermissionRequestChip::CreateBubble() { prompt_bubble_ = new PermissionPromptBubbleView( browser_, delegate(), chip_shown_time_, PermissionPromptStyle::kChip); - prompt_bubble_->SetOnBubbleDismissedByUserCallback(base::BindOnce( - &PermissionRequestChip::OnPromptBubbleDismissed, base::Unretained(this))); - RecordChipButtonPressed(); return prompt_bubble_; @@ -128,6 +125,17 @@ } } +void PermissionRequestChip::OnWidgetDestroying(views::Widget* widget) { + DCHECK_EQ(prompt_bubble_->GetWidget(), widget); + prompt_bubble_ = nullptr; + if (widget->closed_reason() == views::Widget::ClosedReason::kEscKeyPressed || + widget->closed_reason() == + views::Widget::ClosedReason::kCloseButtonClicked) { + OnPromptBubbleDismissed(); + } + PermissionChip::OnWidgetDestroying(widget); +} + void PermissionRequestChip::RecordChipButtonPressed() { base::UmaHistogramMediumTimes("Permissions.Chip.TimeToInteraction", base::TimeTicks::Now() - chip_shown_time_);
diff --git a/chrome/browser/ui/views/location_bar/permission_request_chip.h b/chrome/browser/ui/views/location_bar/permission_request_chip.h index 6570817..9a6322b 100644 --- a/chrome/browser/ui/views/location_bar/permission_request_chip.h +++ b/chrome/browser/ui/views/location_bar/permission_request_chip.h
@@ -32,6 +32,7 @@ private: // PermissionChip: views::View* CreateBubble() override; + void OnWidgetDestroying(views::Widget* widget) override; void ShowBubble() override; void RecordChipButtonPressed();
diff --git a/chrome/browser/ui/views/omnibox/omnibox_view_views.cc b/chrome/browser/ui/views/omnibox/omnibox_view_views.cc index 7ca95e0b..bb0c0409 100644 --- a/chrome/browser/ui/views/omnibox/omnibox_view_views.cc +++ b/chrome/browser/ui/views/omnibox/omnibox_view_views.cc
@@ -667,7 +667,8 @@ // fakebox is hidden and there's only whitespace in the omnibox, it's // difficult for the user to see that the focus moved to the omnibox. (model()->focus_state() == OMNIBOX_FOCUS_INVISIBLE && - std::all_of(text.begin(), text.end(), base::IsUnicodeWhitespace))) { + std::all_of(text.begin(), text.end(), + base::IsUnicodeWhitespace<char16_t>))) { return; }
diff --git a/chrome/browser/ui/views/passwords/password_bubble_view_base.cc b/chrome/browser/ui/views/passwords/password_bubble_view_base.cc index 7070060..5770fba 100644 --- a/chrome/browser/ui/views/passwords/password_bubble_view_base.cc +++ b/chrome/browser/ui/views/passwords/password_bubble_view_base.cc
@@ -108,8 +108,19 @@ // static void PasswordBubbleViewBase::CloseCurrentBubble() { - if (g_manage_passwords_bubble_) + if (g_manage_passwords_bubble_) { + // It can be the case that a password bubble is being closed while another + // password bubble is being opened. The metrics recorder can be shared + // between them and it doesn't understand the sequence [open1, open2, + // close1, close2]. Therefore, we reset the model early (before the bubble + // destructor) to get the following sequence of events [open1, close1, + // open2, close2]. + PasswordBubbleControllerBase* controller = + g_manage_passwords_bubble_->GetController(); + DCHECK(controller); + controller->OnBubbleClosing(); g_manage_passwords_bubble_->GetWidget()->Close(); + } } // static @@ -200,18 +211,3 @@ SetTitle(controller->GetTitle()); SetShowTitle(!controller->GetTitle().empty()); } - -void PasswordBubbleViewBase::OnWidgetClosing(views::Widget* widget) { - LocationBarBubbleDelegateView::OnWidgetClosing(widget); - if (widget != GetWidget()) - return; - // It can be the case that a password bubble is being closed while another - // password bubble is being opened. The metrics recorder can be shared - // between them and it doesn't understand the sequence [open1, open2, - // close1, close2]. Therefore, we reset the model early (before the bubble - // destructor) to get the following sequence of events [open1, close1, - // open2, close2]. - PasswordBubbleControllerBase* controller = GetController(); - DCHECK(controller); - controller->OnBubbleClosing(); -}
diff --git a/chrome/browser/ui/views/passwords/password_bubble_view_base.h b/chrome/browser/ui/views/passwords/password_bubble_view_base.h index 27663346..0a37831 100644 --- a/chrome/browser/ui/views/passwords/password_bubble_view_base.h +++ b/chrome/browser/ui/views/passwords/password_bubble_view_base.h
@@ -90,9 +90,6 @@ // views::BubbleDialogDelegateView: void Init() override; - // WidgetObserver: - void OnWidgetClosing(views::Widget* widget) override; - // Singleton instance of the Password bubble.The instance is owned by the // Bubble and will be deleted when the bubble closes. static PasswordBubbleViewBase* g_manage_passwords_bubble_;
diff --git a/chrome/browser/ui/views/permission_bubble/permission_prompt_bubble_view.cc b/chrome/browser/ui/views/permission_bubble/permission_prompt_bubble_view.cc index a3cb4ef..49e498e9 100644 --- a/chrome/browser/ui/views/permission_bubble/permission_prompt_bubble_view.cc +++ b/chrome/browser/ui/views/permission_bubble/permission_prompt_bubble_view.cc
@@ -274,16 +274,6 @@ return l10n_util::GetStringFUTF16(message_id, GetDisplayName()); } -void PermissionPromptBubbleView::OnWidgetDestroying(views::Widget* widget) { - if (on_bubble_dismissed_by_user_callback_ && - (widget->closed_reason() == views::Widget::ClosedReason::kEscKeyPressed || - widget->closed_reason() == - views::Widget::ClosedReason::kCloseButtonClicked)) { - std::move(on_bubble_dismissed_by_user_callback_).Run(); - } - BubbleDialogDelegateView::OnWidgetDestroying(widget); -} - std::u16string PermissionPromptBubbleView::GetAccessibleWindowTitle() const { // Generate one of: // $origin wants to: $permission
diff --git a/chrome/browser/ui/views/permission_bubble/permission_prompt_bubble_view.h b/chrome/browser/ui/views/permission_bubble/permission_prompt_bubble_view.h index 0885683..da87329b 100644 --- a/chrome/browser/ui/views/permission_bubble/permission_prompt_bubble_view.h +++ b/chrome/browser/ui/views/permission_bubble/permission_prompt_bubble_view.h
@@ -51,16 +51,12 @@ void UpdateAnchorPosition(); void SetPromptStyle(PermissionPromptStyle prompt_style); - void SetOnBubbleDismissedByUserCallback(base::OnceClosure callback) { - on_bubble_dismissed_by_user_callback_ = std::move(callback); - } // views::BubbleDialogDelegateView: void AddedToWidget() override; bool ShouldShowCloseButton() const override; std::u16string GetAccessibleWindowTitle() const override; std::u16string GetWindowTitle() const override; - void OnWidgetDestroying(views::Widget* widget) override; void AcceptPermission(); void AcceptPermissionThisTime(); @@ -98,8 +94,6 @@ base::TimeTicks permission_requested_time_; PermissionPromptStyle prompt_style_; - - base::OnceClosure on_bubble_dismissed_by_user_callback_; }; #endif // CHROME_BROWSER_UI_VIEWS_PERMISSION_BUBBLE_PERMISSION_PROMPT_BUBBLE_VIEW_H_
diff --git a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc index 02b141b1..5e447a1 100644 --- a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc +++ b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
@@ -265,6 +265,7 @@ #include "chrome/browser/ui/webui/chromeos/multidevice_internals/multidevice_internals_ui.h" #include "chrome/browser/ui/webui/chromeos/multidevice_setup/multidevice_setup_dialog.h" #include "chrome/browser/ui/webui/chromeos/network_ui.h" +#include "chrome/browser/ui/webui/chromeos/notification_tester/notification_tester_ui.h" #include "chrome/browser/ui/webui/chromeos/parent_access/parent_access_ui.h" #include "chrome/browser/ui/webui/chromeos/power_ui.h" #include "chrome/browser/ui/webui/chromeos/set_time_ui.h" @@ -938,6 +939,8 @@ return &NewComponentUI<ash::file_manager::FileManagerUI, ChromeFileManagerUIDelegate>; } + if (url.host_piece() == chrome::kChromeUINotificationTesterHost) + return &NewWebUI<chromeos::NotificationTesterUI>; if (url.host_piece() == chrome::kChromeUIAccountManagerErrorHost) return &NewWebUI<chromeos::AccountManagerErrorUI>; if (url.host_piece() == chrome::kChromeUIAccountMigrationWelcomeHost)
diff --git a/chrome/browser/ui/webui/chromeos/login/recommend_apps_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/recommend_apps_screen_handler.cc index c34281e0..eeb3e365 100644 --- a/chrome/browser/ui/webui/chromeos/login/recommend_apps_screen_handler.cc +++ b/chrome/browser/ui/webui/chromeos/login/recommend_apps_screen_handler.cc
@@ -13,6 +13,7 @@ #include "chrome/browser/profiles/profile.h" #include "chrome/browser/profiles/profile_manager.h" #include "chrome/browser/ui/app_list/arc/arc_fast_app_reinstall_starter.h" +#include "chrome/common/chrome_features.h" #include "chrome/grit/component_extension_resources.h" #include "chrome/grit/generated_resources.h" #include "components/login/localized_values_builder.h" @@ -165,15 +166,16 @@ void RecommendAppsScreenHandler::LoadAppListInUI(base::Value app_list) { RecordUmaScreenState(RecommendAppsScreenState::SHOW); - const ui::ResourceBundle& resource_bundle = - ui::ResourceBundle::GetSharedInstance(); // TODO(crbug.com/1261902): Clean-up old implementation once feature is // launched. - std::string app_list_webview = resource_bundle.LoadDataResourceString( - features::IsOobeNewRecommendAppsEnabled() - ? IDR_ARC_SUPPORT_RECOMMEND_APP_LIST_VIEW_HTML - : IDR_ARC_SUPPORT_RECOMMEND_APP_OLD_LIST_VIEW_HTML); - CallJS("login.RecommendAppsScreen.setWebview", app_list_webview); + if (!features::IsOobeNewRecommendAppsEnabled() || + !base::FeatureList::IsEnabled(::features::kAppDiscoveryForOobe)) { + const ui::ResourceBundle& resource_bundle = + ui::ResourceBundle::GetSharedInstance(); + std::string app_list_webview = resource_bundle.LoadDataResourceString( + IDR_ARC_SUPPORT_RECOMMEND_APP_OLD_LIST_VIEW_HTML); + CallJS("login.RecommendAppsScreen.setWebview", app_list_webview); + } CallJS("login.RecommendAppsScreen.loadAppList", std::move(app_list)); }
diff --git a/chrome/browser/ui/webui/chromeos/notification_tester/notification_tester_ui.cc b/chrome/browser/ui/webui/chromeos/notification_tester/notification_tester_ui.cc new file mode 100644 index 0000000..fd439ab6 --- /dev/null +++ b/chrome/browser/ui/webui/chromeos/notification_tester/notification_tester_ui.cc
@@ -0,0 +1,28 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/ui/webui/chromeos/notification_tester/notification_tester_ui.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/common/url_constants.h" +#include "chrome/grit/browser_resources.h" +#include "chrome/grit/generated_resources.h" +#include "content/public/browser/web_ui_data_source.h" + +namespace chromeos { + +NotificationTesterUI::NotificationTesterUI(content::WebUI* web_ui) + : content::WebUIController(web_ui) { + // Set up the chrome://notification-tester source. + content::WebUIDataSource* html_source = + content::WebUIDataSource::Create(chrome::kChromeUINotificationTesterHost); + + // Add required resources. + html_source->SetDefaultResource(IDR_NOTIFICATION_TESTER_HTML); + Profile* profile = Profile::FromWebUI(web_ui); + content::WebUIDataSource::Add(profile, html_source); +} + +NotificationTesterUI::~NotificationTesterUI() = default; + +} // namespace chromeos
diff --git a/chrome/browser/ui/webui/chromeos/notification_tester/notification_tester_ui.h b/chrome/browser/ui/webui/chromeos/notification_tester/notification_tester_ui.h new file mode 100644 index 0000000..254370f --- /dev/null +++ b/chrome/browser/ui/webui/chromeos/notification_tester/notification_tester_ui.h
@@ -0,0 +1,23 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_UI_WEBUI_CHROMEOS_NOTIFICATION_TESTER_NOTIFICATION_TESTER_UI_H_ +#define CHROME_BROWSER_UI_WEBUI_CHROMEOS_NOTIFICATION_TESTER_NOTIFICATION_TESTER_UI_H_ + +#include "content/public/browser/web_ui_controller.h" + +namespace chromeos { + +// The UI controller for NotificationTester page. +class NotificationTesterUI : public content::WebUIController { + public: + explicit NotificationTesterUI(content::WebUI* web_ui); + NotificationTesterUI(const NotificationTesterUI&) = delete; + NotificationTesterUI& operator=(const NotificationTesterUI&) = delete; + ~NotificationTesterUI() override; +}; + +} // namespace chromeos + +#endif // CHROME_BROWSER_UI_WEBUI_CHROMEOS_NOTIFICATION_TESTER_NOTIFICATION_TESTER_UI_H_
diff --git a/chrome/browser/ui/webui/policy/policy_ui_handler.cc b/chrome/browser/ui/webui/policy/policy_ui_handler.cc index 3bc0789..f75f18d8 100644 --- a/chrome/browser/ui/webui/policy/policy_ui_handler.cc +++ b/chrome/browser/ui/webui/policy/policy_ui_handler.cc
@@ -38,6 +38,9 @@ #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/chrome_select_file_policy.h" #include "chrome/browser/ui/managed_ui.h" +#include "chrome/browser/ui/webui/policy/status_provider/cloud_policy_core_status_provider.h" +#include "chrome/browser/ui/webui/policy/status_provider/status_provider_util.h" +#include "chrome/browser/ui/webui/policy/status_provider/user_cloud_policy_status_provider.h" #include "chrome/browser/ui/webui/webui_util.h" #include "chrome/grit/chromium_strings.h" #include "components/enterprise/browser/controller/browser_dm_token_storage.h" @@ -91,6 +94,7 @@ #include "chrome/browser/ash/policy/off_hours/device_off_hours_controller.h" #include "chrome/browser/ash/profiles/profile_helper.h" #include "chrome/browser/browser_process_platform_part.h" +#include "chrome/browser/ui/webui/policy/status_provider/device_cloud_policy_status_provider_chromeos.h" #include "components/user_manager/user_manager.h" #else #include "components/policy/core/common/cloud/user_cloud_policy_manager.h" @@ -122,122 +126,6 @@ namespace em = enterprise_management; -namespace { - -// Adds a new entry to |dict| with the affiliation status of the user associated -// with |profile|. This method shouldn't be called for device scope status. -void GetUserAffiliationStatus(base::DictionaryValue* dict, Profile* profile) { - CHECK(profile); - -#if BUILDFLAG(IS_CHROMEOS_ASH) - const user_manager::User* user = - ash::ProfileHelper::Get()->GetUserByProfile(profile); - if (!user) - return; - dict->SetBoolKey("isAffiliated", user->IsAffiliated()); -#else - // Don't show affiliation status if the browser isn't enrolled in CBCM. -#if BUILDFLAG(IS_CHROMEOS_LACROS) - if (!profile->IsMainProfile()) -#endif // BUILDFLAG(IS_CHROMEOS_LACROS) - { - if (!policy::BrowserDMTokenStorage::Get()->RetrieveDMToken().is_valid()) - return; - } - dict->SetBoolKey("isAffiliated", - chrome::enterprise_util::IsProfileAffiliated(profile)); -#endif // BUILDFLAG(IS_CHROMEOS_ASH) -} - -#if BUILDFLAG(IS_CHROMEOS_ASH) -void GetOffHoursStatus(base::DictionaryValue* dict) { - policy::off_hours::DeviceOffHoursController* off_hours_controller = - ash::DeviceSettingsService::Get()->device_off_hours_controller(); - if (off_hours_controller) { - dict->SetBoolKey("isOffHoursActive", - off_hours_controller->is_off_hours_mode()); - } -} - -// Adds a new entry to |dict| with the enterprise domain manager of the user -// associated with |profile|. This method shouldn't be called for device scope -// status. -void GetUserManager(base::DictionaryValue* dict, Profile* profile) { - CHECK(profile); - - absl::optional<std::string> account_manager = - chrome::GetAccountManagerIdentity(profile); - if (account_manager) { - dict->SetStringKey("enterpriseDomainManager", *account_manager); - } -} -#endif // BUILDFLAG(IS_CHROMEOS_ASH) - -void ExtractDomainFromUsername(base::DictionaryValue* dict) { - const std::string* username = dict->FindStringKey("username"); - if (username && !username->empty()) - dict->SetStringKey("domain", gaia::ExtractDomainName(*username)); -} - -// MachineStatus box labels itself as `machine policies` on desktop. In the -// domain of mobile devices such as iOS or Android we want to label this box as -// `device policies`. This is a helper function that retrieves the expected -// labelKey -std::string GetMachineStatusLegendKey() { -#if BUILDFLAG(IS_ANDROID) - return "statusDevice"; -#else - return "statusMachine"; -#endif // BUILDFLAG(IS_ANDROID) -} - -} // namespace - -// Status provider implementation that pulls cloud policy status from a -// CloudPolicyCore instance provided at construction time. Also listens for -// changes on that CloudPolicyCore and reports them through the status change -// callback. -class CloudPolicyCoreStatusProvider - : public policy::PolicyStatusProvider, - public policy::CloudPolicyStore::Observer { - public: - explicit CloudPolicyCoreStatusProvider(policy::CloudPolicyCore* core); - - CloudPolicyCoreStatusProvider(const CloudPolicyCoreStatusProvider&) = delete; - CloudPolicyCoreStatusProvider& operator=( - const CloudPolicyCoreStatusProvider&) = delete; - - ~CloudPolicyCoreStatusProvider() override; - - // policy::CloudPolicyStore::Observer implementation. - void OnStoreLoaded(policy::CloudPolicyStore* store) override; - void OnStoreError(policy::CloudPolicyStore* store) override; - - protected: - // Policy status is read from the CloudPolicyClient, CloudPolicyStore and - // CloudPolicyRefreshScheduler hosted by this |core_|. - raw_ptr<policy::CloudPolicyCore> core_; -}; - -// A cloud policy status provider for user policy. -class UserCloudPolicyStatusProvider : public CloudPolicyCoreStatusProvider { - public: - explicit UserCloudPolicyStatusProvider(policy::CloudPolicyCore* core, - Profile* profile); - - UserCloudPolicyStatusProvider(const UserCloudPolicyStatusProvider&) = delete; - UserCloudPolicyStatusProvider& operator=( - const UserCloudPolicyStatusProvider&) = delete; - - ~UserCloudPolicyStatusProvider() override; - - // CloudPolicyCoreStatusProvider implementation. - void GetStatus(base::DictionaryValue* dict) override; - - private: - raw_ptr<Profile> profile_; -}; - #if BUILDFLAG(IS_CHROMEOS_LACROS) // A cloud policy status provider for device account. class UserPolicyStatusProviderLacros : public policy::PolicyStatusProvider { @@ -285,27 +173,6 @@ #endif // BUILDFLAG(IS_CHROMEOS_ASH) #if BUILDFLAG(IS_CHROMEOS_ASH) -// A cloud policy status provider for device policy. -class DeviceCloudPolicyStatusProviderChromeOS - : public CloudPolicyCoreStatusProvider { - public: - explicit DeviceCloudPolicyStatusProviderChromeOS( - policy::BrowserPolicyConnectorAsh* connector); - - DeviceCloudPolicyStatusProviderChromeOS( - const DeviceCloudPolicyStatusProviderChromeOS&) = delete; - DeviceCloudPolicyStatusProviderChromeOS& operator=( - const DeviceCloudPolicyStatusProviderChromeOS&) = delete; - - ~DeviceCloudPolicyStatusProviderChromeOS() override; - - // CloudPolicyCoreStatusProvider implementation. - void GetStatus(base::DictionaryValue* dict) override; - - private: - std::string enterprise_domain_manager_; -}; - // A cloud policy status provider that reads policy status from the policy core // associated with the device-local account specified by |user_id| at // construction time. The indirection via user ID and @@ -408,44 +275,6 @@ }; #endif -CloudPolicyCoreStatusProvider::CloudPolicyCoreStatusProvider( - policy::CloudPolicyCore* core) - : core_(core) { - core_->store()->AddObserver(this); - // TODO(bartfab): Add an observer that watches for client errors. Observing - // core_->client() directly is not safe as the client may be destroyed and - // (re-)created anytime if the user signs in or out on desktop platforms. -} - -CloudPolicyCoreStatusProvider::~CloudPolicyCoreStatusProvider() { - core_->store()->RemoveObserver(this); -} - -void CloudPolicyCoreStatusProvider::OnStoreLoaded( - policy::CloudPolicyStore* store) { - NotifyStatusChange(); -} - -void CloudPolicyCoreStatusProvider::OnStoreError( - policy::CloudPolicyStore* store) { - NotifyStatusChange(); -} - -UserCloudPolicyStatusProvider::UserCloudPolicyStatusProvider( - policy::CloudPolicyCore* core, - Profile* profile) - : CloudPolicyCoreStatusProvider(core), profile_(profile) {} - -UserCloudPolicyStatusProvider::~UserCloudPolicyStatusProvider() = default; - -void UserCloudPolicyStatusProvider::GetStatus(base::DictionaryValue* dict) { - if (!core_->store()->is_managed()) - return; - policy::PolicyStatusProvider::GetStatusFromCore(core_, dict); - ExtractDomainFromUsername(dict); - GetUserAffiliationStatus(dict, profile_); -} - #if BUILDFLAG(IS_CHROMEOS_LACROS) UserPolicyStatusProviderLacros::UserPolicyStatusProviderLacros( policy::PolicyLoaderLacros* loader, @@ -506,24 +335,6 @@ #endif // BUILDFLAG(IS_CHROMEOS_ASH) #if BUILDFLAG(IS_CHROMEOS_ASH) -DeviceCloudPolicyStatusProviderChromeOS:: - DeviceCloudPolicyStatusProviderChromeOS( - policy::BrowserPolicyConnectorAsh* connector) - : CloudPolicyCoreStatusProvider( - connector->GetDeviceCloudPolicyManager()->core()) { - enterprise_domain_manager_ = connector->GetEnterpriseDomainManager(); -} - -DeviceCloudPolicyStatusProviderChromeOS:: - ~DeviceCloudPolicyStatusProviderChromeOS() = default; - -void DeviceCloudPolicyStatusProviderChromeOS::GetStatus( - base::DictionaryValue* dict) { - policy::PolicyStatusProvider::GetStatusFromCore(core_, dict); - dict->SetStringKey("enterpriseDomainManager", enterprise_domain_manager_); - GetOffHoursStatus(dict); -} - DeviceLocalAccountPolicyStatusProvider::DeviceLocalAccountPolicyStatusProvider( const std::string& user_id, policy::DeviceLocalAccountPolicyService* service)
diff --git a/chrome/browser/ui/webui/policy/status_provider/cloud_policy_core_status_provider.cc b/chrome/browser/ui/webui/policy/status_provider/cloud_policy_core_status_provider.cc new file mode 100644 index 0000000..11cff2f --- /dev/null +++ b/chrome/browser/ui/webui/policy/status_provider/cloud_policy_core_status_provider.cc
@@ -0,0 +1,30 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/ui/webui/policy/status_provider/cloud_policy_core_status_provider.h" + +#include "components/policy/core/common/cloud/cloud_policy_core.h" + +CloudPolicyCoreStatusProvider::CloudPolicyCoreStatusProvider( + policy::CloudPolicyCore* core) + : core_(core) { + core_->store()->AddObserver(this); + // TODO(bartfab): Add an observer that watches for client errors. Observing + // core_->client() directly is not safe as the client may be destroyed and + // (re-)created anytime if the user signs in or out on desktop platforms. +} + +CloudPolicyCoreStatusProvider::~CloudPolicyCoreStatusProvider() { + core_->store()->RemoveObserver(this); +} + +void CloudPolicyCoreStatusProvider::OnStoreLoaded( + policy::CloudPolicyStore* store) { + NotifyStatusChange(); +} + +void CloudPolicyCoreStatusProvider::OnStoreError( + policy::CloudPolicyStore* store) { + NotifyStatusChange(); +}
diff --git a/chrome/browser/ui/webui/policy/status_provider/cloud_policy_core_status_provider.h b/chrome/browser/ui/webui/policy/status_provider/cloud_policy_core_status_provider.h new file mode 100644 index 0000000..9683be70 --- /dev/null +++ b/chrome/browser/ui/webui/policy/status_provider/cloud_policy_core_status_provider.h
@@ -0,0 +1,41 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_UI_WEBUI_POLICY_STATUS_PROVIDER_CLOUD_POLICY_CORE_STATUS_PROVIDER_H_ +#define CHROME_BROWSER_UI_WEBUI_POLICY_STATUS_PROVIDER_CLOUD_POLICY_CORE_STATUS_PROVIDER_H_ + +#include "components/policy/core/browser/webui/policy_status_provider.h" +#include "components/policy/core/common/cloud/cloud_policy_store.h" + +namespace policy { +class CloudPolicyCore; +} // namespace policy + +// Status provider implementation that pulls cloud policy status from a +// CloudPolicyCore instance provided at construction time. Also listens for +// changes on that CloudPolicyCore and reports them through the status change +// callback. +class CloudPolicyCoreStatusProvider + : public policy::PolicyStatusProvider, + public policy::CloudPolicyStore::Observer { + public: + explicit CloudPolicyCoreStatusProvider(policy::CloudPolicyCore* core); + + CloudPolicyCoreStatusProvider(const CloudPolicyCoreStatusProvider&) = delete; + CloudPolicyCoreStatusProvider& operator=( + const CloudPolicyCoreStatusProvider&) = delete; + + ~CloudPolicyCoreStatusProvider() override; + + // policy::CloudPolicyStore::Observer implementation. + void OnStoreLoaded(policy::CloudPolicyStore* store) override; + void OnStoreError(policy::CloudPolicyStore* store) override; + + protected: + // Policy status is read from the CloudPolicyClient, CloudPolicyStore and + // CloudPolicyRefreshScheduler hosted by this |core_|. + raw_ptr<policy::CloudPolicyCore> core_; +}; + +#endif // CHROME_BROWSER_UI_WEBUI_POLICY_STATUS_PROVIDER_CLOUD_POLICY_CORE_STATUS_PROVIDER_H_
diff --git a/chrome/browser/ui/webui/policy/status_provider/device_cloud_policy_status_provider_chromeos.cc b/chrome/browser/ui/webui/policy/status_provider/device_cloud_policy_status_provider_chromeos.cc new file mode 100644 index 0000000..9cdd72c --- /dev/null +++ b/chrome/browser/ui/webui/policy/status_provider/device_cloud_policy_status_provider_chromeos.cc
@@ -0,0 +1,29 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/ui/webui/policy/status_provider/device_cloud_policy_status_provider_chromeos.h" + +#include <string> + +#include "base/values.h" +#include "chrome/browser/ash/policy/core/browser_policy_connector_ash.h" +#include "chrome/browser/ui/webui/policy/status_provider/status_provider_util.h" + +DeviceCloudPolicyStatusProviderChromeOS:: + DeviceCloudPolicyStatusProviderChromeOS( + policy::BrowserPolicyConnectorAsh* connector) + : CloudPolicyCoreStatusProvider( + connector->GetDeviceCloudPolicyManager()->core()) { + enterprise_domain_manager_ = connector->GetEnterpriseDomainManager(); +} + +DeviceCloudPolicyStatusProviderChromeOS:: + ~DeviceCloudPolicyStatusProviderChromeOS() = default; + +void DeviceCloudPolicyStatusProviderChromeOS::GetStatus( + base::DictionaryValue* dict) { + policy::PolicyStatusProvider::GetStatusFromCore(core_, dict); + dict->SetStringKey("enterpriseDomainManager", enterprise_domain_manager_); + GetOffHoursStatus(dict); +}
diff --git a/chrome/browser/ui/webui/policy/status_provider/device_cloud_policy_status_provider_chromeos.h b/chrome/browser/ui/webui/policy/status_provider/device_cloud_policy_status_provider_chromeos.h new file mode 100644 index 0000000..53cfaae --- /dev/null +++ b/chrome/browser/ui/webui/policy/status_provider/device_cloud_policy_status_provider_chromeos.h
@@ -0,0 +1,39 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_UI_WEBUI_POLICY_STATUS_PROVIDER_DEVICE_CLOUD_POLICY_STATUS_PROVIDER_CHROMEOS_H_ +#define CHROME_BROWSER_UI_WEBUI_POLICY_STATUS_PROVIDER_DEVICE_CLOUD_POLICY_STATUS_PROVIDER_CHROMEOS_H_ + +#include "chrome/browser/ui/webui/policy/status_provider/cloud_policy_core_status_provider.h" + +namespace base { +class DictionaryValue; +} // namespace base + +namespace policy { +class BrowserPolicyConnectorAsh; +} // namespace policy + +// A cloud policy status provider for device policy. +class DeviceCloudPolicyStatusProviderChromeOS + : public CloudPolicyCoreStatusProvider { + public: + explicit DeviceCloudPolicyStatusProviderChromeOS( + policy::BrowserPolicyConnectorAsh* connector); + + DeviceCloudPolicyStatusProviderChromeOS( + const DeviceCloudPolicyStatusProviderChromeOS&) = delete; + DeviceCloudPolicyStatusProviderChromeOS& operator=( + const DeviceCloudPolicyStatusProviderChromeOS&) = delete; + + ~DeviceCloudPolicyStatusProviderChromeOS() override; + + // CloudPolicyCoreStatusProvider implementation. + void GetStatus(base::DictionaryValue* dict) override; + + private: + std::string enterprise_domain_manager_; +}; + +#endif // CHROME_BROWSER_UI_WEBUI_POLICY_STATUS_PROVIDER_DEVICE_CLOUD_POLICY_STATUS_PROVIDER_CHROMEOS_H_
diff --git a/chrome/browser/ui/webui/policy/status_provider/status_provider_util.cc b/chrome/browser/ui/webui/policy/status_provider/status_provider_util.cc new file mode 100644 index 0000000..072d99f --- /dev/null +++ b/chrome/browser/ui/webui/policy/status_provider/status_provider_util.cc
@@ -0,0 +1,75 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/ui/webui/policy/status_provider/status_provider_util.h" + +#include "google_apis/gaia/gaia_auth_util.h" +#if BUILDFLAG(IS_CHROMEOS_ASH) +#include "chrome/browser/ash/policy/off_hours/device_off_hours_controller.h" +#include "chrome/browser/ash/profiles/profile_helper.h" +#include "chrome/browser/ash/settings/device_settings_service.h" +#include "chrome/browser/ui/managed_ui.h" +#include "components/user_manager/user_manager.h" +#else +#include "chrome/browser/enterprise/util/affiliation.h" +#include "components/enterprise/browser/controller/browser_dm_token_storage.h" +#endif + +void ExtractDomainFromUsername(base::DictionaryValue* dict) { + const std::string* username = dict->FindStringKey("username"); + if (username && !username->empty()) + dict->SetStringKey("domain", gaia::ExtractDomainName(*username)); +} + +void GetUserAffiliationStatus(base::DictionaryValue* dict, Profile* profile) { + CHECK(profile); + +#if BUILDFLAG(IS_CHROMEOS_ASH) + const user_manager::User* user = + ash::ProfileHelper::Get()->GetUserByProfile(profile); + if (!user) + return; + dict->SetBoolKey("isAffiliated", user->IsAffiliated()); +#else + // Don't show affiliation status if the browser isn't enrolled in CBCM. +#if BUILDFLAG(IS_CHROMEOS_LACROS) + if (!profile->IsMainProfile()) +#endif // BUILDFLAG(IS_CHROMEOS_LACROS) + { + if (!policy::BrowserDMTokenStorage::Get()->RetrieveDMToken().is_valid()) + return; + } + dict->SetBoolKey("isAffiliated", + chrome::enterprise_util::IsProfileAffiliated(profile)); +#endif // BUILDFLAG(IS_CHROMEOS_ASH) +} + +#if BUILDFLAG(IS_CHROMEOS_ASH) +void GetOffHoursStatus(base::DictionaryValue* dict) { + policy::off_hours::DeviceOffHoursController* off_hours_controller = + ash::DeviceSettingsService::Get()->device_off_hours_controller(); + if (off_hours_controller) { + dict->SetBoolKey("isOffHoursActive", + off_hours_controller->is_off_hours_mode()); + } +} + +void GetUserManager(base::DictionaryValue* dict, Profile* profile) { + CHECK(profile); + + absl::optional<std::string> account_manager = + chrome::GetAccountManagerIdentity(profile); + if (account_manager) { + dict->SetStringKey("enterpriseDomainManager", *account_manager); + } +} +#endif // BUILDFLAG(IS_CHROMEOS_ASH) + +std::string GetMachineStatusLegendKey() { +#if BUILDFLAG(IS_ANDROID) + return "statusDevice"; +#else + return "statusMachine"; +#endif // BUILDFLAG(IS_ANDROID) +}
diff --git a/chrome/browser/ui/webui/policy/status_provider/status_provider_util.h b/chrome/browser/ui/webui/policy/status_provider/status_provider_util.h new file mode 100644 index 0000000..4078f7e --- /dev/null +++ b/chrome/browser/ui/webui/policy/status_provider/status_provider_util.h
@@ -0,0 +1,32 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_UI_WEBUI_POLICY_STATUS_PROVIDER_STATUS_PROVIDER_UTIL_H_ +#define CHROME_BROWSER_UI_WEBUI_POLICY_STATUS_PROVIDER_STATUS_PROVIDER_UTIL_H_ + +#include "base/values.h" +#include "chrome/browser/profiles/profile.h" + +void ExtractDomainFromUsername(base::DictionaryValue* dict); + +// Adds a new entry to |dict| with the affiliation status of the user associated +// with |profile|. This method shouldn't be called for device scope status. +void GetUserAffiliationStatus(base::DictionaryValue* dict, Profile* profile); + +// MachineStatus box labels itself as `machine policies` on desktop. In the +// domain of mobile devices such as iOS or Android we want to label this box as +// `device policies`. This is a helper function that retrieves the expected +// labelKey +std::string GetMachineStatusLegendKey(); + +#if BUILDFLAG(IS_CHROMEOS_ASH) +void GetOffHoursStatus(base::DictionaryValue* dict); + +// Adds a new entry to |dict| with the enterprise domain manager of the user +// associated with |profile|. This method shouldn't be called for device scope +// status. +void GetUserManager(base::DictionaryValue* dict, Profile* profile); +#endif // BUILDFLAG(IS_CHROMEOS_ASH) + +#endif // CHROME_BROWSER_UI_WEBUI_POLICY_STATUS_PROVIDER_STATUS_PROVIDER_UTIL_H_
diff --git a/chrome/browser/ui/webui/policy/status_provider/user_cloud_policy_status_provider.cc b/chrome/browser/ui/webui/policy/status_provider/user_cloud_policy_status_provider.cc new file mode 100644 index 0000000..7153e46 --- /dev/null +++ b/chrome/browser/ui/webui/policy/status_provider/user_cloud_policy_status_provider.cc
@@ -0,0 +1,26 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/ui/webui/policy/status_provider/user_cloud_policy_status_provider.h" + +#include "base/values.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/ui/webui/policy/status_provider/status_provider_util.h" +#include "components/policy/core/common/cloud/cloud_policy_core.h" +#include "components/policy/core/common/cloud/cloud_policy_store.h" + +UserCloudPolicyStatusProvider::UserCloudPolicyStatusProvider( + policy::CloudPolicyCore* core, + Profile* profile) + : CloudPolicyCoreStatusProvider(core), profile_(profile) {} + +UserCloudPolicyStatusProvider::~UserCloudPolicyStatusProvider() = default; + +void UserCloudPolicyStatusProvider::GetStatus(base::DictionaryValue* dict) { + if (!core_->store()->is_managed()) + return; + policy::PolicyStatusProvider::GetStatusFromCore(core_, dict); + ExtractDomainFromUsername(dict); + GetUserAffiliationStatus(dict, profile_); +}
diff --git a/chrome/browser/ui/webui/policy/status_provider/user_cloud_policy_status_provider.h b/chrome/browser/ui/webui/policy/status_provider/user_cloud_policy_status_provider.h new file mode 100644 index 0000000..93098c3e --- /dev/null +++ b/chrome/browser/ui/webui/policy/status_provider/user_cloud_policy_status_provider.h
@@ -0,0 +1,40 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_UI_WEBUI_POLICY_STATUS_PROVIDER_USER_CLOUD_POLICY_STATUS_PROVIDER_H_ +#define CHROME_BROWSER_UI_WEBUI_POLICY_STATUS_PROVIDER_USER_CLOUD_POLICY_STATUS_PROVIDER_H_ + +#include "base/memory/raw_ptr.h" +#include "chrome/browser/ui/webui/policy/status_provider/cloud_policy_core_status_provider.h" + +class Profile; + +namespace base { +class DictionaryValue; +} // namespace base + +namespace policy { +class CloudPolicyCore; +} // namespace policy + +// A cloud policy status provider for user policy. +class UserCloudPolicyStatusProvider : public CloudPolicyCoreStatusProvider { + public: + explicit UserCloudPolicyStatusProvider(policy::CloudPolicyCore* core, + Profile* profile); + + UserCloudPolicyStatusProvider(const UserCloudPolicyStatusProvider&) = delete; + UserCloudPolicyStatusProvider& operator=( + const UserCloudPolicyStatusProvider&) = delete; + + ~UserCloudPolicyStatusProvider() override; + + // CloudPolicyCoreStatusProvider implementation. + void GetStatus(base::DictionaryValue* dict) override; + + private: + raw_ptr<Profile> profile_; +}; + +#endif // CHROME_BROWSER_UI_WEBUI_POLICY_STATUS_PROVIDER_USER_CLOUD_POLICY_STATUS_PROVIDER_H_
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt index 6bb3692..771e548 100644 --- a/chrome/build/linux.pgo.txt +++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@ -chrome-linux-main-1653997591-454c1f5975b0331c533bcd572db5dbce818ba744.profdata +chrome-linux-main-1654019812-195bb7f63d5478756982388ee7634c171ef815cb.profdata
diff --git a/chrome/build/mac-arm.pgo.txt b/chrome/build/mac-arm.pgo.txt index 74c06ad..113f2ba 100644 --- a/chrome/build/mac-arm.pgo.txt +++ b/chrome/build/mac-arm.pgo.txt
@@ -1 +1 @@ -chrome-mac-arm-main-1653911525-52b18476e3e1cda3d2599275ca075f6156d6cbd3.profdata +chrome-mac-arm-main-1654019812-12cf17e770e85a79d9b5d56d2149739bb2f578df.profdata
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt index c94d3be2..7889f26 100644 --- a/chrome/build/mac.pgo.txt +++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@ -chrome-mac-main-1653997591-4979a4d24b373b0d0d10ea2d90b66774a593c8c4.profdata +chrome-mac-main-1654019812-0f0a0aefa982db3247e0b33aeebc738048a67f5c.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt index b4cc190..39656514 100644 --- a/chrome/build/win32.pgo.txt +++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@ -chrome-win32-main-1653997591-d1b24c2fc126ad8e942d7e3cdc39e8f6317623b4.profdata +chrome-win32-main-1654019812-ac3a6f838e5ba25b498a6277633e10e82ea4b3be.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt index 14eff70..2240b35 100644 --- a/chrome/build/win64.pgo.txt +++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@ -chrome-win64-main-1654008797-72e7022d9dda7e1f4d2e3d509a9f885ed63cb218.profdata +chrome-win64-main-1654019812-29cc829ac1922929705cbf37944481c725d85187.profdata
diff --git a/chrome/common/extensions/api/vpn_provider.idl b/chrome/common/extensions/api/vpn_provider.idl index f99f416..fb285a95e 100644 --- a/chrome/common/extensions/api/vpn_provider.idl +++ b/chrome/common/extensions/api/vpn_provider.idl
@@ -4,7 +4,7 @@ // Use the <code>chrome.vpnProvider</code> API to implement a VPN // client. -[platforms=("chromeos"), +[platforms=("chromeos", "lacros"), implemented_in="chrome/browser/chromeos/extensions/vpn_provider/vpn_provider_api.h"] namespace vpnProvider { // A parameters class for the VPN interface.
diff --git a/chrome/common/webui_url_constants.cc b/chrome/common/webui_url_constants.cc index ba407419..6731b459 100644 --- a/chrome/common/webui_url_constants.cc +++ b/chrome/common/webui_url_constants.cc
@@ -330,6 +330,8 @@ const char kChromeUIMultiDeviceSetupUrl[] = "chrome://multidevice-setup"; const char kChromeUINetworkHost[] = "network"; const char kChromeUINetworkUrl[] = "chrome://network"; +const char kChromeUINotificationTesterHost[] = "notification-tester"; +const char kChromeUINotificationTesterURL[] = "chrome://notification-tester"; const char kChromeUIOSCreditsHost[] = "os-credits"; const char kChromeUIOSCreditsURL[] = "chrome://os-credits/"; const char kChromeUIOobeHost[] = "oobe"; @@ -419,6 +421,7 @@ kChromeUIMobileSetupHost, kChromeUIMultiDeviceSetupHost, kChromeUINetworkHost, + kChromeUINotificationTesterHost, kChromeUIOobeHost, kChromeUIOSCreditsHost, kChromeUIOSSettingsHost,
diff --git a/chrome/common/webui_url_constants.h b/chrome/common/webui_url_constants.h index 8b3f93d..23d3add3 100644 --- a/chrome/common/webui_url_constants.h +++ b/chrome/common/webui_url_constants.h
@@ -248,6 +248,8 @@ #if BUILDFLAG(IS_CHROMEOS) extern const char kChromeUIGpuURL[]; extern const char kChromeUIHistogramsURL[]; +extern const char kChromeUINotifGeneratorURL[]; +extern const char kChromeUINotifGeneratorHost[]; #endif #if BUILDFLAG(IS_CHROMEOS_ASH) @@ -307,6 +309,8 @@ extern const char kChromeUIMultiDeviceSetupUrl[]; extern const char kChromeUINetworkHost[]; extern const char kChromeUINetworkUrl[]; +extern const char kChromeUINotificationTesterURL[]; +extern const char kChromeUINotificationTesterHost[]; extern const char kChromeUIOSCreditsHost[]; extern const char kChromeUIOSCreditsURL[]; extern const char kChromeUIOobeHost[];
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn index 9b0a3447..69e39d5 100644 --- a/chrome/test/BUILD.gn +++ b/chrome/test/BUILD.gn
@@ -4584,6 +4584,7 @@ "../browser/chromeos/extensions/login_screen/login_screen_storage/login_screen_storage_apitest.cc", "../browser/chromeos/tablet_mode/tablet_mode_page_behavior_browsertest.cc", "../browser/extensions/api/preference/preference_api_lacros_browsertest.cc", + "../browser/extensions/api/vpn_provider/vpn_provider_apitest.cc", "../browser/lacros/browser_service_lacros_browsertest.cc", "../browser/lacros/browser_test_util.cc", "../browser/lacros/browser_test_util.h", @@ -4626,6 +4627,7 @@ ":test_support", ":test_support_ui", "//chrome/app:command_ids", + "//chrome/browser/chromeos/extensions/vpn_provider", "//chrome/browser/metrics/structured", "//chrome/browser/web_applications:web_applications_test_support", "//chromeos/crosapi/cpp:cpp", @@ -4653,6 +4655,7 @@ data = [ "data/extensions/api_test/login_screen_apis/", + "data/extensions/api_test/vpn_provider/", "data/extensions/api_test/preference/", "data/extensions/platform_apps/minimal/", "data/media/",
diff --git a/chrome/test/chromedriver/key_converter.cc b/chrome/test/chromedriver/key_converter.cc index e0c20961..76c1c2da 100644 --- a/chrome/test/chromedriver/key_converter.cc +++ b/chrome/test/chromedriver/key_converter.cc
@@ -623,13 +623,14 @@ return Status(kUnknownError, "missing 'value'"); int32_t char_index = 0; - uint32_t code_point; + base_icu::UChar32 code_point; base::ReadUnicodeCharacter(raw_key.c_str(), raw_key.size(), &char_index, &code_point); std::string key; if (code_point >= kNormalisedKeyValueBase && - code_point < kNormalisedKeyValueBase + std::size(kNormalisedKeyValue)) { + code_point < base_icu::UChar32{kNormalisedKeyValueBase + + std::size(kNormalisedKeyValue)}) { key = kNormalisedKeyValue[code_point - kNormalisedKeyValueBase]; } if (key.size() == 0)
diff --git a/chrome/test/chromedriver/window_commands.cc b/chrome/test/chromedriver/window_commands.cc index 4f312370..87d1551 100644 --- a/chrome/test/chromedriver/window_commands.cc +++ b/chrome/test/chromedriver/window_commands.cc
@@ -1401,7 +1401,7 @@ if (valid) { // check if key is a single unicode code point int32_t char_index = 0; - uint32_t code_point; + base_icu::UChar32 code_point; valid = base::ReadUnicodeCharacter(key.c_str(), key.size(), &char_index, &code_point) &&
diff --git a/chrome/test/data/webui/certificate_viewer_dialog_test.js b/chrome/test/data/webui/certificate_viewer_dialog_test.js index 6bbf4c2..533d102 100644 --- a/chrome/test/data/webui/certificate_viewer_dialog_test.js +++ b/chrome/test/data/webui/certificate_viewer_dialog_test.js
@@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import {$} from 'chrome://resources/js/util.m.js'; import {eventToPromise} from 'chrome://test/test_util.js'; /** @@ -11,8 +10,8 @@ * @return {?Element} The first found element with a value, null if not found. */ function getElementWithValue(tree) { - for (var i = 0; i < tree.childNodes.length; i++) { - var element = tree.childNodes[i]; + for (let i = 0; i < tree.items.length; i++) { + let element = tree.items[i]; if (element.detail && element.detail.payload && element.detail.payload.val) { return element; @@ -32,40 +31,41 @@ // Tests for the correct common name in the test certificate. test('CommonName', function() { - assertEquals('www.google.com', $('issued-cn').textContent); + assertEquals( + 'www.google.com', document.querySelector('#issued-cn').textContent); }); test('Details', async function() { - var certHierarchy = $('hierarchy'); - var certFields = $('cert-fields'); - var certFieldVal = $('cert-field-value'); + const certHierarchy = document.querySelector('#hierarchy'); + const certFields = document.querySelector('#cert-fields'); + const certFieldVal = document.querySelector('#cert-field-value'); // Select the second tab, causing its data to be loaded if needed. - $('tabbox').selectedIndex = 1; + document.querySelector('cr-tab-box').setAttribute('selected-index', '1'); // There must be at least one certificate in the hierarchy. - assertLT(0, certHierarchy.childNodes.length); + assertLT(0, certHierarchy.items.length); // Wait for the '-for-testing' event to fire only if |certFields| is not // populated yet, otherwise don't wait, the event has already fired. - const whenLoaded = certFields.childNodes.length === 0 ? + const whenLoaded = certFields.items.length === 0 ? eventToPromise( 'certificate-fields-updated-for-testing', document.body) : Promise.resolve(); // Select the first certificate on the chain and ensure the details show up. await whenLoaded; - assertLT(0, certFields.childNodes.length); + assertLT(0, certFields.items.length); // Test that a field can be selected to see the details for that // field. - var item = getElementWithValue(certFields); + const item = getElementWithValue(certFields); assertNotEquals(null, item); certFields.selectedItem = item; assertEquals(item.detail.payload.val, certFieldVal.textContent); // Test that selecting an item without a value empties the field. - certFields.selectedItem = certFields.childNodes[0]; + certFields.selectedItem = certFields.items[0]; assertEquals('', certFieldVal.textContent); }); });
diff --git a/chrome/test/data/webui/chromeos/shimless_rma/onboarding_network_page_test.js b/chrome/test/data/webui/chromeos/shimless_rma/onboarding_network_page_test.js index b45dce48..e831c42 100644 --- a/chrome/test/data/webui/chromeos/shimless_rma/onboarding_network_page_test.js +++ b/chrome/test/data/webui/chromeos/shimless_rma/onboarding_network_page_test.js
@@ -142,6 +142,48 @@ assertFalse(dialog.open); }); + test('DialogReopensAfterHittingConnect', async () => { + networkConfigService.addNetworksForTest(fakeNetworks); + await initializeOnboardingNetworkPage(); + const networkList = component.shadowRoot.querySelector('#networkList'); + + // Add fake unconnected wifi. + const fakeWiFi = OncMojo.getDefaultNetworkState( + chromeos.networkConfig.mojom.NetworkType.kWiFi, 'wifi'); + fakeWiFi.connectionState = + chromeos.networkConfig.mojom.ConnectionStateType.kNotConnected; + networkConfigService.addNetworksForTest(fakeWiFi); + component.refreshNetworks(); + await flushTasks(); + + // Open network dialog. + const network = networkList.networks[networkList.networks.length - 1]; + + const dialog = /** @type {!CrDialogElement} */ ( + component.shadowRoot.querySelector('#dialog')); + assertFalse(dialog.open); + component.showConfig_(network.type, network.guid, network.name); + assertTrue(dialog.open); + await flushTasks(); + + // Click connect button and dialog will be closed. + component.onNetworkSelected_({detail: network}); + const connectButton = /** @type {!CrDialogElement} */ ( + component.shadowRoot.querySelector('#connectButton')); + assertFalse(connectButton.hidden); + connectButton.click(); + component.refreshNetworks(); + await flushTasks(); + assertFalse(dialog.open); + + // Reopen the same dialog. + const dialog2 = /** @type {!CrDialogElement} */ ( + component.shadowRoot.querySelector('#dialog')); + assertFalse(dialog2.open); + component.showConfig_(network.type, network.guid, network.name); + assertTrue(dialog2.open); + }); + test('DisconnectNetwork', async () => { networkConfigService.addNetworksForTest(fakeNetworks); await initializeOnboardingNetworkPage();
diff --git a/chrome/test/data/webui/chromeos/shimless_rma/wrapup_restock_page_test.js b/chrome/test/data/webui/chromeos/shimless_rma/wrapup_restock_page_test.js index 33a8be5..113aacd 100644 --- a/chrome/test/data/webui/chromeos/shimless_rma/wrapup_restock_page_test.js +++ b/chrome/test/data/webui/chromeos/shimless_rma/wrapup_restock_page_test.js
@@ -42,9 +42,7 @@ service.reset(); }); - /** - * @return {!Promise} - */ + /** @return {!Promise} */ function initializeRestockPage() { assertFalse(!!component); @@ -60,15 +58,12 @@ return flushTasks(); } - /** - * @param {string} buttonNameSelector - * @return {!Promise} - */ - function clickButton(buttonNameSelector) { - assertTrue(!!component); - const button = component.shadowRoot.querySelector(buttonNameSelector); - button.click(); + /** @return {!Promise} */ + function clickShutdownButton() { + const shutdownComponent = component.shadowRoot.querySelector('#shutdown'); + assertTrue(!!shutdownComponent); + shutdownComponent.click(); return flushTasks(); } @@ -95,7 +90,7 @@ assertEquals(1, callCounter); }); - test('RestockPagePowerwashButtonCallsShutdownForRestock', async () => { + test('RestockPageOnShutdownCallsShutdownForRestock', async () => { const resolver = new PromiseResolver(); await initializeRestockPage(); let restockCallCounter = 0; @@ -104,61 +99,11 @@ return resolver.promise; }; - await clickButton('#powerwashButton'); + await clickShutdownButton(); assertEquals(1, restockCallCounter); }); - test('ShutDownButtonOpensPowerwashDialog', async () => { - const resolver = new PromiseResolver(); - await initializeRestockPage(); - - let callCount = 0; - service.shutdownForRestock = () => { - callCount++; - return resolver.promise; - }; - await flushTasks(); - - await clickButton('#shutdown'); - - // Don't shut down immediately. - assertEquals(0, callCount); - // Show the dialog instead. - const powerwashDialog = - component.shadowRoot.querySelector('#powerwashDialog'); - assertTrue(!!powerwashDialog); - assertTrue(powerwashDialog.open); - }); - - test('CancelButtonClosesPowerwashDialog', async () => { - await initializeRestockPage(); - await flushTasks(); - const powerwashDialog = - component.shadowRoot.querySelector('#powerwashDialog'); - assertTrue(!!powerwashDialog); - - await clickButton('#shutdown'); - assertTrue(powerwashDialog.open); - - await clickButton('#closePowerwashDialogButton'); - assertFalse(powerwashDialog.open); - }); - - test('PowerwashDialogClosesWhenCompleted', async () => { - await initializeRestockPage(); - - const powerwashDialog = - component.shadowRoot.querySelector('#powerwashDialog'); - assertTrue(!!powerwashDialog); - - await clickButton('#shutdown'); - assertTrue(powerwashDialog.open); - - await clickButton('#powerwashButton'); - assertFalse(powerwashDialog.open); - }); - test('RestockPageButtonsDisabled', async () => { await initializeRestockPage();
diff --git a/chrome/test/data/xr/e2e_test_files/html/webxr_test_camera_access.html b/chrome/test/data/xr/e2e_test_files/html/webxr_test_camera_access.html index ad790a41..18fea99 100644 --- a/chrome/test/data/xr/e2e_test_files/html/webxr_test_camera_access.html +++ b/chrome/test/data/xr/e2e_test_files/html/webxr_test_camera_access.html
@@ -114,6 +114,7 @@ for (let view of pose.views) { if (view.camera) { + ranAtLeastOnce = true; cameraImageTexture = glBinding.getCameraImage(view.camera); assert_not_equals(cameraImageTexture, null, "XRWebGLBinding.getCameraImage(...) returned null texture."); } @@ -122,6 +123,7 @@ if (numPosesFound > MAX_NUM_FRAMES_WITH_POSES) { onARFrameCallback = null; + assert_true(ranAtLeastOnce); done(); } }; @@ -137,7 +139,6 @@ return count; } - // TODO(https://www.crbug.com/1115167): Enable test once pixel reads are working as intended. function stepCheckCameraTextureLifetimeLimitedToOneFrame() { const webglCanvas = document.getElementById('webgl-canvas'); const gl = webglCanvas.getContext('webgl', {
diff --git a/chromecast/cast_core/BUILD.gn b/chromecast/cast_core/BUILD.gn index 410f3228..fcb3dc80 100644 --- a/chromecast/cast_core/BUILD.gn +++ b/chromecast/cast_core/BUILD.gn
@@ -4,6 +4,10 @@ import("//chromecast/chromecast.gni") +cast_source_set("cast_core_switches") { + sources = [ "cast_core_switches.h" ] +} + # When built for use with Cast Core, enble_cast_media_runtime must be true. cast_source_set("core_runtime_lib_simple") { data_deps = [ "//chromecast:cast_shell_pak" ]
diff --git a/chromecast/cast_core/DEPS b/chromecast/cast_core/DEPS index 463c8ed..c4dd9ea 100644 --- a/chromecast/cast_core/DEPS +++ b/chromecast/cast_core/DEPS
@@ -1,7 +1,5 @@ include_rules = [ - "-chromecast/cast_core", - "+chromecast/cast_core/grpc", - "+chromecast/cast_core/runtime", + "+chromecast/cast_core", "+chromecast/app", "+chromecast/base", "+content/public/app",
diff --git a/chromecast/cast_core/cast_core_switches.h b/chromecast/cast_core/cast_core_switches.h new file mode 100644 index 0000000..d2c5eb2 --- /dev/null +++ b/chromecast/cast_core/cast_core_switches.h
@@ -0,0 +1,21 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROMECAST_CAST_CORE_CAST_CORE_SWITCHES_H_ +#define CHROMECAST_CAST_CORE_CAST_CORE_SWITCHES_H_ + +namespace cast { +namespace core { + +// Specifies the Cast Core runtime ID, --cast-core-runtime-id=<runtime_id>. +constexpr char kCastCoreRuntimeIdSwitch[] = "cast-core-runtime-id"; + +// Specifies the Cast Core runtime gRPC endpoint, +// --runtime-service-path=<endpoint>. +constexpr char kRuntimeServicePathSwitch[] = "runtime-service-path"; + +} // namespace core +} // namespace cast + +#endif // CHROMECAST_CAST_CORE_CAST_CORE_SWITCHES_H_
diff --git a/chromecast/cast_core/runtime/browser/BUILD.gn b/chromecast/cast_core/runtime/browser/BUILD.gn index c20a2da..d7e55f4 100644 --- a/chromecast/cast_core/runtime/browser/BUILD.gn +++ b/chromecast/cast_core/runtime/browser/BUILD.gn
@@ -239,6 +239,7 @@ ":runtime_application_dispatcher", "//base", "//chromecast/browser:browser_base", + "//chromecast/cast_core:cast_core_switches", "//chromecast/media/cma/backend/proxy:headers", "//chromecast/metrics:cast_event_builder_simple", "//chromecast/service",
diff --git a/chromecast/cast_core/runtime/browser/cast_runtime_service.cc b/chromecast/cast_core/runtime/browser/cast_runtime_service.cc index c3bb6e3..942bd98 100644 --- a/chromecast/cast_core/runtime/browser/cast_runtime_service.cc +++ b/chromecast/cast_core/runtime/browser/cast_runtime_service.cc
@@ -7,15 +7,10 @@ #include "base/command_line.h" #include "base/process/process.h" #include "chromecast/browser/cast_browser_process.h" +#include "chromecast/cast_core/cast_core_switches.h" #include "chromecast/metrics/cast_event_builder_simple.h" namespace chromecast { -namespace { - -const char kCastCoreRuntimeIdSwitch[] = "cast-core-runtime-id"; -const char kRuntimeServicePathSwitch[] = "runtime-service-path"; - -} // namespace CastRuntimeService::CastRuntimeService( CastWebService* web_service, @@ -37,9 +32,9 @@ void CastRuntimeService::StartInternal() { auto* command_line = base::CommandLine::ForCurrentProcess(); std::string runtime_id = - command_line->GetSwitchValueASCII(kCastCoreRuntimeIdSwitch); + command_line->GetSwitchValueASCII(cast::core::kCastCoreRuntimeIdSwitch); std::string runtime_service_path = - command_line->GetSwitchValueASCII(kRuntimeServicePathSwitch); + command_line->GetSwitchValueASCII(cast::core::kRuntimeServicePathSwitch); if (!app_dispatcher_.Start(runtime_id, runtime_service_path)) { base::Process::TerminateCurrentProcessImmediately(1); }
diff --git a/chromecast/common/activity_url_filter.cc b/chromecast/common/activity_url_filter.cc index a48e6b5..b6bad5a5 100644 --- a/chromecast/common/activity_url_filter.cc +++ b/chromecast/common/activity_url_filter.cc
@@ -9,7 +9,7 @@ ActivityUrlFilter::ActivityUrlFilter( const std::vector<std::string>& url_filters) : url_matcher_(std::make_unique<url_matcher::URLMatcher>()) { - url_matcher::URLMatcherConditionSet::ID id = 0; + base::MatcherStringPattern::ID id = 0; url_matcher::URLMatcherConditionSet::Vector condition_sets; for (const auto& url : url_filters) { url_matcher::URLMatcherConditionSet::Conditions conditions;
diff --git a/chromeos/chromeos_strings.grd b/chromeos/chromeos_strings.grd index fbb6656..cc52b55b 100644 --- a/chromeos/chromeos_strings.grd +++ b/chromeos/chromeos_strings.grd
@@ -2218,8 +2218,8 @@ Close the camera </message> - <message name="IDS_PERSONALIZATION_APP_SCREENSAVER_LABEL" desc="Label for the Screensaver page in Personalization app."> - Screensaver + <message name="IDS_PERSONALIZATION_APP_SCREENSAVER_LABEL" desc="Label for the Screen saver page in Personalization app."> + Screen saver </message> <message name="IDS_PERSONALIZATION_APP_AMBIENT_MODE_PAGE_DESCRIPTION" desc="Description for the ambient mode settings page."> When your screen is idle, show photos, time, weather, and media info. @@ -2291,7 +2291,7 @@ Got it </message> <message name="IDS_PERSONALIZATION_APP_AMBIENT_MODE_ANIMATION_TITLE" desc="Label for the ambient mode animation section."> - Screensaver animation + Screen saver animation </message> <message name="IDS_PERSONALIZATION_APP_AMBIENT_MODE_ANIMATION_SLIDESHOW_LABEL" desc="Label for the ambient mode slideshow animation."> Slide show @@ -2303,13 +2303,13 @@ Float on by </message> <message name="IDS_PERSONALIZATION_APP_AMBIENT_MODE_ZERO_STATE_MESSAGE" desc="Message shown in the Ambient subpage in the personalization hub when ambient mode is disabled."> - Turn on the toggle to select the screensaver options + Turn on the toggle to select the screen saver options </message> <message name="IDS_PERSONALIZATION_APP_AMBIENT_MODE_MULTIPLE_ALBUMS_DESC" desc="Album description shown in the Ambient preview when more than 3 albums are selected"> <ph name="TITLE">$1<ex>Sweden 2020</ex></ph>, +<ph name="NUMBER">$2<ex>2</ex></ph> more albums </message> <message name="IDS_PERSONALIZATION_APP_AMBIENT_MODE_MAIN_PAGE_ZERO_STATE_MESSAGE" desc="Message shown in the Ambient subpage on the main page of personalization hub when ambient mode is disabled."> - Turn the feature on to select screensaver options + Turn the feature on to select screen saver options </message> <message name="IDS_PERSONALIZATION_APP_AMBIENT_MODE_TURN_ON_LABEL" desc="Label of button to turn on ambient mode."> Turn on @@ -3048,7 +3048,7 @@ You can continue with the full repair or powerwash (factory reset) the device to restock the mainboard. </message> <message name="IDS_SHIMLESS_RMA_RESTOCK_SHUTDOWN_BUTTON" desc="Label for the button that triggers device shutdown on the restock page."> - Powerwash & Shut down + Shut down </message> <message name="IDS_SHIMLESS_RMA_RESTOCK_CONTINUE_BUTTON" desc="Label for the continue button on the restock page."> Continue
diff --git a/chromeos/chromeos_strings_grd/IDS_PERSONALIZATION_APP_AMBIENT_MODE_ANIMATION_TITLE.png.sha1 b/chromeos/chromeos_strings_grd/IDS_PERSONALIZATION_APP_AMBIENT_MODE_ANIMATION_TITLE.png.sha1 index f104090..6bee9fd 100644 --- a/chromeos/chromeos_strings_grd/IDS_PERSONALIZATION_APP_AMBIENT_MODE_ANIMATION_TITLE.png.sha1 +++ b/chromeos/chromeos_strings_grd/IDS_PERSONALIZATION_APP_AMBIENT_MODE_ANIMATION_TITLE.png.sha1
@@ -1 +1 @@ -c56cfa2cd733197191119c3934f2ae170eaa98cc \ No newline at end of file +a1d17feb7bffb812cf910436aeff6d2d45d0c36b \ No newline at end of file
diff --git a/chromeos/chromeos_strings_grd/IDS_PERSONALIZATION_APP_AMBIENT_MODE_MAIN_PAGE_ZERO_STATE_MESSAGE.png.sha1 b/chromeos/chromeos_strings_grd/IDS_PERSONALIZATION_APP_AMBIENT_MODE_MAIN_PAGE_ZERO_STATE_MESSAGE.png.sha1 index e16852eb..f064927 100644 --- a/chromeos/chromeos_strings_grd/IDS_PERSONALIZATION_APP_AMBIENT_MODE_MAIN_PAGE_ZERO_STATE_MESSAGE.png.sha1 +++ b/chromeos/chromeos_strings_grd/IDS_PERSONALIZATION_APP_AMBIENT_MODE_MAIN_PAGE_ZERO_STATE_MESSAGE.png.sha1
@@ -1 +1 @@ -837e1db9441e676071172e42f5b9ad74d2915cc2 \ No newline at end of file +edd1d485b691e876ce7dffcc8ae9ebd557fa2397 \ No newline at end of file
diff --git a/chromeos/chromeos_strings_grd/IDS_PERSONALIZATION_APP_AMBIENT_MODE_ZERO_STATE_MESSAGE.png.sha1 b/chromeos/chromeos_strings_grd/IDS_PERSONALIZATION_APP_AMBIENT_MODE_ZERO_STATE_MESSAGE.png.sha1 index 56b6676..5ae7eb036 100644 --- a/chromeos/chromeos_strings_grd/IDS_PERSONALIZATION_APP_AMBIENT_MODE_ZERO_STATE_MESSAGE.png.sha1 +++ b/chromeos/chromeos_strings_grd/IDS_PERSONALIZATION_APP_AMBIENT_MODE_ZERO_STATE_MESSAGE.png.sha1
@@ -1 +1 @@ -9a01c0a9890fda62eef059f172c78336b50be7cc \ No newline at end of file +9a1429757ce8621e60203c08d9a9fcdaf12aa4b1 \ No newline at end of file
diff --git a/chromeos/chromeos_strings_grd/IDS_PERSONALIZATION_APP_SCREENSAVER_LABEL.png.sha1 b/chromeos/chromeos_strings_grd/IDS_PERSONALIZATION_APP_SCREENSAVER_LABEL.png.sha1 index 4168826..c991f8c 100644 --- a/chromeos/chromeos_strings_grd/IDS_PERSONALIZATION_APP_SCREENSAVER_LABEL.png.sha1 +++ b/chromeos/chromeos_strings_grd/IDS_PERSONALIZATION_APP_SCREENSAVER_LABEL.png.sha1
@@ -1 +1 @@ -6632b58c725fe77b7bd095091e3ad9f6314247fd \ No newline at end of file +17d46a862b23bf75822511b9241ad8216af3a8fc \ No newline at end of file
diff --git a/chromeos/chromeos_strings_grd/IDS_SHIMLESS_RMA_RESTOCK_SHUTDOWN_BUTTON.png.sha1 b/chromeos/chromeos_strings_grd/IDS_SHIMLESS_RMA_RESTOCK_SHUTDOWN_BUTTON.png.sha1 index d2c861a..a59cc3fa 100644 --- a/chromeos/chromeos_strings_grd/IDS_SHIMLESS_RMA_RESTOCK_SHUTDOWN_BUTTON.png.sha1 +++ b/chromeos/chromeos_strings_grd/IDS_SHIMLESS_RMA_RESTOCK_SHUTDOWN_BUTTON.png.sha1
@@ -1 +1 @@ -fa70da8877a23fc25894c1bac8ff8ac84067a96b \ No newline at end of file +920f7c170a437d4faeb7c0cbe34a00c74ac4511c \ No newline at end of file
diff --git a/chromeos/crosapi/mojom/test_controller.mojom b/chromeos/crosapi/mojom/test_controller.mojom index 1e45c99..2853d21f 100644 --- a/chromeos/crosapi/mojom/test_controller.mojom +++ b/chromeos/crosapi/mojom/test_controller.mojom
@@ -46,6 +46,24 @@ LoadVpnExtension@1(string extension_name) => (string extension_id); }; +// Allows callers running in lacros to trigger test events that are passed to +// the active Vpn configuration. +// See chrome/browser/lacros/vpn_provider_lacros_apitest.cc for details. +// Next version: 1 +// Next method id: 2 +[Stable, Uuid="e2bca22e-32e9-40b3-a0bc-035b41534496"] +interface TestShillController { + // Simulate |data| being received by the configuration determined by + // |extension_id| and |configuration_name|. + OnPacketReceived@0(string extension_id, string configuration_name, + array<uint8> data); + + // Simulate |message| being received by the configuration determined by + // |extension_id| and |configuration_name|. + OnPlatformMessage@1(string extension_id, string configuration_name, + uint32 message); +}; + // This interface is implemented by Ash-Chrome. // This interface provides tests a mechanism to mutate or query ash. // In the future, this interface may merge with an automation or a11y interface. @@ -181,4 +199,10 @@ // Introduced in M-103. [MinVersion=13] GetAshVersion@22() => (string ash_version); + + // Binds |test_shill_controller| that can be used to simulate packet/message + // events. + [MinVersion=14] + BindTestShillController@23( + pending_receiver<TestShillController> test_shill_controller) => (); };
diff --git a/chromeos/lacros/lacros_service.cc b/chromeos/lacros/lacros_service.cc index db834cb..b7c03646 100644 --- a/chromeos/lacros/lacros_service.cc +++ b/chromeos/lacros/lacros_service.cc
@@ -74,6 +74,7 @@ #include "chromeos/crosapi/mojom/tts.mojom.h" #include "chromeos/crosapi/mojom/url_handler.mojom.h" #include "chromeos/crosapi/mojom/vpn_extension_observer.mojom.h" +#include "chromeos/crosapi/mojom/vpn_service.mojom.h" #include "chromeos/crosapi/mojom/web_app_service.mojom.h" #include "chromeos/crosapi/mojom/web_page_info.mojom.h" #include "chromeos/lacros/lacros_service_never_blocking_state.h" @@ -418,6 +419,8 @@ ConstructRemote< crosapi::mojom::VpnExtensionObserver, &Crosapi::BindVpnExtensionObserver, Crosapi::MethodMinVersions::kBindVpnExtensionObserverMinVersion>(); + ConstructRemote<crosapi::mojom::VpnService, &Crosapi::BindVpnService, + Crosapi::MethodMinVersions::kBindVpnServiceMinVersion>(); #if !BUILDFLAG(IS_CHROMEOS_DEVICE) // The test controller is not available on production devices as tests only
diff --git a/chromeos/network/shill_property_util.cc b/chromeos/network/shill_property_util.cc index 66c18c0..51e5933 100644 --- a/chromeos/network/shill_property_util.cc +++ b/chromeos/network/shill_property_util.cc
@@ -33,14 +33,14 @@ std::string ValidateUTF8(const std::string& str) { std::string result; for (int32_t index = 0; index < static_cast<int32_t>(str.size()); ++index) { - uint32_t code_point_out; + base_icu::UChar32 code_point_out; bool is_unicode_char = base::ReadUnicodeCharacter(str.c_str(), str.size(), &index, &code_point_out); - const uint32_t kFirstNonControlChar = 0x20; + constexpr base_icu::UChar32 kFirstNonControlChar = 0x20; if (is_unicode_char && (code_point_out >= kFirstNonControlChar)) { base::WriteUnicodeCharacter(code_point_out, &result); } else { - const uint32_t kReplacementChar = 0xFFFD; + constexpr base_icu::UChar32 kReplacementChar = 0xFFFD; // Puts kReplacementChar if character is a control character [0,0x20) // or is not readable UTF8. base::WriteUnicodeCharacter(kReplacementChar, &result);
diff --git a/components/autofill_assistant/browser/actions/get_element_status_action.cc b/components/autofill_assistant/browser/actions/get_element_status_action.cc index cc00ed2..408c02d 100644 --- a/components/autofill_assistant/browser/actions/get_element_status_action.cc +++ b/components/autofill_assistant/browser/actions/get_element_status_action.cc
@@ -28,7 +28,7 @@ std::string RemoveWhitespace(const std::string& value) { std::string copy = value; - base::EraseIf(copy, base::IsUnicodeWhitespace); + base::EraseIf(copy, base::IsUnicodeWhitespace<char>); return copy; }
diff --git a/components/autofill_assistant/browser/public/external_action_delegate.h b/components/autofill_assistant/browser/public/external_action_delegate.h index 4a5008dc..32161ab0 100644 --- a/components/autofill_assistant/browser/public/external_action_delegate.h +++ b/components/autofill_assistant/browser/public/external_action_delegate.h
@@ -14,6 +14,7 @@ // script. class ExternalActionDelegate { public: + virtual ~ExternalActionDelegate() = default; // Called when the script reaches an external action. // The |start_dom_checks_callback| can optionally be called to start the DOM // checks. This will allow interrupts to trigger (if the action itself allows
diff --git a/components/autofill_assistant/browser/starter_heuristic.cc b/components/autofill_assistant/browser/starter_heuristic.cc index fc306b82..872876f 100644 --- a/components/autofill_assistant/browser/starter_heuristic.cc +++ b/components/autofill_assistant/browser/starter_heuristic.cc
@@ -74,8 +74,8 @@ return; } url_matcher::URLMatcherConditionSet::Vector condition_sets; - base::flat_map<url_matcher::URLMatcherConditionSet::ID, std::string> mapping; - url_matcher::URLMatcherConditionSet::ID next_condition_set_id = 0; + base::flat_map<base::MatcherStringPattern::ID, std::string> mapping; + base::MatcherStringPattern::ID next_condition_set_id = 0; for (const auto& heuristic : heuristics->GetListDeprecated()) { auto* intent = heuristic.FindKeyOfType(kHeuristicIntentKey, base::Value::Type::STRING); @@ -130,8 +130,7 @@ return matching_intents; } - std::set<url_matcher::URLMatcherConditionSet::ID> matches = - url_matcher_.MatchURL(url); + std::set<base::MatcherStringPattern::ID> matches = url_matcher_.MatchURL(url); for (const auto& match : matches) { auto intent = matcher_id_to_intent_map_.find(match); if (intent == matcher_id_to_intent_map_.end()) {
diff --git a/components/autofill_assistant/browser/starter_heuristic.h b/components/autofill_assistant/browser/starter_heuristic.h index 52d5e40..aa0e602 100644 --- a/components/autofill_assistant/browser/starter_heuristic.h +++ b/components/autofill_assistant/browser/starter_heuristic.h
@@ -62,7 +62,7 @@ // Arbitrary mapping of matcher IDs to intent strings. This mapping is built // dynamically to allow the heuristic to work on intents that are otherwise // unknown to the client. - base::flat_map<url_matcher::URLMatcherConditionSet::ID, std::string> + base::flat_map<base::MatcherStringPattern::ID, std::string> matcher_id_to_intent_map_; };
diff --git a/components/autofill_payments_strings.grdp b/components/autofill_payments_strings.grdp index 5c939c46..d5e2fe4 100644 --- a/components/autofill_payments_strings.grdp +++ b/components/autofill_payments_strings.grdp
@@ -139,6 +139,12 @@ </message> </else> </if> + <message name="IDS_AUTOFILL_SAVE_CARD_PROMPT_TITLE_EXPERIMENT_ENCRYPTED_AND_SECURE" desc="Title text for the Autofill save card prompt when the card is to be saved by uploading it to Google Payments. The prompt can be either a bubble or an infobar. This is an experiment text and may change in the future."> + Save card securely + </message> + <message name="IDS_AUTOFILL_SAVE_CARD_PROMPT_TITLE_EXPERIMENT_FASTER_AND_PROTECTED" desc="Title text for the Autofill save card prompt when the card is to be saved by uploading it to Google Payments. The prompt can be either a bubble or an infobar. This is an experiment text and may change in the future."> + Save card? + </message> <message name="IDS_AUTOFILL_SAVE_CARD_PROMPT_TITLE_TO_CLOUD_V3" desc="Title text for the Autofill save card prompt when the card is to be saved by uploading it to Google Payments, according to January 2018 UI guidelines. The prompt can be either a bubble or an infobar."> Save card? </message> @@ -166,6 +172,12 @@ <message name="IDS_AUTOFILL_SAVE_CARD_PROMPT_UPLOAD_EXPLANATION_V3" desc="Explanation of the effect of the Autofill save card prompt when the card is to be saved by uploading it to Google Payments, according to April 2018 UI guidelines. The prompt will be shown in a bubble below the omnibox."> To pay faster next time, save your card and billing address to your Google Account. </message> + <message name="IDS_AUTOFILL_SAVE_CARD_PROMPT_UPLOAD_EXPLANATION_EXPERIMENT_ENCRYPTED_AND_SECURE" desc="Explanation of the effect of the Autofill save card prompt when the card is to be saved by uploading it to Google Payments. The prompt will be shown in a bubble below the omnibox. This is an experiment text and may change in the future."> + It’ll be encrypted, saved securely and the CVC is never stored. + </message> + <message name="IDS_AUTOFILL_SAVE_CARD_PROMPT_UPLOAD_EXPLANATION_EXPERIMENT_FASTER_AND_PROTECTED" desc="Explanation of the effect of the Autofill save card prompt when the card is to be saved by uploading it to Google Payments. The prompt will be shown in a bubble below the omnibox. This is an experiment text and may change in the future."> + Pay faster next time and protect your card with Google’s industry-leading security. + </message> <message name="IDS_AUTOFILL_SAVE_CARD_PROMPT_UPLOAD_EXPLANATION_V3_WITH_NAME" desc="Explanation of the effect of the Autofill save card prompt when the card is to be saved by uploading it to Google Payments, according to April 2018 UI guidelines. The prompt will be shown in a bubble below the omnibox."> To pay faster next time, save your card, name, and billing address to your Google Account. </message>
diff --git a/components/autofill_payments_strings_grdp/IDS_AUTOFILL_SAVE_CARD_PROMPT_TITLE_EXPERIMENT_ENCRYPTED_AND_SECURE.png.sha1 b/components/autofill_payments_strings_grdp/IDS_AUTOFILL_SAVE_CARD_PROMPT_TITLE_EXPERIMENT_ENCRYPTED_AND_SECURE.png.sha1 new file mode 100644 index 0000000..bc126eb --- /dev/null +++ b/components/autofill_payments_strings_grdp/IDS_AUTOFILL_SAVE_CARD_PROMPT_TITLE_EXPERIMENT_ENCRYPTED_AND_SECURE.png.sha1
@@ -0,0 +1 @@ +3954965caed137db4c9a5c60753e68ba889416f6 \ No newline at end of file
diff --git a/components/autofill_payments_strings_grdp/IDS_AUTOFILL_SAVE_CARD_PROMPT_TITLE_EXPERIMENT_FASTER_AND_PROTECTED.png.sha1 b/components/autofill_payments_strings_grdp/IDS_AUTOFILL_SAVE_CARD_PROMPT_TITLE_EXPERIMENT_FASTER_AND_PROTECTED.png.sha1 new file mode 100644 index 0000000..bdfbe2c --- /dev/null +++ b/components/autofill_payments_strings_grdp/IDS_AUTOFILL_SAVE_CARD_PROMPT_TITLE_EXPERIMENT_FASTER_AND_PROTECTED.png.sha1
@@ -0,0 +1 @@ +71a20cbaa4eefc2b2b38164e42a2dde78995b5b1 \ No newline at end of file
diff --git a/components/autofill_payments_strings_grdp/IDS_AUTOFILL_SAVE_CARD_PROMPT_UPLOAD_EXPLANATION_EXPERIMENT_ENCRYPTED_AND_SECURE.png.sha1 b/components/autofill_payments_strings_grdp/IDS_AUTOFILL_SAVE_CARD_PROMPT_UPLOAD_EXPLANATION_EXPERIMENT_ENCRYPTED_AND_SECURE.png.sha1 new file mode 100644 index 0000000..4cbafd21 --- /dev/null +++ b/components/autofill_payments_strings_grdp/IDS_AUTOFILL_SAVE_CARD_PROMPT_UPLOAD_EXPLANATION_EXPERIMENT_ENCRYPTED_AND_SECURE.png.sha1
@@ -0,0 +1 @@ +a399eb6bfc146b537ed09de6d8d9022736f6930d \ No newline at end of file
diff --git a/components/autofill_payments_strings_grdp/IDS_AUTOFILL_SAVE_CARD_PROMPT_UPLOAD_EXPLANATION_EXPERIMENT_FASTER_AND_PROTECTED.png.sha1 b/components/autofill_payments_strings_grdp/IDS_AUTOFILL_SAVE_CARD_PROMPT_UPLOAD_EXPLANATION_EXPERIMENT_FASTER_AND_PROTECTED.png.sha1 new file mode 100644 index 0000000..03d3e65 --- /dev/null +++ b/components/autofill_payments_strings_grdp/IDS_AUTOFILL_SAVE_CARD_PROMPT_UPLOAD_EXPLANATION_EXPERIMENT_FASTER_AND_PROTECTED.png.sha1
@@ -0,0 +1 @@ +75415291bc382b21eae385d42272a837bc519485 \ No newline at end of file
diff --git a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/chips/OWNERS b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/chips/OWNERS index 8c116e7..0fef2b7 100644 --- a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/chips/OWNERS +++ b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/chips/OWNERS
@@ -1,3 +1,6 @@ +gangwu@chromium.org + +# Secondary donnd@chromium.org dtrainor@chromium.org
diff --git a/components/cast_streaming/browser/playback_command_dispatcher.cc b/components/cast_streaming/browser/playback_command_dispatcher.cc index 1a53541..062a9853 100644 --- a/components/cast_streaming/browser/playback_command_dispatcher.cc +++ b/components/cast_streaming/browser/playback_command_dispatcher.cc
@@ -32,18 +32,12 @@ muxer_ = std::make_unique<RendererControlMultiplexer>(std::move(renderer), task_runner_); - // Create a "fake" media::mojom::Renderer so that the RpcCallTranslator can - // pass commands to the |muxer_|. - mojo::Remote<media::mojom::Renderer> translators_renderer; - RegisterCommandSource(translators_renderer.BindNewPipeAndPassReceiver()); - auto message_processor_callback = base::BindRepeating( &PlaybackCommandDispatcher::SendRemotingRpcMessageToRemote, weak_factory_.GetWeakPtr()); renderer_call_translator_ = std::make_unique<remoting::RendererRpcCallTranslator>( - std::move(message_processor_callback), - std::move(translators_renderer)); + std::move(message_processor_callback), muxer_.get()); } PlaybackCommandDispatcher::~PlaybackCommandDispatcher() {
diff --git a/components/cast_streaming/browser/renderer_rpc_call_translator.cc b/components/cast_streaming/browser/renderer_rpc_call_translator.cc index 43189774..d31a81b 100644 --- a/components/cast_streaming/browser/renderer_rpc_call_translator.cc +++ b/components/cast_streaming/browser/renderer_rpc_call_translator.cc
@@ -11,10 +11,10 @@ RendererRpcCallTranslator::RendererRpcCallTranslator( RpcMessageProcessor processor, - mojo::Remote<media::mojom::Renderer> remote_renderer) + media::mojom::Renderer* renderer) : message_processor_(std::move(processor)), renderer_client_receiver_(this), - renderer_remote_(std::move(remote_renderer)), + renderer_(std::move(renderer)), weak_factory_(this) {} RendererRpcCallTranslator::~RendererRpcCallTranslator() = default; @@ -22,7 +22,7 @@ void RendererRpcCallTranslator::OnRpcInitialize() { if (!has_been_initialized_) { has_been_initialized_ = true; - renderer_remote_->Initialize( + renderer_->Initialize( renderer_client_receiver_.BindNewEndpointAndPassRemote(), /* streams */ {}, /* media_url_params */ nullptr, base::BindOnce(&RendererRpcCallTranslator::OnInitializeCompleted, @@ -34,21 +34,20 @@ void RendererRpcCallTranslator::OnRpcFlush(uint32_t audio_count, uint32_t video_count) { - renderer_remote_->Flush( - base::BindOnce(&RendererRpcCallTranslator::OnFlushCompleted, - weak_factory_.GetWeakPtr(), handle_)); + renderer_->Flush(base::BindOnce(&RendererRpcCallTranslator::OnFlushCompleted, + weak_factory_.GetWeakPtr(), handle_)); } void RendererRpcCallTranslator::OnRpcStartPlayingFrom(base::TimeDelta time) { - renderer_remote_->StartPlayingFrom(time); + renderer_->StartPlayingFrom(time); } void RendererRpcCallTranslator::OnRpcSetPlaybackRate(double playback_rate) { - renderer_remote_->SetPlaybackRate(playback_rate); + renderer_->SetPlaybackRate(playback_rate); } void RendererRpcCallTranslator::OnRpcSetVolume(double volume) { - renderer_remote_->SetVolume(volume); + renderer_->SetVolume(volume); } void RendererRpcCallTranslator::OnTimeUpdate(base::TimeDelta media_time,
diff --git a/components/cast_streaming/browser/renderer_rpc_call_translator.h b/components/cast_streaming/browser/renderer_rpc_call_translator.h index 6cb5a504..cdd70ab5 100644 --- a/components/cast_streaming/browser/renderer_rpc_call_translator.h +++ b/components/cast_streaming/browser/renderer_rpc_call_translator.h
@@ -31,13 +31,12 @@ openscreen::cast::RpcMessenger::Handle handle, std::unique_ptr<openscreen::cast::RpcMessage>)>; - // |remote_renderer| is the remote media::mojom::Renderer to which commands + // |renderer| is the remote media::mojom::Renderer to which commands // translated from proto messages should be sent. // |processor| is responsible for handling any proto messages ready to be sent // out. - explicit RendererRpcCallTranslator( - RpcMessageProcessor processor, - mojo::Remote<media::mojom::Renderer> remote_renderer); + explicit RendererRpcCallTranslator(RpcMessageProcessor processor, + media::mojom::Renderer* renderer); ~RendererRpcCallTranslator() override; // Sets the |handle| to be used for future outgoing RPC calls. @@ -87,7 +86,7 @@ mojo::AssociatedReceiver<media::mojom::RendererClient> renderer_client_receiver_; - mojo::Remote<media::mojom::Renderer> renderer_remote_; + media::mojom::Renderer* renderer_; openscreen::cast::RpcMessenger::Handle handle_ = openscreen::cast::RpcMessenger::kInvalidHandle;
diff --git a/components/certificate_transparency/chrome_require_ct_delegate.cc b/components/certificate_transparency/chrome_require_ct_delegate.cc index 28b2ef4..e806fbd 100644 --- a/components/certificate_transparency/chrome_require_ct_delegate.cc +++ b/components/certificate_transparency/chrome_require_ct_delegate.cc
@@ -229,7 +229,7 @@ // Scheme and port are ignored by the policy, so it's OK to construct a // new GURL here. However, |hostname| is in network form, not URL form, // so it's necessary to wrap IPv6 addresses in brackets. - std::set<url_matcher::URLMatcherConditionSet::ID> matching_ids = + std::set<base::MatcherStringPattern::ID> matching_ids = url_matcher_->MatchURL( GURL("https://" + net::HostPortPair(hostname, 443).HostForURL())); if (matching_ids.empty())
diff --git a/components/certificate_transparency/chrome_require_ct_delegate.h b/components/certificate_transparency/chrome_require_ct_delegate.h index 8b3fed9b..e3865b7 100644 --- a/components/certificate_transparency/chrome_require_ct_delegate.h +++ b/components/certificate_transparency/chrome_require_ct_delegate.h
@@ -96,8 +96,8 @@ bool FilterTakesPrecedence(const Filter& lhs, const Filter& rhs) const; std::unique_ptr<url_matcher::URLMatcher> url_matcher_; - url_matcher::URLMatcherConditionSet::ID next_id_; - std::map<url_matcher::URLMatcherConditionSet::ID, Filter> filters_; + base::MatcherStringPattern::ID next_id_; + std::map<base::MatcherStringPattern::ID, Filter> filters_; // Both SPKI lists are sorted. net::HashValueVector spkis_;
diff --git a/components/certificate_transparency/tools/PRESUBMIT.py b/components/certificate_transparency/tools/PRESUBMIT.py index df9579e..0da9174 100644 --- a/components/certificate_transparency/tools/PRESUBMIT.py +++ b/components/certificate_transparency/tools/PRESUBMIT.py
@@ -19,12 +19,14 @@ test_path = input_api.os_path.join(input_api.PresubmitLocalPath(), 'make_ct_known_logs_list_unittest.py') cmd_name = 'make_ct_known_logs_list_unittest' - cmd = [input_api.python_executable, test_path] + cmd = [test_path] test_cmd = input_api.Command( name=cmd_name, cmd=cmd, kwargs={}, - message=output_api.PresubmitPromptWarning) + message=output_api.PresubmitPromptWarning, + python3=True + ) return input_api.RunTests([test_cmd])
diff --git a/components/enterprise/content/clipboard_restriction_service.h b/components/enterprise/content/clipboard_restriction_service.h index 113cda9..01f3081 100644 --- a/components/enterprise/content/clipboard_restriction_service.h +++ b/components/enterprise/content/clipboard_restriction_service.h
@@ -51,7 +51,7 @@ PrefChangeRegistrar pref_change_registrar_; PrefService* pref_service_; - url_matcher::URLMatcherConditionSet::ID next_id_; + base::MatcherStringPattern::ID next_id_; std::unique_ptr<url_matcher::URLMatcher> enable_url_matcher_; std::unique_ptr<url_matcher::URLMatcher> disable_url_matcher_;
diff --git a/components/exo/shell_surface_base.cc b/components/exo/shell_surface_base.cc index 4a781677..e8d3fe7 100644 --- a/components/exo/shell_surface_base.cc +++ b/components/exo/shell_surface_base.cc
@@ -635,12 +635,21 @@ void ShellSurfaceBase::SetRestoreInfo(int32_t restore_session_id, int32_t restore_window_id) { + // TODO(crbug.com/1327490): Rename restore info variables. // Restore information must be set before widget is created. DCHECK(!widget_); restore_session_id_.emplace(restore_session_id); restore_window_id_.emplace(restore_window_id); } +void ShellSurfaceBase::SetRestoreInfoWithWindowIdSource( + int32_t restore_session_id, + const std::string& restore_window_id_source) { + restore_session_id_.emplace(restore_session_id); + if (!restore_window_id_source.empty()) + restore_window_id_source_.emplace(restore_window_id_source); +} + void ShellSurfaceBase::SetDisplay(int64_t display_id) { TRACE_EVENT1("exo", "ShellSurfaceBase::SetDisplay", "display_id", display_id); @@ -1292,6 +1301,13 @@ params.init_properties_container.SetProperty( app_restore::kRestoreWindowIdKey, *restore_window_id_); } + if (restore_window_id_source_) { + params.init_properties_container.SetProperty( + app_restore::kRestoreWindowIdKey, + app_restore::FetchRestoreWindowId(*restore_window_id_source_)); + params.init_properties_container.SetProperty( + app_restore::kAppIdKey, restore_window_id_source_.value()); + } #if BUILDFLAG(IS_CHROMEOS_ASH) // Restore `params` to those of the saved `restore_window_id_`.
diff --git a/components/exo/shell_surface_base.h b/components/exo/shell_surface_base.h index 62aeaaf..192eefe3 100644 --- a/components/exo/shell_surface_base.h +++ b/components/exo/shell_surface_base.h
@@ -162,6 +162,12 @@ // session id and restore id, respectively. void SetRestoreInfo(int32_t restore_id, int32_t restore_window_id); + // Set `restore_window_id_source` to be the app id for Restore to fetch window + // id for. + void SetRestoreInfoWithWindowIdSource( + int32_t restore_id, + const std::string& restore_window_id_source); + // Returns a trace value representing the state of the surface. std::unique_ptr<base::trace_event::TracedValue> AsTracedValue() const; @@ -447,8 +453,13 @@ bool pending_pip_ = false; bool in_extended_drag_ = false; absl::optional<std::string> initial_workspace_; + + // Restore members. These pass window restore related ids from exo clients, + // e.g. Lacros, so that the window can be created with the correct restore + // info looked up using the ids. absl::optional<int32_t> restore_session_id_; absl::optional<int32_t> restore_window_id_; + absl::optional<std::string> restore_window_id_source_; // Overlay members. std::unique_ptr<views::Widget> overlay_widget_;
diff --git a/components/exo/shell_surface_unittest.cc b/components/exo/shell_surface_unittest.cc index 79c61e6c..f865ac0 100644 --- a/components/exo/shell_surface_unittest.cc +++ b/components/exo/shell_surface_unittest.cc
@@ -2263,6 +2263,7 @@ expected.ClampToCenteredSize(size); EXPECT_EQ(expected, shell_surface->GetWidget()->GetWindowBoundsInScreen()); } + // Test that restore info is set correctly. TEST_F(ShellSurfaceTest, SetRestoreInfo) { int32_t restore_session_id = 200; @@ -2285,6 +2286,29 @@ app_restore::kRestoreWindowIdKey)); } +// Test that restore id is set correctly. +TEST_F(ShellSurfaceTest, SetRestoreInfoWithWindowIdSource) { + int32_t restore_session_id = 200; + const std::string app_id = "app_id"; + + gfx::Size size(20, 30); + auto shell_surface = + test::ShellSurfaceBuilder(size).SetNoCommit().BuildShellSurface(); + + shell_surface->SetRestoreInfoWithWindowIdSource(restore_session_id, app_id); + shell_surface->Restore(); + shell_surface->root_surface()->Commit(); + + EXPECT_TRUE(shell_surface->GetWidget()->IsVisible()); + + // FetchRestoreWindowId will return 0, because no app with id "app_id" is + // installed. + EXPECT_EQ(0, shell_surface->GetWidget()->GetNativeWindow()->GetProperty( + app_restore::kRestoreWindowIdKey)); + EXPECT_EQ(app_id, *shell_surface->GetWidget()->GetNativeWindow()->GetProperty( + app_restore::kAppIdKey)); +} + // Surfaces without non-client view should not crash. TEST_F(ShellSurfaceTest, NoNonClientViewWithConfigure) { // Popup windows don't have a non-client view.
diff --git a/components/exo/test/exo_test_suite_aura.cc b/components/exo/test/exo_test_suite_aura.cc index a3aa9d9..e00ed4c 100644 --- a/components/exo/test/exo_test_suite_aura.cc +++ b/components/exo/test/exo_test_suite_aura.cc
@@ -16,11 +16,11 @@ void ExoTestSuiteAura::Initialize() { base::TestSuite::Initialize(); - gl::GLSurfaceTestSupport::InitializeOneOff(); + display_ = gl::GLSurfaceTestSupport::InitializeOneOff(); } void ExoTestSuiteAura::Shutdown() { - gl::GLSurfaceTestSupport::ShutdownGL(); + gl::GLSurfaceTestSupport::ShutdownGL(display_); base::TestSuite::Shutdown(); }
diff --git a/components/exo/test/exo_test_suite_aura.h b/components/exo/test/exo_test_suite_aura.h index 8217346..37cbbd7 100644 --- a/components/exo/test/exo_test_suite_aura.h +++ b/components/exo/test/exo_test_suite_aura.h
@@ -6,6 +6,7 @@ #define COMPONENTS_EXO_TEST_EXO_TEST_SUITE_AURA_H_ #include "base/test/test_suite.h" +#include "ui/gl/gl_display.h" namespace exo { namespace test { @@ -27,6 +28,9 @@ // base::TestSuite: void Initialize() override; void Shutdown() override; + + private: + gl::GLDisplay* display_ = nullptr; }; } // namespace test
diff --git a/components/exo/wayland/protocol/aura-shell.xml b/components/exo/wayland/protocol/aura-shell.xml index 8a4bffb..44ae6eae 100644 --- a/components/exo/wayland/protocol/aura-shell.xml +++ b/components/exo/wayland/protocol/aura-shell.xml
@@ -24,7 +24,7 @@ DEALINGS IN THE SOFTWARE. </copyright> - <interface name="zaura_shell" version="31"> + <interface name="zaura_shell" version="32"> <description summary="aura_shell"> The global interface exposing aura shell capabilities is used to instantiate an interface extension for a wl_surface object. @@ -616,7 +616,7 @@ </event> </interface> - <interface name="zaura_toplevel" version="31"> + <interface name="zaura_toplevel" version="32"> <description summary="aura shell interface to the toplevel shell"> An interface to the toplevel shell, which allows the client to access shell specific functionality. @@ -710,7 +710,7 @@ This is not double buffered and must be set before sending first commit. </description> <arg name="restore_session_id" type="int" summary="unique browser session id"/> - <arg name="restore_window_id" type="int" summary="restore browser window id "/> + <arg name="restore_window_id" type="int" summary="restore browser window id"/> </request> <request name="set_system_modal" since="31"> @@ -728,6 +728,14 @@ unset. The compositor will then allow other windows to recieve events. </description> </request> + + <request name="set_restore_info_with_window_id_source" since="32"> + <description summary="set session id and restore window id source"> + Request session id and restore id of the window. Set the information used by compositor to restore the toplevel surface state, such as window position, window state, upon creation. This is not double buffered and must be set before sending first commit. This is different from set_restore_info, used for clients that create multiple windows associated with restore_id_source. + </description> + <arg name="restore_session_id" type="int" summary="unique browser session id"/> + <arg name="restore_window_id_source" type="string" summary="restore window id source"/> + </request> </interface> <interface name="zaura_popup" version="28">
diff --git a/components/exo/wayland/zaura_shell.cc b/components/exo/wayland/zaura_shell.cc index ea481e07..6b463be 100644 --- a/components/exo/wayland/zaura_shell.cc +++ b/components/exo/wayland/zaura_shell.cc
@@ -709,6 +709,13 @@ shell_surface_->SetRestoreInfo(restore_session_id, restore_window_id); } +void AuraToplevel::SetRestoreInfoWithWindowIdSource( + int32_t restore_session_id, + const std::string& restore_window_id_source) { + shell_surface_->SetRestoreInfoWithWindowIdSource(restore_session_id, + restore_window_id_source); +} + void AuraToplevel::OnOriginChange(const gfx::Point& origin) { zaura_toplevel_send_origin_change(aura_toplevel_resource_, origin.x(), origin.y()); @@ -1080,6 +1087,15 @@ GetUserDataAs<AuraToplevel>(resource)->SetSystemModal(false); } +void aura_toplevel_set_restore_info_with_window_id_source( + wl_client* client, + wl_resource* resource, + int32_t restore_session_id, + const char* restore_window_id_source) { + GetUserDataAs<AuraToplevel>(resource)->SetRestoreInfoWithWindowIdSource( + restore_session_id, restore_window_id_source); +} + const struct zaura_toplevel_interface aura_toplevel_implementation = { aura_toplevel_set_orientation_lock, aura_toplevel_surface_submission_in_pixel_coordinates, @@ -1088,6 +1104,7 @@ aura_toplevel_set_restore_info, aura_toplevel_set_system_modal, aura_toplevel_unset_system_modal, + aura_toplevel_set_restore_info_with_window_id_source, }; void aura_popup_surface_submission_in_pixel_coordinates(wl_client* client,
diff --git a/components/exo/wayland/zaura_shell.h b/components/exo/wayland/zaura_shell.h index dda9f92..46d8cab9 100644 --- a/components/exo/wayland/zaura_shell.h +++ b/components/exo/wayland/zaura_shell.h
@@ -24,7 +24,7 @@ namespace wayland { class SerialTracker; -constexpr uint32_t kZAuraShellVersion = 31; +constexpr uint32_t kZAuraShellVersion = 32; // Adds bindings to the Aura Shell. Normally this implies Ash on ChromeOS // builds. On non-ChromeOS builds the protocol provides access to Aura windowing @@ -118,6 +118,9 @@ void SetClientUsesScreenCoordinates(); void SetWindowBounds(int32_t x, int32_t y, int32_t width, int32_t height); void SetRestoreInfo(int32_t restore_session_id, int32_t restore_window_id); + void SetRestoreInfoWithWindowIdSource( + int32_t restore_session_id, + const std::string& restore_window_id_source); void SetSystemModal(bool modal); void OnConfigure(const gfx::Rect& bounds,
diff --git a/components/external_intents/android/java/src/org/chromium/components/external_intents/ExternalNavigationHandler.java b/components/external_intents/android/java/src/org/chromium/components/external_intents/ExternalNavigationHandler.java index 1a5b4b9f..c4f7fc5 100644 --- a/components/external_intents/android/java/src/org/chromium/components/external_intents/ExternalNavigationHandler.java +++ b/components/external_intents/android/java/src/org/chromium/components/external_intents/ExternalNavigationHandler.java
@@ -650,13 +650,15 @@ * the link is not an incoming intent from another application, unless it's a redirect. */ private boolean preferToShowIntentPicker(ExternalNavigationParams params, - int pageTransitionCore, boolean isExternalProtocol, boolean isFormSubmit, - boolean incomingIntentRedirect, boolean isFromIntent, + boolean isExternalProtocol, boolean incomingIntentRedirect, QueryIntentActivitiesSupplier resolveInfos) { + int pageTransitionCore = params.getPageTransition() & PageTransition.CORE_MASK; + boolean isFormSubmit = pageTransitionCore == PageTransition.FORM_SUBMIT; + // https://crbug.com/1232514: On Android S, since WebAPKs aren't verified apps they are // never launched as the result of a suitable Intent, the user's default browser will be // opened instead. As a temporary solution, have Chrome launch the WebAPK. - if (isFromIntent && mDelegate.shouldLaunchWebApksOnInitialIntent()) { + if (params.isFromIntent() && mDelegate.shouldLaunchWebApksOnInitialIntent()) { boolean suitableWebApk = pickWebApkIfSoleIntentHandler(resolveInfos.get()) != null; if (suitableWebApk) return true; } @@ -977,13 +979,13 @@ * within the same host, keep the navigation inside the browser unless the set of * available apps to handle the new navigation is different. http://crbug.com/463138 */ - private boolean shouldStayWithinHost(ExternalNavigationParams params, boolean isLink, - boolean isFormSubmit, List<ResolveInfo> resolvingInfos, boolean isExternalProtocol) { - if (isExternalProtocol) return false; + private boolean shouldStayWithinHost(ExternalNavigationParams params, + List<ResolveInfo> resolvingInfos, boolean isExternalProtocol) { + if (isExternalProtocol || !params.isRendererInitiated()) return false; GURL previousUrl = getLastCommittedUrl(); if (previousUrl == null) previousUrl = params.getReferrerUrl(); - if (previousUrl.isEmpty() || (!isLink && !isFormSubmit)) return false; + if (previousUrl.isEmpty()) return false; GURL currentUrl = params.getUrl(); @@ -1361,11 +1363,6 @@ if (isUnhandledWtaiProtocol(params)) return OverrideUrlLoadingResult.forNoOverride(); - int pageTransitionCore = params.getPageTransition() & PageTransition.CORE_MASK; - boolean isLink = params.isLinkTransition(); - boolean isFromIntent = params.isFromIntent(); - boolean isFormSubmit = pageTransitionCore == PageTransition.FORM_SUBMIT; - if (redirectShouldStayInApp(params, isExternalProtocol, targetIntent)) { return OverrideUrlLoadingResult.forNoOverride(); } @@ -1373,8 +1370,8 @@ Intent debugIntent = new Intent(targetIntent); QueryIntentActivitiesSupplier resolvingInfos = new QueryIntentActivitiesSupplier(targetIntent); - if (!preferToShowIntentPicker(params, pageTransitionCore, isExternalProtocol, isFormSubmit, - incomingIntentRedirect, isFromIntent, resolvingInfos)) { + if (!preferToShowIntentPicker( + params, isExternalProtocol, incomingIntentRedirect, resolvingInfos)) { return OverrideUrlLoadingResult.forNoOverride(); } @@ -1403,8 +1400,7 @@ // From this point on we should only have URLs from intent URIs, or URLs for // apps with specialized handlers (including custom schemes). - if (shouldStayWithinHost( - params, isLink, isFormSubmit, resolvingInfos.get(), isExternalProtocol)) { + if (shouldStayWithinHost(params, resolvingInfos.get(), isExternalProtocol)) { return OverrideUrlLoadingResult.forNoOverride(); }
diff --git a/components/feed/core/v2/tasks/load_stream_from_store_task.cc b/components/feed/core/v2/tasks/load_stream_from_store_task.cc index a0070792..d6ee630 100644 --- a/components/feed/core/v2/tasks/load_stream_from_store_task.cc +++ b/components/feed/core/v2/tasks/load_stream_from_store_task.cc
@@ -69,16 +69,8 @@ return; } if (!ignore_account_) { - const AccountInfo& account_info = feed_stream_.GetAccountInfo(); - if (result.stream_data.signed_in() && result.stream_data.gaia().empty()) { - // TODO(crbug.com/1268575): For backward compatibility, set the gaia in - // stream_data if it is unset. Remove this code after it's been in at - // least one Chrome release. - result.stream_data.set_gaia(account_info.gaia); - result.stream_data.set_email(account_info.email); - } - if (result.stream_data.signed_in()) { + const AccountInfo& account_info = feed_stream_.GetAccountInfo(); if (result.stream_data.gaia() != account_info.gaia || result.stream_data.email() != account_info.email) { Complete(LoadStreamStatus::kDataInStoreIsForAnotherUser,
diff --git a/components/feed/core/v2/web_feed_subscriptions/web_feed_index.cc b/components/feed/core/v2/web_feed_subscriptions/web_feed_index.cc index 58c74d62..16ad555 100644 --- a/components/feed/core/v2/web_feed_subscriptions/web_feed_index.cc +++ b/components/feed/core/v2/web_feed_subscriptions/web_feed_index.cc
@@ -34,7 +34,7 @@ return host_suffix < other_host_suffix; } std::string host_suffix; - int condition_id; + base::MatcherStringPattern::ID condition_id; }; explicit HostSuffixMatcher(std::vector<Entry> entries) { @@ -44,7 +44,8 @@ // Find all host suffixes that match `host_string`, and add the match IDs to // `match_set`. - void FindMatches(const std::string& host_string, std::set<int>& match_set) { + void FindMatches(const std::string& host_string, + std::set<base::MatcherStringPattern::ID>& match_set) { base::StringPiece host(host_string); if (host.empty()) return; @@ -62,7 +63,8 @@ } private: - void FindExactMatches(base::StringPiece prefix, std::set<int>& match_set) { + void FindExactMatches(base::StringPiece prefix, + std::set<base::MatcherStringPattern::ID>& match_set) { auto iter = std::lower_bound(entries_.begin(), entries_.end(), prefix); while (iter != entries_.end() && iter->host_suffix == prefix) { match_set.insert(iter->condition_id); @@ -73,7 +75,7 @@ std::vector<Entry> entries_; }; -constexpr int kFirstConditionId = 1; +constexpr base::MatcherStringPattern::ID kFirstConditionId = 1; // References a set of conditions that all must be true to identify a Web Feed. // Conditions in this context are represented by unique integer IDs. Each @@ -111,7 +113,8 @@ // Search each 'vertical slice' independently: page URL, page host suffix, // page suffix, and RSS URL. Collect set of matching IDs. - std::set<int> matching_ids = page_url_matcher_.MatchURL(page_url); + std::set<base::MatcherStringPattern::ID> matching_ids = + page_url_matcher_.MatchURL(page_url); for (const GURL& rss_url : rss_urls) { auto ids = rss_url_matcher_.MatchURL(rss_url); matching_ids.insert(ids.begin(), ids.end()); @@ -128,7 +131,7 @@ // scan is easier and likely pretty fast since condition_sets_ is dense and // flat. - int mcs_first_condition_id = kFirstConditionId; + base::MatcherStringPattern::ID mcs_first_condition_id = kFirstConditionId; int matches_for_mcs = 0; auto match_iter = matching_ids.begin(); for (size_t i = 0; @@ -376,7 +379,7 @@ std::unique_ptr<EntrySet> entry_set_ = std::make_unique<EntrySet>(); // Temporary state for building `entry_set_`. - int next_condition_set_id = kFirstConditionId; + base::MatcherStringPattern::ID next_condition_set_id = kFirstConditionId; std::vector<scoped_refptr<url_matcher::URLMatcherConditionSet>> page_conditions_; std::vector<scoped_refptr<url_matcher::URLMatcherConditionSet>>
diff --git a/components/messages/android/internal/java/src/org/chromium/components/messages/MessageBannerCoordinator.java b/components/messages/android/internal/java/src/org/chromium/components/messages/MessageBannerCoordinator.java index d299f8c..22ee419 100644 --- a/components/messages/android/internal/java/src/org/chromium/components/messages/MessageBannerCoordinator.java +++ b/components/messages/android/internal/java/src/org/chromium/components/messages/MessageBannerCoordinator.java
@@ -8,8 +8,6 @@ import android.content.res.Resources; import androidx.annotation.VisibleForTesting; -import androidx.core.view.ViewCompat; -import androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat; import org.chromium.base.Callback; import org.chromium.base.annotations.MockedInTests; @@ -60,11 +58,6 @@ mTimer = new MessageAutoDismissTimer(); mOnTimeUp = onTimeUp; view.setSwipeHandler(mMediator); - ViewCompat.replaceAccessibilityAction( - view, AccessibilityActionCompat.ACTION_DISMISS, null, (v, c) -> { - messageDismissed.run(); - return false; - }); view.setPopupMenuShownListener( createPopupMenuShownListener(mTimer, mAutodismissDurationMs.get(), mOnTimeUp)); } @@ -122,6 +115,14 @@ }); } + void cancelTimer() { + mTimer.cancelTimer(); + } + + void startTimer() { + mTimer.startTimer(mAutodismissDurationMs.get(), mOnTimeUp); + } + void setOnTouchRunnable(Runnable runnable) { mMediator.setOnTouchRunnable(runnable); }
diff --git a/components/messages/android/internal/java/src/org/chromium/components/messages/SingleActionMessage.java b/components/messages/android/internal/java/src/org/chromium/components/messages/SingleActionMessage.java index 09b481d..d8417f0 100644 --- a/components/messages/android/internal/java/src/org/chromium/components/messages/SingleActionMessage.java +++ b/components/messages/android/internal/java/src/org/chromium/components/messages/SingleActionMessage.java
@@ -15,12 +15,13 @@ import org.chromium.base.Callback; import org.chromium.base.supplier.BooleanSupplier; import org.chromium.base.supplier.Supplier; +import org.chromium.components.messages.MessageContainer.MessageContainerA11yDelegate; import org.chromium.ui.modelutil.PropertyModel; /** * Coordinator to show / hide a banner message on given container and delegate events. */ -public class SingleActionMessage implements MessageStateHandler { +public class SingleActionMessage implements MessageStateHandler, MessageContainerA11yDelegate { /** * The interface that consumers of SingleActionMessage should implement to receive notification * that the message was dismissed. @@ -98,6 +99,7 @@ () -> { mDismissHandler.invoke(mModel, DismissReason.TIMER); }); } mContainer.addMessage(mView); + mContainer.setA11yDelegate(this); // Wait until the message and the container are measured before showing the message. This // is required in case the animation set-up requires the height of the container, e.g. @@ -150,6 +152,22 @@ } return true; } + + @Override + public void onA11yFocused() { + mMessageBanner.cancelTimer(); + } + + @Override + public void onA11yFocusCleared() { + mMessageBanner.startTimer(); + } + + @Override + public void onA11yDismiss() { + mDismissHandler.invoke(mModel, DismissReason.GESTURE); + } + private void handlePrimaryAction(View v) { // Avoid running the primary action callback if the message has already been dismissed. if (mMessageDismissed) return;
diff --git a/components/messages/android/java/src/org/chromium/components/messages/MessageContainer.java b/components/messages/android/java/src/org/chromium/components/messages/MessageContainer.java index 2ae4c6c6..64465d6 100644 --- a/components/messages/android/java/src/org/chromium/components/messages/MessageContainer.java +++ b/components/messages/android/java/src/org/chromium/components/messages/MessageContainer.java
@@ -8,10 +8,13 @@ import android.util.AttributeSet; import android.view.View; import android.view.ViewGroup; +import android.view.accessibility.AccessibilityEvent; import android.widget.FrameLayout; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.core.view.ViewCompat; +import androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat; import org.chromium.base.TraceEvent; import org.chromium.ui.base.ViewUtils; @@ -20,10 +23,38 @@ * Container holding messages. */ public class MessageContainer extends FrameLayout { + interface MessageContainerA11yDelegate { + void onA11yFocused(); + void onA11yFocusCleared(); + void onA11yDismiss(); + } + private View mMessageBannerView; + private MessageContainerA11yDelegate mA11yDelegate; public MessageContainer(@NonNull Context context, @Nullable AttributeSet attrs) { super(context, attrs); + setAccessibilityDelegate(new AccessibilityDelegate() { + @Override + public void onInitializeAccessibilityEvent( + @NonNull View host, @NonNull AccessibilityEvent event) { + if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED) { + if (mA11yDelegate != null) mA11yDelegate.onA11yFocused(); + } else if (event.getEventType() + == AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED) { + if (mA11yDelegate != null) mA11yDelegate.onA11yFocusCleared(); + } + super.onInitializeAccessibilityEvent(host, event); + } + }); + ViewCompat.replaceAccessibilityAction( + this, AccessibilityActionCompat.ACTION_DISMISS, null, (v, c) -> { + if (mA11yDelegate != null) { + mA11yDelegate.onA11yDismiss(); + return true; + } + return false; + }); } /** @@ -54,6 +85,7 @@ ViewUtils.setAncestorsShouldClipChildren(this, true); removeAllViews(); mMessageBannerView = null; + mA11yDelegate = null; } public int getMessageBannerHeight() { @@ -72,6 +104,10 @@ } } + void setA11yDelegate(MessageContainerA11yDelegate a11yDelegate) { + mA11yDelegate = a11yDelegate; + } + /** * Runs a {@link Runnable} after the message's initial layout. If the view is already laid out, * the {@link Runnable} will be called immediately.
diff --git a/components/mirroring/service/mirroring_features.cc b/components/mirroring/service/mirroring_features.cc index 28df046..a10354a 100644 --- a/components/mirroring/service/mirroring_features.cc +++ b/components/mirroring/service/mirroring_features.cc
@@ -8,11 +8,6 @@ namespace mirroring { namespace features { -// Controls whether the Open Screen libcast SenderSession is used for -// initializing and managing streaming sessions, or the legacy implementation. -const base::Feature kOpenscreenCastStreamingSession{ - "OpenscreenCastStreamingSession", base::FEATURE_DISABLED_BY_DEFAULT}; - // Controls whether offers using the AV1 codec for video encoding are included // in mirroring negotiations in addition to the VP8 codec, or offers only // include VP8.
diff --git a/components/mirroring/service/remoting_sender.cc b/components/mirroring/service/remoting_sender.cc index e20da5d..5500c51 100644 --- a/components/mirroring/service/remoting_sender.cc +++ b/components/mirroring/service/remoting_sender.cc
@@ -25,10 +25,10 @@ mojo::ScopedDataPipeConsumerHandle pipe, mojo::PendingReceiver<media::mojom::RemotingDataStreamSender> stream_sender, base::OnceClosure error_callback) - : FrameSender(cast_environment, - transport, - config, - media::cast::NewFixedCongestionControl(config.max_bitrate)), + : frame_sender_(media::cast::FrameSender::Create(cast_environment, + config, + transport, + this)), clock_(cast_environment->Clock()), error_callback_(std::move(error_callback)), data_pipe_reader_(new media::MojoDataPipeReader(std::move(pipe))), @@ -67,14 +67,13 @@ return 0; } -base::TimeDelta RemotingSender::GetInFlightMediaDuration() const { +base::TimeDelta RemotingSender::GetEncoderBacklogDuration() const { NOTREACHED(); return base::TimeDelta(); } -void RemotingSender::OnCancelSendingFrames() { - // One or more frames were canceled. This may allow pending input operations - // to complete. +void RemotingSender::OnFrameCanceled(media::cast::FrameId frame_id) { + // The frame cancellation may allow for the next input task to complete. ProcessNextInputTask(); } @@ -121,40 +120,41 @@ } // If there would be too many frames in-flight, do not proceed. - if (GetUnacknowledgedFrameCount() >= media::cast::kMaxUnackedFrames) { + if (frame_sender_->GetUnacknowledgedFrameCount() >= + media::cast::kMaxUnackedFrames) { VLOG(1) << "Cannot send frame now because too many frames are in flight."; return; } - const bool is_first_frame_to_be_sent = last_send_time_.is_null(); - const media::cast::FrameId frame_id = is_first_frame_to_be_sent - ? media::cast::FrameId::first() - : (last_sent_frame_id_ + 1); - - base::TimeTicks last_frame_reference_time = last_send_time_; + const bool is_first_frame = (next_frame_id_ == media::cast::FrameId::first()); auto remoting_frame = std::make_unique<media::cast::SenderEncodedFrame>(); - remoting_frame->frame_id = frame_id; + remoting_frame->frame_id = next_frame_id_; if (flow_restart_pending_) { remoting_frame->dependency = media::cast::EncodedFrame::KEY; flow_restart_pending_ = false; } else { - DCHECK(!is_first_frame_to_be_sent); + DCHECK(!is_first_frame); remoting_frame->dependency = media::cast::EncodedFrame::DEPENDENT; } remoting_frame->referenced_frame_id = remoting_frame->dependency == media::cast::EncodedFrame::KEY - ? frame_id - : frame_id - 1; + ? next_frame_id_ + : next_frame_id_ - 1; remoting_frame->reference_time = clock_->NowTicks(); remoting_frame->encode_completion_time = remoting_frame->reference_time; + + base::TimeTicks last_frame_reference_time; media::cast::RtpTimeTicks last_frame_rtp_timestamp; - if (is_first_frame_to_be_sent) { + if (is_first_frame) { last_frame_reference_time = remoting_frame->reference_time; last_frame_rtp_timestamp = media::cast::RtpTimeTicks() - media::cast::RtpTimeDelta::FromTicks(1); } else { - last_frame_rtp_timestamp = GetRecordedRtpTimestamp(frame_id - 1); + last_frame_reference_time = frame_sender_->LastSendTime(); + last_frame_rtp_timestamp = + frame_sender_->GetRecordedRtpTimestamp(next_frame_id_ - 1); } + // Ensure each successive frame's RTP timestamp is unique, but otherwise just // base it on the reference time. remoting_frame->rtp_timestamp = @@ -165,8 +165,8 @@ media::cast::kRemotingRtpTimebase)); remoting_frame->data.swap(next_frame_data_); - SendEncodedFrame(0, std::move(remoting_frame)); - + frame_sender_->EnqueueFrame(std::move(remoting_frame)); + next_frame_id_++; OnInputTaskComplete(); }
diff --git a/components/mirroring/service/remoting_sender.h b/components/mirroring/service/remoting_sender.h index fedf825d..2328a8b 100644 --- a/components/mirroring/service/remoting_sender.h +++ b/components/mirroring/service/remoting_sender.h
@@ -33,7 +33,7 @@ // a CastTransport. class COMPONENT_EXPORT(MIRRORING_SERVICE) RemotingSender final : public media::mojom::RemotingDataStreamSender, - public media::cast::FrameSender { + public media::cast::FrameSender::Client { public: // |transport| is expected to outlive this class. RemotingSender(scoped_refptr<media::cast::CastEnvironment> cast_environment, @@ -62,10 +62,10 @@ void SendFrame(uint32_t frame_size) override; void CancelInFlightData() override; - // FrameSender override. + // FrameSender::Client overrides. int GetNumberOfFramesInEncoder() const override; - base::TimeDelta GetInFlightMediaDuration() const override; - void OnCancelSendingFrames() override; + base::TimeDelta GetEncoderBacklogDuration() const override; + void OnFrameCanceled(media::cast::FrameId frame_id) override; // Attempt to run next pending input task, popping the head of the input queue // as each task succeeds. @@ -90,6 +90,9 @@ SEQUENCE_CHECKER(sequence_checker_); + // The backing frame sender implementation. + std::unique_ptr<media::cast::FrameSender> frame_sender_; + raw_ptr<const base::TickClock> clock_; // Callback that is run to notify when a fatal error occurs. @@ -120,6 +123,10 @@ // to mark the next frame as the start of a new sequence. bool flow_restart_pending_; + // The next frame's ID. Before any frames are sent, this will be the ID of + // the first frame. + media::cast::FrameId next_frame_id_ = media::cast::FrameId::first(); + // NOTE: Weak pointers must be invalidated before all other member variables. base::WeakPtrFactory<RemotingSender> weak_factory_{this}; };
diff --git a/components/mirroring/service/remoting_sender_unittest.cc b/components/mirroring/service/remoting_sender_unittest.cc index 903288b..1fdf403 100644 --- a/components/mirroring/service/remoting_sender_unittest.cc +++ b/components/mirroring/service/remoting_sender_unittest.cc
@@ -141,7 +141,8 @@ // Give CastRemotingSender a small RTT measurement to prevent kickstart // testing from taking too long. - remoting_sender_->OnMeasuredRoundTripTime(base::Milliseconds(1)); + remoting_sender_->frame_sender_->OnMeasuredRoundTripTime( + base::Milliseconds(1)); RunPendingTasks(); } @@ -158,11 +159,11 @@ protected: media::cast::FrameId latest_acked_frame_id() const { - return remoting_sender_->latest_acked_frame_id_; + return remoting_sender_->frame_sender_->LatestAckedFrameId(); } int NumberOfFramesInFlight() { - return remoting_sender_->GetUnacknowledgedFrameCount(); + return remoting_sender_->frame_sender_->GetUnacknowledgedFrameCount(); } size_t GetSizeOfNextFrameData() { @@ -203,7 +204,7 @@ void AckUpToAndIncluding(media::cast::FrameId frame_id) { media::cast::RtcpCastMessage cast_feedback(receiver_ssrc_); cast_feedback.ack_frame_id = frame_id; - remoting_sender_->OnReceivedCastFeedback(cast_feedback); + remoting_sender_->frame_sender_->OnReceivedCastFeedback(cast_feedback); } void AckOldestInFlightFrames(int count) {
diff --git a/components/optimization_guide/core/store_update_data.cc b/components/optimization_guide/core/store_update_data.cc index d5c636ef..056a5be 100644 --- a/components/optimization_guide/core/store_update_data.cc +++ b/components/optimization_guide/core/store_update_data.cc
@@ -150,7 +150,7 @@ expiry_duration = base::Seconds(prediction_model.model_info().valid_duration().seconds()); } else { - expiry_duration = features::StoredFetchedHintsFreshnessDuration(); + expiry_duration = features::StoredModelsValidDuration(); } expiry_time_ = base::Time::Now() + expiry_duration; entry_proto.set_expiry_time_secs(
diff --git a/components/optimization_guide/core/store_update_data_unittest.cc b/components/optimization_guide/core/store_update_data_unittest.cc index e36087ea..2e2c1bf 100644 --- a/components/optimization_guide/core/store_update_data_unittest.cc +++ b/components/optimization_guide/core/store_update_data_unittest.cc
@@ -136,7 +136,7 @@ // Verify there is 1 store entry. const auto update_entries = prediction_model_update->TakeUpdateEntries(); EXPECT_EQ(1ul, update_entries->size()); - // Verify expiry time taken from hint rather than the default expiry time of + // Verify expiry time taken from model rather than the default expiry time of // the store update data. bool found_prediction_model_entry = false; for (const auto& entry : *update_entries) { @@ -153,6 +153,44 @@ EXPECT_TRUE(found_prediction_model_entry); } +TEST(StoreUpdateDataTest, DefaultExpiryPredictionModelUpdateData) { + // Verify creating a Prediction Model update data. + proto::PredictionModel prediction_model; + + proto::ModelInfo* model_info = prediction_model.mutable_model_info(); + model_info->set_version(1); + model_info->set_optimization_target( + proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD); + model_info->add_supported_model_engine_versions( + proto::ModelEngineVersion::MODEL_ENGINE_VERSION_DECISION_TREE); + model_info->set_keep_beyond_valid_duration(false); + + std::unique_ptr<StoreUpdateData> prediction_model_update = + StoreUpdateData::CreatePredictionModelStoreUpdateData(base::Time::Now()); + prediction_model_update->CopyPredictionModelIntoUpdateData(prediction_model); + EXPECT_FALSE(prediction_model_update->component_version().has_value()); + EXPECT_FALSE(prediction_model_update->update_time().has_value()); + // Verify there is 1 store entry. + const auto update_entries = prediction_model_update->TakeUpdateEntries(); + EXPECT_EQ(1ul, update_entries->size()); + // Verify expiry time taken from the default expiry time of model. + bool found_prediction_model_entry = false; + for (const auto& entry : *update_entries) { + proto::StoreEntry store_entry = entry.second; + if (store_entry.entry_type() == proto::PREDICTION_MODEL) { + found_prediction_model_entry = true; + base::Time expected_expiry_time = + base::Time::Now() + features::StoredModelsValidDuration(); + EXPECT_EQ(expected_expiry_time.ToDeltaSinceWindowsEpoch().InSeconds(), + store_entry.expiry_time_secs()); + EXPECT_EQ(store_entry.keep_beyond_valid_duration(), + model_info->keep_beyond_valid_duration()); + break; + } + } + EXPECT_TRUE(found_prediction_model_entry); +} + } // namespace } // namespace optimization_guide
diff --git a/components/os_crypt/key_storage_keyring_unittest.cc b/components/os_crypt/key_storage_keyring_unittest.cc index ae92edb..cbd4c72 100644 --- a/components/os_crypt/key_storage_keyring_unittest.cc +++ b/components/os_crypt/key_storage_keyring_unittest.cc
@@ -9,6 +9,7 @@ #include "base/test/test_simple_task_runner.h" #include "build/branding_buildflags.h" +#include "build/build_config.h" #include "components/os_crypt/keyring_util_linux.h" #include "testing/gtest/include/gtest/gtest.h" @@ -138,7 +139,13 @@ MockGnomeKeyringLoader::TearDown(); } -TEST_F(GnomeKeyringTest, KeyringRepeats) { +// crbug.com/1211311 Disable due to persistently failing. +#if BUILDFLAG(GOOGLE_CHROME_BRANDING) && BUILDFLAG(IS_LINUX) +#define MAYBE_KeyringRepeats DISABLED_KeyringRepeats +#else +#define MAYBE_KeyringRepeats KeyringRepeats +#endif +TEST_F(GnomeKeyringTest, MAYBE_KeyringRepeats) { absl::optional<std::string> password = keyring_.GetKey(); EXPECT_TRUE(password.has_value()); EXPECT_FALSE(password.value().empty()); @@ -147,7 +154,13 @@ EXPECT_EQ(password.value(), password_repeat.value()); } -TEST_F(GnomeKeyringTest, KeyringCreatesRandomised) { +// crbug.com/1211311 Disable due to persistently failing. +#if BUILDFLAG(GOOGLE_CHROME_BRANDING) && BUILDFLAG(IS_LINUX) +#define MAYBE_KeyringCreatesRandomised DISABLED_KeyringCreatesRandomised +#else +#define MAYBE_KeyringCreatesRandomised KeyringCreatesRandomised +#endif +TEST_F(GnomeKeyringTest, MAYBE_KeyringCreatesRandomised) { absl::optional<std::string> password = keyring_.GetKey(); MockGnomeKeyringLoader::ResetForOSCrypt(); absl::optional<std::string> password_new = keyring_.GetKey();
diff --git a/components/password_manager/core/browser/sync/password_proto_utils.cc b/components/password_manager/core/browser/sync/password_proto_utils.cc index da2eeae..ccf7b15 100644 --- a/components/password_manager/core/browser/sync/password_proto_utils.cc +++ b/components/password_manager/core/browser/sync/password_proto_utils.cc
@@ -37,6 +37,46 @@ base::Microseconds(time)); } +// Trims the notes field in the sync_pb::PasswordSpecificsData proto. If neither +// the high level notes field nor any of the individual notes contains populated +// fields, the high level field is cleared. +void TrimPasswordSpecificsDataNotesForCaching( + sync_pb::PasswordSpecificsData& trimmed_password_data) { + // `notes` field should be cleared if all notes are empty. + bool non_empty_note_exists = false; + // Iterate over all notes and clear all supported fields. + for (sync_pb::PasswordSpecificsData_Notes_Note& note : + *trimmed_password_data.mutable_notes()->mutable_note()) { + // Remember the `unique_display_name` such that if this note needs to be + // cached, the `unique_display_name` is required to be able reconcile cached + // notes during commit. + std::string unique_display_name = note.unique_display_name(); + note.clear_unique_display_name(); + note.clear_value(); + note.clear_date_created_windows_epoch_micros(); + note.clear_hide_by_default(); + if (note.ByteSizeLong() != 0) { + non_empty_note_exists = true; + // Set the `unique_display_name` since it's required during the + // reconciliation step in PasswordNotesToProto(). + note.set_unique_display_name(unique_display_name); + } + } + if (non_empty_note_exists) { + // Since some of the notes contain populated fields, no more trimming is + // possible. + return; + } else { + trimmed_password_data.mutable_notes()->clear_note(); + } + // None of the individual notes contains populated fields. If the high level + // Notes proto doesn't contain unknown fields either, we should clear the + // notes field when trimming. + if (trimmed_password_data.notes().unknown_fields().empty()) { + trimmed_password_data.clear_notes(); + } +} + } // namespace sync_pb::PasswordSpecificsData_PasswordIssues PasswordIssuesMapToProto( @@ -102,6 +142,52 @@ return form_issues; } +std::vector<PasswordNote> PasswordNotesFromProto( + const sync_pb::PasswordSpecificsData_Notes& notes_proto) { + std::vector<PasswordNote> notes; + for (const sync_pb::PasswordSpecificsData_Notes_Note& note : + notes_proto.note()) { + notes.emplace_back( + base::UTF8ToUTF16(note.unique_display_name()), + base::UTF8ToUTF16(note.value()), + ConvertToBaseTime(note.date_created_windows_epoch_micros()), + note.hide_by_default()); + } + return notes; +} + +sync_pb::PasswordSpecificsData_Notes PasswordNotesToProto( + const std::vector<PasswordNote>& notes, + const sync_pb::PasswordSpecificsData_Notes& base_notes) { + sync_pb::PasswordSpecificsData_Notes notes_proto = base_notes; + for (const PasswordNote& note : notes) { + sync_pb::PasswordSpecificsData_Notes_Note* note_proto = nullptr; + // Try to find a corresponding cached note. Since `unique_display_name` is + // unique per password, and immutable, it can be used to reconcile notes. + // `unique_display_name` is cached in TrimPasswordSpecificsDataForCaching(). + for (sync_pb::PasswordSpecificsData_Notes_Note& cached_note : + *notes_proto.mutable_note()) { + if (cached_note.unique_display_name() == + base::UTF16ToUTF8(note.unique_display_name)) { + note_proto = &cached_note; + break; + } + } + // If no corresponding cached note is found, add a new one. + if (!note_proto) { + note_proto = notes_proto.add_note(); + note_proto->set_unique_display_name( + base::UTF16ToUTF8(note.unique_display_name)); + } + + note_proto->set_value(base::UTF16ToUTF8(note.value)); + note_proto->set_date_created_windows_epoch_micros( + note.date_created.ToDeltaSinceWindowsEpoch().InMicroseconds()); + note_proto->set_hide_by_default(note.hide_by_default); + } + return notes_proto; +} + sync_pb::PasswordSpecificsData TrimPasswordSpecificsDataForCaching( const sync_pb::PasswordSpecificsData& password_specifics_data) { sync_pb::PasswordSpecificsData trimmed_password_data = @@ -124,6 +210,9 @@ trimmed_password_data.clear_date_last_used(); trimmed_password_data.clear_password_issues(); trimmed_password_data.clear_date_password_modified_windows_epoch_micros(); + + TrimPasswordSpecificsDataNotesForCaching(trimmed_password_data); + return trimmed_password_data; } @@ -181,7 +270,8 @@ : password_form.federation_origin.Serialize()); *password_data.mutable_password_issues() = PasswordIssuesMapToProto(password_form.password_issues); - + *password_data.mutable_notes() = + PasswordNotesToProto(password_form.notes, base_password_data.notes()); return password_data; } @@ -273,7 +363,7 @@ password.federation_origin = url::Origin::Create(GURL(password_data.federation_url())); password.password_issues = PasswordIssuesMapFromProto(password_data); - + password.notes = PasswordNotesFromProto(password_data.notes()); return password; }
diff --git a/components/password_manager/core/browser/sync/password_proto_utils.h b/components/password_manager/core/browser/sync/password_proto_utils.h index 159e374..9fbd0e3cc 100644 --- a/components/password_manager/core/browser/sync/password_proto_utils.h +++ b/components/password_manager/core/browser/sync/password_proto_utils.h
@@ -14,6 +14,7 @@ class PasswordSpecifics; class PasswordSpecificsData; class PasswordSpecificsData_PasswordIssues; +class PasswordSpecificsData_Notes; class PasswordWithLocalData; class ListPasswordsResult; } // namespace sync_pb @@ -31,6 +32,19 @@ base::flat_map<InsecureType, InsecurityMetadata> PasswordIssuesMapFromProto( const sync_pb::PasswordSpecificsData& password_data); +// Converts a sync_pb::PasswordSpecificsData_Notes to a +// std::vector<PasswordNote>. +std::vector<PasswordNote> PasswordNotesFromProto( + const sync_pb::PasswordSpecificsData_Notes& notes_proto); + +// Converts a std::vector<PasswordNote> to a +// sync_pb::PasswordSpecificsData_Notes. `base_notes` is intended for carrying +// over unknown and unsupported note fields when there is a local modification +// to an existing sync entity. +sync_pb::PasswordSpecificsData_Notes PasswordNotesToProto( + const std::vector<PasswordNote>& notes, + const sync_pb::PasswordSpecificsData_Notes& base_notes); + // Returns sync_pb::PasswordSpecifics based on given `password_form`. // `base_password_data` is intended for carrying over unknown and unsupported // fields when there is a local modification to an existing sync entity.
diff --git a/components/password_manager/core/browser/sync/password_proto_utils_unittest.cc b/components/password_manager/core/browser/sync/password_proto_utils_unittest.cc index 2f88fa64..5fdcf60 100644 --- a/components/password_manager/core/browser/sync/password_proto_utils_unittest.cc +++ b/components/password_manager/core/browser/sync/password_proto_utils_unittest.cc
@@ -88,6 +88,9 @@ password_specifics.set_federation_url(std::string()); *password_specifics.mutable_password_issues() = CreateSpecificsDataIssues(issue_types); + // The current code always populates notes for outgoing protos even when + // non-exists. + password_specifics.mutable_notes(); return password_specifics; } @@ -106,6 +109,84 @@ Eq(specifics_data.password_issues().SerializeAsString())); } +TEST(PasswordProtoUtilsTest, ConvertPasswordNoteToNotesProtoAndBack) { + std::vector<PasswordNote> notes; + notes.emplace_back(u"unique_display_name", u"value", + /*date_created*/ base::Time::Now(), + /*hide_by_default=*/true); + notes.emplace_back(u"unique_display_name2", u"value2", + /*date_created*/ base::Time::Now() - base::Hours(1), + /*hide_by_default=*/false); + sync_pb::PasswordSpecificsData_Notes base_notes_proto; + EXPECT_EQ(notes, PasswordNotesFromProto( + PasswordNotesToProto(notes, base_notes_proto))); +} + +TEST(PasswordProtoUtilsTest, + CacheNoteUniqueDisplayNameWhenNoteContainsUnknownField) { + const std::string kNoteUniqueDisplayName = "Note Unique Display Name"; + sync_pb::PasswordSpecificsData password_specifics_data; + sync_pb::PasswordSpecificsData_Notes_Note* note = + password_specifics_data.mutable_notes()->add_note(); + note->set_unique_display_name(kNoteUniqueDisplayName); + *note->mutable_unknown_fields() = "unknown_fields"; + sync_pb::PasswordSpecificsData trimmed_specifics = + TrimPasswordSpecificsDataForCaching(password_specifics_data); + // The unique_display_name field should be cached since it's necessary for + // reconciliation of notes with cached ones during commit. + EXPECT_EQ(kNoteUniqueDisplayName, + trimmed_specifics.notes().note(0).unique_display_name()); +} + +TEST(PasswordProtoUtilsTest, ReconcileCachedNotesUsingUnqiueDisplayName) { + const std::string kNoteUniqueDisplayName1 = "Note Unique Display Name 1"; + const std::string kNoteValue1 = "Note Value 1"; + const std::string kNoteUnknownFields1 = "Note Unknown Fields 1"; + const std::string kNoteUniqueDisplayName2 = "Note Unique Display Name 2"; + const std::string kNoteValue2 = "Note Value 2"; + const std::string kNoteUnknownFields2 = "Note Unknown Fields 2"; + + // Create a base note proto that contains two notes with unknown fields. + sync_pb::PasswordSpecificsData_Notes base_notes; + + sync_pb::PasswordSpecificsData_Notes_Note* note_proto1 = + base_notes.add_note(); + note_proto1->set_unique_display_name(kNoteUniqueDisplayName1); + *note_proto1->mutable_unknown_fields() = kNoteUnknownFields1; + + sync_pb::PasswordSpecificsData_Notes_Note* note_proto2 = + base_notes.add_note(); + note_proto2->set_unique_display_name(kNoteUniqueDisplayName2); + *note_proto2->mutable_unknown_fields() = kNoteUnknownFields2; + + // Create the notes to be committed with the same unique display names in the + // base specifics. Notes will be reconciled using the unique display name and + // hence the order shouldn't matter. + std::vector<PasswordNote> notes; + notes.emplace_back(base::UTF8ToUTF16(kNoteUniqueDisplayName2), + base::UTF8ToUTF16(kNoteValue2), + /*date_created=*/base::Time::Now(), + /*hide_by_default=*/true); + notes.emplace_back(base::UTF8ToUTF16(kNoteUniqueDisplayName1), + base::UTF8ToUTF16(kNoteValue1), + /*date_created=*/base::Time::Now(), + /*hide_by_default=*/true); + + // Reconciliation should preserve the order of the notes in the base specifics + // and carry over the known fields. + sync_pb::PasswordSpecificsData_Notes reconciled_notes = + PasswordNotesToProto(notes, base_notes); + EXPECT_EQ(kNoteUniqueDisplayName1, + reconciled_notes.note(0).unique_display_name()); + EXPECT_EQ(kNoteValue1, reconciled_notes.note(0).value()); + EXPECT_EQ(kNoteUnknownFields1, reconciled_notes.note(0).unknown_fields()); + + EXPECT_EQ(kNoteUniqueDisplayName2, + reconciled_notes.note(1).unique_display_name()); + EXPECT_EQ(kNoteValue2, reconciled_notes.note(1).value()); + EXPECT_EQ(kNoteUnknownFields2, reconciled_notes.note(1).unknown_fields()); +} + TEST(PasswordProtoUtilsTest, ConvertSpecificsToFormAndBack) { sync_pb::PasswordSpecifics specifics; *specifics.mutable_client_only_encrypted_data() =
diff --git a/components/password_manager/core/browser/sync/password_sync_bridge.cc b/components/password_manager/core/browser/sync/password_sync_bridge.cc index e716a29..3def8b31 100644 --- a/components/password_manager/core/browser/sync/password_sync_bridge.cc +++ b/components/password_manager/core/browser/sync/password_sync_bridge.cc
@@ -133,7 +133,10 @@ password_specifics.display_name() && password_form.icon_url.spec() == password_specifics.avatar_url() && url::Origin::Create(GURL(password_specifics.federation_url())) - .Serialize() == password_form.federation_origin.Serialize()); + .Serialize() == + password_form.federation_origin.Serialize()) && + password_form.notes == + PasswordNotesFromProto(password_specifics.notes()); } // Returns true iff |password_specifics| and |password_form| are equal
diff --git a/components/permissions/android/permission_prompt/permission_prompt_android.cc b/components/permissions/android/permission_prompt/permission_prompt_android.cc index 0ccb430c..230400f 100644 --- a/components/permissions/android/permission_prompt/permission_prompt_android.cc +++ b/components/permissions/android/permission_prompt/permission_prompt_android.cc
@@ -82,23 +82,11 @@ requests[1]->request_type() == RequestType::kMicStream)); } -static bool IsValidARCameraAccessRequestGroup( - const std::vector<PermissionRequest*>& requests) { - if (requests.size() < 2) - return false; - return ((requests[0]->request_type() == RequestType::kArSession && - requests[1]->request_type() == RequestType::kCameraStream) || - (requests[0]->request_type() == RequestType::kCameraStream && - requests[1]->request_type() == RequestType::kArSession)); -} - -// Grouped permission requests can only be Mic+Camera, Camera+Mic, -// AR + Camera, or Camera + AR. +// Grouped permission requests can only be Mic+Camera, Camera+Mic. static void CheckValidRequestGroup( const std::vector<PermissionRequest*>& requests) { DCHECK_EQ(static_cast<size_t>(2u), requests.size()); - DCHECK((IsValidMediaRequestGroup(requests)) || - (IsValidARCameraAccessRequestGroup(requests))); + DCHECK((IsValidMediaRequestGroup(requests))); } int PermissionPromptAndroid::GetIconId() const { @@ -126,19 +114,11 @@ } } CheckValidRequestGroup(requests); - if (IsValidARCameraAccessRequestGroup(requests)) { - return l10n_util::GetStringFUTF16( - IDS_AR_AND_MEDIA_CAPTURE_VIDEO_INFOBAR_TEXT, - url_formatter::FormatUrlForSecurityDisplay( - delegate_->GetRequestingOrigin(), - url_formatter::SchemeDisplay::OMIT_CRYPTOGRAPHIC)); - } else { - return l10n_util::GetStringFUTF16( - IDS_MEDIA_CAPTURE_AUDIO_AND_VIDEO_INFOBAR_TEXT, - url_formatter::FormatUrlForSecurityDisplay( - delegate_->GetRequestingOrigin(), - url_formatter::SchemeDisplay::OMIT_CRYPTOGRAPHIC)); - } + return l10n_util::GetStringFUTF16( + IDS_MEDIA_CAPTURE_AUDIO_AND_VIDEO_INFOBAR_TEXT, + url_formatter::FormatUrlForSecurityDisplay( + delegate_->GetRequestingOrigin(), + url_formatter::SchemeDisplay::OMIT_CRYPTOGRAPHIC)); } } // namespace permissions
diff --git a/components/permissions/contexts/webxr_permission_context.cc b/components/permissions/contexts/webxr_permission_context.cc index 4dee1e9..fa05ee1 100644 --- a/components/permissions/contexts/webxr_permission_context.cc +++ b/components/permissions/contexts/webxr_permission_context.cc
@@ -37,13 +37,8 @@ // There are two other permissions that need to check corresponding OS-level // permissions, and they take two different approaches to this. Geolocation only // stores the permission ContentSetting if both requests are granted (or if the -// site permission is "Block"). The media permissions follow something more -// similar to this approach, first querying and storing the site-specific -// ContentSetting and then querying for the additional OS permissions as needed. -// However, this is done in MediaStreamDevicesController, not their permission -// context. By persisting and then running additional code as needed, we thus -// mimic that flow, but keep all logic contained into the permission context -// class. +// site permission is "Block"). The media permissions are now following the +// approach found here. void WebXrPermissionContext::NotifyPermissionSet( const PermissionRequestID& id, const GURL& requesting_origin, @@ -53,6 +48,15 @@ ContentSetting content_setting, bool is_one_time) { DCHECK(!is_one_time); + + // Note that this method calls into base class implementation version of + // `NotifyPermissionSet()`, which would call `UpdateTabContext()`. + // This is fine, even in cases where we call the base method with a parameter + // that does not correspond to user's answer to Chrome-level permission, + // because `WebXrPermissionContext` does *not* have a custom implementation + // for `UpdateTabContext()` - if it did, we'd need to stop calling into base + // class with the parameter not matching user's answer. + // Only AR needs to check for additional permissions, and then only if it was // actually allowed. if (!(content_settings_type_ == ContentSettingsType::AR && @@ -135,5 +139,14 @@ id, requesting_origin, embedding_origin, std::move(callback), false /*persist*/, setting, /*is_one_time=*/false); } + +void WebXrPermissionContext::UpdateTabContext( + const permissions::PermissionRequestID& id, + const GURL& requesting_origin, + bool allowed) { + // See the comment in `NotifyPermissionSet()` for context on why this method + // should be empty. +} + #endif // BUILDFLAG(IS_ANDROID) } // namespace permissions
diff --git a/components/permissions/contexts/webxr_permission_context.h b/components/permissions/contexts/webxr_permission_context.h index 596b2ef..4da3d4e 100644 --- a/components/permissions/contexts/webxr_permission_context.h +++ b/components/permissions/contexts/webxr_permission_context.h
@@ -40,6 +40,10 @@ ContentSetting content_setting, bool is_one_time) override; + void UpdateTabContext(const permissions::PermissionRequestID& id, + const GURL& requesting_origin, + bool allowed) override; + void OnAndroidPermissionDecided(const PermissionRequestID& id, const GURL& requesting_origin, const GURL& embedding_origin,
diff --git a/components/permissions/permission_request_manager.cc b/components/permissions/permission_request_manager.cc index e7fe078..2ec2a6b 100644 --- a/components/permissions/permission_request_manager.cc +++ b/components/permissions/permission_request_manager.cc
@@ -112,10 +112,6 @@ return type == RequestType::kMicStream || type == RequestType::kCameraStream; } -bool IsArOrCameraRequest(RequestType type) { - return type == RequestType::kArSession || type == RequestType::kCameraStream; -} - bool ShouldGroupRequests(PermissionRequest* a, PermissionRequest* b) { if (a->requesting_origin() != b->requesting_origin()) return false; @@ -125,12 +121,6 @@ return true; } - // Group if the requests are an AR and a Camera Access request. - if (IsArOrCameraRequest(a->request_type()) && - IsArOrCameraRequest(b->request_type())) { - return true; - } - return false; }
diff --git a/components/permissions_strings.grdp b/components/permissions_strings.grdp index ae25a6a..89fcc650 100644 --- a/components/permissions_strings.grdp +++ b/components/permissions_strings.grdp
@@ -24,12 +24,6 @@ <message name="IDS_AR_INFOBAR_TEXT" desc="Text requesting permission for a site to use AR"> <ph name="URL">$1<ex>google.com</ex></ph> wants to create a 3D map of your surroundings and track camera position </message> - <!-- TODO(https://bugs.chromium.org/p/chromium/issues/detail?id=1106874) --> - <message name="IDS_AR_AND_MEDIA_CAPTURE_VIDEO_INFOBAR_TEXT" desc="Text requesting permission for a site to use AR and the Camera Access Feature"> - <ph name="URL">$1<ex>google.com</ex></ph> wants to: - • Create a 3D map of your surroundings and track camera position - • Use your camera - </message> <message name="IDS_IDLE_DETECTION_INFOBAR_TEXT" desc="Text requesting permission for a site to know when the user is idle."> <ph name="URL">$1<ex>google.com</ex></ph> wants to know when you're actively using this device </message>
diff --git a/components/permissions_strings_grdp/IDS_AR_AND_MEDIA_CAPTURE_VIDEO_INFOBAR_TEXT.png.sha1 b/components/permissions_strings_grdp/IDS_AR_AND_MEDIA_CAPTURE_VIDEO_INFOBAR_TEXT.png.sha1 deleted file mode 100644 index 338e2af..0000000 --- a/components/permissions_strings_grdp/IDS_AR_AND_MEDIA_CAPTURE_VIDEO_INFOBAR_TEXT.png.sha1 +++ /dev/null
@@ -1 +0,0 @@ -190759b575d8b4da48fc1bdec0a14e94a34c648e \ No newline at end of file
diff --git a/components/policy/core/browser/url_blocklist_manager.cc b/components/policy/core/browser/url_blocklist_manager.cc index b00a452..555636a 100644 --- a/components/policy/core/browser/url_blocklist_manager.cc +++ b/components/policy/core/browser/url_blocklist_manager.cc
@@ -148,7 +148,7 @@ URLBlocklist::URLBlocklistState URLBlocklist::GetURLBlocklistState( const GURL& url) const { - std::set<URLMatcherConditionSet::ID> matching_ids = + std::set<base::MatcherStringPattern::ID> matching_ids = url_matcher_->MatchURL(url); const FilterComponents* max = nullptr;
diff --git a/components/policy/core/browser/url_blocklist_manager.h b/components/policy/core/browser/url_blocklist_manager.h index 35ee99c8..fd66142 100644 --- a/components/policy/core/browser/url_blocklist_manager.h +++ b/components/policy/core/browser/url_blocklist_manager.h
@@ -75,9 +75,8 @@ const url_matcher::util::FilterComponents& lhs, const url_matcher::util::FilterComponents& rhs); - url_matcher::URLMatcherConditionSet::ID id_ = 0; - std::map<url_matcher::URLMatcherConditionSet::ID, - url_matcher::util::FilterComponents> + base::MatcherStringPattern::ID id_ = 0; + std::map<base::MatcherStringPattern::ID, url_matcher::util::FilterComponents> filters_; std::unique_ptr<url_matcher::URLMatcher> url_matcher_; };
diff --git a/components/reporting/client/mock_report_queue.cc b/components/reporting/client/mock_report_queue.cc index 3738cb6..9d8f8127 100644 --- a/components/reporting/client/mock_report_queue.cc +++ b/components/reporting/client/mock_report_queue.cc
@@ -6,8 +6,8 @@ namespace reporting { -MockReportQueue::MockReportQueue() = default; +MockReportQueueStrict::MockReportQueueStrict() = default; -MockReportQueue::~MockReportQueue() = default; +MockReportQueueStrict::~MockReportQueueStrict() = default; } // namespace reporting
diff --git a/components/reporting/client/mock_report_queue.h b/components/reporting/client/mock_report_queue.h index 89bda0f..1cb4202 100644 --- a/components/reporting/client/mock_report_queue.h +++ b/components/reporting/client/mock_report_queue.h
@@ -18,17 +18,17 @@ namespace reporting { // A mock of ReportQueue for use in testing. -class MockReportQueue : public ReportQueue { +class MockReportQueueStrict : public ReportQueue { public: - MockReportQueue(); - ~MockReportQueue() override; + MockReportQueueStrict(); + ~MockReportQueueStrict() override; MOCK_METHOD(void, AddRecord, - (base::StringPiece, Priority, ReportQueue::EnqueueCallback), - (const override)); + (base::StringPiece, Priority, EnqueueCallback), + (const)); - MOCK_METHOD(void, Flush, (Priority, ReportQueue::FlushCallback), (override)); + MOCK_METHOD(void, Flush, (Priority, FlushCallback), (override)); MOCK_METHOD( (base::OnceCallback<void(StatusOr<std::unique_ptr<ReportQueue>>)>), @@ -37,6 +37,9 @@ (const override)); }; +// Most of the time no need to log uninterested calls. +typedef ::testing::NiceMock<MockReportQueueStrict> MockReportQueue; + } // namespace reporting #endif // COMPONENTS_REPORTING_CLIENT_MOCK_REPORT_QUEUE_H_
diff --git a/components/reporting/client/mock_report_queue_provider.cc b/components/reporting/client/mock_report_queue_provider.cc index bb4d5938..e225d445 100644 --- a/components/reporting/client/mock_report_queue_provider.cc +++ b/components/reporting/client/mock_report_queue_provider.cc
@@ -63,7 +63,7 @@ .WillRepeatedly([]() { auto report_queue = std::unique_ptr<MockReportQueue, base::OnTaskRunnerDeleter>( - new NiceMock<MockReportQueue>(), + new MockReportQueue(), base::OnTaskRunnerDeleter( base::ThreadPool::CreateSequencedTaskRunner({})));
diff --git a/components/reporting/client/report_queue_impl_unittest.cc b/components/reporting/client/report_queue_impl_unittest.cc index 084258d..805e9c8 100644 --- a/components/reporting/client/report_queue_impl_unittest.cc +++ b/components/reporting/client/report_queue_impl_unittest.cc
@@ -270,7 +270,7 @@ base::queue<ReportQueue::EnqueueCallback> enqueue_cb_queue; int enqueue_count = 0; - auto mock_queue = std::make_unique<testing::NiceMock<MockReportQueue>>(); + auto mock_queue = std::make_unique<MockReportQueue>(); EXPECT_CALL(*mock_queue, AddRecord) .Times(3) .WillRepeatedly(
diff --git a/components/reporting/metrics/metric_report_queue_unittest.cc b/components/reporting/metrics/metric_report_queue_unittest.cc index a131966..48266cd 100644 --- a/components/reporting/metrics/metric_report_queue_unittest.cc +++ b/components/reporting/metrics/metric_report_queue_unittest.cc
@@ -47,10 +47,11 @@ }; TEST_F(MetricReportQueueTest, ManualUpload) { - auto mock_queue = std::unique_ptr<MockReportQueue, base::OnTaskRunnerDeleter>( - new testing::StrictMock<MockReportQueue>(), - base::OnTaskRunnerDeleter( - base::ThreadPool::CreateSequencedTaskRunner({}))); + auto mock_queue = + std::unique_ptr<MockReportQueueStrict, base::OnTaskRunnerDeleter>( + new MockReportQueueStrict(), + base::OnTaskRunnerDeleter( + base::ThreadPool::CreateSequencedTaskRunner({}))); auto* mock_queue_ptr = mock_queue.get(); MetricData record; record.set_timestamp_ms(123456); @@ -84,7 +85,7 @@ int upload_count = 0; auto mock_queue = std::unique_ptr<MockReportQueue, base::OnTaskRunnerDeleter>( - new testing::NiceMock<MockReportQueue>(), + new MockReportQueue(), base::OnTaskRunnerDeleter( base::ThreadPool::CreateSequencedTaskRunner({}))); auto* mock_queue_ptr = mock_queue.get(); @@ -132,10 +133,11 @@ TEST_F(MetricReportQueueTest, RateControlledFlush_TimeNotElapsed) { settings_->SetInteger(kRateSettingPath, kRateMs); - auto mock_queue = std::unique_ptr<MockReportQueue, base::OnTaskRunnerDeleter>( - new testing::StrictMock<MockReportQueue>(), - base::OnTaskRunnerDeleter( - base::ThreadPool::CreateSequencedTaskRunner({}))); + auto mock_queue = + std::unique_ptr<MockReportQueueStrict, base::OnTaskRunnerDeleter>( + new MockReportQueueStrict(), + base::OnTaskRunnerDeleter( + base::ThreadPool::CreateSequencedTaskRunner({}))); auto* mock_queue_ptr = mock_queue.get(); MetricData record; record.set_timestamp_ms(123456); @@ -168,10 +170,11 @@ TEST_F(MetricReportQueueTest, RateControlledFlush_TimeElapsed) { settings_->SetInteger(kRateSettingPath, kRateMs); - auto mock_queue = std::unique_ptr<MockReportQueue, base::OnTaskRunnerDeleter>( - new testing::StrictMock<MockReportQueue>(), - base::OnTaskRunnerDeleter( - base::ThreadPool::CreateSequencedTaskRunner({}))); + auto mock_queue = + std::unique_ptr<MockReportQueueStrict, base::OnTaskRunnerDeleter>( + new MockReportQueueStrict(), + base::OnTaskRunnerDeleter( + base::ThreadPool::CreateSequencedTaskRunner({}))); auto* mock_queue_ptr = mock_queue.get(); MetricData record; record.set_timestamp_ms(123456);
diff --git a/components/reporting/storage/missive_storage_module.cc b/components/reporting/storage/missive_storage_module.cc index d2e6768c..f204532 100644 --- a/components/reporting/storage/missive_storage_module.cc +++ b/components/reporting/storage/missive_storage_module.cc
@@ -36,15 +36,13 @@ return base::WrapRefCounted(new MissiveStorageModule(std::move(delegate))); } -void MissiveStorageModule::AddRecord( - Priority priority, - Record record, - base::OnceCallback<void(Status)> callback) { +void MissiveStorageModule::AddRecord(Priority priority, + Record record, + EnqueueCallback callback) { delegate_->AddRecord(priority, std::move(record), std::move(callback)); } -void MissiveStorageModule::Flush(Priority priority, - base::OnceCallback<void(Status)> callback) { +void MissiveStorageModule::Flush(Priority priority, FlushCallback callback) { delegate_->Flush(priority, std::move(callback)); }
diff --git a/components/reporting/storage/missive_storage_module.h b/components/reporting/storage/missive_storage_module.h index ef5a104..52d3d1e 100644 --- a/components/reporting/storage/missive_storage_module.h +++ b/components/reporting/storage/missive_storage_module.h
@@ -37,9 +37,8 @@ virtual void AddRecord(const Priority priority, Record record, - base::OnceCallback<void(Status)> callback) = 0; - virtual void Flush(Priority priority, - base::OnceCallback<void(Status)> callback) = 0; + EnqueueCallback callback) = 0; + virtual void Flush(Priority priority, FlushCallback callback) = 0; virtual void ReportSuccess(const SequenceInformation& sequence_information, bool force) = 0; virtual void UpdateEncryptionKey( @@ -56,14 +55,13 @@ // Calls |missive_delegate_->AddRecord| forwarding the arguments. void AddRecord(Priority priority, Record record, - base::OnceCallback<void(Status)> callback) override; + EnqueueCallback callback) override; // Calls |missive_delegate_->Flush| to initiate upload of collected records // according to the priority. Called usually for a queue with an infinite or // very large upload period. Multiple |Flush| calls can safely run in // parallel. Returns error if cannot start upload. - void Flush(Priority priority, - base::OnceCallback<void(Status)> callback) override; + void Flush(Priority priority, FlushCallback callback) override; // Once a record has been successfully uploaded, the sequence information // can be passed back to the StorageModule here for record deletion.
diff --git a/components/reporting/storage/missive_storage_module_delegate_impl.cc b/components/reporting/storage/missive_storage_module_delegate_impl.cc index 3c763c2..ebf8a0a1 100644 --- a/components/reporting/storage/missive_storage_module_delegate_impl.cc +++ b/components/reporting/storage/missive_storage_module_delegate_impl.cc
@@ -26,13 +26,13 @@ void MissiveStorageModuleDelegateImpl::AddRecord( Priority priority, Record record, - base::OnceCallback<void(Status)> callback) { + MissiveStorageModule::EnqueueCallback callback) { add_record_.Run(priority, std::move(record), std::move(callback)); } void MissiveStorageModuleDelegateImpl::Flush( Priority priority, - base::OnceCallback<void(Status)> callback) { + MissiveStorageModule::FlushCallback callback) { flush_.Run(priority, std::move(callback)); }
diff --git a/components/reporting/storage/missive_storage_module_delegate_impl.h b/components/reporting/storage/missive_storage_module_delegate_impl.h index 2031608..fa20b66f 100644 --- a/components/reporting/storage/missive_storage_module_delegate_impl.h +++ b/components/reporting/storage/missive_storage_module_delegate_impl.h
@@ -18,9 +18,10 @@ : public MissiveStorageModule::MissiveStorageModuleDelegateInterface { public: using AddRecordCallback = base::RepeatingCallback< - void(Priority, Record, base::OnceCallback<void(Status)>)>; + void(Priority, Record, MissiveStorageModule::EnqueueCallback)>; using FlushCallback = - base::RepeatingCallback<void(Priority, base::OnceCallback<void(Status)>)>; + base::RepeatingCallback<void(Priority, + MissiveStorageModule::FlushCallback)>; MissiveStorageModuleDelegateImpl(AddRecordCallback add_record, FlushCallback flush); @@ -28,10 +29,10 @@ void AddRecord(Priority priority, Record record, - base::OnceCallback<void(Status)> callback) override; + MissiveStorageModule::EnqueueCallback callback) override; void Flush(Priority priority, - base::OnceCallback<void(Status)> callback) override; + MissiveStorageModule::FlushCallback callback) override; void ReportSuccess(const SequenceInformation& sequence_information, bool force) override;
diff --git a/components/reporting/storage/storage_module.cc b/components/reporting/storage/storage_module.cc index 9676175..84707b1 100644 --- a/components/reporting/storage/storage_module.cc +++ b/components/reporting/storage/storage_module.cc
@@ -31,7 +31,7 @@ void StorageModule::AddRecord(Priority priority, Record record, - base::OnceCallback<void(Status)> callback) { + EnqueueCallback callback) { storage_->Write(priority, std::move(record), std::move(callback)); } @@ -46,8 +46,7 @@ })); } -void StorageModule::Flush(Priority priority, - base::OnceCallback<void(Status)> callback) { +void StorageModule::Flush(Priority priority, FlushCallback callback) { std::move(callback).Run(storage_->Flush(priority)); }
diff --git a/components/reporting/storage/storage_module.h b/components/reporting/storage/storage_module.h index 9e9f675..f8f6542 100644 --- a/components/reporting/storage/storage_module.h +++ b/components/reporting/storage/storage_module.h
@@ -40,14 +40,13 @@ // called. void AddRecord(Priority priority, Record record, - base::OnceCallback<void(Status)> callback) override; + EnqueueCallback callback) override; // Initiates upload of collected records according to the priority. // Called usually for a queue with an infinite or very large upload period. // Multiple |Flush| calls can safely run in parallel. // Returns error if cannot start upload. - void Flush(Priority priority, - base::OnceCallback<void(Status)> callback) override; + void Flush(Priority priority, FlushCallback callback) override; // Once a record has been successfully uploaded, the sequence information // can be passed back to the StorageModule here for record deletion.
diff --git a/components/reporting/storage/storage_module_interface.h b/components/reporting/storage/storage_module_interface.h index f597b0b..cb5db0d 100644 --- a/components/reporting/storage/storage_module_interface.h +++ b/components/reporting/storage/storage_module_interface.h
@@ -17,23 +17,25 @@ class StorageModuleInterface : public base::RefCountedThreadSafe<StorageModuleInterface> { public: + using EnqueueCallback = base::OnceCallback<void(Status)>; + using FlushCallback = base::OnceCallback<void(Status)>; + StorageModuleInterface(const StorageModuleInterface& other) = delete; StorageModuleInterface& operator=(const StorageModuleInterface& other) = delete; - // AddRecord will add |record| (taking ownership) to the - // |StorageModuleInterface| according to the provided |priority|. On - // completion, |callback| is called. + // AddRecord will add |record| (taking ownership) to the |StorageModule| + // according to the provided |priority|. On completion, |callback| will be + // called. virtual void AddRecord(Priority priority, Record record, - base::OnceCallback<void(Status)> callback) = 0; + EnqueueCallback callback) = 0; // Initiates upload of collected records according to the priority. // Called usually for a queue with an infinite or very large upload period. // Multiple |Flush| calls can safely run in parallel. // Returns error if cannot start upload. - virtual void Flush(Priority priority, - base::OnceCallback<void(Status)> callback) = 0; + virtual void Flush(Priority priority, FlushCallback callback) = 0; // Once a record has been successfully uploaded, the sequence information // can be passed back to the StorageModuleInterface here for record deletion.
diff --git a/components/reporting/storage/test_storage_module.cc b/components/reporting/storage/test_storage_module.cc index 735cbcb9..7e837f73 100644 --- a/components/reporting/storage/test_storage_module.cc +++ b/components/reporting/storage/test_storage_module.cc
@@ -21,17 +21,18 @@ TestStorageModuleStrict::TestStorageModuleStrict() { ON_CALL(*this, AddRecord) - .WillByDefault(Invoke(this, &TestStorageModule::AddRecordSuccessfully)); + .WillByDefault( + Invoke(this, &TestStorageModuleStrict::AddRecordSuccessfully)); ON_CALL(*this, Flush) .WillByDefault( - WithArg<1>(Invoke([](base::OnceCallback<void(Status)> callback) { + WithArg<1>(Invoke([](StorageModuleInterface::FlushCallback callback) { std::move(callback).Run(Status::StatusOK()); }))); } TestStorageModuleStrict::~TestStorageModuleStrict() = default; -Record TestStorageModuleStrict::record() const { +const Record& TestStorageModuleStrict::record() const { EXPECT_TRUE(record_.has_value()); return record_.value(); } @@ -41,10 +42,9 @@ return priority_.value(); } -void TestStorageModuleStrict::AddRecordSuccessfully( - Priority priority, - Record record, - base::OnceCallback<void(Status)> callback) { +void TestStorageModuleStrict::AddRecordSuccessfully(Priority priority, + Record record, + EnqueueCallback callback) { record_ = std::move(record); priority_ = priority; std::move(callback).Run(Status::StatusOK());
diff --git a/components/reporting/storage/test_storage_module.h b/components/reporting/storage/test_storage_module.h index 7736b2f..a0ce0065 100644 --- a/components/reporting/storage/test_storage_module.h +++ b/components/reporting/storage/test_storage_module.h
@@ -24,14 +24,12 @@ MOCK_METHOD(void, AddRecord, - (Priority priority, - Record record, - base::OnceCallback<void(Status)> callback), + (Priority priority, Record record, EnqueueCallback callback), (override)); MOCK_METHOD(void, Flush, - (Priority priority, base::OnceCallback<void(Status)> callback), + (Priority priority, FlushCallback callback), (override)); MOCK_METHOD(void, @@ -44,7 +42,7 @@ (SignedEncryptionInfo signed_encryption_key), (override)); - Record record() const; + const Record& record() const; Priority priority() const; protected: @@ -53,7 +51,7 @@ private: void AddRecordSuccessfully(Priority priority, Record record, - base::OnceCallback<void(Status)> callback); + EnqueueCallback callback); absl::optional<Record> record_; absl::optional<Priority> priority_;
diff --git a/components/safe_browsing/content/common/file_type_policies_policy_util.cc b/components/safe_browsing/content/common/file_type_policies_policy_util.cc index 8a787c63..15df3fa 100644 --- a/components/safe_browsing/content/common/file_type_policies_policy_util.cc +++ b/components/safe_browsing/content/common/file_type_policies_policy_util.cc
@@ -64,7 +64,7 @@ if (!domains_for_extension.GetListDeprecated().empty()) { url_matcher::URLMatcher matcher; - url_matcher::URLMatcherConditionSet::ID id(0); + base::MatcherStringPattern::ID id(0); url_matcher::util::AddFilters(&matcher, true, &id, &domains_for_extension); auto matching_set_size = matcher.MatchURL(normalized_url).size();
diff --git a/components/segmentation_platform/internal/selection/segment_result_provider_unittest.cc b/components/segmentation_platform/internal/selection/segment_result_provider_unittest.cc index 6dc2450..578b4da6 100644 --- a/components/segmentation_platform/internal/selection/segment_result_provider_unittest.cc +++ b/components/segmentation_platform/internal/selection/segment_result_provider_unittest.cc
@@ -7,6 +7,7 @@ #include "base/test/gmock_callback_support.h" #include "base/test/simple_test_clock.h" #include "base/test/task_environment.h" +#include "components/prefs/testing_pref_service.h" #include "components/segmentation_platform/internal/database/mock_signal_database.h" #include "components/segmentation_platform/internal/database/mock_signal_storage_config.h" #include "components/segmentation_platform/internal/database/test_segment_info_database.h" @@ -17,7 +18,9 @@ #include "components/segmentation_platform/internal/platform_options.h" #include "components/segmentation_platform/internal/scheduler/execution_service.h" #include "components/segmentation_platform/internal/signals/signal_handler.h" +#include "components/segmentation_platform/public/local_state_helper.h" #include "components/segmentation_platform/public/model_provider.h" +#include "components/segmentation_platform/public/segmentation_platform_service.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -92,6 +95,8 @@ segment_database_.get(), &signal_storage_config_, default_manager_.get(), execution_service_.get(), &clock_, /*force_refresh_results=*/false); + SegmentationPlatformService::RegisterLocalStatePrefs(prefs_.registry()); + LocalStateHelper::GetInstance().Initialize(&prefs_); } void TearDown() override { @@ -170,6 +175,7 @@ std::unique_ptr<test::TestSegmentInfoDatabase> segment_database_; MockSignalStorageConfig signal_storage_config_; std::unique_ptr<SegmentResultProvider> score_provider_; + TestingPrefServiceSimple prefs_; }; TEST_F(SegmentResultProviderTest, GetScoreWithoutInfo) {
diff --git a/components/services/filesystem/file_impl_unittest.cc b/components/services/filesystem/file_impl_unittest.cc index 519d857..170b371 100644 --- a/components/services/filesystem/file_impl_unittest.cc +++ b/components/services/filesystem/file_impl_unittest.cc
@@ -361,6 +361,12 @@ // Note: Ignore nanoseconds, since it may not always be supported. We expect at // least second-resolution support though. +// TODO(https://crbug.com/702990): Remove this test once last_access_time has +// been removed after PPAPI has been deprecated. Fuchsia does not support touch, +// which breaks this test that relies on it. Since PPAPI is being deprecated, +// this test is excluded from the Fuchsia build. +// See https://crbug.com/1077456 for details. +#if !BUILDFLAG(IS_FUCHSIA) TEST_F(FileImplTest, StatTouch) { mojo::Remote<mojom::Directory> directory = CreateTempDir(); base::File::Error error; @@ -430,6 +436,7 @@ // TODO(vtl): Also test Touch() "now" options. // TODO(vtl): Also test touching both atime and mtime. } +#endif // !BUILDFLAG(IS_FUCHSIA) TEST_F(FileImplTest, TellSeek) { mojo::Remote<mojom::Directory> directory = CreateTempDir();
diff --git a/components/sync/base/features.h b/components/sync/base/features.h index e56225d..70c59a2 100644 --- a/components/sync/base/features.h +++ b/components/sync/base/features.h
@@ -36,6 +36,13 @@ inline constexpr base::FeatureParam<int> kMinGuResponsesToIgnoreKey{ &kIgnoreSyncEncryptionKeysLongMissing, "MinGuResponsesToIgnoreKey", 50}; +// When enabled, Sync machinery will read and writes password notes to the +// `encrypted_notes_backup` field inside the PasswordSpecifics proto. Together +// with the logic on the server. this protects against notes being overwritten +// by legacy clients not supporting password notes. +inline constexpr base::Feature kReadWritePasswordNotesBackupField{ + "ReadWritePasswordNotesBackupField", base::FEATURE_DISABLED_BY_DEFAULT}; + // Allows custom passphrase users to receive Wallet data for secondary accounts // while in transport-only mode. inline constexpr base::Feature
diff --git a/components/sync/engine/commit_contribution_impl.cc b/components/sync/engine/commit_contribution_impl.cc index 473e5f4f..da8fd3d 100644 --- a/components/sync/engine/commit_contribution_impl.cc +++ b/components/sync/engine/commit_contribution_impl.cc
@@ -11,6 +11,7 @@ #include "base/guid.h" #include "base/logging.h" #include "base/values.h" +#include "components/sync/base/features.h" #include "components/sync/base/time.h" #include "components/sync/base/unique_position.h" #include "components/sync/engine/commit_and_get_updates_types.h" @@ -290,6 +291,20 @@ password_data, encrypted_password.mutable_password()->mutable_encrypted()); DCHECK(result); + if (base::FeatureList::IsEnabled( + syncer::kReadWritePasswordNotesBackupField)) { + // `encrypted_notes_backup` field needs to be populated regardless of + // whether or not there are any notes. + result = cryptographer_->Encrypt(password_data.notes(), + encrypted_password.mutable_password() + ->mutable_encrypted_notes_backup()); + DCHECK(result); + // When encrypting both blobs succeeds, both encrypted blobs must use the + // key name. + DCHECK_EQ( + encrypted_password.password().encrypted().key_name(), + encrypted_password.password().encrypted_notes_backup().key_name()); + } *commit_proto->mutable_specifics() = std::move(encrypted_password); commit_proto->set_name("encrypted"); } else if (cryptographer_) {
diff --git a/components/sync/engine/commit_contribution_impl_unittest.cc b/components/sync/engine/commit_contribution_impl_unittest.cc index e6086b8..365957e 100644 --- a/components/sync/engine/commit_contribution_impl_unittest.cc +++ b/components/sync/engine/commit_contribution_impl_unittest.cc
@@ -12,7 +12,9 @@ #include "base/callback_helpers.h" #include "base/hash/sha1.h" #include "base/test/mock_callback.h" +#include "base/test/scoped_feature_list.h" #include "components/sync/base/client_tag_hash.h" +#include "components/sync/base/features.h" #include "components/sync/base/model_type.h" #include "components/sync/base/unique_position.h" #include "components/sync/engine/nigori/cryptographer.h" @@ -379,6 +381,89 @@ contribution.ProcessCommitFailure(SyncCommitError::kNetworkError); } +TEST(CommitContributionImplTest, ShouldPopulatePasswordNotesBackup) { + base::test::ScopedFeatureList feature_list; + feature_list.InitAndEnableFeature(syncer::kReadWritePasswordNotesBackupField); + + const std::string kNoteValue = "Note Value"; + auto data = std::make_unique<syncer::EntityData>(); + data->client_tag_hash = kTag; + sync_pb::PasswordSpecificsData* password_data = + data->specifics.mutable_password()->mutable_client_only_encrypted_data(); + sync_pb::PasswordSpecificsData_Notes_Note* note = + password_data->mutable_notes()->add_note(); + note->set_value(kNoteValue); + + auto request_data = std::make_unique<CommitRequestData>(); + base::Base64Encode(base::SHA1HashString(data->specifics.SerializeAsString()), + &request_data->specifics_hash); + request_data->entity = std::move(data); + + CommitRequestDataList requests_data; + requests_data.push_back(std::move(request_data)); + + std::unique_ptr<FakeCryptographer> cryptographer = + FakeCryptographer::FromSingleDefaultKey("dummy"); + CommitContributionImpl contribution( + PASSWORDS, sync_pb::DataTypeContext(), std::move(requests_data), + /*on_commit_response_callback=*/base::NullCallback(), + /*on_full_commit_failure_callback=*/base::NullCallback(), + cryptographer.get(), PassphraseType::kImplicitPassphrase, + /*only_commit_specifics=*/false); + + sync_pb::ClientToServerMessage msg; + contribution.AddToCommitMessage(&msg); + + ASSERT_EQ(1, msg.commit().entries().size()); + SyncEntity entity = msg.commit().entries(0); + ASSERT_TRUE(entity.specifics().has_password()); + + // Verify the contents of the encrypted notes backup blob. + sync_pb::PasswordSpecificsData_Notes decrypted_notes; + cryptographer->Decrypt(entity.specifics().password().encrypted_notes_backup(), + &decrypted_notes); + ASSERT_EQ(1, decrypted_notes.note_size()); + EXPECT_EQ(kNoteValue, decrypted_notes.note(0).value()); +} + +TEST(CommitContributionImplTest, + ShouldPopulatePasswordNotesBackupWhenNoLocalNotes) { + base::test::ScopedFeatureList feature_list; + feature_list.InitAndEnableFeature(syncer::kReadWritePasswordNotesBackupField); + + auto data = std::make_unique<syncer::EntityData>(); + data->client_tag_hash = kTag; + sync_pb::PasswordSpecificsData* password_data = + data->specifics.mutable_password()->mutable_client_only_encrypted_data(); + password_data->set_signon_realm("signon_realm"); + + auto request_data = std::make_unique<CommitRequestData>(); + base::Base64Encode(base::SHA1HashString(data->specifics.SerializeAsString()), + &request_data->specifics_hash); + request_data->entity = std::move(data); + + CommitRequestDataList requests_data; + requests_data.push_back(std::move(request_data)); + + std::unique_ptr<FakeCryptographer> cryptographer = + FakeCryptographer::FromSingleDefaultKey("dummy"); + CommitContributionImpl contribution( + PASSWORDS, sync_pb::DataTypeContext(), std::move(requests_data), + /*on_commit_response_callback=*/base::NullCallback(), + /*on_full_commit_failure_callback=*/base::NullCallback(), + cryptographer.get(), PassphraseType::kImplicitPassphrase, + /*only_commit_specifics=*/false); + + sync_pb::ClientToServerMessage msg; + contribution.AddToCommitMessage(&msg); + + ASSERT_EQ(1, msg.commit().entries().size()); + SyncEntity entity = msg.commit().entries(0); + ASSERT_TRUE(entity.specifics().has_password()); + EXPECT_FALSE( + entity.specifics().password().encrypted_notes_backup().blob().empty()); +} + } // namespace } // namespace syncer
diff --git a/components/sync/engine/model_type_worker.cc b/components/sync/engine/model_type_worker.cc index 19e7332..c916bcc9 100644 --- a/components/sync/engine/model_type_worker.cc +++ b/components/sync/engine/model_type_worker.cc
@@ -166,6 +166,33 @@ DLOG(ERROR) << "Failed to decrypt a decryptable password"; return false; } + // The `notes` field in the PasswordSpecificsData is the authoritative value. + // When set, it disregards whatever `encrypted_notes_backup` contains. + if (out->password().client_only_encrypted_data().has_notes()) { + return true; + } + if (!in.password().has_encrypted_notes_backup()) { + return true; + } + if (!base::FeatureList::IsEnabled( + syncer::kReadWritePasswordNotesBackupField)) { + return true; + } + // It is guaranteed that if `encrypted()` is decryptable, then + // `encrypted_notes_backup()` must be decryptable too. Failure to decrypt + // `encrypted_notes_backup()` indicates a data corruption. + if (!cryptographer.Decrypt(in.password().encrypted_notes_backup(), + out->mutable_password() + ->mutable_client_only_encrypted_data() + ->mutable_notes())) { + // TODO(crbug.com/1326554): Log UMA metrics for cases when notes backup + // encrypted blob is corrupted. + return false; + } + // TODO(crbug.com/1326554): Properly handle the case when both blobs are + // decryptable but with different keys. Ideally the password should be + // re-uploaded potentially by setting needs_reupload boolean in + // UpdateResponseData or EntityData. return true; }
diff --git a/components/sync/engine/model_type_worker_unittest.cc b/components/sync/engine/model_type_worker_unittest.cc index 36b01607..2d21926 100644 --- a/components/sync/engine/model_type_worker_unittest.cc +++ b/components/sync/engine/model_type_worker_unittest.cc
@@ -2387,4 +2387,104 @@ EXPECT_FALSE(worker()->HasLocalChangesForTest()); } +class ModelTypeWorkerPasswordsTestWithNotes + : public ModelTypeWorkerPasswordsTest { + public: + ModelTypeWorkerPasswordsTestWithNotes() { + feature_list_.InitAndEnableFeature( + syncer::kReadWritePasswordNotesBackupField); + } + ~ModelTypeWorkerPasswordsTestWithNotes() override = default; + + private: + base::test::ScopedFeatureList feature_list_; +}; + +TEST_F(ModelTypeWorkerPasswordsTestWithNotes, + ShouldIgnoreTheEncryptedNotesBackupWhenNotesInPasswordSpecificsData) { + const std::string kPasswordInSpecificsNote = "Note Value"; + const std::string kPasswordNoteBackup = "Note Backup"; + NormalInitialize(); + + // Create a new Nigori and allow the cryptographer to decrypt it. + AddPendingKey(); + DecryptPendingKey(); + + // Set a value for the note in the PasswordSpecificsData. + sync_pb::PasswordSpecificsData unencrypted_password; + unencrypted_password.set_password_value(kPassword); + unencrypted_password.mutable_notes()->add_note()->set_value( + kPasswordInSpecificsNote); + sync_pb::EntitySpecifics encrypted_specifics = + EncryptPasswordSpecificsWithNthKey(1, unencrypted_password); + + sync_pb::PasswordSpecificsData_Notes notes_backup; + notes_backup.add_note()->set_value(kPasswordNoteBackup); + + FakeCryptographer::FromSingleDefaultKey(GetNthKeyName(1)) + ->EncryptString(notes_backup.SerializeAsString(), + encrypted_specifics.mutable_password() + ->mutable_encrypted_notes_backup()); + + // Receive an encrypted password, encrypted with a key that is already known. + SyncEntity entity = server()->UpdateFromServer( + /*version_offset=*/10, kHash1, encrypted_specifics); + worker()->ProcessGetUpdatesResponse(server()->GetProgress(), + server()->GetContext(), {&entity}, + status_controller()); + worker()->ApplyUpdates(status_controller()); + + ASSERT_TRUE(processor()->HasUpdateResponse(kHash1)); + const UpdateResponseData& update = processor()->GetUpdateResponse(kHash1); + ASSERT_TRUE( + update.entity.specifics.password().has_client_only_encrypted_data()); + EXPECT_EQ(kPasswordInSpecificsNote, update.entity.specifics.password() + .client_only_encrypted_data() + .notes() + .note(0) + .value()); +} + +TEST_F(ModelTypeWorkerPasswordsTestWithNotes, + ShouldUseTheEncryptedNotesBackupWhenMissingInPasswordSpecificsData) { + const std::string kPasswordNoteBackup = "Note Backup"; + NormalInitialize(); + + // Create a new Nigori and allow the cryptographer to decrypt it. + AddPendingKey(); + DecryptPendingKey(); + + // Set a value for the note in the PasswordSpecificsData. + sync_pb::PasswordSpecificsData unencrypted_password; + unencrypted_password.set_password_value(kPassword); + sync_pb::EntitySpecifics encrypted_specifics = + EncryptPasswordSpecificsWithNthKey(1, unencrypted_password); + + sync_pb::PasswordSpecificsData_Notes notes_backup; + notes_backup.add_note()->set_value(kPasswordNoteBackup); + + FakeCryptographer::FromSingleDefaultKey(GetNthKeyName(1)) + ->EncryptString(notes_backup.SerializeAsString(), + encrypted_specifics.mutable_password() + ->mutable_encrypted_notes_backup()); + + // Receive an encrypted password, encrypted with a key that is already known. + SyncEntity entity = server()->UpdateFromServer( + /*version_offset=*/10, kHash1, encrypted_specifics); + worker()->ProcessGetUpdatesResponse(server()->GetProgress(), + server()->GetContext(), {&entity}, + status_controller()); + worker()->ApplyUpdates(status_controller()); + + ASSERT_TRUE(processor()->HasUpdateResponse(kHash1)); + const UpdateResponseData& update = processor()->GetUpdateResponse(kHash1); + ASSERT_TRUE( + update.entity.specifics.password().has_client_only_encrypted_data()); + EXPECT_EQ(kPasswordNoteBackup, update.entity.specifics.password() + .client_only_encrypted_data() + .notes() + .note(0) + .value()); +} + } // namespace syncer
diff --git a/components/translate/core/language_detection/chinese_script_classifier.cc b/components/translate/core/language_detection/chinese_script_classifier.cc index b280079..37f53df3 100644 --- a/components/translate/core/language_detection/chinese_script_classifier.cc +++ b/components/translate/core/language_detection/chinese_script_classifier.cc
@@ -783,7 +783,7 @@ base::TruncateUTF8ToByteSize(input, 500, &input_subset); // Remove whitespace since transliterators may not preserve it. - base::EraseIf(input_subset, base::IsUnicodeWhitespace); + base::EraseIf(input_subset, base::IsUnicodeWhitespace<char>); // Convert the input to icu::UnicodeString so we can iterate over codepoints. icu::UnicodeString input_codepoints =
diff --git a/components/url_matcher/regex_set_matcher.cc b/components/url_matcher/regex_set_matcher.cc index 893a241..92cbbc57 100644 --- a/components/url_matcher/regex_set_matcher.cc +++ b/components/url_matcher/regex_set_matcher.cc
@@ -68,7 +68,7 @@ std::vector<RegexSetMatcher::RE2ID> RegexSetMatcher::FindSubstringMatches( const std::string& text) const { - std::set<int> atoms_set; + std::set<base::MatcherStringPattern::ID> atoms_set; substring_matcher_->Match(text, &atoms_set); return std::vector<RE2ID>(atoms_set.begin(), atoms_set.end()); }
diff --git a/components/url_matcher/url_matcher.cc b/components/url_matcher/url_matcher.cc index 1fce666e..17c6ca42 100644 --- a/components/url_matcher/url_matcher.cc +++ b/components/url_matcher/url_matcher.cc
@@ -265,9 +265,9 @@ const char kQuerySeparator = '&'; } // namespace -URLMatcherConditionFactory::URLMatcherConditionFactory() : id_counter_(0) {} +URLMatcherConditionFactory::URLMatcherConditionFactory() = default; -URLMatcherConditionFactory::~URLMatcherConditionFactory() {} +URLMatcherConditionFactory::~URLMatcherConditionFactory() = default; std::string URLMatcherConditionFactory::CanonicalizeURLForComponentSearches( const GURL& url) const { @@ -560,7 +560,7 @@ return query; } -int URLMatcherConditionFactory::GetNextID() { +base::MatcherStringPattern::ID URLMatcherConditionFactory::GetNextID() { id_counter_++; if (id_counter_ == MatcherStringPattern::kInvalidId) @@ -736,12 +736,13 @@ URLMatcherConditionSet::~URLMatcherConditionSet() {} -URLMatcherConditionSet::URLMatcherConditionSet(ID id, - const Conditions& conditions) +URLMatcherConditionSet::URLMatcherConditionSet( + base::MatcherStringPattern::ID id, + const Conditions& conditions) : id_(id), conditions_(conditions) {} URLMatcherConditionSet::URLMatcherConditionSet( - ID id, + base::MatcherStringPattern::ID id, const Conditions& conditions, std::unique_ptr<URLMatcherSchemeFilter> scheme_filter, std::unique_ptr<URLMatcherPortFilter> port_filter) @@ -751,7 +752,7 @@ port_filter_(std::move(port_filter)) {} URLMatcherConditionSet::URLMatcherConditionSet( - ID id, + base::MatcherStringPattern::ID id, const Conditions& conditions, const QueryConditions& query_conditions, std::unique_ptr<URLMatcherSchemeFilter> scheme_filter, @@ -815,11 +816,11 @@ } void URLMatcher::RemoveConditionSets( - const std::vector<URLMatcherConditionSet::ID>& condition_set_ids) { - for (auto i = condition_set_ids.begin(); i != condition_set_ids.end(); ++i) { - DCHECK(url_matcher_condition_sets_.find(*i) != + const std::vector<base::MatcherStringPattern::ID>& condition_set_ids) { + for (auto id : condition_set_ids) { + DCHECK(url_matcher_condition_sets_.find(id) != url_matcher_condition_sets_.end()); - url_matcher_condition_sets_.erase(*i); + url_matcher_condition_sets_.erase(id); } UpdateInternalDatastructures(); } @@ -828,7 +829,7 @@ UpdateConditionFactory(); } -std::set<URLMatcherConditionSet::ID> URLMatcher::MatchURL( +std::set<base::MatcherStringPattern::ID> URLMatcher::MatchURL( const GURL& url) const { // Find all IDs of MatcherStringPatterns that match |url|. // See URLMatcherConditionFactory for the canonicalization of URLs and the @@ -857,7 +858,7 @@ // Calculate all URLMatcherConditionSets for which all URLMatcherConditions // were fulfilled. - std::set<URLMatcherConditionSet::ID> result; + std::set<base::MatcherStringPattern::ID> result; for (auto i = matches.begin(); i != matches.end(); ++i) { // For each URLMatcherConditionSet there is exactly one condition // registered in substring_match_triggers_. This means that the following @@ -866,7 +867,7 @@ auto triggered_condition_sets_iter = substring_match_triggers_.find(*i); if (triggered_condition_sets_iter == substring_match_triggers_.end()) continue; // Not all substring matches are triggers for a condition set. - const std::set<URLMatcherConditionSet::ID>& condition_sets = + const std::set<base::MatcherStringPattern::ID>& condition_sets = triggered_condition_sets_iter->second; for (auto j = condition_sets.begin(); j != condition_sets.end(); ++j) { auto condition_set_iter = url_matcher_condition_sets_.find(*j);
diff --git a/components/url_matcher/url_matcher.h b/components/url_matcher/url_matcher.h index 88ef5f2..15acc05 100644 --- a/components/url_matcher/url_matcher.h +++ b/components/url_matcher/url_matcher.h
@@ -207,12 +207,12 @@ bool append_end_of_query_component) const; // Return the next MatcherStringPattern id to use. - int GetNextID(); + base::MatcherStringPattern::ID GetNextID(); // Counter that ensures that all created MatcherStringPatterns have unique // IDs. Note that substring patterns and regex patterns will use different // IDs. - int id_counter_; + base::MatcherStringPattern::ID id_counter_ = 0; // This comparison considers only the pattern() value of the // MatcherStringPatterns. @@ -326,19 +326,18 @@ class URL_MATCHER_EXPORT URLMatcherConditionSet : public base::RefCounted<URLMatcherConditionSet> { public: - // Valid IDs will be >= 0. - typedef int ID; typedef std::set<URLMatcherCondition> Conditions; typedef std::set<URLQueryElementMatcherCondition> QueryConditions; typedef std::vector<scoped_refptr<URLMatcherConditionSet>> Vector; // Matches if all conditions in |conditions| are fulfilled. - URLMatcherConditionSet(ID id, const Conditions& conditions); + URLMatcherConditionSet(base::MatcherStringPattern::ID id, + const Conditions& conditions); // Matches if all conditions in |conditions|, |scheme_filter| and // |port_filter| are fulfilled. |scheme_filter| and |port_filter| may be NULL, // in which case, no restrictions are imposed on the scheme/port of a URL. - URLMatcherConditionSet(ID id, + URLMatcherConditionSet(base::MatcherStringPattern::ID id, const Conditions& conditions, std::unique_ptr<URLMatcherSchemeFilter> scheme_filter, std::unique_ptr<URLMatcherPortFilter> port_filter); @@ -347,7 +346,7 @@ // |scheme_filter| and |port_filter| are fulfilled. |scheme_filter| and // |port_filter| may be NULL, in which case, no restrictions are imposed on // the scheme/port of a URL. - URLMatcherConditionSet(ID id, + URLMatcherConditionSet(base::MatcherStringPattern::ID id, const Conditions& conditions, const QueryConditions& query_conditions, std::unique_ptr<URLMatcherSchemeFilter> scheme_filter, @@ -356,7 +355,7 @@ URLMatcherConditionSet(const URLMatcherConditionSet&) = delete; URLMatcherConditionSet& operator=(const URLMatcherConditionSet&) = delete; - ID id() const { return id_; } + base::MatcherStringPattern::ID id() const { return id_; } const Conditions& conditions() const { return conditions_; } const QueryConditions& query_conditions() const { return query_conditions_; } @@ -372,7 +371,7 @@ private: friend class base::RefCounted<URLMatcherConditionSet>; ~URLMatcherConditionSet(); - ID id_; + base::MatcherStringPattern::ID id_ = 0; Conditions conditions_; QueryConditions query_conditions_; std::unique_ptr<URLMatcherSchemeFilter> scheme_filter_; @@ -401,13 +400,13 @@ // currently registered. This function should be called with large batches // of |condition_set_ids| at a time to improve performance. void RemoveConditionSets( - const std::vector<URLMatcherConditionSet::ID>& condition_set_ids); + const std::vector<base::MatcherStringPattern::ID>& condition_set_ids); // Removes all unused condition sets from the ConditionFactory. void ClearUnusedConditionSets(); // Returns the IDs of all URLMatcherConditionSet that match to this |url|. - std::set<URLMatcherConditionSet::ID> MatchURL(const GURL& url) const; + std::set<base::MatcherStringPattern::ID> MatchURL(const GURL& url) const; // Returns the URLMatcherConditionFactory that must be used to create // URLMatcherConditionSets for this URLMatcher. @@ -429,7 +428,7 @@ // Maps the ID of a URLMatcherConditionSet to the respective // URLMatcherConditionSet. - typedef std::map<URLMatcherConditionSet::ID, + typedef std::map<base::MatcherStringPattern::ID, scoped_refptr<URLMatcherConditionSet>> URLMatcherConditionSets; URLMatcherConditionSets url_matcher_condition_sets_; @@ -437,7 +436,7 @@ // Maps a MatcherStringPattern ID to the URLMatcherConditions that need to // be triggered in case of a MatcherStringPattern match. typedef std::map<base::MatcherStringPattern::ID, - std::set<URLMatcherConditionSet::ID>> + std::set<base::MatcherStringPattern::ID>> MatcherStringPatternTriggers; MatcherStringPatternTriggers substring_match_triggers_;
diff --git a/components/url_matcher/url_matcher_factory.cc b/components/url_matcher/url_matcher_factory.cc index ca5b7cc4..cdc11bf 100644 --- a/components/url_matcher/url_matcher_factory.cc +++ b/components/url_matcher/url_matcher_factory.cc
@@ -109,7 +109,7 @@ URLMatcherFactory::CreateFromURLFilterDictionary( URLMatcherConditionFactory* url_matcher_condition_factory, const base::Value::Dict& url_filter_dict, - URLMatcherConditionSet::ID id, + base::MatcherStringPattern::ID id, std::string* error) { std::unique_ptr<URLMatcherSchemeFilter> url_matcher_schema_filter; std::unique_ptr<URLMatcherPortFilter> url_matcher_port_filter;
diff --git a/components/url_matcher/url_matcher_factory.h b/components/url_matcher/url_matcher_factory.h index 8ae9b0d2..ab11083 100644 --- a/components/url_matcher/url_matcher_factory.h +++ b/components/url_matcher/url_matcher_factory.h
@@ -33,7 +33,7 @@ static scoped_refptr<URLMatcherConditionSet> CreateFromURLFilterDictionary( URLMatcherConditionFactory* url_matcher_condition_factory, const base::Value::Dict& url_filter_dict, - URLMatcherConditionSet::ID id, + base::MatcherStringPattern::ID id, std::string* error); private:
diff --git a/components/url_matcher/url_matcher_unittest.cc b/components/url_matcher/url_matcher_unittest.cc index c6e6b12..6d5545f 100644 --- a/components/url_matcher/url_matcher_unittest.cc +++ b/components/url_matcher/url_matcher_unittest.cc
@@ -466,7 +466,7 @@ scoped_refptr<URLMatcherConditionSet> condition_set( new URLMatcherConditionSet(1, conditions)); - EXPECT_EQ(1, condition_set->id()); + EXPECT_EQ(1u, condition_set->id()); EXPECT_EQ(2u, condition_set->conditions().size()); } @@ -486,7 +486,7 @@ scoped_refptr<URLMatcherConditionSet> condition_set( new URLMatcherConditionSet(1, conditions)); - EXPECT_EQ(1, condition_set->id()); + EXPECT_EQ(1u, condition_set->id()); EXPECT_EQ(2u, condition_set->conditions().size()); std::set<MatcherStringPattern::ID> matching_patterns; @@ -763,7 +763,7 @@ conditions1.insert(factory->CreateHostSuffixCondition("example.com")); conditions1.insert(factory->CreatePathContainsCondition("foo")); - const int kConditionSetId1 = 1; + const base::MatcherStringPattern::ID kConditionSetId1 = 1; URLMatcherConditionSet::Vector insert1; insert1.push_back(base::MakeRefCounted<URLMatcherConditionSet>( kConditionSetId1, conditions1)); @@ -775,7 +775,7 @@ URLMatcherConditionSet::Conditions conditions2; conditions2.insert(factory->CreateHostSuffixCondition("example.com")); - const int kConditionSetId2 = 2; + const base::MatcherStringPattern::ID kConditionSetId2 = 2; URLMatcherConditionSet::Vector insert2; insert2.push_back(base::MakeRefCounted<URLMatcherConditionSet>( kConditionSetId2, conditions2)); @@ -784,7 +784,7 @@ EXPECT_EQ(1u, matcher.MatchURL(url2).size()); // This should be the cached singleton. - int patternId1 = + base::MatcherStringPattern::ID patternId1 = factory->CreateHostSuffixCondition("example.com").string_pattern()->id(); // Third insert. @@ -792,7 +792,7 @@ conditions3.insert(factory->CreateHostSuffixCondition("example.com")); conditions3.insert(factory->CreateURLMatchesCondition("x.*[0-9]")); - const int kConditionSetId3 = 3; + const base::MatcherStringPattern::ID kConditionSetId3 = 3; URLMatcherConditionSet::Vector insert3; insert3.push_back(base::MakeRefCounted<URLMatcherConditionSet>( kConditionSetId3, conditions3)); @@ -801,21 +801,21 @@ EXPECT_EQ(1u, matcher.MatchURL(url2).size()); // Removal of third insert. - std::vector<URLMatcherConditionSet::ID> remove3; + std::vector<base::MatcherStringPattern::ID> remove3; remove3.push_back(kConditionSetId3); matcher.RemoveConditionSets(remove3); EXPECT_EQ(2u, matcher.MatchURL(url1).size()); EXPECT_EQ(1u, matcher.MatchURL(url2).size()); // Removal of second insert. - std::vector<URLMatcherConditionSet::ID> remove2; + std::vector<base::MatcherStringPattern::ID> remove2; remove2.push_back(kConditionSetId2); matcher.RemoveConditionSets(remove2); EXPECT_EQ(1u, matcher.MatchURL(url1).size()); EXPECT_EQ(0u, matcher.MatchURL(url2).size()); // Removal of first insert. - std::vector<URLMatcherConditionSet::ID> remove1; + std::vector<base::MatcherStringPattern::ID> remove1; remove1.push_back(kConditionSetId1); matcher.RemoveConditionSets(remove1); EXPECT_EQ(0u, matcher.MatchURL(url1).size()); @@ -825,7 +825,7 @@ // The cached singleton in matcher.condition_factory_ should be destroyed to // free memory. - int patternId2 = + base::MatcherStringPattern::ID patternId2 = factory->CreateHostSuffixCondition("example.com").string_pattern()->id(); // If patternId1 and patternId2 are different that indicates that // matcher.condition_factory_ does not leak memory by holding onto @@ -878,7 +878,7 @@ // Due to '?' the condition created here is a prefix-testing condition. conditions.insert(factory->CreateQueryContainsCondition("?test=val&a=b")); - const int kConditionSetId = 1; + const base::MatcherStringPattern::ID kConditionSetId = 1; URLMatcherConditionSet::Vector insert; insert.push_back(base::MakeRefCounted<URLMatcherConditionSet>(kConditionSetId, conditions)); @@ -896,7 +896,7 @@ URLMatcherConditionSet::Conditions conditions; conditions.insert(factory->CreateOriginAndPathMatchesCondition("w..hp")); - const int kConditionSetId = 1; + const base::MatcherStringPattern::ID kConditionSetId = 1; URLMatcherConditionSet::Vector insert; insert.push_back(base::MakeRefCounted<URLMatcherConditionSet>(kConditionSetId, conditions)); @@ -914,7 +914,7 @@ URLMatcherConditionSet::Conditions conditions; conditions.insert(factory->CreateOriginAndPathMatchesCondition("val")); - const int kConditionSetId = 1; + const base::MatcherStringPattern::ID kConditionSetId = 1; URLMatcherConditionSet::Vector insert; insert.push_back(base::MakeRefCounted<URLMatcherConditionSet>(kConditionSetId, conditions));
diff --git a/components/url_matcher/url_util.cc b/components/url_matcher/url_util.cc index 41a0c73..4cec96d 100644 --- a/components/url_matcher/url_util.cc +++ b/components/url_matcher/url_util.cc
@@ -260,7 +260,7 @@ scoped_refptr<URLMatcherConditionSet> CreateConditionSet( URLMatcher* url_matcher, - int id, + base::MatcherStringPattern::ID id, const std::string& scheme, const std::string& host, bool match_subdomains, @@ -410,9 +410,9 @@ void AddFilters(URLMatcher* matcher, bool allow, - URLMatcherConditionSet::ID* id, + base::MatcherStringPattern::ID* id, const base::ListValue* patterns, - std::map<url_matcher::URLMatcherConditionSet::ID, + std::map<base::MatcherStringPattern::ID, url_matcher::util::FilterComponents>* filters) { URLMatcherConditionSet::Vector all_conditions; base::Value::ConstListView patterns_list = patterns->GetListDeprecated(); @@ -449,9 +449,9 @@ void AddFilters(URLMatcher* matcher, bool allow, - URLMatcherConditionSet::ID* id, + base::MatcherStringPattern::ID* id, const std::vector<std::string>& patterns, - std::map<url_matcher::URLMatcherConditionSet::ID, + std::map<base::MatcherStringPattern::ID, url_matcher::util::FilterComponents>* filters) { URLMatcherConditionSet::Vector all_conditions; size_t size = std::min(kMaxFiltersAllowed, patterns.size()); @@ -481,13 +481,13 @@ void AddAllowFilters(url_matcher::URLMatcher* matcher, const base::ListValue* patterns) { - url_matcher::URLMatcherConditionSet::ID id(0); + base::MatcherStringPattern::ID id(0); AddFilters(matcher, true, &id, patterns); } void AddAllowFilters(url_matcher::URLMatcher* matcher, const std::vector<std::string>& patterns) { - url_matcher::URLMatcherConditionSet::ID id(0); + base::MatcherStringPattern::ID id(0); AddFilters(matcher, true, &id, patterns); }
diff --git a/components/url_matcher/url_util.h b/components/url_matcher/url_util.h index 868f2ea..8a29308 100644 --- a/components/url_matcher/url_util.h +++ b/components/url_matcher/url_util.h
@@ -57,7 +57,7 @@ // or block-list (false) filter. URL_MATCHER_EXPORT scoped_refptr<url_matcher::URLMatcherConditionSet> CreateConditionSet(url_matcher::URLMatcher* url_matcher, - url_matcher::URLMatcherConditionSet::ID id, + base::MatcherStringPattern::ID id, const std::string& scheme, const std::string& host, bool match_subdomains, @@ -98,9 +98,9 @@ URL_MATCHER_EXPORT void AddFilters( url_matcher::URLMatcher* matcher, bool allow, - url_matcher::URLMatcherConditionSet::ID* id, + base::MatcherStringPattern::ID* id, const base::ListValue* patterns, - std::map<url_matcher::URLMatcherConditionSet::ID, + std::map<base::MatcherStringPattern::ID, url_matcher::util::FilterComponents>* filters = nullptr); // Adds the filters in `patterns` to `url_matcher` as a ConditionSet::Vector. @@ -115,9 +115,9 @@ URL_MATCHER_EXPORT void AddFilters( url_matcher::URLMatcher* matcher, bool allow, - url_matcher::URLMatcherConditionSet::ID* id, + base::MatcherStringPattern::ID* id, const std::vector<std::string>& patterns, - std::map<url_matcher::URLMatcherConditionSet::ID, + std::map<base::MatcherStringPattern::ID, url_matcher::util::FilterComponents>* filters = nullptr); URL_MATCHER_EXPORT void AddAllowFilters(url_matcher::URLMatcher* matcher,
diff --git a/components/viz/service/display/display.cc b/components/viz/service/display/display.cc index 7204a6aa..c374c6d 100644 --- a/components/viz/service/display/display.cc +++ b/components/viz/service/display/display.cc
@@ -108,9 +108,6 @@ gfx::PresentationFeedback SanitizePresentationFeedback( const gfx::PresentationFeedback& feedback, base::TimeTicks draw_time) { - // Temporary to investigate large presentation times. - // https://crbug.com/894440 - DCHECK(!draw_time.is_null()); if (feedback.timestamp.is_null()) return feedback; @@ -130,26 +127,10 @@ gfx::PresentationFeedback::kVSync)) != 0) ? kAllowedDeltaFromFuture : base::TimeDelta(); - if (feedback.timestamp > now + allowed_delta_from_future) { - const auto diff = feedback.timestamp - now; - UMA_HISTOGRAM_MEDIUM_TIMES( - "Graphics.PresentationTimestamp.InvalidFromFuture", diff); + if ((feedback.timestamp > now + allowed_delta_from_future) || + (feedback.timestamp < draw_time)) { return gfx::PresentationFeedback::Failure(); } - - if (feedback.timestamp < draw_time) { - const auto diff = draw_time - feedback.timestamp; - UMA_HISTOGRAM_MEDIUM_TIMES( - "Graphics.PresentationTimestamp.InvalidBeforeSwap", diff); - return gfx::PresentationFeedback::Failure(); - } - - const auto difference = feedback.timestamp - draw_time; - if (difference.InMinutes() > 3) { - UMA_HISTOGRAM_CUSTOM_TIMES( - "Graphics.PresentationTimestamp.LargePresentationDelta", difference, - base::Minutes(3), base::Hours(1), 50); - } return feedback; }
diff --git a/components/viz/service/display/display_unittest.cc b/components/viz/service/display/display_unittest.cc index b464a80..9c0a5087 100644 --- a/components/viz/service/display/display_unittest.cc +++ b/components/viz/service/display/display_unittest.cc
@@ -68,7 +68,6 @@ #include "ui/gfx/delegated_ink_point.h" #include "ui/gfx/mojom/delegated_ink_point_renderer.mojom.h" #include "ui/gfx/overlay_transform.h" -#include "ui/gfx/presentation_feedback.h" using testing::_; using testing::AnyNumber; @@ -3752,137 +3751,6 @@ UpdateBeginFrameTime(sub_support.get(), frame_time); } -TEST_F(DisplayTest, InvalidPresentationTimestamps) { - RendererSettings settings; - id_allocator_.GenerateId(); - const LocalSurfaceId local_surface_id( - id_allocator_.GetCurrentLocalSurfaceId()); - - // Set up first display. - SetUpSoftwareDisplay(settings); - StubDisplayClient client; - display_->Initialize(&client, manager_.surface_manager()); - display_->SetLocalSurfaceId(local_surface_id, 1.f); - display_->Resize(gfx::Size(25, 25)); - - { - // A regular presentation timestamp. - base::HistogramTester histograms; - CompositorFrame frame = - CompositorFrameBuilder() - .AddRenderPass(gfx::Rect(25, 25), gfx::Rect(25, 25)) - .Build(); - support_->SubmitCompositorFrame(local_surface_id, std::move(frame)); - display_->DrawAndSwap({base::TimeTicks::Now(), base::TimeTicks::Now()}); - display_->DidReceiveSwapBuffersAck(GetTestSwapTimings(), - /*release_fence=*/gfx::GpuFenceHandle()); - display_->DidReceivePresentationFeedback({base::TimeTicks::Now(), {}, 0}); - EXPECT_THAT(histograms.GetAllSamples( - "Graphics.PresentationTimestamp.InvalidBeforeSwap"), - testing::IsEmpty()); - EXPECT_THAT(histograms.GetAllSamples( - "Graphics.PresentationTimestamp.InvalidFromFuture"), - testing::IsEmpty()); - } - - { - // A presentation-timestamp that is earlier than the swap time. - base::HistogramTester histograms; - CompositorFrame frame = - CompositorFrameBuilder() - .AddRenderPass(gfx::Rect(25, 25), gfx::Rect(25, 25)) - .Build(); - support_->SubmitCompositorFrame(local_surface_id, std::move(frame)); - display_->DrawAndSwap({base::TimeTicks::Now(), base::TimeTicks::Now()}); - display_->DidReceiveSwapBuffersAck(GetTestSwapTimings(), - /*release_fence=*/gfx::GpuFenceHandle()); - display_->DidReceivePresentationFeedback( - {base::TimeTicks::Now() - base::Seconds(1), {}, 0}); - EXPECT_THAT(histograms.GetAllSamples( - "Graphics.PresentationTimestamp.InvalidFromFuture"), - testing::IsEmpty()); - auto buckets = histograms.GetAllSamples( - "Graphics.PresentationTimestamp.InvalidBeforeSwap"); - ASSERT_EQ(buckets.size(), 1u); - EXPECT_GT(buckets[0].min, 0); - EXPECT_LE(buckets[0].min, 1000); - EXPECT_EQ(buckets[0].count, 1); - } - - { - // A presentation-timestamp that is in the near-future with hwclock: this - // should be valid. - base::HistogramTester histograms; - CompositorFrame frame = - CompositorFrameBuilder() - .AddRenderPass(gfx::Rect(25, 25), gfx::Rect(25, 25)) - .Build(); - support_->SubmitCompositorFrame(local_surface_id, std::move(frame)); - display_->DrawAndSwap({base::TimeTicks::Now(), base::TimeTicks::Now()}); - display_->DidReceiveSwapBuffersAck(GetTestSwapTimings(), - /*release_fence=*/gfx::GpuFenceHandle()); - display_->DidReceivePresentationFeedback( - {base::TimeTicks::Now() + base::Milliseconds(1), - {}, - gfx::PresentationFeedback::kHWClock}); - EXPECT_THAT(histograms.GetAllSamples( - "Graphics.PresentationTimestamp.InvalidBeforeSwap"), - testing::IsEmpty()); - EXPECT_THAT(histograms.GetAllSamples( - "Graphics.PresentationTimestamp.InvalidFromFuture"), - testing::IsEmpty()); - } - - { - // A presentation-timestamp that is in the near-future. - base::HistogramTester histograms; - CompositorFrame frame = - CompositorFrameBuilder() - .AddRenderPass(gfx::Rect(25, 25), gfx::Rect(25, 25)) - .Build(); - support_->SubmitCompositorFrame(local_surface_id, std::move(frame)); - display_->DrawAndSwap({base::TimeTicks::Now(), base::TimeTicks::Now()}); - display_->DidReceiveSwapBuffersAck(GetTestSwapTimings(), - /*release_fence=*/gfx::GpuFenceHandle()); - display_->DidReceivePresentationFeedback( - {base::TimeTicks::Now() + base::Milliseconds(1), {}, 0}); - EXPECT_THAT(histograms.GetAllSamples( - "Graphics.PresentationTimestamp.InvalidBeforeSwap"), - testing::IsEmpty()); - auto buckets = histograms.GetAllSamples( - "Graphics.PresentationTimestamp.InvalidFromFuture"); - ASSERT_EQ(buckets.size(), 1u); - EXPECT_GE(buckets[0].min, 0); - EXPECT_LE(buckets[0].min, 1); - EXPECT_EQ(buckets[0].count, 1); - } - - { - // A presentation-timestamp that is in the future. - base::HistogramTester histograms; - CompositorFrame frame = - CompositorFrameBuilder() - .AddRenderPass(gfx::Rect(25, 25), gfx::Rect(25, 25)) - .Build(); - support_->SubmitCompositorFrame(local_surface_id, std::move(frame)); - display_->DrawAndSwap({base::TimeTicks::Now(), base::TimeTicks::Now()}); - display_->DidReceiveSwapBuffersAck(GetTestSwapTimings(), - /*release_fence=*/gfx::GpuFenceHandle()); - display_->DidReceivePresentationFeedback( - {base::TimeTicks::Now() + base::Seconds(1), {}, 0}); - EXPECT_THAT(histograms.GetAllSamples( - "Graphics.PresentationTimestamp.InvalidBeforeSwap"), - testing::IsEmpty()); - - auto buckets = histograms.GetAllSamples( - "Graphics.PresentationTimestamp.InvalidFromFuture"); - ASSERT_EQ(buckets.size(), 1u); - EXPECT_GT(buckets[0].min, 0); - EXPECT_LE(buckets[0].min, 1000); - EXPECT_EQ(buckets[0].count, 1); - } -} - TEST_F(DisplayTest, DrawOcclusionWithRoundedCornerDoesNotOcclude) { SetUpGpuDisplay(RendererSettings());
diff --git a/components/webrtc/media_stream_devices_controller.cc b/components/webrtc/media_stream_devices_controller.cc index 95d0140..e9cd2d78 100644 --- a/components/webrtc/media_stream_devices_controller.cc +++ b/components/webrtc/media_stream_devices_controller.cc
@@ -22,6 +22,7 @@ #include "services/network/public/cpp/is_potentially_trustworthy.h" #include "third_party/blink/public/common/permissions/permission_utils.h" #include "third_party/blink/public/mojom/mediastream/media_stream.mojom.h" +#include "third_party/blink/public/mojom/permissions/permission_status.mojom-shared.h" #include "third_party/blink/public/mojom/permissions_policy/permissions_policy.mojom.h" #if BUILDFLAG(IS_ANDROID) @@ -95,8 +96,6 @@ permissions::PermissionManager* permission_manager = permissions::PermissionsClient::Get()->GetPermissionManager( web_contents->GetBrowserContext()); - bool will_prompt_for_audio = false; - bool will_prompt_for_video = false; if (controller->ShouldRequestAudio()) { permissions::PermissionResult permission_status = @@ -112,8 +111,6 @@ } permission_types.push_back(blink::PermissionType::AUDIO_CAPTURE); - will_prompt_for_audio = - permission_status.content_setting == CONTENT_SETTING_ASK; } if (controller->ShouldRequestVideo()) { permissions::PermissionResult permission_status = @@ -129,8 +126,6 @@ } permission_types.push_back(blink::PermissionType::VIDEO_CAPTURE); - will_prompt_for_video = - permission_status.content_setting == CONTENT_SETTING_ASK; bool has_pan_tilt_zoom_camera = controller->HasAvailableDevices( ContentSettingsType::CAMERA_PAN_TILT_ZOOM, @@ -164,9 +159,8 @@ ->RequestPermissionsFromCurrentDocument( permission_types, rfh, request.user_gesture, base::BindOnce( - &MediaStreamDevicesController::RequestAndroidPermissionsIfNeeded, - web_contents, std::move(controller), will_prompt_for_audio, - will_prompt_for_video)); + &MediaStreamDevicesController::PromptAnsweredGroupedRequest, + std::move(controller))); } MediaStreamDevicesController::~MediaStreamDevicesController() { @@ -200,98 +194,6 @@ request, &denial_reason_); } -void MediaStreamDevicesController::RequestAndroidPermissionsIfNeeded( - content::WebContents* web_contents, - std::unique_ptr<MediaStreamDevicesController> controller, - bool did_prompt_for_audio, - bool did_prompt_for_video, - const std::vector<blink::mojom::PermissionStatus>& permissions_status) { - std::vector<ContentSetting> responses; - std::transform(permissions_status.begin(), permissions_status.end(), - back_inserter(responses), - permissions::PermissionUtil::PermissionStatusToContentSetting); - -#if BUILDFLAG(IS_ANDROID) - // If either audio or video was previously allowed and Chrome no longer has - // the necessary permissions, show a infobar to attempt to address this - // mismatch. - std::vector<ContentSettingsType> content_settings_types; - // The audio setting will always be the first one in the vector, if it was - // requested. - // If the user was already prompted for mic (|did_prompt_for_audio| flag), we - // would have requested Android permission at that point. - if (!did_prompt_for_audio && controller->ShouldRequestAudio() && - responses.front() == CONTENT_SETTING_ALLOW) { - content_settings_types.push_back(ContentSettingsType::MEDIASTREAM_MIC); - } - - // If the user was already prompted for camera (|did_prompt_for_video| flag), - // we would have requested Android permission at that point. - if (!did_prompt_for_video && controller->ShouldRequestVideo() && - responses.back() == CONTENT_SETTING_ALLOW) { - content_settings_types.push_back(ContentSettingsType::MEDIASTREAM_CAMERA); - } - - // If the user was already prompted for camera (|did_prompt_for_video| flag), - // we would have requested Android permission at that point. - if (!did_prompt_for_video && controller->ShouldRequestVideo() && - responses.back() == CONTENT_SETTING_ALLOW) { - content_settings_types.push_back(ContentSettingsType::MEDIASTREAM_CAMERA); - } - if (content_settings_types.empty()) { - controller->PromptAnsweredGroupedRequest(responses); - return; - } - - permissions::PermissionRepromptState reprompt_state = - permissions::ShouldRepromptUserForPermissions(web_contents, - content_settings_types); - switch (reprompt_state) { - case permissions::PermissionRepromptState::kNoNeed: - controller->PromptAnsweredGroupedRequest(responses); - return; - - case permissions::PermissionRepromptState::kShow: - permissions::PermissionsClient::Get()->RepromptForAndroidPermissions( - web_contents, content_settings_types, - base::BindOnce(&MediaStreamDevicesController::AndroidOSPromptAnswered, - std::move(controller), responses)); - return; - - case permissions::PermissionRepromptState::kCannotShow: { - std::vector<ContentSetting> blocked_responses(responses.size(), - CONTENT_SETTING_BLOCK); - controller->PromptAnsweredGroupedRequest(blocked_responses); - return; - } - } - - NOTREACHED() << "Unknown show permission infobar state."; -#else - controller->PromptAnsweredGroupedRequest(responses); -#endif -} - -#if BUILDFLAG(IS_ANDROID) -// static -void MediaStreamDevicesController::AndroidOSPromptAnswered( - std::unique_ptr<MediaStreamDevicesController> controller, - std::vector<ContentSetting> responses, - bool android_prompt_granted) { - if (!android_prompt_granted) { - // Only permissions that were previously ALLOW for a site will have had - // their android permissions requested. It's only in that case that we need - // to change the setting to BLOCK to reflect that it wasn't allowed. - for (size_t i = 0; i < responses.size(); ++i) { - if (responses[i] == CONTENT_SETTING_ALLOW) - responses[i] = CONTENT_SETTING_BLOCK; - } - } - - controller->PromptAnsweredGroupedRequest(responses); -} -#endif // BUILDFLAG(IS_ANDROID) - bool MediaStreamDevicesController::ShouldRequestAudio() const { return audio_setting_ == CONTENT_SETTING_ASK; } @@ -522,7 +424,7 @@ } void MediaStreamDevicesController::PromptAnsweredGroupedRequest( - const std::vector<ContentSetting>& responses) { + const std::vector<blink::mojom::PermissionStatus>& permissions_status) { if (content::RenderFrameHost::FromID(request_.render_process_id, request_.render_frame_id) == nullptr) { // The frame requesting media devices was removed while we were waiting for @@ -530,6 +432,11 @@ return; } + std::vector<ContentSetting> responses; + std::transform(permissions_status.begin(), permissions_status.end(), + back_inserter(responses), + permissions::PermissionUtil::PermissionStatusToContentSetting); + bool need_audio = ShouldRequestAudio(); bool need_video = ShouldRequestVideo(); bool blocked_by_permissions_policy = need_audio || need_video;
diff --git a/components/webrtc/media_stream_devices_controller.h b/components/webrtc/media_stream_devices_controller.h index 61b20438..6f0043b 100644 --- a/components/webrtc/media_stream_devices_controller.h +++ b/components/webrtc/media_stream_devices_controller.h
@@ -66,14 +66,6 @@ bool did_prompt_for_video, const std::vector<blink::mojom::PermissionStatus>& responses); -#if BUILDFLAG(IS_ANDROID) - // Called when the Android OS-level prompt is answered. - static void AndroidOSPromptAnswered( - std::unique_ptr<MediaStreamDevicesController> controller, - std::vector<ContentSetting> responses, - bool android_prompt_granted); -#endif // BUILDFLAG(IS_ANDROID) - // Returns true if audio/video should be requested through the // PermissionManager. We won't try to request permission if the request is // already blocked for some other reason, e.g. there are no devices available. @@ -104,7 +96,7 @@ // Called when a permission prompt is answered through the PermissionManager. void PromptAnsweredGroupedRequest( - const std::vector<ContentSetting>& responses); + const std::vector<blink::mojom::PermissionStatus>& permissions_status); bool HasAvailableDevices(ContentSettingsType content_type, const std::string& device_id) const;
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn index eb4f27a..f6a133d4 100644 --- a/content/browser/BUILD.gn +++ b/content/browser/BUILD.gn
@@ -2738,7 +2738,7 @@ assert(enable_basic_printing) deps += [ "//printing" ] - if (is_chromeos_ash) { + if (is_chromeos) { sources += [ "renderer_host/pepper/pepper_vpn_provider_message_filter_chromeos.cc", "renderer_host/pepper/pepper_vpn_provider_message_filter_chromeos.h", @@ -3179,6 +3179,8 @@ "xr/service/xr_device_service.h", "xr/service/xr_frame_sink_client_impl.cc", "xr/service/xr_frame_sink_client_impl.h", + "xr/service/xr_permission_results.cc", + "xr/service/xr_permission_results.h", "xr/service/xr_runtime_manager_impl.cc", "xr/service/xr_runtime_manager_impl.h", "xr/xr_utils.cc",
diff --git a/content/browser/attribution_reporting/attribution_report.cc b/content/browser/attribution_reporting/attribution_report.cc index 7ac5038..d81e437 100644 --- a/content/browser/attribution_reporting/attribution_report.cc +++ b/content/browser/attribution_reporting/attribution_report.cc
@@ -239,7 +239,6 @@ cbor::Value::MapValue value; value.emplace("reporting_origin", common_source_info.reporting_origin().Serialize()); - value.emplace("source_site", common_source_info.ImpressionSite().Serialize()); value.emplace("destination", common_source_info.ConversionDestination().Serialize());
diff --git a/content/browser/attribution_reporting/attribution_report_network_sender_unittest.cc b/content/browser/attribution_reporting/attribution_report_network_sender_unittest.cc index 8c86850..747f9e0 100644 --- a/content/browser/attribution_reporting/attribution_report_network_sender_unittest.cc +++ b/content/browser/attribution_reporting/attribution_report_network_sender_unittest.cc
@@ -45,13 +45,20 @@ using Checkpoint = ::testing::MockFunction<void(int)>; -const char kReportUrl[] = +const char kEventLevelReportUrl[] = "https://report.test/.well-known/attribution-reporting/" "report-event-attribution"; -const char kDebugReportUrl[] = +const char kDebugEventLevelReportUrl[] = "https://report.test/.well-known/attribution-reporting/debug/" "report-event-attribution"; +const char kAggregatableReportUrl[] = + "https://report.test/.well-known/attribution-reporting/" + "report-aggregate-attribution"; +const char kDebugAggregatableReportUrl[] = + "https://report.test/.well-known/attribution-reporting/debug/" + "report-aggregate-attribution"; + AttributionReport DefaultReport() { return ReportBuilder( AttributionInfoBuilder(SourceBuilder(base::Time()).BuildStored()) @@ -95,7 +102,7 @@ base::DoNothing()); EXPECT_EQ(1, test_url_loader_factory_.NumPending()); EXPECT_TRUE(test_url_loader_factory_.SimulateResponseForPendingRequest( - kReportUrl, "")); + kEventLevelReportUrl, "")); } TEST_F(AttributionReportNetworkSenderTest, LoadFlags) { @@ -169,12 +176,12 @@ base::DoNothing()); const network::ResourceRequest* pending_request; - EXPECT_TRUE( - test_url_loader_factory_.IsPending(kReportUrl, &pending_request)); + EXPECT_TRUE(test_url_loader_factory_.IsPending(kEventLevelReportUrl, + &pending_request)); EXPECT_EQ(test_case.expected_report, network::GetUploadData(*pending_request)); EXPECT_TRUE(test_url_loader_factory_.SimulateResponseForPendingRequest( - kReportUrl, "")); + kEventLevelReportUrl, "")); } } @@ -199,19 +206,20 @@ base::DoNothing()); const network::ResourceRequest* pending_request; - EXPECT_TRUE( - test_url_loader_factory_.IsPending(kDebugReportUrl, &pending_request)); + EXPECT_TRUE(test_url_loader_factory_.IsPending(kDebugEventLevelReportUrl, + &pending_request)); EXPECT_EQ(kExpectedReportBody, network::GetUploadData(*pending_request)); EXPECT_TRUE(test_url_loader_factory_.SimulateResponseForPendingRequest( - kDebugReportUrl, "")); + kDebugEventLevelReportUrl, "")); // Verify that debug and non-debug reports have the same body. network_sender_->SendReport(report, /*is_debug_report=*/false, base::DoNothing()); - EXPECT_TRUE(test_url_loader_factory_.IsPending(kReportUrl, &pending_request)); + EXPECT_TRUE(test_url_loader_factory_.IsPending(kEventLevelReportUrl, + &pending_request)); EXPECT_EQ(kExpectedReportBody, network::GetUploadData(*pending_request)); EXPECT_TRUE(test_url_loader_factory_.SimulateResponseForPendingRequest( - kReportUrl, "")); + kEventLevelReportUrl, "")); } TEST_F(AttributionReportNetworkSenderTest, DebugReportSent_NoMetricsRecorded) { @@ -289,12 +297,12 @@ base::DoNothing()); const network::ResourceRequest* pending_request; - EXPECT_TRUE( - test_url_loader_factory_.IsPending(kReportUrl, &pending_request)); + EXPECT_TRUE(test_url_loader_factory_.IsPending(kEventLevelReportUrl, + &pending_request)); EXPECT_EQ(test_case.expected_report, network::GetUploadData(*pending_request)); EXPECT_TRUE(test_url_loader_factory_.SimulateResponseForPendingRequest( - kReportUrl, "")); + kEventLevelReportUrl, "")); } } @@ -344,7 +352,7 @@ callback_.Get()); EXPECT_EQ(1, test_url_loader_factory_.NumPending()); EXPECT_TRUE(test_url_loader_factory_.SimulateResponseForPendingRequest( - kReportUrl, "", code)); + kEventLevelReportUrl, "", code)); Mock::VerifyAndClear(&callback_); } @@ -359,7 +367,7 @@ EXPECT_EQ(1, test_url_loader_factory_.NumPending()); network_sender_.reset(); EXPECT_FALSE(test_url_loader_factory_.SimulateResponseForPendingRequest( - kReportUrl, "")); + kEventLevelReportUrl, "")); } TEST_F(AttributionReportNetworkSenderTest, ReportRequestHangs_TimesOut) { @@ -406,7 +414,7 @@ // By default, headers are not sent for network errors. test_url_loader_factory_.SimulateResponseForPendingRequest( - GURL(kReportUrl), + GURL(kEventLevelReportUrl), network::URLLoaderCompletionStatus(test_case.net_error), network::mojom::URLResponseHead::New(), ""); @@ -421,7 +429,7 @@ // Simulate a retry-able network error with headers received. test_url_loader_factory_.AddResponse( - GURL(kReportUrl), + GURL(kEventLevelReportUrl), /*head=*/std::move(head), /*content=*/"", network::URLLoaderCompletionStatus(net::ERR_INTERNET_DISCONNECTED), network::TestURLLoaderFactory::Redirects(), @@ -452,7 +460,7 @@ EXPECT_EQ(1, test_url_loader_factory_.NumPending()); EXPECT_TRUE(test_url_loader_factory_.SimulateResponseForPendingRequest( - kReportUrl, "", net::HttpStatusCode::HTTP_BAD_REQUEST)); + kEventLevelReportUrl, "", net::HttpStatusCode::HTTP_BAD_REQUEST)); } TEST_F(AttributionReportNetworkSenderTest, @@ -470,7 +478,7 @@ // Simulate the request failing due to network change. test_url_loader_factory_.SimulateResponseForPendingRequest( - GURL(kReportUrl), + GURL(kEventLevelReportUrl), network::URLLoaderCompletionStatus(net::ERR_NETWORK_CHANGED), network::mojom::URLResponseHead::New(), ""); @@ -479,7 +487,7 @@ // Simulate a second request failure due to network change. test_url_loader_factory_.SimulateResponseForPendingRequest( - GURL(kReportUrl), + GURL(kEventLevelReportUrl), network::URLLoaderCompletionStatus(net::ERR_NETWORK_CHANGED), network::mojom::URLResponseHead::New(), ""); @@ -502,7 +510,7 @@ // Simulate the request failing due to network change. test_url_loader_factory_.SimulateResponseForPendingRequest( - GURL(kReportUrl), + GURL(kEventLevelReportUrl), network::URLLoaderCompletionStatus(net::ERR_NETWORK_CHANGED), network::mojom::URLResponseHead::New(), ""); @@ -510,7 +518,8 @@ EXPECT_EQ(1, test_url_loader_factory_.NumPending()); // Simulate a second request failure due to network change. - test_url_loader_factory_.SimulateResponseForPendingRequest(kReportUrl, ""); + test_url_loader_factory_.SimulateResponseForPendingRequest( + kEventLevelReportUrl, ""); histograms.ExpectUniqueSample("Conversions.ReportRetrySucceed2", true, 1); } @@ -538,7 +547,7 @@ // We should run the sent callback even if there is an http error. EXPECT_TRUE(test_url_loader_factory_.SimulateResponseForPendingRequest( - kReportUrl, "", net::HttpStatusCode::HTTP_BAD_REQUEST)); + kEventLevelReportUrl, "", net::HttpStatusCode::HTTP_BAD_REQUEST)); } TEST_F(AttributionReportNetworkSenderTest, ManyReports_AllSentSuccessfully) { @@ -555,7 +564,7 @@ // properly handled. for (int i = 9; i >= 0; i--) { EXPECT_TRUE(test_url_loader_factory_.SimulateResponseForPendingRequest( - kReportUrl, "")); + kEventLevelReportUrl, "")); } EXPECT_EQ(0, test_url_loader_factory_.NumPending()); } @@ -568,7 +577,7 @@ network_sender_->SendReport(report, /*is_debug_report=*/false, base::DoNothing()); EXPECT_TRUE(test_url_loader_factory_.SimulateResponseForPendingRequest( - kReportUrl, "")); + kEventLevelReportUrl, "")); // kOk = 0. histograms.ExpectUniqueSample("Conversions.ReportStatus2", 0, 1); histograms.ExpectUniqueSample( @@ -582,7 +591,7 @@ base::DoNothing()); network::URLLoaderCompletionStatus completion_status(net::ERR_FAILED); EXPECT_TRUE(test_url_loader_factory_.SimulateResponseForPendingRequest( - GURL(kReportUrl), completion_status, + GURL(kEventLevelReportUrl), completion_status, network::mojom::URLResponseHead::New(), "")); // kInternalError = 1. histograms.ExpectUniqueSample("Conversions.ReportStatus2", 1, 1); @@ -595,7 +604,7 @@ network_sender_->SendReport(report, /*is_debug_report=*/false, base::DoNothing()); EXPECT_TRUE(test_url_loader_factory_.SimulateResponseForPendingRequest( - kReportUrl, "", net::HTTP_UNAUTHORIZED)); + kEventLevelReportUrl, "", net::HTTP_UNAUTHORIZED)); // kExternalError = 2. histograms.ExpectUniqueSample("Conversions.ReportStatus2", 2, 1); histograms.ExpectUniqueSample( @@ -604,4 +613,71 @@ } } +TEST_F(AttributionReportNetworkSenderTest, + AggregatableReportSent_ReportBodySetCorrectly) { + static constexpr char kExpectedReportBody[] = + R"({"aggregation_service_payloads":"not generated prior to send",)" + R"("attribution_destination":"https://conversion.test",)" + R"("shared_info":"not generated prior to send",)" + R"("source_registration_time":"1234483200",)" + R"("source_site":"https://impression.test"})"; + + AttributionReport report = + ReportBuilder(AttributionInfoBuilder( + SourceBuilder(base::Time::FromJavaTime(1234483200000)) + .BuildStored()) + .Build()) + .SetAggregatableHistogramContributions( + {AggregatableHistogramContribution(/*key=*/1, /*value=*/2)}) + .BuildAggregatableAttribution(); + + network_sender_->SendReport(report, /*is_debug_report=*/false, + base::DoNothing()); + + const network::ResourceRequest* pending_request; + EXPECT_TRUE(test_url_loader_factory_.IsPending(kAggregatableReportUrl, + &pending_request)); + EXPECT_EQ(kExpectedReportBody, network::GetUploadData(*pending_request)); + EXPECT_TRUE(test_url_loader_factory_.SimulateResponseForPendingRequest( + kAggregatableReportUrl, "")); +} + +TEST_F(AttributionReportNetworkSenderTest, + DebugAggregatableReportSent_ReportUrlAndBodySetCorrectly) { + static constexpr char kExpectedReportBody[] = + R"({"aggregation_service_payloads":"not generated prior to send",)" + R"("attribution_destination":"https://conversion.test",)" + R"("shared_info":"not generated prior to send",)" + R"("source_registration_time":"1234483200",)" + R"("source_site":"https://impression.test"})"; + + AttributionReport report = + ReportBuilder(AttributionInfoBuilder( + SourceBuilder(base::Time::FromJavaTime(1234483200000)) + .BuildStored()) + .Build()) + .SetAggregatableHistogramContributions( + {AggregatableHistogramContribution(/*key=*/1, /*value=*/2)}) + .BuildAggregatableAttribution(); + + network_sender_->SendReport(report, /*is_debug_report=*/true, + base::DoNothing()); + + const network::ResourceRequest* pending_request; + EXPECT_TRUE(test_url_loader_factory_.IsPending(kDebugAggregatableReportUrl, + &pending_request)); + EXPECT_EQ(kExpectedReportBody, network::GetUploadData(*pending_request)); + EXPECT_TRUE(test_url_loader_factory_.SimulateResponseForPendingRequest( + kDebugAggregatableReportUrl, "")); + + // Verify that debug and non-debug reports have the same body. + network_sender_->SendReport(report, /*is_debug_report=*/false, + base::DoNothing()); + EXPECT_TRUE(test_url_loader_factory_.IsPending(kAggregatableReportUrl, + &pending_request)); + EXPECT_EQ(kExpectedReportBody, network::GetUploadData(*pending_request)); + EXPECT_TRUE(test_url_loader_factory_.SimulateResponseForPendingRequest( + kAggregatableReportUrl, "")); +} + } // namespace content
diff --git a/content/browser/attribution_reporting/attribution_report_unittest.cc b/content/browser/attribution_reporting/attribution_report_unittest.cc index dae79ea9..ed5d7a0 100644 --- a/content/browser/attribution_reporting/attribution_report_unittest.cc +++ b/content/browser/attribution_reporting/attribution_report_unittest.cc
@@ -80,14 +80,8 @@ } TEST(AttributionReportTest, PrivacyBudgetKey) { - // Pre-hashed CBOR bytes, base64-encoded for brevity: - // "pWd2ZXJzaW9uYGtkZXN0aW5hdGlvbndodHRwczovL2NvbnZlcnNpb24udGVzdGtzb3VyY2Vf" - // "c2l0ZXdodHRwczovL2ltcHJlc3Npb24udGVzdHByZXBvcnRpbmdfb3JpZ2luc2h0dHBzOi8v" - // "cmVwb3J0LnRlc3R4GHNvdXJjZV9yZWdpc3RyYXRpb25fdGltZRpJlLgA" - - // base64-encoded SHA256 hash string of the bytes above. const std::string kExpectedPrivacyBudgetKey( - "aOEbtVxG8dYzAR2K/xuW/OppNaQikp5RdjAXshOQ9w8="); + "hTxwDPlUIel3dB39m09GOkeAkMLcYVdE6H0eK2t1RYc="); AttributionReport report = ReportBuilder(AttributionInfoBuilder(
diff --git a/content/browser/renderer_host/pepper/content_browser_pepper_host_factory.cc b/content/browser/renderer_host/pepper/content_browser_pepper_host_factory.cc index f1befd4..949a1e2 100644 --- a/content/browser/renderer_host/pepper/content_browser_pepper_host_factory.cc +++ b/content/browser/renderer_host/pepper/content_browser_pepper_host_factory.cc
@@ -28,7 +28,7 @@ #include "ppapi/proxy/ppapi_messages.h" #include "ppapi/shared_impl/ppapi_permissions.h" -#if BUILDFLAG(IS_CHROMEOS_ASH) +#if BUILDFLAG(IS_CHROMEOS) #include "content/browser/renderer_host/pepper/pepper_vpn_provider_message_filter_chromeos.h" #endif @@ -173,7 +173,7 @@ new PepperPrintingHost(host_->GetPpapiHost(), instance, resource, std::move(manager))); } -#if BUILDFLAG(IS_CHROMEOS_ASH) +#if BUILDFLAG(IS_CHROMEOS) case PpapiHostMsg_VpnProvider_Create::ID: { scoped_refptr<PepperVpnProviderMessageFilter> vpn_provider( new PepperVpnProviderMessageFilter(host_, instance));
diff --git a/content/browser/webauth/client_data_json.cc b/content/browser/webauth/client_data_json.cc index e527288a..9c3e329b 100644 --- a/content/browser/webauth/client_data_json.cc +++ b/content/browser/webauth/client_data_json.cc
@@ -37,7 +37,7 @@ while (offset < length) { const int32_t prior_offset = offset; // Input strings must be valid UTF-8. - uint32_t codepoint; + base_icu::UChar32 codepoint; CHECK(base::ReadUnicodeCharacter(in_bytes, length, &offset, &codepoint)); // offset is updated by |ReadUnicodeCharacter| to index the last byte of the // codepoint. Increment it to index the first byte of the next codepoint for
diff --git a/content/browser/xr/service/vr_service_impl.cc b/content/browser/xr/service/vr_service_impl.cc index 530d0b9..e880366 100644 --- a/content/browser/xr/service/vr_service_impl.cc +++ b/content/browser/xr/service/vr_service_impl.cc
@@ -5,6 +5,7 @@ #include "content/browser/xr/service/vr_service_impl.h" #include <utility> +#include <vector> #include "base/bind.h" #include "base/containers/contains.h" @@ -12,6 +13,7 @@ #include "base/dcheck_is_on.h" #include "base/feature_list.h" #include "base/metrics/histogram_macros.h" +#include "base/ranges/algorithm.h" #include "base/stl_util.h" #include "base/trace_event/common/trace_event_common.h" #include "build/build_config.h" @@ -19,6 +21,7 @@ #include "content/browser/web_contents/web_contents_impl.h" #include "content/browser/xr/metrics/session_metrics_helper.h" #include "content/browser/xr/service/browser_xr_runtime_impl.h" +#include "content/browser/xr/service/xr_permission_results.h" #include "content/browser/xr/service/xr_runtime_manager_impl.h" #include "content/public/browser/browser_context.h" #include "content/public/browser/permission_controller.h" @@ -32,7 +35,9 @@ #include "device/vr/buildflags/buildflags.h" #include "device/vr/public/cpp/session_mode.h" #include "device/vr/public/mojom/vr_service.mojom-shared.h" +#include "third_party/abseil-cpp/absl/types/optional.h" #include "third_party/blink/public/common/permissions/permission_utils.h" +#include "third_party/blink/public/mojom/permissions/permission_status.mojom-shared.h" namespace { @@ -44,6 +49,10 @@ return runtime_options; } +// Helper, returns collection of permissions required for XR session creation +// for session with mode set to |mode|, and with enabled features listed in +// |required_features| and |optional_features|. The order in the result matters, +// there will be no duplicates in the result. std::vector<blink::PermissionType> GetRequiredPermissions( device::mojom::XRSessionMode mode, const std::unordered_set<device::mojom::XRSessionFeature>& @@ -52,25 +61,42 @@ optional_features) { std::vector<blink::PermissionType> permissions; - switch (mode) { - case device::mojom::XRSessionMode::kInline: - permissions.push_back(blink::PermissionType::SENSORS); - break; - case device::mojom::XRSessionMode::kImmersiveVr: - permissions.push_back(blink::PermissionType::VR); - break; - case device::mojom::XRSessionMode::kImmersiveAr: - permissions.push_back(blink::PermissionType::AR); - break; + auto mode_permission = content::XrPermissionResults::GetPermissionFor(mode); + if (mode_permission) { + permissions.push_back(*mode_permission); } - if (base::Contains(required_features, - device::mojom::XRSessionFeature::CAMERA_ACCESS) || - base::Contains(optional_features, - device::mojom::XRSessionFeature::CAMERA_ACCESS)) { - permissions.push_back(blink::PermissionType::VIDEO_CAPTURE); + for (const auto& required_feature : required_features) { + auto feature_permission = + content::XrPermissionResults::GetPermissionFor(required_feature); + if (feature_permission && + !base::Contains(permissions, *feature_permission)) { + permissions.push_back(*feature_permission); + } } + for (const auto& optional_feature : optional_features) { + auto feature_permission = + content::XrPermissionResults::GetPermissionFor(optional_feature); + if (feature_permission && + !base::Contains(permissions, *feature_permission)) { + permissions.push_back(*feature_permission); + } + } + +// If both AR and VIDEO_CAPTURE are present, AR must be before VIDEO_CAPTURE. +#if DCHECK_IS_ON() + { + auto ar_it = base::ranges::find(permissions, blink::PermissionType::AR); + auto camera_it = + base::ranges::find(permissions, blink::PermissionType::VIDEO_CAPTURE); + + if (ar_it != permissions.end() && camera_it != permissions.end()) { + DCHECK_GT(std::distance(ar_it, camera_it), 0); + } + } +#endif + return permissions; } @@ -514,22 +540,45 @@ SessionRequestData request, const std::vector<blink::PermissionType>& permissions, const std::vector<blink::mojom::PermissionStatus>& permission_statuses) { - DVLOG(2) << __func__; + DVLOG(2) << __func__ << ": permissions.size()=" << permissions.size(); DCHECK_EQ(permissions.size(), permission_statuses.size()); - bool is_consent_granted = true; - for (size_t i = 0; i < permission_statuses.size(); ++i) { - const blink::mojom::PermissionStatus& permission_status = - permission_statuses[i]; - DVLOG(3) << __func__ << ": index=" << i - << ", permission=" << base::to_underlying(permissions[i]) - << ", status=" << permission_status; - if (permission_status != blink::mojom::PermissionStatus::GRANTED) { - is_consent_granted = false; - break; + const XrPermissionResults permission_results(permissions, + permission_statuses); + + bool is_consent_granted = + permission_results.HasPermissionsFor(request.options->mode); + DVLOG(2) << __func__ << ": is_consent_granted=" << is_consent_granted; + + if (is_consent_granted) { + for (auto& required_feature : request.required_features) { + if (!permission_results.HasPermissionsFor(required_feature)) { + DVLOG(1) << __func__ << ": required_feature=" << required_feature + << " lacks neccessary permissions"; + is_consent_granted = false; + break; + } } } + if (is_consent_granted) { + std::unordered_set<device::mojom::XRSessionFeature> + granted_optional_features; + + for (auto& optional_feature : request.optional_features) { + if (permission_results.HasPermissionsFor(optional_feature)) { + granted_optional_features.insert(optional_feature); + } else { + DVLOG(2) << __func__ << ": optional_feature=" << optional_feature + << " lacks neccessary permissions"; + } + } + + // Replace optional features on the request with the ones that have been + // granted by the user: + std::swap(request.optional_features, granted_optional_features); + } + if (!is_consent_granted) { std::move(request.callback) .Run(device::mojom::RequestSessionResult::NewFailureReason(
diff --git a/content/browser/xr/service/xr_permission_results.cc b/content/browser/xr/service/xr_permission_results.cc new file mode 100644 index 0000000..5e11ea59 --- /dev/null +++ b/content/browser/xr/service/xr_permission_results.cc
@@ -0,0 +1,96 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/browser/xr/service/xr_permission_results.h" + +#include "base/containers/contains.h" +#include "third_party/abseil-cpp/absl/types/optional.h" +#include "third_party/blink/public/common/permissions/permission_utils.h" + +namespace { + +// Helper, creates a map from permission type to its permission status based +// on |permissions| and |permission_statuses|. Those 2 vectors must have equal +// length, and the permission status for permission at `permissions[i]` is +// assumed to be in `permission_statuses[i]`. +base::flat_map<blink::PermissionType, blink::mojom::PermissionStatus> +CreatePermissionTypeToStatusMap( + const std::vector<blink::PermissionType>& permissions, + const std::vector<blink::mojom::PermissionStatus>& permission_statuses) { + DCHECK_EQ(permissions.size(), permission_statuses.size()); + + base::flat_map<blink::PermissionType, blink::mojom::PermissionStatus> result; + for (size_t i = 0; i < permissions.size(); ++i) { + result[permissions[i]] = permission_statuses[i]; + } + return result; +} + +} // namespace + +namespace content { + +XrPermissionResults::XrPermissionResults( + const std::vector<blink::PermissionType>& permission_types, + const std::vector<blink::mojom::PermissionStatus>& permission_statuses) + : permission_type_to_status_( + CreatePermissionTypeToStatusMap(permission_types, + permission_statuses)) {} + +XrPermissionResults::~XrPermissionResults() = default; + +bool XrPermissionResults::HasPermissionsFor( + device::mojom::XRSessionMode mode) const { + auto mode_permission = GetPermissionFor(mode); + if (!mode_permission) { + return true; + } + + return HasPermissionsFor(*mode_permission); +} + +bool XrPermissionResults::HasPermissionsFor( + device::mojom::XRSessionFeature feature) const { + auto feature_permission = GetPermissionFor(feature); + if (!feature_permission) { + return true; + } + + return HasPermissionsFor(*feature_permission); +} + +bool XrPermissionResults::HasPermissionsFor( + blink::PermissionType permission_type) const { + if (!base::Contains(permission_type_to_status_, permission_type)) { + return false; + } + + return permission_type_to_status_.at(permission_type) == + blink::mojom::PermissionStatus::GRANTED; +} + +// static +absl::optional<blink::PermissionType> XrPermissionResults::GetPermissionFor( + device::mojom::XRSessionMode mode) { + switch (mode) { + case device::mojom::XRSessionMode::kInline: + return blink::PermissionType::SENSORS; + case device::mojom::XRSessionMode::kImmersiveVr: + return blink::PermissionType::VR; + case device::mojom::XRSessionMode::kImmersiveAr: + return blink::PermissionType::AR; + } +} + +// static +absl::optional<blink::PermissionType> XrPermissionResults::GetPermissionFor( + device::mojom::XRSessionFeature feature) { + if (feature == device::mojom::XRSessionFeature::CAMERA_ACCESS) { + return blink::PermissionType::VIDEO_CAPTURE; + } + + return absl::nullopt; +} + +} // namespace content
diff --git a/content/browser/xr/service/xr_permission_results.h b/content/browser/xr/service/xr_permission_results.h new file mode 100644 index 0000000..6d00059 --- /dev/null +++ b/content/browser/xr/service/xr_permission_results.h
@@ -0,0 +1,50 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_BROWSER_XR_SERVICE_XR_PERMISSION_RESULTS_H_ +#define CONTENT_BROWSER_XR_SERVICE_XR_PERMISSION_RESULTS_H_ + +#include <vector> + +#include "base/containers/flat_map.h" +#include "device/vr/public/mojom/vr_service.mojom-shared.h" +#include "third_party/abseil-cpp/absl/types/optional.h" +#include "third_party/blink/public/common/permissions/permission_utils.h" + +namespace content { + +// Helper class used to check whether permissions that have been granted satisfy +// requirements for XR session creation given session mode and features that are +// to be enabled on it. +class XrPermissionResults { + public: + XrPermissionResults( + const std::vector<blink::PermissionType>& permission_types, + const std::vector<blink::mojom::PermissionStatus>& permission_statuses); + ~XrPermissionResults(); + + // Checks if |permission_type_to_status| contains permissions necessary to + // create an XR session with |mode|. Returns `true` if so, `false` otherwise. + bool HasPermissionsFor(device::mojom::XRSessionMode mode) const; + + // Checks if the XR session feature has all the required permissions to be + // enabled. Returns `true` if |feature|'s permission requirements are + // satisfied, `false` otherwise. + bool HasPermissionsFor(device::mojom::XRSessionFeature feature) const; + + static absl::optional<blink::PermissionType> GetPermissionFor( + device::mojom::XRSessionMode mode); + static absl::optional<blink::PermissionType> GetPermissionFor( + device::mojom::XRSessionFeature feature); + + private: + const base::flat_map<blink::PermissionType, blink::mojom::PermissionStatus> + permission_type_to_status_; + + bool HasPermissionsFor(blink::PermissionType permission_type) const; +}; + +} // namespace content + +#endif // CONTENT_BROWSER_XR_SERVICE_XR_PERMISSION_RESULTS_H_
diff --git a/content/child/dwrite_font_proxy/font_fallback_win.cc b/content/child/dwrite_font_proxy/font_fallback_win.cc index f8995c91..4435749 100644 --- a/content/child/dwrite_font_proxy/font_fallback_win.cc +++ b/content/child/dwrite_font_proxy/font_fallback_win.cc
@@ -201,7 +201,7 @@ uint32_t length = 0; // How much of the text can actually be mapped. while (static_cast<uint32_t>(character_index) < text.length()) { BOOL exists = false; - uint32_t character = 0; + base_icu::UChar32 character = 0; if (!base::ReadUnicodeCharacter(text.c_str(), text.length(), &character_index, &character)) break;
diff --git a/content/common/content_navigation_policy.cc b/content/common/content_navigation_policy.cc index 9028afdd2d..9b3c64d 100644 --- a/content/common/content_navigation_policy.cc +++ b/content/common/content_navigation_policy.cc
@@ -183,7 +183,8 @@ constexpr base::FeatureParam<RenderDocumentLevel>::Option render_document_levels[] = { {RenderDocumentLevel::kCrashedFrame, "crashed-frame"}, - {RenderDocumentLevel::kSubframe, "subframe"}}; + {RenderDocumentLevel::kSubframe, "subframe"}, + {RenderDocumentLevel::kAllFrames, "all-frames"}}; const base::FeatureParam<RenderDocumentLevel> render_document_level{ &features::kRenderDocument, kRenderDocumentLevelParameterName, RenderDocumentLevel::kCrashedFrame, &render_document_levels}; @@ -202,6 +203,10 @@ return GetRenderDocumentLevel() >= RenderDocumentLevel::kSubframe; } +bool ShouldCreateNewHostForAllFrames() { + return GetRenderDocumentLevel() >= RenderDocumentLevel::kAllFrames; +} + bool ShouldSkipEarlyCommitPendingForCrashedFrame() { static bool skip_early_commit_pending_for_crashed_frame = base::FeatureList::IsEnabled(
diff --git a/content/common/content_navigation_policy.h b/content/common/content_navigation_policy.h index 34be63d..9636b2bb 100644 --- a/content/common/content_navigation_policy.h +++ b/content/common/content_navigation_policy.h
@@ -92,8 +92,11 @@ kCrashedFrame = 1, // Also do not reuse RenderFrameHosts when navigating subframes. kSubframe = 2, + // Do not reuse RenderFrameHosts when navigating any frame. + kAllFrames = 3, }; CONTENT_EXPORT bool ShouldCreateNewHostForSameSiteSubframe(); +CONTENT_EXPORT bool ShouldCreateNewHostForAllFrames(); CONTENT_EXPORT RenderDocumentLevel GetRenderDocumentLevel(); CONTENT_EXPORT std::string GetRenderDocumentLevelName( RenderDocumentLevel level);
diff --git a/content/common/partition_alloc_support.cc b/content/common/partition_alloc_support.cc index d0c769ea..f671199 100644 --- a/content/common/partition_alloc_support.cc +++ b/content/common/partition_alloc_support.cc
@@ -21,6 +21,7 @@ #include "base/feature_list.h" #include "base/location.h" #include "base/memory/nonscannable_memory.h" +#include "base/memory/raw_ptr_asan_service.h" #include "base/no_destructor.h" #include "base/time/time.h" #include "build/build_config.h" @@ -218,9 +219,11 @@ bool enable_brp = false; [[maybe_unused]] bool split_main_partition = false; [[maybe_unused]] bool use_dedicated_aligned_partition = false; -#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) -#if BUILDFLAG(USE_BACKUP_REF_PTR) - bool process_affected_by_brp_flag = false; + [[maybe_unused]] bool process_affected_by_brp_flag = false; + +#if (BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && \ + BUILDFLAG(USE_BACKUP_REF_PTR)) || \ + BUILDFLAG(USE_ASAN_BACKUP_REF_PTR) if (base::FeatureList::IsEnabled( base::features::kPartitionAllocBackupRefPtr)) { // No specified process type means this is the Browser process. @@ -242,7 +245,26 @@ break; } } +#endif +#if BUILDFLAG(USE_ASAN_BACKUP_REF_PTR) + if (process_affected_by_brp_flag) { + base::RawPtrAsanService::GetInstance().Configure( + base::EnableDereferenceCheck( + base::features::kBackupRefPtrAsanEnableDereferenceCheckParam.Get()), + base::EnableExtractionCheck( + base::features::kBackupRefPtrAsanEnableExtractionCheckParam.Get()), + base::EnableInstantiationCheck( + base::features::kBackupRefPtrAsanEnableInstantiationCheckParam + .Get())); + } else { + base::RawPtrAsanService::GetInstance().Configure( + base::EnableDereferenceCheck(false), base::EnableExtractionCheck(false), + base::EnableInstantiationCheck(false)); + } +#endif // BUILDFLAG(USE_ASAN_BACKUP_REF_PTR) + +#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && BUILDFLAG(USE_BACKUP_REF_PTR) if (process_affected_by_brp_flag) { switch (base::features::kBackupRefPtrModeParam.Get()) { case base::features::BackupRefPtrMode::kDisabled: @@ -275,8 +297,8 @@ break; } } -#endif // BUILDFLAG(USE_BACKUP_REF_PTR) -#endif // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) +#endif // BUILDFLAG(USE_BACKUP_REF_PTR) && + // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) #if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) base::allocator::ConfigurePartitions(
diff --git a/content/public/browser/browser_main_parts.h b/content/public/browser/browser_main_parts.h index da32e765..b236b8ad 100644 --- a/content/public/browser/browser_main_parts.h +++ b/content/public/browser/browser_main_parts.h
@@ -17,30 +17,33 @@ namespace content { -// This class contains different "stages" to be executed by |BrowserMain()|, -// Each stage is represented by a single BrowserMainParts method, called from -// the corresponding method in |BrowserMainLoop| (e.g., EarlyInitialization()) -// which does the following: -// - calls a method (e.g., "PreEarlyInitialization()") which implements -// platform / tookit specific code for that stage. -// - calls various methods for things common to all platforms (for that stage). -// - calls a method (e.g., "PostEarlyInitialization()") for platform-specific -// code to be called after the common code. +// This class contains different "stages" to be executed by |BrowserMain()|. // // Stages: // -// - EarlyInitialization: things which should be done as soon as possible on -// program start (such as setting up signal handlers) +// ** Cross-platform startup stages. +// ** Invoked during BrowserMainRunnerImpl::Initialize(), after +// ContentMainRunner's full initialization. // -// - ToolkitInitialized: similar to EarlyInitialization but for the UI toolkit. -// Allows an embedder to do any extra toolkit initialization. +// - PreEarlyInitialization: things to be be done as soon as possible on +// program start (such as setting up signal handlers; checking auto-restarts +// on update; etc.). Core APIs like base::FeatureList, +// base::ThreadTaskRunnerHandle, and base::ThreadPool are already functional +// at this point (ThreadPool will accept but not run tasks until +// PostCreateThreads). +// +// - PostEarlyInitialization: things to be be as soon as possible but can/must +// wait after the few things in BrowserMainLoop's own EarlyInitialization. +// +// - ToolkitInitialized: similar to PostEarlyInitialization but for the UI +// toolkit. Allows an embedder to do any extra toolkit initialization. // // - PreCreateMainMessageLoop: things to be done at some generic time before // the creation of the main message loop. // -// - PostCreateMainMessageLoop: things that should be done as early as possible -// but need the main message loop to be around (i.e. APIs like -// ThreadTaskRunnerHandle, BrowserThread::UI are up). +// - PostCreateMainMessageLoop: things to be done as early as possible but +// which need the main message loop to be around (i.e. BrowserThread::UI is +// up). // // - PreCreateThreads: things that don't need to happen super early but still // need to happen during single-threaded initialization (e.g. immutable @@ -53,11 +56,10 @@ // https://chromium.googlesource.com/chromium/src/+/main/docs/threading_and_tasks.md#threading-lexicon // // - PostCreateThreads: things that should be done as early as possible but -// need browser process threads to be alive (i.e. APIs like base::ThreadPool -// and BrowserThread::IO are up). In other words, core things that must be -// initialized before PreMainMessageLoopRun. +// need browser process threads to be alive (i.e. BrowserThread::IO is up and +// base::ThreadPool is running tasks). // -// - PreMainMessageLoopRun: in doubt, put things here. At this stage all core +// - PreMainMessageLoopRun: IN DOUBT, PUT THINGS HERE. At this stage all core // APIs have been initialized. Services that must be initialized before the // browser is considered functional can be initialized from here. Ideally // only the frontend is initialized here while the backend takes advantage of @@ -65,10 +67,10 @@ // happen on the main thread eventually but don't need to block startup // should post a BEST_EFFORT task from this stage. // -// ** End of cross-platform startup stages ** -// Stages above are run as part of startup stages in -// BrowserMainLoop::CreateStartupTasks() and can even be run eagerly (e.g. -// Android app warmup attempts to run these async. +// ** End of cross-platform startup stages. +// ** Stages above are run as part of startup stages in +// ** BrowserMainLoop::CreateStartupTasks() and can even be run eagerly (e.g. +// ** Android app warmup attempts to run these async. // // - WillRunMainMessageLoop: The main thread's RunLoop will be run // *immediately* upon returning from this method. While PreMainMessageLoopRun @@ -146,8 +148,8 @@ // This gives BrowserMainParts one last opportunity to tweak the upcoming main // message loop run. The embedder may replace |run_loop| to alter the default // RunLoop about to be run (must not be nullified, override - // CanRunMainMessageLoop to cancel the run). Note: This point is never - // reached on Android as it never invokes MainMessageLoopRun(), + // ShouldInterceptMainMessageLoopRun to cancel the run). Note: This point is + // never reached on Android as it never invokes MainMessageLoopRun(), // InterceptMainMessageLoopRun() is Android's last chance at altering the // default native loop run. virtual void WillRunMainMessageLoop(
diff --git a/content/test/content_test_suite.cc b/content/test/content_test_suite.cc index 528fecf..e247d25 100644 --- a/content/test/content_test_suite.cc +++ b/content/test/content_test_suite.cc
@@ -63,11 +63,12 @@ base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); bool is_child_process = command_line->HasSwitch(switches::kTestChildProcess); if (!is_child_process) { - gl::GLSurfaceTestSupport::InitializeNoExtensionsOneOff(); + gl::GLDisplay* display = + gl::GLSurfaceTestSupport::InitializeNoExtensionsOneOff(); auto* gpu_feature_info = gpu::GetTestGpuThreadHolder()->GetGpuFeatureInfo(); gl::init::SetDisabledExtensionsPlatform( gpu_feature_info->disabled_extensions); - gl::init::InitializeExtensionSettingsOneOffPlatform(); + gl::init::InitializeExtensionSettingsOneOffPlatform(display); } RegisterContentWebUIConfigs();
diff --git a/content/test/data/attribution_reporting/aggregatable_report_goldens/latest/report_1.json b/content/test/data/attribution_reporting/aggregatable_report_goldens/latest/report_1.json index e63e9fe..f9524a9f 100644 --- a/content/test/data/attribution_reporting/aggregatable_report_goldens/latest/report_1.json +++ b/content/test/data/attribution_reporting/aggregatable_report_goldens/latest/report_1.json
@@ -2,12 +2,12 @@ "aggregation_service_payloads": [ { "debug_cleartext_payload": "omRkYXRhgaJldmFsdWVEAAAAAmZidWNrZXRQAAAAAAAAAAAAAAAAAAAAAWlvcGVyYXRpb25paGlzdG9ncmFt", "key_id": "example_id", - "payload": "cVrsURY6QvrT1fsdPxWOXb3bjlzB6ih3APjLpydjyRoAJ6FTyZf9AUj1l5VlDweb3fssxCaMkz5jr5oHPlehnBVjrKLiSvtX3mTJ3CJD9DEp3uiiKV5Fsjv4G4RJcdwZERnJN/8s1OWJ6syEqKnJ" + "payload": "P6qDfnLgaTL2jLv6tRHXauNGi1DpBn7KFxX2/HyEzVCP+LwzwsRntTyrnalnZuVTux8bdILblwBa6ehWDfyf1tZ/jLr0uFr+DC6oisTb6sDkFpkCELK/62IoDqsNYzl/DyAmFd9NKaepMoS6+GYH" } ], "attribution_destination": "https://conversion.test", - "shared_info": "{\"debug_mode\":\"enabled\",\"privacy_budget_key\":\"aOEbtVxG8dYzAR2K/xuW/OppNaQikp5RdjAXshOQ9w8=\",\"report_id\":\"21abd97f-73e8-4b88-9389-a9fee6abda5e\",\"reporting_origin\":\"https://report.test\",\"scheduled_report_time\":\"1234486400\",\"version\":\"\"}", + "shared_info": "{\"debug_mode\":\"enabled\",\"privacy_budget_key\":\"hTxwDPlUIel3dB39m09GOkeAkMLcYVdE6H0eK2t1RYc=\",\"report_id\":\"21abd97f-73e8-4b88-9389-a9fee6abda5e\",\"reporting_origin\":\"https://report.test\",\"scheduled_report_time\":\"1234486400\",\"version\":\"\"}", "source_debug_key": "123", "source_registration_time": "1234483200", "source_site": "https://impression.test", "trigger_debug_key": "456" -} \ No newline at end of file +}
diff --git a/content/test/fuzzer/attribution_simulator_fuzzer.cc b/content/test/fuzzer/attribution_simulator_fuzzer.cc index 30f2c89..b671626 100644 --- a/content/test/fuzzer/attribution_simulator_fuzzer.cc +++ b/content/test/fuzzer/attribution_simulator_fuzzer.cc
@@ -9,10 +9,9 @@ #include <utility> #include "base/json/json_reader.h" +#include "base/time/time.h" #include "base/values.h" -#include "content/public/browser/attribution_reporting.h" -#include "content/public/test/attribution_simulator.h" -#include "content/public/test/attribution_simulator_environment.h" +#include "content/test/attribution_simulator_input_parser.h" #include "testing/libfuzzer/proto/json.pb.h" #include "testing/libfuzzer/proto/json_proto_converter.h" #include "testing/libfuzzer/proto/lpm_interface.h" @@ -21,8 +20,6 @@ namespace content { DEFINE_PROTO_FUZZER(const json_proto::JsonValue& json_value) { - static AttributionSimulatorEnvironment env(/*argc=*/0, /*argv=*/nullptr); - json_proto::JsonProtoConverter converter; std::string native_input = converter.Convert(json_value); @@ -34,15 +31,9 @@ if (!input) return; - // TODO(apaseltiner): Fuzz options as well. - const AttributionSimulationOptions options{ - // Disable noise to make it more likely for fuzz failures to be - // reproducible. - .noise_mode = AttributionNoiseMode::kNone, - }; - - std::stringstream error_stream; - RunAttributionSimulation(std::move(*input), options, error_stream); + std::ostringstream error_stream; + ParseAttributionSimulationInput(std::move(*input), + /*offset_time=*/base::Time(), error_stream); } } // namespace content
diff --git a/extensions/browser/api/declarative/declarative_rule.h b/extensions/browser/api/declarative/declarative_rule.h index 01af605..2a899c0 100644 --- a/extensions/browser/api/declarative/declarative_rule.h +++ b/extensions/browser/api/declarative/declarative_rule.h
@@ -87,7 +87,7 @@ // IsFulfilled(|match_data|). If there is no such condition, then false is // returned. If |url_match_trigger| is -1, this function returns whether any // of the conditions without URL attributes is satisfied. - bool IsFulfilled(url_matcher::URLMatcherConditionSet::ID url_match_trigger, + bool IsFulfilled(base::MatcherStringPattern::ID url_match_trigger, const typename ConditionT::MatchData& match_data) const; // Appends the URLMatcherConditionSet from all conditions to |condition_sets|. @@ -101,7 +101,7 @@ private: using URLMatcherIdToCondition = - std::map<url_matcher::URLMatcherConditionSet::ID, const ConditionT*>; + std::map<base::MatcherStringPattern::ID, const ConditionT*>; DeclarativeConditionSet( Conditions conditions, @@ -274,11 +274,11 @@ // DeclarativeConditionSet // -template<typename ConditionT> +template <typename ConditionT> bool DeclarativeConditionSet<ConditionT>::IsFulfilled( - url_matcher::URLMatcherConditionSet::ID url_match_trigger, + base::MatcherStringPattern::ID url_match_trigger, const typename ConditionT::MatchData& match_data) const { - if (url_match_trigger == -1) { + if (url_match_trigger == base::MatcherStringPattern::kInvalidId) { // Invalid trigger -- indication that we should only check conditions // without URL attributes. for (const ConditionT* condition : conditions_without_urls_) {
diff --git a/extensions/browser/api/declarative/declarative_rule_unittest.cc b/extensions/browser/api/declarative/declarative_rule_unittest.cc index 9aa09d01..dc7adb0 100644 --- a/extensions/browser/api/declarative/declarative_rule_unittest.cc +++ b/extensions/browser/api/declarative/declarative_rule_unittest.cc
@@ -99,14 +99,14 @@ struct FulfillableCondition { struct MatchData { int value; - const std::set<URLMatcherConditionSet::ID>& url_matches; + const std::set<base::MatcherStringPattern::ID>& url_matches; }; scoped_refptr<URLMatcherConditionSet> condition_set; - int condition_set_id; + base::MatcherStringPattern::ID condition_set_id; int max_value; - URLMatcherConditionSet::ID url_matcher_condition_set_id() const { + base::MatcherStringPattern::ID url_matcher_condition_set_id() const { return condition_set_id; } @@ -121,7 +121,7 @@ } bool IsFulfilled(const MatchData& match_data) const { - if (condition_set_id != -1 && + if (condition_set_id != base::MatcherStringPattern::kInvalidId && !base::Contains(match_data.url_matches, condition_set_id)) return false; return match_data.value <= max_value; @@ -138,13 +138,16 @@ *error = "Expected dict"; return result; } - result->condition_set_id = dict->FindIntKey("url_id").value_or(-1); + const auto id = dict->FindIntKey("url_id"); + result->condition_set_id = + id.has_value() ? static_cast<base::MatcherStringPattern::ID>(id.value()) + : base::MatcherStringPattern::kInvalidId; if (absl::optional<int> max_value_int = dict->FindIntKey("max")) { result->max_value = *max_value_int; } else { *error = "Expected integer at ['max']"; } - if (result->condition_set_id != -1) { + if (result->condition_set_id != base::MatcherStringPattern::kInvalidId) { result->condition_set = new URLMatcherConditionSet( result->condition_set_id, URLMatcherConditionSet::Conditions()); @@ -169,7 +172,7 @@ ASSERT_TRUE(result); EXPECT_EQ(4u, result->conditions().size()); - std::set<URLMatcherConditionSet::ID> url_matches; + std::set<base::MatcherStringPattern::ID> url_matches; FulfillableCondition::MatchData match_data = { 0, url_matches }; EXPECT_FALSE(result->IsFulfilled(1, match_data)) << "Testing an ID that's not in url_matches forwards to the Condition, " @@ -198,9 +201,9 @@ URLMatcherConditionSet::Vector condition_sets; result->GetURLMatcherConditionSets(&condition_sets); ASSERT_EQ(3U, condition_sets.size()); - EXPECT_EQ(1, condition_sets[0]->id()); - EXPECT_EQ(2, condition_sets[1]->id()); - EXPECT_EQ(3, condition_sets[2]->id()); + EXPECT_EQ(1U, condition_sets[0]->id()); + EXPECT_EQ(2U, condition_sets[1]->id()); + EXPECT_EQ(3U, condition_sets[2]->id()); } // DeclarativeAction
diff --git a/extensions/browser/api/declarative_net_request/regex_rules_matcher.cc b/extensions/browser/api/declarative_net_request/regex_rules_matcher.cc index 1f32ffe..9284801 100644 --- a/extensions/browser/api/declarative_net_request/regex_rules_matcher.cc +++ b/extensions/browser/api/declarative_net_request/regex_rules_matcher.cc
@@ -272,7 +272,7 @@ // To pre-filter the set of regexes to match against |params|, we first need // to compute the set of candidate strings tracked by |substring_matcher_| // within |params.lower_cased_url_spec|. - std::set<int> candidate_ids_set; + std::set<base::MatcherStringPattern::ID> candidate_ids_set; DCHECK(substring_matcher_); substring_matcher_->Match(*params.lower_cased_url_spec, &candidate_ids_set); std::vector<int> candidate_ids_list(candidate_ids_set.begin(),
diff --git a/extensions/browser/api/declarative_webrequest/webrequest_condition.cc b/extensions/browser/api/declarative_webrequest/webrequest_condition.cc index 237396c..445e1ba5 100644 --- a/extensions/browser/api/declarative_webrequest/webrequest_condition.cc +++ b/extensions/browser/api/declarative_webrequest/webrequest_condition.cc
@@ -21,7 +21,7 @@ namespace keys = extensions::declarative_webrequest_constants; namespace { -static URLMatcherConditionSet::ID g_next_id = 0; +static base::MatcherStringPattern::ID g_next_id = 0; // TODO(battre): improve error messaging to give more meaningful messages // to the extension developer.
diff --git a/extensions/browser/api/declarative_webrequest/webrequest_condition.h b/extensions/browser/api/declarative_webrequest/webrequest_condition.h index 1a9f75d..94cfc8b 100644 --- a/extensions/browser/api/declarative_webrequest/webrequest_condition.h +++ b/extensions/browser/api/declarative_webrequest/webrequest_condition.h
@@ -41,7 +41,7 @@ ~WebRequestDataWithMatchIds(); raw_ptr<const WebRequestData> data; - std::set<url_matcher::URLMatcherConditionSet::ID> url_match_ids; + std::set<base::MatcherStringPattern::ID> url_match_ids; }; // Representation of a condition in the Declarative WebRequest API. A condition
diff --git a/extensions/browser/api/declarative_webrequest/webrequest_condition_unittest.cc b/extensions/browser/api/declarative_webrequest/webrequest_condition_unittest.cc index 325d8f3..c315f53 100644 --- a/extensions/browser/api/declarative_webrequest/webrequest_condition_unittest.cc +++ b/extensions/browser/api/declarative_webrequest/webrequest_condition_unittest.cc
@@ -309,7 +309,7 @@ result->GetURLMatcherConditionSets(&url_matcher_condition_set); matcher.AddConditionSets(url_matcher_condition_set); - std::set<URLMatcherConditionSet::ID> url_match_ids; + std::set<base::MatcherStringPattern::ID> url_match_ids; // Test various URLs. GURL http_url("http://www.example.com");
diff --git a/extensions/browser/api/declarative_webrequest/webrequest_rules_registry.cc b/extensions/browser/api/declarative_webrequest/webrequest_rules_registry.cc index 8f5cbc8..3a792c8 100644 --- a/extensions/browser/api/declarative_webrequest/webrequest_rules_registry.cc +++ b/extensions/browser/api/declarative_webrequest/webrequest_rules_registry.cc
@@ -62,7 +62,8 @@ // 1st phase -- add all rules with some conditions without UrlFilter // attributes. for (const auto* rule : rules_with_untriggered_conditions_) { - if (rule->conditions().IsFulfilled(-1, request_data)) + if (rule->conditions().IsFulfilled(base::MatcherStringPattern::kInvalidId, + request_data)) result.insert(rule); } @@ -215,7 +216,7 @@ const std::string& extension_id, const std::vector<std::string>& rule_identifiers) { // URLMatcherConditionSet IDs that can be removed from URLMatcher. - std::vector<URLMatcherConditionSet::ID> remove_from_url_matcher; + std::vector<base::MatcherStringPattern::ID> remove_from_url_matcher; RulesMap& registered_rules = webrequest_rules_[extension_id]; for (const std::string& identifier : rule_identifiers) { @@ -246,7 +247,7 @@ const std::string& extension_id) { // First we get out all URLMatcherConditionSets and remove the rule references // from |rules_with_untriggered_conditions_|. - std::vector<URLMatcherConditionSet::ID> remove_from_url_matcher; + std::vector<base::MatcherStringPattern::ID> remove_from_url_matcher; for (const auto& rule_id_rule_pair : webrequest_rules_[extension_id]) CleanUpAfterRule(rule_id_rule_pair.second.get(), &remove_from_url_matcher); url_matcher_.RemoveConditionSets(remove_from_url_matcher); @@ -258,7 +259,7 @@ void WebRequestRulesRegistry::CleanUpAfterRule( const WebRequestRule* rule, - std::vector<URLMatcherConditionSet::ID>* remove_from_url_matcher) { + std::vector<base::MatcherStringPattern::ID>* remove_from_url_matcher) { URLMatcherConditionSet::Vector condition_sets; rule->conditions().GetURLMatcherConditionSets(&condition_sets); for (const scoped_refptr<URLMatcherConditionSet>& condition_set :
diff --git a/extensions/browser/api/declarative_webrequest/webrequest_rules_registry.h b/extensions/browser/api/declarative_webrequest/webrequest_rules_registry.h index 207eb27..14f7d0c 100644 --- a/extensions/browser/api/declarative_webrequest/webrequest_rules_registry.h +++ b/extensions/browser/api/declarative_webrequest/webrequest_rules_registry.h
@@ -62,7 +62,7 @@ // represents the {'host_suffix': 'example.com', 'path_prefix': '/query'} part. // We will then ask the URLMatcher, whether a given URL // "http://www.example.com/query/" has any matches, and the URLMatcher -// will respond with the URLMatcherConditionSet::ID. We can map this +// will respond with the base::MatcherStringPattern::ID. We can map this // to the WebRequestRule and check whether also the other conditions (in this // example 'scheme': 'http') are fulfilled. class WebRequestRulesRegistry : public RulesRegistry { @@ -119,10 +119,10 @@ HostPermissionsChecker); using RuleTriggers = - std::map<url_matcher::URLMatcherConditionSet::ID, const WebRequestRule*>; + std::map<base::MatcherStringPattern::ID, const WebRequestRule*>; using RulesMap = std::map<WebRequestRule::RuleId, std::unique_ptr<const WebRequestRule>>; - using URLMatches = std::set<url_matcher::URLMatcherConditionSet::ID>; + using URLMatches = std::set<base::MatcherStringPattern::ID>; using RuleSet = std::set<const WebRequestRule*>; // This bundles all consistency checkers. Returns true in case of consistency @@ -150,9 +150,9 @@ // and add every of the rule's URLMatcherConditionSet to // |remove_from_url_matcher|, so that the caller can remove them from the // matcher later. - void CleanUpAfterRule(const WebRequestRule* rule, - std::vector<url_matcher::URLMatcherConditionSet::ID>* - remove_from_url_matcher); + void CleanUpAfterRule( + const WebRequestRule* rule, + std::vector<base::MatcherStringPattern::ID>* remove_from_url_matcher); // This is a helper function to GetMatches. Rules triggered by |url_matches| // get added to |result| if one of their conditions is fulfilled. @@ -162,7 +162,7 @@ RuleSet* result) const; // Map that tells us which WebRequestRule may match under the condition that - // the URLMatcherConditionSet::ID was returned by the |url_matcher_|. + // the base::MatcherStringPattern::ID was returned by the |url_matcher_|. RuleTriggers rule_triggers_; // These rules contain condition sets with conditions without URL attributes.
diff --git a/extensions/common/event_filter.cc b/extensions/common/event_filter.cc index 74eb0da7..9f610382 100644 --- a/extensions/common/event_filter.cc +++ b/extensions/common/event_filter.cc
@@ -109,7 +109,7 @@ const base::Value::Dict& url_filter, URLMatcherConditionSet::Vector* condition_sets) { std::string error; - URLMatcherConditionSet::ID condition_set_id = next_condition_set_id_++; + base::MatcherStringPattern::ID condition_set_id = next_condition_set_id_++; condition_sets->push_back(URLMatcherFactory::CreateFromURLFilterDictionary( url_matcher_.condition_factory(), url_filter, @@ -147,7 +147,7 @@ const EventMatcherMap& matcher_map = it->second; const GURL& url_to_match_against = event_info.url ? *event_info.url : GURL::EmptyGURL(); - std::set<URLMatcherConditionSet::ID> matching_condition_set_ids = + std::set<base::MatcherStringPattern::ID> matching_condition_set_ids = url_matcher_.MatchURL(url_to_match_against); for (const auto& id_key : matching_condition_set_ids) { auto matcher_id = condition_set_id_to_event_matcher_id_.find(id_key);
diff --git a/extensions/common/event_filter.h b/extensions/common/event_filter.h index cf0ec5c..fe1db81 100644 --- a/extensions/common/event_filter.h +++ b/extensions/common/event_filter.h
@@ -89,7 +89,7 @@ private: std::unique_ptr<EventMatcher> event_matcher_; // The id sets in |url_matcher_| that this EventMatcher owns. - std::vector<url_matcher::URLMatcherConditionSet::ID> condition_set_ids_; + std::vector<base::MatcherStringPattern::ID> condition_set_ids_; raw_ptr<url_matcher::URLMatcher> url_matcher_; }; @@ -116,12 +116,12 @@ MatcherID next_id_; // The next id to assign to a condition set passed to URLMatcher. - url_matcher::URLMatcherConditionSet::ID next_condition_set_id_; + base::MatcherStringPattern::ID next_condition_set_id_; // Maps condition set ids, which URLMatcher operates in, to event matcher // ids, which the interface to this class operates in. As each EventFilter // can specify many condition sets this is a many to one relationship. - std::map<url_matcher::URLMatcherConditionSet::ID, MatcherID> + std::map<base::MatcherStringPattern::ID, MatcherID> condition_set_id_to_event_matcher_id_; // Maps from event matcher ids to the name of the event they match on.
diff --git a/extensions/docs/extension_tests.md b/extensions/docs/extension_tests.md index ba1f009..95c223fd 100644 --- a/extensions/docs/extension_tests.md +++ b/extensions/docs/extension_tests.md
@@ -215,6 +215,83 @@ chrome.action.onClicked.addListener(() => { chrome.test.notifyPass(); }); ``` +#### ScriptResultQueue +A `ScriptResultQueue` is queue of results passed from +`chrome.test.sendScriptResult()`. Conceptually, it's somewhat similar to an +`ExtensionTestMessageListener` (they each allow getting data from running +JS), with a few key differences: +- It's one-directional (it does not allow communicating back with the JS), +- It takes any serializable argument, so can be used to pass objects, ints, + etc, and +- It queues up multiple results. + +**Example Usage** + +```c++ +// test.cc +IN_PROC_BROWSER_TEST_F(...) { + LoadExtension(...); + ScriptResultQueue result_queue; + ClickAction(); + base::Value first_result = result_queue.GetNextResult(); + ClickAction(); + base::Value second_result = result_queue.GetNextResult(); +} +``` + +```js +// extension_script.js +chrome.action.onClicked.addListener((tab) => { + chrome.test.sendScriptResult(tab); +}); +``` + +#### BackgroundScriptExecutor +A `BackgroundScriptExecutor` is used to execute Javascript in the context of a +test extension's background context. This class works with both service +worker-based and background page-based extensions. It can internally leverage +`ScriptResultQueue` to capture asynchronous results. + +**Example Usage** + +```c++ +// test.cc +IN_PROC_BROWSER_TEST_F(...) { + static constexpr char kManifest[] = + R"({ + "name": "test ext", + "manifest_version": 2, + "version": "0.1", + "background": {"service_worker": "background.js"}, + "permissions": ["scripting"], + "host_permissions": ["<all_urls>"] + })"; + TestExtensionDir test_dir; + test_dir.WriteManifest(kManifest); + test_dir.WriteFile(FILE_PATH_LITERAL("background.js"), "// Empty")); + const Extension* extension = LoadExtension(test_dir.UnpackedPath()); + SetUpTestPage(); + static constexpr char kScript[] = + R"((async () => { + let injectionResults = + await chrome.scripting.executeScript( + { + target: {tabId: %d}, + func: () => { return document.title; } + }); + let title = injectionResults[0].result; + chrome.test.sendScriptResult(title); + })())"; + base::Value script_result = + BackgroundScriptExecutor::ExecuteScript( + extension->id(), + base::StringPrintf(kScript, GetActiveTabId()), + BackgroundScriptExecutor::ResultCapture::kSendScriptResult); + EXPECT_THAT(script_result, + base::test::IsJson(R"("My Page Title")")); +} +``` + ### Test Suites #### ExtensionServiceTestBase A somewhat poorly-named common unit test suite class that sets up an extension
diff --git a/extensions/renderer/api/automation/automation_ax_tree_wrapper.cc b/extensions/renderer/api/automation/automation_ax_tree_wrapper.cc index a2a0dff80..07bbfa1 100644 --- a/extensions/renderer/api/automation/automation_ax_tree_wrapper.cc +++ b/extensions/renderer/api/automation/automation_ax_tree_wrapper.cc
@@ -586,7 +586,9 @@ ui::AXNode* AutomationAXTreeWrapper::GetParentNodeFromParentTreeAsAXNode() const { AutomationAXTreeWrapper* wrapper = const_cast<AutomationAXTreeWrapper*>(this); - return owner_->GetParent(tree_.root(), &wrapper); + return owner_->GetParent(tree_.root(), &wrapper, + /* should_use_app_id = */ true, + /* requires_unignored = */ false); } } // namespace extensions
diff --git a/extensions/renderer/api/automation/automation_internal_custom_bindings.cc b/extensions/renderer/api/automation/automation_internal_custom_bindings.cc index a08b21e..45b49674 100644 --- a/extensions/renderer/api/automation/automation_internal_custom_bindings.cc +++ b/extensions/renderer/api/automation/automation_internal_custom_bindings.cc
@@ -2166,7 +2166,8 @@ ui::AXNode* AutomationInternalCustomBindings::GetParent( ui::AXNode* node, AutomationAXTreeWrapper** in_out_tree_wrapper, - bool should_use_app_id) const { + bool should_use_app_id, + bool requires_unignored) const { if (should_use_app_id && node->HasStringAttribute(ax::mojom::StringAttribute::kAppId)) { ui::AXNode* parent_app_node = @@ -2179,7 +2180,15 @@ } } - ui::AXNode* parent = node->GetUnignoredParent(); + ui::AXNode* parent = nullptr; + if (!requires_unignored) { + parent = node->GetParent(); + if (parent) + return parent; + return GetHostInParentTree(in_out_tree_wrapper); + } + + parent = node->GetUnignoredParent(); if (!parent) { // Search up ancestor trees until we find one with a host that is unignored. while ((parent = GetHostInParentTree(in_out_tree_wrapper))) {
diff --git a/extensions/renderer/api/automation/automation_internal_custom_bindings.h b/extensions/renderer/api/automation/automation_internal_custom_bindings.h index e1db568..826dbd9 100644 --- a/extensions/renderer/api/automation/automation_internal_custom_bindings.h +++ b/extensions/renderer/api/automation/automation_internal_custom_bindings.h
@@ -64,9 +64,15 @@ // If |node| is the root of its tree, the return value will be the host // node of the parent tree and |in_out_tree_wrapper| will be updated to // point to that parent tree. + // + // Optionally, |should_use_app_id|, if true, considers + // ax::mojom::IntAttribute::kChildTreeNodeAppId when moving to ancestors. + // |requires_unignored|, if true, keeps moving to ancestors until an unignored + // ancestor parent is found. ui::AXNode* GetParent(ui::AXNode* node, AutomationAXTreeWrapper** in_out_tree_wrapper, - bool should_use_app_id = true) const; + bool should_use_app_id = true, + bool requires_unignored = true) const; // Gets the hosting node in a parent tree. ui::AXNode* GetHostInParentTree(
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc index 1ab9e6d..76308e5d 100644 --- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc +++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc
@@ -196,10 +196,10 @@ DCHECK(normalized_init.extensions.empty() || *normalized_init.extensions.rbegin() == ' '); gl::SetGLGetProcAddressProc(gl::MockGLInterface::GetGLProcAddress); - gl::GLSurfaceTestSupport::InitializeOneOffWithMockBindings(); + display_ = gl::GLSurfaceTestSupport::InitializeOneOffWithMockBindings(); gl_ = std::make_unique<StrictMock<MockGLInterface>>(); - ::gl::MockGLInterface::SetGLInterface(gl_.get()); + gl::MockGLInterface::SetGLInterface(gl_.get()); SetupMockGLBehaviors(); @@ -620,9 +620,9 @@ decoder_.reset(); group_->Destroy(mock_decoder_.get(), false); command_buffer_service_.reset(); - ::gl::MockGLInterface::SetGLInterface(nullptr); + gl::MockGLInterface::SetGLInterface(nullptr); gl_.reset(); - gl::init::ShutdownGL(false); + gl::GLSurfaceTestSupport::ShutdownGL(display_); } void GLES2DecoderTestBase::TearDown() { @@ -2431,7 +2431,7 @@ gl::init::InitializeStaticGLBindingsImplementation( gl::GLImplementationParts(gl::kGLImplementationEGLANGLE), false); - gl::init::InitializeGLOneOffPlatformImplementation( + display_ = gl::init::InitializeGLOneOffPlatformImplementation( /*fallback_to_software_gl=*/false, /*disable_gl_drawing=*/false, /*init_extensions=*/true, @@ -2493,7 +2493,7 @@ decoder_.reset(); group_ = nullptr; command_buffer_service_.reset(); - gl::init::ShutdownGL(false); + gl::init::ShutdownGL(display_, false); } void GLES2DecoderPassthroughTestBase::SetBucketData(uint32_t bucket_id,
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.h b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.h index db9c633..ed64675 100644 --- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.h +++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.h
@@ -42,6 +42,7 @@ #include "gpu/config/gpu_driver_bug_workarounds.h" #include "gpu/config/gpu_preferences.h" #include "testing/gtest/include/gtest/gtest.h" +#include "ui/gl/gl_display.h" #include "ui/gl/gl_mock.h" #include "ui/gl/gl_surface_stub.h" #include "ui/gl/gl_version_info.h" @@ -702,6 +703,7 @@ std::unique_ptr<MockGLES2Decoder> mock_decoder_; std::unique_ptr<GLES2Decoder> decoder_; std::unique_ptr<MemoryTracker> memory_tracker_; + gl::GLDisplay* display_ = nullptr; bool surface_supports_draw_rectangle_ = false; @@ -1035,6 +1037,7 @@ TraceOutputter outputter_; std::unique_ptr<GLES2DecoderPassthroughImpl> decoder_; scoped_refptr<ContextGroup> group_; + gl::GLDisplay* display_ = nullptr; }; } // namespace gles2
diff --git a/gpu/command_buffer/service/gpu_fence_manager_unittest.cc b/gpu/command_buffer/service/gpu_fence_manager_unittest.cc index 1a77b602..3ccc31f3 100644 --- a/gpu/command_buffer/service/gpu_fence_manager_unittest.cc +++ b/gpu/command_buffer/service/gpu_fence_manager_unittest.cc
@@ -61,7 +61,7 @@ void SetupMockEGL(const char* extensions) { gl::SetGLGetProcAddressProc(gl::MockEGLInterface::GetGLProcAddress); egl_ = std::make_unique<::testing::NiceMock<::gl::MockEGLInterface>>(); - ::gl::MockEGLInterface::SetEGLInterface(egl_.get()); + gl::MockEGLInterface::SetEGLInterface(egl_.get()); const EGLDisplay kDummyDisplay = reinterpret_cast<EGLDisplay>(0x1001); ON_CALL(*egl_, QueryString(_, EGL_EXTENSIONS)) @@ -73,10 +73,13 @@ gl::ClearBindingsEGL(); gl::InitializeStaticGLBindingsEGL(); - gl::GLSurfaceEGL::InitializeOneOffForTesting(); + display_ = gl::GLSurfaceEGL::InitializeOneOffForTesting(); } - void TeardownMockEGL() { egl_.reset(); } + void TeardownMockEGL() { + gl::GLSurfaceEGL::ShutdownOneOff(display_); + egl_.reset(); + } void SetupFeatureInfo(const char* gl_extensions, const char* gl_version, @@ -91,6 +94,7 @@ std::unique_ptr<GpuFenceManager> manager_; std::unique_ptr<MockErrorState> error_state_; std::unique_ptr<::testing::NiceMock<::gl::MockEGLInterface>> egl_; + gl::GLDisplayEGL* display_ = nullptr; }; TEST_F(GpuFenceManagerTest, Basic) {
diff --git a/gpu/command_buffer/service/gpu_service_test.cc b/gpu/command_buffer/service/gpu_service_test.cc index 85cd16c..7f57e87 100644 --- a/gpu/command_buffer/service/gpu_service_test.cc +++ b/gpu/command_buffer/service/gpu_service_test.cc
@@ -29,7 +29,7 @@ testing::Test::SetUp(); gl::SetGLGetProcAddressProc(gl::MockGLInterface::GetGLProcAddress); - gl::GLSurfaceTestSupport::InitializeOneOffWithMockBindings(); + display_ = gl::GLSurfaceTestSupport::InitializeOneOffWithMockBindings(); gl_ = std::make_unique<::testing::StrictMock<::gl::MockGLInterface>>(); ::gl::MockGLInterface::SetGLInterface(gl_.get()); @@ -51,7 +51,7 @@ surface_ = nullptr; ::gl::MockGLInterface::SetGLInterface(nullptr); gl_.reset(); - gl::init::ShutdownGL(false); + gl::GLSurfaceTestSupport::ShutdownGL(display_); ran_teardown_ = true; testing::Test::TearDown();
diff --git a/gpu/command_buffer/service/gpu_service_test.h b/gpu/command_buffer/service/gpu_service_test.h index fa4d5fa2..1bede07 100644 --- a/gpu/command_buffer/service/gpu_service_test.h +++ b/gpu/command_buffer/service/gpu_service_test.h
@@ -11,6 +11,7 @@ #include "base/test/task_environment.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" +#include "ui/gl/gl_display.h" #include "ui/gl/gl_mock.h" namespace gl { @@ -43,6 +44,7 @@ scoped_refptr<gl::GLContextStub> context_; scoped_refptr<gl::GLSurfaceStub> surface_; base::test::SingleThreadTaskEnvironment task_environment_; + gl::GLDisplay* display_ = nullptr; }; } // namespace gles2
diff --git a/gpu/command_buffer/service/gr_cache_controller_unittest.cc b/gpu/command_buffer/service/gr_cache_controller_unittest.cc index 3276a0f..807287c8 100644 --- a/gpu/command_buffer/service/gr_cache_controller_unittest.cc +++ b/gpu/command_buffer/service/gr_cache_controller_unittest.cc
@@ -26,7 +26,7 @@ class GrCacheControllerTest : public testing::Test { public: void SetUp() override { - gl::GLSurfaceTestSupport::InitializeOneOffWithStubBindings(); + display_ = gl::GLSurfaceTestSupport::InitializeOneOffWithStubBindings(); gpu::GpuDriverBugWorkarounds workarounds; scoped_refptr<gl::GLShareGroup> share_group = new gl::GLShareGroup(); @@ -53,7 +53,7 @@ controller_ = nullptr; context_state_ = nullptr; task_runner_ = nullptr; - gl::init::ShutdownGL(false); + gl::GLSurfaceTestSupport::ShutdownGL(display_); } GrDirectContext* gr_context() { return context_state_->gr_context(); } @@ -62,6 +62,7 @@ scoped_refptr<SharedContextState> context_state_; scoped_refptr<base::TestMockTimeTaskRunner> task_runner_; std::unique_ptr<GrCacheController> controller_; + gl::GLDisplay* display_ = nullptr; }; TEST_F(GrCacheControllerTest, PurgeGrCache) {
diff --git a/gpu/command_buffer/service/image_reader_gl_owner_unittest.cc b/gpu/command_buffer/service/image_reader_gl_owner_unittest.cc index 72445a8..05fb2355 100644 --- a/gpu/command_buffer/service/image_reader_gl_owner_unittest.cc +++ b/gpu/command_buffer/service/image_reader_gl_owner_unittest.cc
@@ -38,7 +38,7 @@ gl::init::InitializeStaticGLBindingsImplementation( gl::GLImplementationParts(gl::kGLImplementationEGLGLES2), false); - gl::init::InitializeGLOneOffPlatformImplementation( + display_ = gl::init::InitializeGLOneOffPlatformImplementation( /*fallback_to_software_gl=*/false, /*disable_gl_drawing=*/false, /*init_extensions=*/true, @@ -83,7 +83,7 @@ context_ = nullptr; share_group_ = nullptr; surface_ = nullptr; - gl::init::ShutdownGL(false); + gl::init::ShutdownGL(display_, false); } bool IsImageReaderSupported() const { @@ -99,6 +99,7 @@ scoped_refptr<gl::GLShareGroup> share_group_; scoped_refptr<gl::GLSurface> surface_; base::test::TaskEnvironment task_environment_; + gl::GLDisplay* display_ = nullptr; }; TEST_F(ImageReaderGLOwnerTest, ImageReaderObjectCreation) {
diff --git a/gpu/command_buffer/service/raster_decoder_unittest.cc b/gpu/command_buffer/service/raster_decoder_unittest.cc index 62616ab..32940e5 100644 --- a/gpu/command_buffer/service/raster_decoder_unittest.cc +++ b/gpu/command_buffer/service/raster_decoder_unittest.cc
@@ -198,7 +198,7 @@ class RasterDecoderOOPTest : public testing::Test, DecoderClient { public: void SetUp() override { - gl::GLSurfaceTestSupport::InitializeOneOff(); + display_ = gl::GLSurfaceTestSupport::InitializeOneOff(); gpu::GpuDriverBugWorkarounds workarounds; scoped_refptr<gl::GLShareGroup> share_group = new gl::GLShareGroup(); @@ -252,7 +252,7 @@ context_state_.reset(); context_state_ = nullptr; - gl::init::ShutdownGL(false); + gl::GLSurfaceTestSupport::ShutdownGL(display_); } RasterDecoderOOPTest() : memory_tracker_(nullptr) { @@ -368,6 +368,7 @@ std::unique_ptr<SharedImageFactory> shared_image_factory_; SharedImageManager shared_image_manager_; gles2::MailboxManagerImpl mailbox_manager_; + gl::GLDisplay* display_ = nullptr; }; TEST_F(RasterDecoderOOPTest, CopyTexSubImage2DSizeMismatch) {
diff --git a/gpu/command_buffer/service/raster_decoder_unittest_base.cc b/gpu/command_buffer/service/raster_decoder_unittest_base.cc index 95c5de3..c0da5e4c 100644 --- a/gpu/command_buffer/service/raster_decoder_unittest_base.cc +++ b/gpu/command_buffer/service/raster_decoder_unittest_base.cc
@@ -99,7 +99,7 @@ // For easier substring/extension matching gl::SetGLGetProcAddressProc(gl::MockGLInterface::GetGLProcAddress); - gl::GLSurfaceTestSupport::InitializeOneOffWithMockBindings(); + display_ = gl::GLSurfaceTestSupport::InitializeOneOffWithMockBindings(); gl_ = std::make_unique<StrictMock<MockGLInterface>>(); ::gl::MockGLInterface::SetGLInterface(gl_.get()); @@ -218,7 +218,7 @@ shared_context_state_.reset(); ::gl::MockGLInterface::SetGLInterface(nullptr); gl_.reset(); - gl::init::ShutdownGL(false); + gl::GLSurfaceTestSupport::ShutdownGL(display_); } void RasterDecoderTestBase::TearDown() {
diff --git a/gpu/command_buffer/service/raster_decoder_unittest_base.h b/gpu/command_buffer/service/raster_decoder_unittest_base.h index b8edc537..1625699 100644 --- a/gpu/command_buffer/service/raster_decoder_unittest_base.h +++ b/gpu/command_buffer/service/raster_decoder_unittest_base.h
@@ -30,6 +30,7 @@ #include "gpu/config/gpu_driver_bug_workarounds.h" #include "gpu/config/gpu_preferences.h" #include "testing/gtest/include/gtest/gtest.h" +#include "ui/gl/gl_display.h" #include "ui/gl/gl_mock.h" #include "ui/gl/gl_surface_stub.h" #include "ui/gl/gl_version_info.h" @@ -193,6 +194,7 @@ base::test::SingleThreadTaskEnvironment task_environment_; raw_ptr<gles2::MockCopyTextureResourceManager> copy_texture_manager_; // not owned + gl::GLDisplay* display_ = nullptr; }; class RasterDecoderManualInitTest : public RasterDecoderTestBase {
diff --git a/gpu/command_buffer/service/surface_texture_gl_owner_unittest.cc b/gpu/command_buffer/service/surface_texture_gl_owner_unittest.cc index fa97fd0..e2de124 100644 --- a/gpu/command_buffer/service/surface_texture_gl_owner_unittest.cc +++ b/gpu/command_buffer/service/surface_texture_gl_owner_unittest.cc
@@ -39,7 +39,7 @@ void SetUp() override { gl::init::InitializeStaticGLBindingsImplementation( gl::GLImplementationParts(gl::kGLImplementationEGLGLES2), false); - gl::init::InitializeGLOneOffPlatformImplementation( + display_ = gl::init::InitializeGLOneOffPlatformImplementation( /*fallback_to_software_gl=*/false, /*disable_gl_drawing=*/false, /*init_extensions=*/true, @@ -83,7 +83,7 @@ context_ = nullptr; share_group_ = nullptr; surface_ = nullptr; - gl::init::ShutdownGL(false); + gl::init::ShutdownGL(display_, false); } scoped_refptr<TextureOwner> surface_texture_; @@ -95,6 +95,7 @@ scoped_refptr<gl::GLShareGroup> share_group_; scoped_refptr<gl::GLSurface> surface_; base::test::TaskEnvironment task_environment_; + gl::GLDisplay* display_ = nullptr; }; TEST_F(SurfaceTextureGLOwnerTest, OwnerReturnsServiceId) {
diff --git a/gpu/command_buffer/tests/gl_test_setup_helper.cc b/gpu/command_buffer/tests/gl_test_setup_helper.cc index 54e3acf..7b7d8f1 100644 --- a/gpu/command_buffer/tests/gl_test_setup_helper.cc +++ b/gpu/command_buffer/tests/gl_test_setup_helper.cc
@@ -44,7 +44,7 @@ ui::OzonePlatform::InitializeForGPU(params); #endif // defined(USE_OZONE) - gpu::GLTestHelper::InitializeGLDefault(); + display_ = gpu::GLTestHelper::InitializeGLDefault(); ::gles2::Initialize(); } @@ -53,7 +53,7 @@ // Otherwise the gpu-service tries to access GL during tear-down and causes // crashes. viz::TestGpuServiceHolder::ResetInstance(); - gl::init::ShutdownGL(/*due_to_fallback=*/false); + gl::init::ShutdownGL(display_, /*due_to_fallback=*/false); task_environment_ = nullptr; }
diff --git a/gpu/command_buffer/tests/gl_test_setup_helper.h b/gpu/command_buffer/tests/gl_test_setup_helper.h index 07b1d7b4..01d6eba 100644 --- a/gpu/command_buffer/tests/gl_test_setup_helper.h +++ b/gpu/command_buffer/tests/gl_test_setup_helper.h
@@ -7,6 +7,7 @@ #include "base/test/task_environment.h" #include "testing/gtest/include/gtest/gtest.h" +#include "ui/gl/gl_display.h" namespace gpu { @@ -26,6 +27,7 @@ private: std::unique_ptr<base::test::TaskEnvironment> task_environment_; + gl::GLDisplay* display_ = nullptr; }; } // namespace gpu
diff --git a/gpu/command_buffer/tests/gl_test_utils.cc b/gpu/command_buffer/tests/gl_test_utils.cc index d1f3235..28fc32c 100644 --- a/gpu/command_buffer/tests/gl_test_utils.cc +++ b/gpu/command_buffer/tests/gl_test_utils.cc
@@ -35,26 +35,27 @@ const uint8_t GLTestHelper::kCheckClearValue; #endif -bool GLTestHelper::InitializeGL(gl::GLImplementation gl_impl) { +gl::GLDisplay* GLTestHelper::InitializeGL(gl::GLImplementation gl_impl) { + gl::GLDisplay* display = nullptr; if (gl_impl == gl::GLImplementation::kGLImplementationNone) { - if (!gl::init::InitializeGLNoExtensionsOneOff(/*init_bindings=*/true, - /*system_device_id=*/0)) - return false; + display = gl::init::InitializeGLNoExtensionsOneOff(/*init_bindings=*/true, + /*system_device_id=*/0); } else { if (!gl::init::InitializeStaticGLBindingsImplementation( gl::GLImplementationParts(gl_impl), /*fallback_to_software_gl=*/false)) - return false; + return nullptr; - if (!gl::init::InitializeGLOneOffPlatformImplementation( - /*fallback_to_software_gl=*/false, - /*disable_gl_drawing=*/false, - /*init_extensions=*/false, - /*system_device_id=*/0)) { - return false; - } + display = gl::init::InitializeGLOneOffPlatformImplementation( + /*fallback_to_software_gl=*/false, + /*disable_gl_drawing=*/false, + /*init_extensions=*/false, + /*system_device_id=*/0); } + if (!display) + return nullptr; + gpu::GPUInfo gpu_info; gpu::CollectGraphicsInfoForTesting(&gpu_info); gpu::GLManager::g_gpu_feature_info = gpu::ComputeGpuFeatureInfo( @@ -64,10 +65,12 @@ gl::init::SetDisabledExtensionsPlatform( gpu::GLManager::g_gpu_feature_info.disabled_extensions); - return gl::init::InitializeExtensionSettingsOneOffPlatform(); + if (!gl::init::InitializeExtensionSettingsOneOffPlatform(display)) + return nullptr; + return display; } -bool GLTestHelper::InitializeGLDefault() { +gl::GLDisplay* GLTestHelper::InitializeGLDefault() { return GLTestHelper::InitializeGL( gl::GLImplementation::kGLImplementationNone); } @@ -409,8 +412,9 @@ } gl_reinitialized_ = true; - gl::init::ShutdownGL(false /* due_to_fallback */); - if (!GLTestHelper::InitializeGL(new_impl.gl)) { + gl::init::ShutdownGL(gl_display_, false /* due_to_fallback */); + gl_display_ = GLTestHelper::InitializeGL(new_impl.gl); + if (!gl_display_) { LOG(INFO) << "Skip test, failed to initialize EGL"; return false; } @@ -444,8 +448,8 @@ gl_.Destroy(); if (gl_reinitialized_) { - gl::init::ShutdownGL(false /* due_to_fallback */); - GLTestHelper::InitializeGLDefault(); + gl::init::ShutdownGL(gl_display_, false /* due_to_fallback */); + gl_display_ = GLTestHelper::InitializeGLDefault(); } gl_reinitialized_ = false;
diff --git a/gpu/command_buffer/tests/gl_test_utils.h b/gpu/command_buffer/tests/gl_test_utils.h index 501f9fd..098f6493 100644 --- a/gpu/command_buffer/tests/gl_test_utils.h +++ b/gpu/command_buffer/tests/gl_test_utils.h
@@ -14,6 +14,7 @@ #include "build/build_config.h" #include "gpu/command_buffer/tests/gl_manager.h" +#include "ui/gl/gl_display.h" #include "ui/gl/gl_implementation.h" namespace gl { @@ -26,8 +27,8 @@ public: static const uint8_t kCheckClearValue = 123u; - static bool InitializeGL(gl::GLImplementation gl_impl); - static bool InitializeGLDefault(); + static gl::GLDisplay* InitializeGL(gl::GLImplementation gl_impl); + static gl::GLDisplay* InitializeGLDefault(); static bool HasExtension(const char* extension); static bool CheckGLError(const char* msg, int line); @@ -140,6 +141,7 @@ gl::GLWindowSystemBindingInfo window_system_binding_info_; gfx::ExtensionSet egl_extensions_; gfx::ExtensionSet gl_extensions_; + gl::GLDisplay* gl_display_ = nullptr; }; } // namespace gpu
diff --git a/gpu/config/gpu_info_collector.cc b/gpu/config/gpu_info_collector.cc index b0c51a1..56406bb 100644 --- a/gpu/config/gpu_info_collector.cc +++ b/gpu/config/gpu_info_collector.cc
@@ -289,9 +289,10 @@ if (CollectGraphicsDeviceInfoFromCommandLine(command_line, gpu_info)) return true; + // We can't check if passthrough is supported yet because GL may not be + // initialized. gpu_info->passthrough_cmd_decoder = - gl::UsePassthroughCommandDecoder(command_line) && - gl::PassthroughCommandDecoderSupported(); + gl::UsePassthroughCommandDecoder(command_line); bool fallback_to_software = false; absl::optional<gl::GLImplementationParts> implementation = @@ -337,6 +338,11 @@ TRACE_EVENT0("startup", "gpu_info_collector::CollectGraphicsInfoGL"); DCHECK_NE(gl::GetGLImplementation(), gl::kGLImplementationNone); + // Now that we can check GL extensions, update passthrough support info. + if (!gl::PassthroughCommandDecoderSupported()) { + gpu_info->passthrough_cmd_decoder = false; + } + scoped_refptr<gl::GLSurface> surface(InitializeGLSurface()); if (!surface.get()) { LOG(ERROR) << "Could not create surface for info collection.";
diff --git a/gpu/config/gpu_info_collector_unittest.cc b/gpu/config/gpu_info_collector_unittest.cc index 5dfcf87..9e1881dd 100644 --- a/gpu/config/gpu_info_collector_unittest.cc +++ b/gpu/config/gpu_info_collector_unittest.cc
@@ -59,7 +59,7 @@ void SetUp() override { testing::Test::SetUp(); gl::SetGLGetProcAddressProc(gl::MockGLInterface::GetGLProcAddress); - gl::GLSurfaceTestSupport::InitializeOneOffWithMockBindings(); + display_ = gl::GLSurfaceTestSupport::InitializeOneOffWithMockBindings(); gl_ = std::make_unique<::testing::StrictMock<::gl::MockGLInterface>>(); ::gl::MockGLInterface::SetGLInterface(gl_.get()); switch (GetParam()) { @@ -184,12 +184,12 @@ void TearDown() override { ::gl::MockGLInterface::SetGLInterface(nullptr); gl_.reset(); - gl::init::ShutdownGL(false); + gl::GLSurfaceTestSupport::ShutdownGL(display_); testing::Test::TearDown(); } - public: + protected: // Use StrictMock to make 100% sure we know how GL will be called. std::unique_ptr<::testing::StrictMock<::gl::MockGLInterface>> gl_; GPUInfo test_values_; @@ -199,6 +199,8 @@ // Persistent storage is needed for the split extension string. std::vector<std::string> split_extensions_; + + gl::GLDisplay* display_ = nullptr; }; INSTANTIATE_TEST_SUITE_P(GPUConfig,
diff --git a/gpu/config/gpu_util.cc b/gpu/config/gpu_util.cc index 33b5b58..7776a4b 100644 --- a/gpu/config/gpu_util.cc +++ b/gpu/config/gpu_util.cc
@@ -47,6 +47,7 @@ #include "third_party/abseil-cpp/absl/types/optional.h" #include "ui/gfx/extension_set.h" #include "ui/gl/buildflags.h" +#include "ui/gl/gl_display_manager.h" #include "ui/gl/gl_implementation.h" #include "ui/gl/gl_switches.h" @@ -127,7 +128,9 @@ // synchronization, this is based on Android native fence sync // support. If that is unavailable, i.e. on emulator or SwiftShader, // don't claim SurfaceControl support. - if (!gl::GLSurfaceEGL::GetGLDisplayEGL()->IsAndroidNativeFenceSyncSupported()) + if (!gl::GLDisplayManagerEGL::GetInstance() + ->GetDisplay(gl::GpuPreference::kDefault) + ->IsAndroidNativeFenceSyncSupported()) return kGpuFeatureStatusDisabled; DCHECK(gfx::SurfaceControl::IsSupported()); @@ -747,10 +750,10 @@ } #if BUILDFLAG(IS_ANDROID) -bool InitializeGLThreadSafe(base::CommandLine* command_line, - const GpuPreferences& gpu_preferences, - GPUInfo* out_gpu_info, - GpuFeatureInfo* out_gpu_feature_info) { +gl::GLDisplay* InitializeGLThreadSafe(base::CommandLine* command_line, + const GpuPreferences& gpu_preferences, + GPUInfo* out_gpu_info, + GpuFeatureInfo* out_gpu_feature_info) { static base::NoDestructor<base::Lock> gl_bindings_initialization_lock; base::AutoLock auto_lock(*gl_bindings_initialization_lock); DCHECK(command_line); @@ -761,15 +764,23 @@ if (gpu_info_cached) { // GL bindings have already been initialized in another thread. DCHECK_NE(gl::kGLImplementationNone, gl::GetGLImplementation()); - return true; + return gl::GLDisplayManagerEGL::GetInstance()->GetDisplay( + gl::GpuPreference::kDefault); } + + gl::GLDisplay* gl_display = nullptr; if (gl::GetGLImplementation() == gl::kGLImplementationNone) { // Some tests initialize bindings by themselves. - if (!gl::init::InitializeGLNoExtensionsOneOff(/*init_bindings=*/true, - /*system_device_id=*/0)) { + gl_display = + gl::init::InitializeGLNoExtensionsOneOff(/*init_bindings=*/true, + /*system_device_id=*/0); + if (!gl_display) { VLOG(1) << "gl::init::InitializeGLNoExtensionsOneOff failed"; - return false; + return nullptr; } + } else { + gl_display = gl::GLDisplayManagerEGL::GetInstance()->GetDisplay( + gl::GpuPreference::kDefault); } CollectContextGraphicsInfo(out_gpu_info); *out_gpu_feature_info = ComputeGpuFeatureInfo(*out_gpu_info, gpu_preferences, @@ -778,13 +789,13 @@ gl::init::SetDisabledExtensionsPlatform( out_gpu_feature_info->disabled_extensions); } - if (!gl::init::InitializeExtensionSettingsOneOffPlatform()) { + if (!gl::init::InitializeExtensionSettingsOneOffPlatform(gl_display)) { VLOG(1) << "gl::init::InitializeExtensionSettingsOneOffPlatform failed"; - return false; + return nullptr; } CacheGPUInfo(*out_gpu_info); CacheGpuFeatureInfo(*out_gpu_feature_info); - return true; + return gl_display; } #endif // BUILDFLAG(IS_ANDROID)
diff --git a/gpu/config/gpu_util.h b/gpu/config/gpu_util.h index f72970c..08b18c4 100644 --- a/gpu/config/gpu_util.h +++ b/gpu/config/gpu_util.h
@@ -8,6 +8,7 @@ #include "build/build_config.h" #include "gpu/config/gpu_feature_info.h" #include "gpu/gpu_export.h" +#include "ui/gl/gl_display.h" namespace base { class CommandLine; @@ -62,10 +63,11 @@ // bindings, create a GL context, collects GPUInfo, make blocklist and // GPU driver bug workaround decisions. This is intended to be called // by Android WebView render thread and in-process GPU thread. -GPU_EXPORT bool InitializeGLThreadSafe(base::CommandLine* command_line, - const GpuPreferences& gpu_preferences, - GPUInfo* out_gpu_info, - GpuFeatureInfo* out_gpu_feature_info); +GPU_EXPORT gl::GLDisplay* InitializeGLThreadSafe( + base::CommandLine* command_line, + const GpuPreferences& gpu_preferences, + GPUInfo* out_gpu_info, + GpuFeatureInfo* out_gpu_feature_info); #endif // BUILDFLAG(IS_ANDROID) // Returns whether SwiftShader should be enabled. If true, the proper command
diff --git a/gpu/gles2_conform_support/egl/thread_state.cc b/gpu/gles2_conform_support/egl/thread_state.cc index 624b9dc0..e7764db 100644 --- a/gpu/gles2_conform_support/egl/thread_state.cc +++ b/gpu/gles2_conform_support/egl/thread_state.cc
@@ -22,6 +22,7 @@ #include "gpu/gles2_conform_support/egl/surface.h" #include "gpu/gles2_conform_support/egl/test_support.h" #include "ui/gl/gl_context.h" +#include "ui/gl/gl_display.h" #include "ui/gl/gl_surface.h" #include "ui/gl/gl_switches.h" #include "ui/gl/init/gl_factory.h" @@ -88,8 +89,9 @@ #if defined(USE_OZONE) ui::OzonePlatform::InitializeForGPU(ui::OzonePlatform::InitParams()); #endif - gl::init::InitializeGLNoExtensionsOneOff(/*init_bindings=*/true, - /*system_device_id=*/0); + gl::GLDisplay* display = + gl::init::InitializeGLNoExtensionsOneOff(/*init_bindings=*/true, + /*system_device_id=*/0); gpu::GpuFeatureInfo gpu_feature_info; if (!command_line->HasSwitch(switches::kDisableGpuDriverBugWorkarounds)) { gpu::GPUInfo gpu_info; @@ -101,7 +103,7 @@ gl::init::SetDisabledExtensionsPlatform( gpu_feature_info.disabled_extensions); - gl::init::InitializeExtensionSettingsOneOffPlatform(); + gl::init::InitializeExtensionSettingsOneOffPlatform(display); } g_egl_default_display = new egl::Display();
diff --git a/gpu/ipc/common/gpu_memory_buffer_impl_test_template.h b/gpu/ipc/common/gpu_memory_buffer_impl_test_template.h index c8c6bca..cbbffd6c 100644 --- a/gpu/ipc/common/gpu_memory_buffer_impl_test_template.h +++ b/gpu/ipc/common/gpu_memory_buffer_impl_test_template.h
@@ -22,6 +22,7 @@ #include "testing/gtest/include/gtest/gtest.h" #include "ui/gfx/buffer_format_util.h" #include "ui/gfx/mojom/buffer_types.mojom.h" +#include "ui/gl/gl_display.h" #if BUILDFLAG(IS_WIN) || defined(USE_OZONE) #include "ui/gl/init/gl_factory.h" @@ -52,8 +53,10 @@ #if BUILDFLAG(IS_WIN) || defined(USE_OZONE) // Overridden from testing::Test: - void SetUp() override { gl::GLSurfaceTestSupport::InitializeOneOff(); } - void TearDown() override { gl::init::ShutdownGL(false); } + void SetUp() override { + display_ = gl::GLSurfaceTestSupport::InitializeOneOff(); + } + void TearDown() override { gl::GLSurfaceTestSupport::ShutdownGL(display_); } #endif protected: @@ -62,6 +65,7 @@ private: GpuMemoryBufferSupport gpu_memory_buffer_support_; + gl::GLDisplay* display_ = nullptr; void FreeGpuMemoryBuffer(base::OnceClosure free_callback, bool* destroyed,
diff --git a/gpu/ipc/service/gpu_channel_test_common.cc b/gpu/ipc/service/gpu_channel_test_common.cc index f2dbc67..0149d19 100644 --- a/gpu/ipc/service/gpu_channel_test_common.cc +++ b/gpu/ipc/service/gpu_channel_test_common.cc
@@ -86,9 +86,9 @@ new TestGpuChannelManagerDelegate(scheduler_.get())) { // We need GL bindings to actually initialize command buffers. if (use_stub_bindings) { - gl::GLSurfaceTestSupport::InitializeOneOffWithStubBindings(); + display_ = gl::GLSurfaceTestSupport::InitializeOneOffWithStubBindings(); } else { - gl::GLSurfaceTestSupport::InitializeOneOff(); + display_ = gl::GLSurfaceTestSupport::InitializeOneOff(); } GpuFeatureInfo feature_info; @@ -110,7 +110,7 @@ // Command buffers can post tasks and run GL in destruction so do this first. channel_manager_ = nullptr; task_environment_.RunUntilIdle(); - gl::init::ShutdownGL(false); + gl::GLSurfaceTestSupport::ShutdownGL(display_); } GpuChannel* GpuChannelTestCommon::CreateChannel(int32_t client_id,
diff --git a/gpu/ipc/service/gpu_channel_test_common.h b/gpu/ipc/service/gpu_channel_test_common.h index d56372d..338a579 100644 --- a/gpu/ipc/service/gpu_channel_test_common.h +++ b/gpu/ipc/service/gpu_channel_test_common.h
@@ -16,6 +16,7 @@ #include "gpu/command_buffer/common/context_result.h" #include "gpu/ipc/common/gpu_channel.mojom.h" #include "testing/gtest/include/gtest/gtest.h" +#include "ui/gl/gl_display.h" namespace base { namespace trace_event { @@ -67,6 +68,7 @@ std::unique_ptr<Scheduler> scheduler_; std::unique_ptr<TestGpuChannelManagerDelegate> channel_manager_delegate_; std::unique_ptr<GpuChannelManager> channel_manager_; + gl::GLDisplay* display_ = nullptr; }; } // namespace gpu
diff --git a/gpu/ipc/service/gpu_init.cc b/gpu/ipc/service/gpu_init.cc index 37e2ce5..f1ea8c0 100644 --- a/gpu/ipc/service/gpu_init.cc +++ b/gpu/ipc/service/gpu_init.cc
@@ -421,11 +421,13 @@ return false; #else // !(BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)) SaveHardwareGpuInfoAndGpuFeatureInfo(); - gl::init::ShutdownGL(true); + gl::init::ShutdownGL(nullptr, true); gl_initialized = false; #endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) } + gl::GLDisplay* gl_display = nullptr; + if (!gl_initialized) { // Pause watchdog. LoadLibrary in GLBindings may take long time. if (watchdog_thread_) @@ -439,8 +441,9 @@ if (watchdog_thread_) watchdog_thread_->ResumeWatchdog(); if (gl::GetGLImplementation() != gl::kGLImplementationDisabled) { - gl_initialized = gl::init::InitializeGLNoExtensionsOneOff( + gl_display = gl::init::InitializeGLNoExtensionsOneOff( /*init_bindings*/ false, system_device_id); + gl_initialized = !!gl_display; if (!gl_initialized) { VLOG(1) << "gl::init::InitializeGLNoExtensionsOneOff failed"; return false; @@ -514,11 +517,12 @@ return false; #else SaveHardwareGpuInfoAndGpuFeatureInfo(); - gl::init::ShutdownGL(true); + gl::init::ShutdownGL(gl_display, true); watchdog_thread_ = nullptr; watchdog_init.SetGpuWatchdogPtr(nullptr); - if (!gl::init::InitializeGLNoExtensionsOneOff(/*init_bindings*/ true, - system_device_id)) { + gl_display = gl::init::InitializeGLNoExtensionsOneOff( + /*init_bindings=*/true, system_device_id); + if (!gl_display) { VLOG(1) << "gl::init::InitializeGLNoExtensionsOneOff with SwiftShader " << "failed"; @@ -621,7 +625,7 @@ gl::init::SetDisabledExtensionsPlatform( gpu_feature_info_.disabled_extensions); } - if (!gl::init::InitializeExtensionSettingsOneOffPlatform()) { + if (!gl::init::InitializeExtensionSettingsOneOffPlatform(gl_display)) { VLOG(1) << "gl::init::InitializeExtensionSettingsOneOffPlatform failed"; return false; } @@ -804,11 +808,14 @@ } #endif // !BUILDFLAG(IS_CASTOS) + gl::GLDisplay* gl_display = nullptr; + gl_use_swiftshader_ = EnableSwiftShaderIfNeeded( command_line, gpu_feature_info_, gpu_preferences_.disable_software_rasterizer, needs_more_info); - if (!gl::init::InitializeGLNoExtensionsOneOff(/*init_bindings=*/true, - /*system_device_id=*/0)) { + gl_display = gl::init::InitializeGLNoExtensionsOneOff(/*init_bindings=*/true, + /*system_device_id=*/0); + if (!gl_display) { VLOG(1) << "gl::init::InitializeGLNoExtensionsOneOff failed"; return; } @@ -823,9 +830,10 @@ gpu_preferences_.disable_software_rasterizer, false); if (gl_use_swiftshader_) { SaveHardwareGpuInfoAndGpuFeatureInfo(); - gl::init::ShutdownGL(true); - if (!gl::init::InitializeGLNoExtensionsOneOff(/*init_bindings=*/true, - /*system_device_id=*/0)) { + gl::init::ShutdownGL(gl_display, true); + gl_display = gl::init::InitializeGLNoExtensionsOneOff( + /*init_bindings=*/true, /*system_device_id=*/0); + if (!gl_display) { VLOG(1) << "gl::init::InitializeGLNoExtensionsOneOff failed " << "with SwiftShader"; return; @@ -853,7 +861,7 @@ gl::init::SetDisabledExtensionsPlatform( gpu_feature_info_.disabled_extensions); } - if (!gl::init::InitializeExtensionSettingsOneOffPlatform()) { + if (!gl::init::InitializeExtensionSettingsOneOffPlatform(gl_display)) { VLOG(1) << "gl::init::InitializeExtensionSettingsOneOffPlatform failed"; } default_offscreen_surface_ = @@ -878,9 +886,10 @@ gpu_preferences_.disable_software_rasterizer, false); if (gl_use_swiftshader_) { SaveHardwareGpuInfoAndGpuFeatureInfo(); - gl::init::ShutdownGL(true); - if (!gl::init::InitializeGLNoExtensionsOneOff(/*init_bindings=*/true, - /*system_device_id=*/0)) { + gl::init::ShutdownGL(gl_display, true); + gl_display = gl::init::InitializeGLNoExtensionsOneOff( + /*init_bindings=*/true, /*system_device_id=*/0); + if (!gl_display) { VLOG(1) << "gl::init::InitializeGLNoExtensionsOneOff failed " << "with SwiftShader"; return;
diff --git a/gpu/ipc/service/gpu_memory_buffer_factory_test_template.h b/gpu/ipc/service/gpu_memory_buffer_factory_test_template.h index 8d4888b8..36663b2 100644 --- a/gpu/ipc/service/gpu_memory_buffer_factory_test_template.h +++ b/gpu/ipc/service/gpu_memory_buffer_factory_test_template.h
@@ -14,6 +14,7 @@ #include "gpu/ipc/service/gpu_memory_buffer_factory.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/gfx/buffer_format_util.h" +#include "ui/gl/gl_display.h" #if BUILDFLAG(IS_WIN) || defined(USE_OZONE) #include "base/command_line.h" @@ -35,9 +36,9 @@ DCHECK(base::CommandLine::ForCurrentProcess()->HasSwitch( switches::kUseGpuInTests)); #endif - gl::GLSurfaceTestSupport::InitializeOneOff(); + display_ = gl::GLSurfaceTestSupport::InitializeOneOff(); } - void TearDown() override { gl::init::ShutdownGL(false); } + void TearDown() override { gl::GLSurfaceTestSupport::ShutdownGL(display_); } #endif // BUILDFLAG(IS_WIN) || defined(USE_OZONE) protected: @@ -45,6 +46,7 @@ base::test::TaskEnvironment::MainThreadType::UI}; GpuMemoryBufferFactoryType factory_; + gl::GLDisplay* display_ = nullptr; }; TYPED_TEST_SUITE_P(GpuMemoryBufferFactoryTest);
diff --git a/infra/config/generated/luci/cr-buildbucket.cfg b/infra/config/generated/luci/cr-buildbucket.cfg index e8b94f2..b3be2e0 100644 --- a/infra/config/generated/luci/cr-buildbucket.cfg +++ b/infra/config/generated/luci/cr-buildbucket.cfg
@@ -65568,11 +65568,10 @@ builders { name: "linux-rel" swarming_host: "chromium-swarm.appspot.com" - dimensions: "builder:linux-rel" dimensions: "cores:2" dimensions: "cpu:x86-64" dimensions: "os:Ubuntu-18.04" - dimensions: "pool:luci.chromium.try" + dimensions: "pool:luci.chromium.try.orchestrator" exe { cipd_package: "infra/chromium/bootstrapper/${platform}" cipd_version: "latest"
diff --git a/infra/config/lib/try.star b/infra/config/lib/try.star index 8fba65f..dcc2fa2 100644 --- a/infra/config/lib/try.star +++ b/infra/config/lib/try.star
@@ -232,6 +232,7 @@ *, name, compilator, + use_orchestrator_pool = False, **kwargs): """Define an orchestrator builder. @@ -252,6 +253,9 @@ name: The name of the orchestrator. compilator: A string identifying the associated compilator. Compilators can be defined using try_.compilator_builder. + use_orchestrator_pool: Whether to use the bots in + luci.chromium.try.orchestrator pool. This kwarg should be taken out + once all CQ builders are migrated to be srcless (crbug/1287228) **kwargs: Additional kwargs to be forwarded to try_.builder. The following kwargs will have defaults applied if not set: * builderless: True on branches, False on main @@ -265,7 +269,14 @@ if not builder_group: fail("builder_group must be specified") - kwargs.setdefault("builderless", not settings.is_main) + # TODO(crbug/1287228): Make this the default once all CQ builders are + # migrated to be srcless + if use_orchestrator_pool: + kwargs.setdefault("pool", "luci.chromium.try.orchestrator") + kwargs.setdefault("builderless", None) + else: + kwargs.setdefault("builderless", not settings.is_main) + kwargs.setdefault("cores", defaults.orchestrator_cores.get()) kwargs.setdefault("executable", "recipe:chromium/orchestrator")
diff --git a/infra/config/subprojects/chromium/try/tryserver.chromium.linux.star b/infra/config/subprojects/chromium/try/tryserver.chromium.linux.star index 470a1d3d..ed952736 100644 --- a/infra/config/subprojects/chromium/try/tryserver.chromium.linux.star +++ b/infra/config/subprojects/chromium/try/tryserver.chromium.linux.star
@@ -255,6 +255,7 @@ experiments = { "remove_src_checkout_experiment": 100, }, + use_orchestrator_pool = True, ) try_.compilator_builder(
diff --git a/ios/chrome/app/app_startup_parameters.h b/ios/chrome/app/app_startup_parameters.h index a156696..7b285ad 100644 --- a/ios/chrome/app/app_startup_parameters.h +++ b/ios/chrome/app/app_startup_parameters.h
@@ -13,13 +13,14 @@ enum class ApplicationModeForTabOpening { NORMAL, INCOGNITO, CURRENT }; -enum NTPTabOpeningPostOpeningAction { +enum TabOpeningPostOpeningAction { // No action should be done NO_ACTION = 0, START_VOICE_SEARCH, START_QR_CODE_SCANNER, FOCUS_OMNIBOX, - NTP_TAB_OPENING_POST_OPENING_ACTION_COUNT, + SHOW_DEFAULT_BROWSER_SETTINGS, + TAB_OPENING_POST_OPENING_ACTION_COUNT, }; class GURL; @@ -52,9 +53,9 @@ @property(nonatomic, readwrite, assign) BOOL launchInIncognito; // The mode in which the tab must be opened. @property(nonatomic, readonly) ApplicationModeForTabOpening applicationMode; -// Action to be taken after opening the initial NTP. +// Action to be taken after loading the URL. @property(nonatomic, readwrite, assign) - NTPTabOpeningPostOpeningAction postOpeningAction; + TabOpeningPostOpeningAction postOpeningAction; // Boolean to track if a Payment Request response is requested at startup. @property(nonatomic, readwrite, assign) BOOL completePaymentRequest; // Text query that should be executed on startup.
diff --git a/ios/chrome/app/app_startup_parameters.mm b/ios/chrome/app/app_startup_parameters.mm index 0db8ba6a..16a21a9 100644 --- a/ios/chrome/app/app_startup_parameters.mm +++ b/ios/chrome/app/app_startup_parameters.mm
@@ -93,4 +93,11 @@ : ApplicationModeForTabOpening::NORMAL; } +- (void)setPostOpeningAction:(TabOpeningPostOpeningAction)action { + // Only NO_ACTION or SHOW_DEFAULT_BROWSER_SETTINGS are allowed on non NTP. + DCHECK(action == NO_ACTION || action == SHOW_DEFAULT_BROWSER_SETTINGS || + _externalURL == GURL(kChromeUINewTabURL)); + _postOpeningAction = action; +} + @end
diff --git a/ios/chrome/app/application_delegate/mock_tab_opener.h b/ios/chrome/app/application_delegate/mock_tab_opener.h index 0a3c152..3d29926 100644 --- a/ios/chrome/app/application_delegate/mock_tab_opener.h +++ b/ios/chrome/app/application_delegate/mock_tab_opener.h
@@ -30,7 +30,7 @@ - (void)resetURL; - (ProceduralBlock)completionBlockForTriggeringAction: - (NTPTabOpeningPostOpeningAction)action; + (TabOpeningPostOpeningAction)action; @end #endif // IOS_CHROME_APP_APPLICATION_DELEGATE_MOCK_TAB_OPENER_H_
diff --git a/ios/chrome/app/application_delegate/mock_tab_opener.mm b/ios/chrome/app/application_delegate/mock_tab_opener.mm index c8e923fd..6f0fab2c 100644 --- a/ios/chrome/app/application_delegate/mock_tab_opener.mm +++ b/ios/chrome/app/application_delegate/mock_tab_opener.mm
@@ -55,7 +55,7 @@ } - (ProceduralBlock)completionBlockForTriggeringAction: - (NTPTabOpeningPostOpeningAction)action { + (TabOpeningPostOpeningAction)action { // Stub return nil; }
diff --git a/ios/chrome/app/application_delegate/tab_opening.h b/ios/chrome/app/application_delegate/tab_opening.h index b8b3e412..f4a2613 100644 --- a/ios/chrome/app/application_delegate/tab_opening.h +++ b/ios/chrome/app/application_delegate/tab_opening.h
@@ -54,7 +54,7 @@ // `dismissModalsAndOpenSelectedTabInMode:withURL:transition:completion:`. // This block must only be executed if new tab opened on NTP. - (ProceduralBlock)completionBlockForTriggeringAction: - (NTPTabOpeningPostOpeningAction)action; + (TabOpeningPostOpeningAction)action; // Whether the `URL` is already opened, in regular mode. - (BOOL)URLIsOpenedInRegularMode:(const GURL&)URL;
diff --git a/ios/chrome/app/application_delegate/url_opener.mm b/ios/chrome/app/application_delegate/url_opener.mm index 0c9da79..5a90dc2 100644 --- a/ios/chrome/app/application_delegate/url_opener.mm +++ b/ios/chrome/app/application_delegate/url_opener.mm
@@ -27,6 +27,9 @@ // Key of the UMA Startup.MobileSessionStartFromApps histogram. const char* const kUMAMobileSessionStartFromAppsHistogram = "Startup.MobileSessionStartFromApps"; +// Key of the UMA Startup.ShowDefaultPromoFromApps histogram. +const char* const kUMAShowDefaultPromoFromAppsHistogram = + "Startup.ShowDefaultPromoFromApps"; } // namespace @implementation URLOpener @@ -58,6 +61,11 @@ UMA_HISTOGRAM_ENUMERATION(kUMAMobileSessionStartFromAppsHistogram, callerApp, MOBILE_SESSION_CALLER_APP_COUNT); + if (params.postOpeningAction == SHOW_DEFAULT_BROWSER_SETTINGS) { + UMA_HISTOGRAM_ENUMERATION(kUMAShowDefaultPromoFromAppsHistogram, callerApp, + MOBILE_SESSION_CALLER_APP_COUNT); + } + if (initStage == InitStageFirstRun) { UMA_HISTOGRAM_ENUMERATION("FirstRun.LaunchSource", [params launchSource], first_run::LAUNCH_SIZE);
diff --git a/ios/chrome/app/main_controller.mm b/ios/chrome/app/main_controller.mm index d142348..59436e2 100644 --- a/ios/chrome/app/main_controller.mm +++ b/ios/chrome/app/main_controller.mm
@@ -971,8 +971,23 @@ }]; } +// Some experiments value may be useful for first-party applications, so save +// the value in the shared application group. +- (void)saveFieldTrialValuesForGroupApp { + NSUserDefaults* sharedDefaults = app_group::GetCommonGroupUserDefaults(); + NSNumber* supportsShowDefaultBrowserPromo = + @(base::FeatureList::IsEnabled(kDefaultBrowserIntentsShowSettings)); + + NSDictionary* capabilities = @{ + app_group:: + kChromeShowDefaultBrowserPromoCapability : supportsShowDefaultBrowserPromo + }; + [sharedDefaults setObject:capabilities + forKey:app_group::kChromeCapabilitiesPreference]; +} + // Some extensions need the value of field trials but can't get them because the -// field trial infrastruction isn't in extensions. Save the necessary values to +// field trial infrastructure isn't in extensions. Save the necessary values to // NSUserDefaults here. - (void)saveFieldTrialValuesForExtensions { using password_manager::features::kIOSEnablePasswordManagerBrandingUpdate;
diff --git a/ios/chrome/app/startup/chrome_app_startup_parameters.mm b/ios/chrome/app/startup/chrome_app_startup_parameters.mm index 421f861..cb09180 100644 --- a/ios/chrome/app/startup/chrome_app_startup_parameters.mm +++ b/ios/chrome/app/startup/chrome_app_startup_parameters.mm
@@ -152,6 +152,14 @@ } } +TabOpeningPostOpeningAction XCallbackPoaToPostOpeningAction( + const std::string& poa_param) { + if (poa_param == "default-browser-settings") { + return SHOW_DEFAULT_BROWSER_SETTINGS; + } + return NO_ACTION; +} + } // namespace @implementation ChromeAppStartupParameters { @@ -254,12 +262,19 @@ (!url.SchemeIs(url::kHttpScheme) && !url.SchemeIs(url::kHttpsScheme))) { return nil; } + TabOpeningPostOpeningAction postOpeningAction = + XCallbackPoaToPostOpeningAction(parameters["poa"]); - return [[ChromeAppStartupParameters alloc] initWithExternalURL:url - declaredSourceApp:appId - secureSourceApp:nil - completeURL:completeURL]; - + ChromeAppStartupParameters* startupParameters = + [[ChromeAppStartupParameters alloc] initWithExternalURL:url + declaredSourceApp:appId + secureSourceApp:nil + completeURL:completeURL]; + // postOpeningAction can only be NO_ACTION or SHOW_DEFAULT_BROWSER_SETTINGS + // (these are the only values returned by `XCallbackPoaToPostOpeningAction`) + // so this assignment should not DCHECK, no matter what the URL is. + startupParameters.postOpeningAction = postOpeningAction; + return startupParameters; } else if (gurl.SchemeIsFile()) { UMA_HISTOGRAM_ENUMERATION(kUMAMobileSessionStartActionHistogram, START_ACTION_OPEN_FILE,
diff --git a/ios/chrome/browser/default_browser/BUILD.gn b/ios/chrome/browser/default_browser/BUILD.gn new file mode 100644 index 0000000..1354dd9 --- /dev/null +++ b/ios/chrome/browser/default_browser/BUILD.gn
@@ -0,0 +1,8 @@ +# Copyright 2022 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +source_set("default_browser") { + configs += [ "//build/config/compiler:enable_arc" ] + sources = [ "promo_source.h" ] +}
diff --git a/ios/chrome/browser/default_browser/promo_source.h b/ios/chrome/browser/default_browser/promo_source.h new file mode 100644 index 0000000..43a18dd --- /dev/null +++ b/ios/chrome/browser/default_browser/promo_source.h
@@ -0,0 +1,17 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef IOS_CHROME_BROWSER_DEFAULT_BROWSER_PROMO_SOURCE_H_ +#define IOS_CHROME_BROWSER_DEFAULT_BROWSER_PROMO_SOURCE_H_ + +// An histogram to report the source of the default browser promo. +// Used for UMA, do not reorder. +enum class DefaultBrowserPromoSource { + kSettings = 0, + kOmnibox, + kExternalIntent, + kMaxValue = kExternalIntent, +}; + +#endif // IOS_CHROME_BROWSER_DEFAULT_BROWSER_PROMO_SOURCE_H_
diff --git a/ios/chrome/browser/flags/about_flags.mm b/ios/chrome/browser/flags/about_flags.mm index 3c1ec86b..cb809f9 100644 --- a/ios/chrome/browser/flags/about_flags.mm +++ b/ios/chrome/browser/flags/about_flags.mm
@@ -951,6 +951,10 @@ {"3p-intents-in-incognito", flag_descriptions::kIOS3PIntentsInIncognitoName, flag_descriptions::kIOS3PIntentsInIncognitoDescription, flags_ui::kOsIos, FEATURE_VALUE_TYPE(kIOS3PIntentsInIncognito)}, + {"default-browser-intents-show-settings", + flag_descriptions::kDefaultBrowserIntentsShowSettingsName, + flag_descriptions::kDefaultBrowserIntentsShowSettingsDescription, + flags_ui::kOsIos, FEATURE_VALUE_TYPE(kDefaultBrowserIntentsShowSettings)}, }; bool SkipConditionalFeatureEntry(const flags_ui::FeatureEntry& entry) {
diff --git a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc index 04fe0ffe..f71aeea 100644 --- a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc +++ b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
@@ -159,6 +159,12 @@ "When enabled, will show a modified default browser fullscreen modal promo " "UI."; +const char kDefaultBrowserIntentsShowSettingsName[] = + "Default Browser Intents show settings"; +const char kDefaultBrowserIntentsShowSettingsDescription[] = + "When enabled, external apps can trigger the settings screen showing " + "default browser tutorial."; + const char kAddSettingForDefaultPageModeName[] = "Let user choose default mode"; const char kAddSettingForDefaultPageModeDescription[] = "When enabled, the user can choose if they want the page in Desktop or "
diff --git a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h index dde4561..819d5e2f 100644 --- a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h +++ b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
@@ -135,6 +135,11 @@ extern const char kDefaultBrowserFullscreenPromoExperimentName[]; extern const char kDefaultBrowserFullscreenPromoExperimentDescription[]; +// Title and description for the flag to show the default browser tutorial from +// an external app. +extern const char kDefaultBrowserIntentsShowSettingsName[]; +extern const char kDefaultBrowserIntentsShowSettingsDescription[]; + // Title and description for the flag that is used to let the user choose the // default mode (Mobile/Desktop) they would like to use when requesting a page. extern const char kAddSettingForDefaultPageModeName[];
diff --git a/ios/chrome/browser/ios_chrome_main_parts.mm b/ios/chrome/browser/ios_chrome_main_parts.mm index c43c354..04fd297d 100644 --- a/ios/chrome/browser/ios_chrome_main_parts.mm +++ b/ios/chrome/browser/ios_chrome_main_parts.mm
@@ -302,7 +302,8 @@ // immediately after starting metrics recording. IOSChromeMetricsServiceAccessor::RegisterSyntheticFieldTrial( "CrashpadIOS", - crash_reporter::IsCrashpadRunning() ? "Enabled" : "Disabled"); + crash_reporter::IsCrashpadRunning() ? "Enabled" : "Disabled", + variations::SyntheticTrialAnnotationMode::kCurrentLog); // Because the CleanExitBeacon flag takes 2 restarts to take effect, register // a synthetic field trial when the user defaults beacon is set. Called @@ -310,7 +311,8 @@ IOSChromeMetricsServiceAccessor::RegisterSyntheticFieldTrial( "UseUserDefaultsForExitedCleanlyBeacon", metrics::CleanExitBeacon::ShouldUseUserDefaultsBeacon() ? "Enabled" - : "Disabled"); + : "Disabled", + variations::SyntheticTrialAnnotationMode::kCurrentLog); #if BUILDFLAG(ENABLE_RLZ) // Init the RLZ library. This just schedules a task on the file thread to be
diff --git a/ios/chrome/browser/ui/commands/application_commands.h b/ios/chrome/browser/ui/commands/application_commands.h index 2c23b3f..7136a280 100644 --- a/ios/chrome/browser/ui/commands/application_commands.h +++ b/ios/chrome/browser/ui/commands/application_commands.h
@@ -15,6 +15,7 @@ @class ShowSigninCommand; @class StartVoiceSearchCommand; @class UIViewController; +enum class DefaultBrowserPromoSource; namespace syncer { enum class TrustedVaultUserActionTriggerForUMA; } // namespace syncer @@ -67,7 +68,9 @@ // Shows the settings page informing the user how to set Chrome as the default // browser. - (void)showDefaultBrowserSettingsFromViewController: - (UIViewController*)baseViewController; + (UIViewController*)baseViewController + sourceForUMA: + (DefaultBrowserPromoSource)source; // Shows the settings page allowing the user to clear their browsing data. - (void)showClearBrowsingDataSettings;
diff --git a/ios/chrome/browser/ui/content_suggestions/BUILD.gn b/ios/chrome/browser/ui/content_suggestions/BUILD.gn index b1e612c..8e4851c 100644 --- a/ios/chrome/browser/ui/content_suggestions/BUILD.gn +++ b/ios/chrome/browser/ui/content_suggestions/BUILD.gn
@@ -323,6 +323,7 @@ "//ios/chrome/browser:pref_names", "//ios/chrome/browser:utils", "//ios/chrome/browser/ui:feature_flags", + "//ios/chrome/browser/ui/authentication:eg_test_support+eg2", "//ios/chrome/browser/ui/content_suggestions/cells:constants", "//ios/chrome/browser/ui/ntp:constants", "//ios/chrome/browser/ui/ntp:feature_flags", @@ -332,6 +333,7 @@ "//ios/chrome/browser/ui/toolbar/public:constants", "//ios/chrome/test:eg_test_support+eg2", "//ios/chrome/test/earl_grey:eg_test_support+eg2", + "//ios/public/provider/chrome/browser/signin:fake_chrome_identity", "//ios/testing/earl_grey:eg_test_support+eg2", "//ios/third_party/earl_grey2:test_lib", "//net:test_support",
diff --git a/ios/chrome/browser/ui/content_suggestions/ntp_home_egtest.mm b/ios/chrome/browser/ui/content_suggestions/ntp_home_egtest.mm index 87279765..5d38ff2 100644 --- a/ios/chrome/browser/ui/content_suggestions/ntp_home_egtest.mm +++ b/ios/chrome/browser/ui/content_suggestions/ntp_home_egtest.mm
@@ -10,6 +10,8 @@ #include "components/strings/grit/components_strings.h" #include "ios/chrome/browser/chrome_switches.h" #import "ios/chrome/browser/pref_names.h" +#import "ios/chrome/browser/ui/authentication/signin_earl_grey.h" +#import "ios/chrome/browser/ui/authentication/signin_earl_grey_ui_test_util.h" #import "ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_cells_constants.h" #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_constants.h" #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_feature.h" @@ -29,6 +31,7 @@ #import "ios/chrome/test/earl_grey/chrome_matchers.h" #import "ios/chrome/test/earl_grey/chrome_test_case.h" #import "ios/chrome/test/scoped_eg_synchronization_disabler.h" +#import "ios/public/provider/chrome/browser/signin/fake_chrome_identity.h" #import "ios/testing/earl_grey/disabled_test_macros.h" #import "ios/testing/earl_grey/earl_grey_test.h" #include "net/test/embedded_test_server/embedded_test_server.h" @@ -1007,14 +1010,18 @@ // Tests that the scroll position is maintained when switching from the Discover // feed to the Following feed without fully scrolling into the feed. -- (void)testScrollPositionMaintainedWhenSwitchingFeedAboveFeed { +// TODO(crbug.com/1330667): Re-enable when fixed. +- (void)DISABLED_testScrollPositionMaintainedWhenSwitchingFeedAboveFeed { if (![ChromeEarlGrey isWebChannelsEnabled]) { EARL_GREY_TEST_SKIPPED(@"Only applicable with Web Channels enabled."); } + // Sign in to enable Following. + [self signInThroughSettingsMenu]; + // Scrolls down a bit, not fully into the feed. [[EarlGrey selectElementWithMatcher:chrome_test_util::NTPCollectionView()] - performAction:grey_scrollInDirection(kGREYDirectionDown, 150)]; + performAction:grey_scrollInDirection(kGREYDirectionDown, 50)]; // Saves the content offset and switches to the Following feed. UICollectionView* collectionView = [NewTabPageAppInterface collectionView]; @@ -1029,6 +1036,35 @@ @"Content offset is not the same after switching feeds."); } +// Tests that the regular feed header is visible when signed out, and is swapped +// for the Following feed header after signing in. +// TODO(crbug.com/1330667): Re-enable when fixed. +- (void)DISABLED_testFollowingFeedHeaderIsVisibleWhenSignedIn { + if (![ChromeEarlGrey isWebChannelsEnabled]) { + EARL_GREY_TEST_SKIPPED(@"Only applicable with Web Channels enabled."); + } + + // Check that regular feed header is visible when signed out, and not + // Following header. + [[EarlGrey + selectElementWithMatcher:grey_accessibilityID( + kNTPFeedHeaderSegmentedControlIdentifier)] + assertWithMatcher:grey_not(grey_sufficientlyVisible())]; + [[EarlGrey selectElementWithMatcher:chrome_test_util::DiscoverHeaderLabel()] + assertWithMatcher:grey_sufficientlyVisible()]; + + // Sign in to enable Following feed. + [self signInThroughSettingsMenu]; + + // Check that Following header is now visible, and not regular feed header. + [[EarlGrey + selectElementWithMatcher:grey_accessibilityID( + kNTPFeedHeaderSegmentedControlIdentifier)] + assertWithMatcher:grey_sufficientlyVisible()]; + [[EarlGrey selectElementWithMatcher:chrome_test_util::DiscoverHeaderLabel()] + assertWithMatcher:grey_not(grey_sufficientlyVisible())]; +} + #pragma mark - Helpers - (void)addMostVisitedTile { @@ -1164,6 +1200,19 @@ GREYAssertFalse(feed_visible, @"Expect feed to be hidden!"); } +// Opens the settings menu, signs in using a fake identity, and closes the menu. +- (void)signInThroughSettingsMenu { + FakeChromeIdentity* fakeIdentity = [FakeChromeIdentity fakeIdentity1]; + [SigninEarlGrey addFakeIdentity:fakeIdentity]; + [ChromeEarlGreyUI openSettingsMenu]; + [ChromeEarlGreyUI + tapSettingsMenuButton:chrome_test_util::PrimarySignInButton()]; + [SigninEarlGreyUI tapSigninConfirmationDialog]; + [ChromeEarlGrey waitForMatcher:chrome_test_util::SettingsAccountButton()]; + [[EarlGrey selectElementWithMatcher:chrome_test_util::SettingsDoneButton()] + performAction:grey_tap()]; +} + #pragma mark - Matchers // Returns the segment of the feed header with a given title.
diff --git a/ios/chrome/browser/ui/main/BUILD.gn b/ios/chrome/browser/ui/main/BUILD.gn index c5962052..1446f762 100644 --- a/ios/chrome/browser/ui/main/BUILD.gn +++ b/ios/chrome/browser/ui/main/BUILD.gn
@@ -155,6 +155,7 @@ "//ios/chrome/browser/crash_report", "//ios/chrome/browser/crash_report:crash_report_internal", "//ios/chrome/browser/crash_report/breadcrumbs", + "//ios/chrome/browser/default_browser", "//ios/chrome/browser/discover_feed", "//ios/chrome/browser/discover_feed:discover_feed_factory", "//ios/chrome/browser/first_run",
diff --git a/ios/chrome/browser/ui/main/scene_controller.mm b/ios/chrome/browser/ui/main/scene_controller.mm index f2cfcf5..fcc6b9d 100644 --- a/ios/chrome/browser/ui/main/scene_controller.mm +++ b/ios/chrome/browser/ui/main/scene_controller.mm
@@ -51,6 +51,7 @@ #include "ios/chrome/browser/crash_report/crash_keys_helper.h" #include "ios/chrome/browser/crash_report/crash_report_helper.h" #import "ios/chrome/browser/crash_report/crash_restore_helper.h" +#include "ios/chrome/browser/default_browser/promo_source.h" #import "ios/chrome/browser/discover_feed/discover_feed_service.h" #import "ios/chrome/browser/discover_feed/discover_feed_service_factory.h" #import "ios/chrome/browser/first_run/first_run.h" @@ -242,7 +243,7 @@ // switcher dismissal. It can only be YES if the QR Scanner experiment is // enabled. @property(nonatomic, readwrite) - NTPTabOpeningPostOpeningAction NTPActionAfterTabSwitcherDismissal; + TabOpeningPostOpeningAction NTPActionAfterTabSwitcherDismissal; // The main coordinator, lazily created the first time it is accessed. Manages // the main view controller. This property should not be accessed before the @@ -1054,7 +1055,7 @@ - (BOOL)potentiallyInterestedUser { // If skipping first run, not in Safe Mode, no post opening action and the // launch is not after a crash, consider showing the default browser promo. - NTPTabOpeningPostOpeningAction postOpeningAction = + TabOpeningPostOpeningAction postOpeningAction = self.NTPActionAfterTabSwitcherDismissal; if (self.startupParameters) { postOpeningAction = self.startupParameters.postOpeningAction; @@ -1946,13 +1947,16 @@ } - (void)showDefaultBrowserSettingsFromViewController: - (UIViewController*)baseViewController { + (UIViewController*)baseViewController + sourceForUMA: + (DefaultBrowserPromoSource)source { if (!baseViewController) { baseViewController = self.currentInterface.viewController; } if (self.settingsNavigationController) { [self.settingsNavigationController - showDefaultBrowserSettingsFromViewController:baseViewController]; + showDefaultBrowserSettingsFromViewController:baseViewController + sourceForUMA:source]; return; } Browser* browser = self.mainInterface.browser; @@ -2178,31 +2182,24 @@ #pragma mark Tab opening utility methods. - (ProceduralBlock)completionBlockForTriggeringAction: - (NTPTabOpeningPostOpeningAction)action { + (TabOpeningPostOpeningAction)action { + __weak __typeof(self) weakSelf = self; switch (action) { case START_VOICE_SEARCH: return ^{ - [self startVoiceSearchInCurrentBVC]; + [weakSelf startVoiceSearchInCurrentBVC]; }; case START_QR_CODE_SCANNER: return ^{ - if (!self.currentInterface.browser) { - return; - } - id<QRScannerCommands> QRHandler = HandlerForProtocol( - self.currentInterface.browser->GetCommandDispatcher(), - QRScannerCommands); - [QRHandler showQRScanner]; + [weakSelf startQRCodeScanner]; }; case FOCUS_OMNIBOX: return ^{ - if (!self.currentInterface.browser) { - return; - } - id<OmniboxCommands> focusHandler = HandlerForProtocol( - self.currentInterface.browser->GetCommandDispatcher(), - OmniboxCommands); - [focusHandler focusOmnibox]; + [weakSelf focusOmnibox]; + }; + case SHOW_DEFAULT_BROWSER_SETTINGS: + return ^{ + [weakSelf showDefaultBrowserSettings]; }; default: return nil; @@ -2224,6 +2221,37 @@ [self.currentInterface.bvc startVoiceSearch]; } +- (void)startQRCodeScanner { + if (!self.currentInterface.browser) { + return; + } + id<QRScannerCommands> QRHandler = HandlerForProtocol( + self.currentInterface.browser->GetCommandDispatcher(), QRScannerCommands); + [QRHandler showQRScanner]; +} + +- (void)focusOmnibox { + if (!self.currentInterface.browser) { + return; + } + id<OmniboxCommands> omniboxCommandsHandler = HandlerForProtocol( + self.currentInterface.browser->GetCommandDispatcher(), OmniboxCommands); + [omniboxCommandsHandler focusOmnibox]; +} + +- (void)showDefaultBrowserSettings { + if (!self.currentInterface.browser) { + return; + } + id<ApplicationSettingsCommands> applicationSettingsCommandsHandler = + HandlerForProtocol(self.currentInterface.browser->GetCommandDispatcher(), + ApplicationSettingsCommands); + [applicationSettingsCommandsHandler + showDefaultBrowserSettingsFromViewController:nil + sourceForUMA:DefaultBrowserPromoSource:: + kExternalIntent]; +} + #pragma mark - TabSwitching - (BOOL)openNewTabFromTabSwitcher { @@ -2571,8 +2599,6 @@ [self completionBlockForTriggeringAction:[self.startupParameters postOpeningAction]]; - // Commands are only allowed on NTP. - DCHECK(IsURLNtp(urlLoadParams.web_params.url) || !startupCompletion); ProceduralBlock tabOpenedCompletion = nil; if (startupCompletion && completion) { tabOpenedCompletion = ^{
diff --git a/ios/chrome/browser/ui/ntp/new_tab_page_feature.h b/ios/chrome/browser/ui/ntp/new_tab_page_feature.h index a9b970c..8bc0c6ce 100644 --- a/ios/chrome/browser/ui/ntp/new_tab_page_feature.h +++ b/ios/chrome/browser/ui/ntp/new_tab_page_feature.h
@@ -69,4 +69,8 @@ // Whether the Discover feed ablation experiment is enabled. bool IsFeedAblationEnabled(); +// Whether the ghost cards should be shown when refreshing Discover feed +// content. +bool IsDiscoverFeedGhostCardsEnabled(); + #endif // IOS_CHROME_BROWSER_UI_NTP_NEW_TAB_PAGE_FEATURE_H_
diff --git a/ios/chrome/browser/ui/ntp/new_tab_page_feature.mm b/ios/chrome/browser/ui/ntp/new_tab_page_feature.mm index 49845cf..c83976eb 100644 --- a/ios/chrome/browser/ui/ntp/new_tab_page_feature.mm +++ b/ios/chrome/browser/ui/ntp/new_tab_page_feature.mm
@@ -44,6 +44,9 @@ const base::Feature kEnableFeedAblation{"FeedAblationEnabled", base::FEATURE_DISABLED_BY_DEFAULT}; +const base::Feature kDiscoverFeedGhostCardsEnabled{ + "DiscoverFeedGhostCardsEnabled", base::FEATURE_DISABLED_BY_DEFAULT}; + bool IsDiscoverFeedPreviewEnabled() { return base::FeatureList::IsEnabled(kEnableDiscoverFeedPreview); } @@ -67,3 +70,7 @@ bool IsFeedAblationEnabled() { return base::FeatureList::IsEnabled(kEnableFeedAblation); } + +bool IsDiscoverFeedGhostCardsEnabled() { + return base::FeatureList::IsEnabled(kDiscoverFeedGhostCardsEnabled); +}
diff --git a/ios/chrome/browser/ui/omnibox/popup/BUILD.gn b/ios/chrome/browser/ui/omnibox/popup/BUILD.gn index c6a60151..ed2441f 100644 --- a/ios/chrome/browser/ui/omnibox/popup/BUILD.gn +++ b/ios/chrome/browser/ui/omnibox/popup/BUILD.gn
@@ -77,6 +77,7 @@ "//ios/chrome/app/strings", "//ios/chrome/browser", "//ios/chrome/browser/browser_state", + "//ios/chrome/browser/default_browser", "//ios/chrome/browser/favicon", "//ios/chrome/browser/main:public", "//ios/chrome/browser/search_engines",
diff --git a/ios/chrome/browser/ui/omnibox/popup/omnibox_pedal_annotator.mm b/ios/chrome/browser/ui/omnibox/popup/omnibox_pedal_annotator.mm index f9da9e4..f54c5a29 100644 --- a/ios/chrome/browser/ui/omnibox/popup/omnibox_pedal_annotator.mm +++ b/ios/chrome/browser/ui/omnibox/popup/omnibox_pedal_annotator.mm
@@ -10,6 +10,7 @@ #include "components/omnibox/browser/actions/omnibox_pedal_concepts.h" #include "components/omnibox/browser/autocomplete_match.h" #include "ios/chrome/browser/chrome_url_constants.h" +#include "ios/chrome/browser/default_browser/promo_source.h" #import "ios/chrome/browser/ui/commands/application_commands.h" #import "ios/chrome/browser/ui/commands/omnibox_commands.h" #import "ios/chrome/browser/ui/commands/open_new_tab_command.h" @@ -82,6 +83,14 @@ }]; } case OmniboxPedalId::SET_CHROME_AS_DEFAULT_BROWSER: { + ProceduralBlock action = ^{ + [omniboxCommandHandler cancelOmniboxEdit]; + [pedalsEndpoint + showDefaultBrowserSettingsFromViewController:nil + sourceForUMA: + DefaultBrowserPromoSource:: + kOmnibox]; + }; return [[OmniboxPedalData alloc] initWithTitle:hint subtitle:l10n_util::GetNSString( @@ -90,11 +99,7 @@ imageName:@"pedal_default_browser" type:pedalType incognito:incognito - action:^{ - [omniboxCommandHandler cancelOmniboxEdit]; - [pedalsEndpoint - showDefaultBrowserSettingsFromViewController:nil]; - }]; + action:action]; } case OmniboxPedalId::MANAGE_PASSWORDS: { return [[OmniboxPedalData alloc]
diff --git a/ios/chrome/browser/ui/settings/default_browser/BUILD.gn b/ios/chrome/browser/ui/settings/default_browser/BUILD.gn index 79581f95..66d758f8 100644 --- a/ios/chrome/browser/ui/settings/default_browser/BUILD.gn +++ b/ios/chrome/browser/ui/settings/default_browser/BUILD.gn
@@ -12,6 +12,7 @@ "resources:chrome_icon", "resources:default_browser_world", "//ios/chrome/app/strings:ios_strings_grit", + "//ios/chrome/browser/default_browser", "//ios/chrome/browser/ui:feature_flags", "//ios/chrome/browser/ui/settings:constants", "//ios/chrome/browser/ui/settings:settings_root",
diff --git a/ios/chrome/browser/ui/settings/default_browser/default_browser_settings_table_view_controller.h b/ios/chrome/browser/ui/settings/default_browser/default_browser_settings_table_view_controller.h index 7d4eb67..2d413c2 100644 --- a/ios/chrome/browser/ui/settings/default_browser/default_browser_settings_table_view_controller.h +++ b/ios/chrome/browser/ui/settings/default_browser/default_browser_settings_table_view_controller.h
@@ -5,6 +5,7 @@ #ifndef IOS_CHROME_BROWSER_UI_SETTINGS_DEFAULT_BROWSER_DEFAULT_BROWSER_SETTINGS_TABLE_VIEW_CONTROLLER_H_ #define IOS_CHROME_BROWSER_UI_SETTINGS_DEFAULT_BROWSER_DEFAULT_BROWSER_SETTINGS_TABLE_VIEW_CONTROLLER_H_ +#import "ios/chrome/browser/default_browser/promo_source.h" #import "ios/chrome/browser/ui/settings/settings_root_table_view_controller.h" // Controller for the UI that shows the user how to set Chrome as the default @@ -15,6 +16,9 @@ - (instancetype)init NS_DESIGNATED_INITIALIZER; - (instancetype)initWithStyle:(UITableViewStyle)style NS_UNAVAILABLE; +// The feature that triggered this view controller. +@property(nonatomic, assign) DefaultBrowserPromoSource source; + @end #endif // IOS_CHROME_BROWSER_UI_SETTINGS_DEFAULT_BROWSER_DEFAULT_BROWSER_SETTINGS_TABLE_VIEW_CONTROLLER_H_
diff --git a/ios/chrome/browser/ui/settings/default_browser/default_browser_settings_table_view_controller.mm b/ios/chrome/browser/ui/settings/default_browser/default_browser_settings_table_view_controller.mm index fef6c4e..6728e4a5 100644 --- a/ios/chrome/browser/ui/settings/default_browser/default_browser_settings_table_view_controller.mm +++ b/ios/chrome/browser/ui/settings/default_browser/default_browser_settings_table_view_controller.mm
@@ -4,6 +4,7 @@ #import "ios/chrome/browser/ui/settings/default_browser/default_browser_settings_table_view_controller.h" +#include "base/metrics/histogram_functions.h" #include "base/metrics/user_metrics.h" #include "base/metrics/user_metrics_action.h" #import "ios/chrome/browser/ui/settings/settings_table_view_controller_constants.h" @@ -128,6 +129,8 @@ if (itemType == ItemTypeOpenSettingsButton) { base::RecordAction(base::UserMetricsAction("Settings.DefaultBrowser")); + base::UmaHistogramEnumeration("Settings.DefaultBrowserFromSource", + self.source); [[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString]
diff --git a/ios/chrome/browser/ui/settings/settings_navigation_controller.mm b/ios/chrome/browser/ui/settings/settings_navigation_controller.mm index fe1b5e72f..3119ef6 100644 --- a/ios/chrome/browser/ui/settings/settings_navigation_controller.mm +++ b/ios/chrome/browser/ui/settings/settings_navigation_controller.mm
@@ -798,10 +798,13 @@ } - (void)showDefaultBrowserSettingsFromViewController: - (UIViewController*)baseViewController { + (UIViewController*)baseViewController + sourceForUMA: + (DefaultBrowserPromoSource)source { DefaultBrowserSettingsTableViewController* controller = [[DefaultBrowserSettingsTableViewController alloc] init]; controller.dispatcher = [self.settingsNavigationDelegate handlerForSettings]; + controller.source = source; [self pushViewController:controller animated:YES]; }
diff --git a/ios/chrome/browser/ui/ui_feature_flags.cc b/ios/chrome/browser/ui/ui_feature_flags.cc index ba210c1..6a0df4bc 100644 --- a/ios/chrome/browser/ui/ui_feature_flags.cc +++ b/ios/chrome/browser/ui/ui_feature_flags.cc
@@ -37,6 +37,9 @@ "DefaultBrowserFullscreenPromoExperiment", base::FEATURE_DISABLED_BY_DEFAULT}; +const base::Feature kDefaultBrowserIntentsShowSettings{ + "DefaultBrowserIntentsShowSettings", base::FEATURE_DISABLED_BY_DEFAULT}; + const base::Feature kIOSNewOmniboxImplementation{ "kIOSNewOmniboxImplementation", base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/ios/chrome/browser/ui/ui_feature_flags.h b/ios/chrome/browser/ui/ui_feature_flags.h index dfad6743..d57701f 100644 --- a/ios/chrome/browser/ui/ui_feature_flags.h +++ b/ios/chrome/browser/ui/ui_feature_flags.h
@@ -43,6 +43,9 @@ // Feature flag that experiments with the default browser fullscreen promo UI. extern const base::Feature kDefaultBrowserFullscreenPromoExperiment; +// Feature flag that allows external apps to show default browser settings. +extern const base::Feature kDefaultBrowserIntentsShowSettings; + // Feature flag that shows iOS 15 context menu, instead of tooltip popover, // during a location bar long press gesture. extern const base::Feature kIOSLocationBarUseNativeContextMenu;
diff --git a/ios/chrome/common/app_group/app_group_constants.h b/ios/chrome/common/app_group/app_group_constants.h index 13abbf8..9afa116 100644 --- a/ios/chrome/common/app_group/app_group_constants.h +++ b/ios/chrome/common/app_group/app_group_constants.h
@@ -27,6 +27,14 @@ OPEN_IN_CHROME_ITEM }; +// The key of a preference containing a dictionary of capabilities supported by +// the current version of Chrome. +extern NSString* const kChromeCapabilitiesPreference; + +// ---- Chrome capabilities ----- +// Show default browser promo capability. +extern NSString* const kChromeShowDefaultBrowserPromoCapability; + // The x-callback-url indicating that an application in the group requires a // command. extern const char kChromeAppGroupXCallbackCommand[]; @@ -158,10 +166,17 @@ NSURL* CrashpadFolder(); // Returns an autoreleased pointer to the shared user defaults if an -// application group is defined. If not (i.e. on simulator, or if entitlements -// do not allow it) returns [NSUserDefaults standardUserDefaults]. +// application group is defined for the application and its extensions. +// If not (i.e. on simulator, or if entitlements do not allow it) returns +// [NSUserDefaults standardUserDefaults]. NSUserDefaults* GetGroupUserDefaults(); +// Returns an autoreleased pointer to the shared user defaults if a group is +// defined for the application and other application of the same developer. If +// not (i.e. on simulator, or if entitlements do not allow it) returns +// [NSUserDefaults standardUserDefaults]. +NSUserDefaults* GetCommonGroupUserDefaults(); + // The application name of |application|. NSString* ApplicationName(AppGroupApplications application);
diff --git a/ios/chrome/common/app_group/app_group_constants.mm b/ios/chrome/common/app_group/app_group_constants.mm index 1578a65..ec1c3db5 100644 --- a/ios/chrome/common/app_group/app_group_constants.mm +++ b/ios/chrome/common/app_group/app_group_constants.mm
@@ -15,6 +15,11 @@ namespace app_group { +extern NSString* const kChromeCapabilitiesPreference = @"Chrome.Capabilities"; + +extern NSString* const kChromeShowDefaultBrowserPromoCapability = + @"ShowDefaultBrowserPromo"; + const char kChromeAppGroupXCallbackCommand[] = "app-group-command"; NSString* const kChromeExtensionFieldTrialPreference = @"Extension.FieldTrial"; @@ -93,11 +98,26 @@ } } +NSUserDefaults* GetCommonGroupUserDefaults() { + NSString* applicationGroup = CommonApplicationGroup(); + if (applicationGroup) { + NSUserDefaults* defaults = + [[NSUserDefaults alloc] initWithSuiteName:applicationGroup]; + if (defaults) + return defaults; + } + + // On a device, the entitlements should always provide an application group to + // the application. This is not the case on simulator. + DCHECK(TARGET_IPHONE_SIMULATOR); + return [NSUserDefaults standardUserDefaults]; +} + NSUserDefaults* GetGroupUserDefaults() { - NSUserDefaults* defaults = nil; NSString* applicationGroup = ApplicationGroup(); if (applicationGroup) { - defaults = [[NSUserDefaults alloc] initWithSuiteName:applicationGroup]; + NSUserDefaults* defaults = + [[NSUserDefaults alloc] initWithSuiteName:applicationGroup]; if (defaults) return defaults; }
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1 index 4b517bb0..a3777c0d 100644 --- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1 +++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@ -100565ad0afa6954a255575b57d3b8a9d0993461 \ No newline at end of file +68fab8fc9226e6579db684a3d14acf5b654a5478 \ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1 index fcea66c..adb6b6c 100644 --- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1 +++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@ -27684947d3a44af681a8f9ad03c144fe4417513c \ No newline at end of file +caa462f6f432c8b13d484eb49200b058d4a4256f \ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1 index 99e717a..30fc99d 100644 --- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1 +++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@ -effbc01232f9b43bac8840fb8a8cbed9be70c3b4 \ No newline at end of file +450904f0a8c51deced1f8cb664b4795b38db0de3 \ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1 index 3b34c58..acda0ca 100644 --- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1 +++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@ -c4fd94c85adcec24c26946af5568f34e320bf90e \ No newline at end of file +ac2d6010c8b2edbd6a804e23d12f3cd3f50fd29b \ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.ios.zip.sha1 index 97f8815..1dc503b 100644 --- a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.ios.zip.sha1 +++ b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@ -72e3edc89c5cf42b7c25c665606e6de4e9cc268b \ No newline at end of file +f7c52d2ffe76ea884072b0720a9ed097f5be7d81 \ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.iossimulator.zip.sha1 index 7ae97791..3e11286 100644 --- a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.iossimulator.zip.sha1 +++ b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@ -fcf0298443aa781e6b0e06ab7639ba318ffa7b6f \ No newline at end of file +b8846835ac42e18f5a4f2104c8ce73319485d832 \ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1 index 1da40d4..72f5f68 100644 --- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1 +++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@ -02b9c1b4b8e0dad8cf24f3396ee6195b06c991da \ No newline at end of file +3eac3899dca38a3a6ba86904c2c969011deff95e \ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1 index b62792d..d3dbffb3 100644 --- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1 +++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@ -8f4ebce5d7eb22ddfbc116fdcb1d850e501163b0 \ No newline at end of file +5b858c77c8be47ad40261ffc192b7f1a70fa0c03 \ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1 index e20f2837..e7584bd 100644 --- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1 +++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@ -42611eda077089a275437ead73e944b6d8aa25f3 \ No newline at end of file +7c31dba391f1af7bfb2a9b53f658a128236e28d4 \ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1 index b5ce33e..7da47043 100644 --- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1 +++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@ -14e7d13c9a275285dc4fd830017efce5b7538096 \ No newline at end of file +a19aa3467f108a49c9e99943b027dde6049bbe5c \ No newline at end of file
diff --git a/media/base/media_switches.cc b/media/base/media_switches.cc index 58c2ca6..3071f55 100644 --- a/media/base/media_switches.cc +++ b/media/base/media_switches.cc
@@ -399,6 +399,11 @@ #endif }; +// Controls whether the Open Screen libcast SenderSession is used for +// initializing and managing streaming sessions, or the legacy implementation. +const base::Feature kOpenscreenCastStreamingSession{ + "OpenscreenCastStreamingSession", base::FEATURE_DISABLED_BY_DEFAULT}; + // Approach original pre-REC MSE object URL autorevoking behavior, though await // actual attempt to use the object URL for attachment to perform revocation. // This will hopefully reduce runtime memory bloat for pages that do not
diff --git a/media/base/media_switches.h b/media/base/media_switches.h index 3b969a3..ea1d3b74 100644 --- a/media/base/media_switches.h +++ b/media/base/media_switches.h
@@ -176,6 +176,7 @@ MEDIA_EXPORT extern const base::Feature kMediaSessionWebRTC; MEDIA_EXPORT extern const base::Feature kMemoryPressureBasedSourceBufferGC; MEDIA_EXPORT extern const base::Feature kMultiPlaneVideoCaptureSharedImages; +MEDIA_EXPORT extern const base::Feature kOpenscreenCastStreamingSession; MEDIA_EXPORT extern const base::Feature kOverlayFullscreenVideo; MEDIA_EXPORT extern const base::Feature kPictureInPicture; MEDIA_EXPORT extern const base::Feature kPlatformAudioEncoder;
diff --git a/media/capture/video/mac/video_capture_device_avfoundation_mac.mm b/media/capture/video/mac/video_capture_device_avfoundation_mac.mm index 793e21f2..642a46d 100644 --- a/media/capture/video/mac/video_capture_device_avfoundation_mac.mm +++ b/media/capture/video/mac/video_capture_device_avfoundation_mac.mm
@@ -415,16 +415,15 @@ selector:@selector(onVideoError:) name:AVCaptureSessionRuntimeErrorNotification object:_captureSession]; + [_captureSession startRunning]; - // The configuration lock must be held when calling startRunning to ensure - // `_bestCaptureFormat` is used. Calling setActiveFormat after startRunning - // will incur a double configuration cost. - if (_bestCaptureFormat && [_captureDevice lockForConfiguration:nil]) { - [_captureDevice setActiveFormat:_bestCaptureFormat]; - [_captureSession startRunning]; - [_captureDevice unlockForConfiguration]; - } else { - [_captureSession startRunning]; + // Update the active capture format once the capture session is running. + // Setting it before the capture session is running has no effect. + if (_bestCaptureFormat) { + if ([_captureDevice lockForConfiguration:nil]) { + [_captureDevice setActiveFormat:_bestCaptureFormat]; + [_captureDevice unlockForConfiguration]; + } } {
diff --git a/media/cast/BUILD.gn b/media/cast/BUILD.gn index 6335908..7298d8b 100644 --- a/media/cast/BUILD.gn +++ b/media/cast/BUILD.gn
@@ -185,6 +185,8 @@ } } +# TODO(https://crbug.com/1327074): should be split into multiple source sets +# once the new Open Screen frame sender implementation is added. source_set("sender") { sources = [ "cast_sender.h", @@ -196,6 +198,8 @@ "sender/congestion_control.h", "sender/frame_sender.cc", "sender/frame_sender.h", + "sender/frame_sender_impl.cc", + "sender/frame_sender_impl.h", "sender/performance_metrics_overlay.cc", "sender/performance_metrics_overlay.h", "sender/video_sender.cc",
diff --git a/media/cast/common/sender_encoded_frame.cc b/media/cast/common/sender_encoded_frame.cc index 6d0d2863..2828f7d5 100644 --- a/media/cast/common/sender_encoded_frame.cc +++ b/media/cast/common/sender_encoded_frame.cc
@@ -7,9 +7,7 @@ namespace media { namespace cast { -SenderEncodedFrame::SenderEncodedFrame() - : EncodedFrame(), encoder_utilization(-1.0), lossy_utilization(-1.0) {} - +SenderEncodedFrame::SenderEncodedFrame() = default; SenderEncodedFrame::~SenderEncodedFrame() = default; } // namespace cast
diff --git a/media/cast/common/sender_encoded_frame.h b/media/cast/common/sender_encoded_frame.h index 7e7f8d82..2f859bd 100644 --- a/media/cast/common/sender_encoded_frame.h +++ b/media/cast/common/sender_encoded_frame.h
@@ -26,7 +26,10 @@ // indicating the encoder utilized more resources than a maximum sustainable // rate, based on the data volume of the input. Negative values indicate the // field was not computed. - double encoder_utilization; + double encoder_utilization = -1.0; + + // The bitrate the encoder used for encoding this frame. + int encoder_bitrate = 0; // The amount of "lossiness" needed to encode the frame within the targeted // bandwidth. More-complex frame content and/or lower target encode bitrates @@ -38,9 +41,7 @@ // very small, and values greater than 1.0 indicating the encoder cannot // encode the frame within the target bitrate (even at its lowest quality // setting). Negative values indicate the field was not computed. - // - // TODO(jophba): Rename to idealized_bitrate_utilization. - double lossy_utilization = {}; + double lossiness = -1.0; // The time at which the encode of the frame completed. base::TimeTicks encode_completion_time;
diff --git a/media/cast/encoding/audio_encoder.cc b/media/cast/encoding/audio_encoder.cc index 7cfac59..2ecf019 100644 --- a/media/cast/encoding/audio_encoder.cc +++ b/media/cast/encoding/audio_encoder.cc
@@ -57,11 +57,13 @@ int num_channels, int sampling_rate, int samples_per_frame, + int bitrate, FrameEncodedCallback callback) : cast_environment_(cast_environment), codec_(codec), num_channels_(num_channels), samples_per_frame_(samples_per_frame), + bitrate_(bitrate), callback_(std::move(callback)), operational_status_(STATUS_UNINITIALIZED), frame_duration_(base::Seconds(static_cast<double>(samples_per_frame_) / @@ -156,7 +158,7 @@ // by the signal duration. audio_frame->encoder_utilization = (base::TimeTicks::Now() - start_time) / frame_duration_; - + audio_frame->encoder_bitrate = bitrate_; TRACE_EVENT_NESTABLE_ASYNC_END1( "cast.stream", "Audio Encode", TRACE_ID_LOCAL(audio_frame.get()), "encoder_utilization", audio_frame->encoder_utilization); @@ -192,6 +194,7 @@ const Codec codec_; const int num_channels_; const int samples_per_frame_; + const int bitrate_; const FrameEncodedCallback callback_; // Subclass' ctor is expected to set this to STATUS_INITIALIZED. @@ -241,6 +244,7 @@ num_channels, sampling_rate, sampling_rate / kDefaultFramesPerSecond, /* 10 ms frames */ + bitrate, std::move(callback)), encoder_memory_(new uint8_t[opus_encoder_get_size(num_channels)]), opus_encoder_(reinterpret_cast<OpusEncoder*>(encoder_memory_.get())), @@ -347,6 +351,7 @@ num_channels, sampling_rate, kAccessUnitSamples, + bitrate, std::move(callback)), input_buffer_(AudioBus::Create(num_channels, kAccessUnitSamples)), input_bus_(AudioBus::CreateWrapper(num_channels)), @@ -540,7 +545,7 @@ // Reset the buffer size field to the buffer capacity. converter_abl_.mBuffers[0].mDataByteSize = max_access_unit_size_; - // Encode the current input buffer. This is a sychronous call. + // Encode the current input buffer. This is a synchronous call. OSStatus oserr; UInt32 io_num_packets = 1; AudioStreamPacketDescription packet_description; @@ -692,6 +697,7 @@ num_channels, sampling_rate, sampling_rate / kDefaultFramesPerSecond, /* 10 ms frames */ + 0 /* bitrate, which is unused for the PCM16 implementation */, std::move(callback)), buffer_(new int16_t[num_channels * samples_per_frame_]) { if (ImplBase::operational_status_ != STATUS_UNINITIALIZED)
diff --git a/media/cast/encoding/audio_encoder_unittest.cc b/media/cast/encoding/audio_encoder_unittest.cc index d8972a8..3280266 100644 --- a/media/cast/encoding/audio_encoder_unittest.cc +++ b/media/cast/encoding/audio_encoder_unittest.cc
@@ -73,7 +73,7 @@ EXPECT_GT(upper_bound_, encoded_frame->reference_time); EXPECT_LE(0.0, encoded_frame->encoder_utilization); - EXPECT_EQ(-1.0, encoded_frame->lossy_utilization); + EXPECT_EQ(-1.0, encoded_frame->lossiness); ++frames_received_; }
diff --git a/media/cast/encoding/av1_encoder.cc b/media/cast/encoding/av1_encoder.cc index d0ff926..2d80c5c 100644 --- a/media/cast/encoding/av1_encoder.cc +++ b/media/cast/encoding/av1_encoder.cc
@@ -269,7 +269,6 @@ const base::TimeDelta processing_time = base::TimeTicks::Now() - start_time; encoded_frame->encoder_utilization = processing_time / predicted_frame_duration; - // Compute lossy utilization. The AV1 encoder took an estimated guess at what // quantizer value would produce an encoded frame size as close to the target // as possible. Now that the frame has been encoded and the number of bytes @@ -278,6 +277,7 @@ // used as the lossy utilization. const double actual_bitrate = encoded_frame->data.size() * 8.0 / predicted_frame_duration.InSecondsF(); + encoded_frame->encoder_bitrate = actual_bitrate; const double target_bitrate = 1000.0 * config_.rc_target_bitrate; DCHECK_GT(target_bitrate, 0.0); const double bitrate_utilization = actual_bitrate / target_bitrate; @@ -288,12 +288,12 @@ // Side note: If it was possible for the encoder to encode within the target // number of bytes, the |perfect_quantizer| will be in the range [0.0,63.0]. // If it was never possible, the value will be greater than 63.0. - encoded_frame->lossy_utilization = perfect_quantizer / 63.0; + encoded_frame->lossiness = perfect_quantizer / 63.0; DVLOG(2) << "AV1 encoded frame_id " << encoded_frame->frame_id << ", sized: " << encoded_frame->data.size() << ", encoder_utilization: " << encoded_frame->encoder_utilization - << ", lossy_utilization: " << encoded_frame->lossy_utilization + << ", lossiness: " << encoded_frame->lossiness << " (quantizer chosen by the encoder was " << quantizer << ')'; if (encoded_frame->dependency == EncodedFrame::KEY) {
diff --git a/media/cast/encoding/external_video_encoder.cc b/media/cast/encoding/external_video_encoder.cc index b7d23aa..c0172b4 100644 --- a/media/cast/encoding/external_video_encoder.cc +++ b/media/cast/encoding/external_video_encoder.cc
@@ -462,11 +462,12 @@ static_cast<double>(in_progress_frame_encodes_.size()) / kBacklogRedlineThreshold; - const double actual_bit_rate = + const double actual_bitrate = encoded_frame->data.size() * 8.0 / frame_duration.InSecondsF(); + encoded_frame->encoder_bitrate = actual_bitrate; DCHECK_GT(request.target_bit_rate, 0); const double bitrate_utilization = - actual_bit_rate / request.target_bit_rate; + actual_bitrate / request.target_bit_rate; double quantizer = QuantizerEstimator::NO_RESULT; // If the quantizer can be parsed from the key frame, try to parse // the following delta frames as well. @@ -507,7 +508,7 @@ codec_profile_ == media::VP8PROFILE_ANY ? static_cast<int>(QuantizerEstimator::MAX_VP8_QUANTIZER) : static_cast<int>(kMaxH264Quantizer); - encoded_frame->lossy_utilization = + encoded_frame->lossiness = bitrate_utilization * (quantizer / max_quantizer); } } else {
diff --git a/media/cast/encoding/fake_software_video_encoder.cc b/media/cast/encoding/fake_software_video_encoder.cc index 4b5a5f6f..b300e96 100644 --- a/media/cast/encoding/fake_software_video_encoder.cc +++ b/media/cast/encoding/fake_software_video_encoder.cc
@@ -66,10 +66,10 @@ if (encoded_frame->dependency == EncodedFrame::KEY) { encoded_frame->encoder_utilization = 1.0; - encoded_frame->lossy_utilization = 6.0; + encoded_frame->lossiness = 6.0; } else { encoded_frame->encoder_utilization = 0.8; - encoded_frame->lossy_utilization = 0.8; + encoded_frame->lossiness = 0.8; } }
diff --git a/media/cast/encoding/h264_vt_encoder.cc b/media/cast/encoding/h264_vt_encoder.cc index 9290f35f..67177fc 100644 --- a/media/cast/encoding/h264_vt_encoder.cc +++ b/media/cast/encoding/h264_vt_encoder.cc
@@ -161,6 +161,8 @@ StatusChangeCallback status_change_cb) : cast_environment_(cast_environment), video_config_(video_config), + average_bitrate_((video_config_.min_bitrate + video_config_.max_bitrate) / + 2), status_change_cb_(std::move(status_change_cb)), next_frame_id_(FrameId::first()), encode_next_frame_as_keyframe_(false), @@ -300,11 +302,8 @@ 240); session_property_setter.Set( kVTCompressionPropertyKey_MaxKeyFrameIntervalDuration, 240); - // TODO(jfroy): implement better bitrate control - // https://crbug.com/425352 - session_property_setter.Set( - kVTCompressionPropertyKey_AverageBitRate, - (video_config_.min_bitrate + video_config_.max_bitrate) / 2); + session_property_setter.Set(kVTCompressionPropertyKey_AverageBitRate, + average_bitrate_); session_property_setter.Set( kVTCompressionPropertyKey_ExpectedFrameRate, static_cast<int>(video_config_.max_frame_rate + 0.5)); @@ -500,7 +499,7 @@ auto* encoder = reinterpret_cast<H264VideoToolboxEncoder*>(encoder_opaque); std::unique_ptr<InProgressH264VTFrameEncode> request( reinterpret_cast<InProgressH264VTFrameEncode*>(request_opaque)); - bool keyframe = false; + bool is_keyframe = false; bool has_frame_data = false; if (status != noErr) { @@ -518,8 +517,8 @@ // If the NotSync key is not present, it implies Sync, which indicates a // keyframe (at least I think, VT documentation is, erm, sparse). Could // alternatively use kCMSampleAttachmentKey_DependsOnOthers == false. - keyframe = !CFDictionaryContainsKey(sample_attachments, - kCMSampleAttachmentKey_NotSync); + is_keyframe = !CFDictionaryContainsKey(sample_attachments, + kCMSampleAttachmentKey_NotSync); has_frame_data = true; } @@ -531,7 +530,7 @@ encoded_frame->frame_id = frame_id; encoded_frame->reference_time = request->reference_time; encoded_frame->rtp_timestamp = request->rtp_timestamp; - if (keyframe) { + if (is_keyframe) { encoded_frame->dependency = EncodedFrame::KEY; encoded_frame->referenced_frame_id = frame_id; } else { @@ -549,12 +548,13 @@ } if (has_frame_data) { - video_toolbox::CopySampleBufferToAnnexBBuffer(sbuf, keyframe, + video_toolbox::CopySampleBufferToAnnexBBuffer(sbuf, is_keyframe, &encoded_frame->data); } encoded_frame->encode_completion_time = encoder->cast_environment_->Clock()->NowTicks(); + encoded_frame->encoder_bitrate = encoder->average_bitrate_; encoder->cast_environment_->GetTaskRunner(CastEnvironment::MAIN) ->PostTask(FROM_HERE, base::BindOnce(std::move(request->frame_encoded_callback),
diff --git a/media/cast/encoding/h264_vt_encoder.h b/media/cast/encoding/h264_vt_encoder.h index ee448e8..aa05756 100644 --- a/media/cast/encoding/h264_vt_encoder.h +++ b/media/cast/encoding/h264_vt_encoder.h
@@ -88,6 +88,10 @@ // invalidate compression sessions. const FrameSenderConfig video_config_; + // The VideoToolbox does not support bitrate configuration, so we use a + // constant bitrate determined on construction. + const int average_bitrate_; + // Frame size of the current compression session. Can be changed by submitting // a frame of a different size, which will cause a compression session reset. gfx::Size frame_size_;
diff --git a/media/cast/encoding/video_encoder_unittest.cc b/media/cast/encoding/video_encoder_unittest.cc index 2253b7b2..d30944d4 100644 --- a/media/cast/encoding/video_encoder_unittest.cc +++ b/media/cast/encoding/video_encoder_unittest.cc
@@ -313,8 +313,9 @@ if (is_testing_software_vp8_encoder()) { ASSERT_TRUE(std::isfinite(encoded_frame->encoder_utilization)); EXPECT_LE(0.0, encoded_frame->encoder_utilization); - ASSERT_TRUE(std::isfinite(encoded_frame->lossy_utilization)); - EXPECT_LE(0.0, encoded_frame->lossy_utilization); + EXPECT_LE(0, encoded_frame->encoder_bitrate); + ASSERT_TRUE(std::isfinite(encoded_frame->lossiness)); + EXPECT_LE(0.0, encoded_frame->lossiness); } } }
diff --git a/media/cast/encoding/vpx_encoder.cc b/media/cast/encoding/vpx_encoder.cc index d0d64bd..a03015c 100644 --- a/media/cast/encoding/vpx_encoder.cc +++ b/media/cast/encoding/vpx_encoder.cc
@@ -318,6 +318,7 @@ // used as the lossy utilization. const double actual_bitrate = encoded_frame->data.size() * 8.0 / predicted_frame_duration.InSecondsF(); + encoded_frame->encoder_bitrate = actual_bitrate; const double target_bitrate = 1000.0 * config_.rc_target_bitrate; DCHECK_GT(target_bitrate, 0.0); const double bitrate_utilization = actual_bitrate / target_bitrate; @@ -328,12 +329,12 @@ // Side note: If it was possible for the encoder to encode within the target // number of bytes, the |perfect_quantizer| will be in the range [0.0,63.0]. // If it was never possible, the value will be greater than 63.0. - encoded_frame->lossy_utilization = perfect_quantizer / 63.0; + encoded_frame->lossiness = perfect_quantizer / 63.0; DVLOG(2) << "VPX encoded frame_id " << encoded_frame->frame_id << ", sized: " << encoded_frame->data.size() << ", encoder_utilization: " << encoded_frame->encoder_utilization - << ", lossy_utilization: " << encoded_frame->lossy_utilization + << ", lossiness: " << encoded_frame->lossiness << " (quantizer chosen by the encoder was " << quantizer << ')'; if (encoded_frame->dependency == EncodedFrame::KEY) {
diff --git a/media/cast/sender/audio_sender.cc b/media/cast/sender/audio_sender.cc index 56e95b5..27bea44 100644 --- a/media/cast/sender/audio_sender.cc +++ b/media/cast/sender/audio_sender.cc
@@ -14,24 +14,23 @@ #include "media/cast/encoding/audio_encoder.h" #include "media/cast/net/cast_transport_config.h" -namespace media { -namespace cast { +namespace media::cast { AudioSender::AudioSender(scoped_refptr<CastEnvironment> cast_environment, const FrameSenderConfig& audio_config, StatusChangeOnceCallback status_change_cb, CastTransport* const transport_sender) - : FrameSender(cast_environment, - transport_sender, - audio_config, - NewFixedCongestionControl(audio_config.max_bitrate)), - samples_in_encoder_(0) { + : cast_environment_(cast_environment), + rtp_timebase_(audio_config.rtp_timebase), + frame_sender_(FrameSender::Create(cast_environment, + audio_config, + transport_sender, + this)) { if (!audio_config.use_external_encoder) { audio_encoder_ = std::make_unique<AudioEncoder>( - cast_environment, audio_config.channels, audio_config.rtp_timebase, + std::move(cast_environment), audio_config.channels, rtp_timebase_, audio_config.max_bitrate, audio_config.codec, - base::BindRepeating(&AudioSender::OnEncodedAudioFrame, AsWeakPtr(), - audio_config.max_bitrate)); + base::BindRepeating(&AudioSender::OnEncodedAudioFrame, AsWeakPtr())); } // AudioEncoder provides no operational status changes during normal use. @@ -46,8 +45,8 @@ // The number of samples per encoded audio frame depends on the codec and its // initialization parameters. Now that we have an encoder, we can calculate // the maximum frame rate. - max_frame_rate_ = - audio_config.rtp_timebase / audio_encoder_->GetSamplesPerFrame(); + frame_sender_->SetMaxFrameRate(rtp_timebase_ / + audio_encoder_->GetSamplesPerFrame()); } AudioSender::~AudioSender() = default; @@ -62,8 +61,8 @@ } const base::TimeDelta next_frame_duration = - RtpTimeDelta::FromTicks(audio_bus->frames()).ToTimeDelta(rtp_timebase()); - if (ShouldDropNextFrame(next_frame_duration)) + RtpTimeDelta::FromTicks(audio_bus->frames()).ToTimeDelta(rtp_timebase_); + if (frame_sender_->ShouldDropNextFrame(next_frame_duration)) return; samples_in_encoder_ += audio_bus->frames(); @@ -71,33 +70,38 @@ audio_encoder_->InsertAudio(std::move(audio_bus), recorded_time); } +void AudioSender::SetTargetPlayoutDelay( + base::TimeDelta new_target_playout_delay) { + frame_sender_->SetTargetPlayoutDelay(new_target_playout_delay); +} + +base::TimeDelta AudioSender::GetTargetPlayoutDelay() const { + return frame_sender_->GetTargetPlayoutDelay(); +} + base::WeakPtr<AudioSender> AudioSender::AsWeakPtr() { return weak_factory_.GetWeakPtr(); } int AudioSender::GetNumberOfFramesInEncoder() const { // Note: It's possible for a partial frame to be in the encoder, but returning - // the floor() is good enough for the "design limit" check in FrameSender. + // the floor() is good enough for the "design limit" check in FrameSenderImpl. return samples_in_encoder_ / audio_encoder_->GetSamplesPerFrame(); } -base::TimeDelta AudioSender::GetInFlightMediaDuration() const { - const int samples_in_flight = samples_in_encoder_ + - GetUnacknowledgedFrameCount() * audio_encoder_->GetSamplesPerFrame(); - return RtpTimeDelta::FromTicks(samples_in_flight).ToTimeDelta(rtp_timebase()); +base::TimeDelta AudioSender::GetEncoderBacklogDuration() const { + return RtpTimeDelta::FromTicks(samples_in_encoder_) + .ToTimeDelta(rtp_timebase_); } void AudioSender::OnEncodedAudioFrame( - int encoder_bitrate, std::unique_ptr<SenderEncodedFrame> encoded_frame, int samples_skipped) { DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); samples_in_encoder_ -= audio_encoder_->GetSamplesPerFrame() + samples_skipped; DCHECK_GE(samples_in_encoder_, 0); - - SendEncodedFrame(encoder_bitrate, std::move(encoded_frame)); + frame_sender_->EnqueueFrame(std::move(encoded_frame)); } -} // namespace cast -} // namespace media +} // namespace media::cast
diff --git a/media/cast/sender/audio_sender.h b/media/cast/sender/audio_sender.h index 19cfbf8a..e3f3fd4b 100644 --- a/media/cast/sender/audio_sender.h +++ b/media/cast/sender/audio_sender.h
@@ -17,8 +17,7 @@ #include "media/cast/cast_sender.h" #include "media/cast/sender/frame_sender.h" -namespace media { -namespace cast { +namespace media::cast { class AudioEncoder; @@ -28,7 +27,7 @@ // RTCP packets. // Additionally it posts a bunch of delayed tasks to the main thread for various // timeouts. -class AudioSender final : public FrameSender { +class AudioSender final : public FrameSender::Client { public: AudioSender(scoped_refptr<CastEnvironment> cast_environment, const FrameSenderConfig& audio_config, @@ -46,29 +45,39 @@ void InsertAudio(std::unique_ptr<AudioBus> audio_bus, const base::TimeTicks& recorded_time); + void SetTargetPlayoutDelay(base::TimeDelta new_target_playout_delay); + base::TimeDelta GetTargetPlayoutDelay() const; + base::WeakPtr<AudioSender> AsWeakPtr(); protected: + // FrameSender::Client overrides. int GetNumberOfFramesInEncoder() const final; - base::TimeDelta GetInFlightMediaDuration() const final; + base::TimeDelta GetEncoderBacklogDuration() const final; private: // Called by the |audio_encoder_| with the next EncodedFrame to send. - void OnEncodedAudioFrame(int encoder_bitrate, - std::unique_ptr<SenderEncodedFrame> encoded_frame, + void OnEncodedAudioFrame(std::unique_ptr<SenderEncodedFrame> encoded_frame, int samples_skipped); + scoped_refptr<CastEnvironment> cast_environment_; + + // The number of RTP units advanced per second; + const int rtp_timebase_; + + // The backing frame sender implementation. + std::unique_ptr<FrameSender> frame_sender_; + // Encodes AudioBuses into EncodedFrames. std::unique_ptr<AudioEncoder> audio_encoder_; // The number of audio samples enqueued in |audio_encoder_|. - int samples_in_encoder_; + int samples_in_encoder_ = 0; // NOTE: Weak pointers must be invalidated before all other member variables. base::WeakPtrFactory<AudioSender> weak_factory_{this}; }; -} // namespace cast -} // namespace media +} // namespace media::cast #endif // MEDIA_CAST_SENDER_AUDIO_SENDER_H_
diff --git a/media/cast/sender/audio_sender_unittest.cc b/media/cast/sender/audio_sender_unittest.cc index 27bc067..b7bd01a 100644 --- a/media/cast/sender/audio_sender_unittest.cc +++ b/media/cast/sender/audio_sender_unittest.cc
@@ -26,8 +26,7 @@ #include "media/cast/test/utility/audio_utility.h" #include "testing/gtest/include/gtest/gtest.h" -namespace media { -namespace cast { +namespace media::cast { namespace { @@ -164,5 +163,4 @@ EXPECT_LE(1, transport_->number_of_rtcp_packets()); } -} // namespace cast -} // namespace media +} // namespace media::cast
diff --git a/media/cast/sender/frame_sender.cc b/media/cast/sender/frame_sender.cc index c3403ef5..e407977 100644 --- a/media/cast/sender/frame_sender.cc +++ b/media/cast/sender/frame_sender.cc
@@ -1,481 +1,14 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "media/cast/sender/frame_sender.h" +#include "base/feature_list.h" +#include "media/base/media_switches.h" -#include <algorithm> -#include <limits> -#include <memory> -#include <utility> -#include <vector> +namespace media::cast { -#include "base/bind.h" -#include "base/logging.h" -#include "base/numerics/safe_conversions.h" -#include "base/trace_event/trace_event.h" -#include "media/cast/common/sender_encoded_frame.h" -#include "media/cast/constants.h" - -namespace media { -namespace cast { -namespace { - -constexpr int kNumAggressiveReportsSentAtStart = 100; -constexpr base::TimeDelta kMinSchedulingDelay = base::Milliseconds(1); -constexpr base::TimeDelta kReceiverProcessTime = base::Milliseconds(250); - -// The additional number of frames that can be in-flight when input exceeds the -// maximum frame rate. -constexpr int kMaxFrameBurst = 5; - -} // namespace - -// Convenience macro used in logging statements throughout this file. -#define SENDER_SSRC (is_audio_ ? "AUDIO[" : "VIDEO[") << ssrc_ << "] " - -FrameSender::RtcpClient::RtcpClient(base::WeakPtr<FrameSender> frame_sender) - : frame_sender_(frame_sender) {} - -FrameSender::RtcpClient::~RtcpClient() = default; - -void FrameSender::RtcpClient::OnReceivedCastMessage( - const RtcpCastMessage& cast_message) { - if (frame_sender_) - frame_sender_->OnReceivedCastFeedback(cast_message); -} - -void FrameSender::RtcpClient::OnReceivedRtt(base::TimeDelta round_trip_time) { - if (frame_sender_) - frame_sender_->OnMeasuredRoundTripTime(round_trip_time); -} - -void FrameSender::RtcpClient::OnReceivedPli() { - if (frame_sender_) - frame_sender_->OnReceivedPli(); -} - -FrameSender::FrameSender(scoped_refptr<CastEnvironment> cast_environment, - CastTransport* const transport_sender, - const FrameSenderConfig& config, - CongestionControl* congestion_control) - : cast_environment_(cast_environment), - transport_sender_(transport_sender), - ssrc_(config.sender_ssrc), - min_playout_delay_(config.min_playout_delay.is_zero() - ? config.max_playout_delay - : config.min_playout_delay), - max_playout_delay_(config.max_playout_delay), - animated_playout_delay_(config.animated_playout_delay.is_zero() - ? config.max_playout_delay - : config.animated_playout_delay), - send_target_playout_delay_(false), - max_frame_rate_(config.max_frame_rate), - num_aggressive_rtcp_reports_sent_(0), - duplicate_ack_counter_(0), - congestion_control_(congestion_control), - picture_lost_at_receiver_(false), - rtp_timebase_(config.rtp_timebase), - is_audio_(config.rtp_payload_type <= RtpPayloadType::AUDIO_LAST), - max_ack_delay_(config.max_playout_delay) { - DCHECK(transport_sender_); - DCHECK_GT(rtp_timebase_, 0); - DCHECK(congestion_control_); - // We assume animated content to begin with since that is the common use - // case today. - VLOG(1) << SENDER_SSRC << "min latency " - << min_playout_delay_.InMilliseconds() << "max latency " - << max_playout_delay_.InMilliseconds() << "animated latency " - << animated_playout_delay_.InMilliseconds(); - SetTargetPlayoutDelay(animated_playout_delay_); - - CastTransportRtpConfig transport_config; - transport_config.ssrc = config.sender_ssrc; - transport_config.feedback_ssrc = config.receiver_ssrc; - transport_config.rtp_payload_type = config.rtp_payload_type; - transport_config.aes_key = config.aes_key; - transport_config.aes_iv_mask = config.aes_iv_mask; - - transport_sender->InitializeStream( - transport_config, - std::make_unique<FrameSender::RtcpClient>(weak_factory_.GetWeakPtr())); -} - +FrameSender::FrameSender() = default; FrameSender::~FrameSender() = default; -void FrameSender::ScheduleNextRtcpReport() { - DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); - - cast_environment_->PostDelayedTask( - CastEnvironment::MAIN, FROM_HERE, - base::BindOnce(&FrameSender::SendRtcpReport, weak_factory_.GetWeakPtr(), - true), - kRtcpReportInterval); -} - -void FrameSender::SendRtcpReport(bool schedule_future_reports) { - DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); - - // Sanity-check: We should have sent at least the first frame by this point. - DCHECK(!last_send_time_.is_null()); - - // Create lip-sync info for the sender report. The last sent frame's - // reference time and RTP timestamp are used to estimate an RTP timestamp in - // terms of "now." Note that |now| is never likely to be precise to an exact - // frame boundary; and so the computation here will result in a - // |now_as_rtp_timestamp| value that is rarely equal to any one emitted by the - // encoder. - const base::TimeTicks now = cast_environment_->Clock()->NowTicks(); - const base::TimeDelta time_delta = - now - GetRecordedReferenceTime(last_sent_frame_id_); - const RtpTimeDelta rtp_delta = - RtpTimeDelta::FromTimeDelta(time_delta, rtp_timebase_); - const RtpTimeTicks now_as_rtp_timestamp = - GetRecordedRtpTimestamp(last_sent_frame_id_) + rtp_delta; - transport_sender_->SendSenderReport(ssrc_, now, now_as_rtp_timestamp); - - if (schedule_future_reports) - ScheduleNextRtcpReport(); -} - -void FrameSender::OnMeasuredRoundTripTime(base::TimeDelta round_trip_time) { - DCHECK_GT(round_trip_time, base::TimeDelta()); - current_round_trip_time_ = round_trip_time; - max_ack_delay_ = 2 * std::max(current_round_trip_time_, base::TimeDelta()) + - kReceiverProcessTime; - max_ack_delay_ = std::min(max_ack_delay_, target_playout_delay_); -} - -void FrameSender::SetTargetPlayoutDelay( - base::TimeDelta new_target_playout_delay) { - if (send_target_playout_delay_ && - target_playout_delay_ == new_target_playout_delay) { - return; - } - new_target_playout_delay = std::max(new_target_playout_delay, - min_playout_delay_); - new_target_playout_delay = std::min(new_target_playout_delay, - max_playout_delay_); - VLOG(2) << SENDER_SSRC << "Target playout delay changing from " - << target_playout_delay_.InMilliseconds() << " ms to " - << new_target_playout_delay.InMilliseconds() << " ms."; - target_playout_delay_ = new_target_playout_delay; - max_ack_delay_ = std::min(max_ack_delay_, target_playout_delay_); - send_target_playout_delay_ = true; - congestion_control_->UpdateTargetPlayoutDelay(target_playout_delay_); -} - -void FrameSender::ResendCheck() { - DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); - DCHECK(!last_send_time_.is_null()); - const base::TimeDelta time_since_last_send = - cast_environment_->Clock()->NowTicks() - last_send_time_; - if (time_since_last_send > max_ack_delay_) { - if (latest_acked_frame_id_ == last_sent_frame_id_) { - // Last frame acked, no point in doing anything - } else { - VLOG(1) << SENDER_SSRC << "ACK timeout; last acked frame: " - << latest_acked_frame_id_; - ResendForKickstart(); - } - } - ScheduleNextResendCheck(); -} - -void FrameSender::ScheduleNextResendCheck() { - DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); - DCHECK(!last_send_time_.is_null()); - base::TimeDelta time_to_next = - last_send_time_ - cast_environment_->Clock()->NowTicks() + max_ack_delay_; - time_to_next = std::max(time_to_next, kMinSchedulingDelay); - cast_environment_->PostDelayedTask( - CastEnvironment::MAIN, FROM_HERE, - base::BindOnce(&FrameSender::ResendCheck, weak_factory_.GetWeakPtr()), - time_to_next); -} - -void FrameSender::ResendForKickstart() { - DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); - DCHECK(!last_send_time_.is_null()); - VLOG(1) << SENDER_SSRC << "Resending last packet of frame " - << last_sent_frame_id_ << " to kick-start."; - last_send_time_ = cast_environment_->Clock()->NowTicks(); - transport_sender_->ResendFrameForKickstart(ssrc_, last_sent_frame_id_); -} - -void FrameSender::RecordLatestFrameTimestamps(FrameId frame_id, - base::TimeTicks reference_time, - RtpTimeTicks rtp_timestamp) { - DCHECK(!reference_time.is_null()); - frame_reference_times_[frame_id.lower_8_bits()] = reference_time; - frame_rtp_timestamps_[frame_id.lower_8_bits()] = rtp_timestamp; -} - -base::TimeTicks FrameSender::GetRecordedReferenceTime(FrameId frame_id) const { - return frame_reference_times_[frame_id.lower_8_bits()]; -} - -RtpTimeTicks FrameSender::GetRecordedRtpTimestamp(FrameId frame_id) const { - return frame_rtp_timestamps_[frame_id.lower_8_bits()]; -} - -int FrameSender::GetUnacknowledgedFrameCount() const { - if (last_send_time_.is_null()) - return 0; - const int count = last_sent_frame_id_ - latest_acked_frame_id_; - DCHECK_GE(count, 0); - return count; -} - -base::TimeDelta FrameSender::GetAllowedInFlightMediaDuration() const { - // The total amount allowed in-flight media should equal the amount that fits - // within the entire playout delay window, plus the amount of time it takes to - // receive an ACK from the receiver. - return target_playout_delay_ + (current_round_trip_time_ / 2); -} - -void FrameSender::SendEncodedFrame( - int requested_bitrate_before_encode, - std::unique_ptr<SenderEncodedFrame> encoded_frame) { - DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); - - VLOG(2) << SENDER_SSRC << "About to send another frame: last_sent=" - << last_sent_frame_id_ << ", latest_acked=" << latest_acked_frame_id_; - - const FrameId frame_id = encoded_frame->frame_id; - const bool is_first_frame_to_be_sent = last_send_time_.is_null(); - - if (picture_lost_at_receiver_ && - (encoded_frame->dependency == EncodedFrame::KEY)) { - picture_lost_at_receiver_ = false; - DCHECK(frame_id > latest_acked_frame_id_); - // Cancel sending remaining frames. - std::vector<FrameId> cancel_sending_frames; - for (FrameId id = latest_acked_frame_id_ + 1; id < frame_id; ++id) { - cancel_sending_frames.push_back(id); - } - transport_sender_->CancelSendingFrames(ssrc_, cancel_sending_frames); - OnCancelSendingFrames(); - } - - last_send_time_ = cast_environment_->Clock()->NowTicks(); - last_sent_frame_id_ = frame_id; - // If this is the first frame about to be sent, fake the value of - // |latest_acked_frame_id_| to indicate the receiver starts out all caught up. - // Also, schedule the periodic frame re-send checks. - if (is_first_frame_to_be_sent) { - latest_acked_frame_id_ = frame_id - 1; - ScheduleNextResendCheck(); - } - - VLOG_IF(1, !is_audio_ && encoded_frame->dependency == EncodedFrame::KEY) - << SENDER_SSRC << "Sending encoded key frame, id=" << frame_id; - - std::unique_ptr<FrameEvent> encode_event(new FrameEvent()); - encode_event->timestamp = encoded_frame->encode_completion_time; - encode_event->type = FRAME_ENCODED; - encode_event->media_type = is_audio_ ? AUDIO_EVENT : VIDEO_EVENT; - encode_event->rtp_timestamp = encoded_frame->rtp_timestamp; - encode_event->frame_id = frame_id; - encode_event->size = base::checked_cast<uint32_t>(encoded_frame->data.size()); - encode_event->key_frame = encoded_frame->dependency == EncodedFrame::KEY; - encode_event->target_bitrate = requested_bitrate_before_encode; - encode_event->encoder_cpu_utilization = encoded_frame->encoder_utilization; - encode_event->idealized_bitrate_utilization = - encoded_frame->lossy_utilization; - cast_environment_->logger()->DispatchFrameEvent(std::move(encode_event)); - - RecordLatestFrameTimestamps(frame_id, - encoded_frame->reference_time, - encoded_frame->rtp_timestamp); - - if (!is_audio_) { - // Used by chrome/browser/media/cast_mirroring_performance_browsertest.cc - TRACE_EVENT_INSTANT1( - "cast_perf_test", "VideoFrameEncoded", - TRACE_EVENT_SCOPE_THREAD, - "rtp_timestamp", encoded_frame->rtp_timestamp.lower_32_bits()); - } - - // At the start of the session, it's important to send reports before each - // frame so that the receiver can properly compute playout times. The reason - // more than one report is sent is because transmission is not guaranteed, - // only best effort, so send enough that one should almost certainly get - // through. - if (num_aggressive_rtcp_reports_sent_ < kNumAggressiveReportsSentAtStart) { - // SendRtcpReport() will schedule future reports to be made if this is the - // last "aggressive report." - ++num_aggressive_rtcp_reports_sent_; - const bool is_last_aggressive_report = - (num_aggressive_rtcp_reports_sent_ == kNumAggressiveReportsSentAtStart); - VLOG_IF(1, is_last_aggressive_report) - << SENDER_SSRC << "Sending last aggressive report."; - SendRtcpReport(is_last_aggressive_report); - } - - congestion_control_->SendFrameToTransport( - frame_id, encoded_frame->data.size() * 8, last_send_time_); - - if (send_target_playout_delay_) { - encoded_frame->new_playout_delay_ms = - target_playout_delay_.InMilliseconds(); - } - - const char* name = is_audio_ ? "Audio Transport" : "Video Transport"; - TRACE_EVENT_NESTABLE_ASYNC_BEGIN1( - "cast.stream", name, TRACE_ID_WITH_SCOPE(name, frame_id.lower_32_bits()), - "rtp_timestamp", encoded_frame->rtp_timestamp.lower_32_bits()); - transport_sender_->InsertFrame(ssrc_, *encoded_frame); -} - -void FrameSender::OnCancelSendingFrames() {} - -void FrameSender::OnReceivedCastFeedback(const RtcpCastMessage& cast_feedback) { - DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); - - const bool have_valid_rtt = current_round_trip_time_.is_positive(); - if (have_valid_rtt) { - congestion_control_->UpdateRtt(current_round_trip_time_); - - // Having the RTT value implies the receiver sent back a receiver report - // based on it having received a report from here. Therefore, ensure this - // sender stops aggressively sending reports. - if (num_aggressive_rtcp_reports_sent_ < kNumAggressiveReportsSentAtStart) { - VLOG(1) << SENDER_SSRC - << "No longer a need to send reports aggressively (sent " - << num_aggressive_rtcp_reports_sent_ << ")."; - num_aggressive_rtcp_reports_sent_ = kNumAggressiveReportsSentAtStart; - ScheduleNextRtcpReport(); - } - } - - if (last_send_time_.is_null()) - return; // Cannot get an ACK without having first sent a frame. - - if (cast_feedback.missing_frames_and_packets.empty() && - cast_feedback.received_later_frames.empty()) { - if (latest_acked_frame_id_ == cast_feedback.ack_frame_id) { - VLOG(1) << SENDER_SSRC << "Received duplicate ACK for frame " - << latest_acked_frame_id_; - TRACE_EVENT_INSTANT2( - "cast.stream", "Duplicate ACK", TRACE_EVENT_SCOPE_THREAD, - "ack_frame_id", cast_feedback.ack_frame_id.lower_32_bits(), - "last_sent_frame_id", last_sent_frame_id_.lower_32_bits()); - } - // We only count duplicate ACKs when we have sent newer frames. - if (latest_acked_frame_id_ == cast_feedback.ack_frame_id && - latest_acked_frame_id_ != last_sent_frame_id_) { - duplicate_ack_counter_++; - } else { - duplicate_ack_counter_ = 0; - } - if (duplicate_ack_counter_ >= 2 && duplicate_ack_counter_ % 3 == 2) { - ResendForKickstart(); - } - } else { - // Only count duplicated ACKs if there is no NACK request in between. - // This is to avoid aggresive resend. - duplicate_ack_counter_ = 0; - } - - base::TimeTicks now = cast_environment_->Clock()->NowTicks(); - congestion_control_->AckFrame(cast_feedback.ack_frame_id, now); - if (!cast_feedback.received_later_frames.empty()) { - // Ack the received frames. - congestion_control_->AckLaterFrames(cast_feedback.received_later_frames, - now); - } - - std::unique_ptr<FrameEvent> ack_event(new FrameEvent()); - ack_event->timestamp = now; - ack_event->type = FRAME_ACK_RECEIVED; - ack_event->media_type = is_audio_ ? AUDIO_EVENT : VIDEO_EVENT; - ack_event->rtp_timestamp = - GetRecordedRtpTimestamp(cast_feedback.ack_frame_id); - ack_event->frame_id = cast_feedback.ack_frame_id; - cast_environment_->logger()->DispatchFrameEvent(std::move(ack_event)); - - const bool is_acked_out_of_order = - cast_feedback.ack_frame_id < latest_acked_frame_id_; - VLOG(2) << SENDER_SSRC - << "Received ACK" << (is_acked_out_of_order ? " out-of-order" : "") - << " for frame " << cast_feedback.ack_frame_id; - if (is_acked_out_of_order) { - TRACE_EVENT_INSTANT2( - "cast.stream", "ACK out of order", TRACE_EVENT_SCOPE_THREAD, - "ack_frame_id", cast_feedback.ack_frame_id.lower_32_bits(), - "latest_acked_frame_id", latest_acked_frame_id_.lower_32_bits()); - } else if (latest_acked_frame_id_ < cast_feedback.ack_frame_id) { - // Cancel resends of acked frames. - std::vector<FrameId> frames_to_cancel; - frames_to_cancel.reserve(cast_feedback.ack_frame_id - - latest_acked_frame_id_); - do { - ++latest_acked_frame_id_; - frames_to_cancel.push_back(latest_acked_frame_id_); - // This is a good place to match the trace for frame ids - // since this ensures we not only track frame ids that are - // implicitly ACKed, but also handles duplicate ACKs - const char* name = is_audio_ ? "Audio Transport" : "Video Transport"; - TRACE_EVENT_NESTABLE_ASYNC_END1( - "cast.stream", name, - TRACE_ID_WITH_SCOPE(name, latest_acked_frame_id_.lower_32_bits()), - "RTT_usecs", current_round_trip_time_.InMicroseconds()); - } while (latest_acked_frame_id_ < cast_feedback.ack_frame_id); - transport_sender_->CancelSendingFrames(ssrc_, frames_to_cancel); - OnCancelSendingFrames(); - } -} - -void FrameSender::OnReceivedPli() { - picture_lost_at_receiver_ = true; -} - -bool FrameSender::ShouldDropNextFrame(base::TimeDelta frame_duration) const { - // Check that accepting the next frame won't cause more frames to become - // in-flight than the system's design limit. - const int count_frames_in_flight = - GetUnacknowledgedFrameCount() + GetNumberOfFramesInEncoder(); - if (count_frames_in_flight >= kMaxUnackedFrames) { - VLOG(1) << SENDER_SSRC << "Dropping: Too many frames would be in-flight."; - return true; - } - - // Check that accepting the next frame won't exceed the configured maximum - // frame rate, allowing for short-term bursts. - base::TimeDelta duration_in_flight = GetInFlightMediaDuration(); - const double max_frames_in_flight = - max_frame_rate_ * duration_in_flight.InSecondsF(); - if (count_frames_in_flight >= max_frames_in_flight + kMaxFrameBurst) { - VLOG(1) << SENDER_SSRC << "Dropping: Burst threshold would be exceeded."; - return true; - } - - // Check that accepting the next frame won't exceed the allowed in-flight - // media duration. - const base::TimeDelta duration_would_be_in_flight = - duration_in_flight + frame_duration; - const base::TimeDelta allowed_in_flight = GetAllowedInFlightMediaDuration(); - if (VLOG_IS_ON(1)) { - const int64_t percent = - allowed_in_flight.is_positive() - ? base::ClampRound<int64_t>(duration_would_be_in_flight / - allowed_in_flight * 100) - : std::numeric_limits<int64_t>::max(); - VLOG_IF(1, percent > 50) - << SENDER_SSRC - << duration_in_flight.InMicroseconds() << " usec in-flight + " - << frame_duration.InMicroseconds() << " usec for next frame --> " - << percent << "% of allowed in-flight."; - } - if (duration_would_be_in_flight > allowed_in_flight) { - VLOG(1) << SENDER_SSRC << "Dropping: In-flight duration would be too high."; - return true; - } - - // Next frame is accepted. - return false; -} - -} // namespace cast -} // namespace media +} // namespace media::cast
diff --git a/media/cast/sender/frame_sender.h b/media/cast/sender/frame_sender.h index 7e0b1fc..02221e2 100644 --- a/media/cast/sender/frame_sender.h +++ b/media/cast/sender/frame_sender.h
@@ -1,8 +1,6 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// -// This is the base class for an object that send frames to a receiver. #ifndef MEDIA_CAST_SENDER_FRAME_SENDER_H_ #define MEDIA_CAST_SENDER_FRAME_SENDER_H_ @@ -19,196 +17,107 @@ #include "media/cast/net/rtcp/rtcp_defines.h" #include "media/cast/sender/congestion_control.h" -namespace media { -namespace cast { +namespace media::cast { struct SenderEncodedFrame; +class CastEnvironment; +class CastTransport; +// This is the pure virtual interface for an object that sends encoded frames +// to a receiver. class FrameSender { public: - FrameSender(scoped_refptr<CastEnvironment> cast_environment, - CastTransport* const transport_sender, - const FrameSenderConfig& config, - CongestionControl* congestion_control); + // The client is responsible for implementing some encoder-specific methods + // as well as having the option to subscribe to frame cancellation events. + class Client { + public: + virtual ~Client(); + // Returns the number of frames in the encoder's backlog. + virtual int GetNumberOfFramesInEncoder() const = 0; + + // Should return the amount of playback time that is in the encoder's + // backlog. Assuming that the encoder emits frames consecutively, this is + // the same as the difference between the smallest and largest presentation + // timestamps in the backlog. + virtual base::TimeDelta GetEncoderBacklogDuration() const = 0; + + // The frame associated with |frame_id| was canceled and not sent. + virtual void OnFrameCanceled(FrameId frame_id) {} + }; + + static std::unique_ptr<FrameSender> Create( + scoped_refptr<CastEnvironment> cast_environment, + const FrameSenderConfig& config, + CastTransport* const transport_sender, + Client* client); + + FrameSender(); + FrameSender(FrameSender&&) = delete; FrameSender(const FrameSender&) = delete; FrameSender& operator=(const FrameSender&) = delete; - + FrameSender& operator=(FrameSender&&) = delete; virtual ~FrameSender(); - int rtp_timebase() const { return rtp_timebase_; } - - // Calling this function is only valid if the receiver supports the + // Setting of the target playout delay. It should be communicated to the + // receiver on the next encoded frame. + // NOTE: Calling this function is only valid if the receiver supports the // "extra_playout_delay", rtp extension. - void SetTargetPlayoutDelay(base::TimeDelta new_target_playout_delay); + virtual void SetTargetPlayoutDelay( + base::TimeDelta new_target_playout_delay) = 0; + virtual base::TimeDelta GetTargetPlayoutDelay() const = 0; - base::TimeDelta GetTargetPlayoutDelay() const { - return target_playout_delay_; - } + // Whether a key frame is needed, typically caused by a picture loss + // indication event. + virtual bool NeedsKeyFrame() const = 0; - // Called by the encoder with the next EncodeFrame to send. - void SendEncodedFrame(int requested_bitrate_before_encode, - std::unique_ptr<SenderEncodedFrame> encoded_frame); - - protected: - // Returns the number of frames in the encoder's backlog. - virtual int GetNumberOfFramesInEncoder() const = 0; - - // Returns the duration of the data in the encoder's backlog plus the duration - // of sent, unacknowledged frames. - virtual base::TimeDelta GetInFlightMediaDuration() const = 0; - - // One or more frames were canceled. - virtual void OnCancelSendingFrames(); - - protected: - class RtcpClient : public RtcpObserver { - public: - explicit RtcpClient(base::WeakPtr<FrameSender> frame_sender); - ~RtcpClient() override; - - void OnReceivedCastMessage(const RtcpCastMessage& cast_message) override; - void OnReceivedRtt(base::TimeDelta round_trip_time) override; - void OnReceivedPli() override; - - private: - const base::WeakPtr<FrameSender> frame_sender_; - }; - // Schedule and execute periodic sending of RTCP report. - void ScheduleNextRtcpReport(); - void SendRtcpReport(bool schedule_future_reports); - - // Protected for testability. - void OnReceivedCastFeedback(const RtcpCastMessage& cast_feedback); - - // Called when a Pli message is received. - void OnReceivedPli(); - - void OnMeasuredRoundTripTime(base::TimeDelta rtt); - - const scoped_refptr<CastEnvironment> cast_environment_; - - // Sends encoded frames over the configured transport (e.g., UDP). In - // Chromium, this could be a proxy that first sends the frames from a renderer - // process to the browser process over IPC, with the browser process being - // responsible for "packetizing" the frames and pushing packets into the - // network layer. - const raw_ptr<CastTransport> transport_sender_; - - const uint32_t ssrc_; - - protected: - // Schedule and execute periodic checks for re-sending packets. If no - // acknowledgements have been received for "too long," FrameSender will - // speculatively re-send certain packets of an unacked frame to kick-start - // re-transmission. This is a last resort tactic to prevent the session from - // getting stuck after a long outage. - void ScheduleNextResendCheck(); - void ResendCheck(); - void ResendForKickstart(); + // Called by the encoder with the next encoded frame to send. + virtual void EnqueueFrame( + std::unique_ptr<SenderEncodedFrame> encoded_frame) = 0; // Returns true if too many frames would be in-flight by encoding and sending // the next frame having the given |frame_duration|. - bool ShouldDropNextFrame(base::TimeDelta frame_duration) const; + // + // Callers are recommended to compute the frame duration based on the + // difference between the next and last frames' reference times, or the period + // between frames of the configured max frame rate if the reference times are + // unavailable. + virtual bool ShouldDropNextFrame(base::TimeDelta frame_duration) const = 0; - // Record or retrieve a recent history of each frame's timestamps. - // Warning: If a frame ID too far in the past is requested, the getters will - // silently succeed but return incorrect values. Be sure to respect - // media::cast::kMaxUnackedFrames. - void RecordLatestFrameTimestamps(FrameId frame_id, - base::TimeTicks reference_time, - RtpTimeTicks rtp_timestamp); - base::TimeTicks GetRecordedReferenceTime(FrameId frame_id) const; - RtpTimeTicks GetRecordedRtpTimestamp(FrameId frame_id) const; + // Returns the RTP timestamp on the frame associated with |frame_id|. + virtual RtpTimeTicks GetRecordedRtpTimestamp(FrameId frame_id) const = 0; // Returns the number of frames that were sent but not yet acknowledged. - int GetUnacknowledgedFrameCount() const; + virtual int GetUnacknowledgedFrameCount() const = 0; - // Playout delay represents total amount of time between a frame's - // capture/recording on the sender and its playback on the receiver - // (i.e., shown to a user). This should be a value large enough to - // give the system sufficient time to encode, transmit/retransmit, - // receive, decode, and render; given its run-time environment - // (sender/receiver hardware performance, network conditions,etc.). + // Returns the suggested bitrate the next frame should be encoded at. + virtual int GetSuggestedBitrate(base::TimeTicks playout_time, + base::TimeDelta playout_delay) = 0; - // The |target_playout delay_| is the current delay that is adaptively - // adjusted based on feedback from video capture engine and the congestion - // control. In case of interactive content, the target is adjusted to start - // at |min_playout_delay_| and in case of animated content, it starts out at - // |animated_playout_delay_| and then adaptively adjust based on feedback - // from congestion control. - base::TimeDelta target_playout_delay_; - const base::TimeDelta min_playout_delay_; - const base::TimeDelta max_playout_delay_; - // Starting playout delay for animated content. - const base::TimeDelta animated_playout_delay_; + // Configuration specific methods. - // If true, we transmit the target playout delay to the receiver. - bool send_target_playout_delay_; + // The maximum frame rate. + virtual double MaxFrameRate() const = 0; + virtual void SetMaxFrameRate(double max_frame_rate) = 0; - // Max encoded frames generated per second. - double max_frame_rate_; + // The current target playout delay. + virtual base::TimeDelta TargetPlayoutDelay() const = 0; - // Counts how many RTCP reports are being "aggressively" sent (i.e., one per - // frame) at the start of the session. Once a threshold is reached, RTCP - // reports are instead sent at the configured interval + random drift. - int num_aggressive_rtcp_reports_sent_; + // The current, estimated round trip time. + virtual base::TimeDelta CurrentRoundTripTime() const = 0; - // This is "null" until the first frame is sent. Thereafter, this tracks the - // last time any frame was sent or re-sent. - base::TimeTicks last_send_time_; + // When the last frame was sent. + virtual base::TimeTicks LastSendTime() const = 0; - // The ID of the last frame sent. This member is invalid until - // |!last_send_time_.is_null()|. - FrameId last_sent_frame_id_; + // The latest acknowledged frame ID. + virtual FrameId LatestAckedFrameId() const = 0; - // The ID of the latest (not necessarily the last) frame that has been - // acknowledged. This member is invalid until |!last_send_time_.is_null()|. - FrameId latest_acked_frame_id_; - - // Counts the number of duplicate ACK that are being received. When this - // number reaches a threshold, the sender will take this as a sign that the - // receiver hasn't yet received the first packet of the next frame. In this - // case, FrameSender will trigger a re-send of the next frame. - int duplicate_ack_counter_; - - // This object controls how we change the bitrate to make sure the - // buffer doesn't overflow. - std::unique_ptr<CongestionControl> congestion_control_; - - // The most recently measured round trip time. - base::TimeDelta current_round_trip_time_; - - // This flag is set true when a Pli message is received. It is cleared once - // the FrameSender scheduled an encoded key frame to be sent. - bool picture_lost_at_receiver_; - - private: - // Returns the maximum media duration currently allowed in-flight. This - // fluctuates in response to the currently-measured network latency. - base::TimeDelta GetAllowedInFlightMediaDuration() const; - - // RTP timestamp increment representing one second. - const int rtp_timebase_; - - const bool is_audio_; - - // This is the maximum delay that the sender should get ack from receiver. - // Otherwise, sender will call ResendForKickstart(). - base::TimeDelta max_ack_delay_; - - // Ring buffers to keep track of recent frame timestamps (both in terms of - // local reference time and RTP media time). These should only be accessed - // through the Record/GetXXX() methods. The index into this ring - // buffer is the lower 8 bits of the FrameId. - base::TimeTicks frame_reference_times_[256]; - RtpTimeTicks frame_rtp_timestamps_[256]; - - // NOTE: Weak pointers must be invalidated before all other member variables. - base::WeakPtrFactory<FrameSender> weak_factory_{this}; + // RTCP client-specific methods. + virtual void OnReceivedCastFeedback(const RtcpCastMessage& cast_feedback) = 0; + virtual void OnReceivedPli() = 0; + virtual void OnMeasuredRoundTripTime(base::TimeDelta rtt) = 0; }; -} // namespace cast -} // namespace media +} // namespace media::cast #endif // MEDIA_CAST_SENDER_FRAME_SENDER_H_
diff --git a/media/cast/sender/frame_sender_impl.cc b/media/cast/sender/frame_sender_impl.cc new file mode 100644 index 0000000..77b969c --- /dev/null +++ b/media/cast/sender/frame_sender_impl.cc
@@ -0,0 +1,559 @@ +// Copyright 2014 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/cast/sender/frame_sender_impl.h" + +#include <algorithm> +#include <limits> +#include <memory> +#include <utility> +#include <vector> + +#include "base/bind.h" +#include "base/feature_list.h" +#include "base/logging.h" +#include "base/numerics/safe_conversions.h" +#include "base/trace_event/trace_event.h" +#include "media/base/media_switches.h" +#include "media/cast/common/sender_encoded_frame.h" +#include "media/cast/constants.h" + +namespace media::cast { +namespace { + +constexpr int kNumAggressiveReportsSentAtStart = 100; +constexpr base::TimeDelta kMinSchedulingDelay = base::Milliseconds(1); +constexpr base::TimeDelta kReceiverProcessTime = base::Milliseconds(250); + +// The additional number of frames that can be in-flight when input exceeds the +// maximum frame rate. +constexpr int kMaxFrameBurst = 5; + +} // namespace + +// static +std::unique_ptr<FrameSender> FrameSender::Create( + scoped_refptr<CastEnvironment> cast_environment, + const FrameSenderConfig& config, + CastTransport* const transport_sender, + Client* client) { + // TODO(https://crbug.com/1212803): return a new OpenscreenSender if the + // Open Screen cast streaming session flag is enabled. + if (base::FeatureList::IsEnabled(kOpenscreenCastStreamingSession)) { + NOTIMPLEMENTED() + << "Enabled the OpenscreenCastStreamingFlag, but no FrameSenderImpl " + "implementation yet."; + return nullptr; + } + + return std::make_unique<FrameSenderImpl>(cast_environment, config, + transport_sender, client); +} + +// Convenience macro used in logging statements throughout this file. +#define SENDER_SSRC \ + (is_audio_ ? "AUDIO[" : "VIDEO[") << config_.sender_ssrc << "] " + +FrameSenderImpl::Client::~Client() = default; + +FrameSenderImpl::RtcpClient::RtcpClient( + base::WeakPtr<FrameSenderImpl> frame_sender) + : frame_sender_(frame_sender) {} + +FrameSenderImpl::RtcpClient::~RtcpClient() = default; + +void FrameSenderImpl::RtcpClient::OnReceivedCastMessage( + const RtcpCastMessage& cast_message) { + if (frame_sender_) + frame_sender_->OnReceivedCastFeedback(cast_message); +} + +void FrameSenderImpl::RtcpClient::OnReceivedRtt( + base::TimeDelta round_trip_time) { + if (frame_sender_) + frame_sender_->OnMeasuredRoundTripTime(round_trip_time); +} + +void FrameSenderImpl::RtcpClient::OnReceivedPli() { + if (frame_sender_) + frame_sender_->OnReceivedPli(); +} + +FrameSenderImpl::FrameSenderImpl( + scoped_refptr<CastEnvironment> cast_environment, + const FrameSenderConfig& config, + CastTransport* const transport_sender, + Client* client) + : cast_environment_(cast_environment), + config_(config), + target_playout_delay_(config.max_playout_delay), + max_frame_rate_(config.max_frame_rate), + transport_sender_(transport_sender), + client_(client), + is_audio_(config.rtp_payload_type <= RtpPayloadType::AUDIO_LAST), + // We only use the adaptive control for software video encoding. + congestion_control_( + (!config.use_external_encoder && !is_audio_) + ? NewAdaptiveCongestionControl(cast_environment->Clock(), + config.max_bitrate, + config.min_bitrate, + max_frame_rate_) + : NewFixedCongestionControl( + (config.min_bitrate + config.max_bitrate) / 2) + + ), + max_ack_delay_(config_.max_playout_delay) { + DCHECK(transport_sender_); + DCHECK_GT(config_.rtp_timebase, 0); + DCHECK(congestion_control_); + // We assume animated content to begin with since that is the common use + // case today. + VLOG(1) << SENDER_SSRC << "min latency " + << config_.min_playout_delay.InMilliseconds() << "max latency " + << config_.max_playout_delay.InMilliseconds() << "animated latency " + << config_.animated_playout_delay.InMilliseconds(); + SetTargetPlayoutDelay(config_.animated_playout_delay); + + CastTransportRtpConfig transport_config; + transport_config.ssrc = config.sender_ssrc; + transport_config.feedback_ssrc = config.receiver_ssrc; + transport_config.rtp_payload_type = config.rtp_payload_type; + transport_config.aes_key = config.aes_key; + transport_config.aes_iv_mask = config.aes_iv_mask; + transport_sender_->InitializeStream( + transport_config, std::make_unique<FrameSenderImpl::RtcpClient>( + weak_factory_.GetWeakPtr())); +} + +FrameSenderImpl::~FrameSenderImpl() = default; + +bool FrameSenderImpl::NeedsKeyFrame() const { + return picture_lost_at_receiver_; +} + +base::TimeTicks FrameSenderImpl::GetRecordedReferenceTime( + FrameId frame_id) const { + return frame_reference_times_[frame_id.lower_8_bits()]; +} + +void FrameSenderImpl::ScheduleNextRtcpReport() { + DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); + + cast_environment_->PostDelayedTask( + CastEnvironment::MAIN, FROM_HERE, + base::BindOnce(&FrameSenderImpl::SendRtcpReport, + weak_factory_.GetWeakPtr(), true), + kRtcpReportInterval); +} + +void FrameSenderImpl::SendRtcpReport(bool schedule_future_reports) { + DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); + + // Sanity-check: We should have sent at least the first frame by this point. + DCHECK(!last_send_time_.is_null()); + + // Create lip-sync info for the sender report. The last sent frame's + // reference time and RTP timestamp are used to estimate an RTP timestamp in + // terms of "now." Note that |now| is never likely to be precise to an exact + // frame boundary; and so the computation here will result in a + // |now_as_rtp_timestamp| value that is rarely equal to any one emitted by the + // encoder. + const base::TimeTicks now = cast_environment_->Clock()->NowTicks(); + const base::TimeDelta time_delta = + now - GetRecordedReferenceTime(last_sent_frame_id_); + const RtpTimeDelta rtp_delta = + RtpTimeDelta::FromTimeDelta(time_delta, config_.rtp_timebase); + const RtpTimeTicks now_as_rtp_timestamp = + GetRecordedRtpTimestamp(last_sent_frame_id_) + rtp_delta; + transport_sender_->SendSenderReport(config_.sender_ssrc, now, + now_as_rtp_timestamp); + + if (schedule_future_reports) + ScheduleNextRtcpReport(); +} + +void FrameSenderImpl::OnMeasuredRoundTripTime(base::TimeDelta round_trip_time) { + DCHECK_GT(round_trip_time, base::TimeDelta()); + current_round_trip_time_ = round_trip_time; + max_ack_delay_ = 2 * std::max(current_round_trip_time_, base::TimeDelta()) + + kReceiverProcessTime; + max_ack_delay_ = std::min(max_ack_delay_, target_playout_delay_); +} + +void FrameSenderImpl::SetTargetPlayoutDelay( + base::TimeDelta new_target_playout_delay) { + if (send_target_playout_delay_ && + target_playout_delay_ == new_target_playout_delay) { + return; + } + new_target_playout_delay = + std::max(new_target_playout_delay, config_.min_playout_delay); + new_target_playout_delay = + std::min(new_target_playout_delay, config_.max_playout_delay); + VLOG(2) << SENDER_SSRC << "Target playout delay changing from " + << target_playout_delay_.InMilliseconds() << " ms to " + << new_target_playout_delay.InMilliseconds() << " ms."; + target_playout_delay_ = new_target_playout_delay; + max_ack_delay_ = std::min(max_ack_delay_, target_playout_delay_); + send_target_playout_delay_ = true; + congestion_control_->UpdateTargetPlayoutDelay(target_playout_delay_); +} + +base::TimeDelta FrameSenderImpl::GetTargetPlayoutDelay() const { + return target_playout_delay_; +} + +void FrameSenderImpl::ResendCheck() { + DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); + DCHECK(!last_send_time_.is_null()); + const base::TimeDelta time_since_last_send = + cast_environment_->Clock()->NowTicks() - last_send_time_; + if (time_since_last_send > max_ack_delay_) { + if (latest_acked_frame_id_ == last_sent_frame_id_) { + // Last frame acked, no point in doing anything + } else { + VLOG(1) << SENDER_SSRC + << "ACK timeout; last acked frame: " << latest_acked_frame_id_; + ResendForKickstart(); + } + } + ScheduleNextResendCheck(); +} + +void FrameSenderImpl::ScheduleNextResendCheck() { + DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); + DCHECK(!last_send_time_.is_null()); + base::TimeDelta time_to_next = + last_send_time_ - cast_environment_->Clock()->NowTicks() + max_ack_delay_; + time_to_next = std::max(time_to_next, kMinSchedulingDelay); + cast_environment_->PostDelayedTask( + CastEnvironment::MAIN, FROM_HERE, + base::BindOnce(&FrameSenderImpl::ResendCheck, weak_factory_.GetWeakPtr()), + time_to_next); +} + +void FrameSenderImpl::ResendForKickstart() { + DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); + DCHECK(!last_send_time_.is_null()); + VLOG(1) << SENDER_SSRC << "Resending last packet of frame " + << last_sent_frame_id_ << " to kick-start."; + last_send_time_ = cast_environment_->Clock()->NowTicks(); + transport_sender_->ResendFrameForKickstart(config_.sender_ssrc, + last_sent_frame_id_); +} + +void FrameSenderImpl::RecordLatestFrameTimestamps( + FrameId frame_id, + base::TimeTicks reference_time, + RtpTimeTicks rtp_timestamp) { + DCHECK(!reference_time.is_null()); + frame_reference_times_[frame_id.lower_8_bits()] = reference_time; + frame_rtp_timestamps_[frame_id.lower_8_bits()] = rtp_timestamp; +} + +base::TimeDelta FrameSenderImpl::GetInFlightMediaDuration() const { + const base::TimeDelta encoder_duration = client_->GetEncoderBacklogDuration(); + // No frames are in flight, so only look at the encoder duration. + if (last_sent_frame_id_ == latest_acked_frame_id_) { + return encoder_duration; + } + + const RtpTimeTicks oldest_acked_timestamp = + GetRecordedRtpTimestamp(latest_acked_frame_id_); + const RtpTimeTicks newest_acked_timestamp = + GetRecordedRtpTimestamp(last_sent_frame_id_); + return (newest_acked_timestamp - oldest_acked_timestamp) + .ToTimeDelta(config_.rtp_timebase) + + encoder_duration; +} + +RtpTimeTicks FrameSenderImpl::GetRecordedRtpTimestamp(FrameId frame_id) const { + return frame_rtp_timestamps_[frame_id.lower_8_bits()]; +} + +int FrameSenderImpl::GetUnacknowledgedFrameCount() const { + if (last_send_time_.is_null()) + return 0; + const int count = last_sent_frame_id_ - latest_acked_frame_id_; + DCHECK_GE(count, 0); + return count; +} + +int FrameSenderImpl::GetSuggestedBitrate(base::TimeTicks playout_time, + base::TimeDelta playout_delay) { + return congestion_control_->GetBitrate(playout_time, playout_delay); +} + +double FrameSenderImpl::MaxFrameRate() const { + return max_frame_rate_; +} + +void FrameSenderImpl::SetMaxFrameRate(double max_frame_rate) { + max_frame_rate_ = max_frame_rate; +} + +base::TimeDelta FrameSenderImpl::TargetPlayoutDelay() const { + return target_playout_delay_; +} +base::TimeDelta FrameSenderImpl::CurrentRoundTripTime() const { + return current_round_trip_time_; +} +base::TimeTicks FrameSenderImpl::LastSendTime() const { + return last_send_time_; +} +FrameId FrameSenderImpl::LatestAckedFrameId() const { + return latest_acked_frame_id_; +} + +base::TimeDelta FrameSenderImpl::GetAllowedInFlightMediaDuration() const { + // The total amount allowed in-flight media should equal the amount that fits + // within the entire playout delay window, plus the amount of time it takes to + // receive an ACK from the receiver. + return target_playout_delay_ + (current_round_trip_time_ / 2); +} + +void FrameSenderImpl::EnqueueFrame( + std::unique_ptr<SenderEncodedFrame> encoded_frame) { + DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); + + VLOG(2) << SENDER_SSRC + << "About to send another frame: last_sent=" << last_sent_frame_id_ + << ", latest_acked=" << latest_acked_frame_id_; + + const FrameId frame_id = encoded_frame->frame_id; + const bool is_first_frame_to_be_sent = last_send_time_.is_null(); + + if (picture_lost_at_receiver_ && + (encoded_frame->dependency == EncodedFrame::KEY)) { + picture_lost_at_receiver_ = false; + DCHECK(frame_id > latest_acked_frame_id_); + // Cancel sending remaining frames. + std::vector<FrameId> cancel_sending_frames; + for (FrameId id = latest_acked_frame_id_ + 1; id < frame_id; ++id) { + cancel_sending_frames.push_back(id); + client_->OnFrameCanceled(id); + } + transport_sender_->CancelSendingFrames(config_.sender_ssrc, + cancel_sending_frames); + } + + last_send_time_ = cast_environment_->Clock()->NowTicks(); + + DCHECK(frame_id > last_sent_frame_id_) << "enqueued frames out of order."; + last_sent_frame_id_ = frame_id; + // If this is the first frame about to be sent, fake the value of + // |latest_acked_frame_id_| to indicate the receiver starts out all + // caught up. Also, schedule the periodic frame re-send checks. + if (is_first_frame_to_be_sent) { + latest_acked_frame_id_ = frame_id - 1; + ScheduleNextResendCheck(); + } + + VLOG_IF(1, !is_audio_ && encoded_frame->dependency == EncodedFrame::KEY) + << SENDER_SSRC << "Sending encoded key frame, id=" << frame_id; + + std::unique_ptr<FrameEvent> encode_event(new FrameEvent()); + encode_event->timestamp = encoded_frame->encode_completion_time; + encode_event->type = FRAME_ENCODED; + encode_event->media_type = is_audio_ ? AUDIO_EVENT : VIDEO_EVENT; + encode_event->rtp_timestamp = encoded_frame->rtp_timestamp; + encode_event->frame_id = frame_id; + encode_event->size = base::checked_cast<uint32_t>(encoded_frame->data.size()); + encode_event->key_frame = encoded_frame->dependency == EncodedFrame::KEY; + encode_event->target_bitrate = encoded_frame->encoder_bitrate; + encode_event->encoder_cpu_utilization = encoded_frame->encoder_utilization; + encode_event->idealized_bitrate_utilization = encoded_frame->lossiness; + cast_environment_->logger()->DispatchFrameEvent(std::move(encode_event)); + + RecordLatestFrameTimestamps(frame_id, encoded_frame->reference_time, + encoded_frame->rtp_timestamp); + + if (!is_audio_) { + // Used by chrome/browser/media/cast_mirroring_performance_browsertest.cc + TRACE_EVENT_INSTANT1("cast_perf_test", "VideoFrameEncoded", + TRACE_EVENT_SCOPE_THREAD, "rtp_timestamp", + encoded_frame->rtp_timestamp.lower_32_bits()); + } + + // At the start of the session, it's important to send reports before each + // frame so that the receiver can properly compute playout times. The reason + // more than one report is sent is because transmission is not guaranteed, + // only best effort, so send enough that one should almost certainly get + // through. + if (num_aggressive_rtcp_reports_sent_ < kNumAggressiveReportsSentAtStart) { + // SendRtcpReport() will schedule future reports to be made if this is the + // last "aggressive report." + ++num_aggressive_rtcp_reports_sent_; + const bool is_last_aggressive_report = + (num_aggressive_rtcp_reports_sent_ == kNumAggressiveReportsSentAtStart); + VLOG_IF(1, is_last_aggressive_report) + << SENDER_SSRC << "Sending last aggressive report."; + SendRtcpReport(is_last_aggressive_report); + } + + congestion_control_->SendFrameToTransport( + frame_id, encoded_frame->data.size() * 8, last_send_time_); + + if (send_target_playout_delay_) { + encoded_frame->new_playout_delay_ms = + target_playout_delay_.InMilliseconds(); + } + + const char* name = is_audio_ ? "Audio Transport" : "Video Transport"; + TRACE_EVENT_NESTABLE_ASYNC_BEGIN1( + "cast.stream", name, TRACE_ID_WITH_SCOPE(name, frame_id.lower_32_bits()), + "rtp_timestamp", encoded_frame->rtp_timestamp.lower_32_bits()); + transport_sender_->InsertFrame(config_.sender_ssrc, *encoded_frame); +} + +void FrameSenderImpl::OnReceivedCastFeedback( + const RtcpCastMessage& cast_feedback) { + DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); + + const bool have_valid_rtt = current_round_trip_time_.is_positive(); + if (have_valid_rtt) { + congestion_control_->UpdateRtt(current_round_trip_time_); + + // Having the RTT value implies the receiver sent back a receiver report + // based on it having received a report from here. Therefore, ensure this + // sender stops aggressively sending reports. + if (num_aggressive_rtcp_reports_sent_ < kNumAggressiveReportsSentAtStart) { + VLOG(1) << SENDER_SSRC + << "No longer a need to send reports aggressively (sent " + << num_aggressive_rtcp_reports_sent_ << ")."; + num_aggressive_rtcp_reports_sent_ = kNumAggressiveReportsSentAtStart; + ScheduleNextRtcpReport(); + } + } + + if (last_send_time_.is_null()) + return; // Cannot get an ACK without having first sent a frame. + + if (cast_feedback.missing_frames_and_packets.empty() && + cast_feedback.received_later_frames.empty()) { + if (latest_acked_frame_id_ == cast_feedback.ack_frame_id) { + VLOG(1) << SENDER_SSRC << "Received duplicate ACK for frame " + << latest_acked_frame_id_; + TRACE_EVENT_INSTANT2( + "cast.stream", "Duplicate ACK", TRACE_EVENT_SCOPE_THREAD, + "ack_frame_id", cast_feedback.ack_frame_id.lower_32_bits(), + "last_sent_frame_id", last_sent_frame_id_.lower_32_bits()); + } + // We only count duplicate ACKs when we have sent newer frames. + if (latest_acked_frame_id_ == cast_feedback.ack_frame_id && + latest_acked_frame_id_ != last_sent_frame_id_) { + duplicate_ack_counter_++; + } else { + duplicate_ack_counter_ = 0; + } + if (duplicate_ack_counter_ >= 2 && duplicate_ack_counter_ % 3 == 2) { + ResendForKickstart(); + } + } else { + // Only count duplicated ACKs if there is no NACK request in between. + // This is to avoid aggressive resend. + duplicate_ack_counter_ = 0; + } + + base::TimeTicks now = cast_environment_->Clock()->NowTicks(); + congestion_control_->AckFrame(cast_feedback.ack_frame_id, now); + if (!cast_feedback.received_later_frames.empty()) { + // Ack the received frames. + congestion_control_->AckLaterFrames(cast_feedback.received_later_frames, + now); + } + + std::unique_ptr<FrameEvent> ack_event(new FrameEvent()); + ack_event->timestamp = now; + ack_event->type = FRAME_ACK_RECEIVED; + ack_event->media_type = is_audio_ ? AUDIO_EVENT : VIDEO_EVENT; + ack_event->rtp_timestamp = + GetRecordedRtpTimestamp(cast_feedback.ack_frame_id); + ack_event->frame_id = cast_feedback.ack_frame_id; + cast_environment_->logger()->DispatchFrameEvent(std::move(ack_event)); + + const bool is_acked_out_of_order = + cast_feedback.ack_frame_id < latest_acked_frame_id_; + VLOG(2) << SENDER_SSRC << "Received ACK" + << (is_acked_out_of_order ? " out-of-order" : "") << " for frame " + << cast_feedback.ack_frame_id; + if (is_acked_out_of_order) { + TRACE_EVENT_INSTANT2( + "cast.stream", "ACK out of order", TRACE_EVENT_SCOPE_THREAD, + "ack_frame_id", cast_feedback.ack_frame_id.lower_32_bits(), + "latest_acked_frame_id", latest_acked_frame_id_.lower_32_bits()); + } else if (latest_acked_frame_id_ < cast_feedback.ack_frame_id) { + // Cancel resends of acked frames. + std::vector<FrameId> frames_to_cancel; + frames_to_cancel.reserve(cast_feedback.ack_frame_id - + latest_acked_frame_id_); + do { + ++latest_acked_frame_id_; + frames_to_cancel.push_back(latest_acked_frame_id_); + client_->OnFrameCanceled(latest_acked_frame_id_); + // This is a good place to match the trace for frame ids + // since this ensures we not only track frame ids that are + // implicitly ACKed, but also handles duplicate ACKs + const char* name = is_audio_ ? "Audio Transport" : "Video Transport"; + TRACE_EVENT_NESTABLE_ASYNC_END1( + "cast.stream", name, + TRACE_ID_WITH_SCOPE(name, latest_acked_frame_id_.lower_32_bits()), + "RTT_usecs", current_round_trip_time_.InMicroseconds()); + } while (latest_acked_frame_id_ < cast_feedback.ack_frame_id); + transport_sender_->CancelSendingFrames(config_.sender_ssrc, + frames_to_cancel); + } +} + +void FrameSenderImpl::OnReceivedPli() { + picture_lost_at_receiver_ = true; +} + +bool FrameSenderImpl::ShouldDropNextFrame( + base::TimeDelta frame_duration) const { + // Check that accepting the next frame won't cause more frames to become + // in-flight than the system's design limit. + const int count_frames_in_flight = + GetUnacknowledgedFrameCount() + client_->GetNumberOfFramesInEncoder(); + if (count_frames_in_flight >= kMaxUnackedFrames) { + VLOG(1) << SENDER_SSRC << "Dropping: Too many frames would be in-flight."; + return true; + } + + // Check that accepting the next frame won't exceed the configured maximum + // frame rate, allowing for short-term bursts. + const base::TimeDelta duration_in_flight = GetInFlightMediaDuration(); + const double max_frames_in_flight = + max_frame_rate_ * duration_in_flight.InSecondsF(); + if (count_frames_in_flight >= max_frames_in_flight + kMaxFrameBurst) { + VLOG(1) << SENDER_SSRC << "Dropping: Burst threshold would be exceeded."; + return true; + } + + // Check that accepting the next frame won't exceed the allowed in-flight + // media duration. + const base::TimeDelta duration_would_be_in_flight = + duration_in_flight + frame_duration; + const base::TimeDelta allowed_in_flight = GetAllowedInFlightMediaDuration(); + if (VLOG_IS_ON(1)) { + const int64_t percent = + allowed_in_flight.is_positive() + ? base::ClampRound<int64_t>(duration_would_be_in_flight / + allowed_in_flight * 100) + : std::numeric_limits<int64_t>::max(); + VLOG_IF(1, percent > 50) + << SENDER_SSRC << duration_in_flight.InMicroseconds() + << " usec in-flight + " << frame_duration.InMicroseconds() + << " usec for next frame --> " << percent << "% of allowed in-flight."; + } + if (duration_would_be_in_flight > allowed_in_flight) { + VLOG(1) << SENDER_SSRC << "Dropping: In-flight duration would be too high."; + return true; + } + + // Next frame is accepted. + return false; +} + +} // namespace media::cast
diff --git a/media/cast/sender/frame_sender_impl.h b/media/cast/sender/frame_sender_impl.h new file mode 100644 index 0000000..41e3d8d1 --- /dev/null +++ b/media/cast/sender/frame_sender_impl.h
@@ -0,0 +1,182 @@ +// Copyright 2014 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. +// +// This is the base class for an object that send frames to a receiver. + +#ifndef MEDIA_CAST_SENDER_FRAME_SENDER_IMPL_H_ +#define MEDIA_CAST_SENDER_FRAME_SENDER_IMPL_H_ + +#include <stdint.h> + +#include "base/memory/raw_ptr.h" +#include "base/memory/ref_counted.h" +#include "base/memory/weak_ptr.h" +#include "base/time/time.h" +#include "media/cast/cast_config.h" +#include "media/cast/cast_environment.h" +#include "media/cast/net/cast_transport.h" +#include "media/cast/net/rtcp/rtcp_defines.h" +#include "media/cast/sender/congestion_control.h" +#include "media/cast/sender/frame_sender.h" + +namespace media::cast { + +struct SenderEncodedFrame; + +class FrameSenderImpl : public FrameSender { + public: + FrameSenderImpl(scoped_refptr<CastEnvironment> cast_environment, + const FrameSenderConfig& config, + CastTransport* const transport_sender, + Client* client); + ~FrameSenderImpl() override; + + // FrameSender overrides. + void SetTargetPlayoutDelay(base::TimeDelta new_target_playout_delay) override; + base::TimeDelta GetTargetPlayoutDelay() const override; + bool NeedsKeyFrame() const override; + void EnqueueFrame(std::unique_ptr<SenderEncodedFrame> encoded_frame) override; + bool ShouldDropNextFrame(base::TimeDelta frame_duration) const override; + RtpTimeTicks GetRecordedRtpTimestamp(FrameId frame_id) const override; + int GetUnacknowledgedFrameCount() const override; + int GetSuggestedBitrate(base::TimeTicks playout_time, + base::TimeDelta playout_delay) override; + double MaxFrameRate() const override; + void SetMaxFrameRate(double max_frame_rate) override; + base::TimeDelta TargetPlayoutDelay() const override; + base::TimeDelta CurrentRoundTripTime() const override; + base::TimeTicks LastSendTime() const override; + FrameId LatestAckedFrameId() const override; + void OnReceivedCastFeedback(const RtcpCastMessage& cast_feedback) override; + void OnReceivedPli() override; + void OnMeasuredRoundTripTime(base::TimeDelta rtt) override; + + private: + // Helper for getting the reference time recorded on the frame associated + // with |frame_id|. + base::TimeTicks GetRecordedReferenceTime(FrameId frame_id) const; + + // Schedule and execute periodic checks for re-sending packets. If no + // acknowledgements have been received for "too long," FrameSenderImpl will + // speculatively re-send certain packets of an unacked frame to kick-start + // re-transmission. This is a last resort tactic to prevent the session from + // getting stuck after a long outage. + void ScheduleNextResendCheck(); + void ResendCheck(); + void ResendForKickstart(); + + // Schedule and execute periodic sending of RTCP report. + void ScheduleNextRtcpReport(); + void SendRtcpReport(bool schedule_future_reports); + + // Record or retrieve a recent history of each frame's timestamps. + // Warning: If a frame ID too far in the past is requested, the getters will + // silently succeed but return incorrect values. Be sure to respect + // media::cast::kMaxUnackedFrames. + void RecordLatestFrameTimestamps(FrameId frame_id, + base::TimeTicks reference_time, + RtpTimeTicks rtp_timestamp); + + base::TimeDelta GetInFlightMediaDuration() const; + + private: + class RtcpClient : public RtcpObserver { + public: + explicit RtcpClient(base::WeakPtr<FrameSenderImpl> frame_sender); + ~RtcpClient() override; + + void OnReceivedCastMessage(const RtcpCastMessage& cast_message) override; + void OnReceivedRtt(base::TimeDelta round_trip_time) override; + void OnReceivedPli() override; + + private: + const base::WeakPtr<FrameSenderImpl> frame_sender_; + }; + + // The cast environment. + const scoped_refptr<CastEnvironment> cast_environment_; + + // The configuration provided upon initialization. + const FrameSenderConfig config_; + + // The target playout delay, may fluctuate between the min and max delays + // stored in |config_|. + base::TimeDelta target_playout_delay_; + + // Max encoded frames generated per second. + double max_frame_rate_; + + // Sends encoded frames over the configured transport (e.g., UDP). In + // Chromium, this could be a proxy that first sends the frames from a renderer + // process to the browser process over IPC, with the browser process being + // responsible for "packetizing" the frames and pushing packets into the + // network layer. + const raw_ptr<CastTransport> transport_sender_; + + // The frame sender client. + Client* client_ = nullptr; + + // Whether this is an audio or video frame sender. + const bool is_audio_; + + // The congestion control manages frame statistics and helps make decisions + // about what bitrate we encode the next frame at. + std::unique_ptr<CongestionControl> congestion_control_; + + // This is the maximum delay that the sender should get ack from receiver. + // Otherwise, sender will call ResendForKickstart(). + base::TimeDelta max_ack_delay_; + + // This is "null" until the first frame is sent. Thereafter, this tracks the + // last time any frame was sent or re-sent. + base::TimeTicks last_send_time_; + + // The ID of the last frame sent. This member is invalid until + // |!last_send_time_.is_null()|. + FrameId last_sent_frame_id_; + + // The ID of the latest (not necessarily the last) frame that has been + // acknowledged. This member is invalid until |!last_send_time_.is_null()|. + FrameId latest_acked_frame_id_; + + // The most recently measured round trip time. + base::TimeDelta current_round_trip_time_; + + // This is the maximum delay that the sender should get ack from receiver. + // Counts how many RTCP reports are being "aggressively" sent (i.e., one per + // frame) at the start of the session. Once a threshold is reached, RTCP + // reports are instead sent at the configured interval + random drift. + int num_aggressive_rtcp_reports_sent_ = 0; + + // Counts the number of duplicate ACK that are being received. When this + // number reaches a threshold, the sender will take this as a sign that the + // receiver hasn't yet received the first packet of the next frame. In this + // case, FrameSenderImpl will trigger a re-send of the next frame. + int duplicate_ack_counter_ = 0; + + // This flag is set true when a Pli message is received. It is cleared once + // the FrameSenderImpl scheduled an encoded key frame to be sent. + bool picture_lost_at_receiver_ = false; + + // Should send the target playout delay with the next frame. + bool send_target_playout_delay_ = false; + + // Returns the maximum media duration currently allowed in-flight. This + // fluctuates in response to the currently-measured network latency. + base::TimeDelta GetAllowedInFlightMediaDuration() const; + + // Ring buffers to keep track of recent frame timestamps (both in terms of + // local reference time and RTP media time). These should only be accessed + // through the Record/GetXXX() methods. The index into this ring + // buffer is the lower 8 bits of the FrameId. + base::TimeTicks frame_reference_times_[256]; + RtpTimeTicks frame_rtp_timestamps_[256]; + + // NOTE: Weak pointers must be invalidated before all other member variables. + base::WeakPtrFactory<FrameSenderImpl> weak_factory_{this}; +}; + +} // namespace media::cast + +#endif // MEDIA_CAST_SENDER_FRAME_SENDER_IMPL_H_
diff --git a/media/cast/sender/performance_metrics_overlay.cc b/media/cast/sender/performance_metrics_overlay.cc index 04619ac..b242cb1 100644 --- a/media/cast/sender/performance_metrics_overlay.cc +++ b/media/cast/sender/performance_metrics_overlay.cc
@@ -221,7 +221,7 @@ int target_bitrate, int frames_ago, double encoder_utilization, - double lossy_utilization, + double lossiness, scoped_refptr<VideoFrame> source) { if (!VLOG_IS_ON(1)) return source; @@ -334,8 +334,7 @@ // Line 1: Recent utilization metrics. const int encoder_pct = base::saturated_cast<int>(encoder_utilization * 100.0 + 0.5); - const int lossy_pct = - base::saturated_cast<int>(lossy_utilization * 100.0 + 0.5); + const int lossy_pct = base::saturated_cast<int>(lossiness * 100.0 + 0.5); RenderLineOfText(base::StringPrintf("%d %3.1d%% %3.1d%%", frames_ago, encoder_pct, lossy_pct), top, frame.get());
diff --git a/media/cast/sender/performance_metrics_overlay.h b/media/cast/sender/performance_metrics_overlay.h index 774effe..f770a97d7 100644 --- a/media/cast/sender/performance_metrics_overlay.h +++ b/media/cast/sender/performance_metrics_overlay.h
@@ -71,7 +71,7 @@ int target_bitrate, int frames_ago, double encoder_utilization, - double lossy_utilization, + double lossiness, scoped_refptr<VideoFrame> source); } // namespace cast
diff --git a/media/cast/sender/video_sender.cc b/media/cast/sender/video_sender.cc index 965a19f76..187051d 100644 --- a/media/cast/sender/video_sender.cc +++ b/media/cast/sender/video_sender.cc
@@ -18,8 +18,7 @@ #include "media/cast/net/cast_transport_config.h" #include "media/cast/sender/performance_metrics_overlay.h" -namespace media { -namespace cast { +namespace media::cast { namespace { @@ -28,24 +27,27 @@ // a combination of cast_benchmark runs and manual testing. // // This is how many round trips we think we need on the network. -const int kRoundTripsNeeded = 4; +constexpr int kRoundTripsNeeded = 4; // This is an estimate of all the the constant time needed independent of // network quality (e.g., additional time that accounts for encode and decode // time). -const int kConstantTimeMs = 75; +constexpr int kConstantTimeMs = 75; // The target maximum utilization of the encoder and network resources. This is // used to attenuate the actual measured utilization values in order to provide // "breathing room" (i.e., to ensure there will be sufficient CPU and bandwidth // available to handle the occasional more-complex frames). -const int kTargetUtilizationPercentage = 75; +constexpr int kTargetUtilizationPercentage = 75; -// This is the minimum duration in milliseconds that the sender sends key frame -// request to the encoder on receiving Pli messages. This is used to prevent -// sending multiple requests while the sender is waiting for an encoded key -// frame or receiving multiple Pli messages in a short period. -const int64_t kMinKeyFrameRequestOnPliIntervalMs = 500; +// This is the minimum duration that the sender sends key frame to the encoder +// on receiving Pli messages. This is used to prevent sending multiple requests +// while the sender is waiting for an encoded key frame or receiving multiple +// Pli messages in a short period. +constexpr base::TimeDelta kMinKeyFrameRequestInterval = base::Milliseconds(500); + +// This is the minimum amount of frames between issuing key frame requests. +constexpr int kMinKeyFrameRequestFrameInterval = 6; // Extract capture begin/end timestamps from |video_frame|'s metadata and log // it. @@ -94,24 +96,16 @@ CastTransport* const transport_sender, PlayoutDelayChangeCB playout_delay_change_cb, media::VideoCaptureFeedbackCB feedback_callback) - : FrameSender( - cast_environment, - transport_sender, - video_config, - video_config.use_external_encoder - ? NewFixedCongestionControl( - (video_config.min_bitrate + video_config.max_bitrate) / 2) - : NewAdaptiveCongestionControl(cast_environment->Clock(), - video_config.max_bitrate, - video_config.min_bitrate, - video_config.max_frame_rate)), - frames_in_encoder_(0), - last_bitrate_(0), + : frame_sender_(FrameSender::Create(cast_environment, + video_config, + transport_sender, + this)), + cast_environment_(cast_environment), + min_playout_delay_(video_config.min_playout_delay), + max_playout_delay_(video_config.max_playout_delay), + animated_playout_delay_(video_config.animated_playout_delay), playout_delay_change_cb_(std::move(playout_delay_change_cb)), - feedback_cb_(feedback_callback), - low_latency_mode_(false), - last_reported_encoder_utilization_(-1.0), - last_reported_lossy_utilization_(-1.0) { + feedback_cb_(feedback_callback) { video_encoder_ = VideoEncoder::Create(cast_environment_, video_config, status_change_cb, create_vea_cb); if (!video_encoder_) { @@ -172,13 +166,14 @@ // Request a key frame when a Pli message was received, and it has been passed // long enough from the last time sending key frame request on receiving a Pli // message. - if (picture_lost_at_receiver_) { - const int64_t min_attemp_interval_ms = - std::max(kMinKeyFrameRequestOnPliIntervalMs, - 6 * target_playout_delay_.InMilliseconds()); + if (frame_sender_->NeedsKeyFrame()) { + const base::TimeDelta min_attempt_interval = std::max( + kMinKeyFrameRequestInterval, + kMinKeyFrameRequestFrameInterval * frame_sender_->TargetPlayoutDelay()); + if (last_time_attempted_to_resolve_pli_.is_null() || - ((reference_time - last_time_attempted_to_resolve_pli_) - .InMilliseconds() > min_attemp_interval_ms)) { + ((reference_time - last_time_attempted_to_resolve_pli_) > + min_attempt_interval)) { video_encoder_->GenerateKeyFrame(); last_time_attempted_to_resolve_pli_ = reference_time; } @@ -186,22 +181,23 @@ // Two video frames are needed to compute the exact media duration added by // the next frame. If there are no frames in the encoder, compute a guess - // based on the configured |max_frame_rate_|. Any error introduced by this + // based on the configured max frame rate. Any error introduced by this // guess will be eliminated when |duration_in_encoder_| is updated in // OnEncodedVideoFrame(). const base::TimeDelta duration_added_by_next_frame = frames_in_encoder_ > 0 ? reference_time - last_enqueued_frame_reference_time_ - : base::Seconds(1.0 / max_frame_rate_); + : base::Seconds(1.0 / frame_sender_->MaxFrameRate()); - if (ShouldDropNextFrame(duration_added_by_next_frame)) { + if (frame_sender_->ShouldDropNextFrame(duration_added_by_next_frame)) { base::TimeDelta new_target_delay = - std::min(current_round_trip_time_ * kRoundTripsNeeded + + std::min(frame_sender_->CurrentRoundTripTime() * kRoundTripsNeeded + base::Milliseconds(kConstantTimeMs), max_playout_delay_); // In case of low latency mode, we prefer frame drops over increasing // playout time. - if (!low_latency_mode_ && new_target_delay > target_playout_delay_) { + if (!low_latency_mode_ && + new_target_delay > frame_sender_->TargetPlayoutDelay()) { // In case we detect user is no longer in a low latency mode and there is // a need to drop a frame, we ensure the playout delay is at-least the // the starting value for playing animated content. @@ -234,8 +230,9 @@ return; } - const int bitrate = congestion_control_->GetBitrate( - reference_time + target_playout_delay_, target_playout_delay_); + const int bitrate = frame_sender_->GetSuggestedBitrate( + reference_time + frame_sender_->TargetPlayoutDelay(), + frame_sender_->TargetPlayoutDelay()); if (bitrate != last_bitrate_) { video_encoder_->SetBitRate(bitrate); last_bitrate_ = bitrate; @@ -245,13 +242,13 @@ const scoped_refptr<VideoFrame> frame_to_encode = MaybeRenderPerformanceMetricsOverlay( - GetTargetPlayoutDelay(), low_latency_mode_, bitrate, + frame_sender_->GetTargetPlayoutDelay(), low_latency_mode_, bitrate, frames_in_encoder_ + 1, last_reported_encoder_utilization_, - last_reported_lossy_utilization_, std::move(video_frame)); + last_reported_lossiness_, std::move(video_frame)); if (video_encoder_->EncodeVideoFrame( frame_to_encode, reference_time, base::BindOnce(&VideoSender::OnEncodedVideoFrame, AsWeakPtr(), - frame_to_encode, bitrate))) { + frame_to_encode))) { TRACE_EVENT_NESTABLE_ASYNC_BEGIN1( "cast.stream", "Video Encode", TRACE_ID_LOCAL(frame_to_encode.get()), "rtp_timestamp", rtp_timestamp.lower_32_bits()); @@ -271,6 +268,15 @@ return video_encoder_ ? video_encoder_->CreateVideoFrameFactory() : nullptr; } +void VideoSender::SetTargetPlayoutDelay( + base::TimeDelta new_target_playout_delay) { + frame_sender_->SetTargetPlayoutDelay(new_target_playout_delay); +} + +base::TimeDelta VideoSender::GetTargetPlayoutDelay() const { + return frame_sender_->GetTargetPlayoutDelay(); +} + base::WeakPtr<VideoSender> VideoSender::AsWeakPtr() { return weak_factory_.GetWeakPtr(); } @@ -279,19 +285,12 @@ return frames_in_encoder_; } -base::TimeDelta VideoSender::GetInFlightMediaDuration() const { - if (GetUnacknowledgedFrameCount() > 0) { - const FrameId oldest_unacked_frame_id = latest_acked_frame_id_ + 1; - return last_enqueued_frame_reference_time_ - - GetRecordedReferenceTime(oldest_unacked_frame_id); - } else { - return duration_in_encoder_; - } +base::TimeDelta VideoSender::GetEncoderBacklogDuration() const { + return duration_in_encoder_; } void VideoSender::OnEncodedVideoFrame( scoped_refptr<media::VideoFrame> video_frame, - int encoder_bitrate, std::unique_ptr<SenderEncodedFrame> encoded_frame) { DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); @@ -306,19 +305,18 @@ last_enqueued_frame_reference_time_ - encoded_frame->reference_time; last_reported_encoder_utilization_ = encoded_frame->encoder_utilization; - last_reported_lossy_utilization_ = encoded_frame->lossy_utilization; + last_reported_lossiness_ = encoded_frame->lossiness; TRACE_EVENT_NESTABLE_ASYNC_END2( "cast.stream", "Video Encode", TRACE_ID_LOCAL(video_frame.get()), - "encoder_utilization", last_reported_encoder_utilization_, - "lossy_utilization", last_reported_lossy_utilization_); + "encoder_utilization", last_reported_encoder_utilization_, "lossiness", + last_reported_lossiness_); // Report the resource utilization for processing this frame. Take the // greater of the two utilization values and attenuate them such that the // target utilization is reported as the maximum sustainable amount. const double attenuated_utilization = - std::max(last_reported_encoder_utilization_, - last_reported_lossy_utilization_) / + std::max(last_reported_encoder_utilization_, last_reported_lossiness_) / (kTargetUtilizationPercentage / 100.0); if (attenuated_utilization >= 0.0) { // Key frames are artificially capped to 1.0 because their actual @@ -333,8 +331,7 @@ feedback_cb_.Run(feedback); } - SendEncodedFrame(encoder_bitrate, std::move(encoded_frame)); + frame_sender_->EnqueueFrame(std::move(encoded_frame)); } -} // namespace cast -} // namespace media +} // namespace media::cast
diff --git a/media/cast/sender/video_sender.h b/media/cast/sender/video_sender.h index ff9db1f..c369620 100644 --- a/media/cast/sender/video_sender.h +++ b/media/cast/sender/video_sender.h
@@ -16,14 +16,13 @@ #include "media/cast/cast_config.h" #include "media/cast/cast_sender.h" #include "media/cast/common/rtp_time.h" -#include "media/cast/sender/congestion_control.h" #include "media/cast/sender/frame_sender.h" namespace media { - class VideoFrame; +} -namespace cast { +namespace media::cast { class CastTransport; class VideoEncoder; @@ -37,7 +36,7 @@ // RTCP packets. // Additionally it posts a bunch of delayed tasks to the main thread for various // timeouts. -class VideoSender : public FrameSender { +class VideoSender : public FrameSender::Client { public: VideoSender(scoped_refptr<CastEnvironment> cast_environment, const FrameSenderConfig& video_config, @@ -63,25 +62,36 @@ // the encoder does not have any such capability, returns null. std::unique_ptr<VideoFrameFactory> CreateVideoFrameFactory(); + void SetTargetPlayoutDelay(base::TimeDelta new_target_playout_delay); + base::TimeDelta GetTargetPlayoutDelay() const; + base::WeakPtr<VideoSender> AsWeakPtr(); protected: + // FrameSender::Client overrides. int GetNumberOfFramesInEncoder() const final; - base::TimeDelta GetInFlightMediaDuration() const final; + base::TimeDelta GetEncoderBacklogDuration() const final; + + // Exposed as protected for testing. + FrameSender* frame_sender_for_testing() { return frame_sender_.get(); } private: // Called by the |video_encoder_| with the next EncodedFrame to send. void OnEncodedVideoFrame(scoped_refptr<media::VideoFrame> video_frame, - int encoder_bitrate, std::unique_ptr<SenderEncodedFrame> encoded_frame); + // The backing frame sender implementation. + std::unique_ptr<FrameSender> frame_sender_; + // Encodes media::VideoFrame images into EncodedFrames. Per configuration, // this will point to either the internal software-based encoder or a proxy to // a hardware-based encoder. std::unique_ptr<VideoEncoder> video_encoder_; + scoped_refptr<CastEnvironment> cast_environment_; + // The number of frames queued for encoding, but not yet sent. - int frames_in_encoder_; + int frames_in_encoder_ = 0; // The duration of video queued for encoding, but not yet sent. base::TimeDelta duration_in_encoder_; @@ -92,7 +102,15 @@ // Remember what we set the bitrate to before, no need to set it again if // we get the same value. - int last_bitrate_; + int last_bitrate_ = 0; + + // The total amount of time between a frame's capture/recording on the sender + // and its playback on the receiver (i.e., shown to a user). + base::TimeDelta min_playout_delay_; + base::TimeDelta max_playout_delay_; + + // Starting playout delay when streaming animated content. + base::TimeDelta animated_playout_delay_; PlayoutDelayChangeCB playout_delay_change_cb_; @@ -101,13 +119,13 @@ // Indicates we are operating in a mode where the target playout latency is // low for best user experience. When operating in low latency mode, we // prefer dropping frames over increasing target playout time. - bool low_latency_mode_; + bool low_latency_mode_ = false; // The video encoder's performance metrics as of the last call to // OnEncodedVideoFrame(). See header file comments for SenderEncodedFrame for // an explanation of these values. - double last_reported_encoder_utilization_; - double last_reported_lossy_utilization_; + double last_reported_encoder_utilization_ = -1.0; + double last_reported_lossiness_ = -1.0; // This tracks the time when the request was sent to encoder to encode a key // frame on receiving a Pli message. It is used to limit the sender not @@ -118,7 +136,6 @@ base::WeakPtrFactory<VideoSender> weak_factory_{this}; }; -} // namespace cast -} // namespace media +} // namespace media::cast #endif // MEDIA_CAST_SENDER_VIDEO_SENDER_H_
diff --git a/media/cast/sender/video_sender_unittest.cc b/media/cast/sender/video_sender_unittest.cc index 71c4d84..d515925 100644 --- a/media/cast/sender/video_sender_unittest.cc +++ b/media/cast/sender/video_sender_unittest.cc
@@ -34,8 +34,7 @@ #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" -namespace media { -namespace cast { +namespace media::cast { namespace { static const uint8_t kPixelValue = 123; @@ -126,8 +125,11 @@ base::BindRepeating(&PeerVideoSender::ProcessFeedback, base::Unretained(this))) {} - using VideoSender::OnReceivedCastFeedback; - using VideoSender::OnReceivedPli; + void OnReceivedCastFeedback(const RtcpCastMessage& cast_feedback) { + frame_sender_for_testing()->OnReceivedCastFeedback(cast_feedback); + } + + void OnReceivedPli() { frame_sender_for_testing()->OnReceivedPli(); } void ProcessFeedback(const media::VideoCaptureFeedback& feedback) { feedback_ = feedback; @@ -640,5 +642,4 @@ EXPECT_EQ(2, transport_->number_of_rtp_packets()); } -} // namespace cast -} // namespace media +} // namespace media::cast
diff --git a/media/gpu/android/codec_image_unittest.cc b/media/gpu/android/codec_image_unittest.cc index 70dd296..e55f6858 100644 --- a/media/gpu/android/codec_image_unittest.cc +++ b/media/gpu/android/codec_image_unittest.cc
@@ -52,7 +52,7 @@ gl::init::InitializeStaticGLBindingsImplementation( gl::GLImplementationParts(gl::kGLImplementationEGLGLES2), false); - gl::init::InitializeGLOneOffPlatformImplementation( + display_ = gl::init::InitializeGLOneOffPlatformImplementation( /*fallback_to_software_gl=*/false, /*disable_gl_drawing=*/false, /*init_extensions=*/false, @@ -83,7 +83,7 @@ context_ = nullptr; share_group_ = nullptr; surface_ = nullptr; - gl::init::ShutdownGL(false); + gl::init::ShutdownGL(display_, false); wrapper_->TakeCodecSurfacePair(); } @@ -121,6 +121,7 @@ scoped_refptr<gl::GLShareGroup> share_group_; scoped_refptr<gl::GLSurface> surface_; GLuint texture_id_ = 0; + gl::GLDisplay* display_ = nullptr; class PromotionHintReceiver { public:
diff --git a/media/gpu/vaapi/vaapi_wrapper.cc b/media/gpu/vaapi/vaapi_wrapper.cc index 8baa6b9..9ef5fb1f 100644 --- a/media/gpu/vaapi/vaapi_wrapper.cc +++ b/media/gpu/vaapi/vaapi_wrapper.cc
@@ -497,7 +497,9 @@ bool IsModeEncoding(VaapiWrapper::CodecMode mode) { return mode == VaapiWrapper::CodecMode::kEncodeConstantBitrate || - mode == VaapiWrapper::CodecMode::kEncodeConstantQuantizationParameter; + mode == + VaapiWrapper::CodecMode::kEncodeConstantQuantizationParameter || + mode == VaapiWrapper::CodecMode::kEncodeVariableBitrate; } bool GetNV12VisibleWidthBytes(int visible_width, @@ -956,7 +958,8 @@ VAEntrypointEncSliceLP}, // kEncodeConstantBitrate. {VAEntrypointEncSlice, VAEntrypointEncSliceLP}, // kEncodeConstantQuantizationParameter. - {VAEntrypointVideoProc} // kVideoProcess. + {VAEntrypointEncSlice, VAEntrypointEncSliceLP}, // kEncodeVariableBitrate. + {VAEntrypointVideoProc} // kVideoProcess. }; static_assert(std::size(kAllowedEntryPoints) == VaapiWrapper::kCodecModeMax, ""); @@ -1018,6 +1021,8 @@ required_attribs->push_back({VAConfigAttribRateControl, VA_RC_CBR}); if (mode == VaapiWrapper::kEncodeConstantQuantizationParameter) required_attribs->push_back({VAConfigAttribRateControl, VA_RC_CQP}); + if (mode == VaapiWrapper::kEncodeConstantQuantizationParameter) + required_attribs->push_back({VAConfigAttribRateControl, VA_RC_VBR}); constexpr VAProfile kSupportedH264VaProfilesForEncoding[] = { VAProfileH264ConstrainedBaseline, VAProfileH264Main, VAProfileH264High}; @@ -1191,6 +1196,7 @@ #endif VaapiWrapper::kEncodeConstantBitrate, VaapiWrapper::kEncodeConstantQuantizationParameter, + VaapiWrapper::kEncodeVariableBitrate, VaapiWrapper::kVideoProcess }; static_assert(std::size(kWrapperModes) == VaapiWrapper::kCodecModeMax, ""); @@ -1949,6 +1955,7 @@ #endif case VaapiWrapper::kEncodeConstantBitrate: case VaapiWrapper::kEncodeConstantQuantizationParameter: + case VaapiWrapper::kEncodeVariableBitrate: if (profile == VAProfileJPEGBaseline) return VAEntrypointEncPicture; DCHECK(IsModeEncoding(mode)); @@ -3138,6 +3145,10 @@ DCHECK_NE(va_profile, VAProfileJPEGBaseline) << "JPEG Encoding doesn't support CQP bitrate control"; } + if (mode_ == kEncodeVariableBitrate) { + DCHECK_NE(va_profile, VAProfileJPEGBaseline) + << "JPEG Encoding doesn't support VBR bitrate control"; + } #endif // DCHECK_IS_ON() #if BUILDFLAG(IS_CHROMEOS_ASH)
diff --git a/media/gpu/vaapi/vaapi_wrapper.h b/media/gpu/vaapi/vaapi_wrapper.h index 00e9e9a..d6feb85 100644 --- a/media/gpu/vaapi/vaapi_wrapper.h +++ b/media/gpu/vaapi/vaapi_wrapper.h
@@ -138,6 +138,7 @@ kEncodeConstantBitrate, // Encode with Constant Bitrate algorithm. kEncodeConstantQuantizationParameter, // Encode with Constant Quantization // Parameter algorithm. + kEncodeVariableBitrate, // Encode with variable bitrate algorithm. kVideoProcess, kCodecModeMax, };
diff --git a/media/gpu/windows/d3d11_texture_wrapper_unittest.cc b/media/gpu/windows/d3d11_texture_wrapper_unittest.cc index 3a182d0..313de844 100644 --- a/media/gpu/windows/d3d11_texture_wrapper_unittest.cc +++ b/media/gpu/windows/d3d11_texture_wrapper_unittest.cc
@@ -44,7 +44,7 @@ task_runner_ = task_environment_.GetMainThreadTaskRunner(); - gl::GLSurfaceTestSupport::InitializeOneOffImplementation( + display_ = gl::GLSurfaceTestSupport::InitializeOneOffImplementation( gl::GLImplementationParts(gl::ANGLEImplementation::kD3D11), false); surface_ = gl::init::CreateOffscreenGLSurface(gfx::Size()); share_group_ = new gl::GLShareGroup(); @@ -66,7 +66,7 @@ context_ = nullptr; share_group_ = nullptr; surface_ = nullptr; - gl::init::ShutdownGL(false); + gl::GLSurfaceTestSupport::ShutdownGL(display_); } base::test::TaskEnvironment task_environment_; @@ -83,6 +83,8 @@ // a wrapper. scoped_refptr<FakeCommandBufferHelper> fake_command_buffer_helper_; GetCommandBufferHelperCB get_helper_cb_; + + gl::GLDisplay* display_ = nullptr; }; TEST_F(D3D11TextureWrapperUnittest, NV12InitSucceeds) {
diff --git a/media/mojo/mojom/media_types.mojom b/media/mojo/mojom/media_types.mojom index 3443490..38493d3 100644 --- a/media/mojo/mojom/media_types.mojom +++ b/media/mojo/mojom/media_types.mojom
@@ -492,6 +492,7 @@ array<mojo_base.mojom.Value> frames; StatusData? cause; mojo_base.mojom.Value data; + uint64 packed_root_cause; }; struct EncoderStatus {
diff --git a/media/mojo/mojom/status_mojom_traits.cc b/media/mojo/mojom/status_mojom_traits.cc index 7c88970..3f9433476 100644 --- a/media/mojo/mojom/status_mojom_traits.cc +++ b/media/mojo/mojom/status_mojom_traits.cc
@@ -15,6 +15,7 @@ media::internal::StatusData>::Read(media::mojom::StatusDataDataView data, media::internal::StatusData* output) { output->code = data.code(); + output->packed_root_cause = data.packed_root_cause(); if (!data.ReadGroup(&output->group)) return false;
diff --git a/media/mojo/mojom/status_mojom_traits.h b/media/mojo/mojom/status_mojom_traits.h index 7180389..4e21f87 100644 --- a/media/mojo/mojom/status_mojom_traits.h +++ b/media/mojo/mojom/status_mojom_traits.h
@@ -47,6 +47,11 @@ return input.data; } + static media::UKMPackedType packed_root_cause( + const media::internal::StatusData& input) { + return input.packed_root_cause; + } + static bool Read(media::mojom::StatusDataDataView data, media::internal::StatusData* output); };
diff --git a/media/renderers/paint_canvas_video_renderer_unittest.cc b/media/renderers/paint_canvas_video_renderer_unittest.cc index 98363ec..fa81b71 100644 --- a/media/renderers/paint_canvas_video_renderer_unittest.cc +++ b/media/renderers/paint_canvas_video_renderer_unittest.cc
@@ -1047,7 +1047,7 @@ using GetColorCallback = base::RepeatingCallback<SkColor(int, int)>; void SetUp() override { - gl::GLSurfaceTestSupport::InitializeOneOff(); + display_ = gl::GLSurfaceTestSupport::InitializeOneOff(); enable_pixels_.emplace(); media_context_ = base::MakeRefCounted<viz::TestInProcessContextProvider>( viz::TestContextType::kGpuRaster, /*support_locking=*/false); @@ -1074,7 +1074,7 @@ media_context_.reset(); enable_pixels_.reset(); viz::TestGpuServiceHolder::ResetInstance(); - gl::GLSurfaceTestSupport::ShutdownGL(); + gl::GLSurfaceTestSupport::ShutdownGL(display_); } // Uses CopyVideoFrameTexturesToGLTexture to copy |frame| into a GL texture, @@ -1237,6 +1237,7 @@ PaintCanvasVideoRenderer renderer_; scoped_refptr<VideoFrame> cropped_frame_; base::test::TaskEnvironment task_environment_; + gl::GLDisplay* display_ = nullptr; }; TEST_F(PaintCanvasVideoRendererWithGLTest, CopyVideoFrameYUVDataToGLTexture) {
diff --git a/net/base/backoff_entry_serializer.cc b/net/base/backoff_entry_serializer.cc index d0f41cd..76015d29 100644 --- a/net/base/backoff_entry_serializer.cc +++ b/net/base/backoff_entry_serializer.cc
@@ -36,10 +36,10 @@ base::Value BackoffEntrySerializer::SerializeToValue(const BackoffEntry& entry, base::Time time_now) { - std::vector<base::Value> serialized; - serialized.emplace_back(SerializationFormatVersion::kVersion2); + base::Value::List serialized; + serialized.Append(SerializationFormatVersion::kVersion2); - serialized.emplace_back(entry.failure_count()); + serialized.Append(entry.failure_count()); // Convert both |base::TimeTicks| values into |base::TimeDelta| values by // subtracting |kZeroTicks. This way, the top-level subtraction uses @@ -64,9 +64,8 @@ // Redundantly stores both the remaining time delta and the absolute time. // The delta is used to work around some cases where wall clock time changes. - serialized.emplace_back( - base::NumberToString(backoff_duration.InMicroseconds())); - serialized.emplace_back( + serialized.Append(base::NumberToString(backoff_duration.InMicroseconds())); + serialized.Append( base::NumberToString(absolute_release_time.ToInternalValue())); return base::Value(std::move(serialized)); @@ -79,20 +78,20 @@ base::Time time_now) { if (!serialized.is_list()) return nullptr; - const base::Value::ConstListView& list_view = serialized.GetListDeprecated(); + const base::Value::List& list = serialized.GetList(); - if (list_view.size() != 4) + if (list.size() != 4) return nullptr; - if (!list_view[0].is_int()) + if (!list[0].is_int()) return nullptr; - int version_number = list_view[0].GetInt(); + int version_number = list[0].GetInt(); if (version_number != kVersion1 && version_number != kVersion2) return nullptr; - if (!list_view[1].is_int()) + if (!list[1].is_int()) return nullptr; - int failure_count = list_view[1].GetInt(); + int failure_count = list[1].GetInt(); if (failure_count < 0) { return nullptr; } @@ -101,17 +100,17 @@ base::TimeDelta original_backoff_duration; switch (version_number) { case kVersion1: { - if (!list_view[2].is_double()) + if (!list[2].is_double()) return nullptr; - double original_backoff_duration_double = list_view[2].GetDouble(); + double original_backoff_duration_double = list[2].GetDouble(); original_backoff_duration = base::Seconds(original_backoff_duration_double); break; } case kVersion2: { - if (!list_view[2].is_string()) + if (!list[2].is_string()) return nullptr; - std::string original_backoff_duration_string = list_view[2].GetString(); + std::string original_backoff_duration_string = list[2].GetString(); int64_t original_backoff_duration_us; if (!base::StringToInt64(original_backoff_duration_string, &original_backoff_duration_us)) { @@ -125,9 +124,9 @@ NOTREACHED() << "Unexpected version_number: " << version_number; } - if (!list_view[3].is_string()) + if (!list[3].is_string()) return nullptr; - std::string absolute_release_time_string = list_view[3].GetString(); + std::string absolute_release_time_string = list[3].GetString(); int64_t absolute_release_time_us; if (!base::StringToInt64(absolute_release_time_string,
diff --git a/net/http/transport_security_state_static.json b/net/http/transport_security_state_static.json index b4c3af22b..4daa172 100644 --- a/net/http/transport_security_state_static.json +++ b/net/http/transport_security_state_static.json
@@ -273,6 +273,7 @@ { "name": "boo", "policy": "public-suffix", "mode": "force-https", "include_subdomains": true }, { "name": "dad", "policy": "public-suffix", "mode": "force-https", "include_subdomains": true }, { "name": "day", "policy": "public-suffix", "mode": "force-https", "include_subdomains": true }, + { "name": "channel", "policy": "public-suffix", "mode": "force-https", "include_subdomains": true }, // Domains on the public suffix list that have requested to be preloaded. { "name": "bmoattachments.org", "policy": "public-suffix", "mode": "force-https", "include_subdomains": true }, { "name": "now.sh", "policy": "public-suffix", "mode": "force-https", "include_subdomains": true }, @@ -167308,6 +167309,280 @@ { "name": "wrentham.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, { "name": "wrenthamfire.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, { "name": "wrenthampolice.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "acsd-az.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "akronohiorescue.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "aledoil.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "allendalecounty.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "almenatownship.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "altoona-wi.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "arcoidaho.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "barnesvilleohio.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "belknapcounty.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "bergenfieldnj.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "berkeleyca.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "blanchardla.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "blnc.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "bloomingtonelectionsil.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "blounttn.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "boscawennh.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "boscobelwi.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "boydwi.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "brain.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "brightonvt.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "brillionwi.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "broadwayva.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "brookvillepoliceny.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "calcasieusheriff.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "canjo.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "canterburynh.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "capecod.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "carlislecounty.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "casscountyil.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "ccuanj.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "chaskamn.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "cherrycountyne.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "chestercountysc.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "cicerony.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "ciceronypd.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "cityofbayminetteal.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "cityofclare.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "cityofdelafieldwi.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "cityoflancasterpa.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "cityoflodiwi.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "cityofnewkirkok.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "cityoftybee.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "clallamcountywa.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "clatsopcounty.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "clevelandheights.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "clintoncomo.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "clintontwpnj.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "colburnadamswi.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "colquittcountyga.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "contracostacr.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "contracostacre.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "contracostavote.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "coralsprings.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "cortezsanitation.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "countyofbarton.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "covid.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "cskt.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "dadecountymo911.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "danvillein.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "darientownwi.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "dauphincounty.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "dawsoncountyga.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "dawsonmt.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "dekalbcountymo.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "dewittcountyil.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "dewittmi.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "dfspdfl.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "douglascountymn.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "driveelectric.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "eaglevilletn.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "eastgrmi.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "eauclairecounty.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "edgarcountyillinois.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "elgintexas.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "erwinmi.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "eurekaca.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "fairfaxcountypartners.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "fairleevt.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "falmouthpublicsafetyma.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "fayettecountyillinois.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "fbilab.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "fortatkinsonia.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "franklin-townshipohio.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "franklincountync.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "franklincountyne.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "franklintownshipcarbonpa.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "fultoncountyilelections.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "garfieldcountyne.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "garibaldi.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "gayga.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "gilbertsvilleny.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "gloucesterva.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "graftoncountynh.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "grantcountyne.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "greatfallsmt.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "greeleycountyne.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "greensborovt.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "greenwoodny.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "guilfordvt.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "gwinnettcounty.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "halescornerswi.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "hamiltonil.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "hamiltonvotesfl.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "hardincountyky.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "hardincountyohio.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "hardwickvt.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "harfordvotes.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "hebronnh.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "holmenwi.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "illinoiscourtscommission.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "indianlakepa.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "indtwpmi.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "iowasmokefreeair.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "jcrhcdwy.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "jcsava.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "jonescreektx.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "kanecountyil.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "kendallvillein.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "kenockeetownshipmi.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "kentcountyde.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "kewauneeco.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "kimballcountyne.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "knoxcountyne.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "lakesaintlouismo.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "laketanglewoodtx.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "landolakeswi.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "lasallecountyil.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "laschoolpolice.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "lawrencecountysd.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "lebanondodgewi.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "leecountyfl.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "lemontownshippa.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "libertygrovewi.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "limatownshipmi.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "lincolnri.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "loganengineeroh.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "lynwoodca.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "lyontwp-higginsmi.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "macoupinvotes.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "magnoliawi.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "mapletonmn.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "maplewoodnj.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "masoncountywaelections.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "masonkysheriff.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "mbtaadvisoryboard.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "mdbre.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "medfordwi.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "medicalbillrights.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "mercercountypa.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "millcreekwa.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "monmouthmaine.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "monroecountyny-opi.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "montcalmcountymi.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "montgomerycountyil.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "morgancounty-il.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "mountvernonin.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "mtsafe.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "mullett-townshipmi.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "murphysfire.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "mwmopd.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "natronaincident.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "natronasheriff.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "neenahwi.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "nemo911.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "nettletonms.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "newbraunfels.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "nextlegends.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "nuckollscountyne.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "nvcmaine.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "ocalapd.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "oconlakewi.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "owyheecounty.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "ozaukeecounty.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "panamacity.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "pbcpao.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "pembinewi.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "pennsauken.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "permarisk.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "perrycountyal.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "pewamo.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "pikecountyil.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "pinerivermi.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "pittsvillemd.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "pointpleasantbeachnj.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "portofedmonds.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "portofephrata.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "portofnenana.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "pottsvillepa.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "putnamvalley.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "randolphcountyil.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "ridgewaywi.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "rockfallsil.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "rockporttx.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "rockspringswi.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "rosstownshipmi.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "salemma.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "sarcoxiemo.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "scituateri.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "scottsvalley.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "sharonnh.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "shastacounty.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "silverlakeks.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "southsideplacetx.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "springdalewi.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "srbc.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "stevenscountymn.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "stevensonal.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "stgeorgeks.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "stmaryswv.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "stonewallwcidtx.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "tazewell-il.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "tazewellcountyjury.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "thebraininitiative.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "thurstonauditor.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "thurstonwavotes.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "tobnm.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "tomahwi.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "townofavonwi.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "townofbarneswi.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "townofbrothertownwi.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "townofbrunswickwi.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "townofclearfieldwi.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "townofclymanwi.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "townofcoldspringny.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "townofdecaturwi.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "townofdrammen-wi.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "townofdraperwi.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "townofexcelsiorwi.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "townofforestvernonwi.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "townofgraftonwi.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "townofgrantshawanowi.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "townofhamburgny.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "townofhustisford.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "townoflomira.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "townofmertonwi.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "townofnevawi.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "townofrichmond-walworthwi.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "townofrudolphwi.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "townofthreelakeswi.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "townofuniondoorwi.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "townofvarnamtown.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "townofwarrensccwi.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "townofwendellnc.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "townofwestfordwi.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "townofwrightstownwi.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "vamoneysearch.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "vandenbroekwi.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "villageofarenawi.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "villageofbellevuewi.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "villageofcatskillny.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "villageofcolemanwi.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "villageoflomira.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "villageofnaponeene.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "villageofwales.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "vintonia.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "vofwittenbergwi.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "votebradfordfl.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "voteclaycountymo.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "votehighlands.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "wadleyga.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "warrencountyia.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "warrencountyva.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "washingtoncountytx.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "washtenaw.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "watervillevalleynh.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "wausharacountywi.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "westburlingtoniowa.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "westlibertykypolice.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "westsalemwi.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "wfbvillage.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "whitehallal.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "williamstownmi.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "ycfld.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, + { "name": "ylwd.gov", "policy": "public-suffix-requested", "mode": "force-https", "include_subdomains": true }, // END OF ETLD-OWNER REQUESTED ENTRIES // To avoid trailing comma changes from showing up in diffs, we place a
diff --git a/remoting/host/input_injector_x11.cc b/remoting/host/input_injector_x11.cc index 47cc303..a895fb9 100644 --- a/remoting/host/input_injector_x11.cc +++ b/remoting/host/input_injector_x11.cc
@@ -353,7 +353,7 @@ const std::string text = event.text(); for (int32_t index = 0; index < static_cast<int32_t>(text.size()); ++index) { - uint32_t code_point; + base_icu::UChar32 code_point; if (!base::ReadUnicodeCharacter(text.c_str(), text.size(), &index, &code_point)) { continue;
diff --git a/remoting/host/installer/win/chromoting.wxs b/remoting/host/installer/win/chromoting.wxs index b409b3a..1ac2d5e2 100644 --- a/remoting/host/installer/win/chromoting.wxs +++ b/remoting/host/installer/win/chromoting.wxs
@@ -864,6 +864,12 @@ Execute="deferred" Impersonate="no" Return="ignore"/> + <CustomAction Id="kill_webauthn_native_message_host" + Directory="binaries" + ExeCommand='cmd /c taskkill /T /F /IM remote_webauthn.exe' + Execute="deferred" + Impersonate="no" + Return="ignore"/> <InstallExecuteSequence> <!-- Set REINSTALL=all and REINSTALLMODE=amus if the user reruns the @@ -904,6 +910,10 @@ <Custom Action="kill_me2me_native_message_host" After="InstallInitialize"> Installed </Custom> + <Custom Action="kill_webauthn_native_message_host" + After="InstallInitialize"> + Installed + </Custom> <Custom Action="make_symbolic_link" After="InstallFiles"> NOT Installed </Custom>
diff --git a/services/network/expect_ct_reporter.cc b/services/network/expect_ct_reporter.cc index 3f1ca5f..229848a 100644 --- a/services/network/expect_ct_reporter.cc +++ b/services/network/expect_ct_reporter.cc
@@ -51,16 +51,16 @@ return false; } -base::ListValue GetPEMEncodedChainAsList( +base::Value::List GetPEMEncodedChainAsList( const net::X509Certificate* cert_chain) { if (!cert_chain) - return base::ListValue(); + return base::Value::List(); - base::ListValue result; + base::Value::List result; std::vector<std::string> pem_encoded_chain; cert_chain->GetPEMEncodedChain(&pem_encoded_chain); - for (const std::string& cert : pem_encoded_chain) - result.Append(cert); + for (std::string& cert : pem_encoded_chain) + result.Append(std::move(cert)); return result; } @@ -81,7 +81,7 @@ } bool AddSCT(const net::SignedCertificateTimestampAndStatus& sct, - base::ListValue* list) { + base::Value::List* list) { base::Value::Dict list_item; // Chrome implements RFC6962, not 6962-bis, so the reports contain v1 SCTs. list_item.Set("version", 1); @@ -166,25 +166,26 @@ if (!base::FeatureList::IsEnabled(features::kExpectCTReporting)) return; - base::Value outer_report(base::Value::Type::DICTIONARY); - base::Value* report = outer_report.SetKey( - "expect-ct-report", base::Value(base::Value::Type::DICTIONARY)); - report->SetStringKey("hostname", host_port_pair.host()); - report->SetIntKey("port", host_port_pair.port()); - report->SetStringKey("date-time", base::TimeToISO8601(base::Time::Now())); - report->SetStringKey("effective-expiration-date", - base::TimeToISO8601(expiration)); - report->SetKey("served-certificate-chain", - GetPEMEncodedChainAsList(served_certificate_chain)); - report->SetKey("validated-certificate-chain", - GetPEMEncodedChainAsList(validated_certificate_chain)); + base::Value::Dict outer_report; - base::ListValue scts; + base::Value::Dict report; + report.Set("hostname", host_port_pair.host()); + report.Set("port", host_port_pair.port()); + report.Set("date-time", base::TimeToISO8601(base::Time::Now())); + report.Set("effective-expiration-date", base::TimeToISO8601(expiration)); + report.Set("served-certificate-chain", + GetPEMEncodedChainAsList(served_certificate_chain)); + report.Set("validated-certificate-chain", + GetPEMEncodedChainAsList(validated_certificate_chain)); + + base::Value::List scts; for (const auto& sct_and_status : signed_certificate_timestamps) { if (!AddSCT(sct_and_status, &scts)) LOG(ERROR) << "Failed to add signed certificate timestamp to list"; } - report->SetKey("scts", std::move(scts)); + report.Set("scts", std::move(scts)); + + outer_report.Set("expect-ct-report", std::move(report)); std::string serialized_report; if (!base::JSONWriter::Write(outer_report, &serialized_report)) {
diff --git a/services/network/expect_ct_reporter_unittest.cc b/services/network/expect_ct_reporter_unittest.cc index a2a2dd13..6f914c0 100644 --- a/services/network/expect_ct_reporter_unittest.cc +++ b/services/network/expect_ct_reporter_unittest.cc
@@ -132,10 +132,9 @@ // in |chain|. void CheckReportCertificateChain( const scoped_refptr<net::X509Certificate>& expected_cert, - const base::ListValue& chain) { + const base::Value::List& chain_list) { std::vector<std::string> pem_encoded_chain; expected_cert->GetPEMEncodedChain(&pem_encoded_chain); - base::Value::ConstListView chain_list = chain.GetListDeprecated(); ASSERT_EQ(pem_encoded_chain.size(), chain_list.size()); for (size_t i = 0; i < pem_encoded_chain.size(); i++) { @@ -163,44 +162,43 @@ ::testing::AssertionResult FindSCTInReportList( const scoped_refptr<net::ct::SignedCertificateTimestamp>& expected_sct, net::ct::SCTVerifyStatus expected_status, - const base::ListValue& report_list) { + const base::Value::List& report_list) { std::string expected_serialized_sct; if (!net::ct::EncodeSignedCertificateTimestamp(expected_sct, &expected_serialized_sct)) { return ::testing::AssertionFailure() << "Failed to serialize SCT"; } - for (const base::Value& report_sct_value : report_list.GetListDeprecated()) { + for (const base::Value& report_sct_value : report_list) { if (!report_sct_value.is_dict()) { return ::testing::AssertionFailure() << "Failed to get dictionary value from report SCT list"; } - const base::DictionaryValue& report_sct = - base::Value::AsDictionaryValue(report_sct_value); - std::string serialized_sct; - EXPECT_TRUE(report_sct.GetString("serialized_sct", &serialized_sct)); + const base::Value::Dict& report_sct = report_sct_value.GetDict(); + const std::string* serialized_sct = report_sct.FindString("serialized_sct"); + EXPECT_TRUE(serialized_sct); std::string decoded_serialized_sct; - EXPECT_TRUE(base::Base64Decode(serialized_sct, &decoded_serialized_sct)); + EXPECT_TRUE(base::Base64Decode(*serialized_sct, &decoded_serialized_sct)); if (decoded_serialized_sct != expected_serialized_sct) continue; - std::string source; - EXPECT_TRUE(report_sct.GetString("source", &source)); - EXPECT_EQ(expected_sct->origin, SCTOriginStringToOrigin(source)); + const std::string* source = report_sct.FindString("source"); + EXPECT_TRUE(source); + EXPECT_EQ(expected_sct->origin, SCTOriginStringToOrigin(*source)); - std::string report_status; - EXPECT_TRUE(report_sct.GetString("status", &report_status)); + const std::string* report_status = report_sct.FindString("status"); + EXPECT_TRUE(report_status); switch (expected_status) { case net::ct::SCT_STATUS_LOG_UNKNOWN: - EXPECT_EQ("unknown", report_status); + EXPECT_EQ("unknown", *report_status); break; case net::ct::SCT_STATUS_INVALID_SIGNATURE: case net::ct::SCT_STATUS_INVALID_TIMESTAMP: { - EXPECT_EQ("invalid", report_status); + EXPECT_EQ("invalid", *report_status); break; } case net::ct::SCT_STATUS_OK: { - EXPECT_EQ("valid", report_status); + EXPECT_EQ("valid", *report_status); break; } case net::ct::SCT_STATUS_NONE: @@ -216,8 +214,8 @@ // from an Expect CT report. void CheckReportSCTs( const net::SignedCertificateTimestampAndStatusList& expected_scts, - const base::ListValue& scts) { - EXPECT_EQ(expected_scts.size(), scts.GetListDeprecated().size()); + const base::Value::List& scts) { + EXPECT_EQ(expected_scts.size(), scts.size()); for (const auto& expected_sct : expected_scts) { ASSERT_TRUE( FindSCTInReportList(expected_sct.sct, expected_sct.status, scts)); @@ -232,44 +230,41 @@ const net::HostPortPair& host_port, const std::string& expiration, const net::SSLInfo& ssl_info) { - std::unique_ptr<base::Value> value( - base::JSONReader::ReadDeprecated(serialized_report)); + absl::optional<base::Value> value(base::JSONReader::Read(serialized_report)); ASSERT_TRUE(value); - ASSERT_TRUE(value->is_dict()); - base::DictionaryValue* outer_report_dict; - ASSERT_TRUE(value->GetAsDictionary(&outer_report_dict)); + base::Value::Dict* outer_report_dict = value->GetIfDict(); + ASSERT_TRUE(outer_report_dict); - base::DictionaryValue* report_dict; - ASSERT_TRUE( - outer_report_dict->GetDictionary("expect-ct-report", &report_dict)); + base::Value::Dict* report_dict = + outer_report_dict->FindDict("expect-ct-report"); + ASSERT_TRUE(report_dict); - std::string report_hostname; - EXPECT_TRUE(report_dict->GetString("hostname", &report_hostname)); - EXPECT_EQ(host_port.host(), report_hostname); - int report_port; - EXPECT_TRUE(report_dict->GetInteger("port", &report_port)); + std::string* report_hostname = report_dict->FindString("hostname"); + EXPECT_TRUE(report_hostname); + EXPECT_EQ(host_port.host(), *report_hostname); + absl::optional<int> report_port = report_dict->FindInt("port"); EXPECT_EQ(host_port.port(), report_port); - std::string report_expiration; - EXPECT_TRUE( - report_dict->GetString("effective-expiration-date", &report_expiration)); - EXPECT_EQ(expiration, report_expiration); + std::string* report_expiration = + report_dict->FindString("effective-expiration-date"); + EXPECT_TRUE(report_expiration); + EXPECT_EQ(expiration, *report_expiration); - const base::ListValue* report_served_certificate_chain = nullptr; - ASSERT_TRUE(report_dict->GetList("served-certificate-chain", - &report_served_certificate_chain)); + const base::Value::List* report_served_certificate_chain = + report_dict->FindList("served-certificate-chain"); + ASSERT_TRUE(report_served_certificate_chain); ASSERT_NO_FATAL_FAILURE(CheckReportCertificateChain( ssl_info.unverified_cert, *report_served_certificate_chain)); - const base::ListValue* report_validated_certificate_chain = nullptr; - ASSERT_TRUE(report_dict->GetList("validated-certificate-chain", - &report_validated_certificate_chain)); + const base::Value::List* report_validated_certificate_chain = + report_dict->FindList("validated-certificate-chain"); + ASSERT_TRUE(report_validated_certificate_chain); ASSERT_NO_FATAL_FAILURE(CheckReportCertificateChain( ssl_info.cert, *report_validated_certificate_chain)); - const base::ListValue* report_scts = nullptr; - ASSERT_TRUE(report_dict->GetList("scts", &report_scts)); + const base::Value::List* report_scts = report_dict->FindList("scts"); + ASSERT_TRUE(report_scts); ASSERT_NO_FATAL_FAILURE( CheckReportSCTs(ssl_info.signed_certificate_timestamps, *report_scts));
diff --git a/services/network/network_context.cc b/services/network/network_context.cc index 812cfeb..52f1d8b1f 100644 --- a/services/network/network_context.cc +++ b/services/network/network_context.cc
@@ -1506,7 +1506,7 @@ const std::string& domain, const net::NetworkIsolationKey& network_isolation_key, GetExpectCTStateCallback callback) { - base::Value result(base::Value::Type::DICTIONARY); + base::Value::Dict result; if (base::IsStringASCII(domain)) { net::TransportSecurityState* transport_security_state = url_request_context()->transport_security_state(); @@ -1517,26 +1517,26 @@ // TODO(estark): query static Expect-CT state as well. if (found) { - result.SetStringKey("dynamic_expect_ct_domain", domain); - result.SetDoubleKey("dynamic_expect_ct_observed", - dynamic_expect_ct_state.last_observed.ToDoubleT()); - result.SetDoubleKey("dynamic_expect_ct_expiry", - dynamic_expect_ct_state.expiry.ToDoubleT()); - result.SetBoolKey("dynamic_expect_ct_enforce", - dynamic_expect_ct_state.enforce); - result.SetStringKey("dynamic_expect_ct_report_uri", - dynamic_expect_ct_state.report_uri.spec()); + result.Set("dynamic_expect_ct_domain", domain); + result.Set("dynamic_expect_ct_observed", + dynamic_expect_ct_state.last_observed.ToDoubleT()); + result.Set("dynamic_expect_ct_expiry", + dynamic_expect_ct_state.expiry.ToDoubleT()); + result.Set("dynamic_expect_ct_enforce", + dynamic_expect_ct_state.enforce); + result.Set("dynamic_expect_ct_report_uri", + dynamic_expect_ct_state.report_uri.spec()); } - result.SetBoolKey("result", found); + result.Set("result", found); } else { - result.SetStringKey("error", "no Expect-CT state active"); + result.Set("error", "no Expect-CT state active"); } } else { - result.SetStringKey("error", "non-ASCII domain name"); + result.Set("error", "non-ASCII domain name"); } - std::move(callback).Run(std::move(result)); + std::move(callback).Run(base::Value(std::move(result))); } void NetworkContext::MaybeEnqueueSCTReport(
diff --git a/services/network/network_context_unittest.cc b/services/network/network_context_unittest.cc index 72d8e6c..29f18b24 100644 --- a/services/network/network_context_unittest.cc +++ b/services/network/network_context_unittest.cc
@@ -4860,10 +4860,9 @@ run_loop.Run(); EXPECT_TRUE(state.is_dict()); - const base::Value* result = - state.FindKeyOfType("result", base::Value::Type::BOOLEAN); - ASSERT_TRUE(result != nullptr); - EXPECT_FALSE(result->GetBool()); + absl::optional<bool> result = state.GetDict().FindBool("result"); + EXPECT_TRUE(result.has_value()); + EXPECT_EQ(false, *result); } // Add the host data. @@ -4887,25 +4886,24 @@ run_loop.Run(); EXPECT_TRUE(state.is_dict()); - const base::Value* value = state.FindKeyOfType("dynamic_expect_ct_domain", - base::Value::Type::STRING); - ASSERT_TRUE(value != nullptr); - EXPECT_EQ(kTestDomain, value->GetString()); + const base::Value::Dict& dict = state.GetDict(); + const std::string* dynamic_expect_ct_domain = + dict.FindString("dynamic_expect_ct_domain"); + ASSERT_TRUE(dynamic_expect_ct_domain); + EXPECT_EQ(kTestDomain, *dynamic_expect_ct_domain); - value = state.FindKeyOfType("dynamic_expect_ct_expiry", - base::Value::Type::DOUBLE); - ASSERT_TRUE(value != nullptr); - EXPECT_EQ(expiry.ToDoubleT(), value->GetDouble()); + absl::optional<double> dynamic_expect_ct_expiry = + dict.FindDouble("dynamic_expect_ct_expiry"); + EXPECT_EQ(expiry.ToDoubleT(), dynamic_expect_ct_expiry); - value = state.FindKeyOfType("dynamic_expect_ct_enforce", - base::Value::Type::BOOLEAN); - ASSERT_TRUE(value != nullptr); - EXPECT_EQ(enforce, value->GetBool()); + absl::optional<bool> dynamic_expect_ct_enforce = + dict.FindBool("dynamic_expect_ct_enforce"); + EXPECT_EQ(enforce, *dynamic_expect_ct_enforce); - value = state.FindKeyOfType("dynamic_expect_ct_report_uri", - base::Value::Type::STRING); - ASSERT_TRUE(value != nullptr); - EXPECT_EQ(report_uri, value->GetString()); + const std::string* dynamic_expect_ct_report_uri = + dict.FindString("dynamic_expect_ct_report_uri"); + ASSERT_TRUE(dynamic_expect_ct_report_uri); + EXPECT_EQ(report_uri, *dynamic_expect_ct_report_uri); } // Using a different NetworkIsolationKey should return no result. @@ -4918,10 +4916,9 @@ run_loop.Run(); EXPECT_TRUE(state.is_dict()); - const base::Value* result = - state.FindKeyOfType("result", base::Value::Type::BOOLEAN); - ASSERT_TRUE(result != nullptr); - EXPECT_FALSE(result->GetBool()); + absl::optional<bool> result = state.GetDict().FindBool("result"); + EXPECT_TRUE(result.has_value()); + EXPECT_EQ(false, *result); } // Delete host data. @@ -4945,10 +4942,9 @@ run_loop.Run(); EXPECT_TRUE(state.is_dict()); - const base::Value* result = - state.FindKeyOfType("result", base::Value::Type::BOOLEAN); - ASSERT_TRUE(result != nullptr); - EXPECT_FALSE(result->GetBool()); + absl::optional<bool> result = state.GetDict().FindBool("result"); + EXPECT_TRUE(result.has_value()); + EXPECT_EQ(false, *result); } }
diff --git a/services/network/sct_auditing/sct_auditing_handler.cc b/services/network/sct_auditing/sct_auditing_handler.cc index adb85cd..5fa61232 100644 --- a/services/network/sct_auditing/sct_auditing_handler.cc +++ b/services/network/sct_auditing/sct_auditing_handler.cc
@@ -174,29 +174,29 @@ bool SCTAuditingHandler::SerializeData(std::string* output) { DCHECK(foreground_runner_->RunsTasksInCurrentSequence()); - base::Value reports(base::Value::Type::LIST); + base::Value::List reports; for (const auto& kv : pending_reporters_) { auto reporter_key = kv.first; auto* reporter = kv.second.get(); - base::Value report_entry(base::Value::Type::DICTIONARY); + base::Value::Dict report_entry; - report_entry.SetStringKey(kReporterKeyKey, reporter_key.ToString()); + report_entry.Set(kReporterKeyKey, reporter_key.ToString()); if (reporter->sct_hashdance_metadata()) { - report_entry.SetKey(kSCTHashdanceMetadataKey, - reporter->sct_hashdance_metadata()->ToValue()); + report_entry.Set(kSCTHashdanceMetadataKey, + reporter->sct_hashdance_metadata()->ToValue()); } base::Value backoff_entry_value = net::BackoffEntrySerializer::SerializeToValue( *reporter->backoff_entry(), base::Time::Now()); - report_entry.SetKey(kBackoffEntryKey, std::move(backoff_entry_value)); + report_entry.Set(kBackoffEntryKey, std::move(backoff_entry_value)); std::string serialized_report; reporter->report()->SerializeToString(&serialized_report); base::Base64Encode(serialized_report, &serialized_report); - report_entry.SetStringKey(kReportKey, serialized_report); + report_entry.Set(kReportKey, serialized_report); reports.Append(std::move(report_entry)); } @@ -212,18 +212,17 @@ return; } - for (base::Value& sct_entry : value->GetListDeprecated()) { + for (base::Value& sct_entry : value->GetList()) { + base::Value::Dict* entry_dict = sct_entry.GetIfDict(); if (!sct_entry.is_dict()) { continue; } - const std::string* reporter_key_string = - sct_entry.FindStringKey(kReporterKeyKey); - const std::string* report_string = sct_entry.FindStringKey(kReportKey); + std::string* reporter_key_string = entry_dict->FindString(kReporterKeyKey); + std::string* report_string = entry_dict->FindString(kReportKey); const absl::optional<base::Value> sct_metadata_value = - sct_entry.ExtractKey(kSCTHashdanceMetadataKey); - const base::Value* backoff_entry_value = - sct_entry.FindKey(kBackoffEntryKey); + entry_dict->Extract(kSCTHashdanceMetadataKey); + const base::Value* backoff_entry_value = entry_dict->Find(kBackoffEntryKey); if (!reporter_key_string || !report_string || !backoff_entry_value) { continue;
diff --git a/services/network/sct_auditing/sct_auditing_reporter.cc b/services/network/sct_auditing/sct_auditing_reporter.cc index 8e2fa1a..b2ea41d 100644 --- a/services/network/sct_auditing/sct_auditing_reporter.cc +++ b/services/network/sct_auditing/sct_auditing_reporter.cc
@@ -146,18 +146,19 @@ // static absl::optional<SCTAuditingReporter::SCTHashdanceMetadata> SCTAuditingReporter::SCTHashdanceMetadata::FromValue(const base::Value& value) { - if (!value.is_dict()) { + const base::Value::Dict* dict = value.GetIfDict(); + if (!dict) { return absl::nullopt; } - const std::string* encoded_leaf_hash = value.FindStringKey(kLeafHashKey); + const std::string* encoded_leaf_hash = dict->FindString(kLeafHashKey); const absl::optional<base::Time> issued = - base::ValueToTime(value.FindKey(kIssuedKey)); - const std::string* encoded_log_id = value.FindStringKey(kLogIdKey); + base::ValueToTime(dict->Find(kIssuedKey)); + const std::string* encoded_log_id = dict->FindString(kLogIdKey); const absl::optional<base::TimeDelta> log_mmd = - base::ValueToTimeDelta(value.FindKey(kLogMMDKey)); + base::ValueToTimeDelta(dict->Find(kLogMMDKey)); const absl::optional<base::Time> certificate_expiry = - base::ValueToTime(value.FindKey(kCertificateExpiry)); + base::ValueToTime(dict->Find(kCertificateExpiry)); if (!encoded_leaf_hash || !encoded_log_id || !log_mmd || !issued || !certificate_expiry) { return absl::nullopt; @@ -186,16 +187,15 @@ default; base::Value SCTAuditingReporter::SCTHashdanceMetadata::ToValue() const { - base::DictionaryValue value; - value.SetStringKey( - kLeafHashKey, - base::Base64Encode(base::as_bytes(base::make_span(leaf_hash)))); - value.SetKey(kIssuedKey, base::TimeToValue(issued)); - value.SetStringKey( - kLogIdKey, base::Base64Encode(base::as_bytes(base::make_span(log_id)))); - value.SetKey(kLogMMDKey, base::TimeDeltaToValue(log_mmd)); - value.SetKey(kCertificateExpiry, base::TimeToValue(certificate_expiry)); - return std::move(value); + base::Value::Dict dict; + dict.Set(kLeafHashKey, + base::Base64Encode(base::as_bytes(base::make_span(leaf_hash)))); + dict.Set(kIssuedKey, base::TimeToValue(issued)); + dict.Set(kLogIdKey, + base::Base64Encode(base::as_bytes(base::make_span(log_id)))); + dict.Set(kLogMMDKey, base::TimeDeltaToValue(log_mmd)); + dict.Set(kCertificateExpiry, base::TimeToValue(certificate_expiry)); + return base::Value(std::move(dict)); } // static @@ -368,13 +368,14 @@ } absl::optional<base::Value> result = base::JSONReader::Read(*response_body); - if (!result) { + if (!result || !result->is_dict()) { RecordLookupQueryResult(LookupQueryResult::kInvalidJson); MaybeRetryRequest(); return; } - const std::string* status = result->FindStringKey(kLookupStatusKey); + const base::Value::Dict& result_dict = result->GetDict(); + const std::string* status = result_dict.FindString(kLookupStatusKey); if (!status) { RecordLookupQueryResult(LookupQueryResult::kInvalidJson); MaybeRetryRequest(); @@ -387,7 +388,7 @@ } const std::string* server_timestamp_string = - result->FindStringKey(kLookupTimestampKey); + result_dict.FindString(kLookupTimestampKey); if (!server_timestamp_string) { RecordLookupQueryResult(LookupQueryResult::kInvalidJson); MaybeRetryRequest(); @@ -410,16 +411,22 @@ } // Find the corresponding log entry. - const base::Value* logs = result->FindListKey(kLookupLogStatusKey); + const base::Value::List* logs = result_dict.FindList(kLookupLogStatusKey); if (!logs) { RecordLookupQueryResult(LookupQueryResult::kInvalidJson); MaybeRetryRequest(); return; } - const base::Value* found_log = nullptr; - for (const auto& log : logs->GetListDeprecated()) { - const std::string* encoded_log_id = log.FindStringKey(kLookupLogIdKey); + const base::Value::Dict* found_log = nullptr; + for (const auto& log : *logs) { + const base::Value::Dict* log_dict = log.GetIfDict(); + if (!log_dict) { + RecordLookupQueryResult(LookupQueryResult::kInvalidJson); + MaybeRetryRequest(); + return; + } + const std::string* encoded_log_id = log_dict->FindString(kLookupLogIdKey); std::string log_id; if (!encoded_log_id || !base::Base64Decode(*encoded_log_id, &log_id)) { RecordLookupQueryResult(LookupQueryResult::kInvalidJson); @@ -427,7 +434,7 @@ return; } if (log_id == sct_hashdance_metadata_->log_id) { - found_log = &log; + found_log = log_dict; break; } } @@ -440,7 +447,7 @@ } const std::string* ingested_until_string = - found_log->FindStringKey(kLookupIngestedUntilKey); + found_log->FindString(kLookupIngestedUntilKey); if (!ingested_until_string) { RecordLookupQueryResult(LookupQueryResult::kInvalidJson); MaybeRetryRequest(); @@ -462,7 +469,8 @@ return; } - const base::Value* suffix_value = result->FindListKey(kLookupHashSuffixKey); + const base::Value::List* suffix_value = + result_dict.FindList(kLookupHashSuffixKey); if (!suffix_value) { RecordLookupQueryResult(LookupQueryResult::kInvalidJson); MaybeRetryRequest(); @@ -476,11 +484,10 @@ hash_suffix = base::Base64Encode(base::as_bytes(base::make_span(hash_suffix))); base::Value hash_suffix_value(std::move(hash_suffix)); - const auto suffixes = suffix_value->GetListDeprecated(); // TODO(nsatragno): it would be neat if the backend returned a sorted list and // we could binary search it instead. - if (std::find(suffixes.begin(), suffixes.end(), hash_suffix_value) != - suffixes.end()) { + if (std::find(suffix_value->begin(), suffix_value->end(), + hash_suffix_value) != suffix_value->end()) { // Found the SCT in the suffix list, all done. RecordLookupQueryResult(LookupQueryResult::kSCTSuffixFound); std::move(done_callback_).Run(reporter_key_);
diff --git a/services/network/sct_auditing/sct_auditing_reporter_unittest.cc b/services/network/sct_auditing/sct_auditing_reporter_unittest.cc index 14b55e15..238df8b0 100644 --- a/services/network/sct_auditing/sct_auditing_reporter_unittest.cc +++ b/services/network/sct_auditing/sct_auditing_reporter_unittest.cc
@@ -191,12 +191,13 @@ }; TEST_F(SCTAuditingReporterTest, SCTHashdanceMetadataFromValue) { - base::DictionaryValue valid_value; - valid_value.SetStringKey("leaf_hash", kLeafHashBase64); - valid_value.SetStringKey("issued", kIssuedSerialized); - valid_value.SetStringKey("log_id", kLogIdBase64); - valid_value.SetStringKey("log_mmd", kLogMMDSerialized); - valid_value.SetStringKey("cert_expiry", kCertExpirySerialized); + base::Value::Dict valid_value_dict; + valid_value_dict.Set("leaf_hash", kLeafHashBase64); + valid_value_dict.Set("issued", kIssuedSerialized); + valid_value_dict.Set("log_id", kLogIdBase64); + valid_value_dict.Set("log_mmd", kLogMMDSerialized); + valid_value_dict.Set("cert_expiry", kCertExpirySerialized); + base::Value valid_value(std::move(valid_value_dict)); auto metadata = SCTAuditingReporter::SCTHashdanceMetadata::FromValue(valid_value); @@ -209,43 +210,43 @@ base::Time::UnixEpoch() + base::Seconds(10)); { base::Value invalid_value = valid_value.Clone(); - invalid_value.RemoveKey("leaf_hash"); + invalid_value.GetDict().Remove("leaf_hash"); EXPECT_FALSE( SCTAuditingReporter::SCTHashdanceMetadata::FromValue(invalid_value)); } { base::Value invalid_value = valid_value.Clone(); - invalid_value.RemoveKey("issued"); + invalid_value.GetDict().Remove("issued"); EXPECT_FALSE( SCTAuditingReporter::SCTHashdanceMetadata::FromValue(invalid_value)); } { base::Value invalid_value = valid_value.Clone(); - invalid_value.RemoveKey("log_id"); + invalid_value.GetDict().Remove("log_id"); EXPECT_FALSE( SCTAuditingReporter::SCTHashdanceMetadata::FromValue(invalid_value)); } { base::Value invalid_value = valid_value.Clone(); - invalid_value.RemoveKey("log_mmd"); + invalid_value.GetDict().Remove("log_mmd"); EXPECT_FALSE( SCTAuditingReporter::SCTHashdanceMetadata::FromValue(invalid_value)); } { base::Value invalid_value = valid_value.Clone(); - invalid_value.SetStringKey("leaf_hash", "invalid base64"); + invalid_value.GetDict().Set("leaf_hash", "invalid base64"); EXPECT_FALSE( SCTAuditingReporter::SCTHashdanceMetadata::FromValue(invalid_value)); } { base::Value invalid_value = valid_value.Clone(); - invalid_value.SetStringKey("log_id", "invalid base64"); + invalid_value.GetDict().Set("log_id", "invalid base64"); EXPECT_FALSE( SCTAuditingReporter::SCTHashdanceMetadata::FromValue(invalid_value)); } { base::Value invalid_value = valid_value.Clone(); - invalid_value.RemoveKey("cert_expiry"); + invalid_value.GetDict().Remove("cert_expiry"); EXPECT_FALSE( SCTAuditingReporter::SCTHashdanceMetadata::FromValue(invalid_value)); } @@ -263,12 +264,13 @@ metadata.log_mmd = base::Seconds(42); metadata.certificate_expiry = base::Time::UnixEpoch() + base::Seconds(10); base::Value value = metadata.ToValue(); - ASSERT_TRUE(value.is_dict()); - EXPECT_EQ(*value.FindStringKey("leaf_hash"), kLeafHashBase64); - EXPECT_EQ(*value.FindStringKey("issued"), kIssuedSerialized); - EXPECT_EQ(*value.FindStringKey("log_id"), kLogIdBase64); - EXPECT_EQ(*value.FindStringKey("log_mmd"), kLogMMDSerialized); - EXPECT_EQ(*value.FindStringKey("cert_expiry"), kCertExpirySerialized); + const base::Value::Dict* dict = value.GetIfDict(); + ASSERT_TRUE(dict); + EXPECT_EQ(*dict->FindString("leaf_hash"), kLeafHashBase64); + EXPECT_EQ(*dict->FindString("issued"), kIssuedSerialized); + EXPECT_EQ(*dict->FindString("log_id"), kLogIdBase64); + EXPECT_EQ(*dict->FindString("log_mmd"), kLogMMDSerialized); + EXPECT_EQ(*dict->FindString("cert_expiry"), kCertExpirySerialized); } // Tests that a hashdance lookup that does not find the SCT reports it.
diff --git a/storage/browser/quota/quota_database.cc b/storage/browser/quota/quota_database.cc index 903f4845..1eaad92 100644 --- a/storage/browser/quota/quota_database.cc +++ b/storage/browser/quota/quota_database.cc
@@ -18,6 +18,7 @@ #include "base/files/file_util.h" #include "base/metrics/histogram_functions.h" #include "base/sequence_checker.h" +#include "base/time/clock.h" #include "components/services/storage/public/cpp/buckets/constants.h" #include "components/services/storage/public/cpp/quota_error_or.h" #include "sql/database.h" @@ -70,6 +71,8 @@ const int kCommitIntervalMs = 30000; +base::Clock* g_clock_for_testing = nullptr; + void RecordDatabaseResetHistogram(const DatabaseResetReason reason) { base::UmaHistogramEnumeration("Quota.QuotaDatabaseReset", reason); } @@ -185,8 +188,7 @@ last_modified(last_modified) {} // QuotaDatabase ------------------------------------------------------------ -QuotaDatabase::QuotaDatabase(const base::FilePath& profile_path, - const base::Clock& clock) +QuotaDatabase::QuotaDatabase(const base::FilePath& profile_path) : storage_directory_( profile_path.empty() ? nullptr @@ -197,8 +199,7 @@ : storage_directory_->path().AppendASCII(kDatabaseName)), legacy_db_file_path_(profile_path.empty() ? base::FilePath() - : profile_path.AppendASCII(kDatabaseName)), - clock_(clock) { + : profile_path.AppendASCII(kDatabaseName)) { DETACH_FROM_SEQUENCE(sequence_checker_); } @@ -272,7 +273,7 @@ // Don't bother updating anything if the bucket is expired. if (!bucket_result->expiration.is_null() && - (bucket_result->expiration <= clock_.Now())) { + (bucket_result->expiration <= GetNow())) { return bucket_result; } @@ -865,6 +866,16 @@ is_disabled_ = disable; } +// static +base::Time QuotaDatabase::GetNow() { + return g_clock_for_testing ? g_clock_for_testing->Now() : base::Time::Now(); +} + +// static +void QuotaDatabase::SetClockForTesting(base::Clock* clock) { + g_clock_for_testing = clock; +} + void QuotaDatabase::Commit() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); if (!db_) @@ -1179,9 +1190,8 @@ sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql)); BindBucketInitParamsToInsertStatement(params, type, /*use_count=*/0, - /*last_accessed=*/clock_.Now(), - /*last_modified=*/clock_.Now(), - statement); + /*last_accessed=*/GetNow(), + /*last_modified=*/GetNow(), statement); QuotaErrorOr<BucketInfo> result = BucketInfoFromSqlStatement(statement); const bool done = !statement.Step(); DCHECK(done);
diff --git a/storage/browser/quota/quota_database.h b/storage/browser/quota/quota_database.h index 826acaf0..74de5f5 100644 --- a/storage/browser/quota/quota_database.h +++ b/storage/browser/quota/quota_database.h
@@ -17,7 +17,6 @@ #include "base/files/file_path.h" #include "base/sequence_checker.h" #include "base/thread_annotations.h" -#include "base/time/clock.h" #include "base/time/time.h" #include "base/timer/timer.h" #include "base/types/id_type.h" @@ -32,6 +31,10 @@ #include "third_party/blink/public/common/storage_key/storage_key.h" #include "third_party/blink/public/mojom/quota/quota_types.mojom-shared.h" +namespace base { +class Clock; +} + namespace sql { class Database; class MetaTable; @@ -93,7 +96,7 @@ static constexpr char kDatabaseName[] = "QuotaManager"; // If `profile_path` is empty, an in-memory database will be used. - QuotaDatabase(const base::FilePath& profile_path, const base::Clock& clock); + explicit QuotaDatabase(const base::FilePath& profile_path); QuotaDatabase(const QuotaDatabase&) = delete; QuotaDatabase& operator=(const QuotaDatabase&) = delete; @@ -254,6 +257,9 @@ // Manually disable database to test database error scenarios for testing. void SetDisabledForTesting(bool disable); + static base::Time GetNow(); + static void SetClockForTesting(base::Clock* clock); + private: struct COMPONENT_EXPORT(STORAGE_BROWSER) QuotaTableEntry { std::string host; @@ -326,8 +332,6 @@ bool is_recreating_ GUARDED_BY_CONTEXT(sequence_checker_) = false; bool is_disabled_ GUARDED_BY_CONTEXT(sequence_checker_) = false; - const base::Clock& clock_; - base::OneShotTimer timer_ GUARDED_BY_CONTEXT(sequence_checker_); friend class QuotaDatabaseTest;
diff --git a/storage/browser/quota/quota_database_migrations_unittest.cc b/storage/browser/quota/quota_database_migrations_unittest.cc index 177110f..6eeeb9e 100644 --- a/storage/browser/quota/quota_database_migrations_unittest.cc +++ b/storage/browser/quota/quota_database_migrations_unittest.cc
@@ -5,7 +5,6 @@ #include "base/files/file_util.h" #include "base/files/scoped_temp_dir.h" #include "base/path_service.h" -#include "base/time/default_clock.h" #include "components/services/storage/public/cpp/constants.h" #include "sql/database.h" #include "sql/meta_table.h" @@ -71,7 +70,7 @@ } void MigrateDatabase() { - QuotaDatabase db(ProfilePath(), clock_); + QuotaDatabase db(ProfilePath()); EXPECT_EQ(db.EnsureOpened(), QuotaError::kNone); DCHECK_CALLED_ON_VALID_SEQUENCE(db.sequence_checker_); @@ -88,13 +87,12 @@ } std::string GetQuotaDatabaseSchema() { - QuotaDatabase db(ProfilePath(), clock_); + QuotaDatabase db(ProfilePath()); EXPECT_EQ(db.EnsureOpened(), QuotaError::kNone); DCHECK_CALLED_ON_VALID_SEQUENCE(db.sequence_checker_); return db.db_->GetSchema(); } - base::DefaultClock clock_; base::ScopedTempDir temp_directory_; };
diff --git a/storage/browser/quota/quota_database_unittest.cc b/storage/browser/quota/quota_database_unittest.cc index 51ec57e..7ea769f 100644 --- a/storage/browser/quota/quota_database_unittest.cc +++ b/storage/browser/quota/quota_database_unittest.cc
@@ -19,7 +19,6 @@ #include "base/sequence_checker.h" #include "base/test/metrics/histogram_tester.h" #include "base/test/task_environment.h" -#include "base/time/default_clock.h" #include "build/build_config.h" #include "components/services/storage/public/cpp/buckets/bucket_locator.h" #include "components/services/storage/public/cpp/buckets/constants.h" @@ -82,8 +81,8 @@ } std::unique_ptr<QuotaDatabase> CreateDatabase(bool is_incognito) { - return std::make_unique<QuotaDatabase>( - is_incognito ? base::FilePath() : ProfilePath(), clock_); + return std::make_unique<QuotaDatabase>(is_incognito ? base::FilePath() + : ProfilePath()); } bool EnsureOpened(QuotaDatabase* db) { @@ -180,7 +179,6 @@ private: base::test::SingleThreadTaskEnvironment task_environment_; base::ScopedTempDir temp_directory_; - base::DefaultClock clock_; }; TEST_P(QuotaDatabaseTest, EnsureOpened) { @@ -196,8 +194,7 @@ } TEST_P(QuotaDatabaseTest, RazeAndReopenWithNoDb) { - base::DefaultClock clock; - QuotaDatabase db(use_in_memory_db() ? base::FilePath() : DbPath(), clock); + QuotaDatabase db(use_in_memory_db() ? base::FilePath() : DbPath()); // RazeAndReopen() with no db tries to create the db one last time. EXPECT_EQ(db.RazeAndReopen(), QuotaError::kNone); @@ -1114,8 +1111,7 @@ #endif // !BUILDFLAG(IS_MAC) TEST_F(QuotaDatabaseTest, UpdateOrCreateBucket_CorruptedDatabase) { - base::DefaultClock clock; - QuotaDatabase db(ProfilePath(), clock); + QuotaDatabase db(ProfilePath()); BucketInitParams params( StorageKey::CreateFromStringForTesting("http://google/"), "google_bucket"); @@ -1147,8 +1143,7 @@ } TEST_P(QuotaDatabaseTest, Expiration) { - base::DefaultClock clock; - QuotaDatabase db(ProfilePath(), clock); + QuotaDatabase db(ProfilePath()); // Default `expiration` value. BucketInitParams params( @@ -1175,8 +1170,7 @@ } TEST_P(QuotaDatabaseTest, Persistent) { - base::DefaultClock clock; - QuotaDatabase db(ProfilePath(), clock); + QuotaDatabase db(ProfilePath()); // Default `persistent` value. BucketInitParams params(
diff --git a/storage/browser/quota/quota_manager_impl.cc b/storage/browser/quota/quota_manager_impl.cc index 0b6282a2..90e96bb 100644 --- a/storage/browser/quota/quota_manager_impl.cc +++ b/storage/browser/quota/quota_manager_impl.cc
@@ -41,7 +41,6 @@ #include "base/thread_annotations.h" #include "base/threading/thread_restrictions.h" #include "base/threading/thread_task_runner_handle.h" -#include "base/time/default_clock.h" #include "base/time/time.h" #include "base/trace_event/trace_event.h" #include "base/types/pass_key.h" @@ -1041,8 +1040,7 @@ get_settings_function_(get_settings_function), quota_change_callback_(std::move(quota_change_callback)), special_storage_policy_(std::move(special_storage_policy)), - get_volume_info_fn_(&QuotaManagerImpl::GetVolumeInfo), - clock_(std::make_unique<base::DefaultClock>()) { + get_volume_info_fn_(&QuotaManagerImpl::GetVolumeInfo) { DCHECK_EQ(settings_.refresh_interval, base::TimeDelta::Max()); if (!get_settings_function.is_null()) { // Reset the interval to ensure we use the get_settings_function @@ -1072,7 +1070,7 @@ return; } if (!bucket_params.expiration.is_null() && - (bucket_params.expiration <= clock_->Now())) { + (bucket_params.expiration <= QuotaDatabase::GetNow())) { std::move(callback).Run(QuotaError::kIllegalOperation); return; } @@ -1822,7 +1820,7 @@ proxy_->InvalidateQuotaManagerImpl(base::PassKey<QuotaManagerImpl>()); if (database_) - db_runner_->DeleteSoon(FROM_HERE, database_.release()); + db_runner_->DeleteSoon(FROM_HERE, database_.get()); } QuotaManagerImpl::EvictionContext::EvictionContext() = default; @@ -1837,8 +1835,8 @@ } // Use an empty path to open an in-memory only database for incognito. - database_ = std::make_unique<QuotaDatabase>( - is_incognito_ ? base::FilePath() : profile_path_, *clock_); + database_ = + new QuotaDatabase(is_incognito_ ? base::FilePath() : profile_path_); temporary_usage_tracker_ = std::make_unique<UsageTracker>( this, client_types_[StorageType::kTemporary], StorageType::kTemporary, @@ -2216,7 +2214,7 @@ buckets_in_error_[eviction_context_.evicted_bucket.id]++; if (status == blink::mojom::QuotaStatusCode::kOk) { - base::Time now = clock_->Now(); + base::Time now = QuotaDatabase::GetNow(); base::UmaHistogramCounts1M( QuotaManagerImpl::kEvictedBucketAccessedCountHistogram, entry.use_count); @@ -2478,7 +2476,7 @@ std::map<StorageKey, int64_t> usage_map = GetUsageTracker(StorageType::kTemporary)->GetCachedStorageKeysUsage(); - base::Time now = clock_->Now(); + base::Time now = QuotaDatabase::GetNow(); for (const auto& info : entries) { if (info.type != StorageType::kTemporary) continue; @@ -2857,7 +2855,7 @@ DidDatabaseWork(result.ok() || result.error() != QuotaError::kDatabaseError); if (result.ok() && !result->expiration.is_null() && - result->expiration <= clock_->Now()) { + result->expiration <= QuotaDatabase::GetNow()) { DeleteBucketDataInternal( result->ToBucketLocator(), AllQuotaClientTypes(), base::BindOnce(&QuotaManagerImpl::DidDeleteBucketForRecreation,
diff --git a/storage/browser/quota/quota_manager_impl.h b/storage/browser/quota/quota_manager_impl.h index 437cc200..aa13b14 100644 --- a/storage/browser/quota/quota_manager_impl.h +++ b/storage/browser/quota/quota_manager_impl.h
@@ -45,7 +45,6 @@ #include "third_party/blink/public/mojom/quota/quota_types.mojom-shared.h" namespace base { -class Clock; class SequencedTaskRunner; class SingleThreadTaskRunner; class TaskRunner; @@ -772,7 +771,11 @@ storage_key_for_pending_storage_pressure_callback_; scoped_refptr<base::SingleThreadTaskRunner> io_thread_; scoped_refptr<base::SequencedTaskRunner> db_runner_; - mutable std::unique_ptr<QuotaDatabase> database_; + + // QuotaManagerImpl creates `database_` and later schedules it for deletion on + // `db_runner_`. Thus, `database_` may outlive `this`. + raw_ptr<QuotaDatabase> database_ = nullptr; + bool is_bootstrapping_database_ = false; // Queued callbacks to QuotaDatabase that will run after database bootstrap is // complete. @@ -861,8 +864,6 @@ bucket_data_deleters_; std::unique_ptr<StorageKeyGathererTask> storage_key_gatherer_; - std::unique_ptr<base::Clock> clock_; - SEQUENCE_CHECKER(sequence_checker_); base::WeakPtrFactory<QuotaManagerImpl> weak_factory_{this};
diff --git a/storage/browser/quota/quota_manager_unittest.cc b/storage/browser/quota/quota_manager_unittest.cc index 1b48eb0..9d0d7a8 100644 --- a/storage/browser/quota/quota_manager_unittest.cc +++ b/storage/browser/quota/quota_manager_unittest.cc
@@ -809,18 +809,17 @@ TEST_F(QuotaManagerImplTest, UpdateOrCreateBucket_Expiration) { auto clock = std::make_unique<base::SimpleTestClock>(); - base::SimpleTestClock* clock_ptr = clock.get(); - quota_manager_impl_->clock_ = std::move(clock); - clock_ptr->SetNow(base::Time::Now()); + QuotaDatabase::SetClockForTesting(clock.get()); + clock->SetNow(base::Time::Now()); BucketInitParams params(ToStorageKey("http://a.com/"), "bucket_a"); - params.expiration = clock_ptr->Now() - base::Days(1); + params.expiration = clock->Now() - base::Days(1); auto bucket = UpdateOrCreateBucket(params); ASSERT_FALSE(bucket.ok()); // Create a new bucket. - params.expiration = clock_ptr->Now() + base::Days(1); + params.expiration = clock->Now() + base::Days(1); params.quota = 1000; bucket = UpdateOrCreateBucket(params); ASSERT_TRUE(bucket.ok()); @@ -828,7 +827,7 @@ EXPECT_EQ(bucket->quota, 1000); // Get/Update the same bucket. Verify expiration is updated, but quota is not. - params.expiration = clock_ptr->Now() + base::Days(5); + params.expiration = clock->Now() + base::Days(5); params.quota = 500; bucket = UpdateOrCreateBucket(params); ASSERT_TRUE(bucket.ok()); @@ -837,11 +836,13 @@ // Verify that the bucket is clobbered due to being expired. In this case, the // new quota is respected. - clock_ptr->Advance(base::Days(20)); + clock->Advance(base::Days(20)); params.expiration = base::Time(); bucket = UpdateOrCreateBucket(params); EXPECT_EQ(bucket->expiration, params.expiration); EXPECT_EQ(bucket->quota, 500); + + QuotaDatabase::SetClockForTesting(nullptr); } TEST_F(QuotaManagerImplTest, GetOrCreateBucketSync) {
diff --git a/testing/buildbot/chrome.json b/testing/buildbot/chrome.json index 2f5520fe..f2691de5 100644 --- a/testing/buildbot/chrome.json +++ b/testing/buildbot/chrome.json
@@ -1958,6 +1958,22 @@ }, { "args": [], + "cros_board": "atlas", + "cros_img": "atlas-release/R102-14695.85.0", + "name": "lacros_all_tast_tests ATLAS_RELEASE_STABLE", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": {}, + "tast_expr": "(\"group:mainline\" && \"dep:lacros\" && !informational)", + "test": "lacros_all_tast_tests", + "test_id_prefix": "ninja://chromeos/lacros:lacros_all_tast_tests/", + "timeout_sec": 10800, + "variant_id": "ATLAS_RELEASE_STABLE" + }, + { + "args": [], "cros_board": "eve", "cros_img": "eve-release/R104-14844.0.0", "name": "lacros_all_tast_tests EVE_RELEASE_LKGM", @@ -2003,6 +2019,22 @@ "test_id_prefix": "ninja://chromeos/lacros:lacros_all_tast_tests/", "timeout_sec": 10800, "variant_id": "EVE_RELEASE_BETA" + }, + { + "args": [], + "cros_board": "eve", + "cros_img": "eve-release/R102-14695.85.0", + "name": "lacros_all_tast_tests EVE_RELEASE_STABLE", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": {}, + "tast_expr": "(\"group:mainline\" && \"dep:lacros\" && !informational)", + "test": "lacros_all_tast_tests", + "test_id_prefix": "ninja://chromeos/lacros:lacros_all_tast_tests/", + "timeout_sec": 10800, + "variant_id": "EVE_RELEASE_STABLE" } ] }, @@ -2099,6 +2131,22 @@ }, { "args": [], + "cros_board": "hana", + "cros_img": "hana-release/R102-14695.85.0", + "name": "lacros_all_tast_tests HANA_RELEASE_STABLE", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": {}, + "tast_expr": "(\"group:mainline\" && \"dep:lacros\" && !informational)", + "test": "lacros_all_tast_tests", + "test_id_prefix": "ninja://chromeos/lacros:lacros_all_tast_tests/", + "timeout_sec": 10800, + "variant_id": "HANA_RELEASE_STABLE" + }, + { + "args": [], "cros_board": "jacuzzi", "cros_img": "jacuzzi-release/R104-14844.0.0", "name": "lacros_all_tast_tests JACUZZI_RELEASE_LKGM", @@ -2200,6 +2248,23 @@ "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/lacros-arm.ozone_unittests.filter" ], + "cros_board": "hana", + "cros_img": "hana-release/R102-14695.85.0", + "name": "ozone_unittests HANA_RELEASE_STABLE", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": {}, + "test": "ozone_unittests", + "test_id_prefix": "ninja://ui/ozone:ozone_unittests/", + "timeout_sec": 3600, + "variant_id": "HANA_RELEASE_STABLE" + }, + { + "args": [ + "--test-launcher-filter-file=../../testing/buildbot/filters/lacros-arm.ozone_unittests.filter" + ], "cros_board": "jacuzzi", "cros_img": "jacuzzi-release/R104-14844.0.0", "name": "ozone_unittests JACUZZI_RELEASE_LKGM", @@ -2302,6 +2367,23 @@ "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/lacros-arm.viz_unittests.filter" ], + "cros_board": "hana", + "cros_img": "hana-release/R102-14695.85.0", + "name": "viz_unittests HANA_RELEASE_STABLE", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": {}, + "test": "viz_unittests", + "test_id_prefix": "ninja://components/viz:viz_unittests/", + "timeout_sec": 3600, + "variant_id": "HANA_RELEASE_STABLE" + }, + { + "args": [ + "--test-launcher-filter-file=../../testing/buildbot/filters/lacros-arm.viz_unittests.filter" + ], "cros_board": "jacuzzi", "cros_img": "jacuzzi-release/R104-14844.0.0", "name": "viz_unittests JACUZZI_RELEASE_LKGM",
diff --git a/testing/buildbot/chromium.chromiumos.json b/testing/buildbot/chromium.chromiumos.json index 06b3cd2..4354317 100644 --- a/testing/buildbot/chromium.chromiumos.json +++ b/testing/buildbot/chromium.chromiumos.json
@@ -1198,7 +1198,7 @@ "--browser=cros-chrome", "--passthrough", "-v", - "--extra-browser-args=--log-level=0 --js-flags=--expose-gc --force_high_performance_gpu", + "--extra-browser-args=--log-level=0 --js-flags=--expose-gc --force_high_performance_gpu --disable-features=BackgroundVideoPauseOptimization", "--read-abbreviated-json-results-from=../../content/test/data/gpu/webgl_conformance_tests_output.json", "--remote=127.0.0.1", "--remote-ssh-port=9222" @@ -5865,21 +5865,21 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5094.0/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5095.0/test_ash_chrome" ], "isolate_profile_data": true, "merge": { "args": [], "script": "//testing/merge_scripts/standard_gtest_merge.py" }, - "name": "interactive_ui_tests Lacros version skew testing ash 104.0.5094.0", + "name": "interactive_ui_tests Lacros version skew testing ash 104.0.5095.0", "swarming": { "can_use_on_swarming_builders": true, "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v104.0.5094.0", - "revision": "version:104.0.5094.0" + "location": "lacros_version_skew_tests_v104.0.5095.0", + "revision": "version:104.0.5095.0" } ], "dimension_sets": [ @@ -5892,7 +5892,7 @@ }, "test": "interactive_ui_tests", "test_id_prefix": "ninja://chrome/test:interactive_ui_tests/", - "variant_id": "Lacros version skew testing ash 104.0.5094.0" + "variant_id": "Lacros version skew testing ash 104.0.5095.0" }, { "isolate_profile_data": true, @@ -6030,21 +6030,21 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5094.0/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5095.0/test_ash_chrome" ], "isolate_profile_data": true, "merge": { "args": [], "script": "//testing/merge_scripts/standard_gtest_merge.py" }, - "name": "lacros_chrome_browsertests Lacros version skew testing ash 104.0.5094.0", + "name": "lacros_chrome_browsertests Lacros version skew testing ash 104.0.5095.0", "swarming": { "can_use_on_swarming_builders": true, "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v104.0.5094.0", - "revision": "version:104.0.5094.0" + "location": "lacros_version_skew_tests_v104.0.5095.0", + "revision": "version:104.0.5095.0" } ], "dimension_sets": [ @@ -6056,7 +6056,7 @@ }, "test": "lacros_chrome_browsertests", "test_id_prefix": "ninja://chrome/test:lacros_chrome_browsertests/", - "variant_id": "Lacros version skew testing ash 104.0.5094.0" + "variant_id": "Lacros version skew testing ash 104.0.5095.0" }, { "args": [ @@ -6176,21 +6176,21 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5094.0/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5095.0/test_ash_chrome" ], "isolate_profile_data": true, "merge": { "args": [], "script": "//testing/merge_scripts/standard_gtest_merge.py" }, - "name": "lacros_chrome_browsertests_run_in_series Lacros version skew testing ash 104.0.5094.0", + "name": "lacros_chrome_browsertests_run_in_series Lacros version skew testing ash 104.0.5095.0", "swarming": { "can_use_on_swarming_builders": true, "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v104.0.5094.0", - "revision": "version:104.0.5094.0" + "location": "lacros_version_skew_tests_v104.0.5095.0", + "revision": "version:104.0.5095.0" } ], "dimension_sets": [ @@ -6202,7 +6202,7 @@ }, "test": "lacros_chrome_browsertests_run_in_series", "test_id_prefix": "ninja://chrome/test:lacros_chrome_browsertests_run_in_series/", - "variant_id": "Lacros version skew testing ash 104.0.5094.0" + "variant_id": "Lacros version skew testing ash 104.0.5095.0" }, { "isolate_profile_data": true,
diff --git a/testing/buildbot/chromium.fuchsia.fyi.json b/testing/buildbot/chromium.fuchsia.fyi.json index e2bdd0c..5664662 100644 --- a/testing/buildbot/chromium.fuchsia.fyi.json +++ b/testing/buildbot/chromium.fuchsia.fyi.json
@@ -2039,7 +2039,7 @@ }, { "args": [ - "--test-launcher-filter-file=../../testing/buildbot/filters/fuchsia.lsan.perfetto_unittests.filter" + "--gtest_filter=-PagedMemoryTest.AccessUncommittedMemoryTriggersASAN" ], "merge": { "args": [],
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json index c2c0045..88cc7e3 100644 --- a/testing/buildbot/chromium.fyi.json +++ b/testing/buildbot/chromium.fyi.json
@@ -25401,6 +25401,25 @@ "test_id_prefix": "ninja://content/test:content_browsertests/" }, { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "arm64", + "inside_docker": "1", + "os": "Ubuntu-20.04" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "filesystem_service_unittests", + "test_id_prefix": "ninja://components/services/filesystem:filesystem_service_unittests/" + }, + { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/fuchsia.unit_tests.filter" ], @@ -25479,6 +25498,24 @@ "test_id_prefix": "ninja://content/test:content_browsertests/" }, { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "kvm": "1", + "os": "Ubuntu-18.04" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "filesystem_service_unittests", + "test_id_prefix": "ninja://components/services/filesystem:filesystem_service_unittests/" + }, + { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/fuchsia.unit_tests.filter" ], @@ -26885,6 +26922,27 @@ }, { "args": [ + "--custom-image=workstation.qemu-x64-release" + ], + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "kvm": "1", + "os": "Ubuntu-18.04" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "filesystem_service_unittests", + "test_id_prefix": "ninja://components/services/filesystem:filesystem_service_unittests/" + }, + { + "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/fuchsia.unit_tests.filter", "--custom-image=workstation.qemu-x64-release" ], @@ -88105,21 +88163,21 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5094.0/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5095.0/test_ash_chrome" ], "isolate_profile_data": true, "merge": { "args": [], "script": "//testing/merge_scripts/standard_gtest_merge.py" }, - "name": "interactive_ui_tests Lacros version skew testing ash 104.0.5094.0", + "name": "interactive_ui_tests Lacros version skew testing ash 104.0.5095.0", "swarming": { "can_use_on_swarming_builders": true, "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v104.0.5094.0", - "revision": "version:104.0.5094.0" + "location": "lacros_version_skew_tests_v104.0.5095.0", + "revision": "version:104.0.5095.0" } ], "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", @@ -88127,7 +88185,7 @@ }, "test": "interactive_ui_tests", "test_id_prefix": "ninja://chrome/test:interactive_ui_tests/", - "variant_id": "Lacros version skew testing ash 104.0.5094.0" + "variant_id": "Lacros version skew testing ash 104.0.5095.0" }, { "isolate_profile_data": true, @@ -88240,28 +88298,28 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5094.0/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5095.0/test_ash_chrome" ], "isolate_profile_data": true, "merge": { "args": [], "script": "//testing/merge_scripts/standard_gtest_merge.py" }, - "name": "lacros_chrome_browsertests Lacros version skew testing ash 104.0.5094.0", + "name": "lacros_chrome_browsertests Lacros version skew testing ash 104.0.5095.0", "swarming": { "can_use_on_swarming_builders": true, "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v104.0.5094.0", - "revision": "version:104.0.5094.0" + "location": "lacros_version_skew_tests_v104.0.5095.0", + "revision": "version:104.0.5095.0" } ], "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, "test": "lacros_chrome_browsertests", "test_id_prefix": "ninja://chrome/test:lacros_chrome_browsertests/", - "variant_id": "Lacros version skew testing ash 104.0.5094.0" + "variant_id": "Lacros version skew testing ash 104.0.5095.0" }, { "args": [ @@ -88361,28 +88419,28 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5094.0/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5095.0/test_ash_chrome" ], "isolate_profile_data": true, "merge": { "args": [], "script": "//testing/merge_scripts/standard_gtest_merge.py" }, - "name": "lacros_chrome_browsertests_run_in_series Lacros version skew testing ash 104.0.5094.0", + "name": "lacros_chrome_browsertests_run_in_series Lacros version skew testing ash 104.0.5095.0", "swarming": { "can_use_on_swarming_builders": true, "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v104.0.5094.0", - "revision": "version:104.0.5094.0" + "location": "lacros_version_skew_tests_v104.0.5095.0", + "revision": "version:104.0.5095.0" } ], "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, "test": "lacros_chrome_browsertests_run_in_series", "test_id_prefix": "ninja://chrome/test:lacros_chrome_browsertests_run_in_series/", - "variant_id": "Lacros version skew testing ash 104.0.5094.0" + "variant_id": "Lacros version skew testing ash 104.0.5095.0" }, { "isolate_profile_data": true, @@ -89720,20 +89778,20 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5094.0/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5095.0/test_ash_chrome" ], "merge": { "args": [], "script": "//testing/merge_scripts/standard_gtest_merge.py" }, - "name": "interactive_ui_tests Lacros version skew testing ash 104.0.5094.0", + "name": "interactive_ui_tests Lacros version skew testing ash 104.0.5095.0", "swarming": { "can_use_on_swarming_builders": true, "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v104.0.5094.0", - "revision": "version:104.0.5094.0" + "location": "lacros_version_skew_tests_v104.0.5095.0", + "revision": "version:104.0.5095.0" } ], "dimension_sets": [ @@ -89747,7 +89805,7 @@ }, "test": "interactive_ui_tests", "test_id_prefix": "ninja://chrome/test:interactive_ui_tests/", - "variant_id": "Lacros version skew testing ash 104.0.5094.0" + "variant_id": "Lacros version skew testing ash 104.0.5095.0" }, { "merge": { @@ -89885,20 +89943,20 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5094.0/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5095.0/test_ash_chrome" ], "merge": { "args": [], "script": "//testing/merge_scripts/standard_gtest_merge.py" }, - "name": "lacros_chrome_browsertests Lacros version skew testing ash 104.0.5094.0", + "name": "lacros_chrome_browsertests Lacros version skew testing ash 104.0.5095.0", "swarming": { "can_use_on_swarming_builders": true, "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v104.0.5094.0", - "revision": "version:104.0.5094.0" + "location": "lacros_version_skew_tests_v104.0.5095.0", + "revision": "version:104.0.5095.0" } ], "dimension_sets": [ @@ -89911,7 +89969,7 @@ }, "test": "lacros_chrome_browsertests", "test_id_prefix": "ninja://chrome/test:lacros_chrome_browsertests/", - "variant_id": "Lacros version skew testing ash 104.0.5094.0" + "variant_id": "Lacros version skew testing ash 104.0.5095.0" }, { "args": [ @@ -90031,20 +90089,20 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5094.0/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5095.0/test_ash_chrome" ], "merge": { "args": [], "script": "//testing/merge_scripts/standard_gtest_merge.py" }, - "name": "lacros_chrome_browsertests_run_in_series Lacros version skew testing ash 104.0.5094.0", + "name": "lacros_chrome_browsertests_run_in_series Lacros version skew testing ash 104.0.5095.0", "swarming": { "can_use_on_swarming_builders": true, "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v104.0.5094.0", - "revision": "version:104.0.5094.0" + "location": "lacros_version_skew_tests_v104.0.5095.0", + "revision": "version:104.0.5095.0" } ], "dimension_sets": [ @@ -90057,7 +90115,7 @@ }, "test": "lacros_chrome_browsertests_run_in_series", "test_id_prefix": "ninja://chrome/test:lacros_chrome_browsertests_run_in_series/", - "variant_id": "Lacros version skew testing ash 104.0.5094.0" + "variant_id": "Lacros version skew testing ash 104.0.5095.0" }, { "merge": { @@ -91553,20 +91611,20 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5094.0/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5095.0/test_ash_chrome" ], "merge": { "args": [], "script": "//testing/merge_scripts/standard_gtest_merge.py" }, - "name": "interactive_ui_tests Lacros version skew testing ash 104.0.5094.0", + "name": "interactive_ui_tests Lacros version skew testing ash 104.0.5095.0", "swarming": { "can_use_on_swarming_builders": true, "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v104.0.5094.0", - "revision": "version:104.0.5094.0" + "location": "lacros_version_skew_tests_v104.0.5095.0", + "revision": "version:104.0.5095.0" } ], "dimension_sets": [ @@ -91580,7 +91638,7 @@ }, "test": "interactive_ui_tests", "test_id_prefix": "ninja://chrome/test:interactive_ui_tests/", - "variant_id": "Lacros version skew testing ash 104.0.5094.0" + "variant_id": "Lacros version skew testing ash 104.0.5095.0" }, { "merge": { @@ -91718,20 +91776,20 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5094.0/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5095.0/test_ash_chrome" ], "merge": { "args": [], "script": "//testing/merge_scripts/standard_gtest_merge.py" }, - "name": "lacros_chrome_browsertests Lacros version skew testing ash 104.0.5094.0", + "name": "lacros_chrome_browsertests Lacros version skew testing ash 104.0.5095.0", "swarming": { "can_use_on_swarming_builders": true, "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v104.0.5094.0", - "revision": "version:104.0.5094.0" + "location": "lacros_version_skew_tests_v104.0.5095.0", + "revision": "version:104.0.5095.0" } ], "dimension_sets": [ @@ -91744,7 +91802,7 @@ }, "test": "lacros_chrome_browsertests", "test_id_prefix": "ninja://chrome/test:lacros_chrome_browsertests/", - "variant_id": "Lacros version skew testing ash 104.0.5094.0" + "variant_id": "Lacros version skew testing ash 104.0.5095.0" }, { "args": [ @@ -91864,20 +91922,20 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5094.0/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5095.0/test_ash_chrome" ], "merge": { "args": [], "script": "//testing/merge_scripts/standard_gtest_merge.py" }, - "name": "lacros_chrome_browsertests_run_in_series Lacros version skew testing ash 104.0.5094.0", + "name": "lacros_chrome_browsertests_run_in_series Lacros version skew testing ash 104.0.5095.0", "swarming": { "can_use_on_swarming_builders": true, "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v104.0.5094.0", - "revision": "version:104.0.5094.0" + "location": "lacros_version_skew_tests_v104.0.5095.0", + "revision": "version:104.0.5095.0" } ], "dimension_sets": [ @@ -91890,7 +91948,7 @@ }, "test": "lacros_chrome_browsertests_run_in_series", "test_id_prefix": "ninja://chrome/test:lacros_chrome_browsertests_run_in_series/", - "variant_id": "Lacros version skew testing ash 104.0.5094.0" + "variant_id": "Lacros version skew testing ash 104.0.5095.0" }, { "merge": { @@ -92625,20 +92683,20 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5094.0/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5095.0/test_ash_chrome" ], "merge": { "args": [], "script": "//testing/merge_scripts/standard_gtest_merge.py" }, - "name": "interactive_ui_tests Lacros version skew testing ash 104.0.5094.0", + "name": "interactive_ui_tests Lacros version skew testing ash 104.0.5095.0", "swarming": { "can_use_on_swarming_builders": true, "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v104.0.5094.0", - "revision": "version:104.0.5094.0" + "location": "lacros_version_skew_tests_v104.0.5095.0", + "revision": "version:104.0.5095.0" } ], "dimension_sets": [ @@ -92651,7 +92709,7 @@ }, "test": "interactive_ui_tests", "test_id_prefix": "ninja://chrome/test:interactive_ui_tests/", - "variant_id": "Lacros version skew testing ash 104.0.5094.0" + "variant_id": "Lacros version skew testing ash 104.0.5095.0" } ] },
diff --git a/testing/buildbot/filters/BUILD.gn b/testing/buildbot/filters/BUILD.gn index 5ce5c82..542533b 100644 --- a/testing/buildbot/filters/BUILD.gn +++ b/testing/buildbot/filters/BUILD.gn
@@ -253,10 +253,6 @@ data = [] } -source_set("perfetto_unittests_filters") { - data = [ "//testing/buildbot/filters/fuchsia.lsan.perfetto_unittests.filter" ] -} - source_set("gl_tests_filters") { testonly = true
diff --git a/testing/buildbot/filters/fuchsia.lsan.perfetto_unittests.filter b/testing/buildbot/filters/fuchsia.lsan.perfetto_unittests.filter deleted file mode 100644 index 2c4c99d..0000000 --- a/testing/buildbot/filters/fuchsia.lsan.perfetto_unittests.filter +++ /dev/null
@@ -1,2 +0,0 @@ -# crbug.com/1199334 Error messages only show up in klog --PagedMemoryTest.AccessUncommittedMemoryTriggersASAN
diff --git a/testing/buildbot/test_suite_exceptions.pyl b/testing/buildbot/test_suite_exceptions.pyl index 56054eb..d121d692 100644 --- a/testing/buildbot/test_suite_exceptions.pyl +++ b/testing/buildbot/test_suite_exceptions.pyl
@@ -2504,7 +2504,8 @@ }, 'fuchsia-fyi-x64-asan': { 'args': [ - '--test-launcher-filter-file=../../testing/buildbot/filters/fuchsia.lsan.perfetto_unittests.filter', + # TODO(crbug.com/1199334): Error messages only show up in klog. + '--gtest_filter=-PagedMemoryTest.AccessUncommittedMemoryTriggersASAN' ], }, }, @@ -3427,6 +3428,10 @@ ], 'modifications': { 'chromeos-amd64-generic-rel': { + 'args': [ + # Added to debug crbug.com/1293967. + '--extra-browser-args=--disable-features=BackgroundVideoPauseOptimization', + ], 'swarming': { 'quickrun_shards': 40, },
diff --git a/testing/buildbot/test_suites.pyl b/testing/buildbot/test_suites.pyl index 0809f259..207b902 100644 --- a/testing/buildbot/test_suites.pyl +++ b/testing/buildbot/test_suites.pyl
@@ -1911,6 +1911,7 @@ 'shards': 8, }, }, + 'filesystem_service_unittests': {}, 'unit_tests': { 'args': [ '--test-launcher-filter-file=../../testing/buildbot/filters/fuchsia.unit_tests.filter', @@ -7412,6 +7413,7 @@ 'CROS_HANA_RELEASE_LKGM', 'CROS_HANA_RELEASE_DEV', 'CROS_HANA_RELEASE_BETA', + 'CROS_HANA_RELEASE_STABLE', 'CROS_JACUZZI_RELEASE_LKGM', 'CROS_JACUZZI_RELEASE_DEV', 'CROS_JACUZZI_RELEASE_BETA', @@ -7438,9 +7440,11 @@ 'CROS_ATLAS_RELEASE_LKGM', 'CROS_ATLAS_RELEASE_DEV', 'CROS_ATLAS_RELEASE_BETA', + 'CROS_ATLAS_RELEASE_STABLE', 'CROS_EVE_RELEASE_LKGM', 'CROS_EVE_RELEASE_DEV', 'CROS_EVE_RELEASE_BETA', + 'CROS_EVE_RELEASE_STABLE', ] }, },
diff --git a/testing/buildbot/variants.pyl b/testing/buildbot/variants.pyl index 4115dbb..2ee0e7d 100644 --- a/testing/buildbot/variants.pyl +++ b/testing/buildbot/variants.pyl
@@ -22,15 +22,15 @@ }, 'LACROS_VERSION_SKEW_CANARY': { 'args': [ - '--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5094.0/test_ash_chrome', + '--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5095.0/test_ash_chrome', ], - 'identifier': 'Lacros version skew testing ash 104.0.5094.0', + 'identifier': 'Lacros version skew testing ash 104.0.5095.0', 'swarming': { 'cipd_packages': [ { 'cipd_package': 'chromium/testing/linux-ash-chromium/x86_64/ash.zip', - 'location': 'lacros_version_skew_tests_v104.0.5094.0', - 'revision': 'version:104.0.5094.0', + 'location': 'lacros_version_skew_tests_v104.0.5095.0', + 'revision': 'version:104.0.5095.0', }, ], }, @@ -919,6 +919,15 @@ 'enabled': True, 'identifier': 'ATLAS_RELEASE_BETA', }, + 'CROS_ATLAS_RELEASE_STABLE': { + 'skylab': { + 'cros_board': 'atlas', + 'cros_chrome_version': '102.0.5005.75', + 'cros_img': 'atlas-release/R102-14695.85.0', + }, + 'enabled': True, + 'identifier': 'ATLAS_RELEASE_STABLE', + }, 'CROS_EVE_RELEASE_LKGM': { 'skylab': { 'cros_board': 'eve', @@ -946,6 +955,15 @@ 'enabled': True, 'identifier': 'EVE_RELEASE_BETA', }, + 'CROS_EVE_RELEASE_STABLE': { + 'skylab': { + 'cros_board': 'eve', + 'cros_chrome_version': '102.0.5005.75', + 'cros_img': 'eve-release/R102-14695.85.0', + }, + 'enabled': True, + 'identifier': 'EVE_RELEASE_STABLE', + }, 'CROS_EVE_FULL': { 'skylab': { 'cros_board': 'eve', @@ -982,6 +1000,15 @@ 'enabled': True, 'identifier': 'HANA_RELEASE_BETA', }, + 'CROS_HANA_RELEASE_STABLE': { + 'skylab': { + 'cros_board': 'hana', + 'cros_chrome_version': '102.0.5005.75', + 'cros_img': 'hana-release/R102-14695.85.0', + }, + 'enabled': True, + 'identifier': 'HANA_RELEASE_STABLE', + }, 'CROS_JACUZZI_RELEASE_LKGM': { 'skylab': { 'cros_board': 'jacuzzi',
diff --git a/testing/test.gni b/testing/test.gni index 7594f85..8f8ef703 100644 --- a/testing/test.gni +++ b/testing/test.gni
@@ -626,10 +626,6 @@ data_deps += [ "//testing:test_scripts_shared" ] - # TODO(crbug.com/1199334): Because perfetto unit test is defined outside - # of chromium, the dependency to the filter file is added here. - data_deps += [ "//testing/buildbot/filters:perfetto_unittests_filters" ] - if (use_rts) { data_deps += [ ":${target_name}__rts_filters" ] }
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json index 279d319d..72c2e0d9 100644 --- a/testing/variations/fieldtrial_testing_config.json +++ b/testing/variations/fieldtrial_testing_config.json
@@ -4603,16 +4603,18 @@ ], "experiments": [ { - "name": "Enabled", + "name": "EnabledWithRichContentWithDismissalAndSnooze", "params": { + "BubbleVariant": "RichContentWithDismissalAndSnooze", "availability": "any", "event_trigger": "name:password_suggestions_iph_triggered;comparator:==0;window:1825;storage:1825", "event_used": "name:password_suggestions_shown;comparator:==0;window:90;storage:360", "session_rate": "any", "session_rate_impact": "none", - "snooze_params": "max_limit:1,snooze_interval:14" + "snooze_params": "max_limit:2,snooze_interval:4" }, "enable_features": [ + "BubbleRichIPH", "IPH_PasswordSuggestions" ] } @@ -4744,6 +4746,21 @@ ] } ], + "IOSWebChannels": [ + { + "platforms": [ + "ios" + ], + "experiments": [ + { + "name": "EnabledWebChannels", + "enable_features": [ + "EnableWebChannels" + ] + } + ] + } + ], "IdentifiabilityStudy": [ { "platforms": [
diff --git a/third_party/blink/renderer/bindings/generated_in_core.gni b/third_party/blink/renderer/bindings/generated_in_core.gni index bd3abb6..7fcb1b3 100644 --- a/third_party/blink/renderer/bindings/generated_in_core.gni +++ b/third_party/blink/renderer/bindings/generated_in_core.gni
@@ -655,6 +655,8 @@ "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_css_page_rule.h", "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_css_perspective.cc", "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_css_perspective.h", + "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_css_position_fallback_rule.h", + "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_css_position_fallback_rule.cc", "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_css_position_value.cc", "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_css_position_value.h", "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_css_property_rule.cc", @@ -697,6 +699,8 @@ "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_css_transition.h", "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_css_translate.cc", "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_css_translate.h", + "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_css_try_rule.h", + "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_css_try_rule.cc", "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_css_unit_value.cc", "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_css_unit_value.h", "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_css_unparsed_value.cc",
diff --git a/third_party/blink/renderer/bindings/generated_in_modules.gni b/third_party/blink/renderer/bindings/generated_in_modules.gni index c81539d..70dac509 100644 --- a/third_party/blink/renderer/bindings/generated_in_modules.gni +++ b/third_party/blink/renderer/bindings/generated_in_modules.gni
@@ -389,8 +389,8 @@ "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_gpu_buffer_binding_layout.h", "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_gpu_buffer_descriptor.cc", "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_gpu_buffer_descriptor.h", - "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_gpu_canvas_compositing_alpha_mode.cc", - "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_gpu_canvas_compositing_alpha_mode.h", + "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_gpu_canvas_alpha_mode.cc", + "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_gpu_canvas_alpha_mode.h", "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_gpu_canvas_configuration.cc", "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_gpu_canvas_configuration.h", "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_gpu_color_dict.cc",
diff --git a/third_party/blink/renderer/bindings/idl_in_core.gni b/third_party/blink/renderer/bindings/idl_in_core.gni index a10a511..507961f 100644 --- a/third_party/blink/renderer/bindings/idl_in_core.gni +++ b/third_party/blink/renderer/bindings/idl_in_core.gni
@@ -50,6 +50,7 @@ "//third_party/blink/renderer/core/css/css_media_rule.idl", "//third_party/blink/renderer/core/css/css_namespace_rule.idl", "//third_party/blink/renderer/core/css/css_page_rule.idl", + "//third_party/blink/renderer/core/css/css_position_fallback_rule.idl", "//third_party/blink/renderer/core/css/css_property_rule.idl", "//third_party/blink/renderer/core/css/css_rule.idl", "//third_party/blink/renderer/core/css/css_rule_list.idl", @@ -113,6 +114,7 @@ "//third_party/blink/renderer/core/css/style_media.idl", "//third_party/blink/renderer/core/css/style_sheet.idl", "//third_party/blink/renderer/core/css/style_sheet_list.idl", + "//third_party/blink/renderer/core/css/css_try_rule.idl", "//third_party/blink/renderer/core/document_transition/document_transition.idl", "//third_party/blink/renderer/core/document_transition/document_transition_callback.idl", "//third_party/blink/renderer/core/document_transition/document_transition_set_element_options.idl",
diff --git a/third_party/blink/renderer/build/scripts/core/css/properties/templates/css_properties.h.tmpl b/third_party/blink/renderer/build/scripts/core/css/properties/templates/css_properties.h.tmpl index 917c185c..282e1c3d 100644 --- a/third_party/blink/renderer/build/scripts/core/css/properties/templates/css_properties.h.tmpl +++ b/third_party/blink/renderer/build/scripts/core/css/properties/templates/css_properties.h.tmpl
@@ -52,6 +52,7 @@ (property.valid_for_canvas_formatted_text and 'kValidForCanvasFormattedText' or ''), (property.valid_for_canvas_formatted_text_run and 'kValidForCanvasFormattedTextRun' or ''), (property.valid_for_keyframe and 'kValidForKeyframe' or ''), + (property.valid_for_position_fallback and 'kValidForPositionFallback' or ''), (is_surrogate and 'kSurrogate' or ''), (property.font and 'kAffectsFont' or ''), (property.is_background and 'kBackground' or ''),
diff --git a/third_party/blink/renderer/core/css/build.gni b/third_party/blink/renderer/core/css/build.gni index a8ee0f9..bb41edf 100644 --- a/third_party/blink/renderer/core/css/build.gni +++ b/third_party/blink/renderer/core/css/build.gni
@@ -192,6 +192,8 @@ "css_pending_substitution_value.h", "css_pending_system_font_value.cc", "css_pending_system_font_value.h", + "css_position_fallback_rule.cc", + "css_position_fallback_rule.h", "css_primitive_value.cc", "css_primitive_value.h", "css_primitive_value_mappings.h", @@ -258,6 +260,8 @@ "css_to_length_conversion_data.h", "css_length_resolver.cc", "css_length_resolver.h", + "css_try_rule.cc", + "css_try_rule.h", "css_unicode_range_value.cc", "css_unicode_range_value.h", "css_unset_value.cc",
diff --git a/third_party/blink/renderer/core/css/css_position_fallback_rule.cc b/third_party/blink/renderer/core/css/css_position_fallback_rule.cc new file mode 100644 index 0000000..4a02968f --- /dev/null +++ b/third_party/blink/renderer/core/css/css_position_fallback_rule.cc
@@ -0,0 +1,80 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/core/css/css_position_fallback_rule.h" + +#include "third_party/blink/renderer/core/css/css_rule_list.h" +#include "third_party/blink/renderer/core/css/css_try_rule.h" +#include "third_party/blink/renderer/platform/wtf/text/string_builder.h" + +namespace blink { + +StyleRulePositionFallback::StyleRulePositionFallback(const AtomicString& name) + : StyleRuleBase(kPositionFallback), name_(name) {} + +StyleRulePositionFallback::StyleRulePositionFallback( + const StyleRulePositionFallback&) = default; + +StyleRulePositionFallback::~StyleRulePositionFallback() = default; + +void StyleRulePositionFallback::ParserAppendTryRule(StyleRuleTry* try_rule) { + try_rules_.push_back(try_rule); +} + +void StyleRulePositionFallback::TraceAfterDispatch(Visitor* visitor) const { + visitor->Trace(try_rules_); + StyleRuleBase::TraceAfterDispatch(visitor); +} + +CSSPositionFallbackRule::CSSPositionFallbackRule( + StyleRulePositionFallback* position_fallback_rule, + CSSStyleSheet* parent) + : CSSRule(parent), + position_fallback_rule_(position_fallback_rule), + rule_list_cssom_wrapper_( + MakeGarbageCollected<LiveCSSRuleList<CSSPositionFallbackRule>>( + this)) { + CreateChildRuleWrappers(); +} + +CSSPositionFallbackRule::~CSSPositionFallbackRule() = default; + +void CSSPositionFallbackRule::CreateChildRuleWrappers() { + child_rule_cssom_wrappers_.clear(); + child_rule_cssom_wrappers_.ReserveCapacity( + position_fallback_rule_->TryRules().size()); + for (StyleRuleTry* try_rule : position_fallback_rule_->TryRules()) { + child_rule_cssom_wrappers_.push_back( + MakeGarbageCollected<CSSTryRule>(try_rule, this)); + } +} + +String CSSPositionFallbackRule::cssText() const { + StringBuilder result; + result.Append("@position-fallback "); + result.Append(name()); + result.Append(" {\n"); + for (const CSSTryRule* child_rule : child_rule_cssom_wrappers_) { + result.Append(" "); + result.Append(child_rule->cssText()); + result.Append("\n"); + } + result.Append("}"); + return result.ReleaseString(); +} + +void CSSPositionFallbackRule::Reattach(StyleRuleBase* rule) { + DCHECK(rule); + position_fallback_rule_ = To<StyleRulePositionFallback>(rule); + CreateChildRuleWrappers(); +} + +void CSSPositionFallbackRule::Trace(Visitor* visitor) const { + visitor->Trace(position_fallback_rule_); + visitor->Trace(child_rule_cssom_wrappers_); + visitor->Trace(rule_list_cssom_wrapper_); + CSSRule::Trace(visitor); +} + +} // namespace blink
diff --git a/third_party/blink/renderer/core/css/css_position_fallback_rule.h b/third_party/blink/renderer/core/css/css_position_fallback_rule.h new file mode 100644 index 0000000..a0d9a34 --- /dev/null +++ b/third_party/blink/renderer/core/css/css_position_fallback_rule.h
@@ -0,0 +1,87 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_CSS_CSS_POSITION_FALLBACK_RULE_H_ +#define THIRD_PARTY_BLINK_RENDERER_CORE_CSS_CSS_POSITION_FALLBACK_RULE_H_ + +#include "third_party/blink/renderer/core/css/css_rule.h" +#include "third_party/blink/renderer/core/css/style_rule.h" + +namespace blink { + +class CSSTryRule; +class StyleRuleTry; + +class StyleRulePositionFallback final : public StyleRuleBase { + public: + explicit StyleRulePositionFallback(const AtomicString&); + StyleRulePositionFallback(const StyleRulePositionFallback&); + ~StyleRulePositionFallback(); + + const AtomicString& Name() const { return name_; } + const HeapVector<Member<StyleRuleTry>>& TryRules() const { + return try_rules_; + } + + void ParserAppendTryRule(StyleRuleTry*); + + StyleRulePositionFallback* Copy() const { + return MakeGarbageCollected<StyleRulePositionFallback>(*this); + } + + void TraceAfterDispatch(Visitor*) const; + + private: + AtomicString name_; + HeapVector<Member<StyleRuleTry>> try_rules_; +}; + +template <> +struct DowncastTraits<StyleRulePositionFallback> { + static bool AllowFrom(const StyleRuleBase& rule) { + return rule.IsPositionFallbackRule(); + } +}; + +class CSSPositionFallbackRule final : public CSSRule { + DEFINE_WRAPPERTYPEINFO(); + + public: + CSSPositionFallbackRule(StyleRulePositionFallback*, CSSStyleSheet* parent); + ~CSSPositionFallbackRule() final; + + String name() const { return position_fallback_rule_->Name(); } + + Type GetType() const final { return kPositionFallbackRule; } + CSSRuleList* cssRules() const final { return rule_list_cssom_wrapper_; } + + // For CSSRuleList. + unsigned length() const { return child_rule_cssom_wrappers_.size(); } + CSSTryRule* Item(unsigned index) const { + return index < length() ? child_rule_cssom_wrappers_[index] : nullptr; + } + + String cssText() const final; + void Reattach(StyleRuleBase*) final; + + void Trace(Visitor*) const final; + + private: + void CreateChildRuleWrappers(); + + Member<StyleRulePositionFallback> position_fallback_rule_; + HeapVector<Member<CSSTryRule>> child_rule_cssom_wrappers_; + Member<CSSRuleList> rule_list_cssom_wrapper_; +}; + +template <> +struct DowncastTraits<CSSPositionFallbackRule> { + static bool AllowFrom(const CSSRule& rule) { + return rule.GetType() == CSSRule::kPositionFallbackRule; + } +}; + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_CORE_CSS_CSS_POSITION_FALLBACK_RULE_H_
diff --git a/third_party/blink/renderer/core/css/css_position_fallback_rule.idl b/third_party/blink/renderer/core/css/css_position_fallback_rule.idl new file mode 100644 index 0000000..13a40cf1 --- /dev/null +++ b/third_party/blink/renderer/core/css/css_position_fallback_rule.idl
@@ -0,0 +1,11 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// TODO(crbug.com/1309178): Define CSSOM API for @position-fallback at rule. + +[Exposed=Window, RuntimeEnabled=CSSAnchorPositioning] +interface CSSPositionFallbackRule : CSSRule { + readonly attribute CSSOMString name; + readonly attribute CSSRuleList cssRules; +};
diff --git a/third_party/blink/renderer/core/css/css_properties.json5 b/third_party/blink/renderer/core/css/css_properties.json5 index 09f1520..c24bf91 100644 --- a/third_party/blink/renderer/core/css/css_properties.json5 +++ b/third_party/blink/renderer/core/css/css_properties.json5
@@ -638,6 +638,15 @@ default: true, valid_type: "bool", }, + + // - valid_for_position_fallback: true + // + // Whether the property can be used in a @try rule of @position-fallback + // https://tabatkins.github.io/specs/css-anchor-position/#fallback-rule + valid_for_position_fallback: { + default: false, + valid_type: "bool", + }, }, // Members in the data objects should appear in the same order as in the @@ -2063,6 +2072,7 @@ resolver: "bottom", }, supports_incremental_style: true, + valid_for_position_fallback: true, }, { name: "box-shadow", @@ -2774,6 +2784,7 @@ }, supports_incremental_style: true, valid_for_canvas_formatted_text: true, + valid_for_position_fallback: true, }, { name: "hyphens", @@ -2866,6 +2877,7 @@ resolver: "left", }, supports_incremental_style: true, + valid_for_position_fallback: true, }, { name: "letter-spacing", @@ -3147,6 +3159,7 @@ resolver: "vertical", }, supports_incremental_style: true, + valid_for_position_fallback: true, }, { name: "max-width", @@ -3164,6 +3177,7 @@ resolver: "horizontal", }, supports_incremental_style: true, + valid_for_position_fallback: true, }, { name: "min-height", @@ -3180,6 +3194,7 @@ resolver: "vertical", }, supports_incremental_style: true, + valid_for_position_fallback: true, }, { name: "min-width", @@ -3196,6 +3211,7 @@ resolver: "horizontal", }, supports_incremental_style: true, + valid_for_position_fallback: true, }, { name: "mix-blend-mode", @@ -3807,6 +3823,7 @@ resolver: "right", }, supports_incremental_style: true, + valid_for_position_fallback: true, }, { name: "r", @@ -4700,6 +4717,7 @@ resolver: "top", }, supports_incremental_style: true, + valid_for_position_fallback: true, }, { name: "touch-action", @@ -5566,6 +5584,7 @@ }, supports_incremental_style: true, valid_for_canvas_formatted_text: true, + valid_for_position_fallback: true, }, { name: "will-change", @@ -5624,6 +5643,7 @@ }, keywords: ["auto"], typedom_types: ["Keyword", "Length", "Percentage"], + valid_for_position_fallback: true, }, { name: "block-size", @@ -5635,6 +5655,7 @@ }, keywords: ["auto"], typedom_types: ["Keyword", "Length", "Percentage"], + valid_for_position_fallback: true, }, { name: "min-inline-size", @@ -5644,6 +5665,7 @@ resolver: "inline", }, typedom_types: ["Length", "Percentage"], + valid_for_position_fallback: true, }, { name: "min-block-size", @@ -5653,6 +5675,7 @@ resolver: "block", }, typedom_types: ["Length", "Percentage"], + valid_for_position_fallback: true, }, { name: "max-inline-size", @@ -5663,6 +5686,7 @@ }, keywords: ["none"], typedom_types: ["Keyword", "Length", "Percentage"], + valid_for_position_fallback: true, }, { name: "max-block-size", @@ -5673,6 +5697,7 @@ }, keywords: ["none"], typedom_types: ["Keyword", "Length", "Percentage"], + valid_for_position_fallback: true, }, { name: "margin-inline-start", @@ -5874,6 +5899,7 @@ resolver: "inline-start", }, typedom_types: ["Length", "Percentage"], + valid_for_position_fallback: true, }, { name: "inset-inline-end", @@ -5883,6 +5909,7 @@ resolver: "inline-end", }, typedom_types: ["Length", "Percentage"], + valid_for_position_fallback: true, }, { name: "inset-block-start", @@ -5892,6 +5919,7 @@ resolver: "block-start", }, typedom_types: ["Length", "Percentage"], + valid_for_position_fallback: true, }, { name: "inset-block-end", @@ -5901,6 +5929,7 @@ resolver: "block-end", }, typedom_types: ["Length", "Percentage"], + valid_for_position_fallback: true, }, { name: "border-start-start-radius", @@ -6608,16 +6637,19 @@ name: "inset", longhands: ["top", "right", "bottom", "left"], property_methods: ["ParseShorthand", "CSSValueFromComputedStyleInternal"], + valid_for_position_fallback: true, }, { name: "inset-block", longhands: ["inset-block-start", "inset-block-end"], property_methods: ["ParseShorthand", "CSSValueFromComputedStyleInternal"], + valid_for_position_fallback: true, }, { name: "inset-inline", longhands: ["inset-inline-start", "inset-inline-end"], property_methods: ["ParseShorthand", "CSSValueFromComputedStyleInternal"], + valid_for_position_fallback: true, }, { name: "list-style",
diff --git a/third_party/blink/renderer/core/css/css_rule.h b/third_party/blink/renderer/core/css/css_rule.h index f0dcd53..05262ce9 100644 --- a/third_party/blink/renderer/core/css/css_rule.h +++ b/third_party/blink/renderer/core/css/css_rule.h
@@ -71,6 +71,8 @@ kLayerStatementRule = 20, kFontPaletteValuesRule = 21, kScopeRule = 22, + kPositionFallbackRule = 23, + kTryRule = 24, }; virtual Type GetType() const = 0;
diff --git a/third_party/blink/renderer/core/css/css_try_rule.cc b/third_party/blink/renderer/core/css/css_try_rule.cc new file mode 100644 index 0000000..46afa52c --- /dev/null +++ b/third_party/blink/renderer/core/css/css_try_rule.cc
@@ -0,0 +1,51 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/core/css/css_try_rule.h" + +#include "third_party/blink/renderer/core/css/css_position_fallback_rule.h" +#include "third_party/blink/renderer/core/css/css_property_value_set.h" +#include "third_party/blink/renderer/platform/wtf/text/string_builder.h" + +namespace blink { + +StyleRuleTry::StyleRuleTry(CSSPropertyValueSet* properties) + : StyleRuleBase(kTry), properties_(properties) {} + +StyleRuleTry::~StyleRuleTry() = default; + +void StyleRuleTry::TraceAfterDispatch(Visitor* visitor) const { + visitor->Trace(properties_); + StyleRuleBase::TraceAfterDispatch(visitor); +} + +CSSTryRule::CSSTryRule(StyleRuleTry* try_rule, CSSPositionFallbackRule* parent) + : CSSRule(nullptr), try_rule_(try_rule) { + SetParentRule(parent); +} + +CSSTryRule::~CSSTryRule() = default; + +String CSSTryRule::cssText() const { + StringBuilder result; + result.Append("@try { "); + if (!try_rule_->Properties().IsEmpty()) { + result.Append(try_rule_->Properties().AsText()); + result.Append(" "); + } + result.Append("}"); + return result.ReleaseString(); +} + +void CSSTryRule::Reattach(StyleRuleBase* rule) { + DCHECK(rule); + try_rule_ = To<StyleRuleTry>(rule); +} + +void CSSTryRule::Trace(Visitor* visitor) const { + visitor->Trace(try_rule_); + CSSRule::Trace(visitor); +} + +} // namespace blink
diff --git a/third_party/blink/renderer/core/css/css_try_rule.h b/third_party/blink/renderer/core/css/css_try_rule.h new file mode 100644 index 0000000..51b1051 --- /dev/null +++ b/third_party/blink/renderer/core/css/css_try_rule.h
@@ -0,0 +1,53 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_CSS_CSS_TRY_RULE_H_ +#define THIRD_PARTY_BLINK_RENDERER_CORE_CSS_CSS_TRY_RULE_H_ + +#include "third_party/blink/renderer/core/css/css_rule.h" +#include "third_party/blink/renderer/core/css/style_rule.h" + +namespace blink { + +class CSSPositionFallbackRule; + +class StyleRuleTry final : public StyleRuleBase { + public: + explicit StyleRuleTry(CSSPropertyValueSet*); + ~StyleRuleTry(); + + const CSSPropertyValueSet& Properties() const { return *properties_; } + + void TraceAfterDispatch(Visitor*) const; + + private: + Member<CSSPropertyValueSet> properties_; +}; + +template <> +struct DowncastTraits<StyleRuleTry> { + static bool AllowFrom(const StyleRuleBase& rule) { return rule.IsTryRule(); } +}; + +class CSSTryRule final : public CSSRule { + DEFINE_WRAPPERTYPEINFO(); + + public: + CSSTryRule(StyleRuleTry*, CSSPositionFallbackRule* parent); + ~CSSTryRule() final; + + Type GetType() const final { return kTryRule; } + + String cssText() const final; + void Reattach(StyleRuleBase*) final; + + void Trace(Visitor*) const final; + + private: + Member<StyleRuleTry> try_rule_; +}; + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_CORE_CSS_CSS_TRY_RULE_H_
diff --git a/third_party/blink/renderer/core/css/css_try_rule.idl b/third_party/blink/renderer/core/css/css_try_rule.idl new file mode 100644 index 0000000..661a6ad --- /dev/null +++ b/third_party/blink/renderer/core/css/css_try_rule.idl
@@ -0,0 +1,8 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// TODO(crbug.com/1309178): Define CSSOM API for @try at rule. + +[Exposed=Window, RuntimeEnabled=CSSAnchorPositioning] +interface CSSTryRule : CSSRule { };
diff --git a/third_party/blink/renderer/core/css/parser/at_rule_descriptor_parser.cc b/third_party/blink/renderer/core/css/parser/at_rule_descriptor_parser.cc index 4117e5da..05a1b20 100644 --- a/third_party/blink/renderer/core/css/parser/at_rule_descriptor_parser.cc +++ b/third_party/blink/renderer/core/css/parser/at_rule_descriptor_parser.cc
@@ -188,6 +188,8 @@ case StyleRule::kScope: case StyleRule::kSupports: case StyleRule::kViewport: + case StyleRule::kPositionFallback: + case StyleRule::kTry: // TODO(andruud): Handle other descriptor types here. NOTREACHED(); return nullptr;
diff --git a/third_party/blink/renderer/core/css/parser/css_at_rule_id.cc b/third_party/blink/renderer/core/css/parser/css_at_rule_id.cc index 94676c0..cbc8bfb 100644 --- a/third_party/blink/renderer/core/css/parser/css_at_rule_id.cc +++ b/third_party/blink/renderer/core/css/parser/css_at_rule_id.cc
@@ -34,6 +34,11 @@ return kCSSAtRuleNamespace; if (EqualIgnoringASCIICase(name, "page")) return kCSSAtRulePage; + if (EqualIgnoringASCIICase(name, "position-fallback")) { + if (RuntimeEnabledFeatures::CSSAnchorPositioningEnabled()) + return kCSSAtRulePositionFallback; + return kCSSAtRuleInvalid; + } if (EqualIgnoringASCIICase(name, "property")) return kCSSAtRuleProperty; if (EqualIgnoringASCIICase(name, "container")) { @@ -55,6 +60,11 @@ } if (EqualIgnoringASCIICase(name, "supports")) return kCSSAtRuleSupports; + if (EqualIgnoringASCIICase(name, "try")) { + if (RuntimeEnabledFeatures::CSSAnchorPositioningEnabled()) + return kCSSAtRuleTry; + return kCSSAtRuleInvalid; + } if (EqualIgnoringASCIICase(name, "viewport")) return kCSSAtRuleViewport; if (EqualIgnoringASCIICase(name, "-webkit-keyframes")) @@ -114,6 +124,10 @@ case kCSSAtRuleViewport: feature = WebFeature::kCSSAtRuleViewport; break; + case kCSSAtRulePositionFallback: + case kCSSAtRuleTry: + // TODO(crbug.com/1309178): Add use counter. + return; case kCSSAtRuleWebkitKeyframes: feature = WebFeature::kCSSAtRuleWebkitKeyframes;
diff --git a/third_party/blink/renderer/core/css/parser/css_at_rule_id.h b/third_party/blink/renderer/core/css/parser/css_at_rule_id.h index 4613c48..05749e3 100644 --- a/third_party/blink/renderer/core/css/parser/css_at_rule_id.h +++ b/third_party/blink/renderer/core/css/parser/css_at_rule_id.h
@@ -22,12 +22,14 @@ kCSSAtRuleMedia, kCSSAtRuleNamespace, kCSSAtRulePage, + kCSSAtRulePositionFallback, kCSSAtRuleProperty, kCSSAtRuleContainer, kCSSAtRuleCounterStyle, kCSSAtRuleScope, kCSSAtRuleScrollTimeline, kCSSAtRuleSupports, + kCSSAtRuleTry, kCSSAtRuleViewport, kCSSAtRuleWebkitKeyframes, };
diff --git a/third_party/blink/renderer/core/css/parser/css_parser_impl.cc b/third_party/blink/renderer/core/css/parser/css_parser_impl.cc index 74ebbe4..233c4f3 100644 --- a/third_party/blink/renderer/core/css/parser/css_parser_impl.cc +++ b/third_party/blink/renderer/core/css/parser/css_parser_impl.cc
@@ -11,7 +11,9 @@ #include "third_party/blink/renderer/core/css/css_custom_ident_value.h" #include "third_party/blink/renderer/core/css/css_custom_property_declaration.h" #include "third_party/blink/renderer/core/css/css_keyframes_rule.h" +#include "third_party/blink/renderer/core/css/css_position_fallback_rule.h" #include "third_party/blink/renderer/core/css/css_style_sheet.h" +#include "third_party/blink/renderer/core/css/css_try_rule.h" #include "third_party/blink/renderer/core/css/parser/at_rule_descriptor_parser.h" #include "third_party/blink/renderer/core/css/parser/container_query_parser.h" #include "third_party/blink/renderer/core/css/parser/css_at_rule_id.h" @@ -447,6 +449,7 @@ StyleRuleBase* rule) { if (!rule || allowed_rules == CSSParserImpl::kKeyframeRules || allowed_rules == CSSParserImpl::kFontFeatureRules || + allowed_rules == CSSParserImpl::kTryRules || allowed_rules == CSSParserImpl::kNoRules) return allowed_rules; DCHECK_LE(allowed_rules, CSSParserImpl::kRegularRules); @@ -483,6 +486,9 @@ case kFontFeatureRuleList: allowed_rules = kFontFeatureRules; break; + case kPositionFallbackRuleList: + allowed_rules = kTryRules; + break; default: NOTREACHED(); } @@ -590,6 +596,11 @@ } else if (allowed_rules <= kAllowNamespaceRules && id == kCSSAtRuleNamespace) { return ConsumeNamespaceRule(stream); + } else if (allowed_rules == kTryRules) { + if (id == kCSSAtRuleTry) + return ConsumeTryRule(stream); + ConsumeErroneousAtRule(stream); + return nullptr; } else { DCHECK_LE(allowed_rules, kRegularRules); @@ -622,6 +633,8 @@ return ConsumeScopeRule(stream); case kCSSAtRuleCounterStyle: return ConsumeCounterStyleRule(stream); + case kCSSAtRulePositionFallback: + return ConsumePositionFallbackRule(stream); default: ConsumeErroneousAtRule(stream); return nullptr; // Parse error, unrecognised or not-allowed at-rule @@ -676,6 +689,16 @@ return MakeGarbageCollected<StyleRuleFontFace>( CreateCSSPropertyValueSet(parsed_properties_, kCSSFontFaceRuleMode)); } + if (allowed_rules == kTryRules) { + // We reach here only when there's a parse error. Treat everything before + // the first block we reach as a bad prelude, then skip this block. + stream.EnsureLookAhead(); + stream.ConsumeUntilPeekedTypeIs<kLeftBraceToken>(); + if (!stream.AtEnd()) { + CSSParserTokenStream::BlockGuard guard(stream); + } + return nullptr; + } NOTREACHED(); return nullptr; @@ -1265,6 +1288,71 @@ return MakeGarbageCollected<StyleRuleLayerBlock>(std::move(name), rules); } +StyleRulePositionFallback* CSSParserImpl::ConsumePositionFallbackRule( + CSSParserTokenStream& stream) { + wtf_size_t prelude_offset_start = stream.LookAheadOffset(); + CSSParserTokenRange prelude = ConsumeAtRulePrelude(stream); + wtf_size_t prelude_offset_end = stream.LookAheadOffset(); + if (!ConsumeEndOfPreludeForAtRuleWithBlock(stream)) + return nullptr; + CSSParserTokenStream::BlockGuard guard(stream); + + const CSSParserToken& name_token = prelude.ConsumeIncludingWhitespace(); + if (!prelude.AtEnd()) + return nullptr; + + String name; // <dashed-ident> + if (name_token.GetType() == kIdentToken) { + name = name_token.Value().ToString(); + if (!name.StartsWith("--")) + return nullptr; + } else { + return nullptr; + } + + if (observer_) { + observer_->StartRuleHeader(StyleRule::kPositionFallback, + prelude_offset_start); + observer_->EndRuleHeader(prelude_offset_end); + observer_->StartRuleBody(stream.Offset()); + } + + auto* position_fallback_rule = + MakeGarbageCollected<StyleRulePositionFallback>(AtomicString(name)); + ConsumeRuleList( + stream, kPositionFallbackRuleList, + [position_fallback_rule](StyleRuleBase* try_rule) { + position_fallback_rule->ParserAppendTryRule(To<StyleRuleTry>(try_rule)); + }); + + if (observer_) + observer_->EndRuleBody(stream.Offset()); + + return position_fallback_rule; +} + +StyleRuleTry* CSSParserImpl::ConsumeTryRule(CSSParserTokenStream& stream) { + wtf_size_t prelude_offset_start = stream.LookAheadOffset(); + CSSParserTokenRange prelude = ConsumeAtRulePrelude(stream); + wtf_size_t prelude_offset_end = stream.LookAheadOffset(); + if (!ConsumeEndOfPreludeForAtRuleWithBlock(stream)) + return nullptr; + CSSParserTokenStream::BlockGuard guard(stream); + + prelude.ConsumeWhitespace(); + if (!prelude.AtEnd()) + return nullptr; + + if (observer_) { + observer_->StartRuleHeader(StyleRule::kTry, prelude_offset_start); + observer_->EndRuleHeader(prelude_offset_end); + } + + ConsumeDeclarationList(stream, StyleRule::kTry); + return MakeGarbageCollected<StyleRuleTry>( + CreateCSSPropertyValueSet(parsed_properties_, context_->Mode())); +} + StyleRuleKeyframe* CSSParserImpl::ConsumeKeyframeStyleRule( const CSSParserTokenRange prelude, const RangeOffset& prelude_offset, @@ -1338,7 +1426,8 @@ rule_type == StyleRule::kCounterStyle || rule_type == StyleRule::kFontPaletteValues || rule_type == StyleRule::kScrollTimeline || - rule_type == StyleRule::kKeyframe || rule_type == StyleRule::kScope; + rule_type == StyleRule::kKeyframe || rule_type == StyleRule::kScope || + rule_type == StyleRule::kTry; bool use_observer = observer_ && is_observer_rule_type; if (use_observer) { observer_->StartRuleBody(stream.Offset()); @@ -1431,12 +1520,16 @@ } // @rules other than FontFace still handled with legacy code. - if (important && rule_type == StyleRule::kKeyframe) + if (important && + (rule_type == StyleRule::kKeyframe || rule_type == StyleRule::kTry)) { return; + } if (unresolved_property == CSSPropertyID::kVariable) { - if (rule_type != StyleRule::kStyle && rule_type != StyleRule::kKeyframe) + if (rule_type != StyleRule::kStyle && rule_type != StyleRule::kKeyframe && + rule_type != StyleRule::kTry) { return; + } AtomicString variable_name = lhs.Value().ToAtomicString(); bool is_animation_tainted = rule_type == StyleRule::kKeyframe; ConsumeVariableValue(tokenized_value, variable_name, important,
diff --git a/third_party/blink/renderer/core/css/parser/css_parser_impl.h b/third_party/blink/renderer/core/css/parser/css_parser_impl.h index fd85235b8..9391092 100644 --- a/third_party/blink/renderer/core/css/parser/css_parser_impl.h +++ b/third_party/blink/renderer/core/css/parser/css_parser_impl.h
@@ -36,8 +36,10 @@ class StyleRuleMedia; class StyleRuleNamespace; class StyleRulePage; +class StyleRulePositionFallback; class StyleRuleProperty; class StyleRuleSupports; +class StyleRuleTry; class StyleRuleViewport; class StyleSheetContents; class Element; @@ -68,6 +70,7 @@ kRegularRules, kKeyframeRules, kFontFeatureRules, + kTryRules, kNoRules, // For parsing at-rules inside declaration lists }; @@ -150,6 +153,7 @@ kRegularRuleList, kKeyframesRuleList, kFontFeatureRuleList, + kPositionFallbackRuleList, }; // Returns whether the first encountered rule was valid @@ -179,6 +183,8 @@ StyleRuleBase* ConsumeScopeRule(CSSParserTokenStream&); StyleRuleContainer* ConsumeContainerRule(CSSParserTokenStream&); StyleRuleBase* ConsumeLayerRule(CSSParserTokenStream&); + StyleRulePositionFallback* ConsumePositionFallbackRule(CSSParserTokenStream&); + StyleRuleTry* ConsumeTryRule(CSSParserTokenStream&); StyleRuleKeyframe* ConsumeKeyframeStyleRule(CSSParserTokenRange prelude, const RangeOffset& prelude_offset,
diff --git a/third_party/blink/renderer/core/css/parser/css_property_parser.cc b/third_party/blink/renderer/core/css/parser/css_property_parser.cc index 47778e4..53218fc6 100644 --- a/third_party/blink/renderer/core/css/parser/css_property_parser.cc +++ b/third_party/blink/renderer/core/css/parser/css_property_parser.cc
@@ -68,6 +68,8 @@ return true; case StyleRule::kKeyframe: return property.IsValidForKeyframe(); + case StyleRule::kTry: + return property.IsValidForPositionFallback(); default: NOTREACHED(); return false;
diff --git a/third_party/blink/renderer/core/css/properties/css_property.h b/third_party/blink/renderer/core/css/properties/css_property.h index c61eeae..6f9dfc8 100644 --- a/third_party/blink/renderer/core/css/properties/css_property.h +++ b/third_party/blink/renderer/core/css/properties/css_property.h
@@ -73,6 +73,9 @@ return flags_ & kValidForCanvasFormattedTextRun; } bool IsValidForKeyframe() const { return flags_ & kValidForKeyframe; } + bool IsValidForPositionFallback() const { + return flags_ & kValidForPositionFallback; + } bool IsSurrogate() const { return flags_ & kSurrogate; } bool AffectsFont() const { return flags_ & kAffectsFont; } bool IsBackground() const { return flags_ & kBackground; } @@ -191,6 +194,8 @@ kLegacyOverlapping = 1 << 29, // See valid_for_keyframes in css_properties.json5 kValidForKeyframe = 1 << 30, + // See valid_for_position_fallback in css_properties.json5 + kValidForPositionFallback = 1u << 31, }; constexpr CSSProperty(CSSPropertyID property_id,
diff --git a/third_party/blink/renderer/core/css/style_rule.cc b/third_party/blink/renderer/core/css/style_rule.cc index 9964d9328..840117c 100644 --- a/third_party/blink/renderer/core/css/style_rule.cc +++ b/third_party/blink/renderer/core/css/style_rule.cc
@@ -34,11 +34,13 @@ #include "third_party/blink/renderer/core/css/css_media_rule.h" #include "third_party/blink/renderer/core/css/css_namespace_rule.h" #include "third_party/blink/renderer/core/css/css_page_rule.h" +#include "third_party/blink/renderer/core/css/css_position_fallback_rule.h" #include "third_party/blink/renderer/core/css/css_property_rule.h" #include "third_party/blink/renderer/core/css/css_scope_rule.h" #include "third_party/blink/renderer/core/css/css_scroll_timeline_rule.h" #include "third_party/blink/renderer/core/css/css_style_rule.h" #include "third_party/blink/renderer/core/css/css_supports_rule.h" +#include "third_party/blink/renderer/core/css/css_try_rule.h" #include "third_party/blink/renderer/core/css/parser/container_query_parser.h" #include "third_party/blink/renderer/core/css/parser/css_parser_context.h" #include "third_party/blink/renderer/core/css/parser/css_parser_impl.h" @@ -128,6 +130,12 @@ case kCounterStyle: To<StyleRuleCounterStyle>(this)->TraceAfterDispatch(visitor); return; + case kPositionFallback: + To<StyleRulePositionFallback>(this)->TraceAfterDispatch(visitor); + return; + case kTry: + To<StyleRuleTry>(this)->TraceAfterDispatch(visitor); + return; } NOTREACHED(); } @@ -191,6 +199,12 @@ case kCounterStyle: To<StyleRuleCounterStyle>(this)->~StyleRuleCounterStyle(); return; + case kPositionFallback: + To<StyleRulePositionFallback>(this)->~StyleRulePositionFallback(); + return; + case kTry: + To<StyleRuleTry>(this)->~StyleRuleTry(); + return; } NOTREACHED(); } @@ -237,6 +251,11 @@ return To<StyleRuleContainer>(this)->Copy(); case kCounterStyle: return To<StyleRuleCounterStyle>(this)->Copy(); + case kPositionFallback: + return To<StyleRulePositionFallback>(this)->Copy(); + case kTry: + NOTREACHED(); + return nullptr; } NOTREACHED(); return nullptr; @@ -311,6 +330,11 @@ rule = MakeGarbageCollected<CSSCounterStyleRule>( To<StyleRuleCounterStyle>(self), parent_sheet); break; + case kPositionFallback: + rule = MakeGarbageCollected<CSSPositionFallbackRule>( + To<StyleRulePositionFallback>(self), parent_sheet); + break; + case kTry: case kKeyframe: case kCharset: case kViewport:
diff --git a/third_party/blink/renderer/core/css/style_rule.h b/third_party/blink/renderer/core/css/style_rule.h index 8d438e11..f1392c1f 100644 --- a/third_party/blink/renderer/core/css/style_rule.h +++ b/third_party/blink/renderer/core/css/style_rule.h
@@ -61,6 +61,8 @@ kScrollTimeline, kSupports, kViewport, + kPositionFallback, + kTry, }; // Name of a cascade layer as given by an @layer rule, split at '.' into a @@ -92,6 +94,8 @@ bool IsSupportsRule() const { return GetType() == kSupports; } bool IsViewportRule() const { return GetType() == kViewport; } bool IsImportRule() const { return GetType() == kImport; } + bool IsPositionFallbackRule() const { return GetType() == kPositionFallback; } + bool IsTryRule() const { return GetType() == kTry; } StyleRuleBase* Copy() const;
diff --git a/third_party/blink/renderer/core/css/style_sheet_contents.cc b/third_party/blink/renderer/core/css/style_sheet_contents.cc index 364af66..86f9ae0 100644 --- a/third_party/blink/renderer/core/css/style_sheet_contents.cc +++ b/third_party/blink/renderer/core/css/style_sheet_contents.cc
@@ -562,6 +562,8 @@ case StyleRuleBase::kSupports: case StyleRuleBase::kViewport: case StyleRuleBase::kFontPaletteValues: + case StyleRuleBase::kPositionFallback: + case StyleRuleBase::kTry: break; case StyleRuleBase::kCounterStyle: if (To<StyleRuleCounterStyle>(rule)->HasFailedOrCanceledSubresources())
diff --git a/third_party/blink/renderer/core/frame/frame_serializer.cc b/third_party/blink/renderer/core/frame/frame_serializer.cc index 4ddcbe7..ca21cbe 100644 --- a/third_party/blink/renderer/core/frame/frame_serializer.cc +++ b/third_party/blink/renderer/core/frame/frame_serializer.cc
@@ -464,6 +464,8 @@ case CSSRule::kNamespaceRule: case CSSRule::kViewportRule: case CSSRule::kLayerStatementRule: + case CSSRule::kPositionFallbackRule: + case CSSRule::kTryRule: break; } }
diff --git a/third_party/blink/renderer/core/layout/layout_box.cc b/third_party/blink/renderer/core/layout/layout_box.cc index ca9aa7b..051d9298 100644 --- a/third_party/blink/renderer/core/layout/layout_box.cc +++ b/third_party/blink/renderer/core/layout/layout_box.cc
@@ -83,6 +83,7 @@ #include "third_party/blink/renderer/core/layout/ng/geometry/ng_box_strut.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.h" #include "third_party/blink/renderer/core/layout/ng/layout_ng_fieldset.h" +#include "third_party/blink/renderer/core/layout/ng/legacy_layout_tree_walking.h" #include "third_party/blink/renderer/core/layout/ng/ng_box_fragment.h" #include "third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.h" #include "third_party/blink/renderer/core/layout/ng/ng_constraint_space.h" @@ -4540,7 +4541,9 @@ return; Length h; - if (IsOutOfFlowPositioned()) { + if (IsManagedByLayoutNG(*this) && HasOverrideLogicalHeight()) { + computed_values.extent_ = OverrideLogicalHeight(); + } else if (IsOutOfFlowPositioned()) { ComputePositionedLogicalHeight(computed_values); if (HasOverrideLogicalHeight()) computed_values.extent_ = OverrideLogicalHeight();
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object.cc b/third_party/blink/renderer/modules/accessibility/ax_object.cc index 1152fd1..c750263 100644 --- a/third_party/blink/renderer/modules/accessibility/ax_object.cc +++ b/third_party/blink/renderer/modules/accessibility/ax_object.cc
@@ -1230,6 +1230,9 @@ SerializeLangAttribute(node_data); // Propagates using all nodes' values. } + // Always try to serialize child tree ids. + SerializeChildTreeID(node_data); + // Return early. The following attributes are unnecessary for ignored nodes. // Exception: focusable ignored nodes are fully serialized, so that reasonable // verbalizations can be made if they actually receive focus. @@ -1275,6 +1278,23 @@ } } +void AXObject::SerializeChildTreeID(ui::AXNodeData* node_data) { + // If this is an HTMLFrameOwnerElement (such as an iframe), we may need + // to embed the ID of the child frame. + if (auto* html_frame_owner_element = + DynamicTo<HTMLFrameOwnerElement>(GetElement())) { + if (Frame* child_frame = html_frame_owner_element->ContentFrame()) { + absl::optional<base::UnguessableToken> child_token = + child_frame->GetEmbeddingToken(); + if (child_token && !(IsDetached() || ChildCountIncludingIgnored())) { + ui::AXTreeID child_tree_id = + ui::AXTreeID::FromToken(child_token.value()); + node_data->AddChildTreeId(child_tree_id); + } + } + } +} + void AXObject::SerializeChooserPopupAttributes(ui::AXNodeData* node_data) { AXObject* chooser_popup = ChooserPopup(); if (!chooser_popup) @@ -1902,21 +1922,6 @@ GetTextIndent()); } - // If this is an HTMLFrameOwnerElement (such as an iframe), we may need - // to embed the ID of the child frame. - if (auto* html_frame_owner_element = - DynamicTo<HTMLFrameOwnerElement>(GetElement())) { - if (Frame* child_frame = html_frame_owner_element->ContentFrame()) { - absl::optional<base::UnguessableToken> child_token = - child_frame->GetEmbeddingToken(); - if (child_token && !(IsDetached() || ChildCountIncludingIgnored())) { - ui::AXTreeID child_tree_id = - ui::AXTreeID::FromToken(child_token.value()); - node_data->AddChildTreeId(child_tree_id); - } - } - } - if (accessibility_mode.has_mode(ui::AXMode::kScreenReader) || accessibility_mode.has_mode(ui::AXMode::kPDF)) { // The DOMNodeID from Blink. Currently only populated when using
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object.h b/third_party/blink/renderer/modules/accessibility/ax_object.h index dd7c420..c902b5d 100644 --- a/third_party/blink/renderer/modules/accessibility/ax_object.h +++ b/third_party/blink/renderer/modules/accessibility/ax_object.h
@@ -1394,6 +1394,7 @@ // Helpers for serialization. void SerializeActionAttributes(ui::AXNodeData* node_data); + void SerializeChildTreeID(ui::AXNodeData* node_data); void SerializeChooserPopupAttributes(ui::AXNodeData* node_data); void SerializeColorAttributes(ui::AXNodeData* node_data); void SerializeElementAttributes(ui::AXNodeData* node_data);
diff --git a/third_party/blink/renderer/modules/payments/payment_request.cc b/third_party/blink/renderer/modules/payments/payment_request.cc index 8dabfaa3..e6a69e6 100644 --- a/third_party/blink/renderer/modules/payments/payment_request.cc +++ b/third_party/blink/renderer/modules/payments/payment_request.cc
@@ -448,7 +448,7 @@ WebFeature::kSecurePaymentConfirmation); output->secure_payment_confirmation = SecurePaymentConfirmationHelper::ParseSecurePaymentConfirmationData( - input, exception_state); + input, execution_context, exception_state); } }
diff --git a/third_party/blink/renderer/modules/payments/secure_payment_confirmation_helper.cc b/third_party/blink/renderer/modules/payments/secure_payment_confirmation_helper.cc index 606f675..0d9f545 100644 --- a/third_party/blink/renderer/modules/payments/secure_payment_confirmation_helper.cc +++ b/third_party/blink/renderer/modules/payments/secure_payment_confirmation_helper.cc
@@ -11,6 +11,7 @@ #include "third_party/blink/renderer/bindings/core/v8/native_value_traits_impl.h" #include "third_party/blink/renderer/bindings/modules/v8/v8_payment_credential_instrument.h" #include "third_party/blink/renderer/bindings/modules/v8/v8_secure_payment_confirmation_request.h" +#include "third_party/blink/renderer/core/execution_context/execution_context.h" #include "third_party/blink/renderer/modules/payments/secure_payment_confirmation_type_converter.h" #include "third_party/blink/renderer/platform/bindings/exception_state.h" @@ -27,6 +28,7 @@ ::payments::mojom::blink::SecurePaymentConfirmationRequestPtr SecurePaymentConfirmationHelper::ParseSecurePaymentConfirmationData( const ScriptValue& input, + ExecutionContext& execution_context, ExceptionState& exception_state) { DCHECK(!input.IsEmpty()); SecurePaymentConfirmationRequest* request = @@ -91,6 +93,13 @@ return nullptr; } + // Opt Out should only be carried through if the flag is enabled. + if (request->hasShowOptOut() && + !blink::RuntimeEnabledFeatures::SecurePaymentConfirmationOptOutEnabled( + &execution_context)) { + request->setShowOptOut(false); + } + return mojo::ConvertTo< payments::mojom::blink::SecurePaymentConfirmationRequestPtr>(request); }
diff --git a/third_party/blink/renderer/modules/payments/secure_payment_confirmation_helper.h b/third_party/blink/renderer/modules/payments/secure_payment_confirmation_helper.h index b5da12ab..3e92a49 100644 --- a/third_party/blink/renderer/modules/payments/secure_payment_confirmation_helper.h +++ b/third_party/blink/renderer/modules/payments/secure_payment_confirmation_helper.h
@@ -11,6 +11,7 @@ namespace blink { class ScriptValue; +class ExecutionContext; class ExceptionState; class SecurePaymentConfirmationHelper { @@ -20,7 +21,9 @@ // Parse 'secure-payment-confirmation' data in |input| and return the result // or throw an exception. static ::payments::mojom::blink::SecurePaymentConfirmationRequestPtr - ParseSecurePaymentConfirmationData(const ScriptValue& input, ExceptionState&); + ParseSecurePaymentConfirmationData(const ScriptValue& input, + ExecutionContext&, + ExceptionState&); }; } // namespace blink
diff --git a/third_party/blink/renderer/modules/payments/secure_payment_confirmation_type_converter.cc b/third_party/blink/renderer/modules/payments/secure_payment_confirmation_type_converter.cc index fcb7f89..df5cf82 100644 --- a/third_party/blink/renderer/modules/payments/secure_payment_confirmation_type_converter.cc +++ b/third_party/blink/renderer/modules/payments/secure_payment_confirmation_type_converter.cc
@@ -10,7 +10,6 @@ #include "third_party/blink/renderer/bindings/core/v8/v8_union_arraybuffer_arraybufferview.h" #include "third_party/blink/renderer/bindings/modules/v8/v8_payment_credential_instrument.h" #include "third_party/blink/renderer/modules/credentialmanagement/credential_manager_type_converters.h" -#include "third_party/blink/renderer/platform/runtime_enabled_features.h" #include "third_party/blink/renderer/platform/weborigin/kurl.h" #include "third_party/blink/renderer/platform/wtf/vector.h" @@ -59,10 +58,7 @@ if (input->hasPayeeName()) output->payee_name = input->payeeName(); - output->show_opt_out = false; - if (blink::RuntimeEnabledFeatures::SecurePaymentConfirmationOptOutEnabled()) { - output->show_opt_out = input->getShowOptOutOr(false); - } + output->show_opt_out = input->getShowOptOutOr(false); return output; }
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_canvas_configuration.idl b/third_party/blink/renderer/modules/webgpu/gpu_canvas_configuration.idl index df597f1..5f29ac9 100644 --- a/third_party/blink/renderer/modules/webgpu/gpu_canvas_configuration.idl +++ b/third_party/blink/renderer/modules/webgpu/gpu_canvas_configuration.idl
@@ -4,7 +4,7 @@ // https://gpuweb.github.io/gpuweb/ -enum GPUCanvasCompositingAlphaMode { +enum GPUCanvasAlphaMode { "opaque", "premultiplied", }; @@ -15,8 +15,9 @@ GPUTextureUsageFlags usage = 16; // GPUTextureUsage.RENDER_ATTACHMENT sequence<GPUTextureFormat> viewFormats = []; GPUPredefinedColorSpace colorSpace = "srgb"; - GPUCanvasCompositingAlphaMode compositingAlphaMode; + GPUCanvasAlphaMode alphaMode; - // TODO(crbug.com/1069302): Remove after deprecation period. + // TODO(crbug.com/1069302): Remove the following after a deprecation period. GPUExtent3D size; + GPUCanvasAlphaMode compositingAlphaMode; };
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_canvas_context.cc b/third_party/blink/renderer/modules/webgpu/gpu_canvas_context.cc index ed52571..7efb175 100644 --- a/third_party/blink/renderer/modules/webgpu/gpu_canvas_context.cc +++ b/third_party/blink/renderer/modules/webgpu/gpu_canvas_context.cc
@@ -6,7 +6,7 @@ #include "components/viz/common/resources/resource_format_utils.h" #include "third_party/blink/renderer/bindings/core/v8/v8_union_htmlcanvaselement_offscreencanvas.h" -#include "third_party/blink/renderer/bindings/modules/v8/v8_gpu_canvas_compositing_alpha_mode.h" +#include "third_party/blink/renderer/bindings/modules/v8/v8_gpu_canvas_alpha_mode.h" #include "third_party/blink/renderer/bindings/modules/v8/v8_gpu_canvas_configuration.h" #include "third_party/blink/renderer/bindings/modules/v8/v8_union_canvasrenderingcontext2d_gpucanvascontext_imagebitmaprenderingcontext_webgl2renderingcontext_webglrenderingcontext.h" #include "third_party/blink/renderer/bindings/modules/v8/v8_union_gpucanvascontext_imagebitmaprenderingcontext_offscreencanvasrenderingcontext2d_webgl2renderingcontext_webglrenderingcontext.h" @@ -206,15 +206,20 @@ return; } - alpha_mode_ = V8GPUCanvasCompositingAlphaMode::Enum::kPremultiplied; + alpha_mode_ = V8GPUCanvasAlphaMode::Enum::kPremultiplied; if (descriptor->hasCompositingAlphaMode()) { alpha_mode_ = descriptor->compositingAlphaMode().AsEnum(); + configured_device_->AddConsoleWarning( + "compositingAlphaMode is deprecated and will soon be removed. Please " + "set alphaMode instead."); + } else if (descriptor->hasAlphaMode()) { + alpha_mode_ = descriptor->alphaMode().AsEnum(); } else { configured_device_->AddConsoleWarning( - "The default GPUCanvasCompositingAlphaMode will change from " + "The default GPUCanvasAlphaMode will change from " "\"premultiplied\" to \"opaque\". " - "Please explicitly pass \"premultiplied\" if you would like to " - "continue using that compositing mode."); + "Please explicitly set alphaMode to \"premultiplied\" if you would " + "like to continue using that compositing mode."); } // TODO(crbug.com/1326473): Implement support for context viewFormats.
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_canvas_context.h b/third_party/blink/renderer/modules/webgpu/gpu_canvas_context.h index 7b40e24b..c58536f0 100644 --- a/third_party/blink/renderer/modules/webgpu/gpu_canvas_context.h +++ b/third_party/blink/renderer/modules/webgpu/gpu_canvas_context.h
@@ -100,7 +100,7 @@ Member<GPUDevice> configured_device_; WGPUTextureUsage usage_; WGPUTextureFormat format_; - V8GPUCanvasCompositingAlphaMode::Enum alpha_mode_; + V8GPUCanvasAlphaMode::Enum alpha_mode_; bool stopped_ = false;
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_swap_chain.cc b/third_party/blink/renderer/modules/webgpu/gpu_swap_chain.cc index 3b0d25a..a5823a8 100644 --- a/third_party/blink/renderer/modules/webgpu/gpu_swap_chain.cc +++ b/third_party/blink/renderer/modules/webgpu/gpu_swap_chain.cc
@@ -19,20 +19,19 @@ namespace blink { -GPUSwapChain::GPUSwapChain( - GPUCanvasContext* context, - GPUDevice* device, - WGPUTextureUsage usage, - WGPUTextureFormat format, - cc::PaintFlags::FilterQuality filter_quality, - V8GPUCanvasCompositingAlphaMode::Enum compositing_alpha_mode, - gfx::Size size) +GPUSwapChain::GPUSwapChain(GPUCanvasContext* context, + GPUDevice* device, + WGPUTextureUsage usage, + WGPUTextureFormat format, + cc::PaintFlags::FilterQuality filter_quality, + V8GPUCanvasAlphaMode::Enum alpha_mode, + gfx::Size size) : DawnObjectBase(device->GetDawnControlClient()), device_(device), context_(context), usage_(usage), format_(format), - compositing_alpha_mode_(compositing_alpha_mode), + alpha_mode_(alpha_mode), size_(size) { // TODO: Use label from GPUObjectDescriptorBase. swap_buffers_ = base::AdoptRef(new WebGPUSwapBufferProvider( @@ -41,8 +40,8 @@ // Note: SetContentsOpaque is only an optimization hint. It doesn't // actually make the contents opaque. - switch (compositing_alpha_mode) { - case V8GPUCanvasCompositingAlphaMode::Enum::kOpaque: { + switch (alpha_mode) { + case V8GPUCanvasAlphaMode::Enum::kOpaque: { CcLayer()->SetContentsOpaque(true); WGPUShaderModuleWGSLDescriptor wgsl_desc = { @@ -91,7 +90,7 @@ GetProcs().shaderModuleRelease(shader_module); break; } - case V8GPUCanvasCompositingAlphaMode::Enum::kPremultiplied: + case V8GPUCanvasAlphaMode::Enum::kPremultiplied: CcLayer()->SetContentsOpaque(false); break; } @@ -377,10 +376,9 @@ void GPUSwapChain::OnTextureTransferred() { DCHECK(texture_); // The texture is about to be transferred to the compositor. - // For compositing alpha mode Opaque, clear the alpha channel to - // 1.0. - switch (compositing_alpha_mode_) { - case V8GPUCanvasCompositingAlphaMode::Enum::kOpaque: { + // For alpha mode Opaque, clear the alpha channel to 1.0. + switch (alpha_mode_) { + case V8GPUCanvasAlphaMode::Enum::kOpaque: { WGPUTextureView attachment_view = GetProcs().textureCreateView(texture_->GetHandle(), nullptr); @@ -421,7 +419,7 @@ GetProcs().textureViewRelease(attachment_view); break; } - case V8GPUCanvasCompositingAlphaMode::Enum::kPremultiplied: + case V8GPUCanvasAlphaMode::Enum::kPremultiplied: break; } texture_ = nullptr;
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_swap_chain.h b/third_party/blink/renderer/modules/webgpu/gpu_swap_chain.h index 2e43efa..54c64fe 100644 --- a/third_party/blink/renderer/modules/webgpu/gpu_swap_chain.h +++ b/third_party/blink/renderer/modules/webgpu/gpu_swap_chain.h
@@ -5,7 +5,7 @@ #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_WEBGPU_GPU_SWAP_CHAIN_H_ #define THIRD_PARTY_BLINK_RENDERER_MODULES_WEBGPU_GPU_SWAP_CHAIN_H_ -#include "third_party/blink/renderer/bindings/modules/v8/v8_gpu_canvas_compositing_alpha_mode.h" +#include "third_party/blink/renderer/bindings/modules/v8/v8_gpu_canvas_alpha_mode.h" #include "third_party/blink/renderer/modules/webgpu/dawn_object.h" #include "third_party/blink/renderer/platform/graphics/gpu/webgpu_swap_buffer_provider.h" #include "third_party/blink/renderer/platform/heap/garbage_collected.h" @@ -31,7 +31,7 @@ WGPUTextureUsage, WGPUTextureFormat, cc::PaintFlags::FilterQuality, - V8GPUCanvasCompositingAlphaMode::Enum, + V8GPUCanvasAlphaMode::Enum, gfx::Size); GPUSwapChain(const GPUSwapChain&) = delete; @@ -81,7 +81,7 @@ Member<GPUCanvasContext> context_; const WGPUTextureUsage usage_; const WGPUTextureFormat format_; - const V8GPUCanvasCompositingAlphaMode::Enum compositing_alpha_mode_; + const V8GPUCanvasAlphaMode::Enum alpha_mode_; const gfx::Size size_; Member<GPUTexture> texture_;
diff --git a/third_party/blink/renderer/modules/xr/xr_system.cc b/third_party/blink/renderer/modules/xr/xr_system.cc index 36c1121..cbcc3738 100644 --- a/third_party/blink/renderer/modules/xr/xr_system.cc +++ b/third_party/blink/renderer/modules/xr/xr_system.cc
@@ -327,7 +327,6 @@ case device::mojom::XRSessionFeature::HIT_TEST: case device::mojom::XRSessionFeature::LIGHT_ESTIMATION: case device::mojom::XRSessionFeature::ANCHORS: - case device::mojom::XRSessionFeature::CAMERA_ACCESS: case device::mojom::XRSessionFeature::PLANE_DETECTION: case device::mojom::XRSessionFeature::DEPTH: case device::mojom::XRSessionFeature::IMAGE_TRACKING: @@ -336,6 +335,13 @@ return context->IsFeatureEnabled( mojom::blink::PermissionsPolicyFeature::kWebXr, ReportOptions::kReportOnFailure); + case device::mojom::XRSessionFeature::CAMERA_ACCESS: + return context->IsFeatureEnabled( + mojom::blink::PermissionsPolicyFeature::kWebXr, + ReportOptions::kReportOnFailure) && + context->IsFeatureEnabled( + mojom::blink::PermissionsPolicyFeature::kCamera, + ReportOptions::kReportOnFailure); } }
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5 index 758ece2..82f668fd 100644 --- a/third_party/blink/renderer/platform/runtime_enabled_features.json5 +++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -2129,6 +2129,8 @@ }, { name: "SecurePaymentConfirmationOptOut", + origin_trial_feature_name: "SecurePaymentConfirmationOptOut", + origin_trial_allows_third_party: true }, { name: "SendBeaconThrowForBlobWithNonSimpleType",
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations index 1b1759a9..bae3641 100644 --- a/third_party/blink/web_tests/TestExpectations +++ b/third_party/blink/web_tests/TestExpectations
@@ -7052,4 +7052,10 @@ crbug.com/1330300 fast/css-grid-layout/grid-auto-repeat-huge-grid-006.html [ Pass Timeout ] # Sheriff 2022-05-31 -crbug.com/1330555 [ Mac11 ] external/wpt/service-workers/service-worker/resource-timing-fetch-variants.https.html [ Pass Failure ] +crbug.com/1330555 external/wpt/service-workers/service-worker/resource-timing-fetch-variants.https.html [ Pass Failure ] + +# fast/canvas/OffscreenCanvas-2d-drawImage.html +crbug.com/1273409 [ Mac11 ] fast/canvas/OffscreenCanvas-2d-drawImage.html [ Pass Failure ] +crbug.com/1273409 [ Mac11-arm64 ] fast/canvas/OffscreenCanvas-2d-drawImage.html [ Pass Failure ] +crbug.com/1273409 [ Linux ] fast/canvas/OffscreenCanvas-2d-drawImage.html [ Pass Failure ] +crbug.com/1273409 [ Win10.20h2 ] fast/canvas/OffscreenCanvas-2d-drawImage.html [ Pass Failure ]
diff --git a/third_party/blink/web_tests/bindings/webidl-sequence-conversion.html b/third_party/blink/web_tests/bindings/webidl-sequence-conversion.html index 1264659..4a4ba86 100644 --- a/third_party/blink/web_tests/bindings/webidl-sequence-conversion.html +++ b/third_party/blink/web_tests/bindings/webidl-sequence-conversion.html
@@ -9,8 +9,12 @@ var div = document.querySelector("div"); var clickEvent = null; + var path = null; - div.addEventListener("click", function (ev) { clickEvent = ev; }); + div.addEventListener("click", function (ev) { + clickEvent = ev; + path = ev.composedPath(); + }); div.click(); assert_not_equals(clickEvent, null, "click event captured"); @@ -23,14 +27,12 @@ set: function () { setter_called = true; } }); - var path = clickEvent.path; - delete Array.prototype["0"]; assert_false(getter_called, "Array.prototype[0] getter called"); assert_false(setter_called, "Array.prototype[0] setter called"); - assert_equals(clickEvent.path.length, 5, "click event path length"); + assert_equals(path.length, 5, "click event path length"); }, "conversion should use [[DefineOwnProperty]]");
diff --git a/third_party/blink/web_tests/external/wpt/credential-management/fedcm-logout.sub.https.html b/third_party/blink/web_tests/external/wpt/credential-management/fedcm-logout.sub.https.html index 7024638..ad775d8 100644 --- a/third_party/blink/web_tests/external/wpt/credential-management/fedcm-logout.sub.https.html +++ b/third_party/blink/web_tests/external/wpt/credential-management/fedcm-logout.sub.https.html
@@ -4,7 +4,9 @@ <script src="/resources/testharness.js"></script> <script src="/resources/testharnessreport.js"></script> <body> -<script> +<script type="module"> + import {set_fedcm_cookie} from './support/fedcm-helper.js'; + const url_prefix = 'https://{{host}}:{{ports[https][0]}}/credential-management/support/'; const test_options = { federated: { @@ -28,18 +30,8 @@ nonce: '2', }; - async function set_cookie() { - // Wait for the identity provider's cookie to be installed. - await new Promise(resolve => { - const img = document.createElement('img'); - img.addEventListener('error', resolve); - img.src = url_prefix + 'set_cookie'; - document.body.appendChild(img); - }); - } - promise_test(async t => { - await set_cookie(); + await set_fedcm_cookie(); const cred = await navigator.credentials.get(test_options); const token = await cred.login(login_options); assert_equals(token.idToken, "token"); @@ -54,7 +46,7 @@ }, "logout should throw an exception when not logged in."); promise_test(async t => { - await set_cookie(); + await set_fedcm_cookie(); const cred = await navigator.credentials.get(test_options_no_hint); const token = await cred.login(login_options); assert_equals(token.idToken, "token");
diff --git a/third_party/blink/web_tests/external/wpt/credential-management/fedcm-network-requests.sub.https.html b/third_party/blink/web_tests/external/wpt/credential-management/fedcm-network-requests.sub.https.html index 0984189..16da68b 100644 --- a/third_party/blink/web_tests/external/wpt/credential-management/fedcm-network-requests.sub.https.html +++ b/third_party/blink/web_tests/external/wpt/credential-management/fedcm-network-requests.sub.https.html
@@ -6,16 +6,12 @@ <body> -<script> +<script type="module"> +import {set_fedcm_cookie} from './support/fedcm-helper.js'; + const url_prefix = 'https://{{host}}:{{ports[https][0]}}/credential-management/support/'; promise_test(async t => { - // Install and wait for the identity provider's cookie to be installed. - await new Promise(resolve => { - const img = document.createElement('img'); - img.src = url_prefix + 'set_cookie'; - img.addEventListener('error', resolve); - document.body.appendChild(img); - }); + await set_fedcm_cookie(); const result = await navigator.credentials.get({ federated: { providers: [{
diff --git a/third_party/blink/web_tests/external/wpt/credential-management/support/fedcm-helper.js b/third_party/blink/web_tests/external/wpt/credential-management/support/fedcm-helper.js index 428a2fc..1017427c 100644 --- a/third_party/blink/web_tests/external/wpt/credential-management/support/fedcm-helper.js +++ b/third_party/blink/web_tests/external/wpt/credential-management/support/fedcm-helper.js
@@ -18,3 +18,13 @@ } }, name, properties); } + +// Set the identity provider cookie. +export function set_fedcm_cookie() { + return new Promise(resolve => { + const img = document.createElement('img'); + img.src = 'support/set_cookie'; + img.addEventListener('error', resolve); + document.body.appendChild(img); + }); +}
diff --git a/third_party/blink/web_tests/fast/dom/shadow/event-path-for-user-agent-shadow-tree.html b/third_party/blink/web_tests/fast/dom/shadow/event-path-for-user-agent-shadow-tree.html index 652d784..a8e223f 100644 --- a/third_party/blink/web_tests/fast/dom/shadow/event-path-for-user-agent-shadow-tree.html +++ b/third_party/blink/web_tests/fast/dom/shadow/event-path-for-user-agent-shadow-tree.html
@@ -18,8 +18,8 @@ <script> ['sandbox', 'details', 'details-child', 'summary', 'summary-child'].forEach(function(path) { getNodeInComposedTree(path).addEventListener('click', function(event) { - debug('\nevent.path on node ' + dumpNode(event.currentTarget)); - debug(dumpNodeList(event.path)); + debug('\nevent.composedPath() on node ' + dumpNode(event.currentTarget)); + debug(dumpNodeList(event.composedPath())); }); }); var clickEvent = document.createEvent("MouseEvents");
diff --git a/third_party/blink/web_tests/fast/dom/shadow/event-path-load.html b/third_party/blink/web_tests/fast/dom/shadow/event-path-load.html index 60e13a0..cf488bf 100644 --- a/third_party/blink/web_tests/fast/dom/shadow/event-path-load.html +++ b/third_party/blink/web_tests/fast/dom/shadow/event-path-load.html
@@ -7,14 +7,14 @@ var img = document.getElementById("img"); img.onload = function (e) { - var path = e.path; + var path = e.composedPath(); test(function () { assert_equals(path.length, 4, 'path.length'); assert_equals(path[0], img, 'path[0] should be img'); assert_equals(path[1], document.body, 'path[1] should be body'); assert_equals(path[2], document.documentElement, 'path[2] should be html'); assert_equals(path[3], document, 'path[3] should be document'); - }, "event.path for img.onload"); + }, "event.composedPath() for img.onload"); done(); }; </script>
diff --git a/third_party/blink/web_tests/fast/dom/shadow/event-path-not-in-document.html b/third_party/blink/web_tests/fast/dom/shadow/event-path-not-in-document.html index fbd0851..a4aaf63 100644 --- a/third_party/blink/web_tests/fast/dom/shadow/event-path-not-in-document.html +++ b/third_party/blink/web_tests/fast/dom/shadow/event-path-not-in-document.html
@@ -10,7 +10,7 @@ var elementNotInDocument = document.createElement('div'); elementNotInDocument.id = "element-not-in-document"; elementNotInDocument.addEventListener('click', function(event) { - debug(dumpNodeList(event.path)); + debug(dumpNodeList(event.composedPath())); }); var clickEvent = document.createEvent("MouseEvents"); clickEvent.initMouseEvent('click', true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
diff --git a/third_party/blink/web_tests/fast/dom/shadow/event-path-svg.html b/third_party/blink/web_tests/fast/dom/shadow/event-path-svg.html index aab00fa..07171af 100644 --- a/third_party/blink/web_tests/fast/dom/shadow/event-path-svg.html +++ b/third_party/blink/web_tests/fast/dom/shadow/event-path-svg.html
@@ -7,7 +7,7 @@ </svg> <script> function handler(evnet) { - debug(dumpNodeList(event.path)); + debug(dumpNodeList(event.composedPath())); } circle.addEventListener('click', handler); document.addEventListener('click', handler);
diff --git a/third_party/blink/web_tests/fast/dom/shadow/event-path-window-load.html b/third_party/blink/web_tests/fast/dom/shadow/event-path-window-load.html index ea08b521..78d8b4e 100644 --- a/third_party/blink/web_tests/fast/dom/shadow/event-path-window-load.html +++ b/third_party/blink/web_tests/fast/dom/shadow/event-path-window-load.html
@@ -6,11 +6,11 @@ var img = document.getElementById("img"); window.onload = function (e) { - var path = e.path; + var path = e.composedPath(); test(function () { assert_equals(path.length, 1, 'path.length'); assert_equals(path[0], window, 'path[0] should be window'); - }, "event.path for window.onload"); + }, "event.composedPath() for window.onload"); done(); }; </script>
diff --git a/third_party/blink/web_tests/fast/dom/shadow/event-path.html b/third_party/blink/web_tests/fast/dom/shadow/event-path.html index 1172e79b..12fb7f1 100644 --- a/third_party/blink/web_tests/fast/dom/shadow/event-path.html +++ b/third_party/blink/web_tests/fast/dom/shadow/event-path.html
@@ -10,19 +10,19 @@ <script> var b = document.getElementById('b'); b.addEventListener('click', function(event) { - var path = event.path; + var path = event.composedPath(); test(function () { assert_event_path_for_b(path); - }, "event.path in click event"); + }, "event.composedPath() in click event"); path[1] = ''; test(function () { - assert_event_path_for_b(event.path); - }, "Make sure that event.path returns static NodeList."); + assert_event_path_for_b(event.composedPath()); + }, "Make sure that event.composedPath() returns static NodeList."); }); var clickEvent = document.createEvent("MouseEvents"); test(function () { - assert_equals(clickEvent.path.length, 0); -}, "Make sure that event.path is empty before dispatching the event."); + assert_equals(clickEvent.composedPath().length, 0); +}, "Make sure that event.composedPath() is empty before dispatching the event."); clickEvent.initMouseEvent('click', true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null); b.dispatchEvent(clickEvent); @@ -38,6 +38,6 @@ } test(function () { - assert_event_path_for_b(clickEvent.path); -}, "Make sure that event.path isn't emptified after dispatching the event."); + assert_equals(clickEvent.composedPath().length, 0); +}, "Make sure that event.composedPath() is emptified after dispatching the event."); </script>
diff --git a/third_party/blink/web_tests/fast/events/crash-on-querying-event-path.html b/third_party/blink/web_tests/fast/events/crash-on-querying-event-path.html index 9598291..046e247 100644 --- a/third_party/blink/web_tests/fast/events/crash-on-querying-event-path.html +++ b/third_party/blink/web_tests/fast/events/crash-on-querying-event-path.html
@@ -21,7 +21,7 @@ } function tryToCrash() { - // Access of eventObj.path caused the crash. + // Access of eventObj.composedPath() caused the crash. // The test is somewhat flaky, in that the test may pass as correct // despite the bug being the code. The exact conditions // are unclear, but 1, asan helps detect the crash and 2, the @@ -31,7 +31,7 @@ gc(); gc(); gc(); - var path = eventObj.path; + var path = eventObj.composedPath(); debug(path); testPassed('totally did not crash.');
diff --git a/third_party/blink/web_tests/platform/generic/fast/dom/shadow/event-path-for-user-agent-shadow-tree-expected.txt b/third_party/blink/web_tests/platform/generic/fast/dom/shadow/event-path-for-user-agent-shadow-tree-expected.txt index 13079d8..5760fb8 100644 --- a/third_party/blink/web_tests/platform/generic/fast/dom/shadow/event-path-for-user-agent-shadow-tree-expected.txt +++ b/third_party/blink/web_tests/platform/generic/fast/dom/shadow/event-path-for-user-agent-shadow-tree-expected.txt
@@ -1,27 +1,27 @@ Dispaching a click event on #details-child -event.path on node #details-child +event.composedPath() on node #details-child #details-child, #details, #sandbox, [object HTMLBodyElement], [object HTMLHtmlElement], [object HTMLDocument], [object Window], length: 7 -event.path on node #details +event.composedPath() on node #details #details-child, #details, #sandbox, [object HTMLBodyElement], [object HTMLHtmlElement], [object HTMLDocument], [object Window], length: 7 -event.path on node #sandbox +event.composedPath() on node #sandbox #details-child, #details, #sandbox, [object HTMLBodyElement], [object HTMLHtmlElement], [object HTMLDocument], [object Window], length: 7 Dispaching a click event on #summary-child -event.path on node #summary-child +event.composedPath() on node #summary-child #summary-child, #summary, #details, #sandbox, [object HTMLBodyElement], [object HTMLHtmlElement], [object HTMLDocument], [object Window], length: 8 -event.path on node #summary +event.composedPath() on node #summary #summary-child, #summary, #details, #sandbox, [object HTMLBodyElement], [object HTMLHtmlElement], [object HTMLDocument], [object Window], length: 8 -event.path on node #details +event.composedPath() on node #details #summary-child, #summary, #details, #sandbox, [object HTMLBodyElement], [object HTMLHtmlElement], [object HTMLDocument], [object Window], length: 8 -event.path on node #sandbox +event.composedPath() on node #sandbox #summary-child, #summary, #details, #sandbox, [object HTMLBodyElement], [object HTMLHtmlElement], [object HTMLDocument], [object Window], length: 8 PASS successfullyParsed is true
diff --git a/third_party/blink/web_tests/platform/generic/webexposed/global-interface-listing-expected.txt b/third_party/blink/web_tests/platform/generic/webexposed/global-interface-listing-expected.txt index a2aa4de..4c9387d 100644 --- a/third_party/blink/web_tests/platform/generic/webexposed/global-interface-listing-expected.txt +++ b/third_party/blink/web_tests/platform/generic/webexposed/global-interface-listing-expected.txt
@@ -826,6 +826,11 @@ getter length method constructor setter length +interface CSSPositionFallbackRule : CSSRule + attribute @@toStringTag + getter cssRules + getter name + method constructor interface CSSPositionValue : CSSStyleValue attribute @@toStringTag getter x @@ -999,6 +1004,9 @@ setter x setter y setter z +interface CSSTryRule : CSSRule + attribute @@toStringTag + method constructor interface CSSUnitValue : CSSNumericValue attribute @@toStringTag getter unit
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-anchor-position/at-fallback-position-allowed-declarations.html b/third_party/blink/web_tests/wpt_internal/css/css-anchor-position/at-fallback-position-allowed-declarations.html new file mode 100644 index 0000000..938361e --- /dev/null +++ b/third_party/blink/web_tests/wpt_internal/css/css-anchor-position/at-fallback-position-allowed-declarations.html
@@ -0,0 +1,89 @@ +<!DOCTYPE html> +<title>Tests which properties are allowed in @fallback-position</title> +<link rel="help" href="https://tabatkins.github.io/specs/css-anchor-position/#fallback-rule"> +<link rel="author" href="mailto:xiaochengh@chromium.org"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style id="style"></style> +<script> +function cleanup() { + while (style.sheet.cssRules.length) + style.sheet.deleteRule(0); +} + +function test_allowed_property(property) { + test(t => { + t.add_cleanup(cleanup); + const value = '1px'; + const serialization = `${property}: ${value}`; + const rule = `@position-fallback --foo { @try { ${property}: ${value}; } }`; + const index = style.sheet.insertRule(rule); + const parsed = style.sheet.cssRules[index].cssRules[0]; + assert_equals(parsed.cssText, `@try { ${serialization}; }`); + }, `${property} is allowed in @fallback-position`); +} + +function test_disallowed_property(property) { + test(t => { + t.add_cleanup(cleanup); + const value = '1px'; + const rule = `@position-fallback --foo { @try { ${property}: ${value}; } }`; + const index = style.sheet.insertRule(rule); + const parsed = style.sheet.cssRules[index].cssRules[0]; + assert_equals(parsed.cssText, `@try { }`); + }, `${property} is disallowed in @fallback-position`); +} + +// Inset properties are allowed +test_allowed_property('top'); +test_allowed_property('bottom'); +test_allowed_property('left'); +test_allowed_property('right'); +test_allowed_property('inset-block-start'); +test_allowed_property('inset-block-end'); +test_allowed_property('inset-inline-start'); +test_allowed_property('inset-inline-end'); +test_allowed_property('inset-block'); +test_allowed_property('inset-inline'); +test_allowed_property('inset'); + +// Sizing properties are allowed +test_allowed_property('width'); +test_allowed_property('height'); +test_allowed_property('block-size'); +test_allowed_property('inline-size'); +test_allowed_property('min-width'); +test_allowed_property('min-height'); +test_allowed_property('min-block-size'); +test_allowed_property('min-inline-size'); +test_allowed_property('max-width'); +test_allowed_property('max-height'); +test_allowed_property('max-block-size'); +test_allowed_property('max-inline-size'); + +// Custom properties are allowed +test_allowed_property('--custom'); + +// Margin properties are disallowed +test_disallowed_property('margin-left'); +test_disallowed_property('margin-right'); +test_disallowed_property('margin-top'); +test_disallowed_property('margin-bottom'); +test_disallowed_property('margin'); + +// Test some other disallowed properties +test_disallowed_property('font-size'); +test_disallowed_property('border-width'); +test_disallowed_property('padding'); +test_disallowed_property('display'); +test_disallowed_property('position'); +test_disallowed_property('float'); + +test(t => { + t.add_cleanup(cleanup); + const rule = `@position-fallback --foo { @try { top: 1px !important; } }`; + const index = style.sheet.insertRule(rule); + const parsed = style.sheet.cssRules[index].cssRules[0]; + assert_equals(parsed.cssText, `@try { }`); +}, `!important declarations are disallowed in @fallback-position`); +</script>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-anchor-position/at-fallback-position-parse.html b/third_party/blink/web_tests/wpt_internal/css/css-anchor-position/at-fallback-position-parse.html new file mode 100644 index 0000000..1c1ff2f --- /dev/null +++ b/third_party/blink/web_tests/wpt_internal/css/css-anchor-position/at-fallback-position-parse.html
@@ -0,0 +1,39 @@ +<!DOCTYPE html> +<title>Tests parsing of @fallback-position rule</title> +<link rel="help" href="https://tabatkins.github.io/specs/css-anchor-position/#fallback-rule"> +<link rel="author" href="mailto:xiaochengh@chromium.org"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/parsing-testcommon.js"></script> +<script> +test_valid_rule('@position-fallback --foo {\n}'); +test_valid_rule(`@position-fallback --foo { + @try { } +}`); + +// @position-fallback needs exactly one <dashed-ident> as its name +test_invalid_rule('@position-fallback {\n}'); +test_invalid_rule('@position-fallback foo {\n}'); +test_invalid_rule('@position-fallback --foo --bar {\n}'); +test_invalid_rule('@position-fallback --foo, --bar {\n}'); + +// @position-fallback accepts only @try blocks as its child rules. Other +// contents should be ignored. +test_valid_rule('@position-fallback --foo { top: 1px; }', + '@position-fallback --foo {\n}'); +test_valid_rule('@position-fallback --foo { --bar: 1px; }', + '@position-fallback --foo {\n}'); +test_valid_rule('@position-fallback --foo { @keyframes bar {} }', + '@position-fallback --foo {\n}'); +test_valid_rule('@position-fallback --foo { @font-face {} }', + '@position-fallback --foo {\n}'); +test_valid_rule('@position-fallback --foo { arbitrary garbage }', + '@position-fallback --foo {\n}'); + +// @try accepts only regular style declarations. Other contents should be +// ignored. +test_valid_rule('@position-fallback --foo { @try { @keyframes bar { } } }', + '@position-fallback --foo {\n @try { }\n}'); +test_valid_rule('@position-fallback --foo { @try { @font-face { } } }', + '@position-fallback --foo {\n @try { }\n}'); +</script>
diff --git a/third_party/pyyaml/LICENSE b/third_party/pyyaml/LICENSE new file mode 100644 index 0000000..2f1b8e1 --- /dev/null +++ b/third_party/pyyaml/LICENSE
@@ -0,0 +1,20 @@ +Copyright (c) 2017-2021 Ingy döt Net +Copyright (c) 2006-2016 Kirill Simonov + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE.
diff --git a/third_party/pyyaml/README.chromium b/third_party/pyyaml/README.chromium new file mode 100644 index 0000000..fb1cd4b --- /dev/null +++ b/third_party/pyyaml/README.chromium
@@ -0,0 +1,14 @@ +Name: pyyaml +Short Name: pyyaml +URL: https://github.com/yaml/pyyaml +Version: 8cdff2c80573b8be8e8ad28929264a913a63aa33 +CPEPrefix: unknown +Security Critical: no +License: MIT +License File: NOT_SHIPPED + +Description: +A full-featured YAML processing framework for Python + +Retrieved by downloading https://github.com/yaml/pyyaml/tree/release/6.0 at revision 8cdff2c80573b8be8e8ad28929264a913a63aa33. +Only the content of https://github.com/yaml/pyyaml/tree/release/6.0/lib/yaml is found in this directory.
diff --git a/third_party/pyyaml/__init__.py b/third_party/pyyaml/__init__.py new file mode 100644 index 0000000..465041d --- /dev/null +++ b/third_party/pyyaml/__init__.py
@@ -0,0 +1,390 @@ + +from .error import * + +from .tokens import * +from .events import * +from .nodes import * + +from .loader import * +from .dumper import * + +__version__ = '6.0' +try: + from .cyaml import * + __with_libyaml__ = True +except ImportError: + __with_libyaml__ = False + +import io + +#------------------------------------------------------------------------------ +# XXX "Warnings control" is now deprecated. Leaving in the API function to not +# break code that uses it. +#------------------------------------------------------------------------------ +def warnings(settings=None): + if settings is None: + return {} + +#------------------------------------------------------------------------------ +def scan(stream, Loader=Loader): + """ + Scan a YAML stream and produce scanning tokens. + """ + loader = Loader(stream) + try: + while loader.check_token(): + yield loader.get_token() + finally: + loader.dispose() + +def parse(stream, Loader=Loader): + """ + Parse a YAML stream and produce parsing events. + """ + loader = Loader(stream) + try: + while loader.check_event(): + yield loader.get_event() + finally: + loader.dispose() + +def compose(stream, Loader=Loader): + """ + Parse the first YAML document in a stream + and produce the corresponding representation tree. + """ + loader = Loader(stream) + try: + return loader.get_single_node() + finally: + loader.dispose() + +def compose_all(stream, Loader=Loader): + """ + Parse all YAML documents in a stream + and produce corresponding representation trees. + """ + loader = Loader(stream) + try: + while loader.check_node(): + yield loader.get_node() + finally: + loader.dispose() + +def load(stream, Loader): + """ + Parse the first YAML document in a stream + and produce the corresponding Python object. + """ + loader = Loader(stream) + try: + return loader.get_single_data() + finally: + loader.dispose() + +def load_all(stream, Loader): + """ + Parse all YAML documents in a stream + and produce corresponding Python objects. + """ + loader = Loader(stream) + try: + while loader.check_data(): + yield loader.get_data() + finally: + loader.dispose() + +def full_load(stream): + """ + Parse the first YAML document in a stream + and produce the corresponding Python object. + + Resolve all tags except those known to be + unsafe on untrusted input. + """ + return load(stream, FullLoader) + +def full_load_all(stream): + """ + Parse all YAML documents in a stream + and produce corresponding Python objects. + + Resolve all tags except those known to be + unsafe on untrusted input. + """ + return load_all(stream, FullLoader) + +def safe_load(stream): + """ + Parse the first YAML document in a stream + and produce the corresponding Python object. + + Resolve only basic YAML tags. This is known + to be safe for untrusted input. + """ + return load(stream, SafeLoader) + +def safe_load_all(stream): + """ + Parse all YAML documents in a stream + and produce corresponding Python objects. + + Resolve only basic YAML tags. This is known + to be safe for untrusted input. + """ + return load_all(stream, SafeLoader) + +def unsafe_load(stream): + """ + Parse the first YAML document in a stream + and produce the corresponding Python object. + + Resolve all tags, even those known to be + unsafe on untrusted input. + """ + return load(stream, UnsafeLoader) + +def unsafe_load_all(stream): + """ + Parse all YAML documents in a stream + and produce corresponding Python objects. + + Resolve all tags, even those known to be + unsafe on untrusted input. + """ + return load_all(stream, UnsafeLoader) + +def emit(events, stream=None, Dumper=Dumper, + canonical=None, indent=None, width=None, + allow_unicode=None, line_break=None): + """ + Emit YAML parsing events into a stream. + If stream is None, return the produced string instead. + """ + getvalue = None + if stream is None: + stream = io.StringIO() + getvalue = stream.getvalue + dumper = Dumper(stream, canonical=canonical, indent=indent, width=width, + allow_unicode=allow_unicode, line_break=line_break) + try: + for event in events: + dumper.emit(event) + finally: + dumper.dispose() + if getvalue: + return getvalue() + +def serialize_all(nodes, stream=None, Dumper=Dumper, + canonical=None, indent=None, width=None, + allow_unicode=None, line_break=None, + encoding=None, explicit_start=None, explicit_end=None, + version=None, tags=None): + """ + Serialize a sequence of representation trees into a YAML stream. + If stream is None, return the produced string instead. + """ + getvalue = None + if stream is None: + if encoding is None: + stream = io.StringIO() + else: + stream = io.BytesIO() + getvalue = stream.getvalue + dumper = Dumper(stream, canonical=canonical, indent=indent, width=width, + allow_unicode=allow_unicode, line_break=line_break, + encoding=encoding, version=version, tags=tags, + explicit_start=explicit_start, explicit_end=explicit_end) + try: + dumper.open() + for node in nodes: + dumper.serialize(node) + dumper.close() + finally: + dumper.dispose() + if getvalue: + return getvalue() + +def serialize(node, stream=None, Dumper=Dumper, **kwds): + """ + Serialize a representation tree into a YAML stream. + If stream is None, return the produced string instead. + """ + return serialize_all([node], stream, Dumper=Dumper, **kwds) + +def dump_all(documents, stream=None, Dumper=Dumper, + default_style=None, default_flow_style=False, + canonical=None, indent=None, width=None, + allow_unicode=None, line_break=None, + encoding=None, explicit_start=None, explicit_end=None, + version=None, tags=None, sort_keys=True): + """ + Serialize a sequence of Python objects into a YAML stream. + If stream is None, return the produced string instead. + """ + getvalue = None + if stream is None: + if encoding is None: + stream = io.StringIO() + else: + stream = io.BytesIO() + getvalue = stream.getvalue + dumper = Dumper(stream, default_style=default_style, + default_flow_style=default_flow_style, + canonical=canonical, indent=indent, width=width, + allow_unicode=allow_unicode, line_break=line_break, + encoding=encoding, version=version, tags=tags, + explicit_start=explicit_start, explicit_end=explicit_end, sort_keys=sort_keys) + try: + dumper.open() + for data in documents: + dumper.represent(data) + dumper.close() + finally: + dumper.dispose() + if getvalue: + return getvalue() + +def dump(data, stream=None, Dumper=Dumper, **kwds): + """ + Serialize a Python object into a YAML stream. + If stream is None, return the produced string instead. + """ + return dump_all([data], stream, Dumper=Dumper, **kwds) + +def safe_dump_all(documents, stream=None, **kwds): + """ + Serialize a sequence of Python objects into a YAML stream. + Produce only basic YAML tags. + If stream is None, return the produced string instead. + """ + return dump_all(documents, stream, Dumper=SafeDumper, **kwds) + +def safe_dump(data, stream=None, **kwds): + """ + Serialize a Python object into a YAML stream. + Produce only basic YAML tags. + If stream is None, return the produced string instead. + """ + return dump_all([data], stream, Dumper=SafeDumper, **kwds) + +def add_implicit_resolver(tag, regexp, first=None, + Loader=None, Dumper=Dumper): + """ + Add an implicit scalar detector. + If an implicit scalar value matches the given regexp, + the corresponding tag is assigned to the scalar. + first is a sequence of possible initial characters or None. + """ + if Loader is None: + loader.Loader.add_implicit_resolver(tag, regexp, first) + loader.FullLoader.add_implicit_resolver(tag, regexp, first) + loader.UnsafeLoader.add_implicit_resolver(tag, regexp, first) + else: + Loader.add_implicit_resolver(tag, regexp, first) + Dumper.add_implicit_resolver(tag, regexp, first) + +def add_path_resolver(tag, path, kind=None, Loader=None, Dumper=Dumper): + """ + Add a path based resolver for the given tag. + A path is a list of keys that forms a path + to a node in the representation tree. + Keys can be string values, integers, or None. + """ + if Loader is None: + loader.Loader.add_path_resolver(tag, path, kind) + loader.FullLoader.add_path_resolver(tag, path, kind) + loader.UnsafeLoader.add_path_resolver(tag, path, kind) + else: + Loader.add_path_resolver(tag, path, kind) + Dumper.add_path_resolver(tag, path, kind) + +def add_constructor(tag, constructor, Loader=None): + """ + Add a constructor for the given tag. + Constructor is a function that accepts a Loader instance + and a node object and produces the corresponding Python object. + """ + if Loader is None: + loader.Loader.add_constructor(tag, constructor) + loader.FullLoader.add_constructor(tag, constructor) + loader.UnsafeLoader.add_constructor(tag, constructor) + else: + Loader.add_constructor(tag, constructor) + +def add_multi_constructor(tag_prefix, multi_constructor, Loader=None): + """ + Add a multi-constructor for the given tag prefix. + Multi-constructor is called for a node if its tag starts with tag_prefix. + Multi-constructor accepts a Loader instance, a tag suffix, + and a node object and produces the corresponding Python object. + """ + if Loader is None: + loader.Loader.add_multi_constructor(tag_prefix, multi_constructor) + loader.FullLoader.add_multi_constructor(tag_prefix, multi_constructor) + loader.UnsafeLoader.add_multi_constructor(tag_prefix, multi_constructor) + else: + Loader.add_multi_constructor(tag_prefix, multi_constructor) + +def add_representer(data_type, representer, Dumper=Dumper): + """ + Add a representer for the given type. + Representer is a function accepting a Dumper instance + and an instance of the given data type + and producing the corresponding representation node. + """ + Dumper.add_representer(data_type, representer) + +def add_multi_representer(data_type, multi_representer, Dumper=Dumper): + """ + Add a representer for the given type. + Multi-representer is a function accepting a Dumper instance + and an instance of the given data type or subtype + and producing the corresponding representation node. + """ + Dumper.add_multi_representer(data_type, multi_representer) + +class YAMLObjectMetaclass(type): + """ + The metaclass for YAMLObject. + """ + def __init__(cls, name, bases, kwds): + super(YAMLObjectMetaclass, cls).__init__(name, bases, kwds) + if 'yaml_tag' in kwds and kwds['yaml_tag'] is not None: + if isinstance(cls.yaml_loader, list): + for loader in cls.yaml_loader: + loader.add_constructor(cls.yaml_tag, cls.from_yaml) + else: + cls.yaml_loader.add_constructor(cls.yaml_tag, cls.from_yaml) + + cls.yaml_dumper.add_representer(cls, cls.to_yaml) + +class YAMLObject(metaclass=YAMLObjectMetaclass): + """ + An object that can dump itself to a YAML stream + and load itself from a YAML stream. + """ + + __slots__ = () # no direct instantiation, so allow immutable subclasses + + yaml_loader = [Loader, FullLoader, UnsafeLoader] + yaml_dumper = Dumper + + yaml_tag = None + yaml_flow_style = None + + @classmethod + def from_yaml(cls, loader, node): + """ + Convert a representation node to a Python object. + """ + return loader.construct_yaml_object(node, cls) + + @classmethod + def to_yaml(cls, dumper, data): + """ + Convert a Python object to a representation node. + """ + return dumper.represent_yaml_object(cls.yaml_tag, data, cls, + flow_style=cls.yaml_flow_style) +
diff --git a/third_party/pyyaml/composer.py b/third_party/pyyaml/composer.py new file mode 100644 index 0000000..6d15cb4 --- /dev/null +++ b/third_party/pyyaml/composer.py
@@ -0,0 +1,139 @@ + +__all__ = ['Composer', 'ComposerError'] + +from .error import MarkedYAMLError +from .events import * +from .nodes import * + +class ComposerError(MarkedYAMLError): + pass + +class Composer: + + def __init__(self): + self.anchors = {} + + def check_node(self): + # Drop the STREAM-START event. + if self.check_event(StreamStartEvent): + self.get_event() + + # If there are more documents available? + return not self.check_event(StreamEndEvent) + + def get_node(self): + # Get the root node of the next document. + if not self.check_event(StreamEndEvent): + return self.compose_document() + + def get_single_node(self): + # Drop the STREAM-START event. + self.get_event() + + # Compose a document if the stream is not empty. + document = None + if not self.check_event(StreamEndEvent): + document = self.compose_document() + + # Ensure that the stream contains no more documents. + if not self.check_event(StreamEndEvent): + event = self.get_event() + raise ComposerError("expected a single document in the stream", + document.start_mark, "but found another document", + event.start_mark) + + # Drop the STREAM-END event. + self.get_event() + + return document + + def compose_document(self): + # Drop the DOCUMENT-START event. + self.get_event() + + # Compose the root node. + node = self.compose_node(None, None) + + # Drop the DOCUMENT-END event. + self.get_event() + + self.anchors = {} + return node + + def compose_node(self, parent, index): + if self.check_event(AliasEvent): + event = self.get_event() + anchor = event.anchor + if anchor not in self.anchors: + raise ComposerError(None, None, "found undefined alias %r" + % anchor, event.start_mark) + return self.anchors[anchor] + event = self.peek_event() + anchor = event.anchor + if anchor is not None: + if anchor in self.anchors: + raise ComposerError("found duplicate anchor %r; first occurrence" + % anchor, self.anchors[anchor].start_mark, + "second occurrence", event.start_mark) + self.descend_resolver(parent, index) + if self.check_event(ScalarEvent): + node = self.compose_scalar_node(anchor) + elif self.check_event(SequenceStartEvent): + node = self.compose_sequence_node(anchor) + elif self.check_event(MappingStartEvent): + node = self.compose_mapping_node(anchor) + self.ascend_resolver() + return node + + def compose_scalar_node(self, anchor): + event = self.get_event() + tag = event.tag + if tag is None or tag == '!': + tag = self.resolve(ScalarNode, event.value, event.implicit) + node = ScalarNode(tag, event.value, + event.start_mark, event.end_mark, style=event.style) + if anchor is not None: + self.anchors[anchor] = node + return node + + def compose_sequence_node(self, anchor): + start_event = self.get_event() + tag = start_event.tag + if tag is None or tag == '!': + tag = self.resolve(SequenceNode, None, start_event.implicit) + node = SequenceNode(tag, [], + start_event.start_mark, None, + flow_style=start_event.flow_style) + if anchor is not None: + self.anchors[anchor] = node + index = 0 + while not self.check_event(SequenceEndEvent): + node.value.append(self.compose_node(node, index)) + index += 1 + end_event = self.get_event() + node.end_mark = end_event.end_mark + return node + + def compose_mapping_node(self, anchor): + start_event = self.get_event() + tag = start_event.tag + if tag is None or tag == '!': + tag = self.resolve(MappingNode, None, start_event.implicit) + node = MappingNode(tag, [], + start_event.start_mark, None, + flow_style=start_event.flow_style) + if anchor is not None: + self.anchors[anchor] = node + while not self.check_event(MappingEndEvent): + #key_event = self.peek_event() + item_key = self.compose_node(node, None) + #if item_key in node.value: + # raise ComposerError("while composing a mapping", start_event.start_mark, + # "found duplicate key", key_event.start_mark) + item_value = self.compose_node(node, item_key) + #node.value[item_key] = item_value + node.value.append((item_key, item_value)) + end_event = self.get_event() + node.end_mark = end_event.end_mark + return node +
diff --git a/third_party/pyyaml/constructor.py b/third_party/pyyaml/constructor.py new file mode 100644 index 0000000..619acd3 --- /dev/null +++ b/third_party/pyyaml/constructor.py
@@ -0,0 +1,748 @@ + +__all__ = [ + 'BaseConstructor', + 'SafeConstructor', + 'FullConstructor', + 'UnsafeConstructor', + 'Constructor', + 'ConstructorError' +] + +from .error import * +from .nodes import * + +import collections.abc, datetime, base64, binascii, re, sys, types + +class ConstructorError(MarkedYAMLError): + pass + +class BaseConstructor: + + yaml_constructors = {} + yaml_multi_constructors = {} + + def __init__(self): + self.constructed_objects = {} + self.recursive_objects = {} + self.state_generators = [] + self.deep_construct = False + + def check_data(self): + # If there are more documents available? + return self.check_node() + + def check_state_key(self, key): + """Block special attributes/methods from being set in a newly created + object, to prevent user-controlled methods from being called during + deserialization""" + if self.get_state_keys_blacklist_regexp().match(key): + raise ConstructorError(None, None, + "blacklisted key '%s' in instance state found" % (key,), None) + + def get_data(self): + # Construct and return the next document. + if self.check_node(): + return self.construct_document(self.get_node()) + + def get_single_data(self): + # Ensure that the stream contains a single document and construct it. + node = self.get_single_node() + if node is not None: + return self.construct_document(node) + return None + + def construct_document(self, node): + data = self.construct_object(node) + while self.state_generators: + state_generators = self.state_generators + self.state_generators = [] + for generator in state_generators: + for dummy in generator: + pass + self.constructed_objects = {} + self.recursive_objects = {} + self.deep_construct = False + return data + + def construct_object(self, node, deep=False): + if node in self.constructed_objects: + return self.constructed_objects[node] + if deep: + old_deep = self.deep_construct + self.deep_construct = True + if node in self.recursive_objects: + raise ConstructorError(None, None, + "found unconstructable recursive node", node.start_mark) + self.recursive_objects[node] = None + constructor = None + tag_suffix = None + if node.tag in self.yaml_constructors: + constructor = self.yaml_constructors[node.tag] + else: + for tag_prefix in self.yaml_multi_constructors: + if tag_prefix is not None and node.tag.startswith(tag_prefix): + tag_suffix = node.tag[len(tag_prefix):] + constructor = self.yaml_multi_constructors[tag_prefix] + break + else: + if None in self.yaml_multi_constructors: + tag_suffix = node.tag + constructor = self.yaml_multi_constructors[None] + elif None in self.yaml_constructors: + constructor = self.yaml_constructors[None] + elif isinstance(node, ScalarNode): + constructor = self.__class__.construct_scalar + elif isinstance(node, SequenceNode): + constructor = self.__class__.construct_sequence + elif isinstance(node, MappingNode): + constructor = self.__class__.construct_mapping + if tag_suffix is None: + data = constructor(self, node) + else: + data = constructor(self, tag_suffix, node) + if isinstance(data, types.GeneratorType): + generator = data + data = next(generator) + if self.deep_construct: + for dummy in generator: + pass + else: + self.state_generators.append(generator) + self.constructed_objects[node] = data + del self.recursive_objects[node] + if deep: + self.deep_construct = old_deep + return data + + def construct_scalar(self, node): + if not isinstance(node, ScalarNode): + raise ConstructorError(None, None, + "expected a scalar node, but found %s" % node.id, + node.start_mark) + return node.value + + def construct_sequence(self, node, deep=False): + if not isinstance(node, SequenceNode): + raise ConstructorError(None, None, + "expected a sequence node, but found %s" % node.id, + node.start_mark) + return [self.construct_object(child, deep=deep) + for child in node.value] + + def construct_mapping(self, node, deep=False): + if not isinstance(node, MappingNode): + raise ConstructorError(None, None, + "expected a mapping node, but found %s" % node.id, + node.start_mark) + mapping = {} + for key_node, value_node in node.value: + key = self.construct_object(key_node, deep=deep) + if not isinstance(key, collections.abc.Hashable): + raise ConstructorError("while constructing a mapping", node.start_mark, + "found unhashable key", key_node.start_mark) + value = self.construct_object(value_node, deep=deep) + mapping[key] = value + return mapping + + def construct_pairs(self, node, deep=False): + if not isinstance(node, MappingNode): + raise ConstructorError(None, None, + "expected a mapping node, but found %s" % node.id, + node.start_mark) + pairs = [] + for key_node, value_node in node.value: + key = self.construct_object(key_node, deep=deep) + value = self.construct_object(value_node, deep=deep) + pairs.append((key, value)) + return pairs + + @classmethod + def add_constructor(cls, tag, constructor): + if not 'yaml_constructors' in cls.__dict__: + cls.yaml_constructors = cls.yaml_constructors.copy() + cls.yaml_constructors[tag] = constructor + + @classmethod + def add_multi_constructor(cls, tag_prefix, multi_constructor): + if not 'yaml_multi_constructors' in cls.__dict__: + cls.yaml_multi_constructors = cls.yaml_multi_constructors.copy() + cls.yaml_multi_constructors[tag_prefix] = multi_constructor + +class SafeConstructor(BaseConstructor): + + def construct_scalar(self, node): + if isinstance(node, MappingNode): + for key_node, value_node in node.value: + if key_node.tag == 'tag:yaml.org,2002:value': + return self.construct_scalar(value_node) + return super().construct_scalar(node) + + def flatten_mapping(self, node): + merge = [] + index = 0 + while index < len(node.value): + key_node, value_node = node.value[index] + if key_node.tag == 'tag:yaml.org,2002:merge': + del node.value[index] + if isinstance(value_node, MappingNode): + self.flatten_mapping(value_node) + merge.extend(value_node.value) + elif isinstance(value_node, SequenceNode): + submerge = [] + for subnode in value_node.value: + if not isinstance(subnode, MappingNode): + raise ConstructorError("while constructing a mapping", + node.start_mark, + "expected a mapping for merging, but found %s" + % subnode.id, subnode.start_mark) + self.flatten_mapping(subnode) + submerge.append(subnode.value) + submerge.reverse() + for value in submerge: + merge.extend(value) + else: + raise ConstructorError("while constructing a mapping", node.start_mark, + "expected a mapping or list of mappings for merging, but found %s" + % value_node.id, value_node.start_mark) + elif key_node.tag == 'tag:yaml.org,2002:value': + key_node.tag = 'tag:yaml.org,2002:str' + index += 1 + else: + index += 1 + if merge: + node.value = merge + node.value + + def construct_mapping(self, node, deep=False): + if isinstance(node, MappingNode): + self.flatten_mapping(node) + return super().construct_mapping(node, deep=deep) + + def construct_yaml_null(self, node): + self.construct_scalar(node) + return None + + bool_values = { + 'yes': True, + 'no': False, + 'true': True, + 'false': False, + 'on': True, + 'off': False, + } + + def construct_yaml_bool(self, node): + value = self.construct_scalar(node) + return self.bool_values[value.lower()] + + def construct_yaml_int(self, node): + value = self.construct_scalar(node) + value = value.replace('_', '') + sign = +1 + if value[0] == '-': + sign = -1 + if value[0] in '+-': + value = value[1:] + if value == '0': + return 0 + elif value.startswith('0b'): + return sign*int(value[2:], 2) + elif value.startswith('0x'): + return sign*int(value[2:], 16) + elif value[0] == '0': + return sign*int(value, 8) + elif ':' in value: + digits = [int(part) for part in value.split(':')] + digits.reverse() + base = 1 + value = 0 + for digit in digits: + value += digit*base + base *= 60 + return sign*value + else: + return sign*int(value) + + inf_value = 1e300 + while inf_value != inf_value*inf_value: + inf_value *= inf_value + nan_value = -inf_value/inf_value # Trying to make a quiet NaN (like C99). + + def construct_yaml_float(self, node): + value = self.construct_scalar(node) + value = value.replace('_', '').lower() + sign = +1 + if value[0] == '-': + sign = -1 + if value[0] in '+-': + value = value[1:] + if value == '.inf': + return sign*self.inf_value + elif value == '.nan': + return self.nan_value + elif ':' in value: + digits = [float(part) for part in value.split(':')] + digits.reverse() + base = 1 + value = 0.0 + for digit in digits: + value += digit*base + base *= 60 + return sign*value + else: + return sign*float(value) + + def construct_yaml_binary(self, node): + try: + value = self.construct_scalar(node).encode('ascii') + except UnicodeEncodeError as exc: + raise ConstructorError(None, None, + "failed to convert base64 data into ascii: %s" % exc, + node.start_mark) + try: + if hasattr(base64, 'decodebytes'): + return base64.decodebytes(value) + else: + return base64.decodestring(value) + except binascii.Error as exc: + raise ConstructorError(None, None, + "failed to decode base64 data: %s" % exc, node.start_mark) + + timestamp_regexp = re.compile( + r'''^(?P<year>[0-9][0-9][0-9][0-9]) + -(?P<month>[0-9][0-9]?) + -(?P<day>[0-9][0-9]?) + (?:(?:[Tt]|[ \t]+) + (?P<hour>[0-9][0-9]?) + :(?P<minute>[0-9][0-9]) + :(?P<second>[0-9][0-9]) + (?:\.(?P<fraction>[0-9]*))? + (?:[ \t]*(?P<tz>Z|(?P<tz_sign>[-+])(?P<tz_hour>[0-9][0-9]?) + (?::(?P<tz_minute>[0-9][0-9]))?))?)?$''', re.X) + + def construct_yaml_timestamp(self, node): + value = self.construct_scalar(node) + match = self.timestamp_regexp.match(node.value) + values = match.groupdict() + year = int(values['year']) + month = int(values['month']) + day = int(values['day']) + if not values['hour']: + return datetime.date(year, month, day) + hour = int(values['hour']) + minute = int(values['minute']) + second = int(values['second']) + fraction = 0 + tzinfo = None + if values['fraction']: + fraction = values['fraction'][:6] + while len(fraction) < 6: + fraction += '0' + fraction = int(fraction) + if values['tz_sign']: + tz_hour = int(values['tz_hour']) + tz_minute = int(values['tz_minute'] or 0) + delta = datetime.timedelta(hours=tz_hour, minutes=tz_minute) + if values['tz_sign'] == '-': + delta = -delta + tzinfo = datetime.timezone(delta) + elif values['tz']: + tzinfo = datetime.timezone.utc + return datetime.datetime(year, month, day, hour, minute, second, fraction, + tzinfo=tzinfo) + + def construct_yaml_omap(self, node): + # Note: we do not check for duplicate keys, because it's too + # CPU-expensive. + omap = [] + yield omap + if not isinstance(node, SequenceNode): + raise ConstructorError("while constructing an ordered map", node.start_mark, + "expected a sequence, but found %s" % node.id, node.start_mark) + for subnode in node.value: + if not isinstance(subnode, MappingNode): + raise ConstructorError("while constructing an ordered map", node.start_mark, + "expected a mapping of length 1, but found %s" % subnode.id, + subnode.start_mark) + if len(subnode.value) != 1: + raise ConstructorError("while constructing an ordered map", node.start_mark, + "expected a single mapping item, but found %d items" % len(subnode.value), + subnode.start_mark) + key_node, value_node = subnode.value[0] + key = self.construct_object(key_node) + value = self.construct_object(value_node) + omap.append((key, value)) + + def construct_yaml_pairs(self, node): + # Note: the same code as `construct_yaml_omap`. + pairs = [] + yield pairs + if not isinstance(node, SequenceNode): + raise ConstructorError("while constructing pairs", node.start_mark, + "expected a sequence, but found %s" % node.id, node.start_mark) + for subnode in node.value: + if not isinstance(subnode, MappingNode): + raise ConstructorError("while constructing pairs", node.start_mark, + "expected a mapping of length 1, but found %s" % subnode.id, + subnode.start_mark) + if len(subnode.value) != 1: + raise ConstructorError("while constructing pairs", node.start_mark, + "expected a single mapping item, but found %d items" % len(subnode.value), + subnode.start_mark) + key_node, value_node = subnode.value[0] + key = self.construct_object(key_node) + value = self.construct_object(value_node) + pairs.append((key, value)) + + def construct_yaml_set(self, node): + data = set() + yield data + value = self.construct_mapping(node) + data.update(value) + + def construct_yaml_str(self, node): + return self.construct_scalar(node) + + def construct_yaml_seq(self, node): + data = [] + yield data + data.extend(self.construct_sequence(node)) + + def construct_yaml_map(self, node): + data = {} + yield data + value = self.construct_mapping(node) + data.update(value) + + def construct_yaml_object(self, node, cls): + data = cls.__new__(cls) + yield data + if hasattr(data, '__setstate__'): + state = self.construct_mapping(node, deep=True) + data.__setstate__(state) + else: + state = self.construct_mapping(node) + data.__dict__.update(state) + + def construct_undefined(self, node): + raise ConstructorError(None, None, + "could not determine a constructor for the tag %r" % node.tag, + node.start_mark) + +SafeConstructor.add_constructor( + 'tag:yaml.org,2002:null', + SafeConstructor.construct_yaml_null) + +SafeConstructor.add_constructor( + 'tag:yaml.org,2002:bool', + SafeConstructor.construct_yaml_bool) + +SafeConstructor.add_constructor( + 'tag:yaml.org,2002:int', + SafeConstructor.construct_yaml_int) + +SafeConstructor.add_constructor( + 'tag:yaml.org,2002:float', + SafeConstructor.construct_yaml_float) + +SafeConstructor.add_constructor( + 'tag:yaml.org,2002:binary', + SafeConstructor.construct_yaml_binary) + +SafeConstructor.add_constructor( + 'tag:yaml.org,2002:timestamp', + SafeConstructor.construct_yaml_timestamp) + +SafeConstructor.add_constructor( + 'tag:yaml.org,2002:omap', + SafeConstructor.construct_yaml_omap) + +SafeConstructor.add_constructor( + 'tag:yaml.org,2002:pairs', + SafeConstructor.construct_yaml_pairs) + +SafeConstructor.add_constructor( + 'tag:yaml.org,2002:set', + SafeConstructor.construct_yaml_set) + +SafeConstructor.add_constructor( + 'tag:yaml.org,2002:str', + SafeConstructor.construct_yaml_str) + +SafeConstructor.add_constructor( + 'tag:yaml.org,2002:seq', + SafeConstructor.construct_yaml_seq) + +SafeConstructor.add_constructor( + 'tag:yaml.org,2002:map', + SafeConstructor.construct_yaml_map) + +SafeConstructor.add_constructor(None, + SafeConstructor.construct_undefined) + +class FullConstructor(SafeConstructor): + # 'extend' is blacklisted because it is used by + # construct_python_object_apply to add `listitems` to a newly generate + # python instance + def get_state_keys_blacklist(self): + return ['^extend$', '^__.*__$'] + + def get_state_keys_blacklist_regexp(self): + if not hasattr(self, 'state_keys_blacklist_regexp'): + self.state_keys_blacklist_regexp = re.compile('(' + '|'.join(self.get_state_keys_blacklist()) + ')') + return self.state_keys_blacklist_regexp + + def construct_python_str(self, node): + return self.construct_scalar(node) + + def construct_python_unicode(self, node): + return self.construct_scalar(node) + + def construct_python_bytes(self, node): + try: + value = self.construct_scalar(node).encode('ascii') + except UnicodeEncodeError as exc: + raise ConstructorError(None, None, + "failed to convert base64 data into ascii: %s" % exc, + node.start_mark) + try: + if hasattr(base64, 'decodebytes'): + return base64.decodebytes(value) + else: + return base64.decodestring(value) + except binascii.Error as exc: + raise ConstructorError(None, None, + "failed to decode base64 data: %s" % exc, node.start_mark) + + def construct_python_long(self, node): + return self.construct_yaml_int(node) + + def construct_python_complex(self, node): + return complex(self.construct_scalar(node)) + + def construct_python_tuple(self, node): + return tuple(self.construct_sequence(node)) + + def find_python_module(self, name, mark, unsafe=False): + if not name: + raise ConstructorError("while constructing a Python module", mark, + "expected non-empty name appended to the tag", mark) + if unsafe: + try: + __import__(name) + except ImportError as exc: + raise ConstructorError("while constructing a Python module", mark, + "cannot find module %r (%s)" % (name, exc), mark) + if name not in sys.modules: + raise ConstructorError("while constructing a Python module", mark, + "module %r is not imported" % name, mark) + return sys.modules[name] + + def find_python_name(self, name, mark, unsafe=False): + if not name: + raise ConstructorError("while constructing a Python object", mark, + "expected non-empty name appended to the tag", mark) + if '.' in name: + module_name, object_name = name.rsplit('.', 1) + else: + module_name = 'builtins' + object_name = name + if unsafe: + try: + __import__(module_name) + except ImportError as exc: + raise ConstructorError("while constructing a Python object", mark, + "cannot find module %r (%s)" % (module_name, exc), mark) + if module_name not in sys.modules: + raise ConstructorError("while constructing a Python object", mark, + "module %r is not imported" % module_name, mark) + module = sys.modules[module_name] + if not hasattr(module, object_name): + raise ConstructorError("while constructing a Python object", mark, + "cannot find %r in the module %r" + % (object_name, module.__name__), mark) + return getattr(module, object_name) + + def construct_python_name(self, suffix, node): + value = self.construct_scalar(node) + if value: + raise ConstructorError("while constructing a Python name", node.start_mark, + "expected the empty value, but found %r" % value, node.start_mark) + return self.find_python_name(suffix, node.start_mark) + + def construct_python_module(self, suffix, node): + value = self.construct_scalar(node) + if value: + raise ConstructorError("while constructing a Python module", node.start_mark, + "expected the empty value, but found %r" % value, node.start_mark) + return self.find_python_module(suffix, node.start_mark) + + def make_python_instance(self, suffix, node, + args=None, kwds=None, newobj=False, unsafe=False): + if not args: + args = [] + if not kwds: + kwds = {} + cls = self.find_python_name(suffix, node.start_mark) + if not (unsafe or isinstance(cls, type)): + raise ConstructorError("while constructing a Python instance", node.start_mark, + "expected a class, but found %r" % type(cls), + node.start_mark) + if newobj and isinstance(cls, type): + return cls.__new__(cls, *args, **kwds) + else: + return cls(*args, **kwds) + + def set_python_instance_state(self, instance, state, unsafe=False): + if hasattr(instance, '__setstate__'): + instance.__setstate__(state) + else: + slotstate = {} + if isinstance(state, tuple) and len(state) == 2: + state, slotstate = state + if hasattr(instance, '__dict__'): + if not unsafe and state: + for key in state.keys(): + self.check_state_key(key) + instance.__dict__.update(state) + elif state: + slotstate.update(state) + for key, value in slotstate.items(): + if not unsafe: + self.check_state_key(key) + setattr(instance, key, value) + + def construct_python_object(self, suffix, node): + # Format: + # !!python/object:module.name { ... state ... } + instance = self.make_python_instance(suffix, node, newobj=True) + yield instance + deep = hasattr(instance, '__setstate__') + state = self.construct_mapping(node, deep=deep) + self.set_python_instance_state(instance, state) + + def construct_python_object_apply(self, suffix, node, newobj=False): + # Format: + # !!python/object/apply # (or !!python/object/new) + # args: [ ... arguments ... ] + # kwds: { ... keywords ... } + # state: ... state ... + # listitems: [ ... listitems ... ] + # dictitems: { ... dictitems ... } + # or short format: + # !!python/object/apply [ ... arguments ... ] + # The difference between !!python/object/apply and !!python/object/new + # is how an object is created, check make_python_instance for details. + if isinstance(node, SequenceNode): + args = self.construct_sequence(node, deep=True) + kwds = {} + state = {} + listitems = [] + dictitems = {} + else: + value = self.construct_mapping(node, deep=True) + args = value.get('args', []) + kwds = value.get('kwds', {}) + state = value.get('state', {}) + listitems = value.get('listitems', []) + dictitems = value.get('dictitems', {}) + instance = self.make_python_instance(suffix, node, args, kwds, newobj) + if state: + self.set_python_instance_state(instance, state) + if listitems: + instance.extend(listitems) + if dictitems: + for key in dictitems: + instance[key] = dictitems[key] + return instance + + def construct_python_object_new(self, suffix, node): + return self.construct_python_object_apply(suffix, node, newobj=True) + +FullConstructor.add_constructor( + 'tag:yaml.org,2002:python/none', + FullConstructor.construct_yaml_null) + +FullConstructor.add_constructor( + 'tag:yaml.org,2002:python/bool', + FullConstructor.construct_yaml_bool) + +FullConstructor.add_constructor( + 'tag:yaml.org,2002:python/str', + FullConstructor.construct_python_str) + +FullConstructor.add_constructor( + 'tag:yaml.org,2002:python/unicode', + FullConstructor.construct_python_unicode) + +FullConstructor.add_constructor( + 'tag:yaml.org,2002:python/bytes', + FullConstructor.construct_python_bytes) + +FullConstructor.add_constructor( + 'tag:yaml.org,2002:python/int', + FullConstructor.construct_yaml_int) + +FullConstructor.add_constructor( + 'tag:yaml.org,2002:python/long', + FullConstructor.construct_python_long) + +FullConstructor.add_constructor( + 'tag:yaml.org,2002:python/float', + FullConstructor.construct_yaml_float) + +FullConstructor.add_constructor( + 'tag:yaml.org,2002:python/complex', + FullConstructor.construct_python_complex) + +FullConstructor.add_constructor( + 'tag:yaml.org,2002:python/list', + FullConstructor.construct_yaml_seq) + +FullConstructor.add_constructor( + 'tag:yaml.org,2002:python/tuple', + FullConstructor.construct_python_tuple) + +FullConstructor.add_constructor( + 'tag:yaml.org,2002:python/dict', + FullConstructor.construct_yaml_map) + +FullConstructor.add_multi_constructor( + 'tag:yaml.org,2002:python/name:', + FullConstructor.construct_python_name) + +class UnsafeConstructor(FullConstructor): + + def find_python_module(self, name, mark): + return super(UnsafeConstructor, self).find_python_module(name, mark, unsafe=True) + + def find_python_name(self, name, mark): + return super(UnsafeConstructor, self).find_python_name(name, mark, unsafe=True) + + def make_python_instance(self, suffix, node, args=None, kwds=None, newobj=False): + return super(UnsafeConstructor, self).make_python_instance( + suffix, node, args, kwds, newobj, unsafe=True) + + def set_python_instance_state(self, instance, state): + return super(UnsafeConstructor, self).set_python_instance_state( + instance, state, unsafe=True) + +UnsafeConstructor.add_multi_constructor( + 'tag:yaml.org,2002:python/module:', + UnsafeConstructor.construct_python_module) + +UnsafeConstructor.add_multi_constructor( + 'tag:yaml.org,2002:python/object:', + UnsafeConstructor.construct_python_object) + +UnsafeConstructor.add_multi_constructor( + 'tag:yaml.org,2002:python/object/new:', + UnsafeConstructor.construct_python_object_new) + +UnsafeConstructor.add_multi_constructor( + 'tag:yaml.org,2002:python/object/apply:', + UnsafeConstructor.construct_python_object_apply) + +# Constructor is same as UnsafeConstructor. Need to leave this in place in case +# people have extended it directly. +class Constructor(UnsafeConstructor): + pass
diff --git a/third_party/pyyaml/cyaml.py b/third_party/pyyaml/cyaml.py new file mode 100644 index 0000000..0c21345 --- /dev/null +++ b/third_party/pyyaml/cyaml.py
@@ -0,0 +1,101 @@ + +__all__ = [ + 'CBaseLoader', 'CSafeLoader', 'CFullLoader', 'CUnsafeLoader', 'CLoader', + 'CBaseDumper', 'CSafeDumper', 'CDumper' +] + +from yaml._yaml import CParser, CEmitter + +from .constructor import * + +from .serializer import * +from .representer import * + +from .resolver import * + +class CBaseLoader(CParser, BaseConstructor, BaseResolver): + + def __init__(self, stream): + CParser.__init__(self, stream) + BaseConstructor.__init__(self) + BaseResolver.__init__(self) + +class CSafeLoader(CParser, SafeConstructor, Resolver): + + def __init__(self, stream): + CParser.__init__(self, stream) + SafeConstructor.__init__(self) + Resolver.__init__(self) + +class CFullLoader(CParser, FullConstructor, Resolver): + + def __init__(self, stream): + CParser.__init__(self, stream) + FullConstructor.__init__(self) + Resolver.__init__(self) + +class CUnsafeLoader(CParser, UnsafeConstructor, Resolver): + + def __init__(self, stream): + CParser.__init__(self, stream) + UnsafeConstructor.__init__(self) + Resolver.__init__(self) + +class CLoader(CParser, Constructor, Resolver): + + def __init__(self, stream): + CParser.__init__(self, stream) + Constructor.__init__(self) + Resolver.__init__(self) + +class CBaseDumper(CEmitter, BaseRepresenter, BaseResolver): + + def __init__(self, stream, + default_style=None, default_flow_style=False, + canonical=None, indent=None, width=None, + allow_unicode=None, line_break=None, + encoding=None, explicit_start=None, explicit_end=None, + version=None, tags=None, sort_keys=True): + CEmitter.__init__(self, stream, canonical=canonical, + indent=indent, width=width, encoding=encoding, + allow_unicode=allow_unicode, line_break=line_break, + explicit_start=explicit_start, explicit_end=explicit_end, + version=version, tags=tags) + Representer.__init__(self, default_style=default_style, + default_flow_style=default_flow_style, sort_keys=sort_keys) + Resolver.__init__(self) + +class CSafeDumper(CEmitter, SafeRepresenter, Resolver): + + def __init__(self, stream, + default_style=None, default_flow_style=False, + canonical=None, indent=None, width=None, + allow_unicode=None, line_break=None, + encoding=None, explicit_start=None, explicit_end=None, + version=None, tags=None, sort_keys=True): + CEmitter.__init__(self, stream, canonical=canonical, + indent=indent, width=width, encoding=encoding, + allow_unicode=allow_unicode, line_break=line_break, + explicit_start=explicit_start, explicit_end=explicit_end, + version=version, tags=tags) + SafeRepresenter.__init__(self, default_style=default_style, + default_flow_style=default_flow_style, sort_keys=sort_keys) + Resolver.__init__(self) + +class CDumper(CEmitter, Serializer, Representer, Resolver): + + def __init__(self, stream, + default_style=None, default_flow_style=False, + canonical=None, indent=None, width=None, + allow_unicode=None, line_break=None, + encoding=None, explicit_start=None, explicit_end=None, + version=None, tags=None, sort_keys=True): + CEmitter.__init__(self, stream, canonical=canonical, + indent=indent, width=width, encoding=encoding, + allow_unicode=allow_unicode, line_break=line_break, + explicit_start=explicit_start, explicit_end=explicit_end, + version=version, tags=tags) + Representer.__init__(self, default_style=default_style, + default_flow_style=default_flow_style, sort_keys=sort_keys) + Resolver.__init__(self) +
diff --git a/third_party/pyyaml/dumper.py b/third_party/pyyaml/dumper.py new file mode 100644 index 0000000..6aadba55 --- /dev/null +++ b/third_party/pyyaml/dumper.py
@@ -0,0 +1,62 @@ + +__all__ = ['BaseDumper', 'SafeDumper', 'Dumper'] + +from .emitter import * +from .serializer import * +from .representer import * +from .resolver import * + +class BaseDumper(Emitter, Serializer, BaseRepresenter, BaseResolver): + + def __init__(self, stream, + default_style=None, default_flow_style=False, + canonical=None, indent=None, width=None, + allow_unicode=None, line_break=None, + encoding=None, explicit_start=None, explicit_end=None, + version=None, tags=None, sort_keys=True): + Emitter.__init__(self, stream, canonical=canonical, + indent=indent, width=width, + allow_unicode=allow_unicode, line_break=line_break) + Serializer.__init__(self, encoding=encoding, + explicit_start=explicit_start, explicit_end=explicit_end, + version=version, tags=tags) + Representer.__init__(self, default_style=default_style, + default_flow_style=default_flow_style, sort_keys=sort_keys) + Resolver.__init__(self) + +class SafeDumper(Emitter, Serializer, SafeRepresenter, Resolver): + + def __init__(self, stream, + default_style=None, default_flow_style=False, + canonical=None, indent=None, width=None, + allow_unicode=None, line_break=None, + encoding=None, explicit_start=None, explicit_end=None, + version=None, tags=None, sort_keys=True): + Emitter.__init__(self, stream, canonical=canonical, + indent=indent, width=width, + allow_unicode=allow_unicode, line_break=line_break) + Serializer.__init__(self, encoding=encoding, + explicit_start=explicit_start, explicit_end=explicit_end, + version=version, tags=tags) + SafeRepresenter.__init__(self, default_style=default_style, + default_flow_style=default_flow_style, sort_keys=sort_keys) + Resolver.__init__(self) + +class Dumper(Emitter, Serializer, Representer, Resolver): + + def __init__(self, stream, + default_style=None, default_flow_style=False, + canonical=None, indent=None, width=None, + allow_unicode=None, line_break=None, + encoding=None, explicit_start=None, explicit_end=None, + version=None, tags=None, sort_keys=True): + Emitter.__init__(self, stream, canonical=canonical, + indent=indent, width=width, + allow_unicode=allow_unicode, line_break=line_break) + Serializer.__init__(self, encoding=encoding, + explicit_start=explicit_start, explicit_end=explicit_end, + version=version, tags=tags) + Representer.__init__(self, default_style=default_style, + default_flow_style=default_flow_style, sort_keys=sort_keys) + Resolver.__init__(self) +
diff --git a/third_party/pyyaml/emitter.py b/third_party/pyyaml/emitter.py new file mode 100644 index 0000000..a664d01 --- /dev/null +++ b/third_party/pyyaml/emitter.py
@@ -0,0 +1,1137 @@ + +# Emitter expects events obeying the following grammar: +# stream ::= STREAM-START document* STREAM-END +# document ::= DOCUMENT-START node DOCUMENT-END +# node ::= SCALAR | sequence | mapping +# sequence ::= SEQUENCE-START node* SEQUENCE-END +# mapping ::= MAPPING-START (node node)* MAPPING-END + +__all__ = ['Emitter', 'EmitterError'] + +from .error import YAMLError +from .events import * + +class EmitterError(YAMLError): + pass + +class ScalarAnalysis: + def __init__(self, scalar, empty, multiline, + allow_flow_plain, allow_block_plain, + allow_single_quoted, allow_double_quoted, + allow_block): + self.scalar = scalar + self.empty = empty + self.multiline = multiline + self.allow_flow_plain = allow_flow_plain + self.allow_block_plain = allow_block_plain + self.allow_single_quoted = allow_single_quoted + self.allow_double_quoted = allow_double_quoted + self.allow_block = allow_block + +class Emitter: + + DEFAULT_TAG_PREFIXES = { + '!' : '!', + 'tag:yaml.org,2002:' : '!!', + } + + def __init__(self, stream, canonical=None, indent=None, width=None, + allow_unicode=None, line_break=None): + + # The stream should have the methods `write` and possibly `flush`. + self.stream = stream + + # Encoding can be overridden by STREAM-START. + self.encoding = None + + # Emitter is a state machine with a stack of states to handle nested + # structures. + self.states = [] + self.state = self.expect_stream_start + + # Current event and the event queue. + self.events = [] + self.event = None + + # The current indentation level and the stack of previous indents. + self.indents = [] + self.indent = None + + # Flow level. + self.flow_level = 0 + + # Contexts. + self.root_context = False + self.sequence_context = False + self.mapping_context = False + self.simple_key_context = False + + # Characteristics of the last emitted character: + # - current position. + # - is it a whitespace? + # - is it an indention character + # (indentation space, '-', '?', or ':')? + self.line = 0 + self.column = 0 + self.whitespace = True + self.indention = True + + # Whether the document requires an explicit document indicator + self.open_ended = False + + # Formatting details. + self.canonical = canonical + self.allow_unicode = allow_unicode + self.best_indent = 2 + if indent and 1 < indent < 10: + self.best_indent = indent + self.best_width = 80 + if width and width > self.best_indent*2: + self.best_width = width + self.best_line_break = '\n' + if line_break in ['\r', '\n', '\r\n']: + self.best_line_break = line_break + + # Tag prefixes. + self.tag_prefixes = None + + # Prepared anchor and tag. + self.prepared_anchor = None + self.prepared_tag = None + + # Scalar analysis and style. + self.analysis = None + self.style = None + + def dispose(self): + # Reset the state attributes (to clear self-references) + self.states = [] + self.state = None + + def emit(self, event): + self.events.append(event) + while not self.need_more_events(): + self.event = self.events.pop(0) + self.state() + self.event = None + + # In some cases, we wait for a few next events before emitting. + + def need_more_events(self): + if not self.events: + return True + event = self.events[0] + if isinstance(event, DocumentStartEvent): + return self.need_events(1) + elif isinstance(event, SequenceStartEvent): + return self.need_events(2) + elif isinstance(event, MappingStartEvent): + return self.need_events(3) + else: + return False + + def need_events(self, count): + level = 0 + for event in self.events[1:]: + if isinstance(event, (DocumentStartEvent, CollectionStartEvent)): + level += 1 + elif isinstance(event, (DocumentEndEvent, CollectionEndEvent)): + level -= 1 + elif isinstance(event, StreamEndEvent): + level = -1 + if level < 0: + return False + return (len(self.events) < count+1) + + def increase_indent(self, flow=False, indentless=False): + self.indents.append(self.indent) + if self.indent is None: + if flow: + self.indent = self.best_indent + else: + self.indent = 0 + elif not indentless: + self.indent += self.best_indent + + # States. + + # Stream handlers. + + def expect_stream_start(self): + if isinstance(self.event, StreamStartEvent): + if self.event.encoding and not hasattr(self.stream, 'encoding'): + self.encoding = self.event.encoding + self.write_stream_start() + self.state = self.expect_first_document_start + else: + raise EmitterError("expected StreamStartEvent, but got %s" + % self.event) + + def expect_nothing(self): + raise EmitterError("expected nothing, but got %s" % self.event) + + # Document handlers. + + def expect_first_document_start(self): + return self.expect_document_start(first=True) + + def expect_document_start(self, first=False): + if isinstance(self.event, DocumentStartEvent): + if (self.event.version or self.event.tags) and self.open_ended: + self.write_indicator('...', True) + self.write_indent() + if self.event.version: + version_text = self.prepare_version(self.event.version) + self.write_version_directive(version_text) + self.tag_prefixes = self.DEFAULT_TAG_PREFIXES.copy() + if self.event.tags: + handles = sorted(self.event.tags.keys()) + for handle in handles: + prefix = self.event.tags[handle] + self.tag_prefixes[prefix] = handle + handle_text = self.prepare_tag_handle(handle) + prefix_text = self.prepare_tag_prefix(prefix) + self.write_tag_directive(handle_text, prefix_text) + implicit = (first and not self.event.explicit and not self.canonical + and not self.event.version and not self.event.tags + and not self.check_empty_document()) + if not implicit: + self.write_indent() + self.write_indicator('---', True) + if self.canonical: + self.write_indent() + self.state = self.expect_document_root + elif isinstance(self.event, StreamEndEvent): + if self.open_ended: + self.write_indicator('...', True) + self.write_indent() + self.write_stream_end() + self.state = self.expect_nothing + else: + raise EmitterError("expected DocumentStartEvent, but got %s" + % self.event) + + def expect_document_end(self): + if isinstance(self.event, DocumentEndEvent): + self.write_indent() + if self.event.explicit: + self.write_indicator('...', True) + self.write_indent() + self.flush_stream() + self.state = self.expect_document_start + else: + raise EmitterError("expected DocumentEndEvent, but got %s" + % self.event) + + def expect_document_root(self): + self.states.append(self.expect_document_end) + self.expect_node(root=True) + + # Node handlers. + + def expect_node(self, root=False, sequence=False, mapping=False, + simple_key=False): + self.root_context = root + self.sequence_context = sequence + self.mapping_context = mapping + self.simple_key_context = simple_key + if isinstance(self.event, AliasEvent): + self.expect_alias() + elif isinstance(self.event, (ScalarEvent, CollectionStartEvent)): + self.process_anchor('&') + self.process_tag() + if isinstance(self.event, ScalarEvent): + self.expect_scalar() + elif isinstance(self.event, SequenceStartEvent): + if self.flow_level or self.canonical or self.event.flow_style \ + or self.check_empty_sequence(): + self.expect_flow_sequence() + else: + self.expect_block_sequence() + elif isinstance(self.event, MappingStartEvent): + if self.flow_level or self.canonical or self.event.flow_style \ + or self.check_empty_mapping(): + self.expect_flow_mapping() + else: + self.expect_block_mapping() + else: + raise EmitterError("expected NodeEvent, but got %s" % self.event) + + def expect_alias(self): + if self.event.anchor is None: + raise EmitterError("anchor is not specified for alias") + self.process_anchor('*') + self.state = self.states.pop() + + def expect_scalar(self): + self.increase_indent(flow=True) + self.process_scalar() + self.indent = self.indents.pop() + self.state = self.states.pop() + + # Flow sequence handlers. + + def expect_flow_sequence(self): + self.write_indicator('[', True, whitespace=True) + self.flow_level += 1 + self.increase_indent(flow=True) + self.state = self.expect_first_flow_sequence_item + + def expect_first_flow_sequence_item(self): + if isinstance(self.event, SequenceEndEvent): + self.indent = self.indents.pop() + self.flow_level -= 1 + self.write_indicator(']', False) + self.state = self.states.pop() + else: + if self.canonical or self.column > self.best_width: + self.write_indent() + self.states.append(self.expect_flow_sequence_item) + self.expect_node(sequence=True) + + def expect_flow_sequence_item(self): + if isinstance(self.event, SequenceEndEvent): + self.indent = self.indents.pop() + self.flow_level -= 1 + if self.canonical: + self.write_indicator(',', False) + self.write_indent() + self.write_indicator(']', False) + self.state = self.states.pop() + else: + self.write_indicator(',', False) + if self.canonical or self.column > self.best_width: + self.write_indent() + self.states.append(self.expect_flow_sequence_item) + self.expect_node(sequence=True) + + # Flow mapping handlers. + + def expect_flow_mapping(self): + self.write_indicator('{', True, whitespace=True) + self.flow_level += 1 + self.increase_indent(flow=True) + self.state = self.expect_first_flow_mapping_key + + def expect_first_flow_mapping_key(self): + if isinstance(self.event, MappingEndEvent): + self.indent = self.indents.pop() + self.flow_level -= 1 + self.write_indicator('}', False) + self.state = self.states.pop() + else: + if self.canonical or self.column > self.best_width: + self.write_indent() + if not self.canonical and self.check_simple_key(): + self.states.append(self.expect_flow_mapping_simple_value) + self.expect_node(mapping=True, simple_key=True) + else: + self.write_indicator('?', True) + self.states.append(self.expect_flow_mapping_value) + self.expect_node(mapping=True) + + def expect_flow_mapping_key(self): + if isinstance(self.event, MappingEndEvent): + self.indent = self.indents.pop() + self.flow_level -= 1 + if self.canonical: + self.write_indicator(',', False) + self.write_indent() + self.write_indicator('}', False) + self.state = self.states.pop() + else: + self.write_indicator(',', False) + if self.canonical or self.column > self.best_width: + self.write_indent() + if not self.canonical and self.check_simple_key(): + self.states.append(self.expect_flow_mapping_simple_value) + self.expect_node(mapping=True, simple_key=True) + else: + self.write_indicator('?', True) + self.states.append(self.expect_flow_mapping_value) + self.expect_node(mapping=True) + + def expect_flow_mapping_simple_value(self): + self.write_indicator(':', False) + self.states.append(self.expect_flow_mapping_key) + self.expect_node(mapping=True) + + def expect_flow_mapping_value(self): + if self.canonical or self.column > self.best_width: + self.write_indent() + self.write_indicator(':', True) + self.states.append(self.expect_flow_mapping_key) + self.expect_node(mapping=True) + + # Block sequence handlers. + + def expect_block_sequence(self): + indentless = (self.mapping_context and not self.indention) + self.increase_indent(flow=False, indentless=indentless) + self.state = self.expect_first_block_sequence_item + + def expect_first_block_sequence_item(self): + return self.expect_block_sequence_item(first=True) + + def expect_block_sequence_item(self, first=False): + if not first and isinstance(self.event, SequenceEndEvent): + self.indent = self.indents.pop() + self.state = self.states.pop() + else: + self.write_indent() + self.write_indicator('-', True, indention=True) + self.states.append(self.expect_block_sequence_item) + self.expect_node(sequence=True) + + # Block mapping handlers. + + def expect_block_mapping(self): + self.increase_indent(flow=False) + self.state = self.expect_first_block_mapping_key + + def expect_first_block_mapping_key(self): + return self.expect_block_mapping_key(first=True) + + def expect_block_mapping_key(self, first=False): + if not first and isinstance(self.event, MappingEndEvent): + self.indent = self.indents.pop() + self.state = self.states.pop() + else: + self.write_indent() + if self.check_simple_key(): + self.states.append(self.expect_block_mapping_simple_value) + self.expect_node(mapping=True, simple_key=True) + else: + self.write_indicator('?', True, indention=True) + self.states.append(self.expect_block_mapping_value) + self.expect_node(mapping=True) + + def expect_block_mapping_simple_value(self): + self.write_indicator(':', False) + self.states.append(self.expect_block_mapping_key) + self.expect_node(mapping=True) + + def expect_block_mapping_value(self): + self.write_indent() + self.write_indicator(':', True, indention=True) + self.states.append(self.expect_block_mapping_key) + self.expect_node(mapping=True) + + # Checkers. + + def check_empty_sequence(self): + return (isinstance(self.event, SequenceStartEvent) and self.events + and isinstance(self.events[0], SequenceEndEvent)) + + def check_empty_mapping(self): + return (isinstance(self.event, MappingStartEvent) and self.events + and isinstance(self.events[0], MappingEndEvent)) + + def check_empty_document(self): + if not isinstance(self.event, DocumentStartEvent) or not self.events: + return False + event = self.events[0] + return (isinstance(event, ScalarEvent) and event.anchor is None + and event.tag is None and event.implicit and event.value == '') + + def check_simple_key(self): + length = 0 + if isinstance(self.event, NodeEvent) and self.event.anchor is not None: + if self.prepared_anchor is None: + self.prepared_anchor = self.prepare_anchor(self.event.anchor) + length += len(self.prepared_anchor) + if isinstance(self.event, (ScalarEvent, CollectionStartEvent)) \ + and self.event.tag is not None: + if self.prepared_tag is None: + self.prepared_tag = self.prepare_tag(self.event.tag) + length += len(self.prepared_tag) + if isinstance(self.event, ScalarEvent): + if self.analysis is None: + self.analysis = self.analyze_scalar(self.event.value) + length += len(self.analysis.scalar) + return (length < 128 and (isinstance(self.event, AliasEvent) + or (isinstance(self.event, ScalarEvent) + and not self.analysis.empty and not self.analysis.multiline) + or self.check_empty_sequence() or self.check_empty_mapping())) + + # Anchor, Tag, and Scalar processors. + + def process_anchor(self, indicator): + if self.event.anchor is None: + self.prepared_anchor = None + return + if self.prepared_anchor is None: + self.prepared_anchor = self.prepare_anchor(self.event.anchor) + if self.prepared_anchor: + self.write_indicator(indicator+self.prepared_anchor, True) + self.prepared_anchor = None + + def process_tag(self): + tag = self.event.tag + if isinstance(self.event, ScalarEvent): + if self.style is None: + self.style = self.choose_scalar_style() + if ((not self.canonical or tag is None) and + ((self.style == '' and self.event.implicit[0]) + or (self.style != '' and self.event.implicit[1]))): + self.prepared_tag = None + return + if self.event.implicit[0] and tag is None: + tag = '!' + self.prepared_tag = None + else: + if (not self.canonical or tag is None) and self.event.implicit: + self.prepared_tag = None + return + if tag is None: + raise EmitterError("tag is not specified") + if self.prepared_tag is None: + self.prepared_tag = self.prepare_tag(tag) + if self.prepared_tag: + self.write_indicator(self.prepared_tag, True) + self.prepared_tag = None + + def choose_scalar_style(self): + if self.analysis is None: + self.analysis = self.analyze_scalar(self.event.value) + if self.event.style == '"' or self.canonical: + return '"' + if not self.event.style and self.event.implicit[0]: + if (not (self.simple_key_context and + (self.analysis.empty or self.analysis.multiline)) + and (self.flow_level and self.analysis.allow_flow_plain + or (not self.flow_level and self.analysis.allow_block_plain))): + return '' + if self.event.style and self.event.style in '|>': + if (not self.flow_level and not self.simple_key_context + and self.analysis.allow_block): + return self.event.style + if not self.event.style or self.event.style == '\'': + if (self.analysis.allow_single_quoted and + not (self.simple_key_context and self.analysis.multiline)): + return '\'' + return '"' + + def process_scalar(self): + if self.analysis is None: + self.analysis = self.analyze_scalar(self.event.value) + if self.style is None: + self.style = self.choose_scalar_style() + split = (not self.simple_key_context) + #if self.analysis.multiline and split \ + # and (not self.style or self.style in '\'\"'): + # self.write_indent() + if self.style == '"': + self.write_double_quoted(self.analysis.scalar, split) + elif self.style == '\'': + self.write_single_quoted(self.analysis.scalar, split) + elif self.style == '>': + self.write_folded(self.analysis.scalar) + elif self.style == '|': + self.write_literal(self.analysis.scalar) + else: + self.write_plain(self.analysis.scalar, split) + self.analysis = None + self.style = None + + # Analyzers. + + def prepare_version(self, version): + major, minor = version + if major != 1: + raise EmitterError("unsupported YAML version: %d.%d" % (major, minor)) + return '%d.%d' % (major, minor) + + def prepare_tag_handle(self, handle): + if not handle: + raise EmitterError("tag handle must not be empty") + if handle[0] != '!' or handle[-1] != '!': + raise EmitterError("tag handle must start and end with '!': %r" % handle) + for ch in handle[1:-1]: + if not ('0' <= ch <= '9' or 'A' <= ch <= 'Z' or 'a' <= ch <= 'z' \ + or ch in '-_'): + raise EmitterError("invalid character %r in the tag handle: %r" + % (ch, handle)) + return handle + + def prepare_tag_prefix(self, prefix): + if not prefix: + raise EmitterError("tag prefix must not be empty") + chunks = [] + start = end = 0 + if prefix[0] == '!': + end = 1 + while end < len(prefix): + ch = prefix[end] + if '0' <= ch <= '9' or 'A' <= ch <= 'Z' or 'a' <= ch <= 'z' \ + or ch in '-;/?!:@&=+$,_.~*\'()[]': + end += 1 + else: + if start < end: + chunks.append(prefix[start:end]) + start = end = end+1 + data = ch.encode('utf-8') + for ch in data: + chunks.append('%%%02X' % ord(ch)) + if start < end: + chunks.append(prefix[start:end]) + return ''.join(chunks) + + def prepare_tag(self, tag): + if not tag: + raise EmitterError("tag must not be empty") + if tag == '!': + return tag + handle = None + suffix = tag + prefixes = sorted(self.tag_prefixes.keys()) + for prefix in prefixes: + if tag.startswith(prefix) \ + and (prefix == '!' or len(prefix) < len(tag)): + handle = self.tag_prefixes[prefix] + suffix = tag[len(prefix):] + chunks = [] + start = end = 0 + while end < len(suffix): + ch = suffix[end] + if '0' <= ch <= '9' or 'A' <= ch <= 'Z' or 'a' <= ch <= 'z' \ + or ch in '-;/?:@&=+$,_.~*\'()[]' \ + or (ch == '!' and handle != '!'): + end += 1 + else: + if start < end: + chunks.append(suffix[start:end]) + start = end = end+1 + data = ch.encode('utf-8') + for ch in data: + chunks.append('%%%02X' % ch) + if start < end: + chunks.append(suffix[start:end]) + suffix_text = ''.join(chunks) + if handle: + return '%s%s' % (handle, suffix_text) + else: + return '!<%s>' % suffix_text + + def prepare_anchor(self, anchor): + if not anchor: + raise EmitterError("anchor must not be empty") + for ch in anchor: + if not ('0' <= ch <= '9' or 'A' <= ch <= 'Z' or 'a' <= ch <= 'z' \ + or ch in '-_'): + raise EmitterError("invalid character %r in the anchor: %r" + % (ch, anchor)) + return anchor + + def analyze_scalar(self, scalar): + + # Empty scalar is a special case. + if not scalar: + return ScalarAnalysis(scalar=scalar, empty=True, multiline=False, + allow_flow_plain=False, allow_block_plain=True, + allow_single_quoted=True, allow_double_quoted=True, + allow_block=False) + + # Indicators and special characters. + block_indicators = False + flow_indicators = False + line_breaks = False + special_characters = False + + # Important whitespace combinations. + leading_space = False + leading_break = False + trailing_space = False + trailing_break = False + break_space = False + space_break = False + + # Check document indicators. + if scalar.startswith('---') or scalar.startswith('...'): + block_indicators = True + flow_indicators = True + + # First character or preceded by a whitespace. + preceded_by_whitespace = True + + # Last character or followed by a whitespace. + followed_by_whitespace = (len(scalar) == 1 or + scalar[1] in '\0 \t\r\n\x85\u2028\u2029') + + # The previous character is a space. + previous_space = False + + # The previous character is a break. + previous_break = False + + index = 0 + while index < len(scalar): + ch = scalar[index] + + # Check for indicators. + if index == 0: + # Leading indicators are special characters. + if ch in '#,[]{}&*!|>\'\"%@`': + flow_indicators = True + block_indicators = True + if ch in '?:': + flow_indicators = True + if followed_by_whitespace: + block_indicators = True + if ch == '-' and followed_by_whitespace: + flow_indicators = True + block_indicators = True + else: + # Some indicators cannot appear within a scalar as well. + if ch in ',?[]{}': + flow_indicators = True + if ch == ':': + flow_indicators = True + if followed_by_whitespace: + block_indicators = True + if ch == '#' and preceded_by_whitespace: + flow_indicators = True + block_indicators = True + + # Check for line breaks, special, and unicode characters. + if ch in '\n\x85\u2028\u2029': + line_breaks = True + if not (ch == '\n' or '\x20' <= ch <= '\x7E'): + if (ch == '\x85' or '\xA0' <= ch <= '\uD7FF' + or '\uE000' <= ch <= '\uFFFD' + or '\U00010000' <= ch < '\U0010ffff') and ch != '\uFEFF': + unicode_characters = True + if not self.allow_unicode: + special_characters = True + else: + special_characters = True + + # Detect important whitespace combinations. + if ch == ' ': + if index == 0: + leading_space = True + if index == len(scalar)-1: + trailing_space = True + if previous_break: + break_space = True + previous_space = True + previous_break = False + elif ch in '\n\x85\u2028\u2029': + if index == 0: + leading_break = True + if index == len(scalar)-1: + trailing_break = True + if previous_space: + space_break = True + previous_space = False + previous_break = True + else: + previous_space = False + previous_break = False + + # Prepare for the next character. + index += 1 + preceded_by_whitespace = (ch in '\0 \t\r\n\x85\u2028\u2029') + followed_by_whitespace = (index+1 >= len(scalar) or + scalar[index+1] in '\0 \t\r\n\x85\u2028\u2029') + + # Let's decide what styles are allowed. + allow_flow_plain = True + allow_block_plain = True + allow_single_quoted = True + allow_double_quoted = True + allow_block = True + + # Leading and trailing whitespaces are bad for plain scalars. + if (leading_space or leading_break + or trailing_space or trailing_break): + allow_flow_plain = allow_block_plain = False + + # We do not permit trailing spaces for block scalars. + if trailing_space: + allow_block = False + + # Spaces at the beginning of a new line are only acceptable for block + # scalars. + if break_space: + allow_flow_plain = allow_block_plain = allow_single_quoted = False + + # Spaces followed by breaks, as well as special character are only + # allowed for double quoted scalars. + if space_break or special_characters: + allow_flow_plain = allow_block_plain = \ + allow_single_quoted = allow_block = False + + # Although the plain scalar writer supports breaks, we never emit + # multiline plain scalars. + if line_breaks: + allow_flow_plain = allow_block_plain = False + + # Flow indicators are forbidden for flow plain scalars. + if flow_indicators: + allow_flow_plain = False + + # Block indicators are forbidden for block plain scalars. + if block_indicators: + allow_block_plain = False + + return ScalarAnalysis(scalar=scalar, + empty=False, multiline=line_breaks, + allow_flow_plain=allow_flow_plain, + allow_block_plain=allow_block_plain, + allow_single_quoted=allow_single_quoted, + allow_double_quoted=allow_double_quoted, + allow_block=allow_block) + + # Writers. + + def flush_stream(self): + if hasattr(self.stream, 'flush'): + self.stream.flush() + + def write_stream_start(self): + # Write BOM if needed. + if self.encoding and self.encoding.startswith('utf-16'): + self.stream.write('\uFEFF'.encode(self.encoding)) + + def write_stream_end(self): + self.flush_stream() + + def write_indicator(self, indicator, need_whitespace, + whitespace=False, indention=False): + if self.whitespace or not need_whitespace: + data = indicator + else: + data = ' '+indicator + self.whitespace = whitespace + self.indention = self.indention and indention + self.column += len(data) + self.open_ended = False + if self.encoding: + data = data.encode(self.encoding) + self.stream.write(data) + + def write_indent(self): + indent = self.indent or 0 + if not self.indention or self.column > indent \ + or (self.column == indent and not self.whitespace): + self.write_line_break() + if self.column < indent: + self.whitespace = True + data = ' '*(indent-self.column) + self.column = indent + if self.encoding: + data = data.encode(self.encoding) + self.stream.write(data) + + def write_line_break(self, data=None): + if data is None: + data = self.best_line_break + self.whitespace = True + self.indention = True + self.line += 1 + self.column = 0 + if self.encoding: + data = data.encode(self.encoding) + self.stream.write(data) + + def write_version_directive(self, version_text): + data = '%%YAML %s' % version_text + if self.encoding: + data = data.encode(self.encoding) + self.stream.write(data) + self.write_line_break() + + def write_tag_directive(self, handle_text, prefix_text): + data = '%%TAG %s %s' % (handle_text, prefix_text) + if self.encoding: + data = data.encode(self.encoding) + self.stream.write(data) + self.write_line_break() + + # Scalar streams. + + def write_single_quoted(self, text, split=True): + self.write_indicator('\'', True) + spaces = False + breaks = False + start = end = 0 + while end <= len(text): + ch = None + if end < len(text): + ch = text[end] + if spaces: + if ch is None or ch != ' ': + if start+1 == end and self.column > self.best_width and split \ + and start != 0 and end != len(text): + self.write_indent() + else: + data = text[start:end] + self.column += len(data) + if self.encoding: + data = data.encode(self.encoding) + self.stream.write(data) + start = end + elif breaks: + if ch is None or ch not in '\n\x85\u2028\u2029': + if text[start] == '\n': + self.write_line_break() + for br in text[start:end]: + if br == '\n': + self.write_line_break() + else: + self.write_line_break(br) + self.write_indent() + start = end + else: + if ch is None or ch in ' \n\x85\u2028\u2029' or ch == '\'': + if start < end: + data = text[start:end] + self.column += len(data) + if self.encoding: + data = data.encode(self.encoding) + self.stream.write(data) + start = end + if ch == '\'': + data = '\'\'' + self.column += 2 + if self.encoding: + data = data.encode(self.encoding) + self.stream.write(data) + start = end + 1 + if ch is not None: + spaces = (ch == ' ') + breaks = (ch in '\n\x85\u2028\u2029') + end += 1 + self.write_indicator('\'', False) + + ESCAPE_REPLACEMENTS = { + '\0': '0', + '\x07': 'a', + '\x08': 'b', + '\x09': 't', + '\x0A': 'n', + '\x0B': 'v', + '\x0C': 'f', + '\x0D': 'r', + '\x1B': 'e', + '\"': '\"', + '\\': '\\', + '\x85': 'N', + '\xA0': '_', + '\u2028': 'L', + '\u2029': 'P', + } + + def write_double_quoted(self, text, split=True): + self.write_indicator('"', True) + start = end = 0 + while end <= len(text): + ch = None + if end < len(text): + ch = text[end] + if ch is None or ch in '"\\\x85\u2028\u2029\uFEFF' \ + or not ('\x20' <= ch <= '\x7E' + or (self.allow_unicode + and ('\xA0' <= ch <= '\uD7FF' + or '\uE000' <= ch <= '\uFFFD'))): + if start < end: + data = text[start:end] + self.column += len(data) + if self.encoding: + data = data.encode(self.encoding) + self.stream.write(data) + start = end + if ch is not None: + if ch in self.ESCAPE_REPLACEMENTS: + data = '\\'+self.ESCAPE_REPLACEMENTS[ch] + elif ch <= '\xFF': + data = '\\x%02X' % ord(ch) + elif ch <= '\uFFFF': + data = '\\u%04X' % ord(ch) + else: + data = '\\U%08X' % ord(ch) + self.column += len(data) + if self.encoding: + data = data.encode(self.encoding) + self.stream.write(data) + start = end+1 + if 0 < end < len(text)-1 and (ch == ' ' or start >= end) \ + and self.column+(end-start) > self.best_width and split: + data = text[start:end]+'\\' + if start < end: + start = end + self.column += len(data) + if self.encoding: + data = data.encode(self.encoding) + self.stream.write(data) + self.write_indent() + self.whitespace = False + self.indention = False + if text[start] == ' ': + data = '\\' + self.column += len(data) + if self.encoding: + data = data.encode(self.encoding) + self.stream.write(data) + end += 1 + self.write_indicator('"', False) + + def determine_block_hints(self, text): + hints = '' + if text: + if text[0] in ' \n\x85\u2028\u2029': + hints += str(self.best_indent) + if text[-1] not in '\n\x85\u2028\u2029': + hints += '-' + elif len(text) == 1 or text[-2] in '\n\x85\u2028\u2029': + hints += '+' + return hints + + def write_folded(self, text): + hints = self.determine_block_hints(text) + self.write_indicator('>'+hints, True) + if hints[-1:] == '+': + self.open_ended = True + self.write_line_break() + leading_space = True + spaces = False + breaks = True + start = end = 0 + while end <= len(text): + ch = None + if end < len(text): + ch = text[end] + if breaks: + if ch is None or ch not in '\n\x85\u2028\u2029': + if not leading_space and ch is not None and ch != ' ' \ + and text[start] == '\n': + self.write_line_break() + leading_space = (ch == ' ') + for br in text[start:end]: + if br == '\n': + self.write_line_break() + else: + self.write_line_break(br) + if ch is not None: + self.write_indent() + start = end + elif spaces: + if ch != ' ': + if start+1 == end and self.column > self.best_width: + self.write_indent() + else: + data = text[start:end] + self.column += len(data) + if self.encoding: + data = data.encode(self.encoding) + self.stream.write(data) + start = end + else: + if ch is None or ch in ' \n\x85\u2028\u2029': + data = text[start:end] + self.column += len(data) + if self.encoding: + data = data.encode(self.encoding) + self.stream.write(data) + if ch is None: + self.write_line_break() + start = end + if ch is not None: + breaks = (ch in '\n\x85\u2028\u2029') + spaces = (ch == ' ') + end += 1 + + def write_literal(self, text): + hints = self.determine_block_hints(text) + self.write_indicator('|'+hints, True) + if hints[-1:] == '+': + self.open_ended = True + self.write_line_break() + breaks = True + start = end = 0 + while end <= len(text): + ch = None + if end < len(text): + ch = text[end] + if breaks: + if ch is None or ch not in '\n\x85\u2028\u2029': + for br in text[start:end]: + if br == '\n': + self.write_line_break() + else: + self.write_line_break(br) + if ch is not None: + self.write_indent() + start = end + else: + if ch is None or ch in '\n\x85\u2028\u2029': + data = text[start:end] + if self.encoding: + data = data.encode(self.encoding) + self.stream.write(data) + if ch is None: + self.write_line_break() + start = end + if ch is not None: + breaks = (ch in '\n\x85\u2028\u2029') + end += 1 + + def write_plain(self, text, split=True): + if self.root_context: + self.open_ended = True + if not text: + return + if not self.whitespace: + data = ' ' + self.column += len(data) + if self.encoding: + data = data.encode(self.encoding) + self.stream.write(data) + self.whitespace = False + self.indention = False + spaces = False + breaks = False + start = end = 0 + while end <= len(text): + ch = None + if end < len(text): + ch = text[end] + if spaces: + if ch != ' ': + if start+1 == end and self.column > self.best_width and split: + self.write_indent() + self.whitespace = False + self.indention = False + else: + data = text[start:end] + self.column += len(data) + if self.encoding: + data = data.encode(self.encoding) + self.stream.write(data) + start = end + elif breaks: + if ch not in '\n\x85\u2028\u2029': + if text[start] == '\n': + self.write_line_break() + for br in text[start:end]: + if br == '\n': + self.write_line_break() + else: + self.write_line_break(br) + self.write_indent() + self.whitespace = False + self.indention = False + start = end + else: + if ch is None or ch in ' \n\x85\u2028\u2029': + data = text[start:end] + self.column += len(data) + if self.encoding: + data = data.encode(self.encoding) + self.stream.write(data) + start = end + if ch is not None: + spaces = (ch == ' ') + breaks = (ch in '\n\x85\u2028\u2029') + end += 1
diff --git a/third_party/pyyaml/error.py b/third_party/pyyaml/error.py new file mode 100644 index 0000000..b796b4dc --- /dev/null +++ b/third_party/pyyaml/error.py
@@ -0,0 +1,75 @@ + +__all__ = ['Mark', 'YAMLError', 'MarkedYAMLError'] + +class Mark: + + def __init__(self, name, index, line, column, buffer, pointer): + self.name = name + self.index = index + self.line = line + self.column = column + self.buffer = buffer + self.pointer = pointer + + def get_snippet(self, indent=4, max_length=75): + if self.buffer is None: + return None + head = '' + start = self.pointer + while start > 0 and self.buffer[start-1] not in '\0\r\n\x85\u2028\u2029': + start -= 1 + if self.pointer-start > max_length/2-1: + head = ' ... ' + start += 5 + break + tail = '' + end = self.pointer + while end < len(self.buffer) and self.buffer[end] not in '\0\r\n\x85\u2028\u2029': + end += 1 + if end-self.pointer > max_length/2-1: + tail = ' ... ' + end -= 5 + break + snippet = self.buffer[start:end] + return ' '*indent + head + snippet + tail + '\n' \ + + ' '*(indent+self.pointer-start+len(head)) + '^' + + def __str__(self): + snippet = self.get_snippet() + where = " in \"%s\", line %d, column %d" \ + % (self.name, self.line+1, self.column+1) + if snippet is not None: + where += ":\n"+snippet + return where + +class YAMLError(Exception): + pass + +class MarkedYAMLError(YAMLError): + + def __init__(self, context=None, context_mark=None, + problem=None, problem_mark=None, note=None): + self.context = context + self.context_mark = context_mark + self.problem = problem + self.problem_mark = problem_mark + self.note = note + + def __str__(self): + lines = [] + if self.context is not None: + lines.append(self.context) + if self.context_mark is not None \ + and (self.problem is None or self.problem_mark is None + or self.context_mark.name != self.problem_mark.name + or self.context_mark.line != self.problem_mark.line + or self.context_mark.column != self.problem_mark.column): + lines.append(str(self.context_mark)) + if self.problem is not None: + lines.append(self.problem) + if self.problem_mark is not None: + lines.append(str(self.problem_mark)) + if self.note is not None: + lines.append(self.note) + return '\n'.join(lines) +
diff --git a/third_party/pyyaml/events.py b/third_party/pyyaml/events.py new file mode 100644 index 0000000..f79ad38 --- /dev/null +++ b/third_party/pyyaml/events.py
@@ -0,0 +1,86 @@ + +# Abstract classes. + +class Event(object): + def __init__(self, start_mark=None, end_mark=None): + self.start_mark = start_mark + self.end_mark = end_mark + def __repr__(self): + attributes = [key for key in ['anchor', 'tag', 'implicit', 'value'] + if hasattr(self, key)] + arguments = ', '.join(['%s=%r' % (key, getattr(self, key)) + for key in attributes]) + return '%s(%s)' % (self.__class__.__name__, arguments) + +class NodeEvent(Event): + def __init__(self, anchor, start_mark=None, end_mark=None): + self.anchor = anchor + self.start_mark = start_mark + self.end_mark = end_mark + +class CollectionStartEvent(NodeEvent): + def __init__(self, anchor, tag, implicit, start_mark=None, end_mark=None, + flow_style=None): + self.anchor = anchor + self.tag = tag + self.implicit = implicit + self.start_mark = start_mark + self.end_mark = end_mark + self.flow_style = flow_style + +class CollectionEndEvent(Event): + pass + +# Implementations. + +class StreamStartEvent(Event): + def __init__(self, start_mark=None, end_mark=None, encoding=None): + self.start_mark = start_mark + self.end_mark = end_mark + self.encoding = encoding + +class StreamEndEvent(Event): + pass + +class DocumentStartEvent(Event): + def __init__(self, start_mark=None, end_mark=None, + explicit=None, version=None, tags=None): + self.start_mark = start_mark + self.end_mark = end_mark + self.explicit = explicit + self.version = version + self.tags = tags + +class DocumentEndEvent(Event): + def __init__(self, start_mark=None, end_mark=None, + explicit=None): + self.start_mark = start_mark + self.end_mark = end_mark + self.explicit = explicit + +class AliasEvent(NodeEvent): + pass + +class ScalarEvent(NodeEvent): + def __init__(self, anchor, tag, implicit, value, + start_mark=None, end_mark=None, style=None): + self.anchor = anchor + self.tag = tag + self.implicit = implicit + self.value = value + self.start_mark = start_mark + self.end_mark = end_mark + self.style = style + +class SequenceStartEvent(CollectionStartEvent): + pass + +class SequenceEndEvent(CollectionEndEvent): + pass + +class MappingStartEvent(CollectionStartEvent): + pass + +class MappingEndEvent(CollectionEndEvent): + pass +
diff --git a/third_party/pyyaml/loader.py b/third_party/pyyaml/loader.py new file mode 100644 index 0000000..e90c1122 --- /dev/null +++ b/third_party/pyyaml/loader.py
@@ -0,0 +1,63 @@ + +__all__ = ['BaseLoader', 'FullLoader', 'SafeLoader', 'Loader', 'UnsafeLoader'] + +from .reader import * +from .scanner import * +from .parser import * +from .composer import * +from .constructor import * +from .resolver import * + +class BaseLoader(Reader, Scanner, Parser, Composer, BaseConstructor, BaseResolver): + + def __init__(self, stream): + Reader.__init__(self, stream) + Scanner.__init__(self) + Parser.__init__(self) + Composer.__init__(self) + BaseConstructor.__init__(self) + BaseResolver.__init__(self) + +class FullLoader(Reader, Scanner, Parser, Composer, FullConstructor, Resolver): + + def __init__(self, stream): + Reader.__init__(self, stream) + Scanner.__init__(self) + Parser.__init__(self) + Composer.__init__(self) + FullConstructor.__init__(self) + Resolver.__init__(self) + +class SafeLoader(Reader, Scanner, Parser, Composer, SafeConstructor, Resolver): + + def __init__(self, stream): + Reader.__init__(self, stream) + Scanner.__init__(self) + Parser.__init__(self) + Composer.__init__(self) + SafeConstructor.__init__(self) + Resolver.__init__(self) + +class Loader(Reader, Scanner, Parser, Composer, Constructor, Resolver): + + def __init__(self, stream): + Reader.__init__(self, stream) + Scanner.__init__(self) + Parser.__init__(self) + Composer.__init__(self) + Constructor.__init__(self) + Resolver.__init__(self) + +# UnsafeLoader is the same as Loader (which is and was always unsafe on +# untrusted input). Use of either Loader or UnsafeLoader should be rare, since +# FullLoad should be able to load almost all YAML safely. Loader is left intact +# to ensure backwards compatibility. +class UnsafeLoader(Reader, Scanner, Parser, Composer, Constructor, Resolver): + + def __init__(self, stream): + Reader.__init__(self, stream) + Scanner.__init__(self) + Parser.__init__(self) + Composer.__init__(self) + Constructor.__init__(self) + Resolver.__init__(self)
diff --git a/third_party/pyyaml/nodes.py b/third_party/pyyaml/nodes.py new file mode 100644 index 0000000..c4f070c4 --- /dev/null +++ b/third_party/pyyaml/nodes.py
@@ -0,0 +1,49 @@ + +class Node(object): + def __init__(self, tag, value, start_mark, end_mark): + self.tag = tag + self.value = value + self.start_mark = start_mark + self.end_mark = end_mark + def __repr__(self): + value = self.value + #if isinstance(value, list): + # if len(value) == 0: + # value = '<empty>' + # elif len(value) == 1: + # value = '<1 item>' + # else: + # value = '<%d items>' % len(value) + #else: + # if len(value) > 75: + # value = repr(value[:70]+u' ... ') + # else: + # value = repr(value) + value = repr(value) + return '%s(tag=%r, value=%s)' % (self.__class__.__name__, self.tag, value) + +class ScalarNode(Node): + id = 'scalar' + def __init__(self, tag, value, + start_mark=None, end_mark=None, style=None): + self.tag = tag + self.value = value + self.start_mark = start_mark + self.end_mark = end_mark + self.style = style + +class CollectionNode(Node): + def __init__(self, tag, value, + start_mark=None, end_mark=None, flow_style=None): + self.tag = tag + self.value = value + self.start_mark = start_mark + self.end_mark = end_mark + self.flow_style = flow_style + +class SequenceNode(CollectionNode): + id = 'sequence' + +class MappingNode(CollectionNode): + id = 'mapping' +
diff --git a/third_party/pyyaml/parser.py b/third_party/pyyaml/parser.py new file mode 100644 index 0000000..13a5995d --- /dev/null +++ b/third_party/pyyaml/parser.py
@@ -0,0 +1,589 @@ + +# The following YAML grammar is LL(1) and is parsed by a recursive descent +# parser. +# +# stream ::= STREAM-START implicit_document? explicit_document* STREAM-END +# implicit_document ::= block_node DOCUMENT-END* +# explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END* +# block_node_or_indentless_sequence ::= +# ALIAS +# | properties (block_content | indentless_block_sequence)? +# | block_content +# | indentless_block_sequence +# block_node ::= ALIAS +# | properties block_content? +# | block_content +# flow_node ::= ALIAS +# | properties flow_content? +# | flow_content +# properties ::= TAG ANCHOR? | ANCHOR TAG? +# block_content ::= block_collection | flow_collection | SCALAR +# flow_content ::= flow_collection | SCALAR +# block_collection ::= block_sequence | block_mapping +# flow_collection ::= flow_sequence | flow_mapping +# block_sequence ::= BLOCK-SEQUENCE-START (BLOCK-ENTRY block_node?)* BLOCK-END +# indentless_sequence ::= (BLOCK-ENTRY block_node?)+ +# block_mapping ::= BLOCK-MAPPING_START +# ((KEY block_node_or_indentless_sequence?)? +# (VALUE block_node_or_indentless_sequence?)?)* +# BLOCK-END +# flow_sequence ::= FLOW-SEQUENCE-START +# (flow_sequence_entry FLOW-ENTRY)* +# flow_sequence_entry? +# FLOW-SEQUENCE-END +# flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? +# flow_mapping ::= FLOW-MAPPING-START +# (flow_mapping_entry FLOW-ENTRY)* +# flow_mapping_entry? +# FLOW-MAPPING-END +# flow_mapping_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? +# +# FIRST sets: +# +# stream: { STREAM-START } +# explicit_document: { DIRECTIVE DOCUMENT-START } +# implicit_document: FIRST(block_node) +# block_node: { ALIAS TAG ANCHOR SCALAR BLOCK-SEQUENCE-START BLOCK-MAPPING-START FLOW-SEQUENCE-START FLOW-MAPPING-START } +# flow_node: { ALIAS ANCHOR TAG SCALAR FLOW-SEQUENCE-START FLOW-MAPPING-START } +# block_content: { BLOCK-SEQUENCE-START BLOCK-MAPPING-START FLOW-SEQUENCE-START FLOW-MAPPING-START SCALAR } +# flow_content: { FLOW-SEQUENCE-START FLOW-MAPPING-START SCALAR } +# block_collection: { BLOCK-SEQUENCE-START BLOCK-MAPPING-START } +# flow_collection: { FLOW-SEQUENCE-START FLOW-MAPPING-START } +# block_sequence: { BLOCK-SEQUENCE-START } +# block_mapping: { BLOCK-MAPPING-START } +# block_node_or_indentless_sequence: { ALIAS ANCHOR TAG SCALAR BLOCK-SEQUENCE-START BLOCK-MAPPING-START FLOW-SEQUENCE-START FLOW-MAPPING-START BLOCK-ENTRY } +# indentless_sequence: { ENTRY } +# flow_collection: { FLOW-SEQUENCE-START FLOW-MAPPING-START } +# flow_sequence: { FLOW-SEQUENCE-START } +# flow_mapping: { FLOW-MAPPING-START } +# flow_sequence_entry: { ALIAS ANCHOR TAG SCALAR FLOW-SEQUENCE-START FLOW-MAPPING-START KEY } +# flow_mapping_entry: { ALIAS ANCHOR TAG SCALAR FLOW-SEQUENCE-START FLOW-MAPPING-START KEY } + +__all__ = ['Parser', 'ParserError'] + +from .error import MarkedYAMLError +from .tokens import * +from .events import * +from .scanner import * + +class ParserError(MarkedYAMLError): + pass + +class Parser: + # Since writing a recursive-descendant parser is a straightforward task, we + # do not give many comments here. + + DEFAULT_TAGS = { + '!': '!', + '!!': 'tag:yaml.org,2002:', + } + + def __init__(self): + self.current_event = None + self.yaml_version = None + self.tag_handles = {} + self.states = [] + self.marks = [] + self.state = self.parse_stream_start + + def dispose(self): + # Reset the state attributes (to clear self-references) + self.states = [] + self.state = None + + def check_event(self, *choices): + # Check the type of the next event. + if self.current_event is None: + if self.state: + self.current_event = self.state() + if self.current_event is not None: + if not choices: + return True + for choice in choices: + if isinstance(self.current_event, choice): + return True + return False + + def peek_event(self): + # Get the next event. + if self.current_event is None: + if self.state: + self.current_event = self.state() + return self.current_event + + def get_event(self): + # Get the next event and proceed further. + if self.current_event is None: + if self.state: + self.current_event = self.state() + value = self.current_event + self.current_event = None + return value + + # stream ::= STREAM-START implicit_document? explicit_document* STREAM-END + # implicit_document ::= block_node DOCUMENT-END* + # explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END* + + def parse_stream_start(self): + + # Parse the stream start. + token = self.get_token() + event = StreamStartEvent(token.start_mark, token.end_mark, + encoding=token.encoding) + + # Prepare the next state. + self.state = self.parse_implicit_document_start + + return event + + def parse_implicit_document_start(self): + + # Parse an implicit document. + if not self.check_token(DirectiveToken, DocumentStartToken, + StreamEndToken): + self.tag_handles = self.DEFAULT_TAGS + token = self.peek_token() + start_mark = end_mark = token.start_mark + event = DocumentStartEvent(start_mark, end_mark, + explicit=False) + + # Prepare the next state. + self.states.append(self.parse_document_end) + self.state = self.parse_block_node + + return event + + else: + return self.parse_document_start() + + def parse_document_start(self): + + # Parse any extra document end indicators. + while self.check_token(DocumentEndToken): + self.get_token() + + # Parse an explicit document. + if not self.check_token(StreamEndToken): + token = self.peek_token() + start_mark = token.start_mark + version, tags = self.process_directives() + if not self.check_token(DocumentStartToken): + raise ParserError(None, None, + "expected '<document start>', but found %r" + % self.peek_token().id, + self.peek_token().start_mark) + token = self.get_token() + end_mark = token.end_mark + event = DocumentStartEvent(start_mark, end_mark, + explicit=True, version=version, tags=tags) + self.states.append(self.parse_document_end) + self.state = self.parse_document_content + else: + # Parse the end of the stream. + token = self.get_token() + event = StreamEndEvent(token.start_mark, token.end_mark) + assert not self.states + assert not self.marks + self.state = None + return event + + def parse_document_end(self): + + # Parse the document end. + token = self.peek_token() + start_mark = end_mark = token.start_mark + explicit = False + if self.check_token(DocumentEndToken): + token = self.get_token() + end_mark = token.end_mark + explicit = True + event = DocumentEndEvent(start_mark, end_mark, + explicit=explicit) + + # Prepare the next state. + self.state = self.parse_document_start + + return event + + def parse_document_content(self): + if self.check_token(DirectiveToken, + DocumentStartToken, DocumentEndToken, StreamEndToken): + event = self.process_empty_scalar(self.peek_token().start_mark) + self.state = self.states.pop() + return event + else: + return self.parse_block_node() + + def process_directives(self): + self.yaml_version = None + self.tag_handles = {} + while self.check_token(DirectiveToken): + token = self.get_token() + if token.name == 'YAML': + if self.yaml_version is not None: + raise ParserError(None, None, + "found duplicate YAML directive", token.start_mark) + major, minor = token.value + if major != 1: + raise ParserError(None, None, + "found incompatible YAML document (version 1.* is required)", + token.start_mark) + self.yaml_version = token.value + elif token.name == 'TAG': + handle, prefix = token.value + if handle in self.tag_handles: + raise ParserError(None, None, + "duplicate tag handle %r" % handle, + token.start_mark) + self.tag_handles[handle] = prefix + if self.tag_handles: + value = self.yaml_version, self.tag_handles.copy() + else: + value = self.yaml_version, None + for key in self.DEFAULT_TAGS: + if key not in self.tag_handles: + self.tag_handles[key] = self.DEFAULT_TAGS[key] + return value + + # block_node_or_indentless_sequence ::= ALIAS + # | properties (block_content | indentless_block_sequence)? + # | block_content + # | indentless_block_sequence + # block_node ::= ALIAS + # | properties block_content? + # | block_content + # flow_node ::= ALIAS + # | properties flow_content? + # | flow_content + # properties ::= TAG ANCHOR? | ANCHOR TAG? + # block_content ::= block_collection | flow_collection | SCALAR + # flow_content ::= flow_collection | SCALAR + # block_collection ::= block_sequence | block_mapping + # flow_collection ::= flow_sequence | flow_mapping + + def parse_block_node(self): + return self.parse_node(block=True) + + def parse_flow_node(self): + return self.parse_node() + + def parse_block_node_or_indentless_sequence(self): + return self.parse_node(block=True, indentless_sequence=True) + + def parse_node(self, block=False, indentless_sequence=False): + if self.check_token(AliasToken): + token = self.get_token() + event = AliasEvent(token.value, token.start_mark, token.end_mark) + self.state = self.states.pop() + else: + anchor = None + tag = None + start_mark = end_mark = tag_mark = None + if self.check_token(AnchorToken): + token = self.get_token() + start_mark = token.start_mark + end_mark = token.end_mark + anchor = token.value + if self.check_token(TagToken): + token = self.get_token() + tag_mark = token.start_mark + end_mark = token.end_mark + tag = token.value + elif self.check_token(TagToken): + token = self.get_token() + start_mark = tag_mark = token.start_mark + end_mark = token.end_mark + tag = token.value + if self.check_token(AnchorToken): + token = self.get_token() + end_mark = token.end_mark + anchor = token.value + if tag is not None: + handle, suffix = tag + if handle is not None: + if handle not in self.tag_handles: + raise ParserError("while parsing a node", start_mark, + "found undefined tag handle %r" % handle, + tag_mark) + tag = self.tag_handles[handle]+suffix + else: + tag = suffix + #if tag == '!': + # raise ParserError("while parsing a node", start_mark, + # "found non-specific tag '!'", tag_mark, + # "Please check 'http://pyyaml.org/wiki/YAMLNonSpecificTag' and share your opinion.") + if start_mark is None: + start_mark = end_mark = self.peek_token().start_mark + event = None + implicit = (tag is None or tag == '!') + if indentless_sequence and self.check_token(BlockEntryToken): + end_mark = self.peek_token().end_mark + event = SequenceStartEvent(anchor, tag, implicit, + start_mark, end_mark) + self.state = self.parse_indentless_sequence_entry + else: + if self.check_token(ScalarToken): + token = self.get_token() + end_mark = token.end_mark + if (token.plain and tag is None) or tag == '!': + implicit = (True, False) + elif tag is None: + implicit = (False, True) + else: + implicit = (False, False) + event = ScalarEvent(anchor, tag, implicit, token.value, + start_mark, end_mark, style=token.style) + self.state = self.states.pop() + elif self.check_token(FlowSequenceStartToken): + end_mark = self.peek_token().end_mark + event = SequenceStartEvent(anchor, tag, implicit, + start_mark, end_mark, flow_style=True) + self.state = self.parse_flow_sequence_first_entry + elif self.check_token(FlowMappingStartToken): + end_mark = self.peek_token().end_mark + event = MappingStartEvent(anchor, tag, implicit, + start_mark, end_mark, flow_style=True) + self.state = self.parse_flow_mapping_first_key + elif block and self.check_token(BlockSequenceStartToken): + end_mark = self.peek_token().start_mark + event = SequenceStartEvent(anchor, tag, implicit, + start_mark, end_mark, flow_style=False) + self.state = self.parse_block_sequence_first_entry + elif block and self.check_token(BlockMappingStartToken): + end_mark = self.peek_token().start_mark + event = MappingStartEvent(anchor, tag, implicit, + start_mark, end_mark, flow_style=False) + self.state = self.parse_block_mapping_first_key + elif anchor is not None or tag is not None: + # Empty scalars are allowed even if a tag or an anchor is + # specified. + event = ScalarEvent(anchor, tag, (implicit, False), '', + start_mark, end_mark) + self.state = self.states.pop() + else: + if block: + node = 'block' + else: + node = 'flow' + token = self.peek_token() + raise ParserError("while parsing a %s node" % node, start_mark, + "expected the node content, but found %r" % token.id, + token.start_mark) + return event + + # block_sequence ::= BLOCK-SEQUENCE-START (BLOCK-ENTRY block_node?)* BLOCK-END + + def parse_block_sequence_first_entry(self): + token = self.get_token() + self.marks.append(token.start_mark) + return self.parse_block_sequence_entry() + + def parse_block_sequence_entry(self): + if self.check_token(BlockEntryToken): + token = self.get_token() + if not self.check_token(BlockEntryToken, BlockEndToken): + self.states.append(self.parse_block_sequence_entry) + return self.parse_block_node() + else: + self.state = self.parse_block_sequence_entry + return self.process_empty_scalar(token.end_mark) + if not self.check_token(BlockEndToken): + token = self.peek_token() + raise ParserError("while parsing a block collection", self.marks[-1], + "expected <block end>, but found %r" % token.id, token.start_mark) + token = self.get_token() + event = SequenceEndEvent(token.start_mark, token.end_mark) + self.state = self.states.pop() + self.marks.pop() + return event + + # indentless_sequence ::= (BLOCK-ENTRY block_node?)+ + + def parse_indentless_sequence_entry(self): + if self.check_token(BlockEntryToken): + token = self.get_token() + if not self.check_token(BlockEntryToken, + KeyToken, ValueToken, BlockEndToken): + self.states.append(self.parse_indentless_sequence_entry) + return self.parse_block_node() + else: + self.state = self.parse_indentless_sequence_entry + return self.process_empty_scalar(token.end_mark) + token = self.peek_token() + event = SequenceEndEvent(token.start_mark, token.start_mark) + self.state = self.states.pop() + return event + + # block_mapping ::= BLOCK-MAPPING_START + # ((KEY block_node_or_indentless_sequence?)? + # (VALUE block_node_or_indentless_sequence?)?)* + # BLOCK-END + + def parse_block_mapping_first_key(self): + token = self.get_token() + self.marks.append(token.start_mark) + return self.parse_block_mapping_key() + + def parse_block_mapping_key(self): + if self.check_token(KeyToken): + token = self.get_token() + if not self.check_token(KeyToken, ValueToken, BlockEndToken): + self.states.append(self.parse_block_mapping_value) + return self.parse_block_node_or_indentless_sequence() + else: + self.state = self.parse_block_mapping_value + return self.process_empty_scalar(token.end_mark) + if not self.check_token(BlockEndToken): + token = self.peek_token() + raise ParserError("while parsing a block mapping", self.marks[-1], + "expected <block end>, but found %r" % token.id, token.start_mark) + token = self.get_token() + event = MappingEndEvent(token.start_mark, token.end_mark) + self.state = self.states.pop() + self.marks.pop() + return event + + def parse_block_mapping_value(self): + if self.check_token(ValueToken): + token = self.get_token() + if not self.check_token(KeyToken, ValueToken, BlockEndToken): + self.states.append(self.parse_block_mapping_key) + return self.parse_block_node_or_indentless_sequence() + else: + self.state = self.parse_block_mapping_key + return self.process_empty_scalar(token.end_mark) + else: + self.state = self.parse_block_mapping_key + token = self.peek_token() + return self.process_empty_scalar(token.start_mark) + + # flow_sequence ::= FLOW-SEQUENCE-START + # (flow_sequence_entry FLOW-ENTRY)* + # flow_sequence_entry? + # FLOW-SEQUENCE-END + # flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? + # + # Note that while production rules for both flow_sequence_entry and + # flow_mapping_entry are equal, their interpretations are different. + # For `flow_sequence_entry`, the part `KEY flow_node? (VALUE flow_node?)?` + # generate an inline mapping (set syntax). + + def parse_flow_sequence_first_entry(self): + token = self.get_token() + self.marks.append(token.start_mark) + return self.parse_flow_sequence_entry(first=True) + + def parse_flow_sequence_entry(self, first=False): + if not self.check_token(FlowSequenceEndToken): + if not first: + if self.check_token(FlowEntryToken): + self.get_token() + else: + token = self.peek_token() + raise ParserError("while parsing a flow sequence", self.marks[-1], + "expected ',' or ']', but got %r" % token.id, token.start_mark) + + if self.check_token(KeyToken): + token = self.peek_token() + event = MappingStartEvent(None, None, True, + token.start_mark, token.end_mark, + flow_style=True) + self.state = self.parse_flow_sequence_entry_mapping_key + return event + elif not self.check_token(FlowSequenceEndToken): + self.states.append(self.parse_flow_sequence_entry) + return self.parse_flow_node() + token = self.get_token() + event = SequenceEndEvent(token.start_mark, token.end_mark) + self.state = self.states.pop() + self.marks.pop() + return event + + def parse_flow_sequence_entry_mapping_key(self): + token = self.get_token() + if not self.check_token(ValueToken, + FlowEntryToken, FlowSequenceEndToken): + self.states.append(self.parse_flow_sequence_entry_mapping_value) + return self.parse_flow_node() + else: + self.state = self.parse_flow_sequence_entry_mapping_value + return self.process_empty_scalar(token.end_mark) + + def parse_flow_sequence_entry_mapping_value(self): + if self.check_token(ValueToken): + token = self.get_token() + if not self.check_token(FlowEntryToken, FlowSequenceEndToken): + self.states.append(self.parse_flow_sequence_entry_mapping_end) + return self.parse_flow_node() + else: + self.state = self.parse_flow_sequence_entry_mapping_end + return self.process_empty_scalar(token.end_mark) + else: + self.state = self.parse_flow_sequence_entry_mapping_end + token = self.peek_token() + return self.process_empty_scalar(token.start_mark) + + def parse_flow_sequence_entry_mapping_end(self): + self.state = self.parse_flow_sequence_entry + token = self.peek_token() + return MappingEndEvent(token.start_mark, token.start_mark) + + # flow_mapping ::= FLOW-MAPPING-START + # (flow_mapping_entry FLOW-ENTRY)* + # flow_mapping_entry? + # FLOW-MAPPING-END + # flow_mapping_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? + + def parse_flow_mapping_first_key(self): + token = self.get_token() + self.marks.append(token.start_mark) + return self.parse_flow_mapping_key(first=True) + + def parse_flow_mapping_key(self, first=False): + if not self.check_token(FlowMappingEndToken): + if not first: + if self.check_token(FlowEntryToken): + self.get_token() + else: + token = self.peek_token() + raise ParserError("while parsing a flow mapping", self.marks[-1], + "expected ',' or '}', but got %r" % token.id, token.start_mark) + if self.check_token(KeyToken): + token = self.get_token() + if not self.check_token(ValueToken, + FlowEntryToken, FlowMappingEndToken): + self.states.append(self.parse_flow_mapping_value) + return self.parse_flow_node() + else: + self.state = self.parse_flow_mapping_value + return self.process_empty_scalar(token.end_mark) + elif not self.check_token(FlowMappingEndToken): + self.states.append(self.parse_flow_mapping_empty_value) + return self.parse_flow_node() + token = self.get_token() + event = MappingEndEvent(token.start_mark, token.end_mark) + self.state = self.states.pop() + self.marks.pop() + return event + + def parse_flow_mapping_value(self): + if self.check_token(ValueToken): + token = self.get_token() + if not self.check_token(FlowEntryToken, FlowMappingEndToken): + self.states.append(self.parse_flow_mapping_key) + return self.parse_flow_node() + else: + self.state = self.parse_flow_mapping_key + return self.process_empty_scalar(token.end_mark) + else: + self.state = self.parse_flow_mapping_key + token = self.peek_token() + return self.process_empty_scalar(token.start_mark) + + def parse_flow_mapping_empty_value(self): + self.state = self.parse_flow_mapping_key + return self.process_empty_scalar(self.peek_token().start_mark) + + def process_empty_scalar(self, mark): + return ScalarEvent(None, None, (True, False), '', mark, mark) +
diff --git a/third_party/pyyaml/reader.py b/third_party/pyyaml/reader.py new file mode 100644 index 0000000..774b021 --- /dev/null +++ b/third_party/pyyaml/reader.py
@@ -0,0 +1,185 @@ +# This module contains abstractions for the input stream. You don't have to +# looks further, there are no pretty code. +# +# We define two classes here. +# +# Mark(source, line, column) +# It's just a record and its only use is producing nice error messages. +# Parser does not use it for any other purposes. +# +# Reader(source, data) +# Reader determines the encoding of `data` and converts it to unicode. +# Reader provides the following methods and attributes: +# reader.peek(length=1) - return the next `length` characters +# reader.forward(length=1) - move the current position to `length` characters. +# reader.index - the number of the current character. +# reader.line, stream.column - the line and the column of the current character. + +__all__ = ['Reader', 'ReaderError'] + +from .error import YAMLError, Mark + +import codecs, re + +class ReaderError(YAMLError): + + def __init__(self, name, position, character, encoding, reason): + self.name = name + self.character = character + self.position = position + self.encoding = encoding + self.reason = reason + + def __str__(self): + if isinstance(self.character, bytes): + return "'%s' codec can't decode byte #x%02x: %s\n" \ + " in \"%s\", position %d" \ + % (self.encoding, ord(self.character), self.reason, + self.name, self.position) + else: + return "unacceptable character #x%04x: %s\n" \ + " in \"%s\", position %d" \ + % (self.character, self.reason, + self.name, self.position) + +class Reader(object): + # Reader: + # - determines the data encoding and converts it to a unicode string, + # - checks if characters are in allowed range, + # - adds '\0' to the end. + + # Reader accepts + # - a `bytes` object, + # - a `str` object, + # - a file-like object with its `read` method returning `str`, + # - a file-like object with its `read` method returning `unicode`. + + # Yeah, it's ugly and slow. + + def __init__(self, stream): + self.name = None + self.stream = None + self.stream_pointer = 0 + self.eof = True + self.buffer = '' + self.pointer = 0 + self.raw_buffer = None + self.raw_decode = None + self.encoding = None + self.index = 0 + self.line = 0 + self.column = 0 + if isinstance(stream, str): + self.name = "<unicode string>" + self.check_printable(stream) + self.buffer = stream+'\0' + elif isinstance(stream, bytes): + self.name = "<byte string>" + self.raw_buffer = stream + self.determine_encoding() + else: + self.stream = stream + self.name = getattr(stream, 'name', "<file>") + self.eof = False + self.raw_buffer = None + self.determine_encoding() + + def peek(self, index=0): + try: + return self.buffer[self.pointer+index] + except IndexError: + self.update(index+1) + return self.buffer[self.pointer+index] + + def prefix(self, length=1): + if self.pointer+length >= len(self.buffer): + self.update(length) + return self.buffer[self.pointer:self.pointer+length] + + def forward(self, length=1): + if self.pointer+length+1 >= len(self.buffer): + self.update(length+1) + while length: + ch = self.buffer[self.pointer] + self.pointer += 1 + self.index += 1 + if ch in '\n\x85\u2028\u2029' \ + or (ch == '\r' and self.buffer[self.pointer] != '\n'): + self.line += 1 + self.column = 0 + elif ch != '\uFEFF': + self.column += 1 + length -= 1 + + def get_mark(self): + if self.stream is None: + return Mark(self.name, self.index, self.line, self.column, + self.buffer, self.pointer) + else: + return Mark(self.name, self.index, self.line, self.column, + None, None) + + def determine_encoding(self): + while not self.eof and (self.raw_buffer is None or len(self.raw_buffer) < 2): + self.update_raw() + if isinstance(self.raw_buffer, bytes): + if self.raw_buffer.startswith(codecs.BOM_UTF16_LE): + self.raw_decode = codecs.utf_16_le_decode + self.encoding = 'utf-16-le' + elif self.raw_buffer.startswith(codecs.BOM_UTF16_BE): + self.raw_decode = codecs.utf_16_be_decode + self.encoding = 'utf-16-be' + else: + self.raw_decode = codecs.utf_8_decode + self.encoding = 'utf-8' + self.update(1) + + NON_PRINTABLE = re.compile('[^\x09\x0A\x0D\x20-\x7E\x85\xA0-\uD7FF\uE000-\uFFFD\U00010000-\U0010ffff]') + def check_printable(self, data): + match = self.NON_PRINTABLE.search(data) + if match: + character = match.group() + position = self.index+(len(self.buffer)-self.pointer)+match.start() + raise ReaderError(self.name, position, ord(character), + 'unicode', "special characters are not allowed") + + def update(self, length): + if self.raw_buffer is None: + return + self.buffer = self.buffer[self.pointer:] + self.pointer = 0 + while len(self.buffer) < length: + if not self.eof: + self.update_raw() + if self.raw_decode is not None: + try: + data, converted = self.raw_decode(self.raw_buffer, + 'strict', self.eof) + except UnicodeDecodeError as exc: + character = self.raw_buffer[exc.start] + if self.stream is not None: + position = self.stream_pointer-len(self.raw_buffer)+exc.start + else: + position = exc.start + raise ReaderError(self.name, position, character, + exc.encoding, exc.reason) + else: + data = self.raw_buffer + converted = len(data) + self.check_printable(data) + self.buffer += data + self.raw_buffer = self.raw_buffer[converted:] + if self.eof: + self.buffer += '\0' + self.raw_buffer = None + break + + def update_raw(self, size=4096): + data = self.stream.read(size) + if self.raw_buffer is None: + self.raw_buffer = data + else: + self.raw_buffer += data + self.stream_pointer += len(data) + if not data: + self.eof = True
diff --git a/third_party/pyyaml/representer.py b/third_party/pyyaml/representer.py new file mode 100644 index 0000000..808ca06d --- /dev/null +++ b/third_party/pyyaml/representer.py
@@ -0,0 +1,389 @@ + +__all__ = ['BaseRepresenter', 'SafeRepresenter', 'Representer', + 'RepresenterError'] + +from .error import * +from .nodes import * + +import datetime, copyreg, types, base64, collections + +class RepresenterError(YAMLError): + pass + +class BaseRepresenter: + + yaml_representers = {} + yaml_multi_representers = {} + + def __init__(self, default_style=None, default_flow_style=False, sort_keys=True): + self.default_style = default_style + self.sort_keys = sort_keys + self.default_flow_style = default_flow_style + self.represented_objects = {} + self.object_keeper = [] + self.alias_key = None + + def represent(self, data): + node = self.represent_data(data) + self.serialize(node) + self.represented_objects = {} + self.object_keeper = [] + self.alias_key = None + + def represent_data(self, data): + if self.ignore_aliases(data): + self.alias_key = None + else: + self.alias_key = id(data) + if self.alias_key is not None: + if self.alias_key in self.represented_objects: + node = self.represented_objects[self.alias_key] + #if node is None: + # raise RepresenterError("recursive objects are not allowed: %r" % data) + return node + #self.represented_objects[alias_key] = None + self.object_keeper.append(data) + data_types = type(data).__mro__ + if data_types[0] in self.yaml_representers: + node = self.yaml_representers[data_types[0]](self, data) + else: + for data_type in data_types: + if data_type in self.yaml_multi_representers: + node = self.yaml_multi_representers[data_type](self, data) + break + else: + if None in self.yaml_multi_representers: + node = self.yaml_multi_representers[None](self, data) + elif None in self.yaml_representers: + node = self.yaml_representers[None](self, data) + else: + node = ScalarNode(None, str(data)) + #if alias_key is not None: + # self.represented_objects[alias_key] = node + return node + + @classmethod + def add_representer(cls, data_type, representer): + if not 'yaml_representers' in cls.__dict__: + cls.yaml_representers = cls.yaml_representers.copy() + cls.yaml_representers[data_type] = representer + + @classmethod + def add_multi_representer(cls, data_type, representer): + if not 'yaml_multi_representers' in cls.__dict__: + cls.yaml_multi_representers = cls.yaml_multi_representers.copy() + cls.yaml_multi_representers[data_type] = representer + + def represent_scalar(self, tag, value, style=None): + if style is None: + style = self.default_style + node = ScalarNode(tag, value, style=style) + if self.alias_key is not None: + self.represented_objects[self.alias_key] = node + return node + + def represent_sequence(self, tag, sequence, flow_style=None): + value = [] + node = SequenceNode(tag, value, flow_style=flow_style) + if self.alias_key is not None: + self.represented_objects[self.alias_key] = node + best_style = True + for item in sequence: + node_item = self.represent_data(item) + if not (isinstance(node_item, ScalarNode) and not node_item.style): + best_style = False + value.append(node_item) + if flow_style is None: + if self.default_flow_style is not None: + node.flow_style = self.default_flow_style + else: + node.flow_style = best_style + return node + + def represent_mapping(self, tag, mapping, flow_style=None): + value = [] + node = MappingNode(tag, value, flow_style=flow_style) + if self.alias_key is not None: + self.represented_objects[self.alias_key] = node + best_style = True + if hasattr(mapping, 'items'): + mapping = list(mapping.items()) + if self.sort_keys: + try: + mapping = sorted(mapping) + except TypeError: + pass + for item_key, item_value in mapping: + node_key = self.represent_data(item_key) + node_value = self.represent_data(item_value) + if not (isinstance(node_key, ScalarNode) and not node_key.style): + best_style = False + if not (isinstance(node_value, ScalarNode) and not node_value.style): + best_style = False + value.append((node_key, node_value)) + if flow_style is None: + if self.default_flow_style is not None: + node.flow_style = self.default_flow_style + else: + node.flow_style = best_style + return node + + def ignore_aliases(self, data): + return False + +class SafeRepresenter(BaseRepresenter): + + def ignore_aliases(self, data): + if data is None: + return True + if isinstance(data, tuple) and data == (): + return True + if isinstance(data, (str, bytes, bool, int, float)): + return True + + def represent_none(self, data): + return self.represent_scalar('tag:yaml.org,2002:null', 'null') + + def represent_str(self, data): + return self.represent_scalar('tag:yaml.org,2002:str', data) + + def represent_binary(self, data): + if hasattr(base64, 'encodebytes'): + data = base64.encodebytes(data).decode('ascii') + else: + data = base64.encodestring(data).decode('ascii') + return self.represent_scalar('tag:yaml.org,2002:binary', data, style='|') + + def represent_bool(self, data): + if data: + value = 'true' + else: + value = 'false' + return self.represent_scalar('tag:yaml.org,2002:bool', value) + + def represent_int(self, data): + return self.represent_scalar('tag:yaml.org,2002:int', str(data)) + + inf_value = 1e300 + while repr(inf_value) != repr(inf_value*inf_value): + inf_value *= inf_value + + def represent_float(self, data): + if data != data or (data == 0.0 and data == 1.0): + value = '.nan' + elif data == self.inf_value: + value = '.inf' + elif data == -self.inf_value: + value = '-.inf' + else: + value = repr(data).lower() + # Note that in some cases `repr(data)` represents a float number + # without the decimal parts. For instance: + # >>> repr(1e17) + # '1e17' + # Unfortunately, this is not a valid float representation according + # to the definition of the `!!float` tag. We fix this by adding + # '.0' before the 'e' symbol. + if '.' not in value and 'e' in value: + value = value.replace('e', '.0e', 1) + return self.represent_scalar('tag:yaml.org,2002:float', value) + + def represent_list(self, data): + #pairs = (len(data) > 0 and isinstance(data, list)) + #if pairs: + # for item in data: + # if not isinstance(item, tuple) or len(item) != 2: + # pairs = False + # break + #if not pairs: + return self.represent_sequence('tag:yaml.org,2002:seq', data) + #value = [] + #for item_key, item_value in data: + # value.append(self.represent_mapping(u'tag:yaml.org,2002:map', + # [(item_key, item_value)])) + #return SequenceNode(u'tag:yaml.org,2002:pairs', value) + + def represent_dict(self, data): + return self.represent_mapping('tag:yaml.org,2002:map', data) + + def represent_set(self, data): + value = {} + for key in data: + value[key] = None + return self.represent_mapping('tag:yaml.org,2002:set', value) + + def represent_date(self, data): + value = data.isoformat() + return self.represent_scalar('tag:yaml.org,2002:timestamp', value) + + def represent_datetime(self, data): + value = data.isoformat(' ') + return self.represent_scalar('tag:yaml.org,2002:timestamp', value) + + def represent_yaml_object(self, tag, data, cls, flow_style=None): + if hasattr(data, '__getstate__'): + state = data.__getstate__() + else: + state = data.__dict__.copy() + return self.represent_mapping(tag, state, flow_style=flow_style) + + def represent_undefined(self, data): + raise RepresenterError("cannot represent an object", data) + +SafeRepresenter.add_representer(type(None), + SafeRepresenter.represent_none) + +SafeRepresenter.add_representer(str, + SafeRepresenter.represent_str) + +SafeRepresenter.add_representer(bytes, + SafeRepresenter.represent_binary) + +SafeRepresenter.add_representer(bool, + SafeRepresenter.represent_bool) + +SafeRepresenter.add_representer(int, + SafeRepresenter.represent_int) + +SafeRepresenter.add_representer(float, + SafeRepresenter.represent_float) + +SafeRepresenter.add_representer(list, + SafeRepresenter.represent_list) + +SafeRepresenter.add_representer(tuple, + SafeRepresenter.represent_list) + +SafeRepresenter.add_representer(dict, + SafeRepresenter.represent_dict) + +SafeRepresenter.add_representer(set, + SafeRepresenter.represent_set) + +SafeRepresenter.add_representer(datetime.date, + SafeRepresenter.represent_date) + +SafeRepresenter.add_representer(datetime.datetime, + SafeRepresenter.represent_datetime) + +SafeRepresenter.add_representer(None, + SafeRepresenter.represent_undefined) + +class Representer(SafeRepresenter): + + def represent_complex(self, data): + if data.imag == 0.0: + data = '%r' % data.real + elif data.real == 0.0: + data = '%rj' % data.imag + elif data.imag > 0: + data = '%r+%rj' % (data.real, data.imag) + else: + data = '%r%rj' % (data.real, data.imag) + return self.represent_scalar('tag:yaml.org,2002:python/complex', data) + + def represent_tuple(self, data): + return self.represent_sequence('tag:yaml.org,2002:python/tuple', data) + + def represent_name(self, data): + name = '%s.%s' % (data.__module__, data.__name__) + return self.represent_scalar('tag:yaml.org,2002:python/name:'+name, '') + + def represent_module(self, data): + return self.represent_scalar( + 'tag:yaml.org,2002:python/module:'+data.__name__, '') + + def represent_object(self, data): + # We use __reduce__ API to save the data. data.__reduce__ returns + # a tuple of length 2-5: + # (function, args, state, listitems, dictitems) + + # For reconstructing, we calls function(*args), then set its state, + # listitems, and dictitems if they are not None. + + # A special case is when function.__name__ == '__newobj__'. In this + # case we create the object with args[0].__new__(*args). + + # Another special case is when __reduce__ returns a string - we don't + # support it. + + # We produce a !!python/object, !!python/object/new or + # !!python/object/apply node. + + cls = type(data) + if cls in copyreg.dispatch_table: + reduce = copyreg.dispatch_table[cls](data) + elif hasattr(data, '__reduce_ex__'): + reduce = data.__reduce_ex__(2) + elif hasattr(data, '__reduce__'): + reduce = data.__reduce__() + else: + raise RepresenterError("cannot represent an object", data) + reduce = (list(reduce)+[None]*5)[:5] + function, args, state, listitems, dictitems = reduce + args = list(args) + if state is None: + state = {} + if listitems is not None: + listitems = list(listitems) + if dictitems is not None: + dictitems = dict(dictitems) + if function.__name__ == '__newobj__': + function = args[0] + args = args[1:] + tag = 'tag:yaml.org,2002:python/object/new:' + newobj = True + else: + tag = 'tag:yaml.org,2002:python/object/apply:' + newobj = False + function_name = '%s.%s' % (function.__module__, function.__name__) + if not args and not listitems and not dictitems \ + and isinstance(state, dict) and newobj: + return self.represent_mapping( + 'tag:yaml.org,2002:python/object:'+function_name, state) + if not listitems and not dictitems \ + and isinstance(state, dict) and not state: + return self.represent_sequence(tag+function_name, args) + value = {} + if args: + value['args'] = args + if state or not isinstance(state, dict): + value['state'] = state + if listitems: + value['listitems'] = listitems + if dictitems: + value['dictitems'] = dictitems + return self.represent_mapping(tag+function_name, value) + + def represent_ordered_dict(self, data): + # Provide uniform representation across different Python versions. + data_type = type(data) + tag = 'tag:yaml.org,2002:python/object/apply:%s.%s' \ + % (data_type.__module__, data_type.__name__) + items = [[key, value] for key, value in data.items()] + return self.represent_sequence(tag, [items]) + +Representer.add_representer(complex, + Representer.represent_complex) + +Representer.add_representer(tuple, + Representer.represent_tuple) + +Representer.add_multi_representer(type, + Representer.represent_name) + +Representer.add_representer(collections.OrderedDict, + Representer.represent_ordered_dict) + +Representer.add_representer(types.FunctionType, + Representer.represent_name) + +Representer.add_representer(types.BuiltinFunctionType, + Representer.represent_name) + +Representer.add_representer(types.ModuleType, + Representer.represent_module) + +Representer.add_multi_representer(object, + Representer.represent_object) +
diff --git a/third_party/pyyaml/resolver.py b/third_party/pyyaml/resolver.py new file mode 100644 index 0000000..3522bdaa --- /dev/null +++ b/third_party/pyyaml/resolver.py
@@ -0,0 +1,227 @@ + +__all__ = ['BaseResolver', 'Resolver'] + +from .error import * +from .nodes import * + +import re + +class ResolverError(YAMLError): + pass + +class BaseResolver: + + DEFAULT_SCALAR_TAG = 'tag:yaml.org,2002:str' + DEFAULT_SEQUENCE_TAG = 'tag:yaml.org,2002:seq' + DEFAULT_MAPPING_TAG = 'tag:yaml.org,2002:map' + + yaml_implicit_resolvers = {} + yaml_path_resolvers = {} + + def __init__(self): + self.resolver_exact_paths = [] + self.resolver_prefix_paths = [] + + @classmethod + def add_implicit_resolver(cls, tag, regexp, first): + if not 'yaml_implicit_resolvers' in cls.__dict__: + implicit_resolvers = {} + for key in cls.yaml_implicit_resolvers: + implicit_resolvers[key] = cls.yaml_implicit_resolvers[key][:] + cls.yaml_implicit_resolvers = implicit_resolvers + if first is None: + first = [None] + for ch in first: + cls.yaml_implicit_resolvers.setdefault(ch, []).append((tag, regexp)) + + @classmethod + def add_path_resolver(cls, tag, path, kind=None): + # Note: `add_path_resolver` is experimental. The API could be changed. + # `new_path` is a pattern that is matched against the path from the + # root to the node that is being considered. `node_path` elements are + # tuples `(node_check, index_check)`. `node_check` is a node class: + # `ScalarNode`, `SequenceNode`, `MappingNode` or `None`. `None` + # matches any kind of a node. `index_check` could be `None`, a boolean + # value, a string value, or a number. `None` and `False` match against + # any _value_ of sequence and mapping nodes. `True` matches against + # any _key_ of a mapping node. A string `index_check` matches against + # a mapping value that corresponds to a scalar key which content is + # equal to the `index_check` value. An integer `index_check` matches + # against a sequence value with the index equal to `index_check`. + if not 'yaml_path_resolvers' in cls.__dict__: + cls.yaml_path_resolvers = cls.yaml_path_resolvers.copy() + new_path = [] + for element in path: + if isinstance(element, (list, tuple)): + if len(element) == 2: + node_check, index_check = element + elif len(element) == 1: + node_check = element[0] + index_check = True + else: + raise ResolverError("Invalid path element: %s" % element) + else: + node_check = None + index_check = element + if node_check is str: + node_check = ScalarNode + elif node_check is list: + node_check = SequenceNode + elif node_check is dict: + node_check = MappingNode + elif node_check not in [ScalarNode, SequenceNode, MappingNode] \ + and not isinstance(node_check, str) \ + and node_check is not None: + raise ResolverError("Invalid node checker: %s" % node_check) + if not isinstance(index_check, (str, int)) \ + and index_check is not None: + raise ResolverError("Invalid index checker: %s" % index_check) + new_path.append((node_check, index_check)) + if kind is str: + kind = ScalarNode + elif kind is list: + kind = SequenceNode + elif kind is dict: + kind = MappingNode + elif kind not in [ScalarNode, SequenceNode, MappingNode] \ + and kind is not None: + raise ResolverError("Invalid node kind: %s" % kind) + cls.yaml_path_resolvers[tuple(new_path), kind] = tag + + def descend_resolver(self, current_node, current_index): + if not self.yaml_path_resolvers: + return + exact_paths = {} + prefix_paths = [] + if current_node: + depth = len(self.resolver_prefix_paths) + for path, kind in self.resolver_prefix_paths[-1]: + if self.check_resolver_prefix(depth, path, kind, + current_node, current_index): + if len(path) > depth: + prefix_paths.append((path, kind)) + else: + exact_paths[kind] = self.yaml_path_resolvers[path, kind] + else: + for path, kind in self.yaml_path_resolvers: + if not path: + exact_paths[kind] = self.yaml_path_resolvers[path, kind] + else: + prefix_paths.append((path, kind)) + self.resolver_exact_paths.append(exact_paths) + self.resolver_prefix_paths.append(prefix_paths) + + def ascend_resolver(self): + if not self.yaml_path_resolvers: + return + self.resolver_exact_paths.pop() + self.resolver_prefix_paths.pop() + + def check_resolver_prefix(self, depth, path, kind, + current_node, current_index): + node_check, index_check = path[depth-1] + if isinstance(node_check, str): + if current_node.tag != node_check: + return + elif node_check is not None: + if not isinstance(current_node, node_check): + return + if index_check is True and current_index is not None: + return + if (index_check is False or index_check is None) \ + and current_index is None: + return + if isinstance(index_check, str): + if not (isinstance(current_index, ScalarNode) + and index_check == current_index.value): + return + elif isinstance(index_check, int) and not isinstance(index_check, bool): + if index_check != current_index: + return + return True + + def resolve(self, kind, value, implicit): + if kind is ScalarNode and implicit[0]: + if value == '': + resolvers = self.yaml_implicit_resolvers.get('', []) + else: + resolvers = self.yaml_implicit_resolvers.get(value[0], []) + wildcard_resolvers = self.yaml_implicit_resolvers.get(None, []) + for tag, regexp in resolvers + wildcard_resolvers: + if regexp.match(value): + return tag + implicit = implicit[1] + if self.yaml_path_resolvers: + exact_paths = self.resolver_exact_paths[-1] + if kind in exact_paths: + return exact_paths[kind] + if None in exact_paths: + return exact_paths[None] + if kind is ScalarNode: + return self.DEFAULT_SCALAR_TAG + elif kind is SequenceNode: + return self.DEFAULT_SEQUENCE_TAG + elif kind is MappingNode: + return self.DEFAULT_MAPPING_TAG + +class Resolver(BaseResolver): + pass + +Resolver.add_implicit_resolver( + 'tag:yaml.org,2002:bool', + re.compile(r'''^(?:yes|Yes|YES|no|No|NO + |true|True|TRUE|false|False|FALSE + |on|On|ON|off|Off|OFF)$''', re.X), + list('yYnNtTfFoO')) + +Resolver.add_implicit_resolver( + 'tag:yaml.org,2002:float', + re.compile(r'''^(?:[-+]?(?:[0-9][0-9_]*)\.[0-9_]*(?:[eE][-+][0-9]+)? + |\.[0-9][0-9_]*(?:[eE][-+][0-9]+)? + |[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+\.[0-9_]* + |[-+]?\.(?:inf|Inf|INF) + |\.(?:nan|NaN|NAN))$''', re.X), + list('-+0123456789.')) + +Resolver.add_implicit_resolver( + 'tag:yaml.org,2002:int', + re.compile(r'''^(?:[-+]?0b[0-1_]+ + |[-+]?0[0-7_]+ + |[-+]?(?:0|[1-9][0-9_]*) + |[-+]?0x[0-9a-fA-F_]+ + |[-+]?[1-9][0-9_]*(?::[0-5]?[0-9])+)$''', re.X), + list('-+0123456789')) + +Resolver.add_implicit_resolver( + 'tag:yaml.org,2002:merge', + re.compile(r'^(?:<<)$'), + ['<']) + +Resolver.add_implicit_resolver( + 'tag:yaml.org,2002:null', + re.compile(r'''^(?: ~ + |null|Null|NULL + | )$''', re.X), + ['~', 'n', 'N', '']) + +Resolver.add_implicit_resolver( + 'tag:yaml.org,2002:timestamp', + re.compile(r'''^(?:[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9] + |[0-9][0-9][0-9][0-9] -[0-9][0-9]? -[0-9][0-9]? + (?:[Tt]|[ \t]+)[0-9][0-9]? + :[0-9][0-9] :[0-9][0-9] (?:\.[0-9]*)? + (?:[ \t]*(?:Z|[-+][0-9][0-9]?(?::[0-9][0-9])?))?)$''', re.X), + list('0123456789')) + +Resolver.add_implicit_resolver( + 'tag:yaml.org,2002:value', + re.compile(r'^(?:=)$'), + ['=']) + +# The following resolver is only for documentation purposes. It cannot work +# because plain scalars cannot start with '!', '&', or '*'. +Resolver.add_implicit_resolver( + 'tag:yaml.org,2002:yaml', + re.compile(r'^(?:!|&|\*)$'), + list('!&*')) +
diff --git a/third_party/pyyaml/scanner.py b/third_party/pyyaml/scanner.py new file mode 100644 index 0000000..de925b0 --- /dev/null +++ b/third_party/pyyaml/scanner.py
@@ -0,0 +1,1435 @@ + +# Scanner produces tokens of the following types: +# STREAM-START +# STREAM-END +# DIRECTIVE(name, value) +# DOCUMENT-START +# DOCUMENT-END +# BLOCK-SEQUENCE-START +# BLOCK-MAPPING-START +# BLOCK-END +# FLOW-SEQUENCE-START +# FLOW-MAPPING-START +# FLOW-SEQUENCE-END +# FLOW-MAPPING-END +# BLOCK-ENTRY +# FLOW-ENTRY +# KEY +# VALUE +# ALIAS(value) +# ANCHOR(value) +# TAG(value) +# SCALAR(value, plain, style) +# +# Read comments in the Scanner code for more details. +# + +__all__ = ['Scanner', 'ScannerError'] + +from .error import MarkedYAMLError +from .tokens import * + +class ScannerError(MarkedYAMLError): + pass + +class SimpleKey: + # See below simple keys treatment. + + def __init__(self, token_number, required, index, line, column, mark): + self.token_number = token_number + self.required = required + self.index = index + self.line = line + self.column = column + self.mark = mark + +class Scanner: + + def __init__(self): + """Initialize the scanner.""" + # It is assumed that Scanner and Reader will have a common descendant. + # Reader do the dirty work of checking for BOM and converting the + # input data to Unicode. It also adds NUL to the end. + # + # Reader supports the following methods + # self.peek(i=0) # peek the next i-th character + # self.prefix(l=1) # peek the next l characters + # self.forward(l=1) # read the next l characters and move the pointer. + + # Had we reached the end of the stream? + self.done = False + + # The number of unclosed '{' and '['. `flow_level == 0` means block + # context. + self.flow_level = 0 + + # List of processed tokens that are not yet emitted. + self.tokens = [] + + # Add the STREAM-START token. + self.fetch_stream_start() + + # Number of tokens that were emitted through the `get_token` method. + self.tokens_taken = 0 + + # The current indentation level. + self.indent = -1 + + # Past indentation levels. + self.indents = [] + + # Variables related to simple keys treatment. + + # A simple key is a key that is not denoted by the '?' indicator. + # Example of simple keys: + # --- + # block simple key: value + # ? not a simple key: + # : { flow simple key: value } + # We emit the KEY token before all keys, so when we find a potential + # simple key, we try to locate the corresponding ':' indicator. + # Simple keys should be limited to a single line and 1024 characters. + + # Can a simple key start at the current position? A simple key may + # start: + # - at the beginning of the line, not counting indentation spaces + # (in block context), + # - after '{', '[', ',' (in the flow context), + # - after '?', ':', '-' (in the block context). + # In the block context, this flag also signifies if a block collection + # may start at the current position. + self.allow_simple_key = True + + # Keep track of possible simple keys. This is a dictionary. The key + # is `flow_level`; there can be no more that one possible simple key + # for each level. The value is a SimpleKey record: + # (token_number, required, index, line, column, mark) + # A simple key may start with ALIAS, ANCHOR, TAG, SCALAR(flow), + # '[', or '{' tokens. + self.possible_simple_keys = {} + + # Public methods. + + def check_token(self, *choices): + # Check if the next token is one of the given types. + while self.need_more_tokens(): + self.fetch_more_tokens() + if self.tokens: + if not choices: + return True + for choice in choices: + if isinstance(self.tokens[0], choice): + return True + return False + + def peek_token(self): + # Return the next token, but do not delete if from the queue. + # Return None if no more tokens. + while self.need_more_tokens(): + self.fetch_more_tokens() + if self.tokens: + return self.tokens[0] + else: + return None + + def get_token(self): + # Return the next token. + while self.need_more_tokens(): + self.fetch_more_tokens() + if self.tokens: + self.tokens_taken += 1 + return self.tokens.pop(0) + + # Private methods. + + def need_more_tokens(self): + if self.done: + return False + if not self.tokens: + return True + # The current token may be a potential simple key, so we + # need to look further. + self.stale_possible_simple_keys() + if self.next_possible_simple_key() == self.tokens_taken: + return True + + def fetch_more_tokens(self): + + # Eat whitespaces and comments until we reach the next token. + self.scan_to_next_token() + + # Remove obsolete possible simple keys. + self.stale_possible_simple_keys() + + # Compare the current indentation and column. It may add some tokens + # and decrease the current indentation level. + self.unwind_indent(self.column) + + # Peek the next character. + ch = self.peek() + + # Is it the end of stream? + if ch == '\0': + return self.fetch_stream_end() + + # Is it a directive? + if ch == '%' and self.check_directive(): + return self.fetch_directive() + + # Is it the document start? + if ch == '-' and self.check_document_start(): + return self.fetch_document_start() + + # Is it the document end? + if ch == '.' and self.check_document_end(): + return self.fetch_document_end() + + # TODO: support for BOM within a stream. + #if ch == '\uFEFF': + # return self.fetch_bom() <-- issue BOMToken + + # Note: the order of the following checks is NOT significant. + + # Is it the flow sequence start indicator? + if ch == '[': + return self.fetch_flow_sequence_start() + + # Is it the flow mapping start indicator? + if ch == '{': + return self.fetch_flow_mapping_start() + + # Is it the flow sequence end indicator? + if ch == ']': + return self.fetch_flow_sequence_end() + + # Is it the flow mapping end indicator? + if ch == '}': + return self.fetch_flow_mapping_end() + + # Is it the flow entry indicator? + if ch == ',': + return self.fetch_flow_entry() + + # Is it the block entry indicator? + if ch == '-' and self.check_block_entry(): + return self.fetch_block_entry() + + # Is it the key indicator? + if ch == '?' and self.check_key(): + return self.fetch_key() + + # Is it the value indicator? + if ch == ':' and self.check_value(): + return self.fetch_value() + + # Is it an alias? + if ch == '*': + return self.fetch_alias() + + # Is it an anchor? + if ch == '&': + return self.fetch_anchor() + + # Is it a tag? + if ch == '!': + return self.fetch_tag() + + # Is it a literal scalar? + if ch == '|' and not self.flow_level: + return self.fetch_literal() + + # Is it a folded scalar? + if ch == '>' and not self.flow_level: + return self.fetch_folded() + + # Is it a single quoted scalar? + if ch == '\'': + return self.fetch_single() + + # Is it a double quoted scalar? + if ch == '\"': + return self.fetch_double() + + # It must be a plain scalar then. + if self.check_plain(): + return self.fetch_plain() + + # No? It's an error. Let's produce a nice error message. + raise ScannerError("while scanning for the next token", None, + "found character %r that cannot start any token" % ch, + self.get_mark()) + + # Simple keys treatment. + + def next_possible_simple_key(self): + # Return the number of the nearest possible simple key. Actually we + # don't need to loop through the whole dictionary. We may replace it + # with the following code: + # if not self.possible_simple_keys: + # return None + # return self.possible_simple_keys[ + # min(self.possible_simple_keys.keys())].token_number + min_token_number = None + for level in self.possible_simple_keys: + key = self.possible_simple_keys[level] + if min_token_number is None or key.token_number < min_token_number: + min_token_number = key.token_number + return min_token_number + + def stale_possible_simple_keys(self): + # Remove entries that are no longer possible simple keys. According to + # the YAML specification, simple keys + # - should be limited to a single line, + # - should be no longer than 1024 characters. + # Disabling this procedure will allow simple keys of any length and + # height (may cause problems if indentation is broken though). + for level in list(self.possible_simple_keys): + key = self.possible_simple_keys[level] + if key.line != self.line \ + or self.index-key.index > 1024: + if key.required: + raise ScannerError("while scanning a simple key", key.mark, + "could not find expected ':'", self.get_mark()) + del self.possible_simple_keys[level] + + def save_possible_simple_key(self): + # The next token may start a simple key. We check if it's possible + # and save its position. This function is called for + # ALIAS, ANCHOR, TAG, SCALAR(flow), '[', and '{'. + + # Check if a simple key is required at the current position. + required = not self.flow_level and self.indent == self.column + + # The next token might be a simple key. Let's save it's number and + # position. + if self.allow_simple_key: + self.remove_possible_simple_key() + token_number = self.tokens_taken+len(self.tokens) + key = SimpleKey(token_number, required, + self.index, self.line, self.column, self.get_mark()) + self.possible_simple_keys[self.flow_level] = key + + def remove_possible_simple_key(self): + # Remove the saved possible key position at the current flow level. + if self.flow_level in self.possible_simple_keys: + key = self.possible_simple_keys[self.flow_level] + + if key.required: + raise ScannerError("while scanning a simple key", key.mark, + "could not find expected ':'", self.get_mark()) + + del self.possible_simple_keys[self.flow_level] + + # Indentation functions. + + def unwind_indent(self, column): + + ## In flow context, tokens should respect indentation. + ## Actually the condition should be `self.indent >= column` according to + ## the spec. But this condition will prohibit intuitively correct + ## constructions such as + ## key : { + ## } + #if self.flow_level and self.indent > column: + # raise ScannerError(None, None, + # "invalid indentation or unclosed '[' or '{'", + # self.get_mark()) + + # In the flow context, indentation is ignored. We make the scanner less + # restrictive then specification requires. + if self.flow_level: + return + + # In block context, we may need to issue the BLOCK-END tokens. + while self.indent > column: + mark = self.get_mark() + self.indent = self.indents.pop() + self.tokens.append(BlockEndToken(mark, mark)) + + def add_indent(self, column): + # Check if we need to increase indentation. + if self.indent < column: + self.indents.append(self.indent) + self.indent = column + return True + return False + + # Fetchers. + + def fetch_stream_start(self): + # We always add STREAM-START as the first token and STREAM-END as the + # last token. + + # Read the token. + mark = self.get_mark() + + # Add STREAM-START. + self.tokens.append(StreamStartToken(mark, mark, + encoding=self.encoding)) + + + def fetch_stream_end(self): + + # Set the current indentation to -1. + self.unwind_indent(-1) + + # Reset simple keys. + self.remove_possible_simple_key() + self.allow_simple_key = False + self.possible_simple_keys = {} + + # Read the token. + mark = self.get_mark() + + # Add STREAM-END. + self.tokens.append(StreamEndToken(mark, mark)) + + # The steam is finished. + self.done = True + + def fetch_directive(self): + + # Set the current indentation to -1. + self.unwind_indent(-1) + + # Reset simple keys. + self.remove_possible_simple_key() + self.allow_simple_key = False + + # Scan and add DIRECTIVE. + self.tokens.append(self.scan_directive()) + + def fetch_document_start(self): + self.fetch_document_indicator(DocumentStartToken) + + def fetch_document_end(self): + self.fetch_document_indicator(DocumentEndToken) + + def fetch_document_indicator(self, TokenClass): + + # Set the current indentation to -1. + self.unwind_indent(-1) + + # Reset simple keys. Note that there could not be a block collection + # after '---'. + self.remove_possible_simple_key() + self.allow_simple_key = False + + # Add DOCUMENT-START or DOCUMENT-END. + start_mark = self.get_mark() + self.forward(3) + end_mark = self.get_mark() + self.tokens.append(TokenClass(start_mark, end_mark)) + + def fetch_flow_sequence_start(self): + self.fetch_flow_collection_start(FlowSequenceStartToken) + + def fetch_flow_mapping_start(self): + self.fetch_flow_collection_start(FlowMappingStartToken) + + def fetch_flow_collection_start(self, TokenClass): + + # '[' and '{' may start a simple key. + self.save_possible_simple_key() + + # Increase the flow level. + self.flow_level += 1 + + # Simple keys are allowed after '[' and '{'. + self.allow_simple_key = True + + # Add FLOW-SEQUENCE-START or FLOW-MAPPING-START. + start_mark = self.get_mark() + self.forward() + end_mark = self.get_mark() + self.tokens.append(TokenClass(start_mark, end_mark)) + + def fetch_flow_sequence_end(self): + self.fetch_flow_collection_end(FlowSequenceEndToken) + + def fetch_flow_mapping_end(self): + self.fetch_flow_collection_end(FlowMappingEndToken) + + def fetch_flow_collection_end(self, TokenClass): + + # Reset possible simple key on the current level. + self.remove_possible_simple_key() + + # Decrease the flow level. + self.flow_level -= 1 + + # No simple keys after ']' or '}'. + self.allow_simple_key = False + + # Add FLOW-SEQUENCE-END or FLOW-MAPPING-END. + start_mark = self.get_mark() + self.forward() + end_mark = self.get_mark() + self.tokens.append(TokenClass(start_mark, end_mark)) + + def fetch_flow_entry(self): + + # Simple keys are allowed after ','. + self.allow_simple_key = True + + # Reset possible simple key on the current level. + self.remove_possible_simple_key() + + # Add FLOW-ENTRY. + start_mark = self.get_mark() + self.forward() + end_mark = self.get_mark() + self.tokens.append(FlowEntryToken(start_mark, end_mark)) + + def fetch_block_entry(self): + + # Block context needs additional checks. + if not self.flow_level: + + # Are we allowed to start a new entry? + if not self.allow_simple_key: + raise ScannerError(None, None, + "sequence entries are not allowed here", + self.get_mark()) + + # We may need to add BLOCK-SEQUENCE-START. + if self.add_indent(self.column): + mark = self.get_mark() + self.tokens.append(BlockSequenceStartToken(mark, mark)) + + # It's an error for the block entry to occur in the flow context, + # but we let the parser detect this. + else: + pass + + # Simple keys are allowed after '-'. + self.allow_simple_key = True + + # Reset possible simple key on the current level. + self.remove_possible_simple_key() + + # Add BLOCK-ENTRY. + start_mark = self.get_mark() + self.forward() + end_mark = self.get_mark() + self.tokens.append(BlockEntryToken(start_mark, end_mark)) + + def fetch_key(self): + + # Block context needs additional checks. + if not self.flow_level: + + # Are we allowed to start a key (not necessary a simple)? + if not self.allow_simple_key: + raise ScannerError(None, None, + "mapping keys are not allowed here", + self.get_mark()) + + # We may need to add BLOCK-MAPPING-START. + if self.add_indent(self.column): + mark = self.get_mark() + self.tokens.append(BlockMappingStartToken(mark, mark)) + + # Simple keys are allowed after '?' in the block context. + self.allow_simple_key = not self.flow_level + + # Reset possible simple key on the current level. + self.remove_possible_simple_key() + + # Add KEY. + start_mark = self.get_mark() + self.forward() + end_mark = self.get_mark() + self.tokens.append(KeyToken(start_mark, end_mark)) + + def fetch_value(self): + + # Do we determine a simple key? + if self.flow_level in self.possible_simple_keys: + + # Add KEY. + key = self.possible_simple_keys[self.flow_level] + del self.possible_simple_keys[self.flow_level] + self.tokens.insert(key.token_number-self.tokens_taken, + KeyToken(key.mark, key.mark)) + + # If this key starts a new block mapping, we need to add + # BLOCK-MAPPING-START. + if not self.flow_level: + if self.add_indent(key.column): + self.tokens.insert(key.token_number-self.tokens_taken, + BlockMappingStartToken(key.mark, key.mark)) + + # There cannot be two simple keys one after another. + self.allow_simple_key = False + + # It must be a part of a complex key. + else: + + # Block context needs additional checks. + # (Do we really need them? They will be caught by the parser + # anyway.) + if not self.flow_level: + + # We are allowed to start a complex value if and only if + # we can start a simple key. + if not self.allow_simple_key: + raise ScannerError(None, None, + "mapping values are not allowed here", + self.get_mark()) + + # If this value starts a new block mapping, we need to add + # BLOCK-MAPPING-START. It will be detected as an error later by + # the parser. + if not self.flow_level: + if self.add_indent(self.column): + mark = self.get_mark() + self.tokens.append(BlockMappingStartToken(mark, mark)) + + # Simple keys are allowed after ':' in the block context. + self.allow_simple_key = not self.flow_level + + # Reset possible simple key on the current level. + self.remove_possible_simple_key() + + # Add VALUE. + start_mark = self.get_mark() + self.forward() + end_mark = self.get_mark() + self.tokens.append(ValueToken(start_mark, end_mark)) + + def fetch_alias(self): + + # ALIAS could be a simple key. + self.save_possible_simple_key() + + # No simple keys after ALIAS. + self.allow_simple_key = False + + # Scan and add ALIAS. + self.tokens.append(self.scan_anchor(AliasToken)) + + def fetch_anchor(self): + + # ANCHOR could start a simple key. + self.save_possible_simple_key() + + # No simple keys after ANCHOR. + self.allow_simple_key = False + + # Scan and add ANCHOR. + self.tokens.append(self.scan_anchor(AnchorToken)) + + def fetch_tag(self): + + # TAG could start a simple key. + self.save_possible_simple_key() + + # No simple keys after TAG. + self.allow_simple_key = False + + # Scan and add TAG. + self.tokens.append(self.scan_tag()) + + def fetch_literal(self): + self.fetch_block_scalar(style='|') + + def fetch_folded(self): + self.fetch_block_scalar(style='>') + + def fetch_block_scalar(self, style): + + # A simple key may follow a block scalar. + self.allow_simple_key = True + + # Reset possible simple key on the current level. + self.remove_possible_simple_key() + + # Scan and add SCALAR. + self.tokens.append(self.scan_block_scalar(style)) + + def fetch_single(self): + self.fetch_flow_scalar(style='\'') + + def fetch_double(self): + self.fetch_flow_scalar(style='"') + + def fetch_flow_scalar(self, style): + + # A flow scalar could be a simple key. + self.save_possible_simple_key() + + # No simple keys after flow scalars. + self.allow_simple_key = False + + # Scan and add SCALAR. + self.tokens.append(self.scan_flow_scalar(style)) + + def fetch_plain(self): + + # A plain scalar could be a simple key. + self.save_possible_simple_key() + + # No simple keys after plain scalars. But note that `scan_plain` will + # change this flag if the scan is finished at the beginning of the + # line. + self.allow_simple_key = False + + # Scan and add SCALAR. May change `allow_simple_key`. + self.tokens.append(self.scan_plain()) + + # Checkers. + + def check_directive(self): + + # DIRECTIVE: ^ '%' ... + # The '%' indicator is already checked. + if self.column == 0: + return True + + def check_document_start(self): + + # DOCUMENT-START: ^ '---' (' '|'\n') + if self.column == 0: + if self.prefix(3) == '---' \ + and self.peek(3) in '\0 \t\r\n\x85\u2028\u2029': + return True + + def check_document_end(self): + + # DOCUMENT-END: ^ '...' (' '|'\n') + if self.column == 0: + if self.prefix(3) == '...' \ + and self.peek(3) in '\0 \t\r\n\x85\u2028\u2029': + return True + + def check_block_entry(self): + + # BLOCK-ENTRY: '-' (' '|'\n') + return self.peek(1) in '\0 \t\r\n\x85\u2028\u2029' + + def check_key(self): + + # KEY(flow context): '?' + if self.flow_level: + return True + + # KEY(block context): '?' (' '|'\n') + else: + return self.peek(1) in '\0 \t\r\n\x85\u2028\u2029' + + def check_value(self): + + # VALUE(flow context): ':' + if self.flow_level: + return True + + # VALUE(block context): ':' (' '|'\n') + else: + return self.peek(1) in '\0 \t\r\n\x85\u2028\u2029' + + def check_plain(self): + + # A plain scalar may start with any non-space character except: + # '-', '?', ':', ',', '[', ']', '{', '}', + # '#', '&', '*', '!', '|', '>', '\'', '\"', + # '%', '@', '`'. + # + # It may also start with + # '-', '?', ':' + # if it is followed by a non-space character. + # + # Note that we limit the last rule to the block context (except the + # '-' character) because we want the flow context to be space + # independent. + ch = self.peek() + return ch not in '\0 \t\r\n\x85\u2028\u2029-?:,[]{}#&*!|>\'\"%@`' \ + or (self.peek(1) not in '\0 \t\r\n\x85\u2028\u2029' + and (ch == '-' or (not self.flow_level and ch in '?:'))) + + # Scanners. + + def scan_to_next_token(self): + # We ignore spaces, line breaks and comments. + # If we find a line break in the block context, we set the flag + # `allow_simple_key` on. + # The byte order mark is stripped if it's the first character in the + # stream. We do not yet support BOM inside the stream as the + # specification requires. Any such mark will be considered as a part + # of the document. + # + # TODO: We need to make tab handling rules more sane. A good rule is + # Tabs cannot precede tokens + # BLOCK-SEQUENCE-START, BLOCK-MAPPING-START, BLOCK-END, + # KEY(block), VALUE(block), BLOCK-ENTRY + # So the checking code is + # if <TAB>: + # self.allow_simple_keys = False + # We also need to add the check for `allow_simple_keys == True` to + # `unwind_indent` before issuing BLOCK-END. + # Scanners for block, flow, and plain scalars need to be modified. + + if self.index == 0 and self.peek() == '\uFEFF': + self.forward() + found = False + while not found: + while self.peek() == ' ': + self.forward() + if self.peek() == '#': + while self.peek() not in '\0\r\n\x85\u2028\u2029': + self.forward() + if self.scan_line_break(): + if not self.flow_level: + self.allow_simple_key = True + else: + found = True + + def scan_directive(self): + # See the specification for details. + start_mark = self.get_mark() + self.forward() + name = self.scan_directive_name(start_mark) + value = None + if name == 'YAML': + value = self.scan_yaml_directive_value(start_mark) + end_mark = self.get_mark() + elif name == 'TAG': + value = self.scan_tag_directive_value(start_mark) + end_mark = self.get_mark() + else: + end_mark = self.get_mark() + while self.peek() not in '\0\r\n\x85\u2028\u2029': + self.forward() + self.scan_directive_ignored_line(start_mark) + return DirectiveToken(name, value, start_mark, end_mark) + + def scan_directive_name(self, start_mark): + # See the specification for details. + length = 0 + ch = self.peek(length) + while '0' <= ch <= '9' or 'A' <= ch <= 'Z' or 'a' <= ch <= 'z' \ + or ch in '-_': + length += 1 + ch = self.peek(length) + if not length: + raise ScannerError("while scanning a directive", start_mark, + "expected alphabetic or numeric character, but found %r" + % ch, self.get_mark()) + value = self.prefix(length) + self.forward(length) + ch = self.peek() + if ch not in '\0 \r\n\x85\u2028\u2029': + raise ScannerError("while scanning a directive", start_mark, + "expected alphabetic or numeric character, but found %r" + % ch, self.get_mark()) + return value + + def scan_yaml_directive_value(self, start_mark): + # See the specification for details. + while self.peek() == ' ': + self.forward() + major = self.scan_yaml_directive_number(start_mark) + if self.peek() != '.': + raise ScannerError("while scanning a directive", start_mark, + "expected a digit or '.', but found %r" % self.peek(), + self.get_mark()) + self.forward() + minor = self.scan_yaml_directive_number(start_mark) + if self.peek() not in '\0 \r\n\x85\u2028\u2029': + raise ScannerError("while scanning a directive", start_mark, + "expected a digit or ' ', but found %r" % self.peek(), + self.get_mark()) + return (major, minor) + + def scan_yaml_directive_number(self, start_mark): + # See the specification for details. + ch = self.peek() + if not ('0' <= ch <= '9'): + raise ScannerError("while scanning a directive", start_mark, + "expected a digit, but found %r" % ch, self.get_mark()) + length = 0 + while '0' <= self.peek(length) <= '9': + length += 1 + value = int(self.prefix(length)) + self.forward(length) + return value + + def scan_tag_directive_value(self, start_mark): + # See the specification for details. + while self.peek() == ' ': + self.forward() + handle = self.scan_tag_directive_handle(start_mark) + while self.peek() == ' ': + self.forward() + prefix = self.scan_tag_directive_prefix(start_mark) + return (handle, prefix) + + def scan_tag_directive_handle(self, start_mark): + # See the specification for details. + value = self.scan_tag_handle('directive', start_mark) + ch = self.peek() + if ch != ' ': + raise ScannerError("while scanning a directive", start_mark, + "expected ' ', but found %r" % ch, self.get_mark()) + return value + + def scan_tag_directive_prefix(self, start_mark): + # See the specification for details. + value = self.scan_tag_uri('directive', start_mark) + ch = self.peek() + if ch not in '\0 \r\n\x85\u2028\u2029': + raise ScannerError("while scanning a directive", start_mark, + "expected ' ', but found %r" % ch, self.get_mark()) + return value + + def scan_directive_ignored_line(self, start_mark): + # See the specification for details. + while self.peek() == ' ': + self.forward() + if self.peek() == '#': + while self.peek() not in '\0\r\n\x85\u2028\u2029': + self.forward() + ch = self.peek() + if ch not in '\0\r\n\x85\u2028\u2029': + raise ScannerError("while scanning a directive", start_mark, + "expected a comment or a line break, but found %r" + % ch, self.get_mark()) + self.scan_line_break() + + def scan_anchor(self, TokenClass): + # The specification does not restrict characters for anchors and + # aliases. This may lead to problems, for instance, the document: + # [ *alias, value ] + # can be interpreted in two ways, as + # [ "value" ] + # and + # [ *alias , "value" ] + # Therefore we restrict aliases to numbers and ASCII letters. + start_mark = self.get_mark() + indicator = self.peek() + if indicator == '*': + name = 'alias' + else: + name = 'anchor' + self.forward() + length = 0 + ch = self.peek(length) + while '0' <= ch <= '9' or 'A' <= ch <= 'Z' or 'a' <= ch <= 'z' \ + or ch in '-_': + length += 1 + ch = self.peek(length) + if not length: + raise ScannerError("while scanning an %s" % name, start_mark, + "expected alphabetic or numeric character, but found %r" + % ch, self.get_mark()) + value = self.prefix(length) + self.forward(length) + ch = self.peek() + if ch not in '\0 \t\r\n\x85\u2028\u2029?:,]}%@`': + raise ScannerError("while scanning an %s" % name, start_mark, + "expected alphabetic or numeric character, but found %r" + % ch, self.get_mark()) + end_mark = self.get_mark() + return TokenClass(value, start_mark, end_mark) + + def scan_tag(self): + # See the specification for details. + start_mark = self.get_mark() + ch = self.peek(1) + if ch == '<': + handle = None + self.forward(2) + suffix = self.scan_tag_uri('tag', start_mark) + if self.peek() != '>': + raise ScannerError("while parsing a tag", start_mark, + "expected '>', but found %r" % self.peek(), + self.get_mark()) + self.forward() + elif ch in '\0 \t\r\n\x85\u2028\u2029': + handle = None + suffix = '!' + self.forward() + else: + length = 1 + use_handle = False + while ch not in '\0 \r\n\x85\u2028\u2029': + if ch == '!': + use_handle = True + break + length += 1 + ch = self.peek(length) + handle = '!' + if use_handle: + handle = self.scan_tag_handle('tag', start_mark) + else: + handle = '!' + self.forward() + suffix = self.scan_tag_uri('tag', start_mark) + ch = self.peek() + if ch not in '\0 \r\n\x85\u2028\u2029': + raise ScannerError("while scanning a tag", start_mark, + "expected ' ', but found %r" % ch, self.get_mark()) + value = (handle, suffix) + end_mark = self.get_mark() + return TagToken(value, start_mark, end_mark) + + def scan_block_scalar(self, style): + # See the specification for details. + + if style == '>': + folded = True + else: + folded = False + + chunks = [] + start_mark = self.get_mark() + + # Scan the header. + self.forward() + chomping, increment = self.scan_block_scalar_indicators(start_mark) + self.scan_block_scalar_ignored_line(start_mark) + + # Determine the indentation level and go to the first non-empty line. + min_indent = self.indent+1 + if min_indent < 1: + min_indent = 1 + if increment is None: + breaks, max_indent, end_mark = self.scan_block_scalar_indentation() + indent = max(min_indent, max_indent) + else: + indent = min_indent+increment-1 + breaks, end_mark = self.scan_block_scalar_breaks(indent) + line_break = '' + + # Scan the inner part of the block scalar. + while self.column == indent and self.peek() != '\0': + chunks.extend(breaks) + leading_non_space = self.peek() not in ' \t' + length = 0 + while self.peek(length) not in '\0\r\n\x85\u2028\u2029': + length += 1 + chunks.append(self.prefix(length)) + self.forward(length) + line_break = self.scan_line_break() + breaks, end_mark = self.scan_block_scalar_breaks(indent) + if self.column == indent and self.peek() != '\0': + + # Unfortunately, folding rules are ambiguous. + # + # This is the folding according to the specification: + + if folded and line_break == '\n' \ + and leading_non_space and self.peek() not in ' \t': + if not breaks: + chunks.append(' ') + else: + chunks.append(line_break) + + # This is Clark Evans's interpretation (also in the spec + # examples): + # + #if folded and line_break == '\n': + # if not breaks: + # if self.peek() not in ' \t': + # chunks.append(' ') + # else: + # chunks.append(line_break) + #else: + # chunks.append(line_break) + else: + break + + # Chomp the tail. + if chomping is not False: + chunks.append(line_break) + if chomping is True: + chunks.extend(breaks) + + # We are done. + return ScalarToken(''.join(chunks), False, start_mark, end_mark, + style) + + def scan_block_scalar_indicators(self, start_mark): + # See the specification for details. + chomping = None + increment = None + ch = self.peek() + if ch in '+-': + if ch == '+': + chomping = True + else: + chomping = False + self.forward() + ch = self.peek() + if ch in '0123456789': + increment = int(ch) + if increment == 0: + raise ScannerError("while scanning a block scalar", start_mark, + "expected indentation indicator in the range 1-9, but found 0", + self.get_mark()) + self.forward() + elif ch in '0123456789': + increment = int(ch) + if increment == 0: + raise ScannerError("while scanning a block scalar", start_mark, + "expected indentation indicator in the range 1-9, but found 0", + self.get_mark()) + self.forward() + ch = self.peek() + if ch in '+-': + if ch == '+': + chomping = True + else: + chomping = False + self.forward() + ch = self.peek() + if ch not in '\0 \r\n\x85\u2028\u2029': + raise ScannerError("while scanning a block scalar", start_mark, + "expected chomping or indentation indicators, but found %r" + % ch, self.get_mark()) + return chomping, increment + + def scan_block_scalar_ignored_line(self, start_mark): + # See the specification for details. + while self.peek() == ' ': + self.forward() + if self.peek() == '#': + while self.peek() not in '\0\r\n\x85\u2028\u2029': + self.forward() + ch = self.peek() + if ch not in '\0\r\n\x85\u2028\u2029': + raise ScannerError("while scanning a block scalar", start_mark, + "expected a comment or a line break, but found %r" % ch, + self.get_mark()) + self.scan_line_break() + + def scan_block_scalar_indentation(self): + # See the specification for details. + chunks = [] + max_indent = 0 + end_mark = self.get_mark() + while self.peek() in ' \r\n\x85\u2028\u2029': + if self.peek() != ' ': + chunks.append(self.scan_line_break()) + end_mark = self.get_mark() + else: + self.forward() + if self.column > max_indent: + max_indent = self.column + return chunks, max_indent, end_mark + + def scan_block_scalar_breaks(self, indent): + # See the specification for details. + chunks = [] + end_mark = self.get_mark() + while self.column < indent and self.peek() == ' ': + self.forward() + while self.peek() in '\r\n\x85\u2028\u2029': + chunks.append(self.scan_line_break()) + end_mark = self.get_mark() + while self.column < indent and self.peek() == ' ': + self.forward() + return chunks, end_mark + + def scan_flow_scalar(self, style): + # See the specification for details. + # Note that we loose indentation rules for quoted scalars. Quoted + # scalars don't need to adhere indentation because " and ' clearly + # mark the beginning and the end of them. Therefore we are less + # restrictive then the specification requires. We only need to check + # that document separators are not included in scalars. + if style == '"': + double = True + else: + double = False + chunks = [] + start_mark = self.get_mark() + quote = self.peek() + self.forward() + chunks.extend(self.scan_flow_scalar_non_spaces(double, start_mark)) + while self.peek() != quote: + chunks.extend(self.scan_flow_scalar_spaces(double, start_mark)) + chunks.extend(self.scan_flow_scalar_non_spaces(double, start_mark)) + self.forward() + end_mark = self.get_mark() + return ScalarToken(''.join(chunks), False, start_mark, end_mark, + style) + + ESCAPE_REPLACEMENTS = { + '0': '\0', + 'a': '\x07', + 'b': '\x08', + 't': '\x09', + '\t': '\x09', + 'n': '\x0A', + 'v': '\x0B', + 'f': '\x0C', + 'r': '\x0D', + 'e': '\x1B', + ' ': '\x20', + '\"': '\"', + '\\': '\\', + '/': '/', + 'N': '\x85', + '_': '\xA0', + 'L': '\u2028', + 'P': '\u2029', + } + + ESCAPE_CODES = { + 'x': 2, + 'u': 4, + 'U': 8, + } + + def scan_flow_scalar_non_spaces(self, double, start_mark): + # See the specification for details. + chunks = [] + while True: + length = 0 + while self.peek(length) not in '\'\"\\\0 \t\r\n\x85\u2028\u2029': + length += 1 + if length: + chunks.append(self.prefix(length)) + self.forward(length) + ch = self.peek() + if not double and ch == '\'' and self.peek(1) == '\'': + chunks.append('\'') + self.forward(2) + elif (double and ch == '\'') or (not double and ch in '\"\\'): + chunks.append(ch) + self.forward() + elif double and ch == '\\': + self.forward() + ch = self.peek() + if ch in self.ESCAPE_REPLACEMENTS: + chunks.append(self.ESCAPE_REPLACEMENTS[ch]) + self.forward() + elif ch in self.ESCAPE_CODES: + length = self.ESCAPE_CODES[ch] + self.forward() + for k in range(length): + if self.peek(k) not in '0123456789ABCDEFabcdef': + raise ScannerError("while scanning a double-quoted scalar", start_mark, + "expected escape sequence of %d hexadecimal numbers, but found %r" % + (length, self.peek(k)), self.get_mark()) + code = int(self.prefix(length), 16) + chunks.append(chr(code)) + self.forward(length) + elif ch in '\r\n\x85\u2028\u2029': + self.scan_line_break() + chunks.extend(self.scan_flow_scalar_breaks(double, start_mark)) + else: + raise ScannerError("while scanning a double-quoted scalar", start_mark, + "found unknown escape character %r" % ch, self.get_mark()) + else: + return chunks + + def scan_flow_scalar_spaces(self, double, start_mark): + # See the specification for details. + chunks = [] + length = 0 + while self.peek(length) in ' \t': + length += 1 + whitespaces = self.prefix(length) + self.forward(length) + ch = self.peek() + if ch == '\0': + raise ScannerError("while scanning a quoted scalar", start_mark, + "found unexpected end of stream", self.get_mark()) + elif ch in '\r\n\x85\u2028\u2029': + line_break = self.scan_line_break() + breaks = self.scan_flow_scalar_breaks(double, start_mark) + if line_break != '\n': + chunks.append(line_break) + elif not breaks: + chunks.append(' ') + chunks.extend(breaks) + else: + chunks.append(whitespaces) + return chunks + + def scan_flow_scalar_breaks(self, double, start_mark): + # See the specification for details. + chunks = [] + while True: + # Instead of checking indentation, we check for document + # separators. + prefix = self.prefix(3) + if (prefix == '---' or prefix == '...') \ + and self.peek(3) in '\0 \t\r\n\x85\u2028\u2029': + raise ScannerError("while scanning a quoted scalar", start_mark, + "found unexpected document separator", self.get_mark()) + while self.peek() in ' \t': + self.forward() + if self.peek() in '\r\n\x85\u2028\u2029': + chunks.append(self.scan_line_break()) + else: + return chunks + + def scan_plain(self): + # See the specification for details. + # We add an additional restriction for the flow context: + # plain scalars in the flow context cannot contain ',' or '?'. + # We also keep track of the `allow_simple_key` flag here. + # Indentation rules are loosed for the flow context. + chunks = [] + start_mark = self.get_mark() + end_mark = start_mark + indent = self.indent+1 + # We allow zero indentation for scalars, but then we need to check for + # document separators at the beginning of the line. + #if indent == 0: + # indent = 1 + spaces = [] + while True: + length = 0 + if self.peek() == '#': + break + while True: + ch = self.peek(length) + if ch in '\0 \t\r\n\x85\u2028\u2029' \ + or (ch == ':' and + self.peek(length+1) in '\0 \t\r\n\x85\u2028\u2029' + + (u',[]{}' if self.flow_level else u''))\ + or (self.flow_level and ch in ',?[]{}'): + break + length += 1 + if length == 0: + break + self.allow_simple_key = False + chunks.extend(spaces) + chunks.append(self.prefix(length)) + self.forward(length) + end_mark = self.get_mark() + spaces = self.scan_plain_spaces(indent, start_mark) + if not spaces or self.peek() == '#' \ + or (not self.flow_level and self.column < indent): + break + return ScalarToken(''.join(chunks), True, start_mark, end_mark) + + def scan_plain_spaces(self, indent, start_mark): + # See the specification for details. + # The specification is really confusing about tabs in plain scalars. + # We just forbid them completely. Do not use tabs in YAML! + chunks = [] + length = 0 + while self.peek(length) in ' ': + length += 1 + whitespaces = self.prefix(length) + self.forward(length) + ch = self.peek() + if ch in '\r\n\x85\u2028\u2029': + line_break = self.scan_line_break() + self.allow_simple_key = True + prefix = self.prefix(3) + if (prefix == '---' or prefix == '...') \ + and self.peek(3) in '\0 \t\r\n\x85\u2028\u2029': + return + breaks = [] + while self.peek() in ' \r\n\x85\u2028\u2029': + if self.peek() == ' ': + self.forward() + else: + breaks.append(self.scan_line_break()) + prefix = self.prefix(3) + if (prefix == '---' or prefix == '...') \ + and self.peek(3) in '\0 \t\r\n\x85\u2028\u2029': + return + if line_break != '\n': + chunks.append(line_break) + elif not breaks: + chunks.append(' ') + chunks.extend(breaks) + elif whitespaces: + chunks.append(whitespaces) + return chunks + + def scan_tag_handle(self, name, start_mark): + # See the specification for details. + # For some strange reasons, the specification does not allow '_' in + # tag handles. I have allowed it anyway. + ch = self.peek() + if ch != '!': + raise ScannerError("while scanning a %s" % name, start_mark, + "expected '!', but found %r" % ch, self.get_mark()) + length = 1 + ch = self.peek(length) + if ch != ' ': + while '0' <= ch <= '9' or 'A' <= ch <= 'Z' or 'a' <= ch <= 'z' \ + or ch in '-_': + length += 1 + ch = self.peek(length) + if ch != '!': + self.forward(length) + raise ScannerError("while scanning a %s" % name, start_mark, + "expected '!', but found %r" % ch, self.get_mark()) + length += 1 + value = self.prefix(length) + self.forward(length) + return value + + def scan_tag_uri(self, name, start_mark): + # See the specification for details. + # Note: we do not check if URI is well-formed. + chunks = [] + length = 0 + ch = self.peek(length) + while '0' <= ch <= '9' or 'A' <= ch <= 'Z' or 'a' <= ch <= 'z' \ + or ch in '-;/?:@&=+$,_.!~*\'()[]%': + if ch == '%': + chunks.append(self.prefix(length)) + self.forward(length) + length = 0 + chunks.append(self.scan_uri_escapes(name, start_mark)) + else: + length += 1 + ch = self.peek(length) + if length: + chunks.append(self.prefix(length)) + self.forward(length) + length = 0 + if not chunks: + raise ScannerError("while parsing a %s" % name, start_mark, + "expected URI, but found %r" % ch, self.get_mark()) + return ''.join(chunks) + + def scan_uri_escapes(self, name, start_mark): + # See the specification for details. + codes = [] + mark = self.get_mark() + while self.peek() == '%': + self.forward() + for k in range(2): + if self.peek(k) not in '0123456789ABCDEFabcdef': + raise ScannerError("while scanning a %s" % name, start_mark, + "expected URI escape sequence of 2 hexadecimal numbers, but found %r" + % self.peek(k), self.get_mark()) + codes.append(int(self.prefix(2), 16)) + self.forward(2) + try: + value = bytes(codes).decode('utf-8') + except UnicodeDecodeError as exc: + raise ScannerError("while scanning a %s" % name, start_mark, str(exc), mark) + return value + + def scan_line_break(self): + # Transforms: + # '\r\n' : '\n' + # '\r' : '\n' + # '\n' : '\n' + # '\x85' : '\n' + # '\u2028' : '\u2028' + # '\u2029 : '\u2029' + # default : '' + ch = self.peek() + if ch in '\r\n\x85': + if self.prefix(2) == '\r\n': + self.forward(2) + else: + self.forward() + return '\n' + elif ch in '\u2028\u2029': + self.forward() + return ch + return ''
diff --git a/third_party/pyyaml/serializer.py b/third_party/pyyaml/serializer.py new file mode 100644 index 0000000..fe911e6 --- /dev/null +++ b/third_party/pyyaml/serializer.py
@@ -0,0 +1,111 @@ + +__all__ = ['Serializer', 'SerializerError'] + +from .error import YAMLError +from .events import * +from .nodes import * + +class SerializerError(YAMLError): + pass + +class Serializer: + + ANCHOR_TEMPLATE = 'id%03d' + + def __init__(self, encoding=None, + explicit_start=None, explicit_end=None, version=None, tags=None): + self.use_encoding = encoding + self.use_explicit_start = explicit_start + self.use_explicit_end = explicit_end + self.use_version = version + self.use_tags = tags + self.serialized_nodes = {} + self.anchors = {} + self.last_anchor_id = 0 + self.closed = None + + def open(self): + if self.closed is None: + self.emit(StreamStartEvent(encoding=self.use_encoding)) + self.closed = False + elif self.closed: + raise SerializerError("serializer is closed") + else: + raise SerializerError("serializer is already opened") + + def close(self): + if self.closed is None: + raise SerializerError("serializer is not opened") + elif not self.closed: + self.emit(StreamEndEvent()) + self.closed = True + + #def __del__(self): + # self.close() + + def serialize(self, node): + if self.closed is None: + raise SerializerError("serializer is not opened") + elif self.closed: + raise SerializerError("serializer is closed") + self.emit(DocumentStartEvent(explicit=self.use_explicit_start, + version=self.use_version, tags=self.use_tags)) + self.anchor_node(node) + self.serialize_node(node, None, None) + self.emit(DocumentEndEvent(explicit=self.use_explicit_end)) + self.serialized_nodes = {} + self.anchors = {} + self.last_anchor_id = 0 + + def anchor_node(self, node): + if node in self.anchors: + if self.anchors[node] is None: + self.anchors[node] = self.generate_anchor(node) + else: + self.anchors[node] = None + if isinstance(node, SequenceNode): + for item in node.value: + self.anchor_node(item) + elif isinstance(node, MappingNode): + for key, value in node.value: + self.anchor_node(key) + self.anchor_node(value) + + def generate_anchor(self, node): + self.last_anchor_id += 1 + return self.ANCHOR_TEMPLATE % self.last_anchor_id + + def serialize_node(self, node, parent, index): + alias = self.anchors[node] + if node in self.serialized_nodes: + self.emit(AliasEvent(alias)) + else: + self.serialized_nodes[node] = True + self.descend_resolver(parent, index) + if isinstance(node, ScalarNode): + detected_tag = self.resolve(ScalarNode, node.value, (True, False)) + default_tag = self.resolve(ScalarNode, node.value, (False, True)) + implicit = (node.tag == detected_tag), (node.tag == default_tag) + self.emit(ScalarEvent(alias, node.tag, implicit, node.value, + style=node.style)) + elif isinstance(node, SequenceNode): + implicit = (node.tag + == self.resolve(SequenceNode, node.value, True)) + self.emit(SequenceStartEvent(alias, node.tag, implicit, + flow_style=node.flow_style)) + index = 0 + for item in node.value: + self.serialize_node(item, node, index) + index += 1 + self.emit(SequenceEndEvent()) + elif isinstance(node, MappingNode): + implicit = (node.tag + == self.resolve(MappingNode, node.value, True)) + self.emit(MappingStartEvent(alias, node.tag, implicit, + flow_style=node.flow_style)) + for key, value in node.value: + self.serialize_node(key, node, None) + self.serialize_node(value, node, key) + self.emit(MappingEndEvent()) + self.ascend_resolver() +
diff --git a/third_party/pyyaml/tokens.py b/third_party/pyyaml/tokens.py new file mode 100644 index 0000000..4d0b48a --- /dev/null +++ b/third_party/pyyaml/tokens.py
@@ -0,0 +1,104 @@ + +class Token(object): + def __init__(self, start_mark, end_mark): + self.start_mark = start_mark + self.end_mark = end_mark + def __repr__(self): + attributes = [key for key in self.__dict__ + if not key.endswith('_mark')] + attributes.sort() + arguments = ', '.join(['%s=%r' % (key, getattr(self, key)) + for key in attributes]) + return '%s(%s)' % (self.__class__.__name__, arguments) + +#class BOMToken(Token): +# id = '<byte order mark>' + +class DirectiveToken(Token): + id = '<directive>' + def __init__(self, name, value, start_mark, end_mark): + self.name = name + self.value = value + self.start_mark = start_mark + self.end_mark = end_mark + +class DocumentStartToken(Token): + id = '<document start>' + +class DocumentEndToken(Token): + id = '<document end>' + +class StreamStartToken(Token): + id = '<stream start>' + def __init__(self, start_mark=None, end_mark=None, + encoding=None): + self.start_mark = start_mark + self.end_mark = end_mark + self.encoding = encoding + +class StreamEndToken(Token): + id = '<stream end>' + +class BlockSequenceStartToken(Token): + id = '<block sequence start>' + +class BlockMappingStartToken(Token): + id = '<block mapping start>' + +class BlockEndToken(Token): + id = '<block end>' + +class FlowSequenceStartToken(Token): + id = '[' + +class FlowMappingStartToken(Token): + id = '{' + +class FlowSequenceEndToken(Token): + id = ']' + +class FlowMappingEndToken(Token): + id = '}' + +class KeyToken(Token): + id = '?' + +class ValueToken(Token): + id = ':' + +class BlockEntryToken(Token): + id = '-' + +class FlowEntryToken(Token): + id = ',' + +class AliasToken(Token): + id = '<alias>' + def __init__(self, value, start_mark, end_mark): + self.value = value + self.start_mark = start_mark + self.end_mark = end_mark + +class AnchorToken(Token): + id = '<anchor>' + def __init__(self, value, start_mark, end_mark): + self.value = value + self.start_mark = start_mark + self.end_mark = end_mark + +class TagToken(Token): + id = '<tag>' + def __init__(self, value, start_mark, end_mark): + self.value = value + self.start_mark = start_mark + self.end_mark = end_mark + +class ScalarToken(Token): + id = '<scalar>' + def __init__(self, value, plain, start_mark, end_mark, style=None): + self.value = value + self.plain = plain + self.start_mark = start_mark + self.end_mark = end_mark + self.style = style +
diff --git a/third_party/subresource-filter-ruleset/README.chromium b/third_party/subresource-filter-ruleset/README.chromium index 8293d69..daf7e0c 100644 --- a/third_party/subresource-filter-ruleset/README.chromium +++ b/third_party/subresource-filter-ruleset/README.chromium
@@ -1,6 +1,6 @@ Name: EasyList URL: https://easylist.to/easylist/easylist.txt -Version: 202204111702 +Version: 202205171532 License: Creative Commons Attribution-ShareAlike 3.0 Unported License Android Compatible: yes License File: LICENSE
diff --git a/third_party/subresource-filter-ruleset/data/UnindexedRules.sha1 b/third_party/subresource-filter-ruleset/data/UnindexedRules.sha1 index 61cc458..8233c34a 100644 --- a/third_party/subresource-filter-ruleset/data/UnindexedRules.sha1 +++ b/third_party/subresource-filter-ruleset/data/UnindexedRules.sha1
@@ -1 +1 @@ -88bd294563a3e1ba663375609e83dfed3b57e6fe \ No newline at end of file +f43d903b13ab969e2276e304795ce164f22f893c \ No newline at end of file
diff --git a/third_party/subresource-filter-ruleset/manifest.json b/third_party/subresource-filter-ruleset/manifest.json index 6bab987..b2c687e 100644 --- a/third_party/subresource-filter-ruleset/manifest.json +++ b/third_party/subresource-filter-ruleset/manifest.json
@@ -2,5 +2,5 @@ "manifest_version": 2, "name": "Subresource Filtering Rules", "ruleset_format": 1, - "version": "9.35.0" + "version": "9.36.0" }
diff --git a/third_party/tflite/README.chromium b/third_party/tflite/README.chromium index 19cbc89..3595c82f 100644 --- a/third_party/tflite/README.chromium +++ b/third_party/tflite/README.chromium
@@ -1,8 +1,8 @@ Name: TensorFlow Lite Short Name: tflite URL: https://github.com/tensorflow/tensorflow -Version: 6959967708cc680fe0d17381b5a421413783bda1 -Date: 2022/05/23 +Version: 5ddefa9f8d1455c8ca694ba1a511ba82e3a88960 +Date: 2022/05/31 License: Apache 2.0 License File: LICENSE Security Critical: Yes
diff --git a/third_party/webxr_test_pages/webxr-samples/proposals/camera-access-barebones.html b/third_party/webxr_test_pages/webxr-samples/proposals/camera-access-barebones.html index 008e592e..c1952d5 100644 --- a/third_party/webxr_test_pages/webxr-samples/proposals/camera-access-barebones.html +++ b/third_party/webxr_test_pages/webxr-samples/proposals/camera-access-barebones.html
@@ -43,8 +43,11 @@ <header> <details open> <summary>Camera Access Barebones</summary> + This sample demonstrates extremely simple use of WebXR's camera access by creating a rotating cube and applying the camera's image as a texture to it. <p> - This sample demonstrates extremely simple use of WebXR's camera access by creating a rotating cube and applying the camera's image as a texture to it. + <input id="cameraOptional" type="checkbox"> + <label for="cameraOptional">Should "camera-access" feature be requested optionally?</label><br/> + <a class="back" href="./index.html">Back</a> </p> <div id="warning-zone"></div> @@ -61,6 +64,8 @@ import * as mat4 from "../js/third-party/gl-matrix/mat4.js" import {QueryArgs} from '../js/cottontail/src/util/query-args.js'; + const cameraOptionalElement = document.getElementById('cameraOptional'); + // XR globals. let xrButton = document.getElementById('xr-button'); let xrSession = null; @@ -150,9 +155,16 @@ function onButtonClicked() { if (!xrSession) { const sessionOptions = { - requiredFeatures: ['camera-access'] + requiredFeatures: [], + optionalFeatures: [], }; + if(cameraOptionalElement.checked) { + sessionOptions.optionalFeatures.push('camera-access'); + } else { + sessionOptions.requiredFeatures.push('camera-access'); + } + if (use_dom_overlay) { sessionOptions.requiredFeatures.push('dom-overlay'); sessionOptions.domOverlay = { root: textOverlayElement };
diff --git a/third_party/zxcvbn-cpp/native-src/zxcvbn/util.cpp b/third_party/zxcvbn-cpp/native-src/zxcvbn/util.cpp index dbbbce3..69d69bf 100644 --- a/third_party/zxcvbn-cpp/native-src/zxcvbn/util.cpp +++ b/third_party/zxcvbn-cpp/native-src/zxcvbn/util.cpp
@@ -45,7 +45,7 @@ const char* src = &*it; int32_t src_len = std::distance(it, end); int32_t char_index = 0; - uint32_t code_point_out; + base_icu::UChar32 code_point_out; base::ReadUnicodeCharacter(src, src_len, &char_index, &code_point_out); return {code_point_out, it + ++char_index};
diff --git a/third_party/zxcvbn-cpp/patches/base_utf8.diff b/third_party/zxcvbn-cpp/patches/base_utf8.diff index 5dbb2049..c7a81c5b 100644 --- a/third_party/zxcvbn-cpp/patches/base_utf8.diff +++ b/third_party/zxcvbn-cpp/patches/base_utf8.diff
@@ -58,7 +58,7 @@ + const char* src = &*it; + int32_t src_len = std::distance(it, end); + int32_t char_index = 0; -+ uint32_t code_point_out; ++ base_icu::UChar32 code_point_out; -bool utf8_valid(std::string::const_iterator start, - std::string::const_iterator end) {
diff --git a/tools/clang/scripts/build.py b/tools/clang/scripts/build.py index 2df2392..a2ec9e5 100755 --- a/tools/clang/scripts/build.py +++ b/tools/clang/scripts/build.py
@@ -1000,7 +1000,8 @@ if args.build_mac_arm: assert platform.machine() != 'arm64', 'build_mac_arm for cross build only' cmake_args += ['-DCMAKE_OSX_ARCHITECTURES=arm64', - '-DLLVM_USE_HOST_TOOLS=ON'] + '-DLLVM_USE_HOST_TOOLS=ON', + '-DCMAKE_SYSTEM_NAME=Darwin'] # The default LLVM_DEFAULT_TARGET_TRIPLE depends on the host machine. # Set it explicitly to make the build of clang more hermetic, and also to
diff --git a/tools/clang/scripts/update.py b/tools/clang/scripts/update.py index 43cf1e36..a6af098 100755 --- a/tools/clang/scripts/update.py +++ b/tools/clang/scripts/update.py
@@ -35,8 +35,8 @@ # https://chromium.googlesource.com/chromium/src/+/main/docs/updating_clang.md # Reverting problematic clang rolls is safe, though. # This is the output of `git describe` and is usable as a commit-ish. -CLANG_REVISION = 'llvmorg-15-init-10717-ge00cbbec' -CLANG_SUB_REVISION = 1 +CLANG_REVISION = 'llvmorg-15-init-11722-g3f3a235a' +CLANG_SUB_REVISION = 2 PACKAGE_VERSION = '%s-%s' % (CLANG_REVISION, CLANG_SUB_REVISION) RELEASE_VERSION = '15.0.0'
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml index 0a8ec411..ad7499fa 100644 --- a/tools/metrics/histograms/enums.xml +++ b/tools/metrics/histograms/enums.xml
@@ -50566,6 +50566,12 @@ <int value="4" label="Background prevented impression"/> </enum> +<enum name="IOSDefaultBrowserPromoSource"> + <int value="0" label="Settings"/> + <int value="1" label="Omnibox"/> + <int value="2" label="External Intent"/> +</enum> + <enum name="IOSDefaultBrowserTailoredPromoType"> <int value="0" label="Unkown"/> <int value="1" label="Made for iOS"/> @@ -89695,6 +89701,14 @@ <int value="20" label="OTHER"/> </enum> +<enum name="StandbyBucket"> + <int value="0" label="Active"/> + <int value="1" label="WorkingSet"/> + <int value="2" label="Frequent"/> + <int value="3" label="Rare"/> + <int value="4" label="Restricted"/> +</enum> + <enum name="StarEntryPointAction"> <int value="0" label="Add bookmark"/> <int value="1" label="Edit bookmark"/>
diff --git a/tools/metrics/histograms/metadata/METRIC_REVIEWER_OWNERS b/tools/metrics/histograms/metadata/METRIC_REVIEWER_OWNERS index 0b24669..6bdc9e8 100644 --- a/tools/metrics/histograms/metadata/METRIC_REVIEWER_OWNERS +++ b/tools/metrics/histograms/metadata/METRIC_REVIEWER_OWNERS
@@ -66,7 +66,6 @@ mutexlox@chromium.org nancylingwang@chromium.org nigeltao@chromium.org -nohle@chromium.org nyquist@chromium.org olivierli@chromium.org olivierrobin@chromium.org
diff --git a/tools/metrics/histograms/metadata/android/histograms.xml b/tools/metrics/histograms/metadata/android/histograms.xml index 6d975668..b1bd0d1 100644 --- a/tools/metrics/histograms/metadata/android/histograms.xml +++ b/tools/metrics/histograms/metadata/android/histograms.xml
@@ -503,6 +503,27 @@ </summary> </histogram> +<histogram name="Android.BackgroundRestrictions.IsBackgroundRestricted" + enum="Boolean" expires_after="2022-10-23"> + <owner>shaktisahu@chromium.org</owner> + <owner>chrome-segmentation-platform@google.com</owner> + <summary> + Records whether user has enabled background restriction for chrome. Recorded + after native initialization in browser process. + </summary> +</histogram> + +<histogram name="Android.BackgroundRestrictions.StandbyBucket" + enum="StandbyBucket" expires_after="2022-10-23"> + <owner>shaktisahu@chromium.org</owner> + <owner>chrome-segmentation-platform@google.com</owner> + <summary> + Records the standby bucket (UsageStatsManager.getAppStandbyBucket) in which + chrome is currently placed by android. Recorded after native initialization + in browser process. + </summary> +</histogram> + <histogram name="Android.BackgroundTaskScheduler.ExactTaskCreated" enum="BackgroundTaskId" expires_after="2022-05-01"> <owner>fgorski@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/bluetooth/OWNERS b/tools/metrics/histograms/metadata/bluetooth/OWNERS index a85142b7aa..a354111 100644 --- a/tools/metrics/histograms/metadata/bluetooth/OWNERS +++ b/tools/metrics/histograms/metadata/bluetooth/OWNERS
@@ -3,6 +3,6 @@ # Prefer sending CLs to the owners listed below. # Use chromium-metrics-reviews@google.com as a backup. chadduffin@chromium.org +crisrael@google.com khorimoto@chromium.org -nohle@chromium.org reillyg@chromium.org
diff --git a/tools/metrics/histograms/metadata/bluetooth/histograms.xml b/tools/metrics/histograms/metadata/bluetooth/histograms.xml index e167cdfd..2ff1055 100644 --- a/tools/metrics/histograms/metadata/bluetooth/histograms.xml +++ b/tools/metrics/histograms/metadata/bluetooth/histograms.xml
@@ -1188,7 +1188,7 @@ <histogram name="Bluetooth.Linux.ConnectToServiceInsecurelyResult" enum="BluetoothAdapterConnectToServiceInsecurelyResult" expires_after="2023-02-23"> - <owner>nohle@chromium.org</owner> + <owner>hansenmichael@google.com</owner> <owner>nearby-share-chromeos-eng@google.com</owner> <summary> Result of attempting a call to mojom::Adapter::ConnectToServiceInsecurely().
diff --git a/tools/metrics/histograms/metadata/chromeos/OWNERS b/tools/metrics/histograms/metadata/chromeos/OWNERS index eccde9e9..a0cfd1ab 100644 --- a/tools/metrics/histograms/metadata/chromeos/OWNERS +++ b/tools/metrics/histograms/metadata/chromeos/OWNERS
@@ -4,8 +4,8 @@ # Use chromium-metrics-reviews@google.com as a backup. azeemarshad@chromium.org chadduffin@chromium.org +crisrael@google.com jorgelo@chromium.org khorimoto@chromium.org -nohle@chromium.org tsergeant@chromium.org zentaro@chromium.org
diff --git a/tools/metrics/histograms/metadata/chromeos/histograms.xml b/tools/metrics/histograms/metadata/chromeos/histograms.xml index eb51b59b..2e17a70 100644 --- a/tools/metrics/histograms/metadata/chromeos/histograms.xml +++ b/tools/metrics/histograms/metadata/chromeos/histograms.xml
@@ -62,7 +62,7 @@ <owner>cros-oac@google.com</owner> </variant> <variant name="NearbyShare" summary="Nearby Share"> - <owner>nohle@chromium.org</owner> + <owner>hansenmichael@google.com</owner> <owner>nearby-share-chromeos-eng@google.com</owner> </variant> <variant name="NearbyShareBackgroundScanning"
diff --git a/tools/metrics/histograms/metadata/compositing/histograms.xml b/tools/metrics/histograms/metadata/compositing/histograms.xml index 945b7583..cf2c2f7 100644 --- a/tools/metrics/histograms/metadata/compositing/histograms.xml +++ b/tools/metrics/histograms/metadata/compositing/histograms.xml
@@ -882,44 +882,6 @@ </token> </histogram> -<histogram name="Graphics.PresentationTimestamp.InvalidBeforeSwap" units="ms" - expires_after="2022-05-01"> - <owner>sadrul@chromium.org</owner> - <owner>graphics-dev@chromium.org</owner> - <summary> - Presentation timestamp comes from the driver when showing a display frame on - screen. These timestamps could be invalid, and be earlier than the swap - time. This metric records how much time before the swap-time such invalid - timestamps are. If the presentation timestamp is after the swap-time, then - this metric is not recorded. - </summary> -</histogram> - -<histogram name="Graphics.PresentationTimestamp.InvalidFromFuture" units="ms" - expires_after="2022-01-02"> - <owner>sadrul@chromium.org</owner> - <owner>graphics-dev@chromium.org</owner> - <summary> - Presentation timestamp comes from the driver when showing a display frame on - screen. These timestamps can sometimes be in the future. This metric records - how far in the future these timestamps can be. If the timestamp is not in - the future, then this metric is not recorded. - </summary> -</histogram> - -<histogram name="Graphics.PresentationTimestamp.LargePresentationDelta" - units="ms" expires_after="2022-01-02"> - <owner>sadrul@chromium.org</owner> - <owner>graphics-dev@chromium.org</owner> - <summary> - Measures very large (more than 3 minutes) delay in presenting display - frames. Presentation timestamp comes from the driver when showing a display - frame on screen. There are times when the presentation can be delayed a long - time. This metric reports how often this can happen. This is reported only - if the presentation takes 3 minutes or more. - </summary> -</histogram> - <histogram name="Graphics.Smoothness.95pctPercentDroppedFrames_1sWindow" units="%" expires_after="2022-10-23"> <owner>behdadb@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/cross_device/OWNERS b/tools/metrics/histograms/metadata/cross_device/OWNERS index 6badf6b..6f87ca4 100644 --- a/tools/metrics/histograms/metadata/cross_device/OWNERS +++ b/tools/metrics/histograms/metadata/cross_device/OWNERS
@@ -3,4 +3,3 @@ # Prefer sending CLs to the owners listed below. # Use chromium-metrics-reviews@google.com as a backup. crisrael@google.com -nohle@chromium.org
diff --git a/tools/metrics/histograms/metadata/cross_device/histograms.xml b/tools/metrics/histograms/metadata/cross_device/histograms.xml index 6bf88a7..079ed583 100644 --- a/tools/metrics/histograms/metadata/cross_device/histograms.xml +++ b/tools/metrics/histograms/metadata/cross_device/histograms.xml
@@ -134,7 +134,7 @@ <histogram name="CryptAuth.ClientAppMetadata.IsManufacturerEmpty" enum="BooleanEmpty" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>hansenmichael@google.com</owner> <owner>better-together-dev@google.com</owner> <summary> Indicates whether or not the device manufacturer name returned in the @@ -145,7 +145,7 @@ <histogram name="CryptAuth.ClientAppMetadata.IsModelEmpty" enum="BooleanEmpty" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>hansenmichael@google.com</owner> <owner>better-together-dev@google.com</owner> <summary> Indicates whether or not the device model name returned in the hardware info @@ -155,7 +155,7 @@ <histogram name="CryptAuth.ClientAppMetadataInstanceIdTokenFetch.Result" enum="InstanceIDResult" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>hansenmichael@google.com</owner> <owner>better-together-dev@google.com</owner> <summary> Indicates the result of fetching an InstanceId token during the construction @@ -165,7 +165,7 @@ <histogram name="CryptAuth.ClientAppMetadataInstanceIdTokenFetch.Retries" units="retries" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>hansenmichael@google.com</owner> <owner>better-together-dev@google.com</owner> <summary> Counts the number of times the InstanceId token fetch got retried because it @@ -184,7 +184,7 @@ <histogram name="CryptAuth.DeviceSyncService.ClientAppMetadataFetch.AsyncTaskResult" enum="CryptAuthAsyncTaskResult" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>hansberry@chromium.org</owner> <owner>better-together-dev@google.com</owner> <summary> Records the result of the async ClientAppMetadata retrieval performed during @@ -196,7 +196,7 @@ <histogram name="CryptAuth.DeviceSyncService.ClientAppMetadataFetch.ExecutionTime" units="ms" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>hansberry@chromium.org</owner> <owner>better-together-dev@google.com</owner> <summary> Records the execution time of the async ClientAppMetadata retrieval @@ -207,7 +207,7 @@ <histogram name="CryptAuth.DeviceSyncService.GcmRegistration.ExecutionTime" units="ms" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>hansberry@chromium.org</owner> <owner>better-together-dev@google.com</owner> <summary> Records the execution time of GCM registration performed during @@ -218,7 +218,7 @@ <histogram name="CryptAuth.DeviceSyncService.GcmRegistration.Success" enum="BooleanSuccess" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>hansberry@chromium.org</owner> <owner>better-together-dev@google.com</owner> <summary> Records the result of GCM registration performed during initialization of @@ -228,7 +228,7 @@ <histogram name="CryptAuth.DeviceSyncService.Initialization.ExecutionTime" units="ms" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>hansberry@chromium.org</owner> <owner>better-together-dev@google.com</owner> <summary> Records the execution time of the DeviceSync service initialization. @@ -264,7 +264,7 @@ <histogram name="CryptAuth.DeviceSyncV2.DeviceActivityGetter.ApiCallResult.GetDevicesActivityStatus" enum="CryptAuthApiCallResult" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>hansberry@chromium.org</owner> <owner>better-together-dev@google.com</owner> <summary> Records the result of the async GetDevicesActivityStatus API call to @@ -276,7 +276,7 @@ <histogram name="CryptAuth.DeviceSyncV2.DeviceActivityGetter.ExecutionTime.GetDevicesActivityStatus" units="ms" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>hansberry@chromium.org</owner> <owner>better-together-dev@google.com</owner> <summary> Records the execution time of the async GetDevicesActivityStatus API call @@ -288,7 +288,7 @@ <histogram name="CryptAuth.DeviceSyncV2.DeviceNotifier.ApiCallResult.NotifyGroupDevices" enum="CryptAuthApiCallResult" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>hansberry@chromium.org</owner> <owner>better-together-dev@google.com</owner> <summary> Records the result of the async BatchNotifyGroupDevices API call to @@ -300,7 +300,7 @@ <histogram name="CryptAuth.DeviceSyncV2.DeviceNotifier.ExecutionTime.NotifyGroupDevices" units="ms" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>hansberry@chromium.org</owner> <owner>better-together-dev@google.com</owner> <summary> Records the execution time of the async BatchNotifyGroupDevices API call to @@ -312,7 +312,7 @@ <histogram name="CryptAuth.DeviceSyncV2.DeviceSyncer.AsyncTaskResult.DeviceMetadataDecryption" enum="CryptAuthAsyncTaskResult" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>hansberry@chromium.org</owner> <owner>better-together-dev@google.com</owner> <summary> Records the result of the async device metadata decryption during the @@ -327,7 +327,7 @@ <histogram name="CryptAuth.DeviceSyncV2.DeviceSyncer.AsyncTaskResult.GroupPrivateKeyDecryption" enum="CryptAuthAsyncTaskResult" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>hansberry@chromium.org</owner> <owner>better-together-dev@google.com</owner> <summary> Records the result of the async group private key decryption during the @@ -339,7 +339,7 @@ <histogram name="CryptAuth.DeviceSyncV2.DeviceSyncer.ExecutionTime.DeviceMetadataDecryption" units="ms" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>hansberry@chromium.org</owner> <owner>better-together-dev@google.com</owner> <summary> Records the execution time of the async device metadata decryption during @@ -351,7 +351,7 @@ <histogram name="CryptAuth.DeviceSyncV2.DeviceSyncer.ExecutionTime.GroupPrivateKeyDecryption" units="ms" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>hansberry@chromium.org</owner> <owner>better-together-dev@google.com</owner> <summary> Records the execution time of the async group private key decryption during @@ -363,7 +363,7 @@ <histogram name="CryptAuth.DeviceSyncV2.DeviceSyncer.IsGroupPrivateKeyConsistent" enum="BooleanConsistent" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>hansberry@chromium.org</owner> <owner>better-together-dev@google.com</owner> <summary> Records whether or not the group private key sent by CryptAuth matches the @@ -375,7 +375,7 @@ <histogram name="CryptAuth.DeviceSyncV2.DeviceSyncer.IsLocalDeviceMetadataConsistent" enum="BooleanConsistent" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>hansberry@chromium.org</owner> <owner>better-together-dev@google.com</owner> <summary> Records whether or not the local device metadata agrees with that sent by @@ -386,7 +386,7 @@ <histogram name="CryptAuth.DeviceSyncV2.DeviceSyncer.MetadataDecryptionSuccess" enum="BooleanSuccess" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>hansberry@chromium.org</owner> <owner>better-together-dev@google.com</owner> <summary> Records whether or not device metadata sent by CryptAuth in the SyncMetadata @@ -397,7 +397,7 @@ <histogram name="CryptAuth.DeviceSyncV2.DeviceSyncer.MetadataParsingSuccess" enum="BooleanSuccess" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>hansberry@chromium.org</owner> <owner>better-together-dev@google.com</owner> <summary> Records whether or not device metadata sent by CryptAuth in the SyncMetadata @@ -410,7 +410,7 @@ <histogram name="CryptAuth.DeviceSyncV2.FeatureStatusGetter.ApiCallResult.GetFeatureStatuses" enum="CryptAuthApiCallResult" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>hansberry@chromium.org</owner> <owner>better-together-dev@google.com</owner> <summary> Records the result of the async BatchGetFeatureStatuses API call during the @@ -422,7 +422,7 @@ <histogram name="CryptAuth.DeviceSyncV2.FeatureStatusGetter.CorrectNumberOfDevicesInResponse" enum="BooleanExpected" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>hansberry@chromium.org</owner> <owner>better-together-dev@google.com</owner> <summary> Records whether or not the expected number of devices were sent in the @@ -434,7 +434,7 @@ <histogram name="CryptAuth.DeviceSyncV2.FeatureStatusGetter.ExecutionTime.GetFeatureStatuses" units="ms" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>hansberry@chromium.org</owner> <owner>better-together-dev@google.com</owner> <summary> Records the execution time of the async BatchGetFeatureStatuses API call @@ -446,7 +446,7 @@ <histogram name="CryptAuth.DeviceSyncV2.FeatureStatusGetter.IsDuplicateDeviceId" enum="BooleanDuplicate" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>hansberry@chromium.org</owner> <owner>better-together-dev@google.com</owner> <summary> Records whether or not a device ID in the BatchGetFeatureStatuses response @@ -458,7 +458,7 @@ <histogram name="CryptAuth.DeviceSyncV2.FeatureStatusGetter.IsKnownFeatureType" enum="BooleanKnown" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>hansberry@chromium.org</owner> <owner>better-together-dev@google.com</owner> <summary> Records whether or not the feature type strings returned in the @@ -472,7 +472,7 @@ <histogram name="CryptAuth.DeviceSyncV2.FeatureStatusGetter.IsUnsupportedFeatureMarkedEnabled" enum="Boolean" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>hansberry@chromium.org</owner> <owner>better-together-dev@google.com</owner> <summary> Records whether or not a feature is marked as enabled even though it is @@ -485,7 +485,7 @@ <histogram name="CryptAuth.DeviceSyncV2.FeatureStatusGetter.WasDeviceInResponseRequested" enum="BooleanRequested" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>hansberry@chromium.org</owner> <owner>better-together-dev@google.com</owner> <summary> Records whether or not a device in the BatchGetFeatureStatuses response was @@ -497,7 +497,7 @@ <histogram name="CryptAuth.DeviceSyncV2.FeatureStatusSetter.ApiCallResult.SetFeatureStatuses" enum="CryptAuthApiCallResult" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>hansberry@chromium.org</owner> <owner>better-together-dev@google.com</owner> <summary> Records the result of the async BatchSetFeatureStatuses API call to @@ -509,7 +509,7 @@ <histogram name="CryptAuth.DeviceSyncV2.FeatureStatusSetter.ExecutionTime.SetFeatureStatuses" units="ms" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>hansberry@chromium.org</owner> <owner>better-together-dev@google.com</owner> <summary> Records the execution time of the async BatchSetFeatureStatuses API call to @@ -521,7 +521,7 @@ <histogram name="CryptAuth.DeviceSyncV2.GroupPrivateKeySharer.ApiCallResult.ShareGroupPrivateKey" enum="CryptAuthApiCallResult" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>hansberry@chromium.org</owner> <owner>better-together-dev@google.com</owner> <summary> Records the result of the async ShareGroupPrivateKey API call during the @@ -533,7 +533,7 @@ <histogram name="CryptAuth.DeviceSyncV2.GroupPrivateKeySharer.AsyncTaskResult.GroupPrivateKeyEncryption" enum="CryptAuthAsyncTaskResult" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>hansberry@chromium.org</owner> <owner>better-together-dev@google.com</owner> <summary> Records the result of the async group private key encryption during the @@ -548,7 +548,7 @@ <histogram name="CryptAuth.DeviceSyncV2.GroupPrivateKeySharer.EncryptionSuccess" enum="BooleanSuccess" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>hansberry@chromium.org</owner> <owner>better-together-dev@google.com</owner> <summary> Records whether or not the group private key was able to be encrypted using @@ -561,7 +561,7 @@ <histogram name="CryptAuth.DeviceSyncV2.GroupPrivateKeySharer.ExecutionTime.GroupPrivateKeyEncryption" units="ms" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>hansberry@chromium.org</owner> <owner>better-together-dev@google.com</owner> <summary> Records the execution time of the async group private key encryption during @@ -573,7 +573,7 @@ <histogram name="CryptAuth.DeviceSyncV2.GroupPrivateKeySharer.ExecutionTime.ShareGroupPrivateKey" units="ms" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>hansberry@chromium.org</owner> <owner>better-together-dev@google.com</owner> <summary> Records the execution time of the async ShareGroupPrivateKey API call during @@ -585,7 +585,7 @@ <histogram name="CryptAuth.DeviceSyncV2.GroupPrivateKeySharer.IsEncryptingKeyEmpty" enum="Boolean" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>hansberry@chromium.org</owner> <owner>better-together-dev@google.com</owner> <summary> Records whether or not a device's key used to encrypt the group private key @@ -598,7 +598,7 @@ <histogram name="CryptAuth.DeviceSyncV2.InvocationReason" enum="CryptAuthV2InvocationReason" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>hansberry@chromium.org</owner> <owner>better-together-dev@google.com</owner> <summary> The clients' reasons for making CryptAuth v2 DeviceSync requests. Recorded @@ -609,7 +609,7 @@ <histogram name="CryptAuth.DeviceSyncV2.MetadataSyncer.ApiCallResult.FirstSyncMetadata" enum="CryptAuthApiCallResult" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>hansberry@chromium.org</owner> <owner>better-together-dev@google.com</owner> <summary> Records the result of the first async SyncMetadata API call during the @@ -621,7 +621,7 @@ <histogram name="CryptAuth.DeviceSyncV2.MetadataSyncer.ApiCallResult.SecondSyncMetadata" enum="CryptAuthApiCallResult" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>hansberry@chromium.org</owner> <owner>better-together-dev@google.com</owner> <summary> Records the result of the second async SyncMetadata API call during the @@ -633,7 +633,7 @@ <histogram name="CryptAuth.DeviceSyncV2.MetadataSyncer.AsyncTaskResult.GroupKeyCreation" enum="CryptAuthAsyncTaskResult" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>hansberry@chromium.org</owner> <owner>better-together-dev@google.com</owner> <summary> Records the result of the async group key creation during the CryptAuth v2 @@ -645,7 +645,7 @@ <histogram name="CryptAuth.DeviceSyncV2.MetadataSyncer.AsyncTaskResult.LocalDeviceMetadataEncryption" enum="CryptAuthAsyncTaskResult" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>hansberry@chromium.org</owner> <owner>better-together-dev@google.com</owner> <summary> Records the result of the async local device metadata encryption during the @@ -657,7 +657,7 @@ <histogram name="CryptAuth.DeviceSyncV2.MetadataSyncer.ExecutionTime.FirstSyncMetadata" units="ms" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>hansberry@chromium.org</owner> <owner>better-together-dev@google.com</owner> <summary> Records the execution time of the first async SyncMetadata API call during @@ -669,7 +669,7 @@ <histogram name="CryptAuth.DeviceSyncV2.MetadataSyncer.ExecutionTime.GroupKeyCreation" units="ms" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>hansberry@chromium.org</owner> <owner>better-together-dev@google.com</owner> <summary> Records the execution time of the async group key creation during the @@ -681,7 +681,7 @@ <histogram name="CryptAuth.DeviceSyncV2.MetadataSyncer.ExecutionTime.LocalDeviceMetadataEncryption" units="ms" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>hansberry@chromium.org</owner> <owner>better-together-dev@google.com</owner> <summary> Records the execution time of the async local device metadata encryption @@ -693,7 +693,7 @@ <histogram name="CryptAuth.DeviceSyncV2.MetadataSyncer.ExecutionTime.SecondSyncMetadata" units="ms" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>hansberry@chromium.org</owner> <owner>better-together-dev@google.com</owner> <summary> Records the execution time of the second async SyncMetadata API call during @@ -705,7 +705,7 @@ <histogram name="CryptAuth.DeviceSyncV2.MetadataSyncer.IsDeviceMetadataPacketValid" enum="Boolean" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>hansberry@chromium.org</owner> <owner>better-together-dev@google.com</owner> <summary> Records whether or not the device metadata packets sent by CryptAuth have @@ -716,7 +716,7 @@ <histogram name="CryptAuth.DeviceSyncV2.MetadataSyncer.IsDuplicateDeviceId" enum="Boolean" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>hansberry@chromium.org</owner> <owner>better-together-dev@google.com</owner> <summary> Records whether or not the device ID from a device metadata packet sent by @@ -728,7 +728,7 @@ <histogram name="CryptAuth.DeviceSyncV2.RemoteDeviceProvider.NumV1Devices" units="count" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>hansberry@chromium.org</owner> <owner>better-together-dev@google.com</owner> <summary> Records the number of devices returned by a v1 DeviceSync. Only recorded @@ -740,7 +740,7 @@ <histogram name="CryptAuth.DeviceSyncV2.RemoteDeviceProvider.NumV2Devices" units="count" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>hansberry@chromium.org</owner> <owner>better-together-dev@google.com</owner> <summary> Records the number of devices returned by a v2 DeviceSync. Only recorded @@ -753,7 +753,7 @@ <histogram name="CryptAuth.DeviceSyncV2.RemoteDeviceProvider.PercentageOfV1DevicesReplacedByV2Devices" units="%" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>hansberry@chromium.org</owner> <owner>better-together-dev@google.com</owner> <summary> Records the percentage of devices returned by a v2 DeviceSync that replace @@ -768,7 +768,7 @@ <histogram name="CryptAuth.DeviceSyncV2.RemoteDeviceProvider.PercentageOfV2DevicesWithDecryptedPublicKey" units="%" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>hansberry@chromium.org</owner> <owner>better-together-dev@google.com</owner> <summary> Records the percentage of devices returned by a v2 DeviceSync that have a @@ -782,7 +782,7 @@ <histogram name="CryptAuth.DeviceSyncV2.RemoteDeviceProvider.RatioOfV2ToV1Devices" units="%" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>hansberry@chromium.org</owner> <owner>better-together-dev@google.com</owner> <summary> Records the ratio of the number of devices returned by a v2 DeviceSync to @@ -799,7 +799,7 @@ <histogram name="CryptAuth.DeviceSyncV2.Result.DidDeviceRegistryChange" enum="BooleanChanged" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>hansberry@chromium.org</owner> <owner>better-together-dev@google.com</owner> <summary> Indicates whether or not any device data in the device registry changed as a @@ -810,7 +810,7 @@ <histogram name="CryptAuth.DeviceSyncV2.Result.ResultCode" enum="CryptAuthV2DeviceSyncResultCode" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>hansberry@chromium.org</owner> <owner>better-together-dev@google.com</owner> <summary> The CryptAuth v2 DeviceSync attempt results broken down by result code. @@ -820,7 +820,7 @@ <histogram name="CryptAuth.DeviceSyncV2.Result.ResultType" enum="CryptAuthV2DeviceSyncResultType" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>hansberry@chromium.org</owner> <owner>better-together-dev@google.com</owner> <summary> Indicates whether the CryptAuth v2 DeviceSync attempt succeeded, failed, or @@ -830,7 +830,7 @@ <histogram name="CryptAuth.Enrollment.Result" enum="BooleanSuccess" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>hansberry@chromium.org</owner> <owner>better-together-dev@google.com</owner> <summary> The top-level result of the CryptAuth device enrollment process. @@ -839,7 +839,7 @@ <histogram name="CryptAuth.EnrollmentV2.ApiCallResult.EnrollKeys" enum="CryptAuthApiCallResult" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>hansberry@chromium.org</owner> <owner>better-together-dev@google.com</owner> <summary> Records the result of the async EnrollKeys API call to CryptAuth during the @@ -850,7 +850,7 @@ <histogram name="CryptAuth.EnrollmentV2.ApiCallResult.SyncKeys" enum="CryptAuthApiCallResult" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>hansberry@chromium.org</owner> <owner>better-together-dev@google.com</owner> <summary> Records the result of the async SyncKeys API call to CryptAuth during the @@ -861,7 +861,7 @@ <histogram name="CryptAuth.EnrollmentV2.AsyncTaskResult.KeyCreation" enum="CryptAuthAsyncTaskResult" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>hansberry@chromium.org</owner> <owner>better-together-dev@google.com</owner> <summary> Records the result of the async key creation call during the CryptAuth v2 @@ -872,7 +872,7 @@ <histogram name="CryptAuth.EnrollmentV2.ExecutionTime.EnrollKeys" units="ms" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>hansberry@chromium.org</owner> <owner>better-together-dev@google.com</owner> <summary> Records the execution time of the async EnrollKeys API call to CryptAuth @@ -883,7 +883,7 @@ <histogram name="CryptAuth.EnrollmentV2.ExecutionTime.KeyCreation" units="ms" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>hansberry@chromium.org</owner> <owner>better-together-dev@google.com</owner> <summary> Records the execution time of the async key creation call during the @@ -894,7 +894,7 @@ <histogram name="CryptAuth.EnrollmentV2.ExecutionTime.SyncKeys" units="ms" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>hansberry@chromium.org</owner> <owner>better-together-dev@google.com</owner> <summary> Records the execution time of the async SyncKeys API call to CryptAuth @@ -905,7 +905,7 @@ <histogram name="CryptAuth.EnrollmentV2.InvocationReason" enum="CryptAuthV2InvocationReason" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>hansberry@chromium.org</owner> <owner>better-together-dev@google.com</owner> <summary> The clients' reasons for making CryptAuth v2 Enrollment requests. Recorded @@ -915,7 +915,7 @@ <histogram name="CryptAuth.EnrollmentV2.Result.ResultCode" enum="CryptAuthV2EnrollmentResult" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>hansberry@chromium.org</owner> <owner>better-together-dev@google.com</owner> <summary> The CryptAuth v2 Enrollment attempt results broken down by result code. @@ -925,7 +925,7 @@ <histogram name="CryptAuth.EnrollmentV2.Result.Success" enum="BooleanSuccess" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>hansberry@chromium.org</owner> <owner>better-together-dev@google.com</owner> <summary> The success or failure of CryptAuth v2 Enrollment requests. Recorded when an @@ -935,7 +935,7 @@ <histogram name="CryptAuth.EnrollmentV2.UserKeyPairState" enum="CryptAuthV2EnrollmentUserKeyPairState" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>hansberry@chromium.org</owner> <owner>better-together-dev@google.com</owner> <summary> The state--at system start time--of persisted user key pairs, generated @@ -946,7 +946,7 @@ <histogram name="CryptAuth.Gcm.Message.FeatureType" enum="CryptAuthFeatureType" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>hansberry@chromium.org</owner> <owner>better-together-dev@google.com</owner> <summary> Records the multi-device feature type sent in a GCM message from CryptAuth @@ -957,7 +957,7 @@ <histogram name="CryptAuth.Gcm.Message.IsDeviceSyncGroupNameValid" enum="Boolean" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>hansberry@chromium.org</owner> <owner>better-together-dev@google.com</owner> <summary> Records whether or not the DeviceSync group name sent in a GCM message from @@ -969,7 +969,7 @@ <histogram name="CryptAuth.Gcm.Message.IsKnownFeatureType" enum="Boolean" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>hansberry@chromium.org</owner> <owner>better-together-dev@google.com</owner> <summary> Records whether or not the multi-device feature type sent in a GCM message @@ -981,7 +981,7 @@ <histogram name="CryptAuth.Gcm.Message.TargetService.AreTickleTypeAndTargetServiceBothSpecified" enum="Boolean" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>hansberry@chromium.org</owner> <owner>better-together-dev@google.com</owner> <summary> Records whether or not the CryptAuth TargetService was specified in a GCM @@ -995,7 +995,7 @@ <histogram name="CryptAuth.Gcm.Message.TargetService.FromRegistrationTickleType" enum="CryptAuthTargetService" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>hansberry@chromium.org</owner> <owner>better-together-dev@google.com</owner> <summary> The CryptAuth TargetService--Enrollment or DeviceSync--indicated by the @@ -1007,7 +1007,7 @@ <histogram name="CryptAuth.Gcm.Message.TargetService.FromTargetServiceValue" enum="CryptAuthTargetService" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>hansberry@chromium.org</owner> <owner>better-together-dev@google.com</owner> <summary> The CryptAuth TargetService--Enrollment or DeviceSync--indicated by the @@ -1019,7 +1019,7 @@ <histogram name="CryptAuth.Gcm.Registration.AttemptTimeWithRetries" units="ms" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>hansberry@chromium.org</owner> <owner>better-together-dev@google.com</owner> <summary> The time it takes the GCM driver to return the result of a GCM registration @@ -1035,7 +1035,7 @@ <histogram name="CryptAuth.Gcm.Registration.Result" enum="GCMClientResult" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>hansberry@chromium.org</owner> <owner>better-together-dev@google.com</owner> <summary> The result code from a GCM registration request made by the CryptAuth GCM @@ -1051,7 +1051,7 @@ <histogram name="CryptAuth.GetLocalDeviceMetadata.HasId" enum="BooleanPresent" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>hansberry@chromium.org</owner> <owner>better-together-dev@google.com</owner> <summary> Records whether or not the loaded local-device has a non-trivial identifier. @@ -1063,7 +1063,7 @@ <histogram name="CryptAuth.GetLocalDeviceMetadata.IsReady" enum="BooleanReady" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>hansberry@chromium.org</owner> <owner>better-together-dev@google.com</owner> <summary> Records whether or not the DeviceSync client is fully set up/ready when @@ -1076,7 +1076,7 @@ <histogram name="CryptAuth.GetLocalDeviceMetadata.Result" enum="BooleanSuccess" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>hansberry@chromium.org</owner> <owner>better-together-dev@google.com</owner> <summary> Records whether or not the local-device-data request to the DeviceSync @@ -1087,7 +1087,7 @@ <histogram name="CryptAuth.InstanceId.DidInstanceIdChange" enum="BooleanChanged" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>hansberry@chromium.org</owner> <owner>better-together-dev@google.com</owner> <summary> Indicates whether or not the Instance ID used by the CryptAuth client @@ -1098,7 +1098,7 @@ <histogram name="CryptAuth.InstanceId.DidInstanceIdTokenChange" enum="BooleanChanged" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>hansberry@chromium.org</owner> <owner>better-together-dev@google.com</owner> <summary> Indicates whether or not the Instance ID token used by the CryptAuth client @@ -1941,7 +1941,7 @@ <histogram name="MultiDevice.Setup.HasDuplicateEligibleHostDeviceNames" enum="BooleanDuplicate" expires_after="2023-02-01"> - <owner>nohle@google.com</owner> + <owner>hansenmichael@google.com</owner> <owner>better-together-dev@google.com</owner> <summary> Records whether or not the list of eligible host phones has duplicate @@ -1964,7 +1964,7 @@ <histogram name="MultiDevice.Setup.HostVerifier.DoesHostHaveCryptoData" enum="Boolean" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>hansenmichael@google.com</owner> <owner>better-together-dev@google.com</owner> <summary> Records if a multi-device host device has all of the crypto data necessary
diff --git a/tools/metrics/histograms/metadata/custom_tabs/histograms.xml b/tools/metrics/histograms/metadata/custom_tabs/histograms.xml index 398a225..4dc4368f 100644 --- a/tools/metrics/histograms/metadata/custom_tabs/histograms.xml +++ b/tools/metrics/histograms/metadata/custom_tabs/histograms.xml
@@ -258,7 +258,7 @@ </histogram> <histogram name="CustomTabs.ShareOptionLocation" enum="ShareOptionLocation" - expires_after="M105"> + expires_after="M109"> <owner>sophey@chromium.org</owner> <owner>src/components/send_tab_to_self/OWNERS</owner> <summary>
diff --git a/tools/metrics/histograms/metadata/nearby/OWNERS b/tools/metrics/histograms/metadata/nearby/OWNERS index 6badf6b..6f87ca4 100644 --- a/tools/metrics/histograms/metadata/nearby/OWNERS +++ b/tools/metrics/histograms/metadata/nearby/OWNERS
@@ -3,4 +3,3 @@ # Prefer sending CLs to the owners listed below. # Use chromium-metrics-reviews@google.com as a backup. crisrael@google.com -nohle@chromium.org
diff --git a/tools/metrics/histograms/metadata/nearby/histograms.xml b/tools/metrics/histograms/metadata/nearby/histograms.xml index 179c11e..0dd1811 100644 --- a/tools/metrics/histograms/metadata/nearby/histograms.xml +++ b/tools/metrics/histograms/metadata/nearby/histograms.xml
@@ -160,7 +160,7 @@ <histogram name="Nearby.Connections.InstantMessaging.{Direction}Express.OAuthTokenFetchResult" enum="BooleanSuccess" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>hansenmichael@google.com</owner> <owner>nearby-share-chromeos-eng@google.com</owner> <summary> Records whether or not the user was able to fetch the OAuth token necessary @@ -176,7 +176,7 @@ <histogram name="Nearby.Connections.InstantMessaging.{Direction}Express.Result" enum="BooleanSuccess" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>hansenmichael@google.com</owner> <owner>nearby-share-chromeos-eng@google.com</owner> <summary> Records whether an HTTPS call to {Direction} the Instant Messaging API @@ -191,7 +191,7 @@ <histogram name="Nearby.Connections.InstantMessaging.{Direction}Express.Result.FailureReason" enum="CombinedHttpResponseAndNetErrorCode" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>hansenmichael@google.com</owner> <owner>nearby-share-chromeos-eng@google.com</owner> <summary> Records the network error code or HTTPS response code from a failed HTTPS @@ -233,7 +233,7 @@ <histogram name="Nearby.Connections.WifiLan.ConnectResult" enum="NearbyConnectionsWifiLanConnectResult" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>hansenmichael@google.com</owner> <owner>nearby-share-chromeos-eng@google.com</owner> <summary> Records the result of trying to connect to another device's TCP server @@ -246,7 +246,7 @@ <histogram name="Nearby.Connections.WifiLan.ListenResult" enum="NearbyConnectionsWifiLanListenResult" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>hansenmichael@google.com</owner> <owner>nearby-share-chromeos-eng@google.com</owner> <summary> Records the result of listening for and accepting incoming TCP socket @@ -259,7 +259,7 @@ <histogram name="Nearby.Connections.WifiLan.Socket.AcceptResult" enum="NetErrorCodes" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>hansenmichael@google.com</owner> <owner>nearby-share-chromeos-eng@google.com</owner> <summary> Records the result of a TCP server socket accepting incoming connections. @@ -273,7 +273,7 @@ <histogram name="Nearby.Connections.WifiLan.TimeToConnect" units="ms" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>hansenmichael@google.com</owner> <owner>nearby-share-chromeos-eng@google.com</owner> <summary> Records the time it takes to successfully connect to another device's TCP @@ -394,7 +394,7 @@ <histogram name="Nearby.Share.Certificates.Manager.BluetoothMacAddressPresentForPrivateCertificateCreation" enum="BooleanPresent" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>pushi@google.com</owner> <owner>nearby-share-chromeos-eng@google.com</owner> <summary> Records whether or not a Bluetooth MAC address was able to be fetched during @@ -501,7 +501,7 @@ <histogram name="Nearby.Share.Certificates.Storage.InitializeSuccessDuration" units="ms" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>crisrael@google.com</owner> <owner>nearby-share-chromeos-eng@google.com</owner> <summary> Records the time necessary to successfully initialize the Nearby Share @@ -532,7 +532,7 @@ <histogram name="Nearby.Share.Connection.EstablishOutgoingConnection.Success" enum="BooleanSuccess" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>pushi@google.com</owner> <owner>nearby-share-chromeos-eng@google.com</owner> <summary> Records whether an attempt to establish a connection to a receiving device @@ -546,7 +546,7 @@ <histogram name="Nearby.Share.Connection.EstablishOutgoingConnectionStatus" enum="NearbyShareFinalStatus" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>crisrael@google.com</owner> <owner>nearby-share-chromeos-eng@google.com</owner> <summary> Records whether an attempt to establish a connection to a receiving device @@ -559,7 +559,7 @@ <histogram name="Nearby.Share.Connection.TimeToEstablishOutgoingConnection" units="ms" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>pushi@google.com</owner> <owner>nearby-share-chromeos-eng@google.com</owner> <summary> Records the time necessary to successfully establish a connection to a @@ -573,7 +573,7 @@ <histogram name="Nearby.Share.Contacts.CanGetProfileUserName" enum="BooleanSuccess" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>crisrael@google.com</owner> <owner>nearby-share-chromeos-eng@google.com</owner> <summary> Records whether or not we able to retrieve the profile user name--think @@ -588,7 +588,7 @@ <histogram name="Nearby.Share.Contacts.DownloadPageCount.{Result}" units="pages" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>pushi@google.com</owner> <owner>nearby-share-chromeos-eng@google.com</owner> <summary> Contacts are downloaded in pages, i.e., multiple ListContactPeople RPC @@ -608,7 +608,7 @@ <histogram name="Nearby.Share.Contacts.DownloadResult" enum="BooleanSuccess" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>crisrael@google.com</owner> <owner>nearby-share-chromeos-eng@google.com</owner> <summary> Records whether or not the full, possibly paginated contact download from @@ -622,7 +622,7 @@ <histogram name="Nearby.Share.Contacts.HttpResult" enum="NearbyShareHttpResult" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>pushi@google.com</owner> <owner>nearby-share-chromeos-eng@google.com</owner> <summary> Records the result of each ListContactPeople RPC call to the Nearby Share @@ -636,7 +636,7 @@ <histogram name="Nearby.Share.Contacts.NumContacts.{Type}" units="contacts" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>crisrael@google.com</owner> <owner>nearby-share-chromeos-eng@google.com</owner> <summary> Records the number of {Type} contacts downloaded from the Nearby server. @@ -659,7 +659,7 @@ <histogram name="Nearby.Share.Contacts.Percent{Type}" units="%" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>pushi@google.com</owner> <owner>nearby-share-chromeos-eng@google.com</owner> <summary> Records the percentage of {Type} Emitted every time contacts are downloaded @@ -682,7 +682,7 @@ <histogram name="Nearby.Share.Contacts.TimeToDownload.{Result}" units="ms" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>crisrael@google.com</owner> <owner>nearby-share-chromeos-eng@google.com</owner> <summary> Records the time spent {Result}. Emitted every time contacts are downloaded @@ -700,7 +700,7 @@ <histogram name="Nearby.Share.DeviceType{Direction}" enum="NearbyShareDeviceType" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>pushi@google.com</owner> <owner>nearby-share-chromeos-eng@google.com</owner> <summary> Records the device type that the user {Direction}. Emitted when a transfer @@ -716,7 +716,7 @@ <histogram name="Nearby.Share.Discovery.Delay.FromStartDiscoveryTo{EndState}" units="ms" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>crisrael@google.com</owner> <owner>nearby-share-chromeos-eng@google.com</owner> <summary> Records the delay between the start of the discovery session and {EndState}. @@ -731,7 +731,7 @@ <histogram name="Nearby.Share.Discovery.FurthestDiscoveryProgress" enum="NearbyShareDiscoveryProgress" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>pushi@google.com</owner> <owner>nearby-share-chromeos-eng@google.com</owner> <summary> Records the furthest state reached in a single Nearby Share discovery @@ -741,7 +741,7 @@ <histogram name="Nearby.Share.Discovery.LookUpSelectedShareTarget" enum="BooleanFound" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>crisrael@google.com</owner> <owner>nearby-share-chromeos-eng@google.com</owner> <summary> Records whether or not the selected share target on the Nearby Share @@ -753,7 +753,7 @@ <histogram name="Nearby.Share.Discovery.NumShareTargets.{Variation}" units="share targets" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>pushi@google.com</owner> <owner>nearby-share-chromeos-eng@google.com</owner> <summary> Records the number of share targets {Variation} in a single discovery @@ -768,7 +768,7 @@ <histogram name="Nearby.Share.Discovery.{Operation}" enum="NearbyShareServiceStatusCode" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>crisrael@google.com</owner> <owner>nearby-share-chromeos-eng@google.com</owner> <summary> Records the result of {Operation}. Emitted immediately after the operation @@ -787,7 +787,7 @@ <histogram name="Nearby.Share.Enabled" enum="NearbyShareEnabledState" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>pushi@google.com</owner> <owner>nearby-share-chromeos-eng@google.com</owner> <summary> Records if the user has the Nearby Share feature enabled, and if not, @@ -800,7 +800,7 @@ <histogram name="Nearby.Share.EnabledStateChanged" enum="BooleanEnabled" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>crisrael@google.com</owner> <owner>nearby-share-chromeos-eng@google.com</owner> <summary> Records the enabled/disabled state of Nearby Share. Emitted when the feature @@ -812,7 +812,7 @@ <histogram name="Nearby.Share.IsKnownContact" enum="BooleanKnown" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>pushi@google.com</owner> <owner>nearby-share-chromeos-eng@google.com</owner> <summary> Records whether or not the device shared with is a known contact, i.e., @@ -834,7 +834,7 @@ <histogram name="Nearby.Share.Medium.ChangedToMedium" enum="NearbyConnectionsMedium" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>crisrael@google.com</owner> <owner>nearby-share-chromeos-eng@google.com</owner> <summary> Records the data-transfer medium that the Nearby Connections library @@ -845,7 +845,7 @@ <histogram name="Nearby.Share.Medium.InitiateBandwidthUpgradeResult" enum="BooleanSuccess" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>pushi@google.com</owner> <owner>nearby-share-chromeos-eng@google.com</owner> <summary> Records the result of Nearby Share starting a bandwidth upgrade via the @@ -859,7 +859,7 @@ <histogram name="Nearby.Share.Medium.RequestedBandwidthUpgradeResult" enum="BooleanUpgraded" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>crisrael@google.com</owner> <owner>nearby-share-chromeos-eng@google.com</owner> <summary> Records if the transfer medium was successfully upgraded after an explicit @@ -915,7 +915,7 @@ <histogram name="Nearby.Share.Payload.AttachmentType{Variation}" enum="NearbyShareAttachmentType" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>pushi@google.com</owner> <owner>nearby-share-chromeos-eng@google.com</owner> <summary> Records the attachment types from {Variation}. Emitted after all attachment @@ -935,7 +935,7 @@ <histogram name="Nearby.Share.Payload.FinalStatus{UpgradedMedium}" enum="NearbyShareFinalStatus" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>crisrael@google.com</owner> <owner>nearby-share-chromeos-eng@google.com</owner> <summary> Records whether a payload transfer succeeded, failed, or was cancelled when @@ -954,7 +954,7 @@ <histogram name="Nearby.Share.Payload.Medium" enum="NearbyShareUpgradedMedium" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>pushi@google.com</owner> <owner>nearby-share-chromeos-eng@google.com</owner> <summary> Records if a payload transfer used an upgraded bandwidth medium, and if so, @@ -966,7 +966,7 @@ <histogram name="Nearby.Share.Payload.Medium.Over5MbTransferred{ShareTargetType}" enum="NearbyShareUpgradedMedium" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>crisrael@google.com</owner> <owner>nearby-share-chromeos-eng@google.com</owner> <summary> Records if a payload transfer with {ShareTargetType} used an upgraded @@ -985,7 +985,7 @@ <histogram name="Nearby.Share.Payload.NumAttachments{Type}" units="attachments" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>pushi@google.com</owner> <owner>nearby-share-chromeos-eng@google.com</owner> <summary> Records the number of {Type} attachments intended to be sent in a Nearby @@ -1001,7 +1001,7 @@ <histogram name="Nearby.Share.Payload.TotalSize{Variation}" units="KB" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>crisrael@google.com</owner> <owner>nearby-share-chromeos-eng@google.com</owner> <summary> Records the total payload size of a Nearby Share transfer {Variation}. @@ -1034,7 +1034,7 @@ <histogram name="Nearby.Share.Payload.TransferRate{Variation}" units="KB/s" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>pushi@google.com</owner> <owner>nearby-share-chromeos-eng@google.com</owner> <summary> Records the payload transfer rate of a Nearby Share transfer {Variation}. @@ -1067,7 +1067,7 @@ <histogram name="Nearby.Share.StartAdvertising.Result.FailureReason{Mode}" enum="NearbyShareStartAdvertisingFailureReason" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>crisrael@google.com</owner> <owner>nearby-share-chromeos-eng@google.com</owner> <summary> Records the reason Nearby Share advertising failed to start in {Mode} mode. @@ -1081,7 +1081,7 @@ <histogram name="Nearby.Share.StartAdvertising.Result{Mode}" enum="BooleanSuccess" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>pushi@google.com</owner> <owner>nearby-share-chromeos-eng@google.com</owner> <summary> Records the result of starting Nearby Share advertising in {Mode} mode. @@ -1095,7 +1095,7 @@ <histogram name="Nearby.Share.TimeFromInitiateSendToRemoteDeviceNotification" units="ms" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>crisrael@google.com</owner> <owner>nearby-share-chromeos-eng@google.com</owner> <summary> Records the time from initiating a send on a share target to sending the @@ -1110,7 +1110,7 @@ <histogram name="Nearby.Share.TimeFromLocalAcceptToTransferStart" units="ms" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>pushi@google.com</owner> <owner>nearby-share-chromeos-eng@google.com</owner> <summary> Records the time from accepting a share on the receiving device until the @@ -1122,7 +1122,7 @@ <histogram name="Nearby.Share.Transfer.FinalStatus{Variation}" enum="NearbyShareTransferFinalStatus" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>crisrael@google.com</owner> <owner>nearby-share-chromeos-eng@google.com</owner> <summary> Records the final status of a Nearby Share transfer {Variation}. Emitted @@ -1147,7 +1147,7 @@ <histogram name="Nearby.Share.Transfer.Success" enum="BooleanSuccess" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>pushi@google.com</owner> <owner>nearby-share-chromeos-eng@google.com</owner> <summary> Records the success/failure of a Nearby Share transfer. Emitted when a @@ -1159,7 +1159,7 @@ <histogram name="Nearby.Share.Transfer.Success.{Direction}.{ShareTargetType}.{ContactStatus}" enum="BooleanSuccess" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>crisrael@google.com</owner> <owner>nearby-share-chromeos-eng@google.com</owner> <summary> Records the success/failure of a Nearby Share {Direction} transfer with @@ -1185,7 +1185,7 @@ <histogram name="Nearby.Share.VisibilityChoice" enum="NearbyShareVisibility" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>pushi@google.com</owner> <owner>nearby-share-chromeos-eng@google.com</owner> <summary> Records the user's chosen degree of visibility to their contacts, selected
diff --git a/tools/metrics/histograms/metadata/net/histograms.xml b/tools/metrics/histograms/metadata/net/histograms.xml index 4216fc2..2132e51 100644 --- a/tools/metrics/histograms/metadata/net/histograms.xml +++ b/tools/metrics/histograms/metadata/net/histograms.xml
@@ -2066,7 +2066,7 @@ </histogram> <histogram name="Net.QuicChromiumClientStream.HandleOnCloseConnectionError" - enum="QuicErrorCodes" expires_after="2022-07-11"> + enum="QuicErrorCodes" expires_after="2023-05-11"> <owner>renjietang@chromium.org</owner> <owner>src/net/OWNERS</owner> <summary> @@ -2791,7 +2791,7 @@ </histogram> <histogram name="Net.QuicSession.ConnectionFlowControlBlocked" - enum="BooleanBlocked" expires_after="2022-07-11"> + enum="BooleanBlocked" expires_after="2023-05-11"> <owner>dschinazi@chromium.org</owner> <owner>src/net/quic/OWNERS</owner> <summary> @@ -3176,7 +3176,7 @@ </histogram> <histogram name="Net.QuicSession.MaxReordering" units="units" - expires_after="2022-07-11"> + expires_after="2023-05-11"> <owner>dschinazi@chromium.org</owner> <owner>src/net/quic/OWNERS</owner> <summary> @@ -3194,7 +3194,7 @@ </histogram> <histogram name="Net.QuicSession.MaxReorderingTimeLongRtt" units="%" - expires_after="2022-07-11"> + expires_after="2023-05-11"> <owner>dschinazi@chromium.org</owner> <owner>src/net/quic/OWNERS</owner> <summary> @@ -3204,7 +3204,7 @@ </histogram> <histogram name="Net.QuicSession.MigrateToSocketSuccess" enum="Boolean" - expires_after="2022-07-11"> + expires_after="2023-05-11"> <owner>renjietang@chromium.org</owner> <owner>src/net/quic/OWNERS</owner> <summary> @@ -3298,7 +3298,7 @@ </histogram> <histogram name="Net.QuicSession.NumTotalStreams" units="units" - expires_after="2022-07-11"> + expires_after="2023-05-11"> <owner>dschinazi@chromium.org</owner> <owner>src/net/quic/OWNERS</owner> <summary> @@ -3821,7 +3821,7 @@ </histogram> <histogram name="Net.QuicSession.StreamFlowControlBlocked" - enum="BooleanBlocked" expires_after="2022-07-11"> + enum="BooleanBlocked" expires_after="2023-05-11"> <owner>dschinazi@chromium.org</owner> <owner>src/net/quic/OWNERS</owner> <summary> @@ -4478,7 +4478,7 @@ </histogram> <histogram name="Net.SpdySession.GoAwayReceived" enum="Http2WireErrorCodes" - expires_after="2022-07-11"> + expires_after="2023-05-11"> <owner>bnc@chromium.org</owner> <owner>src/net/OWNERS</owner> <summary>
diff --git a/tools/metrics/histograms/metadata/phonehub/OWNERS b/tools/metrics/histograms/metadata/phonehub/OWNERS index 6badf6b..6f87ca4 100644 --- a/tools/metrics/histograms/metadata/phonehub/OWNERS +++ b/tools/metrics/histograms/metadata/phonehub/OWNERS
@@ -3,4 +3,3 @@ # Prefer sending CLs to the owners listed below. # Use chromium-metrics-reviews@google.com as a backup. crisrael@google.com -nohle@chromium.org
diff --git a/tools/metrics/histograms/metadata/phonehub/histograms.xml b/tools/metrics/histograms/metadata/phonehub/histograms.xml index 3fdd94a..bdf9e8b 100644 --- a/tools/metrics/histograms/metadata/phonehub/histograms.xml +++ b/tools/metrics/histograms/metadata/phonehub/histograms.xml
@@ -249,7 +249,7 @@ <histogram name="PhoneHub.NotificationAccessSetup.SuccessfulSetupDuration" units="ms" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>jonmann@chromium.org</owner> <owner>chromeos-cross-device-eng@google.com</owner> <summary> Records the time it takes for the user to successfully go through the @@ -262,7 +262,7 @@ <histogram name="PhoneHub.NotificationAccessSetup.{AllStatusesOrLastStatus}" enum="PhoneHubNotificationAccessSetupStatus" expires_after="2023-02-01"> - <owner>nohle@chromium.org</owner> + <owner>jonmann@chromium.org</owner> <owner>chromeos-cross-device-eng@google.com</owner> <summary> Records {AllStatusesOrLastStatus} the Chromebook UI that guides them through
diff --git a/tools/metrics/histograms/metadata/settings/histograms.xml b/tools/metrics/histograms/metadata/settings/histograms.xml index 8c9ccfb..e242928b 100644 --- a/tools/metrics/histograms/metadata/settings/histograms.xml +++ b/tools/metrics/histograms/metadata/settings/histograms.xml
@@ -59,6 +59,17 @@ </summary> </histogram> +<histogram name="Settings.DefaultBrowserFromSource" + enum="IOSDefaultBrowserPromoSource" expires_after="2023-05-23"> + <owner>djean@google.com</owner> + <owner>olivierrobin@google.com</owner> + <summary> + The source surface that requested displaying the default browser system + settings. Logged when the user actually opens the iOS system settings (IOS + only). + </summary> +</histogram> + <histogram name="Settings.FilterOnLoadTime" units="ms" expires_after="2018-08-30"> <owner>gab@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/startup/histograms.xml b/tools/metrics/histograms/metadata/startup/histograms.xml index d764d31..1d0ae66 100644 --- a/tools/metrics/histograms/metadata/startup/histograms.xml +++ b/tools/metrics/histograms/metadata/startup/histograms.xml
@@ -852,6 +852,17 @@ </summary> </histogram> +<histogram name="Startup.ShowDefaultPromoFromApps" + enum="MobileSessionCallerApp" expires_after="2023-05-23"> + <owner>djean@chromium.org</owner> + <owner>olivierrobin@chromium.org</owner> + <summary> + The external application requesting showing the default browser settings. + Logged when the command is received from the external application. Histogram + only for iOS. + </summary> +</histogram> + <histogram name="Startup.StartupBrowserCreator_Start" units="ms" expires_after="M77"> <owner>rkaplow@chromium.org</owner>
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json index f967681e..bf9ad6c 100644 --- a/tools/perf/core/perfetto_binary_roller/binary_deps.json +++ b/tools/perf/core/perfetto_binary_roller/binary_deps.json
@@ -6,15 +6,15 @@ }, "win": { "hash": "362305e5177cc8f1a2938840b6ddcc86544ff0fb", - "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/c720c42be93f7497119755c639166715fd0726bb/trace_processor_shell.exe" + "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/40eee45831024f09bd55fdbe57a0e13718b9f7e7/trace_processor_shell.exe" }, "linux_arm": { "hash": "58893933be305d3bfe0a72ebebcacde2ac3ca893", "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux_arm/49b4b5dcbc312d8d2c3751cf29238b8efeb4e494/trace_processor_shell" }, "mac": { - "hash": "c596583f38124f15f97db8fef7b9a71dded377b5", - "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/mac/feeb5c1043fc9fe022b6c720aae568fbac7b0f1a/trace_processor_shell" + "hash": "39700253dea36c27987c8776d997fdff6c081bf0", + "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/mac/c720c42be93f7497119755c639166715fd0726bb/trace_processor_shell" }, "mac_arm64": { "hash": "e1ad4861384b06d911a65f035317914b8cc975c6", @@ -22,7 +22,7 @@ }, "linux": { "hash": "ec0b651002a120069aead56ce8729e63ed0a0b27", - "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/c720c42be93f7497119755c639166715fd0726bb/trace_processor_shell" + "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/40eee45831024f09bd55fdbe57a0e13718b9f7e7/trace_processor_shell" } }, "power_profile.sql": {
diff --git a/ui/accessibility/platform/ax_platform_node_auralinux.cc b/ui/accessibility/platform/ax_platform_node_auralinux.cc index 981ab44..fe163d5 100644 --- a/ui/accessibility/platform/ax_platform_node_auralinux.cc +++ b/ui/accessibility/platform/ax_platform_node_auralinux.cc
@@ -1018,7 +1018,7 @@ offset = obj->UnicodeToUTF16OffsetInText(offset); int32_t limited_offset = base::clamp(offset, 0, text_length); - uint32_t code_point; + base_icu::UChar32 code_point; base::ReadUnicodeCharacter(text.c_str(), text_length + 1, &limited_offset, &code_point); return code_point; @@ -4333,7 +4333,7 @@ std::u16string text = GetHypertext(); int32_t text_length = text.size(); for (int32_t i = 0; i < text_length; i++) { - uint32_t code_point; + base_icu::UChar32 code_point; size_t original_i = i; base::ReadUnicodeCharacter(text.c_str(), text_length + 1, &i, &code_point);
diff --git a/ui/chromeos/events/event_rewriter_chromeos.cc b/ui/chromeos/events/event_rewriter_chromeos.cc index 2494069..73127b0 100644 --- a/ui/chromeos/events/event_rewriter_chromeos.cc +++ b/ui/chromeos/events/event_rewriter_chromeos.cc
@@ -493,8 +493,8 @@ return EventRewriterChromeOS::kDeviceHotrodRemote; } - if (base::LowerCaseEqualsASCII(keyboard_device.name, - "virtual core keyboard")) { + if (base::EqualsCaseInsensitiveASCII(keyboard_device.name, + "virtual core keyboard")) { VLOG(1) << "Xorg virtual '" << keyboard_device.name << "' connected: id=" << keyboard_device.id; return EventRewriterChromeOS::kDeviceVirtualCoreKeyboard; @@ -523,9 +523,10 @@ bool found_apple = false; bool found_keyboard = false; for (size_t i = 0; i < tokens.size(); ++i) { - if (!found_apple && base::LowerCaseEqualsASCII(tokens[i], "apple")) + if (!found_apple && base::EqualsCaseInsensitiveASCII(tokens[i], "apple")) found_apple = true; - if (!found_keyboard && base::LowerCaseEqualsASCII(tokens[i], "keyboard")) + if (!found_keyboard && + base::EqualsCaseInsensitiveASCII(tokens[i], "keyboard")) found_keyboard = true; } if (found_apple) {
diff --git a/ui/events/keycodes/dom/keycode_converter.cc b/ui/events/keycodes/dom/keycode_converter.cc index a8a1615e..bdfb307 100644 --- a/ui/events/keycodes/dom/keycode_converter.cc +++ b/ui/events/keycodes/dom/keycode_converter.cc
@@ -316,7 +316,7 @@ // Otherwise, if the string contains a single Unicode character, // the key value is that character. int32_t char_index = 0; - uint32_t character; + base_icu::UChar32 character; if (base::ReadUnicodeCharacter(key.c_str(), static_cast<int32_t>(key.length()), &char_index, &character) &&
diff --git a/ui/file_manager/file_manager/foreground/elements/files_tooltip_unittest.m.js b/ui/file_manager/file_manager/foreground/elements/files_tooltip_unittest.m.js index 5e89f82..6569f76a 100644 --- a/ui/file_manager/file_manager/foreground/elements/files_tooltip_unittest.m.js +++ b/ui/file_manager/file_manager/foreground/elements/files_tooltip_unittest.m.js
@@ -246,7 +246,11 @@ assertEquals('card-tooltip', tooltip.className); assertEquals('card-label', label.className); - assertEquals('962px', tooltip.style.left); + // A border with 2px insets (top=bottom=left=right=2px) will be + // applied to the window when drak/light feature is enabled. See + // more details at crrev.com/c/3656414. + assertTrue( + `962px` == tooltip.style.left || `958px` == tooltip.style.left); assertEquals('162px', tooltip.style.top); cheeseButton.dispatchEvent(new MouseEvent('mouseout'));
diff --git a/ui/file_manager/file_manager/foreground/js/ui/breadcrumb.js b/ui/file_manager/file_manager/foreground/js/ui/breadcrumb.js index 597f049..d294604 100644 --- a/ui/file_manager/file_manager/foreground/js/ui/breadcrumb.js +++ b/ui/file_manager/file_manager/foreground/js/ui/breadcrumb.js
@@ -215,7 +215,7 @@ return; } - const element = event.path[0]; + const element = event.composedPath()[0]; if (element.hasAttribute('elider')) { this.toggleMenu_(); return;
diff --git a/ui/gl/delegated_ink_point_renderer_gpu_unittest.cc b/ui/gl/delegated_ink_point_renderer_gpu_unittest.cc index a1eaa516..12d4fe0 100644 --- a/ui/gl/delegated_ink_point_renderer_gpu_unittest.cc +++ b/ui/gl/delegated_ink_point_renderer_gpu_unittest.cc
@@ -97,8 +97,8 @@ protected: void SetUp() override { // Without this, the following check always fails. - gl::init::InitializeGLNoExtensionsOneOff(/*init_bindings=*/true, - /*system_device_id=*/0); + display_ = gl::init::InitializeGLNoExtensionsOneOff(/*init_bindings=*/true, + /*system_device_id=*/0); if (!gl::DirectCompositionSurfaceWin::GetDirectCompositionDevice()) { LOG(WARNING) << "GL implementation not using DirectComposition, skipping test."; @@ -124,7 +124,7 @@ context_ = nullptr; if (surface_) DestroySurface(std::move(surface_)); - gl::init::ShutdownGL(false); + gl::init::ShutdownGL(display_, false); } private: @@ -164,6 +164,7 @@ HWND parent_window_; scoped_refptr<DirectCompositionSurfaceWin> surface_; scoped_refptr<GLContext> context_; + GLDisplay* display_ = nullptr; // Used as a reference when making DelegatedInkMetadatas based on previously // sent points.
diff --git a/ui/gl/direct_composition_surface_win.cc b/ui/gl/direct_composition_surface_win.cc index 6c524088..480c380 100644 --- a/ui/gl/direct_composition_surface_win.cc +++ b/ui/gl/direct_composition_surface_win.cc
@@ -410,7 +410,7 @@ } // static -void DirectCompositionSurfaceWin::InitializeOneOff() { +void DirectCompositionSurfaceWin::InitializeOneOff(GLDisplayEGL* display) { DCHECK(!g_dcomp_device); if (base::CommandLine::ForCurrentProcess()->HasSwitch( @@ -431,7 +431,7 @@ // EGL_KHR_no_config_context surface compatibility is required to be able to // MakeCurrent with the default pbuffer surface. - if (!GLSurfaceEGL::GetGLDisplayEGL()->IsEGLNoConfigContextSupported()) { + if (!display->IsEGLNoConfigContextSupported()) { DLOG(ERROR) << "EGL_KHR_no_config_context not supported"; return; }
diff --git a/ui/gl/direct_composition_surface_win.h b/ui/gl/direct_composition_surface_win.h index beb4172..0e2a094 100644 --- a/ui/gl/direct_composition_surface_win.h +++ b/ui/gl/direct_composition_surface_win.h
@@ -60,7 +60,7 @@ DirectCompositionSurfaceWin& operator=(const DirectCompositionSurfaceWin&) = delete; - static void InitializeOneOff(); + static void InitializeOneOff(GLDisplayEGL* display); static void ShutdownOneOff(); static const Microsoft::WRL::ComPtr<IDCompositionDevice2>&
diff --git a/ui/gl/direct_composition_surface_win_unittest.cc b/ui/gl/direct_composition_surface_win_unittest.cc index 67ede44..a7b6ec9 100644 --- a/ui/gl/direct_composition_surface_win_unittest.cc +++ b/ui/gl/direct_composition_surface_win_unittest.cc
@@ -123,8 +123,8 @@ fake_power_monitor_source_.SetOnBatteryPower(true); // Without this, the following check always fails. - gl::init::InitializeGLNoExtensionsOneOff(/*init_bindings=*/true, - /*system_device_id=*/0); + display_ = gl::init::InitializeGLNoExtensionsOneOff( + /*init_bindings=*/true, /*system_device_id=*/0); if (!DirectCompositionSurfaceWin::GetDirectCompositionDevice()) { LOG(WARNING) << "DirectComposition not supported, skipping test."; return; @@ -142,7 +142,7 @@ context_ = nullptr; if (surface_) DestroySurface(std::move(surface_)); - gl::init::ShutdownGL(false); + gl::init::ShutdownGL(display_, false); } scoped_refptr<DirectCompositionSurfaceWin> @@ -176,6 +176,7 @@ scoped_refptr<DirectCompositionSurfaceWin> surface_; scoped_refptr<GLContext> context_; base::test::ScopedPowerMonitorTestSource fake_power_monitor_source_; + GLDisplay* display_ = nullptr; }; TEST_F(DirectCompositionSurfaceTest, TestMakeCurrent) {
diff --git a/ui/gl/egl_api_unittest.cc b/ui/gl/egl_api_unittest.cc index ffd94d0..d6b8588 100644 --- a/ui/gl/egl_api_unittest.cc +++ b/ui/gl/egl_api_unittest.cc
@@ -18,6 +18,7 @@ void SetUp() override { fake_client_extension_string_ = ""; fake_extension_string_ = ""; + display_ = nullptr; g_driver_egl.fn.eglInitializeFn = &FakeInitialize; g_driver_egl.fn.eglTerminateFn = &FakeTerminate; @@ -35,7 +36,7 @@ } void TearDown() override { - init::ShutdownGL(false); + init::ShutdownGL(display_, false); api_.reset(nullptr); fake_client_extension_string_ = ""; @@ -50,8 +51,8 @@ SetDisabledExtensionsEGL(disabled_extensions); } g_driver_egl.InitializeClientExtensionBindings(); - GLSurfaceEGL::InitializeDisplay(EGLDisplayPlatform(EGL_DEFAULT_DISPLAY), - /*system_device_id=*/0); + display_ = GLSurfaceEGL::InitializeDisplay( + EGLDisplayPlatform(EGL_DEFAULT_DISPLAY), /*system_device_id=*/0); g_driver_egl.InitializeExtensionBindings(); } @@ -108,6 +109,7 @@ static const char* fake_extension_string_; static const char* fake_client_extension_string_; + GLDisplay* display_ = nullptr; std::unique_ptr<RealEGLApi> api_; };
diff --git a/ui/gl/gl_egl_api_implementation.cc b/ui/gl/gl_egl_api_implementation.cc index 20300b21..0261916c 100644 --- a/ui/gl/gl_egl_api_implementation.cc +++ b/ui/gl/gl_egl_api_implementation.cc
@@ -134,8 +134,8 @@ g_current_egl_context->SetDisabledExtensions(disabled_extensions); } -bool InitializeExtensionSettingsOneOffEGL() { - return GLSurfaceEGL::InitializeExtensionSettingsOneOff(); +bool InitializeExtensionSettingsOneOffEGL(GLDisplayEGL* display) { + return GLSurfaceEGL::InitializeExtensionSettingsOneOff(display); } } // namespace gl
diff --git a/ui/gl/gl_egl_api_implementation.h b/ui/gl/gl_egl_api_implementation.h index e11d0dd..9dace80 100644 --- a/ui/gl/gl_egl_api_implementation.h +++ b/ui/gl/gl_egl_api_implementation.h
@@ -11,6 +11,7 @@ #include "base/memory/raw_ptr.h" #include "ui/gl/gl_bindings.h" +#include "ui/gl/gl_display.h" #include "ui/gl/gl_export.h" namespace gl { @@ -21,7 +22,7 @@ GL_EXPORT void ClearBindingsEGL(); GL_EXPORT bool GetGLWindowSystemBindingInfoEGL(GLWindowSystemBindingInfo* info); GL_EXPORT void SetDisabledExtensionsEGL(const std::string& disabled_extensions); -GL_EXPORT bool InitializeExtensionSettingsOneOffEGL(); +GL_EXPORT bool InitializeExtensionSettingsOneOffEGL(GLDisplayEGL* display); class GL_EXPORT EGLApiBase : public EGLApi { public:
diff --git a/ui/gl/gl_surface_egl.cc b/ui/gl/gl_surface_egl.cc index d3df5378..de30d9b4 100644 --- a/ui/gl/gl_surface_egl.cc +++ b/ui/gl/gl_surface_egl.cc
@@ -185,8 +185,6 @@ namespace gl { -bool GLSurfaceEGL::initialized_ = false; - namespace { class EGLGpuSwitchingObserver; @@ -994,35 +992,39 @@ } // static -bool GLSurfaceEGL::InitializeOneOff(EGLDisplayPlatform native_display, - uint64_t system_device_id) { - if (initialized_) - return true; +GLDisplayEGL* GLSurfaceEGL::InitializeOneOff(EGLDisplayPlatform native_display, + uint64_t system_device_id) { + GLDisplayEGL* display = + GLDisplayManagerEGL::GetInstance()->GetDisplay(system_device_id); + if (display->GetDisplay() == EGL_NO_DISPLAY) { + // Must be called before InitializeDisplay(). + g_driver_egl.InitializeClientExtensionBindings(); - // Must be called before InitializeDisplay(). - g_driver_egl.InitializeClientExtensionBindings(); + display = InitializeDisplay(native_display, system_device_id); + if (display->GetDisplay() == EGL_NO_DISPLAY) + return nullptr; - GLDisplayEGL* display = InitializeDisplay(native_display, system_device_id); - if (display->GetDisplay() == EGL_NO_DISPLAY) - return false; + // Must be called after InitializeDisplay(). + g_driver_egl.InitializeExtensionBindings(); - // Must be called after InitializeDisplay(). - g_driver_egl.InitializeExtensionBindings(); - - return InitializeOneOffCommon(display); + InitializeOneOffCommon(display); + } + return display; } // static -bool GLSurfaceEGL::InitializeOneOffForTesting() { +GLDisplayEGL* GLSurfaceEGL::InitializeOneOffForTesting() { g_driver_egl.InitializeClientExtensionBindings(); - GLDisplayEGL* display = GetGLDisplayEGL(); + GLDisplayEGL* display = + GLDisplayManagerEGL::GetInstance()->GetDisplay(GpuPreference::kDefault); display->SetDisplay(eglGetCurrentDisplay()); g_driver_egl.InitializeExtensionBindings(); - return InitializeOneOffCommon(display); + InitializeOneOffCommon(display); + return display; } // static -bool GLSurfaceEGL::InitializeOneOffCommon(GLDisplayEGL* display) { +void GLSurfaceEGL::InitializeOneOffCommon(GLDisplayEGL* display) { display->egl_client_extensions = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS); display->egl_extensions = @@ -1161,27 +1163,25 @@ ui::GpuSwitchingManager::GetInstance()->AddObserver( g_egl_gpu_switching_observer); } - - initialized_ = true; - return true; } // static -bool GLSurfaceEGL::InitializeExtensionSettingsOneOff() { - if (!initialized_) +bool GLSurfaceEGL::InitializeExtensionSettingsOneOff(GLDisplayEGL* display) { + DCHECK(display); + if (display->GetDisplay() == EGL_NO_DISPLAY) return false; g_driver_egl.UpdateConditionalExtensionBindings(); - GetGLDisplayEGL()->egl_client_extensions = + display->egl_client_extensions = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS); - GetGLDisplayEGL()->egl_extensions = - eglQueryString(GetGLDisplayEGL()->GetDisplay(), EGL_EXTENSIONS); + display->egl_extensions = + eglQueryString(display->GetDisplay(), EGL_EXTENSIONS); return true; } // static -void GLSurfaceEGL::ShutdownOneOff() { - if (!initialized_) { +void GLSurfaceEGL::ShutdownOneOff(GLDisplayEGL* display) { + if (!display || display->GetDisplay() == EGL_NO_DISPLAY) { return; } @@ -1191,13 +1191,10 @@ delete g_egl_gpu_switching_observer; g_egl_gpu_switching_observer = nullptr; } - GLDisplayEGL* display = GetGLDisplayEGL(); angle::ResetPlatform(display->GetDisplay()); - if (display->GetDisplay() != EGL_NO_DISPLAY) { - DCHECK(g_driver_egl.fn.eglTerminateFn); - eglTerminate(display->GetDisplay()); - display->SetDisplay(EGL_NO_DISPLAY); - } + DCHECK(g_driver_egl.fn.eglTerminateFn); + eglTerminate(display->GetDisplay()); + display->SetDisplay(EGL_NO_DISPLAY); display->egl_client_extensions = nullptr; display->egl_extensions = nullptr; @@ -1214,8 +1211,6 @@ display->egl_display_texture_share_group_supported = false; display->egl_create_context_client_arrays_supported = false; display->egl_angle_feature_control_supported = false; - - initialized_ = false; } GLSurfaceEGL::~GLSurfaceEGL() = default; @@ -1225,7 +1220,8 @@ // static GLDisplayEGL* GLSurfaceEGL::InitializeDisplay(EGLDisplayPlatform native_display, uint64_t system_device_id) { - GLDisplayEGL* gl_display = GetGLDisplayEGL(); + GLDisplayEGL* gl_display = + GLDisplayManagerEGL::GetInstance()->GetDisplay(system_device_id); if (gl_display->GetDisplay() != EGL_NO_DISPLAY) { return gl_display; } @@ -1312,17 +1308,17 @@ for (size_t disp_index = 0; disp_index < init_displays.size(); ++disp_index) { DisplayType display_type = init_displays[disp_index]; - EGLDisplay display = GetDisplayFromType( + EGLDisplay egl_display = GetDisplayFromType( display_type, gl_display, enabled_angle_features, disabled_angle_features, disable_all_angle_features, system_device_id); - if (display == EGL_NO_DISPLAY) { + if (egl_display == EGL_NO_DISPLAY) { LOG(ERROR) << "EGL display query failed with error " << GetLastEGLErrorString(); } // Init ANGLE platform now that we have the global display. if (supports_angle) { - if (!angle::InitializePlatform(display)) { + if (!angle::InitializePlatform(egl_display)) { LOG(ERROR) << "ANGLE Platform initialization failed."; } @@ -1338,7 +1334,7 @@ ->MaybeGetScopedDisplayUnsetForVulkan(); } - if (!eglInitialize(display, nullptr, nullptr)) { + if (!eglInitialize(egl_display, nullptr, nullptr)) { bool is_last = disp_index == init_displays.size() - 1; LOG(ERROR) << "eglInitialize " << DisplayTypeString(display_type) @@ -1361,7 +1357,7 @@ UMA_HISTOGRAM_ENUMERATION("GPU.EGLDisplayType", display_type, DISPLAY_TYPE_MAX); - gl_display->SetDisplay(display); + gl_display->SetDisplay(egl_display); gl_display->display_type = display_type; break; } @@ -1392,7 +1388,7 @@ DCHECK(!surface_); format_ = format; - if (!display_->GetDisplay()) { + if (display_->GetDisplay() == EGL_NO_DISPLAY) { LOG(ERROR) << "Trying to create surface with invalid display."; return false; }
diff --git a/ui/gl/gl_surface_egl.h b/ui/gl/gl_surface_egl.h index e53b967..56d276e 100644 --- a/ui/gl/gl_surface_egl.h +++ b/ui/gl/gl_surface_egl.h
@@ -61,11 +61,15 @@ // |system_device_id| specifies which GPU to use on a multi-GPU system. // If its value is 0, use the default GPU of the system. - static bool InitializeOneOff(EGLDisplayPlatform native_display, - uint64_t system_device_id); - static bool InitializeOneOffForTesting(); - static bool InitializeExtensionSettingsOneOff(); - static void ShutdownOneOff(); + // Calling this functionm a second time on the same |system_device_id| + // is a no-op and returns the same GLDisplayEGL. + // TODO(https://crbug.com/1251724): This will be called once per display + // when Chrome begins to support multi-gpu rendering. + static GLDisplayEGL* InitializeOneOff(EGLDisplayPlatform native_display, + uint64_t system_device_id); + static GLDisplayEGL* InitializeOneOffForTesting(); + static bool InitializeExtensionSettingsOneOff(GLDisplayEGL* display); + static void ShutdownOneOff(GLDisplayEGL* display); // |system_device_id| specifies which GPU to use on a multi-GPU system. // If its value is 0, use the default GPU of the system. static GLDisplayEGL* InitializeDisplay(EGLDisplayPlatform native_display, @@ -79,8 +83,7 @@ GLDisplayEGL* display_ = nullptr; private: - static bool InitializeOneOffCommon(GLDisplayEGL* display); - static bool initialized_; + static void InitializeOneOffCommon(GLDisplayEGL* display); }; // Encapsulates an EGL surface bound to a view.
diff --git a/ui/gl/gl_surface_egl_unittest.cc b/ui/gl/gl_surface_egl_unittest.cc index dabd89c..c27bef3 100644 --- a/ui/gl/gl_surface_egl_unittest.cc +++ b/ui/gl/gl_surface_egl_unittest.cc
@@ -32,15 +32,18 @@ protected: void SetUp() override { #if BUILDFLAG(IS_WIN) - GLSurfaceTestSupport::InitializeOneOffImplementation( + display_ = GLSurfaceTestSupport::InitializeOneOffImplementation( GLImplementationParts(kGLImplementationEGLANGLE), true); #else - GLSurfaceTestSupport::InitializeOneOffImplementation( + display_ = GLSurfaceTestSupport::InitializeOneOffImplementation( GLImplementationParts(kGLImplementationEGLGLES2), true); #endif } - void TearDown() override { gl::init::ShutdownGL(false); } + void TearDown() override { GLSurfaceTestSupport::ShutdownGL(display_); } + + private: + GLDisplay* display_ = nullptr; }; #if !defined(MEMORY_SANITIZER)
diff --git a/ui/gl/gpu_timing_unittest.cc b/ui/gl/gpu_timing_unittest.cc index 0b33a2ee..096b52e5 100644 --- a/ui/gl/gpu_timing_unittest.cc +++ b/ui/gl/gpu_timing_unittest.cc
@@ -39,8 +39,8 @@ context_ = nullptr; surface_ = nullptr; if (setup_) { - MockGLInterface::SetGLInterface(NULL); - init::ShutdownGL(false); + MockGLInterface::SetGLInterface(nullptr); + GLSurfaceTestSupport::ShutdownGL(display_); } setup_ = false; cpu_time_bounded_ = false; @@ -51,7 +51,7 @@ void SetupGLContext(const char* gl_version, const char* gl_extensions) { ASSERT_FALSE(setup_) << "Cannot setup GL context twice."; SetGLGetProcAddressProc(MockGLInterface::GetGLProcAddress); - GLSurfaceTestSupport::InitializeOneOffWithMockBindings(); + display_ = GLSurfaceTestSupport::InitializeOneOffWithMockBindings(); gl_ = std::make_unique<::testing::StrictMock<MockGLInterface>>(); MockGLInterface::SetGLInterface(gl_.get()); @@ -86,6 +86,7 @@ scoped_refptr<GLContextStub> context_; scoped_refptr<GLSurfaceStub> surface_; GPUTimingFake gpu_timing_fake_queries_; + GLDisplay* display_ = nullptr; }; TEST_F(GPUTimingTest, FakeTimerTest) {
diff --git a/ui/gl/init/gl_factory.cc b/ui/gl/init/gl_factory.cc index 01b8dd9..70fdfb35 100644 --- a/ui/gl/init/gl_factory.cc +++ b/ui/gl/init/gl_factory.cc
@@ -15,6 +15,7 @@ #include "base/trace_event/trace_event.h" #include "build/build_config.h" #include "third_party/abseil-cpp/absl/types/optional.h" +#include "ui/gl/gl_display_manager.h" #include "ui/gl/gl_share_group.h" #include "ui/gl/gl_surface.h" #include "ui/gl/gl_utils.h" @@ -131,8 +132,8 @@ return GLImplementationParts(kGLImplementationNone); } -bool InitializeGLOneOffPlatformHelper(bool init_extensions, - uint64_t system_device_id) { +GLDisplay* InitializeGLOneOffPlatformHelper(bool init_extensions, + uint64_t system_device_id) { TRACE_EVENT1("gpu,startup", "gl::init::InitializeGLOneOffPlatformHelper", "init_extensions", init_extensions); @@ -147,26 +148,30 @@ } // namespace -bool InitializeGLOneOff(uint64_t system_device_id) { +GLDisplay* InitializeGLOneOff(uint64_t system_device_id) { TRACE_EVENT0("gpu,startup", "gl::init::InitializeOneOff"); if (!InitializeStaticGLBindingsOneOff()) - return false; - if (GetGLImplementation() == kGLImplementationDisabled) - return true; + return nullptr; + if (GetGLImplementation() == kGLImplementationDisabled) { + return GLDisplayManagerEGL::GetInstance()->GetDisplay( + GpuPreference::kDefault); + } return InitializeGLOneOffPlatformHelper(true, system_device_id); } -bool InitializeGLNoExtensionsOneOff(bool init_bindings, - uint64_t system_device_id) { +GLDisplay* InitializeGLNoExtensionsOneOff(bool init_bindings, + uint64_t system_device_id) { TRACE_EVENT1("gpu,startup", "gl::init::InitializeNoExtensionsOneOff", "init_bindings", init_bindings); if (init_bindings) { if (!InitializeStaticGLBindingsOneOff()) - return false; - if (GetGLImplementation() == kGLImplementationDisabled) - return true; + return nullptr; + if (GetGLImplementation() == kGLImplementationDisabled) { + return GLDisplayManagerEGL::GetInstance()->GetDisplay( + GpuPreference::kDefault); + } } return InitializeGLOneOffPlatformHelper(false, system_device_id); @@ -196,48 +201,51 @@ bool initialized = InitializeStaticGLBindings(impl); if (!initialized && fallback_to_software_gl) { - ShutdownGL(/*due_to_fallback*/ true); + ShutdownGL(nullptr, /*due_to_fallback*/ true); initialized = InitializeStaticGLBindings(GetSoftwareGLImplementation()); } if (!initialized) { - ShutdownGL(/*due_to_fallback*/ false); + ShutdownGL(nullptr, /*due_to_fallback*/ false); return false; } return true; } -bool InitializeGLOneOffPlatformImplementation(bool fallback_to_software_gl, - bool disable_gl_drawing, - bool init_extensions, - uint64_t system_device_id) { +GLDisplay* InitializeGLOneOffPlatformImplementation( + bool fallback_to_software_gl, + bool disable_gl_drawing, + bool init_extensions, + uint64_t system_device_id) { if (IsSoftwareGLImplementation(GetGLImplementationParts())) fallback_to_software_gl = false; - bool initialized = InitializeGLOneOffPlatform(system_device_id); + GLDisplay* display = InitializeGLOneOffPlatform(system_device_id); + bool initialized = !!display; if (!initialized && fallback_to_software_gl) { - ShutdownGL(/*due_to_fallback*/ true); - initialized = InitializeStaticGLBindings(GetSoftwareGLImplementation()) && - InitializeGLOneOffPlatform(system_device_id); + ShutdownGL(nullptr, /*due_to_fallback=*/true); + if (InitializeStaticGLBindings(GetSoftwareGLImplementation())) { + display = InitializeGLOneOffPlatform(system_device_id); + initialized = !!display; + } } if (initialized && init_extensions) { - initialized = InitializeExtensionSettingsOneOffPlatform(); + initialized = InitializeExtensionSettingsOneOffPlatform(display); } - if (!initialized) - ShutdownGL(false); - - if (initialized) { - DVLOG(1) << "Using " - << GetGLImplementationGLName(GetGLImplementationParts()) - << " GL implementation."; - if (disable_gl_drawing) - InitializeNullDrawGLBindings(); + if (!initialized) { + ShutdownGL(display, false); + return nullptr; } - return initialized; + + DVLOG(1) << "Using " << GetGLImplementationGLName(GetGLImplementationParts()) + << " GL implementation."; + if (disable_gl_drawing) + InitializeNullDrawGLBindings(); + return display; } -void ShutdownGL(bool due_to_fallback) { - ShutdownGLPlatform(); +void ShutdownGL(GLDisplay* display, bool due_to_fallback) { + ShutdownGLPlatform(display); UnloadGLNativeLibraries(due_to_fallback); SetGLImplementation(kGLImplementationNone);
diff --git a/ui/gl/init/gl_factory.h b/ui/gl/init/gl_factory.h index bf6765f..218f4d9 100644 --- a/ui/gl/init/gl_factory.h +++ b/ui/gl/init/gl_factory.h
@@ -12,6 +12,7 @@ #include "base/memory/ref_counted.h" #include "ui/gfx/geometry/size.h" #include "ui/gfx/native_widget_types.h" +#include "ui/gl/gl_display.h" #include "ui/gl/gl_implementation.h" #include "ui/gl/gl_surface_format.h" #include "ui/gl/gpu_preference.h" @@ -35,13 +36,14 @@ // Initializes GL bindings and extension settings. // |system_device_id| specifies which GPU to use on a multi-GPU system. // If its value is 0, use the default GPU of the system. -GL_INIT_EXPORT bool InitializeGLOneOff(uint64_t system_device_id); +GL_INIT_EXPORT GLDisplay* InitializeGLOneOff(uint64_t system_device_id); // Initializes GL bindings without initializing extension settings. // |system_device_id| specifies which GPU to use on a multi-GPU system. // If its value is 0, use the default GPU of the system. -GL_INIT_EXPORT bool InitializeGLNoExtensionsOneOff(bool init_bindings, - uint64_t system_device_id); +GL_INIT_EXPORT GLDisplay* InitializeGLNoExtensionsOneOff( + bool init_bindings, + uint64_t system_device_id); // Initializes GL bindings - load dlls and get proc address according to gl // command line switch. @@ -49,7 +51,8 @@ // Initialize plaiform dependent extension settings, including bindings, // capabilities, etc. -GL_INIT_EXPORT bool InitializeExtensionSettingsOneOffPlatform(); +GL_INIT_EXPORT bool InitializeExtensionSettingsOneOffPlatform( + GLDisplay* display); // Initializes GL bindings using the provided parameters. This might be required // for use in tests. @@ -62,14 +65,15 @@ // successfully. // |system_device_id| specifies which GPU to use on a multi-GPU system. // If its value is 0, use the default GPU of the system. -GL_INIT_EXPORT bool InitializeGLOneOffPlatformImplementation( +GL_INIT_EXPORT GLDisplay* InitializeGLOneOffPlatformImplementation( bool fallback_to_software_gl, bool disable_gl_drawing, bool init_extensions, uint64_t system_device_id); // Clears GL bindings and resets GL implementation. -GL_INIT_EXPORT void ShutdownGL(bool due_to_fallback); +// Calling this function a second time on the same |display| is a no-op. +GL_INIT_EXPORT void ShutdownGL(GLDisplay* display, bool due_to_fallback); // Return information about the GL window system binding implementation (e.g., // EGL, GLX, WGL). Returns true if the information was retrieved successfully.
diff --git a/ui/gl/init/gl_factory_android.cc b/ui/gl/init/gl_factory_android.cc index 222e612..c508a04e 100644 --- a/ui/gl/init/gl_factory_android.cc +++ b/ui/gl/init/gl_factory_android.cc
@@ -176,13 +176,14 @@ } } -bool InitializeExtensionSettingsOneOffPlatform() { +bool InitializeExtensionSettingsOneOffPlatform(GLDisplay* display) { GLImplementation implementation = GetGLImplementation(); DCHECK_NE(kGLImplementationNone, implementation); switch (implementation) { case kGLImplementationEGLGLES2: case kGLImplementationEGLANGLE: - return InitializeExtensionSettingsOneOffEGL(); + return InitializeExtensionSettingsOneOffEGL( + static_cast<GLDisplayEGL*>(display)); case kGLImplementationMockGL: case kGLImplementationStubGL: return true;
diff --git a/ui/gl/init/gl_factory_mac.cc b/ui/gl/init/gl_factory_mac.cc index a70f8631..4de2742 100644 --- a/ui/gl/init/gl_factory_mac.cc +++ b/ui/gl/init/gl_factory_mac.cc
@@ -166,7 +166,7 @@ // TODO(zmo): Implement this if needs arise. } -bool InitializeExtensionSettingsOneOffPlatform() { +bool InitializeExtensionSettingsOneOffPlatform(GLDisplay* display) { GLImplementation implementation = GetGLImplementation(); DCHECK_NE(kGLImplementationNone, implementation); // TODO(zmo): Implement this if needs arise.
diff --git a/ui/gl/init/gl_factory_ozone.cc b/ui/gl/init/gl_factory_ozone.cc index 7259e7b..57b4c97 100644 --- a/ui/gl/init/gl_factory_ozone.cc +++ b/ui/gl/init/gl_factory_ozone.cc
@@ -120,9 +120,9 @@ } } -bool InitializeExtensionSettingsOneOffPlatform() { +bool InitializeExtensionSettingsOneOffPlatform(GLDisplay* display) { if (HasGLOzone()) - return GetGLOzone()->InitializeExtensionSettingsOneOffPlatform(); + return GetGLOzone()->InitializeExtensionSettingsOneOffPlatform(display); switch (GetGLImplementation()) { case kGLImplementationMockGL:
diff --git a/ui/gl/init/gl_factory_win.cc b/ui/gl/init/gl_factory_win.cc index 3aa35bb..f61749a 100644 --- a/ui/gl/init/gl_factory_win.cc +++ b/ui/gl/init/gl_factory_win.cc
@@ -115,12 +115,13 @@ } } -bool InitializeExtensionSettingsOneOffPlatform() { +bool InitializeExtensionSettingsOneOffPlatform(GLDisplay* display) { GLImplementation implementation = GetGLImplementation(); DCHECK_NE(kGLImplementationNone, implementation); switch (implementation) { case kGLImplementationEGLANGLE: - return InitializeExtensionSettingsOneOffEGL(); + return InitializeExtensionSettingsOneOffEGL( + static_cast<GLDisplayEGL*>(display)); case kGLImplementationMockGL: case kGLImplementationStubGL: return true;
diff --git a/ui/gl/init/gl_initializer.h b/ui/gl/init/gl_initializer.h index 06445b3..12c30d4 100644 --- a/ui/gl/init/gl_initializer.h +++ b/ui/gl/init/gl_initializer.h
@@ -6,6 +6,7 @@ #define UI_GL_INIT_GL_INITIALIZER_H_ #include "ui/gl/buildflags.h" +#include "ui/gl/gl_display.h" #include "ui/gl/gl_implementation.h" namespace gl { @@ -20,7 +21,7 @@ // InitializeGLOneOffPlatformImplementation() instead. // |system_device_id| specifies which GPU to use on a multi-GPU system. // If its value is 0, use the default GPU of the system. -bool InitializeGLOneOffPlatform(uint64_t system_device_id); +GLDisplay* InitializeGLOneOffPlatform(uint64_t system_device_id); // Initializes a particular GL implementation. bool InitializeStaticGLBindings(GLImplementationParts implementation); @@ -30,7 +31,8 @@ #endif // BUILDFLAG(USE_STATIC_ANGLE) // Clears GL bindings for all implementations supported by platform. -void ShutdownGLPlatform(); +// Calling this function a second time on the same |display| is a no-op. +void ShutdownGLPlatform(GLDisplay* display); } // namespace init } // namespace gl
diff --git a/ui/gl/init/gl_initializer_android.cc b/ui/gl/init/gl_initializer_android.cc index d26c601..5abd2d9 100644 --- a/ui/gl/init/gl_initializer_android.cc +++ b/ui/gl/init/gl_initializer_android.cc
@@ -10,6 +10,7 @@ #include "base/logging.h" #include "base/native_library.h" #include "ui/gl/gl_bindings.h" +#include "ui/gl/gl_display_manager.h" #include "ui/gl/gl_egl_api_implementation.h" #include "ui/gl/gl_gl_api_implementation.h" #include "ui/gl/gl_surface_egl.h" @@ -76,18 +77,20 @@ } // namespace -bool InitializeGLOneOffPlatform(uint64_t system_device_id) { +GLDisplay* InitializeGLOneOffPlatform(uint64_t system_device_id) { switch (GetGLImplementation()) { case kGLImplementationEGLGLES2: - case kGLImplementationEGLANGLE: - if (!GLSurfaceEGL::InitializeOneOff( - EGLDisplayPlatform(EGL_DEFAULT_DISPLAY), system_device_id)) { + case kGLImplementationEGLANGLE: { + GLDisplay* display = GLSurfaceEGL::InitializeOneOff( + EGLDisplayPlatform(EGL_DEFAULT_DISPLAY), system_device_id); + if (!display) { LOG(ERROR) << "GLSurfaceEGL::InitializeOneOff failed."; - return false; + return nullptr; } - return true; + return display; + } default: - return true; + return GLDisplayManagerEGL::GetInstance()->GetDisplay(system_device_id); } } @@ -113,8 +116,8 @@ return false; } -void ShutdownGLPlatform() { - GLSurfaceEGL::ShutdownOneOff(); +void ShutdownGLPlatform(GLDisplay* display) { + GLSurfaceEGL::ShutdownOneOff(static_cast<GLDisplayEGL*>(display)); ClearBindingsEGL(); ClearBindingsGL(); }
diff --git a/ui/gl/init/gl_initializer_mac.cc b/ui/gl/init/gl_initializer_mac.cc index b1fbd1f..fa9771391 100644 --- a/ui/gl/init/gl_initializer_mac.cc +++ b/ui/gl/init/gl_initializer_mac.cc
@@ -18,6 +18,7 @@ #include "base/threading/thread_restrictions.h" #include "ui/gl/gl_bindings.h" #include "ui/gl/gl_context.h" +#include "ui/gl/gl_display_manager.h" #include "ui/gl/gl_gl_api_implementation.h" #include "ui/gl/gl_implementation.h" #include "ui/gl/gl_surface.h" @@ -170,28 +171,29 @@ } // namespace -bool InitializeGLOneOffPlatform(uint64_t system_device_id) { +GLDisplay* InitializeGLOneOffPlatform(uint64_t system_device_id) { switch (GetGLImplementation()) { case kGLImplementationDesktopGL: case kGLImplementationDesktopGLCoreProfile: case kGLImplementationAppleGL: if (!InitializeOneOffForSandbox()) { LOG(ERROR) << "GLSurfaceCGL::InitializeOneOff failed."; - return false; } - return true; + return GLDisplayManagerEGL::GetInstance()->GetDisplay(system_device_id); #if defined(USE_EGL) case kGLImplementationEGLGLES2: - case kGLImplementationEGLANGLE: - if (!GLSurfaceEGL::InitializeOneOff(EGLDisplayPlatform(0), - system_device_id)) { + case kGLImplementationEGLANGLE: { + GLDisplay* display = GLSurfaceEGL::InitializeOneOff(EGLDisplayPlatform(0), + system_device_id); + if (!display) { LOG(ERROR) << "GLSurfaceEGL::InitializeOneOff failed."; - return false; + return nullptr; } - return true; + return display; + } #endif // defined(USE_EGL) default: - return true; + return GLDisplayManagerEGL::GetInstance()->GetDisplay(system_device_id); } } @@ -229,10 +231,10 @@ return false; } -void ShutdownGLPlatform() { +void ShutdownGLPlatform(GLDisplay* display) { ClearBindingsGL(); #if defined(USE_EGL) - GLSurfaceEGL::ShutdownOneOff(); + GLSurfaceEGL::ShutdownOneOff(static_cast<GLDisplayEGL*>(display)); ClearBindingsEGL(); #endif // defined(USE_EGL) }
diff --git a/ui/gl/init/gl_initializer_ozone.cc b/ui/gl/init/gl_initializer_ozone.cc index 36aef21..36c73023 100644 --- a/ui/gl/init/gl_initializer_ozone.cc +++ b/ui/gl/init/gl_initializer_ozone.cc
@@ -7,6 +7,7 @@ #include "base/check_op.h" #include "base/notreached.h" #include "ui/gl/gl_bindings.h" +#include "ui/gl/gl_display_manager.h" #include "ui/gl/gl_gl_api_implementation.h" #include "ui/gl/gl_surface.h" @@ -19,20 +20,20 @@ namespace gl { namespace init { -bool InitializeGLOneOffPlatform(uint64_t) { +GLDisplay* InitializeGLOneOffPlatform(uint64_t system_device_id) { if (HasGLOzone()) { gl::GLDisplayEglUtil::SetInstance(gl::GLDisplayEglUtilOzone::GetInstance()); - return GetGLOzone()->InitializeGLOneOffPlatform(); + return GetGLOzone()->InitializeGLOneOffPlatform(system_device_id); } switch (GetGLImplementation()) { case kGLImplementationMockGL: case kGLImplementationStubGL: - return true; + return GLDisplayManagerEGL::GetInstance()->GetDisplay(system_device_id); default: NOTREACHED(); } - return false; + return nullptr; } bool InitializeStaticGLBindings(GLImplementationParts implementation) { @@ -58,9 +59,9 @@ return false; } -void ShutdownGLPlatform() { +void ShutdownGLPlatform(GLDisplay* display) { if (HasGLOzone()) { - GetGLOzone()->ShutdownGL(); + GetGLOzone()->ShutdownGL(display); return; }
diff --git a/ui/gl/init/gl_initializer_win.cc b/ui/gl/init/gl_initializer_win.cc index 06798da..960065c 100644 --- a/ui/gl/init/gl_initializer_win.cc +++ b/ui/gl/init/gl_initializer_win.cc
@@ -18,6 +18,7 @@ #include "base/trace_event/trace_event.h" #include "base/win/windows_version.h" #include "ui/gl/gl_bindings.h" +#include "ui/gl/gl_display_manager.h" #include "ui/gl/gl_egl_api_implementation.h" #include "ui/gl/gl_gl_api_implementation.h" #include "ui/gl/gl_surface_egl.h" @@ -122,25 +123,27 @@ } // namespace -bool InitializeGLOneOffPlatform(uint64_t system_device_id) { +GLDisplay* InitializeGLOneOffPlatform(uint64_t system_device_id) { VSyncProviderWin::InitializeOneOff(); switch (GetGLImplementation()) { - case kGLImplementationEGLANGLE: - if (!GLSurfaceEGL::InitializeOneOff(EGLDisplayPlatform(GetDC(nullptr)), - system_device_id)) { + case kGLImplementationEGLANGLE: { + GLDisplayEGL* display = GLSurfaceEGL::InitializeOneOff( + EGLDisplayPlatform(GetDC(nullptr)), system_device_id); + if (!display) { LOG(ERROR) << "GLSurfaceEGL::InitializeOneOff failed."; - return false; + return nullptr; } - DirectCompositionSurfaceWin::InitializeOneOff(); - break; + DirectCompositionSurfaceWin::InitializeOneOff(display); + return display; + } case kGLImplementationMockGL: case kGLImplementationStubGL: break; default: NOTREACHED(); } - return true; + return GLDisplayManagerEGL::GetInstance()->GetDisplay(system_device_id); } bool InitializeStaticGLBindings(GLImplementationParts implementation) { @@ -170,9 +173,9 @@ return false; } -void ShutdownGLPlatform() { +void ShutdownGLPlatform(GLDisplay* display) { DirectCompositionSurfaceWin::ShutdownOneOff(); - GLSurfaceEGL::ShutdownOneOff(); + GLSurfaceEGL::ShutdownOneOff(static_cast<GLDisplayEGL*>(display)); ClearBindingsEGL(); ClearBindingsGL(); }
diff --git a/ui/gl/test/gl_image_test_support.cc b/ui/gl/test/gl_image_test_support.cc index c3d5bb4..81b85cf4 100644 --- a/ui/gl/test/gl_image_test_support.cc +++ b/ui/gl/test/gl_image_test_support.cc
@@ -33,6 +33,8 @@ } } // namespace +GLDisplay* GLImageTestSupport::display_ = nullptr; + // static void GLImageTestSupport::InitializeGL( absl::optional<GLImplementationParts> prefered_impl) { @@ -50,7 +52,7 @@ prefered_impl ? *prefered_impl : allowed_impls[0]; DCHECK(impl.IsAllowed(allowed_impls)); - GLSurfaceTestSupport::InitializeOneOffImplementation(impl, true); + display_ = GLSurfaceTestSupport::InitializeOneOffImplementation(impl, true); #if defined(USE_OZONE) // Make sure all the tasks posted to the current task runner by the // initialization functions are run before running the tests. @@ -60,7 +62,7 @@ // static void GLImageTestSupport::CleanupGL() { - init::ShutdownGL(false); + GLSurfaceTestSupport::ShutdownGL(display_); } // static
diff --git a/ui/gl/test/gl_image_test_support.h b/ui/gl/test/gl_image_test_support.h index e427a6b..393872f 100644 --- a/ui/gl/test/gl_image_test_support.h +++ b/ui/gl/test/gl_image_test_support.h
@@ -9,6 +9,7 @@ #include "third_party/abseil-cpp/absl/types/optional.h" #include "ui/gfx/buffer_types.h" +#include "ui/gl/gl_display.h" #include "ui/gl/gl_implementation.h" namespace gl { @@ -31,6 +32,9 @@ gfx::BufferFormat format, const uint8_t color[4], uint8_t* data); + + private: + static GLDisplay* display_; }; } // namespace gl
diff --git a/ui/gl/test/gl_surface_test_support.cc b/ui/gl/test/gl_surface_test_support.cc index 22ee46e..1afadab 100644 --- a/ui/gl/test/gl_surface_test_support.cc +++ b/ui/gl/test/gl_surface_test_support.cc
@@ -25,7 +25,8 @@ namespace gl { namespace { -void InitializeOneOffHelper(bool init_extensions) { + +GLDisplay* InitializeOneOffHelper(bool init_extensions) { DCHECK_EQ(kGLImplementationNone, GetGLImplementation()); #if defined(USE_OZONE) @@ -69,70 +70,70 @@ CHECK(gl::init::InitializeStaticGLBindingsImplementation( impl, fallback_to_software_gl)); - CHECK(gl::init::InitializeGLOneOffPlatformImplementation( + GLDisplay* display = gl::init::InitializeGLOneOffPlatformImplementation( fallback_to_software_gl, disable_gl_drawing, init_extensions, - /*system_device_id=*/0)); + /*system_device_id=*/0); + CHECK(display); + return display; } } // namespace // static -void GLSurfaceTestSupport::InitializeOneOff() { - InitializeOneOffHelper(true); +GLDisplay* GLSurfaceTestSupport::InitializeOneOff() { + return InitializeOneOffHelper(true); } // static -void GLSurfaceTestSupport::InitializeNoExtensionsOneOff() { - InitializeOneOffHelper(false); +GLDisplay* GLSurfaceTestSupport::InitializeNoExtensionsOneOff() { + return InitializeOneOffHelper(false); } // static -void GLSurfaceTestSupport::InitializeOneOffImplementation( +GLDisplay* GLSurfaceTestSupport::InitializeOneOffImplementation( GLImplementationParts impl, bool fallback_to_software_gl) { DCHECK(!base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kUseGL)) << "kUseGL has not effect in tests"; - // This method may be called multiple times in the same process to set up - // bindings in different ways. - init::ShutdownGL(false); - bool disable_gl_drawing = false; bool init_extensions = true; CHECK(gl::init::InitializeStaticGLBindingsImplementation( impl, fallback_to_software_gl)); - CHECK(gl::init::InitializeGLOneOffPlatformImplementation( + GLDisplay* display = gl::init::InitializeGLOneOffPlatformImplementation( fallback_to_software_gl, disable_gl_drawing, init_extensions, - /*system_device_id=*/0)); + /*system_device_id=*/0); + CHECK(display); + return display; } // static -void GLSurfaceTestSupport::InitializeOneOffWithMockBindings() { +GLDisplay* GLSurfaceTestSupport::InitializeOneOffWithMockBindings() { #if defined(USE_OZONE) ui::OzonePlatform::InitParams params; params.single_process = true; ui::OzonePlatform::InitializeForGPU(params); #endif - InitializeOneOffImplementation(GLImplementationParts(kGLImplementationMockGL), - false); + return InitializeOneOffImplementation( + GLImplementationParts(kGLImplementationMockGL), false); } // static -void GLSurfaceTestSupport::InitializeOneOffWithStubBindings() { +GLDisplay* GLSurfaceTestSupport::InitializeOneOffWithStubBindings() { #if defined(USE_OZONE) ui::OzonePlatform::InitParams params; params.single_process = true; ui::OzonePlatform::InitializeForGPU(params); #endif - InitializeOneOffImplementation(GLImplementationParts(kGLImplementationStubGL), - false); + return InitializeOneOffImplementation( + GLImplementationParts(kGLImplementationStubGL), false); } // static -void GLSurfaceTestSupport::ShutdownGL() { - init::ShutdownGL(false); +void GLSurfaceTestSupport::ShutdownGL(GLDisplay* display) { + init::ShutdownGL(display, false); } } // namespace gl
diff --git a/ui/gl/test/gl_surface_test_support.h b/ui/gl/test/gl_surface_test_support.h index 89c662cf..ac0db72f 100644 --- a/ui/gl/test/gl_surface_test_support.h +++ b/ui/gl/test/gl_surface_test_support.h
@@ -5,19 +5,21 @@ #ifndef UI_GL_TEST_GL_SURFACE_TEST_SUPPORT_H_ #define UI_GL_TEST_GL_SURFACE_TEST_SUPPORT_H_ +#include "ui/gl/gl_display.h" #include "ui/gl/gl_implementation.h" namespace gl { class GLSurfaceTestSupport { public: - static void InitializeOneOff(); - static void InitializeNoExtensionsOneOff(); - static void InitializeOneOffImplementation(GLImplementationParts impl, - bool fallback_to_swiftshader); - static void InitializeOneOffWithMockBindings(); - static void InitializeOneOffWithStubBindings(); - static void ShutdownGL(); + static GLDisplay* InitializeOneOff(); + static GLDisplay* InitializeNoExtensionsOneOff(); + static GLDisplay* InitializeOneOffImplementation( + GLImplementationParts impl, + bool fallback_to_swiftshader); + static GLDisplay* InitializeOneOffWithMockBindings(); + static GLDisplay* InitializeOneOffWithStubBindings(); + static void ShutdownGL(GLDisplay* display); }; } // namespace gl
diff --git a/ui/gtk/gtk_ui.cc b/ui/gtk/gtk_ui.cc index 98558b63..12193ad 100644 --- a/ui/gtk/gtk_ui.cc +++ b/ui/gtk/gtk_ui.cc
@@ -404,42 +404,6 @@ : base::TimeDelta(); } -ui::NativeTheme* GtkUi::GetNativeTheme(aura::Window* window) const { - return GetNativeTheme(use_system_theme_callback_.is_null() || - use_system_theme_callback_.Run(window)); -} - -ui::NativeTheme* GtkUi::GetNativeTheme(bool use_system_theme) const { - return use_system_theme ? native_theme_ - : ui::NativeTheme::GetInstanceForNativeUi(); -} - -void GtkUi::SetUseSystemThemeCallback(UseSystemThemeCallback callback) { - use_system_theme_callback_ = std::move(callback); -} - -bool GtkUi::GetDefaultUsesSystemTheme() const { - std::unique_ptr<base::Environment> env(base::Environment::Create()); - - switch (base::nix::GetDesktopEnvironment(env.get())) { - case base::nix::DESKTOP_ENVIRONMENT_CINNAMON: - case base::nix::DESKTOP_ENVIRONMENT_GNOME: - case base::nix::DESKTOP_ENVIRONMENT_PANTHEON: - case base::nix::DESKTOP_ENVIRONMENT_UKUI: - case base::nix::DESKTOP_ENVIRONMENT_UNITY: - case base::nix::DESKTOP_ENVIRONMENT_XFCE: - return true; - case base::nix::DESKTOP_ENVIRONMENT_KDE3: - case base::nix::DESKTOP_ENVIRONMENT_KDE4: - case base::nix::DESKTOP_ENVIRONMENT_KDE5: - case base::nix::DESKTOP_ENVIRONMENT_OTHER: - return false; - } - // Unless GetDesktopEnvironment() badly misbehaves, this should never happen. - NOTREACHED(); - return false; -} - gfx::Image GtkUi::GetIconForContentType(const std::string& content_type, int dip_size, float scale) const { @@ -699,6 +663,10 @@ &theme); } +ui::NativeTheme* GtkUi::GetNativeTheme() const { + return native_theme_; +} + bool GtkUi::MatchEvent(const ui::Event& event, std::vector<ui::TextEditCommandAuraLinux>* commands) { // GTK4 dropped custom key bindings.
diff --git a/ui/gtk/gtk_ui.h b/ui/gtk/gtk_ui.h index 59c7c4f..65f9d6c 100644 --- a/ui/gtk/gtk_ui.h +++ b/ui/gtk/gtk_ui.h
@@ -80,10 +80,6 @@ SkColor GetInactiveSelectionBgColor() const override; SkColor GetInactiveSelectionFgColor() const override; base::TimeDelta GetCursorBlinkInterval() const override; - ui::NativeTheme* GetNativeTheme(aura::Window* window) const override; - ui::NativeTheme* GetNativeTheme(bool use_system_theme) const override; - void SetUseSystemThemeCallback(UseSystemThemeCallback callback) override; - bool GetDefaultUsesSystemTheme() const override; gfx::Image GetIconForContentType(const std::string& content_type, int size, float scale) const override; @@ -99,6 +95,7 @@ int GetCursorThemeSize() override; std::vector<std::string> GetAvailableSystemThemeNamesForTest() const override; void SetSystemThemeByNameForTest(const std::string& theme_name) override; + ui::NativeTheme* GetNativeTheme() const override; // ui::TextEditKeybindingDelegate: bool MatchEvent(const ui::Event& event, @@ -184,11 +181,6 @@ base::flat_map<WindowFrameActionSource, WindowFrameAction> window_frame_actions_; - // Used to determine whether the system theme should be used for a window. If - // no override is provided or the callback returns true, GtkUi will default - // to a NativeThemeGtk instance. - UseSystemThemeCallback use_system_theme_callback_; - float device_scale_factor_ = 1.0f; // Paints a native window frame. Typically only one of these will be
diff --git a/ui/ozone/common/gl_ozone_egl.cc b/ui/ozone/common/gl_ozone_egl.cc index 2034d0a..c7e3707 100644 --- a/ui/ozone/common/gl_ozone_egl.cc +++ b/ui/ozone/common/gl_ozone_egl.cc
@@ -15,13 +15,15 @@ namespace ui { -bool GLOzoneEGL::InitializeGLOneOffPlatform() { - if (!gl::GLSurfaceEGL::InitializeOneOff(GetNativeDisplay(), - /*system_device_id=*/0)) { +gl::GLDisplay* GLOzoneEGL::InitializeGLOneOffPlatform( + uint64_t system_device_id) { + gl::GLDisplay* display = + gl::GLSurfaceEGL::InitializeOneOff(GetNativeDisplay(), system_device_id); + if (!display) { LOG(ERROR) << "GLSurfaceEGL::InitializeOneOff failed."; - return false; + return nullptr; } - return true; + return display; } bool GLOzoneEGL::InitializeStaticGLBindings( @@ -41,12 +43,14 @@ gl::SetDisabledExtensionsEGL(disabled_extensions); } -bool GLOzoneEGL::InitializeExtensionSettingsOneOffPlatform() { - return gl::InitializeExtensionSettingsOneOffEGL(); +bool GLOzoneEGL::InitializeExtensionSettingsOneOffPlatform( + gl::GLDisplay* display) { + return gl::InitializeExtensionSettingsOneOffEGL( + static_cast<gl::GLDisplayEGL*>(display)); } -void GLOzoneEGL::ShutdownGL() { - gl::GLSurfaceEGL::ShutdownOneOff(); +void GLOzoneEGL::ShutdownGL(gl::GLDisplay* display) { + gl::GLSurfaceEGL::ShutdownOneOff(static_cast<gl::GLDisplayEGL*>(display)); gl::ClearBindingsGL(); gl::ClearBindingsEGL(); }
diff --git a/ui/ozone/common/gl_ozone_egl.h b/ui/ozone/common/gl_ozone_egl.h index 11b44340..29d0aa5 100644 --- a/ui/ozone/common/gl_ozone_egl.h +++ b/ui/ozone/common/gl_ozone_egl.h
@@ -24,13 +24,14 @@ ~GLOzoneEGL() override {} // GLOzone: - bool InitializeGLOneOffPlatform() override; + gl::GLDisplay* InitializeGLOneOffPlatform(uint64_t system_device_id) override; bool InitializeStaticGLBindings( const gl::GLImplementationParts& implementation) override; void SetDisabledExtensionsPlatform( const std::string& disabled_extensions) override; - bool InitializeExtensionSettingsOneOffPlatform() override; - void ShutdownGL() override; + bool InitializeExtensionSettingsOneOffPlatform( + gl::GLDisplay* display) override; + void ShutdownGL(gl::GLDisplay* display) override; bool GetGLWindowSystemBindingInfo( const gl::GLVersionInfo& gl_info, gl::GLWindowSystemBindingInfo* info) override;
diff --git a/ui/ozone/platform/wayland/host/shell_toplevel_wrapper.h b/ui/ozone/platform/wayland/host/shell_toplevel_wrapper.h index bbc50ff..4177604 100644 --- a/ui/ozone/platform/wayland/host/shell_toplevel_wrapper.h +++ b/ui/ozone/platform/wayland/host/shell_toplevel_wrapper.h
@@ -105,6 +105,10 @@ virtual void SetRestoreInfo(int32_t restore_session_id, int32_t restore_window_id) = 0; + virtual void SetRestoreInfoWithWindowIdSource( + int32_t restore_session_id, + const std::string& restore_window_id_source) = 0; + // Request that the server set the orientation lock to the provided lock type. // This is only accepted if the requesting window is running in immersive // fullscreen mode and in a tablet configuration.
diff --git a/ui/ozone/platform/wayland/host/wayland_toplevel_window.cc b/ui/ozone/platform/wayland/host/wayland_toplevel_window.cc index eec5729..e56a894 100644 --- a/ui/ozone/platform/wayland/host/wayland_toplevel_window.cc +++ b/ui/ozone/platform/wayland/host/wayland_toplevel_window.cc
@@ -497,6 +497,7 @@ } restore_session_id_ = properties.restore_session_id; restore_window_id_ = properties.restore_window_id; + restore_window_id_source_ = properties.restore_window_id_source; SetPinnedModeExtension(this, static_cast<PinnedModeExtension*>(this)); SetSystemModalExtension(this, static_cast<SystemModalExtension*>(this)); @@ -881,8 +882,14 @@ zaura_surface_set_occlusion_tracking(aura_surface_.get()); SetImmersiveFullscreenStatus(false); SetInitialWorkspace(); - if (restore_session_id_) - shell_toplevel_->SetRestoreInfo(restore_session_id_, restore_window_id_); + if (restore_window_id_) { + DCHECK(!restore_window_id_source_); + shell_toplevel_->SetRestoreInfo(restore_session_id_, + restore_window_id_.value()); + } else if (restore_window_id_source_) { + shell_toplevel_->SetRestoreInfoWithWindowIdSource( + restore_session_id_, restore_window_id_source_.value()); + } UpdateSystemModal(); }
diff --git a/ui/ozone/platform/wayland/host/wayland_toplevel_window.h b/ui/ozone/platform/wayland/host/wayland_toplevel_window.h index b6008b3..2911821 100644 --- a/ui/ozone/platform/wayland/host/wayland_toplevel_window.h +++ b/ui/ozone/platform/wayland/host/wayland_toplevel_window.h
@@ -286,8 +286,11 @@ // See https://crbug.com/1223005 bool set_geometry_on_next_frame_ = false; + // Information used by the compositor to restore the window state upon + // creation. int32_t restore_session_id_ = 0; - int32_t restore_window_id_ = 0; + absl::optional<int32_t> restore_window_id_ = 0; + absl::optional<std::string> restore_window_id_source_; // Current modal status. bool system_modal_ = false;
diff --git a/ui/ozone/platform/wayland/host/wayland_zaura_shell.cc b/ui/ozone/platform/wayland/host/wayland_zaura_shell.cc index 5114ddf..bd5ba098 100644 --- a/ui/ozone/platform/wayland/host/wayland_zaura_shell.cc +++ b/ui/ozone/platform/wayland/host/wayland_zaura_shell.cc
@@ -20,7 +20,7 @@ namespace { constexpr uint32_t kMinVersion = 1; -constexpr uint32_t kMaxVersion = 31; +constexpr uint32_t kMaxVersion = 32; } // static
diff --git a/ui/ozone/platform/wayland/host/xdg_toplevel_wrapper_impl.cc b/ui/ozone/platform/wayland/host/xdg_toplevel_wrapper_impl.cc index aaa1792a2..a2254c4 100644 --- a/ui/ozone/platform/wayland/host/xdg_toplevel_wrapper_impl.cc +++ b/ui/ozone/platform/wayland/host/xdg_toplevel_wrapper_impl.cc
@@ -414,4 +414,16 @@ } } +void XDGToplevelWrapperImpl::SetRestoreInfoWithWindowIdSource( + int32_t restore_session_id, + const std::string& restore_window_id_source) { + if (aura_toplevel_ && + zaura_toplevel_get_version(aura_toplevel_.get()) >= + ZAURA_TOPLEVEL_SET_RESTORE_INFO_WITH_WINDOW_ID_SOURCE_SINCE_VERSION) { + zaura_toplevel_set_restore_info_with_window_id_source( + aura_toplevel_.get(), restore_session_id, + restore_window_id_source.c_str()); + } +} + } // namespace ui
diff --git a/ui/ozone/platform/wayland/host/xdg_toplevel_wrapper_impl.h b/ui/ozone/platform/wayland/host/xdg_toplevel_wrapper_impl.h index 9ede1649..5bdf82dc 100644 --- a/ui/ozone/platform/wayland/host/xdg_toplevel_wrapper_impl.h +++ b/ui/ozone/platform/wayland/host/xdg_toplevel_wrapper_impl.h
@@ -46,6 +46,7 @@ void Unlock() override; void RequestWindowBounds(const gfx::Rect& bounds) override; void SetRestoreInfo(int32_t, int32_t) override; + void SetRestoreInfoWithWindowIdSource(int32_t, const std::string&) override; void SetSystemModal(bool modal) override; bool SupportsScreenCoordinates() const override;
diff --git a/ui/ozone/platform/wayland/host/zxdg_toplevel_v6_wrapper_impl.cc b/ui/ozone/platform/wayland/host/zxdg_toplevel_v6_wrapper_impl.cc index baa26fc9..ce770a5d9 100644 --- a/ui/ozone/platform/wayland/host/zxdg_toplevel_v6_wrapper_impl.cc +++ b/ui/ozone/platform/wayland/host/zxdg_toplevel_v6_wrapper_impl.cc
@@ -199,6 +199,10 @@ void ZXDGToplevelV6WrapperImpl::SetRestoreInfo(int32_t, int32_t) {} +void ZXDGToplevelV6WrapperImpl::SetRestoreInfoWithWindowIdSource( + int32_t, + const std::string&) {} + void ZXDGToplevelV6WrapperImpl::SetSystemModal(bool modal) { NOTREACHED(); }
diff --git a/ui/ozone/platform/wayland/host/zxdg_toplevel_v6_wrapper_impl.h b/ui/ozone/platform/wayland/host/zxdg_toplevel_v6_wrapper_impl.h index 46ca991..15cc90f0 100644 --- a/ui/ozone/platform/wayland/host/zxdg_toplevel_v6_wrapper_impl.h +++ b/ui/ozone/platform/wayland/host/zxdg_toplevel_v6_wrapper_impl.h
@@ -48,6 +48,9 @@ void RequestWindowBounds(const gfx::Rect& geometry) override; void SetRestoreInfo(int32_t restore_session_id, int32_t restore_window_id) override; + void SetRestoreInfoWithWindowIdSource( + int32_t restore_session_id, + const std::string& restore_window_id_source) override; void SetSystemModal(bool modal) override; bool SupportsScreenCoordinates() const override;
diff --git a/ui/ozone/platform/x11/gl_ozone_glx.cc b/ui/ozone/platform/x11/gl_ozone_glx.cc index b40b3c5..c3c7e62 100644 --- a/ui/ozone/platform/x11/gl_ozone_glx.cc +++ b/ui/ozone/platform/x11/gl_ozone_glx.cc
@@ -8,6 +8,7 @@ #include "build/build_config.h" #include "ui/gl/gl_context.h" #include "ui/gl/gl_context_glx.h" +#include "ui/gl/gl_display_manager.h" #include "ui/gl/gl_gl_api_implementation.h" #include "ui/gl/gl_glx_api_implementation.h" #include "ui/gl/gl_surface_glx_x11.h" @@ -24,12 +25,15 @@ } // namespace -bool GLOzoneGLX::InitializeGLOneOffPlatform() { +gl::GLDisplay* GLOzoneGLX::InitializeGLOneOffPlatform( + uint64_t system_device_id) { + // TODO(https://crbug.com/1251724): GLSurfaceGLX::InitializeOneOff() + // should take |system_device_id| and return a GLDisplayX11. if (!gl::GLSurfaceGLX::InitializeOneOff()) { LOG(ERROR) << "GLSurfaceGLX::InitializeOneOff failed."; - return false; + return nullptr; } - return true; + return gl::GLDisplayManagerX11::GetInstance()->GetDisplay(system_device_id); } bool GLOzoneGLX::InitializeStaticGLBindings( @@ -73,11 +77,12 @@ gl::SetDisabledExtensionsGLX(disabled_extensions); } -bool GLOzoneGLX::InitializeExtensionSettingsOneOffPlatform() { +bool GLOzoneGLX::InitializeExtensionSettingsOneOffPlatform( + gl::GLDisplay* display) { return gl::InitializeExtensionSettingsOneOffGLX(); } -void GLOzoneGLX::ShutdownGL() { +void GLOzoneGLX::ShutdownGL(gl::GLDisplay* display) { gl::ClearBindingsGL(); gl::ClearBindingsGLX(); }
diff --git a/ui/ozone/platform/x11/gl_ozone_glx.h b/ui/ozone/platform/x11/gl_ozone_glx.h index 61d51d5e..1fb05ff 100644 --- a/ui/ozone/platform/x11/gl_ozone_glx.h +++ b/ui/ozone/platform/x11/gl_ozone_glx.h
@@ -19,13 +19,14 @@ ~GLOzoneGLX() override {} - bool InitializeGLOneOffPlatform() override; + gl::GLDisplay* InitializeGLOneOffPlatform(uint64_t system_device_id) override; bool InitializeStaticGLBindings( const gl::GLImplementationParts& implementation) override; void SetDisabledExtensionsPlatform( const std::string& disabled_extensions) override; - bool InitializeExtensionSettingsOneOffPlatform() override; - void ShutdownGL() override; + bool InitializeExtensionSettingsOneOffPlatform( + gl::GLDisplay* display) override; + void ShutdownGL(gl::GLDisplay* display) override; bool GetGLWindowSystemBindingInfo( const gl::GLVersionInfo& gl_info, gl::GLWindowSystemBindingInfo* info) override;
diff --git a/ui/ozone/public/gl_ozone.h b/ui/ozone/public/gl_ozone.h index ccfa309b..ef4ec09e 100644 --- a/ui/ozone/public/gl_ozone.h +++ b/ui/ozone/public/gl_ozone.h
@@ -11,6 +11,7 @@ #include "base/memory/ref_counted.h" #include "ui/gfx/geometry/size.h" #include "ui/gfx/native_widget_types.h" +#include "ui/gl/gl_display.h" #include "ui/gl/gl_implementation.h" #include "ui/gl/gpu_preference.h" @@ -37,7 +38,8 @@ const gl::GLImplementationParts& implementation) = 0; // Performs any one off initialization for GL implementation. - virtual bool InitializeGLOneOffPlatform() = 0; + virtual gl::GLDisplay* InitializeGLOneOffPlatform( + uint64_t system_device_id) = 0; // Disables the specified extensions in the window system bindings, // e.g., GLX, EGL, etc. This is part of the GPU driver bug workarounds @@ -48,10 +50,11 @@ // Initializes extension related settings for window system bindings that // will be affected by SetDisabledExtensionsPlatform(). This function is // called after SetDisabledExtensionsPlatform() to finalize the bindings. - virtual bool InitializeExtensionSettingsOneOffPlatform() = 0; + virtual bool InitializeExtensionSettingsOneOffPlatform( + gl::GLDisplay* display) = 0; // Clears static GL bindings. - virtual void ShutdownGL() = 0; + virtual void ShutdownGL(gl::GLDisplay* display) = 0; // Returns information about the GL window system binding implementation (eg. // EGL, GLX, WGL). Returns true if the information was retrieved successfully.
diff --git a/ui/platform_window/platform_window_init_properties.h b/ui/platform_window/platform_window_init_properties.h index 7e12663..1a28dd57 100644 --- a/ui/platform_window/platform_window_init_properties.h +++ b/ui/platform_window/platform_window_init_properties.h
@@ -132,9 +132,12 @@ // manager to match the desktop entry and group windows. std::string wayland_app_id; - // Specifies the unique browser session id and the restore window id. + // Specifies the unique session id and the restore window id. int32_t restore_session_id; - int32_t restore_window_id; + absl::optional<int32_t> restore_window_id; + + // Specifies the source to get `restore_window_id` from. + absl::optional<std::string> restore_window_id_source; #endif #if defined(USE_OZONE)
diff --git a/ui/qt/BUILD.gn b/ui/qt/BUILD.gn index a75de440..a74956f 100644 --- a/ui/qt/BUILD.gn +++ b/ui/qt/BUILD.gn
@@ -89,6 +89,8 @@ ":qt_interface", "//base", "//skia", + "//ui/color", + "//ui/color:mixers", "//ui/gfx", "//ui/native_theme", "//ui/shell_dialogs",
diff --git a/ui/qt/DEPS b/ui/qt/DEPS index bfa0817c..a41bfb8 100644 --- a/ui/qt/DEPS +++ b/ui/qt/DEPS
@@ -1,6 +1,8 @@ include_rules = [ "+third_party/skia", + "+ui/color", "+ui/gfx", + "+ui/native_theme", "+ui/shell_dialogs", "+ui/views", ]
diff --git a/ui/qt/qt_interface.h b/ui/qt/qt_interface.h index 569af9e..5115ed6 100644 --- a/ui/qt/qt_interface.h +++ b/ui/qt/qt_interface.h
@@ -10,6 +10,8 @@ #include <stdint.h> #include <stdlib.h> +using SkColor = uint32_t; + namespace qt { // std::string cannot be passed over the library boundary, so this class acts @@ -57,6 +59,11 @@ kFull, }; +enum class ColorRole { + kWindowBg, + kWindowFg, +}; + struct FontRenderParams { bool antialiasing; bool use_bitmaps; @@ -96,7 +103,9 @@ virtual double GetScaleFactor() const = 0; virtual FontRenderParams GetFontRenderParams() const = 0; virtual FontDescription GetFontDescription() const = 0; - virtual Image GetIconForContentType(const String& content_type, int size) = 0; + virtual Image GetIconForContentType(const String& content_type, + int size) const = 0; + virtual SkColor GetColor(ColorRole role) const = 0; }; } // namespace qt
diff --git a/ui/qt/qt_shim.cc b/ui/qt/qt_shim.cc index 89c77435..abe4ba4 100644 --- a/ui/qt/qt_shim.cc +++ b/ui/qt/qt_shim.cc
@@ -11,6 +11,7 @@ #include <QIcon> #include <QMimeDatabase> #include <QMimeType> +#include <QPalette> namespace qt { @@ -42,6 +43,109 @@ } } +// Obtain the average color of a gradient. +SkColor GradientColor(const QGradient& gradient) { + QGradientStops stops = gradient.stops(); + if (stops.empty()) + return QColorConstants::Transparent.rgba(); + + float a = 0; + float r = 0; + float g = 0; + float b = 0; + for (int i = 0; i < stops.size(); i++) { + // Determine the extents of this stop. The whole gradient interval is [0, + // 1], so extend to the endpoint if this is the first or last stop. + float left_interval = + i == 0 ? stops[i].first : (stops[i].first - stops[i - 1].first) / 2; + float right_interval = i == stops.size() - 1 + ? 1 - stops[i].first + : (stops[i + 1].first - stops[i].first) / 2; + float length = left_interval + right_interval; + + // alpha() returns a value in [0, 255] and alphaF() returns a value in + // [0, 1]. The color values are not premultiplied so the RGB channels need + // to be multiplied by the alpha (in range [0, 1]) before summing. The + // alpha doesn't need to be multiplied, so we just sum color.alpha() in + // range [0, 255] directly. + const QColor& color = stops[i].second; + a += color.alpha() * length; + r += color.alphaF() * color.red() * length; + g += color.alphaF() * color.green() * length; + b += color.alphaF() * color.blue() * length; + } + return qRgba(r, g, b, a); +} + +// Obtain the average color of a texture. +SkColor TextureColor(QImage image) { + size_t size = image.width() * image.height(); + if (!size) + return QColorConstants::Transparent.rgba(); + + if (image.format() != QImage::Format_ARGB32_Premultiplied) + image = image.convertToFormat(QImage::Format_ARGB32_Premultiplied); + + size_t a = 0; + size_t r = 0; + size_t g = 0; + size_t b = 0; + const auto* pixels = image.bits(); + for (size_t i = 0; i < size; i++) { + a += pixels[4 * i + 0]; + r += pixels[4 * i + 1]; + g += pixels[4 * i + 2]; + b += pixels[4 * i + 3]; + } + return qRgba(r / size, g / size, b / size, a / size); +} + +SkColor BrushColor(const QBrush& brush) { + QColor color = brush.color(); + auto alpha_blend = [&](uint8_t alpha) { + QColor blended = color; + blended.setAlpha(blended.alpha() * alpha / 255); + return blended.rgba(); + }; + + switch (brush.style()) { + case Qt::SolidPattern: + return alpha_blend(0xFF); + case Qt::Dense1Pattern: + return alpha_blend(0xE0); + case Qt::Dense2Pattern: + return alpha_blend(0xC0); + case Qt::Dense3Pattern: + return alpha_blend(0xA0); + case Qt::Dense4Pattern: + return alpha_blend(0x80); + case Qt::Dense5Pattern: + return alpha_blend(0x60); + case Qt::Dense6Pattern: + return alpha_blend(0x40); + case Qt::Dense7Pattern: + return alpha_blend(0x20); + case Qt::NoBrush: + return alpha_blend(0x00); + case Qt::HorPattern: + case Qt::VerPattern: + return alpha_blend(0x20); + case Qt::CrossPattern: + return alpha_blend(0x40); + case Qt::BDiagPattern: + case Qt::FDiagPattern: + return alpha_blend(0x20); + case Qt::DiagCrossPattern: + return alpha_blend(0x40); + case Qt::LinearGradientPattern: + case Qt::RadialGradientPattern: + case Qt::ConicalGradientPattern: + return GradientColor(*brush.gradient()); + case Qt::TexturePattern: + return TextureColor(brush.textureImage()); + } +} + } // namespace QtShim::QtShim(QtInterface::Delegate* delegate, int* argc, char** argv) @@ -77,7 +181,8 @@ }; } -Image QtShim::GetIconForContentType(const String& content_type, int size) { +Image QtShim::GetIconForContentType(const String& content_type, + int size) const { QMimeDatabase db; for (const char* mime : {content_type.c_str(), "application/octet-stream"}) { auto mt = db.mimeTypeForName(mime); @@ -97,6 +202,16 @@ return {}; } +SkColor QtShim::GetColor(ColorRole role) const { + auto palette = app_.palette(); + switch (role) { + case ColorRole::kWindowBg: + return BrushColor(palette.brush(QPalette::ColorRole::Window)); + case ColorRole::kWindowFg: + return BrushColor(palette.brush(QPalette::ColorRole::WindowText)); + } +} + void QtShim::FontChanged(const QFont& font) { delegate_->FontChanged(); }
diff --git a/ui/qt/qt_shim.h b/ui/qt/qt_shim.h index d565363..ef680d22 100644 --- a/ui/qt/qt_shim.h +++ b/ui/qt/qt_shim.h
@@ -26,7 +26,9 @@ double GetScaleFactor() const override; FontRenderParams GetFontRenderParams() const override; FontDescription GetFontDescription() const override; - Image GetIconForContentType(const String& content_type, int size) override; + Image GetIconForContentType(const String& content_type, + int size) const override; + SkColor GetColor(ColorRole role) const override; private slots: void FontChanged(const QFont& font);
diff --git a/ui/qt/qt_ui.cc b/ui/qt/qt_ui.cc index cb72bb1..edad14a 100644 --- a/ui/qt/qt_ui.cc +++ b/ui/qt/qt_ui.cc
@@ -12,6 +12,9 @@ #include "base/notreached.h" #include "base/path_service.h" #include "third_party/skia/include/core/SkBitmap.h" +#include "ui/color/color_mixer.h" +#include "ui/color/color_provider.h" +#include "ui/color/color_recipe.h" #include "ui/gfx/color_palette.h" #include "ui/gfx/font.h" #include "ui/gfx/font_render_params.h" @@ -19,6 +22,7 @@ #include "ui/gfx/image/image.h" #include "ui/gfx/image/image_skia_rep.h" #include "ui/gfx/image/image_skia_source.h" +#include "ui/native_theme/native_theme_base.h" #include "ui/qt/qt_interface.h" #include "ui/shell_dialogs/select_file_policy.h" #include "ui/views/controls/button/label_button_border.h" @@ -68,6 +72,17 @@ } // namespace +// We currently don't render any QT widgets, so this class is just a stub. +class QtNativeTheme : public ui::NativeThemeBase { + public: + QtNativeTheme() + : ui::NativeThemeBase(/*should_only_use_dark_colors=*/false, + /*is_custom_system_theme=*/true) {} + QtNativeTheme(const QtNativeTheme&) = delete; + QtNativeTheme& operator=(const QtNativeTheme&) = delete; + ~QtNativeTheme() override = default; +}; + QtUi::QtUi() = default; QtUi::~QtUi() = default; @@ -121,7 +136,9 @@ cmd_line_ = CopyCmdLine(*base::CommandLine::ForCurrentProcess()); shim_.reset((reinterpret_cast<decltype(&CreateQtInterface)>( create_qt_interface)(this, &cmd_line_.argc, cmd_line_.argv.data()))); - + native_theme_ = std::make_unique<QtNativeTheme>(); + ui::ColorProviderManager::Get().AppendColorProviderInitializer( + base::BindRepeating(&QtUi::AddNativeColorMixer, base::Unretained(this))); FontChanged(); return true; @@ -172,25 +189,6 @@ return base::TimeDelta(); } -ui::NativeTheme* QtUi::GetNativeTheme(aura::Window* window) const { - NOTIMPLEMENTED_LOG_ONCE(); - return ui::NativeTheme::GetInstanceForNativeUi(); -} - -ui::NativeTheme* QtUi::GetNativeTheme(bool use_system_theme) const { - NOTIMPLEMENTED_LOG_ONCE(); - return ui::NativeTheme::GetInstanceForNativeUi(); -} - -void QtUi::SetUseSystemThemeCallback(UseSystemThemeCallback callback) { - NOTIMPLEMENTED_LOG_ONCE(); -} - -bool QtUi::GetDefaultUsesSystemTheme() const { - NOTIMPLEMENTED_LOG_ONCE(); - return false; -} - gfx::Image QtUi::GetIconForContentType(const std::string& content_type, int size, float scale) const { @@ -281,6 +279,10 @@ DCHECK(theme_name.empty()); } +ui::NativeTheme* QtUi::GetNativeTheme() const { + return native_theme_.get(); +} + bool QtUi::MatchEvent(const ui::Event& event, std::vector<ui::TextEditCommandAuraLinux>* commands) { // QT doesn't have "key themes" (eg. readline bindings) like GTK. @@ -322,6 +324,18 @@ }; } +void QtUi::AddNativeColorMixer(ui::ColorProvider* provider, + const ui::ColorProviderManager::Key& key) { + if (key.system_theme == ui::ColorProviderManager::SystemTheme::kDefault) + return; + + ui::ColorMixer& mixer = provider->AddMixer(); + mixer[ui::kColorNativeWindowBackground] = { + shim_->GetColor(ColorRole::kWindowBg)}; + mixer[ui::kColorNativeLabelForeground] = { + shim_->GetColor(ColorRole::kWindowFg)}; +} + std::unique_ptr<views::LinuxUI> CreateQtUi() { return std::make_unique<QtUi>(); }
diff --git a/ui/qt/qt_ui.h b/ui/qt/qt_ui.h index 08b06aa..4e19bcb 100644 --- a/ui/qt/qt_ui.h +++ b/ui/qt/qt_ui.h
@@ -13,6 +13,8 @@ namespace qt { +class QtNativeTheme; + // Interface to QT desktop features. class QtUi : public views::LinuxUI, QtInterface::Delegate { public: @@ -53,10 +55,6 @@ SkColor GetInactiveSelectionBgColor() const override; SkColor GetInactiveSelectionFgColor() const override; base::TimeDelta GetCursorBlinkInterval() const override; - ui::NativeTheme* GetNativeTheme(aura::Window* window) const override; - ui::NativeTheme* GetNativeTheme(bool use_system_theme) const override; - void SetUseSystemThemeCallback(UseSystemThemeCallback callback) override; - bool GetDefaultUsesSystemTheme() const override; gfx::Image GetIconForContentType(const std::string& content_type, int size, float scale) const override; @@ -72,6 +70,7 @@ int GetCursorThemeSize() override; std::vector<std::string> GetAvailableSystemThemeNamesForTest() const override; void SetSystemThemeByNameForTest(const std::string& theme_name) override; + ui::NativeTheme* GetNativeTheme() const override; // ui::TextEditKeybindingDelegate: bool MatchEvent(const ui::Event& event, @@ -81,6 +80,9 @@ void FontChanged() override; private: + void AddNativeColorMixer(ui::ColorProvider* provider, + const ui::ColorProviderManager::Key& key); + // QT modifies argc and argv, and they must be kept alive while // `shim_` is alive. CmdLineArgs cmd_line_; @@ -94,6 +96,8 @@ gfx::FontRenderParams font_params_; std::unique_ptr<QtInterface> shim_; + + std::unique_ptr<QtNativeTheme> native_theme_; }; // This should be the only symbol exported from this component.
diff --git a/ui/views/linux_ui/linux_ui.cc b/ui/views/linux_ui/linux_ui.cc index 9adee8b..95deac9 100644 --- a/ui/views/linux_ui/linux_ui.cc +++ b/ui/views/linux_ui/linux_ui.cc
@@ -5,8 +5,11 @@ #include "ui/views/linux_ui/linux_ui.h" #include <cstdio> +#include <utility> #include "base/command_line.h" +#include "base/environment.h" +#include "base/nix/xdg_util.h" #include "build/build_config.h" #include "ui/base/ime/linux/linux_input_method_context_factory.h" #include "ui/gfx/skia_font_delegate.h" @@ -71,6 +74,41 @@ device_scale_factor_observer_list_.RemoveObserver(observer); } +ui::NativeTheme* LinuxUI::GetNativeTheme(aura::Window* window) const { + return GetNativeTheme(use_system_theme_callback_.is_null() || + use_system_theme_callback_.Run(window)); +} + +ui::NativeTheme* LinuxUI::GetNativeTheme(bool use_system_theme) const { + return use_system_theme ? GetNativeTheme() + : ui::NativeTheme::GetInstanceForNativeUi(); +} + +void LinuxUI::SetUseSystemThemeCallback(UseSystemThemeCallback callback) { + use_system_theme_callback_ = std::move(callback); +} + +bool LinuxUI::GetDefaultUsesSystemTheme() const { + std::unique_ptr<base::Environment> env = base::Environment::Create(); + + // TODO(https://crbug.com/1317782): This logic won't be necessary after + // the GTK/QT backend is chosen based on the environment. + switch (base::nix::GetDesktopEnvironment(env.get())) { + case base::nix::DESKTOP_ENVIRONMENT_CINNAMON: + case base::nix::DESKTOP_ENVIRONMENT_GNOME: + case base::nix::DESKTOP_ENVIRONMENT_PANTHEON: + case base::nix::DESKTOP_ENVIRONMENT_UKUI: + case base::nix::DESKTOP_ENVIRONMENT_UNITY: + case base::nix::DESKTOP_ENVIRONMENT_XFCE: + return true; + case base::nix::DESKTOP_ENVIRONMENT_KDE3: + case base::nix::DESKTOP_ENVIRONMENT_KDE4: + case base::nix::DESKTOP_ENVIRONMENT_KDE5: + case base::nix::DESKTOP_ENVIRONMENT_OTHER: + return false; + } +} + // static LinuxUI::CmdLineArgs LinuxUI::CopyCmdLine( const base::CommandLine& command_line) {
diff --git a/ui/views/linux_ui/linux_ui.h b/ui/views/linux_ui/linux_ui.h index 3242a3f7..7d59a2c 100644 --- a/ui/views/linux_ui/linux_ui.h +++ b/ui/views/linux_ui/linux_ui.h
@@ -27,7 +27,7 @@ #include "ui/shell_dialogs/shell_dialog_linux.h" #endif -// The main entrypoint into Linux toolkit specific code. GTK code should only +// The main entrypoint into Linux toolkit specific code. GTK/QT code should only // be executed behind this interface. namespace aura { @@ -112,6 +112,19 @@ // factor. void RemoveDeviceScaleFactorObserver(DeviceScaleFactorObserver* observer); + // Returns the NativeTheme that reflects the theme used by `window`. + ui::NativeTheme* GetNativeTheme(aura::Window* window) const; + + // Returns the classic or system NativeTheme depending on `use_system_theme`. + ui::NativeTheme* GetNativeTheme(bool use_system_theme) const; + + // Sets a callback that determines whether to use the system theme. + void SetUseSystemThemeCallback(UseSystemThemeCallback callback); + + // Returns whether we should be using the native theme provided by this + // object by default. + bool GetDefaultUsesSystemTheme() const; + // Returns true on success. If false is returned, this instance shouldn't // be used and the behavior of all functions is undefined. [[nodiscard]] virtual bool Initialize() = 0; @@ -129,19 +142,6 @@ virtual SkColor GetInactiveSelectionFgColor() const = 0; virtual base::TimeDelta GetCursorBlinkInterval() const = 0; - // Returns the NativeTheme that reflects the theme used by `window`. - virtual ui::NativeTheme* GetNativeTheme(aura::Window* window) const = 0; - - // Returns the classic or system NativeTheme depending on `use_system_theme`. - virtual ui::NativeTheme* GetNativeTheme(bool use_system_theme) const = 0; - - // Sets a callback that determines whether to use the system theme. - virtual void SetUseSystemThemeCallback(UseSystemThemeCallback callback) = 0; - - // Returns whether we should be using the native theme provided by this - // object by default. - virtual bool GetDefaultUsesSystemTheme() const = 0; - // Returns the icon for a given content type from the icon theme. // TODO(davidben): Add an observer for the theme changing, so we can drop the // caches. @@ -213,7 +213,14 @@ return device_scale_factor_observer_list_; } + virtual ui::NativeTheme* GetNativeTheme() const = 0; + private: + // Used to determine whether the system theme should be used for a window. If + // no override is provided or the callback returns true, LinuxUI will default + // to GetNativeTheme(). + UseSystemThemeCallback use_system_theme_callback_; + // Objects to notify when the window frame button order changes. base::ObserverList<views::WindowButtonOrderObserver>::Unchecked window_button_order_observer_list_;
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_linux.cc b/ui/views/widget/desktop_aura/desktop_window_tree_host_linux.cc index b871dc9..e861828 100644 --- a/ui/views/widget/desktop_aura/desktop_window_tree_host_linux.cc +++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_linux.cc
@@ -33,11 +33,7 @@ #include "ui/accessibility/platform/atk_util_auralinux.h" #endif -#if BUILDFLAG(IS_CHROMEOS_LACROS) -#include "ui/views/widget/desktop_aura/window_event_filter_lacros.h" -#else #include "ui/views/widget/desktop_aura/window_event_filter_linux.h" -#endif namespace views { namespace { @@ -167,7 +163,6 @@ return result; } -#if !BUILDFLAG(IS_CHROMEOS_LACROS) void DesktopWindowTreeHostLinux::DispatchEvent(ui::Event* event) { // In Windows, the native events sent to chrome are separated into client // and non-client versions of events, which we record on our LocatedEvent @@ -220,7 +215,6 @@ if (!event->handled()) WindowTreeHostPlatform::DispatchEvent(event); } -#endif // !BUILDFLAG(IS_CHROMEOS_LACROS) void DesktopWindowTreeHostLinux::OnClosed() { DestroyNonClientEventFilter();
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_linux.h b/ui/views/widget/desktop_aura/desktop_window_tree_host_linux.h index 72d7edd..e6842b0 100644 --- a/ui/views/widget/desktop_aura/desktop_window_tree_host_linux.h +++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_linux.h
@@ -29,13 +29,8 @@ namespace views { -#if BUILDFLAG(IS_CHROMEOS_LACROS) -class WindowEventFilterLacros; -using WindowEventFilterClass = WindowEventFilterLacros; -#else class WindowEventFilterLinux; using WindowEventFilterClass = WindowEventFilterLinux; -#endif // Contains Linux specific implementation, which supports both X11 and Wayland // backend. @@ -73,9 +68,7 @@ Widget::MoveLoopEscapeBehavior escape_behavior) override; // PlatformWindowDelegate: -#if !BUILDFLAG(IS_CHROMEOS_LACROS) void DispatchEvent(ui::Event* event) override; -#endif void OnClosed() override; ui::X11Extension* GetX11Extension();
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.cc b/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.cc index c8207771..0ff9b8f6 100644 --- a/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.cc +++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.cc
@@ -158,8 +158,11 @@ #endif #if BUILDFLAG(IS_CHROMEOS) + // Set restore members for windows to know ids upon creation. See the + // corresponding comment in Widget::InitParams. properties.restore_session_id = params.restore_session_id; properties.restore_window_id = params.restore_window_id; + properties.restore_window_id_source = params.restore_window_id_source; #endif #if BUILDFLAG(IS_FUCHSIA)
diff --git a/ui/views/widget/widget.h b/ui/views/widget/widget.h index d1032f9..d44b04c2 100644 --- a/ui/views/widget/widget.h +++ b/ui/views/widget/widget.h
@@ -395,8 +395,16 @@ bool headless_mode = false; #if BUILDFLAG(IS_CHROMEOS_LACROS) + // TODO(crbug.com/1327490): Rename restore info variables. + // Only used by Wayland. Specifies the session id window key, the restore + // window id, and the app id, respectively, respectively, used by the + // compositor to restore window state upon creation. Only one of + // `restore_window_id` and `restore_window_id_source` should be set, as + // `restore_window_id_source` is used for widgets without inherent restore + // window ids, e.g. Chrome apps. int32_t restore_session_id = 0; - int32_t restore_window_id = 0; + absl::optional<int32_t> restore_window_id; + absl::optional<std::string> restore_window_id_source; #endif // Contains any properties with which the native widget should be
diff --git a/ui/webui/resources/cr_components/chromeos/network/network_config.js b/ui/webui/resources/cr_components/chromeos/network/network_config.js index 4c07bde..486ffaf 100644 --- a/ui/webui/resources/cr_components/chromeos/network/network_config.js +++ b/ui/webui/resources/cr_components/chromeos/network/network_config.js
@@ -597,8 +597,8 @@ * @private */ onEnterEvent_(event) { - if (event.path[0].localName === 'network-config-input' || - event.path[0].localName === 'network-password-input') { + if (event.composedPath()[0].localName === 'network-config-input' || + event.composedPath()[0].localName === 'network-password-input') { this.onEnterPressedInInput_(); event.stopPropagation(); }
diff --git a/ui/webui/resources/cr_elements/find_shortcut_behavior.js b/ui/webui/resources/cr_elements/find_shortcut_behavior.js index 5e5ef8b..393a7e9 100644 --- a/ui/webui/resources/cr_elements/find_shortcut_behavior.js +++ b/ui/webui/resources/cr_elements/find_shortcut_behavior.js
@@ -39,7 +39,8 @@ } if (!shortcutCtrlF.matchesEvent(e) && - (isTextInputElement(e.path[0]) || !shortcutSlash.matchesEvent(e))) { + (isTextInputElement(/** @type {!Element} */ (e.composedPath()[0])) || + !shortcutSlash.matchesEvent(e))) { return; }
diff --git a/ui/webui/resources/js/cr/ui/focus_row_behavior.js b/ui/webui/resources/js/cr/ui/focus_row_behavior.js index b49124c..0ee9a91 100644 --- a/ui/webui/resources/js/cr/ui/focus_row_behavior.js +++ b/ui/webui/resources/js/cr/ui/focus_row_behavior.js
@@ -30,7 +30,7 @@ * @param {!Event} e */ onFocus(row, e) { - const element = e.path[0]; + const element = /** @type {!Element} */ (e.composedPath()[0]); const focusableElement = cr.ui.FocusRow.getFocusableElement(element); if (element !== focusableElement) { focusableElement.focus();
diff --git a/url/url_canon_internal.cc b/url/url_canon_internal.cc index 3050ba5..48a1e74b1 100644 --- a/url/url_canon_internal.cc +++ b/url/url_canon_internal.cc
@@ -25,7 +25,7 @@ if (static_cast<UCHAR>(source[i]) >= 0x80) { // ReadChar will fill the code point with kUnicodeReplacementCharacter // when the input is invalid, which is what we want. - unsigned code_point; + base_icu::UChar32 code_point; ReadUTFChar(source, &i, length, &code_point); AppendUTF8EscapedValue(code_point, output); } else { @@ -233,7 +233,7 @@ 0, // 0xE0 - 0xFF }; -const char16_t kUnicodeReplacementCharacter = 0xfffd; +const base_icu::UChar32 kUnicodeReplacementCharacter = 0xfffd; void AppendStringOfType(const char* source, int length, SharedCharTypes type, @@ -248,8 +248,10 @@ DoAppendStringOfType<char16_t, char16_t>(source, length, type, output); } -bool ReadUTFChar(const char* str, int* begin, int length, - unsigned* code_point_out) { +bool ReadUTFChar(const char* str, + int* begin, + int length, + base_icu::UChar32* code_point_out) { // This depends on ints and int32s being the same thing. If they're not, it // will fail to compile. // TODO(mmenke): This should probably be fixed. @@ -264,7 +266,7 @@ bool ReadUTFChar(const char16_t* str, int* begin, int length, - unsigned* code_point_out) { + base_icu::UChar32* code_point_out) { // This depends on ints and int32s being the same thing. If they're not, it // will fail to compile. // TODO(mmenke): This should probably be fixed. @@ -293,7 +295,7 @@ CanonOutput* output) { bool success = true; for (int i = 0; i < input_len; i++) { - unsigned code_point; + base_icu::UChar32 code_point; success &= ReadUTFChar(input, &i, input_len, &code_point); AppendUTF8Value(code_point, output); } @@ -305,7 +307,7 @@ CanonOutputT<char16_t>* output) { bool success = true; for (int i = 0; i < input_len; i++) { - unsigned code_point; + base_icu::UChar32 code_point; success &= ReadUTFChar(input, &i, input_len, &code_point); AppendUTF16Value(code_point, output); }
diff --git a/url/url_canon_internal.h b/url/url_canon_internal.h index b486673..def4636 100644 --- a/url/url_canon_internal.h +++ b/url/url_canon_internal.h
@@ -15,6 +15,7 @@ #include "base/component_export.h" #include "base/notreached.h" +#include "base/third_party/icu/icu_utf.h" #include "url/url_canon.h" namespace url { @@ -139,7 +140,7 @@ } // The character we'll substitute for undecodable or invalid characters. -extern const char16_t kUnicodeReplacementCharacter; +extern const base_icu::UChar32 kUnicodeReplacementCharacter; // UTF-8 functions ------------------------------------------------------------ @@ -155,7 +156,7 @@ bool ReadUTFChar(const char* str, int* begin, int length, - unsigned* code_point_out); + base_icu::UChar32* code_point_out); // Generic To-UTF-8 converter. This will call the given append method for each // character that should be appended, with the given output method. Wrappers @@ -163,8 +164,10 @@ // // The char_value must have already been checked that it's a valid Unicode // character. -template<class Output, void Appender(unsigned char, Output*)> -inline void DoAppendUTF8(unsigned char_value, Output* output) { +template <class Output, void Appender(unsigned char, Output*)> +inline void DoAppendUTF8(base_icu::UChar32 char_value, Output* output) { + DCHECK(char_value >= 0); + DCHECK(char_value <= 0x10FFFF); if (char_value <= 0x7f) { Appender(static_cast<unsigned char>(char_value), output); } else if (char_value <= 0x7ff) { @@ -181,7 +184,7 @@ output); Appender(static_cast<unsigned char>(0x80 | (char_value & 0x3f)), output); - } else if (char_value <= 0x10FFFF) { // Max Unicode code point. + } else { // 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx Appender(static_cast<unsigned char>(0xf0 | (char_value >> 18)), output); @@ -189,11 +192,7 @@ output); Appender(static_cast<unsigned char>(0x80 | ((char_value >> 6) & 0x3f)), output); - Appender(static_cast<unsigned char>(0x80 | (char_value & 0x3f)), - output); - } else { - // Invalid UTF-8 character (>20 bits). - NOTREACHED(); + Appender(static_cast<unsigned char>(0x80 | (char_value & 0x3f)), output); } } @@ -207,7 +206,7 @@ // Writes the given character to the output as UTF-8. This does NO checking // of the validity of the Unicode characters; the caller should ensure that // the value it is appending is valid to append. -inline void AppendUTF8Value(unsigned char_value, CanonOutput* output) { +inline void AppendUTF8Value(base_icu::UChar32 char_value, CanonOutput* output) { DoAppendUTF8<CanonOutput, AppendCharToOutput>(char_value, output); } @@ -215,7 +214,8 @@ // characters (even when they are ASCII). This does NO checking of the // validity of the Unicode characters; the caller should ensure that the value // it is appending is valid to append. -inline void AppendUTF8EscapedValue(unsigned char_value, CanonOutput* output) { +inline void AppendUTF8EscapedValue(base_icu::UChar32 char_value, + CanonOutput* output) { DoAppendUTF8<CanonOutput, AppendEscapedChar>(char_value, output); } @@ -233,10 +233,10 @@ bool ReadUTFChar(const char16_t* str, int* begin, int length, - unsigned* code_point_out); + base_icu::UChar32* code_point_out); // Equivalent to U16_APPEND_UNSAFE in ICU but uses our output method. -inline void AppendUTF16Value(unsigned code_point, +inline void AppendUTF16Value(base_icu::UChar32 code_point, CanonOutputT<char16_t>* output) { if (code_point > 0xffff) { output->push_back(static_cast<char16_t>((code_point >> 10) + 0xd7c0)); @@ -274,7 +274,7 @@ // UTF-16 input. ReadUTFChar will handle invalid characters for us and give // us the kUnicodeReplacementCharacter, so we don't have to do special // checking after failure, just pass through the failure to the caller. - unsigned char_value; + base_icu::UChar32 char_value; bool success = ReadUTFChar(str, begin, length, &char_value); AppendUTF8EscapedValue(char_value, output); return success; @@ -286,7 +286,7 @@ // ReadUTF8Char will handle invalid characters for us and give us the // kUnicodeReplacementCharacter, so we don't have to do special checking // after failure, just pass through the failure to the caller. - unsigned ch; + base_icu::UChar32 ch; bool success = ReadUTFChar(str, begin, length, &ch); AppendUTF8EscapedValue(ch, output); return success;
diff --git a/url/url_util.cc b/url/url_util.cc index c9b72d3..1f0de84 100644 --- a/url/url_util.cc +++ b/url/url_util.cc
@@ -839,14 +839,14 @@ // next_ch will point to the last character of the decoded // character. int next_character = i; - unsigned code_point; + base_icu::UChar32 code_point; if (ReadUTFChar(unescaped_chars.data(), &next_character, unescaped_chars.length(), &code_point)) { // Valid UTF-8 character, convert to UTF-16. AppendUTF16Value(code_point, output); i = next_character; } else if (mode == DecodeURLMode::kUTF8) { - DCHECK_EQ(code_point, 0xFFFDU); + DCHECK_EQ(code_point, 0xFFFD); AppendUTF16Value(code_point, output); i = next_character; } else {
diff --git a/weblayer/grit_strings_allowlist.txt b/weblayer/grit_strings_allowlist.txt index 1e4b4ba77c..3b4dfe4f 100644 --- a/weblayer/grit_strings_allowlist.txt +++ b/weblayer/grit_strings_allowlist.txt
@@ -7,7 +7,6 @@ IDS_ACCESSIBILITY_EVENTS_INFOBAR_TEXT IDS_ACCESSIBILITY_EVENTS_PERMISSION_FRAGMENT IDS_AMBIENT_BADGE_INSTALL -IDS_AR_AND_MEDIA_CAPTURE_VIDEO_INFOBAR_TEXT IDS_AR_INFOBAR_TEXT IDS_AR_PERMISSION_FRAGMENT IDS_BEFORERELOAD_APP_MESSAGEBOX_TITLE