diff --git a/.eslintrc.js b/.eslintrc.js index be406d5..387c492 100644 --- a/.eslintrc.js +++ b/.eslintrc.js
@@ -17,6 +17,7 @@ 'brace-style': ['error', '1tbs'], 'curly': ['error', 'multi-line', 'consistent'], 'new-parens': 'error', + 'no-array-constructor': 'error', 'no-console': ['error', {allow: ['info', 'warn', 'error', 'assert']}], 'no-extra-boolean-cast': 'error', 'no-extra-semi': 'error',
diff --git a/DEPS b/DEPS index 99fc140..8da762d 100644 --- a/DEPS +++ b/DEPS
@@ -280,7 +280,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling Skia # and whatever else without interference from each other. - 'skia_revision': 'a10e6c1025274a5f2cc4958c39a102755b45bb34', + 'skia_revision': '2292a55e385c2c22ee4b0cf2e34fa4f5304b6241', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling V8 # and whatever else without interference from each other. @@ -359,7 +359,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling devtools-frontend # and whatever else without interference from each other. - 'devtools_frontend_revision': 'd2741940ef1cf0bc2572e52e7e9677b74166416d', + 'devtools_frontend_revision': '6c4b06be17e8a7ae237290a5589eacc6f89a47bf', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling libprotobuf-mutator # and whatever else without interference from each other. @@ -395,7 +395,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling feed # and whatever else without interference from each other. - 'dawn_revision': 'dadf11909a2efb8dea2d88261f6aa9715513d61c', + 'dawn_revision': '2f1b0dc47d8316f3db6e5b9a55b873cd3079cea5', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling feed # and whatever else without interference from each other. @@ -1133,7 +1133,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' + '@' + 'f2b987bce92c3db8f1bc6f8c482c45c62fedcedf', + 'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '2a5efd3e0fa04ba2f4408b1514e7211715480043', 'condition': 'checkout_chromeos', }, @@ -1156,7 +1156,7 @@ }, 'src/third_party/depot_tools': - Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '2f3c96d5e4eeef5bc0992fc7140c2d198b71f669', + Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '418f021a0eddf7a207d8e5b68cccf0588c3a9724', 'src/third_party/devtools-frontend/src': Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'), @@ -1553,7 +1553,7 @@ }, 'src/third_party/perfetto': - Var('android_git') + '/platform/external/perfetto.git' + '@' + '2a59c7427cfd780bcb98dfad01772b44468edc7a', + Var('android_git') + '/platform/external/perfetto.git' + '@' + '4d2a825326de4078f54adf4035ba2eff9901a052', 'src/third_party/perl': { 'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3', @@ -1796,7 +1796,7 @@ Var('chromium_git') + '/v8/v8.git' + '@' + Var('v8_revision'), 'src-internal': { - 'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@b30e642420249994535e6b8d9cc292b07d4034f7', + 'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@651b84b4189ae15e0d560044ed653dd0798f5bb3', 'condition': 'checkout_src_internal', }, @@ -1826,7 +1826,7 @@ 'packages': [ { 'package': 'chromeos_internal/apps/help_app/app', - 'version': '-6tfPiC-tl4DErnlvar3SY-mesNNq9Y4YR7Hq1Vb-F4C', + 'version': 'SIWoxdNNhtEW34ggbOoHdo5pi6nqFHv4keKLTelV8DYC', }, ], 'condition': 'checkout_chromeos and checkout_src_internal', @@ -1837,7 +1837,7 @@ 'packages': [ { 'package': 'chromeos_internal/apps/media_app/app', - 'version': 'WsmCtJ_DmkP5Pbw9a5dbqXendI4L_kIJX-S9ykYePXsC', + 'version': '3DDMcfjqwB9lHOJNbjxvwVfZ2QyJOwXFk65zXhh1kLcC', }, ], 'condition': 'checkout_chromeos and checkout_src_internal',
diff --git a/android_webview/browser/aw_feature_list_creator.cc b/android_webview/browser/aw_feature_list_creator.cc index f30a3a6d..a7ab41d 100644 --- a/android_webview/browser/aw_feature_list_creator.cc +++ b/android_webview/browser/aw_feature_list_creator.cc
@@ -47,6 +47,7 @@ #include "components/variations/pref_names.h" #include "components/variations/service/safe_seed_manager.h" #include "components/variations/service/variations_service.h" +#include "components/variations/variations_switches.h" #include "content/public/common/content_switch_dependent_feature_overrides.h" #include "net/base/features.h" #include "net/nqe/pref_names.h" @@ -247,14 +248,17 @@ aw_feature_entries::RegisterEnabledFeatureEntries(feature_list.get()); auto* metrics_client = AwMetricsServiceClient::GetInstance(); + const base::CommandLine* command_line = + base::CommandLine::ForCurrentProcess(); // Populate FieldTrialList. Since |low_entropy_provider| is null, it will fall // back to the provider we previously gave to FieldTrialList, which is a low // entropy provider. The X-Client-Data header is not reported on WebView, so // we pass an empty object as the |low_entropy_source_value|. variations_field_trial_creator_->SetUpFieldTrials( variation_ids, - GetSwitchDependentFeatureOverrides( - *base::CommandLine::ForCurrentProcess()), + command_line->GetSwitchValueASCII( + variations::switches::kForceVariationIds), + GetSwitchDependentFeatureOverrides(*command_line), /*low_entropy_provider=*/nullptr, std::move(feature_list), metrics_client->metrics_state_manager(), aw_field_trials_.get(), &ignored_safe_seed_manager, /*low_entropy_source_value=*/absl::nullopt);
diff --git a/ash/BUILD.gn b/ash/BUILD.gn index abf8ba9..5bb0d3d 100644 --- a/ash/BUILD.gn +++ b/ash/BUILD.gn
@@ -1889,6 +1889,8 @@ "wm/event_client_impl.h", "wm/float/float_controller.cc", "wm/float/float_controller.h", + "wm/float/tablet_mode_float_window_resizer.cc", + "wm/float/tablet_mode_float_window_resizer.h", "wm/fullscreen_window_finder.cc", "wm/fullscreen_window_finder.h", "wm/gestures/back_gesture/back_gesture_affordance.cc",
diff --git a/ash/accelerators/accelerator_controller_impl.cc b/ash/accelerators/accelerator_controller_impl.cc index 91d98f0..84a5e95 100644 --- a/ash/accelerators/accelerator_controller_impl.cc +++ b/ash/accelerators/accelerator_controller_impl.cc
@@ -824,7 +824,7 @@ DCHECK(chromeos::wm::features::IsFloatWindowEnabled()); aura::Window* window = window_util::GetActiveWindow(); DCHECK(window); - // TODO(sammiequon|shidi): Add some UI like a bounce if a window cannnot be + // TODO(sammiequon|shidi): Add some UI like a bounce if a window cannot be // floated. chromeos::ToggleFloating(window); base::RecordAction(UserMetricsAction("Accel_Toggle_Floating"));
diff --git a/ash/frame/non_client_frame_view_ash.cc b/ash/frame/non_client_frame_view_ash.cc index 4143595..b273b09 100644 --- a/ash/frame/non_client_frame_view_ash.cc +++ b/ash/frame/non_client_frame_view_ash.cc
@@ -98,7 +98,8 @@ if (window_state_->IsFullscreen()) return; if (Shell::Get()->tablet_mode_controller()->ShouldAutoHideTitlebars( - widget_)) { + widget_) && + !window_state_->IsFloated()) { ImmersiveFullscreenController::EnableForWidget(widget_, true); } } @@ -127,7 +128,7 @@ Shell::Get()->tablet_mode_controller() && Shell::Get()->tablet_mode_controller()->ShouldAutoHideTitlebars( widget)) { - if (window_state->IsMinimized()) + if (window_state->IsMinimized() || window_state->IsFloated()) ImmersiveFullscreenController::EnableForWidget(widget_, false); else if (window_state->IsMaximized()) ImmersiveFullscreenController::EnableForWidget(widget_, true);
diff --git a/ash/login/ui/login_expanded_public_account_view_unittest.cc b/ash/login/ui/login_expanded_public_account_view_unittest.cc index 8ec136c..b5d2c8c 100644 --- a/ash/login/ui/login_expanded_public_account_view_unittest.cc +++ b/ash/login/ui/login_expanded_public_account_view_unittest.cc
@@ -18,7 +18,7 @@ #include "testing/gtest/include/gtest/gtest.h" #include "ui/base/l10n/l10n_util.h" #include "ui/events/test/event_generator.h" -#include "ui/views/controls/link.h" +#include "ui/views/controls/link_fragment.h" #include "ui/views/layout/box_layout_view.h" #include "ui/views/test/combobox_test_api.h" #include "ui/views/widget/widget.h" @@ -192,8 +192,9 @@ // Tap on the learn more link. const auto& children = test_api.learn_more_label()->children(); - const auto it = base::ranges::find(children, views::Link::kViewClassName, - &views::View::GetClassName); + const auto it = + base::ranges::find(children, views::LinkFragment::kViewClassName, + &views::View::GetClassName); DCHECK(it != children.cend()); TapOnView(*it); ASSERT_NE(test_api.learn_more_dialog(), nullptr);
diff --git a/ash/public/cpp/wallpaper/wallpaper_controller_client.h b/ash/public/cpp/wallpaper/wallpaper_controller_client.h index 40293427..95cde1d3 100644 --- a/ash/public/cpp/wallpaper/wallpaper_controller_client.h +++ b/ash/public/cpp/wallpaper/wallpaper_controller_client.h
@@ -28,10 +28,6 @@ // Opens the wallpaper picker window. virtual void OpenWallpaperPicker() = 0; - // Closes the app side of the wallpaper preview (top header bar) if it is - // currently open. - virtual void MaybeClosePreviewWallpaper() = 0; - // Sets the default wallpaper and removes the file for the previous wallpaper. virtual void SetDefaultWallpaper( const AccountId& account_id,
diff --git a/ash/wallpaper/test_wallpaper_controller_client.cc b/ash/wallpaper/test_wallpaper_controller_client.cc index 8a723e1..dd9ce9b 100644 --- a/ash/wallpaper/test_wallpaper_controller_client.cc +++ b/ash/wallpaper/test_wallpaper_controller_client.cc
@@ -45,7 +45,6 @@ void TestWallpaperControllerClient::ResetCounts() { open_count_ = 0; - close_preview_count_ = 0; set_default_wallpaper_count_ = 0; migrate_collection_id_from_chrome_app_count_ = 0; fetch_daily_refresh_wallpaper_param_ = std::string(); @@ -61,10 +60,6 @@ open_count_++; } -void TestWallpaperControllerClient::MaybeClosePreviewWallpaper() { - close_preview_count_++; -} - void TestWallpaperControllerClient::SetDefaultWallpaper( const AccountId& account_id, bool show_wallpaper,
diff --git a/ash/wallpaper/test_wallpaper_controller_client.h b/ash/wallpaper/test_wallpaper_controller_client.h index 47dfb662..cdcb20c 100644 --- a/ash/wallpaper/test_wallpaper_controller_client.h +++ b/ash/wallpaper/test_wallpaper_controller_client.h
@@ -35,7 +35,6 @@ const std::vector<backdrop::Image>& images); size_t open_count() const { return open_count_; } - size_t close_preview_count() const { return close_preview_count_; } size_t set_default_wallpaper_count() const { return set_default_wallpaper_count_; } @@ -80,7 +79,6 @@ // WallpaperControllerClient: void OpenWallpaperPicker() override; - void MaybeClosePreviewWallpaper() override; void SetDefaultWallpaper( const AccountId& account_id, bool show_wallpaper, @@ -117,7 +115,6 @@ private: size_t open_count_ = 0; - size_t close_preview_count_ = 0; size_t set_default_wallpaper_count_ = 0; size_t migrate_collection_id_from_chrome_app_count_ = 0; size_t fetch_images_for_collection_count_ = 0;
diff --git a/ash/wallpaper/wallpaper_controller_impl.cc b/ash/wallpaper/wallpaper_controller_impl.cc index 0b935e6..8e2258e8 100644 --- a/ash/wallpaper/wallpaper_controller_impl.cc +++ b/ash/wallpaper/wallpaper_controller_impl.cc
@@ -764,9 +764,6 @@ DCHECK(!reload_preview_wallpaper_callback_); return; } - // May be null in tests. - if (wallpaper_controller_client_) - wallpaper_controller_client_->MaybeClosePreviewWallpaper(); CancelPreviewWallpaper(); }
diff --git a/ash/wallpaper/wallpaper_controller_impl.h b/ash/wallpaper/wallpaper_controller_impl.h index 348f6e5..98aa3d8 100644 --- a/ash/wallpaper/wallpaper_controller_impl.h +++ b/ash/wallpaper/wallpaper_controller_impl.h
@@ -157,10 +157,8 @@ // always return true thereafter. bool HasShownAnyWallpaper() const; - // Ash cannot close the chrome side of the wallpaper preview so this function - // tells the chrome side to do so. Also Ash cannot tell whether or not the - // wallpaper picker is currently open so this will close the wallpaper preview - // if it is open and do nothing if it is not open. + // Exit wallpaper preview state if it is open and do nothing if it is not + // open. void MaybeClosePreviewWallpaper(); // Shows the wallpaper and alerts observers of changes.
diff --git a/ash/wallpaper/wallpaper_controller_unittest.cc b/ash/wallpaper/wallpaper_controller_unittest.cc index 6170c10..bfcbdd2 100644 --- a/ash/wallpaper/wallpaper_controller_unittest.cc +++ b/ash/wallpaper/wallpaper_controller_unittest.cc
@@ -387,11 +387,20 @@ void OnWallpaperColorsChanged() override { ++colors_changed_count_; } void OnWallpaperBlurChanged() override { ++blur_changed_count_; } void OnFirstWallpaperShown() override { ++first_shown_count_; } + void OnWallpaperPreviewStarted() override { + DCHECK(!is_in_wallpaper_preview_); + is_in_wallpaper_preview_ = true; + } + void OnWallpaperPreviewEnded() override { + DCHECK(is_in_wallpaper_preview_); + is_in_wallpaper_preview_ = false; + } int colors_changed_count() const { return colors_changed_count_; } int blur_changed_count() const { return blur_changed_count_; } int first_shown_count() const { return first_shown_count_; } int wallpaper_changed_count() const { return wallpaper_changed_count_; } + bool is_in_wallpaper_preview() const { return is_in_wallpaper_preview_; } private: WallpaperController* controller_; @@ -399,6 +408,7 @@ int blur_changed_count_ = 0; int first_shown_count_ = 0; int wallpaper_changed_count_ = 0; + bool is_in_wallpaper_preview_ = false; }; } // namespace @@ -2531,9 +2541,12 @@ gfx::ImageSkia custom_wallpaper = CreateImage(640, 480, kWallpaperColor); EXPECT_NE(kWallpaperColor, GetWallpaperColor()); ClearWallpaperCount(); + + TestWallpaperControllerObserver observer(controller_); controller_->SetCustomWallpaper(account_id_1, file_name_1, layout, custom_wallpaper, true /*preview_mode=*/); RunAllTasksUntilIdle(); + EXPECT_TRUE(observer.is_in_wallpaper_preview()); EXPECT_EQ(1, GetWallpaperCount()); EXPECT_EQ(kWallpaperColor, GetWallpaperColor()); // Verify that the user wallpaper info remains unchanged during the preview. @@ -2547,12 +2560,12 @@ ClearWallpaperCount(); EnterOverview(); RunAllTasksUntilIdle(); + EXPECT_FALSE(observer.is_in_wallpaper_preview()); EXPECT_EQ(1, GetWallpaperCount()); EXPECT_NE(kWallpaperColor, GetWallpaperColor()); EXPECT_EQ(controller_->GetWallpaperType(), WallpaperType::kDefault); EXPECT_EQ(user_wallpaper_info, default_wallpaper_info); EXPECT_TRUE(Shell::Get()->overview_controller()->InOverviewSession()); - EXPECT_EQ(1u, client_.close_preview_count()); } TEST_F(WallpaperControllerTest, ClosePreviewWallpaperOnWindowCycleStart) { @@ -2576,6 +2589,8 @@ CreateTestWindow(gfx::Rect(0, 0, 100, 100))); WindowState::Get(wallpaper_picker_window.get())->Activate(); + TestWallpaperControllerObserver observer(controller_); + // Set a custom wallpaper for the user and enable preview. Verify that the // wallpaper is changed to the expected color. const WallpaperLayout layout = WALLPAPER_LAYOUT_CENTER; @@ -2585,6 +2600,7 @@ controller_->SetCustomWallpaper(account_id_1, file_name_1, layout, custom_wallpaper, true /*preview_mode=*/); RunAllTasksUntilIdle(); + EXPECT_TRUE(observer.is_in_wallpaper_preview()); EXPECT_EQ(1, GetWallpaperCount()); EXPECT_EQ(kWallpaperColor, GetWallpaperColor()); // Verify that the user wallpaper info remains unchanged during the preview. @@ -2598,12 +2614,12 @@ Shell::Get()->window_cycle_controller()->HandleCycleWindow( WindowCycleController::WindowCyclingDirection::kForward); RunAllTasksUntilIdle(); + EXPECT_FALSE(observer.is_in_wallpaper_preview()); EXPECT_EQ(1, GetWallpaperCount()); EXPECT_NE(kWallpaperColor, GetWallpaperColor()); EXPECT_EQ(controller_->GetWallpaperType(), WallpaperType::kDefault); EXPECT_EQ(user_wallpaper_info, default_wallpaper_info); EXPECT_TRUE(Shell::Get()->window_cycle_controller()->IsCycling()); - EXPECT_EQ(1u, client_.close_preview_count()); } TEST_F(WallpaperControllerTest, @@ -2628,6 +2644,8 @@ CreateTestWindow(gfx::Rect(0, 0, 100, 100))); WindowState::Get(wallpaper_picker_window.get())->Activate(); + TestWallpaperControllerObserver observer(controller_); + // Set a custom wallpaper for the user and enable preview. Verify that the // wallpaper is changed to the expected color. const WallpaperLayout layout = WALLPAPER_LAYOUT_CENTER; @@ -2637,6 +2655,7 @@ controller_->SetCustomWallpaper(account_id_1, file_name_1, layout, custom_wallpaper, true /*preview_mode=*/); RunAllTasksUntilIdle(); + EXPECT_TRUE(observer.is_in_wallpaper_preview()); EXPECT_EQ(1, GetWallpaperCount()); EXPECT_EQ(kWallpaperColor, GetWallpaperColor()); // Verify that the user wallpaper info remains unchanged during the preview. @@ -2650,13 +2669,13 @@ SimulateUserLogin(account_id_2); controller_->ShowUserWallpaper(account_id_2); RunAllTasksUntilIdle(); + EXPECT_FALSE(observer.is_in_wallpaper_preview()); EXPECT_EQ(1, GetWallpaperCount()); EXPECT_NE(kWallpaperColor, GetWallpaperColor()); EXPECT_EQ(controller_->GetWallpaperType(), WallpaperType::kDefault); EXPECT_TRUE( controller_->GetUserWallpaperInfo(account_id_2, &user_wallpaper_info)); EXPECT_EQ(user_wallpaper_info, default_wallpaper_info); - EXPECT_EQ(1u, client_.close_preview_count()); } TEST_F(WallpaperControllerTest, ConfirmPreviewWallpaper) {
diff --git a/ash/webui/os_feedback_ui/untrusted_resources/untrusted_index.html b/ash/webui/os_feedback_ui/untrusted_resources/untrusted_index.html index 3a1da19..eb5b3719 100644 --- a/ash/webui/os_feedback_ui/untrusted_resources/untrusted_index.html +++ b/ash/webui/os_feedback_ui/untrusted_resources/untrusted_index.html
@@ -12,6 +12,9 @@ margin: 0; } </style> + <link rel="stylesheet" href="//resources/chromeos/colors/cros_styles.css"> + <link rel="stylesheet" href="//resources/css/text_defaults.css"> + <link rel="stylesheet" href="//resources/css/md_colors.css"> </head> <body>
diff --git a/ash/webui/personalization_app/resources/trusted/keyboard_backlight/keyboard_backlight_element.html b/ash/webui/personalization_app/resources/trusted/keyboard_backlight/keyboard_backlight_element.html index 2f69a36..23b896efa 100644 --- a/ash/webui/personalization_app/resources/trusted/keyboard_backlight/keyboard_backlight_element.html +++ b/ash/webui/personalization_app/resources/trusted/keyboard_backlight/keyboard_backlight_element.html
@@ -9,7 +9,7 @@ '. label .' '. options .' '. . .'; - grid-template-columns: 8px 1fr 20px; + grid-template-columns: 12px 1fr 20px; grid-template-rows: auto 1fr 20px; } @@ -22,7 +22,7 @@ grid-area: label; justify-content: space-between; margin-block-start: 20px; - margin-inline-start: 12px; + margin-inline-start: 8px; } #keyboardBacklightLabel > p { @@ -72,7 +72,7 @@ } .color-inner-container svg { - fill: var(--cros-text-color-primary); + fill: var(--cros-icon-color-primary); } .color-container:focus-visible {
diff --git a/ash/wm/float/float_controller_unittest.cc b/ash/wm/float/float_controller_unittest.cc index b812a1c..d3204094 100644 --- a/ash/wm/float/float_controller_unittest.cc +++ b/ash/wm/float/float_controller_unittest.cc
@@ -10,6 +10,7 @@ #include "ash/test/ash_test_base.h" #include "ash/wm/tablet_mode/tablet_mode_controller.h" #include "base/test/scoped_feature_list.h" +#include "chromeos/ui/frame/immersive/immersive_fullscreen_controller.h" #include "chromeos/ui/wm/features.h" #include "ui/aura/test/test_window_delegate.h" #include "ui/wm/core/window_util.h" @@ -130,4 +131,26 @@ EXPECT_FALSE(controller->IsFloated(window.get())); } +// Tests that windows floated in tablet mode have immersive mode disabled, +// showing their title bars. +TEST_F(WindowFloatTest, TabletImmersiveMode) { + // Create a test app window that has a header. + auto window = CreateAppWindow(); + auto* immersive_controller = chromeos::ImmersiveFullscreenController::Get( + views::Widget::GetWidgetForNativeView(window.get())); + + // Enter tablet mode and float `window`. + Shell::Get()->tablet_mode_controller()->SetEnabledForTest(true); + EXPECT_TRUE(immersive_controller->IsEnabled()); + + PressAndReleaseKey(ui::VKEY_F, ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN); + EXPECT_FALSE(immersive_controller->IsEnabled()); + + PressAndReleaseKey(ui::VKEY_F, ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN); + EXPECT_TRUE(immersive_controller->IsEnabled()); + + // TODO(crbug.com/1339489): Add tests to check immersive mode when transition + // to tablet from clamshell and vice versa. +} + } // namespace ash
diff --git a/ash/wm/float/tablet_mode_float_window_resizer.cc b/ash/wm/float/tablet_mode_float_window_resizer.cc new file mode 100644 index 0000000..65aa07d --- /dev/null +++ b/ash/wm/float/tablet_mode_float_window_resizer.cc
@@ -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. + +#include "ash/wm/float/tablet_mode_float_window_resizer.h" + +#include "ash/wm/window_state.h" +#include "chromeos/ui/wm/features.h" +#include "ui/aura/window.h" + +namespace ash { + +TabletModeFloatWindowResizer::TabletModeFloatWindowResizer( + WindowState* window_state) + : WindowResizer(window_state) { + DCHECK(chromeos::wm::features::IsFloatWindowEnabled()); +} + +TabletModeFloatWindowResizer::~TabletModeFloatWindowResizer() { + window_state_->DeleteDragDetails(); +} + +void TabletModeFloatWindowResizer::Drag(const gfx::PointF& location_in_parent, + int event_flags) { + gfx::Rect bounds = CalculateBoundsForDrag(location_in_parent); + if (bounds != GetTarget()->bounds()) + SetBoundsDuringResize(bounds); +} + +void TabletModeFloatWindowResizer::CompleteDrag() { + // Keep the bounds where it was last dragged to. + // TODO(crbug.com/1338715): Magnetize to the closest corner on release. +} + +void TabletModeFloatWindowResizer::RevertDrag() { + GetTarget()->SetBounds(details().initial_bounds_in_parent); +} + +void TabletModeFloatWindowResizer::FlingOrSwipe(ui::GestureEvent* event) {} + +} // namespace ash
diff --git a/ash/wm/float/tablet_mode_float_window_resizer.h b/ash/wm/float/tablet_mode_float_window_resizer.h new file mode 100644 index 0000000..31d5f815 --- /dev/null +++ b/ash/wm/float/tablet_mode_float_window_resizer.h
@@ -0,0 +1,34 @@ +// 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 ASH_WM_FLOAT_TABLET_MODE_FLOAT_WINDOW_RESIZER_H_ +#define ASH_WM_FLOAT_TABLET_MODE_FLOAT_WINDOW_RESIZER_H_ + +#include "ash/wm/window_resizer.h" + +namespace ash { + +class WindowState; + +// WindowResizer implementation for floated windows in tablet mode. +// TODO(crbug.com/1338715): This resizer adds the most basic dragging. It needs +// to stick to edges and magnetize to corners on release. +class TabletModeFloatWindowResizer : public WindowResizer { + public: + explicit TabletModeFloatWindowResizer(WindowState* window_state); + TabletModeFloatWindowResizer(const TabletModeFloatWindowResizer&) = delete; + TabletModeFloatWindowResizer& operator=(const TabletModeFloatWindowResizer&) = + delete; + ~TabletModeFloatWindowResizer() override; + + // WindowResizer: + void Drag(const gfx::PointF& location_in_parent, int event_flags) override; + void CompleteDrag() override; + void RevertDrag() override; + void FlingOrSwipe(ui::GestureEvent* event) override; +}; + +} // namespace ash + +#endif // ASH_WM_FLOAT_TABLET_MODE_FLOAT_WINDOW_RESIZER_H_
diff --git a/ash/wm/workspace/workspace_window_resizer.cc b/ash/wm/workspace/workspace_window_resizer.cc index 244a4ea..4988e8c 100644 --- a/ash/wm/workspace/workspace_window_resizer.cc +++ b/ash/wm/workspace/workspace_window_resizer.cc
@@ -21,6 +21,7 @@ #include "ash/wm/default_window_resizer.h" #include "ash/wm/desks/desks_util.h" #include "ash/wm/drag_window_resizer.h" +#include "ash/wm/float/tablet_mode_float_window_resizer.h" #include "ash/wm/overview/overview_controller.h" #include "ash/wm/pip/pip_window_resizer.h" #include "ash/wm/tablet_mode/tablet_mode_browser_window_drag_delegate.h" @@ -299,14 +300,23 @@ aura::Window* window, const gfx::PointF& point_in_parent, int window_component, - ::wm::WindowMoveSource source) { + wm::WindowMoveSource source) { + WindowState* window_state = WindowState::Get(window); + + // Dragging floated windows in tablet mode is allowed. + // TODO(crbug.com/1338715): Investigate if we need to wrap the resizer in a + // DragWindowResizer. + if (window_state->IsFloated() && window_component == HTCAPTION) { + window_state->CreateDragDetails(point_in_parent, HTCAPTION, source); + return std::make_unique<TabletModeFloatWindowResizer>(window_state); + } + // Window dragging from top and tab dragging are disabled if "WebUITabStrip" // feature is enabled. "WebUITabStrip" will be enabled on 81 for Krane and on // 82 for all other boards. if (features::IsWebUITabStripEnabled()) return nullptr; - WindowState* window_state = WindowState::Get(window); // Only maximized/fullscreen/snapped window can be dragged from the top of // the screen. if (!window_state->IsMaximized() && !window_state->IsFullscreen() &&
diff --git a/base/check.cc b/base/check.cc index a62f5fc..349579b 100644 --- a/base/check.cc +++ b/base/check.cc
@@ -14,11 +14,71 @@ #endif #include "base/check_op.h" +#include "base/debug/dump_without_crashing.h" #include "base/logging.h" +#include "base/thread_annotations.h" #include "build/build_config.h" +#include <atomic> + namespace logging { +namespace { + +#if defined(DCHECK_IS_CONFIGURABLE) +void DCheckDumpOnceWithoutCrashing() { + // Best-effort gate to prevent multiple DCHECKs from being dumped. This will + // race if multiple threads DCHECK at the same time, but we'll eventually stop + // reporting and at most report once per thread. + static std::atomic<bool> has_dumped = false; + if (!has_dumped.load(std::memory_order_relaxed)) { + // Note that dumping may fail if the crash handler hasn't been set yet. In + // that case we want to try again on the next failing DCHECK. + if (base::debug::DumpWithoutCrashingUnthrottled()) + has_dumped.store(true, std::memory_order_relaxed); + } +} + +class DCheckLogMessage : public LogMessage { + public: + using LogMessage::LogMessage; + ~DCheckLogMessage() override { + if (severity() != logging::LOGGING_FATAL) + DCheckDumpOnceWithoutCrashing(); + } +}; + +#if BUILDFLAG(IS_WIN) +class DCheckWin32ErrorLogMessage : public Win32ErrorLogMessage { + public: + using Win32ErrorLogMessage::Win32ErrorLogMessage; + ~DCheckWin32ErrorLogMessage() override { + if (severity() != logging::LOGGING_FATAL) + DCheckDumpOnceWithoutCrashing(); + } +}; +#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) +class DCheckErrnoLogMessage : public ErrnoLogMessage { + public: + using ErrnoLogMessage::ErrnoLogMessage; + ~DCheckErrnoLogMessage() override { + if (severity() != logging::LOGGING_FATAL) + DCheckDumpOnceWithoutCrashing(); + } +}; +#endif // BUILDFLAG(IS_WIN) +#else +static_assert(logging::LOGGING_DCHECK == logging::LOGGING_FATAL); +typedef LogMessage DCheckLogMessage; +#if BUILDFLAG(IS_WIN) +typedef Win32ErrorLogMessage DCheckWin32ErrorLogMessage; +#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) +typedef ErrnoLogMessage DCheckErrnoLogMessage; +#endif // BUILDFLAG(IS_WIN) +#endif // defined(DCHECK_IS_CONFIGURABLE) + +} // namespace + CheckError CheckError::Check(const char* file, int line, const char* condition) { @@ -40,7 +100,7 @@ CheckError CheckError::DCheck(const char* file, int line, const char* condition) { - auto* const log_message = new LogMessage(file, line, LOGGING_DCHECK); + auto* const log_message = new DCheckLogMessage(file, line, LOGGING_DCHECK); log_message->stream() << "Check failed: " << condition << ". "; return CheckError(log_message); } @@ -48,7 +108,7 @@ CheckError CheckError::DCheckOp(const char* file, int line, CheckOpResult* check_op_result) { - auto* const log_message = new LogMessage(file, line, LOGGING_DCHECK); + auto* const log_message = new DCheckLogMessage(file, line, LOGGING_DCHECK); log_message->stream() << "Check failed: " << check_op_result->message_; free(check_op_result->message_); check_op_result->message_ = nullptr; @@ -80,10 +140,10 @@ SystemErrorCode err_code = logging::GetLastSystemErrorCode(); #if BUILDFLAG(IS_WIN) auto* const log_message = - new Win32ErrorLogMessage(file, line, LOGGING_DCHECK, err_code); + new DCheckWin32ErrorLogMessage(file, line, LOGGING_DCHECK, err_code); #elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) auto* const log_message = - new ErrnoLogMessage(file, line, LOGGING_DCHECK, err_code); + new DCheckErrnoLogMessage(file, line, LOGGING_DCHECK, err_code); #endif log_message->stream() << "Check failed: " << condition << ". "; return CheckError(log_message);
diff --git a/cc/animation/animation_host_unittest.cc b/cc/animation/animation_host_unittest.cc index fa79bed..bebb3bc 100644 --- a/cc/animation/animation_host_unittest.cc +++ b/cc/animation/animation_host_unittest.cc
@@ -337,9 +337,7 @@ // Create scroll timeline that links scroll animation and worklet animation // together. - std::vector<double> scroll_offsets; - scroll_offsets.push_back(0); - scroll_offsets.push_back(100); + ScrollTimeline::ScrollOffsets scroll_offsets(0, 100); auto scroll_timeline = ScrollTimeline::Create( element_id, ScrollTimeline::ScrollDown, scroll_offsets); @@ -380,9 +378,7 @@ // Create scroll timeline that links scroll animation and scroll-linked // animation together. - std::vector<double> scroll_offsets; - scroll_offsets.push_back(0); - scroll_offsets.push_back(100); + ScrollTimeline::ScrollOffsets scroll_offsets(0, 100); auto scroll_timeline = ScrollTimeline::Create( element_id_, ScrollTimeline::ScrollDown, scroll_offsets); @@ -458,10 +454,7 @@ timeline_->AttachAnimation(mock_scroll_animation); host_impl_->AddToTicking(mock_scroll_animation); - std::vector<double> scroll_offsets; - scroll_offsets.push_back(0); - scroll_offsets.push_back(100); - + ScrollTimeline::ScrollOffsets scroll_offsets(0, 100); auto scroll_timeline = ScrollTimeline::Create( element_id_, ScrollTimeline::ScrollDown, scroll_offsets);
diff --git a/cc/animation/scroll_offset_animation_curve_factory.cc b/cc/animation/scroll_offset_animation_curve_factory.cc index 761b549..553bffe 100644 --- a/cc/animation/scroll_offset_animation_curve_factory.cc +++ b/cc/animation/scroll_offset_animation_curve_factory.cc
@@ -35,7 +35,7 @@ if (scroll_type == ScrollType::kAutoScroll) return CreateLinearAnimation(target_value); - if (base::FeatureList::IsEnabled(features::kImpulseScrollAnimations)) + if (features::IsImpulseScrollAnimationEnabled()) return CreateImpulseAnimation(target_value); return CreateEaseInOutAnimation(
diff --git a/cc/animation/scroll_timeline.cc b/cc/animation/scroll_timeline.cc index f8d2481..06f54a4 100644 --- a/cc/animation/scroll_timeline.cc +++ b/cc/animation/scroll_timeline.cc
@@ -27,33 +27,23 @@ direction == ScrollTimeline::ScrollLeft; } -bool ValidateScrollOffsets(const std::vector<double>& scroll_offsets) { - return scroll_offsets.empty() || scroll_offsets.size() >= 2.0; -} - } // namespace -template double ComputeProgress<std::vector<double>>( - double, - const std::vector<double>&); - ScrollTimeline::ScrollTimeline(absl::optional<ElementId> scroller_id, ScrollDirection direction, - const std::vector<double> scroll_offsets, + absl::optional<ScrollOffsets> scroll_offsets, int animation_timeline_id) : AnimationTimeline(animation_timeline_id), pending_id_(scroller_id), direction_(direction), - scroll_offsets_(scroll_offsets) { - DCHECK(ValidateScrollOffsets(scroll_offsets_)); -} + pending_offsets_(scroll_offsets) {} ScrollTimeline::~ScrollTimeline() = default; scoped_refptr<ScrollTimeline> ScrollTimeline::Create( absl::optional<ElementId> scroller_id, ScrollTimeline::ScrollDirection direction, - const std::vector<double> scroll_offsets) { + absl::optional<ScrollOffsets> scroll_offsets) { return base::WrapRefCounted( new ScrollTimeline(scroller_id, direction, scroll_offsets, AnimationIdProvider::NextTimelineId())); @@ -61,15 +51,16 @@ scoped_refptr<AnimationTimeline> ScrollTimeline::CreateImplInstance() const { return base::WrapRefCounted( - new ScrollTimeline(pending_id_, direction_, scroll_offsets_, id())); + new ScrollTimeline(pending_id_, direction_, pending_offsets_, id())); } bool ScrollTimeline::IsActive(const ScrollTree& scroll_tree, bool is_active_tree) const { // Blink passes empty scroll offsets when the timeline is inactive. - if (scroll_offsets_.empty()) { + if ((is_active_tree && !active_offsets_) || + (!is_active_tree && !pending_offsets_)) return false; - } + // If pending tree with our scroller hasn't been activated, or the scroller // has been removed (e.g. if it is no longer composited). if ((is_active_tree && !active_id_) || (!is_active_tree && !pending_id_)) @@ -113,35 +104,26 @@ DCHECK_GE(max_offset, 0); DCHECK_GE(current_offset, 0); - DCHECK_GE(scroll_offsets_.size(), 2u); - double resolved_start_scroll_offset = scroll_offsets_[0]; - double resolved_end_scroll_offset = - scroll_offsets_[scroll_offsets_.size() - 1]; - - // TODO(crbug.com/1060384): Once the spec has been updated to state what the - // expected result is when startScrollOffset >= endScrollOffset, we might need - // to add a special case here. See - // https://github.com/WICG/scroll-animations/issues/20 - - // 3. If current scroll offset is less than startScrollOffset: - if (current_offset < resolved_start_scroll_offset) { - return base::TimeTicks(); + double start_offset = 0; + double end_offset = 0; + if (is_active_tree) { + DCHECK(active_offsets_); + start_offset = active_offsets_->start; + end_offset = active_offsets_->end; + } else { + DCHECK(pending_offsets_); + start_offset = pending_offsets_->start; + end_offset = pending_offsets_->end; } - // 4. If current scroll offset is greater than or equal to endScrollOffset: - if (current_offset >= resolved_end_scroll_offset) { - return base::TimeTicks() + base::Milliseconds(kScrollTimelineDurationMs); - } - - // Otherwise, - // 5.1 Let progress be a result of applying calculate scroll timeline progress - // procedure for current scroll offset. - // 5.2 The current time is the result of evaluating the following expression: - // progress × timeline duration to get the percentage + // TODO(crbug.com/1338167): Update once + // github.com/w3c/csswg-drafts/issues/7401 is resolved. + double progress = + end_offset == start_offset + ? 1 + : (current_offset - start_offset) / (end_offset - start_offset); return base::TimeTicks() + - base::Milliseconds(ComputeProgress<std::vector<double>>( - current_offset, scroll_offsets_) * - kScrollTimelineDurationMs); + base::Milliseconds(progress * kScrollTimelineDurationMs); } void ScrollTimeline::PushPropertiesTo(AnimationTimeline* impl_timeline) { @@ -149,16 +131,12 @@ DCHECK(impl_timeline); ScrollTimeline* scroll_timeline = ToScrollTimeline(impl_timeline); scroll_timeline->pending_id_ = pending_id_; - // TODO(smcgruer): This leads to incorrect behavior in the current design, - // because we end up using the pending start/end scroll offset for the active - // tree too. Instead we need to either split these (like pending_id_ and - // active_id_) or have a ScrollTimeline per tree. - scroll_timeline->scroll_offsets_ = scroll_offsets_; - DCHECK(ValidateScrollOffsets(scroll_timeline->scroll_offsets_)); + scroll_timeline->pending_offsets_ = pending_offsets_; } void ScrollTimeline::ActivateTimeline() { active_id_ = pending_id_; + active_offsets_ = pending_offsets_; for (auto& kv : id_to_animation_map_) { auto& animation = kv.second; if (animation->IsWorkletAnimation()) @@ -200,17 +178,15 @@ void ScrollTimeline::UpdateScrollerIdAndScrollOffsets( absl::optional<ElementId> pending_id, - const std::vector<double> scroll_offsets) { - if (pending_id_ == pending_id && scroll_offsets_ == scroll_offsets) { + absl::optional<ScrollOffsets> pending_offsets) { + if (pending_id_ == pending_id && pending_offsets_ == pending_offsets) return; - } // When the scroller id changes it will first be modified in the pending tree. // Then later (when the pending tree is promoted to active) // |ActivateTimeline| will be called and will set the |active_id_|. pending_id_ = pending_id; - scroll_offsets_ = scroll_offsets; - DCHECK(ValidateScrollOffsets(scroll_offsets_)); + pending_offsets_ = pending_offsets; SetNeedsPushProperties(); }
diff --git a/cc/animation/scroll_timeline.h b/cc/animation/scroll_timeline.h index a654855..482a874 100644 --- a/cc/animation/scroll_timeline.h +++ b/cc/animation/scroll_timeline.h
@@ -35,19 +35,31 @@ ScrollRight, }; + struct ScrollOffsets { + ScrollOffsets(double start_offset, double end_offset) { + start = start_offset; + end = end_offset; + } + bool operator==(const ScrollOffsets& other) const { + return start == other.start && end == other.end; + } + double start = 0; + double end = 0; + }; + // 100% is represented as 100s or 100000ms. We store it here in Milliseconds // because that is the time unit returned by functions like CurrentTime. static constexpr double kScrollTimelineDurationMs = 100000; ScrollTimeline(absl::optional<ElementId> scroller_id, ScrollDirection direction, - const std::vector<double> scroll_offsets, + absl::optional<ScrollOffsets> scroll_offsets, int animation_timeline_id); static scoped_refptr<ScrollTimeline> Create( absl::optional<ElementId> scroller_id, ScrollDirection direction, - const std::vector<double> scroll_offsets); + absl::optional<ScrollOffsets> scroll_offsets); // Create a copy of this ScrollTimeline intended for the impl thread in the // compositor. @@ -68,7 +80,7 @@ void UpdateScrollerIdAndScrollOffsets( absl::optional<ElementId> scroller_id, - const std::vector<double> scroll_offsets); + absl::optional<ScrollOffsets> scroll_offsets); void PushPropertiesTo(AnimationTimeline* impl_timeline) override; void ActivateTimeline() override; @@ -82,14 +94,14 @@ absl::optional<ElementId> GetPendingIdForTest() const { return pending_id_; } ScrollDirection GetDirectionForTest() const { return direction_; } absl::optional<double> GetStartScrollOffsetForTest() const { - if (scroll_offsets_.empty()) + if (!pending_offsets_) return absl::nullopt; - return scroll_offsets_[0]; + return pending_offsets_->start; } absl::optional<double> GetEndScrollOffsetForTest() const { - if (scroll_offsets_.empty()) + if (!pending_offsets_) return absl::nullopt; - return scroll_offsets_[1]; + return pending_offsets_->end; } bool IsScrollTimeline() const override; @@ -108,9 +120,8 @@ // it should base its current time on, and where the origin point is. ScrollDirection direction_; - // This defines scroll ranges of the scroller that the ScrollTimeline is - // active within. If no ranges are defined the timeline is inactive. - std::vector<double> scroll_offsets_; + absl::optional<ScrollOffsets> pending_offsets_; + absl::optional<ScrollOffsets> active_offsets_; }; inline ScrollTimeline* ToScrollTimeline(AnimationTimeline* timeline) { @@ -124,53 +135,6 @@ return static_cast<const ScrollTimeline*>(timeline); } -// https://drafts.csswg.org/scroll-animations-1/#progress-calculation-algorithm -template <typename T> -double ComputeProgress(double current_offset, const T& resolved_offsets) { - // 1. Let scroll offsets be the result of applying the procedure to resolve - // scroll timeline offsets for scrollOffsets. - DCHECK_GE(resolved_offsets.size(), 2u); - // When start offset is greater than end offset, current time is calculated - // outside of this method. - DCHECK_LT(resolved_offsets[0], resolved_offsets[resolved_offsets.size() - 1]); - // When animation is in before or after phase, current time is calculated - // outside of this method. - DCHECK_GE(current_offset, resolved_offsets[0]); - DCHECK_LT(current_offset, resolved_offsets[resolved_offsets.size() - 1]); - // Traverse scroll offsets from the back to find first interval that - // contains the current offset. In case of overlapping offsets, last matching - // interval in the list is used to calculate the current time. The rational - // for choosing last matching offset is to be consistent with CSS property - // overrides. - - // 2. Let offset index correspond to the position of the last offset in scroll - // offsets whose value is less than or equal to offset and the value at the - // following position greater than offset - int offset_index; - for (offset_index = resolved_offsets.size() - 2; - offset_index > 0 && resolved_offsets[offset_index] > current_offset; - offset_index--) { - DCHECK_LT(current_offset, resolved_offsets[offset_index + 1]); - } - // 3. Let start offset be the offset value at position offset index in - // scroll offsets. - double start_offset = resolved_offsets[offset_index]; - // 4. Let end offset be the value of next offset in scroll offsets after - // start offset. - double end_offset = resolved_offsets[offset_index + 1]; - // 5. Let size be the number of offsets in scroll offsets. - unsigned int size = resolved_offsets.size(); - // 6. Let offset weight be the result of evaluating 1 / (size - 1). - double offset_weight = 1.0 / (size - 1); - // 7. Let interval progress be the result of evaluating - // (offset - start offset) / (end offset - start offset). - double interval_progress = - (current_offset - start_offset) / (end_offset - start_offset); - // 8. Return the result of evaluating - // (offset index + interval progress) × offset weight. - return (offset_index + interval_progress) * offset_weight; -} - } // namespace cc #endif // CC_ANIMATION_SCROLL_TIMELINE_H_
diff --git a/cc/animation/scroll_timeline_unittest.cc b/cc/animation/scroll_timeline_unittest.cc index 6d66c54..5318a05 100644 --- a/cc/animation/scroll_timeline_unittest.cc +++ b/cc/animation/scroll_timeline_unittest.cc
@@ -24,6 +24,12 @@ #define EXPECT_SCROLL_TIMELINE_TIME_NEAR(expected, value) \ EXPECT_NEAR(expected, ToDouble(value), time_error_ms) +#define EXPECT_SCROLL_TIMELINE_BEFORE_START(value) \ + EXPECT_LT(ToDouble(value), 0); + +#define EXPECT_SCROLL_TIMELINE_AFTER_END(value) \ + EXPECT_GT(ToDouble(value), ScrollTimeline::kScrollTimelineDurationMs); + void SetScrollOffset(PropertyTrees* property_trees, ElementId scroller_id, gfx::PointF offset) { @@ -123,9 +129,7 @@ }; TEST_F(ScrollTimelineTest, BasicCurrentTimeCalculations) { - std::vector<double> scroll_offsets; - scroll_offsets.push_back(0); - scroll_offsets.push_back(100); + ScrollTimeline::ScrollOffsets scroll_offsets(0, 100); scoped_refptr<ScrollTimeline> vertical_timeline = ScrollTimeline::Create( scroller_id(), ScrollTimeline::ScrollDown, scroll_offsets); @@ -150,118 +154,6 @@ horizontal_timeline->CurrentTime(scroll_tree(), false)); } -TEST_F(ScrollTimelineTest, MultipleScrollOffsetsCurrentTimeCalculations) { - double scroll_size = - content_size().height() - container_size().height(); // 400 - - std::vector<double> scroll_offsets; - scroll_offsets.push_back(0); - scroll_offsets.push_back(100.0); - scroll_offsets.push_back(250.0); - scroll_offsets.push_back(scroll_size); - - scoped_refptr<ScrollTimeline> vertical_timeline = ScrollTimeline::Create( - scroller_id(), ScrollTimeline::ScrollDown, scroll_offsets); - - unsigned int offset = 0; - double w = 1.0 / 3.0; // offset weight - double p = 0; // progress within the offset - - // Scale necessary to convert absolute unit times to progress based values - double scale = ScrollTimeline::kScrollTimelineDurationMs / scroll_size; - - SetScrollOffset(&property_trees(), scroller_id(), gfx::PointF()); - EXPECT_SCROLL_TIMELINE_TIME_NEAR( - (offset + p) * w * scroll_size * scale, - vertical_timeline->CurrentTime(scroll_tree(), false)); - - p = (70.0 - 0.0) / (100.0 - 0.0); - SetScrollOffset(&property_trees(), scroller_id(), gfx::PointF(0, 70)); - EXPECT_SCROLL_TIMELINE_TIME_NEAR( - (offset + p) * w * scroll_size * scale, - vertical_timeline->CurrentTime(scroll_tree(), false)); - - offset = 1; - p = 0; - SetScrollOffset(&property_trees(), scroller_id(), gfx::PointF(0, 100)); - EXPECT_SCROLL_TIMELINE_TIME_NEAR( - (offset + p) * w * scroll_size * scale, - vertical_timeline->CurrentTime(scroll_tree(), false)); - - p = (150.0 - 100.0) / (250.0 - 100.0); - SetScrollOffset(&property_trees(), scroller_id(), gfx::PointF(0, 150)); - EXPECT_SCROLL_TIMELINE_TIME_NEAR( - (offset + p) * w * scroll_size * scale, - vertical_timeline->CurrentTime(scroll_tree(), false)); - - offset = 2; - p = 0; - SetScrollOffset(&property_trees(), scroller_id(), gfx::PointF(0, 250)); - EXPECT_SCROLL_TIMELINE_TIME_NEAR( - (offset + p) * w * scroll_size * scale, - vertical_timeline->CurrentTime(scroll_tree(), false)); - - p = (350.0 - 250.0) / (400.0 - 250.0); - SetScrollOffset(&property_trees(), scroller_id(), gfx::PointF(0, 350)); - EXPECT_SCROLL_TIMELINE_TIME_NEAR( - (offset + p) * w * scroll_size * scale, - vertical_timeline->CurrentTime(scroll_tree(), false)); - - SetScrollOffset(&property_trees(), scroller_id(), gfx::PointF(0, 400)); - EXPECT_SCROLL_TIMELINE_TIME_NEAR( - ScrollTimeline::kScrollTimelineDurationMs, - vertical_timeline->CurrentTime(scroll_tree(), false)); -} - -TEST_F(ScrollTimelineTest, OverlappingScrollOffsets) { - double scroll_size = 100.0; - - // Start offset is greater than end offset ==> animation progress is - // either 0% or 100%. - std::vector<double> scroll_offsets = {350.0, 200.0, 50.0}; - - scoped_refptr<ScrollTimeline> vertical_timeline = ScrollTimeline::Create( - scroller_id(), ScrollTimeline::ScrollDown, scroll_offsets); - - // Offset is less than start offset ==> current time is 0. - SetScrollOffset(&property_trees(), scroller_id(), gfx::PointF(0, 300)); - EXPECT_SCROLL_TIMELINE_TIME_NEAR( - 0, vertical_timeline->CurrentTime(scroll_tree(), false)); - - // Scale necessary to convert absolute unit times to progress based values - double scale = ScrollTimeline::kScrollTimelineDurationMs / scroll_size; - - // Offset is greater than end offset ==> current time is 100%. - SetScrollOffset(&property_trees(), scroller_id(), gfx::PointF(0, 360)); - EXPECT_SCROLL_TIMELINE_TIME_NEAR( - scroll_size * scale, - vertical_timeline->CurrentTime(scroll_tree(), false)); - - scroll_offsets = {0.0, 400.0, 200.0}; - - vertical_timeline = ScrollTimeline::Create( - scroller_id(), ScrollTimeline::ScrollDown, scroll_offsets); - - SetScrollOffset(&property_trees(), scroller_id(), gfx::PointF(0, 100)); - // Scroll offset is 25% of [0, 400) range, which maps to [0% 50%) of the - // entire scroll range. - EXPECT_SCROLL_TIMELINE_TIME_NEAR( - scroll_size * 0.5 * 0.25 * scale, - vertical_timeline->CurrentTime(scroll_tree(), false)); - - scroll_offsets = {200.0, 0.0, 400.0}; - - vertical_timeline = ScrollTimeline::Create( - scroller_id(), ScrollTimeline::ScrollDown, scroll_offsets); - - SetScrollOffset(&property_trees(), scroller_id(), gfx::PointF(0, 300)); - // Scroll offset is 75% of [0, 400) range, which maps to [50% 100%) of the - // entire scroll range. - EXPECT_SCROLL_TIMELINE_TIME_NEAR( - scroll_size * (0.5 + 0.5 * 0.75) * scale, - vertical_timeline->CurrentTime(scroll_tree(), false)); -} - // This test ensures that the ScrollTimeline's active scroller id is correct. We // had a few crashes caused by assuming that the id would be available in the // active tree before the activation happened; see http://crbug.com/853231 @@ -283,9 +175,7 @@ container_size()); double scroll_size = content_size().height() - container_size().height(); - std::vector<double> scroll_offsets; - scroll_offsets.push_back(0); - scroll_offsets.push_back(scroll_size); + ScrollTimeline::ScrollOffsets scroll_offsets(0, scroll_size); double halfwayY = scroll_size / 2.; double expectedTime = 0.5 * ScrollTimeline::kScrollTimelineDurationMs; @@ -321,9 +211,7 @@ TEST_F(ScrollTimelineTest, CurrentTimeIsAdjustedForPixelSnapping) { double scroll_size = content_size().height() - container_size().height(); - std::vector<double> scroll_offsets; - scroll_offsets.push_back(0); - scroll_offsets.push_back(scroll_size); + ScrollTimeline::ScrollOffsets scroll_offsets(0, scroll_size); scoped_refptr<ScrollTimeline> timeline = ScrollTimeline::Create( scroller_id(), ScrollTimeline::ScrollDown, scroll_offsets); @@ -346,21 +234,20 @@ TEST_F(ScrollTimelineTest, CurrentTimeHandlesStartScrollOffset) { double scroll_size = content_size().height() - container_size().height(); const double start_scroll_offset = 20; - std::vector<double> scroll_offsets; - scroll_offsets.push_back(start_scroll_offset); - scroll_offsets.push_back(scroll_size); + ScrollTimeline::ScrollOffsets scroll_offsets(start_scroll_offset, + scroll_size); scoped_refptr<ScrollTimeline> timeline = ScrollTimeline::Create( scroller_id(), ScrollTimeline::ScrollDown, scroll_offsets); - // Unscrolled, the timeline should read a current time of 0 since the current - // offset (0) will be less than the startScrollOffset. + // Unscrolled, the timeline should read a current time of < 0 since the + // current offset (0) will be less than the startScrollOffset. SetScrollOffset(&property_trees(), scroller_id(), gfx::PointF()); - EXPECT_SCROLL_TIMELINE_TIME_NEAR(0, - timeline->CurrentTime(scroll_tree(), false)); + EXPECT_SCROLL_TIMELINE_BEFORE_START( + timeline->CurrentTime(scroll_tree(), false)); SetScrollOffset(&property_trees(), scroller_id(), gfx::PointF(0, 19)); - EXPECT_SCROLL_TIMELINE_TIME_NEAR(0, - timeline->CurrentTime(scroll_tree(), false)); + EXPECT_SCROLL_TIMELINE_BEFORE_START( + timeline->CurrentTime(scroll_tree(), false).value()); SetScrollOffset(&property_trees(), scroller_id(), gfx::PointF(0, 20)); EXPECT_SCROLL_TIMELINE_TIME_NEAR(0, @@ -380,16 +267,13 @@ TEST_F(ScrollTimelineTest, CurrentTimeHandlesEndScrollOffset) { double scroll_size = content_size().height() - container_size().height(); const double end_scroll_offset = scroll_size - 20; - std::vector<double> scroll_offsets; - scroll_offsets.push_back(0); // should be absl::nullopt - scroll_offsets.push_back(end_scroll_offset); + ScrollTimeline::ScrollOffsets scroll_offsets(0, end_scroll_offset); scoped_refptr<ScrollTimeline> timeline = ScrollTimeline::Create( scroller_id(), ScrollTimeline::ScrollDown, scroll_offsets); SetScrollOffset(&property_trees(), scroller_id(), gfx::PointF(0, scroll_size)); - EXPECT_SCROLL_TIMELINE_TIME_NEAR(ScrollTimeline::kScrollTimelineDurationMs, - timeline->CurrentTime(scroll_tree(), false)); + EXPECT_SCROLL_TIMELINE_AFTER_END(timeline->CurrentTime(scroll_tree(), false)); SetScrollOffset(&property_trees(), scroller_id(), gfx::PointF(0, scroll_size - 20)); @@ -413,9 +297,8 @@ double scroll_size = content_size().height() - container_size().height(); double start_scroll_offset = 20; double end_scroll_offset = scroll_size - 50; - std::vector<double> scroll_offsets; - scroll_offsets.push_back(start_scroll_offset); - scroll_offsets.push_back(end_scroll_offset); + ScrollTimeline::ScrollOffsets scroll_offsets(start_scroll_offset, + end_scroll_offset); scoped_refptr<ScrollTimeline> timeline = ScrollTimeline::Create( scroller_id(), ScrollTimeline::ScrollDown, scroll_offsets); SetScrollOffset(&property_trees(), scroller_id(), @@ -427,11 +310,15 @@ } TEST_F(ScrollTimelineTest, CurrentTimeHandlesEqualStartAndEndScrollOffset) { - std::vector<double> scroll_offsets; - scroll_offsets.push_back(20); - scroll_offsets.push_back(20); + ScrollTimeline::ScrollOffsets scroll_offsets(20, 20); scoped_refptr<ScrollTimeline> timeline = ScrollTimeline::Create( scroller_id(), ScrollTimeline::ScrollDown, scroll_offsets); + + SetScrollOffset(&property_trees(), scroller_id(), gfx::PointF(0, 20)); + + EXPECT_SCROLL_TIMELINE_TIME_NEAR(ScrollTimeline::kScrollTimelineDurationMs, + timeline->CurrentTime(scroll_tree(), false)); + SetScrollOffset(&property_trees(), scroller_id(), gfx::PointF(0, 150)); EXPECT_SCROLL_TIMELINE_TIME_NEAR(ScrollTimeline::kScrollTimelineDurationMs, @@ -440,19 +327,22 @@ TEST_F(ScrollTimelineTest, CurrentTimeHandlesStartOffsetLargerThanEndScrollOffset) { - std::vector<double> scroll_offsets; - scroll_offsets.push_back(50); - scroll_offsets.push_back(10); + ScrollTimeline::ScrollOffsets scroll_offsets(50, 10); scoped_refptr<ScrollTimeline> timeline = ScrollTimeline::Create( scroller_id(), ScrollTimeline::ScrollDown, scroll_offsets); - SetScrollOffset(&property_trees(), scroller_id(), gfx::PointF(0, 40)); - EXPECT_SCROLL_TIMELINE_TIME_NEAR(0, - timeline->CurrentTime(scroll_tree(), false)); + + // Timeline direction reversed. + SetScrollOffset(&property_trees(), scroller_id(), gfx::PointF(0, 0)); + EXPECT_SCROLL_TIMELINE_AFTER_END(timeline->CurrentTime(scroll_tree(), false)); + + SetScrollOffset(&property_trees(), scroller_id(), gfx::PointF(0, 30)); + EXPECT_SCROLL_TIMELINE_TIME_NEAR( + ScrollTimeline::kScrollTimelineDurationMs / 2, + timeline->CurrentTime(scroll_tree(), false)); SetScrollOffset(&property_trees(), scroller_id(), gfx::PointF(0, 150)); - - EXPECT_SCROLL_TIMELINE_TIME_NEAR(ScrollTimeline::kScrollTimelineDurationMs, - timeline->CurrentTime(scroll_tree(), false)); + EXPECT_SCROLL_TIMELINE_BEFORE_START( + timeline->CurrentTime(scroll_tree(), false)); } TEST_F(ScrollTimelineTest, CurrentTimeHandlesScrollOffsets) { @@ -460,18 +350,17 @@ const double scroller_height = content_size().height() - container_size().height(); const double end_scroll_offset = scroller_height - 20; - std::vector<double> scroll_offsets; - scroll_offsets.push_back(start_scroll_offset); - scroll_offsets.push_back(end_scroll_offset); + ScrollTimeline::ScrollOffsets scroll_offsets(start_scroll_offset, + end_scroll_offset); scoped_refptr<ScrollTimeline> timeline = ScrollTimeline::Create( scroller_id(), ScrollTimeline::ScrollDown, scroll_offsets); - // Before the start_scroll_offset the current time should be 0 + // Before the start_scroll_offset the current time should be < 0 SetScrollOffset(&property_trees(), scroller_id(), gfx::PointF(0, start_scroll_offset - 10)); - EXPECT_SCROLL_TIMELINE_TIME_NEAR(0, - timeline->CurrentTime(scroll_tree(), false)); + EXPECT_SCROLL_TIMELINE_BEFORE_START( + timeline->CurrentTime(scroll_tree(), false)); // At the end_scroll_offset the current time should be 100% SetScrollOffset(&property_trees(), scroller_id(), @@ -479,19 +368,16 @@ EXPECT_SCROLL_TIMELINE_TIME_NEAR(ScrollTimeline::kScrollTimelineDurationMs, timeline->CurrentTime(scroll_tree(), false)); - // After the end_scroll_offset the current time should be 100% + // After the end_scroll_offset the current time should be > 100% SetScrollOffset(&property_trees(), scroller_id(), gfx::PointF(0, end_scroll_offset + 10)); - EXPECT_SCROLL_TIMELINE_TIME_NEAR(ScrollTimeline::kScrollTimelineDurationMs, - timeline->CurrentTime(scroll_tree(), false)); + EXPECT_SCROLL_TIMELINE_AFTER_END(timeline->CurrentTime(scroll_tree(), false)); } TEST_F(ScrollTimelineTest, Activeness) { // ScrollTimeline with zero scroller id is inactive. - std::vector<double> scroll_offsets; double scroll_size = content_size().height() - container_size().height(); - scroll_offsets.push_back(0); - scroll_offsets.push_back(scroll_size); + ScrollTimeline::ScrollOffsets scroll_offsets(0, scroll_size); scoped_refptr<ScrollTimeline> inactive_timeline1 = ScrollTimeline::Create( absl::nullopt, ScrollTimeline::ScrollDown, scroll_offsets); EXPECT_FALSE( @@ -511,9 +397,9 @@ inactive_timeline2->IsActive(scroll_tree(), true /*is_active_tree*/)); // ScrollTimeline with empty scroll offsets is inactive. - std::vector<double> empty_scroll_offsets; - scoped_refptr<ScrollTimeline> inactive_timeline3 = ScrollTimeline::Create( - scroller_id(), ScrollTimeline::ScrollDown, empty_scroll_offsets); + scoped_refptr<ScrollTimeline> inactive_timeline3 = + ScrollTimeline::Create(scroller_id(), ScrollTimeline::ScrollDown, + /* scroll_offsets */ absl::nullopt); EXPECT_FALSE( inactive_timeline3->IsActive(scroll_tree(), false /*is_active_tree*/)); EXPECT_FALSE(
diff --git a/cc/animation/worklet_animation_unittest.cc b/cc/animation/worklet_animation_unittest.cc index 7cfe9e8..823ae30 100644 --- a/cc/animation/worklet_animation_unittest.cc +++ b/cc/animation/worklet_animation_unittest.cc
@@ -60,7 +60,7 @@ MockScrollTimeline() : ScrollTimeline(ElementId(), ScrollTimeline::ScrollDown, - std::vector<double>(), + /* scroll_offsets */ absl::nullopt, AnimationIdProvider::NextTimelineId()) {} MOCK_CONST_METHOD2(CurrentTime, absl::optional<base::TimeTicks>(const ScrollTree&, bool));
diff --git a/cc/paint/skottie_wrapper_impl.cc b/cc/paint/skottie_wrapper_impl.cc index 70357530..85901b8 100644 --- a/cc/paint/skottie_wrapper_impl.cc +++ b/cc/paint/skottie_wrapper_impl.cc
@@ -295,6 +295,7 @@ void Seek(float t, FrameDataCallback frame_data_cb) override LOCKS_EXCLUDED(lock_) { + TRACE_EVENT1("cc", "SkottieWrapperImpl::Seek", "timestamp", t); base::AutoLock lock(lock_); // There's no need to reset |current_frame_data_cb_| to null when finished. // The callback is guaranteed to only be invoked synchronously during calls @@ -310,6 +311,7 @@ const SkottieColorMap& color_map, const SkottieTextPropertyValueMap& text_map) override LOCKS_EXCLUDED(lock_) { + TRACE_EVENT1("cc", "SkottieWrapperImpl::Draw", "timestamp", t); base::AutoLock lock(lock_); current_frame_data_cb_ = std::move(frame_data_cb); property_manager_->color_property_manager().SetNewValues(color_map);
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn index 3ecb4ac..75fbdd9 100644 --- a/chrome/android/BUILD.gn +++ b/chrome/android/BUILD.gn
@@ -852,6 +852,7 @@ "//chrome/browser/segmentation_platform:jni_headers", "//chrome/browser/tab:jni_headers", "//chrome/browser/touch_to_fill/android:jni_headers", + "//chrome/browser/ui/android/fast_checkout:jni_headers", "//chrome/browser/ui/android/favicon:jni_headers", "//chrome/browser/ui/android/logo:jni_headers", "//chrome/browser/ui/android/omnibox:jni_headers", @@ -1357,6 +1358,7 @@ "javatests/src/org/chromium/chrome/browser/ntp/IncognitoDescriptionViewRenderTest.java", "javatests/src/org/chromium/chrome/browser/ntp/TitleUtilTest.java", "javatests/src/org/chromium/chrome/browser/tab/WebContentsStateBridgeTest.java", + "javatests/src/org/chromium/chrome/browser/tab/state/CouponPersistedTabDataTest.java", "javatests/src/org/chromium/chrome/browser/tab/state/FilePersistedTabDataStorageTest.java", "javatests/src/org/chromium/chrome/browser/tab/state/PersistedTabDataTest.java", "javatests/src/org/chromium/chrome/browser/tab/state/PriceDropMetricsLoggerTest.java",
diff --git a/chrome/android/chrome_junit_test_java_sources.gni b/chrome/android/chrome_junit_test_java_sources.gni index 932d985..ad30221 100644 --- a/chrome/android/chrome_junit_test_java_sources.gni +++ b/chrome/android/chrome_junit_test_java_sources.gni
@@ -86,6 +86,7 @@ "junit/src/org/chromium/chrome/browser/cryptids/ProbabilisticCryptidRendererUnitTest.java", "junit/src/org/chromium/chrome/browser/customtabs/ClientManagerTest.java", "junit/src/org/chromium/chrome/browser/customtabs/CloseButtonNavigatorTest.java", + "junit/src/org/chromium/chrome/browser/customtabs/CustomTabActivityLifecycleUmaTrackerUnitTest.java", "junit/src/org/chromium/chrome/browser/customtabs/CustomTabIntentDataProviderTest.java", "junit/src/org/chromium/chrome/browser/customtabs/CustomTabNavigationBarControllerTest.java", "junit/src/org/chromium/chrome/browser/customtabs/CustomTabNightModeStateControllerTest.java",
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/AppHooks.java b/chrome/android/java/src/org/chromium/chrome/browser/AppHooks.java index 44831e96..5e24942 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/AppHooks.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/AppHooks.java
@@ -28,6 +28,8 @@ import org.chromium.chrome.browser.metrics.VariationsSession; import org.chromium.chrome.browser.notifications.chime.ChimeDelegate; import org.chromium.chrome.browser.omaha.RequestGenerator; +import org.chromium.chrome.browser.partnerbookmarks.PartnerBookmark; +import org.chromium.chrome.browser.partnerbookmarks.PartnerBookmarksDelegateImpl; import org.chromium.chrome.browser.password_manager.GooglePasswordManagerUIProvider; import org.chromium.chrome.browser.policy.PolicyAuditor; import org.chromium.chrome.browser.rlz.RevenueStats; @@ -238,6 +240,14 @@ } /** + * @return An iterator of partner bookmarks. + */ + @Nullable + public PartnerBookmark.BookmarkIterator getPartnerBookmarkIterator() { + return new PartnerBookmarksDelegateImpl().createIterator(); + } + + /** * @return A new {@link DigitalWellbeingClient} instance. */ public DigitalWellbeingClient createDigitalWellbeingClient() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/LaunchIntentDispatcher.java b/chrome/android/java/src/org/chromium/chrome/browser/LaunchIntentDispatcher.java index 44cad04..3381564 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/LaunchIntentDispatcher.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/LaunchIntentDispatcher.java
@@ -366,6 +366,10 @@ // Create and fire a launch intent. Intent launchIntent = createCustomTabActivityIntent(mActivity, mIntent); + Uri extraReferrer = mActivity.getReferrer(); + if (extraReferrer != null) { + launchIntent.putExtra(IntentHandler.EXTRA_ACTIVITY_REFERRER, extraReferrer.toString()); + } // Allow disk writes during startActivity() to avoid strict mode violations on some // Samsung devices, see https://crbug.com/796548.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivityLifecycleUmaTracker.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivityLifecycleUmaTracker.java index 1229db6..5bd5d1ad 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivityLifecycleUmaTracker.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivityLifecycleUmaTracker.java
@@ -6,14 +6,25 @@ import static org.chromium.chrome.browser.dependency_injection.ChromeCommonQualifiers.SAVED_INSTANCE_SUPPLIER; +import android.app.Activity; +import android.content.Intent; +import android.net.Uri; import android.os.Bundle; +import android.os.SystemClock; +import android.text.TextUtils; +import androidx.annotation.StringDef; +import androidx.annotation.VisibleForTesting; + +import org.chromium.base.IntentUtils; import org.chromium.base.metrics.RecordHistogram; import org.chromium.base.metrics.RecordUserAction; import org.chromium.base.supplier.Supplier; import org.chromium.chrome.browser.IntentHandler; import org.chromium.chrome.browser.browserservices.intents.BrowserServicesIntentDataProvider; +import org.chromium.chrome.browser.customtabs.features.TabInteractionRecorder; import org.chromium.chrome.browser.dependency_injection.ActivityScope; +import org.chromium.chrome.browser.flags.ChromeFeatureList; import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher; import org.chromium.chrome.browser.lifecycle.NativeInitObserver; import org.chromium.chrome.browser.lifecycle.PauseResumeWithNativeObserver; @@ -21,6 +32,9 @@ import org.chromium.chrome.browser.preferences.SharedPreferencesManager; import org.chromium.chrome.browser.webapps.WebappCustomTabTimeSpentLogger; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + import javax.inject.Inject; import javax.inject.Named; @@ -30,10 +44,23 @@ @ActivityScope public class CustomTabActivityLifecycleUmaTracker implements PauseResumeWithNativeObserver, NativeInitObserver { + /** + * Identifier used for last CCT client App. Used as suffix for histogram + * "CustomTabs.RetainableSessions.TimeBetweenLaunch". + */ + @StringDef({ClientIdentifierType.DIFFERENT, ClientIdentifierType.MIXED, + ClientIdentifierType.REFERRER, ClientIdentifierType.PACKAGE_NAME}) + @Retention(RetentionPolicy.SOURCE) + @interface ClientIdentifierType { + String DIFFERENT = ".Different"; + String MIXED = ".Mixed"; + String REFERRER = ".Referrer"; + String PACKAGE_NAME = ".PackageName"; + } private final BrowserServicesIntentDataProvider mIntentDataProvider; - private final CustomTabsConnection mConnection; private final Supplier<Bundle> mSavedInstanceStateSupplier; + private final Activity mActivity; private WebappCustomTabTimeSpentLogger mWebappTimeSpentLogger; private boolean mIsInitialResume = true; @@ -88,11 +115,10 @@ @Inject public CustomTabActivityLifecycleUmaTracker(ActivityLifecycleDispatcher lifecycleDispatcher, - BrowserServicesIntentDataProvider intentDataProvider, - CustomTabsConnection customTabsConnection, + BrowserServicesIntentDataProvider intentDataProvider, Activity activity, @Named(SAVED_INSTANCE_SUPPLIER) Supplier<Bundle> savedInstanceStateSupplier) { mIntentDataProvider = intentDataProvider; - mConnection = customTabsConnection; + mActivity = activity; mSavedInstanceStateSupplier = savedInstanceStateSupplier; lifecycleDispatcher.register(this); @@ -111,15 +137,26 @@ String lastUrl = preferences.readString(ChromePreferenceKeys.CUSTOM_TABS_LAST_URL, null); String urlToLoad = mIntentDataProvider.getUrlToLoad(); - if (lastUrl != null && lastUrl.equals(urlToLoad)) { + boolean launchWithSameUrl = lastUrl != null && lastUrl.equals(urlToLoad); + if (launchWithSameUrl) { RecordUserAction.record("CustomTabsMenuOpenSameUrl"); } else { preferences.writeString(ChromePreferenceKeys.CUSTOM_TABS_LAST_URL, urlToLoad); } + if (ChromeFeatureList.isEnabled(ChromeFeatureList.CCT_RETAINING_STATE)) { + String clientPackage = mIntentDataProvider.getClientPackageName(); + String referrer = getReferrerUriString(mActivity); + int taskId = mActivity.getTaskId(); + + recordForRetainableSessions( + clientPackage, referrer, taskId, preferences, launchWithSameUrl); + TabInteractionRecorder.resetTabInteractionRecords(); + } recordUserAction(); recordMetrics(); } + mIsInitialResume = false; mWebappTimeSpentLogger = WebappCustomTabTimeSpentLogger.createInstanceAndStartTimer( @@ -141,4 +178,94 @@ mWebappTimeSpentLogger.onPause(); } } + + /** + * Record shared pref and histogram when an retainable CCT session is launched back to back with + * same embedded app and URL, and user action has seen in the previous CCT. The embedded app is + * determine through taskId + package name combination. For the package name to use, this + * function will bias clientPackage if provided, otherwise fallback to referrer. + * + * @param clientPackage Package name get from CCT service + * @param referrer Referrer of the CCT activity. + * @param taskId The task Id of CCT activity. + * @param preferences Instance from {@link SharedPreferencesManager#getInstance()}. + */ + @VisibleForTesting + static void recordForRetainableSessions(String clientPackage, String referrer, int taskId, + SharedPreferencesManager preferences, boolean launchWithSameUrl) { + String prevClientPackage = + preferences.readString(ChromePreferenceKeys.CUSTOM_TABS_LAST_CLIENT_PACKAGE, null); + String prevReferrer = + preferences.readString(ChromePreferenceKeys.CUSTOM_TABS_LAST_REFERRER, null); + int prevTaskId = preferences.readInt(ChromePreferenceKeys.CUSTOM_TABS_LAST_TASK_ID); + + preferences.writeInt(ChromePreferenceKeys.CUSTOM_TABS_LAST_TASK_ID, taskId); + if (TextUtils.isEmpty(clientPackage)) { + preferences.removeKey(ChromePreferenceKeys.CUSTOM_TABS_LAST_CLIENT_PACKAGE); + preferences.writeString(ChromePreferenceKeys.CUSTOM_TABS_LAST_REFERRER, referrer); + } else { + preferences.writeString( + ChromePreferenceKeys.CUSTOM_TABS_LAST_CLIENT_PACKAGE, clientPackage); + preferences.removeKey(ChromePreferenceKeys.CUSTOM_TABS_LAST_REFERRER); + } + + if (!launchWithSameUrl || prevTaskId != taskId + || !preferences.readBoolean( + ChromePreferenceKeys.CUSTOM_TABS_LAST_CLOSE_TAB_INTERACTION, false)) { + return; + } + + boolean hasClientPackage = !TextUtils.isEmpty(clientPackage); + String suffix = ClientIdentifierType.DIFFERENT; + if (hasClientPackage && TextUtils.equals(clientPackage, prevClientPackage)) { + suffix = ClientIdentifierType.PACKAGE_NAME; + } else if (!TextUtils.isEmpty(referrer) && TextUtils.equals(referrer, prevReferrer)) { + suffix = ClientIdentifierType.REFERRER; + } else { + String currentPackage = + hasClientPackage ? clientPackage : Uri.parse(referrer).getHost(); + String prevPackage = !TextUtils.isEmpty(prevClientPackage) + ? prevClientPackage + : Uri.parse(prevReferrer).getHost(); + + if (TextUtils.equals(currentPackage, prevPackage)) { + suffix = ClientIdentifierType.MIXED; + } + } + + String histogramPrefix = "CustomTabs.RetainableSessions.TimeBetweenLaunch"; + long time = SystemClock.uptimeMillis(); + long lastClosedTime = + preferences.readLong(ChromePreferenceKeys.CUSTOM_TABS_LAST_CLOSE_TIMESTAMP); + if (lastClosedTime != 0 && lastClosedTime < time) { + RecordHistogram.recordLongTimesHistogram( + histogramPrefix + suffix, time - lastClosedTime); + } + } + + /** + * Get the referrer for the given activity. If the activity is launched through launcher + * activity, the referrer is set through {@link IntentHandler#EXTRA_ACTIVITY_REFERRER}; if not, + * check {@link Activity#getReferrer()}; if both return empty, fallback to + * {@link IntentHandler#getReferrerPolicyFromIntent(Intent)}. + */ + @VisibleForTesting + static String getReferrerUriString(Activity activity) { + if (activity == null || activity.getIntent() == null) { + return ""; + } + + Intent intent = activity.getIntent(); + String extraReferrer = + IntentUtils.safeGetStringExtra(intent, IntentHandler.EXTRA_ACTIVITY_REFERRER); + if (extraReferrer != null) { + return extraReferrer; + } + + Uri activityReferrer = activity.getReferrer(); + if (activityReferrer != null) { + return activityReferrer.toString(); + } + return IntentHandler.getReferrerUrlIncludingExtraHeaders(intent); + } }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/TabInteractionRecorder.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/TabInteractionRecorder.java index e240b92..dbcc16a6 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/TabInteractionRecorder.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/TabInteractionRecorder.java
@@ -87,6 +87,15 @@ RecordHistogram.recordBooleanHistogram("CustomTabs.HadInteractionOnClose", hadInteraction); } + /** + * Remove all the shared preferences related to tab interactions. + */ + public static void resetTabInteractionRecords() { + SharedPreferencesManager pref = SharedPreferencesManager.getInstance(); + pref.removeKey(ChromePreferenceKeys.CUSTOM_TABS_LAST_CLOSE_TIMESTAMP); + pref.removeKey(ChromePreferenceKeys.CUSTOM_TABS_LAST_CLOSE_TAB_INTERACTION); + } + @NativeMethods interface Natives { TabInteractionRecorder getFromTab(Tab tab);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/partnerbookmarks/PartnerBookmarksShim.java b/chrome/android/java/src/org/chromium/chrome/browser/partnerbookmarks/PartnerBookmarksShim.java index d105a447..34b1cd3 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/partnerbookmarks/PartnerBookmarksShim.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/partnerbookmarks/PartnerBookmarksShim.java
@@ -7,6 +7,7 @@ import android.content.Context; import android.content.pm.ApplicationInfo; +import org.chromium.chrome.browser.AppHooks; import org.chromium.chrome.browser.partnercustomizations.PartnerBrowserCustomizations; import org.chromium.components.version_info.VersionInfo; @@ -33,7 +34,7 @@ PartnerBookmarksReader reader = new PartnerBookmarksReader(context, PartnerBrowserCustomizations.getInstance(), - new PartnerBookmarksDelegateImpl()::createIterator); + AppHooks.get()::getPartnerBookmarkIterator); boolean systemOrPreStable = (context.getApplicationInfo().flags & ApplicationInfo.FLAG_SYSTEM) == 1
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileGroup.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileGroup.java index d3e4fbe..f4c877f 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileGroup.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileGroup.java
@@ -291,7 +291,7 @@ @Override public void onIconMadeAvailable(GURL siteUrl) { for (Tile tile : findTilesForUrl(siteUrl)) { - mTileRenderer.updateIcon(tile, () -> mObserver.onTileIconChanged(tile)); + mTileRenderer.updateIcon(tile, mTileSetupDelegate); } }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileRenderer.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileRenderer.java index de93a772..dc3a623 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileRenderer.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileRenderer.java
@@ -214,9 +214,8 @@ // Note: It is important that the callbacks below don't keep a reference to the tile or // modify them as there is no guarantee that the same tile would be used to update the view. - if (mImageFetcher != null && tile.getSource() != TileSource.EXPLORE) { - updateIcon(tile, setupDelegate.createIconLoadCallback(tile)); - } + updateIcon(tile, setupDelegate); + updateContentDescription(tile, tileView); TileGroup.TileInteractionDelegate delegate = setupDelegate.createInteractionDelegate(tile); if (tile.getSource() == TileSource.HOMEPAGE) { @@ -237,20 +236,52 @@ return tileView; } - public void updateIcon(final Tile tile, final Runnable iconCallback) { + /** @return True, if the tile represents a Search query. */ + private boolean isSearchTile(Tile tile) { TemplateUrlService searchService = TemplateUrlServiceFactory.get(); - if (searchService != null - && searchService.isSearchResultsPageFromDefaultSearchProvider(tile.getData().url)) { + return searchService != null + && searchService.isSearchResultsPageFromDefaultSearchProvider(tile.getUrl()); + } + + /** + * Given a Tile data and TileView, apply appropriate content description that will be announced + * when the view is focused for accessibility. + * The objective of the description is to offer audible guidance that helps users differentiate + * navigation (open www.site.com) and search (search www.site.com). + * + * @param tile Tile data that carries information about the destination URL. + * @param tileView The view that should receive updated content description. + */ + private void updateContentDescription(Tile tile, SuggestionsTileView tileView) { + if (isSearchTile(tile)) { + tileView.setContentDescription(mContext.getString( + R.string.accessibility_omnibox_most_visited_tile_search, tile.getTitle())); + } else { + tileView.setContentDescription( + mContext.getString(R.string.accessibility_omnibox_most_visited_tile_navigate, + tile.getTitle(), tile.getUrl().getHost())); + } + } + + /** + * Update tile decoration. + * + * @param tile Tile data that carries information about the target site. + * @param setupDelegate The delegate used to setup callbacks and listeners for the new view. + */ + public void updateIcon(final Tile tile, TileGroup.TileSetupDelegate setupDelegate) { + if (isSearchTile(tile)) { // We already have an icon, and could trigger the update instantly. // Problem is, the TileView is likely not attached yet and the update would not be // properly reflected. Yield. + final Runnable iconCallback = setupDelegate.createIconLoadCallback(tile); PostTask.postTask(UiThreadTaskTraits.DEFAULT, () -> { setTileIconFromRes(tile, R.drawable.ic_suggestion_magnifier); if (iconCallback != null) iconCallback.run(); }); - } else { - mImageFetcher.makeLargeIconRequest(tile.getData().url, mMinIconSize, - new LargeIconCallbackImpl(tile, iconCallback)); + } else if (mImageFetcher != null && tile.getSource() != TileSource.EXPLORE) { + mImageFetcher.makeLargeIconRequest(tile.getUrl(), mMinIconSize, + new LargeIconCallbackImpl(tile, setupDelegate.createIconLoadCallback(tile))); } }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/app/bookmarks/BookmarkTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/app/bookmarks/BookmarkTest.java index 7cfae9e..d6c7b52 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/app/bookmarks/BookmarkTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/app/bookmarks/BookmarkTest.java
@@ -1283,6 +1283,7 @@ @SmallTest @Features.EnableFeatures({ChromeFeatureList.READ_LATER + ":use_cct/false"}) @Restriction({UiRestriction.RESTRICTION_TYPE_PHONE}) + @DisabledTest(message = "crbug.com/1339976") public void testReadingListOpenInRegularTab() throws Exception { addReadingListBookmark(TEST_PAGE_TITLE_GOOGLE, mTestUrlA); BookmarkPromoHeader.forcePromoStateForTests(SyncPromoState.NO_PROMO);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityRenderTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityRenderTest.java index 3329a7c..10cdb883 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityRenderTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityRenderTest.java
@@ -190,7 +190,7 @@ public void custom_color_red() throws IOException { Context context = InstrumentationRegistry.getContext(); mIntent = CustomTabsIntentTestUtils.createCustomTabIntent( - context, mUrl, builder -> { builder.setToolbarColor(Color.RED); }); + context, mUrl, true, builder -> { builder.setToolbarColor(Color.RED); }); startActivityAndRenderToolbar("cct_red" + mRunWithHttps); } @@ -201,7 +201,7 @@ public void custom_color_black() throws IOException { Context context = InstrumentationRegistry.getContext(); mIntent = CustomTabsIntentTestUtils.createCustomTabIntent( - context, mUrl, builder -> { builder.setToolbarColor(Color.BLACK); }); + context, mUrl, true, builder -> { builder.setToolbarColor(Color.BLACK); }); startActivityAndRenderToolbar("cct_black" + mRunWithHttps); }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityTest.java index 0430fadb..1e98575 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityTest.java
@@ -38,6 +38,7 @@ import android.os.SystemClock; import android.provider.Browser; import android.support.test.InstrumentationRegistry; +import android.support.test.runner.lifecycle.Stage; import android.view.View; import android.view.ViewGroup; import android.view.WindowManager; @@ -72,6 +73,7 @@ import org.chromium.base.library_loader.LibraryLoader; import org.chromium.base.metrics.RecordHistogram; import org.chromium.base.task.PostTask; +import org.chromium.base.test.util.ApplicationTestUtils; import org.chromium.base.test.util.CallbackHelper; import org.chromium.base.test.util.CommandLineFlags; import org.chromium.base.test.util.Criteria; @@ -95,6 +97,7 @@ import org.chromium.chrome.browser.browserservices.intents.BrowserServicesIntentDataProvider; import org.chromium.chrome.browser.browserservices.verification.OriginVerifier; import org.chromium.chrome.browser.contextmenu.ContextMenuCoordinator; +import org.chromium.chrome.browser.customtabs.CustomTabActivityLifecycleUmaTracker.ClientIdentifierType; import org.chromium.chrome.browser.customtabs.CustomTabsIntentTestUtils.OnFinishedForTest; import org.chromium.chrome.browser.customtabs.content.CustomTabActivityNavigationController.FinishReason; import org.chromium.chrome.browser.customtabs.features.toolbar.CustomTabToolbar; @@ -143,6 +146,7 @@ import org.chromium.net.test.EmbeddedTestServer; import org.chromium.net.test.util.TestWebServer; import org.chromium.ui.mojom.WindowOpenDisposition; +import org.chromium.ui.test.util.BlankUiTestActivity; import org.chromium.ui.test.util.UiRestriction; import org.chromium.ui.util.ColorUtils; import org.chromium.url.GURL; @@ -163,6 +167,7 @@ + "Unit test conversion tracked in crbug.com/1217031") public class CustomTabActivityTest { private static final int TIMEOUT_PAGE_LOAD_SECONDS = 10; + private static final String TEST_PACKAGE = "org.chromium.chrome.tests"; private static final String TEST_PAGE = "/chrome/test/data/android/google.html"; private static final String TEST_PAGE_2 = "/chrome/test/data/android/test.html"; private static final String FRAGMENT_TEST_PAGE = "/chrome/test/data/android/fragment.html"; @@ -226,6 +231,10 @@ @After public void tearDown() { TestThreadUtils.runOnUiThreadBlocking(() -> FirstRunStatus.setFirstRunFlowComplete(false)); + SharedPreferencesManager pref = SharedPreferencesManager.getInstance(); + pref.removeKey(ChromePreferenceKeys.CUSTOM_TABS_LAST_TASK_ID); + pref.removeKey(ChromePreferenceKeys.CUSTOM_TABS_LAST_URL); + pref.removeKey(ChromePreferenceKeys.CUSTOM_TABS_LAST_CLIENT_PACKAGE); SharedPreferencesManager.getInstance().removeKey( ChromePreferenceKeys.CUSTOM_TABS_LAST_CLOSE_TAB_INTERACTION); @@ -663,6 +672,74 @@ @Test @SmallTest + @Features.EnableFeatures(ChromeFeatureList.CCT_RETAINING_STATE) + public void testRecordRetainableSession_WithCctSession() throws Exception { + Activity emptyActivity = startBlankUiTestActivity(); + + // Write shared pref as it there's a previous CCT launch with sessions. + SharedPreferencesManager pref = SharedPreferencesManager.getInstance(); + pref.writeString(ChromePreferenceKeys.CUSTOM_TABS_LAST_URL, mTestPage); + pref.writeString(ChromePreferenceKeys.CUSTOM_TABS_LAST_CLIENT_PACKAGE, TEST_PACKAGE); + pref.writeInt(ChromePreferenceKeys.CUSTOM_TABS_LAST_TASK_ID, emptyActivity.getTaskId()); + pref.writeLong( + ChromePreferenceKeys.CUSTOM_TABS_LAST_CLOSE_TIMESTAMP, SystemClock.uptimeMillis()); + pref.writeBoolean(ChromePreferenceKeys.CUSTOM_TABS_LAST_CLOSE_TAB_INTERACTION, true); + + Intent intent = CustomTabsIntentTestUtils.createCustomTabIntent( + InstrumentationRegistry.getContext(), mTestPage, false, builder -> {}); + CustomTabsConnection connection = CustomTabsTestUtils.warmUpAndWait(); + CustomTabsSessionToken token = CustomTabsSessionToken.getSessionTokenFromIntent(intent); + connection.newSession(token); + intent.setData(Uri.parse(mTestPage)); + + CustomTabActivity cctActivity = ApplicationTestUtils.waitForActivityWithClass( + CustomTabActivity.class, Stage.CREATED, () -> emptyActivity.startActivity(intent)); + mCustomTabActivityTestRule.setActivity(cctActivity); + mCustomTabActivityTestRule.waitForActivityCompletelyLoaded(); + + assertLastLaunchedClientAppRecorded(ClientIdentifierType.PACKAGE_NAME, TEST_PACKAGE, + mTestPage, cctActivity.getTaskId(), true); + } + + @Test + @SmallTest + @Features.EnableFeatures(ChromeFeatureList.CCT_RETAINING_STATE) + public void testRecordRetainableSession_WithoutWarmupAndSession() { + Context context = InstrumentationRegistry.getContext(); + Activity emptyActivity = startBlankUiTestActivity(); + + Intent intent = + CustomTabsIntentTestUtils + .createCustomTabIntent(context, mTestPage, + /*launchAsNewTask=*/false, builder -> {}) + .putExtra(IntentHandler.EXTRA_ACTIVITY_REFERRER, context.getPackageName()); + + CustomTabActivity cctActivity = ApplicationTestUtils.waitForActivityWithClass( + CustomTabActivity.class, Stage.CREATED, () -> emptyActivity.startActivity(intent)); + mCustomTabActivityTestRule.setActivity(cctActivity); + mCustomTabActivityTestRule.waitForActivityCompletelyLoaded(); + + assertLastLaunchedClientAppRecorded( + ClientIdentifierType.REFERRER, "", mTestPage, cctActivity.getTaskId(), false); + TestThreadUtils.runOnUiThreadBlocking(() -> getActivity().finish()); + CriteriaHelper.pollUiThread(() -> getActivity().isDestroyed()); + + // Write shared prefs as it the last CCT session has saw tab interactions. + SharedPreferencesManager pref = SharedPreferencesManager.getInstance(); + pref.writeBoolean(ChromePreferenceKeys.CUSTOM_TABS_LAST_CLOSE_TAB_INTERACTION, true); + + // Start another CCT with same intent right away. + Intent newIntent = new Intent(intent); + cctActivity = ApplicationTestUtils.waitForActivityWithClass(CustomTabActivity.class, + Stage.CREATED, () -> emptyActivity.startActivity(newIntent)); + mCustomTabActivityTestRule.setActivity(cctActivity); + mCustomTabActivityTestRule.waitForActivityCompletelyLoaded(); + assertLastLaunchedClientAppRecorded( + ClientIdentifierType.REFERRER, "", mTestPage, getActivity().getTaskId(), true); + } + + @Test + @SmallTest @DisabledTest(message = "https://crbug.com/1308065") public void testLoadNewUrlWithSession() throws Exception { final Context context = InstrumentationRegistry.getTargetContext(); @@ -1920,6 +1997,13 @@ InstrumentationRegistry.getTargetContext(), mTestPage)); } + private Activity startBlankUiTestActivity() { + Context context = InstrumentationRegistry.getContext(); + Intent emptyIntent = new Intent(context, BlankUiTestActivity.class); + emptyIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + return InstrumentationRegistry.getInstrumentation().startActivitySync(emptyIntent); + } + private static class ElementContentCriteria implements Runnable { private final Tab mTab; private final String mJsFunction; @@ -1963,4 +2047,20 @@ private SessionDataHolder getSessionDataHolder() { return ChromeApplicationImpl.getComponent().resolveSessionDataHolder(); } + + private void assertLastLaunchedClientAppRecorded(String histogramSuffix, String clientPackage, + String url, int taskId, boolean umaRecorded) { + SharedPreferencesManager pref = SharedPreferencesManager.getInstance(); + String histogramName = "CustomTabs.RetainableSessions.TimeBetweenLaunch" + histogramSuffix; + + Assert.assertEquals("Client package name in shared pref is different.", clientPackage, + pref.readString(ChromePreferenceKeys.CUSTOM_TABS_LAST_CLIENT_PACKAGE, "")); + Assert.assertEquals("Url in shared pref is different.", url, + pref.readString(ChromePreferenceKeys.CUSTOM_TABS_LAST_URL, "")); + Assert.assertEquals("Task id in shared pref is different.", taskId, + pref.readInt(ChromePreferenceKeys.CUSTOM_TABS_LAST_TASK_ID, 0)); + Assert.assertEquals(String.format("<%s> not recorded correctly.", histogramName), + umaRecorded ? 1 : 0, + RecordHistogram.getHistogramTotalCountForTesting(histogramName)); + } }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabsIntentTestUtils.java b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabsIntentTestUtils.java index ed3a291..248a3e8 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabsIntentTestUtils.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabsIntentTestUtils.java
@@ -68,14 +68,14 @@ * the {@link CustomTabActivity}. */ public static Intent createMinimalCustomTabIntent(Context context, String url) { - return createCustomTabIntent(context, url, builder -> {}); + return createCustomTabIntent(context, url, /*launchAsNewTask=*/true, builder -> {}); } /** * Creates an Intent that launches a CustomTabActivity, allows some customization. */ - public static Intent createCustomTabIntent( - Context context, String url, Callback<CustomTabsIntent.Builder> customizer) { + public static Intent createCustomTabIntent(Context context, String url, boolean launchAsNewTask, + Callback<CustomTabsIntent.Builder> customizer) { CustomTabsIntent.Builder builder = new CustomTabsIntent.Builder(CustomTabsSession.createMockSessionForTesting( new ComponentName(context, ChromeLauncherActivity.class))); @@ -84,7 +84,7 @@ Intent intent = customTabsIntent.intent; intent.setAction(Intent.ACTION_VIEW); intent.setData(Uri.parse(url)); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + if (launchAsNewTask) intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); return intent; } @@ -112,7 +112,7 @@ */ public static Intent createMinimalCustomTabIntentWithTheme( Context context, String url, boolean inNightMode) { - return createCustomTabIntent(context, url, builder -> { + return createCustomTabIntent(context, url, /*launchAsNewTask=*/true, builder -> { builder.setColorScheme(inNightMode ? CustomTabsIntent.COLOR_SCHEME_DARK : CustomTabsIntent.COLOR_SCHEME_LIGHT); });
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/geo/GeolocationHeaderTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/geo/GeolocationHeaderTest.java index 7f98330..b3786bb 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/geo/GeolocationHeaderTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/geo/GeolocationHeaderTest.java
@@ -20,8 +20,6 @@ import org.chromium.base.test.util.CommandLineFlags; import org.chromium.base.test.util.Feature; import org.chromium.chrome.browser.flags.ChromeSwitches; -import org.chromium.chrome.browser.omnibox.geo.VisibleNetworks.VisibleCell; -import org.chromium.chrome.browser.omnibox.geo.VisibleNetworks.VisibleWifi; import org.chromium.chrome.browser.profiles.Profile; import org.chromium.chrome.browser.tab.Tab; import org.chromium.chrome.test.ChromeJUnit4ClassRunner; @@ -33,9 +31,6 @@ import org.chromium.components.content_settings.ContentSettingsType; import org.chromium.content_public.browser.test.util.TestThreadUtils; -import java.util.Arrays; -import java.util.HashSet; - /** * Tests for GeolocationHeader and GeolocationTracker. */ @@ -55,33 +50,12 @@ private static final double LOCATION_LAT = 20.3; private static final double LOCATION_LONG = 155.8; private static final float LOCATION_ACCURACY = 20f; - private static final VisibleWifi VISIBLE_WIFI1 = - VisibleWifi.create("ssid1", "11:11:11:11:11:11", -1, 10L); - private static final VisibleWifi VISIBLE_WIFI3 = - VisibleWifi.create("ssid3", "11:11:11:11:11:13", -30, 30L); - private static final VisibleCell VISIBLE_CELL1 = VisibleCell.builder(VisibleCell.RadioType.CDMA) - .setCellId(10) - .setLocationAreaCode(11) - .setMobileCountryCode(12) - .setMobileNetworkCode(13) - .setTimestamp(10L) - .build(); - private static final VisibleCell VISIBLE_CELL2 = VisibleCell.builder(VisibleCell.RadioType.GSM) - .setCellId(20) - .setLocationAreaCode(21) - .setMobileCountryCode(22) - .setMobileNetworkCode(23) - .setTimestamp(20L) - .build(); @Before public void setUp() throws InterruptedException { mActivityTestRule.startMainActivityOnBlankPage(); mOmniboxTestUtils = new OmniboxTestUtils(mActivityTestRule.getActivity()); - VisibleNetworks visibleNetworks = VisibleNetworks.create(VISIBLE_WIFI1, VISIBLE_CELL1, - new HashSet<>(Arrays.asList(VISIBLE_WIFI3)), - new HashSet<>(Arrays.asList(VISIBLE_CELL2))); - VisibleNetworksTracker.setVisibleNetworksForTesting(visibleNetworks); + GeolocationHeader.setAppPermissionGrantedForTesting(true); } @Test
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/tab/state/CouponPersistedTabDataTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/tab/state/CouponPersistedTabDataTest.java new file mode 100644 index 0000000..49f77b6 --- /dev/null +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/tab/state/CouponPersistedTabDataTest.java
@@ -0,0 +1,101 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chrome.browser.tab.state; + +import androidx.test.filters.SmallTest; + +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TestRule; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import org.chromium.base.test.BaseJUnit4ClassRunner; +import org.chromium.base.test.util.Batch; +import org.chromium.base.test.util.JniMocker; +import org.chromium.chrome.browser.endpoint_fetcher.EndpointFetcher; +import org.chromium.chrome.browser.endpoint_fetcher.EndpointFetcherJni; +import org.chromium.chrome.browser.profiles.Profile; +import org.chromium.chrome.browser.tab.MockTab; +import org.chromium.chrome.browser.tab.Tab; +import org.chromium.chrome.test.util.browser.Features; +import org.chromium.content_public.browser.test.util.TestThreadUtils; + +import java.nio.ByteBuffer; +import java.util.concurrent.ExecutionException; + +/** + * Test relating to {@link CouponPersistedTabData} + */ +@RunWith(BaseJUnit4ClassRunner.class) +@Batch(Batch.UNIT_TESTS) +public class CouponPersistedTabDataTest { + @Rule + public JniMocker mMocker = new JniMocker(); + + @Rule + public TestRule mProcessor = new Features.InstrumentationProcessor(); + + @Mock + private EndpointFetcher.Natives mEndpointFetcherJniMock; + + @Mock + private Profile mProfileMock; + + private static final String SERIALIZE_DESERIALIZE_NAME = "25% Off"; + private static final String SERIALIZE_DESERIALIZE_CODE = "DISCOUNT25"; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mMocker.mock(EndpointFetcherJni.TEST_HOOKS, mEndpointFetcherJniMock); + TestThreadUtils.runOnUiThreadBlocking( + () -> PersistedTabDataConfiguration.setUseTestConfig(true)); + Profile.setLastUsedProfileForTesting(mProfileMock); + } + + @After + public void tearDown() { + Profile.setLastUsedProfileForTesting(null); + PersistedTabDataConfiguration.setUseTestConfig(false); + } + + @SmallTest + @Test + public void testSerializeDeserialize() throws ExecutionException { + Tab tab = TestThreadUtils.runOnUiThreadBlocking(() -> new MockTab(1, false)); + CouponPersistedTabData couponPersistedTabData = new CouponPersistedTabData(tab, + new CouponPersistedTabData.Coupon( + SERIALIZE_DESERIALIZE_NAME, SERIALIZE_DESERIALIZE_CODE)); + ByteBuffer serialized = couponPersistedTabData.getSerializeSupplier().get(); + CouponPersistedTabData deserialized = new CouponPersistedTabData(tab); + Assert.assertTrue(deserialized.deserialize(serialized)); + Assert.assertEquals(SERIALIZE_DESERIALIZE_NAME, deserialized.getCoupon().couponName); + Assert.assertEquals(SERIALIZE_DESERIALIZE_CODE, deserialized.getCoupon().promoCode); + } + + @SmallTest + @Test + public void testSerializeDeserializeNull() throws ExecutionException { + Tab tab = TestThreadUtils.runOnUiThreadBlocking(() -> new MockTab(1, false)); + CouponPersistedTabData deserialized = new CouponPersistedTabData(tab, null); + Assert.assertFalse(deserialized.deserialize(null)); + } + + @SmallTest + @Test + public void testSerializeDeserializeNoRemainingBytes() throws ExecutionException { + Tab tab = TestThreadUtils.runOnUiThreadBlocking(() -> new MockTab(1, false)); + CouponPersistedTabData couponPersistedTabData = new CouponPersistedTabData(tab, null); + ByteBuffer serialized = couponPersistedTabData.getSerializeSupplier().get(); + CouponPersistedTabData deserialized = new CouponPersistedTabData(tab); + Assert.assertFalse(serialized.hasRemaining()); + Assert.assertFalse(deserialized.deserialize(serialized)); + } +}
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CustomTabActivityLifecycleUmaTrackerUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CustomTabActivityLifecycleUmaTrackerUnitTest.java new file mode 100644 index 0000000..771c5157 --- /dev/null +++ b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CustomTabActivityLifecycleUmaTrackerUnitTest.java
@@ -0,0 +1,211 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chrome.browser.customtabs; + +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; + +import android.app.Activity; +import android.content.Intent; +import android.net.Uri; +import android.os.SystemClock; +import android.text.TextUtils; + +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.annotation.Config; +import org.robolectric.shadows.ShadowSystemClock; + +import org.chromium.base.metrics.test.ShadowRecordHistogram; +import org.chromium.base.test.BaseRobolectricTestRunner; +import org.chromium.chrome.browser.IntentHandler; +import org.chromium.chrome.browser.customtabs.CustomTabActivityLifecycleUmaTracker.ClientIdentifierType; +import org.chromium.chrome.browser.preferences.ChromePreferenceKeys; +import org.chromium.chrome.browser.preferences.SharedPreferencesManager; + +import java.util.concurrent.TimeUnit; + +/** + * Unit test for {@link CustomTabActivityLifecycleUmaTracker}. + */ +@RunWith(BaseRobolectricTestRunner.class) +@Config(shadows = {ShadowRecordHistogram.class, ShadowSystemClock.class}) +public class CustomTabActivityLifecycleUmaTrackerUnitTest { + private static final String PACKAGE_A = "com.example.test.package"; + private static final String PACKAGE_B = "org.test.mypackage"; + private static final String REFERRER_A = "android-app://" + PACKAGE_A; + private static final String REFERRER_B = "android-app://" + PACKAGE_B; + private static final int TASK_ID_123 = 123; + + private SharedPreferencesManager mPref; + + @Before + public void setUp() { + mPref = SharedPreferencesManager.getInstance(); + mPref.writeLong( + ChromePreferenceKeys.CUSTOM_TABS_LAST_CLOSE_TIMESTAMP, SystemClock.uptimeMillis()); + mPref.writeBoolean(ChromePreferenceKeys.CUSTOM_TABS_LAST_CLOSE_TAB_INTERACTION, true); + + ShadowSystemClock.advanceBy(1, TimeUnit.MINUTES); + } + + @After + public void tearDown() { + mPref.removeKey(ChromePreferenceKeys.CUSTOM_TABS_LAST_CLOSE_TAB_INTERACTION); + mPref.removeKey(ChromePreferenceKeys.CUSTOM_TABS_LAST_CLIENT_PACKAGE); + mPref.removeKey(ChromePreferenceKeys.CUSTOM_TABS_LAST_CLOSE_TIMESTAMP); + mPref.removeKey(ChromePreferenceKeys.CUSTOM_TABS_LAST_REFERRER); + mPref.removeKey(ChromePreferenceKeys.CUSTOM_TABS_LAST_TASK_ID); + mPref.removeKey(ChromePreferenceKeys.CUSTOM_TABS_LAST_URL); + + ShadowRecordHistogram.reset(); + ShadowSystemClock.reset(); + } + + @Test + public void testRecord_SamePackageName() { + recordPrefForTesting(PACKAGE_A, null, TASK_ID_123); + CustomTabActivityLifecycleUmaTracker.recordForRetainableSessions( + PACKAGE_A, REFERRER_A, TASK_ID_123, mPref, /*launchWithSameUri=*/true); + assertInteractionRecorded(ClientIdentifierType.PACKAGE_NAME); + } + + @Test + public void testRecord_DiffPackageName() { + recordPrefForTesting(PACKAGE_A, null, TASK_ID_123); + CustomTabActivityLifecycleUmaTracker.recordForRetainableSessions( + PACKAGE_B, REFERRER_B, TASK_ID_123, mPref, /*launchWithSameUri=*/true); + assertInteractionRecorded(ClientIdentifierType.DIFFERENT); + } + + @Test + public void testRecord_SameReferrer() { + recordPrefForTesting(null, REFERRER_A, TASK_ID_123); + CustomTabActivityLifecycleUmaTracker.recordForRetainableSessions( + null, REFERRER_A, TASK_ID_123, mPref, /*launchWithSameUri=*/true); + assertInteractionRecorded(ClientIdentifierType.REFERRER); + } + + @Test + public void testRecord_DiffReferrer() { + recordPrefForTesting(null, REFERRER_A, TASK_ID_123); + CustomTabActivityLifecycleUmaTracker.recordForRetainableSessions( + null, REFERRER_B, TASK_ID_123, mPref, /*launchWithSameUri=*/true); + assertInteractionRecorded(ClientIdentifierType.DIFFERENT); + } + + @Test + public void testRecord_Mixed_ReferrerThenPackage() { + recordPrefForTesting(null, REFERRER_A, TASK_ID_123); + CustomTabActivityLifecycleUmaTracker.recordForRetainableSessions( + PACKAGE_A, "Random referral", TASK_ID_123, mPref, /*launchWithSameUri=*/true); + assertInteractionRecorded(ClientIdentifierType.MIXED); + } + + @Test + public void testRecord_Mixed_PackageThenReferrer() { + recordPrefForTesting(PACKAGE_A, null, TASK_ID_123); + CustomTabActivityLifecycleUmaTracker.recordForRetainableSessions( + null, REFERRER_A, TASK_ID_123, mPref, /*launchWithSameUri=*/true); + assertInteractionRecorded(ClientIdentifierType.MIXED); + } + + @Test + public void testRecord_DiffUri() { + recordPrefForTesting(null, REFERRER_A, TASK_ID_123); + CustomTabActivityLifecycleUmaTracker.recordForRetainableSessions( + PACKAGE_A, REFERRER_A, TASK_ID_123, mPref, /*launchWithSameUri=*/false); + assertNoInteractionRecorded(); + } + + @Test + public void testRecord_DiffTaskId() { + recordPrefForTesting(PACKAGE_A, null, TASK_ID_123); + CustomTabActivityLifecycleUmaTracker.recordForRetainableSessions( + PACKAGE_A, REFERRER_A, 99, mPref, /*launchWithSameUri=*/true); + assertNoInteractionRecorded(); + } + + @Test + public void testRecord_NoInteraction() { + mPref.writeBoolean(ChromePreferenceKeys.CUSTOM_TABS_LAST_CLOSE_TAB_INTERACTION, false); + + recordPrefForTesting(PACKAGE_A, null, TASK_ID_123); + CustomTabActivityLifecycleUmaTracker.recordForRetainableSessions( + PACKAGE_A, REFERRER_A, TASK_ID_123, mPref, /*launchWithSameUri=*/true); + assertNoInteractionRecorded(); + } + + @Test + public void testGetReferrer() { + String extraActivityReferrer = "android-app://extra.activity.referrer"; + Uri activityReferrer = Uri.parse("android-app://activity.referrer"); + String extraReferrerName = "android-app://extra.referrer.name"; + + Assert.assertEquals("IntentHandler.EXTRA_ACTIVITY_REFERRER should be used.", + extraActivityReferrer, + getReferrer(buildMockActivity( + extraActivityReferrer, activityReferrer, extraReferrerName))); + Assert.assertEquals("Activity#getReferrer should be used.", activityReferrer.toString(), + getReferrer(buildMockActivity(null, activityReferrer, extraReferrerName))); + Assert.assertEquals("Intent.EXTRA_REFERRER should be used.", extraReferrerName, + getReferrer(buildMockActivity(null, null, extraReferrerName))); + } + + @Test + public void testGetReferrer_InvalidInputs() { + Assert.assertTrue(TextUtils.isEmpty(getReferrer(null))); + Assert.assertTrue(TextUtils.isEmpty(getReferrer(mock(Activity.class)))); + + Activity activityWithNoReferral = buildMockActivity(null, null, null); + Assert.assertTrue(TextUtils.isEmpty(getReferrer(activityWithNoReferral))); + } + + private void recordPrefForTesting(String prefPackageName, String prefReferrer, int prefTaskId) { + mPref.writeString(ChromePreferenceKeys.CUSTOM_TABS_LAST_CLIENT_PACKAGE, prefPackageName); + mPref.writeString(ChromePreferenceKeys.CUSTOM_TABS_LAST_REFERRER, prefReferrer); + mPref.writeInt(ChromePreferenceKeys.CUSTOM_TABS_LAST_TASK_ID, prefTaskId); + } + + private void assertInteractionRecorded(@ClientIdentifierType String expectedSuffix) { + String prefix = "CustomTabs.RetainableSessions.TimeBetweenLaunch"; + String[] suffixes = {ClientIdentifierType.REFERRER, ClientIdentifierType.PACKAGE_NAME, + ClientIdentifierType.MIXED, ClientIdentifierType.DIFFERENT}; + + for (String suffix : suffixes) { + String histogram = prefix + suffix; + Assert.assertEquals("<" + histogram + "> record is different.", + expectedSuffix.equals(suffix) ? 1 : 0, + ShadowRecordHistogram.getHistogramTotalCountForTesting(histogram)); + } + } + + private void assertNoInteractionRecorded() { + assertInteractionRecorded(""); + } + + // For test readability + private String getReferrer(Activity activity) { + return CustomTabActivityLifecycleUmaTracker.getReferrerUriString(activity); + } + + private Activity buildMockActivity( + String extraActivityReferrer, Uri activityReferrer, String extraReferrerName) { + Activity activity = mock(Activity.class); + Intent intent = mock(Intent.class); + + doReturn(intent).when(activity).getIntent(); + doReturn(activityReferrer).when(activity).getReferrer(); + doReturn(extraActivityReferrer) + .when(intent) + .getStringExtra(IntentHandler.EXTRA_ACTIVITY_REFERRER); + doReturn(extraReferrerName).when(intent).getStringExtra(Intent.EXTRA_REFERRER_NAME); + + return activity; + } +}
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd index 006b71e95..e919e4a 100644 --- a/chrome/app/generated_resources.grd +++ b/chrome/app/generated_resources.grd
@@ -12287,6 +12287,13 @@ <message name="IDS_WEBAUTHN_BLUETOOTH_POWER_ON_MANUAL_NEXT" desc="Button text. The user clicks this once they manually turned on Bluetooth, so that Chrome would retry connecting to security keys over Bluetooth."> Try again </message> + + <if expr="is_macosx"> + <message name="IDS_WEBAUTHN_BLUETOOTH_PERMISSION" desc="Body text in a dialog that appears when the user has requested some function that depends on Bluetooth, but Chrome doesn't have permission to use Bluetooth. The action on this dialog is a button that will open the macOS System Preferences so that the user can grant permission."> + Chrome needs permission to use Bluetooth to connect to your device + </message> + </if> + <message name="IDS_WEBAUTHN_TRANSPORT_POPUP_LABEL" desc="Menu item text. The user selects this to verify their identity on a web site (i.e. sign in) using a different hardware-based authentication mechanism from what they have selected previously."> Choose another option </message>
diff --git a/chrome/app/generated_resources_grd/IDS_WEBAUTHN_BLUETOOTH_PERMISSION.png.sha1 b/chrome/app/generated_resources_grd/IDS_WEBAUTHN_BLUETOOTH_PERMISSION.png.sha1 new file mode 100644 index 0000000..fc29fd3 --- /dev/null +++ b/chrome/app/generated_resources_grd/IDS_WEBAUTHN_BLUETOOTH_PERMISSION.png.sha1
@@ -0,0 +1 @@ +06813bb2bca58aa7df2c91be2659a6e796912e44 \ No newline at end of file
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn index e9f0512c..b4aa4b5 100644 --- a/chrome/browser/BUILD.gn +++ b/chrome/browser/BUILD.gn
@@ -775,8 +775,8 @@ "memory/memory_ablation_study.h", "memory_details.cc", "memory_details.h", - "metrics/bluetooth_available_utility.cc", - "metrics/bluetooth_available_utility.h", + "metrics/bluetooth_metrics_provider.cc", + "metrics/bluetooth_metrics_provider.h", "metrics/chrome_browser_main_extra_parts_metrics.cc", "metrics/chrome_browser_main_extra_parts_metrics.h", "metrics/chrome_feature_list_creator.cc", @@ -4412,6 +4412,7 @@ "//chrome/services/media_gallery_util/public/cpp", "//components/accuracy_tips", "//components/app_constants", + "//components/autofill_assistant/browser/public:proto", "//components/commerce/core:public", "//components/constrained_window", "//components/device_signals/core/browser",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc index 98eb3c5..34be555 100644 --- a/chrome/browser/about_flags.cc +++ b/chrome/browser/about_flags.cc
@@ -1154,17 +1154,17 @@ {"RichAutocompletionAutocompleteTitlesMinChar", "3"}, {"RichAutocompletionAutocompleteNonPrefixMinChar", "5"}}; const FeatureEntry::FeatureParam kOmniboxRichAutocompletionAggressive2[] = { - {"RichAutocompletionAutocompleteTitles", "true"}, + {"RichAutocompletionAutocompleteTitlesShortcutProvider", "true"}, {"RichAutocompletionAutocompleteTitlesMinChar", "2"}, {"RichAutocompletionAutocompleteShortcutText", "true"}, {"RichAutocompletionAutocompleteShortcutTextMinChar", "2"}}; const FeatureEntry::FeatureParam kOmniboxRichAutocompletionAggressive3[] = { - {"RichAutocompletionAutocompleteTitles", "true"}, + {"RichAutocompletionAutocompleteTitlesShortcutProvider", "true"}, {"RichAutocompletionAutocompleteTitlesMinChar", "3"}, {"RichAutocompletionAutocompleteShortcutText", "true"}, {"RichAutocompletionAutocompleteShortcutTextMinChar", "3"}}; const FeatureEntry::FeatureParam kOmniboxRichAutocompletionAggressive4[] = { - {"RichAutocompletionAutocompleteTitles", "true"}, + {"RichAutocompletionAutocompleteTitlesShortcutProvider", "true"}, {"RichAutocompletionAutocompleteTitlesMinChar", "4"}, {"RichAutocompletionAutocompleteShortcutText", "true"}, {"RichAutocompletionAutocompleteShortcutTextMinChar", "4"}}; @@ -7749,7 +7749,7 @@ {flag_descriptions::kEnableLensFullscreenSearchFlagId, flag_descriptions::kEnableLensFullscreenSearchName, flag_descriptions::kEnableLensFullscreenSearchDescription, kOsDesktop, - FEATURE_VALUE_TYPE(lens::features::kLensFullscreenSearch)}, + FEATURE_VALUE_TYPE(lens::features::kLensSearchOptimizations)}, {flag_descriptions::kEnableLensStandaloneFlagId, flag_descriptions::kEnableLensStandaloneName, flag_descriptions::kEnableLensStandaloneDescription, kOsDesktop, @@ -8662,6 +8662,11 @@ FEATURE_VALUE_TYPE(blink::features::kSystemColorChooser)}, #endif // BUILDFLAG(IS_MAC) + {"ignore-sync-encryption-keys-long-missing", + flag_descriptions::kIgnoreSyncEncryptionKeysLongMissingName, + flag_descriptions::kIgnoreSyncEncryptionKeysLongMissingDescription, kOsAll, + FEATURE_VALUE_TYPE(syncer::kIgnoreSyncEncryptionKeysLongMissing)}, + // NOTE: Adding a new flag requires adding a corresponding entry to enum // "LoginCustomFlags" in tools/metrics/histograms/enums.xml. See "Flag // Histograms" in tools/metrics/histograms/README.md (run the
diff --git a/chrome/browser/app_controller_mac_browsertest.mm b/chrome/browser/app_controller_mac_browsertest.mm index 3864676..57a5ffb 100644 --- a/chrome/browser/app_controller_mac_browsertest.mm +++ b/chrome/browser/app_controller_mac_browsertest.mm
@@ -1131,7 +1131,8 @@ // Test that switching tabs updates the handoff URL. browser()->tab_strip_model()->ActivateTabAt( - 0, {TabStripModel::GestureType::kOther}); + 0, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); EXPECT_EQ(g_handoff_url, test_url1); EXPECT_TRUE(base::EndsWith(g_handoff_title, u"title1.html"));
diff --git a/chrome/browser/ash/BUILD.gn b/chrome/browser/ash/BUILD.gn index b03fa3c..72b98f2 100644 --- a/chrome/browser/ash/BUILD.gn +++ b/chrome/browser/ash/BUILD.gn
@@ -2303,6 +2303,7 @@ "//chromeos/dbus/oobe_config", "//chromeos/dbus/permission_broker", "//chromeos/dbus/shill", + "//chromeos/dbus/smbprovider", "//chromeos/dbus/u2f", "//chromeos/dbus/util", "//chromeos/dbus/virtual_file_provider",
diff --git a/chrome/browser/ash/arc/fileapi/arc_file_system_bridge.cc b/chrome/browser/ash/arc/fileapi/arc_file_system_bridge.cc index e1eabbd..1e7b7a13 100644 --- a/chrome/browser/ash/arc/fileapi/arc_file_system_bridge.cc +++ b/chrome/browser/ash/arc/fileapi/arc_file_system_bridge.cc
@@ -32,7 +32,6 @@ #include "chrome/browser/ash/file_manager/path_util.h" #include "chrome/browser/chromeos/fileapi/external_file_url_util.h" #include "chrome/browser/profiles/profile.h" -#include "chromeos/dbus/dbus_thread_manager.h" #include "chromeos/dbus/virtual_file_provider/virtual_file_provider_client.h" #include "content/public/browser/browser_task_traits.h" #include "content/public/browser/browser_thread.h" @@ -401,12 +400,10 @@ std::move(callback).Run(absl::nullopt); return; } - chromeos::DBusThreadManager::Get() - ->GetVirtualFileProviderClient() - ->GenerateVirtualFileId( - size, base::BindOnce(&ArcFileSystemBridge::OnGenerateVirtualFileId, - weak_ptr_factory_.GetWeakPtr(), url_decoded, - std::move(callback))); + chromeos::VirtualFileProviderClient::Get()->GenerateVirtualFileId( + size, base::BindOnce(&ArcFileSystemBridge::OnGenerateVirtualFileId, + weak_ptr_factory_.GetWeakPtr(), url_decoded, + std::move(callback))); } void ArcFileSystemBridge::OnGenerateVirtualFileId( @@ -431,12 +428,10 @@ return; } - chromeos::DBusThreadManager::Get() - ->GetVirtualFileProviderClient() - ->OpenFileById(id.value(), - base::BindOnce(&ArcFileSystemBridge::OnOpenFileById, - weak_ptr_factory_.GetWeakPtr(), url_decoded, - std::move(callback), id.value())); + chromeos::VirtualFileProviderClient::Get()->OpenFileById( + id.value(), base::BindOnce(&ArcFileSystemBridge::OnOpenFileById, + weak_ptr_factory_.GetWeakPtr(), url_decoded, + std::move(callback), id.value())); } void ArcFileSystemBridge::OnOpenFileById(const GURL& url_decoded,
diff --git a/chrome/browser/ash/arc/fileapi/arc_file_system_bridge_unittest.cc b/chrome/browser/ash/arc/fileapi/arc_file_system_bridge_unittest.cc index d884323..12d7242 100644 --- a/chrome/browser/ash/arc/fileapi/arc_file_system_bridge_unittest.cc +++ b/chrome/browser/ash/arc/fileapi/arc_file_system_bridge_unittest.cc
@@ -27,8 +27,8 @@ #include "chrome/test/base/testing_browser_process.h" #include "chrome/test/base/testing_profile_manager.h" #include "chromeos/ash/components/dbus/concierge/concierge_client.h" -#include "chromeos/dbus/dbus_thread_manager.h" #include "chromeos/dbus/virtual_file_provider/fake_virtual_file_provider_client.h" +#include "chromeos/dbus/virtual_file_provider/virtual_file_provider_client.h" #include "content/public/test/browser_task_environment.h" #include "content/public/test/test_utils.h" #include "storage/browser/file_system/external_mount_points.h" @@ -57,8 +57,8 @@ void SetUp() override { ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); - chromeos::DBusThreadManager::Initialize(); ash::ConciergeClient::InitializeFake(/*fake_cicerone_client=*/nullptr); + chromeos::VirtualFileProviderClient::InitializeFake(); profile_manager_ = std::make_unique<TestingProfileManager>( TestingBrowserProcess::GetGlobal()); ASSERT_TRUE(profile_manager_->SetUp()); @@ -82,8 +82,8 @@ arc_bridge_service_.file_system()->CloseInstance(&fake_file_system_); arc_file_system_bridge_.reset(); profile_manager_.reset(); + chromeos::VirtualFileProviderClient::Shutdown(); ash::ConciergeClient::Shutdown(); - chromeos::DBusThreadManager::Shutdown(); } protected: @@ -219,7 +219,7 @@ // Set up fake virtual file provider client. constexpr char kId[] = "testfile"; auto* fake_client = static_cast<chromeos::FakeVirtualFileProviderClient*>( - chromeos::DBusThreadManager::Get()->GetVirtualFileProviderClient()); + chromeos::VirtualFileProviderClient::Get()); fake_client->set_expected_size(kTestFileSize); fake_client->set_result_id(kId); @@ -253,7 +253,7 @@ constexpr char kId[] = "testfile"; auto* fake_client = static_cast<chromeos::FakeVirtualFileProviderClient*>( - chromeos::DBusThreadManager::Get()->GetVirtualFileProviderClient()); + chromeos::VirtualFileProviderClient::Get()); fake_client->set_expected_size(kTestFileSize); fake_client->set_result_id(kId); fake_client->set_result_fd(base::ScopedFD(temp_file.TakePlatformFile()));
diff --git a/chrome/browser/ash/borealis/borealis_context_unittest.cc b/chrome/browser/ash/borealis/borealis_context_unittest.cc index c77da45..df2bc27 100644 --- a/chrome/browser/ash/borealis/borealis_context_unittest.cc +++ b/chrome/browser/ash/borealis/borealis_context_unittest.cc
@@ -26,7 +26,6 @@ #include "chromeos/ash/components/dbus/concierge/fake_concierge_client.h" #include "chromeos/ash/components/dbus/seneschal/fake_seneschal_client.h" #include "chromeos/dbus/chunneld/fake_chunneld_client.h" -#include "chromeos/dbus/dbus_thread_manager.h" #include "components/exo/shell_surface_util.h" #include "content/public/test/browser_task_environment.h" #include "testing/gtest/include/gtest/gtest.h" @@ -140,7 +139,7 @@ TEST_F(BorealisContextTest, ChunneldFailure) { auto* chunneld_client = static_cast<chromeos::FakeChunneldClient*>( - chromeos::DBusThreadManager::Get()->GetChunneldClient()); + chromeos::ChunneldClient::Get()); chunneld_client->NotifyChunneldStopped(); histogram_tester_.ExpectUniqueSample(
diff --git a/chrome/browser/ash/crostini/ansible/ansible_management_service_unittest.cc b/chrome/browser/ash/crostini/ansible/ansible_management_service_unittest.cc index e107120..2147a47 100644 --- a/chrome/browser/ash/crostini/ansible/ansible_management_service_unittest.cc +++ b/chrome/browser/ash/crostini/ansible/ansible_management_service_unittest.cc
@@ -12,6 +12,7 @@ #include "chromeos/ash/components/dbus/cicerone/cicerone_client.h" #include "chromeos/ash/components/dbus/concierge/concierge_client.h" #include "chromeos/ash/components/dbus/seneschal/seneschal_client.h" +#include "chromeos/dbus/chunneld/chunneld_client.h" #include "chromeos/dbus/dbus_thread_manager.h" #include "components/prefs/pref_service.h" #include "content/public/test/browser_task_environment.h" @@ -34,6 +35,7 @@ public: AnsibleManagementServiceTest() { chromeos::DBusThreadManager::Initialize(); + chromeos::ChunneldClient::InitializeFake(); ash::CiceroneClient::InitializeFake(); ash::ConciergeClient::InitializeFake(); ash::SeneschalClient::InitializeFake(); @@ -67,6 +69,7 @@ ash::SeneschalClient::Shutdown(); ash::ConciergeClient::Shutdown(); ash::CiceroneClient::Shutdown(); + chromeos::ChunneldClient::Shutdown(); chromeos::DBusThreadManager::Shutdown(); }
diff --git a/chrome/browser/ash/crostini/crostini_disk_unittest.cc b/chrome/browser/ash/crostini/crostini_disk_unittest.cc index ae68fb6..227d466 100644 --- a/chrome/browser/ash/crostini/crostini_disk_unittest.cc +++ b/chrome/browser/ash/crostini/crostini_disk_unittest.cc
@@ -17,6 +17,7 @@ #include "chromeos/ash/components/dbus/concierge/concierge_service.pb.h" #include "chromeos/ash/components/dbus/concierge/fake_concierge_client.h" #include "chromeos/ash/components/dbus/seneschal/seneschal_client.h" +#include "chromeos/dbus/chunneld/chunneld_client.h" #include "chromeos/dbus/dbus_thread_manager.h" #include "content/public/test/browser_task_environment.h" #include "testing/gmock/include/gmock/gmock-matchers.h" @@ -54,6 +55,7 @@ public: CrostiniDiskTestDbus() { chromeos::DBusThreadManager::Initialize(); + chromeos::ChunneldClient::InitializeFake(); ash::CiceroneClient::InitializeFake(); ash::ConciergeClient::InitializeFake(); ash::SeneschalClient::InitializeFake(); @@ -73,6 +75,7 @@ ash::SeneschalClient::Shutdown(); ash::ConciergeClient::Shutdown(); ash::CiceroneClient::Shutdown(); + chromeos::ChunneldClient::Shutdown(); chromeos::DBusThreadManager::Shutdown(); }
diff --git a/chrome/browser/ash/crostini/crostini_export_import_unittest.cc b/chrome/browser/ash/crostini/crostini_export_import_unittest.cc index e48c4cd..ecaf3ff7 100644 --- a/chrome/browser/ash/crostini/crostini_export_import_unittest.cc +++ b/chrome/browser/ash/crostini/crostini_export_import_unittest.cc
@@ -21,6 +21,7 @@ #include "chromeos/ash/components/dbus/seneschal/fake_seneschal_client.h" #include "chromeos/ash/components/dbus/seneschal/seneschal_client.h" #include "chromeos/ash/components/dbus/seneschal/seneschal_service.pb.h" +#include "chromeos/dbus/chunneld/chunneld_client.h" #include "chromeos/dbus/dbus_thread_manager.h" #include "components/prefs/pref_service.h" #include "content/public/test/browser_task_environment.h" @@ -125,6 +126,7 @@ : default_container_id_(DefaultContainerId()), custom_container_id_(kCrostiniDefaultVmType, "MyVM", "MyContainer") { chromeos::DBusThreadManager::Initialize(); + chromeos::ChunneldClient::InitializeFake(); ash::CiceroneClient::InitializeFake(); ash::ConciergeClient::InitializeFake(); ash::SeneschalClient::InitializeFake(); @@ -139,6 +141,7 @@ ash::SeneschalClient::Shutdown(); ash::ConciergeClient::Shutdown(); ash::CiceroneClient::Shutdown(); + chromeos::ChunneldClient::Shutdown(); chromeos::DBusThreadManager::Shutdown(); }
diff --git a/chrome/browser/ash/crostini/crostini_installer_unittest.cc b/chrome/browser/ash/crostini/crostini_installer_unittest.cc index 0384a99..006c7ca 100644 --- a/chrome/browser/ash/crostini/crostini_installer_unittest.cc +++ b/chrome/browser/ash/crostini/crostini_installer_unittest.cc
@@ -27,6 +27,7 @@ #include "chromeos/ash/components/dbus/concierge/concierge_service.pb.h" #include "chromeos/ash/components/dbus/concierge/fake_concierge_client.h" #include "chromeos/ash/components/dbus/seneschal/seneschal_client.h" +#include "chromeos/dbus/chunneld/chunneld_client.h" #include "chromeos/dbus/dbus_thread_manager.h" #include "chromeos/dbus/dlcservice/dlcservice_client.h" #include "content/public/test/browser_task_environment.h" @@ -116,6 +117,7 @@ chromeos::DlcserviceClient::InitializeFake(); chromeos::DBusThreadManager::Initialize(); + chromeos::ChunneldClient::InitializeFake(); ash::CiceroneClient::InitializeFake(); SetOSRelease(); waiting_fake_concierge_client_ = @@ -153,6 +155,7 @@ ash::SeneschalClient::Shutdown(); ash::ConciergeClient::Shutdown(); ash::CiceroneClient::Shutdown(); + chromeos::ChunneldClient::Shutdown(); chromeos::DBusThreadManager::Shutdown(); chromeos::DlcserviceClient::Shutdown();
diff --git a/chrome/browser/ash/crostini/crostini_manager_unittest.cc b/chrome/browser/ash/crostini/crostini_manager_unittest.cc index 482d068..d4d764e 100644 --- a/chrome/browser/ash/crostini/crostini_manager_unittest.cc +++ b/chrome/browser/ash/crostini/crostini_manager_unittest.cc
@@ -47,6 +47,7 @@ #include "chromeos/ash/components/dbus/seneschal/seneschal_client.h" #include "chromeos/ash/components/dbus/session_manager/fake_session_manager_client.h" #include "chromeos/ash/components/dbus/userdataauth/fake_cryptohome_misc_client.h" +#include "chromeos/dbus/chunneld/chunneld_client.h" #include "chromeos/dbus/dbus_thread_manager.h" #include "chromeos/dbus/dlcservice/dlcservice_client.h" #include "components/account_id/account_id.h" @@ -193,6 +194,7 @@ browser_part_(g_browser_process->platform_part()) { chromeos::DBusThreadManager::Initialize(); ash::AnomalyDetectorClient::InitializeFake(); + chromeos::ChunneldClient::InitializeFake(); ash::CiceroneClient::InitializeFake(); ash::ConciergeClient::InitializeFake(); ash::SeneschalClient::InitializeFake(); @@ -211,6 +213,7 @@ ash::SeneschalClient::Shutdown(); ash::ConciergeClient::Shutdown(); ash::CiceroneClient::Shutdown(); + chromeos::ChunneldClient::Shutdown(); chromeos::DBusThreadManager::Shutdown(); }
diff --git a/chrome/browser/ash/crostini/crostini_package_notification_unittest.cc b/chrome/browser/ash/crostini/crostini_package_notification_unittest.cc index 7eb16791..13d116e 100644 --- a/chrome/browser/ash/crostini/crostini_package_notification_unittest.cc +++ b/chrome/browser/ash/crostini/crostini_package_notification_unittest.cc
@@ -14,6 +14,7 @@ #include "chromeos/ash/components/dbus/cicerone/cicerone_client.h" #include "chromeos/ash/components/dbus/concierge/concierge_client.h" #include "chromeos/ash/components/dbus/seneschal/seneschal_client.h" +#include "chromeos/dbus/chunneld/chunneld_client.h" #include "chromeos/dbus/dbus_thread_manager.h" #include "content/public/test/browser_task_environment.h" #include "testing/gmock/include/gmock/gmock.h" @@ -35,6 +36,7 @@ void SetUp() override { DBusThreadManager::Initialize(); + chromeos::ChunneldClient::InitializeFake(); ash::CiceroneClient::InitializeFake(); ash::ConciergeClient::InitializeFake(); ash::SeneschalClient::InitializeFake(); @@ -57,6 +59,7 @@ ash::SeneschalClient::Shutdown(); ash::ConciergeClient::Shutdown(); ash::CiceroneClient::Shutdown(); + chromeos::ChunneldClient::Shutdown(); DBusThreadManager::Shutdown(); }
diff --git a/chrome/browser/ash/crostini/crostini_package_service_unittest.cc b/chrome/browser/ash/crostini/crostini_package_service_unittest.cc index f3a7ea9..bd2650d 100644 --- a/chrome/browser/ash/crostini/crostini_package_service_unittest.cc +++ b/chrome/browser/ash/crostini/crostini_package_service_unittest.cc
@@ -25,6 +25,7 @@ #include "chromeos/ash/components/dbus/concierge/fake_concierge_client.h" #include "chromeos/ash/components/dbus/seneschal/fake_seneschal_client.h" #include "chromeos/ash/components/dbus/seneschal/seneschal_client.h" +#include "chromeos/dbus/chunneld/chunneld_client.h" #include "chromeos/dbus/dbus_thread_manager.h" #include "chromeos/dbus/vm_applications/apps.pb.h" #include "content/public/test/browser_task_environment.h" @@ -169,7 +170,7 @@ void SetUp() override { DBusThreadManager::Initialize(); - + chromeos::ChunneldClient::InitializeFake(); ash::CiceroneClient::InitializeFake(); ash::ConciergeClient::InitializeFake(); ash::SeneschalClient::InitializeFake(); @@ -231,6 +232,7 @@ ash::SeneschalClient::Shutdown(); ash::ConciergeClient::Shutdown(); ash::CiceroneClient::Shutdown(); + chromeos::ChunneldClient::Shutdown(); DBusThreadManager::Shutdown(); }
diff --git a/chrome/browser/ash/crostini/crostini_port_forwarder_unittest.cc b/chrome/browser/ash/crostini/crostini_port_forwarder_unittest.cc index c35f521..b60bf5074 100644 --- a/chrome/browser/ash/crostini/crostini_port_forwarder_unittest.cc +++ b/chrome/browser/ash/crostini/crostini_port_forwarder_unittest.cc
@@ -11,6 +11,7 @@ #include "chromeos/ash/components/dbus/cicerone/cicerone_client.h" #include "chromeos/ash/components/dbus/concierge/concierge_client.h" #include "chromeos/ash/components/dbus/seneschal/seneschal_client.h" +#include "chromeos/dbus/chunneld/chunneld_client.h" #include "chromeos/dbus/dbus_thread_manager.h" #include "chromeos/dbus/permission_broker/fake_permission_broker_client.h" #include "content/public/test/browser_task_environment.h" @@ -46,6 +47,7 @@ void SetUp() override { chromeos::DBusThreadManager::Initialize(); + chromeos::ChunneldClient::InitializeFake(); ash::CiceroneClient::InitializeFake(); ash::ConciergeClient::InitializeFake(); ash::SeneschalClient::InitializeFake(); @@ -72,6 +74,7 @@ ash::SeneschalClient::Shutdown(); ash::ConciergeClient::Shutdown(); ash::CiceroneClient::Shutdown(); + chromeos::ChunneldClient::Shutdown(); chromeos::DBusThreadManager::Shutdown(); }
diff --git a/chrome/browser/ash/crostini/crostini_sshfs_unittest.cc b/chrome/browser/ash/crostini/crostini_sshfs_unittest.cc index cd6ef2cb8..85644b42 100644 --- a/chrome/browser/ash/crostini/crostini_sshfs_unittest.cc +++ b/chrome/browser/ash/crostini/crostini_sshfs_unittest.cc
@@ -25,6 +25,7 @@ #include "chromeos/ash/components/dbus/concierge/concierge_client.h" #include "chromeos/ash/components/dbus/concierge/fake_concierge_client.h" #include "chromeos/ash/components/dbus/seneschal/seneschal_client.h" +#include "chromeos/dbus/chunneld/chunneld_client.h" #include "chromeos/dbus/cros_disks/cros_disks_client.h" #include "chromeos/dbus/dbus_thread_manager.h" #include "chromeos/dbus/vm_applications/apps.pb.h" @@ -66,6 +67,7 @@ public: CrostiniSshfsHelperTest() { chromeos::DBusThreadManager::Initialize(); + chromeos::ChunneldClient::InitializeFake(); ash::CiceroneClient::InitializeFake(); ash::ConciergeClient::InitializeFake(); ash::SeneschalClient::InitializeFake(); @@ -105,6 +107,7 @@ ash::SeneschalClient::Shutdown(); ash::ConciergeClient::Shutdown(); ash::CiceroneClient::Shutdown(); + chromeos::ChunneldClient::Shutdown(); chromeos::DBusThreadManager::Shutdown(); }
diff --git a/chrome/browser/ash/crostini/crostini_upgrade_available_notification_unittest.cc b/chrome/browser/ash/crostini/crostini_upgrade_available_notification_unittest.cc index c10ccb5..f5473a2 100644 --- a/chrome/browser/ash/crostini/crostini_upgrade_available_notification_unittest.cc +++ b/chrome/browser/ash/crostini/crostini_upgrade_available_notification_unittest.cc
@@ -21,6 +21,7 @@ #include "chromeos/ash/components/dbus/cicerone/cicerone_service.pb.h" #include "chromeos/ash/components/dbus/concierge/concierge_client.h" #include "chromeos/ash/components/dbus/seneschal/seneschal_client.h" +#include "chromeos/dbus/chunneld/chunneld_client.h" #include "chromeos/dbus/dbus_thread_manager.h" #include "content/public/test/browser_task_environment.h" #include "testing/gtest/include/gtest/gtest.h" @@ -46,6 +47,7 @@ void SetUp() override { BrowserWithTestWindowTest::SetUp(); chromeos::DBusThreadManager::Initialize(); + chromeos::ChunneldClient::InitializeFake(); ash::CiceroneClient::InitializeFake(); ash::ConciergeClient::InitializeFake(); ash::SeneschalClient::InitializeFake(); @@ -63,6 +65,7 @@ ash::SeneschalClient::Shutdown(); ash::ConciergeClient::Shutdown(); ash::CiceroneClient::Shutdown(); + chromeos::ChunneldClient::Shutdown(); chromeos::DBusThreadManager::Shutdown(); }
diff --git a/chrome/browser/ash/crostini/crostini_util_unittest.cc b/chrome/browser/ash/crostini/crostini_util_unittest.cc index d46214f..bea1208b 100644 --- a/chrome/browser/ash/crostini/crostini_util_unittest.cc +++ b/chrome/browser/ash/crostini/crostini_util_unittest.cc
@@ -19,6 +19,7 @@ #include "chromeos/ash/components/dbus/concierge/concierge_client.h" #include "chromeos/ash/components/dbus/concierge/fake_concierge_client.h" #include "chromeos/ash/components/dbus/seneschal/seneschal_client.h" +#include "chromeos/dbus/chunneld/chunneld_client.h" #include "chromeos/dbus/dbus_thread_manager.h" #include "chromeos/dbus/dlcservice/dlcservice_client.h" #include "content/public/test/browser_task_environment.h" @@ -47,6 +48,7 @@ TestingBrowserProcess::GetGlobal())), browser_part_(g_browser_process->platform_part()) { chromeos::DBusThreadManager::Initialize(); + chromeos::ChunneldClient::InitializeFake(); ash::CiceroneClient::InitializeFake(); ash::ConciergeClient::InitializeFake(); ash::SeneschalClient::InitializeFake(); @@ -58,6 +60,7 @@ ash::SeneschalClient::Shutdown(); ash::ConciergeClient::Shutdown(); ash::CiceroneClient::Shutdown(); + chromeos::ChunneldClient::Shutdown(); chromeos::DBusThreadManager::Shutdown(); }
diff --git a/chrome/browser/ash/dbus/ash_dbus_helper.cc b/chrome/browser/ash/dbus/ash_dbus_helper.cc index 6ff90bc..03d85c1 100644 --- a/chrome/browser/ash/dbus/ash_dbus_helper.cc +++ b/chrome/browser/ash/dbus/ash_dbus_helper.cc
@@ -51,6 +51,7 @@ #include "chromeos/dbus/arc/arc_sensor_service_client.h" #include "chromeos/dbus/attestation/attestation_client.h" #include "chromeos/dbus/cdm_factory_daemon/cdm_factory_daemon_client.h" +#include "chromeos/dbus/chunneld/chunneld_client.h" #include "chromeos/dbus/constants/dbus_paths.h" #include "chromeos/dbus/dbus_thread_manager.h" #include "chromeos/dbus/dlcservice/dlcservice_client.h" @@ -65,9 +66,11 @@ #include "chromeos/dbus/permission_broker/permission_broker_client.h" #include "chromeos/dbus/power/power_manager_client.h" #include "chromeos/dbus/resourced/resourced_client.h" +#include "chromeos/dbus/smbprovider/smb_provider_client.h" #include "chromeos/dbus/tpm_manager/tpm_manager_client.h" #include "chromeos/dbus/u2f/u2f_client.h" #include "chromeos/dbus/update_engine/update_engine_client.h" +#include "chromeos/dbus/virtual_file_provider/virtual_file_provider_client.h" #include "chromeos/dbus/vm_plugin_dispatcher/vm_plugin_dispatcher_client.h" #include "device/bluetooth/dbus/bluez_dbus_manager.h" #include "device/bluetooth/floss/floss_dbus_manager.h" @@ -123,6 +126,7 @@ InitializeDBusClient<AuthPolicyClient>(bus); InitializeDBusClient<BiodClient>(bus); // For device::Fingerprint. InitializeDBusClient<chromeos::CdmFactoryDaemonClient>(bus); + InitializeDBusClient<chromeos::ChunneldClient>(bus); InitializeDBusClient<CiceroneClient>(bus); // ConciergeClient depends on CiceroneClient. InitializeDBusClient<ConciergeClient>(bus); @@ -154,6 +158,7 @@ InitializeDBusClient<chromeos::ResourcedClient>(bus); InitializeDBusClient<SeneschalClient>(bus); InitializeDBusClient<SessionManagerClient>(bus); + InitializeDBusClient<SmbProviderClient>(bus); InitializeDBusClient<SpacedClient>(bus); InitializeDBusClient<SystemClockClient>(bus); InitializeDBusClient<SystemProxyClient>(bus); @@ -163,6 +168,7 @@ InitializeDBusClient<chromeos::UpdateEngineClient>(bus); InitializeDBusClient<UserDataAuthClient>(bus); InitializeDBusClient<UpstartClient>(bus); + InitializeDBusClient<chromeos::VirtualFileProviderClient>(bus); InitializeDBusClient<chromeos::VmPluginDispatcherClient>(bus); // Initialize the device settings service so that we'll take actions per @@ -220,6 +226,7 @@ } // Other D-Bus clients are shut down, also in reverse order of initialization. chromeos::VmPluginDispatcherClient::Shutdown(); + chromeos::VirtualFileProviderClient::Shutdown(); UpstartClient::Shutdown(); UserDataAuthClient::Shutdown(); chromeos::UpdateEngineClient::Shutdown(); @@ -229,6 +236,7 @@ SystemProxyClient::Shutdown(); SystemClockClient::Shutdown(); SpacedClient::Shutdown(); + SmbProviderClient::Shutdown(); SessionManagerClient::Shutdown(); SeneschalClient::Shutdown(); chromeos::ResourcedClient::Shutdown(); @@ -265,6 +273,7 @@ CrasAudioClient::Shutdown(); ConciergeClient::Shutdown(); CiceroneClient::Shutdown(); + chromeos::ChunneldClient::Shutdown(); chromeos::CdmFactoryDaemonClient::Shutdown(); BiodClient::Shutdown(); AuthPolicyClient::Shutdown();
diff --git a/chrome/browser/ash/exo/chrome_data_exchange_delegate_unittest.cc b/chrome/browser/ash/exo/chrome_data_exchange_delegate_unittest.cc index de8cb87..e466de92 100644 --- a/chrome/browser/ash/exo/chrome_data_exchange_delegate_unittest.cc +++ b/chrome/browser/ash/exo/chrome_data_exchange_delegate_unittest.cc
@@ -28,6 +28,7 @@ #include "chromeos/ash/components/dbus/concierge/concierge_client.h" #include "chromeos/ash/components/dbus/seneschal/fake_seneschal_client.h" #include "chromeos/ash/components/dbus/seneschal/seneschal_client.h" +#include "chromeos/dbus/chunneld/chunneld_client.h" #include "chromeos/dbus/dbus_thread_manager.h" #include "components/exo/shell_surface_util.h" #include "content/public/common/drop_data.h" @@ -73,6 +74,7 @@ public: void SetUp() override { chromeos::DBusThreadManager::Initialize(); + chromeos::ChunneldClient::InitializeFake(); ash::CiceroneClient::InitializeFake(); ConciergeClient::InitializeFake(); SeneschalClient::InitializeFake(); @@ -120,6 +122,7 @@ SeneschalClient::Shutdown(); ConciergeClient::Shutdown(); ash::CiceroneClient::Shutdown(); + chromeos::ChunneldClient::Shutdown(); chromeos::DBusThreadManager::Shutdown(); }
diff --git a/chrome/browser/ash/file_manager/path_util_unittest.cc b/chrome/browser/ash/file_manager/path_util_unittest.cc index 305863f..49e5453d 100644 --- a/chrome/browser/ash/file_manager/path_util_unittest.cc +++ b/chrome/browser/ash/file_manager/path_util_unittest.cc
@@ -46,6 +46,7 @@ #include "chromeos/ash/components/dbus/cicerone/cicerone_client.h" #include "chromeos/ash/components/dbus/concierge/concierge_client.h" #include "chromeos/ash/components/dbus/seneschal/seneschal_client.h" +#include "chromeos/dbus/chunneld/chunneld_client.h" #include "chromeos/dbus/cros_disks/cros_disks_client.h" #include "chromeos/dbus/dbus_thread_manager.h" #include "components/account_id/account_id.h" @@ -330,6 +331,7 @@ // Initialize DBUS and running container. chromeos::DBusThreadManager::Initialize(); + chromeos::ChunneldClient::InitializeFake(); ash::CiceroneClient::InitializeFake(); ash::ConciergeClient::InitializeFake(); ash::SeneschalClient::InitializeFake(); @@ -506,6 +508,7 @@ profile_.reset(); ash::SeneschalClient::Shutdown(); ash::ConciergeClient::Shutdown(); + chromeos::ChunneldClient::Shutdown(); chromeos::DBusThreadManager::Shutdown(); }
diff --git a/chrome/browser/ash/file_manager/trash_unittest_base.cc b/chrome/browser/ash/file_manager/trash_unittest_base.cc index 09dd8bd..02e6ec9b 100644 --- a/chrome/browser/ash/file_manager/trash_unittest_base.cc +++ b/chrome/browser/ash/file_manager/trash_unittest_base.cc
@@ -14,6 +14,7 @@ #include "chrome/browser/ash/file_manager/volume_manager_factory.h" #include "chrome/browser/ash/login/users/fake_chrome_user_manager.h" #include "chromeos/ash/components/dbus/seneschal/seneschal_client.h" +#include "chromeos/dbus/chunneld/chunneld_client.h" #include "chromeos/dbus/dbus_thread_manager.h" #include "storage/browser/file_system/external_mount_points.h" #include "storage/browser/test/test_file_system_context.h" @@ -61,6 +62,7 @@ ASSERT_TRUE(base::CreateDirectory(downloads_dir_)); chromeos::DBusThreadManager::Initialize(); + chromeos::ChunneldClient::InitializeFake(); ash::CiceroneClient::InitializeFake(); ash::ConciergeClient::InitializeFake(); ash::SeneschalClient::InitializeFake(); @@ -98,6 +100,7 @@ ash::SeneschalClient::Shutdown(); ash::ConciergeClient::Shutdown(); ash::CiceroneClient::Shutdown(); + chromeos::ChunneldClient::Shutdown(); chromeos::DBusThreadManager::Shutdown(); }
diff --git a/chrome/browser/ash/guest_os/dbus_test_helper.cc b/chrome/browser/ash/guest_os/dbus_test_helper.cc index 71a6a3f..9878161 100644 --- a/chrome/browser/ash/guest_os/dbus_test_helper.cc +++ b/chrome/browser/ash/guest_os/dbus_test_helper.cc
@@ -7,6 +7,7 @@ #include "chromeos/ash/components/dbus/cicerone/fake_cicerone_client.h" #include "chromeos/ash/components/dbus/concierge/fake_concierge_client.h" #include "chromeos/ash/components/dbus/seneschal/fake_seneschal_client.h" +#include "chromeos/dbus/chunneld/fake_chunneld_client.h" #include "chromeos/dbus/dbus_thread_manager.h" #include "chromeos/dbus/dlcservice/fake_dlcservice_client.h" @@ -73,10 +74,20 @@ return ash::FakeConciergeClient::Get(); } +FakeChunneldHelper::FakeChunneldHelper(BasicDBusHelper* basic_helper) { + DCHECK(basic_helper); + chromeos::ChunneldClient::InitializeFake(); +} + +FakeChunneldHelper::~FakeChunneldHelper() { + chromeos::ChunneldClient::Shutdown(); +} + FakeVmServicesHelper::FakeVmServicesHelper() : FakeCiceroneHelper(this), FakeSeneschalHelper(this), FakeDlcserviceHelper(this), - FakeConciergeHelper(this) {} + FakeConciergeHelper(this), + FakeChunneldHelper(this) {} } // namespace guest_os
diff --git a/chrome/browser/ash/guest_os/dbus_test_helper.h b/chrome/browser/ash/guest_os/dbus_test_helper.h index 693676c8..55e3a84 100644 --- a/chrome/browser/ash/guest_os/dbus_test_helper.h +++ b/chrome/browser/ash/guest_os/dbus_test_helper.h
@@ -58,6 +58,12 @@ ash::FakeConciergeClient* FakeConciergeClient(); }; +class FakeChunneldHelper { + public: + explicit FakeChunneldHelper(BasicDBusHelper* basic_helper); + ~FakeChunneldHelper(); +}; + // A class for less boilerplate in VM tests. Have your fixture inherit from this // class, and the dbus services common to most VMs get initialised with fakes // during before your test and torn down correctly after. @@ -66,7 +72,8 @@ public FakeCiceroneHelper, public FakeSeneschalHelper, public FakeDlcserviceHelper, - public FakeConciergeHelper { + public FakeConciergeHelper, + public FakeChunneldHelper { public: FakeVmServicesHelper(); };
diff --git a/chrome/browser/ash/guest_os/guest_os_share_path_unittest.cc b/chrome/browser/ash/guest_os/guest_os_share_path_unittest.cc index ee91a18e..8699039 100644 --- a/chrome/browser/ash/guest_os/guest_os_share_path_unittest.cc +++ b/chrome/browser/ash/guest_os/guest_os_share_path_unittest.cc
@@ -39,6 +39,7 @@ #include "chromeos/ash/components/dbus/seneschal/fake_seneschal_client.h" #include "chromeos/ash/components/dbus/seneschal/seneschal_client.h" #include "chromeos/ash/components/dbus/seneschal/seneschal_service.pb.h" +#include "chromeos/dbus/chunneld/chunneld_client.h" #include "chromeos/dbus/dbus_thread_manager.h" #include "chromeos/dbus/dlcservice/dlcservice_client.h" #include "chromeos/dbus/vm_plugin_dispatcher/vm_plugin_dispatcher_client.h" @@ -225,6 +226,7 @@ TestingBrowserProcess::GetGlobal())), browser_part_(g_browser_process->platform_part()) { chromeos::DBusThreadManager::Initialize(); + chromeos::ChunneldClient::InitializeFake(); ash::CiceroneClient::InitializeFake(); ash::ConciergeClient::InitializeFake(); ash::SeneschalClient::InitializeFake(); @@ -242,6 +244,7 @@ ash::SeneschalClient::Shutdown(); ash::ConciergeClient::Shutdown(); ash::CiceroneClient::Shutdown(); + chromeos::ChunneldClient::Shutdown(); chromeos::DBusThreadManager::Shutdown(); }
diff --git a/chrome/browser/ash/guest_os/guest_os_stability_monitor.cc b/chrome/browser/ash/guest_os/guest_os_stability_monitor.cc index c80d5cb..6c39106 100644 --- a/chrome/browser/ash/guest_os/guest_os_stability_monitor.cc +++ b/chrome/browser/ash/guest_os/guest_os_stability_monitor.cc
@@ -5,7 +5,7 @@ #include "chrome/browser/ash/guest_os/guest_os_stability_monitor.h" #include "base/metrics/histogram_functions.h" -#include "chromeos/dbus/dbus_thread_manager.h" +#include "chromeos/dbus/chunneld/chunneld_client.h" namespace guest_os { @@ -33,8 +33,7 @@ base::BindOnce(&GuestOsStabilityMonitor::SeneschalStarted, weak_ptr_factory_.GetWeakPtr())); - auto* chunneld_client = - chromeos::DBusThreadManager::Get()->GetChunneldClient(); + auto* chunneld_client = chromeos::ChunneldClient::Get(); DCHECK(chunneld_client); chunneld_client->WaitForServiceToBeAvailable( base::BindOnce(&GuestOsStabilityMonitor::ChunneldStarted, @@ -70,8 +69,7 @@ void GuestOsStabilityMonitor::ChunneldStarted(bool is_available) { DCHECK(is_available); - auto* chunneld_client = - chromeos::DBusThreadManager::Get()->GetChunneldClient(); + auto* chunneld_client = chromeos::ChunneldClient::Get(); DCHECK(chunneld_client); chunneld_observer_.Observe(chunneld_client); }
diff --git a/chrome/browser/ash/guest_os/guest_os_stability_monitor_unittest.cc b/chrome/browser/ash/guest_os/guest_os_stability_monitor_unittest.cc index bd57da7..6b64246e 100644 --- a/chrome/browser/ash/guest_os/guest_os_stability_monitor_unittest.cc +++ b/chrome/browser/ash/guest_os/guest_os_stability_monitor_unittest.cc
@@ -20,6 +20,7 @@ #include "chromeos/ash/components/dbus/concierge/fake_concierge_client.h" #include "chromeos/ash/components/dbus/seneschal/fake_seneschal_client.h" #include "chromeos/ash/components/dbus/seneschal/seneschal_client.h" +#include "chromeos/dbus/chunneld/chunneld_client.h" #include "chromeos/dbus/chunneld/fake_chunneld_client.h" #include "chromeos/dbus/dbus_thread_manager.h" #include "content/public/test/browser_task_environment.h" @@ -31,6 +32,7 @@ public: GuestOsStabilityMonitorTest() : task_env_() { chromeos::DBusThreadManager::Initialize(); + chromeos::ChunneldClient::InitializeFake(); ash::CiceroneClient::InitializeFake(); ash::ConciergeClient::InitializeFake(); ash::SeneschalClient::InitializeFake(); @@ -58,6 +60,7 @@ ash::SeneschalClient::Shutdown(); ash::ConciergeClient::Shutdown(); ash::CiceroneClient::Shutdown(); + chromeos::ChunneldClient::Shutdown(); chromeos::DBusThreadManager::Shutdown(); } @@ -127,7 +130,7 @@ TEST_F(GuestOsStabilityMonitorTest, ChunneldFailure) { auto* chunneld_client = static_cast<chromeos::FakeChunneldClient*>( - chromeos::DBusThreadManager::Get()->GetChunneldClient()); + chromeos::ChunneldClient::Get()); chunneld_client->NotifyChunneldStopped(); histogram_tester_.ExpectUniqueSample(crostini::kCrostiniStabilityHistogram,
diff --git a/chrome/browser/ash/plugin_vm/plugin_vm_files_unittest.cc b/chrome/browser/ash/plugin_vm/plugin_vm_files_unittest.cc index 9a4a8f0..8115155 100644 --- a/chrome/browser/ash/plugin_vm/plugin_vm_files_unittest.cc +++ b/chrome/browser/ash/plugin_vm/plugin_vm_files_unittest.cc
@@ -26,6 +26,7 @@ #include "chromeos/ash/components/dbus/cicerone/fake_cicerone_client.h" #include "chromeos/ash/components/dbus/concierge/concierge_client.h" #include "chromeos/ash/components/dbus/seneschal/seneschal_client.h" +#include "chromeos/dbus/chunneld/chunneld_client.h" #include "chromeos/dbus/dbus_thread_manager.h" #include "chromeos/dbus/vm_applications/apps.pb.h" #include "chromeos/dbus/vm_plugin_dispatcher/vm_plugin_dispatcher_client.h" @@ -102,6 +103,7 @@ ash::CiceroneClient::InitializeFake(); ash::ConciergeClient::InitializeFake(); ash::SeneschalClient::InitializeFake(); + chromeos::ChunneldClient::InitializeFake(); chromeos::VmPluginDispatcherClient::InitializeFake(); } ~ScopedDBusThreadManager() { @@ -109,6 +111,7 @@ ash::SeneschalClient::Shutdown(); ash::ConciergeClient::Shutdown(); ash::CiceroneClient::Shutdown(); + chromeos::ChunneldClient::Shutdown(); chromeos::DBusThreadManager::Shutdown(); } } dbus_thread_manager_;
diff --git a/chrome/browser/ash/plugin_vm/plugin_vm_manager_impl_unittest.cc b/chrome/browser/ash/plugin_vm/plugin_vm_manager_impl_unittest.cc index 35bdc889..eb55068d 100644 --- a/chrome/browser/ash/plugin_vm/plugin_vm_manager_impl_unittest.cc +++ b/chrome/browser/ash/plugin_vm/plugin_vm_manager_impl_unittest.cc
@@ -28,6 +28,7 @@ #include "chromeos/ash/components/dbus/concierge/fake_concierge_client.h" #include "chromeos/ash/components/dbus/seneschal/fake_seneschal_client.h" #include "chromeos/ash/components/dbus/seneschal/seneschal_client.h" +#include "chromeos/dbus/chunneld/chunneld_client.h" #include "chromeos/dbus/dbus_thread_manager.h" #include "chromeos/dbus/dlcservice/fake_dlcservice_client.h" #include "chromeos/dbus/vm_plugin_dispatcher/fake_vm_plugin_dispatcher_client.h" @@ -50,6 +51,7 @@ public: PluginVmManagerImplTest() { chromeos::DBusThreadManager::Initialize(); + chromeos::ChunneldClient::InitializeFake(); ash::CiceroneClient::InitializeFake(); ash::ConciergeClient::InitializeFake(); ash::SeneschalClient::InitializeFake(); @@ -95,6 +97,7 @@ ash::SeneschalClient::Shutdown(); ash::ConciergeClient::Shutdown(); ash::CiceroneClient::Shutdown(); + chromeos::ChunneldClient::Shutdown(); chromeos::DBusThreadManager::Shutdown(); }
diff --git a/chrome/browser/ash/policy/active_directory/active_directory_policy_manager.cc b/chrome/browser/ash/policy/active_directory/active_directory_policy_manager.cc index 9cdb0de9..8ac5a5e 100644 --- a/chrome/browser/ash/policy/active_directory/active_directory_policy_manager.cc +++ b/chrome/browser/ash/policy/active_directory/active_directory_policy_manager.cc
@@ -29,8 +29,7 @@ namespace { // List of policies where variables like ${MACHINE_NAME} should be expanded. -constexpr const char* kPoliciesToExpand[] = {key::kNativePrinters, - key::kPrinters}; +constexpr const char* kPoliciesToExpand[] = {key::kPrinters}; // Fetch policy every 90 minutes which matches the Windows default: // https://technet.microsoft.com/en-us/library/cc940895.aspx
diff --git a/chrome/browser/ash/policy/dlp/dlp_files_controller_unittest.cc b/chrome/browser/ash/policy/dlp/dlp_files_controller_unittest.cc index 50e1394..aeca94b 100644 --- a/chrome/browser/ash/policy/dlp/dlp_files_controller_unittest.cc +++ b/chrome/browser/ash/policy/dlp/dlp_files_controller_unittest.cc
@@ -32,6 +32,7 @@ #include "chromeos/ash/components/dbus/cicerone/cicerone_client.h" #include "chromeos/ash/components/dbus/concierge/concierge_client.h" #include "chromeos/ash/components/dbus/seneschal/seneschal_client.h" +#include "chromeos/dbus/chunneld/chunneld_client.h" #include "chromeos/dbus/dbus_thread_manager.h" #include "chromeos/dbus/dlp/dlp_client.h" #include "chromeos/dbus/dlp/dlp_service.pb.h" @@ -405,6 +406,7 @@ crostini_features.set_enabled(true); chromeos::DBusThreadManager::Initialize(); + chromeos::ChunneldClient::InitializeFake(); ash::CiceroneClient::InitializeFake(); ash::ConciergeClient::InitializeFake(); ash::SeneschalClient::InitializeFake(); @@ -440,6 +442,7 @@ DlpFilesControllerTest::TearDown(); chromeos::DBusThreadManager::Shutdown(); + chromeos::ChunneldClient::Shutdown(); ash::CiceroneClient::Shutdown(); ash::ConciergeClient::Shutdown(); ash::SeneschalClient::Shutdown();
diff --git a/chrome/browser/ash/policy/handlers/lock_to_single_user_manager_unittest.cc b/chrome/browser/ash/policy/handlers/lock_to_single_user_manager_unittest.cc index 66c563c..a541192 100644 --- a/chrome/browser/ash/policy/handlers/lock_to_single_user_manager_unittest.cc +++ b/chrome/browser/ash/policy/handlers/lock_to_single_user_manager_unittest.cc
@@ -28,6 +28,7 @@ #include "chromeos/ash/components/dbus/concierge/fake_concierge_client.h" #include "chromeos/ash/components/dbus/seneschal/seneschal_client.h" #include "chromeos/ash/components/dbus/userdataauth/fake_cryptohome_misc_client.h" +#include "chromeos/dbus/chunneld/chunneld_client.h" #include "chromeos/dbus/dbus_thread_manager.h" #include "chromeos/dbus/vm_plugin_dispatcher/vm_plugin_dispatcher_client.h" #include "components/account_id/account_id.h" @@ -51,7 +52,7 @@ // This is required before Concierge tests start calling // DBusThreadManager::Get() for GuestOsStabilityMonitor. chromeos::DBusThreadManager::Initialize(); - + chromeos::ChunneldClient::InitializeFake(); ash::CiceroneClient::InitializeFake(); ash::ConciergeClient::InitializeFake(); ash::SeneschalClient::InitializeFake(); @@ -109,6 +110,7 @@ ash::SeneschalClient::Shutdown(); ash::ConciergeClient::Shutdown(); ash::CiceroneClient::Shutdown(); + chromeos::ChunneldClient::Shutdown(); chromeos::DBusThreadManager::Shutdown(); }
diff --git a/chrome/browser/ash/smb_client/smb_service.cc b/chrome/browser/ash/smb_client/smb_service.cc index 2920d361..97d6776 100644 --- a/chrome/browser/ash/smb_client/smb_service.cc +++ b/chrome/browser/ash/smb_client/smb_service.cc
@@ -426,13 +426,7 @@ } SmbProviderClient* SmbService::GetSmbProviderClient() const { - // If the DBusThreadManager or the SmbProviderClient aren't available, - // there isn't much we can do. This should only happen when running tests. - if (!chromeos::DBusThreadManager::IsInitialized() || - !chromeos::DBusThreadManager::Get()) { - return nullptr; - } - return chromeos::DBusThreadManager::Get()->GetSmbProviderClient(); + return chromeos::SmbProviderClient::Get(); } void SmbService::RestoreMounts() {
diff --git a/chrome/browser/ash/smb_client/smb_service_unittest.cc b/chrome/browser/ash/smb_client/smb_service_unittest.cc index 90ac0f4c4..56eaee2 100644 --- a/chrome/browser/ash/smb_client/smb_service_unittest.cc +++ b/chrome/browser/ash/smb_client/smb_service_unittest.cc
@@ -44,8 +44,8 @@ #include "chrome/test/base/testing_profile.h" #include "chrome/test/base/testing_profile_manager.h" #include "chromeos/ash/components/dbus/concierge/concierge_client.h" -#include "chromeos/dbus/dbus_thread_manager.h" #include "chromeos/dbus/smbprovider/fake_smb_provider_client.h" +#include "chromeos/dbus/smbprovider/smb_provider_client.h" #include "components/user_manager/scoped_user_manager.h" #include "content/public/test/browser_task_environment.h" #include "storage/browser/file_system/external_mount_points.h" @@ -171,10 +171,7 @@ user_manager_enabler_ = std::make_unique<user_manager::ScopedUserManager>( std::move(user_manager_temp)); - // This isn't used, but still needs to exist. - chromeos::DBusThreadManager::Initialize(); - chromeos::DBusThreadManager::GetSetterForTesting()->SetSmbProviderClient( - std::make_unique<FakeSmbProviderClient>()); + SmbProviderClient::InitializeFake(); ConciergeClient::InitializeFake(/*fake_cicerone_client=*/nullptr); // Takes ownership of |disk_mount_manager_|, but Shutdown() must be called. @@ -187,7 +184,7 @@ profile_manager_.reset(); disks::DiskMountManager::Shutdown(); ConciergeClient::Shutdown(); - chromeos::DBusThreadManager::Shutdown(); + SmbProviderClient::Shutdown(); } void CreateService(TestingProfile* profile) {
diff --git a/chrome/browser/ash/smb_client/smb_share_finder_unittest.cc b/chrome/browser/ash/smb_client/smb_share_finder_unittest.cc index 0f9c5766..63580e1 100644 --- a/chrome/browser/ash/smb_client/smb_share_finder_unittest.cc +++ b/chrome/browser/ash/smb_client/smb_share_finder_unittest.cc
@@ -133,6 +133,8 @@ std::make_unique<InMemoryHostLocator>(should_run_synchronously); host_locator_ = host_locator.get(); + // If re-initializing the client, ensure the old client is destroyed first. + fake_client_.reset(); fake_client_ = std::make_unique<FakeSmbProviderClient>(should_run_synchronously); share_finder_ = std::make_unique<SmbShareFinder>(fake_client_.get());
diff --git a/chrome/browser/ash/usb/cros_usb_detector_unittest.cc b/chrome/browser/ash/usb/cros_usb_detector_unittest.cc index 1c9ccb4a..aa80397 100644 --- a/chrome/browser/ash/usb/cros_usb_detector_unittest.cc +++ b/chrome/browser/ash/usb/cros_usb_detector_unittest.cc
@@ -39,6 +39,7 @@ #include "chromeos/ash/components/dbus/concierge/concierge_client.h" #include "chromeos/ash/components/dbus/concierge/fake_concierge_client.h" #include "chromeos/ash/components/dbus/seneschal/seneschal_client.h" +#include "chromeos/dbus/chunneld/chunneld_client.h" #include "chromeos/dbus/cros_disks/cros_disks_client.h" #include "chromeos/dbus/dbus_thread_manager.h" #include "chromeos/dbus/vm_plugin_dispatcher/fake_vm_plugin_dispatcher_client.h" @@ -136,6 +137,7 @@ CrosUsbDetectorTest() { // Needed for ChunneldClient via GuestOsStabilityMonitor. chromeos::DBusThreadManager::Initialize(); + chromeos::ChunneldClient::InitializeFake(); ash::CiceroneClient::InitializeFake(); ConciergeClient::InitializeFake(); SeneschalClient::InitializeFake(); @@ -160,6 +162,7 @@ SeneschalClient::Shutdown(); ConciergeClient::Shutdown(); ash::CiceroneClient::Shutdown(); + chromeos::ChunneldClient::Shutdown(); chromeos::DBusThreadManager::Shutdown(); }
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 6169010..98ab0c51 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
@@ -4,6 +4,7 @@ #include "chrome/browser/autofill_assistant/password_change/apc_external_action_delegate.h" +#include <algorithm> #include <string> #include <vector> @@ -16,6 +17,8 @@ #include "chrome/browser/ui/autofill_assistant/password_change/password_change_run_controller.h" #include "chrome/browser/ui/autofill_assistant/password_change/password_change_run_display.h" #include "chrome/grit/generated_resources.h" +#include "components/autofill_assistant/browser/public/external_action.pb.h" +#include "components/autofill_assistant/browser/public/external_action_delegate.h" #include "ui/base/l10n/l10n_util.h" #include "url/gurl.h" @@ -157,7 +160,7 @@ return; } - DCHECK(choice_index < base_prompt_return_values_.size()); + CHECK(choice_index < base_prompt_return_values_.size()); autofill_assistant::password_change::BasePromptSpecification::Result result; result.set_selected_tag(base_prompt_return_values_[choice_index]); @@ -247,10 +250,16 @@ specification) { base_prompt_should_send_payload_ = specification.has_output_key(); - // TODO(crbug.com/1331202): Set up DOM checking and matching of DOM conditions - // to return values. - + // TODO(crbug.com/1331202): If this causes flickering, separate prompt + // argument extraction and showing the base prompt. ShowBasePrompt(specification); + + // `this` outlives the script controller, therefore we can pass an unretained + // pointer. + std::move(start_dom_checks_callback_) + .Run(base::BindRepeating( + &ApcExternalActionDelegate::OnBasePromptDomUpdateReceived, + base::Unretained(this))); } void ApcExternalActionDelegate::HandleGeneratedPasswordPrompt( @@ -267,6 +276,31 @@ EndAction(false); } +void ApcExternalActionDelegate::OnBasePromptDomUpdateReceived( + const autofill_assistant::external::ElementConditionsUpdate& update) { + // To ensure predictable behavior, we always choose the condition with the + // smallest index if there are multiple fulfilled conditions. + size_t minimum_satisfied_index = base_prompt_return_values_.size(); + + for (const auto& condition : update.results()) { + if (condition.satisfied()) { + // Ids must be within the range of the return values vector. + if (condition.id() < 0 || static_cast<size_t>(condition.id()) >= + base_prompt_return_values_.size()) { + DLOG(ERROR) << "dom condition id is out of bounds"; + EndAction(false); + return; + } + minimum_satisfied_index = std::min(minimum_satisfied_index, + static_cast<size_t>(condition.id())); + } + } + + if (minimum_satisfied_index < base_prompt_return_values_.size()) { + OnBasePromptChoiceSelected(minimum_satisfied_index); + } +} + 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 e181ec1..50d2a12 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
@@ -10,6 +10,7 @@ #include "base/memory/raw_ptr.h" #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.pb.h" #include "components/autofill_assistant/browser/public/external_action_delegate.h" #include "third_party/abseil-cpp/absl/types/optional.h" @@ -88,6 +89,9 @@ const autofill_assistant::password_change::UpdateSidePanelSpecification& specification); + void OnBasePromptDomUpdateReceived( + const autofill_assistant::external::ElementConditionsUpdate& update); + // The callback that terminates the current action. base::OnceCallback<void(const autofill_assistant::external::Result& result)> end_action_callback_;
diff --git a/chrome/browser/autofill_assistant/password_change/apc_external_action_delegate_unittest.cc b/chrome/browser/autofill_assistant/password_change/apc_external_action_delegate_unittest.cc index 4c02237..216e605 100644 --- a/chrome/browser/autofill_assistant/password_change/apc_external_action_delegate_unittest.cc +++ b/chrome/browser/autofill_assistant/password_change/apc_external_action_delegate_unittest.cc
@@ -5,6 +5,8 @@ #include "chrome/browser/autofill_assistant/password_change/apc_external_action_delegate.h" #include <memory> +#include <utility> +#include <vector> #include "base/strings/string_piece.h" #include "base/strings/utf_string_conversions.h" @@ -14,6 +16,7 @@ #include "chrome/browser/ui/autofill_assistant/password_change/mock_password_change_run_display.h" #include "chrome/browser/ui/autofill_assistant/password_change/password_change_run_display.h" #include "chrome/grit/generated_resources.h" +#include "components/autofill_assistant/browser/public/external_action.pb.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/base/l10n/l10n_util.h" @@ -47,6 +50,17 @@ constexpr char kUrl[] = "https://wwww.example.com"; +autofill_assistant::external::ElementConditionsUpdate CreateDomUpdate( + const std::vector<std::pair<int, bool>>& updates) { + autofill_assistant::external::ElementConditionsUpdate proto; + for (const auto& [id, satisfied] : updates) { + auto* result = proto.add_results(); + result->set_id(id); + result->set_satisfied(satisfied); + } + return proto; +} + // Helper function to create a sample proto for a base prompt. autofill_assistant::password_change::BasePromptSpecification CreateBasePrompt() { @@ -213,8 +227,7 @@ EXPECT_FALSE(result.has_result_info()); } -TEST_F(ApcExternalActionDelegateTest, - ReceiveBasePromptAction_WithoutDomConditions) { +TEST_F(ApcExternalActionDelegateTest, ReceiveBasePromptAction_FromViewClick) { base::MockOnceCallback<void( const autofill_assistant::external::Result& result)> result_callback; @@ -228,8 +241,8 @@ autofill_assistant::external::Result result; EXPECT_CALL(result_callback, Run).WillOnce(SaveArg<0>(&result)); - // DOM checks will never be started. - EXPECT_CALL(start_dom_checks_callback, Run).Times(0); + // DOM checks are always started. + EXPECT_CALL(start_dom_checks_callback, Run); autofill_assistant::password_change::BasePromptSpecification proto = CreateBasePrompt(); @@ -266,7 +279,91 @@ } TEST_F(ApcExternalActionDelegateTest, - ReceiveBasePromptAction_WithoutDomConditionsAndWithoutResultKey) { + ReceiveBasePromptAction_FromDomCondition) { + base::MockOnceCallback<void( + const autofill_assistant::external::Result& result)> + result_callback; + base::MockOnceCallback<void(DomUpdateCallback)> start_dom_checks_callback; + + std::vector<PasswordChangeRunDisplay::PromptChoice> choices; + EXPECT_CALL(*display(), ShowBasePrompt); + + // Save the prompt result. + autofill_assistant::external::Result result; + EXPECT_CALL(result_callback, Run).WillOnce(SaveArg<0>(&result)); + + // DOM checks are started. + DomUpdateCallback dom_update_callback; + EXPECT_CALL(start_dom_checks_callback, Run) + .WillOnce(SaveArg<0>(&dom_update_callback)); + + autofill_assistant::password_change::BasePromptSpecification proto = + CreateBasePrompt(); + action_delegate()->OnActionRequested(CreateAction(proto), + start_dom_checks_callback.Get(), + result_callback.Get()); + + // But no result is sent yet. + EXPECT_FALSE(result.has_success()); + + // After receiving a valid DOM condition ... + EXPECT_CALL(*display(), ClearPrompt); + dom_update_callback.Run(CreateDomUpdate({{1, true}, {0, true}})); + + // ... there is now a result. + EXPECT_TRUE(result.has_success()); + EXPECT_TRUE(result.success()); + EXPECT_TRUE(result.has_result_info() && + result.result_info().has_result_payload()); + autofill_assistant::password_change::BasePromptSpecification::Result + prompt_result; + ASSERT_TRUE( + prompt_result.ParseFromString(result.result_info().result_payload())); + + EXPECT_TRUE(prompt_result.has_selected_tag()); + // The result with index 0 is selected even though the arguments of the + // DomUpdateCallback were not ordered. + EXPECT_EQ(prompt_result.selected_tag(), kPromptTag1); +} + +TEST_F(ApcExternalActionDelegateTest, + ReceiveBasePromptAction_FailOnInvalidDomCondition) { + base::MockOnceCallback<void( + const autofill_assistant::external::Result& result)> + result_callback; + base::MockOnceCallback<void(DomUpdateCallback)> start_dom_checks_callback; + + std::vector<PasswordChangeRunDisplay::PromptChoice> choices; + EXPECT_CALL(*display(), ShowBasePrompt); + + // Save the prompt result. + autofill_assistant::external::Result result; + EXPECT_CALL(result_callback, Run).WillOnce(SaveArg<0>(&result)); + + // DOM checks are started. + DomUpdateCallback dom_update_callback; + EXPECT_CALL(start_dom_checks_callback, Run) + .WillOnce(SaveArg<0>(&dom_update_callback)); + + autofill_assistant::password_change::BasePromptSpecification proto = + CreateBasePrompt(); + action_delegate()->OnActionRequested(CreateAction(proto), + start_dom_checks_callback.Get(), + result_callback.Get()); + + // But no result is sent yet. + EXPECT_FALSE(result.has_success()); + + // After receiving an invalid DOM condition ... + dom_update_callback.Run(CreateDomUpdate({{-1, true}, {0, true}})); + + // ... the action fails. + EXPECT_TRUE(result.has_success()); + EXPECT_FALSE(result.success()); +} + +TEST_F(ApcExternalActionDelegateTest, + ReceiveBasePromptAction_FromViewClickWithoutResultKey) { // TODO: Check that we do not send a result if the result key field is not // set. base::MockOnceCallback<void( @@ -282,8 +379,8 @@ autofill_assistant::external::Result result; EXPECT_CALL(result_callback, Run).WillOnce(SaveArg<0>(&result)); - // DOM checks will never be started. - EXPECT_CALL(start_dom_checks_callback, Run).Times(0); + // DOM checks are started. + EXPECT_CALL(start_dom_checks_callback, Run); autofill_assistant::password_change::BasePromptSpecification proto = CreateBasePrompt();
diff --git a/chrome/browser/browser_commands_unittest.cc b/chrome/browser/browser_commands_unittest.cc index 2e560d7..707bb909 100644 --- a/chrome/browser/browser_commands_unittest.cc +++ b/chrome/browser/browser_commands_unittest.cc
@@ -219,7 +219,8 @@ // Select the second tab and make it go forward in a new background tab. browser()->tab_strip_model()->ActivateTabAt( - 1, {TabStripModel::GestureType::kOther}); + 1, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); // TODO(crbug.com/11055): It should not be necessary to commit the load here, // but because of this bug, it will assert later if we don't. When the bug is // fixed, one of the three commits here related to this bug should be removed @@ -246,7 +247,8 @@ // Now do back in a new foreground tab. Don't bother re-checking every sngle // thing above, just validate that it's opening properly. browser()->tab_strip_model()->ActivateTabAt( - 2, {TabStripModel::GestureType::kOther}); + 2, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); // TODO(crbug.com/11055): see the comment above about why we need this. CommitPendingLoad(&second->GetController()); chrome::GoBack(browser(), WindowOpenDisposition::NEW_FOREGROUND_TAB); @@ -291,7 +293,8 @@ // Select the second tab and make it go forward in a new background tab. browser()->tab_strip_model()->ActivateTabAt( - 1, {TabStripModel::GestureType::kOther}); + 1, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); // TODO(crbug.com/11055): see the comment above about why we need this. CommitPendingLoad( &browser()->tab_strip_model()->GetActiveWebContents()->GetController()); @@ -404,7 +407,9 @@ // Add Second tab. WebContents* second_tab = tab_strip_model->GetWebContentsAt(1); - tab_strip_model->ActivateTabAt(1, {TabStripModel::GestureType::kOther}); + tab_strip_model->ActivateTabAt( + 1, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); EXPECT_TRUE(tab_strip_model->IsTabSelected(1)); zoom::PageZoom::Zoom(second_tab, content::PAGE_ZOOM_OUT);
diff --git a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.cc b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.cc index 9b7af74..9804d47 100644 --- a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.cc +++ b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.cc
@@ -125,7 +125,6 @@ #include "content/public/browser/browser_thread.h" #include "content/public/browser/browsing_data_filter_builder.h" #include "content/public/browser/host_zoom_map.h" -#include "content/public/browser/plugin_data_remover.h" #include "content/public/browser/prefetch_service_delegate.h" #include "content/public/browser/ssl_host_state_delegate.h" #include "content/public/browser/storage_partition.h"
diff --git a/chrome/browser/captive_portal/captive_portal_browsertest.cc b/chrome/browser/captive_portal/captive_portal_browsertest.cc index 4c54dd8d..0a7c205 100644 --- a/chrome/browser/captive_portal/captive_portal_browsertest.cc +++ b/chrome/browser/captive_portal/captive_portal_browsertest.cc
@@ -1803,7 +1803,9 @@ // it must happen before the captive_portal::CaptivePortalService sends out // its test request, so waiting for PortalObserver to see that request // prevents it from confusing the MultiNavigationObservers used later. - tab_strip_model->ActivateTabAt(0, {TabStripModel::GestureType::kOther}); + tab_strip_model->ActivateTabAt( + 0, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); browser->OpenURL(content::OpenURLParams(timeout_url, content::Referrer(), WindowOpenDisposition::CURRENT_TAB, ui::PAGE_TRANSITION_TYPED, false)); @@ -1820,7 +1822,9 @@ WaitForJobs(1); // Simulate logging in. - tab_strip_model->ActivateTabAt(1, {TabStripModel::GestureType::kOther}); + tab_strip_model->ActivateTabAt( + 1, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); SetSlowSSLLoadTime(tab_reloader, base::Days(1)); Login(browser, 1 /* num_loading_tabs */, 0 /* num_timed_out_tabs */, 1 /* expected_portal_checks */); @@ -2423,12 +2427,16 @@ // Activate the timed out tab and navigate it to a timeout again. TabStripModel* tab_strip_model = browser()->tab_strip_model(); - tab_strip_model->ActivateTabAt(0, {TabStripModel::GestureType::kOther}); + tab_strip_model->ActivateTabAt( + 0, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); FastTimeoutBehindCaptivePortal(browser(), false); // Activate and navigate the captive portal tab. This should not trigger a // reload of the tab with the error. - tab_strip_model->ActivateTabAt(1, {TabStripModel::GestureType::kOther}); + tab_strip_model->ActivateTabAt( + 1, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); NavigateLoginTab(browser(), 0, 1); // Simulate logging in. @@ -2488,7 +2496,9 @@ SlowLoadBehindCaptivePortal(browser(), false); - tab_strip_model->ActivateTabAt(1, {TabStripModel::GestureType::kOther}); + tab_strip_model->ActivateTabAt( + 1, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); Login(browser(), 2 /* num_loading_tabs */, 0 /* num_timed_out_tabs */, 1 /* expected_portal_checks */); FailLoadsAfterLogin(browser(), 2); @@ -2506,7 +2516,9 @@ // Switch back to the hung tab from the login tab, and abort the navigation. TabStripModel* tab_strip_model = browser()->tab_strip_model(); - tab_strip_model->ActivateTabAt(0, {TabStripModel::GestureType::kOther}); + tab_strip_model->ActivateTabAt( + 0, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); chrome::Stop(browser()); navigation_observer.WaitForNavigations(1); @@ -2516,7 +2528,9 @@ EXPECT_EQ(captive_portal::CaptivePortalTabReloader::STATE_NONE, GetStateOfTabReloaderAt(browser(), 0)); - tab_strip_model->ActivateTabAt(1, {TabStripModel::GestureType::kOther}); + tab_strip_model->ActivateTabAt( + 1, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); Login(browser(), 0 /* num_loading_tabs */, 0 /* num_timed_out_tabs */, 1 /* expected_portal_checks */); } @@ -2531,14 +2545,18 @@ // Navigate the error tab to a non-error page. TabStripModel* tab_strip_model = browser()->tab_strip_model(); - tab_strip_model->ActivateTabAt(0, {TabStripModel::GestureType::kOther}); + tab_strip_model->ActivateTabAt( + 0, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); ASSERT_TRUE(ui_test_utils::NavigateToURL( browser(), embedded_test_server()->GetURL("/title2.html"))); EXPECT_EQ(captive_portal::CaptivePortalTabReloader::STATE_NONE, GetStateOfTabReloaderAt(browser(), 0)); // Simulate logging in. - tab_strip_model->ActivateTabAt(1, {TabStripModel::GestureType::kOther}); + tab_strip_model->ActivateTabAt( + 1, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); Login(browser(), 0 /* num_loading_tabs */, 0 /* num_timed_out_tabs */, 1 /* expected_portal_checks */); } @@ -2600,7 +2618,9 @@ // Activate the error page tab again and go back. TabStripModel* tab_strip_model = browser()->tab_strip_model(); - tab_strip_model->ActivateTabAt(0, {TabStripModel::GestureType::kOther}); + tab_strip_model->ActivateTabAt( + 0, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); chrome::GoBack(browser(), WindowOpenDisposition::CURRENT_TAB); navigation_observer.WaitForNavigations(1);
diff --git a/chrome/browser/chromeos/extensions/echo_private/echo_private_apitest.cc b/chrome/browser/chromeos/extensions/echo_private/echo_private_apitest.cc index 121e3194..7b13fdf 100644 --- a/chrome/browser/chromeos/extensions/echo_private/echo_private_apitest.cc +++ b/chrome/browser/chromeos/extensions/echo_private/echo_private_apitest.cc
@@ -104,7 +104,8 @@ EXPECT_TRUE( AddTabAtIndex(0, GURL("about:blank"), ui::PAGE_TRANSITION_LINK)); browser()->tab_strip_model()->ActivateTabAt( - 0, {TabStripModel::GestureType::kOther}); + 0, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); return extensions::ExtensionTabUtil::GetTabId( browser()->tab_strip_model()->GetActiveWebContents()); }
diff --git a/chrome/browser/devtools/devtools_window.cc b/chrome/browser/devtools/devtools_window.cc index 497eab43..0b23420 100644 --- a/chrome/browser/devtools/devtools_window.cc +++ b/chrome/browser/devtools/devtools_window.cc
@@ -36,6 +36,7 @@ #include "chrome/browser/ui/prefs/prefs_tab_helper.h" #include "chrome/browser/ui/scoped_tabbed_browser_displayer.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" +#include "chrome/browser/ui/tabs/tab_strip_user_gesture_details.h" #include "chrome/browser/ui/webui/devtools_ui.h" #include "chrome/common/chrome_switches.h" #include "chrome/common/pref_names.h" @@ -909,8 +910,10 @@ main_web_contents_->SetDelegate(this); TabStripModel* tab_strip_model = inspected_browser->tab_strip_model(); - tab_strip_model->ActivateTabAt(inspected_tab_index, - {TabStripModel::GestureType::kOther}); + tab_strip_model->ActivateTabAt( + inspected_tab_index, + TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); inspected_window->UpdateDevTools(); main_web_contents_->SetInitialFocus();
diff --git a/chrome/browser/download/download_browsertest.cc b/chrome/browser/download/download_browsertest.cc index d8e3e81..52b2760 100644 --- a/chrome/browser/download/download_browsertest.cc +++ b/chrome/browser/download/download_browsertest.cc
@@ -5585,7 +5585,8 @@ // Go to the first tab. browser()->tab_strip_model()->ActivateTabAt( - 0, {TabStripModel::GestureType::kOther}); + 0, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); EXPECT_EQ(2, browser()->tab_strip_model()->count()); // The shelf should now be closed.
diff --git a/chrome/browser/extensions/api/cryptotoken_private/cryptotoken_private_api.cc b/chrome/browser/extensions/api/cryptotoken_private/cryptotoken_private_api.cc index 68abba2..969f24f 100644 --- a/chrome/browser/extensions/api/cryptotoken_private/cryptotoken_private_api.cc +++ b/chrome/browser/extensions/api/cryptotoken_private/cryptotoken_private_api.cc
@@ -322,25 +322,20 @@ frame->GetLastCommittedURL(), response_headers, extension_misc::kCryptotokenDeprecationTrialName, base::Time::Now())); - const bool u2f_api_enterprise_policy_enabled = - Profile::FromBrowserContext(browser_context()) - ->GetPrefs() - ->GetBoolean(extensions::pref_names::kU2fSecurityKeyApiEnabled); DCHECK( base::FeatureList::IsEnabled(extensions_features::kU2FSecurityKeyAPI) || - u2f_api_enterprise_policy_enabled || u2f_api_origin_trial_enabled); + u2f_api_origin_trial_enabled); // Don't show a permission prompt if its feature flag is disabled, or if the // site enrolled in the deprecation trial (since they're obviously aware of - // the deprecation), or if the enterprise policy to override U2F - // deprecation-related changes has been enabled. + // the deprecation). // // Also don't show the prompt in "non-regular" ChromeOS profiles, which // includes CrOS SAML sign-in context that doesn't support permission prompts // (crbug.com/1257293). if (!base::FeatureList::IsEnabled(device::kU2fPermissionPrompt) || - u2f_api_enterprise_policy_enabled || u2f_api_origin_trial_enabled) { + u2f_api_origin_trial_enabled) { return RespondNow(OneArgument(base::Value(true))); }
diff --git a/chrome/browser/extensions/api/cryptotoken_private/cryptotoken_private_api_unittest.cc b/chrome/browser/extensions/api/cryptotoken_private/cryptotoken_private_api_unittest.cc index 6a6c5a37..96ced8a 100644 --- a/chrome/browser/extensions/api/cryptotoken_private/cryptotoken_private_api_unittest.cc +++ b/chrome/browser/extensions/api/cryptotoken_private/cryptotoken_private_api_unittest.cc
@@ -394,23 +394,4 @@ EXPECT_TRUE(result); } -TEST_F(CryptoTokenPermissionTest, EnterprisePolicyOverridesRequestPrompt) { - // Setting the deprecation override policy should cause the prompt to be - // suppressed. This should be true even when the API has been - // default-disabled, because the policy overrides that too. - for (bool api_enabled : {false, true}) { - SCOPED_TRACE(api_enabled); - base::test::ScopedFeatureList feature_list; - feature_list.InitWithFeatureState(extensions_features::kU2FSecurityKeyAPI, - api_enabled); - browser()->profile()->GetPrefs()->Set( - extensions::pref_names::kU2fSecurityKeyApiEnabled, base::Value(true)); - bool result = false; - ASSERT_TRUE(CanMakeU2fApiRequest( - "https://test.com", permissions::PermissionRequestManager::NONE, - &result)); - EXPECT_TRUE(result); - } -} - } // namespace extensions
diff --git a/chrome/browser/extensions/api/cryptotoken_private/cryptotoken_private_browsertest.cc b/chrome/browser/extensions/api/cryptotoken_private/cryptotoken_private_browsertest.cc index 148f747..003d8a4 100644 --- a/chrome/browser/extensions/api/cryptotoken_private/cryptotoken_private_browsertest.cc +++ b/chrome/browser/extensions/api/cryptotoken_private/cryptotoken_private_browsertest.cc
@@ -378,26 +378,6 @@ } } -IN_PROC_BROWSER_TEST_P(CryptotokenBrowserTest, ConnectWithEnterprisePolicy) { - // Connection succeeds regardless of feature flag state with the enterprise - // policy overriding deprecation changes. - browser()->profile()->GetPrefs()->Set( - extensions::pref_names::kU2fSecurityKeyApiEnabled, base::Value(true)); - ASSERT_TRUE(ui_test_utils::NavigateToURL( - browser(), https_server_.GetURL(kNonOriginTrialDomain, "/empty.html"))); - ExpectConnectSuccess(); -} - -IN_PROC_BROWSER_TEST_P(CryptotokenBrowserTest, - SignWithEnterprisePolicyDoesNotShowPrompt) { - browser()->profile()->GetPrefs()->Set( - extensions::pref_names::kU2fSecurityKeyApiEnabled, base::Value(true)); - GURL url = GURL(https_server_.GetURL(kNonOriginTrialDomain, "/empty.html")); - ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); - std::string app_id = url::Origin::Create(url).Serialize(); - ExpectSignSuccess(app_id, PromptExpectation::kNoPrompt); -} - IN_PROC_BROWSER_TEST_P(CryptotokenBrowserTest, InsecureOriginCannotConnect) { // Connections from insecure origins always fail. ASSERT_TRUE(ui_test_utils::NavigateToURL(
diff --git a/chrome/browser/extensions/api/extension_action/browser_action_apitest.cc b/chrome/browser/extensions/api/extension_action/browser_action_apitest.cc index 0b7ae5b3..3890138 100644 --- a/chrome/browser/extensions/api/extension_action/browser_action_apitest.cc +++ b/chrome/browser/extensions/api/extension_action/browser_action_apitest.cc
@@ -548,7 +548,8 @@ // Go back to first tab, changed title should reappear. browser()->tab_strip_model()->ActivateTabAt( - 0, {TabStripModel::GestureType::kOther}); + 0, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); EXPECT_EQ("Showing icon 2", GetBrowserActionsBar()->GetTooltip(extension->id()));
diff --git a/chrome/browser/extensions/api/extension_action/browser_action_interactive_test.cc b/chrome/browser/extensions/api/extension_action/browser_action_interactive_test.cc index 8e29c13e..3a43205 100644 --- a/chrome/browser/extensions/api/extension_action/browser_action_interactive_test.cc +++ b/chrome/browser/extensions/api/extension_action/browser_action_interactive_test.cc
@@ -432,7 +432,8 @@ ExtensionHostTestHelper host_helper(profile()); // Change active tabs, the extension popup should close. browser()->tab_strip_model()->ActivateTabAt( - 0, {TabStripModel::GestureType::kOther}); + 0, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); host_helper.WaitForHostDestroyed(); EXPECT_FALSE(ExtensionActionTestHelper::Create(browser())->HasPopup());
diff --git a/chrome/browser/extensions/api/extension_action/page_action_apitest.cc b/chrome/browser/extensions/api/extension_action/page_action_apitest.cc index 7c8ef6f..c223d12 100644 --- a/chrome/browser/extensions/api/extension_action/page_action_apitest.cc +++ b/chrome/browser/extensions/api/extension_action/page_action_apitest.cc
@@ -222,7 +222,8 @@ browser(), embedded_test_server()->GetURL("/simple.html"))); chrome::NewTab(browser()); browser()->tab_strip_model()->ActivateTabAt( - 0, {TabStripModel::GestureType::kOther}); + 0, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); // Give the extension time to show the page action on the tab. WaitForPageActionVisibilityChangeTo(1);
diff --git a/chrome/browser/extensions/api/tabs/tabs_api.cc b/chrome/browser/extensions/api/tabs/tabs_api.cc index 24b84b76..ff63a45 100644 --- a/chrome/browser/extensions/api/tabs/tabs_api.cc +++ b/chrome/browser/extensions/api/tabs/tabs_api.cc
@@ -61,6 +61,7 @@ #include "chrome/browser/ui/tabs/tab_group.h" #include "chrome/browser/ui/tabs/tab_group_model.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" +#include "chrome/browser/ui/tabs/tab_strip_user_gesture_details.h" #include "chrome/browser/ui/tabs/tab_utils.h" #include "chrome/browser/ui/window_sizer/window_sizer.h" #include "chrome/browser/web_applications/web_app_helpers.h" @@ -789,7 +790,10 @@ if (!contents && urls.empty() && window_type == Browser::TYPE_NORMAL) { chrome::NewTab(new_window); } - chrome::SelectNumberedTab(new_window, 0, {TabStripModel::GestureType::kNone}); + chrome::SelectNumberedTab( + new_window, 0, + TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kNone)); if (focused) { new_window->window()->Show();
diff --git a/chrome/browser/extensions/api/tabs/tabs_api_unittest.cc b/chrome/browser/extensions/api/tabs/tabs_api_unittest.cc index 7154cba7..c5c8395 100644 --- a/chrome/browser/extensions/api/tabs/tabs_api_unittest.cc +++ b/chrome/browser/extensions/api/tabs/tabs_api_unittest.cc
@@ -1117,8 +1117,9 @@ ASSERT_EQ(2, tab_strip_model->count()); // Activate first tab. - tab_strip_model->ActivateTabAt(tab1_index, - {TabStripModel::GestureType::kOther}); + tab_strip_model->ActivateTabAt( + tab1_index, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); // Go back without tab_id. But first tab should be navigated since it's // activated. @@ -1149,8 +1150,9 @@ controller.GetLastCommittedEntry()->GetTransitionType()); // Activate second tab. - tab_strip_model->ActivateTabAt(tab2_index, - {TabStripModel::GestureType::kOther}); + tab_strip_model->ActivateTabAt( + tab2_index, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); auto goback_function2 = base::MakeRefCounted<TabsGoBackFunction>(); goback_function2->set_extension(extension_with_tabs_permission.get());
diff --git a/chrome/browser/extensions/api/tabs/tabs_test.cc b/chrome/browser/extensions/api/tabs/tabs_test.cc index c2204c8..29556e2 100644 --- a/chrome/browser/extensions/api/tabs/tabs_test.cc +++ b/chrome/browser/extensions/api/tabs/tabs_test.cc
@@ -2032,11 +2032,13 @@ ASSERT_TRUE(navigation_manager.WaitForRequestStart()); browser()->tab_strip_model()->ActivateTabAt( - 0, {TabStripModel::GestureType::kOther}); + 0, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); EXPECT_EQ(first_web_contents, browser()->tab_strip_model()->GetActiveWebContents()); browser()->tab_strip_model()->ActivateTabAt( - 1, {TabStripModel::GestureType::kOther}); + 1, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); EXPECT_EQ(second_web_contents, browser()->tab_strip_model()->GetActiveWebContents());
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json index 7db7892..0370cc0 100644 --- a/chrome/browser/flag-metadata.json +++ b/chrome/browser/flag-metadata.json
@@ -3675,6 +3675,11 @@ "expiry_milestone": -1 }, { + "name": "ignore-sync-encryption-keys-long-missing", + "owners": ["victorvianna", "chrome-sync-dev@google.com"], + "expiry_milestone": 116 + }, + { "name": "improve-reader-mode-prompt", "owners": ["lazzzis@google.com", "aishwaryarj@google.com", "//chrome/android/java/src/org/chromium/chrome/browser/dom_distiller/OWNERS"], @@ -5414,7 +5419,7 @@ { "name": "scroll-unification", "owners": [ "skobes@chromium.org", "input-dev@chromium.org" ], - "expiry_milestone": 105 + "expiry_milestone": 110 }, { "name": "scrollable-tabstrip",
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc index e3ba6fc..fc82837 100644 --- a/chrome/browser/flag_descriptions.cc +++ b/chrome/browser/flag_descriptions.cc
@@ -1547,6 +1547,14 @@ "Overrides the built-in software rendering list and enables " "GPU-acceleration on unsupported system configurations."; +const char kIgnoreSyncEncryptionKeysLongMissingName[] = + "Ignore Chrome Sync encryption keys long missing"; +const char kIgnoreSyncEncryptionKeysLongMissingDescription[] = + "Drops pending encrypted updates if their key has been missing for a " + "(configurable) number of consecutive GetUpdates. Restarting the browser " + "resets the counter. The threshold is configurable via the " + "MinGuResponsesToIgnoreKey feature parameter."; + const char kImprovedDesksKeyboardShortcutsName[] = "Enable improved desks keyboard shortcuts"; const char kImprovedDesksKeyboardShortcutsDescription[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h index 042bdb9..952a2c07 100644 --- a/chrome/browser/flag_descriptions.h +++ b/chrome/browser/flag_descriptions.h
@@ -866,6 +866,9 @@ extern const char kIgnoreGpuBlocklistName[]; extern const char kIgnoreGpuBlocklistDescription[]; +extern const char kIgnoreSyncEncryptionKeysLongMissingName[]; +extern const char kIgnoreSyncEncryptionKeysLongMissingDescription[]; + extern const char kImprovedDesksKeyboardShortcutsName[]; extern const char kImprovedDesksKeyboardShortcutsDescription[];
diff --git a/chrome/browser/flags/android/chrome_feature_list.cc b/chrome/browser/flags/android/chrome_feature_list.cc index 2c36c678..8130c4f 100644 --- a/chrome/browser/flags/android/chrome_feature_list.cc +++ b/chrome/browser/flags/android/chrome_feature_list.cc
@@ -853,7 +853,7 @@ base::FEATURE_ENABLED_BY_DEFAULT}; const base::Feature kShowExtendedPreloadingSetting{ - "ShowExtendedPreloadingSetting", base::FEATURE_DISABLED_BY_DEFAULT}; + "ShowExtendedPreloadingSetting", base::FEATURE_ENABLED_BY_DEFAULT}; const base::Feature kStartSurfaceAndroid{"StartSurfaceAndroid", base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/chrome/browser/lacros/web_contents_can_go_back_observer_browsertest.cc b/chrome/browser/lacros/web_contents_can_go_back_observer_browsertest.cc index d98d51a..7653086 100644 --- a/chrome/browser/lacros/web_contents_can_go_back_observer_browsertest.cc +++ b/chrome/browser/lacros/web_contents_can_go_back_observer_browsertest.cc
@@ -165,7 +165,8 @@ // Switch to a different tab, and verify whether the `can go back` property // updates accordingly. browser()->tab_strip_model()->ActivateTabAt( - 0, {TabStripModel::GestureType::kOther}); + 0, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); EXPECT_EQ(0, browser()->tab_strip_model()->active_index()); EXPECT_TRUE(chrome::CanGoBack(browser()));
diff --git a/chrome/browser/lookalikes/lookalike_url_navigation_throttle.cc b/chrome/browser/lookalikes/lookalike_url_navigation_throttle.cc index b27e0625..dda9d41 100644 --- a/chrome/browser/lookalikes/lookalike_url_navigation_throttle.cc +++ b/chrome/browser/lookalikes/lookalike_url_navigation_throttle.cc
@@ -476,12 +476,6 @@ return false; } - // If the URL is in the component allowlist, don't show any warning. - if (reputation::IsUrlAllowlistedBySafetyTipsComponent( - proto, url.GetWithEmptyPath())) { - return false; - } - // GetDomainInfo() is expensive, so do possible early-abort checks first. base::TimeTicks get_domain_info_start = base::TimeTicks::Now(); const DomainInfo navigated_domain = GetDomainInfo(url); @@ -538,13 +532,24 @@ GURL::Replacements replace_host; replace_host.SetHostStr(suggested_domain); *suggested_url = url.ReplaceComponents(replace_host).GetWithEmptyPath(); - return true; + + // Only flag the URL if its not allowed to spoof the suggested URL. + if (!reputation::IsUrlAllowlistedBySafetyTipsComponent( + proto, url.GetWithEmptyPath(), *suggested_url)) { + return true; + } } if (ShouldBlockBySpoofCheckResult(navigated_domain)) { *match_type = LookalikeUrlMatchType::kFailedSpoofChecks; *suggested_url = GURL(); - return true; + + // Only flag the URL if its not allowed to spoof itself (which is how we + // indicate spoof-check-specific allowlisting). + if (!reputation::IsUrlAllowlistedBySafetyTipsComponent( + proto, url.GetWithEmptyPath(), url.GetWithEmptyPath())) { + return true; + } } return false;
diff --git a/chrome/browser/lookalikes/lookalike_url_navigation_throttle_browsertest.cc b/chrome/browser/lookalikes/lookalike_url_navigation_throttle_browsertest.cc index ed5b5c2..9fba0f4 100644 --- a/chrome/browser/lookalikes/lookalike_url_navigation_throttle_browsertest.cc +++ b/chrome/browser/lookalikes/lookalike_url_navigation_throttle_browsertest.cc
@@ -197,6 +197,30 @@ EXPECT_EQ(nullptr, GetCurrentInterstitial(web_contents)); } +// Add an allowlist with entries that aren't allowlisted for all domains. +void ConfigureAllowlistWithScopes() { + auto config_proto = reputation::GetOrCreateSafetyTipsConfig(); + config_proto->clear_allowed_pattern(); + config_proto->clear_canonical_pattern(); + config_proto->clear_cohort(); + + // Note: allowed_pattern must be sorted, so "Allowed*" comes before "May*". + + // may-spoof-anyone.com has no cohort. + auto* patternWildcard = config_proto->add_allowed_pattern(); + patternWildcard->set_pattern("may-spoof-anyone.com/"); + + // may-spoof-google.com is only allowed to spoof google.com. + config_proto->add_canonical_pattern()->set_pattern("google.com/"); + auto* pattern = config_proto->add_allowed_pattern(); + pattern->set_pattern("may-spoof-google.com/"); + auto* cohort = config_proto->add_cohort(); + cohort->add_canonical_index(0); // google.com + pattern->add_cohort_index(0); + + reputation::SetSafetyTipsRemoteConfigProto(std::move(config_proto)); +} + namespace test { #include "components/url_formatter/spoof_checks/top_domains/browsertest_domains-trie-inc.cc" } @@ -545,6 +569,36 @@ CheckUkm({kNavigatedUrl}, "TriggeredByInitialUrl", false); } +// Embedding a top domain would normally show an interstitial, but shouldn't +// here because it's narrowly allowlisted. +IN_PROC_BROWSER_TEST_P(LookalikeUrlNavigationThrottleBrowserTest, + TargetEmbedding_ScopedAllowlistMatch) { + ConfigureAllowlistWithScopes(); + const GURL kNavigatedUrl = GetURL("google.com.may-spoof-google.com"); + SetEngagementScore(browser(), kNavigatedUrl, kLowEngagement); + + TestInterstitialNotShown(browser(), kNavigatedUrl); + CheckNoUkm(); +} + +// Same as TargetEmbedding_ScopedAllowlistMatch, but the attacker-controlled +// domain is spoofing an unauthorized victim. This should show a warning. +IN_PROC_BROWSER_TEST_P(LookalikeUrlNavigationThrottleBrowserTest, + TargetEmbedding_ScopedAllowlistMatchWrongDomain) { + ConfigureAllowlistWithScopes(); + const GURL kNavigatedUrl = GetURL("blogspot.com.may-spoof-google.com"); + const GURL kExpectedSuggestedUrl = GetURLWithoutPath("blogspot.com"); + SetEngagementScore(browser(), kNavigatedUrl, kLowEngagement); + + base::HistogramTester histograms; + TestMetricsRecordedAndInterstitialShown( + browser(), histograms, kNavigatedUrl, kExpectedSuggestedUrl, + NavigationSuggestionEvent::kMatchTargetEmbedding); + CheckUkm({kNavigatedUrl}, "MatchType", + LookalikeUrlMatchType::kTargetEmbedding); + CheckUkm({kNavigatedUrl}, "TriggeredByInitialUrl", false); +} + // Same as TargetEmbedding_TopDomain_Match, but has a redirect where the first // and last URLs are both target embedding matches. Should only record // metrics for the first URL. Regression test for crbug.com/1136296.
diff --git a/chrome/browser/mac/bluetooth_utility.h b/chrome/browser/mac/bluetooth_utility.h index 8f4f559..f057dec 100644 --- a/chrome/browser/mac/bluetooth_utility.h +++ b/chrome/browser/mac/bluetooth_utility.h
@@ -18,7 +18,7 @@ // there is no further indication of whether Low Energy is supported. BLUETOOTH_AVAILABLE_LE_UNKNOWN = 4, BLUETOOTH_NOT_SUPPORTED = 5, - BLUETOOTH_AVAILABILITY_COUNT, + kMaxValue = BLUETOOTH_NOT_SUPPORTED }; // Returns the bluetooth availability of the system's hardware.
diff --git a/chrome/browser/media/webrtc/webrtc_getdisplaymedia_browsertest.cc b/chrome/browser/media/webrtc/webrtc_getdisplaymedia_browsertest.cc index 48e753b..9cebac0 100644 --- a/chrome/browser/media/webrtc/webrtc_getdisplaymedia_browsertest.cc +++ b/chrome/browser/media/webrtc/webrtc_getdisplaymedia_browsertest.cc
@@ -646,7 +646,8 @@ int target_index = browser()->tab_strip_model()->GetIndexOfWebContents(target_tab); browser()->tab_strip_model()->ActivateTabAt( - target_index, {TabStripModel::GestureType::kOther}); + target_index, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); ASSERT_EQ(target_tab, browser()->tab_strip_model()->GetActiveWebContents()); // We navigate to a FileURL so that the origin will change, which should @@ -688,7 +689,8 @@ int target_index = browser()->tab_strip_model()->GetIndexOfWebContents(target_tab); browser()->tab_strip_model()->ActivateTabAt( - target_index, {TabStripModel::GestureType::kOther}); + target_index, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); ASSERT_EQ(target_tab, browser()->tab_strip_model()->GetActiveWebContents()); // We navigate using the test server so that the origin doesn't change.
diff --git a/chrome/browser/metrics/bluetooth_available_utility.cc b/chrome/browser/metrics/bluetooth_available_utility.cc deleted file mode 100644 index 2163c1d..0000000 --- a/chrome/browser/metrics/bluetooth_available_utility.cc +++ /dev/null
@@ -1,96 +0,0 @@ -// Copyright 2019 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/metrics/bluetooth_available_utility.h" - -#include "base/bind.h" -#include "base/metrics/histogram_macros.h" -#include "build/build_config.h" -#include "chrome/browser/mac/bluetooth_utility.h" -#include "content/public/browser/browser_task_traits.h" -#include "content/public/browser/browser_thread.h" -#include "device/bluetooth/bluetooth_adapter.h" -#include "device/bluetooth/bluetooth_adapter_factory.h" - -#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) -#include "device/bluetooth/dbus/bluez_dbus_manager.h" -#include "device/bluetooth/floss/floss_dbus_manager.h" -#include "device/bluetooth/floss/floss_features.h" -#endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) - -namespace bluetooth_utility { - -// These values are persisted to logs. Entries should not be renumbered and -// numeric values should never be reused. -enum class BluetoothStackName { kBlueZ = 0, kFloss = 1, kMaxValue = kFloss }; - -void ReportAvailability(BluetoothAvailability availability) { - UMA_HISTOGRAM_ENUMERATION("Bluetooth.Availability.v2", availability, - BLUETOOTH_AVAILABILITY_COUNT); -} - -void ReportStackName(BluetoothStackName name) { - UMA_HISTOGRAM_ENUMERATION("Bluetooth.StackName", name); -} - -void OnGetAdapter(scoped_refptr<device::BluetoothAdapter> adapter) { - if (!adapter->IsPresent()) { - ReportAvailability(BLUETOOTH_NOT_AVAILABLE); - return; - } - - if (!device::BluetoothAdapterFactory::Get()->IsLowEnergySupported()) { - ReportAvailability(BLUETOOTH_AVAILABLE_WITHOUT_LE); - return; - } - - ReportAvailability(BLUETOOTH_AVAILABLE_WITH_LE); -} - -void ReportBluetoothAvailability() { - // This is only relevant for desktop platforms. -#if BUILDFLAG(IS_MAC) - // TODO(kenrb): This is separate from other platforms because we get a - // little bit of extra information from the Mac-specific code. It might not - // be worth having the extra code path, and we should consider whether to - // combine them (https://crbug.com/907279). - ReportAvailability(bluetooth_utility::GetBluetoothAvailability()); -#elif BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) - // GetAdapter must be called on the UI thread, because it creates a - // WeakPtr, which is checked from that thread on future calls. - if (!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)) { - content::GetUIThreadTaskRunner({base::TaskPriority::BEST_EFFORT}) - ->PostTask(FROM_HERE, base::BindOnce(&ReportBluetoothAvailability)); - return; - } - -#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) - bool is_initialized; - - if (base::FeatureList::IsEnabled(floss::features::kFlossEnabled)) { - ReportStackName(BluetoothStackName::kFloss); - is_initialized = floss::FlossDBusManager::IsInitialized(); - } else { - ReportStackName(BluetoothStackName::kBlueZ); - is_initialized = bluez::BluezDBusManager::IsInitialized(); - } - - // This is for tests that have not initialized bluez/floss or dbus thread - // manager. Outside of tests these are initialized earlier during browser - // startup. - if (!is_initialized) - return; -#endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) - - if (!device::BluetoothAdapterFactory::Get()->IsBluetoothSupported()) { - ReportAvailability(BLUETOOTH_NOT_SUPPORTED); - return; - } - - device::BluetoothAdapterFactory::Get()->GetAdapter( - base::BindOnce(&OnGetAdapter)); -#endif -} - -} // namespace bluetooth_utility
diff --git a/chrome/browser/metrics/bluetooth_available_utility.h b/chrome/browser/metrics/bluetooth_available_utility.h deleted file mode 100644 index dfa79b4..0000000 --- a/chrome/browser/metrics/bluetooth_available_utility.h +++ /dev/null
@@ -1,17 +0,0 @@ -// Copyright 2019 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_METRICS_BLUETOOTH_AVAILABLE_UTILITY_H_ -#define CHROME_BROWSER_METRICS_BLUETOOTH_AVAILABLE_UTILITY_H_ - -namespace bluetooth_utility { - -// Reports the bluetooth availability of the system's hardware. -// This currently only works on ChromeOS and Windows. For Bluetooth -// availability on OS X, see chrome/browser/mac/bluetooth_utility.h. -void ReportBluetoothAvailability(); - -} // namespace bluetooth_utility - -#endif // CHROME_BROWSER_METRICS_BLUETOOTH_AVAILABLE_UTILITY_H_
diff --git a/chrome/browser/metrics/bluetooth_metrics_provider.cc b/chrome/browser/metrics/bluetooth_metrics_provider.cc new file mode 100644 index 0000000..273f4a6 --- /dev/null +++ b/chrome/browser/metrics/bluetooth_metrics_provider.cc
@@ -0,0 +1,105 @@ +// 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/metrics/bluetooth_metrics_provider.h" + +#include "base/bind.h" +#include "base/metrics/histogram_functions.h" +#include "content/public/browser/browser_task_traits.h" +#include "content/public/browser/browser_thread.h" +#include "device/bluetooth/bluetooth_adapter_factory.h" + +#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) +#include "device/bluetooth/dbus/bluez_dbus_manager.h" +#include "device/bluetooth/floss/floss_dbus_manager.h" +#include "device/bluetooth/floss/floss_features.h" +#endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) + +namespace metrics { + +BluetoothMetricsProvider::BluetoothMetricsProvider() { + GetBluetoothAvailability(); +} + +BluetoothMetricsProvider::~BluetoothMetricsProvider() = default; + +void BluetoothMetricsProvider::ProvideCurrentSessionData( + metrics::ChromeUserMetricsExtension* uma_proto) { +#if BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC) || \ + BUILDFLAG(IS_WIN) + base::UmaHistogramEnumeration("Bluetooth.Availability.v2", + bluetooth_availability_); + +#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) + base::UmaHistogramEnumeration("Bluetooth.StackName", bluetooth_stack_name_); +#endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) +#endif // BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC) || + // BUILDFLAG(IS_WIN) +} + +void BluetoothMetricsProvider::OnGetAdapter( + scoped_refptr<device::BluetoothAdapter> adapter) { + if (!adapter->IsPresent()) { + bluetooth_availability_ = BluetoothAvailability::BLUETOOTH_NOT_AVAILABLE; + return; + } + + if (!device::BluetoothAdapterFactory::Get()->IsLowEnergySupported()) { + bluetooth_availability_ = + BluetoothAvailability::BLUETOOTH_AVAILABLE_WITHOUT_LE; + return; + } + + bluetooth_availability_ = BluetoothAvailability::BLUETOOTH_AVAILABLE_WITH_LE; +} + +void BluetoothMetricsProvider::GetBluetoothAvailability() { + // This is only relevant for desktop platforms. +#if BUILDFLAG(IS_MAC) + // TODO(kenrb): This is separate from other platforms because we get a + // little bit of extra information from the Mac-specific code. It might not + // be worth having the extra code path, and we should consider whether to + // combine them (https://crbug.com/907279). + bluetooth_availability_ = bluetooth_utility::GetBluetoothAvailability(); +#elif BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_WIN) + // GetAdapter must be called on the UI thread, because it creates a + // WeakPtr, which is checked from that thread on future calls. + if (!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)) { + content::GetUIThreadTaskRunner({base::TaskPriority::BEST_EFFORT}) + ->PostTask( + FROM_HERE, + base::BindOnce(&BluetoothMetricsProvider::GetBluetoothAvailability, + weak_ptr_factory_.GetWeakPtr())); + return; + } + +#if BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_LINUX) + bool is_initialized; + + if (base::FeatureList::IsEnabled(floss::features::kFlossEnabled)) { + bluetooth_stack_name_ = BluetoothStackName::kFloss; + is_initialized = floss::FlossDBusManager::IsInitialized(); + } else { + bluetooth_stack_name_ = BluetoothStackName::kBlueZ; + is_initialized = bluez::BluezDBusManager::IsInitialized(); + } + + // This is for tests that have not initialized bluez/floss or dbus thread + // manager. Outside of tests these are initialized earlier during browser + // startup. + if (!is_initialized) + return; +#endif // BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_LINUX) + + if (!device::BluetoothAdapterFactory::Get()->IsBluetoothSupported()) { + bluetooth_availability_ = BluetoothAvailability::BLUETOOTH_NOT_SUPPORTED; + return; + } + + device::BluetoothAdapterFactory::Get()->GetAdapter(base::BindOnce( + &BluetoothMetricsProvider::OnGetAdapter, weak_ptr_factory_.GetWeakPtr())); +#endif +} + +} // namespace metrics
diff --git a/chrome/browser/metrics/bluetooth_metrics_provider.h b/chrome/browser/metrics/bluetooth_metrics_provider.h new file mode 100644 index 0000000..9c07e4e --- /dev/null +++ b/chrome/browser/metrics/bluetooth_metrics_provider.h
@@ -0,0 +1,60 @@ +// 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_METRICS_BLUETOOTH_METRICS_PROVIDER_H_ +#define CHROME_BROWSER_METRICS_BLUETOOTH_METRICS_PROVIDER_H_ + +#include "base/memory/weak_ptr.h" +#include "build/build_config.h" +#include "chrome/browser/mac/bluetooth_utility.h" +#include "components/metrics/metrics_provider.h" +#include "device/bluetooth/bluetooth_adapter.h" + +using bluetooth_utility::BluetoothAvailability; + +namespace metrics { + +// These values are persisted to logs. Entries should not be renumbered and +// numeric values should never be reused. +enum class BluetoothStackName { + kBlueZ = 0, + kFloss = 1, + kUnknown = 2, + kMaxValue = kUnknown +}; + +// BluetoothMetricsProvider reports the Bluetooth usage and stack identifiers. +class BluetoothMetricsProvider : public metrics::MetricsProvider { + public: + BluetoothMetricsProvider(); + + BluetoothMetricsProvider(const BluetoothMetricsProvider&) = delete; + BluetoothMetricsProvider& operator=(const BluetoothMetricsProvider&) = delete; + + ~BluetoothMetricsProvider() override; + + // metrics::MetricsProvider: + void ProvideCurrentSessionData( + metrics::ChromeUserMetricsExtension* uma_proto) override; + + private: + void OnGetAdapter(scoped_refptr<device::BluetoothAdapter> adapter); + void GetBluetoothAvailability(); + +#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) + BluetoothStackName bluetooth_stack_name_ = BluetoothStackName::kUnknown; +#endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) + // bluetooth_availability_ is initialized to BLUETOOTH_AVAILABILITY_ERROR here + // as a precaution to the asynchronized fetch of Bluetooth adapter + // availability. This variable gets updated only once during the class + // construction time through GetBluetoothAvailability() and its callback + // OnGetAdapter(). + BluetoothAvailability bluetooth_availability_ = + BluetoothAvailability::BLUETOOTH_AVAILABILITY_ERROR; + base::WeakPtrFactory<BluetoothMetricsProvider> weak_ptr_factory_{this}; +}; + +} // namespace metrics + +#endif // CHROME_BROWSER_METRICS_BLUETOOTH_METRICS_PROVIDER_H_
diff --git a/chrome/browser/metrics/chrome_browser_main_extra_parts_metrics.cc b/chrome/browser/metrics/chrome_browser_main_extra_parts_metrics.cc index bed300c..087a88d 100644 --- a/chrome/browser/metrics/chrome_browser_main_extra_parts_metrics.cc +++ b/chrome/browser/metrics/chrome_browser_main_extra_parts_metrics.cc
@@ -30,7 +30,6 @@ #include "chrome/browser/chrome_browser_main.h" #include "chrome/browser/enterprise/browser_management/management_service_factory.h" #include "chrome/browser/google/google_brand.h" -#include "chrome/browser/metrics/bluetooth_available_utility.h" #include "chrome/browser/metrics/chrome_metrics_service_accessor.h" #include "chrome/browser/metrics/power/battery_level_provider.h" #include "chrome/browser/metrics/power/power_metrics_reporter.h" @@ -522,8 +521,6 @@ crypto::MeasureTPMAvailabilityWin(); #endif // BUILDFLAG(IS_WIN) - bluetooth_utility::ReportBluetoothAvailability(); - // Record whether Chrome is the default browser or not. // Disabled on Linux due to hanging browser tests, see crbug.com/1216328. #if !BUILDFLAG(IS_LINUX)
diff --git a/chrome/browser/metrics/chrome_feature_list_creator.cc b/chrome/browser/metrics/chrome_feature_list_creator.cc index 0d23b13..93893ba 100644 --- a/chrome/browser/metrics/chrome_feature_list_creator.cc +++ b/chrome/browser/metrics/chrome_feature_list_creator.cc
@@ -47,6 +47,7 @@ #include "components/variations/pref_names.h" #include "components/variations/service/variations_service.h" #include "components/variations/variations_crash_keys.h" +#include "components/variations/variations_switches.h" #include "content/public/common/content_switch_dependent_feature_overrides.h" #include "ui/base/resource/resource_bundle.h" @@ -61,11 +62,23 @@ ChromeFeatureListCreator::~ChromeFeatureListCreator() = default; void ChromeFeatureListCreator::CreateFeatureList() { + // Get the variation IDs passed through the command line. This is done early + // on because ConvertFlagsToSwitches() will append to the command line + // the variation IDs from flags (so that they are visible in about://version). + // This will be passed on to `VariationsService::SetUpFieldTrials()`, which + // will manually fetch the variation IDs from flags (hence the reason we do + // not pass the mutated command line, otherwise the IDs will be duplicated). + // It also distinguishes between variation IDs coming from the command line + // and from flags, so we cannot rely on simply putting them all in the + // command line. + const std::string command_line_variation_ids = + base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( + variations::switches::kForceVariationIds); CreatePrefService(); ConvertFlagsToSwitches(); CreateMetricsServices(); SetupInitialPrefs(); - SetUpFieldTrials(); + SetUpFieldTrials(command_line_variation_ids); } void ChromeFeatureListCreator::SetApplicationLocale(const std::string& locale) { @@ -190,7 +203,8 @@ flags_ui::kAddSentinels); } -void ChromeFeatureListCreator::SetUpFieldTrials() { +void ChromeFeatureListCreator::SetUpFieldTrials( + const std::string& command_line_variation_ids) { browser_field_trials_ = std::make_unique<ChromeBrowserFieldTrials>(local_state_.get()); @@ -212,7 +226,7 @@ variations::VariationsService* variations_service = metrics_services_manager_->GetVariationsService(); variations_service->SetUpFieldTrials( - variation_ids, + variation_ids, command_line_variation_ids, content::GetSwitchDependentFeatureOverrides( *base::CommandLine::ForCurrentProcess()), std::move(feature_list), browser_field_trials_.get());
diff --git a/chrome/browser/metrics/chrome_feature_list_creator.h b/chrome/browser/metrics/chrome_feature_list_creator.h index 0904c96..4de41c9 100644 --- a/chrome/browser/metrics/chrome_feature_list_creator.h +++ b/chrome/browser/metrics/chrome_feature_list_creator.h
@@ -91,7 +91,15 @@ private: void CreatePrefService(); void ConvertFlagsToSwitches(); - void SetUpFieldTrials(); + + // Sets up the field trials and related initialization. Call only after + // about:flags have been converted to switches. However, + // |command_line_variation_ids| should be the value of the + // "--force-variation-ids" switch before it is mutated. See + // VariationsFieldTrialCreator::SetUpFieldTrials() for the format of + // |command_line_variation_ids|. + void SetUpFieldTrials(const std::string& command_line_variation_ids); + void CreateMetricsServices(); // Imports variations initial preference any preferences (to local state)
diff --git a/chrome/browser/metrics/chrome_metrics_service_client.cc b/chrome/browser/metrics/chrome_metrics_service_client.cc index c02ed8f..57837d1 100644 --- a/chrome/browser/metrics/chrome_metrics_service_client.cc +++ b/chrome/browser/metrics/chrome_metrics_service_client.cc
@@ -42,6 +42,7 @@ #include "chrome/browser/chrome_notification_types.h" #include "chrome/browser/google/google_brand.h" #include "chrome/browser/history/history_service_factory.h" +#include "chrome/browser/metrics/bluetooth_metrics_provider.h" #include "chrome/browser/metrics/cached_metrics_profile.h" #include "chrome/browser/metrics/chrome_metrics_extensions_helper.h" #include "chrome/browser/metrics/chrome_metrics_service_accessor.h" @@ -763,6 +764,9 @@ metrics_service_->RegisterMetricsProvider( std::make_unique<safe_browsing::SafeBrowsingMetricsProvider>()); + metrics_service_->RegisterMetricsProvider( + std::make_unique<metrics::BluetoothMetricsProvider>()); + #if BUILDFLAG(IS_ANDROID) metrics_service_->RegisterMetricsProvider( std::make_unique<metrics::AndroidMetricsProvider>());
diff --git a/chrome/browser/metrics/chrome_metrics_service_client_unittest.cc b/chrome/browser/metrics/chrome_metrics_service_client_unittest.cc index 430bc576..0f664c0 100644 --- a/chrome/browser/metrics/chrome_metrics_service_client_unittest.cc +++ b/chrome/browser/metrics/chrome_metrics_service_client_unittest.cc
@@ -155,7 +155,7 @@ size_t expected_providers = 2; // This is the number of metrics providers that are outside any #if macros. - expected_providers += 22; + expected_providers += 23; int sample_rate; if (ChromeMetricsServicesManagerClient::GetSamplingRatePerMille(
diff --git a/chrome/browser/notifications/notification_interactive_uitest.cc b/chrome/browser/notifications/notification_interactive_uitest.cc index 1ff6d36..8a46ebe 100644 --- a/chrome/browser/notifications/notification_interactive_uitest.cc +++ b/chrome/browser/notifications/notification_interactive_uitest.cc
@@ -469,7 +469,8 @@ browser(), GURL("about:blank"), WindowOpenDisposition::NEW_BACKGROUND_TAB, ui_test_utils::BROWSER_TEST_WAIT_FOR_TAB); browser()->tab_strip_model()->ActivateTabAt( - 0, {TabStripModel::GestureType::kOther}); + 0, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GetTestPageURL())); CreateSimpleNotification(browser(), true); ASSERT_EQ(1, GetNotificationCount());
diff --git a/chrome/browser/page_load_metrics/integration_tests/layout_instability_browsertest.cc b/chrome/browser/page_load_metrics/integration_tests/layout_instability_browsertest.cc index f4e35b8..2d50308 100644 --- a/chrome/browser/page_load_metrics/integration_tests/layout_instability_browsertest.cc +++ b/chrome/browser/page_load_metrics/integration_tests/layout_instability_browsertest.cc
@@ -7,6 +7,7 @@ #include "base/test/trace_event_analyzer.h" #include "build/build_config.h" #include "chrome/test/base/ui_test_utils.h" +#include "components/page_load_metrics/browser/page_load_metrics_test_waiter.h" #include "components/page_load_metrics/browser/page_load_metrics_util.h" #include "content/public/test/browser_test.h" #include "services/metrics/public/cpp/ukm_builders.h" @@ -31,6 +32,10 @@ void LayoutInstabilityTest::RunWPT(const std::string& test_file, bool trace_only) { + auto waiter = std::make_unique<page_load_metrics::PageLoadMetricsTestWaiter>( + web_contents()); + waiter->AddPageExpectation( + page_load_metrics::PageLoadMetricsTestWaiter::TimingField::kLayoutShift); Start(); StartTracing({"loading", TRACE_DISABLED_BY_DEFAULT("layout_shift.debug")}); Load("/layout-instability/" + test_file); @@ -45,6 +50,8 @@ if (trace_only) return; + waiter->Wait(); + // Finish session. ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GURL("about:blank"))); @@ -115,13 +122,7 @@ } } -// TODO(crbug.com/1336973): Re-enable this test. -#if BUILDFLAG(IS_LINUX) -#define MAYBE_SimpleBlockMovement DISABLED_SimpleBlockMovement -#else -#define MAYBE_SimpleBlockMovement SimpleBlockMovement -#endif // BUILDFLAG(IS_LINUX) -IN_PROC_BROWSER_TEST_F(LayoutInstabilityTest, MAYBE_SimpleBlockMovement) { +IN_PROC_BROWSER_TEST_F(LayoutInstabilityTest, SimpleBlockMovement) { RunWPT("simple-block-movement.html"); }
diff --git a/chrome/browser/page_load_metrics/observers/foreground_duration_ukm_observer_browsertest.cc b/chrome/browser/page_load_metrics/observers/foreground_duration_ukm_observer_browsertest.cc index e338162..65215ca 100644 --- a/chrome/browser/page_load_metrics/observers/foreground_duration_ukm_observer_browsertest.cc +++ b/chrome/browser/page_load_metrics/observers/foreground_duration_ukm_observer_browsertest.cc
@@ -104,10 +104,18 @@ EXPECT_EQ(url1, tab_strip_model->GetWebContentsAt(0)->GetLastCommittedURL()); EXPECT_EQ(url2, tab_strip_model->GetWebContentsAt(1)->GetLastCommittedURL()); - tab_strip_model->ActivateTabAt(0, {TabStripModel::GestureType::kOther}); - tab_strip_model->ActivateTabAt(1, {TabStripModel::GestureType::kOther}); - tab_strip_model->ActivateTabAt(0, {TabStripModel::GestureType::kOther}); - tab_strip_model->ActivateTabAt(1, {TabStripModel::GestureType::kOther}); + tab_strip_model->ActivateTabAt( + 0, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); + tab_strip_model->ActivateTabAt( + 1, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); + tab_strip_model->ActivateTabAt( + 0, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); + tab_strip_model->ActivateTabAt( + 1, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); tab_strip_model->CloseAllTabs(); ExpectMetricCountForUrl(url1, "ForegroundDuration", 3); ExpectMetricCountForUrl(url1, "ForegroundNumInputEvents", 3);
diff --git a/chrome/browser/page_load_metrics/observers/live_tab_count_page_load_metrics_observer_browsertest.cc b/chrome/browser/page_load_metrics/observers/live_tab_count_page_load_metrics_observer_browsertest.cc index e067c485..d35d138 100644 --- a/chrome/browser/page_load_metrics/observers/live_tab_count_page_load_metrics_observer_browsertest.cc +++ b/chrome/browser/page_load_metrics/observers/live_tab_count_page_load_metrics_observer_browsertest.cc
@@ -95,7 +95,8 @@ // Switch tabs so the paint events occur. browser()->tab_strip_model()->ActivateTabAt( - 1, {TabStripModel::GestureType::kOther}); + 1, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); waiter->Wait();
diff --git a/chrome/browser/page_load_metrics/page_load_metrics_browsertest.cc b/chrome/browser/page_load_metrics/page_load_metrics_browsertest.cc index 309fab0b..dad4dec 100644 --- a/chrome/browser/page_load_metrics/page_load_metrics_browsertest.cc +++ b/chrome/browser/page_load_metrics/page_load_metrics_browsertest.cc
@@ -2638,7 +2638,9 @@ ASSERT_TRUE(tab_strip); ASSERT_EQ(2, tab_strip->count()); ASSERT_EQ(0, tab_strip->active_index()); - tab_strip->ActivateTabAt(1, {TabStripModel::GestureType::kOther}); + tab_strip->ActivateTabAt( + 1, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); session_restore_paint_waiter.WaitForForegroundTabs(1);
diff --git a/chrome/browser/policy/configuration_policy_handler_list_factory.cc b/chrome/browser/policy/configuration_policy_handler_list_factory.cc index 35817700..6e0ef4a 100644 --- a/chrome/browser/policy/configuration_policy_handler_list_factory.cc +++ b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
@@ -749,9 +749,6 @@ { key::kCloudPrintProxyEnabled, prefs::kCloudPrintProxyEnabled, base::Value::Type::BOOLEAN }, - { key::kCloudPrintSubmitEnabled, - prefs::kCloudPrintSubmitEnabled, - base::Value::Type::BOOLEAN }, { key::kShowAppsShortcutInBookmarkBar, bookmarks::prefs::kShowAppsShortcutInBookmarkBar, base::Value::Type::BOOLEAN }, @@ -1591,9 +1588,6 @@ { key::kSecurityKeyPermitAttestation, prefs::kSecurityKeyPermitAttestation, base::Value::Type::LIST }, - { key::kU2fSecurityKeyApiEnabled, - extensions::pref_names::kU2fSecurityKeyApiEnabled, - base::Value::Type::BOOLEAN }, #endif // BUILDFLAG(ENABLE_EXTENSIONS) #if !BUILDFLAG(IS_CHROMEOS) && BUILDFLAG(ENABLE_EXTENSIONS)
diff --git a/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceKeys.java b/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceKeys.java index 4c8cf99e..0acf1d81 100644 --- a/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceKeys.java +++ b/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceKeys.java
@@ -208,11 +208,20 @@ public static final KeyPrefix CUSTOM_TABS_DEX_LAST_UPDATE_TIME_PREF_PREFIX = new KeyPrefix("pref_local_custom_tabs_module_dex_last_update_time_*"); - public static final String CUSTOM_TABS_LAST_URL = "pref_last_custom_tab_url"; + + /** Package name of the client app that uses CCT service of the last launched CCT. */ + public static final String CUSTOM_TABS_LAST_CLIENT_PACKAGE = + "Chrome.CustomTabs.LastClientPackage"; public static final String CUSTOM_TABS_LAST_CLOSE_TIMESTAMP = "Chrome.CustomTabs.LastCloseTimestamp"; public static final String CUSTOM_TABS_LAST_CLOSE_TAB_INTERACTION = "Chrome.CustomTabs.LastCloseTabInteraction"; + /** The referrer URI string of the last launched CCT. */ + public static final String CUSTOM_TABS_LAST_REFERRER = "Chrome.CustomTabs.LastReferrer"; + /** {@link Activity#getTaskId()} of the last launched CCT. */ + public static final String CUSTOM_TABS_LAST_TASK_ID = "Chrome.CustomTabs.LastTaskId"; + /** Uri of the last launched CCT. */ + public static final String CUSTOM_TABS_LAST_URL = "pref_last_custom_tab_url"; /** * Keys used to save whether it is ready to promo. @@ -969,9 +978,12 @@ CONTEXT_MENU_SEARCH_WITH_GOOGLE_LENS_CLICKED, CONTEXT_MENU_SHOP_IMAGE_WITH_GOOGLE_LENS_CLICKED, CONTINUOUS_SEARCH_DISMISSAL_COUNT, - CRYPTID_LAST_RENDER_TIMESTAMP, - CUSTOM_TABS_LAST_CLOSE_TIMESTAMP, + CUSTOM_TABS_LAST_CLIENT_PACKAGE, CUSTOM_TABS_LAST_CLOSE_TAB_INTERACTION, + CUSTOM_TABS_LAST_CLOSE_TIMESTAMP, + CUSTOM_TABS_LAST_REFERRER, + CUSTOM_TABS_LAST_TASK_ID, + CRYPTID_LAST_RENDER_TIMESTAMP, DEFAULT_BROWSER_PROMO_LAST_DEFAULT_STATE, DEFAULT_BROWSER_PROMO_LAST_PROMO_TIME, DEFAULT_BROWSER_PROMO_PROMOED_BY_SYSTEM_SETTINGS,
diff --git a/chrome/browser/prefetch/prefetch_proxy/prefetch_proxy_browsertest.cc b/chrome/browser/prefetch/prefetch_proxy/prefetch_proxy_browsertest.cc index 445a7ba..b9d441d 100644 --- a/chrome/browser/prefetch/prefetch_proxy/prefetch_proxy_browsertest.cc +++ b/chrome/browser/prefetch/prefetch_proxy/prefetch_proxy_browsertest.cc
@@ -509,9 +509,9 @@ // features since order is tricky when doing different feature lists between // base and derived classes. virtual void SetFeatures() { - // Important: Features with parameters can't be used here, because it will - // cause a failed DCHECK in the SSL reporting test. - scoped_feature_list_.InitAndEnableFeature(features::kIsolatePrerenders); + scoped_feature_list_.InitAndEnableFeatureWithParameters( + features::kIsolatePrerenders, + {{"use_speculation_rules", "false"}, {"max_srp_prefetches", "1"}}); } void SetUpOnMainThread() override { @@ -2355,7 +2355,10 @@ class PolicyTestPrefetchProxyBrowserTest : public policy::PolicyTest { public: void SetUp() override { - scoped_feature_list_.InitAndEnableFeature(features::kIsolatePrerenders); + scoped_feature_list_.InitWithFeatures( + {features::kIsolatePrerenders, + blink::features::kSpeculationRulesPrefetchProxy}, + {}); policy::PolicyTest::SetUp(); } @@ -2368,14 +2371,34 @@ return browser()->tab_strip_model()->GetActiveWebContents(); } - void MakeNavigationPrediction(const GURL& doc_url, - const std::vector<GURL>& predicted_urls) { - NavigationPredictorKeyedServiceFactory::GetForProfile(browser()->profile()) - ->OnPredictionUpdated( - GetWebContents(), doc_url, - NavigationPredictorKeyedService::PredictionSource:: - kAnchorElementsParsedFromWebPage, - predicted_urls); + void InsertSpeculation(bool use_prefetch_proxy, + const std::vector<GURL>& prefetch_urls) { + std::string speculation_script = R"( + var script = document.createElement('script'); + script.type = 'speculationrules'; + script.text = `{ + "prefetch": [{ + "source": "list", + "urls": [)"; + + bool first = true; + for (const GURL& url : prefetch_urls) { + if (!first) + speculation_script.append(","); + first = false; + speculation_script.append("\"").append(url.spec()).append("\""); + } + speculation_script.append("]"); + + if (use_prefetch_proxy) + speculation_script.append(R"(, + "requires": ["anonymous-client-ip-when-cross-origin"])"); + speculation_script.append(R"( + }] + }`; + document.head.appendChild(script);)"); + + EXPECT_TRUE(ExecuteScript(GetWebContents(), speculation_script)); } private: @@ -2398,7 +2421,8 @@ PrefetchProxyTabHelper::FromWebContents(GetWebContents()); GURL doc_url("https://www.google.com/search?q=test"); - MakeNavigationPrediction(doc_url, {GURL("https://test.com/")}); + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), doc_url)); + InsertSpeculation(true, {GURL("https://test.com/")}); base::RunLoop().RunUntilIdle(); EXPECT_EQ(tab_helper->srp_metrics().predicted_urls_count_, 0U); @@ -2412,7 +2436,8 @@ PrefetchProxyTabHelper::FromWebContents(GetWebContents()); GURL doc_url("https://www.google.com/search?q=test"); - MakeNavigationPrediction(doc_url, {GURL("https://test.com/")}); + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), doc_url)); + InsertSpeculation(true, {GURL("https://test.com/")}); base::RunLoop().RunUntilIdle(); EXPECT_EQ(tab_helper->srp_metrics().predicted_urls_count_, 1U); @@ -2426,6 +2451,15 @@ CertReportHelper::SetFakeOfficialBuildForTesting(); } + void SetFeatures() override { + // Important: Features with parameters can't be used here, because it will + // cause a failed DCHECK in the SSL reporting test. + scoped_feature_list_.InitWithFeatures( + {features::kIsolatePrerenders, + blink::features::kSpeculationRulesPrefetchProxy}, + {}); + } + void SetUpCommandLine(base::CommandLine* cmd) override { PrefetchProxyBrowserTest::SetUpCommandLine(cmd); cmd->RemoveSwitch("ignore-certificate-errors"); @@ -2446,6 +2480,9 @@ return nullptr; return helper->GetBlockingPageForCurrentlyCommittedNavigationForTesting(); } + + private: + base::test::ScopedFeatureList scoped_feature_list_; }; IN_PROC_BROWSER_TEST_F( @@ -2460,7 +2497,7 @@ https_expired_server.ServeFilesFromSourceDirectory("chrome/test/data"); ASSERT_TRUE(https_expired_server.Start()); - GURL safe_page = GetOriginServerURL("/simple.html"); + GURL safe_page = GURL("https://www.google.com/search?q=test"); // Opt in to sending reports for invalid certificate chains. certificate_reporting_test_utils::SetCertReportingOptIn( @@ -2482,8 +2519,7 @@ tab_helper_observer.SetOnPrefetchErrorClosure( prefetch_run_loop.QuitClosure()); - GURL doc_url("https://www.google.com/search?q=test"); - MakeNavigationPrediction(doc_url, {eligible_link}); + InsertSpeculation(false, true, {eligible_link}); // This run loop stops when the prefetches completes with its error. prefetch_run_loop.Run(); @@ -2918,9 +2954,12 @@ } void SetFeatures() override { - PrefetchProxyBrowserTest::SetFeatures(); - scoped_feature_list_.InitAndEnableFeature( - blink::features::kLightweightNoStatePrefetch); + scoped_feature_list_.InitWithFeaturesAndParameters( + {{features::kIsolatePrerenders, + {{"use_speculation_rules", "false"}, + {"max_subresource_count_per_prerender", "50"}}}, + {blink::features::kLightweightNoStatePrefetch, {}}}, + {}); } private: @@ -3575,19 +3614,21 @@ void SetFeatures() override { PrefetchProxyBrowserTest::SetFeatures(); - scoped_feature_list_.InitAndEnableFeature( - blink::features::kLightweightNoStatePrefetch); - probing_scoped_feature_list_.InitAndEnableFeatureWithParameters( - features::kIsolatePrerendersMustProbeOrigin, + scoped_feature_list_.InitWithFeaturesAndParameters( { - {"do_canary", "false"}, - {"ineligible_decoy_request_probability", "0"}, - }); + {features::kIsolatePrerenders, + {{"use_speculation_rules", "false"}, + {"max_subresource_count_per_prerender", "50"}}}, + {blink::features::kLightweightNoStatePrefetch, {}}, + {features::kIsolatePrerendersMustProbeOrigin, + {{"do_canary", "false"}, + {"ineligible_decoy_request_probability", "0"}}}, + }, + {}); } private: base::test::ScopedFeatureList scoped_feature_list_; - base::test::ScopedFeatureList probing_scoped_feature_list_; }; IN_PROC_BROWSER_TEST_F(ProbingAndNSPEnabledPrefetchProxyBrowserTest, @@ -4058,7 +4099,9 @@ void SetFeatures() override { scoped_feature_list_.InitWithFeaturesAndParameters( {{features::kIsolatePrerenders, - {{"use_speculation_rules", "true"}, {"max_srp_prefetches", "3"}}}, + {{"use_speculation_rules", "true"}, + {"max_srp_prefetches", "3"}, + {"max_subresource_count_per_prerender", "50"}}}, {blink::features::kLightweightNoStatePrefetch, {}}, {blink::features::kSpeculationRulesPrefetchProxy, {}}}, {{features::kLazyImageLoading}}); @@ -4518,6 +4561,7 @@ void SetFeatures() override { scoped_feature_list_.InitAndEnableFeatureWithParameters( features::kIsolatePrerenders, { + {"use_speculation_rules", "false"}, {"cacheable_duration", "0"}, }); } @@ -4581,7 +4625,8 @@ scoped_feature_list_.InitAndEnableFeatureWithParameters( features::kIsolatePrerenders, - {{"use_individual_network_contexts", "true"}}); + {{"use_speculation_rules", "false"}, + {"use_individual_network_contexts", "true"}}); } private:
diff --git a/chrome/browser/prefetch/prefetch_proxy/prefetch_proxy_features.cc b/chrome/browser/prefetch/prefetch_proxy/prefetch_proxy_features.cc index 6e0d990..303f72c 100644 --- a/chrome/browser/prefetch/prefetch_proxy/prefetch_proxy_features.cc +++ b/chrome/browser/prefetch/prefetch_proxy/prefetch_proxy_features.cc
@@ -8,11 +8,23 @@ // Forces all eligible prerenders to be done in an isolated manner such that no // user-identifying information is used during the prefetch. -const base::Feature kIsolatePrerenders{"IsolatePrerenders", - base::FEATURE_DISABLED_BY_DEFAULT}; +const base::Feature kIsolatePrerenders { + "IsolatePrerenders", +#if BUILDFLAG(IS_ANDROID) + base::FEATURE_ENABLED_BY_DEFAULT +#else + base::FEATURE_DISABLED_BY_DEFAULT +#endif +}; // Forces Chrome to probe the origin before reusing a cached response. -const base::Feature kIsolatePrerendersMustProbeOrigin{ - "IsolatePrerendersMustProbeOrigin", base::FEATURE_DISABLED_BY_DEFAULT}; +const base::Feature kIsolatePrerendersMustProbeOrigin { + "IsolatePrerendersMustProbeOrigin", +#if BUILDFLAG(IS_ANDROID) + base::FEATURE_ENABLED_BY_DEFAULT +#else + base::FEATURE_DISABLED_BY_DEFAULT +#endif +}; } // namespace features
diff --git a/chrome/browser/prefetch/prefetch_proxy/prefetch_proxy_params.cc b/chrome/browser/prefetch/prefetch_proxy/prefetch_proxy_params.cc index 50005ec0..d9e99871 100644 --- a/chrome/browser/prefetch/prefetch_proxy/prefetch_proxy_params.cc +++ b/chrome/browser/prefetch/prefetch_proxy/prefetch_proxy_params.cc
@@ -77,7 +77,7 @@ } int max = base::GetFieldTrialParamByFeatureAsInt(features::kIsolatePrerenders, - "max_srp_prefetches", 1); + "max_srp_prefetches", 5); if (max < 0) { return absl::nullopt; } @@ -166,7 +166,7 @@ if (url.is_valid()) { return url; } - return GURL("http://tls.tunnel.check.googlezip.net/connect"); + return GURL("http://tls-tunnel-check.googlezip.net/connect"); } GURL PrefetchProxyDNSCanaryCheckURL() { @@ -175,7 +175,7 @@ if (url.is_valid()) { return url; } - return GURL("http://dns.tunnel.check.googlezip.net/connect"); + return GURL("http://dns-tunnel-check.googlezip.net/connect"); } base::TimeDelta PrefetchProxyCanaryCheckCacheLifetime() { @@ -194,21 +194,21 @@ } return base::GetFieldTrialParamByFeatureAsInt( - features::kIsolatePrerenders, "max_subresource_count_per_prerender", 50); + features::kIsolatePrerenders, "max_subresource_count_per_prerender", 0); } bool PrefetchProxyStartsSpareRenderer() { return base::CommandLine::ForCurrentProcess()->HasSwitch( "isolated-prerender-start-spare-renderer") || base::GetFieldTrialParamByFeatureAsBool(features::kIsolatePrerenders, - "start_spare_renderer", false); + "start_spare_renderer", true); } bool PrefetchProxyUseSpeculationRules() { return base::CommandLine::ForCurrentProcess()->HasSwitch( "isolated-prerender-use-speculation-rules") || - base::GetFieldTrialParamByFeatureAsBool( - features::kIsolatePrerenders, "use_speculation_rules", false); + base::GetFieldTrialParamByFeatureAsBool(features::kIsolatePrerenders, + "use_speculation_rules", true); } bool PrefetchProxyShouldPrefetchPosition(size_t position) { @@ -279,7 +279,7 @@ bool PrefetchProxyAllowAllDomainsForExtendedPreloading() { return base::GetFieldTrialParamByFeatureAsBool( features::kIsolatePrerenders, "allow_all_domains_for_extended_preloading", - false); + true); } base::TimeDelta PrefetchProxyCacheableDuration() { @@ -294,7 +294,7 @@ bool PrefetchProxyUseIndividualNetworkContextsForEachPrefetch() { return base::GetFieldTrialParamByFeatureAsBool( - features::kIsolatePrerenders, "use_individual_network_contexts", false); + features::kIsolatePrerenders, "use_individual_network_contexts", true); } bool PrefetchProxySupportNonPrivatePrefetches() {
diff --git a/chrome/browser/prefetch/prefetch_proxy/prefetch_proxy_tab_helper_unittest.cc b/chrome/browser/prefetch/prefetch_proxy/prefetch_proxy_tab_helper_unittest.cc index 6c0ce023..3c8448b 100644 --- a/chrome/browser/prefetch/prefetch_proxy/prefetch_proxy_tab_helper_unittest.cc +++ b/chrome/browser/prefetch/prefetch_proxy/prefetch_proxy_tab_helper_unittest.cc
@@ -418,7 +418,8 @@ PrefetchProxyTabHelperTest() { scoped_feature_list_.InitAndEnableFeatureWithParameters( features::kIsolatePrerenders, - {{"ineligible_decoy_request_probability", "0"}}); + {{"use_speculation_rules", "false"}, + {"ineligible_decoy_request_probability", "0"}}); } }; @@ -938,7 +939,8 @@ public: PrefetchProxyTabHelperWithHTMLOnly() { scoped_feature_list_.InitAndEnableFeatureWithParameters( - features::kIsolatePrerenders, {{"html_only", "true"}}); + features::kIsolatePrerenders, + {{"use_speculation_rules", "false"}, {"html_only", "true"}}); } }; @@ -1354,7 +1356,8 @@ PrefetchProxyTabHelperWithDecoyTest() { scoped_feature_list_.InitAndEnableFeatureWithParameters( features::kIsolatePrerenders, - {{"ineligible_decoy_request_probability", "1"}, + {{"use_speculation_rules", "false"}, + {"ineligible_decoy_request_probability", "1"}, {"max_srp_prefetches", "2"}}); } }; @@ -1497,7 +1500,8 @@ PrefetchProxyTabHelperBodyLimitTest() { scoped_feature_list_.InitAndEnableFeatureWithParameters( features::kIsolatePrerenders, - {{"max_mainframe_body_length_kb", "0"}, + {{"use_speculation_rules", "false"}, + {"max_mainframe_body_length_kb", "0"}, {"ineligible_decoy_request_probability", "0"}}); } }; @@ -1548,7 +1552,8 @@ PrefetchProxyTabHelperPredictionPositionsTest() { scoped_feature_list_.InitAndEnableFeatureWithParameters( features::kIsolatePrerenders, - {{"prefetch_positions", "0"}, + {{"use_speculation_rules", "false"}, + {"prefetch_positions", "0"}, {"ineligible_decoy_request_probability", "0"}}); } }; @@ -1584,7 +1589,8 @@ PrefetchProxyTabHelperNoPrefetchesTest() { scoped_feature_list_.InitAndEnableFeatureWithParameters( features::kIsolatePrerenders, - {{"max_srp_prefetches", "0"}, + {{"use_speculation_rules", "false"}, + {"max_srp_prefetches", "0"}, {"ineligible_decoy_request_probability", "0"}}); } }; @@ -1630,7 +1636,8 @@ PrefetchProxyTabHelperUnlimitedPrefetchesTest() { scoped_feature_list_.InitAndEnableFeatureWithParameters( features::kIsolatePrerenders, - {{"max_srp_prefetches", "-1"}, + {{"use_speculation_rules", "false"}, + {"max_srp_prefetches", "-1"}, {"ineligible_decoy_request_probability", "0"}}); } }; @@ -1699,7 +1706,8 @@ PrefetchProxyTabHelperConcurrentPrefetchesTest() { scoped_feature_list_.InitAndEnableFeatureWithParameters( features::kIsolatePrerenders, - {{"max_concurrent_prefetches", "2"}, + {{"use_speculation_rules", "false"}, + {"max_concurrent_prefetches", "2"}, {"max_srp_prefetches", "-1"}, {"ineligible_decoy_request_probability", "0"}}); } @@ -1759,7 +1767,8 @@ PrefetchProxyTabHelperLimitedPrefetchesTest() { scoped_feature_list_.InitAndEnableFeatureWithParameters( features::kIsolatePrerenders, - {{"max_srp_prefetches", "2"}, + {{"use_speculation_rules", "false"}, + {"max_srp_prefetches", "2"}, {"ineligible_decoy_request_probability", "0"}}); } }; @@ -1908,7 +1917,8 @@ PrefetchProxyTabHelperRedirectWithDecoyTest() { scoped_feature_list_.InitAndEnableFeatureWithParameters( features::kIsolatePrerenders, - {{"ineligible_decoy_request_probability", "1"}, + {{"use_speculation_rules", "false"}, + {"ineligible_decoy_request_probability", "1"}, {"max_srp_prefetches", "2"}}); } }; @@ -2006,7 +2016,8 @@ PrefetchProxyTabHelperRedirectTest() { scoped_feature_list_.InitAndEnableFeatureWithParameters( features::kIsolatePrerenders, - {{"ineligible_decoy_request_probability", "0"}}); + {{"use_speculation_rules", "false"}, + {"ineligible_decoy_request_probability", "0"}}); } }; @@ -2109,7 +2120,8 @@ // way. scoped_feature_list_.InitAndEnableFeatureWithParameters( features::kIsolatePrerenders, - {{"max_srp_prefetches", "-1"}, + {{"use_speculation_rules", "false"}, + {"max_srp_prefetches", "-1"}, {"ineligible_decoy_request_probability", "0"}}); } };
diff --git a/chrome/browser/prefetch/prefetch_proxy/prefetch_proxy_url_loader_interceptor_unittest.cc b/chrome/browser/prefetch/prefetch_proxy/prefetch_proxy_url_loader_interceptor_unittest.cc index 070525c..508f7a0 100644 --- a/chrome/browser/prefetch/prefetch_proxy/prefetch_proxy_url_loader_interceptor_unittest.cc +++ b/chrome/browser/prefetch/prefetch_proxy/prefetch_proxy_url_loader_interceptor_unittest.cc
@@ -70,6 +70,13 @@ PrefetchProxyURLLoaderInterceptorTest() = default; ~PrefetchProxyURLLoaderInterceptorTest() override = default; + void SetUp() override { + ChromeRenderViewHostTestHarness::SetUp(); + + scoped_feature_list_.InitAndDisableFeature( + features::kIsolatePrerendersMustProbeOrigin); + } + void TearDown() override { prerender::NoStatePrefetchManager* no_state_prefetch_manager = prerender::NoStatePrefetchManagerFactory::GetForBrowserContext( @@ -113,6 +120,7 @@ private: absl::optional<bool> was_intercepted_; base::OnceClosure waiting_for_callback_closure_; + base::test::ScopedFeatureList scoped_feature_list_; }; TEST_F(PrefetchProxyURLLoaderInterceptorTest, DISABLE_ASAN(WantIntercept)) {
diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc index 4a6a777..4b07a0e 100644 --- a/chrome/browser/prefs/browser_prefs.cc +++ b/chrome/browser/prefs/browser_prefs.cc
@@ -180,6 +180,7 @@ #include "extensions/browser/api/runtime/runtime_api.h" #include "extensions/browser/extension_prefs.h" #include "extensions/browser/permissions_manager.h" +#include "extensions/browser/pref_names.h" #if BUILDFLAG(IS_CHROMEOS_ASH) #include "chrome/browser/ash/attestation/tpm_challenge_key.h" #include "chrome/browser/ash/crosapi/browser_data_migrator.h" @@ -1217,9 +1218,6 @@ DeviceOAuth2TokenStoreDesktop::RegisterPrefs(registry); #endif - registry->RegisterBooleanPref( - policy::policy_prefs::kSetTimeoutWithout1MsClampEnabled, false); - #if BUILDFLAG(ENABLE_SCREEN_AI_SERVICE) screen_ai::RegisterLocalStatePrefs(registry); #endif // BUILDFLAG(ENABLE_SCREEN_AI_SERVICE) @@ -1947,6 +1945,12 @@ profile_prefs->ClearPref(kImprovedShortcutsNotificationShownCount); #endif // BUILDFLAG(IS_CHROMEOS_ASH) + // Added 06/2022. +#if BUILDFLAG(ENABLE_EXTENSIONS) + profile_prefs->ClearPref(extensions::pref_names::kU2fSecurityKeyApiEnabled); +#endif + profile_prefs->ClearPref(prefs::kCloudPrintSubmitEnabled); + // Please don't delete the following line. It is used by PRESUBMIT.py. // END_MIGRATE_OBSOLETE_PROFILE_PREFS
diff --git a/chrome/browser/renderer_context_menu/render_view_context_menu_browsertest.cc b/chrome/browser/renderer_context_menu/render_view_context_menu_browsertest.cc index dc5829e..d9f81ce 100644 --- a/chrome/browser/renderer_context_menu/render_view_context_menu_browsertest.cc +++ b/chrome/browser/renderer_context_menu/render_view_context_menu_browsertest.cc
@@ -99,6 +99,7 @@ #include "testing/gtest/include/gtest/gtest.h" #include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h" #include "third_party/blink/public/common/context_menu_data/context_menu_data.h" +#include "third_party/blink/public/common/context_menu_data/edit_flags.h" #include "third_party/blink/public/common/input/web_input_event.h" #include "third_party/blink/public/common/switches.h" #include "third_party/blink/public/mojom/context_menu/context_menu.mojom.h" @@ -368,11 +369,21 @@ } protected: + struct PdfInfo { + // The selected text in the PDF when context menu is created. + std::u16string selection_text; + + // Whether the PDF has copy permission. + bool can_copy; + }; + guest_view::TestGuestViewManager* test_guest_view_manager() const { return test_guest_view_manager_; } - std::unique_ptr<TestRenderViewContextMenu> SetupAndCreateMenu() { + // Creates a context menu with the given `info`: + std::unique_ptr<TestRenderViewContextMenu> SetupAndCreateMenuWithPdfInfo( + const PdfInfo& info) { // Load a pdf page. GURL page_url = ui_test_utils::GetTestUrl( base::FilePath(FILE_PATH_LITERAL("pdf")), @@ -399,12 +410,22 @@ params.frame_url = extension_frame_->GetLastCommittedURL(); params.media_type = blink::mojom::ContextMenuDataMediaType::kPlugin; params.media_flags |= blink::ContextMenuData::kMediaCanRotate; + params.selection_text = info.selection_text; + // Mimic how `edit_flag` is set in ContextMenuController::ShowContextMenu(). + if (info.can_copy) + params.edit_flags |= blink::ContextMenuDataEditFlags::kCanCopy; + auto menu = std::make_unique<TestRenderViewContextMenu>(*extension_frame_, params); menu->Init(); return menu; } + std::unique_ptr<TestRenderViewContextMenu> SetupAndCreateMenu() { + return SetupAndCreateMenuWithPdfInfo( + {/*selection_text=*/u"", /*can_copy=*/true}); + } + // Helper function for testing context menu of a pdf plugin inside a web page. void TestContextMenuOfPdfInsideWebPage( const base::FilePath::CharType* file_name) { @@ -2142,6 +2163,34 @@ ASSERT_FALSE(menu->IsCommandIdEnabled(IDC_CONTENT_CONTEXT_ROTATECCW)); } +IN_PROC_BROWSER_TEST_F(PdfPluginContextMenuBrowserTest, CopyWithoutText) { + std::unique_ptr<TestRenderViewContextMenu> menu = SetupAndCreateMenu(); + + // Test that 'Copy' doesn't exist. + ASSERT_FALSE(menu->IsItemPresent(IDC_CONTENT_CONTEXT_COPY)); +} + +IN_PROC_BROWSER_TEST_F(PdfPluginContextMenuBrowserTest, CopyText) { + std::unique_ptr<TestRenderViewContextMenu> menu = + SetupAndCreateMenuWithPdfInfo( + {/*selection_text=*/u"text", /*can_copy=*/true}); + + // Test that 'Copy' exists and it is enabled. + ASSERT_TRUE(menu->IsItemPresent(IDC_CONTENT_CONTEXT_COPY)); + ASSERT_TRUE(menu->IsCommandIdEnabled(IDC_CONTENT_CONTEXT_COPY)); +} + +IN_PROC_BROWSER_TEST_F(PdfPluginContextMenuBrowserTest, + CopyTextWithRestriction) { + std::unique_ptr<TestRenderViewContextMenu> menu = + SetupAndCreateMenuWithPdfInfo( + {/*selection_text=*/u"text", /*can_copy=*/false}); + + // Test that 'Copy' exists and it is disabled. + ASSERT_TRUE(menu->IsItemPresent(IDC_CONTENT_CONTEXT_COPY)); + ASSERT_FALSE(menu->IsCommandIdEnabled(IDC_CONTENT_CONTEXT_COPY)); +} + IN_PROC_BROWSER_TEST_F(PdfPluginContextMenuBrowserTest, IframedPdfHasNoPageItems) { TestContextMenuOfPdfInsideWebPage(FILE_PATH_LITERAL("test-iframe-pdf.html"));
diff --git a/chrome/browser/reputation/reputation_service.cc b/chrome/browser/reputation/reputation_service.cc index 6a9c9cc1..246acd1 100644 --- a/chrome/browser/reputation/reputation_service.cc +++ b/chrome/browser/reputation/reputation_service.cc
@@ -70,12 +70,13 @@ } }; -// Returns whether or not the Safety Tip should be suppressed for the given URL. -// Checks SafeBrowsing-style permutations of |url| against the component updater -// allowlist, as well as any enterprise-set allowlisting of the hostname, and -// returns whether the URL is explicitly allowed. Fails closed, so that warnings -// are suppressed if the component is unavailable. -bool ShouldSuppressWarning(Profile* profile, const GURL& url) { +// Returns whether or not the Safety Tip should be suppressed on the given URL, +// if it's accused of spoofing |victim_url|. Checks both against the component +// updater allowlist, as well as any enterprise-set allowlist. Fails closed, so +// that warnings are suppressed if the component is unavailable. +bool ShouldSuppressWarning(Profile* profile, + const GURL& url, + const GURL& victim_url) { // Check any policy-set allowlist. if (IsAllowedByEnterprisePolicy(profile->GetPrefs(), url)) { return true; @@ -90,7 +91,8 @@ // flag on any known false positives until the client received the update. return true; } - return reputation::IsUrlAllowlistedBySafetyTipsComponent(proto, url); + return reputation::IsUrlAllowlistedBySafetyTipsComponent( + proto, url.GetWithEmptyPath(), victim_url.GetWithEmptyPath()); } // Gets the eTLD+1 of the provided hostname, including private registries (e.g. @@ -185,13 +187,6 @@ // decision with other heuristics that may trigger later. bool done_checking_reputation_status = false; - // 0. Server-side warning suppression. - // If the URL is on the allowlist list, do nothing else. This is only used to - // mitigate false positives, so no further processing should be done. - if (ShouldSuppressWarning(profile_, url)) { - done_checking_reputation_status = true; - } - // 1. Engagement check // Ensure that this URL is not already engaged. We can't use the synchronous // SiteEngagementService::IsEngagementAtLeast as it has side effects. This @@ -250,6 +245,16 @@ done_checking_reputation_status = true; } + // If we found a SafetyTipStatus, possibly clear it if the URL is on the + // allowlist. + if (result.safety_tip_status != SafetyTipStatus::kUnknown && + result.safety_tip_status != SafetyTipStatus::kNone && + result.safety_tip_status != SafetyTipStatus::kBadKeyword && + ShouldSuppressWarning(profile_, url, result.suggested_url)) { + result.safety_tip_status = SafetyTipStatus::kNone; + result.suggested_url = GURL(); + } + if (IsIgnored(url)) { if (result.safety_tip_status == SafetyTipStatus::kBadReputation) { result.safety_tip_status = SafetyTipStatus::kBadReputationIgnored;
diff --git a/chrome/browser/resource_coordinator/tab_activity_watcher_browsertest.cc b/chrome/browser/resource_coordinator/tab_activity_watcher_browsertest.cc index bfa6433..987aa8a 100644 --- a/chrome/browser/resource_coordinator/tab_activity_watcher_browsertest.cc +++ b/chrome/browser/resource_coordinator/tab_activity_watcher_browsertest.cc
@@ -211,7 +211,8 @@ test_clock.Advance(base::Minutes(1)); browser()->tab_strip_model()->ActivateTabAt( - 0, {TabStripModel::GestureType::kOther}); + 0, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); test_clock.Advance(base::Minutes(1)); // A background tab is scored successfully. @@ -257,7 +258,8 @@ // Switching to another tab logs the previously active tab. browser()->tab_strip_model()->ActivateTabAt( - 0, {TabStripModel::GestureType::kOther}); + 0, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); { SCOPED_TRACE(""); ukm_entry_checker_->ExpectNewEntry(kTabMetricsEntryName, kTabUrls[2], @@ -266,7 +268,8 @@ } browser()->tab_strip_model()->ActivateTabAt( - 1, {TabStripModel::GestureType::kOther}); + 1, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); { SCOPED_TRACE(""); ukm_entry_checker_->ExpectNewEntry(kTabMetricsEntryName, kTabUrls[0], @@ -339,7 +342,8 @@ // Sanity check: the new tab doesn't have a beforeunload handler. browser()->tab_strip_model()->ActivateTabAt( - 0, {TabStripModel::GestureType::kOther}); + 0, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); { SCOPED_TRACE(""); ukm_entry_checker_->ExpectNewEntry(kTabMetricsEntryName, test_urls_[0], @@ -388,7 +392,8 @@ 1, std::move(owned_dragged_contents), TabStripModel::ADD_NONE); dragged_contents->WasShown(); browser_2->tab_strip_model()->ActivateTabAt( - 1, {TabStripModel::GestureType::kOther}); + 1, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); EXPECT_EQ(0, ukm_entry_checker_->NumNewEntriesRecorded(kFOCEntryName)); // The first tab in this window was backgrounded when the new one was @@ -481,7 +486,8 @@ // Switching to first tab logs a forgrounded event for test_urls_[0] // and a backgrounded event for test_urls_[1]. browser()->tab_strip_model()->ActivateTabAt( - 0, {TabStripModel::GestureType::kOther}); + 0, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); { SCOPED_TRACE(""); ukm_entry_checker_->ExpectNewEntry(kTabMetricsEntryName, test_urls_[1], @@ -572,7 +578,8 @@ test_clock.Advance(base::Minutes(1)); // Activate tab@0. browser()->tab_strip_model()->ActivateTabAt( - 0, {TabStripModel::GestureType::kOther}); + 0, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); test_clock.Advance(base::Minutes(1)); // TabManager_TabMetrics should not have been logged yet. @@ -609,7 +616,8 @@ // Reactivate tab@1 should log a ForegroundedOrClosed event with LabelId as // label_id_1. browser()->tab_strip_model()->ActivateTabAt( - 1, {TabStripModel::GestureType::kOther}); + 1, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); test_clock.Advance(base::Minutes(1)); { SCOPED_TRACE(""); @@ -696,7 +704,8 @@ // Switching to first tab logs a forgrounded event for test_urls_[0]. browser()->tab_strip_model()->ActivateTabAt( - 0, {TabStripModel::GestureType::kOther}); + 0, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); { SCOPED_TRACE(""); UkmMetricMap expected_metrics = { @@ -832,7 +841,8 @@ // Switching to another tab logs the previously active tab. browser()->tab_strip_model()->ActivateTabAt( - 0, {TabStripModel::GestureType::kOther}); + 0, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); { SCOPED_TRACE(""); ukm_entry_checker_->ExpectNewEntry(kTabMetricsEntryName, test_urls_[1], @@ -851,7 +861,8 @@ // Switching to another tab logs the previously active tab. browser()->tab_strip_model()->ActivateTabAt( - 1, {TabStripModel::GestureType::kOther}); + 1, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); { SCOPED_TRACE(""); UkmMetricMap expected_metrics = kBasicMetricValues;
diff --git a/chrome/browser/resource_coordinator/tab_lifecycle_unit_source_unittest.cc b/chrome/browser/resource_coordinator/tab_lifecycle_unit_source_unittest.cc index 7737e1d..9d0d2f6 100644 --- a/chrome/browser/resource_coordinator/tab_lifecycle_unit_source_unittest.cc +++ b/chrome/browser/resource_coordinator/tab_lifecycle_unit_source_unittest.cc
@@ -359,7 +359,9 @@ // Focus the tab. Expect the state to be ACTIVE. EXPECT_CALL(tab_observer_, OnDiscardedStateChange(::testing::_, reason, false)); - tab_strip_model_->ActivateTabAt(0, {TabStripModel::GestureType::kOther}); + tab_strip_model_->ActivateTabAt( + 0, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); ::testing::Mock::VerifyAndClear(&tab_observer_); EXPECT_EQ(LifecycleUnitState::ACTIVE, background_lifecycle_unit->GetState()); @@ -446,7 +448,9 @@ // Activate the first tab. task_environment()->FastForwardBy(kShortDelay); auto time_before_activate = NowTicks(); - tab_strip_model_->ActivateTabAt(0, {TabStripModel::GestureType::kOther}); + tab_strip_model_->ActivateTabAt( + 0, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); EXPECT_TRUE(IsFocused(first_lifecycle_unit)); EXPECT_EQ(time_before_activate, second_lifecycle_unit->GetLastFocusedTime()); @@ -517,7 +521,7 @@ // were destroyed from TabStripModelObserver::TabClosingAt(). If a tab was // detached (TabStripModel::DetachWebContentsAt) and its WebContents destroyed, // the TabLifecycleUnit was never destroyed. This was solved by giving ownership -// of a TabLifecycleUnit to a WebContentsUserData. +// of a tab lifecycleunit to a WebContentsUserData. TEST_F(TabLifecycleUnitSourceTest, DetachAndDeleteWebContents) { LifecycleUnit* first_lifecycle_unit = nullptr; LifecycleUnit* second_lifecycle_unit = nullptr;
diff --git a/chrome/browser/resource_coordinator/tab_manager_browsertest.cc b/chrome/browser/resource_coordinator/tab_manager_browsertest.cc index a26df4a..241d12b3 100644 --- a/chrome/browser/resource_coordinator/tab_manager_browsertest.cc +++ b/chrome/browser/resource_coordinator/tab_manager_browsertest.cc
@@ -291,7 +291,8 @@ EXPECT_FALSE(IsTabDiscarded(GetWebContentsAt(2))); // Kill the third tab after making second tab active. - tsm()->ActivateTabAt(1, {TabStripModel::GestureType::kOther}); + tsm()->ActivateTabAt(1, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); // Advance time so everything is urgent discardable again. test_clock_.Advance(kBackgroundUrgentProtectionTime); @@ -443,7 +444,8 @@ EXPECT_TRUE(tab_manager->DiscardTabImpl(LifecycleUnitDiscardReason::URGENT)); // Activate the 2nd tab. - tsm->ActivateTabAt(1, {TabStripModel::GestureType::kOther}); + tsm->ActivateTabAt(1, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); EXPECT_EQ(1, tsm->active_index()); // Advance the clock for less than the protection time.
diff --git a/chrome/browser/resource_coordinator/tab_metrics_logger_interactive_uitest.cc b/chrome/browser/resource_coordinator/tab_metrics_logger_interactive_uitest.cc index a5b5d625..165bd6a 100644 --- a/chrome/browser/resource_coordinator/tab_metrics_logger_interactive_uitest.cc +++ b/chrome/browser/resource_coordinator/tab_metrics_logger_interactive_uitest.cc
@@ -120,13 +120,15 @@ DiscardTabAt(0); EXPECT_EQ(CurrentTabFeatures(0).discard_count, 1); browser()->tab_strip_model()->ActivateTabAt( - 0, {TabStripModel::GestureType::kOther}); + 0, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); EXPECT_EQ(CurrentTabFeatures(0).discard_count, 1); DiscardTabAt(1); EXPECT_EQ(CurrentTabFeatures(1).discard_count, 1); browser()->tab_strip_model()->ActivateTabAt( - 1, {TabStripModel::GestureType::kOther}); + 1, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); EXPECT_EQ(CurrentTabFeatures(1).discard_count, 1); DiscardTabAt(0);
diff --git a/chrome/browser/resources/bluetooth_internals/BUILD.gn b/chrome/browser/resources/bluetooth_internals/BUILD.gn index e51645a..8be2e22 100644 --- a/chrome/browser/resources/bluetooth_internals/BUILD.gn +++ b/chrome/browser/resources/bluetooth_internals/BUILD.gn
@@ -3,6 +3,7 @@ # found in the LICENSE file. import("//tools/grit/grit_rule.gni") +import("//tools/typescript/ts_library.gni") import("//ui/webui/resources/tools/generate_grd.gni") bluetooth_grd_prefix = "bluetooth_internals" @@ -38,13 +39,36 @@ deps = [ ":build_internal_mojo_grdp", ":build_public_mojo_grdp", + ":build_ts", ] input_files = [ + "bluetooth_internals.css", + "bluetooth_internals.html", + "menu.svg", + ] + input_files_base_dir = rebase_path(".", "//") + + grdp_files = [ + public_mojo_grdp_file, + internals_mojo_grdp_file, + ] + + manifest_files = [ "$target_gen_dir/tsconfig.manifest" ] +} + +# TODO(crbug.com/1337318): This page should be converted to TypeScript but this +# will be a lot of work. Passing the JavaScript files through the TypeScript +# compiler will provide basic static checks (e.g. syntax) without validating +# types. +ts_library("build_ts") { + root_dir = "." + out_dir = "$target_gen_dir/tsc" + tsconfig_base = "tsconfig_base.json" + in_files = [ "adapter_broker.js", "adapter_page.js", "debug_log_page.js", "characteristic_list.js", - "bluetooth_internals.css", "descriptor_list.js", "device_broker.js", "device_collection.js", @@ -53,9 +77,7 @@ "device_utils.js", "devices_page.js", "expandable_list.js", - "bluetooth_internals.html", "bluetooth_internals.js", - "menu.svg", "main.js", "object_fieldset.js", "page_manager.js", @@ -65,12 +87,7 @@ "snackbar.js", "value_control.js", ] - input_files_base_dir = rebase_path("./", "//") - - grdp_files = [ - public_mojo_grdp_file, - internals_mojo_grdp_file, - ] + deps = [ "//ui/webui/resources:library" ] } grit("resources") {
diff --git a/chrome/browser/resources/bluetooth_internals/tsconfig_base.json b/chrome/browser/resources/bluetooth_internals/tsconfig_base.json new file mode 100644 index 0000000..99a81eca --- /dev/null +++ b/chrome/browser/resources/bluetooth_internals/tsconfig_base.json
@@ -0,0 +1,6 @@ +{ + "extends": "../../../../tools/typescript/tsconfig_base.json", + "compilerOptions": { + "allowJs": true + } +}
diff --git a/chrome/browser/resources/chromeos/audio/input_page.ts b/chrome/browser/resources/chromeos/audio/input_page.ts index 3bc89af..c64d349 100644 --- a/chrome/browser/resources/chromeos/audio/input_page.ts +++ b/chrome/browser/resources/chromeos/audio/input_page.ts
@@ -141,7 +141,7 @@ } record(source: MediaStream) { - let chunks = new Array<Blob>(); + let chunks: Blob[] = []; const recordButton = $('record-btn'); const clipSection = $('audio-file'); this.mediaRecorder = new MediaRecorder(source); @@ -164,7 +164,7 @@ audio.controls = true; const blob = new Blob(chunks, {'type': 'audio/ogg; codecs=opus'}); - chunks = new Array<Blob>(); + chunks = []; const audioURL = window.URL.createObjectURL(blob); audio.src = audioURL; this.testInputFeedback.set('audioUrl', audioURL);
diff --git a/chrome/browser/resources/settings/chromeos/crostini_page/crostini_port_forwarding.js b/chrome/browser/resources/settings/chromeos/crostini_page/crostini_port_forwarding.js index 812f7f1..d222cbd 100644 --- a/chrome/browser/resources/settings/chromeos/crostini_page/crostini_port_forwarding.js +++ b/chrome/browser/resources/settings/chromeos/crostini_page/crostini_port_forwarding.js
@@ -100,7 +100,7 @@ * List of ports are currently being forwarded. * @private {!Array<?CrostiniPortActiveSetting>} */ - this.activePorts_ = new Array(); + this.activePorts_ = []; /** * Tracks the last port that was selected for removal.
diff --git a/chrome/browser/resources/settings/chromeos/device_page/display.js b/chrome/browser/resources/settings/chromeos/device_page/display.js index 14efcb8..32171423 100644 --- a/chrome/browser/resources/settings/chromeos/device_page/display.js +++ b/chrome/browser/resources/settings/chromeos/device_page/display.js
@@ -641,7 +641,7 @@ // Clear the mappings before recalculating. this.modeToParentModeMap_ = new Map(); this.parentModeToRefreshRateMap_ = new Map(); - this.displayModeList_ = new Array(); + this.displayModeList_ = []; // Build the modes into a nested map of width => height => refresh rate. const modes = this.createModeMap_(selectedDisplay); @@ -715,7 +715,7 @@ // Add an entry in the outer map for |parentModeIndex|. The inner // array (the value at |parentModeIndex|) will be populated with all // possible refresh rates for the given resolution. - this.parentModeToRefreshRateMap_.set(parentModeIndex, new Array()); + this.parentModeToRefreshRateMap_.set(parentModeIndex, []); const resolutionOption = this.i18n('displayResolutionOnlyMenuItem', width, height);
diff --git a/chrome/browser/safe_browsing/safe_browsing_blocking_page_test.cc b/chrome/browser/safe_browsing/safe_browsing_blocking_page_test.cc index 13710c8..c4c26eed 100644 --- a/chrome/browser/safe_browsing/safe_browsing_blocking_page_test.cc +++ b/chrome/browser/safe_browsing/safe_browsing_blocking_page_test.cc
@@ -1311,7 +1311,8 @@ // Interstitial still displays in the background tab. browser()->tab_strip_model()->ActivateTabAt( - 0, {TabStripModel::GestureType::kOther}); + 0, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); EXPECT_EQ(0, browser()->tab_strip_model()->active_index()); EXPECT_EQ(interstitial_tab, browser()->tab_strip_model()->GetActiveWebContents()); @@ -2766,7 +2767,8 @@ // Interstitial should still display in the background tab. browser()->tab_strip_model()->ActivateTabAt( - 0, {TabStripModel::GestureType::kOther}); + 0, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); EXPECT_EQ(0, browser()->tab_strip_model()->active_index()); EXPECT_EQ(interstitial_tab, browser()->tab_strip_model()->GetActiveWebContents());
diff --git a/chrome/browser/sessions/session_restore.cc b/chrome/browser/sessions/session_restore.cc index 2c68856..d463d8c2 100644 --- a/chrome/browser/sessions/session_restore.cc +++ b/chrome/browser/sessions/session_restore.cc
@@ -69,6 +69,7 @@ #include "chrome/browser/ui/tabs/tab_group.h" #include "chrome/browser/ui/tabs/tab_group_model.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" +#include "chrome/browser/ui/tabs/tab_strip_user_gesture_details.h" #include "chrome/browser/ui/ui_features.h" #include "chrome/browser/ui/webui/whats_new/whats_new_util.h" #include "chrome/browser/web_applications/web_app_provider.h" @@ -293,7 +294,8 @@ if (use_new_window) { browser->tab_strip_model()->ActivateTabAt( - 0, {TabStripModel::GestureType::kOther}); + 0, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); browser->window()->Show(); } NotifySessionServiceOfRestoredTabs(browser, @@ -871,7 +873,9 @@ DCHECK(browser); DCHECK(browser->tab_strip_model()->count()); browser->tab_strip_model()->ActivateTabAt( - selected_tab_index, {TabStripModel::GestureType::kOther}); + selected_tab_index, + TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); if (browser_ == browser) return;
diff --git a/chrome/browser/sessions/session_restore_browsertest.cc b/chrome/browser/sessions/session_restore_browsertest.cc index 01392fd..e758061 100644 --- a/chrome/browser/sessions/session_restore_browsertest.cc +++ b/chrome/browser/sessions/session_restore_browsertest.cc
@@ -1832,7 +1832,8 @@ ASSERT_EQ(1, browser()->tab_strip_model()->active_index()); // Select the pinned tab. browser()->tab_strip_model()->ActivateTabAt( - 0, {TabStripModel::GestureType::kOther}); + 0, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); ASSERT_EQ(0, browser()->tab_strip_model()->active_index()); Profile* profile = browser()->profile(); @@ -2255,7 +2256,8 @@ // Activate the tabs one by one following the specified activation order. for (int i : activation_order) browser()->tab_strip_model()->ActivateTabAt( - i, {TabStripModel::GestureType::kOther}); + i, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); // Close the browser. auto keep_alive = std::make_unique<ScopedKeepAlive>( @@ -2290,7 +2292,8 @@ // Activate the 2nd tab before the browser closes. This should be persisted in // the following test. new_browser->tab_strip_model()->ActivateTabAt( - 1, {TabStripModel::GestureType::kOther}); + 1, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); } IN_PROC_BROWSER_TEST_F(SmartSessionRestoreTest, MAYBE_CorrectLoadingOrder) {
diff --git a/chrome/browser/signin/signin_ui_util_unittest.cc b/chrome/browser/signin/signin_ui_util_unittest.cc index 6485ccf..ceb13a5 100644 --- a/chrome/browser/signin/signin_ui_util_unittest.cc +++ b/chrome/browser/signin/signin_ui_util_unittest.cc
@@ -422,7 +422,9 @@ ASSERT_EQ(0, tab_strip->active_index()); GURL other_url = GURL("http://example.com"); AddTab(browser(), other_url); - tab_strip->ActivateTabAt(0, {TabStripModel::GestureType::kOther}); + tab_strip->ActivateTabAt( + 0, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); ASSERT_EQ(other_url, tab_strip->GetActiveWebContents()->GetVisibleURL()); ASSERT_EQ(0, tab_strip->active_index());
diff --git a/chrome/browser/site_isolation/site_details_browsertest.cc b/chrome/browser/site_isolation/site_details_browsertest.cc index 48e9303..0386584 100644 --- a/chrome/browser/site_isolation/site_details_browsertest.cc +++ b/chrome/browser/site_isolation/site_details_browsertest.cc
@@ -565,7 +565,8 @@ // be three processes estimated by IsolateExtensions: one for extension3, one // for extension1's background page, and one for the web iframe in tab2. browser()->tab_strip_model()->ActivateTabAt( - 0, {TabStripModel::GestureType::kOther}); + 0, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); ASSERT_TRUE(ui_test_utils::NavigateToURL( browser(), extension3->GetResourceURL("blank_iframe.html"))); details = new TestMemoryDetails();
diff --git a/chrome/browser/ssl/ssl_browsertest.cc b/chrome/browser/ssl/ssl_browsertest.cc index d18eca4..cffe191 100644 --- a/chrome/browser/ssl/ssl_browsertest.cc +++ b/chrome/browser/ssl/ssl_browsertest.cc
@@ -4239,7 +4239,8 @@ EXPECT_FALSE(tab->GetRenderWidgetHostView()->IsShowing()); browser()->tab_strip_model()->ActivateTabAt( - 1, {TabStripModel::GestureType::kOther}); + 1, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); EXPECT_TRUE(tab->GetRenderWidgetHostView()->IsShowing()); }
diff --git a/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/state/CouponPersistedTabData.java b/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/state/CouponPersistedTabData.java index ee03c63a..6729f53 100644 --- a/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/state/CouponPersistedTabData.java +++ b/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/state/CouponPersistedTabData.java
@@ -7,11 +7,16 @@ import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; +import com.google.protobuf.InvalidProtocolBufferException; + import org.chromium.base.Callback; +import org.chromium.base.Log; import org.chromium.base.supplier.Supplier; import org.chromium.chrome.browser.tab.Tab; +import org.chromium.chrome.browser.tab.proto.CouponPersistedTabData.CouponPersistedTabDataProto; import java.nio.ByteBuffer; +import java.util.Locale; /** * {@link PersistedTabData} for Shopping websites with coupons. @@ -74,14 +79,42 @@ return mCoupon; } - // TODO(crbug.com/1337470): Implement deserialize & serialize methods. @Override Supplier<ByteBuffer> getSerializeSupplier() { - return () -> null; + CouponPersistedTabDataProto.Builder builder = CouponPersistedTabDataProto.newBuilder(); + if (mCoupon != null) { + if (mCoupon.promoCode != null) { + builder.setCode(mCoupon.promoCode); + } + + if (mCoupon.couponName != null) { + builder.setName(mCoupon.couponName); + } + } + return () -> { + return builder.build().toByteString().asReadOnlyByteBuffer(); + }; } @Override boolean deserialize(@Nullable ByteBuffer bytes) { + // Do not attempt to deserialize if the bytes are null + if (bytes == null || !bytes.hasRemaining()) { + return false; + } + try { + CouponPersistedTabDataProto couponPersistedTabDataProto = + CouponPersistedTabDataProto.parseFrom(bytes); + mCoupon = new Coupon( + couponPersistedTabDataProto.getName(), couponPersistedTabDataProto.getCode()); + return true; + } catch (InvalidProtocolBufferException e) { + Log.e(TAG, + String.format(Locale.US, + "There was a problem deserializing " + + "CouponPersistedTabData. Details: %s", + e.getMessage())); + } return false; }
diff --git a/chrome/browser/tab_contents/view_source_browsertest.cc b/chrome/browser/tab_contents/view_source_browsertest.cc index 480a20d..7398f17 100644 --- a/chrome/browser/tab_contents/view_source_browsertest.cc +++ b/chrome/browser/tab_contents/view_source_browsertest.cc
@@ -215,7 +215,8 @@ // Switch back to the first tab and navigate it cross-process. browser()->tab_strip_model()->ActivateTabAt( - 0, {TabStripModel::GestureType::kOther}); + 0, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GURL(chrome::kChromeUIVersionURL))); EXPECT_TRUE(chrome::CanViewSource(browser()));
diff --git a/chrome/browser/translate/translate_manager_browsertest.cc b/chrome/browser/translate/translate_manager_browsertest.cc index b59edc5..237a8ae 100644 --- a/chrome/browser/translate/translate_manager_browsertest.cc +++ b/chrome/browser/translate/translate_manager_browsertest.cc
@@ -1293,7 +1293,8 @@ // Make restored tab active to (on some platforms) initiate language // detection. browser()->tab_strip_model()->ActivateTabAt( - 0, {TabStripModel::GestureType::kOther}); + 0, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); content::WebContents* restored_web_contents = browser()->tab_strip_model()->GetWebContentsAt(0); @@ -2066,7 +2067,8 @@ // Make restored tab active to (on some platforms) initiate language // detection. browser()->tab_strip_model()->ActivateTabAt( - 0, {TabStripModel::GestureType::kOther}); + 0, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); content::WebContents* restored_web_contents = browser()->tab_strip_model()->GetWebContentsAt(0);
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn index 64f9d624..2291572 100644 --- a/chrome/browser/ui/BUILD.gn +++ b/chrome/browser/ui/BUILD.gn
@@ -1372,6 +1372,7 @@ "tabs/tab_strip_model_observer.h", "tabs/tab_strip_model_stats_recorder.cc", "tabs/tab_strip_model_stats_recorder.h", + "tabs/tab_strip_user_gesture_details.h", "tabs/tab_style.cc", "tabs/tab_style.h", "tabs/tab_switch_event_latency_recorder.cc",
diff --git a/chrome/browser/ui/android/fast_checkout/BUILD.gn b/chrome/browser/ui/android/fast_checkout/BUILD.gn index 7692b2d..7c4cda01 100644 --- a/chrome/browser/ui/android/fast_checkout/BUILD.gn +++ b/chrome/browser/ui/android/fast_checkout/BUILD.gn
@@ -13,3 +13,7 @@ ] resources_package = "org.chromium.chrome.browser.ui.fast_checkout" } + +generate_jni("jni_headers") { + sources = [ "internal/java/src/org/chromium/chrome/browser/ui/fast_checkout/FastCheckoutBridge.java" ] +}
diff --git a/chrome/browser/ui/android/fast_checkout/internal/BUILD.gn b/chrome/browser/ui/android/fast_checkout/internal/BUILD.gn index 0e05feef..f93fc74 100644 --- a/chrome/browser/ui/android/fast_checkout/internal/BUILD.gn +++ b/chrome/browser/ui/android/fast_checkout/internal/BUILD.gn
@@ -5,11 +5,18 @@ import("//build/config/android/rules.gni") android_library("java") { + sources = [ + "java/src/org/chromium/chrome/browser/ui/fast_checkout/FastCheckoutBridge.java", + "java/src/org/chromium/chrome/browser/ui/fast_checkout/FastCheckoutCoordinator.java", + ] deps = [ "//base:base_java", + "//base:jni_java", "//build/android:build_java", "//chrome/browser/ui/android/fast_checkout:java", "//components/browser_ui/bottomsheet/android:java", + "//third_party/androidx:androidx_annotation_annotation_java", + "//ui/android:ui_no_recycler_view_java", ] - sources = [ "java/src/org/chromium/chrome/browser/ui/fast_checkout/FastCheckoutCoordinator.java" ] + annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ] }
diff --git a/chrome/browser/ui/android/fast_checkout/internal/java/src/org/chromium/chrome/browser/ui/fast_checkout/FastCheckoutBridge.java b/chrome/browser/ui/android/fast_checkout/internal/java/src/org/chromium/chrome/browser/ui/fast_checkout/FastCheckoutBridge.java new file mode 100644 index 0000000..e8bd060 --- /dev/null +++ b/chrome/browser/ui/android/fast_checkout/internal/java/src/org/chromium/chrome/browser/ui/fast_checkout/FastCheckoutBridge.java
@@ -0,0 +1,84 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chrome.browser.ui.fast_checkout; + +import androidx.annotation.Nullable; + +import org.chromium.base.annotations.CalledByNative; +import org.chromium.chrome.browser.ui.fast_checkout.data.FastCheckoutAutofillProfile; +import org.chromium.chrome.browser.ui.fast_checkout.data.FastCheckoutCreditCard; +import org.chromium.components.browser_ui.bottomsheet.BottomSheetController; +import org.chromium.components.browser_ui.bottomsheet.BottomSheetControllerProvider; +import org.chromium.ui.base.WindowAndroid; + +/** + * This bridge creates and initializes a {@link FastCheckoutComponent} on construction and forwards + * native calls to it. + */ +class FastCheckoutBridge implements FastCheckoutComponent.Delegate { + private long mNativeFastCheckoutBridge; + private final FastCheckoutComponent mFastCheckoutComponent; + + private FastCheckoutBridge(long nativeBridge, WindowAndroid windowAndroid, + BottomSheetController bottomSheetController) { + mNativeFastCheckoutBridge = nativeBridge; + mFastCheckoutComponent = new FastCheckoutCoordinator(); + mFastCheckoutComponent.initialize( + windowAndroid.getContext().get(), bottomSheetController, this); + } + + @CalledByNative + private static @Nullable FastCheckoutBridge create( + long nativeBridge, WindowAndroid windowAndroid) { + BottomSheetController bottomSheetController = + BottomSheetControllerProvider.from(windowAndroid); + if (bottomSheetController == null) return null; + return new FastCheckoutBridge(nativeBridge, windowAndroid, bottomSheetController); + } + + @CalledByNative + private void showBottomSheet( + FastCheckoutAutofillProfile[] profiles, FastCheckoutCreditCard[] creditCards) { + mFastCheckoutComponent.showOptions(profiles, creditCards); + } + + @CalledByNative + private static void setAutofillProfile(FastCheckoutAutofillProfile[] profiles, int index, + FastCheckoutAutofillProfile profile) { + profiles[index] = profile; + } + + @CalledByNative + private static void setCreditCard( + FastCheckoutCreditCard[] creditCards, int index, FastCheckoutCreditCard creditCard) { + creditCards[index] = creditCard; + } + + @CalledByNative + private static FastCheckoutAutofillProfile[] createAutofillProfilesArray(int size) { + return new FastCheckoutAutofillProfile[size]; + } + + @CalledByNative + private static FastCheckoutCreditCard[] createCreditCardsArray(int size) { + return new FastCheckoutCreditCard[size]; + } + + @CalledByNative + private void destroy() { + mNativeFastCheckoutBridge = 0; + } + + @Override + public void onDismissed() { + // TODO(crbug.com/1334642): Call native side to continue dismissing. + } + + @Override + public void onOptionsSelected( + FastCheckoutAutofillProfile profile, FastCheckoutCreditCard creditCard) { + // TODO(crbug.com/1334642): Call native side to continue filling. + } +}
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/geo/GeolocationHeader.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/geo/GeolocationHeader.java index 0adc1992..9f6a7f8 100644 --- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/geo/GeolocationHeader.java +++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/geo/GeolocationHeader.java
@@ -276,12 +276,14 @@ if (!hasGeolocationPermission()) { if (recordUma) recordHistogram(UMA_LOCATION_DISABLED_FOR_CHROME_APP); + Log.i(TAG, "[crbug/1338183] App permission is missing"); return HeaderState.LOCATION_PERMISSION_BLOCKED; } // Only send X-Geo header if the user hasn't disabled geolocation for url. if (isLocationDisabledForUrl(profile, uri)) { if (recordUma) recordHistogram(UMA_LOCATION_DISABLED_FOR_GOOGLE_DOMAIN); + Log.i(TAG, "[crbug/1338183] Site permission is missing"); return HeaderState.LOCATION_PERMISSION_BLOCKED; } @@ -305,11 +307,7 @@ @Nullable public static String getGeoHeader(String url, Tab tab) { Profile profile = Profile.fromWebContents(tab.getWebContents()); - Log.i(TAG, "[getGeoHeader] getGeoHeader for url " + url); - if (profile == null) { - Log.i(TAG, "[getGeoHeader] getGeoHeader failed because of a null profile"); - return null; - } + if (profile == null) return null; return getGeoHeader(url, profile, tab); } @@ -360,7 +358,7 @@ long locationAge = Long.MAX_VALUE; @HeaderState int headerState = geoHeaderStateForUrl(profile, url, true); - Log.i(TAG, "[getGeoHeader] headerState: " + headerState); + Log.i(TAG, "[crbug/1338183] headerState: " + headerState); if (headerState == HeaderState.HEADER_ENABLED) { locationToAttach = GeolocationTracker.getLastKnownLocation( ContextUtils.getApplicationContext()); @@ -413,10 +411,6 @@ String visibleNetworksProtoEncoding = encodeProtoVisibleNetworks(visibleNetworksToAttach); - Log.i(TAG, "[getGeoHeader] locationProtoEncoding: " + locationProtoEncoding); - Log.i(TAG, - "[getGeoHeader] visibleNetworksProtoEncoding: " + visibleNetworksProtoEncoding); - if (locationProtoEncoding == null && visibleNetworksProtoEncoding == null) return null; StringBuilder header = new StringBuilder(XGEO_HEADER_PREFIX);
diff --git a/chrome/browser/ui/app_list/search/app_search_provider_unittest.cc b/chrome/browser/ui/app_list/search/app_search_provider_unittest.cc index 71f6554..993235f 100644 --- a/chrome/browser/ui/app_list/search/app_search_provider_unittest.cc +++ b/chrome/browser/ui/app_list/search/app_search_provider_unittest.cc
@@ -44,6 +44,7 @@ #include "chromeos/ash/components/dbus/cicerone/cicerone_client.h" #include "chromeos/ash/components/dbus/concierge/concierge_client.h" #include "chromeos/ash/components/dbus/seneschal/seneschal_client.h" +#include "chromeos/dbus/chunneld/chunneld_client.h" #include "chromeos/dbus/dbus_thread_manager.h" #include "components/crx_file/id_util.h" #include "components/services/app_service/public/cpp/app_types.h" @@ -643,6 +644,7 @@ public: void SetUp() override { chromeos::DBusThreadManager::Initialize(); + chromeos::ChunneldClient::InitializeFake(); ash::CiceroneClient::InitializeFake(); ash::ConciergeClient::InitializeFake(); ash::SeneschalClient::InitializeFake(); @@ -661,6 +663,7 @@ ash::SeneschalClient::Shutdown(); ash::ConciergeClient::Shutdown(); ash::CiceroneClient::Shutdown(); + chromeos::ChunneldClient::Shutdown(); chromeos::DBusThreadManager::Shutdown(); } };
diff --git a/chrome/browser/ui/ash/shelf/shelf_context_menu_unittest.cc b/chrome/browser/ui/ash/shelf/shelf_context_menu_unittest.cc index ac8fcea9..bad84465 100644 --- a/chrome/browser/ui/ash/shelf/shelf_context_menu_unittest.cc +++ b/chrome/browser/ui/ash/shelf/shelf_context_menu_unittest.cc
@@ -53,6 +53,7 @@ #include "chromeos/ash/components/dbus/cicerone/cicerone_client.h" #include "chromeos/ash/components/dbus/concierge/concierge_client.h" #include "chromeos/ash/components/dbus/seneschal/seneschal_client.h" +#include "chromeos/dbus/chunneld/chunneld_client.h" #include "chromeos/dbus/dbus_thread_manager.h" #include "components/exo/shell_surface_util.h" #include "components/prefs/pref_service.h" @@ -105,6 +106,7 @@ void SetUp() override { chromeos::DBusThreadManager::Initialize(); + chromeos::ChunneldClient::InitializeFake(); ash::CiceroneClient::InitializeFake(); ash::ConciergeClient::InitializeFake(); ash::SeneschalClient::InitializeFake(); @@ -210,6 +212,7 @@ ash::SeneschalClient::Shutdown(); ash::ConciergeClient::Shutdown(); ash::CiceroneClient::Shutdown(); + chromeos::ChunneldClient::Shutdown(); chromeos::DBusThreadManager::Shutdown(); }
diff --git a/chrome/browser/ui/ash/wallpaper_controller_client_impl.cc b/chrome/browser/ui/ash/wallpaper_controller_client_impl.cc index 299df9d..4347871 100644 --- a/chrome/browser/ui/ash/wallpaper_controller_client_impl.cc +++ b/chrome/browser/ui/ash/wallpaper_controller_client_impl.cc
@@ -47,7 +47,6 @@ #include "chrome/browser/ash/system_web_apps/types/system_web_app_type.h" #include "chrome/browser/ash/wallpaper_handlers/wallpaper_handlers.h" #include "chrome/browser/browser_process.h" -#include "chrome/browser/chromeos/extensions/wallpaper_private_api.h" #include "chrome/browser/profiles/profile_manager.h" #include "chrome/browser/signin/identity_manager_factory.h" #include "chrome/browser/sync/sync_service_factory.h" @@ -676,19 +675,6 @@ profile, ash::SystemWebAppType::PERSONALIZATION, params); } -void WallpaperControllerClientImpl::MaybeClosePreviewWallpaper() { - Profile* profile = ProfileManager::GetActiveUserProfile(); - DCHECK(profile); - - extensions::EventRouter* event_router = extensions::EventRouter::Get(profile); - - auto event = std::make_unique<extensions::Event>( - extensions::events::WALLPAPER_PRIVATE_ON_CLOSE_PREVIEW_WALLPAPER, - extensions::api::wallpaper_private::OnClosePreviewWallpaper::kEventName, - base::Value::List()); - event_router->DispatchEventToExtension(kWallpaperManagerId, std::move(event)); -} - void WallpaperControllerClientImpl::SetDefaultWallpaper( const AccountId& account_id, bool show_wallpaper,
diff --git a/chrome/browser/ui/ash/wallpaper_controller_client_impl.h b/chrome/browser/ui/ash/wallpaper_controller_client_impl.h index 651738c..efd9a7c 100644 --- a/chrome/browser/ui/ash/wallpaper_controller_client_impl.h +++ b/chrome/browser/ui/ash/wallpaper_controller_client_impl.h
@@ -66,7 +66,6 @@ // ash::WallpaperControllerClient: void OpenWallpaperPicker() override; - void MaybeClosePreviewWallpaper() override; void SetDefaultWallpaper( const AccountId& account_id, bool show_wallpaper,
diff --git a/chrome/browser/ui/browser_command_controller.cc b/chrome/browser/ui/browser_command_controller.cc index c582ceb..4013ab3 100644 --- a/chrome/browser/ui/browser_command_controller.cc +++ b/chrome/browser/ui/browser_command_controller.cc
@@ -42,6 +42,7 @@ #include "chrome/browser/ui/page_info/page_info_dialog.h" #include "chrome/browser/ui/singleton_tabs.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" +#include "chrome/browser/ui/tabs/tab_strip_user_gesture_details.h" #include "chrome/browser/ui/ui_features.h" #include "chrome/browser/ui/web_applications/app_browser_controller.h" #include "chrome/browser/ui/web_applications/web_app_dialog_utils.h" @@ -490,13 +491,17 @@ break; case IDC_SELECT_NEXT_TAB: base::RecordAction(base::UserMetricsAction("Accel_SelectNextTab")); - SelectNextTab(browser_, - {TabStripModel::GestureType::kKeyboard, time_stamp}); + SelectNextTab( + browser_, + TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kKeyboard, time_stamp)); break; case IDC_SELECT_PREVIOUS_TAB: base::RecordAction(base::UserMetricsAction("Accel_SelectPreviousTab")); - SelectPreviousTab(browser_, - {TabStripModel::GestureType::kKeyboard, time_stamp}); + SelectPreviousTab( + browser_, + TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kKeyboard, time_stamp)); break; case IDC_MOVE_TAB_NEXT: MoveTabNext(browser_); @@ -513,13 +518,17 @@ case IDC_SELECT_TAB_6: case IDC_SELECT_TAB_7: base::RecordAction(base::UserMetricsAction("Accel_SelectNumberedTab")); - SelectNumberedTab(browser_, id - IDC_SELECT_TAB_0, - {TabStripModel::GestureType::kKeyboard, time_stamp}); + SelectNumberedTab( + browser_, id - IDC_SELECT_TAB_0, + TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kKeyboard, time_stamp)); break; case IDC_SELECT_LAST_TAB: base::RecordAction(base::UserMetricsAction("Accel_SelectNumberedTab")); - SelectLastTab(browser_, - {TabStripModel::GestureType::kKeyboard, time_stamp}); + SelectLastTab( + browser_, + TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kKeyboard, time_stamp)); break; case IDC_DUPLICATE_TAB: DuplicateTab(browser_);
diff --git a/chrome/browser/ui/browser_commands.cc b/chrome/browser/ui/browser_commands.cc index 1138415..1a1551c 100644 --- a/chrome/browser/ui/browser_commands.cc +++ b/chrome/browser/ui/browser_commands.cc
@@ -80,6 +80,7 @@ #include "chrome/browser/ui/tab_dialogs.h" #include "chrome/browser/ui/tabs/tab_group.h" #include "chrome/browser/ui/tabs/tab_group_model.h" +#include "chrome/browser/ui/tabs/tab_strip_user_gesture_details.h" #include "chrome/browser/ui/translate/translate_bubble_ui_action_logger.h" #include "chrome/browser/ui/ui_features.h" #include "chrome/browser/ui/user_education/reopen_tab_in_product_help.h" @@ -787,13 +788,13 @@ } void SelectNextTab(Browser* browser, - TabStripModel::UserGestureDetails gesture_detail) { + TabStripUserGestureDetails gesture_detail) { base::RecordAction(UserMetricsAction("SelectNextTab")); browser->tab_strip_model()->SelectNextTab(gesture_detail); } void SelectPreviousTab(Browser* browser, - TabStripModel::UserGestureDetails gesture_detail) { + TabStripUserGestureDetails gesture_detail) { base::RecordAction(UserMetricsAction("SelectPrevTab")); browser->tab_strip_model()->SelectPreviousTab(gesture_detail); } @@ -810,7 +811,7 @@ void SelectNumberedTab(Browser* browser, int index, - TabStripModel::UserGestureDetails gesture_detail) { + TabStripUserGestureDetails gesture_detail) { int visible_count = 0; for (int i = 0; i < browser->tab_strip_model()->count(); i++) { if (browser->tab_strip_model()->IsTabCollapsed(i)) { @@ -826,7 +827,7 @@ } void SelectLastTab(Browser* browser, - TabStripModel::UserGestureDetails gesture_detail) { + TabStripUserGestureDetails gesture_detail) { for (int i = browser->tab_strip_model()->count() - 1; i >= 0; i--) { if (!browser->tab_strip_model()->IsTabCollapsed(i)) { base::RecordAction(UserMetricsAction("SelectLastTab"));
diff --git a/chrome/browser/ui/browser_commands.h b/chrome/browser/ui/browser_commands.h index 65fc5a9..c16c8bee 100644 --- a/chrome/browser/ui/browser_commands.h +++ b/chrome/browser/ui/browser_commands.h
@@ -13,8 +13,8 @@ #include "chrome/browser/devtools/devtools_toggle_action.h" #include "chrome/browser/devtools/devtools_window.h" #include "chrome/browser/ui/chrome_pages.h" -#include "chrome/browser/ui/tabs/tab_strip_model.h" #include "chrome/browser/ui/tabs/tab_strip_model_delegate.h" +#include "chrome/browser/ui/tabs/tab_strip_user_gesture_details.h" #include "components/services/screen_ai/buildflags/buildflags.h" #include "content/public/common/page_zoom.h" #include "printing/buildflags/buildflags.h" @@ -97,23 +97,23 @@ void RestoreTab(Browser* browser); void SelectNextTab( Browser* browser, - TabStripModel::UserGestureDetails gesture_detail = - TabStripModel::UserGestureDetails(TabStripModel::GestureType::kOther)); + TabStripUserGestureDetails gesture_detail = TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); void SelectPreviousTab( Browser* browser, - TabStripModel::UserGestureDetails gesture_detail = - TabStripModel::UserGestureDetails(TabStripModel::GestureType::kOther)); + TabStripUserGestureDetails gesture_detail = TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); void MoveTabNext(Browser* browser); void MoveTabPrevious(Browser* browser); void SelectNumberedTab( Browser* browser, int index, - TabStripModel::UserGestureDetails gesture_detail = - TabStripModel::UserGestureDetails(TabStripModel::GestureType::kOther)); + TabStripUserGestureDetails gesture_detail = TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); void SelectLastTab( Browser* browser, - TabStripModel::UserGestureDetails gesture_detail = - TabStripModel::UserGestureDetails(TabStripModel::GestureType::kOther)); + TabStripUserGestureDetails gesture_detail = TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); void DuplicateTab(Browser* browser); bool CanDuplicateTab(const Browser* browser); bool CanDuplicateKeyboardFocusedTab(const Browser* browser);
diff --git a/chrome/browser/ui/browser_focus_uitest.cc b/chrome/browser/ui/browser_focus_uitest.cc index 9e1dec2..e0e8843 100644 --- a/chrome/browser/ui/browser_focus_uitest.cc +++ b/chrome/browser/ui/browser_focus_uitest.cc
@@ -260,7 +260,8 @@ for (int j = 0; j < 5; j++) { // Activate the tab. browser()->tab_strip_model()->ActivateTabAt( - j, {TabStripModel::GestureType::kOther}); + j, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); // Activate the location bar or the page. if (kFocusPage[i][j]) { @@ -274,14 +275,16 @@ for (int j = 0; j < 5; j++) { // Activate the tab. browser()->tab_strip_model()->ActivateTabAt( - j, {TabStripModel::GestureType::kOther}); + j, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); ViewID vid = kFocusPage[i][j] ? VIEW_ID_TAB_CONTAINER : VIEW_ID_OMNIBOX; ASSERT_TRUE(IsViewFocused(vid)); } browser()->tab_strip_model()->ActivateTabAt( - 0, {TabStripModel::GestureType::kOther}); + 0, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); // Try the above, but with ctrl+tab. Since tab normally changes focus, // this has regressed in the past. Loop through several times to be sure. for (int j = 0; j < 15; j++) { @@ -295,7 +298,8 @@ // As above, but with ctrl+shift+tab. browser()->tab_strip_model()->ActivateTabAt( - 4, {TabStripModel::GestureType::kOther}); + 4, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); for (int j = 14; j >= 0; --j) { ViewID vid = kFocusPage[i][j % 5] ? VIEW_ID_TAB_CONTAINER : VIEW_ID_OMNIBOX; @@ -331,7 +335,8 @@ // Select 1st tab, focus should still be on the location-bar. // (bug http://crbug.com/23296) browser()->tab_strip_model()->ActivateTabAt( - 0, {TabStripModel::GestureType::kOther}); + 0, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); ASSERT_TRUE(IsViewFocused(VIEW_ID_OMNIBOX)); // Now open the find box again, switch to another tab and come back, the focus @@ -339,10 +344,12 @@ chrome::Find(browser()); ASSERT_TRUE(IsViewFocused(VIEW_ID_FIND_IN_PAGE_TEXT_FIELD)); browser()->tab_strip_model()->ActivateTabAt( - 1, {TabStripModel::GestureType::kOther}); + 1, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); ASSERT_TRUE(IsViewFocused(VIEW_ID_TAB_CONTAINER)); browser()->tab_strip_model()->ActivateTabAt( - 0, {TabStripModel::GestureType::kOther}); + 0, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); ASSERT_TRUE(IsViewFocused(VIEW_ID_FIND_IN_PAGE_TEXT_FIELD)); }
diff --git a/chrome/browser/ui/browser_navigator.cc b/chrome/browser/ui/browser_navigator.cc index e4e4ef2..c92d6e5 100644 --- a/chrome/browser/ui/browser_navigator.cc +++ b/chrome/browser/ui/browser_navigator.cc
@@ -38,6 +38,7 @@ #include "chrome/browser/ui/status_bubble.h" #include "chrome/browser/ui/tab_helpers.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" +#include "chrome/browser/ui/tabs/tab_strip_user_gesture_details.h" #include "chrome/browser/ui/web_applications/app_browser_controller.h" #include "chrome/browser/ui/web_applications/system_web_app_ui_utils.h" #include "chrome/browser/web_applications/web_app_helpers.h" @@ -848,8 +849,9 @@ if (params->source_contents != contents_to_navigate_or_insert) { // Use the index before the potential close below, because it could // make the index refer to a different tab. - auto gesture_type = user_initiated ? TabStripModel::GestureType::kOther - : TabStripModel::GestureType::kNone; + auto gesture_type = user_initiated + ? TabStripUserGestureDetails::GestureType::kOther + : TabStripUserGestureDetails::GestureType::kNone; bool should_close_this_tab = false; if (params->disposition == WindowOpenDisposition::SWITCH_TO_TAB) { // Close orphaned NTP (and the like) with no history when the user @@ -867,8 +869,8 @@ } } } - params->browser->tab_strip_model()->ActivateTabAt(singleton_index, - {gesture_type}); + params->browser->tab_strip_model()->ActivateTabAt( + singleton_index, TabStripUserGestureDetails(gesture_type)); // Close tab after switch so index remains correct. if (should_close_this_tab) params->source_contents->Close();
diff --git a/chrome/browser/ui/browser_navigator_browsertest.cc b/chrome/browser/ui/browser_navigator_browsertest.cc index 3f8162c..c82e29e 100644 --- a/chrome/browser/ui/browser_navigator_browsertest.cc +++ b/chrome/browser/ui/browser_navigator_browsertest.cc
@@ -675,7 +675,8 @@ WebContents* new_tab = browser()->tab_strip_model()->GetActiveWebContents(); browser()->tab_strip_model()->ActivateTabAt( - 0, {TabStripModel::GestureType::kOther}); + 0, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); NavigateHelper(singleton_url, browser(), WindowOpenDisposition::SWITCH_TO_TAB, false, new_tab); @@ -748,7 +749,8 @@ false); browser()->tab_strip_model()->ActivateTabAt( - 1, {TabStripModel::GestureType::kOther}); + 1, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); OmniboxView* omnibox_view = location_bar->GetOmniboxView(); EXPECT_EQ(omnibox_view->model()->focus_state(), @@ -1617,7 +1619,8 @@ } browser()->tab_strip_model()->ActivateTabAt( - 0, {TabStripModel::GestureType::kOther}); + 0, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); { content::LoadStopObserver observer(
diff --git a/chrome/browser/ui/browser_unittest.cc b/chrome/browser/ui/browser_unittest.cc index b78565d..478ce10 100644 --- a/chrome/browser/ui/browser_unittest.cc +++ b/chrome/browser/ui/browser_unittest.cc
@@ -84,7 +84,9 @@ EXPECT_TRUE(raw_contents2->IsCrashed()); // Selecting the second tab does not cause a load or clear the crash. - tab_strip_model->ActivateTabAt(1, {TabStripModel::GestureType::kOther}); + tab_strip_model->ActivateTabAt( + 1, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); EXPECT_TRUE(tab_strip_model->IsTabSelected(1)); EXPECT_FALSE(raw_contents2->IsLoading()); EXPECT_TRUE(raw_contents2->IsCrashed()); @@ -116,7 +118,9 @@ WebContentsTester::For(raw_contents2)->NavigateAndCommit(GURL("about:blank")); WebContentsTester::For(raw_contents2)->TestSetIsLoading(false); - tab_strip_model->ActivateTabAt(1, {TabStripModel::GestureType::kOther}); + tab_strip_model->ActivateTabAt( + 1, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); ASSERT_TRUE( raw_contents2->GetPrimaryMainFrame()->GetView()->GetBackgroundColor()); EXPECT_EQ( @@ -381,7 +385,8 @@ // Activate the 2nd tab which is non-NTP. browser()->tab_strip_model()->ActivateTabAt( - 1, {TabStripModel::GestureType::kOther}); + 1, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); EXPECT_EQ(BookmarkBar::HIDDEN, browser()->bookmark_bar_state()); EXPECT_EQ(BookmarkBar::HIDDEN, window_bookmark_bar_state()); @@ -392,13 +397,15 @@ // Activate the 1st tab which is NTP. browser()->tab_strip_model()->ActivateTabAt( - 0, {TabStripModel::GestureType::kOther}); + 0, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); EXPECT_EQ(BookmarkBar::SHOW, browser()->bookmark_bar_state()); EXPECT_EQ(BookmarkBar::SHOW, window_bookmark_bar_state()); // Activate the 2nd tab which is non-NTP. browser()->tab_strip_model()->ActivateTabAt( - 1, {TabStripModel::GestureType::kOther}); + 1, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); EXPECT_EQ(BookmarkBar::SHOW, browser()->bookmark_bar_state()); EXPECT_EQ(BookmarkBar::SHOW, window_bookmark_bar_state()); }
diff --git a/chrome/browser/ui/cocoa/applescript/window_applescript.mm b/chrome/browser/ui/cocoa/applescript/window_applescript.mm index 169ac04..f9bc3b0 100644 --- a/chrome/browser/ui/cocoa/applescript/window_applescript.mm +++ b/chrome/browser/ui/cocoa/applescript/window_applescript.mm
@@ -3,6 +3,7 @@ // found in the LICENSE file. #import "chrome/browser/ui/cocoa/applescript/window_applescript.h" +#include "chrome/browser/ui/tabs/tab_strip_user_gesture_details.h" #include <memory> @@ -29,6 +30,7 @@ #include "chrome/browser/ui/exclusive_access/exclusive_access_manager.h" #include "chrome/browser/ui/tab_contents/core_tab_helper.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" +#include "chrome/browser/ui/tabs/tab_strip_user_gesture_details.h" #include "chrome/common/url_constants.h" #include "content/public/browser/web_contents.h" @@ -133,7 +135,8 @@ int atIndex = [anActiveTabIndex intValue] - 1; if (atIndex >= 0 && atIndex < _browser->tab_strip_model()->count()) { _browser->tab_strip_model()->ActivateTabAt( - atIndex, {TabStripModel::GestureType::kOther}); + atIndex, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); } else AppleScript::SetError(AppleScript::errInvalidTabIndex); }
diff --git a/chrome/browser/ui/cocoa/tab_menu_bridge.mm b/chrome/browser/ui/cocoa/tab_menu_bridge.mm index b347857..f011667b 100644 --- a/chrome/browser/ui/cocoa/tab_menu_bridge.mm +++ b/chrome/browser/ui/cocoa/tab_menu_bridge.mm
@@ -5,6 +5,7 @@ #include "chrome/browser/ui/cocoa/tab_menu_bridge.h" #import <Cocoa/Cocoa.h> +#include "chrome/browser/ui/tabs/tab_strip_user_gesture_details.h" #include "base/callback.h" #include "base/mac/scoped_nsobject.h" @@ -12,6 +13,7 @@ #include "chrome/browser/ui/recently_audible_helper.h" #include "chrome/browser/ui/tab_ui_helper.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" +#include "chrome/browser/ui/tabs/tab_strip_user_gesture_details.h" #include "chrome/grit/generated_resources.h" #include "ui/base/l10n/l10n_util.h" #include "ui/base/l10n/l10n_util_mac.h" @@ -153,8 +155,9 @@ DCHECK_EQ(item.target, menu_listener_.get()); int index = [menu_item_.submenu indexOfItem:item] - dynamic_items_start_; - model_->ActivateTabAt(index, TabStripModel::UserGestureDetails( - TabStripModel::GestureType::kTabMenu)); + model_->ActivateTabAt(index, + TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kTabMenu)); } void TabMenuBridge::OnTabStripModelChanged(
diff --git a/chrome/browser/ui/exclusive_access/fullscreen_controller_state_unittest.cc b/chrome/browser/ui/exclusive_access/fullscreen_controller_state_unittest.cc index c3d55af..5f204f76 100644 --- a/chrome/browser/ui/exclusive_access/fullscreen_controller_state_unittest.cc +++ b/chrome/browser/ui/exclusive_access/fullscreen_controller_state_unittest.cc
@@ -529,7 +529,8 @@ // Activate the first tab and tell its WebContents it is being captured. browser()->tab_strip_model()->ActivateTabAt( - 0, {TabStripModel::GestureType::kOther}); + 0, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); const gfx::Size kCaptureSize(1280, 720); auto capture_handle = first_tab->IncrementCapturerCount(kCaptureSize, /*stay_hidden=*/false, @@ -550,7 +551,8 @@ // Switch to the other tab. Check that the first tab was resized to the // WebContents' preferred size. browser()->tab_strip_model()->ActivateTabAt( - 1, {TabStripModel::GestureType::kOther}); + 1, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); EXPECT_FALSE(browser()->window()->IsFullscreen()); EXPECT_TRUE(wc_delegate->IsFullscreenForTabOrPending(first_tab)); EXPECT_FALSE(wc_delegate->IsFullscreenForTabOrPending(second_tab)); @@ -559,7 +561,8 @@ // Switch back to the first tab and exit fullscreen. browser()->tab_strip_model()->ActivateTabAt( - 0, {TabStripModel::GestureType::kOther}); + 0, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); EXPECT_FALSE(browser()->window()->IsFullscreen()); EXPECT_TRUE(wc_delegate->IsFullscreenForTabOrPending(first_tab)); EXPECT_FALSE(wc_delegate->IsFullscreenForTabOrPending(second_tab)); @@ -595,7 +598,8 @@ // and fullscreen that. The second tab will cause the browser window to // expand to fill the screen. browser()->tab_strip_model()->ActivateTabAt( - 0, {TabStripModel::GestureType::kOther}); + 0, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); const gfx::Size kCaptureSize(1280, 720); auto capture_handle = first_tab->IncrementCapturerCount(kCaptureSize, /*stay_hidden=*/false, @@ -606,7 +610,8 @@ EXPECT_FALSE(wc_delegate->IsFullscreenForTabOrPending(second_tab)); EXPECT_FALSE(GetFullscreenController()->IsWindowFullscreenForTabOrPending()); browser()->tab_strip_model()->ActivateTabAt( - 1, {TabStripModel::GestureType::kOther}); + 1, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); ASSERT_TRUE(InvokeEvent(TAB_FULLSCREEN_TRUE)); ASSERT_TRUE(InvokeEvent(WINDOW_CHANGE)); EXPECT_TRUE(browser()->window()->IsFullscreen()); @@ -625,7 +630,8 @@ // Finally, exit fullscreen on the captured tab. browser()->tab_strip_model()->ActivateTabAt( - 0, {TabStripModel::GestureType::kOther}); + 0, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); ASSERT_TRUE(InvokeEvent(TAB_FULLSCREEN_FALSE)); EXPECT_FALSE(browser()->window()->IsFullscreen()); EXPECT_FALSE(wc_delegate->IsFullscreenForTabOrPending(first_tab)); @@ -657,7 +663,8 @@ // and fullscreen that. The second tab will cause the browser window to // expand to fill the screen. browser()->tab_strip_model()->ActivateTabAt( - 0, {TabStripModel::GestureType::kOther}); + 0, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); const gfx::Size kCaptureSize(1280, 720); auto capture_handle = first_tab->IncrementCapturerCount(kCaptureSize, /*stay_hidden=*/false, @@ -668,7 +675,8 @@ EXPECT_FALSE(wc_delegate->IsFullscreenForTabOrPending(second_tab)); EXPECT_FALSE(GetFullscreenController()->IsWindowFullscreenForTabOrPending()); browser()->tab_strip_model()->ActivateTabAt( - 1, {TabStripModel::GestureType::kOther}); + 1, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); ASSERT_TRUE(InvokeEvent(TAB_FULLSCREEN_TRUE)); ASSERT_TRUE(InvokeEvent(WINDOW_CHANGE)); EXPECT_TRUE(browser()->window()->IsFullscreen()); @@ -712,7 +720,8 @@ // Start capturing the tab and fullscreen it. The state of the browser window // should remain unchanged. browser()->tab_strip_model()->ActivateTabAt( - 0, {TabStripModel::GestureType::kOther}); + 0, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); const gfx::Size kCaptureSize(1280, 720); auto capture_handle = tab->IncrementCapturerCount(kCaptureSize, /*stay_hidden=*/false, @@ -762,7 +771,8 @@ // Start capturing the tab with stay_hidden==true, and fullscreen it. // The the browser window should enter fullscreen. browser()->tab_strip_model()->ActivateTabAt( - 0, {TabStripModel::GestureType::kOther}); + 0, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); auto capture_handle = tab->IncrementCapturerCount(gfx::Size(), /*stay_hidden=*/true, /*stay_awake=*/true); @@ -847,7 +857,8 @@ // Activate the first tab and tell its WebContents it is being captured. browser()->tab_strip_model()->ActivateTabAt( - 0, {TabStripModel::GestureType::kOther}); + 0, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); const gfx::Size kCaptureSize(1280, 720); auto capture_handle = tab->IncrementCapturerCount(kCaptureSize, /*stay_hidden=*/false,
diff --git a/chrome/browser/ui/extensions/application_launch.cc b/chrome/browser/ui/extensions/application_launch.cc index 4d75ae3..6d03b6b 100644 --- a/chrome/browser/ui/extensions/application_launch.cc +++ b/chrome/browser/ui/extensions/application_launch.cc
@@ -36,6 +36,7 @@ #include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/extensions/extension_enable_flow.h" #include "chrome/browser/ui/extensions/extension_enable_flow_delegate.h" +#include "chrome/browser/ui/tabs/tab_strip_user_gesture_details.h" #include "chrome/browser/ui/web_applications/web_app_launch_utils.h" #include "chrome/browser/web_applications/web_app_helpers.h" #include "chrome/common/extensions/manifest_handlers/app_launch_info.h" @@ -251,7 +252,9 @@ tab_index = model->GetIndexOfWebContents(existing_tab); } if (params.tabstrip_add_types & TabStripModel::ADD_ACTIVE) { - model->ActivateTabAt(tab_index, {TabStripModel::GestureType::kOther}); + model->ActivateTabAt( + tab_index, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); } contents = existing_tab;
diff --git a/chrome/browser/ui/omnibox/omnibox_view_browsertest.cc b/chrome/browser/ui/omnibox/omnibox_view_browsertest.cc index 7341f30..c65e5c46 100644 --- a/chrome/browser/ui/omnibox/omnibox_view_browsertest.cc +++ b/chrome/browser/ui/omnibox/omnibox_view_browsertest.cc
@@ -615,7 +615,8 @@ // Switch back to the first tab. The user text should be cleared, and the // omnibox should have the new URL. browser()->tab_strip_model()->ActivateTabAt( - 0, {TabStripModel::GestureType::kOther}); + 0, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); EXPECT_EQ(ASCIIToUTF16(url2.spec()), omnibox_view->GetText()); } @@ -1083,7 +1084,8 @@ // Switch back to the first tab. browser()->tab_strip_model()->ActivateTabAt( - 0, {TabStripModel::GestureType::kOther}); + 0, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); // Make sure we're still in keyword mode. ASSERT_TRUE(omnibox_view->model()->is_keyword_selected()); @@ -1095,9 +1097,11 @@ // Switch to the second tab and back to the first. browser()->tab_strip_model()->ActivateTabAt( - 1, {TabStripModel::GestureType::kOther}); + 1, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); browser()->tab_strip_model()->ActivateTabAt( - 0, {TabStripModel::GestureType::kOther}); + 0, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); // Make sure we're still in keyword mode. ASSERT_TRUE(omnibox_view->model()->is_keyword_selected());
diff --git a/chrome/browser/ui/signin_view_controller.cc b/chrome/browser/ui/signin_view_controller.cc index 6e0a7ee..5183ad8 100644 --- a/chrome/browser/ui/signin_view_controller.cc +++ b/chrome/browser/ui/signin_view_controller.cc
@@ -20,6 +20,7 @@ #include "chrome/browser/ui/signin_modal_dialog.h" #include "chrome/browser/ui/signin_modal_dialog_impl.h" #include "chrome/browser/ui/signin_view_controller_delegate.h" +#include "chrome/browser/ui/tabs/tab_strip_user_gesture_details.h" #include "chrome/browser/ui/webui/signin/signin_utils.h" #include "components/signin/public/base/consent_level.h" #include "components/signin/public/base/signin_buildflags.h" @@ -372,8 +373,10 @@ signin_metrics::AccessPoint::ACCESS_POINT_EXTENSIONS) { // Extensions do not activate the tab to prevent misbehaving // extensions to keep focusing the signin tab. - tab_strip->ActivateTabAt(dice_tab_index, - {TabStripModel::GestureType::kOther}); + tab_strip->ActivateTabAt( + dice_tab_index, + TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); } // Do not create a new signin tab, because there is already one. return;
diff --git a/chrome/browser/ui/tabs/hover_tab_selector.cc b/chrome/browser/ui/tabs/hover_tab_selector.cc index b16d118d..1916294 100644 --- a/chrome/browser/ui/tabs/hover_tab_selector.cc +++ b/chrome/browser/ui/tabs/hover_tab_selector.cc
@@ -10,6 +10,7 @@ #include "base/task/single_thread_task_runner.h" #include "base/threading/thread_task_runner_handle.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" +#include "chrome/browser/ui/tabs/tab_strip_user_gesture_details.h" HoverTabSelector::HoverTabSelector(TabStripModel* tab_strip_model) : tab_strip_model_(tab_strip_model), tab_transition_tab_index_(-1) { @@ -48,7 +49,9 @@ void HoverTabSelector::PerformTabTransition() { DCHECK(tab_transition_tab_index_ >= 0 && tab_transition_tab_index_ < tab_strip_model_->count()); - tab_strip_model_->ActivateTabAt(tab_transition_tab_index_, - {TabStripModel::GestureType::kOther}); + tab_strip_model_->ActivateTabAt( + tab_transition_tab_index_, + TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); }
diff --git a/chrome/browser/ui/tabs/tab_activity_simulator.cc b/chrome/browser/ui/tabs/tab_activity_simulator.cc index ef66053..c5f6a12 100644 --- a/chrome/browser/ui/tabs/tab_activity_simulator.cc +++ b/chrome/browser/ui/tabs/tab_activity_simulator.cc
@@ -66,8 +66,9 @@ // which is what actually triggers TabActivityWatcher to log the change. For // a TestWebContents, we must manually call WasHidden(), and do the reverse // for the newly activated tab. - tab_strip_model->ActivateTabAt(new_index, - {TabStripModel::GestureType::kOther}); + tab_strip_model->ActivateTabAt( + new_index, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); active_contents->WasHidden(); new_contents->WasShown(); }
diff --git a/chrome/browser/ui/tabs/tab_menu_model_unittest.cc b/chrome/browser/ui/tabs/tab_menu_model_unittest.cc index 4a47bd06..2a9df4d4 100644 --- a/chrome/browser/ui/tabs/tab_menu_model_unittest.cc +++ b/chrome/browser/ui/tabs/tab_menu_model_unittest.cc
@@ -164,7 +164,9 @@ feed::WebFeedTabHelper* web_feed_tab_helper2 = feed::WebFeedTabHelper::FromWebContents(web_contents2); - tab_strip->ActivateTabAt(0, {TabStripModel::GestureType::kOther}); + tab_strip->ActivateTabAt( + 0, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); tab_strip->ExtendSelectionTo(2); // Neither "Follow site" nor "Unfollow site" should be added when there is at
diff --git a/chrome/browser/ui/tabs/tab_strip_model.cc b/chrome/browser/ui/tabs/tab_strip_model.cc index 01c5221d..2d1f6c5 100644 --- a/chrome/browser/ui/tabs/tab_strip_model.cc +++ b/chrome/browser/ui/tabs/tab_strip_model.cc
@@ -588,7 +588,8 @@ } } -void TabStripModel::ActivateTabAt(int index, UserGestureDetails user_gesture) { +void TabStripModel::ActivateTabAt(int index, + TabStripUserGestureDetails user_gesture) { ReentrancyCheck reentrancy_check(&reentrancy_guard_); CHECK(ContainsIndex(index)); @@ -596,16 +597,17 @@ // Maybe increment count of tabs 'scrubbed' by mouse or key press for // histogram data. - if (user_gesture.type == GestureType::kMouse || - user_gesture.type == GestureType::kKeyboard) { + if (user_gesture.type == TabStripUserGestureDetails::GestureType::kMouse || + user_gesture.type == TabStripUserGestureDetails::GestureType::kKeyboard) { constexpr base::TimeDelta kMaxTimeConsideredScrubbing = base::Milliseconds(1500); base::TimeDelta elapsed_time_since_tab_switch = base::TimeTicks::Now() - last_tab_switch_timestamp_; if (elapsed_time_since_tab_switch <= kMaxTimeConsideredScrubbing) { - if (user_gesture.type == GestureType::kMouse) + if (user_gesture.type == TabStripUserGestureDetails::GestureType::kMouse) ++tabs_scrubbed_by_mouse_press_count_; - else if (user_gesture.type == GestureType::kKeyboard) + else if (user_gesture.type == + TabStripUserGestureDetails::GestureType::kKeyboard) ++tabs_scrubbed_by_key_press_count_; } } @@ -613,16 +615,16 @@ TabSwitchEventLatencyRecorder::EventType event_type; switch (user_gesture.type) { - case GestureType::kMouse: + case TabStripUserGestureDetails::GestureType::kMouse: event_type = TabSwitchEventLatencyRecorder::EventType::kMouse; break; - case GestureType::kKeyboard: + case TabStripUserGestureDetails::GestureType::kKeyboard: event_type = TabSwitchEventLatencyRecorder::EventType::kKeyboard; break; - case GestureType::kTouch: + case TabStripUserGestureDetails::GestureType::kTouch: event_type = TabSwitchEventLatencyRecorder::EventType::kTouch; break; - case GestureType::kWheel: + case TabStripUserGestureDetails::GestureType::kWheel: event_type = TabSwitchEventLatencyRecorder::EventType::kWheel; break; default: @@ -633,11 +635,12 @@ event_type); ui::ListSelectionModel new_model = selection_model_; new_model.SetSelectedIndex(index); - SetSelection(std::move(new_model), - user_gesture.type != GestureType::kNone - ? TabStripModelObserver::CHANGE_REASON_USER_GESTURE - : TabStripModelObserver::CHANGE_REASON_NONE, - /*triggered_by_other_operation=*/false); + SetSelection( + std::move(new_model), + user_gesture.type != TabStripUserGestureDetails::GestureType::kNone + ? TabStripModelObserver::CHANGE_REASON_USER_GESTURE + : TabStripModelObserver::CHANGE_REASON_NONE, + /*triggered_by_other_operation=*/false); } void TabStripModel::RecordTabScrubbingMetrics() { @@ -1106,15 +1109,15 @@ CLOSE_CREATE_HISTORICAL_TAB | CLOSE_USER_GESTURE); } -void TabStripModel::SelectNextTab(UserGestureDetails detail) { +void TabStripModel::SelectNextTab(TabStripUserGestureDetails detail) { SelectRelativeTab(TabRelativeDirection::kNext, detail); } -void TabStripModel::SelectPreviousTab(UserGestureDetails detail) { +void TabStripModel::SelectPreviousTab(TabStripUserGestureDetails detail) { SelectRelativeTab(TabRelativeDirection::kPrevious, detail); } -void TabStripModel::SelectLastTab(UserGestureDetails detail) { +void TabStripModel::SelectLastTab(TabStripUserGestureDetails detail) { ActivateTabAt(count() - 1, detail); } @@ -2104,7 +2107,7 @@ } void TabStripModel::SelectRelativeTab(TabRelativeDirection direction, - UserGestureDetails detail) { + TabStripUserGestureDetails detail) { // This may happen during automated testing or if a user somehow buffers // many key accelerators. if (contents_data_.empty())
diff --git a/chrome/browser/ui/tabs/tab_strip_model.h b/chrome/browser/ui/tabs/tab_strip_model.h index 7f9077b..d6e9a27 100644 --- a/chrome/browser/ui/tabs/tab_strip_model.h +++ b/chrome/browser/ui/tabs/tab_strip_model.h
@@ -24,6 +24,7 @@ #include "base/timer/timer.h" #include "build/build_config.h" #include "chrome/browser/ui/tabs/tab_group_controller.h" +#include "chrome/browser/ui/tabs/tab_strip_user_gesture_details.h" #include "chrome/browser/ui/tabs/tab_switch_event_latency_recorder.h" #include "components/sessions/core/session_id.h" #include "components/tab_groups/tab_group_id.h" @@ -302,35 +303,14 @@ // Detaches the WebContents at the specified index and immediately deletes it. void DetachAndDeleteWebContentsAt(int index); - // User gesture type that triggers ActivateTabAt. kNone indicates that it was - // not triggered by a user gesture, but by a by-product of some other action. - enum class GestureType { - kMouse, - kTouch, - kWheel, - kKeyboard, - kOther, - kTabMenu, - kNone - }; - - // Encapsulates user gesture information for tab activation - struct UserGestureDetails { - UserGestureDetails(GestureType type, - base::TimeTicks time_stamp = base::TimeTicks::Now()) - : type(type), time_stamp(time_stamp) {} - - GestureType type; - base::TimeTicks time_stamp; - }; - // Makes the tab at the specified index the active tab. |gesture_detail.type| // contains the gesture type that triggers the tab activation. // |gesture_detail.time_stamp| contains the timestamp of the user gesture, if // any. - void ActivateTabAt(int index, - UserGestureDetails gesture_detail = - UserGestureDetails(GestureType::kNone)); + void ActivateTabAt( + int index, + TabStripUserGestureDetails gesture_detail = TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kNone)); // Report histogram metrics for the number of tabs 'scrubbed' within a given // interval of time. Scrubbing is considered to be a tab activated for <= 1.5 @@ -493,13 +473,16 @@ // Select adjacent tabs void SelectNextTab( - UserGestureDetails detail = UserGestureDetails(GestureType::kOther)); + TabStripUserGestureDetails detail = TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); void SelectPreviousTab( - UserGestureDetails detail = UserGestureDetails(GestureType::kOther)); + TabStripUserGestureDetails detail = TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); // Selects the last tab in the tab strip. void SelectLastTab( - UserGestureDetails detail = UserGestureDetails(GestureType::kOther)); + TabStripUserGestureDetails detail = TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); // Moves the active in the specified direction. Respects group boundaries. void MoveTabNext(); @@ -800,7 +783,7 @@ // Selects either the next tab (kNext), or the previous tab (kPrevious). void SelectRelativeTab(TabRelativeDirection direction, - UserGestureDetails detail); + TabStripUserGestureDetails detail); // Moves the active tabs into the next slot (kNext), or the // previous slot (kPrevious). Respects group boundaries and creates
diff --git a/chrome/browser/ui/tabs/tab_strip_model_stats_recorder_unittest.cc b/chrome/browser/ui/tabs/tab_strip_model_stats_recorder_unittest.cc index 29e1b08..f30819a1 100644 --- a/chrome/browser/ui/tabs/tab_strip_model_stats_recorder_unittest.cc +++ b/chrome/browser/ui/tabs/tab_strip_model_stats_recorder_unittest.cc
@@ -50,7 +50,8 @@ // Reactivate the first tab. tabstrip.ActivateTabAt(tabstrip.GetIndexOfWebContents(raw_contents1), - {TabStripModel::GestureType::kOther}); + TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); tester.ExpectUniqueSample( "Tabs.StateTransfer.Target_Active", @@ -133,7 +134,9 @@ static_cast<int>(TabStripModelStatsRecorder::TabState::ACTIVE), 1); // Switch to the first tab in strip 2. - tabstrip2.ActivateTabAt(0, {TabStripModel::GestureType::kOther}); + tabstrip2.ActivateTabAt(0, + TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); tester.ExpectUniqueSample( "Tabs.StateTransfer.Target_Active", static_cast<int>(TabStripModelStatsRecorder::TabState::INACTIVE), 4); @@ -178,7 +181,8 @@ // Reactivate the first tab tabstrip.ActivateTabAt(tabstrip.GetIndexOfWebContents(raw_contents0), - {TabStripModel::GestureType::kOther}); + TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); tester.ExpectUniqueSample( "Tabs.StateTransfer.NumberOfOtherTabsActivatedBeforeMadeActive", 9, 1); @@ -213,13 +217,18 @@ // Switch between tabs {0,1} for 5 times, then switch to tab 2 for (int i = 0; i < 5; ++i) { - tabstrip.ActivateTabAt(tabstrip.GetIndexOfWebContents(raw_contents0), - {TabStripModel::GestureType::kOther}); - tabstrip.ActivateTabAt(tabstrip.GetIndexOfWebContents(raw_contents1), - {TabStripModel::GestureType::kOther}); + tabstrip.ActivateTabAt( + tabstrip.GetIndexOfWebContents(raw_contents0), + TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); + tabstrip.ActivateTabAt( + tabstrip.GetIndexOfWebContents(raw_contents1), + TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); } tabstrip.ActivateTabAt(tabstrip.GetIndexOfWebContents(raw_contents2), - {TabStripModel::GestureType::kOther}); + TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); EXPECT_THAT( tester.GetAllSamples(
diff --git a/chrome/browser/ui/tabs/tab_strip_model_unittest.cc b/chrome/browser/ui/tabs/tab_strip_model_unittest.cc index b4796ac6..5ff2b5a 100644 --- a/chrome/browser/ui/tabs/tab_strip_model_unittest.cc +++ b/chrome/browser/ui/tabs/tab_strip_model_unittest.cc
@@ -27,6 +27,7 @@ #include "chrome/browser/ui/browser_list.h" #include "chrome/browser/ui/tabs/tab_group.h" #include "chrome/browser/ui/tabs/tab_group_model.h" +#include "chrome/browser/ui/tabs/tab_strip_user_gesture_details.h" #include "chrome/browser/ui/tabs/tab_utils.h" #include "chrome/browser/ui/tabs/test_tab_strip_model_delegate.h" #include "chrome/test/base/browser_with_test_window_test.h" @@ -539,7 +540,9 @@ // Test ActivateTabAt { - tabstrip.ActivateTabAt(2, {TabStripModel::GestureType::kOther}); + tabstrip.ActivateTabAt( + 2, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); EXPECT_EQ(3, observer.GetStateCount()); State s1(raw_contents2, 1, MockTabStripModelObserver::DEACTIVATE); observer.ExpectStateEquals(0, s1); @@ -682,7 +685,9 @@ // Test SelectNextTab, SelectPreviousTab, SelectLastTab { // Make sure the second of the two tabs is selected first... - tabstrip.ActivateTabAt(1, {TabStripModel::GestureType::kOther}); + tabstrip.ActivateTabAt( + 1, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); tabstrip.SelectPreviousTab(); EXPECT_EQ(0, tabstrip.active_index()); tabstrip.SelectLastTab(); @@ -925,7 +930,8 @@ // Now select it, so that GestureType != kNone causes the opener // relationship to be forgotten... tabstrip.ActivateTabAt(tabstrip.count() - 1, - {TabStripModel::GestureType::kOther}); + TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); EXPECT_EQ(tabstrip.count() - 1, tabstrip.active_index()); EXPECT_EQ(raw_fg_nonlink_contents, tabstrip.GetActiveWebContents()); @@ -975,7 +981,9 @@ EXPECT_EQ(raw_opener1, tabstrip.GetOpenerOfWebContentsAt(2)); // Activate the parent tab again. - tabstrip.ActivateTabAt(0, {TabStripModel::GestureType::kOther}); + tabstrip.ActivateTabAt(0, + TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); EXPECT_EQ(1, GetID(tabstrip.GetActiveWebContents())); // Open another link in a new background tab. @@ -1017,7 +1025,9 @@ EXPECT_EQ(1, tabstrip.GetIndexOfLastWebContentsOpenedBy(raw_opener1, 0)); // Activate the child tab: - tabstrip.ActivateTabAt(1, {TabStripModel::GestureType::kOther}); + tabstrip.ActivateTabAt(1, + TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); EXPECT_EQ(11, GetID(tabstrip.GetActiveWebContents())); // Open a link in a new background grandchild tab. @@ -1032,7 +1042,9 @@ EXPECT_EQ(2, tabstrip.GetIndexOfLastWebContentsOpenedBy(raw_child11, 1)); // Activate the parent tab again: - tabstrip.ActivateTabAt(0, {TabStripModel::GestureType::kOther}); + tabstrip.ActivateTabAt(0, + TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); EXPECT_EQ(1, GetID(tabstrip.GetActiveWebContents())); // Open another link in a new background child tab (a sibling of child11). @@ -1099,7 +1111,9 @@ ASSERT_EQ(0, tabstrip.active_index()); tabstrip.ForgetAllOpeners(); - tabstrip.ActivateTabAt(1, {TabStripModel::GestureType::kOther}); + tabstrip.ActivateTabAt(1, + TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); ASSERT_EQ(1, tabstrip.active_index()); tabstrip.CloseWebContentsAt(1, TabStripModel::CLOSE_NONE); @@ -1130,7 +1144,9 @@ tabstrip.ForgetAllOpeners(); tabstrip.AddToNewGroup({0, 1, 2}); - tabstrip.ActivateTabAt(1, {TabStripModel::GestureType::kOther}); + tabstrip.ActivateTabAt(1, + TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); ASSERT_EQ(1, tabstrip.active_index()); tabstrip.CloseWebContentsAt(1, TabStripModel::CLOSE_NONE); @@ -1160,7 +1176,9 @@ tabstrip.ForgetAllOpeners(); tab_groups::TabGroupId group = tabstrip.AddToNewGroup({0, 1}); - tabstrip.ActivateTabAt(1, {TabStripModel::GestureType::kOther}); + tabstrip.ActivateTabAt(1, + TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); ASSERT_EQ(1, tabstrip.active_index()); absl::optional<int> next_active = @@ -1188,7 +1206,9 @@ tabstrip.ForgetAllOpeners(); tab_groups::TabGroupId group = tabstrip.AddToNewGroup({2, 3}); - tabstrip.ActivateTabAt(2, {TabStripModel::GestureType::kOther}); + tabstrip.ActivateTabAt(2, + TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); ASSERT_EQ(2, tabstrip.active_index()); absl::optional<int> next_active = @@ -1216,7 +1236,9 @@ tabstrip.ForgetAllOpeners(); tab_groups::TabGroupId group = tabstrip.AddToNewGroup({0, 1, 2, 3}); - tabstrip.ActivateTabAt(1, {TabStripModel::GestureType::kOther}); + tabstrip.ActivateTabAt(1, + TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); ASSERT_EQ(1, tabstrip.active_index()); absl::optional<int> next_active = @@ -1242,7 +1264,9 @@ CreateWebContents()); ASSERT_EQ(0, tabstrip.active_index()); - tabstrip.ActivateTabAt(2, {TabStripModel::GestureType::kOther}); + tabstrip.ActivateTabAt(2, + TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); ASSERT_EQ(2, tabstrip.active_index()); tabstrip.CloseWebContentsAt(2, TabStripModel::CLOSE_NONE); @@ -1538,7 +1562,9 @@ EXPECT_EQ(5, tabstrip.count()); int dummy_index = tabstrip.count() - 1; - tabstrip.ActivateTabAt(dummy_index, {TabStripModel::GestureType::kOther}); + tabstrip.ActivateTabAt(dummy_index, + TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); EXPECT_EQ(raw_dummy, tabstrip.GetActiveWebContents()); tabstrip.ExecuteContextMenuCommand(dummy_index, @@ -1624,7 +1650,9 @@ EXPECT_EQ(2, tabstrip.count()); // Re-select the home page. - tabstrip.ActivateTabAt(0, {TabStripModel::GestureType::kOther}); + tabstrip.ActivateTabAt(0, + TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); // Open a bunch of tabs by simulating middle clicking on links on the home // page. @@ -1654,7 +1682,9 @@ // is constructed to start at the middle WebContents in the tree to make sure // the cursor wraps around to the first WebContents in the tree before // closing the opener or any other WebContents. - tabstrip.ActivateTabAt(2, {TabStripModel::GestureType::kOther}); + tabstrip.ActivateTabAt(2, + TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); tabstrip.CloseSelectedTabs(); EXPECT_EQ(raw_middle_click_contents3, tabstrip.GetActiveWebContents()); tabstrip.CloseSelectedTabs(); @@ -1694,7 +1724,9 @@ EXPECT_EQ(2, tabstrip.count()); // Re-select the home page. - tabstrip.ActivateTabAt(0, {TabStripModel::GestureType::kOther}); + tabstrip.ActivateTabAt(0, + TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); // Open a tab by simulating a left click on a link that opens in a new tab. std::unique_ptr<WebContents> left_click_contents = CreateWebContents(); @@ -1746,7 +1778,9 @@ EXPECT_EQ(2, tabstrip.count()); // Re-select the home page. - tabstrip.ActivateTabAt(0, {TabStripModel::GestureType::kOther}); + tabstrip.ActivateTabAt(0, + TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); // Open a new blank tab in the foreground. std::unique_ptr<WebContents> new_blank_contents = CreateWebContents(); @@ -1806,7 +1840,9 @@ EXPECT_EQ(2, tabstrip.count()); // Re-select the first tab (home page). - tabstrip.ActivateTabAt(0, {TabStripModel::GestureType::kOther}); + tabstrip.ActivateTabAt(0, + TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); // Open a bunch of tabs by simulating middle clicking on links on the home // page. @@ -1829,7 +1865,9 @@ EXPECT_EQ(raw_typed_page_contents, tabstrip.GetActiveWebContents()); // Step back into the context by selecting a tab inside it. - tabstrip.ActivateTabAt(2, {TabStripModel::GestureType::kOther}); + tabstrip.ActivateTabAt(2, + TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); EXPECT_EQ(raw_middle_click_contents2, tabstrip.GetActiveWebContents()); // Now test that closing tabs selects to the right until there are no more, @@ -1970,7 +2008,8 @@ ui::PAGE_TRANSITION_LINK, TabStripModel::ADD_NONE); // Select page A.A - strip.ActivateTabAt(1, {TabStripModel::GestureType::kOther}); + strip.ActivateTabAt(1, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); EXPECT_EQ(raw_page_a_a_contents, strip.GetActiveWebContents()); // Simulate a middle click to open page A.A.A @@ -2019,7 +2058,8 @@ } // Switch to page B's tab. - strip.ActivateTabAt(1, {TabStripModel::GestureType::kOther}); + strip.ActivateTabAt(1, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); // Open a New Tab at the end of the strip (simulate Ctrl+T) std::unique_ptr<WebContents> new_contents = CreateWebContents(); @@ -2106,7 +2146,8 @@ TabStripModel::ADD_NONE); // Tell the TabStripModel that we are navigating page D via a link click. - strip.ActivateTabAt(3, {TabStripModel::GestureType::kOther}); + strip.ActivateTabAt(3, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); strip.TabNavigating(raw_page_d_contents, ui::PAGE_TRANSITION_LINK); // Close page D, page C should be selected. (part of same opener tree). @@ -2148,7 +2189,8 @@ strip.AddWebContents(std::move(page_d_contents), -1, ui::PAGE_TRANSITION_LINK, TabStripModel::ADD_NONE); - strip.ActivateTabAt(2, {TabStripModel::GestureType::kOther}); + strip.ActivateTabAt(2, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); // TEST 1: A tab in the middle of a bunch of tabs is active and the user opens // a new tab at the end of the strip. Closing that new tab will select the tab @@ -2181,10 +2223,13 @@ TabStripModel::ADD_ACTIVE); // Now select the first tab. - strip.ActivateTabAt(0, {TabStripModel::GestureType::kOther}); + strip.ActivateTabAt(0, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); // Now select the last tab. - strip.ActivateTabAt(strip.count() - 1, {TabStripModel::GestureType::kOther}); + strip.ActivateTabAt(strip.count() - 1, + TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); // Now close the last tab. The next adjacent should be selected. strip.CloseWebContentsAt(strip.count() - 1, TabStripModel::CLOSE_NONE); @@ -2192,7 +2237,8 @@ // TEST 3: As above, but the user does multiple navigations and thus the tab's // opener relationship is forgotten. - strip.ActivateTabAt(2, {TabStripModel::GestureType::kOther}); + strip.ActivateTabAt(2, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); // Open a new tab but navigate away from the new tab page. new_tab_contents = CreateWebContents(); @@ -2843,7 +2889,8 @@ EXPECT_EQ(raw_page_b1_contents, strip.GetWebContentsAt(4)); // Switch to A. - strip.ActivateTabAt(2, {TabStripModel::GestureType::kOther}); + strip.ActivateTabAt(2, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); EXPECT_EQ(raw_page_a_contents, strip.GetActiveWebContents()); // Open page A2 in the background from A. It should end up after A1. @@ -2890,7 +2937,8 @@ strip.AddObserver(&observer); // Selection and active tab change. - strip.ActivateTabAt(3, {TabStripModel::GestureType::kOther}); + strip.ActivateTabAt(3, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); ASSERT_EQ(2, observer.GetStateCount()); ASSERT_EQ(observer.GetStateAt(0).action, MockTabStripModelObserver::ACTIVATE); State s1(raw_contents3, 3, MockTabStripModelObserver::SELECT); @@ -2988,7 +3036,8 @@ strip.AddObserver(&observer); // This changes the selection (0 is no longer selected) but the selected_index // still remains at 1. - strip.ActivateTabAt(1, {TabStripModel::GestureType::kOther}); + strip.ActivateTabAt(1, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); ASSERT_EQ(1, observer.GetStateCount()); State s(raw_contents2, 1, MockTabStripModelObserver::SELECT); s.src_index = 1; @@ -3148,7 +3197,8 @@ TabStripModel::ADD_ACTIVE | TabStripModel::ADD_PINNED); // Activate the first tab (a). - strip.ActivateTabAt(0, {TabStripModel::GestureType::kOther}); + strip.ActivateTabAt(0, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); // Open two more tabs as link clicks. The first tab, c, should appear after // the pinned tabs followed by the second tab (d). @@ -3180,7 +3230,8 @@ strip.AppendWebContents(CreateWebContents(), false); strip.AddObserver(&observer); - strip.ActivateTabAt(1, {TabStripModel::GestureType::kOther}); + strip.ActivateTabAt(1, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); EXPECT_EQ(1, strip.active_index()); strip.MoveWebContentsAt(2, 3, true); @@ -3196,7 +3247,8 @@ EXPECT_FALSE(strip.GetTabGroupForTab(0).has_value()); - strip.ActivateTabAt(0, {TabStripModel::GestureType::kOther}); + strip.ActivateTabAt(0, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); strip.CloseAllTabs(); } @@ -3211,7 +3263,8 @@ EXPECT_TRUE(strip.GetTabGroupForTab(0).has_value()); - strip.ActivateTabAt(0, {TabStripModel::GestureType::kOther}); + strip.ActivateTabAt(0, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); strip.CloseAllTabs(); } @@ -4271,13 +4324,16 @@ ASSERT_FALSE(has_tab_switch_start_time(1)); // ActivateTabAt should only update the start time if the active tab changes. - strip.ActivateTabAt(1, {TabStripModel::GestureType::kOther}); + strip.ActivateTabAt(1, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); EXPECT_FALSE(has_tab_switch_start_time(0)); EXPECT_FALSE(has_tab_switch_start_time(1)); - strip.ActivateTabAt(0, {TabStripModel::GestureType::kOther}); + strip.ActivateTabAt(0, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); EXPECT_TRUE(has_tab_switch_start_time(0)); EXPECT_FALSE(has_tab_switch_start_time(1)); - strip.ActivateTabAt(1, {TabStripModel::GestureType::kOther}); + strip.ActivateTabAt(1, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); EXPECT_TRUE(has_tab_switch_start_time(0)); EXPECT_TRUE(has_tab_switch_start_time(1)); }
diff --git a/chrome/browser/ui/tabs/tab_strip_user_gesture_details.h b/chrome/browser/ui/tabs/tab_strip_user_gesture_details.h new file mode 100644 index 0000000..5f5891e --- /dev/null +++ b/chrome/browser/ui/tabs/tab_strip_user_gesture_details.h
@@ -0,0 +1,33 @@ +// 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_TABS_TAB_STRIP_USER_GESTURE_DETAILS_H_ +#define CHROME_BROWSER_UI_TABS_TAB_STRIP_USER_GESTURE_DETAILS_H_ + +#include "base/time/time.h" + +// Encapsulates user gesture information for tab activation +struct TabStripUserGestureDetails { + // User gesture type that triggers ActivateTabAt. kNone indicates that it was + // not triggered by a user gesture, but by a by-product of some other action. + enum class GestureType { + kMouse, + kTouch, + kWheel, + kKeyboard, + kOther, + kTabMenu, + kNone + }; + + explicit TabStripUserGestureDetails( + GestureType type, + base::TimeTicks time_stamp = base::TimeTicks::Now()) + : type(type), time_stamp(time_stamp) {} + + GestureType type; + base::TimeTicks time_stamp; +}; + +#endif // CHROME_BROWSER_UI_TABS_TAB_STRIP_USER_GESTURE_DETAILS_H_
diff --git a/chrome/browser/ui/user_education/active_tab_tracker_unittest.cc b/chrome/browser/ui/user_education/active_tab_tracker_unittest.cc index e9cd96b..77b0e6f22 100644 --- a/chrome/browser/ui/user_education/active_tab_tracker_unittest.cc +++ b/chrome/browser/ui/user_education/active_tab_tracker_unittest.cc
@@ -10,6 +10,7 @@ #include "base/test/mock_callback.h" #include "base/test/simple_test_tick_clock.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" +#include "chrome/browser/ui/tabs/tab_strip_user_gesture_details.h" #include "chrome/browser/ui/tabs/test_tab_strip_model_delegate.h" #include "chrome/test/base/testing_profile.h" #include "content/public/browser/web_contents.h" @@ -80,7 +81,8 @@ AddTab(&model); clock()->Advance(kTimeStep); - model.ActivateTabAt(0, {TabStripModel::GestureType::kOther}); + model.ActivateTabAt(0, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); clock()->Advance(kTimeStep); @@ -102,12 +104,15 @@ AddTab(&model); AddTab(&model); - model.ActivateTabAt(0, {TabStripModel::GestureType::kOther}); + model.ActivateTabAt(0, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); clock()->Advance(kTimeStep); - model.ActivateTabAt(1, {TabStripModel::GestureType::kOther}); - model.ActivateTabAt(0, {TabStripModel::GestureType::kOther}); + model.ActivateTabAt(1, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); + model.ActivateTabAt(0, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); EXPECT_CALL(cb, Run(&model, base::TimeDelta())).Times(1); CloseTabAt(&model, 0); @@ -127,7 +132,8 @@ AddTab(&model); AddTab(&model); - model.ActivateTabAt(0, {TabStripModel::GestureType::kOther}); + model.ActivateTabAt(0, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); EXPECT_CALL(cb, Run(_, _)).Times(0); CloseTabAt(&model, 1); @@ -153,10 +159,14 @@ AddTab(&model_2); clock()->Advance(kTimeStep); - model_1.ActivateTabAt(0, {TabStripModel::GestureType::kOther}); + model_1.ActivateTabAt(0, + TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); clock()->Advance(kTimeStep); - model_2.ActivateTabAt(0, {TabStripModel::GestureType::kOther}); + model_2.ActivateTabAt(0, + TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); { InSequence seq;
diff --git a/chrome/browser/ui/views/apps/chrome_native_app_window_views_aura_ash.cc b/chrome/browser/ui/views/apps/chrome_native_app_window_views_aura_ash.cc index ff0fe4b..36f72b7 100644 --- a/chrome/browser/ui/views/apps/chrome_native_app_window_views_aura_ash.cc +++ b/chrome/browser/ui/views/apps/chrome_native_app_window_views_aura_ash.cc
@@ -536,7 +536,9 @@ // is no need for immersive mode. // TODO(crbug.com/801619): This adds a little extra animation // when minimizing or unminimizing window. - return ash::TabletMode::IsInTabletMode() && CanResize() && !IsMinimized(); + return ash::TabletMode::IsInTabletMode() && CanResize() && !IsMinimized() && + GetNativeWindow()->GetProperty(chromeos::kWindowStateTypeKey) != + chromeos::WindowStateType::kFloated; } void ChromeNativeAppWindowViewsAuraAsh::UpdateImmersiveMode() {
diff --git a/chrome/browser/ui/views/autofill/payments/local_card_migration_browsertest.cc b/chrome/browser/ui/views/autofill/payments/local_card_migration_browsertest.cc index 0b00cdf..e381bff 100644 --- a/chrome/browser/ui/views/autofill/payments/local_card_migration_browsertest.cc +++ b/chrome/browser/ui/views/autofill/payments/local_card_migration_browsertest.cc
@@ -1115,14 +1115,18 @@ ui::PAGE_TRANSITION_TYPED, /*check_navigation_success=*/true)); TabStripModel* tab_model = GetBrowser(0)->tab_strip_model(); - tab_model->ActivateTabAt(1, {TabStripModel::GestureType::kOther}); + tab_model->ActivateTabAt( + 1, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); WaitForAnimationToComplete(); // Ensures bubble and icon go away if user navigates to another tab. EXPECT_FALSE(GetLocalCardMigrationIconView()->GetVisible()); EXPECT_FALSE(GetLocalCardMigrationOfferBubbleViews()); - tab_model->ActivateTabAt(0, {TabStripModel::GestureType::kOther}); + tab_model->ActivateTabAt( + 0, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); WaitForAnimationToComplete(); // If the user navigates back, shows only the icon not the bubble.
diff --git a/chrome/browser/ui/views/autofill/payments/save_card_bubble_views_browsertest.cc b/chrome/browser/ui/views/autofill/payments/save_card_bubble_views_browsertest.cc index ed15b4e..88a8896 100644 --- a/chrome/browser/ui/views/autofill/payments/save_card_bubble_views_browsertest.cc +++ b/chrome/browser/ui/views/autofill/payments/save_card_bubble_views_browsertest.cc
@@ -2149,14 +2149,18 @@ ui::PAGE_TRANSITION_TYPED, /*check_navigation_success=*/true)); TabStripModel* tab_model = GetBrowser(0)->tab_strip_model(); - tab_model->ActivateTabAt(1, {TabStripModel::GestureType::kOther}); + tab_model->ActivateTabAt( + 1, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); WaitForAnimationToEnd(); // Ensures bubble and icon go away if user navigates to another tab. EXPECT_FALSE(GetSaveCardIconView()->GetVisible()); EXPECT_FALSE(GetSaveCardBubbleViews()); - tab_model->ActivateTabAt(0, {TabStripModel::GestureType::kOther}); + tab_model->ActivateTabAt( + 0, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); WaitForAnimationToEnd(); // If the user navigates back, shows only the icon not the bubble.
diff --git a/chrome/browser/ui/views/extensions/extensions_request_access_dialog_view_browsertest.cc b/chrome/browser/ui/views/extensions/extensions_request_access_dialog_view_browsertest.cc index 66ef11f..e9013d7 100644 --- a/chrome/browser/ui/views/extensions/extensions_request_access_dialog_view_browsertest.cc +++ b/chrome/browser/ui/views/extensions/extensions_request_access_dialog_view_browsertest.cc
@@ -38,6 +38,9 @@ extensions_features::kExtensionsMenuAccessControl}; }; -IN_PROC_BROWSER_TEST_F(ExtensionsRequestAccessDialogViewBrowserTest, InvokeUi) { +// TODO(crbug.com/1339738): Flaky on win-clang and win/win64 trunk builds. +// ExtensionsRequestAccessDialog may not longer be used, wait to see if the class is +// deleted before fixing this. +IN_PROC_BROWSER_TEST_F(ExtensionsRequestAccessDialogViewBrowserTest, DISABLED_InvokeUi) { ShowAndVerifyUi(); }
diff --git a/chrome/browser/ui/views/find_bar_views_interactive_uitest.cc b/chrome/browser/ui/views/find_bar_views_interactive_uitest.cc index bddfa4f..72da2b4 100644 --- a/chrome/browser/ui/views/find_bar_views_interactive_uitest.cc +++ b/chrome/browser/ui/views/find_bar_views_interactive_uitest.cc
@@ -127,7 +127,8 @@ // Select tab A. browser()->tab_strip_model()->ActivateTabAt( - 0, {TabStripModel::GestureType::kOther}); + 0, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); // Close tab B. browser()->tab_strip_model()->CloseWebContentsAt(1, @@ -368,13 +369,15 @@ // Select tab A. Find bar should select "bc". browser()->tab_strip_model()->ActivateTabAt( - 0, {TabStripModel::GestureType::kOther}); + 0, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); EXPECT_TRUE(IsViewFocused(browser(), VIEW_ID_FIND_IN_PAGE_TEXT_FIELD)); EXPECT_EQ(u"bc", GetFindBarSelectedText()); // Select tab B. Find bar should select "de". browser()->tab_strip_model()->ActivateTabAt( - 1, {TabStripModel::GestureType::kOther}); + 1, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); EXPECT_TRUE(IsViewFocused(browser(), VIEW_ID_FIND_IN_PAGE_TEXT_FIELD)); EXPECT_EQ(u"de", GetFindBarSelectedText()); } @@ -419,13 +422,15 @@ // Select tab A. Find bar should get focus. browser()->tab_strip_model()->ActivateTabAt( - 0, {TabStripModel::GestureType::kOther}); + 0, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); EXPECT_TRUE(IsViewFocused(browser(), VIEW_ID_FIND_IN_PAGE_TEXT_FIELD)); EXPECT_EQ(u"a", GetFindBarSelectedText()); // Select tab B. Location bar should get focus. browser()->tab_strip_model()->ActivateTabAt( - 1, {TabStripModel::GestureType::kOther}); + 1, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); EXPECT_TRUE(IsViewFocused(browser(), VIEW_ID_OMNIBOX)); } @@ -451,7 +456,8 @@ // Select tab A. Find bar should get focus. browser()->tab_strip_model()->ActivateTabAt( - 0, {TabStripModel::GestureType::kOther}); + 0, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); EXPECT_TRUE(IsViewFocused(browser(), VIEW_ID_FIND_IN_PAGE_TEXT_FIELD)); // Dismiss the Find box. Focus should go to the content view.
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_browsertest.cc b/chrome/browser/ui/views/frame/browser_non_client_frame_view_browsertest.cc index a641263..37d0add 100644 --- a/chrome/browser/ui/views/frame/browser_non_client_frame_view_browsertest.cc +++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_browsertest.cc
@@ -9,7 +9,6 @@ #include "build/chromeos_buildflags.h" #include "chrome/browser/extensions/extension_browsertest.h" #include "chrome/browser/scoped_disable_client_side_decorations_for_test.h" -#include "chrome/browser/themes/theme_properties.h" #include "chrome/browser/themes/theme_service.h" #include "chrome/browser/themes/theme_service_factory.h" #include "chrome/browser/ui/page_action/page_action_icon_type.h" @@ -31,7 +30,6 @@ #include "content/public/test/theme_change_waiter.h" #include "third_party/blink/public/mojom/frame/fullscreen.mojom.h" #include "third_party/blink/public/mojom/manifest/manifest.mojom.h" -#include "ui/base/theme_provider.h" #include "ui/color/color_id.h" #include "ui/color/color_provider.h" @@ -179,10 +177,9 @@ // color to the system color (not the app theme color); otherwise the title // and border would clash horribly with the GTK title bar. // (https://crbug.com/878636) - const ui::ThemeProvider* theme_provider = - GetAppFrameView()->GetThemeProvider(); - const SkColor frame_color = - theme_provider->GetColor(ThemeProperties::COLOR_FRAME_ACTIVE); + const ui::ColorProvider* color_provider = + GetAppFrameView()->GetColorProvider(); + const SkColor frame_color = color_provider->GetColor(ui::kColorFrameActive); EXPECT_EQ(frame_color, GetAppFrameView()->GetFrameColor(BrowserFrameActiveState::kActive)); #else
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 89dccc5..ab8871d 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
@@ -632,25 +632,14 @@ if (web_app_frame_toolbar()) web_app_frame_toolbar()->SetVisible(should_show_caption_buttons); - if (enabled) { - // Enter immersive mode if the feature is enabled and the widget is not - // already in fullscreen mode. Popups that are not activated but not - // minimized are still put in immersive mode, since they may still be - // visible but not activated due to something transparent and/or not - // fullscreen (ie. fullscreen launcher). - if (!frame()->IsFullscreen() && !browser_view()->GetSupportsTabStrip() && - !frame()->IsMinimized()) { - browser_view()->immersive_mode_controller()->SetEnabled(true); - return; - } - } else { - // Exit immersive mode if the feature is enabled and the widget is not in - // fullscreen mode. - if (!frame()->IsFullscreen() && !browser_view()->GetSupportsTabStrip()) { - browser_view()->immersive_mode_controller()->SetEnabled(false); - return; - } - } + ImmersiveModeController* immersive_mode_controller = + browser_view()->immersive_mode_controller(); + const bool was_enabled = immersive_mode_controller->IsEnabled(); + immersive_mode_controller->SetEnabled(ShouldEnableImmersiveModeController()); + + // Do not relayout if immersive mode has not changed. + if (was_enabled == immersive_mode_controller->IsEnabled()) + return; InvalidateLayout(); // Can be null in tests. @@ -699,6 +688,27 @@ ResetWindowControls(); } + if (key == chromeos::kWindowStateTypeKey) { + if (!chromeos::TabletState::Get()->InTabletMode()) + return; + + // Update the window controls if we are entering or exiting float state. + const bool enter_floated = IsFloated(); + const bool exit_floated = static_cast<chromeos::WindowStateType>(old) == + chromeos::WindowStateType::kFloated; + if (!enter_floated && !exit_floated) + return; + + ResetWindowControls(); + + // Additionally updates immersive mode for PWA/SWA so that we show the title + // bar when floated, and hide the title bar otherwise. + browser_view()->immersive_mode_controller()->SetEnabled( + ShouldEnableImmersiveModeController()); + + return; + } + if (key == chromeos::kIsShowingInOverviewKey) { OnAddedToOrRemovedFromOverview(); return; @@ -794,8 +804,11 @@ bool BrowserNonClientFrameViewChromeOS::GetShowCaptionButtonsWhenNotInOverview() const { - return UsePackagedAppHeaderStyle(browser_view()->browser()) || - !chromeos::TabletState::Get()->InTabletMode(); + if (UsePackagedAppHeaderStyle(browser_view()->browser())) + return true; + if (!chromeos::TabletState::Get()->InTabletMode()) + return true; + return IsFloated(); } int BrowserNonClientFrameViewChromeOS::GetToolbarLeftInset() const { @@ -823,6 +836,10 @@ } bool BrowserNonClientFrameViewChromeOS::GetShouldPaint() const { + // Floated windows show their frame as they need to be dragged or hidden. + if (IsFloated()) + return true; + #if BUILDFLAG(ENABLE_WEBUI_TAB_STRIP) // Normal windows that have a WebUI-based tab strip do not need a browser // frame as no tab strip is drawn on top of the browser frame. @@ -1049,6 +1066,32 @@ theme_changed_animation_callback_.callback()); } +bool BrowserNonClientFrameViewChromeOS::IsFloated() const { + return GetFrameWindow()->GetProperty(chromeos::kWindowStateTypeKey) == + chromeos::WindowStateType::kFloated; +} + +bool BrowserNonClientFrameViewChromeOS::ShouldEnableImmersiveModeController() + const { + if (chromeos::TabletState::Get()->InTabletMode()) { + // Tabbed browsers do not support immersive mode in tablet mode. We use the + // web ui touchable tabstrip, which has its own sliding mechanism to view + // the tabs. + if (browser_view()->GetSupportsTabStrip()) + return false; + + // No immersive mode for minimized windows as they aren't visible, and + // floated windows need a permanent header to drag. + if (frame()->IsMinimized() || IsFloated()) + return false; + + return true; + } + + // In clamshell mode, we want immersive mode if fullscreen. + return frame()->IsFullscreen(); +} + const aura::Window* BrowserNonClientFrameViewChromeOS::GetFrameWindow() const { return const_cast<BrowserNonClientFrameViewChromeOS*>(this)->GetFrameWindow(); }
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_chromeos.h b/chrome/browser/ui/views/frame/browser_non_client_frame_view_chromeos.h index 9433a9e..48e0e2d 100644 --- a/chrome/browser/ui/views/frame/browser_non_client_frame_view_chromeos.h +++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_chromeos.h
@@ -143,8 +143,6 @@ FRIEND_TEST_ALL_PREFIXES(BrowserNonClientFrameViewChromeOSTest, BrowserHeaderVisibilityInTabletModeTest); FRIEND_TEST_ALL_PREFIXES(BrowserNonClientFrameViewChromeOSTest, - AppHeaderVisibilityInTabletModeTest); - FRIEND_TEST_ALL_PREFIXES(BrowserNonClientFrameViewChromeOSTest, ImmersiveModeTopViewInset); FRIEND_TEST_ALL_PREFIXES(BrowserNonClientFrameViewChromeOSTest, ToggleTabletModeOnMinimizedWindow); @@ -152,6 +150,8 @@ ActiveStateOfButtonMatchesWidget); FRIEND_TEST_ALL_PREFIXES(BrowserNonClientFrameViewChromeOSTest, RestoreMinimizedBrowserUpdatesCaption); + FRIEND_TEST_ALL_PREFIXES(FloatBrowserNonClientFrameViewChromeOSTest, + AppHeaderVisibilityInTabletModeTest); FRIEND_TEST_ALL_PREFIXES(ImmersiveModeControllerChromeosWebAppBrowserTest, FrameLayoutToggleTabletMode); FRIEND_TEST_ALL_PREFIXES(HomeLauncherBrowserNonClientFrameViewChromeOSTest, @@ -234,6 +234,12 @@ // Called any time the theme has changed and may need to be animated. void MaybeAnimateThemeChanged(); + // Returns whether the associated window is currently floated or not. + bool IsFloated() const; + + // Helper to check whether we should enable immersive mode. + bool ShouldEnableImmersiveModeController() const; + // Returns the top level aura::Window for this browser window. const aura::Window* GetFrameWindow() const; aura::Window* GetFrameWindow();
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 69275cc..0b4831e 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
@@ -1157,43 +1157,6 @@ ash::SplitViewTestApi::SnapPosition::LEFT); EXPECT_FALSE(frame_view->caption_button_container_->GetVisible()); } - -// Test that for a browser app window, its caption buttons may or may not hide -// in tablet mode. -IN_PROC_BROWSER_TEST_P(BrowserNonClientFrameViewChromeOSTest, - AppHeaderVisibilityInTabletModeTest) { - // Create a browser app window. - Browser::CreateParams params = Browser::CreateParams::CreateForApp( - "test_browser_app", true /* trusted_source */, gfx::Rect(), - browser()->profile(), true); - params.initial_show_state = ui::SHOW_STATE_DEFAULT; - Browser* browser2 = Browser::Create(params); - AddBlankTabAndShow(browser2); - BrowserView* browser_view2 = BrowserView::GetBrowserViewForBrowser(browser2); - Widget* widget2 = browser_view2->GetWidget(); - BrowserNonClientFrameViewChromeOS* frame_view2 = - GetFrameViewChromeOS(browser_view2); - widget2->GetNativeWindow()->SetProperty( - aura::client::kResizeBehaviorKey, - aura::client::kResizeBehaviorCanMaximize | - aura::client::kResizeBehaviorCanResize); - StartOverview(); - EXPECT_FALSE(frame_view2->caption_button_container_->GetVisible()); - EndOverview(); - EXPECT_TRUE(frame_view2->caption_button_container_->GetVisible()); - - ASSERT_NO_FATAL_FAILURE( - ash::ShellTestApi().SetTabletModeEnabledForTest(true)); - StartOverview(); - EXPECT_FALSE(frame_view2->caption_button_container_->GetVisible()); - - EndOverview(); - EXPECT_TRUE(frame_view2->caption_button_container_->GetVisible()); - - ash::SplitViewTestApi().SnapWindow( - widget2->GetNativeWindow(), ash::SplitViewTestApi::SnapPosition::RIGHT); - EXPECT_TRUE(frame_view2->caption_button_container_->GetVisible()); -} #endif // BUILDFLAG(IS_CHROMEOS_ASH) // Regression test for https://crbug.com/879851. @@ -1280,6 +1243,71 @@ EXPECT_EQ(inset_normal, inset_in_overview_mode); } +class FloatBrowserNonClientFrameViewChromeOSTest + : public TopChromeMdParamTest<InProcessBrowserTest> { + public: + FloatBrowserNonClientFrameViewChromeOSTest() = default; + FloatBrowserNonClientFrameViewChromeOSTest( + const FloatBrowserNonClientFrameViewChromeOSTest&) = delete; + FloatBrowserNonClientFrameViewChromeOSTest& operator=( + const FloatBrowserNonClientFrameViewChromeOSTest&) = delete; + ~FloatBrowserNonClientFrameViewChromeOSTest() override = default; + + private: + base::test::ScopedFeatureList scoped_feature_list_{ + chromeos::wm::features::kFloatWindow}; +}; + +// Test that for a browser app window, its caption buttons may or may not hide +// in tablet mode. +IN_PROC_BROWSER_TEST_P(FloatBrowserNonClientFrameViewChromeOSTest, + AppHeaderVisibilityInTabletModeTest) { + // Create a browser app window. + Browser::CreateParams params = Browser::CreateParams::CreateForApp( + "test_browser_app", /*trusted_source=*/true, gfx::Rect(), + browser()->profile(), true); + params.initial_show_state = ui::SHOW_STATE_DEFAULT; + Browser* browser2 = Browser::Create(params); + AddBlankTabAndShow(browser2); + BrowserView* browser_view2 = BrowserView::GetBrowserViewForBrowser(browser2); + Widget* widget2 = browser_view2->GetWidget(); + BrowserNonClientFrameViewChromeOS* frame_view2 = + GetFrameViewChromeOS(browser_view2); + widget2->GetNativeWindow()->SetProperty( + aura::client::kResizeBehaviorKey, + aura::client::kResizeBehaviorCanMaximize | + aura::client::kResizeBehaviorCanResize); + StartOverview(); + EXPECT_FALSE(frame_view2->caption_button_container_->GetVisible()); + EndOverview(); + EXPECT_TRUE(frame_view2->caption_button_container_->GetVisible()); + + ASSERT_NO_FATAL_FAILURE( + ash::ShellTestApi().SetTabletModeEnabledForTest(true)); + StartOverview(); + EXPECT_FALSE(frame_view2->caption_button_container_->GetVisible()); + + EndOverview(); + EXPECT_TRUE(frame_view2->caption_button_container_->GetVisible()); + + auto* immersive_controller = chromeos::ImmersiveFullscreenController::Get( + views::Widget::GetWidgetForNativeView(widget2->GetNativeWindow())); + + // Snap a window. Immersive mode is enabled so its title bar is not visible. + ash::SplitViewTestApi().SnapWindow( + widget2->GetNativeWindow(), ash::SplitViewTestApi::SnapPosition::RIGHT); + EXPECT_TRUE(frame_view2->caption_button_container_->GetVisible()); + EXPECT_TRUE(immersive_controller->IsEnabled()); + + // Float a window. Immersive mode is disabled so its title bar is visible. + ui::test::EventGenerator event_generator( + widget2->GetNativeWindow()->GetRootWindow()); + event_generator.PressAndReleaseKey(ui::VKEY_F, + ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN); + EXPECT_TRUE(frame_view2->caption_button_container_->GetVisible()); + EXPECT_FALSE(immersive_controller->IsEnabled()); +} + namespace { class HomeLauncherBrowserNonClientFrameViewChromeOSTest @@ -1420,6 +1448,7 @@ #if BUILDFLAG(IS_CHROMEOS_ASH) INSTANTIATE_TEST_SUITE(BrowserNonClientFrameViewChromeOSTestWithWebUiTabStrip); INSTANTIATE_TEST_SUITE(WebAppNonClientFrameViewAshTest); +INSTANTIATE_TEST_SUITE(FloatBrowserNonClientFrameViewChromeOSTest); INSTANTIATE_TEST_SUITE(HomeLauncherBrowserNonClientFrameViewChromeOSTest); INSTANTIATE_TEST_SUITE(TabSearchFrameCaptionButtonTest); #endif // BUILDFLAG(IS_CHROMEOS_ASH)
diff --git a/chrome/browser/ui/views/frame/browser_root_view.cc b/chrome/browser/ui/views/frame/browser_root_view.cc index c0c8124..2fe5d24 100644 --- a/chrome/browser/ui/views/frame/browser_root_view.cc +++ b/chrome/browser/ui/views/frame/browser_root_view.cc
@@ -21,6 +21,7 @@ #include "chrome/browser/ui/browser_navigator.h" #include "chrome/browser/ui/color/chrome_color_id.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" +#include "chrome/browser/ui/tabs/tab_strip_user_gesture_details.h" #include "chrome/browser/ui/ui_features.h" #include "chrome/browser/ui/views/frame/browser_frame.h" #include "chrome/browser/ui/views/frame/tab_strip_region_view.h" @@ -280,7 +281,9 @@ if (whole_scroll_offset < 0 && model->active_index() + 1 < model->count()) { chrome::SelectNextTab( - browser, {TabStripModel::GestureType::kWheel, event.time_stamp()}); + browser, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kWheel, + event.time_stamp())); return true; } @@ -288,7 +291,9 @@ // tab-strip. if (whole_scroll_offset > 0 && model->active_index() > 0) { chrome::SelectPreviousTab( - browser, {TabStripModel::GestureType::kWheel, event.time_stamp()}); + browser, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kWheel, + event.time_stamp())); return true; } }
diff --git a/chrome/browser/ui/views/frame/browser_view_browsertest.cc b/chrome/browser/ui/views/frame/browser_view_browsertest.cc index 8138edc..7a19c32 100644 --- a/chrome/browser/ui/views/frame/browser_view_browsertest.cc +++ b/chrome/browser/ui/views/frame/browser_view_browsertest.cc
@@ -141,7 +141,8 @@ chrome::AddTabAt(browser2, GURL(), -1, true); chrome::AddTabAt(browser2, GURL(), -1, true); browser2->tab_strip_model()->ActivateTabAt( - 0, {TabStripModel::GestureType::kOther}); + 0, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); TestWebContentsObserver observer( browser2->tab_strip_model()->GetWebContentsAt(0), browser2->tab_strip_model()->GetWebContentsAt(1)); @@ -249,14 +250,16 @@ // Go to empty tab. Bookmark bar should hide. browser()->tab_strip_model()->ActivateTabAt( - 0, {TabStripModel::GestureType::kOther}); + 0, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); EXPECT_FALSE(bookmark_bar->GetVisible()); EXPECT_EQ(0, observer.change_count()); observer.clear_change_count(); // Go to ntp tab. Bookmark bar should not show. browser()->tab_strip_model()->ActivateTabAt( - 1, {TabStripModel::GestureType::kOther}); + 1, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); EXPECT_FALSE(bookmark_bar->GetVisible()); EXPECT_EQ(0, observer.change_count()); observer.clear_change_count(); @@ -265,13 +268,15 @@ browser()->profile()->GetPrefs()->SetBoolean( bookmarks::prefs::kShowBookmarkBar, true); browser()->tab_strip_model()->ActivateTabAt( - 0, {TabStripModel::GestureType::kOther}); + 0, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); EXPECT_TRUE(bookmark_bar->GetVisible()); EXPECT_EQ(1, observer.change_count()); observer.clear_change_count(); browser()->tab_strip_model()->ActivateTabAt( - 1, {TabStripModel::GestureType::kOther}); + 1, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); EXPECT_TRUE(bookmark_bar->GetVisible()); EXPECT_EQ(0, observer.change_count()); observer.clear_change_count();
diff --git a/chrome/browser/ui/views/frame/immersive_mode_controller_chromeos.cc b/chrome/browser/ui/views/frame/immersive_mode_controller_chromeos.cc index 453a316..e664b47 100644 --- a/chrome/browser/ui/views/frame/immersive_mode_controller_chromeos.cc +++ b/chrome/browser/ui/views/frame/immersive_mode_controller_chromeos.cc
@@ -13,6 +13,7 @@ #include "chrome/browser/ui/views/tabs/tab_strip.h" #include "chromeos/ui/base/tablet_state.h" #include "chromeos/ui/base/window_properties.h" +#include "chromeos/ui/base/window_state_type.h" #include "chromeos/ui/frame/immersive/immersive_revealed_lock.h" #include "content/public/browser/web_contents.h" #include "ui/aura/client/aura_constants.h" @@ -145,10 +146,19 @@ if (platform_util::IsBrowserLockedFullscreen(browser_view_->browser())) return; + // TODO(sammiequon): Investigate if we can move immersive mode logic to the + // browser non client frame view. + DCHECK_EQ(browser_view_->frame(), widget); + if (widget->GetNativeWindow()->GetProperty(chromeos::kWindowStateTypeKey) == + chromeos::WindowStateType::kFloated) { + chromeos::ImmersiveFullscreenController::EnableForWidget(widget, false); + return; + } + // Enable immersive mode if the widget is activated. Do not disable immersive // mode if the widget deactivates, but is not minimized. chromeos::ImmersiveFullscreenController::EnableForWidget( - browser_view_->frame(), active || !widget->IsMinimized()); + widget, active && !widget->IsMinimized()); } void ImmersiveModeControllerChromeos::LayoutBrowserRootView() {
diff --git a/chrome/browser/ui/views/page_info/safety_tip_page_info_bubble_view_browsertest.cc b/chrome/browser/ui/views/page_info/safety_tip_page_info_bubble_view_browsertest.cc index 9ab5d87..c2ead422 100644 --- a/chrome/browser/ui/views/page_info/safety_tip_page_info_bubble_view_browsertest.cc +++ b/chrome/browser/ui/views/page_info/safety_tip_page_info_bubble_view_browsertest.cc
@@ -229,6 +229,34 @@ } } +// Add an allowlist with entries that aren't allowlisted for all domains. +void ConfigureAllowlistWithScopes() { + auto config_proto = reputation::GetOrCreateSafetyTipsConfig(); + config_proto->clear_allowed_pattern(); + config_proto->clear_canonical_pattern(); + config_proto->clear_cohort(); + + // Note: allowed_pattern must be sorted, so "googlee" comes before "gooogle". + + // googlee.com may spoof google.com + config_proto->add_canonical_pattern()->set_pattern("google.com/"); + auto* pattern = config_proto->add_allowed_pattern(); + pattern->set_pattern("googlee.com/"); + auto* cohort = config_proto->add_cohort(); + cohort->add_canonical_index(0); // google.com + pattern->add_cohort_index(0); + + // gooogle.com may spoof blogspot, but not google. + config_proto->add_canonical_pattern()->set_pattern("blogspot.com/"); + pattern = config_proto->add_allowed_pattern(); + pattern->set_pattern("gooogle.com/"); + cohort = config_proto->add_cohort(); + cohort->add_canonical_index(1); // blogspot.com + pattern->add_cohort_index(1); + + reputation::SetSafetyTipsRemoteConfigProto(std::move(config_proto)); +} + } // namespace class SafetyTipPageInfoBubbleViewBrowserTest : public InProcessBrowserTest { @@ -806,11 +834,10 @@ CheckRecordedHeuristicsUkmCount(0); } -// Tests that Safety Tips trigger (or not) on lookalike domains with edit -// distance when enabled, and not otherwise. +// Tests that Safety Tips trigger on lookalike domains with edit distance. IN_PROC_BROWSER_TEST_F(SafetyTipPageInfoBubbleViewBrowserTest, TriggersOnEditDistance) { - // This domain is an edit distance from google.com. + // This domain is one edit from google.com. const GURL kNavigatedUrl = GetURL("goooglé.com"); const GURL kTargetUrl = GetURL("google.com"); SetEngagementScore(browser(), kNavigatedUrl, kLowEngagement); @@ -819,6 +846,34 @@ EXPECT_TRUE(IsUIShowing()); } +// Tests that Safety Tips don't trigger when using a scoped allowlist. +IN_PROC_BROWSER_TEST_F(SafetyTipPageInfoBubbleViewBrowserTest, + DoesntTriggersOnScopedAllowlist) { + // This domain is one edit from google.com, but is allowed to spoof google. + const GURL kNavigatedUrl = GetURL("googlee.com"); + const GURL kTargetUrl = GetURL("google.com"); + ConfigureAllowlistWithScopes(); + SetEngagementScore(browser(), kNavigatedUrl, kLowEngagement); + SetEngagementScore(browser(), kTargetUrl, kHighEngagement); + NavigateToURL(browser(), kNavigatedUrl, WindowOpenDisposition::CURRENT_TAB); + EXPECT_FALSE(IsUIShowing()); + ASSERT_NO_FATAL_FAILURE(CheckPageInfoDoesNotShowSafetyTipInfo(browser())); +} + +// Tests that Safety Tips trigger when the URL is on the allowlist, but is +// scoped to a different domain. +IN_PROC_BROWSER_TEST_F(SafetyTipPageInfoBubbleViewBrowserTest, + TriggersOnWrongScopedAllowlist) { + // This domain is one edit from google.com, and may spoof blogspot.com... + const GURL kNavigatedUrl = GetURL("gooogle.com"); + const GURL kTargetUrl = GetURL("google.com"); + ConfigureAllowlistWithScopes(); + SetEngagementScore(browser(), kNavigatedUrl, kLowEngagement); + SetEngagementScore(browser(), kTargetUrl, kHighEngagement); + NavigateToURL(browser(), kNavigatedUrl, WindowOpenDisposition::CURRENT_TAB); + EXPECT_TRUE(IsUIShowing()); +} + // Tests that Character Swap for engaged sites is disabled by default. IN_PROC_BROWSER_TEST_F(SafetyTipPageInfoBubbleViewBrowserTest, TriggersOnCharacterSwap_SiteEngagement_NotLaunched) {
diff --git a/chrome/browser/ui/views/passwords/password_bubble_interactive_uitest.cc b/chrome/browser/ui/views/passwords/password_bubble_interactive_uitest.cc index 3e0e43e..4bcffb27 100644 --- a/chrome/browser/ui/views/passwords/password_bubble_interactive_uitest.cc +++ b/chrome/browser/ui/views/passwords/password_bubble_interactive_uitest.cc
@@ -288,13 +288,17 @@ ASSERT_TRUE(AddTabAtIndex(1, embedded_test_server()->GetURL("/empty.html"), ui::PAGE_TRANSITION_TYPED)); TabStripModel* tab_model = browser()->tab_strip_model(); - tab_model->ActivateTabAt(1, {TabStripModel::GestureType::kOther}); + tab_model->ActivateTabAt( + 1, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); EXPECT_FALSE(IsBubbleShowing()); EXPECT_EQ(1, tab_model->active_index()); SetupPendingPassword(); EXPECT_TRUE(IsBubbleShowing()); // Back to the first tab. - tab_model->ActivateTabAt(0, {TabStripModel::GestureType::kOther}); + tab_model->ActivateTabAt( + 0, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); EXPECT_FALSE(IsBubbleShowing()); } @@ -304,13 +308,17 @@ ASSERT_TRUE(AddTabAtIndex(1, embedded_test_server()->GetURL("/empty.html"), ui::PAGE_TRANSITION_TYPED)); TabStripModel* tab_model = browser()->tab_strip_model(); - tab_model->ActivateTabAt(1, {TabStripModel::GestureType::kOther}); + tab_model->ActivateTabAt( + 1, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); EXPECT_FALSE(IsBubbleShowing()); EXPECT_EQ(1, tab_model->active_index()); SetupPendingPassword(); EXPECT_TRUE(IsBubbleShowing()); // Back to the first tab. Set up the bubble. - tab_model->ActivateTabAt(0, {TabStripModel::GestureType::kOther}); + tab_model->ActivateTabAt( + 0, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); // Drain message pump to ensure the bubble view is cleared so that it can be // created again (it is checked on Mac to prevent re-opening the bubble when // clicking the location bar button repeatedly).
diff --git a/chrome/browser/ui/views/sad_tab_view_interactive_uitest.cc b/chrome/browser/ui/views/sad_tab_view_interactive_uitest.cc index ac22d80..21b7aa9 100644 --- a/chrome/browser/ui/views/sad_tab_view_interactive_uitest.cc +++ b/chrome/browser/ui/views/sad_tab_view_interactive_uitest.cc
@@ -203,7 +203,9 @@ // Switch back to the first tab. TabStripModel* tab_strip_model = browser()->tab_strip_model(); EXPECT_EQ(1, tab_strip_model->active_index()); - tab_strip_model->ActivateTabAt(0, {TabStripModel::GestureType::kOther}); + tab_strip_model->ActivateTabAt( + 0, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); EXPECT_EQ(0, tab_strip_model->active_index()); content::WebContents* web_contents = tab_strip_model->GetActiveWebContents(); EXPECT_TRUE(web_contents->IsCrashed()); @@ -215,7 +217,9 @@ EXPECT_FALSE(web_contents->IsCrashed()); // Switch to the second tab, reload it too. - tab_strip_model->ActivateTabAt(1, {TabStripModel::GestureType::kOther}); + tab_strip_model->ActivateTabAt( + 1, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); web_contents = tab_strip_model->GetActiveWebContents(); EXPECT_TRUE(web_contents->IsCrashed()); ClickOnActionButtonInSadTab();
diff --git a/chrome/browser/ui/views/side_panel/side_panel_coordinator.cc b/chrome/browser/ui/views/side_panel/side_panel_coordinator.cc index aaaaf35..b5f942f 100644 --- a/chrome/browser/ui/views/side_panel/side_panel_coordinator.cc +++ b/chrome/browser/ui/views/side_panel/side_panel_coordinator.cc
@@ -8,8 +8,6 @@ #include "base/bind.h" #include "base/callback.h" #include "base/callback_forward.h" -#include "base/metrics/user_metrics.h" -#include "base/metrics/user_metrics_action.h" #include "chrome/browser/feature_engagement/tracker_factory.h" #include "chrome/browser/themes/theme_properties.h" #include "chrome/browser/ui/color/chrome_color_id.h" @@ -19,7 +17,6 @@ #include "chrome/browser/ui/views/side_panel/side_panel_combobox_model.h" #include "chrome/browser/ui/views/side_panel/side_panel_content_proxy.h" #include "chrome/browser/ui/views/side_panel/side_panel_entry.h" -#include "chrome/browser/ui/views/side_panel/side_panel_util.h" #include "chrome/browser/ui/views/side_panel/side_panel_web_ui_view.h" #include "chrome/browser/ui/views/toolbar/toolbar_view.h" #include "chrome/common/webui_url_constants.h" @@ -167,7 +164,9 @@ browser_view_->browser()->tab_strip_model()->RemoveObserver(this); } -void SidePanelCoordinator::Show(absl::optional<SidePanelEntry::Id> entry_id) { +void SidePanelCoordinator::Show( + absl::optional<SidePanelEntry::Id> entry_id, + absl::optional<SidePanelUtil::SidePanelOpenTrigger> open_trigger) { if (!entry_id.has_value()) entry_id = GetLastActiveEntryId().value_or(kDefaultEntry); @@ -177,7 +176,8 @@ if (GetContentView() == nullptr) { InitializeSidePanel(); - base::RecordAction(base::UserMetricsAction("SidePanel.Show")); + opened_timestamp_ = base::TimeTicks::Now(); + SidePanelUtil::RecordSidePanelOpen(open_trigger); // Record usage for side panel promo. feature_engagement::TrackerFactory::GetForBrowserContext( browser_view_->GetProfile()) @@ -241,14 +241,14 @@ if (views::View* content_view = GetContentView()) browser_view_->right_aligned_side_panel()->RemoveChildViewT(content_view); header_combobox_ = nullptr; - base::RecordAction(base::UserMetricsAction("SidePanel.Hide")); + SidePanelUtil::RecordSidePanelClosed(opened_timestamp_); } void SidePanelCoordinator::Toggle() { if (IsSidePanelShowing()) { Close(); } else { - Show(); + Show(absl::nullopt, SidePanelUtil::SidePanelOpenTrigger::kToolbarButton); } }
diff --git a/chrome/browser/ui/views/side_panel/side_panel_coordinator.h b/chrome/browser/ui/views/side_panel/side_panel_coordinator.h index 7a919a9..759e27d 100644 --- a/chrome/browser/ui/views/side_panel/side_panel_coordinator.h +++ b/chrome/browser/ui/views/side_panel/side_panel_coordinator.h
@@ -6,10 +6,12 @@ #define CHROME_BROWSER_UI_VIEWS_SIDE_PANEL_SIDE_PANEL_COORDINATOR_H_ #include "base/memory/raw_ptr.h" +#include "base/time/time.h" #include "chrome/browser/ui/tabs/tab_strip_model_observer.h" #include "chrome/browser/ui/views/side_panel/side_panel_entry.h" #include "chrome/browser/ui/views/side_panel/side_panel_registry.h" #include "chrome/browser/ui/views/side_panel/side_panel_registry_observer.h" +#include "chrome/browser/ui/views/side_panel/side_panel_util.h" class BrowserView; class SidePanelComboboxModel; @@ -38,7 +40,9 @@ SidePanelCoordinator& operator=(const SidePanelCoordinator&) = delete; ~SidePanelCoordinator() override; - void Show(absl::optional<SidePanelEntry::Id> entry_id = absl::nullopt); + void Show(absl::optional<SidePanelEntry::Id> entry_id = absl::nullopt, + absl::optional<SidePanelUtil::SidePanelOpenTrigger> open_trigger = + absl::nullopt); void Close(); void Toggle(); @@ -115,6 +119,11 @@ // entries. bool no_delays_for_testing_ = false; + // Timestamp of when the side panel was opened. Updated when the side panel is + // triggered to be opened, not when visibility changes. These can differ due + // to delays for loading content. This is used for metrics. + base::TimeTicks opened_timestamp_; + const raw_ptr<BrowserView> browser_view_; raw_ptr<SidePanelRegistry> global_registry_; absl::optional<SidePanelEntry::Id> last_active_global_entry_id_;
diff --git a/chrome/browser/ui/views/side_panel/side_panel_util.cc b/chrome/browser/ui/views/side_panel/side_panel_util.cc index 9668893..7eaebae 100644 --- a/chrome/browser/ui/views/side_panel/side_panel_util.cc +++ b/chrome/browser/ui/views/side_panel/side_panel_util.cc
@@ -4,6 +4,9 @@ #include "chrome/browser/ui/views/side_panel/side_panel_util.h" +#include "base/metrics/histogram_functions.h" +#include "base/metrics/user_metrics.h" +#include "base/metrics/user_metrics_action.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/ui_features.h" @@ -66,3 +69,18 @@ std::make_unique<SidePanelContentProxy>(true).release()); return content_view->GetProperty(kSidePanelContentProxyKey); } + +void SidePanelUtil::RecordSidePanelOpen( + absl::optional<SidePanelUtil::SidePanelOpenTrigger> trigger) { + base::RecordAction(base::UserMetricsAction("SidePanel.Show")); + + if (trigger.has_value()) + base::UmaHistogramEnumeration("SidePanel.OpenTrigger", trigger.value()); +} + +void SidePanelUtil::RecordSidePanelClosed(base::TimeTicks opened_timestamp) { + base::RecordAction(base::UserMetricsAction("SidePanel.Hide")); + + base::UmaHistogramLongTimes("SidePanel.OpenDuration", + base::TimeTicks::Now() - opened_timestamp); +}
diff --git a/chrome/browser/ui/views/side_panel/side_panel_util.h b/chrome/browser/ui/views/side_panel/side_panel_util.h index 535ff651..ed8b3b8f 100644 --- a/chrome/browser/ui/views/side_panel/side_panel_util.h +++ b/chrome/browser/ui/views/side_panel/side_panel_util.h
@@ -5,6 +5,9 @@ #ifndef CHROME_BROWSER_UI_VIEWS_SIDE_PANEL_SIDE_PANEL_UTIL_H_ #define CHROME_BROWSER_UI_VIEWS_SIDE_PANEL_SIDE_PANEL_UTIL_H_ +#include "base/time/time.h" +#include "third_party/abseil-cpp/absl/types/optional.h" + class Browser; class SidePanelRegistry; class SidePanelContentProxy; @@ -15,6 +18,16 @@ class SidePanelUtil { public: + // These values are persisted to logs. Entries should not be renumbered and + // numeric values should never be reused. + enum class SidePanelOpenTrigger { + kToolbarButton = 0, + kLensContextMenu = 1, + kSideSearchPageAction = 2, + kNotesInPageContextMenu = 3, + kMaxValue = kNotesInPageContextMenu, + }; + static void PopulateGlobalEntries(Browser* browser, SidePanelRegistry* global_registry); @@ -22,6 +35,9 @@ // exist, this creates one indicating the view is available. static SidePanelContentProxy* GetSidePanelContentProxy( views::View* content_view); + + static void RecordSidePanelOpen(absl::optional<SidePanelOpenTrigger> trigger); + static void RecordSidePanelClosed(base::TimeTicks opened_timestamp); }; #endif // CHROME_BROWSER_UI_VIEWS_SIDE_PANEL_SIDE_PANEL_UTIL_H_
diff --git a/chrome/browser/ui/views/side_panel/user_note/user_note_ui_coordinator.cc b/chrome/browser/ui/views/side_panel/user_note/user_note_ui_coordinator.cc index 15bf0c97..81fa0162 100644 --- a/chrome/browser/ui/views/side_panel/user_note/user_note_ui_coordinator.cc +++ b/chrome/browser/ui/views/side_panel/user_note/user_note_ui_coordinator.cc
@@ -241,7 +241,9 @@ void UserNoteUICoordinator::Show() { auto* side_panel_coordinator = BrowserView::GetBrowserViewForBrowser(browser_)->side_panel_coordinator(); - side_panel_coordinator->Show(SidePanelEntry::Id::kUserNote); + side_panel_coordinator->Show( + SidePanelEntry::Id::kUserNote, + SidePanelUtil::SidePanelOpenTrigger::kNotesInPageContextMenu); } void UserNoteUICoordinator::OnTabStripModelChanged(
diff --git a/chrome/browser/ui/views/tab_sharing/tab_sharing_ui_views_browsertest.cc b/chrome/browser/ui/views/tab_sharing/tab_sharing_ui_views_browsertest.cc index edd8dd2d..0781aac 100644 --- a/chrome/browser/ui/views/tab_sharing/tab_sharing_ui_views_browsertest.cc +++ b/chrome/browser/ui/views/tab_sharing/tab_sharing_ui_views_browsertest.cc
@@ -15,6 +15,7 @@ #include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_list.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" +#include "chrome/browser/ui/tabs/tab_strip_user_gesture_details.h" #include "chrome/browser/ui/views/frame/browser_view.h" #include "chrome/common/webui_url_constants.h" #include "chrome/grit/generated_resources.h" @@ -123,7 +124,8 @@ void ActivateTab(Browser* browser, int tab) { browser->tab_strip_model()->ActivateTabAt( - tab, {TabStripModel::GestureType::kMouse}); + tab, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kMouse)); } constexpr int kNullTabIndex = -1;
diff --git a/chrome/browser/ui/views/tabs/browser_tab_strip_controller.cc b/chrome/browser/ui/views/tabs/browser_tab_strip_controller.cc index 288ca84..4fc2e72 100644 --- a/chrome/browser/ui/views/tabs/browser_tab_strip_controller.cc +++ b/chrome/browser/ui/views/tabs/browser_tab_strip_controller.cc
@@ -37,6 +37,7 @@ #include "chrome/browser/ui/tabs/tab_renderer_data.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" #include "chrome/browser/ui/tabs/tab_strip_model_delegate.h" +#include "chrome/browser/ui/tabs/tab_strip_user_gesture_details.h" #include "chrome/browser/ui/tabs/tab_utils.h" #include "chrome/browser/ui/ui_features.h" #include "chrome/browser/ui/user_education/reopen_tab_in_product_help.h" @@ -270,13 +271,14 @@ content::PeakGpuMemoryTracker::Create( content::PeakGpuMemoryTracker::Usage::CHANGE_TAB); - TabStripModel::UserGestureDetails gesture_detail( - TabStripModel::GestureType::kOther, event.time_stamp()); - TabStripModel::GestureType type = TabStripModel::GestureType::kOther; + TabStripUserGestureDetails gesture_detail( + TabStripUserGestureDetails::GestureType::kOther, event.time_stamp()); + TabStripUserGestureDetails::GestureType type = + TabStripUserGestureDetails::GestureType::kOther; if (event.type() == ui::ET_MOUSE_PRESSED) - type = TabStripModel::GestureType::kMouse; + type = TabStripUserGestureDetails::GestureType::kMouse; else if (event.type() == ui::ET_GESTURE_TAP_DOWN) - type = TabStripModel::GestureType::kTouch; + type = TabStripUserGestureDetails::GestureType::kTouch; gesture_detail.type = type; model_->ActivateTabAt(model_index, gesture_detail); @@ -390,14 +392,18 @@ base::RecordAction(base::UserMetricsAction("TabGroups_CannotCollapse")); return false; } - model_->ActivateTabAt(next_active.value(), - {TabStripModel::GestureType::kOther}); + model_->ActivateTabAt( + next_active.value(), + TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); } else { // If the active tab is not in the group that is toggling to collapse, // reactive the active tab to deselect any other potentially selected // tabs. - model_->ActivateTabAt(GetActiveIndex(), - {TabStripModel::GestureType::kOther}); + model_->ActivateTabAt( + GetActiveIndex(), + TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); } if (origin != ToggleTabGroupCollapsedStateOrigin::kImplicitAction) { base::RecordAction(
diff --git a/chrome/browser/ui/views/tabs/tab_scrubber_chromeos.cc b/chrome/browser/ui/views/tabs/tab_scrubber_chromeos.cc index dc11c37..6fa988a8 100644 --- a/chrome/browser/ui/views/tabs/tab_scrubber_chromeos.cc +++ b/chrome/browser/ui/views/tabs/tab_scrubber_chromeos.cc
@@ -274,7 +274,9 @@ UMA_HISTOGRAM_TIMES("Tabs.ScrubDuration", base::TimeTicks::Now() - scrubbing_start_time_); browser_->tab_strip_model()->ActivateTabAt( - highlighted_tab_, {TabStripModel::GestureType::kOther}); + highlighted_tab_, + TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); } tab_strip->RemoveObserver(this); }
diff --git a/chrome/browser/ui/views/task_manager_view_browsertest.cc b/chrome/browser/ui/views/task_manager_view_browsertest.cc index 12d1647..2fa5731 100644 --- a/chrome/browser/ui/views/task_manager_view_browsertest.cc +++ b/chrome/browser/ui/views/task_manager_view_browsertest.cc
@@ -237,7 +237,8 @@ // Activate tab 0. The selection should not change. browser()->tab_strip_model()->ActivateTabAt( - 0, {TabStripModel::GestureType::kOther}); + 0, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); EXPECT_EQ(1UL, GetTable()->selection_model().size()); EXPECT_EQ(GetTable()->GetFirstSelectedRow(), FindRowForTab(browser()->tab_strip_model()->GetWebContentsAt(1)));
diff --git a/chrome/browser/ui/views/webauthn/authenticator_request_sheet_view.cc b/chrome/browser/ui/views/webauthn/authenticator_request_sheet_view.cc index 97f1d19..263356a 100644 --- a/chrome/browser/ui/views/webauthn/authenticator_request_sheet_view.cc +++ b/chrome/browser/ui/views/webauthn/authenticator_request_sheet_view.cc
@@ -172,13 +172,15 @@ views::LayoutProvider::Get()->GetDistanceMetric( views::DISTANCE_RELATED_CONTROL_VERTICAL))); - auto title_label = std::make_unique<views::Label>( - model()->GetStepTitle(), views::style::CONTEXT_DIALOG_TITLE, - views::style::STYLE_PRIMARY); - title_label->SetMultiLine(true); - title_label->SetHorizontalAlignment(gfx::ALIGN_LEFT); - title_label->SetAllowCharacterBreak(true); - label_container->AddChildView(title_label.release()); + const std::u16string title = model()->GetStepTitle(); + if (!title.empty()) { + auto title_label = std::make_unique<views::Label>( + title, views::style::CONTEXT_DIALOG_TITLE, views::style::STYLE_PRIMARY); + title_label->SetMultiLine(true); + title_label->SetHorizontalAlignment(gfx::ALIGN_LEFT); + title_label->SetAllowCharacterBreak(true); + label_container->AddChildView(title_label.release()); + } std::u16string description = model()->GetStepDescription(); if (!description.empty()) {
diff --git a/chrome/browser/ui/views/webauthn/sheet_view_factory.cc b/chrome/browser/ui/views/webauthn/sheet_view_factory.cc index 769865e..b0c2db7 100644 --- a/chrome/browser/ui/views/webauthn/sheet_view_factory.cc +++ b/chrome/browser/ui/views/webauthn/sheet_view_factory.cc
@@ -7,6 +7,7 @@ #include <utility> #include "base/check.h" +#include "build/build_config.h" #include "chrome/browser/ui/autofill/payments/webauthn_dialog_model.h" #include "chrome/browser/ui/views/webauthn/authenticator_bio_enrollment_sheet_view.h" #include "chrome/browser/ui/views/webauthn/authenticator_client_pin_entry_sheet_view.h" @@ -127,6 +128,13 @@ std::make_unique<AuthenticatorBlePowerOnManualSheetModel>( dialog_model)); break; +#if BUILDFLAG(IS_MAC) + case Step::kBlePermissionMac: + sheet_view = std::make_unique<AuthenticatorRequestSheetView>( + std::make_unique<AuthenticatorBlePermissionMacSheetModel>( + dialog_model)); + break; +#endif case Step::kOffTheRecordInterstitial: sheet_view = std::make_unique<AuthenticatorRequestSheetView>( std::make_unique<AuthenticatorOffTheRecordInterstitialSheetModel>(
diff --git a/chrome/browser/ui/web_applications/web_app_launch_process.cc b/chrome/browser/ui/web_applications/web_app_launch_process.cc index 4846dc0..6088fa9 100644 --- a/chrome/browser/ui/web_applications/web_app_launch_process.cc +++ b/chrome/browser/ui/web_applications/web_app_launch_process.cc
@@ -17,7 +17,7 @@ #include "chrome/browser/ui/browser_navigator.h" #include "chrome/browser/ui/browser_navigator_params.h" #include "chrome/browser/ui/browser_window.h" -#include "chrome/browser/ui/tabs/tab_strip_model.h" +#include "chrome/browser/ui/tabs/tab_strip_user_gesture_details.h" #include "chrome/browser/ui/web_applications/app_browser_controller.h" #include "chrome/browser/ui/web_applications/share_target_utils.h" #include "chrome/browser/ui/web_applications/system_web_app_ui_utils.h" @@ -345,7 +345,9 @@ /*is_renderer_initiated=*/false)); content::WebContents* web_contents = tab_strip->GetActiveWebContents(); - tab_strip->ActivateTabAt(tab_index, {TabStripModel::GestureType::kOther}); + tab_strip->ActivateTabAt( + tab_index, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); SetWebContentsActingAsApp(web_contents, params_.app_id); return {.web_contents = web_contents, .did_navigate = true}; }
diff --git a/chrome/browser/ui/webauthn/authenticator_dialog_browsertest.cc b/chrome/browser/ui/webauthn/authenticator_dialog_browsertest.cc index b74b6ff..44840e65 100644 --- a/chrome/browser/ui/webauthn/authenticator_dialog_browsertest.cc +++ b/chrome/browser/ui/webauthn/authenticator_dialog_browsertest.cc
@@ -268,6 +268,12 @@ model_->SetCurrentStepForTesting( AuthenticatorRequestDialogModel::Step::kCableActivate); } +#if BUILDFLAG(IS_MAC) + else if (name == "ble_permission_mac") { + model_->SetCurrentStepForTesting( + AuthenticatorRequestDialogModel::Step::kBlePermissionMac); + } +#endif #define EXP_SHEET(x) \ else if (name == "server_link_sheet_" #x) { \ @@ -480,3 +486,9 @@ EXP_SHEET(ARM_5) EXP_SHEET(ARM_6) #undef EXP_SHEET + +#if BUILDFLAG(IS_MAC) +IN_PROC_BROWSER_TEST_F(AuthenticatorDialogTest, InvokeUi_ble_permission_mac) { + ShowAndVerifyUi(); +} +#endif
diff --git a/chrome/browser/ui/webauthn/sheet_models.cc b/chrome/browser/ui/webauthn/sheet_models.cc index a3ef562..01c11a8cf 100644 --- a/chrome/browser/ui/webauthn/sheet_models.cc +++ b/chrome/browser/ui/webauthn/sheet_models.cc
@@ -495,6 +495,52 @@ dialog_model()->PowerOnBleAdapter(); } +#if BUILDFLAG(IS_MAC) + +// AuthenticatorBlePermissionMacSheetModel +// ------------------------------------ + +const gfx::VectorIcon& +AuthenticatorBlePermissionMacSheetModel::GetStepIllustration( + ImageColorScheme color_scheme) const { + return color_scheme == ImageColorScheme::kDark + ? kWebauthnErrorBluetoothDarkIcon + : kWebauthnErrorBluetoothIcon; +} + +std::u16string AuthenticatorBlePermissionMacSheetModel::GetStepTitle() const { + // An empty title causes the title View to be omitted. + return u""; +} + +std::u16string AuthenticatorBlePermissionMacSheetModel::GetStepDescription() + const { + return l10n_util::GetStringUTF16(IDS_WEBAUTHN_BLUETOOTH_PERMISSION); +} + +bool AuthenticatorBlePermissionMacSheetModel::IsAcceptButtonVisible() const { + return true; +} + +bool AuthenticatorBlePermissionMacSheetModel::IsAcceptButtonEnabled() const { + return true; +} + +bool AuthenticatorBlePermissionMacSheetModel::IsCancelButtonVisible() const { + return false; +} + +std::u16string AuthenticatorBlePermissionMacSheetModel::GetAcceptButtonLabel() + const { + return l10n_util::GetStringUTF16(IDS_OPEN_PREFERENCES_LINK); +} + +void AuthenticatorBlePermissionMacSheetModel::OnAccept() { + dialog_model()->OpenBlePreferences(); +} + +#endif // IS_MAC + // AuthenticatorOffTheRecordInterstitialSheetModel // -----------------------------------------
diff --git a/chrome/browser/ui/webauthn/sheet_models.h b/chrome/browser/ui/webauthn/sheet_models.h index 32f2981..212ed23 100644 --- a/chrome/browser/ui/webauthn/sheet_models.h +++ b/chrome/browser/ui/webauthn/sheet_models.h
@@ -9,6 +9,7 @@ #include <string> #include "base/memory/raw_ptr.h" +#include "build/build_config.h" #include "chrome/browser/ui/webauthn/authenticator_request_sheet_model.h" #include "chrome/browser/ui/webauthn/transport_hover_list_model.h" #include "chrome/browser/webauthn/authenticator_request_dialog_model.h" @@ -232,6 +233,28 @@ bool busy_powering_on_ble_ = false; }; +#if BUILDFLAG(IS_MAC) + +class AuthenticatorBlePermissionMacSheetModel + : public AuthenticatorSheetModelBase { + public: + using AuthenticatorSheetModelBase::AuthenticatorSheetModelBase; + + private: + // AuthenticatorSheetModelBase: + const gfx::VectorIcon& GetStepIllustration( + ImageColorScheme color_scheme) const override; + std::u16string GetStepTitle() const override; + std::u16string GetStepDescription() const override; + bool IsAcceptButtonVisible() const override; + bool IsAcceptButtonEnabled() const override; + bool IsCancelButtonVisible() const override; + std::u16string GetAcceptButtonLabel() const override; + void OnAccept() override; +}; + +#endif // IS_MAC + class AuthenticatorOffTheRecordInterstitialSheetModel : public AuthenticatorSheetModelBase { public:
diff --git a/chrome/browser/ui/webui/print_preview/print_preview_ui_browsertest.cc b/chrome/browser/ui/webui/print_preview/print_preview_ui_browsertest.cc index 2ff2872..a18c6f3 100644 --- a/chrome/browser/ui/webui/print_preview/print_preview_ui_browsertest.cc +++ b/chrome/browser/ui/webui/print_preview/print_preview_ui_browsertest.cc
@@ -140,13 +140,15 @@ ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP); browser()->tab_strip_model()->ActivateTabAt( - 0, {TabStripModel::GestureType::kOther}); + 0, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); // Navigate main tab to hide print preview. ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GURL("about:blank"))); browser()->tab_strip_model()->ActivateTabAt( - 1, {TabStripModel::GestureType::kOther}); + 1, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); } #endif // BUILDFLAG(IS_WIN)
diff --git a/chrome/browser/webauthn/authenticator_request_dialog_model.cc b/chrome/browser/webauthn/authenticator_request_dialog_model.cc index 25f3e6e..2b3df8d 100644 --- a/chrome/browser/webauthn/authenticator_request_dialog_model.cc +++ b/chrome/browser/webauthn/authenticator_request_dialog_model.cc
@@ -12,6 +12,7 @@ #include "base/containers/contains.h" #include "base/metrics/histogram_functions.h" #include "base/observer_list.h" +#include "base/process/launch.h" #include "base/strings/utf_string_conversions.h" #include "base/threading/sequenced_task_runner_handle.h" #include "build/build_config.h" @@ -27,6 +28,10 @@ #include "ui/base/l10n/l10n_util.h" #include "ui/gfx/text_elider.h" +#if BUILDFLAG(IS_MAC) +#include "base/mac/mac_util.h" +#endif + namespace { // BleEvent enumerates user-visible BLE events. @@ -262,6 +267,21 @@ current_step() == Step::kOffTheRecordInterstitial || current_step() == Step::kNotStarted); +#if BUILDFLAG(IS_MAC) + // The BLE permission screen is only shown on macOS <= 12 because: + // * System Preferences has been renamed to System Settings, so the + // string on the button would need to be changed. + // * Opening Preferences/Settings at the BLE permissions page is broken. + if (transport_availability()->ble_access_denied && + base::mac::IsAtMostOS12()) { + // |step| is not saved because macOS asks the user to restart Chrome + // after permission has been granted. So the user will end up retrying + // the whole WebAuthn request in the new process. + SetCurrentStep(Step::kBlePermissionMac); + return; + } +#endif + if (ble_adapter_is_powered()) { base::UmaHistogramEnumeration("WebAuthentication.BLEUserEvents", BleEvent::kAlreadyPowered); @@ -303,6 +323,19 @@ bluetooth_adapter_power_on_callback_.Run(); } +#if BUILDFLAG(IS_MAC) +void AuthenticatorRequestDialogModel::OpenBlePreferences() { + DCHECK_EQ(current_step(), Step::kBlePermissionMac); + + base::LaunchOptions opts; + opts.disclaim_responsibility = true; + base::LaunchProcess({"open", + "x-apple.systempreferences:com.apple.preference." + "security?Privacy_Bluetooth"}, + opts); +} +#endif // IS_MAC + void AuthenticatorRequestDialogModel::TryUsbDevice() { DCHECK_EQ(current_step(), Step::kUsbInsertAndActivate); }
diff --git a/chrome/browser/webauthn/authenticator_request_dialog_model.h b/chrome/browser/webauthn/authenticator_request_dialog_model.h index e62df6d..3ac3c6e 100644 --- a/chrome/browser/webauthn/authenticator_request_dialog_model.h +++ b/chrome/browser/webauthn/authenticator_request_dialog_model.h
@@ -86,6 +86,9 @@ // Bluetooth Low Energy (BLE). kBlePowerOnAutomatic, kBlePowerOnManual, +#if BUILDFLAG(IS_MAC) + kBlePermissionMac, +#endif // Let the user confirm that they want to create a credential in an // off-the-record browsing context. Used for platform and caBLE credentials, @@ -345,6 +348,11 @@ // Valid action when at step: kBlePowerOnAutomatic. void PowerOnBleAdapter(); + // Open the system dialog to grant BLE permission to Chrome. + // + // Valid action when at step: kBlePermissionMac. + void OpenBlePreferences(); + // Tries if a USB device is present -- the user claims they plugged it in. // // Valid action when at step: kUsbInsert.
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt index 0f75b455..688dcf96 100644 --- a/chrome/build/linux.pgo.txt +++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@ -chrome-linux-main-1656331133-e188c75043fb172bb25e56585a65bfb4d555a028.profdata +chrome-linux-main-1656352780-29b864beabc43da931f35ed5e96ec6730b553ff3.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt index 0eae1728..90cc493 100644 --- a/chrome/build/win32.pgo.txt +++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@ -chrome-win32-main-1656331133-a87a490e3491ee6b22dae312d1d5347435280cba.profdata +chrome-win32-main-1656352780-5daf3e33415b6f908ce296574bcb01e54bb545c7.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt index e9c6e43..4585cb4 100644 --- a/chrome/build/win64.pgo.txt +++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@ -chrome-win64-main-1656331133-4becbbd6a2c3171b7cf4baeaf684180260bd6617.profdata +chrome-win64-main-1656352780-6a26ecd34ca03ea103fa8e994bbbe865a5597d76.profdata
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn index cb667d6..2f8eb3cc 100644 --- a/chrome/test/BUILD.gn +++ b/chrome/test/BUILD.gn
@@ -6857,6 +6857,7 @@ "//chrome/browser/ui/webui/new_tab_page:mojo_bindings", "//chrome/browser/web_applications:web_applications_test_support", "//components/app_constants", + "//components/autofill_assistant/browser/public:proto", "//components/autofill_assistant/browser/public:unit_test_support", "//components/chrome_cleaner/test:test_name_helper", "//components/endpoint_fetcher:endpoint_fetcher",
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/ChromeActivityTestRule.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/ChromeActivityTestRule.java index eff689e..b2f6643 100644 --- a/chrome/test/android/javatests/src/org/chromium/chrome/test/ChromeActivityTestRule.java +++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/ChromeActivityTestRule.java
@@ -200,7 +200,11 @@ public void startActivityCompletely(Intent intent) { launchActivity(intent); waitForActivityNativeInitializationComplete(); + waitForActivityCompletelyLoaded(); + } + /** Wait until the activity is completely loaded, and a tab is shown. */ + public void waitForActivityCompletelyLoaded() { CriteriaHelper.pollUiThread( () -> getActivity().getActivityTab() != null, "Tab never selected/initialized."); Tab tab = getActivity().getActivityTab();
diff --git a/chrome/test/data/policy/policy_test_cases.json b/chrome/test/data/policy/policy_test_cases.json index 137d188..106139f 100644 --- a/chrome/test/data/policy/policy_test_cases.json +++ b/chrome/test/data/policy/policy_test_cases.json
@@ -18376,7 +18376,7 @@ }, "prefs": { "policy.set_timeout_without_1ms_clamp": { - "location": "local_state" + "location": "user_profile" } } }
diff --git a/chrome/test/data/webui/BUILD.gn b/chrome/test/data/webui/BUILD.gn index 9137841..e4ab3d2 100644 --- a/chrome/test/data/webui/BUILD.gn +++ b/chrome/test/data/webui/BUILD.gn
@@ -561,6 +561,8 @@ in_files = [ "chrome_timeticks_test.ts", "color_provider_css_colors_test.ts", + "cr_focus_row_behavior_test.ts", + "resources/list_property_update_behavior_tests.ts", "resources/list_property_update_mixin_tests.ts", "text_defaults_test.ts", ]
diff --git a/chrome/test/data/webui/cr_focus_row_behavior_interactive_test.js b/chrome/test/data/webui/cr_focus_row_behavior_interactive_test.js index 064729c8..40e8c66 100644 --- a/chrome/test/data/webui/cr_focus_row_behavior_interactive_test.js +++ b/chrome/test/data/webui/cr_focus_row_behavior_interactive_test.js
@@ -15,7 +15,7 @@ var CrFocusRowBehaviorTest = class extends PolymerInteractiveUITest { /** @override */ get browsePreload() { - return 'chrome://test/test_loader.html?module=cr_focus_row_behavior_test.js&host=test'; + return 'chrome://test/test_loader.html?module=cr_focus_row_behavior_test.js'; } /** @override */
diff --git a/chrome/test/data/webui/cr_focus_row_behavior_test.js b/chrome/test/data/webui/cr_focus_row_behavior_test.js deleted file mode 100644 index 82179c71..0000000 --- a/chrome/test/data/webui/cr_focus_row_behavior_test.js +++ /dev/null
@@ -1,163 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// clang-format off -import {FocusRowBehavior} from 'chrome://resources/js/cr/ui/focus_row_behavior.m.js'; -import {getDeepActiveElement} from 'chrome://resources/js/util.m.js'; -import {down, pressAndReleaseKeyOn, up} from 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.js'; -import {html, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js'; -import {eventToPromise, waitAfterNextRender} from 'chrome://test/test_util.js'; - -// clang-format on - -suite('cr-focus-row-behavior-test', function() { - /** @type {FocusableIronListItemElement} */ let testElement; - - suiteSetup(function() { - Polymer({ - is: 'button-three', - - _template: html` - <button> - fake button three - </button> - `, - - /** @return {!Element} */ - getFocusableElement: function() { - return this.$$('button'); - }, - }); - - Polymer({ - is: 'focus-row-element', - - _template: html` - <div id="container" focus-row-container> - <span>fake text</span> - <button id="control" focus-row-control focus-type='fake-btn'> - fake button - </button> - <button id="controlTwo" focus-row-control focus-type='fake-btn-two'> - fake button two - </button> - <button-three focus-row-control focus-type='fake-btn-three'> - </button-three> - </div> - `, - - behaviors: [FocusRowBehavior], - focusCallCount: 0, - - focus: function() { - this.focusCallCount++; - }, - }); - }); - - setup(async function() { - PolymerTest.clearBody(); - - testElement = document.createElement('focus-row-element'); - document.body.appendChild(testElement); - - // Block so that FocusRowBehavior.attached can run. - await waitAfterNextRender(testElement); - // Wait one more time to ensure that async setup in FocusRowBehavior has - // executed. - await waitAfterNextRender(testElement); - }); - - test('ID is not overriden when index is set', function() { - assertFalse(testElement.hasAttribute('id')); - assertFalse(testElement.hasAttribute('aria-rowindex')); - testElement.id = 'test-id'; - assertTrue(testElement.hasAttribute('id')); - assertEquals('test-id', testElement.id); - assertFalse(testElement.hasAttribute('aria-rowindex')); - testElement.focusRowIndex = 5; // Arbitrary index. - assertTrue(testElement.hasAttribute('id')); - assertEquals('test-id', testElement.id); - assertTrue(testElement.hasAttribute('aria-rowindex')); - }); - - test('ID and aria-rowindex are only set when index is set', function() { - assertFalse(testElement.hasAttribute('id')); - assertFalse(testElement.hasAttribute('aria-rowindex')); - testElement.focusRowIndex = 5; // Arbitrary index. - assertTrue(testElement.hasAttribute('id')); - assertTrue(testElement.hasAttribute('aria-rowindex')); - }); - - test('item passes focus to first focusable child', function() { - let focused = false; - testElement.$.control.addEventListener('focus', function() { - focused = true; - }); - testElement.fire('focus'); - assertTrue(focused); - }); - - test('will focus a similar item that was last focused', function() { - const lastButton = document.createElement('button'); - lastButton.setAttribute('focus-type', 'fake-btn-two'); - testElement.lastFocused = lastButton; - - let focused = false; - testElement.$.controlTwo.addEventListener('focus', function() { - focused = true; - }); - testElement.fire('focus'); - assertTrue(focused); - }); - - test('mouse clicks on the row does not focus the controls', function() { - let focused = false; - testElement.$.control.addEventListener('focus', function() { - focused = true; - }); - down(testElement); - up(testElement); - testElement.click(); - // iron-list is responsible for firing 'focus' after taps, but is not used - // in the test, so its necessary to manually fire 'focus' after tap. - testElement.fire('focus'); - assertFalse(focused); - }); - - test('when focus-override is defined, returned element gains focus', () => { - const lastButton = document.createElement('button'); - lastButton.setAttribute('focus-type', 'fake-btn-three'); - testElement.lastFocused = lastButton; - - const wait = eventToPromise('focus', testElement); - testElement.fire('focus'); - return wait.then(() => { - const button = getDeepActiveElement(); - assertEquals('fake button three', button.textContent.trim()); - }); - }); - - test('when shift+tab pressed on first control, focus on container', () => { - const first = testElement.$.control; - const second = testElement.$.controlTwo; - pressAndReleaseKeyOn(first, '', 'shift', 'Tab'); - assertEquals(1, testElement.focusCallCount); - pressAndReleaseKeyOn(second, '', 'shift', 'Tab'); - assertEquals(1, testElement.focusCallCount); - - // Simulate updating a row with same first control. - testElement.fire('dom-change'); - pressAndReleaseKeyOn(first, '', 'shift', 'Tab'); - assertEquals(2, testElement.focusCallCount); - pressAndReleaseKeyOn(second, '', 'shift', 'Tab'); - assertEquals(2, testElement.focusCallCount); - - // Simulate updating row with different first control. - first.remove(); - testElement.fire('dom-change'); - pressAndReleaseKeyOn(second, '', 'shift', 'Tab'); - assertEquals(3, testElement.focusCallCount); - }); -});
diff --git a/chrome/test/data/webui/cr_focus_row_behavior_test.ts b/chrome/test/data/webui/cr_focus_row_behavior_test.ts new file mode 100644 index 0000000..a593917 --- /dev/null +++ b/chrome/test/data/webui/cr_focus_row_behavior_test.ts
@@ -0,0 +1,189 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// clang-format off +import {FocusRowBehavior} from 'chrome://resources/js/cr/ui/focus_row_behavior.m.js'; +import {getDeepActiveElement} from 'chrome://resources/js/util.m.js'; +import {down, pressAndReleaseKeyOn, up} from 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.js'; +import {html, PolymerElement, mixinBehaviors} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js'; +import {eventToPromise, waitAfterNextRender} from 'chrome://webui-test/test_util.js'; +import {assertFalse, assertTrue, assertEquals} from 'chrome://webui-test/chai_assert.js'; + +// clang-format on + +class ButtonThreeElement extends PolymerElement { + static get is() { + return 'button-three'; + } + + static get template() { + return html` + <button> + fake button three + </button> + `; + } + + getFocusableElement() { + return this.shadowRoot!.querySelector('button'); + } +} +customElements.define(ButtonThreeElement.is, ButtonThreeElement); + +const TestElementBase = mixinBehaviors([FocusRowBehavior], PolymerElement) as + {new (): PolymerElement & FocusRowBehavior}; + +interface TestFocusRowBehaviorElement { + $: { + control: HTMLElement, + controlTwo: HTMLElement, + }; +} + +class TestFocusRowBehaviorElement extends TestElementBase { + static get is() { + return 'test-focus-row-behavior-element'; + } + + static get template() { + return html` + <div id="container" focus-row-container> + <span>fake text</span> + <button id="control" focus-row-control focus-type='fake-btn'> + fake button + </button> + <button id="controlTwo" focus-row-control focus-type='fake-btn-two'> + fake button two + </button> + <button-three focus-row-control focus-type='fake-btn-three'> + </button-three> + </div> + `; + } + + focusCallCount: number = 0; + + override focus() { + this.focusCallCount++; + } +} +customElements.define( + TestFocusRowBehaviorElement.is, TestFocusRowBehaviorElement); + +declare global { + interface HTMLElementTagNameMap { + 'test-focus-row-behavior-element': TestFocusRowBehaviorElement; + } +} + + +suite('cr-focus-row-behavior-test', function() { + let testElement: TestFocusRowBehaviorElement; + + setup(async function() { + document.body.innerHTML = ''; + + testElement = document.createElement('test-focus-row-behavior-element'); + document.body.appendChild(testElement); + + // Block so that FocusRowBehavior.attached can run. + await waitAfterNextRender(testElement); + // Wait one more time to ensure that async setup in FocusRowBehavior has + // executed. + await waitAfterNextRender(testElement); + }); + + test('ID is not overriden when index is set', function() { + assertFalse(testElement.hasAttribute('id')); + assertFalse(testElement.hasAttribute('aria-rowindex')); + testElement.id = 'test-id'; + assertTrue(testElement.hasAttribute('id')); + assertEquals('test-id', testElement.id); + assertFalse(testElement.hasAttribute('aria-rowindex')); + testElement.focusRowIndex = 5; // Arbitrary index. + assertTrue(testElement.hasAttribute('id')); + assertEquals('test-id', testElement.id); + assertTrue(testElement.hasAttribute('aria-rowindex')); + }); + + test('ID and aria-rowindex are only set when index is set', function() { + assertFalse(testElement.hasAttribute('id')); + assertFalse(testElement.hasAttribute('aria-rowindex')); + testElement.focusRowIndex = 5; // Arbitrary index. + assertTrue(testElement.hasAttribute('id')); + assertTrue(testElement.hasAttribute('aria-rowindex')); + }); + + test('item passes focus to first focusable child', function() { + let focused = false; + testElement.$.control.addEventListener('focus', function() { + focused = true; + }); + testElement.dispatchEvent(new CustomEvent('focus')); + assertTrue(focused); + }); + + test('will focus a similar item that was last focused', function() { + const lastButton = document.createElement('button'); + lastButton.setAttribute('focus-type', 'fake-btn-two'); + testElement.lastFocused = lastButton; + + let focused = false; + testElement.$.controlTwo.addEventListener('focus', function() { + focused = true; + }); + testElement.dispatchEvent(new CustomEvent('focus')); + assertTrue(focused); + }); + + test('mouse clicks on the row does not focus the controls', function() { + let focused = false; + testElement.$.control.addEventListener('focus', function() { + focused = true; + }); + down(testElement); + up(testElement); + testElement.click(); + // iron-list is responsible for firing 'focus' after taps, but is not used + // in the test, so its necessary to manually fire 'focus' after tap. + testElement.dispatchEvent(new CustomEvent('focus')); + assertFalse(focused); + }); + + test('when focus-override is defined, returned element gains focus', () => { + const lastButton = document.createElement('button'); + lastButton.setAttribute('focus-type', 'fake-btn-three'); + testElement.lastFocused = lastButton; + + const wait = eventToPromise('focus', testElement); + testElement.dispatchEvent(new CustomEvent('focus')); + return wait.then(() => { + const button = getDeepActiveElement(); + assertTrue(!!button); + assertEquals('fake button three', button.textContent!.trim()); + }); + }); + + test('when shift+tab pressed on first control, focus on container', () => { + const first = testElement.$.control; + const second = testElement.$.controlTwo; + pressAndReleaseKeyOn(first, 0, 'shift', 'Tab'); + assertEquals(1, testElement.focusCallCount); + pressAndReleaseKeyOn(second, 0, 'shift', 'Tab'); + assertEquals(1, testElement.focusCallCount); + + // Simulate updating a row with same first control. + testElement.dispatchEvent(new CustomEvent('dom-change')); + pressAndReleaseKeyOn(first, 0, 'shift', 'Tab'); + assertEquals(2, testElement.focusCallCount); + pressAndReleaseKeyOn(second, 0, 'shift', 'Tab'); + assertEquals(2, testElement.focusCallCount); + + // Simulate updating row with different first control. + first.remove(); + testElement.dispatchEvent(new CustomEvent('dom-change')); + pressAndReleaseKeyOn(second, 0, 'shift', 'Tab'); + assertEquals(3, testElement.focusCallCount); + }); +});
diff --git a/chrome/test/data/webui/js/BUILD.gn b/chrome/test/data/webui/js/BUILD.gn index b546bbb1..507ad1c 100644 --- a/chrome/test/data/webui/js/BUILD.gn +++ b/chrome/test/data/webui/js/BUILD.gn
@@ -32,11 +32,7 @@ "parse_html_subset_test.ts", "parse_html_subset_trusted_types_test.ts", "promise_resolver_test.ts", - - # TODO(1189595): Need to add TypeScript definitions for TrustedHTML before - # passing this file to the TS Compiler. - #"static_types_test.js", - + "static_types_test.ts", "util_test.ts", "web_ui_listener_mixin_test.ts",
diff --git a/chrome/test/data/webui/js/static_types_test.js b/chrome/test/data/webui/js/static_types_test.ts similarity index 87% rename from chrome/test/data/webui/js/static_types_test.js rename to chrome/test/data/webui/js/static_types_test.ts index 4557488..e2789d4 100644 --- a/chrome/test/data/webui/js/static_types_test.js +++ b/chrome/test/data/webui/js/static_types_test.ts
@@ -3,7 +3,8 @@ // found in the LICENSE file. import {getTrustedHTML, getTrustedScript, getTrustedScriptURL} from 'chrome://resources/js/static_types.js'; -import {assertEquals, assertNotReached} from '../chai_assert.js'; + +import {assertEquals, assertNotReached, assertThrows} from '../chai_assert.js'; suite('StaticTypesTest', function() { test('compatible with Trusted Types', () => { @@ -42,7 +43,7 @@ }); test('throws when invalid', () => { - const ensureThrows = function(arg) { + function ensureThrows(arg: any) { assertThrows(() => { getTrustedHTML(arg); }); @@ -52,7 +53,7 @@ assertThrows(() => { getTrustedScriptURL(arg); }); - }; + } const a = 'test'; ensureThrows(a); @@ -60,12 +61,8 @@ const b = [a]; ensureThrows(b); - const c = b; - c.raw = b; + // c holds stringified value of `test`, which isn't a template literal. + const c = `test`; ensureThrows(c); - - // d holds stringified value of `test`, which isn't a template literal. - const d = `test`; - ensureThrows(d); }); });
diff --git a/chrome/test/data/webui/js/webui_resource_module_async_browsertest.js b/chrome/test/data/webui/js/webui_resource_module_async_browsertest.js index 4436f9e9..146edc9 100644 --- a/chrome/test/data/webui/js/webui_resource_module_async_browsertest.js +++ b/chrome/test/data/webui/js/webui_resource_module_async_browsertest.js
@@ -172,7 +172,7 @@ var StaticTypesTest = class extends WebUIResourceModuleAsyncTest { /** @override */ get browsePreload() { - return 'chrome://test/test_loader.html?module=js/static_types_test.js&host=test'; + return 'chrome://test/test_loader.html?module=js/static_types_test.js'; } };
diff --git a/chrome/test/data/webui/resources/list_property_update_behavior_tests.js b/chrome/test/data/webui/resources/list_property_update_behavior_tests.ts similarity index 66% rename from chrome/test/data/webui/resources/list_property_update_behavior_tests.js rename to chrome/test/data/webui/resources/list_property_update_behavior_tests.ts index fde99c8..e1bc960 100644 --- a/chrome/test/data/webui/resources/list_property_update_behavior_tests.js +++ b/chrome/test/data/webui/resources/list_property_update_behavior_tests.ts
@@ -5,138 +5,161 @@ /** @fileoverview Suite of tests for the ListPropertyUpdateBehavior. */ import {ListPropertyUpdateBehavior} from 'chrome://resources/js/list_property_update_behavior.m.js'; -import {Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js'; +import {mixinBehaviors, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js'; +import {assertDeepEquals, assertEquals, assertFalse, assertNotEquals, assertTrue} from 'chrome://webui-test/chai_assert.js'; + +type SimpleArrayEntry = { + id: number, +}; + +type ComplexArrayEntry = { + letter: string, + words: string[], +}; + +/** A test element that implements the ListPropertyUpdateBehavior. */ +const ListPropertyUpdateBehaviorTestElementBase = + mixinBehaviors([ListPropertyUpdateBehavior], PolymerElement) as + {new (): PolymerElement & ListPropertyUpdateBehavior}; + +class ListPropertyUpdateBehaviorTestElement extends + ListPropertyUpdateBehaviorTestElementBase { + static get is() { + return 'list-property-update-behavior-test-element'; + } + + static get properties() { + return { + /** + * A test array containing objects with Array properties. The elements + * in the array represent an object that maps a list of |words| to the + * |letter| that they begin with. + */ + complexArray: Array, + + /** + * A test array containing objects with numerical |id|s. + */ + simpleArray: Array, + }; + } + + complexArray: ComplexArrayEntry[] = []; + simpleArray: SimpleArrayEntry[] = []; + + constructor() { + super(); + + this.resetSimpleArray(); + this.resetComplexArray(); + } + + resetComplexArray() { + this.complexArray = [ + {letter: 'a', words: ['adventure', 'apple']}, + {letter: 'b', words: ['banana', 'bee', 'bottle']}, + {letter: 'c', words: ['car']}, + ]; + } + + resetSimpleArray() { + this.simpleArray = [{id: 1}, {id: 2}, {id: 3}]; + } + + /** + * Updates the |complexArray| with |newArray| using the + * ListPropertyUpdateBehavior.updateList() method. This method will + * iterate through the elements of |complexArray| to check if their + * |words| property array need to be updated if |complexArray| did not + * have any changes. + * @param newArray The array update |complexArray| with. + * @return An object that has a |topArrayChanged| property set to true if + * notifySplices() was called for the 'complexArray' property path and + * a |wordsArrayChanged| property set to true if notifySplices() was + * called for the |words| property on an item of |complexArray|. + */ + updateComplexArray(newArray: ComplexArrayEntry[]): + {topArrayChanged: boolean, wordsArrayChanged: boolean} { + if (this.updateList( + 'complexArray', x => x.letter, newArray, + true /* identityBasedUpdate */)) { + return {topArrayChanged: true, wordsArrayChanged: false}; + } + + // At this point, |complexArray| and |newArray| should have the same + // elements. + let wordsSplicesNotified = false; + assertEquals(this.complexArray.length, newArray.length); + this.complexArray.forEach((item, i) => { + assertEquals(item.letter, newArray[i]!.letter); + const propertyPath = 'complexArray.' + i + '.words'; + const newWordsArray = newArray[i]!.words; + + if (this.updateList(propertyPath, x => x, newWordsArray)) { + wordsSplicesNotified = true; + } + }); + + return { + topArrayChanged: false, + wordsArrayChanged: wordsSplicesNotified, + }; + } + + /** + * Updates the |simpleArray| with |newArray| using the + * ListPropertyUpdateBehavior.updateList() method. + * @param newArray The array to update |simpleArray| with. + * @returns True if the update called notifySplices() for + * |simpleArray|. + */ + updateSimpleArray(newArray: SimpleArrayEntry[]): boolean { + return this.updateList('simpleArray', x => String(x.id), newArray); + } +} + +declare global { + interface HTMLElementTagNameMap { + 'list-property-update-behavior-test-element': + ListPropertyUpdateBehaviorTestElement; + } +} + +customElements.define( + ListPropertyUpdateBehaviorTestElement.is, + ListPropertyUpdateBehaviorTestElement); suite('ListPropertyUpdateBehavior', function() { /** - * A list property update behavior test element created before each test. - * @type {ListPropertyUpdateBehaviorTestElement} + * A list-property-update-behavior test element created before each test. */ - let testElement; - - suiteSetup(function() { - /** A test element that implements the ListPropertyUpdateBehavior. */ - Polymer({ - is: 'list-property-update-behavior-test-element', - - behaviors: [ListPropertyUpdateBehavior], - - properties: { - /** - * A test array containing objects with Array properties. The elements - * in the array represent an object that maps a list of |words| to the - * |letter| that they begin with. - * @type {!Array<{letter: !string, words: !Array<string>}>} - */ - complexArray: { - type: Array, - }, - - /** - * A test array containing objects with numerical |id|s. - * @type {!Array<{id: !number}>} - */ - simpleArray: { - type: Array, - }, - }, - - /** @override */ - created: function() { - this.resetSimpleArray(); - this.resetComplexArray(); - }, - - resetComplexArray() { - this.complexArray = [ - {letter: 'a', words: ['adventure', 'apple']}, - {letter: 'b', words: ['banana', 'bee', 'bottle']}, - {letter: 'c', words: ['car']}, - ]; - }, - - resetSimpleArray() { - this.simpleArray = [{id: 1}, {id: 2}, {id: 3}]; - }, - - /** - * Updates the |complexArray| with |newArray| using the - * ListPropertyUpdateBehavior.updateList() method. This method will - * iterate through the elements of |complexArray| to check if their - * |words| property array need to be updated if |complexArray| did not - * have any changes. - * @param {!Array{letter: !string, words: !Array<string>}>} newArray The - * array update |complexArray| with. - * @returns {{topArrayChanged: boolean, wordsArrayChanged: boolean}} An - * object that has a |topArrayChanged| property set to true if - * notifySplices() was called for the 'complexArray' property path and - * a |wordsArrayChanged| property set to true if notifySplices() was - * called for the |words| property on an item of |complexArray|. - */ - updateComplexArray(newArray) { - if (this.updateList( - 'complexArray', x => x.letter, newArray, - true /* identityBasedUpdate */)) { - return {topArrayChanged: true, wordsArrayChanged: false}; - } - - // At this point, |complexArray| and |newArray| should have the same - // elements. - let wordsSplicesNotified = false; - assertEquals(this.complexArray.length, newArray.length); - this.complexArray.forEach((item, i) => { - assertEquals(item.letter, newArray[i].letter); - const propertyPath = 'complexArray.' + i + '.words'; - const newWordsArray = newArray[i].words; - - if (this.updateList(propertyPath, x => x, newWordsArray)) { - wordsSplicesNotified = true; - } - }); - - return { - topArrayChanged: false, - wordsArrayChanged: wordsSplicesNotified, - }; - }, - - /** - * Updates the |simpleArray| with |newArray| using the - * ListPropertyUpdateBehavior.updateList() method. - * @param {!Array{id: !number}>} newArray The array to update - * |simpleArray| with. - * @returns {boolean} True if the update called notifySplices() for - * |simpleArray|. - */ - updateSimpleArray(newArray) { - return this.updateList('simpleArray', x => x.id, newArray); - }, - }); - }); + let testElement: ListPropertyUpdateBehaviorTestElement; // Initialize a list-property-update-behavior-test-element before each test. setup(function() { - PolymerTest.clearBody(); + document.body.innerHTML = ''; testElement = document.createElement('list-property-update-behavior-test-element'); document.body.appendChild(testElement); }); - function assertSimpleArrayEquals(array, expectedArray) { + function assertSimpleArrayEquals( + array: SimpleArrayEntry[], expectedArray: SimpleArrayEntry[]) { assertEquals(array.length, expectedArray.length); array.forEach((item, i) => { - assertEquals(item.id, expectedArray[i].id); + assertEquals(item.id, expectedArray[i]!.id); }); } - function assertComplexArrayEquals(array, expectedArray) { + function assertComplexArrayEquals( + array: ComplexArrayEntry[], expectedArray: ComplexArrayEntry[]) { assertEquals(array.length, expectedArray.length); array.forEach((item, i) => { - assertEquals(item.letter, expectedArray[i].letter); - assertEquals(item.words.length, expectedArray[i].words.length); + assertEquals(item.letter, expectedArray[i]!.letter); + assertEquals(item.words.length, expectedArray[i]!.words.length); item.words.forEach((word, j) => { - assertEquals(word, expectedArray[i].words[j]); + assertEquals(word, expectedArray[i]!.words[j]); }); }); } @@ -291,8 +314,9 @@ assertTrue(newArray[0].words.length > 0); assertNotEquals('apricot', newArray[0].words[0]); newArray[0].words = ['apricot']; - assertTrue(testElement.updateList('complexArray', x => x.letter, newArray)); - assertDeepEquals(['apricot'], testElement.complexArray[0].words); + assertTrue(testElement.updateList( + 'complexArray', (x: ComplexArrayEntry) => x.letter, newArray)); + assertDeepEquals(['apricot'], testElement.complexArray[0]!.words); }); test('first item modified with same uid and last item removed', () => { @@ -302,8 +326,9 @@ newArray[0].words = ['apricot']; assertTrue(newArray.length > 1); newArray.pop(); - assertTrue(testElement.updateList('complexArray', x => x.letter, newArray)); - assertDeepEquals(['apricot'], testElement.complexArray[0].words); + assertTrue(testElement.updateList( + 'complexArray', (x: ComplexArrayEntry) => x.letter, newArray)); + assertDeepEquals(['apricot'], testElement.complexArray[0]!.words); }); test('updateList() function triggers notifySplices()', () => {
diff --git a/chrome/test/data/webui/resources/webui_resources_browsertest.js b/chrome/test/data/webui/resources/webui_resources_browsertest.js index bece9ee..39dd41c 100644 --- a/chrome/test/data/webui/resources/webui_resources_browsertest.js +++ b/chrome/test/data/webui/resources/webui_resources_browsertest.js
@@ -27,7 +27,7 @@ class extends WebUIResourcesBrowserTest { /** @override */ get browsePreload() { - return 'chrome://test/test_loader.html?module=resources/list_property_update_behavior_tests.js&host=test'; + return 'chrome://test/test_loader.html?module=resources/list_property_update_behavior_tests.js'; } };
diff --git a/chrome/test/data/webui/settings/chromeos/test_crostini_browser_proxy.js b/chrome/test/data/webui/settings/chromeos/test_crostini_browser_proxy.js index 7edef58..e244f75 100644 --- a/chrome/test/data/webui/settings/chromeos/test_crostini_browser_proxy.js +++ b/chrome/test/data/webui/settings/chromeos/test_crostini_browser_proxy.js
@@ -167,7 +167,7 @@ /** @override */ getCrostiniActivePorts() { this.methodCalled('getCrostiniActivePorts'); - return Promise.resolve(new Array()); + return Promise.resolve([]); } /** @override */
diff --git a/chrome/test/ppapi/ppapi_browsertest.cc b/chrome/test/ppapi/ppapi_browsertest.cc index ad5cc27..8c68d18 100644 --- a/chrome/test/ppapi/ppapi_browsertest.cc +++ b/chrome/test/ppapi/ppapi_browsertest.cc
@@ -1937,7 +1937,8 @@ // Switch back to the test tab. browser()->tab_strip_model()->ActivateTabAt( - 0, {TabStripModel::GestureType::kOther}); + 0, TabStripUserGestureDetails( + TabStripUserGestureDetails::GestureType::kOther)); ASSERT_TRUE(observer.Run()) << handler.error_message(); EXPECT_STREQ("PASS", handler.message().c_str());
diff --git a/chromecast/media/audio/cma_audio_output.cc b/chromecast/media/audio/cma_audio_output.cc index b7949aa..732faa9 100644 --- a/chromecast/media/audio/cma_audio_output.cc +++ b/chromecast/media/audio/cma_audio_output.cc
@@ -91,9 +91,17 @@ // If AUDIO_PREFETCH is enabled, we're able to push audio ahead of // realtime. Set the sync mode to kModeSyncPts to allow cma backend to // buffer the early pushed data, instead of dropping them. + // If the output is created with a valid audio track session id, it means + // the output stream is owned by other native applications on Android. + // In that case, other native applications relay on the reported playback + // position to do av sync or use hardware av sync mode. Set the sync mode + // to kModeApkSyncPts to avoid setting timestamp of silence buffers pushed + // by us to allow the backend decoder distinguishes real audio data vs + // silence. audio_params_.effects() & ::media::AudioParameters::AUDIO_PREFETCH - ? (use_hw_av_sync_ ? MediaPipelineDeviceParams::kModeHwAvSyncPts - : MediaPipelineDeviceParams::kModeSyncPts) + ? (audio_track_session_id > 0 + ? MediaPipelineDeviceParams::kModeApkSyncPts + : MediaPipelineDeviceParams::kModeSyncPts) : MediaPipelineDeviceParams::kModeIgnorePts, MediaPipelineDeviceParams::kAudioStreamNormal, cma_backend_task_runner.get(), GetContentType(device_id), device_id);
diff --git a/chromecast/media/cma/backend/android/audio_decoder_android.cc b/chromecast/media/cma/backend/android/audio_decoder_android.cc index 87fc4d1..b3b5f53 100644 --- a/chromecast/media/cma/backend/android/audio_decoder_android.cc +++ b/chromecast/media/cma/backend/android/audio_decoder_android.cc
@@ -14,7 +14,6 @@ #include "base/trace_event/trace_event.h" #include "chromecast/base/task_runner_impl.h" #include "chromecast/media/api/decoder_buffer_base.h" -#include "chromecast/media/cma/backend/android/audio_sink_manager.h" #include "chromecast/media/cma/backend/android/media_pipeline_backend_android.h" #include "chromecast/media/cma/base/decoder_buffer_adapter.h" #include "chromecast/media/cma/base/decoder_config_adapter.h" @@ -67,8 +66,7 @@ // static int64_t MediaPipelineBackend::AudioDecoder::GetMinimumBufferedTime( const AudioConfig& config) { - return AudioSinkAndroid::GetMinimumBufferedTime( - AudioSinkManager::GetDefaultSinkType(), config); + return AudioSinkAndroid::GetMinimumBufferedTime(config); } AudioDecoderAndroid::AudioDecoderAndroid(MediaPipelineBackendAndroid* backend) @@ -80,7 +78,6 @@ pushed_eos_(false), sink_error_(false), current_pts_(kInvalidTimestamp), - sink_(AudioSinkManager::GetDefaultSinkType()), pending_output_frames_(kNoPendingOutput), volume_multiplier_(1.0f), pool_(new ::media::AudioBufferMemoryPool()), @@ -115,9 +112,6 @@ pushed_eos_ = false; current_pts_ = kInvalidTimestamp; pending_output_frames_ = kNoPendingOutput; - - last_sink_delay_.timestamp_microseconds = kInvalidTimestamp; - last_sink_delay_.delay_microseconds = 0; } bool AudioDecoderAndroid::Start(int64_t start_pts) { @@ -166,7 +160,6 @@ TRACE_FUNCTION_ENTRY0(); DCHECK(sink_); sink_->SetPaused(false); - last_sink_delay_ = AudioDecoderAndroid::RenderingDelay(); return true; } @@ -297,7 +290,6 @@ backend_->ContentType()); sink_->SetStreamVolumeMultiplier(volume_multiplier_); pending_output_frames_ = kNoPendingOutput; - last_sink_delay_ = AudioDecoderAndroid::RenderingDelay(); } void AudioDecoderAndroid::CreateDecoder() { @@ -352,7 +344,10 @@ AudioDecoderAndroid::RenderingDelay AudioDecoderAndroid::GetRenderingDelay() { TRACE_FUNCTION_ENTRY0(); - AudioDecoderAndroid::RenderingDelay delay = last_sink_delay_; + if (!sink_) { + return AudioDecoderAndroid::RenderingDelay(); + } + AudioDecoderAndroid::RenderingDelay delay = sink_->GetRenderingDelay(); if (delay.timestamp_microseconds != kInvalidTimestamp) { double usec_per_sample = 1000000.0 / config_.samples_per_second; @@ -638,15 +633,13 @@ config_.sample_format == kSampleFormatPlanarF32); } -void AudioDecoderAndroid::OnWritePcmCompletion(BufferStatus status, - const RenderingDelay& delay) { +void AudioDecoderAndroid::OnWritePcmCompletion(BufferStatus status) { DVLOG(3) << __func__ << ": status=" << status; TRACE_FUNCTION_ENTRY0(); DCHECK(task_runner_->BelongsToCurrentThread()); DCHECK_EQ(MediaPipelineBackendAndroid::kBufferSuccess, status); pending_output_frames_ = kNoPendingOutput; - last_sink_delay_ = delay; task_runner_->PostTask(FROM_HERE, base::BindOnce(&AudioDecoderAndroid::PushMorePcm,
diff --git a/chromecast/media/cma/backend/android/audio_decoder_android.h b/chromecast/media/cma/backend/android/audio_decoder_android.h index 3047b32..300ad18 100644 --- a/chromecast/media/cma/backend/android/audio_decoder_android.h +++ b/chromecast/media/cma/backend/android/audio_decoder_android.h
@@ -74,8 +74,7 @@ }; // AudioSinkAndroid::Delegate implementation: - void OnWritePcmCompletion(BufferStatus status, - const RenderingDelay& delay) override; + void OnWritePcmCompletion(BufferStatus status) override; void OnSinkError(SinkError error) override; void CleanUpPcm(); @@ -116,7 +115,6 @@ int64_t current_pts_; ManagedAudioSink sink_; - RenderingDelay last_sink_delay_; int64_t pending_output_frames_; float volume_multiplier_;
diff --git a/chromecast/media/cma/backend/android/audio_sink_android.cc b/chromecast/media/cma/backend/android/audio_sink_android.cc index ad6d40b..424f1e3 100644 --- a/chromecast/media/cma/backend/android/audio_sink_android.cc +++ b/chromecast/media/cma/backend/android/audio_sink_android.cc
@@ -12,23 +12,12 @@ namespace media { // static -int64_t AudioSinkAndroid::GetMinimumBufferedTime(SinkType sink_type, - const AudioConfig& config) { - const int64_t kDefaultMinBufferTimeUs = 50000; - switch (sink_type) { - case AudioSinkAndroid::kSinkTypeNativeBased: - // TODO(ckuiper): implement a sink using native code. - NOTREACHED() << "Native-based audio sink is not implemented yet!"; - break; - case AudioSinkAndroid::kSinkTypeJavaBased: - return AudioSinkAndroidAudioTrackImpl::GetMinimumBufferedTime( - config.channel_number, config.samples_per_second); - } - return kDefaultMinBufferTimeUs; +int64_t AudioSinkAndroid::GetMinimumBufferedTime(const AudioConfig& config) { + return AudioSinkAndroidAudioTrackImpl::GetMinimumBufferedTime( + config.channel_number, config.samples_per_second); } -ManagedAudioSink::ManagedAudioSink(SinkType sink_type) - : sink_type_(sink_type), sink_(nullptr) {} +ManagedAudioSink::ManagedAudioSink() : sink_(nullptr) {} ManagedAudioSink::~ManagedAudioSink() { Remove(); @@ -47,18 +36,9 @@ const std::string& device_id, AudioContentType content_type) { Remove(); - - LOG(INFO) << __func__ << ": Creating new sink of type=" << sink_type_; - switch (sink_type_) { - case AudioSinkAndroid::kSinkTypeNativeBased: - // TODO(ckuiper): implement a sink using native code. - NOTREACHED() << "Native-based audio sink is not implemented yet!"; - break; - case AudioSinkAndroid::kSinkTypeJavaBased: - sink_ = new AudioSinkAndroidAudioTrackImpl( - delegate, num_channels, samples_per_second, audio_track_session_id, - primary, use_hw_av_sync, device_id, content_type); - } + sink_ = new AudioSinkAndroidAudioTrackImpl( + delegate, num_channels, samples_per_second, audio_track_session_id, + primary, use_hw_av_sync, device_id, content_type); AudioSinkManager::Get()->Add(sink_); }
diff --git a/chromecast/media/cma/backend/android/audio_sink_android.h b/chromecast/media/cma/backend/android/audio_sink_android.h index 2a625617..56924bd 100644 --- a/chromecast/media/cma/backend/android/audio_sink_android.h +++ b/chromecast/media/cma/backend/android/audio_sink_android.h
@@ -32,11 +32,6 @@ kInternalError, }; - enum SinkType { - kSinkTypeJavaBased, // Java-based (using AudioTrack) - kSinkTypeNativeBased // Native-based (not implemented yet) - }; - class Delegate { public: using SinkError = AudioSinkAndroid::SinkError; @@ -44,8 +39,7 @@ // Called when the last data passed to WritePcm() has been successfully // added to the queue. virtual void OnWritePcmCompletion( - MediaPipelineBackendAndroid::BufferStatus status, - const MediaPipelineBackendAndroid::RenderingDelay& delay) = 0; + MediaPipelineBackendAndroid::BufferStatus status) = 0; // Called when a sink error occurs. No further data should be written. virtual void OnSinkError(SinkError error) = 0; @@ -54,8 +48,7 @@ virtual ~Delegate() {} }; - static int64_t GetMinimumBufferedTime(SinkType sink_type, - const AudioConfig& config); + static int64_t GetMinimumBufferedTime(const AudioConfig& config); AudioSinkAndroid() {} virtual ~AudioSinkAndroid() {} @@ -85,6 +78,9 @@ // stream multiplier and limiter multiplier. virtual float EffectiveVolume() const = 0; + // Returns the current audio rendering delay. + virtual MediaPipelineBackendAndroid::RenderingDelay GetRenderingDelay() = 0; + // Getters virtual int input_samples_per_second() const = 0; virtual bool primary() const = 0; @@ -97,10 +93,9 @@ // destroyed. Inspired by std::unique_ptr<>. class ManagedAudioSink { public: - using SinkType = AudioSinkAndroid::SinkType; using Delegate = AudioSinkAndroid::Delegate; - explicit ManagedAudioSink(SinkType sink_type); + ManagedAudioSink(); ManagedAudioSink(const ManagedAudioSink&) = delete; ManagedAudioSink& operator=(const ManagedAudioSink&) = delete; @@ -128,7 +123,6 @@ private: void Remove(); - SinkType sink_type_; AudioSinkAndroid* sink_; };
diff --git a/chromecast/media/cma/backend/android/audio_sink_android_audiotrack_impl.cc b/chromecast/media/cma/backend/android/audio_sink_android_audiotrack_impl.cc index 2bec137e..2926802 100644 --- a/chromecast/media/cma/backend/android/audio_sink_android_audiotrack_impl.cc +++ b/chromecast/media/cma/backend/android/audio_sink_android_audiotrack_impl.cc
@@ -133,6 +133,22 @@ return content_type_; } +MediaPipelineBackendAndroid::RenderingDelay +AudioSinkAndroidAudioTrackImpl::GetRenderingDelay() { + DVLOG(3) << __func__ << "(" << this << "): " + << " delay=" << sink_rendering_delay_.delay_microseconds + << " ts=" << sink_rendering_delay_.timestamp_microseconds; + // TODO(ziyangch): Add a rate limiter to avoid calling AudioTrack.getTimestamp + // too frequent. + Java_AudioSinkAudioTrackImpl_getAudioTrackTimestamp( + base::android::AttachCurrentThread(), j_audio_sink_audiotrack_impl_); + sink_rendering_delay_.audio_track_frame_position = + direct_audio_track_timestamp_address_[0]; + sink_rendering_delay_.audio_track_nano_time = + direct_audio_track_timestamp_address_[1]; + return sink_rendering_delay_; +} + void AudioSinkAndroidAudioTrackImpl::FinalizeOnFeederThread() { RUN_ON_FEEDER_THREAD(FinalizeOnFeederThread); wait_for_eos_task_.Cancel(); @@ -150,11 +166,14 @@ JNIEnv* env, const JavaParamRef<jobject>& obj, const JavaParamRef<jobject>& pcm_byte_buffer, - const JavaParamRef<jobject>& timestamp_byte_buffer) { + const JavaParamRef<jobject>& rendering_delay_byte_buffer, + const JavaParamRef<jobject>& audio_track_timestamp_byte_buffer) { direct_pcm_buffer_address_ = static_cast<uint8_t*>(env->GetDirectBufferAddress(pcm_byte_buffer)); direct_rendering_delay_address_ = static_cast<uint64_t*>( - env->GetDirectBufferAddress(timestamp_byte_buffer)); + env->GetDirectBufferAddress(rendering_delay_byte_buffer)); + direct_audio_track_timestamp_address_ = static_cast<uint64_t*>( + env->GetDirectBufferAddress(audio_track_timestamp_byte_buffer)); } void AudioSinkAndroidAudioTrackImpl::WritePcm( @@ -178,7 +197,7 @@ if (pending_data_->data_size() == 0) { LOG(INFO) << __func__ << "(" << this << "): empty data buffer!"; - PostPcmCallback(sink_rendering_delay_); + PostPcmCallback(); return; } @@ -221,7 +240,7 @@ TrackRawMonotonicClockDeviation(); - PostPcmCallback(sink_rendering_delay_); + PostPcmCallback(); } void AudioSinkAndroidAudioTrackImpl::ScheduleWaitForEosTask() { @@ -244,7 +263,7 @@ void AudioSinkAndroidAudioTrackImpl::OnPlayoutDone() { DCHECK(feeder_task_runner_->BelongsToCurrentThread()); DCHECK(state_ == kStateGotEos); - PostPcmCallback(sink_rendering_delay_); + PostPcmCallback(); } int AudioSinkAndroidAudioTrackImpl::ReformatData() { @@ -334,20 +353,15 @@ TrackRawMonotonicClockDeviation(); - PostPcmCallback(sink_rendering_delay_); + PostPcmCallback(); } -void AudioSinkAndroidAudioTrackImpl::PostPcmCallback( - const MediaPipelineBackendAndroid::RenderingDelay& delay) { - RUN_ON_CALLER_THREAD(PostPcmCallback, delay); +void AudioSinkAndroidAudioTrackImpl::PostPcmCallback() { + RUN_ON_CALLER_THREAD(PostPcmCallback); DCHECK(pending_data_); - DVLOG(3) << __func__ << "(" << this << "): " - << " delay=" << delay.delay_microseconds - << " ts=" << delay.timestamp_microseconds; pending_data_ = nullptr; pending_data_bytes_already_fed_ = 0; - delegate_->OnWritePcmCompletion(MediaPipelineBackendAndroid::kBufferSuccess, - delay); + delegate_->OnWritePcmCompletion(MediaPipelineBackendAndroid::kBufferSuccess); } void AudioSinkAndroidAudioTrackImpl::SignalError( @@ -373,6 +387,7 @@ j_audio_sink_audiotrack_impl_); } else { LOG(INFO) << __func__ << "(" << this << "): Unpausing"; + sink_rendering_delay_ = MediaPipelineBackendAndroid::RenderingDelay(); state_ = kStateNormalPlayback; Java_AudioSinkAudioTrackImpl_play(base::android::AttachCurrentThread(), j_audio_sink_audiotrack_impl_);
diff --git a/chromecast/media/cma/backend/android/audio_sink_android_audiotrack_impl.h b/chromecast/media/cma/backend/android/audio_sink_android_audiotrack_impl.h index ec70d591..0fcbd63 100644 --- a/chromecast/media/cma/backend/android/audio_sink_android_audiotrack_impl.h +++ b/chromecast/media/cma/backend/android/audio_sink_android_audiotrack_impl.h
@@ -55,14 +55,17 @@ JNIEnv* env, const base::android::JavaParamRef<jobject>& obj, const base::android::JavaParamRef<jobject>& pcm_byte_buffer, - const base::android::JavaParamRef<jobject>& timestamp_byte_buffer); + const base::android::JavaParamRef<jobject>& rendering_delay_byte_buffer, + const base::android::JavaParamRef<jobject>& + audio_track_timestamp_byte_buffer); - // AudioSinkAndroid implementation + // AudioSinkAndroid implementation: void WritePcm(scoped_refptr<DecoderBufferBase> data) override; void SetPaused(bool paused) override; void SetStreamVolumeMultiplier(float multiplier) override; void SetLimiterVolumeMultiplier(float multiplier) override; float EffectiveVolume() const override; + MediaPipelineBackendAndroid::RenderingDelay GetRenderingDelay() override; // Getters int input_samples_per_second() const override; @@ -106,8 +109,7 @@ void TrackRawMonotonicClockDeviation(); - void PostPcmCallback( - const MediaPipelineBackendAndroid::RenderingDelay& delay); + void PostPcmCallback(); void SignalError(AudioSinkAndroid::SinkError error); void PostError(AudioSinkAndroid::SinkError error); @@ -135,6 +137,8 @@ uint8_t* direct_pcm_buffer_address_; // PCM audio data native->java // rendering delay+timestamp return value, java->native uint64_t* direct_rendering_delay_address_; + // AudioTrack.getTimestamp return value, java->native + uint64_t* direct_audio_track_timestamp_address_; // Java AudioSinkAudioTrackImpl instance. const base::android::ScopedJavaGlobalRef<jobject>
diff --git a/chromecast/media/cma/backend/android/audio_sink_manager.cc b/chromecast/media/cma/backend/android/audio_sink_manager.cc index 434681a..5f1ab60 100644 --- a/chromecast/media/cma/backend/android/audio_sink_manager.cc +++ b/chromecast/media/cma/backend/android/audio_sink_manager.cc
@@ -33,11 +33,6 @@ return sink_manager_instance.get(); } -// static -AudioSinkAndroid::SinkType AudioSinkManager::GetDefaultSinkType() { - return AudioSinkAndroid::kSinkTypeJavaBased; -} - AudioSinkManager::AudioSinkManager() {} AudioSinkManager::~AudioSinkManager() {}
diff --git a/chromecast/media/cma/backend/android/audio_sink_manager.h b/chromecast/media/cma/backend/android/audio_sink_manager.h index b4e260f4..982a38c 100644 --- a/chromecast/media/cma/backend/android/audio_sink_manager.h +++ b/chromecast/media/cma/backend/android/audio_sink_manager.h
@@ -23,14 +23,6 @@ AudioSinkManager(const AudioSinkManager&) = delete; AudioSinkManager& operator=(const AudioSinkManager&) = delete; - static AudioSinkAndroid::SinkType GetDefaultSinkType(); - - // Gets the Android audio session ids used for media and communication (TTS) - // tracks. - // Set a return value pointer to null if that id is not needed. - // Returns true if the ids populated are valid. - static bool GetSessionIds(int* media_id, int* communication_id); - // Adds the given sink instance to the vector. void Add(AudioSinkAndroid* sink);
diff --git a/chromecast/media/cma/backend/android/java/src/org/chromium/chromecast/cma/backend/android/AudioSinkAudioTrackImpl.java b/chromecast/media/cma/backend/android/java/src/org/chromium/chromecast/cma/backend/android/AudioSinkAudioTrackImpl.java index c5eb08ab..d719ad2 100644 --- a/chromecast/media/cma/backend/android/java/src/org/chromium/chromecast/cma/backend/android/AudioSinkAudioTrackImpl.java +++ b/chromecast/media/cma/backend/android/java/src/org/chromium/chromecast/cma/backend/android/AudioSinkAudioTrackImpl.java
@@ -10,6 +10,7 @@ import android.media.AudioTrack; import android.os.Build; import android.os.SystemClock; +import android.util.Pair; import android.util.SparseIntArray; import androidx.annotation.IntDef; @@ -25,6 +26,8 @@ import java.lang.annotation.RetentionPolicy; import java.nio.ByteBuffer; import java.nio.ByteOrder; +import java.util.LinkedList; +import java.util.Queue; /** * Implements an audio sink object using Android's AudioTrack module to @@ -97,11 +100,7 @@ private static final long TIMESTAMP_UPDATE_PERIOD = 250 * MSEC_IN_NSEC; private static final long UNDERRUN_LOG_THROTTLE_PERIOD = SEC_IN_NSEC; - // Internally Android fetches data from AudioTrack buffer in periods of 20ms. - private static final long ANDROID_AUDIO_PERIOD_SIZE_US = 20000; - - // Threshold at which we start logging low buffer warnings. - private static final long VERY_LOW_BUFFER_LEVEL = ANDROID_AUDIO_PERIOD_SIZE_US; + private static final int START_THRESHOLD_MS = 50; private static long sInstanceCounter; @@ -180,12 +179,13 @@ private long mTimestampStabilityCounter; // Counts consecutive stable timestamps at startup. private long mTimestampStabilityStartTimeNsec; // Time when we started being stable. - private long mLastRenderingDelayUsecs; - private int mLastUnderrunCount; // Statistics private long mTotalFramesWritten; + // Store intervals of audio buffers without timestamp, [startPosition, endPosition). + private Queue<Pair<Long, Long>> mPendingFramesWithoutTimestamp; + private long mTotalPlayedFramesWithoutTimestamp; // Sample Rate calculator private long mSRWindowStartTimeNsec; @@ -199,6 +199,8 @@ private ByteBuffer mPcmBuffer; // PCM audio data (native->java) private ByteBuffer mRenderingDelayBuffer; // RenderingDelay return value // (java->native) + private ByteBuffer + mAudioTrackTimestampBuffer; // AudioTrack.getTimestamp return value (java->native) /** * Converts the given nanoseconds value into microseconds with proper rounding. It is assumed @@ -264,13 +266,16 @@ int bytesPerBuffer, int sessionId, boolean useHwAvSync) { mNativeAudioSinkAudioTrackImpl = nativeAudioSinkAudioTrackImpl; mLastTimestampUpdateNsec = NO_TIMESTAMP; - mLastRenderingDelayUsecs = NO_TIMESTAMP; mTriggerTimestampUpdateNow = false; mTimestampStabilityCounter = 0; mReferenceTimestampState = ReferenceTimestampState.STARTING_UP; mOriginalFramePosOfLastTimestamp = NO_FRAME_POSITION; mLastUnderrunCount = 0; mTotalFramesWritten = 0; + if (isValidSessionId(sessionId) && !useHwAvSync) { + mPendingFramesWithoutTimestamp = new LinkedList<>(); + } + mTotalPlayedFramesWithoutTimestamp = 0; init(castContentType, channelCount, sampleRateInHz, bytesPerBuffer, sessionId, useHwAvSync); } @@ -345,6 +350,15 @@ .build()); if (isValidSessionId(sessionId)) builder.setSessionId(sessionId); mAudioTrack = builder.build(); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && isValidSessionId(sessionId)) { + // The playback will not be started until Android AudioTrack has more data than + // the start threshold. Reduce the start threshold to 50ms in order to start + // playback as soon as possible after starting or resuming. Sometimes other + // native applications like Youtube will not push audio data until we play all + // pushed data before pausing. See b/237011415. + int startThresholdInFrames = START_THRESHOLD_MS * mSampleRateInHz / 1000; + mAudioTrack.setStartThresholdInFrames(startThresholdInFrames); + } } while (mAudioTrack == null && retries++ < MAX_RETRIES_FOR_AUDIO_TRACKS); // Allocated shared buffers. @@ -353,9 +367,16 @@ mRenderingDelayBuffer = ByteBuffer.allocateDirect(2 * 8); // 2 long mRenderingDelayBuffer.order(ByteOrder.nativeOrder()); + // Initialize with a invalid rendering delay. + mRenderingDelayBuffer.putLong(0, 0); + mRenderingDelayBuffer.putLong(8, NO_TIMESTAMP); + + mAudioTrackTimestampBuffer = ByteBuffer.allocateDirect(2 * 8); // 2 long + mAudioTrackTimestampBuffer.order(ByteOrder.nativeOrder()); AudioSinkAudioTrackImplJni.get().cacheDirectBufferAddress(mNativeAudioSinkAudioTrackImpl, - AudioSinkAudioTrackImpl.this, mPcmBuffer, mRenderingDelayBuffer); + AudioSinkAudioTrackImpl.this, mPcmBuffer, mRenderingDelayBuffer, + mAudioTrackTimestampBuffer); } @CalledByNative @@ -394,8 +415,10 @@ return mAudioTrack.getPlayState() == AudioTrack.PLAYSTATE_PAUSED; } - /** Stops the AudioTrack and returns an estimate of the time it takes for the remaining data - * left in the internal queue to be played out (in usecs). */ + /** + * Stops the AudioTrack and returns an estimate of the time it takes for the remaining data + * left in the internal queue to be played out (in usecs). + */ @CalledByNative private long prepareForShutdown() { long playtimeLeftNsecs; @@ -451,7 +474,8 @@ return 0; } - /** Writes the PCM data of the given size into the AudioTrack object. The + /** + * Writes the PCM data of the given size into the AudioTrack object. The * PCM data is provided through the memory-mapped ByteBuffer. * * Returns the number of bytes written into the AudioTrack object, -1 for @@ -466,9 +490,6 @@ + " underruns=" + mLastUnderrunCount); } - // Check buffer level before feeding in new data. - if (haveValidRefPoint()) checkBufferLevel(); - // Setup the PCM ByteBuffer correctly. mPcmBuffer.limit(sizeInBytes); mPcmBuffer.position(0); @@ -525,6 +546,10 @@ } int framesWritten = bytesWritten / (mSampleSize * mChannelCount); + if (mPendingFramesWithoutTimestamp != null && timestampNs == NO_TIMESTAMP) { + mPendingFramesWithoutTimestamp.add( + Pair.create(mTotalFramesWritten, mTotalFramesWritten + framesWritten)); + } mTotalFramesWritten += framesWritten; if (DEBUG_LEVEL >= 3) { @@ -549,24 +574,46 @@ return bytesWritten; } + @CalledByNative + public void getAudioTrackTimestamp() { + AudioTimestamp timestamp = new AudioTimestamp(); + if (!mAudioTrack.getTimestamp(timestamp)) { + mAudioTrackTimestampBuffer.putLong(0, 0); + mAudioTrackTimestampBuffer.putLong(8, NO_TIMESTAMP); + return; + } + if (mPendingFramesWithoutTimestamp == null) { + mAudioTrackTimestampBuffer.putLong(0, timestamp.framePosition); + mAudioTrackTimestampBuffer.putLong(8, timestamp.nanoTime); + return; + } + while (!mPendingFramesWithoutTimestamp.isEmpty() + && timestamp.framePosition >= mPendingFramesWithoutTimestamp.peek().second) { + // Calculate the total frames without timestamp before current reported position. + mTotalPlayedFramesWithoutTimestamp += (mPendingFramesWithoutTimestamp.peek().second + - mPendingFramesWithoutTimestamp.peek().first); + mPendingFramesWithoutTimestamp.remove(); + } + assert timestamp.framePosition >= mTotalPlayedFramesWithoutTimestamp; + if (!mPendingFramesWithoutTimestamp.isEmpty() + && timestamp.framePosition >= mPendingFramesWithoutTimestamp.peek().first) { + // The reported position is in the middle of an audio buffer without timestamp. Use + // the start position to calculate the accurate position. + mAudioTrackTimestampBuffer.putLong(0, + mPendingFramesWithoutTimestamp.peek().first + - mTotalPlayedFramesWithoutTimestamp); + } else { + mAudioTrackTimestampBuffer.putLong( + 0, timestamp.framePosition - mTotalPlayedFramesWithoutTimestamp); + } + mAudioTrackTimestampBuffer.putLong(8, timestamp.nanoTime); + } + /** Returns the elapsed time from the given start_time until now, in nsec. */ private long elapsedNsec(long startTimeNsec) { return System.nanoTime() - startTimeNsec; } - private void checkBufferLevel() { - long bufferLevel = mTotalFramesWritten - mAudioTrack.getPlaybackHeadPosition(); - long bufferLevelUsec = convertNsecsToUsecs(convertFramesToNanoTime(bufferLevel)); - if (bufferLevelUsec <= VERY_LOW_BUFFER_LEVEL) { - long lastRenderingDelayUsec = - (mLastRenderingDelayUsecs == NO_TIMESTAMP) ? -1 : mLastRenderingDelayUsecs; - boolean hitUnderrun = (getUnderrunCount() != mLastUnderrunCount); - mBufferLevelWarningLog.log(mTag, - "Low buffer level=" + bufferLevelUsec + "us " - + " RD=" + lastRenderingDelayUsec + (hitUnderrun ? "us *" : "us")); - } - } - private void updateSampleRateMeasure(long framesWritten) { if (mSRWindowFramesWritten == 0) { // Start new window. @@ -587,29 +634,51 @@ private void updateRenderingDelay() { checkForUnderruns(); updateRefPointTimestamp(); + long playoutTimeUsecs; + long delayUsecs; + long nowUsecs = convertNsecsToUsecs(System.nanoTime()); if (!haveValidRefPoint()) { + // Timestamp is resynced because of resuming, reuse the last valid stable rendering + // delay before pausing. + if (mRenderingDelayBuffer.getLong(8) != NO_TIMESTAMP) { + mRenderingDelayBuffer.putLong(8, nowUsecs); + return; + } + if (mUseHwAvSync) { + // Hw av sync stream uses the timestamp in the audio buffer instead + // of the reported rendering delay to do synchronization. Therefore + // it is safe to report zero rendering delay when it is not + // available. + mRenderingDelayBuffer.putLong(0, 0); + mRenderingDelayBuffer.putLong(8, nowUsecs); + return; + } + AudioTimestamp timestamp = new AudioTimestamp(); + if (mPendingFramesWithoutTimestamp != null && mAudioTrack.getTimestamp(timestamp)) { + // mPendingFramesWithoutTimestamp is not null indicates we are using the playback + // position instead of the rendering delay to do av sync. Therefore, it is not + // necessary to wait for a stable timestamp reference point. Immediately return a + // valid rendering delay when it is available could reduce the silence buffer + // pushed by cma backend. + playoutTimeUsecs = convertNsecsToUsecs(timestamp.nanoTime + + convertFramesToNanoTime(mTotalFramesWritten - timestamp.framePosition)); + delayUsecs = playoutTimeUsecs - nowUsecs; + mRenderingDelayBuffer.putLong(0, delayUsecs); + mRenderingDelayBuffer.putLong(8, nowUsecs); + return; + } // No timestamp available yet, just put dummy values and return. mRenderingDelayBuffer.putLong(0, 0); - // Hw av sync stream uses the timestamp in the audio buffer instead - // of the reported rendering delay to do synchronization. Therefore - // it is safe to report zero rendering delay when it is not - // available. - mRenderingDelayBuffer.putLong( - 8, mUseHwAvSync ? convertNsecsToUsecs(System.nanoTime()) : NO_TIMESTAMP); - mLastRenderingDelayUsecs = NO_TIMESTAMP; + mRenderingDelayBuffer.putLong(8, NO_TIMESTAMP); return; } // Interpolate to get proper Rendering delay. - long playoutTimeNsecs = getInterpolatedTStampNsecs(mTotalFramesWritten); - long playoutTimeUsecs = convertNsecsToUsecs(playoutTimeNsecs); - long nowUsecs = convertNsecsToUsecs(System.nanoTime()); - long delayUsecs = playoutTimeUsecs - nowUsecs; - + playoutTimeUsecs = convertNsecsToUsecs(getInterpolatedTStampNsecs(mTotalFramesWritten)); + delayUsecs = playoutTimeUsecs - nowUsecs; // Populate RenderingDelay return value for native land. mRenderingDelayBuffer.putLong(0, delayUsecs); mRenderingDelayBuffer.putLong(8, nowUsecs); - mLastRenderingDelayUsecs = delayUsecs; if (DEBUG_LEVEL >= 3) { Log.i(mTag, "RenderingDelay: delay=" + delayUsecs + " play=" + nowUsecs); @@ -655,6 +724,8 @@ + ")! Resetting rendering delay logic."); // Invalidate timestamp (resets RenderingDelay). mLastUnderrunCount = underruns; + mRenderingDelayBuffer.putLong(0, 0); + mRenderingDelayBuffer.putLong(8, NO_TIMESTAMP); resyncTimestamp(ReferenceTimestampState.RESYNCING_AFTER_UNDERRUN); } } @@ -804,6 +875,6 @@ interface Natives { void cacheDirectBufferAddress(long nativeAudioSinkAndroidAudioTrackImpl, AudioSinkAudioTrackImpl caller, ByteBuffer mPcmBuffer, - ByteBuffer mRenderingDelayBuffer); + ByteBuffer mRenderingDelayBuffer, ByteBuffer mAudioTrackTimestampBuffer); } }
diff --git a/chromecast/public/media/media_pipeline_backend.h b/chromecast/public/media/media_pipeline_backend.h index 92af40c7..11f63308 100644 --- a/chromecast/public/media/media_pipeline_backend.h +++ b/chromecast/public/media/media_pipeline_backend.h
@@ -109,13 +109,33 @@ // delay measurement was taken. Both times in microseconds. struct RenderingDelay { RenderingDelay() - : delay_microseconds(0), timestamp_microseconds(INT64_MIN) {} + : delay_microseconds(0), + timestamp_microseconds(INT64_MIN), + audio_track_frame_position(0), + audio_track_nano_time(INT64_MIN) {} RenderingDelay(int64_t delay_microseconds_in, int64_t timestamp_microseconds_in) : delay_microseconds(delay_microseconds_in), - timestamp_microseconds(timestamp_microseconds_in) {} + timestamp_microseconds(timestamp_microseconds_in), + audio_track_frame_position(0), + audio_track_nano_time(INT64_MIN) {} + RenderingDelay(int64_t delay_microseconds_in, + int64_t timestamp_microseconds_in, + int64_t audio_track_frame_position_in, + int64_t audio_track_nano_time_in) + : delay_microseconds(delay_microseconds_in), + timestamp_microseconds(timestamp_microseconds_in), + audio_track_frame_position(audio_track_frame_position_in), + audio_track_nano_time(audio_track_nano_time_in) {} int64_t delay_microseconds; int64_t timestamp_microseconds; + // TODO(ziyangch): Create a new struct and add a new getter function for + // audio track timestamp. + // Position in frames relative to start of an assumed audio stream in the + // Android AudioTrack. + int64_t audio_track_frame_position; + // Time associated with the frame in the Android audio pipeline. + int64_t audio_track_nano_time; }; // Statistics (computed since last call to backend Start).
diff --git a/chromecast/public/media/media_pipeline_device_params.h b/chromecast/public/media/media_pipeline_device_params.h index 6d70886..a6e5622 100644 --- a/chromecast/public/media/media_pipeline_device_params.h +++ b/chromecast/public/media/media_pipeline_device_params.h
@@ -44,9 +44,13 @@ kModeIgnorePtsAndVSync = 2, // Almost same as kModeSyncPts except two things: // 1. When pushing silence to the backend decoder, set an invalid timestamp - // to the silence buffer. - // 2. When pushing non-silence buffers, do not adjust the timestamp. - kModeHwAvSyncPts = 3, + // to the silence buffer. If the stream uses hardware av sync mode, it will + // drop the silence buffer. Otherwise we will play the silence. + // 2. When pushing non-silence buffers, do not adjust the timestamp. When + // calculating the rendering delay, the silence buffer will be counted. But + // when calculating the current playback position of the real audio data, + // buffers without timestamp, like silence buffer, will not be counted. + kModeApkSyncPts = 3, }; enum AudioStreamType {
diff --git a/chromeos/ash/components/oobe_quick_start/connectivity/fast_pair_advertiser.cc b/chromeos/ash/components/oobe_quick_start/connectivity/fast_pair_advertiser.cc index 221701a..0e73cd2 100644 --- a/chromeos/ash/components/oobe_quick_start/connectivity/fast_pair_advertiser.cc +++ b/chromeos/ash/components/oobe_quick_start/connectivity/fast_pair_advertiser.cc
@@ -17,10 +17,6 @@ constexpr uint8_t kFastPairModelId[] = {0x41, 0xc0, 0xd9}; constexpr uint16_t kCompanyId = 0x00e0; -// TODO(b/207087915): This value comes from Android, but we may need to -// find a more appropriate power setting for Chrome OS devices. -const int8_t kAdjustedTxPower = -66; - } // namespace // static @@ -90,9 +86,6 @@ std::make_unique<device::BluetoothAdvertisement::ServiceData>(); auto payload = std::vector<uint8_t>(std::begin(kFastPairModelId), std::end(kFastPairModelId)); - auto service_metadata = GenerateServiceMetadata(); - payload.insert(std::end(payload), std::begin(service_metadata), - std::end(service_metadata)); service_data->insert(std::pair<std::string, std::vector<uint8_t>>( kFastPairServiceUuid, payload)); advertisement_data->set_service_data(std::move(service_data)); @@ -104,8 +97,6 @@ kCompanyId, manufacturer_metadata)); advertisement_data->set_manufacturer_data(std::move(manufacturer_data)); - advertisement_data->set_include_tx_power(true); - adapter_->RegisterAdvertisement( std::move(advertisement_data), base::BindOnce(&FastPairAdvertiser::OnRegisterAdvertisement, @@ -155,16 +146,6 @@ // |this| might be destroyed here, do not access local fields. } -std::vector<uint8_t> FastPairAdvertiser::GenerateServiceMetadata() { - std::vector<uint8_t> metadata; - - // Note: We convert this to a positive value before transport to align with - // Android's behavior. - int8_t powerConverted = -kAdjustedTxPower; - metadata.push_back(powerConverted); - return metadata; -} - std::vector<uint8_t> FastPairAdvertiser::GenerateManufacturerMetadata() { // TODO(b/235403498): This code may need to be updated later to be derived // from the device BT address. It is not required in order for the
diff --git a/chromeos/ash/components/oobe_quick_start/connectivity/fast_pair_advertiser.h b/chromeos/ash/components/oobe_quick_start/connectivity/fast_pair_advertiser.h index 0201b6a..85b7284 100644 --- a/chromeos/ash/components/oobe_quick_start/connectivity/fast_pair_advertiser.h +++ b/chromeos/ash/components/oobe_quick_start/connectivity/fast_pair_advertiser.h
@@ -68,9 +68,6 @@ void OnUnregisterAdvertisementError( device::BluetoothAdvertisement::ErrorCode error_code); - // Returns metadata in format [ adjusted_tx_power (1 byte) ]. - std::vector<uint8_t> GenerateServiceMetadata(); - // Returns metadata in format [ fast_pair_code (2 bytes) ]. std::vector<uint8_t> GenerateManufacturerMetadata();
diff --git a/chromeos/ash/components/oobe_quick_start/connectivity/fast_pair_advertiser_unittest.cc b/chromeos/ash/components/oobe_quick_start/connectivity/fast_pair_advertiser_unittest.cc index bdac6ec..7b4e643 100644 --- a/chromeos/ash/components/oobe_quick_start/connectivity/fast_pair_advertiser_unittest.cc +++ b/chromeos/ash/components/oobe_quick_start/connectivity/fast_pair_advertiser_unittest.cc
@@ -24,7 +24,6 @@ constexpr const char kFastPairServiceUuid[] = "0000fe2c-0000-1000-8000-00805f9b34fb"; const uint8_t kFastPairModelId[] = {0x41, 0xc0, 0xd9}; -const int8_t kAdjustedTxPower = -66; } // namespace @@ -154,12 +153,6 @@ auto expected_payload = std::vector<uint8_t>(std::begin(kFastPairModelId), std::end(kFastPairModelId)); - std::vector<uint8_t> metadata; - int8_t power_converted = -kAdjustedTxPower; - metadata.push_back(power_converted); - expected_payload.insert(std::end(expected_payload), std::begin(metadata), - std::end(metadata)); - EXPECT_EQ(expected_payload, register_args_->service_data[kFastPairServiceUuid]); }
diff --git a/chromeos/components/quick_answers/BUILD.gn b/chromeos/components/quick_answers/BUILD.gn index f4c358f..f8680da 100644 --- a/chromeos/components/quick_answers/BUILD.gn +++ b/chromeos/components/quick_answers/BUILD.gn
@@ -38,6 +38,8 @@ "utils/quick_answers_metrics.h", "utils/quick_answers_utils.cc", "utils/quick_answers_utils.h", + "utils/spell_check_language.cc", + "utils/spell_check_language.h", "utils/spell_checker.cc", "utils/spell_checker.h", "utils/unit_conversion_constants.cc",
diff --git a/chromeos/components/quick_answers/public/cpp/quick_answers_state.cc b/chromeos/components/quick_answers/public/cpp/quick_answers_state.cc index 21f9216a..64e0d48 100644 --- a/chromeos/components/quick_answers/public/cpp/quick_answers_state.cc +++ b/chromeos/components/quick_answers/public/cpp/quick_answers_state.cc
@@ -91,9 +91,13 @@ if (resolved_application_locale_.empty()) return; - is_eligible_ = IsQuickAnswersAllowedForLocale( + bool is_eligible = IsQuickAnswersAllowedForLocale( resolved_application_locale_, icu::Locale::getDefault().getName()); + if (is_eligible_ == is_eligible) + return; + is_eligible_ = is_eligible; + for (auto& observer : observers_) { observer.OnEligibilityChanged(is_eligible_); }
diff --git a/chromeos/components/quick_answers/utils/spell_check_language.cc b/chromeos/components/quick_answers/utils/spell_check_language.cc new file mode 100644 index 0000000..935f268 --- /dev/null +++ b/chromeos/components/quick_answers/utils/spell_check_language.cc
@@ -0,0 +1,258 @@ +// 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 "chromeos/components/quick_answers/utils/spell_check_language.h" + +#include "base/files/file_util.h" +#include "base/logging.h" +#include "base/path_service.h" +#include "base/task/task_runner_util.h" +#include "base/task/task_traits.h" +#include "base/task/thread_pool.h" +#include "base/threading/scoped_blocking_call.h" +#include "chrome/common/chrome_paths.h" +#include "components/spellcheck/common/spellcheck_common.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/browser/service_process_host.h" +#include "services/network/public/cpp/resource_request.h" +#include "services/network/public/cpp/shared_url_loader_factory.h" +#include "services/network/public/cpp/simple_url_loader.h" +#include "services/network/public/mojom/url_response_head.mojom.h" + +namespace quick_answers { +namespace { + +constexpr char kDownloadServerUrl[] = + "https://redirector.gvt1.com/edgedl/chrome/dict/"; + +constexpr net::NetworkTrafficAnnotationTag kNetworkTrafficAnnotationTag = + net::DefineNetworkTrafficAnnotation("quick_answers_spellchecker", R"( + semantics { + sender: "Quick answers Spellchecker" + description: + "Download dictionary for Quick answers feature if necessary." + trigger: "Quick answers feature enabled." + data: + "The spell checking language identifier. No user identifier is " + "sent." + destination: GOOGLE_OWNED_SERVICE + } + policy { + cookies_allowed: NO + setting: + "Quick Answers can be enabled/disabled in ChromeOS Settings and" + "is subject to eligibility requirements." + chrome_policy { + QuickAnswersEnabled { + QuickAnswersEnabled: false + } + } + })"); + +constexpr int kMaxRetries = 3; + +base::FilePath GetDictionaryFilePath(const std::string& language) { + base::FilePath dict_dir; + base::PathService::Get(chrome::DIR_APP_DICTIONARIES, &dict_dir); + base::FilePath dict_path = + spellcheck::GetVersionedFileName(language, dict_dir); + return dict_path; +} + +GURL GetDictionaryURL(const std::string& file_name) { + return GURL(std::string(kDownloadServerUrl) + base::ToLowerASCII(file_name)); +} + +bool SaveDictionaryData(std::unique_ptr<std::string> data, + const base::FilePath& file_path) { + base::ScopedBlockingCall scoped_blocking_call(FROM_HERE, + base::BlockingType::MAY_BLOCK); + + // Create a temporary file. + base::FilePath tmp_path; + if (!base::CreateTemporaryFileInDir(file_path.DirName(), &tmp_path)) { + LOG(ERROR) << "Failed to create a temporary file."; + return false; + } + + // Write to the temporary file. + size_t bytes_written = + base::WriteFile(tmp_path, data->data(), data->length()); + if (bytes_written != data->length()) { + base::DeleteFile(tmp_path); + LOG(ERROR) << "Failed to write dictionary data to the temporary file"; + return false; + } + + // Atomically rename the temporary file to become the real one. + return base::ReplaceFile(tmp_path, file_path, nullptr); +} + +base::File OpenDictionaryFile(const base::FilePath& file_path) { + base::ScopedBlockingCall scoped_blocking_call(FROM_HERE, + base::BlockingType::MAY_BLOCK); + + base::File file(file_path, base::File::FLAG_READ | base::File::FLAG_OPEN); + return file; +} + +void CloseDictionaryFile(base::File file) { + base::ScopedBlockingCall scoped_blocking_call(FROM_HERE, + base::BlockingType::MAY_BLOCK); + file.Close(); +} + +void RemoveDictionaryFile(const base::FilePath& file_path) { + base::ScopedBlockingCall scoped_blocking_call(FROM_HERE, + base::BlockingType::MAY_BLOCK); + + base::DeleteFile(file_path); +} + +} // namespace + +SpellCheckLanguage::SpellCheckLanguage( + scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory) + : task_runner_(base::ThreadPool::CreateSequencedTaskRunner( + {base::MayBlock(), base::TaskPriority::USER_VISIBLE})), + url_loader_factory_(url_loader_factory) {} + +SpellCheckLanguage::~SpellCheckLanguage() = default; + +void SpellCheckLanguage::Initialize(const std::string& locale) { + locale_ = locale; + dictionary_file_path_ = GetDictionaryFilePath(locale); + + base::PostTaskAndReplyWithResult( + task_runner_.get(), FROM_HERE, + base::BindOnce(&base::PathExists, dictionary_file_path_), + base::BindOnce(&SpellCheckLanguage::OnPathExistsComplete, + weak_factory_.GetWeakPtr())); +} + +void SpellCheckLanguage::CheckSpelling(const std::string& word, + CheckSpellingCallback callback) { + if (!dictionary_initialized_) { + std::move(callback).Run(false); + return; + } + + dictionary_->CheckSpelling(word, std::move(callback)); +} + +void SpellCheckLanguage::InitializeSpellCheckService() { + if (!service_) { + service_ = content::ServiceProcessHost::Launch<mojom::SpellCheckService>( + content::ServiceProcessHost::Options() + .WithDisplayName("Quick answers spell check service") + .Pass()); + } + + base::PostTaskAndReplyWithResult( + task_runner_.get(), FROM_HERE, + base::BindOnce(&OpenDictionaryFile, dictionary_file_path_), + base::BindOnce(&SpellCheckLanguage::OnOpenDictionaryFileComplete, + weak_factory_.GetWeakPtr())); +} + +void SpellCheckLanguage::OnSimpleURLLoaderComplete( + std::unique_ptr<std::string> data) { + int response_code = -1; + if (loader_->ResponseInfo() && loader_->ResponseInfo()->headers) + response_code = loader_->ResponseInfo()->headers->response_code(); + + if (loader_->NetError() != net::OK || ((response_code / 100) != 2)) { + LOG(ERROR) << "Failed to download the dictionary."; + MaybeRetryInitialize(); + return; + } + + // Basic sanity check on the dictionary data. + if (!data || data->size() < 4 || data->compare(0, 4, "BDic") != 0) { + LOG(ERROR) << "Downloaded dictionary data is empty or broken."; + MaybeRetryInitialize(); + return; + } + + base::PostTaskAndReplyWithResult( + task_runner_.get(), FROM_HERE, + base::BindOnce(&SaveDictionaryData, std::move(data), + dictionary_file_path_), + base::BindOnce(&SpellCheckLanguage::OnSaveDictionaryDataComplete, + weak_factory_.GetWeakPtr())); +} + +void SpellCheckLanguage::OnDictionaryCreated( + mojo::PendingRemote<mojom::SpellCheckDictionary> dictionary) { + dictionary_.reset(); + + if (dictionary.is_valid()) { + dictionary_.Bind(std::move(dictionary)); + dictionary_initialized_ = true; + return; + } + + MaybeRetryInitialize(); +} + +void SpellCheckLanguage::MaybeRetryInitialize() { + task_runner_->PostTask( + FROM_HERE, base::BindOnce(&RemoveDictionaryFile, dictionary_file_path_)); + + if (num_retries_ >= kMaxRetries) { + LOG(ERROR) << "Service initialize failed after max retries"; + service_.reset(); + return; + } + + ++num_retries_; + Initialize(locale_); +} + +void SpellCheckLanguage::OnPathExistsComplete(bool path_exists) { + // If the dictionary is not available, try to download it from the server. + if (!path_exists) { + auto url = + GetDictionaryURL(dictionary_file_path_.BaseName().MaybeAsASCII()); + + auto resource_request = std::make_unique<network::ResourceRequest>(); + resource_request->url = url; + resource_request->credentials_mode = network::mojom::CredentialsMode::kOmit; + loader_ = network::SimpleURLLoader::Create(std::move(resource_request), + kNetworkTrafficAnnotationTag); + loader_->SetRetryOptions( + /*max_retries=*/5, + network::SimpleURLLoader::RetryMode::RETRY_ON_5XX | + network::SimpleURLLoader::RETRY_ON_NETWORK_CHANGE); + + // TODO(b/226221138): Probably use |DownloadToTempFile| instead. + loader_->DownloadToStringOfUnboundedSizeUntilCrashAndDie( + url_loader_factory_.get(), + base::BindOnce(&SpellCheckLanguage::OnSimpleURLLoaderComplete, + base::Unretained(this))); + return; + } + + InitializeSpellCheckService(); +} + +void SpellCheckLanguage::OnSaveDictionaryDataComplete(bool dictionary_saved) { + if (!dictionary_saved) { + MaybeRetryInitialize(); + return; + } + + InitializeSpellCheckService(); +} + +void SpellCheckLanguage::OnOpenDictionaryFileComplete(base::File file) { + service_->CreateDictionary( + file.Duplicate(), base::BindOnce(&SpellCheckLanguage::OnDictionaryCreated, + base::Unretained(this))); + + task_runner_->PostTask(FROM_HERE, + base::BindOnce(&CloseDictionaryFile, std::move(file))); +} + +} // namespace quick_answers
diff --git a/chromeos/components/quick_answers/utils/spell_check_language.h b/chromeos/components/quick_answers/utils/spell_check_language.h new file mode 100644 index 0000000..a9d9e83 --- /dev/null +++ b/chromeos/components/quick_answers/utils/spell_check_language.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 CHROMEOS_COMPONENTS_QUICK_ANSWERS_UTILS_SPELL_CHECK_LANGUAGE_H_ +#define CHROMEOS_COMPONENTS_QUICK_ANSWERS_UTILS_SPELL_CHECK_LANGUAGE_H_ + +#include <memory> +#include <string> + +#include "base/files/file_path.h" +#include "base/memory/weak_ptr.h" +#include "base/scoped_observation.h" +#include "base/task/sequenced_task_runner.h" +#include "chromeos/components/quick_answers/public/cpp/quick_answers_state.h" +#include "chromeos/components/quick_answers/public/mojom/spell_check.mojom.h" +#include "mojo/public/cpp/bindings/remote.h" + +namespace network { +class SharedURLLoaderFactory; +class SimpleURLLoader; +} // namespace network + +namespace quick_answers { + +// Utility class for spell check. +class SpellCheckLanguage : public QuickAnswersStateObserver { + public: + using CheckSpellingCallback = base::OnceCallback<void(bool)>; + + explicit SpellCheckLanguage( + scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory); + + SpellCheckLanguage(const SpellCheckLanguage&) = delete; + SpellCheckLanguage& operator=(const SpellCheckLanguage&) = delete; + + ~SpellCheckLanguage() override; + + void Initialize(const std::string& locale); + + // Check spelling of the given word, run |callback| with true if the word is + // spelled correctly. Virtual for testing. + virtual void CheckSpelling(const std::string& word, + CheckSpellingCallback callback); + + base::WeakPtr<SpellCheckLanguage> GetWeakPtr() { + return weak_factory_.GetWeakPtr(); + } + + private: + void InitializeSpellCheckService(); + + void OnSimpleURLLoaderComplete(std::unique_ptr<std::string> response_body); + + void OnDictionaryCreated( + mojo::PendingRemote<mojom::SpellCheckDictionary> dictionary); + + void MaybeRetryInitialize(); + + // The reply points for PostTaskAndReplyWithResult. + void OnPathExistsComplete(bool path_exists); + void OnSaveDictionaryDataComplete(bool dictionary_saved); + void OnOpenDictionaryFileComplete(base::File file); + + // Task runner where the file operations takes place. + scoped_refptr<base::SequencedTaskRunner> const task_runner_; + + std::string locale_; + + // Whether the spell check dictionary has been successfully initialized. + bool dictionary_initialized_ = false; + + // Number of retries used for initializing the spell check service. + int num_retries_ = 0; + + base::FilePath dictionary_file_path_; + scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_; + std::unique_ptr<network::SimpleURLLoader> loader_; + mojo::Remote<mojom::SpellCheckService> service_; + mojo::Remote<mojom::SpellCheckDictionary> dictionary_; + + base::WeakPtrFactory<SpellCheckLanguage> weak_factory_{this}; +}; + +} // namespace quick_answers + +#endif // CHROMEOS_COMPONENTS_QUICK_ANSWERS_UTILS_SPELL_CHECK_LANGUAGE_H_
diff --git a/chromeos/components/quick_answers/utils/spell_checker.cc b/chromeos/components/quick_answers/utils/spell_checker.cc index 3e2d3e5..2d1f936 100644 --- a/chromeos/components/quick_answers/utils/spell_checker.cc +++ b/chromeos/components/quick_answers/utils/spell_checker.cc
@@ -4,277 +4,127 @@ #include "chromeos/components/quick_answers/utils/spell_checker.h" -#include "base/files/file_util.h" #include "base/logging.h" -#include "base/path_service.h" -#include "base/task/task_runner_util.h" -#include "base/task/task_traits.h" -#include "base/task/thread_pool.h" -#include "base/threading/scoped_blocking_call.h" -#include "chrome/common/chrome_paths.h" -#include "components/spellcheck/common/spellcheck_common.h" -#include "content/public/browser/browser_thread.h" -#include "content/public/browser/service_process_host.h" -#include "services/network/public/cpp/resource_request.h" +#include "base/strings/string_split.h" +#include "chromeos/constants/chromeos_features.h" #include "services/network/public/cpp/shared_url_loader_factory.h" -#include "services/network/public/cpp/simple_url_loader.h" -#include "services/network/public/mojom/url_response_head.mojom.h" +#include "ui/base/l10n/l10n_util.h" namespace quick_answers { namespace { -constexpr char kDownloadServerUrl[] = - "https://redirector.gvt1.com/edgedl/chrome/dict/"; - -constexpr net::NetworkTrafficAnnotationTag kNetworkTrafficAnnotationTag = - net::DefineNetworkTrafficAnnotation("quick_answers_spellchecker", R"( - semantics { - sender: "Quick answers Spellchecker" - description: - "Download dictionary for Quick answers feature if necessary." - trigger: "Quick answers feature enabled." - data: - "The spell checking language identifier. No user identifier is " - "sent." - destination: GOOGLE_OWNED_SERVICE - } - policy { - cookies_allowed: NO - setting: - "Quick Answers can be enabled/disabled in ChromeOS Settings and" - "is subject to eligibility requirements." - chrome_policy { - QuickAnswersEnabled { - QuickAnswersEnabled: false - } - } - })"); - -constexpr int kMaxRetries = 3; - -base::FilePath GetDictionaryFilePath(const std::string& language) { - base::FilePath dict_dir; - base::PathService::Get(chrome::DIR_APP_DICTIONARIES, &dict_dir); - base::FilePath dict_path = - spellcheck::GetVersionedFileName(language, dict_dir); - return dict_path; -} - -GURL GetDictionaryURL(const std::string& file_name) { - return GURL(std::string(kDownloadServerUrl) + base::ToLowerASCII(file_name)); -} - -bool SaveDictionaryData(std::unique_ptr<std::string> data, - const base::FilePath& file_path) { - base::ScopedBlockingCall scoped_blocking_call(FROM_HERE, - base::BlockingType::MAY_BLOCK); - - // Create a temporary file. - base::FilePath tmp_path; - if (!base::CreateTemporaryFileInDir(file_path.DirName(), &tmp_path)) { - LOG(ERROR) << "Failed to create a temporary file."; - return false; - } - - // Write to the temporary file. - size_t bytes_written = - base::WriteFile(tmp_path, data->data(), data->length()); - if (bytes_written != data->length()) { - base::DeleteFile(tmp_path); - LOG(ERROR) << "Failed to write dictionary data to the temporary file"; - return false; - } - - // Atomically rename the temporary file to become the real one. - return base::ReplaceFile(tmp_path, file_path, nullptr); -} - -base::File OpenDictionaryFile(const base::FilePath& file_path) { - base::ScopedBlockingCall scoped_blocking_call(FROM_HERE, - base::BlockingType::MAY_BLOCK); - - base::File file(file_path, base::File::FLAG_READ | base::File::FLAG_OPEN); - return file; -} - -void CloseDictionaryFile(base::File file) { - base::ScopedBlockingCall scoped_blocking_call(FROM_HERE, - base::BlockingType::MAY_BLOCK); - file.Close(); -} - -void RemoveDictionaryFile(const base::FilePath& file_path) { - base::ScopedBlockingCall scoped_blocking_call(FROM_HERE, - base::BlockingType::MAY_BLOCK); - - base::DeleteFile(file_path); -} - } // namespace SpellChecker::SpellChecker( scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory) - : task_runner_(base::ThreadPool::CreateSequencedTaskRunner( - {base::MayBlock(), base::TaskPriority::USER_VISIBLE})), - url_loader_factory_(url_loader_factory) { + : url_loader_factory_(url_loader_factory) { quick_answers_state_observation_.Observe(QuickAnswersState::Get()); } -SpellChecker::~SpellChecker() = default; +SpellChecker::~SpellChecker() { + spellcheck_languages_.clear(); +} void SpellChecker::CheckSpelling(const std::string& word, CheckSpellingCallback callback) { - if (!dictionary_initialized_) { + auto iterator = spellcheck_languages_.begin(); + if (iterator == spellcheck_languages_.end()) { std::move(callback).Run(false); return; } - dictionary_->CheckSpelling(word, std::move(callback)); + iterator->get()->CheckSpelling( + word, base::BindOnce(&SpellChecker::CollectResults, + base::Unretained(this), word, std::move(callback), + iterator, languages_list_version_)); } void SpellChecker::OnSettingsEnabled(bool enabled) { feature_enabled_ = enabled; - // Reset spell check service if the feature is disabled. - if (!enabled) { - dictionary_.reset(); - service_.reset(); - return; - } - - if (!dictionary_file_path_.empty()) - InitializeDictionary(); + OnStateUpdated(); } void SpellChecker::OnApplicationLocaleReady(const std::string& locale) { - dictionary_file_path_ = GetDictionaryFilePath(locale); + application_locale_ = locale; - if (feature_enabled_) - InitializeDictionary(); + OnStateUpdated(); } -void SpellChecker::InitializeDictionary() { - DCHECK(!dictionary_file_path_.empty()); +void SpellChecker::OnPreferredLanguagesChanged( + const std::string& preferred_languages) { + preferred_languages_ = preferred_languages; - base::PostTaskAndReplyWithResult( - task_runner_.get(), FROM_HERE, - base::BindOnce(&base::PathExists, dictionary_file_path_), - base::BindOnce(&SpellChecker::OnPathExistsComplete, - weak_factory_.GetWeakPtr())); + OnStateUpdated(); } -void SpellChecker::InitializeSpellCheckService() { - if (!service_) { - service_ = content::ServiceProcessHost::Launch<mojom::SpellCheckService>( - content::ServiceProcessHost::Options() - .WithDisplayName("Quick answers spell check service") - .Pass()); - } +void SpellChecker::OnEligibilityChanged(bool eligible) { + feature_eligible_ = eligible; - base::PostTaskAndReplyWithResult( - task_runner_.get(), FROM_HERE, - base::BindOnce(&OpenDictionaryFile, dictionary_file_path_), - base::BindOnce(&SpellChecker::OnOpenDictionaryFileComplete, - weak_factory_.GetWeakPtr())); + OnStateUpdated(); } -void SpellChecker::OnSimpleURLLoaderComplete( - std::unique_ptr<std::string> data) { - int response_code = -1; - if (loader_->ResponseInfo() && loader_->ResponseInfo()->headers) - response_code = loader_->ResponseInfo()->headers->response_code(); - - if (loader_->NetError() != net::OK || ((response_code / 100) != 2)) { - LOG(ERROR) << "Failed to download the dictionary."; - MaybeRetryInitialize(); +void SpellChecker::OnStateUpdated() { + if (!feature_eligible_.has_value() || !feature_enabled_.has_value() || + !application_locale_.has_value() || !preferred_languages_.has_value()) { + // Still waiting for all of the states to be ready. return; } - // Basic sanity check on the dictionary data. - if (!data || data->size() < 4 || data->compare(0, 4, "BDic") != 0) { - LOG(ERROR) << "Downloaded dictionary data is empty or broken."; - MaybeRetryInitialize(); + if (!feature_eligible_.value() || !feature_enabled_.value()) { + spellcheck_languages_.clear(); + languages_list_version_++; return; } - base::PostTaskAndReplyWithResult( - task_runner_.get(), FROM_HERE, - base::BindOnce(&SaveDictionaryData, std::move(data), - dictionary_file_path_), - base::BindOnce(&SpellChecker::OnSaveDictionaryDataComplete, - weak_factory_.GetWeakPtr())); + std::set<std::string> languages; + languages.insert(l10n_util::GetLanguage(application_locale_.value())); + + // Add preferred languages. + if (chromeos::features::IsQuickAnswersForMoreLocalesEnabled()) { + auto preferred_languages_list = + base::SplitString(preferred_languages_.value(), ",", + base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY); + for (const std::string& locale : preferred_languages_list) { + languages.insert(l10n_util::GetLanguage(locale)); + } + } + + spellcheck_languages_.clear(); + languages_list_version_++; + for (auto language : languages) { + spellcheck_languages_.push_back( + std::make_unique<SpellCheckLanguage>(url_loader_factory_)); + spellcheck_languages_.back()->Initialize(language); + } } -void SpellChecker::OnDictionaryCreated( - mojo::PendingRemote<mojom::SpellCheckDictionary> dictionary) { - dictionary_.reset(); - - if (dictionary.is_valid()) { - dictionary_.Bind(std::move(dictionary)); - dictionary_initialized_ = true; +void SpellChecker::CollectResults(const std::string& word, + CheckSpellingCallback callback, + SpellCheckLanguageIterator iterator, + int languages_list_version, + bool is_correct) { + if (is_correct) { + std::move(callback).Run(true); return; } - MaybeRetryInitialize(); -} - -void SpellChecker::MaybeRetryInitialize() { - task_runner_->PostTask( - FROM_HERE, base::BindOnce(&RemoveDictionaryFile, dictionary_file_path_)); - - if (num_retries_ >= kMaxRetries) { - LOG(ERROR) << "Service initialize failed after max retries"; - service_.reset(); + // The languages list has been updated, return false for the current call. + if (languages_list_version != languages_list_version_) { + std::move(callback).Run(true); return; } - ++num_retries_; - InitializeDictionary(); -} - -void SpellChecker::OnPathExistsComplete(bool path_exists) { - // If the dictionary is not available, try to download it from the server. - if (!path_exists) { - auto url = - GetDictionaryURL(dictionary_file_path_.BaseName().MaybeAsASCII()); - - auto resource_request = std::make_unique<network::ResourceRequest>(); - resource_request->url = url; - resource_request->credentials_mode = network::mojom::CredentialsMode::kOmit; - loader_ = network::SimpleURLLoader::Create(std::move(resource_request), - kNetworkTrafficAnnotationTag); - loader_->SetRetryOptions( - /*max_retries=*/5, - network::SimpleURLLoader::RetryMode::RETRY_ON_5XX | - network::SimpleURLLoader::RETRY_ON_NETWORK_CHANGE); - - // TODO(b/226221138): Probably use |DownloadToTempFile| instead. - loader_->DownloadToStringOfUnboundedSizeUntilCrashAndDie( - url_loader_factory_.get(), - base::BindOnce(&SpellChecker::OnSimpleURLLoaderComplete, - base::Unretained(this))); + iterator++; + if (iterator == spellcheck_languages_.end()) { + std::move(callback).Run(false); return; } - InitializeSpellCheckService(); -} - -void SpellChecker::OnSaveDictionaryDataComplete(bool dictionary_saved) { - if (!dictionary_saved) { - MaybeRetryInitialize(); - return; - } - - InitializeSpellCheckService(); -} - -void SpellChecker::OnOpenDictionaryFileComplete(base::File file) { - service_->CreateDictionary(file.Duplicate(), - base::BindOnce(&SpellChecker::OnDictionaryCreated, - base::Unretained(this))); - - task_runner_->PostTask(FROM_HERE, - base::BindOnce(&CloseDictionaryFile, std::move(file))); + iterator->get()->CheckSpelling( + word, base::BindOnce(&SpellChecker::CollectResults, + base::Unretained(this), word, std::move(callback), + iterator, languages_list_version)); } } // namespace quick_answers
diff --git a/chromeos/components/quick_answers/utils/spell_checker.h b/chromeos/components/quick_answers/utils/spell_checker.h index f78dbbe..cead569 100644 --- a/chromeos/components/quick_answers/utils/spell_checker.h +++ b/chromeos/components/quick_answers/utils/spell_checker.h
@@ -8,17 +8,14 @@ #include <memory> #include <string> -#include "base/files/file_path.h" #include "base/memory/weak_ptr.h" #include "base/scoped_observation.h" -#include "base/task/sequenced_task_runner.h" #include "chromeos/components/quick_answers/public/cpp/quick_answers_state.h" -#include "chromeos/components/quick_answers/public/mojom/spell_check.mojom.h" +#include "chromeos/components/quick_answers/utils/spell_check_language.h" #include "mojo/public/cpp/bindings/remote.h" namespace network { class SharedURLLoaderFactory; -class SimpleURLLoader; } // namespace network namespace quick_answers { @@ -27,6 +24,8 @@ class SpellChecker : public QuickAnswersStateObserver { public: using CheckSpellingCallback = base::OnceCallback<void(bool)>; + using SpellCheckLanguageIterator = + std::vector<std::unique_ptr<SpellCheckLanguage>>::iterator; explicit SpellChecker( scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory); @@ -44,44 +43,41 @@ // QuickAnswersStateObserver: void OnSettingsEnabled(bool enabled) override; void OnApplicationLocaleReady(const std::string& locale) override; + void OnPreferredLanguagesChanged( + const std::string& preferred_languages) override; + void OnEligibilityChanged(bool eligible) override; base::WeakPtr<SpellChecker> GetWeakPtr() { return weak_factory_.GetWeakPtr(); } private: - void InitializeDictionary(); - void InitializeSpellCheckService(); + // Called when the Quick answers states are updated. + void OnStateUpdated(); - void OnSimpleURLLoaderComplete(std::unique_ptr<std::string> response_body); + // Collect spell check results from the language indicated by |iterator|. Run + // |callback| with true if the word is found in the current language, + // otherwise continue to check the next language. + void CollectResults(const std::string& word, + CheckSpellingCallback callback, + SpellCheckLanguageIterator iterator, + int languages_list_version, + bool is_correct); - void OnDictionaryCreated( - mojo::PendingRemote<mojom::SpellCheckDictionary> dictionary); - - void MaybeRetryInitialize(); - - // The reply points for PostTaskAndReplyWithResult. - void OnPathExistsComplete(bool path_exists); - void OnSaveDictionaryDataComplete(bool dictionary_saved); - void OnOpenDictionaryFileComplete(base::File file); - - // Task runner where the file operations takes place. - scoped_refptr<base::SequencedTaskRunner> const task_runner_; - - // Whether the Quick answers feature is enabled in settings. - bool feature_enabled_ = false; - - // Whether the spell check dictionary has been successfully initialized. - bool dictionary_initialized_ = false; - - // Number of retries used for initializing the spell check service. - int num_retries_ = 0; - - base::FilePath dictionary_file_path_; scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_; - std::unique_ptr<network::SimpleURLLoader> loader_; - mojo::Remote<mojom::SpellCheckService> service_; - mojo::Remote<mojom::SpellCheckDictionary> dictionary_; + + // States of the Quick answers feature. + absl::optional<bool> feature_eligible_; + absl::optional<bool> feature_enabled_; + absl::optional<std::string> application_locale_; + absl::optional<std::string> preferred_languages_; + + // List of spell check languages. + std::vector<std::unique_ptr<SpellCheckLanguage>> spellcheck_languages_; + + // Version of the languages list to invalidate pending calls if the languages + // has ben updated. + int languages_list_version_ = 0; base::ScopedObservation<QuickAnswersState, QuickAnswersStateObserver> quick_answers_state_observation_{this};
diff --git a/chromeos/dbus/chunneld/chunneld_client.cc b/chromeos/dbus/chunneld/chunneld_client.cc index 5af12e4..ed4050ba 100644 --- a/chromeos/dbus/chunneld/chunneld_client.cc +++ b/chromeos/dbus/chunneld/chunneld_client.cc
@@ -12,15 +12,19 @@ #include "base/memory/weak_ptr.h" #include "base/observer_list.h" #include "base/threading/thread_task_runner_handle.h" +#include "chromeos/dbus/chunneld/fake_chunneld_client.h" #include "dbus/bus.h" #include "dbus/message.h" #include "third_party/cros_system_api/dbus/chunneld/dbus-constants.h" namespace chromeos { +namespace { + +ChunneldClient* g_instance = nullptr; class ChunneldClientImpl : public ChunneldClient { public: - ChunneldClientImpl() {} + ChunneldClientImpl() = default; ~ChunneldClientImpl() override = default; @@ -41,7 +45,6 @@ chunneld_proxy_->WaitForServiceToBeAvailable(std::move(callback)); } - protected: void Init(dbus::Bus* bus) override { chunneld_proxy_ = bus->GetObjectProxy( vm_tools::chunneld::kChunneldServiceName, @@ -92,12 +95,38 @@ base::WeakPtrFactory<ChunneldClientImpl> weak_ptr_factory_{this}; }; -ChunneldClient::ChunneldClient() = default; +} // namespace -ChunneldClient::~ChunneldClient() = default; +// static +ChunneldClient* ChunneldClient::Get() { + return g_instance; +} -std::unique_ptr<ChunneldClient> ChunneldClient::Create() { - return std::make_unique<ChunneldClientImpl>(); +// static +void ChunneldClient::Initialize(dbus::Bus* bus) { + CHECK(bus); + (new ChunneldClientImpl())->Init(bus); +} + +// static +void ChunneldClient::InitializeFake() { + (new FakeChunneldClient())->Init(nullptr); +} + +// static +void ChunneldClient::Shutdown() { + CHECK(g_instance); + delete g_instance; +} + +ChunneldClient::ChunneldClient() { + CHECK(!g_instance); + g_instance = this; +} + +ChunneldClient::~ChunneldClient() { + CHECK_EQ(g_instance, this); + g_instance = nullptr; } } // namespace chromeos
diff --git a/chromeos/dbus/chunneld/chunneld_client.h b/chromeos/dbus/chunneld/chunneld_client.h index f547a5e..e6f7af8 100644 --- a/chromeos/dbus/chunneld/chunneld_client.h +++ b/chromeos/dbus/chunneld/chunneld_client.h
@@ -26,21 +26,28 @@ virtual void ChunneldServiceStarted() = 0; }; + // Returns the global instance if initialized. May return null. + static ChunneldClient* Get(); + + // Creates and initializes the global instance. |bus| must not be null. + static void Initialize(dbus::Bus* bus); + + // Creates and initializes a fake global instance used on Linux desktop, if + // no instance already exists. + static void InitializeFake(); + + // Destroys the global instance if it has been initialized. + static void Shutdown(); + + ChunneldClient(const ChunneldClient&) = delete; + ChunneldClient& operator=(const ChunneldClient&) = delete; + // Adds an observer. virtual void AddObserver(Observer* observer) = 0; // Removes an observer if added. virtual void RemoveObserver(Observer* observer) = 0; - ~ChunneldClient() override; - - ChunneldClient(const ChunneldClient&) = delete; - ChunneldClient& operator=(const ChunneldClient&) = delete; - - // Factory function, creates a new instance and returns ownership. - // For normal usage, access the singleton via DBusThreadManager::Get(). - static std::unique_ptr<ChunneldClient> Create(); - // Registers |callback| to run when the Concierge service becomes available. // If the service is already available, or if connecting to the name-owner- // changed signal fails, |callback| will be run once asynchronously. @@ -50,8 +57,9 @@ dbus::ObjectProxy::WaitForServiceToBeAvailableCallback callback) = 0; protected: - // Create() should be used instead. + // Initialize() should be used instead. ChunneldClient(); + ~ChunneldClient() override; }; } // namespace chromeos
diff --git a/chromeos/dbus/chunneld/fake_chunneld_client.h b/chromeos/dbus/chunneld/fake_chunneld_client.h index babee6c6..ea4e20d 100644 --- a/chromeos/dbus/chunneld/fake_chunneld_client.h +++ b/chromeos/dbus/chunneld/fake_chunneld_client.h
@@ -22,6 +22,7 @@ FakeChunneldClient& operator=(const FakeChunneldClient&) = delete; // ChunneldClient: + void Init(dbus::Bus* bus) override {} void AddObserver(Observer* observer) override; void RemoveObserver(Observer* observer) override; void WaitForServiceToBeAvailable( @@ -30,9 +31,6 @@ void NotifyChunneldStopped(); void NotifyChunneldStarted(); - protected: - void Init(dbus::Bus* bus) override {} - private: void InitializeProtoResponses();
diff --git a/chromeos/dbus/dbus_clients_browser.cc b/chromeos/dbus/dbus_clients_browser.cc index 8fd5ad5..21d5baa 100644 --- a/chromeos/dbus/dbus_clients_browser.cc +++ b/chromeos/dbus/dbus_clients_browser.cc
@@ -17,8 +17,6 @@ #include "chromeos/dbus/arc/fake_arc_obb_mounter_client.h" #include "chromeos/dbus/cec_service/cec_service_client.h" #include "chromeos/dbus/cec_service/fake_cec_service_client.h" -#include "chromeos/dbus/chunneld/chunneld_client.h" -#include "chromeos/dbus/chunneld/fake_chunneld_client.h" #include "chromeos/dbus/cros_disks/cros_disks_client.h" #include "chromeos/dbus/cros_disks/fake_cros_disks_client.h" #include "chromeos/dbus/dbus_thread_manager.h" @@ -38,10 +36,6 @@ #include "chromeos/dbus/oobe_config/oobe_configuration_client.h" #include "chromeos/dbus/runtime_probe/fake_runtime_probe_client.h" #include "chromeos/dbus/runtime_probe/runtime_probe_client.h" -#include "chromeos/dbus/smbprovider/fake_smb_provider_client.h" -#include "chromeos/dbus/smbprovider/smb_provider_client.h" -#include "chromeos/dbus/virtual_file_provider/fake_virtual_file_provider_client.h" -#include "chromeos/dbus/virtual_file_provider/virtual_file_provider_client.h" namespace chromeos { @@ -67,7 +61,6 @@ CREATE_DBUS_CLIENT(ArcObbMounterClient, use_real_clients); cec_service_client_ = CREATE_DBUS_CLIENT(CecServiceClient, use_real_clients); cros_disks_client_ = CREATE_DBUS_CLIENT(CrosDisksClient, use_real_clients); - chunneld_client_ = CREATE_DBUS_CLIENT(ChunneldClient, use_real_clients); debug_daemon_client_ = CREATE_DBUS_CLIENT(DebugDaemonClient, use_real_clients); easy_unlock_client_ = CREATE_DBUS_CLIENT(EasyUnlockClient, use_real_clients); @@ -81,10 +74,6 @@ CREATE_DBUS_CLIENT(OobeConfigurationClient, use_real_clients); runtime_probe_client_ = CREATE_DBUS_CLIENT(RuntimeProbeClient, use_real_clients); - smb_provider_client_ = - CREATE_DBUS_CLIENT(SmbProviderClient, use_real_clients); - virtual_file_provider_client_ = - CREATE_DBUS_CLIENT(VirtualFileProviderClient, use_real_clients); } DBusClientsBrowser::~DBusClientsBrowser() = default; @@ -97,7 +86,6 @@ arc_midis_client_->Init(system_bus); arc_obb_mounter_client_->Init(system_bus); cec_service_client_->Init(system_bus); - chunneld_client_->Init(system_bus); cros_disks_client_->Init(system_bus); debug_daemon_client_->Init(system_bus); easy_unlock_client_->Init(system_bus); @@ -107,8 +95,6 @@ image_loader_client_->Init(system_bus); oobe_configuration_client_->Init(system_bus); runtime_probe_client_->Init(system_bus); - smb_provider_client_->Init(system_bus); - virtual_file_provider_client_->Init(system_bus); } } // namespace chromeos
diff --git a/chromeos/dbus/dbus_clients_browser.h b/chromeos/dbus/dbus_clients_browser.h index 23703a3..33f0cd2 100644 --- a/chromeos/dbus/dbus_clients_browser.h +++ b/chromeos/dbus/dbus_clients_browser.h
@@ -20,7 +20,6 @@ class ArcMidisClient; class ArcObbMounterClient; class CecServiceClient; -class ChunneldClient; class CrosDisksClient; class DebugDaemonClient; class EasyUnlockClient; @@ -30,8 +29,6 @@ class ImageLoaderClient; class OobeConfigurationClient; class RuntimeProbeClient; -class SmbProviderClient; -class VirtualFileProviderClient; // Owns D-Bus clients. // TODO(jamescook): Rename this class. "Browser" refers to the browser process @@ -59,7 +56,6 @@ std::unique_ptr<ArcMidisClient> arc_midis_client_; std::unique_ptr<ArcObbMounterClient> arc_obb_mounter_client_; std::unique_ptr<CecServiceClient> cec_service_client_; - std::unique_ptr<ChunneldClient> chunneld_client_; std::unique_ptr<CrosDisksClient> cros_disks_client_; std::unique_ptr<DebugDaemonClient> debug_daemon_client_; std::unique_ptr<EasyUnlockClient> easy_unlock_client_; @@ -69,8 +65,6 @@ std::unique_ptr<ImageLoaderClient> image_loader_client_; std::unique_ptr<OobeConfigurationClient> oobe_configuration_client_; std::unique_ptr<RuntimeProbeClient> runtime_probe_client_; - std::unique_ptr<SmbProviderClient> smb_provider_client_; - std::unique_ptr<VirtualFileProviderClient> virtual_file_provider_client_; }; } // namespace chromeos
diff --git a/chromeos/dbus/dbus_thread_manager.cc b/chromeos/dbus/dbus_thread_manager.cc index 181baf7..bbc15f4f 100644 --- a/chromeos/dbus/dbus_thread_manager.cc +++ b/chromeos/dbus/dbus_thread_manager.cc
@@ -15,7 +15,6 @@ #include "chromeos/dbus/arc/arc_midis_client.h" #include "chromeos/dbus/arc/arc_obb_mounter_client.h" #include "chromeos/dbus/cec_service/cec_service_client.h" -#include "chromeos/dbus/chunneld/chunneld_client.h" #include "chromeos/dbus/common/dbus_client.h" #include "chromeos/dbus/cros_disks/cros_disks_client.h" #include "chromeos/dbus/dbus_clients_browser.h" @@ -27,7 +26,6 @@ #include "chromeos/dbus/oobe_config/oobe_configuration_client.h" #include "chromeos/dbus/runtime_probe/runtime_probe_client.h" #include "chromeos/dbus/shill/shill_clients.h" -#include "chromeos/dbus/smbprovider/smb_provider_client.h" namespace chromeos { @@ -73,10 +71,6 @@ : nullptr; } -ChunneldClient* DBusThreadManager::GetChunneldClient() { - return clients_browser_ ? clients_browser_->chunneld_client_.get() : nullptr; -} - CrosDisksClient* DBusThreadManager::GetCrosDisksClient() { RETURN_DBUS_CLIENT(cros_disks_client_); } @@ -111,16 +105,6 @@ : nullptr; } -SmbProviderClient* DBusThreadManager::GetSmbProviderClient() { - RETURN_DBUS_CLIENT(smb_provider_client_); -} - -VirtualFileProviderClient* DBusThreadManager::GetVirtualFileProviderClient() { - return clients_browser_ - ? clients_browser_->virtual_file_provider_client_.get() - : nullptr; -} - #undef RETURN_DBUS_CLIENT void DBusThreadManager::InitializeClients() { @@ -213,9 +197,4 @@ image_loader_client_ = std::move(client); } -void DBusThreadManagerSetter::SetSmbProviderClient( - std::unique_ptr<SmbProviderClient> client) { - smb_provider_client_ = std::move(client); -} - } // namespace chromeos
diff --git a/chromeos/dbus/dbus_thread_manager.h b/chromeos/dbus/dbus_thread_manager.h index ff6575b..b2c7c21 100644 --- a/chromeos/dbus/dbus_thread_manager.h +++ b/chromeos/dbus/dbus_thread_manager.h
@@ -20,7 +20,6 @@ class ArcMidisClient; class ArcObbMounterClient; class CecServiceClient; -class ChunneldClient; class CrosDisksClient; class DBusClientsBrowser; class DBusThreadManagerSetter; @@ -31,8 +30,6 @@ class ImageLoaderClient; class OobeConfigurationClient; class RuntimeProbeClient; -class SmbProviderClient; -class VirtualFileProviderClient; // THIS CLASS IS BEING DEPRECATED. See README.md for guidelines and // https://crbug.com/647367 for details. @@ -74,7 +71,6 @@ ArcMidisClient* GetArcMidisClient(); ArcObbMounterClient* GetArcObbMounterClient(); CecServiceClient* GetCecServiceClient(); - ChunneldClient* GetChunneldClient(); CrosDisksClient* GetCrosDisksClient(); DebugDaemonClient* GetDebugDaemonClient(); EasyUnlockClient* GetEasyUnlockClient(); @@ -83,8 +79,6 @@ ImageLoaderClient* GetImageLoaderClient(); OobeConfigurationClient* GetOobeConfigurationClient(); RuntimeProbeClient* GetRuntimeProbeClient(); - SmbProviderClient* GetSmbProviderClient(); - VirtualFileProviderClient* GetVirtualFileProviderClient(); private: DBusThreadManager(); @@ -108,7 +102,6 @@ void SetGnubbyClient(std::unique_ptr<GnubbyClient> client); void SetImageBurnerClient(std::unique_ptr<ImageBurnerClient> client); void SetImageLoaderClient(std::unique_ptr<ImageLoaderClient> client); - void SetSmbProviderClient(std::unique_ptr<SmbProviderClient> client); private: friend class DBusThreadManager; @@ -124,7 +117,6 @@ std::unique_ptr<GnubbyClient> gnubby_client_; std::unique_ptr<ImageBurnerClient> image_burner_client_; std::unique_ptr<ImageLoaderClient> image_loader_client_; - std::unique_ptr<SmbProviderClient> smb_provider_client_; }; } // namespace chromeos
diff --git a/chromeos/dbus/smbprovider/smb_provider_client.cc b/chromeos/dbus/smbprovider/smb_provider_client.cc index b1a27f53..3f4655e 100644 --- a/chromeos/dbus/smbprovider/smb_provider_client.cc +++ b/chromeos/dbus/smbprovider/smb_provider_client.cc
@@ -11,6 +11,7 @@ #include "base/files/file_util.h" #include "base/logging.h" #include "base/memory/weak_ptr.h" +#include "chromeos/dbus/smbprovider/fake_smb_provider_client.h" #include "dbus/bus.h" #include "dbus/message.h" #include "dbus/object_proxy.h" @@ -19,6 +20,8 @@ namespace { +SmbProviderClient* g_instance = nullptr; + smbprovider::ErrorType GetErrorFromReader(dbus::MessageReader* reader) { int32_t int_error; if (!reader->PopInt32(&int_error) || @@ -91,7 +94,6 @@ &callback); } - protected: // DBusClient override. void Init(dbus::Bus* bus) override { proxy_ = bus->GetObjectProxy( @@ -225,13 +227,36 @@ } // namespace -SmbProviderClient::SmbProviderClient() = default; - -SmbProviderClient::~SmbProviderClient() = default; +// static +SmbProviderClient* SmbProviderClient::Get() { + return g_instance; +} // static -std::unique_ptr<SmbProviderClient> SmbProviderClient::Create() { - return std::make_unique<SmbProviderClientImpl>(); +void SmbProviderClient::Initialize(dbus::Bus* bus) { + CHECK(bus); + (new SmbProviderClientImpl())->Init(bus); +} + +// static +void SmbProviderClient::InitializeFake() { + (new FakeSmbProviderClient())->Init(nullptr); +} + +// static +void SmbProviderClient::Shutdown() { + CHECK(g_instance); + delete g_instance; +} + +SmbProviderClient::SmbProviderClient() { + CHECK(!g_instance); + g_instance = this; +} + +SmbProviderClient::~SmbProviderClient() { + CHECK_EQ(g_instance, this); + g_instance = nullptr; } } // namespace chromeos
diff --git a/chromeos/dbus/smbprovider/smb_provider_client.h b/chromeos/dbus/smbprovider/smb_provider_client.h index cb97c0f1..77c1b41 100644 --- a/chromeos/dbus/smbprovider/smb_provider_client.h +++ b/chromeos/dbus/smbprovider/smb_provider_client.h
@@ -38,11 +38,18 @@ SmbProviderClient(const SmbProviderClient&) = delete; SmbProviderClient& operator=(const SmbProviderClient&) = delete; - ~SmbProviderClient() override; + // Returns the global instance if initialized. May return null. + static SmbProviderClient* Get(); - // Factory function, creates a new instance and returns ownership. - // For normal usage, access the singleton via DBusThreadManager::Get(). - static std::unique_ptr<SmbProviderClient> Create(); + // Creates and initializes the global instance. |bus| must not be null. + static void Initialize(dbus::Bus* bus); + + // Creates and initializes a fake global instance used on Linux desktop, if + // no instance already exists. + static void InitializeFake(); + + // Destroys the global instance if it has been initialized. + static void Shutdown(); // Calls GetShares. This gets the shares from |server_url| and calls // |callback| when shares are found. The DirectoryEntryListProto will contain @@ -65,8 +72,9 @@ ParseNetBiosPacketCallback callback) = 0; protected: - // Create() should be used instead. + // Initialize() should be used instead. SmbProviderClient(); + ~SmbProviderClient() override; }; } // namespace chromeos
diff --git a/chromeos/dbus/virtual_file_provider/virtual_file_provider_client.cc b/chromeos/dbus/virtual_file_provider/virtual_file_provider_client.cc index ccf58fa..2abb038 100644 --- a/chromeos/dbus/virtual_file_provider/virtual_file_provider_client.cc +++ b/chromeos/dbus/virtual_file_provider/virtual_file_provider_client.cc
@@ -10,6 +10,7 @@ #include "base/callback.h" #include "base/callback_helpers.h" #include "base/logging.h" +#include "chromeos/dbus/virtual_file_provider/fake_virtual_file_provider_client.h" #include "dbus/bus.h" #include "dbus/message.h" #include "dbus/object_proxy.h" @@ -19,6 +20,8 @@ namespace { +VirtualFileProviderClient* g_instance = nullptr; + class VirtualFileProviderClientImpl : public VirtualFileProviderClient { public: VirtualFileProviderClientImpl() {} @@ -55,7 +58,6 @@ weak_ptr_factory_.GetWeakPtr(), std::move(callback))); } - protected: // DBusClient override. void Init(dbus::Bus* bus) override { proxy_ = bus->GetObjectProxy( @@ -105,13 +107,36 @@ } // namespace -VirtualFileProviderClient::VirtualFileProviderClient() = default; - -VirtualFileProviderClient::~VirtualFileProviderClient() = default; +// static +VirtualFileProviderClient* VirtualFileProviderClient::Get() { + return g_instance; +} // static -std::unique_ptr<VirtualFileProviderClient> VirtualFileProviderClient::Create() { - return std::make_unique<VirtualFileProviderClientImpl>(); +void VirtualFileProviderClient::Initialize(dbus::Bus* bus) { + CHECK(bus); + (new VirtualFileProviderClientImpl())->Init(bus); +} + +// static +void VirtualFileProviderClient::InitializeFake() { + (new FakeVirtualFileProviderClient())->Init(nullptr); +} + +// static +void VirtualFileProviderClient::Shutdown() { + CHECK(g_instance); + delete g_instance; +} + +VirtualFileProviderClient::VirtualFileProviderClient() { + CHECK(!g_instance); + g_instance = this; +} + +VirtualFileProviderClient::~VirtualFileProviderClient() { + CHECK_EQ(g_instance, this); + g_instance = nullptr; } } // namespace chromeos
diff --git a/chromeos/dbus/virtual_file_provider/virtual_file_provider_client.h b/chromeos/dbus/virtual_file_provider/virtual_file_provider_client.h index 9637edfa..a8c4e6e8 100644 --- a/chromeos/dbus/virtual_file_provider/virtual_file_provider_client.h +++ b/chromeos/dbus/virtual_file_provider/virtual_file_provider_client.h
@@ -30,12 +30,18 @@ base::OnceCallback<void(const absl::optional<std::string>& id)>; using OpenFileByIdCallback = base::OnceCallback<void(base::ScopedFD fd)>; - VirtualFileProviderClient(); - ~VirtualFileProviderClient() override; + // Returns the global instance if initialized. May return null. + static VirtualFileProviderClient* Get(); - // Factory function, creates a new instance and returns ownership. - // For normal usage, access the singleton via DBusThreadManager::Get(). - static std::unique_ptr<VirtualFileProviderClient> Create(); + // Creates and initializes the global instance. |bus| must not be null. + static void Initialize(dbus::Bus* bus); + + // Creates and initializes a fake global instance used on Linux desktop, if + // no instance already exists. + static void InitializeFake(); + + // Destroys the global instance if it has been initialized. + static void Shutdown(); // Generates and returns a unique ID, to be used by OpenFileById() for FD // creation. |size| will be used to perform boundary check when FD is seeked. @@ -47,6 +53,10 @@ // the read request is forwarded to the request handler. virtual void OpenFileById(const std::string& id, OpenFileByIdCallback callback) = 0; + + protected: + VirtualFileProviderClient(); + ~VirtualFileProviderClient() override; }; } // namespace chromeos
diff --git a/components/android_autofill/browser/android_autofill_manager.cc b/components/android_autofill/browser/android_autofill_manager.cc index 4330a12..db8575e 100644 --- a/components/android_autofill/browser/android_autofill_manager.cc +++ b/components/android_autofill/browser/android_autofill_manager.cc
@@ -64,9 +64,12 @@ NOTREACHED(); } -void AndroidAutofillManager::SetFillViaAutofillAssistantIntent( - const FormData& form, - const FormFieldData& field, +void AndroidAutofillManager::SetProfileFillViaAutofillAssistantIntent( + const autofill_assistant::AutofillAssistantIntent intent) { + NOTREACHED(); +} + +void AndroidAutofillManager::SetCreditCardFillViaAutofillAssistantIntent( const autofill_assistant::AutofillAssistantIntent intent) { NOTREACHED(); }
diff --git a/components/android_autofill/browser/android_autofill_manager.h b/components/android_autofill/browser/android_autofill_manager.h index 67d8e63..5ec843d8 100644 --- a/components/android_autofill/browser/android_autofill_manager.h +++ b/components/android_autofill/browser/android_autofill_manager.h
@@ -75,9 +75,10 @@ mojom::RendererFormDataAction action, const FormData& form); - void SetFillViaAutofillAssistantIntent( - const FormData& form, - const FormFieldData& field, + void SetProfileFillViaAutofillAssistantIntent( + const autofill_assistant::AutofillAssistantIntent intent) override; + + void SetCreditCardFillViaAutofillAssistantIntent( const autofill_assistant::AutofillAssistantIntent intent) override; protected:
diff --git a/components/autofill/content/browser/content_autofill_driver.cc b/components/autofill/content/browser/content_autofill_driver.cc index 8f159765..6e57a48 100644 --- a/components/autofill/content/browser/content_autofill_driver.cc +++ b/components/autofill/content/browser/content_autofill_driver.cc
@@ -391,10 +391,10 @@ const autofill_assistant::AutofillAssistantIntent intent) { DCHECK(autofill_manager_); if (fill_data.is_profile()) { - autofill_manager_->SetFillViaAutofillAssistantIntent(form, field, intent); + autofill_manager_->SetProfileFillViaAutofillAssistantIntent(intent); autofill_manager_->FillProfileForm(fill_data.profile(), form, field); } else if (fill_data.is_credit_card()) { - autofill_manager_->SetFillViaAutofillAssistantIntent(form, field, intent); + autofill_manager_->SetCreditCardFillViaAutofillAssistantIntent(intent); autofill_manager_->FillCreditCardForm( /*query_id=*/kNoQueryId, form, field, fill_data.credit_card(), fill_data.cvc());
diff --git a/components/autofill/core/browser/autofill_manager.h b/components/autofill/core/browser/autofill_manager.h index 64bb3ce..43d4d67d 100644 --- a/components/autofill/core/browser/autofill_manager.h +++ b/components/autofill/core/browser/autofill_manager.h
@@ -168,6 +168,16 @@ const FormData& form, const FormFieldData& field) = 0; + // Profile Autofill was triggered by assistant's |intent|. This only affects + // metrics logging. + virtual void SetProfileFillViaAutofillAssistantIntent( + const autofill_assistant::AutofillAssistantIntent intent) = 0; + + // Credit Card Autofill was triggered by assistant's |intent|. This only + // affects metrics logging. + virtual void SetCreditCardFillViaAutofillAssistantIntent( + const autofill_assistant::AutofillAssistantIntent intent) = 0; + // Invoked when changes of the forms have been detected: the forms in // |updated_forms| are either new or have changed, and the forms in // |removed_forms| have been removed from the DOM (but may be re-added to the @@ -281,12 +291,6 @@ OnServerRequestError(form_signature, request_type, http_error); } - // Autofill was triggered by assistant's |intent|. - virtual void SetFillViaAutofillAssistantIntent( - const FormData& form, - const FormFieldData& field, - const autofill_assistant::AutofillAssistantIntent intent) = 0; - #ifdef UNIT_TEST // A public wrapper that calls |mutable_form_structures| for testing purposes // only.
diff --git a/components/autofill/core/browser/autofill_manager_unittest.cc b/components/autofill/core/browser/autofill_manager_unittest.cc index 2a2b5a4..671cc38 100644 --- a/components/autofill/core/browser/autofill_manager_unittest.cc +++ b/components/autofill/core/browser/autofill_manager_unittest.cc
@@ -76,10 +76,12 @@ const FormFieldData& field), (override)); MOCK_METHOD(void, - SetFillViaAutofillAssistantIntent, - (const FormData& form, - const FormFieldData& field, - const autofill_assistant::AutofillAssistantIntent intent), + SetProfileFillViaAutofillAssistantIntent, + (const autofill_assistant::AutofillAssistantIntent intent), + (override)); + MOCK_METHOD(void, + SetCreditCardFillViaAutofillAssistantIntent, + (const autofill_assistant::AutofillAssistantIntent intent), (override)); MOCK_METHOD(void, OnFocusNoLongerOnForm,
diff --git a/components/autofill/core/browser/browser_autofill_manager.cc b/components/autofill/core/browser/browser_autofill_manager.cc index b5cf986..af75561 100644 --- a/components/autofill/core/browser/browser_autofill_manager.cc +++ b/components/autofill/core/browser/browser_autofill_manager.cc
@@ -1188,18 +1188,14 @@ /*query_id=*/kNoQueryId, form, field, profile); } -void BrowserAutofillManager::SetFillViaAutofillAssistantIntent( - const FormData& form, - const FormFieldData& field, +void BrowserAutofillManager::SetProfileFillViaAutofillAssistantIntent( const autofill_assistant::AutofillAssistantIntent intent) { - FormStructure* form_structure = nullptr; - AutofillField* autofill_field = nullptr; - if (!GetCachedFormAndField(form, field, &form_structure, &autofill_field)) - return; + address_form_event_logger_->SetAutofillAssistantIntentForFilling(intent); +} - auto* logger = GetEventFormLogger(autofill_field->Type().group()); - if (logger) - logger->SetAutofillAssistantIntentForFilling(intent); +void BrowserAutofillManager::SetCreditCardFillViaAutofillAssistantIntent( + const autofill_assistant::AutofillAssistantIntent intent) { + credit_card_form_event_logger_->SetAutofillAssistantIntentForFilling(intent); } void BrowserAutofillManager::FillOrPreviewVirtualCardInformation(
diff --git a/components/autofill/core/browser/browser_autofill_manager.h b/components/autofill/core/browser/browser_autofill_manager.h index c6ea4a1..2291194 100644 --- a/components/autofill/core/browser/browser_autofill_manager.h +++ b/components/autofill/core/browser/browser_autofill_manager.h
@@ -281,9 +281,10 @@ // to be uploadable. Exposed for testing. bool ShouldUploadForm(const FormStructure& form); - void SetFillViaAutofillAssistantIntent( - const FormData& form, - const FormFieldData& field, + void SetProfileFillViaAutofillAssistantIntent( + const autofill_assistant::AutofillAssistantIntent intent) override; + + void SetCreditCardFillViaAutofillAssistantIntent( const autofill_assistant::AutofillAssistantIntent intent) override; // Returns the last form the autofill manager considered in this frame.
diff --git a/components/autofill/core/common/autofill_features.cc b/components/autofill/core/common/autofill_features.cc index 3227296..239f842 100644 --- a/components/autofill/core/common/autofill_features.cc +++ b/components/autofill/core/common/autofill_features.cc
@@ -347,7 +347,7 @@ // shown. This is to prevent double clicks accidentally accepting suggestions. // TODO(crbug/1279268): Remove once launched. const base::Feature kAutofillIgnoreEarlyClicksOnPopup{ - "AutofillIgnoreEarlyClicksOnPopup", base::FEATURE_ENABLED_BY_DEFAULT}; + "AutofillIgnoreEarlyClicksOnPopup", base::FEATURE_DISABLED_BY_DEFAULT}; // The duration for which clicks on the just-shown Autofill popup should be // ignored if AutofillIgnoreEarlyClicksOnPopup is enabled. @@ -356,7 +356,7 @@ const base::FeatureParam<base::TimeDelta> kAutofillIgnoreEarlyClicksOnPopupDuration{ &kAutofillIgnoreEarlyClicksOnPopup, "duration", - base::Milliseconds(250)}; + base::Milliseconds(500)}; // When enabled, HTML autocomplete values that do not map to any known type, but // look reasonable (e.g. contain "address") are simply ignored. Without the
diff --git a/components/commerce/core/BUILD.gn b/components/commerce/core/BUILD.gn index 065afa3..13fd9aa 100644 --- a/components/commerce/core/BUILD.gn +++ b/components/commerce/core/BUILD.gn
@@ -145,6 +145,7 @@ ":feature_list", ":metrics", ":proto", + ":subscriptions", "//base", "//components/bookmarks/browser", "//components/keyed_service/core", @@ -198,3 +199,17 @@ "//url:url", ] } + +source_set("subscriptions") { + sources = [ + "subscriptions/commerce_subscription.cc", + "subscriptions/commerce_subscription.h", + "subscriptions/subscriptions_manager.cc", + "subscriptions/subscriptions_manager.h", + ] + + deps = [ + ":feature_list", + "//base", + ] +}
diff --git a/components/commerce/core/shopping_service.cc b/components/commerce/core/shopping_service.cc index 8379727..4e6b29bf 100644 --- a/components/commerce/core/shopping_service.cc +++ b/components/commerce/core/shopping_service.cc
@@ -16,6 +16,8 @@ #include "components/commerce/core/proto/merchant_trust.pb.h" #include "components/commerce/core/proto/price_tracking.pb.h" #include "components/commerce/core/shopping_bookmark_model_observer.h" +#include "components/commerce/core/subscriptions/commerce_subscription.h" +#include "components/commerce/core/subscriptions/subscriptions_manager.h" #include "components/commerce/core/web_wrapper.h" #include "components/optimization_guide/core/new_optimization_guide_decider.h" #include "components/optimization_guide/core/optimization_guide_util.h" @@ -60,6 +62,8 @@ shopping_bookmark_observer_ = std::make_unique<ShoppingBookmarkModelObserver>(bookmark_model); } + + subscriptions_manager_ = std::make_unique<SubscriptionsManager>(); } void ShoppingService::RegisterPrefs(PrefRegistrySimple* registry) { @@ -341,6 +345,20 @@ std::move(callback).Run(url, std::move(info)); } +void ShoppingService::Subscribe( + std::unique_ptr<std::vector<CommerceSubscription>> subscriptions, + base::OnceCallback<void(bool)> callback) { + subscriptions_manager_->Subscribe(std::move(subscriptions), + std::move(callback)); +} + +void ShoppingService::Unsubscribe( + std::unique_ptr<std::vector<CommerceSubscription>> subscriptions, + base::OnceCallback<void(bool)> callback) { + subscriptions_manager_->Unsubscribe(std::move(subscriptions), + std::move(callback)); +} + void ShoppingService::Shutdown() {} ShoppingService::~ShoppingService() = default;
diff --git a/components/commerce/core/shopping_service.h b/components/commerce/core/shopping_service.h index 6e911bbc..8289687 100644 --- a/components/commerce/core/shopping_service.h +++ b/components/commerce/core/shopping_service.h
@@ -35,7 +35,9 @@ namespace commerce { class ShoppingBookmarkModelObserver; +class SubscriptionsManager; class WebWrapper; +struct CommerceSubscription; // Information returned by the product info APIs. struct ProductInfo { @@ -94,6 +96,18 @@ void GetMerchantInfoForUrl(const GURL& url, MerchantInfoCallback callback); + // Create new subscriptions in batch if needed, and will notify |callback| if + // the operation completes successfully. + void Subscribe( + std::unique_ptr<std::vector<CommerceSubscription>> subscriptions, + base::OnceCallback<void(bool)> callback); + + // Delete existing subscriptions in batch if needed, and will notify + // |callback| if the operation completes successfully. + void Unsubscribe( + std::unique_ptr<std::vector<CommerceSubscription>> subscriptions, + base::OnceCallback<void(bool)> callback); + void Shutdown() override; private: @@ -190,6 +204,8 @@ std::tuple<uint32_t, bool, std::unique_ptr<ProductInfo>>> product_info_cache_; + std::unique_ptr<SubscriptionsManager> subscriptions_manager_; + base::WeakPtrFactory<ShoppingService> weak_ptr_factory_; };
diff --git a/components/commerce/core/subscriptions/commerce_subscription.cc b/components/commerce/core/subscriptions/commerce_subscription.cc new file mode 100644 index 0000000..42a2570 --- /dev/null +++ b/components/commerce/core/subscriptions/commerce_subscription.cc
@@ -0,0 +1,44 @@ +// 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 <string> + +#include "components/commerce/core/subscriptions/commerce_subscription.h" + +namespace commerce { + +UserSeenOffer::UserSeenOffer(uint64_t offer_id, + long user_seen_price, + const std::string& country_code) + : offer_id(offer_id), + user_seen_price(user_seen_price), + country_code(country_code) {} +UserSeenOffer::~UserSeenOffer() = default; + +CommerceSubscription::CommerceSubscription(SubscriptionType type, + IdentifierType id_type, + uint64_t id, + ManagementType management_type) + : type(type), + id_type(id_type), + id(id), + management_type(management_type), + user_seen_offer(absl::nullopt), + timestamp(0) {} + +CommerceSubscription::CommerceSubscription( + SubscriptionType type, + IdentifierType id_type, + uint64_t id, + ManagementType management_type, + absl::optional<UserSeenOffer> user_seen_offer) + : type(type), + id_type(id_type), + id(id), + management_type(management_type), + user_seen_offer(std::move(user_seen_offer)), + timestamp(0) {} +CommerceSubscription::~CommerceSubscription() = default; + +} // namespace commerce
diff --git a/components/commerce/core/subscriptions/commerce_subscription.h b/components/commerce/core/subscriptions/commerce_subscription.h new file mode 100644 index 0000000..9aeb718 --- /dev/null +++ b/components/commerce/core/subscriptions/commerce_subscription.h
@@ -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. + +#ifndef COMPONENTS_COMMERCE_CORE_SUBSCRIPTIONS_COMMERCE_SUBSCRIPTION_H_ +#define COMPONENTS_COMMERCE_CORE_SUBSCRIPTIONS_COMMERCE_SUBSCRIPTION_H_ + +#include <string> + +#include "third_party/abseil-cpp/absl/types/optional.h" + +namespace commerce { + +// The type of subscription. +enum class SubscriptionType { + // Unspecified type. + kTypeUnspecified = 0, + // Subscription for price tracking. + kPriceTrack = 1, +}; + +// The type of subscription identifier. +enum class IdentifierType { + // Unspecified identifier type. + kIdentifierTypeUnspecified = 0, + // Offer id identifier, used in chrome-managed price tracking. + kOfferId = 1, + // Product cluster id identifier, used in user-managed price tracking. + kProductClusterId = 2, +}; + +// The type of subscription management. +enum class ManagementType { + // Unspecified management type. + kTypeUnspecified = 0, + // Automatic chrome-managed subscription. + kChromeManaged = 1, + // Explicit user-managed subscription. + kUserManaged = 2, +}; + +struct UserSeenOffer { + UserSeenOffer(uint64_t offer_id, + long user_seen_price, + const std::string& country_code); + ~UserSeenOffer(); + + const uint64_t offer_id; + const long user_seen_price; + const std::string country_code; +}; + +struct CommerceSubscription { + CommerceSubscription(SubscriptionType type, + IdentifierType id_type, + uint64_t id, + ManagementType management_type); + CommerceSubscription(SubscriptionType type, + IdentifierType id_type, + uint64_t id, + ManagementType management_type, + absl::optional<UserSeenOffer> user_seen_offer); + ~CommerceSubscription(); + + const SubscriptionType type; + const IdentifierType id_type; + const uint64_t id; + const ManagementType management_type; + const absl::optional<UserSeenOffer> user_seen_offer; + const uint64_t timestamp; +}; + +} // namespace commerce + +#endif // COMPONENTS_COMMERCE_CORE_SUBSCRIPTIONS_COMMERCE_SUBSCRIPTION_H_
diff --git a/components/commerce/core/subscriptions/subscriptions_manager.cc b/components/commerce/core/subscriptions/subscriptions_manager.cc new file mode 100644 index 0000000..c0453d9 --- /dev/null +++ b/components/commerce/core/subscriptions/subscriptions_manager.cc
@@ -0,0 +1,137 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/commerce/core/subscriptions/subscriptions_manager.h" +#include "components/commerce/core/commerce_feature_list.h" +#include "components/commerce/core/subscriptions/commerce_subscription.h" + +#include <queue> +#include <string> + +namespace commerce { + +SubscriptionsManager::SubscriptionsManager() : weak_ptr_factory_(this) { + InitSubscriptions(); +} +SubscriptionsManager::~SubscriptionsManager() = default; + +SubscriptionsManager::Request::Request(SubscriptionType type, + AsyncOperation operation, + base::OnceCallback<void(bool)> callback) + : type(type), operation(operation), callback(std::move(callback)) { + CHECK(operation == AsyncOperation::kInit); +} +SubscriptionsManager::Request::Request( + SubscriptionType type, + AsyncOperation operation, + std::unique_ptr<std::vector<CommerceSubscription>> subscriptions, + base::OnceCallback<void(bool)> callback) + : type(type), + operation(operation), + subscriptions(std::move(subscriptions)), + callback(std::move(callback)) { + CHECK(operation == AsyncOperation::kSubscribe || + operation == AsyncOperation::kUnsubscribe); +} +SubscriptionsManager::Request::~Request() = default; + +void SubscriptionsManager::Subscribe( + std::unique_ptr<std::vector<CommerceSubscription>> subscriptions, + base::OnceCallback<void(bool)> callback) { + SubscriptionType type = (*subscriptions)[0].type; + pending_requests_.push(std::make_unique<Request>( + type, AsyncOperation::kSubscribe, std::move(subscriptions), + base::BindOnce( + [](base::WeakPtr<SubscriptionsManager> manager, + base::OnceCallback<void(bool)> callback, bool result) { + std::move(callback).Run(result); + manager->OnRequestCompletion(); + }, + weak_ptr_factory_.GetWeakPtr(), std::move(callback)))); + CheckAndProcessRequest(); +} + +void SubscriptionsManager::Unsubscribe( + std::unique_ptr<std::vector<CommerceSubscription>> subscriptions, + base::OnceCallback<void(bool)> callback) { + SubscriptionType type = (*subscriptions)[0].type; + pending_requests_.push(std::make_unique<Request>( + type, AsyncOperation::kUnsubscribe, std::move(subscriptions), + base::BindOnce( + [](base::WeakPtr<SubscriptionsManager> manager, + base::OnceCallback<void(bool)> callback, bool result) { + std::move(callback).Run(result); + manager->OnRequestCompletion(); + }, + weak_ptr_factory_.GetWeakPtr(), std::move(callback)))); + CheckAndProcessRequest(); +} + +void SubscriptionsManager::InitSubscriptions() { + init_succeeded_ = false; + if (base::FeatureList::IsEnabled(commerce::kShoppingList)) { + pending_requests_.push(std::make_unique<Request>( + SubscriptionType::kPriceTrack, AsyncOperation::kInit, + base::BindOnce( + [](base::WeakPtr<SubscriptionsManager> manager, bool result) { + manager->init_succeeded_ = result; + manager->OnRequestCompletion(); + }, + weak_ptr_factory_.GetWeakPtr()))); + } + CheckAndProcessRequest(); +} + +void SubscriptionsManager::CheckAndProcessRequest() { + if (has_request_running_ || pending_requests_.empty()) + return; + + // If there is no request running, we can start processing next request in the + // queue. + has_request_running_ = true; + std::unique_ptr<Request> request = std::move(pending_requests_.front()); + pending_requests_.pop(); + CHECK(request->type != SubscriptionType::kTypeUnspecified); + + switch (request->operation) { + case AsyncOperation::kInit: + ProcessInitRequest(std::move(request)); + break; + case AsyncOperation::kSubscribe: + ProcessSubscribeRequest(std::move(request)); + break; + case AsyncOperation::kUnsubscribe: + ProcessUnsubscribeRequest(std::move(request)); + break; + } +} + +void SubscriptionsManager::OnRequestCompletion() { + has_request_running_ = false; + CheckAndProcessRequest(); +} + +void SubscriptionsManager::ProcessInitRequest(std::unique_ptr<Request> request){ + // TODO: Get all subscriptions from server and sync with local db. +}; + +void SubscriptionsManager::ProcessSubscribeRequest( + std::unique_ptr<Request> request) { + if (!init_succeeded_) { + std::move(request->callback).Run(false); + return; + } + // TODO: Check local db and send request to server. +} + +void SubscriptionsManager::ProcessUnsubscribeRequest( + std::unique_ptr<Request> request) { + if (!init_succeeded_) { + std::move(request->callback).Run(false); + return; + } + // TODO: Check local db and send request to server. +} + +} // namespace commerce
diff --git a/components/commerce/core/subscriptions/subscriptions_manager.h b/components/commerce/core/subscriptions/subscriptions_manager.h new file mode 100644 index 0000000..be43290 --- /dev/null +++ b/components/commerce/core/subscriptions/subscriptions_manager.h
@@ -0,0 +1,92 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_COMMERCE_CORE_SUBSCRIPTIONS_SUBSCRIPTIONS_MANAGER_H_ +#define COMPONENTS_COMMERCE_CORE_SUBSCRIPTIONS_SUBSCRIPTIONS_MANAGER_H_ + +#include <queue> +#include <string> +#include <unordered_map> + +#include "base/callback.h" +#include "base/check.h" + +namespace commerce { + +enum class SubscriptionType; +struct CommerceSubscription; + +class SubscriptionsManager { + public: + SubscriptionsManager(); + SubscriptionsManager(const SubscriptionsManager&) = delete; + SubscriptionsManager& operator=(const SubscriptionsManager&) = delete; + ~SubscriptionsManager(); + + void Subscribe( + std::unique_ptr<std::vector<CommerceSubscription>> subscriptions, + base::OnceCallback<void(bool)> callback); + + void Unsubscribe( + std::unique_ptr<std::vector<CommerceSubscription>> subscriptions, + base::OnceCallback<void(bool)> callback); + + private: + enum class AsyncOperation { + kInit = 0, + kSubscribe = 1, + kUnsubscribe = 2, + }; + + struct Request { + Request(SubscriptionType type, + AsyncOperation operation, + base::OnceCallback<void(bool)> callback); + Request(SubscriptionType type, + AsyncOperation operation, + std::unique_ptr<std::vector<CommerceSubscription>> subscriptions, + base::OnceCallback<void(bool)> callback); + ~Request(); + + SubscriptionType type; + AsyncOperation operation; + std::unique_ptr<std::vector<CommerceSubscription>> subscriptions; + base::OnceCallback<void(bool)> callback; + }; + + // Fetch all backend subscriptions and sync with local storage. This should + // only be called on manager instantiation and user primary account changed. + void InitSubscriptions(); + + // Check if there is any request running. If not, process the next request in + // the queue. + void CheckAndProcessRequest(); + + // On request completion, mark that no request is running and then check next + // request. This is chained to the main callback when Request object is built. + void OnRequestCompletion(); + + void ProcessSubscribeRequest(std::unique_ptr<Request> request); + + void ProcessUnsubscribeRequest(std::unique_ptr<Request> request); + + void ProcessInitRequest(std::unique_ptr<Request> request); + + // Hold coming requests until previous ones have finished to avoid race + // conditions. + std::queue<std::unique_ptr<Request>> pending_requests_; + + // Whether the initialization is successful. If not, all (un)subscribe + // operations will fail immediately. + bool init_succeeded_ = false; + + // Whether there is any request running. + bool has_request_running_ = false; + + base::WeakPtrFactory<SubscriptionsManager> weak_ptr_factory_; +}; + +} // namespace commerce + +#endif // COMPONENTS_COMMERCE_CORE_SUBSCRIPTIONS_SUBSCRIPTIONS_MANAGER_H_
diff --git a/components/exo/wayland/weston_test.cc b/components/exo/wayland/weston_test.cc index d97a1b72..6cbc250 100644 --- a/components/exo/wayland/weston_test.cc +++ b/components/exo/wayland/weston_test.cc
@@ -23,6 +23,7 @@ #include "ui/events/keycodes/dom/dom_code.h" #include "ui/events/keycodes/dom/keycode_converter.h" #include "ui/events/keycodes/keyboard_code_conversion.h" +#include "ui/wm/core/coordinate_conversion.h" #include "ui/wm/core/window_util.h" namespace exo { @@ -99,15 +100,14 @@ auto* weston_test = GetUserDataAs<WestonTestState>(resource); // Convert cursor point from window space to root space - gfx::Point point_in_root(x, y); + gfx::Point point_in_screen(x, y); if (surface_resource) { aura::Window* window = GetUserDataAs<Surface>(surface_resource)->window(); - aura::Window::ConvertPointToTarget(window, window->GetRootWindow(), - &point_in_root); + wm::ConvertPointToScreen(window, &point_in_screen); } base::RunLoop run_loop; - ui_controls::SendMouseMoveNotifyWhenDone(point_in_root.x(), point_in_root.y(), - run_loop.QuitClosure()); + ui_controls::SendMouseMoveNotifyWhenDone( + point_in_screen.x(), point_in_screen.y(), run_loop.QuitClosure()); { // Do not process incoming wayland events which may destroy resources. ScopedEventDispatchDisabler disable(weston_test->server);
diff --git a/components/flags_ui/flags_state.cc b/components/flags_ui/flags_state.cc index 7124851..da41851 100644 --- a/components/flags_ui/flags_state.cc +++ b/components/flags_ui/flags_state.cc
@@ -16,6 +16,7 @@ #include "base/logging.h" #include "base/metrics/field_trial.h" #include "base/stl_util.h" +#include "base/strings/strcat.h" #include "base/strings/string_piece.h" #include "base/strings/string_tokenizer.h" #include "base/strings/string_util.h" @@ -751,8 +752,7 @@ false, command_line); } if (!variation_ids.empty()) { - command_line->AppendSwitchASCII(variations::switches::kForceVariationIds, - base::JoinString(variation_ids, ",")); + MergeVariationIdsCommandLineSwitch(variation_ids, command_line); } if (sentinels == kAddSentinels) { @@ -788,6 +788,25 @@ command_line->AppendSwitchASCII(switch_name, switch_value); } +void FlagsState::MergeVariationIdsCommandLineSwitch( + const std::vector<std::string>& variation_ids, + base::CommandLine* command_line) { + DCHECK(!variation_ids.empty()); + std::string variation_ids_switch = command_line->GetSwitchValueASCII( + variations::switches::kForceVariationIds); + + // At this point, the switch value is guaranteed to change since + // |variation_ids| is not empty. Hence, we do not conditionally update the + // switch value, as is done in FlagsState::MergeFeatureCommandLineSwitch(). + // Note that it is an error to try to set the same variation id in multiple + // ways. + command_line->AppendSwitchASCII( + variations::switches::kForceVariationIds, + base::StrCat({variation_ids_switch, + variation_ids_switch.empty() ? "" : ",", + base::JoinString(variation_ids, ",")})); +} + std::set<std::string> FlagsState::SanitizeList( const FlagsStorage* storage, const std::set<std::string>& enabled_entries,
diff --git a/components/flags_ui/flags_state.h b/components/flags_ui/flags_state.h index 06fef2a2..e70c09b62 100644 --- a/components/flags_ui/flags_state.h +++ b/components/flags_ui/flags_state.h
@@ -225,6 +225,12 @@ bool feature_state, base::CommandLine* command_line); + // Updates |command_line| by merging the value of the --force-variation-ids + // list with corresponding entries in |variation_ids|. + void MergeVariationIdsCommandLineSwitch( + const std::vector<std::string>& variation_ids, + base::CommandLine* command_line); + // Sanitizes |enabled_entries| to only contain entries that are defined in the // |feature_entries_| and whose |supported_platforms| matches |platform_mask|. // Pass -1 to |platform_mask| to not do platform filtering.
diff --git a/components/lens/lens_features.cc b/components/lens/lens_features.cc index 2c39849..3a391d4e 100644 --- a/components/lens/lens_features.cc +++ b/components/lens/lens_features.cc
@@ -13,21 +13,18 @@ const base::Feature kLensStandalone{"LensStandalone", base::FEATURE_ENABLED_BY_DEFAULT}; -const base::Feature kLensFullscreenSearch{"LensFullscreenSearch", - base::FEATURE_DISABLED_BY_DEFAULT}; +const base::Feature kLensImageCompression{"LensImageCompression", + base::FEATURE_ENABLED_BY_DEFAULT}; -const base::FeatureParam<bool> kUseGoogleAsVisualSearchProvider{ - &kLensStandalone, "use-google-as-visual-search-provider", false}; +const base::Feature kLensSearchOptimizations{"LensSearchOptimizations", + base::FEATURE_DISABLED_BY_DEFAULT}; + +const base::Feature kLensTransparentImagesFix{ + "LensTransparentImagesFix", base::FEATURE_DISABLED_BY_DEFAULT}; const base::FeatureParam<bool> kRegionSearchMacCursorFix{ &kLensStandalone, "region-search-mac-cursor-fix", true}; -const base::FeatureParam<bool> kRegionSearchUseMenuItemAltText1{ - &kLensStandalone, "use-menu-item-alt-text-1", false}; - -const base::FeatureParam<bool> kRegionSearchUseMenuItemAltText2{ - &kLensStandalone, "use-menu-item-alt-text-2", false}; - const base::FeatureParam<bool> kEnableUKMLoggingForRegionSearch{ &kLensStandalone, "region-search-enable-ukm-logging", true}; @@ -37,20 +34,32 @@ const base::FeatureParam<bool> kEnableSidePanelForLens{ &kLensStandalone, "enable-side-panel", true}; -constexpr base::FeatureParam<int> kMaxPixelsForRegionSearch{ - &kLensStandalone, "region-search-dimensions-max-pixels", 1000}; - -constexpr base::FeatureParam<int> kMaxAreaForRegionSearch{ - &kLensStandalone, "region-search-dimensions-max-area", 1000000}; - -constexpr base::FeatureParam<int> kMaxPixelsForImageSearch{ - &kLensStandalone, "dimensions-max-pixels", 1000}; - constexpr base::FeatureParam<std::string> kHomepageURLForLens{ &kLensStandalone, "lens-homepage-url", "https://lens.google.com/"}; -const base::FeatureParam<bool> kSendImagesAsPng{&kLensStandalone, - "send-png-images", false}; +constexpr base::FeatureParam<int> kMaxPixelsForRegionSearch{ + &kLensImageCompression, "region-search-dimensions-max-pixels", 1000}; + +constexpr base::FeatureParam<int> kMaxAreaForRegionSearch{ + &kLensImageCompression, "region-search-dimensions-max-area", 1000000}; + +constexpr base::FeatureParam<int> kMaxPixelsForImageSearch{ + &kLensImageCompression, "dimensions-max-pixels", 1000}; + +const base::FeatureParam<bool> kUseGoogleAsVisualSearchProvider{ + &kLensSearchOptimizations, "use-google-as-visual-search-provider", false}; + +const base::FeatureParam<bool> kRegionSearchUseMenuItemAltText1{ + &kLensSearchOptimizations, "use-menu-item-alt-text-1", false}; + +const base::FeatureParam<bool> kRegionSearchUseMenuItemAltText2{ + &kLensSearchOptimizations, "use-menu-item-alt-text-2", false}; + +// Default is set to true but it is only enabled if kLensSearchOptimizations is +// enabled. This setup allows us to have fullscreen search as a toggleable +// experience in chrome://flags +const base::FeatureParam<bool> kEnableLensFullscreenSearch{ + &kLensSearchOptimizations, "enable-lens-fullscreen-search", true}; bool GetEnableUKMLoggingForRegionSearch() { return kEnableUKMLoggingForRegionSearch.Get(); @@ -78,21 +87,26 @@ bool UseRegionSearchMenuItemAltText1() { return base::FeatureList::IsEnabled(kLensStandalone) && + base::FeatureList::IsEnabled(kLensSearchOptimizations) && kRegionSearchUseMenuItemAltText1.Get(); } bool UseRegionSearchMenuItemAltText2() { return base::FeatureList::IsEnabled(kLensStandalone) && + base::FeatureList::IsEnabled(kLensSearchOptimizations) && kRegionSearchUseMenuItemAltText2.Get(); } bool UseGoogleAsVisualSearchProvider() { return base::FeatureList::IsEnabled(kLensStandalone) && + base::FeatureList::IsEnabled(kLensSearchOptimizations) && kUseGoogleAsVisualSearchProvider.Get(); } bool IsLensFullscreenSearchEnabled() { - return base::FeatureList::IsEnabled(kLensFullscreenSearch); + return base::FeatureList::IsEnabled(kLensStandalone) && + base::FeatureList::IsEnabled(kLensSearchOptimizations) && + kEnableLensFullscreenSearch.Get(); } bool IsLensSidePanelEnabled() { @@ -102,7 +116,7 @@ bool GetSendImagesAsPng() { return base::FeatureList::IsEnabled(kLensStandalone) && - kSendImagesAsPng.Get(); + base::FeatureList::IsEnabled(kLensTransparentImagesFix); } } // namespace features
diff --git a/components/lens/lens_features.h b/components/lens/lens_features.h index 3ac3dd8f..f228e96 100644 --- a/components/lens/lens_features.h +++ b/components/lens/lens_features.h
@@ -16,8 +16,15 @@ // Enables context menu search by image sending to the Lens homepage. extern const base::Feature kLensStandalone; -// Enables Lens fullscreen search on Desktop platforms. -extern const base::Feature kLensFullscreenSearch; +// Feature that controls the compression of images before they are sent to Lens. +extern const base::Feature kLensImageCompression; + +// Enables a variety of changes aimed to improve user's engagement with current +// Lens features. +extern const base::Feature kLensSearchOptimizations; + +// Enables a fix to properly handle transparent images in Lens Image Search +extern const base::Feature kLensTransparentImagesFix; // Enables a fix for cursor pointer/crosshair state over overlay on Mac. // TODO(crbug/1266514): make default and remove feature once launched. @@ -42,8 +49,8 @@ // Enables the side panel for Lens features on Chrome where supported. extern const base::FeatureParam<bool> kEnableSidePanelForLens; -// Sends images as PNG to Lens Standalone to fix issues with transparency. -extern const base::FeatureParam<bool> kSendImagesAsPng; +// Enables Lens fullscreen search on Desktop platforms. +extern const base::FeatureParam<bool> kEnableFullscreenSearch; // Returns whether to enable UKM logging for Lens Region Search feature. extern bool GetEnableUKMLoggingForRegionSearch();
diff --git a/components/omnibox/browser/history_cluster_provider.cc b/components/omnibox/browser/history_cluster_provider.cc index 204be41c..c6ac7de 100644 --- a/components/omnibox/browser/history_cluster_provider.cc +++ b/components/omnibox/browser/history_cluster_provider.cc
@@ -22,9 +22,9 @@ SearchProvider* search_provider) : AutocompleteProvider(AutocompleteProvider::TYPE_HISTORY_CLUSTER_PROVIDER), client_(client), - listener_(listener), search_provider_(search_provider) { DCHECK(search_provider_); + AddListener(listener); search_provider_->AddListener(this); } @@ -59,7 +59,7 @@ void HistoryClusterProvider::OnProviderUpdate(bool updated_matches) { if (done_ || !search_provider_->done()) return; - listener_->OnProviderUpdate(CreateMatches()); + NotifyListeners(CreateMatches()); } bool HistoryClusterProvider::CreateMatches() {
diff --git a/components/omnibox/browser/history_cluster_provider.h b/components/omnibox/browser/history_cluster_provider.h index b886c0d..f08c4fd 100644 --- a/components/omnibox/browser/history_cluster_provider.h +++ b/components/omnibox/browser/history_cluster_provider.h
@@ -49,7 +49,6 @@ // These are never null. const raw_ptr<AutocompleteProviderClient> client_; - const raw_ptr<AutocompleteProviderListener> listener_; const raw_ptr<SearchProvider> search_provider_; };
diff --git a/components/omnibox/browser/search_suggestion_parser.cc b/components/omnibox/browser/search_suggestion_parser.cc index c65805c..541339f2 100644 --- a/components/omnibox/browser/search_suggestion_parser.cc +++ b/components/omnibox/browser/search_suggestion_parser.cc
@@ -676,7 +676,8 @@ absl::optional<int> suggestion_group_id; if (suggestion_details && - suggestion_details->GetListDeprecated()[index].is_dict()) { + suggestion_details->GetListDeprecated()[index].is_dict() && + !suggestion_details->GetListDeprecated()[index].DictEmpty()) { const base::Value& suggestion_detail = suggestion_details->GetListDeprecated()[index]; match_contents =
diff --git a/components/policy/resources/policy_templates.json b/components/policy/resources/policy_templates.json index e097d0f..8097d01 100644 --- a/components/policy/resources/policy_templates.json +++ b/components/policy/resources/policy_templates.json
@@ -30926,7 +30926,7 @@ 'owners': ['file://third_party/blink/renderer/core/frame/OWNERS', 'shaseley@chromium.org', 'bokan@chromium.org', 'dcheng@chromium.org', 'japhet@chromium.org'], 'type': 'main', 'schema': { 'type': 'boolean' }, - 'supported_on': ['chrome_os:101-104', 'chrome.*:101-104', 'android:101-104'], + 'supported_on': ['chrome_os:101-107', 'chrome.*:101-107', 'android:101-107'], 'features': { 'per_profile': True, 'dynamic_refresh': True,
diff --git a/components/policy/tools/generate_policy_source.py b/components/policy/tools/generate_policy_source.py index 390360c7..974e573 100755 --- a/components/policy/tools/generate_policy_source.py +++ b/components/policy/tools/generate_policy_source.py
@@ -90,7 +90,8 @@ raise RuntimeError('Platform "%s" is not supported' % platform) return PLATFORM_STRINGS[platform] - def __init__(self, policy, chrome_major_version, target_platform, valid_tags): + def __init__(self, policy, chrome_major_version, deprecation_milestone_buffer, + target_platform, valid_tags): self.id = policy['id'] self.name = policy['name'] self.tags = policy.get('tags', None) @@ -133,8 +134,9 @@ # Skip if filtering by Chromium version and the current Chromium version # does not support the policy. if chrome_major_version: - if (int(version_min) > chrome_major_version or - version_max != '' and int(version_max) < chrome_major_version): + if (int(version_min) > chrome_major_version + or version_max != '' and int(version_max) < + chrome_major_version - deprecation_milestone_buffer): continue self.platforms.update(self._ConvertPlatform(platform)) @@ -331,6 +333,13 @@ dest='policy_templates_file', help='path to the policy_templates.json input file', metavar='FILE') + parser.add_argument( + '--deprecation-milestone-buffer', + dest='deprecation_milestone_buffer', + type=int, + help='Number of major versions before a code for a policy stops being ' + 'generated', + default=2) args = parser.parse_args() has_arg_error = False @@ -358,6 +367,7 @@ version_path = args.chrome_version_file target_platform = args.target_platform template_file_name = args.policy_templates_file + deprecation_milestone_buffer = int(args.deprecation_milestone_buffer) # --target-platform accepts "chromeos" as its input because that's what is # used within GN. Within policy templates, "chrome_os" is used instead. @@ -372,8 +382,8 @@ template_file_contents = _LoadJSONFile(template_file_name) risk_tags = RiskTags(template_file_contents) policy_details = [ - PolicyDetails(policy, chrome_major_version, target_platform, - risk_tags.GetValidTags()) + PolicyDetails(policy, chrome_major_version, deprecation_milestone_buffer, + target_platform, risk_tags.GetValidTags()) for policy in template_file_contents['policy_definitions'] if policy['type'] != 'group' ] @@ -490,19 +500,26 @@ ] +# Returns the policies supported by at least one platform. +def _GetSupportedPolicies(policies): + return [ + policy for policy in policies + if len(policy.platforms) + len(policy.future_on) > 0 + ] + #------------------ policy constants header ------------------------# # Return a list of all policies of type |metapolicy_type|. def _GetMetapoliciesOfType(policies, metapolicy_type): return [ - policy.name for policy in policies - if policy.metapolicy_type == metapolicy_type + policy for policy in policies if policy.metapolicy_type == metapolicy_type ] -def _WritePolicyConstantHeader(policies, policy_atomic_groups, target_platform, - f, risk_tags): +def _WritePolicyConstantHeader(all_policies, policy_atomic_groups, + target_platform, f, risk_tags): + policies = _GetSupportedPolicies(all_policies) f.write('''#ifndef COMPONENTS_POLICY_POLICY_CONSTANTS_H_ #define COMPONENTS_POLICY_POLICY_CONSTANTS_H_ @@ -1090,8 +1107,10 @@ return [], None -def _WritePolicyConstantSource(policies, policy_atomic_groups, target_platform, - f, risk_tags): +def _WritePolicyConstantSource(all_policies, policy_atomic_groups, + target_platform, f, risk_tags): + policies = _GetSupportedPolicies(all_policies) + policy_names = [policy.name for policy in policies] f.write('''#include "components/policy/policy_constants.h" #include <algorithm> @@ -1296,7 +1315,8 @@ for group in policy_atomic_groups: f.write('const char* const %s[] = {' % (group.name)) for policy in group.policies: - f.write('key::k%s, ' % (policy)) + if policy in policy_names: + f.write('key::k%s, ' % (policy)) f.write('nullptr};\n') f.write('\n} // namespace\n') f.write('\n} // namespace group\n\n') @@ -1319,7 +1339,7 @@ METAPOLICY_TYPE['merge']) f.write('const char* const kMerge[%s] = {\n' % len(merge_metapolicies)) for metapolicy in merge_metapolicies: - f.write(' key::k%s,\n' % metapolicy) + f.write(' key::k%s,\n' % metapolicy.name) f.write('};\n\n') # Populate precedence metapolicy array. @@ -1328,7 +1348,7 @@ f.write('const char* const kPrecedence[%s] = {\n' % len(precedence_metapolicies)) for metapolicy in precedence_metapolicies: - f.write(' key::k%s,\n' % metapolicy) + f.write(' key::k%s,\n' % metapolicy.name) f.write('};\n\n') f.write('} // namespace metapolicy\n\n')
diff --git a/components/policy/tools/generate_policy_source_test.py b/components/policy/tools/generate_policy_source_test.py index 2628272..79da976 100755 --- a/components/policy/tools/generate_policy_source_test.py +++ b/components/policy/tools/generate_policy_source_test.py
@@ -56,7 +56,8 @@ "schema": { "type": "boolean" }, - "supported_on": ["chrome_os:1-"], + "supported_on": + ["chrome_os:1-", "chrome.*:1-", "android:1-", "ios:1-"], "features": { "metapolicy_type": "merge", }, @@ -70,7 +71,8 @@ "schema": { "type": "boolean" }, - "supported_on": ["chrome_os:1-"], + "supported_on": + ["chrome_os:1-", "chrome.*:1-", "android:1-", "ios:1-"], "features": { "metapolicy_type": "precedence", }, @@ -104,6 +106,39 @@ "caption": "CloudManagementEnrollmentToken caption", "desc": "CloudManagementEnrollmentToken desc" }, { + "name": "DeprecatedButGenerated", + "type": "string", + "schema": { + "type": "string" + }, + "supported_on": ["chrome_os:1-93"], + "id": 7, + "tags": [], + "caption": "DeprecatedButGenerated caption", + "desc": "DeprecatedButGenerated desc" + }, { + "name": "DeprecatedNotGenerated", + "type": "string", + "schema": { + "type": "string" + }, + "supported_on": ["chrome_os:1-92"], + "id": 8, + "tags": [], + "caption": "DeprecatedNotGenerated caption", + "desc": "DeprecatedNotGenerated desc" + }, { + "name": "UnsupportedPolicy", + "type": "string", + "schema": { + "type": "string" + }, + "supported_on": [], + "id": 9, + "tags": [], + "caption": "UnsupportedPolicy caption", + "desc": "UnsupportedPolicy desc" + }, { "name": "ChunkZeroLastFieldBooleanPolicy", "type": "main", "schema": { @@ -163,13 +198,14 @@ } def setUp(self): - self.maxDiff = 10000 self.chrome_major_version = 94 self.target_platform = 'chrome_os' + self.deprecation_milestone_buffer = 1 self.all_target_platforms = ['win', 'mac', 'linux', 'chromeos', 'fuchsia'] self.risk_tags = generate_policy_source.RiskTags(self.TEMPLATES_JSON) self.policies = [ generate_policy_source.PolicyDetails(policy, self.chrome_major_version, + self.deprecation_milestone_buffer, self.target_platform, self.risk_tags.GetValidTags()) for policy in self.TEMPLATES_JSON['policy_definitions'] @@ -314,18 +350,17 @@ def testGetMetapoliciesOfType(self): merge_metapolicies = generate_policy_source._GetMetapoliciesOfType( self.policies, "merge") - self.assertListEqual(["ExampleBoolMergeMetapolicy"], merge_metapolicies) self.assertEqual(1, len(merge_metapolicies)) + self.assertEqual("ExampleBoolMergeMetapolicy", merge_metapolicies[0].name) precedence_metapolicies = generate_policy_source._GetMetapoliciesOfType( self.policies, "precedence") - self.assertListEqual(["ExampleBoolPrecedenceMetapolicy"], - precedence_metapolicies) self.assertEqual(1, len(precedence_metapolicies)) + self.assertEqual("ExampleBoolPrecedenceMetapolicy", + precedence_metapolicies[0].name) invalid_metapolicies = generate_policy_source._GetMetapoliciesOfType( self.policies, "invalid") - self.assertListEqual([], invalid_metapolicies) self.assertEqual(0, len(invalid_metapolicies)) def testWritePolicyConstantHeader(self):
diff --git a/components/policy/tools/generate_policy_source_test_data.py b/components/policy/tools/generate_policy_source_test_data.py index 2bc4db62c..f358abd 100644 --- a/components/policy/tools/generate_policy_source_test_data.py +++ b/components/policy/tools/generate_policy_source_test_data.py
@@ -35,6 +35,7 @@ optional BooleanPolicyProto ExampleBoolPrecedenceMetapolicy = 6; optional BooleanPolicyProto CloudOnlyPolicy = 7; optional StringPolicyProto CloudManagementEnrollmentToken = 8; + optional StringPolicyProto DeprecatedButGenerated = 9; optional BooleanPolicyProto ChunkZeroLastFieldBooleanPolicy = 1017; optional CloudPolicySubProto1 subProto1 = 1018; optional CloudPolicySubProto2 subProto2 = 1019; @@ -79,7 +80,7 @@ // // ExampleBoolMergeMetapolicy desc // -// Supported on: chrome_os +// Supported on: android, chrome_os, fuchsia, ios, linux, mac, win message ExampleBoolMergeMetapolicyProto { optional PolicyOptions policy_options = 1; optional bool ExampleBoolMergeMetapolicy = 2; @@ -89,7 +90,7 @@ // // ExampleBoolPrecedenceMetapolicy desc // -// Supported on: chrome_os +// Supported on: android, chrome_os, fuchsia, ios, linux, mac, win message ExampleBoolPrecedenceMetapolicyProto { optional PolicyOptions policy_options = 1; optional bool ExampleBoolPrecedenceMetapolicy = 2; @@ -115,6 +116,36 @@ optional string CloudManagementEnrollmentToken = 2; } +// DeprecatedButGenerated caption +// +// DeprecatedButGenerated desc +// +// Supported on: chrome_os +message DeprecatedButGeneratedProto { + optional PolicyOptions policy_options = 1; + optional string DeprecatedButGenerated = 2; +} + +// DeprecatedNotGenerated caption +// +// DeprecatedNotGenerated desc +// +// Supported on: +message DeprecatedNotGeneratedProto { + optional PolicyOptions policy_options = 1; + optional string DeprecatedNotGenerated = 2; +} + +// UnsupportedPolicy caption +// +// UnsupportedPolicy desc +// +// Supported on: +message UnsupportedPolicyProto { + optional PolicyOptions policy_options = 1; + optional string UnsupportedPolicy = 2; +} + // ChunkZeroLastFieldBooleanPolicy caption // // ChunkZeroLastFieldBooleanPolicy desc. @@ -188,6 +219,9 @@ optional ExampleBoolPrecedenceMetapolicyProto ExampleBoolPrecedenceMetapolicy = 6; optional CloudOnlyPolicyProto CloudOnlyPolicy = 7; optional CloudManagementEnrollmentTokenProto CloudManagementEnrollmentToken = 8; + optional DeprecatedButGeneratedProto DeprecatedButGenerated = 9; + optional DeprecatedNotGeneratedProto DeprecatedNotGenerated = 10; + optional UnsupportedPolicyProto UnsupportedPolicy = 11; optional ChunkZeroLastFieldBooleanPolicyProto ChunkZeroLastFieldBooleanPolicy = 1017; optional ChromeSettingsSubProto1 subProto1 = 1018; optional ChromeSettingsSubProto2 subProto2 = 1019; @@ -258,6 +292,7 @@ extern const char kExampleBoolPrecedenceMetapolicy[]; extern const char kCloudOnlyPolicy[]; extern const char kCloudManagementEnrollmentToken[]; +extern const char kDeprecatedButGenerated[]; extern const char kChunkZeroLastFieldBooleanPolicy[]; extern const char kChunkOneFirstFieldBooleanPolicy[]; extern const char kChunkOneLastFieldBooleanPolicy[]; @@ -325,7 +360,7 @@ const em::CloudPolicySettings& policy); const StringPolicyType type; }; -extern const std::array<StringPolicyAccess, 4> kStringPolicyAccess; +extern const std::array<StringPolicyAccess, 5> kStringPolicyAccess; // Read access to the protobufs of all supported stringlist user policies. struct StringListPolicyAccess { @@ -380,6 +415,8 @@ { false, false, false, 5, 0, { } }, // CloudManagementEnrollmentToken { false, false, false, 6, 0, { } }, + // DeprecatedButGenerated + { false, false, false, 7, 0, { } }, // ChunkZeroLastFieldBooleanPolicy { false, false, false, 1015, 0, { } }, // ChunkOneFirstFieldBooleanPolicy @@ -408,6 +445,7 @@ { key::kChunkZeroLastFieldBooleanPolicy, 1 }, { key::kCloudManagementEnrollmentToken, 2 }, { key::kCloudOnlyPolicy, 1 }, + { key::kDeprecatedButGenerated, 2 }, { key::kExampleBoolMergeMetapolicy, 1 }, { key::kExampleBoolPolicy, 1 }, { key::kExampleBoolPrecedenceMetapolicy, 1 }, @@ -416,7 +454,7 @@ const internal::PropertiesNode kProperties[] = { // Begin End PatternEnd RequiredBegin RequiredEnd Additional Properties - { 0, 11, 11, 0, 0, -1 }, // root node + { 0, 12, 12, 0, 0, -1 }, // root node }; const internal::SchemaData* GetChromeSchemaData() { @@ -462,7 +500,7 @@ // First index in kPropertyNodes of the Chrome policies. static const int begin_index = 0; // One-past-the-end of the Chrome policies in kPropertyNodes. - static const int end_index = 11; + static const int end_index = 12; const internal::PropertyNode* begin = kPropertyNodes + begin_index; const internal::PropertyNode* end = kPropertyNodes + end_index; @@ -493,6 +531,7 @@ const char kExampleBoolPrecedenceMetapolicy[] = "ExampleBoolPrecedenceMetapolicy"; const char kCloudOnlyPolicy[] = "CloudOnlyPolicy"; const char kCloudManagementEnrollmentToken[] = "CloudManagementEnrollmentToken"; +const char kDeprecatedButGenerated[] = "DeprecatedButGenerated"; const char kChunkZeroLastFieldBooleanPolicy[] = "ChunkZeroLastFieldBooleanPolicy"; const char kChunkOneFirstFieldBooleanPolicy[] = "ChunkOneFirstFieldBooleanPolicy"; const char kChunkOneLastFieldBooleanPolicy[] = "ChunkOneLastFieldBooleanPolicy"; @@ -606,7 +645,7 @@ const std::array<IntegerPolicyAccess, 0> kIntegerPolicyAccess {{ }}; -const std::array<StringPolicyAccess, 4> kStringPolicyAccess {{ +const std::array<StringPolicyAccess, 5> kStringPolicyAccess {{ {key::kExampleStringPolicy, false, [](const em::CloudPolicySettings& policy) { @@ -629,6 +668,17 @@ }, StringPolicyType::STRING }, + {key::kDeprecatedButGenerated, + false, + [](const em::CloudPolicySettings& policy) { + return policy.has_deprecatedbutgenerated(); + }, + [](const em::CloudPolicySettings& policy) + -> const em::StringPolicyProto& { + return policy.deprecatedbutgenerated(); + }, + StringPolicyType::STRING + }, {key::kChunkTwoFirstFieldStringPolicy, false, [](const em::CloudPolicySettings& policy) { @@ -695,6 +745,7 @@ extern const char kExampleBoolPrecedenceMetapolicy[]; extern const char kCloudOnlyPolicy[]; extern const char kCloudManagementEnrollmentToken[]; +extern const char kDeprecatedButGenerated[]; extern const char kChunkZeroLastFieldBooleanPolicy[]; extern const char kChunkOneFirstFieldBooleanPolicy[]; extern const char kChunkOneLastFieldBooleanPolicy[]; @@ -734,7 +785,7 @@ enterprise_management::StringPolicyProto* (*mutable_proto_ptr)( enterprise_management::CloudPolicySettings* policy); }; -extern const std::array<StringPolicyAccess, 4> kStringPolicyAccess; +extern const std::array<StringPolicyAccess, 5> kStringPolicyAccess; // Access to the mutable protobuf function of all supported stringlist user // policies. @@ -767,6 +818,7 @@ const char kExampleBoolPrecedenceMetapolicy[] = "ExampleBoolPrecedenceMetapolicy"; const char kCloudOnlyPolicy[] = "CloudOnlyPolicy"; const char kCloudManagementEnrollmentToken[] = "CloudManagementEnrollmentToken"; +const char kDeprecatedButGenerated[] = "DeprecatedButGenerated"; const char kChunkZeroLastFieldBooleanPolicy[] = "ChunkZeroLastFieldBooleanPolicy"; const char kChunkOneFirstFieldBooleanPolicy[] = "ChunkOneFirstFieldBooleanPolicy"; const char kChunkOneLastFieldBooleanPolicy[] = "ChunkOneLastFieldBooleanPolicy"; @@ -834,7 +886,7 @@ const std::array<IntegerPolicyAccess, 0> kIntegerPolicyAccess {{ }}; -const std::array<StringPolicyAccess, 4> kStringPolicyAccess {{ +const std::array<StringPolicyAccess, 5> kStringPolicyAccess {{ {key::kExampleStringPolicy, false, [](em::CloudPolicySettings* policy) @@ -849,6 +901,13 @@ return policy->mutable_cloudmanagementenrollmenttoken(); } }, + {key::kDeprecatedButGenerated, + false, + [](em::CloudPolicySettings* policy) + -> em::StringPolicyProto* { + return policy->mutable_deprecatedbutgenerated(); + } + }, {key::kChunkTwoFirstFieldStringPolicy, false, [](em::CloudPolicySettings* policy) @@ -911,6 +970,12 @@ android:restrictionType="bool"/> <restriction + android:key="DeprecatedButGenerated" + android:title="@string/DeprecatedButGeneratedTitle" + android:description="@string/DeprecatedButGeneratedDesc" + android:restrictionType="string"/> + + <restriction android:key="ExampleBoolMergeMetapolicy" android:title="@string/ExampleBoolMergeMetapolicyTitle" android:description="@string/ExampleBoolMergeMetapolicyDesc"
diff --git a/components/policy/tools/template_writers/writers/ios_app_config_writer.py b/components/policy/tools/template_writers/writers/ios_app_config_writer.py index a5a61f9..74d09067 100755 --- a/components/policy/tools/template_writers/writers/ios_app_config_writer.py +++ b/components/policy/tools/template_writers/writers/ios_app_config_writer.py
@@ -138,7 +138,9 @@ constraint = self.AddElement(parent, 'constraint', attrs) if 'enum' in policy['type']: values_element = self.AddElement(constraint, 'values', {}) - for v in policy['schema']['enum']: + enum = policy['schema']['enum'] if 'enum' in policy['schema'] else policy[ + 'schema']['items']['enum'] + for v in enum: value = self.AddElement(values_element, 'value', {}) self.AddText(value, _ParseSchemaTypeValueToString(v, policy['schema']['type']))
diff --git a/components/policy/tools/template_writers/writers/ios_app_config_writer_unittest.py b/components/policy/tools/template_writers/writers/ios_app_config_writer_unittest.py index 8774f2e..89cd7f4 100755 --- a/components/policy/tools/template_writers/writers/ios_app_config_writer_unittest.py +++ b/components/policy/tools/template_writers/writers/ios_app_config_writer_unittest.py
@@ -229,8 +229,11 @@ 'desc': 'string-enum-list description', 'schema': { - 'type': 'string', - 'enum': ['0', '1'], + 'type': 'array', + 'items': { + 'type': 'string', + 'enum': ['0', '1'], + }, }, 'items': [{ 'name': 'item0',
diff --git a/components/reputation/core/BUILD.gn b/components/reputation/core/BUILD.gn index 53ff4b0..16d13aa 100644 --- a/components/reputation/core/BUILD.gn +++ b/components/reputation/core/BUILD.gn
@@ -29,6 +29,7 @@ sources = [ "safety_tips_config_unittest.cc" ] deps = [ ":core", + ":proto", "//testing/gtest", "//url", ]
diff --git a/components/reputation/core/safety_tip_test_utils.cc b/components/reputation/core/safety_tip_test_utils.cc index bf2983d..5cbb2130 100644 --- a/components/reputation/core/safety_tip_test_utils.cc +++ b/components/reputation/core/safety_tip_test_utils.cc
@@ -12,10 +12,7 @@ namespace reputation { -namespace { - -// Retrieve existing config proto if set, or create a new one otherwise. -std::unique_ptr<SafetyTipsConfig> GetConfig() { +std::unique_ptr<SafetyTipsConfig> GetOrCreateSafetyTipsConfig() { auto* old = GetSafetyTipsRemoteConfigProto(); if (old) { return std::make_unique<SafetyTipsConfig>(*old); @@ -27,15 +24,13 @@ return conf; } -} // namespace - void InitializeSafetyTipConfig() { - SetSafetyTipsRemoteConfigProto(GetConfig()); + SetSafetyTipsRemoteConfigProto(GetOrCreateSafetyTipsConfig()); } void SetSafetyTipPatternsWithFlagType(std::vector<std::string> patterns, FlaggedPage::FlagType type) { - auto config_proto = GetConfig(); + auto config_proto = GetOrCreateSafetyTipsConfig(); config_proto->clear_flagged_page(); std::sort(patterns.begin(), patterns.end()); @@ -55,7 +50,7 @@ void SetSafetyTipAllowlistPatterns(std::vector<std::string> patterns, std::vector<std::string> target_patterns, std::vector<std::string> common_words) { - auto config_proto = GetConfig(); + auto config_proto = GetOrCreateSafetyTipsConfig(); config_proto->clear_allowed_pattern(); config_proto->clear_allowed_target_pattern(); config_proto->clear_common_word(); @@ -85,7 +80,7 @@ void AddSafetyTipHeuristicLaunchConfigForTesting( reputation::HeuristicLaunchConfig::Heuristic heuristic, int launch_percentage) { - auto config_proto = GetConfig(); + auto config_proto = GetOrCreateSafetyTipsConfig(); reputation::HeuristicLaunchConfig* launch_config = config_proto->add_launch_config(); launch_config->set_heuristic(heuristic);
diff --git a/components/reputation/core/safety_tip_test_utils.h b/components/reputation/core/safety_tip_test_utils.h index 30e1e20..465c1f0 100644 --- a/components/reputation/core/safety_tip_test_utils.h +++ b/components/reputation/core/safety_tip_test_utils.h
@@ -12,6 +12,10 @@ namespace reputation { +// Retrieve any existing Safety Tips config proto if set, or create a new one +// otherwise. +std::unique_ptr<SafetyTipsConfig> GetOrCreateSafetyTipsConfig(); + // Initialize component configuration. Necessary to enable Safety Tips for // testing, as no heuristics trigger if the allowlist is inaccessible. void InitializeSafetyTipConfig();
diff --git a/components/reputation/core/safety_tips.proto b/components/reputation/core/safety_tips.proto index 0c771a4..ddacfa8 100644 --- a/components/reputation/core/safety_tips.proto +++ b/components/reputation/core/safety_tips.proto
@@ -25,6 +25,11 @@ // example.test/test-path-for-safety-tips/test.html. Also see the comment for // |allowed_pattern| field. optional string pattern = 1; + + // The index of any cohort[s] that this entry is allowed to spoof. If this + // field is unset and the pattern is allowlisted, the pattern may spoof any + // domain. Has no meaning when used in canonical_pattern. + repeated uint32 cohort_index = 2; } message HostPattern { @@ -63,6 +68,17 @@ optional uint32 launch_percentage = 2; } +// A set of domains allowed to be spoofed by a given allowlist entry. +// allowed_pattern or canonical_pattern indices may appear in multiple +// Cohorts, and multiple allowed_patterns may point to the same Cohort. +message Cohort { + // Indexes in `allowed_pattern` in this cohort. + repeated uint32 allowed_index = 1; + + // Indexes in `canonical_pattern` in this cohort. + repeated uint32 canonical_index = 2; +} + // Configuration for the safety tips component. A binary version of this proto // will be distributed to Chrome clients via component updater. The binary will // contain a single instance of this message. @@ -103,4 +119,12 @@ // Launch configurations for new heuristics. Each new heuristic being launched // gets its own config. Multiple heuristics can be enabled at the same time. repeated HeuristicLaunchConfig launch_config = 6; + + // canonical_pattern is a list of hostnames that are only ever spoofed, and do + // no spoofing of their own. Entries are pointed to by one or more Cohort. + repeated UrlPattern canonical_pattern = 7; + + // A Cohort is a set of domains that may be spoofed by the allowed_pattern + // that points to it. + repeated Cohort cohort = 8; }
diff --git a/components/reputation/core/safety_tips_config.cc b/components/reputation/core/safety_tips_config.cc index 15149e4..9a5f72a 100644 --- a/components/reputation/core/safety_tips_config.cc +++ b/components/reputation/core/safety_tips_config.cc
@@ -80,6 +80,53 @@ return security_state::SafetyTipStatus::kNone; } +// Return whether |canonical_url| is a member of the designated cohort. +bool IsUrlAllowedByCohort(const SafetyTipsConfig* proto, + const GURL& canonical_url, + unsigned cohort_index) { + DCHECK(proto); + DCHECK(canonical_url.is_valid()); + + // Ensure that the cohort index is valid before using it. If it isn't valid, + // we just pretend the cohort didn't include the canonical URL. + if (cohort_index >= static_cast<unsigned>(proto->cohort_size())) { + return false; + } + + const auto& cohort = proto->cohort(cohort_index); + + // For each possible URL pattern, see if any of the indicated allowed_index or + // canonical_index entries correspond to a matching pattern since both sets of + // indices are considered valid spoof targets. + std::vector<std::string> patterns; + UrlToSafetyTipPatterns(canonical_url, &patterns); + for (const auto& search_pattern : patterns) { + for (const unsigned allowed_index : cohort.allowed_index()) { + // Skip over invalid indices. + if (allowed_index >= + static_cast<unsigned>(proto->allowed_pattern_size())) { + continue; + } + const auto& pattern = proto->allowed_pattern(allowed_index).pattern(); + if (pattern == search_pattern) { + return true; + } + } + for (const unsigned canonical_index : cohort.canonical_index()) { + // Skip over invalid indices. + if (canonical_index >= + static_cast<unsigned>(proto->canonical_pattern_size())) { + continue; + } + const auto& pattern = proto->canonical_pattern(canonical_index).pattern(); + if (pattern == search_pattern) { + return true; + } + } + } + return false; +} + } // namespace // static @@ -93,24 +140,35 @@ } bool IsUrlAllowlistedBySafetyTipsComponent(const SafetyTipsConfig* proto, - const GURL& url) { + const GURL& visited_url, + const GURL& canonical_url) { DCHECK(proto); - DCHECK(url.is_valid()); + DCHECK(visited_url.is_valid()); std::vector<std::string> patterns; - UrlToSafetyTipPatterns(url, &patterns); - auto allowed_pages = proto->allowed_pattern(); + UrlToSafetyTipPatterns(visited_url, &patterns); + auto allowed_patterns = proto->allowed_pattern(); for (const auto& pattern : patterns) { UrlPattern search_target; search_target.set_pattern(pattern); - auto lower = std::lower_bound( - allowed_pages.begin(), allowed_pages.end(), search_target, + auto maybe_before = std::lower_bound( + allowed_patterns.begin(), allowed_patterns.end(), search_target, [](const UrlPattern& a, const UrlPattern& b) -> bool { return a.pattern() < b.pattern(); }); - if (lower != allowed_pages.end() && pattern == lower->pattern()) { - return true; + if (maybe_before != allowed_patterns.end() && + pattern == maybe_before->pattern()) { + // If no cohorts are given, it's a universal allowlist entry. + if (maybe_before->cohort_index_size() == 0) { + return true; + } + + for (const unsigned cohort_index : maybe_before->cohort_index()) { + if (IsUrlAllowedByCohort(proto, canonical_url, cohort_index)) { + return true; + } + } } } return false;
diff --git a/components/reputation/core/safety_tips_config.h b/components/reputation/core/safety_tips_config.h index 67cd7932..3a80516 100644 --- a/components/reputation/core/safety_tips_config.h +++ b/components/reputation/core/safety_tips_config.h
@@ -25,10 +25,14 @@ // a safety tip. const SafetyTipsConfig* GetSafetyTipsRemoteConfigProto(); -// Checks SafeBrowsing-style permutations of |url| against the component updater -// allowlist and returns whether the URL is explicitly allowed. +// Checks permutations of |visited_url| against the component updater allowlist +// and returns whether the URL is explicitly allowed to spoof |canonical_url|. +// +// Cases when canonical_url is unknown (as in kFailedSpoofChecks) are treated as +// if they're trying to spoof themselves, so set canonical_url = visited_url. bool IsUrlAllowlistedBySafetyTipsComponent(const SafetyTipsConfig* proto, - const GURL& url); + const GURL& visited_url, + const GURL& canonical_url); // Checks |hostname| against the component updater target allowlist and returns // whether it is explicitly allowed.
diff --git a/components/reputation/core/safety_tips_config_unittest.cc b/components/reputation/core/safety_tips_config_unittest.cc index 85ca115..56079ba 100644 --- a/components/reputation/core/safety_tips_config_unittest.cc +++ b/components/reputation/core/safety_tips_config_unittest.cc
@@ -11,13 +11,137 @@ namespace reputation { -TEST(SafetyTipsConfigTest, TestUrlAllowlist) { +// Build an allowlist with testable scoped allowlist entries. +void ConfigureAllowlistWithScopes() { + auto config_proto = GetOrCreateSafetyTipsConfig(); + config_proto->clear_allowed_pattern(); + config_proto->clear_canonical_pattern(); + config_proto->clear_cohort(); + + // Note that allowed_pattern *must* stay sorted. + + // error-canonical-index.tld has a cohort with an invalid allowed index. + auto* pattern_bad_allowed = config_proto->add_allowed_pattern(); + pattern_bad_allowed->set_pattern("error-allowed-index.tld/"); + auto* cohort_bad_allowed = config_proto->add_cohort(); + cohort_bad_allowed->add_allowed_index(100); + pattern_bad_allowed->add_cohort_index(0); // cohort_bad_allowed + + // error-canonical-index.tld has a cohort with an invalid canonical index. + auto* pattern_bad_canonical = config_proto->add_allowed_pattern(); + pattern_bad_canonical->set_pattern("error-canonical-index.tld/"); + auto* cohort_bad_canonical = config_proto->add_cohort(); + cohort_bad_canonical->add_canonical_index(100); + pattern_bad_canonical->add_cohort_index(1); // cohort_bad_canonical + + // error-cohort-index.tld has an invalid index. + auto* pattern_bad_cohort = config_proto->add_allowed_pattern(); + pattern_bad_cohort->set_pattern("error-cohort-index.tld/"); + pattern_bad_cohort->add_cohort_index(100); + + // siteA.tld is only a canonical_pattern, so can't spoof anyone. + config_proto->add_canonical_pattern()->set_pattern("sitea.tld/"); + + // siteB.tld is only allowed to spoof siteA.tld and itself. + auto* pattern_b = config_proto->add_allowed_pattern(); + pattern_b->set_pattern("siteb.tld/"); + auto* cohort_b = config_proto->add_cohort(); + cohort_b->add_allowed_index(3); // siteB + cohort_b->add_canonical_index(0); // siteA + pattern_b->add_cohort_index(2); // cohort_b + + // siteC.tld is allowed to spoof siteB.tld and itself. + auto* pattern_c = config_proto->add_allowed_pattern(); + pattern_c->set_pattern("sitec.tld/"); + auto* cohort_c = config_proto->add_cohort(); + cohort_c->add_allowed_index(3); // siteB + cohort_c->add_allowed_index(4); // siteC + pattern_c->add_cohort_index(3); // cohort_c + + // siteD.tld is allowed to spoof anyone, so has no cohort. + auto* pattern_d = config_proto->add_allowed_pattern(); + pattern_d->set_pattern("sited.tld/"); + + // Implicitly, siteE.tld can't spoof anyone, since it isn't in the proto. + + SetSafetyTipsRemoteConfigProto(std::move(config_proto)); +} + +// Minimal test for an unscoped allowlist entry. +TEST(SafetyTipsConfigTest, TestBasicUrlAllowlist) { SetSafetyTipAllowlistPatterns({"example.com/"}, {}, {}); auto* config = GetSafetyTipsRemoteConfigProto(); + + // Basic unscoped entries are allowed to spoof any ("canonical") domain. EXPECT_TRUE(IsUrlAllowlistedBySafetyTipsComponent( - config, GURL("http://example.com"))); + config, GURL("http://example.com"), GURL("http://example.com"))); + EXPECT_TRUE(IsUrlAllowlistedBySafetyTipsComponent( + config, GURL("http://example.com"), GURL("http://example.org"))); + EXPECT_FALSE(IsUrlAllowlistedBySafetyTipsComponent( - config, GURL("http://example.org"))); + config, GURL("http://example.org"), GURL("http://example.org"))); +} + +// Tests for a scoped allowlist (i.e. entries not permitted to spoof anything). +TEST(SafetyTipsConfigTest, TestScopedUrlAllowlist) { + ConfigureAllowlistWithScopes(); + auto* config = GetSafetyTipsRemoteConfigProto(); + + // Site A is only a canonical domain, so can't spoof anything. + EXPECT_FALSE(IsUrlAllowlistedBySafetyTipsComponent( + config, GURL("http://sitea.tld"), GURL("http://sitea.tld"))); + EXPECT_FALSE(IsUrlAllowlistedBySafetyTipsComponent( + config, GURL("http://sitea.tld"), GURL("http://siteb.tld"))); + EXPECT_FALSE(IsUrlAllowlistedBySafetyTipsComponent( + config, GURL("http://sitea.tld"), GURL("http://sitee.tld"))); + + // Site B can spoof sites A & B, but not other stuff. + EXPECT_TRUE(IsUrlAllowlistedBySafetyTipsComponent( + config, GURL("http://siteb.tld"), GURL("http://sitea.tld"))); + EXPECT_TRUE(IsUrlAllowlistedBySafetyTipsComponent( + config, GURL("http://siteb.tld"), GURL("http://siteb.tld"))); + EXPECT_FALSE(IsUrlAllowlistedBySafetyTipsComponent( + config, GURL("http://siteb.tld"), GURL("http://sitec.tld"))); + EXPECT_FALSE(IsUrlAllowlistedBySafetyTipsComponent( + config, GURL("http://siteb.tld"), GURL("http://sited.tld"))); + EXPECT_FALSE(IsUrlAllowlistedBySafetyTipsComponent( + config, GURL("http://siteb.tld"), GURL("http://sitee.tld"))); + + // Site C can spoof sites B and C, but not anything else. + EXPECT_FALSE(IsUrlAllowlistedBySafetyTipsComponent( + config, GURL("http://sitec.tld"), GURL("http://sitea.tld"))); + EXPECT_TRUE(IsUrlAllowlistedBySafetyTipsComponent( + config, GURL("http://sitec.tld"), GURL("http://siteb.tld"))); + EXPECT_TRUE(IsUrlAllowlistedBySafetyTipsComponent( + config, GURL("http://sitec.tld"), GURL("http://sitec.tld"))); + EXPECT_FALSE(IsUrlAllowlistedBySafetyTipsComponent( + config, GURL("http://sitec.tld"), GURL("http://sited.tld"))); + EXPECT_FALSE(IsUrlAllowlistedBySafetyTipsComponent( + config, GURL("http://sitec.tld"), GURL("http://sitee.tld"))); + + // Site D has a wildcard, and can spoof anyone. + EXPECT_TRUE(IsUrlAllowlistedBySafetyTipsComponent( + config, GURL("http://sited.tld"), GURL("http://sitea.tld"))); + EXPECT_TRUE(IsUrlAllowlistedBySafetyTipsComponent( + config, GURL("http://sited.tld"), GURL("http://siteb.tld"))); + EXPECT_TRUE(IsUrlAllowlistedBySafetyTipsComponent( + config, GURL("http://sited.tld"), GURL("http://sitec.tld"))); + EXPECT_TRUE(IsUrlAllowlistedBySafetyTipsComponent( + config, GURL("http://sited.tld"), GURL("http://sited.tld"))); + EXPECT_TRUE(IsUrlAllowlistedBySafetyTipsComponent( + config, GURL("http://sited.tld"), GURL("http://sitee.tld"))); + + // These sites all have invalid indices in their entries. Each should + // invalidate the relevant part of their allowlist entry (i.e. act as if it's + // not there), rather than crashing. + EXPECT_FALSE(IsUrlAllowlistedBySafetyTipsComponent( + config, GURL("http://error-cohort-index.tld"), GURL("http://sitea.tld"))); + EXPECT_FALSE(IsUrlAllowlistedBySafetyTipsComponent( + config, GURL("http://error-canonical-index.tld"), + GURL("http://sitea.tld"))); + EXPECT_FALSE(IsUrlAllowlistedBySafetyTipsComponent( + config, GURL("http://error-allowed-index.tld"), + GURL("http://sitea.tld"))); } TEST(SafetyTipsConfigTest, TestTargetUrlAllowlist) {
diff --git a/components/safe_browsing/content/browser/password_protection/password_protection_service_unittest.cc b/components/safe_browsing/content/browser/password_protection/password_protection_service_unittest.cc index 42dac9e9..296284ad 100644 --- a/components/safe_browsing/content/browser/password_protection/password_protection_service_unittest.cc +++ b/components/safe_browsing/content/browser/password_protection/password_protection_service_unittest.cc
@@ -1300,7 +1300,6 @@ EXPECT_TRUE(actual_request->frames(0).has_password_field()); ASSERT_TRUE(actual_request->has_password_reuse_event()); const auto& reuse_event = actual_request->password_reuse_event(); - EXPECT_TRUE(reuse_event.is_chrome_signin_password()); EXPECT_EQ(0, reuse_event.domains_matching_password_size()); #if !BUILDFLAG(IS_ANDROID) VerifyContentAreaSizeCollection(*actual_request); @@ -1331,7 +1330,6 @@ password_protection_service_->GetLatestRequestProto(); ASSERT_TRUE(actual_request->has_password_reuse_event()); const auto& reuse_event = actual_request->password_reuse_event(); - EXPECT_FALSE(reuse_event.is_chrome_signin_password()); if (password_protection_service_->IsExtendedReporting() && !password_protection_service_->IsIncognito()) {
diff --git a/components/safe_browsing/content/browser/web_ui/safe_browsing_ui.cc b/components/safe_browsing/content/browser/web_ui/safe_browsing_ui.cc index 328abd2..259ca79 100644 --- a/components/safe_browsing/content/browser/web_ui/safe_browsing_ui.cc +++ b/components/safe_browsing/content/browser/web_ui/safe_browsing_ui.cc
@@ -1595,8 +1595,6 @@ event_dict.Set("domains_matching_password", std::move(domains_list)); event_dict.Set("frame_id", event.frame_id()); - event_dict.Set("is_chrome_signin_password", - event.is_chrome_signin_password()); std::string sync_account_type; switch (event.sync_account_type()) {
diff --git a/components/safe_browsing/core/browser/password_protection/password_protection_request.cc b/components/safe_browsing/core/browser/password_protection/password_protection_request.cc index c0fe7698..51503d5 100644 --- a/components/safe_browsing/core/browser/password_protection/password_protection_request.cc +++ b/components/safe_browsing/core/browser/password_protection/password_protection_request.cc
@@ -264,7 +264,6 @@ request_proto_->mutable_password_reuse_event(); bool matches_signin_password = password_type_ == PasswordType::PRIMARY_ACCOUNT_PASSWORD; - reuse_event->set_is_chrome_signin_password(matches_signin_password); reuse_event->set_reused_password_type( password_protection_service_->GetPasswordProtectionReusedPasswordType( password_type_));
diff --git a/components/safe_browsing/core/common/proto/csd.proto b/components/safe_browsing/core/common/proto/csd.proto index 4112daa..1907291 100644 --- a/components/safe_browsing/core/common/proto/csd.proto +++ b/components/safe_browsing/core/common/proto/csd.proto
@@ -321,12 +321,11 @@ // The frame that the password reuse is detected. optional int32 frame_id = 2; - // TODO(crbug/914410): Remove once ReusedPasswordAccountType is implemented. - // Whether the reused password is used for Chrome signin. - optional bool is_chrome_signin_password = 3; + // Deprecated field. + reserved 3; // TODO(crbug/914410): Remove once ReusedPasswordAccountType is implemented. - // Sync account type. Only set if |is_chrome_signin_password| is true. + // Sync account type. enum SyncAccountType { // Not a sign-in user. NOT_SIGNED_IN = 0;
diff --git a/components/sync/base/features.h b/components/sync/base/features.h index 43ceade2..26f0845b 100644 --- a/components/sync/base/features.h +++ b/components/sync/base/features.h
@@ -31,7 +31,7 @@ // The threshold for kIgnoreSyncEncryptionKeysLongMissing to start ignoring keys // (measured in number of GetUpdatesResponses messages). inline constexpr base::FeatureParam<int> kMinGuResponsesToIgnoreKey{ - &kIgnoreSyncEncryptionKeysLongMissing, "MinGuResponsesToIgnoreKey", 50}; + &kIgnoreSyncEncryptionKeysLongMissing, "MinGuResponsesToIgnoreKey", 3}; // When enabled, Sync machinery will read and writes password notes to the // `encrypted_notes_backup` field inside the PasswordSpecifics proto. Together
diff --git a/components/variations/android/java/src/org/chromium/components/variations/firstrun/VariationsSeedFetcher.java b/components/variations/android/java/src/org/chromium/components/variations/firstrun/VariationsSeedFetcher.java index 8347d29..25885d2 100644 --- a/components/variations/android/java/src/org/chromium/components/variations/firstrun/VariationsSeedFetcher.java +++ b/components/variations/android/java/src/org/chromium/components/variations/firstrun/VariationsSeedFetcher.java
@@ -79,8 +79,8 @@ int ANDROID_WEBVIEW = 1; } - private static final String VARIATIONS_SERVER_URL = - "https://clientservices.googleapis.com/chrome-variations/seed?osname="; + private static final String DEFAULT_VARIATIONS_SERVER_URL = + "https://clientservices.googleapis.com/chrome-variations/seed"; private static final int READ_TIMEOUT = 3000; // time in ms private static final int REQUEST_TIMEOUT = 1000; // time in ms @@ -205,7 +205,10 @@ @VisibleForTesting protected String getConnectionString(@VariationsPlatform int platform, String restrictMode, String milestone, String channel) { - String urlString = VARIATIONS_SERVER_URL; + // TODO(crbug/1302862): Consider reusing native VariationsService::GetVariationsServerURL(). + String urlString = CommandLine.getInstance().getSwitchValue( + VariationsSwitches.VARIATIONS_SERVER_URL, DEFAULT_VARIATIONS_SERVER_URL); + urlString += "?osname="; switch (platform) { case VariationsPlatform.ANDROID: urlString += "android";
diff --git a/components/variations/android/junit/src/org/chromium/components/variations/firstrun/VariationsSeedFetcherTest.java b/components/variations/android/junit/src/org/chromium/components/variations/firstrun/VariationsSeedFetcherTest.java index 06d77d99..6153bb40 100644 --- a/components/variations/android/junit/src/org/chromium/components/variations/firstrun/VariationsSeedFetcherTest.java +++ b/components/variations/android/junit/src/org/chromium/components/variations/firstrun/VariationsSeedFetcherTest.java
@@ -537,6 +537,20 @@ } /** + * Test method to make sure {@link VariationsSeedFetcher#getConnectionString()} honors the + * "--variations-server-url" switch. + */ + @Test + @CommandLineFlags.Add(VariationsSwitches.VARIATIONS_SERVER_URL + "=http://localhost:8080/seed") + public void testGetConnectionString_HonorsServerUrlCommandlineSwitch() { + String urlString = mFetcher.getConnectionString( + VariationsSeedFetcher.VariationsPlatform.ANDROID, sRestrict, sMilestone, sChannel); + + // The URL should start with the variations server URL passed as a switch. + assertTrue(urlString, urlString.startsWith("http://localhost:8080/seed")); + } + + /** * Test method to make sure {@link VariationsSeedFetcher#getAvailableInstanceManipulations()} * honors the * "--enable-finch-seed-delta-compression" switch.
diff --git a/components/variations/service/variations_field_trial_creator.cc b/components/variations/service/variations_field_trial_creator.cc index e32fc0f..0442f7f 100644 --- a/components/variations/service/variations_field_trial_creator.cc +++ b/components/variations/service/variations_field_trial_creator.cc
@@ -202,6 +202,7 @@ bool VariationsFieldTrialCreator::SetUpFieldTrials( const std::vector<std::string>& variation_ids, + const std::string& command_line_variation_ids, const std::vector<base::FeatureList::FeatureOverrideInfo>& extra_overrides, std::unique_ptr<const base::FieldTrial::EntropyProvider> low_entropy_provider, @@ -231,13 +232,10 @@ VariationsIdsProvider* http_header_provider = VariationsIdsProvider::GetInstance(); http_header_provider->SetLowEntropySourceValue(low_entropy_source_value); - const base::CommandLine* command_line = - base::CommandLine::ForCurrentProcess(); // Force the variation ids selected in chrome://flags and/or specified using // the command-line flag. auto result = http_header_provider->ForceVariationIds( - variation_ids, - command_line->GetSwitchValueASCII(switches::kForceVariationIds)); + variation_ids, command_line_variation_ids); switch (result) { case VariationsIdsProvider::ForceIdsResult::INVALID_SWITCH_ENTRY: @@ -253,6 +251,8 @@ break; } + const base::CommandLine* command_line = + base::CommandLine::ForCurrentProcess(); bool success = http_header_provider->ForceDisableVariationIds( command_line->GetSwitchValueASCII(switches::kForceDisableVariationIds)); if (!success) {
diff --git a/components/variations/service/variations_field_trial_creator.h b/components/variations/service/variations_field_trial_creator.h index 15eb5dce..acb72ee 100644 --- a/components/variations/service/variations_field_trial_creator.h +++ b/components/variations/service/variations_field_trial_creator.h
@@ -106,8 +106,11 @@ // Sets up field trials based on stored variations seed data. Returns whether // setup completed successfully. // - // |variation_ids| allows for forcing ids selected in chrome://flags and/or - // specified using the command-line flag. + // |variation_ids| allows for forcing ids selected in chrome://flags. + // |command_line_variation_ids| allows for forcing ids through the + // "--force-variation-ids" command line flag. It should be a comma-separated + // list of variation ids. Ids prefixed with the character "t" will be treated + // as Trigger Variation Ids. // |extra_overrides| gives a list of feature overrides that should be applied // after the features explicitly disabled/enabled from the command line via // --disable-features and --enable-features, but before field trials. @@ -130,6 +133,7 @@ // field trials. bool SetUpFieldTrials( const std::vector<std::string>& variation_ids, + const std::string& command_line_variation_ids, const std::vector<base::FeatureList::FeatureOverrideInfo>& extra_overrides, std::unique_ptr<const base::FieldTrial::EntropyProvider>
diff --git a/components/variations/service/variations_field_trial_creator_unittest.cc b/components/variations/service/variations_field_trial_creator_unittest.cc index 2242d0b..7208fe8 100644 --- a/components/variations/service/variations_field_trial_creator_unittest.cc +++ b/components/variations/service/variations_field_trial_creator_unittest.cc
@@ -299,6 +299,8 @@ TestPlatformFieldTrials platform_field_trials; return VariationsFieldTrialCreator::SetUpFieldTrials( /*variation_ids=*/std::vector<std::string>(), + base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( + switches::kForceVariationIds), std::vector<base::FeatureList::FeatureOverrideInfo>(), /*low_entropy_provider=*/nullptr, std::make_unique<base::FeatureList>(), metrics_state_manager_.get(), &platform_field_trials, @@ -848,6 +850,8 @@ // active. EXPECT_TRUE(field_trial_creator.SetUpFieldTrials( /*variation_ids=*/std::vector<std::string>(), + base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( + switches::kForceVariationIds), std::vector<base::FeatureList::FeatureOverrideInfo>(), /*low_entropy_provider=*/nullptr, std::make_unique<base::FeatureList>(), metrics_state_manager.get(), &platform_field_trials, &safe_seed_manager,
diff --git a/components/variations/service/variations_service.cc b/components/variations/service/variations_service.cc index 87fe040..bc93203 100644 --- a/components/variations/service/variations_service.cc +++ b/components/variations/service/variations_service.cc
@@ -952,13 +952,15 @@ bool VariationsService::SetUpFieldTrials( const std::vector<std::string>& variation_ids, + const std::string& command_line_variation_ids, const std::vector<base::FeatureList::FeatureOverrideInfo>& extra_overrides, std::unique_ptr<base::FeatureList> feature_list, variations::PlatformFieldTrials* platform_field_trials) { return field_trial_creator_.SetUpFieldTrials( - variation_ids, extra_overrides, CreateLowEntropyProvider(), - std::move(feature_list), state_manager_, platform_field_trials, - &safe_seed_manager_, state_manager_->GetLowEntropySource()); + variation_ids, command_line_variation_ids, extra_overrides, + CreateLowEntropyProvider(), std::move(feature_list), state_manager_, + platform_field_trials, &safe_seed_manager_, + state_manager_->GetLowEntropySource()); } void VariationsService::OverrideCachedUIStrings() {
diff --git a/components/variations/service/variations_service.h b/components/variations/service/variations_service.h index ef942c9..60a8332 100644 --- a/components/variations/service/variations_service.h +++ b/components/variations/service/variations_service.h
@@ -189,6 +189,7 @@ // Wrapper around VariationsFieldTrialCreator::SetUpFieldTrials(). bool SetUpFieldTrials( const std::vector<std::string>& variation_ids, + const std::string& command_line_variation_ids, const std::vector<base::FeatureList::FeatureOverrideInfo>& extra_overrides, std::unique_ptr<base::FeatureList> feature_list,
diff --git a/components/variations/variations_ids_provider.cc b/components/variations/variations_ids_provider.cc index 3cf51c5d..6c9083758 100644 --- a/components/variations/variations_ids_provider.cc +++ b/components/variations/variations_ids_provider.cc
@@ -7,6 +7,7 @@ #include <algorithm> #include "base/base64.h" +#include "base/containers/contains.h" #include "base/metrics/histogram_macros.h" #include "base/no_destructor.h" #include "base/observer_list.h" @@ -15,6 +16,7 @@ #include "base/strings/string_util.h" #include "base/synchronization/lock.h" #include "components/variations/proto/client_variations.pb.h" +#include "components/variations/variations_associated_data.h" #include "components/variations/variations_client.h" #include "components/variations/variations_features.h" @@ -159,10 +161,12 @@ const std::string& command_line_variation_ids) { force_enabled_ids_set_.clear(); - if (!AddVariationIdsToSet(variation_ids, &force_enabled_ids_set_)) + if (!AddVariationIdsToSet(variation_ids, /*should_dedupe=*/true, + &force_enabled_ids_set_)) return ForceIdsResult::INVALID_VECTOR_ENTRY; if (!ParseVariationIdsParameter(command_line_variation_ids, + /*should_dedupe=*/true, &force_enabled_ids_set_)) { return ForceIdsResult::INVALID_SWITCH_ENTRY; } @@ -178,7 +182,12 @@ bool VariationsIdsProvider::ForceDisableVariationIds( const std::string& command_line_variation_ids) { force_disabled_ids_set_.clear(); + // |should_dedupe| is false here in order to add the IDs specified in + // |command_line_variation_ids| to |force_disabled_ids_set_| even if they were + // defined before. The IDs are not marked as active; they are marked as + // disabled. if (!ParseVariationIdsParameter(command_line_variation_ids, + /*should_dedupe=*/false, &force_disabled_ids_set_)) { return false; } @@ -252,6 +261,9 @@ for (const SyntheticTrialGroup& group : groups) { VariationID id = GetGoogleVariationIDFromHashes( GOOGLE_WEB_PROPERTIES_ANY_CONTEXT, group.id()); + // TODO(crbug/1294948): Handle duplicated IDs in such a way that is visible + // to developers, but non-intrusive to users. See + // crrev/c/3628020/comments/e278cd12_2bb863ef for discussions. if (id != EMPTY_ID) { synthetic_variation_ids_set_.insert( VariationIDEntry(id, GOOGLE_WEB_PROPERTIES_ANY_CONTEXT)); @@ -301,6 +313,9 @@ for (int i = 0; i < ID_COLLECTION_COUNT; ++i) { IDCollectionKey key = static_cast<IDCollectionKey>(i); const VariationID id = GetGoogleVariationID(key, trial_name, group_name); + // TODO(crbug/1294948): Handle duplicated IDs in such a way that is visible + // to developers, but non-intrusive to users. See + // crrev/c/3628020/comments/e278cd12_2bb863ef for discussions. if (id != EMPTY_ID) variation_ids_set_.insert(VariationIDEntry(id, key)); } @@ -414,9 +429,9 @@ return hashed; } -// static bool VariationsIdsProvider::AddVariationIdsToSet( const std::vector<std::string>& variation_ids, + bool should_dedupe, std::set<VariationIDEntry>* target_set) { for (const std::string& entry : variation_ids) { if (entry.empty()) { @@ -433,6 +448,14 @@ target_set->clear(); return false; } + + if (should_dedupe && IsDuplicateId(variation_id)) { + DVLOG(1) << "Invalid variation ID specified: " << entry + << " (it is already in use)"; + target_set->clear(); + return false; + } + target_set->insert(VariationIDEntry( variation_id, trigger_id ? GOOGLE_WEB_PROPERTIES_TRIGGER_ANY_CONTEXT : GOOGLE_WEB_PROPERTIES_ANY_CONTEXT)); @@ -440,9 +463,9 @@ return true; } -// static bool VariationsIdsProvider::ParseVariationIdsParameter( const std::string& command_line_variation_ids, + bool should_dedupe, std::set<VariationIDEntry>* target_set) { if (command_line_variation_ids.empty()) return true; @@ -450,7 +473,8 @@ std::vector<std::string> variation_ids_from_command_line = base::SplitString(command_line_variation_ids, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL); - return AddVariationIdsToSet(variation_ids_from_command_line, target_set); + return AddVariationIdsToSet(variation_ids_from_command_line, should_dedupe, + target_set); } std::string VariationsIdsProvider::GetClientDataHeaderWhileLocked( @@ -530,4 +554,22 @@ return result; } +bool VariationsIdsProvider::IsDuplicateId(VariationID id) { + for (int i = 0; i < ID_COLLECTION_COUNT; ++i) { + IDCollectionKey key = static_cast<IDCollectionKey>(i); + // GOOGLE_APP ids may be duplicated. Further validation is done in + // GroupMapAccessor::ValidateID(). + if (key == GOOGLE_APP) + continue; + + VariationIDEntry entry(id, key); + if (base::Contains(variation_ids_set_, entry) || + base::Contains(force_enabled_ids_set_, entry) || + base::Contains(synthetic_variation_ids_set_, entry)) { + return true; + } + } + return false; +} + } // namespace variations
diff --git a/components/variations/variations_ids_provider.h b/components/variations/variations_ids_provider.h index d812c43..73307202 100644 --- a/components/variations/variations_ids_provider.h +++ b/components/variations/variations_ids_provider.h
@@ -222,16 +222,22 @@ std::string GenerateBase64EncodedProto(bool is_signed_in, bool is_first_party_context); - // Adds variation ids and trigger variation ids to |target_set|. - static bool AddVariationIdsToSet( - const std::vector<std::string>& variation_ids, - std::set<VariationIDEntry>* target_set); + // Adds variation ids and trigger variation ids to |target_set|. If + // |should_dedupe| is true, the ids in |variation_ids| that have already been + // added as non-Google-app ids are not added to |target_set|. Returns false if + // any variation ids are malformed or duplicated. Returns true otherwise. + bool AddVariationIdsToSet(const std::vector<std::string>& variation_ids, + bool should_dedupe, + std::set<VariationIDEntry>* target_set); // Parses a comma-separated string of variation ids and trigger variation ids - // and adds them to |target_set|. - static bool ParseVariationIdsParameter( - const std::string& command_line_variation_ids, - std::set<VariationIDEntry>* target_set); + // and adds them to |target_set|. If |should_dedupe| is true, ids that have + // already been added as non-Google-app ids are not added to |target_set|. + // Returns false if any variation ids are malformed or duplicated. Returns + // true otherwise. + bool ParseVariationIdsParameter(const std::string& command_line_variation_ids, + bool should_dedupe, + std::set<VariationIDEntry>* target_set); // Returns the value of the X-Client-Data header corresponding to // |is_signed_in| and |web_visibility|. Considering |web_visibility| may allow @@ -249,6 +255,12 @@ std::vector<VariationID> GetVariationsVectorImpl( const std::set<IDCollectionKey>& key); + // Returns whether |id| has already been added to the active set of variation + // ids. This includes ids from field trials, synthetic trials, and forced ids. + // Note that Google app ids are treated differently. They may be reused as a + // Google Web id. + bool IsDuplicateId(VariationID id); + const Mode mode_; // Guards access to variables below.
diff --git a/components/variations/variations_ids_provider_unittest.cc b/components/variations/variations_ids_provider_unittest.cc index 38aee5a..b117318 100644 --- a/components/variations/variations_ids_provider_unittest.cc +++ b/components/variations/variations_ids_provider_unittest.cc
@@ -119,6 +119,24 @@ provider.ForceVariationIds({"12", "50"}, "tabc456")); provider.InitVariationIDsCacheIfNeeded(); EXPECT_TRUE(provider.GetClientDataHeaders(/*is_signed_in=*/false).is_null()); + + // Duplicate experiment ids. + EXPECT_EQ(VariationsIdsProvider::ForceIdsResult::INVALID_VECTOR_ENTRY, + provider.ForceVariationIds({"1", "2", "t1"}, "")); + provider.InitVariationIDsCacheIfNeeded(); + EXPECT_TRUE(provider.GetClientDataHeaders(/*is_signed_in=*/false).is_null()); + + // Duplicate command-line ids. + EXPECT_EQ(VariationsIdsProvider::ForceIdsResult::INVALID_SWITCH_ENTRY, + provider.ForceVariationIds({}, "t10,11,10")); + provider.InitVariationIDsCacheIfNeeded(); + EXPECT_TRUE(provider.GetClientDataHeaders(/*is_signed_in=*/false).is_null()); + + // Duplicate experiment and command-line ids. + EXPECT_EQ(VariationsIdsProvider::ForceIdsResult::INVALID_SWITCH_ENTRY, + provider.ForceVariationIds({"20", "t21"}, "21")); + provider.InitVariationIDsCacheIfNeeded(); + EXPECT_TRUE(provider.GetClientDataHeaders(/*is_signed_in=*/false).is_null()); } TEST_F(VariationsIdsProviderTest, ForceDisableVariationIds_ValidCommandLine) {
diff --git a/content/browser/interest_group/auction_runner.h b/content/browser/interest_group/auction_runner.h index 97f6edb..13c2a214 100644 --- a/content/browser/interest_group/auction_runner.h +++ b/content/browser/interest_group/auction_runner.h
@@ -828,9 +828,8 @@ size_t num_second_highest_bids_ = 0; // The numeric value of the bid that got the second highest score. When - // there's a tie for second highest score, just take the most recent one ( - // any bid with the second highest score can be the most recent one since - // the order of bids getting scored is arbitrary). + // there's a tie for the second highest score, one of the second highest + // scoring bids is randomly chosen. double highest_scoring_other_bid_ = 0.0; double second_highest_score_ = 0.0; // Whether all bids of the highest score are from the same interest group
diff --git a/content/browser/loader/resource_scheduler_browsertest.cc b/content/browser/loader/resource_scheduler_browsertest.cc index c1f707f..963808be4 100644 --- a/content/browser/loader/resource_scheduler_browsertest.cc +++ b/content/browser/loader/resource_scheduler_browsertest.cc
@@ -32,7 +32,7 @@ }; IN_PROC_BROWSER_TEST_F(ResourceSchedulerBrowserTest, - ResourceLoadingExperimentIncognito) { + DISABLED_ResourceLoadingExperimentIncognito) { GURL url(embedded_test_server()->GetURL( "/resource_loading/resource_loading_non_mobile.html")); @@ -43,7 +43,7 @@ } IN_PROC_BROWSER_TEST_F(ResourceSchedulerBrowserTest, - ResourceLoadingExperimentNormal) { + DISABLED_ResourceLoadingExperimentNormal) { GURL url(embedded_test_server()->GetURL( "/resource_loading/resource_loading_non_mobile.html")); Shell* browser = shell();
diff --git a/content/browser/message_port_provider.cc b/content/browser/message_port_provider.cc index 1e9ab138..66ec89d 100644 --- a/content/browser/message_port_provider.cc +++ b/content/browser/message_port_provider.cc
@@ -87,12 +87,9 @@ } #endif -// TODO(crbug.com/1329657): The last IS_FUCHSIA check will not be needed once -// its build sets enable_cast_receiver. -#if BUILDFLAG(ENABLE_CAST_RECEIVER) && \ - (BUILDFLAG(IS_FUCHSIA) || BUILDFLAG(IS_CASTOS) || \ - BUILDFLAG(IS_CAST_ANDROID)) || \ - BUILDFLAG(IS_FUCHSIA) +#if BUILDFLAG(IS_FUCHSIA) || \ + BUILDFLAG(ENABLE_CAST_RECEIVER) && \ + (BUILDFLAG(IS_CASTOS) || BUILDFLAG(IS_CAST_ANDROID)) // static void MessagePortProvider::PostMessageToFrame( Page& page,
diff --git a/content/public/browser/BUILD.gn b/content/public/browser/BUILD.gn index ec8b01e..7e6d83d 100644 --- a/content/public/browser/BUILD.gn +++ b/content/public/browser/BUILD.gn
@@ -527,7 +527,6 @@ sources += [ "browser_ppapi_host.h", "pepper_vpn_provider_resource_host_proxy.h", - "plugin_data_remover.h", "plugin_service.h", "plugin_service_filter.h", ]
diff --git a/content/public/browser/message_port_provider.h b/content/public/browser/message_port_provider.h index 15e99c9..7ce49765 100644 --- a/content/public/browser/message_port_provider.h +++ b/content/public/browser/message_port_provider.h
@@ -18,12 +18,9 @@ #include "base/android/scoped_java_ref.h" #endif -// TODO(crbug.com/1329657): The last IS_FUCHSIA check will not be needed once -// its build sets enable_cast_receiver. -#if BUILDFLAG(ENABLE_CAST_RECEIVER) && \ - (BUILDFLAG(IS_FUCHSIA) || BUILDFLAG(IS_CASTOS) || \ - BUILDFLAG(IS_CAST_ANDROID)) || \ - BUILDFLAG(IS_FUCHSIA) +#if BUILDFLAG(IS_FUCHSIA) || \ + BUILDFLAG(ENABLE_CAST_RECEIVER) && \ + (BUILDFLAG(IS_CASTOS) || BUILDFLAG(IS_CAST_ANDROID)) #include "third_party/blink/public/common/messaging/message_port_channel.h" #endif @@ -57,12 +54,11 @@ const base::android::JavaParamRef<jobjectArray>& ports); #endif // BUILDFLAG(IS_ANDROID) -// TODO(crbug.com/1329657): The last IS_FUCHSIA check will not be needed once -// its build sets enable_cast_receiver. -#if BUILDFLAG(ENABLE_CAST_RECEIVER) && \ - (BUILDFLAG(IS_FUCHSIA) || BUILDFLAG(IS_CASTOS) || \ - BUILDFLAG(IS_CAST_ANDROID)) || \ - BUILDFLAG(IS_FUCHSIA) +// Fuchsia WebEngine always uses this version. +// Some Cast Receiver implementations use it too. +#if BUILDFLAG(IS_FUCHSIA) || \ + BUILDFLAG(ENABLE_CAST_RECEIVER) && \ + (BUILDFLAG(IS_CASTOS) || BUILDFLAG(IS_CAST_ANDROID)) // If |target_origin| is unset, then no origin scoping is applied. static void PostMessageToFrame( Page& page,
diff --git a/content/public/browser/plugin_data_remover.h b/content/public/browser/plugin_data_remover.h deleted file mode 100644 index e242cf2..0000000 --- a/content/public/browser/plugin_data_remover.h +++ /dev/null
@@ -1,38 +0,0 @@ -// Copyright (c) 2012 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_PUBLIC_BROWSER_PLUGIN_DATA_REMOVER_H_ -#define CONTENT_PUBLIC_BROWSER_PLUGIN_DATA_REMOVER_H_ - -#include <vector> - -#include "base/time/time.h" -#include "content/common/content_export.h" - -namespace base { -class WaitableEvent; -} - -namespace content { -struct WebPluginInfo; - -class BrowserContext; - -class CONTENT_EXPORT PluginDataRemover { - public: - static PluginDataRemover* Create(content::BrowserContext* browser_context); - virtual ~PluginDataRemover() {} - - // Starts removing plugin data stored since |begin_time|. - virtual base::WaitableEvent* StartRemoving(base::Time begin_time) = 0; - - // Returns a list of all plugins that support removing LSO data. This method - // will use cached plugin data. Call PluginService::GetPlugins() if the latest - // data is needed. - static void GetSupportedPlugins(std::vector<WebPluginInfo>* plugins); -}; - -} // namespace content - -#endif // CONTENT_PUBLIC_BROWSER_PLUGIN_DATA_REMOVER_H_
diff --git a/content/shell/browser/shell_content_browser_client.cc b/content/shell/browser/shell_content_browser_client.cc index 9f5c6665..6a7bc3c 100644 --- a/content/shell/browser/shell_content_browser_client.cc +++ b/content/shell/browser/shell_content_browser_client.cc
@@ -39,6 +39,7 @@ #include "components/variations/service/variations_field_trial_creator.h" #include "components/variations/service/variations_service.h" #include "components/variations/service/variations_service_client.h" +#include "components/variations/variations_switches.h" #include "content/public/browser/client_certificate_delegate.h" #include "content/public/browser/login_delegate.h" #include "content/public/browser/navigation_throttle.h" @@ -715,13 +716,16 @@ variations::SafeSeedManager safe_seed_manager(local_state_.get()); + const base::CommandLine* command_line = + base::CommandLine::ForCurrentProcess(); // Since this is a test-only code path, some arguments to SetUpFieldTrials are // null. // TODO(crbug/1248066): Consider passing a low entropy provider and source. field_trial_creator.SetUpFieldTrials( variation_ids, - content::GetSwitchDependentFeatureOverrides( - *base::CommandLine::ForCurrentProcess()), + command_line->GetSwitchValueASCII( + variations::switches::kForceVariationIds), + content::GetSwitchDependentFeatureOverrides(*command_line), /*low_entropy_provider=*/nullptr, std::move(feature_list), metrics_state_manager.get(), field_trials_.get(), &safe_seed_manager, /*low_entropy_source_value=*/absl::nullopt);
diff --git a/device/fido/cable/fido_cable_discovery.cc b/device/fido/cable/fido_cable_discovery.cc index 5ce361c6..cd8be64e 100644 --- a/device/fido/cable/fido_cable_discovery.cc +++ b/device/fido/cable/fido_cable_discovery.cc
@@ -278,8 +278,11 @@ switch (fido::mac::ProcessIsSigned()) { case fido::mac::CodeSigningState::kSigned: FIDO_LOG(DEBUG) << "Bluetooth authorized: " - << (adapter_->GetOsPermissionStatus() != - BluetoothAdapter::PermissionStatus::kDenied); + << static_cast<int>(adapter_->GetOsPermissionStatus()); + if (adapter_->GetOsPermissionStatus() == + BluetoothAdapter::PermissionStatus::kDenied) { + observer()->BleDenied(); + } break; case fido::mac::CodeSigningState::kNotSigned: FIDO_LOG(DEBUG)
diff --git a/device/fido/fido_discovery_base.h b/device/fido/fido_discovery_base.h index b338e552..09b1af7 100644 --- a/device/fido/fido_discovery_base.h +++ b/device/fido/fido_discovery_base.h
@@ -42,6 +42,12 @@ FidoAuthenticator* authenticator) = 0; virtual void AuthenticatorRemoved(FidoDiscoveryBase* discovery, FidoAuthenticator* authenticator) = 0; + + // BleDenied is called if the user has denied access to the BLE hardware. + // This is macOS-specific and, unlike information like the power state, this + // information is only available once the caBLE discovery has opened the BLE + // adaptor. Thus the signal is plumbed via this observer interface. + virtual void BleDenied() {} }; // Start authenticator discovery. The Observer must have been set before this
diff --git a/device/fido/fido_request_handler_base.cc b/device/fido/fido_request_handler_base.cc index 9641d49f..ea1bb34 100644 --- a/device/fido/fido_request_handler_base.cc +++ b/device/fido/fido_request_handler_base.cc
@@ -387,6 +387,10 @@ #endif // BUILDFLAG(IS_WIN) } +void FidoRequestHandlerBase::BleDenied() { + transport_availability_info_.ble_access_denied = true; +} + void FidoRequestHandlerBase::GetPlatformCredentialStatus( FidoAuthenticator* platform_authenticator) { transport_availability_callback_readiness_
diff --git a/device/fido/fido_request_handler_base.h b/device/fido/fido_request_handler_base.h index 9e52164..867cbb8 100644 --- a/device/fido/fido_request_handler_base.h +++ b/device/fido/fido_request_handler_base.h
@@ -89,6 +89,10 @@ bool is_ble_powered = false; bool can_power_on_ble_adapter = false; + // ble_access_denied is set to true if Chromium does not have permission + // to use the BLE adaptor. Resolving this is a platform-specific operation. + bool ble_access_denied = false; + // Indicates whether the native Windows WebAuthn API is available. // Dispatching to it should be controlled by the embedder. // @@ -294,6 +298,7 @@ FidoAuthenticator* authenticator) override; void AuthenticatorRemoved(FidoDiscoveryBase* discovery, FidoAuthenticator* authenticator) override; + void BleDenied() override; // GetPlatformCredentialStatus is called to learn whether a platform // authenticator has credentials responsive to the current request. If this
diff --git a/extensions/browser/api/messaging/message_service.cc b/extensions/browser/api/messaging/message_service.cc index dea3e83e..729869f 100644 --- a/extensions/browser/api/messaging/message_service.cc +++ b/extensions/browser/api/messaging/message_service.cc
@@ -292,8 +292,7 @@ is_web_connection = true; // Sites can only connect to the CryptoToken component extension if it - // has been enabled via feature flag, enterprise policy or deprecation - // trial. + // has been enabled via feature flag or deprecation trial. // TODO(1224886): Delete together with CryptoToken code. if (target_extension_id == extension_misc::kCryptotokenExtensionId) { blink::TrialTokenValidator validator; @@ -302,8 +301,6 @@ const bool u2f_api_enabled = base::FeatureList::IsEnabled( extensions_features::kU2FSecurityKeyAPI) || - ExtensionPrefs::Get(context)->pref_service()->GetBoolean( - extensions::pref_names::kU2fSecurityKeyApiEnabled) || (response_headers && validator.RequestEnablesFeature( source_render_frame_host->GetLastCommittedURL(),
diff --git a/extensions/browser/extension_event_histogram_value.h b/extensions/browser/extension_event_histogram_value.h index cce873c..b53026ad 100644 --- a/extensions/browser/extension_event_histogram_value.h +++ b/extensions/browser/extension_event_histogram_value.h
@@ -481,6 +481,7 @@ CERTIFICATEPROVIDER_ON_CERTIFICATES_UPDATE_REQUESTED = 459, CERTIFICATEPROVIDER_ON_SIGNATURE_REQUESTED = 460, WINDOWS_ON_BOUNDS_CHANGED = 461, + // Obsolete since removing wallpaper extension b/193788853. WALLPAPER_PRIVATE_ON_CLOSE_PREVIEW_WALLPAPER = 462, PASSWORDS_PRIVATE_ON_WEAK_CREDENTIALS_CHANGED = 463, ACCESSIBILITY_PRIVATE_ON_MAGNIFIER_BOUNDS_CHANGED = 464,
diff --git a/extensions/docs/events.md b/extensions/docs/events.md index 96a9940..3cc848e 100644 --- a/extensions/docs/events.md +++ b/extensions/docs/events.md
@@ -10,8 +10,8 @@ An event listener registered in the renderer process is sent to the browser process (via IPC). The browser process stores the listener information in `EventListenerMap`. Events are dispatched from the browser process to the -renderer process via IPC. If browser process requires to persist any listener, -it does so by storing the listener information in the prefs. +renderer process via IPC. If the browser process requires persistence of any +listener, it does so by storing the listener information in `ExtensionPrefs`. ## Relevant concepts @@ -71,9 +71,9 @@ ### Additional notes about lazy event dispatching -Recall that a lazy listener is like a regular listeners, except that it is +Recall that a lazy listener is like a regular listener, except that it is registered from a lazy context. A lazy context can be shut down. If an -interesting event ocurrs while a lazy context (with a listener to that event) +interesting event occurs while a lazy context (with a listener to that event) is no longer running, then the lazy context is woken up to dispatch the event. The following (simplified) steps describe how dispatch is performed.
diff --git a/gpu/command_buffer/service/gpu_fence_manager_unittest.cc b/gpu/command_buffer/service/gpu_fence_manager_unittest.cc index 95f56497..d467b91 100644 --- a/gpu/command_buffer/service/gpu_fence_manager_unittest.cc +++ b/gpu/command_buffer/service/gpu_fence_manager_unittest.cc
@@ -18,8 +18,9 @@ #include "ui/gfx/gpu_fence.h" #include "ui/gfx/gpu_fence_handle.h" #include "ui/gl/egl_mock.h" +#include "ui/gl/gl_display.h" #include "ui/gl/gl_egl_api_implementation.h" -#include "ui/gl/gl_surface_egl.h" +#include "ui/gl/gl_utils.h" #if BUILDFLAG(IS_POSIX) #include <unistd.h> @@ -74,11 +75,13 @@ gl::ClearBindingsEGL(); gl::InitializeStaticGLBindingsEGL(); - display_ = gl::GLSurfaceEGL::InitializeOneOffForTesting(); + display_ = gl::GetDefaultDisplayEGL(); + display_->InitializeForTesting(); } void TeardownMockEGL() { - gl::GLSurfaceEGL::ShutdownOneOff(display_); + if (display_) + display_->Shutdown(); egl_.reset(); }
diff --git a/infra/config/generated/builders/ci/win32-archive-dbg/properties.json b/infra/config/generated/builders/ci/win32-archive-dbg/properties.json index c518c1c5..b707d9d 100644 --- a/infra/config/generated/builders/ci/win32-archive-dbg/properties.json +++ b/infra/config/generated/builders/ci/win32-archive-dbg/properties.json
@@ -1,4 +1,42 @@ { + "$build/chromium_tests_builder_config": { + "builder_config": { + "builder_db": { + "entries": [ + { + "builder_id": { + "bucket": "ci", + "builder": "win32-archive-dbg", + "project": "chromium" + }, + "builder_spec": { + "builder_group": "chromium", + "execution_mode": "COMPILE_AND_TEST", + "legacy_chromium_config": { + "apply_configs": [ + "clobber", + "mb" + ], + "build_config": "Debug", + "config": "chromium", + "target_bits": 32 + }, + "legacy_gclient_config": { + "config": "chromium" + } + } + } + ] + }, + "builder_ids": [ + { + "bucket": "ci", + "builder": "win32-archive-dbg", + "project": "chromium" + } + ] + } + }, "$build/reclient": { "instance": "rbe-chromium-trusted", "jobs": 250,
diff --git a/infra/config/generated/luci/cr-buildbucket.cfg b/infra/config/generated/luci/cr-buildbucket.cfg index 7754758..51f4d93 100644 --- a/infra/config/generated/luci/cr-buildbucket.cfg +++ b/infra/config/generated/luci/cr-buildbucket.cfg
@@ -15606,7 +15606,7 @@ ' "android"' ' ]' '}' - execution_timeout_secs: 50400 + execution_timeout_secs: 54000 build_numbers: YES service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com" experiments {
diff --git a/infra/config/subprojects/chromium/ci/chromium.android.star b/infra/config/subprojects/chromium/ci/chromium.android.star index 16f9e3e..d20670c6 100644 --- a/infra/config/subprojects/chromium/ci/chromium.android.star +++ b/infra/config/subprojects/chromium/ci/chromium.android.star
@@ -578,7 +578,7 @@ ), # We have limited tablet capacity and thus limited ability to run # tests in parallel, hence the high timeout. - execution_timeout = 14 * time.hour, + execution_timeout = 15 * time.hour, triggered_by = ["ci/Android arm Builder (dbg)"], )
diff --git a/infra/config/subprojects/chromium/ci/chromium.star b/infra/config/subprojects/chromium/ci/chromium.star index 15759d64..9707df4 100644 --- a/infra/config/subprojects/chromium/ci/chromium.star +++ b/infra/config/subprojects/chromium/ci/chromium.star
@@ -580,6 +580,20 @@ ci.builder( name = "win32-archive-dbg", + builder_spec = builder_config.builder_spec( + gclient_config = builder_config.gclient_config( + config = "chromium", + ), + chromium_config = builder_config.chromium_config( + config = "chromium", + apply_configs = [ + "clobber", + "mb", + ], + build_config = builder_config.build_config.DEBUG, + target_bits = 32, + ), + ), console_view_entry = consoles.console_view_entry( category = "win|dbg", short_name = "32",
diff --git a/ios/chrome/browser/ios_chrome_main_parts.h b/ios/chrome/browser/ios_chrome_main_parts.h index 56a12f1..83e9750 100644 --- a/ios/chrome/browser/ios_chrome_main_parts.h +++ b/ios/chrome/browser/ios_chrome_main_parts.h
@@ -36,8 +36,12 @@ void PostDestroyThreads() override; // Sets up the field trials and related initialization. Call only after - // about:flags have been converted to switches. - void SetUpFieldTrials(); + // about:flags have been converted to switches. However, + // |command_line_variation_ids| should be the value of the + // "--force-variation-ids" switch before it is mutated. See + // VariationsFieldTrialCreator::SetUpFieldTrials() for the format of + // |command_line_variation_ids|. + void SetUpFieldTrials(const std::string& command_line_variation_ids); // Constructs the metrics service and initializes metrics recording. void SetupMetrics();
diff --git a/ios/chrome/browser/ios_chrome_main_parts.mm b/ios/chrome/browser/ios_chrome_main_parts.mm index 325f737..b4248cf 100644 --- a/ios/chrome/browser/ios_chrome_main_parts.mm +++ b/ios/chrome/browser/ios_chrome_main_parts.mm
@@ -45,6 +45,7 @@ #include "components/variations/synthetic_trials_active_group_id_provider.h" #include "components/variations/variations_crash_keys.h" #include "components/variations/variations_ids_provider.h" +#include "components/variations/variations_switches.h" #include "ios/chrome/browser/application_context_impl.h" #include "ios/chrome/browser/browser_state/browser_state_keyed_service_factories.h" #include "ios/chrome/browser/browser_state/chrome_browser_state.h" @@ -213,10 +214,23 @@ break; } + base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); + // Get the variation IDs passed through the command line. This is done early + // on because ConvertFlagsToSwitches() will append to the command line + // the variation IDs from flags (so that they are visible in about://version). + // This will be passed on to `VariationsService::SetUpFieldTrials()`, which + // will manually fetch the variation IDs from flags (hence the reason we do + // not pass the mutated command line, otherwise the IDs will be duplicated). + // It also distinguishes between variation IDs coming from the command line + // and from flags, so we cannot rely on simply putting them all in the + // command line. + const std::string command_line_variation_ids = + command_line->GetSwitchValueASCII( + variations::switches::kForceVariationIds); + // Convert freeform experimental settings into switches before initializing // local state, in case any of the settings affect policy. - AppendSwitchesFromExperimentalSettings( - base::CommandLine::ForCurrentProcess()); + AppendSwitchesFromExperimentalSettings(command_line); // Initialize local state. local_state_ = application_context_->GetLocalState(); @@ -224,14 +238,13 @@ flags_ui::PrefServiceFlagsStorage flags_storage( application_context_->GetLocalState()); - ConvertFlagsToSwitches(&flags_storage, - base::CommandLine::ForCurrentProcess()); + ConvertFlagsToSwitches(&flags_storage, command_line); // Now that the command line has been mutated based on about:flags, we can // initialize field trials. The field trials are needed by IOThread's // initialization which happens in BrowserProcess:PreCreateThreads. Metrics // initialization is handled in PreMainMessageLoopRun since it posts tasks. - SetUpFieldTrials(); + SetUpFieldTrials(command_line_variation_ids); // Set metrics upload for stack/heap profiles. IOSThreadProfiler::SetBrowserProcessReceiverCallback(base::BindRepeating( @@ -400,7 +413,8 @@ } // This will be called after the command-line has been mutated by about:flags -void IOSChromeMainParts::SetUpFieldTrials() { +void IOSChromeMainParts::SetUpFieldTrials( + const std::string& command_line_variation_ids) { base::SetRecordActionTaskRunner(web::GetUIThreadTaskRunner({})); // FeatureList requires VariationsIdsProvider to be created. @@ -421,7 +435,8 @@ RegisterAllFeatureVariationParameters(&flags_storage, feature_list.get()); application_context_->GetVariationsService()->SetUpFieldTrials( - variation_ids, std::vector<base::FeatureList::FeatureOverrideInfo>(), + variation_ids, command_line_variation_ids, + std::vector<base::FeatureList::FeatureOverrideInfo>(), std::move(feature_list), &ios_field_trials_); }
diff --git a/ios/chrome/browser/policy/BUILD.gn b/ios/chrome/browser/policy/BUILD.gn index f5629646..41718683 100644 --- a/ios/chrome/browser/policy/BUILD.gn +++ b/ios/chrome/browser/policy/BUILD.gn
@@ -75,6 +75,7 @@ "//components/safe_browsing/core/common:safe_browsing_policy_handler", "//components/safe_browsing/core/common:safe_browsing_prefs", "//components/search_engines", + "//components/security_interstitials/core", "//components/strings:components_strings_grit", "//components/translate/core/browser:translate_pref_names", "//components/unified_consent:unified_consent",
diff --git a/ios/chrome/browser/policy/configuration_policy_handler_list_factory.mm b/ios/chrome/browser/policy/configuration_policy_handler_list_factory.mm index 6279362..e010005 100644 --- a/ios/chrome/browser/policy/configuration_policy_handler_list_factory.mm +++ b/ios/chrome/browser/policy/configuration_policy_handler_list_factory.mm
@@ -27,6 +27,7 @@ #include "components/safe_browsing/core/common/safe_browsing_policy_handler.h" #include "components/safe_browsing/core/common/safe_browsing_prefs.h" #include "components/search_engines/default_search_policy_handler.h" +#include "components/security_interstitials/core/https_only_mode_policy_handler.h" #include "components/signin/public/base/signin_pref_names.h" #include "components/sync/driver/sync_policy_handler.h" #include "components/translate/core/browser/translate_pref_names.h" @@ -158,6 +159,8 @@ std::make_unique<policy::NewTabPageLocationPolicyHandler>()); handlers->AddHandler(std::make_unique<policy::URLBlocklistPolicyHandler>( policy::key::kURLBlocklist)); + handlers->AddHandler(std::make_unique<policy::HttpsOnlyModePolicyHandler>( + prefs::kHttpsOnlyModeEnabled)); return handlers; }
diff --git a/ios/components/security_interstitials/lookalikes/lookalike_url_tab_helper.mm b/ios/components/security_interstitials/lookalikes/lookalike_url_tab_helper.mm index a9157e1..24a37cad 100644 --- a/ios/components/security_interstitials/lookalikes/lookalike_url_tab_helper.mm +++ b/ios/components/security_interstitials/lookalikes/lookalike_url_tab_helper.mm
@@ -81,12 +81,6 @@ std::move(callback).Run(CreateAllowDecision()); return; } - // If the URL is in the component updater allowlist, don't show any warning. - if (reputation::IsUrlAllowlistedBySafetyTipsComponent( - proto, response_url.GetWithEmptyPath())) { - std::move(callback).Run(CreateAllowDecision()); - return; - } // TODO(crbug.com/1104386): If this is a reload and if the current // URL is the last URL of the stored redirect chain, the interstitial @@ -115,7 +109,12 @@ }); if (!GetMatchingDomain(navigated_domain, engaged_sites, in_target_allowlist, proto, &matched_domain, &match_type)) { - if (ShouldBlockBySpoofCheckResult(navigated_domain)) { + // If the URL fails a spoof check, and isn't in the component allowlist, + // then show a spoof check interstitial. + if (ShouldBlockBySpoofCheckResult(navigated_domain) && + !reputation::IsUrlAllowlistedBySafetyTipsComponent( + proto, response_url.GetWithEmptyPath(), + response_url.GetWithEmptyPath())) { match_type = LookalikeUrlMatchType::kFailedSpoofChecks; RecordUMAFromMatchType(match_type); LookalikeUrlContainer* lookalike_container = @@ -133,29 +132,37 @@ RecordUMAFromMatchType(match_type); - if (ShouldBlockLookalikeUrlNavigation(match_type)) { - const std::string suggested_domain = GetETLDPlusOne(matched_domain); - DCHECK(!suggested_domain.empty()); - GURL::Replacements replace_host; - replace_host.SetHostStr(suggested_domain); - const GURL suggested_url = - response_url.ReplaceComponents(replace_host).GetWithEmptyPath(); - LookalikeUrlContainer* lookalike_container = - LookalikeUrlContainer::FromWebState(web_state()); - lookalike_container->SetLookalikeUrlInfo(suggested_url, response_url, - match_type); + const std::string suggested_domain = GetETLDPlusOne(matched_domain); + DCHECK(!suggested_domain.empty()); + GURL::Replacements replace_host; + replace_host.SetHostStr(suggested_domain); + const GURL suggested_url = + response_url.ReplaceComponents(replace_host).GetWithEmptyPath(); - std::move(callback).Run(CreateLookalikeErrorDecision()); + // If the URL is in the component updater allowlist, don't show any warning. + if (reputation::IsUrlAllowlistedBySafetyTipsComponent( + proto, response_url.GetWithEmptyPath(), + suggested_url.GetWithEmptyPath())) { + std::move(callback).Run(CreateAllowDecision()); return; } - // Interstitial normally records UKM, but still record when it's not shown. - RecordUkmForLookalikeUrlBlockingPage( - ukm::GetSourceIdForWebStateDocument(web_state()), match_type, - LookalikeUrlBlockingPageUserAction::kInterstitialNotShown, - /*triggered_by_initial_url=*/false); + if (!ShouldBlockLookalikeUrlNavigation(match_type)) { + // Interstitial normally records UKM, but still record when it's not shown. + RecordUkmForLookalikeUrlBlockingPage( + ukm::GetSourceIdForWebStateDocument(web_state()), match_type, + LookalikeUrlBlockingPageUserAction::kInterstitialNotShown, + /*triggered_by_initial_url=*/false); - std::move(callback).Run(CreateAllowDecision()); + std::move(callback).Run(CreateAllowDecision()); + return; + } + + LookalikeUrlContainer* lookalike_container = + LookalikeUrlContainer::FromWebState(web_state()); + lookalike_container->SetLookalikeUrlInfo(suggested_url, response_url, + match_type); + std::move(callback).Run(CreateLookalikeErrorDecision()); } WEB_STATE_USER_DATA_KEY_IMPL(LookalikeUrlTabHelper)
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 a8bd167..7993d90 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 @@ -b3be12d1dc7687a0918c420e60105a939d810323 \ No newline at end of file +a772b3d6902fea1ae01076e95e48e3a29dbe0d69 \ 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 8bc33cd..72c2386 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 @@ -0c641280e4efe1b832246f1c058acfedf4dfde21 \ No newline at end of file +f249c785b5daedf48d84f25323638654f9866f15 \ 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 38857e9d..63eba1d 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 @@ -f7cafb609bfda98f82fa3c7ff08a01f7be9cde41 \ No newline at end of file +a1f7e2376115f3c4f87dafeb54e5d00a4c66e501 \ 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 33f4cd4..0fd4c9c0 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 @@ -730abc018dfb9e451c6d0ecc86b5c373a810116e \ No newline at end of file +17db2e62db79c5e2506deaa68b7fe080ae362a5b \ 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 5903037..a5f5c0f 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 @@ -bd7ec7ed6038a31d6d2ccd4e65e437996f56be76 \ No newline at end of file +ced3fb93702e019496a9b3058d99b37d0fa01146 \ 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 48dbbe2..15a6c27 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 @@ -d3505cc5cf770c62c0c05fae40a7cba3917efbd1 \ No newline at end of file +f5e7cb805688f3c3725a88df68453c851292b98f \ 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 f9c0a3e..43d38a4 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 @@ -7dd7eba0aaf693ec6671807d36cedb1f2f93eee1 \ No newline at end of file +383ef88835ff1e3a9f2853b10ad08d68a3777376 \ 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 c0d370a..4d4836b 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 @@ -57fd4ef18bda74e8627dd7fbcf26e7574a56689b \ No newline at end of file +6071dcac78bc319ff66b8c54cb041022f9111557 \ 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 b6c9b85..755db478 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 @@ -8dcee02f88c8aa2eb7d30ac1626f25d0f7a4ad1a \ No newline at end of file +254b64a71b2e14b18c1f161aadd55f906c6a08bd \ 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 65cf4e4..378d43b 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 @@ -e881e266d8d0ad69fbc17c28e7d211ad3b32caf1 \ No newline at end of file +dd94367dad63a5cf1d98dd75f289f4d56a9e94f9 \ No newline at end of file
diff --git a/media/formats/hls/playlist_common.cc b/media/formats/hls/playlist_common.cc index c35c933..3daf7d0 100644 --- a/media/formats/hls/playlist_common.cc +++ b/media/formats/hls/playlist_common.cc
@@ -122,7 +122,8 @@ } // URIs may be relative to the playlist URI, resolve it against that. - auto resolved_uri = playlist_uri.Resolve(std::move(uri_str_result).value()); + auto resolved_uri = + playlist_uri.Resolve(std::move(uri_str_result).value().Str()); if (!resolved_uri.is_valid()) { return ParseStatusCode::kInvalidUri; }
diff --git a/media/formats/hls/source_string.cc b/media/formats/hls/source_string.cc index 7f847fb..d8dee3f 100644 --- a/media/formats/hls/source_string.cc +++ b/media/formats/hls/source_string.cc
@@ -11,31 +11,67 @@ namespace media::hls { +template <> SourceString SourceString::Create(base::PassKey<SourceLineIterator>, size_t line, base::StringPiece str) { - return SourceString(line, 1, str); + return SourceString(line, 1, str, {}); } -SourceString SourceString::CreateForTesting(base::StringPiece str) { - return SourceString::CreateForTesting(1, 1, str); +template <> +ResolvedSourceString ResolvedSourceString::Create( + base::PassKey<VariableDictionary>, + size_t line, + size_t column, + base::StringPiece str, + ResolvedSourceStringState resolution_state) { + return ResolvedSourceString(line, column, str, resolution_state); } +template <typename ResolutionState> +GenericSourceString<ResolutionState> +GenericSourceString<ResolutionState>::CreateForTesting(base::StringPiece str) { + return GenericSourceString::CreateForTesting(1, 1, str); +} + +template <> SourceString SourceString::CreateForTesting(size_t line, size_t column, base::StringPiece str) { - return SourceString(line, column, str); + return SourceString::CreateForTesting(line, column, str, {}); } -SourceString::SourceString(size_t line, size_t column, base::StringPiece str) - : line_(line), column_(column), str_(str) {} +template <> +ResolvedSourceString ResolvedSourceString::CreateForTesting( + size_t line, + size_t column, + base::StringPiece str) { + return ResolvedSourceString::CreateForTesting( + line, column, str, + ResolvedSourceStringState{.contains_substitutions = false}); +} -SourceString SourceString::Substr(size_t pos, size_t count) const { +template <typename ResolutionState> +GenericSourceString<ResolutionState> +GenericSourceString<ResolutionState>::CreateForTesting( + size_t line, + size_t column, + base::StringPiece str, + ResolutionState resolution_state) { + return GenericSourceString(line, column, str, resolution_state); +} + +template <typename ResolutionState> +GenericSourceString<ResolutionState> +GenericSourceString<ResolutionState>::Substr(size_t pos, size_t count) const { const auto column = column_ + pos; - return SourceString(line_, column, str_.substr(pos, count)); + return GenericSourceString(line_, column, str_.substr(pos, count), + resolution_state_); } -SourceString SourceString::Consume(size_t count) { +template <typename ResolutionState> +GenericSourceString<ResolutionState> +GenericSourceString<ResolutionState>::Consume(size_t count) { count = std::min(count, str_.size()); auto consumed = Substr(0, count); @@ -44,6 +80,34 @@ return consumed; } +template <> +ResolvedSourceString SourceString::SkipVariableSubstitution() const { + return ResolvedSourceString( + Line(), Column(), Str(), + ResolvedSourceStringState{.contains_substitutions = false}); +} + +template <> +bool SourceString::ContainsSubstitutions() const { + return false; +} + +template <> +bool ResolvedSourceString::ContainsSubstitutions() const { + return resolution_state_.contains_substitutions; +} + +template <typename ResolutionState> +GenericSourceString<ResolutionState>::GenericSourceString( + size_t line, + size_t column, + base::StringPiece str, + ResolutionState resolution_state) + : line_(line), + column_(column), + str_(str), + resolution_state_(resolution_state) {} + SourceLineIterator::SourceLineIterator(base::StringPiece source) : current_line_(1), source_(source) {} @@ -75,4 +139,12 @@ return SourceString::Create({}, line_number, line_content); } +// These forward declarations tell the compiler that we will use +// `GenericSourceString` with these arguments, allowing us to keep these +// definitions in our .cc without causing linker errors. This also means if +// anyone tries to instantiate a `GenericSourceString` with anything but these +// two specializations they'll most likely get linker errors. +template class MEDIA_EXPORT GenericSourceString<SourceStringState>; +template class MEDIA_EXPORT GenericSourceString<ResolvedSourceStringState>; + } // namespace media::hls
diff --git a/media/formats/hls/source_string.h b/media/formats/hls/source_string.h index 48dfd215..9dd615d 100644 --- a/media/formats/hls/source_string.h +++ b/media/formats/hls/source_string.h
@@ -14,18 +14,52 @@ namespace media::hls { struct SourceLineIterator; +class VariableDictionary; + +// Type representing the resolution state for a `SourceString`. +// As there is only one state here (unresolved), this struct is empty. +struct SourceStringState {}; + +// Type containing the resolution state for a `ResolvedSourceString`. +struct ResolvedSourceStringState { + // Whether this string has undergone variable substitution and has + // substitutions applied to the original source. + bool contains_substitutions; +}; + +template <typename T> +class GenericSourceString; + +// A `SourceString` is a slice of the original manifest string that may contain +// unresolved variable references. +using SourceString = GenericSourceString<SourceStringState>; + +// A `ResolvedSourceString` is a string slice that has either undergone or +// skipped variable substitution, and may differ from the original source. +using ResolvedSourceString = GenericSourceString<ResolvedSourceStringState>; // This structure represents contents of a single line in an HLS manifest, not // including the line ending. This may be the entire line, or a substring of the // line (clipped at either/both ends). -struct MEDIA_EXPORT SourceString { - static SourceString Create(base::PassKey<SourceLineIterator>, - size_t line, - base::StringPiece str); - static SourceString CreateForTesting(base::StringPiece str); - static SourceString CreateForTesting(size_t line, - size_t column, - base::StringPiece str); +template <typename ResolutionState> +class MEDIA_EXPORT GenericSourceString { + public: + static GenericSourceString Create(base::PassKey<SourceLineIterator>, + size_t line, + base::StringPiece str); + static GenericSourceString Create(base::PassKey<VariableDictionary>, + size_t line, + size_t column, + base::StringPiece str, + ResolutionState resolution_state); + static GenericSourceString CreateForTesting(base::StringPiece str); + static GenericSourceString CreateForTesting(size_t line, + size_t column, + base::StringPiece str); + static GenericSourceString CreateForTesting(size_t line, + size_t column, + base::StringPiece str, + ResolutionState resolution_state); // Returns the 1-based line index of this SourceString within the manifest. size_t Line() const { return line_; } @@ -42,21 +76,58 @@ size_t Size() const { return str_.size(); } - SourceString Substr(size_t pos = 0, - size_t count = base::StringPiece::npos) const; + GenericSourceString Substr(size_t pos = 0, + size_t count = base::StringPiece::npos) const; // Consumes this string up to the given count, which may be longer than this // string. Returns the substring that was consumed. - SourceString Consume(size_t count = base::StringPiece::npos); + GenericSourceString Consume(size_t count = base::StringPiece::npos); + + // Produces a `ResolvedSourceString` by bypassing variable substitution. + // This is useful for passing strings that must not contain variables to + // functions consuming strings that may or may not have contained variable + // references. + ResolvedSourceString SkipVariableSubstitution() const; + + // Returns whether this string contains variable substitutions, i.e. is + // different from the original source. + bool ContainsSubstitutions() const; private: - SourceString(size_t line, size_t column, base::StringPiece str); + template <typename> + friend class GenericSourceString; + + GenericSourceString(size_t line, + size_t column, + base::StringPiece str, + ResolutionState resolution_state); size_t line_; size_t column_; base::StringPiece str_; + ResolutionState resolution_state_; }; +// `SourceLineIterator` may not create resolved source strings +template <> +ResolvedSourceString ResolvedSourceString::Create( + base::PassKey<SourceLineIterator>, + size_t line, + base::StringPiece str) = delete; + +// `VariableDictionary` may not create unresolved source strings +template <> +SourceString SourceString::Create(base::PassKey<VariableDictionary>, + size_t line, + size_t column, + base::StringPiece str, + SourceStringState resolution_state) = delete; + +// Resolved source strings may not skip variable substitution +template <> +ResolvedSourceString ResolvedSourceString::SkipVariableSubstitution() const = + delete; + // Exposes a line-based iteration API over the source text of an HLS manifest. struct MEDIA_EXPORT SourceLineIterator { explicit SourceLineIterator(base::StringPiece source);
diff --git a/media/formats/hls/tags.cc b/media/formats/hls/tags.cc index 7b04bb2..b634262 100644 --- a/media/formats/hls/tags.cc +++ b/media/formats/hls/tags.cc
@@ -35,7 +35,8 @@ return ParseStatusCode::kMalformedTag; } - auto value = types::ParseDecimalInteger(*tag.GetContent()); + auto value = + types::ParseDecimalInteger(tag.GetContent()->SkipVariableSubstitution()); if (value.has_error()) { return ParseStatus(ParseStatusCode::kMalformedTag) .AddCause(std::move(value).error()); @@ -255,7 +256,8 @@ // Extract duration // TODO(crbug.com/1284763): Below version 3 this should be rounded to an // integer - auto duration_result = types::ParseDecimalFloatingPoint(duration_str); + auto duration_result = + types::ParseDecimalFloatingPoint(duration_str.SkipVariableSubstitution()); if (duration_result.has_error()) { return ParseStatus(ParseStatusCode::kMalformedTag) .AddCause(std::move(duration_result).error()); @@ -422,7 +424,8 @@ // Extract the 'BANDWIDTH' attribute if (map.HasValue(XStreamInfTagAttribute::kBandwidth)) { auto bandwidth = types::ParseDecimalInteger( - map.GetValue(XStreamInfTagAttribute::kBandwidth)); + map.GetValue(XStreamInfTagAttribute::kBandwidth) + .SkipVariableSubstitution()); if (bandwidth.has_error()) { return ParseStatus(ParseStatusCode::kMalformedTag) .AddCause(std::move(bandwidth).error()); @@ -436,7 +439,8 @@ // Extract the 'AVERAGE-BANDWIDTH' attribute if (map.HasValue(XStreamInfTagAttribute::kAverageBandwidth)) { auto average_bandwidth = types::ParseDecimalInteger( - map.GetValue(XStreamInfTagAttribute::kAverageBandwidth)); + map.GetValue(XStreamInfTagAttribute::kAverageBandwidth) + .SkipVariableSubstitution()); if (average_bandwidth.has_error()) { return ParseStatus(ParseStatusCode::kMalformedTag) .AddCause(std::move(average_bandwidth).error()); @@ -448,7 +452,8 @@ // Extract the 'SCORE' attribute if (map.HasValue(XStreamInfTagAttribute::kScore)) { auto score = types::ParseDecimalFloatingPoint( - map.GetValue(XStreamInfTagAttribute::kScore)); + map.GetValue(XStreamInfTagAttribute::kScore) + .SkipVariableSubstitution()); if (score.has_error()) { return ParseStatus(ParseStatusCode::kMalformedTag) .AddCause(std::move(score).error()); @@ -466,13 +471,14 @@ return ParseStatus(ParseStatusCode::kMalformedTag) .AddCause(std::move(codecs).error()); } - out.codecs = std::string{std::move(codecs).value()}; + out.codecs = std::string{std::move(codecs).value().Str()}; } // Extract the 'RESOLUTION' attribute if (map.HasValue(XStreamInfTagAttribute::kResolution)) { auto resolution = types::DecimalResolution::Parse( - map.GetValue(XStreamInfTagAttribute::kResolution)); + map.GetValue(XStreamInfTagAttribute::kResolution) + .SkipVariableSubstitution()); if (resolution.has_error()) { return ParseStatus(ParseStatusCode::kMalformedTag) .AddCause(std::move(resolution).error()); @@ -483,7 +489,8 @@ // Extract the 'FRAME-RATE' attribute if (map.HasValue(XStreamInfTagAttribute::kFrameRate)) { auto frame_rate = types::ParseDecimalFloatingPoint( - map.GetValue(XStreamInfTagAttribute::kFrameRate)); + map.GetValue(XStreamInfTagAttribute::kFrameRate) + .SkipVariableSubstitution()); if (frame_rate.has_error()) { return ParseStatus(ParseStatusCode::kMalformedTag) .AddCause(std::move(frame_rate).error()); @@ -500,7 +507,8 @@ return ParseStatusCode::kMalformedTag; } - auto duration_result = types::ParseDecimalInteger(tag.GetContent().value()); + auto duration_result = types::ParseDecimalInteger( + tag.GetContent().value().SkipVariableSubstitution()); if (duration_result.has_error()) { return ParseStatus(ParseStatusCode::kMalformedTag) .AddCause(std::move(duration_result).error()); @@ -534,7 +542,8 @@ base::TimeDelta part_target; if (map.HasValue(XPartInfTagAttribute::kPartTarget)) { auto result = types::ParseDecimalFloatingPoint( - map.GetValue(XPartInfTagAttribute::kPartTarget)); + map.GetValue(XPartInfTagAttribute::kPartTarget) + .SkipVariableSubstitution()); if (result.has_error()) { return ParseStatus(ParseStatusCode::kMalformedTag) @@ -573,7 +582,8 @@ absl::optional<base::TimeDelta> can_skip_until; if (map.HasValue(XServerControlTagAttribute::kCanSkipUntil)) { auto result = types::ParseDecimalFloatingPoint( - map.GetValue(XServerControlTagAttribute::kCanSkipUntil)); + map.GetValue(XServerControlTagAttribute::kCanSkipUntil) + .SkipVariableSubstitution()); if (result.has_error()) { return ParseStatus(ParseStatusCode::kMalformedTag) @@ -606,7 +616,8 @@ absl::optional<base::TimeDelta> hold_back; if (map.HasValue(XServerControlTagAttribute::kHoldBack)) { auto result = types::ParseDecimalFloatingPoint( - map.GetValue(XServerControlTagAttribute::kHoldBack)); + map.GetValue(XServerControlTagAttribute::kHoldBack) + .SkipVariableSubstitution()); if (result.has_error()) { return ParseStatus(ParseStatusCode::kMalformedTag) @@ -624,7 +635,8 @@ absl::optional<base::TimeDelta> part_hold_back; if (map.HasValue(XServerControlTagAttribute::kPartHoldBack)) { auto result = types::ParseDecimalFloatingPoint( - map.GetValue(XServerControlTagAttribute::kPartHoldBack)); + map.GetValue(XServerControlTagAttribute::kPartHoldBack) + .SkipVariableSubstitution()); if (result.has_error()) { return ParseStatus(ParseStatusCode::kMalformedTag) @@ -671,7 +683,8 @@ return ParseStatusCode::kMalformedTag; } - auto range = types::ByteRangeExpression::Parse(*tag.GetContent()); + auto range = types::ByteRangeExpression::Parse( + tag.GetContent()->SkipVariableSubstitution()); if (range.has_error()) { return ParseStatus(ParseStatusCode::kMalformedTag) .AddCause(std::move(range).error());
diff --git a/media/formats/hls/types.cc b/media/formats/hls/types.cc index 264f44d..e1b530cf 100644 --- a/media/formats/hls/types.cc +++ b/media/formats/hls/types.cc
@@ -139,7 +139,8 @@ } // namespace -ParseStatus::Or<DecimalInteger> ParseDecimalInteger(SourceString source_str) { +ParseStatus::Or<DecimalInteger> ParseDecimalInteger( + ResolvedSourceString source_str) { static const base::NoDestructor<re2::RE2> decimal_integer_regex("\\d{1,20}"); const auto str = source_str.Str(); @@ -161,7 +162,7 @@ } ParseStatus::Or<DecimalFloatingPoint> ParseDecimalFloatingPoint( - SourceString source_str) { + ResolvedSourceString source_str) { // Utilize signed parsing function auto result = ParseSignedDecimalFloatingPoint(source_str); if (result.has_error()) { @@ -178,7 +179,7 @@ } ParseStatus::Or<SignedDecimalFloatingPoint> ParseSignedDecimalFloatingPoint( - SourceString source_str) { + ResolvedSourceString source_str) { // Accept no decimal point, decimal point with leading digits, trailing // digits, or both static const base::NoDestructor<re2::RE2> decimal_floating_point_regex( @@ -202,7 +203,7 @@ } ParseStatus::Or<DecimalResolution> DecimalResolution::Parse( - SourceString source_str) { + ResolvedSourceString source_str) { // decimal-resolution values are in the format: DecimalInteger 'x' // DecimalInteger const auto x_index = source_str.Str().find_first_of('x'); @@ -229,7 +230,7 @@ } ParseStatus::Or<ByteRangeExpression> ByteRangeExpression::Parse( - SourceString source_str) { + ResolvedSourceString source_str) { // If this ByteRange has an offset, it will be separated from the length by // '@'. const auto at_index = source_str.Str().find_first_of('@'); @@ -271,7 +272,7 @@ return ByteRange(length, offset); } -ParseStatus::Or<base::StringPiece> ParseQuotedString( +ParseStatus::Or<ResolvedSourceString> ParseQuotedString( SourceString source_str, const VariableDictionary& variable_dict, VariableDictionary::SubstitutionBuffer& sub_buffer) {
diff --git a/media/formats/hls/types.h b/media/formats/hls/types.h index 22f08ead..b8cd9d51a8 100644 --- a/media/formats/hls/types.h +++ b/media/formats/hls/types.h
@@ -19,27 +19,28 @@ using DecimalInteger = uint64_t; MEDIA_EXPORT ParseStatus::Or<DecimalInteger> ParseDecimalInteger( - SourceString source_str); + ResolvedSourceString source_str); // A `DecimalFloatingPoint` is an unsigned floating-point value. // https://datatracker.ietf.org/doc/html/draft-pantos-hls-rfc8216bis#:~:text=on%20its%20AttributeNames.%0A%0A%20%20%20o-,decimal%2Dfloating%2Dpoint,-%3A%20an%20unquoted%20string using DecimalFloatingPoint = double; MEDIA_EXPORT ParseStatus::Or<DecimalFloatingPoint> ParseDecimalFloatingPoint( - SourceString source_str); + ResolvedSourceString source_str); // A `SignedDecimalFloatingPoint` is a signed floating-point value. // https://datatracker.ietf.org/doc/html/draft-pantos-hls-rfc8216bis#:~:text=decimal%20positional%20notation.%0A%0A%20%20%20o-,signed%2Ddecimal%2Dfloating%2Dpoint,-%3A%20an%20unquoted%20string using SignedDecimalFloatingPoint = double; MEDIA_EXPORT ParseStatus::Or<SignedDecimalFloatingPoint> -ParseSignedDecimalFloatingPoint(SourceString source_str); +ParseSignedDecimalFloatingPoint(ResolvedSourceString source_str); // A `DecimalResolution` is a set of two `DecimalInteger`s describing width and // height. // https://datatracker.ietf.org/doc/html/draft-pantos-hls-rfc8216bis#:~:text=enumerated%2Dstring%2Dlist.%0A%0A%20%20%20o-,decimal%2Dresolution,-%3A%20two%20decimal%2Dintegers struct MEDIA_EXPORT DecimalResolution { - static ParseStatus::Or<DecimalResolution> Parse(SourceString source_str); + static ParseStatus::Or<DecimalResolution> Parse( + ResolvedSourceString source_str); types::DecimalInteger width; types::DecimalInteger height; @@ -49,7 +50,8 @@ // in tags describing byte ranges of a resource. // https://datatracker.ietf.org/doc/html/draft-pantos-hls-rfc8216bis#section-4.4.4.2 struct MEDIA_EXPORT ByteRangeExpression { - static ParseStatus::Or<ByteRangeExpression> Parse(SourceString source_str); + static ParseStatus::Or<ByteRangeExpression> Parse( + ResolvedSourceString source_str); // The length of the sub-range, in bytes. types::DecimalInteger length; @@ -88,7 +90,7 @@ // Parses a string surrounded by double-quotes ("), returning the inner string. // These appear in the context of attribute-lists, and are subject to variable // substitution. `sub_buffer` must outlive the returned string. -MEDIA_EXPORT ParseStatus::Or<base::StringPiece> ParseQuotedString( +MEDIA_EXPORT ParseStatus::Or<ResolvedSourceString> ParseQuotedString( SourceString source_str, const VariableDictionary& variable_dict, VariableDictionary::SubstitutionBuffer& sub_buffer);
diff --git a/media/formats/hls/types_unittest.cc b/media/formats/hls/types_unittest.cc index 1b482cdd..591604f 100644 --- a/media/formats/hls/types_unittest.cc +++ b/media/formats/hls/types_unittest.cc
@@ -20,24 +20,23 @@ const auto error_test = [](base::StringPiece input, const base::Location& from = base::Location::Current()) { - auto result = - types::ParseDecimalInteger(SourceString::CreateForTesting(1, 1, input)); + auto result = types::ParseDecimalInteger( + ResolvedSourceString::CreateForTesting(input)); ASSERT_TRUE(result.has_error()) << from.ToString(); auto error = std::move(result).error(); EXPECT_EQ(error.code(), ParseStatusCode::kFailedToParseDecimalInteger) << from.ToString(); }; - const auto ok_test = [](base::StringPiece input, - types::DecimalInteger expected, - const base::Location& from = - base::Location::Current()) { - auto result = - types::ParseDecimalInteger(SourceString::CreateForTesting(1, 1, input)); - ASSERT_TRUE(result.has_value()) << from.ToString(); - auto value = std::move(result).value(); - EXPECT_EQ(value, expected) << from.ToString(); - }; + const auto ok_test = + [](base::StringPiece input, types::DecimalInteger expected, + const base::Location& from = base::Location::Current()) { + auto result = types::ParseDecimalInteger( + ResolvedSourceString::CreateForTesting(input)); + ASSERT_TRUE(result.has_value()) << from.ToString(); + auto value = std::move(result).value(); + EXPECT_EQ(value, expected) << from.ToString(); + }; // Empty string is not allowed error_test(""); @@ -76,23 +75,22 @@ const base::Location& from = base::Location::Current()) { auto result = types::ParseDecimalFloatingPoint( - SourceString::CreateForTesting(1, 1, input)); + ResolvedSourceString::CreateForTesting(input)); ASSERT_TRUE(result.has_error()) << from.ToString(); auto error = std::move(result).error(); EXPECT_EQ(error.code(), ParseStatusCode::kFailedToParseDecimalFloatingPoint) << from.ToString(); }; - const auto ok_test = [](base::StringPiece input, - types::DecimalFloatingPoint expected, - const base::Location& from = - base::Location::Current()) { - auto result = types::ParseDecimalFloatingPoint( - SourceString::CreateForTesting(1, 1, input)); - ASSERT_TRUE(result.has_value()) << from.ToString(); - auto value = std::move(result).value(); - EXPECT_DOUBLE_EQ(value, expected) << from.ToString(); - }; + const auto ok_test = + [](base::StringPiece input, types::DecimalFloatingPoint expected, + const base::Location& from = base::Location::Current()) { + auto result = types::ParseDecimalFloatingPoint( + ResolvedSourceString::CreateForTesting(input)); + ASSERT_TRUE(result.has_value()) << from.ToString(); + auto value = std::move(result).value(); + EXPECT_DOUBLE_EQ(value, expected) << from.ToString(); + }; // Empty string is not allowed error_test(""); @@ -128,7 +126,7 @@ const base::Location& from = base::Location::Current()) { auto result = types::ParseSignedDecimalFloatingPoint( - SourceString::CreateForTesting(1, 1, input)); + ResolvedSourceString::CreateForTesting(input)); ASSERT_TRUE(result.has_error()) << from.ToString(); auto error = std::move(result).error(); EXPECT_EQ(error.code(), @@ -136,16 +134,15 @@ << from.ToString(); }; - const auto ok_test = [](base::StringPiece input, - types::SignedDecimalFloatingPoint expected, - const base::Location& from = - base::Location::Current()) { - auto result = types::ParseSignedDecimalFloatingPoint( - SourceString::CreateForTesting(1, 1, input)); - ASSERT_TRUE(result.has_value()) << from.ToString(); - auto value = std::move(result).value(); - EXPECT_DOUBLE_EQ(value, expected) << from.ToString(); - }; + const auto ok_test = + [](base::StringPiece input, types::SignedDecimalFloatingPoint expected, + const base::Location& from = base::Location::Current()) { + auto result = types::ParseSignedDecimalFloatingPoint( + ResolvedSourceString::CreateForTesting(input)); + ASSERT_TRUE(result.has_value()) << from.ToString(); + auto value = std::move(result).value(); + EXPECT_DOUBLE_EQ(value, expected) << from.ToString(); + }; // Empty string is not allowed error_test(""); @@ -520,7 +517,8 @@ VariableDictionary::SubstitutionBuffer sub_buffer; auto out = types::ParseQuotedString(in_str, dict, sub_buffer); ASSERT_TRUE(out.has_value()) << from.ToString(); - EXPECT_EQ(std::move(out).value(), expected_out) << from.ToString(); + EXPECT_EQ(std::move(out).value().Str(), expected_out) + << from.ToString(); }; const auto error_test = [&dict](base::StringPiece in, @@ -568,7 +566,7 @@ const base::Location& from = base::Location::Current()) { auto result = types::DecimalResolution::Parse( - SourceString::CreateForTesting(1, 1, input)); + ResolvedSourceString::CreateForTesting(input)); ASSERT_TRUE(result.has_error()) << from.ToString(); auto error = std::move(result).error(); EXPECT_EQ(error.code(), ParseStatusCode::kFailedToParseDecimalResolution) @@ -579,7 +577,7 @@ [](base::StringPiece input, types::DecimalResolution expected, const base::Location& from = base::Location::Current()) { auto result = types::DecimalResolution::Parse( - SourceString::CreateForTesting(1, 1, input)); + ResolvedSourceString::CreateForTesting(input)); ASSERT_TRUE(result.has_value()) << from.ToString(); auto value = std::move(result).value(); EXPECT_EQ(value.width, expected.width) << from.ToString(); @@ -643,7 +641,7 @@ const base::Location& from = base::Location::Current()) { auto result = types::ByteRangeExpression::Parse( - SourceString::CreateForTesting(input)); + ResolvedSourceString::CreateForTesting(input)); ASSERT_TRUE(result.has_error()); auto error = std::move(result).error(); EXPECT_EQ(error.code(), ParseStatusCode::kFailedToParseByteRange) @@ -653,7 +651,7 @@ [](base::StringPiece input, types::ByteRangeExpression expected, const base::Location& from = base::Location::Current()) { auto result = types::ByteRangeExpression::Parse( - SourceString::CreateForTesting(input)); + ResolvedSourceString::CreateForTesting(input)); ASSERT_TRUE(result.has_value()); auto value = std::move(result).value(); EXPECT_EQ(value.length, expected.length);
diff --git a/media/formats/hls/variable_dictionary.cc b/media/formats/hls/variable_dictionary.cc index ffd513c..16ccaee 100644 --- a/media/formats/hls/variable_dictionary.cc +++ b/media/formats/hls/variable_dictionary.cc
@@ -60,6 +60,10 @@ } // namespace +VariableDictionary::SubstitutionBuffer::SubstitutionBuffer() = default; + +VariableDictionary::SubstitutionBuffer::~SubstitutionBuffer() = default; + VariableDictionary::VariableDictionary() = default; VariableDictionary::~VariableDictionary() = default; @@ -76,30 +80,48 @@ return absl::nullopt; } - return iter->second; + return *iter->second; } bool VariableDictionary::Insert(types::VariableName name, std::string value) { - return entries_.try_emplace(std::move(name).GetName(), std::move(value)) + return entries_ + .try_emplace(std::move(name).GetName(), + std::make_unique<std::string>(std::move(value))) .second; } -ParseStatus::Or<base::StringPiece> VariableDictionary::Resolve( +ParseStatus::Or<ResolvedSourceString> VariableDictionary::Resolve( SourceString input, SubstitutionBuffer& buffer) const { // Get the first variable reference. If this fails, there were no references // and we don't need to allocate anything. auto next_var = GetNextVariable(input); if (!next_var.tail) { - return next_var.head.Str(); + return ResolvedSourceString::Create( + {}, input.Line(), input.Column(), input.Str(), + ResolvedSourceStringState{.contains_substitutions = false}); } - buffer.buf_.clear(); + // If there was a variable reference, but it consisted of the entire input + // string, then simply return a reference to the substitution string. + if (next_var.head.Empty() && next_var.tail->second.Empty()) { + auto value = Find(next_var.tail->first); + if (!value) { + return ParseStatus(ParseStatusCode::kVariableUndefined) + .WithData("key", next_var.tail->first.GetName()); + } + + return ResolvedSourceString::Create( + {}, input.Line(), input.Column(), *value, + ResolvedSourceStringState{.contains_substitutions = true}); + } + + auto& string_buf = buffer.strings_.emplace_back(); while (true) { // Append the substring leading to the variable, and abort if there was no // variable reference - buffer.buf_.append(next_var.head.Str().data(), next_var.head.Str().size()); + string_buf.append(next_var.head.Str().data(), next_var.head.Str().size()); if (!next_var.tail) { break; } @@ -107,16 +129,17 @@ // Look up the variable value auto value = Find(next_var.tail->first); if (!value) { - // TODO(crbug.com/1311111): Create a more structured way of serializing return ParseStatus(ParseStatusCode::kVariableUndefined) .WithData("key", next_var.tail->first.GetName()); } - buffer.buf_.append(value->data(), value->size()); + string_buf.append(value->data(), value->size()); next_var = GetNextVariable(next_var.tail->second); } - return base::StringPiece{buffer.buf_}; + return ResolvedSourceString::Create( + {}, input.Line(), input.Column(), string_buf, + ResolvedSourceStringState{.contains_substitutions = true}); } } // namespace media::hls
diff --git a/media/formats/hls/variable_dictionary.h b/media/formats/hls/variable_dictionary.h index e21e880..e5400de 100644 --- a/media/formats/hls/variable_dictionary.h +++ b/media/formats/hls/variable_dictionary.h
@@ -5,34 +5,36 @@ #ifndef MEDIA_FORMATS_HLS_VARIABLE_DICTIONARY_H_ #define MEDIA_FORMATS_HLS_VARIABLE_DICTIONARY_H_ +#include <list> #include <string> #include "base/strings/string_piece.h" +#include "base/types/pass_key.h" #include "media/base/media_export.h" #include "media/formats/hls/parse_status.h" +#include "media/formats/hls/source_string.h" #include "third_party/abseil-cpp/absl/types/optional.h" namespace media::hls { -struct SourceString; - namespace types { class VariableName; } class MEDIA_EXPORT VariableDictionary { public: - class SubstitutionBuffer { + class MEDIA_EXPORT SubstitutionBuffer { public: friend VariableDictionary; - SubstitutionBuffer() = default; + SubstitutionBuffer(); + ~SubstitutionBuffer(); SubstitutionBuffer(const SubstitutionBuffer&) = delete; SubstitutionBuffer(const SubstitutionBuffer&&) = delete; SubstitutionBuffer& operator=(const SubstitutionBuffer&) = delete; SubstitutionBuffer& operator=(SubstitutionBuffer&&) = delete; private: - std::string buf_; + std::list<std::string> strings_; }; VariableDictionary(); @@ -53,12 +55,13 @@ bool Insert(types::VariableName name, std::string value); // Attempts to resolve all variable references within the given input string - // using this dictionary, returning a `base::StringPiece` with the fully + // using this dictionary, returning a `ResolvedSourceString` with the fully // resolved string, or an error if one occurred. `buffer` will be used to // build the resulting string if any substitutions occur, and the caller must - // ensure that it outlives the `base::StringPiece` returned by this function. - // As an optimization, the buffer will not be used if no substitutions are - // necessary. + // ensure that it outlives the `ResolvedSourceString` returned by this + // function. As an optimization, the buffer will not be used if no + // substitutions are necessary, or if the substitution consisted of the entire + // input string. // // This implementation is based on a somewhat pedantic interpretation of the // spec: @@ -69,11 +72,12 @@ // If a given sequence doesn't exactly match that format then it's ignored, // rather than treated as an error. However, if it does match that format and // the variable name is undefined, it's treated as an error. - ParseStatus::Or<base::StringPiece> Resolve(SourceString input, - SubstitutionBuffer& buffer) const; + ParseStatus::Or<ResolvedSourceString> Resolve( + SourceString input, + SubstitutionBuffer& buffer) const; private: - base::flat_map<std::string, std::string> entries_; + base::flat_map<std::string, std::unique_ptr<std::string>> entries_; }; } // namespace media::hls
diff --git a/media/formats/hls/variable_dictionary_unittest.cc b/media/formats/hls/variable_dictionary_unittest.cc index c64ced4a..745cc7f2 100644 --- a/media/formats/hls/variable_dictionary_unittest.cc +++ b/media/formats/hls/variable_dictionary_unittest.cc
@@ -33,12 +33,17 @@ void OkTest(const VariableDictionary& dict, base::StringPiece in, base::StringPiece expected_out, + bool substitutions_expected, const base::Location& from = base::Location::Current()) { const auto source_str = SourceString::CreateForTesting(in); + EXPECT_FALSE(source_str.ContainsSubstitutions()) << from.ToString(); VariableDictionary::SubstitutionBuffer buffer; auto result = dict.Resolve(source_str, buffer); ASSERT_TRUE(result.has_value()) << from.ToString(); - EXPECT_EQ(std::move(result).value(), expected_out) << from.ToString(); + auto result_str = std::move(result).value(); + EXPECT_EQ(result_str.Str(), expected_out) << from.ToString(); + EXPECT_EQ(result_str.ContainsSubstitutions(), substitutions_expected) + << from.ToString(); } void ErrorTest(const VariableDictionary& dict, @@ -52,14 +57,20 @@ EXPECT_EQ(std::move(result).error(), expected_error) << from.ToString(); } +// Helper for cases where no substitutions should occur +void NopTest(const VariableDictionary& dict, + base::StringPiece in, + const base::Location& from = base::Location::Current()) { + OkTest(dict, in, in, false, from); +} + } // namespace TEST(HlsVariableDictionaryTest, BasicSubstitution) { VariableDictionary dict = CreateBasicDictionary(); OkTest(dict, "The NAME's {$NAME}, {$_0THER-1dent} {$NAME}. Agent {$IDENT}", - "The NAME's bond, {$james} bond. Agent 007"); - OkTest(dict, "This $tring {has} ${no} v{}{}ar}}s", - "This $tring {has} ${no} v{}{}ar}}s"); + "The NAME's bond, {$james} bond. Agent 007", true); + NopTest(dict, "This $tring {has} ${no} v{}{}ar}}s"); } TEST(HlsVariableDictionaryTest, VariableUndefined) { @@ -72,7 +83,7 @@ EXPECT_EQ(dict.Find(CreateVarName("test")), absl::nullopt); ErrorTest(dict, "Hello {$test}", ParseStatusCode::kVariableUndefined); - OkTest(dict, "Hello {$TEST}", "Hello FOO"); + OkTest(dict, "Hello {$TEST}", "Hello FOO", true); ErrorTest(dict, "Hello {$TEST} {$TEST1}", ParseStatusCode::kVariableUndefined); } @@ -109,25 +120,25 @@ auto dict = CreateBasicDictionary(); // Variable refs with invalid variable names are ignored - OkTest(dict, "http://{$}.com", "http://{$}.com"); - OkTest(dict, "http://{$ NAME}.com", "http://{$ NAME}.com"); - OkTest(dict, "http://{$NAME }.com", "http://{$NAME }.com"); - OkTest(dict, "http://{$:NAME}.com", "http://{$:NAME}.com"); + NopTest(dict, "http://{$}.com"); + NopTest(dict, "http://{$ NAME}.com"); + NopTest(dict, "http://{$NAME }.com"); + NopTest(dict, "http://{$:NAME}.com"); // Incomplete variable ref sequences are ignored - OkTest(dict, "http://{$NAME", "http://{$NAME"); - OkTest(dict, "http://{NAME}.com", "http://{NAME}.com"); - OkTest(dict, "http://${NAME}.com", "http://${NAME}.com"); - OkTest(dict, "http://$NAME.com", "http://$NAME.com"); + NopTest(dict, "http://{$NAME"); + NopTest(dict, "http://{NAME}.com"); + NopTest(dict, "http://${NAME}.com"); + NopTest(dict, "http://$NAME.com"); // Valid ref sequences surrounded by invalid ref sequences should *not* be // ignored OkTest(dict, "http://{$}{$ NAME}{$NAME}}{$NAME }.com", - "http://{$}{$ NAME}bond}{$NAME }.com"); + "http://{$}{$ NAME}bond}{$NAME }.com", true); // Valid ref sequences nested within invalid ref sequences should *not* be // ignored - OkTest(dict, "http://{$ {$NAME}}.com", "http://{$ bond}.com"); + OkTest(dict, "http://{$ {$NAME}}.com", "http://{$ bond}.com", true); } TEST(HlsVariableDictionaryTest, ExplosiveVariableDefs) { @@ -137,11 +148,12 @@ EXPECT_TRUE(dict.Insert(CreateVarName("LOL2"), "{$LOL1}{$LOL1}{$LOL1}")); EXPECT_TRUE(dict.Insert(CreateVarName("LOL3"), "{$LOL2}{$LOL2}{$LOL2}")); OkTest(dict, "{$LOL3}{$LOL3}{$LOL3}", - "{$LOL2}{$LOL2}{$LOL2}{$LOL2}{$LOL2}{$LOL2}{$LOL2}{$LOL2}{$LOL2}"); + "{$LOL2}{$LOL2}{$LOL2}{$LOL2}{$LOL2}{$LOL2}{$LOL2}{$LOL2}{$LOL2}", + true); // Variable substitution is by design not cyclical EXPECT_TRUE(dict.Insert(CreateVarName("CYCLE"), "{$CYCLE}")); - OkTest(dict, "{$CYCLE}", "{$CYCLE}"); + OkTest(dict, "{$CYCLE}", "{$CYCLE}", true); } } // namespace media::hls
diff --git a/pdf/pdf_view_web_plugin.cc b/pdf/pdf_view_web_plugin.cc index 72f7103..aec54f4 100644 --- a/pdf/pdf_view_web_plugin.cc +++ b/pdf/pdf_view_web_plugin.cc
@@ -639,6 +639,10 @@ return engine_->CanRedo(); } +bool PdfViewWebPlugin::CanCopy() const { + return engine_->HasPermission(DocumentPermission::kCopy); +} + bool PdfViewWebPlugin::ExecuteEditCommand(const blink::WebString& name, const blink::WebString& value) { if (name == "SelectAll")
diff --git a/pdf/pdf_view_web_plugin.h b/pdf/pdf_view_web_plugin.h index 8c1a697..c546a41 100644 --- a/pdf/pdf_view_web_plugin.h +++ b/pdf/pdf_view_web_plugin.h
@@ -241,6 +241,7 @@ bool HasEditableText() const override; bool CanUndo() const override; bool CanRedo() const override; + bool CanCopy() const override; bool ExecuteEditCommand(const blink::WebString& name, const blink::WebString& value) override; blink::WebURL LinkAtPosition(const gfx::Point& /*position*/) const override;
diff --git a/pdf/pdf_view_web_plugin_unittest.cc b/pdf/pdf_view_web_plugin_unittest.cc index 914ed316..d7f149b 100644 --- a/pdf/pdf_view_web_plugin_unittest.cc +++ b/pdf/pdf_view_web_plugin_unittest.cc
@@ -835,6 +835,7 @@ EXPECT_EQ(kContentRestrictionCopy | kContentRestrictionCut | kContentRestrictionPaste | kContentRestrictionPrint, plugin_->GetContentRestrictions()); + EXPECT_FALSE(plugin_->CanCopy()); } TEST_F(PdfViewWebPluginTest, GetContentRestrictionsWithCopyAllowed) { @@ -845,6 +846,7 @@ EXPECT_EQ(kContentRestrictionCut | kContentRestrictionPaste | kContentRestrictionPrint, plugin_->GetContentRestrictions()); + EXPECT_TRUE(plugin_->CanCopy()); } TEST_F(PdfViewWebPluginTest, GetContentRestrictionsWithPrintLowQualityAllowed) {
diff --git a/pdf/pdfium/pdfium_engine.cc b/pdf/pdfium/pdfium_engine.cc index 96cd371e..e226635 100644 --- a/pdf/pdfium/pdfium_engine.cc +++ b/pdf/pdfium/pdfium_engine.cc
@@ -2168,9 +2168,6 @@ } std::string PDFiumEngine::GetSelectedText() { - if (!HasPermission(DocumentPermission::kCopy)) - return std::string(); - std::u16string result; for (size_t i = 0; i < selection_.size(); ++i) { std::u16string current_selection_text = selection_[i].GetText();
diff --git a/pdf/pdfium/pdfium_engine_unittest.cc b/pdf/pdfium/pdfium_engine_unittest.cc index 1185ccca..d6e93b4 100644 --- a/pdf/pdfium/pdfium_engine_unittest.cc +++ b/pdf/pdfium/pdfium_engine_unittest.cc
@@ -678,23 +678,43 @@ EXPECT_TRUE(engine->HandleInputEvent(raw_key_down_event)); } +namespace { +#if BUILDFLAG(IS_WIN) +constexpr char kSelectTextExpectedText[] = + "Hello, world!\r\nGoodbye, world!\r\nHello, world!\r\nGoodbye, world!"; +#else +constexpr char kSelectTextExpectedText[] = + "Hello, world!\nGoodbye, world!\nHello, world!\nGoodbye, world!"; +#endif +} // namespace + TEST_F(PDFiumEngineTest, SelectText) { NiceMock<MockTestClient> client; std::unique_ptr<PDFiumEngine> engine = InitializeEngine(&client, FILE_PATH_LITERAL("hello_world2.pdf")); ASSERT_TRUE(engine); + EXPECT_TRUE(engine->HasPermission(DocumentPermission::kCopy)); + EXPECT_THAT(engine->GetSelectedText(), IsEmpty()); engine->SelectAll(); -#if BUILDFLAG(IS_WIN) - constexpr char kExpectedText[] = - "Hello, world!\r\nGoodbye, world!\r\nHello, world!\r\nGoodbye, world!"; -#else - constexpr char kExpectedText[] = - "Hello, world!\nGoodbye, world!\nHello, world!\nGoodbye, world!"; -#endif - EXPECT_EQ(kExpectedText, engine->GetSelectedText()); + EXPECT_EQ(kSelectTextExpectedText, engine->GetSelectedText()); +} + +TEST_F(PDFiumEngineTest, SelectTextWithCopyRestriction) { + NiceMock<MockTestClient> client; + std::unique_ptr<PDFiumEngine> engine = InitializeEngine( + &client, FILE_PATH_LITERAL("hello_world2_with_copy_restriction.pdf")); + ASSERT_TRUE(engine); + + EXPECT_FALSE(engine->HasPermission(DocumentPermission::kCopy)); + + // The copy restriction should not affect the text selection hehavior. + EXPECT_THAT(engine->GetSelectedText(), IsEmpty()); + + engine->SelectAll(); + EXPECT_EQ(kSelectTextExpectedText, engine->GetSelectedText()); } TEST_F(PDFiumEngineTest, SelectCroppedText) {
diff --git a/pdf/test/data/hello_world2_with_copy_restriction.pdf b/pdf/test/data/hello_world2_with_copy_restriction.pdf new file mode 100644 index 0000000..090447b --- /dev/null +++ b/pdf/test/data/hello_world2_with_copy_restriction.pdf Binary files differ
diff --git a/services/network/first_party_sets/first_party_sets_context_config.h b/services/network/first_party_sets/first_party_sets_context_config.h index 88c6bee..7adca4c 100644 --- a/services/network/first_party_sets/first_party_sets_context_config.h +++ b/services/network/first_party_sets/first_party_sets_context_config.h
@@ -11,7 +11,6 @@ // info in the given network context. class FirstPartySetsContextConfig { public: - FirstPartySetsContextConfig() = default; explicit FirstPartySetsContextConfig(bool enabled); bool is_enabled() const { return enabled_; }
diff --git a/services/network/first_party_sets/first_party_sets_manager_unittest.cc b/services/network/first_party_sets/first_party_sets_manager_unittest.cc index 93a9295..9fde2d4 100644 --- a/services/network/first_party_sets/first_party_sets_manager_unittest.cc +++ b/services/network/first_party_sets/first_party_sets_manager_unittest.cc
@@ -37,7 +37,8 @@ class FirstPartySetsManagerTest : public ::testing::Test { public: - explicit FirstPartySetsManagerTest(bool enabled) : manager_(enabled) {} + explicit FirstPartySetsManagerTest(bool enabled, bool context_enabled) + : manager_(enabled), fps_context_config_(context_enabled) {} void SetCompleteSets( const base::flat_map<net::SchemefulSite, net::SchemefulSite>& content) { @@ -99,9 +100,8 @@ class FirstPartySetsManagerDisabledTest : public FirstPartySetsManagerTest { public: - FirstPartySetsManagerDisabledTest() : FirstPartySetsManagerTest(false) { - // FPS setting by the browser overrules FPS setting by the context. - SetFirstPartySetsContextConfig(true); + FirstPartySetsManagerDisabledTest() + : FirstPartySetsManagerTest(/*enabled=*/false, /*context_enabled=*/true) { } }; @@ -176,9 +176,8 @@ class FirstPartySetsEnabledTest : public FirstPartySetsManagerTest { public: - FirstPartySetsEnabledTest() : FirstPartySetsManagerTest(true) { - SetFirstPartySetsContextConfig(true); - } + FirstPartySetsEnabledTest() + : FirstPartySetsManagerTest(/*enabled=*/true, /*context_enabled=*/true) {} }; TEST_F(FirstPartySetsEnabledTest, Sets_IsEmpty) {
diff --git a/testing/buildbot/chromium.chromiumos.json b/testing/buildbot/chromium.chromiumos.json index 6d3d15d..a7573a7 100644 --- a/testing/buildbot/chromium.chromiumos.json +++ b/testing/buildbot/chromium.chromiumos.json
@@ -5688,21 +5688,21 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5145.0/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5146.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 105.0.5145.0", + "name": "interactive_ui_tests Lacros version skew testing ash 105.0.5146.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_v105.0.5145.0", - "revision": "version:105.0.5145.0" + "location": "lacros_version_skew_tests_v105.0.5146.0", + "revision": "version:105.0.5146.0" } ], "dimension_sets": [ @@ -5715,7 +5715,7 @@ }, "test": "interactive_ui_tests", "test_id_prefix": "ninja://chrome/test:interactive_ui_tests/", - "variant_id": "Lacros version skew testing ash 105.0.5145.0" + "variant_id": "Lacros version skew testing ash 105.0.5146.0" }, { "isolate_profile_data": true, @@ -5853,21 +5853,21 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5145.0/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5146.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 105.0.5145.0", + "name": "lacros_chrome_browsertests Lacros version skew testing ash 105.0.5146.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_v105.0.5145.0", - "revision": "version:105.0.5145.0" + "location": "lacros_version_skew_tests_v105.0.5146.0", + "revision": "version:105.0.5146.0" } ], "dimension_sets": [ @@ -5879,7 +5879,7 @@ }, "test": "lacros_chrome_browsertests", "test_id_prefix": "ninja://chrome/test:lacros_chrome_browsertests/", - "variant_id": "Lacros version skew testing ash 105.0.5145.0" + "variant_id": "Lacros version skew testing ash 105.0.5146.0" }, { "args": [ @@ -5999,21 +5999,21 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5145.0/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5146.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 105.0.5145.0", + "name": "lacros_chrome_browsertests_run_in_series Lacros version skew testing ash 105.0.5146.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_v105.0.5145.0", - "revision": "version:105.0.5145.0" + "location": "lacros_version_skew_tests_v105.0.5146.0", + "revision": "version:105.0.5146.0" } ], "dimension_sets": [ @@ -6025,7 +6025,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 105.0.5145.0" + "variant_id": "Lacros version skew testing ash 105.0.5146.0" }, { "isolate_profile_data": true,
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json index 2f0dae19..bcb5322 100644 --- a/testing/buildbot/chromium.fyi.json +++ b/testing/buildbot/chromium.fyi.json
@@ -92911,21 +92911,21 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5145.0/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5146.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 105.0.5145.0", + "name": "interactive_ui_tests Lacros version skew testing ash 105.0.5146.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_v105.0.5145.0", - "revision": "version:105.0.5145.0" + "location": "lacros_version_skew_tests_v105.0.5146.0", + "revision": "version:105.0.5146.0" } ], "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", @@ -92933,7 +92933,7 @@ }, "test": "interactive_ui_tests", "test_id_prefix": "ninja://chrome/test:interactive_ui_tests/", - "variant_id": "Lacros version skew testing ash 105.0.5145.0" + "variant_id": "Lacros version skew testing ash 105.0.5146.0" }, { "isolate_profile_data": true, @@ -93046,28 +93046,28 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5145.0/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5146.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 105.0.5145.0", + "name": "lacros_chrome_browsertests Lacros version skew testing ash 105.0.5146.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_v105.0.5145.0", - "revision": "version:105.0.5145.0" + "location": "lacros_version_skew_tests_v105.0.5146.0", + "revision": "version:105.0.5146.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 105.0.5145.0" + "variant_id": "Lacros version skew testing ash 105.0.5146.0" }, { "args": [ @@ -93167,28 +93167,28 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5145.0/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5146.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 105.0.5145.0", + "name": "lacros_chrome_browsertests_run_in_series Lacros version skew testing ash 105.0.5146.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_v105.0.5145.0", - "revision": "version:105.0.5145.0" + "location": "lacros_version_skew_tests_v105.0.5146.0", + "revision": "version:105.0.5146.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 105.0.5145.0" + "variant_id": "Lacros version skew testing ash 105.0.5146.0" }, { "isolate_profile_data": true, @@ -94526,20 +94526,20 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5145.0/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5146.0/test_ash_chrome" ], "merge": { "args": [], "script": "//testing/merge_scripts/standard_gtest_merge.py" }, - "name": "interactive_ui_tests Lacros version skew testing ash 105.0.5145.0", + "name": "interactive_ui_tests Lacros version skew testing ash 105.0.5146.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_v105.0.5145.0", - "revision": "version:105.0.5145.0" + "location": "lacros_version_skew_tests_v105.0.5146.0", + "revision": "version:105.0.5146.0" } ], "dimension_sets": [ @@ -94553,7 +94553,7 @@ }, "test": "interactive_ui_tests", "test_id_prefix": "ninja://chrome/test:interactive_ui_tests/", - "variant_id": "Lacros version skew testing ash 105.0.5145.0" + "variant_id": "Lacros version skew testing ash 105.0.5146.0" }, { "merge": { @@ -94691,20 +94691,20 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5145.0/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5146.0/test_ash_chrome" ], "merge": { "args": [], "script": "//testing/merge_scripts/standard_gtest_merge.py" }, - "name": "lacros_chrome_browsertests Lacros version skew testing ash 105.0.5145.0", + "name": "lacros_chrome_browsertests Lacros version skew testing ash 105.0.5146.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_v105.0.5145.0", - "revision": "version:105.0.5145.0" + "location": "lacros_version_skew_tests_v105.0.5146.0", + "revision": "version:105.0.5146.0" } ], "dimension_sets": [ @@ -94717,7 +94717,7 @@ }, "test": "lacros_chrome_browsertests", "test_id_prefix": "ninja://chrome/test:lacros_chrome_browsertests/", - "variant_id": "Lacros version skew testing ash 105.0.5145.0" + "variant_id": "Lacros version skew testing ash 105.0.5146.0" }, { "args": [ @@ -94837,20 +94837,20 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5145.0/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5146.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 105.0.5145.0", + "name": "lacros_chrome_browsertests_run_in_series Lacros version skew testing ash 105.0.5146.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_v105.0.5145.0", - "revision": "version:105.0.5145.0" + "location": "lacros_version_skew_tests_v105.0.5146.0", + "revision": "version:105.0.5146.0" } ], "dimension_sets": [ @@ -94863,7 +94863,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 105.0.5145.0" + "variant_id": "Lacros version skew testing ash 105.0.5146.0" }, { "merge": { @@ -96359,20 +96359,20 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5145.0/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5146.0/test_ash_chrome" ], "merge": { "args": [], "script": "//testing/merge_scripts/standard_gtest_merge.py" }, - "name": "interactive_ui_tests Lacros version skew testing ash 105.0.5145.0", + "name": "interactive_ui_tests Lacros version skew testing ash 105.0.5146.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_v105.0.5145.0", - "revision": "version:105.0.5145.0" + "location": "lacros_version_skew_tests_v105.0.5146.0", + "revision": "version:105.0.5146.0" } ], "dimension_sets": [ @@ -96386,7 +96386,7 @@ }, "test": "interactive_ui_tests", "test_id_prefix": "ninja://chrome/test:interactive_ui_tests/", - "variant_id": "Lacros version skew testing ash 105.0.5145.0" + "variant_id": "Lacros version skew testing ash 105.0.5146.0" }, { "merge": { @@ -96524,20 +96524,20 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5145.0/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5146.0/test_ash_chrome" ], "merge": { "args": [], "script": "//testing/merge_scripts/standard_gtest_merge.py" }, - "name": "lacros_chrome_browsertests Lacros version skew testing ash 105.0.5145.0", + "name": "lacros_chrome_browsertests Lacros version skew testing ash 105.0.5146.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_v105.0.5145.0", - "revision": "version:105.0.5145.0" + "location": "lacros_version_skew_tests_v105.0.5146.0", + "revision": "version:105.0.5146.0" } ], "dimension_sets": [ @@ -96550,7 +96550,7 @@ }, "test": "lacros_chrome_browsertests", "test_id_prefix": "ninja://chrome/test:lacros_chrome_browsertests/", - "variant_id": "Lacros version skew testing ash 105.0.5145.0" + "variant_id": "Lacros version skew testing ash 105.0.5146.0" }, { "args": [ @@ -96670,20 +96670,20 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5145.0/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5146.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 105.0.5145.0", + "name": "lacros_chrome_browsertests_run_in_series Lacros version skew testing ash 105.0.5146.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_v105.0.5145.0", - "revision": "version:105.0.5145.0" + "location": "lacros_version_skew_tests_v105.0.5146.0", + "revision": "version:105.0.5146.0" } ], "dimension_sets": [ @@ -96696,7 +96696,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 105.0.5145.0" + "variant_id": "Lacros version skew testing ash 105.0.5146.0" }, { "merge": { @@ -97431,20 +97431,20 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5145.0/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5146.0/test_ash_chrome" ], "merge": { "args": [], "script": "//testing/merge_scripts/standard_gtest_merge.py" }, - "name": "interactive_ui_tests Lacros version skew testing ash 105.0.5145.0", + "name": "interactive_ui_tests Lacros version skew testing ash 105.0.5146.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_v105.0.5145.0", - "revision": "version:105.0.5145.0" + "location": "lacros_version_skew_tests_v105.0.5146.0", + "revision": "version:105.0.5146.0" } ], "dimension_sets": [ @@ -97457,7 +97457,7 @@ }, "test": "interactive_ui_tests", "test_id_prefix": "ninja://chrome/test:interactive_ui_tests/", - "variant_id": "Lacros version skew testing ash 105.0.5145.0" + "variant_id": "Lacros version skew testing ash 105.0.5146.0" } ] },
diff --git a/testing/buildbot/chromium.memory.json b/testing/buildbot/chromium.memory.json index 091f30e..e42187f 100644 --- a/testing/buildbot/chromium.memory.json +++ b/testing/buildbot/chromium.memory.json
@@ -19085,21 +19085,21 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5145.0/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5146.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 105.0.5145.0", + "name": "interactive_ui_tests Lacros version skew testing ash 105.0.5146.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_v105.0.5145.0", - "revision": "version:105.0.5145.0" + "location": "lacros_version_skew_tests_v105.0.5146.0", + "revision": "version:105.0.5146.0" } ], "dimension_sets": [ @@ -19112,7 +19112,7 @@ }, "test": "interactive_ui_tests", "test_id_prefix": "ninja://chrome/test:interactive_ui_tests/", - "variant_id": "Lacros version skew testing ash 105.0.5145.0" + "variant_id": "Lacros version skew testing ash 105.0.5146.0" }, { "isolate_profile_data": true, @@ -19250,21 +19250,21 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5145.0/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5146.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 105.0.5145.0", + "name": "lacros_chrome_browsertests Lacros version skew testing ash 105.0.5146.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_v105.0.5145.0", - "revision": "version:105.0.5145.0" + "location": "lacros_version_skew_tests_v105.0.5146.0", + "revision": "version:105.0.5146.0" } ], "dimension_sets": [ @@ -19276,7 +19276,7 @@ }, "test": "lacros_chrome_browsertests", "test_id_prefix": "ninja://chrome/test:lacros_chrome_browsertests/", - "variant_id": "Lacros version skew testing ash 105.0.5145.0" + "variant_id": "Lacros version skew testing ash 105.0.5146.0" }, { "args": [ @@ -19396,21 +19396,21 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5145.0/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5146.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 105.0.5145.0", + "name": "lacros_chrome_browsertests_run_in_series Lacros version skew testing ash 105.0.5146.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_v105.0.5145.0", - "revision": "version:105.0.5145.0" + "location": "lacros_version_skew_tests_v105.0.5146.0", + "revision": "version:105.0.5146.0" } ], "dimension_sets": [ @@ -19422,7 +19422,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 105.0.5145.0" + "variant_id": "Lacros version skew testing ash 105.0.5146.0" }, { "isolate_profile_data": true,
diff --git a/testing/buildbot/internal.chrome.fyi.json b/testing/buildbot/internal.chrome.fyi.json index ac37a34..7c0acf12 100644 --- a/testing/buildbot/internal.chrome.fyi.json +++ b/testing/buildbot/internal.chrome.fyi.json
@@ -1,18 +1,23 @@ { "AAAAA1 AUTOGENERATED FILE DO NOT EDIT": {}, "AAAAA2 See generate_buildbot_json.py to make changes": {}, - "win-chrome-finch-fyi": { - "additional_compile_targets": [ - "chrome" - ], + "linux-finch-smoke-chrome": { "isolated_scripts": [ { + "args": [ + "--git-revision=${got_revision}" + ], "isolate_name": "variations_smoke_tests", "merge": { "args": [], "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, "name": "variations_smoke_tests", + "precommit_args": [ + "--gerrit-issue=${patch_issue}", + "--gerrit-patchset=${patch_set}", + "--buildbucket-id=${buildbucket_build_id}" + ], "resultdb": { "enable": true, "result_format": "single" @@ -21,8 +26,79 @@ "can_use_on_swarming_builders": true, "dimension_sets": [ { - "os": "Windows-10-15063", - "pool": "chrome.tests" + "os": "Ubuntu-18.04", + "pool": "chrome.tests.finch" + } + ], + "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://chrome/test:variations_smoke_tests/" + } + ] + }, + "mac-finch-smoke-chrome": { + "isolated_scripts": [ + { + "args": [ + "--git-revision=${got_revision}" + ], + "isolate_name": "variations_smoke_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "variations_smoke_tests", + "precommit_args": [ + "--gerrit-issue=${patch_issue}", + "--gerrit-patchset=${patch_set}", + "--buildbucket-id=${buildbucket_build_id}" + ], + "resultdb": { + "enable": true, + "result_format": "single" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "arm64", + "os": "Mac-11|Mac-12", + "pool": "chrome.tests.finch" + } + ], + "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://chrome/test:variations_smoke_tests/" + } + ] + }, + "win-finch-smoke-chrome": { + "isolated_scripts": [ + { + "args": [ + "--git-revision=${got_revision}" + ], + "isolate_name": "variations_smoke_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "variations_smoke_tests", + "precommit_args": [ + "--gerrit-issue=${patch_issue}", + "--gerrit-patchset=${patch_set}", + "--buildbucket-id=${buildbucket_build_id}" + ], + "resultdb": { + "enable": true, + "result_format": "single" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "os": "Windows-10-19042", + "pool": "chrome.tests.finch" } ], "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com"
diff --git a/testing/buildbot/mixins.pyl b/testing/buildbot/mixins.pyl index 2ce7376..300b2747 100644 --- a/testing/buildbot/mixins.pyl +++ b/testing/buildbot/mixins.pyl
@@ -224,6 +224,13 @@ }, }, }, + 'chrome-finch-swarming-pool': { + 'swarming': { + 'dimensions': { + 'pool': 'chrome.tests.finch', + }, + }, + }, 'chrome-swarming-pool': { 'swarming': { 'dimensions': {
diff --git a/testing/buildbot/test_suites.pyl b/testing/buildbot/test_suites.pyl index dd53e64..3face2c 100644 --- a/testing/buildbot/test_suites.pyl +++ b/testing/buildbot/test_suites.pyl
@@ -495,6 +495,19 @@ }, }, + 'chrome_finch_smoke_tests': { + 'variations_smoke_tests': { + 'isolate_name': 'variations_smoke_tests', + 'mixins': [ + 'skia_gold_test', + ], + 'resultdb': { + 'enable': True, + 'result_format': 'single' + }, + }, + }, + 'chrome_isolated_script_tests': { 'chrome_sizes': { 'merge': {
diff --git a/testing/buildbot/variants.pyl b/testing/buildbot/variants.pyl index 80d294d..6396d317c 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_v105.0.5145.0/test_ash_chrome', + '--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5146.0/test_ash_chrome', ], - 'identifier': 'Lacros version skew testing ash 105.0.5145.0', + 'identifier': 'Lacros version skew testing ash 105.0.5146.0', 'swarming': { 'cipd_packages': [ { 'cipd_package': 'chromium/testing/linux-ash-chromium/x86_64/ash.zip', - 'location': 'lacros_version_skew_tests_v105.0.5145.0', - 'revision': 'version:105.0.5145.0', + 'location': 'lacros_version_skew_tests_v105.0.5146.0', + 'revision': 'version:105.0.5146.0', }, ], },
diff --git a/testing/buildbot/waterfalls.pyl b/testing/buildbot/waterfalls.pyl index 54d5a11..fec90d9 100644 --- a/testing/buildbot/waterfalls.pyl +++ b/testing/buildbot/waterfalls.pyl
@@ -6401,6 +6401,41 @@ { 'project': 'chrome', 'bucket': 'ci', + 'name': 'internal.chrome.fyi', + 'mixins': ['chrome-tester-service-account'], + 'machines': { + 'linux-finch-smoke-chrome': { + 'mixins': [ + 'chrome-finch-swarming-pool', + 'linux-bionic', + ], + 'test_suites': { + 'isolated_scripts': 'chrome_finch_smoke_tests', + }, + }, + 'mac-finch-smoke-chrome': { + 'mixins': [ + 'chrome-finch-swarming-pool', + 'mac_11_or_12_arm64', + ], + 'test_suites': { + 'isolated_scripts': 'chrome_finch_smoke_tests', + }, + }, + 'win-finch-smoke-chrome': { + 'mixins': [ + 'chrome-finch-swarming-pool', + 'win10', + ], + 'test_suites': { + 'isolated_scripts': 'chrome_finch_smoke_tests', + }, + }, + }, + }, + { + 'project': 'chrome', + 'bucket': 'ci', 'name': 'internal.chromeos.fyi', 'mixins': ['chrome-tester-service-account'], 'machines': {
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json index 1e26506..703da39 100644 --- a/testing/variations/fieldtrial_testing_config.json +++ b/testing/variations/fieldtrial_testing_config.json
@@ -1096,6 +1096,28 @@ ] } ], + "AutofillIgnoreEarlyClicksOnPopup": [ + { + "platforms": [ + "chromeos", + "chromeos_lacros", + "linux", + "mac", + "windows" + ], + "experiments": [ + { + "name": "Enabled_500ms", + "params": { + "duration": "500ms" + }, + "enable_features": [ + "AutofillIgnoreEarlyClicksOnPopup" + ] + } + ] + } + ], "AutofillKeyboardAccessory": [ { "platforms": [ @@ -1213,6 +1235,27 @@ ] } ], + "AutofillProfileImportFromUnfocusableFields": [ + { + "platforms": [ + "android", + "chromeos", + "chromeos_lacros", + "ios", + "linux", + "mac", + "windows" + ], + "experiments": [ + { + "name": "Enabled", + "enable_features": [ + "AutofillProfileImportFromUnfocusableFields" + ] + } + ] + } + ], "AutofillRationalizeStreetAddressAndAddressLine": [ { "platforms": [
diff --git a/third_party/blink/public/web/web_plugin.h b/third_party/blink/public/web/web_plugin.h index 9d8d55d..9773e8d 100644 --- a/third_party/blink/public/web/web_plugin.h +++ b/third_party/blink/public/web/web_plugin.h
@@ -167,6 +167,7 @@ virtual bool CanUndo() const { return false; } virtual bool CanRedo() const { return false; } + virtual bool CanCopy() const { return true; } virtual bool ExecuteEditCommand(const WebString& name, const WebString& value) {
diff --git a/third_party/blink/renderer/bindings/generated_in_core.gni b/third_party/blink/renderer/bindings/generated_in_core.gni index 8dc00a6..8c0a232 100644 --- a/third_party/blink/renderer/bindings/generated_in_core.gni +++ b/third_party/blink/renderer/bindings/generated_in_core.gni
@@ -335,8 +335,6 @@ "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_scroll_options.h", "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_scroll_state_init.cc", "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_scroll_state_init.h", - "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_scroll_timeline_element_based_offset.cc", - "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_scroll_timeline_element_based_offset.h", "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_scroll_timeline_options.cc", "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_scroll_timeline_options.h", "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_scroll_to_options.cc", @@ -416,8 +414,6 @@ "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_directive_type.h", "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_document_ready_state.cc", "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_document_ready_state.h", - "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_edge.cc", - "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_edge.h", "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_edit_context_enter_key_hint.cc", "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_edit_context_enter_key_hint.h", "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_edit_context_input_mode.cc", @@ -1594,8 +1590,6 @@ "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_union_bytestringbytestringrecord_bytestringsequencesequence.h", "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_union_compositeoperationorauto_compositeoperationorautosequence.cc", "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_union_compositeoperationorauto_compositeoperationorautosequence.h", - "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_union_csskeywordvalue_cssnumericvalue_scrolltimelineelementbasedoffset_string.cc", - "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_union_csskeywordvalue_cssnumericvalue_scrolltimelineelementbasedoffset_string.h", "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_union_csscolorvalue_cssstylevalue.cc", "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_union_csscolorvalue_cssstylevalue.h", "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_union_csskeywordvalue_cssnumericvalue_string.cc",
diff --git a/third_party/blink/renderer/bindings/idl_in_core.gni b/third_party/blink/renderer/bindings/idl_in_core.gni index b0172da..387be90 100644 --- a/third_party/blink/renderer/bindings/idl_in_core.gni +++ b/third_party/blink/renderer/bindings/idl_in_core.gni
@@ -28,7 +28,6 @@ "//third_party/blink/renderer/core/animation/optional_effect_timing.idl", "//third_party/blink/renderer/core/animation/scroll_direction.idl", "//third_party/blink/renderer/core/animation/scroll_timeline.idl", - "//third_party/blink/renderer/core/animation/scroll_timeline_element_based_offset.idl", "//third_party/blink/renderer/core/animation/scroll_timeline_options.idl", "//third_party/blink/renderer/core/animation/view_timeline.idl", "//third_party/blink/renderer/core/animation/view_timeline_options.idl",
diff --git a/third_party/blink/renderer/core/animation/BUILD.gn b/third_party/blink/renderer/core/animation/BUILD.gn index 02fbe1789..3ebc157e 100644 --- a/third_party/blink/renderer/core/animation/BUILD.gn +++ b/third_party/blink/renderer/core/animation/BUILD.gn
@@ -230,8 +230,6 @@ "sampled_effect.h", "scroll_timeline.cc", "scroll_timeline.h", - "scroll_timeline_offset.cc", - "scroll_timeline_offset.h", "scroll_timeline_util.cc", "scroll_timeline_util.h", "side_index.h",
diff --git a/third_party/blink/renderer/core/animation/animation.cc b/third_party/blink/renderer/core/animation/animation.cc index 67ca3b9..f0f97c5 100644 --- a/third_party/blink/renderer/core/animation/animation.cc +++ b/third_party/blink/renderer/core/animation/animation.cc
@@ -457,7 +457,7 @@ // Synchronously resolve pending pause task. if (pending_pause_) { - SetHoldTimeAndPhase(new_current_time, TimelinePhase::kActive); + hold_time_ = new_current_time; ApplyPendingPlaybackRate(); start_time_ = absl::nullopt; pending_pause_ = false; @@ -480,12 +480,11 @@ void Animation::SetCurrentTimeInternal(AnimationTimeDelta new_current_time) { absl::optional<AnimationTimeDelta> previous_start_time = start_time_; absl::optional<AnimationTimeDelta> previous_hold_time = hold_time_; - absl::optional<TimelinePhase> previous_hold_phase = hold_phase_; // Update either the hold time or the start time. if (hold_time_ || !start_time_ || !timeline_ || !timeline_->IsActive() || playback_rate_ == 0) { - SetHoldTimeAndPhase(new_current_time, TimelinePhase::kActive); + hold_time_ = new_current_time; } else { start_time_ = CalculateStartTime(new_current_time); } @@ -499,23 +498,10 @@ // Reset the previous current time. previous_current_time_ = absl::nullopt; - if (previous_start_time != start_time_ || previous_hold_time != hold_time_ || - previous_hold_phase != hold_phase_) + if (previous_start_time != start_time_ || previous_hold_time != hold_time_) SetOutdated(); } -void Animation::SetHoldTimeAndPhase( - absl::optional<AnimationTimeDelta> new_hold_time, - TimelinePhase new_hold_phase) { - hold_time_ = new_hold_time; - hold_phase_ = new_hold_phase; -} - -void Animation::ResetHoldTimeAndPhase() { - hold_time_ = absl::nullopt; - hold_phase_ = absl::nullopt; -} - V8CSSNumberish* Animation::startTime() const { if (start_time_) { return ConvertTimeToCSSNumberish(start_time_.value()); @@ -564,22 +550,10 @@ return ConvertTimeToCSSNumberish(calculated_current_time); } -bool Animation::ValidateHoldTimeAndPhase() const { - return hold_phase_ || - ((!hold_phase_ || hold_phase_ == TimelinePhase::kInactive) && - !hold_time_); -} - absl::optional<AnimationTimeDelta> Animation::CurrentTimeInternal() const { - DCHECK(ValidateHoldTimeAndPhase()); return hold_time_ ? hold_time_ : CalculateCurrentTime(); } -TimelinePhase Animation::CurrentPhaseInternal() const { - DCHECK(ValidateHoldTimeAndPhase()); - return hold_phase_ ? hold_phase_.value() : CalculateCurrentPhase(); -} - absl::optional<AnimationTimeDelta> Animation::UnlimitedCurrentTime() const { return CalculateAnimationPlayState() == kPaused || !start_time_ ? CurrentTimeInternal() @@ -826,7 +800,7 @@ start_time_ = ready_time; } else { start_time_ = ready_time - hold_time_.value() / playback_rate_; - ResetHoldTimeAndPhase(); + hold_time_ = absl::nullopt; } } else if (start_time_ && pending_playback_rate_) { // B: If animation’s start time is resolved and animation has a pending @@ -845,7 +819,7 @@ (ready_time - start_time_.value()) * playback_rate_; ApplyPendingPlaybackRate(); if (playback_rate_ == 0) { - SetHoldTimeAndPhase(current_time_to_match, CalculateCurrentPhase()); + hold_time_ = current_time_to_match; start_time_ = ready_time; } else { start_time_ = ready_time - current_time_to_match / playback_rate_; @@ -877,8 +851,7 @@ // let animation’s hold time be the result of evaluating // (ready time - start time) × playback rate. if (start_time_ && !hold_time_) { - SetHoldTimeAndPhase((ready_time - start_time_.value()) * playback_rate_, - CalculateCurrentPhase()); + hold_time_ = (ready_time - start_time_.value()) * playback_rate_; } // 3. Apply any pending playback rate on animation. @@ -983,8 +956,7 @@ if (old_current_time) { reset_current_time_on_resume_ = true; start_time_ = absl::nullopt; - SetHoldTimeAndPhase(progress * EffectEnd(), - TimelinePhase::kInactive); + hold_time_ = progress * EffectEnd(); } else if (PendingInternal()) { start_time_ = boundary_time; } @@ -1004,7 +976,7 @@ // animation is not “sticky” but is re-evaluated based on its updated // current time. if (start_time_) - ResetHoldTimeAndPhase(); + hold_time_ = absl::nullopt; // 5. Run the procedure to update an animation’s finished state for animation // with the did seek flag set to false, and the synchronously notify flag @@ -1048,12 +1020,6 @@ return (timeline_time.value() - start_time_.value()) * playback_rate_; } -TimelinePhase Animation::CalculateCurrentPhase() const { - if (!start_time_ || !timeline_) - return TimelinePhase::kInactive; - return timeline_->Phase(); -} - // https://www.w3.org/TR/web-animations-1/#setting-the-start-time-of-an-animation void Animation::setStartTime(const V8CSSNumberish* start_time, ExceptionState& exception_state) { @@ -1080,13 +1046,12 @@ // is only possible to set either the start time or the animation’s current // time. if (!timeline_time && new_start_time) { - ResetHoldTimeAndPhase(); + hold_time_ = absl::nullopt; } // 3. Let previous current time be animation’s current time. absl::optional<AnimationTimeDelta> previous_current_time = CurrentTimeInternal(); - TimelinePhase previous_current_phase = CurrentPhaseInternal(); // 4. Apply any pending playback rate on animation. ApplyPendingPlaybackRate(); @@ -1114,10 +1079,10 @@ // current time is unresolved. if (start_time_) { if (playback_rate_ != 0) { - ResetHoldTimeAndPhase(); + hold_time_ = absl::nullopt; } } else { - SetHoldTimeAndPhase(previous_current_time, previous_current_phase); + hold_time_ = previous_current_time; } // 7. If animation has a pending play task or a pending pause task, cancel @@ -1389,7 +1354,7 @@ if (has_finite_timeline) { start_time_ = seek_time; } else { - SetHoldTimeAndPhase(seek_time, TimelinePhase::kActive); + hold_time_ = seek_time; } } @@ -1415,7 +1380,7 @@ SetCompositorPending(false); // 11. Run the procedure to update an animation’s finished state for animation - // with the did seek flag set to false (continuous) , and thesynchronously + // with the did seek flag set to false (continuous), and synchronously // notify flag set to false. UpdateFinishedState(UpdateType::kContinuous, NotificationType::kAsync); @@ -1519,10 +1484,10 @@ if (seek_time) { if (has_finite_timeline) { start_time_ = seek_time; - ResetHoldTimeAndPhase(); + hold_time_ = absl::nullopt; ApplyPendingPlaybackRate(); } else { - SetHoldTimeAndPhase(seek_time, TimelinePhase::kActive); + hold_time_ = seek_time; } } @@ -1631,7 +1596,7 @@ start_time_ = CalculateStartTime(new_current_time); if (pending_pause_ && start_time_) { - ResetHoldTimeAndPhase(); + hold_time_ = absl::nullopt; pending_pause_ = false; if (ready_promise_) ResolvePromiseMaybeAsync(ready_promise_.Get()); @@ -1671,7 +1636,6 @@ // value. double playback_rate = EffectivePlaybackRate(); absl::optional<AnimationTimeDelta> hold_time; - TimelinePhase hold_phase; if (playback_rate > 0 && GreaterThanOrEqualWithinTimeTolerance( @@ -1685,9 +1649,7 @@ hold_time = EffectEnd(); } } - hold_phase = did_seek ? TimelinePhase::kActive : CalculateCurrentPhase(); - - SetHoldTimeAndPhase(hold_time, hold_phase); + hold_time_ = hold_time; } else if (playback_rate < 0 && unconstrained_current_time.value() <= AnimationTimeDelta()) { if (did_seek) { @@ -1699,7 +1661,6 @@ hold_time = AnimationTimeDelta(); } } - hold_phase = did_seek ? TimelinePhase::kActive : CalculateCurrentPhase(); // Hack for resolving precision issue at zero. if (hold_time.has_value() && @@ -1707,12 +1668,12 @@ hold_time = AnimationTimeDelta(); } - SetHoldTimeAndPhase(hold_time, hold_phase); + hold_time_ = hold_time; } else if (playback_rate != 0) { // Update start time and reset hold time. if (did_seek && hold_time_) start_time_ = CalculateStartTime(hold_time_.value()); - ResetHoldTimeAndPhase(); + hold_time_ = absl::nullopt; } } @@ -2308,20 +2269,16 @@ if (content_) { absl::optional<AnimationTimeDelta> inherited_time; - TimelinePhase inherited_phase = TimelinePhase::kInactive; if (!idle) { inherited_time = CurrentTimeInternal(); // Special case for end-exclusivity when playing backwards. if (inherited_time == AnimationTimeDelta() && EffectivePlaybackRate() < 0) inherited_time = ANIMATION_TIME_DELTA_FROM_SECONDS(-1); - - inherited_phase = CurrentPhaseInternal(); } - content_->UpdateInheritedTime(inherited_time, inherited_phase, - AtScrollTimelineBoundary(), playback_rate_, - reason); + content_->UpdateInheritedTime(inherited_time, AtScrollTimelineBoundary(), + playback_rate_, reason); // After updating the animation time if the animation is no longer current // blink will no longer composite the element (see @@ -2427,7 +2384,7 @@ pending_pause_ = pending_play_ = false; } - ResetHoldTimeAndPhase(); + hold_time_ = absl::nullopt; start_time_ = absl::nullopt; // Apply changes synchronously. @@ -2548,7 +2505,7 @@ is_paused_for_testing_ = true; pending_pause_ = false; pending_play_ = false; - SetHoldTimeAndPhase(pause_time, TimelinePhase::kActive); + hold_time_ = pause_time; start_time_ = absl::nullopt; UpdateCompositedPaintStatus(); }
diff --git a/third_party/blink/renderer/core/animation/animation.h b/third_party/blink/renderer/core/animation/animation.h index 78c0ce7f..e0519e4 100644 --- a/third_party/blink/renderer/core/animation/animation.h +++ b/third_party/blink/renderer/core/animation/animation.h
@@ -320,7 +320,6 @@ DispatchEventResult DispatchEventInternal(Event&) override; void AddedEventListener(const AtomicString& event_type, RegisteredEventListener&) override; - TimelinePhase CurrentPhaseInternal() const; virtual AnimationEffect::EventDelegate* CreateEventDelegate( Element* target, const AnimationEffect::EventDelegate* old_event_delegate) { @@ -328,11 +327,6 @@ } private: - void SetHoldTimeAndPhase(absl::optional<AnimationTimeDelta> new_hold_time, - TimelinePhase new_hold_phase); - void ResetHoldTimeAndPhase(); - bool ValidateHoldTimeAndPhase() const; - void ClearOutdated(); void ForceServiceOnNextFrame(); @@ -348,7 +342,6 @@ absl::optional<AnimationTimeDelta> CalculateStartTime( AnimationTimeDelta current_time) const; absl::optional<AnimationTimeDelta> CalculateCurrentTime() const; - TimelinePhase CalculateCurrentPhase() const; V8CSSNumberish* ConvertTimeToCSSNumberish( absl::optional<AnimationTimeDelta>) const; @@ -432,7 +425,6 @@ absl::optional<double> pending_playback_rate_; absl::optional<AnimationTimeDelta> start_time_; absl::optional<AnimationTimeDelta> hold_time_; - absl::optional<TimelinePhase> hold_phase_; absl::optional<AnimationTimeDelta> previous_current_time_; bool reset_current_time_on_resume_ = false;
diff --git a/third_party/blink/renderer/core/animation/animation_effect.cc b/third_party/blink/renderer/core/animation/animation_effect.cc index 02328ae..3dd2d17a 100644 --- a/third_party/blink/renderer/core/animation/animation_effect.cc +++ b/third_party/blink/renderer/core/animation/animation_effect.cc
@@ -255,31 +255,8 @@ InvalidateAndNotifyOwner(); } -absl::optional<Timing::Phase> TimelinePhaseToTimingPhase( - absl::optional<TimelinePhase> phase) { - absl::optional<Timing::Phase> result; - if (phase) { - switch (phase.value()) { - case TimelinePhase::kBefore: - result = Timing::Phase::kPhaseBefore; - break; - case TimelinePhase::kActive: - result = Timing::Phase::kPhaseActive; - break; - case TimelinePhase::kAfter: - result = Timing::Phase::kPhaseAfter; - break; - case TimelinePhase::kInactive: - // Timing::Phase does not have an inactive phase. - break; - } - } - return result; -} - void AnimationEffect::UpdateInheritedTime( absl::optional<AnimationTimeDelta> inherited_time, - absl::optional<TimelinePhase> inherited_timeline_phase, bool at_progress_timeline_boundary, double inherited_playback_rate, TimingUpdateReason reason) const { @@ -287,21 +264,15 @@ (inherited_playback_rate < 0) ? Timing::AnimationDirection::kBackwards : Timing::AnimationDirection::kForwards; - absl::optional<Timing::Phase> timeline_phase = - TimelinePhaseToTimingPhase(inherited_timeline_phase); - bool needs_update = needs_update_ || last_update_time_ != inherited_time || - (owner_ && owner_->EffectSuppressed()) || - last_update_phase_ != timeline_phase; + (owner_ && owner_->EffectSuppressed()); needs_update_ = false; last_update_time_ = inherited_time; - last_update_phase_ = timeline_phase; if (needs_update) { Timing::CalculatedTiming calculated = SpecifiedTiming().CalculateTimings( - inherited_time, timeline_phase, at_progress_timeline_boundary, - NormalizedTiming(), direction, IsA<KeyframeEffect>(this), - inherited_playback_rate); + inherited_time, at_progress_timeline_boundary, NormalizedTiming(), + direction, IsA<KeyframeEffect>(this), inherited_playback_rate); const bool was_canceled = calculated.phase != calculated_.phase && calculated.phase == Timing::kPhaseNone;
diff --git a/third_party/blink/renderer/core/animation/animation_effect.h b/third_party/blink/renderer/core/animation/animation_effect.h index eb1ff83b..549989e4 100644 --- a/third_party/blink/renderer/core/animation/animation_effect.h +++ b/third_party/blink/renderer/core/animation/animation_effect.h
@@ -151,7 +151,6 @@ // it will (if necessary) recalculate timings and (if necessary) call // UpdateChildrenAndEffects. void UpdateInheritedTime(absl::optional<AnimationTimeDelta> inherited_time, - absl::optional<TimelinePhase> inherited_phase, bool at_progress_timeline_boundary, double inherited_playback_rate, TimingUpdateReason) const; @@ -190,7 +189,6 @@ mutable absl::optional<Timing::NormalizedTiming> normalized_; mutable bool needs_update_; mutable absl::optional<AnimationTimeDelta> last_update_time_; - mutable absl::optional<Timing::Phase> last_update_phase_; AnimationTimeDelta cancel_time_; const Timing::CalculatedTiming& EnsureCalculated() const; void EnsureNormalizedTiming() const;
diff --git a/third_party/blink/renderer/core/animation/animation_effect_test.cc b/third_party/blink/renderer/core/animation/animation_effect_test.cc index ff4890801..ccbe306e 100644 --- a/third_party/blink/renderer/core/animation/animation_effect_test.cc +++ b/third_party/blink/renderer/core/animation/animation_effect_test.cc
@@ -87,7 +87,6 @@ event_delegate_->Reset(); AnimationEffect::UpdateInheritedTime( ANIMATION_TIME_DELTA_FROM_SECONDS(time), - /* inherited_phase */ absl::nullopt, /* at_progress_timeline_boundary */ false, /* inherited_playback_rate */ 1.0, reason); }
diff --git a/third_party/blink/renderer/core/animation/animation_test_helpers.cc b/third_party/blink/renderer/core/animation/animation_test_helpers.cc index f6baaae7..123b725 100644 --- a/third_party/blink/renderer/core/animation/animation_test_helpers.cc +++ b/third_party/blink/renderer/core/animation/animation_test_helpers.cc
@@ -5,7 +5,6 @@ #include "third_party/blink/renderer/core/animation/animation_test_helpers.h" #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h" -#include "third_party/blink/renderer/bindings/core/v8/v8_union_csskeywordvalue_cssnumericvalue_scrolltimelineelementbasedoffset_string.h" #include "third_party/blink/renderer/core/animation/css_interpolation_environment.h" #include "third_party/blink/renderer/core/animation/css_interpolation_types_map.h" #include "third_party/blink/renderer/core/animation/invalidatable_interpolation.h" @@ -100,20 +99,5 @@ cascade.Apply(); } -V8ScrollTimelineOffset* OffsetFromString(Document& document, - const String& string) { - const CSSValue* value = css_test_helpers::ParseValue( - document, "<length-percentage> | auto", string); - - if (const auto* primitive = DynamicTo<CSSPrimitiveValue>(value)) { - return MakeGarbageCollected<V8ScrollTimelineOffset>( - CSSNumericValue::FromCSSValue(*primitive)); - } else if (DynamicTo<CSSIdentifierValue>(value)) { - return MakeGarbageCollected<V8ScrollTimelineOffset>( - CSSKeywordValue::Create("auto")); - } - return MakeGarbageCollected<V8ScrollTimelineOffset>(string); -} - } // namespace animation_test_helpers } // namespace blink
diff --git a/third_party/blink/renderer/core/animation/animation_test_helpers.h b/third_party/blink/renderer/core/animation/animation_test_helpers.h index 3aee9cb..8a7ca73 100644 --- a/third_party/blink/renderer/core/animation/animation_test_helpers.h +++ b/third_party/blink/renderer/core/animation/animation_test_helpers.h
@@ -7,7 +7,6 @@ #include "third_party/blink/renderer/bindings/core/v8/v8_typedefs.h" #include "third_party/blink/renderer/core/animation/interpolation.h" -#include "third_party/blink/renderer/core/animation/scroll_timeline_offset.h" #include "third_party/blink/renderer/platform/wtf/text/string_view.h" #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h" #include "v8/include/v8.h" @@ -49,14 +48,6 @@ // InvalidatableInterpolation. void EnsureInterpolatedValueCached(ActiveInterpolations*, Document&, Element*); -// Returns one of the following: -// -// - A CSSNumericValue, if the incoming string can be parsed as a -// <length-percentage>. -// - A CSSKeywordValue. if the incoming string can be parsed as 'auto'. -// - Otherwise, the incoming string. -V8ScrollTimelineOffset* OffsetFromString(Document&, const String&); - } // namespace animation_test_helpers } // namespace blink
diff --git a/third_party/blink/renderer/core/animation/animation_timeline.h b/third_party/blink/renderer/core/animation/animation_timeline.h index 0153316c..2a7ab31 100644 --- a/third_party/blink/renderer/core/animation/animation_timeline.h +++ b/third_party/blink/renderer/core/animation/animation_timeline.h
@@ -19,7 +19,7 @@ class Document; -enum class TimelinePhase { kInactive, kBefore, kActive, kAfter }; +enum class TimelinePhase { kInactive, kActive }; class CORE_EXPORT AnimationTimeline : public ScriptWrappable { DEFINE_WRAPPERTYPEINFO();
diff --git a/third_party/blink/renderer/core/animation/build.gni b/third_party/blink/renderer/core/animation/build.gni index 1ddc7c9..4695b3d 100644 --- a/third_party/blink/renderer/core/animation/build.gni +++ b/third_party/blink/renderer/core/animation/build.gni
@@ -28,7 +28,6 @@ "keyframe_effect_test.cc", "list_interpolation_functions_test.cc", "property_handle_test.cc", - "scroll_timeline_offset_test.cc", "scroll_timeline_test.cc", "scroll_timeline_util_test.cc", "svg_number_interpolation_type_test.cc",
diff --git a/third_party/blink/renderer/core/animation/css/css_animations.cc b/third_party/blink/renderer/core/animation/css/css_animations.cc index 9ee81c2..89cecf52 100644 --- a/third_party/blink/renderer/core/animation/css/css_animations.cc +++ b/third_party/blink/renderer/core/animation/css/css_animations.cc
@@ -729,12 +729,10 @@ timeline != existing_animation->Timeline()) { DCHECK(!is_animation_style_change); - absl::optional<TimelinePhase> inherited_phase; absl::optional<AnimationTimeDelta> inherited_time; absl::optional<AnimationTimeDelta> timeline_duration; if (timeline) { - inherited_phase = absl::make_optional(timeline->Phase()); inherited_time = animation->UnlimitedCurrentTime(); timeline_duration = timeline->GetDuration(); @@ -780,8 +778,8 @@ CreateKeyframeEffectModel( resolver, element, animating_element, &style, parent_style, name, keyframe_timing_function.get(), i), - timing, is_paused, inherited_time, inherited_phase, - timeline_duration, animation->playbackRate()), + timing, is_paused, inherited_time, timeline_duration, + animation->playbackRate()), specified_timing, keyframes_rule, timeline, animation_data->PlayStateList()); if (toggle_pause_state) @@ -790,7 +788,6 @@ } else { DCHECK(!is_animation_style_change); AnimationTimeline* timeline = ComputeTimeline(&element, timeline_name); - absl::optional<TimelinePhase> inherited_phase; absl::optional<AnimationTimeDelta> inherited_time = AnimationTimeDelta(); @@ -798,7 +795,6 @@ if (timeline) { timeline_duration = timeline->GetDuration(); if (!timeline->IsMonotonicallyIncreasing()) { - inherited_phase = absl::make_optional(timeline->Phase()); inherited_time = timeline->CurrentTime(); } } @@ -808,8 +804,7 @@ CreateKeyframeEffectModel(resolver, element, animating_element, &style, parent_style, name, keyframe_timing_function.get(), i), - timing, is_paused, inherited_time, inherited_phase, - timeline_duration, 1.0), + timing, is_paused, inherited_time, timeline_duration, 1.0), specified_timing, keyframes_rule, timeline, animation_data->PlayStateList()); } @@ -1430,9 +1425,8 @@ state.update.StartTransition( property, state.before_change_style, state.cloned_style, reversing_adjusted_start_value, reversing_shortening_factor, - *MakeGarbageCollected<InertEffect>(model, timing, false, - AnimationTimeDelta(), absl::nullopt, - absl::nullopt, 1.0)); + *MakeGarbageCollected<InertEffect>( + model, timing, false, AnimationTimeDelta(), absl::nullopt, 1.0)); DCHECK(!state.animating_element.GetElementAnimations() || !state.animating_element.GetElementAnimations() ->IsAnimationStyleChange()); @@ -1624,7 +1618,7 @@ auto* inert_animation_for_sampling = MakeGarbageCollected<InertEffect>( effect->Model(), effect->SpecifiedTiming(), false, current_time, - absl::nullopt, absl::nullopt, animation->playbackRate()); + /* timeline_duration */ absl::nullopt, animation->playbackRate()); HeapVector<Member<Interpolation>> sample; inert_animation_for_sampling->Sample(sample);
diff --git a/third_party/blink/renderer/core/animation/css/css_scroll_timeline.cc b/third_party/blink/renderer/core/animation/css/css_scroll_timeline.cc index 07cc9d3a..364d135 100644 --- a/third_party/blink/renderer/core/animation/css/css_scroll_timeline.cc +++ b/third_party/blink/renderer/core/animation/css/css_scroll_timeline.cc
@@ -5,7 +5,7 @@ #include "third_party/blink/renderer/core/animation/css/css_scroll_timeline.h" #include "third_party/blink/renderer/core/animation/document_animations.h" -#include "third_party/blink/renderer/core/css/css_element_offset_value.h" +#include "third_party/blink/renderer/core/css/css_function_value.h" #include "third_party/blink/renderer/core/css/css_id_selector_value.h" #include "third_party/blink/renderer/core/css/css_identifier_value.h" #include "third_party/blink/renderer/core/css/css_value_list.h" @@ -32,14 +32,6 @@ return IsIdentifier(value, CSSValueID::kNone); } -bool IsStart(const CSSValue* value) { - return IsIdentifier(value, CSSValueID::kStart); -} - -bool IsEnd(const CSSValue* value) { - return IsIdentifier(value, CSSValueID::kEnd); -} - const cssvalue::CSSIdSelectorValue* GetIdSelectorValue(const CSSValue* value) { if (const auto* selector = DynamicTo<CSSFunctionValue>(value)) { if (selector->FunctionType() != CSSValueID::kSelector) @@ -61,39 +53,6 @@ return absl::nullopt; } -Element* ComputeElementOffsetTarget(Document& document, const CSSValue* value) { - if (const auto* id = GetIdSelectorValue(value)) - return document.getElementById(id->Id()); - return nullptr; -} - -String ComputeElementOffsetEdge(const CSSValue* value) { - if (!value || IsStart(value)) - return "start"; - DCHECK(IsEnd(value)); - return "end"; -} - -double ComputeElementOffsetThreshold(const CSSValue* value) { - if (auto* primitive_value = DynamicTo<CSSPrimitiveValue>(value)) { - DCHECK(primitive_value->IsNumber()); - return primitive_value->GetDoubleValue(); - } - return 0; -} - -ScrollTimelineElementBasedOffset* ComputeElementBasedOffset( - Document& document, - const cssvalue::CSSElementOffsetValue* value) { - auto* offset = ScrollTimelineElementBasedOffset::Create(); - Element* target = ComputeElementOffsetTarget(document, value->Target()); - if (target) - offset->setTarget(target); - offset->setEdge(ComputeElementOffsetEdge(value->Edge())); - offset->setThreshold(ComputeElementOffsetThreshold(value->Threshold())); - return offset; -} - ScrollTimeline::ScrollDirection ComputeScrollDirection(const CSSValue* value) { CSSValueID value_id = CSSValueID::kAuto; @@ -114,38 +73,6 @@ } } -ScrollTimelineOffset* ComputeScrollOffset(Document& document, - const CSSValue* value) { - if (auto* primitive_value = DynamicTo<CSSPrimitiveValue>(value)) - return MakeGarbageCollected<ScrollTimelineOffset>(primitive_value); - if (auto* offset = DynamicTo<cssvalue::CSSElementOffsetValue>(value)) { - auto* element_based = ComputeElementBasedOffset(document, offset); - return MakeGarbageCollected<ScrollTimelineOffset>(element_based); - } - DCHECK(!value || IsAuto(value)); - return MakeGarbageCollected<ScrollTimelineOffset>(); -} - -HeapVector<Member<ScrollTimelineOffset>> ComputeScrollOffsets( - Document& document, - const CSSValue* start, - const CSSValue* end) { - HeapVector<Member<ScrollTimelineOffset>> offsets; - - const bool start_is_auto = !start || IsAuto(start); - const bool end_is_auto = !end || IsAuto(end); - - // TODO(crbug.com/1094014): scroll_offsets will replace start and end - // offsets once spec decision on multiple scroll offsets is finalized. - // https://github.com/w3c/csswg-drafts/issues/4912 - if (!start_is_auto) - offsets.push_back(ComputeScrollOffset(document, start)); - if (!end_is_auto || !start_is_auto) - offsets.push_back(ComputeScrollOffset(document, end)); - - return offsets; -} - class ElementReferenceObserver : public IdTargetObserver { public: ElementReferenceObserver(Document* document, @@ -181,22 +108,6 @@ document, id->Id(), timeline)); } - // TODO(crbug.com/1094014): The 'offsets' descriptor will replace the 'start' - // and 'end' descriptors eventually. - HeapVector<Member<const CSSValue>> offsets = {rule->GetStart(), - rule->GetEnd()}; - - for (const CSSValue* offset : offsets) { - const auto* element_offset = - DynamicTo<cssvalue::CSSElementOffsetValue>(offset); - if (!element_offset) - continue; - if (const auto* id = GetIdSelectorValue(element_offset->Target())) { - observers.push_back(MakeGarbageCollected<ElementReferenceObserver>( - document, id->Id(), timeline)); - } - } - return observers; } @@ -206,7 +117,6 @@ StyleRuleScrollTimeline& rule) : source_(ComputeScrollSource(document, rule.GetSource())), direction_(ComputeScrollDirection(rule.GetOrientation())), - offsets_(ComputeScrollOffsets(document, rule.GetStart(), rule.GetEnd())), rule_(&rule) {} // TODO(crbug.com/1329159): Support nearest scroll ancestor. @@ -215,21 +125,15 @@ document, ReferenceType::kSource, options.source_.value_or(document->ScrollingElementNoLayout()), - options.direction_, - std::move(options.offsets_)), + options.direction_), rule_(options.rule_) { DCHECK(rule_); } -const AtomicString& CSSScrollTimeline::Name() const { - return rule_->GetName(); -} - bool CSSScrollTimeline::Matches(const Options& options) const { // TODO(crbug.com/1060384): Support ReferenceType::kNearestAncestor. return HasExplicitSource() && (SourceInternal() == options.source_) && - (GetOrientation() == options.direction_) && - (ScrollOffsetsEqual(options.offsets_)) && (rule_ == options.rule_); + (GetOrientation() == options.direction_) && (rule_ == options.rule_); } void CSSScrollTimeline::AnimationAttached(Animation* animation) {
diff --git a/third_party/blink/renderer/core/animation/css/css_scroll_timeline.h b/third_party/blink/renderer/core/animation/css/css_scroll_timeline.h index 11bef85..fd90291 100644 --- a/third_party/blink/renderer/core/animation/css/css_scroll_timeline.h +++ b/third_party/blink/renderer/core/animation/css/css_scroll_timeline.h
@@ -31,7 +31,6 @@ absl::optional<Element*> source_; ScrollTimeline::ScrollDirection direction_; - HeapVector<Member<ScrollTimelineOffset>> offsets_; StyleRuleScrollTimeline* rule_; };
diff --git a/third_party/blink/renderer/core/animation/css/css_scroll_timeline_test.cc b/third_party/blink/renderer/core/animation/css/css_scroll_timeline_test.cc index 3a5b927..86d4ea19 100644 --- a/third_party/blink/renderer/core/animation/css/css_scroll_timeline_test.cc +++ b/third_party/blink/renderer/core/animation/css/css_scroll_timeline_test.cc
@@ -87,8 +87,6 @@ ASSERT_FALSE(HasObservers("scroller2")); ASSERT_FALSE(HasObservers("scroller3")); ASSERT_FALSE(HasObservers("redefined")); - ASSERT_FALSE(HasObservers("offset1")); - ASSERT_FALSE(HasObservers("offset2")); SetBodyInnerHTML(R"HTML( <style> @@ -101,7 +99,6 @@ } @scroll-timeline timeline2 { source: selector(#scroller2); - start: selector(#offset1); } div { animation: anim 10s; @@ -120,7 +117,6 @@ EXPECT_TRUE(HasObservers("scroller1")); EXPECT_TRUE(HasObservers("scroller2")); - EXPECT_TRUE(HasObservers("offset1")); Element* element1 = GetDocument().getElementById("element1"); Element* element2 = GetDocument().getElementById("element2"); @@ -134,7 +130,6 @@ style_element->setTextContent(R"CSS( @scroll-timeline timeline2 { source: selector(#redefined); - start: selector(#offset2); } @scroll-timeline timeline3 { source: selector(#scroller3); @@ -150,8 +145,6 @@ EXPECT_FALSE(HasObservers("scroller2")); EXPECT_TRUE(HasObservers("scroller3")); EXPECT_TRUE(HasObservers("redefined")); - EXPECT_FALSE(HasObservers("offset1")); - EXPECT_TRUE(HasObservers("offset2")); // Remove the <style> element again. style_element->remove(); @@ -161,8 +154,6 @@ EXPECT_TRUE(HasObservers("scroller2")); EXPECT_FALSE(HasObservers("scroller3")); EXPECT_FALSE(HasObservers("redefined")); - EXPECT_TRUE(HasObservers("offset1")); - EXPECT_FALSE(HasObservers("offset2")); } TEST_F(CSSScrollTimelineTest, SharedTimelines) {
diff --git a/third_party/blink/renderer/core/animation/effect_stack_test.cc b/third_party/blink/renderer/core/animation/effect_stack_test.cc index a446640..e20b918 100644 --- a/third_party/blink/renderer/core/animation/effect_stack_test.cc +++ b/third_party/blink/renderer/core/animation/effect_stack_test.cc
@@ -74,9 +74,8 @@ InertEffect* MakeInertEffect(KeyframeEffectModelBase* effect) { Timing timing; timing.fill_mode = Timing::FillMode::BOTH; - return MakeGarbageCollected<InertEffect>(effect, timing, false, - AnimationTimeDelta(), - absl::nullopt, absl::nullopt, 1.0); + return MakeGarbageCollected<InertEffect>( + effect, timing, false, AnimationTimeDelta(), absl::nullopt, 1.0); } KeyframeEffect* MakeKeyframeEffect(KeyframeEffectModelBase* effect,
diff --git a/third_party/blink/renderer/core/animation/inert_effect.cc b/third_party/blink/renderer/core/animation/inert_effect.cc index bad9847..7c5857b 100644 --- a/third_party/blink/renderer/core/animation/inert_effect.cc +++ b/third_party/blink/renderer/core/animation/inert_effect.cc
@@ -38,20 +38,18 @@ const Timing& timing, bool paused, absl::optional<AnimationTimeDelta> inherited_time, - absl::optional<TimelinePhase> inherited_phase, absl::optional<AnimationTimeDelta> timeline_duration, double playback_rate) : AnimationEffect(timing), model_(model), paused_(paused), inherited_time_(inherited_time), - inherited_phase_(inherited_phase), timeline_duration_(timeline_duration), playback_rate_(playback_rate) {} void InertEffect::Sample(HeapVector<Member<Interpolation>>& result) const { - UpdateInheritedTime(inherited_time_, inherited_phase_, false, playback_rate_, - kTimingUpdateOnDemand); + UpdateInheritedTime(inherited_time_, /* at_scroll_timeline_boundary */ false, + playback_rate_, kTimingUpdateOnDemand); if (!IsInEffect()) { result.clear(); return;
diff --git a/third_party/blink/renderer/core/animation/inert_effect.h b/third_party/blink/renderer/core/animation/inert_effect.h index 79c2496..27efe66 100644 --- a/third_party/blink/renderer/core/animation/inert_effect.h +++ b/third_party/blink/renderer/core/animation/inert_effect.h
@@ -46,7 +46,6 @@ const Timing&, bool paused, absl::optional<AnimationTimeDelta> inherited_time, - absl::optional<TimelinePhase> inherited_phase, absl::optional<AnimationTimeDelta> timeline_duration, double playback_rate);
diff --git a/third_party/blink/renderer/core/animation/inert_effect_test.cc b/third_party/blink/renderer/core/animation/inert_effect_test.cc index 767b8e0d..a621b19 100644 --- a/third_party/blink/renderer/core/animation/inert_effect_test.cc +++ b/third_party/blink/renderer/core/animation/inert_effect_test.cc
@@ -24,8 +24,7 @@ auto* inert_effect = MakeGarbageCollected<InertEffect>( opacity_model, timing, /* paused */ false, AnimationTimeDelta(), - TimelinePhase::kActive, absl::nullopt, - /* playback_rate */ 1.0); + /* timeline_duration */ absl::nullopt, /* playback_rate */ 1.0); HeapVector<Member<Interpolation>> interpolations; // Calling Sample ensures Timing is calculated. inert_effect->Sample(interpolations); @@ -40,8 +39,7 @@ auto* inert_effect = MakeGarbageCollected<InertEffect>( opacity_model, timing, /* paused */ false, AnimationTimeDelta(), - TimelinePhase::kActive, absl::nullopt, - /* playback_rate */ 1.0); + /* timeline_duration */ absl::nullopt, /* playback_rate */ 1.0); HeapVector<Member<Interpolation>> interpolations; // Calling Sample ensures Timing is calculated. inert_effect->Sample(interpolations); @@ -56,8 +54,7 @@ auto* inert_effect = MakeGarbageCollected<InertEffect>( opacity_model, timing, /* paused */ false, AnimationTimeDelta(), - TimelinePhase::kActive, absl::nullopt, - /* playback_rate */ -1.0); + /* timeline_duration */ absl::nullopt, /* playback_rate */ -1.0); HeapVector<Member<Interpolation>> interpolations; // Calling Sample ensures Timing is calculated. inert_effect->Sample(interpolations); @@ -76,13 +73,11 @@ auto* opacity_effect = MakeGarbageCollected<InertEffect>( opacity_model, timing, /* paused */ false, AnimationTimeDelta(), - TimelinePhase::kActive, absl::nullopt, - /* playback_rate */ 1.0); + /* timeline_duration */ absl::nullopt, /* playback_rate */ 1.0); auto* color_effect = MakeGarbageCollected<InertEffect>( color_model, timing, /* paused */ false, AnimationTimeDelta(), - TimelinePhase::kActive, absl::nullopt, - /* playback_rate */ 1.0); + /* timeline_duration */ absl::nullopt, /* playback_rate */ 1.0); EXPECT_TRUE(opacity_effect->Affects(PropertyHandle(GetCSSPropertyOpacity()))); EXPECT_FALSE(opacity_effect->Affects(PropertyHandle(GetCSSPropertyColor())));
diff --git a/third_party/blink/renderer/core/animation/scroll_timeline.cc b/third_party/blink/renderer/core/animation/scroll_timeline.cc index 397b91c..8824f62 100644 --- a/third_party/blink/renderer/core/animation/scroll_timeline.cc +++ b/third_party/blink/renderer/core/animation/scroll_timeline.cc
@@ -9,9 +9,7 @@ #include "base/memory/values_equivalent.h" #include "third_party/abseil-cpp/absl/types/optional.h" #include "third_party/blink/renderer/bindings/core/v8/v8_scroll_timeline_options.h" -#include "third_party/blink/renderer/bindings/core/v8/v8_union_csskeywordvalue_cssnumericvalue_scrolltimelineelementbasedoffset_string.h" #include "third_party/blink/renderer/bindings/core/v8/v8_union_cssnumericvalue_double.h" -#include "third_party/blink/renderer/core/animation/scroll_timeline_offset.h" #include "third_party/blink/renderer/core/animation/scroll_timeline_util.h" #include "third_party/blink/renderer/core/animation/worklet_animation_base.h" #include "third_party/blink/renderer/core/animation/worklet_animation_controller.h" @@ -81,29 +79,6 @@ return nullptr; } - HeapVector<Member<ScrollTimelineOffset>> scroll_offsets; - // TODO(crbug.com/1329159): Replace scrollOffsets once ratified in the spec - // rewrite. - // https://drafts.csswg.org/scroll-animations-1/#set-the-offset-value - for (auto& offset : options->scrollOffsets()) { - ScrollTimelineOffset* scroll_offset = ScrollTimelineOffset::Create(offset); - if (!scroll_offset) { - exception_state.ThrowTypeError("Invalid scroll offset"); - return nullptr; - } - // 2.1 If val is a CSSKeywordValue and matches the grammar auto and pos - // equals to 0 or size - 1: Return val. - unsigned int pos = scroll_offsets.size(); - if (scroll_offset->IsDefaultValue() && - !(pos == 0 || pos == (options->scrollOffsets().size() - 1))) { - exception_state.ThrowTypeError( - "Invalid scrollOffsets: 'auto' can only be set as start or end " - "offset"); - return nullptr; - } - scroll_offsets.push_back(scroll_offset); - } - // The scrollingElement depends on style/layout-tree in quirks mode. Update // such that subsequent calls to ScrollingElementNoLayout returns up-to-date // information. @@ -112,8 +87,7 @@ return MakeGarbageCollected<ScrollTimeline>( &document, ReferenceType::kSource, - source.value_or(document.ScrollingElementNoLayout()), orientation, - scroll_offsets); + source.value_or(document.ScrollingElementNoLayout()), orientation); } bool ScrollTimeline::StringToScrollDirection( @@ -138,17 +112,14 @@ return false; } -ScrollTimeline::ScrollTimeline( - Document* document, - ReferenceType reference_type, - Element* reference, - ScrollDirection orientation, - HeapVector<Member<ScrollTimelineOffset>> scroll_offsets) +ScrollTimeline::ScrollTimeline(Document* document, + ReferenceType reference_type, + Element* reference, + ScrollDirection orientation) : AnimationTimeline(document), reference_type_(reference_type), reference_element_(reference), - orientation_(orientation), - scroll_offsets_(std::move(scroll_offsets)) { + orientation_(orientation) { UpdateResolvedSource(); SnapshotState(); } @@ -167,116 +138,18 @@ return layout_box && layout_box->IsScrollContainer(); } -ScrollTimelineOffset* ScrollTimeline::StartScrollOffset() const { - // Single entry offset in scrollOffsets is considered as 'end'. Thus, - // resolving start offset only if there is at least 2 offsets. - return scroll_offsets_.size() >= 2 ? scroll_offsets_.at(0) : nullptr; -} -ScrollTimelineOffset* ScrollTimeline::EndScrollOffset() const { - // End offset is always the last offset in scrollOffsets if exists. - return scroll_offsets_.size() >= 1 - ? scroll_offsets_.at(scroll_offsets_.size() - 1) - : nullptr; +absl::optional<ScrollOffsets> ScrollTimeline::GetResolvedScrollOffsets() const { + return timeline_state_snapshotted_.scroll_offsets; } -const std::vector<double> ScrollTimeline::GetResolvedScrollOffsets() const { - std::vector<double> resolved_offsets; - for (const auto& offset : timeline_state_snapshotted_.scroll_offsets) - resolved_offsets.push_back(offset); - return resolved_offsets; -} - -// Resolves scroll offsets and stores them into resolved_offsets argument. -// Returns true if the offsets are resolved. -// https://drafts.csswg.org/scroll-animations-1/#effective-scroll-offsets-algorithm -bool ScrollTimeline::ResolveScrollOffsets( - WTF::Vector<double>& resolved_offsets) const { - // 1. Let effective scroll offsets be an empty list of effective scroll - // offsets. - DCHECK(resolved_offsets.IsEmpty()); - DCHECK(ComputeIsActive()); - LayoutBox* layout_box = resolved_source_->GetLayoutBox(); - DCHECK(layout_box); - - double current_offset; - double max_offset; - GetCurrentAndMaxOffset(layout_box, current_offset, max_offset); - - auto orientation = ToPhysicalScrollOrientation(orientation_, *layout_box); - - // 2. Let first offset be true. - // first_offset signifies weather min or max scroll offset is pushed to - // effective scroll offsets. - bool first_offset = true; - - // 3. If scrollOffsets is empty - if (scroll_offsets_.size() == 0) { - // Start and end offsets resolve to 'auto'. - // 3.1 Run the procedure to resolve a scroll timeline offset for auto with - // the is first flag set to first offset and add the resulted value into - // effective scroll offsets. - resolved_offsets.push_back(0); - // 3.2 Set first offset to false. - // 3.3 Run the procedure to resolve a scroll timeline offset for auto with - // the is first flag set to first offset and add the resulted value into - // effective scroll offsets. - resolved_offsets.push_back(max_offset); - return true; - } - // Single entry offset in scrollOffsets is considered as 'end'. - // 4. If scrollOffsets has exactly one element - if (scroll_offsets_.size() == 1) { - // 4.1 Run the procedure to resolve a scroll timeline offset for auto with - // the is first flag set to first offset and add the resulted value into - // effective scroll offsets. - resolved_offsets.push_back(0); - // 4.2 Set first offset to false. - first_offset = false; - } - - // 5. For each scroll offset in the list of scrollOffsets, perform the - // following steps: - for (auto& offset : scroll_offsets_) { - // 5.1 Let effective offset be the result of applying the procedure to - // resolve a scroll timeline offset for scroll offset with the is first flag - // set to first offset. - auto resolved_offset = - offset->ResolveOffset(resolved_source_, orientation, max_offset, - first_offset ? 0 : max_offset); - if (!resolved_offset) { - // 5.2 If effective offset is null, the effective scroll offsets is empty - // and abort the remaining steps. - resolved_offsets.clear(); - return false; - } - // 5.3 Add effective offset into effective scroll offsets. - resolved_offsets.push_back(resolved_offset.value()); - - // 5.4 Set first offset to false. - first_offset = false; - } - DCHECK_GE(resolved_offsets.size(), 2u); - // 6. Return effective scroll offsets. - return true; -} - +// TODO(crbug.com/1336260): Since phase can only be kActive or kInactive and +// currentTime can only be null if phase is inactive or before the first +// snapshot we can probably drop phase. AnimationTimeline::PhaseAndTime ScrollTimeline::CurrentPhaseAndTime() { return {timeline_state_snapshotted_.phase, timeline_state_snapshotted_.current_time}; } -bool ScrollTimeline::ScrollOffsetsEqual( - const HeapVector<Member<ScrollTimelineOffset>>& other) const { - if (scroll_offsets_.size() != other.size()) - return false; - wtf_size_t size = scroll_offsets_.size(); - for (wtf_size_t i = 0; i < size; ++i) { - if (!base::ValuesEquivalent(scroll_offsets_.at(i), other.at(i))) - return false; - } - return true; -} - V8CSSNumberish* ScrollTimeline::ConvertTimeToProgress( AnimationTimeDelta time) const { return MakeGarbageCollected<V8CSSNumberish>( @@ -298,6 +171,9 @@ return MakeGarbageCollected<V8CSSNumberish>(CSSUnitValues::percent(100)); } +// TODO(crbug.com/1060384): This section is missing from the spec rewrite. +// Resolved to remove the before and after phases in +// https://github.com/w3c/csswg-drafts/issues/7240. // https://drafts.csswg.org/scroll-animations-1/#current-time-algorithm ScrollTimeline::TimelineState ScrollTimeline::ComputeTimelineState() { UpdateResolvedSource(); @@ -305,70 +181,76 @@ // 1. If scroll timeline is inactive, return an unresolved time value. // https://github.com/WICG/scroll-animations/issues/31 // https://wicg.github.io/scroll-animations/#current-time-algorithm - WTF::Vector<double> resolved_offsets; if (!ComputeIsActive()) { return {TimelinePhase::kInactive, /*current_time*/ absl::nullopt, - resolved_offsets}; + /* scroll_offsets */ absl::nullopt}; } + DCHECK(resolved_source_); LayoutBox* layout_box = resolved_source_->GetLayoutBox(); - // 2. Otherwise, let current scroll offset be the current scroll offset of - // scrollSource in the direction specified by orientation. - double current_offset; - double max_offset; - GetCurrentAndMaxOffset(layout_box, current_offset, max_offset); + // Layout box and scrollable area must exist since the timeline is active. + DCHECK(layout_box); + DCHECK(layout_box->GetScrollableArea()); - bool resolved = ResolveScrollOffsets(resolved_offsets); + // Depending on the writing-mode and direction, the scroll origin shifts and + // the scroll offset may be negative. The easiest way to deal with this is to + // use only the magnitude of the scroll offset, and compare it to (max_offset + // - min_offset). + PaintLayerScrollableArea* scrollable_area = layout_box->GetScrollableArea(); - if (!resolved) { - DCHECK(resolved_offsets.IsEmpty()); - return {TimelinePhase::kInactive, /*current_time*/ absl::nullopt, - resolved_offsets}; - } + // Using the absolute value of the scroll offset only makes sense if either + // the max or min scroll offset for a given axis is 0. This should be + // guaranteed by the scroll origin code, but these DCHECKs ensure that. + DCHECK(scrollable_area->MaximumScrollOffset().y() == 0 || + scrollable_area->MinimumScrollOffset().y() == 0); + DCHECK(scrollable_area->MaximumScrollOffset().x() == 0 || + scrollable_area->MinimumScrollOffset().x() == 0); - // TODO(crbug.com/1060384): Support determination of offsets for a - // ViewTimeline. + ScrollOffset scroll_offset = scrollable_area->GetScrollOffset(); + auto physical_orientation = + ToPhysicalScrollOrientation(orientation_, *layout_box); + double current_offset = (physical_orientation == kHorizontalScroll) + ? scroll_offset.x() + : scroll_offset.y(); + // When using a rtl direction, current_offset grows correctly from 0 to + // max_offset, but is negative. Since our offsets are all just deltas along + // the orientation direction, we can just take the absolute current_offset and + // use that everywhere. + current_offset = std::abs(current_offset); - double start_offset = resolved_offsets[0]; - double end_offset = resolved_offsets[resolved_offsets.size() - 1]; + // TODO(crbug.com/1329159): Override in ViewTimeline to compute offsets + // corresponding to the 'cover' range. + double start_offset = GetStartOffset(scrollable_area, physical_orientation); + double end_offset = GetEndOffset(scrollable_area, physical_orientation); - // TODO(crbug.com/1060384): Once the spec has been updated to state what the - // expected result is when startScrollOffset >= endScrollOffset, we might need - // to add a special case here. See - // https://github.com/WICG/scroll-animations/issues/20 - - // 3. The current time is the result corresponding to the first matching - // condition from below: - // 3.1 If current scroll offset is less than effective start offset: - // The current time is 0. - if (current_offset < start_offset) { - return {TimelinePhase::kBefore, base::TimeDelta(), resolved_offsets}; - } + // TODO(crbug.com/1338167): Update once + // github.com/w3c/csswg-drafts/issues/7401 is resolved. + double progress = + (end_offset == start_offset) + ? 1 + : (current_offset - start_offset) / (end_offset - start_offset); base::TimeDelta duration = base::Seconds(GetDuration()->InSecondsF()); + absl::optional<base::TimeDelta> calculated_current_time = + base::Milliseconds(progress * duration.InMillisecondsF()); - // 3.2 If current scroll offset is greater than or equal to effective end - // offset: - // The current time is the effective time range. - if (current_offset >= end_offset) { - // If end_offset is greater than or equal to the maximum scroll offset of - // scrollSource in orientation then return active phase, otherwise return - // after phase. - TimelinePhase phase = end_offset >= max_offset ? TimelinePhase::kActive - : TimelinePhase::kAfter; - return {phase, duration, resolved_offsets}; - } + return {TimelinePhase::kActive, calculated_current_time, + absl::make_optional<ScrollOffsets>(start_offset, end_offset)}; +} - // 3.3 Otherwise, - // 3.3.1 Let progress be a result of applying calculate scroll timeline - // progress procedure for current scroll offset. - // 3.3.2 The current time is the result of evaluating the following - // expression: - // progress × effective time range - absl::optional<base::TimeDelta> calculated_current_time = base::Milliseconds( - scroll_timeline_util::ComputeProgress(current_offset, resolved_offsets) * - duration.InMillisecondsF()); - return {TimelinePhase::kActive, calculated_current_time, resolved_offsets}; +double ScrollTimeline::GetStartOffset( + PaintLayerScrollableArea* scrollable_area, + ScrollOrientation physical_orientation) const { + return 0; +} + +double ScrollTimeline::GetEndOffset( + PaintLayerScrollableArea* scrollable_area, + ScrollOrientation physical_orientation) const { + ScrollOffset scroll_dimensions = scrollable_area->MaximumScrollOffset() - + scrollable_area->MinimumScrollOffset(); + return physical_orientation == kHorizontalScroll ? scroll_dimensions.x() + : scroll_dimensions.y(); } // Scroll-linked animations are initialized with the start time of zero. @@ -483,17 +365,6 @@ } } -const HeapVector<Member<V8ScrollTimelineOffset>> ScrollTimeline::scrollOffsets() - const { - HeapVector<Member<V8ScrollTimelineOffset>> scroll_offsets; - for (auto& offset : scroll_offsets_) { - scroll_offsets.push_back(offset->ToV8ScrollTimelineOffset()); - // 'auto' can only be the end offset. - DCHECK(!offset->IsDefaultValue() || scroll_offsets.size() == 2); - } - return scroll_offsets; -} - void ScrollTimeline::GetCurrentAndMaxOffset(const LayoutBox* layout_box, double& current_offset, double& max_offset) const { @@ -588,7 +459,6 @@ void ScrollTimeline::Trace(Visitor* visitor) const { visitor->Trace(reference_element_); visitor->Trace(resolved_source_); - visitor->Trace(scroll_offsets_); visitor->Trace(attached_worklet_animations_); AnimationTimeline::Trace(visitor); } @@ -644,6 +514,7 @@ void ScrollTimeline::UpdateCompositorTimeline() { if (!compositor_timeline_) return; + ToScrollTimeline(compositor_timeline_.get()) ->UpdateScrollerIdAndScrollOffsets( scroll_timeline_util::GetCompositorScrollElementId(resolved_source_),
diff --git a/third_party/blink/renderer/core/animation/scroll_timeline.h b/third_party/blink/renderer/core/animation/scroll_timeline.h index 26573b8a..e3cdcc5 100644 --- a/third_party/blink/renderer/core/animation/scroll_timeline.h +++ b/third_party/blink/renderer/core/animation/scroll_timeline.h
@@ -7,18 +7,20 @@ #include "base/gtest_prod_util.h" #include "base/time/time.h" +#include "cc/animation/scroll_timeline.h" #include "third_party/blink/renderer/bindings/core/v8/v8_typedefs.h" #include "third_party/blink/renderer/core/animation/animation_timeline.h" -#include "third_party/blink/renderer/core/animation/scroll_timeline_offset.h" #include "third_party/blink/renderer/core/animation/timing.h" #include "third_party/blink/renderer/core/core_export.h" #include "third_party/blink/renderer/core/css/css_primitive_value.h" #include "third_party/blink/renderer/core/dom/element.h" +#include "third_party/blink/renderer/core/scroll/scroll_types.h" #include "third_party/blink/renderer/platform/heap/collection_support/heap_hash_set.h" #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h" namespace blink { +class PaintLayerScrollableArea; class ScrollTimelineOptions; class WorkletAnimationBase; @@ -34,6 +36,8 @@ class CORE_EXPORT ScrollTimeline : public AnimationTimeline { DEFINE_WRAPPERTYPEINFO(); + using ScrollOffsets = cc::ScrollTimeline::ScrollOffsets; + public: enum ScrollDirection { kBlock, @@ -57,8 +61,7 @@ ScrollTimeline(Document*, ReferenceType reference_type, Element* reference, - ScrollDirection, - HeapVector<Member<ScrollTimelineOffset>>); + ScrollDirection); static bool StringToScrollDirection(String scroll_direction, ScrollTimeline::ScrollDirection& result); @@ -79,7 +82,6 @@ // IDL API implementation. Element* source() const; String orientation(); - const HeapVector<Member<V8ScrollTimelineOffset>> scrollOffsets() const; V8CSSNumberish* currentTime() override; V8CSSNumberish* duration() override; @@ -93,7 +95,7 @@ // Return the latest resolved scroll offsets. This will be empty when // timeline is inactive. - const std::vector<double> GetResolvedScrollOffsets() const; + absl::optional<ScrollOffsets> GetResolvedScrollOffsets() const; ScrollDirection GetOrientation() const { return orientation_; } @@ -147,8 +149,6 @@ protected: PhaseAndTime CurrentPhaseAndTime() override; - bool ScrollOffsetsEqual( - const HeapVector<Member<ScrollTimelineOffset>>& other) const; virtual Element* ReferenceElement() const { return reference_element_.Get(); } @@ -164,6 +164,13 @@ void UpdateResolvedSource(); + // Scroll offsets corresponding to 0% and 100% progress. By default, these + // correspond to the scroll range of the container. + virtual double GetStartOffset(PaintLayerScrollableArea* scrollable_area, + ScrollOrientation physical_orientation) const; + virtual double GetEndOffset(PaintLayerScrollableArea* scrollable_area, + ScrollOrientation physical_orientation) const; + private: FRIEND_TEST_ALL_PREFIXES(ScrollTimelineTest, MultipleScrollOffsetsClamping); FRIEND_TEST_ALL_PREFIXES(ScrollTimelineTest, ResolveScrollOffsets); @@ -174,19 +181,12 @@ bool ComputeIsActive() const; PhaseAndTime ComputeCurrentPhaseAndTime() const; - // Resolve scroll offsets The resolution process turns length-based values - // into concrete length values resolving percentages and zoom factor. For - // element-based values it computes the corresponding length value that maps - // to the particular element intersection. See - // |ScrollTimelineOffset::ResolveOffset()| for more details. - bool ResolveScrollOffsets(WTF::Vector<double>& resolved_offsets) const; - struct TimelineState { + // TODO(crbug.com/1338167): Remove phase as it can be inferred from + // current_time. TimelinePhase phase; absl::optional<base::TimeDelta> current_time; - // The resolved version of scroll offset. The vector is empty - // when timeline is inactive (e.g., when source does not overflow). - WTF::Vector<double> scroll_offsets; + absl::optional<ScrollOffsets> scroll_offsets; bool operator==(const TimelineState& other) const { return phase == other.phase && current_time == other.current_time && @@ -195,8 +195,6 @@ }; TimelineState ComputeTimelineState(); - ScrollTimelineOffset* StartScrollOffset() const; - ScrollTimelineOffset* EndScrollOffset() const; // Use time_check true to request next service if time has changed. // false - regardless of time change. @@ -206,7 +204,6 @@ Member<Element> reference_element_; Member<Node> resolved_source_; ScrollDirection orientation_; - HeapVector<Member<ScrollTimelineOffset>> scroll_offsets_; // Snapshotted value produced by the last SnapshotState call. TimelineState timeline_state_snapshotted_;
diff --git a/third_party/blink/renderer/core/animation/scroll_timeline.idl b/third_party/blink/renderer/core/animation/scroll_timeline.idl index ac7bb94..3d453969 100644 --- a/third_party/blink/renderer/core/animation/scroll_timeline.idl +++ b/third_party/blink/renderer/core/animation/scroll_timeline.idl
@@ -2,9 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -typedef (CSSNumericValue or CSSKeywordish) ScrollTimelineContainerBasedOffset; -typedef (ScrollTimelineContainerBasedOffset or ScrollTimelineElementBasedOffset) ScrollTimelineOffset; - // https://wicg.github.io/scroll-animations/#scrolltimeline-interface [ RuntimeEnabled=ScrollTimeline, @@ -13,5 +10,4 @@ [CallWith=Document, RaisesException, MeasureAs=ScrollTimelineConstructor] constructor(optional ScrollTimelineOptions options = {}); readonly attribute Element? source; readonly attribute ScrollDirection orientation; - readonly attribute FrozenArray<ScrollTimelineOffset> scrollOffsets; };
diff --git a/third_party/blink/renderer/core/animation/scroll_timeline_element_based_offset.idl b/third_party/blink/renderer/core/animation/scroll_timeline_element_based_offset.idl deleted file mode 100644 index 189c0fc..0000000 --- a/third_party/blink/renderer/core/animation/scroll_timeline_element_based_offset.idl +++ /dev/null
@@ -1,16 +0,0 @@ -// Copyright 2020 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// Experimental IDL of element-based offsets based on this proposal: -// https://github.com/w3c/csswg-drafts/issues/4337 - -enum Edge {"start", "end"}; - -dictionary ScrollTimelineElementBasedOffset { - Element target; - double threshold = 0.0; - Edge edge = "start"; - // TODO(majidvp): Add other values from proposal. http://crbug.com/1023375 - // DOMString rootMargin; -}; \ No newline at end of file
diff --git a/third_party/blink/renderer/core/animation/scroll_timeline_offset.cc b/third_party/blink/renderer/core/animation/scroll_timeline_offset.cc deleted file mode 100644 index ddd0b69..0000000 --- a/third_party/blink/renderer/core/animation/scroll_timeline_offset.cc +++ /dev/null
@@ -1,245 +0,0 @@ -// Copyright 2020 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "third_party/blink/renderer/core/animation/scroll_timeline_offset.h" - -#include "base/memory/values_equivalent.h" -#include "third_party/abseil-cpp/absl/types/optional.h" -#include "third_party/blink/renderer/bindings/core/v8/v8_scroll_timeline_element_based_offset.h" -#include "third_party/blink/renderer/bindings/core/v8/v8_union_csskeywordvalue_cssnumericvalue_scrolltimelineelementbasedoffset_string.h" -#include "third_party/blink/renderer/core/css/css_to_length_conversion_data.h" -#include "third_party/blink/renderer/core/css/cssom/css_keyword_value.h" -#include "third_party/blink/renderer/core/css/cssom/css_numeric_value.h" -#include "third_party/blink/renderer/core/dom/node_computed_style.h" -#include "third_party/blink/renderer/core/layout/layout_box.h" -#include "third_party/blink/renderer/core/layout/layout_view.h" -#include "third_party/blink/renderer/platform/geometry/layout_unit.h" -#include "third_party/blink/renderer/platform/geometry/length_functions.h" - -namespace blink { - -namespace { - -bool ValidateElementBasedOffset( - const ScrollTimelineElementBasedOffset* offset) { - if (!offset->hasTarget()) - return false; - - if (offset->hasThreshold()) { - if (offset->threshold() < 0 || offset->threshold() > 1) - return false; - } - - return true; -} - -// TODO(majidvp): Dedup. This is a copy of the function in -// third_party/blink/renderer/core/intersection_observer/intersection_geometry.cc -// http://crbug.com/1023375 - -// Return true if ancestor is in the containing block chain above descendant. -bool IsContainingBlockChainDescendant(const LayoutObject* descendant, - const LayoutObject* ancestor) { - if (!ancestor || !descendant) - return false; - LocalFrame* ancestor_frame = ancestor->GetDocument().GetFrame(); - LocalFrame* descendant_frame = descendant->GetDocument().GetFrame(); - if (ancestor_frame != descendant_frame) - return false; - - while (descendant && descendant != ancestor) - descendant = descendant->ContainingBlock(); - return descendant; -} - -bool ElementBasedOffsetsEqual(ScrollTimelineElementBasedOffset* o1, - ScrollTimelineElementBasedOffset* o2) { - if (o1 == o2) - return true; - if (!o1 || !o2) - return false; - // TODO(crbug.com/1070871): Use targetOr(nullptr) after migration is done. - Element* target_or_null1 = o1->hasTarget() ? o1->target() : nullptr; - Element* target_or_null2 = o2->hasTarget() ? o2->target() : nullptr; - return target_or_null1 == target_or_null2 && o1->edge() == o2->edge() && - o1->threshold() == o2->threshold(); -} - -} // namespace - -// static -ScrollTimelineOffset* ScrollTimelineOffset::Create( - const V8ScrollTimelineOffset* offset) { - switch (offset->GetContentType()) { - case V8ScrollTimelineOffset::ContentType::kCSSKeywordValue: { - const auto* keyword = offset->GetAsCSSKeywordValue(); - if (keyword->KeywordValueID() != CSSValueID::kAuto) - return nullptr; - return MakeGarbageCollected<ScrollTimelineOffset>(); - } - case V8ScrollTimelineOffset::ContentType::kCSSNumericValue: { - const auto* value = - To<CSSPrimitiveValue>(offset->GetAsCSSNumericValue()->ToCSSValue()); - bool matches_length_percentage = - !value || value->IsLength() || value->IsPercentage() || - value->IsCalculatedPercentageWithLength(); - if (!matches_length_percentage) - return nullptr; - return MakeGarbageCollected<ScrollTimelineOffset>(value); - } - case V8ScrollTimelineOffset::ContentType:: - kScrollTimelineElementBasedOffset: { - auto* value = offset->GetAsScrollTimelineElementBasedOffset(); - if (!ValidateElementBasedOffset(value)) - return nullptr; - return MakeGarbageCollected<ScrollTimelineOffset>(value); - } - case V8ScrollTimelineOffset::ContentType::kString: { - if (offset->GetAsString().IsEmpty()) - return nullptr; - const auto* keyword = CSSKeywordValue::Create(offset->GetAsString()); - if (keyword->KeywordValueID() != CSSValueID::kAuto) - return nullptr; - return MakeGarbageCollected<ScrollTimelineOffset>(); - } - } - NOTREACHED(); - return nullptr; -} - -absl::optional<double> ScrollTimelineOffset::ResolveOffset( - Node* scroll_source, - ScrollOrientation orientation, - double max_offset, - double default_offset) { - const LayoutBox* root_box = scroll_source->GetLayoutBox(); - DCHECK(root_box); - Document& document = root_box->GetDocument(); - - if (length_based_offset_) { - // Resolve scroll based offset. - const ComputedStyle& computed_style = root_box->StyleRef(); - const ComputedStyle* root_style = - document.documentElement() - ? document.documentElement()->GetComputedStyle() - : document.GetComputedStyle(); - - // TOOD(crbug.com/1223030): Handle container relative units. - CSSToLengthConversionData conversion_data = CSSToLengthConversionData( - &computed_style, root_style, document.GetLayoutView(), - CSSToLengthConversionData::ContainerSizes(), - computed_style.EffectiveZoom()); - double resolved = FloatValueForLength( - length_based_offset_->ConvertToLength(conversion_data), max_offset); - - return resolved; - } else if (element_based_offset_) { - if (!element_based_offset_->hasTarget()) - return absl::nullopt; - Element* target = element_based_offset_->target(); - const LayoutBox* target_box = target->GetLayoutBox(); - - // It is possible for target to not have a layout box e.g., if it is an - // unattached element. In which case we return the default offset for now. - // - // See the spec discussion here: - // https://github.com/w3c/csswg-drafts/issues/4337#issuecomment-610997231 - if (!target_box) - return absl::nullopt; - - if (!IsContainingBlockChainDescendant(target_box, root_box)) - return absl::nullopt; - - PhysicalRect target_rect = target_box->PhysicalBorderBoxRect(); - target_rect = target_box->LocalToAncestorRect( - target_rect, root_box, - kTraverseDocumentBoundaries | kIgnoreScrollOffset); - - PhysicalRect root_rect(root_box->PhysicalBorderBoxRect()); - - LayoutUnit root_edge; - LayoutUnit target_edge; - - // Here is the simple diagram that shows the computation. - // - // +-----+ - // | | +------+ - // | | | | - // edge:start +----+-----+-------------------+-----+-------+ - // | |xxxxxx| |xxxxx| | - // | +------+ |xxxxx| | - // | +-----+ | - // | | - // threshold: | A) 0 B) 0.5 C) 1 | - // | | - // | +-----+ | - // | +------+ |xxxxx| | - // | |xxxxxx| |xxxxx| | - // edge: end +----+-----+-------------------+-----+-------+ - // | | | | - // | | +------+ - // +-----+ - // - // We always take the target top edge and compute the distance to the - // root's selected edge. This give us (C) in start edge case and (A) in - // end edge case. - // - // To take threshold into account we simply add (1-threshold) or threshold - // in start and end edge cases respectively. - bool is_start = element_based_offset_->edge() == "start"; - float threshold_adjustment = is_start - ? (1 - element_based_offset_->threshold()) - : element_based_offset_->threshold(); - - if (orientation == kVerticalScroll) { - root_edge = is_start ? root_rect.Y() : root_rect.Bottom(); - target_edge = target_rect.Y(); - // Note that threshold is considered as a portion of target and not as a - // portion of root. IntersectionObserver has option to allow both. - target_edge += (threshold_adjustment * target_rect.Height()); - } else { // kHorizontalScroll - root_edge = is_start ? root_rect.X() : root_rect.Right(); - target_edge = target_rect.X(); - target_edge += (threshold_adjustment * target_rect.Width()); - } - - LayoutUnit offset = target_edge - root_edge; - return std::min(std::max(offset.ToDouble(), 0.0), max_offset); - } else { - // Resolve the default case (i.e., 'auto' value) - return default_offset; - } -} - -V8ScrollTimelineOffset* ScrollTimelineOffset::ToV8ScrollTimelineOffset() const { - if (length_based_offset_) { - return MakeGarbageCollected<V8ScrollTimelineOffset>( - CSSNumericValue::FromCSSValue(*length_based_offset_.Get())); - } else if (element_based_offset_) { - return MakeGarbageCollected<V8ScrollTimelineOffset>(element_based_offset_); - } - // This is the default value (i.e., 'auto' value) - return MakeGarbageCollected<V8ScrollTimelineOffset>( - CSSKeywordValue::Create("auto")); -} - -bool ScrollTimelineOffset::operator==(const ScrollTimelineOffset& o) const { - return base::ValuesEquivalent(length_based_offset_, o.length_based_offset_) && - ElementBasedOffsetsEqual(element_based_offset_, - o.element_based_offset_); -} - -ScrollTimelineOffset::ScrollTimelineOffset(const CSSPrimitiveValue* offset) - : length_based_offset_(offset), element_based_offset_(nullptr) {} - -ScrollTimelineOffset::ScrollTimelineOffset( - ScrollTimelineElementBasedOffset* offset) - : length_based_offset_(nullptr), element_based_offset_(offset) {} - -void ScrollTimelineOffset::Trace(blink::Visitor* visitor) const { - visitor->Trace(length_based_offset_); - visitor->Trace(element_based_offset_); -} - -} // namespace blink
diff --git a/third_party/blink/renderer/core/animation/scroll_timeline_offset.h b/third_party/blink/renderer/core/animation/scroll_timeline_offset.h deleted file mode 100644 index 2021780..0000000 --- a/third_party/blink/renderer/core/animation/scroll_timeline_offset.h +++ /dev/null
@@ -1,70 +0,0 @@ -// Copyright 2020 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_ANIMATION_SCROLL_TIMELINE_OFFSET_H_ -#define THIRD_PARTY_BLINK_RENDERER_CORE_ANIMATION_SCROLL_TIMELINE_OFFSET_H_ - -#include "third_party/abseil-cpp/absl/types/optional.h" -#include "third_party/blink/renderer/bindings/core/v8/v8_scroll_timeline_element_based_offset.h" -#include "third_party/blink/renderer/bindings/core/v8/v8_typedefs.h" -#include "third_party/blink/renderer/core/core_export.h" -#include "third_party/blink/renderer/core/css/css_style_sheet.h" -#include "third_party/blink/renderer/core/css/properties/css_parsing_utils.h" -#include "third_party/blink/renderer/core/scroll/scroll_types.h" - -namespace blink { - -// Represent a scroll timeline start/end offset which can be an -// scroll offset or an element based offset -class CORE_EXPORT ScrollTimelineOffset final - : public GarbageCollected<ScrollTimelineOffset> { - public: - static ScrollTimelineOffset* Create(const V8ScrollTimelineOffset* offset); - - // Create a default offset representing 'auto'. - ScrollTimelineOffset() = default; - // Create a scroll based offset. - explicit ScrollTimelineOffset(const CSSPrimitiveValue*); - // Create an element based offset. - explicit ScrollTimelineOffset(ScrollTimelineElementBasedOffset*); - - void Trace(blink::Visitor*) const; - - // Resolves this offset against the scroll source and in the given orientation - // returning eqiuvalent concrete scroll offset. - // - // - Length-based values are converted into concrete length values resolving - // percentages and zoom factor. - // - Element-based values are resolved to the equivalent scroll offset that - // satisfy the requirement. - // - Auto value simply returns the |detfault_offset|. - // - // max offset is expected to be the maximum scroll offset in the scroll - // orientation. - // - // Returns nullopt if the offset cannot be resolved. - absl::optional<double> ResolveOffset(Node* scroll_source, - ScrollOrientation, - double max_offset, - double default_offset); - - V8ScrollTimelineOffset* ToV8ScrollTimelineOffset() const; - bool IsDefaultValue() const { - return !length_based_offset_ && !element_based_offset_; - } - - bool operator==(const ScrollTimelineOffset&) const; - bool operator!=(const ScrollTimelineOffset& o) const { return !(*this == o); } - - private: - // We either have an scroll or element based offset so at any time one of - // these is null. If both are null, it represents the default value of - // 'auto'. - Member<const CSSPrimitiveValue> length_based_offset_; - Member<ScrollTimelineElementBasedOffset> element_based_offset_; -}; - -} // namespace blink - -#endif // THIRD_PARTY_BLINK_RENDERER_CORE_ANIMATION_SCROLL_TIMELINE_OFFSET_H_
diff --git a/third_party/blink/renderer/core/animation/scroll_timeline_offset_test.cc b/third_party/blink/renderer/core/animation/scroll_timeline_offset_test.cc deleted file mode 100644 index 41b620e..0000000 --- a/third_party/blink/renderer/core/animation/scroll_timeline_offset_test.cc +++ /dev/null
@@ -1,88 +0,0 @@ -// Copyright 2020 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "third_party/blink/renderer/core/animation/scroll_timeline_offset.h" - -#include "testing/gtest/include/gtest/gtest.h" -#include "third_party/blink/renderer/bindings/core/v8/v8_union_csskeywordvalue_cssnumericvalue_scrolltimelineelementbasedoffset_string.h" -#include "third_party/blink/renderer/core/animation/animation_test_helpers.h" -#include "third_party/blink/renderer/core/animation/scroll_timeline_offset.h" -#include "third_party/blink/renderer/core/css/parser/css_parser_context.h" -#include "third_party/blink/renderer/core/dom/document.h" -#include "third_party/blink/renderer/core/html/html_element.h" -#include "third_party/blink/renderer/core/testing/page_test_base.h" - -namespace blink { - -class ScrollTimelineOffsetTest : public PageTestBase { - public: - ScrollTimelineOffset* ScrollBasedOffsetFrom(String string) { - return ScrollTimelineOffset::Create( - animation_test_helpers::OffsetFromString(GetDocument(), string)); - } - - ScrollTimelineOffset* ElementBasedOffsetFrom(Element* target, - String edge, - double threshold) { - auto* inner = CreateElementBasedOffset(target, edge, threshold); - if (!inner) - return nullptr; - auto* param = MakeGarbageCollected<V8ScrollTimelineOffset>(inner); - return ScrollTimelineOffset::Create(param); - } - - private: - ScrollTimelineElementBasedOffset* CreateElementBasedOffset(Element* target, - String edge, - double threshold) { - auto* value = ScrollTimelineElementBasedOffset::Create(); - value->setTarget(target); - value->setEdge(edge); - value->setThreshold(threshold); - return value; - } -}; - -TEST_F(ScrollTimelineOffsetTest, Equality) { - GetDocument().body()->setInnerHTML("<i id=e1></i><i id=e2></i>"); - UpdateAllLifecyclePhasesForTest(); - Element* e1 = GetDocument().getElementById("e1"); - Element* e2 = GetDocument().getElementById("e2"); - - ASSERT_TRUE(e1); - ASSERT_TRUE(e2); - - EXPECT_EQ(*ScrollBasedOffsetFrom("10px"), *ScrollBasedOffsetFrom("10px")); - EXPECT_EQ(*ScrollBasedOffsetFrom("10%"), *ScrollBasedOffsetFrom("10%")); - EXPECT_EQ(*ElementBasedOffsetFrom(e1, "start", 0), - *ElementBasedOffsetFrom(e1, "start", 0)); - - // Different types of offset: - EXPECT_NE(*ScrollBasedOffsetFrom("10px"), - *ElementBasedOffsetFrom(e1, "start", 0)); - EXPECT_NE(*ElementBasedOffsetFrom(e1, "start", 0), - *ScrollBasedOffsetFrom("10px")); - - // Different unit: - EXPECT_NE(*ScrollBasedOffsetFrom("10px"), *ScrollBasedOffsetFrom("10%")); - EXPECT_NE(*ScrollBasedOffsetFrom("10em"), *ScrollBasedOffsetFrom("10px")); - - // Different value: - EXPECT_NE(*ScrollBasedOffsetFrom("10em"), *ScrollBasedOffsetFrom("50em")); - EXPECT_NE(*ScrollBasedOffsetFrom("10px"), *ScrollBasedOffsetFrom("10.5px")); - - // Different target: - EXPECT_NE(*ElementBasedOffsetFrom(e1, "start", 0), - *ElementBasedOffsetFrom(e2, "start", 0)); - - // Different edge: - EXPECT_NE(*ElementBasedOffsetFrom(e1, "start", 0), - *ElementBasedOffsetFrom(e1, "end", 0)); - - // Different threshold: - EXPECT_NE(*ElementBasedOffsetFrom(e1, "start", 0), - *ElementBasedOffsetFrom(e1, "start", 1)); -} - -} // namespace blink
diff --git a/third_party/blink/renderer/core/animation/scroll_timeline_options.idl b/third_party/blink/renderer/core/animation/scroll_timeline_options.idl index e6d8ff2..6a219b8 100644 --- a/third_party/blink/renderer/core/animation/scroll_timeline_options.idl +++ b/third_party/blink/renderer/core/animation/scroll_timeline_options.idl
@@ -7,5 +7,4 @@ dictionary ScrollTimelineOptions { Element? source; ScrollDirection orientation = "block"; - sequence<ScrollTimelineOffset> scrollOffsets = []; };
diff --git a/third_party/blink/renderer/core/animation/scroll_timeline_test.cc b/third_party/blink/renderer/core/animation/scroll_timeline_test.cc index b7c8b1db..5bac6d7 100644 --- a/third_party/blink/renderer/core/animation/scroll_timeline_test.cc +++ b/third_party/blink/renderer/core/animation/scroll_timeline_test.cc
@@ -6,7 +6,6 @@ #include "testing/gtest/include/gtest/gtest.h" #include "third_party/blink/renderer/bindings/core/v8/v8_scroll_timeline_options.h" -#include "third_party/blink/renderer/bindings/core/v8/v8_union_csskeywordvalue_cssnumericvalue_scrolltimelineelementbasedoffset_string.h" #include "third_party/blink/renderer/core/animation/animation_clock.h" #include "third_party/blink/renderer/core/animation/animation_test_helpers.h" #include "third_party/blink/renderer/core/animation/document_animations.h" @@ -35,24 +34,6 @@ #define EXPECT_TIME_NEAR(expected, value) \ EXPECT_NEAR(expected, value, time_error_ms) -void ExpectVectorDoubleEqual(const WTF::Vector<double>& expected, - const WTF::Vector<double>& value) { - EXPECT_EQ(expected.size(), value.size()); - for (unsigned int i = 0; i < expected.size(); i++) - EXPECT_DOUBLE_EQ(expected[i], value[i]); -} - -HeapVector<Member<ScrollTimelineOffset>> CreateScrollOffsets( - ScrollTimelineOffset* start_scroll_offset = - MakeGarbageCollected<ScrollTimelineOffset>(), - ScrollTimelineOffset* end_scroll_offset = - MakeGarbageCollected<ScrollTimelineOffset>()) { - HeapVector<Member<ScrollTimelineOffset>> scroll_offsets; - scroll_offsets.push_back(start_scroll_offset); - scroll_offsets.push_back(end_scroll_offset); - return scroll_offsets; -} - Animation* CreateTestAnimation(AnimationTimeline* timeline) { Timing timing; timing.iteration_duration = ANIMATION_TIME_DELTA_FROM_SECONDS(0.1); @@ -87,23 +68,15 @@ } return count; } - - V8ScrollTimelineOffset* OffsetFromString(const String& value) { - return animation_test_helpers::OffsetFromString(GetDocument(), value); - } }; class TestScrollTimeline : public ScrollTimeline { public: - TestScrollTimeline(Document* document, - Element* source, - HeapVector<Member<ScrollTimelineOffset>> scroll_offsets = - CreateScrollOffsets()) + TestScrollTimeline(Document* document, Element* source) : ScrollTimeline(document, ScrollTimeline::ReferenceType::kSource, source, - ScrollTimeline::kVertical, - std::move(scroll_offsets)), + ScrollTimeline::kVertical), next_service_scheduled_(false) {} void ScheduleServiceOnNextFrame() override { @@ -140,104 +113,6 @@ } TEST_F(ScrollTimelineTest, - CurrentTimeIsNullIfScrollOffsetIsBeyondStartAndEndScrollOffset) { - SetBodyInnerHTML(R"HTML( - <style> - #scroller { overflow: scroll; width: 100px; height: 100px; } - #spacer { height: 1000px; } - </style> - <div id='scroller'> - <div id ='spacer'></div> - </div> - )HTML"); - - auto* scroller = - To<LayoutBoxModelObject>(GetLayoutObjectByElementId("scroller")); - ASSERT_TRUE(scroller); - ASSERT_TRUE(scroller->IsScrollContainer()); - PaintLayerScrollableArea* scrollable_area = scroller->GetScrollableArea(); - ASSERT_TRUE(scrollable_area); - ScrollTimelineOptions* options = ScrollTimelineOptions::Create(); - options->setSource(GetElementById("scroller")); - options->setScrollOffsets( - {OffsetFromString("10px"), OffsetFromString("90px")}); - ScrollTimeline* scroll_timeline = - ScrollTimeline::Create(GetDocument(), options, ASSERT_NO_EXCEPTION); - - scrollable_area->SetScrollOffset(ScrollOffset(0, 5), - mojom::blink::ScrollType::kProgrammatic); - // Simulate a new animation frame which allows the timeline to compute new - // current time. - SimulateFrame(); - EXPECT_EQ(scroll_timeline->CurrentTimeSeconds(), 0); - - scrollable_area->SetScrollOffset(ScrollOffset(0, 10), - mojom::blink::ScrollType::kProgrammatic); - SimulateFrame(); - EXPECT_EQ(scroll_timeline->CurrentTimeSeconds(), 0); - - scrollable_area->SetScrollOffset(ScrollOffset(0, 50), - mojom::blink::ScrollType::kProgrammatic); - SimulateFrame(); - EXPECT_EQ(scroll_timeline->CurrentTimeSeconds(), 50); - - scrollable_area->SetScrollOffset(ScrollOffset(0, 90), - mojom::blink::ScrollType::kProgrammatic); - SimulateFrame(); - EXPECT_EQ(scroll_timeline->CurrentTime(), scroll_timeline->GetDuration()); - - scrollable_area->SetScrollOffset(ScrollOffset(0, 100), - mojom::blink::ScrollType::kProgrammatic); - SimulateFrame(); - EXPECT_EQ(scroll_timeline->CurrentTime(), scroll_timeline->GetDuration()); - EXPECT_TRUE(scroll_timeline->IsActive()); -} - -TEST_F(ScrollTimelineTest, - CurrentTimeIsNullIfEndScrollOffsetIsLessThanStartScrollOffset) { - SetBodyInnerHTML(R"HTML( - <style> - #scroller { overflow: scroll; width: 100px; height: 100px; } - #spacer { height: 1000px; } - </style> - <div id='scroller'> - <div id ='spacer'></div> - </div> - )HTML"); - - auto* scroller = - To<LayoutBoxModelObject>(GetLayoutObjectByElementId("scroller")); - ASSERT_TRUE(scroller); - ASSERT_TRUE(scroller->IsScrollContainer()); - PaintLayerScrollableArea* scrollable_area = scroller->GetScrollableArea(); - ASSERT_TRUE(scrollable_area); - ScrollTimelineOptions* options = ScrollTimelineOptions::Create(); - options->setSource(GetElementById("scroller")); - options->setScrollOffsets( - {OffsetFromString("80px"), OffsetFromString("40px")}); - ScrollTimeline* scroll_timeline = - ScrollTimeline::Create(GetDocument(), options, ASSERT_NO_EXCEPTION); - - scrollable_area->SetScrollOffset(ScrollOffset(0, 20), - mojom::blink::ScrollType::kProgrammatic); - // Simulate a new animation frame which allows the timeline to compute new - // current time. - SimulateFrame(); - EXPECT_EQ(0, scroll_timeline->CurrentTimeSeconds()); - - scrollable_area->SetScrollOffset(ScrollOffset(0, 60), - mojom::blink::ScrollType::kProgrammatic); - SimulateFrame(); - EXPECT_EQ(0, scroll_timeline->CurrentTimeSeconds()); - - scrollable_area->SetScrollOffset(ScrollOffset(0, 100), - mojom::blink::ScrollType::kProgrammatic); - SimulateFrame(); - EXPECT_EQ(scroll_timeline->CurrentTime(), scroll_timeline->GetDuration()); - EXPECT_TRUE(scroll_timeline->IsActive()); -} - -TEST_F(ScrollTimelineTest, UsingDocumentScrollingElementShouldCorrectlyResolveToDocument) { SetBodyInnerHTML(R"HTML( <style> @@ -299,7 +174,7 @@ Persistent<ScrollTimeline> scroll_timeline = MakeGarbageCollected<ScrollTimeline>( &GetDocument(), ScrollTimeline::ReferenceType::kSource, scroll_source, - ScrollTimeline::kBlock, CreateScrollOffsets()); + ScrollTimeline::kBlock); // Sanity checks. ASSERT_EQ(scroll_timeline->source(), nullptr); @@ -546,10 +421,8 @@ // Use empty offsets as 'auto'. TestScrollTimeline* scroll_timeline = - MakeGarbageCollected<TestScrollTimeline>( - &GetDocument(), scroller_element, - CreateScrollOffsets(MakeGarbageCollected<ScrollTimelineOffset>(), - MakeGarbageCollected<ScrollTimelineOffset>())); + MakeGarbageCollected<TestScrollTimeline>(&GetDocument(), + scroller_element); NonThrowableExceptionState exception_state; Timing timing; timing.iteration_duration = ANIMATION_TIME_DELTA_FROM_SECONDS(30); @@ -592,10 +465,8 @@ // Use empty offsets as 'auto'. TestScrollTimeline* scroll_timeline = - MakeGarbageCollected<TestScrollTimeline>( - &GetDocument(), scroller_element, - CreateScrollOffsets(MakeGarbageCollected<ScrollTimelineOffset>(), - MakeGarbageCollected<ScrollTimelineOffset>())); + MakeGarbageCollected<TestScrollTimeline>(&GetDocument(), + scroller_element); NonThrowableExceptionState exception_state; Timing timing; timing.iteration_duration = ANIMATION_TIME_DELTA_FROM_SECONDS(30); @@ -688,6 +559,7 @@ scroll_timeline, exception_state); scroll_animation->play(); UpdateAllLifecyclePhasesForTest(); + // Scroll to finished state. scrollable_area->SetScrollOffset(ScrollOffset(0, 100), mojom::blink::ScrollType::kProgrammatic); @@ -700,7 +572,7 @@ EXPECT_EQ(1u, scroll_timeline->AnimationsNeedingUpdateCount()); // Scroll back. - scrollable_area->SetScrollOffset(ScrollOffset(0, 80), + scrollable_area->SetScrollOffset(ScrollOffset(0, 50), mojom::blink::ScrollType::kProgrammatic); SimulateFrame(); // Verify that the animation as back to running. @@ -823,220 +695,6 @@ EXPECT_FALSE(event_listener->EventReceived()); } -TEST_F(ScrollTimelineTest, ResolveScrollOffsets) { - SetBodyInnerHTML(R"HTML( - <style> - #scroller { overflow: scroll; width: 100px; height: 100px; } - #spacer { height: 1000px; } - </style> - <div id='scroller'> - <div id ='spacer'></div> - </div> - )HTML"); - - auto* scroller = - To<LayoutBoxModelObject>(GetLayoutObjectByElementId("scroller")); - ASSERT_TRUE(scroller); - PaintLayerScrollableArea* scrollable_area = scroller->GetScrollableArea(); - ASSERT_TRUE(scrollable_area); - ScrollTimelineOptions* options = ScrollTimelineOptions::Create(); - options->setSource(GetElementById("scroller")); - // Empty scroll offsets resolve into [0, 100%]. - HeapVector<Member<V8ScrollTimelineOffset>> scroll_offsets = {}; - options->setScrollOffsets(scroll_offsets); - - ScrollTimeline* scroll_timeline = - ScrollTimeline::Create(GetDocument(), options, ASSERT_NO_EXCEPTION); - - WTF::Vector<double> resolved_offsets; - WTF::Vector<double> expected_offsets = {0, 900.0}; - scroll_timeline->ResolveScrollOffsets(resolved_offsets); - ExpectVectorDoubleEqual(expected_offsets, resolved_offsets); - - // Single 'auto' offset resolve into [0, 100%]. - scroll_offsets = {OffsetFromString("auto")}; - options->setScrollOffsets(scroll_offsets); - scroll_timeline = - ScrollTimeline::Create(GetDocument(), options, ASSERT_NO_EXCEPTION); - resolved_offsets.clear(); - scroll_timeline->ResolveScrollOffsets(resolved_offsets); - expected_offsets = {0, 900.0}; - ExpectVectorDoubleEqual(expected_offsets, resolved_offsets); - - // Start and end 'auto' offsets resolve into [0, 100%]. - scroll_offsets = {OffsetFromString("auto"), OffsetFromString("auto")}; - options->setScrollOffsets(scroll_offsets); - scroll_timeline = - ScrollTimeline::Create(GetDocument(), options, ASSERT_NO_EXCEPTION); - resolved_offsets.clear(); - scroll_timeline->ResolveScrollOffsets(resolved_offsets); - expected_offsets = {0, 900.0}; - ExpectVectorDoubleEqual(expected_offsets, resolved_offsets); - - // Three offsets, start and end are 'auto' resolve into [0, middle offset, - // 100%]. - scroll_offsets = {OffsetFromString("auto"), OffsetFromString("500px"), - OffsetFromString("auto")}; - options->setScrollOffsets(scroll_offsets); - scroll_timeline = - ScrollTimeline::Create(GetDocument(), options, ASSERT_NO_EXCEPTION); - resolved_offsets.clear(); - scroll_timeline->ResolveScrollOffsets(resolved_offsets); - expected_offsets = {0, 500.0, 900.0}; - ExpectVectorDoubleEqual(expected_offsets, resolved_offsets); -} - -TEST_F(ScrollTimelineTest, MultipleScrollOffsetsCurrentTimeCalculations) { - SetBodyInnerHTML(R"HTML( - <style> - #scroller { overflow: scroll; width: 100px; height: 100px; } - #spacer { height: 1000px; } - </style> - <div id='scroller'> - <div id ='spacer'></div> - </div> - )HTML"); - - auto* scroller = - To<LayoutBoxModelObject>(GetLayoutObjectByElementId("scroller")); - ASSERT_TRUE(scroller); - ASSERT_TRUE(scroller->IsScrollContainer()); - PaintLayerScrollableArea* scrollable_area = scroller->GetScrollableArea(); - ASSERT_TRUE(scrollable_area); - ScrollTimelineOptions* options = ScrollTimelineOptions::Create(); - options->setSource(GetElementById("scroller")); - HeapVector<Member<V8ScrollTimelineOffset>> scroll_offsets; - scroll_offsets.push_back(OffsetFromString("10px")); - scroll_offsets.push_back(OffsetFromString("20px")); - scroll_offsets.push_back(OffsetFromString("40px")); - scroll_offsets.push_back(OffsetFromString("90px")); - options->setScrollOffsets(scroll_offsets); - - ScrollTimeline* scroll_timeline = - ScrollTimeline::Create(GetDocument(), options, ASSERT_NO_EXCEPTION); - - EXPECT_EQ(scroll_timeline->CurrentTimeSeconds(), 0); - - scrollable_area->SetScrollOffset(ScrollOffset(0, 10), - mojom::blink::ScrollType::kProgrammatic); - // Simulate a new animation frame which allows the timeline to compute new - // current phase and time. - SimulateFrame(); - EXPECT_EQ(0, scroll_timeline->CurrentTimeSeconds().value()); - - scrollable_area->SetScrollOffset(ScrollOffset(0, 12), - mojom::blink::ScrollType::kProgrammatic); - SimulateFrame(); - - unsigned int offset = 0; - double w = 1.0 / 3.0; // offset weight - double p = (12.0 - 10.0) / (20.0 - 10.0); // progress within the offset - double duration = 100.0; - EXPECT_TIME_NEAR((offset + p) * w * duration, - scroll_timeline->CurrentTimeSeconds().value()); - - scrollable_area->SetScrollOffset(ScrollOffset(0, 20), - mojom::blink::ScrollType::kProgrammatic); - SimulateFrame(); - offset = 1; - p = 0; - EXPECT_TIME_NEAR((offset + p) * w * duration, - scroll_timeline->CurrentTimeSeconds().value()); - - scrollable_area->SetScrollOffset(ScrollOffset(0, 30), - mojom::blink::ScrollType::kProgrammatic); - SimulateFrame(); - p = (30.0 - 20.0) / (40.0 - 20.0); - EXPECT_TIME_NEAR((offset + p) * w * duration, - scroll_timeline->CurrentTimeSeconds().value()); - - scrollable_area->SetScrollOffset(ScrollOffset(0, 40), - mojom::blink::ScrollType::kProgrammatic); - SimulateFrame(); - offset = 2; - p = 0; - EXPECT_TIME_NEAR((offset + p) * w * duration, - scroll_timeline->CurrentTimeSeconds().value()); - - scrollable_area->SetScrollOffset(ScrollOffset(0, 80), - mojom::blink::ScrollType::kProgrammatic); - SimulateFrame(); - p = (80.0 - 40.0) / (90.0 - 40.0); - EXPECT_TIME_NEAR((offset + p) * w * duration, - scroll_timeline->CurrentTimeSeconds().value()); - - scrollable_area->SetScrollOffset(ScrollOffset(0, 90), - mojom::blink::ScrollType::kProgrammatic); - SimulateFrame(); - EXPECT_EQ(100, scroll_timeline->CurrentTimeSeconds().value()); - - scrollable_area->SetScrollOffset(ScrollOffset(0, 100), - mojom::blink::ScrollType::kProgrammatic); - SimulateFrame(); - EXPECT_EQ(100, scroll_timeline->CurrentTimeSeconds().value()); -} - -TEST_F(ScrollTimelineTest, OverlappingScrollOffsets) { - SetBodyInnerHTML(R"HTML( - <style> - #scroller { overflow: scroll; width: 100px; height: 100px; } - #spacer { height: 1000px; } - </style> - <div id='scroller'> - <div id ='spacer'></div> - </div> - )HTML"); - - auto* scroller = - To<LayoutBoxModelObject>(GetLayoutObjectByElementId("scroller")); - ASSERT_TRUE(scroller); - PaintLayerScrollableArea* scrollable_area = scroller->GetScrollableArea(); - ASSERT_TRUE(scrollable_area); - ScrollTimelineOptions* options = ScrollTimelineOptions::Create(); - options->setSource(GetElementById("scroller")); - HeapVector<Member<V8ScrollTimelineOffset>> scroll_offsets = { - OffsetFromString("90px"), OffsetFromString("40px"), - OffsetFromString("10px")}; - options->setScrollOffsets(scroll_offsets); - - ScrollTimeline* scroll_timeline = - ScrollTimeline::Create(GetDocument(), options, ASSERT_NO_EXCEPTION); - - scrollable_area->SetScrollOffset(ScrollOffset(0, 80), - mojom::blink::ScrollType::kProgrammatic); - SimulateFrame(); - EXPECT_EQ(0, scroll_timeline->CurrentTimeSeconds().value()); - - scrollable_area->SetScrollOffset(ScrollOffset(0, 95), - mojom::blink::ScrollType::kProgrammatic); - SimulateFrame(); - EXPECT_EQ(100, scroll_timeline->CurrentTimeSeconds().value()); - - scroll_offsets = {OffsetFromString("0px"), OffsetFromString("100px"), - OffsetFromString("50px")}; - options->setScrollOffsets(scroll_offsets); - - scroll_timeline = - ScrollTimeline::Create(GetDocument(), options, ASSERT_NO_EXCEPTION); - - scrollable_area->SetScrollOffset(ScrollOffset(0, 40), - mojom::blink::ScrollType::kProgrammatic); - SimulateFrame(); - EXPECT_EQ(20, scroll_timeline->CurrentTimeSeconds().value()); - - scroll_offsets = {OffsetFromString("50px"), OffsetFromString("0px"), - OffsetFromString("100px")}; - options->setScrollOffsets(scroll_offsets); - - scroll_timeline = - ScrollTimeline::Create(GetDocument(), options, ASSERT_NO_EXCEPTION); - - scrollable_area->SetScrollOffset(ScrollOffset(0, 60), - mojom::blink::ScrollType::kProgrammatic); - SimulateFrame(); - EXPECT_EQ(80, scroll_timeline->CurrentTimeSeconds().value()); -} - TEST_F(ScrollTimelineTest, WeakReferences) { SetBodyInnerHTML(R"HTML( <style>
diff --git a/third_party/blink/renderer/core/animation/scroll_timeline_util.cc b/third_party/blink/renderer/core/animation/scroll_timeline_util.cc index 070097c..24e0de6cd 100644 --- a/third_party/blink/renderer/core/animation/scroll_timeline_util.cc +++ b/third_party/blink/renderer/core/animation/scroll_timeline_util.cc
@@ -95,12 +95,6 @@ : CompositorScrollTimeline::ScrollUp; } -double ComputeProgress(double current_offset, - const WTF::Vector<double>& resolved_offsets) { - return cc::ComputeProgress<WTF::Vector<double>>(current_offset, - resolved_offsets); -} - } // namespace scroll_timeline_util } // namespace blink
diff --git a/third_party/blink/renderer/core/animation/scroll_timeline_util.h b/third_party/blink/renderer/core/animation/scroll_timeline_util.h index d53c61f..b0ff626 100644 --- a/third_party/blink/renderer/core/animation/scroll_timeline_util.h +++ b/third_party/blink/renderer/core/animation/scroll_timeline_util.h
@@ -14,6 +14,7 @@ namespace blink { using CompositorScrollTimeline = cc::ScrollTimeline; +using ScrollOffsets = cc::ScrollTimeline::ScrollOffsets; class AnimationTimeline; class ComputedStyle; @@ -38,8 +39,7 @@ CompositorScrollTimeline::ScrollDirection CORE_EXPORT ConvertOrientation(ScrollTimeline::ScrollDirection, const ComputedStyle*); -double ComputeProgress(double current_offset, - const WTF::Vector<double>& resolved_offsets); +absl::optional<ScrollOffsets> CreateScrollOffsets(ScrollTimeline* timeline); } // namespace scroll_timeline_util
diff --git a/third_party/blink/renderer/core/animation/scroll_timeline_util_test.cc b/third_party/blink/renderer/core/animation/scroll_timeline_util_test.cc index aa47b9b..dc50248 100644 --- a/third_party/blink/renderer/core/animation/scroll_timeline_util_test.cc +++ b/third_party/blink/renderer/core/animation/scroll_timeline_util_test.cc
@@ -6,7 +6,6 @@ #include "testing/gtest/include/gtest/gtest.h" #include "third_party/blink/renderer/bindings/core/v8/v8_scroll_timeline_options.h" -#include "third_party/blink/renderer/bindings/core/v8/v8_union_csskeywordvalue_cssnumericvalue_scrolltimelineelementbasedoffset_string.h" #include "third_party/blink/renderer/core/animation/animation_test_helpers.h" #include "third_party/blink/renderer/core/animation/document_timeline.h" #include "third_party/blink/renderer/core/css/resolver/style_resolver.h" @@ -16,29 +15,15 @@ namespace blink { -namespace { - -HeapVector<Member<ScrollTimelineOffset>> CreateScrollOffsets( - ScrollTimelineOffset* start_scroll_offset, - ScrollTimelineOffset* end_scroll_offset) { - HeapVector<Member<ScrollTimelineOffset>> scroll_offsets; - scroll_offsets.push_back(start_scroll_offset); - scroll_offsets.push_back(end_scroll_offset); - return scroll_offsets; -} - -} // namespace - namespace scroll_timeline_util { using ScrollTimelineUtilTest = PageTestBase; // This test covers only the basic conversions for element id, time range, -// orientation, and start and end scroll offset. Complex orientation conversions -// are tested in the GetOrientation* tests, and complex start/end scroll offset -// resolutions are tested in blink::ScrollTimelineTest. +// and orientation. Complex orientation conversions are tested in the +// GetOrientation* tests. TEST_F(ScrollTimelineUtilTest, ToCompositorScrollTimeline) { - using animation_test_helpers::OffsetFromString; + // using animation_test_helpers::OffsetFromString; SetBodyInnerHTML(R"HTML( <style> @@ -62,8 +47,6 @@ ScrollTimelineOptions* options = ScrollTimelineOptions::Create(); options->setSource(scroller); options->setOrientation("block"); - options->setScrollOffsets({OffsetFromString(GetDocument(), "50px"), - OffsetFromString(GetDocument(), "auto")}); ScrollTimeline* timeline = ScrollTimeline::Create(GetDocument(), options, ASSERT_NO_EXCEPTION); @@ -73,9 +56,6 @@ EXPECT_EQ(compositor_timeline->GetPendingIdForTest(), element_id); EXPECT_EQ(compositor_timeline->GetDirectionForTest(), CompositorScrollTimeline::ScrollDown); - EXPECT_EQ(compositor_timeline->GetStartScrollOffsetForTest(), 50); - // 900 is contents-size - scroller-viewport == 1000 - 100 - EXPECT_EQ(compositor_timeline->GetEndScrollOffsetForTest(), 900); } TEST_F(ScrollTimelineUtilTest, ToCompositorScrollTimelineNullParameter) { @@ -94,14 +74,9 @@ // source. The alternative approach would require us to remove the // documentElement from the document. Element* source = nullptr; - ScrollTimelineOffset* start_scroll_offset = - MakeGarbageCollected<ScrollTimelineOffset>(); - ScrollTimelineOffset* end_scroll_offset = - MakeGarbageCollected<ScrollTimelineOffset>(); ScrollTimeline* timeline = MakeGarbageCollected<ScrollTimeline>( &GetDocument(), ScrollTimeline::ReferenceType::kSource, source, - ScrollTimeline::kBlock, - CreateScrollOffsets(start_scroll_offset, end_scroll_offset)); + ScrollTimeline::kBlock); scoped_refptr<CompositorScrollTimeline> compositor_timeline = ToCompositorScrollTimeline(timeline); @@ -121,11 +96,6 @@ scoped_refptr<CompositorScrollTimeline> compositor_timeline = ToCompositorScrollTimeline(timeline); EXPECT_TRUE(compositor_timeline.get()); - // Here we just want to test the start/end scroll offset. - // ToCompositorScrollTimelineNullSource covers the expected pending id - // and ConvertOrientationNullStyle covers the orientation conversion. - EXPECT_EQ(compositor_timeline->GetStartScrollOffsetForTest(), absl::nullopt); - EXPECT_EQ(compositor_timeline->GetEndScrollOffsetForTest(), absl::nullopt); } TEST_F(ScrollTimelineUtilTest, ConvertOrientationPhysicalCases) {
diff --git a/third_party/blink/renderer/core/animation/timing.cc b/third_party/blink/renderer/core/animation/timing.cc index 8dead5bb..e39f1bb 100644 --- a/third_party/blink/renderer/core/animation/timing.cc +++ b/third_party/blink/renderer/core/animation/timing.cc
@@ -176,7 +176,6 @@ Timing::CalculatedTiming Timing::CalculateTimings( absl::optional<AnimationTimeDelta> local_time, - absl::optional<Phase> timeline_phase, bool at_progress_timeline_boundary, const NormalizedTiming& normalized_timing, AnimationDirection animation_direction, @@ -186,7 +185,7 @@ const AnimationTimeDelta duration = normalized_timing.iteration_duration; Timing::Phase current_phase = - CalculatePhase(normalized_timing, local_time, timeline_phase, + CalculatePhase(normalized_timing, local_time, at_progress_timeline_boundary, animation_direction); const absl::optional<AnimationTimeDelta> active_time = CalculateActiveTime(
diff --git a/third_party/blink/renderer/core/animation/timing.h b/third_party/blink/renderer/core/animation/timing.h index 2c1b9f3..4ce9c90 100644 --- a/third_party/blink/renderer/core/animation/timing.h +++ b/third_party/blink/renderer/core/animation/timing.h
@@ -184,7 +184,6 @@ CalculatedTiming CalculateTimings( absl::optional<AnimationTimeDelta> local_time, - absl::optional<Phase> timeline_phase, bool at_progress_timeline_boundary, const NormalizedTiming& normalized_timing, AnimationDirection animation_direction,
diff --git a/third_party/blink/renderer/core/animation/timing_calculations.h b/third_party/blink/renderer/core/animation/timing_calculations.h index 2e32511..2235c33 100644 --- a/third_party/blink/renderer/core/animation/timing_calculations.h +++ b/third_party/blink/renderer/core/animation/timing_calculations.h
@@ -112,7 +112,6 @@ static inline Timing::Phase CalculatePhase( const Timing::NormalizedTiming& normalized, absl::optional<AnimationTimeDelta> local_time, - absl::optional<Timing::Phase> timeline_phase, bool at_progress_timeline_boundary, Timing::AnimationDirection direction) { DCHECK(GreaterThanOrEqualToWithinTimeTolerance(normalized.active_duration, @@ -123,27 +122,21 @@ AnimationTimeDelta before_active_boundary_time = std::max(std::min(normalized.start_delay, normalized.end_time), AnimationTimeDelta()); - - if ((timeline_phase && timeline_phase.value() == Timing::kPhaseBefore) || - ((!timeline_phase || - (timeline_phase && timeline_phase.value() == Timing::kPhaseActive)) && - (local_time.value() < before_active_boundary_time || - (direction == Timing::AnimationDirection::kBackwards && - local_time.value() == before_active_boundary_time && - !at_progress_timeline_boundary)))) { + if (local_time.value() < before_active_boundary_time || + (direction == Timing::AnimationDirection::kBackwards && + local_time.value() == before_active_boundary_time && + !at_progress_timeline_boundary)) { return Timing::kPhaseBefore; } + AnimationTimeDelta active_after_boundary_time = std::max(std::min(normalized.start_delay + normalized.active_duration, normalized.end_time), AnimationTimeDelta()); - if ((timeline_phase && timeline_phase.value() == Timing::kPhaseAfter) || - ((!timeline_phase || - (timeline_phase && timeline_phase.value() == Timing::kPhaseActive)) && - (local_time.value() > active_after_boundary_time || - (direction == Timing::AnimationDirection::kForwards && - local_time.value() == active_after_boundary_time && - !at_progress_timeline_boundary)))) { + if (local_time.value() > active_after_boundary_time || + (direction == Timing::AnimationDirection::kForwards && + local_time.value() == active_after_boundary_time && + !at_progress_timeline_boundary)) { return Timing::kPhaseAfter; } return Timing::kPhaseActive;
diff --git a/third_party/blink/renderer/core/animation/timing_test.cc b/third_party/blink/renderer/core/animation/timing_test.cc index 795d92d..ae3157b91 100644 --- a/third_party/blink/renderer/core/animation/timing_test.cc +++ b/third_party/blink/renderer/core/animation/timing_test.cc
@@ -17,10 +17,10 @@ Timing::AnimationDirection animation_direction = playback_rate < 0 ? Timing::AnimationDirection::kBackwards : Timing::AnimationDirection::kForwards; - return timing_.CalculateTimings( - local_time, /*timeline_phase*/ absl::nullopt, - /* at_progress_timeline_boundary */ false, normalized_timing_, - animation_direction, is_keyframe_effect, playback_rate); + return timing_.CalculateTimings(local_time, + /* at_progress_timeline_boundary */ false, + normalized_timing_, animation_direction, + is_keyframe_effect, playback_rate); } bool IsCurrent(absl::optional<double> local_time, double playback_rate) { absl::optional<AnimationTimeDelta> local_time_delta;
diff --git a/third_party/blink/renderer/core/animation/view_timeline.cc b/third_party/blink/renderer/core/animation/view_timeline.cc index 97e1fd1..b75685a 100644 --- a/third_party/blink/renderer/core/animation/view_timeline.cc +++ b/third_party/blink/renderer/core/animation/view_timeline.cc
@@ -24,23 +24,15 @@ return nullptr; } - // TODO(crbug.com/1329159): Remove scroll_offsets. Currently needed for - // the ScrollTimeline constructor. - HeapVector<Member<ScrollTimelineOffset>> scroll_offsets; - - return MakeGarbageCollected<ViewTimeline>(&document, subject, orientation, - scroll_offsets); + return MakeGarbageCollected<ViewTimeline>(&document, subject, orientation); } -ViewTimeline::ViewTimeline( - Document* document, - Element* subject, - ScrollDirection orientation, - HeapVector<Member<ScrollTimelineOffset>> scroll_offsets) +ViewTimeline::ViewTimeline(Document* document, + Element* subject, + ScrollDirection orientation) : ScrollTimeline(document, ReferenceType::kNearestAncestor, subject, - orientation, - scroll_offsets) {} + orientation) {} } // namespace blink
diff --git a/third_party/blink/renderer/core/animation/view_timeline.h b/third_party/blink/renderer/core/animation/view_timeline.h index 7812a6f..18a4912 100644 --- a/third_party/blink/renderer/core/animation/view_timeline.h +++ b/third_party/blink/renderer/core/animation/view_timeline.h
@@ -29,13 +29,7 @@ public: static ViewTimeline* Create(Document&, ViewTimelineOptions*, ExceptionState&); - // TODO(crbug.com/1329159): Add additional arguments as ratified. - // TODO(crbug.com/1329159): Remove scroll-offsets. Presently needed for the - // ScrollTimeline constructor. - ViewTimeline(Document*, - Element* subject, - ScrollDirection orientation, - HeapVector<Member<ScrollTimelineOffset>> scroll_offsets); + ViewTimeline(Document*, Element* subject, ScrollDirection orientation); bool IsViewTimeline() const override { return true; }
diff --git a/third_party/blink/renderer/core/document_transition/document_transition_style_tracker.cc b/third_party/blink/renderer/core/document_transition/document_transition_style_tracker.cc index 94f5df3..29f2a68 100644 --- a/third_party/blink/renderer/core/document_transition/document_transition_style_tracker.cc +++ b/third_party/blink/renderer/core/document_transition/document_transition_style_tracker.cc
@@ -4,6 +4,8 @@ #include "third_party/blink/renderer/core/document_transition/document_transition_style_tracker.h" +#include <limits> + #include "components/viz/common/shared_element_resource_id.h" #include "third_party/blink/public/resources/grit/blink_resources.h" #include "third_party/blink/renderer/core/animation/element_animations.h" @@ -416,7 +418,6 @@ element_data->cached_border_box_size_in_css_space = element_data->border_box_size_in_css_space; element_data->cached_viewport_matrix = element_data->viewport_matrix; - element_data->cached_device_pixel_ratio = element_data->device_pixel_ratio; element_data->cached_visual_overflow_rect_in_layout_space = element_data->visual_overflow_rect_in_layout_space; element_data->effect_node = nullptr; @@ -685,7 +686,7 @@ continue; } - float device_pixel_ratio = document_->DevicePixelRatio(); + const float device_pixel_ratio = document_->DevicePixelRatio(); TransformationMatrix viewport_matrix = layout_object->LocalToAbsoluteTransform(); viewport_matrix.Zoom(1.0 / device_pixel_ratio); @@ -701,6 +702,11 @@ LayoutUnit(entry_size->blockSize())) : LayoutSize(LayoutUnit(entry_size->blockSize()), LayoutUnit(entry_size->inlineSize())); + if (float effective_zoom = layout_object->StyleRef().EffectiveZoom(); + std::abs(effective_zoom - device_pixel_ratio) >= + std::numeric_limits<float>::epsilon()) { + border_box_size_in_css_space.Scale(effective_zoom / device_pixel_ratio); + } PhysicalRect visual_overflow_rect_in_layout_space; if (auto* box = DynamicTo<LayoutBox>(layout_object)) @@ -711,7 +717,6 @@ if (viewport_matrix == element_data->viewport_matrix && border_box_size_in_css_space == element_data->border_box_size_in_css_space && - device_pixel_ratio == element_data->device_pixel_ratio && visual_overflow_rect_in_layout_space == element_data->visual_overflow_rect_in_layout_space && writing_mode == element_data->container_writing_mode) { @@ -720,7 +725,6 @@ element_data->viewport_matrix = viewport_matrix; element_data->border_box_size_in_css_space = border_box_size_in_css_space; - element_data->device_pixel_ratio = device_pixel_ratio; element_data->visual_overflow_rect_in_layout_space = visual_overflow_rect_in_layout_space; element_data->container_writing_mode = writing_mode; @@ -962,6 +966,7 @@ })CSS"); } + float device_pixel_ratio = document_->DevicePixelRatio(); for (auto& entry : element_data_map_) { const auto& document_transition_tag = entry.key.GetString(); auto& element_data = entry.value; @@ -985,8 +990,7 @@ height: %dpx; transform: %s; writing-mode: %s; - } - )CSS", + })CSS", border_box_in_css_space.width(), border_box_in_css_space.height(), ComputedStyleUtils::ValueForTransformationMatrix( element_data->viewport_matrix, 1, false) @@ -995,7 +999,6 @@ .c_str(), writing_mode_stream.str().c_str()); - float device_pixel_ratio = document_->DevicePixelRatio(); absl::optional<String> incoming_inset = ComputeInsetDifference( element_data->visual_overflow_rect_in_layout_space, border_box_in_css_space, device_pixel_ratio); @@ -1005,8 +1008,7 @@ builder.AppendFormat( R"CSS({ object-view-box: %s; - } - )CSS", + })CSS", incoming_inset->Utf8().c_str()); } @@ -1019,8 +1021,7 @@ builder.AppendFormat( R"CSS({ object-view-box: %s; - } - )CSS", + })CSS", outgoing_inset->Utf8().c_str()); } @@ -1039,8 +1040,7 @@ width: %dpx; height: %dpx; } - } - )CSS", + })CSS", ComputedStyleUtils::ValueForTransformationMatrix( element_data->cached_viewport_matrix, 1, false) ->CssText()
diff --git a/third_party/blink/renderer/core/document_transition/document_transition_style_tracker.h b/third_party/blink/renderer/core/document_transition/document_transition_style_tracker.h index 0fc5fc3d..8ca40a6 100644 --- a/third_party/blink/renderer/core/document_transition/document_transition_style_tracker.h +++ b/third_party/blink/renderer/core/document_transition/document_transition_style_tracker.h
@@ -142,12 +142,10 @@ // |target_element|. This information is mirrored into the UA stylesheet. LayoutSize border_box_size_in_css_space; TransformationMatrix viewport_matrix; - float device_pixel_ratio = 1.f; // Computed info cached before the DOM switches to the new state. LayoutSize cached_border_box_size_in_css_space; TransformationMatrix cached_viewport_matrix; - float cached_device_pixel_ratio = 1.f; // Valid if there is an element in the old DOM generating a snapshot. viz::SharedElementResourceId old_snapshot_id;
diff --git a/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.cc index decd35c..cbc618f6 100644 --- a/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.cc +++ b/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.cc
@@ -37,6 +37,7 @@ #include "third_party/blink/renderer/core/layout/ng/ng_positioned_float.h" #include "third_party/blink/renderer/core/layout/ng/ng_space_utils.h" #include "third_party/blink/renderer/core/layout/ng/ng_unpositioned_float.h" +#include "third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_utils.h" #include "third_party/blink/renderer/core/mathml/mathml_element.h" #include "third_party/blink/renderer/core/mathml_names.h" #include "third_party/blink/renderer/core/style/computed_style.h" @@ -1026,8 +1027,10 @@ } // At this point, perform any final table-cell adjustments needed. - if (ConstraintSpace().IsTableCell()) - FinalizeForTableCell(unconstrained_intrinsic_block_size); + if (ConstraintSpace().IsTableCell()) { + NGTableAlgorithmUtils::FinalizeTableCellLayout( + unconstrained_intrinsic_block_size, &container_builder_); + } NGOutOfFlowLayoutPart(Node(), ConstraintSpace(), &container_builder_).Run(); @@ -2300,76 +2303,6 @@ return child_bfc_block_offset; } -void NGBlockLayoutAlgorithm::FinalizeForTableCell( - LayoutUnit unconstrained_intrinsic_block_size) { - const bool has_inflow_children = !container_builder_.Children().IsEmpty(); - - // Hide table-cells if: - // - They are within a collapsed column(s). - // - They have "empty-cells: hide", non-collapsed borders, and no children. - container_builder_.SetIsHiddenForPaint( - ConstraintSpace().IsTableCellHiddenForPaint() || - (ConstraintSpace().HideTableCellIfEmpty() && !has_inflow_children)); - - container_builder_.SetHasCollapsedBorders( - ConstraintSpace().IsTableCellWithCollapsedBorders()); - - container_builder_.SetIsTableNGPart(); - - container_builder_.SetTableCellColumnIndex( - ConstraintSpace().TableCellColumnIndex()); - - // If we're resuming after a break, there'll be no alignment, since the - // fragment will start at the block-start edge of the fragmentainer then. - if (IsResumingLayout(BreakToken())) - return; - - switch (Style().VerticalAlign()) { - case EVerticalAlign::kTop: - // Do nothing for 'top' vertical alignment. - break; - case EVerticalAlign::kBaselineMiddle: - case EVerticalAlign::kSub: - case EVerticalAlign::kSuper: - case EVerticalAlign::kTextTop: - case EVerticalAlign::kTextBottom: - case EVerticalAlign::kLength: - // All of the above are treated as 'baseline' for the purposes of - // table-cell vertical alignment. - case EVerticalAlign::kBaseline: - // Table-cells (with baseline vertical alignment) always produce a - // baseline of their end-content edge (even if the content doesn't have - // any baselines). - if (!container_builder_.Baseline() || - Node().ShouldApplyLayoutContainment()) { - container_builder_.SetBaseline(unconstrained_intrinsic_block_size - - BorderScrollbarPadding().block_end); - } - - // Only adjust if we have *inflow* children. If we only have - // OOF-positioned children don't align them to the alignment baseline. - if (has_inflow_children) { - if (auto alignment_baseline = - ConstraintSpace().TableCellAlignmentBaseline()) { - container_builder_.MoveChildrenInBlockDirection( - *alignment_baseline - *container_builder_.Baseline()); - } - } - break; - case EVerticalAlign::kMiddle: - container_builder_.MoveChildrenInBlockDirection( - (container_builder_.FragmentBlockSize() - - unconstrained_intrinsic_block_size) / - 2); - break; - case EVerticalAlign::kBottom: - container_builder_.MoveChildrenInBlockDirection( - container_builder_.FragmentBlockSize() - - unconstrained_intrinsic_block_size); - break; - }; -} - LayoutUnit NGBlockLayoutAlgorithm::FragmentainerSpaceAvailable() const { return FragmentainerSpaceAtBfcStart(ConstraintSpace()) - container_builder_.BfcBlockOffset().value_or(
diff --git a/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.h b/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.h index 45fc071..0e96f2a 100644 --- a/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.h +++ b/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.h
@@ -212,9 +212,6 @@ NGInlineChildLayoutContext*, const NGInlineBreakToken** previous_inline_break_token); - // Performs any final adjustments for table-cells. - void FinalizeForTableCell(LayoutUnit unconstrained_intrinsic_block_size); - // Return the amount of block space available in the current fragmentainer // for the node being laid out by this algorithm. LayoutUnit FragmentainerSpaceAvailable() const;
diff --git a/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm.cc index 3049470..899b92f 100644 --- a/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm.cc +++ b/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm.cc
@@ -18,6 +18,7 @@ #include "third_party/blink/renderer/core/layout/ng/ng_length_utils.h" #include "third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.h" #include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h" +#include "third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_utils.h" #include "third_party/blink/renderer/core/style/computed_style.h" namespace blink { @@ -293,6 +294,7 @@ if (const auto* token = BreakToken()) previously_consumed_block_size = token->ConsumedBlockSize(); + LayoutUnit unconstrained_intrinsic_block_size = intrinsic_block_size_; intrinsic_block_size_ = ClampIntrinsicBlockSize(ConstraintSpace(), Node(), BreakToken(), BorderScrollbarPadding(), intrinsic_block_size_); @@ -330,10 +332,10 @@ #endif } - // TODO(mstensho): We need to do more here (vertical alignment, for instance), - // if this is a table cell. - if (ConstraintSpace().IsTableCell()) - container_builder_.SetIsTableNGPart(); + if (ConstraintSpace().IsTableCell()) { + NGTableAlgorithmUtils::FinalizeTableCellLayout( + unconstrained_intrinsic_block_size, &container_builder_); + } NGOutOfFlowLayoutPart(Node(), ConstraintSpace(), &container_builder_).Run(); @@ -1288,6 +1290,14 @@ size = std::min(size, available_outer_space.ClampNegativeToZero()); } + // Table-cell sizing is special. The aspects of specified block-size (and its + // min/max variants) that are actually honored by table cells is taken care of + // in the table layout algorithm. A constraint space with fixed block-size + // will be passed from the table layout algorithm if necessary. Leave it + // alone. + if (ConstraintSpace().IsTableCell()) + return size; + // The {,min-,max-}block-size properties are specified on the multicol // container, but here we're calculating the column block sizes inside the // multicol container, which isn't exactly the same. We may shrink the column
diff --git a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm.cc index 4be9128e..6cc5f5b 100644 --- a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm.cc +++ b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm.cc
@@ -631,6 +631,10 @@ if (result->Status() == NGLayoutResult::kNeedsRelayoutAsLastTableBox) return RelayoutAsLastTableBox(); + if (result->Status() == NGLayoutResult::kNeedsEarlierBreak) { + return RelayoutAndBreakEarlier<NGTableLayoutAlgorithm>( + *result->GetEarlyBreak()); + } return result; } @@ -1251,9 +1255,6 @@ } } - if (!child_iterator.NextChild()) - container_builder_.SetHasSeenAllChildren(); - if (table_box_extent) { // If we broke inside a section, the block-end border/padding shouldn't be // added to this fragment. @@ -1289,7 +1290,23 @@ if (pending_repeated_footer && table_box_extent) { DCHECK(table_box_will_continue); - // We broke before we got to the footer. Add it now. + // We broke before we got to the footer. Add it now. Before doing that, + // though, also insert break tokens for the sections that we didn't get to + // (if any), so that things will be resumed correctly when laying out the + // next table fragment (inserting a break token for the repeated footer + // alone would make the table child iterator skip any preceding sections). + auto entry = child_iterator.NextChild(); + for (; NGBlockNode child = entry.GetNode(); + entry = child_iterator.NextChild()) { + if (child == grouped_children.footer) + break; + + auto* token = NGBlockBreakToken::CreateBreakBefore( + child, /* is_forced_break */ false); + container_builder_.AddBreakToken(token); + } + DCHECK_EQ(entry.GetNode(), grouped_children.footer); + LogicalOffset offset(section_inline_offset, child_block_offset); NGConstraintSpace child_space = CreateSectionConstraintSpace( grouped_children.footer, offset.block_offset, @@ -1317,6 +1334,9 @@ } } + if (!child_iterator.NextChild()) + container_builder_.SetHasSeenAllChildren(); + LayoutUnit column_block_size; LogicalRect table_grid_rect; @@ -1385,7 +1405,9 @@ NGBreakStatus status = FinishFragmentation( Node(), ConstraintSpace(), BlockEndBorderPadding(), FragmentainerSpaceAtBfcStart(ConstraintSpace()), &container_builder_); - // TODO(mstensho): Deal with early-breaks. + if (status == NGBreakStatus::kNeedsEarlierBreak) + return container_builder_.Abort(NGLayoutResult::kNeedsEarlierBreak); + DCHECK_EQ(status, NGBreakStatus::kContinue); // Which side to include is normally handled by FinishFragmentation(), but
diff --git a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_utils.cc b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_utils.cc index 79aefdc..9c32021 100644 --- a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_utils.cc +++ b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_utils.cc
@@ -7,8 +7,11 @@ #include "third_party/blink/renderer/core/layout/geometry/logical_size.h" #include "third_party/blink/renderer/core/layout/ng/ng_block_node.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" #include "third_party/blink/renderer/core/layout/ng/ng_constraint_space_builder.h" #include "third_party/blink/renderer/core/layout/ng/ng_disable_side_effects_scope.h" +#include "third_party/blink/renderer/core/layout/ng/ng_fragmentation_utils.h" #include "third_party/blink/renderer/core/layout/ng/ng_layout_result.h" #include "third_party/blink/renderer/core/layout/ng/ng_length_utils.h" #include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h" @@ -694,6 +697,73 @@ section_block_size, treat_section_as_tbody)); } +void NGTableAlgorithmUtils::FinalizeTableCellLayout( + LayoutUnit unconstrained_intrinsic_block_size, + NGBoxFragmentBuilder* builder) { + const NGBlockNode& node = builder->Node(); + const NGConstraintSpace& space = builder->ConstraintSpace(); + const bool has_inflow_children = !builder->Children().IsEmpty(); + + // Hide table-cells if: + // - They are within a collapsed column(s). + // - They have "empty-cells: hide", non-collapsed borders, and no children. + builder->SetIsHiddenForPaint( + space.IsTableCellHiddenForPaint() || + (space.HideTableCellIfEmpty() && !has_inflow_children)); + + builder->SetHasCollapsedBorders(space.IsTableCellWithCollapsedBorders()); + + builder->SetIsTableNGPart(); + + builder->SetTableCellColumnIndex(space.TableCellColumnIndex()); + + // If we're resuming after a break, there'll be no alignment, since the + // fragment will start at the block-start edge of the fragmentainer then. + if (IsResumingLayout(builder->PreviousBreakToken())) + return; + + switch (node.Style().VerticalAlign()) { + case EVerticalAlign::kTop: + // Do nothing for 'top' vertical alignment. + break; + case EVerticalAlign::kBaselineMiddle: + case EVerticalAlign::kSub: + case EVerticalAlign::kSuper: + case EVerticalAlign::kTextTop: + case EVerticalAlign::kTextBottom: + case EVerticalAlign::kLength: + // All of the above are treated as 'baseline' for the purposes of + // table-cell vertical alignment. + case EVerticalAlign::kBaseline: + // Table-cells (with baseline vertical alignment) always produce a + // baseline of their end-content edge (even if the content doesn't have + // any baselines). + if (!builder->Baseline() || node.ShouldApplyLayoutContainment()) { + builder->SetBaseline(unconstrained_intrinsic_block_size - + builder->BorderScrollbarPadding().block_end); + } + + // Only adjust if we have *inflow* children. If we only have + // OOF-positioned children don't align them to the alignment baseline. + if (has_inflow_children) { + if (auto alignment_baseline = space.TableCellAlignmentBaseline()) { + builder->MoveChildrenInBlockDirection(*alignment_baseline - + *builder->Baseline()); + } + } + break; + case EVerticalAlign::kMiddle: + builder->MoveChildrenInBlockDirection( + (builder->FragmentBlockSize() - unconstrained_intrinsic_block_size) / + 2); + break; + case EVerticalAlign::kBottom: + builder->MoveChildrenInBlockDirection(builder->FragmentBlockSize() - + unconstrained_intrinsic_block_size); + break; + }; +} + void NGColspanCellTabulator::StartRow() { current_column_ = 0; }
diff --git a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_utils.h b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_utils.h index 83a4c96..0ebddb3c1 100644 --- a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_utils.h +++ b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_utils.h
@@ -83,6 +83,11 @@ NGTableTypes::Sections* sections, NGTableTypes::Rows* rows, NGTableTypes::CellBlockConstraints* cell_block_constraints); + + // Performs any final adjustments for table-cells at the end of layout. + static void FinalizeTableCellLayout( + LayoutUnit unconstrained_intrinsic_block_size, + NGBoxFragmentBuilder*); }; // NGColspanCellTabulator keeps track of columns occupied by colspanned cells
diff --git a/third_party/blink/renderer/core/page/context_menu_controller.cc b/third_party/blink/renderer/core/page/context_menu_controller.cc index 857d783b..8835cf4 100644 --- a/third_party/blink/renderer/core/page/context_menu_controller.cc +++ b/third_party/blink/renderer/core/page/context_menu_controller.cc
@@ -559,7 +559,8 @@ WebString text = plugin->SelectionAsText(); if (!text.IsEmpty()) { data.selected_text = text.Utf8(); - data.edit_flags |= ContextMenuDataEditFlags::kCanCopy; + if (plugin->CanCopy()) + data.edit_flags |= ContextMenuDataEditFlags::kCanCopy; } bool plugin_can_edit_text = plugin->CanEditText(); if (plugin_can_edit_text) { @@ -766,19 +767,25 @@ if (from_touch && !ShouldShowContextMenuFromTouch(data)) return false; - absl::optional<gfx::Point> host_context_menu_location; - auto* main_frame = - WebLocalFrameImpl::FromFrame(DynamicTo<LocalFrame>(page_->MainFrame())); - if (main_frame) { - host_context_menu_location = - main_frame->FrameWidgetImpl()->GetAndResetContextMenuLocation(); - } - WebLocalFrameImpl* selected_web_frame = WebLocalFrameImpl::FromFrame(selected_frame); if (!selected_web_frame || !selected_web_frame->Client()) return false; + absl::optional<gfx::Point> host_context_menu_location; + if (selected_web_frame->FrameWidgetImpl()) { + host_context_menu_location = + selected_web_frame->FrameWidgetImpl()->GetAndResetContextMenuLocation(); + } + if (!host_context_menu_location.has_value()) { + auto* main_frame = + WebLocalFrameImpl::FromFrame(DynamicTo<LocalFrame>(page_->MainFrame())); + if (main_frame && main_frame != selected_web_frame) { + host_context_menu_location = + main_frame->FrameWidgetImpl()->GetAndResetContextMenuLocation(); + } + } + selected_web_frame->ShowContextMenu( context_menu_client_receiver_.BindNewEndpointAndPassRemote( selected_web_frame->GetTaskRunner(TaskType::kInternalDefault)),
diff --git a/third_party/blink/renderer/core/page/context_menu_controller_test.cc b/third_party/blink/renderer/core/page/context_menu_controller_test.cc index 66dea2d0..c8cca3cd 100644 --- a/third_party/blink/renderer/core/page/context_menu_controller_test.cc +++ b/third_party/blink/renderer/core/page/context_menu_controller_test.cc
@@ -4,6 +4,10 @@ #include "third_party/blink/renderer/core/page/context_menu_controller.h" +#include <algorithm> +#include <limits> +#include <utility> + #include "base/run_loop.h" #include "base/test/metrics/histogram_tester.h" #include "base/test/scoped_feature_list.h" @@ -30,7 +34,7 @@ #include "third_party/blink/renderer/core/html/html_document.h" #include "third_party/blink/renderer/core/html/media/html_video_element.h" #include "third_party/blink/renderer/core/input/context_menu_allowed_scope.h" -#include "third_party/blink/renderer/core/page/context_menu_controller.h" +#include "third_party/blink/renderer/core/page/focus_controller.h" #include "third_party/blink/renderer/core/testing/core_unit_test_helper.h" #include "third_party/blink/renderer/platform/heap/garbage_collected.h" #include "third_party/blink/renderer/platform/mediastream/media_stream_component.h" @@ -63,8 +67,9 @@ public: void UpdateContextMenuDataForTesting( const ContextMenuData& data, - const absl::optional<gfx::Point>&) override { + const absl::optional<gfx::Point>& host_context_menu_location) override { context_menu_data_ = data; + host_context_menu_location_ = host_context_menu_location; } WebMediaPlayer* CreateMediaPlayer( @@ -82,8 +87,13 @@ return context_menu_data_; } + const absl::optional<gfx::Point>& host_context_menu_location() const { + return host_context_menu_location_; + } + private: ContextMenuData context_menu_data_; + absl::optional<gfx::Point> host_context_menu_location_; }; void RegisterMockedImageURLLoad(const String& url) { @@ -92,13 +102,13 @@ test::CoreTestDataPath(kTestResourceFilename), kTestResourceMimeType); } -} // anonymous namespace +} // namespace class ContextMenuControllerTest : public testing::Test, public ::testing::WithParamInterface<bool> { public: - explicit ContextMenuControllerTest( - bool penetrating_image_selection_enabled = GetParam()) { + ContextMenuControllerTest() { + bool penetrating_image_selection_enabled = GetParam(); feature_list_.InitWithFeatureState( features::kEnablePenetratingImageSelection, penetrating_image_selection_enabled); @@ -1811,4 +1821,64 @@ // TODO(crbug.com/1184996): Add additional unit test for blocking frame logging. +class ContextMenuControllerRemoteParentFrameTest + : public testing::Test, + public ::testing::WithParamInterface<bool> { + public: + ContextMenuControllerRemoteParentFrameTest() { + bool penetrating_image_selection_enabled = GetParam(); + feature_list_.InitWithFeatureState( + features::kEnablePenetratingImageSelection, + penetrating_image_selection_enabled); + } + + void SetUp() override { + web_view_helper_.InitializeRemote(); + web_view_helper_.RemoteMainFrame()->View()->DisableAutoResizeForTesting( + gfx::Size(640, 480)); + + child_frame_ = web_view_helper_.CreateLocalChild( + *web_view_helper_.RemoteMainFrame(), + /*name=*/"child", + /*properties=*/{}, + /*previous_sibling=*/nullptr, &child_web_frame_client_); + frame_test_helpers::LoadFrame(child_frame_, "data:text/html,some page"); + + auto& focus_controller = + child_frame_->GetFrame()->GetPage()->GetFocusController(); + focus_controller.SetActive(true); + focus_controller.SetFocusedFrame(child_frame_->GetFrame()); + } + + void ShowContextMenu(const gfx::Point& point) { + child_frame_->LocalRootFrameWidget()->ShowContextMenu( + ui::mojom::MenuSourceType::MOUSE, point); + base::RunLoop().RunUntilIdle(); + } + + const TestWebFrameClientImpl& child_web_frame_client() const { + return child_web_frame_client_; + } + + protected: + base::test::ScopedFeatureList feature_list_; + TestWebFrameClientImpl child_web_frame_client_; + frame_test_helpers::WebViewHelper web_view_helper_; + Persistent<WebLocalFrameImpl> child_frame_; +}; + +INSTANTIATE_TEST_SUITE_P(, + ContextMenuControllerRemoteParentFrameTest, + ::testing::Bool()); + +TEST_P(ContextMenuControllerRemoteParentFrameTest, ShowContextMenuInChild) { + const gfx::Point kPoint(123, 234); + ShowContextMenu(kPoint); + + const absl::optional<gfx::Point>& host_context_menu_location = + child_web_frame_client().host_context_menu_location(); + ASSERT_TRUE(host_context_menu_location.has_value()); + EXPECT_EQ(kPoint, host_context_menu_location.value()); +} + } // namespace blink
diff --git a/third_party/blink/renderer/core/paint/paint_layer_scrollable_area_test.cc b/third_party/blink/renderer/core/paint/paint_layer_scrollable_area_test.cc index b2a8a140..78df104c 100644 --- a/third_party/blink/renderer/core/paint/paint_layer_scrollable_area_test.cc +++ b/third_party/blink/renderer/core/paint/paint_layer_scrollable_area_test.cc
@@ -37,23 +37,6 @@ } }; -HeapVector<Member<ScrollTimelineOffset>> CreateScrollOffsets( - ScrollTimelineOffset* start_scroll_offset = - MakeGarbageCollected<ScrollTimelineOffset>( - CSSNumericLiteralValue::Create( - 10.0, - CSSPrimitiveValue::UnitType::kPixels)), - ScrollTimelineOffset* end_scroll_offset = - MakeGarbageCollected<ScrollTimelineOffset>( - CSSNumericLiteralValue::Create( - 90.0, - CSSPrimitiveValue::UnitType::kPixels))) { - HeapVector<Member<ScrollTimelineOffset>> scroll_offsets; - scroll_offsets.push_back(start_scroll_offset); - scroll_offsets.push_back(end_scroll_offset); - return scroll_offsets; -} - } // namespace #if BUILDFLAG(IS_FUCHSIA) @@ -1440,15 +1423,11 @@ class ScrollTimelineForTest : public ScrollTimeline { public: - ScrollTimelineForTest(Document* document, - Element* scroll_source, - HeapVector<Member<ScrollTimelineOffset>> - scroll_offsets = CreateScrollOffsets()) + ScrollTimelineForTest(Document* document, Element* scroll_source) : ScrollTimeline(document, ScrollTimeline::ReferenceType::kSource, scroll_source, - ScrollTimeline::kVertical, - std::move(scroll_offsets)), + ScrollTimeline::kVertical), invalidated_(false) {} void Invalidate() override { ScrollTimeline::Invalidate();
diff --git a/third_party/blink/renderer/modules/animationworklet/worklet_animation.cc b/third_party/blink/renderer/modules/animationworklet/worklet_animation.cc index 8eaaac3..0f678110 100644 --- a/third_party/blink/renderer/modules/animationworklet/worklet_animation.cc +++ b/third_party/blink/renderer/modules/animationworklet/worklet_animation.cc
@@ -416,7 +416,8 @@ // update the value in the next frame. if (IsActive(play_state_)) { for (auto& effect : effects_) { - effect->UpdateInheritedTime(absl::nullopt, absl::nullopt, false, + effect->UpdateInheritedTime(absl::nullopt, + /* at_scroll_timeline_boundary */ false, playback_rate_, kTimingUpdateOnDemand); } } @@ -503,7 +504,7 @@ local_times_[i] ? absl::make_optional(AnimationTimeDelta(local_times_[i].value())) : absl::nullopt, - absl::nullopt, false, playback_rate_, reason); + /* at_scroll_timeline_boundary */ false, playback_rate_, reason); } }
diff --git a/third_party/blink/renderer/modules/animationworklet/worklet_animation_effect.cc b/third_party/blink/renderer/modules/animationworklet/worklet_animation_effect.cc index 92687d2..5a668793 100644 --- a/third_party/blink/renderer/modules/animationworklet/worklet_animation_effect.cc +++ b/third_party/blink/renderer/modules/animationworklet/worklet_animation_effect.cc
@@ -38,8 +38,7 @@ local_time = AnimationTimeDelta(local_time_.value()); } calculated_ = specified_timing_.CalculateTimings( - local_time, /*timeline_phase*/ absl::nullopt, - /*at_progress_timeline_boundary*/ false, normalized_timing_, + local_time, /*at_progress_timeline_boundary*/ false, normalized_timing_, Timing::AnimationDirection::kForwards, false, playback_rate); }
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender.cc b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender.cc index 2e4dfc3..6167f1e 100644 --- a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender.cc +++ b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender.cc
@@ -348,11 +348,6 @@ } // https://w3c.github.io/webrtc-svc/ if (encoding->hasScalabilityMode()) { - if (encoding->scalabilityMode() == "L1T2") { - webrtc_encoding.num_temporal_layers = 2; - } else if (encoding->scalabilityMode() == "L1T3") { - webrtc_encoding.num_temporal_layers = 3; - } webrtc_encoding.scalability_mode = encoding->scalabilityMode().Utf8(); } webrtc_encoding.adaptive_ptime = encoding->adaptivePtime();
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender_impl.cc b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender_impl.cc index b17bb55..cc3df85 100644 --- a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender_impl.cc +++ b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender_impl.cc
@@ -289,8 +289,6 @@ new_parameters.encodings[i].rid = encoding.rid; new_parameters.encodings[i].scale_resolution_down_by = encoding.scale_resolution_down_by; - new_parameters.encodings[i].num_temporal_layers = - encoding.num_temporal_layers; new_parameters.encodings[i].scalability_mode = encoding.scalability_mode; new_parameters.encodings[i].adaptive_ptime = encoding.adaptive_ptime; }
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_render_pipeline.cc b/third_party/blink/renderer/modules/webgpu/gpu_render_pipeline.cc index 0adc01b3..15e9e49 100644 --- a/third_party/blink/renderer/modules/webgpu/gpu_render_pipeline.cc +++ b/third_party/blink/renderer/modules/webgpu/gpu_render_pipeline.cc
@@ -131,9 +131,16 @@ } } -WGPUDepthStencilState AsDawnType(const GPUDepthStencilState* webgpu_desc) { +WGPUDepthStencilState AsDawnType(GPUDevice* device, + const GPUDepthStencilState* webgpu_desc, + ExceptionState& exception_state) { DCHECK(webgpu_desc); + if (!device->ValidateTextureFormatUsage(webgpu_desc->format(), + exception_state)) { + return {}; + } + WGPUDepthStencilState dawn_desc = {}; dawn_desc.nextInChain = nullptr; dawn_desc.format = AsDawnEnum(webgpu_desc->format()); @@ -307,7 +314,8 @@ // DepthStencil if (webgpu_desc->hasDepthStencil()) { - dawn_desc_info->depth_stencil = AsDawnType(webgpu_desc->depthStencil()); + dawn_desc_info->depth_stencil = + AsDawnType(device, webgpu_desc->depthStencil(), exception_state); dawn_desc_info->dawn_desc.depthStencil = &dawn_desc_info->depth_stencil; }
diff --git a/third_party/blink/web_tests/FlagExpectations/disable-layout-ng b/third_party/blink/web_tests/FlagExpectations/disable-layout-ng index 9353a27..37f7d88 100644 --- a/third_party/blink/web_tests/FlagExpectations/disable-layout-ng +++ b/third_party/blink/web_tests/FlagExpectations/disable-layout-ng
@@ -1796,6 +1796,8 @@ crbug.com/1303102 external/wpt/css/css-images/object-view-box* [ Skip ] +crbug.com/1303102 virtual/document-transition/wpt_internal/document-transition/new-content-with-overflow-zoomed.html [ Skip ] +crbug.com/1303102 virtual/document-transition/wpt_internal/document-transition/old-content-with-overflow-zoomed.html [ Skip ] crbug.com/1321217 virtual/document-transition/wpt_internal/document-transition/old-content-with-overflow.html [ Skip ] crbug.com/1321217 virtual/document-transition/wpt_internal/document-transition/new-content-with-overflow.html [ Skip ] crbug.com/1303102 virtual/document-transition/wpt_internal/document-transition/no-root-capture.html [ Skip ]
diff --git a/third_party/blink/web_tests/FlagExpectations/highdpi b/third_party/blink/web_tests/FlagExpectations/highdpi index ab21373..4201be0 100644 --- a/third_party/blink/web_tests/FlagExpectations/highdpi +++ b/third_party/blink/web_tests/FlagExpectations/highdpi
@@ -1376,6 +1376,8 @@ crbug.com/1295281 virtual/document-transition/wpt_internal/document-transition/old-content-object-fit-fill.html [ Failure ] crbug.com/1295281 virtual/document-transition/wpt_internal/document-transition/new-content-object-fit-fill.html [ Failure ] crbug.com/1329180 virtual/document-transition/wpt_internal/document-transition/new-and-old-sizes-match.html [ Failure ] +crbug.com/1295280 virtual/document-transition/wpt_internal/document-transition/old-content-with-overflow-zoomed.html [ Failure ] +crbug.com/1295280 virtual/document-transition/wpt_internal/document-transition/new-content-with-overflow-zoomed.html [ Failure ] crbug.com/1314903 external/wpt/css/css-sizing/contain-intrinsic-size/animation/contain-intrinsic-size-interpolation.html [ Failure ]
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations index 6f64c12..bc4867aa 100644 --- a/third_party/blink/web_tests/TestExpectations +++ b/third_party/blink/web_tests/TestExpectations
@@ -1610,7 +1610,9 @@ virtual/layout_ng_table_frag/external/wpt/css/css-break/table/repeated-section/hit-test.tentative.html [ Pass ] virtual/layout_ng_table_frag/external/wpt/css/css-break/table/repeated-section/inline-block.tentative.html [ Pass ] virtual/layout_ng_table_frag/external/wpt/css/css-break/table/repeated-section/multicol.tentative.html [ Pass ] +virtual/layout_ng_table_frag/external/wpt/css/css-break/table/repeated-section/multiple-row-groups.tentative.html [ Pass ] virtual/layout_ng_table_frag/external/wpt/css/css-break/table/table-border-005.html [ Pass ] +virtual/layout_ng_table_frag/external/wpt/css/css-break/table/table-border-006.html [ Pass ] virtual/layout_ng_table_frag/external/wpt/css/css-break/table/table-cell-expansion-003.html [ Pass ] virtual/layout_ng_table_frag/external/wpt/css/css-break/table/table-col-paint-htb-ltr.html [ Pass ] virtual/layout_ng_table_frag/external/wpt/css/css-break/table/table-collapsed-borders-paint-at-boundary.tentative.html [ Pass ] @@ -1620,14 +1622,14 @@ virtual/layout_ng_table_frag/external/wpt/css/css-break/table/table-row-paint-vlr-rtl.html [ Pass ] virtual/layout_ng_table_frag/external/wpt/css/css-break/table/table-row-paint-vrl-rtl.html [ Pass ] virtual/layout_ng_table_frag/external/wpt/css/css-break/table/table-section-paint-vrl-rtl.html [ Pass ] +virtual/layout_ng_table_frag/external/wpt/css/css-multicol/table/table-cell-multicol-nested-002.html [ Pass ] +virtual/layout_ng_table_frag/external/wpt/css/css-multicol/table/table-cell-multicol-nested-003.html [ Pass ] virtual/layout_ng_table_frag/fragmentation/repeating-thead-under-repeating-thead.html [ Pass ] virtual/layout_ng_table_frag/fragmentation/single-line-cells-repeating-thead-cell-straddles-page-unsplittable-div.html [ Pass ] ### Tests failing with LayoutNGTableFragmentation enabled: crbug.com/1295905 [ Mac11-arm64 ] virtual/layout_ng_table_frag/external/wpt/css/css-break/table/sections-and-captions-mixed-order.html [ Failure ] crbug.com/1078927 virtual/layout_ng_table_frag/external/wpt/css/css-multicol/table/balance-table-with-border-spacing.html [ Failure ] -crbug.com/1078927 virtual/layout_ng_table_frag/external/wpt/css/css-multicol/table/multicol-table-cell-height-002.xht [ Failure ] -crbug.com/1078927 virtual/layout_ng_table_frag/external/wpt/css/css-multicol/table/multicol-table-cell-vertical-align-001.xht [ Failure ] crbug.com/1078927 virtual/layout_ng_table_frag/fragmentation/border-spacing-break-before-unbreakable-row.html [ Failure ] crbug.com/1078927 virtual/layout_ng_table_frag/fragmentation/break-in-second-table-section.html [ Failure ] crbug.com/1078927 virtual/layout_ng_table_frag/fragmentation/break-in-tbody-after-caption.html [ Failure ] @@ -1644,7 +1646,7 @@ crbug.com/1078927 virtual/layout_ng_table_frag/fragmentation/single-line-cells-multiple-tables-caption-repeating-thead-tfoot-with-border-spacing-at-top-of-row-3.html [ Failure ] crbug.com/1078927 virtual/layout_ng_table_frag/fragmentation/single-line-cells-multiple-tables-caption-repeating-thead-tfoot-with-border-spacing-at-top-of-row-4.html [ Failure ] crbug.com/1078927 virtual/layout_ng_table_frag/fragmentation/single-line-cells-multiple-tables-caption-repeating-thead-tfoot-with-border-spacing-at-top-of-row.html [ Failure ] -crbug.com/1078927 virtual/layout_ng_table_frag/fragmentation/single-line-cells-multiple-tables-repeating-thead-with-border-spacing-at-top-of-row.html [ Crash Pass ] +crbug.com/1335870 virtual/layout_ng_table_frag/fragmentation/single-line-cells-multiple-tables-repeating-thead-with-border-spacing-at-top-of-row.html [ Failure ] crbug.com/1078927 virtual/layout_ng_table_frag/fragmentation/single-line-cells-nested-repeating-thead-2.html [ Failure ] crbug.com/1078927 virtual/layout_ng_table_frag/fragmentation/single-line-cells-repeating-thead-cell-straddles-page.html [ Failure ] crbug.com/1078927 virtual/layout_ng_table_frag/fragmentation/single-line-cells-repeating-thead-starts-middle-of-page-break-after-avoid-3.html [ Failure ] @@ -2213,6 +2215,15 @@ crbug.com/1067031 external/wpt/css/css-overflow/webkit-line-clamp-035.html [ Failure ] +crbug.com/1339525 [ Win ] external/wpt/css/css-shapes/shape-outside/values/shape-margin-001.html [ Pass Failure ] +crbug.com/1339525 [ Win ] external/wpt/css/css-shapes/shape-outside/values/shape-outside-circle-004.html [ Pass Failure ] +crbug.com/1339525 [ Win ] external/wpt/css/css-shapes/shape-outside/values/shape-outside-circle-005.html [ Pass Failure ] +crbug.com/1339525 [ Win ] external/wpt/css/css-shapes/shape-outside/values/shape-outside-ellipse-004.html [ Pass Failure ] +crbug.com/1339525 [ Win ] external/wpt/css/css-shapes/shape-outside/values/shape-outside-ellipse-005.html [ Pass Failure ] +crbug.com/1339525 [ Win ] external/wpt/css/css-shapes/shape-outside/values/shape-outside-inset-003.html [ Pass Failure ] +crbug.com/1339525 [ Win ] external/wpt/css/css-shapes/shape-outside/values/shape-outside-polygon-004.html [ Pass Failure ] +crbug.com/1339525 [ Win ] external/wpt/css/css-shapes/shape-outside/values/shape-outside-shape-arguments-000.html [ Pass Failure ] + crbug.com/424365 external/wpt/css/css-shapes/shape-outside/shape-image/shape-image-024.html [ Failure ] crbug.com/1129522 external/wpt/css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-008.html [ Failure ] crbug.com/1129522 external/wpt/css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-006.html [ Failure ] @@ -3935,7 +3946,9 @@ crbug.com/1078927 external/wpt/css/css-break/table/repeated-section/hit-test.tentative.html [ Failure ] crbug.com/1078927 external/wpt/css/css-break/table/repeated-section/inline-block.tentative.html [ Failure ] crbug.com/1078927 external/wpt/css/css-break/table/repeated-section/multicol.tentative.html [ Failure ] +crbug.com/1078927 external/wpt/css/css-break/table/repeated-section/multiple-row-groups.tentative.html [ Failure ] crbug.com/1078927 external/wpt/css/css-break/table/table-border-005.html [ Failure ] +crbug.com/1078927 external/wpt/css/css-break/table/table-border-006.html [ Failure ] crbug.com/1078927 external/wpt/css/css-break/table/table-cell-expansion-003.html [ Failure ] crbug.com/1078927 external/wpt/css/css-break/table/table-col-paint-htb-ltr.html [ Failure ] crbug.com/1078927 external/wpt/css/css-break/table/table-collapsed-borders-paint-at-boundary.tentative.html [ Failure ] @@ -3976,6 +3989,8 @@ crbug.com/481431 external/wpt/css/css-multicol/multicol-zero-height-003.html [ Failure ] crbug.com/1191124 external/wpt/css/css-multicol/spanner-fragmentation-012.html [ Failure ] crbug.com/1224888 external/wpt/css/css-multicol/spanner-in-opacity.html [ Failure ] +crbug.com/1078927 external/wpt/css/css-multicol/table/table-cell-multicol-nested-002.html [ Failure ] +crbug.com/1078927 external/wpt/css/css-multicol/table/table-cell-multicol-nested-003.html [ Failure ] crbug.com/849459 fragmentation/repeating-thead-under-repeating-thead.html [ Failure ] crbug.com/1078927 fragmentation/single-line-cells-repeating-thead-cell-straddles-page-unsplittable-div.html [ Failure ] @@ -7071,7 +7086,3 @@ crbug.com/1339293 [ Linux ] virtual/threaded-preload-scanner/external/wpt/html/semantics/scripting-1/the-script-element/execution-timing/029.html [ Failure Pass ] crbug.com/1339293 [ Linux ] virtual/threaded/external/wpt/requestidlecallback/deadline-max-rAF-dynamic.html [ Failure Pass ] -crbug.com/1339755 [ Mac10.13 ] compositing/overflow/nested-render-surfaces-with-rotation.html [ Skip ] -crbug.com/1339755 [ Mac10.13 ] media/video-zoom-controls.html [ Skip ] -crbug.com/1339755 [ Mac10.13 ] virtual/oopr-canvas2d/fast/canvas/image-object-in-canvas.html [ Skip ] -crbug.com/1339755 [ Mac10.13 ] virtual/oopr-canvas2d/fast/canvas/quadraticCurveTo.xml [ Skip ]
diff --git a/third_party/blink/web_tests/external/wpt/credential-management/support/fedcm-manifest-not-in-list/fedcm.json b/third_party/blink/web_tests/external/wpt/credential-management/support/fedcm-manifest-not-in-list/fedcm.json index 9d919790..c044a7f 100644 --- a/third_party/blink/web_tests/external/wpt/credential-management/support/fedcm-manifest-not-in-list/fedcm.json +++ b/third_party/blink/web_tests/external/wpt/credential-management/support/fedcm-manifest-not-in-list/fedcm.json
@@ -2,5 +2,4 @@ "accounts_endpoint": "../accounts.py", "client_metadata_endpoint": "../client_metadata.py", "id_token_endpoint": "../id_token.py", - "revocation_endpoint": "../revoke.py" }
diff --git a/third_party/blink/web_tests/external/wpt/credential-management/support/revoke.py b/third_party/blink/web_tests/external/wpt/credential-management/support/revoke.py deleted file mode 100644 index ed6fe00d..0000000 --- a/third_party/blink/web_tests/external/wpt/credential-management/support/revoke.py +++ /dev/null
@@ -1,6 +0,0 @@ -def main(request, response): - if not b"hint" in request.POST: - return (500, [], "Missing hint") - if request.POST[b"hint"] == b"fail": - return (500, [], "Fail requested") - return (204, [], "")
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/table/repeated-section/multiple-row-groups.tentative.html b/third_party/blink/web_tests/external/wpt/css/css-break/table/repeated-section/multiple-row-groups.tentative.html new file mode 100644 index 0000000..96e6173 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-break/table/repeated-section/multiple-row-groups.tentative.html
@@ -0,0 +1,24 @@ +<!DOCTYPE html> +<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org"> +<link rel="help" href="https://www.w3.org/TR/css-tables-3/#repeated-headers"> +<link rel="match" href="../../../reference/ref-filled-green-100px-square.xht"> +<p>Test passes if there is a filled green square and <strong>no red</strong>.</p> +<div style="columns:4; gap:0; column-fill:auto; width:100px; height:100px; background:red;"> + <div style="display:table; width:100%;"> + <div style="display:table-footer-group; break-inside:avoid;"> + <div style="height:10px; background:green;"></div> + </div> + <div style="display:table-row-group;"> + <div style="height:150px; background:green;"></div> + </div> + <div style="display:table-row-group;"> + <div style="height:20px; background:green;"></div> + </div> + <div style="display:table-row-group;"> + <div style="height:90px; background:green;"></div> + </div> + <div style="display:table-row-group;"> + <div style="height:100px; background:green;"></div> + </div> + </div> +</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/table/table-border-006.html b/third_party/blink/web_tests/external/wpt/css/css-break/table/table-border-006.html new file mode 100644 index 0000000..f0ebf25 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-break/table/table-border-006.html
@@ -0,0 +1,17 @@ +<!DOCTYPE html> +<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org"> +<link rel="help" href="https://www.w3.org/TR/css-break-3/#box-splitting"> +<link rel="match" href="../../reference/ref-filled-green-100px-square.xht"> +<p>Test passes if there is a filled green square and <strong>no red</strong>.</p> +<div style="width:100px; height:100px; background:red;"> + <div style="columns:2; gap:0; column-fill:auto; height:170px;"> + <div style="display:table; width:100%; border-bottom:30px solid green;"> + <div style="break-inside:avoid; display:table-row;"> + <div style="height:100px; background:green;"></div> + </div> + <div style="break-inside:avoid; display:table-row;"> + <div style="height:70px; background:green;"></div> + </div> + </div> + </div> +</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-cascade/layer-scroll-timeline-override.html b/third_party/blink/web_tests/external/wpt/css/css-cascade/layer-scroll-timeline-override.html index 59b8590..abdbed0 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-cascade/layer-scroll-timeline-override.html +++ b/third_party/blink/web_tests/external/wpt/css/css-cascade/layer-scroll-timeline-override.html
@@ -7,7 +7,7 @@ <script src="/web-animations/testcommon.js"></script> <style> #scroller { - overflow: scroll; + overflow: hidden; width: 100px; height: 100px; } @@ -26,7 +26,7 @@ } #reference { - width: 150px; + width: 125px; } #target { @@ -54,15 +54,13 @@ @scroll-timeline timeline { source: selector(#scroller); - start: 0px; - end: 50px; + orientation: block; } @layer { @scroll-timeline timeline { source: selector(#scroller); - start: 0px; - end: 100px; + orientation: inline; } } ` @@ -80,16 +78,14 @@ @layer override { @scroll-timeline timeline { source: selector(#scroller); - start: 0px; - end: 50px; + orientation: block; } } @layer base { @scroll-timeline timeline { source: selector(#scroller); - start: 0px; - end: 100px; + orientation: inline; } } ` @@ -107,8 +103,7 @@ @layer override { @scroll-timeline timeline { source: selector(#scroller); - start: 0px; - end: 50px; + orientation: block; } } `, @@ -116,8 +111,7 @@ @layer base { @scroll-timeline timeline { source: selector(#scroller); - start: 0px; - end: 100px; + orientation: inline; } } ` @@ -135,8 +129,7 @@ @layer base { @scroll-timeline timeline { source: selector(#scroller); - start: 0px; - end: 100px; + orientation: inline; } } `, @@ -144,8 +137,7 @@ @layer override { @scroll-timeline timeline { source: selector(#scroller); - start: 0px; - end: 50px; + orientation: block; } } `
diff --git a/third_party/blink/web_tests/external/wpt/css/css-multicol/table/table-cell-multicol-nested-001.html b/third_party/blink/web_tests/external/wpt/css/css-multicol/table/table-cell-multicol-nested-001.html new file mode 100644 index 0000000..5cb9eaf --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-multicol/table/table-cell-multicol-nested-001.html
@@ -0,0 +1,13 @@ +<!DOCTYPE html> +<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org"> +<link rel="help" href="https://www.w3.org/TR/css-multicol-1/#propdef-column-count"> +<link rel="match" href="../../reference/ref-filled-green-100px-square.xht"> +<meta name="assert" content="Test that a balanced multicol table cell inside another fragmentation context is sized and fragmented correctly"> +<p>Test passes if there is a filled green square and <strong>no red</strong>.</p> +<div style="columns:2; gap:0; column-fill:auto; width:100px; height:100px; background:red;"> + <div style="height:50px; background:green;"></div> + <div style="display:table-cell; columns:2; gap:0; width:50px; background:red;"> + <div style="height:200px; background:green;"></div> + </div> + <div style="height:50px; background:green;"></div> +</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-multicol/table/table-cell-multicol-nested-002.html b/third_party/blink/web_tests/external/wpt/css/css-multicol/table/table-cell-multicol-nested-002.html new file mode 100644 index 0000000..b250841 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-multicol/table/table-cell-multicol-nested-002.html
@@ -0,0 +1,13 @@ +<!DOCTYPE html> +<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org"> +<link rel="help" href="https://www.w3.org/TR/css-multicol-1/#propdef-column-count"> +<link rel="match" href="../../reference/ref-filled-green-100px-square.xht"> +<meta name="assert" content="Test that a balanced multicol table cell inside another fragmentation context is sized and fragmented correctly - its height and max-height should be ignored, since the intrinsic size is larger"> +<p>Test passes if there is a filled green square and <strong>no red</strong>.</p> +<div style="columns:2; gap:0; column-fill:auto; width:100px; height:100px; background:red;"> + <div style="height:50px; background:green;"></div> + <div style="display:table-cell; columns:2; gap:0; height:10px; max-height:10px; width:50px; background:red;"> + <div style="height:200px; background:green;"></div> + </div> + <div style="height:50px; background:green;"></div> +</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-multicol/table/table-cell-multicol-nested-003.html b/third_party/blink/web_tests/external/wpt/css/css-multicol/table/table-cell-multicol-nested-003.html new file mode 100644 index 0000000..f4e52910f --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-multicol/table/table-cell-multicol-nested-003.html
@@ -0,0 +1,13 @@ +<!DOCTYPE html> +<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org"> +<link rel="help" href="https://www.w3.org/TR/css-multicol-1/#propdef-column-count"> +<link rel="match" href="../../reference/ref-filled-green-100px-square.xht"> +<meta name="assert" content="Test that a balanced multicol table cell inside another fragmentation context is sized and fragmented correctly, and that it gets stretched by the specified height, which is larger than the intrinsic size"> +<p>Test passes if there is a filled green square and <strong>no red</strong>.</p> +<div style="columns:2; gap:0; column-fill:auto; width:100px; height:100px; background:red;"> + <div style="height:50px; background:green;"></div> + <div style="display:table-cell; columns:2; gap:0; height:100px; width:50px; background:red;"> + <div style="height:150px; background:green;"></div> + </div> + <div style="margin-top:-25px; height:75px; background:green;"></div> +</div>
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/css/at-scroll-timeline-before-phase.html b/third_party/blink/web_tests/external/wpt/scroll-animations/css/at-scroll-timeline-before-phase.html deleted file mode 100644 index d2a978b2..0000000 --- a/third_party/blink/web_tests/external/wpt/scroll-animations/css/at-scroll-timeline-before-phase.html +++ /dev/null
@@ -1,61 +0,0 @@ -<!DOCTYPE html> -<link rel="help" src="https://drafts.csswg.org/scroll-animations-1/#scroll-timeline-at-rule"> -<link rel="help" src="https://drafts.csswg.org/scroll-animations-1/#phase-algorithm"> -<script src="/resources/testharness.js"></script> -<script src="/resources/testharnessreport.js"></script> -<script src="/web-animations/testcommon.js"></script> -<style> - #scroller { - overflow: scroll; - width: 100px; - height: 100px; - } - #contents { - height: 200px; - } - @keyframes expand { - from { width: 100px; } - to { width: 200px; } - } - #element { - width: 0px; - } - /* Ensure stable expectations if feature is not supported */ - @supports not (animation-timeline:foo) { - #element { animation-play-state: paused; } - } -</style> -<div id=scroller> - <div id=contents></div> -</div> -<div id=container></div> -<script> - promise_test(async (t) => { - try { - // Make sure scroller has a layout box. - await waitForNextFrame(); - - container.innerHTML = ` - <div id=element></div> - <style> - @scroll-timeline timeline { - source: selector(#scroller); - start: 50px; - end: 100px; - } - #element { - animation: expand 10s linear; - animation-timeline: timeline; - } - </style> - `; - // Animation should not apply in before phase. - assert_equals(getComputedStyle(element).width, '0px'); - await waitForNextFrame(); - // Animation should still not apply. - assert_equals(getComputedStyle(element).width, '0px'); - } finally { - container.innerHTML = ''; - } - }, 'Animation does not apply when timeline phase is before'); -</script>
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/css/at-scroll-timeline-element-offsets.html b/third_party/blink/web_tests/external/wpt/scroll-animations/css/at-scroll-timeline-element-offsets.html deleted file mode 100644 index f0301cf..0000000 --- a/third_party/blink/web_tests/external/wpt/scroll-animations/css/at-scroll-timeline-element-offsets.html +++ /dev/null
@@ -1,247 +0,0 @@ -<!DOCTYPE html> -<title>@scroll-timeline: Element-based offsets</title> -<link rel="help" src="https://drafts.csswg.org/scroll-animations-1/#scroll-timeline-at-rule"> -<link rel="help" src="https://drafts.csswg.org/scroll-animations-1/#element-based-offset-section"> -<script src="/resources/testharness.js"></script> -<script src="/resources/testharnessreport.js"></script> -<script src="/web-animations/testcommon.js"></script> -<style> - #scroller { - overflow: scroll; - width: 100px; - height: 100px; - } - .filler { - height: 150px; - background-color: darkgray; - } - .offset { - height: 50px; - background-color: green; - } - @keyframes expand { - from { width: 100px; } - to { width: 200px; } - } - @scroll-timeline timeline_start_start { - source: selector(#scroller); - start: selector(#offset1); - end: selector(#offset2); - } - @scroll-timeline timeline_end_end { - source: selector(#scroller); - start: selector(#offset1) end; - end: selector(#offset2) end; - } - @scroll-timeline timeline_end_start { - source: selector(#scroller); - start: selector(#offset1) end; - end: selector(#offset2) start; - } - @scroll-timeline timeline_end_1_end { - source: selector(#scroller); - start: selector(#offset1) end 1; - end: selector(#offset2) end; - } - @scroll-timeline timeline_end_start_05 { - source: selector(#scroller); - start: selector(#offset1) end; - end: selector(#offset2) 0.5; - } - @scroll-timeline timeline_start_400px { - source: selector(#scroller); - start: selector(#offset1); - end: 400px; - } - @scroll-timeline timeline_50px_end { - source: selector(#scroller); - start: 50px; - end: selector(#offset2) end; - } - @scroll-timeline timeline_outside { - source: selector(#scroller); - start: selector(#offset_outside); - end: auto; - } - @scroll-timeline timeline_display_none { - source: selector(#scroller); - start: selector(#offset_display_none); - end: auto; - } - @scroll-timeline timeline_null_target { - source: selector(#scroller); - start: selector(#no_such_id); - end: selector(#no_such_id); - } - - #container > div { - width: 0px; - animation-name: expand; - animation-duration: 10s; - animation-timing-function: linear; - } - /* Ensure stable expectations if feature is not supported */ - @supports not (animation-timeline:foo) { - #container > div { animation-play-state: paused; } - } - #element_start_start { animation-timeline: timeline_start_start; } - #element_end_end { animation-timeline: timeline_end_end; } - #element_end_start { animation-timeline: timeline_end_start; } - #element_end_1_end { animation-timeline: timeline_end_1_end; } - #element_end_start_05 { animation-timeline: timeline_end_start_05; } - #element_start_400px { animation-timeline: timeline_start_400px; } - #element_50px_end { animation-timeline: timeline_50px_end; } - #element_outside { animation-timeline: timeline_outside; } - #element_display_none { animation-timeline: timeline_display_none; } - #element_null_target { animation-timeline: timeline_null_target; } -</style> -<div id=scroller> - <div id=contents> - <div class=filler></div> - <div class=offset id=offset1></div> - <div class=filler></div> - <div class=offset id=offset2></div> - <div class=filler></div> - </div> -</div> -<div id=offset_outside></div> -<div id=offset_display_none style="display:none"></div> -<div id=container> - <div id=element_start_start></div> - <div id=element_end_end></div> - <div id=element_end_start></div> - <div id=element_end_1_end></div> - <div id=element_end_start_05></div> - <div id=element_start_400px></div> - <div id=element_50px_end></div> - <div id=element_outside></div> - <div id=element_display_none></div> - <div id=element_null_target></div> -</div> -<script> - - // The contents of the scroller looks approximately like this: - // - // +-------+ - // | | - // | 150px | filler - // | | - // +-------+ - // +-------+ - // | 50px | #offset1 - // +-------+ - // +-------+ - // | | - // | 150px | filler - // | | - // +-------+ - // +-------+ - // | 50px | #offset2 - // +-------+ - // +-------+ - // | | - // | 150px | filler - // | | - // +-------+ - // - // The height of the scrollport is 100px. - - // Scrolls top to 'offset', waits for a frame, then call the provided - // assertions function. - function test_scroll(element, offset, assertions, description) { - promise_test(async (t) => { - scroller.scrollTop = offset; - await waitForNextFrame(); - assertions(); - }, `${description} [${element.id}]`); - } - - // Tests that the computed value of 'width' on element is the expected value - // after scrolling top to the specifed offset. - function test_width_at_scroll_top(element, offset, expected) { - test_scroll(element, offset, () => { - assert_equals(getComputedStyle(element).width, expected); - }, `Scroll at offset ${offset} updates animation correctly`); - } - - // [200, 400] - test_width_at_scroll_top(element_start_start, 0, '0px'); - test_width_at_scroll_top(element_start_start, 199, '0px'); - test_width_at_scroll_top(element_start_start, 200, '100px'); - test_width_at_scroll_top(element_start_start, 300, '150px'); - test_width_at_scroll_top(element_start_start, 398, '199px'); - test_width_at_scroll_top(element_start_start, 400, '0px'); - - // [50, 250] - test_width_at_scroll_top(element_end_end, 0, '0px'); - test_width_at_scroll_top(element_end_end, 49, '0px'); - test_width_at_scroll_top(element_end_end, 50, '100px'); - test_width_at_scroll_top(element_end_end, 150, '150px'); - test_width_at_scroll_top(element_end_end, 248, '199px'); - test_width_at_scroll_top(element_end_end, 250, '0px'); - - // [50, 400] - test_width_at_scroll_top(element_end_start, 0, '0px'); - test_width_at_scroll_top(element_end_start, 49, '0px'); - test_width_at_scroll_top(element_end_start, 50, '100px'); - test_width_at_scroll_top(element_end_start, 225, '150px'); - test_width_at_scroll_top(element_end_start, 393, '198px'); - test_width_at_scroll_top(element_end_start, 400, '0px'); - - // [100, 250] - test_width_at_scroll_top(element_end_1_end, 0, '0px'); - test_width_at_scroll_top(element_end_1_end, 99, '0px'); - test_width_at_scroll_top(element_end_1_end, 100, '100px'); - test_width_at_scroll_top(element_end_1_end, 175, '150px'); - test_width_at_scroll_top(element_end_1_end, 247, '198px'); - test_width_at_scroll_top(element_end_1_end, 250, '0px'); - - // [50, 375] - test_width_at_scroll_top(element_end_start_05, 0, '0px'); - test_width_at_scroll_top(element_end_start_05, 49, '0px'); - test_width_at_scroll_top(element_end_start_05, 50, '100px'); - test_width_at_scroll_top(element_end_start_05, 206, '148px'); - test_width_at_scroll_top(element_end_start_05, 362, '196px'); - test_width_at_scroll_top(element_end_start_05, 375, '0px'); - - // [200, 300] - test_width_at_scroll_top(element_start_400px, 0, '0px'); - test_width_at_scroll_top(element_start_400px, 199, '0px'); - test_width_at_scroll_top(element_start_400px, 200, '100px'); - test_width_at_scroll_top(element_start_400px, 300, '150px'); - test_width_at_scroll_top(element_start_400px, 398, '199px'); - test_width_at_scroll_top(element_start_400px, 400, '0px'); - - // [50, 250] - test_width_at_scroll_top(element_50px_end, 0, '0px'); - test_width_at_scroll_top(element_50px_end, 49, '0px'); - test_width_at_scroll_top(element_50px_end, 50, '100px'); - test_width_at_scroll_top(element_50px_end, 150, '150px'); - test_width_at_scroll_top(element_50px_end, 248, '199px'); - test_width_at_scroll_top(element_50px_end, 250, '0px'); - - // Offset not a decendant of scroller (=> no effect value) - test_width_at_scroll_top(element_outside, 0, '0px'); - test_width_at_scroll_top(element_outside, 100, '0px'); - test_width_at_scroll_top(element_outside, 200, '0px'); - test_width_at_scroll_top(element_outside, 300, '0px'); - test_width_at_scroll_top(element_outside, 400, '0px'); - test_width_at_scroll_top(element_outside, 450, '0px'); - - // Target of element-based offset has no layout box (=> no effect value) - test_width_at_scroll_top(element_display_none, 0, '0px'); - test_width_at_scroll_top(element_display_none, 100, '0px'); - test_width_at_scroll_top(element_display_none, 200, '0px'); - test_width_at_scroll_top(element_display_none, 300, '0px'); - test_width_at_scroll_top(element_display_none, 400, '0px'); - test_width_at_scroll_top(element_display_none, 450, '0px'); - - // Target of element-based offset is null (=> no effect value) - test_width_at_scroll_top(element_null_target, 0, '0px'); - test_width_at_scroll_top(element_null_target, 100, '0px'); - test_width_at_scroll_top(element_null_target, 200, '0px'); - test_width_at_scroll_top(element_null_target, 300, '0px'); - test_width_at_scroll_top(element_null_target, 400, '0px'); - test_width_at_scroll_top(element_null_target, 450, '0px'); - -</script>
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/css/at-scroll-timeline-offset-invalidation.tentative.html b/third_party/blink/web_tests/external/wpt/scroll-animations/css/at-scroll-timeline-offset-invalidation.tentative.html deleted file mode 100644 index 13d816a..0000000 --- a/third_party/blink/web_tests/external/wpt/scroll-animations/css/at-scroll-timeline-offset-invalidation.tentative.html +++ /dev/null
@@ -1,207 +0,0 @@ -<!DOCTYPE html> -<title>@scroll-timeline element offset invalidation</title> -<link rel="help" src="https://drafts.csswg.org/scroll-animations-1/#typedef-element-offset"> -<script src="/resources/testharness.js"></script> -<script src="/resources/testharnessreport.js"></script> -<script src="/web-animations/testcommon.js"></script> -<style> - #scroller { - overflow: scroll; - width: 100px; - height: 100px; - } - #scroller > div { - height: 50px; - } - @keyframes expand { - from { width: 100px; } - to { width: 200px; } - } - @scroll-timeline timeline { - source: selector(#scroller); - start: selector(#offset1) end; - end: selector(#offset2) end; - } - #element { - width: 0px; - height: 20px; - animation: expand 1000s linear; - animation-timeline: timeline; - } - /* Ensure stable expectations if feature is not supported */ - @supports not (animation-timeline:foo) { - #element { animation-play-state: paused; } - } -</style> -<div id=scroller></div> -<div id=element></div> -<p class=sibling1></p> -<p class=sibling2></p> -<script> - - function setup() { - while (scroller.firstChild) - scroller.firstChild.remove(); - for (let i = 0; i < 10; i++) - scroller.append(document.createElement('div')); - } - - // The contents of the scroller look like this: - // - // +-------+ - // | 50px | div (0) - // +-------+ - // +-------+ - // | 50px | div (1) - // +-------+ - // +-------+ - // | 50px | div (2) - // +-------+ - // +-------+ - // | 50px | div (3) - // +-------+ - // +-------+ - // | 50px | div (4) - // +-------+ - // +-------+ - // | 50px | div (5) - // +-------+ - // +-------+ - // | 50px | div (6) - // +-------+ - // +-------+ - // | 50px | div (7) - // +-------+ - // +-------+ - // | 50px | div (8) - // +-------+ - // +-------+ - // | 50px | div (9) - // +-------+ - // - // The height of the scrollport is 100px. - - function invalidation_test(func, description) { - promise_test(async (t) => { - setup(); - await func(); - }, description); - } - - function remove(id) { - let old_element = document.getElementById(id); - if (old_element) - old_element.removeAttribute('id'); - } - - function reassign(id, element) { - remove(id); - element.setAttribute('id', id); - } - - async function assert_element_width_at_scroll(expected_width, scroll) { - scroller.scrollTop = scroll; - await waitForNextFrame(); - assert_equals(getComputedStyle(element).width, expected_width); - } - - invalidation_test(async () => { - await assert_element_width_at_scroll('0px', 0); - }, 'Offsets missing'); - - invalidation_test(async () => { - // [50, 150] - reassign('offset1', scroller.children[3]); - reassign('offset2', scroller.children[5]); - await assert_element_width_at_scroll('150px', 100); - - // [100, 150] - reassign('offset1', scroller.children[4]); - await assert_element_width_at_scroll('100px', 100); - }, 'Change first offset'); - - invalidation_test(async () => { - // [50, 150] - reassign('offset1', scroller.children[3]); - reassign('offset2', scroller.children[5]); - await assert_element_width_at_scroll('150px', 100); - - // [50, 250] - reassign('offset2', scroller.children[7]); - await assert_element_width_at_scroll('125px', 100); - }, 'Change second offset'); - - invalidation_test(async () => { - // [50, 250] - reassign('offset1', scroller.children[3]); - reassign('offset2', scroller.children[7]); - await assert_element_width_at_scroll('125px', 100); - - // [0, 200] - reassign('offset1', scroller.children[2]); - reassign('offset2', scroller.children[4]); - await assert_element_width_at_scroll('150px', 50); - }, 'Change both offsets'); - - invalidation_test(async () => { - // [50, 150] - reassign('offset1', scroller.children[3]); - reassign('offset2', scroller.children[5]); - await assert_element_width_at_scroll('150px', 100); - - remove('offset1'); - await assert_element_width_at_scroll('0px', 0); - }, 'Remove first offset'); - - invalidation_test(async () => { - // [50, 150] - reassign('offset1', scroller.children[3]); - reassign('offset2', scroller.children[5]); - await assert_element_width_at_scroll('150px', 100); - - remove('offset2'); - await assert_element_width_at_scroll('0px', 0); - }, 'Remove second offset'); - - invalidation_test(async () => { - // [50, 150] - reassign('offset1', scroller.children[3]); - reassign('offset2', scroller.children[5]); - await assert_element_width_at_scroll('150px', 100); - - remove('offset1'); - remove('offset2'); - await assert_element_width_at_scroll('0px', 0); - }, 'Remove both offsets'); - - invalidation_test(async () => { - // [50, 150] - reassign('offset1', scroller.children[3]); - reassign('offset2', scroller.children[5]); - await assert_element_width_at_scroll('150px', 100); - - reassign('offset1', document.querySelector('.sibling1')); - await assert_element_width_at_scroll('0px', 0); - }, 'Reassign first offset to sibling of scroller'); - - invalidation_test(async () => { - // [50, 150] - reassign('offset1', scroller.children[3]); - reassign('offset2', scroller.children[5]); - await assert_element_width_at_scroll('150px', 100); - - reassign('offset2', document.querySelector('.sibling2')); - await assert_element_width_at_scroll('0px', 0); - }, 'Reassign second offset to sibling of scroller'); - - invalidation_test(async () => { - // [50, 150] - reassign('offset1', scroller.children[3]); - reassign('offset2', scroller.children[5]); - await assert_element_width_at_scroll('150px', 100); - - reassign('offset1', document.querySelector('.sibling1')); - reassign('offset2', document.querySelector('.sibling2')); - await assert_element_width_at_scroll('0px', 0); - }, 'Reassign both offsets to sibling of scroller'); -</script>
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/scroll-timelines/constructor.html b/third_party/blink/web_tests/external/wpt/scroll-animations/scroll-timelines/constructor.html index 0ba2f53..c541256 100644 --- a/third_party/blink/web_tests/external/wpt/scroll-animations/scroll-timelines/constructor.html +++ b/third_party/blink/web_tests/external/wpt/scroll-animations/scroll-timelines/constructor.html
@@ -92,193 +92,4 @@ }; assert_throws_js(TypeError, constructorFunc); }, 'Creating a ScrollTimeline with an invalid orientation value should throw'); - -// scrollOffsets - -function formatOffset(v) { - if (typeof(v) == 'object') - return `${v.constructor.name}(${v.toString()})`; - return `'${v.toString()}'`; -} - -function assert_offsets_equal(a, b) { - assert_equals(formatOffset(a), formatOffset(b)); -} - -test(t => { - assert_array_equals(new ScrollTimeline().scrollOffsets, []); -}, 'A ScrollTimeline created with the default scrollOffsets should default ' + - 'to []'); - -test(t => { - assert_array_equals( - new ScrollTimeline({scrollOffsets: []}).scrollOffsets, []); -}, 'A ScrollTimeline created with empty scrollOffsets should resolve to []'); - -test(t => { - let offsets = - new ScrollTimeline({scrollOffsets: [CSS.percent(20), 'auto']}) - .scrollOffsets; - assert_offsets_equal(offsets[0], CSS.percent(20)); - assert_offsets_equal(offsets[1], new CSSKeywordValue('auto')); -}, 'A ScrollTimeline created with last \'auto\' offset in scrollOffsets ' + - 'should be allowed.'); - -test(t => { - let constructorFunc = function() { - new ScrollTimeline({scrollOffsets: null}) - }; - assert_throws_js(TypeError, constructorFunc); -}, 'Creating a ScrollTimeline with an invalid scrollOffsets value should ' + - 'throw.'); - -test(t => { - let constructorFunc = function() { - new ScrollTimeline( - {scrollOffsets: [CSS.percent(20), 'auto', CSS.percent(50)]}) - }; - assert_throws_js(TypeError, constructorFunc); -}, 'Creating a ScrollTimeline with an scrollOffsets value of ' + - '[CSS.percent(20), \'auto\', CSS.percent(50)] should throw'); - -const gValidScrollOffsetValues = [ - CSS.px(0), - CSS.percent(100).sub(CSS.px(80)), -]; - -const gValidScrollOffsetSuffixes = [ - // Relative lengths. - 'em', - 'ex', - 'ch', - 'rem', - 'vw', - 'vh', - 'vmin', - 'vmax', - // Absolute lengths. - 'cm', - 'mm', - 'q', - 'in', - 'pc', - 'pt', - 'px', - // Percentage. - '%', -]; - -for (let offset of gValidScrollOffsetValues) { - test(function() { - const scrollTimeline = - new ScrollTimeline({scrollOffsets: [offset, offset]}); - - // Special case for 'auto'. This is valid input because of CSSKeywordish, - // but when read back we expect a real CSSKeywordValue. - if (offset === 'auto') - offset = new CSSKeywordValue('auto'); - - assert_offsets_equal(scrollTimeline.scrollOffsets[0], offset); - assert_offsets_equal(scrollTimeline.scrollOffsets[1], offset); - }, '\'' + offset + '\' is a valid scroll offset value'); -} - -for (const suffix of gValidScrollOffsetSuffixes) { - test(function() { - const offset = new CSSUnitValue(75, suffix); - const scrollTimeline = - new ScrollTimeline({scrollOffsets: [offset, offset]}); - - assert_offsets_equal(scrollTimeline.scrollOffsets[0], offset); - assert_offsets_equal(scrollTimeline.scrollOffsets[1], offset); - }, '\'' + suffix + '\' is a valid scroll offset unit'); -} - -// These are deliberately incomplete, just a random sampling of invalid -// values/units. -const gInvalidScrollOffsetValues = [ - '', - 'calc(360deg / 4)', - 'left', - '#ff0000', - 'rgb(0, 128, 0)', - 'url("http://www.example.com/pinkish.gif")', - 'this_is_garbage', - CSS.number(0), - // Multiple valid values. - '100px 5%', - // Values that would be valid if represented with CSS Typed OM: - 0, - '10px', - '10%', - 'calc(100% - 80px)', -]; - -const gInvalidScrollOffsetSuffixes = [ - 'deg', - 's', - 'Hz', - 'dpi', -]; - -for (const offset of gInvalidScrollOffsetValues) { - test(function() { - const constructorFunc = function() { - new ScrollTimeline({scrollOffsets: ['0px', offset]}) - }; - assert_throws_js(TypeError, constructorFunc); - }, formatOffset(offset) + ' is an invalid scroll offset value in scrollOffsets'); -} - -for (const suffix of gInvalidScrollOffsetSuffixes) { - test(function() { - const offset = '75' + suffix; - const constructorFunc = function() { - new ScrollTimeline({scrollOffsets: ['0px', offset]}); - }; - assert_throws_js(TypeError, constructorFunc); - }, '\'' + suffix + '\' is an invalid scroll offset unit in scrollOffsets'); -} - -const offset_target = document.createElement('div'); - -const gValidElementBasedScrollOffsetValues = [ - {target: offset_target}, - {target: offset_target, threshold: 0}, - {target: offset_target, threshold: 0.5}, - {target: offset_target, threshold: 1}, -]; - -for (let offset of gValidElementBasedScrollOffsetValues) { - test(function() { - const scrollTimeline = - new ScrollTimeline({scrollOffsets: [offset, offset]}); - - // Special case unspecified threshold since it gets initialized to 0. - if (!offset.hasOwnProperty('threshold')) - offset.threshold = 0; - - assert_equals(scrollTimeline.scrollOffsets[0].target, offset.target); - assert_equals(scrollTimeline.scrollOffsets[0].threshold, offset.threshold); - assert_equals(scrollTimeline.scrollOffsets[1].target, offset.target); - assert_equals(scrollTimeline.scrollOffsets[1].threshold, offset.threshold); - }, '\'' + JSON.stringify(offset) + '\' is a valid scroll offset value'); -} - -const gInvalidElementBasedScrollOffsetValues = [ - {}, // empty - {target: offset_target, threshold: "test"}, - {target: offset_target, threshold: 2}, - {target: offset_target, threshold: -0.2}, -]; - -for (let offset of gInvalidElementBasedScrollOffsetValues) { - test(function() { - const constructorFunc = function() { - new ScrollTimeline({scrollOffsets: [offset]}) - }; - assert_throws_js(TypeError, constructorFunc); - }, `'${JSON.stringify(offset)}' is an invalid scroll offset value in ` + - `scrollOffsets`); -} </script>
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/scroll-timelines/current-time-writing-modes.html b/third_party/blink/web_tests/external/wpt/scroll-animations/scroll-timelines/current-time-writing-modes.html index ca66aa6..651d5fc4 100644 --- a/third_party/blink/web_tests/external/wpt/scroll-animations/scroll-timelines/current-time-writing-modes.html +++ b/third_party/blink/web_tests/external/wpt/scroll-animations/scroll-timelines/current-time-writing-modes.html
@@ -145,228 +145,4 @@ assert_percents_equal(verticalScrollTimeline.currentTime, 10, 'Scrolled vertical timeline'); }, 'currentTime handles writing-mode: vertical-lr correctly'); - -promise_test(async t => { - const scrollerOverrides = new Map([['direction', 'rtl']]); - const scroller = setupScrollTimelineTest(scrollerOverrides); - const horizontalScrollRange = scroller.scrollWidth - scroller.clientWidth; - - const lengthScrollTimeline = new ScrollTimeline({ - source: scroller, - orientation: 'horizontal', - scrollOffsets: [CSS.px(20), 'auto'] - }); - const percentageScrollTimeline = new ScrollTimeline({ - source: scroller, - orientation: 'horizontal', - scrollOffsets: [CSS.percent(20), 'auto'] - }); - const calcScrollTimeline = new ScrollTimeline({ - source: scroller, - orientation: 'horizontal', - scrollOffsets: [CSS.percent(20).sub(CSS.px(5)), 'auto'] - }); - - // Unscrolled, all timelines should read a current time of 0, since - // the current offset (0) will be less than the startScrollOffset. - assert_percents_equal(lengthScrollTimeline.currentTime, 0, - 'Unscrolled length-based timeline'); - assert_percents_equal(percentageScrollTimeline.currentTime, 0, - 'Unscrolled percentage-based timeline'); - assert_percents_equal(calcScrollTimeline.currentTime, 0, - 'Unscrolled calc-based timeline'); - - // With direction rtl offsets are inverted, such that scrollLeft == 0 - // is the 'zero' point for currentTime. However the - // startScrollOffset is an absolute distance along the offset, so doesn't - // need adjusting. - - // Check the length-based ScrollTimeline. - scroller.scrollLeft = 0; - // Wait for new animation frame which allows the timeline to compute new - // current time. - await waitForNextFrame(); - assert_percents_equal(lengthScrollTimeline.currentTime, 0, - 'Length-based timeline before the startScrollOffset point'); - scroller.scrollLeft = -20; - await waitForNextFrame(); - assert_percents_equal(lengthScrollTimeline.currentTime, 0, - 'Length-based timeline at the startScrollOffset point'); - scroller.scrollLeft = -50; - await waitForNextFrame(); - assert_percents_equal( - lengthScrollTimeline.currentTime, - calculateCurrentTime(50, 20, horizontalScrollRange), - 'Length-based timeline after the startScrollOffset point'); - - // Check the percentage-based ScrollTimeline. - scroller.scrollLeft = -(0.19 * horizontalScrollRange); - await waitForNextFrame(); - assert_percents_equal(percentageScrollTimeline.currentTime, 0, - 'Percentage-based timeline before the startScrollOffset point'); - scroller.scrollLeft = -(0.20 * horizontalScrollRange); - await waitForNextFrame(); - assert_percents_equal(percentageScrollTimeline.currentTime, 0, - 'Percentage-based timeline at the startScrollOffset point'); - scroller.scrollLeft = -(0.4 * horizontalScrollRange); - await waitForNextFrame(); - assert_percents_equal( - percentageScrollTimeline.currentTime, - calculateCurrentTime( - 0.4 * horizontalScrollRange, 0.2 * horizontalScrollRange, - horizontalScrollRange), - 'Percentage-based timeline after the startScrollOffset point'); - - // Check the calc-based ScrollTimeline. - scroller.scrollLeft = -(0.2 * horizontalScrollRange - 10); - await waitForNextFrame(); - assert_percents_equal(calcScrollTimeline.currentTime, 0, - 'Calc-based timeline before the startScrollOffset point'); - scroller.scrollLeft = -(0.2 * horizontalScrollRange - 5); - await waitForNextFrame(); - assert_percents_equal(calcScrollTimeline.currentTime, 0, - 'Calc-based timeline at the startScrollOffset point'); - scroller.scrollLeft = -(0.2 * horizontalScrollRange); - await waitForNextFrame(); - assert_percents_equal( - calcScrollTimeline.currentTime, - calculateCurrentTime( - 0.2 * horizontalScrollRange, 0.2 * horizontalScrollRange - 5, - horizontalScrollRange), - 'Calc-based timeline after the startScrollOffset point'); -}, 'currentTime handles startScrollOffset with direction: rtl correctly'); - -promise_test(async t => { - const scrollerOverrides = new Map([['direction', 'rtl']]); - const scroller = setupScrollTimelineTest(scrollerOverrides); - const horizontalScrollRange = scroller.scrollWidth - scroller.clientWidth; - - const lengthScrollTimeline = new ScrollTimeline({ - source: scroller, - orientation: 'horizontal', - scrollOffsets: [CSS.px(horizontalScrollRange - 20)] - }); - const percentageScrollTimeline = new ScrollTimeline({ - source: scroller, - orientation: 'horizontal', - scrollOffsets: [CSS.percent(80)] - }); - const calcScrollTimeline = new ScrollTimeline({ - source: scroller, - orientation: 'horizontal', - scrollOffsets: [CSS.percent(80).add(CSS.px(5))] - }); - - // With direction rtl offsets are inverted, such that scrollLeft == 0 - // is the 'zero' point for currentTime. However the - // endScrollOffset is an absolute distance along the offset, so doesn't need - // adjusting. - - // Check the length-based ScrollTimeline. - scroller.scrollLeft = -horizontalScrollRange; - // Wait for new animation frame which allows the timeline to compute new - // current time. - await waitForNextFrame(); - assert_percents_equal(lengthScrollTimeline.currentTime, 100, - 'Length-based timeline after the endScrollOffset point'); - scroller.scrollLeft = 20 - horizontalScrollRange; - await waitForNextFrame(); - assert_percents_equal(lengthScrollTimeline.currentTime, 100, - 'Length-based timeline at the endScrollOffset point'); - scroller.scrollLeft = 50 - horizontalScrollRange; - await waitForNextFrame(); - assert_percents_equal( - lengthScrollTimeline.currentTime, - calculateCurrentTime( - horizontalScrollRange - 50, 0, horizontalScrollRange - 20), - 'Length-based timeline before the endScrollOffset point'); - - // Check the percentage-based ScrollTimeline. - scroller.scrollLeft = 0.19 * horizontalScrollRange - horizontalScrollRange; - await waitForNextFrame(); - assert_percents_equal(percentageScrollTimeline.currentTime, 100, - 'Percentage-based timeline after the endScrollOffset point'); - scroller.scrollLeft = 0.20 * horizontalScrollRange - horizontalScrollRange; - await waitForNextFrame(); - assert_percents_equal(percentageScrollTimeline.currentTime, 100, - 'Percentage-based timeline at the endScrollOffset point'); - scroller.scrollLeft = 0.4 * horizontalScrollRange - horizontalScrollRange; - await waitForNextFrame(); - assert_percents_equal( - percentageScrollTimeline.currentTime, - calculateCurrentTime( - 0.6 * horizontalScrollRange, 0, 0.8 * horizontalScrollRange), - 'Percentage-based timeline before the endScrollOffset point'); - - // Check the calc-based ScrollTimeline. 80% + 5px - scroller.scrollLeft = -0.8 * horizontalScrollRange - 10; - await waitForNextFrame(); - assert_percents_equal(calcScrollTimeline.currentTime, 100, - 'Calc-based timeline after the endScrollOffset point'); - scroller.scrollLeft = -0.8 * horizontalScrollRange - 5; - await waitForNextFrame(); - assert_percents_equal(calcScrollTimeline.currentTime, 100, - 'Calc-based timeline at the endScrollOffset point'); - scroller.scrollLeft = -0.8 * horizontalScrollRange; - await waitForNextFrame(); - assert_percents_equal( - calcScrollTimeline.currentTime, - calculateCurrentTime( - 0.8 * horizontalScrollRange, 0, 0.8 * horizontalScrollRange + 5), - 'Calc-based timeline before the endScrollOffset point'); -}, 'currentTime handles endScrollOffset with direction: rtl correctly'); - -promise_test(async t => { - const scrollerOverrides = new Map([['direction', 'rtl']]); - const scroller = setupScrollTimelineTest(scrollerOverrides); - const horizontalScrollRange = scroller.scrollWidth - scroller.clientWidth; - - // When the endScrollOffset is equal to the maximum scroll offset (and there - // are no fill modes), the endScrollOffset is treated as inclusive. - const inclusiveAutoScrollTimeline = new ScrollTimeline({ - source: scroller, - orientation: 'inline', - }); - const inclusiveLengthScrollTimeline = new ScrollTimeline({ - source: scroller, - orientation: 'inline', - scrollOffsets: [CSS.px(horizontalScrollRange)] - }); - const inclusivePercentageScrollTimeline = new ScrollTimeline({ - source: scroller, - orientation: 'inline', - scrollOffsets: [CSS.percent(100)] - }); - const inclusiveCalcScrollTimeline = new ScrollTimeline({ - source: scroller, - orientation: 'inline', - scrollOffsets: [CSS.percent(80).sub(CSS.px(0.2 * horizontalScrollRange))] - }); - - // With direction rtl offsets are inverted, such that scrollLeft == - // horizontalScrollRange is the 'zero' point for currentTime. However the - // endScrollOffset is an absolute distance along the offset, so doesn't need - // adjusting. - - scroller.scrollLeft = 0; - let expectedCurrentTime = calculateCurrentTime( - scroller.scrollLeft, 0, horizontalScrollRange); - // Wait for new animation frame which allows the timeline to compute new - // current time. - await waitForNextFrame(); - - assert_percents_equal( - inclusiveAutoScrollTimeline.currentTime, expectedCurrentTime, - 'Inclusive auto timeline at the endScrollOffset point'); - assert_percents_equal( - inclusiveLengthScrollTimeline.currentTime, expectedCurrentTime, - 'Inclusive length-based timeline at the endScrollOffset point'); - assert_percents_equal( - inclusivePercentageScrollTimeline.currentTime, expectedCurrentTime, - 'Inclusive percentage-based timeline at the endScrollOffset point'); - assert_percents_equal( - inclusiveCalcScrollTimeline.currentTime, expectedCurrentTime, - 'Inclusive calc-based timeline at the endScrollOffset point'); -}, 'currentTime handles endScrollOffset (inclusive case) with direction: rtl' + - ' correctly'); </script>
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/scroll-timelines/current-time.html b/third_party/blink/web_tests/external/wpt/scroll-animations/scroll-timelines/current-time.html deleted file mode 100644 index f011b40c1..0000000 --- a/third_party/blink/web_tests/external/wpt/scroll-animations/scroll-timelines/current-time.html +++ /dev/null
@@ -1,417 +0,0 @@ -<!DOCTYPE html> -<meta charset="utf-8"> -<title>ScrollTimeline current time algorithm</title> -<link rel="help" href="https://wicg.github.io/scroll-animations/#current-time-algorithm"> -<script src="/resources/testharness.js"></script> -<script src="/resources/testharnessreport.js"></script> -<script src="/web-animations/testcommon.js"></script> -<script src="./testcommon.js"></script> - -<body></body> - -<script> -'use strict'; - -promise_test(async t => { - const scroller = setupScrollTimelineTest(); - const scrollTimeline = new ScrollTimeline({ source: scroller }); - - assert_equals(scrollTimeline.duration.unit, "percent", - "duration returns as a percent for scroll timelines"); - assert_equals(scrollTimeline.duration.value, 100, "duration is 100%"); -}, "Scroll timeline correctly returns duration as 100%"); - -promise_test(async t => { - const scroller = setupScrollTimelineTest(); - const scrollerSize = scroller.scrollHeight - scroller.clientHeight; - - const blockScrollTimeline = new ScrollTimeline({ - source: scroller, - orientation: 'block' - }); - const inlineScrollTimeline = new ScrollTimeline({ - source: scroller, - orientation: 'inline' - }); - const horizontalScrollTimeline = new ScrollTimeline({ - source: scroller, - orientation: 'horizontal' - }); - const verticalScrollTimeline = new ScrollTimeline({ - source: scroller, - orientation: 'vertical' - }); - - // Unscrolled, all timelines should read a currentTime of 0. - assert_percents_equal(blockScrollTimeline.currentTime, 0, - 'Unscrolled block timeline'); - assert_percents_equal(inlineScrollTimeline.currentTime, 0, - 'Unscrolled inline timeline'); - assert_percents_equal(horizontalScrollTimeline.currentTime, 0, - 'Unscrolled horizontal timeline'); - assert_percents_equal(verticalScrollTimeline.currentTime, 0, - 'Unscrolled vertical timeline'); - - // Do some scrolling and make sure that the ScrollTimelines update. - scroller.scrollTop = 50; - scroller.scrollLeft = 75; - // Wait for new animation frame which allows the timeline to compute new - // current time. - await waitForNextFrame(); - - const inlineScrollRange = scroller.scrollWidth - scroller.clientWidth; - const expectedInlineCurrentTime = - 100 * scroller.scrollLeft / inlineScrollRange; - - const blockScrollRange = scroller.scrollHeight - scroller.clientHeight; - const expectedBlockCurrentTime = - 100 * scroller.scrollTop / blockScrollRange; - - assert_percents_approx_equal(blockScrollTimeline.currentTime, - expectedBlockCurrentTime, - blockScrollRange, - 'Scrolled block timeline'); - assert_percents_approx_equal(inlineScrollTimeline.currentTime, - expectedInlineCurrentTime, - inlineScrollRange, - 'Scrolled inline timeline'); - assert_percents_approx_equal(horizontalScrollTimeline.currentTime, - expectedInlineCurrentTime, - inlineScrollRange, - 'Scrolled horizontal timeline'); - assert_percents_approx_equal(verticalScrollTimeline.currentTime, - expectedBlockCurrentTime, - blockScrollRange, - 'Scrolled vertical timeline'); -}, 'currentTime calculates the correct time based on scroll progress'); - - -promise_test(async t => { - const scroller = setupScrollTimelineTest(); - const scrollerSize = scroller.scrollHeight - scroller.clientHeight; - - const lengthScrollTimeline = new ScrollTimeline({ - source: scroller, - orientation: 'block', - scrollOffsets: [CSS.px(20), 'auto'] - }); - const percentageScrollTimeline = new ScrollTimeline({ - source: scroller, - orientation: 'block', - scrollOffsets: [CSS.percent(20), 'auto'] - }); - const calcScrollTimeline = new ScrollTimeline({ - source: scroller, - orientation: 'block', - scrollOffsets: [CSS.percent(20).sub(CSS.px(5)), 'auto'] - }); - - // Unscrolled all timelines should read a current time of 0, as the - // current offset (0) will be less than the startScrollOffset. - assert_percents_equal(lengthScrollTimeline.currentTime, 0, - 'Unscrolled length-based timeline current time'); - assert_percents_equal(percentageScrollTimeline.currentTime, 0, - 'Unscrolled percentage-based timeline current time'); - assert_percents_equal(calcScrollTimeline.currentTime, 0, - 'Unscrolled calc-based timeline current time'); - - // Check the length-based ScrollTimeline. - scroller.scrollTop = 19; - // Wait for new animation frame which allows the timeline to compute new - // current time. - await waitForNextFrame(); - assert_percents_equal(lengthScrollTimeline.currentTime, 0, - 'Length-based timeline current time before the startScrollOffset point'); - scroller.scrollTop = 20; - await waitForNextFrame(); - assert_percents_equal(lengthScrollTimeline.currentTime, 0, - 'Length-based timeline current time at the startScrollOffset point'); - scroller.scrollTop = 50; - await waitForNextFrame(); - assert_percents_equal( - lengthScrollTimeline.currentTime, - calculateCurrentTime(50, 20, scrollerSize), - 'Length-based timeline current time after the startScrollOffset point'); - - // Check the percentage-based ScrollTimeline. - scroller.scrollTop = 0.19 * scrollerSize; - await waitForNextFrame(); - assert_percents_equal(percentageScrollTimeline.currentTime, 0, - 'Percentage-based timeline current time before the startScrollOffset ' + - 'point'); - scroller.scrollTop = 0.20 * scrollerSize; - await waitForNextFrame(); - assert_percents_equal(percentageScrollTimeline.currentTime, 0, - 'Percentage-based timeline current time at the startScrollOffset point'); - scroller.scrollTop = 0.50 * scrollerSize; - await waitForNextFrame(); - assert_percents_equal( - percentageScrollTimeline.currentTime, - calculateCurrentTime( - scroller.scrollTop, 0.2 * scrollerSize, scrollerSize), - 'Percentage-based timeline current time after the startScrollOffset ' + - 'point'); - - // Check the calc-based ScrollTimeline. - scroller.scrollTop = 0.2 * scrollerSize - 10; - await waitForNextFrame(); - assert_percents_equal(calcScrollTimeline.currentTime, 0, - 'Calc-based timeline current time before the startScrollOffset point'); - scroller.scrollTop = 0.2 * scrollerSize - 5; - await waitForNextFrame(); - assert_percents_equal(calcScrollTimeline.currentTime, 0, - 'Calc-based timeline current time at the startScrollOffset point'); - scroller.scrollTop = 0.2 * scrollerSize; - await waitForNextFrame(); - assert_percents_equal( - calcScrollTimeline.currentTime, - calculateCurrentTime( - scroller.scrollTop, 0.2 * scrollerSize - 5, scrollerSize), - 'Calc-based timeline current time after the startScrollOffset point'); -}, 'currentTime handles startScrollOffset correctly'); - -promise_test(async t => { - const scroller = setupScrollTimelineTest(); - const scrollerSize = scroller.scrollHeight - scroller.clientHeight; - - // When the endScrollOffset is equal to the maximum scroll offset (and there - // are no fill modes), the endScrollOffset is treated as inclusive. - const inclusiveAutoScrollTimeline = new ScrollTimeline({ - source: scroller, - orientation: 'block', - }); - const inclusiveLengthScrollTimeline = new ScrollTimeline({ - source: scroller, - orientation: 'block', - scrollOffsets: [CSS.px(scrollerSize)] - }); - const inclusivePercentageScrollTimeline = new ScrollTimeline({ - source: scroller, - orientation: 'block', - scrollOffsets: [CSS.percent(100)] - }); - const inclusiveCalcScrollTimeline = new ScrollTimeline({ - source: scroller, - orientation: 'block', - scrollOffsets: [CSS.percent(80).add(CSS.px(0.2 * scrollerSize))] - }); - - scroller.scrollTop = scrollerSize; - let expectedCurrentTime = calculateCurrentTime( - scroller.scrollTop, 0, scrollerSize); - - // Wait for new animation frame which allows the timeline to compute new - // current time. - await waitForNextFrame(); - - assert_percents_equal( - inclusiveAutoScrollTimeline.currentTime, expectedCurrentTime, - 'Inclusive auto timeline at the endScrollOffset point'); - assert_percents_equal( - inclusiveLengthScrollTimeline.currentTime, expectedCurrentTime, - 'Inclusive length-based timeline at the endScrollOffset point'); - assert_percents_equal( - inclusivePercentageScrollTimeline.currentTime, expectedCurrentTime, - 'Inclusive percentage-based timeline at the endScrollOffset point'); - assert_percents_equal( - inclusiveCalcScrollTimeline.currentTime, expectedCurrentTime, - 'Inclusive calc-based timeline at the endScrollOffset point'); -}, 'currentTime handles endScrollOffset correctly (inclusive cases)'); - -promise_test(async t => { - const scroller = setupScrollTimelineTest(); - const scrollerSize = scroller.scrollHeight - scroller.clientHeight; - - const lengthScrollTimeline = new ScrollTimeline({ - source: scroller, - orientation: 'block', - scrollOffsets: [CSS.px(scrollerSize - 20)] - }); - const percentageScrollTimeline = new ScrollTimeline({ - source: scroller, - orientation: 'block', - scrollOffsets: [CSS.percent(80)] - }); - const calcScrollTimeline = new ScrollTimeline({ - source: scroller, - orientation: 'block', - scrollOffsets: [CSS.percent(80).add(CSS.px(5))] - }); - - // Check the length-based ScrollTimeline. - scroller.scrollTop = scrollerSize; - // Wait for new animation frame which allows the timeline to compute new - // current time. - await waitForNextFrame(); - assert_percents_equal(lengthScrollTimeline.currentTime, 100, - 'Length-based timeline current time after the endScrollOffset point'); - scroller.scrollTop = scrollerSize - 20; - await waitForNextFrame(); - assert_percents_equal(lengthScrollTimeline.currentTime, 100, - 'Length-based timeline current time at the endScrollOffset point'); - scroller.scrollTop = scrollerSize - 50; - await waitForNextFrame(); - assert_percents_equal( - lengthScrollTimeline.currentTime, - calculateCurrentTime(scrollerSize - 50, 0, scrollerSize - 20), - 'Length-based timeline current time before the endScrollOffset point'); - - // Check the percentage-based ScrollTimeline. - scroller.scrollTop = 0.81 * scrollerSize; - await waitForNextFrame(); - assert_percents_equal(percentageScrollTimeline.currentTime, 100, - 'Percentage-based timeline current time after the endScrollOffset point'); - scroller.scrollTop = 0.80 * scrollerSize; - await waitForNextFrame(); - assert_percents_equal(percentageScrollTimeline.currentTime, 100, - 'Percentage-based timeline current time at the endScrollOffset point'); - scroller.scrollTop = 0.50 * scrollerSize; - await waitForNextFrame(); - assert_percents_equal( - percentageScrollTimeline.currentTime, - calculateCurrentTime(scroller.scrollTop, 0, 0.8 * scrollerSize), - 'Percentage-based timeline current time before the endScrollOffset ' + - 'point'); - - // Check the calc-based ScrollTimeline. - scroller.scrollTop = 0.8 * scrollerSize + 6; - await waitForNextFrame(); - assert_percents_equal(calcScrollTimeline.currentTime, 100, - 'Calc-based timeline current time after the endScrollOffset point'); - scroller.scrollTop = 0.8 * scrollerSize + 5; - await waitForNextFrame(); - assert_percents_equal(calcScrollTimeline.currentTime, 100, - 'Calc-based timeline current time at the endScrollOffset point'); - scroller.scrollTop = 0.5 * scrollerSize; - await waitForNextFrame(); - assert_percents_equal( - calcScrollTimeline.currentTime, - calculateCurrentTime(scroller.scrollTop, 0, 0.8 * scrollerSize + 5), - 'Calc-based timeline current time before the endScrollOffset point'); -}, 'currentTime handles endScrollOffset correctly'); - -promise_test(async t => { - const scroller = setupScrollTimelineTest(); - const scrollerSize = scroller.scrollHeight - scroller.clientHeight; - - const scrollTimeline = new ScrollTimeline({ - source: scroller, - orientation: 'block', - scrollOffsets: [CSS.px(20), CSS.px(scrollerSize - 50)] - }); - - scroller.scrollTop = 150; - // Wait for new animation frame which allows the timeline to compute new - // current time. - await waitForNextFrame(); - assert_percents_equal( - scrollTimeline.currentTime, - calculateCurrentTime(150, 20, scrollerSize - 50)); -}, 'currentTime handles startScrollOffset and endScrollOffset together' + - ' correctly'); - -promise_test(async t => { - const scroller = setupScrollTimelineTest(); - const scrollTimeline = new ScrollTimeline({ - source: scroller, - orientation: 'block', - scrollOffsets: [CSS.px(20), CSS.px(20)] - }); - - scroller.scrollTop = 150; - // Wait for new animation frame which allows the timeline to compute new - // current time. - await waitForNextFrame(); - assert_percents_equal(scrollTimeline.currentTime, 100); -}, 'currentTime handles startScrollOffset == endScrollOffset correctly'); - -promise_test(async t => { - const scroller = setupScrollTimelineTest(); - const scrollTimeline = new ScrollTimeline({ - source: scroller, - orientation: 'block', - scrollOffsets: [CSS.px(50), CSS.px(10)] - }); - - scroller.scrollTop = 40; - // Wait for new animation frame which allows the timeline to compute new - // current time. - await waitForNextFrame(); - assert_percents_equal(scrollTimeline.currentTime, 0); - scroller.scrollTop = 60; - await waitForNextFrame(); - assert_percents_equal(scrollTimeline.currentTime, 100); -}, 'currentTime handles startScrollOffset > endScrollOffset correctly'); - -promise_test(async t => { - const scroller = setupScrollTimelineTest(); - const scrollTimeline = new ScrollTimeline({ - source: scroller, - orientation: 'block', - scrollOffsets: [CSS.px(10), CSS.px(20), CSS.px(40), CSS.px(70), CSS.px(90)], - }); - - var offset = 0; - var w = 1 / 4; // offset weight - var p = 0; // progress within the offset - - scroller.scrollTop = 10; - // Wait for new animation frame which allows the timeline to compute new - // current time. - await waitForNextFrame(); - - assert_percents_equal(scrollTimeline.currentTime, (offset + p) * w * 100, - "current time calculation when scroll = " + scroller.scrollTop); - - p = (12 - 10) / (20 - 10); - scroller.scrollTop = 12; - await waitForNextFrame(); - assert_percents_equal(scrollTimeline.currentTime, (offset + p) * w * 100, - "current time calculation when scroll = " + scroller.scrollTop); - - offset = 1; - p = 0; - scroller.scrollTop = 20; - await waitForNextFrame(); - assert_percents_equal(scrollTimeline.currentTime, (offset + p) * w * 100, - "current time calculation when scroll = " + scroller.scrollTop); - - p = (35 - 20) / (40 - 20); - scroller.scrollTop = 35; - await waitForNextFrame(); - assert_percents_equal(scrollTimeline.currentTime, (offset + p) * w * 100, - "current time calculation when scroll = " + scroller.scrollTop); - - offset = 2; - p = 0; - scroller.scrollTop = 40; - await waitForNextFrame(); - assert_percents_equal(scrollTimeline.currentTime, (offset + p) * w * 100, - "current time calculation when scroll = " + scroller.scrollTop); - - p = (60 - 40) / (70 - 40); - scroller.scrollTop = 60; - await waitForNextFrame(); - assert_percents_equal(scrollTimeline.currentTime, (offset + p) * w * 100, - "current time calculation when scroll = " + scroller.scrollTop); - - offset = 3; - p = 0; - scroller.scrollTop = 70; - await waitForNextFrame(); - assert_percents_equal(scrollTimeline.currentTime, (offset + p) * w * 100, - "current time calculation when scroll = " + scroller.scrollTop); - - p = (80 - 70) / (90 - 70); - scroller.scrollTop = 80; - await waitForNextFrame(); - assert_percents_equal(scrollTimeline.currentTime, (offset + p) * w * 100, - "current time calculation when scroll = " + scroller.scrollTop); - - scroller.scrollTop = 90; - await waitForNextFrame(); - assert_percents_equal(scrollTimeline.currentTime, 100, - "current time calculation when scroll = " + scroller.scrollTop); -}, 'currentTime calculations when multiple scroll offsets are specified'); - -</script>
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/scroll-timelines/element-based-offset-clamp.html b/third_party/blink/web_tests/external/wpt/scroll-animations/scroll-timelines/element-based-offset-clamp.html deleted file mode 100644 index 6aea8217..0000000 --- a/third_party/blink/web_tests/external/wpt/scroll-animations/scroll-timelines/element-based-offset-clamp.html +++ /dev/null
@@ -1,221 +0,0 @@ -<!DOCTYPE html> -<meta charset=utf-8> -<title>Test clamping logic of element-based scroll offset for scroll timeline.</title> -<script src="/resources/testharness.js"></script> -<script src="/resources/testharnessreport.js"></script> -<script src="/web-animations/testcommon.js"></script> -<script src="testcommon.js"></script> - -<style> -/* - * Overflow hidden prevents user scroll including mouse wheel; however, the - * element is still a scrollable container and can be scrolled programmatically. - * Removing the visible scrollbars in this manner simplifies the position - * calculations in the text. - */ -.scroller { - overflow: hidden; - height: 500px; - width: 500px; - will-change: transform; -} - -.contents { - height: 1200px; - width: 1200px; - position: relative; -} - -.vertical #target { - background: blue; - border-top: 0px solid pink; - border-bottom: 0px solid pink; - box-sizing: border-box; - position: absolute; - width: 100%; - top: var(--start-position); - height: calc(var(--end-position) - var(--start-position)); -} - -.horizontal #target { - background: blue; - border-left: 0px solid pink; - border-right: 0px solid pink; - box-sizing: border-box; - position: absolute; - height: 100%; - left: var(--start-position); - width: calc(var(--end-position) - var(--start-position)); -} -</style> -<div id="log"></div> -<script> - 'use strict'; - - function createScrollerWithTarget(test, config) { - const orientationClass = config.orientation; - const positions = ` - --start-position: ${config.startElementPosition}; - --end-position: ${config.endElementPosition};` - - var scroller = createDiv(test); - scroller.innerHTML = - `<div class='contents' style="${positions}"> - <div id='target'></div> - </div>`; - scroller.classList.add('scroller'); - scroller.classList.add(orientationClass); - - return scroller; - } - - async function createScrollAnimationTest(description, config) { - promise_test(async t => { - const scroller = createScrollerWithTarget(t, config); - t.add_cleanup(() => scroller.remove()); - - const target = scroller.querySelector("#target"); - - // Force layout before creating the scroll timeline to ensure the correct - // scroll range. - target.offsetHeight; - - const timeline = createScrollTimeline(t, { - source: scroller, - orientation: config.orientation, - fill: 'both', - scrollOffsets: [{target: target, edge: 'end', ...config.start}, - {target: target, edge:'start', ...config.end }] - }); - - // Wait for new animation frame which allows the timeline to compute new - // current time. - await waitForNextFrame(); - - const animation = createScrollLinkedAnimation(t, timeline); - - // Verify initial start and current times in Idle state. - assert_equals(animation.currentTime, null, - "The current time is null in Idle state."); - assert_equals(animation.startTime, null, - "The start time is null in Idle state."); - - animation.play(); - assert_true(animation.pending, "Animation is in pending state."); - // Verify initial start and current times in Pending state. - assert_percents_equal(animation.currentTime, 0, - "The current time is zero in Pending state."); - assert_percents_equal(animation.startTime, 0, - "The start time is zero in Pending state."); - - await animation.ready; - // Verify initial start and current times in Playing state. - assert_percents_equal(animation.currentTime, 0, - "The current time is zero in Playing state."); - assert_percents_equal(animation.startTime, 0, - "The start time is zero in Playing state."); - - // Now do some scrolling and make sure that the Animation current time is - // correct. - if (config.orientation == 'vertical') { - scroller.scrollTo({top: config.scrollTo}); - assert_equals(scroller.scrollTop, config.scrollTo); - } else { - scroller.scrollTo({left: config.scrollTo}); - assert_equals(scroller.scrollLeft, config.scrollTo); - } - - await waitForNextFrame(); - - assert_percents_equal( - animation.timeline.currentTime, - config.expectedCurrentTime, - "The timeline current time corresponds to the scroll position of " + - "the scroller."); - assert_percents_equal( - animation.currentTime, - config.expectedCurrentTime, - "The animation current time corresponds to the scroll position of " + - "the scroller."); - assert_percents_equal( - animation.effect.getComputedTiming().localTime, - config.expectedCurrentTime, - "Effect local time corresponds to the scroll position of the " + - "scroller."); - }, description); - } - - // We have no scrollbar and the scroller is symmetric on x & y axis so this - // static value is axis and platform agnostic. - const scroll_max = 700; - - // For this test we setup a single target, and scroll timeline in a way that - // our animation runs from when target enters the scroll port until it fully - // exits it. Then we create various edgecase scenarios to see the clamping - // logic. - // - // Scroller has 500px heights with 1200px content which translates to - // 0 < scroll < 700px - // - // +----------+ ^ - // | | | - // | Scroller | | - // | | | scrollRange - // | | | - // +----------+ | +--+ - // |TT| | |TT| - // +--+ v +----------+ - // | | - // | Scroller | - // | | - // | | - // +----------+ - // - // For each test the expected timeline start/end is in the comment to help - // with the verification. - // - // Note: expectedCurrentTime values are percentages. They are used as such: - // CSS.percent(expectedCurrentTime) - const tests = { - // offsets: [0, 600] - "no clamping is expected": { - startElementPosition: '500px', - endElementPosition: '600px', - scrollTo: 300, - expectedCurrentTime: 50, - }, - // offsets: [0, 600] - "start is visible at zero offset and should get clamped": { - startElementPosition: '400px', - endElementPosition: '600px', - scrollTo: 300, - expectedCurrentTime: 50, - }, - - // offsets: [0, scroll_max] - "end is not reachable and should be clamped": { - startElementPosition: '500px', - endElementPosition: '800px', - scrollTo: scroll_max / 2, - expectedCurrentTime: 50, - }, - - // offsets: [0, scroll_max] - "both start and end are clamped": { - startElementPosition: '400px', - endElementPosition: '800px', - scrollTo: scroll_max / 2, - expectedCurrentTime: 50, - }, - }; - - for (let orientation of ['vertical', 'horizontal']) { - for (let testName in tests) { - const description = `Animation start and current times are correct given - element-based offsets for orienation ${orientation} and ${testName}.`; - const config = tests[testName]; - config.orientation = orientation; - createScrollAnimationTest(description, config); - } - } -</script>
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/scroll-timelines/element-based-offset-unresolved.html b/third_party/blink/web_tests/external/wpt/scroll-animations/scroll-timelines/element-based-offset-unresolved.html deleted file mode 100644 index 5b746d4..0000000 --- a/third_party/blink/web_tests/external/wpt/scroll-animations/scroll-timelines/element-based-offset-unresolved.html +++ /dev/null
@@ -1,88 +0,0 @@ -<!DOCTYPE html> -<meta charset=utf-8> -<title>Validate cases where element-based scroll offsets are unresolved.</title> -<script src="/resources/testharness.js"></script> -<script src="/resources/testharnessreport.js"></script> -<script src="/web-animations/testcommon.js"></script> -<script src="testcommon.js"></script> - -<style> - .scroller { - overflow: auto; - height: 500px; - width: 500px; - will-change: transform; - } - - .contents { - height: 2000px; - width: 2000px; - position: relative; - } - - #start, #end { - background: blue; - border-top: 5px solid pink; - box-sizing: border-box; - width: 100%; - height: 50px; - } - - #start { - position: absolute; - top: 50px; - } - - #end { - position: absolute; - top: 1050px; - } -</style> -<div id="log"></div> - -<div id="not_a_descendant"></div> - -<script> -'use strict'; - -promise_test(async t => { - const scroller = createScrollerWithStartAndEnd(t); - t.add_cleanup(() => scroller.remove()); - scroller.scrollTo({ top: 500 }); - - const not_a_descendant = document.querySelector("#not_a_descendant"); - const end = document.querySelector("#end") - - const timeline = createScrollTimeline(t, { - source: scroller, - orientation: 'block', - scrollOffsets: [{ target: not_a_descendant }, { target: end }] - }); - - await waitForNextFrame(); - assert_equals(timeline.currentTime, null, "The timeline should not be active."); -}, "A valid element-based offset's target should be a descendant of timeline's source"); - -promise_test(async t => { - const scroller = createScrollerWithStartAndEnd(t); - t.add_cleanup(() => scroller.remove()); - scroller.scrollTo({ top: 500 }); - - const start = document.querySelector("#start"); - const end = document.querySelector("#end") - - const timeline = createScrollTimeline(t, { - source: scroller, - orientation: 'block', - scrollOffsets: [{ target: start }, { target: end }] - }); - - start.style.display = "none"; - await waitForNextFrame(); - assert_equals(timeline.currentTime, null, "The timeline should not be active."); - - start.style.display = "block"; - await waitForNextFrame(); - assert_not_equals(timeline.currentTime, null, "The timeline should be active."); -}, "A valid element-based offset's target should have a layout box"); -</script>
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/scroll-timelines/element-based-offset.html b/third_party/blink/web_tests/external/wpt/scroll-animations/scroll-timelines/element-based-offset.html deleted file mode 100644 index 7f18ce8e..0000000 --- a/third_party/blink/web_tests/external/wpt/scroll-animations/scroll-timelines/element-based-offset.html +++ /dev/null
@@ -1,222 +0,0 @@ -<!DOCTYPE html> -<meta charset=utf-8> -<title>Test element-based scroll offset for scroll timeline.</title> -<script src="/resources/testharness.js"></script> -<script src="/resources/testharnessreport.js"></script> -<script src="/web-animations/testcommon.js"></script> -<script src="testcommon.js"></script> - -<style> - .scroller { - overflow: auto; - height: 500px; - width: 500px; - will-change: transform; - } - - .contents { - height: 2000px; - width: 2000px; - position: relative; - } - - .vertical #start, .vertical #end { - background: blue; - border-top: 5px solid pink; - box-sizing: border-box; - width: 100%; - height: 50px; - } - - .vertical #start { - position: absolute; - top: 50px; - } - - .vertical #end { - position: absolute; - top: 1050px; - } - - .horizontal #start, .horizontal #end { - background: blue; - border-left:5px solid pink; - box-sizing: border-box; - height: 100%; - width: 50px; - } - - .horizontal #start { - position: absolute; - left: 50px; - } - - .horizontal #end { - position: absolute; - left: 1050px; - } -</style> -<div id="log"></div> -<script> - 'use strict'; - - async function createScrollAnimationTest(description, config) { - promise_test(async t => { - const scroller = createScrollerWithStartAndEnd(t, config.orientation); - t.add_cleanup(() => scroller.remove()); - - const start = scroller.querySelector("#start"); - const end = scroller.querySelector("#end"); - - // Force layout to ensure the scroll timeline gets the correct scroll - // range. - start.offsetHeight; - - const timeline = createScrollTimeline(t, { - source: scroller, - orientation: config.orientation, - fill: 'both', - scrollOffsets: - [{target: start, ...config.start}, {target: end, ...config.end }] - }); - - // Wait for new animation frame which allows the timeline to compute new - // current time. - await waitForNextFrame(); - - const animation = createScrollLinkedAnimation(t, timeline); - const scrollRange = (config.orientation == 'vertical') - ? end.offsetTop - start.offsetTop - : end.offsetLeft - start.offsetLeft; - - // Verify initial start and current times in Idle state. - assert_equals(animation.currentTime, null, - "The current time is null in Idle state."); - assert_equals(animation.startTime, null, - "The start time is null in Idle state."); - - animation.play(); - assert_true(animation.pending, "Animation is in pending state."); - // Verify initial start and current times in Pending state. - assert_percents_equal(animation.currentTime, 0, - "The current time is zero in Pending state."); - assert_percents_equal(animation.startTime, 0, - "The start time is zero in Pending state."); - - await animation.ready; - // Verify initial start and current times in Playing state. - assert_percents_equal(animation.currentTime, 0, - "The current time is zero in Playing state."); - assert_percents_equal(animation.startTime, 0, - "The start time is zero in Playing state."); - - // Now do some scrolling and make sure that the Animation current time is - // correct. - if (config.orientation == 'vertical') { - scroller.scrollTo({top: config.scrollTo}); - assert_equals(scroller.scrollTop, config.scrollTo); - } else { - scroller.scrollTo({left: config.scrollTo}); - assert_equals(scroller.scrollLeft, config.scrollTo); - } - - await waitForNextFrame(); - assert_percents_approx_equal( - animation.timeline.currentTime, - config.expectedCurrentTime, - scrollRange, - "The timeline current time corresponds to the scroll position of " + - "the scroller."); - assert_percents_approx_equal( - animation.currentTime, - config.expectedCurrentTime, - scrollRange, - "The animation current time corresponds to the scroll position of " + - "the scroller."); - assert_percents_approx_equal( - animation.effect.getComputedTiming().localTime, - config.expectedCurrentTime, - scrollRange, - "Effect local time corresponds to the scroll position of the " + - "scroller."); - }, description); - } - - // start is @ 50px - // end is @ 1050px - // both have 50px heights - // scroller has 500px heights - // For each test the expected start/end is in the comment to help with the - // verification. - // - // Note: expectedCurrentTime values are percentages. They are used as such: - // CSS.percent(expectedCurrentTime) - const tests = { - // offsets: [100, 1100] - "at start": { - scrollTo: 100, - expectedCurrentTime: 0, - }, - // offsets: [100, 1100] - "after start": { - scrollTo: 200, - expectedCurrentTime: 10, - }, - // offsets: [100, 1100] - "at middle" : { - scrollTo: 600, - expectedCurrentTime: 50, - }, - // offsets: [100, 1100] - "at end" : { - scrollTo: 1099, - expectedCurrentTime: 99.9, - }, - // offsets: [100, 1100] - "after end" : { - scrollTo: 1150, - expectedCurrentTime: 100, - }, - // offsets: [75, 1075] - "with threshold 0.5" : { - // give threshold to both start and end to keep scrollRange - // 1000 which simplifies the calculation. - start: {threshold: 0.5}, - end: {threshold: 0.5}, - scrollTo: 600 - 25, - expectedCurrentTime: 50, - }, - // offsets: [50, 1050] - "with threshold 1.0": { - start: {threshold: 1.0}, - end: {threshold: 1.0}, - scrollTo: 600 - 50, - expectedCurrentTime: 50, - }, - // offset: [100, 550] - "with end edge" : { - end: {edge: "end"}, - scrollTo: 325, - expectedCurrentTime: 50, - }, - // offset: [100, 600] - "with end edge and threshold 1.0": { - end: { - threshold: 1.0, - edge: "end" - }, - scrollTo: 350, - expectedCurrentTime: 50, - }, - }; - - for (let orientation of ['vertical', 'horizontal']) { - for (let testName in tests) { - const description = `Animation start and current times are correct given - element-based offsets for orienation ${orientation} and ${testName}.`; - const config = tests[testName]; - config.orientation = orientation; - createScrollAnimationTest(description, config); - } - } -</script>
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/scroll-timelines/layout-changes-on-percentage-based-timeline.html b/third_party/blink/web_tests/external/wpt/scroll-animations/scroll-timelines/layout-changes-on-percentage-based-timeline.html index c69bee2..c5a46a5 100644 --- a/third_party/blink/web_tests/external/wpt/scroll-animations/scroll-timelines/layout-changes-on-percentage-based-timeline.html +++ b/third_party/blink/web_tests/external/wpt/scroll-animations/scroll-timelines/layout-changes-on-percentage-based-timeline.html
@@ -56,31 +56,29 @@ [ { transform: 'translateY(0)', opacity: 1 }, { transform: 'translateY(200px)', opacity: 0 } - ], { - duration: 1000, - } + ] ); const scroller = document.getElementById('scroller'); const timeline = new ScrollTimeline({ - source: scroller, - scrollOffsets: [CSS.percent(20), CSS.percent(80)] + source: scroller }); const animation = new Animation(effect, timeline); animation.play(); + animation.ready.then(_ => { + // Moves the scroller to the end point (240px). + const maxScroll = scroller.scrollHeight - scroller.clientHeight; + scroller.scrollTop = maxScroll * 0.6; - // Moves the scroller to the end point (240px). - const maxScroll = scroller.scrollHeight - scroller.clientHeight; - scroller.scrollTop = maxScroll * 0.6; - - // Makes sure that the animation runs on compositor with current scroll offset - waitForAnimationFrames(2).then(_ => { - // Adds 80px to scroll height which pushes scroll progress back to 50%. - const spacer = document.getElementById('spacer'); - spacer.classList.remove('invisible'); - // Makes sure that the change is propagated to the compositor. + // Makes sure that the animation runs on compositor with current scroll offset waitForAnimationFrames(2).then(_ => { - takeScreenshot(); + // Adds 80px to scroll height which pushes scroll progress back to 50%. + const spacer = document.getElementById('spacer'); + spacer.classList.remove('invisible'); + // Makes sure that the change is propagated to the compositor. + waitForAnimationFrames(2).then(_ => { + takeScreenshot(); + }); }); }); </script>
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/scroll-timelines/multiple-scroll-offsets.html b/third_party/blink/web_tests/external/wpt/scroll-animations/scroll-timelines/multiple-scroll-offsets.html deleted file mode 100644 index 4a39548..0000000 --- a/third_party/blink/web_tests/external/wpt/scroll-animations/scroll-timelines/multiple-scroll-offsets.html +++ /dev/null
@@ -1,126 +0,0 @@ -<!DOCTYPE html> -<meta charset="utf-8"> -<title>ScrollTimeline current time algorithm</title> -<link rel="help" href="https://wicg.github.io/scroll-animations/#current-time-algorithm"> -<script src="/resources/testharness.js"></script> -<script src="/resources/testharnessreport.js"></script> -<script src="/web-animations/testcommon.js"></script> -<script src="./testcommon.js"></script> - -<body></body> - -<script> -'use strict'; - -promise_test(async t => { - const scroller = setupScrollTimelineTest(); - const scrollerSize = scroller.scrollHeight - scroller.clientHeight; - - const scrollTimeline = new ScrollTimeline({ - source: scroller, - orientation: 'block', - scrollOffsets: [CSS.px(10), CSS.px(20), CSS.px(40), CSS.px(70), CSS.px(90)], - }); - - var offset = 0; - var w = 1 / 4; // offset weight - var p = 0; // progress within the offset - - scroller.scrollTop = 10; - assert_percents_equal(scrollTimeline.currentTime, (offset + p) * w * 100, - "current time calculation when scroll = " + scroller.scrollTop); - - p = (12 - 10) / (20 - 10); - scroller.scrollTop = 12; - await waitForNextFrame(); - assert_percents_equal(scrollTimeline.currentTime, (offset + p) * w * 100, - "current time calculation when scroll = " + scroller.scrollTop); - - offset = 1; - p = 0; - scroller.scrollTop = 20; - await waitForNextFrame(); - assert_percents_equal(scrollTimeline.currentTime, (offset + p) * w * 100, - "current time calculation when scroll = " + scroller.scrollTop); - - p = (35 - 20) / (40 - 20); - scroller.scrollTop = 35; - await waitForNextFrame(); - assert_percents_equal(scrollTimeline.currentTime, (offset + p) * w * 100, - "current time calculation when scroll = " + scroller.scrollTop); - - offset = 2; - p = 0; - scroller.scrollTop = 40; - await waitForNextFrame(); - assert_percents_equal(scrollTimeline.currentTime, (offset + p) * w * 100, - "current time calculation when scroll = " + scroller.scrollTop); - - p = (60 - 40) / (70 - 40); - scroller.scrollTop = 60; - await waitForNextFrame(); - assert_percents_equal(scrollTimeline.currentTime, (offset + p) * w * 100, - "current time calculation when scroll = " + scroller.scrollTop); - - offset = 3; - p = 0; - scroller.scrollTop = 70; - await waitForNextFrame(); - assert_percents_equal(scrollTimeline.currentTime, (offset + p) * w * 100, - "current time calculation when scroll = " + scroller.scrollTop); - - p = (80 - 70) / (90 - 70); - scroller.scrollTop = 80; - await waitForNextFrame(); - assert_percents_equal(scrollTimeline.currentTime, (offset + p) * w * 100, - "current time calculation when scroll = " + scroller.scrollTop); - - scroller.scrollTop = 90; - await waitForNextFrame(); - assert_percents_equal(scrollTimeline.currentTime, 100, - "current time calculation when scroll = " + scroller.scrollTop); -}, 'currentTime calculations when multiple scroll offsets are specified'); - -promise_test(async t => { - const scroller = setupScrollTimelineTest(); - const scrollerSize = scroller.scrollHeight - scroller.clientHeight; - - var scrollTimeline = new ScrollTimeline({ - source: scroller, - orientation: 'block', - scrollOffsets: [CSS.px(300), CSS.px(200), CSS.px(10)], - }); - - scroller.scrollTop = 250; - await waitForNextFrame(); - assert_percents_equal(scrollTimeline.currentTime, 0, - "current time calculation when scroll = " + scroller.scrollTop); - - scroller.scrollTop = 400; - await waitForNextFrame(); - assert_percents_equal(scrollTimeline.currentTime, 100, - "current time calculation when scroll = " + scroller.scrollTop); - - scrollTimeline = new ScrollTimeline({ - source: scroller, - orientation: 'block', - scrollOffsets: ['auto', CSS.px(400), CSS.px(200)], - }); - - scroller.scrollTop = 100; - await waitForNextFrame(); - assert_percents_equal(scrollTimeline.currentTime, 12.5, - "current time calculation when scroll = " + scroller.scrollTop); - - scrollTimeline = new ScrollTimeline({ - source: scroller, - orientation: 'block', - scrollOffsets: [CSS.px(200), CSS.px(0), CSS.px(400)], - }); - - scroller.scrollTop = 200; - await waitForNextFrame(); - assert_percents_equal(scrollTimeline.currentTime, 75, - "current time calculation when scroll = " + scroller.scrollTop); -}, 'currentTime calculations when overlapping scroll offsets are specified'); -</script>
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/scroll-timelines/scroll-animation-effect-fill-modes.tentative.html b/third_party/blink/web_tests/external/wpt/scroll-animations/scroll-timelines/scroll-animation-effect-fill-modes.tentative.html index e4e536a..b9cc154 100644 --- a/third_party/blink/web_tests/external/wpt/scroll-animations/scroll-timelines/scroll-animation-effect-fill-modes.tentative.html +++ b/third_party/blink/web_tests/external/wpt/scroll-animations/scroll-timelines/scroll-animation-effect-fill-modes.tentative.html
@@ -31,31 +31,12 @@ }, "Scroll based animation effect fill mode should return 'auto' for" + " getTiming() and should return 'none' for getComputedTiming().") - // Test cases are included where effect delay causes the effect iteration to - // overlap with the timeline start time and also the timeline end time. - // Timeline - // BEFORE +-----------------+ AFTER - // time: 0 timeline.duration - // 1) +-----------------+ - // 2) +------------+ - // 3) +---------------------+ - // 4) +---------------------+ - // 5) +-------------+ - // 6) +---------+ - // 7) +-----------------+ - // 8) +-----------------+ - // 9) +-------------------------+ - - // Note: effects are scaled to fill the timeline so that the start of the - // effect is after the start offset of the timeline, and that the end of the - // effect is at the beginning of the end offset of the timeline. - /* All interesting transitions: - before timeline start - at timeline start - in timeline range - at timeline end - after timeline end + before start delay + at start delay + within active phase + at effect end + after effect end test_case data structure: fill_mode: { @@ -64,33 +45,19 @@ */ const test_cases = { "none": { - 0.10: ["before timeline start", 1], - 0.25: ["at timeline start", 0.3], - 0.50: ["in timeline range", 0.5], - 0.75: ["at timeline end", 1], - 0.90: ["after timeline end", 1] + 0.10: ["before start delay", 1], + 0.25: ["at start delay", 0.3], + 0.50: ["at midpoint", 0.5], + 0.75: ["at effect end", 1], + 0.90: ["after effect end", 1] }, "backwards": { - 0.10: ["before timeline start", 0.3], - 0.25: ["at timeline start", 0.3], - 0.50: ["in timeline range", 0.5], - 0.75: ["at timeline end", 1], - 0.90: ["after timeline end", 1] + 0.10: ["before start delay", 0.3], + 0.25: ["at start delay", 0.3], + 0.50: ["at midpoint", 0.5], + 0.75: ["at effect end", 1], + 0.90: ["after effect end", 1] }, - /* - There is an asymmetry between these 2 mirrored scenarios: - 1. fillmode: forwards + "at timeline end" - 2. fillmode: backwards + "at timeline start" - - In scenario 1, effect is not applied - In scenario 2, effect is applied - - The difference is accounted for by the equality at the end versus strict - inequality at the start when computing the timeline phase. - - This is currently in line with spec expectations but is an issue that - should be addressed. - */ "forwards": { 0.10: ["before timeline start", 1], 0.25: ["at timeline start", 0.3], @@ -105,8 +72,6 @@ 0.75: ["at timeline end", 0.7], 0.90: ["after timeline end", 0.7] }, - // "auto" behaves differently for different effect delay values. These - // cases are handled in scroll-animation-effect-phases.tentative.html "auto": { 0.10: ["before timeline start", 1], 0.25: ["at timeline start", 0.3], @@ -138,17 +103,18 @@ return async t => { const target = createDiv(t); - const timeline = - createScrollTimelineWithOffsets(t, CSS.percent(25), CSS.percent(75)); - const effect = new KeyframeEffect( - target, - { - opacity: [0.3, 0.7] - }, - { - fill: fill_mode - } - ); + const timeline = createScrollTimeline(t); + const effect = + new KeyframeEffect(target, + { opacity: [0.3, 0.7] }, + { + fill: fill_mode, + /* Animation times normalized to fill scroll + range */ + duration: 2000, + delay: 1000, + endDelay: 1000 + }); const animation = new Animation(effect, timeline); const scroller = timeline.source; const maxScroll = scroller.scrollHeight - scroller.clientHeight; @@ -163,10 +129,9 @@ // new current time. await waitForNextFrame(); - assert_equals( - parseFloat(window.getComputedStyle(target).opacity), - expected, - "animation effect applied property value"); + assert_equals(parseFloat(window.getComputedStyle(target).opacity), + expected, + "animation effect applied property value"); } } </script>
diff --git a/third_party/blink/web_tests/external/wpt/web-animations/responsive/resources/block.html b/third_party/blink/web_tests/external/wpt/web-animations/responsive/resources/block.html new file mode 100644 index 0000000..82840559 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/web-animations/responsive/resources/block.html
@@ -0,0 +1,10 @@ +<!DOCTYPE html> +<style> + .testBlock { + width: 100px; + height: 100px; + background: #00cc66; + border-top: 50px solid #ffff00; + } +</style> +<div class="testBlock" id="testBlock"></div> \ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/web-animations/responsive/toggle-animated-iframe-visibility-ref.html b/third_party/blink/web_tests/external/wpt/web-animations/responsive/toggle-animated-iframe-visibility-ref.html new file mode 100644 index 0000000..dab5bed --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/web-animations/responsive/toggle-animated-iframe-visibility-ref.html
@@ -0,0 +1,10 @@ +<!DOCTYPE html> +<style> + .rotated { + transform: rotate(90deg); + } +</style> +<div id="container"> + <iframe class="rotated" src="resources/block.html"> + </iframe> +</div> \ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/web-animations/responsive/toggle-animated-iframe-visibility.html b/third_party/blink/web_tests/external/wpt/web-animations/responsive/toggle-animated-iframe-visibility.html new file mode 100644 index 0000000..f50ffaad --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/web-animations/responsive/toggle-animated-iframe-visibility.html
@@ -0,0 +1,55 @@ +<!DOCTYPE html> +<html class="reftest-wait"> +<meta name="assert" content="This should resume the animation after unhiding the iframe."> +<title>CSS Test (Animations): Unhiding iframe visibility should restart animation. </title> +<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=616270"> +<link rel="match" href="toggle-animated-iframe-visibility-ref.html"> +<script src="/common/reftest-wait.js"></script> + +<div id="container"></div> + +<div id="log"></div> + +<script> + var container; + var block; + var logDiv; + + function verifyVisibility(expected_visibility, message) { + if (getComputedStyle(block).visibility !== expected_visibility) + logDiv.innerHTML = `FAIL: ${message}`; + } + + async function runTest() { + var animation = block.animate( + { transform: [ 'rotate(0deg)', 'rotate(180deg)' ] }, + { + duration: 10000000, + delay: -5000000, + easing: 'cubic-bezier(0, 1, 1, 0)' + }); + + await animation.ready; + + container.style.visibility = 'hidden'; + requestAnimationFrame(() => { + verifyVisibility('hidden', 'style.visibility should be hidden'); + container.style.visibility = 'visible'; + + requestAnimationFrame(() => { + verifyVisibility('visible', 'style.visiblity should be visible'); + takeScreenshot(); + }); + }); + } + + window.onload = function () { + logDiv = document.getElementById('log'); + container = document.getElementById('container'); + block = document.createElement('iframe'); + + container.appendChild(block); + block.onload = runTest; + block.src = 'resources/block.html'; + }; +</script>
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 4d01ed2..8c1e5a4 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
@@ -8386,7 +8386,6 @@ interface ScrollTimeline : AnimationTimeline attribute @@toStringTag getter orientation - getter scrollOffsets getter source method constructor interface SecurityPolicyViolationEvent : Event
diff --git a/third_party/blink/web_tests/platform/mac-mac10.13/compositing/overflow/nested-render-surfaces-with-rotation-expected.png b/third_party/blink/web_tests/platform/mac-mac10.13/compositing/overflow/nested-render-surfaces-with-rotation-expected.png index dc9db7c..054cbe9 100644 --- a/third_party/blink/web_tests/platform/mac-mac10.13/compositing/overflow/nested-render-surfaces-with-rotation-expected.png +++ b/third_party/blink/web_tests/platform/mac-mac10.13/compositing/overflow/nested-render-surfaces-with-rotation-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.13/media/video-zoom-controls-expected.png b/third_party/blink/web_tests/platform/mac-mac10.13/media/video-zoom-controls-expected.png deleted file mode 100644 index 31d2cc6..0000000 --- a/third_party/blink/web_tests/platform/mac-mac10.13/media/video-zoom-controls-expected.png +++ /dev/null Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.13/virtual/backface-visibility-interop/compositing/overflow/nested-render-surfaces-with-rotation-expected.png b/third_party/blink/web_tests/platform/mac-mac10.13/virtual/backface-visibility-interop/compositing/overflow/nested-render-surfaces-with-rotation-expected.png index dc9db7c..054cbe9 100644 --- a/third_party/blink/web_tests/platform/mac-mac10.13/virtual/backface-visibility-interop/compositing/overflow/nested-render-surfaces-with-rotation-expected.png +++ b/third_party/blink/web_tests/platform/mac-mac10.13/virtual/backface-visibility-interop/compositing/overflow/nested-render-surfaces-with-rotation-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.13/virtual/oopr-canvas2d/fast/canvas/image-object-in-canvas-expected.png b/third_party/blink/web_tests/platform/mac-mac10.13/virtual/oopr-canvas2d/fast/canvas/image-object-in-canvas-expected.png deleted file mode 100644 index a9b1dae..0000000 --- a/third_party/blink/web_tests/platform/mac-mac10.13/virtual/oopr-canvas2d/fast/canvas/image-object-in-canvas-expected.png +++ /dev/null Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.13/virtual/oopr-canvas2d/fast/canvas/quadraticCurveTo-expected.png b/third_party/blink/web_tests/platform/mac-mac10.13/virtual/oopr-canvas2d/fast/canvas/quadraticCurveTo-expected.png deleted file mode 100644 index 6f4c626..0000000 --- a/third_party/blink/web_tests/platform/mac-mac10.13/virtual/oopr-canvas2d/fast/canvas/quadraticCurveTo-expected.png +++ /dev/null Binary files differ
diff --git a/third_party/blink/web_tests/wpt_internal/document-transition/content-with-overflow-zoomed-ref.html b/third_party/blink/web_tests/wpt_internal/document-transition/content-with-overflow-zoomed-ref.html new file mode 100644 index 0000000..cbeaa31 --- /dev/null +++ b/third_party/blink/web_tests/wpt_internal/document-transition/content-with-overflow-zoomed-ref.html
@@ -0,0 +1,33 @@ +<!DOCTYPE html> +<title>Shared transitions: shared element with overflow (ref)</title> +<link rel="help" href="https://github.com/WICG/shared-element-transitions"> +<link rel="author" href="mailto:khushalsagar@chromium.org"> +<style> +.target { + width: 80px; + height: 80px; + contain: paint; + background: blue; + overflow-clip-margin: 50px; + page-transition-tag: target; + zoom: 1.5; + border: 2px solid black; +} +.child { + width: 200px; + height: 200px; + position: relative; + top: 50px; + left: 50px; + background: green; + zoom: 1.2; +} +body { background: lightpink; } +</style> + +<div class=ancestor> + <div class=target> + <div class=child> + </div> + </div> +</div>
diff --git a/third_party/blink/web_tests/wpt_internal/document-transition/new-content-with-overflow-zoomed.html b/third_party/blink/web_tests/wpt_internal/document-transition/new-content-with-overflow-zoomed.html new file mode 100644 index 0000000..98de13b --- /dev/null +++ b/third_party/blink/web_tests/wpt_internal/document-transition/new-content-with-overflow-zoomed.html
@@ -0,0 +1,54 @@ +<!DOCTYPE html> +<html class=reftest-wait> +<title>Shared transitions: shared element with overflow</title> +<link rel="help" href="https://github.com/WICG/shared-element-transitions"> +<link rel="author" href="mailto:khushalsagar@chromium.org"> +<link rel="match" href="content-with-overflow-zoomed-ref.html"> +<script src="/common/reftest-wait.js"></script> +<style> +.target { + width: 80px; + height: 80px; + contain: paint; + background: blue; + overflow-clip-margin: 50px; + page-transition-tag: target; + zoom: 1.5; +} +.child { + width: 200px; + height: 200px; + position: relative; + top: 50px; + left: 50px; + background: green; + zoom: 1.2; +} + +html::page-transition-container(target) { animation-duration: 300s; } +html::page-transition-outgoing-image(target) { animation: unset; opacity: 0; } +html::page-transition-incoming-image(target) { + animation: unset; + opacity: 1; + border: 3px solid black; +} + +html::page-transition-container(root) { animation: unset; opacity: 0; } +html::page-transition { background: lightpink; } +</style> + +<div class=ancestor> + <div class=target> + <div class=child> + </div> + </div> +</div> + +<script> +async function runTest() { + document.createDocumentTransition().start(() => + requestAnimationFrame(() => requestAnimationFrame(takeScreenshot))); +} +onload = () => requestAnimationFrame(() => requestAnimationFrame(runTest)); +</script> +
diff --git a/third_party/blink/web_tests/wpt_internal/document-transition/old-content-with-overflow-zoomed.html b/third_party/blink/web_tests/wpt_internal/document-transition/old-content-with-overflow-zoomed.html new file mode 100644 index 0000000..b95aa6d --- /dev/null +++ b/third_party/blink/web_tests/wpt_internal/document-transition/old-content-with-overflow-zoomed.html
@@ -0,0 +1,54 @@ +<!DOCTYPE html> +<html class=reftest-wait> +<title>Shared transitions: shared element with overflow</title> +<link rel="help" href="https://github.com/WICG/shared-element-transitions"> +<link rel="author" href="mailto:khushalsagar@chromium.org"> +<link rel="match" href="content-with-overflow-zoomed-ref.html"> +<script src="/common/reftest-wait.js"></script> +<style> +.target { + width: 80px; + height: 80px; + contain: paint; + background: blue; + overflow-clip-margin: 50px; + page-transition-tag: target; + zoom: 1.5; +} +.child { + width: 200px; + height: 200px; + position: relative; + top: 50px; + left: 50px; + background: green; + zoom: 1.2; +} + +html::page-transition-container(target) { animation-duration: 300s; } +html::page-transition-incoming-image(target) { animation: unset; opacity: 0; } +html::page-transition-outgoing-image(target) { + animation: unset; + opacity: 1; + border: 3px solid black; +} + +html::page-transition-container(root) { animation: unset; opacity: 0; } +html::page-transition { background: lightpink; } +</style> + +<div class=ancestor> + <div class=target> + <div class=child> + </div> + </div> +</div> + +<script> +async function runTest() { + document.createDocumentTransition().start(() => + requestAnimationFrame(() => requestAnimationFrame(takeScreenshot))); +} +onload = () => requestAnimationFrame(() => requestAnimationFrame(runTest)); +</script> +
diff --git a/third_party/crashpad/README.chromium b/third_party/crashpad/README.chromium index d0c37db..cd435e8 100644 --- a/third_party/crashpad/README.chromium +++ b/third_party/crashpad/README.chromium
@@ -2,7 +2,7 @@ Short Name: crashpad URL: https://crashpad.chromium.org/ Version: unknown -Revision: 816c5572b8c6d7aac8fb85db770496bc1f1da439 +Revision: 80f383327eb5c55bc11d5d1d4917bd00a860871b License: Apache 2.0 License File: crashpad/LICENSE Security Critical: yes
diff --git a/third_party/crashpad/crashpad/AUTHORS b/third_party/crashpad/crashpad/AUTHORS index 8dcac32..02103924 100644 --- a/third_party/crashpad/crashpad/AUTHORS +++ b/third_party/crashpad/crashpad/AUTHORS
@@ -12,3 +12,4 @@ Vewd Software AS LG Electronics, Inc. MIPS Technologies, Inc. +Darshan Sen <raisinten@gmail.com>
diff --git a/third_party/crashpad/crashpad/DEPS b/third_party/crashpad/crashpad/DEPS index 2299223..4a20125 100644 --- a/third_party/crashpad/crashpad/DEPS +++ b/third_party/crashpad/crashpad/DEPS
@@ -14,6 +14,7 @@ vars = { 'chromium_git': 'https://chromium.googlesource.com', + 'gn_version': 'git_revision:2ecd43a10266bd091c98e6dcde507c64f6a0dad3', 'pull_linux_clang': False, 'pull_win_toolchain': False, # Controls whether crashpad/build/ios/setup-ios-gn.py is run as part of @@ -25,7 +26,11 @@ deps = { 'buildtools': Var('chromium_git') + '/chromium/src/buildtools.git@' + - '9e121212d42be62a7cce38072f925f8398d11e49', + '8b16338d17cd71b04a6ba28da7322ab6739892c2', + 'buildtools/clang_format/script': + Var('chromium_git') + + '/external/github.com/llvm/llvm-project/clang/tools/clang-format.git@' + + 'c912837e0d82b5ca4b6e790b573b3956d3744c1c', 'crashpad/third_party/edo/edo': { 'url': Var('chromium_git') + '/external/github.com/google/eDistantObject.git@' + '727e556705278598fce683522beedbb9946bfda0', @@ -47,7 +52,37 @@ Var('chromium_git') + '/chromium/src/third_party/zlib@' + '13dc246a58e4b72104d35f9b1809af95221ebda7', - # CIPD packages below. + # CIPD packages. + 'buildtools/linux64': { + 'packages': [ + { + 'package': 'gn/gn/linux-amd64', + 'version': Var('gn_version'), + } + ], + 'dep_type': 'cipd', + 'condition': 'host_os == "linux"', + }, + 'buildtools/mac': { + 'packages': [ + { + 'package': 'gn/gn/mac-${{arch}}', + 'version': Var('gn_version'), + } + ], + 'dep_type': 'cipd', + 'condition': 'host_os == "mac"', + }, + 'buildtools/win': { + 'packages': [ + { + 'package': 'gn/gn/windows-amd64', + 'version': Var('gn_version'), + } + ], + 'dep_type': 'cipd', + 'condition': 'host_os == "win"', + }, 'crashpad/third_party/linux/clang/linux-amd64': { 'packages': [ { @@ -125,7 +160,9 @@ '--no_auth', '--bucket=chromium-clang-format', '--sha1_file', - 'buildtools/mac/clang-format.sha1', + 'buildtools/mac/clang-format.{host_cpu}.sha1', + '--output', + 'buildtools/mac/clang-format', ], }, {
diff --git a/third_party/crashpad/crashpad/client/crash_report_database_generic.cc b/third_party/crashpad/crashpad/client/crash_report_database_generic.cc index c4e040109..742850a 100644 --- a/third_party/crashpad/crashpad/client/crash_report_database_generic.cc +++ b/third_party/crashpad/crashpad/client/crash_report_database_generic.cc
@@ -18,6 +18,7 @@ #include <sys/stat.h> #include <sys/types.h> +#include <mutex> #include <tuple> #include <utility> @@ -258,15 +259,15 @@ static bool WriteMetadata(const base::FilePath& path, const Report& report); Settings& SettingsInternal() { - if (!settings_init_) + std::call_once(settings_init_, [this]() { settings_.Initialize(base_dir_.Append(kSettings)); - settings_init_ = true; + }); return settings_; } base::FilePath base_dir_; Settings settings_; - bool settings_init_ = false; + std::once_flag settings_init_; InitializationStateDcheck initialized_; };
diff --git a/third_party/crashpad/crashpad/client/crash_report_database_mac.mm b/third_party/crashpad/crashpad/client/crash_report_database_mac.mm index 4c9e5e7..e33b932 100644 --- a/third_party/crashpad/crashpad/client/crash_report_database_mac.mm +++ b/third_party/crashpad/crashpad/client/crash_report_database_mac.mm
@@ -27,6 +27,7 @@ #include <array> #include <iterator> +#include <mutex> #include <tuple> #include "base/logging.h" @@ -263,15 +264,15 @@ void CleanOrphanedAttachments(); Settings& SettingsInternal() { - if (!settings_init_) + std::call_once(settings_init_, [this]() { settings_.Initialize(base_dir_.Append(kSettings)); - settings_init_ = true; + }); return settings_; } base::FilePath base_dir_; Settings settings_; - bool settings_init_; + std::once_flag settings_init_; bool xattr_new_names_; InitializationStateDcheck initialized_; }; @@ -280,7 +281,7 @@ : CrashReportDatabase(), base_dir_(path), settings_(), - settings_init_(false), + settings_init_(), xattr_new_names_(false), initialized_() {}
diff --git a/third_party/crashpad/crashpad/client/crash_report_database_win.cc b/third_party/crashpad/crashpad/client/crash_report_database_win.cc index e16aa10..e111d8e 100644 --- a/third_party/crashpad/crashpad/client/crash_report_database_win.cc +++ b/third_party/crashpad/crashpad/client/crash_report_database_win.cc
@@ -21,6 +21,7 @@ #include <time.h> #include <wchar.h> +#include <mutex> #include <tuple> #include <utility> @@ -663,15 +664,15 @@ std::unique_ptr<Metadata> AcquireMetadata(); Settings& SettingsInternal() { - if (!settings_init_) + std::call_once(settings_init_, [this]() { settings_.Initialize(base_dir_.Append(kSettings)); - settings_init_ = true; + }); return settings_; } base::FilePath base_dir_; Settings settings_; - bool settings_init_; + std::once_flag settings_init_; InitializationStateDcheck initialized_; }; @@ -679,7 +680,7 @@ : CrashReportDatabase(), base_dir_(path), settings_(), - settings_init_(false), + settings_init_(), initialized_() {} CrashReportDatabaseWin::~CrashReportDatabaseWin() {
diff --git a/third_party/crashpad/crashpad/client/crashpad_client_linux.cc b/third_party/crashpad/crashpad/client/crashpad_client_linux.cc index 295ec16..9cd452b 100644 --- a/third_party/crashpad/crashpad/client/crashpad_client_linux.cc +++ b/third_party/crashpad/crashpad/client/crashpad_client_linux.cc
@@ -45,9 +45,9 @@ #include "util/linux/socket.h" #include "util/misc/address_sanitizer.h" #include "util/misc/from_pointer_cast.h" -#include "util/posix/double_fork_and_exec.h" #include "util/posix/scoped_mmap.h" #include "util/posix/signals.h" +#include "util/posix/spawn_subprocess.h" namespace crashpad { @@ -459,7 +459,7 @@ argv.push_back(FormatArgumentInt("initial-client-fd", handler_sock.get())); argv.push_back("--shared-client-connection"); - if (!DoubleForkAndExec(argv, nullptr, handler_sock.get(), false, nullptr)) { + if (!SpawnSubprocess(argv, nullptr, handler_sock.get(), false, nullptr)) { return false; } @@ -614,7 +614,7 @@ int socket) { std::vector<std::string> argv = BuildAppProcessArgs( class_name, database, metrics_dir, url, annotations, arguments, socket); - return DoubleForkAndExec(argv, env, socket, false, nullptr); + return SpawnSubprocess(argv, env, socket, false, nullptr); } bool CrashpadClient::StartHandlerWithLinkerAtCrash( @@ -663,7 +663,7 @@ annotations, arguments, socket); - return DoubleForkAndExec(argv, env, socket, false, nullptr); + return SpawnSubprocess(argv, env, socket, false, nullptr); } #endif @@ -697,7 +697,7 @@ argv.push_back(FormatArgumentInt("initial-client-fd", socket)); - return DoubleForkAndExec(argv, nullptr, socket, true, nullptr); + return SpawnSubprocess(argv, nullptr, socket, true, nullptr); } // static
diff --git a/third_party/crashpad/crashpad/client/crashpad_client_mac.cc b/third_party/crashpad/crashpad/client/crashpad_client_mac.cc index 39e3567..ba43ed8 100644 --- a/third_party/crashpad/crashpad/client/crashpad_client_mac.cc +++ b/third_party/crashpad/crashpad/client/crashpad_client_mac.cc
@@ -36,7 +36,7 @@ #include "util/mach/notify_server.h" #include "util/misc/clock.h" #include "util/misc/implicit_cast.h" -#include "util/posix/double_fork_and_exec.h" +#include "util/posix/spawn_subprocess.h" namespace crashpad { @@ -343,7 +343,7 @@ // this parent process, which was probably using the exception server now // being restarted. The handler can’t monitor itself for its own crashes via // this interface. - if (!DoubleForkAndExec( + if (!SpawnSubprocess( argv, nullptr, server_write_fd.get(),
diff --git a/third_party/crashpad/crashpad/client/ios_handler/in_process_intermediate_dump_handler.cc b/third_party/crashpad/crashpad/client/ios_handler/in_process_intermediate_dump_handler.cc index 2869c21..e7e9003 100644 --- a/third_party/crashpad/crashpad/client/ios_handler/in_process_intermediate_dump_handler.cc +++ b/third_party/crashpad/crashpad/client/ios_handler/in_process_intermediate_dump_handler.cc
@@ -811,6 +811,22 @@ CRASHPAD_RAW_LOG_ERROR(kr, "thread_info::THREAD_BASIC_INFO"); } + thread_extended_info extended_info; + count = THREAD_EXTENDED_INFO_COUNT; + kr = thread_info(thread, + THREAD_EXTENDED_INFO, + reinterpret_cast<thread_info_t>(&extended_info), + &count); + if (kr == KERN_SUCCESS) { + WritePropertyBytes( + writer, + IntermediateDumpKey::kThreadName, + reinterpret_cast<const void*>(extended_info.pth_name), + strnlen(extended_info.pth_name, sizeof(extended_info.pth_name))); + } else { + CRASHPAD_RAW_LOG_ERROR(kr, "thread_info::THREAD_EXTENDED_INFO"); + } + thread_precedence_policy precedence; count = THREAD_PRECEDENCE_POLICY_COUNT; boolean_t get_default = FALSE;
diff --git a/third_party/crashpad/crashpad/client/ios_handler/in_process_intermediate_dump_handler_test.cc b/third_party/crashpad/crashpad/client/ios_handler/in_process_intermediate_dump_handler_test.cc index bccc930..f1d8a54 100644 --- a/third_party/crashpad/crashpad/client/ios_handler/in_process_intermediate_dump_handler_test.cc +++ b/third_party/crashpad/crashpad/client/ios_handler/in_process_intermediate_dump_handler_test.cc
@@ -26,6 +26,7 @@ #include "client/simple_string_dictionary.h" #include "gtest/gtest.h" #include "snapshot/ios/process_snapshot_ios_intermediate_dump.h" +#include "test/scoped_set_thread_name.h" #include "test/scoped_temp_dir.h" #include "test/test_paths.h" #include "util/file/filesystem.h" @@ -206,6 +207,8 @@ } TEST_F(InProcessIntermediateDumpHandlerTest, TestThreads) { + const ScopedSetThreadName scoped_set_thread_name("TestThreads"); + WriteReport(); internal::ProcessSnapshotIOSIntermediateDump process_snapshot; ASSERT_TRUE(process_snapshot.InitializeWithFilePath(path(), {})); @@ -221,6 +224,7 @@ &count), 0); EXPECT_EQ(threads[0]->ThreadID(), identifier_info.thread_id); + EXPECT_EQ(threads[0]->ThreadName(), "TestThreads"); } TEST_F(InProcessIntermediateDumpHandlerTest, TestProcess) {
diff --git a/third_party/crashpad/crashpad/handler/linux/cros_crash_report_exception_handler.cc b/third_party/crashpad/crashpad/handler/linux/cros_crash_report_exception_handler.cc index 9e58d94..4804eac 100644 --- a/third_party/crashpad/crashpad/handler/linux/cros_crash_report_exception_handler.cc +++ b/third_party/crashpad/crashpad/handler/linux/cros_crash_report_exception_handler.cc
@@ -29,7 +29,7 @@ #include "util/linux/ptrace_client.h" #include "util/misc/metrics.h" #include "util/misc/uuid.h" -#include "util/posix/double_fork_and_exec.h" +#include "util/posix/spawn_subprocess.h" namespace crashpad { @@ -266,12 +266,11 @@ argv.push_back("--always_allow_feedback"); } - if (!DoubleForkAndExec(argv, - nullptr /* envp */, - file_writer.fd() /* preserve_fd */, - false /* use_path */, - nullptr /* child_function */)) { - LOG(ERROR) << "DoubleForkAndExec failed"; + if (!SpawnSubprocess(argv, + nullptr /* envp */, + file_writer.fd() /* preserve_fd */, + false /* use_path */, + nullptr /* child_function */)) { Metrics::ExceptionCaptureResult( Metrics::CaptureResult::kFinishedWritingCrashReportFailed); return false;
diff --git a/third_party/crashpad/crashpad/minidump/minidump_file_writer.cc b/third_party/crashpad/crashpad/minidump/minidump_file_writer.cc index 43abca8..bc5bf02 100644 --- a/third_party/crashpad/crashpad/minidump/minidump_file_writer.cc +++ b/third_party/crashpad/crashpad/minidump/minidump_file_writer.cc
@@ -26,6 +26,7 @@ #include "minidump/minidump_module_writer.h" #include "minidump/minidump_system_info_writer.h" #include "minidump/minidump_thread_id_map.h" +#include "minidump/minidump_thread_name_list_writer.h" #include "minidump/minidump_thread_writer.h" #include "minidump/minidump_unloaded_module_writer.h" #include "minidump/minidump_user_extension_stream_data_source.h" @@ -34,6 +35,7 @@ #include "snapshot/exception_snapshot.h" #include "snapshot/module_snapshot.h" #include "snapshot/process_snapshot.h" +#include "snapshot/thread_snapshot.h" #include "util/file/file_writer.h" #include "util/numeric/safe_assignment.h" @@ -94,6 +96,21 @@ add_stream_result = AddStream(std::move(thread_list)); DCHECK(add_stream_result); + bool has_thread_name = false; + for (const ThreadSnapshot* thread_snapshot : process_snapshot->Threads()) { + if (!thread_snapshot->ThreadName().empty()) { + has_thread_name = true; + break; + } + } + if (has_thread_name) { + auto thread_name_list = std::make_unique<MinidumpThreadNameListWriter>(); + thread_name_list->InitializeFromSnapshot(process_snapshot->Threads(), + thread_id_map); + add_stream_result = AddStream(std::move(thread_name_list)); + DCHECK(add_stream_result); + } + const ExceptionSnapshot* exception_snapshot = process_snapshot->Exception(); if (exception_snapshot) { auto exception = std::make_unique<MinidumpExceptionWriter>();
diff --git a/third_party/crashpad/crashpad/minidump/minidump_thread_name_list_writer.cc b/third_party/crashpad/crashpad/minidump/minidump_thread_name_list_writer.cc index a955c27..6f49f0c 100644 --- a/third_party/crashpad/crashpad/minidump/minidump_thread_name_list_writer.cc +++ b/third_party/crashpad/crashpad/minidump/minidump_thread_name_list_writer.cc
@@ -17,21 +17,47 @@ #include <utility> #include "base/logging.h" +#include "minidump/minidump_thread_id_map.h" +#include "snapshot/thread_snapshot.h" #include "util/file/file_writer.h" #include "util/numeric/safe_assignment.h" namespace crashpad { MinidumpThreadNameWriter::MinidumpThreadNameWriter() - : MinidumpWritable(), thread_name_(), name_() {} + : MinidumpWritable(), rva_of_thread_name_(), thread_id_(), name_() {} MinidumpThreadNameWriter::~MinidumpThreadNameWriter() {} -const MINIDUMP_THREAD_NAME* MinidumpThreadNameWriter::MinidumpThreadName() - const { +void MinidumpThreadNameWriter::InitializeFromSnapshot( + const ThreadSnapshot* thread_snapshot, + const MinidumpThreadIDMap& thread_id_map) { + DCHECK_EQ(state(), kStateMutable); + + const auto it = thread_id_map.find(thread_snapshot->ThreadID()); + DCHECK(it != thread_id_map.end()); + SetThreadId(it->second); + SetThreadName(thread_snapshot->ThreadName()); +} + +RVA64 MinidumpThreadNameWriter::RvaOfThreadName() const { DCHECK_EQ(state(), kStateWritable); - return &thread_name_; + return rva_of_thread_name_; +} + +uint32_t MinidumpThreadNameWriter::ThreadId() const { + DCHECK_EQ(state(), kStateWritable); + + return thread_id_; +} + +bool MinidumpThreadNameWriter::Freeze() { + DCHECK_EQ(state(), kStateMutable); + + name_->RegisterRVA(&rva_of_thread_name_); + + return MinidumpWritable::Freeze(); } void MinidumpThreadNameWriter::SetThreadName(const std::string& name) { @@ -46,10 +72,9 @@ size_t MinidumpThreadNameWriter::SizeOfObject() { DCHECK_GE(state(), kStateFrozen); - // This object doesn’t directly write anything itself. Its - // MINIDUMP_THREAD_NAME is written by its parent as part of a - // MINIDUMP_THREAD_NAME_LIST, and its children are responsible for writing - // themselves. + // This object doesn’t directly write anything itself. Its parent writes the + // MINIDUMP_THREAD_NAME objects as part of a MINIDUMP_THREAD_NAME_LIST, and + // its children are responsible for writing themselves. return 0; } @@ -63,24 +88,6 @@ return children; } -bool MinidumpThreadNameWriter::WillWriteAtOffsetImpl(FileOffset offset) { - DCHECK_EQ(state(), kStateFrozen); - - // This cannot use RegisterRVA(&thread_name_.RvaOfThreadName), since - // &MINIDUMP_THREAD_NAME_LIST::RvaOfThreadName is not aligned on a pointer - // boundary, so it causes failures on 32-bit ARM. - // - // Instead, manually update the RVA64 to the current file offset since the - // child thread_name_ will write its contents at that offset. - decltype(thread_name_.RvaOfThreadName) local_rva_of_thread_name; - if (!AssignIfInRange(&local_rva_of_thread_name, offset)) { - LOG(ERROR) << "offset " << offset << " out of range"; - return false; - } - thread_name_.RvaOfThreadName = local_rva_of_thread_name; - return MinidumpWritable::WillWriteAtOffsetImpl(offset); -} - bool MinidumpThreadNameWriter::WriteObject(FileWriterInterface* file_writer) { DCHECK_EQ(state(), kStateWritable); @@ -96,6 +103,19 @@ MinidumpThreadNameListWriter::~MinidumpThreadNameListWriter() {} +void MinidumpThreadNameListWriter::InitializeFromSnapshot( + const std::vector<const ThreadSnapshot*>& thread_snapshots, + const MinidumpThreadIDMap& thread_id_map) { + DCHECK_EQ(state(), kStateMutable); + DCHECK(thread_names_.empty()); + + for (const ThreadSnapshot* thread_snapshot : thread_snapshots) { + auto thread = std::make_unique<MinidumpThreadNameWriter>(); + thread->InitializeFromSnapshot(thread_snapshot, thread_id_map); + AddThreadName(std::move(thread)); + } +} + void MinidumpThreadNameListWriter::AddThreadName( std::unique_ptr<MinidumpThreadNameWriter> thread_name) { DCHECK_EQ(state(), kStateMutable); @@ -150,10 +170,15 @@ std::vector<WritableIoVec> iovecs(1, iov); iovecs.reserve(thread_names_.size() + 1); + std::vector<MINIDUMP_THREAD_NAME> minidump_thread_names; + minidump_thread_names.reserve(thread_names_.size()); for (const auto& thread_name : thread_names_) { - iov.iov_base = thread_name->MinidumpThreadName(); - iov.iov_len = sizeof(MINIDUMP_THREAD_NAME); - iovecs.emplace_back(iov); + auto& minidump_thread_name = minidump_thread_names.emplace_back(); + minidump_thread_name.ThreadId = thread_name->ThreadId(); + minidump_thread_name.RvaOfThreadName = thread_name->RvaOfThreadName(); + iov.iov_base = &minidump_thread_name; + iov.iov_len = sizeof(minidump_thread_name); + iovecs.push_back(iov); } return file_writer->WriteIoVec(&iovecs);
diff --git a/third_party/crashpad/crashpad/minidump/minidump_thread_name_list_writer.h b/third_party/crashpad/crashpad/minidump/minidump_thread_name_list_writer.h index 5afab981..f573111c 100644 --- a/third_party/crashpad/crashpad/minidump/minidump_thread_name_list_writer.h +++ b/third_party/crashpad/crashpad/minidump/minidump_thread_name_list_writer.h
@@ -25,6 +25,7 @@ #include "minidump/minidump_stream_writer.h" #include "minidump/minidump_string_writer.h" +#include "minidump/minidump_thread_id_map.h" #include "minidump/minidump_writable.h" namespace crashpad { @@ -45,28 +46,57 @@ ~MinidumpThreadNameWriter() override; - //! \brief Returns a MINIDUMP_THREAD_NAME referencing this object’s data. + //! \brief Initializes the MINIDUMP_THREAD_NAME based on \a thread_snapshot. //! - //! This method is expected to be called by a MinidumpThreadNameListWriter in - //! order to obtain a MINIDUMP_THREAD_NAME to include in its list. + //! \param[in] thread_snapshot The thread snapshot to use as source data. + //! \param[in] thread_id_map A MinidumpThreadIDMap to be consulted to + //! determine the 32-bit minidump thread ID to use for \a thread_snapshot. + //! + //! \note Valid in #kStateMutable. + void InitializeFromSnapshot(const ThreadSnapshot* thread_snapshot, + const MinidumpThreadIDMap& thread_id_map); + + //! \brief Sets the ThreadId for MINIDUMP_THREAD_NAME::ThreadId. + void SetThreadId(uint32_t thread_id) { thread_id_ = thread_id; } + + //! \brief Gets the ThreadId for MINIDUMP_THREAD_NAME::ThreadId. //! //! \note Valid in #kStateWritable. - const MINIDUMP_THREAD_NAME* MinidumpThreadName() const; - - //! \brief Sets MINIDUMP_THREAD_NAME::ThreadId. - void SetThreadId(uint32_t thread_id) { thread_name_.ThreadId = thread_id; } + uint32_t ThreadId() const; //! \brief Sets MINIDUMP_THREAD_NAME::RvaOfThreadName. void SetThreadName(const std::string& thread_name); + //! \brief Returns an RVA64 which has been updated with the relative address + //! of the thread name. + //! + //! This method is expected to be called by a MinidumpThreadNameListWriter in + //! order to obtain the RVA64 of the thread name. + //! + //! \note Valid in #kStateWritable. + RVA64 RvaOfThreadName() const; + private: // MinidumpWritable: + bool Freeze() override; size_t SizeOfObject() override; std::vector<MinidumpWritable*> Children() override; - bool WillWriteAtOffsetImpl(FileOffset offset) override; bool WriteObject(FileWriterInterface* file_writer) override; - MINIDUMP_THREAD_NAME thread_name_; + // This exists as a separate field so MinidumpWritable::RegisterRVA() can be + // used on a guaranteed-aligned pointer (MINIDUMP_THREAD_NAME::RvaOfThreadName + // is not 64-bit aligned, causing issues on ARM). + RVA64 rva_of_thread_name_; + + // Although this class manages the data for a MINIDUMP_THREAD_NAME, it does + // not directly hold a MINIDUMP_THREAD_NAME, as that struct contains a + // non-aligned RVA64 field which prevents it use with + // MinidumpWritable::RegisterRVA(). + // + // Instead, this class individually holds the fields of the + // MINIDUMP_THREAD_NAME which are fetched by MinidumpThreadNameListWriter. + uint32_t thread_id_; + std::unique_ptr<internal::MinidumpUTF16StringWriter> name_; }; @@ -83,6 +113,18 @@ ~MinidumpThreadNameListWriter() override; + //! \brief Adds an initialized MINIDUMP_THREAD_NAME for each thread in \a + //! thread_snapshots to the MINIDUMP_THREAD_NAME_LIST. + //! + //! \param[in] thread_snapshots The thread snapshots to use as source data. + //! \param[in] thread_id_map A MinidumpThreadIDMap previously built by + //! MinidumpThreadListWriter::InitializeFromSnapshot(). + //! + //! \note Valid in #kStateMutable. + void InitializeFromSnapshot( + const std::vector<const ThreadSnapshot*>& thread_snapshots, + const MinidumpThreadIDMap& thread_id_map); + //! \brief Adds a MinidumpThreadNameWriter to the MINIDUMP_THREAD_LIST. //! //! This object takes ownership of \a thread_name and becomes its parent in
diff --git a/third_party/crashpad/crashpad/minidump/minidump_thread_name_list_writer_test.cc b/third_party/crashpad/crashpad/minidump/minidump_thread_name_list_writer_test.cc index b61b1bb..265c12e 100644 --- a/third_party/crashpad/crashpad/minidump/minidump_thread_name_list_writer_test.cc +++ b/third_party/crashpad/crashpad/minidump/minidump_thread_name_list_writer_test.cc
@@ -24,6 +24,7 @@ #include "base/strings/utf_string_conversions.h" #include "gtest/gtest.h" #include "minidump/minidump_file_writer.h" +#include "minidump/minidump_system_info_writer.h" #include "minidump/test/minidump_file_writer_test_util.h" #include "minidump/test/minidump_string_writer_test_util.h" #include "minidump/test/minidump_writable_test_util.h" @@ -137,6 +138,64 @@ kThreadName)); } +TEST(MinidumpThreadNameListWriter, OneThreadWithLeadingPadding) { + MinidumpFileWriter minidump_file_writer; + + // Add a stream before the MINIDUMP_THREAD_NAME_LIST to ensure the thread name + // MINIDUMP_STRING requires leading padding to align to a 4-byte boundary. + auto system_info_writer = std::make_unique<MinidumpSystemInfoWriter>(); + system_info_writer->SetCSDVersion(""); + ASSERT_TRUE(minidump_file_writer.AddStream(std::move(system_info_writer))); + + auto thread_list_writer = std::make_unique<MinidumpThreadNameListWriter>(); + + constexpr uint32_t kThreadID = 0x11111111; + const std::string kThreadName = "ariadne"; + + auto thread_name_list_writer = + std::make_unique<MinidumpThreadNameListWriter>(); + auto thread_name_writer = std::make_unique<MinidumpThreadNameWriter>(); + thread_name_writer->SetThreadId(kThreadID); + thread_name_writer->SetThreadName(kThreadName); + thread_name_list_writer->AddThreadName(std::move(thread_name_writer)); + + ASSERT_TRUE( + minidump_file_writer.AddStream(std::move(thread_name_list_writer))); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + ASSERT_GT(string_file.string().size(), + sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) + + sizeof(MINIDUMP_THREAD_NAME_LIST) + + 1 * sizeof(MINIDUMP_THREAD_NAME)); + + const uint32_t kExpectedStreams = 2; + const MINIDUMP_DIRECTORY* directory; + const MINIDUMP_HEADER* header = + MinidumpHeaderAtStart(string_file.string(), &directory); + ASSERT_NO_FATAL_FAILURE(VerifyMinidumpHeader(header, kExpectedStreams, 0)); + ASSERT_TRUE(directory); + + ASSERT_EQ(directory[0].StreamType, kMinidumpStreamTypeSystemInfo); + ASSERT_EQ(directory[1].StreamType, kMinidumpStreamTypeThreadNameList); + + const MINIDUMP_THREAD_NAME_LIST* thread_name_list = + MinidumpWritableAtLocationDescriptor<MINIDUMP_THREAD_NAME_LIST>( + string_file.string(), directory[1].Location); + ASSERT_TRUE(thread_name_list); + + EXPECT_EQ(thread_name_list->NumberOfThreadNames, 1u); + + MINIDUMP_THREAD_NAME expected = {}; + expected.ThreadId = kThreadID; + + ASSERT_NO_FATAL_FAILURE(ExpectThreadName(&expected, + &thread_name_list->ThreadNames[0], + string_file.string(), + kThreadName)); +} + TEST(MinidumpThreadNameListWriter, TwoThreads_DifferentNames) { MinidumpFileWriter minidump_file_writer; auto thread_list_writer = std::make_unique<MinidumpThreadNameListWriter>();
diff --git a/third_party/crashpad/crashpad/snapshot/fuchsia/process_reader_fuchsia_test.cc b/third_party/crashpad/crashpad/snapshot/fuchsia/process_reader_fuchsia_test.cc index 362be5b..b8e71afb 100644 --- a/third_party/crashpad/crashpad/snapshot/fuchsia/process_reader_fuchsia_test.cc +++ b/third_party/crashpad/crashpad/snapshot/fuchsia/process_reader_fuchsia_test.cc
@@ -22,8 +22,10 @@ #include <iterator> +#include "base/strings/stringprintf.h" #include "gtest/gtest.h" #include "test/multiprocess_exec.h" +#include "test/scoped_set_thread_name.h" #include "test/test_paths.h" #include "util/fuchsia/scoped_task_suspend.h" @@ -32,6 +34,8 @@ namespace { TEST(ProcessReaderFuchsia, SelfBasic) { + const ScopedSetThreadName scoped_set_thread_name("SelfBasic"); + ProcessReaderFuchsia process_reader; ASSERT_TRUE(process_reader.Initialize(*zx::process::self())); @@ -75,7 +79,7 @@ ZX_OK); EXPECT_EQ(threads[0].id, info.koid); EXPECT_EQ(threads[0].state, ZX_THREAD_STATE_RUNNING); - EXPECT_EQ(threads[0].name, "initial-thread"); + EXPECT_EQ(threads[0].name, "SelfBasic"); } constexpr char kTestMemory[] = "Read me from another process"; @@ -118,27 +122,44 @@ test.Run(); } +struct ThreadData { + zx_handle_t port; + std::string name; +}; + void* SignalAndSleep(void* arg) { + const ThreadData* thread_data = reinterpret_cast<const ThreadData*>(arg); + const ScopedSetThreadName scoped_set_thread_name(thread_data->name); zx_port_packet_t packet = {}; packet.type = ZX_PKT_TYPE_USER; - zx_port_queue(*reinterpret_cast<zx_handle_t*>(arg), &packet); + zx_port_queue(thread_data->port, &packet); zx_nanosleep(ZX_TIME_INFINITE); return nullptr; } CRASHPAD_CHILD_TEST_MAIN(ProcessReaderChildThreadsTestMain) { + const ScopedSetThreadName scoped_set_thread_name( + "ProcessReaderChildThreadsTest-Main"); + // Create 5 threads with stack sizes of 4096, 8192, ... zx_handle_t port; zx_status_t status = zx_port_create(0, &port); EXPECT_EQ(status, ZX_OK); constexpr size_t kNumThreads = 5; + struct ThreadData thread_data[kNumThreads] = {{0, 0}}; + for (size_t i = 0; i < kNumThreads; ++i) { + thread_data[i] = { + .port = port, + .name = base::StringPrintf("ProcessReaderChildThreadsTest-%zu", i + 1), + }; pthread_attr_t attr; EXPECT_EQ(pthread_attr_init(&attr), 0); EXPECT_EQ(pthread_attr_setstacksize(&attr, (i + 1) * 4096), 0); pthread_t thread; - EXPECT_EQ(pthread_create(&thread, &attr, &SignalAndSleep, &port), 0); + EXPECT_EQ(pthread_create(&thread, &attr, &SignalAndSleep, &thread_data[i]), + 0); } // Wait until all threads are ready. @@ -179,10 +200,14 @@ const auto& threads = process_reader.Threads(); EXPECT_EQ(threads.size(), 6u); + EXPECT_EQ(threads[0].name, "ProcessReaderChildThreadsTest-main"); + for (size_t i = 1; i < 6; ++i) { ASSERT_GT(threads[i].stack_regions.size(), 0u); EXPECT_GT(threads[i].stack_regions[0].size(), 0u); EXPECT_LE(threads[i].stack_regions[0].size(), i * 4096u); + EXPECT_EQ(threads[i].name, + base::StringPrintf("ProcessReaderChildThreadsTest-%zu", i)); } } };
diff --git a/third_party/crashpad/crashpad/snapshot/fuchsia/thread_snapshot_fuchsia.cc b/third_party/crashpad/crashpad/snapshot/fuchsia/thread_snapshot_fuchsia.cc index 369203a..b12ee861 100644 --- a/third_party/crashpad/crashpad/snapshot/fuchsia/thread_snapshot_fuchsia.cc +++ b/third_party/crashpad/crashpad/snapshot/fuchsia/thread_snapshot_fuchsia.cc
@@ -25,6 +25,7 @@ context_arch_(), context_(), stack_(), + thread_name_(), thread_id_(ZX_KOID_INVALID), thread_specific_data_address_(0), initialized_() {} @@ -60,6 +61,7 @@ // TODO(scottmg): Handle split stack by adding other parts to ExtraMemory(). } + thread_name_ = thread.name; thread_id_ = thread.id; INITIALIZATION_STATE_SET_VALID(initialized_); @@ -81,6 +83,11 @@ return thread_id_; } +std::string ThreadSnapshotFuchsia::ThreadName() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return thread_name_; +} + int ThreadSnapshotFuchsia::SuspendCount() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); // There is not (currently) a suspend count for threads on Fuchsia.
diff --git a/third_party/crashpad/crashpad/snapshot/fuchsia/thread_snapshot_fuchsia.h b/third_party/crashpad/crashpad/snapshot/fuchsia/thread_snapshot_fuchsia.h index b91a514d..5c804fb1 100644 --- a/third_party/crashpad/crashpad/snapshot/fuchsia/thread_snapshot_fuchsia.h +++ b/third_party/crashpad/crashpad/snapshot/fuchsia/thread_snapshot_fuchsia.h
@@ -18,6 +18,8 @@ #include <stdint.h> #include <zircon/types.h> +#include <string> + #include "build/build_config.h" #include "snapshot/cpu_context.h" #include "snapshot/fuchsia/process_reader_fuchsia.h" @@ -56,6 +58,7 @@ const CPUContext* Context() const override; const MemorySnapshot* Stack() const override; uint64_t ThreadID() const override; + std::string ThreadName() const override; int SuspendCount() const override; int Priority() const override; uint64_t ThreadSpecificDataAddress() const override; @@ -71,6 +74,7 @@ #endif CPUContext context_; MemorySnapshotGeneric stack_; + std::string thread_name_; zx_koid_t thread_id_; zx_vaddr_t thread_specific_data_address_; InitializationStateDcheck initialized_;
diff --git a/third_party/crashpad/crashpad/snapshot/ios/process_snapshot_ios_intermediate_dump_test.cc b/third_party/crashpad/crashpad/snapshot/ios/process_snapshot_ios_intermediate_dump_test.cc index 34c34f8..0a170e7 100644 --- a/third_party/crashpad/crashpad/snapshot/ios/process_snapshot_ios_intermediate_dump_test.cc +++ b/third_party/crashpad/crashpad/snapshot/ios/process_snapshot_ios_intermediate_dump_test.cc
@@ -392,6 +392,7 @@ Key::kThreadContextMemoryRegionData, "string", 6)); } } + EXPECT_TRUE(writer->AddPropertyBytes(Key::kThreadName, "ariadne", 7)); } } @@ -411,6 +412,7 @@ uint64_t thread_id = 1; for (auto thread : threads) { EXPECT_EQ(thread->ThreadID(), thread_id); + EXPECT_EQ(thread->ThreadName(), "ariadne"); EXPECT_EQ(thread->SuspendCount(), 666); EXPECT_EQ(thread->Priority(), 5); EXPECT_EQ(thread->ThreadSpecificDataAddress(), thread_id++);
diff --git a/third_party/crashpad/crashpad/snapshot/ios/thread_snapshot_ios_intermediate_dump.cc b/third_party/crashpad/crashpad/snapshot/ios/thread_snapshot_ios_intermediate_dump.cc index ed7b28a..40387fa 100644 --- a/third_party/crashpad/crashpad/snapshot/ios/thread_snapshot_ios_intermediate_dump.cc +++ b/third_party/crashpad/crashpad/snapshot/ios/thread_snapshot_ios_intermediate_dump.cc
@@ -75,6 +75,7 @@ #endif context_(), stack_(), + thread_name_(), thread_id_(0), thread_specific_data_address_(0), suspend_count_(0), @@ -100,6 +101,7 @@ GetDataValueFromMap(thread_data, Key::kThreadID, &thread_id_); GetDataValueFromMap( thread_data, Key::kThreadDataAddress, &thread_specific_data_address_); + GetDataStringFromMap(thread_data, Key::kThreadName, &thread_name_); #if defined(ARCH_CPU_X86_64) typedef x86_thread_state64_t thread_state_type; @@ -218,6 +220,11 @@ return thread_id_; } +std::string ThreadSnapshotIOSIntermediateDump::ThreadName() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return thread_name_; +} + int ThreadSnapshotIOSIntermediateDump::SuspendCount() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); return suspend_count_;
diff --git a/third_party/crashpad/crashpad/snapshot/ios/thread_snapshot_ios_intermediate_dump.h b/third_party/crashpad/crashpad/snapshot/ios/thread_snapshot_ios_intermediate_dump.h index cf9ccec..dafa455 100644 --- a/third_party/crashpad/crashpad/snapshot/ios/thread_snapshot_ios_intermediate_dump.h +++ b/third_party/crashpad/crashpad/snapshot/ios/thread_snapshot_ios_intermediate_dump.h
@@ -15,6 +15,8 @@ #ifndef CRASHPAD_SNAPSHOT_IOS_INTERMEDIATE_DUMP_THREAD_SNAPSHOT_IOS_INTERMEDIATEDUMP_H_ #define CRASHPAD_SNAPSHOT_IOS_INTERMEDIATE_DUMP_THREAD_SNAPSHOT_IOS_INTERMEDIATEDUMP_H_ +#include <string> + #include "build/build_config.h" #include "snapshot/cpu_context.h" #include "snapshot/ios/memory_snapshot_ios_intermediate_dump.h" @@ -49,6 +51,7 @@ const CPUContext* Context() const override; const MemorySnapshot* Stack() const override; uint64_t ThreadID() const override; + std::string ThreadName() const override; int SuspendCount() const override; int Priority() const override; uint64_t ThreadSpecificDataAddress() const override; @@ -65,6 +68,7 @@ CPUContext context_; std::vector<uint8_t> exception_stack_memory_; MemorySnapshotIOSIntermediateDump stack_; + std::string thread_name_; uint64_t thread_id_; uint64_t thread_specific_data_address_; int suspend_count_;
diff --git a/third_party/crashpad/crashpad/snapshot/linux/process_reader_linux.cc b/third_party/crashpad/crashpad/snapshot/linux/process_reader_linux.cc index 5711f34..4b663bb 100644 --- a/third_party/crashpad/crashpad/snapshot/linux/process_reader_linux.cc +++ b/third_party/crashpad/crashpad/snapshot/linux/process_reader_linux.cc
@@ -24,6 +24,7 @@ #include <algorithm> #include "base/logging.h" +#include "base/strings/stringprintf.h" #include "build/build_config.h" #include "snapshot/linux/debug_rendezvous.h" #include "util/linux/auxiliary_vector.h" @@ -52,6 +53,7 @@ : thread_info(), stack_region_address(0), stack_region_size(0), + name(), tid(-1), static_priority(-1), nice_value(-1) {} @@ -64,6 +66,23 @@ return false; } + // From man proc(5): + // + // /proc/[pid]/comm (since Linux 2.6.33) + // + // Different threads in the same process may have different comm values, + // accessible via /proc/[pid]/task/[tid]/comm. + const std::string path = base::StringPrintf( + "/proc/%d/task/%d/comm", connection->GetProcessID(), tid); + if (connection->ReadFileContents(base::FilePath(path), &name)) { + if (!name.empty() && name.back() == '\n') { + // Remove the final newline character. + name.pop_back(); + } + } else { + // Continue on without the thread name. + } + // TODO(jperaza): Collect scheduling priorities via the broker when they can't // be collected directly. have_priorities = false;
diff --git a/third_party/crashpad/crashpad/snapshot/linux/process_reader_linux.h b/third_party/crashpad/crashpad/snapshot/linux/process_reader_linux.h index f44e15f..e8cf107 100644 --- a/third_party/crashpad/crashpad/snapshot/linux/process_reader_linux.h +++ b/third_party/crashpad/crashpad/snapshot/linux/process_reader_linux.h
@@ -60,6 +60,7 @@ ThreadInfo thread_info; LinuxVMAddress stack_region_address; LinuxVMSize stack_region_size; + std::string name; pid_t tid; int sched_policy; int static_priority;
diff --git a/third_party/crashpad/crashpad/snapshot/linux/process_reader_linux_test.cc b/third_party/crashpad/crashpad/snapshot/linux/process_reader_linux_test.cc index 81b3d6e..e4179de 100644 --- a/third_party/crashpad/crashpad/snapshot/linux/process_reader_linux_test.cc +++ b/third_party/crashpad/crashpad/snapshot/linux/process_reader_linux_test.cc
@@ -43,6 +43,7 @@ #include "test/linux/get_tls.h" #include "test/multiprocess.h" #include "test/scoped_module_handle.h" +#include "test/scoped_set_thread_name.h" #include "test/test_paths.h" #include "util/file/file_io.h" #include "util/file/file_writer.h" @@ -169,7 +170,9 @@ void StartThreads(size_t thread_count, size_t stack_size = 0) { for (size_t thread_index = 0; thread_index < thread_count; ++thread_index) { - threads_.push_back(std::make_unique<Thread>()); + const std::string thread_name = + base::StringPrintf("ThreadPool-%zu", thread_index); + threads_.push_back(std::make_unique<Thread>(thread_name)); Thread* thread = threads_.back().get(); pthread_attr_t attr; @@ -211,22 +214,26 @@ } pid_t GetThreadExpectation(size_t thread_index, - ThreadExpectation* expectation) { + ThreadExpectation* expectation, + std::string* thread_name_expectation) { CHECK_LT(thread_index, threads_.size()); const Thread* thread = threads_[thread_index].get(); *expectation = thread->expectation; + *thread_name_expectation = thread->name; return thread->tid; } private: struct Thread { - Thread() + explicit Thread(const std::string& name) : pthread(), expectation(), ready_semaphore(0), exit_semaphore(0), - tid(-1) {} + tid(-1), + name(name) { + } ~Thread() {} pthread_t pthread; @@ -235,10 +242,12 @@ Semaphore ready_semaphore; Semaphore exit_semaphore; pid_t tid; + const std::string name; }; static void* ThreadMain(void* argument) { Thread* thread = static_cast<Thread*>(argument); + const ScopedSetThreadName scoped_set_thread_name(thread->name); CHECK_EQ(setpriority(PRIO_PROCESS, 0, thread->expectation.nice_value), 0) << ErrnoMessage("setpriority"); @@ -260,20 +269,24 @@ }; using ThreadMap = std::map<pid_t, TestThreadPool::ThreadExpectation>; +using ThreadNameMap = std::map<pid_t, std::string>; void ExpectThreads(const ThreadMap& thread_map, + const ThreadNameMap& thread_name_map, const std::vector<ProcessReaderLinux::Thread>& threads, PtraceConnection* connection) { ASSERT_EQ(threads.size(), thread_map.size()); + ASSERT_EQ(threads.size(), thread_name_map.size()); MemoryMap memory_map; ASSERT_TRUE(memory_map.Initialize(connection)); for (const auto& thread : threads) { SCOPED_TRACE( - base::StringPrintf("Thread id %d, tls 0x%" PRIx64 + base::StringPrintf("Thread id %d, name %s, tls 0x%" PRIx64 ", stack addr 0x%" PRIx64 ", stack size 0x%" PRIx64, thread.tid, + thread.name.c_str(), thread.thread_info.thread_specific_data_address, thread.stack_region_address, thread.stack_region_size)); @@ -306,6 +319,10 @@ EXPECT_EQ(thread.sched_policy, iterator->second.sched_policy); EXPECT_EQ(thread.static_priority, iterator->second.static_priority); EXPECT_EQ(thread.nice_value, iterator->second.nice_value); + + const auto& thread_name_iterator = thread_name_map.find(thread.tid); + ASSERT_NE(thread_name_iterator, thread_name_map.end()); + EXPECT_EQ(thread.name, thread_name_iterator->second); } } @@ -322,6 +339,7 @@ private: void MultiprocessParent() override { ThreadMap thread_map; + ThreadNameMap thread_name_map; for (size_t thread_index = 0; thread_index < kThreadCount + 1; ++thread_index) { pid_t tid; @@ -331,6 +349,14 @@ CheckedReadFileExactly( ReadPipeHandle(), &expectation, sizeof(expectation)); thread_map[tid] = expectation; + + std::string::size_type thread_name_length; + CheckedReadFileExactly( + ReadPipeHandle(), &thread_name_length, sizeof(thread_name_length)); + std::string thread_name(thread_name_length, '\0'); + CheckedReadFileExactly( + ReadPipeHandle(), thread_name.data(), thread_name_length); + thread_name_map[tid] = thread_name; } DirectPtraceConnection connection; @@ -340,19 +366,22 @@ ASSERT_TRUE(process_reader.Initialize(&connection)); const std::vector<ProcessReaderLinux::Thread>& threads = process_reader.Threads(); - ExpectThreads(thread_map, threads, &connection); + ExpectThreads(thread_map, thread_name_map, threads, &connection); } void MultiprocessChild() override { TestThreadPool thread_pool; thread_pool.StartThreads(kThreadCount, stack_size_); + const std::string current_thread_name = "MultiprocChild"; + const ScopedSetThreadName scoped_set_thread_name(current_thread_name); + TestThreadPool::ThreadExpectation expectation; #if defined(MEMORY_SANITIZER) // memset() + re-initialization is required to zero padding bytes for MSan. memset(&expectation, 0, sizeof(expectation)); #endif // defined(MEMORY_SANITIZER) - expectation = {}; + expectation = {0}; expectation.tls = GetTLS(); expectation.stack_address = reinterpret_cast<LinuxVMAddress>(&thread_pool); @@ -373,11 +402,28 @@ CheckedWriteFile(WritePipeHandle(), &tid, sizeof(tid)); CheckedWriteFile(WritePipeHandle(), &expectation, sizeof(expectation)); + const std::string::size_type current_thread_name_length = + current_thread_name.length(); + CheckedWriteFile(WritePipeHandle(), + ¤t_thread_name_length, + sizeof(current_thread_name_length)); + CheckedWriteFile(WritePipeHandle(), + current_thread_name.data(), + current_thread_name_length); for (size_t thread_index = 0; thread_index < kThreadCount; ++thread_index) { - tid = thread_pool.GetThreadExpectation(thread_index, &expectation); + std::string thread_name_expectation; + tid = thread_pool.GetThreadExpectation( + thread_index, &expectation, &thread_name_expectation); CheckedWriteFile(WritePipeHandle(), &tid, sizeof(tid)); CheckedWriteFile(WritePipeHandle(), &expectation, sizeof(expectation)); + const std::string::size_type thread_name_length = + thread_name_expectation.length(); + CheckedWriteFile( + WritePipeHandle(), &thread_name_length, sizeof(thread_name_length)); + CheckedWriteFile(WritePipeHandle(), + thread_name_expectation.data(), + thread_name_length); } CheckedReadFileAtEOF(ReadPipeHandle());
diff --git a/third_party/crashpad/crashpad/snapshot/linux/thread_snapshot_linux.cc b/third_party/crashpad/crashpad/snapshot/linux/thread_snapshot_linux.cc index f279e0ad..04776de 100644 --- a/third_party/crashpad/crashpad/snapshot/linux/thread_snapshot_linux.cc +++ b/third_party/crashpad/crashpad/snapshot/linux/thread_snapshot_linux.cc
@@ -133,6 +133,7 @@ context_(), stack_(), thread_specific_data_address_(0), + thread_name_(), thread_id_(-1), priority_(-1), initialized_() {} @@ -200,6 +201,7 @@ thread_specific_data_address_ = thread.thread_info.thread_specific_data_address; + thread_name_ = thread.name; thread_id_ = thread.tid; priority_ = @@ -234,6 +236,11 @@ return thread_id_; } +std::string ThreadSnapshotLinux::ThreadName() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return thread_name_; +} + int ThreadSnapshotLinux::SuspendCount() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); return 0;
diff --git a/third_party/crashpad/crashpad/snapshot/linux/thread_snapshot_linux.h b/third_party/crashpad/crashpad/snapshot/linux/thread_snapshot_linux.h index 40cd7e7..4d1f7a7 100644 --- a/third_party/crashpad/crashpad/snapshot/linux/thread_snapshot_linux.h +++ b/third_party/crashpad/crashpad/snapshot/linux/thread_snapshot_linux.h
@@ -57,6 +57,7 @@ const CPUContext* Context() const override; const MemorySnapshot* Stack() const override; uint64_t ThreadID() const override; + std::string ThreadName() const override; int SuspendCount() const override; int Priority() const override; uint64_t ThreadSpecificDataAddress() const override; @@ -80,6 +81,7 @@ CPUContext context_; MemorySnapshotGeneric stack_; LinuxVMAddress thread_specific_data_address_; + std::string thread_name_; pid_t thread_id_; int priority_; InitializationStateDcheck initialized_;
diff --git a/third_party/crashpad/crashpad/snapshot/mac/process_reader_mac.cc b/third_party/crashpad/crashpad/snapshot/mac/process_reader_mac.cc index 9b2a235..5f9f8b7b 100644 --- a/third_party/crashpad/crashpad/snapshot/mac/process_reader_mac.cc +++ b/third_party/crashpad/crashpad/snapshot/mac/process_reader_mac.cc
@@ -75,6 +75,7 @@ : thread_context(), float_context(), debug_context(), + name(), id(0), stack_region_address(0), stack_region_size(0), @@ -365,6 +366,20 @@ thread.thread_specific_data_address = identifier_info.thread_handle; } + thread_extended_info extended_info; + count = THREAD_EXTENDED_INFO_COUNT; + kr = thread_info(thread.port, + THREAD_EXTENDED_INFO, + reinterpret_cast<thread_info_t>(&extended_info), + &count); + if (kr != KERN_SUCCESS) { + MACH_LOG(WARNING, kr) << "thread_info(THREAD_EXTENDED_INFO)"; + } else { + thread.name.assign( + extended_info.pth_name, + strnlen(extended_info.pth_name, sizeof(extended_info.pth_name))); + } + thread_precedence_policy precedence; count = THREAD_PRECEDENCE_POLICY_COUNT; boolean_t get_default = FALSE;
diff --git a/third_party/crashpad/crashpad/snapshot/mac/process_reader_mac.h b/third_party/crashpad/crashpad/snapshot/mac/process_reader_mac.h index 85cfe7c..4f7792f 100644 --- a/third_party/crashpad/crashpad/snapshot/mac/process_reader_mac.h +++ b/third_party/crashpad/crashpad/snapshot/mac/process_reader_mac.h
@@ -75,6 +75,7 @@ ThreadContext thread_context; FloatContext float_context; DebugContext debug_context; + std::string name; uint64_t id; mach_vm_address_t stack_region_address; mach_vm_size_t stack_region_size;
diff --git a/third_party/crashpad/crashpad/snapshot/mac/process_reader_mac_test.cc b/third_party/crashpad/crashpad/snapshot/mac/process_reader_mac_test.cc index d5104e8..8d1ba2a 100644 --- a/third_party/crashpad/crashpad/snapshot/mac/process_reader_mac_test.cc +++ b/third_party/crashpad/crashpad/snapshot/mac/process_reader_mac_test.cc
@@ -42,6 +42,7 @@ #include "test/mac/dyld.h" #include "test/mac/mach_errors.h" #include "test/mac/mach_multiprocess.h" +#include "test/scoped_set_thread_name.h" #include "util/file/file_io.h" #include "util/mac/mac_util.h" #include "util/mach/mach_extensions.h" @@ -139,6 +140,9 @@ } TEST(ProcessReaderMac, SelfOneThread) { + const ScopedSetThreadName scoped_set_thread_name( + "ProcessReaderMac/SelfOneThread"); + ProcessReaderMac process_reader; ASSERT_TRUE(process_reader.Initialize(mach_task_self())); @@ -151,6 +155,7 @@ ASSERT_GE(threads.size(), 1u); EXPECT_EQ(threads[0].id, PthreadToThreadID(pthread_self())); + EXPECT_EQ(threads[0].name, "ProcessReaderMac/SelfOneThread"); thread_t thread_self = MachThreadSelf(); EXPECT_EQ(threads[0].port, thread_self); @@ -163,9 +168,11 @@ struct ThreadExpectation { mach_vm_address_t stack_address; int suspend_count; + std::string thread_name; }; - TestThreadPool() : thread_infos_() {} + TestThreadPool(const std::string& thread_name_prefix) + : thread_infos_(), thread_name_prefix_(thread_name_prefix) {} TestThreadPool(const TestThreadPool&) = delete; TestThreadPool& operator=(const TestThreadPool&) = delete; @@ -199,7 +206,10 @@ ASSERT_TRUE(thread_infos_.empty()); for (size_t thread_index = 0; thread_index < thread_count; ++thread_index) { - thread_infos_.push_back(std::make_unique<ThreadInfo>()); + std::string thread_name = base::StringPrintf( + "%s-%zu", thread_name_prefix_.c_str(), thread_index); + thread_infos_.push_back( + std::make_unique<ThreadInfo>(std::move(thread_name))); ThreadInfo* thread_info = thread_infos_.back().get(); int rv = pthread_create( @@ -235,18 +245,20 @@ const auto& thread_info = thread_infos_[thread_index]; expectation->stack_address = thread_info->stack_address; expectation->suspend_count = thread_info->suspend_count; + expectation->thread_name = thread_info->thread_name; return PthreadToThreadID(thread_info->pthread); } private: struct ThreadInfo { - ThreadInfo() + ThreadInfo(const std::string& thread_name) : pthread(nullptr), stack_address(0), ready_semaphore(0), exit_semaphore(0), - suspend_count(0) {} + suspend_count(0), + thread_name(thread_name) {} ~ThreadInfo() {} @@ -270,10 +282,14 @@ // The thread’s suspend count. int suspend_count; + + // The thread's name. + const std::string thread_name; }; static void* ThreadMain(void* argument) { ThreadInfo* thread_info = static_cast<ThreadInfo*>(argument); + const ScopedSetThreadName scoped_set_thread_name(thread_info->thread_name); thread_info->stack_address = FromPointerCast<mach_vm_address_t>(&thread_info); @@ -293,6 +309,9 @@ // This is a vector of pointers because the address of a ThreadInfo object is // passed to each thread’s ThreadMain(), so they cannot move around in memory. std::vector<std::unique_ptr<ThreadInfo>> thread_infos_; + + // Prefix to use for each thread's name, suffixed with "-$threadindex". + const std::string thread_name_prefix_; }; using ThreadMap = std::map<uint64_t, TestThreadPool::ThreadExpectation>; @@ -328,6 +347,7 @@ EXPECT_LT(iterator->second.stack_address, thread_stack_region_end); EXPECT_EQ(thread.suspend_count, iterator->second.suspend_count); + EXPECT_EQ(thread.name, iterator->second.thread_name); // Remove the thread from the expectation map since it’s already been // found. This makes it easy to check for duplicate thread IDs, and makes @@ -375,7 +395,7 @@ ProcessReaderMac process_reader; ASSERT_TRUE(process_reader.Initialize(mach_task_self())); - TestThreadPool thread_pool; + TestThreadPool thread_pool("SelfSeveralThreads"); constexpr size_t kChildThreads = 16; ASSERT_NO_FATAL_FAILURE(thread_pool.StartThreads(kChildThreads)); @@ -393,6 +413,8 @@ // There can’t be any duplicate thread IDs. EXPECT_EQ(thread_map.count(thread_id), 0u); + expectation.thread_name = + base::StringPrintf("SelfSeveralThreads-%zu", thread_index); thread_map[thread_id] = expectation; } @@ -432,8 +454,11 @@ class ProcessReaderThreadedChild final : public MachMultiprocess { public: - explicit ProcessReaderThreadedChild(size_t thread_count) - : MachMultiprocess(), thread_count_(thread_count) {} + explicit ProcessReaderThreadedChild(const std::string thread_name_prefix, + size_t thread_count) + : MachMultiprocess(), + thread_name_prefix_(thread_name_prefix), + thread_count_(thread_count) {} ProcessReaderThreadedChild(const ProcessReaderThreadedChild&) = delete; ProcessReaderThreadedChild& operator=(const ProcessReaderThreadedChild&) = @@ -464,6 +489,15 @@ CheckedReadFileExactly(read_handle, &expectation.suspend_count, sizeof(expectation.suspend_count)); + std::string::size_type expected_thread_name_length; + CheckedReadFileExactly(read_handle, + &expected_thread_name_length, + sizeof(expected_thread_name_length)); + std::string expected_thread_name(expected_thread_name_length, '\0'); + CheckedReadFileExactly(read_handle, + expected_thread_name.data(), + expected_thread_name_length); + expectation.thread_name = expected_thread_name; // There can’t be any duplicate thread IDs. EXPECT_EQ(thread_map.count(thread_id), 0u); @@ -480,9 +514,13 @@ } void MachMultiprocessChild() override { - TestThreadPool thread_pool; + TestThreadPool thread_pool(thread_name_prefix_); ASSERT_NO_FATAL_FAILURE(thread_pool.StartThreads(thread_count_)); + const std::string current_thread_name(base::StringPrintf( + "%s-MachMultiprocessChild", thread_name_prefix_.c_str())); + const ScopedSetThreadName scoped_set_thread_name(current_thread_name); + FileHandle write_handle = WritePipeHandle(); // This thread isn’t part of the thread pool, but the parent will be able @@ -501,6 +539,13 @@ CheckedWriteFile(write_handle, &expectation.suspend_count, sizeof(expectation.suspend_count)); + const std::string::size_type current_thread_name_length = + current_thread_name.length(); + CheckedWriteFile(write_handle, + ¤t_thread_name_length, + sizeof(current_thread_name_length)); + CheckedWriteFile( + write_handle, current_thread_name.data(), current_thread_name_length); // Write an entry for everything in the thread pool. for (size_t thread_index = 0; thread_index < thread_count_; @@ -514,6 +559,16 @@ CheckedWriteFile(write_handle, &expectation.suspend_count, sizeof(expectation.suspend_count)); + const std::string thread_pool_thread_name = base::StringPrintf( + "%s-%zu", thread_name_prefix_.c_str(), thread_index); + const std::string::size_type thread_pool_thread_name_length = + thread_pool_thread_name.length(); + CheckedWriteFile(write_handle, + &thread_pool_thread_name_length, + sizeof(thread_pool_thread_name_length)); + CheckedWriteFile(write_handle, + thread_pool_thread_name.data(), + thread_pool_thread_name_length); } // Wait for the parent to signal that it’s OK to exit by closing its end of @@ -521,6 +576,7 @@ CheckedReadFileAtEOF(ReadPipeHandle()); } + const std::string thread_name_prefix_; size_t thread_count_; }; @@ -528,14 +584,16 @@ TEST(ProcessReaderMac, DISABLED_ChildOneThread) { // The main thread plus zero child threads equals one thread. constexpr size_t kChildThreads = 0; - ProcessReaderThreadedChild process_reader_threaded_child(kChildThreads); + ProcessReaderThreadedChild process_reader_threaded_child("ChildOneThread", + kChildThreads); process_reader_threaded_child.Run(); } // TODO(crbug.com/1319307): Test is failing on Mac. Re-enable it. TEST(ProcessReaderMac, DISABLED_ChildSeveralThreads) { constexpr size_t kChildThreads = 64; - ProcessReaderThreadedChild process_reader_threaded_child(kChildThreads); + ProcessReaderThreadedChild process_reader_threaded_child( + "ChildSeveralThreads", kChildThreads); process_reader_threaded_child.Run(); }
diff --git a/third_party/crashpad/crashpad/snapshot/mac/thread_snapshot_mac.cc b/third_party/crashpad/crashpad/snapshot/mac/thread_snapshot_mac.cc index d261f194..485ffc2 100644 --- a/third_party/crashpad/crashpad/snapshot/mac/thread_snapshot_mac.cc +++ b/third_party/crashpad/crashpad/snapshot/mac/thread_snapshot_mac.cc
@@ -26,13 +26,13 @@ context_union_(), context_(), stack_(), + thread_name_(), thread_id_(0), thread_specific_data_address_(0), thread_(MACH_PORT_NULL), suspend_count_(0), priority_(0), - initialized_() { -} + initialized_() {} ThreadSnapshotMac::~ThreadSnapshotMac() { } @@ -44,6 +44,7 @@ thread_ = process_reader_thread.port; thread_id_ = process_reader_thread.id; + thread_name_ = process_reader_thread.name; suspend_count_ = process_reader_thread.suspend_count; priority_ = process_reader_thread.priority; thread_specific_data_address_ = @@ -108,6 +109,11 @@ return thread_id_; } +std::string ThreadSnapshotMac::ThreadName() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return thread_name_; +} + int ThreadSnapshotMac::SuspendCount() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); return suspend_count_;
diff --git a/third_party/crashpad/crashpad/snapshot/mac/thread_snapshot_mac.h b/third_party/crashpad/crashpad/snapshot/mac/thread_snapshot_mac.h index 4d9cb3167..ebdb3ae 100644 --- a/third_party/crashpad/crashpad/snapshot/mac/thread_snapshot_mac.h +++ b/third_party/crashpad/crashpad/snapshot/mac/thread_snapshot_mac.h
@@ -18,6 +18,8 @@ #include <mach/mach.h> #include <stdint.h> +#include <string> + #include "build/build_config.h" #include "snapshot/cpu_context.h" #include "snapshot/mac/process_reader_mac.h" @@ -60,6 +62,7 @@ const CPUContext* Context() const override; const MemorySnapshot* Stack() const override; uint64_t ThreadID() const override; + std::string ThreadName() const override; int SuspendCount() const override; int Priority() const override; uint64_t ThreadSpecificDataAddress() const override; @@ -78,6 +81,7 @@ } context_union_; CPUContext context_; MemorySnapshotGeneric stack_; + std::string thread_name_; uint64_t thread_id_; uint64_t thread_specific_data_address_; thread_t thread_;
diff --git a/third_party/crashpad/crashpad/snapshot/minidump/process_snapshot_minidump.cc b/third_party/crashpad/crashpad/snapshot/minidump/process_snapshot_minidump.cc index 8c870ee..2894244 100644 --- a/third_party/crashpad/crashpad/snapshot/minidump/process_snapshot_minidump.cc +++ b/third_party/crashpad/crashpad/snapshot/minidump/process_snapshot_minidump.cc
@@ -23,6 +23,7 @@ #include "minidump/minidump_extensions.h" #include "snapshot/memory_map_region_snapshot.h" #include "snapshot/minidump/minidump_simple_string_dictionary_reader.h" +#include "snapshot/minidump/minidump_string_reader.h" #include "util/file/file_io.h" namespace crashpad { @@ -576,12 +577,16 @@ return false; } + if (!InitializeThreadNames()) { + return false; + } + for (uint32_t thread_index = 0; thread_index < thread_count; ++thread_index) { const RVA thread_rva = stream_it->second->Rva + sizeof(thread_count) + thread_index * sizeof(MINIDUMP_THREAD); auto thread = std::make_unique<internal::ThreadSnapshotMinidump>(); - if (!thread->Initialize(file_reader_, thread_rva, arch_)) { + if (!thread->Initialize(file_reader_, thread_rva, arch_, thread_names_)) { return false; } @@ -591,6 +596,59 @@ return true; } +bool ProcessSnapshotMinidump::InitializeThreadNames() { + const auto& stream_it = stream_map_.find(kMinidumpStreamTypeThreadNameList); + if (stream_it == stream_map_.end()) { + return true; + } + + if (stream_it->second->DataSize < sizeof(MINIDUMP_THREAD_NAME_LIST)) { + LOG(ERROR) << "thread_name_list size mismatch"; + return false; + } + + if (!file_reader_->SeekSet(stream_it->second->Rva)) { + return false; + } + + uint32_t thread_name_count; + if (!file_reader_->ReadExactly(&thread_name_count, + sizeof(thread_name_count))) { + return false; + } + + if (sizeof(MINIDUMP_THREAD_NAME_LIST) + + thread_name_count * sizeof(MINIDUMP_THREAD_NAME) != + stream_it->second->DataSize) { + LOG(ERROR) << "thread_name_list size mismatch"; + return false; + } + + for (uint32_t thread_name_index = 0; thread_name_index < thread_name_count; + ++thread_name_index) { + const RVA thread_name_rva = + stream_it->second->Rva + sizeof(thread_name_count) + + thread_name_index * sizeof(MINIDUMP_THREAD_NAME); + if (!file_reader_->SeekSet(thread_name_rva)) { + return false; + } + MINIDUMP_THREAD_NAME minidump_thread_name; + if (!file_reader_->ReadExactly(&minidump_thread_name, + sizeof(minidump_thread_name))) { + return false; + } + std::string name; + if (!internal::ReadMinidumpUTF16String( + file_reader_, minidump_thread_name.RvaOfThreadName, &name)) { + return false; + } + + thread_names_.emplace(minidump_thread_name.ThreadId, std::move(name)); + } + + return true; +} + bool ProcessSnapshotMinidump::InitializeSystemSnapshot() { const auto& stream_it = stream_map_.find(kMinidumpStreamTypeSystemInfo); if (stream_it == stream_map_.end()) {
diff --git a/third_party/crashpad/crashpad/snapshot/minidump/process_snapshot_minidump.h b/third_party/crashpad/crashpad/snapshot/minidump/process_snapshot_minidump.h index 18fbc7c..351fc4e 100644 --- a/third_party/crashpad/crashpad/snapshot/minidump/process_snapshot_minidump.h +++ b/third_party/crashpad/crashpad/snapshot/minidump/process_snapshot_minidump.h
@@ -112,6 +112,10 @@ // Initialize(). bool InitializeThreads(); + // Initializes data carried in a MINIDUMP_THREAD_NAME_LIST stream on behalf of + // Initialize(). + bool InitializeThreadNames(); + // Initializes data carried in a MINIDUMP_MEMORY_INFO_LIST stream on behalf of // Initialize(). bool InitializeMemoryInfo(); @@ -147,6 +151,7 @@ std::map<MinidumpStreamType, const MINIDUMP_LOCATION_DESCRIPTOR*> stream_map_; std::vector<std::unique_ptr<internal::ModuleSnapshotMinidump>> modules_; std::vector<std::unique_ptr<internal::ThreadSnapshotMinidump>> threads_; + std::map<uint32_t, std::string> thread_names_; std::vector<UnloadedModuleSnapshot> unloaded_modules_; std::vector<std::unique_ptr<internal::MemoryMapRegionSnapshotMinidump>> mem_regions_;
diff --git a/third_party/crashpad/crashpad/snapshot/minidump/process_snapshot_minidump_test.cc b/third_party/crashpad/crashpad/snapshot/minidump/process_snapshot_minidump_test.cc index ded561b..7fb4388 100644 --- a/third_party/crashpad/crashpad/snapshot/minidump/process_snapshot_minidump_test.cc +++ b/third_party/crashpad/crashpad/snapshot/minidump/process_snapshot_minidump_test.cc
@@ -728,6 +728,104 @@ } } +TEST(ProcessSnapshotMinidump, ThreadsWithNames) { + StringFile string_file; + + MINIDUMP_HEADER header = {}; + EXPECT_TRUE(string_file.Write(&header, sizeof(header))); + + constexpr uint32_t kMinidumpThreadCount = 4; + constexpr uint32_t kBaseThreadId = 42; + + const std::string thread_names[kMinidumpThreadCount] = { + "ariadne", + "theseus", + "pasiphae", + "minos", + }; + + RVA64 thread_name_rva64s[kMinidumpThreadCount]; + for (uint32_t i = 0; i < kMinidumpThreadCount; i++) { + thread_name_rva64s[i] = static_cast<RVA64>(string_file.SeekGet()); + auto name16 = base::UTF8ToUTF16(thread_names[i]); + uint32_t size = + base::checked_cast<uint32_t>(sizeof(name16[0]) * name16.size()); + EXPECT_TRUE(string_file.Write(&size, sizeof(size))); + EXPECT_TRUE(string_file.Write(&name16[0], size)); + } + + MINIDUMP_DIRECTORY minidump_thread_list_directory = {}; + minidump_thread_list_directory.StreamType = kMinidumpStreamTypeThreadList; + minidump_thread_list_directory.Location.DataSize = + sizeof(MINIDUMP_THREAD_LIST) + + kMinidumpThreadCount * sizeof(MINIDUMP_THREAD); + minidump_thread_list_directory.Location.Rva = + static_cast<RVA>(string_file.SeekGet()); + + // Fields in MINIDUMP_THREAD_LIST. + EXPECT_TRUE( + string_file.Write(&kMinidumpThreadCount, sizeof(kMinidumpThreadCount))); + for (uint32_t minidump_thread_index = 0; + minidump_thread_index < kMinidumpThreadCount; + ++minidump_thread_index) { + MINIDUMP_THREAD minidump_thread = {}; + minidump_thread.ThreadId = kBaseThreadId + minidump_thread_index; + EXPECT_TRUE(string_file.Write(&minidump_thread, sizeof(minidump_thread))); + } + + header.StreamDirectoryRva = static_cast<RVA>(string_file.SeekGet()); + EXPECT_TRUE(string_file.Write(&minidump_thread_list_directory, + sizeof(minidump_thread_list_directory))); + + MINIDUMP_DIRECTORY minidump_thread_name_list_directory = {}; + minidump_thread_name_list_directory.StreamType = + kMinidumpStreamTypeThreadNameList; + minidump_thread_name_list_directory.Location.DataSize = + sizeof(MINIDUMP_THREAD_NAME_LIST) + + kMinidumpThreadCount * sizeof(MINIDUMP_THREAD_NAME); + minidump_thread_name_list_directory.Location.Rva = + static_cast<RVA>(string_file.SeekGet()); + + // Fields in MINIDUMP_THREAD_NAME_LIST. + EXPECT_TRUE( + string_file.Write(&kMinidumpThreadCount, sizeof(kMinidumpThreadCount))); + for (uint32_t minidump_thread_index = 0; + minidump_thread_index < kMinidumpThreadCount; + ++minidump_thread_index) { + MINIDUMP_THREAD_NAME minidump_thread_name = {0, 0}; + minidump_thread_name.ThreadId = kBaseThreadId + minidump_thread_index; + minidump_thread_name.RvaOfThreadName = + thread_name_rva64s[minidump_thread_index]; + EXPECT_TRUE( + string_file.Write(&minidump_thread_name, sizeof(minidump_thread_name))); + } + + header.StreamDirectoryRva = static_cast<RVA>(string_file.SeekGet()); + ASSERT_TRUE(string_file.Write(&minidump_thread_list_directory, + sizeof(minidump_thread_list_directory))); + ASSERT_TRUE(string_file.Write(&minidump_thread_name_list_directory, + sizeof(minidump_thread_name_list_directory))); + + header.Signature = MINIDUMP_SIGNATURE; + header.Version = MINIDUMP_VERSION; + header.NumberOfStreams = 2; + EXPECT_TRUE(string_file.SeekSet(0)); + EXPECT_TRUE(string_file.Write(&header, sizeof(header))); + + ProcessSnapshotMinidump process_snapshot; + EXPECT_TRUE(process_snapshot.Initialize(&string_file)); + + std::vector<const ThreadSnapshot*> threads = process_snapshot.Threads(); + ASSERT_EQ(threads.size(), kMinidumpThreadCount); + + size_t idx = 0; + for (const auto& thread : threads) { + EXPECT_EQ(thread->ThreadID(), kBaseThreadId + idx); + EXPECT_EQ(thread->ThreadName(), thread_names[idx]); + idx++; + } +} + TEST(ProcessSnapshotMinidump, System) { const char* cpu_info = "GenuineIntel"; const uint32_t* cpu_info_bytes = reinterpret_cast<const uint32_t*>(cpu_info);
diff --git a/third_party/crashpad/crashpad/snapshot/minidump/thread_snapshot_minidump.cc b/third_party/crashpad/crashpad/snapshot/minidump/thread_snapshot_minidump.cc index f8f3673..26aabd7 100644 --- a/third_party/crashpad/crashpad/snapshot/minidump/thread_snapshot_minidump.cc +++ b/third_party/crashpad/crashpad/snapshot/minidump/thread_snapshot_minidump.cc
@@ -26,15 +26,18 @@ ThreadSnapshotMinidump::ThreadSnapshotMinidump() : ThreadSnapshot(), minidump_thread_(), + thread_name_(), context_(), stack_(), initialized_() {} ThreadSnapshotMinidump::~ThreadSnapshotMinidump() {} -bool ThreadSnapshotMinidump::Initialize(FileReaderInterface* file_reader, - RVA minidump_thread_rva, - CPUArchitecture arch) { +bool ThreadSnapshotMinidump::Initialize( + FileReaderInterface* file_reader, + RVA minidump_thread_rva, + CPUArchitecture arch, + const std::map<uint32_t, std::string>& thread_names) { INITIALIZATION_STATE_SET_INITIALIZING(initialized_); std::vector<unsigned char> minidump_context; @@ -67,6 +70,10 @@ if (!stack_.Initialize(file_reader, stack_info_location)) { return false; } + const auto thread_name_iter = thread_names.find(minidump_thread_.ThreadId); + if (thread_name_iter != thread_names.end()) { + thread_name_ = thread_name_iter->second; + } INITIALIZATION_STATE_SET_VALID(initialized_); return true; @@ -77,6 +84,11 @@ return minidump_thread_.ThreadId; } +std::string ThreadSnapshotMinidump::ThreadName() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return thread_name_; +} + int ThreadSnapshotMinidump::SuspendCount() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); return minidump_thread_.SuspendCount;
diff --git a/third_party/crashpad/crashpad/snapshot/minidump/thread_snapshot_minidump.h b/third_party/crashpad/crashpad/snapshot/minidump/thread_snapshot_minidump.h index 7efb18d..b0ef424 100644 --- a/third_party/crashpad/crashpad/snapshot/minidump/thread_snapshot_minidump.h +++ b/third_party/crashpad/crashpad/snapshot/minidump/thread_snapshot_minidump.h
@@ -17,6 +17,8 @@ #include <windows.h> +#include <map> + #include "minidump/minidump_extensions.h" #include "snapshot/cpu_context.h" #include "snapshot/minidump/memory_snapshot_minidump.h" @@ -46,16 +48,20 @@ //! the thread’s MINIDUMP_THREAD structure is located. //! \param[in] arch The architecture of the system this thread is running on. //! Used to decode CPU Context. + //! \param[in] thread_names Map from thread ID to thread name previously read + //! from the minidump's MINIDUMP_THREAD_NAME_LIST. //! //! \return `true` if the snapshot could be created, `false` otherwise with //! an appropriate message logged. bool Initialize(FileReaderInterface* file_reader, RVA minidump_thread_rva, - CPUArchitecture arch); + CPUArchitecture arch, + const std::map<uint32_t, std::string>& thread_names); const CPUContext* Context() const override; const MemorySnapshot* Stack() const override; uint64_t ThreadID() const override; + std::string ThreadName() const override; int SuspendCount() const override; int Priority() const override; uint64_t ThreadSpecificDataAddress() const override; @@ -71,6 +77,7 @@ bool InitializeContext(const std::vector<unsigned char>& minidump_context); MINIDUMP_THREAD minidump_thread_; + std::string thread_name_; MinidumpContextConverter context_; MemorySnapshotMinidump stack_; InitializationStateDcheck initialized_;
diff --git a/third_party/crashpad/crashpad/snapshot/sanitized/thread_snapshot_sanitized.cc b/third_party/crashpad/crashpad/snapshot/sanitized/thread_snapshot_sanitized.cc index 186776e..4a1fb72 100644 --- a/third_party/crashpad/crashpad/snapshot/sanitized/thread_snapshot_sanitized.cc +++ b/third_party/crashpad/crashpad/snapshot/sanitized/thread_snapshot_sanitized.cc
@@ -39,6 +39,10 @@ return snapshot_->ThreadID(); } +std::string ThreadSnapshotSanitized::ThreadName() const { + return snapshot_->ThreadName(); +} + int ThreadSnapshotSanitized::SuspendCount() const { return snapshot_->SuspendCount(); }
diff --git a/third_party/crashpad/crashpad/snapshot/sanitized/thread_snapshot_sanitized.h b/third_party/crashpad/crashpad/snapshot/sanitized/thread_snapshot_sanitized.h index dca0a0e..b520579 100644 --- a/third_party/crashpad/crashpad/snapshot/sanitized/thread_snapshot_sanitized.h +++ b/third_party/crashpad/crashpad/snapshot/sanitized/thread_snapshot_sanitized.h
@@ -17,6 +17,8 @@ #include "snapshot/thread_snapshot.h" +#include <string> + #include "snapshot/sanitized/memory_snapshot_sanitized.h" #include "util/misc/range_set.h" @@ -44,6 +46,7 @@ const CPUContext* Context() const override; const MemorySnapshot* Stack() const override; uint64_t ThreadID() const override; + std::string ThreadName() const override; int SuspendCount() const override; int Priority() const override; uint64_t ThreadSpecificDataAddress() const override;
diff --git a/third_party/crashpad/crashpad/snapshot/test/test_thread_snapshot.cc b/third_party/crashpad/crashpad/snapshot/test/test_thread_snapshot.cc index ed6d9fd..3e73ecd 100644 --- a/third_party/crashpad/crashpad/snapshot/test/test_thread_snapshot.cc +++ b/third_party/crashpad/crashpad/snapshot/test/test_thread_snapshot.cc
@@ -43,6 +43,10 @@ return thread_id_; } +std::string TestThreadSnapshot::ThreadName() const { + return thread_name_; +} + int TestThreadSnapshot::SuspendCount() const { return suspend_count_; }
diff --git a/third_party/crashpad/crashpad/snapshot/test/test_thread_snapshot.h b/third_party/crashpad/crashpad/snapshot/test/test_thread_snapshot.h index f865bdfe..a1cef21 100644 --- a/third_party/crashpad/crashpad/snapshot/test/test_thread_snapshot.h +++ b/third_party/crashpad/crashpad/snapshot/test/test_thread_snapshot.h
@@ -18,6 +18,7 @@ #include <stdint.h> #include <memory> +#include <string> #include <utility> #include <vector> @@ -63,6 +64,9 @@ } void SetThreadID(uint64_t thread_id) { thread_id_ = thread_id; } + void SetThreadName(const std::string& thread_name) { + thread_name_ = thread_name; + } void SetSuspendCount(int suspend_count) { suspend_count_ = suspend_count; } void SetPriority(int priority) { priority_ = priority; } void SetThreadSpecificDataAddress(uint64_t thread_specific_data_address) { @@ -83,6 +87,7 @@ const CPUContext* Context() const override; const MemorySnapshot* Stack() const override; uint64_t ThreadID() const override; + std::string ThreadName() const override; int SuspendCount() const override; int Priority() const override; uint64_t ThreadSpecificDataAddress() const override; @@ -96,6 +101,7 @@ CPUContext context_; std::unique_ptr<MemorySnapshot> stack_; uint64_t thread_id_; + std::string thread_name_; int suspend_count_; int priority_; uint64_t thread_specific_data_address_;
diff --git a/third_party/crashpad/crashpad/snapshot/thread_snapshot.h b/third_party/crashpad/crashpad/snapshot/thread_snapshot.h index 4d73257..ade3ee6 100644 --- a/third_party/crashpad/crashpad/snapshot/thread_snapshot.h +++ b/third_party/crashpad/crashpad/snapshot/thread_snapshot.h
@@ -17,6 +17,7 @@ #include <stdint.h> +#include <string> #include <vector> namespace crashpad { @@ -51,6 +52,9 @@ //! unique system-wide. virtual uint64_t ThreadID() const = 0; + //! \brief Returns the thread's name. + virtual std::string ThreadName() const = 0; + //! \brief Returns the thread’s suspend count. //! //! A suspend count of `0` denotes a schedulable (not suspended) thread.
diff --git a/third_party/crashpad/crashpad/snapshot/win/process_reader_win.cc b/third_party/crashpad/crashpad/snapshot/win/process_reader_win.cc index 2307380..45a932c0 100644 --- a/third_party/crashpad/crashpad/snapshot/win/process_reader_win.cc +++ b/third_party/crashpad/crashpad/snapshot/win/process_reader_win.cc
@@ -21,13 +21,16 @@ #include "base/notreached.h" #include "base/numerics/safe_conversions.h" +#include "base/strings/utf_string_conversions.h" #include "snapshot/win/cpu_context_win.h" #include "util/misc/capture_context.h" #include "util/misc/time.h" +#include "util/win/get_function.h" #include "util/win/nt_internals.h" #include "util/win/ntstatus_logging.h" #include "util/win/process_structs.h" #include "util/win/scoped_handle.h" +#include "util/win/scoped_local_alloc.h" namespace crashpad { @@ -232,12 +235,9 @@ bool ProcessReaderWin::ThreadContext::InitializeXState( HANDLE thread_handle, ULONG64 XStateCompactionMask) { - static auto initialize_context_2 = []() { - // InitializeContext2 needs Windows 10 build 20348. - HINSTANCE kernel32 = GetModuleHandle(L"Kernel32.dll"); - return reinterpret_cast<decltype(InitializeContext2)*>( - GetProcAddress(kernel32, "InitializeContext2")); - }(); + // InitializeContext2 needs Windows 10 build 20348. + static const auto initialize_context_2 = + GET_FUNCTION(L"kernel32.dll", ::InitializeContext2); if (!initialize_context_2) return false; // We want CET_U xstate to get the ssp, only possible when supported. @@ -276,6 +276,7 @@ ProcessReaderWin::Thread::Thread() : context(), + name(), id(0), teb_address(0), teb_size(0), @@ -460,6 +461,21 @@ thread.stack_region_size = base - limit; } } + // On Windows 10 build 1607 and later, read the thread name. + static const auto get_thread_description = + GET_FUNCTION(L"kernel32.dll", ::GetThreadDescription); + if (get_thread_description) { + wchar_t* thread_description; + HRESULT hr = + get_thread_description(thread_handle.get(), &thread_description); + if (SUCCEEDED(hr)) { + ScopedLocalAlloc thread_description_owner(thread_description); + thread.name = base::WideToUTF8(thread_description); + } else { + LOG(WARNING) << "GetThreadDescription: " + << logging::SystemErrorCodeToString(hr); + } + } threads_.push_back(thread); } }
diff --git a/third_party/crashpad/crashpad/snapshot/win/process_reader_win.h b/third_party/crashpad/crashpad/snapshot/win/process_reader_win.h index 5987c00..90ff1cb 100644 --- a/third_party/crashpad/crashpad/snapshot/win/process_reader_win.h +++ b/third_party/crashpad/crashpad/snapshot/win/process_reader_win.h
@@ -18,6 +18,7 @@ #include <windows.h> #include <sys/time.h> +#include <string> #include <vector> #include "build/build_config.h" @@ -77,6 +78,7 @@ ~Thread() {} ThreadContext context; + std::string name; uint64_t id; WinVMAddress teb_address; WinVMSize teb_size;
diff --git a/third_party/crashpad/crashpad/snapshot/win/process_reader_win_test.cc b/third_party/crashpad/crashpad/snapshot/win/process_reader_win_test.cc index 15a6e2b..2ff9f114 100644 --- a/third_party/crashpad/crashpad/snapshot/win/process_reader_win_test.cc +++ b/third_party/crashpad/crashpad/snapshot/win/process_reader_win_test.cc
@@ -17,9 +17,18 @@ #include <windows.h> #include <string.h> +#include <algorithm> +#include <array> #include <iterator> +#include <set> +#include <string> +#include "base/strings/stringprintf.h" +#include "base/strings/utf_string_conversions.h" +#include "build/build_config.h" +#include "gmock/gmock.h" #include "gtest/gtest.h" +#include "test/scoped_set_thread_name.h" #include "test/win/win_multiprocess.h" #include "util/misc/from_pointer_cast.h" #include "util/synchronization/semaphore.h" @@ -31,6 +40,8 @@ namespace test { namespace { +using ::testing::IsSupersetOf; + TEST(ProcessReaderWin, SelfBasic) { ProcessReaderWin process_reader; ASSERT_TRUE(process_reader.Initialize(GetCurrentProcess(), @@ -98,6 +109,7 @@ } TEST(ProcessReaderWin, SelfOneThread) { + const ScopedSetThreadName scoped_set_thread_name("SelfBasic"); ProcessReaderWin process_reader; ASSERT_TRUE(process_reader.Initialize(GetCurrentProcess(), ProcessSuspensionState::kRunning)); @@ -111,6 +123,9 @@ ASSERT_GE(threads.size(), 1u); EXPECT_EQ(threads[0].id, GetCurrentThreadId()); + if (ScopedSetThreadName::IsSupported()) { + EXPECT_EQ(threads[0].name, "SelfBasic"); + } EXPECT_NE(ProgramCounterFromCONTEXT(threads[0].context.context<CONTEXT>()), nullptr); EXPECT_EQ(threads[0].suspend_count, 0u); @@ -132,18 +147,21 @@ class SleepingThread : public Thread { public: - SleepingThread() : done_(nullptr) {} + explicit SleepingThread(const std::string& thread_name) + : done_(nullptr), thread_name_(thread_name) {} void SetHandle(Semaphore* done) { done_= done; } void ThreadMain() override { + const ScopedSetThreadName scoped_set_thread_name(thread_name_); done_->Wait(); } private: Semaphore* done_; + const std::string thread_name_; }; void WinMultiprocessParent() override { @@ -158,8 +176,31 @@ const auto& threads = process_reader.Threads(); ASSERT_GE(threads.size(), kCreatedThreads + 1); - for (const auto& thread : threads) + + for (const auto& thread : threads) { EXPECT_EQ(thread.suspend_count, 0u); + } + + if (ScopedSetThreadName::IsSupported()) { + EXPECT_EQ(threads[0].name, "WinMultiprocessChild-Main"); + + const std::set<std::string> expected_thread_names = { + "WinMultiprocessChild-1", + "WinMultiprocessChild-2", + "WinMultiprocessChild-3", + }; + // Windows can create threads besides the ones created in + // WinMultiprocessChild(), so keep track of the (non-main) thread names + // and make sure all the expected names are present. + std::set<std::string> thread_names; + for (size_t i = 1; i < threads.size(); i++) { + if (!threads[i].name.empty()) { + thread_names.emplace(threads[i].name); + } + } + + EXPECT_THAT(thread_names, IsSupersetOf(expected_thread_names)); + } } { @@ -173,15 +214,23 @@ // suspended. const auto& threads = process_reader.Threads(); ASSERT_GE(threads.size(), kCreatedThreads + 1); - for (const auto& thread : threads) + for (const auto& thread : threads) { EXPECT_EQ(thread.suspend_count, 0u); + } } } void WinMultiprocessChild() override { + const ScopedSetThreadName scoped_set_thread_name( + "WinMultiprocessChild-Main"); + // Create three dummy threads so we can confirm we read successfully read // more than just the main thread. - SleepingThread threads[kCreatedThreads]; + std::array<SleepingThread, kCreatedThreads> threads = { + SleepingThread(std::string("WinMultiprocessChild-1")), + SleepingThread(std::string("WinMultiprocessChild-2")), + SleepingThread(std::string("WinMultiprocessChild-3")), + }; Semaphore done(0); for (auto& thread : threads) thread.SetHandle(&done);
diff --git a/third_party/crashpad/crashpad/snapshot/win/process_snapshot_win.cc b/third_party/crashpad/crashpad/snapshot/win/process_snapshot_win.cc index f1a20b5..844986db 100644 --- a/third_party/crashpad/crashpad/snapshot/win/process_snapshot_win.cc +++ b/third_party/crashpad/crashpad/snapshot/win/process_snapshot_win.cc
@@ -45,8 +45,7 @@ annotations_simple_map_(), snapshot_time_(), options_(), - initialized_() { -} + initialized_() {} ProcessSnapshotWin::~ProcessSnapshotWin() { }
diff --git a/third_party/crashpad/crashpad/snapshot/win/thread_snapshot_win.cc b/third_party/crashpad/crashpad/snapshot/win/thread_snapshot_win.cc index 2c5569f8..222b2ab 100644 --- a/third_party/crashpad/crashpad/snapshot/win/thread_snapshot_win.cc +++ b/third_party/crashpad/crashpad/snapshot/win/thread_snapshot_win.cc
@@ -165,6 +165,11 @@ return thread_.id; } +std::string ThreadSnapshotWin::ThreadName() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return thread_.name; +} + int ThreadSnapshotWin::SuspendCount() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); return thread_.suspend_count;
diff --git a/third_party/crashpad/crashpad/snapshot/win/thread_snapshot_win.h b/third_party/crashpad/crashpad/snapshot/win/thread_snapshot_win.h index b9fafaa..af18a14 100644 --- a/third_party/crashpad/crashpad/snapshot/win/thread_snapshot_win.h +++ b/third_party/crashpad/crashpad/snapshot/win/thread_snapshot_win.h
@@ -68,6 +68,7 @@ const CPUContext* Context() const override; const MemorySnapshot* Stack() const override; uint64_t ThreadID() const override; + std::string ThreadName() const override; int SuspendCount() const override; int Priority() const override; uint64_t ThreadSpecificDataAddress() const override;
diff --git a/third_party/crashpad/crashpad/test/BUILD.gn b/third_party/crashpad/crashpad/test/BUILD.gn index c2ad5b92..fef0fc7 100644 --- a/third_party/crashpad/crashpad/test/BUILD.gn +++ b/third_party/crashpad/crashpad/test/BUILD.gn
@@ -37,6 +37,7 @@ "scoped_guarded_page.h", "scoped_module_handle.cc", "scoped_module_handle.h", + "scoped_set_thread_name.h", "scoped_temp_dir.cc", "scoped_temp_dir.h", "test_paths.cc", @@ -57,6 +58,14 @@ } } + # TODO(crbug.com/812974): Remove !crashpad_is_fuchsia when Fuchsia is no + # longer treated as a posix platform. + if (crashpad_is_posix && !crashpad_is_fuchsia) { + sources += [ + "scoped_set_thread_name_posix.cc", + ] + } + if (crashpad_is_mac || crashpad_is_ios) { sources += [ "mac/mach_errors.cc", @@ -96,6 +105,7 @@ sources += [ "multiprocess_exec_win.cc", "scoped_guarded_page_win.cc", + "scoped_set_thread_name_win.cc", "scoped_temp_dir_win.cc", "win/child_launcher.cc", "win/child_launcher.h", @@ -109,7 +119,10 @@ } if (crashpad_is_fuchsia) { - sources += [ "multiprocess_exec_fuchsia.cc" ] + sources += [ + "multiprocess_exec_fuchsia.cc", + "scoped_set_thread_name_fuchsia.cc", + ] } public_configs = [ "..:crashpad_config" ]
diff --git a/third_party/crashpad/crashpad/test/scoped_set_thread_name.h b/third_party/crashpad/crashpad/test/scoped_set_thread_name.h new file mode 100644 index 0000000..dc149e4 --- /dev/null +++ b/third_party/crashpad/crashpad/test/scoped_set_thread_name.h
@@ -0,0 +1,51 @@ +// Copyright 2022 The Crashpad Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef CRASHPAD_TEST_SCOPED_SET_THREAD_NAME_H_ +#define CRASHPAD_TEST_SCOPED_SET_THREAD_NAME_H_ + +#include <string> + +#include "build/build_config.h" + +namespace crashpad { +namespace test { + +//! Sets the name of the current thread for the lifetime of this object. +class ScopedSetThreadName final { + public: + explicit ScopedSetThreadName(const std::string& new_thread_name); + + ScopedSetThreadName(const ScopedSetThreadName&) = delete; + ScopedSetThreadName& operator=(const ScopedSetThreadName&) = delete; + + ~ScopedSetThreadName(); + +#if BUILDFLAG(IS_WIN) || DOXYGEN + //! \brief Returns `true` if Windows supports setting and getting thread name. + static bool IsSupported(); +#endif + + private: +#if BUILDFLAG(IS_WIN) + std::wstring original_name_; +#else + const std::string original_name_; +#endif +}; + +} // namespace test +} // namespace crashpad + +#endif // CRASHPAD_TEST_SCOPED_SET_THREAD_NAME_H_
diff --git a/third_party/crashpad/crashpad/test/scoped_set_thread_name_fuchsia.cc b/third_party/crashpad/crashpad/test/scoped_set_thread_name_fuchsia.cc new file mode 100644 index 0000000..7b672d15 --- /dev/null +++ b/third_party/crashpad/crashpad/test/scoped_set_thread_name_fuchsia.cc
@@ -0,0 +1,61 @@ +// Copyright 2022 The Crashpad Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "test/scoped_set_thread_name.h" + +#include <string> + +#include <lib/zx/thread.h> +#include <zircon/syscalls/object.h> +#include <zircon/types.h> + +#include "base/check_op.h" +#include "base/fuchsia/fuchsia_logging.h" + +namespace crashpad { +namespace test { + +namespace { + +std::string GetCurrentThreadName() { + std::string result(ZX_MAX_NAME_LEN, '\0'); + const zx_status_t status = zx::thread::self()->get_property( + ZX_PROP_NAME, result.data(), result.length()); + ZX_CHECK(status == ZX_OK, status) << "get_property(ZX_PROP_NAME)"; + const auto result_nul_idx = result.find('\0'); + CHECK_NE(result_nul_idx, std::string::npos) + << "get_property() did not NUL terminate"; + result.resize(result_nul_idx); + return result; +} + +} // namespace + +ScopedSetThreadName::ScopedSetThreadName(const std::string& new_thread_name) + : original_name_(GetCurrentThreadName()) { + // Fuchsia silently truncates the thread name if it's too long. + CHECK_LT(new_thread_name.length(), ZX_MAX_NAME_LEN); + const zx_status_t status = zx::thread::self()->set_property( + ZX_PROP_NAME, new_thread_name.c_str(), new_thread_name.length()); + ZX_CHECK(status == ZX_OK, status) << "set_property(ZX_PROP_NAME)"; +} + +ScopedSetThreadName::~ScopedSetThreadName() { + const zx_status_t status = zx::thread::self()->set_property( + ZX_PROP_NAME, original_name_.c_str(), original_name_.length()); + ZX_CHECK(status == ZX_OK, status) << "set_property(ZX_PROP_NAME)"; +} + +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/test/scoped_set_thread_name_posix.cc b/third_party/crashpad/crashpad/test/scoped_set_thread_name_posix.cc new file mode 100644 index 0000000..92c61bb --- /dev/null +++ b/third_party/crashpad/crashpad/test/scoped_set_thread_name_posix.cc
@@ -0,0 +1,92 @@ +// Copyright 2022 The Crashpad Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "test/scoped_set_thread_name.h" + +#include <errno.h> +#include <pthread.h> + +#include <ostream> +#include <string> + +#include "base/check.h" +#include "base/check_op.h" +#include "build/build_config.h" + +#if BUILDFLAG(IS_APPLE) +#include <mach/thread_info.h> +#elif BUILDFLAG(IS_ANDROID) +#include <sys/prctl.h> +#endif + +namespace crashpad { +namespace test { + +namespace { + +#if BUILDFLAG(IS_APPLE) +constexpr size_t kPthreadNameMaxLen = MAXTHREADNAMESIZE; +#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_CHROMEOS) +// The kernel headers define this in linux/sched.h as TASK_COMM_LEN, but the +// userspace copy of that header does not define it. +constexpr size_t kPthreadNameMaxLen = 16; +#else +#error Port to your platform +#endif + +void SetCurrentThreadName(const std::string& thread_name) { +#if BUILDFLAG(IS_APPLE) + // Apple's pthread_setname_np() sets errno instead of returning it. + PCHECK(pthread_setname_np(thread_name.c_str()) == 0) << "pthread_setname_np"; +#elif BUILDFLAG(IS_ANDROID) && __ANDROID_API__ < 24 + // pthread_setname_np() requires Android API 24 or later. + CHECK_LT(thread_name.length(), kPthreadNameMaxLen); + PCHECK(prctl(PR_SET_NAME, thread_name.c_str()) == 0) << "prctl(PR_SET_NAME)"; +#else + PCHECK((errno = pthread_setname_np(pthread_self(), thread_name.c_str())) == 0) + << "pthread_setname_np"; +#endif +} + +std::string GetCurrentThreadName() { + std::string result(kPthreadNameMaxLen, '\0'); +#if BUILDFLAG(IS_ANDROID) && __ANDROID_API__ < 24 + static constexpr char kGetThreadNameFunctionName[] = "prctl"; + PCHECK(prctl(PR_GET_NAME, result.data()) == 0) << "prctl(PR_GET_NAME)"; +#else + static constexpr char kGetThreadNameFunctionName[] = "pthread_getname_np"; + PCHECK((errno = pthread_getname_np( + pthread_self(), result.data(), result.length())) == 0) + << "pthread_getname_np"; +#endif + const auto result_nul_idx = result.find('\0'); + CHECK(result_nul_idx != std::string::npos) + << kGetThreadNameFunctionName << " did not NUL terminate"; + result.resize(result_nul_idx); + return result; +} + +} // namespace + +ScopedSetThreadName::ScopedSetThreadName(const std::string& new_thread_name) + : original_name_(GetCurrentThreadName()) { + SetCurrentThreadName(new_thread_name); +} + +ScopedSetThreadName::~ScopedSetThreadName() { + SetCurrentThreadName(original_name_); +} + +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/test/scoped_set_thread_name_win.cc b/third_party/crashpad/crashpad/test/scoped_set_thread_name_win.cc new file mode 100644 index 0000000..37c87ee8 --- /dev/null +++ b/third_party/crashpad/crashpad/test/scoped_set_thread_name_win.cc
@@ -0,0 +1,84 @@ +// Copyright 2022 The Crashpad Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "test/scoped_set_thread_name.h" + +#include <windows.h> + +#include "base/check.h" +#include "base/logging.h" +#include "base/strings/utf_string_conversions.h" +#include "util/win/get_function.h" +#include "util/win/scoped_local_alloc.h" + +namespace crashpad { +namespace test { + +namespace { + +auto GetThreadDescriptionFuncPtr() { + static const auto get_thread_description = + GET_FUNCTION(L"kernel32.dll", ::GetThreadDescription); + return get_thread_description; +} + +auto SetThreadDescriptionFuncPtr() { + static const auto set_thread_description = + GET_FUNCTION(L"kernel32.dll", ::SetThreadDescription); + return set_thread_description; +} + +std::wstring GetCurrentThreadName() { + wchar_t* thread_description; + const auto get_thread_description = GetThreadDescriptionFuncPtr(); + DCHECK(get_thread_description); + HRESULT hr = get_thread_description(GetCurrentThread(), &thread_description); + CHECK(SUCCEEDED(hr)) << "GetThreadDescription: " + << logging::SystemErrorCodeToString(hr); + ScopedLocalAlloc thread_description_owner(thread_description); + return std::wstring(thread_description); +} + +void SetCurrentThreadName(const std::wstring& new_thread_name) { + const auto set_thread_description = SetThreadDescriptionFuncPtr(); + DCHECK(set_thread_description); + HRESULT hr = + set_thread_description(GetCurrentThread(), new_thread_name.c_str()); + CHECK(SUCCEEDED(hr)) << "SetThreadDescription: " + << logging::SystemErrorCodeToString(hr); +} + +} // namespace + +ScopedSetThreadName::ScopedSetThreadName(const std::string& new_thread_name) + : original_name_() { + if (IsSupported()) { + original_name_.assign(GetCurrentThreadName()); + SetCurrentThreadName(base::UTF8ToWide(new_thread_name)); + } +} + +ScopedSetThreadName::~ScopedSetThreadName() { + if (IsSupported()) { + SetCurrentThreadName(original_name_); + } +} + +// static +bool ScopedSetThreadName::IsSupported() { + return GetThreadDescriptionFuncPtr() && SetThreadDescriptionFuncPtr(); +} + +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/BUILD.gn b/third_party/crashpad/crashpad/util/BUILD.gn index b8daef48b..6cec366 100644 --- a/third_party/crashpad/crashpad/util/BUILD.gn +++ b/third_party/crashpad/crashpad/util/BUILD.gn
@@ -296,11 +296,11 @@ sources += [ "posix/close_multiple.cc", "posix/close_multiple.h", - "posix/double_fork_and_exec.cc", - "posix/double_fork_and_exec.h", "posix/drop_privileges.cc", "posix/drop_privileges.h", "posix/process_info.h", + "posix/spawn_subprocess.cc", + "posix/spawn_subprocess.h", # These map signals to and from strings. While Fuchsia defines some of # the common SIGx defines, signals are never raised on Fuchsia, so
diff --git a/third_party/crashpad/crashpad/util/ios/ios_intermediate_dump_format.h b/third_party/crashpad/crashpad/util/ios/ios_intermediate_dump_format.h index 5e36862..ea206430dd 100644 --- a/third_party/crashpad/crashpad/util/ios/ios_intermediate_dump_format.h +++ b/third_party/crashpad/crashpad/util/ios/ios_intermediate_dump_format.h
@@ -99,6 +99,7 @@ TD(kThreadContextMemoryRegions, 6011) \ TD(kThreadContextMemoryRegionAddress, 6012) \ TD(kThreadContextMemoryRegionData, 6013) \ + TD(kThreadName, 6014) \ TD(kMaxValue, 65535) \ // clang-format on
diff --git a/third_party/crashpad/crashpad/util/posix/double_fork_and_exec.cc b/third_party/crashpad/crashpad/util/posix/double_fork_and_exec.cc deleted file mode 100644 index 1960430..0000000 --- a/third_party/crashpad/crashpad/util/posix/double_fork_and_exec.cc +++ /dev/null
@@ -1,166 +0,0 @@ -// Copyright 2017 The Crashpad Authors. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "util/posix/double_fork_and_exec.h" - -#include <stdlib.h> -#include <string.h> -#include <sys/wait.h> -#include <unistd.h> - -#include "base/check_op.h" -#include "base/logging.h" -#include "base/posix/eintr_wrapper.h" -#include "base/strings/stringprintf.h" -#include "util/posix/close_multiple.h" - -namespace crashpad { - -bool DoubleForkAndExec(const std::vector<std::string>& argv, - const std::vector<std::string>* envp, - int preserve_fd, - bool use_path, - void (*child_function)()) { - DCHECK(!envp || !use_path); - - // argv_c contains const char* pointers and is terminated by nullptr. This is - // suitable for passing to execv(). Although argv_c is not used in the parent - // process, it must be built in the parent process because it’s unsafe to do - // so in the child or grandchild process. - std::vector<const char*> argv_c; - argv_c.reserve(argv.size() + 1); - for (const std::string& argument : argv) { - argv_c.push_back(argument.c_str()); - } - argv_c.push_back(nullptr); - - std::vector<const char*> envp_c; - if (envp) { - envp_c.reserve(envp->size() + 1); - for (const std::string& variable : *envp) { - envp_c.push_back(variable.c_str()); - } - envp_c.push_back(nullptr); - } - - // Double-fork(). The three processes involved are parent, child, and - // grandchild. The grandchild will call execv(). The child exits immediately - // after spawning the grandchild, so the grandchild becomes an orphan and its - // parent process ID becomes 1. This relieves the parent and child of the - // responsibility to reap the grandchild with waitpid() or similar. The - // grandchild is expected to outlive the parent process, so the parent - // shouldn’t be concerned with reaping it. This approach means that accidental - // early termination of the handler process will not result in a zombie - // process. - pid_t pid = fork(); - if (pid < 0) { - PLOG(ERROR) << "fork"; - return false; - } - - if (pid == 0) { - // Child process. - - if (child_function) { - child_function(); - } - - // Call setsid(), creating a new process group and a new session, both led - // by this process. The new process group has no controlling terminal. This - // disconnects it from signals generated by the parent process’ terminal. - // - // setsid() is done in the child instead of the grandchild so that the - // grandchild will not be a session leader. If it were a session leader, an - // accidental open() of a terminal device without O_NOCTTY would make that - // terminal the controlling terminal. - // - // It’s not desirable for the grandchild to have a controlling terminal. The - // grandchild manages its own lifetime, such as by monitoring clients on its - // own and exiting when it loses all clients and when it deems it - // appropraite to do so. It may serve clients in different process groups or - // sessions than its original client, and receiving signals intended for its - // original client’s process group could be harmful in that case. - PCHECK(setsid() != -1) << "setsid"; - - pid = fork(); - if (pid < 0) { - PLOG(FATAL) << "fork"; - } - - if (pid > 0) { - // Child process. - - // _exit() instead of exit(), because fork() was called. - _exit(EXIT_SUCCESS); - } - - // Grandchild process. - - CloseMultipleNowOrOnExec(STDERR_FILENO + 1, preserve_fd); - - // &argv_c[0] is a pointer to a pointer to const char data, but because of - // how C (not C++) works, execvp() wants a pointer to a const pointer to - // char data. It modifies neither the data nor the pointers, so the - // const_cast is safe. - char* const* argv_for_execv = const_cast<char* const*>(&argv_c[0]); - - if (envp) { - // This cast is safe for the same reason that the argv_for_execv cast is. - char* const* envp_for_execv = const_cast<char* const*>(&envp_c[0]); - execve(argv_for_execv[0], argv_for_execv, envp_for_execv); - PLOG(FATAL) << "execve " << argv_for_execv[0]; - } - - if (use_path) { - execvp(argv_for_execv[0], argv_for_execv); - PLOG(FATAL) << "execvp " << argv_for_execv[0]; - } - - execv(argv_for_execv[0], argv_for_execv); - PLOG(FATAL) << "execv " << argv_for_execv[0]; - } - - // waitpid() for the child, so that it does not become a zombie process. The - // child normally exits quickly. - // - // Failures from this point on may result in the accumulation of a zombie, but - // should not be considered fatal. Log only warnings, but don’t treat these - // failures as a failure of the function overall. - int status; - pid_t wait_pid = HANDLE_EINTR(waitpid(pid, &status, 0)); - if (wait_pid == -1) { - PLOG(WARNING) << "waitpid"; - return true; - } - DCHECK_EQ(wait_pid, pid); - - if (WIFSIGNALED(status)) { - int sig = WTERMSIG(status); - LOG(WARNING) << base::StringPrintf( - "intermediate process terminated by signal %d (%s)%s", - sig, - strsignal(sig), - WCOREDUMP(status) ? " (core dumped)" : ""); - } else if (!WIFEXITED(status)) { - LOG(WARNING) << base::StringPrintf( - "intermediate process: unknown termination 0x%x", status); - } else if (WEXITSTATUS(status) != EXIT_SUCCESS) { - LOG(WARNING) << "intermediate process exited with code " - << WEXITSTATUS(status); - } - - return true; -} - -} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/posix/double_fork_and_exec.h b/third_party/crashpad/crashpad/util/posix/double_fork_and_exec.h deleted file mode 100644 index 02fc0f2..0000000 --- a/third_party/crashpad/crashpad/util/posix/double_fork_and_exec.h +++ /dev/null
@@ -1,74 +0,0 @@ -// Copyright 2017 The Crashpad Authors. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef CRASHPAD_UTIL_POSIX_DOUBLE_FORK_AND_EXEC_H_ -#define CRASHPAD_UTIL_POSIX_DOUBLE_FORK_AND_EXEC_H_ - -#include <string> -#include <vector> - -namespace crashpad { - -//! \brief Executes a (grand-)child process. -//! -//! The grandchild process will be started through the -//! double-`fork()`-and-`execv()` pattern. This allows the grandchild to fully -//! disassociate from the parent. The grandchild will not be a member of the -//! parent’s process group or session and will not have a controlling terminal, -//! providing isolation from signals not intended for it. The grandchild’s -//! parent process, in terms of the process tree hierarchy, will be the process -//! with process ID 1, relieving any other process of the responsibility to reap -//! it via `waitpid()`. Aside from the three file descriptors associated with -//! the standard input/output streams and any file descriptor passed in \a -//! preserve_fd, the grandchild will not inherit any file descriptors from the -//! parent process. -//! -//! \param[in] argv The argument vector to start the grandchild process with. -//! `argv[0]` is used as the path to the executable. -//! \param[in] envp A vector of environment variables of the form `var=value` to -//! be passed to `execve()`. If this value is `nullptr`, the current -//! environment is used. -//! \param[in] preserve_fd A file descriptor to be inherited by the grandchild -//! process. This file descriptor is inherited in addition to the three file -//! descriptors associated with the standard input/output streams. Use `-1` -//! if no additional file descriptors are to be inherited. -//! \param[in] use_path Whether to consult the `PATH` environment variable when -//! requested to start an executable at a non-absolute path. If `false`, -//! `execv()`, which does not consult `PATH`, will be used. If `true`, -//! `execvp()`, which does consult `PATH`, will be used. -//! \param[in] child_function If not `nullptr`, this function will be called in -//! the intermediate child process, prior to the second `fork()`. Take note -//! that this function will run in the context of a forked process, and must -//! be safe for that purpose. -//! -//! Setting both \a envp to a value other than `nullptr` and \a use_path to -//! `true` is not currently supported. -//! -//! \return `true` on success, and `false` on failure with a message logged. -//! Only failures that occur in the parent process that indicate a definite -//! failure to start the the grandchild are reported in the return value. -//! Failures in the intermediate child or grandchild processes cannot be -//! reported in the return value, and are addressed by logging a message and -//! terminating. The caller assumes the responsibility for detecting such -//! failures, for example, by observing a failure to perform a successful -//! handshake with the grandchild process. -bool DoubleForkAndExec(const std::vector<std::string>& argv, - const std::vector<std::string>* envp, - int preserve_fd, - bool use_path, - void (*child_function)()); - -} // namespace crashpad - -#endif // CRASHPAD_UTIL_POSIX_DOUBLE_FORK_AND_EXEC_H_
diff --git a/third_party/crashpad/crashpad/util/posix/signals.cc b/third_party/crashpad/crashpad/util/posix/signals.cc index f53ceb2..93874e5 100644 --- a/third_party/crashpad/crashpad/util/posix/signals.cc +++ b/third_party/crashpad/crashpad/util/posix/signals.cc
@@ -147,6 +147,25 @@ PLOG(ERROR) << "sigaction " << sig; return false; } + +// Sanitizers can prevent the installation of signal handlers, but sigaction +// does not report this as failure. Attempt to detect this by checking the +// currently installed signal handler. +#if defined(ADDRESS_SANITIZER) || defined(MEMORY_SANITIZER) || \ + defined(THREAD_SANITIZER) || defined(LEAK_SANITIZER) || \ + defined(UNDEFINED_SANITIZER) + struct sigaction installed_handler; + CHECK_EQ(sigaction(sig, nullptr, &installed_handler), 0); + // If the installed handler does not point to the just installed handler, then + // the allow_user_segv_handler sanitizer flag is (probably) disabled. + if (installed_handler.sa_sigaction != handler) { + LOG(WARNING) + << "sanitizers are preventing signal handler installation (sig " << sig + << ")"; + return false; + } +#endif + return true; }
diff --git a/third_party/crashpad/crashpad/util/posix/spawn_subprocess.cc b/third_party/crashpad/crashpad/util/posix/spawn_subprocess.cc new file mode 100644 index 0000000..43fca18 --- /dev/null +++ b/third_party/crashpad/crashpad/util/posix/spawn_subprocess.cc
@@ -0,0 +1,261 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "util/posix/spawn_subprocess.h" + +#include <errno.h> +#include <spawn.h> +#include <stdlib.h> +#include <string.h> +#include <sys/wait.h> +#include <unistd.h> + +#include "base/check.h" +#include "base/check_op.h" +#include "base/logging.h" +#include "base/posix/eintr_wrapper.h" +#include "base/strings/stringprintf.h" +#include "build/build_config.h" +#include "util/posix/close_multiple.h" + +#if BUILDFLAG(IS_ANDROID) +#include <android/api-level.h> +#endif + +extern char** environ; + +namespace crashpad { + +namespace { + +#if BUILDFLAG(IS_APPLE) + +class PosixSpawnAttr { + public: + PosixSpawnAttr() { + PCHECK((errno = posix_spawnattr_init(&attr_)) == 0) + << "posix_spawnattr_init"; + } + + PosixSpawnAttr(const PosixSpawnAttr&) = delete; + PosixSpawnAttr& operator=(const PosixSpawnAttr&) = delete; + + ~PosixSpawnAttr() { + PCHECK((errno = posix_spawnattr_destroy(&attr_)) == 0) + << "posix_spawnattr_destroy"; + } + + void SetFlags(short flags) { + PCHECK((errno = posix_spawnattr_setflags(&attr_, flags)) == 0) + << "posix_spawnattr_setflags"; + } + + const posix_spawnattr_t* Get() const { return &attr_; } + + private: + posix_spawnattr_t attr_; +}; + +class PosixSpawnFileActions { + public: + PosixSpawnFileActions() { + PCHECK((errno = posix_spawn_file_actions_init(&file_actions_)) == 0) + << "posix_spawn_file_actions_init"; + } + + PosixSpawnFileActions(const PosixSpawnFileActions&) = delete; + PosixSpawnFileActions& operator=(const PosixSpawnFileActions&) = delete; + + ~PosixSpawnFileActions() { + PCHECK((errno = posix_spawn_file_actions_destroy(&file_actions_)) == 0) + << "posix_spawn_file_actions_destroy"; + } + + void AddInheritedFileDescriptor(int fd) { + PCHECK((errno = posix_spawn_file_actions_addinherit_np(&file_actions_, + fd)) == 0) + << "posix_spawn_file_actions_addinherit_np"; + } + + const posix_spawn_file_actions_t* Get() const { return &file_actions_; } + + private: + posix_spawn_file_actions_t file_actions_; +}; + +#endif + +} // namespace + +bool SpawnSubprocess(const std::vector<std::string>& argv, + const std::vector<std::string>* envp, + int preserve_fd, + bool use_path, + void (*child_function)()) { + // argv_c contains const char* pointers and is terminated by nullptr. This is + // suitable for passing to posix_spawn*() and execv*(). Although argv_c is not + // used in the parent process, it must be built in the parent process because + // it’s unsafe to do so in the child or grandchild process. + std::vector<const char*> argv_c; + argv_c.reserve(argv.size() + 1); + for (const std::string& argument : argv) { + argv_c.push_back(argument.c_str()); + } + argv_c.push_back(nullptr); + + std::vector<const char*> envp_c; + if (envp) { + envp_c.reserve(envp->size() + 1); + for (const std::string& variable : *envp) { + envp_c.push_back(variable.c_str()); + } + envp_c.push_back(nullptr); + } + + // The three processes involved are parent, child, and grandchild. The child + // exits immediately after spawning the grandchild, so the grandchild becomes + // an orphan and its parent process ID becomes 1. This relieves the parent and + // child of the responsibility to reap the grandchild with waitpid() or + // similar. The grandchild is expected to outlive the parent process, so the + // parent shouldn’t be concerned with reaping it. This approach means that + // accidental early termination of the handler process will not result in a + // zombie process. + pid_t pid = fork(); + if (pid < 0) { + PLOG(ERROR) << "fork"; + return false; + } + + if (pid == 0) { + // Child process. + + if (child_function) { + child_function(); + } + + // Call setsid(), creating a new process group and a new session, both led + // by this process. The new process group has no controlling terminal. This + // disconnects it from signals generated by the parent process’ terminal. + // + // setsid() is done in the child instead of the grandchild so that the + // grandchild will not be a session leader. If it were a session leader, an + // accidental open() of a terminal device without O_NOCTTY would make that + // terminal the controlling terminal. + // + // It’s not desirable for the grandchild to have a controlling terminal. The + // grandchild manages its own lifetime, such as by monitoring clients on its + // own and exiting when it loses all clients and when it deems it + // appropraite to do so. It may serve clients in different process groups or + // sessions than its original client, and receiving signals intended for its + // original client’s process group could be harmful in that case. + PCHECK(setsid() != -1) << "setsid"; + + // &argv_c[0] is a pointer to a pointer to const char data, but because of + // how C (not C++) works, posix_spawn*() and execv*() want a pointer to + // a const pointer to char data. They modify neither the data nor the + // pointers, so the const_cast is safe. + char* const* argv_for_spawn = const_cast<char* const*>(argv_c.data()); + + // This cast is safe for the same reason that the argv_for_spawn cast is. + char* const* envp_for_spawn = + envp ? const_cast<char* const*>(envp_c.data()) : environ; + +#if BUILDFLAG(IS_ANDROID) && __ANDROID_API__ < 28 + pid = fork(); + if (pid < 0) { + PLOG(FATAL) << "fork"; + } + + if (pid > 0) { + // Child process. + + // _exit() instead of exit(), because fork() was called. + _exit(EXIT_SUCCESS); + } + + // Grandchild process. + + CloseMultipleNowOrOnExec(STDERR_FILENO + 1, preserve_fd); + + auto execve_fp = use_path ? execvpe : execve; + execve_fp(argv_for_spawn[0], argv_for_spawn, envp_for_spawn); + PLOG(FATAL) << (use_path ? "execvpe" : "execve"); +#else +#if BUILDFLAG(IS_APPLE) + PosixSpawnAttr attr; + attr.SetFlags(POSIX_SPAWN_CLOEXEC_DEFAULT); + + PosixSpawnFileActions file_actions; + for (int fd = 0; fd <= STDERR_FILENO; ++fd) { + file_actions.AddInheritedFileDescriptor(fd); + } + file_actions.AddInheritedFileDescriptor(preserve_fd); + + const posix_spawnattr_t* attr_p = attr.Get(); + const posix_spawn_file_actions_t* file_actions_p = file_actions.Get(); +#else + CloseMultipleNowOrOnExec(STDERR_FILENO + 1, preserve_fd); + + const posix_spawnattr_t* attr_p = nullptr; + const posix_spawn_file_actions_t* file_actions_p = nullptr; +#endif + + auto posix_spawn_fp = use_path ? posix_spawnp : posix_spawn; + if ((errno = posix_spawn_fp(nullptr, + argv_for_spawn[0], + file_actions_p, + attr_p, + argv_for_spawn, + envp_for_spawn)) != 0) { + PLOG(FATAL) << (use_path ? "posix_spawnp" : "posix_spawn"); + } + + // _exit() instead of exit(), because fork() was called. + _exit(EXIT_SUCCESS); +#endif + } + + // waitpid() for the child, so that it does not become a zombie process. The + // child normally exits quickly. + // + // Failures from this point on may result in the accumulation of a zombie, but + // should not be considered fatal. Log only warnings, but don’t treat these + // failures as a failure of the function overall. + int status; + pid_t wait_pid = HANDLE_EINTR(waitpid(pid, &status, 0)); + if (wait_pid == -1) { + PLOG(WARNING) << "waitpid"; + return true; + } + DCHECK_EQ(wait_pid, pid); + + if (WIFSIGNALED(status)) { + int sig = WTERMSIG(status); + LOG(WARNING) << base::StringPrintf( + "intermediate process terminated by signal %d (%s)%s", + sig, + strsignal(sig), + WCOREDUMP(status) ? " (core dumped)" : ""); + } else if (!WIFEXITED(status)) { + LOG(WARNING) << base::StringPrintf( + "intermediate process: unknown termination 0x%x", status); + } else if (WEXITSTATUS(status) != EXIT_SUCCESS) { + LOG(WARNING) << "intermediate process exited with code " + << WEXITSTATUS(status); + } + + return true; +} + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/posix/spawn_subprocess.h b/third_party/crashpad/crashpad/util/posix/spawn_subprocess.h new file mode 100644 index 0000000..23910810 --- /dev/null +++ b/third_party/crashpad/crashpad/util/posix/spawn_subprocess.h
@@ -0,0 +1,69 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef CRASHPAD_UTIL_POSIX_SPAWN_SUBPROCESS_H_ +#define CRASHPAD_UTIL_POSIX_SPAWN_SUBPROCESS_H_ + +#include <string> +#include <vector> + +namespace crashpad { + +//! \brief Spawns a subprocess. +//! +//! A grandchild process will be started through the +//! `fork()`-and-`posix_spawn()` pattern where supported, and +//! double-`fork()`-and-`execv()` pattern elsewhere. This allows the grandchild +//! to fully disassociate from the parent. The grandchild will not be a member +//! of the parent’s process group or session and will not have a controlling +//! terminal, providing isolation from signals not intended for it. The +//! grandchild’s parent process, in terms of the process tree hierarchy, will be +//! the process with process ID 1, relieving any other process of the +//! responsibility to reap it via `waitpid()`. Aside from the three file +//! descriptors associated with the standard input/output streams and any file +//! descriptor passed in \a preserve_fd, the grandchild will not inherit any +//! file descriptors from the parent process. +//! +//! \param[in] argv The argument vector to start the grandchild process with. +//! `argv[0]` is used as the path to the executable. +//! \param[in] envp A vector of environment variables of the form `var=value` to +//! be passed to the spawned process. If this value is `nullptr`, the +//! current environment is used. +//! \param[in] preserve_fd A file descriptor to be inherited by the grandchild +//! process. This file descriptor is inherited in addition to the three file +//! descriptors associated with the standard input/output streams. Use `-1` +//! if no additional file descriptors are to be inherited. +//! \param[in] use_path Whether to consult the `PATH` environment variable when +//! requested to start an executable at a non-absolute path. +//! \param[in] child_function If not `nullptr`, this function will be called in +//! the intermediate child process. Take note that this function will run in +//! the context of a forked process, and must be safe for that purpose. +//! +//! \return `true` on success, and `false` on failure with a message logged. +//! Only failures that occur in the parent process that indicate a definite +//! failure to start the the grandchild are reported in the return value. +//! Failures in the intermediate child or grandchild processes cannot be +//! reported in the return value, and are addressed by logging a message and +//! terminating. The caller assumes the responsibility for detecting such +//! failures, for example, by observing a failure to perform a successful +//! handshake with the grandchild process. +bool SpawnSubprocess(const std::vector<std::string>& argv, + const std::vector<std::string>* envp, + int preserve_fd, + bool use_path, + void (*child_function)()); + +} // namespace crashpad + +#endif // CRASHPAD_UTIL_POSIX_SPAWN_SUBPROCESS_H_
diff --git a/third_party/lzma_sdk/7z.h b/third_party/lzma_sdk/7z.h index 6c7886e..304f75ff 100644 --- a/third_party/lzma_sdk/7z.h +++ b/third_party/lzma_sdk/7z.h
@@ -1,5 +1,5 @@ /* 7z.h -- 7z interface -2017-04-03 : Igor Pavlov : Public domain */ +2018-07-02 : Igor Pavlov : Public domain */ #ifndef __7Z_H #define __7Z_H @@ -91,6 +91,8 @@ UInt64 *CoderUnpackSizes; // for all coders in all folders Byte *CodersData; + + UInt64 RangeLimit; } CSzAr; UInt64 SzAr_GetFolderUnpackSize(const CSzAr *p, UInt32 folderIndex);
diff --git a/third_party/lzma_sdk/7zArcIn.c b/third_party/lzma_sdk/7zArcIn.c index f74d0fad..0d9dec4 100644 --- a/third_party/lzma_sdk/7zArcIn.c +++ b/third_party/lzma_sdk/7zArcIn.c
@@ -1,5 +1,5 @@ /* 7zArcIn.c -- 7z Input functions -2018-12-31 : Igor Pavlov : Public domain */ +2021-02-09 : Igor Pavlov : Public domain */ #include "Precomp.h" @@ -75,7 +75,7 @@ return SZ_OK; } -void SzBitUi32s_Free(CSzBitUi32s *p, ISzAllocPtr alloc) +static void SzBitUi32s_Free(CSzBitUi32s *p, ISzAllocPtr alloc) { ISzAlloc_Free(alloc, p->Defs); p->Defs = NULL; ISzAlloc_Free(alloc, p->Vals); p->Vals = NULL; @@ -83,7 +83,7 @@ #define SzBitUi64s_Init(p) { (p)->Defs = NULL; (p)->Vals = NULL; } -void SzBitUi64s_Free(CSzBitUi64s *p, ISzAllocPtr alloc) +static void SzBitUi64s_Free(CSzBitUi64s *p, ISzAllocPtr alloc) { ISzAlloc_Free(alloc, p->Defs); p->Defs = NULL; ISzAlloc_Free(alloc, p->Vals); p->Vals = NULL; @@ -105,6 +105,8 @@ p->CoderUnpackSizes = NULL; p->CodersData = NULL; + + p->RangeLimit = 0; } static void SzAr_Free(CSzAr *p, ISzAllocPtr alloc) @@ -502,7 +504,7 @@ return SZ_ERROR_ARCHIVE; if (propsSize >= 0x80) return SZ_ERROR_UNSUPPORTED; - coder->PropsOffset = sd->Data - dataStart; + coder->PropsOffset = (size_t)(sd->Data - dataStart); coder->PropsSize = (Byte)propsSize; sd->Data += (size_t)propsSize; sd->Size -= (size_t)propsSize; @@ -677,7 +679,7 @@ { UInt32 numCoders, ci, numInStreams = 0; - p->FoCodersOffsets[fo] = sd.Data - startBufPtr; + p->FoCodersOffsets[fo] = (size_t)(sd.Data - startBufPtr); RINOK(SzReadNumber32(&sd, &numCoders)); if (numCoders == 0 || numCoders > k_Scan_NumCoders_MAX) @@ -797,7 +799,7 @@ p->FoToCoderUnpackSizes[fo] = numCodersOutStreams; { - size_t dataSize = sd.Data - startBufPtr; + const size_t dataSize = (size_t)(sd.Data - startBufPtr); p->FoStartPackStreamIndex[fo] = packStreamIndex; p->FoCodersOffsets[fo] = dataSize; MY_ALLOC_ZE_AND_CPY(p->CodersData, dataSize, startBufPtr, alloc); @@ -885,7 +887,7 @@ if (numStreams != 1 || !SzBitWithVals_Check(&p->FolderCRCs, i)) numSubDigests += numStreams; } - ssi->sdNumSubStreams.Size = sd->Data - ssi->sdNumSubStreams.Data; + ssi->sdNumSubStreams.Size = (size_t)(sd->Data - ssi->sdNumSubStreams.Data); continue; } if (type == k7zIdCRC || type == k7zIdSize || type == k7zIdEnd) @@ -907,7 +909,7 @@ { ssi->sdSizes.Data = sd->Data; RINOK(SkipNumbers(sd, numUnpackSizesInData)); - ssi->sdSizes.Size = sd->Data - ssi->sdSizes.Data; + ssi->sdSizes.Size = (size_t)(sd->Data - ssi->sdSizes.Data); RINOK(ReadID(sd, &type)); } @@ -919,7 +921,7 @@ { ssi->sdCRCs.Data = sd->Data; RINOK(SkipBitUi32s(sd, numSubDigests)); - ssi->sdCRCs.Size = sd->Data - ssi->sdCRCs.Data; + ssi->sdCRCs.Size = (size_t)(sd->Data - ssi->sdCRCs.Data); } else { @@ -947,7 +949,11 @@ if (type == k7zIdPackInfo) { RINOK(ReadNumber(sd, dataOffset)); + if (*dataOffset > p->RangeLimit) + return SZ_ERROR_ARCHIVE; RINOK(ReadPackInfo(p, sd, alloc)); + if (p->PackPositions[p->NumPackStreams] > p->RangeLimit - *dataOffset) + return SZ_ERROR_ARCHIVE; RINOK(ReadID(sd, &type)); } if (type == k7zIdUnpackInfo) @@ -1028,12 +1034,12 @@ return SZ_ERROR_ARCHIVE; for (p = data + pos; #ifdef _WIN32 - *(const UInt16 *)p != 0 + *(const UInt16 *)(const void *)p != 0 #else p[0] != 0 || p[1] != 0 #endif ; p += 2); - pos = p - data + 2; + pos = (size_t)(p - data) + 2; *offsets++ = (pos >> 1); } while (--numFiles); @@ -1133,6 +1139,8 @@ SRes res; SzAr_Init(&tempAr); + tempAr.RangeLimit = p->db.RangeLimit; + res = SzReadAndDecodePackedStreams(inStream, sd, tempBufs, NUM_ADDITIONAL_STREAMS_MAX, p->startPosAfterHeader, &tempAr, allocTemp); *numTempBufs = tempAr.NumFolders; @@ -1526,11 +1534,13 @@ nextHeaderSize = GetUi64(header + 20); nextHeaderCRC = GetUi32(header + 28); - p->startPosAfterHeader = startArcPos + k7zStartHeaderSize; + p->startPosAfterHeader = (UInt64)startArcPos + k7zStartHeaderSize; if (CrcCalc(header + 12, 20) != GetUi32(header + 8)) return SZ_ERROR_CRC; + p->db.RangeLimit = nextHeaderOffset; + nextHeaderSizeT = (size_t)nextHeaderSize; if (nextHeaderSizeT != nextHeaderSize) return SZ_ERROR_MEM; @@ -1543,13 +1553,13 @@ { Int64 pos = 0; RINOK(ILookInStream_Seek(inStream, &pos, SZ_SEEK_END)); - if ((UInt64)pos < startArcPos + nextHeaderOffset || - (UInt64)pos < startArcPos + k7zStartHeaderSize + nextHeaderOffset || - (UInt64)pos < startArcPos + k7zStartHeaderSize + nextHeaderOffset + nextHeaderSize) + if ((UInt64)pos < (UInt64)startArcPos + nextHeaderOffset || + (UInt64)pos < (UInt64)startArcPos + k7zStartHeaderSize + nextHeaderOffset || + (UInt64)pos < (UInt64)startArcPos + k7zStartHeaderSize + nextHeaderOffset + nextHeaderSize) return SZ_ERROR_INPUT_EOF; } - RINOK(LookInStream_SeekTo(inStream, startArcPos + k7zStartHeaderSize + nextHeaderOffset)); + RINOK(LookInStream_SeekTo(inStream, (UInt64)startArcPos + k7zStartHeaderSize + nextHeaderOffset)); if (!Buf_Create(&buf, nextHeaderSizeT, allocTemp)) return SZ_ERROR_MEM; @@ -1575,6 +1585,8 @@ Buf_Init(&tempBuf); SzAr_Init(&tempAr); + tempAr.RangeLimit = p->db.RangeLimit; + res = SzReadAndDecodePackedStreams(inStream, &sd, &tempBuf, 1, p->startPosAfterHeader, &tempAr, allocTemp); SzAr_Free(&tempAr, allocTemp);
diff --git a/third_party/lzma_sdk/7zCrc.c b/third_party/lzma_sdk/7zCrc.c index b4d84f0..c0cc9bc 100644 --- a/third_party/lzma_sdk/7zCrc.c +++ b/third_party/lzma_sdk/7zCrc.c
@@ -1,5 +1,5 @@ /* 7zCrc.c -- CRC32 init -2017-06-06 : Igor Pavlov : Public domain */ +2021-04-01 : Igor Pavlov : Public domain */ #include "Precomp.h" @@ -26,8 +26,20 @@ typedef UInt32 (MY_FAST_CALL *CRC_FUNC)(UInt32 v, const void *data, size_t size, const UInt32 *table); +extern CRC_FUNC g_CrcUpdateT4; +CRC_FUNC g_CrcUpdateT4; +extern CRC_FUNC g_CrcUpdateT8; +CRC_FUNC g_CrcUpdateT8; +extern +CRC_FUNC g_CrcUpdateT0_32; +CRC_FUNC g_CrcUpdateT0_32; +extern +CRC_FUNC g_CrcUpdateT0_64; +CRC_FUNC g_CrcUpdateT0_64; +extern +CRC_FUNC g_CrcUpdate; CRC_FUNC g_CrcUpdate; UInt32 g_CrcTable[256 * CRC_NUM_TABLES]; @@ -44,6 +56,7 @@ #define CRC_UPDATE_BYTE_2(crc, b) (table[((crc) ^ (b)) & 0xFF] ^ ((crc) >> 8)) +UInt32 MY_FAST_CALL CrcUpdateT1(UInt32 v, const void *data, size_t size, const UInt32 *table); UInt32 MY_FAST_CALL CrcUpdateT1(UInt32 v, const void *data, size_t size, const UInt32 *table) { const Byte *p = (const Byte *)data; @@ -53,6 +66,166 @@ return v; } + +/* ---------- hardware CRC ---------- */ + +#ifdef MY_CPU_LE + +#if defined(MY_CPU_ARM_OR_ARM64) + +// #pragma message("ARM*") + + #if defined(_MSC_VER) + #if defined(MY_CPU_ARM64) + #if (_MSC_VER >= 1910) + // #define USE_ARM64_CRC + #endif + #endif + #elif (defined(__clang__) && (__clang_major__ >= 3)) \ + || (defined(__GNUC__) && (__GNUC__ > 4)) + #if !defined(__ARM_FEATURE_CRC32) + // #define __ARM_FEATURE_CRC32 1 + #if (!defined(__clang__) || (__clang_major__ > 3)) // fix these numbers + // #define ATTRIB_CRC __attribute__((__target__("arch=armv8-a+crc"))) + #endif + #endif + #if defined(__ARM_FEATURE_CRC32) + // #define USE_ARM64_CRC + // #include <arm_acle.h> + #endif + #endif + +#else + +// no hardware CRC + +// #define USE_CRC_EMU + +#ifdef USE_CRC_EMU + +#pragma message("ARM64 CRC emulation") + +MY_FORCE_INLINE +UInt32 __crc32b(UInt32 v, UInt32 data) +{ + const UInt32 *table = g_CrcTable; + v = CRC_UPDATE_BYTE_2(v, (Byte)data); + return v; +} + +MY_FORCE_INLINE +UInt32 __crc32w(UInt32 v, UInt32 data) +{ + const UInt32 *table = g_CrcTable; + v = CRC_UPDATE_BYTE_2(v, (Byte)data); data >>= 8; + v = CRC_UPDATE_BYTE_2(v, (Byte)data); data >>= 8; + v = CRC_UPDATE_BYTE_2(v, (Byte)data); data >>= 8; + v = CRC_UPDATE_BYTE_2(v, (Byte)data); data >>= 8; + return v; +} + +MY_FORCE_INLINE +UInt32 __crc32d(UInt32 v, UInt64 data) +{ + const UInt32 *table = g_CrcTable; + v = CRC_UPDATE_BYTE_2(v, (Byte)data); data >>= 8; + v = CRC_UPDATE_BYTE_2(v, (Byte)data); data >>= 8; + v = CRC_UPDATE_BYTE_2(v, (Byte)data); data >>= 8; + v = CRC_UPDATE_BYTE_2(v, (Byte)data); data >>= 8; + v = CRC_UPDATE_BYTE_2(v, (Byte)data); data >>= 8; + v = CRC_UPDATE_BYTE_2(v, (Byte)data); data >>= 8; + v = CRC_UPDATE_BYTE_2(v, (Byte)data); data >>= 8; + v = CRC_UPDATE_BYTE_2(v, (Byte)data); data >>= 8; + return v; +} + +#endif // USE_CRC_EMU + +#endif // defined(MY_CPU_ARM64) && defined(MY_CPU_LE) + + + +#if defined(USE_ARM64_CRC) || defined(USE_CRC_EMU) + +#define T0_32_UNROLL_BYTES (4 * 4) +#define T0_64_UNROLL_BYTES (4 * 8) + +#ifndef ATTRIB_CRC +#define ATTRIB_CRC +#endif +// #pragma message("USE ARM HW CRC") + +ATTRIB_CRC +UInt32 MY_FAST_CALL CrcUpdateT0_32(UInt32 v, const void *data, size_t size, const UInt32 *table); +ATTRIB_CRC +UInt32 MY_FAST_CALL CrcUpdateT0_32(UInt32 v, const void *data, size_t size, const UInt32 *table) +{ + const Byte *p = (const Byte *)data; + UNUSED_VAR(table); + + for (; size != 0 && ((unsigned)(ptrdiff_t)p & (T0_32_UNROLL_BYTES - 1)) != 0; size--) + v = __crc32b(v, *p++); + + if (size >= T0_32_UNROLL_BYTES) + { + const Byte *lim = p + size; + size &= (T0_32_UNROLL_BYTES - 1); + lim -= size; + do + { + v = __crc32w(v, *(const UInt32 *)(const void *)(p)); + v = __crc32w(v, *(const UInt32 *)(const void *)(p + 4)); p += 2 * 4; + v = __crc32w(v, *(const UInt32 *)(const void *)(p)); + v = __crc32w(v, *(const UInt32 *)(const void *)(p + 4)); p += 2 * 4; + } + while (p != lim); + } + + for (; size != 0; size--) + v = __crc32b(v, *p++); + + return v; +} + +ATTRIB_CRC +UInt32 MY_FAST_CALL CrcUpdateT0_64(UInt32 v, const void *data, size_t size, const UInt32 *table); +ATTRIB_CRC +UInt32 MY_FAST_CALL CrcUpdateT0_64(UInt32 v, const void *data, size_t size, const UInt32 *table) +{ + const Byte *p = (const Byte *)data; + UNUSED_VAR(table); + + for (; size != 0 && ((unsigned)(ptrdiff_t)p & (T0_64_UNROLL_BYTES - 1)) != 0; size--) + v = __crc32b(v, *p++); + + if (size >= T0_64_UNROLL_BYTES) + { + const Byte *lim = p + size; + size &= (T0_64_UNROLL_BYTES - 1); + lim -= size; + do + { + v = __crc32d(v, *(const UInt64 *)(const void *)(p)); + v = __crc32d(v, *(const UInt64 *)(const void *)(p + 8)); p += 2 * 8; + v = __crc32d(v, *(const UInt64 *)(const void *)(p)); + v = __crc32d(v, *(const UInt64 *)(const void *)(p + 8)); p += 2 * 8; + } + while (p != lim); + } + + for (; size != 0; size--) + v = __crc32b(v, *p++); + + return v; +} + +#endif // defined(USE_ARM64_CRC) || defined(USE_CRC_EMU) + +#endif // MY_CPU_LE + + + + void MY_FAST_CALL CrcGenerateTable() { UInt32 i; @@ -123,6 +296,27 @@ } } #endif + #endif + #ifdef MY_CPU_LE + #ifdef USE_ARM64_CRC + if (CPU_IsSupported_CRC32()) + { + g_CrcUpdateT0_32 = CrcUpdateT0_32; + g_CrcUpdateT0_64 = CrcUpdateT0_64; + g_CrcUpdate = + #if defined(MY_CPU_ARM) + CrcUpdateT0_32; + #else + CrcUpdateT0_64; + #endif + } + #endif + + #ifdef USE_CRC_EMU + g_CrcUpdateT0_32 = CrcUpdateT0_32; + g_CrcUpdateT0_64 = CrcUpdateT0_64; + g_CrcUpdate = CrcUpdateT0_64; + #endif #endif }
diff --git a/third_party/lzma_sdk/7zCrcOpt.c b/third_party/lzma_sdk/7zCrcOpt.c index 73beba2..69fad9c 100644 --- a/third_party/lzma_sdk/7zCrcOpt.c +++ b/third_party/lzma_sdk/7zCrcOpt.c
@@ -1,5 +1,5 @@ /* 7zCrcOpt.c -- CRC32 calculation -2017-04-03 : Igor Pavlov : Public domain */ +2021-02-09 : Igor Pavlov : Public domain */ #include "Precomp.h" @@ -9,6 +9,7 @@ #define CRC_UPDATE_BYTE_2(crc, b) (table[((crc) ^ (b)) & 0xFF] ^ ((crc) >> 8)) +UInt32 MY_FAST_CALL CrcUpdateT4(UInt32 v, const void *data, size_t size, const UInt32 *table); UInt32 MY_FAST_CALL CrcUpdateT4(UInt32 v, const void *data, size_t size, const UInt32 *table) { const Byte *p = (const Byte *)data; @@ -16,7 +17,7 @@ v = CRC_UPDATE_BYTE_2(v, *p); for (; size >= 4; size -= 4, p += 4) { - v ^= *(const UInt32 *)p; + v ^= *(const UInt32 *)(const void *)p; v = (table + 0x300)[((v ) & 0xFF)] ^ (table + 0x200)[((v >> 8) & 0xFF)] @@ -28,6 +29,7 @@ return v; } +UInt32 MY_FAST_CALL CrcUpdateT8(UInt32 v, const void *data, size_t size, const UInt32 *table); UInt32 MY_FAST_CALL CrcUpdateT8(UInt32 v, const void *data, size_t size, const UInt32 *table) { const Byte *p = (const Byte *)data; @@ -36,13 +38,13 @@ for (; size >= 8; size -= 8, p += 8) { UInt32 d; - v ^= *(const UInt32 *)p; + v ^= *(const UInt32 *)(const void *)p; v = (table + 0x700)[((v ) & 0xFF)] ^ (table + 0x600)[((v >> 8) & 0xFF)] ^ (table + 0x500)[((v >> 16) & 0xFF)] ^ (table + 0x400)[((v >> 24))]; - d = *((const UInt32 *)p + 1); + d = *((const UInt32 *)(const void *)p + 1); v ^= (table + 0x300)[((d ) & 0xFF)] ^ (table + 0x200)[((d >> 8) & 0xFF)] @@ -72,7 +74,7 @@ v = CRC_UPDATE_BYTE_2_BE(v, *p); for (; size >= 4; size -= 4, p += 4) { - v ^= *(const UInt32 *)p; + v ^= *(const UInt32 *)(const void *)p; v = (table + 0x000)[((v ) & 0xFF)] ^ (table + 0x100)[((v >> 8) & 0xFF)] @@ -94,13 +96,13 @@ for (; size >= 8; size -= 8, p += 8) { UInt32 d; - v ^= *(const UInt32 *)p; + v ^= *(const UInt32 *)(const void *)p; v = (table + 0x400)[((v ) & 0xFF)] ^ (table + 0x500)[((v >> 8) & 0xFF)] ^ (table + 0x600)[((v >> 16) & 0xFF)] ^ (table + 0x700)[((v >> 24))]; - d = *((const UInt32 *)p + 1); + d = *((const UInt32 *)(const void *)p + 1); v ^= (table + 0x000)[((d ) & 0xFF)] ^ (table + 0x100)[((d >> 8) & 0xFF)]
diff --git a/third_party/lzma_sdk/7zDec.c b/third_party/lzma_sdk/7zDec.c index 7c46352..fbfd016 100644 --- a/third_party/lzma_sdk/7zDec.c +++ b/third_party/lzma_sdk/7zDec.c
@@ -1,5 +1,5 @@ /* 7zDec.c -- Decoding from 7z folder -2019-02-02 : Igor Pavlov : Public domain */ +2021-02-09 : Igor Pavlov : Public domain */ #include "Precomp.h" @@ -21,17 +21,20 @@ #endif #define k_Copy 0 -#define k_Delta 3 +#ifndef _7Z_NO_METHOD_LZMA2 #define k_LZMA2 0x21 +#endif #define k_LZMA 0x30101 -#define k_BCJ 0x3030103 #define k_BCJ2 0x303011B +#ifndef _7Z_NO_METHODS_FILTERS +#define k_Delta 3 +#define k_BCJ 0x3030103 #define k_PPC 0x3030205 #define k_IA64 0x3030401 #define k_ARM 0x3030501 #define k_ARMT 0x3030701 #define k_SPARC 0x3030805 - +#endif #ifdef _7ZIP_PPMD_SUPPPORT @@ -56,7 +59,7 @@ return *p->cur++; if (p->res == SZ_OK) { - size_t size = p->cur - p->begin; + size_t size = (size_t)(p->cur - p->begin); p->processed += size; p->res = ILookInStream_Skip(p->inStream, size); size = (1 << 25); @@ -101,28 +104,32 @@ Ppmd7_Init(&ppmd, order); } { - CPpmd7z_RangeDec rc; - Ppmd7z_RangeDec_CreateVTable(&rc); - rc.Stream = &s.vt; - if (!Ppmd7z_RangeDec_Init(&rc)) + ppmd.rc.dec.Stream = &s.vt; + if (!Ppmd7z_RangeDec_Init(&ppmd.rc.dec)) res = SZ_ERROR_DATA; - else if (s.extra) - res = (s.res != SZ_OK ? s.res : SZ_ERROR_DATA); - else + else if (!s.extra) { - SizeT i; - for (i = 0; i < outSize; i++) + Byte *buf = outBuffer; + const Byte *lim = buf + outSize; + for (; buf != lim; buf++) { - int sym = Ppmd7_DecodeSymbol(&ppmd, &rc.vt); + int sym = Ppmd7z_DecodeSymbol(&ppmd); if (s.extra || sym < 0) break; - outBuffer[i] = (Byte)sym; + *buf = (Byte)sym; } - if (i != outSize) - res = (s.res != SZ_OK ? s.res : SZ_ERROR_DATA); - else if (s.processed + (s.cur - s.begin) != inSize || !Ppmd7z_RangeDec_IsFinishedOK(&rc)) + if (buf != lim) res = SZ_ERROR_DATA; + else if (!Ppmd7z_RangeDec_IsFinishedOK(&ppmd.rc.dec)) + { + /* if (Ppmd7z_DecodeSymbol(&ppmd) != PPMD7_SYM_END || !Ppmd7z_RangeDec_IsFinishedOK(&ppmd.rc.dec)) */ + res = SZ_ERROR_DATA; + } } + if (s.extra) + res = (s.res != SZ_OK ? s.res : SZ_ERROR_DATA); + else if (s.processed + (size_t)(s.cur - s.begin) != inSize) + res = SZ_ERROR_DATA; } Ppmd7_Free(&ppmd, allocMain); return res; @@ -365,7 +372,9 @@ return SZ_ERROR_UNSUPPORTED; } +#ifndef _7Z_NO_METHODS_FILTERS #define CASE_BRA_CONV(isa) case k_ ## isa: isa ## _Convert(outBuffer, outSize, 0, 0); break; +#endif static SRes SzFolder_Decode2(const CSzFolder *folder, const Byte *propsData,
diff --git a/third_party/lzma_sdk/7zFile.c b/third_party/lzma_sdk/7zFile.c index 8992fb1c..13d2efa4 100644 --- a/third_party/lzma_sdk/7zFile.c +++ b/third_party/lzma_sdk/7zFile.c
@@ -1,5 +1,5 @@ /* 7zFile.c -- File IO -2017-04-03 : Igor Pavlov : Public domain */ +2021-04-29 : Igor Pavlov : Public domain */ #include "Precomp.h" @@ -7,9 +7,19 @@ #ifndef USE_WINDOWS_FILE -#ifndef UNDER_CE -#include <errno.h> -#endif + #include <errno.h> + + #ifndef USE_FOPEN + #include <stdio.h> + #include <fcntl.h> + #ifdef _WIN32 + #include <io.h> + typedef int ssize_t; + typedef int off_t; + #else + #include <unistd.h> + #endif + #endif #else @@ -23,30 +33,36 @@ And message can be "Network connection was lost" */ -#define kChunkSizeMax (1 << 22) - #endif +#define kChunkSizeMax (1 << 22) + void File_Construct(CSzFile *p) { #ifdef USE_WINDOWS_FILE p->handle = INVALID_HANDLE_VALUE; - #else + #elif defined(USE_FOPEN) p->file = NULL; + #else + p->fd = -1; #endif } #if !defined(UNDER_CE) || !defined(USE_WINDOWS_FILE) + static WRes File_Open(CSzFile *p, const char *name, int writeMode) { #ifdef USE_WINDOWS_FILE + p->handle = CreateFileA(name, writeMode ? GENERIC_WRITE : GENERIC_READ, FILE_SHARE_READ, NULL, writeMode ? CREATE_ALWAYS : OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); return (p->handle != INVALID_HANDLE_VALUE) ? 0 : GetLastError(); - #else + + #elif defined(USE_FOPEN) + p->file = fopen(name, writeMode ? "wb+" : "rb"); return (p->file != 0) ? 0 : #ifdef UNDER_CE @@ -54,13 +70,34 @@ #else errno; #endif + + #else + + int flags = (writeMode ? (O_CREAT | O_EXCL | O_WRONLY) : O_RDONLY); + #ifdef O_BINARY + flags |= O_BINARY; + #endif + p->fd = open(name, flags, 0666); + return (p->fd != -1) ? 0 : errno; + #endif } WRes InFile_Open(CSzFile *p, const char *name) { return File_Open(p, name, 0); } -WRes OutFile_Open(CSzFile *p, const char *name) { return File_Open(p, name, 1); } + +WRes OutFile_Open(CSzFile *p, const char *name) +{ + #if defined(USE_WINDOWS_FILE) || defined(USE_FOPEN) + return File_Open(p, name, 1); + #else + p->fd = creat(name, 0666); + return (p->fd != -1) ? 0 : errno; + #endif +} + #endif + #ifdef USE_WINDOWS_FILE static WRes File_OpenW(CSzFile *p, const WCHAR *name, int writeMode) { @@ -78,74 +115,124 @@ WRes File_Close(CSzFile *p) { #ifdef USE_WINDOWS_FILE + if (p->handle != INVALID_HANDLE_VALUE) { if (!CloseHandle(p->handle)) return GetLastError(); p->handle = INVALID_HANDLE_VALUE; } - #else + + #elif defined(USE_FOPEN) + if (p->file != NULL) { int res = fclose(p->file); if (res != 0) + { + if (res == EOF) + return errno; return res; + } p->file = NULL; } + + #else + + if (p->fd != -1) + { + if (close(p->fd) != 0) + return errno; + p->fd = -1; + } + #endif + return 0; } + WRes File_Read(CSzFile *p, void *data, size_t *size) { size_t originalSize = *size; + *size = 0; if (originalSize == 0) return 0; #ifdef USE_WINDOWS_FILE - *size = 0; do { - DWORD curSize = (originalSize > kChunkSizeMax) ? kChunkSizeMax : (DWORD)originalSize; + const DWORD curSize = (originalSize > kChunkSizeMax) ? kChunkSizeMax : (DWORD)originalSize; DWORD processed = 0; - BOOL res = ReadFile(p->handle, data, curSize, &processed, NULL); + const BOOL res = ReadFile(p->handle, data, curSize, &processed, NULL); data = (void *)((Byte *)data + processed); originalSize -= processed; *size += processed; if (!res) return GetLastError(); + // debug : we can break here for partial reading mode if (processed == 0) break; } while (originalSize > 0); - return 0; + + #elif defined(USE_FOPEN) + + do + { + const size_t curSize = (originalSize > kChunkSizeMax) ? kChunkSizeMax : originalSize; + const size_t processed = fread(data, 1, curSize, p->file); + data = (void *)((Byte *)data + (size_t)processed); + originalSize -= processed; + *size += processed; + if (processed != curSize) + return ferror(p->file); + // debug : we can break here for partial reading mode + if (processed == 0) + break; + } + while (originalSize > 0); #else - - *size = fread(data, 1, originalSize, p->file); - if (*size == originalSize) - return 0; - return ferror(p->file); - + + do + { + const size_t curSize = (originalSize > kChunkSizeMax) ? kChunkSizeMax : originalSize; + const ssize_t processed = read(p->fd, data, curSize); + if (processed == -1) + return errno; + if (processed == 0) + break; + data = (void *)((Byte *)data + (size_t)processed); + originalSize -= (size_t)processed; + *size += (size_t)processed; + // debug : we can break here for partial reading mode + // break; + } + while (originalSize > 0); + #endif + + return 0; } + WRes File_Write(CSzFile *p, const void *data, size_t *size) { size_t originalSize = *size; + *size = 0; if (originalSize == 0) return 0; #ifdef USE_WINDOWS_FILE - *size = 0; do { - DWORD curSize = (originalSize > kChunkSizeMax) ? kChunkSizeMax : (DWORD)originalSize; + const DWORD curSize = (originalSize > kChunkSizeMax) ? kChunkSizeMax : (DWORD)originalSize; DWORD processed = 0; - BOOL res = WriteFile(p->handle, data, curSize, &processed, NULL); - data = (void *)((Byte *)data + processed); + const BOOL res = WriteFile(p->handle, data, curSize, &processed, NULL); + data = (const void *)((const Byte *)data + processed); originalSize -= processed; *size += processed; if (!res) @@ -154,26 +241,52 @@ break; } while (originalSize > 0); - return 0; + + #elif defined(USE_FOPEN) + + do + { + const size_t curSize = (originalSize > kChunkSizeMax) ? kChunkSizeMax : originalSize; + const size_t processed = fwrite(data, 1, curSize, p->file); + data = (void *)((Byte *)data + (size_t)processed); + originalSize -= processed; + *size += processed; + if (processed != curSize) + return ferror(p->file); + if (processed == 0) + break; + } + while (originalSize > 0); #else - *size = fwrite(data, 1, originalSize, p->file); - if (*size == originalSize) - return 0; - return ferror(p->file); - + do + { + const size_t curSize = (originalSize > kChunkSizeMax) ? kChunkSizeMax : originalSize; + const ssize_t processed = write(p->fd, data, curSize); + if (processed == -1) + return errno; + if (processed == 0) + break; + data = (void *)((Byte *)data + (size_t)processed); + originalSize -= (size_t)processed; + *size += (size_t)processed; + } + while (originalSize > 0); + #endif + + return 0; } + WRes File_Seek(CSzFile *p, Int64 *pos, ESzSeek origin) { #ifdef USE_WINDOWS_FILE - LARGE_INTEGER value; DWORD moveMethod; - value.LowPart = (DWORD)*pos; - value.HighPart = (LONG)((UInt64)*pos >> 16 >> 16); /* for case when UInt64 is 32-bit only */ + UInt32 low = (UInt32)*pos; + LONG high = (LONG)((UInt64)*pos >> 16 >> 16); /* for case when UInt64 is 32-bit only */ switch (origin) { case SZ_SEEK_SET: moveMethod = FILE_BEGIN; break; @@ -181,34 +294,52 @@ case SZ_SEEK_END: moveMethod = FILE_END; break; default: return ERROR_INVALID_PARAMETER; } - value.LowPart = SetFilePointer(p->handle, value.LowPart, &value.HighPart, moveMethod); - if (value.LowPart == 0xFFFFFFFF) + low = SetFilePointer(p->handle, (LONG)low, &high, moveMethod); + if (low == (UInt32)0xFFFFFFFF) { WRes res = GetLastError(); if (res != NO_ERROR) return res; } - *pos = ((Int64)value.HighPart << 32) | value.LowPart; + *pos = ((Int64)high << 32) | low; return 0; #else - int moveMethod; - int res; + int moveMethod; // = origin; + switch (origin) { case SZ_SEEK_SET: moveMethod = SEEK_SET; break; case SZ_SEEK_CUR: moveMethod = SEEK_CUR; break; case SZ_SEEK_END: moveMethod = SEEK_END; break; - default: return 1; + default: return EINVAL; } - res = fseek(p->file, (long)*pos, moveMethod); - *pos = ftell(p->file); - return res; - #endif + #if defined(USE_FOPEN) + { + int res = fseek(p->file, (long)*pos, moveMethod); + if (res == -1) + return errno; + *pos = ftell(p->file); + if (*pos == -1) + return errno; + return 0; + } + #else + { + off_t res = lseek(p->fd, (off_t)*pos, moveMethod); + if (res == -1) + return errno; + *pos = res; + return 0; + } + + #endif // USE_FOPEN + #endif // USE_WINDOWS_FILE } + WRes File_GetLength(CSzFile *p, UInt64 *length) { #ifdef USE_WINDOWS_FILE @@ -224,13 +355,31 @@ *length = (((UInt64)sizeHigh) << 32) + sizeLow; return 0; - #else + #elif defined(USE_FOPEN) long pos = ftell(p->file); int res = fseek(p->file, 0, SEEK_END); *length = ftell(p->file); fseek(p->file, pos, SEEK_SET); return res; + + #else + + off_t pos; + *length = 0; + pos = lseek(p->fd, 0, SEEK_CUR); + if (pos != -1) + { + const off_t len2 = lseek(p->fd, 0, SEEK_END); + const off_t res2 = lseek(p->fd, pos, SEEK_SET); + if (len2 != -1) + { + *length = (UInt64)len2; + if (res2 != -1) + return 0; + } + } + return errno; #endif } @@ -241,7 +390,9 @@ static SRes FileSeqInStream_Read(const ISeqInStream *pp, void *buf, size_t *size) { CFileSeqInStream *p = CONTAINER_FROM_VTBL(pp, CFileSeqInStream, vt); - return File_Read(&p->file, buf, size) == 0 ? SZ_OK : SZ_ERROR_READ; + WRes wres = File_Read(&p->file, buf, size); + p->wres = wres; + return (wres == 0) ? SZ_OK : SZ_ERROR_READ; } void FileSeqInStream_CreateVTable(CFileSeqInStream *p) @@ -255,13 +406,17 @@ static SRes FileInStream_Read(const ISeekInStream *pp, void *buf, size_t *size) { CFileInStream *p = CONTAINER_FROM_VTBL(pp, CFileInStream, vt); - return (File_Read(&p->file, buf, size) == 0) ? SZ_OK : SZ_ERROR_READ; + WRes wres = File_Read(&p->file, buf, size); + p->wres = wres; + return (wres == 0) ? SZ_OK : SZ_ERROR_READ; } static SRes FileInStream_Seek(const ISeekInStream *pp, Int64 *pos, ESzSeek origin) { CFileInStream *p = CONTAINER_FROM_VTBL(pp, CFileInStream, vt); - return File_Seek(&p->file, pos, origin); + WRes wres = File_Seek(&p->file, pos, origin); + p->wres = wres; + return (wres == 0) ? SZ_OK : SZ_ERROR_READ; } void FileInStream_CreateVTable(CFileInStream *p) @@ -276,7 +431,8 @@ static size_t FileOutStream_Write(const ISeqOutStream *pp, const void *data, size_t size) { CFileOutStream *p = CONTAINER_FROM_VTBL(pp, CFileOutStream, vt); - File_Write(&p->file, data, &size); + WRes wres = File_Write(&p->file, data, &size); + p->wres = wres; return size; }
diff --git a/third_party/lzma_sdk/7zFile.h b/third_party/lzma_sdk/7zFile.h index 0e79253..788abb6 100644 --- a/third_party/lzma_sdk/7zFile.h +++ b/third_party/lzma_sdk/7zFile.h
@@ -1,17 +1,20 @@ /* 7zFile.h -- File IO -2017-04-03 : Igor Pavlov : Public domain */ +2021-02-15 : Igor Pavlov : Public domain */ #ifndef __7Z_FILE_H #define __7Z_FILE_H #ifdef _WIN32 #define USE_WINDOWS_FILE +// #include <windows.h> #endif #ifdef USE_WINDOWS_FILE #include <windows.h> #else -#include <stdio.h> +// note: USE_FOPEN mode is limited to 32-bit file size +// #define USE_FOPEN +// #include <stdio.h> #endif #include "7zTypes.h" @@ -24,8 +27,10 @@ { #ifdef USE_WINDOWS_FILE HANDLE handle; - #else + #elif defined(USE_FOPEN) FILE *file; + #else + int fd; #endif } CSzFile; @@ -56,6 +61,7 @@ { ISeqInStream vt; CSzFile file; + WRes wres; } CFileSeqInStream; void FileSeqInStream_CreateVTable(CFileSeqInStream *p); @@ -65,6 +71,7 @@ { ISeekInStream vt; CSzFile file; + WRes wres; } CFileInStream; void FileInStream_CreateVTable(CFileInStream *p); @@ -74,6 +81,7 @@ { ISeqOutStream vt; CSzFile file; + WRes wres; } CFileOutStream; void FileOutStream_CreateVTable(CFileOutStream *p);
diff --git a/third_party/lzma_sdk/7zStream.c b/third_party/lzma_sdk/7zStream.c index 6b5aa16..28a1460 100644 --- a/third_party/lzma_sdk/7zStream.c +++ b/third_party/lzma_sdk/7zStream.c
@@ -1,5 +1,5 @@ /* 7zStream.c -- 7z Stream functions -2017-04-03 : Igor Pavlov : Public domain */ +2021-02-09 : Igor Pavlov : Public domain */ #include "Precomp.h" @@ -37,7 +37,7 @@ SRes LookInStream_SeekTo(const ILookInStream *stream, UInt64 offset) { - Int64 t = offset; + Int64 t = (Int64)offset; return ILookInStream_Seek(stream, &t, SZ_SEEK_SET); }
diff --git a/third_party/lzma_sdk/7zTypes.h b/third_party/lzma_sdk/7zTypes.h index 65b3af6..fe4fde3 100644 --- a/third_party/lzma_sdk/7zTypes.h +++ b/third_party/lzma_sdk/7zTypes.h
@@ -1,11 +1,13 @@ /* 7zTypes.h -- Basic types -2018-08-04 : Igor Pavlov : Public domain */ +2021-12-25 : Igor Pavlov : Public domain */ #ifndef __7Z_TYPES_H #define __7Z_TYPES_H #ifdef _WIN32 /* #include <windows.h> */ +#else +#include <errno.h> #endif #include <stddef.h> @@ -43,18 +45,116 @@ typedef int SRes; +#ifdef _MSC_VER + #if _MSC_VER > 1200 + #define MY_ALIGN(n) __declspec(align(n)) + #else + #define MY_ALIGN(n) + #endif +#else + #define MY_ALIGN(n) __attribute__ ((aligned(n))) +#endif + + #ifdef _WIN32 /* typedef DWORD WRes; */ typedef unsigned WRes; #define MY_SRes_HRESULT_FROM_WRes(x) HRESULT_FROM_WIN32(x) -#else +// #define MY_HRES_ERROR__INTERNAL_ERROR MY_SRes_HRESULT_FROM_WRes(ERROR_INTERNAL_ERROR) +#else // _WIN32 + +// #define ENV_HAVE_LSTAT typedef int WRes; -#define MY__FACILITY_WIN32 7 -#define MY__FACILITY__WRes MY__FACILITY_WIN32 -#define MY_SRes_HRESULT_FROM_WRes(x) ((HRESULT)(x) <= 0 ? ((HRESULT)(x)) : ((HRESULT) (((x) & 0x0000FFFF) | (MY__FACILITY__WRes << 16) | 0x80000000))) + +// (FACILITY_ERRNO = 0x800) is 7zip's FACILITY constant to represent (errno) errors in HRESULT +#define MY__FACILITY_ERRNO 0x800 +#define MY__FACILITY_WIN32 7 +#define MY__FACILITY__WRes MY__FACILITY_ERRNO + +#define MY_HRESULT_FROM_errno_CONST_ERROR(x) ((HRESULT)( \ + ( (HRESULT)(x) & 0x0000FFFF) \ + | (MY__FACILITY__WRes << 16) \ + | (HRESULT)0x80000000 )) + +#define MY_SRes_HRESULT_FROM_WRes(x) \ + ((HRESULT)(x) <= 0 ? ((HRESULT)(x)) : MY_HRESULT_FROM_errno_CONST_ERROR(x)) + +// we call macro HRESULT_FROM_WIN32 for system errors (WRes) that are (errno) +#define HRESULT_FROM_WIN32(x) MY_SRes_HRESULT_FROM_WRes(x) + +/* +#define ERROR_FILE_NOT_FOUND 2L +#define ERROR_ACCESS_DENIED 5L +#define ERROR_NO_MORE_FILES 18L +#define ERROR_LOCK_VIOLATION 33L +#define ERROR_FILE_EXISTS 80L +#define ERROR_DISK_FULL 112L +#define ERROR_NEGATIVE_SEEK 131L +#define ERROR_ALREADY_EXISTS 183L +#define ERROR_DIRECTORY 267L +#define ERROR_TOO_MANY_POSTS 298L + +#define ERROR_INTERNAL_ERROR 1359L +#define ERROR_INVALID_REPARSE_DATA 4392L +#define ERROR_REPARSE_TAG_INVALID 4393L +#define ERROR_REPARSE_TAG_MISMATCH 4394L +*/ + +// we use errno equivalents for some WIN32 errors: + +#define ERROR_INVALID_PARAMETER EINVAL +#define ERROR_INVALID_FUNCTION EINVAL +#define ERROR_ALREADY_EXISTS EEXIST +#define ERROR_FILE_EXISTS EEXIST +#define ERROR_PATH_NOT_FOUND ENOENT +#define ERROR_FILE_NOT_FOUND ENOENT +#define ERROR_DISK_FULL ENOSPC +// #define ERROR_INVALID_HANDLE EBADF + +// we use FACILITY_WIN32 for errors that has no errno equivalent +// Too many posts were made to a semaphore. +#define ERROR_TOO_MANY_POSTS ((HRESULT)0x8007012AL) +#define ERROR_INVALID_REPARSE_DATA ((HRESULT)0x80071128L) +#define ERROR_REPARSE_TAG_INVALID ((HRESULT)0x80071129L) + +// if (MY__FACILITY__WRes != FACILITY_WIN32), +// we use FACILITY_WIN32 for COM errors: +#define E_OUTOFMEMORY ((HRESULT)0x8007000EL) +#define E_INVALIDARG ((HRESULT)0x80070057L) +#define MY__E_ERROR_NEGATIVE_SEEK ((HRESULT)0x80070083L) + +/* +// we can use FACILITY_ERRNO for some COM errors, that have errno equivalents: +#define E_OUTOFMEMORY MY_HRESULT_FROM_errno_CONST_ERROR(ENOMEM) +#define E_INVALIDARG MY_HRESULT_FROM_errno_CONST_ERROR(EINVAL) +#define MY__E_ERROR_NEGATIVE_SEEK MY_HRESULT_FROM_errno_CONST_ERROR(EINVAL) +*/ + +// gcc / clang : (sizeof(long) == sizeof(void*)) in 32/64 bits +typedef long INT_PTR; +typedef unsigned long UINT_PTR; + +#define TEXT(quote) quote + +#define FILE_ATTRIBUTE_READONLY 0x0001 +#define FILE_ATTRIBUTE_HIDDEN 0x0002 +#define FILE_ATTRIBUTE_SYSTEM 0x0004 +#define FILE_ATTRIBUTE_DIRECTORY 0x0010 +#define FILE_ATTRIBUTE_ARCHIVE 0x0020 +#define FILE_ATTRIBUTE_DEVICE 0x0040 +#define FILE_ATTRIBUTE_NORMAL 0x0080 +#define FILE_ATTRIBUTE_TEMPORARY 0x0100 +#define FILE_ATTRIBUTE_SPARSE_FILE 0x0200 +#define FILE_ATTRIBUTE_REPARSE_POINT 0x0400 +#define FILE_ATTRIBUTE_COMPRESSED 0x0800 +#define FILE_ATTRIBUTE_OFFLINE 0x1000 +#define FILE_ATTRIBUTE_NOT_CONTENT_INDEXED 0x2000 +#define FILE_ATTRIBUTE_ENCRYPTED 0x4000 + +#define FILE_ATTRIBUTE_UNIX_EXTENSION 0x8000 /* trick for Unix */ #endif @@ -63,6 +163,10 @@ #define RINOK(x) { int __result__ = (x); if (__result__ != 0) return __result__; } #endif +#ifndef RINOK_WRes +#define RINOK_WRes(x) { WRes __result__ = (x); if (__result__ != 0) return __result__; } +#endif + typedef unsigned char Byte; typedef short Int16; typedef unsigned short UInt16; @@ -75,6 +179,40 @@ typedef unsigned int UInt32; #endif + +#ifndef _WIN32 + +typedef int INT; +typedef Int32 INT32; +typedef unsigned int UINT; +typedef UInt32 UINT32; +typedef INT32 LONG; // LONG, ULONG and DWORD must be 32-bit for _WIN32 compatibility +typedef UINT32 ULONG; + +#undef DWORD +typedef UINT32 DWORD; + +#define VOID void + +#define HRESULT LONG + +typedef void *LPVOID; +// typedef void VOID; +// typedef ULONG_PTR DWORD_PTR, *PDWORD_PTR; +// gcc / clang on Unix : sizeof(long==sizeof(void*) in 32 or 64 bits) +typedef long INT_PTR; +typedef unsigned long UINT_PTR; +typedef long LONG_PTR; +typedef unsigned long DWORD_PTR; + +typedef size_t SIZE_T; + +#endif // _WIN32 + + +#define MY_HRES_ERROR__INTERNAL_ERROR ((HRESULT)0x8007054FL) + + #ifdef _SZ_NO_INT_64 /* define _SZ_NO_INT_64, if your compiler doesn't support 64-bit integers. @@ -128,25 +266,37 @@ #define MY_CDECL __cdecl #define MY_FAST_CALL __fastcall -#else +#else // _MSC_VER -#define MY_NO_INLINE -#define MY_FORCE_INLINE -#define MY_CDECL -#define MY_FAST_CALL - -/* inline keyword : for C++ / C99 */ - -/* GCC, clang: */ -/* -#if defined (__GNUC__) && (__GNUC__ >= 4) -#define MY_FORCE_INLINE __attribute__((always_inline)) +#if (defined(__GNUC__) && (__GNUC__ >= 4)) \ + || (defined(__clang__) && (__clang_major__ >= 4)) \ + || defined(__INTEL_COMPILER) \ + || defined(__xlC__) #define MY_NO_INLINE __attribute__((noinline)) +// #define MY_FORCE_INLINE __attribute__((always_inline)) inline +#else +#define MY_NO_INLINE #endif -*/ +#define MY_FORCE_INLINE + + +#define MY_CDECL + +#if defined(_M_IX86) \ + || defined(__i386__) +// #define MY_FAST_CALL __attribute__((fastcall)) +// #define MY_FAST_CALL __attribute__((cdecl)) +#define MY_FAST_CALL +#elif defined(MY_CPU_AMD64) +// #define MY_FAST_CALL __attribute__((ms_abi)) +#define MY_FAST_CALL +#else +#define MY_FAST_CALL #endif +#endif // _MSC_VER + /* The following interfaces use first parameter as pointer to structure */ @@ -335,12 +485,11 @@ GCC 4.8.1 : classes with non-public variable members" */ -#define MY_container_of(ptr, type, m) ((type *)((char *)(1 ? (ptr) : &((type *)0)->m) - MY_offsetof(type, m))) - +#define MY_container_of(ptr, type, m) ((type *)(void *)((char *)(void *)(1 ? (ptr) : &((type *)0)->m) - MY_offsetof(type, m))) #endif -#define CONTAINER_FROM_VTBL_SIMPLE(ptr, type, m) ((type *)(ptr)) +#define CONTAINER_FROM_VTBL_SIMPLE(ptr, type, m) ((type *)(void *)(ptr)) /* #define CONTAINER_FROM_VTBL(ptr, type, m) CONTAINER_FROM_VTBL_SIMPLE(ptr, type, m) @@ -353,6 +502,7 @@ */ +#define MY_memset_0_ARRAY(a) memset((a), 0, sizeof(a)) #ifdef _WIN32
diff --git a/third_party/lzma_sdk/7zVersion.h b/third_party/lzma_sdk/7zVersion.h index c176823..e9363d3 100644 --- a/third_party/lzma_sdk/7zVersion.h +++ b/third_party/lzma_sdk/7zVersion.h
@@ -1,7 +1,7 @@ -#define MY_VER_MAJOR 19 -#define MY_VER_MINOR 00 +#define MY_VER_MAJOR 21 +#define MY_VER_MINOR 07 #define MY_VER_BUILD 0 -#define MY_VERSION_NUMBERS "19.00" +#define MY_VERSION_NUMBERS "21.07" #define MY_VERSION MY_VERSION_NUMBERS #ifdef MY_CPU_NAME @@ -10,12 +10,12 @@ #define MY_VERSION_CPU MY_VERSION #endif -#define MY_DATE "2019-02-21" +#define MY_DATE "2021-12-26" #undef MY_COPYRIGHT #undef MY_VERSION_COPYRIGHT_DATE #define MY_AUTHOR_NAME "Igor Pavlov" #define MY_COPYRIGHT_PD "Igor Pavlov : Public domain" -#define MY_COPYRIGHT_CR "Copyright (c) 1999-2018 Igor Pavlov" +#define MY_COPYRIGHT_CR "Copyright (c) 1999-2021 Igor Pavlov" #ifdef USE_COPYRIGHT_CR #define MY_COPYRIGHT MY_COPYRIGHT_CR
diff --git a/third_party/lzma_sdk/7zr.exe b/third_party/lzma_sdk/7zr.exe index bb1b30b..55b4def 100755 --- a/third_party/lzma_sdk/7zr.exe +++ b/third_party/lzma_sdk/7zr.exe Binary files differ
diff --git a/third_party/lzma_sdk/Alloc.c b/third_party/lzma_sdk/Alloc.c index bcede4b..d1af76c 100644 --- a/third_party/lzma_sdk/Alloc.c +++ b/third_party/lzma_sdk/Alloc.c
@@ -1,12 +1,12 @@ /* Alloc.c -- Memory allocation functions -2018-04-27 : Igor Pavlov : Public domain */ +2021-07-13 : Igor Pavlov : Public domain */ #include "Precomp.h" #include <stdio.h> #ifdef _WIN32 -#include <windows.h> +#include <Windows.h> #endif #include <stdlib.h> @@ -122,7 +122,6 @@ #define Print(s) #define PrintLn() #define PrintHex(v, align) -#define PrintDec(v, align) #define PrintAddr(p) #endif @@ -133,10 +132,11 @@ { if (size == 0) return NULL; + PRINT_ALLOC("Alloc ", g_allocCount, size, NULL); #ifdef _SZ_ALLOC_DEBUG { void *p = malloc(size); - PRINT_ALLOC("Alloc ", g_allocCount, size, p); + // PRINT_ALLOC("Alloc ", g_allocCount, size, p); return p; } #else @@ -172,14 +172,20 @@ VirtualFree(address, 0, MEM_RELEASE); } -#ifndef MEM_LARGE_PAGES -#undef _7ZIP_LARGE_PAGES +#ifdef _7ZIP_LARGE_PAGES + +#ifdef MEM_LARGE_PAGES + #define MY__MEM_LARGE_PAGES MEM_LARGE_PAGES +#else + #define MY__MEM_LARGE_PAGES 0x20000000 #endif -#ifdef _7ZIP_LARGE_PAGES +extern +SIZE_T g_LargePageSize; SIZE_T g_LargePageSize = 0; -typedef SIZE_T (WINAPI *GetLargePageMinimumP)(); -#endif +typedef SIZE_T (WINAPI *GetLargePageMinimumP)(VOID); + +#endif // _7ZIP_LARGE_PAGES void SetLargePageSize() { @@ -214,7 +220,7 @@ size2 = (size + ps) & ~ps; if (size2 >= size) { - void *res = VirtualAlloc(NULL, size2, MEM_COMMIT | MEM_LARGE_PAGES, PAGE_READWRITE); + void *res = VirtualAlloc(NULL, size2, MEM_COMMIT | MY__MEM_LARGE_PAGES, PAGE_READWRITE); if (res) return res; } @@ -241,14 +247,14 @@ static void SzFree(ISzAllocPtr p, void *address) { UNUSED_VAR(p); MyFree(address); } const ISzAlloc g_Alloc = { SzAlloc, SzFree }; +#ifdef _WIN32 static void *SzMidAlloc(ISzAllocPtr p, size_t size) { UNUSED_VAR(p); return MidAlloc(size); } static void SzMidFree(ISzAllocPtr p, void *address) { UNUSED_VAR(p); MidFree(address); } -const ISzAlloc g_MidAlloc = { SzMidAlloc, SzMidFree }; - static void *SzBigAlloc(ISzAllocPtr p, size_t size) { UNUSED_VAR(p); return BigAlloc(size); } static void SzBigFree(ISzAllocPtr p, void *address) { UNUSED_VAR(p); BigFree(address); } +const ISzAlloc g_MidAlloc = { SzMidAlloc, SzMidFree }; const ISzAlloc g_BigAlloc = { SzBigAlloc, SzBigFree }; - +#endif /* uintptr_t : <stdint.h> C99 (optional) @@ -280,13 +286,15 @@ */ #define MY_ALIGN_PTR_DOWN(p, align) ((void *)((((UIntPtr)(p)) & ~((UIntPtr)(align) - 1)))) -#define MY_ALIGN_PTR_UP_PLUS(p, align) MY_ALIGN_PTR_DOWN(((char *)(p) + (align) + ADJUST_ALLOC_SIZE), align) - -#if (_POSIX_C_SOURCE >= 200112L) && !defined(_WIN32) +#if !defined(_WIN32) && defined(_POSIX_C_SOURCE) && (_POSIX_C_SOURCE >= 200112L) #define USE_posix_memalign #endif +#ifndef USE_posix_memalign +#define MY_ALIGN_PTR_UP_PLUS(p, align) MY_ALIGN_PTR_DOWN(((char *)(p) + (align) + ADJUST_ALLOC_SIZE), align) +#endif + /* This posix_memalign() is for test purposes only. We also need special Free() function instead of free(),
diff --git a/third_party/lzma_sdk/Alloc.h b/third_party/lzma_sdk/Alloc.h index 64823764..3be2041e 100644 --- a/third_party/lzma_sdk/Alloc.h +++ b/third_party/lzma_sdk/Alloc.h
@@ -1,5 +1,5 @@ /* Alloc.h -- Memory allocation functions -2018-02-19 : Igor Pavlov : Public domain */ +2021-07-13 : Igor Pavlov : Public domain */ #ifndef __COMMON_ALLOC_H #define __COMMON_ALLOC_H @@ -13,7 +13,7 @@ #ifdef _WIN32 -void SetLargePageSize(); +void SetLargePageSize(void); void *MidAlloc(size_t size); void MidFree(void *address); @@ -30,8 +30,15 @@ #endif extern const ISzAlloc g_Alloc; + +#ifdef _WIN32 extern const ISzAlloc g_BigAlloc; extern const ISzAlloc g_MidAlloc; +#else +#define g_BigAlloc g_AlignedAlloc +#define g_MidAlloc g_AlignedAlloc +#endif + extern const ISzAlloc g_AlignedAlloc;
diff --git a/third_party/lzma_sdk/BUILD.gn b/third_party/lzma_sdk/BUILD.gn index 077bcde0..b276c727 100644 --- a/third_party/lzma_sdk/BUILD.gn +++ b/third_party/lzma_sdk/BUILD.gn
@@ -2,25 +2,55 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. +import("//build/config/arm.gni") + +# TODO(richard.townsend@arm.com): Optimizations temporarily disabled for +# Windows on Arm MSVC builds, see http://crbug.com/v8/10012. +use_arm_neon_optimizations = (target_cpu == "arm" || target_cpu == "arm64") && + arm_use_neon && !(is_win && !is_clang) + config("lzma_sdk_config") { include_dirs = [ "." ] } -# Must be in a config because of how GN orders flags (otherwise -Wall will -# appear after this, and turn it back on). -config("clang_warnings") { - if (is_clang) { - # Upstream uses self-assignment to avoid warnings. - cflags = [ "-Wno-self-assign" ] - } -} - -config("lzma_defines") { +# Must be in a config for -Wno-self-assign because of how GN orders flags +# (otherwise -Wall will appear after this, and turn it back on). +config("lzma_build_config") { defines = [ "_7ZIP_ST", "_7Z_NO_METHODS_FILTERS", "_LZMA_PROB32", ] + + cflags = [] + if (is_clang) { + # Upstream uses self-assignment to avoid warnings. + cflags += [ "-Wno-self-assign" ] + } + + if (use_arm_neon_optimizations) { + if (is_fuchsia) { + defines += [ "ARMV8_OS_FUCHSIA" ] + } + + if (target_cpu == "arm" && arm_version >= 8) { + if (is_clang) { + cflags += [ + "-march=armv8-a", + "-Xclang", + "-target-feature", + "-Xclang", + "+crc", + "-Xclang", + "-target-feature", + "-Xclang", + "+crypto", + ] + } else { + cflags += [ "-march=armv8-a+crc+crypto" ] + } + } + } } static_library("lzma_sdk") { @@ -72,8 +102,7 @@ "//build/config/compiler:no_chromium_code", # Must be after no_chromium_code for warning flags to be ordered correctly. - ":clang_warnings", - ":lzma_defines", + ":lzma_build_config", ] public_configs = [ ":lzma_sdk_config" ] } @@ -92,6 +121,11 @@ "XzDec.c", ] + # TODO(crbug.com/1338627): Enable ARM optimizations + if (target_cpu == "x86" || target_cpu == "x64") { + sources += [ "Sha256Opt.c" ] + } + deps = [ ":lzma_sdk" ] configs -= [ "//build/config/compiler:chromium_code" ] @@ -99,8 +133,7 @@ "//build/config/compiler:no_chromium_code", # Must be after no_chromium_code for warning flags to be ordered correctly. - ":clang_warnings", - ":lzma_defines", + ":lzma_build_config", ] public_configs = [ ":lzma_sdk_config" ] }
diff --git a/third_party/lzma_sdk/Bcj2.c b/third_party/lzma_sdk/Bcj2.c index 9a0046a..c7b9567 100644 --- a/third_party/lzma_sdk/Bcj2.c +++ b/third_party/lzma_sdk/Bcj2.c
@@ -1,5 +1,5 @@ /* Bcj2.c -- BCJ2 Decoder (Converter for x86 code) -2018-04-28 : Igor Pavlov : Public domain */ +2021-02-09 : Igor Pavlov : Public domain */ #include "Precomp.h" @@ -123,7 +123,7 @@ const Byte *src = p->bufs[BCJ2_STREAM_MAIN]; const Byte *srcLim; Byte *dest; - SizeT num = p->lims[BCJ2_STREAM_MAIN] - src; + SizeT num = (SizeT)(p->lims[BCJ2_STREAM_MAIN] - src); if (num == 0) { @@ -134,7 +134,7 @@ dest = p->dest; if (num > (SizeT)(p->destLim - dest)) { - num = p->destLim - dest; + num = (SizeT)(p->destLim - dest); if (num == 0) { p->state = BCJ2_DEC_STATE_ORIG; @@ -168,7 +168,7 @@ break; } - num = src - p->bufs[BCJ2_STREAM_MAIN]; + num = (SizeT)(src - p->bufs[BCJ2_STREAM_MAIN]); if (src == srcLim) { @@ -228,7 +228,7 @@ p->ip += 4; val -= p->ip; dest = p->dest; - rem = p->destLim - dest; + rem = (SizeT)(p->destLim - dest); if (rem < 4) {
diff --git a/third_party/lzma_sdk/Bra.c b/third_party/lzma_sdk/Bra.c index aed17e3..3b854d9c 100644 --- a/third_party/lzma_sdk/Bra.c +++ b/third_party/lzma_sdk/Bra.c
@@ -1,5 +1,5 @@ /* Bra.c -- Converters for RISC code -2017-04-04 : Igor Pavlov : Public domain */ +2021-02-09 : Igor Pavlov : Public domain */ #include "Precomp.h" @@ -22,7 +22,7 @@ for (;;) { if (p >= lim) - return p - data; + return (SizeT)(p - data); p += 4; if (p[-1] == 0xEB) break; @@ -43,7 +43,7 @@ for (;;) { if (p >= lim) - return p - data; + return (SizeT)(p - data); p += 4; if (p[-1] == 0xEB) break; @@ -78,7 +78,7 @@ { UInt32 b3; if (p > lim) - return p - data; + return (SizeT)(p - data); b1 = p[1]; b3 = p[3]; p += 2; @@ -113,7 +113,7 @@ { UInt32 b3; if (p > lim) - return p - data; + return (SizeT)(p - data); b1 = p[1]; b3 = p[3]; p += 2; @@ -162,7 +162,7 @@ for (;;) { if (p >= lim) - return p - data; + return (SizeT)(p - data); p += 4; /* if ((v & 0xFC000003) == 0x48000001) */ if ((p[-4] & 0xFC) == 0x48 && (p[-1] & 3) == 1) @@ -196,7 +196,7 @@ for (;;) { if (p >= lim) - return p - data; + return (SizeT)(p - data); /* v = GetBe32(p); p += 4;
diff --git a/third_party/lzma_sdk/Bra86.c b/third_party/lzma_sdk/Bra86.c index 93ed4d76..10a0fbd1 100644 --- a/third_party/lzma_sdk/Bra86.c +++ b/third_party/lzma_sdk/Bra86.c
@@ -1,5 +1,5 @@ /* Bra86.c -- Converter for x86 code (BCJ) -2017-04-03 : Igor Pavlov : Public domain */ +2021-02-09 : Igor Pavlov : Public domain */ #include "Precomp.h" @@ -25,7 +25,7 @@ break; { - SizeT d = (SizeT)(p - data - pos); + SizeT d = (SizeT)(p - data) - pos; pos = (SizeT)(p - data); if (p >= limit) {
diff --git a/third_party/lzma_sdk/Compiler.h b/third_party/lzma_sdk/Compiler.h index 0cc409d8..a9816fa 100644 --- a/third_party/lzma_sdk/Compiler.h +++ b/third_party/lzma_sdk/Compiler.h
@@ -1,9 +1,13 @@ /* Compiler.h -2017-04-03 : Igor Pavlov : Public domain */ +2021-01-05 : Igor Pavlov : Public domain */ #ifndef __7Z_COMPILER_H #define __7Z_COMPILER_H + #ifdef __clang__ + #pragma clang diagnostic ignored "-Wunused-private-field" + #endif + #ifdef _MSC_VER #ifdef UNDER_CE @@ -25,6 +29,12 @@ #pragma warning(disable : 4786) // identifier was truncated to '255' characters in the debug information #endif + #ifdef __clang__ + #pragma clang diagnostic ignored "-Wdeprecated-declarations" + #pragma clang diagnostic ignored "-Wmicrosoft-exception-spec" + // #pragma clang diagnostic ignored "-Wreserved-id-macro" + #endif + #endif #define UNUSED_VAR(x) (void)x;
diff --git a/third_party/lzma_sdk/CpuArch.c b/third_party/lzma_sdk/CpuArch.c index 02e482e..30451fba 100644 --- a/third_party/lzma_sdk/CpuArch.c +++ b/third_party/lzma_sdk/CpuArch.c
@@ -1,5 +1,5 @@ /* CpuArch.c -- CPU specific code -2018-02-18: Igor Pavlov : Public domain */ +2021-07-13 : Igor Pavlov : Public domain */ #include "Precomp.h" @@ -55,6 +55,47 @@ #define CHECK_CPUID_IS_SUPPORTED #endif +#ifndef USE_ASM + #ifdef _MSC_VER + #if _MSC_VER >= 1600 + #define MY__cpuidex __cpuidex + #else + +/* + __cpuid (function == 4) requires subfunction number in ECX. + MSDN: The __cpuid intrinsic clears the ECX register before calling the cpuid instruction. + __cpuid() in new MSVC clears ECX. + __cpuid() in old MSVC (14.00) doesn't clear ECX + We still can use __cpuid for low (function) values that don't require ECX, + but __cpuid() in old MSVC will be incorrect for some function values: (function == 4). + So here we use the hack for old MSVC to send (subFunction) in ECX register to cpuid instruction, + where ECX value is first parameter for FAST_CALL / NO_INLINE function, + So the caller of MY__cpuidex_HACK() sets ECX as subFunction, and + old MSVC for __cpuid() doesn't change ECX and cpuid instruction gets (subFunction) value. + + DON'T remove MY_NO_INLINE and MY_FAST_CALL for MY__cpuidex_HACK() !!! +*/ + +static +MY_NO_INLINE +void MY_FAST_CALL MY__cpuidex_HACK(UInt32 subFunction, int *CPUInfo, UInt32 function) +{ + UNUSED_VAR(subFunction); + __cpuid(CPUInfo, function); +} + + #define MY__cpuidex(info, func, func2) MY__cpuidex_HACK(func2, info, func) + #pragma message("======== MY__cpuidex_HACK WAS USED ========") + #endif + #else + #define MY__cpuidex(info, func, func2) __cpuid(info, func) + #pragma message("======== (INCORRECT ?) cpuid WAS USED ========") + #endif +#endif + + + + void MyCPUID(UInt32 function, UInt32 *a, UInt32 *b, UInt32 *c, UInt32 *d) { #ifdef USE_ASM @@ -99,18 +140,20 @@ #endif "=c" (*c) , "=d" (*d) - : "0" (function)) ; + : "0" (function), "c"(0) ) ; #endif #else int CPUInfo[4]; - __cpuid(CPUInfo, function); - *a = CPUInfo[0]; - *b = CPUInfo[1]; - *c = CPUInfo[2]; - *d = CPUInfo[3]; + + MY__cpuidex(CPUInfo, (int)function, 0); + + *a = (UInt32)CPUInfo[0]; + *b = (UInt32)CPUInfo[1]; + *c = (UInt32)CPUInfo[2]; + *d = (UInt32)CPUInfo[3]; #endif } @@ -174,7 +217,7 @@ } #if !defined(MY_CPU_AMD64) && defined(_WIN32) -#include <windows.h> +#include <Windows.h> static BoolInt CPU_Sys_Is_SSE_Supported() { OSVERSIONINFO vi; @@ -188,13 +231,101 @@ #define CHECK_SYS_SSE_SUPPORT #endif -BoolInt CPU_Is_Aes_Supported() + +static UInt32 X86_CPUID_ECX_Get_Flags() +{ + Cx86cpuid p; + CHECK_SYS_SSE_SUPPORT + if (!x86cpuid_CheckAndRead(&p)) + return 0; + return p.c; +} + +BoolInt CPU_IsSupported_AES() +{ + return (X86_CPUID_ECX_Get_Flags() >> 25) & 1; +} + +BoolInt CPU_IsSupported_SSSE3() +{ + return (X86_CPUID_ECX_Get_Flags() >> 9) & 1; +} + +BoolInt CPU_IsSupported_SSE41() +{ + return (X86_CPUID_ECX_Get_Flags() >> 19) & 1; +} + +BoolInt CPU_IsSupported_SHA() { Cx86cpuid p; CHECK_SYS_SSE_SUPPORT if (!x86cpuid_CheckAndRead(&p)) return False; - return (p.c >> 25) & 1; + + if (p.maxFunc < 7) + return False; + { + UInt32 d[4] = { 0 }; + MyCPUID(7, &d[0], &d[1], &d[2], &d[3]); + return (d[1] >> 29) & 1; + } +} + +// #include <stdio.h> + +#ifdef _WIN32 +#include <Windows.h> +#endif + +BoolInt CPU_IsSupported_AVX2() +{ + Cx86cpuid p; + CHECK_SYS_SSE_SUPPORT + + #ifdef _WIN32 + #define MY__PF_XSAVE_ENABLED 17 + if (!IsProcessorFeaturePresent(MY__PF_XSAVE_ENABLED)) + return False; + #endif + + if (!x86cpuid_CheckAndRead(&p)) + return False; + if (p.maxFunc < 7) + return False; + { + UInt32 d[4] = { 0 }; + MyCPUID(7, &d[0], &d[1], &d[2], &d[3]); + // printf("\ncpuid(7): ebx=%8x ecx=%8x\n", d[1], d[2]); + return 1 + & (d[1] >> 5); // avx2 + } +} + +BoolInt CPU_IsSupported_VAES_AVX2() +{ + Cx86cpuid p; + CHECK_SYS_SSE_SUPPORT + + #ifdef _WIN32 + #define MY__PF_XSAVE_ENABLED 17 + if (!IsProcessorFeaturePresent(MY__PF_XSAVE_ENABLED)) + return False; + #endif + + if (!x86cpuid_CheckAndRead(&p)) + return False; + if (p.maxFunc < 7) + return False; + { + UInt32 d[4] = { 0 }; + MyCPUID(7, &d[0], &d[1], &d[2], &d[3]); + // printf("\ncpuid(7): ebx=%8x ecx=%8x\n", d[1], d[2]); + return 1 + & (d[1] >> 5) // avx2 + // & (d[1] >> 31) // avx512vl + & (d[2] >> 9); // vaes // VEX-256/EVEX + } } BoolInt CPU_IsSupported_PageGB() @@ -215,4 +346,135 @@ } } + +#elif defined(MY_CPU_ARM_OR_ARM64) + +#ifdef _WIN32 + +#include <Windows.h> + +BoolInt CPU_IsSupported_CRC32() { return IsProcessorFeaturePresent(PF_ARM_V8_CRC32_INSTRUCTIONS_AVAILABLE) ? 1 : 0; } +BoolInt CPU_IsSupported_CRYPTO() { return IsProcessorFeaturePresent(PF_ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE) ? 1 : 0; } +BoolInt CPU_IsSupported_NEON() { return IsProcessorFeaturePresent(PF_ARM_NEON_INSTRUCTIONS_AVAILABLE) ? 1 : 0; } + +#else + +#if defined(__APPLE__) + +/* +#include <stdio.h> +#include <string.h> +static void Print_sysctlbyname(const char *name) +{ + size_t bufSize = 256; + char buf[256]; + int res = sysctlbyname(name, &buf, &bufSize, NULL, 0); + { + int i; + printf("\nres = %d : %s : '%s' : bufSize = %d, numeric", res, name, buf, (unsigned)bufSize); + for (i = 0; i < 20; i++) + printf(" %2x", (unsigned)(Byte)buf[i]); + + } +} +*/ + +static BoolInt My_sysctlbyname_Get_BoolInt(const char *name) +{ + UInt32 val = 0; + if (My_sysctlbyname_Get_UInt32(name, &val) == 0 && val == 1) + return 1; + return 0; +} + + /* + Print_sysctlbyname("hw.pagesize"); + Print_sysctlbyname("machdep.cpu.brand_string"); + */ + +BoolInt CPU_IsSupported_CRC32(void) +{ + return My_sysctlbyname_Get_BoolInt("hw.optional.armv8_crc32"); +} + +BoolInt CPU_IsSupported_NEON(void) +{ + return My_sysctlbyname_Get_BoolInt("hw.optional.neon"); +} + +#ifdef MY_CPU_ARM64 +#define APPLE_CRYPTO_SUPPORT_VAL 1 +#else +#define APPLE_CRYPTO_SUPPORT_VAL 0 +#endif + +BoolInt CPU_IsSupported_SHA1(void) { return APPLE_CRYPTO_SUPPORT_VAL; } +BoolInt CPU_IsSupported_SHA2(void) { return APPLE_CRYPTO_SUPPORT_VAL; } +BoolInt CPU_IsSupported_AES (void) { return APPLE_CRYPTO_SUPPORT_VAL; } + + +#else // __APPLE__ + +#include <sys/auxv.h> + +#if !defined(ARMV8_OS_FUCHSIA) +#define USE_HWCAP +#endif // !defined(ARMV8_OS_FUCHSIA) + +#ifdef USE_HWCAP + +#include <asm/hwcap.h> + + #define MY_HWCAP_CHECK_FUNC_2(name1, name2) \ + BoolInt CPU_IsSupported_ ## name1() { return (getauxval(AT_HWCAP) & (HWCAP_ ## name2)) ? 1 : 0; } + +#ifdef MY_CPU_ARM64 + #define MY_HWCAP_CHECK_FUNC(name) \ + MY_HWCAP_CHECK_FUNC_2(name, name) + MY_HWCAP_CHECK_FUNC_2(NEON, ASIMD) +// MY_HWCAP_CHECK_FUNC (ASIMD) +#elif defined(MY_CPU_ARM) + #define MY_HWCAP_CHECK_FUNC(name) \ + BoolInt CPU_IsSupported_ ## name() { return (getauxval(AT_HWCAP2) & (HWCAP2_ ## name)) ? 1 : 0; } + MY_HWCAP_CHECK_FUNC_2(NEON, NEON) +#endif + +#else // USE_HWCAP + + #define MY_HWCAP_CHECK_FUNC(name) \ + BoolInt CPU_IsSupported_ ## name() { return 0; } + MY_HWCAP_CHECK_FUNC(NEON) + +#endif // USE_HWCAP + +MY_HWCAP_CHECK_FUNC (CRC32) +MY_HWCAP_CHECK_FUNC (SHA1) +MY_HWCAP_CHECK_FUNC (SHA2) +MY_HWCAP_CHECK_FUNC (AES) + +#endif // __APPLE__ +#endif // _WIN32 + +#endif // MY_CPU_ARM_OR_ARM64 + + + +#ifdef __APPLE__ + +#include <sys/sysctl.h> + +int My_sysctlbyname_Get(const char *name, void *buf, size_t *bufSize) +{ + return sysctlbyname(name, buf, bufSize, NULL, 0); +} + +int My_sysctlbyname_Get_UInt32(const char *name, UInt32 *val) +{ + size_t bufSize = sizeof(*val); + int res = My_sysctlbyname_Get(name, val, &bufSize); + if (res == 0 && bufSize != sizeof(*val)) + return EFAULT; + return res; +} + #endif
diff --git a/third_party/lzma_sdk/CpuArch.h b/third_party/lzma_sdk/CpuArch.h index bd429388..529d3a5 100644 --- a/third_party/lzma_sdk/CpuArch.h +++ b/third_party/lzma_sdk/CpuArch.h
@@ -1,5 +1,5 @@ /* CpuArch.h -- CPU specific code -2018-02-18 : Igor Pavlov : Public domain */ +2021-07-13 : Igor Pavlov : Public domain */ #ifndef __CPU_ARCH_H #define __CPU_ARCH_H @@ -14,6 +14,10 @@ If MY_CPU_LE and MY_CPU_BE are not defined, we don't know about ENDIANNESS of platform. MY_CPU_LE_UNALIGN means that CPU is LITTLE ENDIAN and CPU supports unaligned memory accesses. + +MY_CPU_64BIT means that processor can work with 64-bit registers. + MY_CPU_64BIT can be used to select fast code branch + MY_CPU_64BIT doesn't mean that (sizeof(void *) == 8) */ #if defined(_M_X64) \ @@ -24,8 +28,10 @@ #define MY_CPU_AMD64 #ifdef __ILP32__ #define MY_CPU_NAME "x32" + #define MY_CPU_SIZEOF_POINTER 4 #else #define MY_CPU_NAME "x64" + #define MY_CPU_SIZEOF_POINTER 8 #endif #define MY_CPU_64BIT #endif @@ -35,7 +41,8 @@ || defined(__i386__) #define MY_CPU_X86 #define MY_CPU_NAME "x86" - #define MY_CPU_32BIT + /* #define MY_CPU_32BIT */ + #define MY_CPU_SIZEOF_POINTER 4 #endif @@ -59,8 +66,14 @@ || defined(__THUMBEL__) \ || defined(__THUMBEB__) #define MY_CPU_ARM - #define MY_CPU_NAME "arm" - #define MY_CPU_32BIT + + #if defined(__thumb__) || defined(__THUMBEL__) || defined(_M_ARMT) + #define MY_CPU_NAME "armt" + #else + #define MY_CPU_NAME "arm" + #endif + /* #define MY_CPU_32BIT */ + #define MY_CPU_SIZEOF_POINTER 4 #endif @@ -84,17 +97,29 @@ #if defined(__ppc64__) \ - || defined(__powerpc64__) + || defined(__powerpc64__) \ + || defined(__ppc__) \ + || defined(__powerpc__) \ + || defined(__PPC__) \ + || defined(_POWER) + +#if defined(__ppc64__) \ + || defined(__powerpc64__) \ + || defined(_LP64) \ + || defined(__64BIT__) #ifdef __ILP32__ #define MY_CPU_NAME "ppc64-32" + #define MY_CPU_SIZEOF_POINTER 4 #else #define MY_CPU_NAME "ppc64" + #define MY_CPU_SIZEOF_POINTER 8 #endif #define MY_CPU_64BIT -#elif defined(__ppc__) \ - || defined(__powerpc__) +#else #define MY_CPU_NAME "ppc" - #define MY_CPU_32BIT + #define MY_CPU_SIZEOF_POINTER 4 + /* #define MY_CPU_32BIT */ +#endif #endif @@ -111,6 +136,10 @@ #define MY_CPU_X86_OR_AMD64 #endif +#if defined(MY_CPU_ARM) || defined(MY_CPU_ARM64) +#define MY_CPU_ARM_OR_ARM64 +#endif + #ifdef _WIN32 @@ -170,6 +199,40 @@ #error Stop_Compiling_Bad_32_64_BIT #endif +#ifdef __SIZEOF_POINTER__ + #ifdef MY_CPU_SIZEOF_POINTER + #if MY_CPU_SIZEOF_POINTER != __SIZEOF_POINTER__ + #error Stop_Compiling_Bad_MY_CPU_PTR_SIZE + #endif + #else + #define MY_CPU_SIZEOF_POINTER __SIZEOF_POINTER__ + #endif +#endif + +#if defined(MY_CPU_SIZEOF_POINTER) && (MY_CPU_SIZEOF_POINTER == 4) +#if defined (_LP64) + #error Stop_Compiling_Bad_MY_CPU_PTR_SIZE +#endif +#endif + +#ifdef _MSC_VER + #if _MSC_VER >= 1300 + #define MY_CPU_pragma_pack_push_1 __pragma(pack(push, 1)) + #define MY_CPU_pragma_pop __pragma(pack(pop)) + #else + #define MY_CPU_pragma_pack_push_1 + #define MY_CPU_pragma_pop + #endif +#else + #ifdef __xlC__ + #define MY_CPU_pragma_pack_push_1 _Pragma("pack(1)") + #define MY_CPU_pragma_pop _Pragma("pack()") + #else + #define MY_CPU_pragma_pack_push_1 _Pragma("pack(push, 1)") + #define MY_CPU_pragma_pop _Pragma("pack(pop)") + #endif +#endif + #ifndef MY_CPU_NAME #ifdef MY_CPU_LE @@ -189,8 +252,12 @@ #ifdef MY_CPU_LE #if defined(MY_CPU_X86_OR_AMD64) \ - || defined(MY_CPU_ARM64) \ - || defined(__ARM_FEATURE_UNALIGNED) + || defined(MY_CPU_ARM64) + #define MY_CPU_LE_UNALIGN + #define MY_CPU_LE_UNALIGN_64 + #elif defined(__ARM_FEATURE_UNALIGNED) + /* gcc9 for 32-bit arm can use LDRD instruction that requires 32-bit alignment. + So we can't use unaligned 64-bit operations. */ #define MY_CPU_LE_UNALIGN #endif #endif @@ -200,11 +267,15 @@ #define GetUi16(p) (*(const UInt16 *)(const void *)(p)) #define GetUi32(p) (*(const UInt32 *)(const void *)(p)) +#ifdef MY_CPU_LE_UNALIGN_64 #define GetUi64(p) (*(const UInt64 *)(const void *)(p)) +#endif -#define SetUi16(p, v) { *(UInt16 *)(p) = (v); } -#define SetUi32(p, v) { *(UInt32 *)(p) = (v); } -#define SetUi64(p, v) { *(UInt64 *)(p) = (v); } +#define SetUi16(p, v) { *(UInt16 *)(void *)(p) = (v); } +#define SetUi32(p, v) { *(UInt32 *)(void *)(p) = (v); } +#ifdef MY_CPU_LE_UNALIGN_64 +#define SetUi64(p, v) { *(UInt64 *)(void *)(p) = (v); } +#endif #else @@ -218,8 +289,6 @@ ((UInt32)((const Byte *)(p))[2] << 16) | \ ((UInt32)((const Byte *)(p))[3] << 24)) -#define GetUi64(p) (GetUi32(p) | ((UInt64)GetUi32(((const Byte *)(p)) + 4) << 32)) - #define SetUi16(p, v) { Byte *_ppp_ = (Byte *)(p); UInt32 _vvv_ = (v); \ _ppp_[0] = (Byte)_vvv_; \ _ppp_[1] = (Byte)(_vvv_ >> 8); } @@ -230,19 +299,29 @@ _ppp_[2] = (Byte)(_vvv_ >> 16); \ _ppp_[3] = (Byte)(_vvv_ >> 24); } +#endif + + +#ifndef MY_CPU_LE_UNALIGN_64 + +#define GetUi64(p) (GetUi32(p) | ((UInt64)GetUi32(((const Byte *)(p)) + 4) << 32)) + #define SetUi64(p, v) { Byte *_ppp2_ = (Byte *)(p); UInt64 _vvv2_ = (v); \ SetUi32(_ppp2_ , (UInt32)_vvv2_); \ SetUi32(_ppp2_ + 4, (UInt32)(_vvv2_ >> 32)); } #endif + + + #ifdef __has_builtin #define MY__has_builtin(x) __has_builtin(x) #else #define MY__has_builtin(x) 0 #endif -#if defined(MY_CPU_LE_UNALIGN) && /* defined(_WIN64) && */ (_MSC_VER >= 1300) +#if defined(MY_CPU_LE_UNALIGN) && /* defined(_WIN64) && */ defined(_MSC_VER) && (_MSC_VER >= 1300) /* Note: we use bswap instruction, that is unsupported in 386 cpu */ @@ -253,8 +332,8 @@ #pragma intrinsic(_byteswap_uint64) /* #define GetBe16(p) _byteswap_ushort(*(const UInt16 *)(const Byte *)(p)) */ -#define GetBe32(p) _byteswap_ulong(*(const UInt32 *)(const Byte *)(p)) -#define GetBe64(p) _byteswap_uint64(*(const UInt64 *)(const Byte *)(p)) +#define GetBe32(p) _byteswap_ulong (*(const UInt32 *)(const void *)(p)) +#define GetBe64(p) _byteswap_uint64(*(const UInt64 *)(const void *)(p)) #define SetBe32(p, v) (*(UInt32 *)(void *)(p)) = _byteswap_ulong(v) @@ -262,9 +341,9 @@ (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3))) \ || (defined(__clang__) && MY__has_builtin(__builtin_bswap16)) ) -/* #define GetBe16(p) __builtin_bswap16(*(const UInt16 *)(const Byte *)(p)) */ -#define GetBe32(p) __builtin_bswap32(*(const UInt32 *)(const Byte *)(p)) -#define GetBe64(p) __builtin_bswap64(*(const UInt64 *)(const Byte *)(p)) +/* #define GetBe16(p) __builtin_bswap16(*(const UInt16 *)(const void *)(p)) */ +#define GetBe32(p) __builtin_bswap32(*(const UInt32 *)(const void *)(p)) +#define GetBe64(p) __builtin_bswap64(*(const UInt64 *)(const void *)(p)) #define SetBe32(p, v) (*(UInt32 *)(void *)(p)) = __builtin_bswap32(v) @@ -325,10 +404,37 @@ #define x86cpuid_GetModel(ver) (((ver >> 12) & 0xF0) | ((ver >> 4) & 0xF)) #define x86cpuid_GetStepping(ver) (ver & 0xF) -BoolInt CPU_Is_InOrder(); -BoolInt CPU_Is_Aes_Supported(); -BoolInt CPU_IsSupported_PageGB(); +BoolInt CPU_Is_InOrder(void); +BoolInt CPU_IsSupported_AES(void); +BoolInt CPU_IsSupported_AVX2(void); +BoolInt CPU_IsSupported_VAES_AVX2(void); +BoolInt CPU_IsSupported_SSSE3(void); +BoolInt CPU_IsSupported_SSE41(void); +BoolInt CPU_IsSupported_SHA(void); +BoolInt CPU_IsSupported_PageGB(void); + +#elif defined(MY_CPU_ARM_OR_ARM64) + +BoolInt CPU_IsSupported_CRC32(void); +BoolInt CPU_IsSupported_NEON(void); + +#if defined(_WIN32) +BoolInt CPU_IsSupported_CRYPTO(void); +#define CPU_IsSupported_SHA1 CPU_IsSupported_CRYPTO +#define CPU_IsSupported_SHA2 CPU_IsSupported_CRYPTO +#define CPU_IsSupported_AES CPU_IsSupported_CRYPTO +#else +BoolInt CPU_IsSupported_SHA1(void); +BoolInt CPU_IsSupported_SHA2(void); +BoolInt CPU_IsSupported_AES(void); +#endif + +#endif + +#if defined(__APPLE__) +int My_sysctlbyname_Get(const char *name, void *buf, size_t *bufSize); +int My_sysctlbyname_Get_UInt32(const char *name, UInt32 *val); #endif EXTERN_C_END
diff --git a/third_party/lzma_sdk/Delta.c b/third_party/lzma_sdk/Delta.c index e3edd21e..c4a4499 100644 --- a/third_party/lzma_sdk/Delta.c +++ b/third_party/lzma_sdk/Delta.c
@@ -1,5 +1,5 @@ /* Delta.c -- Delta converter -2009-05-26 : Igor Pavlov : Public domain */ +2021-02-09 : Igor Pavlov : Public domain */ #include "Precomp.h" @@ -12,53 +12,158 @@ state[i] = 0; } -static void MyMemCpy(Byte *dest, const Byte *src, unsigned size) -{ - unsigned i; - for (i = 0; i < size; i++) - dest[i] = src[i]; -} void Delta_Encode(Byte *state, unsigned delta, Byte *data, SizeT size) { - Byte buf[DELTA_STATE_SIZE]; - unsigned j = 0; - MyMemCpy(buf, state, delta); + Byte temp[DELTA_STATE_SIZE]; + + if (size == 0) + return; + { - SizeT i; - for (i = 0; i < size;) + unsigned i = 0; + do + temp[i] = state[i]; + while (++i != delta); + } + + if (size <= delta) + { + unsigned i = 0, k; + do { - for (j = 0; j < delta && i < size; i++, j++) + Byte b = *data; + *data++ = (Byte)(b - temp[i]); + temp[i] = b; + } + while (++i != size); + + k = 0; + + do + { + if (i == delta) + i = 0; + state[k] = temp[i++]; + } + while (++k != delta); + + return; + } + + { + Byte *p = data + size - delta; + { + unsigned i = 0; + do + state[i] = *p++; + while (++i != delta); + } + { + const Byte *lim = data + delta; + ptrdiff_t dif = -(ptrdiff_t)delta; + + if (((ptrdiff_t)size + dif) & 1) { - Byte b = data[i]; - data[i] = (Byte)(b - buf[j]); - buf[j] = b; + --p; *p = (Byte)(*p - p[dif]); } + + while (p != lim) + { + --p; *p = (Byte)(*p - p[dif]); + --p; *p = (Byte)(*p - p[dif]); + } + + dif = -dif; + + do + { + --p; *p = (Byte)(*p - temp[--dif]); + } + while (dif != 0); } } - if (j == delta) - j = 0; - MyMemCpy(state, buf + j, delta - j); - MyMemCpy(state + delta - j, buf, j); } + void Delta_Decode(Byte *state, unsigned delta, Byte *data, SizeT size) { - Byte buf[DELTA_STATE_SIZE]; - unsigned j = 0; - MyMemCpy(buf, state, delta); + unsigned i; + const Byte *lim; + + if (size == 0) + return; + + i = 0; + lim = data + size; + + if (size <= delta) { - SizeT i; - for (i = 0; i < size;) + do + *data = (Byte)(*data + state[i++]); + while (++data != lim); + + for (; delta != i; state++, delta--) + *state = state[i]; + data -= i; + } + else + { + /* + #define B(n) b ## n + #define I(n) Byte B(n) = state[n]; + #define U(n) { B(n) = (Byte)((B(n)) + *data++); data[-1] = (B(n)); } + #define F(n) if (data != lim) { U(n) } + + if (delta == 1) { - for (j = 0; j < delta && i < size; i++, j++) + I(0) + if ((lim - data) & 1) { U(0) } + while (data != lim) { U(0) U(0) } + data -= 1; + } + else if (delta == 2) + { + I(0) I(1) + lim -= 1; while (data < lim) { U(0) U(1) } + lim += 1; F(0) + data -= 2; + } + else if (delta == 3) + { + I(0) I(1) I(2) + lim -= 2; while (data < lim) { U(0) U(1) U(2) } + lim += 2; F(0) F(1) + data -= 3; + } + else if (delta == 4) + { + I(0) I(1) I(2) I(3) + lim -= 3; while (data < lim) { U(0) U(1) U(2) U(3) } + lim += 3; F(0) F(1) F(2) + data -= 4; + } + else + */ + { + do { - buf[j] = data[i] = (Byte)(buf[j] + data[i]); + *data = (Byte)(*data + state[i++]); + data++; + } + while (i != delta); + + { + ptrdiff_t dif = -(ptrdiff_t)delta; + do + *data = (Byte)(*data + data[dif]); + while (++data != lim); + data += dif; } } } - if (j == delta) - j = 0; - MyMemCpy(state, buf + j, delta - j); - MyMemCpy(state + delta - j, buf, j); + + do + *state++ = *data; + while (++data != lim); }
diff --git a/third_party/lzma_sdk/DllSecur.c b/third_party/lzma_sdk/DllSecur.c index 5ea108a..d81508c 100644 --- a/third_party/lzma_sdk/DllSecur.c +++ b/third_party/lzma_sdk/DllSecur.c
@@ -1,11 +1,11 @@ /* DllSecur.c -- DLL loading security -2018-02-21 : Igor Pavlov : Public domain */ +2021-12-25 : Igor Pavlov : Public domain */ #include "Precomp.h" #ifdef _WIN32 -#include <windows.h> +#include <Windows.h> #include "DllSecur.h" @@ -33,17 +33,19 @@ #endif +// #define MY_CAST_FUNC (void(*)()) +#define MY_CAST_FUNC + void My_SetDefaultDllDirectories() { #ifndef UNDER_CE OSVERSIONINFO vi; vi.dwOSVersionInfoSize = sizeof(vi); - GetVersionEx(&vi); if (!GetVersionEx(&vi) || vi.dwMajorVersion != 6 || vi.dwMinorVersion != 0) { Func_SetDefaultDllDirectories setDllDirs = (Func_SetDefaultDllDirectories) - GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), "SetDefaultDllDirectories"); + MY_CAST_FUNC GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), "SetDefaultDllDirectories"); if (setDllDirs) if (setDllDirs(MY_LOAD_LIBRARY_SEARCH_SYSTEM32 | MY_LOAD_LIBRARY_SEARCH_USER_DIRS)) return; @@ -66,7 +68,7 @@ if (!GetVersionEx(&vi) || vi.dwMajorVersion != 6 || vi.dwMinorVersion != 0) { Func_SetDefaultDllDirectories setDllDirs = (Func_SetDefaultDllDirectories) - GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), "SetDefaultDllDirectories"); + MY_CAST_FUNC GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), "SetDefaultDllDirectories"); if (setDllDirs) if (setDllDirs(MY_LOAD_LIBRARY_SEARCH_SYSTEM32 | MY_LOAD_LIBRARY_SEARCH_USER_DIRS)) return;
diff --git a/third_party/lzma_sdk/DllSecur.h b/third_party/lzma_sdk/DllSecur.h index e2a049ad..64ff26c 100644 --- a/third_party/lzma_sdk/DllSecur.h +++ b/third_party/lzma_sdk/DllSecur.h
@@ -10,8 +10,8 @@ #ifdef _WIN32 -void My_SetDefaultDllDirectories(); -void LoadSecurityDlls(); +void My_SetDefaultDllDirectories(void); +void LoadSecurityDlls(void); #endif
diff --git a/third_party/lzma_sdk/Executable/7za.exe b/third_party/lzma_sdk/Executable/7za.exe index 2bdd57d2..9544a638 100755 --- a/third_party/lzma_sdk/Executable/7za.exe +++ b/third_party/lzma_sdk/Executable/7za.exe Binary files differ
diff --git a/third_party/lzma_sdk/LzFind.c b/third_party/lzma_sdk/LzFind.c index df55e86..36f73309 100644 --- a/third_party/lzma_sdk/LzFind.c +++ b/third_party/lzma_sdk/LzFind.c
@@ -1,20 +1,69 @@ /* LzFind.c -- Match finder for LZ algorithms -2018-07-08 : Igor Pavlov : Public domain */ +2021-11-29 : Igor Pavlov : Public domain */ #include "Precomp.h" #include <string.h> +// #include <stdio.h> +#include "CpuArch.h" #include "LzFind.h" #include "LzHash.h" -#define kEmptyHashValue 0 -#define kMaxValForNormalize ((UInt32)0xFFFFFFFF) -#define kNormalizeStepMin (1 << 10) /* it must be power of 2 */ -#define kNormalizeMask (~(UInt32)(kNormalizeStepMin - 1)) -#define kMaxHistorySize ((UInt32)7 << 29) +#define kBlockMoveAlign (1 << 7) // alignment for memmove() +#define kBlockSizeAlign (1 << 16) // alignment for block allocation +#define kBlockSizeReserveMin (1 << 24) // it's 1/256 from 4 GB dictinary -#define kStartMaxLen 3 +#define kEmptyHashValue 0 + +#define kMaxValForNormalize ((UInt32)0) +// #define kMaxValForNormalize ((UInt32)(1 << 20) + 0xFFF) // for debug + +// #define kNormalizeAlign (1 << 7) // alignment for speculated accesses + +#define GET_AVAIL_BYTES(p) \ + Inline_MatchFinder_GetNumAvailableBytes(p) + + +// #define kFix5HashSize (kHash2Size + kHash3Size + kHash4Size) +#define kFix5HashSize kFix4HashSize + +/* + HASH2_CALC: + if (hv) match, then cur[0] and cur[1] also match +*/ +#define HASH2_CALC hv = GetUi16(cur); + +// (crc[0 ... 255] & 0xFF) provides one-to-one correspondence to [0 ... 255] + +/* + HASH3_CALC: + if (cur[0]) and (h2) match, then cur[1] also match + if (cur[0]) and (hv) match, then cur[1] and cur[2] also match +*/ +#define HASH3_CALC { \ + UInt32 temp = p->crc[cur[0]] ^ cur[1]; \ + h2 = temp & (kHash2Size - 1); \ + hv = (temp ^ ((UInt32)cur[2] << 8)) & p->hashMask; } + +#define HASH4_CALC { \ + UInt32 temp = p->crc[cur[0]] ^ cur[1]; \ + h2 = temp & (kHash2Size - 1); \ + temp ^= ((UInt32)cur[2] << 8); \ + h3 = temp & (kHash3Size - 1); \ + hv = (temp ^ (p->crc[cur[3]] << kLzHash_CrcShift_1)) & p->hashMask; } + +#define HASH5_CALC { \ + UInt32 temp = p->crc[cur[0]] ^ cur[1]; \ + h2 = temp & (kHash2Size - 1); \ + temp ^= ((UInt32)cur[2] << 8); \ + h3 = temp & (kHash3Size - 1); \ + temp ^= (p->crc[cur[3]] << kLzHash_CrcShift_1); \ + /* h4 = temp & p->hash4Mask; */ /* (kHash4Size - 1); */ \ + hv = (temp ^ (p->crc[cur[4]] << kLzHash_CrcShift_2)) & p->hashMask; } + +#define HASH_ZIP_CALC hv = ((cur[2] | ((UInt32)cur[0] << 8)) ^ p->crc[cur[1]]) & 0xFFFF; + static void LzInWindow_Free(CMatchFinder *p, ISzAllocPtr alloc) { @@ -25,46 +74,57 @@ } } -/* keepSizeBefore + keepSizeAfter + keepSizeReserv must be < 4G) */ -static int LzInWindow_Create(CMatchFinder *p, UInt32 keepSizeReserv, ISzAllocPtr alloc) +static int LzInWindow_Create2(CMatchFinder *p, UInt32 blockSize, ISzAllocPtr alloc) { - UInt32 blockSize = p->keepSizeBefore + p->keepSizeAfter + keepSizeReserv; - if (p->directInput) - { - p->blockSize = blockSize; - return 1; - } + if (blockSize == 0) + return 0; if (!p->bufferBase || p->blockSize != blockSize) { + // size_t blockSizeT; LzInWindow_Free(p, alloc); p->blockSize = blockSize; - p->bufferBase = (Byte *)ISzAlloc_Alloc(alloc, (size_t)blockSize); + // blockSizeT = blockSize; + + // printf("\nblockSize = 0x%x\n", blockSize); + /* + #if defined _WIN64 + // we can allocate 4GiB, but still use UInt32 for (p->blockSize) + // we use UInt32 type for (p->blockSize), because + // we don't want to wrap over 4 GiB, + // when we use (p->streamPos - p->pos) that is UInt32. + if (blockSize >= (UInt32)0 - (UInt32)kBlockSizeAlign) + { + blockSizeT = ((size_t)1 << 32); + printf("\nchanged to blockSizeT = 4GiB\n"); + } + #endif + */ + + p->bufferBase = (Byte *)ISzAlloc_Alloc(alloc, blockSize); + // printf("\nbufferBase = %p\n", p->bufferBase); + // return 0; // for debug } return (p->bufferBase != NULL); } -Byte *MatchFinder_GetPointerToCurrentPos(CMatchFinder *p) { return p->buffer; } +static const Byte *MatchFinder_GetPointerToCurrentPos(CMatchFinder *p) { return p->buffer; } -UInt32 MatchFinder_GetNumAvailableBytes(CMatchFinder *p) { return p->streamPos - p->pos; } +static UInt32 MatchFinder_GetNumAvailableBytes(CMatchFinder *p) { return GET_AVAIL_BYTES(p); } -void MatchFinder_ReduceOffsets(CMatchFinder *p, UInt32 subValue) -{ - p->posLimit -= subValue; - p->pos -= subValue; - p->streamPos -= subValue; -} +MY_NO_INLINE static void MatchFinder_ReadBlock(CMatchFinder *p) { if (p->streamEndWasReached || p->result != SZ_OK) return; - /* We use (p->streamPos - p->pos) value. (p->streamPos < p->pos) is allowed. */ + /* We use (p->streamPos - p->pos) value. + (p->streamPos < p->pos) is allowed. */ if (p->directInput) { - UInt32 curSize = 0xFFFFFFFF - (p->streamPos - p->pos); + UInt32 curSize = 0xFFFFFFFF - GET_AVAIL_BYTES(p); if (curSize > p->directInputRem) curSize = (UInt32)p->directInputRem; p->directInputRem -= curSize; @@ -76,10 +136,22 @@ for (;;) { - Byte *dest = p->buffer + (p->streamPos - p->pos); - size_t size = (p->bufferBase + p->blockSize - dest); + Byte *dest = p->buffer + GET_AVAIL_BYTES(p); + size_t size = (size_t)(p->bufferBase + p->blockSize - dest); if (size == 0) + { + /* we call ReadBlock() after NeedMove() and MoveBlock(). + NeedMove() and MoveBlock() povide more than (keepSizeAfter) + to the end of (blockSize). + So we don't execute this branch in normal code flow. + We can go here, if we will call ReadBlock() before NeedMove(), MoveBlock(). + */ + // p->result = SZ_ERROR_FAIL; // we can show error here return; + } + + // #define kRead 3 + // if (size > kRead) size = kRead; // for debug p->result = ISeqInStream_Read(p->stream, dest, &size); if (p->result != SZ_OK) @@ -90,41 +162,52 @@ return; } p->streamPos += (UInt32)size; - if (p->streamPos - p->pos > p->keepSizeAfter) + if (GET_AVAIL_BYTES(p) > p->keepSizeAfter) return; + /* here and in another (p->keepSizeAfter) checks we keep on 1 byte more than was requested by Create() function + (GET_AVAIL_BYTES(p) >= p->keepSizeAfter) - minimal required size */ } + + // on exit: (p->result != SZ_OK || p->streamEndWasReached || GET_AVAIL_BYTES(p) > p->keepSizeAfter) } + + +MY_NO_INLINE void MatchFinder_MoveBlock(CMatchFinder *p) { + const size_t offset = (size_t)(p->buffer - p->bufferBase) - p->keepSizeBefore; + const size_t keepBefore = (offset & (kBlockMoveAlign - 1)) + p->keepSizeBefore; + p->buffer = p->bufferBase + keepBefore; memmove(p->bufferBase, - p->buffer - p->keepSizeBefore, - (size_t)(p->streamPos - p->pos) + p->keepSizeBefore); - p->buffer = p->bufferBase + p->keepSizeBefore; + p->bufferBase + (offset & ~((size_t)kBlockMoveAlign - 1)), + keepBefore + (size_t)GET_AVAIL_BYTES(p)); } +/* We call MoveBlock() before ReadBlock(). + So MoveBlock() can be wasteful operation, if the whole input data + can fit in current block even without calling MoveBlock(). + in important case where (dataSize <= historySize) + condition (p->blockSize > dataSize + p->keepSizeAfter) is met + So there is no MoveBlock() in that case case. +*/ + int MatchFinder_NeedMove(CMatchFinder *p) { if (p->directInput) return 0; - /* if (p->streamEndWasReached) return 0; */ + if (p->streamEndWasReached || p->result != SZ_OK) + return 0; return ((size_t)(p->bufferBase + p->blockSize - p->buffer) <= p->keepSizeAfter); } void MatchFinder_ReadIfRequired(CMatchFinder *p) { - if (p->streamEndWasReached) - return; - if (p->keepSizeAfter >= p->streamPos - p->pos) + if (p->keepSizeAfter >= GET_AVAIL_BYTES(p)) MatchFinder_ReadBlock(p); } -static void MatchFinder_CheckAndMoveAndRead(CMatchFinder *p) -{ - if (MatchFinder_NeedMove(p)) - MatchFinder_MoveBlock(p); - MatchFinder_ReadBlock(p); -} + static void MatchFinder_SetDefaultSettings(CMatchFinder *p) { @@ -175,39 +258,74 @@ return (CLzRef *)ISzAlloc_Alloc(alloc, sizeInBytes); } +#if (kBlockSizeReserveMin < kBlockSizeAlign * 2) + #error Stop_Compiling_Bad_Reserve +#endif + + + +static UInt32 GetBlockSize(CMatchFinder *p, UInt32 historySize) +{ + UInt32 blockSize = (p->keepSizeBefore + p->keepSizeAfter); + /* + if (historySize > kMaxHistorySize) + return 0; + */ + // printf("\nhistorySize == 0x%x\n", historySize); + + if (p->keepSizeBefore < historySize || blockSize < p->keepSizeBefore) // if 32-bit overflow + return 0; + + { + const UInt32 kBlockSizeMax = (UInt32)0 - (UInt32)kBlockSizeAlign; + const UInt32 rem = kBlockSizeMax - blockSize; + const UInt32 reserve = (blockSize >> (blockSize < ((UInt32)1 << 30) ? 1 : 2)) + + (1 << 12) + kBlockMoveAlign + kBlockSizeAlign; // do not overflow 32-bit here + if (blockSize >= kBlockSizeMax + || rem < kBlockSizeReserveMin) // we reject settings that will be slow + return 0; + if (reserve >= rem) + blockSize = kBlockSizeMax; + else + { + blockSize += reserve; + blockSize &= ~(UInt32)(kBlockSizeAlign - 1); + } + } + // printf("\n LzFind_blockSize = %x\n", blockSize); + // printf("\n LzFind_blockSize = %d\n", blockSize >> 20); + return blockSize; +} + + int MatchFinder_Create(CMatchFinder *p, UInt32 historySize, UInt32 keepAddBufferBefore, UInt32 matchMaxLen, UInt32 keepAddBufferAfter, ISzAllocPtr alloc) { - UInt32 sizeReserv; - - if (historySize > kMaxHistorySize) - { - MatchFinder_Free(p, alloc); - return 0; - } - - sizeReserv = historySize >> 1; - if (historySize >= ((UInt32)3 << 30)) sizeReserv = historySize >> 3; - else if (historySize >= ((UInt32)2 << 30)) sizeReserv = historySize >> 2; - - sizeReserv += (keepAddBufferBefore + matchMaxLen + keepAddBufferAfter) / 2 + (1 << 19); - + /* we need one additional byte in (p->keepSizeBefore), + since we use MoveBlock() after (p->pos++) and before dictionary using */ + // keepAddBufferBefore = (UInt32)0xFFFFFFFF - (1 << 22); // for debug p->keepSizeBefore = historySize + keepAddBufferBefore + 1; - p->keepSizeAfter = matchMaxLen + keepAddBufferAfter; - - /* we need one additional byte, since we use MoveBlock after pos++ and before dictionary using */ - - if (LzInWindow_Create(p, sizeReserv, alloc)) + + keepAddBufferAfter += matchMaxLen; + /* we need (p->keepSizeAfter >= p->numHashBytes) */ + if (keepAddBufferAfter < p->numHashBytes) + keepAddBufferAfter = p->numHashBytes; + // keepAddBufferAfter -= 2; // for debug + p->keepSizeAfter = keepAddBufferAfter; + + if (p->directInput) + p->blockSize = 0; + if (p->directInput || LzInWindow_Create2(p, GetBlockSize(p, historySize), alloc)) { - UInt32 newCyclicBufferSize = historySize + 1; + const UInt32 newCyclicBufferSize = historySize + 1; // do not change it UInt32 hs; p->matchMaxLen = matchMaxLen; { + // UInt32 hs4; p->fixedHashSize = 0; - if (p->numHashBytes == 2) - hs = (1 << 16) - 1; - else + hs = (1 << 16) - 1; + if (p->numHashBytes != 2) { hs = historySize; if (hs > p->expectedDataSize) @@ -218,9 +336,9 @@ hs |= (hs >> 2); hs |= (hs >> 4); hs |= (hs >> 8); + // we propagated 16 bits in (hs). Low 16 bits must be set later hs >>= 1; - hs |= 0xFFFF; /* don't change it! It's required for Deflate */ - if (hs > (1 << 24)) + if (hs >= (1 << 24)) { if (p->numHashBytes == 3) hs = (1 << 24) - 1; @@ -228,12 +346,30 @@ hs >>= 1; /* if (bigHash) mode, GetHeads4b() in LzFindMt.c needs (hs >= ((1 << 24) - 1))) */ } + + // hs = ((UInt32)1 << 25) - 1; // for test + + // (hash_size >= (1 << 16)) : Required for (numHashBytes > 2) + hs |= (1 << 16) - 1; /* don't change it! */ + + // bt5: we adjust the size with recommended minimum size + if (p->numHashBytes >= 5) + hs |= (256 << kLzHash_CrcShift_2) - 1; } p->hashMask = hs; hs++; + + /* + hs4 = (1 << 20); + if (hs4 > hs) + hs4 = hs; + // hs4 = (1 << 16); // for test + p->hash4Mask = hs4 - 1; + */ + if (p->numHashBytes > 2) p->fixedHashSize += kHash2Size; if (p->numHashBytes > 3) p->fixedHashSize += kHash3Size; - if (p->numHashBytes > 4) p->fixedHashSize += kHash4Size; + // if (p->numHashBytes > 4) p->fixedHashSize += hs4; // kHash4Size; hs += p->fixedHashSize; } @@ -242,13 +378,17 @@ size_t numSons; p->historySize = historySize; p->hashSizeSum = hs; - p->cyclicBufferSize = newCyclicBufferSize; + p->cyclicBufferSize = newCyclicBufferSize; // it must be = (historySize + 1) numSons = newCyclicBufferSize; if (p->btMode) numSons <<= 1; newSize = hs + numSons; + // aligned size is not required here, but it can be better for some loops + #define NUM_REFS_ALIGN_MASK 0xF + newSize = (newSize + NUM_REFS_ALIGN_MASK) & ~(size_t)NUM_REFS_ALIGN_MASK; + if (p->hash && p->numRefs == newSize) return 1; @@ -268,33 +408,43 @@ return 0; } + static void MatchFinder_SetLimits(CMatchFinder *p) { - UInt32 limit = kMaxValForNormalize - p->pos; - UInt32 limit2 = p->cyclicBufferSize - p->cyclicBufferPos; + UInt32 k; + UInt32 n = kMaxValForNormalize - p->pos; + if (n == 0) + n = (UInt32)(Int32)-1; // we allow (pos == 0) at start even with (kMaxValForNormalize == 0) - if (limit2 < limit) - limit = limit2; - limit2 = p->streamPos - p->pos; - - if (limit2 <= p->keepSizeAfter) + k = p->cyclicBufferSize - p->cyclicBufferPos; + if (k < n) + n = k; + + k = GET_AVAIL_BYTES(p); { - if (limit2 > 0) - limit2 = 1; + const UInt32 ksa = p->keepSizeAfter; + UInt32 mm = p->matchMaxLen; + if (k > ksa) + k -= ksa; // we must limit exactly to keepSizeAfter for ReadBlock + else if (k >= mm) + { + // the limitation for (p->lenLimit) update + k -= mm; // optimization : to reduce the number of checks + k++; + // k = 1; // non-optimized version : for debug + } + else + { + mm = k; + if (k != 0) + k = 1; + } + p->lenLimit = mm; } - else - limit2 -= p->keepSizeAfter; + if (k < n) + n = k; - if (limit2 < limit) - limit = limit2; - - { - UInt32 lenLimit = p->streamPos - p->pos; - if (lenLimit > p->matchMaxLen) - lenLimit = p->matchMaxLen; - p->lenLimit = lenLimit; - } - p->posLimit = p->pos + limit; + p->posLimit = p->pos + n; } @@ -302,7 +452,7 @@ { size_t i; CLzRef *items = p->hash; - size_t numItems = p->fixedHashSize; + const size_t numItems = p->fixedHashSize; for (i = 0; i < numItems; i++) items[i] = kEmptyHashValue; } @@ -312,72 +462,323 @@ { size_t i; CLzRef *items = p->hash + p->fixedHashSize; - size_t numItems = (size_t)p->hashMask + 1; + const size_t numItems = (size_t)p->hashMask + 1; for (i = 0; i < numItems; i++) items[i] = kEmptyHashValue; } -void MatchFinder_Init_3(CMatchFinder *p, int readData) +void MatchFinder_Init_4(CMatchFinder *p) { - p->cyclicBufferPos = 0; p->buffer = p->bufferBase; - p->pos = - p->streamPos = p->cyclicBufferSize; + { + /* kEmptyHashValue = 0 (Zero) is used in hash tables as NO-VALUE marker. + the code in CMatchFinderMt expects (pos = 1) */ + p->pos = + p->streamPos = + 1; // it's smallest optimal value. do not change it + // 0; // for debug + } p->result = SZ_OK; p->streamEndWasReached = 0; - - if (readData) - MatchFinder_ReadBlock(p); - - MatchFinder_SetLimits(p); } +// (CYC_TO_POS_OFFSET == 0) is expected by some optimized code +#define CYC_TO_POS_OFFSET 0 +// #define CYC_TO_POS_OFFSET 1 // for debug + void MatchFinder_Init(CMatchFinder *p) { MatchFinder_Init_HighHash(p); MatchFinder_Init_LowHash(p); - MatchFinder_Init_3(p, True); + MatchFinder_Init_4(p); + // if (readData) + MatchFinder_ReadBlock(p); + + /* if we init (cyclicBufferPos = pos), then we can use one variable + instead of both (cyclicBufferPos) and (pos) : only before (cyclicBufferPos) wrapping */ + p->cyclicBufferPos = (p->pos - CYC_TO_POS_OFFSET); // init with relation to (pos) + // p->cyclicBufferPos = 0; // smallest value + // p->son[0] = p->son[1] = 0; // unused: we can init skipped record for speculated accesses. + MatchFinder_SetLimits(p); } - -static UInt32 MatchFinder_GetSubValue(CMatchFinder *p) + +#if 0 +#ifdef MY_CPU_X86_OR_AMD64 + #if defined(__clang__) && (__clang_major__ >= 8) \ + || defined(__GNUC__) && (__GNUC__ >= 8) \ + || defined(__INTEL_COMPILER) && (__INTEL_COMPILER >= 1900) + #define USE_SATUR_SUB_128 + #define USE_AVX2 + #define ATTRIB_SSE41 __attribute__((__target__("sse4.1"))) + #define ATTRIB_AVX2 __attribute__((__target__("avx2"))) + #elif defined(_MSC_VER) + #if (_MSC_VER >= 1600) + #define USE_SATUR_SUB_128 + #if (_MSC_VER >= 1900) + #define USE_AVX2 + #include <immintrin.h> // avx + #endif + #endif + #endif + +// #elif defined(MY_CPU_ARM_OR_ARM64) +#elif defined(MY_CPU_ARM64) + + #if defined(__clang__) && (__clang_major__ >= 8) \ + || defined(__GNUC__) && (__GNUC__ >= 8) + #define USE_SATUR_SUB_128 + #ifdef MY_CPU_ARM64 + // #define ATTRIB_SSE41 __attribute__((__target__(""))) + #else + // #define ATTRIB_SSE41 __attribute__((__target__("fpu=crypto-neon-fp-armv8"))) + #endif + + #elif defined(_MSC_VER) + #if (_MSC_VER >= 1910) + #define USE_SATUR_SUB_128 + #endif + #endif + + #if defined(_MSC_VER) && defined(MY_CPU_ARM64) + #include <arm64_neon.h> + #else + #include <arm_neon.h> + #endif + +#endif +#endif + +/* +#ifndef ATTRIB_SSE41 + #define ATTRIB_SSE41 +#endif +#ifndef ATTRIB_AVX2 + #define ATTRIB_AVX2 +#endif +*/ + +#ifdef USE_SATUR_SUB_128 + +// #define _SHOW_HW_STATUS + +#ifdef _SHOW_HW_STATUS +#include <stdio.h> +#define _PRF(x) x +_PRF(;) +#else +#define _PRF(x) +#endif + +#ifdef MY_CPU_ARM_OR_ARM64 + +#ifdef MY_CPU_ARM64 +// #define FORCE_SATUR_SUB_128 +#endif + +typedef uint32x4_t v128; +#define SASUB_128(i) \ + *(v128 *)(void *)(items + (i) * 4) = \ + vsubq_u32(vmaxq_u32(*(const v128 *)(const void *)(items + (i) * 4), sub2), sub2); + +#else + +#include <smmintrin.h> // sse4.1 + +typedef __m128i v128; +#define SASUB_128(i) \ + *(v128 *)(void *)(items + (i) * 4) = \ + _mm_sub_epi32(_mm_max_epu32(*(const v128 *)(const void *)(items + (i) * 4), sub2), sub2); // SSE 4.1 + +#endif + + + +MY_NO_INLINE +static +#ifdef ATTRIB_SSE41 +ATTRIB_SSE41 +#endif +void +MY_FAST_CALL +LzFind_SaturSub_128(UInt32 subValue, CLzRef *items, const CLzRef *lim) { - return (p->pos - p->historySize - 1) & kNormalizeMask; + v128 sub2 = + #ifdef MY_CPU_ARM_OR_ARM64 + vdupq_n_u32(subValue); + #else + _mm_set_epi32((Int32)subValue, (Int32)subValue, (Int32)subValue, (Int32)subValue); + #endif + do + { + SASUB_128(0) + SASUB_128(1) + SASUB_128(2) + SASUB_128(3) + items += 4 * 4; + } + while (items != lim); } + + +#ifdef USE_AVX2 + +#include <immintrin.h> // avx + +#define SASUB_256(i) *(__m256i *)(void *)(items + (i) * 8) = _mm256_sub_epi32(_mm256_max_epu32(*(const __m256i *)(const void *)(items + (i) * 8), sub2), sub2); // AVX2 + +MY_NO_INLINE +static +#ifdef ATTRIB_AVX2 +ATTRIB_AVX2 +#endif +void +MY_FAST_CALL +LzFind_SaturSub_256(UInt32 subValue, CLzRef *items, const CLzRef *lim) +{ + __m256i sub2 = _mm256_set_epi32( + (Int32)subValue, (Int32)subValue, (Int32)subValue, (Int32)subValue, + (Int32)subValue, (Int32)subValue, (Int32)subValue, (Int32)subValue); + do + { + SASUB_256(0) + SASUB_256(1) + items += 2 * 8; + } + while (items != lim); +} +#endif // USE_AVX2 + +#ifndef FORCE_SATUR_SUB_128 +typedef void (MY_FAST_CALL *LZFIND_SATUR_SUB_CODE_FUNC)( + UInt32 subValue, CLzRef *items, const CLzRef *lim); +static LZFIND_SATUR_SUB_CODE_FUNC g_LzFind_SaturSub; +#endif // FORCE_SATUR_SUB_128 + +#endif // USE_SATUR_SUB_128 + + +// kEmptyHashValue must be zero +// #define SASUB_32(i) v = items[i]; m = v - subValue; if (v < subValue) m = kEmptyHashValue; items[i] = m; +#define SASUB_32(i) v = items[i]; if (v < subValue) v = subValue; items[i] = v - subValue; + +#ifdef FORCE_SATUR_SUB_128 + +#define DEFAULT_SaturSub LzFind_SaturSub_128 + +#else + +#define DEFAULT_SaturSub LzFind_SaturSub_32 + +MY_NO_INLINE +static +void +MY_FAST_CALL +LzFind_SaturSub_32(UInt32 subValue, CLzRef *items, const CLzRef *lim) +{ + do + { + UInt32 v; + SASUB_32(0) + SASUB_32(1) + SASUB_32(2) + SASUB_32(3) + SASUB_32(4) + SASUB_32(5) + SASUB_32(6) + SASUB_32(7) + items += 8; + } + while (items != lim); +} + +#endif + + +MY_NO_INLINE void MatchFinder_Normalize3(UInt32 subValue, CLzRef *items, size_t numItems) { - size_t i; - for (i = 0; i < numItems; i++) + #define K_NORM_ALIGN_BLOCK_SIZE (1 << 6) + + CLzRef *lim; + + for (; numItems != 0 && ((unsigned)(ptrdiff_t)items & (K_NORM_ALIGN_BLOCK_SIZE - 1)) != 0; numItems--) { - UInt32 value = items[i]; - if (value <= subValue) - value = kEmptyHashValue; - else - value -= subValue; - items[i] = value; + UInt32 v; + SASUB_32(0); + items++; + } + + { + #define K_NORM_ALIGN_MASK (K_NORM_ALIGN_BLOCK_SIZE / 4 - 1) + lim = items + (numItems & ~(size_t)K_NORM_ALIGN_MASK); + numItems &= K_NORM_ALIGN_MASK; + if (items != lim) + { + #if defined(USE_SATUR_SUB_128) && !defined(FORCE_SATUR_SUB_128) + if (g_LzFind_SaturSub) + g_LzFind_SaturSub(subValue, items, lim); + else + #endif + DEFAULT_SaturSub(subValue, items, lim); + } + items = lim; + } + + + for (; numItems != 0; numItems--) + { + UInt32 v; + SASUB_32(0); + items++; } } -static void MatchFinder_Normalize(CMatchFinder *p) -{ - UInt32 subValue = MatchFinder_GetSubValue(p); - MatchFinder_Normalize3(subValue, p->hash, p->numRefs); - MatchFinder_ReduceOffsets(p, subValue); -} +// call MatchFinder_CheckLimits() only after (p->pos++) update + MY_NO_INLINE static void MatchFinder_CheckLimits(CMatchFinder *p) { + if (// !p->streamEndWasReached && p->result == SZ_OK && + p->keepSizeAfter == GET_AVAIL_BYTES(p)) + { + // we try to read only in exact state (p->keepSizeAfter == GET_AVAIL_BYTES(p)) + if (MatchFinder_NeedMove(p)) + MatchFinder_MoveBlock(p); + MatchFinder_ReadBlock(p); + } + if (p->pos == kMaxValForNormalize) - MatchFinder_Normalize(p); - if (!p->streamEndWasReached && p->keepSizeAfter == p->streamPos - p->pos) - MatchFinder_CheckAndMoveAndRead(p); + if (GET_AVAIL_BYTES(p) >= p->numHashBytes) // optional optimization for last bytes of data. + /* + if we disable normalization for last bytes of data, and + if (data_size == 4 GiB), we don't call wastfull normalization, + but (pos) will be wrapped over Zero (0) in that case. + And we cannot resume later to normal operation + */ + { + // MatchFinder_Normalize(p); + /* after normalization we need (p->pos >= p->historySize + 1); */ + /* we can reduce subValue to aligned value, if want to keep alignment + of (p->pos) and (p->buffer) for speculated accesses. */ + const UInt32 subValue = (p->pos - p->historySize - 1) /* & ~(UInt32)(kNormalizeAlign - 1) */; + // const UInt32 subValue = (1 << 15); // for debug + // printf("\nMatchFinder_Normalize() subValue == 0x%x\n", subValue); + size_t numSonRefs = p->cyclicBufferSize; + if (p->btMode) + numSonRefs <<= 1; + Inline_MatchFinder_ReduceOffsets(p, subValue); + MatchFinder_Normalize3(subValue, p->hash, (size_t)p->hashSizeSum + numSonRefs); + } + if (p->cyclicBufferPos == p->cyclicBufferSize) p->cyclicBufferPos = 0; + MatchFinder_SetLimits(p); } @@ -386,9 +787,9 @@ (lenLimit > maxLen) */ MY_FORCE_INLINE -static UInt32 * Hc_GetMatchesSpec(unsigned lenLimit, UInt32 curMatch, UInt32 pos, const Byte *cur, CLzRef *son, - UInt32 _cyclicBufferPos, UInt32 _cyclicBufferSize, UInt32 cutValue, - UInt32 *distances, unsigned maxLen) +static UInt32 * Hc_GetMatchesSpec(size_t lenLimit, UInt32 curMatch, UInt32 pos, const Byte *cur, CLzRef *son, + size_t _cyclicBufferPos, UInt32 _cyclicBufferSize, UInt32 cutValue, + UInt32 *d, unsigned maxLen) { /* son[_cyclicBufferPos] = curMatch; @@ -396,7 +797,7 @@ { UInt32 delta = pos - curMatch; if (cutValue-- == 0 || delta >= _cyclicBufferSize) - return distances; + return d; { const Byte *pb = cur - delta; curMatch = son[_cyclicBufferPos - delta + ((delta > _cyclicBufferPos) ? _cyclicBufferSize : 0)]; @@ -409,10 +810,10 @@ if (maxLen < len) { maxLen = len; - *distances++ = len; - *distances++ = delta - 1; + *d++ = len; + *d++ = delta - 1; if (len == lenLimit) - return distances; + return d; } } } @@ -421,35 +822,41 @@ const Byte *lim = cur + lenLimit; son[_cyclicBufferPos] = curMatch; + do { - UInt32 delta = pos - curMatch; + UInt32 delta; + + if (curMatch == 0) + break; + // if (curMatch2 >= curMatch) return NULL; + delta = pos - curMatch; if (delta >= _cyclicBufferSize) break; { ptrdiff_t diff; curMatch = son[_cyclicBufferPos - delta + ((delta > _cyclicBufferPos) ? _cyclicBufferSize : 0)]; - diff = (ptrdiff_t)0 - delta; - if (cur[maxLen] == cur[maxLen + diff]) + diff = (ptrdiff_t)0 - (ptrdiff_t)delta; + if (cur[maxLen] == cur[(ptrdiff_t)maxLen + diff]) { const Byte *c = cur; while (*c == c[diff]) { if (++c == lim) { - distances[0] = (UInt32)(lim - cur); - distances[1] = delta - 1; - return distances + 2; + d[0] = (UInt32)(lim - cur); + d[1] = delta - 1; + return d + 2; } } { - unsigned len = (unsigned)(c - cur); + const unsigned len = (unsigned)(c - cur); if (maxLen < len) { maxLen = len; - distances[0] = (UInt32)len; - distances[1] = delta - 1; - distances += 2; + d[0] = (UInt32)len; + d[1] = delta - 1; + d += 2; } } } @@ -457,31 +864,36 @@ } while (--cutValue); - return distances; + return d; } MY_FORCE_INLINE UInt32 * GetMatchesSpec1(UInt32 lenLimit, UInt32 curMatch, UInt32 pos, const Byte *cur, CLzRef *son, - UInt32 _cyclicBufferPos, UInt32 _cyclicBufferSize, UInt32 cutValue, - UInt32 *distances, UInt32 maxLen) + size_t _cyclicBufferPos, UInt32 _cyclicBufferSize, UInt32 cutValue, + UInt32 *d, UInt32 maxLen) { CLzRef *ptr0 = son + ((size_t)_cyclicBufferPos << 1) + 1; CLzRef *ptr1 = son + ((size_t)_cyclicBufferPos << 1); unsigned len0 = 0, len1 = 0; - for (;;) + + UInt32 cmCheck; + + // if (curMatch >= pos) { *ptr0 = *ptr1 = kEmptyHashValue; return NULL; } + + cmCheck = (UInt32)(pos - _cyclicBufferSize); + if ((UInt32)pos <= _cyclicBufferSize) + cmCheck = 0; + + if (cmCheck < curMatch) + do { - UInt32 delta = pos - curMatch; - if (cutValue-- == 0 || delta >= _cyclicBufferSize) - { - *ptr0 = *ptr1 = kEmptyHashValue; - return distances; - } + const UInt32 delta = pos - curMatch; { CLzRef *pair = son + ((size_t)(_cyclicBufferPos - delta + ((delta > _cyclicBufferPos) ? _cyclicBufferSize : 0)) << 1); const Byte *pb = cur - delta; unsigned len = (len0 < len1 ? len0 : len1); - UInt32 pair0 = pair[0]; + const UInt32 pair0 = pair[0]; if (pb[len] == cur[len]) { if (++len != lenLimit && pb[len] == cur[len]) @@ -491,48 +903,60 @@ if (maxLen < len) { maxLen = (UInt32)len; - *distances++ = (UInt32)len; - *distances++ = delta - 1; + *d++ = (UInt32)len; + *d++ = delta - 1; if (len == lenLimit) { *ptr1 = pair0; *ptr0 = pair[1]; - return distances; + return d; } } } if (pb[len] < cur[len]) { *ptr1 = curMatch; + // const UInt32 curMatch2 = pair[1]; + // if (curMatch2 >= curMatch) { *ptr0 = *ptr1 = kEmptyHashValue; return NULL; } + // curMatch = curMatch2; + curMatch = pair[1]; ptr1 = pair + 1; - curMatch = *ptr1; len1 = len; } else { *ptr0 = curMatch; + curMatch = pair[0]; ptr0 = pair; - curMatch = *ptr0; len0 = len; } } } + while(--cutValue && cmCheck < curMatch); + + *ptr0 = *ptr1 = kEmptyHashValue; + return d; } + static void SkipMatchesSpec(UInt32 lenLimit, UInt32 curMatch, UInt32 pos, const Byte *cur, CLzRef *son, - UInt32 _cyclicBufferPos, UInt32 _cyclicBufferSize, UInt32 cutValue) + size_t _cyclicBufferPos, UInt32 _cyclicBufferSize, UInt32 cutValue) { CLzRef *ptr0 = son + ((size_t)_cyclicBufferPos << 1) + 1; CLzRef *ptr1 = son + ((size_t)_cyclicBufferPos << 1); unsigned len0 = 0, len1 = 0; - for (;;) + + UInt32 cmCheck; + + cmCheck = (UInt32)(pos - _cyclicBufferSize); + if ((UInt32)pos <= _cyclicBufferSize) + cmCheck = 0; + + if (// curMatch >= pos || // failure + cmCheck < curMatch) + do { - UInt32 delta = pos - curMatch; - if (cutValue-- == 0 || delta >= _cyclicBufferSize) - { - *ptr0 = *ptr1 = kEmptyHashValue; - return; - } + const UInt32 delta = pos - curMatch; { CLzRef *pair = son + ((size_t)(_cyclicBufferPos - delta + ((delta > _cyclicBufferPos) ? _cyclicBufferSize : 0)) << 1); const Byte *pb = cur - delta; @@ -554,80 +978,108 @@ if (pb[len] < cur[len]) { *ptr1 = curMatch; + curMatch = pair[1]; ptr1 = pair + 1; - curMatch = *ptr1; len1 = len; } else { *ptr0 = curMatch; + curMatch = pair[0]; ptr0 = pair; - curMatch = *ptr0; len0 = len; } } } + while(--cutValue && cmCheck < curMatch); + + *ptr0 = *ptr1 = kEmptyHashValue; + return; } + #define MOVE_POS \ ++p->cyclicBufferPos; \ p->buffer++; \ - if (++p->pos == p->posLimit) MatchFinder_CheckLimits(p); + { const UInt32 pos1 = p->pos + 1; p->pos = pos1; if (pos1 == p->posLimit) MatchFinder_CheckLimits(p); } -#define MOVE_POS_RET MOVE_POS return (UInt32)offset; +#define MOVE_POS_RET MOVE_POS return distances; -static void MatchFinder_MovePos(CMatchFinder *p) { MOVE_POS; } +MY_NO_INLINE +static void MatchFinder_MovePos(CMatchFinder *p) +{ + /* we go here at the end of stream data, when (avail < num_hash_bytes) + We don't update sons[cyclicBufferPos << btMode]. + So (sons) record will contain junk. And we cannot resume match searching + to normal operation, even if we will provide more input data in buffer. + p->sons[p->cyclicBufferPos << p->btMode] = 0; // kEmptyHashValue + if (p->btMode) + p->sons[(p->cyclicBufferPos << p->btMode) + 1] = 0; // kEmptyHashValue + */ + MOVE_POS; +} #define GET_MATCHES_HEADER2(minLen, ret_op) \ - unsigned lenLimit; UInt32 hv; const Byte *cur; UInt32 curMatch; \ + unsigned lenLimit; UInt32 hv; Byte *cur; UInt32 curMatch; \ lenLimit = (unsigned)p->lenLimit; { if (lenLimit < minLen) { MatchFinder_MovePos(p); ret_op; }} \ cur = p->buffer; -#define GET_MATCHES_HEADER(minLen) GET_MATCHES_HEADER2(minLen, return 0) -#define SKIP_HEADER(minLen) GET_MATCHES_HEADER2(minLen, continue) +#define GET_MATCHES_HEADER(minLen) GET_MATCHES_HEADER2(minLen, return distances) +#define SKIP_HEADER(minLen) do { GET_MATCHES_HEADER2(minLen, continue) -#define MF_PARAMS(p) p->pos, p->buffer, p->son, p->cyclicBufferPos, p->cyclicBufferSize, p->cutValue +#define MF_PARAMS(p) lenLimit, curMatch, p->pos, p->buffer, p->son, p->cyclicBufferPos, p->cyclicBufferSize, p->cutValue -#define GET_MATCHES_FOOTER(offset, maxLen) \ - offset = (unsigned)(GetMatchesSpec1((UInt32)lenLimit, curMatch, MF_PARAMS(p), \ - distances + offset, (UInt32)maxLen) - distances); MOVE_POS_RET; +#define SKIP_FOOTER SkipMatchesSpec(MF_PARAMS(p)); MOVE_POS; } while (--num); -#define SKIP_FOOTER \ - SkipMatchesSpec((UInt32)lenLimit, curMatch, MF_PARAMS(p)); MOVE_POS; +#define GET_MATCHES_FOOTER_BASE(_maxLen_, func) \ + distances = func(MF_PARAMS(p), \ + distances, (UInt32)_maxLen_); MOVE_POS_RET; + +#define GET_MATCHES_FOOTER_BT(_maxLen_) \ + GET_MATCHES_FOOTER_BASE(_maxLen_, GetMatchesSpec1) + +#define GET_MATCHES_FOOTER_HC(_maxLen_) \ + GET_MATCHES_FOOTER_BASE(_maxLen_, Hc_GetMatchesSpec) + + #define UPDATE_maxLen { \ - ptrdiff_t diff = (ptrdiff_t)0 - d2; \ + const ptrdiff_t diff = (ptrdiff_t)0 - (ptrdiff_t)d2; \ const Byte *c = cur + maxLen; \ const Byte *lim = cur + lenLimit; \ for (; c != lim; c++) if (*(c + diff) != *c) break; \ maxLen = (unsigned)(c - cur); } -static UInt32 Bt2_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) +static UInt32* Bt2_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) { - unsigned offset; GET_MATCHES_HEADER(2) HASH2_CALC; curMatch = p->hash[hv]; p->hash[hv] = p->pos; - offset = 0; - GET_MATCHES_FOOTER(offset, 1) + GET_MATCHES_FOOTER_BT(1) } -UInt32 Bt3Zip_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) +UInt32* Bt3Zip_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) { - unsigned offset; GET_MATCHES_HEADER(3) HASH_ZIP_CALC; curMatch = p->hash[hv]; p->hash[hv] = p->pos; - offset = 0; - GET_MATCHES_FOOTER(offset, 2) + GET_MATCHES_FOOTER_BT(2) } -static UInt32 Bt3_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) + +#define SET_mmm \ + mmm = p->cyclicBufferSize; \ + if (pos < mmm) \ + mmm = pos; + + +static UInt32* Bt3_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) { + UInt32 mmm; UInt32 h2, d2, pos; - unsigned maxLen, offset; + unsigned maxLen; UInt32 *hash; GET_MATCHES_HEADER(3) @@ -643,29 +1095,32 @@ hash[h2] = pos; (hash + kFix3HashSize)[hv] = pos; - maxLen = 2; - offset = 0; + SET_mmm - if (d2 < p->cyclicBufferSize && *(cur - d2) == *cur) + maxLen = 2; + + if (d2 < mmm && *(cur - d2) == *cur) { UPDATE_maxLen distances[0] = (UInt32)maxLen; distances[1] = d2 - 1; - offset = 2; + distances += 2; if (maxLen == lenLimit) { - SkipMatchesSpec((UInt32)lenLimit, curMatch, MF_PARAMS(p)); + SkipMatchesSpec(MF_PARAMS(p)); MOVE_POS_RET; } } - GET_MATCHES_FOOTER(offset, maxLen) + GET_MATCHES_FOOTER_BT(maxLen) } -static UInt32 Bt4_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) + +static UInt32* Bt4_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) { + UInt32 mmm; UInt32 h2, h3, d2, d3, pos; - unsigned maxLen, offset; + unsigned maxLen; UInt32 *hash; GET_MATCHES_HEADER(4) @@ -676,53 +1131,63 @@ d2 = pos - hash [h2]; d3 = pos - (hash + kFix3HashSize)[h3]; - curMatch = (hash + kFix4HashSize)[hv]; hash [h2] = pos; (hash + kFix3HashSize)[h3] = pos; (hash + kFix4HashSize)[hv] = pos; - maxLen = 0; - offset = 0; + SET_mmm + + maxLen = 3; - if (d2 < p->cyclicBufferSize && *(cur - d2) == *cur) + for (;;) { - maxLen = 2; - distances[0] = 2; - distances[1] = d2 - 1; - offset = 2; - } + if (d2 < mmm && *(cur - d2) == *cur) + { + distances[0] = 2; + distances[1] = d2 - 1; + distances += 2; + if (*(cur - d2 + 2) == cur[2]) + { + // distances[-2] = 3; + } + else if (d3 < mmm && *(cur - d3) == *cur) + { + d2 = d3; + distances[1] = d3 - 1; + distances += 2; + } + else + break; + } + else if (d3 < mmm && *(cur - d3) == *cur) + { + d2 = d3; + distances[1] = d3 - 1; + distances += 2; + } + else + break; - if (d2 != d3 && d3 < p->cyclicBufferSize && *(cur - d3) == *cur) - { - maxLen = 3; - distances[(size_t)offset + 1] = d3 - 1; - offset += 2; - d2 = d3; - } - - if (offset != 0) - { UPDATE_maxLen - distances[(size_t)offset - 2] = (UInt32)maxLen; + distances[-2] = (UInt32)maxLen; if (maxLen == lenLimit) { - SkipMatchesSpec((UInt32)lenLimit, curMatch, MF_PARAMS(p)); - MOVE_POS_RET; + SkipMatchesSpec(MF_PARAMS(p)); + MOVE_POS_RET } + break; } - if (maxLen < 3) - maxLen = 3; - - GET_MATCHES_FOOTER(offset, maxLen) + GET_MATCHES_FOOTER_BT(maxLen) } -/* -static UInt32 Bt5_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) + +static UInt32* Bt5_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) { - UInt32 h2, h3, h4, d2, d3, d4, maxLen, offset, pos; + UInt32 mmm; + UInt32 h2, h3, d2, d3, maxLen, pos; UInt32 *hash; GET_MATCHES_HEADER(5) @@ -733,73 +1198,69 @@ d2 = pos - hash [h2]; d3 = pos - (hash + kFix3HashSize)[h3]; - d4 = pos - (hash + kFix4HashSize)[h4]; + // d4 = pos - (hash + kFix4HashSize)[h4]; curMatch = (hash + kFix5HashSize)[hv]; hash [h2] = pos; (hash + kFix3HashSize)[h3] = pos; - (hash + kFix4HashSize)[h4] = pos; + // (hash + kFix4HashSize)[h4] = pos; (hash + kFix5HashSize)[hv] = pos; - maxLen = 0; - offset = 0; + SET_mmm - if (d2 < p->cyclicBufferSize && *(cur - d2) == *cur) + maxLen = 4; + + for (;;) { - distances[0] = maxLen = 2; - distances[1] = d2 - 1; - offset = 2; - if (*(cur - d2 + 2) == cur[2]) - distances[0] = maxLen = 3; - else if (d3 < p->cyclicBufferSize && *(cur - d3) == *cur) + if (d2 < mmm && *(cur - d2) == *cur) { - distances[2] = maxLen = 3; - distances[3] = d3 - 1; - offset = 4; + distances[0] = 2; + distances[1] = d2 - 1; + distances += 2; + if (*(cur - d2 + 2) == cur[2]) + { + } + else if (d3 < mmm && *(cur - d3) == *cur) + { + distances[1] = d3 - 1; + distances += 2; + d2 = d3; + } + else + break; + } + else if (d3 < mmm && *(cur - d3) == *cur) + { + distances[1] = d3 - 1; + distances += 2; d2 = d3; } - } - else if (d3 < p->cyclicBufferSize && *(cur - d3) == *cur) - { - distances[0] = maxLen = 3; - distances[1] = d3 - 1; - offset = 2; - d2 = d3; - } - - if (d2 != d4 && d4 < p->cyclicBufferSize - && *(cur - d4) == *cur - && *(cur - d4 + 3) == *(cur + 3)) - { - maxLen = 4; - distances[(size_t)offset + 1] = d4 - 1; - offset += 2; - d2 = d4; - } - - if (offset != 0) - { + else + break; + + distances[-2] = 3; + if (*(cur - d2 + 3) != cur[3]) + break; UPDATE_maxLen - distances[(size_t)offset - 2] = maxLen; + distances[-2] = (UInt32)maxLen; if (maxLen == lenLimit) { - SkipMatchesSpec(lenLimit, curMatch, MF_PARAMS(p)); + SkipMatchesSpec(MF_PARAMS(p)); MOVE_POS_RET; } + break; } - - if (maxLen < 4) - maxLen = 4; - GET_MATCHES_FOOTER(offset, maxLen) + GET_MATCHES_FOOTER_BT(maxLen) } -*/ -static UInt32 Hc4_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) + +static UInt32* Hc4_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) { + UInt32 mmm; UInt32 h2, h3, d2, d3, pos; - unsigned maxLen, offset; + unsigned maxLen; UInt32 *hash; GET_MATCHES_HEADER(4) @@ -816,48 +1277,57 @@ (hash + kFix3HashSize)[h3] = pos; (hash + kFix4HashSize)[hv] = pos; - maxLen = 0; - offset = 0; + SET_mmm - if (d2 < p->cyclicBufferSize && *(cur - d2) == *cur) + maxLen = 3; + + for (;;) { - maxLen = 2; - distances[0] = 2; - distances[1] = d2 - 1; - offset = 2; - } - - if (d2 != d3 && d3 < p->cyclicBufferSize && *(cur - d3) == *cur) - { - maxLen = 3; - distances[(size_t)offset + 1] = d3 - 1; - offset += 2; - d2 = d3; - } - - if (offset != 0) - { + if (d2 < mmm && *(cur - d2) == *cur) + { + distances[0] = 2; + distances[1] = d2 - 1; + distances += 2; + if (*(cur - d2 + 2) == cur[2]) + { + // distances[-2] = 3; + } + else if (d3 < mmm && *(cur - d3) == *cur) + { + d2 = d3; + distances[1] = d3 - 1; + distances += 2; + } + else + break; + } + else if (d3 < mmm && *(cur - d3) == *cur) + { + d2 = d3; + distances[1] = d3 - 1; + distances += 2; + } + else + break; + UPDATE_maxLen - distances[(size_t)offset - 2] = (UInt32)maxLen; + distances[-2] = (UInt32)maxLen; if (maxLen == lenLimit) { p->son[p->cyclicBufferPos] = curMatch; MOVE_POS_RET; } + break; } - if (maxLen < 3) - maxLen = 3; - - offset = (unsigned)(Hc_GetMatchesSpec(lenLimit, curMatch, MF_PARAMS(p), - distances + offset, maxLen) - (distances)); - MOVE_POS_RET + GET_MATCHES_FOOTER_HC(maxLen); } -/* -static UInt32 Hc5_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) + +static UInt32 * Hc5_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) { - UInt32 h2, h3, h4, d2, d3, d4, maxLen, offset, pos + UInt32 mmm; + UInt32 h2, h3, d2, d3, maxLen, pos; UInt32 *hash; GET_MATCHES_HEADER(5) @@ -865,242 +1335,237 @@ hash = p->hash; pos = p->pos; - + d2 = pos - hash [h2]; d3 = pos - (hash + kFix3HashSize)[h3]; - d4 = pos - (hash + kFix4HashSize)[h4]; + // d4 = pos - (hash + kFix4HashSize)[h4]; curMatch = (hash + kFix5HashSize)[hv]; hash [h2] = pos; (hash + kFix3HashSize)[h3] = pos; - (hash + kFix4HashSize)[h4] = pos; + // (hash + kFix4HashSize)[h4] = pos; (hash + kFix5HashSize)[hv] = pos; - maxLen = 0; - offset = 0; + SET_mmm + + maxLen = 4; - if (d2 < p->cyclicBufferSize && *(cur - d2) == *cur) + for (;;) { - distances[0] = maxLen = 2; - distances[1] = d2 - 1; - offset = 2; - if (*(cur - d2 + 2) == cur[2]) - distances[0] = maxLen = 3; - else if (d3 < p->cyclicBufferSize && *(cur - d3) == *cur) + if (d2 < mmm && *(cur - d2) == *cur) { - distances[2] = maxLen = 3; - distances[3] = d3 - 1; - offset = 4; + distances[0] = 2; + distances[1] = d2 - 1; + distances += 2; + if (*(cur - d2 + 2) == cur[2]) + { + } + else if (d3 < mmm && *(cur - d3) == *cur) + { + distances[1] = d3 - 1; + distances += 2; + d2 = d3; + } + else + break; + } + else if (d3 < mmm && *(cur - d3) == *cur) + { + distances[1] = d3 - 1; + distances += 2; d2 = d3; } - } - else if (d3 < p->cyclicBufferSize && *(cur - d3) == *cur) - { - distances[0] = maxLen = 3; - distances[1] = d3 - 1; - offset = 2; - d2 = d3; - } - - if (d2 != d4 && d4 < p->cyclicBufferSize - && *(cur - d4) == *cur - && *(cur - d4 + 3) == *(cur + 3)) - { - maxLen = 4; - distances[(size_t)offset + 1] = d4 - 1; - offset += 2; - d2 = d4; - } - - if (offset != 0) - { + else + break; + + distances[-2] = 3; + if (*(cur - d2 + 3) != cur[3]) + break; UPDATE_maxLen - distances[(size_t)offset - 2] = maxLen; + distances[-2] = maxLen; if (maxLen == lenLimit) { p->son[p->cyclicBufferPos] = curMatch; MOVE_POS_RET; } + break; } - if (maxLen < 4) - maxLen = 4; - - offset = (UInt32)(Hc_GetMatchesSpec(lenLimit, curMatch, MF_PARAMS(p), - distances + offset, maxLen) - (distances)); - MOVE_POS_RET + GET_MATCHES_FOOTER_HC(maxLen); } -*/ -UInt32 Hc3Zip_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) + +UInt32* Hc3Zip_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) { - unsigned offset; GET_MATCHES_HEADER(3) HASH_ZIP_CALC; curMatch = p->hash[hv]; p->hash[hv] = p->pos; - offset = (unsigned)(Hc_GetMatchesSpec(lenLimit, curMatch, MF_PARAMS(p), - distances, 2) - (distances)); - MOVE_POS_RET + GET_MATCHES_FOOTER_HC(2) } + static void Bt2_MatchFinder_Skip(CMatchFinder *p, UInt32 num) { - do + SKIP_HEADER(2) { - SKIP_HEADER(2) HASH2_CALC; curMatch = p->hash[hv]; p->hash[hv] = p->pos; - SKIP_FOOTER } - while (--num != 0); + SKIP_FOOTER } void Bt3Zip_MatchFinder_Skip(CMatchFinder *p, UInt32 num) { - do + SKIP_HEADER(3) { - SKIP_HEADER(3) HASH_ZIP_CALC; curMatch = p->hash[hv]; p->hash[hv] = p->pos; - SKIP_FOOTER } - while (--num != 0); + SKIP_FOOTER } static void Bt3_MatchFinder_Skip(CMatchFinder *p, UInt32 num) { - do + SKIP_HEADER(3) { UInt32 h2; UInt32 *hash; - SKIP_HEADER(3) HASH3_CALC; hash = p->hash; curMatch = (hash + kFix3HashSize)[hv]; hash[h2] = (hash + kFix3HashSize)[hv] = p->pos; - SKIP_FOOTER } - while (--num != 0); + SKIP_FOOTER } static void Bt4_MatchFinder_Skip(CMatchFinder *p, UInt32 num) { - do + SKIP_HEADER(4) { UInt32 h2, h3; UInt32 *hash; - SKIP_HEADER(4) HASH4_CALC; hash = p->hash; curMatch = (hash + kFix4HashSize)[hv]; hash [h2] = (hash + kFix3HashSize)[h3] = (hash + kFix4HashSize)[hv] = p->pos; - SKIP_FOOTER } - while (--num != 0); + SKIP_FOOTER } -/* static void Bt5_MatchFinder_Skip(CMatchFinder *p, UInt32 num) { - do + SKIP_HEADER(5) { - UInt32 h2, h3, h4; + UInt32 h2, h3; UInt32 *hash; - SKIP_HEADER(5) HASH5_CALC; hash = p->hash; curMatch = (hash + kFix5HashSize)[hv]; hash [h2] = (hash + kFix3HashSize)[h3] = - (hash + kFix4HashSize)[h4] = + // (hash + kFix4HashSize)[h4] = (hash + kFix5HashSize)[hv] = p->pos; - SKIP_FOOTER } - while (--num != 0); + SKIP_FOOTER } -*/ + + +#define HC_SKIP_HEADER(minLen) \ + do { if (p->lenLimit < minLen) { MatchFinder_MovePos(p); num--; continue; } { \ + Byte *cur; \ + UInt32 *hash; \ + UInt32 *son; \ + UInt32 pos = p->pos; \ + UInt32 num2 = num; \ + /* (p->pos == p->posLimit) is not allowed here !!! */ \ + { const UInt32 rem = p->posLimit - pos; if (num2 > rem) num2 = rem; } \ + num -= num2; \ + { const UInt32 cycPos = p->cyclicBufferPos; \ + son = p->son + cycPos; \ + p->cyclicBufferPos = cycPos + num2; } \ + cur = p->buffer; \ + hash = p->hash; \ + do { \ + UInt32 curMatch; \ + UInt32 hv; + + +#define HC_SKIP_FOOTER \ + cur++; pos++; *son++ = curMatch; \ + } while (--num2); \ + p->buffer = cur; \ + p->pos = pos; \ + if (pos == p->posLimit) MatchFinder_CheckLimits(p); \ + }} while(num); \ + static void Hc4_MatchFinder_Skip(CMatchFinder *p, UInt32 num) { - do - { + HC_SKIP_HEADER(4) + UInt32 h2, h3; - UInt32 *hash; - SKIP_HEADER(4) HASH4_CALC; - hash = p->hash; curMatch = (hash + kFix4HashSize)[hv]; hash [h2] = (hash + kFix3HashSize)[h3] = - (hash + kFix4HashSize)[hv] = p->pos; - p->son[p->cyclicBufferPos] = curMatch; - MOVE_POS - } - while (--num != 0); + (hash + kFix4HashSize)[hv] = pos; + + HC_SKIP_FOOTER } -/* + static void Hc5_MatchFinder_Skip(CMatchFinder *p, UInt32 num) { - do - { - UInt32 h2, h3, h4; - UInt32 *hash; - SKIP_HEADER(5) - HASH5_CALC; - hash = p->hash; - curMatch = hash + kFix5HashSize)[hv]; + HC_SKIP_HEADER(5) + + UInt32 h2, h3; + HASH5_CALC + curMatch = (hash + kFix5HashSize)[hv]; hash [h2] = (hash + kFix3HashSize)[h3] = - (hash + kFix4HashSize)[h4] = - (hash + kFix5HashSize)[hv] = p->pos; - p->son[p->cyclicBufferPos] = curMatch; - MOVE_POS - } - while (--num != 0); + // (hash + kFix4HashSize)[h4] = + (hash + kFix5HashSize)[hv] = pos; + + HC_SKIP_FOOTER } -*/ + void Hc3Zip_MatchFinder_Skip(CMatchFinder *p, UInt32 num) { - do - { - SKIP_HEADER(3) + HC_SKIP_HEADER(3) + HASH_ZIP_CALC; - curMatch = p->hash[hv]; - p->hash[hv] = p->pos; - p->son[p->cyclicBufferPos] = curMatch; - MOVE_POS - } - while (--num != 0); + curMatch = hash[hv]; + hash[hv] = pos; + + HC_SKIP_FOOTER } -void MatchFinder_CreateVTable(CMatchFinder *p, IMatchFinder *vTable) + +void MatchFinder_CreateVTable(CMatchFinder *p, IMatchFinder2 *vTable) { vTable->Init = (Mf_Init_Func)MatchFinder_Init; vTable->GetNumAvailableBytes = (Mf_GetNumAvailableBytes_Func)MatchFinder_GetNumAvailableBytes; vTable->GetPointerToCurrentPos = (Mf_GetPointerToCurrentPos_Func)MatchFinder_GetPointerToCurrentPos; if (!p->btMode) { - /* if (p->numHashBytes <= 4) */ + if (p->numHashBytes <= 4) { vTable->GetMatches = (Mf_GetMatches_Func)Hc4_MatchFinder_GetMatches; vTable->Skip = (Mf_Skip_Func)Hc4_MatchFinder_Skip; } - /* else { vTable->GetMatches = (Mf_GetMatches_Func)Hc5_MatchFinder_GetMatches; vTable->Skip = (Mf_Skip_Func)Hc5_MatchFinder_Skip; } - */ } else if (p->numHashBytes == 2) { @@ -1112,16 +1577,53 @@ vTable->GetMatches = (Mf_GetMatches_Func)Bt3_MatchFinder_GetMatches; vTable->Skip = (Mf_Skip_Func)Bt3_MatchFinder_Skip; } - else /* if (p->numHashBytes == 4) */ + else if (p->numHashBytes == 4) { vTable->GetMatches = (Mf_GetMatches_Func)Bt4_MatchFinder_GetMatches; vTable->Skip = (Mf_Skip_Func)Bt4_MatchFinder_Skip; } - /* else { vTable->GetMatches = (Mf_GetMatches_Func)Bt5_MatchFinder_GetMatches; vTable->Skip = (Mf_Skip_Func)Bt5_MatchFinder_Skip; } - */ +} + + + +void LzFindPrepare() +{ + #ifndef FORCE_SATUR_SUB_128 + #ifdef USE_SATUR_SUB_128 + LZFIND_SATUR_SUB_CODE_FUNC f = NULL; + #ifdef MY_CPU_ARM_OR_ARM64 + { + if (CPU_IsSupported_NEON()) + { + // #pragma message ("=== LzFind NEON") + _PRF(printf("\n=== LzFind NEON\n")); + f = LzFind_SaturSub_128; + } + // f = 0; // for debug + } + #else // MY_CPU_ARM_OR_ARM64 + if (CPU_IsSupported_SSE41()) + { + // #pragma message ("=== LzFind SSE41") + _PRF(printf("\n=== LzFind SSE41\n")); + f = LzFind_SaturSub_128; + + #ifdef USE_AVX2 + if (CPU_IsSupported_AVX2()) + { + // #pragma message ("=== LzFind AVX2") + _PRF(printf("\n=== LzFind AVX2\n")); + f = LzFind_SaturSub_256; + } + #endif + } + #endif // MY_CPU_ARM_OR_ARM64 + g_LzFind_SaturSub = f; + #endif // USE_SATUR_SUB_128 + #endif // FORCE_SATUR_SUB_128 }
diff --git a/third_party/lzma_sdk/LzFind.h b/third_party/lzma_sdk/LzFind.h index 42c13be1..eea873f 100644 --- a/third_party/lzma_sdk/LzFind.h +++ b/third_party/lzma_sdk/LzFind.h
@@ -1,5 +1,5 @@ /* LzFind.h -- Match finder for LZ algorithms -2017-06-10 : Igor Pavlov : Public domain */ +2021-07-13 : Igor Pavlov : Public domain */ #ifndef __LZ_FIND_H #define __LZ_FIND_H @@ -15,7 +15,7 @@ Byte *buffer; UInt32 pos; UInt32 posLimit; - UInt32 streamPos; + UInt32 streamPos; /* wrap over Zero is allowed (streamPos < pos). Use (UInt32)(streamPos - pos) */ UInt32 lenLimit; UInt32 cyclicBufferPos; @@ -51,17 +51,19 @@ UInt64 expectedDataSize; } CMatchFinder; -#define Inline_MatchFinder_GetPointerToCurrentPos(p) ((p)->buffer) +#define Inline_MatchFinder_GetPointerToCurrentPos(p) ((const Byte *)(p)->buffer) -#define Inline_MatchFinder_GetNumAvailableBytes(p) ((p)->streamPos - (p)->pos) +#define Inline_MatchFinder_GetNumAvailableBytes(p) ((UInt32)((p)->streamPos - (p)->pos)) +/* #define Inline_MatchFinder_IsFinishedOK(p) \ ((p)->streamEndWasReached \ && (p)->streamPos == (p)->pos \ && (!(p)->directInput || (p)->directInputRem == 0)) +*/ int MatchFinder_NeedMove(CMatchFinder *p); -Byte *MatchFinder_GetPointerToCurrentPos(CMatchFinder *p); +/* Byte *MatchFinder_GetPointerToCurrentPos(CMatchFinder *p); */ void MatchFinder_MoveBlock(CMatchFinder *p); void MatchFinder_ReadIfRequired(CMatchFinder *p); @@ -76,10 +78,21 @@ ISzAllocPtr alloc); void MatchFinder_Free(CMatchFinder *p, ISzAllocPtr alloc); void MatchFinder_Normalize3(UInt32 subValue, CLzRef *items, size_t numItems); -void MatchFinder_ReduceOffsets(CMatchFinder *p, UInt32 subValue); +// void MatchFinder_ReduceOffsets(CMatchFinder *p, UInt32 subValue); + +/* +#define Inline_MatchFinder_InitPos(p, val) \ + (p)->pos = (val); \ + (p)->streamPos = (val); +*/ + +#define Inline_MatchFinder_ReduceOffsets(p, subValue) \ + (p)->pos -= (subValue); \ + (p)->streamPos -= (subValue); + UInt32 * GetMatchesSpec1(UInt32 lenLimit, UInt32 curMatch, UInt32 pos, const Byte *buffer, CLzRef *son, - UInt32 _cyclicBufferPos, UInt32 _cyclicBufferSize, UInt32 _cutValue, + size_t _cyclicBufferPos, UInt32 _cyclicBufferSize, UInt32 _cutValue, UInt32 *distances, UInt32 maxLen); /* @@ -91,7 +104,7 @@ typedef void (*Mf_Init_Func)(void *object); typedef UInt32 (*Mf_GetNumAvailableBytes_Func)(void *object); typedef const Byte * (*Mf_GetPointerToCurrentPos_Func)(void *object); -typedef UInt32 (*Mf_GetMatches_Func)(void *object, UInt32 *distances); +typedef UInt32 * (*Mf_GetMatches_Func)(void *object, UInt32 *distances); typedef void (*Mf_Skip_Func)(void *object, UInt32); typedef struct _IMatchFinder @@ -101,21 +114,23 @@ Mf_GetPointerToCurrentPos_Func GetPointerToCurrentPos; Mf_GetMatches_Func GetMatches; Mf_Skip_Func Skip; -} IMatchFinder; +} IMatchFinder2; -void MatchFinder_CreateVTable(CMatchFinder *p, IMatchFinder *vTable); +void MatchFinder_CreateVTable(CMatchFinder *p, IMatchFinder2 *vTable); void MatchFinder_Init_LowHash(CMatchFinder *p); void MatchFinder_Init_HighHash(CMatchFinder *p); -void MatchFinder_Init_3(CMatchFinder *p, int readData); +void MatchFinder_Init_4(CMatchFinder *p); void MatchFinder_Init(CMatchFinder *p); -UInt32 Bt3Zip_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances); -UInt32 Hc3Zip_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances); +UInt32* Bt3Zip_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances); +UInt32* Hc3Zip_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances); void Bt3Zip_MatchFinder_Skip(CMatchFinder *p, UInt32 num); void Hc3Zip_MatchFinder_Skip(CMatchFinder *p, UInt32 num); +void LzFindPrepare(void); + EXTERN_C_END #endif
diff --git a/third_party/lzma_sdk/LzHash.h b/third_party/lzma_sdk/LzHash.h index e7c94230..77b898c 100644 --- a/third_party/lzma_sdk/LzHash.h +++ b/third_party/lzma_sdk/LzHash.h
@@ -1,57 +1,34 @@ /* LzHash.h -- HASH functions for LZ algorithms -2015-04-12 : Igor Pavlov : Public domain */ +2019-10-30 : Igor Pavlov : Public domain */ #ifndef __LZ_HASH_H #define __LZ_HASH_H +/* + (kHash2Size >= (1 << 8)) : Required + (kHash3Size >= (1 << 16)) : Required +*/ + #define kHash2Size (1 << 10) #define kHash3Size (1 << 16) -#define kHash4Size (1 << 20) +// #define kHash4Size (1 << 20) #define kFix3HashSize (kHash2Size) #define kFix4HashSize (kHash2Size + kHash3Size) -#define kFix5HashSize (kHash2Size + kHash3Size + kHash4Size) +// #define kFix5HashSize (kHash2Size + kHash3Size + kHash4Size) -#define HASH2_CALC hv = cur[0] | ((UInt32)cur[1] << 8); +/* + We use up to 3 crc values for hash: + crc0 + crc1 << Shift_1 + crc2 << Shift_2 + (Shift_1 = 5) and (Shift_2 = 10) is good tradeoff. + Small values for Shift are not good for collision rate. + Big value for Shift_2 increases the minimum size + of hash table, that will be slow for small files. +*/ -#define HASH3_CALC { \ - UInt32 temp = p->crc[cur[0]] ^ cur[1]; \ - h2 = temp & (kHash2Size - 1); \ - hv = (temp ^ ((UInt32)cur[2] << 8)) & p->hashMask; } - -#define HASH4_CALC { \ - UInt32 temp = p->crc[cur[0]] ^ cur[1]; \ - h2 = temp & (kHash2Size - 1); \ - temp ^= ((UInt32)cur[2] << 8); \ - h3 = temp & (kHash3Size - 1); \ - hv = (temp ^ (p->crc[cur[3]] << 5)) & p->hashMask; } - -#define HASH5_CALC { \ - UInt32 temp = p->crc[cur[0]] ^ cur[1]; \ - h2 = temp & (kHash2Size - 1); \ - temp ^= ((UInt32)cur[2] << 8); \ - h3 = temp & (kHash3Size - 1); \ - temp ^= (p->crc[cur[3]] << 5); \ - h4 = temp & (kHash4Size - 1); \ - hv = (temp ^ (p->crc[cur[4]] << 3)) & p->hashMask; } - -/* #define HASH_ZIP_CALC hv = ((cur[0] | ((UInt32)cur[1] << 8)) ^ p->crc[cur[2]]) & 0xFFFF; */ -#define HASH_ZIP_CALC hv = ((cur[2] | ((UInt32)cur[0] << 8)) ^ p->crc[cur[1]]) & 0xFFFF; - - -#define MT_HASH2_CALC \ - h2 = (p->crc[cur[0]] ^ cur[1]) & (kHash2Size - 1); - -#define MT_HASH3_CALC { \ - UInt32 temp = p->crc[cur[0]] ^ cur[1]; \ - h2 = temp & (kHash2Size - 1); \ - h3 = (temp ^ ((UInt32)cur[2] << 8)) & (kHash3Size - 1); } - -#define MT_HASH4_CALC { \ - UInt32 temp = p->crc[cur[0]] ^ cur[1]; \ - h2 = temp & (kHash2Size - 1); \ - temp ^= ((UInt32)cur[2] << 8); \ - h3 = temp & (kHash3Size - 1); \ - h4 = (temp ^ (p->crc[cur[3]] << 5)) & (kHash4Size - 1); } +#define kLzHash_CrcShift_1 5 +#define kLzHash_CrcShift_2 10 #endif
diff --git a/third_party/lzma_sdk/Lzma2Dec.c b/third_party/lzma_sdk/Lzma2Dec.c index 4e138a4..ac970a84 100644 --- a/third_party/lzma_sdk/Lzma2Dec.c +++ b/third_party/lzma_sdk/Lzma2Dec.c
@@ -1,5 +1,5 @@ /* Lzma2Dec.c -- LZMA2 Decoder -2019-02-02 : Igor Pavlov : Public domain */ +2021-02-09 : Igor Pavlov : Public domain */ /* #define SHOW_DEBUG_INFO */ @@ -93,7 +93,8 @@ LzmaDec_Init(&p->decoder); } -static ELzma2State Lzma2Dec_UpdateState(CLzma2Dec *p, Byte b) +// ELzma2State +static unsigned Lzma2Dec_UpdateState(CLzma2Dec *p, Byte b) { switch (p->state) {
diff --git a/third_party/lzma_sdk/LzmaDec.c b/third_party/lzma_sdk/LzmaDec.c index ba3e1dd..d6742e5a 100644 --- a/third_party/lzma_sdk/LzmaDec.c +++ b/third_party/lzma_sdk/LzmaDec.c
@@ -1,5 +1,5 @@ /* LzmaDec.c -- LZMA Decoder -2018-07-04 : Igor Pavlov : Public domain */ +2021-04-01 : Igor Pavlov : Public domain */ #include "Precomp.h" @@ -13,10 +13,12 @@ #define kNumBitModelTotalBits 11 #define kBitModelTotal (1 << kNumBitModelTotalBits) -#define kNumMoveBits 5 #define RC_INIT_SIZE 5 +#ifndef _LZMA_DEC_OPT + +#define kNumMoveBits 5 #define NORMALIZE if (range < kTopValue) { range <<= 8; code = (code << 8) | (*buf++); } #define IF_BIT_0(p) ttt = *(p); NORMALIZE; bound = (range >> kNumBitModelTotalBits) * (UInt32)ttt; if (code < bound) @@ -62,9 +64,10 @@ probLit = prob + (offs + bit + symbol); \ GET_BIT2(probLit, symbol, offs ^= bit; , ;) +#endif // _LZMA_DEC_OPT -#define NORMALIZE_CHECK if (range < kTopValue) { if (buf >= bufLimit) return DUMMY_ERROR; range <<= 8; code = (code << 8) | (*buf++); } +#define NORMALIZE_CHECK if (range < kTopValue) { if (buf >= bufLimit) return DUMMY_INPUT_EOF; range <<= 8; code = (code << 8) | (*buf++); } #define IF_BIT_0_CHECK(p) ttt = *(p); NORMALIZE_CHECK; bound = (range >> kNumBitModelTotalBits) * (UInt32)ttt; if (code < bound) #define UPDATE_0_CHECK range = bound; @@ -114,6 +117,9 @@ #define kMatchMinLen 2 #define kMatchSpecLenStart (kMatchMinLen + kLenNumLowSymbols * 2 + kLenNumHighSymbols) +#define kMatchSpecLen_Error_Data (1 << 9) +#define kMatchSpecLen_Error_Fail (kMatchSpecLen_Error_Data - 1) + /* External ASM code needs same CLzmaProb array layout. So don't change it. */ /* (probs_1664) is faster and better for code size at some platforms */ @@ -166,10 +172,12 @@ /* p->remainLen : shows status of LZMA decoder: - < kMatchSpecLenStart : normal remain - = kMatchSpecLenStart : finished - = kMatchSpecLenStart + 1 : need init range coder - = kMatchSpecLenStart + 2 : need init range coder and state + < kMatchSpecLenStart : the number of bytes to be copied with (p->rep0) offset + = kMatchSpecLenStart : the LZMA stream was finished with end mark + = kMatchSpecLenStart + 1 : need init range coder + = kMatchSpecLenStart + 2 : need init range coder and state + = kMatchSpecLen_Error_Fail : Internal Code Failure + = kMatchSpecLen_Error_Data + [0 ... 273] : LZMA Data Error */ /* ---------- LZMA_DECODE_REAL ---------- */ @@ -188,23 +196,31 @@ { LzmaDec_TryDummy() was called before to exclude LITERAL and MATCH-REP cases. So first symbol can be only MATCH-NON-REP. And if that MATCH-NON-REP symbol - is not END_OF_PAYALOAD_MARKER, then function returns error code. + is not END_OF_PAYALOAD_MARKER, then the function doesn't write any byte to dictionary, + the function returns SZ_OK, and the caller can use (p->remainLen) and (p->reps[0]) later. } Processing: - first LZMA symbol will be decoded in any case - All checks for limits are at the end of main loop, - It will decode new LZMA-symbols while (p->buf < bufLimit && dicPos < limit), + The first LZMA symbol will be decoded in any case. + All main checks for limits are at the end of main loop, + It decodes additional LZMA-symbols while (p->buf < bufLimit && dicPos < limit), RangeCoder is still without last normalization when (p->buf < bufLimit) is being checked. + But if (p->buf < bufLimit), the caller provided at least (LZMA_REQUIRED_INPUT_MAX + 1) bytes for + next iteration before limit (bufLimit + LZMA_REQUIRED_INPUT_MAX), + that is enough for worst case LZMA symbol with one additional RangeCoder normalization for one bit. + So that function never reads bufLimit [LZMA_REQUIRED_INPUT_MAX] byte. Out: RangeCoder is normalized Result: SZ_OK - OK - SZ_ERROR_DATA - Error - p->remainLen: - < kMatchSpecLenStart : normal remain - = kMatchSpecLenStart : finished + p->remainLen: + < kMatchSpecLenStart : the number of bytes to be copied with (p->reps[0]) offset + = kMatchSpecLenStart : the LZMA stream was finished with end mark + + SZ_ERROR_DATA - error, when the MATCH-Symbol refers out of dictionary + p->remainLen : undefined + p->reps[*] : undefined */ @@ -316,11 +332,6 @@ else { UPDATE_1(prob); - /* - // that case was checked before with kBadRepCode - if (checkDicSize == 0 && processedPos == 0) - return SZ_ERROR_DATA; - */ prob = probs + IsRepG0 + state; IF_BIT_0(prob) { @@ -329,6 +340,13 @@ IF_BIT_0(prob) { UPDATE_0(prob); + + // that case was checked before with kBadRepCode + // if (checkDicSize == 0 && processedPos == 0) { len = kMatchSpecLen_Error_Data + 1; break; } + // The caller doesn't allow (dicPos == limit) case here + // so we don't need the following check: + // if (dicPos == limit) { state = state < kNumLitStates ? 9 : 11; len = 1; break; } + dic[dicPos] = dic[dicPos - rep0 + (dicPos < rep0 ? dicBufSize : 0)]; dicPos++; processedPos++; @@ -518,8 +536,10 @@ state = (state < kNumStates + kNumLitStates) ? kNumLitStates : kNumLitStates + 3; if (distance >= (checkDicSize == 0 ? processedPos: checkDicSize)) { - p->dicPos = dicPos; - return SZ_ERROR_DATA; + len += kMatchSpecLen_Error_Data + kMatchMinLen; + // len = kMatchSpecLen_Error_Data; + // len += kMatchMinLen; + break; } } @@ -532,8 +552,13 @@ if ((rem = limit - dicPos) == 0) { - p->dicPos = dicPos; - return SZ_ERROR_DATA; + /* + We stop decoding and return SZ_OK, and we can resume decoding later. + Any error conditions can be tested later in caller code. + For more strict mode we can stop decoding with error + // len += kMatchSpecLen_Error_Data; + */ + break; } curLen = ((rem < len) ? (unsigned)rem : len); @@ -572,7 +597,7 @@ p->buf = buf; p->range = range; p->code = code; - p->remainLen = (UInt32)len; + p->remainLen = (UInt32)len; // & (kMatchSpecLen_Error_Data - 1); // we can write real length for error matches too. p->dicPos = dicPos; p->processedPos = processedPos; p->reps[0] = rep0; @@ -580,40 +605,61 @@ p->reps[2] = rep2; p->reps[3] = rep3; p->state = (UInt32)state; - + if (len >= kMatchSpecLen_Error_Data) + return SZ_ERROR_DATA; return SZ_OK; } #endif + + static void MY_FAST_CALL LzmaDec_WriteRem(CLzmaDec *p, SizeT limit) { - if (p->remainLen != 0 && p->remainLen < kMatchSpecLenStart) + unsigned len = (unsigned)p->remainLen; + if (len == 0 /* || len >= kMatchSpecLenStart */) + return; { - Byte *dic = p->dic; SizeT dicPos = p->dicPos; - SizeT dicBufSize = p->dicBufSize; - unsigned len = (unsigned)p->remainLen; - SizeT rep0 = p->reps[0]; /* we use SizeT to avoid the BUG of VC14 for AMD64 */ - SizeT rem = limit - dicPos; - if (rem < len) - len = (unsigned)(rem); + Byte *dic; + SizeT dicBufSize; + SizeT rep0; /* we use SizeT to avoid the BUG of VC14 for AMD64 */ + { + SizeT rem = limit - dicPos; + if (rem < len) + { + len = (unsigned)(rem); + if (len == 0) + return; + } + } if (p->checkDicSize == 0 && p->prop.dicSize - p->processedPos <= len) p->checkDicSize = p->prop.dicSize; p->processedPos += (UInt32)len; p->remainLen -= (UInt32)len; - while (len != 0) + dic = p->dic; + rep0 = p->reps[0]; + dicBufSize = p->dicBufSize; + do { - len--; dic[dicPos] = dic[dicPos - rep0 + (dicPos < rep0 ? dicBufSize : 0)]; dicPos++; } + while (--len); p->dicPos = dicPos; } } +/* +At staring of new stream we have one of the following symbols: + - Literal - is allowed + - Non-Rep-Match - is allowed only if it's end marker symbol + - Rep-Match - is not allowed +We use early check of (RangeCoder:Code) over kBadRepCode to simplify main decoding code +*/ + #define kRange0 0xFFFFFFFF #define kBound0 ((kRange0 >> kNumBitModelTotalBits) << (kNumBitModelTotalBits - 1)) #define kBadRepCode (kBound0 + (((kRange0 - kBound0) >> kNumBitModelTotalBits) << (kNumBitModelTotalBits - 1))) @@ -621,69 +667,77 @@ #error Stop_Compiling_Bad_LZMA_Check #endif + +/* +LzmaDec_DecodeReal2(): + It calls LZMA_DECODE_REAL() and it adjusts limit according (p->checkDicSize). + +We correct (p->checkDicSize) after LZMA_DECODE_REAL() and in LzmaDec_WriteRem(), +and we support the following state of (p->checkDicSize): + if (total_processed < p->prop.dicSize) then + { + (total_processed == p->processedPos) + (p->checkDicSize == 0) + } + else + (p->checkDicSize == p->prop.dicSize) +*/ + static int MY_FAST_CALL LzmaDec_DecodeReal2(CLzmaDec *p, SizeT limit, const Byte *bufLimit) { - do + if (p->checkDicSize == 0) { - SizeT limit2 = limit; - if (p->checkDicSize == 0) - { - UInt32 rem = p->prop.dicSize - p->processedPos; - if (limit - p->dicPos > rem) - limit2 = p->dicPos + rem; - - if (p->processedPos == 0) - if (p->code >= kBadRepCode) - return SZ_ERROR_DATA; - } - - RINOK(LZMA_DECODE_REAL(p, limit2, bufLimit)); - + UInt32 rem = p->prop.dicSize - p->processedPos; + if (limit - p->dicPos > rem) + limit = p->dicPos + rem; + } + { + int res = LZMA_DECODE_REAL(p, limit, bufLimit); if (p->checkDicSize == 0 && p->processedPos >= p->prop.dicSize) p->checkDicSize = p->prop.dicSize; - - LzmaDec_WriteRem(p, limit); + return res; } - while (p->dicPos < limit && p->buf < bufLimit && p->remainLen < kMatchSpecLenStart); - - return 0; } + + typedef enum { - DUMMY_ERROR, /* unexpected end of input stream */ + DUMMY_INPUT_EOF, /* need more input data */ DUMMY_LIT, DUMMY_MATCH, DUMMY_REP } ELzmaDummy; -static ELzmaDummy LzmaDec_TryDummy(const CLzmaDec *p, const Byte *buf, SizeT inSize) + +#define IS_DUMMY_END_MARKER_POSSIBLE(dummyRes) ((dummyRes) == DUMMY_MATCH) + +static ELzmaDummy LzmaDec_TryDummy(const CLzmaDec *p, const Byte *buf, const Byte **bufOut) { UInt32 range = p->range; UInt32 code = p->code; - const Byte *bufLimit = buf + inSize; + const Byte *bufLimit = *bufOut; const CLzmaProb *probs = GET_PROBS; unsigned state = (unsigned)p->state; ELzmaDummy res; + for (;;) { const CLzmaProb *prob; UInt32 bound; unsigned ttt; - unsigned posState = CALC_POS_STATE(p->processedPos, (1 << p->prop.pb) - 1); + unsigned posState = CALC_POS_STATE(p->processedPos, ((unsigned)1 << p->prop.pb) - 1); prob = probs + IsMatch + COMBINED_PS_STATE; IF_BIT_0_CHECK(prob) { UPDATE_0_CHECK - /* if (bufLimit - buf >= 7) return DUMMY_LIT; */ - prob = probs + Literal; if (p->checkDicSize != 0 || p->processedPos != 0) prob += ((UInt32)LZMA_LIT_SIZE * - ((((p->processedPos) & ((1 << (p->prop.lp)) - 1)) << p->prop.lc) + - (p->dic[(p->dicPos == 0 ? p->dicBufSize : p->dicPos) - 1] >> (8 - p->prop.lc)))); + ((((p->processedPos) & (((unsigned)1 << (p->prop.lp)) - 1)) << p->prop.lc) + + ((unsigned)p->dic[(p->dicPos == 0 ? p->dicBufSize : p->dicPos) - 1] >> (8 - p->prop.lc)))); if (state < kNumLitStates) { @@ -735,8 +789,7 @@ IF_BIT_0_CHECK(prob) { UPDATE_0_CHECK; - NORMALIZE_CHECK; - return DUMMY_REP; + break; } else { @@ -812,8 +865,6 @@ { unsigned numDirectBits = ((posSlot >> 1) - 1); - /* if (bufLimit - buf >= 8) return DUMMY_MATCH; */ - if (posSlot < kEndPosModelIndex) { prob = probs + SpecPos + ((2 | (posSlot & 1)) << numDirectBits); @@ -844,12 +895,15 @@ } } } + break; } NORMALIZE_CHECK; + + *bufOut = buf; return res; } - +void LzmaDec_InitDicAndState(CLzmaDec *p, BoolInt initDic, BoolInt initState); void LzmaDec_InitDicAndState(CLzmaDec *p, BoolInt initDic, BoolInt initState) { p->remainLen = kMatchSpecLenStart + 1; @@ -872,16 +926,41 @@ } +/* +LZMA supports optional end_marker. +So the decoder can lookahead for one additional LZMA-Symbol to check end_marker. +That additional LZMA-Symbol can require up to LZMA_REQUIRED_INPUT_MAX bytes in input stream. +When the decoder reaches dicLimit, it looks (finishMode) parameter: + if (finishMode == LZMA_FINISH_ANY), the decoder doesn't lookahead + if (finishMode != LZMA_FINISH_ANY), the decoder lookahead, if end_marker is possible for current position + +When the decoder lookahead, and the lookahead symbol is not end_marker, we have two ways: + 1) Strict mode (default) : the decoder returns SZ_ERROR_DATA. + 2) The relaxed mode (alternative mode) : we could return SZ_OK, and the caller + must check (status) value. The caller can show the error, + if the end of stream is expected, and the (status) is noit + LZMA_STATUS_FINISHED_WITH_MARK or LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK. +*/ + + +#define RETURN__NOT_FINISHED__FOR_FINISH \ + *status = LZMA_STATUS_NOT_FINISHED; \ + return SZ_ERROR_DATA; // for strict mode + // return SZ_OK; // for relaxed mode + + SRes LzmaDec_DecodeToDic(CLzmaDec *p, SizeT dicLimit, const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status) { SizeT inSize = *srcLen; (*srcLen) = 0; - *status = LZMA_STATUS_NOT_SPECIFIED; if (p->remainLen > kMatchSpecLenStart) { + if (p->remainLen > kMatchSpecLenStart + 2) + return p->remainLen == kMatchSpecLen_Error_Fail ? SZ_ERROR_FAIL : SZ_ERROR_DATA; + for (; inSize > 0 && p->tempBufSize < RC_INIT_SIZE; (*srcLen)++, inSize--) p->tempBuf[p->tempBufSize++] = *src++; if (p->tempBufSize != 0 && p->tempBuf[0] != 0) @@ -896,6 +975,12 @@ | ((UInt32)p->tempBuf[2] << 16) | ((UInt32)p->tempBuf[3] << 8) | ((UInt32)p->tempBuf[4]); + + if (p->checkDicSize == 0 + && p->processedPos == 0 + && p->code >= kBadRepCode) + return SZ_ERROR_DATA; + p->range = 0xFFFFFFFF; p->tempBufSize = 0; @@ -913,10 +998,21 @@ p->remainLen = 0; } - LzmaDec_WriteRem(p, dicLimit); - - while (p->remainLen != kMatchSpecLenStart) + for (;;) { + if (p->remainLen == kMatchSpecLenStart) + { + if (p->code != 0) + return SZ_ERROR_DATA; + *status = LZMA_STATUS_FINISHED_WITH_MARK; + return SZ_OK; + } + + LzmaDec_WriteRem(p, dicLimit); + + { + // (p->remainLen == 0 || p->dicPos == dicLimit) + int checkEndMarkNow = 0; if (p->dicPos >= dicLimit) @@ -933,92 +1029,174 @@ } if (p->remainLen != 0) { - *status = LZMA_STATUS_NOT_FINISHED; - return SZ_ERROR_DATA; + RETURN__NOT_FINISHED__FOR_FINISH; } checkEndMarkNow = 1; } + // (p->remainLen == 0) + if (p->tempBufSize == 0) { - SizeT processed; const Byte *bufLimit; + int dummyProcessed = -1; + if (inSize < LZMA_REQUIRED_INPUT_MAX || checkEndMarkNow) { - int dummyRes = LzmaDec_TryDummy(p, src, inSize); - if (dummyRes == DUMMY_ERROR) + const Byte *bufOut = src + inSize; + + ELzmaDummy dummyRes = LzmaDec_TryDummy(p, src, &bufOut); + + if (dummyRes == DUMMY_INPUT_EOF) { - memcpy(p->tempBuf, src, inSize); - p->tempBufSize = (unsigned)inSize; + size_t i; + if (inSize >= LZMA_REQUIRED_INPUT_MAX) + break; (*srcLen) += inSize; + p->tempBufSize = (unsigned)inSize; + for (i = 0; i < inSize; i++) + p->tempBuf[i] = src[i]; *status = LZMA_STATUS_NEEDS_MORE_INPUT; return SZ_OK; } - if (checkEndMarkNow && dummyRes != DUMMY_MATCH) + + dummyProcessed = (int)(bufOut - src); + if ((unsigned)dummyProcessed > LZMA_REQUIRED_INPUT_MAX) + break; + + if (checkEndMarkNow && !IS_DUMMY_END_MARKER_POSSIBLE(dummyRes)) { - *status = LZMA_STATUS_NOT_FINISHED; - return SZ_ERROR_DATA; + unsigned i; + (*srcLen) += (unsigned)dummyProcessed; + p->tempBufSize = (unsigned)dummyProcessed; + for (i = 0; i < (unsigned)dummyProcessed; i++) + p->tempBuf[i] = src[i]; + // p->remainLen = kMatchSpecLen_Error_Data; + RETURN__NOT_FINISHED__FOR_FINISH; } + bufLimit = src; + // we will decode only one iteration } else bufLimit = src + inSize - LZMA_REQUIRED_INPUT_MAX; + p->buf = src; - if (LzmaDec_DecodeReal2(p, dicLimit, bufLimit) != 0) - return SZ_ERROR_DATA; - processed = (SizeT)(p->buf - src); - (*srcLen) += processed; - src += processed; - inSize -= processed; - } - else - { - unsigned rem = p->tempBufSize, lookAhead = 0; - while (rem < LZMA_REQUIRED_INPUT_MAX && lookAhead < inSize) - p->tempBuf[rem++] = src[lookAhead++]; - p->tempBufSize = rem; - if (rem < LZMA_REQUIRED_INPUT_MAX || checkEndMarkNow) + { - int dummyRes = LzmaDec_TryDummy(p, p->tempBuf, (SizeT)rem); - if (dummyRes == DUMMY_ERROR) + int res = LzmaDec_DecodeReal2(p, dicLimit, bufLimit); + + SizeT processed = (SizeT)(p->buf - src); + + if (dummyProcessed < 0) { - (*srcLen) += (SizeT)lookAhead; - *status = LZMA_STATUS_NEEDS_MORE_INPUT; - return SZ_OK; + if (processed > inSize) + break; } - if (checkEndMarkNow && dummyRes != DUMMY_MATCH) + else if ((unsigned)dummyProcessed != processed) + break; + + src += processed; + inSize -= processed; + (*srcLen) += processed; + + if (res != SZ_OK) { - *status = LZMA_STATUS_NOT_FINISHED; + p->remainLen = kMatchSpecLen_Error_Data; return SZ_ERROR_DATA; } } + continue; + } + + { + // we have some data in (p->tempBuf) + // in strict mode: tempBufSize is not enough for one Symbol decoding. + // in relaxed mode: tempBufSize not larger than required for one Symbol decoding. + + unsigned rem = p->tempBufSize; + unsigned ahead = 0; + int dummyProcessed = -1; + + while (rem < LZMA_REQUIRED_INPUT_MAX && ahead < inSize) + p->tempBuf[rem++] = src[ahead++]; + + // ahead - the size of new data copied from (src) to (p->tempBuf) + // rem - the size of temp buffer including new data from (src) + + if (rem < LZMA_REQUIRED_INPUT_MAX || checkEndMarkNow) + { + const Byte *bufOut = p->tempBuf + rem; + + ELzmaDummy dummyRes = LzmaDec_TryDummy(p, p->tempBuf, &bufOut); + + if (dummyRes == DUMMY_INPUT_EOF) + { + if (rem >= LZMA_REQUIRED_INPUT_MAX) + break; + p->tempBufSize = rem; + (*srcLen) += (SizeT)ahead; + *status = LZMA_STATUS_NEEDS_MORE_INPUT; + return SZ_OK; + } + + dummyProcessed = (int)(bufOut - p->tempBuf); + + if ((unsigned)dummyProcessed < p->tempBufSize) + break; + + if (checkEndMarkNow && !IS_DUMMY_END_MARKER_POSSIBLE(dummyRes)) + { + (*srcLen) += (unsigned)dummyProcessed - p->tempBufSize; + p->tempBufSize = (unsigned)dummyProcessed; + // p->remainLen = kMatchSpecLen_Error_Data; + RETURN__NOT_FINISHED__FOR_FINISH; + } + } + p->buf = p->tempBuf; - if (LzmaDec_DecodeReal2(p, dicLimit, p->buf) != 0) - return SZ_ERROR_DATA; { - unsigned kkk = (unsigned)(p->buf - p->tempBuf); - if (rem < kkk) - return SZ_ERROR_FAIL; /* some internal error */ - rem -= kkk; - if (lookAhead < rem) - return SZ_ERROR_FAIL; /* some internal error */ - lookAhead -= rem; + // we decode one symbol from (p->tempBuf) here, so the (bufLimit) is equal to (p->buf) + int res = LzmaDec_DecodeReal2(p, dicLimit, p->buf); + + SizeT processed = (SizeT)(p->buf - p->tempBuf); + rem = p->tempBufSize; + + if (dummyProcessed < 0) + { + if (processed > LZMA_REQUIRED_INPUT_MAX) + break; + if (processed < rem) + break; + } + else if ((unsigned)dummyProcessed != processed) + break; + + processed -= rem; + + src += processed; + inSize -= processed; + (*srcLen) += processed; + p->tempBufSize = 0; + + if (res != SZ_OK) + { + p->remainLen = kMatchSpecLen_Error_Data; + return SZ_ERROR_DATA; + } } - (*srcLen) += (SizeT)lookAhead; - src += lookAhead; - inSize -= (SizeT)lookAhead; - p->tempBufSize = 0; } + } } - - if (p->code != 0) - return SZ_ERROR_DATA; - *status = LZMA_STATUS_FINISHED_WITH_MARK; - return SZ_OK; + + /* Some unexpected error: internal error of code, memory corruption or hardware failure */ + p->remainLen = kMatchSpecLen_Error_Fail; + return SZ_ERROR_FAIL; } + SRes LzmaDec_DecodeToBuf(CLzmaDec *p, Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status) { SizeT outSize = *destLen;
diff --git a/third_party/lzma_sdk/LzmaDec.h b/third_party/lzma_sdk/LzmaDec.h index 1f0927a..6f12962 100644 --- a/third_party/lzma_sdk/LzmaDec.h +++ b/third_party/lzma_sdk/LzmaDec.h
@@ -1,5 +1,5 @@ /* LzmaDec.h -- LZMA Decoder -2018-04-21 : Igor Pavlov : Public domain */ +2020-03-19 : Igor Pavlov : Public domain */ #ifndef __LZMA_DEC_H #define __LZMA_DEC_H @@ -181,6 +181,7 @@ LZMA_STATUS_NEEDS_MORE_INPUT LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK SZ_ERROR_DATA - Data error + SZ_ERROR_FAIL - Some unexpected error: internal error of code, memory corruption or hardware failure */ SRes LzmaDec_DecodeToDic(CLzmaDec *p, SizeT dicLimit, @@ -223,6 +224,7 @@ SZ_ERROR_MEM - Memory allocation error SZ_ERROR_UNSUPPORTED - Unsupported properties SZ_ERROR_INPUT_EOF - It needs more bytes in input buffer (src). + SZ_ERROR_FAIL - Some unexpected error: internal error of code, memory corruption or hardware failure */ SRes LzmaDecode(Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen,
diff --git a/third_party/lzma_sdk/LzmaEnc.c b/third_party/lzma_sdk/LzmaEnc.c index 46a0db0..b04a7b7b 100644 --- a/third_party/lzma_sdk/LzmaEnc.c +++ b/third_party/lzma_sdk/LzmaEnc.c
@@ -1,5 +1,5 @@ /* LzmaEnc.c -- LZMA Encoder -2019-01-10: Igor Pavlov : Public domain */ +2021-11-18: Igor Pavlov : Public domain */ #include "Precomp.h" @@ -12,6 +12,7 @@ #include <stdio.h> #endif +#include "CpuArch.h" #include "LzmaEnc.h" #include "LzFind.h" @@ -19,12 +20,25 @@ #include "LzFindMt.h" #endif +/* the following LzmaEnc_* declarations is internal LZMA interface for LZMA2 encoder */ + +SRes LzmaEnc_PrepareForLzma2(CLzmaEncHandle pp, ISeqInStream *inStream, UInt32 keepWindowSize, + ISzAllocPtr alloc, ISzAllocPtr allocBig); +SRes LzmaEnc_MemPrepare(CLzmaEncHandle pp, const Byte *src, SizeT srcLen, + UInt32 keepWindowSize, ISzAllocPtr alloc, ISzAllocPtr allocBig); +SRes LzmaEnc_CodeOneMemBlock(CLzmaEncHandle pp, BoolInt reInit, + Byte *dest, size_t *destLen, UInt32 desiredPackSize, UInt32 *unpackSize); +const Byte *LzmaEnc_GetCurBuf(CLzmaEncHandle pp); +void LzmaEnc_Finish(CLzmaEncHandle pp); +void LzmaEnc_SaveState(CLzmaEncHandle pp); +void LzmaEnc_RestoreState(CLzmaEncHandle pp); + #ifdef SHOW_STAT static unsigned g_STAT_OFFSET = 0; #endif -#define kLzmaMaxHistorySize ((UInt32)3 << 29) -/* #define kLzmaMaxHistorySize ((UInt32)7 << 29) */ +/* for good normalization speed we still reserve 256 MB before 4 GB range */ +#define kLzmaMaxHistorySize ((UInt32)15 << 28) #define kNumTopBits 24 #define kTopValue ((UInt32)1 << kNumTopBits) @@ -36,7 +50,7 @@ #define kNumMoveReducingBits 4 #define kNumBitPriceShiftBits 4 -#define kBitPrice (1 << kNumBitPriceShiftBits) +// #define kBitPrice (1 << kNumBitPriceShiftBits) #define REP_LEN_COUNT 64 @@ -47,6 +61,7 @@ p->reduceSize = (UInt64)(Int64)-1; p->lc = p->lp = p->pb = p->algo = p->fb = p->btMode = p->numHashBytes = p->numThreads = -1; p->writeEndMark = 0; + p->affinity = 0; } void LzmaEncProps_Normalize(CLzmaEncProps *p) @@ -55,16 +70,21 @@ if (level < 0) level = 5; p->level = level; - if (p->dictSize == 0) p->dictSize = (level <= 5 ? (1 << (level * 2 + 14)) : (level <= 7 ? (1 << 25) : (1 << 26))); + if (p->dictSize == 0) + p->dictSize = + ( level <= 3 ? ((UInt32)1 << (level * 2 + 16)) : + ( level <= 6 ? ((UInt32)1 << (level + 19)) : + ( level <= 7 ? ((UInt32)1 << 25) : ((UInt32)1 << 26) + ))); + if (p->dictSize > p->reduceSize) { - unsigned i; - UInt32 reduceSize = (UInt32)p->reduceSize; - for (i = 11; i <= 30; i++) - { - if (reduceSize <= ((UInt32)2 << i)) { p->dictSize = ((UInt32)2 << i); break; } - if (reduceSize <= ((UInt32)3 << i)) { p->dictSize = ((UInt32)3 << i); break; } - } + UInt32 v = (UInt32)p->reduceSize; + const UInt32 kReduceMin = ((UInt32)1 << 12); + if (v < kReduceMin) + v = kReduceMin; + if (p->dictSize > v) + p->dictSize = v; } if (p->lc < 0) p->lc = 3; @@ -74,8 +94,8 @@ if (p->algo < 0) p->algo = (level < 5 ? 0 : 1); if (p->fb < 0) p->fb = (level < 7 ? 32 : 64); if (p->btMode < 0) p->btMode = (p->algo == 0 ? 0 : 1); - if (p->numHashBytes < 0) p->numHashBytes = 4; - if (p->mc == 0) p->mc = (16 + (p->fb >> 1)) >> (p->btMode ? 0 : 1); + if (p->numHashBytes < 0) p->numHashBytes = (p->btMode ? 4 : 5); + if (p->mc == 0) p->mc = (16 + ((unsigned)p->fb >> 1)) >> (p->btMode ? 0 : 1); if (p->numThreads < 0) p->numThreads = @@ -93,18 +113,85 @@ return props.dictSize; } -#if (_MSC_VER >= 1400) -/* BSR code is fast for some new CPUs */ -/* #define LZMA_LOG_BSR */ + +/* +x86/x64: + +BSR: + IF (SRC == 0) ZF = 1, DEST is undefined; + AMD : DEST is unchanged; + IF (SRC != 0) ZF = 0; DEST is index of top non-zero bit + BSR is slow in some processors + +LZCNT: + IF (SRC == 0) CF = 1, DEST is size_in_bits_of_register(src) (32 or 64) + IF (SRC != 0) CF = 0, DEST = num_lead_zero_bits + IF (DEST == 0) ZF = 1; + +LZCNT works only in new processors starting from Haswell. +if LZCNT is not supported by processor, then it's executed as BSR. +LZCNT can be faster than BSR, if supported. +*/ + +// #define LZMA_LOG_BSR + +#if defined(MY_CPU_ARM_OR_ARM64) /* || defined(MY_CPU_X86_OR_AMD64) */ + + #if (defined(__clang__) && (__clang_major__ >= 6)) \ + || (defined(__GNUC__) && (__GNUC__ >= 6)) + #define LZMA_LOG_BSR + #elif defined(_MSC_VER) && (_MSC_VER >= 1300) + // #if defined(MY_CPU_ARM_OR_ARM64) + #define LZMA_LOG_BSR + // #endif + #endif #endif +// #include <intrin.h> + #ifdef LZMA_LOG_BSR -#define kDicLogSizeMaxCompress 32 +#if defined(__clang__) \ + || defined(__GNUC__) -#define BSR2_RET(pos, res) { unsigned long zz; _BitScanReverse(&zz, (pos)); res = (zz + zz) + ((pos >> (zz - 1)) & 1); } +/* + C code: : (30 - __builtin_clz(x)) + gcc9/gcc10 for x64 /x86 : 30 - (bsr(x) xor 31) + clang10 for x64 : 31 + (bsr(x) xor -32) +*/ -static unsigned GetPosSlot1(UInt32 pos) + #define MY_clz(x) ((unsigned)__builtin_clz(x)) + // __lzcnt32 + // __builtin_ia32_lzcnt_u32 + +#else // #if defined(_MSC_VER) + + #ifdef MY_CPU_ARM_OR_ARM64 + + #define MY_clz _CountLeadingZeros + + #else // if defined(MY_CPU_X86_OR_AMD64) + + // #define MY_clz __lzcnt // we can use lzcnt (unsupported by old CPU) + // _BitScanReverse code is not optimal for some MSVC compilers + #define BSR2_RET(pos, res) { unsigned long zz; _BitScanReverse(&zz, (pos)); zz--; \ + res = (zz + zz) + (pos >> zz); } + + #endif // MY_CPU_X86_OR_AMD64 + +#endif // _MSC_VER + + +#ifndef BSR2_RET + + #define BSR2_RET(pos, res) { unsigned zz = 30 - MY_clz(pos); \ + res = (zz + zz) + (pos >> zz); } + +#endif + + +unsigned GetPosSlot1(UInt32 pos); +unsigned GetPosSlot1(UInt32 pos) { unsigned res; BSR2_RET(pos, res); @@ -113,10 +200,10 @@ #define GetPosSlot2(pos, res) { BSR2_RET(pos, res); } #define GetPosSlot(pos, res) { if (pos < 2) res = pos; else BSR2_RET(pos, res); } -#else -#define kNumLogBits (9 + sizeof(size_t) / 2) -/* #define kNumLogBits (11 + sizeof(size_t) / 8 * 3) */ +#else // ! LZMA_LOG_BSR + +#define kNumLogBits (11 + sizeof(size_t) / 8 * 3) #define kDicLogSizeMaxCompress ((kNumLogBits - 1) * 2 + 7) @@ -163,7 +250,7 @@ #define GetPosSlot2(pos, res) { BSR2_RET(pos, res); } #define GetPosSlot(pos, res) { if (pos < kNumFullDistances) res = p->g_FastPos[pos & (kNumFullDistances - 1)]; else BSR2_RET(pos, res); } -#endif +#endif // LZMA_LOG_BSR #define LZMA_NUM_REPS 4 @@ -193,7 +280,7 @@ #define kNumLenToPosStates 4 #define kNumPosSlotBits 6 -#define kDicLogSizeMin 0 +// #define kDicLogSizeMin 0 #define kDicLogSizeMax 32 #define kDistTableSizeMax (kDicLogSizeMax * 2) @@ -299,7 +386,7 @@ typedef struct { void *matchFinderObj; - IMatchFinder matchFinder; + IMatchFinder2 matchFinder; unsigned optCur; unsigned optEnd; @@ -344,10 +431,14 @@ // begin of CMatchFinderMt is used in LZ thread CMatchFinderMt matchFinderMt; // end of CMatchFinderMt is used in BT and HASH threads + // #else + // CMatchFinder matchFinderBase; #endif - CMatchFinder matchFinderBase; + + // we suppose that we have 8-bytes alignment after CMatchFinder + #ifndef _7ZIP_ST Byte pad[128]; #endif @@ -355,8 +446,10 @@ // LZ thread CProbPrice ProbPrices[kBitModelTotal >> kNumMoveReducingBits]; - UInt32 matches[LZMA_MATCH_LEN_MAX * 2 + 2 + 1]; + // we want {len , dist} pairs to be 8-bytes aligned in matches array + UInt32 matches[LZMA_MATCH_LEN_MAX * 2 + 2]; + // we want 8-bytes alignment here UInt32 alignPrices[kAlignTableSize]; UInt32 posSlotPrices[kNumLenToPosStates][kDistTableSizeMax]; UInt32 distancesPrices[kNumLenToPosStates][kNumFullDistances]; @@ -385,12 +478,19 @@ CSaveState saveState; + // BoolInt mf_Failure; #ifndef _7ZIP_ST Byte pad2[128]; #endif } CLzmaEnc; +#define MFB (p->matchFinderBase) +/* +#ifndef _7ZIP_ST +#define MFB (p->matchFinderMt.MatchFinder) +#endif +*/ #define COPY_ARR(dest, src, arr) memcpy(dest->arr, src->arr, sizeof(src->arr)); @@ -455,41 +555,51 @@ if (props.lc > LZMA_LC_MAX || props.lp > LZMA_LP_MAX - || props.pb > LZMA_PB_MAX - || props.dictSize > ((UInt64)1 << kDicLogSizeMaxCompress) - || props.dictSize > kLzmaMaxHistorySize) + || props.pb > LZMA_PB_MAX) return SZ_ERROR_PARAM; + + if (props.dictSize > kLzmaMaxHistorySize) + props.dictSize = kLzmaMaxHistorySize; + + #ifndef LZMA_LOG_BSR + { + const UInt64 dict64 = props.dictSize; + if (dict64 > ((UInt64)1 << kDicLogSizeMaxCompress)) + return SZ_ERROR_PARAM; + } + #endif + p->dictSize = props.dictSize; { - unsigned fb = props.fb; + unsigned fb = (unsigned)props.fb; if (fb < 5) fb = 5; if (fb > LZMA_MATCH_LEN_MAX) fb = LZMA_MATCH_LEN_MAX; p->numFastBytes = fb; } - p->lc = props.lc; - p->lp = props.lp; - p->pb = props.pb; + p->lc = (unsigned)props.lc; + p->lp = (unsigned)props.lp; + p->pb = (unsigned)props.pb; p->fastMode = (props.algo == 0); // p->_maxMode = True; - p->matchFinderBase.btMode = (Byte)(props.btMode ? 1 : 0); + MFB.btMode = (Byte)(props.btMode ? 1 : 0); { unsigned numHashBytes = 4; if (props.btMode) { - if (props.numHashBytes < 2) - numHashBytes = 2; - else if (props.numHashBytes < 4) - numHashBytes = props.numHashBytes; + if (props.numHashBytes < 2) numHashBytes = 2; + else if (props.numHashBytes < 4) numHashBytes = (unsigned)props.numHashBytes; } - p->matchFinderBase.numHashBytes = numHashBytes; + if (props.numHashBytes >= 5) numHashBytes = 5; + + MFB.numHashBytes = numHashBytes; } - p->matchFinderBase.cutValue = props.mc; + MFB.cutValue = props.mc; - p->writeEndMark = props.writeEndMark; + p->writeEndMark = (BoolInt)props.writeEndMark; #ifndef _7ZIP_ST /* @@ -500,6 +610,8 @@ } */ p->multiThread = (props.numThreads > 1); + p->matchFinderMt.btSync.affinity = + p->matchFinderMt.hashSync.affinity = props.affinity; #endif return SZ_OK; @@ -509,7 +621,7 @@ void LzmaEnc_SetDataSize(CLzmaEncHandle pp, UInt64 expectedDataSiize) { CLzmaEnc *p = (CLzmaEnc *)pp; - p->matchFinderBase.expectedDataSize = expectedDataSiize; + MFB.expectedDataSize = expectedDataSiize; } @@ -536,8 +648,8 @@ p->bufBase = NULL; } -#define RangeEnc_GetProcessed(p) ((p)->processed + ((p)->buf - (p)->bufBase) + (p)->cacheSize) -#define RangeEnc_GetProcessed_sizet(p) ((size_t)(p)->processed + ((p)->buf - (p)->bufBase) + (size_t)(p)->cacheSize) +#define RangeEnc_GetProcessed(p) ( (p)->processed + (size_t)((p)->buf - (p)->bufBase) + (p)->cacheSize) +#define RangeEnc_GetProcessed_sizet(p) ((size_t)(p)->processed + (size_t)((p)->buf - (p)->bufBase) + (size_t)(p)->cacheSize) #define RC_BUF_SIZE (1 << 16) @@ -556,12 +668,11 @@ static void RangeEnc_Free(CRangeEnc *p, ISzAllocPtr alloc) { ISzAlloc_Free(alloc, p->bufBase); - p->bufBase = 0; + p->bufBase = NULL; } static void RangeEnc_Init(CRangeEnc *p) { - /* Stream.Init(); */ p->range = 0xFFFFFFFF; p->cache = 0; p->low = 0; @@ -575,12 +686,12 @@ MY_NO_INLINE static void RangeEnc_FlushStream(CRangeEnc *p) { - size_t num; - if (p->res != SZ_OK) - return; - num = p->buf - p->bufBase; - if (num != ISeqOutStream_Write(p->outStream, p->bufBase, num)) - p->res = SZ_ERROR_WRITE; + const size_t num = (size_t)(p->buf - p->bufBase); + if (p->res == SZ_OK) + { + if (num != ISeqOutStream_Write(p->outStream, p->bufBase, num)) + p->res = SZ_ERROR_WRITE; + } p->processed += num; p->buf = p->bufBase; } @@ -656,7 +767,7 @@ range += newBound & mask; \ mask &= (kBitModelTotal - ((1 << kNumMoveBits) - 1)); \ mask += ((1 << kNumMoveBits) - 1); \ - ttt += (Int32)(mask - ttt) >> kNumMoveBits; \ + ttt += (UInt32)((Int32)(mask - ttt) >> kNumMoveBits); \ *(prob) = (CLzmaProb)ttt; \ RC_NORM(p) \ } @@ -749,7 +860,7 @@ bitCount++; } } - ProbPrices[i] = (CProbPrice)((kNumBitModelTotalBits << kCyclesBits) - 15 - bitCount); + ProbPrices[i] = (CProbPrice)(((unsigned)kNumBitModelTotalBits << kCyclesBits) - 15 - bitCount); // printf("\n%3d: %5d", i, ProbPrices[i]); } } @@ -985,7 +1096,11 @@ p->additionalOffset++; p->numAvail = p->matchFinder.GetNumAvailableBytes(p->matchFinderObj); - numPairs = p->matchFinder.GetMatches(p->matchFinderObj, p->matches); + { + const UInt32 *d = p->matchFinder.GetMatches(p->matchFinderObj, p->matches); + // if (!d) { p->mf_Failure = True; *numPairsRes = 0; return 0; } + numPairs = (unsigned)(d - p->matches); + } *numPairsRes = numPairs; #ifdef SHOW_STAT @@ -1001,7 +1116,7 @@ if (numPairs == 0) return 0; { - unsigned len = p->matches[(size_t)numPairs - 2]; + const unsigned len = p->matches[(size_t)numPairs - 2]; if (len != p->numFastBytes) return len; { @@ -1011,7 +1126,7 @@ { const Byte *p1 = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - 1; const Byte *p2 = p1 + len; - ptrdiff_t dif = (ptrdiff_t)-1 - p->matches[(size_t)numPairs - 1]; + const ptrdiff_t dif = (ptrdiff_t)-1 - (ptrdiff_t)p->matches[(size_t)numPairs - 1]; const Byte *lim = p1 + numAvail; for (; p2 != lim && *p2 == p2[dif]; p2++) {} @@ -1167,6 +1282,8 @@ repLens[i] = len; if (len > repLens[repMaxIndex]) repMaxIndex = i; + if (len == LZMA_MATCH_LEN_MAX) // 21.03 : optimization + break; } if (repLens[repMaxIndex] >= p->numFastBytes) @@ -1179,10 +1296,12 @@ } matches = p->matches; + #define MATCHES matches + // #define MATCHES p->matches if (mainLen >= p->numFastBytes) { - p->backRes = matches[(size_t)numPairs - 1] + LZMA_NUM_REPS; + p->backRes = MATCHES[(size_t)numPairs - 1] + LZMA_NUM_REPS; MOVE_POS(p, mainLen - 1) return mainLen; } @@ -1276,13 +1395,13 @@ if (len < 2) len = 2; else - while (len > matches[offs]) + while (len > MATCHES[offs]) offs += 2; for (; ; len++) { COptimal *opt; - UInt32 dist = matches[(size_t)offs + 1]; + UInt32 dist = MATCHES[(size_t)offs + 1]; UInt32 price = normalMatchPrice + GET_PRICE_LEN(&p->lenEnc, posState, len); unsigned lenToPosState = GetLenToPosState(len); @@ -1306,7 +1425,7 @@ opt->extra = 0; } - if (len == matches[offs]) + if (len == MATCHES[offs]) { offs += 2; if (offs == numPairs) @@ -1727,8 +1846,8 @@ if (newLen > numAvail) { newLen = numAvail; - for (numPairs = 0; newLen > matches[numPairs]; numPairs += 2); - matches[numPairs] = (UInt32)newLen; + for (numPairs = 0; newLen > MATCHES[numPairs]; numPairs += 2); + MATCHES[numPairs] = (UInt32)newLen; numPairs += 2; } @@ -1747,9 +1866,9 @@ } offs = 0; - while (startLen > matches[offs]) + while (startLen > MATCHES[offs]) offs += 2; - dist = matches[(size_t)offs + 1]; + dist = MATCHES[(size_t)offs + 1]; // if (dist >= kNumFullDistances) GetPosSlot2(dist, posSlot); @@ -1776,7 +1895,7 @@ } } - if (len == matches[offs]) + if (len == MATCHES[offs]) { // if (p->_maxMode) { // MATCH : LIT : REP_0 @@ -1841,7 +1960,7 @@ offs += 2; if (offs == numPairs) break; - dist = matches[(size_t)offs + 1]; + dist = MATCHES[(size_t)offs + 1]; // if (dist >= kNumFullDistances) GetPosSlot2(dist, posSlot); } @@ -2059,8 +2178,23 @@ return p->result; if (p->rc.res != SZ_OK) p->result = SZ_ERROR_WRITE; - if (p->matchFinderBase.result != SZ_OK) + + #ifndef _7ZIP_ST + if ( + // p->mf_Failure || + (p->mtMode && + ( // p->matchFinderMt.failure_LZ_LZ || + p->matchFinderMt.failure_LZ_BT)) + ) + { + p->result = MY_HRES_ERROR__INTERNAL_ERROR; + // printf("\nCheckErrors p->matchFinderMt.failureLZ\n"); + } + #endif + + if (MFB.result != SZ_OK) p->result = SZ_ERROR_READ; + if (p->result != SZ_OK) p->finished = True; return p->result; @@ -2198,14 +2332,14 @@ -void LzmaEnc_Construct(CLzmaEnc *p) +static void LzmaEnc_Construct(CLzmaEnc *p) { RangeEnc_Construct(&p->rc); - MatchFinder_Construct(&p->matchFinderBase); + MatchFinder_Construct(&MFB); #ifndef _7ZIP_ST + p->matchFinderMt.MatchFinder = &MFB; MatchFinderMt_Construct(&p->matchFinderMt); - p->matchFinderMt.MatchFinder = &p->matchFinderBase; #endif { @@ -2221,7 +2355,6 @@ LzmaEnc_InitPriceTables(p->ProbPrices); p->litProbs = NULL; p->saveState.litProbs = NULL; - } CLzmaEncHandle LzmaEnc_Create(ISzAllocPtr alloc) @@ -2233,7 +2366,7 @@ return p; } -void LzmaEnc_FreeLits(CLzmaEnc *p, ISzAllocPtr alloc) +static void LzmaEnc_FreeLits(CLzmaEnc *p, ISzAllocPtr alloc) { ISzAlloc_Free(alloc, p->litProbs); ISzAlloc_Free(alloc, p->saveState.litProbs); @@ -2241,13 +2374,13 @@ p->saveState.litProbs = NULL; } -void LzmaEnc_Destruct(CLzmaEnc *p, ISzAllocPtr alloc, ISzAllocPtr allocBig) +static void LzmaEnc_Destruct(CLzmaEnc *p, ISzAllocPtr alloc, ISzAllocPtr allocBig) { #ifndef _7ZIP_ST MatchFinderMt_Destruct(&p->matchFinderMt, allocBig); #endif - MatchFinder_Free(&p->matchFinderBase, allocBig); + MatchFinder_Free(&MFB, allocBig); LzmaEnc_FreeLits(p, alloc); RangeEnc_Free(&p->rc, alloc); } @@ -2259,11 +2392,18 @@ } +MY_NO_INLINE static SRes LzmaEnc_CodeOneBlock(CLzmaEnc *p, UInt32 maxPackSize, UInt32 maxUnpackSize) { UInt32 nowPos32, startPos32; if (p->needInit) { + #ifndef _7ZIP_ST + if (p->mtMode) + { + RINOK(MatchFinderMt_InitMt(&p->matchFinderMt)); + } + #endif p->matchFinder.Init(p->matchFinderObj); p->needInit = 0; } @@ -2521,12 +2661,12 @@ // { int y; for (y = 0; y < 100; y++) { FillDistancesPrices(p); // }} - LenPriceEnc_UpdateTables(&p->lenEnc, 1 << p->pb, &p->lenProbs, p->ProbPrices); + LenPriceEnc_UpdateTables(&p->lenEnc, (unsigned)1 << p->pb, &p->lenProbs, p->ProbPrices); } if (p->repLenEncCounter <= 0) { p->repLenEncCounter = REP_LEN_COUNT; - LenPriceEnc_UpdateTables(&p->repLenEnc, 1 << p->pb, &p->repLenProbs, p->ProbPrices); + LenPriceEnc_UpdateTables(&p->repLenEnc, (unsigned)1 << p->pb, &p->repLenProbs, p->ProbPrices); } } @@ -2559,11 +2699,13 @@ static SRes LzmaEnc_Alloc(CLzmaEnc *p, UInt32 keepWindowSize, ISzAllocPtr alloc, ISzAllocPtr allocBig) { UInt32 beforeSize = kNumOpts; + UInt32 dictSize; + if (!RangeEnc_Alloc(&p->rc, alloc)) return SZ_ERROR_MEM; #ifndef _7ZIP_ST - p->mtMode = (p->multiThread && !p->fastMode && (p->matchFinderBase.btMode != 0)); + p->mtMode = (p->multiThread && !p->fastMode && (MFB.btMode != 0)); #endif { @@ -2582,36 +2724,56 @@ } } - p->matchFinderBase.bigHash = (Byte)(p->dictSize > kBigHashDicLimit ? 1 : 0); + MFB.bigHash = (Byte)(p->dictSize > kBigHashDicLimit ? 1 : 0); - if (beforeSize + p->dictSize < keepWindowSize) - beforeSize = keepWindowSize - p->dictSize; + + dictSize = p->dictSize; + if (dictSize == ((UInt32)2 << 30) || + dictSize == ((UInt32)3 << 30)) + { + /* 21.03 : here we reduce the dictionary for 2 reasons: + 1) we don't want 32-bit back_distance matches in decoder for 2 GB dictionary. + 2) we want to elimate useless last MatchFinder_Normalize3() for corner cases, + where data size is aligned for 1 GB: 5/6/8 GB. + That reducing must be >= 1 for such corner cases. */ + dictSize -= 1; + } + + if (beforeSize + dictSize < keepWindowSize) + beforeSize = keepWindowSize - dictSize; + + /* in worst case we can look ahead for + max(LZMA_MATCH_LEN_MAX, numFastBytes + 1 + numFastBytes) bytes. + we send larger value for (keepAfter) to MantchFinder_Create(): + (numFastBytes + LZMA_MATCH_LEN_MAX + 1) + */ #ifndef _7ZIP_ST if (p->mtMode) { - RINOK(MatchFinderMt_Create(&p->matchFinderMt, p->dictSize, beforeSize, p->numFastBytes, - LZMA_MATCH_LEN_MAX - + 1 /* 18.04 */ + RINOK(MatchFinderMt_Create(&p->matchFinderMt, dictSize, beforeSize, + p->numFastBytes, LZMA_MATCH_LEN_MAX + 1 /* 18.04 */ , allocBig)); p->matchFinderObj = &p->matchFinderMt; - p->matchFinderBase.bigHash = (Byte)( - (p->dictSize > kBigHashDicLimit && p->matchFinderBase.hashMask >= 0xFFFFFF) ? 1 : 0); + MFB.bigHash = (Byte)( + (p->dictSize > kBigHashDicLimit && MFB.hashMask >= 0xFFFFFF) ? 1 : 0); MatchFinderMt_CreateVTable(&p->matchFinderMt, &p->matchFinder); } else #endif { - if (!MatchFinder_Create(&p->matchFinderBase, p->dictSize, beforeSize, p->numFastBytes, LZMA_MATCH_LEN_MAX, allocBig)) + if (!MatchFinder_Create(&MFB, dictSize, beforeSize, + p->numFastBytes, LZMA_MATCH_LEN_MAX + 1 /* 21.03 */ + , allocBig)) return SZ_ERROR_MEM; - p->matchFinderObj = &p->matchFinderBase; - MatchFinder_CreateVTable(&p->matchFinderBase, &p->matchFinder); + p->matchFinderObj = &MFB; + MatchFinder_CreateVTable(&MFB, &p->matchFinder); } return SZ_OK; } -void LzmaEnc_Init(CLzmaEnc *p) +static void LzmaEnc_Init(CLzmaEnc *p) { unsigned i; p->state = 0; @@ -2675,12 +2837,14 @@ p->additionalOffset = 0; - p->pbMask = (1 << p->pb) - 1; + p->pbMask = ((unsigned)1 << p->pb) - 1; p->lpMask = ((UInt32)0x100 << p->lp) - ((unsigned)0x100 >> p->lc); + + // p->mf_Failure = False; } -void LzmaEnc_InitPrices(CLzmaEnc *p) +static void LzmaEnc_InitPrices(CLzmaEnc *p) { if (!p->fastMode) { @@ -2694,8 +2858,8 @@ p->repLenEncCounter = REP_LEN_COUNT; - LenPriceEnc_UpdateTables(&p->lenEnc, 1 << p->pb, &p->lenProbs, p->ProbPrices); - LenPriceEnc_UpdateTables(&p->repLenEnc, 1 << p->pb, &p->repLenProbs, p->ProbPrices); + LenPriceEnc_UpdateTables(&p->lenEnc, (unsigned)1 << p->pb, &p->lenProbs, p->ProbPrices); + LenPriceEnc_UpdateTables(&p->repLenEnc, (unsigned)1 << p->pb, &p->repLenProbs, p->ProbPrices); } static SRes LzmaEnc_AllocAndInit(CLzmaEnc *p, UInt32 keepWindowSize, ISzAllocPtr alloc, ISzAllocPtr allocBig) @@ -2719,7 +2883,7 @@ ISzAllocPtr alloc, ISzAllocPtr allocBig) { CLzmaEnc *p = (CLzmaEnc *)pp; - p->matchFinderBase.stream = inStream; + MFB.stream = inStream; p->needInit = 1; p->rc.outStream = outStream; return LzmaEnc_AllocAndInit(p, 0, alloc, allocBig); @@ -2730,16 +2894,16 @@ ISzAllocPtr alloc, ISzAllocPtr allocBig) { CLzmaEnc *p = (CLzmaEnc *)pp; - p->matchFinderBase.stream = inStream; + MFB.stream = inStream; p->needInit = 1; return LzmaEnc_AllocAndInit(p, keepWindowSize, alloc, allocBig); } static void LzmaEnc_SetInputBuf(CLzmaEnc *p, const Byte *src, SizeT srcLen) { - p->matchFinderBase.directInput = 1; - p->matchFinderBase.bufferBase = (Byte *)src; - p->matchFinderBase.directInputRem = srcLen; + MFB.directInput = 1; + MFB.bufferBase = (Byte *)src; + MFB.directInputRem = srcLen; } SRes LzmaEnc_MemPrepare(CLzmaEncHandle pp, const Byte *src, SizeT srcLen, @@ -2781,19 +2945,23 @@ size = p->rem; p->overflow = True; } - memcpy(p->data, data, size); - p->rem -= size; - p->data += size; + if (size != 0) + { + memcpy(p->data, data, size); + p->rem -= size; + p->data += size; + } return size; } +/* UInt32 LzmaEnc_GetNumAvailableBytes(CLzmaEncHandle pp) { const CLzmaEnc *p = (CLzmaEnc *)pp; return p->matchFinder.GetNumAvailableBytes(p->matchFinderObj); } - +*/ const Byte *LzmaEnc_GetCurBuf(CLzmaEncHandle pp) { @@ -2841,6 +3009,7 @@ } +MY_NO_INLINE static SRes LzmaEnc_Encode2(CLzmaEnc *p, ICompressProgress *progress) { SRes res = SZ_OK; @@ -2870,7 +3039,7 @@ LzmaEnc_Finish(p); /* - if (res == SZ_OK && !Inline_MatchFinder_IsFinishedOK(&p->matchFinderBase)) + if (res == SZ_OK && !Inline_MatchFinder_IsFinishedOK(&MFB)) res = SZ_ERROR_FAIL; } */ @@ -2889,35 +3058,43 @@ SRes LzmaEnc_WriteProperties(CLzmaEncHandle pp, Byte *props, SizeT *size) { - CLzmaEnc *p = (CLzmaEnc *)pp; - unsigned i; - UInt32 dictSize = p->dictSize; if (*size < LZMA_PROPS_SIZE) return SZ_ERROR_PARAM; *size = LZMA_PROPS_SIZE; - props[0] = (Byte)((p->pb * 5 + p->lp) * 9 + p->lc); - - if (dictSize >= ((UInt32)1 << 22)) { - UInt32 kDictMask = ((UInt32)1 << 20) - 1; - if (dictSize < (UInt32)0xFFFFFFFF - kDictMask) - dictSize = (dictSize + kDictMask) & ~kDictMask; - } - else for (i = 11; i <= 30; i++) - { - if (dictSize <= ((UInt32)2 << i)) { dictSize = (2 << i); break; } - if (dictSize <= ((UInt32)3 << i)) { dictSize = (3 << i); break; } - } + const CLzmaEnc *p = (const CLzmaEnc *)pp; + const UInt32 dictSize = p->dictSize; + UInt32 v; + props[0] = (Byte)((p->pb * 5 + p->lp) * 9 + p->lc); + + // we write aligned dictionary value to properties for lzma decoder + if (dictSize >= ((UInt32)1 << 21)) + { + const UInt32 kDictMask = ((UInt32)1 << 20) - 1; + v = (dictSize + kDictMask) & ~kDictMask; + if (v < dictSize) + v = dictSize; + } + else + { + unsigned i = 11 * 2; + do + { + v = (UInt32)(2 + (i & 1)) << (i >> 1); + i++; + } + while (v < dictSize); + } - for (i = 0; i < 4; i++) - props[1 + i] = (Byte)(dictSize >> (8 * i)); - return SZ_OK; + SetUi32(props + 1, v); + return SZ_OK; + } } unsigned LzmaEnc_IsWriteEndMark(CLzmaEncHandle pp) { - return ((CLzmaEnc *)pp)->writeEndMark; + return (unsigned)((CLzmaEnc *)pp)->writeEndMark; } @@ -2974,3 +3151,15 @@ LzmaEnc_Destroy(p, alloc, allocBig); return res; } + + +/* +#ifndef _7ZIP_ST +void LzmaEnc_GetLzThreads(CLzmaEncHandle pp, HANDLE lz_threads[2]) +{ + const CLzmaEnc *p = (CLzmaEnc *)pp; + lz_threads[0] = p->matchFinderMt.hashSync.thread; + lz_threads[1] = p->matchFinderMt.btSync.thread; +} +#endif +*/
diff --git a/third_party/lzma_sdk/LzmaEnc.h b/third_party/lzma_sdk/LzmaEnc.h index 9194ee57..bc2ed5042 100644 --- a/third_party/lzma_sdk/LzmaEnc.h +++ b/third_party/lzma_sdk/LzmaEnc.h
@@ -1,5 +1,5 @@ /* LzmaEnc.h -- LZMA Encoder -2017-07-27 : Igor Pavlov : Public domain */ +2019-10-30 : Igor Pavlov : Public domain */ #ifndef __LZMA_ENC_H #define __LZMA_ENC_H @@ -29,6 +29,8 @@ UInt64 reduceSize; /* estimated size of data that will be compressed. default = (UInt64)(Int64)-1. Encoder uses this value to reduce dictionary size */ + + UInt64 affinity; } CLzmaEncProps; void LzmaEncProps_Init(CLzmaEncProps *p);
diff --git a/third_party/lzma_sdk/LzmaLib.h b/third_party/lzma_sdk/LzmaLib.h index 88fa87d..c343a85 100644 --- a/third_party/lzma_sdk/LzmaLib.h +++ b/third_party/lzma_sdk/LzmaLib.h
@@ -1,5 +1,5 @@ /* LzmaLib.h -- LZMA library interface -2013-01-18 : Igor Pavlov : Public domain */ +2021-04-03 : Igor Pavlov : Public domain */ #ifndef __LZMA_LIB_H #define __LZMA_LIB_H @@ -40,14 +40,16 @@ level - compression level: 0 <= level <= 9; level dictSize algo fb - 0: 16 KB 0 32 - 1: 64 KB 0 32 - 2: 256 KB 0 32 - 3: 1 MB 0 32 - 4: 4 MB 0 32 + 0: 64 KB 0 32 + 1: 256 KB 0 32 + 2: 1 MB 0 32 + 3: 4 MB 0 32 + 4: 16 MB 0 32 5: 16 MB 1 32 6: 32 MB 1 32 - 7+: 64 MB 1 64 + 7: 32 MB 1 64 + 8: 64 MB 1 64 + 9: 64 MB 1 64 The default value for "level" is 5. @@ -83,6 +85,11 @@ numThreads - The number of thereads. 1 or 2. The default value is 2. Fast mode (algo = 0) can use only 1 thread. +In: + dest - output data buffer + destLen - output data buffer size + src - input data + srcLen - input data size Out: destLen - processed output size Returns: @@ -108,8 +115,8 @@ LzmaUncompress -------------- In: - dest - output data - destLen - output data size + dest - output data buffer + destLen - output data buffer size src - input data srcLen - input data size Out:
diff --git a/third_party/lzma_sdk/README.chromium b/third_party/lzma_sdk/README.chromium index 1d99c640..a6d547d 100644 --- a/third_party/lzma_sdk/README.chromium +++ b/third_party/lzma_sdk/README.chromium
@@ -1,17 +1,17 @@ Name: LZMA SDK Short Name: lzma URL: http://www.7-zip.org/sdk.html -Version: 19.00 -Date: 2019-02-21 +Version: 21.07 +Date: 2021-12-26 License: Public Domain Security Critical: yes CPEPrefix: unknown Description: -This contains a part of LZMA SDK 19.00. +This contains a part of LZMA SDK 21.07. Local Modifications: -The original code can be found at http://7-zip.org/a/lzma1900.7z. Only parts +The original code can be found at https://7-zip.org/a/lzma2107.7z. Only parts of this archive are copied here. More specifically: 1/ C code required to open 7z archive files and uncompress LZMA @@ -19,6 +19,18 @@ 3/ source code for SfxSetup, a utility for creating self extracting archives 4/ C code required for xz decompression (split into its own static library) +7za.exe and 7zr.exe are both standalone command-line utilities to archive and +extract files. 7zr is "lightweight" and only handles 7zip extensions. 7za can +handle a few more. 7za.exe comes from https://www.7-zip.org/a/7z2107-extra.7z. + +The patch in chromium.patch was applied to 7zCrc.c, CpuArch.c, LZFind.c and +Sha256.c to disable some ARM code that was failing to build in Android and +Fuschia as well as some of the AVX2 and SSE4 code for Windows. In Fuschia, +`#include <asm/hwcap.h>` is not available. In Android builds, `armv8-a+crc` is +not a known target architecture, even when the -march cflag is passed, +specifying the CPU type to use. In Windows, Chromium still supports SSE3, +so it is not be ready to transition to utilizing AVX2 and SSE4, yet. + The patch in Util/SfxSetup/chromium.patch was applied so that: 1/ Fix for includes file names, since the original code causes an include
diff --git a/third_party/lzma_sdk/Sha256.c b/third_party/lzma_sdk/Sha256.c index 04b688c..2199684 100644 --- a/third_party/lzma_sdk/Sha256.c +++ b/third_party/lzma_sdk/Sha256.c
@@ -1,5 +1,5 @@ -/* Crypto/Sha256.c -- SHA-256 Hash -2017-04-03 : Igor Pavlov : Public domain +/* Sha256.c -- SHA-256 Hash +2021-04-01 : Igor Pavlov : Public domain This code is based on public domain code from Wei Dai's Crypto++ library. */ #include "Precomp.h" @@ -10,16 +10,108 @@ #include "RotateDefs.h" #include "Sha256.h" -/* define it for speed optimization */ -#ifndef _SFX -#define _SHA256_UNROLL -#define _SHA256_UNROLL2 +#if defined(_MSC_VER) && (_MSC_VER < 1900) +// #define USE_MY_MM #endif -/* #define _SHA256_UNROLL2 */ +#ifdef MY_CPU_X86_OR_AMD64 + #ifdef _MSC_VER + #if _MSC_VER >= 1200 + #define _SHA_SUPPORTED + #endif + #elif defined(__clang__) + #if (__clang_major__ >= 8) // fix that check + #define _SHA_SUPPORTED + #endif + #elif defined(__GNUC__) + #if (__GNUC__ >= 8) // fix that check + #define _SHA_SUPPORTED + #endif + #elif defined(__INTEL_COMPILER) + #if (__INTEL_COMPILER >= 1800) // fix that check + #define _SHA_SUPPORTED + #endif + #endif +// TODO(crbug.com/1338627): Enable ARM optimizations +#elif 0 // defined(MY_CPU_ARM_OR_ARM64) + #ifdef _MSC_VER + #if _MSC_VER >= 1910 + #define _SHA_SUPPORTED + #endif + #elif defined(__clang__) + #if (__clang_major__ >= 8) // fix that check + #define _SHA_SUPPORTED + #endif + #elif defined(__GNUC__) + #if (__GNUC__ >= 6) // fix that check + #define _SHA_SUPPORTED + #endif + #endif +#endif -void Sha256_Init(CSha256 *p) +void MY_FAST_CALL Sha256_UpdateBlocks(UInt32 state[8], const Byte *data, size_t numBlocks); + +#ifdef _SHA_SUPPORTED + void MY_FAST_CALL Sha256_UpdateBlocks_HW(UInt32 state[8], const Byte *data, size_t numBlocks); + + static SHA256_FUNC_UPDATE_BLOCKS g_FUNC_UPDATE_BLOCKS = Sha256_UpdateBlocks; + static SHA256_FUNC_UPDATE_BLOCKS g_FUNC_UPDATE_BLOCKS_HW; + + #define UPDATE_BLOCKS(p) p->func_UpdateBlocks +#else + #define UPDATE_BLOCKS(p) Sha256_UpdateBlocks +#endif + + +BoolInt Sha256_SetFunction(CSha256 *p, unsigned algo) { + SHA256_FUNC_UPDATE_BLOCKS func = Sha256_UpdateBlocks; + + #ifdef _SHA_SUPPORTED + if (algo != SHA256_ALGO_SW) + { + if (algo == SHA256_ALGO_DEFAULT) + func = g_FUNC_UPDATE_BLOCKS; + else + { + if (algo != SHA256_ALGO_HW) + return False; + func = g_FUNC_UPDATE_BLOCKS_HW; + if (!func) + return False; + } + } + #else + if (algo > 1) + return False; + #endif + + p->func_UpdateBlocks = func; + return True; +} + + +/* define it for speed optimization */ + +#ifdef _SFX + #define STEP_PRE 1 + #define STEP_MAIN 1 +#else + #define STEP_PRE 2 + #define STEP_MAIN 4 + // #define _SHA256_UNROLL +#endif + +#if STEP_MAIN != 16 + #define _SHA256_BIG_W +#endif + + + + +void Sha256_InitState(CSha256 *p) +{ + p->count = 0; p->state[0] = 0x6a09e667; p->state[1] = 0xbb67ae85; p->state[2] = 0x3c6ef372; @@ -28,7 +120,17 @@ p->state[5] = 0x9b05688c; p->state[6] = 0x1f83d9ab; p->state[7] = 0x5be0cd19; - p->count = 0; +} + +void Sha256_Init(CSha256 *p) +{ + p->func_UpdateBlocks = + #ifdef _SHA_SUPPORTED + g_FUNC_UPDATE_BLOCKS; + #else + NULL; + #endif + Sha256_InitState(p); } #define S0(x) (rotrFixed(x, 2) ^ rotrFixed(x,13) ^ rotrFixed(x, 22)) @@ -36,61 +138,100 @@ #define s0(x) (rotrFixed(x, 7) ^ rotrFixed(x,18) ^ (x >> 3)) #define s1(x) (rotrFixed(x,17) ^ rotrFixed(x,19) ^ (x >> 10)) -#define blk0(i) (W[i]) -#define blk2(i) (W[i] += s1(W[((i)-2)&15]) + W[((i)-7)&15] + s0(W[((i)-15)&15])) - #define Ch(x,y,z) (z^(x&(y^z))) #define Maj(x,y,z) ((x&y)|(z&(x|y))) -#ifdef _SHA256_UNROLL2 -#define R(a,b,c,d,e,f,g,h, i) \ - h += S1(e) + Ch(e,f,g) + K[(i)+(size_t)(j)] + (j ? blk2(i) : blk0(i)); \ +#define W_PRE(i) (W[(i) + (size_t)(j)] = GetBe32(data + ((size_t)(j) + i) * 4)) + +#define blk2_main(j, i) s1(w(j, (i)-2)) + w(j, (i)-7) + s0(w(j, (i)-15)) + +#ifdef _SHA256_BIG_W + // we use +i instead of +(i) to change the order to solve CLANG compiler warning for signed/unsigned. + #define w(j, i) W[(size_t)(j) + i] + #define blk2(j, i) (w(j, i) = w(j, (i)-16) + blk2_main(j, i)) +#else + #if STEP_MAIN == 16 + #define w(j, i) W[(i) & 15] + #else + #define w(j, i) W[((size_t)(j) + (i)) & 15] + #endif + #define blk2(j, i) (w(j, i) += blk2_main(j, i)) +#endif + +#define W_MAIN(i) blk2(j, i) + + +#define T1(wx, i) \ + tmp = h + S1(e) + Ch(e,f,g) + K[(i)+(size_t)(j)] + wx(i); \ + h = g; \ + g = f; \ + f = e; \ + e = d + tmp; \ + tmp += S0(a) + Maj(a, b, c); \ + d = c; \ + c = b; \ + b = a; \ + a = tmp; \ + +#define R1_PRE(i) T1( W_PRE, i) +#define R1_MAIN(i) T1( W_MAIN, i) + +#if (!defined(_SHA256_UNROLL) || STEP_MAIN < 8) && (STEP_MAIN >= 4) +#define R2_MAIN(i) \ + R1_MAIN(i) \ + R1_MAIN(i + 1) \ + +#endif + + + +#if defined(_SHA256_UNROLL) && STEP_MAIN >= 8 + +#define T4( a,b,c,d,e,f,g,h, wx, i) \ + h += S1(e) + Ch(e,f,g) + K[(i)+(size_t)(j)] + wx(i); \ + tmp = h; \ + h += d; \ + d = tmp + S0(a) + Maj(a, b, c); \ + +#define R4( wx, i) \ + T4 ( a,b,c,d,e,f,g,h, wx, (i )); \ + T4 ( d,a,b,c,h,e,f,g, wx, (i+1)); \ + T4 ( c,d,a,b,g,h,e,f, wx, (i+2)); \ + T4 ( b,c,d,a,f,g,h,e, wx, (i+3)); \ + +#define R4_PRE(i) R4( W_PRE, i) +#define R4_MAIN(i) R4( W_MAIN, i) + + +#define T8( a,b,c,d,e,f,g,h, wx, i) \ + h += S1(e) + Ch(e,f,g) + K[(i)+(size_t)(j)] + wx(i); \ d += h; \ - h += S0(a) + Maj(a, b, c) + h += S0(a) + Maj(a, b, c); \ -#define RX_8(i) \ - R(a,b,c,d,e,f,g,h, i); \ - R(h,a,b,c,d,e,f,g, i+1); \ - R(g,h,a,b,c,d,e,f, i+2); \ - R(f,g,h,a,b,c,d,e, i+3); \ - R(e,f,g,h,a,b,c,d, i+4); \ - R(d,e,f,g,h,a,b,c, i+5); \ - R(c,d,e,f,g,h,a,b, i+6); \ - R(b,c,d,e,f,g,h,a, i+7) +#define R8( wx, i) \ + T8 ( a,b,c,d,e,f,g,h, wx, i ); \ + T8 ( h,a,b,c,d,e,f,g, wx, i+1); \ + T8 ( g,h,a,b,c,d,e,f, wx, i+2); \ + T8 ( f,g,h,a,b,c,d,e, wx, i+3); \ + T8 ( e,f,g,h,a,b,c,d, wx, i+4); \ + T8 ( d,e,f,g,h,a,b,c, wx, i+5); \ + T8 ( c,d,e,f,g,h,a,b, wx, i+6); \ + T8 ( b,c,d,e,f,g,h,a, wx, i+7); \ -#define RX_16 RX_8(0); RX_8(8); - -#else - -#define a(i) T[(0-(i))&7] -#define b(i) T[(1-(i))&7] -#define c(i) T[(2-(i))&7] -#define d(i) T[(3-(i))&7] -#define e(i) T[(4-(i))&7] -#define f(i) T[(5-(i))&7] -#define g(i) T[(6-(i))&7] -#define h(i) T[(7-(i))&7] - -#define R(i) \ - h(i) += S1(e(i)) + Ch(e(i),f(i),g(i)) + K[(i)+(size_t)(j)] + (j ? blk2(i) : blk0(i)); \ - d(i) += h(i); \ - h(i) += S0(a(i)) + Maj(a(i), b(i), c(i)) \ - -#ifdef _SHA256_UNROLL - -#define RX_8(i) R(i+0); R(i+1); R(i+2); R(i+3); R(i+4); R(i+5); R(i+6); R(i+7); -#define RX_16 RX_8(0); RX_8(8); - -#else - -#define RX_16 unsigned i; for (i = 0; i < 16; i++) { R(i); } +#define R8_PRE(i) R8( W_PRE, i) +#define R8_MAIN(i) R8( W_MAIN, i) #endif -#endif +void MY_FAST_CALL Sha256_UpdateBlocks_HW(UInt32 state[8], const Byte *data, size_t numBlocks); -static const UInt32 K[64] = { +// static +extern MY_ALIGN(64) +const UInt32 SHA256_K_ARRAY[64]; + +MY_ALIGN(64) +const UInt32 SHA256_K_ARRAY[64] = { 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, @@ -109,30 +250,27 @@ 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 }; -static void Sha256_WriteByteBlock(CSha256 *p) -{ - UInt32 W[16]; - unsigned j; - UInt32 *state; +#define K SHA256_K_ARRAY - #ifdef _SHA256_UNROLL2 - UInt32 a,b,c,d,e,f,g,h; + +MY_NO_INLINE +void MY_FAST_CALL Sha256_UpdateBlocks(UInt32 state[8], const Byte *data, size_t numBlocks) +{ + UInt32 W + #ifdef _SHA256_BIG_W + [64]; #else - UInt32 T[8]; + [16]; #endif - for (j = 0; j < 16; j += 4) - { - const Byte *ccc = p->buffer + j * 4; - W[j ] = GetBe32(ccc); - W[j + 1] = GetBe32(ccc + 4); - W[j + 2] = GetBe32(ccc + 8); - W[j + 3] = GetBe32(ccc + 12); - } + unsigned j; - state = p->state; + UInt32 a,b,c,d,e,f,g,h; - #ifdef _SHA256_UNROLL2 + #if !defined(_SHA256_UNROLL) || (STEP_MAIN <= 4) || (STEP_PRE <= 4) + UInt32 tmp; + #endif + a = state[0]; b = state[1]; c = state[2]; @@ -141,39 +279,96 @@ f = state[5]; g = state[6]; h = state[7]; - #else - for (j = 0; j < 8; j++) - T[j] = state[j]; - #endif - for (j = 0; j < 64; j += 16) + while (numBlocks) { - RX_16 + + for (j = 0; j < 16; j += STEP_PRE) + { + #if STEP_PRE > 4 + + #if STEP_PRE < 8 + R4_PRE(0); + #else + R8_PRE(0); + #if STEP_PRE == 16 + R8_PRE(8); + #endif + #endif + + #else + + R1_PRE(0); + #if STEP_PRE >= 2 + R1_PRE(1); + #if STEP_PRE >= 4 + R1_PRE(2); + R1_PRE(3); + #endif + #endif + + #endif } - #ifdef _SHA256_UNROLL2 - state[0] += a; - state[1] += b; - state[2] += c; - state[3] += d; - state[4] += e; - state[5] += f; - state[6] += g; - state[7] += h; - #else - for (j = 0; j < 8; j++) - state[j] += T[j]; - #endif - + for (j = 16; j < 64; j += STEP_MAIN) + { + #if defined(_SHA256_UNROLL) && STEP_MAIN >= 8 + + #if STEP_MAIN < 8 + R4_MAIN(0); + #else + R8_MAIN(0); + #if STEP_MAIN == 16 + R8_MAIN(8); + #endif + #endif + + #else + + R1_MAIN(0); + #if STEP_MAIN >= 2 + R1_MAIN(1); + #if STEP_MAIN >= 4 + R2_MAIN(2); + #if STEP_MAIN >= 8 + R2_MAIN(4); + R2_MAIN(6); + #if STEP_MAIN >= 16 + R2_MAIN(8); + R2_MAIN(10); + R2_MAIN(12); + R2_MAIN(14); + #endif + #endif + #endif + #endif + #endif + } + + a += state[0]; state[0] = a; + b += state[1]; state[1] = b; + c += state[2]; state[2] = c; + d += state[3]; state[3] = d; + e += state[4]; state[4] = e; + f += state[5]; state[5] = f; + g += state[6]; state[6] = g; + h += state[7]; state[7] = h; + + data += 64; + numBlocks--; + } + /* Wipe variables */ /* memset(W, 0, sizeof(W)); */ - /* memset(T, 0, sizeof(T)); */ } #undef S0 #undef S1 #undef s0 #undef s1 +#undef K + +#define Sha256_UpdateBlock(p) UPDATE_BLOCKS(p)(p->state, p->buffer, 1) void Sha256_Update(CSha256 *p, const Byte *data, size_t size) { @@ -193,25 +388,26 @@ return; } - size -= num; - memcpy(p->buffer + pos, data, num); - data += num; + if (pos != 0) + { + size -= num; + memcpy(p->buffer + pos, data, num); + data += num; + Sha256_UpdateBlock(p); + } } - - for (;;) { - Sha256_WriteByteBlock(p); - if (size < 64) - break; - size -= 64; - memcpy(p->buffer, data, 64); - data += 64; - } - - if (size != 0) + size_t numBlocks = size >> 6; + UPDATE_BLOCKS(p)(p->state, data, numBlocks); + size &= 0x3F; + if (size == 0) + return; + data += (numBlocks << 6); memcpy(p->buffer, data, size); + } } + void Sha256_Final(CSha256 *p, Byte *digest) { unsigned pos = (unsigned)p->count & 0x3F; @@ -219,30 +415,73 @@ p->buffer[pos++] = 0x80; - while (pos != (64 - 8)) + if (pos > (64 - 8)) { - pos &= 0x3F; - if (pos == 0) - Sha256_WriteByteBlock(p); - p->buffer[pos++] = 0; + while (pos != 64) { p->buffer[pos++] = 0; } + // memset(&p->buf.buffer[pos], 0, 64 - pos); + Sha256_UpdateBlock(p); + pos = 0; } + /* + if (pos & 3) + { + p->buffer[pos] = 0; + p->buffer[pos + 1] = 0; + p->buffer[pos + 2] = 0; + pos += 3; + pos &= ~3; + } + { + for (; pos < 64 - 8; pos += 4) + *(UInt32 *)(&p->buffer[pos]) = 0; + } + */ + + memset(&p->buffer[pos], 0, (64 - 8) - pos); + { UInt64 numBits = (p->count << 3); SetBe32(p->buffer + 64 - 8, (UInt32)(numBits >> 32)); SetBe32(p->buffer + 64 - 4, (UInt32)(numBits)); } - Sha256_WriteByteBlock(p); + Sha256_UpdateBlock(p); for (i = 0; i < 8; i += 2) { UInt32 v0 = p->state[i]; - UInt32 v1 = p->state[i + 1]; + UInt32 v1 = p->state[(size_t)i + 1]; SetBe32(digest , v0); SetBe32(digest + 4, v1); digest += 8; } - Sha256_Init(p); + Sha256_InitState(p); +} + + +void Sha256Prepare() +{ + #ifdef _SHA_SUPPORTED + SHA256_FUNC_UPDATE_BLOCKS f, f_hw; + f = Sha256_UpdateBlocks; + f_hw = NULL; + #ifdef MY_CPU_X86_OR_AMD64 + #ifndef USE_MY_MM + if (CPU_IsSupported_SHA() + && CPU_IsSupported_SSSE3() + // && CPU_IsSupported_SSE41() + ) + #endif + #else + if (CPU_IsSupported_SHA2()) + #endif + { + // printf("\n========== HW SHA256 ======== \n"); + f = f_hw = Sha256_UpdateBlocks_HW; + } + g_FUNC_UPDATE_BLOCKS = f; + g_FUNC_UPDATE_BLOCKS_HW = f_hw; + #endif }
diff --git a/third_party/lzma_sdk/Sha256.h b/third_party/lzma_sdk/Sha256.h index 3f455db..aa38501 100644 --- a/third_party/lzma_sdk/Sha256.h +++ b/third_party/lzma_sdk/Sha256.h
@@ -1,26 +1,76 @@ /* Sha256.h -- SHA-256 Hash -2013-01-18 : Igor Pavlov : Public domain */ +2021-01-01 : Igor Pavlov : Public domain */ -#ifndef __CRYPTO_SHA256_H -#define __CRYPTO_SHA256_H +#ifndef __7Z_SHA256_H +#define __7Z_SHA256_H #include "7zTypes.h" EXTERN_C_BEGIN -#define SHA256_DIGEST_SIZE 32 +#define SHA256_NUM_BLOCK_WORDS 16 +#define SHA256_NUM_DIGEST_WORDS 8 + +#define SHA256_BLOCK_SIZE (SHA256_NUM_BLOCK_WORDS * 4) +#define SHA256_DIGEST_SIZE (SHA256_NUM_DIGEST_WORDS * 4) + +typedef void (MY_FAST_CALL *SHA256_FUNC_UPDATE_BLOCKS)(UInt32 state[8], const Byte *data, size_t numBlocks); + +/* + if (the system supports different SHA256 code implementations) + { + (CSha256::func_UpdateBlocks) will be used + (CSha256::func_UpdateBlocks) can be set by + Sha256_Init() - to default (fastest) + Sha256_SetFunction() - to any algo + } + else + { + (CSha256::func_UpdateBlocks) is ignored. + } +*/ typedef struct { - UInt32 state[8]; + SHA256_FUNC_UPDATE_BLOCKS func_UpdateBlocks; UInt64 count; - Byte buffer[64]; + UInt64 __pad_2[2]; + UInt32 state[SHA256_NUM_DIGEST_WORDS]; + + Byte buffer[SHA256_BLOCK_SIZE]; } CSha256; + +#define SHA256_ALGO_DEFAULT 0 +#define SHA256_ALGO_SW 1 +#define SHA256_ALGO_HW 2 + +/* +Sha256_SetFunction() +return: + 0 - (algo) value is not supported, and func_UpdateBlocks was not changed + 1 - func_UpdateBlocks was set according (algo) value. +*/ + +BoolInt Sha256_SetFunction(CSha256 *p, unsigned algo); + +void Sha256_InitState(CSha256 *p); void Sha256_Init(CSha256 *p); void Sha256_Update(CSha256 *p, const Byte *data, size_t size); void Sha256_Final(CSha256 *p, Byte *digest); + + + +// void MY_FAST_CALL Sha256_UpdateBlocks(UInt32 state[8], const Byte *data, size_t numBlocks); + +/* +call Sha256Prepare() once at program start. +It prepares all supported implementations, and detects the fastest implementation. +*/ + +void Sha256Prepare(void); + EXTERN_C_END #endif
diff --git a/third_party/lzma_sdk/Sha256Opt.c b/third_party/lzma_sdk/Sha256Opt.c new file mode 100644 index 0000000..decc1382 --- /dev/null +++ b/third_party/lzma_sdk/Sha256Opt.c
@@ -0,0 +1,373 @@ +/* Sha256Opt.c -- SHA-256 optimized code for SHA-256 hardware instructions +2021-04-01 : Igor Pavlov : Public domain */ + +#include "Precomp.h" + +#if defined(_MSC_VER) +#if (_MSC_VER < 1900) && (_MSC_VER >= 1200) +// #define USE_MY_MM +#endif +#endif + +#include "CpuArch.h" + +#ifdef MY_CPU_X86_OR_AMD64 + #if defined(__clang__) + #if (__clang_major__ >= 8) // fix that check + #define USE_HW_SHA + #ifndef __SHA__ + #define ATTRIB_SHA __attribute__((__target__("sha,ssse3"))) + #if defined(_MSC_VER) + // SSSE3: for clang-cl: + #include <tmmintrin.h> + #define __SHA__ + #endif + #endif + + #endif + #elif defined(__GNUC__) + #if (__GNUC__ >= 8) // fix that check + #define USE_HW_SHA + #ifndef __SHA__ + #define ATTRIB_SHA __attribute__((__target__("sha,ssse3"))) + // #pragma GCC target("sha,ssse3") + #endif + #endif + #elif defined(__INTEL_COMPILER) + #if (__INTEL_COMPILER >= 1800) // fix that check + #define USE_HW_SHA + #endif + #elif defined(_MSC_VER) + #ifdef USE_MY_MM + #define USE_VER_MIN 1300 + #else + #define USE_VER_MIN 1910 + #endif + #if _MSC_VER >= USE_VER_MIN + #define USE_HW_SHA + #endif + #endif +// #endif // MY_CPU_X86_OR_AMD64 + +#ifdef USE_HW_SHA + +// #pragma message("Sha256 HW") +// #include <wmmintrin.h> + +#if !defined(_MSC_VER) || (_MSC_VER >= 1900) +#include <immintrin.h> +#else +#include <emmintrin.h> + +#if defined(_MSC_VER) && (_MSC_VER >= 1600) +// #include <intrin.h> +#endif + +#ifdef USE_MY_MM +#include "My_mm.h" +#endif + +#endif + +/* +SHA256 uses: +SSE2: + _mm_loadu_si128 + _mm_storeu_si128 + _mm_set_epi32 + _mm_add_epi32 + _mm_shuffle_epi32 / pshufd + + + +SSSE3: + _mm_shuffle_epi8 / pshufb + _mm_alignr_epi8 +SHA: + _mm_sha256* +*/ + +// K array must be aligned for 16-bytes at least. +// The compiler can look align attribute and selects +// movdqu - for code without align attribute +// movdqa - for code with align attribute +extern +MY_ALIGN(64) +const UInt32 SHA256_K_ARRAY[64]; + +#define K SHA256_K_ARRAY + + +#define ADD_EPI32(dest, src) dest = _mm_add_epi32(dest, src); +#define SHA256_MSG1(dest, src) dest = _mm_sha256msg1_epu32(dest, src); +#define SHA25G_MSG2(dest, src) dest = _mm_sha256msg2_epu32(dest, src); + + +#define LOAD_SHUFFLE(m, k) \ + m = _mm_loadu_si128((const __m128i *)(const void *)(data + (k) * 16)); \ + m = _mm_shuffle_epi8(m, mask); \ + +#define SM1(g0, g1, g2, g3) \ + SHA256_MSG1(g3, g0); \ + +#define SM2(g0, g1, g2, g3) \ + tmp = _mm_alignr_epi8(g1, g0, 4); \ + ADD_EPI32(g2, tmp); \ + SHA25G_MSG2(g2, g1); \ + +// #define LS0(k, g0, g1, g2, g3) LOAD_SHUFFLE(g0, k) +// #define LS1(k, g0, g1, g2, g3) LOAD_SHUFFLE(g1, k+1) + + +#define NNN(g0, g1, g2, g3) + + +#define RND2(t0, t1) \ + t0 = _mm_sha256rnds2_epu32(t0, t1, msg); + +#define RND2_0(m, k) \ + msg = _mm_add_epi32(m, *(const __m128i *) (const void *) &K[(k) * 4]); \ + RND2(state0, state1); \ + msg = _mm_shuffle_epi32(msg, 0x0E); \ + + +#define RND2_1 \ + RND2(state1, state0); \ + + +// We use scheme with 3 rounds ahead for SHA256_MSG1 / 2 rounds ahead for SHA256_MSG2 + +#define R4(k, g0, g1, g2, g3, OP0, OP1) \ + RND2_0(g0, k); \ + OP0(g0, g1, g2, g3); \ + RND2_1; \ + OP1(g0, g1, g2, g3); \ + +#define R16(k, OP0, OP1, OP2, OP3, OP4, OP5, OP6, OP7) \ + R4 ( (k)*4+0, m0, m1, m2, m3, OP0, OP1 ) \ + R4 ( (k)*4+1, m1, m2, m3, m0, OP2, OP3 ) \ + R4 ( (k)*4+2, m2, m3, m0, m1, OP4, OP5 ) \ + R4 ( (k)*4+3, m3, m0, m1, m2, OP6, OP7 ) \ + +#define PREPARE_STATE \ + tmp = _mm_shuffle_epi32(state0, 0x1B); /* abcd */ \ + state0 = _mm_shuffle_epi32(state1, 0x1B); /* efgh */ \ + state1 = state0; \ + state0 = _mm_unpacklo_epi64(state0, tmp); /* cdgh */ \ + state1 = _mm_unpackhi_epi64(state1, tmp); /* abef */ \ + + +void MY_FAST_CALL Sha256_UpdateBlocks_HW(UInt32 state[8], const Byte *data, size_t numBlocks); +#ifdef ATTRIB_SHA +ATTRIB_SHA +#endif +void MY_FAST_CALL Sha256_UpdateBlocks_HW(UInt32 state[8], const Byte *data, size_t numBlocks) +{ + const __m128i mask = _mm_set_epi32(0x0c0d0e0f, 0x08090a0b, 0x04050607, 0x00010203); + __m128i tmp; + __m128i state0, state1; + + if (numBlocks == 0) + return; + + state0 = _mm_loadu_si128((const __m128i *) (const void *) &state[0]); + state1 = _mm_loadu_si128((const __m128i *) (const void *) &state[4]); + + PREPARE_STATE + + do + { + __m128i state0_save, state1_save; + __m128i m0, m1, m2, m3; + __m128i msg; + // #define msg tmp + + state0_save = state0; + state1_save = state1; + + LOAD_SHUFFLE (m0, 0) + LOAD_SHUFFLE (m1, 1) + LOAD_SHUFFLE (m2, 2) + LOAD_SHUFFLE (m3, 3) + + + + R16 ( 0, NNN, NNN, SM1, NNN, SM1, SM2, SM1, SM2 ); + R16 ( 1, SM1, SM2, SM1, SM2, SM1, SM2, SM1, SM2 ); + R16 ( 2, SM1, SM2, SM1, SM2, SM1, SM2, SM1, SM2 ); + R16 ( 3, SM1, SM2, NNN, SM2, NNN, NNN, NNN, NNN ); + + ADD_EPI32(state0, state0_save); + ADD_EPI32(state1, state1_save); + + data += 64; + } + while (--numBlocks); + + PREPARE_STATE + + _mm_storeu_si128((__m128i *) (void *) &state[0], state0); + _mm_storeu_si128((__m128i *) (void *) &state[4], state1); +} + +#endif // USE_HW_SHA + +#elif defined(MY_CPU_ARM_OR_ARM64) + + #if defined(__clang__) + #if (__clang_major__ >= 8) // fix that check + #define USE_HW_SHA + #endif + #elif defined(__GNUC__) + #if (__GNUC__ >= 6) // fix that check + #define USE_HW_SHA + #endif + #elif defined(_MSC_VER) + #if _MSC_VER >= 1910 + #define USE_HW_SHA + #endif + #endif + +#ifdef USE_HW_SHA + +// #pragma message("=== Sha256 HW === ") + +#if defined(__clang__) || defined(__GNUC__) + #ifdef MY_CPU_ARM64 + #define ATTRIB_SHA __attribute__((__target__("+crypto"))) + #else + #define ATTRIB_SHA __attribute__((__target__("fpu=crypto-neon-fp-armv8"))) + #endif +#else + // _MSC_VER + // for arm32 + #define _ARM_USE_NEW_NEON_INTRINSICS +#endif + +#if defined(_MSC_VER) && defined(MY_CPU_ARM64) +#include <arm64_neon.h> +#else +#include <arm_neon.h> +#endif + +typedef uint32x4_t v128; +// typedef __n128 v128; // MSVC + +#ifdef MY_CPU_BE + #define MY_rev32_for_LE(x) +#else + #define MY_rev32_for_LE(x) x = vreinterpretq_u32_u8(vrev32q_u8(vreinterpretq_u8_u32(x))) +#endif + +#define LOAD_128(_p) (*(const v128 *)(const void *)(_p)) +#define STORE_128(_p, _v) *(v128 *)(void *)(_p) = (_v) + +#define LOAD_SHUFFLE(m, k) \ + m = LOAD_128((data + (k) * 16)); \ + MY_rev32_for_LE(m); \ + +// K array must be aligned for 16-bytes at least. +extern +MY_ALIGN(64) +const UInt32 SHA256_K_ARRAY[64]; + +#define K SHA256_K_ARRAY + + +#define SHA256_SU0(dest, src) dest = vsha256su0q_u32(dest, src); +#define SHA25G_SU1(dest, src2, src3) dest = vsha256su1q_u32(dest, src2, src3); + +#define SM1(g0, g1, g2, g3) SHA256_SU0(g3, g0) +#define SM2(g0, g1, g2, g3) SHA25G_SU1(g2, g0, g1) +#define NNN(g0, g1, g2, g3) + + +#define R4(k, g0, g1, g2, g3, OP0, OP1) \ + msg = vaddq_u32(g0, *(const v128 *) (const void *) &K[(k) * 4]); \ + tmp = state0; \ + state0 = vsha256hq_u32( state0, state1, msg ); \ + state1 = vsha256h2q_u32( state1, tmp, msg ); \ + OP0(g0, g1, g2, g3); \ + OP1(g0, g1, g2, g3); \ + + +#define R16(k, OP0, OP1, OP2, OP3, OP4, OP5, OP6, OP7) \ + R4 ( (k)*4+0, m0, m1, m2, m3, OP0, OP1 ) \ + R4 ( (k)*4+1, m1, m2, m3, m0, OP2, OP3 ) \ + R4 ( (k)*4+2, m2, m3, m0, m1, OP4, OP5 ) \ + R4 ( (k)*4+3, m3, m0, m1, m2, OP6, OP7 ) \ + + +void MY_FAST_CALL Sha256_UpdateBlocks_HW(UInt32 state[8], const Byte *data, size_t numBlocks); +#ifdef ATTRIB_SHA +ATTRIB_SHA +#endif +void MY_FAST_CALL Sha256_UpdateBlocks_HW(UInt32 state[8], const Byte *data, size_t numBlocks) +{ + v128 state0, state1; + + if (numBlocks == 0) + return; + + state0 = LOAD_128(&state[0]); + state1 = LOAD_128(&state[4]); + + do + { + v128 state0_save, state1_save; + v128 m0, m1, m2, m3; + v128 msg, tmp; + + state0_save = state0; + state1_save = state1; + + LOAD_SHUFFLE (m0, 0) + LOAD_SHUFFLE (m1, 1) + LOAD_SHUFFLE (m2, 2) + LOAD_SHUFFLE (m3, 3) + + R16 ( 0, NNN, NNN, SM1, NNN, SM1, SM2, SM1, SM2 ); + R16 ( 1, SM1, SM2, SM1, SM2, SM1, SM2, SM1, SM2 ); + R16 ( 2, SM1, SM2, SM1, SM2, SM1, SM2, SM1, SM2 ); + R16 ( 3, SM1, SM2, NNN, SM2, NNN, NNN, NNN, NNN ); + + state0 = vaddq_u32(state0, state0_save); + state1 = vaddq_u32(state1, state1_save); + + data += 64; + } + while (--numBlocks); + + STORE_128(&state[0], state0); + STORE_128(&state[4], state1); +} + +#endif // USE_HW_SHA + +#endif // MY_CPU_ARM_OR_ARM64 + + +#ifndef USE_HW_SHA + +// #error Stop_Compiling_UNSUPPORTED_SHA +// #include <stdlib.h> + +// #include "Sha256.h" +void MY_FAST_CALL Sha256_UpdateBlocks(UInt32 state[8], const Byte *data, size_t numBlocks); + +#pragma message("Sha256 HW-SW stub was used") + +void MY_FAST_CALL Sha256_UpdateBlocks_HW(UInt32 state[8], const Byte *data, size_t numBlocks); +void MY_FAST_CALL Sha256_UpdateBlocks_HW(UInt32 state[8], const Byte *data, size_t numBlocks) +{ + Sha256_UpdateBlocks(state, data, numBlocks); + /* + UNUSED_VAR(state); + UNUSED_VAR(data); + UNUSED_VAR(numBlocks); + exit(1); + return; + */ +} + +#endif
diff --git a/third_party/lzma_sdk/Xz.c b/third_party/lzma_sdk/Xz.c index d9f83df..7c53b60 100644 --- a/third_party/lzma_sdk/Xz.c +++ b/third_party/lzma_sdk/Xz.c
@@ -1,5 +1,5 @@ /* Xz.c - Xz -2017-05-12 : Igor Pavlov : Public domain */ +2021-02-09 : Igor Pavlov : Public domain */ #include "Precomp.h" @@ -41,7 +41,7 @@ unsigned XzFlags_GetCheckSize(CXzStreamFlags f) { unsigned t = XzFlags_GetCheckType(f); - return (t == 0) ? 0 : (4 << ((t - 1) / 3)); + return (t == 0) ? 0 : ((unsigned)4 << ((t - 1) / 3)); } void XzCheck_Init(CXzCheck *p, unsigned mode)
diff --git a/third_party/lzma_sdk/Xz.h b/third_party/lzma_sdk/Xz.h index 544ee18..849b944b 100644 --- a/third_party/lzma_sdk/Xz.h +++ b/third_party/lzma_sdk/Xz.h
@@ -1,5 +1,5 @@ /* Xz.h - Xz interface -2018-07-04 : Igor Pavlov : Public domain */ +2021-04-01 : Igor Pavlov : Public domain */ #ifndef __XZ_H #define __XZ_H @@ -47,7 +47,7 @@ CXzFilter filters[XZ_NUM_FILTERS_MAX]; } CXzBlock; -#define XzBlock_GetNumFilters(p) (((p)->flags & XZ_BF_NUM_FILTERS_MASK) + 1) +#define XzBlock_GetNumFilters(p) (((unsigned)(p)->flags & XZ_BF_NUM_FILTERS_MASK) + 1) #define XzBlock_HasPackSize(p) (((p)->flags & XZ_BF_PACK_SIZE) != 0) #define XzBlock_HasUnpackSize(p) (((p)->flags & XZ_BF_UNPACK_SIZE) != 0) #define XzBlock_HasUnsupportedFlags(p) (((p)->flags & ~(XZ_BF_NUM_FILTERS_MASK | XZ_BF_PACK_SIZE | XZ_BF_UNPACK_SIZE)) != 0) @@ -277,7 +277,10 @@ { XzUnpacker_Init() for() + { XzUnpacker_Code(); + } + XzUnpacker_IsStreamWasFinished() } Interface-2 : Direct output buffer: @@ -288,7 +291,10 @@ XzUnpacker_Init() XzUnpacker_SetOutBufMode(); // to set output buffer and size for() + { XzUnpacker_Code(); // (dest = NULL) in XzUnpacker_Code() + } + XzUnpacker_IsStreamWasFinished() } Interface-3 : Direct output buffer : One call full decoding @@ -296,6 +302,7 @@ It uses Interface-2 internally. { XzUnpacker_CodeFull() + XzUnpacker_IsStreamWasFinished() } */ @@ -309,8 +316,12 @@ SZ_OK status: CODER_STATUS_NOT_FINISHED, - CODER_STATUS_NEEDS_MORE_INPUT - maybe there are more xz streams, - call XzUnpacker_IsStreamWasFinished to check that current stream was finished + CODER_STATUS_NEEDS_MORE_INPUT - the decoder can return it in two cases: + 1) it needs more input data to finish current xz stream + 2) xz stream was finished successfully. But the decoder supports multiple + concatented xz streams. So it expects more input data for new xz streams. + Call XzUnpacker_IsStreamWasFinished() to check that latest xz stream was finished successfully. + SZ_ERROR_MEM - Memory allocation error SZ_ERROR_DATA - Data error SZ_ERROR_UNSUPPORTED - Unsupported method or method properties @@ -335,12 +346,17 @@ const Byte *src, SizeT *srcLen, ECoderFinishMode finishMode, ECoderStatus *status); +/* +If you decode full xz stream(s), then you can call XzUnpacker_IsStreamWasFinished() +after successful XzUnpacker_CodeFull() or after last call of XzUnpacker_Code(). +*/ + BoolInt XzUnpacker_IsStreamWasFinished(const CXzUnpacker *p); /* -XzUnpacker_GetExtraSize() returns then number of uncofirmed bytes, +XzUnpacker_GetExtraSize() returns then number of unconfirmed bytes, if it's in (XZ_STATE_STREAM_HEADER) state or in (XZ_STATE_STREAM_PADDING) state. -These bytes can be some bytes after xz archive, or +These bytes can be some data after xz archive, or it can be start of new xz stream. Call XzUnpacker_GetExtraSize() after XzUnpacker_Code() function to detect real size of @@ -371,19 +387,46 @@ -/* ---------- Multi Threading Decoding ---------- */ + + + +/* ---- Single-Thread and Multi-Thread xz Decoding with Input/Output Streams ---- */ + +/* + if (CXzDecMtProps::numThreads > 1), the decoder can try to use + Multi-Threading. The decoder analyses xz block header, and if + there are pack size and unpack size values stored in xz block header, + the decoder reads compressed data of block to internal buffers, + and then it can start parallel decoding, if there are another blocks. + The decoder can switch back to Single-Thread decoding after some conditions. + + The sequence of calls for xz decoding with in/out Streams: + { + XzDecMt_Create() + XzDecMtProps_Init(XzDecMtProps) to set default values of properties + // then you can change some XzDecMtProps parameters with required values + // here you can set the number of threads and (memUseMax) - the maximum + Memory usage for multithreading decoding. + for() + { + XzDecMt_Decode() // one call per one file + } + XzDecMt_Destroy() + } +*/ typedef struct { - size_t inBufSize_ST; - size_t outStep_ST; - BoolInt ignoreErrors; + size_t inBufSize_ST; // size of input buffer for Single-Thread decoding + size_t outStep_ST; // size of output buffer for Single-Thread decoding + BoolInt ignoreErrors; // if set to 1, the decoder can ignore some errors and it skips broken parts of data. #ifndef _7ZIP_ST - unsigned numThreads; - size_t inBufSize_MT; - size_t memUseMax; + unsigned numThreads; // the number of threads for Multi-Thread decoding. if (umThreads == 1) it will use Single-thread decoding + size_t inBufSize_MT; // size of small input data buffers for Multi-Thread decoding. Big number of such small buffers can be created + size_t memUseMax; // the limit of total memory usage for Multi-Thread decoding. + // it's recommended to set (memUseMax) manually to value that is smaller of total size of RAM in computer. #endif } CXzDecMtProps; @@ -393,7 +436,7 @@ typedef void * CXzDecMtHandle; /* - alloc : XzDecMt uses CAlignOffsetAlloc for addresses allocated by (alloc). + alloc : XzDecMt uses CAlignOffsetAlloc internally for addresses allocated by (alloc). allocMid : for big allocations, aligned allocation is better */ @@ -407,33 +450,46 @@ Byte NumStreams_Defined; Byte NumBlocks_Defined; - Byte DataAfterEnd; + Byte DataAfterEnd; // there are some additional data after good xz streams, and that data is not new xz stream. Byte DecodingTruncated; // Decoding was Truncated, we need only partial output data - UInt64 InSize; // pack size processed + UInt64 InSize; // pack size processed. That value doesn't include the data after + // end of xz stream, if that data was not correct UInt64 OutSize; UInt64 NumStreams; UInt64 NumBlocks; - SRes DecodeRes; - SRes ReadRes; - SRes ProgressRes; - SRes CombinedRes; - SRes CombinedRes_Type; + SRes DecodeRes; // the error code of xz streams data decoding + SRes ReadRes; // error code from ISeqInStream:Read() + SRes ProgressRes; // error code from ICompressProgress:Progress() + SRes CombinedRes; // Combined result error code that shows main rusult + // = S_OK, if there is no error. + // but check also (DataAfterEnd) that can show additional minor errors. + + SRes CombinedRes_Type; // = SZ_ERROR_READ, if error from ISeqInStream + // = SZ_ERROR_PROGRESS, if error from ICompressProgress + // = SZ_ERROR_WRITE, if error from ISeqOutStream + // = SZ_ERROR_* codes for decoding } CXzStatInfo; void XzStatInfo_Clear(CXzStatInfo *p); /* + XzDecMt_Decode() -SRes: - SZ_OK - OK +SRes: it's combined decoding result. It also is equal to stat->CombinedRes. + + SZ_OK - no error + check also output value in (stat->DataAfterEnd) + that can show additional possible error + SZ_ERROR_MEM - Memory allocation error SZ_ERROR_NO_ARCHIVE - is not xz archive SZ_ERROR_ARCHIVE - Headers error SZ_ERROR_DATA - Data Error + SZ_ERROR_UNSUPPORTED - Unsupported method or method properties SZ_ERROR_CRC - CRC Error SZ_ERROR_INPUT_EOF - it needs more input data SZ_ERROR_WRITE - ISeqOutStream error @@ -451,8 +507,9 @@ // Byte *outBuf, size_t *outBufSize, ISeqInStream *inStream, // const Byte *inData, size_t inDataSize, - CXzStatInfo *stat, - int *isMT, // 0 means that ST (Single-Thread) version was used + CXzStatInfo *stat, // out: decoding results and statistics + int *isMT, // out: 0 means that ST (Single-Thread) version was used + // 1 means that MT (Multi-Thread) version was used ICompressProgress *progress); EXTERN_C_END
diff --git a/third_party/lzma_sdk/XzCrc64Opt.c b/third_party/lzma_sdk/XzCrc64Opt.c index b2852de..93a9fff 100644 --- a/third_party/lzma_sdk/XzCrc64Opt.c +++ b/third_party/lzma_sdk/XzCrc64Opt.c
@@ -1,5 +1,5 @@ /* XzCrc64Opt.c -- CRC64 calculation -2017-06-30 : Igor Pavlov : Public domain */ +2021-02-09 : Igor Pavlov : Public domain */ #include "Precomp.h" @@ -9,6 +9,7 @@ #define CRC64_UPDATE_BYTE_2(crc, b) (table[((crc) ^ (b)) & 0xFF] ^ ((crc) >> 8)) +UInt64 MY_FAST_CALL XzCrc64UpdateT4(UInt64 v, const void *data, size_t size, const UInt64 *table); UInt64 MY_FAST_CALL XzCrc64UpdateT4(UInt64 v, const void *data, size_t size, const UInt64 *table) { const Byte *p = (const Byte *)data; @@ -16,7 +17,7 @@ v = CRC64_UPDATE_BYTE_2(v, *p); for (; size >= 4; size -= 4, p += 4) { - UInt32 d = (UInt32)v ^ *(const UInt32 *)p; + UInt32 d = (UInt32)v ^ *(const UInt32 *)(const void *)p; v = (v >> 32) ^ (table + 0x300)[((d ) & 0xFF)] ^ (table + 0x200)[((d >> 8) & 0xFF)] @@ -45,6 +46,7 @@ #define CRC64_UPDATE_BYTE_2_BE(crc, b) (table[(Byte)((crc) >> 56) ^ (b)] ^ ((crc) << 8)) +UInt64 MY_FAST_CALL XzCrc64UpdateT1_BeT4(UInt64 v, const void *data, size_t size, const UInt64 *table); UInt64 MY_FAST_CALL XzCrc64UpdateT1_BeT4(UInt64 v, const void *data, size_t size, const UInt64 *table) { const Byte *p = (const Byte *)data; @@ -54,7 +56,7 @@ v = CRC64_UPDATE_BYTE_2_BE(v, *p); for (; size >= 4; size -= 4, p += 4) { - UInt32 d = (UInt32)(v >> 32) ^ *(const UInt32 *)p; + UInt32 d = (UInt32)(v >> 32) ^ *(const UInt32 *)(const void *)p; v = (v << 32) ^ (table + 0x000)[((d ) & 0xFF)] ^ (table + 0x100)[((d >> 8) & 0xFF)]
diff --git a/third_party/lzma_sdk/XzDec.c b/third_party/lzma_sdk/XzDec.c index 395e83f..3f96a37 100644 --- a/third_party/lzma_sdk/XzDec.c +++ b/third_party/lzma_sdk/XzDec.c
@@ -1,5 +1,5 @@ /* XzDec.c -- Xz Decode -2019-02-02 : Igor Pavlov : Public domain */ +2021-09-04 : Igor Pavlov : Public domain */ #include "Precomp.h" @@ -240,6 +240,7 @@ } +SRes BraState_SetFromMethod(IStateCoder *p, UInt64 id, int encodeMode, ISzAllocPtr alloc); SRes BraState_SetFromMethod(IStateCoder *p, UInt64 id, int encodeMode, ISzAllocPtr alloc) { CBraState *decoder; @@ -772,7 +773,8 @@ #define READ_VARINT_AND_CHECK(buf, pos, size, res) \ { unsigned s = Xz_ReadVarInt(buf + pos, size - pos, res); \ - if (s == 0) return SZ_ERROR_ARCHIVE; pos += s; } + if (s == 0) return SZ_ERROR_ARCHIVE; \ + pos += s; } static BoolInt XzBlock_AreSupportedFilters(const CXzBlock *p) @@ -1038,7 +1040,7 @@ (p->outBuf ? NULL : dest), &destLen2, destFinish, src, &srcLen2, srcFinished2, finishMode2); - + *status = p->decoder.status; XzCheck_Update(&p->check, (p->outBuf ? p->outBuf + p->outDataWritten : dest), destLen2); if (!p->outBuf) @@ -1275,9 +1277,10 @@ } else { + const Byte *ptr = p->buf; p->state = XZ_STATE_STREAM_FOOTER; p->pos = 0; - if (CRC_GET_DIGEST(p->crc) != GetUi32(p->buf)) + if (CRC_GET_DIGEST(p->crc) != GetUi32(ptr)) return SZ_ERROR_CRC; } break; @@ -1456,7 +1459,6 @@ ISeqInStream *inStream; ISeqOutStream *outStream; ICompressProgress *progress; - // CXzStatInfo *stat; BoolInt finishMode; BoolInt outSize_Defined; @@ -1492,8 +1494,9 @@ UInt64 numBlocks; // UInt64 numBadBlocks; - SRes mainErrorCode; - + SRes mainErrorCode; // it's set to error code, if the size Code() output doesn't patch the size from Parsing stage + // it can be = SZ_ERROR_INPUT_EOF + // it can be = SZ_ERROR_DATA, in some another cases BoolInt isBlockHeaderState_Parse; BoolInt isBlockHeaderState_Write; UInt64 outProcessed_Parse; @@ -1877,7 +1880,7 @@ { // if (res == SZ_ERROR_MEM) return res; if (me->props.ignoreErrors && res != SZ_ERROR_MEM) - return S_OK; + return SZ_OK; return res; } } @@ -1898,15 +1901,18 @@ *outCodePos = coder->outCodeSize; *stop = True; + if (srcSize > coder->inPreSize - coder->inCodeSize) + return SZ_ERROR_FAIL; + if (coder->inCodeSize < coder->inPreHeaderSize) { - UInt64 rem = coder->inPreHeaderSize - coder->inCodeSize; - size_t step = srcSize; - if (step > rem) - step = (size_t)rem; + size_t step = coder->inPreHeaderSize - coder->inCodeSize; + if (step > srcSize) + step = srcSize; src += step; srcSize -= step; coder->inCodeSize += step; + *inCodePos = coder->inCodeSize; if (coder->inCodeSize < coder->inPreHeaderSize) { *stop = False; @@ -1956,7 +1962,7 @@ { *inCodePos = coder->inPreSize; *outCodePos = coder->outPreSize; - return S_OK; + return SZ_OK; } return coder->codeRes; } @@ -1966,7 +1972,7 @@ static SRes XzDecMt_Callback_Write(void *pp, unsigned coderIndex, BoolInt needWriteToStream, - const Byte *src, size_t srcSize, + const Byte *src, size_t srcSize, BoolInt isCross, // int srcFinished, BoolInt *needContinue, BoolInt *canRecode) @@ -1985,7 +1991,7 @@ if (!coder->dec.headerParsedOk || !coder->outBuf) { if (me->finishedDecoderIndex < 0) - me->finishedDecoderIndex = coderIndex; + me->finishedDecoderIndex = (int)coderIndex; return SZ_OK; } @@ -2077,7 +2083,7 @@ if (coder->codeRes != SZ_OK) if (!me->props.ignoreErrors) { - me->finishedDecoderIndex = coderIndex; + me->finishedDecoderIndex = (int)coderIndex; return res; } @@ -2086,7 +2092,7 @@ if (coder->inPreSize != coder->inCodeSize || coder->blockPackTotal != coder->inCodeSize) { - me->finishedDecoderIndex = coderIndex; + me->finishedDecoderIndex = (int)coderIndex; return SZ_OK; } @@ -2125,22 +2131,41 @@ return SZ_OK; } + /* + We have processed all xz-blocks of stream, + And xz unpacker is at XZ_STATE_BLOCK_HEADER state, where + (src) is a pointer to xz-Index structure. + We finish reading of current xz-Stream, including Zero padding after xz-Stream. + We exit, if we reach extra byte (first byte of new-Stream or another data). + But we don't update input stream pointer for that new extra byte. + If extra byte is not correct first byte of xz-signature, + we have SZ_ERROR_NO_ARCHIVE error here. + */ + res = XzUnpacker_Code(dec, NULL, &outSizeCur, src, &srcProcessed, me->mtc.readWasFinished, // srcFinished CODER_FINISH_END, // CODER_FINISH_ANY, &status); + + // res = SZ_ERROR_ARCHIVE; // for failure test me->status = status; me->codeRes = res; + if (isCross) + me->mtc.crossStart += srcProcessed; + me->mtc.inProcessed += srcProcessed; me->mtc.mtProgress.totalInSize = me->mtc.inProcessed; + srcSize -= srcProcessed; + src += srcProcessed; + if (res != SZ_OK) { - return S_OK; + return SZ_OK; // return res; } @@ -2149,20 +2174,26 @@ *needContinue = True; me->isBlockHeaderState_Parse = False; me->isBlockHeaderState_Write = False; + + if (!isCross) { Byte *crossBuf = MtDec_GetCrossBuff(&me->mtc); if (!crossBuf) return SZ_ERROR_MEM; - memcpy(crossBuf, src + srcProcessed, srcSize - srcProcessed); + if (srcSize != 0) + memcpy(crossBuf, src, srcSize); + me->mtc.crossStart = 0; + me->mtc.crossEnd = srcSize; } - me->mtc.crossStart = 0; - me->mtc.crossEnd = srcSize - srcProcessed; + + PRF_STR_INT("XZ_STATE_STREAM_HEADER crossEnd = ", (unsigned)me->mtc.crossEnd); + return SZ_OK; } - if (status != CODER_STATUS_NEEDS_MORE_INPUT) + if (status != CODER_STATUS_NEEDS_MORE_INPUT || srcSize != 0) { - return E_FAIL; + return SZ_ERROR_FAIL; } if (me->mtc.readWasFinished) @@ -2174,7 +2205,7 @@ { size_t inPos; size_t inLim; - const Byte *inData; + // const Byte *inData; UInt64 inProgressPrev = me->mtc.inProcessed; // XzDecMt_Prepare_InBuf_ST(p); @@ -2184,9 +2215,8 @@ inPos = 0; inLim = 0; - // outProcessed = 0; - inData = crossBuf; + // inData = crossBuf; for (;;) { @@ -2201,7 +2231,7 @@ { inPos = 0; inLim = me->mtc.inBufSize; - me->mtc.readRes = ISeqInStream_Read(me->inStream, (void *)inData, &inLim); + me->mtc.readRes = ISeqInStream_Read(me->inStream, (void *)crossBuf, &inLim); me->mtc.readProcessed += inLim; if (inLim == 0 || me->mtc.readRes != SZ_OK) me->mtc.readWasFinished = True; @@ -2213,7 +2243,7 @@ res = XzUnpacker_Code(dec, NULL, &outProcessed, - inData + inPos, &inProcessed, + crossBuf + inPos, &inProcessed, (inProcessed == 0), // srcFinished CODER_FINISH_END, &status); @@ -2225,7 +2255,7 @@ if (res != SZ_OK) { - return S_OK; + return SZ_OK; // return res; } @@ -2240,7 +2270,7 @@ } if (status != CODER_STATUS_NEEDS_MORE_INPUT) - return E_FAIL; + return SZ_ERROR_FAIL; if (me->mtc.progress) { @@ -2276,13 +2306,6 @@ p->NumStreams_Defined = False; p->NumBlocks_Defined = False; - // p->IsArc = False; - // p->UnexpectedEnd = False; - // p->Unsupported = False; - // p->HeadersError = False; - // p->DataError = False; - // p->CrcError = False; - p->DataAfterEnd = False; p->DecodingTruncated = False; @@ -2296,6 +2319,16 @@ +/* + XzDecMt_Decode_ST() can return SZ_OK or the following errors + - SZ_ERROR_MEM for memory allocation error + - error from XzUnpacker_Code() function + - SZ_ERROR_WRITE for ISeqOutStream::Write(). stat->CombinedRes_Type = SZ_ERROR_WRITE in that case + - ICompressProgress::Progress() error, stat->CombinedRes_Type = SZ_ERROR_PROGRESS. + But XzDecMt_Decode_ST() doesn't return ISeqInStream::Read() errors. + ISeqInStream::Read() result is set to p->readRes. + also it can set stat->CombinedRes_Type to SZ_ERROR_WRITE or SZ_ERROR_PROGRESS. +*/ static SRes XzDecMt_Decode_ST(CXzDecMt *p #ifndef _7ZIP_ST @@ -2384,7 +2417,7 @@ inPos = 0; inLim = p->inBufSize; inData = p->inBuf; - p->readRes = ISeqInStream_Read(p->inStream, (void *)inData, &inLim); + p->readRes = ISeqInStream_Read(p->inStream, (void *)p->inBuf, &inLim); p->readProcessed += inLim; if (inLim == 0 || p->readRes != SZ_OK) p->readWasFinished = True; @@ -2426,8 +2459,8 @@ if (finished || outProcessed >= outSize) if (outPos != 0) { - size_t written = ISeqOutStream_Write(p->outStream, p->outBuf, outPos); - p->outProcessed += written; + const size_t written = ISeqOutStream_Write(p->outStream, p->outBuf, outPos); + // p->outProcessed += written; // 21.01: BUG fixed if (written != outPos) { stat->CombinedRes_Type = SZ_ERROR_WRITE; @@ -2438,9 +2471,8 @@ if (p->progress && res == SZ_OK) { - UInt64 inDelta = p->inProcessed - inPrev; - UInt64 outDelta = p->outProcessed - outPrev; - if (inDelta >= (1 << 22) || outDelta >= (1 << 22)) + if (p->inProcessed - inPrev >= (1 << 22) || + p->outProcessed - outPrev >= (1 << 22)) { res = ICompressProgress_Progress(p->progress, p->inProcessed, p->outProcessed); if (res != SZ_OK) @@ -2455,14 +2487,31 @@ } if (finished) - return res; + { + // p->codeRes is preliminary error from XzUnpacker_Code. + // and it can be corrected later as final result + // so we return SZ_OK here instead of (res); + return SZ_OK; + // return res; + } } } -static SRes XzStatInfo_SetStat(const CXzUnpacker *dec, + + +/* +XzStatInfo_SetStat() transforms + CXzUnpacker return code and status to combined CXzStatInfo results. + it can convert SZ_OK to SZ_ERROR_INPUT_EOF + it can convert SZ_ERROR_NO_ARCHIVE to SZ_OK and (DataAfterEnd = 1) +*/ + +static void XzStatInfo_SetStat(const CXzUnpacker *dec, int finishMode, - UInt64 readProcessed, UInt64 inProcessed, - SRes res, ECoderStatus status, + // UInt64 readProcessed, + UInt64 inProcessed, + SRes res, // it's result from CXzUnpacker unpacker + ECoderStatus status, BoolInt decodingTruncated, CXzStatInfo *stat) { @@ -2484,12 +2533,20 @@ if (status == CODER_STATUS_NEEDS_MORE_INPUT) { // CODER_STATUS_NEEDS_MORE_INPUT is expected status for correct xz streams + // any extra data is part of correct data extraSize = 0; + // if xz stream was not finished, then we need more data if (!XzUnpacker_IsStreamWasFinished(dec)) res = SZ_ERROR_INPUT_EOF; } - else if (!decodingTruncated || finishMode) // (status == CODER_STATUS_NOT_FINISHED) - res = SZ_ERROR_DATA; + else + { + // CODER_STATUS_FINISHED_WITH_MARK is not possible for multi stream xz decoding + // so he we have (status == CODER_STATUS_NOT_FINISHED) + // if (status != CODER_STATUS_FINISHED_WITH_MARK) + if (!decodingTruncated || finishMode) + res = SZ_ERROR_DATA; + } } else if (res == SZ_ERROR_NO_ARCHIVE) { @@ -2497,24 +2554,29 @@ SZ_ERROR_NO_ARCHIVE is possible for 2 states: XZ_STATE_STREAM_HEADER - if bad signature or bad CRC XZ_STATE_STREAM_PADDING - if non-zero padding data - extraSize / inProcessed don't include "bad" byte + extraSize and inProcessed don't include "bad" byte */ - if (inProcessed != extraSize) // if good streams before error - if (extraSize != 0 || readProcessed != inProcessed) + // if (inProcessed == extraSize), there was no any good xz stream header, and we keep error + if (inProcessed != extraSize) // if there were good xz streams before error + { + // if (extraSize != 0 || readProcessed != inProcessed) { + // he we suppose that all xz streams were finsihed OK, and we have + // some extra data after all streams stat->DataAfterEnd = True; - // there is some good xz stream before. So we set SZ_OK res = SZ_OK; } + } } - stat->DecodeRes = res; + if (stat->DecodeRes == SZ_OK) + stat->DecodeRes = res; stat->InSize -= extraSize; - return res; } + SRes XzDecMt_Decode(CXzDecMtHandle pp, const CXzDecMtProps *props, const UInt64 *outDataSize, int finishMode, @@ -2557,8 +2619,9 @@ p->inProcessed = 0; p->readProcessed = 0; p->readWasFinished = False; + p->readRes = SZ_OK; - p->codeRes = 0; + p->codeRes = SZ_OK; p->status = CODER_STATUS_NOT_SPECIFIED; XzUnpacker_Init(&p->dec); @@ -2589,8 +2652,9 @@ if (p->props.numThreads > 1) { - IMtDecCallback vt; - + IMtDecCallback2 vt; + BoolInt needContinue; + SRes res; // we just free ST buffers here // but we still keep state variables, that was set in XzUnpacker_Init() XzDecMt_FreeSt(p); @@ -2628,45 +2692,45 @@ vt.Code = XzDecMt_Callback_Code; vt.Write = XzDecMt_Callback_Write; + + res = MtDec_Code(&p->mtc); + + + stat->InSize = p->mtc.inProcessed; + + p->inProcessed = p->mtc.inProcessed; + p->readRes = p->mtc.readRes; + p->readWasFinished = p->mtc.readWasFinished; + p->readProcessed = p->mtc.readProcessed; + + tMode = True; + needContinue = False; + + if (res == SZ_OK) { - BoolInt needContinue; - - SRes res = MtDec_Code(&p->mtc); - - stat->InSize = p->mtc.inProcessed; - - p->inProcessed = p->mtc.inProcessed; - p->readRes = p->mtc.readRes; - p->readWasFinished = p->mtc.readWasFinished; - p->readProcessed = p->mtc.readProcessed; - - tMode = True; - needContinue = False; - - if (res == SZ_OK) + if (p->mtc.mtProgress.res != SZ_OK) { - if (p->mtc.mtProgress.res != SZ_OK) - { - res = p->mtc.mtProgress.res; - stat->ProgressRes = res; - stat->CombinedRes_Type = SZ_ERROR_PROGRESS; - } - else - needContinue = p->mtc.needContinue; + res = p->mtc.mtProgress.res; + stat->ProgressRes = res; + stat->CombinedRes_Type = SZ_ERROR_PROGRESS; } - - if (!needContinue) + else + needContinue = p->mtc.needContinue; + } + + if (!needContinue) + { { SRes codeRes; BoolInt truncated = False; ECoderStatus status; - CXzUnpacker *dec; + const CXzUnpacker *dec; stat->OutSize = p->outProcessed; if (p->finishedDecoderIndex >= 0) { - CXzDecMtThread *coder = &p->coders[(unsigned)p->finishedDecoderIndex]; + const CXzDecMtThread *coder = &p->coders[(unsigned)p->finishedDecoderIndex]; codeRes = coder->codeRes; dec = &coder->dec; status = coder->status; @@ -2679,41 +2743,46 @@ truncated = p->parsing_Truncated; } else - return E_FAIL; + return SZ_ERROR_FAIL; + + if (p->mainErrorCode != SZ_OK) + stat->DecodeRes = p->mainErrorCode; XzStatInfo_SetStat(dec, p->finishMode, - p->mtc.readProcessed, p->mtc.inProcessed, + // p->mtc.readProcessed, + p->mtc.inProcessed, codeRes, status, truncated, stat); - - if (res == SZ_OK) - { - if (p->writeRes != SZ_OK) - { - res = p->writeRes; - stat->CombinedRes_Type = SZ_ERROR_WRITE; - } - else if (p->mtc.readRes != SZ_OK && p->mtc.inProcessed == p->mtc.readProcessed) - { - res = p->mtc.readRes; - stat->ReadRes = res; - stat->CombinedRes_Type = SZ_ERROR_READ; - } - else if (p->mainErrorCode != SZ_OK) - { - res = p->mainErrorCode; - } - } - - stat->CombinedRes = res; - if (stat->CombinedRes_Type == SZ_OK) - stat->CombinedRes_Type = res; - return res; } - PRF_STR("----- decoding ST -----"); + if (res == SZ_OK) + { + stat->ReadRes = p->mtc.readRes; + + if (p->writeRes != SZ_OK) + { + res = p->writeRes; + stat->CombinedRes_Type = SZ_ERROR_WRITE; + } + else if (p->mtc.readRes != SZ_OK + // && p->mtc.inProcessed == p->mtc.readProcessed + && stat->DecodeRes == SZ_ERROR_INPUT_EOF) + { + res = p->mtc.readRes; + stat->CombinedRes_Type = SZ_ERROR_READ; + } + else if (stat->DecodeRes != SZ_OK) + res = stat->DecodeRes; + } + + stat->CombinedRes = res; + if (stat->CombinedRes_Type == SZ_OK) + stat->CombinedRes_Type = res; + return res; } + + PRF_STR("----- decoding ST -----"); } #endif @@ -2729,33 +2798,35 @@ , stat ); + #ifndef _7ZIP_ST + // we must set error code from MT decoding at first + if (p->mainErrorCode != SZ_OK) + stat->DecodeRes = p->mainErrorCode; + #endif + XzStatInfo_SetStat(&p->dec, p->finishMode, - p->readProcessed, p->inProcessed, + // p->readProcessed, + p->inProcessed, p->codeRes, p->status, False, // truncated stat); + stat->ReadRes = p->readRes; + if (res == SZ_OK) { - /* - if (p->writeRes != SZ_OK) + if (p->readRes != SZ_OK + // && p->inProcessed == p->readProcessed + && stat->DecodeRes == SZ_ERROR_INPUT_EOF) { - res = p->writeRes; - stat->CombinedRes_Type = SZ_ERROR_WRITE; - } - else - */ - if (p->readRes != SZ_OK && p->inProcessed == p->readProcessed) - { + // we set read error as combined error, only if that error was the reason + // of decoding problem res = p->readRes; - stat->ReadRes = res; stat->CombinedRes_Type = SZ_ERROR_READ; } - #ifndef _7ZIP_ST - else if (p->mainErrorCode != SZ_OK) - res = p->mainErrorCode; - #endif + else if (stat->DecodeRes != SZ_OK) + res = stat->DecodeRes; } stat->CombinedRes = res;
diff --git a/third_party/lzma_sdk/chromium.patch b/third_party/lzma_sdk/chromium.patch new file mode 100644 index 0000000..e2a614b --- /dev/null +++ b/third_party/lzma_sdk/chromium.patch
@@ -0,0 +1,79 @@ +diff --git "a/lzma2107\\C/7zCrc.c" "b/third_party\\lzma_sdk/7zCrc.c" +index f186324ddc609..c0cc9bc7812e0 100644 +--- "a/lzma2107\\C/7zCrc.c" ++++ "b/third_party\\lzma_sdk/7zCrc.c" +@@ -78,20 +78,20 @@ UInt32 MY_FAST_CALL CrcUpdateT1(UInt32 v, const void *data, size_t size, const U + #if defined(_MSC_VER) + #if defined(MY_CPU_ARM64) + #if (_MSC_VER >= 1910) +- #define USE_ARM64_CRC ++ // #define USE_ARM64_CRC + #endif + #endif + #elif (defined(__clang__) && (__clang_major__ >= 3)) \ + || (defined(__GNUC__) && (__GNUC__ > 4)) + #if !defined(__ARM_FEATURE_CRC32) +- #define __ARM_FEATURE_CRC32 1 ++ // #define __ARM_FEATURE_CRC32 1 + #if (!defined(__clang__) || (__clang_major__ > 3)) // fix these numbers +- #define ATTRIB_CRC __attribute__((__target__("arch=armv8-a+crc"))) ++ // #define ATTRIB_CRC __attribute__((__target__("arch=armv8-a+crc"))) + #endif + #endif + #if defined(__ARM_FEATURE_CRC32) +- #define USE_ARM64_CRC +- #include <arm_acle.h> ++ // #define USE_ARM64_CRC ++ // #include <arm_acle.h> + #endif + #endif + +diff --git "a/lzma2107\\C/CpuArch.c" "b/third_party\\lzma_sdk/CpuArch.c" +index fa9afe3970b3f..30451fba9b97b 100644 +--- "a/lzma2107\\C/CpuArch.c" ++++ "b/third_party\\lzma_sdk/CpuArch.c" +@@ -417,7 +417,9 @@ BoolInt CPU_IsSupported_AES (void) { return APPLE_CRYPTO_SUPPORT_VAL; } + + #include <sys/auxv.h> + ++#if !defined(ARMV8_OS_FUCHSIA) + #define USE_HWCAP ++#endif // !defined(ARMV8_OS_FUCHSIA) + + #ifdef USE_HWCAP + +diff --git "a/lzma2107\\C/LzFind.c" "b/third_party\\lzma_sdk/LzFind.c" +index 1b73c28484ccf..36f7330911435 100644 +--- "a/lzma2107\\C/LzFind.c" ++++ "b/third_party\\lzma_sdk/LzFind.c" +@@ -505,7 +505,7 @@ void MatchFinder_Init(CMatchFinder *p) + } + + +- ++#if 0 + #ifdef MY_CPU_X86_OR_AMD64 + #if defined(__clang__) && (__clang_major__ >= 8) \ + || defined(__GNUC__) && (__GNUC__ >= 8) \ +@@ -549,6 +549,7 @@ void MatchFinder_Init(CMatchFinder *p) + #endif + + #endif ++#endif + + /* + #ifndef ATTRIB_SSE41 +diff --git "a/lzma2107\\C/Sha256.c" "b/third_party\\lzma_sdk/Sha256.c" +index 8b3983ea7323d..21996848c9156 100644 +--- "a/lzma2107\\C/Sha256.c" ++++ "b/third_party\\lzma_sdk/Sha256.c" +@@ -32,7 +32,8 @@ This code is based on public domain code from Wei Dai's Crypto++ library. */ + #define _SHA_SUPPORTED + #endif + #endif +-#elif defined(MY_CPU_ARM_OR_ARM64) ++// TODO(crbug.com/1338627): Enable ARM optimizations ++#elif 0 // defined(MY_CPU_ARM_OR_ARM64) + #ifdef _MSC_VER + #if _MSC_VER >= 1910 + #define _SHA_SUPPORTED
diff --git a/third_party/node/README.chromium b/third_party/node/README.chromium index fefe2e7e..3758aaf 100644 --- a/third_party/node/README.chromium +++ b/third_party/node/README.chromium
@@ -248,3 +248,19 @@ Local Modifications: (none) + +Name: HTML Minifier +Short Name: html-minifier +URL: https://www.npmjs.com/package/html-minifier +Version: 4.0.0 +License: MIT +Security Critical: No, as html-minifier itself is only used during build time. +It does affect the contents of shipped HTML files though. + +Description: +HTMLMinifier is a highly configurable, well-tested, JavaScript-based HTML +minifier. + +Local Modifications: +Removed uglify-js dependency since we don't plan to use the --minify-js flag in +Chromium.
diff --git a/third_party/node/html_minifier.patch b/third_party/node/html_minifier.patch new file mode 100644 index 0000000..cce31233 --- /dev/null +++ b/third_party/node/html_minifier.patch
@@ -0,0 +1,12 @@ +diff --git a/src/htmlminifier.js b/src/htmlminifier.js +index d7efa99148a21..eb19dd6b33ff3 100644 +--- a/src/htmlminifier.js ++++ b/src/htmlminifier.js +@@ -5,7 +5,6 @@ var decode = require('he').decode; + var HTMLParser = require('./htmlparser').HTMLParser; + var RelateUrl = require('relateurl'); + var TokenChain = require('./tokenchain'); +-var UglifyJS = require('uglify-js'); + var utils = require('./utils'); + + function trimWhitespace(str) {
diff --git a/third_party/node/node_modules.py b/third_party/node/node_modules.py index d45f1c3..2229aff 100755 --- a/third_party/node/node_modules.py +++ b/third_party/node/node_modules.py
@@ -26,6 +26,11 @@ return _path_in_node_modules('eslint', 'bin', 'eslint') +def PathToHtmlMinifier(): + return _path_in_node_modules('html-minifier', 'cli.js') + + +# TODO(crbug.com/1325169): Delete after migration to html-minifier is complete. def PathToPolymerCssBuild(): return _path_in_node_modules('polymer-css-build', 'bin', 'polymer-css-build')
diff --git a/third_party/node/node_modules.tar.gz.sha1 b/third_party/node/node_modules.tar.gz.sha1 index bc95ff9..5758ae2 100644 --- a/third_party/node/node_modules.tar.gz.sha1 +++ b/third_party/node/node_modules.tar.gz.sha1
@@ -1 +1 @@ -6d04f91df50efaa847885c0296ec9ed440764cb7 +fb45b506e9a41671bb4666652b1cb1a5849dc9aa
diff --git a/third_party/node/npm_exclude.txt b/third_party/node/npm_exclude.txt index d510100..bc68010 100644 --- a/third_party/node/npm_exclude.txt +++ b/third_party/node/npm_exclude.txt
@@ -20,3 +20,7 @@ *.map typescript/lib/* typescript/loc/* + +# This is pulled in from html-minifier, but there is no plan to use the +# --minify-js which is the only entry to that codepath. +uglify-js/*
diff --git a/third_party/node/package-lock.json b/third_party/node/package-lock.json index 0d0285a..b69ec60 100644 --- a/third_party/node/package-lock.json +++ b/third_party/node/package-lock.json
@@ -27,6 +27,7 @@ "crisper": "2.1.1", "eslint": "7.11.0", "eslint-plugin-jsdoc": "37.5.1", + "html-minifier": "4.0.0", "polymer-bundler": "4.0.10", "polymer-css-build": "0.7.0", "rollup": "2.58.0", @@ -1251,6 +1252,15 @@ "node": ">=6" } }, + "node_modules/camel-case": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-3.0.0.tgz", + "integrity": "sha512-+MbKztAYHXPr1jNTSKQF52VpcFjwY5RkR7fxksV8Doo4KAYc5Fl4UJRgthBbTmEx8C54DqahhbLJkDwjI3PI/w==", + "dependencies": { + "no-case": "^2.2.0", + "upper-case": "^1.1.1" + } + }, "node_modules/cancel-token": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/cancel-token/-/cancel-token-0.1.1.tgz", @@ -1272,6 +1282,25 @@ "node": ">=4" } }, + "node_modules/clean-css": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.4.tgz", + "integrity": "sha512-EJUDT7nDVFDvaQgAo2G/PJvxmp1o/c6iXLbswsBbUFXi1Nr+AjA2cKmfbKDMjMvzEe75g3P6JkaDDAKk96A85A==", + "dependencies": { + "source-map": "~0.6.0" + }, + "engines": { + "node": ">= 4.0" + } + }, + "node_modules/clean-css/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/clone": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", @@ -2136,6 +2165,34 @@ "node": ">=4" } }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "bin": { + "he": "bin/he" + } + }, + "node_modules/html-minifier": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/html-minifier/-/html-minifier-4.0.0.tgz", + "integrity": "sha512-aoGxanpFPLg7MkIl/DDFYtb0iWz7jMFGqFhvEDZga6/4QTjneiD8I/NXL1x5aaoCp7FSIT6h/OhykDdPsbtMig==", + "dependencies": { + "camel-case": "^3.0.0", + "clean-css": "^4.2.1", + "commander": "^2.19.0", + "he": "^1.2.0", + "param-case": "^2.1.1", + "relateurl": "^0.2.7", + "uglify-js": "^3.5.1" + }, + "bin": { + "html-minifier": "cli.js" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/ignore": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", @@ -2361,6 +2418,11 @@ "loose-envify": "cli.js" } }, + "node_modules/lower-case": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz", + "integrity": "sha512-2Fgx1Ycm599x+WGpIYwJOvsjmXFzTSc34IwDWALRA/8AopUKAVPwfJ+h5+f85BCp0PWmmJcWzEpxOpoXycMpdA==" + }, "node_modules/lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -2442,6 +2504,14 @@ "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=" }, + "node_modules/no-case": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-2.3.2.tgz", + "integrity": "sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==", + "dependencies": { + "lower-case": "^1.1.1" + } + }, "node_modules/nth-check": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.1.tgz", @@ -2477,6 +2547,14 @@ "node": ">= 0.8.0" } }, + "node_modules/param-case": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/param-case/-/param-case-2.1.1.tgz", + "integrity": "sha512-eQE845L6ot89sk2N8liD8HAuH4ca6Vvr7VWAWwt7+kvvG5aBcPmmphQ68JsEG2qa9n1TykS2DLeMt363AAH8/w==", + "dependencies": { + "no-case": "^2.2.0" + } + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -3155,6 +3233,14 @@ "node": ">=0.1.14" } }, + "node_modules/relateurl": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", + "integrity": "sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=", + "engines": { + "node": ">= 0.10" + } + }, "node_modules/repeating": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", @@ -3617,6 +3703,22 @@ "resolved": "https://registry.npmjs.org/typical/-/typical-2.6.1.tgz", "integrity": "sha1-XAgOXWYcu+OCWdLnCjxyU+hziB0=" }, + "node_modules/uglify-js": { + "version": "3.15.5", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.15.5.tgz", + "integrity": "sha512-hNM5q5GbBRB5xB+PMqVRcgYe4c8jbyZ1pzZhS6jbq54/4F2gFK869ZheiE5A8/t+W5jtTNpWef/5Q9zk639FNQ==", + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/upper-case": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz", + "integrity": "sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg=" + }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -4704,6 +4806,15 @@ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==" }, + "camel-case": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-3.0.0.tgz", + "integrity": "sha512-+MbKztAYHXPr1jNTSKQF52VpcFjwY5RkR7fxksV8Doo4KAYc5Fl4UJRgthBbTmEx8C54DqahhbLJkDwjI3PI/w==", + "requires": { + "no-case": "^2.2.0", + "upper-case": "^1.1.1" + } + }, "cancel-token": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/cancel-token/-/cancel-token-0.1.1.tgz", @@ -4722,6 +4833,21 @@ "supports-color": "^5.3.0" } }, + "clean-css": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.4.tgz", + "integrity": "sha512-EJUDT7nDVFDvaQgAo2G/PJvxmp1o/c6iXLbswsBbUFXi1Nr+AjA2cKmfbKDMjMvzEe75g3P6JkaDDAKk96A85A==", + "requires": { + "source-map": "~0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + } + } + }, "clone": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", @@ -5357,6 +5483,25 @@ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" }, + "he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==" + }, + "html-minifier": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/html-minifier/-/html-minifier-4.0.0.tgz", + "integrity": "sha512-aoGxanpFPLg7MkIl/DDFYtb0iWz7jMFGqFhvEDZga6/4QTjneiD8I/NXL1x5aaoCp7FSIT6h/OhykDdPsbtMig==", + "requires": { + "camel-case": "^3.0.0", + "clean-css": "^4.2.1", + "commander": "^2.19.0", + "he": "^1.2.0", + "param-case": "^2.1.1", + "relateurl": "^0.2.7", + "uglify-js": "^3.5.1" + } + }, "ignore": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", @@ -5525,6 +5670,11 @@ "js-tokens": "^3.0.0 || ^4.0.0" } }, + "lower-case": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz", + "integrity": "sha512-2Fgx1Ycm599x+WGpIYwJOvsjmXFzTSc34IwDWALRA/8AopUKAVPwfJ+h5+f85BCp0PWmmJcWzEpxOpoXycMpdA==" + }, "lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -5591,6 +5741,14 @@ "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=" }, + "no-case": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-2.3.2.tgz", + "integrity": "sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==", + "requires": { + "lower-case": "^1.1.1" + } + }, "nth-check": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.1.tgz", @@ -5620,6 +5778,14 @@ "word-wrap": "^1.2.3" } }, + "param-case": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/param-case/-/param-case-2.1.1.tgz", + "integrity": "sha512-eQE845L6ot89sk2N8liD8HAuH4ca6Vvr7VWAWwt7+kvvG5aBcPmmphQ68JsEG2qa9n1TykS2DLeMt363AAH8/w==", + "requires": { + "no-case": "^2.2.0" + } + }, "parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -6147,6 +6313,11 @@ "resolved": "https://registry.npmjs.org/regextras/-/regextras-0.8.0.tgz", "integrity": "sha512-k519uI04Z3SaY0fLX843MRXnDeG2+vHOFsyhiPZvNLe7r8rD2YNRjq4BQLZZ0oAr2NrtvZlICsXysGNFPGa3CQ==" }, + "relateurl": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", + "integrity": "sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=" + }, "repeating": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", @@ -6478,6 +6649,16 @@ "resolved": "https://registry.npmjs.org/typical/-/typical-2.6.1.tgz", "integrity": "sha1-XAgOXWYcu+OCWdLnCjxyU+hziB0=" }, + "uglify-js": { + "version": "3.15.5", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.15.5.tgz", + "integrity": "sha512-hNM5q5GbBRB5xB+PMqVRcgYe4c8jbyZ1pzZhS6jbq54/4F2gFK869ZheiE5A8/t+W5jtTNpWef/5Q9zk639FNQ==" + }, + "upper-case": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz", + "integrity": "sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg=" + }, "uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
diff --git a/third_party/node/package.json b/third_party/node/package.json index 4e74aad..dbad99f 100644 --- a/third_party/node/package.json +++ b/third_party/node/package.json
@@ -22,6 +22,7 @@ "crisper": "2.1.1", "eslint": "7.11.0", "eslint-plugin-jsdoc": "37.5.1", + "html-minifier": "4.0.0", "polymer-bundler": "4.0.10", "polymer-css-build": "0.7.0", "rollup": "2.58.0",
diff --git a/third_party/node/update_npm_deps b/third_party/node/update_npm_deps index d520f47b..4c6f756 100755 --- a/third_party/node/update_npm_deps +++ b/third_party/node/update_npm_deps
@@ -24,6 +24,9 @@ # Apply local patch to d3. patch -d node_modules/@types/d3/ -p1 < chromium_d3_types_index.patch +# Apply local patch to html-minifier +patch -d node_modules/html-minifier/ -p1 < html_minifier.patch + rsync -c --delete -r -q --include-from="npm_include.txt" --exclude-from="npm_exclude.txt" \ --prune-empty-dirs "node_modules/" "node_modules_filtered/"
diff --git a/tools/android/test_health/OWNERS b/tools/android/test_health/OWNERS index d35eee9..005acb8 100644 --- a/tools/android/test_health/OWNERS +++ b/tools/android/test_health/OWNERS
@@ -1,3 +1,2 @@ -andrewheard@chromium.org fredmello@chromium.org wnwen@chromium.org
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl index 464fa52..4c26347a 100644 --- a/tools/mb/mb_config.pyl +++ b/tools/mb/mb_config.pyl
@@ -787,11 +787,14 @@ 'internal.chrome.fyi': { 'chromeos-amd64-generic-lacros-internal-rel': 'chromeos_amd64-generic_lacros_rel', 'linux-autofill-captured-sites-rel': 'release_bot', + 'linux-finch-smoke-chrome': 'official_goma', 'linux-password-manager-captured-sites-rel': 'release_bot', 'lorenz-graph-dbg': 'android_debug_static_external_bot', 'mac-autofill-captured-sites-rel': 'release_bot', + 'mac-finch-smoke-chrome': 'official_goma_mac', 'win-autofill-captured-sites-rel': 'release_bot', 'win-celab-rel': 'official_celab_release_bot', + 'win-finch-smoke-chrome': 'official_goma', 'win-password-manager-captured-sites-rel': 'release_bot', }, @@ -867,13 +870,16 @@ 'linux-chrome-beta': 'official_goma', 'linux-chrome-stable': 'official_goma', 'linux-chromeos-chrome': 'official_goma_chromeos_include_unwind_tables', + 'linux-finch-smoke-chrome': 'official_goma', 'mac-chrome': 'official_goma_mac', 'mac-chrome-beta': 'official_goma_mac', 'mac-chrome-stable': 'official_goma_mac', + 'mac-finch-smoke-chrome': 'official_goma_mac', 'win-celab-try-rel': 'official_celab_release_bot', 'win-chrome': 'official_goma_x86', 'win-chrome-beta': 'official_goma_x86', 'win-chrome-stable': 'official_goma_x86', + 'win-finch-smoke-chrome': 'official_goma', 'win64-chrome': 'official_goma_x64', 'win64-chrome-beta': 'official_goma_x64', 'win64-chrome-stable': 'official_goma_x64',
diff --git a/tools/mb/mb_config_expectations/internal.chrome.fyi.json b/tools/mb/mb_config_expectations/internal.chrome.fyi.json index cc84dbf..6bd9cb3 100644 --- a/tools/mb/mb_config_expectations/internal.chrome.fyi.json +++ b/tools/mb/mb_config_expectations/internal.chrome.fyi.json
@@ -19,6 +19,13 @@ "use_goma": true } }, + "linux-finch-smoke-chrome": { + "gn_args": { + "is_chrome_branded": true, + "is_official_build": true, + "use_goma": true + } + }, "linux-password-manager-captured-sites-rel": { "gn_args": { "dcheck_always_on": false, @@ -47,6 +54,14 @@ "use_goma": true } }, + "mac-finch-smoke-chrome": { + "gn_args": { + "ignore_missing_widevine_signing_cert": true, + "is_chrome_branded": true, + "is_official_build": true, + "use_goma": true + } + }, "win-autofill-captured-sites-rel": { "gn_args": { "dcheck_always_on": false, @@ -66,6 +81,13 @@ "use_goma": true } }, + "win-finch-smoke-chrome": { + "gn_args": { + "is_chrome_branded": true, + "is_official_build": true, + "use_goma": true + } + }, "win-password-manager-captured-sites-rel": { "gn_args": { "dcheck_always_on": false,
diff --git a/tools/mb/mb_config_expectations/tryserver.chrome.json b/tools/mb/mb_config_expectations/tryserver.chrome.json index 05d3191..f93d83f 100644 --- a/tools/mb/mb_config_expectations/tryserver.chrome.json +++ b/tools/mb/mb_config_expectations/tryserver.chrome.json
@@ -315,6 +315,13 @@ "use_goma": true } }, + "linux-finch-smoke-chrome": { + "gn_args": { + "is_chrome_branded": true, + "is_official_build": true, + "use_goma": true + } + }, "mac-chrome": { "gn_args": { "ignore_missing_widevine_signing_cert": true, @@ -339,6 +346,14 @@ "use_goma": true } }, + "mac-finch-smoke-chrome": { + "gn_args": { + "ignore_missing_widevine_signing_cert": true, + "is_chrome_branded": true, + "is_official_build": true, + "use_goma": true + } + }, "win-celab-try-rel": { "gn_args": { "dcheck_always_on": false, @@ -374,6 +389,13 @@ "use_goma": true } }, + "win-finch-smoke-chrome": { + "gn_args": { + "is_chrome_branded": true, + "is_official_build": true, + "use_goma": true + } + }, "win64-chrome": { "gn_args": { "is_chrome_branded": true,
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml index 5249b4f..e082b06c 100644 --- a/tools/metrics/histograms/enums.xml +++ b/tools/metrics/histograms/enums.xml
@@ -11231,6 +11231,7 @@ <enum name="BluetoothStackName"> <int value="0" label="BlueZ"/> <int value="1" label="Floss"/> + <int value="2" label="Unknown"/> </enum> <enum name="BluetoothStatus"> @@ -55508,6 +55509,7 @@ <int value="-1835592983" label="WebAppEnableLaunchHandler:disabled"/> <int value="-1834967404" label="DesktopCaptureLacrosV2:enabled"/> <int value="-1834841895" label="CrostiniWebUIUpgrader:enabled"/> + <int value="-1834475132" label="IgnoreSyncEncryptionKeysLongMissing:enabled"/> <int value="-1834151268" label="OmniboxDynamicMaxAutocomplete:disabled"/> <int value="-1833149810" label="enable-accessibility-tab-switcher"/> <int value="-1832575380" label="show-saved-copy"/> @@ -56369,6 +56371,7 @@ <int value="-1285790931" label="FencedFrames:disabled"/> <int value="-1285021473" label="save-page-as-mhtml"/> <int value="-1284637134" label="pull-to-refresh"/> + <int value="-1283306503" label="LensSearchOptimizations:disabled"/> <int value="-1283164264" label="CroshSWA:disabled"/> <int value="-1282992935" label="AutofillLocalCardMigrationShowFeedback:disabled"/> @@ -58577,6 +58580,7 @@ <int value="157217034" label="enable-tab-for-desktop-share"/> <int value="157318016" label="AutomaticTabDiscarding:enabled"/> <int value="158490417" label="PhoneHubCameraRoll:enabled"/> + <int value="160429011" label="LensSearchOptimizations:enabled"/> <int value="160506687" label="ArcRtVcpuDualCore:disabled"/> <int value="160524775" label="PDFTwoUpView:disabled"/> <int value="160838658" label="SmartDimModelV3:enabled"/> @@ -61070,6 +61074,7 @@ <int value="1803465156" label="enable-zero-suggest-most-visited"/> <int value="1803470125" label="SyncUSSSessions:enabled"/> <int value="1803914892" label="TemporaryUnexpireFlagsM76:enabled"/> + <int value="1806220475" label="IgnoreSyncEncryptionKeysLongMissing:disabled"/> <int value="1806450300" label="WebUsbDeviceDetection:enabled"/> <int value="1807374811" label="CCTModuleCache:enabled"/> <int value="1808936313" label="AutofillEnableNameSurenameParsing:enabled"/> @@ -87173,6 +87178,13 @@ <int value="2" label="Dismiss"/> </enum> +<enum name="SidePanelOpenTrigger"> + <int value="0" label="Opened from toolbar button"/> + <int value="1" label="Opened from lens context menu"/> + <int value="2" label="Opened from side search page action button"/> + <int value="3" label="Opened from user notes context menu in page"/> +</enum> + <enum name="SideSearchAvailabilityChangeType"> <int value="0" label="Become available"/> <int value="1" label="Become unavailable"/>
diff --git a/tools/metrics/histograms/metadata/bluetooth/histograms.xml b/tools/metrics/histograms/metadata/bluetooth/histograms.xml index 1afc702..b1a873b8c 100644 --- a/tools/metrics/histograms/metadata/bluetooth/histograms.xml +++ b/tools/metrics/histograms/metadata/bluetooth/histograms.xml
@@ -79,7 +79,7 @@ </histogram> <histogram name="Bluetooth.ChromeOS.DeviceConnected.{ConnectionType}" - enum="BluetoothDeviceType" expires_after="2022-07-01"> + enum="BluetoothDeviceType" expires_after="2023-06-01"> <owner>khorimoto@chromium.org</owner> <owner>cros-connectivity@google.com</owner> <summary> @@ -103,7 +103,7 @@ <histogram name="Bluetooth.ChromeOS.DeviceSelectionDuration{DeviceSelectionUISurfaces}" - units="ms" expires_after="2022-07-01"> + units="ms" expires_after="2023-06-01"> <owner>khorimoto@chromium.org</owner> <owner>cros-connectivity@google.com</owner> <summary> @@ -118,7 +118,7 @@ <histogram name="Bluetooth.ChromeOS.DeviceSelectionDuration{DeviceSelectionUISurfaces}.NotPaired{BluetoothTransportTypes}" - units="ms" expires_after="2022-07-01"> + units="ms" expires_after="2023-06-01"> <owner>khorimoto@chromium.org</owner> <owner>cros-connectivity@google.com</owner> <summary> @@ -132,7 +132,7 @@ <histogram name="Bluetooth.ChromeOS.DeviceSelectionDuration{DeviceSelectionUISurfaces}{BluetoothPairedStates}" - units="ms" expires_after="2022-07-01"> + units="ms" expires_after="2023-06-01"> <owner>khorimoto@chromium.org</owner> <owner>cros-connectivity@google.com</owner> <summary>
diff --git a/tools/metrics/histograms/metadata/browser/histograms.xml b/tools/metrics/histograms/metadata/browser/histograms.xml index 729cc5c4..9637c0be 100644 --- a/tools/metrics/histograms/metadata/browser/histograms.xml +++ b/tools/metrics/histograms/metadata/browser/histograms.xml
@@ -1112,6 +1112,27 @@ </summary> </histogram> +<histogram name="SidePanel.OpenDuration" units="ms" expires_after="2023-06-04"> + <owner>corising@chromium.org</owner> + <owner>chrome-desktop-ui-sea@google.com</owner> + <summary> + Records how long the side panel was open for (capped at 1 hour) starting + when the side panel is triggered to be opened. Note this might be different + than when the side panel becomes visible due to delays for loading content. + Recorded when the side panel is closed. + </summary> +</histogram> + +<histogram name="SidePanel.OpenTrigger" enum="SidePanelOpenTrigger" + expires_after="2023-06-04"> + <owner>corising@chromium.org</owner> + <owner>chrome-desktop-ui-sea@google.com</owner> + <summary> + Logs the UI location from which the side panel is triggered to be opened. + Recorded when the side panel is shown and a trigger is provided. + </summary> +</histogram> + </histograms> </histogram-configuration>
diff --git a/tools/metrics/histograms/metadata/custom_tabs/histograms.xml b/tools/metrics/histograms/metadata/custom_tabs/histograms.xml index 33979a9..4300c286 100644 --- a/tools/metrics/histograms/metadata/custom_tabs/histograms.xml +++ b/tools/metrics/histograms/metadata/custom_tabs/histograms.xml
@@ -304,6 +304,46 @@ </summary> </histogram> +<histogram + name="CustomTabs.RetainableSessions.TimeBetweenLaunch{IdentifierType}" + units="seconds" expires_after="2022-11-03"> + <owner>wenyufu@chromium.org</owner> + <owner>chrome-connective-tissue@google.com</owner> + <summary> + When a CCT session is launched, if it is launch with the same Uri and same + taskId, record the duration between previous CCT closure and current CCT + launch. Recorded on CCT launch. + + A CCT can be defined as retainable if: 1) it is launching with the same Uri + as the most recently closed CCT; 2) it is launched from the same embedded + app as the most recently closed CCT; 3) the most recently closed CCT had + user interactions. See "CustomTabs.HadInteractionOnClose" for + information on interactions. + + The histogram suffix identifies whether the CCT is created with the same + intent Uri data and for the same embedded app, launching CCT with the same + taskId and package name. The package name identifier to use will depend on + whether the client app is using CustomTabService. If app is using the CCT + service, the package name will be read from CCT service; otherwise the + package name is read from the referrer of CCT activity. + + This histogram is recorded for {IdentifierType} + </summary> + <token key="IdentifierType"> + <variant name=".Different" + summary="App is not launched from the same embedded app as package + name / referrer is different."/> + <variant name=".Mixed" + summary="Apps not consistently using CCT services. Referrer and + package are used together."/> + <variant name=".PackageName" + summary="Apps using CCT services. Package name is used as identifier."/> + <variant name=".Referrer" + summary="Apps not connecting with CCT services. Activity referrer is + used as identifier."/> + </token> +</histogram> + <histogram name="CustomTabs.ShareOptionLocation" enum="ShareOptionLocation" expires_after="M109"> <owner>sophey@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/service/histograms.xml b/tools/metrics/histograms/metadata/service/histograms.xml index 000d593..6550d7c 100644 --- a/tools/metrics/histograms/metadata/service/histograms.xml +++ b/tools/metrics/histograms/metadata/service/histograms.xml
@@ -1067,7 +1067,7 @@ </histogram> <histogram name="ServiceWorkerCache.Cache.Browser.Match.RelatedFetchEvent" - units="ms" expires_after="2022-08-01"> + units="ms" expires_after="2023-06-24"> <owner>wanderview@chromium.org</owner> <owner>chrome-owp-storage@google.com</owner> <summary>
diff --git a/tools/metrics/histograms/metadata/web_core/histograms.xml b/tools/metrics/histograms/metadata/web_core/histograms.xml index 3bdeb75..07b1d142e 100644 --- a/tools/metrics/histograms/metadata/web_core/histograms.xml +++ b/tools/metrics/histograms/metadata/web_core/histograms.xml
@@ -183,9 +183,8 @@ expires_after="never"> <!-- expires-never: indexeddb heartbeat metric; used for chirp alerts (go/chrome-indexeddb-heartbeat) --> - <owner>dmurph@chromium.org</owner> - <owner>pwnall@chromium.org</owner> - <owner>storage-dev@chromium.org</owner> + <owner>ayui@chromium.org</owner> + <owner>chrome-owp-storage@google.com</owner> <summary> Records actions that take place in IndexedDB. These stats are used for normalization in formulas. See go/chrome-indexeddb-heartbeat @@ -193,10 +192,9 @@ </histogram> <histogram name="WebCore.IndexedDB.BackingStore.ConsistencyError" - enum="IDBLevelDBBackingStoreInternalErrorType" expires_after="2022-08-01"> - <owner>dmurph@chromium.org</owner> - <owner>pwnall@chromium.org</owner> - <owner>storage-dev@chromium.org</owner> + enum="IDBLevelDBBackingStoreInternalErrorType" expires_after="2023-06-24"> + <owner>ayui@chromium.org</owner> + <owner>chrome-owp-storage@google.com</owner> <summary> Methods that encountered consistency errors. Such errors probably point to a bug in our code. @@ -207,9 +205,8 @@ enum="LevelDBStatus" expires_after="never"> <!-- expires-never: indexeddb heartbeat metric; used for chirp alerts (go/chrome-indexeddb-heartbeat) --> - <owner>dmurph@chromium.org</owner> - <owner>pwnall@chromium.org</owner> - <owner>storage-dev@chromium.org</owner> + <owner>ayui@chromium.org</owner> + <owner>chrome-owp-storage@google.com</owner> <summary> Records the result of trying to delete an IndexedDB database, per the user using the IDBFactory::DeleteDatabase API. @@ -220,9 +217,8 @@ enum="LevelDBStatus" expires_after="never"> <!-- expires-never: indexeddb heartbeat metric; used for chirp alerts (go/chrome-indexeddb-heartbeat) --> - <owner>dmurph@chromium.org</owner> - <owner>pwnall@chromium.org</owner> - <owner>storage-dev@chromium.org</owner> + <owner>ayui@chromium.org</owner> + <owner>chrome-owp-storage@google.com</owner> <summary> Records the result of the FIRST attempt at opening the backing store for IndexedDB (i.e. retries are ignored). This is recorded when an API call @@ -233,10 +229,9 @@ </histogram> <histogram name="WebCore.IndexedDB.BackingStore.OpenFirstTrySuccessTime" - units="ms" expires_after="2022-08-01"> - <owner>dmurph@chromium.org</owner> - <owner>pwnall@chromium.org</owner> - <owner>storage-dev@chromium.org</owner> + units="ms" expires_after="2023-06-24"> + <owner>ayui@chromium.org</owner> + <owner>chrome-owp-storage@google.com</owner> <summary> Records the time that it takes to open IndexedDB's backing store. This is only recorded if the backing store was opened successfully on the first @@ -250,9 +245,8 @@ <!-- expires-never: indexeddb heartbeat metric; used for chirp alerts (go/chrome-indexeddb-heartbeat) --> - <owner>dmurph@chromium.org</owner> - <owner>pwnall@chromium.org</owner> - <owner>storage-dev@chromium.org</owner> + <owner>ayui@chromium.org</owner> + <owner>chrome-owp-storage@google.com</owner> <summary> Count of the different success and failure modes when opening an IndexedDB backing store - clean open, successful open with recovery, failed recovery, @@ -264,9 +258,8 @@ enum="IDBLevelDBBackingStoreOpenResult" expires_after="never"> <!-- expires-never: core storage metric; consumed in separate dashboard (go/chrome-storage-dashboard) --> - <owner>dmurph@chromium.org</owner> - <owner>pwnall@chromium.org</owner> - <owner>storage-dev@chromium.org</owner> + <owner>ayui@chromium.org</owner> + <owner>chrome-owp-storage@google.com</owner> <summary> Count of the different success and failure modes when opening an IndexedDB backing store - clean open, successful open with recovery, failed recovery, @@ -275,10 +268,9 @@ </histogram> <histogram name="WebCore.IndexedDB.BackingStore.OverlyLargeOriginLength" - units="characters" expires_after="2022-08-01"> - <owner>dmurph@chromium.org</owner> - <owner>pwnall@chromium.org</owner> - <owner>storage-dev@chromium.org</owner> + units="characters" expires_after="2023-06-24"> + <owner>ayui@chromium.org</owner> + <owner>chrome-owp-storage@google.com</owner> <summary> Length of leveldb directories that cause paths to not fit in the filesystem, either because the individual component is too long or the overall path is @@ -287,37 +279,34 @@ </histogram> <histogram name="WebCore.IndexedDB.BackingStore.ReadError" - enum="IDBLevelDBBackingStoreInternalErrorType" expires_after="2022-08-01"> - <owner>dmurph@chromium.org</owner> - <owner>pwnall@chromium.org</owner> - <owner>storage-dev@chromium.org</owner> + enum="IDBLevelDBBackingStoreInternalErrorType" expires_after="2023-06-24"> + <owner>ayui@chromium.org</owner> + <owner>chrome-owp-storage@google.com</owner> <summary> Methods that encountered leveldb errors while trying to read from disk. </summary> </histogram> <histogram name="WebCore.IndexedDB.BackingStore.WriteError" - enum="IDBLevelDBBackingStoreInternalErrorType" expires_after="2022-08-01"> - <owner>dmurph@chromium.org</owner> - <owner>pwnall@chromium.org</owner> - <owner>storage-dev@chromium.org</owner> + enum="IDBLevelDBBackingStoreInternalErrorType" expires_after="2023-06-24"> + <owner>ayui@chromium.org</owner> + <owner>chrome-owp-storage@google.com</owner> <summary> Methods that encountered leveldb errors while trying to write to disk. </summary> </histogram> <histogram name="WebCore.IndexedDB.Context.ForceCloseReason" - enum="IDBContextForcedCloseReason" expires_after="2022-08-01"> - <owner>cmumford@chromium.org</owner> - <owner>storage-dev@chromium.org</owner> + enum="IDBContextForcedCloseReason" expires_after="2023-06-24"> + <owner>ayui@chromium.org</owner> + <owner>chrome-owp-storage@google.com</owner> <summary>The reason that a forced-close of a backing store occurred.</summary> </histogram> <histogram name="WebCore.IndexedDB.ErrorDuringForceCloseAborts" - enum="LevelDBStatus" expires_after="2022-08-01"> - <owner>dmurph@chromium.org</owner> - <owner>pwnall@chromium.org</owner> - <owner>storage-dev@chromium.org</owner> + enum="LevelDBStatus" expires_after="2023-06-24"> + <owner>ayui@chromium.org</owner> + <owner>chrome-owp-storage@google.com</owner> <summary> Recorded when there is an error during the force close of IndexedDB for an origin. A force close can be triggered either from DevTools, or when there @@ -328,10 +317,9 @@ </histogram> <histogram name="WebCore.IndexedDB.FoundBlobFileForValue" enum="Boolean" - expires_after="2022-12-04"> - <owner>enne@chromium.org</owner> - <owner>mek@chromium.org</owner> - <owner>storage-dev@chromium.org</owner> + expires_after="2023-06-24"> + <owner>ayui@chromium.org</owner> + <owner>chrome-owp-storage@google.com</owner> <summary> Recorded when a blob is attempted to be read from an IndexedDB value. This is triggered for both implicit blob-wrapped large values and explicit blob @@ -342,27 +330,27 @@ </histogram> <histogram name="WebCore.IndexedDB.LevelDB.CloseTime" units="ms" - expires_after="2022-08-01"> - <owner>cmumford@chromium.org</owner> - <owner>storage-dev@chromium.org</owner> + expires_after="2023-06-24"> + <owner>ayui@chromium.org</owner> + <owner>chrome-owp-storage@google.com</owner> <summary> The time that it takes to close IndexedDB's LevelDB backing store. </summary> </histogram> <histogram name="WebCore.IndexedDB.LevelDB.OpenTime" units="ms" - expires_after="2022-08-01"> - <owner>cmumford@chromium.org</owner> - <owner>storage-dev@chromium.org</owner> + expires_after="2023-06-24"> + <owner>ayui@chromium.org</owner> + <owner>chrome-owp-storage@google.com</owner> <summary> The time that it takes to open IndexedDB's LevelDB backing store. </summary> </histogram> <histogram name="WebCore.IndexedDB.LevelDB.WriteTime" units="ms" - expires_after="2022-08-01"> - <owner>cmumford@chromium.org</owner> - <owner>storage-dev@chromium.org</owner> + expires_after="2023-06-24"> + <owner>ayui@chromium.org</owner> + <owner>chrome-owp-storage@google.com</owner> <summary> The time that it takes to write data to an IndexedDB's LevelDB backing store. @@ -370,20 +358,18 @@ </histogram> <histogram name="WebCore.IndexedDB.LevelDBOpenErrors" enum="LevelDBErrorTypes" - expires_after="2022-08-01"> - <owner>dmurph@chromium.org</owner> - <owner>pwnall@chromium.org</owner> - <owner>storage-dev@chromium.org</owner> + expires_after="2023-06-24"> + <owner>ayui@chromium.org</owner> + <owner>chrome-owp-storage@google.com</owner> <summary> Error classes returned by LevelDB when it failed to open a database. </summary> </histogram> <histogram name="WebCore.IndexedDB.LevelDBOpenErrors.BFE" - enum="PlatformFileError" expires_after="2022-08-01"> - <owner>dmurph@chromium.org</owner> - <owner>pwnall@chromium.org</owner> - <owner>storage-dev@chromium.org</owner> + enum="PlatformFileError" expires_after="2023-06-24"> + <owner>ayui@chromium.org</owner> + <owner>chrome-owp-storage@google.com</owner> <summary> Errors (base::File::Error) encountered by a single LevelDBEnv method when opening an IndexedDB instance. @@ -391,40 +377,36 @@ </histogram> <histogram name="WebCore.IndexedDB.LevelDBOpenErrors.Corruption" - enum="LevelDBCorruptionTypes" expires_after="2022-08-01"> - <owner>dmurph@chromium.org</owner> - <owner>pwnall@chromium.org</owner> - <owner>storage-dev@chromium.org</owner> + enum="LevelDBCorruptionTypes" expires_after="2023-06-24"> + <owner>ayui@chromium.org</owner> + <owner>chrome-owp-storage@google.com</owner> <summary> Types of corruption that LevelDB encounters when opening a database. </summary> </histogram> <histogram name="WebCore.IndexedDB.LevelDBOpenErrors.EnvMethod" - enum="LevelDBIOErrorMethods" expires_after="2022-08-01"> - <owner>dmurph@chromium.org</owner> - <owner>pwnall@chromium.org</owner> - <owner>storage-dev@chromium.org</owner> + enum="LevelDBIOErrorMethods" expires_after="2023-06-24"> + <owner>ayui@chromium.org</owner> + <owner>chrome-owp-storage@google.com</owner> <summary> LevelDBEnv methods that generated IO errors when opening a database. </summary> </histogram> <histogram name="WebCore.IndexedDB.LevelDBReadErrors" enum="LevelDBErrorTypes" - expires_after="2022-08-01"> - <owner>dmurph@chromium.org</owner> - <owner>pwnall@chromium.org</owner> - <owner>storage-dev@chromium.org</owner> + expires_after="2023-06-24"> + <owner>ayui@chromium.org</owner> + <owner>chrome-owp-storage@google.com</owner> <summary> Error classes returned by LevelDB when it failed to read a database. </summary> </histogram> <histogram name="WebCore.IndexedDB.LevelDBReadErrors.BFE" - enum="PlatformFileError" expires_after="2022-08-01"> - <owner>dmurph@chromium.org</owner> - <owner>pwnall@chromium.org</owner> - <owner>storage-dev@chromium.org</owner> + enum="PlatformFileError" expires_after="2023-06-24"> + <owner>ayui@chromium.org</owner> + <owner>chrome-owp-storage@google.com</owner> <summary> Errors (base::File::Error) encountered by a single LevelDBEnv method when reading from an IndexedDB instance. @@ -432,40 +414,36 @@ </histogram> <histogram name="WebCore.IndexedDB.LevelDBReadErrors.Corruption" - enum="LevelDBCorruptionTypes" expires_after="2022-08-01"> - <owner>dmurph@chromium.org</owner> - <owner>pwnall@chromium.org</owner> - <owner>storage-dev@chromium.org</owner> + enum="LevelDBCorruptionTypes" expires_after="2023-06-24"> + <owner>ayui@chromium.org</owner> + <owner>chrome-owp-storage@google.com</owner> <summary> Types of corruption that LevelDB encounters when reading a database. </summary> </histogram> <histogram name="WebCore.IndexedDB.LevelDBReadErrors.EnvMethod" - enum="LevelDBIOErrorMethods" expires_after="2022-08-01"> - <owner>dmurph@chromium.org</owner> - <owner>pwnall@chromium.org</owner> - <owner>storage-dev@chromium.org</owner> + enum="LevelDBIOErrorMethods" expires_after="2023-06-24"> + <owner>ayui@chromium.org</owner> + <owner>chrome-owp-storage@google.com</owner> <summary> LevelDBEnv methods that generated IO errors when reading a database. </summary> </histogram> <histogram name="WebCore.IndexedDB.LevelDBWriteErrors" enum="LevelDBErrorTypes" - expires_after="2022-08-01"> - <owner>dmurph@chromium.org</owner> - <owner>pwnall@chromium.org</owner> - <owner>storage-dev@chromium.org</owner> + expires_after="2023-06-24"> + <owner>ayui@chromium.org</owner> + <owner>chrome-owp-storage@google.com</owner> <summary> Error classes returned by LevelDB when it failed to write to a database. </summary> </histogram> <histogram name="WebCore.IndexedDB.LevelDBWriteErrors.BFE" - enum="PlatformFileError" expires_after="2022-08-01"> - <owner>dmurph@chromium.org</owner> - <owner>pwnall@chromium.org</owner> - <owner>storage-dev@chromium.org</owner> + enum="PlatformFileError" expires_after="2023-06-24"> + <owner>ayui@chromium.org</owner> + <owner>chrome-owp-storage@google.com</owner> <summary> Errors (base::File::Error) encountered by a single LevelDBEnv method when writing to an IndexedDB instance. @@ -473,10 +451,9 @@ </histogram> <histogram name="WebCore.IndexedDB.LevelDBWriteErrors.Corruption" - enum="LevelDBCorruptionTypes" expires_after="2022-08-01"> - <owner>dmurph@chromium.org</owner> - <owner>pwnall@chromium.org</owner> - <owner>storage-dev@chromium.org</owner> + enum="LevelDBCorruptionTypes" expires_after="2023-06-24"> + <owner>ayui@chromium.org</owner> + <owner>chrome-owp-storage@google.com</owner> <summary> Types of corruption returned by LevelDB when it failed to write to a database. @@ -484,20 +461,18 @@ </histogram> <histogram name="WebCore.IndexedDB.LevelDBWriteErrors.EnvMethod" - enum="LevelDBIOErrorMethods" expires_after="2022-08-01"> - <owner>dmurph@chromium.org</owner> - <owner>pwnall@chromium.org</owner> - <owner>storage-dev@chromium.org</owner> + enum="LevelDBIOErrorMethods" expires_after="2023-06-24"> + <owner>ayui@chromium.org</owner> + <owner>chrome-owp-storage@google.com</owner> <summary> LevelDBEnv methods that generated IO errors when writing to a database. </summary> </histogram> <histogram name="WebCore.IndexedDB.OpenTime.Cold" units="ms" - expires_after="2022-08-01"> - <owner>dmurph@chromium.org</owner> - <owner>pwnall@chromium.org</owner> - <owner>storage-dev@chromium.org</owner> + expires_after="2023-06-24"> + <owner>ayui@chromium.org</owner> + <owner>chrome-owp-storage@google.com</owner> <summary> Records the full time it takes to open a database that isn't open yet (so files are loaded from disk, metadata checked, etc). The measurement happens @@ -510,10 +485,9 @@ </histogram> <histogram name="WebCore.IndexedDB.OpenTime.Warm" units="ms" - expires_after="2022-08-01"> - <owner>dmurph@chromium.org</owner> - <owner>pwnall@chromium.org</owner> - <owner>storage-dev@chromium.org</owner> + expires_after="2023-06-24"> + <owner>ayui@chromium.org</owner> + <owner>chrome-owp-storage@google.com</owner> <summary> Records the full time it takes to open an already-open database. The measurement happens when a website calls "indexedDB.open", and @@ -525,10 +499,9 @@ </histogram> <histogram name="WebCore.IndexedDB.PutBlobsCount" units="blobs" - expires_after="2022-08-01"> - <owner>dmurph@chromium.org</owner> - <owner>pwnall@chromium.org</owner> - <owner>storage-dev@chromium.org</owner> + expires_after="2023-06-24"> + <owner>ayui@chromium.org</owner> + <owner>chrome-owp-storage@google.com</owner> <summary> The number of blobs being saved in an IndexedDB object store 'put' operation. Recorded for every 'put' operation. @@ -536,10 +509,9 @@ </histogram> <histogram name="WebCore.IndexedDB.PutBlobsTotalSize" units="KB" - expires_after="2022-08-01"> - <owner>dmurph@chromium.org</owner> - <owner>pwnall@chromium.org</owner> - <owner>storage-dev@chromium.org</owner> + expires_after="2023-06-24"> + <owner>ayui@chromium.org</owner> + <owner>chrome-owp-storage@google.com</owner> <summary> The total size of the blobs being saved in an IndexedDB object store 'put' operation. Recorded for every 'put' operation, except when there are no @@ -548,10 +520,9 @@ </histogram> <histogram name="WebCore.IndexedDB.PutKeySize" units="KB" - expires_after="2022-08-01"> - <owner>dmurph@chromium.org</owner> - <owner>pwnall@chromium.org</owner> - <owner>storage-dev@chromium.org</owner> + expires_after="2023-06-24"> + <owner>ayui@chromium.org</owner> + <owner>chrome-owp-storage@google.com</owner> <summary> The size of the IndexedDB key used in an IndexedDB object store 'put' operation. Recorded for every 'put' operation. @@ -559,10 +530,9 @@ </histogram> <histogram name="WebCore.IndexedDB.PutValueSize2" units="KB" - expires_after="2022-08-01"> - <owner>dmurph@chromium.org</owner> - <owner>pwnall@chromium.org</owner> - <owner>storage-dev@chromium.org</owner> + expires_after="2023-06-24"> + <owner>ayui@chromium.org</owner> + <owner>chrome-owp-storage@google.com</owner> <summary> The size of the IndexedDB value used in an IndexedDB object store 'put' operation. Recorded for every 'put' operation. @@ -570,10 +540,9 @@ </histogram> <histogram name="WebCore.IndexedDB.TombstoneSweeper.DeletedTombstonesSize" - units="bytes" expires_after="2022-08-01"> - <owner>dmurph@chromium.org</owner> - <owner>pwnall@chromium.org</owner> - <owner>storage-dev@chromium.org</owner> + units="bytes" expires_after="2023-06-24"> + <owner>ayui@chromium.org</owner> + <owner>chrome-owp-storage@google.com</owner> <summary> Records the total size of tombstones deleted by the IndexedDB Tombstone Sweeper. Recorded on the browser side (back end) when the sweeper has @@ -584,10 +553,9 @@ <histogram name="WebCore.IndexedDB.TombstoneSweeper.DeletionCommitTime.Complete" - units="ms" expires_after="2022-08-01"> - <owner>dmurph@chromium.org</owner> - <owner>pwnall@chromium.org</owner> - <owner>storage-dev@chromium.org</owner> + units="ms" expires_after="2023-06-24"> + <owner>ayui@chromium.org</owner> + <owner>chrome-owp-storage@google.com</owner> <summary> Records the time it takes for the IndexedDB Tombstone Sweeper to commit tombstone deletions. Recorded on the browser side (back end) when the @@ -597,10 +565,9 @@ </histogram> <histogram name="WebCore.IndexedDB.TombstoneSweeper.DeletionTotalTime.Complete" - units="ms" expires_after="2022-08-01"> - <owner>dmurph@chromium.org</owner> - <owner>pwnall@chromium.org</owner> - <owner>storage-dev@chromium.org</owner> + units="ms" expires_after="2023-06-24"> + <owner>ayui@chromium.org</owner> + <owner>chrome-owp-storage@google.com</owner> <summary> Records the time it takes for the IndexedDB Tombstone Sweeper to fully sweep the indexes. Recorded on the browser side (back end) when the sweeper has @@ -610,10 +577,9 @@ </histogram> <histogram name="WebCore.IndexedDB.TombstoneSweeper.DeletionWriteError" - enum="LevelDBStatus" expires_after="2022-08-01"> - <owner>dmurph@chromium.org</owner> - <owner>pwnall@chromium.org</owner> - <owner>storage-dev@chromium.org</owner> + enum="LevelDBStatus" expires_after="2023-06-24"> + <owner>ayui@chromium.org</owner> + <owner>chrome-owp-storage@google.com</owner> <summary> Records when an error occurs during deletion of index tombstones by the IndexedDB Tombstone Sweeper. Recorded on the browser side (back end) when @@ -623,10 +589,9 @@ </histogram> <histogram name="WebCore.IndexedDB.TombstoneSweeper.IndexScanPercent" - units="%/5" expires_after="2022-08-01"> - <owner>dmurph@chromium.org</owner> - <owner>pwnall@chromium.org</owner> - <owner>storage-dev@chromium.org</owner> + units="%/5" expires_after="2023-06-24"> + <owner>ayui@chromium.org</owner> + <owner>chrome-owp-storage@google.com</owner> <summary> Recorded on the browser side (back end) when the IndexedDB Tombstone Sweeper has completed scanning. Records the percentage of the indexes the scanner @@ -636,10 +601,9 @@ </histogram> <histogram name="WebCore.IndexedDB.TombstoneSweeper.NumDeletedTombstones" - units="Index Tombstones" expires_after="2022-08-01"> - <owner>dmurph@chromium.org</owner> - <owner>pwnall@chromium.org</owner> - <owner>storage-dev@chromium.org</owner> + units="Index Tombstones" expires_after="2023-06-24"> + <owner>ayui@chromium.org</owner> + <owner>chrome-owp-storage@google.com</owner> <summary> Records the number of tombstones deleted by the IndexedDB Tombstone Sweeper. Recorded on the browser side (back end) when the sweeper has completed @@ -648,10 +612,9 @@ </histogram> <histogram name="WebCore.IndexedDB.TombstoneSweeper.SweepError" - enum="LevelDBStatus" expires_after="2022-08-01"> - <owner>dmurph@chromium.org</owner> - <owner>pwnall@chromium.org</owner> - <owner>storage-dev@chromium.org</owner> + enum="LevelDBStatus" expires_after="2023-06-24"> + <owner>ayui@chromium.org</owner> + <owner>chrome-owp-storage@google.com</owner> <summary> Recorded on the browser side (back end) when the IndexedDB Tombstone Sweeper encounters an error while sweeping. See https://goo.gl/coKwA7. @@ -659,10 +622,9 @@ </histogram> <histogram name="WebCore.IndexedDB.Transaction.ReadOnly.SizeOnCommit2" - units="KB" expires_after="2022-08-01"> - <owner>dmurph@chromium.org</owner> - <owner>pwnall@chromium.org</owner> - <owner>storage-dev@chromium.org</owner> + units="KB" expires_after="2023-06-24"> + <owner>ayui@chromium.org</owner> + <owner>chrome-owp-storage@google.com</owner> <summary> The total temporary size of an IndexedDB ReadOnly Transaction. Since this is a readonly transaction, the size should only be >0 when the transaction @@ -671,10 +633,9 @@ </histogram> <histogram name="WebCore.IndexedDB.Transaction.ReadOnly.TimeActive" units="ms" - expires_after="2022-08-01"> - <owner>dmurph@chromium.org</owner> - <owner>pwnall@chromium.org</owner> - <owner>storage-dev@chromium.org</owner> + expires_after="2023-06-24"> + <owner>ayui@chromium.org</owner> + <owner>chrome-owp-storage@google.com</owner> <summary> The time it takes for an IndexedDB ReadOnly Transaction to commit, starting from when it starts executing tasks (when it is scheduled). Recorded on @@ -683,10 +644,9 @@ </histogram> <histogram name="WebCore.IndexedDB.Transaction.ReadWrite.SizeOnCommit2" - units="KB" expires_after="2022-08-01"> - <owner>dmurph@chromium.org</owner> - <owner>pwnall@chromium.org</owner> - <owner>storage-dev@chromium.org</owner> + units="KB" expires_after="2023-06-24"> + <owner>ayui@chromium.org</owner> + <owner>chrome-owp-storage@google.com</owner> <summary> The total temporary size of an IndexedDB ReadWrite Transaction. This is the memory that is temporarily stored before writing to disk. Recorded on @@ -695,10 +655,9 @@ </histogram> <histogram name="WebCore.IndexedDB.Transaction.ReadWrite.TimeActive" units="ms" - expires_after="2022-08-01"> - <owner>dmurph@chromium.org</owner> - <owner>pwnall@chromium.org</owner> - <owner>storage-dev@chromium.org</owner> + expires_after="2023-06-24"> + <owner>ayui@chromium.org</owner> + <owner>chrome-owp-storage@google.com</owner> <summary> The time it takes for an IndexedDB ReadWrite Transaction to commit, starting from when it starts executing tasks (when it is scheduled). Recorded on @@ -707,10 +666,9 @@ </histogram> <histogram name="WebCore.IndexedDB.Transaction.VersionChange.SizeOnCommit2" - units="KB" expires_after="2022-08-01"> - <owner>dmurph@chromium.org</owner> - <owner>pwnall@chromium.org</owner> - <owner>storage-dev@chromium.org</owner> + units="KB" expires_after="2023-06-24"> + <owner>ayui@chromium.org</owner> + <owner>chrome-owp-storage@google.com</owner> <summary> The total temporary size of an IndexedDB VersionChange Transaction. This is the memory that is temporarily stored before writing to disk. Version change @@ -720,10 +678,9 @@ </histogram> <histogram name="WebCore.IndexedDB.Transaction.VersionChange.TimeActive" - units="ms" expires_after="2022-08-01"> - <owner>dmurph@chromium.org</owner> - <owner>pwnall@chromium.org</owner> - <owner>storage-dev@chromium.org</owner> + units="ms" expires_after="2023-06-24"> + <owner>ayui@chromium.org</owner> + <owner>chrome-owp-storage@google.com</owner> <summary> The time it takes for an IndexedDB VersionChange Transaction to commit, starting from when it starts executing tasks (when it is scheduled). Version @@ -733,10 +690,9 @@ </histogram> <histogram name="WebCore.IndexedDB.TransactionAbortReason" enum="IDBException" - expires_after="2022-08-01"> - <owner>dmurph@chromium.org</owner> - <owner>pwnall@chromium.org</owner> - <owner>storage-dev@chromium.org</owner> + expires_after="2023-06-24"> + <owner>ayui@chromium.org</owner> + <owner>chrome-owp-storage@google.com</owner> <summary> Recorded on the browser side (back end) when an IndexedDB transaction is aborted, specifically recording the reason for the abort. This can be
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json index fe7decc..2129c70 100644 --- a/tools/perf/core/perfetto_binary_roller/binary_deps.json +++ b/tools/perf/core/perfetto_binary_roller/binary_deps.json
@@ -6,7 +6,7 @@ }, "win": { "hash": "adcf9f60e35908138ad440207ded004627ccf43a", - "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/010e6c1867a689266316ad607725cfd8ed11d3f9/trace_processor_shell.exe" + "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/2a59c7427cfd780bcb98dfad01772b44468edc7a/trace_processor_shell.exe" }, "linux_arm": { "hash": "58893933be305d3bfe0a72ebebcacde2ac3ca893", @@ -21,8 +21,8 @@ "full_remote_path": "perfetto-luci-artifacts/v25.0/mac-arm64/trace_processor_shell" }, "linux": { - "hash": "cd36bf5e5151252ac1b88bf8fc029c06ef2e8570", - "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/010e6c1867a689266316ad607725cfd8ed11d3f9/trace_processor_shell" + "hash": "25e9a9272f3b9bbacc96a3ae6b57f93ea0566549", + "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/048f619e850f327e1e6a9786680bebe582e4279a/trace_processor_shell" } }, "power_profile.sql": {
diff --git a/tools/traffic_annotation/summary/annotations.xml b/tools/traffic_annotation/summary/annotations.xml index ea0a214f..0749192 100644 --- a/tools/traffic_annotation/summary/annotations.xml +++ b/tools/traffic_annotation/summary/annotations.xml
@@ -365,7 +365,7 @@ <item id="help_content_provider" added_in_milestone="102" content_hash_code="0839311f" os_list="chromeos" file_path="ash/webui/os_feedback_ui/backend/help_content_provider.cc" /> <item id="managed_acccount_signin_restrictions_secure_connect_chromeos" added_in_milestone="102" content_hash_code="068bc93d" os_list="chromeos" file_path="chrome/browser/ui/webui/signin/user_cloud_signin_restriction_policy_fetcher_chromeos.cc" /> <item id="wallpaper_download_google_photo" added_in_milestone="102" content_hash_code="034be7b5" os_list="chromeos" file_path="ash/wallpaper/wallpaper_controller_impl.cc" /> - <item id="quick_answers_spellchecker" added_in_milestone="102" content_hash_code="06e81b7f" os_list="chromeos" file_path="chromeos/components/quick_answers/utils/spell_checker.cc" /> + <item id="quick_answers_spellchecker" added_in_milestone="102" content_hash_code="06e81b7f" os_list="chromeos" file_path="chromeos/components/quick_answers/utils/spell_check_language.cc" /> <item id="printing_oauth2_token_exchange_request" added_in_milestone="102" type="partial" second_id="printing_oauth2_http_exchange" content_hash_code="059df373" os_list="chromeos" semantics_fields="2,4" file_path="chrome/browser/ash/printing/oauth2/ipp_endpoint_token_fetcher.cc" /> <item id="fedcm_account_profile_image_fetcher" added_in_milestone="102" content_hash_code="07d6a3e2" os_list="linux,windows,chromeos" file_path="chrome/browser/ui/views/webid/account_selection_bubble_view.cc" /> <item id="device_activity_client_health_check" added_in_milestone="102" content_hash_code="0506113c" os_list="chromeos" file_path="ash/components/device_activity/device_activity_client.cc" />
diff --git a/tools/visual_debugger/frame.js b/tools/visual_debugger/frame.js index 1894d12d..53bf1b2 100644 --- a/tools/visual_debugger/frame.js +++ b/tools/visual_debugger/frame.js
@@ -59,19 +59,24 @@ (this.submissionCount() - 1); } - updateCanvasOrientationAndSize(canvas, orientationDeg, scale) { + updateCanvasOrientation(canvas, orientationDeg) { // Swap canvas width/height for 90 or 270 deg rotations if (orientationDeg === 90 || orientationDeg === 270) { - canvas.width = this.size_.height * scale; - canvas.height = this.size_.width * scale; + canvas.width = this.size_.height; + canvas.height = this.size_.width; } // Restore original canvas width/height for 0 or 180 deg rotations else { - canvas.width = this.size_.width * scale; - canvas.height = this.size_.height * scale; + canvas.width = this.size_.width; + canvas.height = this.size_.height; } } + updateCanvasSize(canvas, scale) { + canvas.width *= scale; + canvas.height *= scale; + } + getFilter(source_index) { const filters = Filter.enabledInstances(); let filter = undefined; @@ -266,11 +271,10 @@ redrawCurrentFrame_() { const frame = this.getCurrentFrame(); if (!frame) return; - frame.updateCanvasOrientationAndSize(this.canvas_, - this.viewOrientation, this.viewScale); + frame.updateCanvasOrientation(this.canvas_, this.viewOrientation); + frame.updateCanvasSize(this.canvas_, this.viewScale); this.updateTransformMatrix(this.viewOrientation); // this.drawContext_.translate(this.translationX, this.translationY); - frame.updateCanvasSize(this.canvas_, this.viewScale); frame.draw(this.canvas_, this.drawContext_, this.viewScale, this.viewOrientation, this.transformMatrix);
diff --git a/ui/android/BUILD.gn b/ui/android/BUILD.gn index f5f8223..8cdcb5e 100644 --- a/ui/android/BUILD.gn +++ b/ui/android/BUILD.gn
@@ -328,6 +328,7 @@ "java/src/org/chromium/ui/resources/dynamics/BitmapDynamicResource.java", "java/src/org/chromium/ui/resources/dynamics/DynamicResource.java", "java/src/org/chromium/ui/resources/dynamics/DynamicResourceLoader.java", + "java/src/org/chromium/ui/resources/dynamics/DynamicResourceSnapshot.java", "java/src/org/chromium/ui/resources/dynamics/ViewResourceAdapter.java", "java/src/org/chromium/ui/resources/dynamics/ViewResourceInflater.java", "java/src/org/chromium/ui/resources/statics/NinePatchData.java", @@ -405,6 +406,7 @@ robolectric_library("ui_junit_test_support") { sources = [ + "junit/src/org/chromium/ui/resources/dynamics/DynamicResourceTestUtils.java", "junit/src/org/chromium/ui/shadows/ShadowAnimatedStateListDrawable.java", "junit/src/org/chromium/ui/shadows/ShadowAppCompatResources.java", "junit/src/org/chromium/ui/shadows/ShadowAsyncLayoutInflater.java", @@ -416,6 +418,7 @@ "//third_party/androidx:androidx_annotation_annotation_java", "//third_party/androidx:androidx_appcompat_appcompat_resources_java", "//third_party/androidx:androidx_asynclayoutinflater_asynclayoutinflater_java", + "//third_party/junit", ] } @@ -504,6 +507,7 @@ "junit/src/org/chromium/ui/widget/ViewRectProviderTest.java", ] deps = [ + ":ui_android_jni_headers", ":ui_java", ":ui_java_test_support", ":ui_javatest_resources",
diff --git a/ui/android/java/src/org/chromium/ui/resources/ResourceFactory.java b/ui/android/java/src/org/chromium/ui/resources/ResourceFactory.java index cd917996..ff0db447 100644 --- a/ui/android/java/src/org/chromium/ui/resources/ResourceFactory.java +++ b/ui/android/java/src/org/chromium/ui/resources/ResourceFactory.java
@@ -28,7 +28,7 @@ } @NativeMethods - interface Natives { + public interface Natives { long createBitmapResource(); long createNinePatchBitmapResource(int paddingLeft, int paddingTop, int paddingRight, int paddingBottom, int apertureLeft, int apertureTop, int apertureRight,
diff --git a/ui/android/java/src/org/chromium/ui/resources/ResourceManager.java b/ui/android/java/src/org/chromium/ui/resources/ResourceManager.java index fa5f51a..a9cd903 100644 --- a/ui/android/java/src/org/chromium/ui/resources/ResourceManager.java +++ b/ui/android/java/src/org/chromium/ui/resources/ResourceManager.java
@@ -17,7 +17,6 @@ import org.chromium.ui.display.DisplayAndroid; import org.chromium.ui.resources.ResourceLoader.ResourceLoaderCallback; import org.chromium.ui.resources.dynamics.BitmapDynamicResource; -import org.chromium.ui.resources.dynamics.DynamicResource; import org.chromium.ui.resources.dynamics.DynamicResourceLoader; import org.chromium.ui.resources.statics.StaticResourceLoader; import org.chromium.ui.resources.system.SystemResourceLoader; @@ -80,7 +79,7 @@ /** * @return A reference to the {@link DynamicResourceLoader} that provides - * {@link DynamicResource} objects to this class. + * {@link Resource} objects to this class. */ public DynamicResourceLoader getDynamicResourceLoader() { return (DynamicResourceLoader) mResourceLoaders.get(
diff --git a/ui/android/java/src/org/chromium/ui/resources/dynamics/BitmapDynamicResource.java b/ui/android/java/src/org/chromium/ui/resources/dynamics/BitmapDynamicResource.java index 8decc17..527c43e 100644 --- a/ui/android/java/src/org/chromium/ui/resources/dynamics/BitmapDynamicResource.java +++ b/ui/android/java/src/org/chromium/ui/resources/dynamics/BitmapDynamicResource.java
@@ -7,17 +7,22 @@ import android.graphics.Bitmap; import android.graphics.Rect; +import androidx.annotation.Nullable; + +import org.chromium.base.Callback; +import org.chromium.ui.resources.Resource; import org.chromium.ui.resources.ResourceFactory; -import org.chromium.ui.resources.statics.NinePatchData; /** * A basic implementation of {@link DynamicResource} to handle updatable bitmaps. */ -public class BitmapDynamicResource extends DynamicResource { +public class BitmapDynamicResource implements DynamicResource { private final int mResId; private Bitmap mBitmap; private final Rect mSize = new Rect(); - private boolean mIsDirty = true; + + @Nullable + private Callback<Resource> mOnResourceReady; public BitmapDynamicResource(int resourceId) { mResId = resourceId; @@ -37,38 +42,22 @@ // Not updating bitmap is still bad, but better than a crash. We will still crash if there // is no bitmap to start with. See http://crbug.com/471234 for more. if (bitmap == null) return; - mIsDirty = true; mBitmap = bitmap; mSize.set(0, 0, mBitmap.getWidth(), mBitmap.getHeight()); } @Override - public Bitmap getBitmap() { - super.getBitmap(); - assert mBitmap != null: "setBitmap() should be called before calling getBitmap() again"; - mIsDirty = false; - Bitmap bitmap = mBitmap; - mBitmap = null; - return bitmap; + public void onResourceRequested() { + if (mOnResourceReady != null && mBitmap != null) { + Resource resource = new DynamicResourceSnapshot( + mBitmap, false, mSize, ResourceFactory.createBitmapResource(null)); + mOnResourceReady.onResult(resource); + mBitmap = null; + } } @Override - public Rect getBitmapSize() { - return mSize; - } - - @Override - public long createNativeResource() { - return ResourceFactory.createBitmapResource(null); - } - - @Override - public NinePatchData getNinePatchData() { - return null; - } - - @Override - public boolean isDirty() { - return mIsDirty; + public void setOnResourceReadyCallback(Callback<Resource> onResourceReady) { + mOnResourceReady = onResourceReady; } }
diff --git a/ui/android/java/src/org/chromium/ui/resources/dynamics/DynamicResource.java b/ui/android/java/src/org/chromium/ui/resources/dynamics/DynamicResource.java index bcd9a62..4bd7f1a 100644 --- a/ui/android/java/src/org/chromium/ui/resources/dynamics/DynamicResource.java +++ b/ui/android/java/src/org/chromium/ui/resources/dynamics/DynamicResource.java
@@ -4,46 +4,27 @@ package org.chromium.ui.resources.dynamics; -import android.graphics.Bitmap; - -import androidx.annotation.CallSuper; - +import org.chromium.base.Callback; import org.chromium.ui.resources.Resource; -import org.chromium.ui.resources.ResourceLoader.ResourceLoaderCallback; /** - * A representation of a dynamic resource. The contents of the resource might change from frame to - * frame. + * A representation of a dynamic resource. The contents may change from frame to frame. It should be + * be able to return a {@link Resource} version of itself asynchronously. The + * {@link DynamicResource} is in charge of tracking when it has changed and should actually be + * returning a copy of itself. */ -public abstract class DynamicResource implements Resource { +public interface DynamicResource { /** - * {@link DynamicResourceLoader#loadResource(int)} only notifies {@link ResourceLoaderCallback} - * if the resource is dirty. Therefore, if the resource is not dirty, this should not be called. - * @return null. This method should be overridden, and ignore the return value here. + * Will be called every render frame to notify the resource. The expectation is that this call + * will happen a lot, but only needs to be responded to when the dynamic resource has had a + * change that would cause the resulting resource to be different in some way, typically a + * change in the return of {@link Resource#getBitmap()}. */ - @Override - @CallSuper - public Bitmap getBitmap() { - assert isDirty() : "getBitmap() should not be called when not dirty"; - return null; - } - - @Override - public boolean shouldRemoveResourceOnNullBitmap() { - return false; - } + void onResourceRequested(); /** - * Note that this is called for every access to the resource during a frame. If a resource is - * dirty, it should not be dirty again during the same looper call. - * {@link DynamicResourceLoader#loadResource(int)} only notifies - * {@link ResourceLoaderCallback#onResourceLoaded} if the resource is dirty. - * Therefore, if the resource is not dirty, {@link #getBitmap()} doesn't get called. - * - * TODO(dtrainor): Add checks so that a dynamic resource **can't** be built more than once each - * frame. - * - * @return Whether or not this resource is dirty and the CC component should be rebuilt. + * Sets the way this dynamic resource will use to return the resource that is ready to be used + * and drawn. */ - abstract boolean isDirty(); -} + void setOnResourceReadyCallback(Callback<Resource> onResourceReady); +} \ No newline at end of file
diff --git a/ui/android/java/src/org/chromium/ui/resources/dynamics/DynamicResourceLoader.java b/ui/android/java/src/org/chromium/ui/resources/dynamics/DynamicResourceLoader.java index 4e72462..493d1bd1 100644 --- a/ui/android/java/src/org/chromium/ui/resources/dynamics/DynamicResourceLoader.java +++ b/ui/android/java/src/org/chromium/ui/resources/dynamics/DynamicResourceLoader.java
@@ -9,8 +9,10 @@ import org.chromium.ui.resources.ResourceLoader; /** - * Handles managing dynamic resources. This is basically a list of dynamic resources that checks - * {@link DynamicResource#isDirty()} before notifying the {@link ResourceLoaderCallback}. + * Handles managing dynamic resources. Because {@link DynamicResource} decide when they are dirty + * and should return a loaded resource, this class mostly just passes through notifications when + * render frames are happening, and hands the captured {@link org.chromium.ui.resources.Resource} + * back in our {@link ResourceLoaderCallback}. */ public class DynamicResourceLoader extends ResourceLoader { private final SparseArray<DynamicResource> mDynamicResources = @@ -29,12 +31,14 @@ /** * Registers a {@link DynamicResource} to be tracked and exposed by this class. - * @param resId The Android id to use. This should be an actual Android id (R.id.some_id). - * @param resource The {@link DynamicResource} to track and expose. + * @param resId The Android id to use. This should be an actual Android id (R.id.some_id). + * @param asyncDynamicResource The {@link DynamicResource} to track and expose. */ - public void registerResource(int resId, DynamicResource resource) { + public void registerResource(int resId, DynamicResource asyncDynamicResource) { assert mDynamicResources.get(resId) == null; - mDynamicResources.put(resId, resource); + mDynamicResources.put(resId, asyncDynamicResource); + asyncDynamicResource.setOnResourceReadyCallback( + (resource) -> notifyLoadFinished(resId, resource)); } /** @@ -42,7 +46,10 @@ * @param resId The Android id representing the {@link DynamicResource}. */ public void unregisterResource(int resId) { + DynamicResource dynamicResource = mDynamicResources.get(resId); + if (dynamicResource == null) return; mDynamicResources.remove(resId); + dynamicResource.setOnResourceReadyCallback(null); notifyResourceUnregistered(resId); } @@ -53,10 +60,9 @@ */ @Override public void loadResource(int resId) { - DynamicResource resource = mDynamicResources.get(resId); - if (resource == null) return; - - if (resource.isDirty()) notifyLoadFinished(resId, resource); + DynamicResource dynamicResource = mDynamicResources.get(resId); + if (dynamicResource == null) return; + dynamicResource.onResourceRequested(); } /**
diff --git a/ui/android/java/src/org/chromium/ui/resources/dynamics/DynamicResourceSnapshot.java b/ui/android/java/src/org/chromium/ui/resources/dynamics/DynamicResourceSnapshot.java new file mode 100644 index 0000000..7a08ac7 --- /dev/null +++ b/ui/android/java/src/org/chromium/ui/resources/dynamics/DynamicResourceSnapshot.java
@@ -0,0 +1,52 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.ui.resources.dynamics; + +import android.graphics.Bitmap; +import android.graphics.Rect; + +import org.chromium.ui.resources.Resource; +import org.chromium.ui.resources.statics.NinePatchData; + +/** The current state of a dynamic resource. */ +public class DynamicResourceSnapshot implements Resource { + private final Bitmap mBitmap; + private final boolean mShouldRemoveResourceOnNullBitmap; + private final Rect mBitmapSize; + private final long mNativeResourceId; + + public DynamicResourceSnapshot(Bitmap bitmap, boolean shouldRemoveResourceOnNullBitmap, + Rect bitmapSize, long nativeResourceId) { + mBitmap = bitmap; + mShouldRemoveResourceOnNullBitmap = shouldRemoveResourceOnNullBitmap; + mBitmapSize = bitmapSize; + mNativeResourceId = nativeResourceId; + } + + @Override + public Bitmap getBitmap() { + return mBitmap; + } + + @Override + public boolean shouldRemoveResourceOnNullBitmap() { + return mShouldRemoveResourceOnNullBitmap; + } + + @Override + public Rect getBitmapSize() { + return mBitmapSize; + } + + @Override + public NinePatchData getNinePatchData() { + return null; + } + + @Override + public long createNativeResource() { + return mNativeResourceId; + } +}
diff --git a/ui/android/java/src/org/chromium/ui/resources/dynamics/ViewResourceAdapter.java b/ui/android/java/src/org/chromium/ui/resources/dynamics/ViewResourceAdapter.java index 2479c4e..69de47f 100644 --- a/ui/android/java/src/org/chromium/ui/resources/dynamics/ViewResourceAdapter.java +++ b/ui/android/java/src/org/chromium/ui/resources/dynamics/ViewResourceAdapter.java
@@ -21,8 +21,11 @@ import android.view.View.OnLayoutChangeListener; import android.view.ViewGroup; +import androidx.annotation.CallSuper; +import androidx.annotation.Nullable; import androidx.annotation.RequiresApi; +import org.chromium.base.Callback; import org.chromium.base.ThreadUtils; import org.chromium.base.TraceEvent; import org.chromium.base.metrics.RecordHistogram; @@ -31,18 +34,17 @@ import org.chromium.base.task.TaskTraits; import org.chromium.ui.resources.Resource; import org.chromium.ui.resources.ResourceFactory; -import org.chromium.ui.resources.statics.NinePatchData; import java.nio.ByteBuffer; import java.util.concurrent.atomic.AtomicInteger; /** - * An adapter that exposes a {@link View} as a {@link DynamicResource}. In order to properly use - * this adapter {@link ViewResourceAdapter#invalidate(Rect)} must be called when parts of the + * An adapter that exposes a {@link View} as a {@link DynamicResourceSnapshot}. In order to properly + * use this adapter {@link ViewResourceAdapter#invalidate(Rect)} must be called when parts of the * {@link View} are invalidated. For {@link ViewGroup}s the easiest way to do this is to override * {@link ViewGroup#invalidateChildInParent(int[], Rect)}. */ -public class ViewResourceAdapter extends DynamicResource implements OnLayoutChangeListener { +public class ViewResourceAdapter implements DynamicResource, OnLayoutChangeListener { private final View mView; private final Rect mDirtyRect = new Rect(); @@ -68,6 +70,9 @@ public enum ImageReaderStatus { NEW, INITIALIZING, UPDATED, RUNNING } private ThreadUtils.ThreadChecker mAdapterThreadChecker = new ThreadUtils.ThreadChecker(); + @Nullable + private Callback<Resource> mOnResourceReady; + // RenderNode was added in API level 29 (Android 10). So restrict AcceleratedImageReader as // well. @RequiresApi(Build.VERSION_CODES.Q) @@ -368,17 +373,16 @@ } /** - * If this resource is dirty ({@link #isDirty()} returned {@code true}), it will recapture a - * {@link Bitmap} of the {@link View}. - * @see DynamicResource#getBitmap() + * Typically called when ({@link #isDirty()} returned {@code true}), to return a new + * {@link Bitmap} and clear out the dirty rect, resulting in a non-dirty view. Depending on the + * draw mechanism, this may return a null bitmap. In such a case, on the next frame, isDirty() + * should still be used to decide whether to call this. * @return A {@link Bitmap} representing the {@link View}. */ - @Override @SuppressWarnings("NewApi") public Bitmap getBitmap() { mAdapterThreadChecker.assertOnValidThread(); TraceEvent.begin("ViewResourceAdapter:getBitmap"); - super.getBitmap(); boolean bitmapReady = false; if (mLastGetBitmapTimestamp > 0) { RecordHistogram.recordLongTimesHistogram("ViewResourceAdapter.GetBitmapInterval", @@ -404,16 +408,6 @@ return mBitmap; } - @Override - public boolean shouldRemoveResourceOnNullBitmap() { - return mUseHardwareBitmapDraw; - } - - @Override - public Rect getBitmapSize() { - return mViewSize; - } - /** * Set the downsampling scale. The rendered size is not affected. * @param scale The scale to use. <1 means the Bitmap is smaller than the View. @@ -426,21 +420,28 @@ mScale = scale; } - /** - * Override this method to create the native resource type for the generated bitmap. - */ - @Override + /** {@see Resource#createNativeResource()}. */ public long createNativeResource() { return ResourceFactory.createBitmapResource(null); } @Override - public final NinePatchData getNinePatchData() { - return null; + public void setOnResourceReadyCallback(Callback<Resource> onResourceReady) { + mOnResourceReady = onResourceReady; } - @Override - public boolean isDirty() { + /** + * Note that this is called for every access to the resource during a frame. If a resource is + * dirty, it should not be dirty again during the same looper call. + * {@link DynamicResourceLoader#loadResource(int)} only notifies + * {@link ResourceLoaderCallback#onResourceLoaded} if the resource is dirty. + * Therefore, if the resource is not dirty, {@link #onResourceRequested()} doesn't get called. + * TODO(dtrainor): Add checks so that a dynamic resource **can't** be built more than once each + * frame. + * @return Whether or not this resource is dirty and the CC component should be rebuilt. + */ + @CallSuper + protected boolean isDirty() { // The bitmap is dirty if some part of it has changed, or in hardware mode we're waiting for // the results of a previous request (null mBitmap or RUNNING). return !mDirtyRect.isEmpty() @@ -450,6 +451,20 @@ } @Override + public void onResourceRequested() { + // TODO(skym): The hardware capture approach should be pushing bitmaps when they're ready, + // and avoid relying on isDirty and/or onResourceRequested signals. However this is an + // intermediate state during refactoring, and is intentionally keeping the old behavior for + // now. + if (mOnResourceReady != null && isDirty()) { + Bitmap bitmap = getBitmap(); + Resource resource = new DynamicResourceSnapshot( + bitmap, mUseHardwareBitmapDraw, mViewSize, createNativeResource()); + mOnResourceReady.onResult(resource); + } + } + + @Override public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) { final int width = right - left;
diff --git a/ui/android/junit/src/org/chromium/ui/resources/dynamics/BitmapDynamicResourceTest.java b/ui/android/junit/src/org/chromium/ui/resources/dynamics/BitmapDynamicResourceTest.java index 719def28..006f609 100644 --- a/ui/android/junit/src/org/chromium/ui/resources/dynamics/BitmapDynamicResourceTest.java +++ b/ui/android/junit/src/org/chromium/ui/resources/dynamics/BitmapDynamicResourceTest.java
@@ -7,17 +7,23 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import static org.mockito.MockitoAnnotations.initMocks; import static org.chromium.base.GarbageCollectionTestUtils.canBeGarbageCollected; import android.graphics.Bitmap; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Mock; import org.robolectric.annotation.Config; import org.chromium.base.test.BaseRobolectricTestRunner; +import org.chromium.base.test.util.JniMocker; +import org.chromium.ui.resources.ResourceFactory; +import org.chromium.ui.resources.ResourceFactoryJni; import java.lang.ref.WeakReference; @@ -29,8 +35,15 @@ public class BitmapDynamicResourceTest { private BitmapDynamicResource mResource; + @Rule + public JniMocker mJniMocker = new JniMocker(); + @Mock + private ResourceFactory.Natives mResourceFactoryJni; + @Before public void setup() { + initMocks(this); + mJniMocker.mock(ResourceFactoryJni.TEST_HOOKS, mResourceFactoryJni); mResource = new BitmapDynamicResource(1); } @@ -38,7 +51,11 @@ public void testGetBitmap() { Bitmap bitmap = Bitmap.createBitmap(1, 2, Bitmap.Config.ARGB_8888); mResource.setBitmap(bitmap); - assertEquals(bitmap, mResource.getBitmap()); + assertEquals(bitmap, DynamicResourceTestUtils.getBitmapSync(mResource)); + + // Bitmap was already returned, next onResourceRequested should no-op. + mResource.setOnResourceReadyCallback((resource) -> { assert false; }); + mResource.onResourceRequested(); } @Test @@ -62,7 +79,24 @@ bitmap = null; assertFalse(canBeGarbageCollected(bitmapWeakReference)); - mResource.getBitmap(); + DynamicResourceTestUtils.getBitmapSync(mResource); assertTrue(canBeGarbageCollected(bitmapWeakReference)); } + + @Test + public void testOnResourceRequested_NotReady() { + Bitmap bitmap = Bitmap.createBitmap(1, 2, Bitmap.Config.ARGB_8888); + + // No callback or bitmap, onResourceRequested should no-op. + mResource.onResourceRequested(); + + // No bitmap, onResourceRequested should no-op. + mResource.setOnResourceReadyCallback((resource) -> { assert false; }); + mResource.onResourceRequested(); + + // No callback, onResourceRequested should no-op. + mResource.setOnResourceReadyCallback(null); + mResource.setBitmap(bitmap); + mResource.onResourceRequested(); + } }
diff --git a/ui/android/junit/src/org/chromium/ui/resources/dynamics/DynamicResourceTestUtils.java b/ui/android/junit/src/org/chromium/ui/resources/dynamics/DynamicResourceTestUtils.java new file mode 100644 index 0000000..8fa5e0c --- /dev/null +++ b/ui/android/junit/src/org/chromium/ui/resources/dynamics/DynamicResourceTestUtils.java
@@ -0,0 +1,45 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.ui.resources.dynamics; + +import static org.junit.Assert.assertNotNull; + +import android.graphics.Bitmap; +import android.graphics.Rect; + +import org.chromium.ui.resources.Resource; + +import java.util.concurrent.atomic.AtomicReference; + +/** + * Test utils class to share accessor patterns for working with {link @DynamicResoursce}. Primarily + * for old tests that were written and verify the behavior of synchronous resources. + */ +public final class DynamicResourceTestUtils { + /** Only works on {@link DynamicResource} that synchronously invoke their callback. */ + public static Rect getBitmapSizeSync(DynamicResource dynamicResource) { + return getResourceSync(dynamicResource).getBitmapSize(); + } + + /** Only works on {@link DynamicResource} that synchronously invoke their callback. */ + public static Bitmap getBitmapSync(DynamicResource dynamicResource) { + return getResourceSync(dynamicResource).getBitmap(); + } + + /** Only works on {@link DynamicResource} that synchronously invoke their callback. */ + public static Resource getResourceSync(DynamicResource dynamicResource) { + AtomicReference<Resource> resourcePointer = new AtomicReference<>(); + dynamicResource.setOnResourceReadyCallback((resource) -> resourcePointer.set(resource)); + dynamicResource.onResourceRequested(); + Resource temp = resourcePointer.get(); + assertNotNull(temp); + + // Clear out the callback which is owning the AtomicReference, which in turn keeps alive the + // Resource and/or Bitmap. Some tests will verify GC is able to reclaim these. + dynamicResource.setOnResourceReadyCallback(null); + + return temp; + } +} \ No newline at end of file
diff --git a/ui/android/junit/src/org/chromium/ui/resources/dynamics/ViewResourceAdapterTest.java b/ui/android/junit/src/org/chromium/ui/resources/dynamics/ViewResourceAdapterTest.java index da93e80..ad7a531 100644 --- a/ui/android/junit/src/org/chromium/ui/resources/dynamics/ViewResourceAdapterTest.java +++ b/ui/android/junit/src/org/chromium/ui/resources/dynamics/ViewResourceAdapterTest.java
@@ -20,12 +20,16 @@ import android.view.View; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.robolectric.annotation.Config; import org.chromium.base.test.BaseRobolectricTestRunner; +import org.chromium.base.test.util.JniMocker; +import org.chromium.ui.resources.ResourceFactory; +import org.chromium.ui.resources.ResourceFactoryJni; import java.lang.ref.WeakReference; @@ -37,6 +41,11 @@ public class ViewResourceAdapterTest { private int mViewWidth; private int mViewHeight; + + @Rule + public JniMocker mJniMocker = new JniMocker(); + @Mock + private ResourceFactory.Natives mResourceFactoryJni; @Mock private View mView; @@ -45,6 +54,7 @@ @Before public void setup() { initMocks(this); + mJniMocker.mock(ResourceFactoryJni.TEST_HOOKS, mResourceFactoryJni); mViewWidth = 200; mViewHeight = 100; @@ -62,7 +72,12 @@ return true; } }; + } + private Rect getBitmapSize() { + // Need to mark dirty before requesting, otherwise it will no-op. + mAdapter.invalidate(null); + return DynamicResourceTestUtils.getBitmapSizeSync(mAdapter); } @Test @@ -76,7 +91,7 @@ @Test public void testGetBitmapSize() { Bitmap bitmap = mAdapter.getBitmap(); - Rect rect = mAdapter.getBitmapSize(); + Rect rect = getBitmapSize(); assertEquals(bitmap.getWidth(), rect.width()); assertEquals(bitmap.getHeight(), rect.height()); } @@ -89,7 +104,7 @@ assertEquals(mViewWidth * scale, bitmap.getWidth(), 1); assertEquals(mViewHeight * scale, bitmap.getHeight(), 1); - Rect rect = mAdapter.getBitmapSize(); + Rect rect = getBitmapSize(); assertEquals(mViewWidth, rect.width()); assertEquals(mViewHeight, rect.height()); }
diff --git a/ui/compositor/layer.cc b/ui/compositor/layer.cc index 7b5519c..0360dbe 100644 --- a/ui/compositor/layer.cc +++ b/ui/compositor/layer.cc
@@ -1600,7 +1600,7 @@ return rounded_corner_radii(); } -gfx::LinearGradient Layer::GetGradientMaskForAnimation() const { +const gfx::LinearGradient& Layer::GetGradientMaskForAnimation() const { return gradient_mask(); }
diff --git a/ui/compositor/layer.h b/ui/compositor/layer.h index c0cd707..3ea74c8 100644 --- a/ui/compositor/layer.h +++ b/ui/compositor/layer.h
@@ -328,7 +328,7 @@ // Gets/sets a gradient mask that is applied to the clip bounds on the layer void SetGradientMask(const gfx::LinearGradient& linear_gradient); - const gfx::LinearGradient gradient_mask() const { + const gfx::LinearGradient& gradient_mask() const { return cc_layer_->gradient_mask(); } @@ -601,7 +601,7 @@ SkColor GetColorForAnimation() const override; gfx::Rect GetClipRectForAnimation() const override; gfx::RoundedCornersF GetRoundedCornersForAnimation() const override; - gfx::LinearGradient GetGradientMaskForAnimation() const override; + const gfx::LinearGradient& GetGradientMaskForAnimation() const override; float GetDeviceScaleFactor() const override; Layer* GetLayer() override; cc::Layer* GetCcLayer() const override;
diff --git a/ui/compositor/layer_animation_delegate.h b/ui/compositor/layer_animation_delegate.h index 79088f3..1107a595 100644 --- a/ui/compositor/layer_animation_delegate.h +++ b/ui/compositor/layer_animation_delegate.h
@@ -59,7 +59,7 @@ virtual SkColor GetColorForAnimation() const = 0; virtual gfx::Rect GetClipRectForAnimation() const = 0; virtual gfx::RoundedCornersF GetRoundedCornersForAnimation() const = 0; - virtual gfx::LinearGradient GetGradientMaskForAnimation() const = 0; + virtual const gfx::LinearGradient& GetGradientMaskForAnimation() const = 0; virtual float GetDeviceScaleFactor() const = 0; virtual ui::Layer* GetLayer() = 0; virtual cc::Layer* GetCcLayer() const = 0;
diff --git a/ui/compositor/layer_animation_element.cc b/ui/compositor/layer_animation_element.cc index 1b070f92..103a6e936 100644 --- a/ui/compositor/layer_animation_element.cc +++ b/ui/compositor/layer_animation_element.cc
@@ -404,7 +404,7 @@ private: gfx::LinearGradient start_; - gfx::LinearGradient target_; + const gfx::LinearGradient target_; }; // ThreadedLayerAnimationElement -----------------------------------------------
diff --git a/ui/compositor/test/test_layer_animation_delegate.cc b/ui/compositor/test/test_layer_animation_delegate.cc index b077818..697a563 100644 --- a/ui/compositor/test/test_layer_animation_delegate.cc +++ b/ui/compositor/test/test_layer_animation_delegate.cc
@@ -175,8 +175,8 @@ return rounded_corners_; } -gfx::LinearGradient TestLayerAnimationDelegate::GetGradientMaskForAnimation() - const { +const gfx::LinearGradient& +TestLayerAnimationDelegate::GetGradientMaskForAnimation() const { return gradient_mask_; }
diff --git a/ui/compositor/test/test_layer_animation_delegate.h b/ui/compositor/test/test_layer_animation_delegate.h index 642335e4d..f1af662 100644 --- a/ui/compositor/test/test_layer_animation_delegate.h +++ b/ui/compositor/test/test_layer_animation_delegate.h
@@ -79,7 +79,7 @@ SkColor GetColorForAnimation() const override; gfx::Rect GetClipRectForAnimation() const override; gfx::RoundedCornersF GetRoundedCornersForAnimation() const override; - gfx::LinearGradient GetGradientMaskForAnimation() const override; + const gfx::LinearGradient& GetGradientMaskForAnimation() const override; float GetDeviceScaleFactor() const override; LayerAnimatorCollection* GetLayerAnimatorCollection() override; ui::Layer* GetLayer() override;
diff --git a/ui/gl/generate_bindings.py b/ui/gl/generate_bindings.py index 7839ed73..898e471 100755 --- a/ui/gl/generate_bindings.py +++ b/ui/gl/generate_bindings.py
@@ -3124,9 +3124,6 @@ class GLContext; """ % {'name': set_name.upper()}) - if set_name == 'egl': - file.write("class GLDisplayEGL;\n") - # Write typedefs for function pointer types. Always use the GL name for the # typedef. file.write('\n') @@ -3161,10 +3158,10 @@ file.write( """ - void InitializeExtensionSettings(GLDisplayEGL* display); - void UpdateConditionalExtensionSettings(GLDisplayEGL* display); + void InitializeExtensionSettings(EGLDisplay display); + void UpdateConditionalExtensionSettings(EGLDisplay display); - static std::string GetPlatformExtensions(GLDisplayEGL* display); + static std::string GetPlatformExtensions(EGLDisplay display); """) file.write('};\n') file.write('\n') @@ -3328,8 +3325,6 @@ 'ui/gl/gl_implementation.h', 'ui/gl/gl_version_info.h', set_header_name ] - if set_name == 'egl': - include_list.append('ui/gl/gl_display.h') includes_string = "\n".join(["#include \"{0}\"".format(h) for h in sorted(include_list)]) @@ -3496,7 +3491,7 @@ file.write("""\ } -void DisplayExtensionsEGL::InitializeExtensionSettings(GLDisplayEGL* display) { +void DisplayExtensionsEGL::InitializeExtensionSettings(EGLDisplay display) { std::string platform_extensions(GetPlatformExtensions(display)); [[maybe_unused]] gfx::ExtensionSet extensions( gfx::MakeExtensionSet(platform_extensions));
diff --git a/ui/gl/gl_bindings.cc b/ui/gl/gl_bindings.cc index 8092fa1..42b1f6e 100644 --- a/ui/gl/gl_bindings.cc +++ b/ui/gl/gl_bindings.cc
@@ -24,7 +24,7 @@ #if defined(USE_EGL) void DisplayExtensionsEGL::UpdateConditionalExtensionSettings( - GLDisplayEGL* display) { + EGLDisplay display) { // For the moment, only two extensions can be conditionally disabled // through GPU driver bug workarounds mechanism: // EGL_KHR_fence_sync @@ -43,12 +43,10 @@ } // static -std::string DisplayExtensionsEGL::GetPlatformExtensions(GLDisplayEGL* display) { - DCHECK(display); - EGLDisplay egl_display = display->GetDisplay(); - if (egl_display == EGL_NO_DISPLAY) +std::string DisplayExtensionsEGL::GetPlatformExtensions(EGLDisplay display) { + if (display == EGL_NO_DISPLAY) return ""; - const char* str = eglQueryString(egl_display, EGL_EXTENSIONS); + const char* str = eglQueryString(display, EGL_EXTENSIONS); return str ? std::string(str) : ""; }
diff --git a/ui/gl/gl_bindings_autogen_egl.cc b/ui/gl/gl_bindings_autogen_egl.cc index 7db07227..e203c35 100644 --- a/ui/gl/gl_bindings_autogen_egl.cc +++ b/ui/gl/gl_bindings_autogen_egl.cc
@@ -13,7 +13,6 @@ #include "base/trace_event/trace_event.h" #include "ui/gl/gl_bindings.h" #include "ui/gl/gl_context.h" -#include "ui/gl/gl_display.h" #include "ui/gl/gl_egl_api_implementation.h" #include "ui/gl/gl_enums.h" #include "ui/gl/gl_implementation.h" @@ -266,7 +265,7 @@ gfx::HasExtension(extensions, "EGL_MESA_platform_surfaceless"); } -void DisplayExtensionsEGL::InitializeExtensionSettings(GLDisplayEGL* display) { +void DisplayExtensionsEGL::InitializeExtensionSettings(EGLDisplay display) { std::string platform_extensions(GetPlatformExtensions(display)); [[maybe_unused]] gfx::ExtensionSet extensions( gfx::MakeExtensionSet(platform_extensions));
diff --git a/ui/gl/gl_bindings_autogen_egl.h b/ui/gl/gl_bindings_autogen_egl.h index 0c192dd3..2fdffb0 100644 --- a/ui/gl/gl_bindings_autogen_egl.h +++ b/ui/gl/gl_bindings_autogen_egl.h
@@ -16,7 +16,6 @@ namespace gl { class GLContext; -class GLDisplayEGL; typedef EGLBoolean(GL_BINDING_CALL* eglBindAPIProc)(EGLenum api); typedef EGLBoolean(GL_BINDING_CALL* eglBindTexImageProc)(EGLDisplay dpy, @@ -395,10 +394,10 @@ bool b_GL_CHROMIUM_egl_android_native_fence_sync_hack; bool b_GL_CHROMIUM_egl_khr_fence_sync_hack; - void InitializeExtensionSettings(GLDisplayEGL* display); - void UpdateConditionalExtensionSettings(GLDisplayEGL* display); + void InitializeExtensionSettings(EGLDisplay display); + void UpdateConditionalExtensionSettings(EGLDisplay display); - static std::string GetPlatformExtensions(GLDisplayEGL* display); + static std::string GetPlatformExtensions(EGLDisplay display); }; struct ProcsEGL {
diff --git a/ui/gl/gl_display.cc b/ui/gl/gl_display.cc index e3a2466..9fc104a2 100644 --- a/ui/gl/gl_display.cc +++ b/ui/gl/gl_display.cc
@@ -666,6 +666,17 @@ GLDisplay::~GLDisplay() = default; #if defined(USE_EGL) +GLDisplayEGL::EGLGpuSwitchingObserver::EGLGpuSwitchingObserver( + EGLDisplay display) + : display_(display) { + DCHECK(display != EGL_NO_DISPLAY); +} + +void GLDisplayEGL::EGLGpuSwitchingObserver::OnGpuSwitched( + GpuPreference active_gpu_heuristic) { + eglHandleGPUSwitchANGLE(display_); +} + GLDisplayEGL::GLDisplayEGL(uint64_t system_device_id) : GLDisplay(system_device_id) { ext = std::make_unique<DisplayExtensionsEGL>(); @@ -678,6 +689,26 @@ return display_; } +void GLDisplayEGL::Shutdown() { + if (display_ == EGL_NO_DISPLAY) + return; + + if (gpu_switching_observer_.get()) { + ui::GpuSwitchingManager::GetInstance()->RemoveObserver( + gpu_switching_observer_.get()); + gpu_switching_observer_.reset(); + } + + angle::ResetPlatform(display_); + DCHECK(g_driver_egl.fn.eglTerminateFn); + eglTerminate(display_); + + display_ = EGL_NO_DISPLAY; + egl_surfaceless_context_supported_ = false; + egl_context_priority_supported_ = false; + egl_android_native_fence_sync_supported_ = false; +} + void GLDisplayEGL::SetDisplay(EGLDisplay display) { display_ = display; } @@ -712,6 +743,36 @@ return this->ext->b_EGL_ANGLE_external_context_and_surface; } +bool GLDisplayEGL::Initialize(EGLDisplayPlatform native_display) { + if (display_ != EGL_NO_DISPLAY) + return true; + + if (!InitializeDisplay(native_display)) + return false; + InitializeCommon(); + + if (ext->b_EGL_ANGLE_power_preference) { + gpu_switching_observer_ = + std::make_unique<EGLGpuSwitchingObserver>(display_); + ui::GpuSwitchingManager::GetInstance()->AddObserver( + gpu_switching_observer_.get()); + } + return true; +} + +void GLDisplayEGL::InitializeForTesting() { + display_ = eglGetCurrentDisplay(); + ext->InitializeExtensionSettings(display_); + InitializeCommon(); +} + +bool GLDisplayEGL::InitializeExtensionSettings() { + if (display_ == EGL_NO_DISPLAY) + return false; + ext->UpdateConditionalExtensionSettings(display_); + return true; +} + // InitializeDisplay is necessary because the static binding code // needs a full Display init before it can query the Display extensions. bool GLDisplayEGL::InitializeDisplay(EGLDisplayPlatform native_display) { @@ -837,7 +898,7 @@ DISPLAY_TYPE_MAX); display_ = display; display_type_ = display_type; - ext->InitializeExtensionSettings(this); + ext->InitializeExtensionSettings(display); return true; } @@ -917,27 +978,6 @@ } #endif } - -bool GLDisplayEGL::InitializeExtensionSettings() { - if (display_ == EGL_NO_DISPLAY) - return false; - ext->UpdateConditionalExtensionSettings(this); - return true; -} - -void GLDisplayEGL::Shutdown() { - if (display_ == EGL_NO_DISPLAY) - return; - - angle::ResetPlatform(display_); - DCHECK(g_driver_egl.fn.eglTerminateFn); - eglTerminate(display_); - - display_ = EGL_NO_DISPLAY; - egl_surfaceless_context_supported_ = false; - egl_context_priority_supported_ = false; - egl_android_native_fence_sync_supported_ = false; -} #endif // defined(USE_EGL) #if defined(USE_GLX) @@ -949,6 +989,8 @@ void* GLDisplayX11::GetDisplay() { return x11::Connection::Get()->GetXlibDisplay(); } + +void GLDisplayX11::Shutdown() {} #endif // defined(USE_GLX) } // namespace gl
diff --git a/ui/gl/gl_display.h b/ui/gl/gl_display.h index a55edd5..8fa42a3 100644 --- a/ui/gl/gl_display.h +++ b/ui/gl/gl_display.h
@@ -13,6 +13,8 @@ #if defined(USE_EGL) #include <EGL/egl.h> + +#include "ui/gl/gpu_switching_manager.h" #endif // defined(USE_EGL) namespace base { @@ -89,6 +91,7 @@ virtual ~GLDisplay(); virtual void* GetDisplay() = 0; + virtual void Shutdown() = 0; protected: explicit GLDisplay(uint64_t system_device_id); @@ -107,6 +110,8 @@ static GLDisplayEGL* GetDisplayForCurrentContext(); EGLDisplay GetDisplay() override; + void Shutdown() override; + void SetDisplay(EGLDisplay display); EGLDisplayPlatform GetNativeDisplay() const; DisplayType GetDisplayType() const; @@ -116,18 +121,30 @@ bool IsAndroidNativeFenceSyncSupported(); bool IsANGLEExternalContextAndSurfaceSupported(); - bool InitializeDisplay(EGLDisplayPlatform native_display); - void InitializeCommon(); + bool Initialize(EGLDisplayPlatform native_display); + void InitializeForTesting(); bool InitializeExtensionSettings(); - void Shutdown(); std::unique_ptr<DisplayExtensionsEGL> ext; private: friend class GLDisplayManager<GLDisplayEGL>; + friend class EGLApiTest; + + class EGLGpuSwitchingObserver final : public ui::GpuSwitchingObserver { + public: + explicit EGLGpuSwitchingObserver(EGLDisplay display); + void OnGpuSwitched(GpuPreference active_gpu_heuristic) override; + + private: + EGLDisplay display_ = EGL_NO_DISPLAY; + }; explicit GLDisplayEGL(uint64_t system_device_id); + bool InitializeDisplay(EGLDisplayPlatform native_display); + void InitializeCommon(); + EGLDisplay display_ = EGL_NO_DISPLAY; EGLDisplayPlatform native_display_ = EGLDisplayPlatform(EGL_DEFAULT_DISPLAY); DisplayType display_type_ = DisplayType::DEFAULT; @@ -135,6 +152,8 @@ bool egl_surfaceless_context_supported_ = false; bool egl_context_priority_supported_ = false; bool egl_android_native_fence_sync_supported_ = false; + + std::unique_ptr<EGLGpuSwitchingObserver> gpu_switching_observer_; }; #endif // defined(USE_EGL) @@ -147,6 +166,7 @@ ~GLDisplayX11() override; void* GetDisplay() override; + void Shutdown() override; private: friend class GLDisplayManager<GLDisplayX11>;
diff --git a/ui/gl/gl_surface_egl.cc b/ui/gl/gl_surface_egl.cc index b362ea0..9008a79d 100644 --- a/ui/gl/gl_surface_egl.cc +++ b/ui/gl/gl_surface_egl.cc
@@ -31,7 +31,6 @@ #include "ui/gl/gl_surface_presentation_helper.h" #include "ui/gl/gl_surface_stub.h" #include "ui/gl/gl_utils.h" -#include "ui/gl/gpu_switching_manager.h" #include "ui/gl/scoped_make_current.h" #include "ui/gl/sync_control_vsync_provider.h" @@ -85,10 +84,6 @@ namespace { -class EGLGpuSwitchingObserver; - -EGLGpuSwitchingObserver* g_egl_gpu_switching_observer = nullptr; - constexpr const char kSwapEventTraceCategories[] = "gpu"; constexpr size_t kMaxTimestampsSupportable = 9; @@ -156,21 +151,6 @@ raw_ptr<GLDisplayEGL> display_; }; -class EGLGpuSwitchingObserver final : public ui::GpuSwitchingObserver { - public: - explicit EGLGpuSwitchingObserver(GLDisplayEGL* display) : display_(display) { - DCHECK(display_); - } - - void OnGpuSwitched(gl::GpuPreference active_gpu_heuristic) override { - DCHECK(display_->ext->b_EGL_ANGLE_power_preference); - eglHandleGPUSwitchANGLE(display_->GetDisplay()); - } - - private: - raw_ptr<GLDisplayEGL> display_ = nullptr; -}; - bool ValidateEglConfig(EGLDisplay display, const EGLint* config_attribs, EGLint* num_configs) { @@ -382,50 +362,6 @@ GpuPreference::kDefault); } -// static -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) { - if (!display->InitializeDisplay(native_display)) - return nullptr; - display->InitializeCommon(); - if (display->ext->b_EGL_ANGLE_power_preference) { - g_egl_gpu_switching_observer = new EGLGpuSwitchingObserver(display); - ui::GpuSwitchingManager::GetInstance()->AddObserver( - g_egl_gpu_switching_observer); - } - } - return display; -} - -// static -GLDisplayEGL* GLSurfaceEGL::InitializeOneOffForTesting() { - GLDisplayEGL* display = - GLDisplayManagerEGL::GetInstance()->GetDisplay(GpuPreference::kDefault); - display->SetDisplay(eglGetCurrentDisplay()); - display->ext->InitializeExtensionSettings(display); - display->InitializeCommon(); - return display; -} - -// static -void GLSurfaceEGL::ShutdownOneOff(GLDisplayEGL* display) { - if (!display || display->GetDisplay() == EGL_NO_DISPLAY) { - return; - } - - if (g_egl_gpu_switching_observer) { - ui::GpuSwitchingManager::GetInstance()->RemoveObserver( - g_egl_gpu_switching_observer); - delete g_egl_gpu_switching_observer; - g_egl_gpu_switching_observer = nullptr; - } - - display->Shutdown(); -} - GLSurfaceEGL::~GLSurfaceEGL() = default; NativeViewGLSurfaceEGL::NativeViewGLSurfaceEGL(
diff --git a/ui/gl/gl_surface_egl.h b/ui/gl/gl_surface_egl.h index ffb507c..31c9491 100644 --- a/ui/gl/gl_surface_egl.h +++ b/ui/gl/gl_surface_egl.h
@@ -50,17 +50,6 @@ static GLDisplayEGL* GetGLDisplayEGL(); - // |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. - // 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 void ShutdownOneOff(GLDisplayEGL* display); - protected: ~GLSurfaceEGL() override;
diff --git a/ui/gl/gl_utils.cc b/ui/gl/gl_utils.cc index dffa2e5..c73fc65 100644 --- a/ui/gl/gl_utils.cc +++ b/ui/gl/gl_utils.cc
@@ -208,6 +208,10 @@ return GLDisplayManagerEGL::GetInstance()->GetDisplay( GpuPreference::kDefault); } + +GLDisplayEGL* GetDisplayEGL(uint64_t system_device_id) { + return GLDisplayManagerEGL::GetInstance()->GetDisplay(system_device_id); +} #endif // USE_EGL #if defined(USE_GLX)
diff --git a/ui/gl/gl_utils.h b/ui/gl/gl_utils.h index 1118348..89a01817 100644 --- a/ui/gl/gl_utils.h +++ b/ui/gl/gl_utils.h
@@ -79,6 +79,9 @@ // Query the default GLDisplayEGL. GL_EXPORT GLDisplayEGL* GetDefaultDisplayEGL(); + +// Query the GLDisplayEGL by |system_device_id|. +GL_EXPORT GLDisplayEGL* GetDisplayEGL(uint64_t system_device_id); #endif // USE_EGL #if defined(USE_GLX)
diff --git a/ui/gl/init/gl_initializer_android.cc b/ui/gl/init/gl_initializer_android.cc index 5abd2d9..792febb 100644 --- a/ui/gl/init/gl_initializer_android.cc +++ b/ui/gl/init/gl_initializer_android.cc
@@ -10,11 +10,10 @@ #include "base/logging.h" #include "base/native_library.h" #include "ui/gl/gl_bindings.h" +#include "ui/gl/gl_display.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" -#include "ui/gl/init/gl_initializer.h" namespace gl { namespace init { @@ -78,20 +77,20 @@ } // namespace GLDisplay* InitializeGLOneOffPlatform(uint64_t system_device_id) { + GLDisplayEGL* display = + GLDisplayManagerEGL::GetInstance()->GetDisplay(system_device_id); switch (GetGLImplementation()) { case kGLImplementationEGLGLES2: - case kGLImplementationEGLANGLE: { - GLDisplay* display = GLSurfaceEGL::InitializeOneOff( - EGLDisplayPlatform(EGL_DEFAULT_DISPLAY), system_device_id); - if (!display) { - LOG(ERROR) << "GLSurfaceEGL::InitializeOneOff failed."; + case kGLImplementationEGLANGLE: + if (!display->Initialize(EGLDisplayPlatform(EGL_DEFAULT_DISPLAY))) { + LOG(ERROR) << "GLDisplayEGL::Initialize failed."; return nullptr; } - return display; - } + break; default: - return GLDisplayManagerEGL::GetInstance()->GetDisplay(system_device_id); + break; } + return display; } bool InitializeStaticGLBindings(GLImplementationParts implementation) { @@ -117,7 +116,8 @@ } void ShutdownGLPlatform(GLDisplay* display) { - GLSurfaceEGL::ShutdownOneOff(static_cast<GLDisplayEGL*>(display)); + if (display) + display->Shutdown(); ClearBindingsEGL(); ClearBindingsGL(); }
diff --git a/ui/gl/init/gl_initializer_mac.cc b/ui/gl/init/gl_initializer_mac.cc index edbbaed..4318ce1f 100644 --- a/ui/gl/init/gl_initializer_mac.cc +++ b/ui/gl/init/gl_initializer_mac.cc
@@ -18,16 +18,15 @@ #include "base/threading/thread_restrictions.h" #include "ui/gl/gl_bindings.h" #include "ui/gl/gl_context.h" +#include "ui/gl/gl_display.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" #include "ui/gl/gpu_switching_manager.h" -#include "ui/gl/init/gl_initializer.h" #if defined(USE_EGL) #include "ui/gl/gl_egl_api_implementation.h" -#include "ui/gl/gl_surface_egl.h" #endif // defined(USE_EGL) namespace gl { @@ -167,28 +166,28 @@ } // namespace GLDisplay* InitializeGLOneOffPlatform(uint64_t system_device_id) { + GLDisplayEGL* display = + GLDisplayManagerEGL::GetInstance()->GetDisplay(system_device_id); switch (GetGLImplementation()) { case kGLImplementationDesktopGL: case kGLImplementationDesktopGLCoreProfile: if (!InitializeOneOffForSandbox()) { LOG(ERROR) << "GLSurfaceCGL::InitializeOneOff failed."; } - return GLDisplayManagerEGL::GetInstance()->GetDisplay(system_device_id); + break; #if defined(USE_EGL) case kGLImplementationEGLGLES2: - case kGLImplementationEGLANGLE: { - GLDisplay* display = GLSurfaceEGL::InitializeOneOff(EGLDisplayPlatform(0), - system_device_id); - if (!display) { - LOG(ERROR) << "GLSurfaceEGL::InitializeOneOff failed."; + case kGLImplementationEGLANGLE: + if (!display->Initialize(EGLDisplayPlatform(0))) { + LOG(ERROR) << "GLDisplayEGL::Initialize failed."; return nullptr; } - return display; - } + break; #endif // defined(USE_EGL) default: - return GLDisplayManagerEGL::GetInstance()->GetDisplay(system_device_id); + break; } + return display; } bool InitializeStaticGLBindings(GLImplementationParts implementation) { @@ -227,7 +226,8 @@ void ShutdownGLPlatform(GLDisplay* display) { ClearBindingsGL(); #if defined(USE_EGL) - GLSurfaceEGL::ShutdownOneOff(static_cast<GLDisplayEGL*>(display)); + if (display) + display->Shutdown(); ClearBindingsEGL(); #endif // defined(USE_EGL) }
diff --git a/ui/gl/init/gl_initializer_win.cc b/ui/gl/init/gl_initializer_win.cc index 960065c..a534d27 100644 --- a/ui/gl/init/gl_initializer_win.cc +++ b/ui/gl/init/gl_initializer_win.cc
@@ -18,10 +18,10 @@ #include "base/trace_event/trace_event.h" #include "base/win/windows_version.h" #include "ui/gl/gl_bindings.h" +#include "ui/gl/gl_display.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" #include "ui/gl/vsync_provider_win.h" namespace gl { @@ -126,24 +126,23 @@ GLDisplay* InitializeGLOneOffPlatform(uint64_t system_device_id) { VSyncProviderWin::InitializeOneOff(); + GLDisplayEGL* display = + GLDisplayManagerEGL::GetInstance()->GetDisplay(system_device_id); switch (GetGLImplementation()) { - case kGLImplementationEGLANGLE: { - GLDisplayEGL* display = GLSurfaceEGL::InitializeOneOff( - EGLDisplayPlatform(GetDC(nullptr)), system_device_id); - if (!display) { - LOG(ERROR) << "GLSurfaceEGL::InitializeOneOff failed."; + case kGLImplementationEGLANGLE: + if (!display->Initialize(EGLDisplayPlatform(GetDC(nullptr)))) { + LOG(ERROR) << "GLDisplayEGL::Initialize failed."; return nullptr; } DirectCompositionSurfaceWin::InitializeOneOff(display); - return display; - } + break; case kGLImplementationMockGL: case kGLImplementationStubGL: break; default: NOTREACHED(); } - return GLDisplayManagerEGL::GetInstance()->GetDisplay(system_device_id); + return display; } bool InitializeStaticGLBindings(GLImplementationParts implementation) { @@ -175,7 +174,8 @@ void ShutdownGLPlatform(GLDisplay* display) { DirectCompositionSurfaceWin::ShutdownOneOff(); - GLSurfaceEGL::ShutdownOneOff(static_cast<GLDisplayEGL*>(display)); + if (display) + display->Shutdown(); ClearBindingsEGL(); ClearBindingsGL(); }
diff --git a/ui/lottie/animation.cc b/ui/lottie/animation.cc index eac05dd0..dff6b09 100644 --- a/ui/lottie/animation.cc +++ b/ui/lottie/animation.cc
@@ -250,6 +250,7 @@ void Animation::PaintFrame(gfx::Canvas* canvas, float t, const gfx::Size& size) { + TRACE_EVENT1("ui", "Animation::PaintFrame", "timestamp", t); DCHECK_GE(t, 0.f); DCHECK_LE(t, 1.f); // Not all of the image assets necessarily appear in the frame at time |t|. To @@ -278,6 +279,7 @@ float t, sk_sp<SkImage>&, SkSamplingOptions&) { + TRACE_EVENT0("ui", "Animation::LoadImageForAsset"); cc::SkottieFrameDataProvider::ImageAsset& image_asset = *image_assets_.at(asset_id); all_frame_data.emplace(asset_id,
diff --git a/ui/ozone/common/gl_ozone_egl.cc b/ui/ozone/common/gl_ozone_egl.cc index 7fd7fea..18d1195 100644 --- a/ui/ozone/common/gl_ozone_egl.cc +++ b/ui/ozone/common/gl_ozone_egl.cc
@@ -7,21 +7,21 @@ #include "ui/gl/gl_bindings.h" #include "ui/gl/gl_context.h" #include "ui/gl/gl_context_egl.h" +#include "ui/gl/gl_display.h" #include "ui/gl/gl_egl_api_implementation.h" #include "ui/gl/gl_gl_api_implementation.h" #include "ui/gl/gl_share_group.h" #include "ui/gl/gl_surface.h" -#include "ui/gl/gl_surface_egl.h" +#include "ui/gl/gl_utils.h" #include "ui/ozone/common/native_pixmap_egl_binding.h" namespace ui { 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."; + gl::GLDisplayEGL* display = gl::GetDisplayEGL(system_device_id); + if (!display->Initialize(GetNativeDisplay())) { + LOG(ERROR) << "GLDisplayEGL::Initialize failed."; return nullptr; } return display; @@ -51,7 +51,8 @@ } void GLOzoneEGL::ShutdownGL(gl::GLDisplay* display) { - gl::GLSurfaceEGL::ShutdownOneOff(static_cast<gl::GLDisplayEGL*>(display)); + if (display) + display->Shutdown(); gl::ClearBindingsGL(); gl::ClearBindingsEGL(); }
diff --git a/ui/ozone/platform/wayland/BUILD.gn b/ui/ozone/platform/wayland/BUILD.gn index 97043d5..c27fbc30 100644 --- a/ui/ozone/platform/wayland/BUILD.gn +++ b/ui/ozone/platform/wayland/BUILD.gn
@@ -645,6 +645,7 @@ "//ui/events:test_support", "//ui/events/types:headers", "//ui/gfx", + "//ui/ozone/platform/wayland", "//ui/platform_window/common", ] }
diff --git a/ui/ozone/platform/wayland/emulate/wayland_input_emulate.cc b/ui/ozone/platform/wayland/emulate/wayland_input_emulate.cc index 1e64f8e..7a862b0 100644 --- a/ui/ozone/platform/wayland/emulate/wayland_input_emulate.cc +++ b/ui/ozone/platform/wayland/emulate/wayland_input_emulate.cc
@@ -15,6 +15,7 @@ #include "base/time/time.h" #include "ui/base/test/ui_controls.h" #include "ui/events/keycodes/dom/keycode_converter.h" +#include "ui/ozone/platform/wayland/host/wayland_window.h" #include "ui/platform_window/common/platform_window_defaults.h" namespace wl { @@ -107,7 +108,8 @@ void WaylandInputEmulate::EmulatePointerMotion( gfx::AcceleratedWidget widget, - const gfx::Point& mouse_surface_loc) { + const gfx::Point& mouse_surface_loc, + const gfx::Point& mouse_screen_loc_in_px) { auto it = windows_.find(widget); DCHECK(it != windows_.end()); @@ -115,7 +117,8 @@ if (!test_window->buffer_attached_and_configured) { auto pending_event = std::make_unique<PendingEvent>(ui::EventType::ET_MOUSE_MOVED, widget); - pending_event->location_in_px = mouse_surface_loc; + pending_event->pointer_surface_location = mouse_surface_loc; + pending_event->pointer_screen_location_in_px = mouse_screen_loc_in_px; test_window->pending_events.emplace_back(std::move(pending_event)); return; } @@ -127,18 +130,30 @@ // If it's a toplevel window, activate it. This results in raising the the // parent window and its children windows. + // TODO(oshima): This is probably not right because a inactive window can + // still receive mouse wheel event, and you may want to move a mouse pointer + // w/o activating a window. A window will be activated when a mouse is + // clicked. auto window_type = wayland_proxy->GetWindowType(widget); if (window_type != ui::PlatformWindowType::kTooltip && window_type != ui::PlatformWindowType::kMenu && !wayland_proxy->WindowHasPointerFocus(widget)) { weston_test_activate_surface(weston_test_, wlsurface); } + bool screen_coordinates = + wayland_proxy->GetWaylandWindowForAcceleratedWidget(widget) + ->IsScreenCoordinatesEnabled(); + auto* target_surface = screen_coordinates ? nullptr : wlsurface; + auto target_location = + screen_coordinates ? mouse_screen_loc_in_px : mouse_surface_loc; + + // TODO(crbug.com/1306688): The coordinate should be in DIP. timespec ts = (base::TimeTicks::Now() - base::TimeTicks()).ToTimeSpec(); - weston_test_move_pointer(weston_test_, wlsurface, + weston_test_move_pointer(weston_test_, target_surface, static_cast<uint64_t>(ts.tv_sec) >> 32, ts.tv_sec & 0xffffffff, ts.tv_nsec, - mouse_surface_loc.x(), mouse_surface_loc.y()); + target_location.x(), target_location.y()); wayland_proxy->ScheduleDisplayFlush(); } @@ -215,7 +230,7 @@ auto* test_window = it->second.get(); if (!test_window->buffer_attached_and_configured) { auto pending_event = std::make_unique<PendingEvent>(event_type, widget); - pending_event->location_in_px = touch_screen_loc; + pending_event->touch_screen_location = touch_screen_loc; pending_event->touch_id = id; test_window->pending_events.emplace_back(std::move(pending_event)); return; @@ -282,7 +297,9 @@ // // This is needed as running some tests doesn't result in sending frames that // require buffers to be created. - auto buffer_size = wayland_proxy->GetWindowBounds(widget).size(); + auto* wayland_window = + wayland_proxy->GetWaylandWindowForAcceleratedWidget(widget); + auto buffer_size = wayland_window->GetBoundsInPixels().size(); // Adjust the buffer size in case if the window was created with empty size. if (buffer_size.IsEmpty()) buffer_size.SetSize(1, 1); @@ -348,8 +365,8 @@ wl_fixed_t x, wl_fixed_t y) { WaylandInputEmulate* emulate = static_cast<WaylandInputEmulate*>(data); - auto mouse_position_on_screen_px = - gfx::Point(wl_fixed_to_int(x), wl_fixed_to_int(y)); + gfx::Point mouse_position_on_screen_px(wl_fixed_to_int(x), + wl_fixed_to_int(y)); for (WaylandInputEmulate::Observer& observer : emulate->observers_) observer.OnPointerMotionGlobal(mouse_position_on_screen_px); } @@ -437,8 +454,9 @@ switch (event->type) { case ui::EventType::ET_MOUSE_MOVED: - input_emulate->EmulatePointerMotion(window->widget, - event->location_in_px); + input_emulate->EmulatePointerMotion( + window->widget, event->pointer_surface_location, + event->pointer_screen_location_in_px); break; case ui::EventType::ET_MOUSE_PRESSED: case ui::EventType::ET_MOUSE_RELEASED: @@ -454,7 +472,8 @@ case ui::EventType::ET_TOUCH_MOVED: case ui::EventType::ET_TOUCH_RELEASED: input_emulate->EmulateTouch(window->widget, event->type, - event->touch_id, event->location_in_px); + event->touch_id, + event->touch_screen_location); break; default: NOTREACHED();
diff --git a/ui/ozone/platform/wayland/emulate/wayland_input_emulate.h b/ui/ozone/platform/wayland/emulate/wayland_input_emulate.h index bb3e2c2..2f0075a9 100644 --- a/ui/ozone/platform/wayland/emulate/wayland_input_emulate.h +++ b/ui/ozone/platform/wayland/emulate/wayland_input_emulate.h
@@ -58,7 +58,8 @@ void AddObserver(Observer* obs); void RemoveObserver(Observer* obs); void EmulatePointerMotion(gfx::AcceleratedWidget widget, - const gfx::Point& mouse_surface_loc); + const gfx::Point& mouse_surface_loc, + const gfx::Point& mouse_screen_loc_in_px); void EmulatePointerButton(gfx::AcceleratedWidget widget, ui::EventType event_type, uint32_t changed_button); @@ -82,10 +83,13 @@ ui::EventType type; gfx::AcceleratedWidget widget; - // Set for type == ET_MOUSE_MOVED || type == ET_TOUCH_*. Location is in - // surface coordinates for mouse events, and in root coordinates for touch - // events. - gfx::Point location_in_px; + // Set for type == ET_MOUSE_MOVED. Locations are + // in surface local, and pixel screen coordinates respectively. + gfx::Point pointer_surface_location; + gfx::Point pointer_screen_location_in_px; + + // Set for type == ET_TOUCH_*. Location is in dip screen coordinates. + gfx::Point touch_screen_location; // Set for type == ET_MOUSE_PRESSED || type == ET_MOUSE_RELEASED. uint32_t mouse_button = 0;
diff --git a/ui/ozone/platform/wayland/host/proxy/wayland_proxy.h b/ui/ozone/platform/wayland/host/proxy/wayland_proxy.h index 5f32460..82c056d 100644 --- a/ui/ozone/platform/wayland/host/proxy/wayland_proxy.h +++ b/ui/ozone/platform/wayland/host/proxy/wayland_proxy.h
@@ -14,10 +14,13 @@ struct wl_surface; namespace gfx { -class Rect; class Size; } // namespace gfx +namespace ui { +class WaylandWindow; +} + namespace wl { // A proxy interface to Ozone/Wayland that is used by input emulation. The @@ -64,6 +67,9 @@ // Returns wl_surface that backs the |widget|. virtual wl_surface* GetWlSurfaceForAcceleratedWidget( gfx::AcceleratedWidget widget) = 0; + // Returns WaylandWindow backed by |widget|. + virtual ui::WaylandWindow* GetWaylandWindowForAcceleratedWidget( + gfx::AcceleratedWidget widget) = 0; // Creates and returns a shm based wl_buffer with |buffer_size|. The shared // memory is hold until DestroyShmForWlBuffer is called. @@ -79,9 +85,6 @@ virtual ui::PlatformWindowType GetWindowType( gfx::AcceleratedWidget widget) = 0; - // Returns bounds in px of the window backed by |widget|. - virtual gfx::Rect GetWindowBounds(gfx::AcceleratedWidget widget) = 0; - virtual bool WindowHasPointerFocus(gfx::AcceleratedWidget widget) = 0; virtual bool WindowHasKeyboardFocus(gfx::AcceleratedWidget widget) = 0;
diff --git a/ui/ozone/platform/wayland/host/proxy/wayland_proxy_impl.cc b/ui/ozone/platform/wayland/host/proxy/wayland_proxy_impl.cc index 6117ed6..7942b4c 100644 --- a/ui/ozone/platform/wayland/host/proxy/wayland_proxy_impl.cc +++ b/ui/ozone/platform/wayland/host/proxy/wayland_proxy_impl.cc
@@ -49,6 +49,13 @@ return window->root_surface()->surface(); } +ui::WaylandWindow* WaylandProxyImpl::GetWaylandWindowForAcceleratedWidget( + gfx::AcceleratedWidget widget) { + auto* window = connection_->wayland_window_manager()->GetWindow(widget); + DCHECK(window); + return window; +} + wl_buffer* WaylandProxyImpl::CreateShmBasedWlBuffer( const gfx::Size& buffer_size) { ui::WaylandShmBuffer shm_buffer(connection_->wayland_buffer_factory(), @@ -78,12 +85,6 @@ return window->type(); } -gfx::Rect WaylandProxyImpl::GetWindowBounds(gfx::AcceleratedWidget widget) { - auto* window = connection_->wayland_window_manager()->GetWindow(widget); - DCHECK(window); - return window->GetBoundsInPixels(); -} - bool WaylandProxyImpl::WindowHasPointerFocus(gfx::AcceleratedWidget widget) { auto* window = connection_->wayland_window_manager()->GetWindow(widget); DCHECK(window);
diff --git a/ui/ozone/platform/wayland/host/proxy/wayland_proxy_impl.h b/ui/ozone/platform/wayland/host/proxy/wayland_proxy_impl.h index 4bb91c8..69a962a9 100644 --- a/ui/ozone/platform/wayland/host/proxy/wayland_proxy_impl.h +++ b/ui/ozone/platform/wayland/host/proxy/wayland_proxy_impl.h
@@ -14,6 +14,7 @@ namespace ui { class WaylandConnection; class WaylandShmBuffer; +class WaylandWindow; } // namespace ui namespace wl { @@ -30,11 +31,12 @@ void RoundTripQueue() override; wl_surface* GetWlSurfaceForAcceleratedWidget( gfx::AcceleratedWidget widget) override; + ui::WaylandWindow* GetWaylandWindowForAcceleratedWidget( + gfx::AcceleratedWidget widget) override; wl_buffer* CreateShmBasedWlBuffer(const gfx::Size& buffer_size) override; void DestroyShmForWlBuffer(wl_buffer* buffer) override; void ScheduleDisplayFlush() override; ui::PlatformWindowType GetWindowType(gfx::AcceleratedWidget widget) override; - gfx::Rect GetWindowBounds(gfx::AcceleratedWidget widget) override; bool WindowHasPointerFocus(gfx::AcceleratedWidget widget) override; bool WindowHasKeyboardFocus(gfx::AcceleratedWidget widget) override;
diff --git a/ui/ozone/platform/wayland/host/wayland_toplevel_window.cc b/ui/ozone/platform/wayland/host/wayland_toplevel_window.cc index 27b58bf..90900dc 100644 --- a/ui/ozone/platform/wayland/host/wayland_toplevel_window.cc +++ b/ui/ozone/platform/wayland/host/wayland_toplevel_window.cc
@@ -329,6 +329,10 @@ return window_shape_in_dips_; } +bool WaylandToplevelWindow::IsScreenCoordinatesEnabled() const { + return screen_coordinates_enabled_; +} + // static void WaylandToplevelWindow::AllowSettingDecorationInsetsForTest(bool allow) { decorations_allowed_for_test_ = allow;
diff --git a/ui/ozone/platform/wayland/host/wayland_toplevel_window.h b/ui/ozone/platform/wayland/host/wayland_toplevel_window.h index 50c2918..936c326 100644 --- a/ui/ozone/platform/wayland/host/wayland_toplevel_window.h +++ b/ui/ozone/platform/wayland/host/wayland_toplevel_window.h
@@ -80,10 +80,7 @@ // WaylandWindow overrides: absl::optional<std::vector<gfx::Rect>> GetWindowShape() const override; - - bool screen_coordinates_enabled() const { - return screen_coordinates_enabled_; - } + bool IsScreenCoordinatesEnabled() const override; // Client-side decorations on Wayland take some portion of the window surface, // and when they are turned on or off, the window geometry is changed. That
diff --git a/ui/ozone/platform/wayland/host/wayland_window.cc b/ui/ozone/platform/wayland/host/wayland_window.cc index 9c16d03..c2ba75c 100644 --- a/ui/ozone/platform/wayland/host/wayland_window.cc +++ b/ui/ozone/platform/wayland/host/wayland_window.cc
@@ -709,6 +709,10 @@ return nullptr; } +bool WaylandWindow::IsScreenCoordinatesEnabled() const { + return false; +} + uint32_t WaylandWindow::DispatchEventToDelegate( const PlatformEvent& native_event) { bool handled = DispatchEventFromNativeUiEvent(
diff --git a/ui/ozone/platform/wayland/host/wayland_window.h b/ui/ozone/platform/wayland/host/wayland_window.h index e189945..9acd02b 100644 --- a/ui/ozone/platform/wayland/host/wayland_window.h +++ b/ui/ozone/platform/wayland/host/wayland_window.h
@@ -309,6 +309,9 @@ // WaylandPopup, if |this| has type of WaylandPopup. virtual WaylandPopup* AsWaylandPopup(); + // Returns true if the window's bounds is in screen coordinates. + virtual bool IsScreenCoordinatesEnabled() const; + scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner() { return ui_task_runner_; }
diff --git a/ui/ozone/platform/wayland/test/wayland_ozone_ui_controls_test_helper.cc b/ui/ozone/platform/wayland/test/wayland_ozone_ui_controls_test_helper.cc index 8567f42..5db8b47 100644 --- a/ui/ozone/platform/wayland/test/wayland_ozone_ui_controls_test_helper.cc +++ b/ui/ozone/platform/wayland/test/wayland_ozone_ui_controls_test_helper.cc
@@ -167,13 +167,13 @@ void WaylandOzoneUIControlsTestHelper::SendMouseMotionNotifyEvent( gfx::AcceleratedWidget widget, const gfx::Point& mouse_loc, - const gfx::Point& mouse_root_loc, + const gfx::Point& mouse_screen_loc_in_px, base::OnceClosure closure) { WaylandGlobalEventWaiter::Create( WaylandGlobalEventWaiter::WaylandEventType::kMotion, mouse_loc, std::move(closure), input_emulate_.get()); - - input_emulate_->EmulatePointerMotion(widget, mouse_loc); + input_emulate_->EmulatePointerMotion(widget, mouse_loc, + mouse_screen_loc_in_px); } void WaylandOzoneUIControlsTestHelper::SendMouseEvent( @@ -182,7 +182,7 @@ int button_state, int accelerator_state, const gfx::Point& mouse_loc, - const gfx::Point& mouse_root_loc, + const gfx::Point& mouse_screen_loc_in_px, base::OnceClosure closure) { uint32_t changed_button = 0; switch (type) { @@ -208,7 +208,7 @@ accelerator_state & ui_controls::kCommand, {}, true); } - SendMouseMotionNotifyEvent(widget, mouse_loc, mouse_root_loc, {}); + SendMouseMotionNotifyEvent(widget, mouse_loc, mouse_screen_loc_in_px, {}); WaylandGlobalEventWaiter::Create( WaylandGlobalEventWaiter::WaylandEventType::kButton, changed_button,
diff --git a/ui/views/BUILD.gn b/ui/views/BUILD.gn index 07b123d8..f681d6de 100644 --- a/ui/views/BUILD.gn +++ b/ui/views/BUILD.gn
@@ -133,6 +133,7 @@ "controls/image_view_base.h", "controls/label.h", "controls/link.h", + "controls/link_fragment.h", "controls/menu/menu_config.h", "controls/menu/menu_controller.h", "controls/menu/menu_controller_delegate.h", @@ -360,6 +361,7 @@ "controls/image_view_base.cc", "controls/label.cc", "controls/link.cc", + "controls/link_fragment.cc", "controls/menu/menu_config.cc", "controls/menu/menu_controller.cc", "controls/menu/menu_delegate.cc", @@ -1141,6 +1143,7 @@ "controls/editable_combobox/editable_combobox_unittest.cc", "controls/image_view_unittest.cc", "controls/label_unittest.cc", + "controls/link_fragment_unittest.cc", "controls/link_unittest.cc", "controls/menu/menu_controller_unittest.cc", "controls/menu/menu_item_view_unittest.cc",
diff --git a/ui/views/controls/link.cc b/ui/views/controls/link.cc index b8d7020..522d723 100644 --- a/ui/views/controls/link.cc +++ b/ui/views/controls/link.cc
@@ -59,6 +59,10 @@ RecalculateFont(); } +bool Link::GetForceUnderline() const { + return force_underline_; +} + ui::Cursor Link::GetCursor(const ui::MouseEvent& event) { if (!GetEnabled()) return ui::Cursor(); @@ -231,6 +235,7 @@ BEGIN_METADATA(Link, Label) ADD_READONLY_PROPERTY_METADATA(SkColor, Color, ui::metadata::SkColorConverter) +ADD_PROPERTY_METADATA(bool, ForceUnderline) END_METADATA } // namespace views
diff --git a/ui/views/controls/link.h b/ui/views/controls/link.h index 405b605..115976c5 100644 --- a/ui/views/controls/link.h +++ b/ui/views/controls/link.h
@@ -61,6 +61,7 @@ SkColor GetColor() const; void SetForceUnderline(bool force_underline); + bool GetForceUnderline() const; // Label: ui::Cursor GetCursor(const ui::MouseEvent& event) override; @@ -84,12 +85,12 @@ bool IsSelectionSupported() const override; private: + virtual void RecalculateFont(); + void SetPressed(bool pressed); void OnClick(const ui::Event& event); - void RecalculateFont(); - void ConfigureFocus(); ClickedCallback callback_;
diff --git a/ui/views/controls/link_fragment.cc b/ui/views/controls/link_fragment.cc new file mode 100644 index 0000000..9f44db5 --- /dev/null +++ b/ui/views/controls/link_fragment.cc
@@ -0,0 +1,88 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/views/controls/link_fragment.h" + +#include <string> + +#include "ui/base/metadata/metadata_impl_macros.h" +#include "ui/views/controls/link.h" +#include "ui/views/style/typography.h" + +namespace views { + +LinkFragment::LinkFragment(const std::u16string& title, + int text_context, + int text_style, + LinkFragment* other_fragment) + : Link(title, text_context, text_style), + prev_fragment_(this), + next_fragment_(this) { + // Connect to the previous fragment if it exists. + if (other_fragment) + Connect(other_fragment); +} + +LinkFragment::~LinkFragment() { + Disconnect(); +} + +void LinkFragment::Connect(LinkFragment* other_fragment) { + DCHECK(prev_fragment_ == this); + DCHECK(next_fragment_ == this); + DCHECK(other_fragment); + + next_fragment_ = other_fragment->next_fragment_; + other_fragment->next_fragment_->prev_fragment_ = this; + prev_fragment_ = other_fragment; + other_fragment->next_fragment_ = this; +} + +void LinkFragment::Disconnect() { + DCHECK((prev_fragment_ != this) == (next_fragment_ != this)); + if (prev_fragment_ != this) { + prev_fragment_->next_fragment_ = next_fragment_; + next_fragment_->prev_fragment_ = prev_fragment_; + } +} + +bool LinkFragment::IsUnderlined() const { + return GetEnabled() && + (HasFocus() || IsMouseHovered() || GetForceUnderline()); +} + +void LinkFragment::RecalculateFont() { + // Check whether any link fragment should be underlined. + bool should_be_underlined = IsUnderlined(); + for (LinkFragment* current_fragment = next_fragment_; + !should_be_underlined && current_fragment != this; + current_fragment = current_fragment->next_fragment_) { + should_be_underlined = current_fragment->IsUnderlined(); + } + + // If the style differs from the current one, update. + if ((font_list().GetFontStyle() & gfx::Font::UNDERLINE) != + should_be_underlined) { + auto MaybeUpdateStyle = [should_be_underlined](LinkFragment* fragment) { + const int style = fragment->font_list().GetFontStyle(); + const int intended_style = should_be_underlined + ? (style | gfx::Font::UNDERLINE) + : (style & ~gfx::Font::UNDERLINE); + fragment->Label::SetFontList( + fragment->font_list().DeriveWithStyle(intended_style)); + fragment->SchedulePaint(); + }; + MaybeUpdateStyle(this); + for (LinkFragment* current_fragment = next_fragment_; + current_fragment != this; + current_fragment = current_fragment->next_fragment_) { + MaybeUpdateStyle(current_fragment); + } + } +} + +BEGIN_METADATA(LinkFragment, Link) +END_METADATA + +} // namespace views
diff --git a/ui/views/controls/link_fragment.h b/ui/views/controls/link_fragment.h new file mode 100644 index 0000000..20a7b87 --- /dev/null +++ b/ui/views/controls/link_fragment.h
@@ -0,0 +1,57 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_VIEWS_CONTROLS_LINK_FRAGMENT_H_ +#define UI_VIEWS_CONTROLS_LINK_FRAGMENT_H_ + +#include "base/memory/raw_ptr.h" +#include "ui/views/controls/link.h" +#include "ui/views/metadata/view_factory.h" +#include "ui/views/style/typography.h" + +namespace views { + +// A `LinkFragment` can be used to represent a logical link that spans across +// multiple lines. Connected `LinkFragment`s adjust their style if any single +// one of them is hovered over of focused. +class VIEWS_EXPORT LinkFragment : public Link { + public: + METADATA_HEADER(LinkFragment); + + explicit LinkFragment(const std::u16string& title = std::u16string(), + int text_context = style::CONTEXT_LABEL, + int text_style = style::STYLE_LINK, + LinkFragment* other_fragment = nullptr); + ~LinkFragment() override; + + LinkFragment(const LinkFragment&) = delete; + LinkFragment& operator=(const LinkFragment&) = delete; + + private: + // Returns whether this fragment indicates that the entire link represented + // by it should be underlined. + bool IsUnderlined() const; + + // Connects `this` to the `other_fragment`. + void Connect(LinkFragment* other_fragment); + + // Disconnects `this` from any other fragments that it may be connected to. + void Disconnect(); + + // Recalculates the font style for this link fragment and, if it is changed, + // updates both this fragment and all other that are connected to it. + void RecalculateFont() override; + + // Pointers to the previous and the next `LinkFragment` if the logical link + // represented by `this` consists of multiple such fragments (e.g. due to + // line breaks). + // If the logical link is just a single `LinkFragment` component, then these + // pointers point to `this`. + raw_ptr<LinkFragment> prev_fragment_; + raw_ptr<LinkFragment> next_fragment_; +}; + +} // namespace views + +#endif // UI_VIEWS_CONTROLS_LINK_FRAGMENT_H_
diff --git a/ui/views/controls/link_fragment_unittest.cc b/ui/views/controls/link_fragment_unittest.cc new file mode 100644 index 0000000..99ee662 --- /dev/null +++ b/ui/views/controls/link_fragment_unittest.cc
@@ -0,0 +1,140 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/views/controls/link_fragment.h" + +#include <array> +#include <memory> + +#include "base/memory/raw_ptr.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/events/test/event_generator.h" +#include "ui/views/controls/base_control_test_widget.h" +#include "ui/views/test/view_metadata_test_utils.h" +#include "ui/views/widget/widget.h" + +namespace views { + +namespace { + +constexpr char16_t kLinkLabel[] = u"Test label"; + +class LinkFragmentTest : public test::BaseControlTestWidget { + public: + LinkFragmentTest() { + for (auto& fragment : fragments_) { + fragment = nullptr; + } + } + ~LinkFragmentTest() override = default; + + void SetUp() override { + test::BaseControlTestWidget::SetUp(); + + event_generator_ = std::make_unique<ui::test::EventGenerator>( + GetContext(), widget()->GetNativeWindow()); + } + + protected: + void CreateWidgetContent(View* container) override { + // Fragment 0 is stand-alone. + fragments_[0] = + container->AddChildView(std::make_unique<LinkFragment>(kLinkLabel)); + gfx::Rect current_rect = + gfx::ScaleToEnclosedRect(container->GetLocalBounds(), 0.3f); + fragments_[0]->SetBoundsRect(current_rect); + int width = current_rect.width(); + + // Fragments 1 and 2 are connected. + current_rect.Offset(width, 0); + fragments_[1] = + container->AddChildView(std::make_unique<LinkFragment>(kLinkLabel)); + fragments_[1]->SetBoundsRect(current_rect); + + current_rect.Offset(width, 0); + fragments_[2] = container->AddChildView(std::make_unique<LinkFragment>( + kLinkLabel, style::CONTEXT_LABEL, style::STYLE_LINK, fragment(1))); + fragments_[2]->SetBoundsRect(current_rect); + } + + LinkFragment* fragment(size_t index) { + DCHECK_LT(index, 3u); + return fragments_[index]; + } + ui::test::EventGenerator* event_generator() { return event_generator_.get(); } + + // Returns bounds of the fragment. + gfx::Rect GetBoundsForFragment(size_t index) { + return fragment(index)->GetBoundsInScreen(); + } + + private: + std::array<raw_ptr<LinkFragment>, 3> fragments_; + std::unique_ptr<ui::test::EventGenerator> event_generator_; +}; + +} // namespace + +TEST_F(LinkFragmentTest, Metadata) { + for (size_t index = 0; index < 3; ++index) { + // Needed to avoid failing DCHECK when setting maximum width. + fragment(index)->SetMultiLine(true); + test::TestViewMetadata(fragment(index)); + } +} + +// Tests that hovering and unhovering a link adds and removes an underline +// under all connected fragments. +TEST_F(LinkFragmentTest, TestUnderlineOnHover) { + // A non-hovered link fragment should not be underlined. + const gfx::Point point_outside = + GetBoundsForFragment(2).bottom_right() + gfx::Vector2d(1, 1); + event_generator()->MoveMouseTo(point_outside); + EXPECT_FALSE(fragment(0)->IsMouseHovered()); + + const auto is_underlined = [this](size_t index) { + return !!(fragment(index)->font_list().GetFontStyle() & + gfx::Font::UNDERLINE); + }; + EXPECT_FALSE(is_underlined(0)); + + // Hovering the first link fragment underlines it. + event_generator()->MoveMouseTo(GetBoundsForFragment(0).CenterPoint()); + EXPECT_TRUE(fragment(0)->IsMouseHovered()); + EXPECT_TRUE(is_underlined(0)); + // The other link fragments stay non-hovered. + EXPECT_FALSE(is_underlined(1)); + EXPECT_FALSE(is_underlined(2)); + + // Un-hovering the link removes the underline again. + event_generator()->MoveMouseTo(point_outside); + EXPECT_FALSE(fragment(0)->IsMouseHovered()); + EXPECT_FALSE(is_underlined(0)); + EXPECT_FALSE(is_underlined(1)); + EXPECT_FALSE(is_underlined(2)); + + // Hovering the second link fragment underlines both the second and the + // third fragment. + event_generator()->MoveMouseTo(GetBoundsForFragment(1).CenterPoint()); + EXPECT_TRUE(fragment(1)->IsMouseHovered()); + EXPECT_FALSE(fragment(2)->IsMouseHovered()); + EXPECT_FALSE(is_underlined(0)); + EXPECT_TRUE(is_underlined(1)); + EXPECT_TRUE(is_underlined(2)); + + // The same is true for hovering the third fragment. + event_generator()->MoveMouseTo(GetBoundsForFragment(2).CenterPoint()); + EXPECT_TRUE(fragment(2)->IsMouseHovered()); + EXPECT_FALSE(is_underlined(0)); + EXPECT_TRUE(is_underlined(1)); + EXPECT_TRUE(is_underlined(2)); + + // Moving outside removes the underline again. + event_generator()->MoveMouseTo(point_outside); + EXPECT_FALSE(is_underlined(0)); + EXPECT_FALSE(is_underlined(1)); + EXPECT_FALSE(is_underlined(2)); +} + +} // namespace views
diff --git a/ui/views/controls/styled_label.cc b/ui/views/controls/styled_label.cc index 8d03c42..283c51f 100644 --- a/ui/views/controls/styled_label.cc +++ b/ui/views/controls/styled_label.cc
@@ -22,6 +22,8 @@ #include "ui/gfx/text_elider.h" #include "ui/gfx/text_utils.h" #include "ui/views/controls/label.h" +#include "ui/views/controls/link.h" +#include "ui/views/controls/link_fragment.h" #include "ui/views/view_class_properties.h" namespace views { @@ -332,8 +334,8 @@ } views::Link* StyledLabel::GetFirstLinkForTesting() { - const auto it = - base::ranges::find(children(), Link::kViewClassName, &View::GetClassName); + const auto it = base::ranges::find(children(), LinkFragment::kViewClassName, + &View::GetClassName); DCHECK(it != children().cend()); return static_cast<views::Link*>(*it); } @@ -365,6 +367,11 @@ // Try to preserve leading whitespace on the first line. bool can_trim_leading_whitespace = false; StyleRanges::const_iterator current_range = style_ranges_.begin(); + + // A pointer to the previous link fragment if a logical link consists of + // multiple `LinkFragment` elements. + LinkFragment* previous_link_fragment = nullptr; + for (std::u16string remaining_string = text_; content_width > 0 && !remaining_string.empty();) { layout_size_info_.line_sizes.emplace_back(0, line_height); @@ -475,18 +482,26 @@ if (chunk.size() > range.end() - position) chunk = chunk.substr(0, range.end() - position); - if (!custom_view) - label = CreateLabel(chunk, style_info, range); + if (!custom_view) { + label = + CreateLabel(chunk, style_info, range, &previous_link_fragment); + } else { + previous_link_fragment = nullptr; + } - if (position + chunk.size() >= range.end()) + if (position + chunk.size() >= range.end()) { ++current_range; + // Links do not connect across separate style ranges. + previous_link_fragment = nullptr; + } } else { chunk = substrings[0]; if (position + chunk.size() > range.start()) chunk = chunk.substr(0, range.start() - position); // This chunk is normal text. - label = CreateLabel(chunk, default_style, range); + label = + CreateLabel(chunk, default_style, range, &previous_link_fragment); } View* child_view = custom_view ? custom_view : label.get(); @@ -533,14 +548,17 @@ std::unique_ptr<Label> StyledLabel::CreateLabel( const std::u16string& text, const RangeStyleInfo& style_info, - const gfx::Range& range) const { + const gfx::Range& range, + LinkFragment** previous_link_fragment) const { std::unique_ptr<Label> result; if (style_info.text_style == style::STYLE_LINK) { // Nothing should (and nothing does) use a custom font for links. DCHECK(!style_info.custom_font); - // Note this ignores |default_text_style_|, in favor of style::STYLE_LINK. - auto link = std::make_unique<Link>(text, text_context_); + // Note this ignores |default_text_style_|, in favor of `style::STYLE_LINK`. + auto link = std::make_unique<LinkFragment>( + text, text_context_, style::STYLE_LINK, *previous_link_fragment); + *previous_link_fragment = link.get(); link->SetCallback(style_info.callback); if (!style_info.accessible_name.empty()) link->SetAccessibleName(style_info.accessible_name); @@ -576,7 +594,7 @@ // TODO(kylixrd): Should updating the label background color even be // allowed if there are custom views? DCHECK((child->GetClassName() == Label::kViewClassName) || - (child->GetClassName() == Link::kViewClassName)); + (child->GetClassName() == LinkFragment::kViewClassName)); static_cast<Label*>(child)->SetBackgroundColor(new_color); } }
diff --git a/ui/views/controls/styled_label.h b/ui/views/controls/styled_label.h index 0693236..3b2bd66 100644 --- a/ui/views/controls/styled_label.h +++ b/ui/views/controls/styled_label.h
@@ -30,6 +30,7 @@ class Label; class Link; +class LinkFragment; // A class which can apply mixed styles to a block of text. Currently, text is // always multiline. Trailing whitespace in the styled label text is not @@ -215,9 +216,11 @@ void CalculateLayout(int width) const; // Creates a Label for a given |text|, |style_info|, and |range|. - std::unique_ptr<Label> CreateLabel(const std::u16string& text, - const RangeStyleInfo& style_info, - const gfx::Range& range) const; + std::unique_ptr<Label> CreateLabel( + const std::u16string& text, + const RangeStyleInfo& style_info, + const gfx::Range& range, + LinkFragment** previous_link_component) const; // Update the label background color from the theme or // |displayed_on_background_color_| if set.
diff --git a/ui/views/controls/styled_label_unittest.cc b/ui/views/controls/styled_label_unittest.cc index 2c4bdb0..699d1f2 100644 --- a/ui/views/controls/styled_label_unittest.cc +++ b/ui/views/controls/styled_label_unittest.cc
@@ -23,6 +23,7 @@ #include "ui/gfx/font_list.h" #include "ui/views/border.h" #include "ui/views/controls/link.h" +#include "ui/views/controls/link_fragment.h" #include "ui/views/style/typography.h" #include "ui/views/test/test_layout_provider.h" #include "ui/views/test/test_views.h" @@ -385,8 +386,9 @@ ASSERT_EQ(3u, styled()->children().size()); EXPECT_EQ(SK_ColorBLUE, LabelAt(styled(), 0)->GetEnabledColor()); - EXPECT_EQ(kDefaultLinkColor, - LabelAt(styled(), 1, Link::kViewClassName)->GetEnabledColor()); + EXPECT_EQ( + kDefaultLinkColor, + LabelAt(styled(), 1, LinkFragment::kViewClassName)->GetEnabledColor()); EXPECT_EQ(kDefaultTextColor, LabelAt(styled(), 2)->GetEnabledColor()); // Test adjusted color readability.
diff --git a/ui/views/test/ui_controls_factory_desktop_aura_ozone.cc b/ui/views/test/ui_controls_factory_desktop_aura_ozone.cc index 8205a81..a364780e 100644 --- a/ui/views/test/ui_controls_factory_desktop_aura_ozone.cc +++ b/ui/views/test/ui_controls_factory_desktop_aura_ozone.cc
@@ -161,8 +161,8 @@ int y, base::OnceClosure closure) override { gfx::Point screen_location(x, y); - gfx::Point root_location = screen_location; aura::Window* root_window; + // Touch release events might not have coordinates that match any window, so // just use whichever window is on top. if (action & ui_controls::RELEASE) @@ -170,19 +170,9 @@ else root_window = RootWindowForPoint(screen_location); - aura::client::ScreenPositionClient* screen_position_client = - aura::client::GetScreenPositionClient(root_window); - if (screen_position_client) { - screen_position_client->ConvertPointFromScreen(root_window, - &root_location); - } - - aura::WindowTreeHost* host = root_window->GetHost(); - gfx::Point screen_point(root_location); - host->ConvertDIPToScreenInPixels(&screen_point); ozone_ui_controls_test_helper_->SendTouchEvent( root_window->GetHost()->GetAcceleratedWidget(), action, id, - screen_point, std::move(closure)); + screen_location, std::move(closure)); return true; }
diff --git a/ui/webui/resources/BUILD.gn b/ui/webui/resources/BUILD.gn index b5d02bf..cc04430f 100644 --- a/ui/webui/resources/BUILD.gn +++ b/ui/webui/resources/BUILD.gn
@@ -186,6 +186,7 @@ "cr_elements/policy/cr_policy_pref_indicator.m.d.ts", "cr_elements/policy/cr_tooltip_icon.m.d.ts", "js/cr/ui/focus_row_behavior.m.d.ts", + "js/cr/ui/list.m.d.ts", "js/i18n_behavior.m.d.ts", "js/list_property_update_behavior.m.d.ts", "js/parse_html_subset.m.d.ts", @@ -216,7 +217,6 @@ "cr_components/chromeos/smb_shares/add_smb_share_dialog.d.ts", "cr_components/chromeos/smb_shares/smb_browser_proxy.d.ts", "js/cr/ui/grid.m.d.ts", - "js/cr/ui/list.m.d.ts", ] } @@ -234,11 +234,16 @@ "js/color_utils.js", "js/cr/event_target.m.js", "js/cr.m.js", + "js/cr/ui.m.js", + "js/cr/ui/array_data_model.m.js", "js/cr/ui/drag_wrapper.js", "js/cr/ui/focus_grid.js", "js/cr/ui/focus_outline_manager.m.js", "js/cr/ui/focus_row.m.js", "js/cr/ui/keyboard_shortcut_list.m.js", + "js/cr/ui/list_item.m.js", + "js/cr/ui/list_selection_controller.m.js", + "js/cr/ui/list_selection_model.m.js", "js/cr/ui/store.js", "js/event_tracker.m.js", "js/load_time_data.m.js", @@ -252,18 +257,13 @@ generate_definitions_js_files += [ "js/cr/ui/menu.m.js", "js/cr/ui/command.m.js", - "js/cr/ui.m.js", "js/cr/ui/position_util.m.js", ] } if (is_chromeos_ash) { generate_definitions_js_files += [ - "js/cr/ui/array_data_model.m.js", - "js/cr/ui/list_item.m.js", - "js/cr/ui/list_selection_model.m.js", "js/cr/ui/list_single_selection_model.m.js", - "js/cr/ui/list_selection_controller.m.js", "js/cr/ui/store_client.js", ] }
diff --git a/ui/webui/resources/js/list_property_update_behavior.m.d.ts b/ui/webui/resources/js/list_property_update_behavior.m.d.ts index 9fc0420..bacfb0b 100644 --- a/ui/webui/resources/js/list_property_update_behavior.m.d.ts +++ b/ui/webui/resources/js/list_property_update_behavior.m.d.ts
@@ -4,9 +4,8 @@ export interface ListPropertyUpdateBehavior { updateList( - propertyPath: string, - identityGetter: ((arg0: any) => (any | string)), - updatedList: object[], identityBasedUpdate?: boolean): boolean; + propertyPath: string, identityGetter: ((arg0: any) => (any | string)), + updatedList: any[], identityBasedUpdate?: boolean): boolean; } declare const ListPropertyUpdateBehavior: object;
diff --git a/ui/webui/resources/js/static_types.d.ts b/ui/webui/resources/js/static_types.d.ts index 3e605bd..75955698 100644 --- a/ui/webui/resources/js/static_types.d.ts +++ b/ui/webui/resources/js/static_types.d.ts
@@ -4,5 +4,7 @@ export function getTrustedHTML(literal: string[]| TemplateStringsArray): TrustedHTML|string; -export function getTrustedScript(literal: string[]): TrustedScript|string; -export function getTrustedScriptURL(literal: string[]): TrustedScriptURL|string; +export function getTrustedScript(literal: string[]| + TemplateStringsArray): TrustedScript|string; +export function getTrustedScriptURL(literal: string[]|TemplateStringsArray): + TrustedScriptURL|string;
diff --git a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/TabCallbackTest.java b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/TabCallbackTest.java index a316ed8..f688a20 100644 --- a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/TabCallbackTest.java +++ b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/TabCallbackTest.java
@@ -21,6 +21,7 @@ import org.chromium.base.test.util.Criteria; import org.chromium.base.test.util.CriteriaHelper; import org.chromium.base.test.util.DisableIf; +import org.chromium.base.test.util.DisabledTest; import org.chromium.content_public.browser.test.util.TestThreadUtils; import org.chromium.content_public.browser.test.util.TestTouchUtils; import org.chromium.weblayer.ContextMenuParams; @@ -384,6 +385,7 @@ @Test @SmallTest + @DisabledTest(message = "crbug.com/1339982") public void testScrollNotificationDirectionChange() throws TimeoutException { final String url = mActivityTestRule.getTestDataURL("tall_page.html"); InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(url);
diff --git a/weblayer/browser/feature_list_creator.cc b/weblayer/browser/feature_list_creator.cc index 7030462..592ff38a 100644 --- a/weblayer/browser/feature_list_creator.cc +++ b/weblayer/browser/feature_list_creator.cc
@@ -11,6 +11,7 @@ #include "components/prefs/pref_service.h" #include "components/variations/service/variations_service.h" #include "components/variations/variations_crash_keys.h" +#include "components/variations/variations_switches.h" #include "content/public/browser/network_service_instance.h" #include "content/public/common/content_switch_dependent_feature_overrides.h" #include "services/network/public/cpp/shared_url_loader_factory.h" @@ -106,10 +107,13 @@ std::vector<std::string> variation_ids; auto feature_list = std::make_unique<base::FeatureList>(); + const base::CommandLine* command_line = + base::CommandLine::ForCurrentProcess(); variations_service_->SetUpFieldTrials( variation_ids, - content::GetSwitchDependentFeatureOverrides( - *base::CommandLine::ForCurrentProcess()), + command_line->GetSwitchValueASCII( + variations::switches::kForceVariationIds), + content::GetSwitchDependentFeatureOverrides(*command_line), std::move(feature_list), &weblayer_field_trials_); variations::InitCrashKeys(); #else