diff --git a/BUILD.gn b/BUILD.gn index 8f37cd2..28ba2fe1 100644 --- a/BUILD.gn +++ b/BUILD.gn
@@ -390,7 +390,6 @@ "//third_party/androidx_javascriptengine", "//third_party/catapult/devil", "//third_party/jni_zero/sample:jni_generator_tests", - "//third_party/jni_zero/test:test_jni_apk", "//third_party/r8:custom_d8_java", "//tools/android:android_tools", "//tools/android:memconsumer",
diff --git a/DEPS b/DEPS index a9048e52..e02b5fc 100644 --- a/DEPS +++ b/DEPS
@@ -308,15 +308,15 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling V8 # and whatever else without interference from each other. - 'src_internal_revision': 'f5048154f82911c84ab994c1546290d567a75ec4', + 'src_internal_revision': 'e992b1f6ab810377d0c9cb55aa6a82752df9e8d9', # 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': '734953afbc19e81884e465829685d2574fe40fc9', + 'skia_revision': '170fbb029e61a116431fd96a35eeeca8ee22c2b2', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling V8 # and whatever else without interference from each other. - 'v8_revision': 'c3a7867744ac5753621f6ff77120217cf165081d', + 'v8_revision': 'b53eb026438b102249f055de1af5d8aff5bb0779', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling ANGLE # and whatever else without interference from each other. @@ -339,7 +339,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling Fuchsia sdk # and whatever else without interference from each other. - 'fuchsia_version': '18.20240215.1.1', + 'fuchsia_version': 'version:18.20240215.1.1', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling google-toolbox-for-mac # and whatever else without interference from each other. @@ -383,11 +383,11 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling catapult # and whatever else without interference from each other. - 'catapult_revision': '6f4a0d6c8731078f74f3de4fc92748c125da43d6', + 'catapult_revision': '7fccadad2a22e004112191dcd082108eb8297b14', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling chromium_variations # and whatever else without interference from each other. - 'chromium_variations_revision': '1fae85ca70bdf203941f8ac90392e7d471247147', + 'chromium_variations_revision': 'd1a16c439233c3c7199fcc3f0507b1d012611ab9', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling CrossBench # and whatever else without interference from each other. @@ -427,7 +427,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': '996ab52aeac453060d7677672ce28e0504ff75f3', + 'dawn_revision': '7b482f48e63500c46952160a5b71b5bb91c746f0', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling feed # and whatever else without interference from each other. @@ -479,7 +479,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. - 'libcxxabi_revision': '5b35c9f06c3f8f17b43bd3da9527d6fecdf916c2', + 'libcxxabi_revision': '204deaa9c53f76d6f23e0d119fc0110adc3ea6f2', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling feed # and whatever else without interference from each other. @@ -827,12 +827,12 @@ 'src/clank': { 'url': Var('chrome_git') + '/clank/internal/apps.git' + '@' + - '86871e647c038acdf24601d6955b545a59642446', + '15ea959101d2efe298a00841d9f8c015a387b57b', 'condition': 'checkout_android and checkout_src_internal', }, 'src/docs/website': { - 'url': Var('chromium_git') + '/website.git' + '@' + 'ecc6daec0bc6bc14edaad139f726d7a0154d4572', + 'url': Var('chromium_git') + '/website.git' + '@' + '05de69cbec5c294245bfb96d9be826065c05e6eb', }, 'src/ios/third_party/earl_grey2/src': { @@ -1164,7 +1164,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' + '@' + '21c56f5c330bab014098f49c6d5b9f2f9cba3473', + 'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '7efba5ebf4460a8038d1d8155528d960f8df82ab', 'condition': 'checkout_chromeos', }, @@ -1199,13 +1199,13 @@ }, 'src/third_party/depot_tools': - Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '4df61147ba67316806617f74347f408d2e4ff2f1', + Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'd972b831c3ca2be979c4009a1fb66baca9dc0b77', 'src/third_party/devtools-frontend/src': Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'), 'src/third_party/devtools-frontend-internal': { - 'url': Var('chrome_git') + '/devtools/devtools-internal.git' + '@' + '2173cbdf60dac9a327293104c19c93e63a3e46ee', + 'url': Var('chrome_git') + '/devtools/devtools-internal.git' + '@' + 'f1626580921976b4de940622ede2cdd7a6911c91', 'condition': 'checkout_src_internal', }, @@ -1665,7 +1665,7 @@ Var('pdfium_git') + '/pdfium.git' + '@' + Var('pdfium_revision'), 'src/third_party/perfetto': - Var('android_git') + '/platform/external/perfetto.git' + '@' + '6427f365ba48d17a474a837b2596ba683655386e', + Var('android_git') + '/platform/external/perfetto.git' + '@' + 'cd05247e5cb7251177910169dfe482f586f6a5bd', 'src/third_party/perl': { 'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '8ef97ff3b7332e38e61b347a2fbed425a4617151', @@ -1850,7 +1850,7 @@ Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + '98673cc24786be6c10dd8908e0b0b4ed27625c6a', 'src/third_party/webrtc': - Var('webrtc_git') + '/src.git' + '@' + '524a06bc5423ef2289bc6102ee5a2b9e747137cc', + Var('webrtc_git') + '/src.git' + '@' + 'a8c47276cb6d3188db8b5a4ba77abd07797c0071', # Wuffs' canonical repository is at github.com/google/wuffs, but we use # Skia's mirror of Wuffs, the same as in upstream Skia's DEPS file. @@ -4036,7 +4036,7 @@ 'src/components/optimization_guide/internal': { 'url': Var('chrome_git') + '/chrome/components/optimization_guide.git' + '@' + - 'cf123dadc9bb84e815991922cc9d4d9277b8b4e5', + 'ee19009832d5daab66094b1325d8d42ebb61e1ad', 'condition': 'checkout_src_internal', }, @@ -4096,7 +4096,7 @@ 'src/ios_internal': { 'url': Var('chrome_git') + '/chrome/ios_internal.git' + '@' + - '4f50ee554da55ee6d7c1382a8b06233257b9ee81', + 'e8610f3ba765ba96d259acd21b689db6e0cbd181', 'condition': 'checkout_ios and checkout_src_internal', },
diff --git a/android_webview/browser/aw_field_trials.cc b/android_webview/browser/aw_field_trials.cc index 50c5edb..ef37f20 100644 --- a/android_webview/browser/aw_field_trials.cc +++ b/android_webview/browser/aw_field_trials.cc
@@ -182,10 +182,6 @@ // FedCM is not yet supported on WebView. aw_feature_overrides.DisableFeature(::features::kFedCm); - // Storage Access permission prompts are not supported on WebView. - aw_feature_overrides.DisableFeature( - permissions::features::kPermissionStorageAccessAPI); - // Disable enhanced track-pad features until WebView's experiment // is fully rolled out to stable. aw_feature_overrides.DisableFeature(ui::kConvertTrackpadEventsToMouse);
diff --git a/ash/BUILD.gn b/ash/BUILD.gn index 73c3d5d..acc36ed 100644 --- a/ash/BUILD.gn +++ b/ash/BUILD.gn
@@ -540,6 +540,8 @@ "display/display_highlight_controller.h", "display/display_move_window_util.cc", "display/display_move_window_util.h", + "display/display_performance_mode_controller.cc", + "display/display_performance_mode_controller.h", "display/display_prefs.cc", "display/display_prefs.h", "display/display_shutdown_observer.cc", @@ -3422,6 +3424,7 @@ "display/display_highlight_controller_unittest.cc", "display/display_manager_unittest.cc", "display/display_move_window_util_unittest.cc", + "display/display_performance_mode_controller_unittest.cc", "display/display_prefs_unittest.cc", "display/display_util_unittest.cc", "display/extended_mouse_warp_controller_unittest.cc",
diff --git a/ash/accelerators/ash_accelerator_configuration.cc b/ash/accelerators/ash_accelerator_configuration.cc index c2f63e4..6317b6f 100644 --- a/ash/accelerators/ash_accelerator_configuration.cc +++ b/ash/accelerators/ash_accelerator_configuration.cc
@@ -19,7 +19,6 @@ #include "ash/session/session_controller_impl.h" #include "ash/shell.h" #include "base/containers/contains.h" -#include "base/containers/cxx20_erase.h" #include "base/containers/span.h" #include "base/logging.h" #include "base/metrics/histogram_functions.h" @@ -500,7 +499,7 @@ CHECK(*found_id == action_id); // Remove accelerator from lookup map. - base::Erase(found_accelerators_iter->second, accelerator); + std::erase(found_accelerators_iter->second, accelerator); // Remove accelerator from reverse lookup map. accelerator_to_id_.Erase(accelerator);
diff --git a/ash/app_list/app_list_controller_impl.cc b/ash/app_list/app_list_controller_impl.cc index a53f93dc..7aa0cad 100644 --- a/ash/app_list/app_list_controller_impl.cc +++ b/ash/app_list/app_list_controller_impl.cc
@@ -631,7 +631,7 @@ if (split_view_active) { foreground_windows = {split_view_controller->primary_window(), split_view_controller->secondary_window()}; - base::EraseIf(foreground_windows, + std::erase_if(foreground_windows, [](aura::Window* window) { return !window; }); } else if (!windows.empty() && !WindowState::Get(windows[0])->IsMinimized()) { foreground_windows.push_back(windows[0]);
diff --git a/ash/app_list/app_list_metrics.cc b/ash/app_list/app_list_metrics.cc index bdeb287c7d..cd0c823 100644 --- a/ash/app_list/app_list_metrics.cc +++ b/ash/app_list/app_list_metrics.cc
@@ -52,12 +52,6 @@ // mode AppList) and the Shelf. constexpr char kAppListAppLaunched[] = "Apps.AppListAppLaunchedV2"; -// UMA histograms that log app launches within the app list, and the shelf. -// Split depending on whether tablet mode is active or not. -constexpr char kAppLaunchInTablet[] = "Apps.AppList.AppLaunched.TabletMode"; -constexpr char kAppLaunchInClamshell[] = - "Apps.AppList.AppLaunched.ClamshellMode"; - // UMA histograms that log launcher workflow actions (launching an app, search // result, or a continue section task) in the app list UI. Split depending on // whether tablet mode is active or not. Note that unlike `kAppListAppLaunched` @@ -276,13 +270,6 @@ bool app_list_shown) { UMA_HISTOGRAM_ENUMERATION(kAppListAppLaunched, launched_from); - if (is_tablet_mode) { - base::UmaHistogramEnumeration(kAppLaunchInTablet, launched_from); - - } else { - base::UmaHistogramEnumeration(kAppLaunchInClamshell, launched_from); - } - if (!is_tablet_mode) { if (!app_list_shown) { UMA_HISTOGRAM_ENUMERATION(kAppListAppLaunchedClosed, launched_from);
diff --git a/ash/ash_strings.grd b/ash/ash_strings.grd index b5c4f44..6660155 100644 --- a/ash/ash_strings.grd +++ b/ash/ash_strings.grd
@@ -7680,6 +7680,9 @@ <message name="IDS_ASH_DOWNLOAD_COMMAND_TEXT_CANCEL" desc="Text of the command to cancel a download."> Cancel </message> + <message name="IDS_ASH_DOWNLOAD_COMMAND_TEXT_COPY_TO_CLIPBOARD" desc="Text of the command to copy the download file to clipboard."> + Copy to clipboard + </message> <message name="IDS_ASH_DOWNLOAD_COMMAND_TEXT_PAUSE" desc="Text of the command to pause a download."> Pause </message>
diff --git a/ash/ash_strings_grd/IDS_ASH_DOWNLOAD_COMMAND_TEXT_COPY_TO_CLIPBOARD.png.sha1 b/ash/ash_strings_grd/IDS_ASH_DOWNLOAD_COMMAND_TEXT_COPY_TO_CLIPBOARD.png.sha1 new file mode 100644 index 0000000..6032559 --- /dev/null +++ b/ash/ash_strings_grd/IDS_ASH_DOWNLOAD_COMMAND_TEXT_COPY_TO_CLIPBOARD.png.sha1
@@ -0,0 +1 @@ +78af3668f6d2945de8f98d57b602be639ddb81e8 \ No newline at end of file
diff --git a/ash/capture_mode/capture_mode_camera_controller.cc b/ash/capture_mode/capture_mode_camera_controller.cc index 5c1b583..76421f8 100644 --- a/ash/capture_mode/capture_mode_camera_controller.cc +++ b/ash/capture_mode/capture_mode_camera_controller.cc
@@ -6,6 +6,7 @@ #include <algorithm> #include <cstring> +#include <vector> #include "ash/accessibility/accessibility_controller.h" #include "ash/capture_mode/capture_mode_camera_preview_view.h" @@ -682,7 +683,7 @@ DCHECK(camera_preview_view_); DCHECK_EQ(selected_camera_, camera_preview_view_->camera_id()); - base::EraseIf(available_cameras_, [&](const CameraInfo& info) { + std::erase_if(available_cameras_, [&](const CameraInfo& info) { return selected_camera_ == info.camera_id; }); @@ -947,7 +948,7 @@ // Move `camera_preview_snap_position_` to the beginning of `snap_positions` // vector, since we should always try the current snap position first. - base::EraseIf(snap_positions, + std::erase_if(snap_positions, [this](CameraPreviewSnapPosition snap_position) { return snap_position == camera_preview_snap_position_; });
diff --git a/ash/capture_mode/capture_mode_demo_tools_controller.cc b/ash/capture_mode/capture_mode_demo_tools_controller.cc index 5ed1a8a6..2bfdb1df 100644 --- a/ash/capture_mode/capture_mode_demo_tools_controller.cc +++ b/ash/capture_mode/capture_mode_demo_tools_controller.cc
@@ -5,6 +5,7 @@ #include "ash/capture_mode/capture_mode_demo_tools_controller.h" #include <memory> +#include <vector> #include "ash/accessibility/accessibility_controller.h" #include "ash/capture_mode/capture_mode_constants.h" @@ -18,7 +19,6 @@ #include "ash/shell.h" #include "base/check_op.h" #include "base/containers/contains.h" -#include "base/containers/cxx20_erase.h" #include "base/containers/unique_ptr_adapters.h" #include "base/location.h" #include "base/notreached.h" @@ -380,7 +380,7 @@ void CaptureModeDemoToolsController::OnMouseHighlightAnimationEnded( PointerHighlightLayer* pointer_highlight_layer_ptr) { - base::EraseIf(mouse_highlight_layers_, + std::erase_if(mouse_highlight_layers_, base::MatchesUniquePtr(pointer_highlight_layer_ptr)); if (on_mouse_highlight_animation_ended_callback_for_test_)
diff --git a/ash/capture_mode/capture_mode_menu_group.cc b/ash/capture_mode/capture_mode_menu_group.cc index 0330ad7..50f0065 100644 --- a/ash/capture_mode/capture_mode_menu_group.cc +++ b/ash/capture_mode/capture_mode_menu_group.cc
@@ -16,7 +16,6 @@ #include "ash/style/ash_color_id.h" #include "ash/style/color_util.h" #include "ash/style/style_util.h" -#include "base/containers/cxx20_erase_vector.h" #include "base/memory/raw_ptr.h" #include "base/ranges/algorithm.h" #include "ui/accessibility/ax_enums.mojom-shared.h" @@ -418,7 +417,7 @@ return; options_container_->RemoveChildViewT(option); - base::Erase(options_, option); + std::erase(options_, option); } void CaptureModeMenuGroup::AddMenuItem(views::Button::PressedCallback callback,
diff --git a/ash/clipboard/clipboard_history_resource_manager.cc b/ash/clipboard/clipboard_history_resource_manager.cc index 916ca78..b1414be3a 100644 --- a/ash/clipboard/clipboard_history_resource_manager.cc +++ b/ash/clipboard/clipboard_history_resource_manager.cc
@@ -5,6 +5,7 @@ #include "ash/clipboard/clipboard_history_resource_manager.h" #include <string> +#include <vector> #include "ash/clipboard/clipboard_history_item.h" #include "ash/clipboard/clipboard_history_url_title_fetcher.h" @@ -243,7 +244,7 @@ // If `item` was attached to a pending request, make sure it is not updated // when rendering finishes. - base::Erase(image_model_request->clipboard_history_item_ids, item.id()); + std::erase(image_model_request->clipboard_history_item_ids, item.id()); if (image_model_request->clipboard_history_item_ids.empty()) { // If no more items are waiting on the image model, cancel the request.
diff --git a/ash/components/arc/test/fake_file_system_instance.cc b/ash/components/arc/test/fake_file_system_instance.cc index 04429b9..954a8bc9 100644 --- a/ash/components/arc/test/fake_file_system_instance.cc +++ b/ash/components/arc/test/fake_file_system_instance.cc
@@ -11,9 +11,9 @@ #include <optional> #include <sstream> #include <utility> +#include <vector> #include "base/containers/contains.h" -#include "base/containers/cxx20_erase_vector.h" #include "base/files/file.h" #include "base/files/file_path.h" #include "base/files/file_util.h" @@ -603,7 +603,7 @@ // Remove this document from lists of children. for (auto& child_iter : child_documents_) { - base::Erase(child_iter.second, key); + std::erase(child_iter.second, key); } base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
diff --git a/ash/constants/ash_features.cc b/ash/constants/ash_features.cc index 7b4fd69..8b543b1a 100644 --- a/ash/constants/ash_features.cc +++ b/ash/constants/ash_features.cc
@@ -3301,6 +3301,10 @@ return base::FeatureList::IsEnabled(kDeskTemplateSync); } +bool IsDisplayPerformanceModeEnabled() { + return base::FeatureList::IsEnabled(kDisplayPerformanceMode); +} + bool IsInputDeviceSettingsLoggingEnabled() { return base::FeatureList::IsEnabled(kEnableInputDeviceSettingsLogging); }
diff --git a/ash/constants/ash_features.h b/ash/constants/ash_features.h index f629f00..1e1f195 100644 --- a/ash/constants/ash_features.h +++ b/ash/constants/ash_features.h
@@ -959,6 +959,7 @@ COMPONENT_EXPORT(ASH_CONSTANTS) bool IsDemoModeGMSCoreWindowCloserEnabled(); COMPONENT_EXPORT(ASH_CONSTANTS) bool IsDeskButtonEnabled(); COMPONENT_EXPORT(ASH_CONSTANTS) bool IsDeskTemplateSyncEnabled(); +COMPONENT_EXPORT(ASH_CONSTANTS) bool IsDisplayPerformanceModeEnabled(); COMPONENT_EXPORT(ASH_CONSTANTS) bool IsInputDeviceSettingsLoggingEnabled(); COMPONENT_EXPORT(ASH_CONSTANTS) bool IsInputDeviceSettingsSplitEnabled(); COMPONENT_EXPORT(ASH_CONSTANTS) bool IsPeripheralCustomizationEnabled();
diff --git a/ash/display/display_performance_mode_controller.cc b/ash/display/display_performance_mode_controller.cc new file mode 100644 index 0000000..e704b44 --- /dev/null +++ b/ash/display/display_performance_mode_controller.cc
@@ -0,0 +1,71 @@ +// Copyright 2024 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ash/display/display_performance_mode_controller.h" + +namespace ash { + +using ModeState = DisplayPerformanceModeController::ModeState; + +DisplayPerformanceModeController::DisplayPerformanceModeController() + : power_status_(PowerStatus::Get()->GetWeakPtr()) { + power_status_->AddObserver(this); +} + +DisplayPerformanceModeController::~DisplayPerformanceModeController() { + if (power_status_) { + power_status_->RemoveObserver(this); + } +} + +ModeState DisplayPerformanceModeController::AddObserver(Observer* observer) { + observers_.AddObserver(observer); + return current_state_; +} + +void DisplayPerformanceModeController::RemoveObserver(Observer* observer) { + observers_.RemoveObserver(observer); +} + +void DisplayPerformanceModeController::OnPowerStatusChanged() { + UpdateCurrentStateAndNotifyIfChanged(); +} + +void DisplayPerformanceModeController::SetHighPerformanceModeByUser( + bool is_high_performance_enabled) { + is_high_performance_enabled_ = is_high_performance_enabled; + UpdateCurrentStateAndNotifyIfChanged(); +} + +void DisplayPerformanceModeController::UpdateCurrentStateAndNotifyIfChanged() { + // Implementation Logic: + // 1. If the user has enabled the high performance mode in the UI, then the + // display features should be in the high performance mode regardless of + // the power status. + // 2. If the user has not enabled the high performance mode in the UI, then + // the display features should be in the power saver mode if the power + // status is in battery saver mode. + // 3. If the user has not enabled the high performance mode in the UI, then + // the display features should be in the intelligent mode if the power + // status is not in battery saver mode. + ModeState new_state = ModeState::kIntelligent; + if (is_high_performance_enabled_) { + new_state = ModeState::kHighPerformance; + } else if (PowerStatus::Get()->IsBatterySaverActive()) { + new_state = ModeState::kPowerSaver; + } + + if (new_state != current_state_) { + current_state_ = new_state; + NotifyObservers(); + } +} + +void DisplayPerformanceModeController::NotifyObservers() { + for (Observer& observer : observers_) { + observer.OnDisplayPerformanceModeChanged(current_state_); + } +} + +} // namespace ash
diff --git a/ash/display/display_performance_mode_controller.h b/ash/display/display_performance_mode_controller.h new file mode 100644 index 0000000..7244059 --- /dev/null +++ b/ash/display/display_performance_mode_controller.h
@@ -0,0 +1,93 @@ +// Copyright 2024 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef ASH_DISPLAY_DISPLAY_PERFORMANCE_MODE_CONTROLLER_H_ +#define ASH_DISPLAY_DISPLAY_PERFORMANCE_MODE_CONTROLLER_H_ + +#include "ash/ash_export.h" +#include "ash/system/power/power_status.h" +#include "base/scoped_observation.h" + +namespace ash { + +// DisplayPerformanceModeController listens to the power status change and the +// Display Performance Mode change in the UI to dictate what state the display +// features should be at. Display features that want to depend on the power and +// user preference would listen to this controller and update their state +// accordingly. +class ASH_EXPORT DisplayPerformanceModeController + : public PowerStatus::Observer { + public: + // DisplayPerformanceModeController exposes 3 different modes that a client + // observing a mode change gets: + + // kHighPerformance: This mode is enabled by the user that wants the best and + // smoothest display experience. This mode does not necessarily indicate that + // a power source is connected. However, in this mode, the display features + // will prioritize performance over power efficiency. + + // kIntelligent: This mode represents an intelligent state for the display + // features. Users can expect that the display features will dynamically + // adjust their behavior based on the power status and user preferences. The + // display features will strive to balance performance and power efficiency, + // optimizing the user experience. This mode is the default mode. + + // kPowerSaver: This mode represents a power-saving state for the display + // features. Users can expect that the display features will prioritize + // power efficiency over performance. This mode is triggered by the system + // Power Saver mode and is currently not user-configurable. + enum ModeState { + kHighPerformance, + kIntelligent, + kPowerSaver, + kDefault = kIntelligent + }; + + class Observer : public base::CheckedObserver { + public: + virtual void OnDisplayPerformanceModeChanged(ModeState new_state) = 0; + + protected: + ~Observer() override = default; + }; + + explicit DisplayPerformanceModeController(); + DisplayPerformanceModeController(const DisplayPerformanceModeController&) = + delete; + DisplayPerformanceModeController& operator=( + const DisplayPerformanceModeController&) = delete; + ~DisplayPerformanceModeController() override; + + // When an observer is added, the current state should be sent to be up to + // date. + ModeState AddObserver(Observer* observer); + void RemoveObserver(Observer* observer); + + void SetHighPerformanceModeByUser(bool is_high_performance_enabled); + + // PowerStatus::Observer: + void OnPowerStatusChanged() override; + + private: + void UpdateCurrentStateAndNotifyIfChanged(); + void NotifyObservers(); + + ModeState current_state_ = ModeState::kIntelligent; + bool is_high_performance_enabled_ = false; + + base::ObserverList<Observer> observers_; + + // Not owned. + // TODO(b/327054689): This pointer is needed because some power tests delete + // PowerStatus without the observers knowing about it, so we have to check for + // its validity before using it. + base::WeakPtr<PowerStatus> power_status_; + + base::WeakPtrFactory<DisplayPerformanceModeController> weak_ptr_factory_{ + this}; +}; + +} // namespace ash + +#endif // ASH_DISPLAY_DISPLAY_PERFORMANCE_MODE_CONTROLLER_H_
diff --git a/ash/display/display_performance_mode_controller_unittest.cc b/ash/display/display_performance_mode_controller_unittest.cc new file mode 100644 index 0000000..7ce1de0 --- /dev/null +++ b/ash/display/display_performance_mode_controller_unittest.cc
@@ -0,0 +1,210 @@ +// Copyright 2024 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ash/display/display_performance_mode_controller.h" + +#include "ash/shell.h" +#include "ash/test/ash_test_base.h" +#include "testing/gmock/include/gmock/gmock.h" + +namespace ash { + +namespace { +power_manager::PowerSupplyProperties BuildFakePowerSupplyProperties( + power_manager::PowerSupplyProperties::ExternalPower charger_state, + double battery_percent) { + power_manager::PowerSupplyProperties fake_power; + fake_power.set_external_power(charger_state); + fake_power.set_battery_percent(battery_percent); + return fake_power; +} +} // namespace + +class DisplayPerformanceModeControllerTest : public AshTestBase { + public: + DisplayPerformanceModeControllerTest() = default; + DisplayPerformanceModeControllerTest( + const DisplayPerformanceModeControllerTest&) = delete; + DisplayPerformanceModeControllerTest& operator=( + const DisplayPerformanceModeControllerTest&) = delete; + ~DisplayPerformanceModeControllerTest() override = default; + + void SetUp() override { + AshTestBase::SetUp(); + + Shell::Get()->display_performance_mode_controller()->AddObserver( + &observer_); + } + + void TearDown() override { + Shell::Get()->display_performance_mode_controller()->RemoveObserver( + &observer_); + + AshTestBase::TearDown(); + } + + protected: + using ModeState = DisplayPerformanceModeController::ModeState; + + class MockObserver : public DisplayPerformanceModeController::Observer { + public: + MOCK_METHOD(void, + OnDisplayPerformanceModeChanged, + (ModeState new_state), + (override)); + }; + + MockObserver observer_; +}; + +TEST_F(DisplayPerformanceModeControllerTest, TestPowerSaverOnLowBattery) { + EXPECT_CALL(observer_, + OnDisplayPerformanceModeChanged(ModeState::kPowerSaver)); + + PowerStatus::Get()->SetProtoForTesting(BuildFakePowerSupplyProperties( + power_manager::PowerSupplyProperties::DISCONNECTED, 10.f)); + PowerStatus::Get()->SetBatterySaverStateForTesting(true); + Shell::Get()->display_performance_mode_controller()->OnPowerStatusChanged(); +} + +TEST_F(DisplayPerformanceModeControllerTest, TestPowerSaverOnAc) { + EXPECT_CALL(observer_, + OnDisplayPerformanceModeChanged(ModeState::kPowerSaver)); + + PowerStatus::Get()->SetProtoForTesting(BuildFakePowerSupplyProperties( + power_manager::PowerSupplyProperties::AC, 100.f)); + PowerStatus::Get()->SetBatterySaverStateForTesting(true); + Shell::Get()->display_performance_mode_controller()->OnPowerStatusChanged(); +} + +TEST_F(DisplayPerformanceModeControllerTest, TestIntelligentOnAc) { + // Should not be called as the default is already intelligent. + EXPECT_CALL(observer_, OnDisplayPerformanceModeChanged).Times(0); + + PowerStatus::Get()->SetProtoForTesting(BuildFakePowerSupplyProperties( + power_manager::PowerSupplyProperties::AC, 15.f)); + Shell::Get()->display_performance_mode_controller()->OnPowerStatusChanged(); +} + +TEST_F(DisplayPerformanceModeControllerTest, + TestResetToIntelligentAfterPowerSaver) { + EXPECT_CALL(observer_, + OnDisplayPerformanceModeChanged(ModeState::kPowerSaver)); + + PowerStatus::Get()->SetProtoForTesting(BuildFakePowerSupplyProperties( + power_manager::PowerSupplyProperties::DISCONNECTED, 15.f)); + PowerStatus::Get()->SetBatterySaverStateForTesting(true); + Shell::Get()->display_performance_mode_controller()->OnPowerStatusChanged(); + + EXPECT_CALL(observer_, + OnDisplayPerformanceModeChanged(ModeState::kIntelligent)); + PowerStatus::Get()->SetBatterySaverStateForTesting(false); + Shell::Get()->display_performance_mode_controller()->OnPowerStatusChanged(); +} + +TEST_F(DisplayPerformanceModeControllerTest, TestHighPerformance) { + EXPECT_CALL(observer_, + OnDisplayPerformanceModeChanged(ModeState::kHighPerformance)); + Shell::Get() + ->display_performance_mode_controller() + ->SetHighPerformanceModeByUser(true); +} + +TEST_F(DisplayPerformanceModeControllerTest, AvoidDuplicateSetting) { + EXPECT_CALL(observer_, + OnDisplayPerformanceModeChanged(ModeState::kHighPerformance)) + .Times(1); + Shell::Get() + ->display_performance_mode_controller() + ->SetHighPerformanceModeByUser(true); + Shell::Get() + ->display_performance_mode_controller() + ->SetHighPerformanceModeByUser(true); +} + +TEST_F(DisplayPerformanceModeControllerTest, + TestHighPerformanceModeBeforeOnPowerSaverBattery) { + EXPECT_CALL(observer_, + OnDisplayPerformanceModeChanged(ModeState::kHighPerformance)) + .Times(1); + + Shell::Get() + ->display_performance_mode_controller() + ->SetHighPerformanceModeByUser(true); + + PowerStatus::Get()->SetProtoForTesting(BuildFakePowerSupplyProperties( + power_manager::PowerSupplyProperties::AC, 100.f)); + PowerStatus::Get()->SetBatterySaverStateForTesting(true); + Shell::Get()->display_performance_mode_controller()->OnPowerStatusChanged(); +} + +TEST_F(DisplayPerformanceModeControllerTest, + TestHighPerformanceModeAfterOnPowerSaverBattery) { + EXPECT_CALL(observer_, + OnDisplayPerformanceModeChanged(ModeState::kPowerSaver)); + PowerStatus::Get()->SetProtoForTesting(BuildFakePowerSupplyProperties( + power_manager::PowerSupplyProperties::AC, 100.f)); + PowerStatus::Get()->SetBatterySaverStateForTesting(true); + Shell::Get()->display_performance_mode_controller()->OnPowerStatusChanged(); + + EXPECT_CALL(observer_, + OnDisplayPerformanceModeChanged(ModeState::kHighPerformance)); + Shell::Get() + ->display_performance_mode_controller() + ->SetHighPerformanceModeByUser(true); +} + +TEST_F(DisplayPerformanceModeControllerTest, + TestTurnOffHighPerformanceToIntelligent) { + EXPECT_CALL(observer_, + OnDisplayPerformanceModeChanged(ModeState::kHighPerformance)); + Shell::Get() + ->display_performance_mode_controller() + ->SetHighPerformanceModeByUser(true); + + EXPECT_CALL(observer_, + OnDisplayPerformanceModeChanged(ModeState::kIntelligent)); + Shell::Get() + ->display_performance_mode_controller() + ->SetHighPerformanceModeByUser(false); +} + +TEST_F(DisplayPerformanceModeControllerTest, + TestTurnOffHighPerformanceToPowerSaver) { + EXPECT_CALL(observer_, + OnDisplayPerformanceModeChanged(ModeState::kPowerSaver)); + PowerStatus::Get()->SetProtoForTesting(BuildFakePowerSupplyProperties( + power_manager::PowerSupplyProperties::DISCONNECTED, 10.f)); + PowerStatus::Get()->SetBatterySaverStateForTesting(true); + Shell::Get()->display_performance_mode_controller()->OnPowerStatusChanged(); + + EXPECT_CALL(observer_, + OnDisplayPerformanceModeChanged(ModeState::kHighPerformance)); + Shell::Get() + ->display_performance_mode_controller() + ->SetHighPerformanceModeByUser(true); + + EXPECT_CALL(observer_, + OnDisplayPerformanceModeChanged(ModeState::kPowerSaver)); + Shell::Get() + ->display_performance_mode_controller() + ->SetHighPerformanceModeByUser(false); +} + +TEST_F(DisplayPerformanceModeControllerTest, TestModeStateOnAddObserver) { + // Set Shiny Mode to go into High Performance mode. + Shell::Get() + ->display_performance_mode_controller() + ->SetHighPerformanceModeByUser(true); + + // Check that adding the controller reports back High Performance mode. + Shell::Get()->display_performance_mode_controller()->RemoveObserver( + &observer_); + ModeState current_state = + Shell::Get()->display_performance_mode_controller()->AddObserver( + &observer_); + EXPECT_EQ(current_state, ModeState::kHighPerformance); +} + +} // namespace ash
diff --git a/ash/game_dashboard/game_dashboard_constants.h b/ash/game_dashboard/game_dashboard_constants.h index 0f0d34c..9b25a65c 100644 --- a/ash/game_dashboard/game_dashboard_constants.h +++ b/ash/game_dashboard/game_dashboard_constants.h
@@ -8,7 +8,7 @@ namespace ash::game_dashboard { // Toolbar padding from the border of the game window. -inline constexpr int kToolbarEdgePadding = 10; +inline constexpr int kToolbarEdgePadding = 16; // Interior margin padding around the game window for the // `GameDashboardWelcomeDialog`.
diff --git a/ash/game_dashboard/game_dashboard_main_menu_view.cc b/ash/game_dashboard/game_dashboard_main_menu_view.cc index a60dc50..dda00707 100644 --- a/ash/game_dashboard/game_dashboard_main_menu_view.cc +++ b/ash/game_dashboard/game_dashboard_main_menu_view.cc
@@ -623,7 +623,8 @@ void GameDashboardMainMenuView::OnFeedbackButtonPressed() { Shell::Get()->shell_delegate()->OpenFeedbackDialog( ShellDelegate::FeedbackSource::kGameDashboard, - /*description_template=*/"#GameDashboard\n\n"); + /*description_template=*/"#GameDashboard\n\n", + /*category_tag=*/std::string()); } void GameDashboardMainMenuView::OnHelpButtonPressed() {
diff --git a/ash/game_dashboard/game_dashboard_toolbar_view.cc b/ash/game_dashboard/game_dashboard_toolbar_view.cc index 1af24ef..bc2f6a1 100644 --- a/ash/game_dashboard/game_dashboard_toolbar_view.cc +++ b/ash/game_dashboard/game_dashboard_toolbar_view.cc
@@ -17,11 +17,13 @@ #include "ash/resources/vector_icons/vector_icons.h" #include "ash/strings/grit/ash_strings.h" #include "ash/style/icon_button.h" +#include "ash/style/system_shadow.h" #include "base/check.h" #include "base/types/cxx23_to_underlying.h" #include "ui/base/l10n/l10n_util.h" #include "ui/base/metadata/metadata_impl_macros.h" #include "ui/chromeos/styles/cros_tokens_color_mappings.h" +#include "ui/color/color_id.h" #include "ui/compositor/layer.h" #include "ui/events/event_handler.h" #include "ui/events/keycodes/keyboard_codes_posix.h" @@ -31,6 +33,8 @@ #include "ui/gfx/geometry/vector2d.h" #include "ui/gfx/geometry/vector2d_conversions.h" #include "ui/views/background.h" +#include "ui/views/border.h" +#include "ui/views/highlight_border.h" #include "ui/views/widget/widget.h" namespace ash { @@ -48,16 +52,20 @@ // Padding between children in the toolbar. constexpr int kBetweenChildSpacing = 4; -std::unique_ptr<IconButton> CreateIconButton(base::RepeatingClosure callback, - const gfx::VectorIcon* icon, - int view_id, - const std::u16string& text, - bool is_togglable) { +std::unique_ptr<IconButton> CreateIconButton( + base::RepeatingClosure callback, + const gfx::VectorIcon* icon, + int view_id, + const std::u16string& text, + bool is_togglable, + ui::ColorId icon_color = cros_tokens::kCrosSysOnSurface) { // TODO(b/290696780): Update logic so the toolbar can drag from icon buttons. auto button = std::make_unique<IconButton>( std::move(callback), IconButton::Type::kMedium, icon, text, /*is_togglable=*/is_togglable, /*has_border=*/true); button->SetID(view_id); + button->SetIconColor(icon_color); + button->SetBackgroundColor(SK_ColorTRANSPARENT); return button; } @@ -232,9 +240,11 @@ SetCrossAxisAlignment(views::BoxLayout::CrossAxisAlignment::kCenter); SetBackground(views::CreateThemedRoundedRectBackground( cros_tokens::kCrosSysSystemBaseElevatedOpaque, kCornerRadius)); - SetPaintToLayer(); - layer()->SetRoundedCornerRadius(gfx::RoundedCornersF(kCornerRadius)); - layer()->SetFillsBoundsOpaquely(false); + SetBorder(views::CreateThemedRoundedRectBorder( + 1, kCornerRadius, ui::ColorIds::kColorCrosSystemHighlightBorder)); + shadow_ = SystemShadow::CreateShadowOnNinePatchLayerForView( + this, SystemShadow::Type::kElevation12); + shadow_->SetRoundedCornerRadius(kCornerRadius); AddShortcutTiles(); } @@ -357,7 +367,7 @@ &kGdToolbarIcon, base::to_underlying(ToolbarViewId::kGamepadButton), l10n_util::GetStringUTF16( IDS_ASH_GAME_DASHBOARD_TOOLBAR_TILE_BUTTON_TITLE), - /*is_togglable=*/false)); + /*is_togglable=*/false, /*icon_color=*/cros_tokens::kCrosSysPrimary)); MayAddGameControlsTile(); @@ -372,8 +382,6 @@ IDS_ASH_GAME_DASHBOARD_RECORD_GAME_TILE_BUTTON_TITLE), /*is_togglable=*/true)); record_game_button_->SetVectorIcon(kGdRecordGameIcon); - record_game_button_->SetIconColor(cros_tokens::kCrosSysOnSurface); - record_game_button_->SetBackgroundToggledColor(cros_tokens::kCrosSysError); record_game_button_->SetToggledVectorIcon(kCaptureModeCircleStopIcon); record_game_button_->SetIconToggledColor(cros_tokens::kCrosSysOnError);
diff --git a/ash/game_dashboard/game_dashboard_toolbar_view.h b/ash/game_dashboard/game_dashboard_toolbar_view.h index 9f1eecdc..d24cc484 100644 --- a/ash/game_dashboard/game_dashboard_toolbar_view.h +++ b/ash/game_dashboard/game_dashboard_toolbar_view.h
@@ -16,6 +16,7 @@ class GameDashboardContext; class IconButton; class ToolbarDragHandler; +class SystemShadow; // GameDashboardToolbarView is the movable toolbar that's attached to the game // window. It contains various quick action tiles for users to access without @@ -104,6 +105,8 @@ // Handles all dragging logic for the toolbar. std::unique_ptr<ToolbarDragHandler> drag_handler_; + + std::unique_ptr<SystemShadow> shadow_; }; } // namespace ash
diff --git a/ash/public/cpp/assistant/assistant_state_base.cc b/ash/public/cpp/assistant/assistant_state_base.cc index 873f0f2..c687dfe 100644 --- a/ash/public/cpp/assistant/assistant_state_base.cc +++ b/ash/public/cpp/assistant/assistant_state_base.cc
@@ -11,41 +11,16 @@ #include "base/functional/bind.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_piece.h" +#include "base/strings/to_string.h" +#include "base/strings/strcat.h" #include "chromeos/ash/components/audio/cras_audio_handler.h" #include "components/prefs/pref_change_registrar.h" #include "components/prefs/pref_service.h" namespace ash { -namespace { - using assistant::prefs::AssistantOnboardingMode; -#define PRINT_VALUE(value) PrintValue(&result, #value, value()) - -template <typename T, std::enable_if_t<std::is_enum<T>::value>* = nullptr> -void PrintValue(std::stringstream* result, const std::optional<T>& value) { - *result << base::NumberToString(static_cast<int>(value.value())); -} - -template <typename T, std::enable_if_t<!std::is_enum<T>::value>* = nullptr> -void PrintValue(std::stringstream* result, const std::optional<T>& value) { - *result << value.value(); -} - -template <typename T> -void PrintValue(std::stringstream* result, - const std::string& name, - const std::optional<T>& value) { - *result << std::endl << " " << name << ": "; - if (value.has_value()) - PrintValue(result, value); - else - *result << ("(no value)"); -} - -} // namespace - AssistantStateBase::AssistantStateBase() = default; AssistantStateBase::~AssistantStateBase() { @@ -54,18 +29,15 @@ } std::string AssistantStateBase::ToString() const { - std::stringstream result; - result << "AssistantStatus: "; - result << assistant_status_; - PRINT_VALUE(settings_enabled); - PRINT_VALUE(context_enabled); - PRINT_VALUE(hotword_enabled); - PRINT_VALUE(allowed_state); - PRINT_VALUE(locale); - PRINT_VALUE(arc_play_store_enabled); - PRINT_VALUE(locked_full_screen_enabled); - PRINT_VALUE(onboarding_mode); - return result.str(); +#define STRINGIFY(field) \ + #field, (field.has_value() ? base::ToString(field.value()) : "(no value)") + return base::StrCat( + {"AssistantStatus: ", base::ToString(assistant_status_), + STRINGIFY(settings_enabled()), STRINGIFY(context_enabled()), + STRINGIFY(hotword_enabled()), STRINGIFY(allowed_state()), + STRINGIFY(locale()), STRINGIFY(arc_play_store_enabled()), + STRINGIFY(locked_full_screen_enabled()), STRINGIFY(onboarding_mode())}); +#undef STRINGIFY } void AssistantStateBase::AddObserver(AssistantStateObserver* observer) {
diff --git a/ash/public/cpp/test/test_desk_profiles_delegate.cc b/ash/public/cpp/test/test_desk_profiles_delegate.cc index 92f4043..13a2d0c 100644 --- a/ash/public/cpp/test/test_desk_profiles_delegate.cc +++ b/ash/public/cpp/test/test_desk_profiles_delegate.cc
@@ -4,7 +4,8 @@ #include "ash/public/cpp/test/test_desk_profiles_delegate.h" -#include "base/containers/cxx20_erase_vector.h" +#include <vector> + #include "base/ranges/algorithm.h" namespace ash { @@ -28,7 +29,7 @@ bool TestDeskProfilesDelegate::RemoveTestProfile(uint64_t profile_id) { CHECK(profile_id != primary_user_profile_id_); - if (base::EraseIf(profiles_, [&](const auto& profile) { + if (std::erase_if(profiles_, [&](const auto& profile) { return profile.profile_id == profile_id; })) { for (auto& observer : observers_) {
diff --git a/ash/shelf/window_scale_animation.cc b/ash/shelf/window_scale_animation.cc index 4046458..80537e9e 100644 --- a/ash/shelf/window_scale_animation.cc +++ b/ash/shelf/window_scale_animation.cc
@@ -5,6 +5,7 @@ #include "ash/shelf/window_scale_animation.h" #include <optional> +#include <vector> #include "ash/public/cpp/shelf_config.h" #include "ash/public/cpp/window_backdrop.h" @@ -194,7 +195,7 @@ // `animation_observer` will get deleted on the next line. auto* window = animation_observer->window(); - base::EraseIf(window_animation_observers_, + std::erase_if(window_animation_observers_, base::MatchesUniquePtr(animation_observer)); if (window_animation_observers_.empty()) {
diff --git a/ash/shell.cc b/ash/shell.cc index 72bed64..2fc9765 100644 --- a/ash/shell.cc +++ b/ash/shell.cc
@@ -63,6 +63,7 @@ #include "ash/display/display_configuration_observer.h" #include "ash/display/display_error_observer.h" #include "ash/display/display_highlight_controller.h" +#include "ash/display/display_performance_mode_controller.h" #include "ash/display/display_prefs.h" #include "ash/display/display_shutdown_observer.h" #include "ash/display/event_transformation_handler.h" @@ -880,6 +881,8 @@ display_highlight_controller_.reset(); + display_performance_mode_controller_.reset(); + // VideoActivityNotifier must be deleted before |video_detector_| is // deleted because it's observing video activity through // VideoDetector::Observer interface. @@ -1730,6 +1733,9 @@ display_highlight_controller_ = std::make_unique<DisplayHighlightController>(); + display_performance_mode_controller_ = + std::make_unique<DisplayPerformanceModeController>(); + if (features::IsDisplayAlignmentAssistanceEnabled()) { display_alignment_controller_ = std::make_unique<DisplayAlignmentController>();
diff --git a/ash/shell.h b/ash/shell.h index 6c441f80..8eb8b38 100644 --- a/ash/shell.h +++ b/ash/shell.h
@@ -144,6 +144,7 @@ class DisplayConfigurationObserver; class DisplayErrorObserver; class DisplayHighlightController; +class DisplayPerformanceModeController; class DisplayPrefs; class DisplayShutdownObserver; class DisplaySpeakerController; @@ -522,6 +523,10 @@ return display_highlight_controller_.get(); } + DisplayPerformanceModeController* display_performance_mode_controller() { + return display_performance_mode_controller_.get(); + } + DockedMagnifierController* docked_magnifier_controller() { return docked_magnifier_controller_.get(); } @@ -1023,6 +1028,8 @@ std::unique_ptr<diagnostics::DiagnosticsLogController> diagnostics_log_controller_; std::unique_ptr<DisplayHighlightController> display_highlight_controller_; + std::unique_ptr<DisplayPerformanceModeController> + display_performance_mode_controller_; std::unique_ptr<DisplaySpeakerController> display_speaker_controller_; std::unique_ptr<DragDropController> drag_drop_controller_; std::unique_ptr<FirmwareUpdateManager> firmware_update_manager_;
diff --git a/ash/shell_delegate.h b/ash/shell_delegate.h index 819d98f..4cebfcf3 100644 --- a/ash/shell_delegate.h +++ b/ash/shell_delegate.h
@@ -58,6 +58,7 @@ enum class FeedbackSource { kFocusMode, kGameDashboard, + kOverview, kWindowLayoutMenu, }; @@ -184,7 +185,8 @@ // `description_template` fields. Note, this will only be used by features // before they are fully launched or removed. virtual void OpenFeedbackDialog(FeedbackSource source, - const std::string& description_template) = 0; + const std::string& description_template, + const std::string& category_tag) = 0; // Calls browser service to open the profile manager. virtual void OpenProfileManager() = 0;
diff --git a/ash/system/focus_mode/focus_mode_detailed_view.cc b/ash/system/focus_mode/focus_mode_detailed_view.cc index 498c118..5709fab 100644 --- a/ash/system/focus_mode/focus_mode_detailed_view.cc +++ b/ash/system/focus_mode/focus_mode_detailed_view.cc
@@ -841,7 +841,8 @@ void FocusModeDetailedView::OnFeedbackButtonPressed() { Shell::Get()->shell_delegate()->OpenFeedbackDialog( ShellDelegate::FeedbackSource::kFocusMode, - /*description_template=*/"#FocusMode"); + /*description_template=*/"#FocusMode", + /*category_tag=*/std::string()); } void FocusModeDetailedView::OnClockMinutePassed() {
diff --git a/ash/system/focus_mode/focus_mode_tasks_provider.cc b/ash/system/focus_mode/focus_mode_tasks_provider.cc index 0394e76e..f6073fc 100644 --- a/ash/system/focus_mode/focus_mode_tasks_provider.cc +++ b/ash/system/focus_mode/focus_mode_tasks_provider.cc
@@ -5,9 +5,9 @@ #include "ash/system/focus_mode/focus_mode_tasks_provider.h" #include <optional> +#include <vector> #include "ash/api/tasks/tasks_types.h" -#include "base/containers/cxx20_erase_vector.h" #include "base/strings/string_number_conversions.h" #include "base/task/sequenced_task_runner.h" #include "base/time/time.h" @@ -144,7 +144,7 @@ } void FocusModeTasksProvider::MarkAsCompleted(const std::string& task_id) { - base::EraseIf(tasks_data_, + std::erase_if(tasks_data_, [task_id](const auto& task) { return task->id == task_id; }); }
diff --git a/ash/system/holding_space/holding_space_tray.cc b/ash/system/holding_space/holding_space_tray.cc index bcc87c1..a072195 100644 --- a/ash/system/holding_space/holding_space_tray.cc +++ b/ash/system/holding_space/holding_space_tray.cc
@@ -5,6 +5,7 @@ #include "ash/system/holding_space/holding_space_tray.h" #include <memory> +#include <vector> #include "ash/accessibility/accessibility_controller.h" #include "ash/ash_element_identifiers.h" @@ -36,7 +37,6 @@ #include "base/check.h" #include "base/containers/adapters.h" #include "base/containers/contains.h" -#include "base/containers/cxx20_erase.h" #include "base/functional/bind.h" #include "base/functional/callback_helpers.h" #include "base/ranges/algorithm.h" @@ -101,7 +101,7 @@ /*fallback_to_filenames=*/true); HoldingSpaceModel* const model = HoldingSpaceController::Get()->model(); - base::EraseIf(unpinned_file_paths, [model](const base::FilePath& file_path) { + std::erase_if(unpinned_file_paths, [model](const base::FilePath& file_path) { return model->ContainsItem(HoldingSpaceItem::Type::kPinnedFile, file_path); });
diff --git a/ash/system/holding_space/holding_space_tray_icon.cc b/ash/system/holding_space/holding_space_tray_icon.cc index 2f085fcce..f49f6c1 100644 --- a/ash/system/holding_space/holding_space_tray_icon.cc +++ b/ash/system/holding_space/holding_space_tray_icon.cc
@@ -4,6 +4,8 @@ #include "ash/system/holding_space/holding_space_tray_icon.h" +#include <vector> + #include "ash/public/cpp/holding_space/holding_space_constants.h" #include "ash/public/cpp/holding_space/holding_space_item.h" #include "ash/public/cpp/holding_space/holding_space_metrics.h" @@ -18,7 +20,6 @@ #include "base/barrier_closure.h" #include "base/containers/adapters.h" #include "base/containers/contains.h" -#include "base/containers/cxx20_erase.h" #include "base/containers/unique_ptr_adapters.h" #include "base/functional/bind.h" #include "base/i18n/rtl.h" @@ -373,7 +374,7 @@ void HoldingSpaceTrayIcon::OnOldItemAnimatedOut( HoldingSpaceTrayIconPreview* preview, const base::RepeatingClosure& callback) { - base::EraseIf(removed_previews_, base::MatchesUniquePtr(preview)); + std::erase_if(removed_previews_, base::MatchesUniquePtr(preview)); callback.Run(); }
diff --git a/ash/system/holding_space/holding_space_view_delegate.cc b/ash/system/holding_space/holding_space_view_delegate.cc index d2f265a8..ce7ac56 100644 --- a/ash/system/holding_space/holding_space_view_delegate.cc +++ b/ash/system/holding_space/holding_space_view_delegate.cc
@@ -4,6 +4,8 @@ #include "ash/system/holding_space/holding_space_view_delegate.h" +#include <vector> + #include "ash/constants/ash_features.h" #include "ash/public/cpp/holding_space/holding_space_client.h" #include "ash/public/cpp/holding_space/holding_space_constants.h" @@ -21,7 +23,6 @@ #include "ash/system/holding_space/holding_space_tray.h" #include "ash/system/holding_space/holding_space_tray_bubble.h" #include "base/containers/contains.h" -#include "base/containers/cxx20_erase_vector.h" #include "base/functional/callback_helpers.h" #include "base/memory/raw_ref.h" #include "base/memory/weak_ptr.h" @@ -613,7 +614,7 @@ if (!in_progress_commands.has_value()) { in_progress_commands = item->in_progress_commands(); } else { - base::EraseIf(in_progress_commands.value(), + std::erase_if(in_progress_commands.value(), [&](const HoldingSpaceItem::InProgressCommand& in_progress_command) { return !holding_space_util::SupportsInProgressCommand(
diff --git a/ash/system/input_device_settings/input_device_notifier.cc b/ash/system/input_device_settings/input_device_notifier.cc index 7b21ff6..0a5a4c1 100644 --- a/ash/system/input_device_settings/input_device_notifier.cc +++ b/ash/system/input_device_settings/input_device_notifier.cc
@@ -5,6 +5,7 @@ #include "ash/system/input_device_settings/input_device_notifier.h" #include <functional> +#include <vector> #include "ash/bluetooth_devices_observer.h" #include "ash/public/cpp/input_device_settings_controller.h" @@ -16,7 +17,6 @@ #include "ash/system/input_device_settings/input_device_settings_pref_names.h" #include "ash/system/input_device_settings/input_device_settings_utils.h" #include "base/containers/contains.h" -#include "base/containers/cxx20_erase_vector.h" #include "base/containers/flat_map.h" #include "base/functional/bind.h" #include "base/ranges/algorithm.h" @@ -219,7 +219,7 @@ // Remove any devices marked as imposters as well. base::ranges::sort(updated_device_list, base::ranges::less(), ExtractDeviceIdFromInputDevice); - base::EraseIf(updated_device_list, [&](const ui::InputDevice& device) { + std::erase_if(updated_device_list, [&](const ui::InputDevice& device) { return IsDeviceASuspectedImposter<DeviceMojomPtr>(bluetooth_observer, device); }); @@ -373,7 +373,7 @@ std::vector<ui::InputDevice> InputDeviceNotifier<mojom::MousePtr, ui::InputDevice>::GetUpdatedDeviceList() { auto mice = ui::DeviceDataManager::GetInstance()->GetMouseDevices(); - base::EraseIf(mice, [](const auto& mouse) { + std::erase_if(mice, [](const auto& mouse) { if (floss::features::IsFlossEnabled()) { if (kFlossExtraMouseVidPid == VendorProductId{mouse.vendor_id, mouse.product_id} &&
diff --git a/ash/system/notification_center/views/ash_notification_view_unittest.cc b/ash/system/notification_center/views/ash_notification_view_unittest.cc index 35bf9eb..e97aa9f 100644 --- a/ash/system/notification_center/views/ash_notification_view_unittest.cc +++ b/ash/system/notification_center/views/ash_notification_view_unittest.cc
@@ -824,9 +824,7 @@ GetHeaderRow(notification_view())->bounds().y()); } -// TODO(crbug.com/326337073): Re-enable this test -TEST_F(AshNotificationViewTest, - DISABLED_ExpandCollapseAnimationsRecordSmoothness) { +TEST_F(AshNotificationViewTest, ExpandCollapseAnimationsRecordSmoothness) { // Enable animations. ui::ScopedAnimationDurationScaleMode duration( ui::ScopedAnimationDurationScaleMode::FAST_DURATION); @@ -887,8 +885,7 @@ } // TODO(crbug.com/1522231): Re-enable this test -TEST_F(AshNotificationViewTest, - DISABLED_ImageExpandCollapseAnimationsRecordSmoothness) { +TEST_F(AshNotificationViewTest, ImageExpandCollapseAnimationsRecordSmoothness) { // Enable animations. ui::ScopedAnimationDurationScaleMode duration( ui::ScopedAnimationDurationScaleMode::FAST_DURATION); @@ -953,9 +950,7 @@ "ScaleAndTranslate.AnimationSmoothness"); } -// TODO(crbug.com/1520190): Re-enable when flakiness is resolved. -TEST_F(AshNotificationViewTest, - DISABLED_GroupExpandCollapseAnimationsRecordSmoothness) { +TEST_F(AshNotificationViewTest, GroupExpandCollapseAnimationsRecordSmoothness) { base::HistogramTester histograms; // Enable animations. @@ -1096,16 +1091,7 @@ "Ash.NotificationView.InlineReply.FadeOut.AnimationSmoothness"); } -// TODO(crbug.com/1518434): Flaky on ChromeOS. -#if BUILDFLAG(IS_CHROMEOS) -#define MAYBE_InlineSettingsAnimationsRecordSmoothness \ - DISABLED_InlineSettingsAnimationsRecordSmoothness -#else -#define MAYBE_InlineSettingsAnimationsRecordSmoothness \ - InlineSettingsAnimationsRecordSmoothness -#endif -TEST_F(AshNotificationViewTest, - MAYBE_InlineSettingsAnimationsRecordSmoothness) { +TEST_F(AshNotificationViewTest, InlineSettingsAnimationsRecordSmoothness) { base::HistogramTester histograms; // Enable animations.
diff --git a/ash/system/power/power_status.cc b/ash/system/power/power_status.cc index 16e0a775..9404112 100644 --- a/ash/system/power/power_status.cc +++ b/ash/system/power/power_status.cc
@@ -451,6 +451,10 @@ return battery_saver_active_; } +base::WeakPtr<PowerStatus> PowerStatus::GetWeakPtr() { + return weak_ptr_factory_.GetWeakPtr(); +} + PowerStatus::PowerStatus() { chromeos::PowerManagerClient::Get()->AddObserver(this); chromeos::PowerManagerClient::Get()->RequestStatusUpdate();
diff --git a/ash/system/power/power_status.h b/ash/system/power/power_status.h index 5ff0192..56089e2 100644 --- a/ash/system/power/power_status.h +++ b/ash/system/power/power_status.h
@@ -263,6 +263,11 @@ // Returns true if battery saver is active. bool IsBatterySaverActive() const; + // TODO(b/327054689): This pointer is needed because some power tests delete + // PowerStatus without the observers knowing about it, so observers have to + // check for its validity before using it. + base::WeakPtr<PowerStatus> GetWeakPtr(); + // Updates |proto_|. Does not notify observers. void SetProtoForTesting(const power_manager::PowerSupplyProperties& proto);
diff --git a/ash/system/video_conference/effects/fake_video_conference_tray_effects_manager.cc b/ash/system/video_conference/effects/fake_video_conference_tray_effects_manager.cc index 316fbd8..aa8b599 100644 --- a/ash/system/video_conference/effects/fake_video_conference_tray_effects_manager.cc +++ b/ash/system/video_conference/effects/fake_video_conference_tray_effects_manager.cc
@@ -4,12 +4,13 @@ #include "ash/system/video_conference/effects/fake_video_conference_tray_effects_manager.h" +#include <vector> + #include "ash/constants/ash_features.h" #include "ash/system/video_conference/bubble/vc_tile_ui_controller.h" #include "ash/system/video_conference/effects/video_conference_tray_effects_delegate.h" #include "ash/system/video_conference/effects/video_conference_tray_effects_manager.h" #include "ash/system/video_conference/effects/video_conference_tray_effects_manager_types.h" -#include "base/containers/cxx20_erase_vector.h" namespace ash { @@ -26,7 +27,7 @@ // Delete any tile UI controllers associated with any of `delegate`'s effects. for (auto* effect : delegate->GetEffects(VcEffectType::kToggle)) { const VcEffectId id = effect->id(); - base::EraseIf( + std::erase_if( tile_ui_controllers_, [&, id](const std::unique_ptr<video_conference::VcTileUiController>& controller) {
diff --git a/ash/system/video_conference/effects/video_conference_tray_effects_manager.cc b/ash/system/video_conference/effects/video_conference_tray_effects_manager.cc index fe910017..ce106b8 100644 --- a/ash/system/video_conference/effects/video_conference_tray_effects_manager.cc +++ b/ash/system/video_conference/effects/video_conference_tray_effects_manager.cc
@@ -5,6 +5,7 @@ #include "ash/system/video_conference/effects/video_conference_tray_effects_manager.h" #include <memory> +#include <vector> #include "ash/constants/ash_features.h" #include "ash/system/video_conference/bubble/vc_tile_ui_controller.h" @@ -12,7 +13,6 @@ #include "ash/system/video_conference/effects/video_conference_tray_effects_manager_types.h" #include "base/check.h" #include "base/check_op.h" -#include "base/containers/cxx20_erase_vector.h" #include "base/observer_list.h" namespace ash { @@ -42,7 +42,7 @@ VcEffectsDelegate* delegate) { DCHECK(delegate); size_t num_items_erased = - base::EraseIf(effect_delegates_, + std::erase_if(effect_delegates_, [delegate](VcEffectsDelegate* d) { return delegate == d; }); DCHECK_EQ(num_items_erased, 1UL);
diff --git a/ash/test_shell_delegate.h b/ash/test_shell_delegate.h index 7e76c44..bca83b4 100644 --- a/ash/test_shell_delegate.h +++ b/ash/test_shell_delegate.h
@@ -101,7 +101,8 @@ bool IsLoggingRedirectDisabled() const override; base::FilePath GetPrimaryUserDownloadsFolder() const override; void OpenFeedbackDialog(FeedbackSource source, - const std::string& description_template) override {} + const std::string& description_template, + const std::string& category_tag) override {} void OpenProfileManager() override {} void SetLastCommittedURLForWindow(const GURL& url); version_info::Channel GetChannel() override;
diff --git a/ash/user_education/holding_space_wallpaper_nudge/holding_space_wallpaper_nudge_controller.cc b/ash/user_education/holding_space_wallpaper_nudge/holding_space_wallpaper_nudge_controller.cc index 4293c66..e1b9be84 100644 --- a/ash/user_education/holding_space_wallpaper_nudge/holding_space_wallpaper_nudge_controller.cc +++ b/ash/user_education/holding_space_wallpaper_nudge/holding_space_wallpaper_nudge_controller.cc
@@ -41,7 +41,6 @@ #include "ash/wallpaper/wallpaper_drag_drop_delegate.h" #include "base/check_is_test.h" #include "base/check_op.h" -#include "base/containers/cxx20_erase_vector.h" #include "base/files/file_path.h" #include "base/scoped_observation.h" #include "base/timer/elapsed_timer.h" @@ -84,7 +83,7 @@ holding_space_util::ExtractFilePaths(data, /*fallback_to_filenames=*/false); - base::EraseIf(unpinned_file_paths, [&](const base::FilePath& file_path) { + std::erase_if(unpinned_file_paths, [&](const base::FilePath& file_path) { return model->ContainsItem(HoldingSpaceItem::Type::kPinnedFile, file_path); });
diff --git a/ash/webui/print_preview_cros/resources/BUILD.gn b/ash/webui/print_preview_cros/resources/BUILD.gn index b997790..0eff57e 100644 --- a/ash/webui/print_preview_cros/resources/BUILD.gn +++ b/ash/webui/print_preview_cros/resources/BUILD.gn
@@ -16,6 +16,9 @@ non_web_component_files = [ "js/data/print_ticket_manager.ts", "js/summary_panel_controller.ts", + "js/fakes/fake_print_preview_page_handler.ts", + "js/utils/mojo_data_providers.ts", + "js/utils/print_preview_cros_app_types.ts", ] web_component_files = [
diff --git a/ash/webui/print_preview_cros/resources/js/data/print_ticket_manager.ts b/ash/webui/print_preview_cros/resources/js/data/print_ticket_manager.ts index 1da0485..02b8f515 100644 --- a/ash/webui/print_preview_cros/resources/js/data/print_ticket_manager.ts +++ b/ash/webui/print_preview_cros/resources/js/data/print_ticket_manager.ts
@@ -2,6 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import {assert} from 'chrome://resources/js/assert.js'; + +import {getPrintPreviewPageHandler} from '../utils/mojo_data_providers.js'; +import {type PrintPreviewPageHandler} from '../utils/print_preview_cros_app_types.js'; /** * @fileoverview @@ -12,11 +16,6 @@ export class PrintTicketManager extends EventTarget { private static instance: PrintTicketManager|null = null; - // Prevent additional initialization. - private constructor() { - super(); - } - static getInstance(): PrintTicketManager { if (PrintTicketManager.instance === null) { PrintTicketManager.instance = new PrintTicketManager(); @@ -29,8 +28,25 @@ PrintTicketManager.instance = null; } + // Non-static properties: + private printPreviewPageHandler: PrintPreviewPageHandler|null; + + // Prevent additional initialization. + private constructor() { + super(); + + // Setup mojo data providers. + this.printPreviewPageHandler = getPrintPreviewPageHandler(); + } + // TODO(b/323421684): Takes current print ticket uses PrintPreviewPageHandler // to initiate actual print request. Also handles print request start and // finish events. - sendPrintRequest(): void {} + sendPrintRequest(): void { + assert(this.printPreviewPageHandler); + + // TODO(b/323421684): Handle result from page handler and update UI if error + // occurred. + this.printPreviewPageHandler!.print(); + } }
diff --git a/ash/webui/print_preview_cros/resources/js/fakes/fake_print_preview_page_handler.ts b/ash/webui/print_preview_cros/resources/js/fakes/fake_print_preview_page_handler.ts new file mode 100644 index 0000000..7729230 --- /dev/null +++ b/ash/webui/print_preview_cros/resources/js/fakes/fake_print_preview_page_handler.ts
@@ -0,0 +1,61 @@ +// Copyright 2024 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import {FakeMethodResolver} from 'chrome://resources/ash/common/fake_method_resolver.js'; + +import {type PrintPreviewPageHandler, type PrintRequestOutcome} from '../utils/print_preview_cros_app_types.js'; + +/** + * @fileoverview + * 'fake_print_preview_page_handler' is a mock implementation of the + * `PrintPreviewPageHandler` mojo interface. + */ + +const PRINT_METHOD = 'print'; +export const FAKE_PRINT_REQUEST_SUCCESSFUL: PrintRequestOutcome = { + success: true, +}; + +export const FAKE_PRINT_REQUEST_FAILURE_INVALID_SETTINGS_ERROR: + PrintRequestOutcome = { + success: false, + error: 'Invalid settings', + }; + +// Fake implementation of the PrintPreviewPageHandler for tests and UI. +export class FakePrintPreviewPageHandler implements PrintPreviewPageHandler { + private methods: FakeMethodResolver = new FakeMethodResolver(); + private callCount: Map<string, number> = new Map<string, number>(); + constructor() { + this.registerMethods(); + } + + private registerMethods() { + this.methods.register(PRINT_METHOD); + this.methods.setResult(PRINT_METHOD, FAKE_PRINT_REQUEST_SUCCESSFUL); + this.callCount.set(PRINT_METHOD, 0); + } + + // Handles restoring state of fake to initial state. + reset(): void { + this.callCount.clear(); + this.methods = new FakeMethodResolver(); + this.registerMethods(); + } + + setPrintResult(result: PrintRequestOutcome) { + this.methods.setResult(PRINT_METHOD, result); + } + + // Mock implementation of print. + print(): Promise<PrintRequestOutcome> { + const prevCallCount = this.callCount.get(PRINT_METHOD) ?? 0; + this.callCount.set(PRINT_METHOD, prevCallCount + 1); + return this.methods.resolveMethod(PRINT_METHOD); + } + + getCallCount(method: string): number { + return this.callCount.get(method) ?? 0; + } +}
diff --git a/ash/webui/print_preview_cros/resources/js/utils/mojo_data_providers.ts b/ash/webui/print_preview_cros/resources/js/utils/mojo_data_providers.ts new file mode 100644 index 0000000..78c1785 --- /dev/null +++ b/ash/webui/print_preview_cros/resources/js/utils/mojo_data_providers.ts
@@ -0,0 +1,33 @@ +// Copyright 2024 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import {assert} from 'chrome://resources/js/assert.js'; + +import {FakePrintPreviewPageHandler} from '../fakes/fake_print_preview_page_handler.js'; + +import {type PrintPreviewPageHandler} from './print_preview_cros_app_types.js'; + +/** + * @fileoverview + * 'mojo_data_providers' contains accessors to shared mojo data providers. As + * well as an override method to be used in tests. + */ + +let printPreviewPageHandler: PrintPreviewPageHandler|null = null; + +// Returns shared instance of PrintPreviewPageHandler. +export function getPrintPreviewPageHandler(): PrintPreviewPageHandler { + if (printPreviewPageHandler == null) { + printPreviewPageHandler = new FakePrintPreviewPageHandler(); + } + + assert(printPreviewPageHandler); + return printPreviewPageHandler; +} + +// Override shared instance of PrintPreviewPageHandle for testing. +export function setPrintPreviewPageHandlerForTesting( + handler: PrintPreviewPageHandler): void { + printPreviewPageHandler = handler; +}
diff --git a/ash/webui/print_preview_cros/resources/js/utils/print_preview_cros_app_types.ts b/ash/webui/print_preview_cros/resources/js/utils/print_preview_cros_app_types.ts new file mode 100644 index 0000000..1975c8e --- /dev/null +++ b/ash/webui/print_preview_cros/resources/js/utils/print_preview_cros_app_types.ts
@@ -0,0 +1,21 @@ +// Copyright 2024 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +/** + * @fileoverview + * 'print_preview_cros_app_types' contains app specific and mojo placeholder + * types. + */ + +export interface PrintRequestOutcome { + success: boolean; + error?: string; +} + +// Placeholder for PrintPreviewPageHandler mojo interface. +export interface PrintPreviewPageHandler { + // Start the print job and close the window. Needs to wait for result to + // display error messaging if starting the print job fails. + print(): Promise<PrintRequestOutcome>; +}
diff --git a/ash/wm/desks/desk.cc b/ash/wm/desks/desk.cc index 1318a68..130a83f 100644 --- a/ash/wm/desks/desk.cc +++ b/ash/wm/desks/desk.cc
@@ -6,6 +6,7 @@ #include <absl/cleanup/cleanup.h> #include <utility> +#include <vector> #include "ash/constants/app_types.h" #include "ash/public/cpp/window_properties.h" @@ -351,7 +352,7 @@ const auto windows = windows_; for (aura::Window* window : windows) { if (window->GetRootWindow() == root) - base::Erase(windows_, window); + std::erase(windows_, window); } if (last_active_root_ == root) { @@ -430,7 +431,7 @@ void Desk::RemoveWindowFromDesk(aura::Window* window) { DCHECK(base::Contains(windows_, window)); - base::Erase(windows_, window); + std::erase(windows_, window); // No need to refresh the mini_views if the destroyed window doesn't show up // there in the first place. Also don't refresh for visible on all desks // windows since they're already refreshed in OnWindowRemoved().
diff --git a/ash/wm/desks/templates/admin_template_launch_tracker.cc b/ash/wm/desks/templates/admin_template_launch_tracker.cc index c77494cd..ecb2ec4 100644 --- a/ash/wm/desks/templates/admin_template_launch_tracker.cc +++ b/ash/wm/desks/templates/admin_template_launch_tracker.cc
@@ -4,6 +4,8 @@ #include "ash/wm/desks/templates/admin_template_launch_tracker.h" +#include <vector> + #include "ash/public/cpp/saved_desk_delegate.h" #include "ash/root_window_settings.h" #include "ash/shell.h" @@ -493,7 +495,7 @@ void AdminTemplateLaunchTracker::OnObserverDone( base::CheckedObserver* observer) { - base::EraseIf(window_observers_, + std::erase_if(window_observers_, [&](const auto& ptr) { return ptr.get() == observer; }); }
diff --git a/ash/wm/desks/templates/saved_desk_presenter.cc b/ash/wm/desks/templates/saved_desk_presenter.cc index 1919fba..3caafe6 100644 --- a/ash/wm/desks/templates/saved_desk_presenter.cc +++ b/ash/wm/desks/templates/saved_desk_presenter.cc
@@ -4,6 +4,8 @@ #include "ash/wm/desks/templates/saved_desk_presenter.h" +#include <vector> + #include "ash/constants/notifier_catalogs.h" #include "ash/public/cpp/app_types_util.h" #include "ash/public/cpp/desk_template.h" @@ -25,7 +27,6 @@ #include "ash/wm/overview/overview_controller.h" #include "ash/wm/overview/overview_grid.h" #include "ash/wm/overview/overview_session.h" -#include "base/containers/cxx20_erase_vector.h" #include "base/functional/bind.h" #include "base/i18n/number_formatting.h" #include "base/memory/raw_ptr.h" @@ -692,7 +693,7 @@ Shell::Get()->mru_window_tracker()->BuildMruWindowList(kActiveDesk); // Get rid of transient windows and all-desks windows. - base::EraseIf(windows, [](aura::Window* window) { + std::erase_if(windows, [](aura::Window* window) { return wm::GetTransientParent(window) != nullptr || desks_util::IsWindowVisibleOnAllWorkspaces(window); });
diff --git a/ash/wm/mru_window_tracker.cc b/ash/wm/mru_window_tracker.cc index 3edf561..add5e79 100644 --- a/ash/wm/mru_window_tracker.cc +++ b/ash/wm/mru_window_tracker.cc
@@ -4,6 +4,8 @@ #include "ash/wm/mru_window_tracker.h" +#include <vector> + #include "ash/constants/app_types.h" #include "ash/public/cpp/window_properties.h" #include "ash/shell.h" @@ -301,7 +303,7 @@ // If nothing was erased, this is a window not currently observed so we want // to observe it as windows created from window restore aren't activated on // creation. - size_t num_erased = base::Erase(mru_windows_, window); + size_t num_erased = std::erase(mru_windows_, window); if (num_erased == 0u) window->AddObserver(this); @@ -345,7 +347,7 @@ // It's possible for OnWindowActivated() to be called after // OnWindowDestroying(). This means we need to override OnWindowDestroyed() // else we may end up with a deleted window in |mru_windows_|. - base::Erase(mru_windows_, window); + std::erase(mru_windows_, window); window->RemoveObserver(this); }
diff --git a/ash/wm/overview/birch/birch_bar_view.cc b/ash/wm/overview/birch/birch_bar_view.cc index 0086a2fb..193cf68 100644 --- a/ash/wm/overview/birch/birch_bar_view.cc +++ b/ash/wm/overview/birch/birch_bar_view.cc
@@ -4,6 +4,8 @@ #include "ash/wm/overview/birch/birch_bar_view.h" +#include <vector> + #include "ash/public/cpp/shelf_config.h" #include "ash/public/cpp/shelf_types.h" #include "ash/public/cpp/window_properties.h" @@ -154,7 +156,7 @@ void BirchBarView::RemoveChip(BirchChipButton* chip) { CHECK(base::Contains(chips_, chip)); - base::Erase(chips_, chip); + std::erase(chips_, chip); // Remove the chip from its owner. if (primary_row_->Contains(chip)) { primary_row_->RemoveChildViewT(chip);
diff --git a/ash/wm/overview/overview_controller.cc b/ash/wm/overview/overview_controller.cc index d59eb4b..441053c 100644 --- a/ash/wm/overview/overview_controller.cc +++ b/ash/wm/overview/overview_controller.cc
@@ -5,6 +5,7 @@ #include "ash/wm/overview/overview_controller.h" #include <utility> +#include <vector> #include "ash/frame_throttler/frame_throttling_controller.h" #include "ash/keyboard/ui/keyboard_ui_controller.h" @@ -290,7 +291,7 @@ void OverviewController::RemoveAndDestroyExitAnimationObserver( DelayedAnimationObserver* animation_observer) { const bool previous_empty = delayed_animations_.empty(); - base::EraseIf(delayed_animations_, + std::erase_if(delayed_animations_, base::MatchesUniquePtr(animation_observer)); if (!overview_session_ && !previous_empty && delayed_animations_.empty()) @@ -306,7 +307,7 @@ void OverviewController::RemoveAndDestroyEnterAnimationObserver( DelayedAnimationObserver* animation_observer) { const bool previous_empty = start_animations_.empty(); - base::EraseIf(start_animations_, base::MatchesUniquePtr(animation_observer)); + std::erase_if(start_animations_, base::MatchesUniquePtr(animation_observer)); if (!previous_empty && start_animations_.empty()) OnStartingAnimationComplete(/*canceled=*/false); @@ -353,7 +354,7 @@ auto end = base::ranges::copy_if(windows, hide_windows.begin(), should_hide_for_overview); hide_windows.resize(end - hide_windows.begin()); - base::EraseIf(windows, window_util::ShouldExcludeForOverview); + std::erase_if(windows, window_util::ShouldExcludeForOverview); // Overview windows will handle showing their transient related windows, so if // a window in |windows| has a transient root also in |windows|, we can remove // it as the transient root will handle showing the window.
diff --git a/ash/wm/overview/overview_grid.cc b/ash/wm/overview/overview_grid.cc index 5c55c932..52556cc 100644 --- a/ash/wm/overview/overview_grid.cc +++ b/ash/wm/overview/overview_grid.cc
@@ -7,6 +7,7 @@ #include <algorithm> #include <string> #include <utility> +#include <vector> #include "ash/accessibility/accessibility_controller.h" #include "ash/constants/app_types.h" @@ -1030,7 +1031,7 @@ // Copy to a local first to avoid a dangling pointer. OverviewDropTarget* drop_target_ptr = std::exchange(drop_target_, nullptr); - size_t erased_count = base::EraseIf( + size_t erased_count = std::erase_if( item_list_, base::MatchesUniquePtr<OverviewItemBase>(drop_target_ptr)); CHECK_EQ(1u, erased_count); @@ -3185,8 +3186,10 @@ if (!feedback_widget_) { auto contents_view = std::make_unique<PillButton>( // TODO(hewer): Add callback to open a feedback page. - views::Button::PressedCallback(), u"Send Feedback", - PillButton::Type::kDefaultWithIconLeading, &kFeedbackIcon); + base::BindRepeating(&OverviewGrid::ShowFeedbackPage, + base::Unretained(this)), + u"Send Feedback", PillButton::Type::kDefaultWithIconLeading, + &kFeedbackIcon); views::Widget::InitParams params; params.init_properties_container.SetProperty(kHideInDeskMiniViewKey, true); @@ -3213,4 +3216,11 @@ contents_size.width(), contents_size.height())); } +void OverviewGrid::ShowFeedbackPage() { + Shell::Get()->shell_delegate()->OpenFeedbackDialog( + ShellDelegate::FeedbackSource::kOverview, + /*description_template=*/std::string(), + /*category_tag=*/"FromForest"); +} + } // namespace ash
diff --git a/ash/wm/overview/overview_grid.h b/ash/wm/overview/overview_grid.h index 7b887f8..d4a1e31 100644 --- a/ash/wm/overview/overview_grid.h +++ b/ash/wm/overview/overview_grid.h
@@ -632,6 +632,9 @@ // feedback page when clicked. The widget will not show in partial overview. void UpdateFeedbackButton(); + // Shows the feedback page with preset information for overview. + void ShowFeedbackPage(); + // The drop target is created when a window or overview item is being dragged, // and is destroyed when the drag ends or overview mode is ended. The drop // target is hidden when a snap preview area is shown. You can drop a window
diff --git a/ash/wm/snap_group/snap_group_controller.cc b/ash/wm/snap_group/snap_group_controller.cc index b87e340..a8a81ad 100644 --- a/ash/wm/snap_group/snap_group_controller.cc +++ b/ash/wm/snap_group/snap_group_controller.cc
@@ -4,6 +4,8 @@ #include "ash/wm/snap_group/snap_group_controller.h" +#include <vector> + #include "ash/root_window_controller.h" #include "ash/shell.h" #include "ash/wm/mru_window_tracker.h" @@ -114,7 +116,7 @@ observer.OnSnapGroupRemoved(snap_group); } - base::EraseIf(snap_groups_, base::MatchesUniquePtr(snap_group)); + std::erase_if(snap_groups_, base::MatchesUniquePtr(snap_group)); return true; }
diff --git a/ash/wm/splitview/split_view_controller.cc b/ash/wm/splitview/split_view_controller.cc index 502d6d2..515055ec 100644 --- a/ash/wm/splitview/split_view_controller.cc +++ b/ash/wm/splitview/split_view_controller.cc
@@ -9,6 +9,7 @@ #include <cstdint> #include <limits> #include <optional> +#include <vector> #include "ash/accessibility/accessibility_controller.h" #include "ash/constants/app_types.h" @@ -2307,17 +2308,17 @@ static_cast<float>(min_right_size) / divider_upper_limit; if (min_size_left_ratio > chromeos::kOneThirdSnapRatio) { // If `primary_window_` can't fit in 1/3, remove 0.33f divider position. - base::Erase(*out_position_ratios, chromeos::kOneThirdSnapRatio); + std::erase(*out_position_ratios, chromeos::kOneThirdSnapRatio); } if (min_size_right_ratio > chromeos::kOneThirdSnapRatio) { // If `secondary_window_` can't fit in 1/3, remove 0.67f divider position. - base::Erase(*out_position_ratios, chromeos::kTwoThirdSnapRatio); + std::erase(*out_position_ratios, chromeos::kTwoThirdSnapRatio); } // Remove 0.5f if a window cannot be snapped. We can get into this state by // snapping a window to two thirds. if (min_size_left_ratio > chromeos::kDefaultSnapRatio || min_size_right_ratio > chromeos::kDefaultSnapRatio) { - base::Erase(*out_position_ratios, chromeos::kDefaultSnapRatio); + std::erase(*out_position_ratios, chromeos::kDefaultSnapRatio); } }
diff --git a/ash/wm/splitview/split_view_metrics_controller.cc b/ash/wm/splitview/split_view_metrics_controller.cc index aaa77fcc..2b582ec 100644 --- a/ash/wm/splitview/split_view_metrics_controller.cc +++ b/ash/wm/splitview/split_view_metrics_controller.cc
@@ -4,6 +4,8 @@ #include "ash/wm/splitview/split_view_metrics_controller.h" +#include <vector> + #include "ash/root_window_controller.h" #include "ash/root_window_settings.h" #include "ash/shell.h" @@ -551,7 +553,7 @@ } first_minimized_window_state_ = nullptr; } - if (base::Erase(observed_windows_, window)) { + if (std::erase(observed_windows_, window)) { WindowState::Get(window)->RemoveObserver(this); window->RemoveObserver(this); }
diff --git a/ash/wm/splitview/split_view_utils.cc b/ash/wm/splitview/split_view_utils.cc index bf11777e..404fed3 100644 --- a/ash/wm/splitview/split_view_utils.cc +++ b/ash/wm/splitview/split_view_utils.cc
@@ -915,10 +915,6 @@ return false; } - // TODO(michelefan): Currently apply the snap source limitations for faster - // flag only. It will be removed when we figure out a good way to restore two - // windows in a snap group. - if (features::IsFasterSplitScreenSetupEnabled()) { if (PrefService* pref = Shell::Get()->session_controller()->GetActivePrefService(); pref && !pref->GetBoolean(prefs::kSnapWindowSuggestions)) { @@ -928,7 +924,6 @@ if (!CanSnapActionSourceStartFasterSplitView(snap_action_source)) { return false; } - } return !IsInOverviewSession(); }
diff --git a/ash/wm/tablet_mode/tablet_mode_window_manager.cc b/ash/wm/tablet_mode/tablet_mode_window_manager.cc index 3108d73..5db006b 100644 --- a/ash/wm/tablet_mode/tablet_mode_window_manager.cc +++ b/ash/wm/tablet_mode/tablet_mode_window_manager.cc
@@ -5,6 +5,7 @@ #include "ash/wm/tablet_mode/tablet_mode_window_manager.h" #include <memory> +#include <vector> #include "ash/public/cpp/window_properties.h" #include "ash/root_window_controller.h" @@ -32,7 +33,6 @@ #include "ash/wm/workspace/workspace_layout_manager.h" #include "ash/wm/workspace_controller.h" #include "base/containers/contains.h" -#include "base/containers/cxx20_erase.h" #include "base/memory/raw_ptr.h" #include "chromeos/ui/base/window_properties.h" #include "ui/aura/client/aura_constants.h" @@ -546,10 +546,9 @@ // IsCarryOverCandidateForSplitView() to be carried over to splitscreen. MruWindowTracker::WindowList mru_windows = Shell::Get()->mru_window_tracker()->BuildWindowForCycleList(kActiveDesk); - base::EraseIf(mru_windows, [](aura::Window* window) { - return window->GetProperty( - chromeos::kIsShowingInOverviewKey); - }); + std::erase_if(mru_windows, [](aura::Window* window) { + return window->GetProperty(chromeos::kIsShowingInOverviewKey); + }); aura::Window* root_window = Shell::GetPrimaryRootWindow(); if (IsCarryOverCandidateForSplitView(mru_windows, 0u, root_window)) { if (GetWindowStateType(mru_windows[0], clamshell_to_tablet) ==
diff --git a/ash/wm/window_cycle/window_cycle_view.cc b/ash/wm/window_cycle/window_cycle_view.cc index 846482ad..1129d10c 100644 --- a/ash/wm/window_cycle/window_cycle_view.cc +++ b/ash/wm/window_cycle/window_cycle_view.cc
@@ -455,8 +455,8 @@ // With no remaining child mini views contained in `preview`, we need to // remove `preview` and clean up the `preview` in `cycle_views_` and // `no_previews_list_`. - base::Erase(cycle_views_, preview); - base::Erase(no_previews_list_, preview); + std::erase(cycle_views_, preview); + std::erase(no_previews_list_, preview); parent->RemoveChildViewT(preview); } // With one of its children now gone, we must re-layout `mirror_container_`.
diff --git a/base/json/json_correctness_fuzzer.cc b/base/json/json_correctness_fuzzer.cc index eb76ead..532d037 100644 --- a/base/json/json_correctness_fuzzer.cc +++ b/base/json/json_correctness_fuzzer.cc
@@ -10,6 +10,7 @@ #include <stdint.h> #include <string> +#include <string_view> #include "base/json/json_reader.h" #include "base/json/json_writer.h" @@ -29,7 +30,7 @@ std::unique_ptr<char[]> input(new char[size - 1]); memcpy(input.get(), data, size - 1); - base::StringPiece input_string(input.get(), size - 1); + std::string_view input_string(input.get(), size - 1); const int options = data[size - 1]; auto result =
diff --git a/base/json/json_parser.cc b/base/json/json_parser.cc index db7c85a..d149095 100644 --- a/base/json/json_parser.cc +++ b/base/json/json_parser.cc
@@ -6,7 +6,7 @@ #include <cmath> #include <iterator> -#include <optional> +#include <string_view> #include <utility> #include <vector> @@ -19,7 +19,6 @@ #include "base/numerics/safe_conversions.h" #include "base/ranges/algorithm.h" #include "base/strings/string_number_conversions.h" -#include "base/strings/string_piece.h" #include "base/strings/string_util.h" #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversion_utils.h" @@ -70,7 +69,7 @@ // UnprefixedHexStringToInt acts like |HexStringToInt|, but enforces that the // input consists purely of hex digits. I.e. no "0x" nor "OX" prefix is // permitted. -bool UnprefixedHexStringToInt(StringPiece input, int* output) { +bool UnprefixedHexStringToInt(std::string_view input, int* output) { for (size_t i = 0; i < input.size(); i++) { if (!IsHexDigit(input[i])) { return false; @@ -127,7 +126,7 @@ JSONParser::~JSONParser() = default; -std::optional<Value> JSONParser::Parse(StringPiece input) { +std::optional<Value> JSONParser::Parse(std::string_view input) { input_ = input; index_ = 0; // Line and column counting is 1-based, but |index_| is 0-based. For example, @@ -230,30 +229,30 @@ // JSONParser private ////////////////////////////////////////////////////////// -std::optional<StringPiece> JSONParser::PeekChars(size_t count) { +std::optional<std::string_view> JSONParser::PeekChars(size_t count) { if (index_ + count > input_.length()) return std::nullopt; // Using StringPiece::substr() is significantly slower (according to // base_perftests) than constructing a substring manually. - return StringPiece(input_.data() + index_, count); + return std::string_view(input_.data() + index_, count); } std::optional<char> JSONParser::PeekChar() { - std::optional<StringPiece> chars = PeekChars(1); + std::optional<std::string_view> chars = PeekChars(1); if (chars) return (*chars)[0]; return std::nullopt; } -std::optional<StringPiece> JSONParser::ConsumeChars(size_t count) { - std::optional<StringPiece> chars = PeekChars(count); +std::optional<std::string_view> JSONParser::ConsumeChars(size_t count) { + std::optional<std::string_view> chars = PeekChars(count); if (chars) index_ += count; return chars; } std::optional<char> JSONParser::ConsumeChar() { - std::optional<StringPiece> chars = ConsumeChars(1); + std::optional<std::string_view> chars = ConsumeChars(1); if (chars) return (*chars)[0]; return std::nullopt; @@ -335,7 +334,7 @@ } bool JSONParser::EatComment() { - std::optional<StringPiece> comment_start = PeekChars(2); + std::optional<std::string_view> comment_start = PeekChars(2); if (!comment_start) return false; @@ -530,7 +529,7 @@ return false; } - // StringBuilder will internally build a StringPiece unless a UTF-16 + // StringBuilder will internally build a std::string_view unless a UTF-16 // conversion occurs, at which point it will perform a copy into a // std::string. StringBuilder string(pos()); @@ -589,12 +588,12 @@ } else { // And if it is an escape sequence, the input string will be adjusted // (either by combining the two characters of an encoded escape sequence, - // or with a UTF conversion), so using StringPiece isn't possible -- force - // a conversion. + // or with a UTF conversion), so using std::string_view isn't possible -- + // force a conversion. string.Convert(); // Read past the escape '\' and ensure there's a character following. - std::optional<StringPiece> escape_sequence = ConsumeChars(2); + std::optional<std::string_view> escape_sequence = ConsumeChars(2); if (!escape_sequence) { ReportError(JSON_INVALID_ESCAPE, -1); return false; @@ -685,7 +684,7 @@ // Entry is at the first X in \uXXXX. bool JSONParser::DecodeUTF16(base_icu::UChar32* out_code_point) { - std::optional<StringPiece> escape_sequence = ConsumeChars(4); + std::optional<std::string_view> escape_sequence = ConsumeChars(4); if (!escape_sequence) return false; @@ -800,7 +799,7 @@ index_ = exit_index; - StringPiece num_string(num_start, end_index - start_index); + std::string_view num_string(num_start, end_index - start_index); int num_int; if (StringToInt(num_string, &num_int)) { @@ -858,7 +857,7 @@ return std::nullopt; } -bool JSONParser::ConsumeIfMatch(StringPiece match) { +bool JSONParser::ConsumeIfMatch(std::string_view match) { if (match == PeekChars(match.size())) { ConsumeChars(match.size()); return true;
diff --git a/base/json/json_parser.h b/base/json/json_parser.h index 7a9cc688..d0c5caa 100644 --- a/base/json/json_parser.h +++ b/base/json/json_parser.h
@@ -11,12 +11,12 @@ #include <memory> #include <optional> #include <string> +#include <string_view> #include "base/base_export.h" #include "base/compiler_specific.h" #include "base/gtest_prod_util.h" #include "base/json/json_common.h" -#include "base/strings/string_piece.h" #include "base/third_party/icu/icu_utf.h" #include "base/values.h" @@ -81,7 +81,7 @@ // result as a Value. // Wrap this in base::FooValue::From() to check the Value is of type Foo and // convert to a FooValue at the same time. - std::optional<Value> Parse(StringPiece input); + std::optional<Value> Parse(std::string_view input); // Returns the error code. JsonParseError error_code() const; @@ -115,7 +115,7 @@ }; // A helper class used for parsing strings. One optimization performed is to - // create base::Value with a StringPiece to avoid unnecessary std::string + // create base::Value with a std::string_view to avoid unnecessary std::string // copies. This is not possible if the input string needs to be decoded from // UTF-16 to UTF-8, or if an escape sequence causes characters to be skipped. // This class centralizes that logic. @@ -136,9 +136,9 @@ // converted, or by appending the UTF8 bytes for the code point. void Append(base_icu::UChar32 point); - // Converts the builder from its default StringPiece to a full std::string, - // performing a copy. Once a builder is converted, it cannot be made a - // StringPiece again. + // Converts the builder from its default std::string_view to a full + // std::string, performing a copy. Once a builder is converted, it cannot be + // made a std::string_view again. void Convert(); // Returns the builder as a string, invalidating all state. This allows @@ -160,14 +160,14 @@ // Returns the next |count| bytes of the input stream, or nullopt if fewer // than |count| bytes remain. - std::optional<StringPiece> PeekChars(size_t count); + std::optional<std::string_view> PeekChars(size_t count); // Calls PeekChars() with a |count| of 1. std::optional<char> PeekChar(); // Returns the next |count| bytes of the input stream, or nullopt if fewer // than |count| bytes remain, and advances the parser position by |count|. - std::optional<StringPiece> ConsumeChars(size_t count); + std::optional<std::string_view> ConsumeChars(size_t count); // Calls ConsumeChars() with a |count| of 1. std::optional<char> ConsumeChar(); @@ -230,7 +230,7 @@ // consumed at the current parser position. Returns false if there are fewer // than |match|-length bytes or if the sequence does not match, and the // parser state is unchanged. - bool ConsumeIfMatch(StringPiece match); + bool ConsumeIfMatch(std::string_view match); // Sets the error information to |code| at the current column, based on // |index_| and |index_last_line_|, with an optional positive/negative @@ -249,7 +249,7 @@ const size_t max_depth_; // The input stream being parsed. Note: Not guaranteed to NUL-terminated. - StringPiece input_; + std::string_view input_; // The index in the input stream to which the parser is wound. size_t index_;
diff --git a/base/json/json_reader.cc b/base/json/json_reader.cc index 62e670c..ec86924 100644 --- a/base/json/json_reader.cc +++ b/base/json/json_reader.cc
@@ -4,7 +4,7 @@ #include "base/json/json_reader.h" -#include <optional> +#include <string_view> #include <utility> #include "base/features.h" @@ -81,7 +81,7 @@ dict.Set(base::RustStrToStringPiece(key), base::Value(As{v})); } -JSONReader::Result DecodeJSONInRust(const base::StringPiece& json, +JSONReader::Result DecodeJSONInRust(std::string_view json, int options, size_t max_depth) { const serde_json_lenient::JsonOptions rust_options = { @@ -134,7 +134,7 @@ #endif // BUILDFLAG(BUILD_RUST_JSON_READER) // static -std::optional<Value> JSONReader::Read(StringPiece json, +std::optional<Value> JSONReader::Read(std::string_view json, int options, size_t max_depth) { #if BUILDFLAG(BUILD_RUST_JSON_READER) @@ -156,7 +156,7 @@ } // static -std::optional<Value::Dict> JSONReader::ReadDict(StringPiece json, +std::optional<Value::Dict> JSONReader::ReadDict(std::string_view json, int options, size_t max_depth) { std::optional<Value> value = Read(json, options, max_depth); @@ -167,8 +167,9 @@ } // static -JSONReader::Result JSONReader::ReadAndReturnValueWithError(StringPiece json, - int options) { +JSONReader::Result JSONReader::ReadAndReturnValueWithError( + std::string_view json, + int options) { #if BUILDFLAG(BUILD_RUST_JSON_READER) SCOPED_UMA_HISTOGRAM_TIMER_MICROS(kSecurityJsonParsingTime); if (UsingRust()) {
diff --git a/base/json/json_reader.h b/base/json/json_reader.h index 52966a5..5ab1f88 100644 --- a/base/json/json_reader.h +++ b/base/json/json_reader.h
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// A JSON parser, converting from a base::StringPiece to a base::Value. +// A JSON parser, converting from a std::string_view to a base::Value. // // The JSON spec is: // https://tools.ietf.org/rfc/rfc8259.txt @@ -14,7 +14,7 @@ // Implementation choices permitted by the RFC: // - Nesting is limited (to a configurable depth, 200 by default). // - Numbers are limited to those representable by a finite double. The -// conversion from a JSON number (in the base::StringPiece input) to a +// conversion from a JSON number (in the std::string_view input) to a // double-flavored base::Value may also be lossy. // - The input (which must be UTF-8) may begin with a BOM (Byte Order Mark). // - Duplicate object keys (strings) are silently allowed. Last key-value pair @@ -38,11 +38,11 @@ #include <optional> #include <string> +#include <string_view> #include "base/base_export.h" #include "base/json/json_common.h" #include "base/strings/string_number_conversions.h" -#include "base/strings/string_piece.h" #include "base/types/expected.h" #include "base/values.h" @@ -105,16 +105,16 @@ JSONReader& operator=(const JSONReader&) = delete; // Reads and parses |json|, returning a Value. - // If |json| is not a properly formed JSON string, returns std::nullopt. + // If |json| is not a properly formed JSON string, returns absl::nullopt. static std::optional<Value> Read( - StringPiece json, + std::string_view json, int options = JSON_PARSE_CHROMIUM_EXTENSIONS, size_t max_depth = internal::kAbsoluteMaxDepth); // Reads and parses |json|, returning a Value::Dict. - // If |json| is not a properly formed JSON dict string, returns std::nullopt. + // If |json| is not a properly formed JSON dict string, returns absl::nullopt. static std::optional<Value::Dict> ReadDict( - StringPiece json, + std::string_view json, int options = JSON_PARSE_CHROMIUM_EXTENSIONS, size_t max_depth = internal::kAbsoluteMaxDepth); @@ -123,7 +123,7 @@ // formatted error message, an error code, and the error location if // appropriate as the error value of the expected type. static Result ReadAndReturnValueWithError( - StringPiece json, + std::string_view json, int options = JSON_PARSE_CHROMIUM_EXTENSIONS); // Determine whether the Rust parser is in use.
diff --git a/base/json/json_reader_fuzzer.cc b/base/json/json_reader_fuzzer.cc index 17a56062..43953a0 100644 --- a/base/json/json_reader_fuzzer.cc +++ b/base/json/json_reader_fuzzer.cc
@@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include <string_view> + #include "base/json/json_reader.h" #include <optional> @@ -21,7 +23,7 @@ std::unique_ptr<char[]> input(new char[size - 1]); memcpy(input.get(), data, size - 1); - StringPiece input_string(input.get(), size - 1); + std::string_view input_string(input.get(), size - 1); const int options = data[size - 1]; @@ -35,7 +37,7 @@ CHECK(JSONWriter::Write(value, &serialized)); std::optional<Value> deserialized = - JSONReader::Read(StringPiece(serialized)); + JSONReader::Read(std::string_view(serialized)); CHECK(deserialized); CHECK_EQ(value, deserialized.value()); }
diff --git a/base/json/json_reader_unittest.cc b/base/json/json_reader_unittest.cc index 10155e4..8b1194ee 100644 --- a/base/json/json_reader_unittest.cc +++ b/base/json/json_reader_unittest.cc
@@ -7,7 +7,7 @@ #include <stddef.h> #include <cmath> -#include <optional> +#include <string_view> #include <utility> #include "base/base_paths.h" @@ -16,7 +16,6 @@ #include "base/logging.h" #include "base/path_service.h" #include "base/rust_buildflags.h" -#include "base/strings/string_piece.h" #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" #include "base/test/gmock_expected_support.h" @@ -31,13 +30,13 @@ // MSan will do a better job detecting over-read errors if the input is not // nul-terminated on the heap. This will copy |input| to a new buffer owned by -// |owner|, returning a base::StringPiece to |owner|. -base::StringPiece MakeNotNullTerminatedInput(const char* input, - std::unique_ptr<char[]>* owner) { +// |owner|, returning a std::string_view to |owner|. +std::string_view MakeNotNullTerminatedInput(const char* input, + std::unique_ptr<char[]>* owner) { size_t str_len = strlen(input); owner->reset(new char[str_len]); memcpy(owner->get(), input, str_len); - return base::StringPiece(owner->get(), str_len); + return std::string_view(owner->get(), str_len); } } // namespace @@ -988,7 +987,7 @@ SCOPED_TRACE(StringPrintf("case %u: \"%s\"", i, test_case.input)); std::unique_ptr<char[]> input_owner; - StringPiece input = + std::string_view input = MakeNotNullTerminatedInput(test_case.input, &input_owner); std::optional<Value> result = JSONReader::Read(input); @@ -1029,7 +1028,8 @@ SCOPED_TRACE(StringPrintf("case %u: \"%s\"", i, test_case)); std::unique_ptr<char[]> input_owner; - StringPiece input = MakeNotNullTerminatedInput(test_case, &input_owner); + std::string_view input = + MakeNotNullTerminatedInput(test_case, &input_owner); EXPECT_FALSE(JSONReader::Read(input)); }
diff --git a/base/json/json_string_value_serializer.cc b/base/json/json_string_value_serializer.cc index 561bfa07..ee858b6 100644 --- a/base/json/json_string_value_serializer.cc +++ b/base/json/json_string_value_serializer.cc
@@ -4,6 +4,8 @@ #include "base/json/json_string_value_serializer.h" +#include <string_view> + #include "base/json/json_reader.h" #include "base/json/json_writer.h" @@ -40,7 +42,7 @@ } JSONStringValueDeserializer::JSONStringValueDeserializer( - const base::StringPiece& json_string, + std::string_view json_string, int options) : json_string_(json_string), options_(options) {}
diff --git a/base/json/json_string_value_serializer.h b/base/json/json_string_value_serializer.h index 5386546..d14fecf 100644 --- a/base/json/json_string_value_serializer.h +++ b/base/json/json_string_value_serializer.h
@@ -7,11 +7,11 @@ #include <memory> #include <string> +#include <string_view> #include "base/base_export.h" #include "base/json/json_reader.h" #include "base/memory/raw_ptr.h" -#include "base/strings/string_piece.h" #include "base/values.h" class BASE_EXPORT JSONStringValueSerializer : public base::ValueSerializer { @@ -53,7 +53,7 @@ // must outlive the JSONStringValueDeserializer. |options| is a bitmask of // JSONParserOptions. explicit JSONStringValueDeserializer( - const base::StringPiece& json_string, + std::string_view json_string, int options = base::JSON_PARSE_CHROMIUM_EXTENSIONS); JSONStringValueDeserializer(const JSONStringValueDeserializer&) = delete; @@ -76,7 +76,7 @@ private: // Data is owned by the caller of the constructor. - base::StringPiece json_string_; + std::string_view json_string_; const int options_; };
diff --git a/base/json/json_value_converter.h b/base/json/json_value_converter.h index 918afce2..5afcdb6f 100644 --- a/base/json/json_value_converter.h +++ b/base/json/json_value_converter.h
@@ -9,13 +9,13 @@ #include <memory> #include <string> +#include <string_view> #include <utility> #include <vector> #include "base/base_export.h" #include "base/logging.h" #include "base/memory/ptr_util.h" -#include "base/strings/string_piece.h" #include "base/values.h" // JSONValueConverter converts a JSON value into a C++ struct in a @@ -70,8 +70,8 @@ // // Sometimes JSON format uses string representations for other types such // like enum, timestamp, or URL. You can use RegisterCustomField method -// and specify a function to convert a StringPiece to your type. -// bool ConvertFunc(StringPiece s, YourEnum* result) { +// and specify a function to convert a std::string_view to your type. +// bool ConvertFunc(std::string_view s, YourEnum* result) { // // do something and return true if succeed... // } // struct Message { @@ -219,7 +219,7 @@ template <typename FieldType> class CustomFieldConverter : public ValueConverter<FieldType> { public: - typedef bool (*ConvertFunc)(StringPiece value, FieldType* field); + typedef bool (*ConvertFunc)(std::string_view value, FieldType* field); explicit CustomFieldConverter(ConvertFunc convert_func) : convert_func_(convert_func) {} @@ -415,7 +415,7 @@ template <typename FieldType> void RegisterCustomField(const std::string& field_name, FieldType StructType::*field, - bool (*convert_func)(StringPiece, FieldType*)) { + bool (*convert_func)(std::string_view, FieldType*)) { fields_.push_back( std::make_unique<internal::FieldConverter<StructType, FieldType>>( field_name, field,
diff --git a/base/json/json_value_converter_unittest.cc b/base/json/json_value_converter_unittest.cc index 9a95421..921aeda 100644 --- a/base/json/json_value_converter_unittest.cc +++ b/base/json/json_value_converter_unittest.cc
@@ -7,10 +7,10 @@ #include <memory> #include <optional> #include <string> +#include <string_view> #include <vector> #include "base/json/json_reader.h" -#include "base/strings/string_piece.h" #include "base/values.h" #include "testing/gtest/include/gtest/gtest.h" @@ -32,7 +32,7 @@ std::vector<std::unique_ptr<std::string>> string_values; SimpleMessage() : foo(0), baz(false), bstruct(false), simple_enum(FOO) {} - static bool ParseSimpleEnum(StringPiece value, SimpleEnum* field) { + static bool ParseSimpleEnum(std::string_view value, SimpleEnum* field) { if (value == "foo") { *field = FOO; return true;
diff --git a/base/json/json_value_serializer_unittest.cc b/base/json/json_value_serializer_unittest.cc index 30ab7bc..12e10a4 100644 --- a/base/json/json_value_serializer_unittest.cc +++ b/base/json/json_value_serializer_unittest.cc
@@ -5,6 +5,7 @@ #include <memory> #include <optional> #include <string> +#include <string_view> #include "base/files/file_util.h" #include "base/files/scoped_temp_dir.h" @@ -13,7 +14,6 @@ #include "base/json/json_string_value_serializer.h" #include "base/json/json_writer.h" #include "base/path_service.h" -#include "base/strings/string_piece.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" #include "base/values.h" @@ -103,11 +103,11 @@ CheckJSONIsStillTheSame(*value); } -// Test proper JSON deserialization from a StringPiece substring. +// Test proper JSON deserialization from a std::string_view substring. TEST(JSONValueDeserializerTest, ReadProperJSONFromStringPiece) { - // Create a StringPiece for the substring of kProperJSONPadded that matches - // kProperJSON. - StringPiece proper_json(kProperJSONPadded); + // Create a std::string_view for the substring of kProperJSONPadded that + // matches kProperJSON. + std::string_view proper_json(kProperJSONPadded); proper_json = proper_json.substr(5, proper_json.length() - 10); JSONStringValueDeserializer str_deserializer(proper_json);
diff --git a/base/json/json_writer.cc b/base/json/json_writer.cc index a58ec155..ef6de84e 100644 --- a/base/json/json_writer.cc +++ b/base/json/json_writer.cc
@@ -8,6 +8,7 @@ #include <cmath> #include <limits> +#include <string_view> #include "base/json/string_escape.h" #include "base/logging.h" @@ -106,7 +107,7 @@ return true; } -bool JSONWriter::BuildJSONString(StringPiece node, size_t depth) { +bool JSONWriter::BuildJSONString(std::string_view node, size_t depth) { EscapeJSONString(node, true, json_string_); return true; }
diff --git a/base/json/json_writer.h b/base/json/json_writer.h index 05aef57..c9eb3bd 100644 --- a/base/json/json_writer.h +++ b/base/json/json_writer.h
@@ -10,11 +10,11 @@ #include <cstdint> #include <optional> #include <string> +#include <string_view> #include "base/base_export.h" #include "base/json/json_common.h" #include "base/memory/raw_ptr.h" -#include "base/strings/string_piece.h" #include "base/values.h" namespace base { @@ -106,7 +106,7 @@ bool BuildJSONString(bool node, size_t depth); bool BuildJSONString(int node, size_t depth); bool BuildJSONString(double node, size_t depth); - bool BuildJSONString(StringPiece node, size_t depth); + bool BuildJSONString(std::string_view node, size_t depth); bool BuildJSONString(const Value::BlobStorage& node, size_t depth); bool BuildJSONString(const Value::Dict& node, size_t depth); bool BuildJSONString(const Value::List& node, size_t depth);
diff --git a/base/json/string_escape.cc b/base/json/string_escape.cc index 84db0f8..aab1a8e 100644 --- a/base/json/string_escape.cc +++ b/base/json/string_escape.cc
@@ -9,6 +9,7 @@ #include <limits> #include <string> +#include <string_view> #include "base/check_op.h" #include "base/strings/string_util.h" @@ -112,29 +113,31 @@ } // namespace -bool EscapeJSONString(StringPiece str, bool put_in_quotes, std::string* dest) { - return EscapeJSONStringImpl(str, put_in_quotes, dest); -} - -bool EscapeJSONString(StringPiece16 str, +bool EscapeJSONString(std::string_view str, bool put_in_quotes, std::string* dest) { return EscapeJSONStringImpl(str, put_in_quotes, dest); } -std::string GetQuotedJSONString(StringPiece str) { +bool EscapeJSONString(std::u16string_view str, + bool put_in_quotes, + std::string* dest) { + return EscapeJSONStringImpl(str, put_in_quotes, dest); +} + +std::string GetQuotedJSONString(std::string_view str) { std::string dest; EscapeJSONStringImpl(str, true, &dest); return dest; } -std::string GetQuotedJSONString(StringPiece16 str) { +std::string GetQuotedJSONString(std::u16string_view str) { std::string dest; EscapeJSONStringImpl(str, true, &dest); return dest; } -std::string EscapeBytesAsInvalidJSONString(StringPiece str, +std::string EscapeBytesAsInvalidJSONString(std::string_view str, bool put_in_quotes) { std::string dest;
diff --git a/base/json/string_escape.h b/base/json/string_escape.h index ca7f7fa..6c93ed2 100644 --- a/base/json/string_escape.h +++ b/base/json/string_escape.h
@@ -8,9 +8,9 @@ #define BASE_JSON_STRING_ESCAPE_H_ #include <string> +#include <string_view> #include "base/base_export.h" -#include "base/strings/string_piece.h" namespace base { @@ -26,21 +26,21 @@ // // If |put_in_quotes| is true, then a leading and trailing double-quote mark // will be appended to |dest| as well. -BASE_EXPORT bool EscapeJSONString(StringPiece str, +BASE_EXPORT bool EscapeJSONString(std::string_view str, bool put_in_quotes, std::string* dest); -// Performs a similar function to the UTF-8 StringPiece version above, +// Performs a similar function to the UTF-8 std::string_view version above, // converting UTF-16 code units to UTF-8 code units and escaping non-printing // control characters. On return, |dest| will contain a valid UTF-8 JSON string. -BASE_EXPORT bool EscapeJSONString(StringPiece16 str, +BASE_EXPORT bool EscapeJSONString(std::u16string_view str, bool put_in_quotes, std::string* dest); // Helper functions that wrap the above two functions but return the value // instead of appending. |put_in_quotes| is always true. -BASE_EXPORT std::string GetQuotedJSONString(StringPiece str); -BASE_EXPORT std::string GetQuotedJSONString(StringPiece16 str); +BASE_EXPORT std::string GetQuotedJSONString(std::string_view str); +BASE_EXPORT std::string GetQuotedJSONString(std::u16string_view str); // Given an arbitrary byte string |str|, this will escape all non-ASCII bytes // as \uXXXX escape sequences. This function is *NOT* meant to be used with @@ -53,7 +53,7 @@ // // The output of this function takes the *appearance* of JSON but is not in // fact valid according to RFC 8259. -BASE_EXPORT std::string EscapeBytesAsInvalidJSONString(StringPiece str, +BASE_EXPORT std::string EscapeBytesAsInvalidJSONString(std::string_view str, bool put_in_quotes); } // namespace base
diff --git a/base/json/string_escape_fuzzer.cc b/base/json/string_escape_fuzzer.cc index 9a63aab..dd67a32 100644 --- a/base/json/string_escape_fuzzer.cc +++ b/base/json/string_escape_fuzzer.cc
@@ -2,9 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "base/json/string_escape.h" - #include <memory> +#include <string_view> + +#include "base/json/string_escape.h" // Entry point for LibFuzzer. extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { @@ -19,7 +20,7 @@ std::unique_ptr<char[]> input(new char[actual_size_char8]); memcpy(input.get(), data, actual_size_char8); - base::StringPiece input_string(input.get(), actual_size_char8); + std::string_view input_string(input.get(), actual_size_char8); std::string escaped_string; base::EscapeJSONString(input_string, put_in_quotes, &escaped_string); @@ -28,7 +29,7 @@ return 0; size_t actual_size_char16 = actual_size_char8 / 2; - base::StringPiece16 input_string16(reinterpret_cast<char16_t*>(input.get()), + std::u16string_view input_string16(reinterpret_cast<char16_t*>(input.get()), actual_size_char16); escaped_string.clear(); base::EscapeJSONString(input_string16, put_in_quotes, &escaped_string);
diff --git a/base/json/values_util_unittest.cc b/base/json/values_util_unittest.cc index ef7cb98..c5a6894 100644 --- a/base/json/values_util_unittest.cc +++ b/base/json/values_util_unittest.cc
@@ -5,9 +5,9 @@ #include "base/json/values_util.h" #include <limits> +#include <string_view> #include "base/files/file_path.h" -#include "base/strings/string_piece.h" #include "base/time/time.h" #include "base/unguessable_token.h" #include "testing/gtest/include/gtest/gtest.h" @@ -19,7 +19,7 @@ TEST(ValuesUtilTest, BasicInt64Limits) { constexpr struct { int64_t input; - StringPiece expected; + std::string_view expected; } kTestCases[] = { {0, "0"}, {-1234, "-1234"}, @@ -71,7 +71,7 @@ TEST(ValuesUtilTest, FilePath) { // Ω is U+03A9 GREEK CAPITAL LETTER OMEGA, a non-ASCII character. - constexpr StringPiece kTestCases[] = { + constexpr std::string_view kTestCases[] = { "/unix/Ω/path.dat", "C:\\windows\\Ω\\path.dat", }; @@ -89,7 +89,7 @@ constexpr struct { uint64_t high; uint64_t low; - StringPiece expected; + std::string_view expected; } kTestCases[] = { {0x123456u, 0x9ABCu, "5634120000000000BC9A000000000000"}, };
diff --git a/base/metrics/OWNERS b/base/metrics/OWNERS index b9a5227..a334389 100644 --- a/base/metrics/OWNERS +++ b/base/metrics/OWNERS
@@ -13,4 +13,3 @@ mpearson@chromium.org rkaplow@chromium.org rogerm@chromium.org -sweilun@chromium.org
diff --git a/base/test/BUILD.gn b/base/test/BUILD.gn index 9cd7f3ea..cf93dc6 100644 --- a/base/test/BUILD.gn +++ b/base/test/BUILD.gn
@@ -278,6 +278,7 @@ deps += [ ":google_test_runner_shared_headers", "//build:blink_buildflags", + "//build:ios_buildflags", ] # With blink, we use the standard unit_test_launcher.cc.
diff --git a/base/test/test_support_ios.mm b/base/test/test_support_ios.mm index 97be8460..e28fa9af 100644 --- a/base/test/test_support_ios.mm +++ b/base/test/test_support_ios.mm
@@ -15,6 +15,7 @@ #include "base/test/test_suite.h" #include "base/test/test_switches.h" #include "build/blink_buildflags.h" +#include "build/ios_buildflags.h" #include "testing/coverage_util_ios.h" // Springboard will kill any iOS app that fails to check in after launch within @@ -245,10 +246,12 @@ #endif _window = nil; +#if !BUILDFLAG(IS_IOS_APP_EXTENSION) // Use the hidden selector to try and cleanly take down the app (otherwise // things can think the app crashed even on a zero exit status). UIApplication* application = [UIApplication sharedApplication]; [application _terminateWithStatus:exitStatus]; +#endif exit(exitStatus); }
diff --git a/base/win/hstring_reference.cc b/base/win/hstring_reference.cc index b254fc64..f98f1a2 100644 --- a/base/win/hstring_reference.cc +++ b/base/win/hstring_reference.cc
@@ -14,16 +14,13 @@ namespace base::win { -HStringReference::HStringReference(const wchar_t* str, size_t length) { +HStringReference::HStringReference(const wchar_t* str) { // String must be null terminated for WindowsCreateStringReference. // nullptr str is OK so long as the length is 0. - DCHECK(str ? str[length] == L'\0' : length == 0); + size_t length = str ? wcslen(str) : 0; const HRESULT hr = ::WindowsCreateStringReference( str, checked_cast<UINT32>(length), &hstring_header_, &hstring_); DCHECK_EQ(hr, S_OK); } -HStringReference::HStringReference(const wchar_t* str) - : HStringReference(str, str ? wcslen(str) : 0) {} - } // namespace base::win
diff --git a/base/win/hstring_reference.h b/base/win/hstring_reference.h index 3cde4ac..41a753da 100644 --- a/base/win/hstring_reference.h +++ b/base/win/hstring_reference.h
@@ -23,7 +23,7 @@ // class BASE_EXPORT HStringReference { public: - HStringReference(const wchar_t* str, size_t len); + // Creates an HStringReference from `str`, which must be null terminated. explicit HStringReference(const wchar_t* str); HSTRING Get() const { return hstring_; }
diff --git a/base/win/hstring_reference_unittest.cc b/base/win/hstring_reference_unittest.cc index 3f67ff2..0a32399 100644 --- a/base/win/hstring_reference_unittest.cc +++ b/base/win/hstring_reference_unittest.cc
@@ -35,8 +35,8 @@ EXPECT_EQ(empty_string.Get(), nullptr); VerifyHSTRINGEquals(empty_string.Get(), kEmptyString); - // Passing a zero length and null string should also return a null HSTRING. - const HStringReference null_string(nullptr, 0); + // Passing a null string should also return a null HSTRING. + const HStringReference null_string(nullptr); EXPECT_EQ(null_string.Get(), nullptr); VerifyHSTRINGEquals(null_string.Get(), kEmptyString); }
diff --git a/build/config/BUILD.gn b/build/config/BUILD.gn index 9410bc6..f3416871 100644 --- a/build/config/BUILD.gn +++ b/build/config/BUILD.gn
@@ -223,12 +223,6 @@ } } -_toolchain_marker_name = - "toolchain_marker_" + get_label_info(current_toolchain, "name") -group(_toolchain_marker_name) { - # Can be used as an assert_no_deps target (assert_no_deps ignores toolchains). -} - group("common_deps") { visibility = [ ":executable_deps", @@ -238,7 +232,7 @@ # WARNING: This group is a dependency of **every executable and shared # library**. Please be careful adding new dependencies here. - public_deps = [ ":$_toolchain_marker_name" ] + public_deps = [] if (using_sanitizer) { public_deps += [ "//build/config/sanitizers:deps" ]
diff --git a/build/config/android/internal_rules.gni b/build/config/android/internal_rules.gni index 8853527..5f27a3f 100644 --- a/build/config/android/internal_rules.gni +++ b/build/config/android/internal_rules.gni
@@ -3036,7 +3036,6 @@ if (target_name == "chrome_java__header") { # Regression test for: https://crbug.com/1154302 - # Ensures that header jars never depend on non-header jars. assert_no_deps = [ "//base:base_java__compile_java" ] } @@ -3753,9 +3752,6 @@ if (defined(invoker.public_deps)) { possible_config_public_deps = invoker.public_deps } - if (defined(invoker.asset_deps)) { - possible_config_deps += invoker.asset_deps - } if (defined(apk_under_test)) { possible_config_deps += [ apk_under_test ] }
diff --git a/build/config/android/rules.gni b/build/config/android/rules.gni index d6a6b1b..018c1b2a 100644 --- a/build/config/android/rules.gni +++ b/build/config/android/rules.gni
@@ -2659,7 +2659,6 @@ "android_manifest_dep", "annotation_processor_deps", "apk_under_test", - "asset_deps", "base_module_target", "chromium_code", "deps", @@ -2698,9 +2697,6 @@ if (defined(_final_dex_path)) { final_dex_path = _final_dex_path } - if (defined(invoker.assert_no_native_deps)) { - assert_no_deps = invoker.assert_no_native_deps - } if (_is_bundle_module) { proto_resources_path = _proto_resources_path @@ -2884,9 +2880,6 @@ ":$_compile_resources_target", ":$_merge_manifest_target", ] + _all_native_libs_deps - if (defined(invoker.asset_deps)) { - _final_deps += invoker.asset_deps - } if (_optimize_resources) { _final_deps += [ ":$_optimize_resources_target" ] } @@ -2947,9 +2940,6 @@ # Need full deps rather than _non_java_deps, because loadable_modules # may include .so files extracted by __unpack_aar targets. deps = _invoker_deps + [ ":$_build_config_target" ] - if (defined(invoker.asset_deps)) { - deps += invoker.asset_deps - } if (_incremental_apk) { _dex_target = "//build/android/incremental_install:apk_dex" @@ -3227,8 +3217,6 @@ "app_as_shared_lib", "art_profile_path", "assert_no_deps", - "assert_no_native_deps", - "asset_deps", "baseline_profile_path", "build_config_include_product_version_resource", "bundles_supported", @@ -3379,8 +3367,6 @@ "annotation_processor_deps", "app_as_shared_lib", "assert_no_deps", - "assert_no_native_deps", - "asset_deps", "base_module_target", "build_config_include_product_version_resource", "bundle_target",
diff --git a/build/ios/extension_bundle_data.gni b/build/ios/extension_bundle_data.gni index 3e824e8..b47f0320 100644 --- a/build/ios/extension_bundle_data.gni +++ b/build/ios/extension_bundle_data.gni
@@ -23,6 +23,7 @@ _extension_name = invoker.extension_name } + forward_variables_from(invoker, [ "testonly" ]) bundle_data(target_name) { public_deps = [ invoker.extension_target ] outputs = [ "{{bundle_contents_dir}}/PlugIns/{{source_file_part}}" ]
diff --git a/cc/paint/record_paint_canvas.cc b/cc/paint/record_paint_canvas.cc index 4ff7deb..93ff950 100644 --- a/cc/paint/record_paint_canvas.cc +++ b/cc/paint/record_paint_canvas.cc
@@ -367,6 +367,13 @@ const gfx::Size& size) : canvas_(size.width(), size.height()) {} +InspectableRecordPaintCanvas::InspectableRecordPaintCanvas( + CreateChildCanvasTag, + const InspectableRecordPaintCanvas& parent) + : canvas_(SkIRect::MakeSize(parent.imageInfo().dimensions())) { + canvas_.setMatrix(parent.canvas_.getLocalToDevice()); +} + InspectableRecordPaintCanvas::~InspectableRecordPaintCanvas() = default; int InspectableRecordPaintCanvas::save() {
diff --git a/cc/paint/record_paint_canvas.h b/cc/paint/record_paint_canvas.h index 16116e4..008290d 100644 --- a/cc/paint/record_paint_canvas.h +++ b/cc/paint/record_paint_canvas.h
@@ -237,6 +237,14 @@ // Don't shadow non-virtual helper functions. using RecordPaintCanvas::clipRect; + protected: + // Creates a child canvas that has the same transform matrix and size as + // `parent`. `CreateChildCanvasTag` is used to differentiate this from a copy + // constructor. + struct CreateChildCanvasTag {}; + InspectableRecordPaintCanvas(CreateChildCanvasTag, + const InspectableRecordPaintCanvas& parent); + private: void clipRRectInternal(const SkRRect& rrect, SkClipOp op,
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn index c73ee3d..09f1c67 100644 --- a/chrome/android/BUILD.gn +++ b/chrome/android/BUILD.gn
@@ -87,7 +87,6 @@ chrome_public_apk_or_module_tmpl(_base_module_target_name) { forward_variables_from(invoker, [ - "assert_no_deps", "add_view_trace_events", "expected_android_manifest", "is_64_bit_browser", @@ -2537,12 +2536,6 @@ target_type = "android_apk" apk_name = "ChromePublic" art_profile_path = "//chrome/android/baseline_profiles/profile.txt" - if (android_64bit_target_cpu) { - # Ensure 64-bit chrome does not depend on 32-bit things. - assert_no_deps = - [ "//build/config:toolchain_marker_" + - get_label_info(android_secondary_abi_toolchain, "name") ] - } } chrome_public_bundle("chrome_public_bundle") { @@ -3870,11 +3863,6 @@ } module_descs = chrome_module_descs - - # Java and native targets form two independent compile graphs. Deps from java targets - # onto native ones (or vice versa) are unnecessary and reduce parallelism. - # This prevents deps from native -> java. - assert_no_deps = [ "//build/android:build_java" ] } chrome_common_shared_library("libchromefortest") {
diff --git a/chrome/android/chrome_public_apk_tmpl.gni b/chrome/android/chrome_public_apk_tmpl.gni index fd6e0d5..dcaf483 100644 --- a/chrome/android/chrome_public_apk_tmpl.gni +++ b/chrome/android/chrome_public_apk_tmpl.gni
@@ -8,7 +8,6 @@ import("//build/config/android/rules.gni") import("//build/config/compiler/compiler.gni") import("//build/config/locales.gni") -import("//build/toolchain/gcc_toolchain.gni") import("//chrome/android/features/dev_ui/dev_ui_module.gni") import("//chrome/android/modules/chrome_bundle_tmpl.gni") import("//chrome/android/trichrome.gni") @@ -381,24 +380,10 @@ } } - # shared_resources_allowlist_target causes a native dep to appear via resources. - if (!_is_monochrome) { - # Java and native targets form two independent compile graphs. Deps from java targets - # onto native ones (or vice versa) are unnecessary and reduce parallelism. - # This prevents most deps from java->native. - # One common violation is generate_jni() targets, which generate - # .srcjars, but also .h files, and so export their native deps. - # Tip: If the dep is due to loadable_modules or android_assets, use "asset_deps" rather than - # "deps". - assert_no_native_deps = [ - "//base", - "//build/config/compiler:compiler_buildflags", - "//build/rust:cxx_cppdeps", - "//third_party/abseil-cpp:absl", - ] - } - - deps = [ "//chrome/android:chrome_base_module_resources" ] + deps = [ + "//chrome/android:chrome_base_module_resources", + "//chrome/android:chrome_public_non_pak_assets", + ] # TODO(agrieve): Make unconditional when moving to trampoline. if (_is_monochrome || _is_trichrome) { @@ -461,23 +446,17 @@ } } - asset_deps = [ "//chrome/android:chrome_public_non_pak_assets" ] - if (defined(invoker.asset_deps)) { - asset_deps += invoker.asset_deps - } - if (_is_bundle && _is_monochrome) { - asset_deps += [ "//chrome/android:monochrome_bundle_module_pak_assets" ] + deps += [ "//chrome/android:monochrome_bundle_module_pak_assets" ] } else if (_is_bundle && _is_trichrome) { - asset_deps += - [ "//chrome/android:trichrome_chrome_bundle_module_pak_assets" ] + deps += [ "//chrome/android:trichrome_chrome_bundle_module_pak_assets" ] } else if (_is_bundle) { - asset_deps += [ "//chrome/android:chrome_bundle_module_pak_assets" ] + deps += [ "//chrome/android:chrome_bundle_module_pak_assets" ] } else if (_is_monochrome) { - asset_deps += [ "//chrome/android:monochrome_apk_pak_assets" ] + deps += [ "//chrome/android:monochrome_apk_pak_assets" ] } else { assert(!_is_trichrome) - asset_deps += [ "//chrome/android:chrome_apk_pak_assets" ] + deps += [ "//chrome/android:chrome_apk_pak_assets" ] } if (defined(invoker.add_upstream_only_deps) && @@ -487,9 +466,9 @@ } else if (!_is_trichrome) { deps += [ "//chrome/android:chrome_public_apk_base_module_resources", + "//chrome/android:chrome_public_non_pak_assets", "//components/browser_ui/styles/android:chrome_public_apk_resources", ] - asset_deps += [ "//chrome/android:chrome_public_non_pak_assets" ] } if (_is_bundle) { deps += [ @@ -536,12 +515,11 @@ (target_cpu == "arm" || (target_cpu == "arm64" && !_is_64_bit_browser))) { if (_is_test) { - asset_deps += - [ "//chrome/android:libchromefortest_unwind_table_assets" ] + deps += [ "//chrome/android:libchromefortest_unwind_table_assets" ] } else if (_is_monochrome || _is_trichrome) { - asset_deps += [ "//chrome/android:libmonochrome_unwind_table_assets" ] + deps += [ "//chrome/android:libmonochrome_unwind_table_assets" ] } else { - asset_deps += [ "//chrome/android:libchrome_unwind_table_assets" ] + deps += [ "//chrome/android:libchrome_unwind_table_assets" ] } }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupTitleEditor.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupTitleEditor.java index 36b7fbf..ac0187c 100644 --- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupTitleEditor.java +++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupTitleEditor.java
@@ -6,14 +6,7 @@ import android.content.Context; -import org.chromium.base.ValueChangedCallback; -import org.chromium.base.supplier.ObservableSupplier; -import org.chromium.chrome.browser.flags.ChromeFeatureList; import org.chromium.chrome.browser.tab.Tab; -import org.chromium.chrome.browser.tabmodel.TabModelFilter; -import org.chromium.chrome.browser.tabmodel.TabModelObserver; -import org.chromium.chrome.browser.tasks.tab_groups.TabGroupModelFilter; -import org.chromium.chrome.browser.tasks.tab_groups.TabGroupModelFilterObserver; import org.chromium.chrome.tab_ui.R; import org.chromium.ui.modelutil.PropertyModel; @@ -24,72 +17,9 @@ */ public abstract class TabGroupTitleEditor { private final Context mContext; - private final ObservableSupplier<TabModelFilter> mCurrentTabModelFilterSupplier; - private final TabModelObserver mTabModelObserver; - private final TabGroupModelFilterObserver mFilterObserver; - private final ValueChangedCallback<TabModelFilter> mCurrentTabModelFilterObserver = - new ValueChangedCallback<>(this::onTabModelFilterChanged); - public TabGroupTitleEditor( - Context context, ObservableSupplier<TabModelFilter> tabModelFilterSupplier) { + public TabGroupTitleEditor(Context context) { mContext = context; - mCurrentTabModelFilterSupplier = tabModelFilterSupplier; - - mTabModelObserver = - new TabModelObserver() { - @Override - public void tabClosureCommitted(Tab tab) { - var filter = (TabGroupModelFilter) mCurrentTabModelFilterSupplier.get(); - int rootId = tab.getRootId(); - Tab groupTab = filter.getGroupLastShownTab(rootId); - if (groupTab == null || !filter.isTabInTabGroup(groupTab)) { - deleteTabGroupTitle(rootId); - } - } - }; - - mFilterObserver = - new TabGroupModelFilterObserver() { - @Override - public void willMergeTabToGroup(Tab movedTab, int newRootId) { - String sourceGroupTitle = getTabGroupTitle(movedTab.getRootId()); - String targetGroupTitle = getTabGroupTitle(newRootId); - if (sourceGroupTitle == null) return; - // If the target group has no title but the source group has a title, - // handover the stored title to the group after merge. - if (targetGroupTitle == null) { - storeTabGroupTitle(newRootId, sourceGroupTitle); - } - } - - @Override - public void willMoveTabOutOfGroup(Tab movedTab, int newRootId) { - int rootId = movedTab.getRootId(); - String title = getTabGroupTitle(rootId); - if (title == null) return; - // If the group size is 2, i.e. the group becomes a single tab after - // ungroup, delete the stored title. When tab groups of size 1 are supported - // this behavior is no longer valid. - var filter = (TabGroupModelFilter) mCurrentTabModelFilterSupplier.get(); - int sizeThreshold = - ChromeFeatureList.sAndroidTabGroupStableIds.isEnabled() ? 1 : 2; - boolean shouldDeleteTitle = - filter.getRelatedTabCountForRootId(rootId) <= sizeThreshold; - if (shouldDeleteTitle) { - deleteTabGroupTitle(rootId); - return; - } - // If the root tab in group is moved out, re-assign the title to the new - // root tab in group. - if (rootId != newRootId) { - deleteTabGroupTitle(rootId); - storeTabGroupTitle(newRootId, title); - } - } - }; - - mCurrentTabModelFilterObserver.onResult( - mCurrentTabModelFilterSupplier.addObserver(mCurrentTabModelFilterObserver)); } /** @@ -146,28 +76,4 @@ * @return The stored title of the related group. */ protected abstract String getTabGroupTitle(int tabRootId); - - /** Destroy any members that needs clean up. */ - public void destroy() { - removeTabModelFilterObservers(mCurrentTabModelFilterSupplier.get()); - mCurrentTabModelFilterSupplier.removeObserver(mCurrentTabModelFilterObserver); - } - - private void onTabModelFilterChanged(TabModelFilter newFilter, TabModelFilter oldFilter) { - removeTabModelFilterObservers(oldFilter); - - if (newFilter != null) { - TabGroupModelFilter newGroupFilter = (TabGroupModelFilter) newFilter; - newGroupFilter.addObserver(mTabModelObserver); - newGroupFilter.addTabGroupObserver(mFilterObserver); - } - } - - private void removeTabModelFilterObservers(TabModelFilter filter) { - if (filter != null) { - TabGroupModelFilter groupFilter = (TabGroupModelFilter) filter; - groupFilter.removeObserver(mTabModelObserver); - groupFilter.removeTabGroupObserver(mFilterObserver); - } - } }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupVisualDataManager.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupVisualDataManager.java new file mode 100644 index 0000000..731fd25 --- /dev/null +++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupVisualDataManager.java
@@ -0,0 +1,165 @@ +// Copyright 2024 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chrome.browser.tasks.tab_management; + +import androidx.annotation.NonNull; + +import org.chromium.chrome.browser.flags.ChromeFeatureList; +import org.chromium.chrome.browser.tab.Tab; +import org.chromium.chrome.browser.tabmodel.TabModelFilterProvider; +import org.chromium.chrome.browser.tabmodel.TabModelObserver; +import org.chromium.chrome.browser.tabmodel.TabModelSelector; +import org.chromium.chrome.browser.tasks.tab_groups.TabGroupColorUtils; +import org.chromium.chrome.browser.tasks.tab_groups.TabGroupModelFilter; +import org.chromium.chrome.browser.tasks.tab_groups.TabGroupModelFilterObserver; +import org.chromium.chrome.browser.tasks.tab_groups.TabGroupTitleUtils; +import org.chromium.components.tab_groups.TabGroupColorId; + +/** + * Manages observers that monitor for updates to tab group visual aspects such as colors and titles. + */ +public class TabGroupVisualDataManager { + private static final int INVALID_COLOR_ID = -1; + + private final TabModelSelector mTabModelSelector; + private TabModelObserver mTabModelObserver; + private TabGroupModelFilterObserver mFilterObserver; + + public TabGroupVisualDataManager(@NonNull TabModelSelector tabModelSelector) { + assert tabModelSelector.isTabStateInitialized(); + mTabModelSelector = tabModelSelector; + + TabModelFilterProvider tabModelFilterProvider = + mTabModelSelector.getTabModelFilterProvider(); + + mTabModelObserver = + new TabModelObserver() { + @Override + public void tabClosureCommitted(Tab tab) { + TabGroupModelFilter filter = + (TabGroupModelFilter) + tabModelFilterProvider.getTabModelFilter(tab.isIncognito()); + int rootId = tab.getRootId(); + Tab groupTab = filter.getGroupLastShownTab(rootId); + if (groupTab == null || !filter.isTabInTabGroup(groupTab)) { + TabGroupTitleUtils.deleteTabGroupTitle(rootId); + + if (ChromeFeatureList.sTabGroupParityAndroid.isEnabled()) { + TabGroupColorUtils.deleteTabGroupColor(rootId); + } + } + } + }; + + mFilterObserver = + new TabGroupModelFilterObserver() { + @Override + public void didCreateNewGroup(int newRootId, TabGroupModelFilter filter) { + // TODO(b/41490324): Store a default color as none will exist, but this + // should be enforced later on with the intro of TabGroupCreationDialog. + if (ChromeFeatureList.sTabGroupParityAndroid.isEnabled()) { + final @TabGroupColorId int colorId = + TabGroupColorUtils.getNextSuggestedColorId(filter); + TabGroupColorUtils.storeTabGroupColor(newRootId, colorId); + } + } + + @Override + public void willMergeTabToGroup(Tab movedTab, int newRootId) { + String sourceGroupTitle = + TabGroupTitleUtils.getTabGroupTitle(movedTab.getRootId()); + String targetGroupTitle = TabGroupTitleUtils.getTabGroupTitle(newRootId); + // If the target group has no title but the source group has a title, + // handover the stored title to the group after merge. + if (sourceGroupTitle != null && targetGroupTitle == null) { + TabGroupTitleUtils.storeTabGroupTitle(newRootId, sourceGroupTitle); + } + + if (ChromeFeatureList.sTabGroupParityAndroid.isEnabled()) { + int sourceGroupColor = + TabGroupColorUtils.getTabGroupColor(movedTab.getRootId()); + int targetGroupColor = TabGroupColorUtils.getTabGroupColor(newRootId); + // If the target group has no color but the source group has a color, + // handover the stored color to the group after merge. + if (sourceGroupColor != INVALID_COLOR_ID + && targetGroupColor == INVALID_COLOR_ID) { + TabGroupColorUtils.storeTabGroupColor(newRootId, sourceGroupColor); + } + } + } + + @Override + public void willMoveTabOutOfGroup(Tab movedTab, int newRootId) { + int rootId = movedTab.getRootId(); + String title = TabGroupTitleUtils.getTabGroupTitle(rootId); + + // If the group size is 2, i.e. the group becomes a single tab after + // ungroup, delete the stored visual data. When tab groups of size 1 are + // supported this behavior is no longer valid. + TabGroupModelFilter filter = + (TabGroupModelFilter) + tabModelFilterProvider.getTabModelFilter( + movedTab.isIncognito()); + int sizeThreshold = + ChromeFeatureList.sAndroidTabGroupStableIds.isEnabled() ? 1 : 2; + boolean shouldDeleteVisualData = + filter.getRelatedTabCountForRootId(rootId) <= sizeThreshold; + if (shouldDeleteVisualData) { + if (title != null) { + TabGroupTitleUtils.deleteTabGroupTitle(rootId); + } + + if (ChromeFeatureList.sTabGroupParityAndroid.isEnabled()) { + TabGroupColorUtils.deleteTabGroupColor(rootId); + } + + return; + } + // If the root tab in group is moved out, re-assign the visual data to the + // new root tab in group. + if (rootId != newRootId) { + if (title != null) { + TabGroupTitleUtils.deleteTabGroupTitle(rootId); + TabGroupTitleUtils.storeTabGroupTitle(newRootId, title); + } + + if (ChromeFeatureList.sTabGroupParityAndroid.isEnabled()) { + int colorId = TabGroupColorUtils.getTabGroupColor(rootId); + assert colorId != INVALID_COLOR_ID; + + TabGroupColorUtils.deleteTabGroupColor(rootId); + TabGroupColorUtils.storeTabGroupColor(newRootId, colorId); + } + } + } + }; + + tabModelFilterProvider.addTabModelFilterObserver(mTabModelObserver); + + ((TabGroupModelFilter) tabModelFilterProvider.getTabModelFilter(false)) + .addTabGroupObserver(mFilterObserver); + ((TabGroupModelFilter) tabModelFilterProvider.getTabModelFilter(true)) + .addTabGroupObserver(mFilterObserver); + } + + /** Destroy any members that need clean up. */ + public void destroy() { + TabModelFilterProvider tabModelFilterProvider = + mTabModelSelector.getTabModelFilterProvider(); + + if (mTabModelObserver != null) { + tabModelFilterProvider.removeTabModelFilterObserver(mTabModelObserver); + mTabModelObserver = null; + } + + if (mFilterObserver != null) { + ((TabGroupModelFilter) tabModelFilterProvider.getTabModelFilter(false)) + .removeTabGroupObserver(mFilterObserver); + ((TabGroupModelFilter) tabModelFilterProvider.getTabModelFilter(true)) + .removeTabGroupObserver(mFilterObserver); + mFilterObserver = null; + } + } +}
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediator.java index b925b2e0..a7ad1a3 100644 --- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediator.java +++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediator.java
@@ -1137,7 +1137,7 @@ mCurrentTabModelFilterSupplier.addObserver(mOnTabModelFilterChanged)); mTabGroupTitleEditor = - new TabGroupTitleEditor(mContext, mCurrentTabModelFilterSupplier) { + new TabGroupTitleEditor(mContext) { @Override protected void updateTabGroupTitle(Tab tab, String title) { // Only update title in PropertyModel for tab switcher. @@ -1689,9 +1689,6 @@ if (mComponentCallbacks != null) { mContext.unregisterComponentCallbacks(mComponentCallbacks); } - if (mTabGroupTitleEditor != null) { - mTabGroupTitleEditor.destroy(); - } unregisterOnScrolledListener(); }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/UndoGroupSnackbarController.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/UndoGroupSnackbarController.java index 6d5600b1..04150a53 100644 --- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/UndoGroupSnackbarController.java +++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/UndoGroupSnackbarController.java
@@ -11,6 +11,7 @@ import org.chromium.base.Token; import org.chromium.chrome.R; +import org.chromium.chrome.browser.flags.ChromeFeatureList; import org.chromium.chrome.browser.tab.Tab; import org.chromium.chrome.browser.tab.TabCreationState; import org.chromium.chrome.browser.tab.TabLaunchType; @@ -18,6 +19,7 @@ import org.chromium.chrome.browser.tabmodel.TabModelSelector; import org.chromium.chrome.browser.tabmodel.TabModelSelectorObserver; import org.chromium.chrome.browser.tabmodel.TabModelSelectorTabModelObserver; +import org.chromium.chrome.browser.tasks.tab_groups.TabGroupColorUtils; import org.chromium.chrome.browser.tasks.tab_groups.TabGroupModelFilter; import org.chromium.chrome.browser.tasks.tab_groups.TabGroupModelFilterObserver; import org.chromium.chrome.browser.tasks.tab_groups.TabGroupTitleUtils; @@ -33,6 +35,8 @@ * and shows a undo snackbar. */ public class UndoGroupSnackbarController implements SnackbarManager.SnackbarController { + private static final int INVALID_COLOR_ID = -1; + private final Context mContext; private final TabModelSelector mTabModelSelector; private final SnackbarManager mSnackbarManager; @@ -46,18 +50,21 @@ public final int tabOriginalRootId; public final @Nullable Token tabOriginalTabGroupId; public final String destinationGroupTitle; + public final int destinationGroupColorId; TabUndoInfo( Tab tab, int tabIndex, int rootId, @Nullable Token tabGroupId, - String destinationGroupTitle) { + String destinationGroupTitle, + int destinationGroupColorId) { this.tab = tab; this.tabOriginalIndex = tabIndex; this.tabOriginalRootId = rootId; this.tabOriginalTabGroupId = tabGroupId; this.destinationGroupTitle = destinationGroupTitle; + this.destinationGroupColorId = destinationGroupColorId; } } @@ -81,7 +88,8 @@ List<Integer> tabOriginalIndex, List<Integer> originalRootId, List<Token> originalTabGroupId, - String destinationGroupTitle) { + String destinationGroupTitle, + int destinationGroupColorId) { assert tabs.size() == tabOriginalIndex.size(); List<TabUndoInfo> tabUndoInfo = new ArrayList<>(); @@ -93,7 +101,12 @@ tabUndoInfo.add( new TabUndoInfo( - tab, index, rootId, tabGroupId, destinationGroupTitle)); + tab, + index, + rootId, + tabGroupId, + destinationGroupTitle, + destinationGroupColorId)); } showUndoGroupSnackbar(tabUndoInfo); } @@ -183,11 +196,16 @@ @Override public void onDismissNoAction(Object actionData) { - // Delete the original tab group titles of the merging tabs once the merge is committed. + // Delete the original tab group titles and colors of the merging tabs once the merge is + // committed. for (TabUndoInfo info : (List<TabUndoInfo>) actionData) { if (info.tab.getRootId() == info.tabOriginalRootId) continue; TabGroupTitleUtils.deleteTabGroupTitle(info.tabOriginalRootId); + + if (ChromeFeatureList.sTabGroupParityAndroid.isEnabled()) { + TabGroupColorUtils.deleteTabGroupColor(info.tabOriginalRootId); + } } } @@ -207,6 +225,17 @@ TabGroupTitleUtils.deleteTabGroupTitle(data.get(0).tab.getRootId()); } + if (ChromeFeatureList.sTabGroupParityAndroid.isEnabled()) { + // If the destination rootID previously did not have a color id associated with it since + // it was either created from a new tab group or was originally a single tab before + // merge, delete that color id on undo. This check deletes the group color for that + // destination rootID, as all tabs still currently share that ID before the undo + // operation is performed. + if (data.get(0).destinationGroupColorId == INVALID_COLOR_ID) { + TabGroupColorUtils.deleteTabGroupColor(data.get(0).tab.getRootId()); + } + } + for (int i = data.size() - 1; i >= 0; i--) { TabUndoInfo info = data.get(i); tabGroupModelFilter.undoGroupedTab(
diff --git a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherAndStartSurfaceLayoutTest.java b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherAndStartSurfaceLayoutTest.java index 5bea146..472e081 100644 --- a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherAndStartSurfaceLayoutTest.java +++ b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherAndStartSurfaceLayoutTest.java
@@ -110,6 +110,7 @@ import org.chromium.chrome.browser.tab.TabUtils; import org.chromium.chrome.browser.tabmodel.TabCreator; import org.chromium.chrome.browser.tabmodel.TabModel; +import org.chromium.chrome.browser.tasks.tab_groups.TabGroupColorUtils; import org.chromium.chrome.browser.tasks.tab_groups.TabGroupModelFilter; import org.chromium.chrome.browser.tasks.tab_groups.TabGroupTitleUtils; import org.chromium.chrome.browser.ui.messages.snackbar.SnackbarManager; @@ -168,6 +169,8 @@ private static final String TEST_URL = "/chrome/test/data/android/google.html"; + private static final int INVALID_COLOR_ID = -1; + // Tests need animation on. @ClassRule public static DisableAnimationsTestRule sEnableAnimationsRule = @@ -1864,6 +1867,7 @@ @Test @MediumTest @UseMethodParameter(RefactorTestParams.class) + @EnableFeatures({ChromeFeatureList.TAB_GROUP_PARITY_ANDROID}) public void testUndoGroupMergeInTabSwitcher_TabToTab(boolean isStartSurfaceRefactorEnabled) { final ChromeTabbedActivity cta = mActivityTestRule.getActivity(); SnackbarManager snackbarManager = mActivityTestRule.getActivity().getSnackbarManager(); @@ -1871,22 +1875,40 @@ enterTabSwitcher(cta); verifyTabSwitcherCardCount(cta, 2); + // Get the next suggested color id. + int nextSuggestedColorId = + TabGroupColorUtils.getNextSuggestedColorId( + (TabGroupModelFilter) + cta.getTabModelSelectorSupplier() + .get() + .getTabModelFilterProvider() + .getCurrentTabModelFilter()); + // Create a tab group. mergeAllNormalTabsToAGroup(cta); assertTrue( snackbarManager.getCurrentSnackbarForTesting().getController() instanceof UndoGroupSnackbarController); + // Assert that the suggested default color was set. + TabModel normalTabModel = cta.getTabModelSelectorSupplier().get().getModel(false); + int tabGroupRootId = normalTabModel.getTabAt(0).getRootId(); + assertEquals(nextSuggestedColorId, TabGroupColorUtils.getTabGroupColor(tabGroupRootId)); + // Undo merge in tab switcher. verifyTabSwitcherCardCount(cta, 1); assertEquals("2", snackbarManager.getCurrentSnackbarForTesting().getTextForTesting()); CriteriaHelper.pollInstrumentationThread(TabUiTestHelper::verifyUndoBarShowingAndClickUndo); verifyTabSwitcherCardCount(cta, 2); + + // Assert the color is no longer set for that group. + assertEquals(INVALID_COLOR_ID, TabGroupColorUtils.getTabGroupColor(tabGroupRootId)); } @Test @MediumTest @UseMethodParameter(RefactorTestParams.class) + @EnableFeatures({ChromeFeatureList.TAB_GROUP_PARITY_ANDROID}) public void testUndoGroupMergeInTabSwitcher_TabToGroupAdjacent( boolean isStartSurfaceRefactorEnabled) { final ChromeTabbedActivity cta = mActivityTestRule.getActivity(); @@ -1895,6 +1917,15 @@ enterTabSwitcher(cta); verifyTabSwitcherCardCount(cta, 3); + // Get the next suggested color id. + int nextSuggestedColorId = + TabGroupColorUtils.getNextSuggestedColorId( + (TabGroupModelFilter) + cta.getTabModelSelectorSupplier() + .get() + .getTabModelFilterProvider() + .getCurrentTabModelFilter()); + // Merge first two tabs into a group. TabModel normalTabModel = cta.getTabModelSelector().getModel(false); List<Tab> tabGroup = @@ -1912,12 +1943,22 @@ instanceof UndoGroupSnackbarController); assertEquals("2", snackbarManager.getCurrentSnackbarForTesting().getTextForTesting()); + // Assert default color was set properly. + assertEquals( + nextSuggestedColorId, + TabGroupColorUtils.getTabGroupColor(normalTabModel.getTabAt(0).getRootId())); + // Merge tab group of 2 at first index with the 3rd tab. mergeAllNormalTabsToAGroup(cta); assertTrue( snackbarManager.getCurrentSnackbarForTesting().getController() instanceof UndoGroupSnackbarController); + // Assert the default color is still the tab group color + assertEquals( + nextSuggestedColorId, + TabGroupColorUtils.getTabGroupColor(normalTabModel.getTabAt(0).getRootId())); + // Undo merge in tab switcher. verifyTabSwitcherCardCount(cta, 1); assertEquals("3", snackbarManager.getCurrentSnackbarForTesting().getTextForTesting()); @@ -1932,12 +1973,21 @@ assertNull( TabGroupTitleUtils.getTabGroupTitle( normalTabModel.getTabAt(2).getRootId())); + assertEquals( + nextSuggestedColorId, + TabGroupColorUtils.getTabGroupColor( + normalTabModel.getTabAt(1).getRootId())); + assertEquals( + INVALID_COLOR_ID, + TabGroupColorUtils.getTabGroupColor( + normalTabModel.getTabAt(2).getRootId())); }); } @Test @MediumTest @UseMethodParameter(RefactorTestParams.class) + @EnableFeatures({ChromeFeatureList.TAB_GROUP_PARITY_ANDROID}) public void testUndoGroupMergeInTabSwitcher_GroupToGroupNonAdjacent( boolean isStartSurfaceRefactorEnabled) { final ChromeTabbedActivity cta = mActivityTestRule.getActivity(); @@ -1946,6 +1996,15 @@ enterTabSwitcher(cta); verifyTabSwitcherCardCount(cta, 5); + // Get the next suggested color id. + int nextSuggestedColorId1 = + TabGroupColorUtils.getNextSuggestedColorId( + (TabGroupModelFilter) + cta.getTabModelSelectorSupplier() + .get() + .getTabModelFilterProvider() + .getCurrentTabModelFilter()); + // Merge last two tabs into a group. TabModel normalTabModel = cta.getTabModelSelector().getModel(false); List<Tab> tabGroup = @@ -1958,6 +2017,20 @@ instanceof UndoGroupSnackbarController); assertEquals("2", snackbarManager.getCurrentSnackbarForTesting().getTextForTesting()); + // Assert default color 1 was set properly. + assertEquals( + nextSuggestedColorId1, + TabGroupColorUtils.getTabGroupColor(normalTabModel.getTabAt(4).getRootId())); + + // Get the next suggested color id. + int nextSuggestedColorId2 = + TabGroupColorUtils.getNextSuggestedColorId( + (TabGroupModelFilter) + cta.getTabModelSelectorSupplier() + .get() + .getTabModelFilterProvider() + .getCurrentTabModelFilter()); + // Merge first two tabs into a group. List<Tab> tabGroup2 = new ArrayList<>( @@ -1976,6 +2049,11 @@ instanceof UndoGroupSnackbarController); assertEquals("2", snackbarManager.getCurrentSnackbarForTesting().getTextForTesting()); + // Assert default color 2 was set properly. + assertEquals( + nextSuggestedColorId2, + TabGroupColorUtils.getTabGroupColor(normalTabModel.getTabAt(1).getRootId())); + // Merge the two tab groups into a group. List<Tab> tabGroup3 = new ArrayList<>( @@ -1985,6 +2063,11 @@ snackbarManager.getCurrentSnackbarForTesting().getController() instanceof UndoGroupSnackbarController); + // Assert default color 2 was set as the overall merged group color. + assertEquals( + nextSuggestedColorId2, + TabGroupColorUtils.getTabGroupColor(normalTabModel.getTabAt(3).getRootId())); + // Undo merge in tab switcher. verifyTabSwitcherCardCount(cta, 2); assertEquals("4", snackbarManager.getCurrentSnackbarForTesting().getTextForTesting()); @@ -2000,12 +2083,21 @@ "Bar", TabGroupTitleUtils.getTabGroupTitle( normalTabModel.getTabAt(0).getRootId())); + assertEquals( + nextSuggestedColorId1, + TabGroupColorUtils.getTabGroupColor( + normalTabModel.getTabAt(4).getRootId())); + assertEquals( + nextSuggestedColorId2, + TabGroupColorUtils.getTabGroupColor( + normalTabModel.getTabAt(0).getRootId())); }); } @Test @MediumTest @UseMethodParameter(RefactorTestParams.class) + @EnableFeatures({ChromeFeatureList.TAB_GROUP_PARITY_ANDROID}) public void testUndoGroupMergeInTabSwitcher_PostMergeGroupTitleCommit( boolean isStartSurfaceRefactorEnabled) { final ChromeTabbedActivity cta = mActivityTestRule.getActivity(); @@ -2014,6 +2106,15 @@ enterTabSwitcher(cta); verifyTabSwitcherCardCount(cta, 3); + // Get the next suggested color id. + int nextSuggestedColorId = + TabGroupColorUtils.getNextSuggestedColorId( + (TabGroupModelFilter) + cta.getTabModelSelectorSupplier() + .get() + .getTabModelFilterProvider() + .getCurrentTabModelFilter()); + // Merge first two tabs into a group. TabModel normalTabModel = cta.getTabModelSelector().getModel(false); List<Tab> tabGroup = @@ -2034,12 +2135,22 @@ instanceof UndoGroupSnackbarController); assertEquals("2", snackbarManager.getCurrentSnackbarForTesting().getTextForTesting()); + // Assert default color was set properly. + assertEquals( + nextSuggestedColorId, + TabGroupColorUtils.getTabGroupColor(normalTabModel.getTabAt(1).getRootId())); + // Merge tab group of 2 at first index with the 3rd tab. mergeAllNormalTabsToAGroup(cta); assertTrue( snackbarManager.getCurrentSnackbarForTesting().getController() instanceof UndoGroupSnackbarController); + // Assert default color was set properly for the overall merged group. + assertEquals( + nextSuggestedColorId, + TabGroupColorUtils.getTabGroupColor(normalTabModel.getTabAt(2).getRootId())); + // Check that the old group title was handed over when the group merge is committed // and no longer exists. TestThreadUtils.runOnUiThreadBlocking(() -> snackbarManager.dismissAllSnackbars()); @@ -2051,6 +2162,130 @@ TabGroupTitleUtils.getTabGroupTitle( normalTabModel.getTabAt(0).getRootId())); }); + + // Assert color still exists post snackbar dismissal. + assertEquals( + nextSuggestedColorId, + TabGroupColorUtils.getTabGroupColor(normalTabModel.getTabAt(1).getRootId())); + } + + @Test + @MediumTest + @UseMethodParameter(RefactorTestParams.class) + @EnableFeatures({ChromeFeatureList.TAB_GROUP_PARITY_ANDROID}) + public void testUndoClosure_UndoGroupClosure(boolean isStartSurfaceRefactorEnabled) { + ChromeTabbedActivity cta = mActivityTestRule.getActivity(); + SnackbarManager snackbarManager = mActivityTestRule.getActivity().getSnackbarManager(); + createTabs(cta, false, 2); + + enterTabSwitcher(cta); + verifyTabSwitcherCardCount(cta, 2); + assertNull(snackbarManager.getCurrentSnackbarForTesting()); + + // Get the next suggested color id. + int nextSuggestedColorId = + TabGroupColorUtils.getNextSuggestedColorId( + (TabGroupModelFilter) + cta.getTabModelSelectorSupplier() + .get() + .getTabModelFilterProvider() + .getCurrentTabModelFilter()); + + // Merge first two tabs into a group. + TabModel normalTabModel = cta.getTabModelSelector().getModel(false); + List<Tab> tabGroup = + new ArrayList<>( + Arrays.asList(normalTabModel.getTabAt(0), normalTabModel.getTabAt(1))); + createTabGroup(cta, false, tabGroup); + verifyTabSwitcherCardCount(cta, 1); + assertTrue( + snackbarManager.getCurrentSnackbarForTesting().getController() + instanceof UndoGroupSnackbarController); + assertEquals("2", snackbarManager.getCurrentSnackbarForTesting().getTextForTesting()); + + // Assert default color was set properly. + assertEquals( + nextSuggestedColorId, + TabGroupColorUtils.getTabGroupColor(normalTabModel.getTabAt(1).getRootId())); + TestThreadUtils.runOnUiThreadBlocking(() -> snackbarManager.dismissAllSnackbars()); + + // Temporarily save the rootID to check during closure. + int groupRootId = normalTabModel.getTabAt(1).getRootId(); + + closeFirstTabInTabSwitcher(cta); + assertTrue( + snackbarManager.getCurrentSnackbarForTesting().getController() + instanceof UndoBarController); + verifyTabSwitcherCardCount(cta, 0); + + // Assert default color still persists. + assertEquals(nextSuggestedColorId, TabGroupColorUtils.getTabGroupColor(groupRootId)); + + CriteriaHelper.pollInstrumentationThread(TabUiTestHelper::verifyUndoBarShowingAndClickUndo); + verifyTabSwitcherCardCount(cta, 1); + + // Assert default color still persists. + assertEquals( + nextSuggestedColorId, + TabGroupColorUtils.getTabGroupColor(normalTabModel.getTabAt(1).getRootId())); + } + + @Test + @MediumTest + @UseMethodParameter(RefactorTestParams.class) + @EnableFeatures({ChromeFeatureList.TAB_GROUP_PARITY_ANDROID}) + public void testUndoClosure_AcceptGroupClosure(boolean isStartSurfaceRefactorEnabled) { + ChromeTabbedActivity cta = mActivityTestRule.getActivity(); + SnackbarManager snackbarManager = mActivityTestRule.getActivity().getSnackbarManager(); + createTabs(cta, false, 2); + + enterTabSwitcher(cta); + verifyTabSwitcherCardCount(cta, 2); + assertNull(snackbarManager.getCurrentSnackbarForTesting()); + + // Get the next suggested color id. + int nextSuggestedColorId = + TabGroupColorUtils.getNextSuggestedColorId( + (TabGroupModelFilter) + cta.getTabModelSelectorSupplier() + .get() + .getTabModelFilterProvider() + .getCurrentTabModelFilter()); + + // Merge first two tabs into a group. + TabModel normalTabModel = cta.getTabModelSelector().getModel(false); + List<Tab> tabGroup = + new ArrayList<>( + Arrays.asList(normalTabModel.getTabAt(0), normalTabModel.getTabAt(1))); + createTabGroup(cta, false, tabGroup); + verifyTabSwitcherCardCount(cta, 1); + assertTrue( + snackbarManager.getCurrentSnackbarForTesting().getController() + instanceof UndoGroupSnackbarController); + assertEquals("2", snackbarManager.getCurrentSnackbarForTesting().getTextForTesting()); + + // Assert default color was set properly. + assertEquals( + nextSuggestedColorId, + TabGroupColorUtils.getTabGroupColor(normalTabModel.getTabAt(1).getRootId())); + TestThreadUtils.runOnUiThreadBlocking(() -> snackbarManager.dismissAllSnackbars()); + + // Temporarily save the rootID to check during closure. + int groupRootId = normalTabModel.getTabAt(1).getRootId(); + + closeFirstTabInTabSwitcher(cta); + assertTrue( + snackbarManager.getCurrentSnackbarForTesting().getController() + instanceof UndoBarController); + verifyTabSwitcherCardCount(cta, 0); + + // Assert default color still persists. + assertEquals(nextSuggestedColorId, TabGroupColorUtils.getTabGroupColor(groupRootId)); + + TestThreadUtils.runOnUiThreadBlocking(() -> snackbarManager.dismissAllSnackbars()); + + // Assert default color is cleared. + assertEquals(INVALID_COLOR_ID, TabGroupColorUtils.getTabGroupColor(groupRootId)); } @Test
diff --git a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupTitleEditorUnitTest.java b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupTitleEditorUnitTest.java index 77f61d2e..f77e09c5 100644 --- a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupTitleEditorUnitTest.java +++ b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupTitleEditorUnitTest.java
@@ -4,108 +4,42 @@ package org.chromium.chrome.browser.tasks.tab_management; -import static androidx.test.espresso.matcher.ViewMatchers.assertThat; -import static org.hamcrest.CoreMatchers.equalTo; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; -import androidx.annotation.Nullable; - -import org.junit.After; 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.ArgumentCaptor; -import org.mockito.Captor; -import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.robolectric.RuntimeEnvironment; -import org.chromium.base.Token; -import org.chromium.base.supplier.ObservableSupplierImpl; import org.chromium.base.test.BaseRobolectricTestRunner; import org.chromium.base.test.util.Features; -import org.chromium.base.test.util.Features.DisableFeatures; -import org.chromium.base.test.util.Features.EnableFeatures; -import org.chromium.chrome.browser.flags.ChromeFeatureList; import org.chromium.chrome.browser.tab.Tab; -import org.chromium.chrome.browser.tabmodel.TabModel; -import org.chromium.chrome.browser.tabmodel.TabModelFilter; -import org.chromium.chrome.browser.tabmodel.TabModelObserver; -import org.chromium.chrome.browser.tasks.tab_groups.TabGroupModelFilter; -import org.chromium.chrome.browser.tasks.tab_groups.TabGroupModelFilterObserver; import org.chromium.chrome.tab_ui.R; -import java.util.ArrayList; -import java.util.Arrays; import java.util.HashMap; -import java.util.List; import java.util.Map; /** Tests for {@link TabGroupTitleEditor}. */ @SuppressWarnings({"ArraysAsListWithZeroOrOneArgument", "ResultOfMethodCallIgnored"}) @RunWith(BaseRobolectricTestRunner.class) -@EnableFeatures(ChromeFeatureList.ANDROID_TAB_GROUP_STABLE_IDS) public class TabGroupTitleEditorUnitTest { @Rule public TestRule mProcessor = new Features.JUnitProcessor(); - private static final String TAB1_TITLE = "Tab1"; - private static final String TAB2_TITLE = "Tab2"; - private static final String TAB3_TITLE = "Tab3"; - private static final String TAB4_TITLE = "Tab4"; - private static final String CUSTOMIZED_TITLE1 = "Some cool tabs"; - private static final String CUSTOMIZED_TITLE2 = "Other cool tabs"; - private static final int TAB1_ID = 456; - private static final int TAB2_ID = 789; - private static final int TAB3_ID = 123; - private static final int TAB4_ID = 357; - private static final Token GROUP_1_ID = new Token(1L, 2L); - private static final Token GROUP_2_ID = new Token(2L, 3L); - - @Mock TabModel mTabModel; - @Mock TabGroupModelFilter mTabGroupModelFilter; - @Mock TabModel mIncognitoTabModel; - @Mock TabGroupModelFilter mIncognitoTabGroupModelFilter; - @Captor ArgumentCaptor<TabModelObserver> mTabModelObserverCaptor; - @Captor ArgumentCaptor<TabGroupModelFilterObserver> mTabGroupModelFilterObserverCaptor; - - private final ObservableSupplierImpl<TabModelFilter> mTabModelFilterSupplier = - new ObservableSupplierImpl<>(); - private Tab mTab1; - private Tab mTab2; - private Tab mTab3; - private Tab mTab4; private Map<String, String> mStorage; private TabGroupTitleEditor mTabGroupTitleEditor; @Before public void setUp() { - MockitoAnnotations.initMocks(this); - mTab1 = TabUiUnitTestUtils.prepareTab(TAB1_ID, TAB1_TITLE); - mTab2 = TabUiUnitTestUtils.prepareTab(TAB2_ID, TAB2_TITLE); - mTab3 = TabUiUnitTestUtils.prepareTab(TAB3_ID, TAB3_TITLE); - mTab4 = TabUiUnitTestUtils.prepareTab(TAB4_ID, TAB4_TITLE); - doReturn(mTabModel).when(mTabGroupModelFilter).getTabModel(); - doReturn(mIncognitoTabModel).when(mIncognitoTabGroupModelFilter).getTabModel(); - mTabModelFilterSupplier.set(mTabGroupModelFilter); - doNothing().when(mTabGroupModelFilter).addObserver(mTabModelObserverCaptor.capture()); - doNothing() - .when(mTabGroupModelFilter) - .addTabGroupObserver(mTabGroupModelFilterObserverCaptor.capture()); - mTabGroupTitleEditor = - new TabGroupTitleEditor(RuntimeEnvironment.application, mTabModelFilterSupplier) { + new TabGroupTitleEditor(RuntimeEnvironment.application) { @Override protected void updateTabGroupTitle(Tab tab, String title) {} @@ -125,226 +59,6 @@ } }; mStorage = new HashMap<>(); - assertTrue(mTabModelFilterSupplier.hasObservers()); - } - - @After - public void tearDown() { - mTabGroupTitleEditor.destroy(); - assertFalse(mTabModelFilterSupplier.hasObservers()); - } - - @Test - public void testChangeModels() { - verify(mTabGroupModelFilter).addObserver(any()); - verify(mTabGroupModelFilter).addTabGroupObserver(any()); - mTabModelFilterSupplier.set(mIncognitoTabGroupModelFilter); - verify(mIncognitoTabGroupModelFilter).addObserver(any()); - verify(mIncognitoTabGroupModelFilter).addTabGroupObserver(any()); - verify(mTabGroupModelFilter).removeObserver(any()); - verify(mTabGroupModelFilter).removeTabGroupObserver(any()); - } - - @Test - public void tabClosureCommitted_RootTab_NotDeleteStoredTitle() { - // Mock that we have a stored title stored with reference to root ID of tab1. - mTabGroupTitleEditor.storeTabGroupTitle(TAB1_ID, CUSTOMIZED_TITLE1); - assertThat(mStorage.size(), equalTo(1)); - - // Mock that tab1, tab2, new tab are in the same group and group root id is TAB1_ID. - Tab newTab = TabUiUnitTestUtils.prepareTab(TAB3_ID, TAB3_TITLE); - List<Tab> tabs = new ArrayList<>(Arrays.asList(mTab1, mTab2, newTab)); - createTabGroup(tabs, TAB1_ID, GROUP_1_ID); - - // Mock that the root tab of the group, tab1, is closed. - List<Tab> groupAfterClosure = new ArrayList<>(Arrays.asList(mTab2, newTab)); - when(mTabGroupModelFilter.getRelatedTabCountForRootId(TAB1_ID)) - .thenReturn(groupAfterClosure.size()); - mTabModelObserverCaptor.getValue().tabClosureCommitted(mTab1); - - assertThat(mStorage.size(), equalTo(1)); - assertThat(mTabGroupTitleEditor.getTabGroupTitle(TAB1_ID), equalTo(CUSTOMIZED_TITLE1)); - } - - @Test - public void tabClosureCommitted_NotRootTab_NotDeleteStoredTitle() { - // Mock that we have a stored title stored with reference to root ID of tab1. - mTabGroupTitleEditor.storeTabGroupTitle(TAB1_ID, CUSTOMIZED_TITLE1); - - // Mock that tab1, tab2, new tab are in the same group and group root id is TAB1_ID. - Tab newTab = TabUiUnitTestUtils.prepareTab(TAB3_ID, TAB3_TITLE); - List<Tab> groupBeforeClosure = new ArrayList<>(Arrays.asList(mTab1, mTab2, newTab)); - createTabGroup(groupBeforeClosure, TAB1_ID, GROUP_1_ID); - - // Mock that tab2 is closed and tab2 is not the root tab. - List<Tab> groupAfterClosure = new ArrayList<>(Arrays.asList(mTab1, newTab)); - when(mTabGroupModelFilter.getRelatedTabCountForRootId(TAB1_ID)) - .thenReturn(groupAfterClosure.size()); - mTabModelObserverCaptor.getValue().tabClosureCommitted(mTab2); - - assertThat(mStorage.size(), equalTo(1)); - assertThat(mTabGroupTitleEditor.getTabGroupTitle(TAB1_ID), equalTo(CUSTOMIZED_TITLE1)); - } - - @Test - @DisableFeatures(ChromeFeatureList.ANDROID_TAB_GROUP_STABLE_IDS) - public void tabClosureCommitted_DeleteStoredTitle_GroupSize1NotSupported() { - // Mock that we have a stored title stored with reference to root ID of tab1. - mTabGroupTitleEditor.storeTabGroupTitle(TAB1_ID, CUSTOMIZED_TITLE1); - assertThat(mStorage.size(), equalTo(1)); - - // Mock that tab1 and tab2 are in the same group and group root id is TAB1_ID. - List<Tab> tabs = new ArrayList<>(Arrays.asList(mTab1, mTab2)); - createTabGroup(tabs, TAB1_ID, GROUP_1_ID); - - // Mock that tab1 is closed and the group becomes a single tab. - when(mTabGroupModelFilter.getRelatedTabCountForRootId(TAB1_ID)).thenReturn(1); - when(mTabGroupModelFilter.isTabInTabGroup(mTab1)).thenReturn(false); - when(mTabGroupModelFilter.isTabInTabGroup(mTab2)).thenReturn(false); - mTabModelObserverCaptor.getValue().tabClosureCommitted(mTab2); - - // The stored title should be deleted. - assertThat(mStorage.size(), equalTo(0)); - } - - @Test - public void tabClosureCommitted_DeleteStoredTitle_GroupSize1Supported() { - // Mock that we have a stored title stored with reference to root ID of tab1. - mTabGroupTitleEditor.storeTabGroupTitle(TAB1_ID, CUSTOMIZED_TITLE1); - assertThat(mStorage.size(), equalTo(1)); - - // Mock that tab1 and tab2 are in the same group and group root id is TAB1_ID. - List<Tab> tabs = new ArrayList<>(Arrays.asList(mTab1, mTab2)); - createTabGroup(tabs, TAB1_ID, GROUP_1_ID); - - // Mock that tab1 is closed and the group becomes a single tab. - when(mTabGroupModelFilter.getRelatedTabCountForRootId(TAB1_ID)).thenReturn(1); - when(mTabGroupModelFilter.isTabInTabGroup(mTab1)).thenReturn(true); - when(mTabGroupModelFilter.isTabInTabGroup(mTab2)).thenReturn(false); - mTabModelObserverCaptor.getValue().tabClosureCommitted(mTab2); - - // The stored title should not be deleted. - assertThat(mStorage.size(), equalTo(1)); - - when(mTabGroupModelFilter.isTabInTabGroup(mTab1)).thenReturn(false); - mTabModelObserverCaptor.getValue().tabClosureCommitted(mTab1); - - // The stored title should be deleted. - assertThat(mStorage.size(), equalTo(0)); - } - - @Test - public void tabMergeIntoGroup_NotDeleteStoredTitle() { - // Mock that we have two stored titles with reference to root ID of tab1 and tab3. - mTabGroupTitleEditor.storeTabGroupTitle(TAB1_ID, CUSTOMIZED_TITLE1); - mTabGroupTitleEditor.storeTabGroupTitle(TAB3_ID, CUSTOMIZED_TITLE2); - assertThat(mStorage.size(), equalTo(2)); - - // Mock that tab1 and tab2 are in the same group and group root id is TAB1_ID; tab3 and tab4 - // are in the same group and group root id is TAB3_ID. - List<Tab> group1 = new ArrayList<>(Arrays.asList(mTab1, mTab2)); - createTabGroup(group1, TAB1_ID, GROUP_1_ID); - List<Tab> group2 = new ArrayList<>(Arrays.asList(mTab3, mTab4)); - createTabGroup(group2, TAB3_ID, GROUP_2_ID); - - mTabGroupModelFilterObserverCaptor.getValue().willMergeTabToGroup(mTab1, TAB3_ID); - - // The title of the source group will not be deleted until the merge is committed, after - // SnackbarController#onDismissNoAction is called for the UndoGroupSnackbarController. - assertThat(mStorage.size(), equalTo(2)); - assertThat(mTabGroupTitleEditor.getTabGroupTitle(TAB1_ID), equalTo(CUSTOMIZED_TITLE1)); - assertThat(mTabGroupTitleEditor.getTabGroupTitle(TAB3_ID), equalTo(CUSTOMIZED_TITLE2)); - } - - @Test - public void tabMergeIntoGroup_HandOverStoredTitle() { - // Mock that we have a stored title stored with reference to root ID of tab1. - mTabGroupTitleEditor.storeTabGroupTitle(TAB1_ID, CUSTOMIZED_TITLE1); - assertThat(mStorage.size(), equalTo(1)); - - // Mock that tab1 and tab2 are in the same group and group root id is TAB1_ID; tab3 and tab4 - // are in the same group and group root id is TAB3_ID. - List<Tab> group1 = new ArrayList<>(Arrays.asList(mTab1, mTab2)); - createTabGroup(group1, TAB1_ID, GROUP_1_ID); - List<Tab> group2 = new ArrayList<>(Arrays.asList(mTab3, mTab4)); - createTabGroup(group2, TAB3_ID, GROUP_2_ID); - - mTabGroupModelFilterObserverCaptor.getValue().willMergeTabToGroup(mTab1, TAB3_ID); - - // The stored title should be assigned to the new root id. The title of the source group - // will not be deleted until the merge is committed, after - // SnackbarController#onDismissNoAction is called for the UndoGroupSnackbarController. - assertThat(mStorage.size(), equalTo(2)); - assertThat(mTabGroupTitleEditor.getTabGroupTitle(TAB1_ID), equalTo(CUSTOMIZED_TITLE1)); - assertThat(mTabGroupTitleEditor.getTabGroupTitle(TAB3_ID), equalTo(CUSTOMIZED_TITLE1)); - } - - @Test - @DisableFeatures(ChromeFeatureList.ANDROID_TAB_GROUP_STABLE_IDS) - public void tabMoveOutOfGroup_DeleteStoredTitle_GroupSize1NotSupported() { - // Mock that we have a stored title stored with reference to root ID of tab1. - mTabGroupTitleEditor.storeTabGroupTitle(TAB1_ID, CUSTOMIZED_TITLE1); - assertThat(mStorage.size(), equalTo(1)); - - // Mock that tab1 and tab2 are in the same group and group root id is TAB1_ID. - List<Tab> tabs = new ArrayList<>(Arrays.asList(mTab1, mTab2)); - createTabGroup(tabs, TAB1_ID, GROUP_1_ID); - - // Mock that we are going to ungroup tab1, and the group becomes a single tab after ungroup. - mTabGroupModelFilterObserverCaptor.getValue().willMoveTabOutOfGroup(mTab1, TAB2_ID); - - // The stored title should be deleted. - assertThat(mStorage.size(), equalTo(0)); - } - - @Test - public void tabMoveOutOfGroup_DeleteStoredTitle_GroupSize1Supported() { - // Mock that we have a stored title stored with reference to root ID of tab1. - mTabGroupTitleEditor.storeTabGroupTitle(TAB1_ID, CUSTOMIZED_TITLE1); - assertThat(mStorage.size(), equalTo(1)); - - // Mock that tab1 and tab2 are in the same group and group root id is TAB1_ID. - List<Tab> tabs = new ArrayList<>(Arrays.asList(mTab1, mTab2)); - createTabGroup(tabs, TAB1_ID, GROUP_1_ID); - - // Mock that we are going to ungroup tab1, and the group becomes a single tab after ungroup. - mTabGroupModelFilterObserverCaptor.getValue().willMoveTabOutOfGroup(mTab1, TAB2_ID); - when(mTabGroupModelFilter.getGroupLastShownTab(TAB1_ID)).thenReturn(mTab1); - when(mTabGroupModelFilter.getGroupLastShownTab(TAB2_ID)).thenReturn(mTab2); - when(mTabGroupModelFilter.getRelatedTabCountForRootId(TAB1_ID)).thenReturn(1); - when(mTabGroupModelFilter.getRelatedTabCountForRootId(TAB2_ID)).thenReturn(1); - when(mTab1.getRootId()).thenReturn(TAB1_ID); - when(mTab1.getTabGroupId()).thenReturn(null); - when(mTabGroupModelFilter.isTabInTabGroup(mTab1)).thenReturn(false); - when(mTab2.getRootId()).thenReturn(TAB2_ID); - - // The stored title should not be deleted. - assertThat(mStorage.size(), equalTo(1)); - - mTabGroupModelFilterObserverCaptor.getValue().willMoveTabOutOfGroup(mTab2, TAB2_ID); - - // The stored title should be deleted. - assertThat(mStorage.size(), equalTo(0)); - } - - @Test - public void tabMoveOutOfGroup_HandOverStoredTitle() { - // Mock that we have a stored title stored with reference to root ID of tab1. - mTabGroupTitleEditor.storeTabGroupTitle(TAB1_ID, CUSTOMIZED_TITLE1); - - // Mock that tab1, tab2 and newTab are in the same group and group root id is TAB1_ID. - Tab newTab = TabUiUnitTestUtils.prepareTab(TAB3_ID, TAB3_TITLE); - List<Tab> tabs = new ArrayList<>(Arrays.asList(mTab1, mTab2, newTab)); - createTabGroup(tabs, TAB1_ID, GROUP_1_ID); - - // Mock that we are going to ungroup tab1, and the group is still a group after ungroup with - // root id become TAB2_ID. - mTabGroupModelFilterObserverCaptor.getValue().willMoveTabOutOfGroup(mTab1, TAB2_ID); - - // The stored title should be assigned to the new root id. - assertThat(mStorage.size(), equalTo(1)); - assertThat(mTabGroupTitleEditor.getTabGroupTitle(TAB1_ID), equalTo(null)); - assertThat(mTabGroupTitleEditor.getTabGroupTitle(TAB2_ID), equalTo(CUSTOMIZED_TITLE1)); } @Test @@ -373,15 +87,4 @@ assertFalse(mTabGroupTitleEditor.isDefaultTitle(fourTabsTitle, 3)); assertFalse(mTabGroupTitleEditor.isDefaultTitle("Foo", fourTabsCount)); } - - private void createTabGroup(List<Tab> tabs, int rootId, @Nullable Token groupId) { - Tab lastTab = tabs.isEmpty() ? null : tabs.get(0); - when(mTabGroupModelFilter.getGroupLastShownTab(rootId)).thenReturn(lastTab); - when(mTabGroupModelFilter.getRelatedTabCountForRootId(rootId)).thenReturn(tabs.size()); - for (Tab tab : tabs) { - when(mTabGroupModelFilter.isTabInTabGroup(tab)).thenReturn(tabs.size() != 1); - when(tab.getRootId()).thenReturn(rootId); - when(tab.getTabGroupId()).thenReturn(groupId); - } - } }
diff --git a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupVisualDataManagerUnitTest.java b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupVisualDataManagerUnitTest.java new file mode 100644 index 0000000..91095c4e --- /dev/null +++ b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupVisualDataManagerUnitTest.java
@@ -0,0 +1,440 @@ +// Copyright 2024 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chrome.browser.tasks.tab_management; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.content.SharedPreferences; + +import androidx.annotation.Nullable; +import androidx.collection.ArraySet; + +import org.junit.After; +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.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import org.chromium.base.ContextUtils; +import org.chromium.base.Token; +import org.chromium.base.test.BaseRobolectricTestRunner; +import org.chromium.base.test.util.Features; +import org.chromium.base.test.util.Features.DisableFeatures; +import org.chromium.base.test.util.Features.EnableFeatures; +import org.chromium.chrome.browser.flags.ChromeFeatureList; +import org.chromium.chrome.browser.tab.Tab; +import org.chromium.chrome.browser.tabmodel.TabModelFilterProvider; +import org.chromium.chrome.browser.tabmodel.TabModelObserver; +import org.chromium.chrome.browser.tabmodel.TabModelSelector; +import org.chromium.chrome.browser.tasks.tab_groups.TabGroupModelFilter; +import org.chromium.chrome.browser.tasks.tab_groups.TabGroupModelFilterObserver; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Set; + +/** Tests for {@link TabGroupVisualDataManager}. */ +@SuppressWarnings({"ArraysAsListWithZeroOrOneArgument", "ResultOfMethodCallIgnored"}) +@RunWith(BaseRobolectricTestRunner.class) +@EnableFeatures({ + ChromeFeatureList.ANDROID_TAB_GROUP_STABLE_IDS, + ChromeFeatureList.TAB_GROUP_PARITY_ANDROID +}) +public class TabGroupVisualDataManagerUnitTest { + @Rule public TestRule mProcessor = new Features.JUnitProcessor(); + + private static final String TAB_GROUP_TITLES_FILE_NAME = "tab_group_titles"; + private static final String TAB_GROUP_COLORS_FILE_NAME = "tab_group_colors"; + + private static final String TAB1_TITLE = "Tab1"; + private static final String TAB2_TITLE = "Tab2"; + private static final String TAB3_TITLE = "Tab3"; + private static final String TAB4_TITLE = "Tab4"; + private static final String CUSTOMIZED_TITLE1 = "Some cool tabs"; + private static final String CUSTOMIZED_TITLE2 = "Other cool tabs"; + private static final int COLOR1_ID = 0; + private static final int COLOR2_ID = 1; + private static final int INVALID_COLOR_ID = -1; + private static final int TAB1_ID = 456; + private static final int TAB2_ID = 789; + private static final int TAB3_ID = 123; + private static final int TAB4_ID = 357; + private static final Token GROUP_1_ID = new Token(1L, 2L); + private static final Token GROUP_2_ID = new Token(2L, 3L); + + @Mock private Context mContext; + @Mock private TabGroupModelFilter mTabGroupModelFilter; + @Mock private TabGroupModelFilter mIncognitoTabGroupModelFilter; + @Mock private TabModelSelector mTabModelSelector; + @Mock private TabModelFilterProvider mTabModelFilterProvider; + @Mock private SharedPreferences mSharedPreferencesTitle; + @Mock private SharedPreferences.Editor mEditorTitle; + @Mock private SharedPreferences.Editor mPutStringEditorTitle; + @Mock private SharedPreferences.Editor mRemoveEditorTitle; + @Mock private SharedPreferences mSharedPreferencesColor; + @Mock private SharedPreferences.Editor mEditorColor; + @Mock private SharedPreferences.Editor mPutIntEditorColor; + @Mock private SharedPreferences.Editor mRemoveEditorColor; + @Captor private ArgumentCaptor<TabModelObserver> mTabModelObserverCaptor; + @Captor private ArgumentCaptor<TabGroupModelFilterObserver> mTabGroupModelFilterObserverCaptor; + + @Captor + private ArgumentCaptor<TabGroupModelFilterObserver> mIncognitoTabGroupModelFilterObserverCaptor; + + private Tab mTab1; + private Tab mTab2; + private Tab mTab3; + private Tab mTab4; + private TabGroupVisualDataManager mTabGroupVisualDataManager; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + mTab1 = TabUiUnitTestUtils.prepareTab(TAB1_ID, TAB1_TITLE); + mTab2 = TabUiUnitTestUtils.prepareTab(TAB2_ID, TAB2_TITLE); + mTab3 = TabUiUnitTestUtils.prepareTab(TAB3_ID, TAB3_TITLE); + mTab4 = TabUiUnitTestUtils.prepareTab(TAB4_ID, TAB4_TITLE); + + doReturn(true).when(mTabModelSelector).isTabStateInitialized(); + doReturn(mTabModelFilterProvider).when(mTabModelSelector).getTabModelFilterProvider(); + doReturn(mTabGroupModelFilter).when(mTabModelFilterProvider).getCurrentTabModelFilter(); + doReturn(mTabGroupModelFilter).when(mTabModelFilterProvider).getTabModelFilter(false); + doReturn(mIncognitoTabGroupModelFilter) + .when(mTabModelFilterProvider) + .getTabModelFilter(true); + + doNothing() + .when(mTabModelFilterProvider) + .addTabModelFilterObserver(mTabModelObserverCaptor.capture()); + doNothing() + .when(mTabGroupModelFilter) + .addTabGroupObserver(mTabGroupModelFilterObserverCaptor.capture()); + doNothing() + .when(mIncognitoTabGroupModelFilter) + .addTabGroupObserver(mIncognitoTabGroupModelFilterObserverCaptor.capture()); + + mTabGroupVisualDataManager = new TabGroupVisualDataManager(mTabModelSelector); + + doReturn(mSharedPreferencesTitle) + .when(mContext) + .getSharedPreferences(TAB_GROUP_TITLES_FILE_NAME, Context.MODE_PRIVATE); + doReturn(mEditorTitle).when(mSharedPreferencesTitle).edit(); + doReturn(mRemoveEditorTitle).when(mEditorTitle).remove(any(String.class)); + doReturn(mPutStringEditorTitle) + .when(mEditorTitle) + .putString(any(String.class), any(String.class)); + + doReturn(mSharedPreferencesColor) + .when(mContext) + .getSharedPreferences(TAB_GROUP_COLORS_FILE_NAME, Context.MODE_PRIVATE); + doReturn(mEditorColor).when(mSharedPreferencesColor).edit(); + doReturn(mRemoveEditorColor).when(mEditorColor).remove(any(String.class)); + doReturn(mPutIntEditorColor) + .when(mEditorColor) + .putInt(any(String.class), any(Integer.class)); + + ContextUtils.initApplicationContextForTests(mContext); + } + + @After + public void tearDown() { + mTabGroupVisualDataManager.destroy(); + } + + @Test + public void tabClosureCommitted_RootTab_NotDeleteStoredTitle() { + // Assume that CUSTOMIZED_TITLE1 and COLOR1_ID are associated with the tab group. + // Mock that tab1, tab2, new tab are in the same group and group root id is TAB1_ID. + Tab newTab = TabUiUnitTestUtils.prepareTab(TAB3_ID, TAB3_TITLE); + List<Tab> tabs = new ArrayList<>(Arrays.asList(mTab1, mTab2, newTab)); + createTabGroup(tabs, TAB1_ID, GROUP_1_ID); + + // Mock that the root tab of the group, tab1, is closed. + mTabModelObserverCaptor.getValue().tabClosureCommitted(mTab1); + + // Verify that the title and color were not deleted. + verify(mEditorTitle, never()).remove(eq(String.valueOf(TAB1_ID))); + verify(mRemoveEditorTitle, never()).apply(); + verify(mEditorColor, never()).remove(eq(String.valueOf(TAB1_ID))); + verify(mRemoveEditorColor, never()).apply(); + } + + @Test + public void tabClosureCommitted_NotRootTab_NotDeleteStoredTitle() { + // Assume that CUSTOMIZED_TITLE1 and COLOR1_ID are associated with the tab group. + // Mock that tab1, tab2, new tab are in the same group and group root id is TAB1_ID. + Tab newTab = TabUiUnitTestUtils.prepareTab(TAB3_ID, TAB3_TITLE); + List<Tab> groupBeforeClosure = new ArrayList<>(Arrays.asList(mTab1, mTab2, newTab)); + createTabGroup(groupBeforeClosure, TAB1_ID, GROUP_1_ID); + + // Mock that tab2 is closed and tab2 is not the root tab. + mTabModelObserverCaptor.getValue().tabClosureCommitted(mTab2); + + // Verify that the title and color were not deleted. + verify(mEditorTitle, never()).remove(eq(String.valueOf(TAB1_ID))); + verify(mRemoveEditorTitle, never()).apply(); + verify(mEditorColor, never()).remove(eq(String.valueOf(TAB1_ID))); + verify(mRemoveEditorColor, never()).apply(); + } + + @Test + @DisableFeatures(ChromeFeatureList.ANDROID_TAB_GROUP_STABLE_IDS) + public void tabClosureCommitted_DeleteStoredTitle_GroupSize1NotSupported() { + // Assume that CUSTOMIZED_TITLE1 and COLOR1_ID are associated with the tab group. + // Mock that tab1 and tab2 are in the same group and group root id is TAB1_ID. + List<Tab> tabs = new ArrayList<>(Arrays.asList(mTab1, mTab2)); + createTabGroup(tabs, TAB1_ID, GROUP_1_ID); + + // Mock that tab1 is closed and the group becomes a single tab. + when(mTabGroupModelFilter.getRelatedTabCountForRootId(TAB1_ID)).thenReturn(1); + when(mTabGroupModelFilter.isTabInTabGroup(mTab1)).thenReturn(false); + when(mTabGroupModelFilter.isTabInTabGroup(mTab2)).thenReturn(false); + mTabModelObserverCaptor.getValue().tabClosureCommitted(mTab2); + + // Verify that the title and color were deleted. + verify(mEditorTitle).remove(eq(String.valueOf(TAB1_ID))); + verify(mRemoveEditorTitle).apply(); + verify(mEditorColor).remove(eq(String.valueOf(TAB1_ID))); + verify(mRemoveEditorColor).apply(); + } + + @Test + public void tabClosureCommitted_DeleteStoredTitle_GroupSize1Supported() { + // Assume that CUSTOMIZED_TITLE1 and COLOR1_ID are associated with the tab group. + // Mock that tab1 and tab2 are in the same group and group root id is TAB1_ID. + List<Tab> tabs = new ArrayList<>(Arrays.asList(mTab1, mTab2)); + createTabGroup(tabs, TAB1_ID, GROUP_1_ID); + + // Mock that tab1 is closed and the group becomes a single tab. + when(mTabGroupModelFilter.getRelatedTabCountForRootId(TAB1_ID)).thenReturn(1); + when(mTabGroupModelFilter.isTabInTabGroup(mTab1)).thenReturn(true); + when(mTabGroupModelFilter.isTabInTabGroup(mTab2)).thenReturn(false); + mTabModelObserverCaptor.getValue().tabClosureCommitted(mTab2); + + // Verify that the title and color were not deleted. + verify(mEditorTitle, never()).remove(eq(String.valueOf(TAB1_ID))); + verify(mRemoveEditorTitle, never()).apply(); + verify(mEditorColor, never()).remove(eq(String.valueOf(TAB1_ID))); + verify(mRemoveEditorColor, never()).apply(); + + when(mTabGroupModelFilter.isTabInTabGroup(mTab1)).thenReturn(false); + mTabModelObserverCaptor.getValue().tabClosureCommitted(mTab1); + + // Verify that the title and color were deleted. + verify(mEditorTitle).remove(eq(String.valueOf(TAB1_ID))); + verify(mRemoveEditorTitle).apply(); + verify(mEditorColor).remove(eq(String.valueOf(TAB1_ID))); + verify(mRemoveEditorColor).apply(); + } + + // TODO(b/41490324): Remove this test when introducing TabGroupCreationDialog logic. + @Test + public void didCreateNewGroup_StoreColor() { + // Mock that tab1 and tab2 are in the same group and group root id is TAB1_ID. + // None of the tab groups have colors associated with them. + List<Tab> tabs = new ArrayList<>(Arrays.asList(mTab1, mTab2)); + createTabGroup(tabs, TAB1_ID, GROUP_1_ID); + + // Create roodId set and mock that it has no color stored. + Set<Integer> rootIdsSet = new ArraySet<>(); + rootIdsSet.add(TAB1_ID); + when(mTabGroupModelFilter.getAllTabGroupRootIds()).thenReturn(rootIdsSet); + when(mSharedPreferencesColor.getInt(String.valueOf(TAB1_ID), INVALID_COLOR_ID)) + .thenReturn(INVALID_COLOR_ID); + + mTabGroupModelFilterObserverCaptor + .getValue() + .didCreateNewGroup(TAB1_ID, mTabGroupModelFilter); + + // Verify that a default color was stored. + verify(mEditorColor).putInt(eq(String.valueOf(TAB1_ID)), eq(COLOR1_ID)); + verify(mPutIntEditorColor).apply(); + } + + @Test + public void tabMergeIntoGroup_NotDeleteStoredTitle() { + // Mock that TITLE1, TITLE2 and COLOR1_ID, COLOR2_ID are associated with the groups. + when(mSharedPreferencesTitle.getString(String.valueOf(TAB1_ID), null)) + .thenReturn(CUSTOMIZED_TITLE1); + when(mSharedPreferencesTitle.getString(String.valueOf(TAB3_ID), null)) + .thenReturn(CUSTOMIZED_TITLE2); + when(mSharedPreferencesColor.getInt(String.valueOf(TAB1_ID), INVALID_COLOR_ID)) + .thenReturn(COLOR1_ID); + when(mSharedPreferencesColor.getInt(String.valueOf(TAB3_ID), INVALID_COLOR_ID)) + .thenReturn(COLOR2_ID); + + // Mock that tab1 and tab2 are in the same group and group root id is TAB1_ID; tab3 and tab4 + // are in the same group and group root id is TAB3_ID. + List<Tab> group1 = new ArrayList<>(Arrays.asList(mTab1, mTab2)); + createTabGroup(group1, TAB1_ID, GROUP_1_ID); + List<Tab> group2 = new ArrayList<>(Arrays.asList(mTab3, mTab4)); + createTabGroup(group2, TAB3_ID, GROUP_2_ID); + + mTabGroupModelFilterObserverCaptor.getValue().willMergeTabToGroup(mTab1, TAB3_ID); + + // The title of the source group will not be deleted until the merge is committed, after + // SnackbarController#onDismissNoAction is called for the UndoGroupSnackbarController. + verify(mEditorTitle, never()).putString(eq(String.valueOf(TAB3_ID)), eq(CUSTOMIZED_TITLE1)); + verify(mRemoveEditorTitle, never()).apply(); + verify(mEditorColor, never()).putInt(eq(String.valueOf(TAB3_ID)), eq(COLOR1_ID)); + verify(mRemoveEditorColor, never()).apply(); + } + + @Test + public void tabMergeIntoGroup_HandOverStoredTitle() { + // Mock that TITLE1 and COLOR1_ID are associated with the group of TAB1_ID. + when(mSharedPreferencesTitle.getString(String.valueOf(TAB1_ID), null)) + .thenReturn(CUSTOMIZED_TITLE1); + when(mSharedPreferencesTitle.getString(String.valueOf(TAB3_ID), null)).thenReturn(null); + when(mSharedPreferencesColor.getInt(String.valueOf(TAB1_ID), INVALID_COLOR_ID)) + .thenReturn(COLOR1_ID); + when(mSharedPreferencesColor.getInt(String.valueOf(TAB3_ID), INVALID_COLOR_ID)) + .thenReturn(INVALID_COLOR_ID); + + // Mock that tab1 and tab2 are in the same group and group root id is TAB1_ID; tab3 and tab4 + // are in the same group and group root id is TAB3_ID. + List<Tab> group1 = new ArrayList<>(Arrays.asList(mTab1, mTab2)); + createTabGroup(group1, TAB1_ID, GROUP_1_ID); + List<Tab> group2 = new ArrayList<>(Arrays.asList(mTab3, mTab4)); + createTabGroup(group2, TAB3_ID, GROUP_2_ID); + + mTabGroupModelFilterObserverCaptor.getValue().willMergeTabToGroup(mTab1, TAB3_ID); + + // The stored title should be assigned to the new root id. The title of the source group + // will not be deleted until the merge is committed, after + // SnackbarController#onDismissNoAction is called for the UndoGroupSnackbarController. + verify(mEditorTitle).putString(eq(String.valueOf(TAB3_ID)), eq(CUSTOMIZED_TITLE1)); + verify(mPutStringEditorTitle).apply(); + verify(mEditorColor).putInt(eq(String.valueOf(TAB3_ID)), eq(COLOR1_ID)); + verify(mPutIntEditorColor).apply(); + } + + @Test + @DisableFeatures(ChromeFeatureList.ANDROID_TAB_GROUP_STABLE_IDS) + public void tabMoveOutOfGroup_DeleteStoredTitle_GroupSize1NotSupported() { + // Mock that TITLE1 and COLOR1_ID are associated with the group of TAB1_ID. + when(mSharedPreferencesTitle.getString(String.valueOf(TAB1_ID), null)) + .thenReturn(CUSTOMIZED_TITLE1); + when(mSharedPreferencesColor.getInt(String.valueOf(TAB1_ID), INVALID_COLOR_ID)) + .thenReturn(COLOR1_ID); + + // Mock that tab1 and tab2 are in the same group and group root id is TAB1_ID. + List<Tab> tabs = new ArrayList<>(Arrays.asList(mTab1, mTab2)); + createTabGroup(tabs, TAB1_ID, GROUP_1_ID); + + // Mock that we are going to ungroup tab1, and the group becomes a single tab after ungroup. + mTabGroupModelFilterObserverCaptor.getValue().willMoveTabOutOfGroup(mTab1, TAB2_ID); + + // Verify that the title and color were deleted. + verify(mEditorTitle).remove(eq(String.valueOf(TAB1_ID))); + verify(mRemoveEditorTitle).apply(); + verify(mEditorColor).remove(eq(String.valueOf(TAB1_ID))); + verify(mRemoveEditorColor).apply(); + } + + @Test + public void tabMoveOutOfGroup_DeleteStoredTitle_GroupSize1Supported() { + // Mock that TITLE1 and COLOR1_ID are associated with the group of TAB1_ID. + when(mSharedPreferencesTitle.getString(String.valueOf(TAB1_ID), null)) + .thenReturn(CUSTOMIZED_TITLE1); + when(mSharedPreferencesColor.getInt(String.valueOf(TAB1_ID), INVALID_COLOR_ID)) + .thenReturn(COLOR1_ID); + + // Mock that tab1 and tab2 are in the same group and group root id is TAB1_ID. + List<Tab> tabs = new ArrayList<>(Arrays.asList(mTab1, mTab2)); + createTabGroup(tabs, TAB1_ID, GROUP_1_ID); + + // Mock that we are going to ungroup tab1, and the group becomes a single tab after ungroup. + when(mTabGroupModelFilter.getGroupLastShownTab(TAB1_ID)).thenReturn(mTab1); + when(mTabGroupModelFilter.getGroupLastShownTab(TAB2_ID)).thenReturn(mTab2); + when(mTabGroupModelFilter.getRelatedTabCountForRootId(TAB1_ID)).thenReturn(2); + when(mTabGroupModelFilter.getRelatedTabCountForRootId(TAB2_ID)).thenReturn(2); + when(mTab1.getRootId()).thenReturn(TAB1_ID); + when(mTab1.getTabGroupId()).thenReturn(null); + when(mTabGroupModelFilter.isTabInTabGroup(mTab1)).thenReturn(false); + when(mTab2.getRootId()).thenReturn(TAB2_ID); + + // Mock the situation that the root tab is not the tab being moved out. + mTabGroupModelFilterObserverCaptor.getValue().willMoveTabOutOfGroup(mTab1, TAB1_ID); + + // Verify that the title and color were not deleted. + verify(mEditorTitle, never()).remove(eq(String.valueOf(TAB1_ID))); + verify(mRemoveEditorTitle, never()).apply(); + verify(mEditorColor, never()).remove(eq(String.valueOf(TAB1_ID))); + verify(mRemoveEditorColor, never()).apply(); + + // Mock that TITLE1 and COLOR1_ID are associated with the group of TAB1_ID. + when(mSharedPreferencesTitle.getString(String.valueOf(TAB2_ID), null)) + .thenReturn(CUSTOMIZED_TITLE1); + when(mSharedPreferencesColor.getInt(String.valueOf(TAB2_ID), INVALID_COLOR_ID)) + .thenReturn(COLOR1_ID); + + // Mock that we are going to ungroup the last tab in a size 1 tab group, and it is the root + // tab. + when(mTabGroupModelFilter.getRelatedTabCountForRootId(TAB1_ID)).thenReturn(1); + when(mTabGroupModelFilter.getRelatedTabCountForRootId(TAB2_ID)).thenReturn(1); + + mTabGroupModelFilterObserverCaptor.getValue().willMoveTabOutOfGroup(mTab2, TAB1_ID); + + // Verify that the title and color were deleted. + verify(mEditorTitle).remove(eq(String.valueOf(TAB2_ID))); + verify(mRemoveEditorTitle).apply(); + verify(mEditorColor).remove(eq(String.valueOf(TAB2_ID))); + verify(mRemoveEditorColor).apply(); + } + + @Test + public void tabMoveOutOfGroup_HandOverStoredTitle() { + // Mock that TITLE1 and COLOR1_ID are associated with the group of TAB1_ID. + when(mSharedPreferencesTitle.getString(String.valueOf(TAB1_ID), null)) + .thenReturn(CUSTOMIZED_TITLE1); + when(mSharedPreferencesColor.getInt(String.valueOf(TAB1_ID), INVALID_COLOR_ID)) + .thenReturn(COLOR1_ID); + + // Mock that tab1, tab2 and newTab are in the same group and group root id is TAB1_ID. + Tab newTab = TabUiUnitTestUtils.prepareTab(TAB3_ID, TAB3_TITLE); + List<Tab> tabs = new ArrayList<>(Arrays.asList(mTab1, mTab2, newTab)); + createTabGroup(tabs, TAB1_ID, GROUP_1_ID); + + // Mock that we are going to ungroup tab1, and the group is still a group after ungroup with + // root id become TAB2_ID. + mTabGroupModelFilterObserverCaptor.getValue().willMoveTabOutOfGroup(mTab1, TAB2_ID); + + // The stored title should be assigned to the new root id. + verify(mEditorTitle).remove(eq(String.valueOf(TAB1_ID))); + verify(mRemoveEditorTitle).apply(); + verify(mEditorTitle).putString(eq(String.valueOf(TAB2_ID)), eq(CUSTOMIZED_TITLE1)); + verify(mPutStringEditorTitle).apply(); + verify(mEditorColor).remove(eq(String.valueOf(TAB1_ID))); + verify(mRemoveEditorColor).apply(); + verify(mEditorColor).putInt(eq(String.valueOf(TAB2_ID)), eq(COLOR1_ID)); + verify(mPutIntEditorColor).apply(); + } + + private void createTabGroup(List<Tab> tabs, int rootId, @Nullable Token groupId) { + Tab lastTab = tabs.isEmpty() ? null : tabs.get(0); + when(mTabGroupModelFilter.getGroupLastShownTab(rootId)).thenReturn(lastTab); + when(mTabGroupModelFilter.getRelatedTabCountForRootId(rootId)).thenReturn(tabs.size()); + for (Tab tab : tabs) { + when(mTabGroupModelFilter.isTabInTabGroup(tab)).thenReturn(tabs.size() != 1); + when(tab.getRootId()).thenReturn(rootId); + when(tab.getTabGroupId()).thenReturn(groupId); + } + } +}
diff --git a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediatorUnitTest.java b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediatorUnitTest.java index 57666c0..bb719bc 100644 --- a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediatorUnitTest.java +++ b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediatorUnitTest.java
@@ -1130,7 +1130,7 @@ mMediator.initWithNative(mProfile); // mTabModelObserverCaptor captures on every initWithNative call. - verify(mTabGroupModelFilter, times(4)).addObserver(mTabModelObserverCaptor.capture()); + verify(mTabGroupModelFilter, times(2)).addObserver(mTabModelObserverCaptor.capture()); initAndAssertAllProperties(); ThumbnailFetcher tab1Fetcher = mModel.get(0).model.get(TabProperties.THUMBNAIL_FETCHER); @@ -3204,11 +3204,11 @@ @Test public void testChangingTabModelFilters() { mCurrentTabModelFilterSupplier.set(mIncognitoTabGroupModelFilter); - // Once for the Mediator and once for the TabGroupTitleEditor. - verify(mTabGroupModelFilter, times(2)).removeObserver(any()); - verify(mTabGroupModelFilter, times(2)).removeTabGroupObserver(any()); - verify(mIncognitoTabGroupModelFilter, times(2)).addObserver(any()); - verify(mIncognitoTabGroupModelFilter, times(2)).addTabGroupObserver(any()); + // Once for the Mediator. + verify(mTabGroupModelFilter).removeObserver(any()); + verify(mTabGroupModelFilter).removeTabGroupObserver(any()); + verify(mIncognitoTabGroupModelFilter).addObserver(any()); + verify(mIncognitoTabGroupModelFilter).addTabGroupObserver(any()); } @Test @@ -3566,10 +3566,10 @@ mMediator.initWithNative(mProfile); assertThat( - mTabModelObserverCaptor.getAllValues().size(), equalTo(tabModelObserverCount + 2)); + mTabModelObserverCaptor.getAllValues().size(), equalTo(tabModelObserverCount + 1)); assertThat( mTabGroupModelFilterObserverCaptor.getAllValues().size(), - equalTo(tabGroupModelFilterObserverCount + 2)); + equalTo(tabGroupModelFilterObserverCount + 1)); mMediatorTabModelObserver = mTabModelObserverCaptor.getAllValues().get(tabModelObserverCount);
diff --git a/chrome/android/features/tab_ui/tab_management_java_sources.gni b/chrome/android/features/tab_ui/tab_management_java_sources.gni index 33102dd..28472d07 100644 --- a/chrome/android/features/tab_ui/tab_management_java_sources.gni +++ b/chrome/android/features/tab_ui/tab_management_java_sources.gni
@@ -14,6 +14,7 @@ "//chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/ColorPickerUtils.java", "//chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/RecyclerViewPosition.java", "//chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUi.java", + "//chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupVisualDataManager.java", "//chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListFaviconProvider.java", "//chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabManagementDelegate.java", "//chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcher.java", @@ -203,6 +204,7 @@ "//chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupCreationTextInputLayoutTest.java", "//chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupTitleEditorUnitTest.java", "//chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiMediatorUnitTest.java", + "//chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupVisualDataManagerUnitTest.java", "//chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabListContainerViewBinderUnitTest.java", "//chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabListEditorActionUnitTestHelper.java", "//chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabListEditorBookmarkActionUnitTest.java",
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java index 97b8366..083de24 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
@@ -209,8 +209,11 @@ import org.chromium.chrome.browser.tasks.ReturnToChromeUtil; import org.chromium.chrome.browser.tasks.ReturnToChromeUtil.ReturnToChromeBackPressHandler; import org.chromium.chrome.browser.tasks.TasksUma; +import org.chromium.chrome.browser.tasks.tab_groups.TabGroupColorUtils; +import org.chromium.chrome.browser.tasks.tab_groups.TabGroupModelFilter; import org.chromium.chrome.browser.tasks.tab_management.CloseAllTabsDialog; import org.chromium.chrome.browser.tasks.tab_management.TabGroupUi; +import org.chromium.chrome.browser.tasks.tab_management.TabGroupVisualDataManager; import org.chromium.chrome.browser.tasks.tab_management.TabManagementDelegateProvider; import org.chromium.chrome.browser.tasks.tab_management.TabSwitcher; import org.chromium.chrome.browser.tasks.tab_management.TabUiFeatureUtilities; @@ -498,6 +501,9 @@ RecordUserAction.record("MobileNewTabOpened"); }; + // Manager for tab group visual data lifecycle updates. + private TabGroupVisualDataManager mTabGroupVisualDataManager; + /** * This class is used to warm up the chrome split ClassLoader. See SplitChromeApplication for * more info @@ -674,6 +680,14 @@ mTabModelOrchestrator.onNativeLibraryReady(getTabContentManager()); + TabModelUtils.runOnTabStateInitialized( + mTabModelSelector, + (tabModelSelector) -> { + assert tabModelSelector != null; + mTabGroupVisualDataManager = + new TabGroupVisualDataManager(tabModelSelector); + }); + // For saving non-incognito tab closures for Recent Tabs. mHistoricalTabModelObserver = new HistoricalTabModelObserver(mTabModelSelector.getModel(false)); @@ -2301,6 +2315,20 @@ this::getSnackbarManager, dialogVisibilitySupplier); + if (ChromeFeatureList.sTabGroupParityAndroid.isEnabled()) { + TabModelUtils.runOnTabStateInitialized( + getTabModelSelectorSupplier().get(), + (tabModelSelectorReturn) -> { + TabGroupColorUtils.assignTabGroupColorsIfApplicable( + (TabGroupModelFilter) + tabModelSelectorReturn + .getTabModelFilterProvider() + .getCurrentTabModelFilter()); + }); + } else { + PostTask.postTask(TaskTraits.UI_DEFAULT, TabGroupColorUtils::clearTabGroupColorInfo); + } + mInactivityTracker = new ChromeInactivityTracker( ChromePreferenceKeys.TABBED_ACTIVITY_LAST_BACKGROUNDED_TIME_MS_PREF); @@ -3586,6 +3614,11 @@ if (mHubProvider != null) mHubProvider.destroy(); + if (mTabGroupVisualDataManager != null) { + mTabGroupVisualDataManager.destroy(); + mTabGroupVisualDataManager = null; + } + super.onDestroyInternal(); }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/accessibility/settings/AccessibilitySettings.java b/chrome/android/java/src/org/chromium/chrome/browser/accessibility/settings/AccessibilitySettings.java index 6625699..67621d75 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/accessibility/settings/AccessibilitySettings.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/accessibility/settings/AccessibilitySettings.java
@@ -14,9 +14,9 @@ import org.chromium.base.ContextUtils; import org.chromium.chrome.R; import org.chromium.chrome.browser.image_descriptions.ImageDescriptionsController; +import org.chromium.chrome.browser.preferences.Pref; import org.chromium.chrome.browser.settings.SettingsLauncherImpl; import org.chromium.components.browser_ui.accessibility.AccessibilitySettingsDelegate; -import org.chromium.components.browser_ui.accessibility.AccessibilitySettingsDelegate.BooleanPreferenceDelegate; import org.chromium.components.browser_ui.accessibility.FontSizePrefs; import org.chromium.components.browser_ui.accessibility.FontSizePrefs.FontSizePrefsObserver; import org.chromium.components.browser_ui.accessibility.PageZoomPreference; @@ -28,6 +28,7 @@ import org.chromium.components.browser_ui.site_settings.AllSiteSettings; import org.chromium.components.browser_ui.site_settings.SingleCategorySettings; import org.chromium.components.browser_ui.site_settings.SiteSettingsCategory; +import org.chromium.components.prefs.PrefService; import org.chromium.content_public.browser.ContentFeatureList; import org.chromium.content_public.browser.ContentFeatureMap; @@ -52,8 +53,8 @@ private ChromeSwitchPreference mForceEnableZoomPref; private boolean mRecordFontSizeChangeOnStop; private AccessibilitySettingsDelegate mDelegate; - private BooleanPreferenceDelegate mReaderForAccessibilityDelegate; private double mPageZoomLatestDefaultZoomPrefValue; + private PrefService mPrefService; private FontSizePrefs mFontSizePrefs; private FontSizePrefsObserver mFontSizePrefsObserver = @@ -71,6 +72,10 @@ } }; + public void setPrefService(PrefService prefService) { + mPrefService = prefService; + } + public void setDelegate(AccessibilitySettingsDelegate delegate) { mDelegate = delegate; mFontSizePrefs = FontSizePrefs.getInstance(delegate.getBrowserContextHandle()); @@ -134,13 +139,9 @@ ChromeSwitchPreference readerForAccessibilityPref = (ChromeSwitchPreference) findPreference(PREF_READER_FOR_ACCESSIBILITY); - mReaderForAccessibilityDelegate = mDelegate.getReaderForAccessibilityDelegate(); - if (mReaderForAccessibilityDelegate != null) { - readerForAccessibilityPref.setChecked(mReaderForAccessibilityDelegate.isEnabled()); - readerForAccessibilityPref.setOnPreferenceChangeListener(this); - } else { - getPreferenceScreen().removePreference(readerForAccessibilityPref); - } + readerForAccessibilityPref.setChecked( + mPrefService.getBoolean(Pref.READER_FOR_ACCESSIBILITY)); + readerForAccessibilityPref.setOnPreferenceChangeListener(this); Preference captions = findPreference(PREF_CAPTIONS); captions.setOnPreferenceClickListener( @@ -214,9 +215,7 @@ } else if (PREF_FORCE_ENABLE_ZOOM.equals(preference.getKey())) { mFontSizePrefs.setForceEnableZoomFromUser((Boolean) newValue); } else if (PREF_READER_FOR_ACCESSIBILITY.equals(preference.getKey())) { - if (mReaderForAccessibilityDelegate != null) { - mReaderForAccessibilityDelegate.setEnabled((Boolean) newValue); - } + mPrefService.setBoolean(Pref.READER_FOR_ACCESSIBILITY, (Boolean) newValue); } else if (PREF_PAGE_ZOOM_DEFAULT_ZOOM.equals(preference.getKey())) { mPageZoomLatestDefaultZoomPrefValue = PageZoomUtils.convertSeekBarValueToZoomLevel((Integer) newValue);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/accessibility/settings/ChromeAccessibilitySettingsDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/accessibility/settings/ChromeAccessibilitySettingsDelegate.java index 29c22e3..69cfc1e 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/accessibility/settings/ChromeAccessibilitySettingsDelegate.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/accessibility/settings/ChromeAccessibilitySettingsDelegate.java
@@ -12,24 +12,6 @@ /** The Chrome implementation of AccessibilitySettingsDelegate. */ public class ChromeAccessibilitySettingsDelegate implements AccessibilitySettingsDelegate { - private static class ReaderForAccessibilityDelegate implements BooleanPreferenceDelegate { - private final Profile mProfile; - - ReaderForAccessibilityDelegate(Profile profile) { - mProfile = profile; - } - - @Override - public boolean isEnabled() { - return UserPrefs.get(mProfile).getBoolean(Pref.READER_FOR_ACCESSIBILITY); - } - - @Override - public void setEnabled(boolean value) { - UserPrefs.get(mProfile).setBoolean(Pref.READER_FOR_ACCESSIBILITY, (Boolean) value); - } - } - private static class TextSizeContrastAccessibilityDelegate implements IntegerPreferenceDelegate { private final BrowserContextHandle mBrowserContextHandle; @@ -67,11 +49,6 @@ } @Override - public BooleanPreferenceDelegate getReaderForAccessibilityDelegate() { - return new ReaderForAccessibilityDelegate(mProfile); - } - - @Override public IntegerPreferenceDelegate getTextSizeContrastAccessibilityDelegate() { return new TextSizeContrastAccessibilityDelegate(getBrowserContextHandle()); }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browsing_data/ClearBrowsingDataFragment.java b/chrome/android/java/src/org/chromium/chrome/browser/browsing_data/ClearBrowsingDataFragment.java index 504d940..51689cb 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/browsing_data/ClearBrowsingDataFragment.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/browsing_data/ClearBrowsingDataFragment.java
@@ -30,6 +30,7 @@ import androidx.preference.Preference; import androidx.preference.PreferenceFragmentCompat; +import org.chromium.base.ApplicationStatus; import org.chromium.base.Callback; import org.chromium.base.CollectionUtil; import org.chromium.base.metrics.RecordHistogram; @@ -43,9 +44,12 @@ import org.chromium.chrome.browser.multiwindow.MultiWindowUtils; import org.chromium.chrome.browser.preferences.Pref; import org.chromium.chrome.browser.profiles.Profile; +import org.chromium.chrome.browser.quick_delete.QuickDeleteController; import org.chromium.chrome.browser.settings.ProfileDependentSetting; import org.chromium.chrome.browser.signin.services.IdentityServicesProvider; import org.chromium.chrome.browser.signin.services.SigninManager; +import org.chromium.chrome.browser.ui.messages.snackbar.Snackbar; +import org.chromium.chrome.browser.ui.messages.snackbar.SnackbarManager; import org.chromium.chrome.browser.ui.signin.SignOutDialogCoordinator; import org.chromium.components.browser_ui.settings.ClickableSpansTextMessagePreference; import org.chromium.components.browser_ui.settings.CustomDividerFragment; @@ -234,6 +238,9 @@ // important domains from being cleared. private ConfirmImportantSitesDialogFragment mConfirmImportantSitesDialog; + private @TimePeriod int mLastSelectedTimePeriod; + private boolean mShouldShowSnackbar; + /** * @return All available {@link DialogOption} entries. */ @@ -404,21 +411,21 @@ Object spinnerSelection = ((SpinnerPreference) findPreference(PREF_TIME_RANGE)).getSelectedOption(); - @TimePeriod int timePeriod = ((TimePeriodSpinnerOption) spinnerSelection).getTimePeriod(); + mLastSelectedTimePeriod = ((TimePeriodSpinnerOption) spinnerSelection).getTimePeriod(); int[] dataTypesArray = CollectionUtil.integerCollectionToIntArray(dataTypes); if (excludedDomains != null && excludedDomains.length != 0) { BrowsingDataBridge.getForProfile(mProfile) .clearBrowsingDataExcludingDomains( this, dataTypesArray, - timePeriod, + mLastSelectedTimePeriod, excludedDomains, excludedDomainReasons, ignoredDomains, ignoredDomainReasons); } else { BrowsingDataBridge.getForProfile(mProfile) - .clearBrowsingData(this, dataTypesArray, timePeriod); + .clearBrowsingData(this, dataTypesArray, mLastSelectedTimePeriod); } // Clear all reported entities. @@ -457,6 +464,7 @@ @Override public void onBrowsingDataCleared() { if (getActivity() == null) return; + mShouldShowSnackbar = QuickDeleteController.isQuickDeleteFollowupEnabled(); // If the user deleted their browsing history, the dialog about other forms of history // is enabled, and it has never been shown before, show it. Note that opening a new @@ -698,6 +706,9 @@ item.destroy(); } mSigninManager.removeSignInStateObserver(this); + if (mShouldShowSnackbar) { + showSnackbar(); + } } // We either show the dialog, or modify the current one to display our messages. This avoids @@ -871,4 +882,47 @@ public void setHelpAndFeedbackLauncher(HelpAndFeedbackLauncher helpAndFeedbackLauncher) { mHelpAndFeedbackLauncher = helpAndFeedbackLauncher; } + + /** Get the last focused activity that has not been destroyed. */ + private Activity getLastFocusedActivity() { + if (ApplicationStatus.hasVisibleActivities()) { + return ApplicationStatus.getLastTrackedFocusedActivity(); + } else { + return null; + } + } + + public SnackbarManager getSnackbarManager() { + Activity activity = getLastFocusedActivity(); + if (activity instanceof SnackbarManager.SnackbarManageable) { + return ((SnackbarManager.SnackbarManageable) activity).getSnackbarManager(); + } + return null; + } + + /** A method to show the post-delete snack-bar confirmation. */ + private void showSnackbar() { + SnackbarManager snackbarManager = getSnackbarManager(); + if (snackbarManager == null) return; + + String snackbarMessage; + if (mLastSelectedTimePeriod == TimePeriod.ALL_TIME) { + snackbarMessage = + getActivity().getString(R.string.quick_delete_snackbar_all_time_message); + } else { + snackbarMessage = + getActivity() + .getString( + R.string.quick_delete_snackbar_message, + TimePeriodUtils.getTimePeriodString( + getActivity(), mLastSelectedTimePeriod)); + } + Snackbar snackbar = + Snackbar.make( + snackbarMessage, + /* controller= */ null, + Snackbar.TYPE_NOTIFICATION, + Snackbar.UMA_CLEAR_BROWSING_DATA); + snackbarManager.showSnackbar(snackbar); + } }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/HistorySyncFirstRunFragment.java b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/HistorySyncFirstRunFragment.java index 0e30d43..ca388b33 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/HistorySyncFirstRunFragment.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/HistorySyncFirstRunFragment.java
@@ -4,6 +4,7 @@ package org.chromium.chrome.browser.firstrun; +import android.content.res.Configuration; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; @@ -34,13 +35,7 @@ @Override public void onResume() { super.onResume(); - assert getPageDelegate().getProfileProviderSupplier().get() != null; - Profile profile = getPageDelegate().getProfileProviderSupplier().get().getOriginalProfile(); - mHistorySyncCoordinator = - new HistorySyncCoordinator( - getLayoutInflater(), this, profile, SigninAccessPoint.START_PAGE); - mFragmentView.removeAllViews(); - mFragmentView.addView(mHistorySyncCoordinator.getView()); + createCoordinatorAndAddToFragment(); } @Override @@ -52,6 +47,15 @@ } } + @Override + public void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + if (mHistorySyncCoordinator == null) { + return; + } + createCoordinatorAndAddToFragment(); + } + /** Implements {@link FirstRunFragment}. */ @Override public void setInitialA11yFocus() { @@ -69,4 +73,22 @@ mHistorySyncCoordinator.destroy(); mHistorySyncCoordinator = null; } + + @Override + public boolean canUseLandscapeLayout() { + return getPageDelegate().canUseLandscapeLayout(); + } + + private void createCoordinatorAndAddToFragment() { + if (mHistorySyncCoordinator != null) { + mHistorySyncCoordinator.destroy(); + } + assert getPageDelegate().getProfileProviderSupplier().get() != null; + Profile profile = getPageDelegate().getProfileProviderSupplier().get().getOriginalProfile(); + mHistorySyncCoordinator = + new HistorySyncCoordinator( + getLayoutInflater(), this, profile, SigninAccessPoint.START_PAGE); + mFragmentView.removeAllViews(); + mFragmentView.addView(mHistorySyncCoordinator.getView()); + } }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/settings/SettingsActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/settings/SettingsActivity.java index 5146dbe..aace464f 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/settings/SettingsActivity.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/settings/SettingsActivity.java
@@ -606,6 +606,7 @@ if (fragment instanceof AccessibilitySettings) { ((AccessibilitySettings) fragment) .setDelegate(new ChromeAccessibilitySettingsDelegate(mProfile)); + ((AccessibilitySettings) fragment).setPrefService(UserPrefs.get(mProfile)); } if (fragment instanceof PasswordSettings) { ((PasswordSettings) fragment).setBottomSheetController(mBottomSheetController);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/site_settings/ChromeSiteSettingsDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/site_settings/ChromeSiteSettingsDelegate.java index 8ff37af9..0f68819 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/site_settings/ChromeSiteSettingsDelegate.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/site_settings/ChromeSiteSettingsDelegate.java
@@ -46,8 +46,6 @@ import org.chromium.components.content_settings.ContentSettingsType; import org.chromium.components.embedder_support.util.Origin; import org.chromium.components.favicon.LargeIconBridge; -import org.chromium.components.permissions.PermissionsAndroidFeatureList; -import org.chromium.components.permissions.PermissionsAndroidFeatureMap; import org.chromium.components.prefs.PrefService; import org.chromium.components.user_prefs.UserPrefs; import org.chromium.content_public.browser.BrowserContextHandle; @@ -141,9 +139,6 @@ return ContentFeatureMap.isEnabled(ContentFeatures.FED_CM); case SiteSettingsCategory.Type.NFC: return ContentFeatureMap.isEnabled(ContentFeatureList.WEB_NFC); - case SiteSettingsCategory.Type.STORAGE_ACCESS: - return PermissionsAndroidFeatureMap.isEnabled( - PermissionsAndroidFeatureList.PERMISSION_STORAGE_ACCESS); case SiteSettingsCategory.Type.ZOOM: return ContentFeatureMap.isEnabled(ContentFeatureList.ACCESSIBILITY_PAGE_ZOOM) && ContentFeatureMap.isEnabled(
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java index f17bc86..b865847 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
@@ -99,6 +99,7 @@ import org.chromium.chrome.browser.price_tracking.PriceTrackingUtilities; import org.chromium.chrome.browser.privacy.settings.PrivacyPreferencesManagerImpl; import org.chromium.chrome.browser.profiles.Profile; +import org.chromium.chrome.browser.readaloud.ReadAloudController; import org.chromium.chrome.browser.search_engines.TemplateUrlServiceFactory; import org.chromium.chrome.browser.share.ShareDelegate; import org.chromium.chrome.browser.tab.SadTab; @@ -329,10 +330,7 @@ private final boolean mIsCustomTab; - @Nullable ObservableSupplier<String> mReadAloudReadabilitySupplier; - - private final Callback<String> mOnReadAloudReadabilitySuccess = - this::onReadAloudReadabilitySuccess; + private ReadAloudController mReadAloudController; private static class TabObscuringCallback implements Callback<Boolean> { private final TabObscuringHandler mTabObscuringHandler; @@ -558,7 +556,8 @@ Supplier<EphemeralTabCoordinator> ephemeralTabCoordinatorSupplier, boolean initializeWithIncognitoColors, @Nullable BackPressManager backPressManager, - @Nullable BooleanSupplier overviewIncognitoSupplier) { + @Nullable BooleanSupplier overviewIncognitoSupplier, + ObservableSupplier<ReadAloudController> readAloudControllerSupplier) { TraceEvent.begin("ToolbarManager.ToolbarManager"); mActivity = activity; mWindowAndroid = windowAndroid; @@ -1289,23 +1288,22 @@ } }; profileSupplier.addObserver(profileObserver); + readAloudControllerSupplier.addObserver( + readAloudController -> { + mReadAloudController = readAloudController; + if (readAloudController != null) { + readAloudController.addReadabilityUpdateListener( + this::onReadAloudReadabilityUpdated); + } + }); TraceEvent.end("ToolbarManager.ToolbarManager"); } // TODO(b/315204103): add tests - public void setReadAloudReadabilitySupplier( - ObservableSupplier<String> readAloudReadabilitySupplier) { - mReadAloudReadabilitySupplier = readAloudReadabilitySupplier; - mReadAloudReadabilitySupplier.addObserver(mOnReadAloudReadabilitySuccess); - } - - // TODO(b/315204103): add tests - private void onReadAloudReadabilitySuccess(String url) { - // Checks if ReadAloud is set as the customized button and the readable url matches the - // current tab + private void onReadAloudReadabilityUpdated() { + // Update the button if ReadAloud is set as the customized button. if (ChromeSharedPreferences.getInstance().readInt(ADAPTIVE_TOOLBAR_CUSTOMIZATION_SETTINGS) - == AdaptiveToolbarButtonVariant.READ_ALOUD - && mTabModelSelector.getCurrentTab().getUrl().getSpec().equals(url)) { + == AdaptiveToolbarButtonVariant.READ_ALOUD) { updateButtonStatus(); } } @@ -1997,9 +1995,10 @@ mStartSurfaceHeaderOffsetChangeListener = null; } - if (mReadAloudReadabilitySupplier != null) { - mReadAloudReadabilitySupplier.removeObserver(mOnReadAloudReadabilitySuccess); - mReadAloudReadabilitySupplier = null; + if (mReadAloudController != null) { + mReadAloudController.removeReadabilityUpdateListener( + this::onReadAloudReadabilityUpdated); + mReadAloudController = null; } mTabObscuringHandler.removeObserver(this);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ui/RootUiCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/ui/RootUiCoordinator.java index b6578e93..4b1698b 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ui/RootUiCoordinator.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ui/RootUiCoordinator.java
@@ -916,7 +916,6 @@ controller.maybeShowPlayer(); } }; - mToolbarManager.setReadAloudReadabilitySupplier(controller.getReadabilitySupplier()); if (mContextualSearchManagerSupplier.get() != null) { mContextualSearchManagerSupplier .get() @@ -1436,7 +1435,8 @@ mEphemeralTabCoordinatorSupplier, mInitializeUiWithIncognitoColors, mBackPressManager, - mOverviewIncognitoSupplier); + mOverviewIncognitoSupplier, + mReadAloudControllerSupplier); if (!mSupportsAppMenuSupplier.getAsBoolean()) { mToolbarManager.getToolbar().disableMenuButton(); }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/browsing_data/ClearBrowsingDataFragmentTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/browsing_data/ClearBrowsingDataFragmentTest.java index ee1d7da5..63b8710 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/browsing_data/ClearBrowsingDataFragmentTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/browsing_data/ClearBrowsingDataFragmentTest.java
@@ -68,6 +68,7 @@ import org.chromium.base.test.util.Features; import org.chromium.base.test.util.HistogramWatcher; import org.chromium.base.test.util.JniMocker; +import org.chromium.chrome.browser.ChromeTabbedActivity; import org.chromium.chrome.browser.browsing_data.BrowsingDataBridge.OnClearBrowsingDataListener; import org.chromium.chrome.browser.browsing_data.ClearBrowsingDataFragment.DialogOption; import org.chromium.chrome.browser.feedback.HelpAndFeedbackLauncher; @@ -79,6 +80,7 @@ import org.chromium.chrome.browser.settings.SettingsActivity; import org.chromium.chrome.browser.settings.SettingsActivityTestRule; import org.chromium.chrome.browser.signin.services.IdentityServicesProvider; +import org.chromium.chrome.browser.ui.messages.snackbar.SnackbarManager; import org.chromium.chrome.test.ChromeJUnit4ClassRunner; import org.chromium.chrome.test.ChromeTabbedActivityTestRule; import org.chromium.chrome.test.R; @@ -790,6 +792,32 @@ ChromeFeatureList.QUICK_DELETE_FOR_ANDROID, ChromeFeatureList.QUICK_DELETE_ANDROID_FOLLOWUP }) + public void testSnackbarShown_defaultTimePeriod_withQuickDeleteV2Enabled() throws Exception { + setDataTypesToClear(DialogOption.CLEAR_CACHE); + + final ClearBrowsingDataFragment preferences = + (ClearBrowsingDataFragment) startPreferences().getMainFragment(); + + TestThreadUtils.runOnUiThreadBlocking( + () -> { + clickClearButton(preferences); + }); + + waitForProgressToComplete(preferences); + mCallbackHelper.waitForFirst(); + + ChromeTabbedActivity activity = mActivityTestRule.getActivity(); + final String expectedSnackbarMessage = + activity.getResources().getString(R.string.quick_delete_snackbar_all_time_message); + waitForSnackbar(expectedSnackbarMessage); + } + + @Test + @MediumTest + @Features.EnableFeatures({ + ChromeFeatureList.QUICK_DELETE_FOR_ANDROID, + ChromeFeatureList.QUICK_DELETE_ANDROID_FOLLOWUP + }) public void testTabsCheckbox_withQuickDeleteV2Enabled() { ClearBrowsingDataFragment preferences = (ClearBrowsingDataFragment) startPreferences().getMainFragment(); @@ -799,6 +827,50 @@ assertNotNull(checkboxPreference); } + @Test + @MediumTest + @Features.EnableFeatures({ + ChromeFeatureList.QUICK_DELETE_FOR_ANDROID, + ChromeFeatureList.QUICK_DELETE_ANDROID_FOLLOWUP + }) + public void testSnackbarShown_changeTimePeriod_withQuickDeleteV2Enabled() throws Exception { + setDataTypesToClear(DialogOption.CLEAR_CACHE); + + final ClearBrowsingDataFragment preferences = + (ClearBrowsingDataFragment) startPreferences().getMainFragment(); + + TestThreadUtils.runOnUiThreadBlocking( + () -> { + changeTimePeriodTo(preferences, TimePeriod.LAST_HOUR); + clickClearButton(preferences); + }); + + waitForProgressToComplete(preferences); + mCallbackHelper.waitForFirst(); + + ChromeTabbedActivity activity = mActivityTestRule.getActivity(); + final String expectedSnackbarMessage = + activity.getString( + R.string.quick_delete_snackbar_message, + TimePeriodUtils.getTimePeriodString(activity, TimePeriod.LAST_HOUR)); + waitForSnackbar(expectedSnackbarMessage); + } + + /** Wait for the snackbar to show on the main activity post deletion. */ + private void waitForSnackbar(String expectedSnackbarMessage) { + ChromeTabbedActivity activity = mActivityTestRule.getActivity(); + CriteriaHelper.pollUiThread( + () -> { + SnackbarManager snackbarManager = activity.getSnackbarManager(); + Criteria.checkThat(snackbarManager.isShowing(), Matchers.is(true)); + TextView snackbarMessage = activity.findViewById(R.id.snackbar_message); + Criteria.checkThat(snackbarMessage, Matchers.notNullValue()); + Criteria.checkThat( + snackbarMessage.getText().toString(), + Matchers.is(expectedSnackbarMessage)); + }); + } + private void setDataTypesToClear(final Integer... typesToClear) { Set<Integer> typesToClearSet = new ArraySet<Integer>(Arrays.asList(typesToClear)); TestThreadUtils.runOnUiThreadBlocking(
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/firstrun/FirstRunActivitySigninAndSyncTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/firstrun/FirstRunActivitySigninAndSyncTest.java index b266e2a..63577805 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/firstrun/FirstRunActivitySigninAndSyncTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/firstrun/FirstRunActivitySigninAndSyncTest.java
@@ -75,6 +75,7 @@ import org.chromium.components.signin.base.AccountCapabilities; import org.chromium.components.signin.identitymanager.ConsentLevel; import org.chromium.components.signin.identitymanager.IdentityManager; +import org.chromium.components.signin.metrics.SigninAccessPoint; import org.chromium.components.signin.test.util.AccountCapabilitiesBuilder; import org.chromium.components.signin.test.util.FakeAccountManagerFacade; import org.chromium.content_public.browser.test.util.TestThreadUtils; @@ -180,6 +181,9 @@ @Restriction({DeviceRestriction.RESTRICTION_TYPE_NON_AUTO}) @Features.EnableFeatures(ChromeFeatureList.REPLACE_SYNC_PROMOS_WITH_SIGN_IN_PROMOS) public void continueButtonClickShowsHistorySyncPage() { + HistogramWatcher histogramWatcher = + HistogramWatcher.newSingleRecordWatcher( + "Signin.HistorySyncOptIn.Started", SigninAccessPoint.START_PAGE); mAccountManagerTestRule.addAccount(TEST_EMAIL); launchFirstRunActivityAndWaitForNativeInitialization(); waitUntilCurrentPageIs(SigninFirstRunFragment.class); @@ -187,6 +191,7 @@ clickButton(R.id.signin_fre_continue_button); + histogramWatcher.assertExpected(); waitUntilCurrentPageIs(HistorySyncFirstRunFragment.class); }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/site_settings/SingleWebsiteSettingsTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/site_settings/SingleWebsiteSettingsTest.java index 3c663e8..134e5ef 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/site_settings/SingleWebsiteSettingsTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/site_settings/SingleWebsiteSettingsTest.java
@@ -35,7 +35,6 @@ import org.chromium.base.test.util.Batch; import org.chromium.base.test.util.CommandLineFlags; import org.chromium.base.test.util.DisableIf; -import org.chromium.base.test.util.Features.EnableFeatures; import org.chromium.chrome.browser.flags.ChromeSwitches; import org.chromium.chrome.browser.profiles.ProfileManager; import org.chromium.chrome.browser.settings.SettingsActivity; @@ -52,7 +51,6 @@ import org.chromium.components.browser_ui.site_settings.WebsitePreferenceBridge; import org.chromium.components.content_settings.ContentSettingValues; import org.chromium.components.content_settings.ContentSettingsType; -import org.chromium.components.permissions.PermissionsAndroidFeatureList; import org.chromium.content_public.browser.test.util.TestThreadUtils; import org.chromium.url.GURL; @@ -202,7 +200,6 @@ @Test @SmallTest - @EnableFeatures(PermissionsAndroidFeatureList.PERMISSION_STORAGE_ACCESS) public void testStorageAccessPermission() { int type = ContentSettingsType.STORAGE_ACCESS; GURL example = new GURL("https://example.com");
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/site_settings/SiteSettingsTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/site_settings/SiteSettingsTest.java index a65efada..8410972 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/site_settings/SiteSettingsTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/site_settings/SiteSettingsTest.java
@@ -132,7 +132,6 @@ import org.chromium.components.content_settings.CookieControlsMode; import org.chromium.components.embedder_support.util.Origin; import org.chromium.components.location.LocationUtils; -import org.chromium.components.permissions.PermissionsAndroidFeatureList; import org.chromium.components.permissions.nfc.NfcSystemLevelSetting; import org.chromium.components.policy.test.annotations.Policies; import org.chromium.components.prefs.PrefService; @@ -1405,7 +1404,6 @@ @Test @SmallTest @Feature({"Preferences"}) - @EnableFeatures({PermissionsAndroidFeatureList.PERMISSION_STORAGE_ACCESS}) public void testOnlyExpectedPreferencesStorageAccess() { testExpectedPreferences( SiteSettingsCategory.Type.STORAGE_ACCESS, @@ -1416,7 +1414,6 @@ @Test @SmallTest @Feature({"Preferences"}) - @EnableFeatures({PermissionsAndroidFeatureList.PERMISSION_STORAGE_ACCESS}) public void testExpectedExceptionsStorageAccess() { createStorageAccessExceptions(); SiteSettingsTestUtils.startSiteSettingsCategory(SiteSettingsCategory.Type.STORAGE_ACCESS); @@ -1437,7 +1434,6 @@ @Test @SmallTest @Feature({"Preferences"}) - @EnableFeatures({PermissionsAndroidFeatureList.PERMISSION_STORAGE_ACCESS}) public void testResetExceptionGroupStorageAccess() { createStorageAccessExceptions(); SiteSettingsTestUtils.startSiteSettingsCategory(SiteSettingsCategory.Type.STORAGE_ACCESS); @@ -1481,7 +1477,6 @@ @Test @SmallTest @Feature({"Preferences"}) - @EnableFeatures({PermissionsAndroidFeatureList.PERMISSION_STORAGE_ACCESS}) public void testBlockExceptionGroupStorageAccess() { createStorageAccessExceptions(); SiteSettingsTestUtils.startSiteSettingsCategory(SiteSettingsCategory.Type.STORAGE_ACCESS); @@ -1523,7 +1518,6 @@ @Test @SmallTest @Feature({"Preferences"}) - @EnableFeatures({PermissionsAndroidFeatureList.PERMISSION_STORAGE_ACCESS}) public void testStorageAccessSubpage() { createStorageAccessExceptions(); final SettingsActivity settingsActivity = @@ -2632,7 +2626,6 @@ @Test @SmallTest @Feature({"RenderTest"}) - @EnableFeatures({PermissionsAndroidFeatureList.PERMISSION_STORAGE_ACCESS}) public void testRenderStorageAccessPage() throws Exception { createStorageAccessExceptions(); renderCategoryPage( @@ -2642,7 +2635,6 @@ @Test @SmallTest @Feature({"RenderTest"}) - @EnableFeatures({PermissionsAndroidFeatureList.PERMISSION_STORAGE_ACCESS}) public void testRenderStorageAccessSubpage() throws Exception { createStorageAccessExceptions(); final SettingsActivity settingsActivity =
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/site_settings/WebsitePermissionsFetcherTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/site_settings/WebsitePermissionsFetcherTest.java index 14c7125..28c274f 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/site_settings/WebsitePermissionsFetcherTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/site_settings/WebsitePermissionsFetcherTest.java
@@ -33,7 +33,6 @@ import org.chromium.base.test.util.CallbackHelper; import org.chromium.base.test.util.CommandLineFlags; import org.chromium.base.test.util.DisabledTest; -import org.chromium.base.test.util.Features.EnableFeatures; import org.chromium.chrome.browser.browsing_data.BrowsingDataBridge; import org.chromium.chrome.browser.browsing_data.BrowsingDataType; import org.chromium.chrome.browser.browsing_data.TimePeriod; @@ -59,7 +58,6 @@ import org.chromium.components.content_settings.ContentSettingValues; import org.chromium.components.content_settings.ContentSettingsType; import org.chromium.components.content_settings.SessionModel; -import org.chromium.components.permissions.PermissionsAndroidFeatureList; import org.chromium.content_public.browser.BrowserContextHandle; import org.chromium.content_public.browser.test.util.TestThreadUtils; @@ -1471,7 +1469,6 @@ @Test @SmallTest - @EnableFeatures({PermissionsAndroidFeatureList.PERMISSION_STORAGE_ACCESS}) public void testIncognitoFetching() throws TimeoutException { WebsitePermissionsFetcher fetcher = new WebsitePermissionsFetcher(UNUSED_BROWSER_CONTEXT_HANDLE); @@ -1513,7 +1510,6 @@ @Test @SmallTest - @EnableFeatures({PermissionsAndroidFeatureList.PERMISSION_STORAGE_ACCESS}) public void testFetchAllSites() { WebsitePermissionsFetcher fetcher = new WebsitePermissionsFetcher(UNUSED_BROWSER_CONTEXT_HANDLE); @@ -1612,7 +1608,6 @@ @Test @SmallTest - @EnableFeatures({PermissionsAndroidFeatureList.PERMISSION_STORAGE_ACCESS}) @UseMethodParameter(EmbargoedParams.class) public void testFetchPreferencesForCategoryEmbeddedPermissionTypes(boolean isEmbargoed) { WebsitePermissionsFetcher fetcher =
diff --git a/chrome/android/modules/chrome_feature_module_tmpl.gni b/chrome/android/modules/chrome_feature_module_tmpl.gni index a7e92b7c..d14a8f4 100644 --- a/chrome/android/modules/chrome_feature_module_tmpl.gni +++ b/chrome/android/modules/chrome_feature_module_tmpl.gni
@@ -152,7 +152,6 @@ # Adds unwind table asset to the chrome apk for the given library target. This # is not part of generic apk assets target since it depends on the main shared # library of the apk, to extract unwind tables. - asset_deps = [] if (defined(_module_desc.include_unwind_assets) && _module_desc.include_unwind_assets) { _needs_32bit_lib = @@ -160,14 +159,11 @@ if (_needs_32bit_lib) { if (_is_monochrome_or_trichrome) { - asset_deps += [ "//chrome/android:libmonochrome_unwind_table_assets" ] + deps += [ "//chrome/android:libmonochrome_unwind_table_assets" ] } else { - asset_deps += [ "//chrome/android:libchrome_unwind_table_assets" ] + deps += [ "//chrome/android:libchrome_unwind_table_assets" ] } } } - if (defined(invoker.asset_deps)) { - asset_deps += invoker.asset_deps - } } }
diff --git a/chrome/app/helper-Info.plist b/chrome/app/helper-Info.plist index 6f8600e3d..a8c71a54 100644 --- a/chrome/app/helper-Info.plist +++ b/chrome/app/helper-Info.plist
@@ -29,6 +29,8 @@ <string>${CHROMIUM_MIN_SYSTEM_VERSION}</string> <key>LSUIElement</key> <string>1</string> + <key>NSCameraReactionEffectGesturesEnabledDefault</key> + <false/> <key>NSSupportsAutomaticGraphicsSwitching</key> <true/> </dict>
diff --git a/chrome/app/os_settings_strings.grdp b/chrome/app/os_settings_strings.grdp index cf60ed32..f2f650e1 100644 --- a/chrome/app/os_settings_strings.grdp +++ b/chrome/app/os_settings_strings.grdp
@@ -5588,6 +5588,9 @@ <message name="IDS_SETTINGS_DISPLAY_NIGHT_LIGHT_TEMP_SLIDER_MIN_LABEL" desc="In Device Settings > Displays, label of the minimum value settable by the color temperature slider."> Cooler </message> + <message name="IDS_SETTINGS_DISPLAY_SHINY_PERFORMANCE_LABEL" desc="In Device Settings > Displays, label of the toggle of enabling Shiny Performance." translateable="false"> + Shiny Performance + </message> <message name="IDS_SETTINGS_DISPLAY_UNIFIED_DESKTOP" desc="In Device Settings > Displays, the label for the control for the unified desktop feature."> Allow windows to span displays </message>
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn index 5a1fe61..5cdf70fb 100644 --- a/chrome/browser/BUILD.gn +++ b/chrome/browser/BUILD.gn
@@ -1864,8 +1864,6 @@ "visibility_timer_tab_helper.h", "vr/vr_tab_helper.cc", "vr/vr_tab_helper.h", - "web_data_service_factory.cc", - "web_data_service_factory.h", "webapps/chrome_webapps_client.cc", "webapps/chrome_webapps_client.h", "webapps/web_app_offline.cc", @@ -1874,6 +1872,8 @@ "webauthn/webauthn_metrics_util.h", "webauthn/webauthn_pref_names.cc", "webauthn/webauthn_pref_names.h", + "webdata_services/web_data_service_factory.cc", + "webdata_services/web_data_service_factory.h", "webid/federated_identity_account_keyed_permission_context.cc", "webid/federated_identity_account_keyed_permission_context.h", "webid/federated_identity_api_permission_context.cc", @@ -2241,6 +2241,7 @@ "//components/history_clusters/core", "//components/history_clusters/history_clusters_internals/webui", "//components/history_clusters/history_clusters_internals/webui:constants", + "//components/history_embeddings", "//components/infobars/content", "//components/infobars/core", "//components/invalidation/impl", @@ -4657,6 +4658,7 @@ "//ui/webui/resources/cr_components/app_management:mojo_bindings", "//ui/webui/resources/cr_components/help_bubble:mojo_bindings", "//ui/webui/resources/cr_components/history_clusters:mojo_bindings", + "//ui/webui/resources/cr_components/history_embeddings:mojo_bindings", ] public_deps += [ "//chrome/common:buildflags",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc index 0bcc649b..375169fa 100644 --- a/chrome/browser/about_flags.cc +++ b/chrome/browser/about_flags.cc
@@ -10139,12 +10139,6 @@ "WebAuthenticationAndroidCredMan")}, #endif // BUILDFLAG(IS_ANDROID) - {"permission-storage-access-api", - flag_descriptions::kPermissionStorageAccessAPIName, - flag_descriptions::kPermissionStorageAccessAPIDescription, - kOsDesktop | kOsAndroid, - FEATURE_VALUE_TYPE(permissions::features::kPermissionStorageAccessAPI)}, - #if BUILDFLAG(IS_ANDROID) {"android-extended-keyboard-shortcuts", flag_descriptions::kAndroidExtendedKeyboardShortcutsName,
diff --git a/chrome/browser/ash/DEPS b/chrome/browser/ash/DEPS index 52895cf..ea251c4 100644 --- a/chrome/browser/ash/DEPS +++ b/chrome/browser/ash/DEPS
@@ -516,6 +516,7 @@ "+chrome/browser/web_applications/web_app_id_constants.h", "+chrome/browser/web_applications/web_app_install_finalizer.h", "+chrome/browser/web_applications/web_app_install_info.h", + "+chrome/browser/web_applications/web_app_install_params.h", "+chrome/browser/web_applications/web_app_install_utils.h", "+chrome/browser/web_applications/web_app_launch_queue.h", "+chrome/browser/web_applications/web_app_provider.h",
diff --git a/chrome/browser/ash/app_list/app_list_client_impl.cc b/chrome/browser/ash/app_list/app_list_client_impl.cc index d6bf501c1..a8175bf 100644 --- a/chrome/browser/ash/app_list/app_list_client_impl.cc +++ b/chrome/browser/ash/app_list/app_list_client_impl.cc
@@ -155,24 +155,6 @@ auto* user_manager = user_manager::UserManager::Get(); user_manager->RemoveSessionStateObserver(this); - // We assume that the current user is new if `state_for_new_user_` has value. - if (state_for_new_user_.has_value() && - !state_for_new_user_->showing_recorded) { - DCHECK(user_manager->IsCurrentUserNew()); - - // Prefer the function to the macro because the usage data is recorded no - // more than once per second. - if (display::Screen::GetScreen()->InTabletMode()) { - base::UmaHistogramEnumeration( - "Apps.AppListUsageByNewUsers.TabletMode", - AppListUsageStateByNewUsers::kNotUsedBeforeDestruction); - } else { - base::UmaHistogramEnumeration( - "Apps.AppListUsageByNewUsers.ClamshellMode", - AppListUsageStateByNewUsers::kNotUsedBeforeDestruction); - } - } - session_manager::SessionManager::Get()->RemoveObserver(this); DCHECK_EQ(this, g_app_list_client_instance); @@ -413,24 +395,6 @@ RecordViewShown(); } else if (current_model_updater_) { current_model_updater_->OnAppListHidden(); - - // Record whether user took action first time they opened the launcher. - // Note that this is recorded only on first user session (otherwise - // `state_for_new_user_` will not be set). - if (state_for_new_user_ && state_for_new_user_->showing_recorded && - !state_for_new_user_->first_open_success_recorded) { - state_for_new_user_->first_open_success_recorded = true; - - if (state_for_new_user_->shown_in_tablet_mode) { - base::UmaHistogramBoolean( - "Apps.AppList.SuccessfulFirstUsageByNewUsers.TabletMode", - state_for_new_user_->action_recorded); - } else { - base::UmaHistogramBoolean( - "Apps.AppList.SuccessfulFirstUsageByNewUsers.ClamshellMode", - state_for_new_user_->action_recorded); - } - } // If the user started search, record no action if a result open event has // not been yet recorded. if (state_for_new_user_ && state_for_new_user_->started_search && @@ -465,19 +429,6 @@ // be both new. It should not happen in the real world. state_for_new_user_ = StateForNewUser(); } else if (state_for_new_user_) { - if (!state_for_new_user_->showing_recorded) { - // We assume that the previous user before switching was new if - // `state_for_new_user_` is not null. - if (display::Screen::GetScreen()->InTabletMode()) { - base::UmaHistogramEnumeration( - "Apps.AppListUsageByNewUsers.TabletMode", - AppListUsageStateByNewUsers::kNotUsedBeforeSwitchingAccounts); - } else { - base::UmaHistogramEnumeration( - "Apps.AppListUsageByNewUsers.ClamshellMode", - AppListUsageStateByNewUsers::kNotUsedBeforeSwitchingAccounts); - } - } state_for_new_user_.reset(); } @@ -811,9 +762,6 @@ "TabletMode", /*sample=*/opening_duration, kTimeMetricsMin, kTimeMetricsMax, kTimeMetricsBucketCount); - - base::UmaHistogramEnumeration("Apps.AppListUsageByNewUsers.TabletMode", - AppListUsageStateByNewUsers::kUsed); } else { UMA_HISTOGRAM_CUSTOM_TIMES( /*name=*/ @@ -822,9 +770,6 @@ "ClamshellMode", /*sample=*/opening_duration, kTimeMetricsMin, kTimeMetricsMax, kTimeMetricsBucketCount); - - base::UmaHistogramEnumeration("Apps.AppListUsageByNewUsers.ClamshellMode", - AppListUsageStateByNewUsers::kUsed); } } }
diff --git a/chrome/browser/ash/app_list/app_list_client_impl.h b/chrome/browser/ash/app_list/app_list_client_impl.h index d1af99e..2511db8 100644 --- a/chrome/browser/ash/app_list/app_list_client_impl.h +++ b/chrome/browser/ash/app_list/app_list_client_impl.h
@@ -169,10 +169,6 @@ // Indicates whether any launcher action has been recorded. bool action_recorded = false; - // Indicates whether the metric to track whether the user took action when - // the launcher was first shown was recorded. - bool first_open_success_recorded = false; - // Whether the user entered a query into the search box. bool started_search = false; // Whether the result that the user launched during their first search
diff --git a/chrome/browser/ash/app_list/app_list_client_impl_browsertest.cc b/chrome/browser/ash/app_list/app_list_client_impl_browsertest.cc index 497525cc..f545d6f 100644 --- a/chrome/browser/ash/app_list/app_list_client_impl_browsertest.cc +++ b/chrome/browser/ash/app_list/app_list_client_impl_browsertest.cc
@@ -1047,10 +1047,6 @@ "Apps.TimeDurationBetweenNewUserSessionActivationAndFirstLauncherOpening." "ClamshellMode", 1); - tester.ExpectBucketCount( - "Apps.AppListUsageByNewUsers.ClamshellMode", - static_cast<int>(AppListClientImpl::AppListUsageStateByNewUsers::kUsed), - 1); } // The duration between OOBE and the first launcher showing should not be @@ -1063,11 +1059,6 @@ // Verify that the launcher usage state is recorded when switching accounts. base::HistogramTester tester; AddUser(registered_user_id_); - tester.ExpectBucketCount( - "Apps.AppListUsageByNewUsers.ClamshellMode", - static_cast<int>(AppListClientImpl::AppListUsageStateByNewUsers:: - kNotUsedBeforeSwitchingAccounts), - 1); // Verify that the metric is not recorded. ShowAppListAndVerify(); @@ -1075,10 +1066,6 @@ "Apps.TimeDurationBetweenNewUserSessionActivationAndFirstLauncherOpening." "ClamshellMode", 0); - tester.ExpectBucketCount( - "Apps.AppListUsageByNewUsers.ClamshellMode", - static_cast<int>(AppListClientImpl::AppListUsageStateByNewUsers::kUsed), - 0); } // The duration between OOBE and the first launcher showing should not be @@ -1099,44 +1086,4 @@ "Apps.TimeDurationBetweenNewUserSessionActivationAndFirstLauncherOpening." "ClamshellMode", 0); - tester.ExpectBucketCount( - "Apps.AppListUsageByNewUsers.ClamshellMode", - static_cast<int>(AppListClientImpl::AppListUsageStateByNewUsers::kUsed), - 0); -} - -class DurationBetweenSeesionActivationAndFirstLauncherShowingShutdownTest - : public DurationBetweenSeesionActivationAndFirstLauncherShowingBrowserTest { - public: - DurationBetweenSeesionActivationAndFirstLauncherShowingShutdownTest() = - default; - ~DurationBetweenSeesionActivationAndFirstLauncherShowingShutdownTest() - override = default; - - protected: - // DurationBetweenSeesionActivationAndFirstLauncherShowingBrowserTest: - void SetUpOnMainThread() override { - DurationBetweenSeesionActivationAndFirstLauncherShowingBrowserTest:: - SetUpOnMainThread(); - histogram_tester_ = std::make_unique<base::HistogramTester>(); - } - - void TearDown() override { - histogram_tester_->ExpectBucketCount( - "Apps.AppListUsageByNewUsers.ClamshellMode", - static_cast<int>(AppListClientImpl::AppListUsageStateByNewUsers:: - kNotUsedBeforeDestruction), - 1); - DurationBetweenSeesionActivationAndFirstLauncherShowingBrowserTest:: - TearDown(); - } - - std::unique_ptr<base::HistogramTester> histogram_tester_; -}; - -// Verify that the launcher usage state is recorded when shutting down. -IN_PROC_BROWSER_TEST_F( - DurationBetweenSeesionActivationAndFirstLauncherShowingShutdownTest, - NotUseLauncherBeforeShuttingDown) { - // Do nothing. Verify the histogram after the browser process is terminated. }
diff --git a/chrome/browser/ash/apps/apk_web_app_installer.cc b/chrome/browser/ash/apps/apk_web_app_installer.cc index 421ed861..ebf779a 100644 --- a/chrome/browser/ash/apps/apk_web_app_installer.cc +++ b/chrome/browser/ash/apps/apk_web_app_installer.cc
@@ -15,6 +15,7 @@ #include "chrome/browser/profiles/profile.h" #include "chrome/browser/web_applications/mojom/user_display_mode.mojom.h" #include "chrome/browser/web_applications/web_app_command_scheduler.h" +#include "chrome/browser/web_applications/web_app_install_params.h" #include "chrome/browser/web_applications/web_app_install_utils.h" #include "chrome/browser/web_applications/web_app_provider.h" #include "chrome/browser/web_applications/web_app_utils.h" @@ -209,12 +210,13 @@ // Doesn't overwrite already existing web app with manifest fields from the // apk. GURL start_url = web_app_install_info_->start_url; - provider->scheduler().InstallFromInfo( + provider->scheduler().InstallFromInfoWithParams( std::move(web_app_install_info_), /*overwrite_existing_manifest_fields=*/false, webapps::WebappInstallSource::ARC, base::BindOnce(&ApkWebAppInstaller::OnWebAppCreated, - base::Unretained(this), std::move(start_url))); + base::Unretained(this), std::move(start_url)), + web_app::WebAppInstallParams()); } }
diff --git a/chrome/browser/ash/display/refresh_rate_controller.cc b/chrome/browser/ash/display/refresh_rate_controller.cc index 4e3ff9a..665f055e 100644 --- a/chrome/browser/ash/display/refresh_rate_controller.cc +++ b/chrome/browser/ash/display/refresh_rate_controller.cc
@@ -92,10 +92,16 @@ return; } - // Enable VRR if battery saver is inactive. + // Enable VRR on the borealis-hosting display if battery saver is inactive. const bool battery_saver_mode_enabled = power_status_->IsBatterySaverActive(); - display_configurator_->SetVrrEnabled(!battery_saver_mode_enabled && - borealis_window_observer_.IsObserving()); + if (borealis_window_observer_.IsObserving() && !battery_saver_mode_enabled) { + display_configurator_->SetVrrEnabled( + {display::Screen::GetScreen() + ->GetDisplayNearestWindow(borealis_window_observer_.GetSource()) + .id()}); + } else { + display_configurator_->SetVrrEnabled({}); + } } display::RefreshRateThrottleState
diff --git a/chrome/browser/ash/display/refresh_rate_controller.h b/chrome/browser/ash/display/refresh_rate_controller.h index fe367171..82b6a88 100644 --- a/chrome/browser/ash/display/refresh_rate_controller.h +++ b/chrome/browser/ash/display/refresh_rate_controller.h
@@ -35,8 +35,7 @@ bool force_throttle = false); RefreshRateController(const RefreshRateController&) = delete; - RefreshRateController& operator=( - const RefreshRateController&) = delete; + RefreshRateController& operator=(const RefreshRateController&) = delete; ~RefreshRateController() override;
diff --git a/chrome/browser/ash/display/refresh_rate_controller_unittest.cc b/chrome/browser/ash/display/refresh_rate_controller_unittest.cc index 85b052d1..aa7b9d8 100644 --- a/chrome/browser/ash/display/refresh_rate_controller_unittest.cc +++ b/chrome/browser/ash/display/refresh_rate_controller_unittest.cc
@@ -90,10 +90,9 @@ ::features::kEnableVariableRefreshRate}, /*disabled_features=*/{}); } - RefreshRateControllerTest(const RefreshRateControllerTest&) = + RefreshRateControllerTest(const RefreshRateControllerTest&) = delete; + RefreshRateControllerTest& operator=(const RefreshRateControllerTest&) = delete; - RefreshRateControllerTest& operator=( - const RefreshRateControllerTest&) = delete; ~RefreshRateControllerTest() override = default; void SetUp() override { @@ -335,8 +334,8 @@ TEST_F(RefreshRateControllerTest, ThrottlingUnaffectedForBorealisOnExternalDisplay) { - constexpr int64_t internal_id = 12345; const int64_t external_id = GetPrimaryDisplay().id(); + const int64_t internal_id = external_id + 1; std::vector<std::unique_ptr<DisplaySnapshot>> snapshots; snapshots.push_back(BuildDualRefreshPanelSnapshot( internal_id, display::DISPLAY_CONNECTION_TYPE_INTERNAL)); @@ -549,32 +548,44 @@ } TEST_F(RefreshRateControllerTest, ShouldEnableVrrForBorealis) { - const int64_t display_id = GetPrimaryDisplay().id(); + const int64_t internal_id = GetPrimaryDisplay().id(); + const int64_t external_id = internal_id + 1; std::vector<std::unique_ptr<DisplaySnapshot>> snapshots; snapshots.push_back(BuildVrrPanelSnapshot( - display_id, display::DISPLAY_CONNECTION_TYPE_INTERNAL)); + internal_id, display::DISPLAY_CONNECTION_TYPE_INTERNAL)); + snapshots.push_back(BuildVrrPanelSnapshot( + external_id, display::DISPLAY_CONNECTION_TYPE_HDMI)); SetUpDisplays(std::move(snapshots)); - ScopedSetInternalDisplayIds set_internal(display_id); + ScopedSetInternalDisplayIds set_internal(internal_id); std::unique_ptr<aura::Window> window( CreateTestWindowInShellWithBounds(GetPrimaryDisplay().work_area())); // Expect VRR to be initially disabled. { - const DisplaySnapshot* snapshot = GetDisplaySnapshot(display_id); - ASSERT_NE(snapshot, nullptr); - ASSERT_TRUE(snapshot->IsVrrCapable()); - EXPECT_FALSE(snapshot->IsVrrEnabled()); + const DisplaySnapshot* internal_snapshot = GetDisplaySnapshot(internal_id); + ASSERT_NE(internal_snapshot, nullptr); + ASSERT_TRUE(internal_snapshot->IsVrrCapable()); + EXPECT_FALSE(internal_snapshot->IsVrrEnabled()); + + const DisplaySnapshot* external_snapshot = GetDisplaySnapshot(external_id); + ASSERT_NE(external_snapshot, nullptr); + ASSERT_TRUE(external_snapshot->IsVrrCapable()); + EXPECT_FALSE(external_snapshot->IsVrrEnabled()); } // Set the game mode to indicate the user is gaming. game_mode_controller_->NotifySetGameMode(GameMode::BOREALIS, ash::WindowState::Get(window.get())); - // Expect the new state to have VRR enabled. + // Expect the new state to have VRR enabled on the Borealis display only. { - const DisplaySnapshot* snapshot = GetDisplaySnapshot(display_id); - ASSERT_NE(snapshot, nullptr); - EXPECT_TRUE(snapshot->IsVrrEnabled()); + const DisplaySnapshot* internal_snapshot = GetDisplaySnapshot(internal_id); + ASSERT_NE(internal_snapshot, nullptr); + EXPECT_TRUE(internal_snapshot->IsVrrEnabled()); + + const DisplaySnapshot* external_snapshot = GetDisplaySnapshot(external_id); + ASSERT_NE(external_snapshot, nullptr); + EXPECT_FALSE(external_snapshot->IsVrrEnabled()); } // Reset the game mode. @@ -583,9 +594,13 @@ // Expect the new state to have VRR disabled. { - const DisplaySnapshot* snapshot = GetDisplaySnapshot(display_id); - ASSERT_NE(snapshot, nullptr); - EXPECT_FALSE(snapshot->IsVrrEnabled()); + const DisplaySnapshot* internal_snapshot = GetDisplaySnapshot(internal_id); + ASSERT_NE(internal_snapshot, nullptr); + EXPECT_FALSE(internal_snapshot->IsVrrEnabled()); + + const DisplaySnapshot* external_snapshot = GetDisplaySnapshot(external_id); + ASSERT_NE(external_snapshot, nullptr); + EXPECT_FALSE(external_snapshot->IsVrrEnabled()); } game_mode_controller_->NotifySetGameMode(GameMode::OFF,
diff --git a/chrome/browser/ash/growth/install_web_app_action_performer.cc b/chrome/browser/ash/growth/install_web_app_action_performer.cc index 367188e..f97157f 100644 --- a/chrome/browser/ash/growth/install_web_app_action_performer.cc +++ b/chrome/browser/ash/growth/install_web_app_action_performer.cc
@@ -98,10 +98,11 @@ void InstallWebAppImpl(web_app::WebAppProvider& provider, std::unique_ptr<web_app::WebAppInstallInfo> web_app_info, growth::ActionPerformer::Callback callback) { - provider.scheduler().InstallFromInfo( + provider.scheduler().InstallFromInfoWithParams( std::move(web_app_info), /* overwrite_existing_manifest_fields= */ true, webapps::WebappInstallSource::EXTERNAL_DEFAULT, - base::BindOnce(&InstallWebAppResult, std::move(callback))); + base::BindOnce(&InstallWebAppResult, std::move(callback)), + web_app::WebAppInstallParams()); } web_app::WebAppProvider* GetWebAppProvider() {
diff --git a/chrome/browser/ash/login/screens/management_transition_screen_browsertest.cc b/chrome/browser/ash/login/screens/management_transition_screen_browsertest.cc index eccbf90..c5b48f35 100644 --- a/chrome/browser/ash/login/screens/management_transition_screen_browsertest.cc +++ b/chrome/browser/ash/login/screens/management_transition_screen_browsertest.cc
@@ -159,7 +159,9 @@ arc::SetArcPlayStoreEnabledForProfile(profile, true); } -IN_PROC_BROWSER_TEST_P(ManagementTransitionScreenTest, SuccessfulTransition) { +// TODO(https://crbug.com/316993299) disabled due to flake. +IN_PROC_BROWSER_TEST_P(ManagementTransitionScreenTest, + DISABLED_SuccessfulTransition) { OobeScreenWaiter(ManagementTransitionScreenView::kScreenId).Wait(); test::OobeJS().ExpectVisiblePath(kManagementDialog);
diff --git a/chrome/browser/ash/login/ui/captive_portal_dialog_delegate_browsertest.cc b/chrome/browser/ash/login/ui/captive_portal_dialog_delegate_browsertest.cc index b9d5748..9ed16c3a 100644 --- a/chrome/browser/ash/login/ui/captive_portal_dialog_delegate_browsertest.cc +++ b/chrome/browser/ash/login/ui/captive_portal_dialog_delegate_browsertest.cc
@@ -31,7 +31,8 @@ // Dialogs that take focus must have a name and role to pass accessibility // checks. GetViewAccessibility().SetRole(ax::mojom::Role::kDialog); - GetViewAccessibility().OverrideName("Test dialog"); + GetViewAccessibility().SetName("Test dialog", + ax::mojom::NameFrom::kAttribute); } ChildModalDialogDelegate(const ChildModalDialogDelegate&) = delete; ChildModalDialogDelegate& operator=(const ChildModalDialogDelegate&) = delete;
diff --git a/chrome/browser/autofill/autocomplete_browsertest.cc b/chrome/browser/autofill/autocomplete_browsertest.cc index c7bc4c57..ffcc2ca 100644 --- a/chrome/browser/autofill/autocomplete_browsertest.cc +++ b/chrome/browser/autofill/autocomplete_browsertest.cc
@@ -16,7 +16,7 @@ #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_navigator_params.h" -#include "chrome/browser/web_data_service_factory.h" +#include "chrome/browser/webdata_services/web_data_service_factory.h" #include "chrome/test/base/in_process_browser_test.h" #include "chrome/test/base/ui_test_utils.h" #include "components/autofill/content/browser/content_autofill_driver.h"
diff --git a/chrome/browser/autofill/autocomplete_history_manager_factory.cc b/chrome/browser/autofill/autocomplete_history_manager_factory.cc index e7dc811..86468c9 100644 --- a/chrome/browser/autofill/autocomplete_history_manager_factory.cc +++ b/chrome/browser/autofill/autocomplete_history_manager_factory.cc
@@ -6,7 +6,7 @@ #include "base/no_destructor.h" #include "chrome/browser/profiles/profile.h" -#include "chrome/browser/web_data_service_factory.h" +#include "chrome/browser/webdata_services/web_data_service_factory.h" #include "components/autofill/core/browser/autocomplete_history_manager.h" #include "components/autofill/core/browser/webdata/autofill_webdata_service.h"
diff --git a/chrome/browser/autofill/autofill_browsertest.cc b/chrome/browser/autofill/autofill_browsertest.cc index 955b5c7..682dc4e 100644 --- a/chrome/browser/autofill/autofill_browsertest.cc +++ b/chrome/browser/autofill/autofill_browsertest.cc
@@ -29,7 +29,7 @@ #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/web_data_service_factory.h" +#include "chrome/browser/webdata_services/web_data_service_factory.h" #include "chrome/test/base/in_process_browser_test.h" #include "chrome/test/base/test_switches.h" #include "chrome/test/base/ui_test_utils.h"
diff --git a/chrome/browser/autofill/personal_data_manager_factory.cc b/chrome/browser/autofill/personal_data_manager_factory.cc index 85ebd8b4..08c51813 100644 --- a/chrome/browser/autofill/personal_data_manager_factory.cc +++ b/chrome/browser/autofill/personal_data_manager_factory.cc
@@ -12,7 +12,7 @@ #include "chrome/browser/profiles/profile.h" #include "chrome/browser/signin/identity_manager_factory.h" #include "chrome/browser/sync/sync_service_factory.h" -#include "chrome/browser/web_data_service_factory.h" +#include "chrome/browser/webdata_services/web_data_service_factory.h" #include "components/autofill/content/browser/content_autofill_shared_storage_handler.h" #include "components/autofill/core/browser/personal_data_manager.h" #include "components/autofill/core/browser/strike_databases/strike_database.h"
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 7749b54..49d2647 100644 --- a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.cc +++ b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.cc
@@ -77,8 +77,8 @@ #include "chrome/browser/translate/chrome_translate_client.h" #include "chrome/browser/ui/find_bar/find_bar_state.h" #include "chrome/browser/ui/find_bar/find_bar_state_factory.h" -#include "chrome/browser/web_data_service_factory.h" #include "chrome/browser/webauthn/chrome_authenticator_request_delegate.h" +#include "chrome/browser/webdata_services/web_data_service_factory.h" #include "chrome/common/buildflags.h" #include "chrome/common/url_constants.h" #include "components/autofill/core/browser/personal_data_manager.h"
diff --git a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_factory.cc b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_factory.cc index 9b69d82..358e67a 100644 --- a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_factory.cc +++ b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_factory.cc
@@ -18,7 +18,7 @@ #include "chrome/browser/profiles/profile.h" #include "chrome/browser/search_engines/template_url_service_factory.h" #include "chrome/browser/sessions/tab_restore_service_factory.h" -#include "chrome/browser/web_data_service_factory.h" +#include "chrome/browser/webdata_services/web_data_service_factory.h" #include "content/public/browser/browser_context.h" #include "extensions/buildflags/buildflags.h"
diff --git a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_unittest.cc b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_unittest.cc index 4b785a4..e33dded 100644 --- a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_unittest.cc +++ b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_unittest.cc
@@ -78,7 +78,7 @@ #include "chrome/browser/sync/sync_service_factory.h" #include "chrome/browser/translate/chrome_translate_client.h" #include "chrome/browser/trusted_vault/trusted_vault_service_factory.h" -#include "chrome/browser/web_data_service_factory.h" +#include "chrome/browser/webdata_services/web_data_service_factory.h" #include "chrome/browser/webid/federated_identity_permission_context.h" #include "chrome/common/chrome_constants.h" #include "chrome/common/chrome_features.h"
diff --git a/chrome/browser/browsing_data/counters/autofill_counter_browsertest.cc b/chrome/browser/browsing_data/counters/autofill_counter_browsertest.cc index 2d295bf..dacacc7 100644 --- a/chrome/browser/browsing_data/counters/autofill_counter_browsertest.cc +++ b/chrome/browser/browsing_data/counters/autofill_counter_browsertest.cc
@@ -17,7 +17,7 @@ #include "base/uuid.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/browser.h" -#include "chrome/browser/web_data_service_factory.h" +#include "chrome/browser/webdata_services/web_data_service_factory.h" #include "chrome/test/base/in_process_browser_test.h" #include "components/autofill/core/browser/autofill_test_utils.h" #include "components/autofill/core/browser/autofill_type.h"
diff --git a/chrome/browser/browsing_data/counters/browsing_data_counter_factory.cc b/chrome/browser/browsing_data/counters/browsing_data_counter_factory.cc index 0815cfb..b0223c7d 100644 --- a/chrome/browser/browsing_data/counters/browsing_data_counter_factory.cc +++ b/chrome/browser/browsing_data/counters/browsing_data_counter_factory.cc
@@ -22,8 +22,8 @@ #include "chrome/browser/password_manager/profile_password_store_factory.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/sync/sync_service_factory.h" -#include "chrome/browser/web_data_service_factory.h" #include "chrome/browser/webauthn/chrome_authenticator_request_delegate.h" +#include "chrome/browser/webdata_services/web_data_service_factory.h" #include "components/browsing_data/core/counters/autofill_counter.h" #include "components/browsing_data/core/counters/browsing_data_counter.h" #include "components/browsing_data/core/counters/history_counter.h"
diff --git a/chrome/browser/browsing_data/counters/sync_aware_counter_browsertest.cc b/chrome/browser/browsing_data/counters/sync_aware_counter_browsertest.cc index b66a0b4..2cb9ac0 100644 --- a/chrome/browser/browsing_data/counters/sync_aware_counter_browsertest.cc +++ b/chrome/browser/browsing_data/counters/sync_aware_counter_browsertest.cc
@@ -16,7 +16,7 @@ #include "chrome/browser/sync/test/integration/sync_service_impl_harness.h" #include "chrome/browser/sync/test/integration/sync_test.h" #include "chrome/browser/ui/browser.h" -#include "chrome/browser/web_data_service_factory.h" +#include "chrome/browser/webdata_services/web_data_service_factory.h" #include "chrome/test/base/in_process_browser_test.h" #include "components/autofill/core/browser/webdata/autofill_webdata_service.h" #include "components/browsing_data/core/browsing_data_utils.h"
diff --git a/chrome/browser/chrome_browser_interface_binders.cc b/chrome/browser/chrome_browser_interface_binders.cc index 010216a..44a658a 100644 --- a/chrome/browser/chrome_browser_interface_binders.cc +++ b/chrome/browser/chrome_browser_interface_binders.cc
@@ -71,6 +71,7 @@ #include "components/history_clusters/core/features.h" #include "components/history_clusters/core/history_clusters_service.h" #include "components/history_clusters/history_clusters_internals/webui/history_clusters_internals_ui.h" +#include "components/history_embeddings/history_embeddings_features.h" #include "components/live_caption/caption_util.h" #include "components/live_caption/pref_names.h" #include "components/no_state_prefetch/browser/no_state_prefetch_contents.h" @@ -216,6 +217,7 @@ #include "ui/webui/resources/cr_components/customize_themes/customize_themes.mojom.h" #include "ui/webui/resources/cr_components/help_bubble/help_bubble.mojom.h" #include "ui/webui/resources/cr_components/history_clusters/history_clusters.mojom.h" +#include "ui/webui/resources/cr_components/history_embeddings/history_embeddings.mojom.h" #include "ui/webui/resources/cr_components/most_visited/most_visited.mojom.h" #include "ui/webui/resources/cr_components/theme_color_picker/theme_color_picker.mojom.h" #include "ui/webui/resources/js/browser_command/browser_command.mojom.h" @@ -1201,6 +1203,10 @@ history_clusters::mojom::PageHandler, HistoryUI>(map); } } + if (base::FeatureList::IsEnabled(history_embeddings::kHistoryEmbeddings)) { + RegisterWebUIControllerInterfaceBinder< + history_embeddings::mojom::PageHandler, HistoryUI>(map); + } RegisterWebUIControllerInterfaceBinder< page_image_service::mojom::PageImageServiceHandler, HistoryUI,
diff --git a/chrome/browser/extensions/BUILD.gn b/chrome/browser/extensions/BUILD.gn index cc98eb9..a8116f2 100644 --- a/chrome/browser/extensions/BUILD.gn +++ b/chrome/browser/extensions/BUILD.gn
@@ -1384,7 +1384,7 @@ ] } - if (enable_pdf) { + if (enable_pdf && enable_screen_ai_service) { sources += [ "api/pdf_viewer_private/pdf_viewer_private_event_router.cc", "api/pdf_viewer_private/pdf_viewer_private_event_router.h",
diff --git a/chrome/browser/extensions/api/api_browser_context_keyed_service_factories.cc b/chrome/browser/extensions/api/api_browser_context_keyed_service_factories.cc index bb02cc3..cf418cd 100644 --- a/chrome/browser/extensions/api/api_browser_context_keyed_service_factories.cc +++ b/chrome/browser/extensions/api/api_browser_context_keyed_service_factories.cc
@@ -42,6 +42,7 @@ #include "components/services/screen_ai/buildflags/buildflags.h" #include "extensions/browser/api/bluetooth_low_energy/bluetooth_low_energy_api.h" #include "extensions/browser/api/networking_private/networking_private_delegate_factory.h" +#include "pdf/buildflags.h" #include "printing/buildflags/buildflags.h" #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) @@ -63,9 +64,9 @@ #include "chrome/browser/extensions/api/image_writer_private/image_writer_controller_lacros.h" #endif -#if BUILDFLAG(ENABLE_SCREEN_AI_SERVICE) +#if BUILDFLAG(ENABLE_PDF) && BUILDFLAG(ENABLE_SCREEN_AI_SERVICE) #include "chrome/browser/extensions/api/pdf_viewer_private/pdf_viewer_private_event_router_factory.h" -#endif // BUILDFLAG(ENABLE_SCREEN_AI_SERVICE) +#endif // BUILDFLAG(ENABLE_PDF) && BUILDFLAG(ENABLE_SCREEN_AI_SERVICE) #if BUILDFLAG(ENABLE_SERVICE_DISCOVERY) #include "chrome/browser/extensions/api/mdns/mdns_api.h" @@ -113,9 +114,9 @@ extensions::OmniboxAPI::GetFactoryInstance(); extensions::PasswordsPrivateDelegateFactory::GetInstance(); extensions::PasswordsPrivateEventRouterFactory::GetInstance(); -#if BUILDFLAG(ENABLE_SCREEN_AI_SERVICE) +#if BUILDFLAG(ENABLE_PDF) && BUILDFLAG(ENABLE_SCREEN_AI_SERVICE) extensions::PdfViewerPrivateEventRouterFactory::GetInstance(); -#endif // BUILDFLAG(ENABLE_SCREEN_AI_SERVICE) +#endif // BUILDFLAG(ENABLE_PDF) && BUILDFLAG(ENABLE_SCREEN_AI_SERVICE) extensions::PreferenceAPI::GetFactoryInstance(); #if BUILDFLAG(IS_CHROMEOS) && BUILDFLAG(USE_CUPS) extensions::PrintingAPIHandler::GetFactoryInstance();
diff --git a/chrome/browser/extensions/api/management/chrome_management_api_delegate.cc b/chrome/browser/extensions/api/management/chrome_management_api_delegate.cc index 6aaa6206..109cbb3 100644 --- a/chrome/browser/extensions/api/management/chrome_management_api_delegate.cc +++ b/chrome/browser/extensions/api/management/chrome_management_api_delegate.cc
@@ -236,12 +236,13 @@ auto* provider = web_app::WebAppProvider::GetForWebApps( Profile::FromBrowserContext(context)); - provider->scheduler().InstallFromInfo( + provider->scheduler().InstallFromInfoWithParams( std::move(web_app_info), /*overwrite_existing_manifest_fields=*/false, webapps::WebappInstallSource::MANAGEMENT_API, base::BindOnce(OnGenerateAppForLinkCompleted, - base::RetainedRef(function))); + base::RetainedRef(function)), + web_app::WebAppInstallParams()); } extensions::api::management::ExtensionInfo CreateExtensionInfoFromWebApp(
diff --git a/chrome/browser/extensions/api/web_request/web_request_apitest.cc b/chrome/browser/extensions/api/web_request/web_request_apitest.cc index 274efcd..593dc46 100644 --- a/chrome/browser/extensions/api/web_request/web_request_apitest.cc +++ b/chrome/browser/extensions/api/web_request/web_request_apitest.cc
@@ -1027,8 +1027,8 @@ << message_; } -// TODO(crbug.com/1450976): test is flaky on Mac11. -#if BUILDFLAG(IS_MAC) +// TODO: crbug.com/1450976 - Re-enable tests on Mac and Lacros. +#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_CHROMEOS_LACROS) #define MAYBE_WebRequestCORSWithExtraHeaders \ DISABLED_WebRequestCORSWithExtraHeaders #else
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json index efe27d5..e6aee84 100644 --- a/chrome/browser/flag-metadata.json +++ b/chrome/browser/flag-metadata.json
@@ -6476,11 +6476,6 @@ "expiry_milestone": 118 }, { - "name": "permission-storage-access-api", - "owners": [ "dullweber@chromium.org", "fsenra@google.com"], - "expiry_milestone": 124 - }, - { "name": "permissive-usb-passthrough", "owners": [ "drmasquatch@chromium.org" @@ -8505,7 +8500,7 @@ { "name": "web-midi", "owners": [ "//third_party/blink/renderer/modules/webaudio/OWNERS", "reillyg@chromium.org", "deviceapi-team@google.com" ], - "expiry_milestone": 124 + "expiry_milestone": 126 }, { "name": "web-otp-backend",
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc index dc950633..83292f6 100644 --- a/chrome/browser/flag_descriptions.cc +++ b/chrome/browser/flag_descriptions.cc
@@ -2888,11 +2888,6 @@ "right-hand side address bar icon for quiet permission prompts. Requires " "chrome://flags/#quiet-notification-prompts to be enabled."; -const char kPermissionStorageAccessAPIName[] = - "Storage Access API permission UI"; -const char kPermissionStorageAccessAPIDescription[] = - "Enables the new Storage Access API permission UI on Desktop"; - const char kShowRelatedWebsiteSetsPermissionGrantsName[] = "Show permission grants from Related Website Sets"; const char kShowRelatedWebsiteSetsPermissionGrantsDescription[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h index 33aeeee..b050577 100644 --- a/chrome/browser/flag_descriptions.h +++ b/chrome/browser/flag_descriptions.h
@@ -1666,9 +1666,6 @@ extern const char kPermissionQuietChipName[]; extern const char kPermissionQuietChipDescription[]; -extern const char kPermissionStorageAccessAPIName[]; -extern const char kPermissionStorageAccessAPIDescription[]; - extern const char kShowRelatedWebsiteSetsPermissionGrantsName[]; extern const char kShowRelatedWebsiteSetsPermissionGrantsDescription[];
diff --git a/chrome/browser/flags/android/chrome_feature_list.cc b/chrome/browser/flags/android/chrome_feature_list.cc index fe8e5f3e..28c33085 100644 --- a/chrome/browser/flags/android/chrome_feature_list.cc +++ b/chrome/browser/flags/android/chrome_feature_list.cc
@@ -280,6 +280,7 @@ &kTestDefaultDisabled, &kTestDefaultEnabled, &kTotallyEdgeToEdge, + &kSafetyHub, &kStartSurfaceAndroid, &kStartSurfaceOnTablet, &kStartSurfaceReturnTime, @@ -879,6 +880,8 @@ "TestDefaultEnabled", base::FEATURE_ENABLED_BY_DEFAULT); +BASE_FEATURE(kSafetyHub, "SafetyHub", base::FEATURE_DISABLED_BY_DEFAULT); + // This feature updates the triggering logic for the default search engine // choice promo. See crbug.com/1471643 for more details. BASE_FEATURE(kSearchEnginesPromoV3,
diff --git a/chrome/browser/flags/android/chrome_feature_list.h b/chrome/browser/flags/android/chrome_feature_list.h index 25977e2b..da32d38 100644 --- a/chrome/browser/flags/android/chrome_feature_list.h +++ b/chrome/browser/flags/android/chrome_feature_list.h
@@ -161,6 +161,7 @@ BASE_DECLARE_FEATURE(kTestDefaultEnabled); BASE_DECLARE_FEATURE(kToolbarUseHardwareBitmapDraw); BASE_DECLARE_FEATURE(kTotallyEdgeToEdge); +BASE_DECLARE_FEATURE(kSafetyHub); BASE_DECLARE_FEATURE(kStartSurfaceAndroid); BASE_DECLARE_FEATURE(kStartSurfaceOnTablet); BASE_DECLARE_FEATURE(kStartSurfaceReturnTime);
diff --git a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java index 1e96af7..865cc58 100644 --- a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java +++ b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
@@ -409,6 +409,7 @@ public static final String RENAME_JOURNEYS = "RenameJourneys"; public static final String REPLACE_SYNC_PROMOS_WITH_SIGN_IN_PROMOS = "ReplaceSyncPromosWithSignInPromos"; + public static final String SAFETY_HUB = "SafetyHub"; public static final String SAFE_BROWSING_DELAYED_WARNINGS = "SafeBrowsingDelayedWarnings"; public static final String SAFE_BROWSING_CALL_NEW_GMS_API_ON_STARTUP = "SafeBrowsingCallNewGmsApiOnStartup"; @@ -803,6 +804,8 @@ newMutableFlagWithSafeDefault(READER_MODE_IN_CCT, false); public static final MutableFlagWithSafeDefault sRecordSuppressionMetrics = newMutableFlagWithSafeDefault(RECORD_SUPPRESSION_METRICS, true); + public static final MutableFlagWithSafeDefault sSafetyHub = + newMutableFlagWithSafeDefault(SAFETY_HUB, false); public static final MutableFlagWithSafeDefault sSearchInCCT = newMutableFlagWithSafeDefault(SEARCH_IN_CCT, false); public static final MutableFlagWithSafeDefault sSearchReadyOmniboxAllowQueryEdit =
diff --git a/chrome/browser/importer/profile_writer.cc b/chrome/browser/importer/profile_writer.cc index b623f1f..88713b9 100644 --- a/chrome/browser/importer/profile_writer.cc +++ b/chrome/browser/importer/profile_writer.cc
@@ -21,7 +21,7 @@ #include "chrome/browser/password_manager/profile_password_store_factory.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/search_engines/template_url_service_factory.h" -#include "chrome/browser/web_data_service_factory.h" +#include "chrome/browser/webdata_services/web_data_service_factory.h" #include "chrome/common/importer/imported_bookmark_entry.h" #include "chrome/common/pref_names.h" #include "components/autofill/core/browser/webdata/autofill_webdata_service.h"
diff --git a/chrome/browser/lacros/web_app_provider_bridge_lacros.cc b/chrome/browser/lacros/web_app_provider_bridge_lacros.cc index 0c867a2..6b0c4788 100644 --- a/chrome/browser/lacros/web_app_provider_bridge_lacros.cc +++ b/chrome/browser/lacros/web_app_provider_bridge_lacros.cc
@@ -159,10 +159,11 @@ std::move(*arc_install_info->additional_policy_ids); } - provider->scheduler().InstallFromInfo( + provider->scheduler().InstallFromInfoWithParams( std::move(install_info), /*overwrite_existing_manifest_fields=*/false, - webapps::WebappInstallSource::ARC, std::move(callback)); + webapps::WebappInstallSource::ARC, std::move(callback), + web_app::WebAppInstallParams()); } // static
diff --git a/chrome/browser/magic_stack/android/BUILD.gn b/chrome/browser/magic_stack/android/BUILD.gn index 65b5dbcf..734617ab 100644 --- a/chrome/browser/magic_stack/android/BUILD.gn +++ b/chrome/browser/magic_stack/android/BUILD.gn
@@ -13,6 +13,7 @@ "java/src/org/chromium/chrome/browser/magic_stack/HomeModulesCoordinator.java", "java/src/org/chromium/chrome/browser/magic_stack/HomeModulesMediator.java", "java/src/org/chromium/chrome/browser/magic_stack/HomeModulesMetricsUtils.java", + "java/src/org/chromium/chrome/browser/magic_stack/HomeModulesRecyclerView.java", "java/src/org/chromium/chrome/browser/magic_stack/ModuleConfigChecker.java", "java/src/org/chromium/chrome/browser/magic_stack/ModuleDelegate.java", "java/src/org/chromium/chrome/browser/magic_stack/ModuleDelegateHost.java", @@ -72,6 +73,7 @@ "junit/src/org/chromium/chrome/browser/magic_stack/HomeModulesCoordinatorUnitTest.java", "junit/src/org/chromium/chrome/browser/magic_stack/HomeModulesMediatorUnitTest.java", "junit/src/org/chromium/chrome/browser/magic_stack/HomeModulesMetricsUtilsUnitTest.java", + "junit/src/org/chromium/chrome/browser/magic_stack/HomeModulesRecyclerViewUnitTest.java", "junit/src/org/chromium/chrome/browser/magic_stack/ModuleRegistryUnitTest.java", ]
diff --git a/chrome/browser/magic_stack/android/java/res/layout/home_modules_recycler_view_layout.xml b/chrome/browser/magic_stack/android/java/res/layout/home_modules_recycler_view_layout.xml index 7dc588d9..cc0f5b1 100644 --- a/chrome/browser/magic_stack/android/java/res/layout/home_modules_recycler_view_layout.xml +++ b/chrome/browser/magic_stack/android/java/res/layout/home_modules_recycler_view_layout.xml
@@ -5,7 +5,7 @@ found in the LICENSE file. --> -<androidx.recyclerview.widget.RecyclerView +<org.chromium.chrome.browser.magic_stack.HomeModulesRecyclerView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/home_modules_recycler_view"
diff --git a/chrome/browser/magic_stack/android/java/src/org/chromium/chrome/browser/magic_stack/CirclePagerIndicatorDecoration.java b/chrome/browser/magic_stack/android/java/src/org/chromium/chrome/browser/magic_stack/CirclePagerIndicatorDecoration.java index db75191..14cb15c 100644 --- a/chrome/browser/magic_stack/android/java/src/org/chromium/chrome/browser/magic_stack/CirclePagerIndicatorDecoration.java +++ b/chrome/browser/magic_stack/android/java/src/org/chromium/chrome/browser/magic_stack/CirclePagerIndicatorDecoration.java
@@ -4,8 +4,6 @@ package org.chromium.chrome.browser.magic_stack; -import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; - import android.content.Context; import android.content.res.Resources; import android.graphics.Canvas; @@ -13,16 +11,19 @@ import android.graphics.Paint.Style; import android.graphics.Rect; import android.view.View; -import android.view.ViewGroup.MarginLayoutParams; import androidx.annotation.ColorInt; import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; +import androidx.core.text.TextUtilsCompat; +import androidx.core.view.ViewCompat; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; import org.chromium.components.browser_ui.widget.displaystyle.UiConfig.DisplayStyle; +import java.util.Locale; + /** Circle pager indicator for recyclerview. */ public class CirclePagerIndicatorDecoration extends RecyclerView.ItemDecoration { private final @ColorInt int mColorActive; @@ -43,11 +44,11 @@ /** Padding between indicators in pixel. */ private final float mIndicatorItemPaddingPx; - private final int mModuleInternalPaddingPx; - private final Paint mPaint = new Paint(); private final boolean mIsTablet; + private final boolean mIsLeftToRight; + /** The start margin of the recyclerview in pixel. */ private int mStartMarginPx; @@ -83,7 +84,10 @@ mIndicatorHeightPx = (int) mIndicatorItemDiameterPx + resources.getDimensionPixelSize(R.dimen.page_indicator_top_margin); - mModuleInternalPaddingPx = resources.getDimensionPixelSize(R.dimen.module_internal_padding); + + mIsLeftToRight = + TextUtilsCompat.getLayoutDirectionFromLocale(Locale.getDefault()) + == ViewCompat.LAYOUT_DIRECTION_LTR; } @Override @@ -194,38 +198,20 @@ // they are hidden. outRect.bottom = itemCount <= mItemPerScreen ? 0 : mIndicatorHeightPx; - // Don't need to change the width of a child view on phones since there is only one item - // shown per screen, and it never changes. - if (!mIsTablet) return; + // If showing one card per screen, the view's width should match the parent recyclerview. + // Thus, we don't need to add extra padding on the left side of any card. + if (!mIsTablet || mItemPerScreen == 1 || itemCount == 1) return; - MarginLayoutParams marginLayoutParams = (MarginLayoutParams) view.getLayoutParams(); - if (mItemPerScreen == 1 || itemCount == 1) { - // If showing one item per screen, the view's width should match the parent - // recyclerview. - marginLayoutParams.width = MATCH_PARENT; - if (mItemPerScreen == 1) { - updateMargin(view, marginLayoutParams); - } - return; + // On a wide screen, we will show 2 cards instead of 1 per screen. The card's width is + // calculated and doesn't match parent's width any more. Add a padding on the left side of + // any card except the first one if it is a left to right language; on the right side if it + // is a right to left language. + int padding = parent.getChildAdapterPosition(view) == 0 ? 0 : (int) mIndicatorItemPaddingPx; + if (mIsLeftToRight) { + outRect.left = padding; + } else { + outRect.right = padding; } - - // On a wide screen, we will show 2 cards instead of 1 on the magic stack. - int position = parent.getChildAdapterPosition(view); - boolean isFirstPosition = position == 0; - - // Updates the width of the view. - outRect.left = isFirstPosition ? 0 : (int) mIndicatorItemPaddingPx; - int width = - (parent.getMeasuredWidth() - mModuleInternalPaddingPx * (mItemPerScreen - 1)) - / mItemPerScreen; - marginLayoutParams.width = width; - updateMargin(view, marginLayoutParams); - } - - private void updateMargin(View view, MarginLayoutParams marginLayoutParams) { - marginLayoutParams.setMarginEnd(mStartMarginPx); - marginLayoutParams.setMarginStart(mStartMarginPx); - view.setLayoutParams(marginLayoutParams); } void onDisplayStyleChanged(int startMarginPx, int itemPerScreen) {
diff --git a/chrome/browser/magic_stack/android/java/src/org/chromium/chrome/browser/magic_stack/HomeModulesCoordinator.java b/chrome/browser/magic_stack/android/java/src/org/chromium/chrome/browser/magic_stack/HomeModulesCoordinator.java index 70b44f5..561616b 100644 --- a/chrome/browser/magic_stack/android/java/src/org/chromium/chrome/browser/magic_stack/HomeModulesCoordinator.java +++ b/chrome/browser/magic_stack/android/java/src/org/chromium/chrome/browser/magic_stack/HomeModulesCoordinator.java
@@ -45,7 +45,7 @@ private final ModuleDelegateHost mModuleDelegateHost; private HomeModulesMediator mMediator; private final SimpleRecyclerViewAdapter mAdapter; - private final RecyclerView mRecyclerView; + private final HomeModulesRecyclerView mRecyclerView; private final ModelList mModel; private final HomeModulesContextMenuManager mHomeModulesContextMenuManager; private final ObservableSupplier<Profile> mProfileSupplier; @@ -132,11 +132,19 @@ private void setupRecyclerView(Activity activity) { boolean isTablet = DeviceFormFactor.isNonMultiDisplayContextOnTablet(activity); + int startMargin = mModuleDelegateHost.getStartMargin(); mUiConfig = isTablet ? mModuleDelegateHost.getUiConfig() : null; + mItemPerScreen = + mUiConfig == null + ? 1 + : CirclePagerIndicatorDecoration.getItemPerScreen( + mUiConfig.getCurrentDisplayStyle()); + mRecyclerView.initialize(isTablet, startMargin, mItemPerScreen); + mPageIndicatorDecoration = new CirclePagerIndicatorDecoration( activity, - mModuleDelegateHost.getStartMargin(), + startMargin, SemanticColorUtils.getDefaultIconColorSecondary(activity), activity.getColor( org.chromium.components.browser_ui.styles.R.color @@ -153,9 +161,6 @@ } }; - // Sets the default value. - mItemPerScreen = 1; - // Snap scroll is supported by the recyclerview if it shows a single item per screen. This // happens on phones or small windows on tablets. if (!isTablet) { @@ -187,15 +192,17 @@ } // Notifies the CirclePageIndicatorDecoration. + int updatedStartMargin = mModuleDelegateHost.getStartMargin(); mPageIndicatorDecoration.onDisplayStyleChanged( - mModuleDelegateHost.getStartMargin(), mItemPerScreen); + updatedStartMargin, mItemPerScreen); + mRecyclerView.onDisplayStyleChanged(updatedStartMargin, mItemPerScreen); // Redraws the recyclerview when display style is changed on tablets. mRecyclerView.invalidateItemDecorations(); }; mUiConfig.addObserver(mDisplayStyleObserver); - mPageIndicatorDecoration.onDisplayStyleChanged( - mModuleDelegateHost.getStartMargin(), mItemPerScreen); + mPageIndicatorDecoration.onDisplayStyleChanged(startMargin, mItemPerScreen); + mRecyclerView.onDisplayStyleChanged(startMargin, mItemPerScreen); } /**
diff --git a/chrome/browser/magic_stack/android/java/src/org/chromium/chrome/browser/magic_stack/HomeModulesRecyclerView.java b/chrome/browser/magic_stack/android/java/src/org/chromium/chrome/browser/magic_stack/HomeModulesRecyclerView.java new file mode 100644 index 0000000..37dc977 --- /dev/null +++ b/chrome/browser/magic_stack/android/java/src/org/chromium/chrome/browser/magic_stack/HomeModulesRecyclerView.java
@@ -0,0 +1,108 @@ +// Copyright 2024 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chrome.browser.magic_stack; + +import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; + +import android.content.Context; +import android.graphics.Canvas; +import android.util.AttributeSet; +import android.view.View; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.VisibleForTesting; +import androidx.recyclerview.widget.RecyclerView; + +/** A custom RecyclerView implementation for the home modules. */ +public class HomeModulesRecyclerView extends RecyclerView { + + /* Whether the activity is running on a tablet.*/ + private boolean mIsTablet; + + /** The value is updated for tablets when displayStyle is changed. */ + private int mItemPerScreen; + + /** The start margin of the recyclerview in pixel. */ + private int mStartMarginPx; + + /* The internal padding between two modules in pixel. */ + private int mModuleInternalPaddingPx; + + public HomeModulesRecyclerView(@NonNull Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + } + + /** + * Initializes the recyclerview. + * + * @param isTablet Whether the activity is running on a tablet. + * @param startMarginPx The start margin of the recyclerview in pixel. + * @param itemPerScreen The number of modules are shown per screen. + */ + void initialize(boolean isTablet, int startMarginPx, int itemPerScreen) { + mIsTablet = isTablet; + mStartMarginPx = startMarginPx; + + mItemPerScreen = itemPerScreen; + mModuleInternalPaddingPx = + getContext().getResources().getDimensionPixelSize(R.dimen.module_internal_padding); + } + + @Override + public void onDraw(@NonNull Canvas c) { + super.onDraw(c); + // Don't need to change the width of a child view on phones since there is only one item + // shown per screen, and it never changes. + if (!mIsTablet) return; + + int itemCount = getAdapter().getItemCount(); + int measuredWidth = getMeasuredWidth(); + for (int i = 0; i < getChildCount(); i++) { + onDrawImpl(getChildAt(i), itemCount, measuredWidth); + } + } + + /** Called when the DisplayStyle is changed. */ + void onDisplayStyleChanged(int startMarginPx, int itemPerScreen) { + mStartMarginPx = startMarginPx; + mItemPerScreen = itemPerScreen; + } + + @VisibleForTesting + void onDrawImpl(View view, int totalChildCount, int measuredWidth) { + MarginLayoutParams marginLayoutParams = (MarginLayoutParams) view.getLayoutParams(); + if (mItemPerScreen == 1 || totalChildCount == 1) { + // If showing one item per screen, the view's width should match the parent + // recyclerview. + if (marginLayoutParams.width == MATCH_PARENT) return; + + marginLayoutParams.width = MATCH_PARENT; + if (mItemPerScreen == 1) { + updateMargin(view, marginLayoutParams); + } + } else { + // On a wide screen, we will show 2 cards instead of 1 on the magic stack. + // Updates the width of the view. + int width = + (measuredWidth - mModuleInternalPaddingPx * (mItemPerScreen - 1)) + / mItemPerScreen; + if (marginLayoutParams.width == width) return; + + marginLayoutParams.width = width; + updateMargin(view, marginLayoutParams); + } + } + + private void updateMargin(View view, MarginLayoutParams marginLayoutParams) { + marginLayoutParams.setMarginEnd(mStartMarginPx); + marginLayoutParams.setMarginStart(mStartMarginPx); + view.setLayoutParams(marginLayoutParams); + } + + void setStartMarginPxForTesting(int startMarginPx) { + mStartMarginPx = startMarginPx; + } +}
diff --git a/chrome/browser/magic_stack/android/junit/src/org/chromium/chrome/browser/magic_stack/CirclePagerIndicatorDecorationUnitTest.java b/chrome/browser/magic_stack/android/junit/src/org/chromium/chrome/browser/magic_stack/CirclePagerIndicatorDecorationUnitTest.java index d8a39c7..2de72f9c 100644 --- a/chrome/browser/magic_stack/android/junit/src/org/chromium/chrome/browser/magic_stack/CirclePagerIndicatorDecorationUnitTest.java +++ b/chrome/browser/magic_stack/android/junit/src/org/chromium/chrome/browser/magic_stack/CirclePagerIndicatorDecorationUnitTest.java
@@ -4,12 +4,9 @@ package org.chromium.chrome.browser.magic_stack; -import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; - import static org.junit.Assert.assertEquals; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -23,7 +20,6 @@ import android.graphics.Paint; import android.graphics.Rect; import android.view.View; -import android.view.ViewGroup.MarginLayoutParams; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; @@ -43,7 +39,6 @@ import org.chromium.base.test.BaseRobolectricTestRunner; import org.chromium.components.browser_ui.widget.displaystyle.HorizontalDisplayStyle; -import org.chromium.components.browser_ui.widget.displaystyle.UiConfig; import org.chromium.components.browser_ui.widget.displaystyle.UiConfig.DisplayStyle; import org.chromium.components.browser_ui.widget.displaystyle.VerticalDisplayStyle; @@ -52,7 +47,6 @@ @Config(manifest = Config.NONE) public class CirclePagerIndicatorDecorationUnitTest { @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule(); - @Mock private UiConfig mUiConfig; @Mock private Canvas mCanvas; @Mock private RecyclerView mRecyclerView; @Mock private RecyclerView.Adapter mAdapter; @@ -66,10 +60,9 @@ private float mIndicatorItemDiameter; private float mIndicatorRadius; - private float mIndicatorItemPadding; + private int mIndicatorItemPadding; private int mParentViewWidth; private int mParentHeight; - private int mModuleInternalPaddingPx; private CirclePagerIndicatorDecoration mDecoration; private Context mContext; @@ -81,13 +74,12 @@ Resources resources = mContext.getResources(); mIndicatorItemPadding = - (float) resources.getDimensionPixelSize(R.dimen.page_indicator_internal_padding); + resources.getDimensionPixelSize(R.dimen.page_indicator_internal_padding); mIndicatorItemDiameter = resources.getDimensionPixelSize(R.dimen.page_indicator_dot_size); mIndicatorRadius = mIndicatorItemDiameter / 2f; mIndicatorHeight = (int) mIndicatorItemDiameter + resources.getDimensionPixelSize(R.dimen.page_indicator_top_margin); - mModuleInternalPaddingPx = resources.getDimensionPixelSize(R.dimen.module_internal_padding); mParentViewWidth = 800; mParentHeight = 400; when(mRecyclerView.getHeight()).thenReturn(mParentHeight); @@ -299,102 +291,88 @@ Rect rect = new Rect(); View view = Mockito.mock(View.class); RecyclerView.State state = Mockito.mock(State.class); - when(mRecyclerView.getMeasuredWidth()).thenReturn(mParentViewWidth); when(mAdapter.getItemCount()).thenReturn(3); + when(mRecyclerView.getChildAdapterPosition(view)).thenReturn(1); mDecoration.getItemOffsetsImpl(rect, view, mRecyclerView, state); - // Verifies that the width is not updated for phones. + // Verifies that the page indicator is shown, but no extra padding is added to any view. assertEquals(mIndicatorHeight, rect.bottom); assertEquals(0, rect.left); - verify(view, never()).getLayoutParams(); } @Test @SmallTest - public void testGetItemOffsetsOneItemOnly_Tablet() { + public void testGetItemOffsets_DoNotShowPageIndicator() { mDecoration = create(/* isTablet= */ true); Rect rect = new Rect(); View view = Mockito.mock(View.class); RecyclerView.State state = Mockito.mock(State.class); + // The recyclerview has only 1 item shown. - when(mAdapter.getItemCount()).thenReturn(1); + int itemCount = 1; + when(mAdapter.getItemCount()).thenReturn(itemCount); // Sets the tablet as a wide screen. - DisplayStyle displayStyle = - new DisplayStyle(HorizontalDisplayStyle.WIDE, VerticalDisplayStyle.REGULAR); - when(mUiConfig.getCurrentDisplayStyle()).thenReturn(displayStyle); - assertEquals(2, getItemPerScreen(mUiConfig.getCurrentDisplayStyle())); - - MarginLayoutParams layoutParams = new MarginLayoutParams(0, 0); - when(view.getLayoutParams()).thenReturn(layoutParams); - - mDecoration.onDisplayStyleChanged(0, getItemPerScreen(displayStyle)); + int itemPerScreen = 2; + mDecoration.onDisplayStyleChanged(0, itemPerScreen); mDecoration.getItemOffsetsImpl(rect, view, mRecyclerView, state); - // Verifies that the space of the indicators are removed. + // Verifies that the space of the indicators are removed when the itemCount is less than the + // itemPerScreen. assertEquals(0, rect.bottom); assertEquals(0, rect.left); - assertEquals(MATCH_PARENT, layoutParams.width); + + itemCount = 2; + when(mAdapter.getItemCount()).thenReturn(itemCount); + mDecoration.onDisplayStyleChanged(0, itemPerScreen); + mDecoration.getItemOffsetsImpl(rect, view, mRecyclerView, state); + // Verifies that the space of the indicators are removed when the itemCount equals to the + // itemPerScreen. + assertEquals(0, rect.bottom); + assertEquals(0, rect.left); } @Test @SmallTest - public void testGetItemOffsets_Tablet() { + public void testGetItemOffsets_ShowPageIndicator() { mDecoration = create(/* isTablet= */ true); Rect rect = new Rect(); View view = Mockito.mock(View.class); RecyclerView.State state = Mockito.mock(State.class); - when(mRecyclerView.getMeasuredWidth()).thenReturn(mParentViewWidth); - int expectedItemWith = (mParentViewWidth - mModuleInternalPaddingPx) / 2; // The recyclerview has 3 items shown. - when(mAdapter.getItemCount()).thenReturn(3); + int itemCount = 3; + when(mAdapter.getItemCount()).thenReturn(itemCount); // Sets the tablet as a wide screen. + int itemPerScreen = 2; + mDecoration.onDisplayStyleChanged(0, itemPerScreen); + mDecoration.getItemOffsetsImpl(rect, view, mRecyclerView, state); + // Verifies that the page indicator is shown when all of the items can't fit in one screen. + assertEquals(mIndicatorHeight, rect.bottom); + // Verifies that no extra padding is added for the first child view. + assertEquals(0, rect.left); + + // Sets the view not be the first child. + when(mRecyclerView.getChildAdapterPosition(view)).thenReturn(1); + mDecoration.getItemOffsetsImpl(rect, view, mRecyclerView, state); + // Verifies that an extra padding is added on the left side of the view. + assertEquals(mIndicatorHeight, rect.bottom); + assertEquals(mIndicatorItemPadding, rect.left); + } + + @Test + @SmallTest + public void testGetItemPerScreen() { + // Sets the tablet as a wide screen. DisplayStyle displayStyle = new DisplayStyle(HorizontalDisplayStyle.WIDE, VerticalDisplayStyle.REGULAR); - when(mUiConfig.getCurrentDisplayStyle()).thenReturn(displayStyle); - assertEquals(2, getItemPerScreen(mUiConfig.getCurrentDisplayStyle())); + assertEquals(2, getItemPerScreen(displayStyle)); - MarginLayoutParams layoutParams = new MarginLayoutParams(0, 0); - when(view.getLayoutParams()).thenReturn(layoutParams); - - mDecoration.onDisplayStyleChanged(0, getItemPerScreen(displayStyle)); - mDecoration.getItemOffsetsImpl(rect, view, mRecyclerView, state); - // Verifies that the width is updated for the view when multiple items are shown per screen. - assertEquals(mIndicatorHeight, rect.bottom); - assertEquals(0, rect.left); - assertEquals(expectedItemWith, layoutParams.width); - - // Changes the current screen to a narrow window on tablets. + // Sets the tablet as a small screen. displayStyle = new DisplayStyle(HorizontalDisplayStyle.REGULAR, VerticalDisplayStyle.REGULAR); - when(mUiConfig.getCurrentDisplayStyle()).thenReturn(displayStyle); - MarginLayoutParams marginLayoutParams = Mockito.mock(MarginLayoutParams.class); - when(view.getLayoutParams()).thenReturn(marginLayoutParams); - assertEquals(1, getItemPerScreen(mUiConfig.getCurrentDisplayStyle())); - int startMargin = 20; - mDecoration.onDisplayStyleChanged(startMargin, getItemPerScreen(displayStyle)); - mDecoration.getItemOffsetsImpl(rect, view, mRecyclerView, state); - - // Verifies that the start margin is set to the view if a single item is shown per screen. - verify(marginLayoutParams).setMarginStart(eq(startMargin)); - verify(marginLayoutParams).setMarginEnd(eq(startMargin)); - verify(view).setLayoutParams(eq(marginLayoutParams)); - - // Set back to wide screen. - displayStyle = new DisplayStyle(HorizontalDisplayStyle.WIDE, VerticalDisplayStyle.REGULAR); - when(mUiConfig.getCurrentDisplayStyle()).thenReturn(displayStyle); - startMargin = 0; - assertEquals(2, getItemPerScreen(mUiConfig.getCurrentDisplayStyle())); - - mDecoration.onDisplayStyleChanged(startMargin, getItemPerScreen(displayStyle)); - mDecoration.getItemOffsetsImpl(rect, view, mRecyclerView, state); - // Verifies that the width is updated for the view when multiple items are shown per screen. - verify(marginLayoutParams).setMarginStart(eq(startMargin)); - verify(marginLayoutParams).setMarginEnd(eq(startMargin)); - verify(view, times(2)).setLayoutParams(eq(marginLayoutParams)); - assertEquals(expectedItemWith, layoutParams.width); + assertEquals(1, getItemPerScreen(displayStyle)); } private CirclePagerIndicatorDecoration create(boolean isTablet) {
diff --git a/chrome/browser/magic_stack/android/junit/src/org/chromium/chrome/browser/magic_stack/HomeModulesCoordinatorUnitTest.java b/chrome/browser/magic_stack/android/junit/src/org/chromium/chrome/browser/magic_stack/HomeModulesCoordinatorUnitTest.java index aa3f038..f6e0fc5 100644 --- a/chrome/browser/magic_stack/android/junit/src/org/chromium/chrome/browser/magic_stack/HomeModulesCoordinatorUnitTest.java +++ b/chrome/browser/magic_stack/android/junit/src/org/chromium/chrome/browser/magic_stack/HomeModulesCoordinatorUnitTest.java
@@ -89,7 +89,7 @@ @Mock private Resources mResources; @Mock private ModuleDelegateHost mModuleDelegateHost; @Mock private ViewGroup mView; - @Mock private RecyclerView mRecyclerView; + @Mock private HomeModulesRecyclerView mRecyclerView; @Mock private UiConfig mUiConfig; @Mock private Configuration mConfiguration; @Mock private ApplicationInfo mApplicationInfo;
diff --git a/chrome/browser/magic_stack/android/junit/src/org/chromium/chrome/browser/magic_stack/HomeModulesRecyclerViewUnitTest.java b/chrome/browser/magic_stack/android/junit/src/org/chromium/chrome/browser/magic_stack/HomeModulesRecyclerViewUnitTest.java new file mode 100644 index 0000000..42e08451 --- /dev/null +++ b/chrome/browser/magic_stack/android/junit/src/org/chromium/chrome/browser/magic_stack/HomeModulesRecyclerViewUnitTest.java
@@ -0,0 +1,115 @@ +// Copyright 2024 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chrome.browser.magic_stack; + +import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; + +import static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.app.Activity; +import android.view.View; +import android.view.ViewGroup.MarginLayoutParams; + +import androidx.test.core.app.ApplicationProvider; +import androidx.test.filters.SmallTest; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.Robolectric; +import org.robolectric.annotation.Config; + +import org.chromium.base.test.BaseRobolectricTestRunner; +import org.chromium.chrome.R; + +@RunWith(BaseRobolectricTestRunner.class) +@Config(manifest = Config.NONE) +public class HomeModulesRecyclerViewUnitTest { + @Mock private View mView; + + private Activity mActivity; + private HomeModulesRecyclerView mRecyclerView; + private int mModuleInternalPaddingPx; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + mActivity = Robolectric.buildActivity(Activity.class).setup().get(); + mActivity.setTheme(R.style.Theme_BrowserUI_DayNight); + mRecyclerView = + (HomeModulesRecyclerView) + mActivity + .getLayoutInflater() + .inflate(R.layout.home_modules_recycler_view_layout, null); + mActivity.setContentView(mRecyclerView); + + mModuleInternalPaddingPx = + ApplicationProvider.getApplicationContext() + .getResources() + .getDimensionPixelSize(R.dimen.module_internal_padding); + } + + @Test + @SmallTest + public void testOnDraw_OneItermPerScreen() { + int itemPerScreen = 1; + int startMarginPx = 0; + int measuredWidth = 500; + mRecyclerView.initialize(/* isTablet= */ true, startMarginPx, itemPerScreen); + + MarginLayoutParams marginLayoutParams = new MarginLayoutParams(100, 100); + when(mView.getLayoutParams()).thenReturn(marginLayoutParams); + startMarginPx = 5; + mRecyclerView.setStartMarginPxForTesting(startMarginPx); + + // Verifies when there is one item per screen, the width is set to MATCH_PARENT. + mRecyclerView.onDrawImpl(mView, 3, measuredWidth); + assertEquals(MATCH_PARENT, marginLayoutParams.width); + assertEquals(startMarginPx, marginLayoutParams.getMarginStart()); + assertEquals(startMarginPx, marginLayoutParams.getMarginEnd()); + verify(mView).setLayoutParams(eq(marginLayoutParams)); + + // Verifies that setLayoutParams() isn't called again whether there isn't any change to the + // width of the view. + mRecyclerView.onDrawImpl(mView, 3, measuredWidth); + assertEquals(MATCH_PARENT, marginLayoutParams.width); + verify(mView).setLayoutParams(eq(marginLayoutParams)); + } + + @Test + @SmallTest + public void testOnDraw_MultipleItermsPerScreen() { + int itemPerScreen = 2; + int startMarginPx = 0; + int measuredWidth = 500; + mRecyclerView.initialize(/* isTablet= */ true, startMarginPx, itemPerScreen); + + MarginLayoutParams marginLayoutParams = new MarginLayoutParams(100, 100); + when(mView.getLayoutParams()).thenReturn(marginLayoutParams); + startMarginPx = 10; + mRecyclerView.setStartMarginPxForTesting(startMarginPx); + int expectedWidth = + (measuredWidth - mModuleInternalPaddingPx * (itemPerScreen - 1)) / itemPerScreen; + + // Verifies the width becomes the half of the parent's width. + mRecyclerView.onDrawImpl(mView, 3, measuredWidth); + assertEquals(expectedWidth, marginLayoutParams.width); + assertEquals(startMarginPx, marginLayoutParams.getMarginStart()); + assertEquals(startMarginPx, marginLayoutParams.getMarginEnd()); + verify(mView).setLayoutParams(eq(marginLayoutParams)); + + // Verifies that setLayoutParams() isn't called again whether there isn't any change to the + // width of the view. + mRecyclerView.onDrawImpl(mView, 3, measuredWidth); + assertEquals(expectedWidth, marginLayoutParams.width); + verify(mView).setLayoutParams(eq(marginLayoutParams)); + } +}
diff --git a/chrome/browser/metrics/chrome_metrics_services_manager_client.h b/chrome/browser/metrics/chrome_metrics_services_manager_client.h index 1d8ca0f..96c2695 100644 --- a/chrome/browser/metrics/chrome_metrics_services_manager_client.h +++ b/chrome/browser/metrics/chrome_metrics_services_manager_client.h
@@ -27,7 +27,6 @@ // Used only for testing. namespace internal { -// TODO(crbug.com/1068796): Replace kMetricsReportingFeature with a better name. BASE_DECLARE_FEATURE(kMetricsReportingFeature); #if BUILDFLAG(IS_ANDROID) BASE_DECLARE_FEATURE(kPostFREFixMetricsReportingFeature);
diff --git a/chrome/browser/metrics/ukm_browsertest.cc b/chrome/browser/metrics/ukm_browsertest.cc index 61ffca7..c6263a0 100644 --- a/chrome/browser/metrics/ukm_browsertest.cc +++ b/chrome/browser/metrics/ukm_browsertest.cc
@@ -232,8 +232,6 @@ class UkmBrowserTestBase : public SyncTest { public: UkmBrowserTestBase() : SyncTest(SINGLE_CLIENT) { - // TODO(crbug.com/1068796): Replace kMetricsReportingFeature with a more - // apt name. // Explicitly enable UKM and disable metrics reporting. Disabling metrics // reporting should affect only UMA--not UKM. scoped_feature_list_.InitWithFeatures({ukm::kUkmFeature},
diff --git a/chrome/browser/password_manager/chrome_password_manager_client_unittest.cc b/chrome/browser/password_manager/chrome_password_manager_client_unittest.cc index 4656937..c7504e0c 100644 --- a/chrome/browser/password_manager/chrome_password_manager_client_unittest.cc +++ b/chrome/browser/password_manager/chrome_password_manager_client_unittest.cc
@@ -265,6 +265,8 @@ const std::u16string& credential) override {} void PreviewField(autofill::FieldRendererId field_id, const std::u16string& value) override {} + void FillField(autofill::FieldRendererId field_id, + const std::u16string& value) override {} void AnnotateFieldsWithParsingResult( const autofill::ParsingResult& parsing_result) override {}
diff --git a/chrome/browser/payments/android/service_worker_payment_app_bridge.cc b/chrome/browser/payments/android/service_worker_payment_app_bridge.cc index c8ff1580..74525f4 100644 --- a/chrome/browser/payments/android/service_worker_payment_app_bridge.cc +++ b/chrome/browser/payments/android/service_worker_payment_app_bridge.cc
@@ -15,7 +15,7 @@ #include "chrome/android/chrome_jni_headers/ServiceWorkerPaymentAppBridge_jni.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/profiles/profile_manager.h" -#include "chrome/browser/web_data_service_factory.h" +#include "chrome/browser/webdata_services/web_data_service_factory.h" #include "components/payments/content/android/payment_handler_host.h" #include "components/payments/content/payment_event_response_util.h" #include "components/payments/content/payment_handler_host.h"
diff --git a/chrome/browser/policy/configuration_policy_handler_list_factory.cc b/chrome/browser/policy/configuration_policy_handler_list_factory.cc index 1a8f483..bf1f775 100644 --- a/chrome/browser/policy/configuration_policy_handler_list_factory.cc +++ b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
@@ -2150,6 +2150,12 @@ client_certificates::prefs::kProvisionManagedClientCertificateForUserPrefs, base::Value::Type::INTEGER }, #endif // + +#if !BUILDFLAG(IS_FUCHSIA) + { key::kProductSpecificationsEnabled, + commerce::kProductSpecificationsEnabledPrefName, + base::Value::Type::BOOLEAN}, +#endif // !BUILDFLAG(IS_FUCHSIA) }; // clang-format on
diff --git a/chrome/browser/profile_resetter/profile_resetter_test_base.cc b/chrome/browser/profile_resetter/profile_resetter_test_base.cc index 723dfb4a..4695332 100644 --- a/chrome/browser/profile_resetter/profile_resetter_test_base.cc +++ b/chrome/browser/profile_resetter/profile_resetter_test_base.cc
@@ -14,7 +14,7 @@ #include "chrome/browser/profiles/profile.h" #include "chrome/browser/search_engine_choice/search_engine_choice_service_factory.h" #include "chrome/browser/search_engines/ui_thread_search_terms_data.h" -#include "chrome/browser/web_data_service_factory.h" +#include "chrome/browser/webdata_services/web_data_service_factory.h" #include "components/keyed_service/core/service_access_type.h" #include "components/search_engines/template_url_service.h" #include "components/search_engines/template_url_service_client.h"
diff --git a/chrome/browser/profile_resetter/profile_resetter_unittest.cc b/chrome/browser/profile_resetter/profile_resetter_unittest.cc index 4592edae..3a2cbeaa 100644 --- a/chrome/browser/profile_resetter/profile_resetter_unittest.cc +++ b/chrome/browser/profile_resetter/profile_resetter_unittest.cc
@@ -36,7 +36,7 @@ #include "chrome/browser/themes/theme_service.h" #include "chrome/browser/themes/theme_service_factory.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" -#include "chrome/browser/web_data_service_factory.h" +#include "chrome/browser/webdata_services/web_data_service_factory.h" #include "chrome/common/pref_names.h" #include "chrome/test/base/browser_with_test_window_test.h" #include "components/content_settings/core/browser/content_settings_info.h"
diff --git a/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc b/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc index 1afa4e1..d68150a 100644 --- a/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc +++ b/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
@@ -203,8 +203,8 @@ #include "chrome/browser/updates/announcement_notification/announcement_notification_service_factory.h" #include "chrome/browser/usb/usb_chooser_context_factory.h" #include "chrome/browser/web_applications/isolated_web_apps/isolated_web_app_reader_registry_factory.h" -#include "chrome/browser/web_data_service_factory.h" #include "chrome/browser/webauthn/enclave_manager_factory.h" +#include "chrome/browser/webdata_services/web_data_service_factory.h" #include "chrome/browser/webid/federated_identity_api_permission_context_factory.h" #include "chrome/browser/webid/federated_identity_auto_reauthn_permission_context_factory.h" #include "chrome/browser/webid/federated_identity_permission_context_factory.h"
diff --git a/chrome/browser/profiles/profile_statistics_aggregator.cc b/chrome/browser/profiles/profile_statistics_aggregator.cc index abbce1c..fb21307a 100644 --- a/chrome/browser/profiles/profile_statistics_aggregator.cc +++ b/chrome/browser/profiles/profile_statistics_aggregator.cc
@@ -17,8 +17,8 @@ #include "chrome/browser/profiles/profile_manager.h" #include "chrome/browser/profiles/profile_statistics.h" #include "chrome/browser/profiles/profile_statistics_factory.h" -#include "chrome/browser/web_data_service_factory.h" #include "chrome/browser/webauthn/chrome_authenticator_request_delegate.h" +#include "chrome/browser/webdata_services/web_data_service_factory.h" #include "components/browsing_data/core/counters/autofill_counter.h" #include "components/browsing_data/core/counters/bookmark_counter.h" #include "components/browsing_data/core/counters/history_counter.h"
diff --git a/chrome/browser/profiles/profile_statistics_unittest.cc b/chrome/browser/profiles/profile_statistics_unittest.cc index f7e4b51..670d1f7 100644 --- a/chrome/browser/profiles/profile_statistics_unittest.cc +++ b/chrome/browser/profiles/profile_statistics_unittest.cc
@@ -24,7 +24,7 @@ #include "chrome/browser/sync/account_bookmark_sync_service_factory.h" #include "chrome/browser/sync/local_or_syncable_bookmark_sync_service_factory.h" #include "chrome/browser/undo/bookmark_undo_service_factory.h" -#include "chrome/browser/web_data_service_factory.h" +#include "chrome/browser/webdata_services/web_data_service_factory.h" #include "chrome/common/pref_names.h" #include "chrome/test/base/testing_browser_process.h" #include "chrome/test/base/testing_profile.h"
diff --git a/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/ReadAloudController.java b/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/ReadAloudController.java index f7d2628..d6efc2ba 100644 --- a/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/ReadAloudController.java +++ b/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/ReadAloudController.java
@@ -17,6 +17,7 @@ import org.chromium.base.ApplicationState; import org.chromium.base.ApplicationStatus; import org.chromium.base.Log; +import org.chromium.base.ObserverList; import org.chromium.base.Promise; import org.chromium.base.ResettersForTesting; import org.chromium.base.UserData; @@ -78,8 +79,7 @@ private static final Class<RestoreState> USER_DATA_KEY = RestoreState.class; private final Activity mActivity; private final ObservableSupplier<Profile> mProfileSupplier; - private final ObservableSupplierImpl<String> mReadabilitySupplier = - new ObservableSupplierImpl(); + private final ObserverList<Runnable> mReadabilityUpdateObserverList = new ObserverList<>(); private final Map<String, String> mSanitizedToFullUrlMap = new HashMap<>(); private final Map<String, Boolean> mReadabilityMap = new HashMap<>(); private final Map<String, Boolean> mTimepointsSupportedMap = new HashMap<>(); @@ -239,9 +239,45 @@ private final ObservableSupplierImpl<String> mSelectedVoiceId; private final ActivityWindowAndroid mActivityWindowAndroid; - private long mTranslationObserverHandle; - private final TranslationObserver mTranslationObserver = - new TranslationObserver() { + /** + * Wrapper for TranslationObserver that keeps track of the tab it is observing and the pointer + * to the underlying native observer so that callers don't need to manage them. + */ + private static class TranslationObserverImpl implements TranslationObserver { + private Tab mTab; + private long mHandle; + + void observeTab(Tab tab) { + if (mTab != null) { + stopObservingTab(mTab); + } + + WebContents webContents = tab.getWebContents(); + if (webContents == null) { + return; + } + + mHandle = TranslateBridge.addTranslationObserver(webContents, this); + mTab = tab; + } + + void stopObservingTab(Tab tab) { + if (mTab == null || mTab != tab) { + return; + } + + WebContents webContents = tab.getWebContents(); + if (webContents != null) { + TranslateBridge.removeTranslationObserver(webContents, mHandle); + } + + mTab = null; + mHandle = 0L; + } + } + + private final TranslationObserverImpl mPlayingTabTranslationObserver = + new TranslationObserverImpl() { @Override public void onIsPageTranslatedChanged(WebContents webContents) { if (mCurrentlyPlayingTab != null) { @@ -258,6 +294,20 @@ } }; + private final TranslationObserverImpl mCurrentTabTranslationObserver = + new TranslationObserverImpl() { + @Override + public void onIsPageTranslatedChanged(WebContents webContents) { + notifyReadabilityMayHaveChanged(); + } + + @Override + public void onPageTranslated( + String sourceLanguage, String translatedLanguage, int errorCode) { + notifyReadabilityMayHaveChanged(); + } + }; + /** * Kicks of readability check on a page load iff: the url is valid, no previous result is * available/pending and if a request has to be sent, the necessary conditions are satisfied. @@ -283,7 +333,7 @@ mReadabilityMap.put(url, isReadable); mTimepointsSupportedMap.put(url, timepointsSupported); mPendingRequests.remove(url); - mReadabilitySupplier.set(mSanitizedToFullUrlMap.get(url)); + notifyReadabilityMayHaveChanged(); } @Override @@ -332,9 +382,6 @@ mActivityLifecycleDispatcher.register(this); } - public ObservableSupplier<String> getReadabilitySupplier() { - return mReadabilitySupplier; - } private void onProfileAvailable(Profile profile) { mProfile = profile; mReadabilityHooks = @@ -360,6 +407,7 @@ ReadAloudMetrics.recordIneligibilityReason( ReadAloudFeatures.getIneligibilityReason()); } + mCurrentTabTranslationObserver.observeTab(tab); } @Override @@ -378,10 +426,11 @@ tab.getUserDataHost().setUserData(USER_DATA_KEY, state); } maybeStopPlayback(tab); + mCurrentTabTranslationObserver.stopObservingTab(tab); } @Override - protected void onTabSelected(Tab tab) { + public void onTabSelected(Tab tab) { super.onTabSelected(tab); if (tab != null && tab.getUrl() != null) { Log.d( @@ -414,6 +463,7 @@ updatedRestored.restore(); tab.getUserDataHost().removeUserData(USER_DATA_KEY); } + mCurrentTabTranslationObserver.observeTab(tab); } } @@ -421,6 +471,14 @@ public void willCloseTab(Tab tab) { Log.d(TAG, "WillCloseTab"); maybeStopPlayback(tab); + mPlayingTabTranslationObserver.stopObservingTab(tab); + mCurrentTabTranslationObserver.stopObservingTab(tab); + } + + @Override + public void onDestroyed(Tab tab) { + mPlayingTabTranslationObserver.stopObservingTab(tab); + mCurrentTabTranslationObserver.stopObservingTab(tab); } }; @@ -523,6 +581,27 @@ return false; } + /** + * Add a runnable to be called when new readability information is available for any page. + * Listeners can then call isReadable() to check a tab's readability. + * + * @param runnable Runnable called when a readability check succeeds or when a page is + * translated. + */ + public void addReadabilityUpdateListener(Runnable runnable) { + mReadabilityUpdateObserverList.addObserver(runnable); + } + + /** + * Remove a runnable previously registered with addReadabilityUpdateListener. No effect if + * runnable was not added. + * + * @param runnable Runnable to remove. + */ + public void removeReadabilityUpdateListener(Runnable runnable) { + mReadabilityUpdateObserverList.removeObserver(runnable); + } + /** Returns true if the tab's current language is supported by the available voices. */ private boolean isTabLanguageSupported(Tab tab) { if (mReadabilityHooks == null) { @@ -597,11 +676,7 @@ resetCurrentPlayback(); mCurrentlyPlayingTab = tab; - mTranslationObserverHandle = - mCurrentlyPlayingTab.getWebContents() != null - ? TranslateBridge.addTranslationObserver( - mCurrentlyPlayingTab.getWebContents(), mTranslationObserver) - : 0L; + mPlayingTabTranslationObserver.observeTab(mCurrentlyPlayingTab); if (!mPlaybackHooks.voicesInitialized()) { mPlaybackHooks.initVoices(); @@ -677,12 +752,7 @@ mPlayback = null; mPlayerCoordinator.recordPlaybackDuration(); } - if (mTranslationObserverHandle != 0L) { - assert mCurrentlyPlayingTab != null; - TranslateBridge.removeTranslationObserver( - mCurrentlyPlayingTab.getWebContents(), mTranslationObserverHandle); - mTranslationObserverHandle = 0L; - } + mPlayingTabTranslationObserver.stopObservingTab(mCurrentlyPlayingTab); mCurrentlyPlayingTab = null; mGlobalRenderFrameId = null; mCurrentPlaybackData = null; @@ -1154,6 +1224,12 @@ mOnUserLeaveHint = true; } + private void notifyReadabilityMayHaveChanged() { + for (Runnable observer : mReadabilityUpdateObserverList) { + observer.run(); + } + } + // Tests. public void setHighlighterForTests(Highlighter highighter) { mHighlighter = highighter; @@ -1172,7 +1248,11 @@ } public TranslationObserver getTranslationObserverForTest() { - return mTranslationObserver; + return mPlayingTabTranslationObserver; + } + + public TranslationObserver getCurrentTabTranslationObserverForTest() { + return mCurrentTabTranslationObserver; } @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
diff --git a/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/ReadAloudControllerUnitTest.java b/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/ReadAloudControllerUnitTest.java index b377499..726b3ca3 100644 --- a/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/ReadAloudControllerUnitTest.java +++ b/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/ReadAloudControllerUnitTest.java
@@ -214,6 +214,10 @@ HistogramWatcher.newSingleRecordWatcher( "ReadAloud.HighlightingEnabled.OnStartup", true); + mTab = mTabModelSelector.getCurrentTab(); + mTab.setGurlOverrideForTesting(sTestGURL); + mTab.setWebContentsOverrideForTesting(mWebContents); + mController = new ReadAloudController( mActivity, @@ -227,10 +231,6 @@ mActivityLifecycleDispatcher); ShadowLooper.runUiThreadTasksIncludingDelayedTasks(); - mTab = mTabModelSelector.getCurrentTab(); - mTab.setGurlOverrideForTesting(sTestGURL); - mTab.setWebContentsOverrideForTesting(mWebContents); - when(mMetadata.languageCode()).thenReturn("en"); when(mPlayback.getMetadata()).thenReturn(mMetadata); when(mWebContents.getMainFrame()).thenReturn(mRenderFrameHost); @@ -487,6 +487,7 @@ @Test public void checkReadabilityOnPageLoad_URLnotReadAloudSupported() { + reset(mHooksImpl); checkURLNotReadAloudSupported(new GURL("invalid")); checkURLNotReadAloudSupported(GURL.emptyGURL()); checkURLNotReadAloudSupported(new GURL("chrome://history/")); @@ -1328,24 +1329,66 @@ // Play tab. requestAndStartPlayback(); - assertEquals(1, mFakeTranslateBridge.getObserverCount()); + // One observer should be registered on the playing tab to stop playback if translated, and + // one is registered regardless of playback for refreshing the entrypoint. + assertEquals(2, mFakeTranslateBridge.getObserverCount()); - // stopping playback should unregister a listener + // stopping playback should unregister the listener that stops playback mController.maybeStopPlayback(mTab); - assertEquals(0, mFakeTranslateBridge.getObserverCount()); + assertEquals(1, mFakeTranslateBridge.getObserverCount()); } @Test public void testTranslationListenerRegistration_nullWebContents() { + assertEquals(1, mFakeTranslateBridge.getObserverCount()); + // Play tab. when(mTab.getWebContents()).thenReturn(null); requestAndStartPlayback(); + assertEquals(1, mFakeTranslateBridge.getObserverCount()); + + mController.maybeStopPlayback(mTab); + assertEquals(1, mFakeTranslateBridge.getObserverCount()); + } + + @Test + public void testTranslationListenersUnregisteredOnTabDestroyed() { + // Play tab. + requestAndStartPlayback(); + // One observer should be registered on the playing tab to stop playback if translated, and + // one is registered regardless of playback for refreshing the entrypoint. + assertEquals(2, mFakeTranslateBridge.getObserverCount()); + + // Both should be removed if the tab is destroyed. + mController.getTabModelTabObserverforTests().onDestroyed(mTab); + assertEquals(0, mFakeTranslateBridge.getObserverCount()); + } + + @Test + public void testTranslationListenerRegisteredOnPageLoad() { + // Listener should be registered already because onTabSelected() is called when + // TabModelTabObserver is created. + assertEquals(1, mFakeTranslateBridge.getObserverCount()); + + // Destroying tab should remove the observer. + mController.getTabModelTabObserverforTests().onDestroyed(mTab); assertEquals(0, mFakeTranslateBridge.getObserverCount()); - // stopping playback should not a listener - mController.maybeStopPlayback(mTab); - assertEquals(0, mFakeTranslateBridge.getObserverCount()); + // Observer should register again on page load. To keep the test simple we'll reuse the same + // tab even though it was used with onDestroyed(). + mController.getTabModelTabObserverforTests().onPageLoadStarted(mTab, sTestGURL); + assertEquals(1, mFakeTranslateBridge.getObserverCount()); + } + + @Test + public void testTranslationListenersUnregistered_nullWebContents() { + assertEquals(1, mFakeTranslateBridge.getObserverCount()); + + // If tab has null web contents, we should not try to remove translation observers. + doReturn(null).when(mTab).getWebContents(); + mController.getTabModelTabObserverforTests().onDestroyed(mTab); + assertEquals(1, mFakeTranslateBridge.getObserverCount()); } @Test @@ -1396,6 +1439,19 @@ } @Test + public void testPageTranslatedNotifiesReadabilityChanged() { + Runnable runnable = Mockito.mock(Runnable.class); + mController.addReadabilityUpdateListener(runnable); + + var translationObserver = mController.getCurrentTabTranslationObserverForTest(); + translationObserver.onPageTranslated("en", "es", 1); + verify(runnable, times(1)).run(); + + translationObserver.onIsPageTranslatedChanged(null); + verify(runnable, times(2)).run(); + } + + @Test public void testStoppingAnyPlayback() { // Play tab. requestAndStartPlayback(); @@ -1448,13 +1504,14 @@ public void testReadabilitySupplier() { String testUrl = "https://en.wikipedia.org/wiki/Google"; + Runnable runnable = Mockito.mock(Runnable.class); + mController.addReadabilityUpdateListener(runnable); mController.maybeCheckReadability(new GURL(testUrl)); verify(mHooksImpl, times(1)).isPageReadable(eq(testUrl), mCallbackCaptor.capture()); mCallbackCaptor.getValue().onSuccess(testUrl, true, false); - - assertEquals(mController.getReadabilitySupplier().get(), testUrl); + verify(runnable).run(); } @Test @@ -1898,6 +1955,7 @@ // TODO(b/322052505): This test won't be necessary if we keep track of profile changes. @Test public void testNoRequestsIfProfileDestroyed() { + reset(mHooksImpl); doReturn(false).when(mMockProfile).isNativeInitialized(); mController = new ReadAloudController(
diff --git a/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/ReadAloudIPHController.java b/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/ReadAloudIPHController.java index 2b2f115..732a141 100644 --- a/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/ReadAloudIPHController.java +++ b/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/ReadAloudIPHController.java
@@ -73,23 +73,22 @@ /** * If the current tab is readable, requests to show a "Listen to this page" IPH for the app menu * and turns on the highlight for the ReadAloud item in the menu. - * - * @param url URL the readability check returns */ - public void maybeShowReadAloudAppMenuIPH(String url) { - if (shouldShowIPH(url)) { - mUserEducationHelper.requestShowIPH( - new IPHCommandBuilder( - mToolbarMenuButton.getContext().getResources(), - FeatureConstants.READ_ALOUD_APP_MENU_FEATURE, - R.string.menu_listen_to_this_page, - R.string.menu_listen_to_this_page) - .setAnchorView(mToolbarMenuButton) - .setOnShowCallback( - () -> turnOnHighlightForMenuItem(R.id.readaloud_menu_id)) - .setOnDismissCallback(this::turnOffHighlightForMenuItem) - .build()); + public void maybeShowReadAloudAppMenuIPH() { + if (!shouldShowIPH()) { + return; } + + mUserEducationHelper.requestShowIPH( + new IPHCommandBuilder( + mToolbarMenuButton.getContext().getResources(), + FeatureConstants.READ_ALOUD_APP_MENU_FEATURE, + R.string.menu_listen_to_this_page, + R.string.menu_listen_to_this_page) + .setAnchorView(mToolbarMenuButton) + .setOnShowCallback(() -> turnOnHighlightForMenuItem(R.id.readaloud_menu_id)) + .setOnDismissCallback(this::turnOffHighlightForMenuItem) + .build()); } private void turnOnHighlightForMenuItem(int highlightMenuItemId) { @@ -100,28 +99,24 @@ mAppMenuHandler.clearMenuHighlight(); } - protected boolean shouldShowIPH(String url) { - if (mCurrentTabSupplier.get() == null - || !mCurrentTabSupplier.get().getUrl().isValid() - || mReadAloudControllerSupplier.get() == null) { + protected boolean shouldShowIPH() { + if (mCurrentTabSupplier.get() == null || !mCurrentTabSupplier.get().getUrl().isValid()) { return false; } - if (mCurrentTabSupplier.get().getUrl().getSpec().equals(url)) { - return mReadAloudControllerSupplier.get().isReadable(mCurrentTabSupplier.get()); - } - return false; + return mReadAloudControllerSupplier.get().isReadable(mCurrentTabSupplier.get()); } void readAloudControllerReady(@Nullable ReadAloudController readAloudController) { if (readAloudController != null) { - mReadAloudReadabilitySupplier = readAloudController.getReadabilitySupplier(); - mReadAloudReadabilitySupplier.addObserver(this::maybeShowReadAloudAppMenuIPH); + readAloudController.addReadabilityUpdateListener(this::maybeShowReadAloudAppMenuIPH); } } public void destroy() { - if (mReadAloudReadabilitySupplier != null) { - mReadAloudReadabilitySupplier.removeObserver(this::maybeShowReadAloudAppMenuIPH); + if (mReadAloudControllerSupplier.get() != null) { + mReadAloudControllerSupplier + .get() + .removeReadabilityUpdateListener(this::maybeShowReadAloudAppMenuIPH); } } }
diff --git a/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/ReadAloudIPHControllerUnitTest.java b/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/ReadAloudIPHControllerUnitTest.java index db854e46..9787ba9 100644 --- a/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/ReadAloudIPHControllerUnitTest.java +++ b/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/ReadAloudIPHControllerUnitTest.java
@@ -90,7 +90,7 @@ @Test @SmallTest public void maybeShowReadAloudAppMenuIPH() { - mController.maybeShowReadAloudAppMenuIPH(sTestGURL.getSpec()); + mController.maybeShowReadAloudAppMenuIPH(); verify(mUserEducationHelper).requestShowIPH(mIPHCommandCaptor.capture()); IPHCommand command = mIPHCommandCaptor.getValue(); @@ -106,22 +106,16 @@ public void maybeShowReadAloudAppMenuIPH_false() { doReturn(false).when(mReadAloudController).isReadable(mTab); - mController.maybeShowReadAloudAppMenuIPH(sTestGURL.getSpec()); + mController.maybeShowReadAloudAppMenuIPH(); verify(mUserEducationHelper, never()).requestShowIPH(mIPHCommandCaptor.capture()); } @Test @SmallTest public void maybeShowReadAloudAppMenuIPH_invalid() { - // mismatched urls - mController.maybeShowReadAloudAppMenuIPH("https://en.wikipedia.org/wiki/Google"); - verify(mUserEducationHelper, never()).requestShowIPH(mIPHCommandCaptor.capture()); - // invalid url - mController.maybeShowReadAloudAppMenuIPH("http://0x100.0/"); - verify(mUserEducationHelper, never()).requestShowIPH(mIPHCommandCaptor.capture()); // null tab doReturn(null).when(mMockTabProvider).get(); - mController.maybeShowReadAloudAppMenuIPH(sTestGURL.getSpec()); + mController.maybeShowReadAloudAppMenuIPH(); verify(mUserEducationHelper, never()).requestShowIPH(mIPHCommandCaptor.capture()); } }
diff --git a/chrome/browser/resources/ash/settings/device_page/display.html b/chrome/browser/resources/ash/settings/device_page/display.html index a13adea..cb40244 100644 --- a/chrome/browser/resources/ash/settings/device_page/display.html +++ b/chrome/browser/resources/ash/settings/device_page/display.html
@@ -46,6 +46,20 @@ </style> +<!-- Display Shiny Performance Mode Controller --> +<div id="displayPerformanceModeSubsection" class="settings-box" + hidden="[[!isDisplayPerformanceSupported_]]"> + <div id="displayPerformanceModeLabel" + class="start text-area" aria-hidden="true"> + <!-- TODO(b/326270858): Translate the label --> + $i18n{displayShinyPerformanceLabel} + </div> + <cr-toggle id="displayPerformanceModeToggle" + aria-labelledby="displayPerformanceModeLabel" + on-change="toggleDisplayPerformanceEnabled_"> + </cr-toggle> +</div> + <!-- Night Light Settings --> <template is="dom-if" if="[[isRevampWayfindingEnabled_]]" restamp>
diff --git a/chrome/browser/resources/ash/settings/device_page/display.ts b/chrome/browser/resources/ash/settings/device_page/display.ts index 5df5ead..3212605d9 100644 --- a/chrome/browser/resources/ash/settings/device_page/display.ts +++ b/chrome/browser/resources/ash/settings/device_page/display.ts
@@ -166,6 +166,13 @@ }, }, + isDisplayPerformanceSupported_: { + type: Boolean, + value() { + return loadTimeData.getBoolean('isDisplayPerformanceSupported'); + }, + }, + ambientColorAvailable_: { type: Boolean, value() { @@ -190,6 +197,11 @@ value: false, }, + isDisplayPerformanceEnabled_: { + type: Boolean, + value: false, + }, + selectedParentModePref_: { type: Object, value: function() { @@ -263,6 +275,7 @@ private displaySettingsProvider: DisplaySettingsProviderInterface; private displayTabNames_: string[]; private invalidDisplayId_: string; + private isDisplayPerformanceEnabled_: boolean; private readonly isRevampWayfindingEnabled_: boolean; private isTabletMode_: boolean; private listAllDisplayModes_: boolean; @@ -1375,6 +1388,11 @@ } } + private toggleDisplayPerformanceEnabled_(): void { + this.isDisplayPerformanceEnabled_ = !this.isDisplayPerformanceEnabled_; + // TODO(b/320526769): Create a mojom call to Ash + } + getInvalidDisplayId(): string { return this.invalidDisplayId_; }
diff --git a/chrome/browser/resources/settings/clear_browsing_data_dialog/clear_browsing_data_dialog.ts b/chrome/browser/resources/settings/clear_browsing_data_dialog/clear_browsing_data_dialog.ts index 8aaac67..41957c5 100644 --- a/chrome/browser/resources/settings/clear_browsing_data_dialog/clear_browsing_data_dialog.ts +++ b/chrome/browser/resources/settings/clear_browsing_data_dialog/clear_browsing_data_dialog.ts
@@ -537,18 +537,6 @@ private async clearBrowsingData_() { this.clearingInProgress_ = true; this.clearingDataAlertString_ = loadTimeData.getString('clearingData'); - const tab = this.$.tabs.selectedItem as HTMLElement; - const dataTypes = this.getSelectedDataTypes_(tab); - const dropdownMenu = - tab.querySelector<SettingsDropdownMenuElement>('.time-range-select'); - assert(dropdownMenu); - const timePeriod = dropdownMenu.pref!.value; - - if (tab.id === 'basic-tab') { - chrome.metricsPrivate.recordUserAction('ClearBrowsingData_BasicTab'); - } else { - chrome.metricsPrivate.recordUserAction('ClearBrowsingData_AdvancedTab'); - } this.setPrefValue( 'browser.last_clear_browsing_data_tab', this.selectedTabIndex_); @@ -563,6 +551,19 @@ 'settings-dropdown-menu[no-set-pref]') .forEach(dropdown => dropdown.sendPrefChange()); + const tab = this.$.tabs.selectedItem as HTMLElement; + const dataTypes = this.getSelectedDataTypes_(tab); + const dropdownMenu = + tab.querySelector<SettingsDropdownMenuElement>('.time-range-select'); + assert(dropdownMenu); + const timePeriod = dropdownMenu.pref!.value; + + if (tab.id === 'basic-tab') { + chrome.metricsPrivate.recordUserAction('ClearBrowsingData_BasicTab'); + } else { + chrome.metricsPrivate.recordUserAction('ClearBrowsingData_AdvancedTab'); + } + const {showHistoryNotice, showPasswordsNotice} = await this.browserProxy_.clearBrowsingData(dataTypes, timePeriod); this.clearingInProgress_ = false;
diff --git a/chrome/browser/resources/settings/privacy_page/privacy_page.html b/chrome/browser/resources/settings/privacy_page/privacy_page.html index d014f08..da791bad 100644 --- a/chrome/browser/resources/settings/privacy_page/privacy_page.html +++ b/chrome/browser/resources/settings/privacy_page/privacy_page.html
@@ -1288,40 +1288,38 @@ </category-setting-exceptions> </settings-subpage> </template> - <template is="dom-if" if="[[enablePermissionStorageAccessApi_]]"> - <template is="dom-if" route-path="/content/storageAccess" no-search> - <settings-subpage page-title="$i18n{siteSettingsStorageAccess}" - search-label="$i18n{siteSettingsAllSitesSearch}" - search-term="{{searchFilter_}}"> - <div class="content-settings-header secondary"> - $i18n{storageAccessDescription} + <template is="dom-if" route-path="/content/storageAccess" no-search> + <settings-subpage page-title="$i18n{siteSettingsStorageAccess}" + search-label="$i18n{siteSettingsAllSitesSearch}" + search-term="{{searchFilter_}}"> + <div class="content-settings-header secondary"> + $i18n{storageAccessDescription} + </div> + <settings-category-default-radio-group + category="[[contentSettingsTypesEnum_.STORAGE_ACCESS]]" + allow-option-label="$i18n{storageAccessAllowed}" + allow-option-icon="settings:storage-access" + block-option-label="$i18n{storageAccessBlocked}" + block-option-icon="settings:storage-access-off"> + </settings-category-default-radio-group> + <!-- Exceptions section. --> + <div class="content-settings-header"> + <h2>$i18n{siteSettingsCustomizedBehaviors}</h2> + <div class="cr-secondary-text"> + $i18n{siteSettingsCustomizedBehaviorsDescription} </div> - <settings-category-default-radio-group - category="[[contentSettingsTypesEnum_.STORAGE_ACCESS]]" - allow-option-label="$i18n{storageAccessAllowed}" - allow-option-icon="settings:storage-access" - block-option-label="$i18n{storageAccessBlocked}" - block-option-icon="settings:storage-access-off"> - </settings-category-default-radio-group> - <!-- Exceptions section. --> - <div class="content-settings-header"> - <h2>$i18n{siteSettingsCustomizedBehaviors}</h2> - <div class="cr-secondary-text"> - $i18n{siteSettingsCustomizedBehaviorsDescription} - </div> - </div> - <storage-access-site-list - category-subtype="[[contentSettingEnum_.BLOCK]]" - category-header="$i18n{storageAccessBlockedExceptions}" - search-filter="[[searchFilter_]]"> - </storage-access-site-list> - <storage-access-site-list - category-subtype="[[contentSettingEnum_.ALLOW]]" - category-header="$i18n{storageAccessAllowedExceptions}" - search-filter="[[searchFilter_]]"> - </storage-access-site-list> - </settings-subpage> - </template> + </div> + <storage-access-site-list + category-subtype="[[contentSettingEnum_.BLOCK]]" + category-header="$i18n{storageAccessBlockedExceptions}" + search-filter="[[searchFilter_]]"> + </storage-access-site-list> + <storage-access-site-list + category-subtype="[[contentSettingEnum_.ALLOW]]" + category-header="$i18n{storageAccessAllowedExceptions}" + search-filter="[[searchFilter_]]"> + </storage-access-site-list> + </settings-subpage> </template> <template is="dom-if" if="[[autoPictureInPictureEnabled_]]"> <template is="dom-if" route-path="/content/autoPictureInPicture" no-search>
diff --git a/chrome/browser/resources/settings/privacy_page/privacy_page.ts b/chrome/browser/resources/settings/privacy_page/privacy_page.ts index 483404b..d3b8b4d 100644 --- a/chrome/browser/resources/settings/privacy_page/privacy_page.ts +++ b/chrome/browser/resources/settings/privacy_page/privacy_page.ts
@@ -188,12 +188,6 @@ value: () => loadTimeData.getBoolean('privateStateTokensEnabled'), }, - enablePermissionStorageAccessApi_: { - type: Boolean, - value: () => - loadTimeData.getBoolean('enablePermissionStorageAccessApi'), - }, - autoPictureInPictureEnabled_: { type: Boolean, value: () => loadTimeData.getBoolean('autoPictureInPictureEnabled'), @@ -354,7 +348,6 @@ private privateStateTokensEnabled_: boolean; private autoPictureInPictureEnabled_: boolean; private safetyCheckNotificationPermissionsEnabled_: boolean; - private enablePermissionStorageAccessApi_: boolean; private enableSafetyHub_: boolean; private focusConfig_: FocusConfig; private searchFilter_: string;
diff --git a/chrome/browser/resources/settings/route.ts b/chrome/browser/resources/settings/route.ts index d9e4f7b..0bf8676 100644 --- a/chrome/browser/resources/settings/route.ts +++ b/chrome/browser/resources/settings/route.ts
@@ -138,11 +138,8 @@ r.SITE_SETTINGS_FILE_SYSTEM_WRITE_DETAILS = r.SITE_SETTINGS_FILE_SYSTEM_WRITE.createChild('siteDetails'); r.SITE_SETTINGS_LOCAL_FONTS = r.SITE_SETTINGS.createChild('localFonts'); + r.SITE_SETTINGS_STORAGE_ACCESS = r.SITE_SETTINGS.createChild('storageAccess'); - if (loadTimeData.getBoolean('enablePermissionStorageAccessApi')) { - r.SITE_SETTINGS_STORAGE_ACCESS = - r.SITE_SETTINGS.createChild('storageAccess'); - } if (loadTimeData.getBoolean('enableAutomaticFullscreenContentSetting')) { r.SITE_SETTINGS_AUTOMATIC_FULLSCREEN = r.SITE_SETTINGS.createChild('automaticFullscreen');
diff --git a/chrome/browser/resources/settings/site_settings_page/site_settings_page.ts b/chrome/browser/resources/settings/site_settings_page/site_settings_page.ts index e2135e18..87626786 100644 --- a/chrome/browser/resources/settings/site_settings_page/site_settings_page.ts +++ b/chrome/browser/resources/settings/site_settings_page/site_settings_page.ts
@@ -339,8 +339,6 @@ icon: 'settings:storage-access', enabledLabel: 'storageAccessAllowed', disabledLabel: 'storageAccessBlocked', - shouldShow: () => - loadTimeData.getBoolean('enablePermissionStorageAccessApi'), }, { route: routes.SITE_SETTINGS_USB_DEVICES, @@ -449,45 +447,36 @@ lists_: { type: Object, value: function() { - // Move `BACKGROUND_SYNC` to the sixth position under the fold if - // `STORAGE_ACCESS` is present. - const enablePermissionStorageAccessApi = - loadTimeData.getBoolean('enablePermissionStorageAccessApi'); - const basic = enablePermissionStorageAccessApi ? Id.STORAGE_ACCESS : - Id.BACKGROUND_SYNC; - const advanced: ContentSettingsTypes[] = - enablePermissionStorageAccessApi ? [Id.BACKGROUND_SYNC] : []; - return { permissionsBasic: buildItemListFromIds([ Id.GEOLOCATION, Id.CAMERA, Id.MIC, Id.NOTIFICATIONS, - basic, + Id.STORAGE_ACCESS, ]), permissionsAdvanced: buildItemListFromIds([ - ...advanced, - ...[Id.SENSORS, - Id.AUTOMATIC_DOWNLOADS, - Id.PROTOCOL_HANDLERS, - Id.MIDI_DEVICES, - Id.USB_DEVICES, - Id.SERIAL_PORTS, - Id.BLUETOOTH_DEVICES, - Id.FILE_SYSTEM_WRITE, - Id.HID_DEVICES, - Id.CLIPBOARD, - Id.PAYMENT_HANDLER, - Id.BLUETOOTH_SCANNING, - Id.AR, - Id.VR, - Id.IDLE_DETECTION, - Id.WEB_PRINTING, - Id.WINDOW_MANAGEMENT, - Id.LOCAL_FONTS, - Id.AUTO_PICTURE_IN_PICTURE, - ], + Id.BACKGROUND_SYNC, + Id.SENSORS, + Id.AUTOMATIC_DOWNLOADS, + Id.PROTOCOL_HANDLERS, + Id.MIDI_DEVICES, + Id.USB_DEVICES, + Id.SERIAL_PORTS, + Id.BLUETOOTH_DEVICES, + Id.FILE_SYSTEM_WRITE, + Id.HID_DEVICES, + Id.CLIPBOARD, + Id.PAYMENT_HANDLER, + Id.BLUETOOTH_SCANNING, + Id.AR, + Id.VR, + Id.IDLE_DETECTION, + Id.WEB_PRINTING, + Id.WINDOW_MANAGEMENT, + Id.LOCAL_FONTS, + Id.AUTO_PICTURE_IN_PICTURE, + ]), contentBasic: buildItemListFromIds([ Id.COOKIES,
diff --git a/chrome/browser/search_engines/template_url_service_factory.cc b/chrome/browser/search_engines/template_url_service_factory.cc index b0b827b..9adb3ee 100644 --- a/chrome/browser/search_engines/template_url_service_factory.cc +++ b/chrome/browser/search_engines/template_url_service_factory.cc
@@ -18,7 +18,7 @@ #include "chrome/browser/search_engine_choice/search_engine_choice_service_factory.h" #include "chrome/browser/search_engines/chrome_template_url_service_client.h" #include "chrome/browser/search_engines/ui_thread_search_terms_data.h" -#include "chrome/browser/web_data_service_factory.h" +#include "chrome/browser/webdata_services/web_data_service_factory.h" #include "components/keyed_service/content/browser_context_keyed_service_factory.h" #include "components/omnibox/common/omnibox_features.h" #include "components/pref_registry/pref_registry_syncable.h"
diff --git a/chrome/browser/signin/identity_manager_factory.cc b/chrome/browser/signin/identity_manager_factory.cc index 1519d08..969620b 100644 --- a/chrome/browser/signin/identity_manager_factory.cc +++ b/chrome/browser/signin/identity_manager_factory.cc
@@ -33,7 +33,7 @@ #endif #if BUILDFLAG(ENABLE_DICE_SUPPORT) -#include "chrome/browser/web_data_service_factory.h" +#include "chrome/browser/webdata_services/web_data_service_factory.h" #include "components/keyed_service/core/service_access_type.h" #if BUILDFLAG(ENABLE_BOUND_SESSION_CREDENTIALS) #include "chrome/browser/signin/bound_session_credentials/unexportable_key_service_factory.h"
diff --git a/chrome/browser/storage_access_api/api_browsertest.cc b/chrome/browser/storage_access_api/api_browsertest.cc index 9906b12..e195ee8d 100644 --- a/chrome/browser/storage_access_api/api_browsertest.cc +++ b/chrome/browser/storage_access_api/api_browsertest.cc
@@ -480,58 +480,16 @@ std::unique_ptr<permissions::MockPermissionPromptFactory> prompt_factory_; }; -struct TestCase { - std::string test_name; - bool permission_saa_feature_enabled; -}; - // Test fixture for core Storage Access API functionality, guaranteed by spec. // This fixture should use the minimal set of features/params. -class StorageAccessAPIBrowserTest - : public StorageAccessAPIBaseBrowserTest, - public testing::WithParamInterface<TestCase> { +class StorageAccessAPIBrowserTest : public StorageAccessAPIBaseBrowserTest { public: StorageAccessAPIBrowserTest() : StorageAccessAPIBaseBrowserTest(/*is_storage_partitioned=*/false) {} - - protected: - std::vector<base::test::FeatureRefAndParams> GetEnabledFeatures() override { - std::vector<base::test::FeatureRefAndParams> enabled; - - if (GetParam().permission_saa_feature_enabled) { - enabled.push_back( - {permissions::features::kPermissionStorageAccessAPI, {}}); - } - - return enabled; - } - - std::vector<base::test::FeatureRef> GetDisabledFeatures() override { - std::vector<base::test::FeatureRef> disabled = - StorageAccessAPIBaseBrowserTest::GetDisabledFeatures(); - - if (!GetParam().permission_saa_feature_enabled) { - disabled.push_back(permissions::features::kPermissionStorageAccessAPI); - } - - return disabled; - } -}; - -class StorageAccessAPIWithPromptsBrowserTest - : public StorageAccessAPIBaseBrowserTest { - public: - StorageAccessAPIWithPromptsBrowserTest() - : StorageAccessAPIBaseBrowserTest(/* is_storage_partitioned=*/true) {} - - protected: - std::vector<base::test::FeatureRefAndParams> GetEnabledFeatures() override { - return {{permissions::features::kPermissionStorageAccessAPI, {}}}; - } }; // Check default values for permissions.query on storage-access. -IN_PROC_BROWSER_TEST_P(StorageAccessAPIBrowserTest, PermissionQueryDefault) { +IN_PROC_BROWSER_TEST_F(StorageAccessAPIBrowserTest, PermissionQueryDefault) { SetBlockThirdPartyCookies(true); NavigateToPageWithFrame(kHostA); @@ -543,7 +501,7 @@ // Check default values for permissions.query on storage-access when 3p cookie // is allowed. -IN_PROC_BROWSER_TEST_P(StorageAccessAPIBrowserTest, +IN_PROC_BROWSER_TEST_F(StorageAccessAPIBrowserTest, PermissionQueryDefault_AllowCrossSiteCookie) { SetBlockThirdPartyCookies(false); @@ -556,8 +514,7 @@ // Test that permissions.query changes to "granted" when a storage access // request was successful. -IN_PROC_BROWSER_TEST_F(StorageAccessAPIWithPromptsBrowserTest, - PermissionQueryGranted) { +IN_PROC_BROWSER_TEST_F(StorageAccessAPIBrowserTest, PermissionQueryGranted) { SetBlockThirdPartyCookies(true); NavigateToPageWithFrame(kHostA); @@ -607,8 +564,7 @@ // Test that permissions.query changes to "denied" when a storage access // request was denied. -IN_PROC_BROWSER_TEST_F(StorageAccessAPIWithPromptsBrowserTest, - PermissionQueryDenied) { +IN_PROC_BROWSER_TEST_F(StorageAccessAPIBrowserTest, PermissionQueryDenied) { SetBlockThirdPartyCookies(true); EnsureUserInteractionOn(kHostB); @@ -656,8 +612,7 @@ UnorderedElementsAre(Pair(net::SchemefulSite(GURL(kOriginB)), false))); } -IN_PROC_BROWSER_TEST_F(StorageAccessAPIWithPromptsBrowserTest, - PermissionQueryCrossSite) { +IN_PROC_BROWSER_TEST_F(StorageAccessAPIBrowserTest, PermissionQueryCrossSite) { SetBlockThirdPartyCookies(true); EnsureUserInteractionOn(kHostA); @@ -684,7 +639,7 @@ EXPECT_EQ(QueryPermission(GetFrame()), "prompt"); } -IN_PROC_BROWSER_TEST_P(StorageAccessAPIBrowserTest, +IN_PROC_BROWSER_TEST_F(StorageAccessAPIBrowserTest, Permission_Denied_WithoutInteraction) { base::HistogramTester histogram_tester; SetBlockThirdPartyCookies(true); @@ -715,7 +670,7 @@ // Validate that a cross-site iframe can bypass third-party cookie blocking via // the Storage Access API. -IN_PROC_BROWSER_TEST_F(StorageAccessAPIWithPromptsBrowserTest, +IN_PROC_BROWSER_TEST_F(StorageAccessAPIBrowserTest, ThirdPartyCookiesIFrameRequestsAccess_CrossSiteIframe) { SetBlockThirdPartyCookies(true); @@ -732,7 +687,7 @@ EXPECT_EQ(ReadCookies(GetFrame(), kHostB), CookieBundle("cross-site=b.test")); } -IN_PROC_BROWSER_TEST_F(StorageAccessAPIWithPromptsBrowserTest, +IN_PROC_BROWSER_TEST_F(StorageAccessAPIBrowserTest, AccessGranted_NoSubsequentUserInteraction) { SetBlockThirdPartyCookies(true); prompt_factory()->set_response_type( @@ -758,7 +713,7 @@ // just that top-level/third-party combination and are still blocked for other // combinations. IN_PROC_BROWSER_TEST_F( - StorageAccessAPIWithPromptsBrowserTest, + StorageAccessAPIBrowserTest, ThirdPartyCookiesIFrameRequestsAccess_CrossSiteIframe_UnrelatedSites) { SetBlockThirdPartyCookies(true); base::HistogramTester histogram_tester; @@ -790,7 +745,7 @@ // Validate that a nested A(B(B)) iframe can obtain cookie access, and that that // access is not shared with the "middle" B iframe. IN_PROC_BROWSER_TEST_F( - StorageAccessAPIWithPromptsBrowserTest, + StorageAccessAPIBrowserTest, ThirdPartyCookiesIFrameRequestsAccess_NestedCrossSiteIframe_InnerRequestsAccess) { SetBlockThirdPartyCookies(true); @@ -819,7 +774,7 @@ // Validate that in a A(B) frame tree, the iframe can make credentialed // same-site requests, even if the requests are cross-origin. -IN_PROC_BROWSER_TEST_F(StorageAccessAPIWithPromptsBrowserTest, +IN_PROC_BROWSER_TEST_F(StorageAccessAPIBrowserTest, ThirdPartyCookiesIFrameRequestsAccess_CrossOriginFetch) { SetBlockThirdPartyCookies(true); @@ -841,7 +796,7 @@ // Validate that in a A(B(B)) frame tree, the middle B iframe can obtain access, // and that access is not shared with the leaf B iframe. IN_PROC_BROWSER_TEST_F( - StorageAccessAPIWithPromptsBrowserTest, + StorageAccessAPIBrowserTest, ThirdPartyCookiesIFrameRequestsAccess_NestedCrossSiteIframe_MiddleRequestsAccess) { SetBlockThirdPartyCookies(true); @@ -870,7 +825,7 @@ // Validate that in a A(B(C)) frame tree, the C leaf iframe can obtain cookie // access. IN_PROC_BROWSER_TEST_F( - StorageAccessAPIWithPromptsBrowserTest, + StorageAccessAPIBrowserTest, ThirdPartyCookiesIFrameRequestsAccess_NestedCrossSiteIframe_DistinctSites) { SetBlockThirdPartyCookies(true); EnsureUserInteractionOn(kHostC); @@ -894,7 +849,7 @@ // Validate that cross-site sibling iframes cannot take advantage of each // other's granted permission. -IN_PROC_BROWSER_TEST_F(StorageAccessAPIWithPromptsBrowserTest, +IN_PROC_BROWSER_TEST_F(StorageAccessAPIBrowserTest, ThirdPartyCookiesCrossSiteSiblingIFrameRequestsAccess) { EnsureUserInteractionOn(kHostC); @@ -956,7 +911,7 @@ // Validate that the Storage Access API does not override any explicit user // settings to block storage access. -IN_PROC_BROWSER_TEST_F(StorageAccessAPIWithPromptsBrowserTest, +IN_PROC_BROWSER_TEST_F(StorageAccessAPIBrowserTest, ThirdPartyCookiesIFrameThirdPartyExceptions) { SetBlockThirdPartyCookies(true); BlockAllCookiesOnHost(kHostB); @@ -975,7 +930,7 @@ // Validate that user settings take precedence for the leaf in a A(B(B)) frame // tree. IN_PROC_BROWSER_TEST_F( - StorageAccessAPIWithPromptsBrowserTest, + StorageAccessAPIBrowserTest, ThirdPartyCookiesIFrameThirdPartyExceptions_NestedSameSite) { SetBlockThirdPartyCookies(true); BlockAllCookiesOnHost(kHostB); @@ -998,7 +953,7 @@ // Validate that user settings take precedence for the leaf in a A(B(C)) frame // tree. IN_PROC_BROWSER_TEST_F( - StorageAccessAPIWithPromptsBrowserTest, + StorageAccessAPIBrowserTest, ThirdPartyCookiesIFrameThirdPartyExceptions_NestedCrossSite) { EnsureUserInteractionOn(kHostC); @@ -1023,7 +978,7 @@ // Validate that user settings take precedence for the leaf in a A(B(A)) frame // tree. IN_PROC_BROWSER_TEST_F( - StorageAccessAPIWithPromptsBrowserTest, + StorageAccessAPIBrowserTest, ThirdPartyCookiesIFrameThirdPartyExceptions_CrossSiteAncestorChain) { EnsureUserInteractionOn(kHostA); SetBlockThirdPartyCookies(true); @@ -1048,7 +1003,7 @@ // Validate that user settings take precedence for the leaf in a A(A) frame // tree. IN_PROC_BROWSER_TEST_F( - StorageAccessAPIWithPromptsBrowserTest, + StorageAccessAPIBrowserTest, ThirdPartyCookiesIFrameThirdPartyExceptions_SameSiteAncestorChain) { EnsureUserInteractionOn(kHostA); SetBlockThirdPartyCookies(true); @@ -1068,7 +1023,7 @@ } // Validates that once a grant is removed access is also removed. -IN_PROC_BROWSER_TEST_F(StorageAccessAPIWithPromptsBrowserTest, +IN_PROC_BROWSER_TEST_F(StorageAccessAPIBrowserTest, ThirdPartyGrantsDeletedAccess) { SetBlockThirdPartyCookies(true); @@ -1086,6 +1041,15 @@ HostContentSettingsMap* settings_map = HostContentSettingsMapFactory::GetForProfile(browser()->profile()); settings_map->ClearSettingsForOneType(ContentSettingsType::STORAGE_ACCESS); + + // Try to ensure that the pref observer is triggered and the updated settings + // are propagated to the network service. + base::RunLoop().RunUntilIdle(); + browser() + ->profile() + ->GetDefaultStoragePartition() + ->FlushNetworkInterfaceForTesting(); + // Verify cookie cannot be accessed. EXPECT_EQ(ReadCookies(GetFrame(), kHostB), NoCookies()); @@ -1096,7 +1060,7 @@ // Validates that if the user explicitly blocks cookies, cookie access is // blocked even with the existing grant. -IN_PROC_BROWSER_TEST_F(StorageAccessAPIWithPromptsBrowserTest, +IN_PROC_BROWSER_TEST_F(StorageAccessAPIBrowserTest, ExplicitUserSettingsBlockThirdPartyGrantsAccess) { SetBlockThirdPartyCookies(true); @@ -1111,6 +1075,13 @@ EXPECT_EQ(ReadCookies(GetFrame(), kHostB), CookieBundle("cross-site=b.test")); BlockAllCookiesOnHost(kHostB); + // Try to ensure that the pref observer is triggered and the updated settings + // are propagated to the network service. + base::RunLoop().RunUntilIdle(); + browser() + ->profile() + ->GetDefaultStoragePartition() + ->FlushNetworkInterfaceForTesting(); // Access change should be reflected immediately. EXPECT_EQ(ReadCookiesAndContent(GetFrame(), kHostB), NoCookiesWithContent()); @@ -1122,8 +1093,7 @@ // Validate that if the iframe's origin is opaque, it cannot obtain storage // access. -IN_PROC_BROWSER_TEST_F(StorageAccessAPIWithPromptsBrowserTest, - OpaqueOriginRejects) { +IN_PROC_BROWSER_TEST_F(StorageAccessAPIBrowserTest, OpaqueOriginRejects) { SetBlockThirdPartyCookies(true); NavigateToPageWithFrame(kHostA); @@ -1141,7 +1111,7 @@ // Validate that if the iframe is sandboxed and allows scripts but is missing // the Storage Access sandbox tag, the iframe cannot obtain storage access. -IN_PROC_BROWSER_TEST_F(StorageAccessAPIWithPromptsBrowserTest, +IN_PROC_BROWSER_TEST_F(StorageAccessAPIBrowserTest, MissingSandboxTokenRejects) { SetBlockThirdPartyCookies(true); @@ -1161,8 +1131,7 @@ // Validate that if the iframe is sandboxed and has the Storage Access sandbox // tag, the iframe can obtain storage access. -IN_PROC_BROWSER_TEST_F(StorageAccessAPIWithPromptsBrowserTest, - SandboxTokenResolves) { +IN_PROC_BROWSER_TEST_F(StorageAccessAPIBrowserTest, SandboxTokenResolves) { SetBlockThirdPartyCookies(true); NavigateToPageWithFrame(kHostA); @@ -1180,8 +1149,7 @@ } // Validates that expired grants don't get reused. -IN_PROC_BROWSER_TEST_F(StorageAccessAPIWithPromptsBrowserTest, - ThirdPartyGrantsExpiry) { +IN_PROC_BROWSER_TEST_F(StorageAccessAPIBrowserTest, ThirdPartyGrantsExpiry) { base::HistogramTester histogram_tester; SetBlockThirdPartyCookies(true); @@ -1240,7 +1208,7 @@ // Validate that if an iframe navigates itself to a same-origin endpoint, and // that navigation does not include any cross-origin redirects, the new document // can inherit storage access. -IN_PROC_BROWSER_TEST_F(StorageAccessAPIWithPromptsBrowserTest, +IN_PROC_BROWSER_TEST_F(StorageAccessAPIBrowserTest, Navigation_SelfInitiated_SameOrigin_Preserves) { SetBlockThirdPartyCookies(true); @@ -1266,7 +1234,7 @@ // same-origin endpoint, and that navigation does not include any cross-origin // redirects, the new document cannot inherit storage access. IN_PROC_BROWSER_TEST_F( - StorageAccessAPIWithPromptsBrowserTest, + StorageAccessAPIBrowserTest, Navigation_NonSelfInitiated_SameOriginDestination_CrossSiteInitiator) { SetBlockThirdPartyCookies(true); @@ -1293,7 +1261,7 @@ // same-origin endpoint (even if the navigation does not include any // cross-origin redirects), the new document cannot inherit storage access. IN_PROC_BROWSER_TEST_F( - StorageAccessAPIWithPromptsBrowserTest, + StorageAccessAPIBrowserTest, Navigation_NonSelfInitiated_SameOriginDestination_SameSiteInitiator) { SetBlockThirdPartyCookies(true); @@ -1325,7 +1293,7 @@ // cross-origin redirects, and the navigated frame has obtained storage access // already), the new document cannot inherit storage access. IN_PROC_BROWSER_TEST_F( - StorageAccessAPIWithPromptsBrowserTest, + StorageAccessAPIBrowserTest, Navigation_NonSelfInitiated_SameOriginDestination_SameSiteInitiator_TargetHasStorageAccess) { SetBlockThirdPartyCookies(true); @@ -1357,7 +1325,7 @@ // Validate that if an iframe navigates itself to a same-site cross-origin // endpoint, and that navigation does not include any cross-origin redirects, // the new document cannot inherit storage access. -IN_PROC_BROWSER_TEST_F(StorageAccessAPIWithPromptsBrowserTest, +IN_PROC_BROWSER_TEST_F(StorageAccessAPIBrowserTest, Navigation_SelfInitiated_SameSiteCrossOrigin) { SetBlockThirdPartyCookies(true); @@ -1384,7 +1352,7 @@ // Validate that if an iframe navigates itself to a cross-site endpoint, and // that navigation does not include any cross-origin redirects, the new document // cannot inherit storage access. -IN_PROC_BROWSER_TEST_F(StorageAccessAPIWithPromptsBrowserTest, +IN_PROC_BROWSER_TEST_F(StorageAccessAPIBrowserTest, Navigation_SelfInitiated_CrossSite) { SetBlockThirdPartyCookies(true); @@ -1409,7 +1377,7 @@ // that navigation include a cross-origin redirect, the new document // cannot inherit storage access. IN_PROC_BROWSER_TEST_F( - StorageAccessAPIWithPromptsBrowserTest, + StorageAccessAPIBrowserTest, Navigation_SelfInitiated_SameOrigin_CrossOriginRedirect) { SetBlockThirdPartyCookies(true); @@ -1442,7 +1410,7 @@ // subsequent same-origin redirect), the new document cannot inherit storage // access. IN_PROC_BROWSER_TEST_F( - StorageAccessAPIWithPromptsBrowserTest, + StorageAccessAPIBrowserTest, Navigation_SelfInitiated_SameOrigin_CrossSiteAndSameSiteRedirects) { SetBlockThirdPartyCookies(true); @@ -1472,7 +1440,7 @@ // Validate that in a A(A) frame tree, the inner A iframe can obtain cookie // access by default. -IN_PROC_BROWSER_TEST_F(StorageAccessAPIWithPromptsBrowserTest, +IN_PROC_BROWSER_TEST_F(StorageAccessAPIBrowserTest, EmbeddedSameOriginCookieAccess) { SetBlockThirdPartyCookies(true); @@ -1490,7 +1458,7 @@ // Validate that in a A(sub.A) frame tree, the inner A iframe can obtain cookie // access by default. -IN_PROC_BROWSER_TEST_F(StorageAccessAPIWithPromptsBrowserTest, +IN_PROC_BROWSER_TEST_F(StorageAccessAPIBrowserTest, EmbeddedSameSiteCookieAccess) { SetBlockThirdPartyCookies(true); @@ -1510,7 +1478,7 @@ // Validate that in a A(B(A)) frame tree, the inner A iframe can obtain cookie // access after requesting access. -IN_PROC_BROWSER_TEST_F(StorageAccessAPIWithPromptsBrowserTest, +IN_PROC_BROWSER_TEST_F(StorageAccessAPIBrowserTest, NestedSameOriginCookieAccess_CrossSiteAncestorChain) { base::HistogramTester histogram_tester; SetBlockThirdPartyCookies(true); @@ -1537,7 +1505,7 @@ // Validate that in a A(B(sub.A)) frame tree, the inner iframe can obtain cookie // access after requesting access. -IN_PROC_BROWSER_TEST_F(StorageAccessAPIWithPromptsBrowserTest, +IN_PROC_BROWSER_TEST_F(StorageAccessAPIBrowserTest, NestedSameSiteCookieAccess_CrossSiteAncestorChain) { SetBlockThirdPartyCookies(true); @@ -1558,7 +1526,7 @@ CookieBundle("cross-site=a.test")); } -IN_PROC_BROWSER_TEST_F(StorageAccessAPIWithPromptsBrowserTest, +IN_PROC_BROWSER_TEST_F(StorageAccessAPIBrowserTest, DedicatedWorker_InheritsStorageAccessFromDocument) { SetBlockThirdPartyCookies(true); prompt_factory()->set_response_type( @@ -1584,7 +1552,7 @@ "cross-site=b.test"); } -IN_PROC_BROWSER_TEST_F(StorageAccessAPIWithPromptsBrowserTest, +IN_PROC_BROWSER_TEST_F(StorageAccessAPIBrowserTest, WebsocketRequestsUseStorageAccessGrants) { net::SpawnedTestServer wss_server( net::SpawnedTestServer::TYPE_WSS, @@ -1628,20 +1596,9 @@ } } -INSTANTIATE_TEST_SUITE_P( - /* no prefix */, - StorageAccessAPIBrowserTest, - testing::ValuesIn<TestCase>({ - {"enable_prompts", true}, - {"disable_prompts", false}, - }), - [](const testing::TestParamInfo<::TestCase>& info) { - return info.param.test_name; - }); - // Validate that in a A(B) frame tree, the embedded B iframe can obtain cookie // access if requested and got accepted. -IN_PROC_BROWSER_TEST_F(StorageAccessAPIWithPromptsBrowserTest, +IN_PROC_BROWSER_TEST_F(StorageAccessAPIBrowserTest, EmbeddedCrossSiteCookieAccess_Accept) { SetBlockThirdPartyCookies(true); @@ -1659,7 +1616,7 @@ // Validate that in a A(B) frame tree, the embedded B iframe can not obtain // cookie access if requested and got denied. -IN_PROC_BROWSER_TEST_F(StorageAccessAPIWithPromptsBrowserTest, +IN_PROC_BROWSER_TEST_F(StorageAccessAPIBrowserTest, EmbeddedCrossSiteCookieAccess_Deny) { SetBlockThirdPartyCookies(true); @@ -1843,8 +1800,7 @@ testing::Bool())); class StorageAccessAPIWithFirstPartySetsBrowserTest - : public StorageAccessAPIBaseBrowserTest, - public testing::WithParamInterface<bool> { + : public StorageAccessAPIBaseBrowserTest { public: StorageAccessAPIWithFirstPartySetsBrowserTest() : StorageAccessAPIBaseBrowserTest(false) {} @@ -1857,30 +1813,9 @@ R"(", "associatedSites": ["https://)", kHostB, R"("])", R"(, "serviceSites": ["https://)", kHostD, R"("]})"})); } - - protected: - std::vector<base::test::FeatureRefAndParams> GetEnabledFeatures() override { - std::vector<base::test::FeatureRefAndParams> enabled = {}; - - if (PermissionStorageAccessAPIFeatureEnabled()) { - enabled.push_back( - {permissions::features::kPermissionStorageAccessAPI, {}}); - } - return enabled; - } - - std::vector<base::test::FeatureRef> GetDisabledFeatures() override { - std::vector<base::test::FeatureRef> disabled; - if (!PermissionStorageAccessAPIFeatureEnabled()) { - disabled.push_back(permissions::features::kPermissionStorageAccessAPI); - } - return disabled; - } - - bool PermissionStorageAccessAPIFeatureEnabled() { return GetParam(); } }; -IN_PROC_BROWSER_TEST_P(StorageAccessAPIWithFirstPartySetsBrowserTest, +IN_PROC_BROWSER_TEST_F(StorageAccessAPIWithFirstPartySetsBrowserTest, Permission_AutograntedWithinFirstPartySet) { base::HistogramTester histogram_tester; // Note: kHostA and kHostB are considered same-party due to the use of @@ -1908,7 +1843,7 @@ Gt(0)); } -IN_PROC_BROWSER_TEST_P(StorageAccessAPIWithFirstPartySetsBrowserTest, +IN_PROC_BROWSER_TEST_F(StorageAccessAPIWithFirstPartySetsBrowserTest, Permission_PromptOrDenyUnderServiceDomain) { prompt_factory()->set_response_type( permissions::PermissionRequestManager::DENY_ALL); @@ -1927,8 +1862,7 @@ // The promise should be rejected; `kHostD` is a service domain. EXPECT_FALSE(content::ExecJs(GetFrame(), "document.requestStorageAccess()")); EXPECT_FALSE(storage::test::HasStorageAccessForFrame(GetFrame())); - EXPECT_EQ(prompt_factory()->TotalRequestCount(), - PermissionStorageAccessAPIFeatureEnabled() ? 1 : 0); + EXPECT_EQ(prompt_factory()->TotalRequestCount(), 1); NavigateFrameTo(EchoCookiesURL(kHostA)); @@ -1936,17 +1870,14 @@ EXPECT_FALSE(storage::test::HasStorageAccessForFrame(GetFrame())); content::FetchHistogramsFromChildProcesses(); - EXPECT_THAT(histogram_tester.GetBucketCount( - kRequestOutcomeHistogram, - PermissionStorageAccessAPIFeatureEnabled() - ? RequestOutcome::kDeniedByUser - : RequestOutcome::kDeniedByFirstPartySet), + EXPECT_THAT(histogram_tester.GetBucketCount(kRequestOutcomeHistogram, + RequestOutcome::kDeniedByUser), Gt(0)); // Ensure that the denied state is not exposed to developers, per the spec. EXPECT_EQ(QueryPermission(GetFrame()), "prompt"); } -IN_PROC_BROWSER_TEST_P( +IN_PROC_BROWSER_TEST_F( StorageAccessAPIWithFirstPartySetsBrowserTest, Permission_AutograntedForServiceDomainWithExistingGrant) { SetBlockThirdPartyCookies(true); @@ -1976,48 +1907,8 @@ EXPECT_EQ(QueryPermission(GetFrame()), "granted"); } -IN_PROC_BROWSER_TEST_P(StorageAccessAPIWithFirstPartySetsBrowserTest, - Permission_AutodeniedOutsideFirstPartySet) { - // If enabled, this will override and disable the autodenial, which does not - // belong in this test. - if (PermissionStorageAccessAPIFeatureEnabled()) { - return; - } - - base::HistogramTester histogram_tester; - // Note: kHostA and kHostC are considered cross-party, since kHostA's set does - // not include kHostC. - SetBlockThirdPartyCookies(true); - - NavigateToPageWithFrame(kHostA); - NavigateFrameTo(EchoCookiesURL(kHostC)); - - EXPECT_EQ(ReadCookiesAndContent(GetFrame(), kHostC), NoCookiesWithContent()); - EXPECT_FALSE(storage::test::HasStorageAccessForFrame(GetFrame())); - - // kHostC cannot request storage access. - EXPECT_FALSE(content::ExecJs(GetFrame(), "document.requestStorageAccess()")); - EXPECT_FALSE(storage::test::HasStorageAccessForFrame(GetFrame())); - - NavigateFrameTo(EchoCookiesURL(kHostC)); - - EXPECT_EQ(ReadCookiesAndContent(GetFrame(), kHostC), NoCookiesWithContent()); - EXPECT_FALSE(storage::test::HasStorageAccessForFrame(GetFrame())); - - content::FetchHistogramsFromChildProcesses(); - - EXPECT_THAT( - histogram_tester.GetBucketCount(kRequestOutcomeHistogram, - RequestOutcome::kDeniedByFirstPartySet), - Gt(0)); -} - -// Verifies kPermissionStorageAccessAPI feature turns off the autodenial. -IN_PROC_BROWSER_TEST_P(StorageAccessAPIWithFirstPartySetsBrowserTest, +IN_PROC_BROWSER_TEST_F(StorageAccessAPIWithFirstPartySetsBrowserTest, Permission_AutodeniedOutsideFirstPartySet_Overridden) { - if (!PermissionStorageAccessAPIFeatureEnabled()) { - return; - } base::HistogramTester histogram_tester; // Note: kHostA and kHostC are considered cross-party, since kHostA's set does // not include kHostC. @@ -2043,7 +1934,7 @@ Gt(0)); } -IN_PROC_BROWSER_TEST_P( +IN_PROC_BROWSER_TEST_F( StorageAccessAPIWithFirstPartySetsBrowserTest, Permission_AutodeniedInsideFirstPartySet_WithoutInteraction) { base::HistogramTester histogram_tester; @@ -2073,7 +1964,7 @@ Gt(0)); } -IN_PROC_BROWSER_TEST_P(StorageAccessAPIWithFirstPartySetsBrowserTest, +IN_PROC_BROWSER_TEST_F(StorageAccessAPIWithFirstPartySetsBrowserTest, PRE_PermissionGrantsResetAfterRestart) { SetBlockThirdPartyCookies(true); @@ -2084,7 +1975,7 @@ ASSERT_EQ("granted", QueryPermission(GetFrame())); } -IN_PROC_BROWSER_TEST_P(StorageAccessAPIWithFirstPartySetsBrowserTest, +IN_PROC_BROWSER_TEST_F(StorageAccessAPIWithFirstPartySetsBrowserTest, PermissionGrantsResetAfterRestart) { SetBlockThirdPartyCookies(true); @@ -2094,26 +1985,7 @@ EXPECT_EQ("prompt", QueryPermission(GetFrame())); } -INSTANTIATE_TEST_SUITE_P( - /* no prefix */, - StorageAccessAPIWithFirstPartySetsBrowserTest, - testing::Bool()); - -class StorageAccessAPIWithFirstPartySetsAndPromptsBrowserTest - : public StorageAccessAPIWithFirstPartySetsBrowserTest { - protected: - std::vector<base::test::FeatureRefAndParams> GetEnabledFeatures() override { - std::vector<base::test::FeatureRefAndParams> enabled = {}; - - if (PermissionStorageAccessAPIFeatureEnabled()) { - enabled.push_back( - {permissions::features::kPermissionStorageAccessAPI, {}}); - } - return enabled; - } -}; - -IN_PROC_BROWSER_TEST_P(StorageAccessAPIWithFirstPartySetsAndPromptsBrowserTest, +IN_PROC_BROWSER_TEST_F(StorageAccessAPIWithFirstPartySetsBrowserTest, Permission_GrantedForServiceDomain) { SetBlockThirdPartyCookies(true); base::HistogramTester histogram_tester; @@ -2136,11 +2008,6 @@ Gt(0)); } -INSTANTIATE_TEST_SUITE_P( - /* no prefix */, - StorageAccessAPIWithFirstPartySetsAndPromptsBrowserTest, - testing::Bool()); - class StorageAccessAPIWithFirstPartySetsAndImplicitGrantsBrowserTest : public StorageAccessAPIBaseBrowserTest { public: @@ -2148,11 +2015,6 @@ : StorageAccessAPIBaseBrowserTest(false) { StorageAccessGrantPermissionContext::SetImplicitGrantLimitForTesting(5); } - - protected: - std::vector<base::test::FeatureRefAndParams> GetEnabledFeatures() override { - return {}; - } }; // Validate that when auto-deny-outside-fps is disabled (but auto-grant is @@ -2182,7 +2044,7 @@ EXPECT_TRUE(storage::test::RequestAndCheckStorageAccessForFrame(GetFrame())); } -IN_PROC_BROWSER_TEST_F(StorageAccessAPIWithPromptsBrowserTest, +IN_PROC_BROWSER_TEST_F(StorageAccessAPIBrowserTest, RequestStorageAccess_CoexistsWithPartitionedCookies) { SetBlockThirdPartyCookies(true); @@ -2293,7 +2155,7 @@ storage::test::ExpectStorageForFrame(GetFrame(), !ExpectPartitionedStorage()); } -IN_PROC_BROWSER_TEST_F(StorageAccessAPIWithPromptsBrowserTest, +IN_PROC_BROWSER_TEST_F(StorageAccessAPIBrowserTest, EnsureOnePromptDenialSuffices) { SetBlockThirdPartyCookies(true); NavigateToPageWithFrame(kHostA); @@ -2327,7 +2189,7 @@ } } -IN_PROC_BROWSER_TEST_F(StorageAccessAPIWithPromptsBrowserTest, +IN_PROC_BROWSER_TEST_F(StorageAccessAPIBrowserTest, DismissalAllowsFuturePrompts) { SetBlockThirdPartyCookies(true); NavigateToPageWithFrame(kHostA); @@ -2365,7 +2227,7 @@ } } -IN_PROC_BROWSER_TEST_F(StorageAccessAPIWithPromptsBrowserTest, +IN_PROC_BROWSER_TEST_F(StorageAccessAPIBrowserTest, TopLevelUserInteractionRequired) { SetBlockThirdPartyCookies(true); @@ -2396,7 +2258,7 @@ EXPECT_EQ(ReadCookies(GetFrame(), kHostA), CookieBundle("cross-site=a.test")); } -IN_PROC_BROWSER_TEST_F(StorageAccessAPIWithPromptsBrowserTest, +IN_PROC_BROWSER_TEST_F(StorageAccessAPIBrowserTest, IncognitoDoesntUseRegularInteractionsOrPermission) { NavigateToPageWithFrame(kHostA); NavigateFrameTo(EchoCookiesURL(kHostB)); @@ -2422,8 +2284,7 @@ "document.requestStorageAccess()")); } -IN_PROC_BROWSER_TEST_F(StorageAccessAPIWithPromptsBrowserTest, - IncognitoCanUseAPI) { +IN_PROC_BROWSER_TEST_F(StorageAccessAPIBrowserTest, IncognitoCanUseAPI) { Browser* incognito_browser = Browser::Create(Browser::CreateParams( browser()->profile()->GetPrimaryOTRProfile(/*create_if_needed=*/true), /*user_gesture=*/true));
diff --git a/chrome/browser/storage_access_api/storage_access_grant_permission_context.cc b/chrome/browser/storage_access_api/storage_access_grant_permission_context.cc index ce39e22..566b8c2 100644 --- a/chrome/browser/storage_access_api/storage_access_grant_permission_context.cc +++ b/chrome/browser/storage_access_api/storage_access_grant_permission_context.cc
@@ -53,14 +53,6 @@ constexpr base::TimeDelta kStorageAccessAPITopLevelUserInteractionBound = base::Days(30); -// `kPermissionStorageAccessAPI` enables StorageAccessAPIwithPrompts -// (https://chromestatus.com/feature/5085655327047680), which should not -// auto-deny if FPS is irrelevant. -bool ShouldAutoDenyOutsideFPS() { - return !base::FeatureList::IsEnabled( - permissions::features::kPermissionStorageAccessAPI); -} - // Returns true if the request wasn't answered by the user explicitly. bool IsImplicitOutcome(RequestOutcome outcome) { switch (outcome) { @@ -343,14 +335,6 @@ break; } } - if (ShouldAutoDenyOutsideFPS()) { - NotifyPermissionSetInternal(request_data.id, request_data.requesting_origin, - request_data.embedding_origin, - std::move(callback), - /*persist=*/true, CONTENT_SETTING_BLOCK, - RequestOutcome::kDeniedByFirstPartySet); - return; - } // Get all of our implicit grants and see which ones apply to our // |requesting_origin|.
diff --git a/chrome/browser/storage_access_api/storage_access_grant_permission_context_unittest.cc b/chrome/browser/storage_access_api/storage_access_grant_permission_context_unittest.cc index 59f8a35d..7c1109b8 100644 --- a/chrome/browser/storage_access_api/storage_access_grant_permission_context_unittest.cc +++ b/chrome/browser/storage_access_api/storage_access_grant_permission_context_unittest.cc
@@ -82,27 +82,16 @@ base::NumberToString(dummy_id) + ".com"); } -struct TestCase { - std::string test_name; - bool permission_saa_feature_enabled; -}; - } // namespace -class StorageAccessGrantPermissionContextTestBase +class StorageAccessGrantPermissionContextTest : public ChromeRenderViewHostTestHarness { public: - StorageAccessGrantPermissionContextTestBase() = default; + StorageAccessGrantPermissionContextTest() = default; void SetUp() override { std::vector<base::test::FeatureRefAndParams> enabled; std::vector<base::test::FeatureRef> disabled; - if (PermissionStorageAccessAPIFeatureEnabled()) { - enabled.push_back( - {permissions::features::kPermissionStorageAccessAPI, {}}); - } else { - disabled.push_back(permissions::features::kPermissionStorageAccessAPI); - } features_.InitWithFeaturesAndParameters(enabled, disabled); ChromeRenderViewHostTestHarness::SetUp(); @@ -147,8 +136,6 @@ ChromeRenderViewHostTestHarness::TearDown(); } - virtual bool PermissionStorageAccessAPIFeatureEnabled() const = 0; - std::unique_ptr<base::test::TestFuture<ContentSetting>> DecidePermission( bool user_gesture) { auto future = std::make_unique<base::test::TestFuture<ContentSetting>>(); @@ -239,13 +226,6 @@ return *mock_permission_prompt_factory_; } - struct PrintToStringParamName { - std::string operator()( - const testing::TestParamInfo<::TestCase>& info) const { - return info.param.test_name; - } - }; - base::HistogramTester& histogram_tester() { return histogram_tester_; } private: @@ -259,28 +239,7 @@ first_party_sets::ScopedMockFirstPartySetsHandler first_party_sets_handler_; }; -class StorageAccessGrantPermissionContextTest - : public StorageAccessGrantPermissionContextTestBase, - public testing::WithParamInterface<TestCase> { - public: - StorageAccessGrantPermissionContextTest() = default; - - bool PermissionStorageAccessAPIFeatureEnabled() const override { - return GetParam().permission_saa_feature_enabled; - } -}; - -class StorageAccessGrantPermissionContextWithPromptsTest - : public StorageAccessGrantPermissionContextTestBase { - public: - StorageAccessGrantPermissionContextWithPromptsTest() = default; - - bool PermissionStorageAccessAPIFeatureEnabled() const override { - return true; - } -}; - -TEST_P(StorageAccessGrantPermissionContextTest, InsecureOriginsDisallowed) { +TEST_F(StorageAccessGrantPermissionContextTest, InsecureOriginsDisallowed) { GURL insecure_url = GURL("http://www.example.com"); EXPECT_FALSE(permission_context()->IsPermissionAvailableToOrigins( insecure_url, insecure_url)); @@ -294,7 +253,7 @@ // Test that after a successful explicit storage access grant, there's a content // setting that applies on an (embedded site, top-level site) scope. -TEST_F(StorageAccessGrantPermissionContextWithPromptsTest, +TEST_F(StorageAccessGrantPermissionContextTest, ExplicitGrantAcceptCrossSiteContentSettings) { // Assert that all content settings are in their initial state. CheckCrossSiteContentSettings(ContentSetting::CONTENT_SETTING_ASK); @@ -326,7 +285,7 @@ // When the Storage Access API feature is enabled and we have a user gesture we // should get a decision. -TEST_F(StorageAccessGrantPermissionContextWithPromptsTest, PermissionDecided) { +TEST_F(StorageAccessGrantPermissionContextTest, PermissionDecided) { auto future = DecidePermission(/*user_gesture=*/true); WaitUntilPrompt(); @@ -349,7 +308,7 @@ } // No user gesture should force a permission rejection. -TEST_P(StorageAccessGrantPermissionContextTest, +TEST_F(StorageAccessGrantPermissionContextTest, PermissionDeniedWithoutUserGesture) { EXPECT_EQ(CONTENT_SETTING_BLOCK, DecidePermissionSync(/*user_gesture=*/false)); @@ -361,7 +320,7 @@ IsEmpty()); } -TEST_P(StorageAccessGrantPermissionContextTest, PermissionGrantReused) { +TEST_F(StorageAccessGrantPermissionContextTest, PermissionGrantReused) { auto* map = HostContentSettingsMapFactory::GetForProfile(profile()); map->SetContentSettingDefaultScope(GetRequesterURL(), GetTopLevelURL(), ContentSettingsType::STORAGE_ACCESS, @@ -374,7 +333,7 @@ UnorderedElementsAre(Pair(GetRequesterSite(), true))); } -TEST_P(StorageAccessGrantPermissionContextTest, BlockReused) { +TEST_F(StorageAccessGrantPermissionContextTest, BlockReused) { auto* map = HostContentSettingsMapFactory::GetForProfile(profile()); map->SetContentSettingDefaultScope(GetRequesterURL(), GetTopLevelURL(), ContentSettingsType::STORAGE_ACCESS, @@ -387,7 +346,7 @@ UnorderedElementsAre(Pair(GetRequesterSite(), true))); } -TEST_P(StorageAccessGrantPermissionContextTest, FpsGrantReused) { +TEST_F(StorageAccessGrantPermissionContextTest, FpsGrantReused) { auto* map = HostContentSettingsMapFactory::GetForProfile(profile()); content_settings::ContentSettingConstraints constraint; constraint.set_session_model( @@ -404,7 +363,7 @@ IsEmpty()); } -TEST_P(StorageAccessGrantPermissionContextTest, +TEST_F(StorageAccessGrantPermissionContextTest, PermissionStatusAsksWhenFeatureEnabled) { EXPECT_EQ(PermissionStatus::ASK, permission_context() @@ -416,7 +375,7 @@ // When 3p cookie access is already allowed by user-agent-specific cookie // settings, request should be allowed without granting an explicit storage // access permission. -TEST_P(StorageAccessGrantPermissionContextTest, AllowedByCookieSettings) { +TEST_F(StorageAccessGrantPermissionContextTest, AllowedByCookieSettings) { // Allow 3p cookies. profile()->GetPrefs()->SetInteger( prefs::kCookieControlsMode, @@ -435,7 +394,7 @@ // When 3p cookie access is blocked by user explicitly, request should be denied // without prompting. -TEST_P(StorageAccessGrantPermissionContextTest, DeniedByCookieSettings) { +TEST_F(StorageAccessGrantPermissionContextTest, DeniedByCookieSettings) { HostContentSettingsMap* settings_map = HostContentSettingsMapFactory::GetForProfile(profile()); settings_map->SetContentSettingDefaultScope( @@ -453,17 +412,8 @@ IsEmpty()); } -INSTANTIATE_TEST_SUITE_P( - /* no prefix */, - StorageAccessGrantPermissionContextTest, - testing::ValuesIn<TestCase>({ - {"enable_prompts", true}, - {"disable_prompts", false}, - }), - StorageAccessGrantPermissionContextTest::PrintToStringParamName()); - class StorageAccessGrantPermissionContextAPIWithImplicitGrantsTest - : public StorageAccessGrantPermissionContextWithPromptsTest { + : public StorageAccessGrantPermissionContextTest { public: StorageAccessGrantPermissionContextAPIWithImplicitGrantsTest() { StorageAccessGrantPermissionContext::SetImplicitGrantLimitForTesting(5); @@ -593,8 +543,7 @@ IsEmpty()); } -TEST_F(StorageAccessGrantPermissionContextWithPromptsTest, - ExplicitGrantDenial) { +TEST_F(StorageAccessGrantPermissionContextTest, ExplicitGrantDenial) { histogram_tester().ExpectTotalCount(kGrantIsImplicitHistogram, 0); histogram_tester().ExpectTotalCount(kPromptResultHistogram, 0); @@ -618,7 +567,7 @@ UnorderedElementsAre(Pair(GetRequesterSite(), false))); } -TEST_F(StorageAccessGrantPermissionContextWithPromptsTest, +TEST_F(StorageAccessGrantPermissionContextTest, ExplicitGrantDenialNotExposedViaQuery) { // Set the content setting to blocked, mimicking a prompt rejection by the // user. @@ -649,8 +598,7 @@ UnorderedElementsAre(Pair(GetRequesterSite(), false))); } -TEST_F(StorageAccessGrantPermissionContextWithPromptsTest, - ExplicitGrantAccept) { +TEST_F(StorageAccessGrantPermissionContextTest, ExplicitGrantAccept) { histogram_tester().ExpectTotalCount(kGrantIsImplicitHistogram, 0); histogram_tester().ExpectTotalCount(kPromptResultHistogram, 0); @@ -675,16 +623,12 @@ } class StorageAccessGrantPermissionContextAPIWithFirstPartySetsTest - : public StorageAccessGrantPermissionContextTestBase { + : public StorageAccessGrantPermissionContextTest { public: StorageAccessGrantPermissionContextAPIWithFirstPartySetsTest() = default; - bool PermissionStorageAccessAPIFeatureEnabled() const override { - return false; - } - void SetUp() override { - StorageAccessGrantPermissionContextTestBase::SetUp(); + StorageAccessGrantPermissionContextTest::SetUp(); // Create a FPS with https://requester.example.com as the member and // https://embedder.com as the primary.
diff --git a/chrome/browser/sync/chrome_sync_client.cc b/chrome/browser/sync/chrome_sync_client.cc index 5b12a9e..7ad5b6e 100644 --- a/chrome/browser/sync/chrome_sync_client.cc +++ b/chrome/browser/sync/chrome_sync_client.cc
@@ -48,7 +48,7 @@ #include "chrome/browser/trusted_vault/trusted_vault_service_factory.h" #include "chrome/browser/ui/ui_features.h" #include "chrome/browser/web_applications/web_app_utils.h" -#include "chrome/browser/web_data_service_factory.h" +#include "chrome/browser/webdata_services/web_data_service_factory.h" #include "chrome/common/buildflags.h" #include "chrome/common/channel_info.h" #include "chrome/common/chrome_paths.h"
diff --git a/chrome/browser/sync/sync_service_factory.cc b/chrome/browser/sync/sync_service_factory.cc index 5a60daf..ee8cebe 100644 --- a/chrome/browser/sync/sync_service_factory.cc +++ b/chrome/browser/sync/sync_service_factory.cc
@@ -48,7 +48,7 @@ #include "chrome/browser/trusted_vault/trusted_vault_service_factory.h" #include "chrome/browser/undo/bookmark_undo_service_factory.h" #include "chrome/browser/web_applications/web_app_provider_factory.h" -#include "chrome/browser/web_data_service_factory.h" +#include "chrome/browser/webdata_services/web_data_service_factory.h" #include "chrome/common/buildflags.h" #include "chrome/common/channel_info.h" #include "components/password_manager/core/browser/sharing/password_receiver_service.h"
diff --git a/chrome/browser/sync/sync_service_factory_unittest.cc b/chrome/browser/sync/sync_service_factory_unittest.cc index 9b21479d..2222174 100644 --- a/chrome/browser/sync/sync_service_factory_unittest.cc +++ b/chrome/browser/sync/sync_service_factory_unittest.cc
@@ -14,7 +14,7 @@ #include "chrome/browser/sync/sync_service_factory.h" #include "chrome/browser/trusted_vault/trusted_vault_service_factory.h" #include "chrome/browser/ui/ui_features.h" -#include "chrome/browser/web_data_service_factory.h" +#include "chrome/browser/webdata_services/web_data_service_factory.h" #include "chrome/common/buildflags.h" #include "chrome/test/base/testing_profile.h" #include "components/data_sharing/public/features.h"
diff --git a/chrome/browser/sync/sync_service_util_browsertest.cc b/chrome/browser/sync/sync_service_util_browsertest.cc deleted file mode 100644 index b10f383f..0000000 --- a/chrome/browser/sync/sync_service_util_browsertest.cc +++ /dev/null
@@ -1,47 +0,0 @@ -// Copyright 2023 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "chrome/browser/sync/sync_service_util.h" - -#include "base/test/scoped_feature_list.h" -#include "build/build_config.h" -#include "chrome/browser/browser_process.h" -#include "chrome/test/base/in_process_browser_test.h" -#include "chrome/test/base/scoped_browser_locale.h" -#include "components/page_image_service/features.h" -#include "components/variations/service/variations_service.h" -#include "content/public/test/browser_test.h" - -class SyncSeviceUtilBrowserTest : public InProcessBrowserTest { - public: - SyncSeviceUtilBrowserTest() { - // The following feature does not affect the tests but it resets the - // experiment from field trial testing configuration. This is required - // because the tests verify the default behavior. - scoped_feature_list_.InitAndEnableFeature( - page_image_service::kImageServiceObserveSyncDownloadStatus); - } - - private: - base::test::ScopedFeatureList scoped_feature_list_; -}; - -IN_PROC_BROWSER_TEST_F(SyncSeviceUtilBrowserTest, - SyncPollEnabledBasedOnPlatformAndCountryLocale) { - auto locale = std::make_unique<ScopedBrowserLocale>("en-US"); - g_browser_process->variations_service()->OverrideStoredPermanentCountry("us"); -#if BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC) || \ - BUILDFLAG(IS_WIN) - EXPECT_TRUE(IsDesktopEnUSLocaleOnlySyncPollFeatureEnabled()); -#else - EXPECT_FALSE(IsDesktopEnUSLocaleOnlySyncPollFeatureEnabled()); -#endif -} - -IN_PROC_BROWSER_TEST_F(SyncSeviceUtilBrowserTest, - SyncPollDisabledBasedOnPlatformAndCountryLocale) { - auto locale = std::make_unique<ScopedBrowserLocale>("en-US"); - g_browser_process->variations_service()->OverrideStoredPermanentCountry("ca"); - EXPECT_FALSE(IsDesktopEnUSLocaleOnlySyncPollFeatureEnabled()); -}
diff --git a/chrome/browser/sync/test/integration/apps_helper.cc b/chrome/browser/sync/test/integration/apps_helper.cc index abf286d..24b31e8 100644 --- a/chrome/browser/sync/test/integration/apps_helper.cc +++ b/chrome/browser/sync/test/integration/apps_helper.cc
@@ -250,7 +250,7 @@ base::RunLoop run_loop; webapps::AppId app_id; auto* provider = web_app::WebAppProvider::GetForTest(profile); - provider->scheduler().InstallFromInfo( + provider->scheduler().InstallFromInfoNoIntegrationForTesting( std::make_unique<web_app::WebAppInstallInfo>(info.Clone()), /*overwrite_existing_manifest_fields=*/true, webapps::WebappInstallSource::OMNIBOX_INSTALL_ICON,
diff --git a/chrome/browser/sync/test/integration/autofill_helper.cc b/chrome/browser/sync/test/integration/autofill_helper.cc index 00684b9..aee95e6 100644 --- a/chrome/browser/sync/test/integration/autofill_helper.cc +++ b/chrome/browser/sync/test/integration/autofill_helper.cc
@@ -21,7 +21,7 @@ #include "chrome/browser/profiles/profile.h" #include "chrome/browser/sync/test/integration/sync_datatype_helper.h" #include "chrome/browser/sync/test/integration/sync_test.h" -#include "chrome/browser/web_data_service_factory.h" +#include "chrome/browser/webdata_services/web_data_service_factory.h" #include "components/autofill/core/browser/autofill_test_utils.h" #include "components/autofill/core/browser/autofill_type.h" #include "components/autofill/core/browser/country_type.h"
diff --git a/chrome/browser/sync/test/integration/single_client_wallet_sync_test.cc b/chrome/browser/sync/test/integration/single_client_wallet_sync_test.cc index 4bb7e14..1769f7a3 100644 --- a/chrome/browser/sync/test/integration/single_client_wallet_sync_test.cc +++ b/chrome/browser/sync/test/integration/single_client_wallet_sync_test.cc
@@ -19,7 +19,7 @@ #include "chrome/browser/sync/test/integration/sync_test.h" #include "chrome/browser/sync/test/integration/updated_progress_marker_checker.h" #include "chrome/browser/sync/test/integration/wallet_helper.h" -#include "chrome/browser/web_data_service_factory.h" +#include "chrome/browser/webdata_services/web_data_service_factory.h" #include "components/autofill/core/browser/autofill_data_util.h" #include "components/autofill/core/browser/data_model/autofill_metadata.h" #include "components/autofill/core/browser/data_model/credit_card.h"
diff --git a/chrome/browser/sync/test/integration/two_client_web_apps_bmo_sync_test.cc b/chrome/browser/sync/test/integration/two_client_web_apps_bmo_sync_test.cc index 8ddafc3..00328294 100644 --- a/chrome/browser/sync/test/integration/two_client_web_apps_bmo_sync_test.cc +++ b/chrome/browser/sync/test/integration/two_client_web_apps_bmo_sync_test.cc
@@ -149,7 +149,7 @@ base::RunLoop run_loop; webapps::AppId app_id; auto* provider = WebAppProvider::GetForTest(profile); - provider->scheduler().InstallFromInfo( + provider->scheduler().InstallFromInfoNoIntegrationForTesting( std::make_unique<web_app::WebAppInstallInfo>(info.Clone()), /*overwrite_existing_manifest_fields=*/true, source, base::BindLambdaForTesting(
diff --git a/chrome/browser/sync/test/integration/wallet_helper.cc b/chrome/browser/sync/test/integration/wallet_helper.cc index fc80a8d..d52c538 100644 --- a/chrome/browser/sync/test/integration/wallet_helper.cc +++ b/chrome/browser/sync/test/integration/wallet_helper.cc
@@ -13,7 +13,7 @@ #include "chrome/browser/autofill/personal_data_manager_factory.h" #include "chrome/browser/sync/test/integration/sync_datatype_helper.h" #include "chrome/browser/sync/test/integration/sync_test.h" -#include "chrome/browser/web_data_service_factory.h" +#include "chrome/browser/webdata_services/web_data_service_factory.h" #include "components/autofill/core/browser/autofill_data_util.h" #include "components/autofill/core/browser/data_model/autofill_metadata.h" #include "components/autofill/core/browser/payments/payments_customer_data.h"
diff --git a/chrome/browser/tab_group/BUILD.gn b/chrome/browser/tab_group/BUILD.gn index a4d40cf0..22788aeb 100644 --- a/chrome/browser/tab_group/BUILD.gn +++ b/chrome/browser/tab_group/BUILD.gn
@@ -9,6 +9,7 @@ android_library("java") { sources = [ "java/src/org/chromium/chrome/browser/tasks/tab_groups/TabGroup.java", + "java/src/org/chromium/chrome/browser/tasks/tab_groups/TabGroupColorUtils.java", "java/src/org/chromium/chrome/browser/tasks/tab_groups/TabGroupModelFilter.java", "java/src/org/chromium/chrome/browser/tasks/tab_groups/TabGroupModelFilterObserver.java", "java/src/org/chromium/chrome/browser/tasks/tab_groups/TabGroupTitleUtils.java", @@ -20,12 +21,15 @@ "//chrome/browser/flags:java", "//chrome/browser/tab:java", "//chrome/browser/tabmodel:java", + "//components/tab_groups:tab_groups_java", "//third_party/androidx:androidx_annotation_annotation_java", + "//third_party/androidx:androidx_collection_collection_java", ] } robolectric_library("junit") { sources = [ + "junit/src/org/chromium/chrome/browser/tasks/tab_groups/TabGroupColorUtilsUnitTest.java", "junit/src/org/chromium/chrome/browser/tasks/tab_groups/TabGroupModelFilterUnitTest.java", "junit/src/org/chromium/chrome/browser/tasks/tab_groups/TabGroupTitleUtilsUnitTest.java", ] @@ -41,6 +45,7 @@ "//chrome/test/android:chrome_java_unit_test_support", "//third_party/android_deps:espresso_java", "//third_party/androidx:androidx_annotation_annotation_java", + "//third_party/androidx:androidx_collection_collection_java", "//third_party/hamcrest:hamcrest_core_java", "//third_party/jni_zero:jni_zero_java", "//third_party/junit",
diff --git a/chrome/browser/tab_group/java/src/org/chromium/chrome/browser/tasks/tab_groups/TabGroupColorUtils.java b/chrome/browser/tab_group/java/src/org/chromium/chrome/browser/tasks/tab_groups/TabGroupColorUtils.java new file mode 100644 index 0000000..0457d57 --- /dev/null +++ b/chrome/browser/tab_group/java/src/org/chromium/chrome/browser/tasks/tab_groups/TabGroupColorUtils.java
@@ -0,0 +1,169 @@ +// Copyright 2024 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chrome.browser.tasks.tab_groups; + +import android.content.Context; +import android.content.SharedPreferences; + +import androidx.collection.ArrayMap; + +import org.chromium.base.ContextUtils; +import org.chromium.chrome.browser.tab.Tab; +import org.chromium.components.tab_groups.TabGroupColorId; + +import java.util.Map; +import java.util.Set; + +/** Helper class to handle tab group color related utilities. */ +public class TabGroupColorUtils { + private static final String TAB_GROUP_COLORS_FILE_NAME = "tab_group_colors"; + private static final String MIGRATION_CHECK = "migration_check"; + private static final int INVALID_COLOR_ID = -1; + private static final int MIGRATION_NOT_DONE = 0; + private static final int MIGRATION_DONE = 1; + + /** + * This method stores tab group colors with reference to {@code tabRootId}. + * + * @param tabRootId The tab root ID which is used as a reference to store group colors. + * @param color The tab group color {@link TabGroupColorId} to store. + */ + public static void storeTabGroupColor(int tabRootId, int color) { + assert tabRootId != Tab.INVALID_TAB_ID; + getSharedPreferences().edit().putInt(String.valueOf(tabRootId), color).apply(); + } + + /** + * This method deletes a specific stored tab group color with reference to {@code tabRootId}. + * + * @param tabRootId The tab root ID whose related tab group color will be deleted. + */ + public static void deleteTabGroupColor(int tabRootId) { + assert tabRootId != Tab.INVALID_TAB_ID; + getSharedPreferences().edit().remove(String.valueOf(tabRootId)).apply(); + } + + /** + * This method fetches tab group colors for the related tab group root ID. + * + * @param tabRootId The tab root ID whose related tab group color will be fetched. + * @return The stored color of the target tab group, default value is -1 (INVALID_COLOR_ID). + */ + public static int getTabGroupColor(int tabRootId) { + assert tabRootId != Tab.INVALID_TAB_ID; + return getSharedPreferences().getInt(String.valueOf(tabRootId), INVALID_COLOR_ID); + } + + /** + * This method assigns a color to all tab groups which do not have an assigned tab color at + * startup. If a migration for all existing tabs has already been performed, skip this logic. + * + * @param tabGroupModelFilter The {@TabGroupModelFilter} that governs the current tab groups. + */ + public static void assignTabGroupColorsIfApplicable(TabGroupModelFilter tabGroupModelFilter) { + // TODO(b/41490324): Consider removing the migration logic when tab group colors are + // launched. There may be an argument to keep this around in case the color info is somehow + // lost between startups, in which case this will at least set some default colors up. In + // theory, once the migrations have been applied to everyone there won't be a need for this. + // + // If the migration is already done, skip the below logic. + if (getSharedPreferences().getInt(MIGRATION_CHECK, MIGRATION_NOT_DONE) == MIGRATION_DONE) { + return; + } + + Map<Integer, Integer> currentColorCountMap = getCurrentColorCountMap(tabGroupModelFilter); + Set<Integer> rootIds = tabGroupModelFilter.getAllTabGroupRootIds(); + + // Assign a color to all tab groups that don't have a color. + for (Integer rootId : rootIds) { + int colorId = getTabGroupColor(rootId); + + // Retrieve the next suggested colorId if the current tab group does not have a color. + if (colorId == INVALID_COLOR_ID) { + int suggestedColorId = getNextSuggestedColorId(currentColorCountMap); + storeTabGroupColor(rootId, suggestedColorId); + currentColorCountMap.put( + suggestedColorId, currentColorCountMap.get(suggestedColorId) + 1); + } + } + + // Mark that the initial migration of tab colors is complete. + getSharedPreferences().edit().putInt(MIGRATION_CHECK, MIGRATION_DONE).apply(); + } + + /** + * This method returns the next suggested colorId to be assigned to a tab group if that tab + * group has no color assigned to it. This algorithm uses a key-value map to store all usage + * counts of the current list of colors in other tab groups. It will select the least used color + * that appears first in the color list. The suggested color value should be a color id of type + * {@link TabGroupColorId}. + * + * @param tabGroupModelFilter The {@link TabGroupModelFilter} that governs all tab groups. + */ + public static int getNextSuggestedColorId(TabGroupModelFilter tabGroupModelFilter) { + // Generate the currentColorCountMap. + Map<Integer, Integer> currentColorCountMap = getCurrentColorCountMap(tabGroupModelFilter); + return getNextSuggestedColorId(currentColorCountMap); + } + + /** + * This method removes the shared preference file. TODO(b/41490324): Consider removing this when + * the feature is launched. + */ + public static void clearTabGroupColorInfo() { + ContextUtils.getApplicationContext().deleteSharedPreferences(TAB_GROUP_COLORS_FILE_NAME); + } + + private static SharedPreferences getSharedPreferences() { + return ContextUtils.getApplicationContext() + .getSharedPreferences(TAB_GROUP_COLORS_FILE_NAME, Context.MODE_PRIVATE); + } + + /** Get a map that indicates the current usage count of each tab group color. */ + private static Map<Integer, Integer> getCurrentColorCountMap( + TabGroupModelFilter tabGroupModelFilter) { + int colorListSize = TabGroupColorId.NUM_ENTRIES; + Map<Integer, Integer> colorCountMap = new ArrayMap<>(colorListSize); + for (int i = 0; i < colorListSize; i++) { + colorCountMap.put(i, 0); + } + + Set<Integer> rootIds = tabGroupModelFilter.getAllTabGroupRootIds(); + + // Filter all tab groups for ones that already have a color assigned. + for (Integer rootId : rootIds) { + int colorId = getTabGroupColor(rootId); + + // If the tab group has a color stored on shared prefs, increment the colorId map count. + if (colorId != INVALID_COLOR_ID) { + colorCountMap.put(colorId, colorCountMap.get(colorId) + 1); + } + } + + return colorCountMap; + } + + /** Impl of getNextSuggestedColorId which assumes a currentColorCountMap has been created. */ + private static int getNextSuggestedColorId(Map<Integer, Integer> currentColorCountMap) { + int colorId = Integer.MAX_VALUE; + int colorCount = Integer.MAX_VALUE; + + for (Map.Entry<Integer, Integer> entry : currentColorCountMap.entrySet()) { + if (entry.getValue() < colorCount) { + colorCount = entry.getValue(); + colorId = entry.getKey(); + } else if (entry.getValue() == colorCount) { + if (entry.getKey() < colorId) { + colorId = entry.getKey(); + } + } + } + + // Assert that the current color count map exists and sets a valid colorId on loop + // iteration, otherwise default to an invalid colorId. + assert colorId != Integer.MAX_VALUE; + return colorId != Integer.MAX_VALUE ? colorId : INVALID_COLOR_ID; + } +}
diff --git a/chrome/browser/tab_group/java/src/org/chromium/chrome/browser/tasks/tab_groups/TabGroupModelFilter.java b/chrome/browser/tab_group/java/src/org/chromium/chrome/browser/tasks/tab_groups/TabGroupModelFilter.java index 6688c35..f8ac830 100644 --- a/chrome/browser/tab_group/java/src/org/chromium/chrome/browser/tasks/tab_groups/TabGroupModelFilter.java +++ b/chrome/browser/tab_group/java/src/org/chromium/chrome/browser/tasks/tab_groups/TabGroupModelFilter.java
@@ -9,6 +9,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; +import androidx.collection.ArraySet; import org.chromium.base.MathUtils; import org.chromium.base.ObserverList; @@ -42,6 +43,8 @@ * https://crbug.com/1523745. */ public class TabGroupModelFilter extends TabModelFilter { + private static final int INVALID_COLOR_ID = -1; + private ObserverList<TabGroupModelFilterObserver> mGroupFilterObserver = new ObserverList<>(); private Map<Integer, Integer> mRootIdToGroupIndexMap = new HashMap<>(); private Map<Integer, TabGroup> mRootIdToGroupMap = new HashMap<>(); @@ -136,7 +139,8 @@ Collections.singletonList(index), Collections.singletonList(tab.getRootId()), Collections.singletonList(null), - null); + null, + INVALID_COLOR_ID); } } } @@ -185,6 +189,7 @@ List<Integer> originalRootIds = new ArrayList<>(); List<Token> originalTabGroupIds = new ArrayList<>(); String destinationGroupTitle = TabGroupTitleUtils.getTabGroupTitle(destinationRootId); + int destinationGroupColorId = TabGroupColorUtils.getTabGroupColor(destinationRootId); boolean didCreateNewGroup = !isTabInTabGroup(sourceTab) && !isTabInTabGroup(destinationTab); @@ -239,7 +244,8 @@ originalIndexes, originalRootIds, originalTabGroupIds, - destinationGroupTitle); + destinationGroupTitle, + destinationGroupColorId); } } } @@ -292,6 +298,7 @@ } int destinationIndexInTabModel = getTabModelDestinationIndex(destinationTab); String destinationGroupTitle = TabGroupTitleUtils.getTabGroupTitle(destinationRootId); + int destinationGroupColorId = TabGroupColorUtils.getTabGroupColor(destinationRootId); for (int i = 0; i < tabs.size(); i++) { Tab tab = tabs.get(i); @@ -355,7 +362,8 @@ originalIndexes, originalRootIds, originalTabGroupIds, - destinationGroupTitle); + destinationGroupTitle, + destinationGroupColorId); } } } @@ -1021,6 +1029,20 @@ super.didMoveTab(tab, newIndex, curIndex); } + /** Get all tab group root ids that are associated with tab groups greater than size 1. */ + public Set<Integer> getAllTabGroupRootIds() { + Set<Integer> uniqueTabGroupRootIds = new ArraySet<>(); + TabList tabList = getTabModel(); + + for (int i = 0; i < tabList.getCount(); i++) { + Tab tab = tabList.getTabAt(i); + if (isTabInTabGroup(tab)) { + uniqueTabGroupRootIds.add(tab.getRootId()); + } + } + return uniqueTabGroupRootIds; + } + private boolean isMoveTabOutOfGroup(Tab movedTab) { return !mRootIdToGroupMap.containsKey(movedTab.getRootId()); }
diff --git a/chrome/browser/tab_group/java/src/org/chromium/chrome/browser/tasks/tab_groups/TabGroupModelFilterObserver.java b/chrome/browser/tab_group/java/src/org/chromium/chrome/browser/tasks/tab_groups/TabGroupModelFilterObserver.java index b6ac970..6fec905 100644 --- a/chrome/browser/tab_group/java/src/org/chromium/chrome/browser/tasks/tab_groups/TabGroupModelFilterObserver.java +++ b/chrome/browser/tab_group/java/src/org/chromium/chrome/browser/tasks/tab_groups/TabGroupModelFilterObserver.java
@@ -73,13 +73,15 @@ * @param tabOriginalRootId The original root id for each modified tab. * @param tabOriginalTabGroupId The original tab group id for each modified tab. * @param destinationGroupTitle The original destination group title. + * @param destinationGroupColorId The original destination group color id. */ default void didCreateGroup( List<Tab> tabs, List<Integer> tabOriginalIndex, List<Integer> tabOriginalRootId, List<Token> tabOriginalTabGroupId, - String destinationGroupTitle) {} + String destinationGroupTitle, + int destinationGroupColorId) {} /** * This method is called after a new tab group is created, either through drag and drop, the tab
diff --git a/chrome/browser/tab_group/junit/src/org/chromium/chrome/browser/tasks/tab_groups/TabGroupColorUtilsUnitTest.java b/chrome/browser/tab_group/junit/src/org/chromium/chrome/browser/tasks/tab_groups/TabGroupColorUtilsUnitTest.java new file mode 100644 index 0000000..cfda4da --- /dev/null +++ b/chrome/browser/tab_group/junit/src/org/chromium/chrome/browser/tasks/tab_groups/TabGroupColorUtilsUnitTest.java
@@ -0,0 +1,273 @@ +// Copyright 2024 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chrome.browser.tasks.tab_groups; + +import static androidx.test.espresso.matcher.ViewMatchers.assertThat; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.content.SharedPreferences; + +import androidx.collection.ArraySet; + +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.robolectric.annotation.Config; + +import org.chromium.base.ContextUtils; +import org.chromium.base.test.BaseRobolectricTestRunner; +import org.chromium.base.test.util.Features; + +import java.util.Set; + +/** Tests for {@link TabGroupColorUtils}. */ +@RunWith(BaseRobolectricTestRunner.class) +@Config(manifest = Config.NONE) +public class TabGroupColorUtilsUnitTest { + @Rule public TestRule mProcessor = new Features.JUnitProcessor(); + + private static final String TAB_GROUP_COLORS_FILE_NAME = "tab_group_colors"; + private static final String MIGRATION_CHECK = "migration_check"; + private static final int INVALID_COLOR_ID = -1; + private static final int MIGRATION_DONE = 1; + + private static final int ROOT_ID_1 = 123; + private static final int ROOT_ID_2 = 456; + private static final int ROOT_ID_3 = 789; + private static final int ROOT_ID_4 = 147; + private static final int ROOT_ID_5 = 258; + private static final int ROOT_ID_6 = 369; + private static final int ROOT_ID_7 = 159; + private static final int ROOT_ID_8 = 160; + private static final int ROOT_ID_9 = 161; + private static final int ROOT_ID_10 = 162; + private static final int COLOR_1 = 0; + private static final int COLOR_2 = 1; + private static final int COLOR_3 = 2; + private static final int COLOR_4 = 3; + private static final int COLOR_5 = 4; + private static final int COLOR_6 = 5; + private static final int COLOR_7 = 6; + private static final int COLOR_8 = 7; + private static final int COLOR_9 = 8; + + @Mock Context mContext; + @Mock TabGroupModelFilter mFilter; + @Mock SharedPreferences mSharedPreferences; + @Mock SharedPreferences.Editor mEditor; + @Mock SharedPreferences.Editor mPutIntEditor; + @Mock SharedPreferences.Editor mRemoveEditor; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + doReturn(mSharedPreferences) + .when(mContext) + .getSharedPreferences(TAB_GROUP_COLORS_FILE_NAME, Context.MODE_PRIVATE); + doReturn(mEditor).when(mSharedPreferences).edit(); + doReturn(mRemoveEditor).when(mEditor).remove(any(String.class)); + doReturn(mPutIntEditor).when(mEditor).putInt(any(String.class), any(Integer.class)); + ContextUtils.initApplicationContextForTests(mContext); + } + + @Test + public void testDeleteTabGroupColor() { + TabGroupColorUtils.deleteTabGroupColor(ROOT_ID_1); + + verify(mEditor).remove(eq(String.valueOf(ROOT_ID_1))); + verify(mRemoveEditor).apply(); + } + + @Test + public void testGetTabGroupColor() { + // Mock that we have a stored tab group color with reference to ROOT_ID. + when(mSharedPreferences.getInt(String.valueOf(ROOT_ID_1), INVALID_COLOR_ID)) + .thenReturn(COLOR_1); + + assertThat(TabGroupColorUtils.getTabGroupColor(ROOT_ID_1), equalTo(COLOR_1)); + } + + @Test + public void testStoreTabGroupColor() { + TabGroupColorUtils.storeTabGroupColor(ROOT_ID_1, COLOR_1); + + verify(mEditor).putInt(eq(String.valueOf(ROOT_ID_1)), eq(COLOR_1)); + verify(mPutIntEditor).apply(); + } + + @Test + public void testAssignDefaultTabGroupColors() { + Set<Integer> rootIdsSet = new ArraySet<>(); + rootIdsSet.add(ROOT_ID_1); + rootIdsSet.add(ROOT_ID_2); + rootIdsSet.add(ROOT_ID_3); + + when(mFilter.getAllTabGroupRootIds()).thenReturn(rootIdsSet); + // Mock that there is no stored tab group color for these root ids. + when(mSharedPreferences.getInt(String.valueOf(ROOT_ID_1), INVALID_COLOR_ID)) + .thenReturn(INVALID_COLOR_ID); + when(mSharedPreferences.getInt(String.valueOf(ROOT_ID_2), INVALID_COLOR_ID)) + .thenReturn(INVALID_COLOR_ID); + when(mSharedPreferences.getInt(String.valueOf(ROOT_ID_3), INVALID_COLOR_ID)) + .thenReturn(INVALID_COLOR_ID); + + TabGroupColorUtils.assignTabGroupColorsIfApplicable(mFilter); + + // Test the scenario where no tab groups have colors so the first colors in order are + // assigned. + verify(mEditor).putInt(eq(String.valueOf(ROOT_ID_1)), eq(COLOR_1)); + verify(mEditor).putInt(eq(String.valueOf(ROOT_ID_2)), eq(COLOR_2)); + verify(mEditor).putInt(eq(String.valueOf(ROOT_ID_3)), eq(COLOR_3)); + verify(mEditor).putInt(eq(MIGRATION_CHECK), eq(MIGRATION_DONE)); + verify(mPutIntEditor, times(4)).apply(); + } + + @Test + public void testNextSuggestedColorFirstAndThird() { + Set<Integer> rootIdsSet = new ArraySet<>(); + rootIdsSet.add(ROOT_ID_1); + rootIdsSet.add(ROOT_ID_2); + + when(mFilter.getAllTabGroupRootIds()).thenReturn(rootIdsSet); + // Mock that the first and third colors already exist. + when(mSharedPreferences.getInt(String.valueOf(ROOT_ID_1), INVALID_COLOR_ID)) + .thenReturn(COLOR_1); + when(mSharedPreferences.getInt(String.valueOf(ROOT_ID_2), INVALID_COLOR_ID)) + .thenReturn(COLOR_3); + + assertEquals(COLOR_2, TabGroupColorUtils.getNextSuggestedColorId(mFilter)); + } + + @Test + public void testNextSuggestedColorDoubleFirstAndSecond() { + Set<Integer> rootIdsSet = new ArraySet<>(); + rootIdsSet.add(ROOT_ID_1); + rootIdsSet.add(ROOT_ID_2); + rootIdsSet.add(ROOT_ID_3); + + when(mFilter.getAllTabGroupRootIds()).thenReturn(rootIdsSet); + // Mock that the first and second colors already exist. + when(mSharedPreferences.getInt(String.valueOf(ROOT_ID_1), INVALID_COLOR_ID)) + .thenReturn(COLOR_1); + when(mSharedPreferences.getInt(String.valueOf(ROOT_ID_2), INVALID_COLOR_ID)) + .thenReturn(COLOR_1); + when(mSharedPreferences.getInt(String.valueOf(ROOT_ID_3), INVALID_COLOR_ID)) + .thenReturn(COLOR_2); + + assertEquals(COLOR_3, TabGroupColorUtils.getNextSuggestedColorId(mFilter)); + } + + @Test + public void testNextSuggestedColorSecondColor() { + Set<Integer> rootIdsSet = new ArraySet<>(); + rootIdsSet.add(ROOT_ID_1); + + when(mFilter.getAllTabGroupRootIds()).thenReturn(rootIdsSet); + // Mock that only the second color already exists. + when(mSharedPreferences.getInt(String.valueOf(ROOT_ID_1), INVALID_COLOR_ID)) + .thenReturn(COLOR_2); + + assertEquals(COLOR_1, TabGroupColorUtils.getNextSuggestedColorId(mFilter)); + } + + @Test + public void testNextSuggestedColorAllColorsUsed() { + Set<Integer> rootIdsSet = new ArraySet<>(); + rootIdsSet.add(ROOT_ID_1); + rootIdsSet.add(ROOT_ID_2); + rootIdsSet.add(ROOT_ID_3); + rootIdsSet.add(ROOT_ID_4); + rootIdsSet.add(ROOT_ID_5); + rootIdsSet.add(ROOT_ID_6); + rootIdsSet.add(ROOT_ID_7); + rootIdsSet.add(ROOT_ID_8); + rootIdsSet.add(ROOT_ID_9); + + when(mFilter.getAllTabGroupRootIds()).thenReturn(rootIdsSet); + // Mock that all colors are used. + when(mSharedPreferences.getInt(String.valueOf(ROOT_ID_1), INVALID_COLOR_ID)) + .thenReturn(COLOR_1); + when(mSharedPreferences.getInt(String.valueOf(ROOT_ID_2), INVALID_COLOR_ID)) + .thenReturn(COLOR_2); + when(mSharedPreferences.getInt(String.valueOf(ROOT_ID_3), INVALID_COLOR_ID)) + .thenReturn(COLOR_3); + when(mSharedPreferences.getInt(String.valueOf(ROOT_ID_4), INVALID_COLOR_ID)) + .thenReturn(COLOR_4); + when(mSharedPreferences.getInt(String.valueOf(ROOT_ID_5), INVALID_COLOR_ID)) + .thenReturn(COLOR_5); + when(mSharedPreferences.getInt(String.valueOf(ROOT_ID_6), INVALID_COLOR_ID)) + .thenReturn(COLOR_6); + when(mSharedPreferences.getInt(String.valueOf(ROOT_ID_7), INVALID_COLOR_ID)) + .thenReturn(COLOR_7); + when(mSharedPreferences.getInt(String.valueOf(ROOT_ID_8), INVALID_COLOR_ID)) + .thenReturn(COLOR_8); + when(mSharedPreferences.getInt(String.valueOf(ROOT_ID_9), INVALID_COLOR_ID)) + .thenReturn(COLOR_9); + + assertEquals(COLOR_1, TabGroupColorUtils.getNextSuggestedColorId(mFilter)); + } + + @Test + public void testNextSuggestedColorContinuousSuggestion() { + Set<Integer> rootIdsSet = new ArraySet<>(); + rootIdsSet.add(ROOT_ID_1); + rootIdsSet.add(ROOT_ID_2); + rootIdsSet.add(ROOT_ID_3); + rootIdsSet.add(ROOT_ID_4); + rootIdsSet.add(ROOT_ID_5); + rootIdsSet.add(ROOT_ID_6); + rootIdsSet.add(ROOT_ID_7); + rootIdsSet.add(ROOT_ID_8); + + when(mFilter.getAllTabGroupRootIds()).thenReturn(rootIdsSet); + // Mock that all colors are used except for COLOR_8. + when(mSharedPreferences.getInt(String.valueOf(ROOT_ID_1), INVALID_COLOR_ID)) + .thenReturn(COLOR_1); + when(mSharedPreferences.getInt(String.valueOf(ROOT_ID_2), INVALID_COLOR_ID)) + .thenReturn(COLOR_2); + when(mSharedPreferences.getInt(String.valueOf(ROOT_ID_3), INVALID_COLOR_ID)) + .thenReturn(COLOR_3); + when(mSharedPreferences.getInt(String.valueOf(ROOT_ID_4), INVALID_COLOR_ID)) + .thenReturn(COLOR_4); + when(mSharedPreferences.getInt(String.valueOf(ROOT_ID_5), INVALID_COLOR_ID)) + .thenReturn(COLOR_5); + when(mSharedPreferences.getInt(String.valueOf(ROOT_ID_6), INVALID_COLOR_ID)) + .thenReturn(COLOR_6); + when(mSharedPreferences.getInt(String.valueOf(ROOT_ID_7), INVALID_COLOR_ID)) + .thenReturn(COLOR_7); + when(mSharedPreferences.getInt(String.valueOf(ROOT_ID_8), INVALID_COLOR_ID)) + .thenReturn(COLOR_9); + + assertEquals(COLOR_8, TabGroupColorUtils.getNextSuggestedColorId(mFilter)); + + // Mock that subsequent addition of the missing color directs the suggestion to COLOR_1. + rootIdsSet.add(ROOT_ID_9); + when(mFilter.getAllTabGroupRootIds()).thenReturn(rootIdsSet); + when(mSharedPreferences.getInt(String.valueOf(ROOT_ID_9), INVALID_COLOR_ID)) + .thenReturn(COLOR_8); + assertEquals(COLOR_1, TabGroupColorUtils.getNextSuggestedColorId(mFilter)); + + // Mock that subsequent addition of the first color directs the suggestion to COLOR_2. + rootIdsSet.add(ROOT_ID_10); + when(mFilter.getAllTabGroupRootIds()).thenReturn(rootIdsSet); + when(mSharedPreferences.getInt(String.valueOf(ROOT_ID_10), INVALID_COLOR_ID)) + .thenReturn(COLOR_1); + assertEquals(COLOR_2, TabGroupColorUtils.getNextSuggestedColorId(mFilter)); + } +}
diff --git a/chrome/browser/tab_group/junit/src/org/chromium/chrome/browser/tasks/tab_groups/TabGroupModelFilterUnitTest.java b/chrome/browser/tab_group/junit/src/org/chromium/chrome/browser/tasks/tab_groups/TabGroupModelFilterUnitTest.java index af846e8f..fd96105 100644 --- a/chrome/browser/tab_group/junit/src/org/chromium/chrome/browser/tasks/tab_groups/TabGroupModelFilterUnitTest.java +++ b/chrome/browser/tab_group/junit/src/org/chromium/chrome/browser/tasks/tab_groups/TabGroupModelFilterUnitTest.java
@@ -30,6 +30,7 @@ import android.content.SharedPreferences; import androidx.annotation.Nullable; +import androidx.collection.ArraySet; import org.junit.Before; import org.junit.Rule; @@ -63,6 +64,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.Set; /** Tests for {@link TabGroupModelFilter}. */ @SuppressWarnings("ResultOfMethodCallIgnored") @@ -108,6 +110,10 @@ private static final String TAB_GROUP_TITLES_FILE_NAME = "tab_group_titles"; private static final String TAB_TITLE = "Tab"; + private static final String TAB_GROUP_COLORS_FILE_NAME = "tab_group_colors"; + private static final int INVALID_COLOR_ID = -1; + private static final int COLOR_ID = 0; + @Rule public TestRule mProcessor = new Features.JUnitProcessor(); @Rule public JniMocker mJniMocker = new JniMocker(); @@ -120,7 +126,9 @@ @Mock Context mContext; - @Mock SharedPreferences mSharedPreferences; + @Mock SharedPreferences mSharedPreferencesTitle; + + @Mock SharedPreferences mSharedPreferencesColor; @Captor ArgumentCaptor<TabModelObserver> mTabModelObserverCaptor; @@ -320,11 +328,15 @@ assertTrue(mTabGroupModelFilter.isTabModelRestored()); } - doReturn(mSharedPreferences) + doReturn(mSharedPreferencesTitle) .when(mContext) .getSharedPreferences(TAB_GROUP_TITLES_FILE_NAME, Context.MODE_PRIVATE); + doReturn(mSharedPreferencesColor) + .when(mContext) + .getSharedPreferences(TAB_GROUP_COLORS_FILE_NAME, Context.MODE_PRIVATE); ContextUtils.initApplicationContextForTests(mContext); - when(mSharedPreferences.getString(anyString(), any())).thenReturn(TAB_TITLE); + when(mSharedPreferencesTitle.getString(anyString(), any())).thenReturn(TAB_TITLE); + when(mSharedPreferencesColor.getInt(anyString(), anyInt())).thenReturn(COLOR_ID); } @Before @@ -785,7 +797,8 @@ Collections.singletonList(0), Collections.singletonList(mTab1.getRootId()), Collections.singletonList(null), - null); + null, + INVALID_COLOR_ID); assertTrue(mTabGroupModelFilter.isTabInTabGroup(mTab1)); mTabGroupModelFilter.moveTabOutOfGroup(TAB1_ID); @@ -1332,7 +1345,7 @@ .didCreateNewGroup(mTab4.getRootId(), mTabGroupModelFilter); verify(mTabGroupModelFilterObserver).didMergeTabToGroup(mTab4, mTab4.getId()); verify(mTabGroupModelFilterObserver, never()) - .didCreateGroup(any(), any(), any(), any(), any()); + .didCreateGroup(any(), any(), any(), any(), any(), anyInt()); assertThat(mTab4.getTabGroupId(), equalTo(tabGroupId)); @@ -1592,7 +1605,8 @@ originalIndexes, originalRootIds, originalTabGroupIds, - TAB_TITLE); + TAB_TITLE, + COLOR_ID); assertArrayEquals( mTabGroupModelFilter.getRelatedTabList(mTab2.getId()).toArray(), expectedGroup.toArray()); @@ -1635,7 +1649,8 @@ originalIndexes, originalRootIds, originalTabGroupIds, - TAB_TITLE); + TAB_TITLE, + COLOR_ID); assertArrayEquals( mTabGroupModelFilter.getRelatedTabList(mTab2.getId()).toArray(), expectedGroup.toArray()); @@ -1671,7 +1686,8 @@ originalIndexes, originalRootIds, originalTabGroupIds, - TAB_TITLE); + TAB_TITLE, + COLOR_ID); assertArrayEquals( mTabGroupModelFilter.getRelatedTabList(mTab1.getId()).toArray(), expectedGroup.toArray()); @@ -1689,7 +1705,7 @@ verify(mTabGroupModelFilterObserver) .didCreateNewGroup(mTab4.getRootId(), mTabGroupModelFilter); verify(mTabGroupModelFilterObserver, never()) - .didCreateGroup(any(), any(), any(), any(), any()); + .didCreateGroup(any(), any(), any(), any(), any(), anyInt()); assertEquals(mTab1.getTabGroupId(), tabGroupId); assertEquals(mTab4.getTabGroupId(), tabGroupId); @@ -1765,4 +1781,15 @@ assertTrue(mTabGroupModelFilter.tabGroupExistsForRootId(mTab3.getRootId())); assertTrue(mTabGroupModelFilter.tabGroupExistsForRootId(mTab5.getRootId())); } + + @Test + public void testGetAllTabGroupRootIds() { + // With the given setup, mTab2 and mTab3 are in a group and mTab5 and mTab6 are in another + // group. + Set<Integer> rootIds = new ArraySet<>(); + rootIds.add(mTab2.getRootId()); + rootIds.add(mTab5.getRootId()); + + assertEquals(rootIds, mTabGroupModelFilter.getAllTabGroupRootIds()); + } }
diff --git a/chrome/browser/tab_resumption/java/src/org/chromium/chrome/browser/tab_resumption/TabResumptionModuleView.java b/chrome/browser/tab_resumption/java/src/org/chromium/chrome/browser/tab_resumption/TabResumptionModuleView.java index 83285e35..cbf9005 100644 --- a/chrome/browser/tab_resumption/java/src/org/chromium/chrome/browser/tab_resumption/TabResumptionModuleView.java +++ b/chrome/browser/tab_resumption/java/src/org/chromium/chrome/browser/tab_resumption/TabResumptionModuleView.java
@@ -23,6 +23,8 @@ private SuggestionBundle mBundle; private boolean mIsSuggestionBundleReady; + private String mTitle; + private String mAllTilesTexts; public TabResumptionModuleView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); @@ -56,7 +58,9 @@ } void setTitle(@Nullable String title) { - ((TextView) findViewById(R.id.tab_resumption_title_description)).setText(title); + mTitle = title; + ((TextView) findViewById(R.id.tab_resumption_title_description)).setText(mTitle); + setContentDescriptionOfTabResumption(); } TabResumptionTileContainerView getTileContainerViewForTesting() { @@ -67,9 +71,22 @@ if (mIsSuggestionBundleReady && mUrlImageProvider != null && mClickCallback != null) { if (mBundle == null) { mTileContainerView.removeAllViews(); + mAllTilesTexts = null; } else { - mTileContainerView.renderAllTiles(mBundle, mUrlImageProvider, mClickCallback); + mAllTilesTexts = + mTileContainerView.renderAllTiles( + mBundle, mUrlImageProvider, mClickCallback); } + setContentDescriptionOfTabResumption(); + } + } + + /** Sets the content description for the tab resumption module. */ + private void setContentDescriptionOfTabResumption() { + if (mTitle != null && mAllTilesTexts != null) { + setContentDescription(mTitle + ". " + mAllTilesTexts); + } else { + setContentDescription(null); } } }
diff --git a/chrome/browser/tab_resumption/java/src/org/chromium/chrome/browser/tab_resumption/TabResumptionTileContainerView.java b/chrome/browser/tab_resumption/java/src/org/chromium/chrome/browser/tab_resumption/TabResumptionTileContainerView.java index b26582d..47a8ebe 100644 --- a/chrome/browser/tab_resumption/java/src/org/chromium/chrome/browser/tab_resumption/TabResumptionTileContainerView.java +++ b/chrome/browser/tab_resumption/java/src/org/chromium/chrome/browser/tab_resumption/TabResumptionTileContainerView.java
@@ -41,13 +41,17 @@ removeAllViews(); } - /** Adds all new {@link TabResumptionTileView} instances, after removing existing ones. */ - public void renderAllTiles( + /** + * Adds all new {@link TabResumptionTileView} instances, after removing existing ones and + * returns the text of all instances. + */ + public String renderAllTiles( SuggestionBundle bundle, UrlImageProvider urlImageProvider, SuggestionClickCallback suggestionClickCallback) { removeAllViews(); + String allTilesTexts = ""; boolean isSingle = bundle.entries.size() == 1; for (SuggestionEntry entry : bundle.entries) { // Add divider if some tile already exists. @@ -68,15 +72,17 @@ TabResumptionTileView tileView = (TabResumptionTileView) LayoutInflater.from(getContext()).inflate(layoutId, this, false); - loadTileTexts(entry, bundle.referenceTimeMs, isSingle, tileView); + allTilesTexts += + loadTileTexts(entry, bundle.referenceTimeMs, isSingle, tileView) + ". "; loadTileUrlImage(entry, urlImageProvider, tileView); tileView.bindSuggestionClickCallback(suggestionClickCallback, entry.url); addView(tileView); } + return allTilesTexts; } - /** Renders the texts of a {@link TabResumptionTileView}. */ - private void loadTileTexts( + /** Renders and returns the texts of a {@link TabResumptionTileView}. */ + private String loadTileTexts( SuggestionEntry entry, long referenceTimeMs, boolean isSingle, @@ -94,6 +100,7 @@ recencyString, entry.url.getHost()); tileView.setSuggestionTextsSingle(preInfoText, entry.title, postInfoText); + return preInfoText + ", " + entry.title + ", " + postInfoText; } else { String infoText = res.getString( @@ -101,6 +108,7 @@ recencyString, entry.sourceName); tileView.setSuggestionTextsMulti(entry.title, infoText); + return entry.title + ", " + infoText; } }
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn index 1ca0be7..a69834a 100644 --- a/chrome/browser/ui/BUILD.gn +++ b/chrome/browser/ui/BUILD.gn
@@ -1657,6 +1657,8 @@ "webui/cr_components/customize_color_scheme_mode/customize_color_scheme_mode_handler.h", "webui/cr_components/history_clusters/history_clusters_util.cc", "webui/cr_components/history_clusters/history_clusters_util.h", + "webui/cr_components/history_embeddings/history_embeddings_handler.cc", + "webui/cr_components/history_embeddings/history_embeddings_handler.h", "webui/cr_components/most_visited/most_visited_handler.cc", "webui/cr_components/most_visited/most_visited_handler.h", "webui/cr_components/theme_color_picker/customize_chrome_colors.cc", @@ -2112,6 +2114,7 @@ "//ui/webui/resources/cr_components/commerce:mojo_bindings", "//ui/webui/resources/cr_components/help_bubble:mojo_bindings", "//ui/webui/resources/cr_components/history_clusters:mojo_bindings", + "//ui/webui/resources/cr_components/history_embeddings:mojo_bindings", "//ui/webui/resources/js/browser_command:mojo_bindings", "//ui/webui/resources/js/metrics_reporter:mojo_bindings", ]
diff --git a/chrome/browser/ui/android/signin/BUILD.gn b/chrome/browser/ui/android/signin/BUILD.gn index 9504c09b..975cbff 100644 --- a/chrome/browser/ui/android/signin/BUILD.gn +++ b/chrome/browser/ui/android/signin/BUILD.gn
@@ -127,7 +127,8 @@ "java/res/layout/account_row.xml", "java/res/layout/confirm_import_sync_data.xml", "java/res/layout/fre_uma_dialog.xml", - "java/res/layout/history_sync_view.xml", + "java/res/layout/history_sync_landscape_view.xml", + "java/res/layout/history_sync_portrait_view.xml", "java/res/layout/signin_first_run_bottom_group_view.xml", "java/res/layout/signin_first_run_landscape_view.xml", "java/res/layout/signin_first_run_portrait_view.xml",
diff --git a/chrome/browser/ui/android/signin/java/res/layout/history_sync_landscape_view.xml b/chrome/browser/ui/android/signin/java/res/layout/history_sync_landscape_view.xml new file mode 100644 index 0000000..a275bfc1 --- /dev/null +++ b/chrome/browser/ui/android/signin/java/res/layout/history_sync_landscape_view.xml
@@ -0,0 +1,127 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +Copyright 2024 The Chromium Authors +Use of this source code is governed by a BSD-style license that can be +found in the LICENSE file. +--> + +<org.chromium.chrome.browser.ui.signin.history_sync.HistorySyncView + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:paddingStart="14dp" + android:paddingEnd="24dp" + android:gravity="fill" + android:orientation="horizontal"> + + <RelativeLayout + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:layout_alignParentStart="true" + android:gravity="center_vertical" + android:importantForAccessibility="no"> + <ImageView + android:id="@+id/history_sync_illustration" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:scaleType="center" + app:srcCompat="@drawable/history_sync_illustration" + android:importantForAccessibility="no" /> + <ImageView + android:id="@+id/account_image" + android:layout_width="48dp" + android:layout_height="48dp" + android:layout_centerInParent="true" + android:importantForAccessibility="no" /> + </RelativeLayout> + + <RelativeLayout + android:layout_width="match_parent" + android:layout_height="match_parent" + android:paddingStart="24dp" + android:gravity="center_vertical"> + + <ScrollView + android:id="@+id/sync_consent_scroll_view" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:fadeScrollbars="false" + android:fillViewport="true"> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:gravity="center_vertical" + android:orientation="vertical"> + + <TextView + android:id="@+id/sync_consent_title" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:gravity="start" + android:textAppearance="@style/TextAppearance.Headline.Primary"/> + + <org.chromium.ui.widget.TextViewWithLeading + android:id="@+id/sync_consent_subtitle" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginEnd="105dp" + android:paddingVertical="8dp" + android:gravity="start" + android:textAppearance="@style/TextAppearance.TextMedium.Secondary" + app:leading="@dimen/text_size_medium_leading" /> + + + <RelativeLayout + android:id="@+id/button_bar" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginBottom="16dp"> + + <org.chromium.ui.widget.ButtonCompat + android:id="@+id/negative_button" + style="@style/TextButton" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentStart="true" /> + + <View + android:layout_width="wrap_content" + android:layout_height="0dp" + android:layout_toEndOf="@id/negative_button" + android:layout_toStartOf="@id/positive_button" + android:visibility="invisible" /> + + <org.chromium.ui.widget.ButtonCompat + android:id="@+id/positive_button" + style="@style/FilledButton.Flat" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentEnd="true" /> + + <org.chromium.ui.widget.ButtonCompat + android:id="@+id/more_button" + style="@style/FilledButton.Flat" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_centerHorizontal="true" + android:drawableEnd="@drawable/down_arrow" + android:drawableTint="?attr/globalFilledButtonTextColor" + android:drawablePadding="8dp" + android:visibility="gone" /> + </RelativeLayout> + </LinearLayout> + </ScrollView> + + <org.chromium.ui.widget.TextViewWithLeading + android:id="@+id/sync_consent_details_description" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginVertical="16dp" + android:layout_alignParentBottom="true" + android:gravity="start" + android:textAppearance="@style/TextAppearance.TextSmall.Secondary" + app:leading="@dimen/text_size_small_leading" /> + </RelativeLayout> +</org.chromium.chrome.browser.ui.signin.history_sync.HistorySyncView> \ No newline at end of file
diff --git a/chrome/browser/ui/android/signin/java/res/layout/history_sync_view.xml b/chrome/browser/ui/android/signin/java/res/layout/history_sync_portrait_view.xml similarity index 100% rename from chrome/browser/ui/android/signin/java/res/layout/history_sync_view.xml rename to chrome/browser/ui/android/signin/java/res/layout/history_sync_portrait_view.xml
diff --git a/chrome/browser/ui/android/signin/java/res/layout/signin_first_run_landscape_view.xml b/chrome/browser/ui/android/signin/java/res/layout/signin_first_run_landscape_view.xml index d897d2a..d072128 100644 --- a/chrome/browser/ui/android/signin/java/res/layout/signin_first_run_landscape_view.xml +++ b/chrome/browser/ui/android/signin/java/res/layout/signin_first_run_landscape_view.xml
@@ -55,9 +55,9 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:gravity="start" - android:paddingBottom="8dp" + android:paddingBottom="14dp" android:paddingStart="24dp" - android:paddingTop="8dp" + android:paddingTop="4dp" android:text="@string/signin_fre_subtitle" android:textAppearance="@style/TextAppearance.TextMedium.Secondary" android:visibility="gone" @@ -110,7 +110,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginHorizontal="24dp" - android:layout_marginVertical="12dp" + android:layout_marginVertical="16dp" android:gravity="start" android:textAppearance="@style/TextAppearance.TextSmall.Secondary" android:text="@string/signin_fre_footer"
diff --git a/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/history_sync/HistorySyncCoordinator.java b/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/history_sync/HistorySyncCoordinator.java index 20a6dc8..b6e06eb 100644 --- a/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/history_sync/HistorySyncCoordinator.java +++ b/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/history_sync/HistorySyncCoordinator.java
@@ -4,6 +4,7 @@ package org.chromium.chrome.browser.ui.signin.history_sync; +import android.content.res.Configuration; import android.view.LayoutInflater; import android.view.View; @@ -17,6 +18,8 @@ /*Delegate for the History Sync MVC */ public interface HistorySyncDelegate { void dismiss(); + + boolean canUseLandscapeLayout(); } private final HistorySyncMediator mMediator; @@ -29,8 +32,8 @@ HistorySyncDelegate delegate, Profile profile, @SigninAccessPoint int accessPoint) { - mView = (HistorySyncView) inflater.inflate(R.layout.history_sync_view, null, false); mMediator = new HistorySyncMediator(inflater.getContext(), delegate, profile, accessPoint); + mView = inflateView(inflater, delegate); mPropertyModelChangeProcessor = PropertyModelChangeProcessor.create( mMediator.getModel(), mView, HistorySyncViewBinder::bind); @@ -48,4 +51,19 @@ public View getView() { return mView; } + + private HistorySyncView inflateView(LayoutInflater inflater, HistorySyncDelegate delegate) { + Configuration configuration = inflater.getContext().getResources().getConfiguration(); + boolean useLandscapeLayout = + delegate.canUseLandscapeLayout() + && configuration.orientation == Configuration.ORIENTATION_LANDSCAPE; + + return (HistorySyncView) + inflater.inflate( + useLandscapeLayout + ? R.layout.history_sync_landscape_view + : R.layout.history_sync_portrait_view, + null, + false); + } }
diff --git a/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/history_sync/HistorySyncRenderTest.java b/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/history_sync/HistorySyncRenderTest.java index 6a2f75f..a45927b 100644 --- a/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/history_sync/HistorySyncRenderTest.java +++ b/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/history_sync/HistorySyncRenderTest.java
@@ -8,6 +8,7 @@ import static androidx.test.espresso.matcher.ViewMatchers.withId; import static org.hamcrest.Matchers.allOf; +import static org.mockito.Mockito.when; import android.content.res.Configuration; import android.view.LayoutInflater; @@ -101,9 +102,12 @@ public final RenderTestRule mRenderTestRule = RenderTestRule.Builder.withPublicCorpus() .setBugComponent(RenderTestRule.Component.SERVICES_SIGN_IN) + .setRevision(1) + .setDescription("New landscape layout") .build(); @Mock private SyncService mSyncServiceMock; + @Mock private HistorySyncCoordinator.HistorySyncDelegate mHistorySyncDelegateMock; private HistorySyncCoordinator mHistorySyncCoordinator; @@ -125,6 +129,7 @@ @Before public void setUp() { NativeLibraryTestUtils.loadNativeLibraryAndInitBrowserProcess(); + when(mHistorySyncDelegateMock.canUseLandscapeLayout()).thenReturn(true); mActivityTestRule.launchActivity(null); mSigninTestRule.addTestAccountThenSignin(); SyncServiceFactory.setInstanceForTesting(mSyncServiceMock); @@ -148,7 +153,7 @@ mHistorySyncCoordinator = new HistorySyncCoordinator( LayoutInflater.from(mActivityTestRule.getActivity()), - () -> {}, + mHistorySyncDelegateMock, ProfileManager.getLastUsedRegularProfile(), SigninAccessPoint.UNKNOWN); mActivityTestRule
diff --git a/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/history_sync/HistorySyncTest.java b/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/history_sync/HistorySyncTest.java index 6ba00d52..240d464 100644 --- a/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/history_sync/HistorySyncTest.java +++ b/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/history_sync/HistorySyncTest.java
@@ -19,7 +19,6 @@ import androidx.test.filters.MediumTest; import org.junit.After; -import org.junit.Assert; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -54,28 +53,6 @@ @CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE}) @DoNotBatch(reason = "This test relies on native initialization") public class HistorySyncTest { - /** Stub implementation of the {@link HistorySyncDelegate} for testing */ - public class HistorySyncTestDelegate implements HistorySyncCoordinator.HistorySyncDelegate { - private HistorySyncCoordinator mCoordinator; - - @Override - public void dismiss() { - if (isCoordinatorDestroyed()) { - return; - } - mCoordinator.destroy(); - mCoordinator = null; - } - - public void setCoordinator(HistorySyncCoordinator coordinator) { - mCoordinator = coordinator; - } - - public boolean isCoordinatorDestroyed() { - return mCoordinator == null; - } - } - @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule().strictness(Strictness.STRICT_STUBS); @@ -88,9 +65,9 @@ private static final @SigninAccessPoint int SIGNIN_ACCESS_POINT = SigninAccessPoint.UNKNOWN; @Mock private SyncService mSyncServiceMock; + @Mock private HistorySyncCoordinator.HistorySyncDelegate mHistorySyncDelegateMock; private HistorySyncCoordinator mHistorySyncCoordinator; - private HistorySyncTestDelegate mDelegate; @Before public void setUp() { @@ -102,13 +79,6 @@ @After public void tearDown() { - TestThreadUtils.runOnUiThreadBlocking( - () -> { - if (mDelegate.isCoordinatorDestroyed()) { - mDelegate.dismiss(); - } - }); - mSigninTestRule.forceSignOut(); } @@ -144,7 +114,7 @@ histogramWatcher.assertExpected(); verify(mSyncServiceMock).setSelectedType(UserSelectableType.HISTORY, true); verify(mSyncServiceMock).setSelectedType(UserSelectableType.TABS, true); - Assert.assertTrue(mDelegate.isCoordinatorDestroyed()); + verify(mHistorySyncDelegateMock).dismiss(); } @Test @@ -159,7 +129,7 @@ histogramWatcher.assertExpected(); verifyNoInteractions(mSyncServiceMock); - Assert.assertTrue(mDelegate.isCoordinatorDestroyed()); + verify(mHistorySyncDelegateMock).dismiss(); } @Test @@ -175,24 +145,22 @@ () -> mSigninTestRule.getPrimaryAccount(ConsentLevel.SIGNIN) == null); histogramWatcher.assertExpected(); - Assert.assertTrue(mDelegate.isCoordinatorDestroyed()); + verify(mHistorySyncDelegateMock).dismiss(); } private void buildHistorySyncCoordinator() { - mDelegate = new HistorySyncTestDelegate(); TestThreadUtils.runOnUiThreadBlocking( () -> { mHistorySyncCoordinator = new HistorySyncCoordinator( LayoutInflater.from(mActivityTestRule.getActivity()), - mDelegate, + mHistorySyncDelegateMock, ProfileManager.getLastUsedRegularProfile(), SIGNIN_ACCESS_POINT); mActivityTestRule .getActivity() .setContentView(mHistorySyncCoordinator.getView()); }); - mDelegate.setCoordinator(mHistorySyncCoordinator); ViewUtils.waitForVisibleView(allOf(withId(R.id.sync_consent_title), isDisplayed())); } }
diff --git a/chrome/browser/ui/ash/birch/birch_calendar_fetcher.cc b/chrome/browser/ui/ash/birch/birch_calendar_fetcher.cc index 96e853b..4c17585 100644 --- a/chrome/browser/ui/ash/birch/birch_calendar_fetcher.cc +++ b/chrome/browser/ui/ash/birch/birch_calendar_fetcher.cc
@@ -126,7 +126,7 @@ sender_->StartRequestWithAuthRetry( std::make_unique<google_apis::calendar::CalendarApiEventsRequest>( sender_.get(), url_generator_, std::move(callback_), start_time_, - end_time_)); + end_time_, /*include_attachments=*/true)); } void BirchCalendarFetcher::SetSenderForTest(
diff --git a/chrome/browser/ui/ash/chrome_shell_delegate.cc b/chrome/browser/ui/ash/chrome_shell_delegate.cc index e8a51e3..8833628 100644 --- a/chrome/browser/ui/ash/chrome_shell_delegate.cc +++ b/chrome/browser/ui/ash/chrome_shell_delegate.cc
@@ -127,6 +127,8 @@ return chrome::FeedbackSource::kFeedbackSourceFocusMode; case ash::ShellDelegate::FeedbackSource::kGameDashboard: return chrome::FeedbackSource::kFeedbackSourceGameDashboard; + case ash::ShellDelegate::FeedbackSource::kOverview: + return chrome::FeedbackSource::kFeedbackSourceOverview; case ash::ShellDelegate::FeedbackSource::kWindowLayoutMenu: return chrome::FeedbackSource::kFeedbackSourceWindowLayoutMenu; } @@ -395,10 +397,11 @@ void ChromeShellDelegate::OpenFeedbackDialog( ShellDelegate::FeedbackSource source, - const std::string& description_template) { + const std::string& description_template, + const std::string& category_tag) { chrome::OpenFeedbackDialog(/*browser=*/nullptr, ToChromeFeedbackSource(source), - description_template); + description_template, category_tag); } void ChromeShellDelegate::OpenProfileManager() {
diff --git a/chrome/browser/ui/ash/chrome_shell_delegate.h b/chrome/browser/ui/ash/chrome_shell_delegate.h index 96b2fbf..cf69d6f 100644 --- a/chrome/browser/ui/ash/chrome_shell_delegate.h +++ b/chrome/browser/ui/ash/chrome_shell_delegate.h
@@ -79,7 +79,8 @@ bool IsLoggingRedirectDisabled() const override; base::FilePath GetPrimaryUserDownloadsFolder() const override; void OpenFeedbackDialog(ShellDelegate::FeedbackSource source, - const std::string& description_template) override; + const std::string& description_template, + const std::string& category_tag) override; void OpenProfileManager() override; static void SetDisableLoggingRedirectForTesting(bool value); static void ResetDisableLoggingRedirectForTesting();
diff --git a/chrome/browser/ui/ash/download_status/display_manager.cc b/chrome/browser/ui/ash/download_status/display_manager.cc index d616477..77008b1 100644 --- a/chrome/browser/ui/ash/download_status/display_manager.cc +++ b/chrome/browser/ui/ash/download_status/display_manager.cc
@@ -25,6 +25,12 @@ #include "chrome/browser/ui/ash/download_status/notification_display_client.h" #include "chromeos/crosapi/mojom/download_controller.mojom.h" #include "chromeos/crosapi/mojom/download_status_updater.mojom.h" +#include "net/base/mime_util.h" +#include "third_party/blink/public/common/mime_util/mime_util.h" +#include "ui/base/clipboard/clipboard_buffer.h" +#include "ui/base/clipboard/file_info.h" +#include "ui/base/clipboard/scoped_clipboard_writer.h" +#include "ui/gfx/image/image_skia.h" namespace ash::download_status { @@ -134,6 +140,15 @@ return file_path.get().BaseName().LossyDisplayName(); } +// Returns true if the file referred to by `file_path` is of an image MIME type. +bool HasSupportedImageMimeType(const base::FilePath& file_path) { + std::string mime_type; + if (net::GetMimeTypeFromFile(file_path, &mime_type)) { + return blink::IsSupportedImageMimeType(mime_type); + } + return false; +} + // Opens the download file specified by `file_path` under the file system // associated with `profile`. void OpenFile(Profile* profile, const base::FilePath& file_path) { @@ -240,22 +255,38 @@ &kResumeIcon, IDS_ASH_DOWNLOAD_COMMAND_TEXT_RESUME, CommandType::kResume); } + const base::FilePath& full_path = *download_status.full_path; switch (download_status.state) { case crosapi::mojom::DownloadState::kComplete: // NOTE: `kOpenFile` is not shown so it doesn't require an icon/text_id. command_infos.emplace_back( - base::BindRepeating( - &DisplayManager::PerformCommand, weak_ptr_factory_.GetWeakPtr(), - CommandType::kOpenFile, *download_status.full_path), + base::BindRepeating(&DisplayManager::PerformCommand, + weak_ptr_factory_.GetWeakPtr(), + CommandType::kOpenFile, full_path), /*icon=*/nullptr, /*text_id=*/-1, CommandType::kOpenFile); // NOTE: The `kShowInFolder` button does not have an icon. command_infos.emplace_back( - base::BindRepeating( - &DisplayManager::PerformCommand, weak_ptr_factory_.GetWeakPtr(), - CommandType::kShowInFolder, *download_status.full_path), + base::BindRepeating(&DisplayManager::PerformCommand, + weak_ptr_factory_.GetWeakPtr(), + CommandType::kShowInFolder, full_path), /*icon=*/nullptr, IDS_ASH_DOWNLOAD_COMMAND_TEXT_SHOW_IN_FOLDER, CommandType::kShowInFolder); + + // Add a command to copy the download file to clipboard if: + // 1. `download_status` has a valid image; AND + // 2. The download file is an image. + // NOTE: The `kCopyToClipboard` button does not require an icon. + if (const gfx::ImageSkia& image = download_status.image; + !image.isNull() && !image.size().IsEmpty() && + HasSupportedImageMimeType(full_path)) { + command_infos.emplace_back( + base::BindRepeating(&DisplayManager::PerformCommand, + weak_ptr_factory_.GetWeakPtr(), + CommandType::kCopyToClipboard, full_path), + /*icon=*/nullptr, IDS_ASH_DOWNLOAD_COMMAND_TEXT_COPY_TO_CLIPBOARD, + CommandType::kCopyToClipboard); + } break; case crosapi::mojom::DownloadState::kInProgress: // NOTE: `kShowInBrowser` is not shown so doesn't require an icon/text_id. @@ -284,7 +315,7 @@ } display_metadata.command_infos = std::move(command_infos); - display_metadata.file_path = *download_status.full_path; + display_metadata.file_path = full_path; display_metadata.image = download_status.image; display_metadata.progress = GetProgress(download_status); display_metadata.secondary_text = download_status.status_text; @@ -301,6 +332,13 @@ download_status_updater_->Cancel(/*guid=*/std::get<std::string>(param), /*callback=*/base::DoNothing()); break; + case CommandType::kCopyToClipboard: { + ui::ScopedClipboardWriter scw(ui::ClipboardBuffer::kCopyPaste); + scw.WriteFilenames(ui::FileInfosToURIList( + /*filenames=*/{ui::FileInfo(std::get<base::FilePath>(param), + /*display_name=*/base::FilePath())})); + break; + } case CommandType::kOpenFile: OpenFile(profile_, std::get<base::FilePath>(param)); break;
diff --git a/chrome/browser/ui/ash/download_status/display_metadata.h b/chrome/browser/ui/ash/download_status/display_metadata.h index 64bd130..4d6b8f1 100644 --- a/chrome/browser/ui/ash/download_status/display_metadata.h +++ b/chrome/browser/ui/ash/download_status/display_metadata.h
@@ -22,6 +22,7 @@ // Lists the types of commands that can be performed on a displayed download. enum class CommandType { kCancel, + kCopyToClipboard, kOpenFile, kPause, kResume,
diff --git a/chrome/browser/ui/ash/download_status/display_test_util.cc b/chrome/browser/ui/ash/download_status/display_test_util.cc index 00d548f..715f1e4b 100644 --- a/chrome/browser/ui/ash/download_status/display_test_util.cc +++ b/chrome/browser/ui/ash/download_status/display_test_util.cc
@@ -22,11 +22,12 @@ crosapi::mojom::DownloadStatusPtr CreateDownloadStatus( Profile* profile, + std::string_view extension, crosapi::mojom::DownloadState state, crosapi::mojom::DownloadProgressPtr progress) { crosapi::mojom::DownloadStatusPtr download_status = crosapi::mojom::DownloadStatus::New(); - download_status->full_path = test::CreateFile(profile); + download_status->full_path = test::CreateFile(profile, extension); download_status->guid = base::UnguessableToken::Create().ToString(); download_status->progress = std::move(progress); download_status->state = state; @@ -36,10 +37,11 @@ crosapi::mojom::DownloadStatusPtr CreateInProgressDownloadStatus( Profile* profile, + std::string_view extension, int64_t received_bytes, const std::optional<int64_t>& total_bytes) { return CreateDownloadStatus( - profile, crosapi::mojom::DownloadState::kInProgress, + profile, extension, crosapi::mojom::DownloadState::kInProgress, crosapi::mojom::DownloadProgress::New( /*loop=*/false, received_bytes, total_bytes.value_or(kUnknownTotalBytes), /*visible=*/true));
diff --git a/chrome/browser/ui/ash/download_status/display_test_util.h b/chrome/browser/ui/ash/download_status/display_test_util.h index 8e947f1..dee4bfd 100644 --- a/chrome/browser/ui/ash/download_status/display_test_util.h +++ b/chrome/browser/ui/ash/download_status/display_test_util.h
@@ -6,6 +6,7 @@ #define CHROME_BROWSER_UI_ASH_DOWNLOAD_STATUS_DISPLAY_TEST_UTIL_H_ #include <optional> +#include <string_view> #include "chromeos/crosapi/mojom/download_status_updater.mojom.h" @@ -13,10 +14,11 @@ namespace ash::download_status { -// Creates a download status associated with a file under the downloads -// directory of `profile`. +// Creates a download status associated with a file with the specified +// `extension` under the downloads directory of `profile`. crosapi::mojom::DownloadStatusPtr CreateDownloadStatus( Profile* profile, + std::string_view extension, crosapi::mojom::DownloadState state, crosapi::mojom::DownloadProgressPtr progress); @@ -24,6 +26,7 @@ // with a file under the downloads directory of `profile`. crosapi::mojom::DownloadStatusPtr CreateInProgressDownloadStatus( Profile* profile, + std::string_view extension, int64_t received_bytes, const std::optional<int64_t>& total_bytes = std::nullopt);
diff --git a/chrome/browser/ui/ash/download_status/holding_space_display_client.cc b/chrome/browser/ui/ash/download_status/holding_space_display_client.cc index 0b2641e3..190ce7ec 100644 --- a/chrome/browser/ui/ash/download_status/holding_space_display_client.cc +++ b/chrome/browser/ui/ash/download_status/holding_space_display_client.cc
@@ -4,6 +4,7 @@ #include "chrome/browser/ui/ash/download_status/holding_space_display_client.h" +#include <optional> #include <utility> #include <vector> @@ -28,14 +29,17 @@ namespace { -// Returns the command ID corresponding to the given command type. +// Returns the command ID corresponding to the given command type if any. If +// there is no such command ID, returns `std::nullopt`. // NOTE: It is fine to map both `CommandType::kOpenFile` and // `CommandType::kShowInBrowser` to `kOpenItem`, because `kOpenItem` is not // accessible from a holding space chip's context menu. -HoldingSpaceCommandId ConvertCommandTypeToId(CommandType type) { +std::optional<HoldingSpaceCommandId> ConvertCommandTypeToId(CommandType type) { switch (type) { case CommandType::kCancel: return HoldingSpaceCommandId::kCancelItem; + case CommandType::kCopyToClipboard: + return std::nullopt; case CommandType::kOpenFile: return HoldingSpaceCommandId::kOpenItem; case CommandType::kPause: @@ -51,12 +55,16 @@ } } -// Returns the holding space item action corresponding to `type`. -holding_space_metrics::ItemAction ConvertCommandTypeToAction(CommandType type) { +// Returns the holding space item action corresponding to `type` if any. If +// there is no such action, returns `std::nullopt`. +std::optional<holding_space_metrics::ItemAction> ConvertCommandTypeToAction( + CommandType type) { using ItemAction = holding_space_metrics::ItemAction; switch (type) { case CommandType::kCancel: return ItemAction::kCancel; + case CommandType::kCopyToClipboard: + return std::nullopt; case CommandType::kOpenFile: return ItemAction::kLaunch; case CommandType::kPause: @@ -121,23 +129,31 @@ // Generate in-progress commands from `display_metadata`. std::vector<HoldingSpaceItem::InProgressCommand> in_progress_commands; for (const auto& command_info : display_metadata.command_infos) { - if (const HoldingSpaceCommandId id = - ConvertCommandTypeToId(command_info.type); - holding_space_util::IsInProgressCommand(id)) { - in_progress_commands.emplace_back( - id, command_info.text_id, command_info.icon, - base::BindRepeating( - [](holding_space_metrics::ItemAction action, - const base::RepeatingClosure& command_callback, - const HoldingSpaceItem* item, HoldingSpaceCommandId command_id, - holding_space_metrics::EventSource event_source) { - command_callback.Run(); - holding_space_metrics::RecordItemAction( - /*items=*/{item}, action, event_source); - }, - ConvertCommandTypeToAction(command_info.type), - command_info.command_callback)); + const std::optional<HoldingSpaceCommandId> id = + ConvertCommandTypeToId(command_info.type); + const std::optional<holding_space_metrics::ItemAction> item_action = + ConvertCommandTypeToAction(command_info.type); + + // Skip `command_info` if: + // 1. It does not have a corresponding ID; OR + // 2. Its corresponding ID is not for an in-progress command; OR + // 3. It does not have a corresponding item action. + if (!id || !holding_space_util::IsInProgressCommand(*id) || !item_action) { + continue; } + + in_progress_commands.emplace_back( + *id, command_info.text_id, command_info.icon, + base::BindRepeating( + [](holding_space_metrics::ItemAction action, + const base::RepeatingClosure& command_callback, + const HoldingSpaceItem* item, HoldingSpaceCommandId command_id, + holding_space_metrics::EventSource event_source) { + command_callback.Run(); + holding_space_metrics::RecordItemAction( + /*items=*/{item}, action, event_source); + }, + *item_action, command_info.command_callback)); } // Specify the backing file.
diff --git a/chrome/browser/ui/ash/download_status/holding_space_display_client_browsertest.cc b/chrome/browser/ui/ash/download_status/holding_space_display_client_browsertest.cc index 5a1f215..6ddf84c 100644 --- a/chrome/browser/ui/ash/download_status/holding_space_display_client_browsertest.cc +++ b/chrome/browser/ui/ash/download_status/holding_space_display_client_browsertest.cc
@@ -137,17 +137,18 @@ Profile* const profile = ProfileManager::GetActiveUserProfile(); crosapi::mojom::DownloadStatusPtr in_progress_download = CreateInProgressDownloadStatus(profile, + /*extension=*/"txt", /*received_bytes=*/0, /*total_bytes=*/1024); in_progress_download->cancellable = true; Update(in_progress_download->Clone()); - crosapi::mojom::DownloadStatusPtr completed_download = - CreateDownloadStatus(profile, crosapi::mojom::DownloadState::kComplete, - crosapi::mojom::DownloadProgress::New( - /*loop=*/false, - /*received_bytes=*/1024, - /*total_bytes=*/1024, - /*visible=*/false)); + crosapi::mojom::DownloadStatusPtr completed_download = CreateDownloadStatus( + profile, /*extension=*/"txt", crosapi::mojom::DownloadState::kComplete, + crosapi::mojom::DownloadProgress::New( + /*loop=*/false, + /*received_bytes=*/1024, + /*total_bytes=*/1024, + /*visible=*/false)); Update(completed_download->Clone()); test_api().Show(); @@ -258,12 +259,13 @@ Profile* const profile = ProfileManager::GetActiveUserProfile(); crosapi::mojom::DownloadStatusPtr in_progress_download = CreateInProgressDownloadStatus(profile, + /*extension=*/"txt", /*received_bytes=*/0, /*total_bytes=*/1024); in_progress_download->cancellable = true; Update(in_progress_download->Clone()); crosapi::mojom::DownloadStatusPtr completed_download = CreateDownloadStatus( - profile, crosapi::mojom::DownloadState::kComplete, + profile, /*extension=*/"txt", crosapi::mojom::DownloadState::kComplete, crosapi::mojom::DownloadProgress::New( /*loop=*/false, /*received_bytes=*/1024, /*total_bytes=*/1024, /*visible=*/false)); @@ -367,14 +369,14 @@ IN_PROC_BROWSER_TEST_F(HoldingSpaceDisplayClientBrowserTest, ClickCompletedDownloadChip) { // Add a completed download. - crosapi::mojom::DownloadStatusPtr download = - CreateDownloadStatus(ProfileManager::GetActiveUserProfile(), - crosapi::mojom::DownloadState::kComplete, - crosapi::mojom::DownloadProgress::New( - /*loop=*/false, - /*received_bytes=*/1024, - /*total_bytes=*/1024, - /*visible=*/false)); + crosapi::mojom::DownloadStatusPtr download = CreateDownloadStatus( + ProfileManager::GetActiveUserProfile(), + /*extension=*/"txt", crosapi::mojom::DownloadState::kComplete, + crosapi::mojom::DownloadProgress::New( + /*loop=*/false, + /*received_bytes=*/1024, + /*total_bytes=*/1024, + /*visible=*/false)); Update(download->Clone()); test_api().Show(); @@ -416,6 +418,7 @@ // Add an in-progress download. crosapi::mojom::DownloadStatusPtr download = CreateInProgressDownloadStatus(ProfileManager::GetActiveUserProfile(), + /*extension=*/"txt", /*received_bytes=*/0, /*total_bytes=*/1024); Update(download->Clone()); @@ -456,6 +459,7 @@ Profile* const active_profile = ProfileManager::GetActiveUserProfile(); crosapi::mojom::DownloadStatusPtr download = CreateInProgressDownloadStatus(active_profile, + /*extension=*/"txt", /*received_bytes=*/0, /*total_bytes=*/1024); Update(download->Clone()); @@ -535,6 +539,7 @@ // Add a new in-progress download with the duplicate download guid. crosapi::mojom::DownloadStatusPtr duplicate_download = CreateInProgressDownloadStatus(active_profile, + /*extension=*/"txt", /*received_bytes=*/0, /*total_bytes=*/1024); duplicate_download->guid = download->guid; @@ -548,8 +553,9 @@ IN_PROC_BROWSER_TEST_F(HoldingSpaceDisplayClientBrowserTest, IndeterminateDownload) { // Create a download with an unknown total bytes count. - crosapi::mojom::DownloadStatusPtr download = CreateInProgressDownloadStatus( - ProfileManager::GetActiveUserProfile(), /*received_bytes=*/0); + crosapi::mojom::DownloadStatusPtr download = + CreateInProgressDownloadStatus(ProfileManager::GetActiveUserProfile(), + /*extension=*/"txt", /*received_bytes=*/0); Update(download->Clone()); test_api().Show(); @@ -568,6 +574,7 @@ // could happen when a download is blocked. crosapi::mojom::DownloadStatusPtr download = CreateInProgressDownloadStatus(ProfileManager::GetActiveUserProfile(), + /*extension=*/"txt", /*received_bytes=*/0, /*total_bytes=*/1024); download->progress->visible = false; @@ -592,6 +599,7 @@ InterruptDownload) { crosapi::mojom::DownloadStatusPtr download = CreateInProgressDownloadStatus(ProfileManager::GetActiveUserProfile(), + /*extension=*/"txt", /*received_bytes=*/0, /*total_bytes=*/1024); Update(download->Clone()); @@ -610,6 +618,7 @@ PauseAndResumeDownloadViaContextMenu) { crosapi::mojom::DownloadStatusPtr download = CreateInProgressDownloadStatus(ProfileManager::GetActiveUserProfile(), + /*extension=*/"txt", /*received_bytes=*/0, /*total_bytes=*/1024); download->pausable = true; @@ -689,6 +698,7 @@ PauseAndResumeDownloadViaSecondaryAction) { crosapi::mojom::DownloadStatusPtr download = CreateInProgressDownloadStatus(ProfileManager::GetActiveUserProfile(), + /*extension=*/"txt", /*received_bytes=*/0, /*total_bytes=*/1024); download->pausable = true; @@ -773,6 +783,7 @@ IN_PROC_BROWSER_TEST_F(HoldingSpaceDisplayClientBrowserTest, SecondaryLabel) { crosapi::mojom::DownloadStatusPtr download = CreateInProgressDownloadStatus(ProfileManager::GetActiveUserProfile(), + /*extension=*/"txt", /*received_bytes=*/0, /*total_bytes=*/1024); Update(download->Clone()); @@ -806,6 +817,7 @@ ServiceSuspendedDuringDownload) { crosapi::mojom::DownloadStatusPtr download = CreateInProgressDownloadStatus(ProfileManager::GetActiveUserProfile(), + /*extension=*/"txt", /*received_bytes=*/0, /*total_bytes=*/1024); Update(download->Clone()); @@ -849,6 +861,7 @@ // Create an in-progress download that can be canceled and paused. crosapi::mojom::DownloadStatusPtr download = CreateInProgressDownloadStatus(ProfileManager::GetActiveUserProfile(), + /*extension=*/"txt", /*received_bytes=*/0, /*total_bytes=*/1024); download->cancellable = true;
diff --git a/chrome/browser/ui/ash/download_status/notification_display_client.cc b/chrome/browser/ui/ash/download_status/notification_display_client.cc index a655207..1502f2fa 100644 --- a/chrome/browser/ui/ash/download_status/notification_display_client.cc +++ b/chrome/browser/ui/ash/download_status/notification_display_client.cc
@@ -144,6 +144,8 @@ switch (command) { case CommandType::kCancel: return "DownloadNotificationV2.Button_Cancel"; + case CommandType::kCopyToClipboard: + return "DownloadNotificationV2.Button_CopyToClipboard"; case CommandType::kOpenFile: return "DownloadNotificationV2.Click_Completed"; case CommandType::kPause: @@ -167,6 +169,7 @@ case CommandType::kShowInBrowser: return true; case CommandType::kCancel: + case CommandType::kCopyToClipboard: case CommandType::kPause: case CommandType::kResume: case CommandType::kShowInFolder: @@ -180,6 +183,7 @@ bool IsButtonClickCommandType(CommandType command) { switch (command) { case CommandType::kCancel: + case CommandType::kCopyToClipboard: case CommandType::kPause: case CommandType::kResume: case CommandType::kShowInFolder:
diff --git a/chrome/browser/ui/ash/download_status/notification_display_client_browsertest.cc b/chrome/browser/ui/ash/download_status/notification_display_client_browsertest.cc index 6e8f758..1200f68 100644 --- a/chrome/browser/ui/ash/download_status/notification_display_client_browsertest.cc +++ b/chrome/browser/ui/ash/download_status/notification_display_client_browsertest.cc
@@ -53,6 +53,8 @@ #include "ui/aura/env_observer.h" #include "ui/aura/test/find_window.h" #include "ui/aura/window.h" +#include "ui/base/clipboard/clipboard.h" +#include "ui/base/clipboard/clipboard_buffer.h" #include "ui/base/l10n/l10n_util.h" #include "ui/gfx/image/image_unittest_util.h" #include "ui/message_center/public/cpp/notification.h" @@ -75,7 +77,9 @@ using ::testing::AllOf; using ::testing::Contains; using ::testing::Each; +using ::testing::ElementsAre; using ::testing::Eq; +using ::testing::Field; using ::testing::Mock; using ::testing::NiceMock; using ::testing::Not; @@ -123,6 +127,8 @@ switch (command_type) { case CommandType::kCancel: return IDS_ASH_DOWNLOAD_COMMAND_TEXT_CANCEL; + case CommandType::kCopyToClipboard: + return IDS_ASH_DOWNLOAD_COMMAND_TEXT_COPY_TO_CLIPBOARD; case CommandType::kOpenFile: NOTREACHED_NORETURN(); case CommandType::kPause: @@ -251,6 +257,7 @@ })); crosapi::mojom::DownloadStatusPtr uncancellable_download = CreateInProgressDownloadStatus(profile, + /*extension=*/"txt", /*received_bytes=*/0, /*total_bytes=*/1024); uncancellable_download->cancellable = false; @@ -278,6 +285,7 @@ })); crosapi::mojom::DownloadStatusPtr cancellable_download = CreateInProgressDownloadStatus(profile, + /*extension=*/"txt", /*received_bytes=*/0, /*total_bytes=*/1024); cancellable_download->cancellable = true; @@ -335,13 +343,13 @@ [¬ification_id](const message_center::Notification& notification) { notification_id = notification.id(); })); - crosapi::mojom::DownloadStatusPtr download = - CreateDownloadStatus(profile, crosapi::mojom::DownloadState::kComplete, - crosapi::mojom::DownloadProgress::New( - /*loop=*/false, - /*received_bytes=*/1024, - /*total_bytes=*/1024, - /*visible=*/false)); + crosapi::mojom::DownloadStatusPtr download = CreateDownloadStatus( + profile, /*extension=*/"txt", crosapi::mojom::DownloadState::kComplete, + crosapi::mojom::DownloadProgress::New( + /*loop=*/false, + /*received_bytes=*/1024, + /*total_bytes=*/1024, + /*visible=*/false)); Update(download->Clone()); Mock::VerifyAndClearExpectations(&service_observer()); @@ -384,6 +392,7 @@ })); crosapi::mojom::DownloadStatusPtr download = CreateInProgressDownloadStatus(profile, + /*extension=*/"txt", /*received_bytes=*/0, /*total_bytes=*/1024); Update(download->Clone()); @@ -418,9 +427,10 @@ // still show. IN_PROC_BROWSER_TEST_F(NotificationDisplayClientBrowserTest, CompleteDownload) { Profile* const profile = ProfileManager::GetActiveUserProfile(); - crosapi::mojom::DownloadStatusPtr download = - CreateDownloadStatus(profile, crosapi::mojom::DownloadState::kInProgress, - /*progress=*/nullptr); + crosapi::mojom::DownloadStatusPtr download = CreateDownloadStatus( + profile, + /*extension=*/"txt", crosapi::mojom::DownloadState::kInProgress, + /*progress=*/nullptr); EXPECT_FALSE(download->target_file_path); std::string notification_id; @@ -544,6 +554,7 @@ Profile* const profile = ProfileManager::GetActiveUserProfile(); crosapi::mojom::DownloadStatusPtr download = CreateInProgressDownloadStatus(profile, + /*extension=*/"txt", /*received_bytes=*/0, /*total_bytes=*/1024); Update(download->Clone()); @@ -580,12 +591,14 @@ notification_id = notification.id(); })); - // Create a download. + // Create an image download. Profile* const profile = ProfileManager::GetActiveUserProfile(); - crosapi::mojom::DownloadStatusPtr download = - CreateInProgressDownloadStatus(profile, - /*received_bytes=*/0, - /*total_bytes=*/1024); + crosapi::mojom::DownloadStatusPtr download = CreateDownloadStatus( + profile, + /*extension=*/"png", crosapi::mojom::DownloadState::kInProgress, + crosapi::mojom::DownloadProgress::New( + /*loop=*/false, /*received_bytes=*/0, + /*total_bytes=*/1024, /*visible=*/true)); Update(download->Clone()); Mock::VerifyAndClearExpectations(&service_observer()); @@ -614,6 +627,49 @@ *large_image_view->original_image().bitmap(), gfx::test::CreateBitmap(/*width=*/360, /*height=*/240, image_color))); + + // An in-progress image download's notification should not have a 'Copy to + // clipboard' button. + const std::u16string copy_to_clipboard_button_text = + l10n_util::GetStringUTF16( + GetCommandTextId(CommandType::kCopyToClipboard)); + EXPECT_THAT( + popup_view->GetActionButtonsForTest(), + Not(Contains(Pointee(Property(&views::LabelButton::GetText, + Eq(copy_to_clipboard_button_text)))))); + + // Complete `download`. Then check action buttons. + MarkDownloadStatusCompleted(*download); + Update(download->Clone()); + const std::vector<raw_ptr<views::LabelButton, VectorExperimental>> + action_buttons = popup_view->GetActionButtonsForTest(); + EXPECT_THAT( + action_buttons, + ElementsAre( + Pointee(Property(&views::LabelButton::GetText, + Eq(l10n_util::GetStringUTF16( + GetCommandTextId(CommandType::kShowInFolder))))), + Pointee(Property(&views::LabelButton::GetText, + Eq(copy_to_clipboard_button_text))))); + + // Click the 'Copy to clipboard' button. Then verify the click is recorded. + base::UserActionTester tester; + auto copy_to_clipboard_button_iter = + base::ranges::find(action_buttons, copy_to_clipboard_button_text, + &views::LabelButton::GetText); + ASSERT_NE(copy_to_clipboard_button_iter, action_buttons.cend()); + test::Click(*copy_to_clipboard_button_iter, ui::EF_NONE); + EXPECT_EQ( + tester.GetActionCount("DownloadNotificationV2.Button_CopyToClipboard"), + 1); + + // Verify the filename in the clipboard as expected. + base::test::TestFuture<std::vector<ui::FileInfo>> test_future; + ui::Clipboard::GetForCurrentThread()->ReadFilenames( + ui::ClipboardBuffer::kCopyPaste, + /*data_dst=*/nullptr, test_future.GetCallback()); + EXPECT_THAT(test_future.Get(), + ElementsAre(Field(&ui::FileInfo::path, *download->full_path))); } // Verifies that the notification of a download with an unknown total bytes @@ -630,6 +686,7 @@ Profile* const profile = ProfileManager::GetActiveUserProfile(); crosapi::mojom::DownloadStatusPtr download = CreateInProgressDownloadStatus(profile, + /*extension=*/"txt", /*received_bytes=*/0); Update(download->Clone()); @@ -668,6 +725,7 @@ })); crosapi::mojom::DownloadStatusPtr download = CreateInProgressDownloadStatus(ProfileManager::GetActiveUserProfile(), + /*extension=*/"txt", /*received_bytes=*/0, /*total_bytes=*/1024); Update(download->Clone()); @@ -691,6 +749,7 @@ })); crosapi::mojom::DownloadStatusPtr download = CreateInProgressDownloadStatus(profile, + /*extension=*/"txt", /*received_bytes=*/0, /*total_bytes=*/1024); download->pausable = true; @@ -803,6 +862,7 @@ })); crosapi::mojom::DownloadStatusPtr download = CreateInProgressDownloadStatus(profile, + /*extension=*/"txt", /*received_bytes=*/0, /*total_bytes=*/1024); Update(download->Clone()); @@ -860,6 +920,7 @@ Profile* const profile = ProfileManager::GetActiveUserProfile(); crosapi::mojom::DownloadStatusPtr download = CreateInProgressDownloadStatus(profile, + /*extension=*/"txt", /*received_bytes=*/0, /*total_bytes=*/1024); download->cancellable = true;
diff --git a/chrome/browser/ui/aura/accessibility/automation_manager_aura_browsertest.cc b/chrome/browser/ui/aura/accessibility/automation_manager_aura_browsertest.cc index f0ee274..f22d9d0 100644 --- a/chrome/browser/ui/aura/accessibility/automation_manager_aura_browsertest.cc +++ b/chrome/browser/ui/aura/accessibility/automation_manager_aura_browsertest.cc
@@ -253,19 +253,22 @@ views::View* view1 = new views::View(); view1->GetViewAccessibility().SetRole(ax::mojom::Role::kDialog); - view1->GetViewAccessibility().OverrideName("view1"); + view1->GetViewAccessibility().SetName("view1", + ax::mojom::NameFrom::kAttribute); view1->SetFocusBehavior(views::View::FocusBehavior::ALWAYS); widget->GetRootView()->AddChildView(view1); views::AXAuraObjWrapper* wrapper1 = cache_ptr->GetOrCreate(view1); views::View* view2 = new views::View(); view2->SetFocusBehavior(views::View::FocusBehavior::ALWAYS); view2->GetViewAccessibility().SetRole(ax::mojom::Role::kDialog); - view2->GetViewAccessibility().OverrideName("view2"); + view2->GetViewAccessibility().SetName("view2", + ax::mojom::NameFrom::kAttribute); widget->GetRootView()->AddChildView(view2); views::AXAuraObjWrapper* wrapper2 = cache_ptr->GetOrCreate(view2); views::View* view3 = new views::View(); view3->GetViewAccessibility().SetRole(ax::mojom::Role::kDialog); - view3->GetViewAccessibility().OverrideName("view3"); + view3->GetViewAccessibility().SetName("view3", + ax::mojom::NameFrom::kAttribute); view3->SetFocusBehavior(views::View::FocusBehavior::ALWAYS); widget->GetRootView()->AddChildView(view3); views::AXAuraObjWrapper* wrapper3 = cache_ptr->GetOrCreate(view3); @@ -528,13 +531,15 @@ views::View* view1 = new views::View(); view1->GetViewAccessibility().SetRole(ax::mojom::Role::kDialog); - view1->GetViewAccessibility().OverrideName("view1"); + view1->GetViewAccessibility().SetName("view1", + ax::mojom::NameFrom::kAttribute); view1->SetFocusBehavior(views::View::FocusBehavior::ALWAYS); widget->GetRootView()->AddChildView(view1); views::View* view2 = new views::View(); view2->SetFocusBehavior(views::View::FocusBehavior::ALWAYS); view2->GetViewAccessibility().SetRole(ax::mojom::Role::kDialog); - view2->GetViewAccessibility().OverrideName("view2"); + view2->GetViewAccessibility().SetName("view2", + ax::mojom::NameFrom::kAttribute); widget->GetRootView()->AddChildView(view2); views::AXAuraObjWrapper* wrapper2 = cache_ptr->GetOrCreate(view2);
diff --git a/chrome/browser/ui/autofill/chrome_autofill_client.cc b/chrome/browser/ui/autofill/chrome_autofill_client.cc index 92d0ea40..41a59c48 100644 --- a/chrome/browser/ui/autofill/chrome_autofill_client.cc +++ b/chrome/browser/ui/autofill/chrome_autofill_client.cc
@@ -58,7 +58,7 @@ #include "chrome/browser/ui/page_info/page_info_dialog.h" #include "chrome/browser/ui/passwords/ui_utils.h" #include "chrome/browser/ui/plus_addresses/plus_address_creation_controller.h" -#include "chrome/browser/web_data_service_factory.h" +#include "chrome/browser/webdata_services/web_data_service_factory.h" #include "chrome/common/channel_info.h" #include "chrome/common/url_constants.h" #include "components/autofill/content/browser/autofill_log_router_factory.h" @@ -1211,11 +1211,12 @@ } void ChromeAutofillClient::ShowAutofillErrorDialog( - const AutofillErrorDialogContext& context) { - autofill_error_dialog_controller_.Show( - context, + AutofillErrorDialogContext context) { + autofill_error_dialog_controller_ = + std::make_unique<AutofillErrorDialogControllerImpl>(std::move(context)); + autofill_error_dialog_controller_->Show( base::BindOnce(&CreateAndShowAutofillErrorDialog, - base::Unretained(&autofill_error_dialog_controller_), + base::Unretained(autofill_error_dialog_controller_.get()), base::Unretained(web_contents()))); }
diff --git a/chrome/browser/ui/autofill/chrome_autofill_client.h b/chrome/browser/ui/autofill/chrome_autofill_client.h index fd73d42..6685cc4 100644 --- a/chrome/browser/ui/autofill/chrome_autofill_client.h +++ b/chrome/browser/ui/autofill/chrome_autofill_client.h
@@ -248,8 +248,7 @@ void DismissOfferNotification() override; void OnVirtualCardDataAvailable( const VirtualCardManualFallbackBubbleOptions& options) override; - void ShowAutofillErrorDialog( - const AutofillErrorDialogContext& context) override; + void ShowAutofillErrorDialog(AutofillErrorDialogContext context) override; void TriggerUserPerceptionOfAutofillSurvey( const std::map<std::string, std::string>& field_filling_stats_data) override; @@ -344,7 +343,8 @@ autofill_cvc_save_message_delegate_; #endif std::unique_ptr<CardUnmaskPromptControllerImpl> unmask_controller_; - AutofillErrorDialogControllerImpl autofill_error_dialog_controller_; + std::unique_ptr<AutofillErrorDialogControllerImpl> + autofill_error_dialog_controller_; std::unique_ptr<CardUnmaskOtpInputDialogControllerImpl> card_unmask_otp_input_dialog_controller_; };
diff --git a/chrome/browser/ui/autofill/payments/desktop_payments_window_manager.cc b/chrome/browser/ui/autofill/payments/desktop_payments_window_manager.cc index fe762b54..c985149 100644 --- a/chrome/browser/ui/autofill/payments/desktop_payments_window_manager.cc +++ b/chrome/browser/ui/autofill/payments/desktop_payments_window_manager.cc
@@ -58,28 +58,40 @@ params.source_contents = &source_contents; params.is_tab_modal_popup = true; - // TODO(crbug.com/1517762): Handle the case where the pop-up is not shown by - // displaying an error message. if (base::WeakPtr<content::NavigationHandle> navigation_handle = Navigate(¶ms)) { content::WebContentsObserver::Observe(navigation_handle->GetWebContents()); + } else { + client_->ShowAutofillErrorDialog( + AutofillErrorDialogContext::WithVirtualCardPermanentOrTemporaryError( + /*is_permanent_error=*/false)); } } void DesktopPaymentsWindowManager::OnWebContentsDestroyedForVcn3ds() { flow_type_ = FlowType::kNoFlow; - if (base::expected<PaymentsWindowManager::RedirectCompletionProof, - PaymentsWindowManager::Vcn3dsAuthenticationPopupErrorType> - result = ParseFinalUrlForVcn3ds(web_contents()->GetVisibleURL()); - result.has_value()) { + base::expected<RedirectCompletionProof, Vcn3dsAuthenticationPopupErrorType> + result = ParseFinalUrlForVcn3ds(web_contents()->GetVisibleURL()); + if (result.has_value()) { CHECK(!result.value()->empty()); return client_->GetPaymentsAutofillClient()->LoadRiskData(base::BindOnce( &DesktopPaymentsWindowManager::OnDidLoadRiskDataForVcn3ds, weak_ptr_factory_.GetWeakPtr(), std::move(result.value()))); } - // TODO(crbug.com/1517762): Trigger an error dialog if `result` is - // `kAuthenticationFailed`. + switch (result.error()) { + case Vcn3dsAuthenticationPopupErrorType::kAuthenticationFailed: + case Vcn3dsAuthenticationPopupErrorType::kInvalidQueryParams: + client_->ShowAutofillErrorDialog( + AutofillErrorDialogContext::WithVirtualCardPermanentOrTemporaryError( + /*is_permanent_error=*/true)); + break; + case Vcn3dsAuthenticationPopupErrorType::kAuthenticationNotCompleted: + break; + } + + // In the case of an error, we show the user an error dialog but still run the + // callback to let the caller know failure occurred. std::move(vcn_3ds_context_->completion_callback) .Run(Vcn3dsAuthenticationResponse()); vcn_3ds_context_.reset(); @@ -109,8 +121,12 @@ client_->GetPaymentsAutofillClient()->CloseAutofillProgressDialog( /*show_confirmation_before_closing=*/response.card.has_value(), /*no_interactive_authentication_callback=*/base::OnceClosure()); - // TODO(crbug.com/1517762): Trigger an error dialog if no card is present in - // `response`. + if (!response.card.has_value()) { + client_->ShowAutofillErrorDialog( + AutofillErrorDialogContext::WithVirtualCardPermanentOrTemporaryError( + /*is_permanent_error=*/true)); + } + std::move(vcn_3ds_context_->completion_callback).Run(std::move(response)); vcn_3ds_context_.reset(); }
diff --git a/chrome/browser/ui/autofill/payments/desktop_payments_window_manager_interactive_uitest.cc b/chrome/browser/ui/autofill/payments/desktop_payments_window_manager_interactive_uitest.cc index 23f30fa..26ec4bc 100644 --- a/chrome/browser/ui/autofill/payments/desktop_payments_window_manager_interactive_uitest.cc +++ b/chrome/browser/ui/autofill/payments/desktop_payments_window_manager_interactive_uitest.cc
@@ -234,6 +234,7 @@ EXPECT_EQ(response->card->expiration_year(), expiration_year); EXPECT_EQ(response->card->record_type(), CreditCard::RecordType::kVirtualCard); + EXPECT_FALSE(client()->autofill_error_dialog_shown()); } // Test that the VCN 3DS pop-up is shown correctly, and on close an @@ -264,6 +265,7 @@ authentication_response(); ASSERT_TRUE(response.has_value()); EXPECT_FALSE(response->card.has_value()); + EXPECT_TRUE(client()->autofill_error_dialog_shown()); } // Test that the VCN 3DS pop-up is shown correctly, and on close an @@ -285,6 +287,7 @@ authentication_response(); ASSERT_TRUE(response.has_value()); EXPECT_FALSE(response->card.has_value()); + EXPECT_FALSE(client()->autofill_error_dialog_shown()); } // Test that the VCN 3DS pop-up is shown correctly, and on close an @@ -314,6 +317,7 @@ authentication_response(); ASSERT_TRUE(response.has_value()); EXPECT_FALSE(response->card.has_value()); + EXPECT_TRUE(client()->autofill_error_dialog_shown()); } // Test that the VCN 3DS pop-up is shown correctly, and when the user cancels
diff --git a/chrome/browser/ui/browser_commands.cc b/chrome/browser/ui/browser_commands.cc index c2a9e1d3..32e77c6 100644 --- a/chrome/browser/ui/browser_commands.cc +++ b/chrome/browser/ui/browser_commands.cc
@@ -1889,12 +1889,12 @@ void OpenFeedbackDialog(Browser* browser, FeedbackSource source, - const std::string& description_template) { + const std::string& description_template, + const std::string& category_tag) { base::RecordAction(UserMetricsAction("Feedback")); chrome::ShowFeedbackPage(browser, source, description_template, std::string() /* description_placeholder_text */, - std::string() /* category_tag */, - std::string() /* extra_diagnostics */); + category_tag, std::string() /* extra_diagnostics */); } void ToggleBookmarkBar(Browser* browser) {
diff --git a/chrome/browser/ui/browser_commands.h b/chrome/browser/ui/browser_commands.h index c4aff62..7f52ce0 100644 --- a/chrome/browser/ui/browser_commands.h +++ b/chrome/browser/ui/browser_commands.h
@@ -223,10 +223,10 @@ bool CanOpenTaskManager(); // Opens task manager UI. Note that |browser| can be nullptr as input. void OpenTaskManager(Browser* browser); -void OpenFeedbackDialog( - Browser* browser, - FeedbackSource source, - const std::string& description_template = std::string()); +void OpenFeedbackDialog(Browser* browser, + FeedbackSource source, + const std::string& description_template = std::string(), + const std::string& category_tag = std::string()); void ToggleBookmarkBar(Browser* browser); void ToggleShowFullURLs(Browser* browser); void ShowAppMenu(Browser* browser);
diff --git a/chrome/browser/ui/chrome_pages.h b/chrome/browser/ui/chrome_pages.h index 227fcba8..86fb34c3 100644 --- a/chrome/browser/ui/chrome_pages.h +++ b/chrome/browser/ui/chrome_pages.h
@@ -127,6 +127,7 @@ kFeedbackSourceLogin, kFeedbackSourceAI, kFeedbackSourceFocusMode, + kFeedbackSourceOverview, // ATTENTION: Before making any changes or adding to feedback collection, // please ensure the teams that operationalize feedback are aware and
diff --git a/chrome/browser/ui/color/chrome_color_id.h b/chrome/browser/ui/color/chrome_color_id.h index 6d8f25c..86443a5d 100644 --- a/chrome/browser/ui/color/chrome_color_id.h +++ b/chrome/browser/ui/color/chrome_color_id.h
@@ -337,6 +337,7 @@ E_CPONLY(kColorPageInfoChosenObjectDeleteButtonIconDisabled) \ E_CPONLY(kColorPageInfoIconHover) \ E_CPONLY(kColorPageInfoIconPressed) \ + E_CPONLY(kColorPageInfoPermissionUsedIcon) \ /* Payments colors. */ \ E_CPONLY(kColorPaymentsFeedbackTipBackground) \ E_CPONLY(kColorPaymentsFeedbackTipBorder) \
diff --git a/chrome/browser/ui/color/material_chrome_color_mixer.cc b/chrome/browser/ui/color/material_chrome_color_mixer.cc index 4c69799..7668f21b 100644 --- a/chrome/browser/ui/color/material_chrome_color_mixer.cc +++ b/chrome/browser/ui/color/material_chrome_color_mixer.cc
@@ -70,6 +70,9 @@ mixer[kColorExtensionsMenuText] = {ui::kColorSysOnSurface}; mixer[kColorExtensionsMenuSecondaryText] = {ui::kColorSysOnSurfaceSubtle}; + // PageInfo colors. + mixer[kColorPageInfoPermissionUsedIcon] = {ui::kColorSysPrimary}; + // Permission Prompt colors. mixer[kColorPermissionPromptRequestText] = {ui::kColorSysOnSurfaceSubtle};
diff --git a/chrome/browser/ui/extensions/extension_action_view_controller.cc b/chrome/browser/ui/extensions/extension_action_view_controller.cc index e5d226e..fc33584 100644 --- a/chrome/browser/ui/extensions/extension_action_view_controller.cc +++ b/chrome/browser/ui/extensions/extension_action_view_controller.cc
@@ -589,6 +589,12 @@ const GURL popup_url = extension_action_->GetPopupUrl(tab_id); + // Skip popup if there is an open security UI that would be covered by it, + // mitigation occlusion/spoofing risks. + if (extensions_container_->HasBlockingSecurityUI()) { + return; + } + std::unique_ptr<extensions::ExtensionViewHost> host = extensions::ExtensionViewHostFactory::CreatePopupHost(popup_url, browser_);
diff --git a/chrome/browser/ui/extensions/extensions_container.h b/chrome/browser/ui/extensions/extensions_container.h index 9bc14c7..3c79588 100644 --- a/chrome/browser/ui/extensions/extensions_container.h +++ b/chrome/browser/ui/extensions/extensions_container.h
@@ -77,6 +77,10 @@ // Whether there are any Extensions registered with the ExtensionsContainer. virtual bool HasAnyExtensions() const = 0; + // Whether there is any security UI in the browser window that would + // overlap with the extensions popup. + virtual bool HasBlockingSecurityUI() const = 0; + // Updates the hover card for `action_view` based on `update_type`. virtual void UpdateToolbarActionHoverCard( ToolbarActionView* action_view,
diff --git a/chrome/browser/ui/messages/android/java/src/org/chromium/chrome/browser/ui/messages/snackbar/Snackbar.java b/chrome/browser/ui/messages/android/java/src/org/chromium/chrome/browser/ui/messages/snackbar/Snackbar.java index a529ede..ebeab406 100644 --- a/chrome/browser/ui/messages/android/java/src/org/chromium/chrome/browser/ui/messages/snackbar/Snackbar.java +++ b/chrome/browser/ui/messages/android/java/src/org/chromium/chrome/browser/ui/messages/snackbar/Snackbar.java
@@ -115,6 +115,7 @@ public static final int UMA_QUICK_DELETE = 60; public static final int UMA_AUTO_TRANSLATE = 61; public static final int UMA_BOOKMARK_MOVED = 62; + public static final int UMA_CLEAR_BROWSING_DATA = 63; private @Nullable SnackbarController mController; private CharSequence mText;
diff --git a/chrome/browser/ui/page_info/page_info_unittest.cc b/chrome/browser/ui/page_info/page_info_unittest.cc index acb633e..5691c6f 100644 --- a/chrome/browser/ui/page_info/page_info_unittest.cc +++ b/chrome/browser/ui/page_info/page_info_unittest.cc
@@ -152,7 +152,6 @@ // TODO(crbug.com/1344787): Fix tests and enable the feature. scoped_feature_list_.InitWithFeatures( { - permissions::features::kPermissionStorageAccessAPI, #if !BUILDFLAG(IS_ANDROID) features::kFileSystemAccessPersistentPermissions, #endif
diff --git a/chrome/browser/ui/views/autofill/payments/autofill_error_dialog_view_native_views_browsertest.cc b/chrome/browser/ui/views/autofill/payments/autofill_error_dialog_view_native_views_browsertest.cc index b4102bb..4831b7a 100644 --- a/chrome/browser/ui/views/autofill/payments/autofill_error_dialog_view_native_views_browsertest.cc +++ b/chrome/browser/ui/views/autofill/payments/autofill_error_dialog_view_native_views_browsertest.cc
@@ -26,10 +26,7 @@ : public DialogBrowserTest, public testing::WithParamInterface<std::tuple<bool, bool>> { public: - AutofillErrorDialogViewNativeViewsBrowserTest() { - autofill_error_dialog_controller_ = - std::make_unique<AutofillErrorDialogControllerImpl>(); - } + AutofillErrorDialogViewNativeViewsBrowserTest() = default; ~AutofillErrorDialogViewNativeViewsBrowserTest() override = default; @@ -61,8 +58,10 @@ AutofillErrorDialogType::kVirtualCardNotEligibleError; } + autofill_error_dialog_controller_ = + std::make_unique<AutofillErrorDialogControllerImpl>( + autofill_error_dialog_context); autofill_error_dialog_controller_->Show( - autofill_error_dialog_context, base::BindOnce(&CreateAndShowAutofillErrorDialog, base::Unretained(controller()), base::Unretained(contents())));
diff --git a/chrome/browser/ui/views/autofill/payments/local_card_migration_uitest.cc b/chrome/browser/ui/views/autofill/payments/local_card_migration_uitest.cc index 2314f03d..de84c84 100644 --- a/chrome/browser/ui/views/autofill/payments/local_card_migration_uitest.cc +++ b/chrome/browser/ui/views/autofill/payments/local_card_migration_uitest.cc
@@ -45,7 +45,7 @@ #include "chrome/browser/ui/views/page_action/page_action_icon_loading_indicator_view.h" #include "chrome/browser/ui/views/page_action/page_action_icon_view.h" #include "chrome/browser/ui/views/toolbar/toolbar_view.h" -#include "chrome/browser/web_data_service_factory.h" +#include "chrome/browser/webdata_services/web_data_service_factory.h" #include "chrome/grit/generated_resources.h" #include "chrome/test/base/in_process_browser_test.h" #include "chrome/test/base/ui_test_utils.h"
diff --git a/chrome/browser/ui/views/autofill/popup/popup_pixel_test.h b/chrome/browser/ui/views/autofill/popup/popup_pixel_test.h index 3008e33..54b1333 100644 --- a/chrome/browser/ui/views/autofill/popup/popup_pixel_test.h +++ b/chrome/browser/ui/views/autofill/popup/popup_pixel_test.h
@@ -37,36 +37,37 @@ // By default, the test class has two parameters: Dark vs light mode and RTL vs // LTR for the text direction of the browser language. template <std::derived_from<PopupBaseView> View, - std::derived_from<AutofillPopupViewDelegate> Controller> + std::derived_from<AutofillPopupViewDelegate> Controller, + typename ParameterType = TestParameterType> class PopupPixelTest : public UiBrowserTest, - public testing::WithParamInterface<TestParameterType> { + public testing::WithParamInterface<ParameterType> { public: PopupPixelTest() = default; ~PopupPixelTest() override = default; - static bool IsDarkModeOn(const TestParameterType& param) { + static bool IsDarkModeOn(const ParameterType& param) { return std::get<0>(param); } - static bool IsBrowserLanguageRTL(const TestParameterType& param) { + static bool IsBrowserLanguageRTL(const ParameterType& param) { return std::get<1>(param); } static std::string GetTestSuffix( - const testing::TestParamInfo<TestParameterType>& param_info) { + const testing::TestParamInfo<ParameterType>& param_info) { return base::StrCat( {IsDarkModeOn(param_info.param) ? "Dark" : "Light", IsBrowserLanguageRTL(param_info.param) ? "BrowserRTL" : "BrowserLTR"}); } void SetUpCommandLine(base::CommandLine* command_line) override { - if (IsDarkModeOn(GetParam())) { + if (IsDarkModeOn(this->GetParam())) { command_line->AppendSwitch(switches::kForceDarkMode); } } void SetUpOnMainThread() override { UiBrowserTest::SetUpOnMainThread(); - base::i18n::SetRTLForTesting(IsBrowserLanguageRTL(GetParam())); + base::i18n::SetRTLForTesting(IsBrowserLanguageRTL(this->GetParam())); content::WebContents* web_contents = browser()->tab_strip_model()->GetActiveWebContents();
diff --git a/chrome/browser/ui/views/autofill/popup/popup_row_view.cc b/chrome/browser/ui/views/autofill/popup/popup_row_view.cc index 3e0c511..53c5ebd 100644 --- a/chrome/browser/ui/views/autofill/popup/popup_row_view.cc +++ b/chrome/browser/ui/views/autofill/popup/popup_row_view.cc
@@ -245,9 +245,11 @@ content_view_->AddObserver(this); content_view_->GetViewAccessibility().SetRole( ax::mojom::Role::kListBoxOption); - content_view_->GetViewAccessibility().OverrideName(GetSuggestionA11yString( - suggestion, - /*add_call_to_action_if_expandable=*/suggestion.is_acceptable)); + content_view_->GetViewAccessibility().SetName( + GetSuggestionA11yString( + suggestion, + /*add_call_to_action_if_expandable=*/suggestion.is_acceptable), + ax::mojom::NameFrom::kAttribute); auto [position, set_size] = ComputePositionInSet(controller_, line_number); content_view_->GetViewAccessibility().SetPosInSet(position); content_view_->GetViewAccessibility().SetSetSize(set_size);
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_bubble_view_unittest.cc b/chrome/browser/ui/views/bookmarks/bookmark_bubble_view_unittest.cc index 0b4c6b84..c23ca9b 100644 --- a/chrome/browser/ui/views/bookmarks/bookmark_bubble_view_unittest.cc +++ b/chrome/browser/ui/views/bookmarks/bookmark_bubble_view_unittest.cc
@@ -14,8 +14,11 @@ #include "chrome/browser/bookmarks/bookmark_model_factory.h" #include "chrome/browser/commerce/shopping_service_factory.h" #include "chrome/browser/feature_engagement/tracker_factory.h" +#include "chrome/browser/signin/chrome_signin_client_factory.h" +#include "chrome/browser/signin/chrome_signin_client_test_util.h" #include "chrome/browser/signin/identity_manager_factory.h" #include "chrome/browser/signin/identity_test_environment_profile_adaptor.h" +#include "chrome/browser/sync/sync_service_factory.h" #include "chrome/browser/ui/browser_element_identifiers.h" #include "chrome/browser/ui/commerce/mock_commerce_ui_tab_helper.h" #include "chrome/browser/ui/signin/bubble_signin_promo_delegate.h" @@ -32,6 +35,7 @@ #include "components/feature_engagement/public/feature_constants.h" #include "components/feature_engagement/test/mock_tracker.h" #include "components/signin/public/identity_manager/identity_test_utils.h" +#include "components/sync/test/test_sync_service.h" #include "ui/base/interaction/element_identifier.h" #include "ui/base/interaction/element_tracker.h" #include "ui/views/bubble/bubble_dialog_delegate_view.h" @@ -100,6 +104,16 @@ {commerce::ShoppingServiceFactory::GetInstance(), base::BindRepeating([](content::BrowserContext* context) { return commerce::MockShoppingService::Build(); + })}, + // Used by IdentityTestEnvironmentProfileAdaptor. + {ChromeSigninClientFactory::GetInstance(), + base::BindRepeating(&BuildChromeSigninClientWithURLLoader, + test_url_loader_factory())}, + // Used by ImageService. + {SyncServiceFactory::GetInstance(), + base::BindRepeating([](content::BrowserContext*) { + return static_cast<std::unique_ptr<KeyedService>>( + std::make_unique<syncer::TestSyncService>()); })}}; IdentityTestEnvironmentProfileAdaptor:: AppendIdentityTestEnvironmentFactories(&factories);
diff --git a/chrome/browser/ui/views/constrained_window_views_browsertest.cc b/chrome/browser/ui/views/constrained_window_views_browsertest.cc index 86ec6c6..a5c8516 100644 --- a/chrome/browser/ui/views/constrained_window_views_browsertest.cc +++ b/chrome/browser/ui/views/constrained_window_views_browsertest.cc
@@ -35,7 +35,8 @@ // Dialogs that take focus must have a name and role to pass accessibility // checks. GetViewAccessibility().SetRole(ax::mojom::Role::kDialog); - GetViewAccessibility().OverrideName("Test dialog"); + GetViewAccessibility().SetName("Test dialog", + ax::mojom::NameFrom::kAttribute); } TestDialog(const TestDialog&) = delete;
diff --git a/chrome/browser/ui/views/desktop_capture/desktop_media_tab_list.cc b/chrome/browser/ui/views/desktop_capture/desktop_media_tab_list.cc index 48ee4181..7d304b66 100644 --- a/chrome/browser/ui/views/desktop_capture/desktop_media_tab_list.cc +++ b/chrome/browser/ui/views/desktop_capture/desktop_media_tab_list.cc
@@ -244,7 +244,8 @@ model_.get(), std::vector<ui::TableColumn>(1), views::TableType::kIconAndText, true); table->set_observer(view_observer_.get()); - table->GetViewAccessibility().OverrideName(accessible_name); + table->GetViewAccessibility().SetName(accessible_name, + ax::mojom::NameFrom::kAttribute); table_ = table.get(); AddChildView(BuildUI(std::move(table)));
diff --git a/chrome/browser/ui/views/device_chooser_content_view.cc b/chrome/browser/ui/views/device_chooser_content_view.cc index 4419375..3975dc89 100644 --- a/chrome/browser/ui/views/device_chooser_content_view.cc +++ b/chrome/browser/ui/views/device_chooser_content_view.cc
@@ -73,8 +73,10 @@ table_view_ = table_view.get(); table_view->SetSelectOnRemove(false); table_view->set_observer(table_view_observer); - table_view->GetViewAccessibility().OverrideName(l10n_util::GetStringUTF16( - IDS_DEVICE_CHOOSER_ACCNAME_COMPATIBLE_DEVICES_LIST)); + table_view->GetViewAccessibility().SetName( + l10n_util::GetStringUTF16( + IDS_DEVICE_CHOOSER_ACCNAME_COMPATIBLE_DEVICES_LIST), + ax::mojom::NameFrom::kAttribute); table_parent_ = AddChildView( views::TableView::CreateScrollViewWithTable(std::move(table_view)));
diff --git a/chrome/browser/ui/views/download/bubble/download_toolbar_button_view.cc b/chrome/browser/ui/views/download/bubble/download_toolbar_button_view.cc index 5f29316..690ee0c 100644 --- a/chrome/browser/ui/views/download/bubble/download_toolbar_button_view.cc +++ b/chrome/browser/ui/views/download/bubble/download_toolbar_button_view.cc
@@ -40,6 +40,7 @@ #include "chrome/browser/ui/views/frame/browser_view.h" #include "chrome/browser/ui/views/toolbar/toolbar_view.h" #include "chrome/grit/generated_resources.h" +#include "components/autofill/content/browser/content_autofill_client.h" #include "components/feature_engagement/public/feature_constants.h" #include "components/safe_browsing/core/common/features.h" #include "components/safe_browsing/core/common/safe_browsing_policy_handler.h" @@ -693,6 +694,7 @@ button_click_time_ = base::TimeTicks(); } + CloseAutofillPopup(); if (ShouldShowBubbleAsInactive()) { bubble_delegate_->GetWidget()->ShowInactive(); bubble_closer_ = std::make_unique<BubbleCloser>(this); @@ -873,6 +875,19 @@ return is_primary_partial_view_; } +void DownloadToolbarButtonView::CloseAutofillPopup() { + content::WebContents* web_contents = + browser_->tab_strip_model()->GetActiveWebContents(); + if (!web_contents) { + return; + } + if (auto* autofill_client = + autofill::ContentAutofillClient::FromWebContents(web_contents)) { + autofill_client->HideAutofillPopup( + autofill::PopupHidingReason::kOverlappingWithAnotherPrompt); + } +} + SkColor DownloadToolbarButtonView::GetIconColor() const { if (is_dormant_) { return GetColorProvider()->GetColor(kColorDownloadToolbarButtonInactive);
diff --git a/chrome/browser/ui/views/download/bubble/download_toolbar_button_view.h b/chrome/browser/ui/views/download/bubble/download_toolbar_button_view.h index 03a51218..89afe66 100644 --- a/chrome/browser/ui/views/download/bubble/download_toolbar_button_view.h +++ b/chrome/browser/ui/views/download/bubble/download_toolbar_button_view.h
@@ -230,6 +230,8 @@ bool ShouldShowBubbleAsInactive() const; + void CloseAutofillPopup(); + // Whether to show the progress ring as a continuously spinning ring, during // deep scanning or if the progress is indeterminate. bool ShouldShowScanningAnimation() const;
diff --git a/chrome/browser/ui/views/download/download_item_view.cc b/chrome/browser/ui/views/download/download_item_view.cc index 3328c5c..3900acc 100644 --- a/chrome/browser/ui/views/download/download_item_view.cc +++ b/chrome/browser/ui/views/download/download_item_view.cc
@@ -929,7 +929,7 @@ views::ViewAccessibility& ax = accessible_alert_->GetViewAccessibility(); ax.SetRole(ax::mojom::Role::kAlert); if (!accessible_alert_text.empty()) - ax.OverrideName(accessible_alert_text); + ax.SetName(accessible_alert_text, ax::mojom::NameFrom::kAttribute); if (announce_accessible_alert_soon_ || !accessible_alert_timer_.IsRunning()) { AnnounceAccessibleAlert(); accessible_alert_timer_.Reset();
diff --git a/chrome/browser/ui/views/download/download_shelf_view.cc b/chrome/browser/ui/views/download/download_shelf_view.cc index e90cef0..7db2277 100644 --- a/chrome/browser/ui/views/download/download_shelf_view.cc +++ b/chrome/browser/ui/views/download/download_shelf_view.cc
@@ -94,8 +94,8 @@ } views::ViewAccessibility& accessibility = GetViewAccessibility(); - accessibility.OverrideName( - l10n_util::GetStringUTF16(IDS_ACCNAME_DOWNLOADS_BAR)); + accessibility.SetName(l10n_util::GetStringUTF16(IDS_ACCNAME_DOWNLOADS_BAR), + ax::mojom::NameFrom::kAttribute); accessibility.SetRole(ax::mojom::Role::kGroup); // Delay 5 seconds if the mouse leaves the shelf by way of entering another
diff --git a/chrome/browser/ui/views/extensions/extensions_menu_view.cc b/chrome/browser/ui/views/extensions/extensions_menu_view.cc index 0a2fcf2..17b8fca 100644 --- a/chrome/browser/ui/views/extensions/extensions_menu_view.cc +++ b/chrome/browser/ui/views/extensions/extensions_menu_view.cc
@@ -110,7 +110,7 @@ GetAccessibleWindowTitle().empty() ? ax::mojom::NameFrom::kAttributeExplicitlyEmpty : ax::mojom::NameFrom::kAttribute; - GetViewAccessibility().OverrideName(GetAccessibleWindowTitle(), name_from); + GetViewAccessibility().SetName(GetAccessibleWindowTitle(), name_from); SetEnableArrowKeyTraversal(true);
diff --git a/chrome/browser/ui/views/extensions/extensions_toolbar_container.cc b/chrome/browser/ui/views/extensions/extensions_toolbar_container.cc index cca5874..d5af9dc 100644 --- a/chrome/browser/ui/views/extensions/extensions_toolbar_container.cc +++ b/chrome/browser/ui/views/extensions/extensions_toolbar_container.cc
@@ -64,22 +64,6 @@ return *callback; } -// Check if there's any security UI that might be spoofable because of -// overlapping with the extension popup. The media picker dialog has been -// identified to be susceptible. See crbug.com/1300006. -bool HasPossiblyOverlappingSecurityUI(Browser* browser) { - views::ElementTrackerViews::ViewList media_picker_dialogs = - views::ElementTrackerViews::GetInstance()->GetAllMatchingViews( - DesktopMediaPickerDialogView::kDesktopMediaPickerDialogViewIdentifier, - browser->window()->GetElementContext()); - - return std::any_of(media_picker_dialogs.begin(), media_picker_dialogs.end(), - [](views::View* dialog_view) { - views::Widget* dialog_widget = dialog_view->GetWidget(); - return dialog_widget && dialog_widget->IsVisible(); - }); -} - } // namespace void ExtensionsToolbarContainer::SetOnVisibleCallbackForTesting( @@ -643,10 +627,6 @@ if (popped_out_action_ || !browser_->window()->IsActive()) return false; - // Don't draw over security UIs. - if (HasPossiblyOverlappingSecurityUI(browser_)) - return false; - ToolbarActionViewController* action = GetActionForId(action_id); DCHECK(action); action->TriggerPopupForAPI(std::move(callback)); @@ -676,6 +656,28 @@ return !actions_.empty(); } +bool ExtensionsToolbarContainer::HasBlockingSecurityUI() const { + // Check if there's any security UI that might be spoofable because of + // overlapping with the extension popup. The media picker dialog has been + // identified to be susceptible. See crbug.com/40058873. + // Not all security UIs are blocking. Non-blocking security UIs can avoid + // being occluded by setting a higher z-order (sub)level. In contrast, + // blocking security UIs cannot leverage z-ordering because they share the + // same rendering layer with the browser window. + // TODO(crbug.com/326681253): block on other possibly overlapping security + // UIs. + views::ElementTrackerViews::ViewList media_picker_dialogs = + views::ElementTrackerViews::GetInstance()->GetAllMatchingViews( + DesktopMediaPickerDialogView::kDesktopMediaPickerDialogViewIdentifier, + browser_->window()->GetElementContext()); + + return std::any_of(media_picker_dialogs.begin(), media_picker_dialogs.end(), + [](views::View* dialog_view) { + views::Widget* dialog_widget = dialog_view->GetWidget(); + return dialog_widget && dialog_widget->IsVisible(); + }); +} + void ExtensionsToolbarContainer::ReorderViews() { const auto& pinned_action_ids = model_->pinned_action_ids(); for (size_t i = 0; i < pinned_action_ids.size(); ++i)
diff --git a/chrome/browser/ui/views/extensions/extensions_toolbar_container.h b/chrome/browser/ui/views/extensions/extensions_toolbar_container.h index 087febe7..42dd240 100644 --- a/chrome/browser/ui/views/extensions/extensions_toolbar_container.h +++ b/chrome/browser/ui/views/extensions/extensions_toolbar_container.h
@@ -203,6 +203,7 @@ std::unique_ptr<ToolbarActionsBarBubbleDelegate> bubble) override; void ToggleExtensionsMenu() override; bool HasAnyExtensions() const override; + bool HasBlockingSecurityUI() const override; void UpdateToolbarActionHoverCard( ToolbarActionView* action_view, ToolbarActionHoverCardUpdateType update_type) override;
diff --git a/chrome/browser/ui/views/frame/immersive_mode_controller_mac.mm b/chrome/browser/ui/views/frame/immersive_mode_controller_mac.mm index e0a0ff2..6a59095 100644 --- a/chrome/browser/ui/views/frame/immersive_mode_controller_mac.mm +++ b/chrome/browser/ui/views/frame/immersive_mode_controller_mac.mm
@@ -96,9 +96,8 @@ browser_view_->frame()->GetFrameView()) ->GetTopInset(false); - browser_view_->tab_overlay_widget()->SetBounds( - gfx::Rect(0, 0, browser_view_->top_container()->size().width(), - tab_widget_height_)); + browser_view_->tab_overlay_widget()->SetSize(gfx::Size( + browser_view_->top_container()->size().width(), tab_widget_height_)); browser_view_->tab_overlay_widget()->Show(); // Move the tab strip to the `tab_overlay_widget`, the host of the @@ -345,7 +344,7 @@ new_size.width(), browser_view_->tab_strip_region_view()->height())); overlay_height_ += tab_widget_height_; } - browser_view_->overlay_widget()->SetBounds(bounds); + browser_view_->overlay_widget()->SetSize(bounds.size()); if (auto* window = GetNSWindowMojo()) { window->OnTopContainerViewBoundsChanged(bounds); }
diff --git a/chrome/browser/ui/views/frame/test_with_browser_view.cc b/chrome/browser/ui/views/frame/test_with_browser_view.cc index 8433f81..ffc6fac 100644 --- a/chrome/browser/ui/views/frame/test_with_browser_view.cc +++ b/chrome/browser/ui/views/frame/test_with_browser_view.cc
@@ -23,7 +23,7 @@ #include "chrome/browser/signin/chrome_signin_client_test_util.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" #include "chrome/browser/ui/views/frame/browser_view.h" -#include "chrome/browser/web_data_service_factory.h" +#include "chrome/browser/webdata_services/web_data_service_factory.h" #include "chrome/test/base/browser_with_test_window_test.h" #include "chrome/test/base/testing_browser_process.h" #include "components/omnibox/browser/autocomplete_classifier.h"
diff --git a/chrome/browser/ui/views/location_bar/content_setting_bubble_dialog_browsertest.cc b/chrome/browser/ui/views/location_bar/content_setting_bubble_dialog_browsertest.cc index 98cc0ac..7f3c240 100644 --- a/chrome/browser/ui/views/location_bar/content_setting_bubble_dialog_browsertest.cc +++ b/chrome/browser/ui/views/location_bar/content_setting_bubble_dialog_browsertest.cc
@@ -114,8 +114,7 @@ GetPopupNavigationDelegateFactoryForTesting(), &CreateTestPopupNavigationDelegate) { scoped_feature_list_.InitWithFeatures( - {features::kQuietNotificationPrompts, - permissions::features::kPermissionStorageAccessAPI}, + {features::kQuietNotificationPrompts}, // Cookies icon intentionally does not show when 3PC are blocked. {content_settings::features::kTrackingProtection3pcd}); }
diff --git a/chrome/browser/ui/views/page_info/page_info_view_factory.cc b/chrome/browser/ui/views/page_info/page_info_view_factory.cc index d2230dc..ce0c0d7 100644 --- a/chrome/browser/ui/views/page_info/page_info_view_factory.cc +++ b/chrome/browser/ui/views/page_info/page_info_view_factory.cc
@@ -15,6 +15,7 @@ #include "build/chromeos_buildflags.h" #include "chrome/app/vector_icons/vector_icons.h" #include "chrome/browser/page_info/page_info_features.h" +#include "chrome/browser/ui/color/chrome_color_id.h" #include "chrome/browser/ui/layout_constants.h" #include "chrome/browser/ui/page_info/chrome_page_info_ui_delegate.h" #include "chrome/browser/ui/view_ids.h" @@ -407,6 +408,10 @@ // If there is no ChromeRefreshIcon currently defined, continue to the rest // of the function. if (icon != nullptr) { + if (info.is_in_use && !show_blocked_badge) { + return ui::ImageModel::FromVectorIcon( + *icon, kColorPageInfoPermissionUsedIcon, GetIconSize()); + } return ui::ImageModel::FromVectorIcon(*icon, ui::kColorIcon, GetIconSize()); }
diff --git a/chrome/browser/ui/views/page_info/permission_toggle_row_view.cc b/chrome/browser/ui/views/page_info/permission_toggle_row_view.cc index d8a0145..63fe4f1c 100644 --- a/chrome/browser/ui/views/page_info/permission_toggle_row_view.cc +++ b/chrome/browser/ui/views/page_info/permission_toggle_row_view.cc
@@ -270,6 +270,7 @@ void PermissionToggleRowView::ResetPermission() { permission_.setting = CONTENT_SETTING_DEFAULT; permission_.is_one_time = false; + permission_.is_in_use = false; PermissionChanged(); }
diff --git a/chrome/browser/ui/views/passwords/password_generation_popup_view_views_browsertest.cc b/chrome/browser/ui/views/passwords/password_generation_popup_view_views_browsertest.cc index 8d3f75b3..a540b59 100644 --- a/chrome/browser/ui/views/passwords/password_generation_popup_view_views_browsertest.cc +++ b/chrome/browser/ui/views/passwords/password_generation_popup_view_views_browsertest.cc
@@ -7,12 +7,15 @@ #include <string> #include "base/memory/weak_ptr.h" +#include "base/strings/strcat.h" +#include "base/test/scoped_feature_list.h" #include "chrome/browser/ui/passwords/password_generation_popup_controller.h" #include "chrome/browser/ui/views/autofill/popup/popup_pixel_test.h" #include "chrome/browser/ui/views/passwords/password_generation_popup_view_views.h" #include "chrome/grit/branded_strings.h" #include "chrome/grit/generated_resources.h" #include "chrome/test/base/in_process_browser_test.h" +#include "components/password_manager/core/browser/features/password_features.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/base/l10n/l10n_util.h" @@ -24,6 +27,7 @@ using ::testing::Combine; using ::testing::Return; using ::testing::ReturnRef; +using ::testing::Values; constexpr char16_t kSampleEmail[] = u"test-account@gmail.com"; @@ -76,7 +80,11 @@ : public autofill::PopupPixelTest<PasswordGenerationPopupViewViews, MockPasswordGenerationPopupController> { public: - PasswordGenerationPopupViewBrowsertest() = default; + PasswordGenerationPopupViewBrowsertest() { + // TODO(crbug.com/326949412): Remove once the experiment concludes. + feature_list_.InitAndDisableFeature( + password_manager::features::kPasswordGenerationExperiment); + } ~PasswordGenerationPopupViewBrowsertest() override = default; void SetUpOnMainThread() override { @@ -90,11 +98,13 @@ ON_CALL(controller(), password).WillByDefault(ReturnRef(password_)); } - void PrepareOfferGenerationState(const std::u16string& suggested_text) { + void PrepareOfferGenerationState() { ON_CALL(controller(), state) .WillByDefault(Return(PasswordGenerationPopupController:: GenerationUIState::kOfferGeneration)); - ON_CALL(controller(), SuggestedText).WillByDefault(Return(suggested_text)); + ON_CALL(controller(), SuggestedText) + .WillByDefault(Return( + l10n_util::GetStringUTF16(IDS_PASSWORD_GENERATION_SUGGESTION_GPM))); } void PrepareEditingSuggestionState() { @@ -132,52 +142,23 @@ private: static constexpr gfx::RectF kElementBounds{100, 100, 250, 50}; const std::u16string password_{u"123!-scfFGamFD"}; + base::test::ScopedFeatureList feature_list_; }; IN_PROC_BROWSER_TEST_P(PasswordGenerationPopupViewBrowsertest, OfferPasswordGeneration) { - PrepareOfferGenerationState( - l10n_util::GetStringUTF16(IDS_PASSWORD_GENERATION_SUGGESTION_GPM)); + PrepareOfferGenerationState(); ShowAndVerifyUi(); } IN_PROC_BROWSER_TEST_P(PasswordGenerationPopupViewBrowsertest, OfferPasswordGenerationHovered) { - PrepareOfferGenerationState( - l10n_util::GetStringUTF16(IDS_PASSWORD_GENERATION_SUGGESTION_GPM)); + PrepareOfferGenerationState(); SetSelected(true); ShowAndVerifyUi(); } IN_PROC_BROWSER_TEST_P(PasswordGenerationPopupViewBrowsertest, - OfferPasswordGenerationWithTrustedAdvice) { - PrepareOfferGenerationState(l10n_util::GetStringUTF16( - IDS_PASSWORD_GENERATION_SUGGESTION_TRUSTED_ADVICE)); - ShowAndVerifyUi(); -} - -IN_PROC_BROWSER_TEST_P(PasswordGenerationPopupViewBrowsertest, - OfferPasswordGenerationWithSafetyFirst) { - PrepareOfferGenerationState(l10n_util::GetStringUTF16( - IDS_PASSWORD_GENERATION_SUGGESTION_SAFETY_FIRST)); - ShowAndVerifyUi(); -} - -IN_PROC_BROWSER_TEST_P(PasswordGenerationPopupViewBrowsertest, - OfferPasswordGenerationWithTrySomethingNew) { - PrepareOfferGenerationState(l10n_util::GetStringUTF16( - IDS_PASSWORD_GENERATION_SUGGESTION_TRY_SOMETHING_NEW)); - ShowAndVerifyUi(); -} - -IN_PROC_BROWSER_TEST_P(PasswordGenerationPopupViewBrowsertest, - OfferPasswordGenerationWithConvenience) { - PrepareOfferGenerationState(l10n_util::GetStringUTF16( - IDS_PASSWORD_GENERATION_SUGGESTION_CONVENIENCE)); - ShowAndVerifyUi(); -} - -IN_PROC_BROWSER_TEST_P(PasswordGenerationPopupViewBrowsertest, EditingSuggestionState) { PrepareEditingSuggestionState(); ShowAndVerifyUi(); @@ -194,3 +175,90 @@ PasswordGenerationPopupViewBrowsertest, Combine(Bool(), Bool()), PasswordGenerationPopupViewBrowsertest::GetTestSuffix); + +using ExperimentParameterType = std::tuple<bool, bool, std::string>; + +// TODO(crbug.com/326949412): Remove once the experiments conclude. +class PasswordGenerationPopupViewWithExperimentsBrowsertest + : public autofill::PopupPixelTest<PasswordGenerationPopupViewViews, + MockPasswordGenerationPopupController, + ExperimentParameterType> { + public: + PasswordGenerationPopupViewWithExperimentsBrowsertest() { + feature_list_.InitWithFeaturesAndParameters( + /*enabled_features=*/{{password_manager::features:: + kPasswordGenerationExperiment, + {{"password_generation_variation", + std::get<2>(GetParam())}}}}, + /*disabled_features=*/{}); + } + ~PasswordGenerationPopupViewWithExperimentsBrowsertest() override = default; + + static std::string GetExperimentTestSuffix( + const testing::TestParamInfo<ExperimentParameterType>& param_info) { + return base::StrCat( + {std::get<0>(param_info.param) ? "Dark" : "Light", + std::get<1>(param_info.param) ? "BrowserRTL" : "BrowserLTR", + std::get<2>(param_info.param)}); + } + + void SetUpOnMainThread() override { + PopupPixelTest::SetUpOnMainThread(); + + ON_CALL(controller(), element_bounds()) + .WillByDefault(ReturnRef(kElementBounds)); + + ON_CALL(controller(), GetPrimaryAccountEmail) + .WillByDefault(Return(kSampleEmail)); + ON_CALL(controller(), password).WillByDefault(ReturnRef(password_)); + } + + void PrepareOfferGenerationState() { + ON_CALL(controller(), state) + .WillByDefault(Return(PasswordGenerationPopupController:: + GenerationUIState::kOfferGeneration)); + ON_CALL(controller(), SuggestedText) + .WillByDefault(Return( + l10n_util::GetStringUTF16(IDS_PASSWORD_GENERATION_SUGGESTION_GPM))); + } + + void ShowUi(const std::string& name) override { + PopupPixelTest::ShowUi(name); + ASSERT_TRUE(view()->Show()); + } + + protected: + // autofill::PopupPixelTest: + PasswordGenerationPopupViewViews* CreateView( + MockPasswordGenerationPopupController& controller) override { + return new PasswordGenerationPopupViewViews( + controller.GetWeakPtr(), views::Widget::GetWidgetForNativeWindow( + browser()->window()->GetNativeWindow())); + } + + private: + static constexpr gfx::RectF kElementBounds{100, 100, 250, 50}; + const std::u16string password_{u"123!-scfFGamFD"}; + base::test::ScopedFeatureList feature_list_; +}; + +IN_PROC_BROWSER_TEST_P(PasswordGenerationPopupViewWithExperimentsBrowsertest, + OfferPasswordGeneration) { + PrepareOfferGenerationState(); + ShowAndVerifyUi(); +} + +INSTANTIATE_TEST_SUITE_P(All, + PasswordGenerationPopupViewWithExperimentsBrowsertest, + Combine(Bool(), + Bool(), + Values("trusted_advice", + "safety_first", + "try_something_new", + "convenience", + "cross_device", + "edit_password", + "chunk_password", + "nudge_password")), + PasswordGenerationPopupViewWithExperimentsBrowsertest:: + GetExperimentTestSuffix);
diff --git a/chrome/browser/ui/views/passwords/password_save_update_view.cc b/chrome/browser/ui/views/passwords/password_save_update_view.cc index f0c53e2..90120f5 100644 --- a/chrome/browser/ui/views/passwords/password_save_update_view.cc +++ b/chrome/browser/ui/views/passwords/password_save_update_view.cc
@@ -454,7 +454,7 @@ views::ViewAccessibility& ax = accessibility_alert_->GetViewAccessibility(); ax.SetRole(ax::mojom::Role::kAlert); - ax.OverrideName(accessibility_alert_text); + ax.SetName(accessibility_alert_text, ax::mojom::NameFrom::kAttribute); accessibility_alert_->NotifyAccessibilityEvent(ax::mojom::Event::kAlert, true); }
diff --git a/chrome/browser/ui/views/permissions/permission_prompt_bubble_base_view_browsertest.cc b/chrome/browser/ui/views/permissions/permission_prompt_bubble_base_view_browsertest.cc index da7381a..9dee962 100644 --- a/chrome/browser/ui/views/permissions/permission_prompt_bubble_base_view_browsertest.cc +++ b/chrome/browser/ui/views/permissions/permission_prompt_bubble_base_view_browsertest.cc
@@ -98,11 +98,6 @@ class PermissionPromptBubbleBaseViewBrowserTest : public DialogBrowserTest { public: - PermissionPromptBubbleBaseViewBrowserTest() { - feature_list_.InitWithFeatures( - {}, {permissions::features::kPermissionStorageAccessAPI}); - } - // DialogBrowserTest: void SetUpOnMainThread() override { host_resolver()->AddRule("*", "127.0.0.1"); @@ -399,35 +394,8 @@ ShowAndVerifyUi(); } -// Test fixture to test the Storage Access prompt with the new Google UI. -// -// We have created a new test fixture for the new Google UI so we can have a -// test for the new and old prompt UI and avoid adding unnecessary Gold images. -// If were to add a new parameter to |PermissionPromptBubbleBaseViewBrowserTest| -// to toggle the PermissionStorageAccessAPI, we would have to add extra Gold -// images for each of the other eleven tests, even though this flag only affects -// the Storage Access prompt. -class StorageAccessEnabledPermissionPromptBubbleViewBrowserTest - : public PermissionPromptBubbleBaseViewBrowserTest { - public: - StorageAccessEnabledPermissionPromptBubbleViewBrowserTest() { - feature_list_.InitWithFeatures( - {permissions::features::kPermissionStorageAccessAPI}, {}); - } - base::test::ScopedFeatureList feature_list_; -}; - -// Host wants to access storage from the site in which it's embedded. Prompt -// with new Google UI. -IN_PROC_BROWSER_TEST_F( - StorageAccessEnabledPermissionPromptBubbleViewBrowserTest, - InvokeUi_storage_access) { - ShowAndVerifyUi(); -} - -IN_PROC_BROWSER_TEST_F( - StorageAccessEnabledPermissionPromptBubbleViewBrowserTest, - OpenHelpCenterLinkInNewTab) { +IN_PROC_BROWSER_TEST_F(PermissionPromptBubbleBaseViewBrowserTest, + OpenHelpCenterLinkInNewTab) { ShowUi("storage_access"); // Get link widget from the prompt.
diff --git a/chrome/browser/ui/views/profiles/incognito_menu_view.cc b/chrome/browser/ui/views/profiles/incognito_menu_view.cc index e2dc1635..791ab794 100644 --- a/chrome/browser/ui/views/profiles/incognito_menu_view.cc +++ b/chrome/browser/ui/views/profiles/incognito_menu_view.cc
@@ -37,7 +37,8 @@ Browser* browser) : ProfileMenuViewBase(anchor_button, browser) { DCHECK(browser->profile()->IsIncognitoProfile()); - GetViewAccessibility().OverrideName(GetAccessibleWindowTitle()); + GetViewAccessibility().SetName(GetAccessibleWindowTitle(), + ax::mojom::NameFrom::kAttribute); base::RecordAction(base::UserMetricsAction("IncognitoMenu_Show")); }
diff --git a/chrome/browser/ui/views/profiles/profile_menu_view_base.cc b/chrome/browser/ui/views/profiles/profile_menu_view_base.cc index c892377..6f6ca760 100644 --- a/chrome/browser/ui/views/profiles/profile_menu_view_base.cc +++ b/chrome/browser/ui/views/profiles/profile_menu_view_base.cc
@@ -708,7 +708,8 @@ // crbug.com/1161166: Orca does not read the accessible window title of the // bubble, so we duplicate it in the top-level menu item. To be revisited // after considering other options, including fixes on the AT side. - GetViewAccessibility().OverrideName(GetAccessibleWindowTitle()); + GetViewAccessibility().SetName(GetAccessibleWindowTitle(), + ax::mojom::NameFrom::kAttribute); #endif std::unique_ptr<views::Label> heading_label; @@ -835,7 +836,8 @@ // accessibility tools can read it together with the button text. The role // change is required by Windows ATs. sync_info_container_->GetViewAccessibility().SetRole(ax::mojom::Role::kGroup); - sync_info_container_->GetViewAccessibility().OverrideName(description); + sync_info_container_->GetViewAccessibility().SetName( + description, ax::mojom::NameFrom::kAttribute); // Add the prominent button at the bottom. auto* button = @@ -970,8 +972,8 @@ // ATs. selectable_profiles_container_->GetViewAccessibility().SetRole( ax::mojom::Role::kGroup); - selectable_profiles_container_->GetViewAccessibility().OverrideName( - profile_mgmt_heading_); + selectable_profiles_container_->GetViewAccessibility().SetName( + profile_mgmt_heading_, ax::mojom::NameFrom::kAttribute); } DCHECK(!image_model.IsEmpty());
diff --git a/chrome/browser/ui/views/sharing_hub/sharing_hub_bubble_action_button.cc b/chrome/browser/ui/views/sharing_hub/sharing_hub_bubble_action_button.cc index a9c9a50b..13bacad5 100644 --- a/chrome/browser/ui/views/sharing_hub/sharing_hub_bubble_action_button.cc +++ b/chrome/browser/ui/views/sharing_hub/sharing_hub_bubble_action_button.cc
@@ -80,7 +80,8 @@ action_info.title, views::style::CONTEXT_MENU)); title_->SetCanProcessEventsWithinSubtree(false); - GetViewAccessibility().OverrideName(title_->GetText()); + GetViewAccessibility().SetName(title_->GetText(), + ax::mojom::NameFrom::kAttribute); } SharingHubBubbleActionButton::~SharingHubBubbleActionButton() = default;
diff --git a/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc b/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc index f32dcbce..39567f5 100644 --- a/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc +++ b/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc
@@ -1890,7 +1890,8 @@ // Dialogs that take focus must have a name and role to pass accessibility // checks. GetViewAccessibility().SetRole(ax::mojom::Role::kDialog); - GetViewAccessibility().OverrideName("Test dialog"); + GetViewAccessibility().SetName("Test dialog", + ax::mojom::NameFrom::kAttribute); } TestDialog(const TestDialog&) = delete;
diff --git a/chrome/browser/ui/views/tabs/tab_hover_card_controller_interactive_uitest.cc b/chrome/browser/ui/views/tabs/tab_hover_card_controller_interactive_uitest.cc index 4af6f06..e799bb40 100644 --- a/chrome/browser/ui/views/tabs/tab_hover_card_controller_interactive_uitest.cc +++ b/chrome/browser/ui/views/tabs/tab_hover_card_controller_interactive_uitest.cc
@@ -751,8 +751,14 @@ EXPECT_FALSE(footer_view->GetVisible()); } +#if BUILDFLAG(IS_WIN) +// https://crbug.com/327245883 +#define MAYBE_BackgroundTabHoverCardContentsHaveCorrectDimensions DISABLED_BackgroundTabHoverCardContentsHaveCorrectDimensions +#else +#define MAYBE_BackgroundTabHoverCardContentsHaveCorrectDimensions BackgroundTabHoverCardContentsHaveCorrectDimensions +#endif IN_PROC_BROWSER_TEST_P(TabHoverCardFadeFooterInteractiveUiTest, - BackgroundTabHoverCardContentsHaveCorrectDimensions) { + MAYBE_BackgroundTabHoverCardContentsHaveCorrectDimensions) { TabStrip* const tab_strip = GetTabStrip(browser()); ASSERT_TRUE( AddTabAtIndex(1, GURL(url::kAboutBlankURL), ui::PAGE_TRANSITION_TYPED));
diff --git a/chrome/browser/ui/views/toolbar/chrome_labs_item_view.cc b/chrome/browser/ui/views/toolbar/chrome_labs_item_view.cc index 17d814f..dc1812a4 100644 --- a/chrome/browser/ui/views/toolbar/chrome_labs_item_view.cc +++ b/chrome/browser/ui/views/toolbar/chrome_labs_item_view.cc
@@ -159,7 +159,8 @@ experiment_description->GetViewAccessibility().OverrideIsIgnored(true); GetViewAccessibility().SetRole(ax::mojom::Role::kGroup); if (!lab.visible_name.empty()) - GetViewAccessibility().OverrideName(lab.visible_name); + GetViewAccessibility().SetName(lab.visible_name, + ax::mojom::NameFrom::kAttribute); // There is currently a MacOS VoiceOver screen reader bug where VoiceOver // does not announce the accessible description for groups
diff --git a/chrome/browser/ui/web_applications/sub_apps_service_impl.cc b/chrome/browser/ui/web_applications/sub_apps_service_impl.cc index 58d3bf4a..b16ca01 100644 --- a/chrome/browser/ui/web_applications/sub_apps_service_impl.cc +++ b/chrome/browser/ui/web_applications/sub_apps_service_impl.cc
@@ -26,6 +26,7 @@ #include "chrome/browser/web_applications/web_app_command_scheduler.h" #include "chrome/browser/web_applications/web_app_helpers.h" #include "chrome/browser/web_applications/web_app_install_finalizer.h" +#include "chrome/browser/web_applications/web_app_install_params.h" #include "chrome/browser/web_applications/web_app_provider.h" #include "chrome/browser/web_applications/web_app_registrar.h" #include "chrome/browser/web_applications/web_app_tab_helper.h" @@ -433,7 +434,7 @@ base::ConcurrentCallbacks<SubAppInstallResult> concurrent; for (auto& install_info : add_call_info.install_infos) { webapps::ManifestId manifest_id = install_info->manifest_id; - provider->scheduler().InstallFromInfo( + provider->scheduler().InstallFromInfoWithParams( std::move(install_info), /*overwrite_existing_manifest_fields=*/false, webapps::WebappInstallSource::SUB_APP, base::BindOnce( @@ -442,7 +443,8 @@ return SubAppInstallResult(manifest_id, app_id, result_code); }, manifest_id) - .Then(concurrent.CreateCallback())); + .Then(concurrent.CreateCallback()), + WebAppInstallParams()); } std::move(concurrent) .Done(base::BindOnce(&SubAppsServiceImpl::FinishAddCall,
diff --git a/chrome/browser/ui/webid/identity_dialog_controller.cc b/chrome/browser/ui/webid/identity_dialog_controller.cc index 78f13db0..2e49d0a 100644 --- a/chrome/browser/ui/webid/identity_dialog_controller.cc +++ b/chrome/browser/ui/webid/identity_dialog_controller.cc
@@ -103,11 +103,6 @@ std::move(on_accounts_displayed_).Run(); } -void IdentityDialogController::ShowIdpSigninFailureDialog( - base::OnceClosure user_notified_callback) { - NOTIMPLEMENTED(); -} - std::string IdentityDialogController::GetTitle() const { return account_view_->GetTitle(); }
diff --git a/chrome/browser/ui/webid/identity_dialog_controller.h b/chrome/browser/ui/webid/identity_dialog_controller.h index 028d844f..9670dbe2 100644 --- a/chrome/browser/ui/webid/identity_dialog_controller.h +++ b/chrome/browser/ui/webid/identity_dialog_controller.h
@@ -67,7 +67,6 @@ const std::optional<TokenError>& error, DismissCallback dismiss_callback, MoreDetailsCallback more_details_callback) override; - void ShowIdpSigninFailureDialog(base::OnceClosure dismiss_callback) override; std::string GetTitle() const override; std::optional<std::string> GetSubtitle() const override;
diff --git a/chrome/browser/ui/webui/ash/settings/pages/device/device_section.cc b/chrome/browser/ui/webui/ash/settings/pages/device/device_section.cc index ffd8417..17f9beb 100644 --- a/chrome/browser/ui/webui/ash/settings/pages/device/device_section.cc +++ b/chrome/browser/ui/webui/ash/settings/pages/device/device_section.cc
@@ -704,6 +704,10 @@ ::switches::kEnableUnifiedDesktop); } +bool IsDisplayPerformanceSupported() { + return ash::features::IsDisplayPerformanceModeEnabled(); +} + bool DoesDeviceSupportAmbientColor() { return ash::features::IsAllowAmbientEQEnabled(); } @@ -1867,6 +1871,8 @@ {"displayScreenExtended", IDS_SETTINGS_DISPLAY_SCREEN_EXTENDED}, {"displayScreenPrimary", IDS_SETTINGS_DISPLAY_SCREEN_PRIMARY}, {"displayScreenTitle", IDS_SETTINGS_DISPLAY_SCREEN}, + {"displayShinyPerformanceLabel", + IDS_SETTINGS_DISPLAY_SHINY_PERFORMANCE_LABEL}, {"displaySizeSliderMaxLabel", IDS_SETTINGS_DISPLAY_ZOOM_SLIDER_MAXIMUM}, {"displaySizeSliderMinLabel", IDS_SETTINGS_DISPLAY_ZOOM_SLIDER_MINIMUM}, {"displayTitle", kIsRevampEnabled ? IDS_OS_SETTINGS_REVAMP_DISPLAY_TITLE @@ -1927,6 +1933,9 @@ html_source->AddBoolean( "allowDisplayAlignmentApi", base::FeatureList::IsEnabled(ash::features::kDisplayAlignAssist)); + + html_source->AddBoolean("isDisplayPerformanceSupported", + IsDisplayPerformanceSupported()); } } // namespace ash::settings
diff --git a/chrome/browser/ui/webui/cr_components/history_embeddings/history_embeddings_handler.cc b/chrome/browser/ui/webui/cr_components/history_embeddings/history_embeddings_handler.cc new file mode 100644 index 0000000..806639d --- /dev/null +++ b/chrome/browser/ui/webui/cr_components/history_embeddings/history_embeddings_handler.cc
@@ -0,0 +1,16 @@ +// Copyright 2024 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/ui/webui/cr_components/history_embeddings/history_embeddings_handler.h" + +HistoryEmbeddingsHandler::HistoryEmbeddingsHandler( + mojo::PendingReceiver<history_embeddings::mojom::PageHandler> + pending_page_handler) + : page_handler_(this, std::move(pending_page_handler)) {} + +HistoryEmbeddingsHandler::~HistoryEmbeddingsHandler() = default; + +void HistoryEmbeddingsHandler::DoSomething(DoSomethingCallback callback) { + std::move(callback).Run(true); +}
diff --git a/chrome/browser/ui/webui/cr_components/history_embeddings/history_embeddings_handler.h b/chrome/browser/ui/webui/cr_components/history_embeddings/history_embeddings_handler.h new file mode 100644 index 0000000..b62f85d --- /dev/null +++ b/chrome/browser/ui/webui/cr_components/history_embeddings/history_embeddings_handler.h
@@ -0,0 +1,32 @@ +// Copyright 2024 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_UI_WEBUI_CR_COMPONENTS_HISTORY_EMBEDDINGS_HISTORY_EMBEDDINGS_HANDLER_H_ +#define CHROME_BROWSER_UI_WEBUI_CR_COMPONENTS_HISTORY_EMBEDDINGS_HISTORY_EMBEDDINGS_HANDLER_H_ + +#include "mojo/public/cpp/bindings/pending_receiver.h" +#include "mojo/public/cpp/bindings/pending_remote.h" +#include "mojo/public/cpp/bindings/receiver.h" +#include "mojo/public/cpp/bindings/remote.h" +#include "ui/webui/mojo_bubble_web_ui_controller.h" +#include "ui/webui/resources/cr_components/history_embeddings/history_embeddings.mojom.h" + +class HistoryEmbeddingsHandler : public history_embeddings::mojom::PageHandler { + public: + explicit HistoryEmbeddingsHandler( + mojo::PendingReceiver<history_embeddings::mojom::PageHandler> + pending_page_handler); + HistoryEmbeddingsHandler(const HistoryEmbeddingsHandler&) = delete; + HistoryEmbeddingsHandler& operator=(const HistoryEmbeddingsHandler&) = delete; + ~HistoryEmbeddingsHandler() override; + + // history_embeddings::mojom::PageHandler: + void DoSomething(DoSomethingCallback callback) override; + + private: + mojo::Receiver<history_embeddings::mojom::PageHandler> page_handler_; + base::WeakPtrFactory<HistoryEmbeddingsHandler> weak_ptr_factory_{this}; +}; + +#endif // CHROME_BROWSER_UI_WEBUI_CR_COMPONENTS_HISTORY_EMBEDDINGS_HISTORY_EMBEDDINGS_HANDLER_H_
diff --git a/chrome/browser/ui/webui/history/history_ui.cc b/chrome/browser/ui/webui/history/history_ui.cc index 4818fd8..91508b3 100644 --- a/chrome/browser/ui/webui/history/history_ui.cc +++ b/chrome/browser/ui/webui/history/history_ui.cc
@@ -23,6 +23,7 @@ #include "chrome/browser/signin/signin_ui_util.h" #include "chrome/browser/ui/ui_features.h" #include "chrome/browser/ui/webui/cr_components/history_clusters/history_clusters_util.h" +#include "chrome/browser/ui/webui/cr_components/history_embeddings/history_embeddings_handler.h" #include "chrome/browser/ui/webui/favicon_source.h" #include "chrome/browser/ui/webui/history/browsing_history_handler.h" #include "chrome/browser/ui/webui/history/foreign_session_handler.h" @@ -242,6 +243,13 @@ } void HistoryUI::BindInterface( + mojo::PendingReceiver<history_embeddings::mojom::PageHandler> + pending_page_handler) { + history_embeddings_handler_ = std::make_unique<HistoryEmbeddingsHandler>( + std::move(pending_page_handler)); +} + +void HistoryUI::BindInterface( mojo::PendingReceiver<history_clusters::mojom::PageHandler> pending_page_handler) { history_clusters_handler_ =
diff --git a/chrome/browser/ui/webui/history/history_ui.h b/chrome/browser/ui/webui/history/history_ui.h index c6e9530..43d178b1 100644 --- a/chrome/browser/ui/webui/history/history_ui.h +++ b/chrome/browser/ui/webui/history/history_ui.h
@@ -15,6 +15,7 @@ #include "ui/base/resource/resource_scale_factor.h" #include "ui/webui/mojo_web_ui_controller.h" #include "ui/webui/resources/cr_components/history_clusters/history_clusters.mojom-forward.h" +#include "ui/webui/resources/cr_components/history_embeddings/history_embeddings.mojom.h" namespace base { class RefCountedMemory; @@ -24,6 +25,8 @@ class HistoryClustersHandler; } +class HistoryEmbeddingsHandler; + namespace page_image_service { class ImageServiceHandler; } @@ -50,6 +53,9 @@ ui::ResourceScaleFactor scale_factor); // Instantiates the implementors of mojom interfaces. + void BindInterface( + mojo::PendingReceiver<history_embeddings::mojom::PageHandler> + pending_page_handler); void BindInterface(mojo::PendingReceiver<history_clusters::mojom::PageHandler> pending_page_handler); void BindInterface( @@ -63,6 +69,7 @@ } private: + std::unique_ptr<HistoryEmbeddingsHandler> history_embeddings_handler_; std::unique_ptr<history_clusters::HistoryClustersHandler> history_clusters_handler_; std::unique_ptr<page_image_service::ImageServiceHandler>
diff --git a/chrome/browser/ui/webui/settings/settings_ui.cc b/chrome/browser/ui/webui/settings/settings_ui.cc index 17c1647..03c34e2 100644 --- a/chrome/browser/ui/webui/settings/settings_ui.cc +++ b/chrome/browser/ui/webui/settings/settings_ui.cc
@@ -551,11 +551,6 @@ performance_manager::user_tuning::IsBatterySaverModeManagedByOS()); html_source->AddBoolean( - "enablePermissionStorageAccessApi", - base::FeatureList::IsEnabled( - permissions::features::kPermissionStorageAccessAPI)); - - html_source->AddBoolean( "autoPictureInPictureEnabled", base::FeatureList::IsEnabled( blink::features::kMediaSessionEnterPictureInPicture));
diff --git a/chrome/browser/ui/webui/signin/ash/inline_login_dialog_browsertest.cc b/chrome/browser/ui/webui/signin/ash/inline_login_dialog_browsertest.cc index 8e98fbb..a60c2a2 100644 --- a/chrome/browser/ui/webui/signin/ash/inline_login_dialog_browsertest.cc +++ b/chrome/browser/ui/webui/signin/ash/inline_login_dialog_browsertest.cc
@@ -39,7 +39,8 @@ // Dialogs that take focus must have a name and role to pass accessibility // checks. GetViewAccessibility().SetRole(ax::mojom::Role::kDialog); - GetViewAccessibility().OverrideName("Test dialog"); + GetViewAccessibility().SetName("Test dialog", + ax::mojom::NameFrom::kAttribute); } ChildModalDialogDelegate(const ChildModalDialogDelegate&) = delete; ChildModalDialogDelegate& operator=(const ChildModalDialogDelegate&) = delete;
diff --git a/chrome/browser/web_applications/commands/install_from_info_command_browsertest.cc b/chrome/browser/web_applications/commands/install_from_info_command_browsertest.cc index 178f419..7524379 100644 --- a/chrome/browser/web_applications/commands/install_from_info_command_browsertest.cc +++ b/chrome/browser/web_applications/commands/install_from_info_command_browsertest.cc
@@ -74,7 +74,7 @@ base::RunLoop loop; webapps::AppId result_app_id; - provider().scheduler().InstallFromInfo( + provider().scheduler().InstallFromInfoNoIntegrationForTesting( std::move(info), /*overwrite_existing_manifest_fields=*/false, install_source, base::BindLambdaForTesting(
diff --git a/chrome/browser/web_applications/commands/os_integration_synchronize_command_unittest.cc b/chrome/browser/web_applications/commands/os_integration_synchronize_command_unittest.cc index 024f91b..bd118aa 100644 --- a/chrome/browser/web_applications/commands/os_integration_synchronize_command_unittest.cc +++ b/chrome/browser/web_applications/commands/os_integration_synchronize_command_unittest.cc
@@ -87,7 +87,7 @@ webapps::WebappInstallSource::OMNIBOX_INSTALL_ICON) { base::test::TestFuture<const webapps::AppId&, webapps::InstallResultCode> result; - provider()->scheduler().InstallFromInfo( + provider()->scheduler().InstallFromInfoNoIntegrationForTesting( std::move(install_info), /*overwrite_existing_manifest_fields=*/true, source, result.GetCallback()); bool success = result.Wait();
diff --git a/chrome/browser/web_applications/os_integration/shortcut_sub_manager_unittest.cc b/chrome/browser/web_applications/os_integration/shortcut_sub_manager_unittest.cc index 4b2ef615..dbd6a0d 100644 --- a/chrome/browser/web_applications/os_integration/shortcut_sub_manager_unittest.cc +++ b/chrome/browser/web_applications/os_integration/shortcut_sub_manager_unittest.cc
@@ -292,8 +292,8 @@ info->icon_bitmaps.any = std::move(icon_map); base::test::TestFuture<const webapps::AppId&, webapps::InstallResultCode> result; - // InstallFromInfo() does not trigger OS integration. - provider().scheduler().InstallFromInfo( + // InstallFromInfoNoIntegrationForTesting() does not trigger OS integration. + provider().scheduler().InstallFromInfoNoIntegrationForTesting( std::move(info), /*overwrite_existing_manifest_fields=*/true, webapps::WebappInstallSource::OMNIBOX_INSTALL_ICON, result.GetCallback());
diff --git a/chrome/browser/web_applications/policy/web_app_policy_manager_browsertest.cc b/chrome/browser/web_applications/policy/web_app_policy_manager_browsertest.cc index f87b5942..7f4a4fd 100644 --- a/chrome/browser/web_applications/policy/web_app_policy_manager_browsertest.cc +++ b/chrome/browser/web_applications/policy/web_app_policy_manager_browsertest.cc
@@ -264,7 +264,7 @@ install_info.get()); auto* provider = WebAppProvider::GetForTest(profile()); - provider->scheduler().InstallFromInfo( + provider->scheduler().InstallFromInfoNoIntegrationForTesting( std::move(install_info), /*overwrite_existing_manifest_fields=*/true, webapps::WebappInstallSource::EXTERNAL_POLICY, base::DoNothing());
diff --git a/chrome/browser/web_applications/test/web_app_install_test_utils.cc b/chrome/browser/web_applications/test/web_app_install_test_utils.cc index 629788e..dfa86dc 100644 --- a/chrome/browser/web_applications/test/web_app_install_test_utils.cc +++ b/chrome/browser/web_applications/test/web_app_install_test_utils.cc
@@ -114,9 +114,9 @@ // In unit tests, we do not have Browser or WebContents instances. Hence we // use `InstallFromInfoCommand` instead of `FetchManifestAndInstallCommand` or // `WebAppInstallCommand` to install the web app. - provider->scheduler().InstallFromInfo(std::move(web_app_info), - overwrite_existing_manifest_fields, - install_source, future.GetCallback()); + provider->scheduler().InstallFromInfoNoIntegrationForTesting( + std::move(web_app_info), overwrite_existing_manifest_fields, + install_source, future.GetCallback()); EXPECT_EQ(webapps::InstallResultCode::kSuccessNewInstall, future.Get<webapps::InstallResultCode>()); @@ -155,7 +155,7 @@ // In unit tests, we do not have Browser or WebContents instances. Hence we // use `InstallFromInfoCommand` instead of `FetchManifestAndInstallCommand` or // `WebAppInstallCommand` to install the web app. - provider->scheduler().InstallFromInfo( + provider->scheduler().InstallFromInfoNoIntegrationForTesting( std::move(web_app_info), /*overwrite_existing_manifest_fields =*/true, is_policy_install ? webapps::WebappInstallSource::EXTERNAL_POLICY : webapps::WebappInstallSource::MENU_CREATE_SHORTCUT,
diff --git a/chrome/browser/web_applications/web_app_command_scheduler.cc b/chrome/browser/web_applications/web_app_command_scheduler.cc index 5bd0feb9..6f6b5d4 100644 --- a/chrome/browser/web_applications/web_app_command_scheduler.cc +++ b/chrome/browser/web_applications/web_app_command_scheduler.cc
@@ -7,6 +7,7 @@ #include <memory> #include <optional> +#include "base/check_is_test.h" #include "base/command_line.h" #include "base/feature_list.h" #include "base/files/file_path.h" @@ -122,12 +123,13 @@ std::move(parent_manifest_id), std::move(callback))); } -void WebAppCommandScheduler::InstallFromInfo( +void WebAppCommandScheduler::InstallFromInfoNoIntegrationForTesting( std::unique_ptr<WebAppInstallInfo> install_info, bool overwrite_existing_manifest_fields, webapps::WebappInstallSource install_surface, OnceInstallCallback install_callback, const base::Location& location) { + CHECK_IS_TEST(); provider_->command_manager().ScheduleCommand( std::make_unique<InstallFromInfoCommand>( &profile_.get(), std::move(install_info),
diff --git a/chrome/browser/web_applications/web_app_command_scheduler.h b/chrome/browser/web_applications/web_app_command_scheduler.h index 2765773f..8aab44d 100644 --- a/chrome/browser/web_applications/web_app_command_scheduler.h +++ b/chrome/browser/web_applications/web_app_command_scheduler.h
@@ -114,11 +114,12 @@ // manifest. // `InstallFromInfo` doesn't install OS hooks. `InstallFromInfoWithParams` // install OS hooks when they are set in `install_params`. - void InstallFromInfo(std::unique_ptr<WebAppInstallInfo> install_info, - bool overwrite_existing_manifest_fields, - webapps::WebappInstallSource install_surface, - OnceInstallCallback install_callback, - const base::Location& location = FROM_HERE); + void InstallFromInfoNoIntegrationForTesting( + std::unique_ptr<WebAppInstallInfo> install_info, + bool overwrite_existing_manifest_fields, + webapps::WebappInstallSource install_surface, + OnceInstallCallback install_callback, + const base::Location& location = FROM_HERE); void InstallFromInfoWithParams( std::unique_ptr<WebAppInstallInfo> install_info,
diff --git a/chrome/browser/web_applications/web_app_icon_manager_browsertest.cc b/chrome/browser/web_applications/web_app_icon_manager_browsertest.cc index 3f2af7b0..d03191f 100644 --- a/chrome/browser/web_applications/web_app_icon_manager_browsertest.cc +++ b/chrome/browser/web_applications/web_app_icon_manager_browsertest.cc
@@ -91,7 +91,7 @@ base::RunLoop run_loop; auto* provider = WebAppProvider::GetForTest(browser()->profile()); - provider->scheduler().InstallFromInfo( + provider->scheduler().InstallFromInfoNoIntegrationForTesting( std::move(install_info), /*overwrite_existing_manifest_fields=*/false, webapps::WebappInstallSource::OMNIBOX_INSTALL_ICON,
diff --git a/chrome/browser/webauthn/enclave_manager.cc b/chrome/browser/webauthn/enclave_manager.cc index 49167a1a..1fe29e9 100644 --- a/chrome/browser/webauthn/enclave_manager.cc +++ b/chrome/browser/webauthn/enclave_manager.cc
@@ -766,6 +766,9 @@ void Process(Event event) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + CHECK(!processing_) << ToString(state_); + processing_ = true; + const State initial_state = state_; const std::string event_str = ToString(event); @@ -849,6 +852,7 @@ // The only internal state transition (i.e. where one state moves to another // without waiting for an external event) allowed is to `kNextAction`. if (state_ != State::kNextAction) { + processing_ = false; return; } @@ -859,7 +863,10 @@ if (state_ == State::kStop) { manager_->Stopped(); // `this` has been deleted now. + return; } + + processing_ = false; } static std::string ToString(State state) { @@ -1522,6 +1529,7 @@ const std::unique_ptr<CoreAccountInfo> primary_account_info_; State state_ = State::kInit; + bool processing_ = false; std::unique_ptr<StoreKeysArgs> store_keys_args_; std::string pending_pin_;
diff --git a/chrome/browser/webdata_services/DIR_METADATA b/chrome/browser/webdata_services/DIR_METADATA new file mode 100644 index 0000000..84f5b819 --- /dev/null +++ b/chrome/browser/webdata_services/DIR_METADATA
@@ -0,0 +1,7 @@ +monorail: { + component: "Internals" +} +team_email: "chromium-reviews@chromium.org" +buganizer_public: { + component_id: 1456292 +}
diff --git a/chrome/browser/webdata_services/OWNERS b/chrome/browser/webdata_services/OWNERS new file mode 100644 index 0000000..05fbb74 --- /dev/null +++ b/chrome/browser/webdata_services/OWNERS
@@ -0,0 +1 @@ +file://components/webdata_services/OWNERS
diff --git a/chrome/browser/web_data_service_factory.cc b/chrome/browser/webdata_services/web_data_service_factory.cc similarity index 98% rename from chrome/browser/web_data_service_factory.cc rename to chrome/browser/webdata_services/web_data_service_factory.cc index 49bdba1..71238e9 100644 --- a/chrome/browser/web_data_service_factory.cc +++ b/chrome/browser/webdata_services/web_data_service_factory.cc
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "chrome/browser/web_data_service_factory.h" +#include "chrome/browser/webdata_services/web_data_service_factory.h" #include "base/files/file_path.h" #include "base/functional/bind.h"
diff --git a/chrome/browser/web_data_service_factory.h b/chrome/browser/webdata_services/web_data_service_factory.h similarity index 92% rename from chrome/browser/web_data_service_factory.h rename to chrome/browser/webdata_services/web_data_service_factory.h index faac2d4..5c8a5a2 100644 --- a/chrome/browser/web_data_service_factory.h +++ b/chrome/browser/webdata_services/web_data_service_factory.h
@@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef CHROME_BROWSER_WEB_DATA_SERVICE_FACTORY_H_ -#define CHROME_BROWSER_WEB_DATA_SERVICE_FACTORY_H_ +#ifndef CHROME_BROWSER_WEBDATA_SERVICES_WEB_DATA_SERVICE_FACTORY_H_ +#define CHROME_BROWSER_WEBDATA_SERVICES_WEB_DATA_SERVICE_FACTORY_H_ #include "base/memory/ref_counted.h" #include "build/build_config.h" @@ -78,4 +78,4 @@ bool ServiceIsNULLWhileTesting() const override; }; -#endif // CHROME_BROWSER_WEB_DATA_SERVICE_FACTORY_H_ +#endif // CHROME_BROWSER_WEBDATA_SERVICES_WEB_DATA_SERVICE_FACTORY_H_
diff --git a/chrome/build/android-arm32.pgo.txt b/chrome/build/android-arm32.pgo.txt index 3501b53..189bcfc 100644 --- a/chrome/build/android-arm32.pgo.txt +++ b/chrome/build/android-arm32.pgo.txt
@@ -1 +1 @@ -chrome-android32-main-1709034969-8c8f1bb05aea5f39fc381bb65fc70625e79b398a-a44e4a5e326ed73137c943c645157d3d58bca36d.profdata +chrome-android32-main-1709056799-5f5189e825f1404c0057f96d91ccb2dbcd3e5f44-d6b7729d8cbb7ade6b97baf90bac574cf6efc744.profdata
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt index 292bd62..8f901b7 100644 --- a/chrome/build/linux.pgo.txt +++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@ -chrome-linux-main-1709034969-aae66e2a5d3e205794fab7a85c937517e697a18c-a44e4a5e326ed73137c943c645157d3d58bca36d.profdata +chrome-linux-main-1709056799-aae34d88ca80a9a1b0bdd8a5b7dbe051d1403586-d6b7729d8cbb7ade6b97baf90bac574cf6efc744.profdata
diff --git a/chrome/build/mac-arm.pgo.txt b/chrome/build/mac-arm.pgo.txt index 007c7af4..19a39c9 100644 --- a/chrome/build/mac-arm.pgo.txt +++ b/chrome/build/mac-arm.pgo.txt
@@ -1 +1 @@ -chrome-mac-arm-main-1709049366-639836269488162b48e688b482f47efdcf6620e7-3de8aede4f38380836d650db46578baf59e6da3c.profdata +chrome-mac-arm-main-1709056799-7400e0edda683bbe8ce2388f45e2b364a9393d14-d6b7729d8cbb7ade6b97baf90bac574cf6efc744.profdata
diff --git a/chrome/build/win-arm64.pgo.txt b/chrome/build/win-arm64.pgo.txt index 1ce971b..5b9754b 100644 --- a/chrome/build/win-arm64.pgo.txt +++ b/chrome/build/win-arm64.pgo.txt
@@ -1 +1 @@ -chrome-win-arm64-main-1709034969-98a9fe11f3c290c0cc36e776e0c0669373fb99d6-a44e4a5e326ed73137c943c645157d3d58bca36d.profdata +chrome-win-arm64-main-1709056799-d3a45c7322b38a7afb3881ec1db28754ba7deda0-d6b7729d8cbb7ade6b97baf90bac574cf6efc744.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt index b783f0c..266ea142 100644 --- a/chrome/build/win32.pgo.txt +++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@ -chrome-win32-main-1709034969-8c10f18d5fdbfd55d35ed90dd47ec64e762591b6-a44e4a5e326ed73137c943c645157d3d58bca36d.profdata +chrome-win32-main-1709045678-a87f50c015123fa7ef8618cda12b48b4048a0247-36e3f3ba78470906cf717bd5c6104b2054d42886.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt index 0855203b..f6c0a4e 100644 --- a/chrome/build/win64.pgo.txt +++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@ -chrome-win64-main-1709034969-47e6e28aa7831ef373fbdcf429ce034b2c52303d-a44e4a5e326ed73137c943c645157d3d58bca36d.profdata +chrome-win64-main-1709056799-57eff0e35bf841127d162da05dfc07cf73bb0798-d6b7729d8cbb7ade6b97baf90bac574cf6efc744.profdata
diff --git a/chrome/common/controlled_frame/controlled_frame.cc b/chrome/common/controlled_frame/controlled_frame.cc index 6954019..8f4a52f 100644 --- a/chrome/common/controlled_frame/controlled_frame.cc +++ b/chrome/common/controlled_frame/controlled_frame.cc
@@ -9,8 +9,10 @@ #include "base/containers/contains.h" #include "chrome/common/chrome_features.h" #include "chrome/common/initialize_extensions_client.h" +#include "components/version_info/version_info.h" #include "extensions/common/extension.h" #include "extensions/common/features/feature.h" +#include "extensions/common/features/feature_channel.h" #include "extensions/common/mojom/context_type.mojom.h" #if BUILDFLAG(IS_CHROMEOS) @@ -69,7 +71,8 @@ // Also allow API exposure in ChromeOS Kiosk mode for web apps. if (base::FeatureList::IsEnabled(features::kWebKioskEnableIwaApis) && IsRunningInKioskMode() && url.SchemeIs(url::kHttpsScheme)) { - is_allowed_for_scheme = true; + is_allowed_for_scheme = + extensions::GetCurrentChannel() != version_info::Channel::STABLE; } #endif
diff --git a/chrome/common/logging_chrome.h b/chrome/common/logging_chrome.h index 6679f79..1363926b 100644 --- a/chrome/common/logging_chrome.h +++ b/chrome/common/logging_chrome.h
@@ -22,14 +22,8 @@ // setting levels in the future. // // The main process might want to delete any old log files on startup by -// setting delete_old_log_file, but the renderer processes should not, or -// they will delete each others' logs. -// -// XXX -// Setting suppress_error_dialogs to true disables any dialogs that would -// normally appear for assertions and crashes, and makes any catchable -// errors (namely assertions) available via GetSilencedErrorCount() -// and GetSilencedError(). +// setting `delete_old_log_file`, but child processes should not, or they +// will delete each others' logs. void InitChromeLogging(const base::CommandLine& command_line, OldFileDeletionState delete_old_log_file); @@ -37,7 +31,7 @@ const base::CommandLine& command_line); #if BUILDFLAG(IS_CHROMEOS) -// Prepare the log file. If |new_log| is true, rotate the previous log file to +// Prepare the log file. If `new_log` is true, rotate the previous log file to // write new logs to the latest log file. Otherwise, we reuse the existing file // if exists. base::FilePath SetUpLogFile(const base::FilePath& target_path, bool new_log);
diff --git a/chrome/renderer/accessibility/read_anything_app_controller.cc b/chrome/renderer/accessibility/read_anything_app_controller.cc index 69aec830..6dda0d3 100644 --- a/chrome/renderer/accessibility/read_anything_app_controller.cc +++ b/chrome/renderer/accessibility/read_anything_app_controller.cc
@@ -1097,7 +1097,7 @@ web_ui_connected_time_ms_ = base::TimeTicks::Now(); base::UmaHistogramLongTimes( "Accessibility.ReadAnything.TimeFromEntryTriggeredToWebUIConnected", - base::TimeTicks::Now() - web_ui_connected_time_ms_); + base::TimeTicks::Now() - renderer_load_triggered_time_ms_); mojo::PendingReceiver<read_anything::mojom::UntrustedPageHandlerFactory> page_handler_factory_receiver = page_handler_factory_.BindNewPipeAndPassReceiver();
diff --git a/chrome/renderer/autofill/password_autofill_agent_browsertest.cc b/chrome/renderer/autofill/password_autofill_agent_browsertest.cc index 96d51079..7bf7cc06 100644 --- a/chrome/renderer/autofill/password_autofill_agent_browsertest.cc +++ b/chrome/renderer/autofill/password_autofill_agent_browsertest.cc
@@ -2343,6 +2343,80 @@ CheckTextFieldsDOMState(std::string(), false, "TextToFill", true); } +// Tests that `FillInfoField` doesn't fill read-only text fields. +TEST_F(PasswordAutofillAgentTest, FillIntoReadonlyTextField) { + // Neither field should be autocompleted. + CheckTextFieldsDOMState( + /*username=*/std::string(), /*username_autofilled=*/false, + /*password=*/std::string(), /*password_autofilled=*/false); + + // If the field is readonly, it should not be affected. + SetElementReadOnly(username_element_, true); + password_autofill_agent_->FillField( + form_util::GetFieldRendererId(username_element_), kAliceUsername16); + CheckTextFieldsDOMState( + /*username=*/std::string(), /*username_autofilled=*/false, + /*password=*/std::string(), /*password_autofilled=*/false); +} + +// Tests that `FillInfoField` correctly fills the username field. +TEST_F(PasswordAutofillAgentTest, FillIntoUsernameField) { + // Neither field should be autocompleted. + CheckTextFieldsDOMState( + /*username=*/std::string(), /*username_autofilled=*/false, + /*password=*/std::string(), /*password_autofilled=*/false); + + password_autofill_agent_->FillField( + form_util::GetFieldRendererId(username_element_), kAliceUsername16); + CheckTextFieldsDOMState( + /*username=*/kAliceUsername, /*username_autofilled=*/true, + /*password=*/std::string(), /*password_autofilled=*/false); +} + +// Tests that `FillInfoField` correctly fills the password field. +TEST_F(PasswordAutofillAgentTest, FillIntoPasswordField) { + // Neither field should be autocompleted. + CheckTextFieldsDOMState( + /*username=*/std::string(), /*username_autofilled=*/false, + /*password=*/std::string(), /*password_autofilled=*/false); + + password_autofill_agent_->FillField( + form_util::GetFieldRendererId(password_element_), kAlicePassword16); + CheckTextFieldsDOMState( + /*username=*/std::string(), /*username_autofilled=*/false, + /*password=*/kAlicePassword, /*password_autofilled=*/true); +} + +// Tests that `FillInfoField` can fill into a random field. +TEST_F(PasswordAutofillAgentTest, FillIntoRandomField) { + WebInputElement random_element = GetInputElementByID("random_field"); + + // The field should not be autocompleted. + EXPECT_EQ(std::string(), random_element.Value().Utf8()); + + password_autofill_agent_->FillField( + form_util::GetFieldRendererId(random_element), kAliceUsername16); + EXPECT_EQ(kAliceUsername, random_element.Value().Utf8()); +} + +// Tests that `FillInfoField` doesn't fill non-existent fields. +TEST_F(PasswordAutofillAgentTest, FillIntoNonExistingField) { + WebInputElement random_element = GetInputElementByID("random_field"); + + // Neither field should be autocompleted. + CheckTextFieldsDOMState( + /*username=*/std::string(), /*username_autofilled=*/false, + /*password=*/std::string(), /*password_autofilled=*/false); + EXPECT_EQ(std::string(), random_element.Value().Utf8()); + + password_autofill_agent_->FillField(FieldRendererId(), kAliceUsername16); + // Neither field should be autocompleted. + CheckTextFieldsDOMState( + /*username=*/std::string(), /*username_autofilled=*/false, + /*password=*/std::string(), /*password_autofilled=*/false); + EXPECT_EQ(std::string(), random_element.Value().Utf8()); +} + // Tests that `ClearPreview` properly clears previewed username and password // with neither username nor password being previously autofilled. TEST_F(PasswordAutofillAgentTest,
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn index 22042b0..f06e731 100644 --- a/chrome/test/BUILD.gn +++ b/chrome/test/BUILD.gn
@@ -2647,7 +2647,6 @@ "../browser/support_tool/screenshot_data_collector_browsertest.cc", "../browser/support_tool/support_tool_util_browsertest.cc", "../browser/sync/sessions/sync_sessions_router_tab_helper_browsertest.cc", - "../browser/sync/sync_service_util_browsertest.cc", "../browser/sync_file_system/mock_local_change_processor.cc", "../browser/sync_file_system/mock_local_change_processor.h", "../browser/sync_file_system/mock_remote_file_sync_service.cc",
diff --git a/chrome/test/chromeos/standalone_browser_test_controller.cc b/chrome/test/chromeos/standalone_browser_test_controller.cc index c7b49f7..d52dd7e 100644 --- a/chrome/test/chromeos/standalone_browser_test_controller.cc +++ b/chrome/test/chromeos/standalone_browser_test_controller.cc
@@ -165,7 +165,7 @@ info->user_display_mode = WindowModeToUserDisplayMode(window_mode); Profile* profile = ProfileManager::GetPrimaryUserProfile(); auto* provider = web_app::WebAppProvider::GetForWebApps(profile); - provider->scheduler().InstallFromInfo( + provider->scheduler().InstallFromInfoNoIntegrationForTesting( std::move(info), /*overwrite_existing_manifest_fields=*/false, webapps::WebappInstallSource::SYNC, @@ -291,7 +291,7 @@ Profile* profile = ProfileManager::GetPrimaryUserProfile(); auto* provider = web_app::WebAppProvider::GetForWebApps(profile); - provider->scheduler().InstallFromInfo( + provider->scheduler().InstallFromInfoNoIntegrationForTesting( std::move(info), /*overwrite_existing_manifest_fields=*/false, webapps::WebappInstallSource::SUB_APP,
diff --git a/chrome/test/data/webui/chromeos/print_preview_cros/BUILD.gn b/chrome/test/data/webui/chromeos/print_preview_cros/BUILD.gn index 19c5ec7..68e59f6b 100644 --- a/chrome/test/data/webui/chromeos/print_preview_cros/BUILD.gn +++ b/chrome/test/data/webui/chromeos/print_preview_cros/BUILD.gn
@@ -15,6 +15,7 @@ ] files = [ + "fake_print_preview_page_handler_test.ts", "print_preview_cros_app_test.ts", "print_ticket_manager_test.ts", "summary_panel_controller_test.ts",
diff --git a/chrome/test/data/webui/chromeos/print_preview_cros/fake_print_preview_page_handler_test.ts b/chrome/test/data/webui/chromeos/print_preview_cros/fake_print_preview_page_handler_test.ts new file mode 100644 index 0000000..f9eaad48 --- /dev/null +++ b/chrome/test/data/webui/chromeos/print_preview_cros/fake_print_preview_page_handler_test.ts
@@ -0,0 +1,41 @@ +// Copyright 2024 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'chrome://os-print/js/fakes/fake_print_preview_page_handler.js'; + +import {FAKE_PRINT_REQUEST_FAILURE_INVALID_SETTINGS_ERROR, FAKE_PRINT_REQUEST_SUCCESSFUL, FakePrintPreviewPageHandler} from 'chrome://os-print/js/fakes/fake_print_preview_page_handler.js'; +import {assert} from 'chrome://resources/js/assert.js'; +import {assertEquals} from 'chrome://webui-test/chromeos/chai_assert.js'; + +suite('PrintPreviewCrosApp', () => { + let printPreviewPageHandler: FakePrintPreviewPageHandler; + + setup(() => { + printPreviewPageHandler = new FakePrintPreviewPageHandler(); + assert(printPreviewPageHandler); + }); + + // Verify initial call count for tracked methods is zero. + test('call count zero', () => { + assertEquals(0, printPreviewPageHandler.getCallCount('print')); + }); + + // Verify the fake PrintPreviewPageHandler returns a successful response by + // default. + test('default fake print request result return successful', async () => { + const result = await printPreviewPageHandler.print(); + assertEquals( + FAKE_PRINT_REQUEST_SUCCESSFUL, result, + `Print request should be successful`); + }); + + // Verify the fake PrintPreviewPageHandler can set expected print request + // result to false and resolve. + test('can set print request result', async () => { + printPreviewPageHandler.setPrintResult( + FAKE_PRINT_REQUEST_FAILURE_INVALID_SETTINGS_ERROR); + const result = await printPreviewPageHandler.print(); + assertEquals(FAKE_PRINT_REQUEST_FAILURE_INVALID_SETTINGS_ERROR, result); + }); +});
diff --git a/chrome/test/data/webui/chromeos/print_preview_cros/print_preview_cros_browsertest.cc b/chrome/test/data/webui/chromeos/print_preview_cros/print_preview_cros_browsertest.cc index c8b25a0..6894bd0 100644 --- a/chrome/test/data/webui/chromeos/print_preview_cros/print_preview_cros_browsertest.cc +++ b/chrome/test/data/webui/chromeos/print_preview_cros/print_preview_cros_browsertest.cc
@@ -36,6 +36,11 @@ base::test::ScopedFeatureList scoped_feature_list_; }; +IN_PROC_BROWSER_TEST_F(PrintPreviewCrosBrowserTest, + FakePrintPreviewPageHandlerTest) { + RunTestAtPath("fake_print_preview_page_handler_test.js"); +} + IN_PROC_BROWSER_TEST_F(PrintPreviewCrosBrowserTest, PrintPreviewCrosAppTest) { RunTestAtPath("print_preview_cros_app_test.js"); }
diff --git a/chrome/test/data/webui/chromeos/print_preview_cros/print_ticket_manager_test.ts b/chrome/test/data/webui/chromeos/print_preview_cros/print_ticket_manager_test.ts index 0947a67a..95a0d40 100644 --- a/chrome/test/data/webui/chromeos/print_preview_cros/print_ticket_manager_test.ts +++ b/chrome/test/data/webui/chromeos/print_preview_cros/print_ticket_manager_test.ts
@@ -5,9 +5,21 @@ import 'chrome://os-print/js/data/print_ticket_manager.js'; import {PrintTicketManager} from 'chrome://os-print/js/data/print_ticket_manager.js'; +import {FakePrintPreviewPageHandler} from 'chrome://os-print/js/fakes/fake_print_preview_page_handler.js'; +import {setPrintPreviewPageHandlerForTesting} from 'chrome://os-print/js/utils/mojo_data_providers.js'; import {assertEquals, assertTrue} from 'chrome://webui-test/chromeos/chai_assert.js'; suite('PrintTicketManager', () => { + let printPreviewPageHandler: FakePrintPreviewPageHandler; + + setup(() => { + PrintTicketManager.resetInstanceForTesting(); + + // Setup fakes for testing. + printPreviewPageHandler = new FakePrintPreviewPageHandler(); + setPrintPreviewPageHandlerForTesting(printPreviewPageHandler); + }); + test('is a singleton', () => { const instance1 = PrintTicketManager.getInstance(); const instance2 = PrintTicketManager.getInstance(); @@ -20,4 +32,12 @@ const instance2 = PrintTicketManager.getInstance(); assertTrue(instance1 !== instance2); }); + + // Verify PrintPreviewPageHandler called when sentPrintRequest triggered. + test('sendPrintRequest calls PrintPreviewPageHandler.print', () => { + const instance = PrintTicketManager.getInstance(); + assertEquals(0, printPreviewPageHandler.getCallCount('print')); + instance.sendPrintRequest(); + assertEquals(1, printPreviewPageHandler.getCallCount('print')); + }); });
diff --git a/chrome/test/data/webui/settings/chromeos/device_page/fake_display_settings_provider.ts b/chrome/test/data/webui/settings/chromeos/device_page/fake_display_settings_provider.ts index 4e359bb..af4dda5 100644 --- a/chrome/test/data/webui/settings/chromeos/device_page/fake_display_settings_provider.ts +++ b/chrome/test/data/webui/settings/chromeos/device_page/fake_display_settings_provider.ts
@@ -29,6 +29,7 @@ private displayConfigurationObservers: DisplayConfigurationObserverInterface[] = []; private isTabletMode: boolean = false; + private performanceSettingEnabled: boolean = false; private internalDisplayHistogram = new Map<DisplaySettingsType, number>(); private externalDisplayHistogram = new Map<DisplaySettingsType, number>(); private displayHistogram = new Map<DisplaySettingsType, number>(); @@ -153,6 +154,15 @@ } } + // Implement DisplaySettingsProviderInterface. + setShinyPerformance(enabled: boolean): void { + this.performanceSettingEnabled = enabled; + } + + getShinyPerformance(): boolean { + return this.performanceSettingEnabled; + } + getInternalDisplayHistogram(): Map<DisplaySettingsType, number> { return this.internalDisplayHistogram; }
diff --git a/chrome/test/data/webui/settings/clear_browsing_data_test.ts b/chrome/test/data/webui/settings/clear_browsing_data_test.ts index b3052617..0fd229a 100644 --- a/chrome/test/data/webui/settings/clear_browsing_data_test.ts +++ b/chrome/test/data/webui/settings/clear_browsing_data_test.ts
@@ -429,8 +429,13 @@ }); async function assertDropdownSelectionPersisted( - tabName: string, prefName: string) { + tabIndex: number, tabName: string, prefName: string) { assertTrue(element.$.clearBrowsingDataDialog.open); + // The user selects the tab of interest. + const crTabs = element.shadowRoot!.querySelector('cr-tabs'); + assertTrue(!!crTabs); + crTabs.selected = tabIndex; + const timePeriodDropdown = getTimePeriodDropdown(tabName, element); const selectElement = timePeriodDropdown.shadowRoot!.querySelector('select'); @@ -452,22 +457,26 @@ assertTrue(!!element.$.cookiesCheckboxBasic); element.$.cookiesCheckboxBasic.$.checkbox.click(); await element.$.cookiesCheckboxBasic.$.checkbox.updateComplete; - // Confirming the deletion persists the dropdown selection to the pref. + // Confirming the deletion persists the dropdown selection to the pref and + // sends the time range for clearing. const actionButton = element.shadowRoot!.querySelector<CrButtonElement>('.action-button'); assertTrue(!!actionButton); actionButton.click(); assertEquals(TimePeriod.LAST_WEEK, element.getPref(prefName).value); + const args = await testBrowserProxy.whenCalled('clearBrowsingData'); + const timeRange = args[1]; + assertEquals(TimePeriod.LAST_WEEK, timeRange); } test('dropdownSelectionPersisted_Basic', function() { return assertDropdownSelectionPersisted( - 'basic-tab', 'browser.clear_data.time_period_basic'); + /*tabIndex*/ 0, 'basic-tab', 'browser.clear_data.time_period_basic'); }); test('dropdownSelectionPersisted_Advanced', function() { return assertDropdownSelectionPersisted( - 'advanced-tab', 'browser.clear_data.time_period'); + /*tabIndex*/ 1, 'advanced-tab', 'browser.clear_data.time_period'); }); test('tabSelection', async function() {
diff --git a/chrome/test/data/webui/settings/settings_browsertest.cc b/chrome/test/data/webui/settings/settings_browsertest.cc index 2cd4616..dbf5a44e 100644 --- a/chrome/test/data/webui/settings/settings_browsertest.cc +++ b/chrome/test/data/webui/settings/settings_browsertest.cc
@@ -884,11 +884,12 @@ protected: SettingsPrivacyPageTest() { scoped_feature_list1_.InitWithFeatures( - {permissions::features::kPermissionStorageAccessAPI, + { #if BUILDFLAG(IS_CHROMEOS) - blink::features::kWebPrinting, + blink::features::kWebPrinting, #endif - features::kSafetyCheckNotificationPermissions, features::kSafetyHub}, + features::kSafetyCheckNotificationPermissions, + features::kSafetyHub}, {}); scoped_feature_list2_.InitAndEnableFeatureWithParameters( features::kFedCm, { @@ -1303,7 +1304,6 @@ scoped_feature_list_.InitWithFeatures( { content_settings::features::kSafetyCheckUnusedSitePermissions, - permissions::features::kPermissionStorageAccessAPI, features::kAutomaticFullscreenContentSetting, features::kSafetyHub, }, @@ -1357,12 +1357,6 @@ "runMochaSuite('UnusedSitePermissionsReviewSafetyHubDisabled')"); } -IN_PROC_BROWSER_TEST_F(SettingsSiteSettingsPageTest, - PermissionStorageAccessApiDisabled) { - RunTest("settings/site_settings_page_test.js", - "runMochaSuite('PermissionStorageAccessApiDisabled')"); -} - IN_PROC_BROWSER_TEST_F(SettingsSiteSettingsPageTest, SafetyHubDisabled) { RunTest("settings/site_settings_page_test.js", "runMochaSuite('SafetyHubDisabled')");
diff --git a/chrome/test/data/webui/settings/site_settings_page_test.ts b/chrome/test/data/webui/settings/site_settings_page_test.ts index 103535a..94580c2 100644 --- a/chrome/test/data/webui/settings/site_settings_page_test.ts +++ b/chrome/test/data/webui/settings/site_settings_page_test.ts
@@ -454,33 +454,6 @@ }); }); -suite('PermissionStorageAccessApiDisabled', function() { - let page: SettingsSiteSettingsPageElement; - - suiteSetup(function() { - loadTimeData.overrideValues({ - enablePermissionStorageAccessApi: false, - }); - }); - - setup(function() { - document.body.innerHTML = window.trustedTypes!.emptyHTML; - page = document.createElement('settings-site-settings-page'); - document.body.appendChild(page); - flush(); - }); - - teardown(function() { - page.remove(); - }); - - test('StorageAccessLinkRow', function() { - assertFalse(isChildVisible( - page.shadowRoot!.querySelector('#basicPermissionsList')!, - '#storage-access')); - }); -}); - // TODO(crbug/1443466): Remove after SafetyHub is launched. suite('SafetyHubDisabled', function() { let page: SettingsSiteSettingsPageElement;
diff --git a/chrome/test/enterprise/e2e/policy/encrypted_reporting/report_cbcm_events.py b/chrome/test/enterprise/e2e/policy/encrypted_reporting/report_cbcm_events.py index 0da34500..75459c9 100644 --- a/chrome/test/enterprise/e2e/policy/encrypted_reporting/report_cbcm_events.py +++ b/chrome/test/enterprise/e2e/policy/encrypted_reporting/report_cbcm_events.py
@@ -20,8 +20,8 @@ @environment(file="../policy_test.asset.textpb") class ReportCbcmEvents(ChromeReportingConnectorTestCase): """ - This test verifies that CBCM enrolled browsers can send events - to the encrypted reporting server. + This test verifies that chrome browsers that are commercially managed + (CBCM browsers) can send events to the ChromeOS encrypted reporting server. """ @before_all @@ -69,7 +69,7 @@ return url @test - def test_ReportExtensionInstallEvent(self): + def test_ReportCbcmEvent(self): test_start_time_in_microseconds = round(time.time() * 1000000) # Enroll browser to managedchrome.com domain
diff --git a/chrome/updater/BUILD.gn b/chrome/updater/BUILD.gn index a1e35b2..87f1bb0 100644 --- a/chrome/updater/BUILD.gn +++ b/chrome/updater/BUILD.gn
@@ -604,6 +604,8 @@ "//chrome/updater/app/server/win:winver", "//chrome/updater/win:wrl_strict", ] + + libs = [ "wtsapi32.lib" ] } if (is_linux) {
diff --git a/chrome/updater/util/win_util.cc b/chrome/updater/util/win_util.cc index 7a000f77..cd90e763 100644 --- a/chrome/updater/util/win_util.cc +++ b/chrome/updater/util/win_util.cc
@@ -13,6 +13,7 @@ #include <windows.h> #include <winhttp.h> #include <wrl/client.h> +#include <wtsapi32.h> #include <algorithm> #include <cstdlib> @@ -1276,4 +1277,140 @@ return true; } +namespace { + +struct ScopedWtsConnectStateCloseTraits { + static WTS_CONNECTSTATE_CLASS* InvalidValue() { return nullptr; } + static void Free(WTS_CONNECTSTATE_CLASS* memory) { ::WTSFreeMemory(memory); } +}; + +struct ScopedWtsSessionInfoCloseTraits { + static PWTS_SESSION_INFO InvalidValue() { return nullptr; } + static void Free(PWTS_SESSION_INFO memory) { ::WTSFreeMemory(memory); } +}; + +using ScopedWtsConnectState = + base::ScopedGeneric<WTS_CONNECTSTATE_CLASS*, + ScopedWtsConnectStateCloseTraits>; +using ScopedWtsSessionInfo = + base::ScopedGeneric<PWTS_SESSION_INFO, ScopedWtsSessionInfoCloseTraits>; + +// Returns `true` if there is a user logged on and active in the specified +// session. +bool IsSessionActive(std::optional<DWORD> session_id) { + if (!session_id) { + return false; + } + + ScopedWtsConnectState wts_connect_state; + DWORD bytes_returned = 0; + if (::WTSQuerySessionInformation( + WTS_CURRENT_SERVER_HANDLE, *session_id, WTSConnectState, + reinterpret_cast<LPTSTR*>( + ScopedWtsConnectState::Receiver(wts_connect_state).get()), + &bytes_returned)) { + CHECK_EQ(bytes_returned, sizeof(WTS_CONNECTSTATE_CLASS)); + return *wts_connect_state.get() == WTSActive; + } + + return false; +} + +// Returns the currently active session. +// `WTSGetActiveConsoleSessionId` retrieves the Terminal Services session +// currently attached to the physical console, so that is attempted first. +// `WTSGetActiveConsoleSessionId` does not work for terminal servers where the +// current active session is always the console. For those, an active session +// is found by enumerating all the sessions that are present on the system, and +// the first active session is returned. +std::optional<DWORD> GetActiveSessionId() { + if (DWORD active_session_id = ::WTSGetActiveConsoleSessionId(); + IsSessionActive(active_session_id)) { + return active_session_id; + } + + ScopedWtsSessionInfo session_info; + DWORD num_sessions = 0; + if (::WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, 0, 1, + ScopedWtsSessionInfo::Receiver(session_info).get(), + &num_sessions)) { + for (size_t i = 0; i < num_sessions; ++i) { + if (session_info.get()[i].State == WTSActive) { + return session_info.get()[i].SessionId; + } + } + } + + return {}; +} + +std::vector<DWORD> FindProcesses(const std::wstring& process_name) { + base::NamedProcessIterator iter(process_name, nullptr); + std::vector<DWORD> pids; + while (const base::ProcessEntry* process_entry = iter.NextProcessEntry()) { + pids.push_back(process_entry->pid()); + } + return pids; +} + +// Returns processes running under `session_id`. +std::vector<DWORD> FindProcessesInSession(const std::wstring& process_name, + std::optional<DWORD> session_id) { + if (!session_id) { + return {}; + } + std::vector<DWORD> pids; + for (const auto pid : FindProcesses(process_name)) { + DWORD process_session = 0; + if (::ProcessIdToSessionId(pid, &process_session) && + (process_session == *session_id)) { + pids.push_back(pid); + } + } + return pids; +} + +// Returns the first instance found of explorer.exe. +std::optional<DWORD> GetExplorerPid() { + std::vector<DWORD> pids = + FindProcessesInSession(L"EXPLORER.EXE", GetActiveSessionId()); + if (pids.empty()) { + return {}; + } + return pids[0]; +} + +// Returns an impersonation token for the user running process_id. +HResultOr<ScopedKernelHANDLE> GetImpersonationToken( + std::optional<DWORD> process_id) { + if (!process_id) { + return base::unexpected(E_UNEXPECTED); + } + base::win::ScopedHandle process(::OpenProcess( + PROCESS_DUP_HANDLE | PROCESS_QUERY_INFORMATION, TRUE, *process_id)); + if (!process.IsValid()) { + return base::unexpected(HRESULTFromLastError()); + } + ScopedKernelHANDLE process_token; + if (!::OpenProcessToken(process.Get(), TOKEN_DUPLICATE | TOKEN_QUERY, + ScopedKernelHANDLE::Receiver(process_token).get())) { + return base::unexpected(HRESULTFromLastError()); + } + ScopedKernelHANDLE user_token; + if (!::DuplicateTokenEx(process_token.get(), + TOKEN_IMPERSONATE | TOKEN_QUERY | + TOKEN_ASSIGN_PRIMARY | TOKEN_DUPLICATE, + NULL, SecurityImpersonation, TokenPrimary, + ScopedKernelHANDLE::Receiver(user_token).get())) { + return base::unexpected(HRESULTFromLastError()); + } + return user_token; +} + +} // namespace + +HResultOr<ScopedKernelHANDLE> GetLoggedOnUserToken() { + return GetImpersonationToken(GetExplorerPid()); +} + } // namespace updater
diff --git a/chrome/updater/util/win_util.h b/chrome/updater/util/win_util.h index cef0f44..4e65f84 100644 --- a/chrome/updater/util/win_util.h +++ b/chrome/updater/util/win_util.h
@@ -36,6 +36,7 @@ #include "base/win/win_util.h" #include "base/win/windows_types.h" #include "chrome/updater/updater_scope.h" +#include "chrome/updater/win/scoped_handle.h" namespace base { class FilePath; @@ -429,6 +430,10 @@ // or it defaults to US English. std::wstring GetTextForSystemError(int error); +// Retrieves the logged on user token for the active explorer process if one +// exists. +HResultOr<ScopedKernelHANDLE> GetLoggedOnUserToken(); + } // namespace updater #endif // CHROME_UPDATER_UTIL_WIN_UTIL_H_
diff --git a/chrome/updater/util/win_util_unittest.cc b/chrome/updater/util/win_util_unittest.cc index de5edf7..5851686f 100644 --- a/chrome/updater/util/win_util_unittest.cc +++ b/chrome/updater/util/win_util_unittest.cc
@@ -48,6 +48,7 @@ #include "chrome/updater/updater_version.h" #include "chrome/updater/util/unit_test_util.h" #include "chrome/updater/util/unit_test_util_win.h" +#include "chrome/updater/win/scoped_impersonation.h" #include "chrome/updater/win/test/test_executables.h" #include "chrome/updater/win/test/test_strings.h" #include "chrome/updater/win/win_constants.h" @@ -576,4 +577,18 @@ L"0x80040200"); } +TEST(WinUtil, GetLoggedOnUserToken) { + if (!::IsUserAnAdmin() || !IsUACOn()) { + return; + } + + ASSERT_TRUE(::IsUserAnAdmin()); + HResultOr<ScopedKernelHANDLE> token = GetLoggedOnUserToken(); + ASSERT_TRUE(token.has_value()); + + ScopedImpersonation impersonate; + ASSERT_TRUE(SUCCEEDED(impersonate.Impersonate(token.value().get()))); + ASSERT_FALSE(::IsUserAnAdmin()); +} + } // namespace updater
diff --git a/chromeos/tast_control.gni b/chromeos/tast_control.gni index 9242ce7..04c8fd5e8 100644 --- a/chromeos/tast_control.gni +++ b/chromeos/tast_control.gni
@@ -122,6 +122,10 @@ "a11y.Smoke", "a11y.Smoke.lacros", + # b/326849541 + "inputs.InputMethodManagement.guest", + "inputs.PhysicalKeyboardEmojiSuggestion.guest", + # READ COMMENT AT TOP BEFORE ADDING NEW TESTS HERE. ]
diff --git a/clank b/clank index 86871e6..15ea959 160000 --- a/clank +++ b/clank
@@ -1 +1 @@ -Subproject commit 86871e647c038acdf24601d6955b545a59642446 +Subproject commit 15ea959101d2efe298a00841d9f8c015a387b57b
diff --git a/components/autofill/content/common/mojom/autofill_agent.mojom b/components/autofill/content/common/mojom/autofill_agent.mojom index af94d41..c2ed1a5 100644 --- a/components/autofill/content/common/mojom/autofill_agent.mojom +++ b/components/autofill/content/common/mojom/autofill_agent.mojom
@@ -124,6 +124,9 @@ // Previews the given `value` into the field identified by `field_id`. PreviewField(FieldRendererId field_id, mojo_base.mojom.String16 value); + // Fills the given `value` into the field identified by `field_id`. + FillField(FieldRendererId field_id, mojo_base.mojom.String16 value); + // Notification to start (`active` == true) or stop (`active` == false) // logging the decisions made about saving the password. SetLoggingState(bool active);
diff --git a/components/autofill/content/renderer/password_autofill_agent.cc b/components/autofill/content/renderer/password_autofill_agent.cc index fa641b5..3442051 100644 --- a/components/autofill/content/renderer/password_autofill_agent.cc +++ b/components/autofill/content/renderer/password_autofill_agent.cc
@@ -869,11 +869,11 @@ element.IsPasswordFieldForAutofill()) && !(username.empty() && element.IsPasswordFieldForAutofill()) && username_element.Value().Utf16() != username) { - FillField(&username_element, username); + DoFillField(username_element, username); } if (!password_element.IsNull()) { - FillPasswordFieldAndSave(&password_element, password); + FillPasswordFieldAndSave(password_element, password); // TODO(crbug.com/1319364): As Touch-To-Fill and auto-submission don't // currently support filling single username fields, the code below is @@ -903,12 +903,12 @@ return; } if (!is_password) { - FillField(&focused_input_element, credential); + DoFillField(focused_input_element, credential); } if (!focused_input_element.IsPasswordFieldForAutofill()) { return; } - FillPasswordFieldAndSave(&focused_input_element, credential); + FillPasswordFieldAndSave(focused_input_element, credential); } void PasswordAutofillAgent::PreviewField(FieldRendererId field_id, @@ -922,6 +922,18 @@ /*is_password=*/input.IsPasswordFieldForAutofill()); } +void PasswordAutofillAgent::FillField(FieldRendererId field_id, + const std::u16string& value) { + WebFormControlElement form_control = + form_util::GetFormControlByRendererId(field_id); + WebInputElement input_element = form_control.DynamicTo<WebInputElement>(); + if (input_element.IsNull() || input_element.IsReadOnly()) { + // Early return for non-input fields such as textarea. + return; + } + DoFillField(input_element, value); +} + void PasswordAutofillAgent::DoPreviewField(WebInputElement& input, const std::u16string& credential, bool is_password) { @@ -934,24 +946,22 @@ input.SetSuggestedValue(WebString::FromUTF16(credential)); } -void PasswordAutofillAgent::FillField(WebInputElement* input, - const std::u16string& credential) { - DCHECK(input); - DCHECK(!input->IsNull()); - input->SetAutofillValue(WebString::FromUTF16(credential)); +void PasswordAutofillAgent::DoFillField(WebInputElement& input, + const std::u16string& credential) { + CHECK(!input.IsNull()); + input.SetAutofillValue(WebString::FromUTF16(credential)); field_data_manager().UpdateFieldDataMap( - form_util::GetFieldRendererId(*input), credential, + form_util::GetFieldRendererId(input), credential, FieldPropertiesFlags::kAutofilledOnUserTrigger); - TrackAutofilledElement(*input); + TrackAutofilledElement(input); } void PasswordAutofillAgent::FillPasswordFieldAndSave( - WebInputElement* password_input, + WebInputElement& password_input, const std::u16string& credential) { - DCHECK(password_input); - DCHECK(password_input->IsPasswordFieldForAutofill()); - FillField(password_input, credential); - InformBrowserAboutUserInput(GetFormElement(*password_input), *password_input); + CHECK(password_input.IsPasswordFieldForAutofill()); + DoFillField(password_input, credential); + InformBrowserAboutUserInput(GetFormElement(password_input), password_input); } void PasswordAutofillAgent::PreviewSuggestion(
diff --git a/components/autofill/content/renderer/password_autofill_agent.h b/components/autofill/content/renderer/password_autofill_agent.h index c2c16ce2..dd7ff91 100644 --- a/components/autofill/content/renderer/password_autofill_agent.h +++ b/components/autofill/content/renderer/password_autofill_agent.h
@@ -140,6 +140,8 @@ const std::u16string& credential) override; void PreviewField(FieldRendererId field_id, const std::u16string& value) override; + void FillField(FieldRendererId field_id, + const std::u16string& value) override; void SetLoggingState(bool active) override; void AnnotateFieldsWithParsingResult( const ParsingResult& parsing_result) override; @@ -394,12 +396,12 @@ // Checks that a given input field is valid before filling the given `input` // with the given `credential` and marking the field as auto-filled. - void FillField(blink::WebInputElement* input, - const std::u16string& credential); + void DoFillField(blink::WebInputElement& input, + const std::u16string& credential); // Uses `FillField` to fill the given `credential` into the `password_input`. // Saves the password for its associated form. - void FillPasswordFieldAndSave(blink::WebInputElement* password_input, + void FillPasswordFieldAndSave(blink::WebInputElement& password_input, const std::u16string& credential); // `form` and `input` are the elements user has just been interacting with
diff --git a/components/autofill/core/browser/autofill_client.cc b/components/autofill/core/browser/autofill_client.cc index ab40594..a6972c7 100644 --- a/components/autofill/core/browser/autofill_client.cc +++ b/components/autofill/core/browser/autofill_client.cc
@@ -257,8 +257,7 @@ } void AutofillClient::ShowAutofillErrorDialog( - const AutofillErrorDialogContext& context) { -} + AutofillErrorDialogContext context) {} LogManager* AutofillClient::GetLogManager() const { return nullptr;
diff --git a/components/autofill/core/browser/autofill_client.h b/components/autofill/core/browser/autofill_client.h index 3cb039cc..ca3e1a1 100644 --- a/components/autofill/core/browser/autofill_client.h +++ b/components/autofill/core/browser/autofill_client.h
@@ -801,8 +801,7 @@ // `server_returned_title` and `server_returned_description` in `context` are // both set, the error dialog that is displayed will have these fields // displayed for the title and description, respectively. - virtual void ShowAutofillErrorDialog( - const AutofillErrorDialogContext& context); + virtual void ShowAutofillErrorDialog(AutofillErrorDialogContext context); // Maybe triggers a hats survey that measures the user's perception of // Autofill. When triggering happens, the survey dialog will be displayed with
diff --git a/components/autofill/core/browser/payments/autofill_error_dialog_context.cc b/components/autofill/core/browser/payments/autofill_error_dialog_context.cc index a8879ef..2973d86 100644 --- a/components/autofill/core/browser/payments/autofill_error_dialog_context.cc +++ b/components/autofill/core/browser/payments/autofill_error_dialog_context.cc
@@ -22,9 +22,18 @@ AutofillErrorDialogContext::AutofillErrorDialogContext( const AutofillErrorDialogContext& other) = default; +AutofillErrorDialogContext::AutofillErrorDialogContext( + AutofillErrorDialogContext&& other) = default; + AutofillErrorDialogContext& AutofillErrorDialogContext::operator=( const AutofillErrorDialogContext&) = default; +AutofillErrorDialogContext& AutofillErrorDialogContext::operator=( + AutofillErrorDialogContext&&) = default; + AutofillErrorDialogContext::~AutofillErrorDialogContext() = default; +bool AutofillErrorDialogContext::operator==( + const AutofillErrorDialogContext& other_context) const = default; + } // namespace autofill
diff --git a/components/autofill/core/browser/payments/autofill_error_dialog_context.h b/components/autofill/core/browser/payments/autofill_error_dialog_context.h index 8a58a21..0fcdc82 100644 --- a/components/autofill/core/browser/payments/autofill_error_dialog_context.h +++ b/components/autofill/core/browser/payments/autofill_error_dialog_context.h
@@ -48,9 +48,13 @@ AutofillErrorDialogContext(); AutofillErrorDialogContext(const AutofillErrorDialogContext& other); + AutofillErrorDialogContext(AutofillErrorDialogContext&& other); AutofillErrorDialogContext& operator=(const AutofillErrorDialogContext&); + AutofillErrorDialogContext& operator=(AutofillErrorDialogContext&&); ~AutofillErrorDialogContext(); + bool operator==(const AutofillErrorDialogContext& other_context) const; + // The type of autofill error dialog that will be displayed. AutofillErrorDialogType type = AutofillErrorDialogType::kTypeUnknown;
diff --git a/components/autofill/core/browser/payments/payments_window_manager.h b/components/autofill/core/browser/payments/payments_window_manager.h index d546f7cd..627e86a 100644 --- a/components/autofill/core/browser/payments/payments_window_manager.h +++ b/components/autofill/core/browser/payments/payments_window_manager.h
@@ -58,8 +58,17 @@ // The error type of the 3DS authentication inside of the pop-up. enum class Vcn3dsAuthenticationPopupErrorType { + // The authentication inside of the 3DS pop-up was a failure. The reason for + // the failure is unknown to Chrome, and can be due to any of several + // possible reasons. Some reasons can be that the user failed to + // authenticate, or there is a server error. kAuthenticationFailed = 0, + // The authentication inside of the 3DS pop-up did not complete. This occurs + // if the user closes the pop-up before finishing the authentication, and + // there are no query params. kAuthenticationNotCompleted = 1, + // The query params are invalid. This should not happen, but since Chrome + // has no control over this it is handled gracefully. kInvalidQueryParams = 2, };
diff --git a/components/autofill/core/browser/test_autofill_client.h b/components/autofill/core/browser/test_autofill_client.h index d5778759..11e2204 100644 --- a/components/autofill/core/browser/test_autofill_client.h +++ b/components/autofill/core/browser/test_autofill_client.h
@@ -467,10 +467,9 @@ PopupHidingReason popup_hiding_reason() { return popup_hidden_reason_; } - void ShowAutofillErrorDialog( - const AutofillErrorDialogContext& context) override { + void ShowAutofillErrorDialog(AutofillErrorDialogContext context) override { autofill_error_dialog_shown_ = true; - autofill_error_dialog_context_ = context; + autofill_error_dialog_context_ = std::move(context); } bool IsAutocompleteEnabled() const override { return true; }
diff --git a/components/autofill/core/browser/ui/payments/autofill_error_dialog_controller_impl.cc b/components/autofill/core/browser/ui/payments/autofill_error_dialog_controller_impl.cc index ca112ba..b6c5456c6 100644 --- a/components/autofill/core/browser/ui/payments/autofill_error_dialog_controller_impl.cc +++ b/components/autofill/core/browser/ui/payments/autofill_error_dialog_controller_impl.cc
@@ -14,35 +14,30 @@ namespace autofill { -AutofillErrorDialogControllerImpl::AutofillErrorDialogControllerImpl() = - default; +AutofillErrorDialogControllerImpl::AutofillErrorDialogControllerImpl( + AutofillErrorDialogContext error_dialog_context) + : error_dialog_context_(std::move(error_dialog_context)) {} AutofillErrorDialogControllerImpl::~AutofillErrorDialogControllerImpl() { - Dismiss(); + DismissIfApplicable(); } void AutofillErrorDialogControllerImpl::Show( - const AutofillErrorDialogContext& autofill_error_dialog_context, base::OnceCallback<base::WeakPtr<AutofillErrorDialogView>()> view_creation_callback) { - if (autofill_error_dialog_view_) { - Dismiss(); - } - CHECK(!autofill_error_dialog_view_); - error_dialog_context_ = autofill_error_dialog_context; autofill_error_dialog_view_ = std::move(view_creation_callback).Run(); CHECK(autofill_error_dialog_view_); base::UmaHistogramEnumeration("Autofill.ErrorDialogShown", - autofill_error_dialog_context.type); + error_dialog_context_.type); // If both |server_returned_title| and |server_returned_description| are // populated, then the error dialog was displayed with the server-driven text. if (error_dialog_context_.server_returned_title && error_dialog_context_.server_returned_description) { base::UmaHistogramEnumeration("Autofill.ErrorDialogShown.WithServerText", - autofill_error_dialog_context.type); + error_dialog_context_.type); } } @@ -135,7 +130,7 @@ IDS_AUTOFILL_ERROR_DIALOG_NEGATIVE_BUTTON_LABEL); } -void AutofillErrorDialogControllerImpl::Dismiss() { +void AutofillErrorDialogControllerImpl::DismissIfApplicable() { if (autofill_error_dialog_view_) { autofill_error_dialog_view_->Dismiss(); }
diff --git a/components/autofill/core/browser/ui/payments/autofill_error_dialog_controller_impl.h b/components/autofill/core/browser/ui/payments/autofill_error_dialog_controller_impl.h index b711809..c87ddfd 100644 --- a/components/autofill/core/browser/ui/payments/autofill_error_dialog_controller_impl.h +++ b/components/autofill/core/browser/ui/payments/autofill_error_dialog_controller_impl.h
@@ -21,7 +21,8 @@ // The controller is destroyed once the view is dismissed. class AutofillErrorDialogControllerImpl : public AutofillErrorDialogController { public: - AutofillErrorDialogControllerImpl(); + explicit AutofillErrorDialogControllerImpl( + AutofillErrorDialogContext error_dialog_context); ~AutofillErrorDialogControllerImpl() override; AutofillErrorDialogControllerImpl(const AutofillErrorDialogControllerImpl&) = @@ -29,10 +30,8 @@ AutofillErrorDialogControllerImpl& operator=( const AutofillErrorDialogControllerImpl&) = delete; - // Show the error dialog for the given `autofill_error_dialog_context` and the - // `view_creation_callback`. - void Show(const AutofillErrorDialogContext& autofill_error_dialog_context, - base::OnceCallback<base::WeakPtr<AutofillErrorDialogView>()> + // Provide the `view_creation_callback` and show the error dialog. + void Show(base::OnceCallback<base::WeakPtr<AutofillErrorDialogView>()> view_creation_callback); // AutofillErrorDialogController. @@ -48,7 +47,7 @@ private: // Dismiss the error dialog if showing. - void Dismiss(); + void DismissIfApplicable(); // The context of the error dialog that is being displayed. Contains // information such as the type of the error dialog that is being displayed. @@ -57,7 +56,7 @@ // |error_dialog_context_| contains this information, the fields in // |error_dialog_context_| should be preferred when displaying the error // dialog. - AutofillErrorDialogContext error_dialog_context_; + const AutofillErrorDialogContext error_dialog_context_; // View that displays the error dialog. base::WeakPtr<AutofillErrorDialogView> autofill_error_dialog_view_;
diff --git a/components/autofill/core/browser/ui/payments/autofill_error_dialog_controller_impl_unittest.cc b/components/autofill/core/browser/ui/payments/autofill_error_dialog_controller_impl_unittest.cc index 1c7d7d1..93b5d80 100644 --- a/components/autofill/core/browser/ui/payments/autofill_error_dialog_controller_impl_unittest.cc +++ b/components/autofill/core/browser/ui/payments/autofill_error_dialog_controller_impl_unittest.cc
@@ -41,16 +41,11 @@ public: AutofillErrorDialogControllerImplTest() = default; - void SetUp() override { - controller_ = std::make_unique<AutofillErrorDialogControllerImpl>(); - } - void ShowPrompt(const AutofillErrorDialogContext& context) { - controller()->Show( - context, - base::BindOnce( - &AutofillErrorDialogControllerImplTest::CreateErrorDialogView, - base::Unretained(this))); + controller_ = std::make_unique<AutofillErrorDialogControllerImpl>(context); + controller_->Show(base::BindOnce( + &AutofillErrorDialogControllerImplTest::CreateErrorDialogView, + base::Unretained(this))); } base::WeakPtr<AutofillErrorDialogView> CreateErrorDialogView() {
diff --git a/components/autofill/ios/browser/resources/suggestion_controller.ts b/components/autofill/ios/browser/resources/suggestion_controller.ts index 71d1aea..0d12626 100644 --- a/components/autofill/ios/browser/resources/suggestion_controller.ts +++ b/components/autofill/ios/browser/resources/suggestion_controller.ts
@@ -367,19 +367,24 @@ currentElement, document.querySelectorAll('*')) !== null; } +declare interface PreviousNextElements { + previous: boolean; + next: boolean; +} + /** * @param formName The name of the form containing the element. * @param fieldName The name of the field containing the element. * @return Whether there is an element in the sequential navigation before and - * after currently active element. The result is returned as a comma - * separated string of the strings `true` and `false`. - * TODO(crbug.com/893368): Return a dictionary with the values instead. + * after currently active element. The result is returned as an object with + * a boolean value for the keys `previous` and `next`. */ -function hasPreviousNextElements(formName: string, fieldName: string): string { - return [ - hasPreviousElement(formName, fieldName), - hasNextElement(formName, fieldName), - ].toString(); +function hasPreviousNextElements( + formName: string, fieldName: string): PreviousNextElements { + return { + previous: hasPreviousElement(formName, fieldName), + next: hasNextElement(formName, fieldName), + }; } gCrWeb.suggestion = {
diff --git a/components/autofill/ios/browser/suggestion_controller_java_script_feature.mm b/components/autofill/ios/browser/suggestion_controller_java_script_feature.mm index 9f64988..5a1587a6b 100644 --- a/components/autofill/ios/browser/suggestion_controller_java_script_feature.mm +++ b/components/autofill/ios/browser/suggestion_controller_java_script_feature.mm
@@ -26,31 +26,26 @@ void ProcessPreviousAndNextElementsPresenceResult( base::OnceCallback<void(bool, bool)> completion_handler, const base::Value* res) { - NSString* result = nil; - if (res && res->is_string()) { - result = base::SysUTF8ToNSString(res->GetString()); - } - // The result maybe an empty string here due to 2 reasons: + // The result may be invalid: // 1) When there is an exception running the JS // 2) There is a race when the page is changing due to which // SuggestionControllerJavaScriptFeature has not yet injected the // __gCrWeb.suggestion object. - // Handle this case gracefully. If a page has overridden - // Array.toString, the string returned may not contain a ",", - // hence this is a defensive measure to early return. - NSArray* components = [result componentsSeparatedByString:@","]; - if (components.count != 2) { + // Handle this case gracefully. + if (!res || !res->is_dict() || res->GetDict().size() != 2) { std::move(completion_handler).Run(false, false); return; } - DCHECK([components[0] isEqualToString:@"true"] || - [components[0] isEqualToString:@"false"]); - bool has_previous_element = [components[0] isEqualToString:@"true"]; - DCHECK([components[1] isEqualToString:@"true"] || - [components[1] isEqualToString:@"false"]); - bool has_next_element = [components[1] isEqualToString:@"true"]; - std::move(completion_handler).Run(has_previous_element, has_next_element); + const base::Value::Dict& dict = res->GetDict(); + std::optional<bool> previous = dict.FindBool("previous"); + std::optional<bool> next = dict.FindBool("next"); + if (!previous || !next) { + std::move(completion_handler).Run(false, false); + return; + } + + std::move(completion_handler).Run(previous.value(), next.value()); } } // namespace
diff --git a/components/blocklist/opt_out_blocklist/sql/opt_out_store_sql.cc b/components/blocklist/opt_out_blocklist/sql/opt_out_store_sql.cc index 33266cf0..bcee528 100644 --- a/components/blocklist/opt_out_blocklist/sql/opt_out_store_sql.cc +++ b/components/blocklist/opt_out_blocklist/sql/opt_out_store_sql.cc
@@ -28,13 +28,6 @@ namespace blocklist { -// When enabled, prefer to use the new recovery module to recover the -// `OptOutStoreSQL` database. See https://crbug.com/1385500 for details. -// This is a kill switch and is not intended to be used in a field trial. -BASE_FEATURE(kOptOutStoreSQLUseBuiltInRecoveryIfSupported, - "OptOutStoreSQLUseBuiltInRecoveryIfSupported", - base::FEATURE_ENABLED_BY_DEFAULT); - namespace { // Command line switch to change the entry per host DB size. @@ -102,8 +95,7 @@ sql::Statement* stmt) { // Attempt to recover a corrupt database, if it is eligible to be recovered. if (sql::BuiltInRecovery::RecoverIfPossible( - db, extended_error, sql::BuiltInRecovery::Strategy::kRecoverOrRaze, - &kOptOutStoreSQLUseBuiltInRecoveryIfSupported)) { + db, extended_error, sql::BuiltInRecovery::Strategy::kRecoverOrRaze)) { // Recovery was attempted. The database handle has been poisoned and the // error callback has been reset.
diff --git a/components/blocklist/opt_out_blocklist/sql/opt_out_store_sql.h b/components/blocklist/opt_out_blocklist/sql/opt_out_store_sql.h index dcbd120..d3d54b9 100644 --- a/components/blocklist/opt_out_blocklist/sql/opt_out_store_sql.h +++ b/components/blocklist/opt_out_blocklist/sql/opt_out_store_sql.h
@@ -27,8 +27,6 @@ namespace blocklist { -BASE_DECLARE_FEATURE(kOptOutStoreSQLUseBuiltInRecoveryIfSupported); - // OptOutStoreSQL is an instance of OptOutStore // which is implemented using a SQLite database. class OptOutStoreSQL : public OptOutStore {
diff --git a/components/browser_ui/accessibility/android/java/src/org/chromium/components/browser_ui/accessibility/AccessibilitySettingsDelegate.java b/components/browser_ui/accessibility/android/java/src/org/chromium/components/browser_ui/accessibility/AccessibilitySettingsDelegate.java index 186b063..90382aa 100644 --- a/components/browser_ui/accessibility/android/java/src/org/chromium/components/browser_ui/accessibility/AccessibilitySettingsDelegate.java +++ b/components/browser_ui/accessibility/android/java/src/org/chromium/components/browser_ui/accessibility/AccessibilitySettingsDelegate.java
@@ -11,15 +11,6 @@ * embedder-specific logic. */ public interface AccessibilitySettingsDelegate { - /** An interface to control a single boolean preference. */ - interface BooleanPreferenceDelegate { - /** @return whether the preference is enabled. */ - boolean isEnabled(); - - /** Called when the preference value is changed. */ - void setEnabled(boolean value); - } - /** An interface to control a single integer preference. */ interface IntegerPreferenceDelegate { /** @return int - Current value of the preference of this instance. */ @@ -36,12 +27,6 @@ BrowserContextHandle getBrowserContextHandle(); /** - * @return the BooleanPreferenceDelegate instance that should be used when rendering the reader - * for accessibility preference. Return null to omit the preference. - */ - BooleanPreferenceDelegate getReaderForAccessibilityDelegate(); - - /** * @return the InterPreferenceDelegate instance that should be used for reading and setting the * text size contrast value for accessibility settings. Return null to omit the preference. */
diff --git a/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/WebsitePermissionsFetcher.java b/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/WebsitePermissionsFetcher.java index f513839..ff672de 100644 --- a/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/WebsitePermissionsFetcher.java +++ b/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/WebsitePermissionsFetcher.java
@@ -16,8 +16,6 @@ import org.chromium.base.CommandLine; import org.chromium.components.content_settings.ContentSettingValues; import org.chromium.components.content_settings.ContentSettingsType; -import org.chromium.components.permissions.PermissionsAndroidFeatureList; -import org.chromium.components.permissions.PermissionsAndroidFeatureMap; import org.chromium.content_public.browser.BrowserContextHandle; import org.chromium.content_public.browser.ContentFeatureList; import org.chromium.content_public.browser.ContentFeatureMap; @@ -99,11 +97,7 @@ case ContentSettingsType.VR: return WebsitePermissionsType.PERMISSION_INFO; case ContentSettingsType.STORAGE_ACCESS: - if (PermissionsAndroidFeatureMap.isEnabled( - PermissionsAndroidFeatureList.PERMISSION_STORAGE_ACCESS)) { return WebsitePermissionsType.EMBEDDED_PERMISSION; - } - return null; case ContentSettingsType.BLUETOOTH_GUARD: case ContentSettingsType.USB_GUARD: return WebsitePermissionsType.CHOSEN_OBJECT_INFO;
diff --git a/components/commerce/core/BUILD.gn b/components/commerce/core/BUILD.gn index 0e3162e..900d14bd 100644 --- a/components/commerce/core/BUILD.gn +++ b/components/commerce/core/BUILD.gn
@@ -34,6 +34,17 @@ ] } +source_set("feature_utils") { + sources = [ + "feature_utils.cc", + "feature_utils.h", + ] + deps = [ + ":account_checker", + ":feature_list", + ] +} + source_set("feature_list_unittests") { testonly = true sources = [ "commerce_feature_list_unittest.cc" ] @@ -206,7 +217,6 @@ ] deps = [ - ":account_checker", ":commerce_constants", ":commerce_subscription_db_content_proto", ":discounts_db_content_proto", @@ -242,7 +252,9 @@ ] public_deps = [ + ":account_checker", ":commerce_types", + ":feature_utils", "//components/commerce/core/subscriptions:subscriptions", "//services/data_decoder/public/cpp", ] @@ -379,6 +391,7 @@ deps = [ ":account_checker", "//base", + "//components/prefs", "//services/network/public/cpp:cpp", "//testing/gmock", ]
diff --git a/components/commerce/core/account_checker.cc b/components/commerce/core/account_checker.cc index ef8f6a6..5d906e09 100644 --- a/components/commerce/core/account_checker.cc +++ b/components/commerce/core/account_checker.cc
@@ -37,11 +37,15 @@ "https://memex-pa.googleapis.com/v1/notifications/preferences"; AccountChecker::AccountChecker( + std::string country, + std::string locale, PrefService* pref_service, signin::IdentityManager* identity_manager, syncer::SyncService* sync_service, scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory) - : pref_service_(pref_service), + : country_(country), + locale_(locale), + pref_service_(pref_service), identity_manager_(identity_manager), sync_service_(sync_service), url_loader_factory_(url_loader_factory), @@ -113,6 +117,18 @@ signin::Tribool::kTrue; } +std::string AccountChecker::GetCountry() { + return country_; +} + +std::string AccountChecker::GetLocale() { + return locale_; +} + +PrefService* AccountChecker::GetPrefs() { + return pref_service_.get(); +} + void AccountChecker::FetchPriceEmailPref() { if (!IsSignedIn()) { return;
diff --git a/components/commerce/core/account_checker.h b/components/commerce/core/account_checker.h index 7989f2e1..4909fdbd 100644 --- a/components/commerce/core/account_checker.h +++ b/components/commerce/core/account_checker.h
@@ -5,6 +5,8 @@ #ifndef COMPONENTS_COMMERCE_CORE_ACCOUNT_CHECKER_H_ #define COMPONENTS_COMMERCE_CORE_ACCOUNT_CHECKER_H_ +#include <string> + #include "base/memory/scoped_refptr.h" #include "components/endpoint_fetcher/endpoint_fetcher.h" #include "components/signin/public/identity_manager/identity_manager.h" @@ -43,12 +45,22 @@ virtual bool IsSubjectToParentalControls(); + // Gets the user's country as determined at startup. + virtual std::string GetCountry(); + + // Gets the user's locale as determine at startup. + virtual std::string GetLocale(); + + virtual PrefService* GetPrefs(); + protected: friend class ShoppingService; friend class MockAccountChecker; // This class should only be initialized in ShoppingService. AccountChecker( + std::string country, + std::string locale, PrefService* pref_service, signin::IdentityManager* identity_manager, syncer::SyncService* sync_service, @@ -96,6 +108,10 @@ void OnFetchPriceEmailPrefJsonParsed( data_decoder::DataDecoder::ValueOrError result); + std::string country_; + + std::string locale_; + raw_ptr<PrefService> pref_service_; raw_ptr<signin::IdentityManager> identity_manager_;
diff --git a/components/commerce/core/account_checker_unittest.cc b/components/commerce/core/account_checker_unittest.cc index 3338156..364fd67 100644 --- a/components/commerce/core/account_checker_unittest.cc +++ b/components/commerce/core/account_checker_unittest.cc
@@ -53,7 +53,9 @@ signin::IdentityManager* identity_manager, syncer::SyncService* sync_service, scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory) - : AccountChecker(pref_service, + : AccountChecker("us", + "en-us", + pref_service, identity_manager, sync_service, std::move(url_loader_factory)) {}
diff --git a/components/commerce/core/feature_utils.cc b/components/commerce/core/feature_utils.cc new file mode 100644 index 0000000..48b6ab6d --- /dev/null +++ b/components/commerce/core/feature_utils.cc
@@ -0,0 +1,36 @@ +// Copyright 2024 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/commerce/core/feature_utils.h" + +#include "components/commerce/core/account_checker.h" +#include "components/commerce/core/commerce_feature_list.h" + +namespace commerce { + +bool IsShoppingListEligible(AccountChecker* account_checker) { + if (!commerce::IsRegionLockedFeatureEnabled( + kShoppingList, kShoppingListRegionLaunched, + account_checker->GetCountry(), account_checker->GetLocale())) { + return false; + } + + if (!account_checker->GetPrefs() || + !IsShoppingListAllowedForEnterprise(account_checker->GetPrefs())) { + return false; + } + + // Make sure the user allows subscriptions to be made and that we can fetch + // store data. + if (!account_checker || !account_checker->IsSignedIn() || + !account_checker->IsSyncingBookmarks() || + !account_checker->IsAnonymizedUrlDataCollectionEnabled() || + account_checker->IsSubjectToParentalControls()) { + return false; + } + + return true; +} + +} // namespace commerce
diff --git a/components/commerce/core/feature_utils.h b/components/commerce/core/feature_utils.h new file mode 100644 index 0000000..0e616d5 --- /dev/null +++ b/components/commerce/core/feature_utils.h
@@ -0,0 +1,22 @@ +// Copyright 2024 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_COMMERCE_CORE_FEATURE_UTILS_H_ +#define COMPONENTS_COMMERCE_CORE_FEATURE_UTILS_H_ + +namespace commerce { + +class AccountChecker; + +// This is a feature check for the "shopping list". This will only return true +// if the user has the feature flag enabled, is signed-in, has MSBB enabled, +// has webapp activity enabled, is allowed by enterprise policy, and (if +// applicable) in an eligible country and locale. The value returned by this +// method can change at runtime, so it should not be used when deciding +// whether to create critical, feature-related infrastructure. +bool IsShoppingListEligible(AccountChecker* account_checker); + +} // namespace commerce + +#endif // COMPONENTS_COMMERCE_CORE_FEATURE_UTILS_H_
diff --git a/components/commerce/core/mock_account_checker.cc b/components/commerce/core/mock_account_checker.cc index 8c3c652..3ad8fde 100644 --- a/components/commerce/core/mock_account_checker.cc +++ b/components/commerce/core/mock_account_checker.cc
@@ -3,16 +3,19 @@ // found in the LICENSE file. #include "components/commerce/core/mock_account_checker.h" +#include "components/prefs/pref_service.h" namespace commerce { MockAccountChecker::MockAccountChecker() - : AccountChecker(nullptr, nullptr, nullptr, nullptr) { + : AccountChecker("", "", nullptr, nullptr, nullptr, nullptr) { // Default to an account checker with the fewest restrictions. SetSignedIn(true); SetSyncingBookmarks(true); SetAnonymizedUrlDataCollectionEnabled(true); SetIsSubjectToParentalControls(false); + SetCountry("us"); + SetLocale("en-us"); } MockAccountChecker::~MockAccountChecker() = default; @@ -36,4 +39,16 @@ .WillByDefault(testing::Return(subject_to_parental_controls)); } +void MockAccountChecker::SetCountry(std::string country) { + ON_CALL(*this, GetCountry).WillByDefault(testing::Return(country)); +} + +void MockAccountChecker::SetLocale(std::string locale) { + ON_CALL(*this, GetLocale).WillByDefault(testing::Return(locale)); +} + +void MockAccountChecker::SetPrefs(PrefService* prefs) { + ON_CALL(*this, GetPrefs).WillByDefault(testing::Return(prefs)); +} + } // namespace commerce
diff --git a/components/commerce/core/mock_account_checker.h b/components/commerce/core/mock_account_checker.h index 3ba7c2d..d8063023 100644 --- a/components/commerce/core/mock_account_checker.h +++ b/components/commerce/core/mock_account_checker.h
@@ -5,9 +5,13 @@ #ifndef COMPONENTS_COMMERCE_CORE_MOCK_ACCOUNT_CHECKER_H_ #define COMPONENTS_COMMERCE_CORE_MOCK_ACCOUNT_CHECKER_H_ +#include <string> + #include "components/commerce/core/account_checker.h" #include "testing/gmock/include/gmock/gmock.h" +class PrefService; + namespace commerce { // Used to mock user account status in tests. @@ -26,6 +30,12 @@ MOCK_METHOD(bool, IsSubjectToParentalControls, (), (override)); + MOCK_METHOD(std::string, GetCountry, (), (override)); + + MOCK_METHOD(std::string, GetLocale, (), (override)); + + MOCK_METHOD(PrefService*, GetPrefs, (), (override)); + void SetSignedIn(bool signed_in); void SetSyncingBookmarks(bool syncing); @@ -33,6 +43,12 @@ void SetAnonymizedUrlDataCollectionEnabled(bool enabled); void SetIsSubjectToParentalControls(bool subject_to_parental_controls); + + void SetCountry(std::string country); + + void SetLocale(std::string locale); + + void SetPrefs(PrefService* prefs); }; } // namespace commerce
diff --git a/components/commerce/core/mock_shopping_service.cc b/components/commerce/core/mock_shopping_service.cc index a243000..dda4e31 100644 --- a/components/commerce/core/mock_shopping_service.cc +++ b/components/commerce/core/mock_shopping_service.cc
@@ -58,6 +58,11 @@ SetGetAllParcelStatusesCallbackValue(std::vector<ParcelTrackingStatus>()); } +void MockShoppingService::SetAccountChecker(AccountChecker* account_checker) { + ON_CALL(*this, GetAccountChecker) + .WillByDefault(testing::Return(account_checker)); +} + void MockShoppingService::SetResponseForGetProductInfoForUrl( std::optional<commerce::ProductInfo> product_info) { ON_CALL(*this, GetProductInfoForUrl)
diff --git a/components/commerce/core/mock_shopping_service.h b/components/commerce/core/mock_shopping_service.h index 8e78eeb3..b9237fd 100644 --- a/components/commerce/core/mock_shopping_service.h +++ b/components/commerce/core/mock_shopping_service.h
@@ -21,6 +21,8 @@ namespace commerce { +class AccountChecker; + // A mock ShoppingService that allows us to decide the response. class MockShoppingService : public commerce::ShoppingService { public: @@ -31,6 +33,7 @@ ~MockShoppingService() override; // commerce::ShoppingService overrides. + MOCK_METHOD(AccountChecker*, GetAccountChecker, (), (override)); MOCK_METHOD(void, GetProductInfoForUrl, (const GURL& url, commerce::ProductInfoCallback callback), @@ -123,6 +126,7 @@ // data for all accessors of shopping data. void SetupPermissiveMock(); + void SetAccountChecker(AccountChecker* account_checker); void SetResponseForGetProductInfoForUrl( std::optional<commerce::ProductInfo> product_info); void SetResponseForGetPriceInsightsInfoForUrl(
diff --git a/components/commerce/core/pref_names.cc b/components/commerce/core/pref_names.cc index dfcfac87a..188246e 100644 --- a/components/commerce/core/pref_names.cc +++ b/components/commerce/core/pref_names.cc
@@ -17,6 +17,7 @@ registry->RegisterTimePref(kCommerceDailyMetricsLastUpdateTime, base::Time()); registry->RegisterTimePref(kShoppingListBookmarkLastUpdateTime, base::Time()); + registry->RegisterBooleanPref(kProductSpecificationsEnabledPrefName, true); registry->RegisterBooleanPref(kShoppingListEnabledPrefName, true); }
diff --git a/components/commerce/core/pref_names.h b/components/commerce/core/pref_names.h index 43562e5..83c61d28 100644 --- a/components/commerce/core/pref_names.h +++ b/components/commerce/core/pref_names.h
@@ -14,6 +14,11 @@ inline constexpr char kShoppingListBookmarkLastUpdateTime[] = "shopping_list_bookmark_last_update_time"; +// This preference is used to enable or disable the product specifications +// feature for enterprise policies. +inline constexpr char kProductSpecificationsEnabledPrefName[] = + "product_specifications_enabled"; + // This setting is primarily for enabling or disabling the shopping list feature // in enterprise settings. inline constexpr char kShoppingListEnabledPrefName[] = "shopping_list_enabled";
diff --git a/components/commerce/core/shopping_service.cc b/components/commerce/core/shopping_service.cc index 1907049..4fe32aee 100644 --- a/components/commerce/core/shopping_service.cc +++ b/components/commerce/core/shopping_service.cc
@@ -24,6 +24,7 @@ #include "components/commerce/core/commerce_feature_list.h" #include "components/commerce/core/commerce_utils.h" #include "components/commerce/core/discounts_storage.h" +#include "components/commerce/core/feature_utils.h" #include "components/commerce/core/metrics/metrics_utils.h" #include "components/commerce/core/metrics/scheduled_metrics_manager.h" #include "components/commerce/core/parcel/parcels_manager.h" @@ -146,7 +147,8 @@ if (identity_manager) { account_checker_ = base::WrapUnique(new AccountChecker( - pref_service, identity_manager, sync_service, url_loader_factory)); + country_on_startup_, locale_on_startup_, pref_service, identity_manager, + sync_service, url_loader_factory)); } if (identity_manager && account_checker_) { @@ -209,6 +211,10 @@ } } +AccountChecker* ShoppingService::GetAccountChecker() { + return account_checker_.get(); +} + void ShoppingService::WebWrapperCreated(WebWrapper* web) {} void ShoppingService::DidNavigatePrimaryMainFrame(WebWrapper* web) { @@ -1486,8 +1492,7 @@ } bool ShoppingService::IsShoppingListEligible() { - return IsShoppingListEligible(account_checker_.get(), pref_service_, - country_on_startup_, locale_on_startup_); + return commerce::IsShoppingListEligible(account_checker_.get()); } void ShoppingService::WaitForReady( @@ -1508,30 +1513,6 @@ AsWeakPtr(), sync_service_, std::move(callback))); } -bool ShoppingService::IsShoppingListEligible(AccountChecker* account_checker, - PrefService* prefs, - const std::string& country_code, - const std::string& locale) { - if (!commerce::IsRegionLockedFeatureEnabled( - kShoppingList, kShoppingListRegionLaunched, country_code, locale)) { - return false; - } - - if (!prefs || !IsShoppingListAllowedForEnterprise(prefs)) - return false; - - // Make sure the user allows subscriptions to be made and that we can fetch - // store data. - if (!account_checker || !account_checker->IsSignedIn() || - !account_checker->IsSyncingBookmarks() || - !account_checker->IsAnonymizedUrlDataCollectionEnabled() || - account_checker->IsSubjectToParentalControls()) { - return false; - } - - return true; -} - void ShoppingService::StartTrackingParcels( const std::vector<std::pair<ParcelIdentifier::Carrier, std::string>>& parcel_identifiers,
diff --git a/components/commerce/core/shopping_service.h b/components/commerce/core/shopping_service.h index 73ab7cfe..c1bce9f 100644 --- a/components/commerce/core/shopping_service.h +++ b/components/commerce/core/shopping_service.h
@@ -247,6 +247,9 @@ ShoppingService(const ShoppingService&) = delete; ShoppingService& operator=(const ShoppingService&) = delete; + // Gets an AccountChecker instance to aid in determining feature eligibility. + virtual AccountChecker* GetAccountChecker(); + // This API retrieves the product information for the provided |url| and // passes the payload back to the caller via |callback|. At minimum, this // API will wait for data from the backend but may provide a "partial" result @@ -551,15 +554,6 @@ static void MergeProductInfoData(ProductInfo* info, const base::Value::Dict& on_page_data_map); - // Check if the shopping list is eligible for use. This not only checks the - // feature flag, but whether the feature is allowed by enterprise policy and - // whether the user is signed in. The value returned here can change during - // runtime so it should not be used when deciding to build infrastructure. - static bool IsShoppingListEligible(AccountChecker* account_checker, - PrefService* prefs, - const std::string& country_code, - const std::string& locale); - void HandleOptGuideMerchantInfoResponse( const GURL& url, MerchantInfoCallback callback,
diff --git a/components/commerce/core/shopping_service_unittest.cc b/components/commerce/core/shopping_service_unittest.cc index b0ad1fb..c2546b7 100644 --- a/components/commerce/core/shopping_service_unittest.cc +++ b/components/commerce/core/shopping_service_unittest.cc
@@ -10,6 +10,7 @@ #include "components/bookmarks/browser/bookmark_model.h" #include "components/bookmarks/browser/bookmark_node.h" #include "components/commerce/core/commerce_feature_list.h" +#include "components/commerce/core/feature_utils.h" #include "components/commerce/core/mock_account_checker.h" #include "components/commerce/core/mock_discounts_storage.h" #include "components/commerce/core/pref_names.h" @@ -99,15 +100,6 @@ ShoppingServiceTestBase::SetUp(); } - // Expose the private feature check for testing. - static bool IsShoppingListEligible(AccountChecker* account_checker, - PrefService* prefs, - const std::string& country, - const std::string& locale) { - return ShoppingService::IsShoppingListEligible(account_checker, prefs, - country, locale); - } - bool ShouldEnableReplaceSyncPromosWithSignInPromos() const { return GetParam(); } @@ -643,13 +635,14 @@ SetShoppingListEnterprisePolicyPref(&prefs, true); MockAccountChecker checker; + checker.SetCountry(kEligibleCountry); + checker.SetLocale(kEligibleLocale); + checker.SetPrefs(&prefs); - ASSERT_TRUE(IsShoppingListEligible(&checker, &prefs, kEligibleCountry, - kEligibleLocale)); + ASSERT_TRUE(IsShoppingListEligible(&checker)); SetShoppingListEnterprisePolicyPref(&prefs, false); - ASSERT_FALSE(IsShoppingListEligible(&checker, &prefs, kEligibleCountry, - kEligibleLocale)); + ASSERT_FALSE(IsShoppingListEligible(&checker)); } TEST_P(ShoppingServiceTest, TestShoppingListEligible_FeatureFlagOff) { @@ -661,9 +654,11 @@ SetShoppingListEnterprisePolicyPref(&prefs, true); MockAccountChecker checker; + checker.SetCountry(kEligibleCountry); + checker.SetLocale(kEligibleLocale); + checker.SetPrefs(&prefs); - ASSERT_FALSE(IsShoppingListEligible(&checker, &prefs, kEligibleCountry, - kEligibleLocale)); + ASSERT_FALSE(IsShoppingListEligible(&checker)); } TEST_P(ShoppingServiceTest, TestShoppingListEligible_MSBB) { @@ -675,14 +670,15 @@ SetShoppingListEnterprisePolicyPref(&prefs, true); MockAccountChecker checker; + checker.SetCountry(kEligibleCountry); + checker.SetLocale(kEligibleLocale); + checker.SetPrefs(&prefs); - ASSERT_TRUE(IsShoppingListEligible(&checker, &prefs, kEligibleCountry, - kEligibleLocale)); + ASSERT_TRUE(IsShoppingListEligible(&checker)); checker.SetAnonymizedUrlDataCollectionEnabled(false); - ASSERT_FALSE(IsShoppingListEligible(&checker, &prefs, kEligibleCountry, - kEligibleLocale)); + ASSERT_FALSE(IsShoppingListEligible(&checker)); } TEST_P(ShoppingServiceTest, TestShoppingListEligible_SignIn) { @@ -694,14 +690,15 @@ SetShoppingListEnterprisePolicyPref(&prefs, true); MockAccountChecker checker; + checker.SetCountry(kEligibleCountry); + checker.SetLocale(kEligibleLocale); + checker.SetPrefs(&prefs); - ASSERT_TRUE(IsShoppingListEligible(&checker, &prefs, kEligibleCountry, - kEligibleLocale)); + ASSERT_TRUE(IsShoppingListEligible(&checker)); checker.SetSignedIn(false); - ASSERT_FALSE(IsShoppingListEligible(&checker, &prefs, kEligibleCountry, - kEligibleLocale)); + ASSERT_FALSE(IsShoppingListEligible(&checker)); } TEST_P(ShoppingServiceTest, TestShoppingListEligible_ChildAccount) { @@ -713,14 +710,15 @@ SetShoppingListEnterprisePolicyPref(&prefs, true); MockAccountChecker checker; + checker.SetCountry(kEligibleCountry); + checker.SetLocale(kEligibleLocale); + checker.SetPrefs(&prefs); - ASSERT_TRUE(IsShoppingListEligible(&checker, &prefs, kEligibleCountry, - kEligibleLocale)); + ASSERT_TRUE(IsShoppingListEligible(&checker)); checker.SetIsSubjectToParentalControls(true); - ASSERT_FALSE(IsShoppingListEligible(&checker, &prefs, kEligibleCountry, - kEligibleLocale)); + ASSERT_FALSE(IsShoppingListEligible(&checker)); } TEST_P(ShoppingServiceTest, TestShoppingListEligible_SyncState) { @@ -732,14 +730,15 @@ SetShoppingListEnterprisePolicyPref(&prefs, true); MockAccountChecker checker; + checker.SetCountry(kEligibleCountry); + checker.SetLocale(kEligibleLocale); + checker.SetPrefs(&prefs); - ASSERT_TRUE(IsShoppingListEligible(&checker, &prefs, kEligibleCountry, - kEligibleLocale)); + ASSERT_TRUE(IsShoppingListEligible(&checker)); checker.SetSyncingBookmarks(false); - ASSERT_FALSE(IsShoppingListEligible(&checker, &prefs, kEligibleCountry, - kEligibleLocale)); + ASSERT_FALSE(IsShoppingListEligible(&checker)); } TEST_P(ShoppingServiceTest, TestShoppingListEligible_CountryAndLocale) { @@ -751,13 +750,18 @@ SetShoppingListEnterprisePolicyPref(&prefs, true); MockAccountChecker checker; + checker.SetCountry(kEligibleCountry); + checker.SetLocale(kEligibleLocale); + checker.SetPrefs(&prefs); - ASSERT_TRUE(IsShoppingListEligible(&checker, &prefs, kEligibleCountry, - kEligibleLocale)); + ASSERT_TRUE(IsShoppingListEligible(&checker)); + + checker.SetCountry("ZZ"); + checker.SetLocale("zz-zz"); // This should continue to work since we can assume, for the sake of the test, // that the experiment config includes the ZZ country and zz-zz locale. - ASSERT_TRUE(IsShoppingListEligible(&checker, &prefs, "ZZ", "zz-zz")); + ASSERT_TRUE(IsShoppingListEligible(&checker)); } TEST_P(ShoppingServiceTest, @@ -770,13 +774,18 @@ SetShoppingListEnterprisePolicyPref(&prefs, true); MockAccountChecker checker; + checker.SetCountry(kEligibleCountry); + checker.SetLocale(kEligibleLocale); + checker.SetPrefs(&prefs); - ASSERT_TRUE(IsShoppingListEligible(&checker, &prefs, kEligibleCountry, - kEligibleLocale)); + ASSERT_TRUE(IsShoppingListEligible(&checker)); + + checker.SetCountry("ZZ"); + checker.SetLocale("zz-zz"); // Same as the previous test, this should still work since, presumably, the // experiment config for "ShoppingList" includes these. - ASSERT_TRUE(IsShoppingListEligible(&checker, &prefs, "ZZ", "zz-zz")); + ASSERT_TRUE(IsShoppingListEligible(&checker)); } TEST_P(ShoppingServiceTest, TestShoppingListEligible_CountryAndLocale_NoFlags) { @@ -789,10 +798,12 @@ MockAccountChecker checker; - ASSERT_FALSE(IsShoppingListEligible(&checker, &prefs, kEligibleCountry, - kEligibleLocale)); + ASSERT_FALSE(IsShoppingListEligible(&checker)); - ASSERT_FALSE(IsShoppingListEligible(&checker, &prefs, "ZZ", "zz-zz")); + checker.SetCountry("ZZ"); + checker.SetLocale("zz-zz"); + + ASSERT_FALSE(IsShoppingListEligible(&checker)); } TEST_P(ShoppingServiceTest, @@ -805,14 +816,19 @@ SetShoppingListEnterprisePolicyPref(&prefs, true); MockAccountChecker checker; + checker.SetCountry(kEligibleCountry); + checker.SetLocale(kEligibleLocale); + checker.SetPrefs(&prefs); - ASSERT_TRUE(IsShoppingListEligible(&checker, &prefs, kEligibleCountry, - kEligibleLocale)); + ASSERT_TRUE(IsShoppingListEligible(&checker)); + + checker.SetCountry("ZZ"); + checker.SetLocale("zz-zz"); // If we only have the region flag enabled, we should be restricted to // specific countries and locales. The fake country and locale below should // be blocked. - ASSERT_FALSE(IsShoppingListEligible(&checker, &prefs, "ZZ", "zz-zz")); + ASSERT_FALSE(IsShoppingListEligible(&checker)); } class ShoppingServiceReadyTest : public ShoppingServiceTest {
diff --git a/components/exo/extended_drag_source.cc b/components/exo/extended_drag_source.cc index ddf02c2..19450c0 100644 --- a/components/exo/extended_drag_source.cc +++ b/components/exo/extended_drag_source.cc
@@ -122,6 +122,9 @@ void OnSurfaceDestroying(Surface* surface) override { if (surface_ == surface) { surface_->RemoveSurfaceObserver(this); + if (surface_->window()->HasObserver(this)) { + surface_->window()->RemoveObserver(this); + } surface_ = nullptr; } }
diff --git a/components/exo/wayland/xdg_shell.cc b/components/exo/wayland/xdg_shell.cc index 26220f24..b3e1a56 100644 --- a/components/exo/wayland/xdg_shell.cc +++ b/components/exo/wayland/xdg_shell.cc
@@ -202,6 +202,7 @@ // Overridden from aura::WindowObserver: void OnWindowDestroying(aura::Window* window) override { + window->RemoveObserver(this); shell_surface_data_ = nullptr; } @@ -565,6 +566,7 @@ // Overridden from aura::WindowObserver: void OnWindowDestroying(aura::Window* window) override { + window->RemoveObserver(this); shell_surface_data_ = nullptr; }
diff --git a/components/favicon/core/favicon_database.cc b/components/favicon/core/favicon_database.cc index b1297ac..dd49f23 100644 --- a/components/favicon/core/favicon_database.cc +++ b/components/favicon/core/favicon_database.cc
@@ -105,13 +105,6 @@ const int kCompatibleVersionNumber = 8; const int kDeprecatedVersionNumber = 6; // and earlier. -// When enabled, prefer to use the new recovery module to recover the -// `FaviconDatabase` database. See https://crbug.com/1385500 for details. -// This is a kill switch and is not intended to be used in a field trial. -BASE_FEATURE(kFaviconDatabaseUseBuiltInRecoveryIfSupported, - "FaviconDatabaseUseBuiltInRecoveryIfSupported", - base::FEATURE_ENABLED_BY_DEFAULT); - void FillIconMapping(const GURL& page_url, sql::Statement& statement, IconMapping* icon_mapping) { @@ -204,8 +197,7 @@ // Attempt to recover a corrupt database, if it is eligible to be recovered. if (sql::BuiltInRecovery::RecoverIfPossible( db, extended_error, - sql::BuiltInRecovery::Strategy::kRecoverWithMetaVersionOrRaze, - &kFaviconDatabaseUseBuiltInRecoveryIfSupported)) { + sql::BuiltInRecovery::Strategy::kRecoverWithMetaVersionOrRaze)) { // Recovery was attempted. The database handle has been poisoned and the // error callback has been reset.
diff --git a/components/favicon/core/favicon_database.h b/components/favicon/core/favicon_database.h index 41480c9..d6dfb86 100644 --- a/components/favicon/core/favicon_database.h +++ b/components/favicon/core/favicon_database.h
@@ -33,8 +33,6 @@ // All earlier updates are ignored. static const int kFaviconUpdateLastRequestedAfterDays = 10; -BASE_DECLARE_FEATURE(kFaviconDatabaseUseBuiltInRecoveryIfSupported); - // This database interface is owned by the history backend and runs on the // history thread. class FaviconDatabase {
diff --git a/components/global_media_controls/public/media_session_notification_item.cc b/components/global_media_controls/public/media_session_notification_item.cc index 4021abe..e934209 100644 --- a/components/global_media_controls/public/media_session_notification_item.cc +++ b/components/global_media_controls/public/media_session_notification_item.cc
@@ -8,6 +8,7 @@ #include "base/functional/bind.h" #include "base/i18n/rtl.h" #include "base/metrics/histogram_macros.h" +#include "base/ranges/algorithm.h" #include "base/time/time.h" #include "components/global_media_controls/public/constants.h" #include "components/media_message_center/media_notification_util.h" @@ -180,6 +181,19 @@ MaybeUnfreeze(); } +void MediaSessionNotificationItem::MediaControllerChapterImageChanged( + int chapter_index, + const SkBitmap& bitmap) { + chapter_artwork_[chapter_index] = gfx::ImageSkia::CreateFrom1xBitmap(bitmap); + + if (view_ && !frozen_) { + view_->UpdateWithChapterArtwork(chapter_index, + chapter_artwork_[chapter_index]); + } else if (frozen_with_chapter_artwork_[chapter_index]) { + MaybeUnfreeze(); + } +} + void MediaSessionNotificationItem::SetView( media_message_center::MediaNotificationView* view) { DCHECK(view_ || view); @@ -193,6 +207,9 @@ view_->UpdateWithMediaPosition(*session_position_); if (session_artwork_.has_value()) view_->UpdateWithMediaArtwork(*session_artwork_); + for (auto& item : chapter_artwork_) { + view_->UpdateWithChapterArtwork(item.first, item.second); + } if (session_favicon_.has_value()) view_->UpdateWithFavicon(*session_favicon_); } else { @@ -300,6 +317,9 @@ frozen_ = true; frozen_with_actions_ = HasActions(); frozen_with_artwork_ = HasArtwork(); + for (auto& item : chapter_artwork_) { + frozen_with_chapter_artwork_[item.first] = HasChapterArtwork(item.first); + } freeze_timer_.Start( FROM_HERE, kFreezeTimerDelay, @@ -390,8 +410,9 @@ } void MediaSessionNotificationItem::MaybeUnfreeze() { - if (!frozen_ && !frozen_with_artwork_) + if (!frozen_ && !frozen_with_artwork_ && !FrozenWithChapterArtwork()) { return; + } if (waiting_for_actions_ && !HasActions()) return; @@ -417,6 +438,13 @@ return; } + for (auto& item : chapter_artwork_) { + if (frozen_with_chapter_artwork_[item.first] && + !HasChapterArtwork(item.first)) { + return; + } + } + UnfreezeArtwork(); } @@ -424,8 +452,9 @@ frozen_ = false; waiting_for_actions_ = false; frozen_with_actions_ = false; - if (!frozen_with_artwork_) + if (!frozen_with_artwork_ && !FrozenWithChapterArtwork()) { freeze_timer_.Stop(); + } // When we unfreeze, we want to fully update |view_| with any changes that // we've avoided sending during the freeze. @@ -445,12 +474,18 @@ // would be slow and unresponsive when trying to skip ahead multiple tracks. void MediaSessionNotificationItem::UnfreezeArtwork() { frozen_with_artwork_ = false; + for (auto& item : chapter_artwork_) { + frozen_with_chapter_artwork_[item.first] = false; + } freeze_timer_.Stop(); if (view_) { if (session_artwork_.has_value()) view_->UpdateWithMediaArtwork(*session_artwork_); if (session_favicon_.has_value()) view_->UpdateWithFavicon(*session_favicon_); + for (auto& item : chapter_artwork_) { + view_->UpdateWithChapterArtwork(item.first, item.second); + } } } @@ -462,8 +497,13 @@ return session_artwork_.has_value() && !session_artwork_->isNull(); } +bool MediaSessionNotificationItem::HasChapterArtwork(int index) const { + auto it = chapter_artwork_.find(index); + return it != chapter_artwork_.end() && !it->second.isNull(); +} + void MediaSessionNotificationItem::OnFreezeTimerFired() { - DCHECK(frozen_ || frozen_with_artwork_); + DCHECK(frozen_ || frozen_with_artwork_ || FrozenWithChapterArtwork()); // If we've just been waiting for actions or artwork, stop waiting and just // show what we have. @@ -471,8 +511,9 @@ if (frozen_) UnfreezeNonArtwork(); - if (frozen_with_artwork_) + if (frozen_with_artwork_ || FrozenWithChapterArtwork()) { UnfreezeArtwork(); + } return; } @@ -510,4 +551,10 @@ : nullptr); } +bool MediaSessionNotificationItem::FrozenWithChapterArtwork() { + auto it = + base::ranges::find_if(frozen_with_chapter_artwork_, + [](const auto& it) { return it.second == true; }); + return it != frozen_with_chapter_artwork_.end(); +} } // namespace global_media_controls
diff --git a/components/global_media_controls/public/media_session_notification_item.h b/components/global_media_controls/public/media_session_notification_item.h index 88f92dc..98c0618 100644 --- a/components/global_media_controls/public/media_session_notification_item.h +++ b/components/global_media_controls/public/media_session_notification_item.h
@@ -100,7 +100,7 @@ media_session::mojom::MediaSessionImageType type, const SkBitmap& bitmap) override; void MediaControllerChapterImageChanged(int chapter_index, - const SkBitmap& bitmap) override {} + const SkBitmap& bitmap) override; // media_message_center::MediaNotificationItem: void SetView(media_message_center::MediaNotificationView* view) override; @@ -174,12 +174,19 @@ bool HasArtwork() const; + // Returns true if there's an image at the chapter `index`. + bool HasChapterArtwork(int index) const; + void OnFreezeTimerFired(); void MaybeHideOrShowNotification(); void UpdateViewCommon(); + // Returns true if we're currently frozen and the frozen view contains + // non-null artwork at any chapter index. + bool FrozenWithChapterArtwork(); + const raw_ptr<Delegate> delegate_; bool is_bound_ = true; @@ -221,6 +228,10 @@ std::optional<gfx::ImageSkia> session_favicon_; + // This map carries the index as the key and the image at this chapter index + // as the value. + base::flat_map<int, gfx::ImageSkia> chapter_artwork_; + // True if the metadata needs to be updated on |view_|. Used to prevent // updating |view_|'s metadata twice on a single change. bool view_needs_metadata_update_ = false; @@ -241,6 +252,10 @@ // artwork. bool frozen_with_artwork_ = false; + // The value is true if we're currently frozen and the frozen view contains + // non-null artwork at the chapter index. + base::flat_map<int, bool> frozen_with_chapter_artwork_; + // The timer that will notify the controller to destroy this item after it // has been frozen for a certain period of time. base::OneShotTimer freeze_timer_;
diff --git a/components/global_media_controls/public/media_session_notification_item_unittest.cc b/components/global_media_controls/public/media_session_notification_item_unittest.cc index edcebbd..0282237e 100644 --- a/components/global_media_controls/public/media_session_notification_item_unittest.cc +++ b/components/global_media_controls/public/media_session_notification_item_unittest.cc
@@ -84,6 +84,15 @@ metadata.artist = u"artist2"; metadata.album = u"album"; + std::vector<media_session::ChapterInformation> expected_chapters; + media_session::MediaImage test_image_1; + test_image_1.src = GURL("https://www.google.com"); + media_session::ChapterInformation test_chapter_1( + /*title=*/u"chapter1", /*start_time=*/base::Seconds(10), + /*artwork=*/{test_image_1}); + expected_chapters.push_back(test_chapter_1); + metadata.chapters = expected_chapters; + EXPECT_CALL(view(), UpdateWithMediaMetadata(_)).Times(0); item().Freeze(base::DoNothing()); item().MediaSessionMetadataChanged(metadata); @@ -125,6 +134,16 @@ media_session::mojom::MediaSessionImageType::kArtwork, image); } +TEST_F(MediaSessionNotificationItemTest, Freezing_DoNotUpdateChapterImage) { + SkBitmap image; + image.allocN32Pixels(10, 10); + image.eraseColor(SK_ColorMAGENTA); + + EXPECT_CALL(view(), UpdateWithChapterArtwork(_, _)).Times(0); + item().Freeze(base::DoNothing()); + item().MediaControllerChapterImageChanged(0, image); +} + TEST_F(MediaSessionNotificationItemTest, Freezing_DoNotUpdatePlaybackState) { EXPECT_CALL(view(), UpdateWithMediaSessionInfo(_)).Times(0); @@ -228,17 +247,27 @@ media_session::mojom::MediaSessionImageType::kArtwork, initial_image); testing::Mock::VerifyAndClearExpectations(&view()); + // Set an chapter image before freezing. + EXPECT_CALL(view(), UpdateWithChapterArtwork(_, _)); + SkBitmap initial_chapter_image; + initial_chapter_image.allocN32Pixels(10, 10); + initial_chapter_image.eraseColor(SK_ColorMAGENTA); + item().MediaControllerChapterImageChanged(0, initial_chapter_image); + testing::Mock::VerifyAndClearExpectations(&view()); + // Freeze the item and clear the metadata. base::MockOnceClosure unfrozen_callback; EXPECT_CALL(unfrozen_callback, Run).Times(0); EXPECT_CALL(view(), UpdateWithMediaSessionInfo(_)).Times(0); EXPECT_CALL(view(), UpdateWithMediaMetadata(_)).Times(0); EXPECT_CALL(view(), UpdateWithMediaArtwork(_)).Times(0); + EXPECT_CALL(view(), UpdateWithChapterArtwork(_, _)).Times(0); item().Freeze(unfrozen_callback.Get()); item().MediaSessionInfoChanged(nullptr); item().MediaSessionMetadataChanged(std::nullopt); item().MediaControllerImageChanged( media_session::mojom::MediaSessionImageType::kArtwork, SkBitmap()); + item().MediaControllerChapterImageChanged(0, SkBitmap()); // The item should be frozen and the view should contain the old data. EXPECT_TRUE(item().frozen()); @@ -264,6 +293,7 @@ EXPECT_CALL(view(), UpdateWithMediaSessionInfo(_)); EXPECT_CALL(view(), UpdateWithMediaMetadata(_)); EXPECT_CALL(view(), UpdateWithMediaArtwork(_)).Times(0); + EXPECT_CALL(view(), UpdateWithChapterArtwork(_, _)).Times(0); media_session::MediaMetadata metadata; metadata.title = u"title2"; metadata.artist = u"artist2";
diff --git a/components/global_media_controls/public/views/media_item_ui_detailed_view.cc b/components/global_media_controls/public/views/media_item_ui_detailed_view.cc index 1d85e3a..3aeada5 100644 --- a/components/global_media_controls/public/views/media_item_ui_detailed_view.cc +++ b/components/global_media_controls/public/views/media_item_ui_detailed_view.cc
@@ -483,6 +483,13 @@ SchedulePaint(); } +void MediaItemUIDetailedView::UpdateWithChapterArtwork( + int index, + const gfx::ImageSkia& image) { + // TODO(b/325142008) Update the chapter list view, which is not implemented + // yet. +} + void MediaItemUIDetailedView::UpdateDeviceSelectorAvailability( bool has_devices) { CHECK(start_casting_button_);
diff --git a/components/global_media_controls/public/views/media_item_ui_detailed_view.h b/components/global_media_controls/public/views/media_item_ui_detailed_view.h index 1ea912c..16e3ba0 100644 --- a/components/global_media_controls/public/views/media_item_ui_detailed_view.h +++ b/components/global_media_controls/public/views/media_item_ui_detailed_view.h
@@ -95,6 +95,8 @@ void UpdateWithMediaPosition( const media_session::MediaPosition& position) override; void UpdateWithMediaArtwork(const gfx::ImageSkia& image) override; + void UpdateWithChapterArtwork(int index, + const gfx::ImageSkia& image) override; void UpdateWithFavicon(const gfx::ImageSkia& icon) override {} void UpdateWithVectorIcon(const gfx::VectorIcon* vector_icon) override {} void UpdateWithMuteStatus(bool mute) override {}
diff --git a/components/history/core/browser/features.cc b/components/history/core/browser/features.cc index e61f66c..77990c1 100644 --- a/components/history/core/browser/features.cc +++ b/components/history/core/browser/features.cc
@@ -84,13 +84,6 @@ "SyncSegmentsData", base::FEATURE_ENABLED_BY_DEFAULT); -// When enabled, prefer to use the new recovery module to recover the -// `TopSitesDatabase` database. See https://crbug.com/1385500 for details. -// This is a kill switch and is not intended to be used in a field trial. -BASE_FEATURE(kTopSitesDatabaseUseBuiltInRecoveryIfSupported, - "TopSitesDatabaseUseBuiltInRecoveryIfSupported", - base::FEATURE_ENABLED_BY_DEFAULT); - // The maximum number of New Tab Page displays to show with synced segments // data. const base::FeatureParam<int> kMaxNumNewTabPageDisplays(
diff --git a/components/history/core/browser/features.h b/components/history/core/browser/features.h index 7b3b151..08c5ac1 100644 --- a/components/history/core/browser/features.h +++ b/components/history/core/browser/features.h
@@ -29,9 +29,6 @@ // is enabled; do not check `kSyncSegmentsData` directly. BASE_DECLARE_FEATURE(kSyncSegmentsData); -// Kill switch for use of the new SQL recovery module in `TopSitesDatabase`. -BASE_DECLARE_FEATURE(kTopSitesDatabaseUseBuiltInRecoveryIfSupported); - // Returns true when both full history sync and synced segments data are // enabled. bool IsSyncSegmentsDataEnabled();
diff --git a/components/history/core/browser/top_sites_database.cc b/components/history/core/browser/top_sites_database.cc index 433e8f4..963d4742 100644 --- a/components/history/core/browser/top_sites_database.cc +++ b/components/history/core/browser/top_sites_database.cc
@@ -105,49 +105,6 @@ } } -// Recover the database to the extent possible, then fixup any broken -// constraints. -void RecoverAndFixup(sql::Database* db, const base::FilePath& db_path) { - // NOTE(shess): If the version changes, review this code. - static_assert(kVersionNumber == 5); - - std::unique_ptr<sql::Recovery> recovery = - sql::Recovery::BeginRecoverDatabase(db, db_path); - if (!recovery) { - return; - } - - // If the [meta] table does not exist, or the [version] key cannot be found, - // then the schema is indeterminate. The only plausible approach would be to - // validate that the schema contains all of the tables and indices and columns - // expected, but that complexity may not be warranted, this case has only been - // seen for a few thousand database files. - int version = 0; - if (!recovery->SetupMeta() || !recovery->GetMetaVersionNumber(&version)) { - sql::Recovery::Unrecoverable(std::move(recovery)); - return; - } - - // In this case the next open will clear the database anyhow. - if (version <= kDeprecatedVersionNumber) { - sql::Recovery::Unrecoverable(std::move(recovery)); - return; - } - - // TODO(shess): Consider marking corrupt databases from the future - // Unrecoverable(), since this histogram value has never been seen. OTOH, - // this may be too risky, because if future code was correlated with - // corruption then rollback would be a sensible response. - if (version > kVersionNumber) { - sql::Recovery::Rollback(std::move(recovery)); - return; - } - - FixTopSitesTable(*recovery->db()); - - std::ignore = sql::Recovery::Recovered(std::move(recovery)); -} - } // namespace void TopSitesDatabase::DatabaseErrorCallback(const base::FilePath& db_path, @@ -156,21 +113,9 @@ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK(db_); - // TODO(https://crbug.com/1513464): Simplify this code as soon as we're - // confident that we can remove sql::Recovery. - if (base::FeatureList::IsEnabled( - kTopSitesDatabaseUseBuiltInRecoveryIfSupported) && - sql::BuiltInRecovery::ShouldAttemptRecovery(db_.get(), extended_error)) { - bool recovery_was_attempted = sql::BuiltInRecovery::RecoverIfPossible( - db_.get(), extended_error, - sql::BuiltInRecovery::Strategy::kRecoverWithMetaVersionOrRaze, - &kTopSitesDatabaseUseBuiltInRecoveryIfSupported); - if (!recovery_was_attempted) { - LOG(ERROR) - << "Recovery should have been attempted if the checks above passed."; - base::debug::DumpWithoutCrashing(); - return; - } + if (sql::BuiltInRecovery::RecoverIfPossible( + db_.get(), extended_error, + sql::BuiltInRecovery::Strategy::kRecoverWithMetaVersionOrRaze)) { // Recovery was attempted. The database handle has been poisoned and the // error callback has been reset. @@ -189,31 +134,10 @@ // `FixTopSitesTable()` every time the database is opened, but in most cases // that would be a (possibly expensive) no-op. needs_fixing_up_ = true; - - // Signal the test-expectation framework that the error was handled. - std::ignore = sql::Database::IsExpectedSqliteError(extended_error); - return; } - if (sql::Recovery::ShouldRecover(extended_error)) { - // Prevent reentrant calls. - db_->reset_error_callback(); - - // After this call, the `db` handle is poisoned so that future calls will - // return errors until the handle is re-opened. - RecoverAndFixup(db_.get(), db_path); - - // The DLOG(FATAL) below is intended to draw immediate attention to errors - // in newly-written code. Database corruption is generally a result of OS - // or hardware issues, not coding errors at the client level, so displaying - // the error would probably lead to confusion. The ignored call signals the - // test-expectation framework that the error was handled. - std::ignore = sql::Database::IsExpectedSqliteError(extended_error); - return; - } - - if (!sql::Database::IsExpectedSqliteError(extended_error)) - DLOG(FATAL) << db_->GetErrorMessage(); + // Signal the test-expectation framework that the error was handled. + std::ignore = sql::Database::IsExpectedSqliteError(extended_error); } // static @@ -333,9 +257,6 @@ // Attempt to fix up the table if recovery was attempted when opening. if (needs_fixing_up_) { - CHECK(base::FeatureList::IsEnabled( - kTopSitesDatabaseUseBuiltInRecoveryIfSupported)); - FixTopSitesTable(*db_); needs_fixing_up_ = false; }
diff --git a/components/history/core/browser/top_sites_database_unittest.cc b/components/history/core/browser/top_sites_database_unittest.cc index 4f411b85..7b86817fc 100644 --- a/components/history/core/browser/top_sites_database_unittest.cc +++ b/components/history/core/browser/top_sites_database_unittest.cc
@@ -78,20 +78,6 @@ base::FilePath file_name_; }; -// Tests both the legacy `sql::Recovery` interface and the newer -// `sql::BuiltInRecovery` interface, if it's supported. -class TopSitesDatabaseRecoveryTest : public TopSitesDatabaseTest, - public testing::WithParamInterface<bool> { - public: - TopSitesDatabaseRecoveryTest() { - scoped_feature_list_.InitWithFeatureState( - kTopSitesDatabaseUseBuiltInRecoveryIfSupported, GetParam()); - } - - private: - base::test::ScopedFeatureList scoped_feature_list_; -}; - // Version 1 is deprecated, the resulting schema should be current, // with no data. TEST_F(TopSitesDatabaseTest, Version1) { @@ -171,7 +157,7 @@ // Version 1 is deprecated, the resulting schema should be current, with no // data. -TEST_P(TopSitesDatabaseRecoveryTest, Recovery1) { +TEST_F(TopSitesDatabaseTest, Recovery1) { // Create an example database. ASSERT_TRUE(CreateDatabaseFromSQL(file_name_, "TopSites.v1.sql")); @@ -204,7 +190,7 @@ // Version 2 is deprecated, the resulting schema should be current, with no // data. -TEST_P(TopSitesDatabaseRecoveryTest, Recovery2) { +TEST_F(TopSitesDatabaseTest, Recovery2) { // Create an example database. ASSERT_TRUE(CreateDatabaseFromSQL(file_name_, "TopSites.v2.sql")); @@ -235,7 +221,7 @@ VerifyDatabaseEmpty(db.db_for_testing()); } -TEST_P(TopSitesDatabaseRecoveryTest, Recovery4_CorruptHeader) { +TEST_F(TopSitesDatabaseTest, Recovery4_CorruptHeader) { // Create an example database. EXPECT_TRUE(CreateDatabaseFromSQL(file_name_, "TopSites.v4.sql")); @@ -277,7 +263,7 @@ } } -TEST_P(TopSitesDatabaseRecoveryTest, Recovery5_CorruptIndex) { +TEST_F(TopSitesDatabaseTest, Recovery5_CorruptIndex) { // Create an example database. ASSERT_TRUE(CreateDatabaseFromSQL(file_name_, "TopSites.v5.sql")); @@ -336,7 +322,7 @@ EXPECT_EQ(kUrl2, urls[2].url); // [2] because of url_rank. } -TEST_P(TopSitesDatabaseRecoveryTest, Recovery5_CorruptIndexAndLostRow) { +TEST_F(TopSitesDatabaseTest, Recovery5_CorruptIndexAndLostRow) { // Create an example database. ASSERT_TRUE(CreateDatabaseFromSQL(file_name_, "TopSites.v5.sql")); @@ -550,6 +536,4 @@ } } -INSTANTIATE_TEST_SUITE_P(All, TopSitesDatabaseRecoveryTest, testing::Bool()); - } // namespace history
diff --git a/components/media_device_salt/media_device_salt_database.cc b/components/media_device_salt/media_device_salt_database.cc index 5b52976..5f5058e2 100644 --- a/components/media_device_salt/media_device_salt_database.cc +++ b/components/media_device_salt/media_device_salt_database.cc
@@ -18,10 +18,6 @@ namespace media_device_salt { -BASE_FEATURE(kMediaDeviceSaltDatabaseUseBuiltInRecoveryIfSupported, - "MediaDeviceSaltDatabaseUseBuiltInRecoveryIfSupported", - base::FEATURE_ENABLED_BY_DEFAULT); - namespace { // The current version of the database schema. constexpr int kCurrentVersion = 1; @@ -226,8 +222,7 @@ sql::UmaHistogramSqliteResult("Media.MediaDevices.SaltDatabaseErrors", error); std::ignore = sql::BuiltInRecovery::RecoverIfPossible( &db_, error, - sql::BuiltInRecovery::Strategy::kRecoverWithMetaVersionOrRaze, - &kMediaDeviceSaltDatabaseUseBuiltInRecoveryIfSupported); + sql::BuiltInRecovery::Strategy::kRecoverWithMetaVersionOrRaze); } } // namespace media_device_salt
diff --git a/components/media_message_center/media_notification_view.h b/components/media_message_center/media_notification_view.h index 956d1a10..e630d3e 100644 --- a/components/media_message_center/media_notification_view.h +++ b/components/media_message_center/media_notification_view.h
@@ -46,6 +46,8 @@ virtual void UpdateWithMediaPosition( const media_session::MediaPosition& position) = 0; virtual void UpdateWithMediaArtwork(const gfx::ImageSkia& image) = 0; + virtual void UpdateWithChapterArtwork(int index, + const gfx::ImageSkia& image) = 0; // Updates the background color to match that of the favicon. virtual void UpdateWithFavicon(const gfx::ImageSkia& icon) = 0; // Sets the icon to be displayed in the notification's header section.
diff --git a/components/media_message_center/media_notification_view_impl.h b/components/media_message_center/media_notification_view_impl.h index 07f5b6d..9102267a 100644 --- a/components/media_message_center/media_notification_view_impl.h +++ b/components/media_message_center/media_notification_view_impl.h
@@ -79,6 +79,11 @@ void UpdateWithMediaPosition( const media_session::MediaPosition& position) override {} void UpdateWithMediaArtwork(const gfx::ImageSkia& image) override; + // This view is used before `media::kGlobalMediaControlsCrOSUpdatedUI` is + // enabled in the `MediaItemUIView`. So we don't need to implement this method + // since there's no chapter images in this view. + void UpdateWithChapterArtwork(int index, + const gfx::ImageSkia& image) override {} void UpdateWithFavicon(const gfx::ImageSkia& icon) override; void UpdateWithVectorIcon(const gfx::VectorIcon* vector_icon) override; void UpdateWithMuteStatus(bool mute) override {}
diff --git a/components/media_message_center/mock_media_notification_view.h b/components/media_message_center/mock_media_notification_view.h index b2888672..72b2e41e 100644 --- a/components/media_message_center/mock_media_notification_view.h +++ b/components/media_message_center/mock_media_notification_view.h
@@ -32,6 +32,8 @@ MOCK_METHOD1(UpdateWithMediaPosition, void(const media_session::MediaPosition&)); MOCK_METHOD1(UpdateWithMediaArtwork, void(const gfx::ImageSkia&)); + MOCK_METHOD2(UpdateWithChapterArtwork, + void(int index, const gfx::ImageSkia& image)); MOCK_METHOD1(UpdateWithFavicon, void(const gfx::ImageSkia&)); MOCK_METHOD1(UpdateWithVectorIcon, void(const gfx::VectorIcon* vector_icon)); MOCK_METHOD1(UpdateWithMuteStatus, void(bool));
diff --git a/components/omnibox/browser/shortcuts_database.cc b/components/omnibox/browser/shortcuts_database.cc index 38bcfe6..6106c54 100644 --- a/components/omnibox/browser/shortcuts_database.cc +++ b/components/omnibox/browser/shortcuts_database.cc
@@ -65,8 +65,7 @@ sql::Statement* stmt) { // Attempt to recover a corrupt database, if it is eligible to be recovered. if (sql::BuiltInRecovery::RecoverIfPossible( - db, extended_error, sql::BuiltInRecovery::Strategy::kRecoverOrRaze, - &omnibox::kShortcutsDatabaseUseBuiltInRecoveryIfSupported)) { + db, extended_error, sql::BuiltInRecovery::Strategy::kRecoverOrRaze)) { // Recovery was attempted. The database handle has been poisoned and the // error callback has been reset.
diff --git a/components/omnibox/common/omnibox_features.cc b/components/omnibox/common/omnibox_features.cc index cf34b09..95c1701 100644 --- a/components/omnibox/common/omnibox_features.cc +++ b/components/omnibox/common/omnibox_features.cc
@@ -542,13 +542,6 @@ "StarterPackExpansion", base::FEATURE_DISABLED_BY_DEFAULT); -// When enabled, prefer to use the new recovery module to recover the -// `ShortcutsDatabase` database. See https://crbug.com/1385500 for details. -// This is a kill switch and is not intended to be used in a field trial. -BASE_FEATURE(kShortcutsDatabaseUseBuiltInRecoveryIfSupported, - "ShortcutsDatabaseUseBuiltInRecoveryIfSupported", - base::FEATURE_ENABLED_BY_DEFAULT); - // If enabled, |SearchProvider| will not function in Zero Suggest. BASE_FEATURE(kAblateSearchProviderWarmup, "AblateSearchProviderWarmup",
diff --git a/components/omnibox/common/omnibox_features.h b/components/omnibox/common/omnibox_features.h index 2c2317e9..08e15f7 100644 --- a/components/omnibox/common/omnibox_features.h +++ b/components/omnibox/common/omnibox_features.h
@@ -159,9 +159,6 @@ BASE_DECLARE_FEATURE(kPolicyIndicationForManagedDefaultSearch); BASE_DECLARE_FEATURE(kStarterPackExpansion); -// Kill switch for use of the new SQL recovery module in `ShortcutsDatabase`. -BASE_DECLARE_FEATURE(kShortcutsDatabaseUseBuiltInRecoveryIfSupported); - BASE_DECLARE_FEATURE(kAblateSearchProviderWarmup); BASE_DECLARE_FEATURE(kOmniboxShortcutsAndroid);
diff --git a/components/optimization_guide/features.gni b/components/optimization_guide/features.gni index 516ff8f..c95ae20 100644 --- a/components/optimization_guide/features.gni +++ b/components/optimization_guide/features.gni
@@ -29,8 +29,7 @@ # Mac: //chrome/installer/mac/signing/parts.py # Windows: //chrome/installer/mini_installer/chrome.release and internal archive files if (is_fuchsia || is_ios || is_android) { - # The library this pulls in depends on open-source LevelDB which is not supported for Fuchsia. - # iOS and Android should work but is not included in the set we release for, so we do + # Fuchsia, iOS, and Android should work but is not included in the set we release for, so we do # not needlessly increase the binary size. build_with_internal_optimization_guide = false } else if (build_with_tflite_lib) {
diff --git a/components/optimization_guide/internal b/components/optimization_guide/internal index cf123da..ee190098 160000 --- a/components/optimization_guide/internal +++ b/components/optimization_guide/internal
@@ -1 +1 @@ -Subproject commit cf123dadc9bb84e815991922cc9d4d9277b8b4e5 +Subproject commit ee19009832d5daab66094b1325d8d42ebb61e1ad
diff --git a/components/optimization_guide/proto/features/tab_organization.proto b/components/optimization_guide/proto/features/tab_organization.proto index e5507d6..8661f314 100644 --- a/components/optimization_guide/proto/features/tab_organization.proto +++ b/components/optimization_guide/proto/features/tab_organization.proto
@@ -19,6 +19,9 @@ // The tabs that belong in this organization. repeated Tab tabs = 2; + + // The id of the pre-existing tab group this corresponds to, if any. + optional string group_id = 3; } message Tab { @@ -38,6 +41,13 @@ // The tab that was active at the time the user requested tab organization. int64 active_tab_id = 2; + + // All pre-existing tab groups. + repeated TabOrganization pre_existing_tab_groups = 3; + + // Whether or not the tab organization response can include reorganizing + // existing tab groups. + bool allow_reorganizing_existing_groups = 4; } message TabOrganizationResponse {
diff --git a/components/page_image_service/features.cc b/components/page_image_service/features.cc index b43acdd..4046507 100644 --- a/components/page_image_service/features.cc +++ b/components/page_image_service/features.cc
@@ -23,6 +23,6 @@ // images for already synced sync entities can be fetched. BASE_FEATURE(kImageServiceObserveSyncDownloadStatus, "ImageServiceObserveSyncDownloadStatus", - base::FEATURE_DISABLED_BY_DEFAULT); + base::FEATURE_ENABLED_BY_DEFAULT); } // namespace page_image_service
diff --git a/components/page_info/android/page_info_controller_android.cc b/components/page_info/android/page_info_controller_android.cc index 5e55a45..7705db8 100644 --- a/components/page_info/android/page_info_controller_android.cc +++ b/components/page_info/android/page_info_controller_android.cc
@@ -147,10 +147,7 @@ permissions_to_display.push_back( ContentSettingsType::FEDERATED_IDENTITY_API); } - if (base::FeatureList::IsEnabled( - permissions::features::kPermissionStorageAccessAPI)) { permissions_to_display.push_back(ContentSettingsType::STORAGE_ACCESS); - } std::map<ContentSettingsType, ContentSetting> user_specified_settings_to_display;
diff --git a/components/page_info/page_info.cc b/components/page_info/page_info.cc index 4e3807d..cd019a2 100644 --- a/components/page_info/page_info.cc +++ b/components/page_info/page_info.cc
@@ -1337,12 +1337,6 @@ } for (ContentSettingsType type : kTwoPatternPermissions) { - if (type == ContentSettingsType::STORAGE_ACCESS && - !base::FeatureList::IsEnabled( - permissions::features::kPermissionStorageAccessAPI)) { - continue; - } - for (auto& requester : GetTwoSitePermissionRequesters(type)) { PermissionInfo permission_info; permission_info.type = type;
diff --git a/components/page_info/page_info_ui.cc b/components/page_info/page_info_ui.cc index 27b6c50..720e4e5 100644 --- a/components/page_info/page_info_ui.cc +++ b/components/page_info/page_info_ui.cc
@@ -888,10 +888,12 @@ DCHECK_EQ(opposite_to_block_setting, CONTENT_SETTING_ALLOW); SetTargetContentSetting(permission, CONTENT_SETTING_BLOCK); permission.is_one_time = false; + permission.is_in_use = false; break; case CONTENT_SETTING_BLOCK: SetTargetContentSetting(permission, opposite_to_block_setting); permission.is_one_time = false; + permission.is_in_use = false; break; case CONTENT_SETTING_DEFAULT: { CreateOppositeToDefaultSiteException(permission, @@ -903,6 +905,7 @@ permission.type)) { permission.is_one_time = true; } + permission.is_in_use = false; break; } case CONTENT_SETTING_ASK:
diff --git a/components/password_manager/content/browser/content_password_manager_driver.cc b/components/password_manager/content/browser/content_password_manager_driver.cc index 0a74325..d4ac651 100644 --- a/components/password_manager/content/browser/content_password_manager_driver.cc +++ b/components/password_manager/content/browser/content_password_manager_driver.cc
@@ -208,6 +208,13 @@ GetPasswordGenerationAgent()->FocusNextFieldAfterPasswords(); } +void ContentPasswordManagerDriver::FillField(autofill::FieldRendererId field_id, + const std::u16string& value) { + if (const auto& agent = GetPasswordAutofillAgent()) { + agent->FillField(field_id, value); + } +} + void ContentPasswordManagerDriver::FillSuggestion( const std::u16string& username, const std::u16string& password) {
diff --git a/components/password_manager/content/browser/content_password_manager_driver.h b/components/password_manager/content/browser/content_password_manager_driver.h index b9695ad..52099a70 100644 --- a/components/password_manager/content/browser/content_password_manager_driver.h +++ b/components/password_manager/content/browser/content_password_manager_driver.h
@@ -69,6 +69,8 @@ autofill::FieldRendererId generation_element_id, const std::u16string& password) override; void FocusNextFieldAfterPasswords() override; + void FillField(autofill::FieldRendererId field_id, + const std::u16string& value) override; void FillSuggestion(const std::u16string& username, const std::u16string& password) override; void FillIntoFocusedField(bool is_password,
diff --git a/components/password_manager/content/browser/content_password_manager_driver_unittest.cc b/components/password_manager/content/browser/content_password_manager_driver_unittest.cc index dc69daf..0098c9f 100644 --- a/components/password_manager/content/browser/content_password_manager_driver_unittest.cc +++ b/components/password_manager/content/browser/content_password_manager_driver_unittest.cc
@@ -104,6 +104,10 @@ PreviewField, (autofill::FieldRendererId, const std::u16string&), (override)); + MOCK_METHOD(void, + FillField, + (autofill::FieldRendererId, const std::u16string&), + (override)); #if BUILDFLAG(IS_ANDROID) MOCK_METHOD(void, KeyboardReplacingSurfaceClosed, (bool), (override)); MOCK_METHOD(void, TriggerFormSubmission, (), (override));
diff --git a/components/password_manager/core/browser/password_manager_driver.h b/components/password_manager/core/browser/password_manager_driver.h index aecd7491..25d9bbd 100644 --- a/components/password_manager/core/browser/password_manager_driver.h +++ b/components/password_manager/core/browser/password_manager_driver.h
@@ -85,6 +85,11 @@ // in account creation). virtual void FocusNextFieldAfterPasswords() {} + // Tells the renderer to fill the given `value` into the field identified by + // the `field_id`. + virtual void FillField(autofill::FieldRendererId field_id, + const std::u16string& value) {} + // Tells the driver to fill the form with the `username` and `password`. virtual void FillSuggestion(const std::u16string& username, const std::u16string& password) = 0;
diff --git a/components/password_manager/core/browser/password_manual_fallback_flow.cc b/components/password_manager/core/browser/password_manual_fallback_flow.cc index 2da78e6..9cdcfe3 100644 --- a/components/password_manager/core/browser/password_manual_fallback_flow.cc +++ b/components/password_manager/core/browser/password_manual_fallback_flow.cc
@@ -108,6 +108,8 @@ // TODO(b/321678448): Fill password form for acceptable suggestions. break; case autofill::PopupItemId::kPasswordFieldByFieldFilling: + password_manager_driver_->FillField(saved_field_id_, + suggestion.main_text.value); // TODO(b/321678448): Fill username. break; case autofill::PopupItemId::kFillPassword: @@ -124,6 +126,8 @@ // Other suggestion types are not supported. NOTREACHED_NORETURN(); } + autofill_client_->HideAutofillPopup( + autofill::PopupHidingReason::kAcceptSuggestion); } void PasswordManualFallbackFlow::DidPerformButtonActionForSuggestion(
diff --git a/components/password_manager/core/browser/password_manual_fallback_flow_unittest.cc b/components/password_manager/core/browser/password_manual_fallback_flow_unittest.cc index fa09906..d521e57 100644 --- a/components/password_manager/core/browser/password_manual_fallback_flow_unittest.cc +++ b/components/password_manager/core/browser/password_manual_fallback_flow_unittest.cc
@@ -10,6 +10,7 @@ #include "components/affiliations/core/browser/fake_affiliation_service.h" #include "components/autofill/core/browser/test_autofill_client.h" #include "components/autofill/core/browser/ui/autofill_popup_delegate.h" +#include "components/autofill/core/common/autofill_test_utils.h" #include "components/autofill/core/common/mojom/autofill_types.mojom-shared.h" #include "components/password_manager/core/browser/password_manager_test_utils.h" #include "components/password_manager/core/browser/password_store/test_password_store.h" @@ -25,6 +26,7 @@ using autofill::AutofillPopupDelegate; using autofill::AutofillSuggestionTriggerSource; using autofill::FieldRendererId; +using autofill::PopupHidingReason; using autofill::PopupItemId; using autofill::TestAutofillClient; using autofill::test::AutofillUnitTestEnvironment; @@ -44,6 +46,7 @@ (const AutofillClient::PopupOpenArgs&, base::WeakPtr<AutofillPopupDelegate>), (override)); + MOCK_METHOD(void, HideAutofillPopup, (PopupHidingReason), (override)); }; class MockPasswordManagerDriver : public StubPasswordManagerDriver { @@ -54,6 +57,10 @@ PreviewField, (FieldRendererId, const std::u16string&), (override)); + MOCK_METHOD(void, + FillField, + (FieldRendererId, const std::u16string&), + (override)); }; class PasswordManualFallbackFlowTest : public ::testing::Test { @@ -258,4 +265,23 @@ PopupItemId::kPasswordFieldByFieldFilling, u"username@example.com")); } +// Test that username field-by-field suggestion is filled into the correct field +// by the manual fallback flow. +TEST_F(PasswordManualFallbackFlowTest, AcceptUsernameFieldByFieldSuggestion) { + ProcessPasswordStoreUpdates(); + + const FieldRendererId field_id = MakeFieldRendererId(); + flow().RunFlow(field_id, gfx::RectF{}, TextDirection::LEFT_TO_RIGHT); + + EXPECT_CALL(driver(), + FillField(field_id, std::u16string(u"username@example.com"))); + EXPECT_CALL(autofill_client(), + HideAutofillPopup(PopupHidingReason::kAcceptSuggestion)); + flow().DidAcceptSuggestion( + autofill::test::CreateAutofillSuggestion( + PopupItemId::kPasswordFieldByFieldFilling, u"username@example.com"), + AutofillPopupDelegate::SuggestionPosition{.row = 0, + .sub_popup_level = 1}); +} + } // namespace password_manager
diff --git a/components/permissions/android/java/src/org/chromium/components/permissions/PermissionsAndroidFeatureList.java b/components/permissions/android/java/src/org/chromium/components/permissions/PermissionsAndroidFeatureList.java index 62c20d8..8663269 100644 --- a/components/permissions/android/java/src/org/chromium/components/permissions/PermissionsAndroidFeatureList.java +++ b/components/permissions/android/java/src/org/chromium/components/permissions/PermissionsAndroidFeatureList.java
@@ -16,6 +16,4 @@ public static final String BLOCK_MIDI_BY_DEFAULT = "BlockMidiByDefault"; public static final String ONE_TIME_PERMISSION = "OneTimePermission"; - - public static final String PERMISSION_STORAGE_ACCESS = "PermissionStorageAccessAPI"; }
diff --git a/components/permissions/android/permission_prompt/permission_prompt_android.cc b/components/permissions/android/permission_prompt/permission_prompt_android.cc index b987e53f..0b5a0cf 100644 --- a/components/permissions/android/permission_prompt/permission_prompt_android.cc +++ b/components/permissions/android/permission_prompt/permission_prompt_android.cc
@@ -113,9 +113,7 @@ const std::vector<raw_ptr<PermissionRequest, VectorExperimental>>& requests = delegate_->Requests(); if (requests.size() == 1) { - if (requests[0]->request_type() == RequestType::kStorageAccess && - base::FeatureList::IsEnabled( - permissions::features::kPermissionStorageAccessAPI)) { + if (requests[0]->request_type() == RequestType::kStorageAccess) { return IDR_ANDROID_GLOBE; } return permissions::GetIconId(requests[0]->request_type()); @@ -152,9 +150,7 @@ delegate_->Requests(); CHECK_GT(requests.size(), 0U); - return requests[0]->request_type() == RequestType::kStorageAccess && - base::FeatureList::IsEnabled( - permissions::features::kPermissionStorageAccessAPI); + return requests[0]->request_type() == RequestType::kStorageAccess; } GURL PermissionPromptAndroid::GetRequestingOrigin() const {
diff --git a/components/permissions/android/permissions_android_feature_map.cc b/components/permissions/android/permissions_android_feature_map.cc index 3dece43..45b1ea50 100644 --- a/components/permissions/android/permissions_android_feature_map.cc +++ b/components/permissions/android/permissions_android_feature_map.cc
@@ -22,7 +22,6 @@ &kAndroidApproximateLocationPermissionSupport, &::features::kBlockMidiByDefault, &features::kOneTimePermission, - &features::kPermissionStorageAccessAPI, }; // static
diff --git a/components/permissions/features.cc b/components/permissions/features.cc index 5600daf..ecb84124 100644 --- a/components/permissions/features.cc +++ b/components/permissions/features.cc
@@ -112,13 +112,6 @@ #endif // BUILDFLAG(IS_ANDROID) -// When enabled, permission grants for Storage Access API will be enabled. -// This includes enabling prompts, a new settings page and page info and -// omnibox integration. -BASE_FEATURE(kPermissionStorageAccessAPI, - "PermissionStorageAccessAPI", - base::FEATURE_ENABLED_BY_DEFAULT); - // When enabled "window-placement" may be used as an alias for // "window-management". Additionally, reverse mappings (i.e. enum to string) // will default to the legacy strings ("window-placement").
diff --git a/components/permissions/features.h b/components/permissions/features.h index 82902a4..3d2ac1e 100644 --- a/components/permissions/features.h +++ b/components/permissions/features.h
@@ -68,9 +68,6 @@ #endif // BUILDFLAG(IS_ANDROID) COMPONENT_EXPORT(PERMISSIONS_COMMON) -BASE_DECLARE_FEATURE(kPermissionStorageAccessAPI); - -COMPONENT_EXPORT(PERMISSIONS_COMMON) BASE_DECLARE_FEATURE(kWindowPlacementPermissionAlias); COMPONENT_EXPORT(PERMISSIONS_COMMON)
diff --git a/components/permissions/permission_request.cc b/components/permissions/permission_request.cc index 15a8693..58fa158 100644 --- a/components/permissions/permission_request.cc +++ b/components/permissions/permission_request.cc
@@ -126,8 +126,6 @@ break; case RequestType::kStorageAccess: // The SA prompt does not currently bold any part of its message. - if (base::FeatureList::IsEnabled( - permissions::features::kPermissionStorageAccessAPI)) { return AnnotatedMessageText( l10n_util::GetStringFUTF16( IDS_CONCAT_TWO_STRINGS_WITH_PERIODS, @@ -139,12 +137,6 @@ requesting_origin_string_formatted, embedding_origin_string_formatted)), /*bolded_ranges=*/{}); - } - return AnnotatedMessageText( - l10n_util::GetStringFUTF16(IDS_STORAGE_ACCESS_INFOBAR_TEXT, - requesting_origin_string_formatted, - embedding_origin_string_formatted), - /*bolded_ranges=*/{}); case RequestType::kTopLevelStorageAccess: NOTREACHED(); break; @@ -352,9 +344,7 @@ } bool PermissionRequest::ShouldUseTwoOriginPrompt() const { - return request_type() == RequestType::kStorageAccess && - base::FeatureList::IsEnabled( - permissions::features::kPermissionStorageAccessAPI); + return request_type() == RequestType::kStorageAccess; } void PermissionRequest::PermissionGranted(bool is_one_time) {
diff --git a/components/permissions/request_type.cc b/components/permissions/request_type.cc index f66f790d..fad9b73f 100644 --- a/components/permissions/request_type.cc +++ b/components/permissions/request_type.cc
@@ -64,10 +64,7 @@ return IDR_ANDROID_INFOBAR_PROTECTED_MEDIA_IDENTIFIER; case RequestType::kStorageAccess: case RequestType::kTopLevelStorageAccess: - return base::FeatureList::IsEnabled( - permissions::features::kPermissionStorageAccessAPI) - ? IDR_ANDROID_STORAGE_ACCESS - : IDR_ANDROID_INFOBAR_PERMISSION_COOKIE; + return IDR_ANDROID_STORAGE_ACCESS; } NOTREACHED(); return 0; @@ -139,12 +136,7 @@ #endif case RequestType::kStorageAccess: case RequestType::kTopLevelStorageAccess: - if (base::FeatureList::IsEnabled( - permissions::features::kPermissionStorageAccessAPI)) { - return vector_icons::kStorageAccessIcon; - } - return cr23 ? vector_icons::kCookieChromeRefreshIcon - : vector_icons::kCookieIcon; + return vector_icons::kStorageAccessIcon; case RequestType::kWindowManagement: return cr23 ? vector_icons::kSelectWindowChromeRefreshIcon : vector_icons::kSelectWindowIcon;
diff --git a/components/permissions/test/mock_permission_prompt.cc b/components/permissions/test/mock_permission_prompt.cc index 8e29d79..6719ac49 100644 --- a/components/permissions/test/mock_permission_prompt.cc +++ b/components/permissions/test/mock_permission_prompt.cc
@@ -73,9 +73,7 @@ EXPECT_FALSE(permissions::GetIconId(request_type).is_empty()); #endif EXPECT_EQ(request->ShouldUseTwoOriginPrompt(), - request_type == permissions::RequestType::kStorageAccess && - base::FeatureList::IsEnabled( - permissions::features::kPermissionStorageAccessAPI)); + request_type == permissions::RequestType::kStorageAccess); } }
diff --git a/components/policy/resources/templates/policies.yaml b/components/policy/resources/templates/policies.yaml index 655439f..16301703 100644 --- a/components/policy/resources/templates/policies.yaml +++ b/components/policy/resources/templates/policies.yaml
@@ -1231,6 +1231,7 @@ 1230: DefaultDirectSocketsSetting 1231: DirectSocketsAllowedForUrls 1232: DirectSocketsBlockedForUrls + 1233: ProductSpecificationsEnabled atomic_groups: 1: Homepage 2: RemoteAccess
diff --git a/components/policy/resources/templates/policy_definitions/Miscellaneous/ProductSpecificationsEnabled.yaml b/components/policy/resources/templates/policy_definitions/Miscellaneous/ProductSpecificationsEnabled.yaml new file mode 100644 index 0000000..6a07643 --- /dev/null +++ b/components/policy/resources/templates/policy_definitions/Miscellaneous/ProductSpecificationsEnabled.yaml
@@ -0,0 +1,29 @@ +caption: Allow the product specifications feature to be enabled +default: true +desc: |- + If this policy is set to Enabled or not set, product specifications will be available to users. + + If this policy is set to Disabled, product specifications will be unavailable. +example_value: false +features: + dynamic_refresh: true + per_profile: true +future_on: +- ios +- android +items: +- caption: The product specifications feature will be available to users. + value: true +- caption: The product specifications feature will not be available to users. + value: false +owners: +- aymana@chromium.org +- mdjones@chromium.org +- chrome-shopping@google.com +schema: + type: boolean +supported_on: +- chrome.*:124- +- chrome_os:124- +tags: [] +type: main
diff --git a/components/policy/test/data/pref_mapping/ProductSpecificationsEnabled.json b/components/policy/test/data/pref_mapping/ProductSpecificationsEnabled.json new file mode 100644 index 0000000..6082a93 --- /dev/null +++ b/components/policy/test/data/pref_mapping/ProductSpecificationsEnabled.json
@@ -0,0 +1,45 @@ +[ + { + "os": [ + "win", + "linux", + "mac", + "chromeos_ash", + "chromeos_lacros", + "android" + ], + "policy_pref_mapping_tests": [ + { + "policies": {}, + "prefs": { + "product_specifications_enabled": { + "default_value": true, + "location": "user_profile" + } + } + }, + { + "policies": { + "ProductSpecificationsEnabled": true + }, + "prefs": { + "product_specifications_enabled": { + "value": true, + "location": "user_profile" + } + } + }, + { + "policies": { + "ProductSpecificationsEnabled": false + }, + "prefs": { + "product_specifications_enabled": { + "value": false, + "location": "user_profile" + } + } + } + ] + } +]
diff --git a/components/printing/test/print_render_frame_helper_browsertest.cc b/components/printing/test/print_render_frame_helper_browsertest.cc index 643e2a8..3882636 100644 --- a/components/printing/test/print_render_frame_helper_browsertest.cc +++ b/components/printing/test/print_render_frame_helper_browsertest.cc
@@ -1392,9 +1392,9 @@ EXPECT_EQ(expected_content_width, page_layout->content_width); EXPECT_EQ(expected_content_height, page_layout->content_height); EXPECT_EQ(expected_margin_top, page_layout->margin_top); - EXPECT_EQ(expected_margin_right, page_layout->margin_right); - EXPECT_EQ(expected_margin_left, page_layout->margin_left); EXPECT_EQ(expected_margin_bottom, page_layout->margin_bottom); + EXPECT_EQ(expected_margin_left, page_layout->margin_left); + EXPECT_EQ(expected_margin_right, page_layout->margin_right); EXPECT_EQ(expected_all_pages_have_custom_size, preview_ui()->all_pages_have_custom_size()); EXPECT_EQ(expected_all_pages_have_custom_orientation, @@ -2075,6 +2075,291 @@ OnClosePrintPreviewDialog(); } +#if defined(MOCK_PRINTER_SUPPORTS_PAGE_IMAGES) + +TEST_F(PrintRenderFrameHelperPreviewTest, + MarginsSizeAndInputScaleAndAvoidOverflowScaleToPrinter3) { + // The default page size in these tests is US Letter - 8.5 by 11 inches. + // Setting the page size to 3 inches and margins to 0.5 inches results in a + // page area of 2 by 2 inches. Setting the input scale factor to 200% shrinks + // this to 1 inch. There's a 1.5in wide block in the test. To make it fit + // without overflowing, Blink will increase the page area size by 3/2 (1.5/1), + // i.e. 50% larger, so that the final page area for layout is 1.5 by 1.5 + // inches. Content that is 5.25 inches tall should therefore require 3 and a + // half pages (1.5 * 3.5 = 5.25). The specified page size is smaller than the + // paper (8.5x11 inches), and should be centered on paper, meaning that the + // margins will be changed so that the sum of the margins and the page size + // will be equal to the paper size. + LoadHTML(R"HTML( + <style> + @page { margin:0.5in; size:3in; } + body { margin:0; } + </style> + <div style="width:1.5in; height:5.25in; background:#0000ff;"></div> + )HTML"); + + print_settings().Set(kSettingPrinterType, + static_cast<int>(mojom::PrinterType::kLocal)); + print_settings().Set(kSettingScaleFactor, 200); + print_settings().Set(kSettingShouldPrintBackgrounds, true); + + printer()->set_should_generate_page_images(true); + + OnPrintPreview(); + + EXPECT_EQ(0u, preview_ui()->print_preview_pages_remaining()); + VerifyDefaultPageLayout(144, 144, 324, 324, 234, 234, true, true); + VerifyPreviewPageCount(4); + + // Look for the #0000ff background on all the pages. + + // First page: + const MockPrinterPage* page = printer()->GetPrinterPage(0); + ASSERT_TRUE(page); + const printing::Image* image = &page->image(); + ASSERT_EQ(image->size(), gfx::Size(612, 792)); + // Top left page content area corner. + EXPECT_EQ(image->pixel_at(233, 323), 0xffffffU); // white outside + EXPECT_EQ(image->pixel_at(234, 324), 0x0000ffU); // blue inside + // Bottom right page content area corner. + EXPECT_EQ(image->pixel_at(377, 467), 0x0000ffU); // blue inside + EXPECT_EQ(image->pixel_at(378, 468), 0xffffffU); // white outside + + // Second page: + page = printer()->GetPrinterPage(1); + ASSERT_TRUE(page); + image = &page->image(); + ASSERT_EQ(image->size(), gfx::Size(612, 792)); + // Top left page content area corner. + EXPECT_EQ(image->pixel_at(233, 323), 0xffffffU); // white outside + EXPECT_EQ(image->pixel_at(234, 324), 0x0000ffU); // blue inside + // Bottom right page content area corner. + EXPECT_EQ(image->pixel_at(377, 467), 0x0000ffU); // blue inside + EXPECT_EQ(image->pixel_at(378, 468), 0xffffffU); // white outside + + // Third page: + page = printer()->GetPrinterPage(2); + ASSERT_TRUE(page); + image = &page->image(); + ASSERT_EQ(image->size(), gfx::Size(612, 792)); + // Top left page content area corner. + EXPECT_EQ(image->pixel_at(233, 323), 0xffffffU); // white outside + EXPECT_EQ(image->pixel_at(234, 324), 0x0000ffU); // blue inside + // Bottom right page content area corner. + EXPECT_EQ(image->pixel_at(377, 467), 0x0000ffU); // blue inside + EXPECT_EQ(image->pixel_at(378, 468), 0xffffffU); // white outside + + // Fourth and last page: + page = printer()->GetPrinterPage(3); + ASSERT_TRUE(page); + image = &page->image(); + ASSERT_EQ(image->size(), gfx::Size(612, 792)); + // Top left page content area corner. + EXPECT_EQ(image->pixel_at(233, 323), 0xffffffU); // white outside + EXPECT_EQ(image->pixel_at(234, 324), 0x0000ffU); // blue inside + // Bottom right corner of the DIV (which occupies half of the last page). + EXPECT_EQ(image->pixel_at(377, 395), 0x0000ffU); // blue inside + EXPECT_EQ(image->pixel_at(378, 396), 0xffffffU); // white outside + + OnClosePrintPreviewDialog(); +} + +TEST_F(PrintRenderFrameHelperPreviewTest, + MarginsSizeAndInputScaleAndAvoidOverflowScaleToPrinter4) { + // The default page size in these tests is US Letter - 8.5 by 11 inches. + // Setting the page size to 5 by 3 inches and margins to 0.5 inches results in + // a page area of 4 by 2 inches. Setting the input scale factor to 200% + // shrinks this to 2 by 1 inches. There's a 3in wide block in the test. To + // make it fit without overflowing, Blink will increase the page area size by + // 3/2, i.e. 50% larger, so that the final page area for layout is 3 by 1.5 + // inches. Content that is 2.25 inches tall should therefore require one and a + // half page (1.5 * 1.5 = 2.25). The specified page size is smaller than the + // paper (8.5x11 inches), and should be centered on paper, meaning that the + // margins will be changed so that the sum of the margins and the page size + // will be equal to the paper size. Additionally, the orientation is + // landscape. + LoadHTML(R"HTML( + <style> + @page { margin:0.5in; size:5in 3in; } + body { margin:0; } + </style> + <div style="width:3in; height:2.25in; background:#0000ff;"></div> + )HTML"); + + print_settings().Set(kSettingPrinterType, + static_cast<int>(mojom::PrinterType::kLocal)); + print_settings().Set(kSettingScaleFactor, 200); + print_settings().Set(kSettingShouldPrintBackgrounds, true); + + printer()->set_should_generate_page_images(true); + + OnPrintPreview(); + + EXPECT_EQ(0u, preview_ui()->print_preview_pages_remaining()); + VerifyDefaultPageLayout(288, 144, 234, 234, 252, 252, true, true); + VerifyPreviewPageCount(2); + + // Look for the #0000ff background on all the pages. + + // First page: + const MockPrinterPage* page = printer()->GetPrinterPage(0); + ASSERT_TRUE(page); + const printing::Image* image = &page->image(); + ASSERT_EQ(image->size(), gfx::Size(792, 612)); + // Top left page content area corner. + EXPECT_EQ(image->pixel_at(251, 233), 0xffffffU); // white outside + EXPECT_EQ(image->pixel_at(252, 234), 0x0000ffU); // blue inside + // Bottom right page content area corner. + EXPECT_EQ(image->pixel_at(539, 377), 0x0000ffU); // blue inside + EXPECT_EQ(image->pixel_at(540, 378), 0xffffffU); // white outside + + // Second page: + page = printer()->GetPrinterPage(1); + ASSERT_TRUE(page); + image = &page->image(); + ASSERT_EQ(image->size(), gfx::Size(792, 612)); + // Top left page content area corner. + EXPECT_EQ(image->pixel_at(251, 233), 0xffffffU); // white outside + EXPECT_EQ(image->pixel_at(252, 234), 0x0000ffU); // blue inside + // Bottom right corner of the DIV (which occupies half of the last page). + EXPECT_EQ(image->pixel_at(539, 305), 0x0000ffU); // blue inside + EXPECT_EQ(image->pixel_at(540, 306), 0xffffffU); // white outside + + OnClosePrintPreviewDialog(); +} + +TEST_F(PrintRenderFrameHelperPreviewTest, + MarginsSizeAndInputScaleAndAvoidOverflowScaleToPrinter5) { + // The default page size in these tests is US Letter - 8.5 by 11 inches. + // Setting the page size to 34 inches and margins to 1 inch results in a page + // area of 32 inches. Setting the input scale factor to 200% shrinks this to + // 16 inches. There's a 24in wide block in the test. To make it fit without + // overflowing, Blink will increase the page area size by 3/2 (24/16), + // i.e. 50% larger, so that the final page area for layout is 24 by 24 inches. + // Content that is 36 inches tall should therefore require one and a half page + // (1.5 * 1.5 = 2.25). The specified page size is larger than the paper + // (8.5x11 inches), and needs to be zoomed down to fit. The zoom factor will + // be 0.25. 8.5 / 34 (short edges) = 0.25, which is less than 11 / 34 (long + // edges). Margins are also adjusted accordingly, since it's essentially the + // entire page box that's scaled down. The result should be centered on paper. + LoadHTML(R"HTML( + <style> + @page { margin:1in; size:34in; } + body { margin:0; } + </style> + <div style="width:24in; height:36in; background:#0000ff;"></div> + )HTML"); + + print_settings().Set(kSettingPrinterType, + static_cast<int>(mojom::PrinterType::kLocal)); + print_settings().Set(kSettingScaleFactor, 200); + print_settings().Set(kSettingShouldPrintBackgrounds, true); + + printer()->set_should_generate_page_images(true); + + OnPrintPreview(); + + EXPECT_EQ(0u, preview_ui()->print_preview_pages_remaining()); + VerifyDefaultPageLayout(576, 576, 108, 108, 18, 18, true, true); + VerifyPreviewPageCount(2); + + // Look for the #0000ff background on the first and last pages. + + // First page: + const MockPrinterPage* page = printer()->GetPrinterPage(0); + ASSERT_TRUE(page); + const printing::Image* image = &page->image(); + ASSERT_EQ(image->size(), gfx::Size(612, 792)); + // Top left page content area corner. + EXPECT_EQ(image->pixel_at(17, 107), 0xffffffU); // white outside + EXPECT_EQ(image->pixel_at(18, 108), 0x0000ffU); // blue inside + // Bottom right page content area corner. + EXPECT_EQ(image->pixel_at(557, 683), 0x0000ffU); // blue inside + EXPECT_EQ(image->pixel_at(558, 684), 0xffffffU); // white outside + + // Second page: + page = printer()->GetPrinterPage(1); + ASSERT_TRUE(page); + image = &page->image(); + ASSERT_EQ(image->size(), gfx::Size(612, 792)); + // Top left page content area corner. + EXPECT_EQ(image->pixel_at(17, 107), 0xffffffU); // white outside + EXPECT_EQ(image->pixel_at(18, 108), 0x0000ffU); // blue inside + // Bottom right corner of the DIV (which occupies half of the last page). + EXPECT_EQ(image->pixel_at(557, 395), 0x0000ffU); // blue inside + EXPECT_EQ(image->pixel_at(558, 396), 0xffffffU); // white outside + + OnClosePrintPreviewDialog(); +} + +TEST_F(PrintRenderFrameHelperPreviewTest, + MarginsSizeAndInputScaleAndAvoidOverflowScaleToPrinter6) { + // The default page size in these tests is US Letter - 8.5 by 11 inches. + // Setting the page size to 33 by 18 inches and margins to 1 inch results in a + // page area of 31 by 16 inches. Setting the input scale factor to 200% + // shrinks this to 15.5 by 8 inches. There's a 23.25in wide block in the + // test. To make it fit without overflowing, Blink will increase the page area + // size by 3/2 (23.25/15.5), i.e. 50% larger, so that the final page area for + // layout is 23.25 by 12 inches. Content that is 18 inches tall should + // therefore require one and a half page (12 * 1.5 = 18). The specified page + // size is larger than the paper (8.5x11 inches), and needs to be zoomed down + // to fit. The zoom factor will be 1/3. 11 / 33 (long edges) = 1/3, which is + // less than 8.5 / 18 (short edges). Margins are also adjusted accordingly, + // since it's essentially the entire page box that's scaled down. + // Additionally, the orientation is landscape. The result should be centered + // on paper. + LoadHTML(R"HTML( + <style> + @page { margin:1in; size:33in 18in; } + body { margin:0; } + </style> + <div style="width:23.25in; height:18in; background:#0000ff;"></div> + )HTML"); + + print_settings().Set(kSettingPrinterType, + static_cast<int>(mojom::PrinterType::kLocal)); + print_settings().Set(kSettingScaleFactor, 200); + print_settings().Set(kSettingShouldPrintBackgrounds, true); + + printer()->set_should_generate_page_images(true); + + OnPrintPreview(); + + EXPECT_EQ(0u, preview_ui()->print_preview_pages_remaining()); + VerifyDefaultPageLayout(744, 384, 114, 114, 24, 24, true, true); + VerifyPreviewPageCount(2); + + // Look for the #0000ff background on the first and last pages. + + // First page: + const MockPrinterPage* page = printer()->GetPrinterPage(0); + ASSERT_TRUE(page); + const printing::Image* image = &page->image(); + ASSERT_EQ(image->size(), gfx::Size(792, 612)); + // Top left page content area corner. + EXPECT_EQ(image->pixel_at(23, 113), 0xffffffU); // white outside + EXPECT_EQ(image->pixel_at(24, 114), 0x0000ffU); // blue inside + // Bottom right page content area corner. + EXPECT_EQ(image->pixel_at(767, 497), 0x0000ffU); // blue inside + EXPECT_EQ(image->pixel_at(768, 498), 0xffffffU); // white outside + + // Second page: + page = printer()->GetPrinterPage(1); + ASSERT_TRUE(page); + image = &page->image(); + ASSERT_EQ(image->size(), gfx::Size(792, 612)); + // Top left page content area corner. + EXPECT_EQ(image->pixel_at(23, 113), 0xffffffU); // white outside + EXPECT_EQ(image->pixel_at(24, 114), 0x0000ffU); // blue inside + // Bottom right corner of the DIV (which occupies half of the last page). + EXPECT_EQ(image->pixel_at(767, 305), 0x0000ffU); // blue inside + EXPECT_EQ(image->pixel_at(768, 306), 0xffffffU); // white outside + + OnClosePrintPreviewDialog(); +} + +#endif // MOCK_PRINTER_SUPPORTS_PAGE_IMAGES + // Test to verify that print preview workflow honor the orientation settings // specified in css. TEST_F(PrintRenderFrameHelperPreviewTest, PrintPreviewHonorsOrientationCss) {
diff --git a/components/saved_tab_groups/README.md b/components/saved_tab_groups/README.md index d77e2bcf..24689937 100644 --- a/components/saved_tab_groups/README.md +++ b/components/saved_tab_groups/README.md
@@ -7,4 +7,16 @@ Saved Tab Groups are built on sync's storage layer. This storage solution works across sessions and when sync is enabled, saved tab groups will sync across -devices updating the tabs and other metadata about the tab group in real-time. \ No newline at end of file +devices updating the tabs and other metadata about the tab group in real-time. + +## Testing + +To run all the relevant C++ unit tests, you can run the `components_unittests` +target and give the `saved_tab_groups` filter file as an argument: + +``` +./out/Default/components_unittests --test-launcher-filter-file=components/saved_tab_groups/components_unittests.filter +``` + +To keep the list of tests updated, you must add the test group name to the +`components_unittests.filter` file whenever writing a new test file.
diff --git a/components/saved_tab_groups/components_unittests.filter b/components/saved_tab_groups/components_unittests.filter new file mode 100644 index 0000000..373e86d --- /dev/null +++ b/components/saved_tab_groups/components_unittests.filter
@@ -0,0 +1,5 @@ +*SavedTabGroupConversionTest.* +*SavedTabGroupModelObserverTest.* +*SavedTabGroupModelTest.* +*SavedTabGroupSyncBridgeTest.* +*SavedTabGroupTest.*
diff --git a/components/services/storage/public/mojom/service_worker_storage_control.mojom b/components/services/storage/public/mojom/service_worker_storage_control.mojom index 4b1de345..f1bfdd6 100644 --- a/components/services/storage/public/mojom/service_worker_storage_control.mojom +++ b/components/services/storage/public/mojom/service_worker_storage_control.mojom
@@ -7,6 +7,7 @@ import "components/services/storage/public/mojom/service_worker_database.mojom"; import "components/services/storage/public/mojom/storage_policy_update.mojom"; import "mojo/public/mojom/base/big_buffer.mojom"; +import "mojo/public/mojom/base/byte_string.mojom"; import "mojo/public/mojom/base/time.mojom"; import "services/network/public/mojom/url_response_head.mojom"; import "third_party/blink/public/mojom/service_worker/service_worker_fetch_handler_type.mojom"; @@ -122,13 +123,13 @@ }; // Represents an entry of user data which is stored with a service worker -// registration. An entry of user data is an arbitrary key-vaule pair. The +// registration. An entry of user data is an arbitrary key-value pair. The // lifetime of user data is tied up with the registration. // It will be deleted when the corresponding registration is deleted. struct ServiceWorkerUserData { int64 registration_id; string key; - string value; + mojo_base.mojom.ByteString value; }; // Controls the state of service worker storage within a partition. This is a @@ -284,7 +285,8 @@ // Succeeds only when all keys are found. On success, the size and the order // of |values| are the same as |keys|. GetUserData(int64 registration_id, array<string> keys) => - (ServiceWorkerDatabaseStatus status, array<string> values); + (ServiceWorkerDatabaseStatus status, + array<mojo_base.mojom.ByteString> values); // Stores `user_data` on persistent storage. StoreUserData(int64 registration_id, blink.mojom.StorageKey key, @@ -297,11 +299,13 @@ // Gets user data values associated with the given |registration_id| // filtered by |key_prefix|. GetUserDataByKeyPrefix(int64 registration_id, string key_prefix) => - (ServiceWorkerDatabaseStatus status, array<string> values); + (ServiceWorkerDatabaseStatus status, + array<mojo_base.mojom.ByteString> values); // Gets user data associated with the given |registration_id| filtered by // |key_prefix|. Returns user data as key-value pairs. GetUserKeysAndDataByKeyPrefix(int64 registration_id, string key_prefix) => - (ServiceWorkerDatabaseStatus status, map<string, string> user_data); + (ServiceWorkerDatabaseStatus status, + map<string, mojo_base.mojom.ByteString> user_data); // Clears user data associated with the given |registration_id| filtered by // |key_prefix|. ClearUserDataByKeyPrefixes(int64 registratation_id,
diff --git a/components/sync/base/features.cc b/components/sync/base/features.cc index 4521baf4..b18a4cf 100644 --- a/components/sync/base/features.cc +++ b/components/sync/base/features.cc
@@ -121,12 +121,7 @@ BASE_FEATURE(kSyncPollImmediatelyOnEveryStartup, "SyncPollImmediatelyOnEveryStartup2", -#if BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC) || \ - BUILDFLAG(IS_WIN) - base::FEATURE_ENABLED_BY_DEFAULT -#else base::FEATURE_DISABLED_BY_DEFAULT -#endif ); #if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)
diff --git a/content/app/ios/appex/BUILD.gn b/content/app/ios/appex/BUILD.gn new file mode 100644 index 0000000..615d5fc --- /dev/null +++ b/content/app/ios/appex/BUILD.gn
@@ -0,0 +1,35 @@ +# Copyright 2024 The Chromium Authors +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import("//build/config/ios/rules.gni") +import("//build/config/ios/swift_source_set.gni") +import("//ios/build/chrome_build.gni") +import("//ios/build/config.gni") + +source_set("content_main_thunk") { + sources = [ + "content_main_thunk.cc", + "content_main_thunk.h", + ] + + deps = [ + "//base", + "//content/public/app", + ] + frameworks = [ "Foundation.framework" ] +} + +swift_source_set("content_process") { + sources = [ "content_process.swift" ] + bridge_header = "content_main_thunk.h" + + frameworks = [ + "Foundation.framework", + "ExtensionFoundation.framework", + "BrowserEngineCore.framework", + "BrowserEngineKit.framework", + ] + + deps = [ ":content_main_thunk" ] +}
diff --git a/content/app/ios/appex/content_main_thunk.cc b/content/app/ios/appex/content_main_thunk.cc new file mode 100644 index 0000000..14b854c --- /dev/null +++ b/content/app/ios/appex/content_main_thunk.cc
@@ -0,0 +1,30 @@ +// Copyright 2024 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <pthread.h> +#include <xpc/xpc.h> +#include "base/mac/mach_port_rendezvous.h" + +// Leaked variables for now. +static size_t g_argc = 0; +static const char** g_argv = nullptr; +static pthread_t g_main_thread; + +// The embedder must implement this. +extern "C" int ContentProcessMain(int argc, const char** argv); + +extern "C" void ContentProcessInit() {} + +void* RunMain(void* data) { + ContentProcessMain((int)g_argc, g_argv); + return nullptr; +} + +extern "C" void ContentProcessHandleNewConnection(xpc_connection_t connection) { + // TODO(dtapuska): For now we create our own main thread, figure out if we can + // use the ExtensionMain (thread 0) as the main thread but calling + // CFRunLoopRunInMode seems to crash it so we can't enter a nested event loop + // with some objects on the stack. + pthread_create(&g_main_thread, NULL, RunMain, NULL); +}
diff --git a/content/app/ios/appex/content_main_thunk.h b/content/app/ios/appex/content_main_thunk.h new file mode 100644 index 0000000..87d4d075 --- /dev/null +++ b/content/app/ios/appex/content_main_thunk.h
@@ -0,0 +1,22 @@ +// Copyright 2024 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_APP_IOS_APPEX_CONTENT_MAIN_THUNK_H_ +#define CONTENT_APP_IOS_APPEX_CONTENT_MAIN_THUNK_H_ + +#import <Foundation/Foundation.h> +#import <xpc/xpc.h> + +#ifdef __cplusplus +extern "C" { +#endif + +void ContentProcessInit(); +void ContentProcessHandleNewConnection(xpc_connection_t); + +#ifdef __cplusplus +} +#endif + +#endif // CONTENT_APP_IOS_APPEX_CONTENT_MAIN_THUNK_H_
diff --git a/content/app/ios/appex/content_process.appex.entitlements b/content/app/ios/appex/content_process.appex.entitlements new file mode 100644 index 0000000..469c5b2a --- /dev/null +++ b/content/app/ios/appex/content_process.appex.entitlements
@@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>com.apple.developer.web-browser-engine.webcontent</key> + <true/> + <key>com.apple.security.get-task-allow</key> + <true/> + <key>com.apple.developer.cs.allow-jit</key> + <true/> +</dict> +</plist> \ No newline at end of file
diff --git a/content/app/ios/appex/content_process.swift b/content/app/ios/appex/content_process.swift new file mode 100644 index 0000000..936fab0 --- /dev/null +++ b/content/app/ios/appex/content_process.swift
@@ -0,0 +1,18 @@ +// Copyright 2024 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import BrowserEngineKit +import ExtensionFoundation +import Foundation + +@main +class ContentProcess: WebContentExtension { + required init() { + ContentProcessInit() + } + + public func handle(xpcConnection: xpc_connection_t) { + ContentProcessHandleNewConnection(xpcConnection) + } +}
diff --git a/content/browser/attribution_reporting/attribution_features.cc b/content/browser/attribution_reporting/attribution_features.cc index 6badaba..d2ff5a4 100644 --- a/content/browser/attribution_reporting/attribution_features.cc +++ b/content/browser/attribution_reporting/attribution_features.cc
@@ -8,13 +8,6 @@ namespace content { -// When enabled, prefer to use the new recovery module to recover the -// `AttributionStorageSql` database. See https://crbug.com/1385500 for details. -// This is a kill switch and is not intended to be used in a field trial. -BASE_FEATURE(kAttributionStorageUseBuiltInRecoveryIfSupported, - "AttributionStorageUseBuiltInRecoveryIfSupported", - base::FEATURE_ENABLED_BY_DEFAULT); - BASE_FEATURE(kAttributionVerboseDebugReporting, "AttributionVerboseDebugReporting", base::FEATURE_ENABLED_BY_DEFAULT);
diff --git a/content/browser/attribution_reporting/attribution_features.h b/content/browser/attribution_reporting/attribution_features.h index 0a3ef23..be8e322 100644 --- a/content/browser/attribution_reporting/attribution_features.h +++ b/content/browser/attribution_reporting/attribution_features.h
@@ -10,9 +10,6 @@ namespace content { -CONTENT_EXPORT BASE_DECLARE_FEATURE( - kAttributionStorageUseBuiltInRecoveryIfSupported); - CONTENT_EXPORT BASE_DECLARE_FEATURE(kAttributionVerboseDebugReporting); CONTENT_EXPORT BASE_DECLARE_FEATURE(
diff --git a/content/browser/attribution_reporting/attribution_storage_sql.cc b/content/browser/attribution_reporting/attribution_storage_sql.cc index 671fcaaa..258f6b1b 100644 --- a/content/browser/attribution_reporting/attribution_storage_sql.cc +++ b/content/browser/attribution_reporting/attribution_storage_sql.cc
@@ -2649,8 +2649,7 @@ // Attempt to recover a corrupt database, if it is eligible to be recovered. if (sql::BuiltInRecovery::RecoverIfPossible( &db_, extended_error, - sql::BuiltInRecovery::Strategy::kRecoverWithMetaVersionOrRaze, - &kAttributionStorageUseBuiltInRecoveryIfSupported)) { + sql::BuiltInRecovery::Strategy::kRecoverWithMetaVersionOrRaze)) { // Recovery was attempted. The database handle has been poisoned and the // error callback has been reset.
diff --git a/content/browser/attribution_reporting/attribution_storage_sql_unittest.cc b/content/browser/attribution_reporting/attribution_storage_sql_unittest.cc index a44bc3c..841a4019 100644 --- a/content/browser/attribution_reporting/attribution_storage_sql_unittest.cc +++ b/content/browser/attribution_reporting/attribution_storage_sql_unittest.cc
@@ -261,38 +261,9 @@ return str; } -// See https://crbug.com/1385500 for details. These tests can become -// un-parameterized once the legacy recovery module is no longer used. -enum class BuiltInRecoveryFeatureFlagState { - kDisabled, - kEnabled, -}; - -class AttributionStorageSqlTest - : public testing::TestWithParam<BuiltInRecoveryFeatureFlagState> { +class AttributionStorageSqlTest : public testing::Test { public: - AttributionStorageSqlTest() { - // Whether or not database recovery uses the new built-in recovery module is - // predicated on both the per-database feature flag and the overarching - // feature flag being enabled. For the sake of these tests, just assume both - // or neither are set. - std::vector<base::test::FeatureRef> use_builtin_recovery_features{ - kAttributionStorageUseBuiltInRecoveryIfSupported, - sql::features::kUseBuiltInRecoveryIfSupported}; - - switch (GetParam()) { - case BuiltInRecoveryFeatureFlagState::kDisabled: - scoped_feature_list_.InitWithFeatures( - /*enabled_features=*/{}, - /*disabled_features=*/use_builtin_recovery_features); - break; - case BuiltInRecoveryFeatureFlagState::kEnabled: - scoped_feature_list_.InitWithFeatures( - /*enabled_features=*/use_builtin_recovery_features, - /*disabled_features=*/{}); - break; - } - } + AttributionStorageSqlTest() = default; void SetUp() override { ASSERT_TRUE(temp_directory_.CreateUniqueTempDir()); } @@ -338,11 +309,6 @@ ConfigurableStorageDelegate* delegate() { return delegate_; } - bool UseBuiltInRecovery() const { - return GetParam() == BuiltInRecoveryFeatureFlagState::kEnabled && - sql::BuiltInRecovery::IsSupported(); - } - void ExpectImpressionRows(size_t expected) { sql::Database raw_db; EXPECT_TRUE(raw_db.Open(db_path())); @@ -432,7 +398,7 @@ raw_ptr<ConfigurableStorageDelegate> delegate_ = nullptr; }; -TEST_P(AttributionStorageSqlTest, +TEST_F(AttributionStorageSqlTest, DatabaseInitialized_TablesAndIndexesLazilyInitialized) { base::HistogramTester histograms; @@ -466,7 +432,7 @@ EXPECT_TRUE(base::PathExists(db_path())); } -TEST_P(AttributionStorageSqlTest, DatabaseReopened_DataPersisted) { +TEST_F(AttributionStorageSqlTest, DatabaseReopened_DataPersisted) { OpenDatabase(); AddReportToStorage(); EXPECT_THAT(storage()->GetAttributionReports(base::Time::Now()), SizeIs(1)); @@ -475,7 +441,7 @@ EXPECT_THAT(storage()->GetAttributionReports(base::Time::Now()), SizeIs(1)); } -TEST_P(AttributionStorageSqlTest, CorruptDatabase_RecoveredOnOpen) { +TEST_F(AttributionStorageSqlTest, CorruptDatabase_RecoveredOnOpen) { OpenDatabase(); AddReportToStorage(); EXPECT_THAT(storage()->GetAttributionReports(base::Time::Now()), SizeIs(1)); @@ -490,19 +456,13 @@ // Open that database and ensure that it does not fail. EXPECT_NO_FATAL_FAILURE(OpenDatabase()); - if (UseBuiltInRecovery()) { - // The database should have been recovered. - EXPECT_THAT(storage()->GetAttributionReports(base::Time::Now()), SizeIs(1)); - } else { - // The recovery process does not recover tables without row IDs, causing no - // data to be returned here. See https://crbug.com/1418026. - EXPECT_THAT(storage()->GetAttributionReports(base::Time::Now()), SizeIs(0)); - } + // The database should have been recovered. + EXPECT_THAT(storage()->GetAttributionReports(base::Time::Now()), SizeIs(1)); EXPECT_TRUE(expecter.SawExpectedErrors()); } -TEST_P(AttributionStorageSqlTest, VersionTooNew_RazesDB) { +TEST_F(AttributionStorageSqlTest, VersionTooNew_RazesDB) { OpenDatabase(); AddReportToStorage(); ASSERT_THAT(storage()->GetAttributionReports(base::Time::Now()), SizeIs(1)); @@ -525,7 +485,7 @@ ASSERT_THAT(storage()->GetAttributionReports(base::Time::Now()), IsEmpty()); } -TEST_P(AttributionStorageSqlTest, +TEST_F(AttributionStorageSqlTest, StoreAndRetrieveReportWithVerification_FeatureEnabled) { base::test::ScopedFeatureList feature_list; feature_list.InitAndEnableFeature( @@ -569,7 +529,7 @@ CloseDatabase(); } -TEST_P(AttributionStorageSqlTest, +TEST_F(AttributionStorageSqlTest, StoreAndRetrieveReportWithoutVerification_FeatureEnabled) { OpenDatabase(); base::test::ScopedFeatureList feature_list; @@ -600,7 +560,7 @@ CloseDatabase(); } -TEST_P(AttributionStorageSqlTest, NullReportWithVerification_FeatureEnabled) { +TEST_F(AttributionStorageSqlTest, NullReportWithVerification_FeatureEnabled) { OpenDatabase(); base::test::ScopedFeatureList feature_list; @@ -649,7 +609,7 @@ CloseDatabase(); } -TEST_P(AttributionStorageSqlTest, +TEST_F(AttributionStorageSqlTest, BothRealAndNullReports_OnlyOneReportWithVerification) { OpenDatabase(); @@ -698,7 +658,7 @@ CloseDatabase(); } -TEST_P(AttributionStorageSqlTest, +TEST_F(AttributionStorageSqlTest, BothRealAndNullReportsReverseShuffle_OnlyOneReportWithVerification) { OpenDatabase(); @@ -748,7 +708,7 @@ CloseDatabase(); } -TEST_P(AttributionStorageSqlTest, +TEST_F(AttributionStorageSqlTest, BothRealAndNullReports_MultipleReportsWithVerification) { OpenDatabase(); @@ -818,7 +778,7 @@ } // Create a source with three triggers and craft a query that will target all. -TEST_P(AttributionStorageSqlTest, ClearDataRangeMultipleReports) { +TEST_F(AttributionStorageSqlTest, ClearDataRangeMultipleReports) { base::HistogramTester histograms; OpenDatabase(); @@ -880,7 +840,7 @@ // target C2 and A2, which will in turn delete the source. We should ensure // that C1 and A1 are properly deleted (reports should not be stored // unattributed). -TEST_P(AttributionStorageSqlTest, ClearDataWithVestigialConversion) { +TEST_F(AttributionStorageSqlTest, ClearDataWithVestigialConversion) { base::HistogramTester histograms; OpenDatabase(); @@ -930,7 +890,7 @@ } // Same as the above test, but with a null filter. -TEST_P(AttributionStorageSqlTest, ClearAllDataWithVestigialConversion) { +TEST_F(AttributionStorageSqlTest, ClearAllDataWithVestigialConversion) { base::HistogramTester histograms; OpenDatabase(); @@ -977,7 +937,7 @@ } // The max time range with a null filter should delete everything. -TEST_P(AttributionStorageSqlTest, DeleteEverything) { +TEST_F(AttributionStorageSqlTest, DeleteEverything) { base::HistogramTester histograms; OpenDatabase(); @@ -1024,7 +984,7 @@ "Conversions.ReportsDeletedInDataClearOperation.Aggregatable", 2, 1); } -TEST_P(AttributionStorageSqlTest, ClearData_KeepRateLimitData) { +TEST_F(AttributionStorageSqlTest, ClearData_KeepRateLimitData) { OpenDatabase(); storage()->StoreSource(SourceBuilder().Build()); EXPECT_EQ(AttributionTrigger::EventLevelResult::kSuccess, @@ -1062,7 +1022,7 @@ } } -TEST_P(AttributionStorageSqlTest, DeleteAttributionDataByDataKey) { +TEST_F(AttributionStorageSqlTest, DeleteAttributionDataByDataKey) { OpenDatabase(); storage()->StoreSource(SourceBuilder() .SetReportingOrigin(*SuitableOrigin::Deserialize( @@ -1101,7 +1061,7 @@ } } -TEST_P(AttributionStorageSqlTest, MaxSourcesPerOrigin) { +TEST_F(AttributionStorageSqlTest, MaxSourcesPerOrigin) { OpenDatabase(); delegate()->set_max_sources_per_origin(2); storage()->StoreSource(SourceBuilder().Build()); @@ -1121,7 +1081,7 @@ EXPECT_EQ(3u, rate_limit_rows); } -TEST_P(AttributionStorageSqlTest, MaxReportsPerDestination) { +TEST_F(AttributionStorageSqlTest, MaxReportsPerDestination) { OpenDatabase(); delegate()->set_max_reports_per_destination( AttributionReport::Type::kEventLevel, 2); @@ -1145,7 +1105,7 @@ EXPECT_EQ(3u, rate_limit_rows); } -TEST_P(AttributionStorageSqlTest, CantOpenDb_FailsSilentlyInRelease) { +TEST_F(AttributionStorageSqlTest, CantOpenDb_FailsSilentlyInRelease) { base::CreateDirectoryAndGetError(db_path(), nullptr); auto sql_storage = std::make_unique<AttributionStorageSql>( @@ -1162,7 +1122,7 @@ .event_level_status()); } -TEST_P(AttributionStorageSqlTest, DatabaseDirDoesExist_CreateDirAndOpenDB) { +TEST_F(AttributionStorageSqlTest, DatabaseDirDoesExist_CreateDirAndOpenDB) { // Give the storage layer a database directory that doesn't exist. std::unique_ptr<AttributionStorage> storage = std::make_unique<AttributionStorageSql>( @@ -1177,7 +1137,7 @@ .event_level_status()); } -TEST_P(AttributionStorageSqlTest, DBinitializationSucceeds_HistogramsRecorded) { +TEST_F(AttributionStorageSqlTest, DBinitializationSucceeds_HistogramsRecorded) { { base::HistogramTester histograms; @@ -1219,7 +1179,7 @@ } } -TEST_P(AttributionStorageSqlTest, +TEST_F(AttributionStorageSqlTest, DBinitializationSucceeds_SourcesPerSourceOrginHistogramsRecorded) { auto create_n_origins = [](size_t n) -> std::vector<SuitableOrigin> { std::vector<SuitableOrigin> origins; @@ -1284,7 +1244,7 @@ } } -TEST_P(AttributionStorageSqlTest, MaxUint64StorageSucceeds) { +TEST_F(AttributionStorageSqlTest, MaxUint64StorageSucceeds) { constexpr uint64_t kMaxUint64 = std::numeric_limits<uint64_t>::max(); OpenDatabase(); @@ -1305,7 +1265,7 @@ ElementsAre(TriggerDebugKeyIs(kMaxUint64))); } -TEST_P(AttributionStorageSqlTest, ImpressionNotExpired_NotDeleted) { +TEST_F(AttributionStorageSqlTest, ImpressionNotExpired_NotDeleted) { OpenDatabase(); storage()->StoreSource( @@ -1318,7 +1278,7 @@ ExpectImpressionRows(2u); } -TEST_P(AttributionStorageSqlTest, ImpressionExpired_Deleted) { +TEST_F(AttributionStorageSqlTest, ImpressionExpired_Deleted) { OpenDatabase(); storage()->StoreSource( @@ -1332,7 +1292,7 @@ ExpectImpressionRows(1u); } -TEST_P(AttributionStorageSqlTest, ImpressionExpired_TooFrequent_NotDeleted) { +TEST_F(AttributionStorageSqlTest, ImpressionExpired_TooFrequent_NotDeleted) { OpenDatabase(); delegate()->set_delete_expired_sources_frequency(base::Milliseconds(4)); @@ -1348,7 +1308,7 @@ ExpectImpressionRows(2u); } -TEST_P(AttributionStorageSqlTest, +TEST_F(AttributionStorageSqlTest, ExpiredImpressionWithPendingConversion_NotDeleted) { OpenDatabase(); @@ -1366,7 +1326,7 @@ ExpectImpressionRows(2u); } -TEST_P(AttributionStorageSqlTest, TwoImpressionsOneExpired_OneDeleted) { +TEST_F(AttributionStorageSqlTest, TwoImpressionsOneExpired_OneDeleted) { OpenDatabase(); storage()->StoreSource( @@ -1383,7 +1343,7 @@ ExpectImpressionRows(2u); } -TEST_P(AttributionStorageSqlTest, ExpiredImpressionWithSentConversion_Deleted) { +TEST_F(AttributionStorageSqlTest, ExpiredImpressionWithSentConversion_Deleted) { OpenDatabase(); const base::TimeDelta kReportDelay = base::Milliseconds(5); @@ -1410,7 +1370,7 @@ ExpectImpressionRows(1u); } -TEST_P(AttributionStorageSqlTest, DeleteAggregatableAttributionReport) { +TEST_F(AttributionStorageSqlTest, DeleteAggregatableAttributionReport) { OpenDatabase(); storage()->StoreSource(TestAggregatableSourceProvider().GetBuilder().Build()); @@ -1438,7 +1398,7 @@ CloseDatabase(); } -TEST_P(AttributionStorageSqlTest, +TEST_F(AttributionStorageSqlTest, ExpiredSourceWithPendingAggregatableAttribution_NotDeleted) { OpenDatabase(); @@ -1474,7 +1434,7 @@ ExpectImpressionRows(2u); } -TEST_P(AttributionStorageSqlTest, +TEST_F(AttributionStorageSqlTest, ExpiredSourceWithSentAggregatableAttribution_Deleted) { OpenDatabase(); @@ -1512,7 +1472,7 @@ ExpectImpressionRows(1u); } -TEST_P(AttributionStorageSqlTest, +TEST_F(AttributionStorageSqlTest, InvalidSourceOriginOrSite_FailsDeserialization) { const struct { const char* sql; @@ -1558,7 +1518,7 @@ } } -TEST_P(AttributionStorageSqlTest, CreateReport_DeletesUnattributedSources) { +TEST_F(AttributionStorageSqlTest, CreateReport_DeletesUnattributedSources) { OpenDatabase(); storage()->StoreSource(SourceBuilder().Build()); storage()->StoreSource(SourceBuilder().Build()); @@ -1573,7 +1533,7 @@ ExpectImpressionRows(1); } -TEST_P(AttributionStorageSqlTest, CreateReport_DeactivatesAttributedSources) { +TEST_F(AttributionStorageSqlTest, CreateReport_DeactivatesAttributedSources) { OpenDatabase(); storage()->StoreSource(SourceBuilder().SetPriority(1).Build()); MaybeCreateAndStoreEventLevelReport(DefaultTrigger()); @@ -1585,7 +1545,7 @@ } // Tests that invalid filter keys present in the serialized data are removed. -TEST_P(AttributionStorageSqlTest, DeserializeFilterData_RemovesReservedKeys) { +TEST_F(AttributionStorageSqlTest, DeserializeFilterData_RemovesReservedKeys) { { OpenDatabase(); storage()->StoreSource(SourceBuilder().Build()); @@ -1615,7 +1575,7 @@ ElementsAre(Pair("x", ElementsAre("y")))); } -TEST_P(AttributionStorageSqlTest, ReportTablesStoreDestinationOrigin) { +TEST_F(AttributionStorageSqlTest, ReportTablesStoreDestinationOrigin) { constexpr char kDestinationOriginA[] = "https://a.d.test"; constexpr char kDestinationOriginB[] = "https://b.d.test"; @@ -1654,7 +1614,7 @@ } } -TEST_P(AttributionStorageSqlTest, FakeReportUsesSourceOriginAsContext) { +TEST_F(AttributionStorageSqlTest, FakeReportUsesSourceOriginAsContext) { OpenDatabase(); delegate()->set_randomized_response(std::vector<FakeEventLevelReport>{ @@ -1681,7 +1641,7 @@ } } -TEST_P(AttributionStorageSqlTest, +TEST_F(AttributionStorageSqlTest, InvalidExpiryOrReportTime_FailsDeserialization) { static constexpr const char* kUpdateSqls[] = { "UPDATE sources SET expiry_time=?", @@ -1743,7 +1703,7 @@ } } -TEST_P(AttributionStorageSqlTest, +TEST_F(AttributionStorageSqlTest, RandomizedResponseRateNotStored_RecalculatedWhenHandled) { { OpenDatabase(); @@ -1788,7 +1748,7 @@ ElementsAre(RandomizedResponseRateIs(0.2))); } -TEST_P(AttributionStorageSqlTest, EpsilonNotStored_RecalculatedWhenHandled) { +TEST_F(AttributionStorageSqlTest, EpsilonNotStored_RecalculatedWhenHandled) { { OpenDatabase(); storage()->StoreSource(SourceBuilder().Build()); @@ -1833,7 +1793,7 @@ attribution_reporting::EventLevelEpsilon()))); } -TEST_P(AttributionStorageSqlTest, +TEST_F(AttributionStorageSqlTest, TriggerDataNotStored_RecalculatedWhenHandled) { { OpenDatabase(); @@ -1893,7 +1853,7 @@ // Having the missing field default to the correct value allows us to avoid a // DB migration to populate the field. -TEST_P(AttributionStorageSqlTest, +TEST_F(AttributionStorageSqlTest, MissingTriggerDataMatchingProtoField_DefaultsToModulus) { proto::AttributionReadOnlySourceData msg; ASSERT_FALSE(msg.has_trigger_data_matching()); @@ -1901,7 +1861,7 @@ proto::AttributionReadOnlySourceData::MODULUS); } -TEST_P(AttributionStorageSqlTest, InvalidReportingOrigin_FailsDeserialization) { +TEST_F(AttributionStorageSqlTest, InvalidReportingOrigin_FailsDeserialization) { const struct { const char* desc; const char* reporting_origin; @@ -1952,7 +1912,7 @@ } } -TEST_P(AttributionStorageSqlTest, +TEST_F(AttributionStorageSqlTest, InvalidEventLevelMetadata_FailsDeserialization) { const struct { const char* desc; @@ -2042,7 +2002,7 @@ } } -TEST_P(AttributionStorageSqlTest, +TEST_F(AttributionStorageSqlTest, InvalidAggregatableMetadata_FailsDeserialization) { const struct { const char* desc; @@ -2241,7 +2201,7 @@ } } -TEST_P(AttributionStorageSqlTest, +TEST_F(AttributionStorageSqlTest, InvalidNullAggregatableMetadata_FailsDeserialization) { const struct { const char* desc; @@ -2339,7 +2299,7 @@ } } -TEST_P(AttributionStorageSqlTest, +TEST_F(AttributionStorageSqlTest, NullAggregatableReport_ValidSourceMatched_FailsDeserialization) { OpenDatabase(); storage()->StoreSource(SourceBuilder().Build()); @@ -2374,7 +2334,7 @@ 1); } -TEST_P(AttributionStorageSqlTest, +TEST_F(AttributionStorageSqlTest, NullAggregatableReport_CorruptedSourceMatched_FailsDeserialization) { OpenDatabase(); storage()->StoreSource(SourceBuilder().Build()); @@ -2410,7 +2370,7 @@ 1); } -TEST_P(AttributionStorageSqlTest, InvalidStoredReportFields_MarkedAsCorrupted) { +TEST_F(AttributionStorageSqlTest, InvalidStoredReportFields_MarkedAsCorrupted) { const struct { const char* desc; bool source_id_mismatch = false; @@ -2604,7 +2564,7 @@ } } -TEST_P(AttributionStorageSqlTest, +TEST_F(AttributionStorageSqlTest, InvalidReportCorrespondingSourceFields_MarkedAsCorrupted) { base::HistogramTester histograms; OpenDatabase(); @@ -2714,7 +2674,7 @@ histograms.ExpectTotalCount("Conversions.CorruptReportsInDatabase5", 27); } -TEST_P(AttributionStorageSqlTest, SourceDebugKeyAndDebugCookieSetCombination) { +TEST_F(AttributionStorageSqlTest, SourceDebugKeyAndDebugCookieSetCombination) { const struct { const char* desc; std::optional<bool> debug_cookie_set; @@ -2812,11 +2772,5 @@ } } -INSTANTIATE_TEST_SUITE_P( - All, - AttributionStorageSqlTest, - testing::Values(BuiltInRecoveryFeatureFlagState::kDisabled, - BuiltInRecoveryFeatureFlagState::kEnabled)); - } // namespace } // namespace content
diff --git a/content/browser/attribution_reporting/privacy_math.cc b/content/browser/attribution_reporting/privacy_math.cc index 3be3f64c4..448de122 100644 --- a/content/browser/attribution_reporting/privacy_math.cc +++ b/content/browser/attribution_reporting/privacy_math.cc
@@ -30,6 +30,14 @@ namespace { +// Since base/numerics does not support checked math for 128 bit types, +// implement it ourselves. This copies the relevant check from CheckedMulImpl. +absl::uint128 CheckMul(absl::uint128 x, absl::uint128 y) { + // Note this is safe even with division with a remainder. + CHECK(y == 0 || x <= absl::Uint128Max() / y); + return x * y; +} + // The max possible number of state combinations given a valid input. // This comes from 20 maximum total reports, 20 reports per type, 5 windows per // type, and 32 distinct trigger data values. @@ -195,7 +203,7 @@ // Optimized fast-path. if (specs.SingleSharedSpec()) { - int64_t states = internal::GetNumberOfStarsAndBarsSequences( + absl::uint128 states = internal::GetNumberOfStarsAndBarsSequences( /*num_stars=*/max_reports, /*num_bars=*/specs.size() * num_windows); DCHECK_EQ(states, GetNumStatesRecursive(it, max_reports, num_windows, @@ -264,7 +272,7 @@ namespace internal { -int64_t BinomialCoefficient(int n, int k) { +absl::uint128 BinomialCoefficient(int n, int k) { DCHECK_GE(n, 0); DCHECK_GE(k, 0); @@ -284,16 +292,17 @@ } // (n choose k) = n (n -1) ... (n - (k - 1)) / k! - // = mul((n + i - i) / i), i from 1 -> k. + // = mul((n + 1 - i) / i), i from 1 -> k. // // You might be surprised that this algorithm works just fine with integer // division (i.e. division occurs cleanly with no remainder). However, this is // true for a very simple reason. Imagine a value of `i` causes division with // remainder in the below algorithm. This immediately implies that // (n choose i) is fractional, which we know is not the case. - int64_t result = 1; + absl::uint128 result = 1; for (int i = 1; i <= k; i++) { - result = base::CheckMul(result, n + 1 - i).ValueOrDie(); + absl::uint128 term = n + 1 - i; + result = CheckMul(result, term); DCHECK_EQ(0, result % i); result = result / i; } @@ -317,7 +326,8 @@ // // We find this set via a simple greedy algorithm. // http://math0.wvstateu.edu/~baker/cs405/code/Combinadics.html -std::vector<int> GetKCombinationAtIndex(int64_t combination_index, int k) { +std::vector<int> GetKCombinationAtIndex(absl::uint128 combination_index, + int k) { DCHECK_GE(combination_index, 0); DCHECK_GE(k, 0); // `k` can be no more than max number of event level reports per source (20). @@ -333,18 +343,20 @@ // maximum a such that (a choose k) <= `combination_index`. Let a_k = a. Use // the previous binomial coefficient to compute the next one. Note: possible // to speed this up via something other than incremental search. - int64_t target = combination_index; + absl::uint128 target = combination_index; int candidate = k - 1; - int64_t binomial_coefficient = 0; // BinomialCoefficient(candidate, k) - int64_t next_binomial_coefficient = 1; // BinomialCoefficient(candidate+1, k) + + // BinomialCoefficient(candidate, k) + absl::uint128 binomial_coefficient = 0; + // BinomialCoefficient(candidate+1, k) + absl::uint128 next_binomial_coefficient = 1; while (next_binomial_coefficient <= target) { candidate++; binomial_coefficient = next_binomial_coefficient; DCHECK_EQ(binomial_coefficient, BinomialCoefficient(candidate, k)); // (n + 1 choose k) = (n choose k) * (n + 1) / (n + 1 - k) - next_binomial_coefficient = - base::CheckMul(binomial_coefficient, candidate + 1).ValueOrDie(); + next_binomial_coefficient = CheckMul(binomial_coefficient, candidate + 1); next_binomial_coefficient /= candidate + 1 - k; } // We know from the k-combination definition, all subsequent values will be @@ -362,14 +374,15 @@ return output_k_combination; } // (n - 1 choose k - 1) = (n choose k) * k / n - binomial_coefficient = binomial_coefficient * (current_k) / candidate; + binomial_coefficient = + CheckMul(binomial_coefficient, current_k) / candidate; current_k--; candidate--; } else { // (n - 1 choose k) = (n choose k) * (n - k) / n binomial_coefficient = - binomial_coefficient * (candidate - current_k) / candidate; + CheckMul(binomial_coefficient, candidate - current_k) / candidate; candidate--; } @@ -394,13 +407,13 @@ return reports; } -int64_t GetNumberOfStarsAndBarsSequences(int num_stars, int num_bars) { +absl::uint128 GetNumberOfStarsAndBarsSequences(int num_stars, int num_bars) { return BinomialCoefficient(num_stars + num_bars, num_stars); } std::vector<int> GetStarIndices(int num_stars, int num_bars, - int64_t sequence_index) { + absl::uint128 sequence_index) { DCHECK_LT(sequence_index, GetNumberOfStarsAndBarsSequences(num_stars, num_bars)); return GetKCombinationAtIndex(sequence_index, num_stars); @@ -449,7 +462,7 @@ std::vector<FakeEventLevelReport> GetFakeReportsForSequenceIndex( const attribution_reporting::TriggerSpecs& specs, int max_reports, - int64_t random_stars_and_bars_sequence_index) { + absl::uint128 random_stars_and_bars_sequence_index) { const attribution_reporting::TriggerSpec* single_spec = specs.SingleSharedSpec(); CHECK(single_spec); @@ -513,9 +526,7 @@ DCHECK_LT(sequence_index, kMaxNumCombinations); fake_reports = specs.SingleSharedSpec() ? internal::GetFakeReportsForSequenceIndex( - specs, max_reports, - base::checked_cast<int64_t>( - absl::Uint128Low64(sequence_index))) + specs, max_reports, sequence_index) : internal::GetFakeReportsForSequenceIndex( specs, max_reports, sequence_index, map); }
diff --git a/content/browser/attribution_reporting/privacy_math.h b/content/browser/attribution_reporting/privacy_math.h index 1456753..4faa896 100644 --- a/content/browser/attribution_reporting/privacy_math.h +++ b/content/browser/attribution_reporting/privacy_math.h
@@ -100,7 +100,7 @@ // // Note: large values of `n` and `k` may overflow. This function internally uses // checked_math to crash safely if this occurs. -CONTENT_EXPORT int64_t BinomialCoefficient(int n, int k); +CONTENT_EXPORT absl::uint128 BinomialCoefficient(int n, int k); // Returns the k-combination associated with the number `combination_index`. In // other words, returns the combination of `k` integers uniquely indexed by @@ -109,21 +109,21 @@ // // The returned vector is guaranteed to have size `k`. CONTENT_EXPORT std::vector<int> GetKCombinationAtIndex( - int64_t combination_index, + absl::uint128 combination_index, int k); // Returns the number of possible sequences of "stars and bars" sequences // https://en.wikipedia.org/wiki/Stars_and_bars_(combinatorics), // which is equivalent to (num_stars + num_bars choose num_stars). -CONTENT_EXPORT int64_t GetNumberOfStarsAndBarsSequences(int num_stars, - int num_bars); +CONTENT_EXPORT absl::uint128 GetNumberOfStarsAndBarsSequences(int num_stars, + int num_bars); // Returns a vector of the indices of every star in the stars and bars sequence // indexed by `sequence_index`. The indexing technique uses the k-combination // utility documented above. CONTENT_EXPORT std::vector<int> GetStarIndices(int num_stars, int num_bars, - int64_t sequence_index); + absl::uint128 sequence_index); // From a vector with the index of every star in a stars and bars sequence, // returns a vector which, for every star, counts the number of bars preceding @@ -154,7 +154,7 @@ CONTENT_EXPORT std::vector<FakeEventLevelReport> GetFakeReportsForSequenceIndex( const attribution_reporting::TriggerSpecs&, int max_event_level_reports, - int64_t random_stars_and_bars_sequence_index); + absl::uint128 random_stars_and_bars_sequence_index); // Note: this method for sampling is not 1:1 with the above function for the // same sequence index, even for equivalent API configs.
diff --git a/content/browser/attribution_reporting/privacy_math_unittest.cc b/content/browser/attribution_reporting/privacy_math_unittest.cc index 49634dc..29c9188 100644 --- a/content/browser/attribution_reporting/privacy_math_unittest.cc +++ b/content/browser/attribution_reporting/privacy_math_unittest.cc
@@ -28,6 +28,46 @@ using ::attribution_reporting::MaxEventLevelReports; using ::attribution_reporting::mojom::SourceType; +attribution_reporting::TriggerSpecs SpecsFromWindowList( + std::vector<int> windows_per_type, + bool collapse_into_single_spec) { + attribution_reporting::TriggerSpecs::TriggerDataIndices indices; + std::vector<attribution_reporting::TriggerSpec> raw_specs; + + bool supportable_by_single_spec = base::ranges::all_of( + windows_per_type, [&](int w) { return w == windows_per_type[0]; }); + + if (collapse_into_single_spec && supportable_by_single_spec) { + std::vector<base::TimeDelta> deltas; + for (int i = 0; i < windows_per_type[0]; i++) { + deltas.emplace_back(base::Days(1) + base::Days(i)); + } + for (int i = 0; i < static_cast<int>(windows_per_type.size()); ++i) { + indices[i] = 0; + } + raw_specs.emplace_back(*attribution_reporting::EventReportWindows::Create( + base::Days(0), deltas)); + auto specs = + *attribution_reporting::TriggerSpecs::Create(indices, raw_specs); + return specs; + } + + int index = 0; + for (int windows : windows_per_type) { + std::vector<base::TimeDelta> deltas; + for (int i = 0; i < windows; i++) { + deltas.emplace_back(base::Days(1) + base::Days(i)); + } + raw_specs.emplace_back(*attribution_reporting::EventReportWindows::Create( + base::Days(0), deltas)); + indices[index] = index; + index++; + } + + auto specs = *attribution_reporting::TriggerSpecs::Create(indices, raw_specs); + return specs; +} + TEST(PrivacyMathTest, BinomialCoefficient) { // Test cases generated via a python program using scipy.special.comb. struct { @@ -133,9 +173,9 @@ // `index` = \sum_{i=1}^k {a_i}\choose{i} TEST(PrivacyMathTest, GetKCombination_MatchesDefinition) { for (int k = 1; k < 5; k++) { - for (int index = 0; index < 3000; index++) { + for (absl::uint128 index = 0; index < 3000; index++) { std::vector<int> combination = internal::GetKCombinationAtIndex(index, k); - int sum = 0; + absl::uint128 sum = 0; for (int i = 0; i < k; i++) { sum += internal::BinomialCoefficient(combination[i], k - i); } @@ -251,7 +291,7 @@ // https://github.com/WICG/attribution-reporting-api/blob/ab43f8c989cf881ffd7a7f71801b98d649ed164a/flexible-event/privacy.test.ts#L38-L69 TEST(PrivacyMathTest, ComputeChannelCapacity) { const struct { - int64_t num_states; + absl::uint128 num_states; double epsilon; double expected; } kTestCases[] = { @@ -407,7 +447,7 @@ // // The probability that t trials are not enough to see all possible results is // at most n^{-t/(n*ln(n)) + 1}. - EXPECT_EQ(static_cast<int64_t>(output_counts.size()), num_states); + EXPECT_EQ(static_cast<absl::uint128>(output_counts.size()), num_states); // For any of the n possible results, the expected number of times it is seen // is equal to 1/n. Moreover, for any possible result, the probability that it @@ -499,48 +539,50 @@ /*tolerance=*/0.1); } +const struct { + MaxEventLevelReports max_reports; + std::vector<int> windows_per_type; + absl::uint128 expected_num_states; +} kNumStateTestCases[] = { + {MaxEventLevelReports(3), {3, 3, 3, 3, 3, 3, 3, 3}, 2925}, + {MaxEventLevelReports(1), {1, 1}, 3}, + + {MaxEventLevelReports(1), {1}, 2}, + {MaxEventLevelReports(5), {1}, 6}, + {MaxEventLevelReports(2), {1, 1, 2, 2}, 28}, + {MaxEventLevelReports(3), {1, 1, 2, 2, 3, 3}, 455}, + + // Cases for # of states > 10000 will skip the unique check, otherwise the + // tests won't ever finish. + {MaxEventLevelReports(20), {5, 5, 5, 5, 5, 5, 5, 5}, 4191844505805495}, + + // This input would overflow any 64 bit integer. + {MaxEventLevelReports(20), + {5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5}, + absl::MakeUint128(/*high=*/9494472u, /*low=*/10758590974061625903u)}, +}; + +TEST(PrivacyMathTest, GetNumStates) { + for (const auto& test_case : kNumStateTestCases) { + // Test both single spec and multi-spec variants to ensure both code paths + // (optimized and non) get exercised. + auto specs = SpecsFromWindowList(test_case.windows_per_type, + /*collapse_into_single_spec=*/true); + EXPECT_EQ(test_case.expected_num_states, + GetNumStates(specs, test_case.max_reports)); + + specs = SpecsFromWindowList(test_case.windows_per_type, + /*collapse_into_single_spec=*/false); + EXPECT_EQ(test_case.expected_num_states, + GetNumStates(specs, test_case.max_reports)); + } +} + TEST(PrivacyMathTest, NumStatesForTriggerSpecs_UniqueSampling) { - const struct { - MaxEventLevelReports max_reports; - std::vector<int> windows_per_type; - absl::uint128 expected_num_states; - } kTestCases[] = { - {MaxEventLevelReports(3), {3, 3, 3, 3, 3, 3, 3, 3}, 2925}, - {MaxEventLevelReports(1), {1, 1}, 3}, - - {MaxEventLevelReports(1), {1}, 2}, - {MaxEventLevelReports(5), {1}, 6}, - {MaxEventLevelReports(2), {1, 1, 2, 2}, 28}, - {MaxEventLevelReports(3), {1, 1, 2, 2, 3, 3}, 455}, - - // Cases for # of states > 10000 will skip the unique check, otherwise the - // tests won't ever finish. - {MaxEventLevelReports(20), {5, 5, 5, 5, 5, 5, 5, 5}, 4191844505805495}, - - // This input would overflow any 64 bit integer. - {MaxEventLevelReports(20), - {5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5}, - absl::MakeUint128(/*high=*/9494472u, /*low=*/10758590974061625903u)}, - }; - - for (const auto& test_case : kTestCases) { - attribution_reporting::TriggerSpecs::TriggerDataIndices indices; - std::vector<attribution_reporting::TriggerSpec> raw_specs; - int index = 0; - for (int windows : test_case.windows_per_type) { - std::vector<base::TimeDelta> deltas; - for (int i = 0; i < windows; i++) { - deltas.emplace_back(base::Days(1) + base::Days(i)); - } - raw_specs.emplace_back(*attribution_reporting::EventReportWindows::Create( - base::Days(0), deltas)); - indices[index] = index; - index++; - } - - auto specs = - *attribution_reporting::TriggerSpecs::Create(indices, raw_specs); + for (const auto& test_case : kNumStateTestCases) { + auto specs = SpecsFromWindowList(test_case.windows_per_type, + /*collapse_into_single_spec=*/false); ASSERT_EQ(test_case.expected_num_states, GetNumStates(specs, test_case.max_reports));
diff --git a/content/browser/bluetooth/advertisement_client.cc b/content/browser/bluetooth/advertisement_client.cc index 0023549..bf1370b 100644 --- a/content/browser/bluetooth/advertisement_client.cc +++ b/content/browser/bluetooth/advertisement_client.cc
@@ -5,6 +5,7 @@ #include "content/browser/bluetooth/advertisement_client.h" #include <utility> +#include <vector> #include "content/browser/bluetooth/bluetooth_blocklist.h" #include "content/browser/bluetooth/bluetooth_metrics.h" @@ -62,7 +63,7 @@ } auto filtered_event = event.Clone(); - base::EraseIf(filtered_event->uuids, [this](const BluetoothUUID& uuid) { + std::erase_if(filtered_event->uuids, [this](const BluetoothUUID& uuid) { return !service_->IsAllowedToAccessService(device_id_, uuid); }); base::EraseIf(
diff --git a/content/browser/bluetooth/web_bluetooth_service_impl.cc b/content/browser/bluetooth/web_bluetooth_service_impl.cc index 46818d4..7ff0f0ed 100644 --- a/content/browser/bluetooth/web_bluetooth_service_impl.cc +++ b/content/browser/bluetooth/web_bluetooth_service_impl.cc
@@ -12,9 +12,9 @@ #include <memory> #include <utility> +#include <vector> #include "base/containers/contains.h" -#include "base/containers/cxx20_erase.h" #include "base/containers/queue.h" #include "base/functional/bind.h" #include "base/functional/callback_helpers.h" @@ -540,7 +540,7 @@ connected_devices_->CloseConnectionsToDevicesNotInList(permitted_ids); - base::EraseIf(watch_advertisements_clients_, + std::erase_if(watch_advertisements_clients_, [&](const std::unique_ptr<WatchAdvertisementsClient>& client) { return !base::Contains(permitted_ids, client->device_id()); }); @@ -1452,11 +1452,11 @@ // TODO(https://crbug.com/1087007): These two classes can potentially be // combined into the same container. - base::EraseIf(scanning_clients_, + std::erase_if(scanning_clients_, [](const std::unique_ptr<ScanningClient>& client) { return !client->is_connected(); }); - base::EraseIf(watch_advertisements_clients_, + std::erase_if(watch_advertisements_clients_, [](const std::unique_ptr<WatchAdvertisementsClient>& client) { return !client->is_connected(); });
diff --git a/content/browser/browsing_topics/browsing_topics_site_data_storage.cc b/content/browser/browsing_topics/browsing_topics_site_data_storage.cc index 82d4bd5..92400bc 100644 --- a/content/browser/browsing_topics/browsing_topics_site_data_storage.cc +++ b/content/browser/browsing_topics/browsing_topics_site_data_storage.cc
@@ -26,14 +26,6 @@ } // namespace -// When enabled, prefer to use the new recovery module to recover the -// `BrowsingTopicsSiteDataStorage` database. See https://crbug.com/1385500 for -// details. This is a kill switch and is not intended to be used in a field -// trial. -BASE_FEATURE(kBrowsingTopicsSiteDataStorageUseBuiltInRecoveryIfSupported, - "BrowsingTopicsSiteDataStorageUseBuiltInRecoveryIfSupported", - base::FEATURE_ENABLED_BY_DEFAULT); - BrowsingTopicsSiteDataStorage::BrowsingTopicsSiteDataStorage( const base::FilePath& path_to_database) : path_to_database_(path_to_database) { @@ -353,8 +345,7 @@ // Attempt to recover a corrupt database, if it is eligible to be recovered. if (sql::BuiltInRecovery::RecoverIfPossible( db_.get(), extended_error, - sql::BuiltInRecovery::Strategy::kRecoverWithMetaVersionOrRaze, - &kBrowsingTopicsSiteDataStorageUseBuiltInRecoveryIfSupported)) { + sql::BuiltInRecovery::Strategy::kRecoverWithMetaVersionOrRaze)) { // Recovery was attempted. The database handle has been poisoned and the // error callback has been reset.
diff --git a/content/browser/browsing_topics/browsing_topics_site_data_storage.h b/content/browser/browsing_topics/browsing_topics_site_data_storage.h index a3e46ef1..0c8e0ed2 100644 --- a/content/browser/browsing_topics/browsing_topics_site_data_storage.h +++ b/content/browser/browsing_topics/browsing_topics_site_data_storage.h
@@ -27,9 +27,6 @@ namespace content { -BASE_DECLARE_FEATURE( - kBrowsingTopicsSiteDataStorageUseBuiltInRecoveryIfSupported); - class CONTENT_EXPORT BrowsingTopicsSiteDataStorage { public: explicit BrowsingTopicsSiteDataStorage(
diff --git a/content/browser/child_process_security_policy_impl.cc b/content/browser/child_process_security_policy_impl.cc index 1792a7f..212357a 100644 --- a/content/browser/child_process_security_policy_impl.cc +++ b/content/browser/child_process_security_policy_impl.cc
@@ -6,10 +6,10 @@ #include <tuple> #include <utility> +#include <vector> #include "base/command_line.h" #include "base/containers/contains.h" -#include "base/containers/cxx20_erase.h" #include "base/debug/crash_logging.h" #include "base/debug/dump_without_crashing.h" #include "base/feature_list.h" @@ -2301,7 +2301,7 @@ base::AutoLock isolated_origins_lock(isolated_origins_lock_); for (auto& iter : isolated_origins_) { - base::EraseIf(iter.second, + std::erase_if(iter.second, [&browser_context](const IsolatedOriginEntry& entry) { // Remove if BrowserContext matches. return (entry.browser_context() == &browser_context); @@ -2702,7 +2702,7 @@ { base::AutoLock isolated_origins_lock(isolated_origins_lock_); for (auto& iter : isolated_origins_) { - base::EraseIf(iter.second, [&browsing_instance_id]( + std::erase_if(iter.second, [&browsing_instance_id]( const IsolatedOriginEntry& entry) { // Remove entries that are specific to `browsing_instance_id` and // do not apply to future BrowsingInstances. @@ -2828,7 +2828,7 @@ const url::Origin& origin) { GURL key(SiteInfo::GetSiteForOrigin(origin)); base::AutoLock isolated_origins_lock(isolated_origins_lock_); - base::EraseIf(isolated_origins_[key], + std::erase_if(isolated_origins_[key], [&origin](const IsolatedOriginEntry& entry) { // Remove if origin matches. return (entry.origin() == origin);
diff --git a/content/browser/content_index/content_index_database.cc b/content/browser/content_index/content_index_database.cc index 52505750..38950c3 100644 --- a/content/browser/content_index/content_index_database.cc +++ b/content/browser/content_index/content_index_database.cc
@@ -7,9 +7,9 @@ #include <optional> #include <set> #include <string> +#include <vector> #include "base/barrier_closure.h" -#include "base/containers/cxx20_erase.h" #include "base/memory/ptr_util.h" #include "base/time/time.h" #include "content/browser/background_fetch/storage/image_helpers.h" @@ -474,7 +474,7 @@ if (!corrupted_sw_ids.empty()) { // Remove soon-to-be-deleted entries. - base::EraseIf(entries, [&corrupted_sw_ids](const auto& entry) { + std::erase_if(entries, [&corrupted_sw_ids](const auto& entry) { return corrupted_sw_ids.count(entry.service_worker_registration_id); });
diff --git a/content/browser/devtools/devtools_agent_host_impl.cc b/content/browser/devtools/devtools_agent_host_impl.cc index d9ad8f6..f16aff8 100644 --- a/content/browser/devtools/devtools_agent_host_impl.cc +++ b/content/browser/devtools/devtools_agent_host_impl.cc
@@ -9,7 +9,6 @@ #include "base/command_line.h" #include "base/containers/contains.h" -#include "base/containers/cxx20_erase.h" #include "base/functional/bind.h" #include "base/memory/ref_counted_memory.h" #include "base/no_destructor.h" @@ -354,7 +353,7 @@ DCHECK_EQ(session, session_owned.get()); // Make sure we dispose session prior to reporting it to the host. session->Dispose(); - base::Erase(sessions_, session); + std::erase(sessions_, session); session_by_client_.erase(session->GetClient()); DetachSession(session); DevToolsManager* manager = DevToolsManager::GetInstance();
diff --git a/content/browser/first_party_sets/database/first_party_sets_database.cc b/content/browser/first_party_sets/database/first_party_sets_database.cc index 6d20e2b..8cda672c 100644 --- a/content/browser/first_party_sets/database/first_party_sets_database.cc +++ b/content/browser/first_party_sets/database/first_party_sets_database.cc
@@ -34,13 +34,6 @@ namespace content { -// When enabled, prefer to use the new recovery module to recover the -// `FirstPartySetsDatabase` database. See https://crbug.com/1385500 for details. -// This is a kill switch and is not intended to be used in a field trial. -BASE_FEATURE(kFirstPartySetsDatabaseUseBuiltInRecoveryIfSupported, - "FirstPartySetsDatabaseUseBuiltInRecoveryIfSupported", - base::FEATURE_ENABLED_BY_DEFAULT); - namespace { // Version number of the database. @@ -743,8 +736,7 @@ // Attempt to recover a corrupt database, if it is eligible to be recovered. if (sql::BuiltInRecovery::RecoverIfPossible( db_.get(), extended_error, - sql::BuiltInRecovery::Strategy::kRecoverWithMetaVersionOrRaze, - &kFirstPartySetsDatabaseUseBuiltInRecoveryIfSupported)) { + sql::BuiltInRecovery::Strategy::kRecoverWithMetaVersionOrRaze)) { // Recovery was attempted. The database handle has been poisoned and the // error callback has been reset.
diff --git a/content/browser/first_party_sets/database/first_party_sets_database.h b/content/browser/first_party_sets/database/first_party_sets_database.h index 624dccb3..1ebfc57 100644 --- a/content/browser/first_party_sets/database/first_party_sets_database.h +++ b/content/browser/first_party_sets/database/first_party_sets_database.h
@@ -34,8 +34,6 @@ namespace content { -BASE_DECLARE_FEATURE(kFirstPartySetsDatabaseUseBuiltInRecoveryIfSupported); - // Wraps its own `sql::Database` instance on behalf of the First-Party Sets // database implementation. This class must be accessed and destroyed on the // same sequence. The sequence must outlive |this|.
diff --git a/content/browser/first_party_sets/first_party_set_parser.cc b/content/browser/first_party_sets/first_party_set_parser.cc index 5465b78..d42edb0 100644 --- a/content/browser/first_party_sets/first_party_set_parser.cc +++ b/content/browser/first_party_sets/first_party_set_parser.cc
@@ -348,14 +348,14 @@ // Erase invalid members/primaries, and collect primary sites that might // become singletons. - base::EraseIf( + std::erase_if( sets, [&](const std::pair<net::SchemefulSite, net::FirstPartySetEntry>& pair) -> bool { return IsInvalidEntry(pair, &possible_singletons); }); // Erase invalid aliases, and collect canonical sites that are primaries and // might become singletons. - base::EraseIf( + std::erase_if( aliases, [&](const std::pair<net::SchemefulSite, net::SchemefulSite>& pair) -> bool { @@ -390,7 +390,7 @@ return; } - base::EraseIf( + std::erase_if( sets, [&](const std::pair<net::SchemefulSite, net::FirstPartySetEntry>& pair) -> bool { return possible_singletons.contains(pair.first); }); @@ -425,8 +425,8 @@ const auto is_singleton = [](const SingleSet& set) { return set.size() <= 1; }; - base::EraseIf(lists.additions, is_singleton); - base::EraseIf(lists.replacements, is_singleton); + std::erase_if(lists.additions, is_singleton); + std::erase_if(lists.replacements, is_singleton); } std::vector<ParseWarning>& warnings() { return warnings_; }
diff --git a/content/browser/indexed_db/BUILD.gn b/content/browser/indexed_db/BUILD.gn index a9418ef..63d37f8 100644 --- a/content/browser/indexed_db/BUILD.gn +++ b/content/browser/indexed_db/BUILD.gn
@@ -16,7 +16,6 @@ "file_path_util.h", "file_stream_reader_to_data_pipe.cc", "file_stream_reader_to_data_pipe.h", - "indexed_db.h", "indexed_db_active_blob_registry.cc", "indexed_db_active_blob_registry.h", "indexed_db_backing_store.cc",
diff --git a/content/browser/indexed_db/database_impl.cc b/content/browser/indexed_db/database_impl.cc index 5cce4d7..18ca9d4 100644 --- a/content/browser/indexed_db/database_impl.cc +++ b/content/browser/indexed_db/database_impl.cc
@@ -17,6 +17,7 @@ #include "components/services/storage/privileged/mojom/indexed_db_client_state_checker.mojom.h" #include "content/browser/indexed_db/indexed_db_callback_helpers.h" #include "content/browser/indexed_db/indexed_db_connection.h" +#include "content/browser/indexed_db/indexed_db_cursor.h" #include "content/browser/indexed_db/indexed_db_database_callbacks.h" #include "content/browser/indexed_db/indexed_db_transaction.h" #include "mojo/public/cpp/bindings/callback_helpers.h" @@ -195,7 +196,8 @@ transaction->ScheduleTask(BindWeakOperation( &IndexedDBDatabase::GetOperation, connection_->database()->AsWeakPtr(), object_store_id, index_id, std::make_unique<IndexedDBKeyRange>(key_range), - key_only ? indexed_db::CURSOR_KEY_ONLY : indexed_db::CURSOR_KEY_AND_VALUE, + key_only ? indexed_db::CursorType::kKeyOnly + : indexed_db::CursorType::kKeyAndValue, std::move(aborting_callback))); } @@ -259,7 +261,8 @@ transaction->ScheduleTask(BindWeakOperation( &IndexedDBDatabase::GetAllOperation, connection_->database()->AsWeakPtr(), object_store_id, index_id, std::make_unique<IndexedDBKeyRange>(key_range), - key_only ? indexed_db::CURSOR_KEY_ONLY : indexed_db::CURSOR_KEY_AND_VALUE, + key_only ? indexed_db::CursorType::kKeyOnly + : indexed_db::CursorType::kKeyAndValue, max_count, std::move(aborting_callback))); } @@ -398,8 +401,8 @@ params->index_id = index_id; params->key_range = std::make_unique<IndexedDBKeyRange>(key_range); params->direction = direction; - params->cursor_type = - key_only ? indexed_db::CURSOR_KEY_ONLY : indexed_db::CURSOR_KEY_AND_VALUE; + params->cursor_type = key_only ? indexed_db::CursorType::kKeyOnly + : indexed_db::CursorType::kKeyAndValue; params->task_type = task_type; params->callback = std::move(aborting_callback); transaction->ScheduleTask(
diff --git a/content/browser/indexed_db/indexed_db.h b/content/browser/indexed_db/indexed_db.h deleted file mode 100644 index f5078d3..0000000 --- a/content/browser/indexed_db/indexed_db.h +++ /dev/null
@@ -1,21 +0,0 @@ -// Copyright 2013 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CONTENT_BROWSER_INDEXED_DB_INDEXED_DB_H_ -#define CONTENT_BROWSER_INDEXED_DB_INDEXED_DB_H_ - -namespace content { - -namespace indexed_db { - -enum CursorType { - CURSOR_KEY_AND_VALUE = 0, - CURSOR_KEY_ONLY -}; - -} // namespace indexed_db - -} // namespace content - -#endif // CONTENT_BROWSER_INDEXED_DB_INDEXED_DB_H_
diff --git a/content/browser/indexed_db/indexed_db_backing_store.h b/content/browser/indexed_db/indexed_db_backing_store.h index aa72cc5..558a2de 100644 --- a/content/browser/indexed_db/indexed_db_backing_store.h +++ b/content/browser/indexed_db/indexed_db_backing_store.h
@@ -28,7 +28,6 @@ #include "components/services/storage/indexed_db/locks/partitioned_lock.h" #include "components/services/storage/privileged/mojom/indexed_db_control_test.mojom.h" #include "components/services/storage/public/cpp/buckets/bucket_locator.h" -#include "content/browser/indexed_db/indexed_db.h" #include "content/browser/indexed_db/indexed_db_external_object.h" #include "content/browser/indexed_db/indexed_db_external_object_storage.h" #include "content/browser/indexed_db/indexed_db_leveldb_coding.h"
diff --git a/content/browser/indexed_db/indexed_db_bucket_context.cc b/content/browser/indexed_db/indexed_db_bucket_context.cc index 8da6aee..3f9a7b4 100644 --- a/content/browser/indexed_db/indexed_db_bucket_context.cc +++ b/content/browser/indexed_db/indexed_db_bucket_context.cc
@@ -4,63 +4,100 @@ #include "content/browser/indexed_db/indexed_db_bucket_context.h" +#include <inttypes.h> +#include <stddef.h> +#include <atomic> +#include <compare> #include <list> +#include <ostream> +#include <set> +#include <type_traits> #include <utility> #include <vector> -#include "base/command_line.h" +#include "base/check.h" +#include "base/check_op.h" +#include "base/compiler_specific.h" #include "base/containers/contains.h" -#include "base/feature_list.h" #include "base/files/file_util.h" #include "base/functional/bind.h" #include "base/functional/callback_helpers.h" +#include "base/location.h" +#include "base/logging.h" +#include "base/metrics/histogram_base.h" #include "base/metrics/histogram_functions.h" #include "base/metrics/histogram_macros.h" +#include "base/no_destructor.h" +#include "base/numerics/checked_math.h" +#include "base/numerics/safe_conversions.h" #include "base/rand_util.h" +#include "base/strings/strcat.h" +#include "base/strings/string_util.h" #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" #include "base/synchronization/waitable_event.h" +#include "base/system/sys_info.h" +#include "base/task/sequenced_task_runner.h" +#include "base/task/task_runner.h" +#include "base/task/task_traits.h" #include "base/task/thread_pool.h" #include "base/timer/elapsed_timer.h" -#include "base/trace_event/base_tracing.h" +#include "base/trace_event/common/trace_event_common.h" +#include "base/trace_event/memory_allocator_dump.h" #include "base/trace_event/memory_dump_manager.h" #include "base/trace_event/process_memory_dump.h" #include "base/uuid.h" #include "build/build_config.h" +#include "components/services/storage/indexed_db/leveldb/leveldb_state.h" +#include "components/services/storage/indexed_db/locks/partitioned_lock_manager.h" #include "components/services/storage/indexed_db/scopes/leveldb_scopes.h" #include "components/services/storage/indexed_db/transactional_leveldb/transactional_leveldb_database.h" #include "components/services/storage/indexed_db/transactional_leveldb/transactional_leveldb_factory.h" #include "components/services/storage/indexed_db/transactional_leveldb/transactional_leveldb_transaction.h" #include "components/services/storage/privileged/mojom/indexed_db_client_state_checker.mojom.h" -#include "components/services/storage/privileged/mojom/indexed_db_control.mojom.h" +#include "components/services/storage/privileged/mojom/indexed_db_internals_types.mojom-shared.h" +#include "components/services/storage/privileged/mojom/indexed_db_internals_types.mojom.h" +#include "components/services/storage/public/mojom/blob_storage_context.mojom-shared.h" #include "components/services/storage/public/mojom/blob_storage_context.mojom.h" #include "content/browser/indexed_db/features.h" #include "content/browser/indexed_db/file_path_util.h" #include "content/browser/indexed_db/file_stream_reader_to_data_pipe.h" #include "content/browser/indexed_db/indexed_db_active_blob_registry.h" #include "content/browser/indexed_db/indexed_db_backing_store.h" -#include "content/browser/indexed_db/indexed_db_bucket_context.h" #include "content/browser/indexed_db/indexed_db_bucket_context_handle.h" #include "content/browser/indexed_db/indexed_db_compaction_task.h" #include "content/browser/indexed_db/indexed_db_connection.h" -#include "content/browser/indexed_db/indexed_db_context_impl.h" #include "content/browser/indexed_db/indexed_db_data_format_version.h" +#include "content/browser/indexed_db/indexed_db_data_loss_info.h" #include "content/browser/indexed_db/indexed_db_database.h" #include "content/browser/indexed_db/indexed_db_database_callbacks.h" #include "content/browser/indexed_db/indexed_db_database_error.h" +#include "content/browser/indexed_db/indexed_db_external_object.h" #include "content/browser/indexed_db/indexed_db_factory_client.h" #include "content/browser/indexed_db/indexed_db_leveldb_coding.h" #include "content/browser/indexed_db/indexed_db_leveldb_operations.h" #include "content/browser/indexed_db/indexed_db_pending_connection.h" #include "content/browser/indexed_db/indexed_db_pre_close_task_queue.h" #include "content/browser/indexed_db/indexed_db_reporting.h" -#include "content/browser/indexed_db/indexed_db_task_helper.h" #include "content/browser/indexed_db/indexed_db_tombstone_sweeper.h" #include "content/browser/indexed_db/indexed_db_transaction.h" +#include "content/browser/indexed_db/list_set.h" #include "content/browser/indexed_db/mock_browsertest_indexed_db_class_factory.h" +#include "env_chromium.h" +#include "mojo/public/cpp/base/big_buffer.h" +#include "mojo/public/cpp/bindings/pending_associated_receiver.h" +#include "mojo/public/cpp/bindings/pending_associated_remote.h" +#include "mojo/public/cpp/bindings/pending_receiver.h" +#include "mojo/public/cpp/bindings/pending_remote.h" +#include "mojo/public/cpp/bindings/struct_ptr.h" +#include "mojo/public/cpp/system/data_pipe.h" #include "net/base/net_errors.h" #include "storage/browser/file_system/file_stream_reader.h" #include "storage/browser/quota/quota_manager_proxy.h" +#include "third_party/blink/public/common/indexeddb/indexeddb_metadata.h" +#include "third_party/blink/public/common/storage_key/storage_key.h" +#include "third_party/blink/public/mojom/file_system_access/file_system_access_transfer_token.mojom.h" +#include "third_party/blink/public/mojom/indexeddb/indexeddb.mojom-shared.h" #include "third_party/blink/public/mojom/indexeddb/indexeddb.mojom.h" #include "third_party/leveldatabase/leveldb_chrome.h"
diff --git a/content/browser/indexed_db/indexed_db_connection_coordinator.cc b/content/browser/indexed_db/indexed_db_connection_coordinator.cc index 3ad2bda..dd06e8aa 100644 --- a/content/browser/indexed_db/indexed_db_connection_coordinator.cc +++ b/content/browser/indexed_db/indexed_db_connection_coordinator.cc
@@ -4,33 +4,54 @@ #include "content/browser/indexed_db/indexed_db_connection_coordinator.h" +#include <atomic> +#include <map> #include <set> +#include <string> #include <tuple> +#include <type_traits> #include <utility> #include <vector> #include "base/auto_reset.h" +#include "base/check.h" +#include "base/check_op.h" +#include "base/functional/bind.h" #include "base/functional/callback_helpers.h" +#include "base/functional/callback_tags.h" #include "base/memory/raw_ptr.h" +#include "base/memory/scoped_refptr.h" +#include "base/metrics/histogram_base.h" #include "base/metrics/histogram_functions.h" #include "base/metrics/histogram_macros.h" +#include "base/notreached.h" #include "base/strings/string_number_conversions.h" +#include "components/services/storage/indexed_db/locks/partitioned_lock.h" #include "components/services/storage/indexed_db/locks/partitioned_lock_manager.h" #include "components/services/storage/indexed_db/scopes/leveldb_scope.h" #include "components/services/storage/indexed_db/scopes/leveldb_scopes.h" #include "components/services/storage/indexed_db/transactional_leveldb/transactional_leveldb_database.h" #include "components/services/storage/indexed_db/transactional_leveldb/transactional_leveldb_factory.h" #include "components/services/storage/indexed_db/transactional_leveldb/transactional_leveldb_transaction.h" +#include "components/services/storage/privileged/mojom/indexed_db_client_state_checker.mojom.h" +#include "content/browser/indexed_db/indexed_db_backing_store.h" #include "content/browser/indexed_db/indexed_db_bucket_context.h" +#include "content/browser/indexed_db/indexed_db_bucket_context_handle.h" #include "content/browser/indexed_db/indexed_db_callback_helpers.h" +#include "content/browser/indexed_db/indexed_db_connection.h" +#include "content/browser/indexed_db/indexed_db_data_loss_info.h" #include "content/browser/indexed_db/indexed_db_database.h" #include "content/browser/indexed_db/indexed_db_database_callbacks.h" #include "content/browser/indexed_db/indexed_db_database_error.h" #include "content/browser/indexed_db/indexed_db_factory_client.h" #include "content/browser/indexed_db/indexed_db_leveldb_coding.h" +#include "content/browser/indexed_db/indexed_db_pending_connection.h" #include "content/browser/indexed_db/indexed_db_reporting.h" +#include "content/browser/indexed_db/indexed_db_task_helper.h" +#include "content/browser/indexed_db/indexed_db_transaction.h" +#include "content/browser/indexed_db/list_set.h" #include "third_party/blink/public/common/indexeddb/indexeddb_metadata.h" -#include "third_party/blink/public/mojom/indexeddb/indexeddb.mojom.h" +#include "third_party/blink/public/mojom/indexeddb/indexeddb.mojom-shared.h" #include "third_party/leveldatabase/env_chromium.h" using base::NumberToString16;
diff --git a/content/browser/indexed_db/indexed_db_context_impl.cc b/content/browser/indexed_db/indexed_db_context_impl.cc index a578367..a64e482 100644 --- a/content/browser/indexed_db/indexed_db_context_impl.cc +++ b/content/browser/indexed_db/indexed_db_context_impl.cc
@@ -4,48 +4,69 @@ #include "content/browser/indexed_db/indexed_db_context_impl.h" +#include <algorithm> +#include <compare> +#include <functional> #include <memory> +#include <ostream> #include <string> -#include <string_view> +#include <type_traits> #include <utility> #include <vector> #include "base/barrier_callback.h" +#include "base/check.h" #include "base/check_op.h" +#include "base/containers/contains.h" +#include "base/files/file.h" #include "base/files/file_enumerator.h" #include "base/files/file_path.h" #include "base/files/file_util.h" #include "base/files/scoped_temp_dir.h" #include "base/functional/bind.h" #include "base/functional/callback_helpers.h" +#include "base/location.h" +#include "base/numerics/clamped_math.h" #include "base/ranges/algorithm.h" -#include "base/strings/string_util.h" +#include "base/strings/string_number_conversions.h" #include "base/task/bind_post_task.h" #include "base/task/sequenced_task_runner.h" +#include "base/task/task_runner.h" +#include "base/task/task_traits.h" #include "base/task/thread_pool.h" -#include "base/threading/thread_restrictions.h" #include "base/time/time.h" #include "base/trace_event/base_tracing.h" -#include "base/values.h" +#include "base/trace_event/common/trace_event_common.h" +#include "base/types/expected.h" +#include "base/types/strong_alias.h" +#include "build/build_config.h" +#include "components/services/storage/privileged/mojom/indexed_db_control.mojom-shared.h" #include "components/services/storage/privileged/mojom/indexed_db_internals_types.mojom.h" #include "components/services/storage/public/cpp/buckets/bucket_info.h" +#include "components/services/storage/public/cpp/buckets/bucket_init_params.h" #include "components/services/storage/public/cpp/buckets/bucket_locator.h" -#include "components/services/storage/public/cpp/buckets/constants.h" #include "components/services/storage/public/cpp/constants.h" #include "components/services/storage/public/cpp/quota_error_or.h" #include "components/services/storage/public/mojom/quota_client.mojom.h" +#include "components/services/storage/public/mojom/storage_policy_update.mojom.h" #include "content/browser/indexed_db/features.h" #include "content/browser/indexed_db/file_path_util.h" #include "content/browser/indexed_db/indexed_db_bucket_context.h" +#include "content/browser/indexed_db/indexed_db_database_error.h" #include "content/browser/indexed_db/indexed_db_factory_client.h" +#include "content/browser/indexed_db/indexed_db_leveldb_coding.h" +#include "mojo/public/cpp/bindings/pending_associated_receiver.h" #include "mojo/public/cpp/bindings/pending_associated_remote.h" #include "mojo/public/cpp/bindings/pending_remote.h" #include "mojo/public/cpp/bindings/self_owned_receiver.h" +#include "mojo/public/cpp/bindings/struct_ptr.h" +#include "net/base/schemeful_site.h" #include "storage/browser/quota/quota_client_type.h" +#include "storage/browser/quota/quota_manager_proxy.h" #include "storage/common/database/database_identifier.h" +#include "third_party/blink/public/common/storage_key/storage_key.h" +#include "third_party/blink/public/mojom/indexeddb/indexeddb.mojom-shared.h" #include "third_party/blink/public/mojom/indexeddb/indexeddb.mojom.h" -#include "third_party/blink/public/mojom/quota/quota_types.mojom-shared.h" -#include "third_party/blink/public/mojom/quota/quota_types.mojom.h" #include "third_party/zlib/google/zip.h" #include "url/origin.h"
diff --git a/content/browser/indexed_db/indexed_db_control_wrapper.cc b/content/browser/indexed_db/indexed_db_control_wrapper.cc index 8210250..27e0165 100644 --- a/content/browser/indexed_db/indexed_db_control_wrapper.cc +++ b/content/browser/indexed_db/indexed_db_control_wrapper.cc
@@ -4,7 +4,16 @@ #include "content/browser/indexed_db/indexed_db_control_wrapper.h" -#include "base/task/sequenced_task_runner.h" +#include <ostream> +#include <utility> + +#include "base/check.h" +#include "base/functional/bind.h" +#include "components/services/storage/public/cpp/buckets/bucket_locator.h" +#include "components/services/storage/public/mojom/storage_policy_update.mojom.h" +#include "content/browser/indexed_db/indexed_db_context_impl.h" +#include "mojo/public/cpp/bindings/pending_receiver.h" +#include "mojo/public/cpp/bindings/pending_remote.h" #include "third_party/blink/public/common/storage_key/storage_key.h" namespace content {
diff --git a/content/browser/indexed_db/indexed_db_cursor.cc b/content/browser/indexed_db/indexed_db_cursor.cc index ec0e152e..0d3a035f 100644 --- a/content/browser/indexed_db/indexed_db_cursor.cc +++ b/content/browser/indexed_db/indexed_db_cursor.cc
@@ -304,10 +304,10 @@ found_primary_keys.push_back(cursor_->primary_key()); switch (cursor_type_) { - case indexed_db::CURSOR_KEY_ONLY: + case indexed_db::CursorType::kKeyOnly: found_values.push_back(IndexedDBValue()); break; - case indexed_db::CURSOR_KEY_AND_VALUE: { + case indexed_db::CursorType::kKeyAndValue: { IndexedDBValue value; value.swap(*cursor_->value()); size_estimate += value.SizeEstimate();
diff --git a/content/browser/indexed_db/indexed_db_cursor.h b/content/browser/indexed_db/indexed_db_cursor.h index 3a7c803..0e22fa2 100644 --- a/content/browser/indexed_db/indexed_db_cursor.h +++ b/content/browser/indexed_db/indexed_db_cursor.h
@@ -24,6 +24,10 @@ namespace content { +namespace indexed_db { +enum class CursorType { kKeyAndValue = 0, kKeyOnly = 1 }; +} // namespace indexed_db + class IndexedDBCursor : public blink::mojom::IDBCursor { public: // Creates a new self-owned instance and binds to `pending_remote`. @@ -54,8 +58,9 @@ return cursor_->primary_key(); } IndexedDBValue* Value() const { - return (cursor_type_ == indexed_db::CURSOR_KEY_ONLY) ? nullptr - : cursor_->value(); + return (cursor_type_ == indexed_db::CursorType::kKeyOnly) + ? nullptr + : cursor_->value(); } void Close();
diff --git a/content/browser/indexed_db/indexed_db_database.cc b/content/browser/indexed_db/indexed_db_database.cc index 689dab8..d4828f7 100644 --- a/content/browser/indexed_db/indexed_db_database.cc +++ b/content/browser/indexed_db/indexed_db_database.cc
@@ -744,7 +744,7 @@ } else { if (index_id == IndexedDBIndexMetadata::kInvalidId) { // ObjectStore Retrieval Operation - if (cursor_type == indexed_db::CURSOR_KEY_ONLY) { + if (cursor_type == indexed_db::CursorType::kKeyOnly) { backing_store_cursor = backing_store()->OpenObjectStoreKeyCursor( transaction->BackingStoreTransaction(), id(), object_store_id, *key_range, blink::mojom::IDBCursorDirection::Next, &s); @@ -753,7 +753,7 @@ transaction->BackingStoreTransaction(), id(), object_store_id, *key_range, blink::mojom::IDBCursorDirection::Next, &s); } - } else if (cursor_type == indexed_db::CURSOR_KEY_ONLY) { + } else if (cursor_type == indexed_db::CursorType::kKeyOnly) { // Index Value Retrieval Operation backing_store_cursor = backing_store()->OpenIndexKeyCursor( transaction->BackingStoreTransaction(), id(), object_store_id, @@ -802,7 +802,7 @@ return s; } - if (cursor_type == indexed_db::CURSOR_KEY_ONLY) { + if (cursor_type == indexed_db::CursorType::kKeyOnly) { std::move(callback).Run( blink::mojom::IDBDatabaseGetResult::NewKey(std::move(*key))); return s; @@ -839,7 +839,7 @@ std::move(callback).Run(blink::mojom::IDBDatabaseGetResult::NewEmpty(true)); return s; } - if (cursor_type == indexed_db::CURSOR_KEY_ONLY) { + if (cursor_type == indexed_db::CursorType::kKeyOnly) { // Index Value Retrieval Operation std::move(callback).Run( blink::mojom::IDBDatabaseGetResult::NewKey(std::move(*primary_key))); @@ -911,7 +911,7 @@ Status s = Status::OK(); std::unique_ptr<IndexedDBBackingStore::Cursor> cursor; - if (cursor_type == indexed_db::CURSOR_KEY_ONLY) { + if (cursor_type == indexed_db::CursorType::kKeyOnly) { // Retrieving keys if (index_id == IndexedDBIndexMetadata::kInvalidId) { // Object Store: Key Retrieval Operation @@ -990,7 +990,7 @@ IndexedDBReturnValue return_value; IndexedDBKey return_key; - if (cursor_type == indexed_db::CURSOR_KEY_ONLY) { + if (cursor_type == indexed_db::CursorType::kKeyOnly) { return_key = cursor->primary_key(); } else { // Retrieving values @@ -1001,13 +1001,14 @@ } } - if (cursor_type == indexed_db::CURSOR_KEY_ONLY) + if (cursor_type == indexed_db::CursorType::kKeyOnly) { found_keys.push_back(return_key); - else + } else { found_values.push_back(return_value); + } // Periodically stream values and keys if we have too many. - if (cursor_type == indexed_db::CURSOR_KEY_ONLY) { + if (cursor_type == indexed_db::CursorType::kKeyOnly) { if (found_keys.size() >= max_values_before_sending) { result_sink->ReceiveKeys(std::move(found_keys)); found_keys.clear(); @@ -1021,7 +1022,7 @@ } } - if (cursor_type == indexed_db::CURSOR_KEY_ONLY) { + if (cursor_type == indexed_db::CursorType::kKeyOnly) { if (!found_keys.empty()) { result_sink->ReceiveKeys(std::move(found_keys)); } @@ -1249,7 +1250,7 @@ Status s; std::unique_ptr<IndexedDBBackingStore::Cursor> backing_store_cursor; if (params->index_id == IndexedDBIndexMetadata::kInvalidId) { - if (params->cursor_type == indexed_db::CURSOR_KEY_ONLY) { + if (params->cursor_type == indexed_db::CursorType::kKeyOnly) { DCHECK_EQ(params->task_type, blink::mojom::IDBTaskType::Normal); backing_store_cursor = backing_store()->OpenObjectStoreKeyCursor( transaction->BackingStoreTransaction(), id(), params->object_store_id, @@ -1261,7 +1262,7 @@ } } else { DCHECK_EQ(params->task_type, blink::mojom::IDBTaskType::Normal); - if (params->cursor_type == indexed_db::CURSOR_KEY_ONLY) { + if (params->cursor_type == indexed_db::CursorType::kKeyOnly) { backing_store_cursor = backing_store()->OpenIndexKeyCursor( transaction->BackingStoreTransaction(), id(), params->object_store_id, params->index_id, *params->key_range, params->direction, &s);
diff --git a/content/browser/indexed_db/indexed_db_database.h b/content/browser/indexed_db/indexed_db_database.h index 137fa4b9a..1f08d8a 100644 --- a/content/browser/indexed_db/indexed_db_database.h +++ b/content/browser/indexed_db/indexed_db_database.h
@@ -24,7 +24,6 @@ #include "base/memory/weak_ptr.h" #include "components/services/storage/indexed_db/locks/partitioned_lock_manager.h" #include "components/services/storage/public/cpp/buckets/bucket_locator.h" -#include "content/browser/indexed_db/indexed_db.h" #include "content/browser/indexed_db/indexed_db_backing_store.h" #include "content/browser/indexed_db/indexed_db_connection_coordinator.h" #include "content/browser/indexed_db/indexed_db_factory_client.h" @@ -51,6 +50,10 @@ class IndexedDBTransaction; struct IndexedDBValue; +namespace indexed_db { +enum class CursorType; +} + class CONTENT_EXPORT IndexedDBDatabase { public: // Identifier is pair of (bucket_locator, database name).
diff --git a/content/browser/indexed_db/indexed_db_database_unittest.cc b/content/browser/indexed_db/indexed_db_database_unittest.cc index 2c743f0..266fc44 100644 --- a/content/browser/indexed_db/indexed_db_database_unittest.cc +++ b/content/browser/indexed_db/indexed_db_database_unittest.cc
@@ -25,7 +25,6 @@ #include "components/services/storage/indexed_db/locks/partitioned_lock_manager.h" #include "components/services/storage/privileged/mojom/indexed_db_client_state_checker.mojom.h" #include "components/services/storage/public/cpp/buckets/bucket_locator.h" -#include "content/browser/indexed_db/indexed_db.h" #include "content/browser/indexed_db/indexed_db_backing_store.h" #include "content/browser/indexed_db/indexed_db_bucket_context.h" #include "content/browser/indexed_db/indexed_db_connection.h"
diff --git a/content/browser/indexed_db/indexed_db_factory_client.cc b/content/browser/indexed_db/indexed_db_factory_client.cc index 73f01563..19821c6 100644 --- a/content/browser/indexed_db/indexed_db_factory_client.cc +++ b/content/browser/indexed_db/indexed_db_factory_client.cc
@@ -4,26 +4,22 @@ #include "content/browser/indexed_db/indexed_db_factory_client.h" -#include <stddef.h> - -#include <algorithm> +#include <forward_list> #include <memory> +#include <ostream> #include <utility> +#include "base/check.h" +#include "base/check_op.h" #include "base/functional/bind.h" +#include "base/location.h" #include "base/task/sequenced_task_runner.h" -#include "base/time/time.h" #include "content/browser/indexed_db/database_impl.h" #include "content/browser/indexed_db/indexed_db_connection.h" -#include "content/browser/indexed_db/indexed_db_context_impl.h" -#include "content/browser/indexed_db/indexed_db_cursor.h" +#include "content/browser/indexed_db/indexed_db_data_loss_info.h" #include "content/browser/indexed_db/indexed_db_database_error.h" -#include "content/browser/indexed_db/indexed_db_return_value.h" -#include "content/browser/indexed_db/indexed_db_transaction.h" -#include "content/browser/indexed_db/indexed_db_value.h" #include "mojo/public/cpp/bindings/pending_associated_remote.h" -#include "storage/browser/quota/quota_manager.h" -#include "third_party/blink/public/common/indexeddb/indexeddb_metadata.h" +#include "third_party/blink/public/mojom/indexeddb/indexeddb.mojom-shared.h" using blink::IndexedDBDatabaseMetadata; using blink::IndexedDBKey;
diff --git a/content/browser/indexed_db/indexed_db_unittest.cc b/content/browser/indexed_db/indexed_db_unittest.cc index b62441b..e9dce0f 100644 --- a/content/browser/indexed_db/indexed_db_unittest.cc +++ b/content/browser/indexed_db/indexed_db_unittest.cc
@@ -2,50 +2,89 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include <stdint.h> +#include <inttypes.h> +#include <map> +#include <memory> +#include <optional> +#include <ostream> +#include <set> +#include <string> +#include <tuple> +#include <type_traits> #include <utility> +#include <vector> +#include "base/auto_reset.h" #include "base/barrier_closure.h" +#include "base/check.h" +#include "base/containers/contains.h" +#include "base/files/file.h" +#include "base/files/file_path.h" #include "base/files/file_util.h" #include "base/files/scoped_temp_dir.h" -#include "base/memory/raw_ptr.h" +#include "base/functional/bind.h" +#include "base/functional/callback.h" +#include "base/functional/callback_helpers.h" +#include "base/location.h" #include "base/memory/scoped_refptr.h" +#include "base/numerics/clamped_math.h" #include "base/run_loop.h" #include "base/strings/stringprintf.h" +#include "base/strings/utf_string_conversions.h" #include "base/task/sequenced_task_runner.h" #include "base/task/single_thread_task_runner.h" -#include "base/test/bind.h" #include "base/test/gmock_callback_support.h" #include "base/test/mock_callback.h" #include "base/test/scoped_feature_list.h" #include "base/test/task_environment.h" #include "base/test/test_future.h" -#include "components/services/storage/indexed_db/locks/partitioned_lock_manager.h" -#include "components/services/storage/indexed_db/transactional_leveldb/transactional_leveldb_database.h" -#include "components/services/storage/privileged/mojom/indexed_db_control.mojom-test-utils.h" +#include "base/time/time.h" +#include "base/timer/timer.h" +#include "base/unguessable_token.h" +#include "build/build_config.h" +#include "components/services/storage/indexed_db/locks/partitioned_lock_id.h" +#include "components/services/storage/privileged/mojom/indexed_db_control.mojom.h" +#include "components/services/storage/public/cpp/buckets/bucket_id.h" +#include "components/services/storage/public/cpp/buckets/bucket_info.h" +#include "components/services/storage/public/cpp/buckets/bucket_init_params.h" #include "components/services/storage/public/cpp/buckets/bucket_locator.h" +#include "components/services/storage/public/cpp/buckets/constants.h" +#include "components/services/storage/public/cpp/quota_error_or.h" +#include "components/services/storage/public/mojom/storage_policy_update.mojom.h" #include "content/browser/indexed_db/features.h" +#include "content/browser/indexed_db/indexed_db_backing_store.h" #include "content/browser/indexed_db/indexed_db_bucket_context.h" -#include "content/browser/indexed_db/indexed_db_connection.h" +#include "content/browser/indexed_db/indexed_db_bucket_context_handle.h" #include "content/browser/indexed_db/indexed_db_context_impl.h" #include "content/browser/indexed_db/indexed_db_data_format_version.h" #include "content/browser/indexed_db/indexed_db_leveldb_coding.h" #include "content/browser/indexed_db/indexed_db_pre_close_task_queue.h" -#include "content/browser/indexed_db/mock_indexed_db_factory_client.h" #include "content/browser/indexed_db/mock_mojo_indexed_db_database_callbacks.h" #include "content/browser/indexed_db/mock_mojo_indexed_db_factory_client.h" +#include "env_chromium.h" +#include "mojo/public/cpp/bindings/associated_remote.h" #include "mojo/public/cpp/bindings/pending_associated_remote.h" +#include "mojo/public/cpp/bindings/pending_receiver.h" #include "mojo/public/cpp/bindings/pending_remote.h" +#include "mojo/public/cpp/bindings/receiver.h" +#include "mojo/public/cpp/bindings/remote.h" +#include "mojo/public/cpp/bindings/struct_ptr.h" #include "net/base/features.h" #include "net/base/schemeful_site.h" -#include "storage/browser/quota/quota_manager.h" -#include "storage/browser/quota/special_storage_policy.h" +#include "storage/browser/test/mock_quota_manager.h" #include "storage/browser/test/mock_quota_manager_proxy.h" #include "storage/browser/test/mock_special_storage_policy.h" +#include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/blink/public/common/indexeddb/indexeddb_key.h" +#include "third_party/blink/public/common/indexeddb/indexeddb_key_path.h" +#include "third_party/blink/public/common/indexeddb/indexeddb_metadata.h" #include "third_party/blink/public/common/storage_key/storage_key.h" -#include "third_party/leveldatabase/src/include/leveldb/env.h" +#include "third_party/blink/public/mojom/indexeddb/indexeddb.mojom.h" +#include "third_party/blink/public/mojom/quota/quota_types.mojom-shared.h" +#include "third_party/blink/public/mojom/storage_key/ancestor_chain_bit.mojom-shared.h" +#include "url/gurl.h" +#include "url/origin.h" using base::test::RunClosure; using blink::IndexedDBDatabaseMetadata;
diff --git a/content/browser/interest_group/auction_runner.cc b/content/browser/interest_group/auction_runner.cc index 44e3d0c..5b6aa78 100644 --- a/content/browser/interest_group/auction_runner.cc +++ b/content/browser/interest_group/auction_runner.cc
@@ -650,7 +650,7 @@ update_owners.end()); // Filter owners not allowed to update. - base::EraseIf(update_owners, [this](const url::Origin& owner) { + std::erase_if(update_owners, [this](const url::Origin& owner) { return !is_interest_group_api_allowed_callback_.Run( ContentBrowserClient::InterestGroupApiOperation::kUpdate, owner); });
diff --git a/content/browser/interest_group/bidding_and_auction_serializer.cc b/content/browser/interest_group/bidding_and_auction_serializer.cc index 73e7668..17bb44c 100644 --- a/content/browser/interest_group/bidding_and_auction_serializer.cc +++ b/content/browser/interest_group/bidding_and_auction_serializer.cc
@@ -8,7 +8,6 @@ #include <vector> #include "base/command_line.h" -#include "base/containers/cxx20_erase.h" #include "base/feature_list.h" #include "base/json/json_string_value_serializer.h" #include "base/metrics/histogram_functions.h" @@ -185,7 +184,7 @@ scoped_refptr<StorageInterestGroups> groups) { std::vector<SingleStorageInterestGroup> groups_to_add = groups->GetInterestGroups(); - base::EraseIf(groups_to_add, [](const SingleStorageInterestGroup& group) { + std::erase_if(groups_to_add, [](const SingleStorageInterestGroup& group) { return (!group->interest_group.ads) || (group->interest_group.ads->size() == 0); });
diff --git a/content/browser/interest_group/interest_group_auction.cc b/content/browser/interest_group/interest_group_auction.cc index 8fd265c..b5c8ae8e 100644 --- a/content/browser/interest_group/interest_group_auction.cc +++ b/content/browser/interest_group/interest_group_auction.cc
@@ -3891,14 +3891,14 @@ } // Ignore interest groups with no bidding script or no ads. - base::EraseIf(interest_groups, [](const SingleStorageInterestGroup& bidder) { + std::erase_if(interest_groups, [](const SingleStorageInterestGroup& bidder) { return !bidder->interest_group.bidding_url || !bidder->interest_group.ads || bidder->interest_group.ads->empty(); }); // Ignore interest groups that don't provide the requested seller // capabilities. - base::EraseIf(interest_groups, + std::erase_if(interest_groups, [this](const SingleStorageInterestGroup& bidder) { return !GroupSatisfiesAllCapabilities( bidder->interest_group,
diff --git a/content/browser/interest_group/interest_group_auction_reporter.cc b/content/browser/interest_group/interest_group_auction_reporter.cc index 99b05c5..a340b7c 100644 --- a/content/browser/interest_group/interest_group_auction_reporter.cc +++ b/content/browser/interest_group/interest_group_auction_reporter.cc
@@ -16,7 +16,6 @@ #include <utility> #include <vector> -#include "base/containers/cxx20_erase_vector.h" #include "base/containers/flat_map.h" #include "base/feature_list.h" #include "base/functional/callback.h" @@ -1146,7 +1145,7 @@ void InterestGroupAuctionReporter::EnforceAttestationsReportUrls( std::vector<GURL>& urls) { - base::EraseIf(urls, [this](const GURL& url) { return !CheckReportUrl(url); }); + std::erase_if(urls, [this](const GURL& url) { return !CheckReportUrl(url); }); } } // namespace content
diff --git a/content/browser/interest_group/interest_group_features.cc b/content/browser/interest_group/interest_group_features.cc index 4cf49df..4f24fce 100644 --- a/content/browser/interest_group/interest_group_features.cc +++ b/content/browser/interest_group/interest_group_features.cc
@@ -25,7 +25,7 @@ // Enable updating userBiddingSignals when updating a user's interests groups. BASE_FEATURE(kEnableUpdatingUserBiddingSignals, "EnableUpdatingUserBiddingSignals", - base::FEATURE_DISABLED_BY_DEFAULT); + base::FEATURE_ENABLED_BY_DEFAULT); // Enable write ahead logging for interest group storage. BASE_FEATURE(kFledgeEnableWALForInterestGroupStorage,
diff --git a/content/browser/preloading/prefetch/contamination_delay_browsertest.cc b/content/browser/preloading/prefetch/contamination_delay_browsertest.cc index e12e349..82cb64f 100644 --- a/content/browser/preloading/prefetch/contamination_delay_browsertest.cc +++ b/content/browser/preloading/prefetch/contamination_delay_browsertest.cc
@@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "base/strings/string_number_conversions.h" #include "base/test/run_until.h" #include "base/test/scoped_feature_list.h" #include "base/test/test_timeouts.h" @@ -31,9 +32,20 @@ class ContaminationDelayBrowserTest : public ContentBrowserTest { protected: ContaminationDelayBrowserTest() { - scoped_feature_list_.InitWithFeatures( - {features::kPrefetchStateContaminationMitigation, - features::kPrefetchRedirects}, + scoped_feature_list_.InitWithFeaturesAndParameters( + {{features::kPrefetchStateContaminationMitigation, {}}, + {features::kPrefetchRedirects, {}}, + // This is needed specifically for CrOS MSAN, where we apply a 10x + // multiplier to all test timeouts, which happens to be enough to push + // the response delay in this test (which is scaled in that way to + // match the slowdown of everything else) over the default prefetch + // timeout. To be resilient also to changes in that value, it is + // expressly overridden here to be a timeout that is much longer and + // scales with the timeout multiplier. + {features::kPrefetchUseContentRefactor, + {{"prefetch_timeout_ms", + base::NumberToString( + TestTimeouts::action_max_timeout().InMilliseconds())}}}}, {}); } @@ -70,7 +82,10 @@ ASSERT_TRUE(base::test::RunUntil([&] { return prefetch_document_manager->GetReferringPageMetrics() .prefetch_successful_count >= 1; - })) << "timed out waiting for prefetch to complete"; + })) << "timed out waiting for prefetch to complete (" + << prefetch_document_manager->GetReferringPageMetrics() + .prefetch_attempted_count + << " attempted)"; } private: @@ -113,13 +128,7 @@ EXPECT_GE(timer.Elapsed(), response_delay()); } -// TODO(crbug.com/325359478): Fix and re-enable for MSAN. -#if defined(MEMORY_SANITIZER) -#define MAYBE_IgnoresSameOrigin DISABLED_IgnoresSameOrigin -#else -#define MAYBE_IgnoresSameOrigin IgnoresSameOrigin -#endif -IN_PROC_BROWSER_TEST_F(ContaminationDelayBrowserTest, MAYBE_IgnoresSameOrigin) { +IN_PROC_BROWSER_TEST_F(ContaminationDelayBrowserTest, IgnoresSameOrigin) { GURL referrer_url = embedded_test_server()->GetURL("referrer.localhost", "/title1.html"); GURL prefetch_url = @@ -132,13 +141,7 @@ EXPECT_LT(timer.Elapsed(), response_delay()); } -// TODO(crbug.com/325359478): Fix and re-enable for MSAN. -#if defined(MEMORY_SANITIZER) -#define MAYBE_IgnoresSameSite DISABLED_IgnoresSameSite -#else -#define MAYBE_IgnoresSameSite IgnoresSameSite -#endif -IN_PROC_BROWSER_TEST_F(ContaminationDelayBrowserTest, MAYBE_IgnoresSameSite) { +IN_PROC_BROWSER_TEST_F(ContaminationDelayBrowserTest, IgnoresSameSite) { GURL referrer_url = embedded_test_server()->GetURL("referrer.localhost", "/title1.html"); GURL prefetch_url = @@ -151,13 +154,7 @@ EXPECT_LT(timer.Elapsed(), response_delay()); } -// TODO(crbug.com/325359478): Fix and re-enable for MSAN. -#if defined(MEMORY_SANITIZER) -#define MAYBE_IgnoresIfExempt DISABLED_IgnoresIfExempt -#else -#define MAYBE_IgnoresIfExempt IgnoresIfExempt -#endif -IN_PROC_BROWSER_TEST_F(ContaminationDelayBrowserTest, MAYBE_IgnoresIfExempt) { +IN_PROC_BROWSER_TEST_F(ContaminationDelayBrowserTest, IgnoresIfExempt) { GURL referrer_url = embedded_test_server()->GetURL("referrer.localhost", "/title1.html"); GURL prefetch_url =
diff --git a/content/browser/preloading/prefetch/prefetch_container.cc b/content/browser/preloading/prefetch/prefetch_container.cc index bdef936..43872b3d 100644 --- a/content/browser/preloading/prefetch/prefetch_container.cc +++ b/content/browser/preloading/prefetch/prefetch_container.cc
@@ -703,7 +703,7 @@ // To avoid spurious reordering, don't remove headers that will be updated // anyway. - base::EraseIf(headers_to_remove, [&](const std::string& header) { + std::erase_if(headers_to_remove, [&](const std::string& header) { return updated_headers.HasHeader(header); });
diff --git a/content/browser/preloading/prefetch/prefetch_document_manager.cc b/content/browser/preloading/prefetch/prefetch_document_manager.cc index f6a19925..f485e64 100644 --- a/content/browser/preloading/prefetch/prefetch_document_manager.cc +++ b/content/browser/preloading/prefetch/prefetch_document_manager.cc
@@ -10,7 +10,6 @@ #include <vector> #include "base/containers/contains.h" -#include "base/containers/cxx20_erase.h" #include "content/browser/browser_context_impl.h" #include "content/browser/preloading/prefetch/prefetch_container.h" #include "content/browser/preloading/prefetch/prefetch_params.h" @@ -244,7 +243,7 @@ return true; }; - base::EraseIf(candidates, should_process_entry); + std::erase_if(candidates, should_process_entry); for (auto& [prefetch_url, prefetch_type, referrer, no_vary_search_expected] : prefetches) {
diff --git a/content/browser/preloading/prefetch/prefetch_service.cc b/content/browser/preloading/prefetch/prefetch_service.cc index bfd0f277..ef62778 100644 --- a/content/browser/preloading/prefetch/prefetch_service.cc +++ b/content/browser/preloading/prefetch/prefetch_service.cc
@@ -7,10 +7,10 @@ #include <memory> #include <optional> #include <utility> +#include <vector> #include "base/auto_reset.h" #include "base/barrier_closure.h" -#include "base/containers/cxx20_erase_vector.h" #include "base/containers/fixed_flat_set.h" #include "base/feature_list.h" #include "base/location.h" @@ -1426,7 +1426,7 @@ prefetch_container->UpdateServingPageMetrics(); } - base::EraseIf(matches, [](const auto* prefetch_container) { + std::erase_if(matches, [](const auto* prefetch_container) { if (prefetch_container->HasPrefetchBeenConsideredToServe()) { DVLOG(1) << "PrefetchService::FindPrefetchContainerToServe: skipped " << "because already considered to serve: "
diff --git a/content/browser/preloading/preloading_decider.cc b/content/browser/preloading/preloading_decider.cc index 599a272..f27c9bf 100644 --- a/content/browser/preloading/preloading_decider.cc +++ b/content/browser/preloading/preloading_decider.cc
@@ -4,6 +4,8 @@ #include "content/browser/preloading/preloading_decider.h" +#include <vector> + #include "base/check_op.h" #include "base/containers/enum_set.h" #include "base/feature_list.h" @@ -370,7 +372,7 @@ // The candidates remaining after this call will be all eager candidates, // and all non-eager candidates whose (url, action) pair has already been // processed. - base::EraseIf(candidates, should_mark_as_on_standby); + std::erase_if(candidates, should_mark_as_on_standby); prefetcher_.ProcessCandidatesForPrefetch(candidates);
diff --git a/content/browser/preloading/prerenderer_impl.cc b/content/browser/preloading/prerenderer_impl.cc index ad43cb2..692ac9b8 100644 --- a/content/browser/preloading/prerenderer_impl.cc +++ b/content/browser/preloading/prerenderer_impl.cc
@@ -4,6 +4,8 @@ #include "content/browser/preloading/prerenderer_impl.h" +#include <vector> + #include "content/browser/preloading/preloading.h" #include "content/browser/preloading/preloading_attempt_impl.h" #include "content/browser/preloading/preloading_trigger_type_impl.h" @@ -175,7 +177,7 @@ // requests rejected by PrerenderHostRegistry can be filtered out. But // ideally PrerenderHostRegistry should implement the history management // mechanism by itself. - base::EraseIf(started_prerenders_, [&](const PrerenderInfo& x) { + std::erase_if(started_prerenders_, [&](const PrerenderInfo& x) { return base::Contains(removed_prerender_rules_set, x.prerender_host_id); }); }
diff --git a/content/browser/renderer_host/media/media_devices_manager.cc b/content/browser/renderer_host/media/media_devices_manager.cc index 025f457..0451462 100644 --- a/content/browser/renderer_host/media/media_devices_manager.cc +++ b/content/browser/renderer_host/media/media_devices_manager.cc
@@ -10,10 +10,10 @@ #include <functional> #include <map> #include <string> +#include <vector> #include "base/command_line.h" #include "base/containers/contains.h" -#include "base/containers/cxx20_erase.h" #include "base/functional/bind.h" #include "base/location.h" #include "base/metrics/histogram_functions.h" @@ -715,7 +715,7 @@ video_capture_manager_->GetDeviceSupportedFormats(device_id, &formats); ReplaceInvalidFrameRatesWithFallback(&formats); // Remove formats that have zero resolution. - base::EraseIf(formats, [](const media::VideoCaptureFormat& format) { + std::erase_if(formats, [](const media::VideoCaptureFormat& format) { return format.frame_size.GetArea() <= 0; }); @@ -1177,7 +1177,7 @@ false /* ignore_group_id */); } - base::EraseIf(requests_, [this](EnumerationRequest& request) { + std::erase_if(requests_, [this](EnumerationRequest& request) { if (IsEnumerationRequestReady(request)) { std::move(request.callback).Run(current_snapshot_); return true;
diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc index 68dee94..d77da7d 100644 --- a/content/browser/renderer_host/render_frame_host_impl.cc +++ b/content/browser/renderer_host/render_frame_host_impl.cc
@@ -12,11 +12,11 @@ #include <tuple> #include <unordered_map> #include <utility> +#include <vector> #include "base/check.h" #include "base/command_line.h" #include "base/containers/contains.h" -#include "base/containers/cxx20_erase.h" #include "base/debug/crash_logging.h" #include "base/debug/dump_without_crashing.h" #include "base/feature_list.h" @@ -6674,7 +6674,7 @@ void RenderFrameHostImpl::RemoveDocumentService( internal::DocumentServiceBase* document_service, base::PassKey<internal::DocumentServiceBase>) { - base::Erase(document_associated_data_->services(), document_service); + std::erase(document_associated_data_->services(), document_service); } FrameTreeNode* RenderFrameHostImpl::FindAndVerifyChild(
diff --git a/content/browser/renderer_host/render_widget_host_impl.cc b/content/browser/renderer_host/render_widget_host_impl.cc index 1f9bdd1..fb8f39a 100644 --- a/content/browser/renderer_host/render_widget_host_impl.cc +++ b/content/browser/renderer_host/render_widget_host_impl.cc
@@ -18,7 +18,6 @@ #include "base/check.h" #include "base/command_line.h" #include "base/containers/contains.h" -#include "base/containers/cxx20_erase.h" #include "base/debug/alias.h" #include "base/debug/dump_without_crashing.h" #include "base/feature_list.h" @@ -1872,7 +1871,7 @@ void RenderWidgetHostImpl::RemoveKeyPressEventCallback( const KeyPressEventCallback& callback) { - base::Erase(key_press_event_callbacks_, callback); + std::erase(key_press_event_callbacks_, callback); } void RenderWidgetHostImpl::AddMouseEventCallback( @@ -1883,7 +1882,7 @@ void RenderWidgetHostImpl::RemoveMouseEventCallback( const MouseEventCallback& callback) { - base::Erase(mouse_event_callbacks_, callback); + std::erase(mouse_event_callbacks_, callback); } void RenderWidgetHostImpl::AddSuppressShowingImeCallback( @@ -1894,7 +1893,7 @@ void RenderWidgetHostImpl::RemoveSuppressShowingImeCallback( const SuppressShowingImeCallback& callback) { - base::Erase(suppress_showing_ime_callbacks_, callback); + std::erase(suppress_showing_ime_callbacks_, callback); } void RenderWidgetHostImpl::AddInputEventObserver(
diff --git a/content/browser/usb/web_usb_service_impl.cc b/content/browser/usb/web_usb_service_impl.cc index 7d11c1ea..e47579b 100644 --- a/content/browser/usb/web_usb_service_impl.cc +++ b/content/browser/usb/web_usb_service_impl.cc
@@ -6,9 +6,9 @@ #include <memory> #include <utility> +#include <vector> #include "base/containers/contains.h" -#include "base/containers/cxx20_erase.h" #include "base/functional/bind.h" #include "base/memory/raw_ptr.h" #include "content/browser/renderer_host/render_frame_host_impl.h" @@ -400,7 +400,7 @@ // permission. auto* delegate = GetContentClient()->browser()->GetUsbDelegate(); auto* browser_context = GetBrowserContext(); - base::EraseIf(device_clients_, [=](const auto& client) { + std::erase_if(device_clients_, [=](const auto& client) { auto* device_info = delegate->GetDeviceInfo(browser_context, client->device_guid()); if (!device_info) @@ -423,7 +423,7 @@ void WebUsbServiceImpl::OnDeviceRemoved( const device::mojom::UsbDeviceInfo& device_info) { - base::EraseIf(device_clients_, [&device_info](const auto& client) { + std::erase_if(device_clients_, [&device_info](const auto& client) { return device_info.guid == client->device_guid(); }); @@ -488,7 +488,7 @@ } void WebUsbServiceImpl::RemoveDeviceClient(const UsbDeviceClient* client) { - base::EraseIf(device_clients_, [client](const auto& this_client) { + std::erase_if(device_clients_, [client](const auto& this_client) { return client == this_client.get(); }); }
diff --git a/content/browser/webid/federated_auth_request_impl.cc b/content/browser/webid/federated_auth_request_impl.cc index 806cb60..944c8247a 100644 --- a/content/browser/webid/federated_auth_request_impl.cc +++ b/content/browser/webid/federated_auth_request_impl.cc
@@ -5,10 +5,10 @@ #include "content/browser/webid/federated_auth_request_impl.h" #include <random> +#include <vector> #include "base/command_line.h" #include "base/containers/contains.h" -#include "base/containers/cxx20_erase.h" #include "base/functional/callback.h" #include "base/json/json_writer.h" #include "base/metrics/histogram_macros.h" @@ -329,16 +329,6 @@ /*for_display=*/true); } -bool ShouldSuppressIdpSigninFailureDialog( - std::optional<TokenStatus> token_status) { - if (!token_status) { - return false; - } - - return token_status == TokenStatus::kAborted || - token_status == TokenStatus::kUnhandledRequest; -} - FederatedAuthRequestPageData* GetPageData(RenderFrameHost* render_frame_host) { return FederatedAuthRequestPageData::GetOrCreateForPage( render_frame_host->GetPage()); @@ -369,7 +359,7 @@ auto filter = [&login_hint](const IdentityRequestAccount& account) { return !base::Contains(account.login_hints, login_hint); }; - base::EraseIf(accounts, filter); + std::erase_if(accounts, filter); FedCmMetrics::NumAccounts num_matching = ComputeNumMatchingAccounts(accounts); base::UmaHistogramEnumeration("Blink.FedCm.LoginHint.NumMatchingAccounts", num_matching); @@ -386,12 +376,12 @@ auto filter = [](const IdentityRequestAccount& account) { return account.domain_hints.empty(); }; - base::EraseIf(accounts, filter); + std::erase_if(accounts, filter); } else { auto filter = [&domain_hint](const IdentityRequestAccount& account) { return !base::Contains(account.domain_hints, domain_hint); }; - base::EraseIf(accounts, filter); + std::erase_if(accounts, filter); } FedCmMetrics::NumAccounts num_matching = ComputeNumMatchingAccounts(accounts); base::UmaHistogramEnumeration("Blink.FedCm.DomainHint.NumMatchingAccounts", @@ -752,18 +742,41 @@ } if (HasPendingRequest()) { - fedcm_metrics_->RecordRequestTokenStatus(TokenStatus::kTooManyRequests, - requirement); + RpMode pending_request_rp_mode = GetPageData(&render_frame_host()) + ->PendingWebIdentityRequest() + ->GetRpMode(); + bool can_replace_pending_request = + IsFedCmButtonModeEnabled() && + idp_get_params_ptrs[0]->mode == RpMode::kButton && + render_frame_host().HasTransientUserActivation() && + pending_request_rp_mode != RpMode::kButton; - AddDevToolsIssue( - blink::mojom::FederatedAuthRequestResult::kErrorTooManyRequests); - AddConsoleErrorMessage( - blink::mojom::FederatedAuthRequestResult::kErrorTooManyRequests); + // TODO(crbug.com/326587232): We should add metrics to capture + // 1. how often a button flow is triggered while a widget flow is pending + // 2. how often a button flow is triggered while a button flow is pending + // 3. how often a widget flow is triggered while a button flow is pending + if (!can_replace_pending_request) { + fedcm_metrics_->RecordRequestTokenStatus(TokenStatus::kTooManyRequests, + requirement); - std::move(callback).Run(RequestTokenStatus::kErrorTooManyRequests, - std::nullopt, "", /*error=*/nullptr, - /*is_auto_selected=*/false); - return; + AddDevToolsIssue( + blink::mojom::FederatedAuthRequestResult::kErrorTooManyRequests); + AddConsoleErrorMessage( + blink::mojom::FederatedAuthRequestResult::kErrorTooManyRequests); + + std::move(callback).Run(RequestTokenStatus::kErrorTooManyRequests, + std::nullopt, "", /*error=*/nullptr, + /*is_auto_selected=*/false); + return; + } + // Cancel the pending request before starting the new button flow request. + // TODO(crbug.com/326587232): Use specific error type. + GetPageData(&render_frame_host()) + ->PendingWebIdentityRequest() + ->CompleteRequestWithError(FederatedAuthRequestResult::kError, + /*token_status=*/std::nullopt, + /*token_error=*/std::nullopt, + /*should_delay_callback=*/false); } bool intercept = false; @@ -792,6 +805,8 @@ /*should_delay_callback=*/false); return; } + } else { + rp_mode_ = RpMode::kWidget; } FederatedApiPermissionStatus permission_status = GetApiPermissionStatus(); @@ -2358,19 +2373,6 @@ return; } - if (result != FederatedAuthRequestResult::kSuccess && - fetch_data_.for_idp_signin && - !ShouldSuppressIdpSigninFailureDialog(token_status)) { - fetch_data_ = FetchData(); - - request_dialog_controller_->ShowIdpSigninFailureDialog(base::BindOnce( - &FederatedAuthRequestImpl::CompleteRequest, - weak_ptr_factory_.GetWeakPtr(), result, std::move(token_status), - std::move(token_error), selected_idp_config_url, id_token, - should_delay_callback)); - return; - } - if (token_status) { fedcm_metrics_->RecordRequestTokenStatus(*token_status, mediation_requirement_); @@ -2469,6 +2471,7 @@ token_error_ = std::nullopt; dialog_type_ = kNone; identity_selection_type_ = kExplicit; + rp_mode_ = RpMode::kWidget; } void FederatedAuthRequestImpl::AddDevToolsIssue(
diff --git a/content/browser/webid/federated_auth_request_impl.h b/content/browser/webid/federated_auth_request_impl.h index 6c6ff88..bddca4e 100644 --- a/content/browser/webid/federated_auth_request_impl.h +++ b/content/browser/webid/federated_auth_request_impl.h
@@ -388,6 +388,8 @@ std::optional<IdpNetworkRequestManager::FedCmErrorUrlType> error_url_type); + RpMode GetRpMode() const { return rp_mode_; } + std::unique_ptr<IdpNetworkRequestManager> network_manager_; std::unique_ptr<IdentityRequestDialogController> request_dialog_controller_;
diff --git a/content/browser/webid/federated_auth_request_impl_unittest.cc b/content/browser/webid/federated_auth_request_impl_unittest.cc index ec97efb..1d6d571 100644 --- a/content/browser/webid/federated_auth_request_impl_unittest.cc +++ b/content/browser/webid/federated_auth_request_impl_unittest.cc
@@ -646,8 +646,6 @@ std::optional<SkColor> brand_text_color; // State related to ShowFailureDialog(). size_t num_show_idp_signin_status_mismatch_dialog_requests{0u}; - // State related to ShowIdpSigninFailureDialog(). - bool did_show_idp_signin_failure_dialog{false}; // State related to ShowErrorDialog(). bool did_show_error_dialog{false}; std::optional<TokenError> token_error; @@ -807,16 +805,6 @@ } } - void ShowIdpSigninFailureDialog(base::OnceClosure dismiss_callback) override { - if (!state_) { - return; - } - - state_->did_show_idp_signin_failure_dialog = true; - base::SequencedTaskRunner::GetCurrentDefault()->PostTask( - FROM_HERE, std::move(dismiss_callback)); - } - base::WeakPtr<TestDialogController> AsWeakPtr() { return weak_ptr_factory_.GetWeakPtr(); } @@ -3880,7 +3868,6 @@ EXPECT_EQ(2u, dialog_controller_state_ .num_show_idp_signin_status_mismatch_dialog_requests); - EXPECT_FALSE(dialog_controller_state_.did_show_idp_signin_failure_dialog); // After the IdP sign-in status was updated, the endpoints should have been // fetched a 2nd time. @@ -3950,7 +3937,6 @@ EXPECT_FALSE(did_show_accounts_dialog()); EXPECT_EQ(1u, dialog_controller_state_ .num_show_idp_signin_status_mismatch_dialog_requests); - EXPECT_TRUE(dialog_controller_state_.did_show_idp_signin_failure_dialog); // After the IdP sign-in status was updated, the endpoints should have been // fetched a 2nd time. @@ -3969,118 +3955,6 @@ CheckAllFedCmSessionIDs(); } -// Test that the IdP-sign-in-failure-dialog is not shown if there is an error -// after the user has selected an account. -TEST_F(FederatedAuthRequestImplTest, - FailAfterAccountSelectionHideDialogDoesNotShowIdpSigninFailureDialog) { - base::test::ScopedFeatureList list; - list.InitAndEnableFeature(features::kFedCmIdpSigninStatusEnabled); - - // Setup dialog controller to fail FedCM request after the user has selected - // an account. - url::Origin rp_origin_to_disable = main_test_rfh()->GetLastCommittedOrigin(); - SetDialogController( - std::make_unique<DisableApiWhenDialogShownDialogController>( - kConfigurationValid, test_api_permission_delegate_.get(), - rp_origin_to_disable)); - - SetNetworkRequestManager( - std::make_unique<ParseStatusOverrideIdpNetworkRequestManager>()); - auto* network_manager = - static_cast<ParseStatusOverrideIdpNetworkRequestManager*>( - test_network_request_manager_.get()); - - url::Origin kIdpOrigin = OriginFromString(kProviderUrlFull); - - // Setup IdP sign-in status mismatch. - network_manager->accounts_parse_status_ = ParseStatus::kInvalidResponseError; - test_permission_delegate_->idp_signin_statuses_[kIdpOrigin] = true; - - RunAuthDontWaitForCallback(kDefaultRequestParameters, kConfigurationValid); - EXPECT_TRUE(did_show_idp_signin_status_mismatch_dialog()); - EXPECT_FALSE(did_show_accounts_dialog()); - - // Simulate user signing into IdP by updating the IdP signin status and - // calling the observer. - test_permission_delegate_->idp_signin_statuses_[kIdpOrigin] = true; - network_manager->accounts_parse_status_ = ParseStatus::kSuccess; - federated_auth_request_impl_->OnIdpSigninStatusReceived( - kIdpOrigin, /*idp_signin_status=*/true); - WaitForCurrentAuthRequest(); - - // Check that the FedCM request failed after the account picker was shown. - RequestExpectations expectations = { - RequestTokenStatus::kError, - FederatedAuthRequestResult::kErrorDisabledInSettings, - /*standalone_console_message=*/std::nullopt, - /*selected_idp_config_url=*/std::nullopt}; - CheckAuthExpectations(kConfigurationValid, expectations); - EXPECT_TRUE(did_show_accounts_dialog()); - - // Check that the IdP-sign-in-failure dialog is not shown. - EXPECT_FALSE(dialog_controller_state_.did_show_idp_signin_failure_dialog); - histogram_tester_.ExpectTotalCount( - "Blink.FedCm.Timing.AccountsDialogShownDuration2", 1); - histogram_tester_.ExpectTotalCount( - "Blink.FedCm.Timing.MismatchDialogShownDuration", 1); - - ExpectUKMPresence("AccountsDialogShown"); - ExpectUKMPresence("MismatchDialogShown"); - ExpectUKMPresence("Timing.AccountsDialogShownDuration"); - ExpectUKMPresence("Timing.MismatchDialogShownDuration"); - CheckAllFedCmSessionIDs(); -} - -// Test that the IdP-sign-in-failure dialog is not shown in the -// following sequence of events: -// 1) Failure dialog is shown due to IdP sign-in status mismatch -// 2) FedCM call is aborted. -TEST_F(FederatedAuthRequestImplTest, - FailureUiAbortDoesNotShowIdpSigninFailureDialog) { - base::test::ScopedFeatureList list; - list.InitAndEnableFeature(features::kFedCmIdpSigninStatusEnabled); - - SetNetworkRequestManager( - std::make_unique<ParseStatusOverrideIdpNetworkRequestManager>()); - auto* network_manager = - static_cast<ParseStatusOverrideIdpNetworkRequestManager*>( - test_network_request_manager_.get()); - - url::Origin kIdpOrigin = OriginFromString(kProviderUrlFull); - - // Setup IdP sign-in status mismatch. - network_manager->accounts_parse_status_ = ParseStatus::kInvalidResponseError; - test_permission_delegate_->idp_signin_statuses_[kIdpOrigin] = true; - - RunAuthDontWaitForCallback(kDefaultRequestParameters, kConfigurationValid); - EXPECT_TRUE(did_show_idp_signin_status_mismatch_dialog()); - EXPECT_FALSE(did_show_accounts_dialog()); - - // Abort the request before DelayTimer kicks in. - federated_auth_request_impl_->CancelTokenRequest(); - - RequestExpectations expectations{RequestTokenStatus::kErrorCanceled, - FederatedAuthRequestResult::kErrorCanceled, - /*standalone_console_message=*/std::nullopt, - /*selected_idp_config_url=*/std::nullopt}; - WaitForCurrentAuthRequest(); - CheckAuthExpectations(kConfigurationValid, expectations); - - // Abort should not trigger IdP-sign-in-failure dialog. - EXPECT_FALSE(dialog_controller_state_.did_show_idp_signin_failure_dialog); - - histogram_tester_.ExpectTotalCount( - "Blink.FedCm.Timing.AccountsDialogShownDuration2", 0); - histogram_tester_.ExpectTotalCount( - "Blink.FedCm.Timing.MismatchDialogShownDuration", 1); - - ExpectNoUKMPresence("AccountsDialogShown"); - ExpectUKMPresence("MismatchDialogShown"); - ExpectNoUKMPresence("Timing.AccountsDialogShownDuration"); - ExpectUKMPresence("Timing.MismatchDialogShownDuration"); - CheckAllFedCmSessionIDs(); -} - // Test that when IdpSigninStatus API is in the metrics-only mode, that an IDP // signed-out status stays signed-out regardless of what is returned by the // accounts endpoint.
diff --git a/content/browser/xr/service/vr_service_impl.cc b/content/browser/xr/service/vr_service_impl.cc index 94c2201..3427141 100644 --- a/content/browser/xr/service/vr_service_impl.cc +++ b/content/browser/xr/service/vr_service_impl.cc
@@ -9,7 +9,6 @@ #include <vector> #include "base/containers/contains.h" -#include "base/containers/cxx20_erase.h" #include "base/dcheck_is_on.h" #include "base/feature_list.h" #include "base/functional/bind.h" @@ -540,7 +539,7 @@ // features, but we don't need to block creation if an optional feature is // not supported. Remove all unsupported optional features from the // optional_features collection before handing it off. - base::EraseIf(options->optional_features, [runtime](auto& feature) { + std::erase_if(options->optional_features, [runtime](auto& feature) { return !runtime->SupportsFeature(feature); });
diff --git a/content/public/browser/identity_request_dialog_controller.cc b/content/public/browser/identity_request_dialog_controller.cc index b1215841..7ae3923 100644 --- a/content/public/browser/identity_request_dialog_controller.cc +++ b/content/public/browser/identity_request_dialog_controller.cc
@@ -109,13 +109,6 @@ return std::nullopt; } -void IdentityRequestDialogController::ShowIdpSigninFailureDialog( - base::OnceClosure dismiss_callback) { - if (!is_interception_enabled_) { - std::move(dismiss_callback).Run(); - } -} - void IdentityRequestDialogController::ShowUrl(LinkType type, const GURL& url) {} WebContents* IdentityRequestDialogController::ShowModalDialog(
diff --git a/content/public/browser/identity_request_dialog_controller.h b/content/public/browser/identity_request_dialog_controller.h index 408c2d9..c1d84095 100644 --- a/content/public/browser/identity_request_dialog_controller.h +++ b/content/public/browser/identity_request_dialog_controller.h
@@ -179,9 +179,6 @@ virtual std::string GetTitle() const; virtual std::optional<std::string> GetSubtitle() const; - // Show dialog notifying user that IdP sign-in failed. - virtual void ShowIdpSigninFailureDialog(base::OnceClosure dismiss_callback); - // Open a popup or similar that shows the specified URL. virtual void ShowUrl(LinkType type, const GURL& url);
diff --git a/content/public/common/content_features.cc b/content/public/common/content_features.cc index 1c2330e..66bf15b 100644 --- a/content/public/common/content_features.cc +++ b/content/public/common/content_features.cc
@@ -619,7 +619,7 @@ // If the network service is enabled, runs it in process. BASE_FEATURE(kNetworkServiceInProcess, "NetworkServiceInProcess2", -#if BUILDFLAG(IS_ANDROID) +#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS) base::FEATURE_ENABLED_BY_DEFAULT #else base::FEATURE_DISABLED_BY_DEFAULT
diff --git a/content/renderer/pepper/pepper_media_device_manager.cc b/content/renderer/pepper/pepper_media_device_manager.cc index e528bc9..da39a08 100644 --- a/content/renderer/pepper/pepper_media_device_manager.cc +++ b/content/renderer/pepper/pepper_media_device_manager.cc
@@ -3,9 +3,9 @@ // found in the LICENSE file. #include "content/renderer/pepper/pepper_media_device_manager.h" +#include <vector> #include "base/check.h" -#include "base/containers/cxx20_erase.h" #include "base/feature_list.h" #include "base/functional/bind.h" #include "base/location.h" @@ -144,7 +144,7 @@ SubscriptionList& subscriptions = device_change_subscriptions_[static_cast<size_t>( ToMediaDeviceType(type))]; - base::EraseIf(subscriptions, + std::erase_if(subscriptions, [subscription_id](const Subscription& subscription) { return subscription.first == subscription_id; });
diff --git a/content/renderer/service_worker/service_worker_provider_context.cc b/content/renderer/service_worker/service_worker_provider_context.cc index e4d7322..d70da714 100644 --- a/content/renderer/service_worker/service_worker_provider_context.cc +++ b/content/renderer/service_worker/service_worker_provider_context.cc
@@ -8,7 +8,6 @@ #include <utility> #include <vector> -#include "base/containers/cxx20_erase.h" #include "base/feature_list.h" #include "base/functional/bind.h" #include "base/memory/ref_counted.h" @@ -361,7 +360,7 @@ void ServiceWorkerProviderContext::UnregisterWorkerFetchContext( blink::mojom::ServiceWorkerWorkerClient* client) { CHECK(main_thread_task_runner_->RunsTasksInCurrentSequence()); - base::EraseIf( + std::erase_if( worker_clients_, [client](const mojo::Remote<blink::mojom::ServiceWorkerWorkerClient>& remote_client) { return remote_client.get() == client; });
diff --git a/content/services/auction_worklet/auction_v8_helper_unittest.cc b/content/services/auction_worklet/auction_v8_helper_unittest.cc index 288c817..3a3622f 100644 --- a/content/services/auction_worklet/auction_v8_helper_unittest.cc +++ b/content/services/auction_worklet/auction_v8_helper_unittest.cc
@@ -1561,9 +1561,10 @@ } TEST_F(AuctionV8HelperTest, ExtractJsonTimeout) { - // Use a shorter timeout so test runs faster. - const base::TimeDelta kTimeout = base::Milliseconds(20); - auto time_limit = helper_->CreateTimeLimit(kTimeout); + // While it's tempting to use a shorter timeout since this is a + // non-termination test, that flakes occasionally, and even more so under + // *SAN, for which the default is auto-adjusted. + auto time_limit = helper_->CreateTimeLimit(/*script_timeout=*/std::nullopt); auto time_limit_scope = std::make_unique<AuctionV8Helper::TimeLimitScope>(time_limit.get());
diff --git a/content/services/auction_worklet/bidder_worklet.cc b/content/services/auction_worklet/bidder_worklet.cc index 0998214..05d31c3 100644 --- a/content/services/auction_worklet/bidder_worklet.cc +++ b/content/services/auction_worklet/bidder_worklet.cc
@@ -995,7 +995,7 @@ if (kanon_mode == mojom::KAnonymityBidMode::kEnforce) { PrivateAggregationRequests non_kanon_pa_requests = std::move(result->pa_requests); - base::EraseIf( + std::erase_if( non_kanon_pa_requests, [](const auction_worklet::mojom::PrivateAggregationRequestPtr& request) { return !HasKAnonFailureComponent(request); });
diff --git a/content/shell/BUILD.gn b/content/shell/BUILD.gn index 7579ffd..22a47b5 100644 --- a/content/shell/BUILD.gn +++ b/content/shell/BUILD.gn
@@ -33,6 +33,8 @@ import("//third_party/fuchsia-gn-sdk/src/package.gni") } else if (is_ios) { import("//build/config/ios/config.gni") + import("//build/config/ios/swift_source_set.gni") + import("//build/ios/extension_bundle_data.gni") } # TODO(crbug.com/1336055, spang): Investigate using shell_views with cast builds as @@ -45,6 +47,24 @@ content_shell_major_version = "999" } +template("extension2_bundle_data") { + assert(defined(invoker.extension_target), + "extension_target must be defined for $target_name") + + _extension_name = get_label_info(invoker.extension_target, "name") + ".appex" + if (defined(invoker.extension_name)) { + _extension_name = invoker.extension_name + } + + bundle_data(target_name) { + testonly = true + public_deps = [ invoker.extension_target ] + outputs = [ "{{bundle_contents_dir}}/Extensions/{{source_file_part}}" ] + sources = [ get_label_info(invoker.extension_target, "root_out_dir") + + "/$_extension_name" ] + } +} + config("content_shell_lib_warnings") { if (is_clang) { # TODO(thakis): Remove this once http://crbug.com/383820 is figured out @@ -616,11 +636,11 @@ # Path to an entitlements file used in ios_content_shell. Can be overridden # to provide an alternative. ios_content_shell_entitlements_path = - "//build/config/ios/entitlements.plist" + "//content/shell/app/ios/content_shell.entitlements" } ios_app_bundle("content_shell") { - info_plist = "app/ios-Info.plist" + info_plist = "app/ios/ios-app.plist" testonly = true sources = [ "app/shell_main.cc" ] @@ -629,15 +649,29 @@ ":content_shell_app", ":content_shell_lib", "//base", + "//build:ios_buildflags", "//content/public/app", ] bundle_deps = [ ":content_shell_framework_resources", "app/ios/resources", ] + if (ios_deployment_target == "17.4") { + deps += [ ":content_process_bundle" ] + } entitlements_path = ios_content_shell_entitlements_path bundle_identifier = ios_content_shell_bundle_identifier } + + if (ios_deployment_target == "17.4" && + current_toolchain == default_toolchain) { + _extension_toolchain = "${current_toolchain}_app_ext" + extension2_bundle_data("content_process_bundle") { + extension_name = "content_process.appex" + extension_target = + "//content/shell/app/ios:content_process(${_extension_toolchain})" + } + } } else { executable("content_shell") { testonly = true
diff --git a/content/shell/app/ios/BUILD.gn b/content/shell/app/ios/BUILD.gn new file mode 100644 index 0000000..7684e84 --- /dev/null +++ b/content/shell/app/ios/BUILD.gn
@@ -0,0 +1,42 @@ +# Copyright 2024 The Chromium Authors +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import("//build/apple/compile_entitlements.gni") +import("//build/apple/tweak_info_plist.gni") +import("//build/config/ios/rules.gni") +import("//ios/build/chrome_build.gni") +import("//ios/build/config.gni") + +tweak_info_plist("tweak_info_plist") { + info_plist = "ios-content.plist" +} + +compile_entitlements("entitlements") { + substitutions = [ "IOS_BUNDLE_ID_PREFIX=$ios_app_bundle_id_prefix" ] + output_name = "$target_gen_dir/content_process.appex.entitlements" + entitlements_templates = + [ "../../../app/ios/appex/content_process.appex.entitlements" ] +} + +ios_appex_bundle("content_process") { + testonly = true + output_name = "content_process" + + sources = [ "../shell_main.cc" ] + + deps = [ + "//content/app/ios/appex:content_process", + "//content/public/app", + "//content/shell:content_shell_app", + "//content/shell:content_shell_lib", + ] + bundle_deps = [ + "//content/shell:content_shell_framework_resources", + "//content/shell/app/ios/resources", + ] + + entitlements_target = ":entitlements" + info_plist_target = ":tweak_info_plist" + bundle_identifier = "$shared_bundle_id_for_test_apps.ContentProcess" +}
diff --git a/content/shell/app/ios/content_shell.entitlements b/content/shell/app/ios/content_shell.entitlements new file mode 100644 index 0000000..2848a71 --- /dev/null +++ b/content/shell/app/ios/content_shell.entitlements
@@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>com.apple.developer.web-browser</key> + <true/> + <key>com.apple.developer.web-browser-engine.host</key> + <true/> +</dict> +</plist> \ No newline at end of file
diff --git a/content/shell/app/ios-Info.plist b/content/shell/app/ios/ios-app.plist similarity index 96% rename from content/shell/app/ios-Info.plist rename to content/shell/app/ios/ios-app.plist index ac61d73..b0557a2 100644 --- a/content/shell/app/ios-Info.plist +++ b/content/shell/app/ios/ios-app.plist
@@ -56,5 +56,7 @@ <string>Allow content_shell access to Bluetooth</string> <key>NSLocationWhenInUseUsageDescription</key> <string>Allow content_shell access to location</string> + <key>com.apple.developer.web-browser-engine.host</key> + <true/> </dict> </plist>
diff --git a/content/shell/app/ios/ios-content.plist b/content/shell/app/ios/ios-content.plist new file mode 100644 index 0000000..c364e13 --- /dev/null +++ b/content/shell/app/ios/ios-content.plist
@@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>EXAppExtensionAttributes</key> + <dict> + <key>EXExtensionPointIdentifier</key> + <string>com.apple.web-browser-engine.content</string> + </dict> + <key>CFBundleIdentifier</key> + <string>${BUNDLE_IDENTIFIER}</string> + <key>CFBundleExecutable</key> + <string>${EXECUTABLE_NAME}</string> + <key>CFBundleVersion</key> + <string>1.0</string> + <key>CFBundleName</key> + <string>${PRODUCT_NAME}</string> +</dict> +</plist> \ No newline at end of file
diff --git a/content/shell/app/shell_main.cc b/content/shell/app/shell_main.cc index 80d6fe7..9810903d 100644 --- a/content/shell/app/shell_main.cc +++ b/content/shell/app/shell_main.cc
@@ -16,6 +16,7 @@ #if BUILDFLAG(IS_IOS) #include "base/at_exit.h" // nogncheck #include "base/command_line.h" // nogncheck +#include "build/ios_buildflags.h" // nogncheck #include "content/public/common/content_switches.h" // nogncheck #include "content/shell/app/ios/shell_application_ios.h" #include "content/shell/app/ios/web_tests_support_ios.h" @@ -48,6 +49,19 @@ #elif BUILDFLAG(IS_IOS) +#if BUILDFLAG(IS_IOS_APP_EXTENSION) +extern "C" int ContentProcessMain(int argc, const char** argv) { + // Create this here since it's needed to start the crash handler. + base::AtExitManager at_exit; + base::CommandLine::Init(argc, argv); + content::ShellMainDelegate delegate; + content::ContentMainParams params(&delegate); + params.argc = argc; + params.argv = argv; + return content::ContentMain(std::move(params)); +} +#else + int main(int argc, const char** argv) { // Create this here since it's needed to start the crash handler. base::AtExitManager at_exit; @@ -75,6 +89,7 @@ return content::ContentMain(std::move(params)); } } +#endif #else
diff --git a/content/shell/browser/shell_content_browser_client.cc b/content/shell/browser/shell_content_browser_client.cc index d53d234..c6cae71 100644 --- a/content/shell/browser/shell_content_browser_client.cc +++ b/content/shell/browser/shell_content_browser_client.cc
@@ -14,7 +14,6 @@ #include "base/base_switches.h" #include "base/command_line.h" -#include "base/containers/cxx20_erase.h" #include "base/containers/flat_set.h" #include "base/feature_list.h" #include "base/files/file.h" @@ -346,7 +345,7 @@ } ShellContentBrowserClient::~ShellContentBrowserClient() { - base::Erase(GetShellContentBrowserClientInstancesImpl(), this); + std::erase(GetShellContentBrowserClientInstancesImpl(), this); } std::unique_ptr<BrowserMainParts>
diff --git a/content/shell/browser/shell_devtools_bindings.cc b/content/shell/browser/shell_devtools_bindings.cc index 63c9a6fd..a528b235 100644 --- a/content/shell/browser/shell_devtools_bindings.cc +++ b/content/shell/browser/shell_devtools_bindings.cc
@@ -7,10 +7,10 @@ #include <stddef.h> #include <utility> +#include <vector> #include "base/base64.h" #include "base/containers/contains.h" -#include "base/containers/cxx20_erase.h" #include "base/functional/bind.h" #include "base/functional/callback_helpers.h" #include "base/json/json_reader.h" @@ -186,7 +186,7 @@ auto* bindings = GetShellDevtoolsBindingsInstances(); DCHECK(base::Contains(*bindings, this)); - base::Erase(*bindings, this); + std::erase(*bindings, this); } // static
diff --git a/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt index f86b280e..6acf56e 100644 --- a/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt +++ b/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
@@ -540,6 +540,20 @@ crbug.com/326283382 [ mac angle-metal graphite-enabled ] conformance/context/context-creation-and-destruction.html [ RetryOnFailure ] +## SkiaGraphite on on SwANGLE ## + +crbug.com/41495039 [ mac angle-swiftshader graphite-enabled ] conformance/context/context-attributes-alpha-depth-stencil-antialias.html [ Failure ] +crbug.com/41495039 [ mac angle-swiftshader graphite-enabled ] conformance/context/context-hidden-alpha.html [ Failure ] +crbug.com/41495039 [ mac angle-swiftshader graphite-enabled ] conformance/textures/misc/copy-tex-image-2d-formats.html [ Failure ] +crbug.com/41495039 [ mac angle-swiftshader graphite-enabled ] conformance/textures/misc/tex-input-validation.html [ Failure ] +crbug.com/41495039 [ mac angle-swiftshader graphite-enabled ] conformance/textures/webgl_canvas/tex-2d-alpha-alpha-unsigned_byte.html [ Failure ] +crbug.com/41495039 [ mac angle-swiftshader graphite-enabled ] conformance/textures/webgl_canvas/tex-2d-luminance-luminance-unsigned_byte.html [ Failure ] +crbug.com/41495039 [ mac angle-swiftshader graphite-enabled ] conformance/textures/webgl_canvas/tex-2d-luminance_alpha-luminance_alpha-unsigned_byte.html [ Failure ] +crbug.com/41495039 [ mac angle-swiftshader graphite-enabled ] conformance/textures/webgl_canvas/tex-2d-rgb-rgb-unsigned_byte.html [ Failure ] +crbug.com/41495039 [ mac angle-swiftshader graphite-enabled ] conformance/textures/webgl_canvas/tex-2d-rgb-rgb-unsigned_short_5_6_5.html [ Failure ] +crbug.com/41495039 [ mac angle-swiftshader graphite-enabled ] conformance/textures/webgl_canvas/tex-2d-rgba-rgba-unsigned_byte.html [ Failure ] +crbug.com/41495039 [ mac angle-swiftshader graphite-enabled ] conformance/textures/webgl_canvas/tex-2d-rgba-rgba-unsigned_short_4_4_4_4.html [ Failure ] + ## Mac AMD failures ## crbug.com/642822 [ mac amd ] conformance/rendering/clipping-wide-points.html [ Failure ]
diff --git a/content/test/web_contents_observer_consistency_checker.cc b/content/test/web_contents_observer_consistency_checker.cc index dcd5f36..913d0d4 100644 --- a/content/test/web_contents_observer_consistency_checker.cc +++ b/content/test/web_contents_observer_consistency_checker.cc
@@ -3,9 +3,9 @@ // found in the LICENSE file. #include "content/test/web_contents_observer_consistency_checker.h" +#include <vector> #include "base/containers/contains.h" -#include "base/containers/cxx20_erase.h" #include "base/memory/ptr_util.h" #include "base/pending_task.h" #include "base/strings/stringprintf.h" @@ -354,7 +354,7 @@ WebContentsObserver::MediaStoppedReason reason) { CHECK(!web_contents_destroyed_); CHECK(base::Contains(active_media_players_, id)); - base::Erase(active_media_players_, id); + std::erase(active_media_players_, id); } bool WebContentsObserverConsistencyChecker::OnMessageReceived(
diff --git a/crypto/apple_keychain_v2.h b/crypto/apple_keychain_v2.h index 75cbd02..3d14b5eb5 100644 --- a/crypto/apple_keychain_v2.h +++ b/crypto/apple_keychain_v2.h
@@ -52,9 +52,8 @@ // ItemDelete wraps the |SecItemDelete| function. virtual OSStatus ItemDelete(CFDictionaryRef query); // ItemDelete wraps the |SecItemUpdate| function. - virtual OSStatus ItemUpdate( - CFDictionaryRef query, - base::apple::ScopedCFTypeRef<CFMutableDictionaryRef> keychain_data); + virtual OSStatus ItemUpdate(CFDictionaryRef query, + CFDictionaryRef keychain_data); protected: AppleKeychainV2();
diff --git a/crypto/apple_keychain_v2.mm b/crypto/apple_keychain_v2.mm index e018683..2e91e72 100644 --- a/crypto/apple_keychain_v2.mm +++ b/crypto/apple_keychain_v2.mm
@@ -80,10 +80,9 @@ return SecItemDelete(query); } -OSStatus AppleKeychainV2::ItemUpdate( - CFDictionaryRef query, - base::apple::ScopedCFTypeRef<CFMutableDictionaryRef> keychain_data) { - return SecItemUpdate(query, keychain_data.get()); +OSStatus AppleKeychainV2::ItemUpdate(CFDictionaryRef query, + CFDictionaryRef keychain_data) { + return SecItemUpdate(query, keychain_data); } } // namespace crypto
diff --git a/crypto/fake_apple_keychain_v2.h b/crypto/fake_apple_keychain_v2.h index 81803153..bb61d64c 100644 --- a/crypto/fake_apple_keychain_v2.h +++ b/crypto/fake_apple_keychain_v2.h
@@ -40,8 +40,7 @@ OSStatus ItemCopyMatching(CFDictionaryRef query, CFTypeRef* result) override; OSStatus ItemDelete(CFDictionaryRef query) override; OSStatus ItemUpdate(CFDictionaryRef query, - base::apple::ScopedCFTypeRef<CFMutableDictionaryRef> - keychain_data) override; + CFDictionaryRef keychain_data) override; private: // items_ contains the keychain items created by `KeyCreateRandomKey`.
diff --git a/crypto/fake_apple_keychain_v2.mm b/crypto/fake_apple_keychain_v2.mm index 0d41ee1e..12254b2 100644 --- a/crypto/fake_apple_keychain_v2.mm +++ b/crypto/fake_apple_keychain_v2.mm
@@ -14,6 +14,7 @@ #import <Foundation/Foundation.h> #import <Security/Security.h> +#include "base/apple/bridging.h" #include "base/apple/foundation_util.h" #include "base/apple/scoped_cftyperef.h" #include "base/check_op.h" @@ -225,9 +226,8 @@ return errSecItemNotFound; } -OSStatus FakeAppleKeychainV2::ItemUpdate( - CFDictionaryRef query, - base::apple::ScopedCFTypeRef<CFMutableDictionaryRef> attributes_to_update) { +OSStatus FakeAppleKeychainV2::ItemUpdate(CFDictionaryRef query, + CFDictionaryRef attributes_to_update) { DCHECK_EQ(base::apple::GetValueFromDictionary<CFStringRef>(query, kSecClass), kSecClassKey); DCHECK(CFEqual(base::apple::GetValueFromDictionary<CFStringRef>( @@ -237,8 +237,7 @@ base::apple::GetValueFromDictionary<CFDataRef>(query, kSecAttrApplicationLabel); DCHECK(query_credential_id); - for (auto it = items_.begin(); it != items_.end(); ++it) { - const base::apple::ScopedCFTypeRef<CFDictionaryRef>& item = *it; + for (base::apple::ScopedCFTypeRef<CFDictionaryRef>& item : items_) { CFDataRef item_credential_id = base::apple::GetValueFromDictionary<CFDataRef>( item.get(), kSecAttrApplicationLabel); @@ -249,16 +248,10 @@ base::apple::ScopedCFTypeRef<CFMutableDictionaryRef> item_copy( CFDictionaryCreateMutableCopy(kCFAllocatorDefault, /*capacity=*/0, item.get())); - size_t size = CFDictionaryGetCount(attributes_to_update.get()); - std::vector<CFStringRef> keys(size, nullptr); - std::vector<CFDictionaryRef> values(size, nullptr); - CFDictionaryGetKeysAndValues(attributes_to_update.get(), - reinterpret_cast<const void**>(keys.data()), - reinterpret_cast<const void**>(values.data())); - for (size_t i = 0; i < size; ++i) { - CFDictionarySetValue(item_copy.get(), keys[i], values[i]); - } - *it = base::apple::ScopedCFTypeRef<CFDictionaryRef>(item_copy.release()); + [base::apple::CFToNSPtrCast(item_copy.get()) + addEntriesFromDictionary:base::apple::CFToNSPtrCast( + attributes_to_update)]; + item = item_copy; return errSecSuccess; } return errSecItemNotFound;
diff --git a/crypto/unexportable_key_unittest.cc b/crypto/unexportable_key_unittest.cc index 634d959..3d6e54f 100644 --- a/crypto/unexportable_key_unittest.cc +++ b/crypto/unexportable_key_unittest.cc
@@ -84,6 +84,13 @@ const base::TimeTicks generate_start = base::TimeTicks::Now(); std::unique_ptr<crypto::UnexportableSigningKey> key = provider->GenerateSigningKeySlowly(algorithms); + if (algo == crypto::SignatureVerifier::SignatureAlgorithm::ECDSA_SHA256) { + if (!key) { + GTEST_SKIP() + << "Workaround for https://issues.chromium.org/issues/41494935"; + } + } + ASSERT_TRUE(key); LOG(INFO) << "Generation took " << (base::TimeTicks::Now() - generate_start);
diff --git a/device/bluetooth/bluetooth_adapter_winrt.cc b/device/bluetooth/bluetooth_adapter_winrt.cc index 9be10888..a7bdfc1 100644 --- a/device/bluetooth/bluetooth_adapter_winrt.cc +++ b/device/bluetooth/bluetooth_adapter_winrt.cc
@@ -14,7 +14,6 @@ #include <vector> #include "base/containers/contains.h" -#include "base/containers/cxx20_erase.h" #include "base/containers/span.h" #include "base/functional/bind.h" #include "base/functional/callback.h" @@ -1369,7 +1368,7 @@ CreateAdvertisementCallback callback) { DCHECK(base::Contains(pending_advertisements_, advertisement)); auto wrapped_advertisement = base::WrapRefCounted(advertisement); - base::Erase(pending_advertisements_, advertisement); + std::erase(pending_advertisements_, advertisement); std::move(callback).Run(std::move(wrapped_advertisement)); } @@ -1379,7 +1378,7 @@ BluetoothAdvertisement::ErrorCode error_code) { // Note: We are not DCHECKing that |pending_advertisements_| contains // |advertisement|, as this method might be invoked during destruction. - base::Erase(pending_advertisements_, advertisement); + std::erase(pending_advertisements_, advertisement); std::move(error_callback).Run(error_code); }
diff --git a/device/fido/fido_device_authenticator.cc b/device/fido/fido_device_authenticator.cc index 34a2376..4377b2a5 100644 --- a/device/fido/fido_device_authenticator.cc +++ b/device/fido/fido_device_authenticator.cc
@@ -7,9 +7,9 @@ #include <algorithm> #include <numeric> #include <utility> +#include <vector> #include "base/containers/contains.h" -#include "base/containers/cxx20_erase_vector.h" #include "base/functional/bind.h" #include "base/logging.h" #include "base/metrics/histogram_functions.h" @@ -1318,7 +1318,7 @@ } } } - bool did_erase = base::EraseIf( + bool did_erase = std::erase_if( *large_blob_array, [&large_blob_keys](const cbor::Value& blob_cbor) { std::optional<LargeBlobData> blob = LargeBlobData::Parse(blob_cbor); return blob &&
diff --git a/device/fido/mac/browsing_data_deletion_unittest.mm b/device/fido/mac/browsing_data_deletion_unittest.mm index 29c6b66..7edc4ac 100644 --- a/device/fido/mac/browsing_data_deletion_unittest.mm +++ b/device/fido/mac/browsing_data_deletion_unittest.mm
@@ -8,6 +8,7 @@ #include <Foundation/Foundation.h> #include <Security/Security.h> +#include "base/apple/bridging.h" #include "base/apple/foundation_util.h" #include "base/apple/osstatus_logging.h" #include "base/apple/scoped_cftyperef.h" @@ -35,6 +36,9 @@ extern const CFStringRef kSecAttrNoLegacy; } +using base::apple::CFToNSPtrCast; +using base::apple::NSToCFPtrCast; + namespace device { using test::TestCallbackReceiver; @@ -53,20 +57,15 @@ // Returns a query to use with Keychain instance methods that returns all // credentials in the non-legacy keychain that are tagged with the keychain // access group used in this test. -base::apple::ScopedCFTypeRef<CFMutableDictionaryRef> BaseQuery() { - base::apple::ScopedCFTypeRef<CFMutableDictionaryRef> query( - CFDictionaryCreateMutable(kCFAllocatorDefault, 0, - &kCFTypeDictionaryKeyCallBacks, - &kCFTypeDictionaryValueCallBacks)); - CFDictionarySetValue(query.get(), kSecClass, kSecClassKey); - base::apple::ScopedCFTypeRef<CFStringRef> access_group_ref( - base::SysUTF8ToCFStringRef(kKeychainAccessGroup)); - CFDictionarySetValue(query.get(), kSecAttrAccessGroup, - access_group_ref.get()); - CFDictionarySetValue(query.get(), kSecAttrNoLegacy, kCFBooleanTrue); - CFDictionarySetValue(query.get(), kSecReturnAttributes, kCFBooleanTrue); - CFDictionarySetValue(query.get(), kSecMatchLimit, kSecMatchLimitAll); - return query; +NSDictionary* BaseQuery() { + return @{ + CFToNSPtrCast(kSecClass) : CFToNSPtrCast(kSecClassKey), + CFToNSPtrCast(kSecAttrAccessGroup) : + base::SysUTF8ToNSString(kKeychainAccessGroup), + CFToNSPtrCast(kSecAttrNoLegacy) : @YES, + CFToNSPtrCast(kSecReturnAttributes) : @YES, + CFToNSPtrCast(kSecMatchLimit) : CFToNSPtrCast(kSecMatchLimitAll), + }; } // Returns all WebAuthn credentials stored in the keychain, regardless of which @@ -75,7 +74,8 @@ base::apple::ScopedCFTypeRef<CFArrayRef> QueryAllCredentials() { base::apple::ScopedCFTypeRef<CFArrayRef> items; OSStatus status = crypto::AppleKeychainV2::GetInstance().ItemCopyMatching( - BaseQuery().get(), reinterpret_cast<CFTypeRef*>(items.InitializeInto())); + NSToCFPtrCast(BaseQuery()), + reinterpret_cast<CFTypeRef*>(items.InitializeInto())); if (status == errSecItemNotFound) { // The API returns null, but we should return an empty array instead to // distinguish from real errors. @@ -95,8 +95,8 @@ } bool ResetKeychain() { - OSStatus status = - crypto::AppleKeychainV2::GetInstance().ItemDelete(BaseQuery().get()); + OSStatus status = crypto::AppleKeychainV2::GetInstance().ItemDelete( + NSToCFPtrCast(BaseQuery())); if (status != errSecSuccess && status != errSecItemNotFound) { OSSTATUS_DLOG(ERROR, status); return false;
diff --git a/device/fido/mac/credential_store.mm b/device/fido/mac/credential_store.mm index 3c6b7ac..39d6b573 100644 --- a/device/fido/mac/credential_store.mm +++ b/device/fido/mac/credential_store.mm
@@ -29,6 +29,9 @@ #include "device/fido/mac/credential_metadata.h" #include "device/fido/mac/touch_id_context.h" +using base::apple::CFToNSPtrCast; +using base::apple::NSToCFPtrCast; + namespace device::fido::mac { namespace { @@ -37,22 +40,15 @@ // the keychain item class, keychain access group and RP ID (unless `rp_id` is // `nullopt`) filled out. More fields can be set on the return value to refine // the query. -base::apple::ScopedCFTypeRef<CFMutableDictionaryRef> DefaultKeychainQuery( - const AuthenticatorConfig& config, - std::optional<std::string> rp_id) { - base::apple::ScopedCFTypeRef<CFMutableDictionaryRef> query( - CFDictionaryCreateMutable(kCFAllocatorDefault, 0, - &kCFTypeDictionaryKeyCallBacks, - &kCFTypeDictionaryValueCallBacks)); - CFDictionarySetValue(query.get(), kSecClass, kSecClassKey); - CFDictionarySetValue( - query.get(), kSecAttrAccessGroup, - base::SysUTF8ToCFStringRef(config.keychain_access_group).get()); +NSMutableDictionary* DefaultKeychainQuery(const AuthenticatorConfig& config, + std::optional<std::string> rp_id) { + NSMutableDictionary* query = [NSMutableDictionary dictionary]; + query[CFToNSPtrCast(kSecClass)] = CFToNSPtrCast(kSecClassKey); + query[CFToNSPtrCast(kSecAttrAccessGroup)] = + base::SysUTF8ToNSString(config.keychain_access_group); if (rp_id) { - CFDictionarySetValue( - query.get(), kSecAttrLabel, - base::SysUTF8ToCFStringRef(EncodeRpId(config.metadata_secret, *rp_id)) - .get()); + query[CFToNSPtrCast(kSecAttrLabel)] = + base::SysUTF8ToNSString(EncodeRpId(config.metadata_secret, *rp_id)); } return query; } @@ -94,22 +90,20 @@ // keychain access group. std::vector<base::apple::ScopedCFTypeRef<CFDictionaryRef>> result; - base::apple::ScopedCFTypeRef<CFMutableDictionaryRef> query( - CFDictionaryCreateMutable(kCFAllocatorDefault, 0, - &kCFTypeDictionaryKeyCallBacks, - &kCFTypeDictionaryValueCallBacks)); - CFDictionarySetValue(query.get(), kSecClass, kSecClassKey); - CFDictionarySetValue(query.get(), kSecAttrAccessGroup, - base::SysUTF8ToCFStringRef(keychain_access_group).get()); - CFDictionarySetValue(query.get(), kSecMatchLimit, kSecMatchLimitAll); - // Return the key reference and its attributes. - CFDictionarySetValue(query.get(), kSecReturnRef, kCFBooleanTrue); - CFDictionarySetValue(query.get(), kSecReturnAttributes, kCFBooleanTrue); + NSDictionary* query = @{ + CFToNSPtrCast(kSecClass) : CFToNSPtrCast(kSecClassKey), + CFToNSPtrCast(kSecAttrAccessGroup) : + base::SysUTF8ToNSString(keychain_access_group), + CFToNSPtrCast(kSecMatchLimit) : CFToNSPtrCast(kSecMatchLimitAll), + // Return the key reference and its attributes. + CFToNSPtrCast(kSecReturnRef) : @YES, + CFToNSPtrCast(kSecReturnAttributes) : @YES, + }; base::apple::ScopedCFTypeRef<CFArrayRef> keychain_items; { OSStatus status = crypto::AppleKeychainV2::GetInstance().ItemCopyMatching( - query.get(), + NSToCFPtrCast(query), reinterpret_cast<CFTypeRef*>(keychain_items.InitializeInto())); if (status == errSecItemNotFound) { DVLOG(1) << "no credentials found"; @@ -221,47 +215,9 @@ const std::string& rp_id, const PublicKeyCredentialUserEntity& user, Discoverable discoverable) const { - base::apple::ScopedCFTypeRef<CFMutableDictionaryRef> params( - CFDictionaryCreateMutable(kCFAllocatorDefault, 0, - &kCFTypeDictionaryKeyCallBacks, - &kCFTypeDictionaryValueCallBacks)); - CFDictionarySetValue( - params.get(), kSecAttrAccessGroup, - base::SysUTF8ToCFStringRef(config_.keychain_access_group).get()); - CFDictionarySetValue(params.get(), kSecAttrKeyType, - kSecAttrKeyTypeECSECPrimeRandom); - CFDictionarySetValue(params.get(), kSecAttrKeySizeInBits, - base::apple::NSToCFPtrCast(@256)); - CFDictionarySetValue(params.get(), kSecAttrSynchronizable, kCFBooleanFalse); - CFDictionarySetValue(params.get(), kSecAttrTokenID, - kSecAttrTokenIDSecureEnclave); + NSMutableDictionary* private_key_params = [NSMutableDictionary dictionary]; + private_key_params[CFToNSPtrCast(kSecAttrIsPermanent)] = @YES; - CFDictionarySetValue( - params.get(), kSecAttrLabel, - base::SysUTF8ToCFStringRef(EncodeRpId(config_.metadata_secret, rp_id)) - .get()); - auto credential_metadata = - CredentialMetadata::FromPublicKeyCredentialUserEntity( - user, discoverable == kDiscoverable); - const std::vector<uint8_t> sealed_metadata = SealCredentialMetadata( - config_.metadata_secret, rp_id, credential_metadata); - CFDictionarySetValue(params.get(), kSecAttrApplicationTag, - base::apple::NSToCFPtrCast([NSData - dataWithBytes:sealed_metadata.data() - length:sealed_metadata.size()])); - const std::vector<uint8_t> credential_id = GenerateRandomCredentialId(); - CFDictionarySetValue( - params.get(), kSecAttrApplicationLabel, - base::apple::NSToCFPtrCast([NSData dataWithBytes:credential_id.data() - length:credential_id.size()])); - base::apple::ScopedCFTypeRef<CFMutableDictionaryRef> private_key_params( - CFDictionaryCreateMutable(kCFAllocatorDefault, 0, - &kCFTypeDictionaryKeyCallBacks, - &kCFTypeDictionaryValueCallBacks)); - CFDictionarySetValue(params.get(), kSecPrivateKeyAttrs, - private_key_params.get()); - CFDictionarySetValue(private_key_params.get(), kSecAttrIsPermanent, - kCFBooleanTrue); // The credential can only be used for signing, and the device needs to be in // an unlocked state. auto flags = kSecAccessControlPrivateKeyUsage; @@ -269,17 +225,44 @@ SecAccessControlCreateWithFlags( kCFAllocatorDefault, kSecAttrAccessibleWhenUnlockedThisDeviceOnly, flags, /*error=*/nullptr)); - CFDictionarySetValue(private_key_params.get(), kSecAttrAccessControl, - access_control.get()); + private_key_params[CFToNSPtrCast(kSecAttrAccessControl)] = + (__bridge id)access_control.get(); if (objc_storage_->authentication_context) { - CFDictionarySetValue( - private_key_params.get(), kSecUseAuthenticationContext, - (__bridge CFTypeRef)objc_storage_->authentication_context); + private_key_params[CFToNSPtrCast(kSecUseAuthenticationContext)] = + objc_storage_->authentication_context; } + + auto credential_metadata = + CredentialMetadata::FromPublicKeyCredentialUserEntity( + user, discoverable == kDiscoverable); + const std::vector<uint8_t> sealed_metadata = SealCredentialMetadata( + config_.metadata_secret, rp_id, credential_metadata); + + const std::vector<uint8_t> credential_id = GenerateRandomCredentialId(); + + NSDictionary* params = @{ + CFToNSPtrCast(kSecAttrAccessGroup) : + base::SysUTF8ToNSString(config_.keychain_access_group), + CFToNSPtrCast(kSecAttrKeyType) : + CFToNSPtrCast(kSecAttrKeyTypeECSECPrimeRandom), + CFToNSPtrCast(kSecAttrKeySizeInBits) : @256, + CFToNSPtrCast(kSecAttrSynchronizable) : @NO, + CFToNSPtrCast(kSecAttrTokenID) : + CFToNSPtrCast(kSecAttrTokenIDSecureEnclave), + CFToNSPtrCast(kSecAttrLabel) : + base::SysUTF8ToNSString(EncodeRpId(config_.metadata_secret, rp_id)), + CFToNSPtrCast(kSecAttrApplicationTag) : + [NSData dataWithBytes:sealed_metadata.data() + length:sealed_metadata.size()], + CFToNSPtrCast(kSecAttrApplicationLabel) : + [NSData dataWithBytes:credential_id.data() length:credential_id.size()], + CFToNSPtrCast(kSecPrivateKeyAttrs) : private_key_params, + }; + base::apple::ScopedCFTypeRef<CFErrorRef> cferr; base::apple::ScopedCFTypeRef<SecKeyRef> private_key = crypto::AppleKeychainV2::GetInstance().KeyCreateRandomKey( - params.get(), cferr.InitializeInto()); + NSToCFPtrCast(params), cferr.InitializeInto()); if (!private_key) { FIDO_LOG(ERROR) << "SecKeyCreateRandomKey failed: " << cferr.get(); return std::nullopt; @@ -316,42 +299,9 @@ credential_id); DCHECK(metadata); - base::apple::ScopedCFTypeRef<CFMutableDictionaryRef> params( - CFDictionaryCreateMutable(kCFAllocatorDefault, 0, - &kCFTypeDictionaryKeyCallBacks, - &kCFTypeDictionaryValueCallBacks)); - CFDictionarySetValue( - params.get(), kSecAttrAccessGroup, - base::SysUTF8ToCFStringRef(config_.keychain_access_group).get()); - CFDictionarySetValue(params.get(), kSecAttrKeyType, - kSecAttrKeyTypeECSECPrimeRandom); - CFDictionarySetValue(params.get(), kSecAttrKeySizeInBits, - base::apple::NSToCFPtrCast(@256)); - CFDictionarySetValue(params.get(), kSecAttrSynchronizable, kCFBooleanFalse); - CFDictionarySetValue(params.get(), kSecAttrTokenID, - kSecAttrTokenIDSecureEnclave); + NSMutableDictionary* private_key_params = [NSMutableDictionary dictionary]; + private_key_params[CFToNSPtrCast(kSecAttrIsPermanent)] = @YES; - CFDictionarySetValue( - params.get(), kSecAttrLabel, - base::SysUTF8ToCFStringRef(EncodeRpId(config_.metadata_secret, rp_id)) - .get()); - CFDictionarySetValue( - params.get(), kSecAttrApplicationTag, - base::SysUTF8ToCFStringRef(EncodeRpIdAndUserIdDeprecated( - config_.metadata_secret, rp_id, user.id)) - .get()); - CFDictionarySetValue( - params.get(), kSecAttrApplicationLabel, - base::apple::NSToCFPtrCast([NSData dataWithBytes:credential_id.data() - length:credential_id.size()])); - base::apple::ScopedCFTypeRef<CFMutableDictionaryRef> private_key_params( - CFDictionaryCreateMutable(kCFAllocatorDefault, 0, - &kCFTypeDictionaryKeyCallBacks, - &kCFTypeDictionaryValueCallBacks)); - CFDictionarySetValue(params.get(), kSecPrivateKeyAttrs, - private_key_params.get()); - CFDictionarySetValue(private_key_params.get(), kSecAttrIsPermanent, - kCFBooleanTrue); // Credential can only be used when the device is unlocked. Private key is // available for signing after user authorization with biometrics or // password. @@ -360,17 +310,35 @@ kCFAllocatorDefault, kSecAttrAccessibleWhenUnlockedThisDeviceOnly, kSecAccessControlPrivateKeyUsage | kSecAccessControlUserPresence, /*error=*/nullptr)); - CFDictionarySetValue(private_key_params.get(), kSecAttrAccessControl, - access_control.get()); + private_key_params[CFToNSPtrCast(kSecAttrAccessControl)] = + (__bridge id)access_control.get(); if (objc_storage_->authentication_context) { - CFDictionarySetValue( - private_key_params.get(), kSecUseAuthenticationContext, - (__bridge CFTypeRef)objc_storage_->authentication_context); + private_key_params[CFToNSPtrCast(kSecUseAuthenticationContext)] = + objc_storage_->authentication_context; } + + NSDictionary* params = @{ + CFToNSPtrCast(kSecAttrAccessGroup) : + base::SysUTF8ToNSString(config_.keychain_access_group), + CFToNSPtrCast(kSecAttrKeyType) : + CFToNSPtrCast(kSecAttrKeyTypeECSECPrimeRandom), + CFToNSPtrCast(kSecAttrKeySizeInBits) : @256, + CFToNSPtrCast(kSecAttrSynchronizable) : @NO, + CFToNSPtrCast(kSecAttrTokenID) : + CFToNSPtrCast(kSecAttrTokenIDSecureEnclave), + CFToNSPtrCast(kSecAttrLabel) : + base::SysUTF8ToNSString(EncodeRpId(config_.metadata_secret, rp_id)), + CFToNSPtrCast(kSecAttrApplicationTag) : base::SysUTF8ToNSString( + EncodeRpIdAndUserIdDeprecated(config_.metadata_secret, rp_id, user.id)), + CFToNSPtrCast(kSecAttrApplicationLabel) : + [NSData dataWithBytes:credential_id.data() length:credential_id.size()], + CFToNSPtrCast(kSecPrivateKeyAttrs) : private_key_params, + }; + base::apple::ScopedCFTypeRef<CFErrorRef> cferr; base::apple::ScopedCFTypeRef<SecKeyRef> private_key = crypto::AppleKeychainV2::GetInstance().KeyCreateRandomKey( - params.get(), cferr.InitializeInto()); + NSToCFPtrCast(params), cferr.InitializeInto()); if (!private_key) { FIDO_LOG(ERROR) << "SecKeyCreateRandomKey failed: " << cferr.get(); return std::nullopt; @@ -526,20 +494,18 @@ // Query all credentials for the RP. Filtering for `rp_id` here ensures we // don't retrieve credentials for other profiles, because their // `kSecAttrLabel` attribute wouldn't match the encoded RP ID. - base::apple::ScopedCFTypeRef<CFMutableDictionaryRef> query = - DefaultKeychainQuery(config_, rp_id); + NSMutableDictionary* query = DefaultKeychainQuery(config_, rp_id); if (objc_storage_->authentication_context) { - CFDictionarySetValue( - query.get(), kSecUseAuthenticationContext, - (__bridge CFTypeRef)objc_storage_->authentication_context); + query[CFToNSPtrCast(kSecUseAuthenticationContext)] = + objc_storage_->authentication_context; } - CFDictionarySetValue(query.get(), kSecReturnRef, kCFBooleanTrue); - CFDictionarySetValue(query.get(), kSecReturnAttributes, kCFBooleanTrue); - CFDictionarySetValue(query.get(), kSecMatchLimit, kSecMatchLimitAll); + query[CFToNSPtrCast(kSecReturnRef)] = @YES; + query[CFToNSPtrCast(kSecReturnAttributes)] = @YES; + query[CFToNSPtrCast(kSecMatchLimit)] = CFToNSPtrCast(kSecMatchLimitAll); base::apple::ScopedCFTypeRef<CFArrayRef> keychain_items; OSStatus status = crypto::AppleKeychainV2::GetInstance().ItemCopyMatching( - query.get(), + NSToCFPtrCast(query), reinterpret_cast<CFTypeRef*>(keychain_items.InitializeInto())); if (status == errSecItemNotFound) { return std::list<Credential>(); @@ -643,14 +609,13 @@ bool TouchIdCredentialStore::DeleteCredentialById( base::span<const uint8_t> credential_id) const { - // The sane way to delete a credential would be by SecKeyRef, like so: + // The reasonable way to delete a credential would be by SecKeyRef, like so: // - // base::apple::ScopedCFTypeRef<CFMutableDictionaryRef> query( - // CFDictionaryCreateMutable(kCFAllocatorDefault, 0, - // &kCFTypeDictionaryKeyCallBacks, - // &kCFTypeDictionaryValueCallBacks)); - // CFDictionarySetValue(query, kSecValueRef, sec_key_ref); - // OSStatus status = AppleKeychainV2::GetInstance().ItemDelete(query); + // NSDictionary* query = @{ + // CFToNSPtrCast(kSecValueRef) : (__bridge id)sec_key_ref, + // }; + // OSStatus status = + // AppleKeychainV2::GetInstance().ItemDelete(NSToCFPtrCast(query)); // // But on macOS that looks for `sec_key_ref` in the legacy keychain instead of // the "iOS" keychain that secure enclave credentials live in, and so the call @@ -658,22 +623,18 @@ // `kSecUseDataProtectionKeychain` to force a query to the right keychain, but // we need to support older versions of macOS for now. Hence, we must delete // keychain items by credential ID (stored in `kSecAttrApplicationLabel`). - // TODO(https://crbug.com/1463798): Update to this better approach that + // TODO(https://crbug.com/40275358): Update to this better approach that // requires 10.15 now that Chromium requires 10.15. - base::apple::ScopedCFTypeRef<CFMutableDictionaryRef> query( - CFDictionaryCreateMutable(kCFAllocatorDefault, 0, - &kCFTypeDictionaryKeyCallBacks, - &kCFTypeDictionaryValueCallBacks)); - CFDictionarySetValue( - query.get(), kSecAttrAccessGroup, - base::SysUTF8ToCFStringRef(config_.keychain_access_group).get()); - CFDictionarySetValue(query.get(), kSecClass, kSecClassKey); - CFDictionarySetValue( - query.get(), kSecAttrApplicationLabel, - base::apple::NSToCFPtrCast([NSData dataWithBytes:credential_id.data() - length:credential_id.size()])); + NSDictionary* query = @{ + CFToNSPtrCast(kSecAttrAccessGroup) : + base::SysUTF8ToNSString(config_.keychain_access_group), + CFToNSPtrCast(kSecClass) : CFToNSPtrCast(kSecClassKey), + CFToNSPtrCast(kSecAttrApplicationLabel) : + [NSData dataWithBytes:credential_id.data() length:credential_id.size()], + }; + OSStatus status = - crypto::AppleKeychainV2::GetInstance().ItemDelete(query.get()); + crypto::AppleKeychainV2::GetInstance().ItemDelete(NSToCFPtrCast(query)); if (status != errSecSuccess) { OSSTATUS_DLOG(ERROR, status) << "SecItemDelete failed"; return false; @@ -693,42 +654,34 @@ FIDO_LOG(ERROR) << "no credentials found"; return false; } - base::apple::ScopedCFTypeRef<CFMutableDictionaryRef> params( - CFDictionaryCreateMutable(kCFAllocatorDefault, 0, - &kCFTypeDictionaryKeyCallBacks, - &kCFTypeDictionaryValueCallBacks)); - bool found_credential = false; + + NSData* sealed_metadata_data = nil; for (Credential& credential : *credentials) { if (credential.credential_id == credential_id) { credential.metadata.user_name = username; std::vector<uint8_t> sealed_metadata = SealCredentialMetadata( config_.metadata_secret, credential.rp_id, credential.metadata); - CFDictionarySetValue(params.get(), kSecAttrApplicationTag, - base::apple::NSToCFPtrCast([NSData - dataWithBytes:sealed_metadata.data() - length:sealed_metadata.size()])); - found_credential = true; + sealed_metadata_data = [NSData dataWithBytes:sealed_metadata.data() + length:sealed_metadata.size()]; break; } } - if (!found_credential) { + if (!sealed_metadata_data) { FIDO_LOG(ERROR) << "no credential with matching credential_id"; return false; } - base::apple::ScopedCFTypeRef<CFMutableDictionaryRef> query( - CFDictionaryCreateMutable(kCFAllocatorDefault, 0, - &kCFTypeDictionaryKeyCallBacks, - &kCFTypeDictionaryValueCallBacks)); - CFDictionarySetValue( - query.get(), kSecAttrAccessGroup, - base::SysUTF8ToCFStringRef(config_.keychain_access_group).get()); - CFDictionarySetValue(query.get(), kSecClass, kSecClassKey); - CFDictionarySetValue( - query.get(), kSecAttrApplicationLabel, - base::apple::NSToCFPtrCast([NSData dataWithBytes:credential_id.data() - length:credential_id.size()])); - OSStatus status = - crypto::AppleKeychainV2::GetInstance().ItemUpdate(query.get(), params); + + NSDictionary* query = @{ + CFToNSPtrCast(kSecAttrAccessGroup) : + base::SysUTF8ToNSString(config_.keychain_access_group), + CFToNSPtrCast(kSecClass) : CFToNSPtrCast(kSecClassKey), + CFToNSPtrCast(kSecAttrApplicationLabel) : + [NSData dataWithBytes:credential_id.data() length:credential_id.size()], + }; + NSDictionary* params = + @{CFToNSPtrCast(kSecAttrApplicationTag) : sealed_metadata_data}; + OSStatus status = crypto::AppleKeychainV2::GetInstance().ItemUpdate( + NSToCFPtrCast(query), NSToCFPtrCast(params)); if (status != errSecSuccess) { OSSTATUS_DLOG(ERROR, status) << "SecItemUpdate failed"; return false;
diff --git a/device/fido/mac/touch_id_context.mm b/device/fido/mac/touch_id_context.mm index 6556fa5..1396fa2 100644 --- a/device/fido/mac/touch_id_context.mm +++ b/device/fido/mac/touch_id_context.mm
@@ -24,6 +24,9 @@ #include "crypto/apple_keychain_v2.h" #include "device/fido/mac/authenticator_config.h" +using base::apple::CFToNSPtrCast; +using base::apple::NSToCFPtrCast; + namespace device::fido::mac { namespace { @@ -62,22 +65,19 @@ base::ScopedBlockingCall scoped_blocking_call(FROM_HERE, base::BlockingType::MAY_BLOCK); - base::apple::ScopedCFTypeRef<CFMutableDictionaryRef> params( - CFDictionaryCreateMutable(kCFAllocatorDefault, 0, - &kCFTypeDictionaryKeyCallBacks, - &kCFTypeDictionaryValueCallBacks)); - CFDictionarySetValue(params.get(), kSecAttrKeyType, - kSecAttrKeyTypeECSECPrimeRandom); - CFDictionarySetValue(params.get(), kSecAttrKeySizeInBits, - base::apple::NSToCFPtrCast(@256)); - CFDictionarySetValue(params.get(), kSecAttrTokenID, - kSecAttrTokenIDSecureEnclave); - CFDictionarySetValue(params.get(), kSecAttrIsPermanent, kCFBooleanFalse); + NSDictionary* params = @{ + CFToNSPtrCast(kSecAttrKeyType) : + CFToNSPtrCast(kSecAttrKeyTypeECSECPrimeRandom), + CFToNSPtrCast(kSecAttrKeySizeInBits) : @256, + CFToNSPtrCast(kSecAttrTokenID) : + CFToNSPtrCast(kSecAttrTokenIDSecureEnclave), + CFToNSPtrCast(kSecAttrIsPermanent) : @NO, + }; base::apple::ScopedCFTypeRef<CFErrorRef> cferr; base::apple::ScopedCFTypeRef<SecKeyRef> private_key( crypto::AppleKeychainV2::GetInstance().KeyCreateRandomKey( - params.get(), cferr.InitializeInto())); + NSToCFPtrCast(params), cferr.InitializeInto())); return !!private_key; }
diff --git a/device/fido/virtual_fido_device.cc b/device/fido/virtual_fido_device.cc index 9eadb3b9..71fa246 100644 --- a/device/fido/virtual_fido_device.cc +++ b/device/fido/virtual_fido_device.cc
@@ -6,8 +6,8 @@ #include <tuple> #include <utility> +#include <vector> -#include "base/containers/cxx20_erase_vector.h" #include "base/logging.h" #include "base/rand_util.h" #include "base/ranges/algorithm.h" @@ -501,7 +501,7 @@ reader.Materialize().value_or(cbor::Value::ArrayValue()); if (credential->large_blob_key) { - base::EraseIf( + std::erase_if( large_blob_array, [&credential](const cbor::Value& blob_cbor) { std::optional<LargeBlobData> blob = LargeBlobData::Parse(blob_cbor); return blob && blob->Decrypt(*credential->large_blob_key).has_value();
diff --git a/device/vr/android/web_xr_presentation_state.cc b/device/vr/android/web_xr_presentation_state.cc index d11fd07..94b4254 100644 --- a/device/vr/android/web_xr_presentation_state.cc +++ b/device/vr/android/web_xr_presentation_state.cc
@@ -6,8 +6,8 @@ #include <iomanip> #include <sstream> +#include <vector> -#include "base/containers/cxx20_erase.h" #include "base/logging.h" #include "base/trace_event/trace_event.h" #include "components/viz/common/frame_sinks/begin_frame_args.h" @@ -200,7 +200,7 @@ // Remove it from the list, and then recycle the frame. DVLOG(3) << DebugState() << __func__; DCHECK_EQ(state_machine_type_, StateMachineType::kVizComposited); - auto erased = base::EraseIf(rendering_frames_, + auto erased = std::erase_if(rendering_frames_, [frame](const WebXrFrame* rendering_frame) { return frame == rendering_frame; });
diff --git a/device/vr/orientation/orientation_device.cc b/device/vr/orientation/orientation_device.cc index aae624e..ed59c46 100644 --- a/device/vr/orientation/orientation_device.cc +++ b/device/vr/orientation/orientation_device.cc
@@ -5,8 +5,8 @@ #include <math.h> #include <numbers> +#include <vector> -#include "base/containers/cxx20_erase.h" #include "base/functional/bind.h" #include "base/memory/ptr_util.h" #include "base/no_destructor.h" @@ -194,7 +194,7 @@ void VROrientationDevice::EndMagicWindowSession(VROrientationSession* session) { DVLOG(2) << __func__; - base::EraseIf(magic_window_sessions_, + std::erase_if(magic_window_sessions_, [session](const std::unique_ptr<VROrientationSession>& item) { return item.get() == session; });
diff --git a/docs/website b/docs/website index ecc6dae..05de69c 160000 --- a/docs/website +++ b/docs/website
@@ -1 +1 @@ -Subproject commit ecc6daec0bc6bc14edaad139f726d7a0154d4572 +Subproject commit 05de69cbec5c294245bfb96d9be826065c05e6eb
diff --git a/extensions/browser/api/declarative_net_request/composite_matcher.cc b/extensions/browser/api/declarative_net_request/composite_matcher.cc index b26bb5f..3cbcd03 100644 --- a/extensions/browser/api/declarative_net_request/composite_matcher.cc +++ b/extensions/browser/api/declarative_net_request/composite_matcher.cc
@@ -8,9 +8,9 @@ #include <iterator> #include <set> #include <utility> +#include <vector> #include "base/containers/contains.h" -#include "base/containers/cxx20_erase.h" #include "base/metrics/histogram_macros.h" #include "base/ranges/algorithm.h" #include "base/time/time.h" @@ -99,7 +99,7 @@ } void CompositeMatcher::RemoveRulesetsWithIDs(const std::set<RulesetID>& ids) { - size_t erased_count = base::EraseIf( + size_t erased_count = std::erase_if( matchers_, [&ids](const std::unique_ptr<RulesetMatcher>& matcher) { return base::Contains(ids, matcher->id()); });
diff --git a/extensions/browser/api/declarative_net_request/declarative_net_request_api.cc b/extensions/browser/api/declarative_net_request/declarative_net_request_api.cc index 2353639..114814d8 100644 --- a/extensions/browser/api/declarative_net_request/declarative_net_request_api.cc +++ b/extensions/browser/api/declarative_net_request/declarative_net_request_api.cc
@@ -9,8 +9,8 @@ #include <set> #include <utility> #include <vector> + #include "base/containers/contains.h" -#include "base/containers/cxx20_erase.h" #include "base/functional/bind.h" #include "base/time/time.h" #include "content/public/browser/browser_task_traits.h" @@ -65,7 +65,7 @@ // Filter the rules by the rule IDs, if provided. if (rule_filter.rule_ids) { const base::flat_set<int>& rule_ids = *rule_filter.rule_ids; - base::EraseIf(rules, [rule_ids](const auto& rule) { + std::erase_if(rules, [rule_ids](const auto& rule) { return !rule_ids.contains(rule.id); }); } @@ -356,7 +356,7 @@ // Exclude any reserved ruleset IDs since they would correspond to // non-static rulesets. - base::EraseIf(public_ids, [](const std::string& id) { + std::erase_if(public_ids, [](const std::string& id) { DCHECK(!id.empty()); return id[0] == declarative_net_request::kReservedRulesetIDPrefix; });
diff --git a/extensions/browser/api/declarative_net_request/extension_url_pattern_index_matcher.cc b/extensions/browser/api/declarative_net_request/extension_url_pattern_index_matcher.cc index e472f6d..0d8fb50d 100644 --- a/extensions/browser/api/declarative_net_request/extension_url_pattern_index_matcher.cc +++ b/extensions/browser/api/declarative_net_request/extension_url_pattern_index_matcher.cc
@@ -9,9 +9,9 @@ #include <list> #include <string> #include <utility> +#include <vector> #include "base/check_op.h" -#include "base/containers/cxx20_erase.h" #include "base/notreached.h" #include "extensions/browser/api/declarative_net_request/request_action.h" #include "extensions/browser/api/declarative_net_request/request_params.h" @@ -120,7 +120,7 @@ params, before_request_matchers_, flat::IndexType_modify_headers); if (min_priority) { - base::EraseIf(rules, [&min_priority](const flat_rule::UrlRule* rule) { + std::erase_if(rules, [&min_priority](const flat_rule::UrlRule* rule) { return rule->priority() <= *min_priority; }); }
diff --git a/extensions/browser/api/declarative_net_request/file_sequence_helper.cc b/extensions/browser/api/declarative_net_request/file_sequence_helper.cc index 6cf3aff..1bcb7c01 100644 --- a/extensions/browser/api/declarative_net_request/file_sequence_helper.cc +++ b/extensions/browser/api/declarative_net_request/file_sequence_helper.cc
@@ -7,11 +7,11 @@ #include <cstdint> #include <set> #include <utility> +#include <vector> #include "base/barrier_closure.h" #include "base/check_op.h" #include "base/containers/contains.h" -#include "base/containers/cxx20_erase.h" #include "base/files/file_util.h" #include "base/files/important_file_writer.h" #include "base/functional/bind.h" @@ -201,7 +201,7 @@ // Remove old rules std::set<int> ids_to_remove(rule_ids_to_remove.begin(), rule_ids_to_remove.end()); - base::EraseIf(*new_rules, [&ids_to_remove](const dnr_api::Rule& rule) { + std::erase_if(*new_rules, [&ids_to_remove](const dnr_api::Rule& rule) { return base::Contains(ids_to_remove, rule.id); });
diff --git a/extensions/browser/api/declarative_net_request/rules_monitor_service.cc b/extensions/browser/api/declarative_net_request/rules_monitor_service.cc index b12c6440..c6b35b7 100644 --- a/extensions/browser/api/declarative_net_request/rules_monitor_service.cc +++ b/extensions/browser/api/declarative_net_request/rules_monitor_service.cc
@@ -5,10 +5,10 @@ #include "extensions/browser/api/declarative_net_request/rules_monitor_service.h" #include <utility> +#include <vector> #include "base/check_op.h" #include "base/containers/contains.h" -#include "base/containers/cxx20_erase.h" #include "base/containers/queue.h" #include "base/files/file_util.h" #include "base/functional/bind.h" @@ -673,7 +673,7 @@ std::set<int> ids_to_remove(rule_ids_to_remove.begin(), rule_ids_to_remove.end()); - base::EraseIf(new_rules, [&ids_to_remove](const dnr_api::Rule& rule) { + std::erase_if(new_rules, [&ids_to_remove](const dnr_api::Rule& rule) { return base::Contains(ids_to_remove, rule.id); });
diff --git a/extensions/browser/api/i18n/i18n_api.cc b/extensions/browser/api/i18n/i18n_api.cc index 0bfdeb6..7797d62 100644 --- a/extensions/browser/api/i18n/i18n_api.cc +++ b/extensions/browser/api/i18n/i18n_api.cc
@@ -8,7 +8,6 @@ #include <string> #include <vector> -#include "base/containers/cxx20_erase.h" #include "base/strings/string_split.h" #include "components/language/core/browser/pref_names.h" #include "components/prefs/pref_service.h" @@ -47,7 +46,7 @@ std::vector<std::string> languages = base::SplitString( accept_languages, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL); - base::Erase(languages, ""); + std::erase(languages, ""); if (languages.empty()) return RespondNow(Error(kEmptyAcceptLanguagesError));
diff --git a/extensions/browser/api/runtime/runtime_api.cc b/extensions/browser/api/runtime/runtime_api.cc index bac494f6..6d3d98c7 100644 --- a/extensions/browser/api/runtime/runtime_api.cc +++ b/extensions/browser/api/runtime/runtime_api.cc
@@ -6,6 +6,7 @@ #include <memory> #include <utility> +#include <vector> #include "base/check.h" #include "base/functional/bind.h" @@ -829,7 +830,7 @@ std::make_move_iterator(frame_contexts.end())); // Erase any contexts that don't match the specified filter. - base::EraseIf(result, + std::erase_if(result, [&filter](const api::runtime::ExtensionContext& context) { return !ExtensionContextMatchesFilter(context, filter); });
diff --git a/extensions/browser/api/socket/udp_socket.cc b/extensions/browser/api/socket/udp_socket.cc index 01f30e656..953356f 100644 --- a/extensions/browser/api/socket/udp_socket.cc +++ b/extensions/browser/api/socket/udp_socket.cc
@@ -5,9 +5,9 @@ #include "extensions/browser/api/socket/udp_socket.h" #include <utility> +#include <vector> #include "base/containers/contains.h" -#include "base/containers/cxx20_erase.h" #include "base/functional/bind.h" #include "base/lazy_instance.h" #include "base/ranges/algorithm.h" @@ -308,7 +308,7 @@ const std::string& normalized_address, int result) { if (result == net::OK) { - base::Erase(multicast_groups_, normalized_address); + std::erase(multicast_groups_, normalized_address); } std::move(callback).Run(result);
diff --git a/extensions/browser/api/web_request/extension_web_request_event_router.cc b/extensions/browser/api/web_request/extension_web_request_event_router.cc index 8092008..0963fac 100644 --- a/extensions/browser/api/web_request/extension_web_request_event_router.cc +++ b/extensions/browser/api/web_request/extension_web_request_event_router.cc
@@ -6,6 +6,7 @@ #include <algorithm> #include <string_view> +#include <vector> #include "base/containers/fixed_flat_map.h" #include "base/feature_list.h" @@ -1660,7 +1661,7 @@ // the *same order* in the extension. In practice, this should pretty much // always be the case, because we require listeners to be set up // synchronously. - size_t erased = base::EraseIf( + size_t erased = std::erase_if( listeners, [browser_context, extension_id, sub_event_name]( const std::unique_ptr<EventListener>& listener) { return listener->id.browser_context == browser_context &&
diff --git a/extensions/browser/extension_prefs.cc b/extensions/browser/extension_prefs.cc index f310a5e..ee971fa 100644 --- a/extensions/browser/extension_prefs.cc +++ b/extensions/browser/extension_prefs.cc
@@ -10,10 +10,10 @@ #include <iterator> #include <string_view> #include <utility> +#include <vector> #include "base/check_op.h" #include "base/containers/contains.h" -#include "base/containers/cxx20_erase.h" #include "base/json/values_util.h" #include "base/observer_list.h" #include "base/ranges/algorithm.h" @@ -1955,7 +1955,7 @@ return !Manifest::ShouldAlwaysLoadExtension(info.extension_location, is_theme); }; - base::EraseIf(extensions_info, predicate); + std::erase_if(extensions_info, predicate); } InitExtensionControlledPrefs(extensions_info);
diff --git a/extensions/browser/script_executor.cc b/extensions/browser/script_executor.cc index 1fe948a..c50423d 100644 --- a/extensions/browser/script_executor.cc +++ b/extensions/browser/script_executor.cc
@@ -7,10 +7,10 @@ #include <map> #include <set> #include <string> +#include <vector> #include "base/check_op.h" #include "base/containers/contains.h" -#include "base/containers/cxx20_erase.h" #include "base/dcheck_is_on.h" #include "base/functional/bind.h" #include "base/hash/hash.h" @@ -143,7 +143,7 @@ void RenderFrameDeleted( content::RenderFrameHost* render_frame_host) override { - int erased_count = base::Erase(pending_render_frames_, render_frame_host); + int erased_count = std::erase(pending_render_frames_, render_frame_host); DCHECK_LE(erased_count, 1); if (erased_count == 0) return; @@ -268,7 +268,7 @@ return; DCHECK(!pending_render_frames_.empty()); - size_t erased = base::Erase(pending_render_frames_, render_frame_host); + size_t erased = std::erase(pending_render_frames_, render_frame_host); DCHECK_EQ(1u, erased); // TODO(devlin): Do we need to trust the renderer for the URL here? Is there
diff --git a/extensions/browser/service_worker/service_worker_host.cc b/extensions/browser/service_worker/service_worker_host.cc index a4ce886e..e479982 100644 --- a/extensions/browser/service_worker/service_worker_host.cc +++ b/extensions/browser/service_worker/service_worker_host.cc
@@ -4,6 +4,8 @@ #include "extensions/browser/service_worker/service_worker_host.h" +#include <vector> + #include "base/containers/unique_ptr_adapters.h" #include "base/trace_event/typed_macros.h" #include "content/public/browser/browser_context.h" @@ -356,8 +358,8 @@ auto* service_worker_host_list = ServiceWorkerHostList::Get( render_process_host_, /*create_if_not_exists=*/false); CHECK(service_worker_host_list); - // base::EraseIf will lead to a call to the destructor for this object. - base::EraseIf(service_worker_host_list->list, base::MatchesUniquePtr(this)); + // std::erase_if will lead to a call to the destructor for this object. + std::erase_if(service_worker_host_list->list, base::MatchesUniquePtr(this)); } void ServiceWorkerHost::RenderProcessExited(
diff --git a/extensions/browser/user_script_loader.cc b/extensions/browser/user_script_loader.cc index dea428a..305e03a 100644 --- a/extensions/browser/user_script_loader.cc +++ b/extensions/browser/user_script_loader.cc
@@ -10,8 +10,8 @@ #include <string> #include <string_view> #include <utility> +#include <vector> -#include "base/containers/cxx20_erase.h" #include "base/functional/bind.h" #include "base/memory/writable_shared_memory_region.h" #include "base/observer_list.h" @@ -305,7 +305,7 @@ loaded_scripts_.reset(); // Filter out any scripts that are queued for removal. - base::EraseIf(scripts_to_load, + std::erase_if(scripts_to_load, [this](const std::unique_ptr<UserScript>& script) { return removed_script_ids_.count(script->id()) > 0u; }); @@ -313,7 +313,7 @@ // Since all scripts managed by an instance of this class should have unique // IDs, remove any already loaded scripts from `scripts_to_load` that will be // updated from `added_scripts_map_`. - base::EraseIf(scripts_to_load, + std::erase_if(scripts_to_load, [this](const std::unique_ptr<UserScript>& script) { return added_scripts_map_.count(script->id()) > 0u; });
diff --git a/extensions/renderer/api/messaging/one_time_message_handler.cc b/extensions/renderer/api/messaging/one_time_message_handler.cc index be6a480..ef300f94 100644 --- a/extensions/renderer/api/messaging/one_time_message_handler.cc +++ b/extensions/renderer/api/messaging/one_time_message_handler.cc
@@ -5,9 +5,9 @@ #include "extensions/renderer/api/messaging/one_time_message_handler.h" #include <map> +#include <vector> #include "base/containers/contains.h" -#include "base/containers/cxx20_erase.h" #include "base/functional/bind.h" #include "base/functional/callback.h" #include "base/ranges/algorithm.h" @@ -562,7 +562,7 @@ // Since there is no way to call the callback anymore, we can remove it from // the pending callbacks. - base::EraseIf( + std::erase_if( data->pending_callbacks, [raw_callback](const std::unique_ptr<OneTimeMessageCallback>& callback) { return reinterpret_cast<CallbackID>(callback.get()) == raw_callback;
diff --git a/extensions/renderer/script_injection_manager.cc b/extensions/renderer/script_injection_manager.cc index bcd888e..99a08dae 100644 --- a/extensions/renderer/script_injection_manager.cc +++ b/extensions/renderer/script_injection_manager.cc
@@ -7,8 +7,9 @@ #include <memory> #include <optional> #include <utility> +#include <vector> + #include "base/auto_reset.h" -#include "base/containers/cxx20_erase.h" #include "base/feature_list.h" #include "base/functional/bind.h" #include "base/memory/raw_ptr.h" @@ -277,7 +278,7 @@ } void ScriptInjectionManager::OnInjectionFinished(ScriptInjection* injection) { - base::EraseIf(running_injections_, + std::erase_if(running_injections_, [&injection](const std::unique_ptr<ScriptInjection>& mode) { return injection == mode.get(); }); @@ -285,7 +286,7 @@ void ScriptInjectionManager::OnUserScriptsUpdated( const mojom::HostID& changed_host) { - base::EraseIf( + std::erase_if( pending_injections_, [&changed_host](const std::unique_ptr<ScriptInjection>& injection) { return changed_host == injection->host_id(); @@ -306,7 +307,7 @@ // note it. active_injection_frames_.erase(frame); - base::EraseIf(pending_injections_, + std::erase_if(pending_injections_, [&frame](const std::unique_ptr<ScriptInjection>& injection) { return injection->render_frame() == frame; });
diff --git a/google_apis/calendar/calendar_api_requests.cc b/google_apis/calendar/calendar_api_requests.cc index b4a6f18..8f4eb47 100644 --- a/google_apis/calendar/calendar_api_requests.cc +++ b/google_apis/calendar/calendar_api_requests.cc
@@ -11,6 +11,7 @@ #include <utility> #include "base/memory/weak_ptr.h" +#include "base/strings/strcat.h" #include "base/values.h" #include "google_apis/calendar/calendar_api_response_types.h" #include "google_apis/common/api_error_codes.h" @@ -56,18 +57,21 @@ // response. constexpr int kMaxResults = 2500; -// Requested fields to be returned in the Event list result. -constexpr char kCalendarEventListFields[] = - "timeZone,etag,kind,items(id,kind,summary,colorId," - "status,start(date),end(date),start(dateTime),end(dateTime),htmlLink," - "attendees(responseStatus,self),attendeesOmitted," - "conferenceData(conferenceId,entryPoints(entryPointType,uri))," - "creator(self))"; - // Requested fields to be returned in the CalendarList result. constexpr char kCalendarListFields[] = "etag,kind,items(id,colorId,selected,primary)"; +// Requested fields to be returned in the Event list result. +std::string GetCalendarEventListFields(bool include_attachments) { + return base::StrCat( + {"timeZone,etag,kind,items(id,kind,summary,colorId," + "status,start(date),end(date),start(dateTime),end(dateTime),htmlLink," + "attendees(responseStatus,self),attendeesOmitted," + "conferenceData(conferenceId,entryPoints(entryPointType,uri))," + "creator(self)", + include_attachments ? ",attachments(title,fileUrl,iconLink)" : "", ")"}); +} + } // namespace CalendarApiGetRequest::CalendarApiGetRequest(RequestSender* sender, @@ -172,7 +176,9 @@ const base::Time& end_time, const std::string& calendar_id, const std::string& calendar_color_id) - : CalendarApiGetRequest(sender, kCalendarEventListFields), + : CalendarApiGetRequest( + sender, + GetCalendarEventListFields(/*include_attachments=*/false)), callback_(std::move(callback)), url_generator_(url_generator), start_time_(start_time), @@ -187,8 +193,10 @@ const CalendarApiUrlGenerator& url_generator, CalendarEventListCallback callback, const base::Time& start_time, - const base::Time& end_time) - : CalendarApiGetRequest(sender, kCalendarEventListFields), + const base::Time& end_time, + bool include_attachments) + : CalendarApiGetRequest(sender, + GetCalendarEventListFields(include_attachments)), callback_(std::move(callback)), url_generator_(url_generator), start_time_(start_time),
diff --git a/google_apis/calendar/calendar_api_requests.h b/google_apis/calendar/calendar_api_requests.h index 48c2f5fc..fa272b4 100644 --- a/google_apis/calendar/calendar_api_requests.h +++ b/google_apis/calendar/calendar_api_requests.h
@@ -118,7 +118,8 @@ const CalendarApiUrlGenerator& url_generator, CalendarEventListCallback callback, const base::Time& start_time, - const base::Time& end_time); + const base::Time& end_time, + bool include_attachments = false); CalendarApiEventsRequest(const CalendarApiEventsRequest&) = delete; CalendarApiEventsRequest& operator=(const CalendarApiEventsRequest&) = delete; ~CalendarApiEventsRequest() override;
diff --git a/google_apis/calendar/calendar_api_requests_unittest.cc b/google_apis/calendar/calendar_api_requests_unittest.cc index 8f3e6fb..c8038ce 100644 --- a/google_apis/calendar/calendar_api_requests_unittest.cc +++ b/google_apis/calendar/calendar_api_requests_unittest.cc
@@ -229,5 +229,48 @@ EXPECT_EQ(events->items()[2]->color_id(), ""); } +// Tests that CalendarApiEventsRequest can generate the correct url when +// attachments are requested. +TEST_F(CalendarApiRequestsTest, GetEventListRequestWithAttachments) { + ApiErrorCode error = OTHER_ERROR; + std::unique_ptr<EventList> events; + base::Time start; + base::Time end; + + EXPECT_TRUE(base::Time::FromString("13 Jun 2021 10:00 GMT", &start)); + EXPECT_TRUE(base::Time::FromString("16 Jun 2021 10:00 GMT", &end)); + + { + base::RunLoop run_loop; + auto request = std::make_unique<CalendarApiEventsRequest>( + request_sender_.get(), *url_generator_, + test_util::CreateQuitCallback( + &run_loop, test_util::CreateCopyResultCallback(&error, &events)), + start, end, /*include_attachments=*/true); + + request_sender_->StartRequestWithAuthRetry(std::move(request)); + run_loop.Run(); + } + + EXPECT_EQ(HTTP_SUCCESS, error); + EXPECT_EQ(net::test_server::METHOD_GET, http_request_.method); + EXPECT_EQ( + "/calendar/v3/calendars/primary/events" + "?timeMin=2021-06-13T10%3A00%3A00.000Z" + "&timeMax=2021-06-16T10%3A00%3A00.000Z" + "&singleEvents=true" + "&maxAttendees=1" + "&maxResults=2500" + "&fields=timeZone%2Cetag%2Ckind%2Citems(id%2Ckind" + "%2Csummary%2CcolorId%2Cstatus" + "%2Cstart(date)%2Cend(date)" + "%2Cstart(dateTime)%2Cend(dateTime)" + "%2ChtmlLink%2Cattendees(responseStatus%2Cself)%2CattendeesOmitted" + "%2CconferenceData(conferenceId%2CentryPoints(entryPointType%2Curi))" + "%2Ccreator(self)" + "%2Cattachments(title%2CfileUrl%2CiconLink))", + http_request_.relative_url); +} + } // namespace calendar } // namespace google_apis
diff --git a/google_apis/gaia/fake_gaia.cc b/google_apis/gaia/fake_gaia.cc index 226cda0..4cd0210 100644 --- a/google_apis/gaia/fake_gaia.cc +++ b/google_apis/gaia/fake_gaia.cc
@@ -11,7 +11,6 @@ #include "base/base64.h" #include "base/base_paths.h" #include "base/containers/contains.h" -#include "base/containers/cxx20_erase.h" #include "base/files/file_path.h" #include "base/files/file_util.h" #include "base/functional/bind.h" @@ -922,7 +921,7 @@ std::string gaia_id; GetQueryParameter(request.GetURL().query(), "gaia_id", &gaia_id); - if (!base::Erase(configuration_.signed_out_gaia_ids, gaia_id)) { + if (!std::erase(configuration_.signed_out_gaia_ids, gaia_id)) { http_response->set_code(net::HTTP_BAD_REQUEST); return; }
diff --git a/gpu/command_buffer/client/query_tracker.cc b/gpu/command_buffer/client/query_tracker.cc index 2792af0..807331d 100644 --- a/gpu/command_buffer/client/query_tracker.cc +++ b/gpu/command_buffer/client/query_tracker.cc
@@ -11,10 +11,10 @@ #include <limits.h> #include <stddef.h> #include <stdint.h> +#include <vector> #include "base/atomicops.h" #include "base/containers/circular_deque.h" -#include "base/containers/cxx20_erase.h" #include "base/numerics/safe_conversions.h" #include "gpu/command_buffer/client/gles2_cmd_helper.h" #include "gpu/command_buffer/client/gles2_implementation.h" @@ -32,7 +32,7 @@ QuerySyncManager::Bucket::~Bucket() = default; void QuerySyncManager::Bucket::FreePendingSyncs() { - base::EraseIf(pending_syncs, [this](const PendingSync& pending) { + std::erase_if(pending_syncs, [this](const PendingSync& pending) { QuerySync* sync = this->syncs + pending.index; if (base::subtle::Acquire_Load(&sync->process_count) == pending.submit_count) {
diff --git a/gpu/command_buffer/service/context_group.cc b/gpu/command_buffer/service/context_group.cc index 4f6ef80..37e2a54 100644 --- a/gpu/command_buffer/service/context_group.cc +++ b/gpu/command_buffer/service/context_group.cc
@@ -10,9 +10,9 @@ #include <algorithm> #include <memory> #include <string> +#include <vector> #include "base/command_line.h" -#include "base/containers/cxx20_erase.h" #include "base/memory/raw_ptr.h" #include "build/build_config.h" #include "gpu/command_buffer/service/buffer_manager.h" @@ -569,7 +569,7 @@ } // namespace anonymous bool ContextGroup::HaveContexts() { - base::EraseIf(decoders_, IsNull); + std::erase_if(decoders_, IsNull); return !decoders_.empty(); } @@ -579,7 +579,7 @@ } void ContextGroup::Destroy(DecoderContext* decoder, bool have_context) { - base::EraseIf(decoders_, WeakPtrEquals<DecoderContext>(decoder)); + std::erase_if(decoders_, WeakPtrEquals<DecoderContext>(decoder)); // If we still have contexts do nothing. if (HaveContexts()) {
diff --git a/gpu/command_buffer/tests/gl_angle_shader_pixel_local_storage_unittest.cc b/gpu/command_buffer/tests/gl_angle_shader_pixel_local_storage_unittest.cc index d6bdc2c9f..f785d82 100644 --- a/gpu/command_buffer/tests/gl_angle_shader_pixel_local_storage_unittest.cc +++ b/gpu/command_buffer/tests/gl_angle_shader_pixel_local_storage_unittest.cc
@@ -13,6 +13,7 @@ #include "gpu/command_buffer/tests/gl_manager.h" #include "gpu/command_buffer/tests/gl_test_utils.h" #include "testing/gtest/include/gtest/gtest.h" +#include "ui/gl/gl_implementation.h" namespace gpu { class ANGLEShaderPixelLocalStorageTest : public testing::Test { @@ -171,6 +172,13 @@ return; } +// Test skipped on Intel-based Macs when running Metal. crbug.com/326278125 +#if BUILDFLAG(IS_MAC) && defined(ARCH_CPU_X86_64) + if (gl::GetANGLEImplementation() == gl::ANGLEImplementation::kMetal) { + return; + } +#endif + GLuint texs[4]; glGenTextures(4, texs); for (GLuint tex : texs) { @@ -213,6 +221,13 @@ return; } +// Test skipped on Intel-based Macs when running Metal. crbug.com/326278125 +#if BUILDFLAG(IS_MAC) && defined(ARCH_CPU_X86_64) + if (gl::GetANGLEImplementation() == gl::ANGLEImplementation::kMetal) { + return; + } +#endif + GLuint tex; glGenTextures(1, &tex); glBindTexture(GL_TEXTURE_2D, tex);
diff --git a/gpu/vulkan/vulkan_image_linux.cc b/gpu/vulkan/vulkan_image_linux.cc index 1d8005d..dec506c 100644 --- a/gpu/vulkan/vulkan_image_linux.cc +++ b/gpu/vulkan/vulkan_image_linux.cc
@@ -5,8 +5,8 @@ #include "gpu/vulkan/vulkan_image.h" #include <tuple> +#include <vector> -#include "base/containers/cxx20_erase.h" #include "base/logging.h" #include "gpu/vulkan/vulkan_device_queue.h" #include "gpu/vulkan/vulkan_function_pointers.h" @@ -152,7 +152,7 @@ // Call GetImageFormatProperties with every modifier and filter the list // down to those that we know work. - base::EraseIf(props_vector, [&](const VkDrmFormatModifierPropertiesEXT& p) { + std::erase_if(props_vector, [&](const VkDrmFormatModifierPropertiesEXT& p) { VkPhysicalDeviceImageDrmFormatModifierInfoEXT mod_info = { .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_DRM_FORMAT_MODIFIER_INFO_EXT, @@ -179,7 +179,7 @@ return false; // Find compatible modifiers. - base::EraseIf(modifiers, [&props_vector](uint64_t modifier) { + std::erase_if(modifiers, [&props_vector](uint64_t modifier) { for (const auto& modifier_props : props_vector) { if (modifier == modifier_props.drmFormatModifier) return false;
diff --git a/infra/config/.lucicfgfmtrc b/infra/config/.lucicfgfmtrc index 0b664a9..7e846306 100644 --- a/infra/config/.lucicfgfmtrc +++ b/infra/config/.lucicfgfmtrc
@@ -38,7 +38,7 @@ arg: "builder_group" arg: "builder_spec" arg: "mirrors" - arg: "try_settings" + arg: "builder_config_settings" arg: "gn_args" arg: "targets" arg: "pool"
diff --git a/infra/config/generated/builders/ci/linux-chromeos-dbg-oslogin/gn-args.json b/infra/config/generated/builders/ci/linux-chromeos-dbg-oslogin/gn-args.json new file mode 100644 index 0000000..143093c --- /dev/null +++ b/infra/config/generated/builders/ci/linux-chromeos-dbg-oslogin/gn-args.json
@@ -0,0 +1,12 @@ +{ + "gn_args": { + "ffmpeg_branding": "ChromeOS", + "is_component_build": true, + "is_debug": true, + "proprietary_codecs": true, + "symbol_level": 1, + "target_os": "chromeos", + "use_cups": true, + "use_remoteexec": true + } +} \ No newline at end of file
diff --git a/infra/config/generated/builders/ci/linux-chromeos-dbg-oslogin/properties.json b/infra/config/generated/builders/ci/linux-chromeos-dbg-oslogin/properties.json new file mode 100644 index 0000000..0c641702 --- /dev/null +++ b/infra/config/generated/builders/ci/linux-chromeos-dbg-oslogin/properties.json
@@ -0,0 +1,63 @@ +{ + "$build/chromium_tests_builder_config": { + "builder_config": { + "additional_exclusions": [ + "infra/config/generated/builders/ci/linux-chromeos-dbg-oslogin/gn-args.json" + ], + "builder_db": { + "entries": [ + { + "builder_id": { + "bucket": "ci", + "builder": "linux-chromeos-dbg-oslogin", + "project": "chromium" + }, + "builder_spec": { + "build_gs_bucket": "chromium-chromiumos-archive", + "builder_group": "chromium.fyi", + "execution_mode": "COMPILE_AND_TEST", + "legacy_chromium_config": { + "apply_configs": [ + "mb" + ], + "build_config": "Debug", + "config": "chromium", + "target_arch": "intel", + "target_bits": 64, + "target_platform": "chromeos" + }, + "legacy_gclient_config": { + "apply_configs": [ + "chromeos" + ], + "config": "chromium" + } + } + } + ] + }, + "builder_ids": [ + { + "bucket": "ci", + "builder": "linux-chromeos-dbg-oslogin", + "project": "chromium" + } + ] + } + }, + "$build/reclient": { + "instance": "rbe-chromium-trusted", + "jobs": 250, + "metrics_project": "chromium-reclient-metrics", + "scandeps_server": true + }, + "$recipe_engine/resultdb/test_presentation": { + "column_keys": [], + "grouping_keys": [ + "status", + "v.test_suite" + ] + }, + "builder_group": "chromium.fyi", + "recipe": "chromium" +} \ No newline at end of file
diff --git a/infra/config/generated/builders/ci/linux-chromeos-dbg-oslogin/shadow-properties.json b/infra/config/generated/builders/ci/linux-chromeos-dbg-oslogin/shadow-properties.json new file mode 100644 index 0000000..999510c --- /dev/null +++ b/infra/config/generated/builders/ci/linux-chromeos-dbg-oslogin/shadow-properties.json
@@ -0,0 +1,8 @@ +{ + "$build/reclient": { + "instance": "rbe-chromium-untrusted", + "jobs": 250, + "metrics_project": "chromium-reclient-metrics", + "scandeps_server": true + } +} \ No newline at end of file
diff --git a/infra/config/generated/builders/gn_args_locations.json b/infra/config/generated/builders/gn_args_locations.json index a5607269..bb0b281 100644 --- a/infra/config/generated/builders/gn_args_locations.json +++ b/infra/config/generated/builders/gn_args_locations.json
@@ -294,6 +294,7 @@ "linux-blink-heap-verification": "ci/linux-blink-heap-verification/gn-args.json", "linux-blink-wpt-reset-rel": "ci/linux-blink-wpt-reset-rel/gn-args.json", "linux-chromeos-annotator-rel": "ci/linux-chromeos-annotator-rel/gn-args.json", + "linux-chromeos-dbg-oslogin": "ci/linux-chromeos-dbg-oslogin/gn-args.json", "linux-fieldtrial-rel": "ci/linux-fieldtrial-rel/gn-args.json", "linux-headless-shell-rel": "ci/linux-headless-shell-rel/gn-args.json", "linux-lacros-builder-fyi-rel": "ci/linux-lacros-builder-fyi-rel/gn-args.json",
diff --git a/infra/config/generated/health-specs/health-specs.json b/infra/config/generated/health-specs/health-specs.json index 3068015..5356bf1 100644 --- a/infra/config/generated/health-specs/health-specs.json +++ b/infra/config/generated/health-specs/health-specs.json
@@ -9501,6 +9501,27 @@ } ] }, + "linux-chromeos-dbg-oslogin": { + "contact_team_email": "chrome-browser-infra@google.com", + "problem_specs": [ + { + "name": "Unhealthy", + "period_days": 7, + "score": 5, + "thresholds": { + "_default": "_default" + } + }, + { + "name": "Low Value", + "period_days": 90, + "score": 1, + "thresholds": { + "_default": "_default" + } + } + ] + }, "linux-chromeos-rel": { "contact_team_email": "chromeos-sw-engprod@google.com", "problem_specs": [
diff --git a/infra/config/generated/luci/cr-buildbucket.cfg b/infra/config/generated/luci/cr-buildbucket.cfg index c0ce45b..3c6b02a 100644 --- a/infra/config/generated/luci/cr-buildbucket.cfg +++ b/infra/config/generated/luci/cr-buildbucket.cfg
@@ -44284,6 +44284,98 @@ contact_team_email: "chromeos-sw-engprod@google.com" } builders { + name: "linux-chromeos-dbg-oslogin" + swarming_host: "chromium-swarm.appspot.com" + dimensions: "builder:linux-chromeos-dbg-oslogin" + dimensions: "cores:8" + dimensions: "cpu:x86-64" + dimensions: "pool:luci.chromium.ci" + exe { + cipd_package: "infra/chromium/bootstrapper/${platform}" + cipd_version: "latest" + cmd: "bootstrapper" + } + properties: + '{' + ' "$bootstrap/exe": {' + ' "exe": {' + ' "cipd_package": "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build",' + ' "cipd_version": "refs/heads/main",' + ' "cmd": [' + ' "luciexe"' + ' ]' + ' }' + ' },' + ' "$bootstrap/properties": {' + ' "properties_file": "infra/config/generated/builders/ci/linux-chromeos-dbg-oslogin/properties.json",' + ' "shadow_properties_file": "infra/config/generated/builders/ci/linux-chromeos-dbg-oslogin/shadow-properties.json",' + ' "top_level_project": {' + ' "ref": "refs/heads/main",' + ' "repo": {' + ' "host": "chromium.googlesource.com",' + ' "project": "chromium/src"' + ' }' + ' }' + ' },' + ' "builder_group": "chromium.fyi",' + ' "led_builder_is_bootstrapped": true,' + ' "recipe": "chromium"' + '}' + priority: 35 + execution_timeout_secs: 36000 + build_numbers: YES + service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "chromium_swarming.expose_merge_script_failures" + value: 100 + } + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "chrome-luci-data" + dataset: "chromium" + table: "ci_test_results" + test_results {} + } + bq_exports { + project: "chrome-luci-data" + dataset: "chromium" + table: "gpu_ci_test_results" + test_results { + predicate { + test_id_regexp: "ninja://chrome/test:telemetry_gpu_integration_test[^/]*/.+" + } + } + } + bq_exports { + project: "chrome-luci-data" + dataset: "chromium" + table: "blink_web_tests_ci_test_results" + test_results { + predicate { + test_id_regexp: "(ninja://[^/]*blink_web_tests/.+)|(ninja://[^/]*_wpt_tests/.+)" + } + } + } + history_options { + use_invocation_timestamp: true + } + } + description_html: "This builder is used to debug spefically oslogin issues relatedto linux-chromeos-dbg-oslogin" + shadow_builder_adjustments { + service_account: "chromium-try-builder@chops-service-accounts.iam.gserviceaccount.com" + pool: "luci.chromium.try" + dimensions: "builder:" + dimensions: "builderless:1" + dimensions: "pool:luci.chromium.try" + } + contact_team_email: "chrome-browser-infra@google.com" + } + builders { name: "linux-chromeos-rel" swarming_host: "chromium-swarm.appspot.com" dimensions: "builder:linux-chromeos-rel"
diff --git a/infra/config/generated/luci/luci-milo.cfg b/infra/config/generated/luci/luci-milo.cfg index 1229ba4..a443894 100644 --- a/infra/config/generated/luci/luci-milo.cfg +++ b/infra/config/generated/luci/luci-milo.cfg
@@ -9513,6 +9513,11 @@ short_name: "lk" } builders { + name: "buildbucket/luci.chromium.ci/linux-chromeos-dbg-oslogin" + category: "linux" + short_name: "lnx" + } + builders { name: "buildbucket/luci.chromium.ci/Linux Builder (reclient compare)" category: "linux" short_name: "re"
diff --git a/infra/config/generated/luci/luci-scheduler.cfg b/infra/config/generated/luci/luci-scheduler.cfg index 60171a3..670c8880 100644 --- a/infra/config/generated/luci/luci-scheduler.cfg +++ b/infra/config/generated/luci/luci-scheduler.cfg
@@ -4843,6 +4843,15 @@ } } job { + id: "linux-chromeos-dbg-oslogin" + realm: "ci" + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "ci" + builder: "linux-chromeos-dbg-oslogin" + } +} +job { id: "linux-chromeos-rel" realm: "ci" buildbucket { @@ -6593,6 +6602,7 @@ triggers: "linux-chromeos-annotator-rel" triggers: "linux-chromeos-archive-rel" triggers: "linux-chromeos-dbg" + triggers: "linux-chromeos-dbg-oslogin" triggers: "linux-chromeos-rel" triggers: "linux-codeql-generator" triggers: "linux-extended-tracing-rel"
diff --git a/infra/config/generated/testing/mixins.pyl b/infra/config/generated/testing/mixins.pyl index 1f5c782..7f8052e4 100644 --- a/infra/config/generated/testing/mixins.pyl +++ b/infra/config/generated/testing/mixins.pyl
@@ -399,6 +399,13 @@ 'service_account': 'chromium-tester@chops-service-accounts.iam.gserviceaccount.com', }, }, + 'chromium-tests-oslogin': { + 'swarming': { + 'dimensions': { + 'pool': 'chromium.tests.oslogin', + }, + }, + }, 'ci_only': { 'ci_only': True, },
diff --git a/infra/config/generated/testing/variants.pyl b/infra/config/generated/testing/variants.pyl index 7ac9198..99bda77 100644 --- a/infra/config/generated/testing/variants.pyl +++ b/infra/config/generated/testing/variants.pyl
@@ -307,16 +307,16 @@ }, 'LACROS_VERSION_SKEW_CANARY': { 'identifier': 'Lacros version skew testing ash canary', - 'description': 'Run with ash-chrome version 124.0.6325.0', + 'description': 'Run with ash-chrome version 124.0.6326.0', 'args': [ - '--ash-chrome-path-override=../../lacros_version_skew_tests_v124.0.6325.0/test_ash_chrome', + '--ash-chrome-path-override=../../lacros_version_skew_tests_v124.0.6326.0/test_ash_chrome', ], 'swarming': { 'cipd_packages': [ { 'cipd_package': 'chromium/testing/linux-ash-chromium/x86_64/ash.zip', - 'location': 'lacros_version_skew_tests_v124.0.6325.0', - 'revision': 'version:124.0.6325.0', + 'location': 'lacros_version_skew_tests_v124.0.6326.0', + 'revision': 'version:124.0.6326.0', }, ], },
diff --git a/infra/config/lib/builder_config.star b/infra/config/lib/builder_config.star index be7f105..5ada922 100644 --- a/infra/config/lib/builder_config.star +++ b/infra/config/lib/builder_config.star
@@ -315,6 +315,23 @@ bisect_archive = bisect_archive, ) +def _ci_settings( + *, + retry_failed_shards = None): + """Settings specific to CI builders. + + Args: + retry_failed_shards: (bool) Whether or not failing shards of a test will + be retried. If retries for all failed shards of a test succeed, the + test will be considered to have passed. + + Returns: + A struct that can be passed to the `ci_settings` argument of the builder. + """ + return struct( + retry_failed_shards = retry_failed_shards, + ) + def _try_settings( *, include_all_triggered_testers = False, @@ -403,6 +420,7 @@ android_config = _android_config, # Function for defining try-specific settings + ci_settings = _ci_settings, try_settings = _try_settings, ) @@ -436,7 +454,7 @@ builder_group, builder_spec, mirrors, - try_settings, + settings, targets, additional_exclusions): """Registers the builder config so the properties can be computed. @@ -450,15 +468,16 @@ builder_group: The name of the group the builder belongs to. builder_spec: The spec describing the configuration for the builder. mirrors: References to the builders that the builder should mirror. - try_settings: The object determining the try-specific settings. + settings: The object determining the additional settings applied to + builder_config. targets: The targets to be built/run by the builder. additional_exclusions: A list of paths that are excluded when analyzing the change to determine affected targets. The paths should be relative to the per-builder output root dir. """ if not builder_spec and not mirrors: - if try_settings: - fail("try_settings specified without builder_spec or mirrors") + if settings: + fail("settings specified without builder_spec or mirrors") # TODO(gbeaty) Eventually make this a failure for the chromium # family of recipes @@ -471,13 +490,23 @@ if targets and not builder_spec: fail("builder_spec must be set to set targets") - if not try_settings: - try_settings = _try_settings(include_all_triggered_testers = not mirrors) + include_all_triggered_testers = None + settings_fields = {} + if settings: + settings_fields = structs.to_proto_properties(settings) + include_all_triggered_testers = settings_fields.pop( + "include_all_triggered_testers", + None, + ) + if include_all_triggered_testers == None: + include_all_triggered_testers = not mirrors + builder_config_key = _BUILDER_CONFIG.add(bucket, name, props = dict( builder_group = builder_group, builder_spec = builder_spec, mirrors = mirrors, - try_settings = try_settings, + include_all_triggered_testers = include_all_triggered_testers, + settings_fields = settings_fields, additional_exclusions = additional_exclusions, )) @@ -531,7 +560,7 @@ parent = bc_state.parent(node) if parent: for m in _get_mirroring_nodes(bc_state, parent): - if m.props.try_settings.include_all_triggered_testers: + if m.props.include_all_triggered_testers: nodes.append(m) return nodes @@ -726,7 +755,7 @@ if parent: add(parent) add(m, parent) - if node.props.try_settings.include_all_triggered_testers: + if node.props.include_all_triggered_testers: for child in bc_state.children(m): add(child, m) @@ -747,7 +776,7 @@ entries = sorted(entries, key = lambda e: _builder_id_sort_key(e["builder_id"])), ), builder_ids = sorted(builder_ids, key = _builder_id_sort_key), - **structs.to_proto_properties(node.props.try_settings) + **node.props.settings_fields ) if node.props.additional_exclusions: builder_config["additional_exclusions"] = [ @@ -757,7 +786,6 @@ ) for exclusion in node.props.additional_exclusions ] - builder_config.pop("include_all_triggered_testers", None) if builder_ids_in_scope_for_testing: builder_config["builder_ids_in_scope_for_testing"] = (
diff --git a/infra/config/lib/builders.star b/infra/config/lib/builders.star index 70accd1..159ee4ff 100644 --- a/infra/config/lib/builders.star +++ b/infra/config/lib/builders.star
@@ -451,7 +451,7 @@ builder_group = args.DEFAULT, builder_spec = None, mirrors = None, - try_settings = None, + builder_config_settings = None, pool = args.DEFAULT, ssd = args.DEFAULT, sheriff_rotations = None, @@ -589,8 +589,9 @@ Cannot be set if `mirrors` is set. mirrors: References to the builders that the builder should mirror. Cannot be set if `builder_spec` is set. - try_settings: Try-builder-specific settings, can only be set if - `mirrors` is set. + builder_config_settings: Additional builder configuration that used by + the recipes. Could be an instance of ci_settings or try_settings. + It can only be set if one of builder_spec or mirrors is set. pool: a string indicating the pool of the machines that run the builder. Emits a dimension of the form 'pool:<pool>'. By default, considered None. When running a builder that has no explicit pool dimension, @@ -761,8 +762,9 @@ if builder_spec and mirrors: fail("Only one of builder_spec or mirrors can be set") - if try_settings and not (builder_spec or mirrors): - fail("try_settings can only be set if builder_spec or mirrors is set") + if builder_config_settings and not (builder_spec or mirrors): + fail("builder_config_settings can only be set if builder_spec or " + + "mirrors is set") dimensions = {} @@ -1040,7 +1042,7 @@ builder_group, builder_spec, mirrors, - try_settings, + builder_config_settings, targets, additional_exclusions, )
diff --git a/infra/config/subprojects/chromium/angle.try.star b/infra/config/subprojects/chromium/angle.try.star index 513edb1..f3a0b2f 100644 --- a/infra/config/subprojects/chromium/angle.try.star +++ b/infra/config/subprojects/chromium/angle.try.star
@@ -46,7 +46,7 @@ "ci/ios-angle-builder", "ci/ios-angle-intel", ], - try_settings = builder_config.try_settings( + builder_config_settings = builder_config.try_settings( retry_failed_shards = False, ), gn_args = gn_args.config(
diff --git a/infra/config/subprojects/chromium/ci/chromium.fyi.star b/infra/config/subprojects/chromium/ci/chromium.fyi.star index 4eb5a3e..74025407 100644 --- a/infra/config/subprojects/chromium/ci/chromium.fyi.star +++ b/infra/config/subprojects/chromium/ci/chromium.fyi.star
@@ -291,6 +291,45 @@ ) ci.builder( + name = "linux-chromeos-dbg-oslogin", + description_html = "This builder is used to debug spefically oslogin issues related" + + "to linux-chromeos-dbg-oslogin", + builder_spec = builder_config.builder_spec( + gclient_config = builder_config.gclient_config( + config = "chromium", + apply_configs = [ + "chromeos", + ], + ), + chromium_config = builder_config.chromium_config( + config = "chromium", + apply_configs = [ + "mb", + ], + build_config = builder_config.build_config.DEBUG, + target_arch = builder_config.target_arch.INTEL, + target_bits = 64, + target_platform = builder_config.target_platform.CHROMEOS, + ), + build_gs_bucket = "chromium-chromiumos-archive", + ), + gn_args = gn_args.config( + configs = [ + "chromeos_with_codecs", + "debug_builder", + "reclient", + "use_cups", + ], + ), + sheriff_rotations = None, + console_view_entry = consoles.console_view_entry( + category = "linux", + short_name = "lnx", + ), + contact_team_email = "chrome-browser-infra@google.com", +) + +ci.builder( name = "linux-chromeos-annotator-rel", builder_spec = builder_config.builder_spec( gclient_config = builder_config.gclient_config(
diff --git a/infra/config/subprojects/chromium/swangle.try.star b/infra/config/subprojects/chromium/swangle.try.star index c1a49a2e..130e2f9 100644 --- a/infra/config/subprojects/chromium/swangle.try.star +++ b/infra/config/subprojects/chromium/swangle.try.star
@@ -53,7 +53,7 @@ mirrors = [ "ci/linux-swangle-chromium-x64", ], - try_settings = builder_config.try_settings( + builder_config_settings = builder_config.try_settings( retry_failed_shards = False, ), gn_args = gn_args.config( @@ -72,7 +72,7 @@ mirrors = [ "ci/linux-swangle-chromium-x64-exp", ], - try_settings = builder_config.try_settings( + builder_config_settings = builder_config.try_settings( retry_failed_shards = False, ), gn_args = gn_args.config( @@ -90,7 +90,7 @@ mirrors = [ "ci/linux-swangle-tot-swiftshader-x64", ], - try_settings = builder_config.try_settings( + builder_config_settings = builder_config.try_settings( retry_failed_shards = False, ), gn_args = "ci/linux-swangle-tot-swiftshader-x64", @@ -103,7 +103,7 @@ mirrors = [ "ci/linux-swangle-x64", ], - try_settings = builder_config.try_settings( + builder_config_settings = builder_config.try_settings( retry_failed_shards = False, ), gn_args = "ci/linux-swangle-x64", @@ -116,7 +116,7 @@ mirrors = [ "ci/linux-swangle-x64-exp", ], - try_settings = builder_config.try_settings( + builder_config_settings = builder_config.try_settings( retry_failed_shards = False, ), gn_args = "ci/linux-swangle-x64-exp", @@ -129,7 +129,7 @@ mirrors = [ "ci/mac-swangle-chromium-x64", ], - try_settings = builder_config.try_settings( + builder_config_settings = builder_config.try_settings( retry_failed_shards = False, ), gn_args = gn_args.config( @@ -148,7 +148,7 @@ mirrors = [ "ci/win-swangle-chromium-x86", ], - try_settings = builder_config.try_settings( + builder_config_settings = builder_config.try_settings( retry_failed_shards = False, ), gn_args = gn_args.config( @@ -166,7 +166,7 @@ mirrors = [ "ci/win-swangle-tot-swiftshader-x64", ], - try_settings = builder_config.try_settings( + builder_config_settings = builder_config.try_settings( retry_failed_shards = False, ), gn_args = "ci/win-swangle-tot-swiftshader-x64", @@ -178,7 +178,7 @@ mirrors = [ "ci/win-swangle-tot-swiftshader-x86", ], - try_settings = builder_config.try_settings( + builder_config_settings = builder_config.try_settings( retry_failed_shards = False, ), gn_args = gn_args.config( @@ -196,7 +196,7 @@ mirrors = [ "ci/win-swangle-x64", ], - try_settings = builder_config.try_settings( + builder_config_settings = builder_config.try_settings( retry_failed_shards = False, ), gn_args = "ci/win-swangle-x64", @@ -209,7 +209,7 @@ mirrors = [ "ci/win-swangle-x86", ], - try_settings = builder_config.try_settings( + builder_config_settings = builder_config.try_settings( retry_failed_shards = False, ), gn_args = gn_args.config(
diff --git a/infra/config/subprojects/chromium/try/tryserver.blink.star b/infra/config/subprojects/chromium/try/tryserver.blink.star index 073bb23..631041b 100644 --- a/infra/config/subprojects/chromium/try/tryserver.blink.star +++ b/infra/config/subprojects/chromium/try/tryserver.blink.star
@@ -54,7 +54,7 @@ target_platform = builder_config.target_platform.LINUX, ), ), - try_settings = builder_config.try_settings( + builder_config_settings = builder_config.try_settings( retry_failed_shards = False, ), gn_args = gn_args.config( @@ -81,7 +81,7 @@ Chrome.\ """, mirrors = ["ci/linux-wpt-chromium-rel"], - try_settings = builder_config.try_settings( + builder_config_settings = builder_config.try_settings( retry_failed_shards = True, ), gn_args = "ci/linux-wpt-chromium-rel", @@ -108,7 +108,7 @@ ), build_gs_bucket = "chromium-fyi-archive", ), - try_settings = builder_config.try_settings( + builder_config_settings = builder_config.try_settings( retry_failed_shards = False, ), gn_args = gn_args.config( @@ -141,7 +141,7 @@ target_platform = builder_config.target_platform.WIN, ), ), - try_settings = builder_config.try_settings( + builder_config_settings = builder_config.try_settings( retry_failed_shards = True, ), gn_args = gn_args.config( @@ -174,7 +174,7 @@ target_platform = builder_config.target_platform.WIN, ), ), - try_settings = builder_config.try_settings( + builder_config_settings = builder_config.try_settings( retry_failed_shards = True, ), gn_args = gn_args.config( @@ -206,7 +206,7 @@ target_platform = builder_config.target_platform.MAC, ), ), - try_settings = builder_config.try_settings( + builder_config_settings = builder_config.try_settings( retry_failed_shards = True, ), gn_args = gn_args.config( @@ -235,7 +235,7 @@ target_platform = builder_config.target_platform.MAC, ), ), - try_settings = builder_config.try_settings( + builder_config_settings = builder_config.try_settings( retry_failed_shards = True, ), gn_args = gn_args.config( @@ -266,7 +266,7 @@ ), build_gs_bucket = "chromium-fyi-archive", ), - try_settings = builder_config.try_settings( + builder_config_settings = builder_config.try_settings( retry_failed_shards = True, ), gn_args = gn_args.config( @@ -296,7 +296,7 @@ target_platform = builder_config.target_platform.MAC, ), ), - try_settings = builder_config.try_settings( + builder_config_settings = builder_config.try_settings( retry_failed_shards = False, ), gn_args = gn_args.config( @@ -325,7 +325,7 @@ target_platform = builder_config.target_platform.MAC, ), ), - try_settings = builder_config.try_settings( + builder_config_settings = builder_config.try_settings( retry_failed_shards = True, ), gn_args = gn_args.config( @@ -355,7 +355,7 @@ target_platform = builder_config.target_platform.MAC, ), ), - try_settings = builder_config.try_settings( + builder_config_settings = builder_config.try_settings( retry_failed_shards = False, ), gn_args = gn_args.config( @@ -384,7 +384,7 @@ target_platform = builder_config.target_platform.MAC, ), ), - try_settings = builder_config.try_settings( + builder_config_settings = builder_config.try_settings( retry_failed_shards = True, ), gn_args = gn_args.config( @@ -414,7 +414,7 @@ target_platform = builder_config.target_platform.MAC, ), ), - try_settings = builder_config.try_settings( + builder_config_settings = builder_config.try_settings( retry_failed_shards = True, ), gn_args = gn_args.config(
diff --git a/infra/config/subprojects/chromium/try/tryserver.chromium.android.star b/infra/config/subprojects/chromium/try/tryserver.chromium.android.star index 242017b..8c7fba1 100644 --- a/infra/config/subprojects/chromium/try/tryserver.chromium.android.star +++ b/infra/config/subprojects/chromium/try/tryserver.chromium.android.star
@@ -986,7 +986,7 @@ mirrors = [ "ci/Android arm64 Builder (dbg)", ], - try_settings = builder_config.try_settings( + builder_config_settings = builder_config.try_settings( include_all_triggered_testers = True, is_compile_only = True, ), @@ -1079,7 +1079,7 @@ mirrors = [ "ci/Android arm64 Builder All Targets (dbg)", ], - try_settings = builder_config.try_settings( + builder_config_settings = builder_config.try_settings( include_all_triggered_testers = True, is_compile_only = True, ), @@ -1113,7 +1113,7 @@ mirrors = [ "ci/Android x64 Builder All Targets (dbg)", ], - try_settings = builder_config.try_settings( + builder_config_settings = builder_config.try_settings( include_all_triggered_testers = True, is_compile_only = True, ), @@ -1162,7 +1162,7 @@ mirrors = [ "ci/Android x86 Builder (dbg)", ], - try_settings = builder_config.try_settings( + builder_config_settings = builder_config.try_settings( include_all_triggered_testers = True, is_compile_only = True, ), @@ -1198,7 +1198,7 @@ mirrors = [ "ci/android-cronet-arm-rel", ], - try_settings = builder_config.try_settings( + builder_config_settings = builder_config.try_settings( is_compile_only = True, ), gn_args = gn_args.config( @@ -1239,7 +1239,7 @@ ), build_gs_bucket = "chromium-gpu-fyi-archive", ), - try_settings = builder_config.try_settings( + builder_config_settings = builder_config.try_settings( retry_failed_shards = False, ), gn_args = gn_args.config( @@ -1296,7 +1296,7 @@ "ci/GPU FYI Android arm64 Builder", "ci/Android FYI Release (Pixel 6)", ], - try_settings = builder_config.try_settings( + builder_config_settings = builder_config.try_settings( retry_failed_shards = False, ), gn_args = "ci/GPU FYI Android arm64 Builder",
diff --git a/infra/config/subprojects/chromium/try/tryserver.chromium.angle.star b/infra/config/subprojects/chromium/try/tryserver.chromium.angle.star index 0aae4f4..cc713e8 100644 --- a/infra/config/subprojects/chromium/try/tryserver.chromium.angle.star +++ b/infra/config/subprojects/chromium/try/tryserver.chromium.angle.star
@@ -33,7 +33,7 @@ "ci/android-angle-chromium-arm64-builder", "ci/android-angle-chromium-arm64-nexus5x", ], - try_settings = builder_config.try_settings( + builder_config_settings = builder_config.try_settings( retry_failed_shards = False, ), gn_args = gn_args.config( @@ -50,7 +50,7 @@ mirrors = [ "ci/fuchsia-angle-builder", ], - try_settings = builder_config.try_settings( + builder_config_settings = builder_config.try_settings( include_all_triggered_testers = True, is_compile_only = True, ), @@ -70,7 +70,7 @@ "ci/linux-angle-chromium-intel", "ci/linux-angle-chromium-nvidia", ], - try_settings = builder_config.try_settings( + builder_config_settings = builder_config.try_settings( retry_failed_shards = False, ), gn_args = gn_args.config( @@ -89,7 +89,7 @@ "ci/mac-angle-chromium-builder", "ci/mac-angle-chromium-intel", ], - try_settings = builder_config.try_settings( + builder_config_settings = builder_config.try_settings( retry_failed_shards = False, ), gn_args = gn_args.config( @@ -111,7 +111,7 @@ "ci/win10-angle-chromium-x64-intel", "ci/win10-angle-chromium-x64-nvidia", ], - try_settings = builder_config.try_settings( + builder_config_settings = builder_config.try_settings( retry_failed_shards = False, ), gn_args = gn_args.config( @@ -129,7 +129,7 @@ mirrors = [ "ci/win-angle-chromium-x86-builder", ], - try_settings = builder_config.try_settings( + builder_config_settings = builder_config.try_settings( include_all_triggered_testers = True, is_compile_only = True, retry_failed_shards = False,
diff --git a/infra/config/subprojects/chromium/try/tryserver.chromium.chromiumos.star b/infra/config/subprojects/chromium/try/tryserver.chromium.chromiumos.star index 238a09c..2c3975b 100644 --- a/infra/config/subprojects/chromium/try/tryserver.chromium.chromiumos.star +++ b/infra/config/subprojects/chromium/try/tryserver.chromium.chromiumos.star
@@ -418,7 +418,7 @@ mirrors = [ "ci/linux-chromeos-dbg", ], - try_settings = builder_config.try_settings( + builder_config_settings = builder_config.try_settings( include_all_triggered_testers = True, is_compile_only = True, ),
diff --git a/infra/config/subprojects/chromium/try/tryserver.chromium.fuchsia.star b/infra/config/subprojects/chromium/try/tryserver.chromium.fuchsia.star index 811d40d..0074089b8 100644 --- a/infra/config/subprojects/chromium/try/tryserver.chromium.fuchsia.star +++ b/infra/config/subprojects/chromium/try/tryserver.chromium.fuchsia.star
@@ -126,7 +126,7 @@ mirrors = [ "ci/fuchsia-x64-dbg", ], - try_settings = builder_config.try_settings( + builder_config_settings = builder_config.try_settings( include_all_triggered_testers = True, is_compile_only = True, ),
diff --git a/infra/config/subprojects/chromium/try/tryserver.chromium.linux.star b/infra/config/subprojects/chromium/try/tryserver.chromium.linux.star index 44fb46a..af44544 100644 --- a/infra/config/subprojects/chromium/try/tryserver.chromium.linux.star +++ b/infra/config/subprojects/chromium/try/tryserver.chromium.linux.star
@@ -158,7 +158,7 @@ mirrors = [ "ci/linux-archive-rel", ], - try_settings = builder_config.try_settings( + builder_config_settings = builder_config.try_settings( include_all_triggered_testers = True, is_compile_only = True, ), @@ -595,7 +595,7 @@ This builder should be removed after migrating linux_chromium_asan_rel_ng from Ninja to Siso. b/277863839 """, mirrors = builder_config.copy_from("try/linux_chromium_asan_rel_ng"), - try_settings = builder_config.try_settings( + builder_config_settings = builder_config.try_settings( is_compile_only = True, ), gn_args = "try/linux_chromium_asan_rel_ng", @@ -706,7 +706,7 @@ name = "linux_chromium_compile_dbg_ng", branch_selector = branches.selector.LINUX_BRANCHES, mirrors = ["ci/Linux Builder (dbg)"], - try_settings = builder_config.try_settings( + builder_config_settings = builder_config.try_settings( include_all_triggered_testers = True, is_compile_only = True, ), @@ -741,7 +741,7 @@ This builder should be removed after migrating linux_chromium_compile_dbg_ng from Ninja to Siso. b/277863839 """, mirrors = builder_config.copy_from("try/linux_chromium_compile_dbg_ng"), - try_settings = builder_config.try_settings( + builder_config_settings = builder_config.try_settings( include_all_triggered_testers = True, is_compile_only = True, ), @@ -767,7 +767,7 @@ mirrors = [ "ci/Linux Builder", ], - try_settings = builder_config.try_settings( + builder_config_settings = builder_config.try_settings( include_all_triggered_testers = True, is_compile_only = True, ), @@ -870,7 +870,7 @@ This builder should be removed after migrating linux_chromium_tsan_rel_ng from Ninja to Siso. b/277863839 """, mirrors = builder_config.copy_from("try/linux_chromium_tsan_rel_ng"), - try_settings = builder_config.try_settings( + builder_config_settings = builder_config.try_settings( is_compile_only = True, ), gn_args = "try/linux_chromium_tsan_rel_ng", @@ -1010,7 +1010,7 @@ ), build_gs_bucket = "chromium-gpu-fyi-archive", ), - try_settings = builder_config.try_settings( + builder_config_settings = builder_config.try_settings( retry_failed_shards = False, ), gn_args = gn_args.config(
diff --git a/infra/config/subprojects/chromium/try/tryserver.chromium.mac.star b/infra/config/subprojects/chromium/try/tryserver.chromium.mac.star index 6b3fe01..1102168 100644 --- a/infra/config/subprojects/chromium/try/tryserver.chromium.mac.star +++ b/infra/config/subprojects/chromium/try/tryserver.chromium.mac.star
@@ -453,7 +453,7 @@ mirrors = [ "ci/Mac Builder (dbg)", ], - try_settings = builder_config.try_settings( + builder_config_settings = builder_config.try_settings( include_all_triggered_testers = True, is_compile_only = True, ), @@ -476,7 +476,7 @@ mirrors = [ "ci/Mac Builder", ], - try_settings = builder_config.try_settings( + builder_config_settings = builder_config.try_settings( include_all_triggered_testers = True, is_compile_only = True, ),
diff --git a/infra/config/subprojects/chromium/try/tryserver.chromium.win.star b/infra/config/subprojects/chromium/try/tryserver.chromium.win.star index aecdfa4..9608946 100644 --- a/infra/config/subprojects/chromium/try/tryserver.chromium.win.star +++ b/infra/config/subprojects/chromium/try/tryserver.chromium.win.star
@@ -187,7 +187,7 @@ mirrors = [ "ci/Win Builder (dbg)", ], - try_settings = builder_config.try_settings( + builder_config_settings = builder_config.try_settings( include_all_triggered_testers = True, is_compile_only = True, ), @@ -214,7 +214,7 @@ mirrors = [ "ci/Win Builder", ], - try_settings = builder_config.try_settings( + builder_config_settings = builder_config.try_settings( include_all_triggered_testers = True, is_compile_only = True, ), @@ -449,7 +449,7 @@ ), build_gs_bucket = "chromium-gpu-fyi-archive", ), - try_settings = builder_config.try_settings( + builder_config_settings = builder_config.try_settings( retry_failed_shards = False, ), gn_args = gn_args.config(
diff --git a/infra/config/targets/lacros-version-skew-variants.json b/infra/config/targets/lacros-version-skew-variants.json index 081d747..feb8db79 100644 --- a/infra/config/targets/lacros-version-skew-variants.json +++ b/infra/config/targets/lacros-version-skew-variants.json
@@ -1,16 +1,16 @@ { "LACROS_VERSION_SKEW_CANARY": { "args": [ - "--ash-chrome-path-override=../../lacros_version_skew_tests_v124.0.6325.0/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v124.0.6326.0/test_ash_chrome" ], - "description": "Run with ash-chrome version 124.0.6325.0", + "description": "Run with ash-chrome version 124.0.6326.0", "identifier": "Lacros version skew testing ash canary", "swarming": { "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v124.0.6325.0", - "revision": "version:124.0.6325.0" + "location": "lacros_version_skew_tests_v124.0.6326.0", + "revision": "version:124.0.6326.0" } ] }
diff --git a/infra/config/targets/mixins.star b/infra/config/targets/mixins.star index 231d583..6865e11 100644 --- a/infra/config/targets/mixins.star +++ b/infra/config/targets/mixins.star
@@ -500,6 +500,15 @@ ) targets.mixin( + name = "chromium-tests-oslogin", + swarming = targets.swarming( + dimensions = { + "pool": "chromium.tests.oslogin", + }, + ), +) + +targets.mixin( name = "dawn_end2end_gpu_test", args = [ "--use-gpu-in-tests",
diff --git a/internal b/internal index f504815..e992b1f 160000 --- a/internal +++ b/internal
@@ -1 +1 @@ -Subproject commit f5048154f82911c84ab994c1546290d567a75ec4 +Subproject commit e992b1f6ab810377d0c9cb55aa6a82752df9e8d9
diff --git a/ios/chrome/browser/autofill/model/BUILD.gn b/ios/chrome/browser/autofill/model/BUILD.gn index 524f8641..e930ecb01 100644 --- a/ios/chrome/browser/autofill/model/BUILD.gn +++ b/ios/chrome/browser/autofill/model/BUILD.gn
@@ -116,6 +116,7 @@ "//ios/chrome/browser/infobars/model", "//ios/chrome/browser/shared/model/application_context", "//ios/chrome/browser/shared/model/browser_state", + "//ios/chrome/browser/shared/public/commands:commands", "//ios/chrome/browser/signin/model", "//ios/chrome/browser/ui/autofill", "//third_party/leveldatabase",
diff --git a/ios/chrome/browser/autofill/model/autofill_tab_helper.h b/ios/chrome/browser/autofill/model/autofill_tab_helper.h index 5d24dba8..f003c10 100644 --- a/ios/chrome/browser/autofill/model/autofill_tab_helper.h +++ b/ios/chrome/browser/autofill/model/autofill_tab_helper.h
@@ -12,6 +12,7 @@ #import "ios/web/public/web_state_user_data.h" @class AutofillAgent; +@protocol AutofillCommands; class ChromeBrowserState; @protocol FormSuggestionProvider; @class UIViewController; @@ -32,6 +33,8 @@ // Sets a weak reference to the view controller used to present UI. void SetBaseViewController(UIViewController* base_view_controller); + void SetCommandsHandler(id<AutofillCommands> commands_handler); + // Returns an object that can provide Autofill suggestions. id<FormSuggestionProvider> GetSuggestionProvider();
diff --git a/ios/chrome/browser/autofill/model/autofill_tab_helper.mm b/ios/chrome/browser/autofill/model/autofill_tab_helper.mm index 3b66c52..a33b6e21 100644 --- a/ios/chrome/browser/autofill/model/autofill_tab_helper.mm +++ b/ios/chrome/browser/autofill/model/autofill_tab_helper.mm
@@ -12,6 +12,7 @@ #import "ios/chrome/browser/infobars/model/infobar_manager_impl.h" #import "ios/chrome/browser/shared/model/application_context/application_context.h" #import "ios/chrome/browser/shared/model/browser_state/chrome_browser_state.h" +#import "ios/chrome/browser/shared/public/commands/autofill_commands.h" #import "ios/chrome/browser/ui/autofill/chrome_autofill_client_ios.h" AutofillTabHelper::~AutofillTabHelper() = default; @@ -21,6 +22,11 @@ autofill_client_->SetBaseViewController(base_view_controller); } +void AutofillTabHelper::SetCommandsHandler( + id<AutofillCommands> commands_handler) { + autofill_client_->set_commands_handler(commands_handler); +} + id<FormSuggestionProvider> AutofillTabHelper::GetSuggestionProvider() { return autofill_agent_; }
diff --git a/ios/chrome/browser/autofill/model/bottom_sheet/autofill_bottom_sheet_tab_helper.h b/ios/chrome/browser/autofill/model/bottom_sheet/autofill_bottom_sheet_tab_helper.h index f00ce112..3287064 100644 --- a/ios/chrome/browser/autofill/model/bottom_sheet/autofill_bottom_sheet_tab_helper.h +++ b/ios/chrome/browser/autofill/model/bottom_sheet/autofill_bottom_sheet_tab_helper.h
@@ -27,7 +27,7 @@ class ScriptMessage; } // namespace web -@protocol AutofillBottomSheetCommands; +@protocol AutofillCommands; @class CommandDispatcher; @protocol PasswordsAccountStorageNoticeHandler; @@ -76,8 +76,7 @@ void OnFormMessageReceived(const web::ScriptMessage& message); // Sets the bottom sheet CommandDispatcher. - void SetAutofillBottomSheetHandler( - id<AutofillBottomSheetCommands> commands_handler); + void SetAutofillBottomSheetHandler(id<AutofillCommands> commands_handler); // Prepare bottom sheet using data from the password form prediction. void AttachPasswordListeners( @@ -154,7 +153,7 @@ void ShowPaymentsBottomSheet(const autofill::FormActivityParams params); // Handler used to request showing the password bottom sheet. - __weak id<AutofillBottomSheetCommands> commands_handler_; + __weak id<AutofillCommands> commands_handler_; // Handler used for the passwords account storage notice. // TODO(crbug.com/1434606): Remove this when the move to account storage
diff --git a/ios/chrome/browser/autofill/model/bottom_sheet/autofill_bottom_sheet_tab_helper.mm b/ios/chrome/browser/autofill/model/bottom_sheet/autofill_bottom_sheet_tab_helper.mm index 197a953..1274724 100644 --- a/ios/chrome/browser/autofill/model/bottom_sheet/autofill_bottom_sheet_tab_helper.mm +++ b/ios/chrome/browser/autofill/model/bottom_sheet/autofill_bottom_sheet_tab_helper.mm
@@ -21,7 +21,7 @@ #import "ios/chrome/browser/autofill/model/bottom_sheet/autofill_bottom_sheet_observer.h" #import "ios/chrome/browser/shared/model/browser_state/chrome_browser_state.h" #import "ios/chrome/browser/shared/model/prefs/pref_names.h" -#import "ios/chrome/browser/shared/public/commands/autofill_bottom_sheet_commands.h" +#import "ios/chrome/browser/shared/public/commands/autofill_commands.h" #import "ios/chrome/browser/shared/public/commands/command_dispatcher.h" #import "ios/chrome/browser/shared/public/features/features.h" #import "ios/web/public/js_messaging/script_message.h" @@ -82,7 +82,7 @@ } void AutofillBottomSheetTabHelper::SetAutofillBottomSheetHandler( - id<AutofillBottomSheetCommands> commands_handler) { + id<AutofillCommands> commands_handler) { commands_handler_ = commands_handler; } @@ -126,8 +126,7 @@ return; } - __weak id<AutofillBottomSheetCommands> weak_commands_handler = - commands_handler_; + __weak id<AutofillCommands> weak_commands_handler = commands_handler_; [password_account_storage_notice_handler_ showAccountStorageNotice:^{ [weak_commands_handler showPasswordBottomSheet:params]; }];
diff --git a/ios/chrome/browser/default_browser/model/utils.mm b/ios/chrome/browser/default_browser/model/utils.mm index 698d5f5..4207c62 100644 --- a/ios/chrome/browser/default_browser/model/utils.mm +++ b/ios/chrome/browser/default_browser/model/utils.mm
@@ -616,11 +616,16 @@ bool HasUserInteractedWithFullscreenPromoBefore() { if (base::FeatureList::IsEnabled( feature_engagement::kDefaultBrowserEligibilitySlidingWindow)) { - return HasRecordedEventForKeyLessThanDelay( - kLastTimeUserInteractedWithFullscreenPromo, - base::Days( - feature_engagement::kDefaultBrowserEligibilitySlidingWindowParam - .Get())); + // When the total promo count is 1 it means that user has seen only the FRE + // promo. The cooldown from FRE will be taken care of in + // ```ComputeCooldown```. Here we only need to check the timestamp of the + // last promo if users seen more than FRE. + return DisplayedFullscreenPromoCount() > 1 && + HasRecordedEventForKeyLessThanDelay( + kLastTimeUserInteractedWithFullscreenPromo, + base::Days( + feature_engagement:: + kDefaultBrowserEligibilitySlidingWindowParam.Get())); } NSNumber* number = GetObjectFromStorageForKey<NSNumber>(
diff --git a/ios/chrome/browser/default_browser/model/utils_unittest.mm b/ios/chrome/browser/default_browser/model/utils_unittest.mm index 51f9702..d90c30b 100644 --- a/ios/chrome/browser/default_browser/model/utils_unittest.mm +++ b/ios/chrome/browser/default_browser/model/utils_unittest.mm
@@ -71,6 +71,9 @@ // Test key in storage for counting past default browser promo interactions. NSString* const kGenericPromoInteractionCount = @"genericPromoInteractionCount"; +// Test Key in storage for counting all past default browser promo displays. +NSString* const kDisplayedFullscreenPromoCount = @"displayedPromoCount"; + class DefaultBrowserUtilsTest : public PlatformTest { protected: void SetUp() override { ClearDefaultBrowserPromoData(); } @@ -79,13 +82,16 @@ base::test::ScopedFeatureList feature_list_; }; +// Overwrite local storage with the provided interaction information. void SimulateUserInteractionWithFullscreenPromo(const base::TimeDelta& timeAgo, - int count) { + int count, + int totalCount) { NSDictionary<NSString*, NSObject*>* values = @{ kUserHasInteractedWithFullscreenPromo : @YES, kLastTimeUserInteractedWithFullscreenPromo : (base::Time::Now() - timeAgo) .ToNSDate(), - kGenericPromoInteractionCount : [NSNumber numberWithInt:count] + kGenericPromoInteractionCount : [NSNumber numberWithInt:count], + kDisplayedFullscreenPromoCount : [NSNumber numberWithInt:totalCount] }; SetValuesInStorage(values); } @@ -360,23 +366,29 @@ {/*disabled=*/feature_engagement:: kDefaultBrowserEligibilitySlidingWindow}); - // Test with multiple interactions. + // Test when there are no interaction recorded yet. EXPECT_FALSE(HasUserInteractedWithFullscreenPromoBefore()); - SimulateUserInteractionWithFullscreenPromo(kMoreThan6Hours, 1); + + // Test that logging first run doesn't affect it. + LogUserInteractionWithFirstRunPromo(true); + EXPECT_FALSE(HasUserInteractedWithFullscreenPromoBefore()); + + // Test with multiple interactions. + SimulateUserInteractionWithFullscreenPromo(kMoreThan6Hours, 1, 2); EXPECT_TRUE(HasUserInteractedWithFullscreenPromoBefore()); - SimulateUserInteractionWithFullscreenPromo(kMoreThan14Days, 2); + SimulateUserInteractionWithFullscreenPromo(kMoreThan14Days, 2, 3); EXPECT_TRUE(HasUserInteractedWithFullscreenPromoBefore()); // Test with a single, more distant interaction. ClearDefaultBrowserPromoData(); EXPECT_FALSE(HasUserInteractedWithFullscreenPromoBefore()); - SimulateUserInteractionWithFullscreenPromo(k6Months, 1); + SimulateUserInteractionWithFullscreenPromo(k6Months, 1, 2); EXPECT_TRUE(HasUserInteractedWithFullscreenPromoBefore()); // Test with a single, even more distant interaction. ClearDefaultBrowserPromoData(); EXPECT_FALSE(HasUserInteractedWithFullscreenPromoBefore()); - SimulateUserInteractionWithFullscreenPromo(k2Years, 1); + SimulateUserInteractionWithFullscreenPromo(k2Years, 1, 2); EXPECT_TRUE(HasUserInteractedWithFullscreenPromoBefore()); } @@ -391,37 +403,65 @@ feature_engagement::kDefaultBrowserEligibilitySlidingWindow, feature_params); - // Test with multiple interactions. + // Test when there are no interaction recorded yet. EXPECT_FALSE(HasUserInteractedWithFullscreenPromoBefore()); - SimulateUserInteractionWithFullscreenPromo(kMoreThan6Hours, 1); + + // Test that logging first run doesn't affect it. + LogUserInteractionWithFirstRunPromo(true); + EXPECT_FALSE(HasUserInteractedWithFullscreenPromoBefore()); + + // Test with multiple interactions. + SimulateUserInteractionWithFullscreenPromo(kMoreThan6Hours, 1, 2); EXPECT_TRUE(HasUserInteractedWithFullscreenPromoBefore()); - SimulateUserInteractionWithFullscreenPromo(kMoreThan14Days, 2); + SimulateUserInteractionWithFullscreenPromo(kMoreThan14Days, 2, 3); EXPECT_TRUE(HasUserInteractedWithFullscreenPromoBefore()); // Test with a single, more distant interaction (but still within the sliding // window limit). ClearDefaultBrowserPromoData(); EXPECT_FALSE(HasUserInteractedWithFullscreenPromoBefore()); - SimulateUserInteractionWithFullscreenPromo(k6Months, 1); + SimulateUserInteractionWithFullscreenPromo(k6Months, 1, 2); EXPECT_TRUE(HasUserInteractedWithFullscreenPromoBefore()); // Test with a single interaction that's outside the sliding window limit. ClearDefaultBrowserPromoData(); EXPECT_FALSE(HasUserInteractedWithFullscreenPromoBefore()); - SimulateUserInteractionWithFullscreenPromo(k2Years, 1); + SimulateUserInteractionWithFullscreenPromo(k2Years, 1, 2); EXPECT_FALSE(HasUserInteractedWithFullscreenPromoBefore()); // Test with multiple interactions, some within and some outside the sliding // window limit. ClearDefaultBrowserPromoData(); EXPECT_FALSE(HasUserInteractedWithFullscreenPromoBefore()); - SimulateUserInteractionWithFullscreenPromo(k5Years, 1); + SimulateUserInteractionWithFullscreenPromo(k5Years, 1, 2); EXPECT_FALSE(HasUserInteractedWithFullscreenPromoBefore()); - SimulateUserInteractionWithFullscreenPromo(k2Years, 2); + SimulateUserInteractionWithFullscreenPromo(k2Years, 2, 3); EXPECT_FALSE(HasUserInteractedWithFullscreenPromoBefore()); - SimulateUserInteractionWithFullscreenPromo(k6Months, 3); + SimulateUserInteractionWithFullscreenPromo(k6Months, 3, 4); EXPECT_TRUE(HasUserInteractedWithFullscreenPromoBefore()); - SimulateUserInteractionWithFullscreenPromo(kMoreThan14Days, 4); + SimulateUserInteractionWithFullscreenPromo(kMoreThan14Days, 4, 5); + EXPECT_TRUE(HasUserInteractedWithFullscreenPromoBefore()); +} + +// Tests that sliding window experiment doesn't not affect the cooldown from +// FRE. +TEST_F(DefaultBrowserUtilsTest, CooldownFromFRESlidingWindowEnabled) { + base::FieldTrialParams feature_params; + feature_params["sliding-window-days"] = "365"; + feature_list_.InitAndEnableFeatureWithParameters( + feature_engagement::kDefaultBrowserEligibilitySlidingWindow, + feature_params); + + // Test when there are no interaction recorded yet. + EXPECT_FALSE(HasUserInteractedWithFullscreenPromoBefore()); + + // Test that logging first run doesn't affect it. + LogUserInteractionWithFirstRunPromo(true); + EXPECT_FALSE(HasUserInteractedWithFullscreenPromoBefore()); + + // Test that logging a generic promo interaction will affect it. + LogUserInteractionWithFullscreenPromo(); + LogFullscreenDefaultBrowserPromoDisplayed(); EXPECT_TRUE(HasUserInteractedWithFullscreenPromoBefore()); }
diff --git a/ios/chrome/browser/policy/model/configuration_policy_handler_list_factory.mm b/ios/chrome/browser/policy/model/configuration_policy_handler_list_factory.mm index eb1c60c4..aa2a6522 100644 --- a/ios/chrome/browser/policy/model/configuration_policy_handler_list_factory.mm +++ b/ios/chrome/browser/policy/model/configuration_policy_handler_list_factory.mm
@@ -144,6 +144,9 @@ { policy::key::kDownloadManagerSaveToDriveSettings, prefs::kIosSaveToDriveDownloadManagerPolicySettings, base::Value::Type::INTEGER }, + { policy::key::kProductSpecificationsEnabled, + commerce::kProductSpecificationsEnabledPrefName, + base::Value::Type::BOOLEAN}, }; // clang-format on
diff --git a/ios/chrome/browser/segmentation_platform/model/segmentation_platform_config.mm b/ios/chrome/browser/segmentation_platform/model/segmentation_platform_config.mm index f8c9ccef..10a85f6a 100644 --- a/ios/chrome/browser/segmentation_platform/model/segmentation_platform_config.mm +++ b/ios/chrome/browser/segmentation_platform/model/segmentation_platform_config.mm
@@ -51,7 +51,7 @@ configs.emplace_back(MostVisitedTilesUser::GetConfig()); // Add new configs here. - base::EraseIf(configs, [](const auto& config) { return !config.get(); }); + std::erase_if(configs, [](const auto& config) { return !config.get(); }); return configs; }
diff --git a/ios/chrome/browser/shared/public/commands/BUILD.gn b/ios/chrome/browser/shared/public/commands/BUILD.gn index 41bb96e..e46887b 100644 --- a/ios/chrome/browser/shared/public/commands/BUILD.gn +++ b/ios/chrome/browser/shared/public/commands/BUILD.gn
@@ -7,7 +7,7 @@ "account_picker_commands.h", "activity_service_commands.h", "application_commands.h", - "autofill_bottom_sheet_commands.h", + "autofill_commands.h", "bookmarks_commands.h", "bring_android_tabs_commands.h", "browser_commands.h",
diff --git a/ios/chrome/browser/shared/public/commands/autofill_bottom_sheet_commands.h b/ios/chrome/browser/shared/public/commands/autofill_commands.h similarity index 60% rename from ios/chrome/browser/shared/public/commands/autofill_bottom_sheet_commands.h rename to ios/chrome/browser/shared/public/commands/autofill_commands.h index 9d05328..db870565 100644 --- a/ios/chrome/browser/shared/public/commands/autofill_bottom_sheet_commands.h +++ b/ios/chrome/browser/shared/public/commands/autofill_commands.h
@@ -2,18 +2,19 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef IOS_CHROME_BROWSER_SHARED_PUBLIC_COMMANDS_AUTOFILL_BOTTOM_SHEET_COMMANDS_H_ -#define IOS_CHROME_BROWSER_SHARED_PUBLIC_COMMANDS_AUTOFILL_BOTTOM_SHEET_COMMANDS_H_ +#ifndef IOS_CHROME_BROWSER_SHARED_PUBLIC_COMMANDS_AUTOFILL_COMMANDS_H_ +#define IOS_CHROME_BROWSER_SHARED_PUBLIC_COMMANDS_AUTOFILL_COMMANDS_H_ #import "components/plus_addresses/plus_address_types.h" namespace autofill { +struct AutofillErrorDialogContext; struct FormActivityParams; struct VirtualCardEnrollUiModel; } // namespace autofill -// Commands related to the passwords bottom sheet. -@protocol AutofillBottomSheetCommands +// Commands related to the Autofill flows (passwords, addresses, payments etc). +@protocol AutofillCommands // Shows the password suggestion view controller. - (void)showPasswordBottomSheet:(const autofill::FormActivityParams&)params; @@ -28,6 +29,11 @@ - (void)showVirtualCardEnrollmentBottomSheet: (const autofill::VirtualCardEnrollUiModel&)model; +// Commands to manage the Autofill error dialog. +- (void)showAutofillErrorDialog: + (autofill::AutofillErrorDialogContext)errorContext; +- (void)dismissAutofillErrorDialog; + @end -#endif // IOS_CHROME_BROWSER_SHARED_PUBLIC_COMMANDS_AUTOFILL_BOTTOM_SHEET_COMMANDS_H_ +#endif // IOS_CHROME_BROWSER_SHARED_PUBLIC_COMMANDS_AUTOFILL_COMMANDS_H_
diff --git a/ios/chrome/browser/shared/ui/table_view/cells/table_view_url_item.mm b/ios/chrome/browser/shared/ui/table_view/cells/table_view_url_item.mm index 6e0530b4..cc49a9c 100644 --- a/ios/chrome/browser/shared/ui/table_view/cells/table_view_url_item.mm +++ b/ios/chrome/browser/shared/ui/table_view/cells/table_view_url_item.mm
@@ -22,6 +22,8 @@ // Default delimiter to use between the hostname and the supplemental URL text // if text is specified but not the delimiter. const char kDefaultSupplementalURLTextDelimiter[] = "•"; +// The max number of lines for the cell title label. +const int kMaxNumberOfLinesForCellTitleLabel = 4; } // namespace #pragma mark - TableViewURLItem @@ -167,7 +169,9 @@ // Set font sizes using dynamic type. _titleLabel.font = [UIFont preferredFontForTextStyle:UIFontTextStyleBody]; _titleLabel.adjustsFontForContentSizeCategory = YES; - _titleLabel.numberOfLines = 0; + // Sometimes a very long url is used as the cell title, so be sure it will + // not stretch the cell to an unlimited number of lines. + _titleLabel.numberOfLines = kMaxNumberOfLinesForCellTitleLabel; _URLLabel.font = [UIFont preferredFontForTextStyle:UIFontTextStyleFootnote]; _URLLabel.adjustsFontForContentSizeCategory = YES; _URLLabel.textColor = [UIColor colorNamed:kTextSecondaryColor];
diff --git a/ios/chrome/browser/ui/autofill/BUILD.gn b/ios/chrome/browser/ui/autofill/BUILD.gn index 4e5c46a..9be2135 100644 --- a/ios/chrome/browser/ui/autofill/BUILD.gn +++ b/ios/chrome/browser/ui/autofill/BUILD.gn
@@ -45,6 +45,7 @@ "//ios/chrome/browser/shared/model/application_context", "//ios/chrome/browser/shared/model/browser_state", "//ios/chrome/browser/shared/model/web_state_list:web_state_list", + "//ios/chrome/browser/shared/public/commands:commands", "//ios/chrome/browser/shared/ui/util", "//ios/chrome/browser/signin/model", "//ios/chrome/browser/ssl/model",
diff --git a/ios/chrome/browser/ui/autofill/chrome_autofill_client_ios.h b/ios/chrome/browser/ui/autofill/chrome_autofill_client_ios.h index f86cef9..7fbe57f9 100644 --- a/ios/chrome/browser/ui/autofill/chrome_autofill_client_ios.h +++ b/ios/chrome/browser/ui/autofill/chrome_autofill_client_ios.h
@@ -34,6 +34,7 @@ #include "components/sync/service/sync_service.h" #include "ios/chrome/browser/shared/model/browser_state/chrome_browser_state.h" +@protocol AutofillCommands; @class UIViewController; namespace web { @@ -42,6 +43,8 @@ namespace autofill { +struct AutofillErrorDialogContext; + namespace payments { class IOSChromePaymentsAutofillClient; } @@ -62,6 +65,11 @@ // Sets a weak reference to the view controller used to present UI. void SetBaseViewController(UIViewController* base_view_controller); + void set_commands_handler(id<AutofillCommands> commands_handler) { + commands_handler_ = commands_handler; + } + id<AutofillCommands> commands_handler() const { return commands_handler_; } + // AutofillClient: version_info::Channel GetChannel() const override; bool IsOffTheRecord() override; @@ -144,6 +152,7 @@ FillingProduct main_filling_product, AutofillSuggestionTriggerSource trigger_source) override; void HideAutofillPopup(PopupHidingReason reason) override; + void ShowAutofillErrorDialog(AutofillErrorDialogContext context) override; bool IsAutocompleteEnabled() const override; bool IsPasswordManagerEnabled() override; void DidFillOrPreviewForm(mojom::ActionPersistence action_persistence, @@ -200,6 +209,8 @@ // A weak reference to the view controller used to present UI. __weak UIViewController* base_view_controller_; + + __weak id<AutofillCommands> commands_handler_; }; } // namespace autofill
diff --git a/ios/chrome/browser/ui/autofill/chrome_autofill_client_ios.mm b/ios/chrome/browser/ui/autofill/chrome_autofill_client_ios.mm index 517e40e..30d874b 100644 --- a/ios/chrome/browser/ui/autofill/chrome_autofill_client_ios.mm +++ b/ios/chrome/browser/ui/autofill/chrome_autofill_client_ios.mm
@@ -20,6 +20,7 @@ #import "components/autofill/core/browser/form_data_importer.h" #import "components/autofill/core/browser/logging/log_manager.h" #import "components/autofill/core/browser/payments/autofill_credit_card_filling_infobar_delegate_mobile.h" +#import "components/autofill/core/browser/payments/autofill_error_dialog_context.h" #import "components/autofill/core/browser/payments/autofill_save_card_delegate.h" #import "components/autofill/core/browser/payments/autofill_save_card_infobar_delegate_mobile.h" #import "components/autofill/core/browser/payments/autofill_save_card_ui_info.h" @@ -59,6 +60,7 @@ #import "ios/chrome/browser/passwords/model/password_tab_helper.h" #import "ios/chrome/browser/plus_addresses/model/plus_address_service_factory.h" #import "ios/chrome/browser/shared/model/application_context/application_context.h" +#import "ios/chrome/browser/shared/public/commands/autofill_commands.h" #import "ios/chrome/browser/signin/model/authentication_service.h" #import "ios/chrome/browser/signin/model/authentication_service_factory.h" #import "ios/chrome/browser/signin/model/identity_manager_factory.h" @@ -214,7 +216,7 @@ ChromeAutofillClientIOS::GetPaymentsAutofillClient() { if (!payments_autofill_client_) { payments_autofill_client_ = - std::make_unique<payments::IOSChromePaymentsAutofillClient>(); + std::make_unique<payments::IOSChromePaymentsAutofillClient>(this); } return payments_autofill_client_.get(); @@ -514,6 +516,17 @@ [bridge_ hideAutofillPopup]; } +void ChromeAutofillClientIOS::ShowAutofillErrorDialog( + AutofillErrorDialogContext context) { + // TODO(b/324609813): Move this function from ChromeAutofillClientIOS to + // IOSChromePaymentsAutofillClient to avoid this call. + if (!payments_autofill_client_) { + payments_autofill_client_ = + std::make_unique<payments::IOSChromePaymentsAutofillClient>(this); + } + payments_autofill_client_->ShowAutofillErrorDialog(std::move(context)); +} + bool ChromeAutofillClientIOS::IsAutocompleteEnabled() const { return prefs::IsAutocompleteEnabled(GetPrefs()); }
diff --git a/ios/chrome/browser/ui/autofill/ios_chrome_payments_autofill_client.h b/ios/chrome/browser/ui/autofill/ios_chrome_payments_autofill_client.h index be4cee0..0d2b89e 100644 --- a/ios/chrome/browser/ui/autofill/ios_chrome_payments_autofill_client.h +++ b/ios/chrome/browser/ui/autofill/ios_chrome_payments_autofill_client.h
@@ -7,14 +7,23 @@ #include "components/autofill/core/browser/payments/payments_autofill_client.h" -namespace autofill::payments { +#include "base/memory/raw_ptr.h" +#include "base/memory/weak_ptr.h" + +namespace autofill { + +class ChromeAutofillClientIOS; +struct AutofillErrorDialogContext; + +namespace payments { // Chrome iOS implementation of PaymentsAutofillClient. Owned by the // ChromeAutofillClientIOS. Created lazily in the ChromeAutofillClientIOS when // it is needed. class IOSChromePaymentsAutofillClient : public PaymentsAutofillClient { public: - IOSChromePaymentsAutofillClient(); + explicit IOSChromePaymentsAutofillClient( + autofill::ChromeAutofillClientIOS* client); IOSChromePaymentsAutofillClient(const IOSChromePaymentsAutofillClient&) = delete; IOSChromePaymentsAutofillClient& operator=( @@ -27,8 +36,15 @@ // PaymentsAutofillClient: void CreditCardUploadCompleted(bool card_saved) override; + + void ShowAutofillErrorDialog(AutofillErrorDialogContext error_context); + + private: + const raw_ref<autofill::ChromeAutofillClientIOS> client_; }; -} // namespace autofill::payments +} // namespace payments -#endif // IOS_CHROME_BROWSER_UI_AUTOFILL_IOS_CHROME_PAYMENTS_AUTOFILL_CLIENT_H_ +} // namespace autofill + +#endif // IOS_CHROME_BROWSER_UI_AUTOFILL_IOS_CHROME_PAYMENTS_AUTOFILL_CLIENT_H_
diff --git a/ios/chrome/browser/ui/autofill/ios_chrome_payments_autofill_client.mm b/ios/chrome/browser/ui/autofill/ios_chrome_payments_autofill_client.mm index b53fa9b..9d177d1 100644 --- a/ios/chrome/browser/ui/autofill/ios_chrome_payments_autofill_client.mm +++ b/ios/chrome/browser/ui/autofill/ios_chrome_payments_autofill_client.mm
@@ -4,12 +4,19 @@ #import "ios/chrome/browser/ui/autofill/ios_chrome_payments_autofill_client.h" +#import "base/check_deref.h" +#import "base/memory/weak_ptr.h" #import "base/strings/sys_string_conversions.h" +#import "components/autofill/core/browser/payments/autofill_error_dialog_context.h" +#import "ios/chrome/browser/shared/public/commands/autofill_commands.h" +#import "ios/chrome/browser/ui/autofill/chrome_autofill_client_ios.h" #import "ios/public/provider/chrome/browser/risk_data/risk_data_api.h" namespace autofill::payments { -IOSChromePaymentsAutofillClient::IOSChromePaymentsAutofillClient() = default; +IOSChromePaymentsAutofillClient::IOSChromePaymentsAutofillClient( + autofill::ChromeAutofillClientIOS* client) + : client_(CHECK_DEREF(client)) {} IOSChromePaymentsAutofillClient::~IOSChromePaymentsAutofillClient() = default; @@ -24,4 +31,10 @@ NOTIMPLEMENTED(); } +void IOSChromePaymentsAutofillClient::ShowAutofillErrorDialog( + AutofillErrorDialogContext error_context) { + [client_->commands_handler() + showAutofillErrorDialog:std::move(error_context)]; +} + } // namespace autofill::payments
diff --git a/ios/chrome/browser/ui/browser_view/browser_coordinator.mm b/ios/chrome/browser/ui/browser_view/browser_coordinator.mm index 95ed2ca..a8e27ed 100644 --- a/ios/chrome/browser/ui/browser_view/browser_coordinator.mm +++ b/ios/chrome/browser/ui/browser_view/browser_coordinator.mm
@@ -10,9 +10,11 @@ #import <optional> #import "base/memory/raw_ptr.h" +#import "base/memory/weak_ptr.h" #import "base/metrics/histogram_functions.h" #import "base/scoped_observation.h" #import "base/strings/sys_string_conversions.h" +#import "components/autofill/core/browser/payments/autofill_error_dialog_context.h" #import "components/commerce/core/shopping_service.h" #import "components/content_settings/core/browser/host_content_settings_map.h" #import "components/feature_engagement/public/event_constants.h" @@ -76,7 +78,7 @@ #import "ios/chrome/browser/shared/model/url/chrome_url_constants.h" #import "ios/chrome/browser/shared/public/commands/activity_service_commands.h" #import "ios/chrome/browser/shared/public/commands/application_commands.h" -#import "ios/chrome/browser/shared/public/commands/autofill_bottom_sheet_commands.h" +#import "ios/chrome/browser/shared/public/commands/autofill_commands.h" #import "ios/chrome/browser/shared/public/commands/browser_coordinator_commands.h" #import "ios/chrome/browser/shared/public/commands/command_dispatcher.h" #import "ios/chrome/browser/shared/public/commands/feed_commands.h" @@ -717,6 +719,8 @@ [self.virtualCardEnrollmentBottomSheetCoordinator stop]; self.virtualCardEnrollmentBottomSheetCoordinator = nil; + [self dismissAutofillErrorDialog]; + [_sendTabToSelfCoordinator stop]; _sendTabToSelfCoordinator = nil; @@ -862,7 +866,7 @@ // handlers. NSArray<Protocol*>* protocols = @[ @protocol(ActivityServiceCommands), - @protocol(AutofillBottomSheetCommands), + @protocol(AutofillCommands), @protocol(BrowserCoordinatorCommands), @protocol(DefaultBrowserPromoNonModalCommands), @protocol(FeedCommands), @@ -1370,6 +1374,8 @@ [self.virtualCardEnrollmentBottomSheetCoordinator stop]; self.virtualCardEnrollmentBottomSheetCoordinator = nil; + [self dismissAutofillErrorDialog]; + [self.printCoordinator stop]; self.printCoordinator = nil; @@ -1612,7 +1618,7 @@ [self.sharingCoordinator start]; } -#pragma mark - AutofillBottomSheetCommands +#pragma mark - AutofillCommands - (void)showPasswordBottomSheet:(const autofill::FormActivityParams&)params { self.passwordSuggestionBottomSheetCoordinator = @@ -1663,6 +1669,13 @@ [self.virtualCardEnrollmentBottomSheetCoordinator start]; } +- (void)showAutofillErrorDialog: + (autofill::AutofillErrorDialogContext)errorContext { +} + +- (void)dismissAutofillErrorDialog { +} + #pragma mark - BrowserCoordinatorCommands - (void)printTabWithBaseViewController:(UIViewController*)baseViewController {
diff --git a/ios/chrome/browser/ui/browser_view/tab_lifecycle_mediator.mm b/ios/chrome/browser/ui/browser_view/tab_lifecycle_mediator.mm index 0514a26..81e984c 100644 --- a/ios/chrome/browser/ui/browser_view/tab_lifecycle_mediator.mm +++ b/ios/chrome/browser/ui/browser_view/tab_lifecycle_mediator.mm
@@ -23,7 +23,7 @@ #import "ios/chrome/browser/prerender/model/prerender_service.h" #import "ios/chrome/browser/shared/model/application_context/application_context.h" #import "ios/chrome/browser/shared/model/web_state_list/web_state_list.h" -#import "ios/chrome/browser/shared/public/commands/autofill_bottom_sheet_commands.h" +#import "ios/chrome/browser/shared/public/commands/autofill_commands.h" #import "ios/chrome/browser/shared/public/commands/command_dispatcher.h" #import "ios/chrome/browser/shared/public/commands/lens_commands.h" #import "ios/chrome/browser/shared/public/commands/mini_map_commands.h" @@ -94,7 +94,7 @@ AutofillBottomSheetTabHelper* bottomSheetTabHelper = AutofillBottomSheetTabHelper::FromWebState(webState); bottomSheetTabHelper->SetAutofillBottomSheetHandler( - HandlerForProtocol(_commandDispatcher, AutofillBottomSheetCommands)); + HandlerForProtocol(_commandDispatcher, AutofillCommands)); if (ios::provider::IsLensSupported()) { LensTabHelper* lensTabHelper = LensTabHelper::FromWebState(webState); @@ -123,8 +123,12 @@ webContentsHandler); DCHECK(_baseViewController); - AutofillTabHelper::FromWebState(webState)->SetBaseViewController( - _baseViewController); + AutofillTabHelper* autofillTabHelper = + AutofillTabHelper::FromWebState(webState); + autofillTabHelper->SetBaseViewController(_baseViewController); + id<AutofillCommands> autofillHandler = + HandlerForProtocol(_commandDispatcher, AutofillCommands); + autofillTabHelper->SetCommandsHandler(autofillHandler); DCHECK(_printCoordinator); PrintTabHelper::FromWebState(webState)->set_printer(_printCoordinator); @@ -199,7 +203,10 @@ NetExportTabHelper::FromWebState(webState)->SetDelegate(nil); - AutofillTabHelper::FromWebState(webState)->SetBaseViewController(nil); + AutofillTabHelper* autofillTabHelper = + AutofillTabHelper::FromWebState(webState); + autofillTabHelper->SetBaseViewController(nil); + autofillTabHelper->SetCommandsHandler(nil); PrintTabHelper::FromWebState(webState)->set_printer(nil);
diff --git a/ios/chrome/browser/ui/settings/password/password_details/password_details_mediator.mm b/ios/chrome/browser/ui/settings/password/password_details/password_details_mediator.mm index 11a18b4..ab33c9a 100644 --- a/ios/chrome/browser/ui/settings/password/password_details/password_details_mediator.mm +++ b/ios/chrome/browser/ui/settings/password/password_details/password_details_mediator.mm
@@ -232,7 +232,7 @@ return; } - // Use the iterator before base::Erase() makes it invalid. + // Use the iterator before std::erase() makes it invalid. self.savedPasswordsPresenter->RemoveCredential(*it); // TODO(crbug.com/1359392). Once kPasswordsGrouping launches, the mediator // should update the passwords model and receive the updates via @@ -241,7 +241,7 @@ // flag is disabled and the password is edited, it's impossible to identify // the new object to show (sign-on realm can't be used as an id, there might // be multiple credentials; nor username/password since the values changed). - base::Erase(_credentials, *it); + std::erase(_credentials, *it); [self providePasswordsToConsumer]; // Update form managers so the list of password suggestions shown to the user @@ -420,7 +420,7 @@ CHECK(self.credentials.size() == 1); password_manager::CredentialUIEntry credential = self.credentials[0]; _manager->UnmuteCredential(credential); - base::Erase(_credentials, credential); + std::erase(_credentials, credential); [self providePasswordsToConsumer]; }
diff --git a/ios/chrome/browser/ui/settings/password/password_settings/password_settings_mediator.mm b/ios/chrome/browser/ui/settings/password/password_settings/password_settings_mediator.mm index 417a3e4..24657af 100644 --- a/ios/chrome/browser/ui/settings/password/password_settings/password_settings_mediator.mm +++ b/ios/chrome/browser/ui/settings/password/password_settings/password_settings_mediator.mm
@@ -215,7 +215,7 @@ // example.com"), so those must be filtered before passing to the exporter. std::vector<CredentialUIEntry> passwords = _savedPasswordsPresenter->GetSavedCredentials(); - base::EraseIf(passwords, [](const auto& credential) { + std::erase_if(passwords, [](const auto& credential) { return credential.blocked_by_user; }); [self.passwordExporter startExportFlow:passwords];
diff --git a/ios/chrome/test/data/policy/policy_test_bundle_data.filelist b/ios/chrome/test/data/policy/policy_test_bundle_data.filelist index 2d597b07..333bc2e9 100644 --- a/ios/chrome/test/data/policy/policy_test_bundle_data.filelist +++ b/ios/chrome/test/data/policy/policy_test_bundle_data.filelist
@@ -59,6 +59,7 @@ //ios/chrome/test/data/policy/pref_mapping/PopupsAllowedForUrls.json //ios/chrome/test/data/policy/pref_mapping/PopupsBlockedForUrls.json //ios/chrome/test/data/policy/pref_mapping/PrintingEnabled.json +//ios/chrome/test/data/policy/pref_mapping/ProductSpecificationsEnabled.json //ios/chrome/test/data/policy/pref_mapping/RestrictAccountsToPatterns.json //ios/chrome/test/data/policy/pref_mapping/SafeBrowsingEnabled.json //ios/chrome/test/data/policy/pref_mapping/SafeBrowsingProtectionLevel.json
diff --git a/ios/chrome/test/data/policy/pref_mapping/ProductSpecificationsEnabled.json b/ios/chrome/test/data/policy/pref_mapping/ProductSpecificationsEnabled.json new file mode 100644 index 0000000..3628605 --- /dev/null +++ b/ios/chrome/test/data/policy/pref_mapping/ProductSpecificationsEnabled.json
@@ -0,0 +1,40 @@ +[ + { + "os": [ + "ios" + ], + "policy_pref_mapping_tests": [ + { + "policies": {}, + "prefs": { + "product_specifications_enabled": { + "default_value": true, + "location": "user_profile" + } + } + }, + { + "policies": { + "ProductSpecificationsEnabled": true + }, + "prefs": { + "product_specifications_enabled": { + "value": true, + "location": "user_profile" + } + } + }, + { + "policies": { + "ProductSpecificationsEnabled": false + }, + "prefs": { + "product_specifications_enabled": { + "value": false, + "location": "user_profile" + } + } + } + ] + } +]
diff --git a/ios/public/provider/chrome/browser/user_feedback/user_feedback_sender.h b/ios/public/provider/chrome/browser/user_feedback/user_feedback_sender.h index a8eb3fca..7021938 100644 --- a/ios/public/provider/chrome/browser/user_feedback/user_feedback_sender.h +++ b/ios/public/provider/chrome/browser/user_feedback/user_feedback_sender.h
@@ -21,6 +21,8 @@ ParcelTracking, // Sent from Unit Conversion. UnitConversion, + // Sent from a content notification. + ContentNotification, }; #endif // IOS_PUBLIC_PROVIDER_CHROME_BROWSER_USER_FEEDBACK_USER_FEEDBACK_SENDER_H_
diff --git a/ios/web/js_features/context_menu/context_menu_java_script_feature.h b/ios/web/js_features/context_menu/context_menu_java_script_feature.h index 4fb5ba7..340b5f9 100644 --- a/ios/web/js_features/context_menu/context_menu_java_script_feature.h +++ b/ios/web/js_features/context_menu/context_menu_java_script_feature.h
@@ -40,6 +40,7 @@ void GetElementAtPoint(WebState* web_state, std::string requestID, CGPoint point, + CGSize web_content_size, ElementDetailsCallback callback); // JavaScriptFeature:
diff --git a/ios/web/js_features/context_menu/context_menu_java_script_feature.mm b/ios/web/js_features/context_menu/context_menu_java_script_feature.mm index 30e66f1..fb62843b 100644 --- a/ios/web/js_features/context_menu/context_menu_java_script_feature.mm +++ b/ios/web/js_features/context_menu/context_menu_java_script_feature.mm
@@ -60,6 +60,7 @@ WebState* web_state, std::string requestID, CGPoint point, + CGSize web_content_size, ElementDetailsCallback callback) { callbacks_[requestID] = std::move(callback); @@ -68,6 +69,8 @@ parameters.Append(requestID); parameters.Append(point.x); parameters.Append(point.y); + parameters.Append(web_content_size.width); + parameters.Append(web_content_size.height); CallJavaScriptFunction(main_frame, "contextMenu.findElementAtPoint", parameters); }
diff --git a/ios/web/js_features/context_menu/context_menu_java_script_feature_unittest.mm b/ios/web/js_features/context_menu/context_menu_java_script_feature_unittest.mm index de907e05..b3f0406 100644 --- a/ios/web/js_features/context_menu/context_menu_java_script_feature_unittest.mm +++ b/ios/web/js_features/context_menu/context_menu_java_script_feature_unittest.mm
@@ -34,6 +34,7 @@ ContextMenuJavaScriptFeature::FromBrowserState(GetBrowserState()) ->GetElementAtPoint( web_state(), request_id, CGPointMake(10.0, 10.0), + CGSizeMake(100.0, 100.0), base::BindOnce(^(const std::string& callback_request_id, const web::ContextMenuParams& params) { EXPECT_EQ(request_id, callback_request_id); @@ -67,6 +68,7 @@ ContextMenuJavaScriptFeature::FromBrowserState(GetBrowserState()) ->GetElementAtPoint( web_state(), request_id, CGPointMake(10.0, 10.0), + CGSizeMake(100.0, 100.0), base::BindOnce(^(const std::string& callback_request_id, const web::ContextMenuParams& params) { EXPECT_EQ(request_id, callback_request_id);
diff --git a/ios/web/js_features/context_menu/context_menu_js_unittest.mm b/ios/web/js_features/context_menu/context_menu_js_unittest.mm index b62ee32..acc8abe 100644 --- a/ios/web/js_features/context_menu/context_menu_js_unittest.mm +++ b/ios/web/js_features/context_menu/context_menu_js_unittest.mm
@@ -37,6 +37,10 @@ // The base url for loaded web pages. const char kTestUrl[] = "https://chromium.test/"; +// A point in the web view's coordinate space on the link returned by +// `GetHtmlForLink()`. +const CGPoint kPointOnLink = {5.0, 2.0}; + // A point in the web view's coordinate space on the image returned by // `GetHtmlForImage()`. const CGPoint kPointOnImage = {50.0, 10.0}; @@ -100,22 +104,14 @@ head ? head : @"", body]; } -// Returns HTML for a link to `href`, with `text`, `id` and inline `style`. -NSString* GetHtmlForLink(const char* id, - const char* href, - const char* text, - const char* style) { - std::string style_attribute = - style ? base::StringPrintf("style=\"%s\" ", style) : ""; - return [NSString stringWithFormat:@"<a id=\"%s\" %shref=\"%s\">%s</a>", id, - style_attribute.c_str(), href, text]; -} - // Returns HTML for a link to `href`, display `text`, and inline `style`. NSString* GetHtmlForLink(const char* href, const char* text, const char* style) { - return GetHtmlForLink("link", href, text, style); + std::string style_attribute = + style ? base::StringPrintf("style=\"%s\" ", style) : ""; + return [NSString stringWithFormat:@"<a %shref=\"%s\">%s</a>", + style_attribute.c_str(), href, text]; } // Returns HTML for an SVG shape which links to `href`. @@ -300,11 +296,11 @@ // Executes __gCrWeb.findElementAtPoint script with the given `point` in the // web view viewport's coordinate space. id ExecuteFindElementFromPointJavaScript(CGPoint point) { - CGFloat scale = web_view().scrollView.zoomScale; + CGSize size = GetWebViewContentSize(); NSString* script = [NSString stringWithFormat:@"__gCrWeb.contextMenu.findElementAtPoint('%" - @"s', %g, %g)", - kRequestId, point.x / scale, point.y / scale]; + @"s', %g, %g, %g, %g)", + kRequestId, point.x, point.y, size.width, size.height]; return web::test::ExecuteJavaScript(web_view(), script); } @@ -321,9 +317,7 @@ elementId]; NSDictionary* body = web::test::ExecuteJavaScript(web_view(), script); - return CGPointMake( - [body[@"x"] floatValue] * web_view().scrollView.zoomScale, - [body[@"y"] floatValue] * web_view().scrollView.zoomScale); + return CGPointMake([body[@"x"] floatValue], [body[@"y"] floatValue]); } // Handles script message responses sent from `web_view()`. @@ -922,7 +916,7 @@ .Set(kContextMenuElementHyperlink, link) .Set(kContextMenuElementTagName, "a"); - CheckElementResult(@"link", expected_value); + CheckElementResult(kPointOnLink, expected_value); } // Tests that a callout information about a link is displayed when @@ -943,7 +937,7 @@ .Set(kContextMenuElementHyperlink, link) .Set(kContextMenuElementTagName, "a"); - CheckElementResult(@"link", expected_value); + CheckElementResult(kPointOnLink, expected_value); } // Tests that no callout information about a link is displayed when @@ -965,7 +959,7 @@ ignored_keys.push_back(kContextMenuElementTextOffset); ignored_keys.push_back(kContextMenuElementSurroundingTextOffset); - CheckElementResult(@"link", expected_value, ignored_keys); + CheckElementResult(kPointOnLink, expected_value, ignored_keys); } // Tests that -webkit-touch-callout property can be inherited from ancester @@ -986,7 +980,7 @@ ignored_keys.push_back(kContextMenuElementTextOffset); ignored_keys.push_back(kContextMenuElementSurroundingTextOffset); - CheckElementResult(@"link", expected_value, ignored_keys); + CheckElementResult(kPointOnLink, expected_value, ignored_keys); } // Tests that setting -webkit-touch-callout property can override the value @@ -1008,7 +1002,7 @@ .Set(kContextMenuElementHyperlink, link) .Set(kContextMenuElementTagName, "a"); - CheckElementResult(@"link", expected_value); + CheckElementResult(kPointOnLink, expected_value); } } // namespace web
diff --git a/ios/web/js_features/context_menu/resources/main_frame_context_menu.ts b/ios/web/js_features/context_menu/resources/main_frame_context_menu.ts index d8b25c4..4b076cf5 100644 --- a/ios/web/js_features/context_menu/resources/main_frame_context_menu.ts +++ b/ios/web/js_features/context_menu/resources/main_frame_context_menu.ts
@@ -21,9 +21,24 @@ * @param y - vertical center of the selected point in web view * coordinates. */ -function findElementAtPoint(requestId: string, x: number, y: number) { +function findElementAtPoint( + requestId: string, x: number, y: number, webViewWidth: number, + _webViewHeight: number) { + const scale = getPageWidth() / webViewWidth; gCrWeb.contextMenuAllFrames.findElementAtPointInPageCoordinates( - requestId, x, y); + requestId, x * scale, y * scale); +} + +/** + * Returns maximum width of the web page. + */ +function getPageWidth(): number { + const documentElement = document.documentElement; + const documentBody = document.body; + return Math.max( + documentElement.clientWidth, documentElement.scrollWidth, + documentElement.offsetWidth, documentBody.scrollWidth, + documentBody.offsetWidth); } gCrWeb.contextMenu = {findElementAtPoint};
diff --git a/ios/web/web_state/ui/crw_context_menu_controller.mm b/ios/web/web_state/ui/crw_context_menu_controller.mm index 0ad4f5ee..2f3a798 100644 --- a/ios/web/web_state/ui/crw_context_menu_controller.mm +++ b/ios/web/web_state/ui/crw_context_menu_controller.mm
@@ -94,9 +94,6 @@ CGPoint locationInWebView = [self.webView.scrollView convertPoint:location fromView:interaction.view]; - locationInWebView.x /= self.webView.scrollView.zoomScale; - locationInWebView.y /= self.webView.scrollView.zoomScale; - std::optional<web::ContextMenuParams> optionalParams = [self fetchContextMenuParamsAtLocation:locationInWebView];
diff --git a/ios/web/web_state/ui/crw_context_menu_element_fetcher.mm b/ios/web/web_state/ui/crw_context_menu_element_fetcher.mm index 33b6705..f42eb4f 100644 --- a/ios/web/web_state/ui/crw_context_menu_element_fetcher.mm +++ b/ios/web/web_state/ui/crw_context_menu_element_fetcher.mm
@@ -76,7 +76,7 @@ __weak __typeof(self) weakSelf = self; context_menu_feature->GetElementAtPoint( - self.webState, requestID, point, + self.webState, requestID, point, self.webView.scrollView.contentSize, base::BindOnce(^(const std::string& innerRequestID, const web::ContextMenuParams& params) { web::ContextMenuParams context_menu_params(params);
diff --git a/ios_internal b/ios_internal index 4f50ee5..e8610f3 160000 --- a/ios_internal +++ b/ios_internal
@@ -1 +1 @@ -Subproject commit 4f50ee554da55ee6d7c1382a8b06233257b9ee81 +Subproject commit e8610f3ba765ba96d259acd21b689db6e0cbd181
diff --git a/media/base/frame_buffer_pool.cc b/media/base/frame_buffer_pool.cc index 9884126..457784a 100644 --- a/media/base/frame_buffer_pool.cc +++ b/media/base/frame_buffer_pool.cc
@@ -6,8 +6,9 @@ #include "base/logging.h" +#include <vector> + #include "base/check_op.h" -#include "base/containers/cxx20_erase.h" #include "base/functional/bind.h" #include "base/functional/callback_helpers.h" #include "base/location.h" @@ -243,7 +244,7 @@ void FrameBufferPool::EraseUnusedResourcesLocked() { lock_.AssertAcquired(); - base::EraseIf(frame_buffers_, [](const std::unique_ptr<FrameBuffer>& buf) { + std::erase_if(frame_buffers_, [](const std::unique_ptr<FrameBuffer>& buf) { return !IsUsedLocked(buf.get()); }); } @@ -264,7 +265,7 @@ frame_buffer->last_use_time = now; } - base::EraseIf(frame_buffers_, [now](const std::unique_ptr<FrameBuffer>& buf) { + std::erase_if(frame_buffers_, [now](const std::unique_ptr<FrameBuffer>& buf) { return !IsUsedLocked(buf.get()) && now - buf->last_use_time > base::Seconds(kStaleFrameLimitSecs); });
diff --git a/media/base/frame_buffer_pool.h b/media/base/frame_buffer_pool.h index 3c989d7..353b9a1 100644 --- a/media/base/frame_buffer_pool.h +++ b/media/base/frame_buffer_pool.h
@@ -36,6 +36,10 @@ // Called when a frame buffer allocation is needed. Upon return |fb_priv| will // be set to a private value used to identify the buffer in future calls and a // buffer of at least |min_size| will be returned. + // + // WARNING: To release the FrameBuffer, clients must either call Shutdown() or + // ReleaseFrameBuffer() in addition to any callbacks returned by + // CreateFrameCallback() (if any are created). uint8_t* GetFrameBuffer(size_t min_size, void** fb_priv); // Called when a frame buffer allocation is no longer needed.
diff --git a/media/base/video_frame_converter.cc b/media/base/video_frame_converter.cc index b75b435..22e5c5c 100644 --- a/media/base/video_frame_converter.cc +++ b/media/base/video_frame_converter.cc
@@ -104,7 +104,7 @@ if (tmp_frame) { tmp_frame->AddDestructionObserver(frame_pool_->CreateFrameCallback(fb_id)); } - + frame_pool_->ReleaseFrameBuffer(fb_id); return tmp_frame; } @@ -155,7 +155,7 @@ wrapped_frame->AddDestructionObserver( frame_pool_->CreateFrameCallback(fb_id)); } - + frame_pool_->ReleaseFrameBuffer(fb_id); return wrapped_frame; }
diff --git a/media/base/video_frame_converter.h b/media/base/video_frame_converter.h index 87b6e03e..9f2ccd4 100644 --- a/media/base/video_frame_converter.h +++ b/media/base/video_frame_converter.h
@@ -45,6 +45,10 @@ EncoderStatus ConvertAndScale(const VideoFrame& src_frame, VideoFrame& dest_frame); + size_t get_pool_size_for_testing() const { + return frame_pool_->get_pool_size_for_testing(); + } + private: // Creates a temporary frame backed by `frame_pool_`. scoped_refptr<VideoFrame> CreateTempFrame(VideoPixelFormat format,
diff --git a/media/base/video_frame_converter_unittest.cc b/media/base/video_frame_converter_unittest.cc index adc160a..ee84d7e 100644 --- a/media/base/video_frame_converter_unittest.cc +++ b/media/base/video_frame_converter_unittest.cc
@@ -165,6 +165,14 @@ EXPECT_DOUBLE_EQ(ssim, 1.0); EXPECT_EQ(psnr, libyuv::kMaxPsnr); } + + // Ensure memory pool is functioning correctly by running conversions which + // use scratch space twice. + if (converter_.get_pool_size_for_testing() > 0) { + EXPECT_EQ(converter_.get_pool_size_for_testing(), 1u); + ASSERT_TRUE(converter_.ConvertAndScale(*src_frame, *dest_frame).is_ok()); + EXPECT_EQ(converter_.get_pool_size_for_testing(), 1u); + } } INSTANTIATE_TEST_SUITE_P(,
diff --git a/media/gpu/android/maybe_render_early_manager.cc b/media/gpu/android/maybe_render_early_manager.cc index 7de5cd5..67085360 100644 --- a/media/gpu/android/maybe_render_early_manager.cc +++ b/media/gpu/android/maybe_render_early_manager.cc
@@ -4,8 +4,9 @@ #include "media/gpu/android/maybe_render_early_manager.h" +#include <vector> + #include "base/containers/contains.h" -#include "base/containers/cxx20_erase.h" #include "base/memory/raw_ptr.h" #include "base/memory/weak_ptr.h" #include "base/task/sequenced_task_runner.h" @@ -56,7 +57,7 @@ DCHECK(base::Contains(images_, image)); // Remember that |image_group_| might not be the same one that |image| // belongs to. - base::Erase(images_, image); + std::erase(images_, image); internal::MaybeRenderEarly(&images_); }
diff --git a/media/gpu/chromeos/video_decoder_pipeline.cc b/media/gpu/chromeos/video_decoder_pipeline.cc index 3af7a5f..d28fe13 100644 --- a/media/gpu/chromeos/video_decoder_pipeline.cc +++ b/media/gpu/chromeos/video_decoder_pipeline.cc
@@ -6,6 +6,7 @@ #include <memory> #include <optional> +#include <vector> #include "base/containers/contains.h" #include "base/feature_list.h" @@ -290,35 +291,35 @@ return std::nullopt; if (workarounds.disable_accelerated_vp8_decode) { - base::EraseIf(configs.value(), [](const auto& config) { + std::erase_if(configs.value(), [](const auto& config) { return config.profile_min >= VP8PROFILE_MIN && config.profile_max <= VP8PROFILE_MAX; }); } if (workarounds.disable_accelerated_vp9_decode) { - base::EraseIf(configs.value(), [](const auto& config) { + std::erase_if(configs.value(), [](const auto& config) { return config.profile_min >= VP9PROFILE_PROFILE0 && config.profile_max <= VP9PROFILE_PROFILE0; }); } if (workarounds.disable_accelerated_vp9_profile2_decode) { - base::EraseIf(configs.value(), [](const auto& config) { + std::erase_if(configs.value(), [](const auto& config) { return config.profile_min >= VP9PROFILE_PROFILE2 && config.profile_max <= VP9PROFILE_PROFILE2; }); } if (workarounds.disable_accelerated_h264_decode) { - base::EraseIf(configs.value(), [](const auto& config) { + std::erase_if(configs.value(), [](const auto& config) { return config.profile_min >= H264PROFILE_MIN && config.profile_max <= H264PROFILE_MAX; }); } if (workarounds.disable_accelerated_hevc_decode) { - base::EraseIf(configs.value(), [](const auto& config) { + std::erase_if(configs.value(), [](const auto& config) { return config.profile_min >= HEVCPROFILE_MIN && config.profile_max <= HEVCPROFILE_MAX; });
diff --git a/media/gpu/gpu_video_encode_accelerator_factory.cc b/media/gpu/gpu_video_encode_accelerator_factory.cc index f6c262c..daede9e 100644 --- a/media/gpu/gpu_video_encode_accelerator_factory.cc +++ b/media/gpu/gpu_video_encode_accelerator_factory.cc
@@ -8,7 +8,6 @@ #include <vector> #include "base/command_line.h" -#include "base/containers/cxx20_erase.h" #include "base/feature_list.h" #include "base/functional/bind.h" #include "base/memory/ptr_util.h" @@ -222,27 +221,27 @@ #endif if (gpu_workarounds.disable_accelerated_av1_encode) { - base::EraseIf(profiles, [](const auto& vea_profile) { + std::erase_if(profiles, [](const auto& vea_profile) { return vea_profile.profile >= AV1PROFILE_PROFILE_MAIN && vea_profile.profile <= AV1PROFILE_PROFILE_PRO; }); } if (gpu_workarounds.disable_accelerated_vp8_encode) { - base::EraseIf(profiles, [](const auto& vea_profile) { + std::erase_if(profiles, [](const auto& vea_profile) { return vea_profile.profile == VP8PROFILE_ANY; }); } if (gpu_workarounds.disable_accelerated_vp9_encode) { - base::EraseIf(profiles, [](const auto& vea_profile) { + std::erase_if(profiles, [](const auto& vea_profile) { return vea_profile.profile >= VP9PROFILE_PROFILE0 && vea_profile.profile <= VP9PROFILE_PROFILE3; }); } if (gpu_workarounds.disable_accelerated_h264_encode) { - base::EraseIf(profiles, [](const auto& vea_profile) { + std::erase_if(profiles, [](const auto& vea_profile) { return vea_profile.profile >= H264PROFILE_MIN && vea_profile.profile <= H264PROFILE_MAX; });
diff --git a/media/gpu/mac/video_toolbox_decompression_metadata.h b/media/gpu/mac/video_toolbox_decompression_metadata.h index e579ff88..b42fdee 100644 --- a/media/gpu/mac/video_toolbox_decompression_metadata.h +++ b/media/gpu/mac/video_toolbox_decompression_metadata.h
@@ -45,6 +45,9 @@ gfx::ColorSpace color_space; std::optional<gfx::HDRMetadata> hdr_metadata; + // The frame should be dropped after decoding. Used to implement Reset(). + bool discard = false; + // Session metadata is included in case the decoder needs to be reconfigured. // TODO(crbug.com/1331597): Pass separately, maybe even independently. VideoToolboxDecompressionSessionMetadata session_metadata;
diff --git a/media/gpu/mac/video_toolbox_decompression_session_manager.cc b/media/gpu/mac/video_toolbox_decompression_session_manager.cc index 6ea5d9c1..f2135b4 100644 --- a/media/gpu/mac/video_toolbox_decompression_session_manager.cc +++ b/media/gpu/mac/video_toolbox_decompression_session_manager.cc
@@ -66,12 +66,17 @@ pending_decodes_ = {}; - DestroySession(); + for (auto& it : active_decodes_) { + it.second->discard = true; + } + + draining_ = false; } size_t VideoToolboxDecompressionSessionManager::NumDecodes() { DVLOG(4) << __func__; DCHECK(task_runner_->RunsTasksInCurrentSequence()); + // Note: This counts frames that will be discarded due to a Reset(). return pending_decodes_.size() + active_decodes_.size(); } @@ -80,9 +85,9 @@ DCHECK(task_runner_->RunsTasksInCurrentSequence()); DCHECK(!has_error_); - Reset(); - has_error_ = true; + pending_decodes_ = {}; + DestroySession(); // We may still be executing inside Decode() and don't want to make a // re-entrant call. @@ -176,7 +181,6 @@ decoder_config.get(), kVTVideoDecoderSpecification_EnableHardwareAcceleratedVideoDecoder, kCFBooleanTrue); - // TODO(crbug.com/1331597): Use session_metadata.allow_software_decoding. CFDictionarySetValue( decoder_config.get(), kVTVideoDecoderSpecification_RequireHardwareAcceleratedVideoDecoder, @@ -306,8 +310,10 @@ } } - // OnOutput() was posted, so this is never re-entrant. - output_cb_.Run(std::move(image), std::move(metadata)); + if (!metadata->discard) { + // OnOutput() was posted, so this is never re-entrant. + output_cb_.Run(std::move(image), std::move(metadata)); + } } void VideoToolboxDecompressionSessionManager::SetDecompressionSessionForTesting(
diff --git a/media/gpu/test/video_encoder/decoder_buffer_validator.cc b/media/gpu/test/video_encoder/decoder_buffer_validator.cc index 9319e7d..0ceb0a4 100644 --- a/media/gpu/test/video_encoder/decoder_buffer_validator.cc +++ b/media/gpu/test/video_encoder/decoder_buffer_validator.cc
@@ -4,9 +4,9 @@ #include "media/gpu/test/video_encoder/decoder_buffer_validator.h" #include <set> +#include <vector> #include "base/containers/contains.h" -#include "base/containers/cxx20_erase.h" #include "base/logging.h" #include "base/numerics/safe_conversions.h" #include "build/build_config.h" @@ -757,7 +757,7 @@ } } for (uint8_t p_diff : vp9.p_diffs) { - if (!base::Erase(expected_pdiffs, p_diff)) { + if (!std::erase(expected_pdiffs, p_diff)) { LOG(ERROR) << "Frame is referencing buffer not contained in the p_diff."; return false; @@ -925,7 +925,7 @@ expected_pdiffs.push_back(new_buffer_state.picture_id - ref.picture_id); } for (uint8_t p_diff : vp9.p_diffs) { - if (!base::Erase(expected_pdiffs, p_diff)) { + if (!std::erase(expected_pdiffs, p_diff)) { LOG(ERROR) << "Frame is referencing buffer not contained in the p_diff."; return false;
diff --git a/media/gpu/vaapi/vaapi_wrapper.cc b/media/gpu/vaapi/vaapi_wrapper.cc index 12d0768..184444f9 100644 --- a/media/gpu/vaapi/vaapi_wrapper.cc +++ b/media/gpu/vaapi/vaapi_wrapper.cc
@@ -19,9 +19,9 @@ #include <string> #include <type_traits> #include <utility> +#include <vector> #include "base/containers/contains.h" -#include "base/containers/cxx20_erase.h" #include "base/containers/fixed_flat_set.h" #include "base/cpu.h" #include "base/environment.h" @@ -3378,7 +3378,7 @@ DVLOG(2) << "Destroying " << va_surfaces.size() << " surfaces"; // vaDestroySurfaces() makes no guarantees about VA_INVALID_SURFACE. - base::Erase(va_surfaces, VA_INVALID_SURFACE); + std::erase(va_surfaces, VA_INVALID_SURFACE); if (va_surfaces.empty()) return;
diff --git a/media/renderers/video_resource_updater.cc b/media/renderers/video_resource_updater.cc index b87a4c9..a0cd5cb9 100644 --- a/media/renderers/video_resource_updater.cc +++ b/media/renderers/video_resource_updater.cc
@@ -8,10 +8,10 @@ #include <stdint.h> #include <string> +#include <vector> #include "base/atomic_sequence_num.h" #include "base/containers/contains.h" -#include "base/containers/cxx20_erase.h" #include "base/functional/bind.h" #include "base/logging.h" #include "base/memory/raw_ptr.h" @@ -1848,7 +1848,7 @@ subplane_si_format.value_or(output_si_format)) || !base::Contains(outplane_plane_sizes, resource->resource_size()); }; - base::EraseIf(all_resources_, can_delete_resource_fn); + std::erase_if(all_resources_, can_delete_resource_fn); // Recycle or allocate resources for each video plane. std::vector<PlaneResource*> plane_resources;
diff --git a/media/video/picture.h b/media/video/picture.h index 4541758b..c0a79a0d 100644 --- a/media/video/picture.h +++ b/media/video/picture.h
@@ -51,7 +51,7 @@ private: int32_t id_; gfx::Size size_; - uint32_t service_texture_id_; + uint32_t service_texture_id_ = 0; uint32_t texture_target_ = 0; VideoPixelFormat pixel_format_ = PIXEL_FORMAT_UNKNOWN; };
diff --git a/mojo/public/cpp/bindings/lib/interface_endpoint_client.cc b/mojo/public/cpp/bindings/lib/interface_endpoint_client.cc index 2826855..75f98ab 100644 --- a/mojo/public/cpp/bindings/lib/interface_endpoint_client.cc +++ b/mojo/public/cpp/bindings/lib/interface_endpoint_client.cc
@@ -9,10 +9,10 @@ #include <optional> #include <string_view> #include <tuple> +#include <vector> #include "base/check.h" #include "base/containers/contains.h" -#include "base/containers/cxx20_erase.h" #include "base/debug/alias.h" #include "base/functional/bind.h" #include "base/location.h" @@ -443,7 +443,7 @@ { base::AutoLock l(sync_calls->lock); - base::Erase(sync_calls->pending_responses, response.get()); + std::erase(sync_calls->pending_responses, response.get()); } if (response->received)
diff --git a/net/base/url_search_params.cc b/net/base/url_search_params.cc index 9215bc4..e20d42f 100644 --- a/net/base/url_search_params.cc +++ b/net/base/url_search_params.cc
@@ -10,7 +10,6 @@ #include <utility> #include <vector> -#include "base/containers/cxx20_erase_vector.h" #include "base/strings/utf_string_conversions.h" #include "net/base/url_util.h" #include "url/gurl.h" @@ -46,13 +45,13 @@ void UrlSearchParams::DeleteAllWithNames( const base::flat_set<std::string>& names) { - base::EraseIf(params_, + std::erase_if(params_, [&](const auto& pair) { return names.contains(pair.first); }); } void UrlSearchParams::DeleteAllExceptWithNames( const base::flat_set<std::string>& names) { - base::EraseIf(params_, + std::erase_if(params_, [&](const auto& pair) { return !names.contains(pair.first); }); }
diff --git a/net/cert/nss_cert_database_chromeos.cc b/net/cert/nss_cert_database_chromeos.cc index 7c68834..9e140a0a 100644 --- a/net/cert/nss_cert_database_chromeos.cc +++ b/net/cert/nss_cert_database_chromeos.cc
@@ -10,8 +10,8 @@ #include <algorithm> #include <memory> #include <utility> +#include <vector> -#include "base/containers/cxx20_erase.h" #include "base/functional/bind.h" #include "base/functional/callback.h" #include "base/location.h" @@ -73,7 +73,7 @@ NSSCertDatabase::ListModules(modules, need_rw); const NSSProfileFilterChromeOS& profile_filter = profile_filter_; - base::EraseIf(*modules, [&profile_filter](crypto::ScopedPK11Slot& module) { + std::erase_if(*modules, [&profile_filter](crypto::ScopedPK11Slot& module) { return !profile_filter.IsModuleAllowed(module.get()); }); } @@ -130,7 +130,7 @@ crypto::ScopedPK11Slot(), add_certs_info, nss_roots_handling)); // Filter certificate information according to user profile. - base::EraseIf(certs_info, [&profile_filter](CertInfo& cert_info) { + std::erase_if(certs_info, [&profile_filter](CertInfo& cert_info) { return !profile_filter.IsCertAllowed(cert_info.cert.get()); });
diff --git a/net/dns/address_sorter_posix.cc b/net/dns/address_sorter_posix.cc index 0f4afb7..6ff0704 100644 --- a/net/dns/address_sorter_posix.cc +++ b/net/dns/address_sorter_posix.cc
@@ -29,8 +29,8 @@ #include <netinet/in_var.h> #endif // BUILDFLAG(IS_IOS) #endif +#include <vector> -#include "base/containers/cxx20_erase_vector.h" #include "base/containers/unique_ptr_adapters.h" #include "base/logging.h" #include "net/base/ip_endpoint.h" @@ -326,7 +326,7 @@ info.src.prefix_length); } } - base::EraseIf(sort_list_, [](auto& element) { return element.failed; }); + std::erase_if(sort_list_, [](auto& element) { return element.failed; }); std::stable_sort(sort_list_.begin(), sort_list_.end(), CompareDestinations); std::vector<IPEndPoint> sorted_result;
diff --git a/net/quic/dedicated_web_transport_http3_client.cc b/net/quic/dedicated_web_transport_http3_client.cc index 96e7abe..248cbb0 100644 --- a/net/quic/dedicated_web_transport_http3_client.cc +++ b/net/quic/dedicated_web_transport_http3_client.cc
@@ -4,8 +4,9 @@ #include "net/quic/dedicated_web_transport_http3_client.h" +#include <vector> + #include "base/containers/contains.h" -#include "base/containers/cxx20_erase.h" #include "base/feature_list.h" #include "base/memory/raw_ptr.h" #include "base/metrics/field_trial_params.h" @@ -937,7 +938,7 @@ retried_with_new_version_ = true; DCHECK(original_supported_versions_.empty()); original_supported_versions_ = supported_versions_; - base::EraseIf( + std::erase_if( supported_versions_, [this](const quic::ParsedQuicVersion& version) { return !base::Contains( session_->connection()->server_supported_versions(), version);
diff --git a/remoting/host/linux/wayland_display.cc b/remoting/host/linux/wayland_display.cc index 76f5c1c..304c008 100644 --- a/remoting/host/linux/wayland_display.cc +++ b/remoting/host/linux/wayland_display.cc
@@ -4,8 +4,9 @@ #include "remoting/host/linux/wayland_display.h" +#include <vector> + #include "base/check.h" -#include "base/containers/cxx20_erase.h" #include "base/hash/hash.h" #include "remoting/base/logging.h" @@ -63,7 +64,7 @@ } bool WaylandDisplay::HandleGlobalRemoveDisplayEvent(uint32_t name) { - size_t num_removed = base::EraseIf( + size_t num_removed = std::erase_if( display_info_, [name](const auto& display_info) { return display_info.id == name; }); DCHECK(num_removed <= 1)
diff --git a/sandbox/policy/sandbox.cc b/sandbox/policy/sandbox.cc index 217e07c..b6495ae 100644 --- a/sandbox/policy/sandbox.cc +++ b/sandbox/policy/sandbox.cc
@@ -23,8 +23,7 @@ #endif // BUILDFLAG(IS_MAC) #if BUILDFLAG(IS_WIN) -#include "base/debug/alias.h" -#include "base/notreached.h" +#include "base/check_op.h" #include "base/process/process_info.h" #include "sandbox/policy/win/sandbox_win.h" #include "sandbox/win/src/sandbox.h" @@ -61,13 +60,9 @@ // will be broken. This has to run before threads and windows are created. ResultCode result = broker_services->CreateAlternateDesktop( Desktop::kAlternateWinstation); - if (result != SBOX_ALL_OK) { - // TODO(crbug.com/1396219) Gather some extra data when this fails. - DWORD gle = GetLastError(); - base::debug::Alias(&result); - base::debug::Alias(&gle); - NOTREACHED_NORETURN(); - } + // This failure is usually caused by third-party software or by the host + // system exhausting its desktop heap. + CHECK(result == SBOX_ALL_OK); } return true; }
diff --git a/sql/BUILD.gn b/sql/BUILD.gn index 5e46b88..02dae9c 100644 --- a/sql/BUILD.gn +++ b/sql/BUILD.gn
@@ -19,24 +19,6 @@ "internal_api_token.h", "meta_table.cc", "meta_table.h", - "recover_module/btree.cc", - "recover_module/btree.h", - "recover_module/cursor.cc", - "recover_module/cursor.h", - "recover_module/integers.cc", - "recover_module/integers.h", - "recover_module/module.cc", - "recover_module/module.h", - "recover_module/pager.cc", - "recover_module/pager.h", - "recover_module/parsing.cc", - "recover_module/parsing.h", - "recover_module/payload.cc", - "recover_module/payload.h", - "recover_module/record.cc", - "recover_module/record.h", - "recover_module/table.cc", - "recover_module/table.h", "recovery.cc", "recovery.h", "sandboxed_vfs.cc", @@ -114,7 +96,6 @@ "database_options_unittest.cc", "database_unittest.cc", "meta_table_unittest.cc", - "recover_module/module_unittest.cc", "recovery_unittest.cc", "sql_memory_dump_provider_unittest.cc", "sqlite_features_unittest.cc", @@ -143,10 +124,8 @@ ] } -# Why "built in"? Chromium used to have bespoke recovery code before SQLite -# had a built-in recovery module. See crbug.com/1513310 -fuzzer_test("sql_built_in_recovery_fuzzer") { - sources = [ "built_in_recovery_fuzzer.cc" ] +fuzzer_test("sql_recovery_fuzzer") { + sources = [ "recovery_fuzzer.cc" ] deps = [ ":sql", "//base", @@ -158,5 +137,5 @@ group("fuzzers") { testonly = true - deps = [ "//sql/fuzzers:sql_built_in_recovery_lpm_fuzzer" ] + deps = [ "//sql/fuzzers:sql_recovery_lpm_fuzzer" ] }
diff --git a/sql/fuzzers/BUILD.gn b/sql/fuzzers/BUILD.gn index fffddb2..9c0f489 100644 --- a/sql/fuzzers/BUILD.gn +++ b/sql/fuzzers/BUILD.gn
@@ -5,8 +5,8 @@ import("//testing/libfuzzer/fuzzer_test.gni") import("//third_party/libprotobuf-mutator/fuzzable_proto_library.gni") -fuzzer_test("sql_built_in_recovery_lpm_fuzzer") { - sources = [ "built_in_recovery_lpm_fuzzer.cc" ] +fuzzer_test("sql_recovery_lpm_fuzzer") { + sources = [ "recovery_lpm_fuzzer.cc" ] deps = [ ":sql_disk_corruption_proto", "//base",
diff --git a/sql/fuzzers/built_in_recovery_lpm_fuzzer.cc b/sql/fuzzers/recovery_lpm_fuzzer.cc similarity index 97% rename from sql/fuzzers/built_in_recovery_lpm_fuzzer.cc rename to sql/fuzzers/recovery_lpm_fuzzer.cc index 34c4ab7..fb9be6d 100644 --- a/sql/fuzzers/built_in_recovery_lpm_fuzzer.cc +++ b/sql/fuzzers/recovery_lpm_fuzzer.cc
@@ -2,9 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// This fuzzer exercises BuiltInRecovery like sql_built_in_recovery_fuzzer, but -// employs a different strategy for generating database files. Rather than -// directly interpreting the fuzzer input as a SQLite database file, this fuzzer +// This fuzzer exercises recovery like sql_recovery_fuzzer, but employs a +// different strategy for generating database files. Rather than directly +// interpreting the fuzzer input as a SQLite database file, this fuzzer // constructs a DB from fuzzer-derived SQL statements and then mutates the file // with fuzzer-derived XOR masks before exercising recovery.
diff --git a/sql/recover_module/README.md b/sql/recover_module/README.md deleted file mode 100644 index fb1e0d6..0000000 --- a/sql/recover_module/README.md +++ /dev/null
@@ -1,94 +0,0 @@ -# SQLite Data Recovery - -This directory implements data recovery heuristics for SQLite databases whose -files were corrupted on disk. The recovery code walks through the B-tree -holding a SQLite table and recovers all records that seem healthy. Even if -recovery succeeds, a recovered table may be missing records, and existing -records may have corrupted values inside them. Any constraints imposed by Chrome -or by SQLite may be broken. - - -## Usage - -The default approach for handling corruption in SQLite databases is to -immediately stop using the database, delete it, and start over with a new -database. The recovery method implemented here is intended for databases used by -Chrome features that handle high-value user data, such as History and Bookmarks. -These features carefully handle data inconsistency edge cases, and their -database schemas are resilient to partial data loss. - -The code is plugged into the rest of Chrome via -[SQLite's virtual table module API](https://sqlite.org/vtab.html). The example -below covers a typical recovery scenario. - -```sql --- Feature table schema. -CREATE TABLE data(name TEXT PRIMARY KEY, value TEXT NOT NULL); - --- Recover in another database. The corrupted one is unreliable. -ATTACH DATABASE '/tmp/db.db' as recovery; --- Re-create the feature table's schema. -CREATE TABLE recovery.feature(name TEXT PRIMARY KEY, value TEXT NOT NULL); --- Start reading the corrupted data. -CREATE VIRTUAL TABLE temp.recover_feature USING recover( - main.feature, -- The corrupted database. - -- Recovery will skip row values that don't have the TEXT type. - name TEXT STRICT NOT NULL, - -- Recovery will include any row value coercible to TEXT. - value TEXT NOT NULL); --- Data recovered from corrupted databases may not meet schema constraints, so --- recovery insertions must use "OR REPLACE" or "OR IGNORE". -INSERT OR REPLACE INTO recovery.feature(rowid, name, value) -SELECT rowid, name, value FROM temp.recover_feature; --- Cleanup after the recovery operation. -DROP TABLE temp.recover_feature; -DETACH DATABASE recovery; --- Replace the corrupted database file with the recovered one. -``` - -The feature invoking the recovery virtual table must know the schema of the -database being recovered. A generic high-level recovery layer should first -recover -[the `sqlite_schema` table](https://www.sqlite.org/fileformat.html#storage_of_the_sql_database_schema), -which has a well known format, then use its contents to recover the schema of -any other table. This recovery module already relies on the integrity of the -`sqlite_schema` table. - -The column definitions in the virtual table creation statement must follow -the syntax _column\_name_ _type\_name_ [`STRICT`] [`NOT NULL`]. _type\_name_ is -[the SQLite data types](https://www.sqlite.org/datatype3.html#storage_classes_and_datatypes), -or the special types `ANY` or `ROWID`. - -The `ANY` type can be used to recover values of all types. - -The `ROWID` type must be used for columns that alias -[rowid](https://www.sqlite.org/lang_createtable.html#rowid). This typically only -happens when a column is declared as `INTEGER PRIMARY KEY`. -Designating `ROWID` columns is essential for decoding records correctly. - -TODO(pwnall): Look into removing `STRICT`, if it's not used. - - -## Limitations - -The current implementation only handles [table -B-trees](https://www.sqlite.org/fileformat.html#b_tree_pages). It cannot -recover [WITHOUT ROWID](https://www.sqlite.org/rowidtable.html) tables, which -are stored in index B-trees. - - -## Code Map - -The code is structured as follows. - -* integers.{cc,h} decodes the integer formats used by SQLite. -* btree.{cc,h} decodes the cells in SQLite's B-tree pages. -* payload.{cc,h} reads record payloads from B-tree pages and overflow pages. -* record.{cc,h} decodes column values from record. -* cursor.{cc,h} implements a SQLite virtual table cursor. -* table.{cc,h} implements one recovery virtual table. -* parsing.{cc,h} parses the SQL strings passed in via `CREATE VIRTUAL TABLE` - and implements the constraints explained above. -* module.{cc,h} implements the SQLite virtual table interface. - -The feature is tested by integration tests that issue SQLite queries.
diff --git a/sql/recover_module/btree.cc b/sql/recover_module/btree.cc deleted file mode 100644 index ad985a3..0000000 --- a/sql/recover_module/btree.cc +++ /dev/null
@@ -1,253 +0,0 @@ -// Copyright 2019 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "sql/recover_module/btree.h" - -#include <algorithm> -#include <limits> -#include <ostream> -#include <type_traits> - -#include "base/check_op.h" -#include "sql/recover_module/integers.h" -#include "sql/recover_module/pager.h" -#include "third_party/sqlite/sqlite3.h" - -namespace sql { -namespace recover { - -namespace { - -// The SQLite database format is documented at the following URLs. -// https://www.sqlite.org/fileformat.html -// https://www.sqlite.org/fileformat2.html -constexpr uint8_t kInnerTablePageType = 0x05; -constexpr uint8_t kLeafTablePageType = 0x0D; - -// Offset from the page header to the page type byte. -constexpr int kPageTypePageOffset = 0; -// Offset from the page header to the 2-byte cell count. -constexpr int kCellCountPageOffset = 3; -// Offset from an inner page header to the 4-byte last child page ID. -constexpr int kLastChildIdInnerPageOffset = 8; -// Offset from an inner page header to the cell pointer array. -constexpr int kFirstCellOfsetInnerPageOffset = 12; -// Offset from a leaf page header to the cell pointer array. -constexpr int kFirstCellOfsetLeafPageOffset = 8; - -} // namespace - -#if !DCHECK_IS_ON() -// In DCHECKed builds, the decoder contains a sequence checker, which has a -// non-trivial destructor. -static_assert(std::is_trivially_destructible<InnerPageDecoder>::value, - "Move the destructor to the .cc file if it's non-trival"); -#endif // !DCHECK_IS_ON() - -InnerPageDecoder::InnerPageDecoder(DatabasePageReader* db_reader) noexcept - : page_id_(db_reader->page_id()), - db_reader_(db_reader), - cell_count_(ComputeCellCount(db_reader)), - next_read_index_(0) { - DCHECK(IsOnValidPage(db_reader)); - DCHECK(DatabasePageReader::IsValidPageId(page_id_)); -} - -int InnerPageDecoder::TryAdvance() { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - DCHECK(CanAdvance()); - - const int sqlite_status = db_reader_->ReadPage(page_id_); - if (sqlite_status != SQLITE_OK) { - // TODO(pwnall): UMA the error code. - - next_read_index_ = cell_count_ + 1; // End the reading process. - return DatabasePageReader::kHighestInvalidPageId; - } - - const uint8_t* const page_data = db_reader_->page_data(); - const int read_index = next_read_index_; - next_read_index_ += 1; - if (read_index == cell_count_) - return LoadBigEndianInt32(page_data + kLastChildIdInnerPageOffset); - - const int cell_pointer_offset = - kFirstCellOfsetInnerPageOffset + (read_index << 1); - DCHECK_LE(cell_pointer_offset + 2, db_reader_->page_size()) - << "ComputeCellCount() used an incorrect upper bound"; - const int cell_pointer = LoadBigEndianUint16(page_data + cell_pointer_offset); - - static_assert(std::numeric_limits<uint16_t>::max() + 4 < - std::numeric_limits<int>::max(), - "The addition below may overflow"); - if (cell_pointer + 4 >= db_reader_->page_size()) { - // Each cell needs 1 byte for the rowid varint, in addition to the 4 bytes - // for the child page number that will be read below. Skip cells that - // obviously go over the page end. - return DatabasePageReader::kHighestInvalidPageId; - } - if (cell_pointer < kFirstCellOfsetInnerPageOffset) { - // The pointer points into the cell's header. - return DatabasePageReader::kHighestInvalidPageId; - } - - return LoadBigEndianInt32(page_data + cell_pointer); -} - -// static -bool InnerPageDecoder::IsOnValidPage(DatabasePageReader* db_reader) { - static_assert(kPageTypePageOffset < DatabasePageReader::kMinUsablePageSize, - "The check below may perform an out-of-bounds memory access"); - return db_reader->page_data()[kPageTypePageOffset] == kInnerTablePageType; -} - -// static -int InnerPageDecoder::ComputeCellCount(DatabasePageReader* db_reader) { - // The B-tree page header stores the cell count. - int header_count = - LoadBigEndianUint16(db_reader->page_data() + kCellCountPageOffset); - static_assert( - kCellCountPageOffset + 2 <= DatabasePageReader::kMinUsablePageSize, - "The read above may be out of bounds"); - - // However, the data may be corrupted. So, use an upper bound based on the - // fact that the cell pointer array should never extend past the end of the - // page. - // - // The page size is always even, because it is either a power of two, for - // most pages, or a power of two minus 100, for the first database page. The - // cell pointer array starts at offset 12. So, each cell pointer must be - // separated from the page buffer's end by an even number of bytes. - DCHECK((db_reader->page_size() - kFirstCellOfsetInnerPageOffset) % 2 == 0); - int upper_bound = - (db_reader->page_size() - kFirstCellOfsetInnerPageOffset) >> 1; - static_assert( - kFirstCellOfsetInnerPageOffset <= DatabasePageReader::kMinUsablePageSize, - "The |upper_bound| computation above may overflow"); - - return std::min(header_count, upper_bound); -} - -#if !DCHECK_IS_ON() -// In DCHECKed builds, the decoder contains a sequence checker, which has a -// non-trivial destructor. -static_assert(std::is_trivially_destructible<LeafPageDecoder>::value, - "Move the destructor to the .cc file if it's non-trival"); -#endif // !DCHECK_IS_ON() - -LeafPageDecoder::LeafPageDecoder(DatabasePageReader* db_reader) noexcept - : page_id_(db_reader->page_id()), - db_reader_(db_reader), - cell_count_(ComputeCellCount(db_reader)), - next_read_index_(0), - last_record_size_(0) { - DCHECK(IsOnValidPage(db_reader)); - DCHECK(DatabasePageReader::IsValidPageId(page_id_)); -} - -bool LeafPageDecoder::TryAdvance() { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - DCHECK(CanAdvance()); - -#if DCHECK_IS_ON() - // DCHECKs use last_record_size == 0 to check for incorrect access to the - // decoder's state. - last_record_size_ = 0; -#endif // DCHECK_IS_ON() - - const int sqlite_status = db_reader_->ReadPage(page_id_); - if (sqlite_status != SQLITE_OK) { - // TODO(pwnall): UMA the error code. - - next_read_index_ = cell_count_; // End the reading process. - return false; - } - - const uint8_t* page_data = db_reader_->page_data(); - const int read_index = next_read_index_; - next_read_index_ += 1; - - const int cell_pointer_offset = - kFirstCellOfsetLeafPageOffset + (read_index << 1); - DCHECK_LE(cell_pointer_offset + 2, db_reader_->page_size()) - << "ComputeCellCount() used an incorrect upper bound"; - const int cell_pointer = LoadBigEndianUint16(page_data + cell_pointer_offset); - - static_assert(std::numeric_limits<uint16_t>::max() + 3 < - std::numeric_limits<int>::max(), - "The addition below may overflow"); - if (cell_pointer + 3 >= db_reader_->page_size()) { - // Each cell needs at least 1 byte for page type varint, 1 byte for the - // rowid varint, and 1 byte for the record header size varint. Skip cells - // that obviously go over the page end. - return false; - } - if (cell_pointer < kFirstCellOfsetLeafPageOffset) { - // The pointer points into the cell's header. - return false; - } - - const uint8_t* const cell_start = page_data + cell_pointer; - const uint8_t* const page_end = page_data + db_reader_->page_size(); - DCHECK_LT(cell_start, page_end) << "Failed to skip over empty cells"; - - const uint8_t* rowid_start; - std::tie(last_record_size_, rowid_start) = ParseVarint(cell_start, page_end); - if (rowid_start == page_end) { - // The value size varint extended to the end of the page, so the rowid - // varint starts past the page end. - return false; - } - if (last_record_size_ <= 0) { - // Each payload needs at least one varint. Skip empty payloads. -#if DCHECK_IS_ON() - // DCHECKs use last_record_size == 0 to check for incorrect access to the - // decoder's state. - last_record_size_ = 0; -#endif // DCHECK_IS_ON() - return false; - } - - const uint8_t* record_start; - std::tie(last_record_rowid_, record_start) = - ParseVarint(rowid_start, page_end); - if (record_start == page_end) { - // The rowid varint extended to the end of the page, so the record starts - // past the page end. Records need at least 1 byte for their header size - // varint, so this suggests corruption. - last_record_size_ = 0; - return false; - } - - last_record_offset_ = record_start - page_data; - return true; -} - -// static -bool LeafPageDecoder::IsOnValidPage(DatabasePageReader* db_reader) { - static_assert(kPageTypePageOffset < DatabasePageReader::kMinUsablePageSize, - "The check below may perform an out-of-bounds memory access"); - return db_reader->page_data()[kPageTypePageOffset] == kLeafTablePageType; -} - -// static -int LeafPageDecoder::ComputeCellCount(DatabasePageReader* db_reader) { - // See InnerPageDecoder::ComputeCellCount() for the reasoning behind the code. - int header_count = - LoadBigEndianUint16(db_reader->page_data() + kCellCountPageOffset); - static_assert( - kCellCountPageOffset + 2 <= DatabasePageReader::kMinUsablePageSize, - "The read above may be out of bounds"); - - int upper_bound = - (db_reader->page_size() - kFirstCellOfsetLeafPageOffset) >> 1; - static_assert( - kFirstCellOfsetLeafPageOffset <= DatabasePageReader::kMinUsablePageSize, - "The |upper_bound| computation above may overflow"); - - return std::min(header_count, upper_bound); -} - -} // namespace recover -} // namespace sql
diff --git a/sql/recover_module/btree.h b/sql/recover_module/btree.h deleted file mode 100644 index 155be1e..0000000 --- a/sql/recover_module/btree.h +++ /dev/null
@@ -1,197 +0,0 @@ -// Copyright 2019 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SQL_RECOVER_MODULE_BTREE_H_ -#define SQL_RECOVER_MODULE_BTREE_H_ - -#include <cstdint> -#include <ostream> - -#include "base/check.h" -#include "base/memory/raw_ptr_exclusion.h" -#include "base/sequence_checker.h" - -namespace sql { -namespace recover { - -class DatabasePageReader; - -// Streaming decoder for inner pages in SQLite table B-trees. -// -// The decoder outputs the page IDs of the inner page's children pages. -// -// An instance can only be used to decode a single page. Instances are not -// thread-safe. -class InnerPageDecoder { - public: - // Creates a decoder for a DatabasePageReader's last read page. - // - // |db_reader| must have been used to read an inner page of a table B-tree. - // |db_reader| must outlive this instance. - explicit InnerPageDecoder(DatabasePageReader* db_reader) noexcept; - ~InnerPageDecoder() noexcept = default; - - InnerPageDecoder(const InnerPageDecoder&) = delete; - InnerPageDecoder& operator=(const InnerPageDecoder&) = delete; - - // The ID of the database page decoded by this instance. - int page_id() const { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - return page_id_; - } - - // Returns true iff TryAdvance() may be called. - bool CanAdvance() const { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - // The <= below is not a typo. Inner nodes store the right-most child - // pointer in their headers, so their child count is (cell_count + 1). - return next_read_index_ <= cell_count_; - } - - // Advances the reader and returns the last read value. - // - // May return an invalid page ID if database was corrupted or the read failed. - // The caller must use DatabasePageReader::IsValidPageId() to verify the - // returned page ID. The caller should continue attempting to read as long as - // CanAdvance() returns true. - int TryAdvance(); - - // True if the given reader may point to an inner page in a table B-tree. - // - // The last ReadPage() call on |db_reader| must have succeeded. - static bool IsOnValidPage(DatabasePageReader* db_reader); - - private: - // Returns the number of cells in the B-tree page. - // - // Checks for database corruption. The caller can assume that the cell pointer - // array with the returned size will not extend past the page buffer. - static int ComputeCellCount(DatabasePageReader* db_reader); - - // The number of the B-tree page this reader is reading. - const int page_id_; - // Used to read the tree page. - // - // Raw pointer usage is acceptable because this instance's owner is expected - // to ensure that the DatabasePageReader outlives this. - // This field is not a raw_ptr<> because it caused a - // std::is_trivially_destructible static_assert failure. - RAW_PTR_EXCLUSION DatabasePageReader* const db_reader_; - // Caches the ComputeCellCount() value for this reader's page. - const int cell_count_ = ComputeCellCount(db_reader_); - - // The reader's cursor state. - // - // Each B-tree page has a header and many cells. In an inner B-tree page, each - // cell points to a child page, and the header points to the last child page. - // So, an inner page with N cells has N+1 children, and |next_read_index_| - // takes values between 0 and |cell_count_| + 1. - int next_read_index_ = 0; - - SEQUENCE_CHECKER(sequence_checker_); -}; - -// Streaming decoder for leaf pages in SQLite table B-trees. -// -// Conceptually, the decoder outputs (rowid, record size, record offset) tuples -// for all the values stored in the leaf page. The tuple members can be accessed -// via last_record_{rowid, size, offset}() methods. -// -// An instance can only be used to decode a single page. Instances are not -// thread-safe. -class LeafPageDecoder { - public: - // Creates a decoder for a DatabasePageReader's last read page. - // - // |db_reader| must have been used to read an inner page of a table B-tree. - // |db_reader| must outlive this instance. - explicit LeafPageDecoder(DatabasePageReader* db_reader) noexcept; - ~LeafPageDecoder() noexcept = default; - - LeafPageDecoder(const LeafPageDecoder&) = delete; - LeafPageDecoder& operator=(const LeafPageDecoder&) = delete; - - // The rowid of the most recent record read by TryAdvance(). - // - // Must only be called after a successful call to TryAdvance(). - int64_t last_record_rowid() const { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - DCHECK(last_record_size_ != 0) - << "TryAdvance() not called / did not succeed"; - return last_record_rowid_; - } - - // The size of the most recent record read by TryAdvance(). - // - // Must only be called after a successful call to TryAdvance(). - int64_t last_record_size() const { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - DCHECK(last_record_size_ != 0) - << "TryAdvance() not called / did not succeed"; - return last_record_size_; - } - - // The page offset of the most recent record read by TryAdvance(). - // - // Must only be called after a successful call to TryAdvance(). - int64_t last_record_offset() const { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - DCHECK(last_record_size_ != 0) - << "TryAdvance() not called / did not succeed"; - return last_record_offset_; - } - - // Returns true iff TryAdvance() may be called. - bool CanAdvance() const { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - return next_read_index_ < cell_count_; - } - - // Advances the reader and returns the last read value. - // - // Returns false if the read fails. The caller should continue attempting to - // read as long as CanAdvance() returns true. - bool TryAdvance(); - - // True if the given reader may point to an inner page in a table B-tree. - // - // The last ReadPage() call on |db_reader| must have succeeded. - static bool IsOnValidPage(DatabasePageReader* db_reader); - - private: - // Returns the number of cells in the B-tree page. - // - // Checks for database corruption. The caller can assume that the cell pointer - // array with the returned size will not extend past the page buffer. - static int ComputeCellCount(DatabasePageReader* db_reader); - - // The number of the B-tree page this reader is reading. - const int64_t page_id_; - // Used to read the tree page. - // - // Raw pointer usage is acceptable because this instance's owner is expected - // to ensure that the DatabasePageReader outlives this. - // This field is not a raw_ptr<> because it caused a - // std::is_trivially_destructible static_assert failure. - RAW_PTR_EXCLUSION DatabasePageReader* const db_reader_; - // Caches the ComputeCellCount() value for this reader's page. - const int cell_count_ = ComputeCellCount(db_reader_); - - // The reader's cursor state. - // - // Each B-tree cell contains a value. So, this member takes values in - // [0, cell_count_). - int next_read_index_ = 0; - - int64_t last_record_size_ = 0; - int64_t last_record_rowid_ = 0; - int last_record_offset_ = 0; - - SEQUENCE_CHECKER(sequence_checker_); -}; - -} // namespace recover -} // namespace sql - -#endif // SQL_RECOVER_MODULE_BTREE_H_
diff --git a/sql/recover_module/cursor.cc b/sql/recover_module/cursor.cc deleted file mode 100644 index 8983af5..0000000 --- a/sql/recover_module/cursor.cc +++ /dev/null
@@ -1,164 +0,0 @@ -// Copyright 2019 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "sql/recover_module/cursor.h" - -#include <ostream> - -#include "base/containers/span.h" -#include "sql/recover_module/table.h" - -namespace sql { -namespace recover { - -VirtualCursor::VirtualCursor(VirtualTable* table) - : table_(table), - db_reader_(table), - payload_reader_(&db_reader_), - record_reader_(&payload_reader_, table->column_specs().size()) { - DCHECK(table_ != nullptr); -} - -VirtualCursor::~VirtualCursor() { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - table_->WillDeleteCursor(this); -} - -int VirtualCursor::First() { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - inner_decoders_.clear(); - leaf_decoder_ = nullptr; - - AppendPageDecoder(table_->root_page_id()); - return Next(); -} - -int VirtualCursor::Next() { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - record_reader_.Reset(); - - while (!inner_decoders_.empty() || leaf_decoder_.get()) { - if (leaf_decoder_.get()) { - if (!leaf_decoder_->CanAdvance()) { - // The leaf has been exhausted. Remove it from the DFS stack. - leaf_decoder_ = nullptr; - continue; - } - if (!leaf_decoder_->TryAdvance()) - continue; - - if (!payload_reader_.Initialize(leaf_decoder_->last_record_size(), - leaf_decoder_->last_record_offset())) { - continue; - } - if (!record_reader_.Initialize()) - continue; - - // Found a healthy record. - if (!IsAcceptableRecord()) { - record_reader_.Reset(); - continue; - } - return SQLITE_OK; - } - - // Try advancing the bottom-most inner node. - DCHECK(!inner_decoders_.empty()); - InnerPageDecoder* inner_decoder = inner_decoders_.back().get(); - if (!inner_decoder->CanAdvance()) { - // The inner node's sub-tree has been visited. Remove from the DFS stack. - inner_decoders_.pop_back(); - continue; - } - int next_page_id = inner_decoder->TryAdvance(); - if (!DatabasePageReader::IsValidPageId(next_page_id)) { - continue; - } - AppendPageDecoder(next_page_id); - } - - // The cursor reached the end of the table. - return SQLITE_OK; -} - -int VirtualCursor::ReadColumn(int column_index, - sqlite3_context* result_context) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - DCHECK_GE(column_index, 0); - DCHECK_LT(column_index, static_cast<int>(table_->column_specs().size())); - DCHECK(record_reader_.IsInitialized()); - - if (table_->column_specs()[column_index].type == ModuleColumnType::kRowId) { - sqlite3_result_int64(result_context, RowId()); - return SQLITE_OK; - } - - if (record_reader_.ReadValue(column_index, result_context)) - return SQLITE_OK; - return SQLITE_ERROR; -} - -int64_t VirtualCursor::RowId() { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - DCHECK(record_reader_.IsInitialized()); - DCHECK(leaf_decoder_.get()); - return leaf_decoder_->last_record_rowid(); -} - -void VirtualCursor::AppendPageDecoder(int page_id) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - DCHECK(leaf_decoder_.get() == nullptr) - << __func__ - << " must only be called when the current path has no leaf decoder"; - - if (db_reader_.ReadPage(page_id) != SQLITE_OK) - return; - - if (LeafPageDecoder::IsOnValidPage(&db_reader_)) { - leaf_decoder_ = std::make_unique<LeafPageDecoder>(&db_reader_); - return; - } - - if (InnerPageDecoder::IsOnValidPage(&db_reader_)) { - // Detect cycles. - for (const auto& decoder : inner_decoders_) { - if (decoder->page_id() == page_id) - return; - } - - // Give up on overly deep tree branches. - // - // SQLite supports up to 2^31 pages. SQLite ensures that inner nodes can - // hold at least 4 child pointers, even in the presence of very large keys. - // So, even poorly balanced trees should not exceed 100 nodes in depth. - // InnerPageDecoder instances take up 32 bytes on 64-bit platforms. - // - // The depth limit below balances recovering broken trees with avoiding - // excessive memory consumption. - constexpr int kMaxTreeDepth = 10000; - if (inner_decoders_.size() == kMaxTreeDepth) - return; - - inner_decoders_.emplace_back( - std::make_unique<InnerPageDecoder>(&db_reader_)); - return; - } -} - -bool VirtualCursor::IsAcceptableRecord() { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - DCHECK(record_reader_.IsInitialized()); - - const std::vector<RecoveredColumnSpec>& column_specs = table_->column_specs(); - const int column_count = static_cast<int>(column_specs.size()); - for (int column_index = 0; column_index < column_count; ++column_index) { - ValueType value_type = record_reader_.GetValueType(column_index); - if (!column_specs[column_index].IsAcceptableValue(value_type)) - return false; - } - return true; -} - -} // namespace recover -} // namespace sql
diff --git a/sql/recover_module/cursor.h b/sql/recover_module/cursor.h deleted file mode 100644 index 4cb0655..0000000 --- a/sql/recover_module/cursor.h +++ /dev/null
@@ -1,139 +0,0 @@ -// Copyright 2019 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SQL_RECOVER_MODULE_CURSOR_H_ -#define SQL_RECOVER_MODULE_CURSOR_H_ - -#include <cstddef> -#include <cstdint> -#include <memory> -#include <utility> - -#include "base/check_op.h" -#include "base/memory/raw_ptr.h" -#include "base/sequence_checker.h" -#include "sql/recover_module/btree.h" -#include "sql/recover_module/pager.h" -#include "sql/recover_module/parsing.h" -#include "sql/recover_module/payload.h" -#include "sql/recover_module/record.h" -#include "third_party/sqlite/sqlite3.h" - -namespace sql { -namespace recover { - -class VirtualTable; - -// Represents a virtual table cursor created by SQLite in a recovery table. -// -// Instances are allocated on the heap using the C++ new operator, and passed to -// SQLite via pointers to the sqlite_vtab members. SQLite is responsible for -// managing the instances' lifetimes. SQLite will call xClose() for every -// successful xOpen(). -// -// Instances are not thread-safe. This should be fine, as long as each SQLite -// statement that reads from a virtual table is only used on one sequence. This -// assumption is verified by a sequence checker. -// -// If it turns out that VirtualCursor needs to be thread-safe, the best solution -// is to add a base::Lock to VirtualCursor, and keep all underlying classes not -// thread-safe. -class VirtualCursor { - public: - // Creates a cursor that iterates over |table|. - // - // |table| must outlive this instance. SQLite is trusted to call xClose() for - // this cursor before calling xDestroy() / xDisconnect() for the virtual table - // related to the cursor. - explicit VirtualCursor(VirtualTable* table); - ~VirtualCursor(); - - VirtualCursor(const VirtualCursor&) = delete; - VirtualCursor& operator=(const VirtualCursor&) = delete; - - // Returns the embedded SQLite virtual table cursor. - // - // This getter is not const because SQLite wants a non-const pointer to the - // structure. - sqlite3_vtab_cursor* SqliteCursor() { return &sqlite_cursor_; } - - // The VirtualCursor instance that embeds a given SQLite virtual table cursor. - // - // |sqlite_cursor| must have been returned by VirtualTable::SqliteCursor(). - static inline VirtualCursor* FromSqliteCursor( - sqlite3_vtab_cursor* sqlite_cursor) { - VirtualCursor* result = reinterpret_cast<VirtualCursor*>( - (reinterpret_cast<char*>(sqlite_cursor) - - offsetof(VirtualCursor, sqlite_cursor_))); - CHECK_EQ(sqlite_cursor, &result->sqlite_cursor_); - return result; - } - - // Seeks the cursor to the first readable row. Returns a SQLite status code. - int First(); - - // Seeks the cursor to the next row. Returns a SQLite status code. - int Next(); - - // Returns true if the cursor points to a valid row, false otherwise. - bool IsValid() const { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - return record_reader_.IsInitialized(); - } - - // Reports a value in the record to SQLite. |column_index| is 0-based. - // - // Returns a SQLite error code. This method can fail can happen if a value is - // stored across overflow pages, and reading one of the overflow pages results - // in an I/O error. - int ReadColumn(int column_index, sqlite3_context* result_context); - - // Returns the rowid of the current row. The cursor must point to a valid row. - int64_t RowId(); - - private: - // Appends a decoder for the given page at the end of the current chain. - // - // No modification is performed in case of failures due to I/O errors or - // database corruption. - void AppendPageDecoder(int page_id); - - // True if the current record is acceptable given the recovery schema. - bool IsAcceptableRecord(); - - // SQLite handle for this cursor. The struct is populated and used by SQLite. - sqlite3_vtab_cursor sqlite_cursor_; - - // The table this cursor was created for. - // - // Raw pointer usage is acceptable because SQLite will ensure that the - // VirtualTable, which is passed around as a sqlite3_vtab*, will outlive this - // cursor, which is passed around as a sqlite3_cursor*. - const raw_ptr<VirtualTable> table_; - - // Reads database pages for this cursor. - DatabasePageReader db_reader_; - - // Reads record payloads for this cursor. - LeafPayloadReader payload_reader_; - - // Reads record rows for this cursor. - RecordReader record_reader_; - - // Decoders for the current chain of inner pages. - // - // The current chain of pages consists of the inner page decoders here and the - // decoder in |leaf_decoder_|. - std::vector<std::unique_ptr<InnerPageDecoder>> inner_decoders_; - - // Decodes the leaf page containing records. - std::unique_ptr<LeafPageDecoder> leaf_decoder_; - - SEQUENCE_CHECKER(sequence_checker_); -}; - -} // namespace recover -} // namespace sql - -#endif // SQL_RECOVER_MODULE_CURSOR_H_
diff --git a/sql/recover_module/integers.cc b/sql/recover_module/integers.cc deleted file mode 100644 index 14566857..0000000 --- a/sql/recover_module/integers.cc +++ /dev/null
@@ -1,39 +0,0 @@ -// Copyright 2019 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "sql/recover_module/integers.h" - -#include "base/check_op.h" - -namespace sql { -namespace recover { - -std::pair<int64_t, const uint8_t*> ParseVarint(const uint8_t* buffer, - const uint8_t* buffer_end) { - DCHECK(buffer != nullptr); - DCHECK(buffer_end != nullptr); - - DCHECK_LT(buffer, buffer_end); - const uint8_t* const regular_buffer_end = - (buffer_end - buffer > kMaxVarintSize - 1) ? buffer + kMaxVarintSize - 1 - : buffer_end; - - uint64_t value = 0; - uint8_t last_byte; - while (buffer < regular_buffer_end) { - last_byte = *buffer; - ++buffer; - value = (value << 7) | (last_byte & 0x7f); - if ((last_byte & 0x80) == 0) - break; - } - if (buffer < buffer_end && (last_byte & 0x80) != 0) { - value = (value << 8) | *buffer; - ++buffer; - } - return {value, buffer}; -} - -} // namespace recover -} // namespace sql
diff --git a/sql/recover_module/integers.h b/sql/recover_module/integers.h deleted file mode 100644 index fb4133e..0000000 --- a/sql/recover_module/integers.h +++ /dev/null
@@ -1,77 +0,0 @@ -// Copyright 2019 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SQL_RECOVER_MODULE_INTEGERS_H_ -#define SQL_RECOVER_MODULE_INTEGERS_H_ - -#include <cstdint> -#include <limits> -#include <utility> - -namespace sql { -namespace recover { - -// Reads an unsigned 16-bit big-endian integer from the given buffer. -// -// |buffer| must point to at least two consecutive bytes of valid memory. -// -// The return type was chosen because it suits the callers best. -inline int LoadBigEndianUint16(const uint8_t* buffer) { - static_assert( - std::numeric_limits<uint16_t>::max() <= std::numeric_limits<int>::max(), - "The value may overflow the return type"); - return (static_cast<int>(buffer[0]) << 8) | static_cast<int>(buffer[1]); -} - -// Reads a signed 32-bit big-endian integer from the given buffer. -// -// |buffer| must point to at least four consecutive bytes of valid memory. -inline int32_t LoadBigEndianInt32(const uint8_t* buffer) { - // The code gets optimized to mov + bswap on x86_64, and to ldr + rev on ARM. - return (static_cast<int32_t>(buffer[0]) << 24) | - (static_cast<int32_t>(buffer[1]) << 16) | - (static_cast<int32_t>(buffer[2]) << 8) | - static_cast<int32_t>(buffer[3]); -} - -// Reads a signed 64-bit big-endian integer from the given buffer. -// -// |buffer| must point to at least eight consecutive bytes of valid memory. -inline int64_t LoadBigEndianInt64(const uint8_t* buffer) { - // The code gets optimized to mov + bswap on x86_64, and to ldr + rev on ARM. - return (static_cast<int64_t>(buffer[0]) << 56) | - (static_cast<int64_t>(buffer[1]) << 48) | - (static_cast<int64_t>(buffer[2]) << 40) | - (static_cast<int64_t>(buffer[3]) << 32) | - (static_cast<int64_t>(buffer[4]) << 24) | - (static_cast<int64_t>(buffer[5]) << 16) | - (static_cast<int64_t>(buffer[6]) << 8) | - static_cast<int64_t>(buffer[7]); -} - -// Reads a SQLite varint. -// -// SQLite varints decode to 64-bit integers, and take up at most 9 bytes. -// If present, the 9th byte holds bits 56-63 of the integer. This deviates from -// Google (protobuf, leveldb) varint encoding, where the last varint byte's top -// bit is always 0. -// -// The implementation assumes that |buffer| and |buffer_end| point into the same -// array of bytes, and that |buffer| < |buffer_end|. The implementation will -// never compute a pointer value larger than |buffer_end|. -// -// Returns the parsed number and a pointer to the first byte past the number. -// Per the rules above, the returned pointer is guaranteed to be between -// |buffer| and |buffer_end|. The returned pointer is also guaranteed to be at -// most |kMaxVarintSize| bytes past |buffer|. -std::pair<int64_t, const uint8_t*> ParseVarint(const uint8_t* buffer, - const uint8_t* buffer_end); - -// The maximum number of bytes used to store a SQLite varint. -constexpr int kMaxVarintSize = 9; - -} // namespace recover -} // namespace sql - -#endif // SQL_RECOVER_MODULE_INTEGERS_H_
diff --git a/sql/recover_module/module.cc b/sql/recover_module/module.cc deleted file mode 100644 index 9795b713..0000000 --- a/sql/recover_module/module.cc +++ /dev/null
@@ -1,281 +0,0 @@ -// Copyright 2019 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "sql/recover_module/module.h" - -#include <cstddef> -#include <cstdint> -#include <ostream> -#include <string> -#include <utility> -#include <vector> - -#include "base/check_op.h" -#include "base/strings/strcat.h" -#include "base/strings/string_piece.h" -#include "base/strings/string_util.h" -#include "sql/recover_module/cursor.h" -#include "sql/recover_module/parsing.h" -#include "sql/recover_module/table.h" -#include "third_party/sqlite/sqlite3.h" - -// https://sqlite.org/vtab.html documents SQLite's virtual table module API. - -namespace sql { -namespace recover { - -namespace { - -// SQLite module argument constants. -static constexpr int kModuleNameArgument = 0; -static constexpr int kVirtualTableDbNameArgument = 1; -static constexpr int kVirtualTableNameArgument = 2; -static constexpr int kBackingTableSpecArgument = 3; -static constexpr int kFirstColumnArgument = 4; - -// Returns an empty vector on parse errors. -std::vector<RecoveredColumnSpec> ParseColumnSpecs(int argc, - const char* const* argv) { - std::vector<RecoveredColumnSpec> result; - DCHECK_GE(argc, kFirstColumnArgument); - result.reserve(argc - kFirstColumnArgument + 1); - - for (int i = kFirstColumnArgument; i < argc; ++i) { - result.emplace_back(ParseColumnSpec(argv[i])); - if (!result.back().IsValid()) { - result.clear(); - break; - } - } - return result; -} - -int ModuleCreate(sqlite3* sqlite_db, - void* /* client_data */, - int argc, - const char* const* argv, - sqlite3_vtab** result_sqlite_table, - char** /* error_string */) { - DCHECK(sqlite_db != nullptr); - if (argc <= kFirstColumnArgument) { - // The recovery module needs at least one column specification. - return SQLITE_ERROR; - } - DCHECK(argv != nullptr); - DCHECK(result_sqlite_table != nullptr); - - // This module is always expected to be registered as "recover". - DCHECK_EQ("recover", base::StringPiece(argv[kModuleNameArgument])); - - base::StringPiece db_name(argv[kVirtualTableDbNameArgument]); - if (db_name != "temp") { - // Refuse to create tables outside the "temp" database. - // - // This check is overly strict. The virtual table can be safely used on any - // temporary database (ATTACH '' AS other_temp). However, there is no easy - // way to determine if an attachment point corresponds to a temporary - // database, and "temp" is sufficient for Chrome's purposes. - return SQLITE_ERROR; - } - - base::StringPiece table_name(argv[kVirtualTableNameArgument]); - if (!base::StartsWith(table_name, "recover_")) { - // In the future, we may deploy UMA metrics that use the virtual table name - // to attribute recovery events to Chrome features. In preparation for that - // future, require all recovery table names to start with "recover_". - return SQLITE_ERROR; - } - - TargetTableSpec backing_table_spec = - ParseTableSpec(argv[kBackingTableSpecArgument]); - if (!backing_table_spec.IsValid()) { - // The parser concluded that the string specifying the backing table is - // invalid. This is definitely an error in the SQL using the virtual table. - return SQLITE_ERROR; - } - - std::vector<RecoveredColumnSpec> column_specs = ParseColumnSpecs(argc, argv); - if (column_specs.empty()) { - // The column specifications were invalid. - return SQLITE_ERROR; - } - - auto [sqlite_status, table] = VirtualTable::Create( - sqlite_db, std::move(backing_table_spec), std::move(column_specs)); - if (sqlite_status != SQLITE_OK) - return sqlite_status; - - { - std::string create_table_sql = table->ToCreateTableSql(); - sqlite3_declare_vtab(sqlite_db, create_table_sql.c_str()); - } - *result_sqlite_table = table->SqliteTable(); - table.release(); // SQLite manages the lifetime of the table. - return SQLITE_OK; -} - -int ModuleConnect(sqlite3* sqlite_db, - void* client_data, - int argc, - const char* const* argv, - sqlite3_vtab** result_sqlite_table, - char** error_string) { - // TODO(pwnall): Figure out if it's acceptable to have "recover" be an - // eponymous table. If so, use ModuleCreate instead of - // ModuleConnect in the entry point table. - return ModuleCreate(sqlite_db, client_data, argc, argv, result_sqlite_table, - error_string); -} - -int ModuleBestIndex(sqlite3_vtab* sqlite_table, - sqlite3_index_info* index_info) { - DCHECK(sqlite_table != nullptr); - DCHECK(index_info != nullptr); - - // The sqlite3_index_info structure is also documented at - // https://www.sqlite.org/draft/c3ref/index_info.html - for (int i = 0; i < index_info->nConstraint; ++i) { - if (index_info->aConstraint[i].usable == static_cast<char>(false)) - continue; - // True asks SQLite to evaluate the constraint and pass the result to any - // follow-up xFilter() calls, via argc/argv. - index_info->aConstraintUsage[i].argvIndex = 0; - // True indicates that the virtual table will check the constraint. - index_info->aConstraintUsage[i].omit = false; - } - index_info->orderByConsumed = static_cast<int>(false); - - // SQLite saves the sqlite_idx_info fields set here and passes the values to - // xFilter(). - index_info->idxStr = nullptr; - index_info->idxNum = 0; - index_info->needToFreeIdxStr = static_cast<int>(false); - - return SQLITE_OK; -} - -int ModuleDisconnect(sqlite3_vtab* sqlite_table) { - DCHECK(sqlite_table != nullptr); - - // SQLite takes ownership of the VirtualTable (which is passed around as a - // sqlite_table) in ModuleCreate() / ModuleConnect(). SQLite then calls - // ModuleDestroy() / ModuleDisconnect() to relinquish ownership of the - // VirtualTable. At this point, the table will not be used again, and can be - // destroyed. - VirtualTable* const table = VirtualTable::FromSqliteTable(sqlite_table); - delete table; - return SQLITE_OK; -} - -int ModuleDestroy(sqlite3_vtab* sqlite_table) { - return ModuleDisconnect(sqlite_table); -} - -int ModuleOpen(sqlite3_vtab* sqlite_table, - sqlite3_vtab_cursor** result_sqlite_cursor) { - DCHECK(sqlite_table != nullptr); - DCHECK(result_sqlite_cursor != nullptr); - - VirtualTable* const table = VirtualTable::FromSqliteTable(sqlite_table); - VirtualCursor* const cursor = table->CreateCursor(); - *result_sqlite_cursor = cursor->SqliteCursor(); - return SQLITE_OK; -} - -int ModuleClose(sqlite3_vtab_cursor* sqlite_cursor) { - DCHECK(sqlite_cursor != nullptr); - - // SQLite takes ownership of the VirtualCursor (which is passed around as a - // sqlite_cursor) in ModuleOpen(). SQLite then calls ModuleClose() to - // relinquish ownership of the VirtualCursor. At this point, the cursor will - // not be used again, and can be destroyed. - VirtualCursor* const cursor = VirtualCursor::FromSqliteCursor(sqlite_cursor); - delete cursor; - return SQLITE_OK; -} - -int ModuleFilter(sqlite3_vtab_cursor* sqlite_cursor, - int /* best_index_num */, - const char* /* best_index_str */, - int /* argc */, - sqlite3_value** /* argv */) { - DCHECK(sqlite_cursor != nullptr); - - VirtualCursor* const cursor = VirtualCursor::FromSqliteCursor(sqlite_cursor); - return cursor->First(); -} - -int ModuleNext(sqlite3_vtab_cursor* sqlite_cursor) { - DCHECK(sqlite_cursor != nullptr); - - VirtualCursor* const cursor = VirtualCursor::FromSqliteCursor(sqlite_cursor); - return cursor->Next(); -} - -int ModuleEof(sqlite3_vtab_cursor* sqlite_cursor) { - DCHECK(sqlite_cursor != nullptr); - - VirtualCursor* const cursor = VirtualCursor::FromSqliteCursor(sqlite_cursor); - return cursor->IsValid() ? 0 : 1; -} - -int ModuleColumn(sqlite3_vtab_cursor* sqlite_cursor, - sqlite3_context* result_context, - int column_index) { - DCHECK(sqlite_cursor != nullptr); - DCHECK(result_context != nullptr); - - VirtualCursor* const cursor = VirtualCursor::FromSqliteCursor(sqlite_cursor); - DCHECK(cursor->IsValid()) << "SQLite called xRowid() without a valid cursor"; - return cursor->ReadColumn(column_index, result_context); -} - -int ModuleRowid(sqlite3_vtab_cursor* sqlite_cursor, - sqlite3_int64* result_rowid) { - DCHECK(sqlite_cursor != nullptr); - DCHECK(result_rowid != nullptr); - - VirtualCursor* const cursor = VirtualCursor::FromSqliteCursor(sqlite_cursor); - DCHECK(cursor->IsValid()) << "SQLite called xRowid() without a valid cursor"; - *result_rowid = cursor->RowId(); - return SQLITE_OK; -} - -// SQLite module API version supported by this implementation. -constexpr int kSqliteModuleApiVersion = 1; - -// Entry points to the SQLite module. -constexpr sqlite3_module kSqliteModule = { - kSqliteModuleApiVersion, - &ModuleCreate, - &ModuleConnect, - &ModuleBestIndex, - &ModuleDisconnect, - &ModuleDestroy, - &ModuleOpen, - &ModuleClose, - &ModuleFilter, - &ModuleNext, - &ModuleEof, - &ModuleColumn, - &ModuleRowid, - /* xUpdate= */ nullptr, - /* xBegin= */ nullptr, - /* xSync= */ nullptr, - /* xCommit= */ nullptr, - /* xRollback= */ nullptr, - /* xFindFunction= */ nullptr, - /* xRename= */ nullptr, -}; - -} // namespace - -int RegisterRecoverExtension(sqlite3* db) { - return sqlite3_create_module_v2(db, "recover", &kSqliteModule, - /* pClientData= */ nullptr, - /* xDestroy= */ nullptr); -} - -} // namespace recover -} // namespace sql
diff --git a/sql/recover_module/module.h b/sql/recover_module/module.h deleted file mode 100644 index a4d40ee4..0000000 --- a/sql/recover_module/module.h +++ /dev/null
@@ -1,21 +0,0 @@ -// Copyright 2019 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SQL_RECOVER_MODULE_MODULE_H_ -#define SQL_RECOVER_MODULE_MODULE_H_ - -struct sqlite3; - -namespace sql { -namespace recover { - -// Registers the "recover" virtual table with a SQLite connection. -// -// Returns a SQLite error code. -int RegisterRecoverExtension(sqlite3* db); - -} // namespace recover -} // namespace sql - -#endif // SQL_RECOVER_MODULE_MODULE_H_
diff --git a/sql/recover_module/module_unittest.cc b/sql/recover_module/module_unittest.cc deleted file mode 100644 index c554db5..0000000 --- a/sql/recover_module/module_unittest.cc +++ /dev/null
@@ -1,1126 +0,0 @@ -// Copyright 2019 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include <random> -#include <string> -#include <tuple> -#include <vector> - -#include "base/files/scoped_temp_dir.h" -#include "base/strings/stringprintf.h" -#include "sql/database.h" -#include "sql/statement.h" -#include "sql/test/database_test_peer.h" -#include "sql/test/scoped_error_expecter.h" -#include "sql/test/test_helpers.h" -#include "sql/transaction.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "third_party/sqlite/sqlite3.h" - -namespace sql { -namespace recover { - -class RecoverModuleTest : public testing::Test { - public: - ~RecoverModuleTest() override = default; - - void SetUp() override { - ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); - ASSERT_TRUE( - db_.Open(temp_dir_.GetPath().AppendASCII("recovery_test.sqlite"))); - ASSERT_TRUE(DatabaseTestPeer::EnableRecoveryExtension(&db_)); - } - - protected: - base::ScopedTempDir temp_dir_; - sql::Database db_{sql::DatabaseOptions{ - .enable_virtual_tables_discouraged = true, - }}; -}; - -TEST_F(RecoverModuleTest, CreateVtable) { - ASSERT_TRUE(db_.Execute("CREATE TABLE backing(t TEXT)")); - EXPECT_TRUE( - db_.Execute("CREATE VIRTUAL TABLE temp.recover_backing " - "USING recover(backing, t TEXT)")); -} -TEST_F(RecoverModuleTest, CreateVtableWithDatabaseSpecifier) { - ASSERT_TRUE(db_.Execute("CREATE TABLE backing(t TEXT)")); - EXPECT_TRUE( - db_.Execute("CREATE VIRTUAL TABLE temp.recover_backing " - "USING recover(main.backing, t TEXT)")); -} -TEST_F(RecoverModuleTest, CreateVtableOnSqliteSchema) { - ASSERT_TRUE(db_.Execute("CREATE TABLE backing(t TEXT)")); - EXPECT_TRUE( - db_.Execute("CREATE VIRTUAL TABLE temp.recover_backing USING recover(" - "sqlite_schema, type TEXT, name TEXT, tbl_name TEXT, " - "rootpage INTEGER, sql TEXT)")); -} - -TEST_F(RecoverModuleTest, CreateVtableFailsOnNonTempTable) { - ASSERT_TRUE(db_.Execute("CREATE TABLE backing(t TEXT)")); - { - sql::test::ScopedErrorExpecter error_expecter; - error_expecter.ExpectError(SQLITE_ERROR); - EXPECT_FALSE(db_.Execute( - "CREATE VIRTUAL TABLE recover_backing USING recover(backing, t TEXT)")); - EXPECT_TRUE(error_expecter.SawExpectedErrors()); - } -} -TEST_F(RecoverModuleTest, CreateVtableFailsOnMissingTable) { - ASSERT_TRUE(db_.Execute("CREATE TABLE backing(t TEXT)")); - { - sql::test::ScopedErrorExpecter error_expecter; - error_expecter.ExpectError(SQLITE_CORRUPT); - EXPECT_FALSE( - db_.Execute("CREATE VIRTUAL TABLE temp.recover_missing " - "USING recover(missing, t TEXT)")); - EXPECT_TRUE(error_expecter.SawExpectedErrors()); - } -} -TEST_F(RecoverModuleTest, CreateVtableFailsOnMissingDatabase) { - ASSERT_TRUE(db_.Execute("CREATE TABLE backing(t TEXT)")); - { - sql::test::ScopedErrorExpecter error_expecter; - error_expecter.ExpectError(SQLITE_CORRUPT); - EXPECT_FALSE( - db_.Execute("CREATE VIRTUAL TABLE temp.recover_backing " - "USING recover(db.backing, t TEXT)")); - EXPECT_TRUE(error_expecter.SawExpectedErrors()); - } -} -TEST_F(RecoverModuleTest, CreateVtableFailsOnTableWithInvalidQualifier) { - ASSERT_TRUE(db_.Execute("CREATE TABLE backing(t TEXT)")); - { - sql::test::ScopedErrorExpecter error_expecter; - error_expecter.ExpectError(SQLITE_CORRUPT); - EXPECT_FALSE( - db_.Execute("CREATE VIRTUAL TABLE temp.recover_backing " - "USING recover(backing invalid, t TEXT)")); - EXPECT_TRUE(error_expecter.SawExpectedErrors()); - } -} -TEST_F(RecoverModuleTest, CreateVtableFailsOnMissingTableName) { - ASSERT_TRUE(db_.Execute("CREATE TABLE backing(t TEXT)")); - { - sql::test::ScopedErrorExpecter error_expecter; - error_expecter.ExpectError(SQLITE_ERROR); - EXPECT_FALSE( - db_.Execute("CREATE VIRTUAL TABLE temp.recover_backing " - "USING recover(main., t TEXT)")); - EXPECT_TRUE(error_expecter.SawExpectedErrors()); - } -} -TEST_F(RecoverModuleTest, CreateVtableFailsOnMissingSchemaSpec) { - ASSERT_TRUE(db_.Execute("CREATE TABLE backing(t TEXT)")); - { - sql::test::ScopedErrorExpecter error_expecter; - error_expecter.ExpectError(SQLITE_ERROR); - EXPECT_FALSE( - db_.Execute("CREATE VIRTUAL TABLE temp.recover_backing " - "USING recover(backing)")); - EXPECT_TRUE(error_expecter.SawExpectedErrors()); - } -} -TEST_F(RecoverModuleTest, CreateVtableFailsOnMissingDbName) { - ASSERT_TRUE(db_.Execute("CREATE TABLE backing(t TEXT)")); - { - sql::test::ScopedErrorExpecter error_expecter; - error_expecter.ExpectError(SQLITE_ERROR); - EXPECT_FALSE( - db_.Execute("CREATE VIRTUAL TABLE temp.recover_backing " - "USING recover(.backing)")); - EXPECT_TRUE(error_expecter.SawExpectedErrors()); - } -} - -TEST_F(RecoverModuleTest, ColumnTypeMappingAny) { - ASSERT_TRUE(db_.Execute("CREATE TABLE backing(t TEXT)")); - EXPECT_TRUE( - db_.Execute("CREATE VIRTUAL TABLE temp.recover_backing " - "USING recover(backing, t ANY)")); - - sql::test::ColumnInfo column_info = - sql::test::ColumnInfo::Create(&db_, "temp", "recover_backing", "t"); - EXPECT_EQ("(nullptr)", column_info.data_type); - EXPECT_EQ("BINARY", column_info.collation_sequence); - EXPECT_FALSE(column_info.has_non_null_constraint); - EXPECT_FALSE(column_info.is_in_primary_key); - EXPECT_FALSE(column_info.is_auto_incremented); -} -TEST_F(RecoverModuleTest, ColumnTypeMappingAnyNotNull) { - ASSERT_TRUE(db_.Execute("CREATE TABLE backing(t TEXT)")); - EXPECT_TRUE( - db_.Execute("CREATE VIRTUAL TABLE temp.recover_backing " - "USING recover(backing, t ANY NOT NULL)")); - - sql::test::ColumnInfo column_info = - sql::test::ColumnInfo::Create(&db_, "temp", "recover_backing", "t"); - EXPECT_EQ("(nullptr)", column_info.data_type); - EXPECT_EQ("BINARY", column_info.collation_sequence); - EXPECT_TRUE(column_info.has_non_null_constraint); - EXPECT_FALSE(column_info.is_in_primary_key); - EXPECT_FALSE(column_info.is_auto_incremented); -} -TEST_F(RecoverModuleTest, ColumnTypeMappingAnyStrict) { - ASSERT_TRUE(db_.Execute("CREATE TABLE backing(t TEXT)")); - { - sql::test::ScopedErrorExpecter error_expecter; - error_expecter.ExpectError(SQLITE_ERROR); - EXPECT_FALSE( - db_.Execute("CREATE VIRTUAL TABLE temp.recover_backing " - "USING recover(backing, t ANY STRICT)")); - EXPECT_TRUE(error_expecter.SawExpectedErrors()); - } -} - -TEST_F(RecoverModuleTest, ColumnTypeExtraKeyword) { - ASSERT_TRUE(db_.Execute("CREATE TABLE backing(t TEXT)")); - { - sql::test::ScopedErrorExpecter error_expecter; - error_expecter.ExpectError(SQLITE_ERROR); - EXPECT_FALSE( - db_.Execute("CREATE VIRTUAL TABLE temp.recover_backing " - "USING recover(backing, t INTEGER SOMETHING)")); - EXPECT_TRUE(error_expecter.SawExpectedErrors()); - } -} -TEST_F(RecoverModuleTest, ColumnTypeNotNullExtraKeyword) { - ASSERT_TRUE(db_.Execute("CREATE TABLE backing(t TEXT)")); - { - sql::test::ScopedErrorExpecter error_expecter; - error_expecter.ExpectError(SQLITE_ERROR); - EXPECT_FALSE( - db_.Execute("CREATE VIRTUAL TABLE temp.recover_backing " - "USING recover(backing, t INTEGER NOT NULL SOMETHING)")); - EXPECT_TRUE(error_expecter.SawExpectedErrors()); - } -} -TEST_F(RecoverModuleTest, ColumnTypeDoubleTypes) { - ASSERT_TRUE(db_.Execute("CREATE TABLE backing(t TEXT)")); - { - sql::test::ScopedErrorExpecter error_expecter; - error_expecter.ExpectError(SQLITE_ERROR); - EXPECT_FALSE( - db_.Execute("CREATE VIRTUAL TABLE temp.recover_backing " - "USING recover(backing, t INTEGER FLOAT)")); - EXPECT_TRUE(error_expecter.SawExpectedErrors()); - } -} -TEST_F(RecoverModuleTest, ColumnTypeNotNullDoubleTypes) { - ASSERT_TRUE(db_.Execute("CREATE TABLE backing(t TEXT)")); - { - sql::test::ScopedErrorExpecter error_expecter; - error_expecter.ExpectError(SQLITE_ERROR); - EXPECT_FALSE( - db_.Execute("CREATE VIRTUAL TABLE temp.recover_backing USING recover(" - "backing, t INTEGER NOT NULL TEXT)")); - EXPECT_TRUE(error_expecter.SawExpectedErrors()); - } -} - -class RecoverModuleColumnTypeMappingTest - : public RecoverModuleTest, - public ::testing::WithParamInterface< - std::tuple<const char*, const char*, bool>> { - public: - ~RecoverModuleColumnTypeMappingTest() override = default; - - void SetUp() override { - ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); - ASSERT_TRUE( - db_.Open(temp_dir_.GetPath().AppendASCII("recovery_test.sqlite"))); - ASSERT_TRUE(DatabaseTestPeer::EnableRecoveryExtension(&db_)); - - std::string sql = - base::StringPrintf("CREATE TABLE backing(data %s)", SchemaType()); - ASSERT_TRUE(db_.Execute(sql.c_str())); - } - - void CreateRecoveryTable(const char* suffix) { - std::string sql = base::StringPrintf( - "CREATE VIRTUAL TABLE temp.recover_backing " - "USING recover(backing, data %s%s)", - SchemaType(), suffix); - ASSERT_TRUE(db_.Execute(sql.c_str())); - } - - const char* SchemaType() const { return std::get<0>(GetParam()); } - const char* ExpectedType() const { return std::get<1>(GetParam()); } - bool IsAlwaysNonNull() const { return std::get<2>(GetParam()); } - - protected: - base::ScopedTempDir temp_dir_; - sql::Database db_; -}; -TEST_P(RecoverModuleColumnTypeMappingTest, Unqualified) { - CreateRecoveryTable(""); - sql::test::ColumnInfo column_info = - sql::test::ColumnInfo::Create(&db_, "temp", "recover_backing", "data"); - EXPECT_EQ(ExpectedType(), column_info.data_type); - EXPECT_EQ("BINARY", column_info.collation_sequence); - EXPECT_EQ(IsAlwaysNonNull(), column_info.has_non_null_constraint); - EXPECT_FALSE(column_info.is_in_primary_key); - EXPECT_FALSE(column_info.is_auto_incremented); -} -TEST_P(RecoverModuleColumnTypeMappingTest, NotNull) { - CreateRecoveryTable(" NOT NULL"); - sql::test::ColumnInfo column_info = - sql::test::ColumnInfo::Create(&db_, "temp", "recover_backing", "data"); - EXPECT_EQ(ExpectedType(), column_info.data_type); - EXPECT_EQ("BINARY", column_info.collation_sequence); - EXPECT_TRUE(column_info.has_non_null_constraint); - EXPECT_FALSE(column_info.is_in_primary_key); - EXPECT_FALSE(column_info.is_auto_incremented); -} -TEST_P(RecoverModuleColumnTypeMappingTest, Strict) { - CreateRecoveryTable(" STRICT"); - sql::test::ColumnInfo column_info = - sql::test::ColumnInfo::Create(&db_, "temp", "recover_backing", "data"); - EXPECT_EQ(ExpectedType(), column_info.data_type); - EXPECT_EQ("BINARY", column_info.collation_sequence); - EXPECT_EQ(IsAlwaysNonNull(), column_info.has_non_null_constraint); - EXPECT_FALSE(column_info.is_in_primary_key); - EXPECT_FALSE(column_info.is_auto_incremented); -} -TEST_P(RecoverModuleColumnTypeMappingTest, StrictNotNull) { - CreateRecoveryTable(" STRICT NOT NULL"); - sql::test::ColumnInfo column_info = - sql::test::ColumnInfo::Create(&db_, "temp", "recover_backing", "data"); - EXPECT_EQ(ExpectedType(), column_info.data_type); - EXPECT_EQ("BINARY", column_info.collation_sequence); - EXPECT_TRUE(column_info.has_non_null_constraint); - EXPECT_FALSE(column_info.is_in_primary_key); - EXPECT_FALSE(column_info.is_auto_incremented); -} -INSTANTIATE_TEST_SUITE_P( - All, - RecoverModuleColumnTypeMappingTest, - ::testing::Values(std::make_tuple("TEXT", "TEXT", false), - std::make_tuple("INTEGER", "INTEGER", false), - std::make_tuple("FLOAT", "FLOAT", false), - std::make_tuple("BLOB", "BLOB", false), - std::make_tuple("NUMERIC", "NUMERIC", false), - std::make_tuple("ROWID", "INTEGER", true))); - -namespace { - -void GenerateAlteredTable(sql::Database* db) { - ASSERT_TRUE(db->Execute("CREATE TABLE altered(t TEXT)")); - ASSERT_TRUE(db->Execute("INSERT INTO altered VALUES('a')")); - ASSERT_TRUE(db->Execute("INSERT INTO altered VALUES('b')")); - ASSERT_TRUE(db->Execute("INSERT INTO altered VALUES('c')")); - ASSERT_TRUE(db->Execute( - "ALTER TABLE altered ADD COLUMN i INTEGER NOT NULL DEFAULT 10")); - ASSERT_TRUE(db->Execute("INSERT INTO altered VALUES('d', 5)")); -} - -} // namespace - -TEST_F(RecoverModuleTest, ReadFromAlteredTableNullDefaults) { - GenerateAlteredTable(&db_); - ASSERT_TRUE( - db_.Execute("CREATE VIRTUAL TABLE temp.recover_altered " - "USING recover(altered, t TEXT, i INTEGER)")); - - sql::Statement statement(db_.GetUniqueStatement( - "SELECT t, i FROM recover_altered ORDER BY rowid")); - ASSERT_TRUE(statement.Step()); - EXPECT_EQ("a", statement.ColumnString(0)); - EXPECT_EQ(sql::ColumnType::kNull, statement.GetColumnType(1)); - ASSERT_TRUE(statement.Step()); - EXPECT_EQ("b", statement.ColumnString(0)); - EXPECT_EQ(sql::ColumnType::kNull, statement.GetColumnType(1)); - ASSERT_TRUE(statement.Step()); - EXPECT_EQ("c", statement.ColumnString(0)); - EXPECT_EQ(sql::ColumnType::kNull, statement.GetColumnType(1)); - ASSERT_TRUE(statement.Step()); - EXPECT_EQ("d", statement.ColumnString(0)); - EXPECT_EQ(5, statement.ColumnInt(1)); - - EXPECT_FALSE(statement.Step()); -} - -TEST_F(RecoverModuleTest, ReadFromAlteredTableSkipsNulls) { - GenerateAlteredTable(&db_); - ASSERT_TRUE( - db_.Execute("CREATE VIRTUAL TABLE temp.recover_altered " - "USING recover(altered, t TEXT, i INTEGER NOT NULL)")); - - sql::Statement statement(db_.GetUniqueStatement( - "SELECT t, i FROM recover_altered ORDER BY rowid")); - ASSERT_TRUE(statement.Step()); - EXPECT_EQ("d", statement.ColumnString(0)); - EXPECT_EQ(5, statement.ColumnInt(1)); - EXPECT_FALSE(statement.Step()); -} - -namespace { - -void GenerateSizedTable(sql::Database* db, - int row_count, - const std::string& prefix) { - ASSERT_TRUE(db->Execute("CREATE TABLE sized(t TEXT, i INTEGER)")); - - sql::Transaction transaction(db); - ASSERT_TRUE(transaction.Begin()); - sql::Statement statement( - db->GetUniqueStatement("INSERT INTO sized VALUES(?, ?)")); - - for (int i = 0; i < row_count; ++i) { - statement.BindString(0, base::StringPrintf("%s%d", prefix.c_str(), i)); - statement.BindInt(1, i); - ASSERT_TRUE(statement.Run()); - statement.Reset(/* clear_bound_vars= */ true); - } - ASSERT_TRUE(transaction.Commit()); -} - -} // namespace - -TEST_F(RecoverModuleTest, LeafNodes) { - GenerateSizedTable(&db_, 10, "Leaf-node-generating line "); - ASSERT_TRUE( - db_.Execute("CREATE VIRTUAL TABLE temp.recover_sized " - "USING recover(sized, t TEXT, i INTEGER NOT NULL)")); - - sql::Statement statement( - db_.GetUniqueStatement("SELECT t, i FROM recover_sized ORDER BY rowid")); - for (int i = 0; i < 10; ++i) { - ASSERT_TRUE(statement.Step()); - EXPECT_EQ(base::StringPrintf("Leaf-node-generating line %d", i), - statement.ColumnString(0)); - EXPECT_EQ(i, statement.ColumnInt(1)); - } - EXPECT_FALSE(statement.Step()); -} - -TEST_F(RecoverModuleTest, EmptyTable) { - GenerateSizedTable(&db_, 0, ""); - ASSERT_TRUE( - db_.Execute("CREATE VIRTUAL TABLE temp.recover_sized " - "USING recover(sized, t TEXT, i INTEGER NOT NULL)")); - sql::Statement statement(db_.GetUniqueStatement( - "SELECT rowid, t, i FROM recover_sized ORDER BY rowid")); - EXPECT_FALSE(statement.Step()); -} - -TEST_F(RecoverModuleTest, SingleLevelInteriorNodes) { - GenerateSizedTable(&db_, 100, "Interior-node-generating line "); - ASSERT_TRUE( - db_.Execute("CREATE VIRTUAL TABLE temp.recover_sized " - "USING recover(sized, t TEXT, i INTEGER NOT NULL)")); - - sql::Statement statement(db_.GetUniqueStatement( - "SELECT rowid, t, i FROM recover_sized ORDER BY rowid")); - for (int i = 0; i < 100; ++i) { - ASSERT_TRUE(statement.Step()); - EXPECT_EQ(i + 1, statement.ColumnInt(0)); - EXPECT_EQ(base::StringPrintf("Interior-node-generating line %d", i), - statement.ColumnString(1)); - EXPECT_EQ(i, statement.ColumnInt(2)); - } - EXPECT_FALSE(statement.Step()); -} - -TEST_F(RecoverModuleTest, MultiLevelInteriorNodes) { - GenerateSizedTable(&db_, 5000, "Interior-node-generating line "); - ASSERT_TRUE( - db_.Execute("CREATE VIRTUAL TABLE temp.recover_sized " - "USING recover(sized, t TEXT, i INTEGER NOT NULL)")); - - sql::Statement statement(db_.GetUniqueStatement( - "SELECT rowid, t, i FROM recover_sized ORDER BY rowid")); - for (int i = 0; i < 5000; ++i) { - ASSERT_TRUE(statement.Step()); - EXPECT_EQ(i + 1, statement.ColumnInt(0)); - EXPECT_EQ(base::StringPrintf("Interior-node-generating line %d", i), - statement.ColumnString(1)); - EXPECT_EQ(i, statement.ColumnInt(2)); - } - EXPECT_FALSE(statement.Step()); -} - -namespace { - -void GenerateTypesTable(sql::Database* db) { - ASSERT_TRUE(db->Execute("CREATE TABLE types(rowtype TEXT, value)")); - - ASSERT_TRUE(db->Execute("INSERT INTO types VALUES('NULL', NULL)")); - ASSERT_TRUE(db->Execute("INSERT INTO types VALUES('INTEGER', 17)")); - ASSERT_TRUE(db->Execute("INSERT INTO types VALUES('FLOAT', 3.1415927)")); - ASSERT_TRUE(db->Execute("INSERT INTO types VALUES('TEXT', 'This is text')")); - ASSERT_TRUE(db->Execute( - "INSERT INTO types VALUES('BLOB', CAST('This is a blob' AS BLOB))")); -} - -} // namespace - -TEST_F(RecoverModuleTest, Any) { - GenerateTypesTable(&db_); - ASSERT_TRUE( - db_.Execute("CREATE VIRTUAL TABLE temp.recover_types " - "USING recover(types, rowtype TEXT, value ANY)")); - sql::Statement statement(db_.GetUniqueStatement( - "SELECT rowid, rowtype, value FROM recover_types")); - - ASSERT_TRUE(statement.Step()); - EXPECT_EQ(1, statement.ColumnInt(0)); - EXPECT_EQ("NULL", statement.ColumnString(1)); - EXPECT_EQ(sql::ColumnType::kNull, statement.GetColumnType(2)); - ASSERT_TRUE(statement.Step()); - EXPECT_EQ(2, statement.ColumnInt(0)); - EXPECT_EQ("INTEGER", statement.ColumnString(1)); - EXPECT_EQ(sql::ColumnType::kInteger, statement.GetColumnType(2)); - EXPECT_EQ(17, statement.ColumnInt(2)); - ASSERT_TRUE(statement.Step()); - EXPECT_EQ(3, statement.ColumnInt(0)); - EXPECT_EQ("FLOAT", statement.ColumnString(1)); - EXPECT_EQ(sql::ColumnType::kFloat, statement.GetColumnType(2)); - EXPECT_DOUBLE_EQ(3.1415927, statement.ColumnDouble(2)); - ASSERT_TRUE(statement.Step()); - EXPECT_EQ(4, statement.ColumnInt(0)); - EXPECT_EQ("TEXT", statement.ColumnString(1)); - EXPECT_EQ(sql::ColumnType::kText, statement.GetColumnType(2)); - EXPECT_EQ("This is text", statement.ColumnString(2)); - ASSERT_TRUE(statement.Step()); - EXPECT_EQ(5, statement.ColumnInt(0)); - EXPECT_EQ("BLOB", statement.ColumnString(1)); - EXPECT_EQ(sql::ColumnType::kBlob, statement.GetColumnType(2)); - std::string blob_text; - ASSERT_TRUE(statement.ColumnBlobAsString(2, &blob_text)); - EXPECT_EQ("This is a blob", blob_text); - - EXPECT_FALSE(statement.Step()); -} - -TEST_F(RecoverModuleTest, Integers) { - GenerateTypesTable(&db_); - ASSERT_TRUE( - db_.Execute("CREATE VIRTUAL TABLE temp.recover_types " - "USING recover(types, rowtype TEXT, value INTEGER)")); - sql::Statement statement(db_.GetUniqueStatement( - "SELECT rowid, rowtype, value FROM recover_types")); - - ASSERT_TRUE(statement.Step()); - EXPECT_EQ(1, statement.ColumnInt(0)); - EXPECT_EQ("NULL", statement.ColumnString(1)); - EXPECT_EQ(sql::ColumnType::kNull, statement.GetColumnType(2)); - ASSERT_TRUE(statement.Step()); - EXPECT_EQ(2, statement.ColumnInt(0)); - EXPECT_EQ("INTEGER", statement.ColumnString(1)); - EXPECT_EQ(sql::ColumnType::kInteger, statement.GetColumnType(2)); - EXPECT_EQ(17, statement.ColumnInt(2)); - - EXPECT_FALSE(statement.Step()); -} - -TEST_F(RecoverModuleTest, NonNullIntegers) { - GenerateTypesTable(&db_); - ASSERT_TRUE(db_.Execute( - "CREATE VIRTUAL TABLE temp.recover_types " - "USING recover(types, rowtype TEXT, value INTEGER NOT NULL)")); - sql::Statement statement(db_.GetUniqueStatement( - "SELECT rowid, rowtype, value FROM recover_types")); - - ASSERT_TRUE(statement.Step()); - EXPECT_EQ(2, statement.ColumnInt(0)); - EXPECT_EQ("INTEGER", statement.ColumnString(1)); - EXPECT_EQ(sql::ColumnType::kInteger, statement.GetColumnType(2)); - EXPECT_EQ(17, statement.ColumnInt(2)); - - EXPECT_FALSE(statement.Step()); -} - -TEST_F(RecoverModuleTest, Floats) { - GenerateTypesTable(&db_); - ASSERT_TRUE( - db_.Execute("CREATE VIRTUAL TABLE temp.recover_types " - "USING recover(types, rowtype TEXT, value FLOAT)")); - sql::Statement statement(db_.GetUniqueStatement( - "SELECT rowid, rowtype, value FROM recover_types")); - - ASSERT_TRUE(statement.Step()); - EXPECT_EQ(1, statement.ColumnInt(0)); - EXPECT_EQ("NULL", statement.ColumnString(1)); - EXPECT_EQ(sql::ColumnType::kNull, statement.GetColumnType(2)); - ASSERT_TRUE(statement.Step()); - EXPECT_EQ(2, statement.ColumnInt(0)); - EXPECT_EQ("INTEGER", statement.ColumnString(1)); - EXPECT_EQ(sql::ColumnType::kInteger, statement.GetColumnType(2)); - EXPECT_EQ(17, statement.ColumnInt(2)); - ASSERT_TRUE(statement.Step()); - EXPECT_EQ(3, statement.ColumnInt(0)); - EXPECT_EQ("FLOAT", statement.ColumnString(1)); - EXPECT_EQ(sql::ColumnType::kFloat, statement.GetColumnType(2)); - EXPECT_DOUBLE_EQ(3.1415927, statement.ColumnDouble(2)); - - EXPECT_FALSE(statement.Step()); -} - -TEST_F(RecoverModuleTest, NonNullFloats) { - GenerateTypesTable(&db_); - ASSERT_TRUE( - db_.Execute("CREATE VIRTUAL TABLE temp.recover_types " - "USING recover(types, rowtype TEXT, value FLOAT NOT NULL)")); - sql::Statement statement(db_.GetUniqueStatement( - "SELECT rowid, rowtype, value FROM recover_types")); - - ASSERT_TRUE(statement.Step()); - EXPECT_EQ(2, statement.ColumnInt(0)); - EXPECT_EQ("INTEGER", statement.ColumnString(1)); - EXPECT_EQ(sql::ColumnType::kInteger, statement.GetColumnType(2)); - EXPECT_EQ(17, statement.ColumnInt(2)); - ASSERT_TRUE(statement.Step()); - EXPECT_EQ(3, statement.ColumnInt(0)); - EXPECT_EQ("FLOAT", statement.ColumnString(1)); - EXPECT_EQ(sql::ColumnType::kFloat, statement.GetColumnType(2)); - EXPECT_DOUBLE_EQ(3.1415927, statement.ColumnDouble(2)); - - EXPECT_FALSE(statement.Step()); -} - -TEST_F(RecoverModuleTest, FloatsStrict) { - GenerateTypesTable(&db_); - ASSERT_TRUE( - db_.Execute("CREATE VIRTUAL TABLE temp.recover_types " - "USING recover(types, rowtype TEXT, value FLOAT STRICT)")); - sql::Statement statement(db_.GetUniqueStatement( - "SELECT rowid, rowtype, value FROM recover_types")); - - ASSERT_TRUE(statement.Step()); - EXPECT_EQ(1, statement.ColumnInt(0)); - EXPECT_EQ("NULL", statement.ColumnString(1)); - EXPECT_EQ(sql::ColumnType::kNull, statement.GetColumnType(2)); - ASSERT_TRUE(statement.Step()); - EXPECT_EQ(3, statement.ColumnInt(0)); - EXPECT_EQ("FLOAT", statement.ColumnString(1)); - EXPECT_EQ(sql::ColumnType::kFloat, statement.GetColumnType(2)); - EXPECT_DOUBLE_EQ(3.1415927, statement.ColumnDouble(2)); - - EXPECT_FALSE(statement.Step()); -} - -TEST_F(RecoverModuleTest, NonNullFloatsStrict) { - GenerateTypesTable(&db_); - ASSERT_TRUE(db_.Execute( - "CREATE VIRTUAL TABLE temp.recover_types " - "USING recover(types, rowtype TEXT, value FLOAT STRICT NOT NULL)")); - sql::Statement statement(db_.GetUniqueStatement( - "SELECT rowid, rowtype, value FROM recover_types")); - - ASSERT_TRUE(statement.Step()); - EXPECT_EQ(3, statement.ColumnInt(0)); - EXPECT_EQ("FLOAT", statement.ColumnString(1)); - EXPECT_EQ(sql::ColumnType::kFloat, statement.GetColumnType(2)); - EXPECT_DOUBLE_EQ(3.1415927, statement.ColumnDouble(2)); - - EXPECT_FALSE(statement.Step()); -} - -TEST_F(RecoverModuleTest, Texts) { - GenerateTypesTable(&db_); - ASSERT_TRUE( - db_.Execute("CREATE VIRTUAL TABLE temp.recover_types " - "USING recover(types, rowtype TEXT, value TEXT)")); - sql::Statement statement(db_.GetUniqueStatement( - "SELECT rowid, rowtype, value FROM recover_types")); - - ASSERT_TRUE(statement.Step()); - EXPECT_EQ(1, statement.ColumnInt(0)); - EXPECT_EQ("NULL", statement.ColumnString(1)); - EXPECT_EQ(sql::ColumnType::kNull, statement.GetColumnType(2)); - ASSERT_TRUE(statement.Step()); - EXPECT_EQ(4, statement.ColumnInt(0)); - EXPECT_EQ("TEXT", statement.ColumnString(1)); - EXPECT_EQ(sql::ColumnType::kText, statement.GetColumnType(2)); - EXPECT_EQ("This is text", statement.ColumnString(2)); - ASSERT_TRUE(statement.Step()); - EXPECT_EQ(5, statement.ColumnInt(0)); - EXPECT_EQ("BLOB", statement.ColumnString(1)); - EXPECT_EQ(sql::ColumnType::kBlob, statement.GetColumnType(2)); - std::string blob_text; - ASSERT_TRUE(statement.ColumnBlobAsString(2, &blob_text)); - EXPECT_EQ("This is a blob", blob_text); - - EXPECT_FALSE(statement.Step()); -} - -TEST_F(RecoverModuleTest, NonNullTexts) { - GenerateTypesTable(&db_); - ASSERT_TRUE( - db_.Execute("CREATE VIRTUAL TABLE temp.recover_types " - "USING recover(types, rowtype TEXT, value TEXT NOT NULL)")); - sql::Statement statement(db_.GetUniqueStatement( - "SELECT rowid, rowtype, value FROM recover_types")); - - ASSERT_TRUE(statement.Step()); - EXPECT_EQ(4, statement.ColumnInt(0)); - EXPECT_EQ("TEXT", statement.ColumnString(1)); - EXPECT_EQ(sql::ColumnType::kText, statement.GetColumnType(2)); - EXPECT_EQ("This is text", statement.ColumnString(2)); - ASSERT_TRUE(statement.Step()); - EXPECT_EQ(5, statement.ColumnInt(0)); - EXPECT_EQ("BLOB", statement.ColumnString(1)); - EXPECT_EQ(sql::ColumnType::kBlob, statement.GetColumnType(2)); - std::string blob_text; - ASSERT_TRUE(statement.ColumnBlobAsString(2, &blob_text)); - EXPECT_EQ("This is a blob", blob_text); - - EXPECT_FALSE(statement.Step()); -} - -TEST_F(RecoverModuleTest, TextsStrict) { - GenerateTypesTable(&db_); - ASSERT_TRUE( - db_.Execute("CREATE VIRTUAL TABLE temp.recover_types " - "USING recover(types, rowtype TEXT, value TEXT STRICT)")); - sql::Statement statement(db_.GetUniqueStatement( - "SELECT rowid, rowtype, value FROM recover_types")); - - ASSERT_TRUE(statement.Step()); - EXPECT_EQ(1, statement.ColumnInt(0)); - EXPECT_EQ("NULL", statement.ColumnString(1)); - EXPECT_EQ(sql::ColumnType::kNull, statement.GetColumnType(2)); - ASSERT_TRUE(statement.Step()); - EXPECT_EQ(4, statement.ColumnInt(0)); - EXPECT_EQ("TEXT", statement.ColumnString(1)); - EXPECT_EQ(sql::ColumnType::kText, statement.GetColumnType(2)); - EXPECT_EQ("This is text", statement.ColumnString(2)); - - EXPECT_FALSE(statement.Step()); -} - -TEST_F(RecoverModuleTest, NonNullTextsStrict) { - GenerateTypesTable(&db_); - ASSERT_TRUE(db_.Execute( - "CREATE VIRTUAL TABLE temp.recover_types " - "USING recover(types, rowtype TEXT, value TEXT STRICT NOT NULL)")); - sql::Statement statement(db_.GetUniqueStatement( - "SELECT rowid, rowtype, value FROM recover_types")); - - ASSERT_TRUE(statement.Step()); - EXPECT_EQ(4, statement.ColumnInt(0)); - EXPECT_EQ("TEXT", statement.ColumnString(1)); - EXPECT_EQ(sql::ColumnType::kText, statement.GetColumnType(2)); - EXPECT_EQ("This is text", statement.ColumnString(2)); - - EXPECT_FALSE(statement.Step()); -} - -TEST_F(RecoverModuleTest, Blobs) { - GenerateTypesTable(&db_); - ASSERT_TRUE( - db_.Execute("CREATE VIRTUAL TABLE temp.recover_types " - "USING recover(types, rowtype TEXT, value BLOB)")); - sql::Statement statement(db_.GetUniqueStatement( - "SELECT rowid, rowtype, value FROM recover_types")); - - ASSERT_TRUE(statement.Step()); - EXPECT_EQ(1, statement.ColumnInt(0)); - EXPECT_EQ("NULL", statement.ColumnString(1)); - EXPECT_EQ(sql::ColumnType::kNull, statement.GetColumnType(2)); - ASSERT_TRUE(statement.Step()); - EXPECT_EQ(5, statement.ColumnInt(0)); - EXPECT_EQ("BLOB", statement.ColumnString(1)); - EXPECT_EQ(sql::ColumnType::kBlob, statement.GetColumnType(2)); - std::string blob_text; - ASSERT_TRUE(statement.ColumnBlobAsString(2, &blob_text)); - EXPECT_EQ("This is a blob", blob_text); - - EXPECT_FALSE(statement.Step()); -} - -TEST_F(RecoverModuleTest, NonNullBlobs) { - GenerateTypesTable(&db_); - ASSERT_TRUE( - db_.Execute("CREATE VIRTUAL TABLE temp.recover_types " - "USING recover(types, rowtype TEXT, value BLOB NOT NULL)")); - sql::Statement statement(db_.GetUniqueStatement( - "SELECT rowid, rowtype, value FROM recover_types")); - - ASSERT_TRUE(statement.Step()); - EXPECT_EQ(5, statement.ColumnInt(0)); - EXPECT_EQ("BLOB", statement.ColumnString(1)); - EXPECT_EQ(sql::ColumnType::kBlob, statement.GetColumnType(2)); - std::string blob_text; - ASSERT_TRUE(statement.ColumnBlobAsString(2, &blob_text)); - EXPECT_EQ("This is a blob", blob_text); - - EXPECT_FALSE(statement.Step()); -} - -TEST_F(RecoverModuleTest, AnyNonNull) { - GenerateTypesTable(&db_); - ASSERT_TRUE( - db_.Execute("CREATE VIRTUAL TABLE temp.recover_types " - "USING recover(types, rowtype TEXT, value ANY NOT NULL)")); - sql::Statement statement(db_.GetUniqueStatement( - "SELECT rowid, rowtype, value FROM recover_types")); - - ASSERT_TRUE(statement.Step()); - EXPECT_EQ(2, statement.ColumnInt(0)); - EXPECT_EQ("INTEGER", statement.ColumnString(1)); - EXPECT_EQ(sql::ColumnType::kInteger, statement.GetColumnType(2)); - EXPECT_EQ(17, statement.ColumnInt(2)); - ASSERT_TRUE(statement.Step()); - EXPECT_EQ(3, statement.ColumnInt(0)); - EXPECT_EQ("FLOAT", statement.ColumnString(1)); - EXPECT_EQ(sql::ColumnType::kFloat, statement.GetColumnType(2)); - EXPECT_DOUBLE_EQ(3.1415927, statement.ColumnDouble(2)); - ASSERT_TRUE(statement.Step()); - EXPECT_EQ(4, statement.ColumnInt(0)); - EXPECT_EQ("TEXT", statement.ColumnString(1)); - EXPECT_EQ(sql::ColumnType::kText, statement.GetColumnType(2)); - EXPECT_EQ("This is text", statement.ColumnString(2)); - ASSERT_TRUE(statement.Step()); - EXPECT_EQ(5, statement.ColumnInt(0)); - EXPECT_EQ("BLOB", statement.ColumnString(1)); - EXPECT_EQ(sql::ColumnType::kBlob, statement.GetColumnType(2)); - std::string blob_text; - ASSERT_TRUE(statement.ColumnBlobAsString(2, &blob_text)); - EXPECT_EQ("This is a blob", blob_text); - - EXPECT_FALSE(statement.Step()); -} - -TEST_F(RecoverModuleTest, RowidAlias) { - GenerateTypesTable(&db_); - - // The id column is an alias for rowid, and its values get serialized as NULL. - ASSERT_TRUE(db_.Execute( - "CREATE TABLE types2(id INTEGER PRIMARY KEY, rowtype TEXT, value)")); - ASSERT_TRUE( - db_.Execute("INSERT INTO types2(id, rowtype, value) " - "SELECT rowid, rowtype, value FROM types WHERE true")); - ASSERT_TRUE(db_.Execute( - "CREATE VIRTUAL TABLE temp.recover_types2 " - "USING recover(types2, id ROWID NOT NULL, rowtype TEXT, value ANY)")); - - sql::Statement statement( - db_.GetUniqueStatement("SELECT id, rowid, rowtype, value FROM types2")); - - ASSERT_TRUE(statement.Step()); - EXPECT_EQ(1, statement.ColumnInt(0)); - EXPECT_EQ(1, statement.ColumnInt(1)); - EXPECT_EQ("NULL", statement.ColumnString(2)); - EXPECT_EQ(sql::ColumnType::kNull, statement.GetColumnType(3)); - ASSERT_TRUE(statement.Step()); - EXPECT_EQ(2, statement.ColumnInt(0)); - EXPECT_EQ(2, statement.ColumnInt(1)); - EXPECT_EQ("INTEGER", statement.ColumnString(2)); - EXPECT_EQ(17, statement.ColumnInt(3)); - ASSERT_TRUE(statement.Step()); - EXPECT_EQ(3, statement.ColumnInt(0)); - EXPECT_EQ(3, statement.ColumnInt(1)); - EXPECT_EQ("FLOAT", statement.ColumnString(2)); - EXPECT_DOUBLE_EQ(3.1415927, statement.ColumnDouble(3)); - ASSERT_TRUE(statement.Step()); - EXPECT_EQ(4, statement.ColumnInt(0)); - EXPECT_EQ(4, statement.ColumnInt(1)); - EXPECT_EQ("TEXT", statement.ColumnString(2)); - EXPECT_EQ("This is text", statement.ColumnString(3)); - ASSERT_TRUE(statement.Step()); - EXPECT_EQ(5, statement.ColumnInt(0)); - EXPECT_EQ(5, statement.ColumnInt(1)); - EXPECT_EQ("BLOB", statement.ColumnString(2)); - std::string blob_text; - ASSERT_TRUE(statement.ColumnBlobAsString(3, &blob_text)); - EXPECT_EQ("This is a blob", blob_text); - - EXPECT_FALSE(statement.Step()); -} - -TEST_F(RecoverModuleTest, IntegerEncodings) { - ASSERT_TRUE(db_.Execute("CREATE TABLE integers(value)")); - - const std::vector<int64_t> values = { - // Encoded directly in type info. - 0, - 1, - // 8-bit signed. - 2, - -2, - 127, - -128, - // 16-bit signed. - 12345, - -12345, - 32767, - -32768, - // 24-bit signed. - 1234567, - -1234567, - 8388607, - -8388608, - // 32-bit signed. - 1234567890, - -1234567890, - 2147483647, - -2147483647, - // 48-bit signed. - 123456789012345, - -123456789012345, - 140737488355327, - -140737488355327, - // 64-bit signed. - 1234567890123456789, - -1234567890123456789, - 9223372036854775807, - -9223372036854775807, - }; - sql::Statement insert( - db_.GetUniqueStatement("INSERT INTO integers VALUES(?)")); - for (int64_t value : values) { - insert.BindInt64(0, value); - ASSERT_TRUE(insert.Run()); - insert.Reset(/* clear_bound_vars= */ true); - } - - ASSERT_TRUE( - db_.Execute("CREATE VIRTUAL TABLE temp.recover_integers " - "USING recover(integers, value INTEGER)")); - sql::Statement select( - db_.GetUniqueStatement("SELECT rowid, value FROM recover_integers")); - for (size_t i = 0; i < values.size(); ++i) { - ASSERT_TRUE(select.Step()) << "Was attemping to read " << values[i]; - EXPECT_EQ(static_cast<int>(i + 1), select.ColumnInt(0)); - EXPECT_EQ(values[i], select.ColumnInt64(1)); - } - EXPECT_FALSE(select.Step()); -} - -TEST_F(RecoverModuleTest, VarintEncodings) { - const std::vector<int64_t> values = { - // 1-byte varints. - 0x00, 0x01, 0x02, 0x7e, 0x7f, - // 2-byte varints - 0x80, 0x81, 0xff, 0x0100, 0x0101, 0x1234, 0x1ffe, 0x1fff, 0x3ffe, 0x3fff, - // 3-byte varints - 0x4000, 0x4001, 0x0ffffe, 0x0fffff, 0x123456, 0x1fedcb, 0x1ffffe, - 0x1fffff, - // 4-byte varints - 0x200000, 0x200001, 0x123456, 0xfedcba, 0xfffffe, 0xffffff, 0x01234567, - 0x0fedcba9, 0x0ffffffe, 0x0fffffff, - // 5-byte varints - 0x10000000, 0x10000001, 0x12345678, 0xfedcba98, 0x01'23456789, - 0x07'fffffffe, 0x07'ffffffff, - // 6-byte varints - 0x08'00000000, 0x08'00000001, 0x12'3456789a, 0xfe'dcba9876, - 0x0123'456789ab, 0x03ff'fffffffe, 0x03ff'ffffffff, - // 7-byte varints - 0x0400'00000000, 0x0400'00000001, 0xfedc'ba987654, 0x012345'6789abcd, - 0x01ffff'fffffffe, 0x01ffff'ffffffff, - // 8-byte varints - 0x020000'00000000, 0x020000'00000001, 0x0fedcb'a9876543, - 0x123456'789abcde, 0xfedcba'98765432, 0xffffff'fffffffe, - 0xffffff'ffffffff, - // 9-byte positive varints - 0x01000000'00000000, 0x01000000'00000001, 0x12345678'9abcdef0, - 0x7fedcba9'87654321, 0x7fffffff'fffffffe, 0x7fffffff'ffffffff, - // 9-byte negative varints - -0x01, -0x02, -0x7e, -0x7f, -0x80, -0x81, -0x12345678'9abcdef0, - -0x7fedcba9'87654321, -0x7fffffff'ffffffff, - -0x7fffffff'ffffffff - 1, // -0x80000000'00000000 is not a valid literal - }; - - ASSERT_TRUE(db_.Execute("CREATE TABLE varints(value INTEGER PRIMARY KEY)")); - ASSERT_TRUE( - db_.Execute("CREATE VIRTUAL TABLE temp.recover_varints " - "USING recover(varints, value ROWID)")); - - for (int64_t value : values) { - sql::Statement insert( - db_.GetUniqueStatement("INSERT INTO varints VALUES(?)")); - insert.BindInt64(0, value); - ASSERT_TRUE(insert.Run()); - - sql::Statement select( - db_.GetUniqueStatement("SELECT rowid, value FROM recover_varints")); - ASSERT_TRUE(select.Step()) << "Was attemping to read " << value; - EXPECT_EQ(value, select.ColumnInt64(0)); - EXPECT_EQ(value, select.ColumnInt64(1)); - EXPECT_FALSE(select.Step()); - - ASSERT_TRUE(db_.Execute("DELETE FROM varints")); - } -} - -TEST_F(RecoverModuleTest, TextEncodings) { - ASSERT_TRUE(db_.Execute("CREATE TABLE encodings(t TEXT)")); - - const std::vector<std::string> values = { - "", "a", "ö", "Mjollnir", "Mjölnir", "Mjǫlnir", - "Mjölner", "Mjølner", "ハンマー", - }; - - sql::Statement insert( - db_.GetUniqueStatement("INSERT INTO encodings VALUES(?)")); - for (const std::string& value : values) { - insert.BindString(0, value); - ASSERT_TRUE(insert.Run()); - insert.Reset(/* clear_bound_vars= */ true); - } - - ASSERT_TRUE( - db_.Execute("CREATE VIRTUAL TABLE temp.recover_encodings " - "USING recover(encodings, t TEXT)")); - sql::Statement select( - db_.GetUniqueStatement("SELECT rowid, t FROM recover_encodings")); - for (size_t i = 0; i < values.size(); ++i) { - ASSERT_TRUE(select.Step()); - EXPECT_EQ(static_cast<int>(i + 1), select.ColumnInt(0)); - EXPECT_EQ(values[i], select.ColumnString(1)); - } - EXPECT_FALSE(select.Step()); -} - -TEST_F(RecoverModuleTest, BlobEncodings) { - ASSERT_TRUE(db_.Execute("CREATE TABLE blob_encodings(t BLOB)")); - - const std::vector<std::vector<uint8_t>> values = { - {}, {0x00}, {0x01}, - {0x42}, {0xff}, {0x00, 0x00}, - {0x00, 0x01}, {0x00, 0xff}, {0x42, 0x43, 0x44, 0x45, 0x46}, - }; - - sql::Statement insert( - db_.GetUniqueStatement("INSERT INTO blob_encodings VALUES(?)")); - for (const std::vector<uint8_t>& value : values) { - insert.BindBlob(0, value); - ASSERT_TRUE(insert.Run()); - insert.Reset(/* clear_bound_vars= */ true); - } - - ASSERT_TRUE( - db_.Execute("CREATE VIRTUAL TABLE temp.recover_blob_encodings " - "USING recover(blob_encodings, t BLOB)")); - sql::Statement select( - db_.GetUniqueStatement("SELECT rowid, t FROM recover_blob_encodings")); - for (size_t i = 0; i < values.size(); ++i) { - ASSERT_TRUE(select.Step()); - EXPECT_EQ(static_cast<int>(i + 1), select.ColumnInt(0)); - - std::vector<uint8_t> column_value; - EXPECT_TRUE(select.ColumnBlobAsVector(1, &column_value)); - EXPECT_EQ(values[i], column_value); - } - EXPECT_FALSE(select.Step()); -} - -namespace { - -std::string RandomString(int size) { - std::mt19937 rng; - std::uniform_int_distribution<int> random_char(32, 127); - std::string large_value; - large_value.reserve(size); - for (int i = 0; i < size; ++i) - large_value.push_back(random_char(rng)); - return large_value; -} - -void CheckLargeValueRecovery(sql::Database* db, int value_size) { - const std::string large_value = RandomString(value_size); - - ASSERT_TRUE(db->Execute("CREATE TABLE overflow(t TEXT)")); - sql::Statement insert( - db->GetUniqueStatement("INSERT INTO overflow VALUES(?)")); - insert.BindString(0, large_value); - ASSERT_TRUE(insert.Run()); - - ASSERT_TRUE(db->Execute("VACUUM")); - - ASSERT_TRUE( - db->Execute("CREATE VIRTUAL TABLE temp.recover_overflow " - "USING recover(overflow, t TEXT)")); - sql::Statement select( - db->GetUniqueStatement("SELECT rowid, t FROM recover_overflow")); - ASSERT_TRUE(select.Step()); - EXPECT_EQ(1, select.ColumnInt(0)); - EXPECT_EQ(large_value, select.ColumnString(1)); -} - -bool HasEnabledAutoVacuum(sql::Database* db) { - sql::Statement pragma(db->GetUniqueStatement("PRAGMA auto_vacuum")); - EXPECT_TRUE(pragma.Step()); - return pragma.ColumnInt(0) != 0; -} - -// The overhead in the table page is: -// * 35 bytes - the cell size cutoff that causes a cell's payload to overflow -// * 1 byte - record header size -// * 2-3 bytes - type ID for the text column -// - texts of 58-8185 bytes use 2 bytes -// - texts of 8186-1048569 bytes use 3 bytes -// -// The overhead below assumes a 2-byte string type ID. -constexpr int kRecordOverhead = 38; - -// Each overflow page uses 4 bytes to store the pointer to the next page. -constexpr int kOverflowOverhead = 4; - -} // namespace - -TEST_F(RecoverModuleTest, ValueWithoutOverflow) { - CheckLargeValueRecovery(&db_, db_.page_size() - kRecordOverhead); - int auto_vacuum_pages = HasEnabledAutoVacuum(&db_) ? 1 : 0; - ASSERT_EQ(2 + auto_vacuum_pages, sql::test::GetPageCount(&db_)) - << "Database should have a root page and a leaf page"; -} - -TEST_F(RecoverModuleTest, ValueWithOneByteOverflow) { - CheckLargeValueRecovery(&db_, db_.page_size() - kRecordOverhead + 1); - int auto_vacuum_pages = HasEnabledAutoVacuum(&db_) ? 1 : 0; - ASSERT_EQ(3 + auto_vacuum_pages, sql::test::GetPageCount(&db_)) - << "Database should have a root page, a leaf page, and 1 overflow page"; -} - -TEST_F(RecoverModuleTest, ValueWithOneOverflowPage) { - CheckLargeValueRecovery( - &db_, db_.page_size() - kRecordOverhead + db_.page_size() / 2); - int auto_vacuum_pages = HasEnabledAutoVacuum(&db_) ? 1 : 0; - ASSERT_EQ(3 + auto_vacuum_pages, sql::test::GetPageCount(&db_)) - << "Database should have a root page, a leaf page, and 1 overflow page"; -} - -TEST_F(RecoverModuleTest, ValueWithOneFullOverflowPage) { - CheckLargeValueRecovery(&db_, db_.page_size() - kRecordOverhead + - db_.page_size() - kOverflowOverhead); - int auto_vacuum_pages = HasEnabledAutoVacuum(&db_) ? 1 : 0; - ASSERT_EQ(3 + auto_vacuum_pages, sql::test::GetPageCount(&db_)) - << "Database should have a root page, a leaf page, and 1 overflow page"; -} - -TEST_F(RecoverModuleTest, ValueWithOneByteSecondOverflowPage) { - CheckLargeValueRecovery(&db_, db_.page_size() - kRecordOverhead + - db_.page_size() - kOverflowOverhead + 1); - int auto_vacuum_pages = HasEnabledAutoVacuum(&db_) ? 1 : 0; - ASSERT_EQ(4 + auto_vacuum_pages, sql::test::GetPageCount(&db_)) - << "Database should have a root page, a leaf page, and 2 overflow pages"; -} - -TEST_F(RecoverModuleTest, ValueWithTwoOverflowPages) { - CheckLargeValueRecovery(&db_, db_.page_size() - kRecordOverhead + - db_.page_size() - kOverflowOverhead + - db_.page_size() / 2); - int auto_vacuum_pages = HasEnabledAutoVacuum(&db_) ? 1 : 0; - ASSERT_EQ(4 + auto_vacuum_pages, sql::test::GetPageCount(&db_)) - << "Database should have a root page, a leaf page, and 2 overflow pages"; -} - -TEST_F(RecoverModuleTest, ValueWithTwoFullOverflowPages) { - // This value is large enough that the varint encoding of its type ID takes up - // 3 bytes, instead of 2. - CheckLargeValueRecovery(&db_, db_.page_size() - kRecordOverhead + - (db_.page_size() - kOverflowOverhead) * 2 - - 1); - int auto_vacuum_pages = HasEnabledAutoVacuum(&db_) ? 1 : 0; - ASSERT_EQ(4 + auto_vacuum_pages, sql::test::GetPageCount(&db_)) - << "Database should have a root page, a leaf page, and 2 overflow pages"; -} - -} // namespace recover -} // namespace sql
diff --git a/sql/recover_module/pager.cc b/sql/recover_module/pager.cc deleted file mode 100644 index 7f5b368..0000000 --- a/sql/recover_module/pager.cc +++ /dev/null
@@ -1,123 +0,0 @@ -// Copyright 2019 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "sql/recover_module/pager.h" - -#include <limits> - -#include "base/logging.h" -#include "sql/recover_module/table.h" -#include "third_party/sqlite/sqlite3.h" - -namespace sql { -namespace recover { - -constexpr int DatabasePageReader::kHighestInvalidPageId; -constexpr int DatabasePageReader::kMinPageSize; -constexpr int DatabasePageReader::kMaxPageSize; -constexpr int DatabasePageReader::kDatabaseHeaderSize; -constexpr int DatabasePageReader::kMinUsablePageSize; -constexpr int DatabasePageReader::kMaxPageId; - -static_assert(DatabasePageReader::kMaxPageId <= std::numeric_limits<int>::max(), - "ints are not appropriate for representing page IDs"); - -DatabasePageReader::DatabasePageReader(VirtualTable* table) - : page_data_(std::make_unique<uint8_t[]>(table->page_size())), - table_(table) { - CHECK(table != nullptr); - CHECK(IsValidPageSize(table->page_size())); -} - -DatabasePageReader::~DatabasePageReader() = default; - -int DatabasePageReader::ReadPage(int page_id) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - CHECK(IsValidPageId(page_id)); - CHECK_LE(page_id, kMaxPageId); - - if (page_id_ == page_id) - return SQLITE_OK; - - sqlite3_file* const sqlite_file = table_->SqliteFile(); - const int page_size = table_->page_size(); - const int page_offset = (page_id == 1) ? kDatabaseHeaderSize : 0; - const int read_size = page_size - page_offset; - static_assert(kMinPageSize >= kDatabaseHeaderSize, - "The |read_size| computation above may overflow"); - - page_size_ = read_size; - CHECK_GE(page_size_, kMinUsablePageSize); - CHECK_LE(page_size_, kMaxPageSize); - - const int64_t read_offset = - static_cast<int64_t>(page_id - 1) * page_size + page_offset; - static_assert(static_cast<int64_t>(kMaxPageId - 1) * kMaxPageSize + - kDatabaseHeaderSize <= - std::numeric_limits<int64_t>::max(), - "The |read_offset| computation above may overflow"); - - int sqlite_status = - RawRead(sqlite_file, read_size, read_offset, page_data_.get()); - - // |page_id_| needs to be set to kHighestInvalidPageId if the read failed. - // Otherwise, future ReadPage() calls with the previous |page_id_| value - // would return SQLITE_OK, but the page data buffer might be trashed. - page_id_ = (sqlite_status == SQLITE_OK) ? page_id : kHighestInvalidPageId; - return sqlite_status; -} - -// static -int DatabasePageReader::RawRead(sqlite3_file* sqlite_file, - int read_size, - int64_t read_offset, - uint8_t* result_buffer) { - CHECK(sqlite_file != nullptr); - CHECK_GE(read_size, 0); - CHECK_GE(read_offset, 0); - CHECK(result_buffer != nullptr); - - // Retry the I/O operations a few times if they fail. This is especially - // useful when recovering from database corruption. - static constexpr int kRetryCount = 10; - - int sqlite_status; - bool got_lock = false; - for (int i = kRetryCount; i > 0; --i) { - sqlite_status = - sqlite_file->pMethods->xLock(sqlite_file, SQLITE_LOCK_SHARED); - if (sqlite_status == SQLITE_OK) { - got_lock = true; - break; - } - } - - // Try reading even if we don't have a shared lock on the database. If the - // read fails, the database page is completely skipped, so any data we might - // get from the read is better than nothing. - for (int i = kRetryCount; i > 0; --i) { - sqlite_status = sqlite_file->pMethods->xRead(sqlite_file, result_buffer, - read_size, read_offset); - if (sqlite_status == SQLITE_OK) - break; - if (sqlite_status == SQLITE_IOERR_SHORT_READ) { - // The read succeeded, but hit EOF. The extra bytes in the page buffer - // are set to zero. This is acceptable for our purposes. - sqlite_status = SQLITE_OK; - break; - } - } - - if (got_lock) { - // TODO(pwnall): This logic was ported from the old C-in-SQLite-style patch. - // Dropping the lock here is incorrect, because the file - // descriptor is shared with the SQLite pager, which may - // expect to be holding a lock. - sqlite_file->pMethods->xUnlock(sqlite_file, SQLITE_LOCK_NONE); - } - return sqlite_status; -} - -} // namespace recover -} // namespace sql
diff --git a/sql/recover_module/pager.h b/sql/recover_module/pager.h deleted file mode 100644 index d60c5a4..0000000 --- a/sql/recover_module/pager.h +++ /dev/null
@@ -1,155 +0,0 @@ -// Copyright 2019 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SQL_RECOVER_MODULE_PAGER_H_ -#define SQL_RECOVER_MODULE_PAGER_H_ - -#include <cstdint> -#include <memory> -#include <ostream> - -#include "base/check_op.h" -#include "base/memory/raw_ptr.h" -#include "base/sequence_checker.h" - -struct sqlite3_file; - -namespace sql { -namespace recover { - -class VirtualTable; - -// Page reader for SQLite database files. -// -// Contains logic for retrying reads on I/O errors. Caches the last read page, -// to facilitate layering in higher-level code. -// -// Instances should be members of high-level constructs such as tables or -// cursors. Instances are not thread-safe. -class DatabasePageReader { - public: - // Guaranteed to be an invalid page number. NB: use `IsValidPageId()` to - // validate a page id. - static constexpr int kHighestInvalidPageId = 0; - - // Minimum database page size supported by SQLite. - static constexpr int kMinPageSize = 512; - // Maximum database page size supported by SQLite. - static constexpr int kMaxPageSize = 65536; - - // The size of the header at the beginning of a SQLite database file. - static constexpr int kDatabaseHeaderSize = 100; - - // Minimum usable size of a SQLite database page. - // - // This differs from |kMinPageSize| because the first page in a SQLite - // database starts with the database header. That page's header starts right - // after the database header. - static constexpr int kMinUsablePageSize = kMinPageSize - kDatabaseHeaderSize; - - // Largest valid page ID in a SQLite database. - // - // This is the maximum value of SQLITE_MAX_PAGE_COUNT plus 1, because page IDs - // start at 1. The numerical value, which is the same as - // std::numeric_limits<int32_t>::max() - 1, is quoted from - // https://www.sqlite.org/limits.html. - static constexpr int kMaxPageId = 2147483646 + 1; - - // Creates a reader that uses the SQLite VFS backing |table|. - // - // |table| must outlive this instance. - explicit DatabasePageReader(VirtualTable* table); - ~DatabasePageReader(); - - DatabasePageReader(const DatabasePageReader&) = delete; - DatabasePageReader& operator=(const DatabasePageReader&) = delete; - - // The page data read by the last ReadPage() call. - // - // The page data is undefined if the last ReadPage() call failed, or if - // ReadPage() was never called. - const uint8_t* page_data() const { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - CHECK(IsValidPageId(page_id_)) - << "Successful ReadPage() required before accessing pager state"; - return page_data_.get(); - } - - // The number of bytes in the page read by the last ReadPage() call. - // - // The result is guaranteed to be in [kMinUsablePageSize, kMaxPageSize]. - // - // In general, pages have the same size. However, the first page in each - // database is smaller, because it starts after the database header. - // - // The result is undefined if the last ReadPage() call failed, or if - // ReadPage() was never called. - int page_size() const { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - CHECK(IsValidPageId(page_id_)) - << "Successful ReadPage() required before accessing pager state"; - CHECK_GE(page_size_, kMinUsablePageSize); - CHECK_LE(page_size_, kMaxPageSize); - return page_size_; - } - - // Returns the |page_id| argument for the last successful ReadPage() call. - // - // The result is undefined if the last ReadPage() call failed, or if - // ReadPage() was never called. - int page_id() const { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - CHECK(IsValidPageId(page_id_)) - << "Successful ReadPage() required before accessing pager state"; - return page_id_; - } - - // Reads a database page. Returns a SQLite status code. - // - // SQLite uses 1-based indexing for its page numbers. - // - // This method is idempotent, because it caches its result. - int ReadPage(int page_id); - - // True if the given database page size is supported by SQLite. - static constexpr bool IsValidPageSize(int page_size) noexcept { - // SQLite page sizes must be powers of two. - return page_size >= kMinPageSize && page_size <= kMaxPageSize && - (page_size & (page_size - 1)) == 0; - } - - // True if the given number is a valid SQLite database page ID. - // - // Valid page IDs are positive 32-bit integers. - static constexpr bool IsValidPageId(int64_t page_id) noexcept { - return page_id > kHighestInvalidPageId && page_id <= kMaxPageId; - } - - // Low-level read wrapper. Returns a SQLite error code. - // - // |read_size| and |read_offset| are expressed in bytes. - static int RawRead(sqlite3_file* sqlite_file, - int read_size, - int64_t read_offset, - uint8_t* buffer); - - private: - // Points to the last page successfully read by ReadPage(). - // Set to kHighestInvalidPageId if the last read was unsuccessful. - int page_id_ = kHighestInvalidPageId; - // Stores the bytes of the last page successfully read by ReadPage(). - // The content is undefined if the last call to ReadPage() did not succeed. - const std::unique_ptr<uint8_t[]> page_data_; - // Raw pointer usage is acceptable because this instance's owner is expected - // to ensure that the VirtualTable outlives this. - const raw_ptr<VirtualTable> table_; - int page_size_ = 0; - - SEQUENCE_CHECKER(sequence_checker_); -}; - -} // namespace recover -} // namespace sql - -#endif // SQL_RECOVER_MODULE_PAGER_H_
diff --git a/sql/recover_module/parsing.cc b/sql/recover_module/parsing.cc deleted file mode 100644 index b1c7289..0000000 --- a/sql/recover_module/parsing.cc +++ /dev/null
@@ -1,218 +0,0 @@ -// Copyright 2019 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "sql/recover_module/parsing.h" - -#include <cstddef> -#include <ostream> -#include <tuple> -#include <utility> - -#include <optional> -#include "base/check.h" -#include "base/notreached.h" -#include "base/strings/strcat.h" -#include "base/strings/string_piece.h" -#include "sql/recover_module/record.h" - -namespace sql { -namespace recover { - -namespace { - -// This module defines whitespace as space (0x20). -constexpr bool IsWhiteSpace(char character) { - return (character == ' '); -} - -// Splits a token out of a SQL string representing a column type. -// -// Tokens are separated by space (0x20) characters. -// -// The SQL must not start with a space character. -// -// Returns the token and the rest of the SQL string. Consumes the space after -// the returned token -- the rest of the SQL string will not start with space. -std::pair<base::StringPiece, base::StringPiece> SplitToken( - base::StringPiece sql) { - DCHECK(sql.empty() || !IsWhiteSpace(sql[0])); - - size_t token_end = 0; - while (token_end < sql.length() && !IsWhiteSpace(sql[token_end])) - ++token_end; - - size_t split = token_end; - while (split < sql.length() && IsWhiteSpace(sql[split])) - ++split; - - return {sql.substr(0, token_end), sql.substr(split)}; -} - -// Column types. -constexpr base::StringPiece kIntegerSql("INTEGER"); -constexpr base::StringPiece kFloatSql("FLOAT"); -constexpr base::StringPiece kTextSql("TEXT"); -constexpr base::StringPiece kBlobSql("BLOB"); -constexpr base::StringPiece kNumericSql("NUMERIC"); -constexpr base::StringPiece kRowidSql("ROWID"); -constexpr base::StringPiece kAnySql("ANY"); - -// SQL keywords recognized by the recovery module. -constexpr base::StringPiece kStrictSql("STRICT"); -constexpr base::StringPiece kNonNullSql1("NOT"); -constexpr base::StringPiece kNonNullSql2("NULL"); - -std::optional<ModuleColumnType> ParseColumnType( - base::StringPiece column_type_sql) { - if (column_type_sql == kIntegerSql) - return ModuleColumnType::kInteger; - if (column_type_sql == kFloatSql) - return ModuleColumnType::kFloat; - if (column_type_sql == kTextSql) - return ModuleColumnType::kText; - if (column_type_sql == kBlobSql) - return ModuleColumnType::kBlob; - if (column_type_sql == kNumericSql) - return ModuleColumnType::kNumeric; - if (column_type_sql == kRowidSql) - return ModuleColumnType::kRowId; - if (column_type_sql == kAnySql) - return ModuleColumnType::kAny; - - return std::nullopt; -} - -// Returns a view into a SQL string representing the column type. -// -// The backing string is guaranteed to live for as long as the process runs. -base::StringPiece ColumnTypeToSql(ModuleColumnType column_type) { - switch (column_type) { - case ModuleColumnType::kInteger: - return kIntegerSql; - case ModuleColumnType::kFloat: - return kFloatSql; - case ModuleColumnType::kText: - return kTextSql; - case ModuleColumnType::kBlob: - return kBlobSql; - case ModuleColumnType::kNumeric: - return kNumericSql; - case ModuleColumnType::kRowId: - return kIntegerSql; // rowids are ints. - case ModuleColumnType::kAny: - return base::StringPiece(); - } - NOTREACHED(); -} - -} // namespace - -std::string RecoveredColumnSpec::ToCreateTableSql() const { - base::StringPiece not_null_sql = (is_non_null) ? " NOT NULL" : ""; - return base::StrCat( - {this->name, " ", ColumnTypeToSql(this->type), not_null_sql}); -} - -bool RecoveredColumnSpec::IsAcceptableValue(ValueType value_type) const { - if (value_type == ValueType::kNull) - return !is_non_null || type == ModuleColumnType::kRowId; - if (type == ModuleColumnType::kAny) - return true; - - if (value_type == ValueType::kInteger) { - return type == ModuleColumnType::kInteger || - (!is_strict && type == ModuleColumnType::kFloat); - } - if (value_type == ValueType::kFloat) { - return type == ModuleColumnType::kFloat || - (!is_strict && type == ModuleColumnType::kFloat); - } - if (value_type == ValueType::kText) - return type == ModuleColumnType::kText; - if (value_type == ValueType::kBlob) { - return type == ModuleColumnType::kBlob || - (!is_strict && type == ModuleColumnType::kText); - } - NOTREACHED() << "Unimplemented value type"; - return false; -} - -RecoveredColumnSpec ParseColumnSpec(const char* sqlite_arg) { - // The result is invalid until its |name| member is set. - RecoveredColumnSpec result; - - auto [column_name, sql] = SplitToken(sqlite_arg); - if (column_name.empty()) { - // Empty column names are invalid. - DCHECK(!result.IsValid()); - return result; - } - - base::StringPiece column_type_sql; - std::tie(column_type_sql, sql) = SplitToken(sql); - std::optional<ModuleColumnType> column_type = - ParseColumnType(column_type_sql); - if (!column_type.has_value()) { - // Invalid column type. - DCHECK(!result.IsValid()); - return result; - } - result.type = column_type.value(); - - // Consume keywords. - result.is_non_null = result.type == ModuleColumnType::kRowId; - while (!sql.empty()) { - base::StringPiece token; - std::tie(token, sql) = SplitToken(sql); - - if (token == kStrictSql) { - if (result.type == ModuleColumnType::kAny) { - // STRICT is incompatible with ANY. - DCHECK(!result.IsValid()); - return result; - } - - result.is_strict = true; - continue; - } - - if (token != kNonNullSql1) { - // Invalid SQL keyword. - DCHECK(!result.IsValid()); - return result; - } - std::tie(token, sql) = SplitToken(sql); - if (token != kNonNullSql2) { - // Invalid SQL keyword. - DCHECK(!result.IsValid()); - return result; - } - result.is_non_null = true; - } - - result.name = std::string(column_name); - return result; -} - -TargetTableSpec ParseTableSpec(const char* sqlite_arg) { - base::StringPiece sql(sqlite_arg); - - size_t separator_pos = sql.find('.'); - if (separator_pos == base::StringPiece::npos) { - // The default attachment point name is "main". - return TargetTableSpec{"main", sqlite_arg}; - } - - if (separator_pos == 0) { - // Empty attachment point names are invalid. - return TargetTableSpec(); - } - - base::StringPiece db_name = sql.substr(0, separator_pos); - base::StringPiece table_name = sql.substr(separator_pos + 1); - return TargetTableSpec{std::string(db_name), std::string(table_name)}; -} - -} // namespace recover -} // namespace sql
diff --git a/sql/recover_module/parsing.h b/sql/recover_module/parsing.h deleted file mode 100644 index 71b3a0cf..0000000 --- a/sql/recover_module/parsing.h +++ /dev/null
@@ -1,72 +0,0 @@ -// Copyright 2019 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SQL_RECOVER_MODULE_PARSING_H_ -#define SQL_RECOVER_MODULE_PARSING_H_ - -#include <string> - -namespace sql { -namespace recover { - -enum class ValueType; - -// The declared data type of a virtual table column. -enum class ModuleColumnType { - kInteger, - kFloat, - kText, - kBlob, - kNumeric, - kRowId, - kAny, -}; - -// User-supplied specification for recovering a column in a corrupted table. -struct RecoveredColumnSpec { - // False if this represents a parsing error. - bool IsValid() const { return !name.empty(); } - // Column description suitable for use in a CREATE TABLE statement. - std::string ToCreateTableSql() const; - // True if the given value type is admitted by this column specification. - bool IsAcceptableValue(ValueType value_type) const; - - // Column name reported to the SQLite engine. - // - // The empty string is (ab)used for representing invalid column information, - // which can be used to communicate parsing errors. - std::string name; - // The column's canonical type. - ModuleColumnType type; - // If true, recovery will skip over null values in this column. - bool is_non_null = false; - // If true, recovery will accept values in this column with compatible types. - bool is_strict = false; -}; - -// Parses a SQLite module argument that holds a table column specification. -// -// Returns an invalid specification (IsValid() returns false) on parsing errors. -RecoveredColumnSpec ParseColumnSpec(const char* sqlite_arg); - -// User-supplied SQL table identifier. -// -// This points to the table whose data is being recovered. -struct TargetTableSpec { - // False if this represents a parsing error. - bool IsValid() const { return !table_name.empty(); } - - // The name of the attachment point of the database containing the table. - std::string db_name; - // The name of the table. Uniquely identifies a table in a database. - std::string table_name; -}; - -// Parses a SQLite module argument that points to a table. -TargetTableSpec ParseTableSpec(const char* sqlite_arg); - -} // namespace recover -} // namespace sql - -#endif // SQL_RECOVER_MODULE_PARSING_H_
diff --git a/sql/recover_module/payload.cc b/sql/recover_module/payload.cc deleted file mode 100644 index a114b75..0000000 --- a/sql/recover_module/payload.cc +++ /dev/null
@@ -1,344 +0,0 @@ -// Copyright 2019 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "sql/recover_module/payload.h" - -#include <algorithm> -#include <cstddef> -#include <limits> -#include <ostream> -#include <type_traits> - -#include "base/check_op.h" -#include "base/logging.h" -#include "sql/recover_module/btree.h" -#include "sql/recover_module/integers.h" -#include "sql/recover_module/pager.h" -#include "third_party/sqlite/sqlite3.h" - -namespace sql { -namespace recover { - -namespace { - -// The size of page IDs pointing to overflow pages. -constexpr int kPageIdSize = sizeof(int32_t); - -// The largest page header size. Inner B-tree pages use this size. -constexpr int kMaxPageOverhead = 12; - -// Maximum number of bytes in a cell used by header and trailer. -// -// The maximum overhead is incurred by cells in leaf table B-tree pages. -// * 2-byte cell pointer, in the cell pointer array -// * 9-byte row ID, for the maximum varint size -// * 8-byte payload size, (varint size for maximum payload size of 2 ** 48) -// * 4-byte first overflow page ID -constexpr int kMaxCellOverhead = 23; - -// Knob used to trade off between having more rows in a tree page and -// avoiding the use of overflow pages for large values. The knob value is -// stored in the database header. -// -// In SQLite 3.6 and above, the knob was fixed to the value below. -static constexpr int kLeafPayloadFraction = 32; - -// Denominator used by all load fractions in SQLite's B-tree logic. -static constexpr int kPayloadFractionDenominator = 255; - -// The maximum size of a payload on a leaf page. -// -// Records whose size exceeds this limit spill over to overflow pages. -// -// The return value is guaranteed to be at least -// LeafPayloadReader::kMinInlineSize, and at most the database page size. -int MaxInlinePayloadSize(int page_size) { - DCHECK_GE(page_size, DatabasePageReader::kMinUsablePageSize); - DCHECK_LE(page_size, DatabasePageReader::kMaxPageSize); - - const int max_inline_payload_size = - page_size - kMaxPageOverhead - kMaxCellOverhead; - DCHECK_GE(max_inline_payload_size, LeafPayloadReader::kMinInlineSize); - static_assert( - DatabasePageReader::kMinPageSize - kMaxPageOverhead - kMaxCellOverhead > - LeafPayloadReader::kMinInlineSize, - "The DCHECK above may fail"); - - return max_inline_payload_size; -} - -// The minimum size of a payload on a B-tree page. -// -// Records that spill over to overflow pages are guaranteed to have at least -// these many bytes stored in the B-tree page. -// -// The return value is guaranteed to be at least -// LeafPayloadReader::kMinInlineSize, and at most -// MaxInlinePayloadSize(page_size). -int MinInlinePayloadSize(int page_size) { - DCHECK_GE(page_size, DatabasePageReader::kMinUsablePageSize); - DCHECK_LE(page_size, DatabasePageReader::kMaxPageSize); - - const int min_inline_payload_size = - ((page_size - kMaxPageOverhead) * kLeafPayloadFraction) / - kPayloadFractionDenominator - - kMaxCellOverhead; - static_assert((DatabasePageReader::kMaxPageSize - kMaxPageOverhead) * - kLeafPayloadFraction <= - std::numeric_limits<int>::max(), - "The |min_inline_payload_size| computation above may overflow"); - DCHECK_GE(min_inline_payload_size, LeafPayloadReader::kMinInlineSize); - static_assert(((DatabasePageReader::kMinUsablePageSize - kMaxPageOverhead) * - kLeafPayloadFraction) / - kPayloadFractionDenominator - - kMaxCellOverhead >= - LeafPayloadReader::kMinInlineSize, - "The DCHECK above may fail"); - - // The minimum inline payload size is ((P - 12) * 32) / 255 - 23. This is - // smaller or equal to ((P - 12) * 255) / 255 - 23, which is P - 35. This is - // the maximum payload size. - DCHECK_LE(min_inline_payload_size, MaxInlinePayloadSize(page_size)); - - return min_inline_payload_size; -} - -// The maximum size of a payload on an overflow page. -// -// The return value is guaranteed to be positive, and at most equal to the page -// size. -int MaxOverflowPayloadSize(int page_size) { - DCHECK_GE(page_size, DatabasePageReader::kMinUsablePageSize); - DCHECK_LE(page_size, DatabasePageReader::kMaxPageSize); - - // Each overflow page starts with a 32-bit integer pointing to the next - // overflow page. The rest of the page stores payload bytes. - const int max_overflow_payload_size = page_size - 4; - - DCHECK_GT(max_overflow_payload_size, 0); - static_assert(DatabasePageReader::kMinUsablePageSize > 4, - "The DCHECK above may fail"); - DCHECK_LE(max_overflow_payload_size, DatabasePageReader::kMaxPageSize); - return max_overflow_payload_size; -} - -} // namespace - -LeafPayloadReader::LeafPayloadReader(DatabasePageReader* db_reader) - : db_reader_(db_reader) {} - -LeafPayloadReader::~LeafPayloadReader() = default; - -bool LeafPayloadReader::Initialize(int64_t payload_size, int payload_offset) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - payload_size_ = payload_size; - inline_payload_offset_ = payload_offset; - page_id_ = db_reader_->page_id(); - - const int page_size = db_reader_->page_size(); - - const int max_inline_payload_size = MaxInlinePayloadSize(page_size); - if (payload_size <= max_inline_payload_size) { - // The payload fits inside the page. - inline_payload_size_ = static_cast<int>(payload_size); - overflow_page_count_ = 0; - } else { - const int min_inline_payload_size = MinInlinePayloadSize(page_size); - - // The payload size is bigger than the maximum inline payload size, so it - // must be bigger than the minimum payload size. This check verifies that - // the subtractions below have non-negative results. - CHECK_GT(payload_size, min_inline_payload_size); - - // Payload sizes are upper-bounded by the page size. - static_assert( - DatabasePageReader::kMaxPageSize * 2 <= std::numeric_limits<int>::max(), - "The additions below may overflow"); - - // Ideally, all bytes in the overflow pages would be used by the payload. - // Check if this can be accomplished within the other payload constraints. - max_overflow_payload_size_ = MaxOverflowPayloadSize(page_size); - const int64_t efficient_overflow_page_count = - (payload_size - min_inline_payload_size) / max_overflow_payload_size_; - const int efficient_overflow_spill = - (payload_size - min_inline_payload_size) % max_overflow_payload_size_; - const int efficient_inline_payload_size = - min_inline_payload_size + efficient_overflow_spill; - - if (efficient_inline_payload_size <= max_inline_payload_size) { - inline_payload_size_ = efficient_inline_payload_size; - overflow_page_count_ = efficient_overflow_page_count; - DCHECK_EQ( - 0, (payload_size - inline_payload_size_) % max_overflow_payload_size_) - << "Overflow pages not fully packed"; - } else { - inline_payload_size_ = min_inline_payload_size; - overflow_page_count_ = efficient_overflow_page_count + 1; - } - - CHECK_LE(inline_payload_size_, max_inline_payload_size); - if (overflow_page_count_ != (payload_size - inline_payload_size_ + - (max_overflow_payload_size_ - 1)) / - max_overflow_payload_size_) { - LOG(ERROR) << "Incorrect overflow page count calculation"; - page_id_ = DatabasePageReader::kHighestInvalidPageId; - return false; - } - } - - CHECK_LE(inline_payload_size_, payload_size); - CHECK_LE(inline_payload_size_, page_size); - - const int first_overflow_page_id_size = - (overflow_page_count_ == 0) ? 0 : kPageIdSize; - - if (inline_payload_offset_ + inline_payload_size_ + - first_overflow_page_id_size > - page_size) { - // Corruption can result in overly large payload sizes. Reject the obvious - // case where the in-page payload extends past the end of the page. - page_id_ = DatabasePageReader::kHighestInvalidPageId; - return false; - } - - overflow_page_ids_.clear(); - overflow_page_ids_.reserve(overflow_page_count_); - return true; -} - -bool LeafPayloadReader::IsInitialized() const { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - return DatabasePageReader::IsValidPageId(page_id_); -} - -bool LeafPayloadReader::ReadPayload(int64_t offset, - int64_t size, - uint8_t* buffer) { - DCHECK(IsInitialized()) - << "Initialize() not called, or last call did not succeed"; - DCHECK_GE(offset, 0); - DCHECK_LT(offset, payload_size_); - DCHECK_GT(size, 0); - DCHECK_LE(offset + size, payload_size_); - DCHECK(buffer != nullptr); - - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - DCHECK(IsInitialized()) - << "Initialize() not called, or last call did not succeed"; - - if (offset < inline_payload_size_) { - // The read overlaps the payload bytes stored in the B-tree page. - if (db_reader_->ReadPage(page_id_) != SQLITE_OK) - return false; - const int page_size = db_reader_->page_size(); - - // The static_cast is safe because inline_payload_size_ is smaller than a - // SQLite page size, which is a 32-bit integer. - const int read_offset = inline_payload_offset_ + static_cast<int>(offset); - DCHECK_LE(read_offset, page_size); - - const int read_size = - (static_cast<int>(offset) + size <= inline_payload_size_) - ? static_cast<int>(size) - : inline_payload_size_ - static_cast<int>(offset); - DCHECK_LE(read_offset + read_size, page_size); - std::copy(db_reader_->page_data() + read_offset, - db_reader_->page_data() + read_offset + read_size, buffer); - - if (read_size == size) { - // The read is entirely inside the B-tree page. - return true; - } - - offset += read_size; - DCHECK_EQ(offset, inline_payload_size_); - DCHECK_GT(size, read_size); - size -= read_size; - buffer += read_size; - } - - // The read is entirely in overflow pages. - DCHECK_GE(offset, inline_payload_size_); - if (max_overflow_payload_size_ <= 0) { - // `max_overflow_payload_size_` should have been set in Initialize() if it's - // to be used here. See https://crbug.com/1417151. - return false; - } - while (size > 0) { - const int overflow_page_index = - (offset - inline_payload_size_) / max_overflow_payload_size_; - DCHECK_LT(overflow_page_index, overflow_page_count_); - const int overflow_page_offset = - (offset - inline_payload_size_) % max_overflow_payload_size_; - - while (overflow_page_ids_.size() <= - static_cast<size_t>(overflow_page_index)) { - if (!PopulateNextOverflowPageId()) - return false; - } - - const int page_id = overflow_page_ids_[overflow_page_index]; - if (db_reader_->ReadPage(page_id) != SQLITE_OK) - return false; - const int page_size = db_reader_->page_size(); - - const int read_offset = kPageIdSize + overflow_page_offset; - DCHECK_LE(read_offset, page_size); - - const int read_size = std::min<int64_t>(page_size - read_offset, size); - DCHECK_LE(read_offset + read_size, page_size); - std::copy(db_reader_->page_data() + read_offset, - db_reader_->page_data() + read_offset + read_size, buffer); - - offset += read_size; - DCHECK_GE(size, read_size); - size -= read_size; - buffer += read_size; - } - - return true; -} - -const uint8_t* LeafPayloadReader::ReadInlinePayload() { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - DCHECK(IsInitialized()) - << "Initialize() not called, or last call did not succeed"; - - if (db_reader_->ReadPage(page_id_) != SQLITE_OK) - return nullptr; - return db_reader_->page_data() + inline_payload_offset_; -} - -bool LeafPayloadReader::PopulateNextOverflowPageId() { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - DCHECK_LT(overflow_page_ids_.size(), - static_cast<size_t>(overflow_page_count_)); - - int page_id_offset; - if (overflow_page_ids_.empty()) { - // The first overflow page ID is right after the payload's inline bytes. - page_id_offset = inline_payload_offset_ + inline_payload_size_; - if (db_reader_->ReadPage(page_id_) != SQLITE_OK) - return false; - } else { - // Overflow pages start with the ID of the next overflow page. - page_id_offset = 0; - if (db_reader_->ReadPage(overflow_page_ids_.back()) != SQLITE_OK) - return false; - } - - DCHECK_LE(page_id_offset + kPageIdSize, db_reader_->page_size()); - const int next_page_id = - LoadBigEndianInt32(db_reader_->page_data() + page_id_offset); - if (!DatabasePageReader::IsValidPageId(next_page_id)) { - // The overflow page is corrupted. - return false; - } - - overflow_page_ids_.push_back(next_page_id); - return true; -} - -} // namespace recover -} // namespace sql
diff --git a/sql/recover_module/payload.h b/sql/recover_module/payload.h deleted file mode 100644 index 8355209..0000000 --- a/sql/recover_module/payload.h +++ /dev/null
@@ -1,150 +0,0 @@ -// Copyright 2019 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SQL_RECOVER_MODULE_PAYLOAD_H_ -#define SQL_RECOVER_MODULE_PAYLOAD_H_ - -#include <cstdint> -#include <ostream> -#include <vector> - -#include "base/check_op.h" -#include "base/memory/raw_ptr.h" -#include "base/sequence_checker.h" -#include "sql/recover_module/pager.h" - -namespace sql { -namespace recover { - -class DatabasePageReader; - -// Reads payloads (records) across B-tree pages and overflow pages. -// -// Instances are designed to be reused for reading multiple payloads. Instances -// are not thread-safe. -// -// Reading a payload is started by calling Initialize() with the information -// from LeafPageDecoder. If the call succeeds, ReadPayload() can be called -// repeatedly. -class LeafPayloadReader { - public: - // Number of payload bytes guaranteed to be on the B-tree page. - // - // The value is derived from the minimum SQLite usable page size, which is - // 380 bytes, and the formula for the minimum payload size given a usable page - // size. - static constexpr int kMinInlineSize = ((380 - 12) * 32) / 255 - 23; - - explicit LeafPayloadReader(DatabasePageReader* db_reader); - ~LeafPayloadReader(); - - LeafPayloadReader(const LeafPayloadReader&) = delete; - LeafPayloadReader& operator=(const LeafPayloadReader&) = delete; - - // Sets up the reader for a new payload. - // - // The DatabasePageReader passed to the constructor must be focused on the - // page containing the payload. - // - // This method must complete successfully before any other method on this - // class can be called. - bool Initialize(int64_t payload_size, int payload_offset); - - // True if the last call to Initialize succeeded. - bool IsInitialized() const; - - // The number of payload bytes that are stored on the B-tree page. - // - // The return value is guaranteed to be non-negative and at most - // payload_size(). - int inline_payload_size() const { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - DCHECK(IsInitialized()) - << "Initialize() not called, or last call did not succeed"; - DCHECK_LE(inline_payload_size_, payload_size_); - return inline_payload_size_; - } - - // Total payload size, in bytes. - // - // This includes the bytes stored in the B-tree page, as well as any bytes - // stored in overflow pages. - // - // The return value is guaranteed to be at least inline_payload_size(). - int payload_size() const { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - DCHECK(IsInitialized()) - << "Initialize() not called, or last call did not succeed"; - DCHECK_LE(inline_payload_size_, payload_size_); - return payload_size_; - } - - // Copies a subset of the payload into a given buffer. - // - // Returns true if the read succeeds. - // - // May only be called after a previous call to Initialize() that returns true. - bool ReadPayload(int64_t offset, int64_t size, uint8_t* buffer); - - // Pulls the B-tree containing the payload into the database reader's cache. - // - // Returns a pointer to the beginning of the payload bytes. The pointer is - // inside the database reader's buffer, and may get invalidated if the - // database reader is used. - // - // Returns null if the read operation fails. - // - // May only be called after a previous call to Initialize() that returns true. - const uint8_t* ReadInlinePayload(); - - private: - // Extends the cached list of overflow page IDs by one page. - // - // Returns false if the operation failed. Failures are due to read errors or - // database corruption. - bool PopulateNextOverflowPageId(); - - // Used to read the pages containing the payload. - // - // Raw pointer usage is acceptable because this instance's owner is expected - // to ensure that the DatabasePageReader outlives this. - const raw_ptr<DatabasePageReader> db_reader_; - - // Total size of the current payload. - int64_t payload_size_ = 0; - - // The ID of the B-tree page containing the current payload's inline bytes. - // - // Set to kHighestInvalidPageId if the reader wasn't successfully initialized. - int page_id_ = DatabasePageReader::kHighestInvalidPageId; - - // The start of the current payload's inline bytes on the B-tree page. - // - // Large payloads extend past the B-tree page containing the payload, via - // overflow pages. - int inline_payload_offset_ = 0; - - // Number of bytes in the current payload stored in its B-tree page. - // - // The rest of the payload is stored on overflow pages. - int inline_payload_size_ = 0; - - // Number of overflow pages used by the payload. - int overflow_page_count_ = 0; - - // Number of bytes in each overflow page that stores the payload. - int max_overflow_payload_size_ = 0; - - // Page IDs for all the payload's overflow pages, in order. - // - // This list is populated on-demand. - std::vector<int> overflow_page_ids_; - - SEQUENCE_CHECKER(sequence_checker_); -}; - -} // namespace recover -} // namespace sql - -#endif // SQL_RECOVER_MODULE_PAYLOAD_H_
diff --git a/sql/recover_module/record.cc b/sql/recover_module/record.cc deleted file mode 100644 index 781e39c0..0000000 --- a/sql/recover_module/record.cc +++ /dev/null
@@ -1,327 +0,0 @@ -// Copyright 2019 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "sql/recover_module/record.h" - -#include <cstddef> -#include <limits> -#include <ostream> -#include <type_traits> - -#include "base/check_op.h" -#include "base/notreached.h" -#include "sql/recover_module/integers.h" -#include "sql/recover_module/payload.h" -#include "third_party/sqlite/sqlite3.h" - -namespace sql { -namespace recover { - -RecordReader::RecordReader(LeafPayloadReader* payload_reader, int column_count) - : payload_reader_(payload_reader), column_count_(column_count) { - DCHECK(payload_reader != nullptr); - DCHECK_GT(column_count, 0); - value_headers_.reserve(column_count); -} - -RecordReader::~RecordReader() = default; - -namespace { - -// Value type indicating a null. -constexpr int kNullType = 0; -// Value type indicating a 1-byte signed integer. -constexpr int kInt1Type = 1; -// Value type indicating a 2-byte signed big-endian integer. -constexpr int kInt2Type = 2; -// Value type indicating a 3-byte signed big-endian integer. -constexpr int kInt3Type = 3; -// Value type indicating a 4-byte signed big-endian integer. -constexpr int kInt4Type = 4; -// Value type indicating a 6-byte signed big-endian integer. -constexpr int kInt6Type = 5; -// Value type indicating an 8-byte signed big-endian integer. -constexpr int kInt8Type = 6; -// Value type indicating a big-endian IEEE 754 64-bit floating point number. -constexpr int kDoubleType = 7; -// Value type indicating the integer 0 (zero). -constexpr int kIntZeroType = 8; -// Value type indicating the integer 1 (one). -constexpr int kIntOneType = 9; -// Value types greater than or equal to this indicate blobs or text. -constexpr int kMinBlobOrStringType = 12; - -// The return value of ParseHeaderType below. -struct ParsedHeaderType { - // True for the special value used to communicate a parsing error. - bool IsInvalid() const { - return type == ValueType::kNull && has_inline_value; - } - - const ValueType type; - const int64_t size; - const int8_t inline_value; - const bool has_inline_value; -}; - -// Decodes a type identifier in a SQLite record header. -// -// The type identifier includes the type and the size. -// -// Returns {kNull, 1} when parsing fails. Null values never require any extra -// bytes, so this special return value will never occur during normal -// processing. -ParsedHeaderType ParseHeaderType(int64_t encoded_type) { - static constexpr int8_t kNoInlineValue = 0; - - if (encoded_type == kNullType) - return {ValueType::kNull, 0, kNoInlineValue, false}; - if (encoded_type == kInt1Type) - return {ValueType::kInteger, 1, kNoInlineValue, false}; - if (encoded_type == kInt2Type) - return {ValueType::kInteger, 2, kNoInlineValue, false}; - if (encoded_type == kInt3Type) - return {ValueType::kInteger, 3, kNoInlineValue, false}; - if (encoded_type == kInt4Type) - return {ValueType::kInteger, 4, kNoInlineValue, false}; - if (encoded_type == kInt6Type) - return {ValueType::kInteger, 6, kNoInlineValue, false}; - if (encoded_type == kInt8Type) - return {ValueType::kInteger, 8, kNoInlineValue, false}; - if (encoded_type == kDoubleType) - return {ValueType::kFloat, 8, kNoInlineValue, false}; - if (encoded_type == kIntZeroType) - return {ValueType::kInteger, 0, 0, true}; - if (encoded_type == kIntOneType) - return {ValueType::kInteger, 0, 1, true}; - - if (encoded_type < kMinBlobOrStringType) { - // Types between |kIntOneType| and |kMinBlobOrStringType| are reserved for - // SQLite internal usage, and should not appear in persistent databases. - // This shows database corruption. - return {ValueType::kNull, 0, kNoInlineValue, true}; - } - - // Blobs and texts take alternating numbers starting at 12. - encoded_type -= kMinBlobOrStringType; - const ValueType value_type = - (encoded_type & 1) == 0 ? ValueType::kBlob : ValueType::kText; - const int64_t value_size = encoded_type >> 1; - return {value_type, value_size, kNoInlineValue, false}; -} - -} // namespace - -bool RecordReader::Initialize() { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - - // The size of |value_headers_| is used in DCHECKs to track whether - // Initialize() succeeded. - value_headers_.clear(); - - int64_t next_value_offset = InitializeHeaderBuffer(); - if (next_value_offset == 0) - return false; - - const uint8_t* header_pointer = header_buffer_.data(); - const uint8_t* header_end = header_buffer_.data() + header_buffer_.size(); - - for (int i = 0; i < column_count_; ++i) { - int64_t encoded_type; - if (header_pointer == header_end) { - // SQLite versions built with SQLITE_ENABLE_NULL_TRIM don't store trailing - // null type IDs in the header. - encoded_type = kNullType; - } else { - std::tie(encoded_type, header_pointer) = - ParseVarint(header_pointer, header_end); - } - - ParsedHeaderType parsed_type = ParseHeaderType(encoded_type); - if (parsed_type.IsInvalid()) { - // Parsing failed. The record is corrupted. - return false; - } - value_headers_.emplace_back(next_value_offset, parsed_type.size, - parsed_type.type, parsed_type.inline_value, - parsed_type.has_inline_value); - - next_value_offset += parsed_type.size; - } - - DCHECK_EQ(value_headers_.size(), static_cast<size_t>(column_count_)); - return true; -} - -bool RecordReader::IsInitialized() const { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - return value_headers_.size() == static_cast<size_t>(column_count_) && - payload_reader_->IsInitialized(); -} - -ValueType RecordReader::GetValueType(int column_index) const { - DCHECK(IsInitialized()); - DCHECK_GE(column_index, 0); - DCHECK_LT(static_cast<size_t>(column_index), value_headers_.size()); - - return value_headers_[column_index].type; -} - -namespace { - -// Deallocates buffers passed to sqlite3_result_{blob,text}64(). -void ValueBytesDeleter(void* buffer) { - DCHECK(buffer != nullptr); - uint8_t* value_bytes = reinterpret_cast<uint8_t*>(buffer); - delete[] value_bytes; -} - -} // namespace - -bool RecordReader::ReadValue(int column_index, - sqlite3_context* receiver) const { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - DCHECK(IsInitialized()); - DCHECK_GE(column_index, 0); - DCHECK_LT(static_cast<size_t>(column_index), value_headers_.size()); - DCHECK(receiver != nullptr); - - const ValueHeader& header = value_headers_[column_index]; - - const int64_t offset = header.offset; - const int64_t size = header.size; - - if (header.type == ValueType::kNull) { - DCHECK_EQ(size, 0); - DCHECK(!header.has_inline_value); - - sqlite3_result_null(receiver); - return true; - } - - if (header.type == ValueType::kInteger) { - if (header.has_inline_value) { - sqlite3_result_int(receiver, header.inline_value); - return true; - } - - uint8_t value_bytes[8]; - DCHECK_GT(size, 0); - DCHECK_LE(size, static_cast<int64_t>(sizeof(value_bytes))); - // SQLite integers are big-endian, so the least significant bytes are at the - // end of the integer's buffer. - uint8_t* const first_read_byte = value_bytes + 8 - size; - if (!payload_reader_->ReadPayload(offset, size, first_read_byte)) - return false; - - // Sign-extend the number. - const uint8_t sign_byte = (*first_read_byte & 0x80) ? 0xff : 0; - for (uint8_t* sign_extended_byte = &value_bytes[0]; - sign_extended_byte < first_read_byte; ++sign_extended_byte) { - *sign_extended_byte = sign_byte; - } - - const int64_t value = LoadBigEndianInt64(value_bytes); - sqlite3_result_int64(receiver, value); - return true; - } - - if (header.type == ValueType::kFloat) { - DCHECK_EQ(header.size, static_cast<int64_t>(sizeof(double))); - DCHECK(!header.has_inline_value); - - union { - double fp; - int64_t integer; - uint8_t bytes[8]; - } value; - static_assert(sizeof(double) == 8, - "double is not the correct type to represent SQLite floats"); - if (!payload_reader_->ReadPayload(header.offset, sizeof(double), - reinterpret_cast<uint8_t*>(&value))) { - return false; - } - // SQLite's doubles are big-endian. - value.integer = LoadBigEndianInt64(value.bytes); - sqlite3_result_double(receiver, value.fp); - return true; - } - - if (header.type == ValueType::kBlob || header.type == ValueType::kText) { - DCHECK_GE(header.size, 0); - DCHECK(!header.has_inline_value); - - uint8_t* const value_bytes = new uint8_t[size]; - if (size > 0 && !payload_reader_->ReadPayload(offset, size, value_bytes)) { - delete[] value_bytes; - return false; - } - if (header.type == ValueType::kBlob) { - sqlite3_result_blob64(receiver, value_bytes, static_cast<uint64_t>(size), - &ValueBytesDeleter); - } else { - DCHECK_EQ(header.type, ValueType::kText); - - const unsigned char encoding = SQLITE_UTF8; - sqlite3_result_text64(receiver, reinterpret_cast<char*>(value_bytes), - static_cast<uint64_t>(size), &ValueBytesDeleter, - encoding); - } - return true; - } - - NOTREACHED() << "Invalid value type"; - return false; -} - -void RecordReader::Reset() { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - value_headers_.clear(); -} - -int64_t RecordReader::InitializeHeaderBuffer() { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - - const uint8_t* const inline_payload_start = - payload_reader_->ReadInlinePayload(); - if (inline_payload_start == nullptr) { - // Read failure. - return 0; - } - - const int64_t inline_payload_size = payload_reader_->inline_payload_size(); - const uint8_t* const inline_payload_end = - inline_payload_start + inline_payload_size; - int64_t header_size; - const uint8_t* payload_header_start; - std::tie(header_size, payload_header_start) = - ParseVarint(inline_payload_start, inline_payload_end); - - if (header_size < 0 || header_size > payload_reader_->payload_size()) { - // The header is bigger than the entire record. This record is corrupted. - return 0; - } - - int header_size_size = payload_header_start - inline_payload_start; - static_assert(std::numeric_limits<int>::max() > kMaxVarintSize, - "The |header_size_size| computation above may overflow"); - - // The header size varint is included in the header size computation. - const int64_t header_data_size = header_size - header_size_size; - if (header_data_size < 0) { - return 0; - } - - header_buffer_.resize(header_data_size); - if (!payload_reader_->ReadPayload(header_size_size, header_data_size, - header_buffer_.data())) { - // Read failure. - return 0; - } - - return header_size; -} - -} // namespace recover -} // namespace sql
diff --git a/sql/recover_module/record.h b/sql/recover_module/record.h deleted file mode 100644 index c039ae669..0000000 --- a/sql/recover_module/record.h +++ /dev/null
@@ -1,152 +0,0 @@ -// Copyright 2019 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SQL_RECOVER_MODULE_RECORD_H_ -#define SQL_RECOVER_MODULE_RECORD_H_ - -#include <cstdint> -#include <vector> - -#include "base/memory/raw_ptr.h" -#include "base/sequence_checker.h" - -struct sqlite3_context; - -namespace sql { -namespace recover { - -// The effective type of a column's value in a SQLite row. -enum class ValueType { - kNull, - kInteger, - kFloat, - kText, - kBlob, -}; - -class LeafPayloadReader; - -// Reads records from SQLite B-trees. -// -// Instances are designed to be reused for reading multiple records. Instances -// are not thread-safe. -// -// A record is a list of column values. SQLite uses "manifest typing", meaning -// that values don't necessarily match the column types declared in the -// table/index schema. -// -// Reading a record is started by calling Initialize(). Afterwards, -// GetValueType() can be used to validate the types of the record's values, and -// ReadValue() can be used to read the values into a SQLite user-defined -// function context. -class RecordReader { - public: - struct ValueHeader { - explicit ValueHeader(int64_t offset, - int64_t size, - ValueType type, - int8_t inline_value, - bool has_inline_value) - : offset(offset), - size(size), - type(type), - inline_value(inline_value), - has_inline_value(has_inline_value) {} - - // The position of the first byte used to encode the value, in the record. - int64_t offset; - // The number of bytes used to encode the value. - int64_t size; - // The SQLite type for the value. - ValueType type; - // The value encoded directly in the type, if |has_inline_value| is true. - int8_t inline_value; - // True if |inline_value| is defined. - bool has_inline_value; - }; - - // Creates an uninitialized record reader from a SQLite table B-tree. - // - // |payload_reader_| must outlive this instance, and should always point to - // leaf pages in the same tree. |column_count| must match the number of - // columns in the table's schema. - // - // The underlying table should not be modified while the record is - // initialized. - explicit RecordReader(LeafPayloadReader* payload_reader_, int column_count); - ~RecordReader(); - - RecordReader(const RecordReader&) = delete; - RecordReader& operator=(const RecordReader&) = delete; - - // Sets up the reader for a new payload. - // - // The LeafPayloadReader passed to the constructor must be focused on the - // page containing the payload. - // - // This method must complete successfully before any other method on this - // class can be called. - bool Initialize(); - - // True if the last call to Initialize succeeded. - bool IsInitialized() const; - - // The type of a value in the record. |column_index| is 0-based. - ValueType GetValueType(int column_index) const; - - // Reads a value in the record into a SQLite user-defined function context. - // - // |column_index| is 0-based. - // - // The value is reported by calling a sqlite3_result_*() function on - // |receiver|. SQLite's result-reporting API is documented at - // https://www.sqlite.org/c3ref/result_blob.html - // - // Returns false if the reading value fails. This can happen if a value is - // stored across overflow pages, and reading one of the overflow pages results - // in an I/O error. - bool ReadValue(int column_index, sqlite3_context* receiver) const; - - // Resets the reader. - // - // This method is idempotent. After it is called, IsInitialized() will return - // false. - void Reset(); - - private: - // Reads the record's header into |header_buffer_|. - // - // Returns the size of the record's header, or 0 (zero) in case of failure. - // No valid record header has 0 bytes, because the record header includes at - // least one varint. - // - // On success, |header_buffer_|'s size will be set correctly. - int64_t InitializeHeaderBuffer(); - - // Stores decoded type IDs from the record's header. - std::vector<ValueHeader> value_headers_; - - // Stores the header of the record being read. - // - // The header is only used during Initialize(). This buffer is reused across - // multiple Initialize() calls to reduce heap churn. - std::vector<uint8_t> header_buffer_; - - // Brings the record's bytes from the SQLite database pages. - // - // Raw pointer usage is acceptable because this instance's owner is expected - // to ensure that the LeafPayloadReader outlives this. - const raw_ptr<LeafPayloadReader> payload_reader_; - - // The number of columns in the table schema. No payload should have more than - // this number of columns. - const int column_count_; - - SEQUENCE_CHECKER(sequence_checker_); -}; - -} // namespace recover -} // namespace sql - -#endif // SQL_RECOVER_MODULE_RECORD_H_
diff --git a/sql/recover_module/table.cc b/sql/recover_module/table.cc deleted file mode 100644 index 7e852bf2..0000000 --- a/sql/recover_module/table.cc +++ /dev/null
@@ -1,220 +0,0 @@ -// Copyright 2019 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "sql/recover_module/table.h" - -#include <optional> -#include "base/check_op.h" -#include "base/strings/strcat.h" -#include "base/strings/string_piece.h" -#include "base/strings/string_util.h" -#include "sql/recover_module/cursor.h" -#include "sql/recover_module/integers.h" -#include "sql/recover_module/pager.h" - -namespace sql { -namespace recover { - -// Returns the number of the page holding the root page of a table's B-tree. -// -// Returns a null optional if the operation fails in any way. The failure is -// most likely due to an incorrect table spec (missing attachment or table). -// Corrupted SQLite metadata can cause failures here. -std::optional<int> GetTableRootPageId(sqlite3* sqlite_db, - const TargetTableSpec& table) { - if (table.table_name == "sqlite_schema") { - // The sqlite_schema table is always rooted at the first page. - // SQLite page IDs use 1-based indexing. - return std::optional<int64_t>(1); - } - - std::string select_sql = - base::StrCat({"SELECT rootpage FROM ", table.db_name, - ".sqlite_schema WHERE type='table' AND tbl_name=?"}); - sqlite3_stmt* sqlite_statement; - if (sqlite3_prepare_v3(sqlite_db, select_sql.c_str(), select_sql.size() + 1, - SQLITE_PREPARE_NO_VTAB, &sqlite_statement, - nullptr) != SQLITE_OK) { - // The sqlite_schema table is missing or its schema is corrupted. - return std::nullopt; - } - - if (sqlite3_bind_text(sqlite_statement, 1, table.table_name.c_str(), - static_cast<int>(table.table_name.size()), - SQLITE_STATIC) != SQLITE_OK) { - // Binding the table name failed. This shouldn't happen. - sqlite3_finalize(sqlite_statement); - return std::nullopt; - } - - if (sqlite3_step(sqlite_statement) != SQLITE_ROW) { - // The database attachment point or table does not exist. - sqlite3_finalize(sqlite_statement); - return std::nullopt; - } - - int64_t root_page = sqlite3_column_int64(sqlite_statement, 0); - sqlite3_finalize(sqlite_statement); - - if (!DatabasePageReader::IsValidPageId(root_page)) { - // Database corruption. - return std::nullopt; - } - - static_assert( - DatabasePageReader::kMaxPageId <= std::numeric_limits<int>::max(), - "Converting the page ID to int may overflow"); - return std::make_optional(static_cast<int>(root_page)); -} - -// Returns (SQLite status, a SQLite database's page size). -std::pair<int, int> GetDatabasePageSize(sqlite3_file* sqlite_file) { - // The SQLite header is documented at: - // https://www.sqlite.org/fileformat.html#the_database_header - // - // Read the entire header. - static constexpr int kHeaderOffset = 0; - static constexpr int kHeaderSize = 100; - uint8_t header_bytes[kHeaderSize]; - int sqlite_status = DatabasePageReader::RawRead(sqlite_file, kHeaderSize, - kHeaderOffset, header_bytes); - if (sqlite_status != SQLITE_OK) - return {sqlite_status, 0}; - - // This computation uses the alternate interpretation that the page size - // header field is a little-endian number encoding the page size divided by - // 256. - static constexpr int kPageSizeHeaderOffset = 16; - const int page_size = - LoadBigEndianUint16(header_bytes + kPageSizeHeaderOffset); - - if (!DatabasePageReader::IsValidPageSize(page_size)) { - // Invalid page numbers are considered irrecoverable corruption. - return {SQLITE_CORRUPT, 0}; - } - - // TODO(pwnall): This method needs a better name. It also checks the database - // header for unsupported edge cases. - - static constexpr int kReservedSizeHeaderOffset = 20; - const uint8_t page_reserved_size = header_bytes[kReservedSizeHeaderOffset]; - if (page_reserved_size != 0) { - // Chrome does not use any extension that requires reserved page space. - return {SQLITE_CORRUPT, 0}; - } - - // The text encoding is stored at offset 56, as a 4-byte big-endian integer. - // However, the range of values is 1-3, so reading the last byte is - // sufficient. - static_assert(SQLITE_UTF8 <= std::numeric_limits<uint8_t>::max(), - "Text encoding field reading shortcut is invalid."); - static constexpr int kTextEncodingHeaderOffset = 59; - const uint8_t text_encoding = header_bytes[kTextEncodingHeaderOffset]; - if (text_encoding != SQLITE_UTF8) { - // This extension only supports databases that use UTF-8 encoding. - return {SQLITE_CORRUPT, 0}; - } - - return {SQLITE_OK, page_size}; -} - -// static -std::pair<int, std::unique_ptr<VirtualTable>> VirtualTable::Create( - sqlite3* sqlite_db, - TargetTableSpec backing_table_spec, - std::vector<RecoveredColumnSpec> column_specs) { - DCHECK(backing_table_spec.IsValid()); - - std::optional<int64_t> backing_table_root_page_id = - GetTableRootPageId(sqlite_db, backing_table_spec); - if (!backing_table_root_page_id.has_value()) { - // Either the backing table specification is incorrect, or the database - // metadata is corrupted beyond hope. - // - // This virtual table is intended to be used by Chrome features, whose code - // is covered by tests. Therefore, the most likely cause is metadata - // corruption. - return {SQLITE_CORRUPT, nullptr}; - } - - sqlite3_file* sqlite_file; - int sqlite_status = - sqlite3_file_control(sqlite_db, backing_table_spec.db_name.c_str(), - SQLITE_FCNTL_FILE_POINTER, &sqlite_file); - if (sqlite_status != SQLITE_OK) { - // Failed to get the backing store's file. GetTableRootPage() succeeded, so - // the attachment point name must be correct. So, this is definitely a - // SQLite error, not a virtual table use error. Report the error code as-is, - // so it can be captured in histograms. - return {sqlite_status, nullptr}; - } - - int page_size; - std::tie(sqlite_status, page_size) = GetDatabasePageSize(sqlite_file); - if (sqlite_status != SQLITE_OK) { - // By the same reasoning as above, report the error code as-is. - return {sqlite_status, nullptr}; - } - - return {SQLITE_OK, - std::make_unique<VirtualTable>(sqlite_db, sqlite_file, - backing_table_root_page_id.value(), - page_size, std::move(column_specs))}; -} - -VirtualTable::VirtualTable(sqlite3* sqlite_db, - sqlite3_file* sqlite_file, - int root_page_id, - int page_size, - std::vector<RecoveredColumnSpec> column_specs) - : sqlite_file_(sqlite_file), - root_page_id_(root_page_id), - page_size_(page_size), - column_specs_(std::move(column_specs)) { - DCHECK(sqlite_db != nullptr); - DCHECK(sqlite_file != nullptr); - DCHECK_GT(root_page_id_, 0); - DCHECK(DatabasePageReader::IsValidPageSize(page_size)); - DCHECK(!column_specs_.empty()); -} - -VirtualTable::~VirtualTable() { -#if DCHECK_IS_ON() - DCHECK_EQ(0, open_cursor_count_.load(std::memory_order_relaxed)) - << "SQLite forgot to xClose() an xOpen()ed cursor"; -#endif // DCHECK_IS_ON() -} - -std::string VirtualTable::ToCreateTableSql() const { - std::vector<std::string> column_sqls; - column_sqls.reserve(column_specs_.size()); - for (const RecoveredColumnSpec& column_spec : column_specs_) - column_sqls.push_back(column_spec.ToCreateTableSql()); - - static constexpr base::StringPiece kCreateTableSqlStart("CREATE TABLE t("); - static constexpr base::StringPiece kCreateTableSqlEnd(")"); - static constexpr base::StringPiece kColumnSqlSeparator(","); - return base::StrCat({kCreateTableSqlStart, - base::JoinString(column_sqls, kColumnSqlSeparator), - kCreateTableSqlEnd}); -} - -VirtualCursor* VirtualTable::CreateCursor() { -#if DCHECK_IS_ON() - open_cursor_count_.fetch_add(1, std::memory_order_relaxed); -#endif // DCHECK_IS_ON() - - VirtualCursor* result = new VirtualCursor(this); - return result; -} - -void VirtualTable::WillDeleteCursor(VirtualCursor* cursor) { -#if DCHECK_IS_ON() - DCHECK_GT(open_cursor_count_.load(std::memory_order_relaxed), 0); - open_cursor_count_.fetch_sub(1, std::memory_order_relaxed); -#endif // DCHECK_IS_ON() -} - -} // namespace recover -} // namespace sql
diff --git a/sql/recover_module/table.h b/sql/recover_module/table.h deleted file mode 100644 index f0d729b..0000000 --- a/sql/recover_module/table.h +++ /dev/null
@@ -1,121 +0,0 @@ -// Copyright 2019 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SQL_RECOVER_MODULE_TABLE_H_ -#define SQL_RECOVER_MODULE_TABLE_H_ - -#include <atomic> -#include <memory> -#include <string> -#include <utility> -#include <vector> - -#include "base/check_op.h" -#include "base/memory/raw_ptr.h" -#include "sql/recover_module/parsing.h" -#include "third_party/sqlite/sqlite3.h" - -namespace sql { -namespace recover { - -class VirtualCursor; - -// Represents a virtual table created by CREATE VIRTUAL TABLE recover(...). -// -// Instances are allocated on the heap using the C++ new operator, and passed to -// SQLite via pointers to the sqlite_vtab members. SQLite is responsible for -// managing the instances' lifetimes. SQLite will call xDisconnect() for every -// successful xConnect(), and xDestroy() for every successful xCreate(). -// -// Instances are thread-safe. -class VirtualTable { - public: - // Returns a SQLite status and a VirtualTable instance. - // - // The VirtualTable is non-null iff the status is SQLITE_OK. - // - // SQLite is trusted to keep |sqlite_db| alive for as long as this instance - // lives. - static std::pair<int, std::unique_ptr<VirtualTable>> Create( - sqlite3* sqlite_db, - TargetTableSpec backing_table_spec, - std::vector<RecoveredColumnSpec> column_specs); - - // Use Create() instead of calling the constructor directly. - explicit VirtualTable(sqlite3* sqlite_db, - sqlite3_file* sqlite_file, - int root_page, - int page_size, - std::vector<RecoveredColumnSpec> column_specs); - ~VirtualTable(); - - VirtualTable(const VirtualTable&) = delete; - VirtualTable& operator=(const VirtualTable&) = delete; - - // Returns the embedded SQLite virtual table. - // - // This getter is not const because SQLite wants a non-const pointer to the - // structure. - sqlite3_vtab* SqliteTable() { return &sqlite_table_; } - - // Returns SQLite VFS file used to access the backing table's database. - // - // This getter is not const because it must return a non-const pointer. - sqlite3_file* SqliteFile() { return sqlite_file_; } - - // Returns the page number of the root page for the table's B-tree. - int root_page_id() const { return root_page_id_; } - - // Returns the page size used by the backing table's database. - int page_size() const { return page_size_; } - - // Returns the schema of the corrupted table being recovered. - const std::vector<RecoveredColumnSpec> column_specs() const { - return column_specs_; - } - - // Returns a SQL statement describing the virtual table's schema. - // - // The return value is suitable to be passed to sqlite3_declare_vtab(). - std::string ToCreateTableSql() const; - - // The VirtualTable instance that embeds a given SQLite virtual table. - // - // |sqlite_table| must have been returned by VirtualTable::SqliteTable(). - static inline VirtualTable* FromSqliteTable(sqlite3_vtab* sqlite_table) { - static_assert(std::is_standard_layout<VirtualTable>::value, - "needed for the reinterpret_cast below"); - static_assert(offsetof(VirtualTable, sqlite_table_) == 0, - "sqlite_table_ must be the first member of the class"); - VirtualTable* const result = reinterpret_cast<VirtualTable*>(sqlite_table); - DCHECK_EQ(sqlite_table, &result->sqlite_table_); - return result; - } - - // Creates a new cursor for iterating over this table. - VirtualCursor* CreateCursor(); - - // Called right before a cursor belonging to this table will be destroyed. - void WillDeleteCursor(VirtualCursor* cursor); - - private: - // SQLite handle for this table. The struct is populated and used by SQLite. - sqlite3_vtab sqlite_table_; - - // See the corresponding getters for these members' purpose. - const raw_ptr<sqlite3_file> sqlite_file_; - const int root_page_id_; - const int page_size_; - const std::vector<RecoveredColumnSpec> column_specs_; - -#if DCHECK_IS_ON() - // Number of cursors that are still opened. - std::atomic<int> open_cursor_count_{0}; -#endif // DCHECK_IS_ON() -}; - -} // namespace recover -} // namespace sql - -#endif // SQL_RECOVER_MODULE_TABLE_H_
diff --git a/sql/recovery.cc b/sql/recovery.cc index dde1b58..2e622e8 100644 --- a/sql/recovery.cc +++ b/sql/recovery.cc
@@ -14,9 +14,6 @@ #include "base/check_op.h" #include "base/containers/contains.h" -#include "base/dcheck_is_on.h" -#include "base/feature_list.h" -#include "base/files/file_path.h" #include "base/format_macros.h" #include "base/functional/bind.h" #include "base/functional/callback_helpers.h" @@ -24,19 +21,12 @@ #include "base/metrics/histogram_functions.h" #include "base/notreached.h" #include "base/strings/strcat.h" -#include "base/strings/string_util.h" -#include "base/strings/stringprintf.h" -#include "base/types/pass_key.h" #include "build/build_config.h" #include "sql/database.h" #include "sql/error_delegate_util.h" #include "sql/internal_api_token.h" #include "sql/meta_table.h" -#include "sql/recover_module/module.h" -#include "sql/sql_features.h" #include "sql/sqlite_result_code.h" -#include "sql/sqlite_result_code_values.h" -#include "sql/statement.h" #include "third_party/sqlite/sqlite3.h" namespace sql { @@ -48,14 +38,9 @@ } // namespace // static -bool BuiltInRecovery::IsSupported() { - return base::FeatureList::IsEnabled(features::kUseBuiltInRecoveryIfSupported); -} - -// static bool BuiltInRecovery::ShouldAttemptRecovery(Database* database, int extended_error) { - return BuiltInRecovery::IsSupported() && database && database->is_open() && + return database && database->is_open() && !database->DbPath(InternalApiToken()).empty() && #if BUILDFLAG(IS_FUCHSIA) // Recovering WAL databases is not supported on Fuchsia. @@ -67,33 +52,15 @@ // static SqliteResultCode BuiltInRecovery::RecoverDatabase(Database* database, Strategy strategy) { - if (!BuiltInRecovery::IsSupported()) { - return SqliteResultCode::kAbort; - } - auto recovery = BuiltInRecovery(database, strategy); return recovery.RecoverAndReplaceDatabase(); } // static -bool BuiltInRecovery::RecoverIfPossible( - Database* database, - int extended_error, - Strategy strategy, - const base::Feature* const use_builtin_recovery_if_supported_flag) { - // If `BuiltInRecovery` is supported at all, check the flag for this specific - // database, provided by the feature team. - bool use_builtin_recovery = - BuiltInRecovery::IsSupported() && - (!use_builtin_recovery_if_supported_flag || - base::FeatureList::IsEnabled(*use_builtin_recovery_if_supported_flag)); - - if (use_builtin_recovery - ? !BuiltInRecovery::ShouldAttemptRecovery(database, extended_error) - : !database || !database->is_open() || - database->DbPath(InternalApiToken()).empty() || - database->UseWALMode() || - !Recovery::ShouldRecover(extended_error)) { +bool BuiltInRecovery::RecoverIfPossible(Database* database, + int extended_error, + Strategy strategy) { + if (!ShouldAttemptRecovery(database, extended_error)) { return false; } @@ -102,23 +69,9 @@ // re-entry. database->reset_error_callback(); - if (use_builtin_recovery) { - CHECK(BuiltInRecovery::IsSupported()); - auto result = BuiltInRecovery::RecoverDatabase(database, strategy); - if (!IsSqliteSuccessCode(result)) { - DLOG(ERROR) << "Database recovery failed with result code: " << result; - } - } else { - switch (strategy) { - case BuiltInRecovery::Strategy::kRecoverOrRaze: - Recovery::RecoverDatabase(database, - database->DbPath(InternalApiToken())); - break; - case BuiltInRecovery::Strategy::kRecoverWithMetaVersionOrRaze: - Recovery::RecoverDatabaseWithMetaVersion( - database, database->DbPath(InternalApiToken())); - break; - } + auto result = BuiltInRecovery::RecoverDatabase(database, strategy); + if (!IsSqliteSuccessCode(result)) { + DLOG(ERROR) << "Database recovery failed with result code: " << result; } return true; @@ -131,7 +84,6 @@ .page_size = database ? database->page_size() : 0, .cache_size = 0, }) { - CHECK(IsSupported()); CHECK(db_); CHECK(db_->is_open()); // Recovery is likely to be used in error handling. To prevent re-entry due to @@ -419,608 +371,4 @@ return true; } -// static -std::unique_ptr<Recovery> Recovery::Begin(Database* database, - const base::FilePath& db_path) { - // Recovery is likely to be initiated in an error handler. Since recovery - // changes the state of the handle, protect against multiple layers attempting - // the same recovery. - if (!database->is_open()) { - // Warn about API mis-use. - DCHECK(database->poisoned(InternalApiToken())) - << "Illegal to recover with closed Database"; - return nullptr; - } - - // Using `new` to access a non-public constructor - std::unique_ptr<Recovery> recovery(new Recovery(database)); - if (!recovery->Init(db_path)) { - // TODO(shess): Should Init() failure result in Raze()? - recovery->Shutdown(POISON); - return nullptr; - } - - return recovery; -} - -// static -bool Recovery::Recovered(std::unique_ptr<Recovery> r) { - return r->Backup(); -} - -// static -void Recovery::Unrecoverable(std::unique_ptr<Recovery> r) { - CHECK(r->db_); - // ~Recovery() will RAZE_AND_POISON. -} - -// static -void Recovery::Rollback(std::unique_ptr<Recovery> r) { - // TODO(shess): Crash / crash and dump? - r->Shutdown(POISON); -} - -Recovery::Recovery(Database* connection) - : db_(connection), - recover_db_({ - .exclusive_locking = false, - .page_size = db_->page_size(), - // The interface to the recovery module is a virtual table. - .enable_virtual_tables_discouraged = true, - }) { - // Files with I/O errors cannot be safely memory-mapped. - recover_db_.set_mmap_disabled(); - - // TODO(shess): This may not handle cases where the default page - // size is used, but the default has changed. I do not think this - // has ever happened. This could be handled by using "PRAGMA - // page_size", at the cost of potential additional failure cases. -} - -Recovery::~Recovery() { - Shutdown(RAZE_AND_POISON); -} - -bool Recovery::Init(const base::FilePath& db_path) { -#if DCHECK_IS_ON() - // set_error_callback() will DCHECK if the database already has an error - // callback. The recovery process is likely to result in SQLite errors, and - // those shouldn't get surfaced to any callback. - db_->set_error_callback(base::DoNothing()); - - // Undo the set_error_callback() above. We only used it for its DCHECK - // behavior. - db_->reset_error_callback(); -#endif // DCHECK_IS_ON() - - // Break any outstanding transactions on the original database to - // prevent deadlocks reading through the attached version. - // TODO(shess): A client may legitimately wish to recover from - // within the transaction context, because it would potentially - // preserve any in-flight changes. Unfortunately, any attach-based - // system could not handle that. A system which manually queried - // one database and stored to the other possibly could, but would be - // more complicated. - db_->RollbackAllTransactions(); - - // Disable exclusive locking mode so that the attached database can - // access things. The locking_mode change is not active until the - // next database access, so immediately force an access. Enabling - // writable_schema allows processing through certain kinds of - // corruption. - // TODO(shess): It would be better to just close the handle, but it - // is necessary for the final backup which rewrites things. It - // might be reasonable to close then re-open the handle. - std::ignore = db_->Execute("PRAGMA writable_schema=1"); - std::ignore = db_->Execute("PRAGMA locking_mode=NORMAL"); - std::ignore = db_->Execute("SELECT COUNT(*) FROM sqlite_schema"); - - // TODO(shess): If this is a common failure case, it might be - // possible to fall back to a memory database. But it probably - // implies that the SQLite tmpdir logic is busted, which could cause - // a variety of other random issues in our code. - if (!recover_db_.OpenTemporary(base::PassKey<Recovery>())) - return false; - - // Enable the recover virtual table for this connection. - int rc = EnableRecoveryExtension(&recover_db_, InternalApiToken()); - if (rc != SQLITE_OK) { - LOG(ERROR) << "Failed to initialize recover module: " - << recover_db_.GetErrorMessage(); - return false; - } - - // Turn on |SQLITE_RecoveryMode| for the handle, which allows - // reading certain broken databases. - if (!recover_db_.Execute("PRAGMA writable_schema=1")) - return false; - - if (!recover_db_.AttachDatabase(db_path, "corrupt", InternalApiToken())) - return false; - - return true; -} - -bool Recovery::Backup() { - CHECK(db_); - CHECK(recover_db_.is_open()); - - // TODO(shess): Some of the failure cases here may need further - // exploration. Just as elsewhere, persistent problems probably - // need to be razed, while anything which might succeed on a future - // run probably should be allowed to try. But since Raze() uses the - // same approach, even that wouldn't work when this code fails. - // - // The documentation for the backup system indicate a relatively - // small number of errors are expected: - // SQLITE_BUSY - cannot lock the destination database. This should - // only happen if someone has another handle to the - // database, Chromium generally doesn't do that. - // SQLITE_LOCKED - someone locked the source database. Should be - // impossible (perhaps anti-virus could?). - // SQLITE_READONLY - destination is read-only. - // SQLITE_IOERR - since source database is temporary, probably - // indicates that the destination contains blocks - // throwing errors, or gross filesystem errors. - // SQLITE_NOMEM - out of memory, should be transient. - // - // AFAICT, SQLITE_BUSY and SQLITE_NOMEM could perhaps be considered - // transient, with SQLITE_LOCKED being unclear. - // - // SQLITE_READONLY and SQLITE_IOERR are probably persistent, with a - // strong chance that Raze() would not resolve them. If Delete() - // deletes the database file, the code could then re-open the file - // and attempt the backup again. - // - // For now, this code attempts a best effort. - - // Backup the original db from the recovered db. - sqlite3_backup* backup = sqlite3_backup_init( - db_->db(InternalApiToken()), kMainDatabaseName, - recover_db_.db(InternalApiToken()), kMainDatabaseName); - if (!backup) { - // Error code is in the destination database handle. - LOG(ERROR) << "sqlite3_backup_init() failed: " - << sqlite3_errmsg(db_->db(InternalApiToken())); - - return false; - } - - // -1 backs up the entire database. - int rc = sqlite3_backup_step(backup, -1); - int pages = sqlite3_backup_pagecount(backup); - // TODO(shess): sqlite3_backup_finish() appears to allow returning a - // different value from sqlite3_backup_step(). Circle back and - // figure out if that can usefully inform the decision of whether to - // retry or not. - sqlite3_backup_finish(backup); - DCHECK_GT(pages, 0); - - if (rc != SQLITE_DONE) { - LOG(ERROR) << "sqlite3_backup_step() failed: " - << sqlite3_errmsg(db_->db(InternalApiToken())); - } - - // The destination database was locked. Give up, but leave the data - // in place. Maybe it won't be locked next time. - if (rc == SQLITE_BUSY || rc == SQLITE_LOCKED) { - Shutdown(POISON); - return false; - } - - // Running out of memory should be transient, retry later. - if (rc == SQLITE_NOMEM) { - Shutdown(POISON); - return false; - } - - // TODO(shess): For now, leave the original database alone. Some errors should - // probably route to RAZE_AND_POISON. - if (rc != SQLITE_DONE) { - Shutdown(POISON); - return false; - } - - // Clean up the recovery db, and terminate the main database - // connection. - Shutdown(POISON); - return true; -} - -void Recovery::Shutdown(Recovery::Disposition raze) { - if (!db_) - return; - - recover_db_.Close(); - if (raze == RAZE_AND_POISON) { - db_->RazeAndPoison(); - } else if (raze == POISON) { - db_->Poison(); - } - db_ = nullptr; -} - -bool Recovery::AutoRecoverTable(const char* table_name, - size_t* rows_recovered) { - // Query the info for the recovered table in database [main]. - std::string query( - base::StringPrintf("PRAGMA main.table_info(%s)", table_name)); - Statement s(db()->GetUniqueStatement(query.c_str())); - - // The columns of the recover virtual table. - std::vector<std::string> create_column_decls; - - // The columns to select from the recover virtual table when copying - // to the recovered table. - std::vector<std::string> insert_columns; - - // If PRIMARY KEY is a single INTEGER column, then it is an alias - // for ROWID. The primary key can be compound, so this can only be - // determined after processing all column data and tracking what is - // seen. |pk_column_count| counts the columns in the primary key. - // |rowid_decl| stores the ROWID version of the last INTEGER column - // seen, which is at |rowid_ofs| in |create_column_decls|. - size_t pk_column_count = 0; - size_t rowid_ofs = 0; // Only valid if rowid_decl is set. - std::string rowid_decl; // ROWID version of column |rowid_ofs|. - - while (s.Step()) { - const std::string column_name(s.ColumnString(1)); - const std::string column_type(s.ColumnString(2)); - const ColumnType default_type = s.GetColumnType(4); - const bool default_is_null = (default_type == ColumnType::kNull); - const int pk_column = s.ColumnInt(5); - - // http://www.sqlite.org/pragma.html#pragma_table_info documents column 5 as - // the 1-based index of the column in the primary key, otherwise 0. - if (pk_column > 0) - ++pk_column_count; - - // Construct column declaration as "name type [optional constraint]". - std::string column_decl = column_name; - - // SQLite's affinity detection is documented at: - // http://www.sqlite.org/datatype3.html#affname - // The gist of it is that CHAR, TEXT, and INT use substring matches. - // TODO(shess): It would be nice to unit test the type handling, - // but it is not obvious to me how to write a test which would - // fail appropriately when something was broken. It would have to - // somehow use data which would allow detecting the various type - // coercions which happen. If STRICT could be enabled, type - // mismatches could be detected by which rows are filtered. - if (base::Contains(column_type, "INT")) { - if (pk_column == 1) { - rowid_ofs = create_column_decls.size(); - rowid_decl = column_name + " ROWID"; - } - column_decl += " INTEGER"; - } else if (base::Contains(column_type, "CHAR") || - base::Contains(column_type, "TEXT")) { - column_decl += " TEXT"; - } else if (column_type == "BLOB") { - column_decl += " BLOB"; - } else if (base::Contains(column_type, "DOUB")) { - column_decl += " FLOAT"; - } else { - // TODO(shess): AFAICT, there remain: - // - contains("CLOB") -> TEXT - // - contains("REAL") -> FLOAT - // - contains("FLOA") -> FLOAT - // - other -> "NUMERIC" - // Just code those in as they come up. - NOTREACHED() << " Unsupported type " << column_type; - return false; - } - - create_column_decls.push_back(column_decl); - - // Per the NOTE in the header file, convert NULL values to the - // DEFAULT. All columns could be IFNULL(column_name,default), but - // the NULL case would require special handling either way. - if (default_is_null) { - insert_columns.push_back(column_name); - } else { - // The default value appears to be pre-quoted, as if it is - // literally from the sqlite_schema CREATE statement. - std::string default_value = s.ColumnString(4); - insert_columns.push_back(base::StringPrintf( - "IFNULL(%s,%s)", column_name.c_str(), default_value.c_str())); - } - } - - // Receiving no column information implies that the table doesn't exist. - if (create_column_decls.empty()) { - return false; - } - - // If the PRIMARY KEY was a single INTEGER column, convert it to ROWID. - if (pk_column_count == 1 && !rowid_decl.empty()) - create_column_decls[rowid_ofs] = rowid_decl; - - std::string recover_create(base::StringPrintf( - "CREATE VIRTUAL TABLE temp.recover_%s USING recover(corrupt.%s, %s)", - table_name, table_name, - base::JoinString(create_column_decls, ",").c_str())); - - // INSERT OR IGNORE means that it will drop rows resulting from constraint - // violations. INSERT OR REPLACE only handles UNIQUE constraint violations. - std::string recover_insert(base::StringPrintf( - "INSERT OR IGNORE INTO main.%s SELECT %s FROM temp.recover_%s", - table_name, base::JoinString(insert_columns, ",").c_str(), table_name)); - - std::string recover_drop( - base::StringPrintf("DROP TABLE temp.recover_%s", table_name)); - - if (!db()->Execute(recover_create.c_str())) - return false; - - if (!db()->Execute(recover_insert.c_str())) { - std::ignore = db()->Execute(recover_drop.c_str()); - return false; - } - - *rows_recovered = db()->GetLastChangeCount(); - - // TODO(shess): Is leaving the recover table around a breaker? - return db()->Execute(recover_drop.c_str()); -} - -bool Recovery::SetupMeta() { - // clang-format off - static const char kCreateSql[] = - "CREATE VIRTUAL TABLE temp.recover_meta USING recover(" - "corrupt.meta," - "key TEXT NOT NULL," - "value ANY" // Whatever is stored. - ")"; - // clang-format on - return db()->Execute(kCreateSql); -} - -bool Recovery::GetMetaVersionNumber(int* version) { - DCHECK(version); - // TODO(shess): DCHECK(db()->DoesTableExist("temp.recover_meta")); - // Unfortunately, DoesTableExist() queries sqlite_schema, not - // sqlite_temp_master. - - static const char kVersionSql[] = - "SELECT value FROM temp.recover_meta WHERE key = 'version'"; - sql::Statement recovery_version(db()->GetUniqueStatement(kVersionSql)); - if (!recovery_version.Step()) - return false; - - *version = recovery_version.ColumnInt(0); - return true; -} - -namespace { - -// Collect statements from [corrupt.sqlite_schema.sql] which start with |prefix| -// (which should be a valid SQL string ending with the space before a table -// name), then apply the statements to [main]. Skip any table named -// 'sqlite_sequence', as that table is created on demand by SQLite if any tables -// use AUTOINCREMENT. -// -// Returns |true| if all of the matching items were created in the main -// database. Returns |false| if an item fails on creation, or if the corrupt -// database schema cannot be queried. -bool SchemaCopyHelper(Database* db, const char* prefix) { - const size_t prefix_len = strlen(prefix); - DCHECK_EQ(' ', prefix[prefix_len - 1]); - - sql::Statement s( - db->GetUniqueStatement("SELECT DISTINCT sql FROM corrupt.sqlite_schema " - "WHERE name<>'sqlite_sequence'")); - while (s.Step()) { - std::string sql = s.ColumnString(0); - - // Skip statements that don't start with |prefix|. - if (sql.compare(0, prefix_len, prefix) != 0) - continue; - - sql.insert(prefix_len, "main."); - if (!db->Execute(sql.c_str())) - return false; - } - return s.Succeeded(); -} - -} // namespace - -// This method is derived from SQLite's vacuum.c. VACUUM operates very -// similarily, creating a new database, populating the schema, then copying the -// data. -// -// TODO(shess): This conservatively uses Rollback() rather than Unrecoverable(). -// With Rollback(), it is expected that the database will continue to generate -// errors. Change the failure cases to Unrecoverable(). -// -// static -std::unique_ptr<Recovery> Recovery::BeginRecoverDatabase( - Database* db, - const base::FilePath& db_path) { - std::unique_ptr<sql::Recovery> recovery = sql::Recovery::Begin(db, db_path); - if (!recovery) { - // Close the underlying sqlite* handle. Windows does not allow deleting - // open files, and all platforms block opening a second sqlite3* handle - // against a database when exclusive locking is set. - db->Poison(); - - // When this code was written, histograms showed that most failures happened - // while attaching a corrupt database. In this case, a large proportion of - // attachment failures were SQLITE_NOTADB. - // - // We currently only delete the database in that specific failure case. - { - Database probe_db; - if (!probe_db.OpenInMemory() || - probe_db.AttachDatabase(db_path, "corrupt", InternalApiToken()) || - probe_db.GetErrorCode() != SQLITE_NOTADB) { - return nullptr; - } - } - - // The database has invalid data in the SQLite header, so it is almost - // certainly not recoverable without manual intervention (and likely not - // recoverable _with_ manual intervention). Clear away the broken database. - if (!sql::Database::Delete(db_path)) - return nullptr; - - // Windows deletion is complicated by file scanners and malware - sometimes - // Delete() appears to succeed, even though the file remains. The following - // attempts to track if this happens often enough to cause concern. - { - Database probe_db; - if (!probe_db.Open(db_path)) - return nullptr; - - if (!probe_db.Execute("PRAGMA auto_vacuum")) - return nullptr; - } - - // The rest of the recovery code could be run on the re-opened database, but - // the database is empty, so there would be no point. - return nullptr; - } - -#if DCHECK_IS_ON() - // This code silently fails to recover fts3 virtual tables. At this time no - // browser database contain fts3 tables. Just to be safe, complain loudly if - // the database contains virtual tables. - // - // fts3 has an [x_segdir] table containing a column [end_block INTEGER]. But - // it actually stores either an integer or a text containing a pair of - // integers separated by a space. AutoRecoverTable() trusts the INTEGER tag - // when setting up the recover vtable, so those rows get dropped. Setting - // that column to ANY may work. - if (db->is_open()) { - sql::Statement s(db->GetUniqueStatement( - "SELECT 1 FROM sqlite_schema WHERE sql LIKE 'CREATE VIRTUAL TABLE %'")); - DCHECK(!s.Step()) << "Recovery of virtual tables not supported"; - } -#endif - - // TODO(shess): vacuum.c turns off checks and foreign keys. - - // TODO(shess): vacuum.c turns synchronous=OFF for the target. I do not fully - // understand this, as the temporary db should not have a journal file at all. - // Perhaps it does in case of cache spill? - - // Copy table schema from [corrupt] to [main]. - if (!SchemaCopyHelper(recovery->db(), "CREATE TABLE ") || - !SchemaCopyHelper(recovery->db(), "CREATE INDEX ") || - !SchemaCopyHelper(recovery->db(), "CREATE UNIQUE INDEX ")) { - // No RecordRecoveryEvent() here because SchemaCopyHelper() already did. - Recovery::Rollback(std::move(recovery)); - return nullptr; - } - - // Run auto-recover against each table, skipping the sequence table. This is - // necessary because table recovery can create the sequence table as a side - // effect, so recovering that table inline could lead to duplicate data. - { - sql::Statement s(recovery->db()->GetUniqueStatement( - "SELECT name FROM sqlite_schema WHERE sql LIKE 'CREATE TABLE %' " - "AND name!='sqlite_sequence'")); - while (s.Step()) { - const std::string name = s.ColumnString(0); - size_t rows_recovered; - if (!recovery->AutoRecoverTable(name.c_str(), &rows_recovered)) { - Recovery::Rollback(std::move(recovery)); - return nullptr; - } - } - if (!s.Succeeded()) { - Recovery::Rollback(std::move(recovery)); - return nullptr; - } - } - - // Overwrite any sequences created. - if (recovery->db()->DoesTableExist("corrupt.sqlite_sequence")) { - std::ignore = recovery->db()->Execute("DELETE FROM main.sqlite_sequence"); - size_t rows_recovered; - if (!recovery->AutoRecoverTable("sqlite_sequence", &rows_recovered)) { - Recovery::Rollback(std::move(recovery)); - return nullptr; - } - } - - // Copy triggers and views directly to sqlite_schema. Any tables they refer - // to should already exist. - static const char kCreateMetaItemsSql[] = - "INSERT INTO main.sqlite_schema " - "SELECT type, name, tbl_name, rootpage, sql " - "FROM corrupt.sqlite_schema WHERE type='view' OR type='trigger'"; - if (!recovery->db()->Execute(kCreateMetaItemsSql)) { - Recovery::Rollback(std::move(recovery)); - return nullptr; - } - - return recovery; -} - -void Recovery::RecoverDatabase(Database* db, const base::FilePath& db_path) { - std::unique_ptr<sql::Recovery> recovery = BeginRecoverDatabase(db, db_path); - - if (recovery) - std::ignore = Recovery::Recovered(std::move(recovery)); -} - -void Recovery::RecoverDatabaseWithMetaVersion(Database* db, - const base::FilePath& db_path) { - std::unique_ptr<sql::Recovery> recovery = BeginRecoverDatabase(db, db_path); - if (!recovery) - return; - - int version = 0; - if (!recovery->SetupMeta() || !recovery->GetMetaVersionNumber(&version)) { - sql::Recovery::Unrecoverable(std::move(recovery)); - return; - } - - std::ignore = Recovery::Recovered(std::move(recovery)); -} - -// static -bool Recovery::ShouldRecover(int extended_error) { - // Trim extended error codes. - int error = extended_error & 0xFF; - switch (error) { - case SQLITE_NOTADB: - // SQLITE_NOTADB happens if the SQLite header is broken. Some earlier - // versions of SQLite return this where other versions return - // SQLITE_CORRUPT, which is a recoverable case. Later versions only - // return this error only in unrecoverable cases, in which case recovery - // will fail with no changes to the database, so there's no harm in - // attempting recovery in this case. - return true; - - case SQLITE_CORRUPT: - // SQLITE_CORRUPT generally means that the database is readable as a - // SQLite database, but some inconsistency has been detected by SQLite. - // In many cases the inconsistency is relatively trivial, such as if an - // index refers to a row which was deleted, in which case most or even all - // of the data can be recovered. This can also be reported if parts of - // the file have been overwritten with garbage data, in which recovery - // should be able to recover partial data. - return true; - - // TODO(shess): Possible future options for automated fixing: - // - SQLITE_CANTOPEN - delete the broken symlink or directory. - // - SQLITE_PERM - permissions could be fixed. - // - SQLITE_READONLY - permissions could be fixed. - // - SQLITE_IOERR - rewrite using new blocks. - // - SQLITE_FULL - recover in memory and rewrite subset of data. - - default: - return false; - } -} - -// static -int Recovery::EnableRecoveryExtension(Database* db, InternalApiToken) { - return sql::recover::RegisterRecoverExtension(db->db(InternalApiToken())); -} - } // namespace sql
diff --git a/sql/recovery.h b/sql/recovery.h index 93fedc7..ea00217 100644 --- a/sql/recovery.h +++ b/sql/recovery.h
@@ -16,7 +16,6 @@ #include "sql/sqlite_result_code_values.h" namespace base { -struct Feature; class FilePath; } @@ -92,8 +91,6 @@ kMaxValue = kFailedBackupRun, }; - [[nodiscard]] static bool IsSupported(); - // Returns true if `RecoverDatabase()` can plausibly fix `database` given this // `extended_error`. This does not guarantee that `RecoverDatabase()` will // successfully recover the database. @@ -132,38 +129,26 @@ [[nodiscard]] static SqliteResultCode RecoverDatabase(Database* database, Strategy strategy); - // Similar to `RecoverDatabase()` above, but with a few key differences: - // - Uses `BuiltInRecovery` or the legacy `Recovery` to recover the - // database, as appropriate. This method facilitates the migration to the - // newer recovery module with minimal impact on feature teams. The - // expectation is that `Recovery` will eventually be removed entirely. - // See https://crbug.com/1385500. + // Similar to `RecoverDatabase()` above, but with a couple key differences: // - Can be called without first checking `ShouldAttemptRecovery()`. // - `database`'s error callback will be reset if recovery is attempted. // - Must only be called from within a database error callback. - // - Includes the option to pass a per-database feature flag indicating - // whether `BuiltInRecovery` should be used to recover this database, if - // it's supported. // // Recommended usage from within a database error callback: // // // Attempt to recover the database, if recovery is possible. // if (sql::BuiltInRecovery::RecoverIfPossible( // &db, extended_error, - // sql::BuiltInRecovery::Strategy::kRecoverWithMetaVersionOrRaze, - // &features::kMyFeatureTeamShouldUseBuiltInRecoveryIfSupported)) { + // sql::BuiltInRecovery::Strategy::kRecoverWithMetaVersionOrRaze)) { // // Recovery was attempted. The database handle has been poisoned and the // // error callback has been reset. // // // ... // } // - [[nodiscard]] static bool RecoverIfPossible( - Database* database, - int extended_error, - Strategy strategy, - const base::Feature* const use_builtin_recovery_if_supported_flag = - nullptr); + [[nodiscard]] static bool RecoverIfPossible(Database* database, + int extended_error, + Strategy strategy); BuiltInRecovery(const BuiltInRecovery&) = delete; BuiltInRecovery& operator=(const BuiltInRecovery&) = delete; @@ -205,194 +190,6 @@ base::FilePath recovery_database_path_; }; -// WARNING: This class is being deprecated. Please use `BuiltInRecovery` for new -// databases. See https://crbug.com/1385500. -// -// Recovery module for sql/. The basic idea is to create a fresh database and -// populate it with the recovered contents of the original database. If -// recovery is successful, the recovered database is backed up over the original -// database. If recovery is not successful, the original database is razed. In -// either case, the original handle is poisoned so that operations on the stack -// do not accidentally disrupt the restored data. -// -// RecoverDatabase() automates this, including recoverying the schema of from -// the suspect database. If a database requires special handling, such as -// recovering between different schema, or tables requiring post-processing, -// then the module can be used manually like: -// -// { -// std::unique_ptr<sql::Recovery> r = -// sql::Recovery::Begin(orig_db, orig_db_path); -// if (r) { -// // Create the schema to recover to. On failure, clear the -// // database. -// if (!r.db()->Execute(kCreateSchemaSql)) { -// sql::Recovery::Unrecoverable(std::move(r)); -// return; -// } -// -// // Recover data in "mytable". -// size_t rows_recovered = 0; -// if (!r.AutoRecoverTable("mytable", 0, &rows_recovered)) { -// sql::Recovery::Unrecoverable(std::move(r)); -// return; -// } -// -// // Manually cleanup additional constraints. -// if (!r.db()->Execute(kCleanupSql)) { -// sql::Recovery::Unrecoverable(std::move(r)); -// return; -// } -// -// // Commit the recovered data to the original database file. -// sql::Recovery::Recovered(std::move(r)); -// } -// } -// -// If Recovered() is not called, then RazeAndPoison() is called on -// orig_db. -class COMPONENT_EXPORT(SQL) Recovery { - public: - Recovery(const Recovery&) = delete; - Recovery& operator=(const Recovery&) = delete; - ~Recovery(); - - // Begin the recovery process by opening a temporary database handle - // and attach the existing database to it at "corrupt". To prevent - // deadlock, all transactions on |database| are rolled back. - // - // Returns nullptr in case of failure, with no cleanup done on the - // original database (except for breaking the transactions). The - // caller should Raze() or otherwise cleanup as appropriate. - // - // TODO(shess): Later versions of SQLite allow extracting the path - // from the database. - // TODO(shess): Allow specifying the connection point? - [[nodiscard]] static std::unique_ptr<Recovery> Begin( - Database* database, - const base::FilePath& db_path); - - // Mark recovery completed by replicating the recovery database over - // the original database, then closing the recovery database. The - // original database handle is poisoned, causing future calls - // against it to fail. - // - // If Recovered() is not called, the destructor will call - // Unrecoverable(). - // - // TODO(shess): At this time, this function can fail while leaving - // the original database intact. Figure out which failure cases - // should go to RazeAndPoison() instead. - [[nodiscard]] static bool Recovered(std::unique_ptr<Recovery> r); - - // Indicate that the database is unrecoverable. The original - // database is razed, and the handle poisoned. - static void Unrecoverable(std::unique_ptr<Recovery> r); - - // When initially developing recovery code, sometimes the possible - // database states are not well-understood without further - // diagnostics. Abandon recovery but do not raze the original - // database. - // NOTE(shess): Only call this when adding recovery support. In the - // steady state, all databases should progress to recovered or razed. - static void Rollback(std::unique_ptr<Recovery> r); - - // Handle to the temporary recovery database. - sql::Database* db() { return &recover_db_; } - - // Attempt to recover the named table from the corrupt database into - // the recovery database using a temporary recover virtual table. - // The virtual table schema is derived from the named table's schema - // in database [main]. Data is copied using INSERT OR IGNORE, so - // duplicates are dropped. - // - // If the source table has fewer columns than the target, the target - // DEFAULT value will be used for those columns. - // - // Returns true if all operations succeeded, with the number of rows - // recovered in |*rows_recovered|. - // - // NOTE(shess): Due to a flaw in the recovery virtual table, at this - // time this code injects the DEFAULT value of the target table in - // locations where the recovery table returns nullptr. This is not - // entirely correct, because it happens both when there is a short - // row (correct) but also where there is an actual NULL value - // (incorrect). - // - // TODO(shess): Flag for INSERT OR REPLACE vs IGNORE. - // TODO(shess): Handle extended table names. - bool AutoRecoverTable(const char* table_name, size_t* rows_recovered); - - // Setup a recover virtual table at temp.recover_meta, reading from - // corrupt.meta. Returns true if created. - // TODO(shess): Perhaps integrate into Begin(). - // TODO(shess): Add helpers to fetch additional items from the meta - // table as needed. - bool SetupMeta(); - - // Fetch the version number from temp.recover_meta. Returns false - // if the query fails, or if there is no version row. Otherwise - // returns true, with the version in |*version_number|. - // - // Only valid to call after successful SetupMeta(). - bool GetMetaVersionNumber(int* version_number); - - // Attempt to recover the database by creating a new database with schema from - // |db|, then copying over as much data as possible. If successful, the - // recovery handle is returned to allow the caller to make additional changes, - // such as validating constraints not expressed in the schema. - // - // In case of SQLITE_NOTADB, the database is deemed unrecoverable and deleted. - [[nodiscard]] static std::unique_ptr<Recovery> BeginRecoverDatabase( - Database* db, - const base::FilePath& db_path); - - // Call BeginRecoverDatabase() to recover the database, then commit the - // changes using Recovered(). After this call, the |db| handle will be - // poisoned (though technically remaining open) so that future calls will - // return errors until the handle is re-opened. - static void RecoverDatabase(Database* db, const base::FilePath& db_path); - - // Variant on RecoverDatabase() which requires that the database have a valid - // meta table with a version value. The meta version value is used by some - // clients to make assertions about the database schema. If this information - // cannot be determined, the database is considered unrecoverable. - static void RecoverDatabaseWithMetaVersion(Database* db, - const base::FilePath& db_path); - - // Returns true for SQLite errors which RecoverDatabase() can plausibly fix. - // This does not guarantee that RecoverDatabase() will successfully recover - // the database. - static bool ShouldRecover(int extended_error); - - // Enables the "recover" SQLite extension for a database connection. - // - // Returns a SQLite error code. - static int EnableRecoveryExtension(Database* db, InternalApiToken); - - private: - explicit Recovery(Database* database); - - // Setup the recovery database handle for Begin(). Returns false in - // case anything failed. - [[nodiscard]] bool Init(const base::FilePath& db_path); - - // Copy the recovered database over the original database. - [[nodiscard]] bool Backup(); - - // Close the recovery database, and poison the original handle. - // |raze| controls whether the original database is razed or just - // poisoned. - enum Disposition { - RAZE_AND_POISON, - POISON, - }; - void Shutdown(Disposition raze); - - raw_ptr<Database> db_; // Original Database connection. - Database recover_db_; // Recovery Database connection. -}; - } // namespace sql #endif // SQL_RECOVERY_H_
diff --git a/sql/built_in_recovery_fuzzer.cc b/sql/recovery_fuzzer.cc similarity index 100% rename from sql/built_in_recovery_fuzzer.cc rename to sql/recovery_fuzzer.cc
diff --git a/sql/recovery_unittest.cc b/sql/recovery_unittest.cc index 6ba67a7..f1ec93d 100644 --- a/sql/recovery_unittest.cc +++ b/sql/recovery_unittest.cc
@@ -57,10 +57,17 @@ return ExecuteWithResults(db, kSql, "|", "\n"); } -// Base class for all recovery-related tests. Each subclass must initialize -// `scoped_feature_list_`, as appropriate. -class SqlRecoveryTestBase : public testing::Test { +// Parameterized to test with and without WAL mode enabled. +class SqlRecoveryTest : public testing::Test, + public testing::WithParamInterface<bool> { public: + SqlRecoveryTest() : db_(DatabaseOptions{.wal_mode = ShouldEnableWal()}) { + scoped_feature_list_.InitWithFeatureStates( + {{features::kEnableWALModeByDefault, ShouldEnableWal()}}); + } + + bool ShouldEnableWal() { return GetParam(); } + void SetUp() override { db_.set_histogram_tag("MyFeatureDatabase"); @@ -93,9 +100,6 @@ } protected: - explicit SqlRecoveryTestBase(DatabaseOptions options) - : db_(std::move(options)) {} - base::test::ScopedFeatureList scoped_feature_list_; base::ScopedTempDir temp_dir_; base::FilePath db_path_; @@ -103,68 +107,14 @@ base::HistogramTester histogram_tester_; }; -// Tests both the legacy `sql::Recovery` interface and the newer -// `sql::BuiltInRecovery` interface, if it's supported. -// -// Parameters are as follows: -// - Is BuiltInRecovery enabled? -// - Is WAL mode enabled? -class SqlRecoveryTest - : public SqlRecoveryTestBase, - public testing::WithParamInterface<std::tuple<bool, bool>> { - public: - SqlRecoveryTest() - : SqlRecoveryTestBase(DatabaseOptions{.wal_mode = UseWalMode()}) { - scoped_feature_list_.InitWithFeatureStates( - {{features::kUseBuiltInRecoveryIfSupported, std::get<0>(GetParam())}, - {features::kEnableWALModeByDefault, UseWalMode()}}); - } +#if BUILDFLAG(IS_FUCHSIA) +// WAL + recovery is not supported on Fuchsia, so only test without WAL mode. +INSTANTIATE_TEST_SUITE_P(All, SqlRecoveryTest, testing::Values(false)); +#else +INSTANTIATE_TEST_SUITE_P(All, SqlRecoveryTest, testing::Bool()); +#endif - bool UseBuiltIn() { - return std::get<0>(GetParam()) && BuiltInRecovery::IsSupported(); - } - - bool UseWalMode() { - // The legacy recovery module does not support recovering WAL databases. - return std::get<1>(GetParam()) && BuiltInRecovery::IsSupported(); - } -}; - -// Tests specific to the newer `sql::BuiltInRecovery` interface. -// -// Creating a new `SqlRecoveryTest` should be preferred, if possible. -// These tests should include a comment indicating why it is not relevant to -// the legacy `sql::Recovery` module. -class SqlBuiltInRecoveryTest : public SqlRecoveryTestBase { - public: - SqlBuiltInRecoveryTest() : SqlRecoveryTestBase(DatabaseOptions{}) { - scoped_feature_list_.InitAndEnableFeature( - features::kUseBuiltInRecoveryIfSupported); - } -}; - -// Tests specific to the legacy `sql::Recovery` interface. -// -// Creating a new `SqlRecoveryTest` should be preferred, if possible. -// These tests should include a comment indicating why it is not relevant to -// the new `sql::BuiltInRecovery` module. -class SqlLegacyRecoveryTest : public SqlRecoveryTestBase { - public: - SqlLegacyRecoveryTest() - // The legacy recovery module does not support recovering WAL databases. - : SqlRecoveryTestBase(DatabaseOptions{.wal_mode = false}) { - scoped_feature_list_.InitAndDisableFeature( - features::kUseBuiltInRecoveryIfSupported); - - // TODO(https://crbug.com/1385500): All databases which use legacy recovery - // must either disable WAL mode manually or be migrated to the new recovery - // module before WAL mode may be turned on globally. This assertion is added - // here as a guard against accidental regression. - assert(!base::FeatureList::IsEnabled(features::kEnableWALModeByDefault)); - } -}; - -TEST_F(SqlBuiltInRecoveryTest, ShouldAttemptRecovery) { +TEST_P(SqlRecoveryTest, ShouldAttemptRecovery) { // Attempt to recover from corruption. ASSERT_TRUE(BuiltInRecovery::ShouldAttemptRecovery(&db_, SQLITE_CORRUPT)); @@ -190,212 +140,6 @@ EXPECT_TRUE(BuiltInRecovery::ShouldAttemptRecovery(&db_, SQLITE_CORRUPT)); } -// Baseline Recovery test covering the different ways to dispose of the scoped -// pointer received from Recovery::Begin(). -// -// This tests behavior of the legacy corruption recovery module which is not -// needed in the new API. Specifically, this tests what happens to the Recovery -// object when it goes out of scope. The new API does not publicly expose such -// an object. -TEST_F(SqlLegacyRecoveryTest, RecoverBasic) { - static const char kCreateSql[] = "CREATE TABLE x (t TEXT)"; - static const char kInsertSql[] = "INSERT INTO x VALUES ('This is a test')"; - static const char kAltInsertSql[] = - "INSERT INTO x VALUES ('That was a test')"; - ASSERT_TRUE(db_.Execute(kCreateSql)); - ASSERT_TRUE(db_.Execute(kInsertSql)); - ASSERT_EQ("CREATE TABLE x (t TEXT)", GetSchema(&db_)); - - // If the Recovery handle goes out of scope without being - // Recovered(), the database is razed. - { - std::unique_ptr<Recovery> recovery = Recovery::Begin(&db_, db_path_); - ASSERT_TRUE(recovery.get()); - } - EXPECT_FALSE(db_.is_open()); - ASSERT_TRUE(Reopen()); - EXPECT_TRUE(db_.is_open()); - ASSERT_EQ("", GetSchema(&db_)); - - // Recreate the database. - ASSERT_TRUE(db_.Execute(kCreateSql)); - ASSERT_TRUE(db_.Execute(kInsertSql)); - ASSERT_EQ("CREATE TABLE x (t TEXT)", GetSchema(&db_)); - - // Unrecoverable() also razes. - { - std::unique_ptr<Recovery> recovery = Recovery::Begin(&db_, db_path_); - ASSERT_TRUE(recovery.get()); - Recovery::Unrecoverable(std::move(recovery)); - - // TODO(shess): Test that calls to recover.db_ start failing. - } - EXPECT_FALSE(db_.is_open()); - ASSERT_TRUE(Reopen()); - EXPECT_TRUE(db_.is_open()); - ASSERT_EQ("", GetSchema(&db_)); - - // Attempting to recover a previously-recovered handle fails early. - { - std::unique_ptr<Recovery> recovery = Recovery::Begin(&db_, db_path_); - ASSERT_TRUE(recovery.get()); - recovery.reset(); - - recovery = Recovery::Begin(&db_, db_path_); - ASSERT_FALSE(recovery.get()); - } - ASSERT_TRUE(Reopen()); - - // Recreate the database. - ASSERT_TRUE(db_.Execute(kCreateSql)); - ASSERT_TRUE(db_.Execute(kInsertSql)); - ASSERT_EQ("CREATE TABLE x (t TEXT)", GetSchema(&db_)); - - // Unrecovered table to distinguish from recovered database. - ASSERT_TRUE(db_.Execute("CREATE TABLE y (c INTEGER)")); - ASSERT_NE("CREATE TABLE x (t TEXT)", GetSchema(&db_)); - - // Recovered() replaces the original with the "recovered" version. - { - std::unique_ptr<Recovery> recovery = Recovery::Begin(&db_, db_path_); - ASSERT_TRUE(recovery.get()); - - // Create the new version of the table. - ASSERT_TRUE(recovery->db()->Execute(kCreateSql)); - - // Insert different data to distinguish from original database. - ASSERT_TRUE(recovery->db()->Execute(kAltInsertSql)); - - // Successfully recovered. - ASSERT_TRUE(Recovery::Recovered(std::move(recovery))); - } - EXPECT_FALSE(db_.is_open()); - ASSERT_TRUE(Reopen()); - EXPECT_TRUE(db_.is_open()); - ASSERT_EQ("CREATE TABLE x (t TEXT)", GetSchema(&db_)); - - const char* kXSql = "SELECT * FROM x ORDER BY 1"; - ASSERT_EQ("That was a test", ExecuteWithResult(&db_, kXSql)); - - // Reset the database contents. - ASSERT_TRUE(db_.Execute("DELETE FROM x")); - ASSERT_TRUE(db_.Execute(kInsertSql)); - - // Rollback() discards recovery progress and leaves the database as it was. - { - std::unique_ptr<Recovery> recovery = Recovery::Begin(&db_, db_path_); - ASSERT_TRUE(recovery.get()); - - ASSERT_TRUE(recovery->db()->Execute(kCreateSql)); - ASSERT_TRUE(recovery->db()->Execute(kAltInsertSql)); - - Recovery::Rollback(std::move(recovery)); - } - EXPECT_FALSE(db_.is_open()); - ASSERT_TRUE(Reopen()); - EXPECT_TRUE(db_.is_open()); - ASSERT_EQ("CREATE TABLE x (t TEXT)", GetSchema(&db_)); - - ASSERT_EQ("This is a test", ExecuteWithResult(&db_, kXSql)); -} - -// Test operation of the virtual table used by Recovery. -// -// This tests behavior of the legacy corruption recovery module which is not -// needed in the new API. Specifically, this tests what happens to virtual -// tables added to the recovery database. The new API does not manually -// create the recovery database. Virtual table support is required to use -// the built-in module, but many of the other tests in this file would fail -// if virtual tables were not supported. -TEST_F(SqlLegacyRecoveryTest, VirtualTable) { - static const char kCreateSql[] = "CREATE TABLE x (t TEXT)"; - ASSERT_TRUE(db_.Execute(kCreateSql)); - ASSERT_TRUE(db_.Execute("INSERT INTO x VALUES ('This is a test')")); - ASSERT_TRUE(db_.Execute("INSERT INTO x VALUES ('That was a test')")); - - // Successfully recover the database. - { - std::unique_ptr<Recovery> recovery = Recovery::Begin(&db_, db_path_); - - // Tables to recover original DB, now at [corrupt]. - static const char kRecoveryCreateSql[] = - "CREATE VIRTUAL TABLE temp.recover_x using recover(" - " corrupt.x," - " t TEXT STRICT" - ")"; - ASSERT_TRUE(recovery->db()->Execute(kRecoveryCreateSql)); - - // Re-create the original schema. - ASSERT_TRUE(recovery->db()->Execute(kCreateSql)); - - // Copy the data from the recovery tables to the new database. - static const char kRecoveryCopySql[] = - "INSERT INTO x SELECT t FROM recover_x"; - ASSERT_TRUE(recovery->db()->Execute(kRecoveryCopySql)); - - // Successfully recovered. - ASSERT_TRUE(Recovery::Recovered(std::move(recovery))); - } - - // Since the database was not corrupt, the entire schema and all - // data should be recovered. - ASSERT_TRUE(Reopen()); - ASSERT_EQ("CREATE TABLE x (t TEXT)", GetSchema(&db_)); - - static const char* kXSql = "SELECT * FROM x ORDER BY 1"; - ASSERT_EQ("That was a test\nThis is a test", - ExecuteWithResults(&db_, kXSql, "|", "\n")); -} - -// Our corruption handling assumes that a corrupt index doesn't impact -// SQL statements that only operate on the associated table. This test verifies -// the assumption. -// -// This tests an assumption of the legacy corruption recovery module which is -// irrelevant to the new API. Specifically, that a corrupt index doesn't -// impact SQL statements that only operate on the associated table. -TEST_F(SqlLegacyRecoveryTest, TableIndependentFromCorruptIndex) { - static const char kCreateTable[] = - "CREATE TABLE rows(indexed INTEGER NOT NULL, unindexed INTEGER NOT NULL)"; - ASSERT_TRUE(db_.Execute(kCreateTable)); - ASSERT_TRUE(db_.Execute("CREATE UNIQUE INDEX rows_index ON rows(indexed)")); - - // Populate the table with powers of two. These numbers make it easy to see if - // SUM() missed a row. - ASSERT_TRUE(db_.Execute("INSERT INTO rows(indexed, unindexed) VALUES(1, 1)")); - ASSERT_TRUE(db_.Execute("INSERT INTO rows(indexed, unindexed) VALUES(2, 2)")); - ASSERT_TRUE(db_.Execute("INSERT INTO rows(indexed, unindexed) VALUES(4, 4)")); - ASSERT_TRUE(db_.Execute("INSERT INTO rows(indexed, unindexed) VALUES(8, 8)")); - - // SQL statement that performs a table scan. SUM(unindexed) heavily nudges - // SQLite to use the table instead of the index. - static const char kUnindexedCountSql[] = "SELECT SUM(unindexed) FROM rows"; - EXPECT_EQ("15", ExecuteWithResult(&db_, kUnindexedCountSql)) - << "No SQL statement should fail before corruption"; - - // SQL statement that performs an index scan. - static const char kIndexedCountSql[] = - "SELECT SUM(indexed) FROM rows INDEXED BY rows_index"; - EXPECT_EQ("15", ExecuteWithResult(&db_, kIndexedCountSql)) - << "Table scan should not fail due to corrupt index"; - - db_.Close(); - ASSERT_TRUE(sql::test::CorruptIndexRootPage(db_path_, "rows_index")); - ASSERT_TRUE(Reopen()); - - { - sql::test::ScopedErrorExpecter expecter; - expecter.ExpectError(SQLITE_CORRUPT); - EXPECT_FALSE(db_.Execute(kIndexedCountSql)) - << "Index scan on corrupt index should fail"; - EXPECT_TRUE(expecter.SawExpectedErrors()) - << "Index scan on corrupt index should fail"; - } - - EXPECT_EQ("15", ExecuteWithResult(&db_, kUnindexedCountSql)) - << "Table scan should not fail due to corrupt index"; -} - TEST_P(SqlRecoveryTest, RecoverCorruptIndex) { static const char kCreateTable[] = "CREATE TABLE rows(indexed INTEGER NOT NULL, unindexed INTEGER NOT NULL)"; @@ -424,28 +168,15 @@ // Recovery::Begin() does not support a pre-existing error callback. db_.reset_error_callback(); - if (UseBuiltIn()) { - EXPECT_EQ(BuiltInRecovery::RecoverDatabase( - &db_, BuiltInRecovery::Strategy::kRecoverOrRaze), - SqliteResultCode::kOk); - histogram_tester_.ExpectUniqueSample( - kRecoveryResultHistogramName, BuiltInRecovery::Result::kSuccess, - /*expected_bucket_count=*/1); - histogram_tester_.ExpectUniqueSample(kRecoveryResultCodeHistogramName, - SqliteLoggedResultCode::kNoError, - /*expected_bucket_count=*/1); - return; - } - - std::unique_ptr<Recovery> recovery = Recovery::Begin(&db_, db_path_); - ASSERT_TRUE(recovery.get()); - - ASSERT_TRUE(recovery->db()->Execute(kCreateTable)); - ASSERT_TRUE(recovery->db()->Execute(kCreateIndex)); - - size_t rows = 0; - ASSERT_TRUE(recovery->AutoRecoverTable("rows", &rows)); - ASSERT_TRUE(Recovery::Recovered(std::move(recovery))); + EXPECT_EQ(BuiltInRecovery::RecoverDatabase( + &db_, BuiltInRecovery::Strategy::kRecoverOrRaze), + SqliteResultCode::kOk); + histogram_tester_.ExpectUniqueSample(kRecoveryResultHistogramName, + BuiltInRecovery::Result::kSuccess, + /*expected_bucket_count=*/1); + histogram_tester_.ExpectUniqueSample(kRecoveryResultCodeHistogramName, + SqliteLoggedResultCode::kNoError, + /*expected_bucket_count=*/1); })); // SUM(unindexed) heavily nudges SQLite to use the table instead of the index. @@ -540,22 +271,9 @@ // Recovery::Begin() does not support a pre-existing error callback. db_.reset_error_callback(); - if (UseBuiltIn()) { - EXPECT_EQ(BuiltInRecovery::RecoverDatabase( - &db_, BuiltInRecovery::Strategy::kRecoverOrRaze), - SqliteResultCode::kOk); - return; - } - - std::unique_ptr<Recovery> recovery = Recovery::Begin(&db_, db_path_); - ASSERT_TRUE(recovery.get()); - - ASSERT_TRUE(recovery->db()->Execute(kCreateTable)); - ASSERT_TRUE(recovery->db()->Execute(kCreateIndex)); - - size_t rows = 0; - ASSERT_TRUE(recovery->AutoRecoverTable("rows", &rows)); - ASSERT_TRUE(Recovery::Recovered(std::move(recovery))); + EXPECT_EQ(BuiltInRecovery::RecoverDatabase( + &db_, BuiltInRecovery::Strategy::kRecoverOrRaze), + SqliteResultCode::kOk); })); // SUM(unindexed) heavily nudges SQLite to use the table instead of the index. @@ -587,26 +305,15 @@ } // Test expected case where everything works. - if (UseBuiltIn()) { - EXPECT_EQ( - BuiltInRecovery::RecoverDatabase( - &db_, BuiltInRecovery::Strategy::kRecoverWithMetaVersionOrRaze), - SqliteResultCode::kOk); - histogram_tester_.ExpectUniqueSample(kRecoveryResultHistogramName, - BuiltInRecovery::Result::kSuccess, - /*expected_bucket_count=*/1); - histogram_tester_.ExpectUniqueSample(kRecoveryResultCodeHistogramName, - SqliteLoggedResultCode::kNoError, - /*expected_bucket_count=*/1); - } else { - std::unique_ptr<Recovery> recovery = Recovery::Begin(&db_, db_path_); - EXPECT_TRUE(recovery->SetupMeta()); - int version = 0; - EXPECT_TRUE(recovery->GetMetaVersionNumber(&version)); - EXPECT_EQ(kVersion, version); - - Recovery::Rollback(std::move(recovery)); - } + EXPECT_EQ(BuiltInRecovery::RecoverDatabase( + &db_, BuiltInRecovery::Strategy::kRecoverWithMetaVersionOrRaze), + SqliteResultCode::kOk); + histogram_tester_.ExpectUniqueSample(kRecoveryResultHistogramName, + BuiltInRecovery::Result::kSuccess, + /*expected_bucket_count=*/1); + histogram_tester_.ExpectUniqueSample(kRecoveryResultCodeHistogramName, + SqliteLoggedResultCode::kNoError, + /*expected_bucket_count=*/1); ASSERT_TRUE(Reopen()); // Handle was poisoned. @@ -615,56 +322,31 @@ // Test version row missing. EXPECT_TRUE(db_.Execute("DELETE FROM meta WHERE key = 'version'")); - if (UseBuiltIn()) { - EXPECT_EQ( - BuiltInRecovery::RecoverDatabase( - &db_, BuiltInRecovery::Strategy::kRecoverWithMetaVersionOrRaze), - SqliteResultCode::kError); - histogram_tester_.ExpectBucketCount( - kRecoveryResultHistogramName, - BuiltInRecovery::Result::kFailedMetaTableVersionWasInvalid, - /*expected_count=*/1); - histogram_tester_.ExpectUniqueSample(kRecoveryResultCodeHistogramName, - SqliteLoggedResultCode::kNoError, - /*expected_bucket_count=*/2); - } else { - std::unique_ptr<Recovery> recovery = Recovery::Begin(&db_, db_path_); - EXPECT_TRUE(recovery->SetupMeta()); - int version = 0; - EXPECT_FALSE(recovery->GetMetaVersionNumber(&version)); - EXPECT_EQ(0, version); - - Recovery::Rollback(std::move(recovery)); - } + EXPECT_EQ(BuiltInRecovery::RecoverDatabase( + &db_, BuiltInRecovery::Strategy::kRecoverWithMetaVersionOrRaze), + SqliteResultCode::kError); + histogram_tester_.ExpectBucketCount( + kRecoveryResultHistogramName, + BuiltInRecovery::Result::kFailedMetaTableVersionWasInvalid, + /*expected_count=*/1); + histogram_tester_.ExpectUniqueSample(kRecoveryResultCodeHistogramName, + SqliteLoggedResultCode::kNoError, + /*expected_bucket_count=*/2); ASSERT_TRUE(Reopen()); // Handle was poisoned. // Test meta table missing. - if (UseBuiltIn()) { - ASSERT_FALSE(db_.DoesTableExist("meta")); + ASSERT_FALSE(db_.DoesTableExist("meta")); - EXPECT_EQ( - BuiltInRecovery::RecoverDatabase( - &db_, BuiltInRecovery::Strategy::kRecoverWithMetaVersionOrRaze), - SqliteResultCode::kError); - histogram_tester_.ExpectBucketCount( - kRecoveryResultHistogramName, - BuiltInRecovery::Result::kFailedMetaTableDoesNotExist, - /*expected_count=*/1); - histogram_tester_.ExpectUniqueSample(kRecoveryResultCodeHistogramName, - SqliteLoggedResultCode::kNoError, - /*expected_bucket_count=*/3); - } else { - // The table was rolled back after the recovery failure. Manually drop the - // table. - ASSERT_TRUE(db_.DoesTableExist("meta")); - EXPECT_TRUE(db_.Execute("DROP TABLE meta")); - - sql::test::ScopedErrorExpecter expecter; - expecter.ExpectError(SQLITE_CORRUPT); // From virtual table. - std::unique_ptr<Recovery> recovery = Recovery::Begin(&db_, db_path_); - EXPECT_FALSE(recovery->SetupMeta()); - ASSERT_TRUE(expecter.SawExpectedErrors()); - } + EXPECT_EQ(BuiltInRecovery::RecoverDatabase( + &db_, BuiltInRecovery::Strategy::kRecoverWithMetaVersionOrRaze), + SqliteResultCode::kError); + histogram_tester_.ExpectBucketCount( + kRecoveryResultHistogramName, + BuiltInRecovery::Result::kFailedMetaTableDoesNotExist, + /*expected_count=*/1); + histogram_tester_.ExpectUniqueSample(kRecoveryResultCodeHistogramName, + SqliteLoggedResultCode::kNoError, + /*expected_bucket_count=*/3); } // Baseline AutoRecoverTable() test. @@ -681,35 +363,9 @@ static const char kXSql[] = "SELECT * FROM x ORDER BY 1"; const std::string orig_data(ExecuteWithResults(&db_, kXSql, "|", "\n")); - if (UseBuiltIn()) { - EXPECT_EQ(BuiltInRecovery::RecoverDatabase( - &db_, BuiltInRecovery::Strategy::kRecoverOrRaze), - SqliteResultCode::kOk); - } else { - // Create a lame-duck table which will not be propagated by recovery to - // detect that the recovery code actually ran. - ASSERT_TRUE(db_.Execute("CREATE TABLE y (c TEXT)")); - ASSERT_NE(orig_schema, GetSchema(&db_)); - - std::unique_ptr<Recovery> recovery = Recovery::Begin(&db_, db_path_); - ASSERT_TRUE(recovery->db()->Execute(kCreateSql)); - - // Save a copy of the temp db's schema before recovering the table. - static const char kTempSchemaSql[] = - "SELECT name, sql FROM sqlite_temp_schema"; - const std::string temp_schema( - ExecuteWithResults(recovery->db(), kTempSchemaSql, "|", "\n")); - - size_t rows = 0; - EXPECT_TRUE(recovery->AutoRecoverTable("x", &rows)); - EXPECT_EQ(2u, rows); - - // Test that any additional temp tables were cleaned up. - EXPECT_EQ(temp_schema, - ExecuteWithResults(recovery->db(), kTempSchemaSql, "|", "\n")); - - ASSERT_TRUE(Recovery::Recovered(std::move(recovery))); - } + EXPECT_EQ(BuiltInRecovery::RecoverDatabase( + &db_, BuiltInRecovery::Strategy::kRecoverOrRaze), + SqliteResultCode::kOk); // Since the database was not corrupt, the entire schema and all // data should be recovered. @@ -717,22 +373,10 @@ ASSERT_EQ(orig_schema, GetSchema(&db_)); ASSERT_EQ(orig_data, ExecuteWithResults(&db_, kXSql, "|", "\n")); - // Recovery fails if the target table doesn't exist. - if (UseBuiltIn()) { - // ... or it can succeed silently, since there's nothing to do. - EXPECT_EQ(BuiltInRecovery::RecoverDatabase( - &db_, BuiltInRecovery::Strategy::kRecoverOrRaze), - SqliteResultCode::kOk); - } else { - std::unique_ptr<Recovery> recovery = Recovery::Begin(&db_, db_path_); - ASSERT_TRUE(recovery->db()->Execute(kCreateSql)); - - // TODO(shess): Should this failure implicitly lead to Raze()? - size_t rows = 0; - EXPECT_FALSE(recovery->AutoRecoverTable("y", &rows)); - - Recovery::Unrecoverable(std::move(recovery)); - } + // Recovery succeeds silently, since there's nothing to do. + EXPECT_EQ(BuiltInRecovery::RecoverDatabase( + &db_, BuiltInRecovery::Strategy::kRecoverOrRaze), + SqliteResultCode::kOk); } // Test that default values correctly replace nulls. The recovery @@ -761,39 +405,9 @@ std::string final_schema(orig_schema); std::string final_data(orig_data); - if (UseBuiltIn()) { - EXPECT_EQ(BuiltInRecovery::RecoverDatabase( - &db_, BuiltInRecovery::Strategy::kRecoverOrRaze), - SqliteResultCode::kOk); - } else { - // Create a lame-duck table which will not be propagated by recovery to - // detect that the recovery code actually ran. - ASSERT_TRUE(db_.Execute("CREATE TABLE y (c TEXT)")); - ASSERT_NE(orig_schema, GetSchema(&db_)); - - // Mechanically adjust the stored schema and data to allow detecting - // where the default value is coming from. The target table is just - // like the original with the default for [t] changed, to signal - // defaults coming from the recovery system. The two %5 rows should - // get the target-table default for [t], while the others should get - // the source-table default. - size_t pos; - while ((pos = final_schema.find("'a''a'")) != std::string::npos) { - final_schema.replace(pos, 6, "'c''c'"); - } - while ((pos = final_data.find("5|a'a")) != std::string::npos) { - final_data.replace(pos, 5, "5|c'c"); - } - std::unique_ptr<Recovery> recovery = Recovery::Begin(&db_, db_path_); - // Different default to detect which table provides the default. - ASSERT_TRUE(recovery->db()->Execute(final_schema.c_str())); - - size_t rows = 0; - EXPECT_TRUE(recovery->AutoRecoverTable("x", &rows)); - EXPECT_EQ(4u, rows); - - ASSERT_TRUE(Recovery::Recovered(std::move(recovery))); - } + EXPECT_EQ(BuiltInRecovery::RecoverDatabase( + &db_, BuiltInRecovery::Strategy::kRecoverOrRaze), + SqliteResultCode::kOk); // Since the database was not corrupt, the entire schema and all // data should be recovered. @@ -802,50 +416,6 @@ ASSERT_EQ(final_data, ExecuteWithResults(&db_, kXSql, "|", "\n")); } -// Test that rows with NULL in a NOT NULL column are filtered -// correctly. In the wild, this would probably happen due to -// corruption, but here it is simulated by recovering a table which -// allowed nulls into a table which does not. -// -// This tests behavior of the legacy corruption recovery module which is not -// needed in the new API. Specifically, this tests what happens to tables -// with NULL values in non-nullable columns. This test is not easily -// replicated, since SQLite does not support ALTER COLUMN. We'll trust that -// it's handled properly upstream. -TEST_F(SqlLegacyRecoveryTest, AutoRecoverTableNullFilter) { - static const char kOrigSchema[] = "CREATE TABLE x (id INTEGER, t TEXT)"; - static const char kFinalSchema[] = - "CREATE TABLE x (id INTEGER, t TEXT NOT NULL)"; - - ASSERT_TRUE(db_.Execute(kOrigSchema)); - ASSERT_TRUE(db_.Execute("INSERT INTO x VALUES (5, NULL)")); - ASSERT_TRUE(db_.Execute("INSERT INTO x VALUES (15, 'this is a test')")); - - // Create a lame-duck table which will not be propagated by recovery to - // detect that the recovery code actually ran. - ASSERT_EQ(kOrigSchema, GetSchema(&db_)); - ASSERT_TRUE(db_.Execute("CREATE TABLE y (c TEXT)")); - ASSERT_NE(kOrigSchema, GetSchema(&db_)); - - { - std::unique_ptr<Recovery> recovery = Recovery::Begin(&db_, db_path_); - ASSERT_TRUE(recovery->db()->Execute(kFinalSchema)); - - size_t rows = 0; - EXPECT_TRUE(recovery->AutoRecoverTable("x", &rows)); - EXPECT_EQ(1u, rows); - - ASSERT_TRUE(Recovery::Recovered(std::move(recovery))); - } - - // The schema should be the same, but only one row of data should - // have been recovered. - ASSERT_TRUE(Reopen()); - ASSERT_EQ(kFinalSchema, GetSchema(&db_)); - static const char kXSql[] = "SELECT * FROM x ORDER BY 1"; - ASSERT_EQ("15|this is a test", ExecuteWithResults(&db_, kXSql, "|", "\n")); -} - // Test AutoRecoverTable with a ROWID alias. TEST_P(SqlRecoveryTest, AutoRecoverTableWithRowid) { // The rowid alias is almost always the first column, intentionally @@ -861,25 +431,9 @@ static const char kXSql[] = "SELECT * FROM x ORDER BY 1"; const std::string orig_data(ExecuteWithResults(&db_, kXSql, "|", "\n")); - if (UseBuiltIn()) { - EXPECT_EQ(BuiltInRecovery::RecoverDatabase( - &db_, BuiltInRecovery::Strategy::kRecoverOrRaze), - SqliteResultCode::kOk); - } else { - // Create a lame-duck table which will not be propagated by recovery to - // detect that the recovery code actually ran. - ASSERT_TRUE(db_.Execute("CREATE TABLE y (c TEXT)")); - ASSERT_NE(orig_schema, GetSchema(&db_)); - - std::unique_ptr<Recovery> recovery = Recovery::Begin(&db_, db_path_); - ASSERT_TRUE(recovery->db()->Execute(kCreateSql)); - - size_t rows = 0; - EXPECT_TRUE(recovery->AutoRecoverTable("x", &rows)); - EXPECT_EQ(2u, rows); - - ASSERT_TRUE(Recovery::Recovered(std::move(recovery))); - } + EXPECT_EQ(BuiltInRecovery::RecoverDatabase( + &db_, BuiltInRecovery::Strategy::kRecoverOrRaze), + SqliteResultCode::kOk); // Since the database was not corrupt, the entire schema and all // data should be recovered. @@ -888,154 +442,6 @@ ASSERT_EQ(orig_data, ExecuteWithResults(&db_, kXSql, "|", "\n")); } -// Test that a compound primary key doesn't fire the ROWID code. -// -// This tests behavior of the legacy corruption recovery module which is not -// needed in the new API. Specifically, this tests how ROWID tables are -// handled. -TEST_F(SqlLegacyRecoveryTest, AutoRecoverTableWithCompoundKey) { - static const char kCreateSql[] = - "CREATE TABLE x (" - "id INTEGER NOT NULL," - "id2 TEXT NOT NULL," - "t TEXT," - "PRIMARY KEY (id, id2)" - ")"; - ASSERT_TRUE(db_.Execute(kCreateSql)); - - // NOTE(shess): Do not accidentally use [id] 1, 2, 3, as those will - // be the ROWID values. - ASSERT_TRUE(db_.Execute("INSERT INTO x VALUES (1, 'a', 'This is a test')")); - ASSERT_TRUE(db_.Execute("INSERT INTO x VALUES (1, 'b', 'That was a test')")); - ASSERT_TRUE(db_.Execute("INSERT INTO x VALUES (2, 'a', 'Another test')")); - - // Save aside a copy of the original schema and data. - const std::string orig_schema(GetSchema(&db_)); - static const char kXSql[] = "SELECT * FROM x ORDER BY 1"; - const std::string orig_data(ExecuteWithResults(&db_, kXSql, "|", "\n")); - - // Create a lame-duck table which will not be propagated by recovery to - // detect that the recovery code actually ran. - ASSERT_TRUE(db_.Execute("CREATE TABLE y (c TEXT)")); - ASSERT_NE(orig_schema, GetSchema(&db_)); - - { - std::unique_ptr<Recovery> recovery = Recovery::Begin(&db_, db_path_); - ASSERT_TRUE(recovery->db()->Execute(kCreateSql)); - - size_t rows = 0; - EXPECT_TRUE(recovery->AutoRecoverTable("x", &rows)); - EXPECT_EQ(3u, rows); - - ASSERT_TRUE(Recovery::Recovered(std::move(recovery))); - } - - // Since the database was not corrupt, the entire schema and all - // data should be recovered. - ASSERT_TRUE(Reopen()); - ASSERT_EQ(orig_schema, GetSchema(&db_)); - ASSERT_EQ(orig_data, ExecuteWithResults(&db_, kXSql, "|", "\n")); -} - -// Test recovering from a table with fewer columns than the target. -// -// This tests behavior of the legacy corruption recovery module which is not -// needed in the new API. Specifically, this tests how tables with missing -// columns are handled. -TEST_F(SqlLegacyRecoveryTest, AutoRecoverTableMissingColumns) { - static const char kCreateSql[] = - "CREATE TABLE x (id INTEGER PRIMARY KEY, t0 TEXT)"; - static const char kAlterSql[] = - "ALTER TABLE x ADD COLUMN t1 TEXT DEFAULT 't'"; - ASSERT_TRUE(db_.Execute(kCreateSql)); - ASSERT_TRUE(db_.Execute("INSERT INTO x VALUES (1, 'This is')")); - ASSERT_TRUE(db_.Execute("INSERT INTO x VALUES (2, 'That was')")); - - // Generate the expected info by faking a table to match what recovery will - // create. - const std::string orig_schema(GetSchema(&db_)); - static const char kXSql[] = "SELECT * FROM x ORDER BY 1"; - std::string expected_schema; - std::string expected_data; - { - ASSERT_TRUE(db_.BeginTransaction()); - ASSERT_TRUE(db_.Execute(kAlterSql)); - - expected_schema = GetSchema(&db_); - expected_data = ExecuteWithResults(&db_, kXSql, "|", "\n"); - - db_.RollbackTransaction(); - } - - // Following tests are pointless if the rollback didn't work. - ASSERT_EQ(orig_schema, GetSchema(&db_)); - - // Recover the previous version of the table into the altered version. - { - std::unique_ptr<Recovery> recovery = Recovery::Begin(&db_, db_path_); - ASSERT_TRUE(recovery->db()->Execute(kCreateSql)); - ASSERT_TRUE(recovery->db()->Execute(kAlterSql)); - size_t rows = 0; - EXPECT_TRUE(recovery->AutoRecoverTable("x", &rows)); - EXPECT_EQ(2u, rows); - ASSERT_TRUE(Recovery::Recovered(std::move(recovery))); - } - - // Since the database was not corrupt, the entire schema and all - // data should be recovered. - ASSERT_TRUE(Reopen()); - ASSERT_EQ(expected_schema, GetSchema(&db_)); - ASSERT_EQ(expected_data, ExecuteWithResults(&db_, kXSql, "|", "\n")); -} - -// Recover a golden file where an interior page has been manually modified so -// that the number of cells is greater than will fit on a single page. This -// case happened in <http://crbug.com/387868>. -TEST_P(SqlRecoveryTest, Bug387868) { - base::FilePath golden_path; - ASSERT_TRUE(base::PathService::Get(sql::test::DIR_TEST_DATA, &golden_path)); - golden_path = golden_path.AppendASCII("recovery_387868"); - db_.Close(); - ASSERT_TRUE(base::CopyFile(golden_path, db_path_)); - ASSERT_TRUE(Reopen()); - - if (UseBuiltIn()) { - EXPECT_EQ(BuiltInRecovery::RecoverDatabase( - &db_, BuiltInRecovery::Strategy::kRecoverOrRaze), - SqliteResultCode::kOk); - } else { - std::unique_ptr<Recovery> recovery = Recovery::Begin(&db_, db_path_); - ASSERT_TRUE(recovery.get()); - - // Create the new version of the table. - static const char kCreateSql[] = - "CREATE TABLE x (id INTEGER PRIMARY KEY, t0 TEXT)"; - ASSERT_TRUE(recovery->db()->Execute(kCreateSql)); - - size_t rows = 0; - EXPECT_TRUE(recovery->AutoRecoverTable("x", &rows)); - EXPECT_EQ(43u, rows); - - // Successfully recovered. - EXPECT_TRUE(Recovery::Recovered(std::move(recovery))); - } -} - -// Memory-mapped I/O interacts poorly with I/O errors. Make sure the recovery -// database doesn't accidentally enable it. -// -// This tests behavior of the legacy corruption recovery module which is not -// needed in the new API. Specifically, that MMAPing is disabled. -TEST_F(SqlLegacyRecoveryTest, NoMmap) { - std::unique_ptr<Recovery> recovery = Recovery::Begin(&db_, db_path_); - ASSERT_TRUE(recovery.get()); - - // In the current implementation, the PRAGMA successfully runs with no result - // rows. Running with a single result of |0| is also acceptable. - Statement s(recovery->db()->GetUniqueStatement("PRAGMA mmap_size")); - EXPECT_TRUE(!s.Step() || !s.ColumnInt64(0)); -} - void TestRecoverDatabase(Database& db, const base::FilePath& db_path, bool with_meta, @@ -1110,13 +516,9 @@ TEST_P(SqlRecoveryTest, RecoverDatabase) { auto run_recovery = base::BindLambdaForTesting([&]() { - if (UseBuiltIn()) { - EXPECT_EQ(BuiltInRecovery::RecoverDatabase( - &db_, BuiltInRecovery::Strategy::kRecoverOrRaze), - SqliteResultCode::kOk); - } else { - Recovery::RecoverDatabase(&db_, db_path_); - } + EXPECT_EQ(BuiltInRecovery::RecoverDatabase( + &db_, BuiltInRecovery::Strategy::kRecoverOrRaze), + SqliteResultCode::kOk); }); TestRecoverDatabase(db_, db_path_, /*with_meta=*/false, @@ -1125,14 +527,10 @@ TEST_P(SqlRecoveryTest, RecoverDatabaseMeta) { auto run_recovery = base::BindLambdaForTesting([&]() { - if (UseBuiltIn()) { - EXPECT_EQ( - BuiltInRecovery::RecoverDatabase( - &db_, BuiltInRecovery::Strategy::kRecoverWithMetaVersionOrRaze), - SqliteResultCode::kOk); - } else { - Recovery::RecoverDatabaseWithMetaVersion(&db_, db_path_); - } + EXPECT_EQ( + BuiltInRecovery::RecoverDatabase( + &db_, BuiltInRecovery::Strategy::kRecoverWithMetaVersionOrRaze), + SqliteResultCode::kOk); }); TestRecoverDatabase(db_, db_path_, /*with_meta=*/true, @@ -1207,31 +605,28 @@ TEST_P(SqlRecoveryTest, RecoverIfPossibleWithPerDatabaseUma) { auto run_recovery = base::BindLambdaForTesting([&]() { EXPECT_TRUE(BuiltInRecovery::RecoverIfPossible( - &db_, SQLITE_CORRUPT, BuiltInRecovery::Strategy::kRecoverOrRaze, - &features::kUseBuiltInRecoveryIfSupported)); + &db_, SQLITE_CORRUPT, BuiltInRecovery::Strategy::kRecoverOrRaze)); }); TestRecoverDatabase(db_, db_path_, /*with_meta=*/false, std::move(run_recovery)); - if (UseBuiltIn()) { - // Log to the overall histograms. - histogram_tester_.ExpectUniqueSample(kRecoveryResultHistogramName, - BuiltInRecovery::Result::kSuccess, - /*expected_bucket_count=*/1); - histogram_tester_.ExpectUniqueSample(kRecoveryResultCodeHistogramName, - SqliteLoggedResultCode::kNoError, - /*expected_bucket_count=*/1); - // And the histograms for this specific feature. - histogram_tester_.ExpectUniqueSample( - base::StrCat({kRecoveryResultHistogramName, ".MyFeatureDatabase"}), - BuiltInRecovery::Result::kSuccess, - /*expected_bucket_count=*/1); - histogram_tester_.ExpectUniqueSample( - base::StrCat({kRecoveryResultCodeHistogramName, ".MyFeatureDatabase"}), - SqliteLoggedResultCode::kNoError, - /*expected_bucket_count=*/1); - } + // Log to the overall histograms. + histogram_tester_.ExpectUniqueSample(kRecoveryResultHistogramName, + BuiltInRecovery::Result::kSuccess, + /*expected_bucket_count=*/1); + histogram_tester_.ExpectUniqueSample(kRecoveryResultCodeHistogramName, + SqliteLoggedResultCode::kNoError, + /*expected_bucket_count=*/1); + // And the histograms for this specific feature. + histogram_tester_.ExpectUniqueSample( + base::StrCat({kRecoveryResultHistogramName, ".MyFeatureDatabase"}), + BuiltInRecovery::Result::kSuccess, + /*expected_bucket_count=*/1); + histogram_tester_.ExpectUniqueSample( + base::StrCat({kRecoveryResultCodeHistogramName, ".MyFeatureDatabase"}), + SqliteLoggedResultCode::kNoError, + /*expected_bucket_count=*/1); } TEST_P(SqlRecoveryTest, RecoverDatabaseWithView) { @@ -1270,13 +665,9 @@ // Database handle is valid before recovery, poisoned after. static constexpr char kTrivialSql[] = "SELECT COUNT(*) FROM sqlite_schema"; EXPECT_TRUE(db.IsSQLValid(kTrivialSql)); - if (UseBuiltIn()) { - EXPECT_EQ(BuiltInRecovery::RecoverDatabase( - &db, BuiltInRecovery::Strategy::kRecoverOrRaze), - SqliteResultCode::kOk); - } else { - Recovery::RecoverDatabase(&db, db_path_); - } + EXPECT_EQ(BuiltInRecovery::RecoverDatabase( + &db, BuiltInRecovery::Strategy::kRecoverOrRaze), + SqliteResultCode::kOk); EXPECT_FALSE(db.IsSQLValid(kTrivialSql)); // Since the database was not corrupt, the entire schema and all data should @@ -1303,21 +694,16 @@ ASSERT_FALSE(Reopen()); // This should "recover" the database by making it valid, but empty. - if (UseBuiltIn()) { - EXPECT_EQ(BuiltInRecovery::RecoverDatabase( - &db_, BuiltInRecovery::Strategy::kRecoverOrRaze), - SqliteResultCode::kNotADatabase); - histogram_tester_.ExpectUniqueSample( - kRecoveryResultHistogramName, - BuiltInRecovery::Result::kFailedRecoveryRun, - /*expected_bucket_count=*/1); - histogram_tester_.ExpectUniqueSample( - kRecoveryResultCodeHistogramName, - SqliteLoggedResultCode::kNotADatabase, - /*expected_bucket_count=*/1); - } else { - Recovery::RecoverDatabase(&db_, db_path_); - } + EXPECT_EQ(BuiltInRecovery::RecoverDatabase( + &db_, BuiltInRecovery::Strategy::kRecoverOrRaze), + SqliteResultCode::kNotADatabase); + histogram_tester_.ExpectUniqueSample( + kRecoveryResultHistogramName, + BuiltInRecovery::Result::kFailedRecoveryRun, + /*expected_bucket_count=*/1); + histogram_tester_.ExpectUniqueSample(kRecoveryResultCodeHistogramName, + SqliteLoggedResultCode::kNotADatabase, + /*expected_bucket_count=*/1); ASSERT_TRUE(expecter.SawExpectedErrors()); } @@ -1347,16 +733,6 @@ ASSERT_TRUE(sql::test::CorruptIndexRootPage(db_path_, "rows_index")); ASSERT_TRUE(Reopen()); - if (!UseBuiltIn()) { - // Run recovery code, then rollback. Database remains the same. - std::unique_ptr<Recovery> recovery = - Recovery::BeginRecoverDatabase(&db_, db_path_); - ASSERT_TRUE(recovery); - Recovery::Rollback(std::move(recovery)); - db_.Close(); - ASSERT_TRUE(Reopen()); - } - static const char kIndexedCountSql[] = "SELECT SUM(indexed) FROM rows INDEXED BY rows_index"; { @@ -1369,16 +745,9 @@ } // Run recovery code, then commit. The index is recovered. - if (UseBuiltIn()) { - EXPECT_EQ(BuiltInRecovery::RecoverDatabase( - &db_, BuiltInRecovery::Strategy::kRecoverOrRaze), - SqliteResultCode::kOk); - } else { - std::unique_ptr<Recovery> recovery = - Recovery::BeginRecoverDatabase(&db_, db_path_); - ASSERT_TRUE(recovery); - ASSERT_TRUE(Recovery::Recovered(std::move(recovery))); - } + EXPECT_EQ(BuiltInRecovery::RecoverDatabase( + &db_, BuiltInRecovery::Strategy::kRecoverOrRaze), + SqliteResultCode::kOk); db_.Close(); ASSERT_TRUE(Reopen()); @@ -1402,22 +771,16 @@ ASSERT_FALSE(Reopen()); // Begin() should fail. - if (UseBuiltIn()) { - EXPECT_EQ(BuiltInRecovery::RecoverDatabase( - &db_, BuiltInRecovery::Strategy::kRecoverOrRaze), - SqliteResultCode::kNotADatabase); - histogram_tester_.ExpectUniqueSample( - kRecoveryResultHistogramName, - BuiltInRecovery::Result::kFailedRecoveryRun, - /*expected_bucket_count=*/1); - histogram_tester_.ExpectUniqueSample( - kRecoveryResultCodeHistogramName, - SqliteLoggedResultCode::kNotADatabase, - /*expected_bucket_count=*/1); - } else { - std::unique_ptr<Recovery> recovery = Recovery::Begin(&db_, db_path_); - EXPECT_FALSE(recovery.get()); - } + EXPECT_EQ(BuiltInRecovery::RecoverDatabase( + &db_, BuiltInRecovery::Strategy::kRecoverOrRaze), + SqliteResultCode::kNotADatabase); + histogram_tester_.ExpectUniqueSample( + kRecoveryResultHistogramName, + BuiltInRecovery::Result::kFailedRecoveryRun, + /*expected_bucket_count=*/1); + histogram_tester_.ExpectUniqueSample(kRecoveryResultCodeHistogramName, + SqliteLoggedResultCode::kNotADatabase, + /*expected_bucket_count=*/1); ASSERT_TRUE(expecter.SawExpectedErrors()); } } @@ -1430,8 +793,7 @@ int initial_page_size, const std::string& expected_initial_page_size, int final_page_size, - const std::string& expected_final_page_size, - bool use_built_in) { + const std::string& expected_final_page_size) { static const char kCreateSql[] = "CREATE TABLE x (t TEXT)"; static const char kInsertSql1[] = "INSERT INTO x VALUES ('This is a test')"; static const char kInsertSql2[] = "INSERT INTO x VALUES ('That was a test')"; @@ -1454,13 +816,9 @@ ASSERT_TRUE(recover_db.Open(db_path)); // Recovery will use the page size set in the database object, which may not // match the file's page size. - if (use_built_in) { - EXPECT_EQ(BuiltInRecovery::RecoverDatabase( - &recover_db, BuiltInRecovery::Strategy::kRecoverOrRaze), - SqliteResultCode::kOk); - } else { - Recovery::RecoverDatabase(&recover_db, db_path); - } + EXPECT_EQ(BuiltInRecovery::RecoverDatabase( + &recover_db, BuiltInRecovery::Strategy::kRecoverOrRaze), + SqliteResultCode::kOk); // Recovery poisoned the handle, must re-open. recover_db.Close(); @@ -1484,63 +842,44 @@ // Check the default page size first. EXPECT_NO_FATAL_FAILURE(TestPageSize( db_path_, DatabaseOptions::kDefaultPageSize, default_page_size, - DatabaseOptions::kDefaultPageSize, default_page_size, UseBuiltIn())); + DatabaseOptions::kDefaultPageSize, default_page_size)); // Sync uses 32k pages. EXPECT_NO_FATAL_FAILURE( - TestPageSize(db_path_, 32768, "32768", 32768, "32768", UseBuiltIn())); + TestPageSize(db_path_, 32768, "32768", 32768, "32768")); // Many clients use 4k pages. This is the SQLite default after 3.12.0. - EXPECT_NO_FATAL_FAILURE( - TestPageSize(db_path_, 4096, "4096", 4096, "4096", UseBuiltIn())); + EXPECT_NO_FATAL_FAILURE(TestPageSize(db_path_, 4096, "4096", 4096, "4096")); // 1k is the default page size before 3.12.0. - EXPECT_NO_FATAL_FAILURE( - TestPageSize(db_path_, 1024, "1024", 1024, "1024", UseBuiltIn())); + EXPECT_NO_FATAL_FAILURE(TestPageSize(db_path_, 1024, "1024", 1024, "1024")); ASSERT_NE("2048", default_page_size); - if (UseBuiltIn()) { - // Databases with no page size specified should recover to the page size of - // the source database. - EXPECT_NO_FATAL_FAILURE(TestPageSize(db_path_, 2048, "2048", - DatabaseOptions::kDefaultPageSize, - "2048", UseBuiltIn())); - } else { - // Databases with no page size specified should recover with the new default - // page size. 2k has never been the default page size. - EXPECT_NO_FATAL_FAILURE(TestPageSize(db_path_, 2048, "2048", - DatabaseOptions::kDefaultPageSize, - default_page_size, UseBuiltIn())); - } + // Databases with no page size specified should recover to the page size of + // the source database. + EXPECT_NO_FATAL_FAILURE(TestPageSize( + db_path_, 2048, "2048", DatabaseOptions::kDefaultPageSize, "2048")); } TEST_P(SqlRecoveryTest, CannotRecoverClosedDb) { db_.Close(); - if (UseBuiltIn()) { - EXPECT_CHECK_DEATH(std::ignore = BuiltInRecovery::RecoverDatabase( - &db_, BuiltInRecovery::Strategy::kRecoverOrRaze)); - } else { - EXPECT_DCHECK_DEATH(Recovery::RecoverDatabase(&db_, db_path_)); - } + EXPECT_CHECK_DEATH(std::ignore = BuiltInRecovery::RecoverDatabase( + &db_, BuiltInRecovery::Strategy::kRecoverOrRaze)); } TEST_P(SqlRecoveryTest, CannotRecoverDbWithErrorCallback) { db_.set_error_callback(base::DoNothing()); - if (UseBuiltIn()) { - EXPECT_CHECK_DEATH(std::ignore = BuiltInRecovery::RecoverDatabase( - &db_, BuiltInRecovery::Strategy::kRecoverOrRaze)); - } else { - EXPECT_DCHECK_DEATH(Recovery::RecoverDatabase(&db_, db_path_)); - } + EXPECT_CHECK_DEATH(std::ignore = BuiltInRecovery::RecoverDatabase( + &db_, BuiltInRecovery::Strategy::kRecoverOrRaze)); } // TODO(https://crbug.com/1255316): Ideally this would be a // `SqlRecoveryTest`, but `Recovery::RecoverDatabase()` does not DCHECK // that it is passed a non-null database pointer and will instead likely result // in unexpected behavior or crashes. -TEST_F(SqlBuiltInRecoveryTest, CannotRecoverNullDb) { +TEST_P(SqlRecoveryTest, CannotRecoverNullDb) { EXPECT_CHECK_DEATH(std::ignore = BuiltInRecovery::RecoverDatabase( nullptr, BuiltInRecovery::Strategy::kRecoverOrRaze)); } @@ -1549,7 +888,7 @@ // `SqlRecoveryTest`, but `Recovery::RecoverDatabase()` does not DCHECK // whether the database is in-memory and will instead likely result in // unexpected behavior or crashes. -TEST_F(SqlBuiltInRecoveryTest, CannotRecoverInMemoryDb) { +TEST_P(SqlRecoveryTest, CannotRecoverInMemoryDb) { Database in_memory_db; ASSERT_TRUE(in_memory_db.OpenInMemory()); @@ -1558,19 +897,11 @@ &in_memory_db, BuiltInRecovery::Strategy::kRecoverOrRaze)); } -TEST_P(SqlRecoveryTest, BuiltInRecoveryNotAttempedIfNotEnabled) { - // `BuiltInRecovery` will return early if the kill switch is disabled. - EXPECT_EQ( - base::FeatureList::IsEnabled(features::kUseBuiltInRecoveryIfSupported), - IsSqliteSuccessCode(BuiltInRecovery::RecoverDatabase( - &db_, BuiltInRecovery::Strategy::kRecoverOrRaze))); -} - // This test mimics the case where a database that was using WAL mode crashed, // then next Chrome launch the database is not opened in WAL mode. This may // occur when e.g. WAL mode if configured via Finch and the user not in the // experiment group on the second launch of Chrome. -TEST_F(SqlBuiltInRecoveryTest, PRE_RecoverFormerlyWalDbAfterCrash) { +TEST_P(SqlRecoveryTest, PRE_RecoverFormerlyWalDbAfterCrash) { base::FilePath wal_db_path = temp_dir_.GetPath().AppendASCII("recovery_wal_test.sqlite"); @@ -1586,7 +917,7 @@ EXPECT_DCHECK_DEATH(wal_db.set_error_callback(base::DoNothing())); } -TEST_F(SqlBuiltInRecoveryTest, RecoverFormerlyWalDbAfterCrash) { +TEST_P(SqlRecoveryTest, RecoverFormerlyWalDbAfterCrash) { base::FilePath wal_db_path = temp_dir_.GetPath().AppendASCII("recovery_wal_test.sqlite"); @@ -1604,20 +935,6 @@ std::move(run_recovery)); } -INSTANTIATE_TEST_SUITE_P( - All, - SqlRecoveryTest, - testing::Values( - std::tuple(true, false), // BuiltInRecovery with non-WAL databases. - std::tuple(false, false) // Legacy recovery with non-WAL databases. -// Recovering WAL databases is not supported on Fuchsia. -#if !BUILDFLAG(IS_FUCHSIA) - , - std::tuple(true, true) // BuiltInRecovery with WAL databases. -#endif - // The legacy recovery module does not support recovering WAL databases. - )); - } // namespace } // namespace sql
diff --git a/sql/sql_features.cc b/sql/sql_features.cc index 3569f14..b6b98b30 100644 --- a/sql/sql_features.cc +++ b/sql/sql_features.cc
@@ -11,13 +11,4 @@ "EnableWALModeByDefault", base::FEATURE_DISABLED_BY_DEFAULT); -// When enabled, `sql::BuiltInRecovery` can be used if it's supported. See -// https://crbug.com/1385500. -// -// This is an overarching kill switch which overrides any database-specific -// flag. See `sql::BuiltInRecovery::RecoverIfPossible()` for more context. -BASE_FEATURE(kUseBuiltInRecoveryIfSupported, - "UseBuiltInRecoveryIfSupported", - base::FEATURE_ENABLED_BY_DEFAULT); - } // namespace sql::features
diff --git a/sql/sql_features.h b/sql/sql_features.h index f807d14..fefcbe6 100644 --- a/sql/sql_features.h +++ b/sql/sql_features.h
@@ -15,7 +15,6 @@ // Alphabetical: COMPONENT_EXPORT(SQL) BASE_DECLARE_FEATURE(kEnableWALModeByDefault); -COMPONENT_EXPORT(SQL) BASE_DECLARE_FEATURE(kUseBuiltInRecoveryIfSupported); } // namespace sql::features
diff --git a/sql/test/database_test_peer.cc b/sql/test/database_test_peer.cc index 5bc68363..5c5d1838 100644 --- a/sql/test/database_test_peer.cc +++ b/sql/test/database_test_peer.cc
@@ -26,9 +26,4 @@ return db->DetachDatabase(attachment_point, InternalApiToken()); } -// static -bool DatabaseTestPeer::EnableRecoveryExtension(Database* db) { - return Recovery::EnableRecoveryExtension(db, InternalApiToken()) == SQLITE_OK; -} - } // namespace sql
diff --git a/sql/test/database_test_peer.h b/sql/test/database_test_peer.h index d0a9a93..ff788f2 100644 --- a/sql/test/database_test_peer.h +++ b/sql/test/database_test_peer.h
@@ -19,8 +19,6 @@ const base::FilePath& other_db_path, const char* attachment_point); static bool DetachDatabase(Database* db, const char* attachment_point); - - static bool EnableRecoveryExtension(Database* db); }; } // namespace sql
diff --git a/storage/browser/quota/quota_database.cc b/storage/browser/quota/quota_database.cc index 4338a5e..1b510c5 100644 --- a/storage/browser/quota/quota_database.cc +++ b/storage/browser/quota/quota_database.cc
@@ -809,7 +809,7 @@ std::ignore = sql::BuiltInRecovery::RecoverIfPossible( db_.get(), error_code, - sql::BuiltInRecovery::Strategy::kRecoverWithMetaVersionOrRaze, nullptr); + sql::BuiltInRecovery::Strategy::kRecoverWithMetaVersionOrRaze); db_.reset(); EnsureOpened();
diff --git a/testing/buildbot/chromium.chromiumos.json b/testing/buildbot/chromium.chromiumos.json index 99c51edc..f7e6e8f8 100644 --- a/testing/buildbot/chromium.chromiumos.json +++ b/testing/buildbot/chromium.chromiumos.json
@@ -5282,9 +5282,9 @@ { "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_v124.0.6325.0/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v124.0.6326.0/test_ash_chrome" ], - "description": "Run with ash-chrome version 124.0.6325.0", + "description": "Run with ash-chrome version 124.0.6326.0", "isolate_profile_data": true, "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" @@ -5294,8 +5294,8 @@ "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v124.0.6325.0", - "revision": "version:124.0.6325.0" + "location": "lacros_version_skew_tests_v124.0.6326.0", + "revision": "version:124.0.6326.0" } ], "dimensions": { @@ -5438,9 +5438,9 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.filter;../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v124.0.6325.0/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v124.0.6326.0/test_ash_chrome" ], - "description": "Run with ash-chrome version 124.0.6325.0", + "description": "Run with ash-chrome version 124.0.6326.0", "isolate_profile_data": true, "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" @@ -5450,8 +5450,8 @@ "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v124.0.6325.0", - "revision": "version:124.0.6325.0" + "location": "lacros_version_skew_tests_v124.0.6326.0", + "revision": "version:124.0.6326.0" } ], "dimensions": {
diff --git a/testing/buildbot/chromium.coverage.json b/testing/buildbot/chromium.coverage.json index dd852f1..2b8bc25 100644 --- a/testing/buildbot/chromium.coverage.json +++ b/testing/buildbot/chromium.coverage.json
@@ -20313,9 +20313,9 @@ { "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_v124.0.6325.0/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v124.0.6326.0/test_ash_chrome" ], - "description": "Run with ash-chrome version 124.0.6325.0", + "description": "Run with ash-chrome version 124.0.6326.0", "isolate_profile_data": true, "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" @@ -20325,8 +20325,8 @@ "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v124.0.6325.0", - "revision": "version:124.0.6325.0" + "location": "lacros_version_skew_tests_v124.0.6326.0", + "revision": "version:124.0.6326.0" } ], "dimensions": { @@ -20463,9 +20463,9 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.filter;../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v124.0.6325.0/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v124.0.6326.0/test_ash_chrome" ], - "description": "Run with ash-chrome version 124.0.6325.0", + "description": "Run with ash-chrome version 124.0.6326.0", "isolate_profile_data": true, "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" @@ -20475,8 +20475,8 @@ "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v124.0.6325.0", - "revision": "version:124.0.6325.0" + "location": "lacros_version_skew_tests_v124.0.6326.0", + "revision": "version:124.0.6326.0" } ], "dimensions": {
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json index 24833f3..a1f8a8c 100644 --- a/testing/buildbot/chromium.fyi.json +++ b/testing/buildbot/chromium.fyi.json
@@ -7401,7 +7401,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -7449,7 +7449,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -7497,7 +7497,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -7545,7 +7545,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -7593,7 +7593,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -7641,7 +7641,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -7689,7 +7689,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -7737,7 +7737,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -7785,7 +7785,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -7833,7 +7833,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -7881,7 +7881,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -7929,7 +7929,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -7977,7 +7977,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -8025,7 +8025,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -8073,7 +8073,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -8121,7 +8121,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -8169,7 +8169,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -8217,7 +8217,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -8265,7 +8265,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -8313,7 +8313,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -8361,7 +8361,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -8409,7 +8409,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -8457,7 +8457,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -8505,7 +8505,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -8553,7 +8553,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -8601,7 +8601,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -8649,7 +8649,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -8697,7 +8697,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -8745,7 +8745,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -8793,7 +8793,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -8841,7 +8841,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -8889,7 +8889,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -8937,7 +8937,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -8985,7 +8985,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -9033,7 +9033,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -9081,7 +9081,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -9132,7 +9132,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -9183,7 +9183,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -9236,7 +9236,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -9290,7 +9290,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -9344,7 +9344,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -9398,7 +9398,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -9452,7 +9452,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -9506,7 +9506,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -9560,7 +9560,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -9614,7 +9614,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -9668,7 +9668,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -9722,7 +9722,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -9774,7 +9774,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -9826,7 +9826,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -9878,7 +9878,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -9929,7 +9929,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -9982,7 +9982,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -10036,7 +10036,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -10085,7 +10085,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -10133,7 +10133,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -10181,7 +10181,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -10229,7 +10229,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -10277,7 +10277,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -10325,7 +10325,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -10373,7 +10373,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -10421,7 +10421,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -10472,7 +10472,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -10524,7 +10524,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -10573,7 +10573,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -10621,7 +10621,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -10669,7 +10669,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -10718,7 +10718,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -10767,7 +10767,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -10815,7 +10815,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -10863,7 +10863,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -10911,7 +10911,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -10959,7 +10959,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -11007,7 +11007,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -11055,7 +11055,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -11103,7 +11103,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -11151,7 +11151,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -11199,7 +11199,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -11247,7 +11247,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -11295,7 +11295,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -11346,7 +11346,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -11397,7 +11397,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -11448,7 +11448,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -11499,7 +11499,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -11550,7 +11550,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -11601,7 +11601,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -11652,7 +11652,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -11703,7 +11703,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -11751,7 +11751,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -11799,7 +11799,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -11847,7 +11847,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -11895,7 +11895,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -11943,7 +11943,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -11991,7 +11991,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -12039,7 +12039,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -12087,7 +12087,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -12135,7 +12135,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -12183,7 +12183,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -12231,7 +12231,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -12279,7 +12279,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -12327,7 +12327,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -12375,7 +12375,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -12423,7 +12423,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -12471,7 +12471,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -12519,7 +12519,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -12567,7 +12567,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -12615,7 +12615,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -12663,7 +12663,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -12711,7 +12711,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -12759,7 +12759,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -12807,7 +12807,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -12855,7 +12855,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -12903,7 +12903,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -12951,7 +12951,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -12999,7 +12999,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -13047,7 +13047,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -13095,7 +13095,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -13143,7 +13143,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -13191,7 +13191,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -13239,7 +13239,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -13287,7 +13287,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -13335,7 +13335,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -13383,7 +13383,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -13431,7 +13431,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -13479,7 +13479,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -13527,7 +13527,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -13575,7 +13575,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -13623,7 +13623,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -13671,7 +13671,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -13719,7 +13719,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -13767,7 +13767,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -13815,7 +13815,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -13863,7 +13863,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -13911,7 +13911,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -13959,7 +13959,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -14007,7 +14007,7 @@ ], "dimensions": { "cpu": "arm64", - "os": "Mac-13" + "os": "Mac-14" }, "named_caches": [ { @@ -40600,6 +40600,1590 @@ } ] }, + "linux-chromeos-dbg-oslogin": { + "gtest_tests": [ + { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "name": "absl_hardening_tests", + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04", + "pool": "chromium.tests.oslogin" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "absl_hardening_tests", + "test_id_prefix": "ninja://third_party/abseil-cpp:absl_hardening_tests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "name": "accessibility_unittests", + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04", + "pool": "chromium.tests.oslogin" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "accessibility_unittests", + "test_id_prefix": "ninja://ui/accessibility:accessibility_unittests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "angle_unittests", + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04", + "pool": "chromium.tests.oslogin" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "angle_unittests", + "test_id_prefix": "ninja://third_party/angle/src/tests:angle_unittests/", + "use_isolated_scripts_api": true + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "name": "app_shell_unittests", + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04", + "pool": "chromium.tests.oslogin" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "app_shell_unittests", + "test_id_prefix": "ninja://extensions/shell:app_shell_unittests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "name": "ash_components_unittests", + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04", + "pool": "chromium.tests.oslogin" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "ash_components_unittests", + "test_id_prefix": "ninja://ash/components:ash_components_unittests/" + }, + { + "ci_only": true, + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "name": "ash_crosapi_tests", + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04", + "pool": "chromium.tests.oslogin" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "ash_crosapi_tests", + "test_id_prefix": "ninja://chrome/test:ash_crosapi_tests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "name": "ash_unittests", + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04", + "pool": "chromium.tests.oslogin" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", + "shards": 5 + }, + "test": "ash_unittests", + "test_id_prefix": "ninja://ash:ash_unittests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "name": "ash_webui_unittests", + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04", + "pool": "chromium.tests.oslogin" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "ash_webui_unittests", + "test_id_prefix": "ninja://ash/webui:ash_webui_unittests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "name": "aura_unittests", + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04", + "pool": "chromium.tests.oslogin" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "aura_unittests", + "test_id_prefix": "ninja://ui/aura:aura_unittests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "name": "base_unittests", + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04", + "pool": "chromium.tests.oslogin" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "base_unittests", + "test_id_prefix": "ninja://base:base_unittests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "name": "blink_common_unittests", + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04", + "pool": "chromium.tests.oslogin" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "blink_common_unittests", + "test_id_prefix": "ninja://third_party/blink/common:blink_common_unittests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "name": "blink_fuzzer_unittests", + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04", + "pool": "chromium.tests.oslogin" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "blink_fuzzer_unittests", + "test_id_prefix": "ninja://third_party/blink/renderer/platform:blink_fuzzer_unittests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "name": "blink_heap_unittests", + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04", + "pool": "chromium.tests.oslogin" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "blink_heap_unittests", + "test_id_prefix": "ninja://third_party/blink/renderer/platform/heap:blink_heap_unittests/" + }, + { + "args": [ + "--git-revision=${got_revision}" + ], + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "name": "blink_platform_unittests", + "precommit_args": [ + "--gerrit-issue=${patch_issue}", + "--gerrit-patchset=${patch_set}", + "--buildbucket-id=${buildbucket_build_id}" + ], + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04", + "pool": "chromium.tests.oslogin" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "blink_platform_unittests", + "test_id_prefix": "ninja://third_party/blink/renderer/platform:blink_platform_unittests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "name": "boringssl_crypto_tests", + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04", + "pool": "chromium.tests.oslogin" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "boringssl_crypto_tests", + "test_id_prefix": "ninja://third_party/boringssl:boringssl_crypto_tests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "name": "boringssl_ssl_tests", + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04", + "pool": "chromium.tests.oslogin" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "boringssl_ssl_tests", + "test_id_prefix": "ninja://third_party/boringssl:boringssl_ssl_tests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "name": "browser_tests", + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04", + "pool": "chromium.tests.oslogin" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", + "shards": 10 + }, + "test": "browser_tests", + "test_id_prefix": "ninja://chrome/test:browser_tests/" + }, + { + "args": [ + "--gtest_filter=-*UsingRealWebcam*" + ], + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "name": "capture_unittests", + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04", + "pool": "chromium.tests.oslogin" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "capture_unittests", + "test_id_prefix": "ninja://media/capture:capture_unittests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "name": "cast_unittests", + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04", + "pool": "chromium.tests.oslogin" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "cast_unittests", + "test_id_prefix": "ninja://media/cast:cast_unittests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "name": "cc_unittests", + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04", + "pool": "chromium.tests.oslogin" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "cc_unittests", + "test_id_prefix": "ninja://cc:cc_unittests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "name": "chrome_app_unittests", + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04", + "pool": "chromium.tests.oslogin" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "chrome_app_unittests", + "test_id_prefix": "ninja://chrome/test:chrome_app_unittests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "name": "chromedriver_unittests", + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04", + "pool": "chromium.tests.oslogin" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "chromedriver_unittests", + "test_id_prefix": "ninja://chrome/test/chromedriver:chromedriver_unittests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "name": "chromeos_components_unittests", + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04", + "pool": "chromium.tests.oslogin" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "chromeos_components_unittests", + "test_id_prefix": "ninja://chromeos/components:chromeos_components_unittests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "name": "chromeos_unittests", + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04", + "pool": "chromium.tests.oslogin" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "chromeos_unittests", + "test_id_prefix": "ninja://chromeos:chromeos_unittests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "name": "components_browsertests", + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04", + "pool": "chromium.tests.oslogin" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "components_browsertests", + "test_id_prefix": "ninja://components:components_browsertests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "name": "components_unittests", + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04", + "pool": "chromium.tests.oslogin" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "components_unittests", + "test_id_prefix": "ninja://components:components_unittests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "name": "compositor_unittests", + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04", + "pool": "chromium.tests.oslogin" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "compositor_unittests", + "test_id_prefix": "ninja://ui/compositor:compositor_unittests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "name": "content_browsertests", + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04", + "pool": "chromium.tests.oslogin" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", + "shards": 8 + }, + "test": "content_browsertests", + "test_id_prefix": "ninja://content/test:content_browsertests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "name": "content_unittests", + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04", + "pool": "chromium.tests.oslogin" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "content_unittests", + "test_id_prefix": "ninja://content/test:content_unittests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "name": "crashpad_tests", + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04", + "pool": "chromium.tests.oslogin" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "crashpad_tests", + "test_id_prefix": "ninja://third_party/crashpad/crashpad:crashpad_tests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "name": "crypto_unittests", + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04", + "pool": "chromium.tests.oslogin" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "crypto_unittests", + "test_id_prefix": "ninja://crypto:crypto_unittests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "name": "dbus_unittests", + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04", + "pool": "chromium.tests.oslogin" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "dbus_unittests", + "test_id_prefix": "ninja://dbus:dbus_unittests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "name": "device_unittests", + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04", + "pool": "chromium.tests.oslogin" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "device_unittests", + "test_id_prefix": "ninja://device:device_unittests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "name": "display_unittests", + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04", + "pool": "chromium.tests.oslogin" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "display_unittests", + "test_id_prefix": "ninja://ui/display:display_unittests/" + }, + { + "ci_only": true, + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "name": "env_chromium_unittests", + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04", + "pool": "chromium.tests.oslogin" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "env_chromium_unittests", + "test_id_prefix": "ninja://third_party/leveldatabase:env_chromium_unittests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "name": "events_unittests", + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04", + "pool": "chromium.tests.oslogin" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "events_unittests", + "test_id_prefix": "ninja://ui/events:events_unittests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "name": "exo_unittests", + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04", + "pool": "chromium.tests.oslogin" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "exo_unittests", + "test_id_prefix": "ninja://components/exo:exo_unittests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "name": "extensions_browsertests", + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04", + "pool": "chromium.tests.oslogin" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "extensions_browsertests", + "test_id_prefix": "ninja://extensions:extensions_browsertests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "name": "extensions_unittests", + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04", + "pool": "chromium.tests.oslogin" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "extensions_unittests", + "test_id_prefix": "ninja://extensions:extensions_unittests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "name": "filesystem_service_unittests", + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04", + "pool": "chromium.tests.oslogin" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "filesystem_service_unittests", + "test_id_prefix": "ninja://components/services/filesystem:filesystem_service_unittests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "name": "gcm_unit_tests", + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04", + "pool": "chromium.tests.oslogin" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "gcm_unit_tests", + "test_id_prefix": "ninja://google_apis/gcm:gcm_unit_tests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "name": "gfx_unittests", + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04", + "pool": "chromium.tests.oslogin" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "gfx_unittests", + "test_id_prefix": "ninja://ui/gfx:gfx_unittests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "name": "gin_unittests", + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04", + "pool": "chromium.tests.oslogin" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "gin_unittests", + "test_id_prefix": "ninja://gin:gin_unittests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "name": "gl_unittests_ozone", + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04", + "pool": "chromium.tests.oslogin" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "gl_unittests_ozone", + "test_id_prefix": "ninja://ui/gl:gl_unittests_ozone/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "name": "google_apis_unittests", + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04", + "pool": "chromium.tests.oslogin" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "google_apis_unittests", + "test_id_prefix": "ninja://google_apis:google_apis_unittests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "name": "gpu_unittests", + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04", + "pool": "chromium.tests.oslogin" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "gpu_unittests", + "test_id_prefix": "ninja://gpu:gpu_unittests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "name": "gwp_asan_unittests", + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04", + "pool": "chromium.tests.oslogin" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "gwp_asan_unittests", + "test_id_prefix": "ninja://components/gwp_asan:gwp_asan_unittests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "name": "interactive_ui_tests", + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04", + "pool": "chromium.tests.oslogin" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", + "shards": 3 + }, + "test": "interactive_ui_tests", + "test_id_prefix": "ninja://chrome/test:interactive_ui_tests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "name": "ipc_tests", + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04", + "pool": "chromium.tests.oslogin" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "ipc_tests", + "test_id_prefix": "ninja://ipc:ipc_tests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "name": "keyboard_unittests", + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04", + "pool": "chromium.tests.oslogin" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "keyboard_unittests", + "test_id_prefix": "ninja://ash/keyboard/ui:keyboard_unittests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "name": "latency_unittests", + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04", + "pool": "chromium.tests.oslogin" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "latency_unittests", + "test_id_prefix": "ninja://ui/latency:latency_unittests/" + }, + { + "ci_only": true, + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "name": "leveldb_unittests", + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04", + "pool": "chromium.tests.oslogin" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "leveldb_unittests", + "test_id_prefix": "ninja://third_party/leveldatabase:leveldb_unittests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "name": "libjingle_xmpp_unittests", + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04", + "pool": "chromium.tests.oslogin" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "libjingle_xmpp_unittests", + "test_id_prefix": "ninja://third_party/libjingle_xmpp:libjingle_xmpp_unittests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "name": "liburlpattern_unittests", + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04", + "pool": "chromium.tests.oslogin" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "liburlpattern_unittests", + "test_id_prefix": "ninja://third_party/liburlpattern:liburlpattern_unittests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "name": "media_unittests", + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04", + "pool": "chromium.tests.oslogin" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "media_unittests", + "test_id_prefix": "ninja://media:media_unittests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "name": "message_center_unittests", + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04", + "pool": "chromium.tests.oslogin" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "message_center_unittests", + "test_id_prefix": "ninja://ui/message_center:message_center_unittests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "name": "midi_unittests", + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04", + "pool": "chromium.tests.oslogin" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "midi_unittests", + "test_id_prefix": "ninja://media/midi:midi_unittests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "name": "mojo_unittests", + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04", + "pool": "chromium.tests.oslogin" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "mojo_unittests", + "test_id_prefix": "ninja://mojo:mojo_unittests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "name": "nacl_loader_unittests", + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04", + "pool": "chromium.tests.oslogin" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "nacl_loader_unittests", + "test_id_prefix": "ninja://components/nacl/loader:nacl_loader_unittests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "name": "native_theme_unittests", + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04", + "pool": "chromium.tests.oslogin" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "native_theme_unittests", + "test_id_prefix": "ninja://ui/native_theme:native_theme_unittests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "name": "net_unittests", + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04", + "pool": "chromium.tests.oslogin" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "net_unittests", + "test_id_prefix": "ninja://net:net_unittests/" + }, + { + "args": [ + "--ozone-platform=headless" + ], + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "name": "ozone_gl_unittests", + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04", + "pool": "chromium.tests.oslogin" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "ozone_gl_unittests", + "test_id_prefix": "ninja://ui/ozone/gl:ozone_gl_unittests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "name": "ozone_unittests", + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04", + "pool": "chromium.tests.oslogin" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "ozone_unittests", + "test_id_prefix": "ninja://ui/ozone:ozone_unittests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "name": "ozone_x11_unittests", + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04", + "pool": "chromium.tests.oslogin" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "ozone_x11_unittests", + "test_id_prefix": "ninja://ui/ozone:ozone_x11_unittests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "name": "pdf_unittests", + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04", + "pool": "chromium.tests.oslogin" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "pdf_unittests", + "test_id_prefix": "ninja://pdf:pdf_unittests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "name": "perfetto_unittests", + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04", + "pool": "chromium.tests.oslogin" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "perfetto_unittests", + "test_id_prefix": "ninja://third_party/perfetto:perfetto_unittests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "name": "ppapi_unittests", + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04", + "pool": "chromium.tests.oslogin" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "ppapi_unittests", + "test_id_prefix": "ninja://ppapi:ppapi_unittests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "name": "printing_unittests", + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04", + "pool": "chromium.tests.oslogin" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "printing_unittests", + "test_id_prefix": "ninja://printing:printing_unittests/" + }, + { + "ci_only": true, + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "name": "pthreadpool_unittests", + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04", + "pool": "chromium.tests.oslogin" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "pthreadpool_unittests", + "test_id_prefix": "ninja://third_party/pthreadpool:pthreadpool_unittests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "name": "remoting_unittests", + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04", + "pool": "chromium.tests.oslogin" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "remoting_unittests", + "test_id_prefix": "ninja://remoting:remoting_unittests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "name": "sandbox_linux_unittests", + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04", + "pool": "chromium.tests.oslogin" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "sandbox_linux_unittests", + "test_id_prefix": "ninja://sandbox/linux:sandbox_linux_unittests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "name": "services_unittests", + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04", + "pool": "chromium.tests.oslogin" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "services_unittests", + "test_id_prefix": "ninja://services:services_unittests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "name": "shell_dialogs_unittests", + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04", + "pool": "chromium.tests.oslogin" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "shell_dialogs_unittests", + "test_id_prefix": "ninja://ui/shell_dialogs:shell_dialogs_unittests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "name": "shell_encryption_unittests", + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04", + "pool": "chromium.tests.oslogin" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "shell_encryption_unittests", + "test_id_prefix": "ninja://third_party/shell-encryption:shell_encryption_unittests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "name": "skia_unittests", + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04", + "pool": "chromium.tests.oslogin" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "skia_unittests", + "test_id_prefix": "ninja://skia:skia_unittests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "name": "snapshot_unittests", + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04", + "pool": "chromium.tests.oslogin" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "snapshot_unittests", + "test_id_prefix": "ninja://ui/snapshot:snapshot_unittests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "name": "sql_unittests", + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04", + "pool": "chromium.tests.oslogin" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "sql_unittests", + "test_id_prefix": "ninja://sql:sql_unittests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "name": "storage_unittests", + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04", + "pool": "chromium.tests.oslogin" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "storage_unittests", + "test_id_prefix": "ninja://storage:storage_unittests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "name": "sync_integration_tests", + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04", + "pool": "chromium.tests.oslogin" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", + "shards": 3 + }, + "test": "sync_integration_tests", + "test_id_prefix": "ninja://chrome/test:sync_integration_tests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "name": "ui_base_unittests", + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04", + "pool": "chromium.tests.oslogin" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "ui_base_unittests", + "test_id_prefix": "ninja://ui/base:ui_base_unittests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "name": "ui_chromeos_unittests", + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04", + "pool": "chromium.tests.oslogin" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "ui_chromeos_unittests", + "test_id_prefix": "ninja://ui/chromeos:ui_chromeos_unittests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "name": "ui_touch_selection_unittests", + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04", + "pool": "chromium.tests.oslogin" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "ui_touch_selection_unittests", + "test_id_prefix": "ninja://ui/touch_selection:ui_touch_selection_unittests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "name": "ui_unittests", + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04", + "pool": "chromium.tests.oslogin" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "ui_unittests", + "test_id_prefix": "ninja://ui/tests:ui_unittests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "name": "unit_tests", + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04", + "pool": "chromium.tests.oslogin" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "unit_tests", + "test_id_prefix": "ninja://chrome/test:unit_tests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "name": "url_unittests", + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04", + "pool": "chromium.tests.oslogin" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "url_unittests", + "test_id_prefix": "ninja://url:url_unittests/" + }, + { + "experiment_percentage": 100, + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "name": "usage_time_limit_unittests", + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04", + "pool": "chromium.tests.oslogin" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "usage_time_limit_unittests", + "test_id_prefix": "ninja://chrome/test:usage_time_limit_unittests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "name": "views_unittests", + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04", + "pool": "chromium.tests.oslogin" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "views_unittests", + "test_id_prefix": "ninja://ui/views:views_unittests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "name": "viz_unittests", + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04", + "pool": "chromium.tests.oslogin" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "viz_unittests", + "test_id_prefix": "ninja://components/viz:viz_unittests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "name": "wayland_client_perftests", + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04", + "pool": "chromium.tests.oslogin" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "wayland_client_perftests", + "test_id_prefix": "ninja://components/exo/wayland:wayland_client_perftests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "name": "wayland_client_tests", + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04", + "pool": "chromium.tests.oslogin" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "wayland_client_tests", + "test_id_prefix": "ninja://components/exo/wayland:wayland_client_tests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "name": "webkit_unit_tests", + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04", + "pool": "chromium.tests.oslogin" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "blink_unittests", + "test_id_prefix": "ninja://third_party/blink/renderer/controller:blink_unittests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "name": "wm_unittests", + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04", + "pool": "chromium.tests.oslogin" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "wm_unittests", + "test_id_prefix": "ninja://ui/wm:wm_unittests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "name": "wtf_unittests", + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04", + "pool": "chromium.tests.oslogin" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "wtf_unittests", + "test_id_prefix": "ninja://third_party/blink/renderer/platform/wtf:wtf_unittests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "name": "zlib_unittests", + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04", + "pool": "chromium.tests.oslogin" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "zlib_unittests", + "test_id_prefix": "ninja://third_party/zlib:zlib_unittests/" + } + ], + "isolated_scripts": [ + { + "merge": { + "script": "//tools/perf/process_perf_results.py" + }, + "name": "chrome_sizes", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04", + "pool": "chromium.tests.oslogin" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "chrome_sizes", + "test_id_prefix": "ninja://chrome/test:chrome_sizes/" + }, + { + "args": [ + "--num-retries=3", + "--test-list=../../third_party/blink/web_tests/TestLists/ppapi", + "--write-run-histories-to=${ISOLATED_OUTDIR}/run_histories.json" + ], + "merge": { + "args": [ + "--verbose" + ], + "script": "//third_party/blink/tools/merge_web_test_results.py" + }, + "name": "ppapi_blink_web_tests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "results_handler": "layout tests", + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04", + "pool": "chromium.tests.oslogin" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "blink_web_tests", + "test_id_prefix": "ninja://:blink_web_tests/" + } + ] + }, "linux-fieldtrial-rel": { "gtest_tests": [ { @@ -41369,9 +42953,9 @@ { "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_v124.0.6325.0/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v124.0.6326.0/test_ash_chrome" ], - "description": "Run with ash-chrome version 124.0.6325.0", + "description": "Run with ash-chrome version 124.0.6326.0", "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, @@ -41380,8 +42964,8 @@ "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v124.0.6325.0", - "revision": "version:124.0.6325.0" + "location": "lacros_version_skew_tests_v124.0.6326.0", + "revision": "version:124.0.6326.0" } ], "dimensions": { @@ -41519,9 +43103,9 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.filter;../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v124.0.6325.0/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v124.0.6326.0/test_ash_chrome" ], - "description": "Run with ash-chrome version 124.0.6325.0", + "description": "Run with ash-chrome version 124.0.6326.0", "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, @@ -41530,8 +43114,8 @@ "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v124.0.6325.0", - "revision": "version:124.0.6325.0" + "location": "lacros_version_skew_tests_v124.0.6326.0", + "revision": "version:124.0.6326.0" } ], "dimensions": { @@ -42868,9 +44452,9 @@ { "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_v124.0.6325.0/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v124.0.6326.0/test_ash_chrome" ], - "description": "Run with ash-chrome version 124.0.6325.0", + "description": "Run with ash-chrome version 124.0.6326.0", "isolate_profile_data": true, "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" @@ -42880,8 +44464,8 @@ "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v124.0.6325.0", - "revision": "version:124.0.6325.0" + "location": "lacros_version_skew_tests_v124.0.6326.0", + "revision": "version:124.0.6326.0" } ], "dimensions": { @@ -43024,9 +44608,9 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.filter;../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v124.0.6325.0/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v124.0.6326.0/test_ash_chrome" ], - "description": "Run with ash-chrome version 124.0.6325.0", + "description": "Run with ash-chrome version 124.0.6326.0", "isolate_profile_data": true, "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" @@ -43036,8 +44620,8 @@ "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v124.0.6325.0", - "revision": "version:124.0.6325.0" + "location": "lacros_version_skew_tests_v124.0.6326.0", + "revision": "version:124.0.6326.0" } ], "dimensions": { @@ -44349,9 +45933,9 @@ { "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_v124.0.6325.0/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v124.0.6326.0/test_ash_chrome" ], - "description": "Run with ash-chrome version 124.0.6325.0", + "description": "Run with ash-chrome version 124.0.6326.0", "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, @@ -44360,8 +45944,8 @@ "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v124.0.6325.0", - "revision": "version:124.0.6325.0" + "location": "lacros_version_skew_tests_v124.0.6326.0", + "revision": "version:124.0.6326.0" } ], "dimensions": { @@ -44499,9 +46083,9 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.filter;../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v124.0.6325.0/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v124.0.6326.0/test_ash_chrome" ], - "description": "Run with ash-chrome version 124.0.6325.0", + "description": "Run with ash-chrome version 124.0.6326.0", "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, @@ -44510,8 +46094,8 @@ "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v124.0.6325.0", - "revision": "version:124.0.6325.0" + "location": "lacros_version_skew_tests_v124.0.6326.0", + "revision": "version:124.0.6326.0" } ], "dimensions": {
diff --git a/testing/buildbot/chromium.memory.json b/testing/buildbot/chromium.memory.json index b0393a3..2a7b34a3 100644 --- a/testing/buildbot/chromium.memory.json +++ b/testing/buildbot/chromium.memory.json
@@ -16343,12 +16343,12 @@ { "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_v124.0.6325.0/test_ash_chrome", + "--ash-chrome-path-override=../../lacros_version_skew_tests_v124.0.6326.0/test_ash_chrome", "--test-launcher-print-test-stdio=always", "--combine-ash-logs-on-bots", "--asan-symbolize-output" ], - "description": "Run with ash-chrome version 124.0.6325.0", + "description": "Run with ash-chrome version 124.0.6326.0", "isolate_profile_data": true, "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" @@ -16358,8 +16358,8 @@ "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v124.0.6325.0", - "revision": "version:124.0.6325.0" + "location": "lacros_version_skew_tests_v124.0.6326.0", + "revision": "version:124.0.6326.0" } ], "dimensions": { @@ -16519,12 +16519,12 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.filter;../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v124.0.6325.0/test_ash_chrome", + "--ash-chrome-path-override=../../lacros_version_skew_tests_v124.0.6326.0/test_ash_chrome", "--test-launcher-print-test-stdio=always", "--combine-ash-logs-on-bots", "--asan-symbolize-output" ], - "description": "Run with ash-chrome version 124.0.6325.0", + "description": "Run with ash-chrome version 124.0.6326.0", "isolate_profile_data": true, "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" @@ -16534,8 +16534,8 @@ "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v124.0.6325.0", - "revision": "version:124.0.6325.0" + "location": "lacros_version_skew_tests_v124.0.6326.0", + "revision": "version:124.0.6326.0" } ], "dimensions": { @@ -19881,7 +19881,8 @@ "dimensions": { "os": "Windows-10-19045" }, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", + "shards": 6 }, "test": "blink_unittests", "test_id_prefix": "ninja://third_party/blink/renderer/controller:blink_unittests/"
diff --git a/testing/buildbot/mixins.pyl b/testing/buildbot/mixins.pyl index 1f5c782..7f8052e4 100644 --- a/testing/buildbot/mixins.pyl +++ b/testing/buildbot/mixins.pyl
@@ -399,6 +399,13 @@ 'service_account': 'chromium-tester@chops-service-accounts.iam.gserviceaccount.com', }, }, + 'chromium-tests-oslogin': { + 'swarming': { + 'dimensions': { + 'pool': 'chromium.tests.oslogin', + }, + }, + }, 'ci_only': { 'ci_only': True, },
diff --git a/testing/buildbot/test_suite_exceptions.pyl b/testing/buildbot/test_suite_exceptions.pyl index c72b969..81df0ba9 100644 --- a/testing/buildbot/test_suite_exceptions.pyl +++ b/testing/buildbot/test_suite_exceptions.pyl
@@ -5293,6 +5293,11 @@ 'shards': 2, } }, + 'win-asan': { + 'swarming': { + 'shards': 6, + }, + }, 'win10-code-coverage': { 'swarming': { 'shards': 4,
diff --git a/testing/buildbot/variants.pyl b/testing/buildbot/variants.pyl index 7ac9198..99bda77 100644 --- a/testing/buildbot/variants.pyl +++ b/testing/buildbot/variants.pyl
@@ -307,16 +307,16 @@ }, 'LACROS_VERSION_SKEW_CANARY': { 'identifier': 'Lacros version skew testing ash canary', - 'description': 'Run with ash-chrome version 124.0.6325.0', + 'description': 'Run with ash-chrome version 124.0.6326.0', 'args': [ - '--ash-chrome-path-override=../../lacros_version_skew_tests_v124.0.6325.0/test_ash_chrome', + '--ash-chrome-path-override=../../lacros_version_skew_tests_v124.0.6326.0/test_ash_chrome', ], 'swarming': { 'cipd_packages': [ { 'cipd_package': 'chromium/testing/linux-ash-chromium/x86_64/ash.zip', - 'location': 'lacros_version_skew_tests_v124.0.6325.0', - 'revision': 'version:124.0.6325.0', + 'location': 'lacros_version_skew_tests_v124.0.6326.0', + 'revision': 'version:124.0.6326.0', }, ], },
diff --git a/testing/buildbot/waterfalls.pyl b/testing/buildbot/waterfalls.pyl index d34488b..89d9674 100644 --- a/testing/buildbot/waterfalls.pyl +++ b/testing/buildbot/waterfalls.pyl
@@ -3411,7 +3411,7 @@ ], 'mixins': [ 'has_native_resultdb_integration', - 'mac_13_arm64', + 'mac_14_arm64', 'mac_toolchain', 'out_dir_arg', 'xcode_15_main', @@ -3572,6 +3572,17 @@ 'scripts': 'test_traffic_annotation_auditor_script' }, }, + 'linux-chromeos-dbg-oslogin': { + 'mixins': [ + 'x86-64', + 'linux-jammy', + 'chromium-tests-oslogin', + ], + 'test_suites': { + 'gtest_tests': 'linux_chromeos_gtests', + 'isolated_scripts': 'linux_chromeos_isolated_scripts', + }, + }, 'linux-fieldtrial-rel': { 'mixins': [ 'linux-jammy',
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json index 7d187e2..553dd63 100644 --- a/testing/variations/fieldtrial_testing_config.json +++ b/testing/variations/fieldtrial_testing_config.json
@@ -8846,6 +8846,21 @@ ] } ], + "IOSDefaultBrowserVideoInSettings": [ + { + "platforms": [ + "ios" + ], + "experiments": [ + { + "name": "Enabled", + "enable_features": [ + "DefaultBrowserVideoInSettings" + ] + } + ] + } + ], "IOSDiscoverFeedSportCard": [ { "platforms": [ @@ -9515,29 +9530,6 @@ ] } ], - "ImageServiceObserveSyncDownloadStatus": [ - { - "platforms": [ - "android", - "chromeos", - "chromeos_lacros", - "linux", - "mac", - "windows" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "ImageServiceObserveSyncDownloadStatus" - ], - "disable_features": [ - "SyncPollImmediatelyOnEveryStartup2" - ] - } - ] - } - ], "ImprovedDownloadPageWarnings": [ { "platforms": [ @@ -11771,6 +11763,30 @@ ] } ], + "OmniboxLogURLScoringSignals": [ + { + "platforms": [ + "android", + "chromeos", + "chromeos_lacros", + "ios", + "linux", + "mac", + "windows" + ], + "experiments": [ + { + "name": "Enabled", + "params": { + "enable_scoring_signals_annotators": "true" + }, + "enable_features": [ + "LogUrlScoringSignals" + ] + } + ] + } + ], "OmniboxMatchToolbarAndStatusBarColor": [ { "platforms": [ @@ -14212,26 +14228,6 @@ ] } ], - "ProtectedAudiencesEnableUpdatingUserBiddingSignals": [ - { - "platforms": [ - "android", - "chromeos", - "chromeos_lacros", - "linux", - "mac", - "windows" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "EnableUpdatingUserBiddingSignals" - ] - } - ] - } - ], "ProtectedAudiencesHeaderDirectFromSellerSignalsStudy": [ { "platforms": [ @@ -14315,6 +14311,26 @@ ] } ], + "ProtectedAudiencesSplitTrustedSignalsFetchingURL": [ + { + "platforms": [ + "android", + "chromeos", + "chromeos_lacros", + "linux", + "mac", + "windows" + ], + "experiments": [ + { + "name": "Enabled", + "enable_features": [ + "FledgeSplitTrustedSignalsFetchingURL" + ] + } + ] + } + ], "ProtectedAudiencesTrustedBiddingSignalsSlotSize": [ { "platforms": [ @@ -15270,6 +15286,14 @@ "SafeBrowsingNewGmsApiForBrowseUrlDatabaseCheck", "SafeBrowsingNewGmsApiForSubresourceFilterCheck" ] + }, + { + "name": "EnabledWithCallOnStartup", + "enable_features": [ + "SafeBrowsingCallNewGmsApiOnStartup", + "SafeBrowsingNewGmsApiForBrowseUrlDatabaseCheck", + "SafeBrowsingNewGmsApiForSubresourceFilterCheck" + ] } ] }
diff --git a/third_party/blink/common/media/watch_time_reporter.cc b/third_party/blink/common/media/watch_time_reporter.cc index 7323ee5..1a2b40ca 100644 --- a/third_party/blink/common/media/watch_time_reporter.cc +++ b/third_party/blink/common/media/watch_time_reporter.cc
@@ -5,8 +5,8 @@ #include "third_party/blink/public/common/media/watch_time_reporter.h" #include <numeric> +#include <vector> -#include "base/containers/cxx20_erase.h" #include "base/functional/bind.h" #include "base/power_monitor/power_monitor.h" #include "base/task/sequenced_task_runner.h" @@ -494,7 +494,7 @@ } } - base::EraseIf(pending_underflow_events_, [](const UnderflowEvent& ufe) { + std::erase_if(pending_underflow_events_, [](const UnderflowEvent& ufe) { return ufe.reported && ufe.duration != media::kNoTimestamp; });
diff --git a/third_party/blink/renderer/bindings/core/v8/callback_invoke_helper.cc b/third_party/blink/renderer/bindings/core/v8/callback_invoke_helper.cc index ddb7f4d..d0cf9a4 100644 --- a/third_party/blink/renderer/bindings/core/v8/callback_invoke_helper.cc +++ b/third_party/blink/renderer/bindings/core/v8/callback_invoke_helper.cc
@@ -13,7 +13,7 @@ #include "third_party/blink/renderer/platform/bindings/callback_interface_base.h" #include "third_party/blink/renderer/platform/bindings/script_forbidden_scope.h" #include "third_party/blink/renderer/platform/runtime_enabled_features.h" -#include "third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h" +#include "third_party/blink/renderer/platform/scheduler/public/task_attribution_tracker.h" namespace blink { @@ -105,8 +105,7 @@ callback_this_ = callback_this.V8Value(callback_->CallbackRelevantScriptState()); } - if (auto* tracker = - ThreadScheduler::Current()->GetTaskAttributionTracker()) { + if (auto* tracker = scheduler::TaskAttributionTracker::From(isolate)) { // There are 3 possible callbacks here: // a) Callbacks which track their registering task as their parent // b) Callbacks which don't do the above, split into two groups: @@ -119,7 +118,7 @@ CallbackFunctionWithTaskAttributionBase>::value) { parent_task = callback_->GetParentTask(); } - if (parent_task || !tracker->RunningTask(isolate)) { + if (parent_task || !tracker->RunningTask()) { task_attribution_scope_ = tracker->CreateTaskScope( callback_->CallbackRelevantScriptState(), parent_task, scheduler::TaskAttributionTracker::TaskScopeType::kCallback);
diff --git a/third_party/blink/renderer/core/css/css_length_resolver.h b/third_party/blink/renderer/core/css/css_length_resolver.h index 3611e56..0c397f3 100644 --- a/third_party/blink/renderer/core/css/css_length_resolver.h +++ b/third_party/blink/renderer/core/css/css_length_resolver.h
@@ -56,6 +56,11 @@ // https://drafts.csswg.org/css-scoping-1/#css-tree-scoped-reference virtual void ReferenceTreeScope() const = 0; + // Called when anchor() or anchor-size() functions are evaluated. + // + // https://drafts.csswg.org/css-anchor-position-1/ + virtual void ReferenceAnchor() const = 0; + // The AnchorEvaluator used to evaluate anchor()/anchor-size() queries, // when the runtime flag CSSAnchorPositioningComputeAnchor is enabled. virtual Length::AnchorEvaluator* AnchorEvaluator() const { return nullptr; }
diff --git a/third_party/blink/renderer/core/css/css_math_expression_node.cc b/third_party/blink/renderer/core/css/css_math_expression_node.cc index 179b54b..09c7a0c2 100644 --- a/third_party/blink/renderer/core/css/css_math_expression_node.cc +++ b/third_party/blink/renderer/core/css/css_math_expression_node.cc
@@ -2504,6 +2504,7 @@ std::optional<LayoutUnit> CSSMathExpressionAnchorQuery::EvaluateQuery( const CalculationExpressionNode& query, const CSSLengthResolver& length_resolver) const { + length_resolver.ReferenceAnchor(); if (Length::AnchorEvaluator* anchor_evaluator = length_resolver.AnchorEvaluator()) { return anchor_evaluator->Evaluate(query);
diff --git a/third_party/blink/renderer/core/css/css_to_length_conversion_data.cc b/third_party/blink/renderer/core/css/css_to_length_conversion_data.cc index 6e5e9f7..1a92209 100644 --- a/third_party/blink/renderer/core/css/css_to_length_conversion_data.cc +++ b/third_party/blink/renderer/core/css/css_to_length_conversion_data.cc
@@ -464,4 +464,8 @@ SetFlag(Flag::kTreeScopedReference); } +void CSSToLengthConversionData::ReferenceAnchor() const { + SetFlag(Flag::kAnchorRelative); +} + } // namespace blink
diff --git a/third_party/blink/renderer/core/css/css_to_length_conversion_data.h b/third_party/blink/renderer/core/css/css_to_length_conversion_data.h index 7350eaa..8ee5c5a 100644 --- a/third_party/blink/renderer/core/css/css_to_length_conversion_data.h +++ b/third_party/blink/renderer/core/css/css_to_length_conversion_data.h
@@ -277,6 +277,9 @@ kTreeScopedReference = 1u << 7, // vi, vb, cqi, cqb, etc kLogicalDirectionRelative = 1u << 8, + // anchor(), anchor-size() + // https://drafts.csswg.org/css-anchor-position-1 + kAnchorRelative = 1u << 9, // Adjust the Flags type above if adding more bits below. }; @@ -340,6 +343,8 @@ line_height_size_ = line_height_size; } + void ReferenceAnchor() const override; + Length::AnchorEvaluator* AnchorEvaluator() const override { return anchor_data_.GetEvaluator(); }
diff --git a/third_party/blink/renderer/core/css/css_to_length_conversion_data_test.cc b/third_party/blink/renderer/core/css/css_to_length_conversion_data_test.cc index c20c94e..baca648 100644 --- a/third_party/blink/renderer/core/css/css_to_length_conversion_data_test.cc +++ b/third_party/blink/renderer/core/css/css_to_length_conversion_data_test.cc
@@ -31,7 +31,7 @@ : result_(result) {} std::optional<LayoutUnit> Evaluate( - const CalculationExpressionNode&) const override { + const CalculationExpressionNode&) override { return result_; }
diff --git a/third_party/blink/renderer/core/css/media_values.h b/third_party/blink/renderer/core/css/media_values.h index 92061122..e66c6a5 100644 --- a/third_party/blink/renderer/core/css/media_values.h +++ b/third_party/blink/renderer/core/css/media_values.h
@@ -139,6 +139,7 @@ // CSSLengthResolver override. void ReferenceTreeScope() const override {} + void ReferenceAnchor() const override {} protected: virtual ContainerSnappedFlags SnappedFlags() const {
diff --git a/third_party/blink/renderer/core/css/post_style_update_scope.h b/third_party/blink/renderer/core/css/post_style_update_scope.h index 984975b..111ef284 100644 --- a/third_party/blink/renderer/core/css/post_style_update_scope.h +++ b/third_party/blink/renderer/core/css/post_style_update_scope.h
@@ -63,6 +63,7 @@ private: friend class PostStyleUpdateScope; friend class ContainerQueryTest; + friend class StyleResolverTest; HeapHashSet<Member<Element>> elements_with_pending_updates_; HeapHashMap<Member<const Element>, Member<const ComputedStyle>> old_styles_;
diff --git a/third_party/blink/renderer/core/css/properties/css_property_test.cc b/third_party/blink/renderer/core/css/properties/css_property_test.cc index 25865c26..f71d53d 100644 --- a/third_party/blink/renderer/core/css/properties/css_property_test.cc +++ b/third_party/blink/renderer/core/css/properties/css_property_test.cc
@@ -38,7 +38,7 @@ : required_mode_(required_mode) {} std::optional<LayoutUnit> Evaluate( - const CalculationExpressionNode&) const override { + const CalculationExpressionNode&) override { return (required_mode_ == GetMode()) ? std::optional<LayoutUnit>(1) : std::optional<LayoutUnit>(); }
diff --git a/third_party/blink/renderer/core/css/resolver/matched_properties_cache.cc b/third_party/blink/renderer/core/css/resolver/matched_properties_cache.cc index 8b195f12..3b066144 100644 --- a/third_party/blink/renderer/core/css/resolver/matched_properties_cache.cc +++ b/third_party/blink/renderer/core/css/resolver/matched_properties_cache.cc
@@ -276,6 +276,11 @@ if (builder.HasContainerRelativeUnits()) { return false; } + if (builder.HasAnchorFunctions()) { + // The result of anchor() and anchor-size() functions can depend on + // the 'anchor' attribute on the element. + return false; + } // Avoiding cache for ::highlight styles, and the originating styles they are // associated with, because the style depends on the highlight names involved // and they're not cached.
diff --git a/third_party/blink/renderer/core/css/resolver/style_resolver.cc b/third_party/blink/renderer/core/css/resolver/style_resolver.cc index dd6e63c..ea520af 100644 --- a/third_party/blink/renderer/core/css/resolver/style_resolver.cc +++ b/third_party/blink/renderer/core/css/resolver/style_resolver.cc
@@ -135,12 +135,13 @@ // position-fallback, we can fall back to the default behavior (in // CSSAnimations) of using the current style on Element as the old style. // - // TODO(crbug.com/1502666): We also need to check whether we are a descendant + // TODO(crbug.com/40943044): We also need to check whether we are a descendant // of an element with position-fallback to cover the case where the descendant // explicitly inherits insets or other valid @try properties from the element - // with position-fallback. + // with position-fallback. This applies to descendants of elements with + // anchor queries as well. return (style_recalc_context.container || - style_recalc_context.is_interleaved_oof || + state.StyleBuilder().HasAnchorFunctions() || (RuntimeEnabledFeatures:: CSSAnchorPositioningCascadeFallbackEnabled() && state.StyleBuilder().PositionFallback())) && @@ -393,6 +394,9 @@ if (flags & static_cast<Flags>(Flag::kTreeScopedReference)) { state.SetHasTreeScopedReference(); } + if (flags & static_cast<Flags>(Flag::kAnchorRelative)) { + builder.SetHasAnchorFunctions(); + } if (flags & static_cast<Flags>(Flag::kLogicalDirectionRelative)) { builder.SetHasLogicalDirectionRelativeUnits(); } @@ -2368,10 +2372,19 @@ return false; } + // TODO(crbug.com/40943044): If we need to disable the optimization for + // elements with position-fallback/anchor(), we probably need to disable + // for descendants of such elements as well. if (RuntimeEnabledFeatures::CSSAnchorPositioningCascadeFallbackEnabled() && base_data->GetBaseComputedStyle()->PositionFallback()) { return false; } + if (RuntimeEnabledFeatures::CSSAnchorPositioningComputeAnchorEnabled() && + base_data->GetBaseComputedStyle()->HasAnchorFunctions()) { + // TODO(crbug.com/41483417): Enable this optimization for styles with + // anchor queries. + return false; + } return true; }
diff --git a/third_party/blink/renderer/core/css/resolver/style_resolver_test.cc b/third_party/blink/renderer/core/css/resolver/style_resolver_test.cc index f07e20a4..34277b8 100644 --- a/third_party/blink/renderer/core/css/resolver/style_resolver_test.cc +++ b/third_party/blink/renderer/core/css/resolver/style_resolver_test.cc
@@ -19,6 +19,7 @@ #include "third_party/blink/renderer/core/css/css_value_list.h" #include "third_party/blink/renderer/core/css/out_of_flow_data.h" #include "third_party/blink/renderer/core/css/parser/css_parser_local_context.h" +#include "third_party/blink/renderer/core/css/post_style_update_scope.h" #include "third_party/blink/renderer/core/css/properties/computed_style_utils.h" #include "third_party/blink/renderer/core/css/properties/css_property_ref.h" #include "third_party/blink/renderer/core/css/properties/longhands.h" @@ -117,9 +118,14 @@ GetStyleEngine().GetPositionFallbackRule(*name); if (rule) { const CSSPropertyValueSet* set = rule->TryPropertyValueSetAt(index); - GetStyleEngine().UpdateStyleForOutOfFlow(element, set); + GetStyleEngine().UpdateStyleForOutOfFlow(element, set, + /* anchor_evaluator */ nullptr); } } + + size_t GetCurrentOldStylesCount() { + return PostStyleUpdateScope::CurrentAnimationData()->old_styles_.size(); + } }; // Variant of `StyleResolverTest` that runs with and without CSSMPCImprovements @@ -1658,6 +1664,117 @@ EXPECT_FALSE(e->ComputedStyleRef().DependsOnSizeContainerQueries()); } +TEST_P(ParameterizedStyleResolverTest, AnchorQueriesMPC) { + ScopedCSSAnchorPositioningComputeAnchorForTest scoped_feature(true); + + GetDocument().documentElement()->setInnerHTML(R"HTML( + <style> + .anchor { + position: absolute; + width: 100px; + height: 100px; + } + #anchor1 { left: 100px; } + #anchor2 { left: 150px; } + .anchored { + position: absolute; + left: anchor(left); + } + </style> + <div class=anchor id=anchor1>X</div> + <div class=anchor id=anchor2>Y</div> + <div class=anchored id=a anchor=anchor1>A</div> + <div class=anchored id=b anchor=anchor2>B</div> + )HTML"); + + UpdateAllLifecyclePhasesForTest(); + + // #a and #b have identical styles, but the implicit anchor makes + // the anchor() queries give two different answers. + + auto* a = GetDocument().getElementById(AtomicString("a")); + auto* b = GetDocument().getElementById(AtomicString("b")); + + ASSERT_TRUE(a); + ASSERT_TRUE(b); + + EXPECT_EQ("100px", ComputedValue("left", a->ComputedStyleRef())); + EXPECT_EQ("150px", ComputedValue("left", b->ComputedStyleRef())); +} + +TEST_P(ParameterizedStyleResolverTest, AnchorQueryNoOldStyle) { + ScopedCSSAnchorPositioningComputeAnchorForTest scoped_feature(true); + + // This captures any calls to StoreOldStyleIfNeeded made during + // StyleResolver::ResolveStyle. + PostStyleUpdateScope post_style_update_scope(GetDocument()); + + GetDocument().documentElement()->setInnerHTML(R"HTML( + <style> + #anchored { + position: absolute; + left: anchor(--a left, 42px); + } + </style> + <div id=anchored>A</div> + )HTML"); + + UpdateAllLifecyclePhasesForTest(); + EXPECT_EQ(0u, GetCurrentOldStylesCount()); +} + +TEST_P(ParameterizedStyleResolverTest, AnchorQueryStoreOldStyle) { + ScopedCSSAnchorPositioningComputeAnchorForTest scoped_feature(true); + + // This captures any calls to StoreOldStyleIfNeeded made during + // StyleResolver::ResolveStyle. + PostStyleUpdateScope post_style_update_scope(GetDocument()); + + GetDocument().documentElement()->setInnerHTML(R"HTML( + <style> + #anchored { + position: absolute; + left: anchor(--a left, 42px); + transition: left 1s; + } + </style> + <div id=anchored>A</div> + )HTML"); + + UpdateAllLifecyclePhasesForTest(); + EXPECT_EQ(1u, GetCurrentOldStylesCount()); +} + +TEST_P(ParameterizedStyleResolverTest, AnchorQueryBaseComputedStyle) { + ScopedCSSAnchorPositioningComputeAnchorForTest scoped_feature(true); + + GetDocument().documentElement()->setInnerHTML(R"HTML( + <style> + #div { + position: absolute; + left: anchor(--a left, 42px); + } + </style> + <div id=div>A</div> + )HTML"); + UpdateAllLifecyclePhasesForTest(); + Element* div = GetDocument().getElementById(AtomicString("div")); + + // Create a situation where the base computed style optimization + // would normally be used. + auto* effect = CreateSimpleKeyframeEffectForTest(div, CSSPropertyID::kWidth, + "50px", "100px"); + GetDocument().Timeline().Play(effect); + UpdateAllLifecyclePhasesForTest(); + EXPECT_EQ("50px", ComputedValue("width", *StyleForId("div"))); + div->SetNeedsAnimationStyleRecalc(); + + // TODO(crbug.com/41483417): Enable this optimization for styles with + // anchor queries. + StyleResolverState state(GetDocument(), *div); + EXPECT_FALSE(StyleResolver::CanReuseBaseComputedStyle(state)); +} + TEST_P(ParameterizedStyleResolverTest, NoCascadeLayers) { GetDocument().documentElement()->setInnerHTML(R"HTML( <style> @@ -3647,6 +3764,101 @@ GetMaxHeight(max_height->ComputedStyleRef()))); } +TEST_P(ParameterizedStyleResolverTest, NoAnchorFunction) { + ScopedCSSAnchorPositioningComputeAnchorForTest scoped_feature(true); + + GetDocument().documentElement()->setInnerHTML(R"HTML( + <style> + div { + left: 10px; + } + </style> + <div id=div></div> + )HTML"); + + UpdateAllLifecyclePhasesForTest(); + + auto* div = GetDocument().getElementById(AtomicString("div")); + ASSERT_TRUE(div); + EXPECT_FALSE(div->ComputedStyleRef().HasAnchorFunctions()); +} + +TEST_P(ParameterizedStyleResolverTest, HasAnchorFunction) { + ScopedCSSAnchorPositioningComputeAnchorForTest scoped_feature(true); + + GetDocument().documentElement()->setInnerHTML(R"HTML( + <style> + div { + left: anchor(--a left); + } + </style> + <div id=div></div> + )HTML"); + + UpdateAllLifecyclePhasesForTest(); + + auto* div = GetDocument().getElementById(AtomicString("div")); + ASSERT_TRUE(div); + EXPECT_TRUE(div->ComputedStyleRef().HasAnchorFunctions()); +} + +TEST_P(ParameterizedStyleResolverTest, HasAnchorFunctionImplicit) { + ScopedCSSAnchorPositioningComputeAnchorForTest scoped_feature(true); + + GetDocument().documentElement()->setInnerHTML(R"HTML( + <style> + div { + left: anchor(left); + } + </style> + <div id=div></div> + )HTML"); + + UpdateAllLifecyclePhasesForTest(); + + auto* div = GetDocument().getElementById(AtomicString("div")); + ASSERT_TRUE(div); + EXPECT_TRUE(div->ComputedStyleRef().HasAnchorFunctions()); +} + +TEST_P(ParameterizedStyleResolverTest, HasAnchorSizeFunction) { + ScopedCSSAnchorPositioningComputeAnchorForTest scoped_feature(true); + + GetDocument().documentElement()->setInnerHTML(R"HTML( + <style> + div { + width: anchor-size(--a width); + } + </style> + <div id=div></div> + )HTML"); + + UpdateAllLifecyclePhasesForTest(); + + auto* div = GetDocument().getElementById(AtomicString("div")); + ASSERT_TRUE(div); + EXPECT_TRUE(div->ComputedStyleRef().HasAnchorFunctions()); +} + +TEST_P(ParameterizedStyleResolverTest, HasAnchorSizeFunctionImplicit) { + ScopedCSSAnchorPositioningComputeAnchorForTest scoped_feature(true); + + GetDocument().documentElement()->setInnerHTML(R"HTML( + <style> + div { + width: anchor-size(width); + } + </style> + <div id=div></div> + )HTML"); + + UpdateAllLifecyclePhasesForTest(); + + auto* div = GetDocument().getElementById(AtomicString("div")); + ASSERT_TRUE(div); + EXPECT_TRUE(div->ComputedStyleRef().HasAnchorFunctions()); +} + TEST_P(StyleResolverTestCQ, CanAffectAnimationsMPC) { GetDocument().documentElement()->setInnerHTML(R"HTML( <style>
diff --git a/third_party/blink/renderer/core/css/style_engine.cc b/third_party/blink/renderer/core/css/style_engine.cc index f09b6b5..fb440508 100644 --- a/third_party/blink/renderer/core/css/style_engine.cc +++ b/third_party/blink/renderer/core/css/style_engine.cc
@@ -3466,8 +3466,10 @@ container.GetLayoutObject()); } -void StyleEngine::UpdateStyleForOutOfFlow(Element& element, - const CSSPropertyValueSet* try_set) { +void StyleEngine::UpdateStyleForOutOfFlow( + Element& element, + const CSSPropertyValueSet* try_set, + Length::AnchorEvaluator* anchor_evaluator) { // Note that we enter this function for any OOF element, not just those that // use position-fallback. Therefore, it's important to return immediately // without doing any work when `try_set` and `existing_try_set` both are @@ -3476,8 +3478,21 @@ OutOfFlowData* out_of_flow_data = element.GetOutOfFlowData(); const CSSPropertyValueSet* existing_try_set = out_of_flow_data ? out_of_flow_data->GetTryPropertyValueSet() : nullptr; - if (existing_try_set == try_set) { - // No need to update style, the try set is the one we already used. + + bool needs_update = false; + + if (existing_try_set != try_set) { + element.EnsureOutOfFlowData().SetTryPropertyValueSet(try_set); + needs_update = true; + } + if (element.ComputedStyleRef().HasAnchorFunctions()) { + CHECK(RuntimeEnabledFeatures::CSSAnchorPositioningComputeAnchorEnabled()); + // TODO(crbug.com/41483417): Store results on OutOfFlowData, and check + // if we actually need an update. + needs_update = true; + } + + if (!needs_update) { return; } @@ -3492,6 +3507,7 @@ StyleRecalcContext style_recalc_context = StyleRecalcContext::FromAncestors(element); style_recalc_context.is_interleaved_oof = true; + style_recalc_context.anchor_evaluator = anchor_evaluator; StyleRecalcChange change = StyleRecalcChange().ForceRecalcChildren();
diff --git a/third_party/blink/renderer/core/css/style_engine.h b/third_party/blink/renderer/core/css/style_engine.h index b4e9a7a..04ba05b3 100644 --- a/third_party/blink/renderer/core/css/style_engine.h +++ b/third_party/blink/renderer/core/css/style_engine.h
@@ -615,7 +615,8 @@ // Updates the style of `element`, and descendants if needed. // The provided `try_set` represents the declaration block from a @try rule. void UpdateStyleForOutOfFlow(Element& element, - const CSSPropertyValueSet* try_set); + const CSSPropertyValueSet* try_set, + Length::AnchorEvaluator*); StyleRulePositionFallback* GetPositionFallbackRule(const ScopedCSSName&); void RecalcStyle();
diff --git a/third_party/blink/renderer/core/css/style_engine_test.cc b/third_party/blink/renderer/core/css/style_engine_test.cc index 9f76b24..7dab2bb5 100644 --- a/third_party/blink/renderer/core/css/style_engine_test.cc +++ b/third_party/blink/renderer/core/css/style_engine_test.cc
@@ -4556,6 +4556,150 @@ EXPECT_FALSE(GetDocument().NeedsLayoutTreeUpdateForNode(*a)); } +TEST_F(StyleEngineTest, UpdateStyleAndLayoutTreeWithAnchorQuery) { + ScopedCSSAnchorPositioningComputeAnchorForTest scoped_feature(true); + + GetDocument().documentElement()->setInnerHTML(R"HTML( + <style> + #anchored { + position: absolute; + left: anchor(--a left, 42px); + } + #anchored.toggle { + left: anchor(--a left, 84px); + } + + #inner { left: inherit; } + </style> + <main id=anchored> + <div id=inner></div> + </main> + )HTML"); + UpdateAllLifecyclePhases(); + EXPECT_FALSE(GetDocument().View()->NeedsLayout()); + + Element* anchored = GetDocument().getElementById(AtomicString("anchored")); + ASSERT_TRUE(anchored); + anchored->classList().Add(AtomicString("toggle")); + + GetDocument().UpdateStyleAndLayoutTree(); + EXPECT_FALSE(GetDocument().View()->NeedsLayout()) + << "Layout should happen as part of UpdateStyleAndLayoutTree"; + + Element* inner = GetDocument().getElementById(AtomicString("inner")); + ASSERT_TRUE(inner); + EXPECT_EQ("84px", ComputedValue(inner, "left")->CssText()); +} + +TEST_F(StyleEngineTest, UpdateStyleAndLayoutTreeForElementWithAnchorQuery) { + ScopedCSSAnchorPositioningComputeAnchorForTest scoped_feature(true); + + GetDocument().documentElement()->setInnerHTML(R"HTML( + <style> + #anchored { + position: absolute; + left: anchor(--a left, 42px); + } + #anchored.toggle { + left: anchor(--a left, 84px); + } + + #inner { left: inherit; } + </style> + <main id=anchored> + <div id=inner></div> + </main> + )HTML"); + UpdateAllLifecyclePhases(); + EXPECT_FALSE(GetDocument().View()->NeedsLayout()); + + Element* anchored = GetDocument().getElementById(AtomicString("anchored")); + ASSERT_TRUE(anchored); + anchored->classList().Add(AtomicString("toggle")); + + Element* inner = GetDocument().getElementById(AtomicString("inner")); + ASSERT_TRUE(inner); + + GetDocument().UpdateStyleAndLayoutTreeForElement(inner, + DocumentUpdateReason::kTest); + EXPECT_FALSE(GetDocument().View()->NeedsLayout()) + << "Layout should happen as part of UpdateStyleAndLayoutTreeForElement"; + + EXPECT_EQ("84px", ComputedValue(inner, "left")->CssText()); +} + +TEST_F(StyleEngineTest, AnchorQueryComputed) { + ScopedCSSAnchorPositioningComputeAnchorForTest scoped_feature(true); + + GetDocument().documentElement()->setInnerHTML(R"HTML( + <style> + #anchor { + anchor-name: --a; + position: absolute; + width: 100px; + height: 100px; + left: 200px; + top: 300px; + } + #anchored { + position: absolute; + width: anchor-size(--a width); + height: anchor-size(--unknown height, 42px); + left: anchor(--a right); + top: anchor(--a bottom); + } + </style> + <div id=anchor>Anchor</div> + <div id=anchored>Anchored</div> + )HTML"); + UpdateAllLifecyclePhasesForTest(); + + Element* anchored = GetDocument().getElementById(AtomicString("anchored")); + ASSERT_TRUE(anchored); + + EXPECT_EQ("300px", ComputedValue(anchored, "left")->CssText()); + EXPECT_EQ("400px", ComputedValue(anchored, "top")->CssText()); + EXPECT_EQ("100px", ComputedValue(anchored, "width")->CssText()); + EXPECT_EQ("42px", ComputedValue(anchored, "height")->CssText()); +} + +TEST_F(StyleEngineTest, AnchorQueryComputedChild) { + ScopedCSSAnchorPositioningComputeAnchorForTest scoped_feature(true); + + GetDocument().documentElement()->setInnerHTML(R"HTML( + <style> + #anchor { + anchor-name: --a; + position: absolute; + width: 100px; + height: 100px; + left: 200px; + top: 300px; + } + #anchored { + position: absolute; + width: anchor-size(--a width); + height: width: anchor-size(--a height); + } + #child { + width: anchor-size(--a width, 42px); + height: inherit; + } + </style> + <div id=anchor>Anchor</div> + <div id=anchored> + <div id=child>Child</div> + </div> + )HTML"); + UpdateAllLifecyclePhasesForTest(); + + Element* child = GetDocument().getElementById(AtomicString("child")); + ASSERT_TRUE(child); + + // Non-absolutely positioned child may not evaluate queries. + EXPECT_EQ("42px", ComputedValue(child, "width")->CssText()); +} + TEST_F(StyleEngineTest, VideoControlsReject) { GetDocument().body()->setInnerHTML(R"HTML( <video controls></video>
diff --git a/third_party/blink/renderer/core/dom/element.cc b/third_party/blink/renderer/core/dom/element.cc index c67c1579..9243df83 100644 --- a/third_party/blink/renderer/core/dom/element.cc +++ b/third_party/blink/renderer/core/dom/element.cc
@@ -3354,8 +3354,8 @@ style = context->AdjustElementStyle(style); } - // TODO(crbug.com/1502666): Descendants can also depend on position-fallback. - if (style->DependsOnSizeContainerQueries() || style->PositionFallback()) { + if (style->DependsOnSizeContainerQueries() || style->PositionFallback() || + style->HasAnchorFunctions()) { GetDocument().GetStyleEngine().SetStyleAffectedByLayout(); } @@ -3541,6 +3541,11 @@ StyleRecalcContext child_recalc_context = local_style_recalc_context; child_recalc_context.is_interleaved_oof = false; + // If we're in StyleEngine::UpdateStyleForOutOfFlow, then anchor_evaluator + // may be non-nullptr to allow evaluation of anchor() and anchor-size() + // queries. Descendants of the current out-of-flow element must not be + // allowed to evaluate such queries, however. + child_recalc_context.anchor_evaluator = nullptr; if (const ComputedStyle* style = GetComputedStyle()) { if (style->CanMatchSizeContainerQueries(*this)) {
diff --git a/third_party/blink/renderer/core/frame/history.cc b/third_party/blink/renderer/core/frame/history.cc index a8c1d44..acdca4b 100644 --- a/third_party/blink/renderer/core/frame/history.cc +++ b/third_party/blink/renderer/core/frame/history.cc
@@ -45,7 +45,7 @@ #include "third_party/blink/renderer/platform/bindings/v8_private_property.h" #include "third_party/blink/renderer/platform/heap/garbage_collected.h" #include "third_party/blink/renderer/platform/scheduler/public/task_attribution_info.h" -#include "third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h" +#include "third_party/blink/renderer/platform/scheduler/public/task_attribution_tracker.h" #include "third_party/blink/renderer/platform/weborigin/kurl.h" #include "third_party/blink/renderer/platform/weborigin/security_origin.h" #include "third_party/blink/renderer/platform/wtf/text/string_view.h" @@ -226,11 +226,12 @@ /*url=*/String("")); // Pass the current task ID so it'd be set as the parent task for the future // popstate event. - auto* tracker = ThreadScheduler::Current()->GetTaskAttributionTracker(); + auto* tracker = + scheduler::TaskAttributionTracker::From(script_state->GetIsolate()); scheduler::TaskAttributionInfo* task = nullptr; if (tracker && script_state->World().IsMainWorld() && frame->IsOutermostMainFrame()) { - task = tracker->RunningTask(script_state->GetIsolate()); + task = tracker->RunningTask(); tracker->AddSameDocumentNavigationTask(task); } DCHECK(frame->Client());
diff --git a/third_party/blink/renderer/core/frame/local_dom_window.cc b/third_party/blink/renderer/core/frame/local_dom_window.cc index 756b7b0..b4a959a7 100644 --- a/third_party/blink/renderer/core/frame/local_dom_window.cc +++ b/third_party/blink/renderer/core/frame/local_dom_window.cc
@@ -151,7 +151,7 @@ #include "third_party/blink/renderer/platform/scheduler/public/dummy_schedulers.h" #include "third_party/blink/renderer/platform/scheduler/public/event_loop.h" #include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h" -#include "third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h" +#include "third_party/blink/renderer/platform/scheduler/public/task_attribution_tracker.h" #include "third_party/blink/renderer/platform/storage/blink_storage_key.h" #include "third_party/blink/renderer/platform/timer.h" #include "third_party/blink/renderer/platform/weborigin/kurl.h" @@ -176,9 +176,10 @@ void SetCurrentTaskAsCallbackParent( CallbackFunctionWithTaskAttributionBase* callback) { ScriptState* script_state = callback->CallbackRelevantScriptState(); - auto* tracker = ThreadScheduler::Current()->GetTaskAttributionTracker(); + auto* tracker = + scheduler::TaskAttributionTracker::From(script_state->GetIsolate()); if (tracker && script_state->World().IsMainWorld()) { - callback->SetParentTask(tracker->RunningTask(script_state->GetIsolate())); + callback->SetParentTask(tracker->RunningTask()); } } @@ -929,9 +930,8 @@ // method. std::unique_ptr<scheduler::TaskAttributionTracker::TaskScope> task_attribution_scope; - CHECK(ThreadScheduler::Current()); - auto* tracker = ThreadScheduler::Current()->GetTaskAttributionTracker(); if (parent_task) { + auto* tracker = scheduler::TaskAttributionTracker::From(GetIsolate()); ScriptState* script_state = ToScriptStateForMainWorld(GetFrame()); if (script_state && tracker) { task_attribution_scope = tracker->CreateTaskScope(
diff --git a/third_party/blink/renderer/core/layout/absolute_utils.cc b/third_party/blink/renderer/core/layout/absolute_utils.cc index 46da19f..b20c6ad68 100644 --- a/third_party/blink/renderer/core/layout/absolute_utils.cc +++ b/third_party/blink/renderer/core/layout/absolute_utils.cc
@@ -354,9 +354,9 @@ return true; } const auto& style = node.Style(); - if (style.LogicalHeight().IsContentOrIntrinsic() || - style.LogicalMinHeight().IsContentOrIntrinsic() || - style.LogicalMaxHeight().IsContentOrIntrinsic()) { + if (style.LogicalHeight().HasContentOrIntrinsic() || + style.LogicalMinHeight().HasContentOrIntrinsic() || + style.LogicalMaxHeight().HasContentOrIntrinsic()) { return false; } if (style.LogicalHeight().IsAuto()) { @@ -597,7 +597,7 @@ const BoxStrut& border_padding, const std::optional<LogicalSize>& replaced_size, WritingDirectionMode container_writing_direction, - const AnchorEvaluatorImpl* anchor_evaluator, + AnchorEvaluatorImpl* anchor_evaluator, LogicalOofDimensions* dimensions) { DCHECK(dimensions); DCHECK_GE(imcb.InlineSize(), LayoutUnit()); @@ -740,7 +740,7 @@ const BoxStrut& border_padding, const std::optional<LogicalSize>& replaced_size, WritingDirectionMode container_writing_direction, - const AnchorEvaluatorImpl* anchor_evaluator, + AnchorEvaluatorImpl* anchor_evaluator, LogicalOofDimensions* dimensions) { DCHECK(dimensions); DCHECK_GE(imcb.BlockSize(), LayoutUnit()); @@ -831,10 +831,12 @@ // Manually resolve any intrinsic/content min/max block-sizes. // TODO(crbug.com/1135207): |ComputeMinMaxBlockSizes()| should handle this. - if (style.LogicalMinHeight().IsContentOrIntrinsic()) + if (style.LogicalMinHeight().HasContentOrIntrinsic()) { min_max_block_sizes.min_size = IntrinsicBlockSizeFunc(); - if (style.LogicalMaxHeight().IsContentOrIntrinsic()) + } + if (style.LogicalMaxHeight().HasContentOrIntrinsic()) { min_max_block_sizes.max_size = IntrinsicBlockSizeFunc(); + } min_max_block_sizes.max_size = std::max(min_max_block_sizes.max_size, min_max_block_sizes.min_size);
diff --git a/third_party/blink/renderer/core/layout/absolute_utils.h b/third_party/blink/renderer/core/layout/absolute_utils.h index a5429ea..5316a6e0 100644 --- a/third_party/blink/renderer/core/layout/absolute_utils.h +++ b/third_party/blink/renderer/core/layout/absolute_utils.h
@@ -174,7 +174,7 @@ const BoxStrut& border_padding, const std::optional<LogicalSize>& replaced_size, WritingDirectionMode container_writing_direction, - const AnchorEvaluatorImpl* anchor_evaluator, + AnchorEvaluatorImpl* anchor_evaluator, LogicalOofDimensions* dimensions); // If layout was performed to determine the position, this will be returned @@ -188,7 +188,7 @@ const BoxStrut& border_padding, const std::optional<LogicalSize>& replaced_size, WritingDirectionMode container_writing_direction, - const AnchorEvaluatorImpl* anchor_evaluator, + AnchorEvaluatorImpl* anchor_evaluator, LogicalOofDimensions* dimensions); } // namespace blink
diff --git a/third_party/blink/renderer/core/layout/anchor_query.cc b/third_party/blink/renderer/core/layout/anchor_query.cc index f48256e9..6de4ac8 100644 --- a/third_party/blink/renderer/core/layout/anchor_query.cc +++ b/third_party/blink/renderer/core/layout/anchor_query.cc
@@ -362,7 +362,7 @@ } std::optional<LayoutUnit> AnchorEvaluatorImpl::Evaluate( - const CalculationExpressionNode& node) const { + const CalculationExpressionNode& node) { DCHECK(node.IsAnchorQuery()); const auto& anchor_query = To<CalculationExpressionAnchorQueryNode>(node); switch (anchor_query.Type()) {
diff --git a/third_party/blink/renderer/core/layout/anchor_query.h b/third_party/blink/renderer/core/layout/anchor_query.h index 7c9024e9..b399f428 100644 --- a/third_party/blink/renderer/core/layout/anchor_query.h +++ b/third_party/blink/renderer/core/layout/anchor_query.h
@@ -315,8 +315,7 @@ // Evaluates the given anchor query. Returns nullopt if the query invalid // (e.g., no target or wrong axis). - std::optional<LayoutUnit> Evaluate( - const CalculationExpressionNode&) const override; + std::optional<LayoutUnit> Evaluate(const CalculationExpressionNode&) override; // Finds the rect of the element referenced by the `position-fallback-bounds` // property, or nullopt if there's no such element.
diff --git a/third_party/blink/renderer/core/layout/flex/flex_layout_algorithm.cc b/third_party/blink/renderer/core/layout/flex/flex_layout_algorithm.cc index c086685..dad10a1 100644 --- a/third_party/blink/renderer/core/layout/flex/flex_layout_algorithm.cc +++ b/third_party/blink/renderer/core/layout/flex/flex_layout_algorithm.cc
@@ -2571,14 +2571,15 @@ // Only allow growth if the item's block-size is auto and either the item // can't shrink or its min-height is auto. - if (item_style.LogicalHeight().IsAutoOrContentOrIntrinsic() && + if (item_style.LogicalHeight().HasAutoOrContentOrIntrinsic() && (!can_shrink || algorithm_.ShouldApplyMinSizeAutoForChild( - *item.ng_input_node.GetLayoutBox()))) + *item.ng_input_node.GetLayoutBox()))) { return true; + } } else { // Don't grow if the item's block-size should be the same as its container. if (WillChildCrossSizeBeContainerCrossSize(item.ng_input_node) && - !Style().LogicalHeight().IsAutoOrContentOrIntrinsic()) { + !Style().LogicalHeight().HasAutoOrContentOrIntrinsic()) { return false; }
diff --git a/third_party/blink/renderer/core/layout/grid/grid_layout_algorithm.cc b/third_party/blink/renderer/core/layout/grid/grid_layout_algorithm.cc index 3027f1d..ec73549 100644 --- a/third_party/blink/renderer/core/layout/grid/grid_layout_algorithm.cc +++ b/third_party/blink/renderer/core/layout/grid/grid_layout_algorithm.cc
@@ -3702,8 +3702,9 @@ // Only allow growth on "auto" block-size items, (a fixed block-size item // can't grow). - if (!item_style.LogicalHeight().IsAutoOrContentOrIntrinsic()) + if (!item_style.LogicalHeight().HasAutoOrContentOrIntrinsic()) { return false; + } // Only allow growth on items which only span a single row. if (grid_item.SpanSize(kForRows) > 1) @@ -3716,7 +3717,7 @@ return false; return !grid_item.IsSpanningFixedMinimumTrack(kForRows) || - Style().LogicalHeight().IsAutoOrContentOrIntrinsic(); + Style().LogicalHeight().HasAutoOrContentOrIntrinsic(); }; wtf_size_t previous_expansion_row_set_index = kNotFound;
diff --git a/third_party/blink/renderer/core/layout/inline/inline_item.h b/third_party/blink/renderer/core/layout/inline/inline_item.h index 130bbf0..ab440fb2 100644 --- a/third_party/blink/renderer/core/layout/inline/inline_item.h +++ b/third_party/blink/renderer/core/layout/inline/inline_item.h
@@ -271,7 +271,8 @@ unsigned start_offset_; unsigned end_offset_; - Member<const ShapeResult> shape_result_; + Member<const ShapeResult> shape_result_{ + nullptr, Member<const ShapeResult>::AtomicInitializerTag{}}; Member<LayoutObject> layout_object_; InlineItemType type_;
diff --git a/third_party/blink/renderer/core/layout/length_utils.cc b/third_party/blink/renderer/core/layout/length_utils.cc index ac49574a..013ef37b 100644 --- a/third_party/blink/renderer/core/layout/length_utils.cc +++ b/third_party/blink/renderer/core/layout/length_utils.cc
@@ -40,8 +40,9 @@ // TODO(https://crbug.com/313072): calc-size() doesn't work correctly // when this function returns true. - if (length.IsPercentOrCalc()) + if (length.HasPercent()) { return constraint_space.PercentageResolutionInlineSize() == kIndefiniteSize; + } if (length.IsFillAvailable()) return constraint_space.AvailableSize().inline_size == kIndefiniteSize; @@ -72,7 +73,7 @@ if (length.IsAuto() || length.IsMinContent() || length.IsMaxContent() || length.IsMinIntrinsic() || length.IsFitContent() || length.IsNone()) return true; - if (length.IsPercentOrCalc()) { + if (length.HasPercent()) { const LayoutUnit percentage_resolution_size = override_percentage_resolution_size ? *override_percentage_resolution_size @@ -93,7 +94,7 @@ const std::optional<MinMaxSizes>& min_max_sizes, const Length& length, LayoutUnit override_available_size, - const Length::AnchorEvaluator* anchor_evaluator) { + Length::AnchorEvaluator* anchor_evaluator) { DCHECK_EQ(constraint_space.GetWritingMode(), style.GetWritingMode()); switch (length.GetType()) { @@ -112,7 +113,8 @@ case Length::kCalculated: { const LayoutUnit percentage_resolution_size = constraint_space.PercentageResolutionInlineSize(); - DCHECK(length.IsFixed() || percentage_resolution_size != kIndefiniteSize) + DCHECK(!length.HasPercent() || + percentage_resolution_size != kIndefiniteSize) << length.ToString(); LayoutUnit value = MinimumValueForLength( length, percentage_resolution_size, @@ -169,7 +171,7 @@ LayoutUnit intrinsic_size, LayoutUnit override_available_size, const LayoutUnit* override_percentage_resolution_size, - const Length::AnchorEvaluator* anchor_evaluator) { + Length::AnchorEvaluator* anchor_evaluator) { DCHECK_EQ(constraint_space.GetWritingMode(), style.GetWritingMode()); switch (length.GetType()) { @@ -190,7 +192,8 @@ override_percentage_resolution_size ? *override_percentage_resolution_size : constraint_space.PercentageResolutionBlockSize(); - DCHECK(length.IsFixed() || percentage_resolution_size != kIndefiniteSize); + DCHECK(!length.HasPercent() || + percentage_resolution_size != kIndefiniteSize); LayoutUnit value = MinimumValueForLength( length, percentage_resolution_size, {.anchor_evaluator = anchor_evaluator, @@ -404,12 +407,11 @@ MinMaxSizesFunc); } -MinMaxSizes ComputeMinMaxBlockSizes( - const ConstraintSpace& space, - const ComputedStyle& style, - const BoxStrut& border_padding, - LayoutUnit override_available_size, - const Length::AnchorEvaluator* anchor_evaluator) { +MinMaxSizes ComputeMinMaxBlockSizes(const ConstraintSpace& space, + const ComputedStyle& style, + const BoxStrut& border_padding, + LayoutUnit override_available_size, + Length::AnchorEvaluator* anchor_evaluator) { if (const std::optional<MinMaxSizes> override_sizes = space.OverrideMinMaxBlockSizes()) { DCHECK_GE(override_sizes->max_size, override_sizes->min_size); @@ -684,7 +686,7 @@ const ConstraintSpace& space, const BoxStrut& border_padding, ReplacedSizeMode mode, - const Length::AnchorEvaluator* anchor_evaluator) { + Length::AnchorEvaluator* anchor_evaluator) { DCHECK(node.IsReplaced()); const ComputedStyle& style = node.Style(); @@ -721,7 +723,7 @@ if (space.IsFixedBlockSize()) { replaced_block = space.AvailableSize().block_size; DCHECK_GE(*replaced_block, 0); - } else if (!block_length.IsAutoOrContentOrIntrinsic() || + } else if (!block_length.HasAutoOrContentOrIntrinsic() || (space.IsBlockAutoBehaviorStretch() && space.AvailableSize().block_size != kIndefiniteSize)) { Length block_length_to_resolve = block_length; @@ -945,12 +947,11 @@ } // namespace // Computes size for a replaced element. -LogicalSize ComputeReplacedSize( - const BlockNode& node, - const ConstraintSpace& space, - const BoxStrut& border_padding, - ReplacedSizeMode mode, - const Length::AnchorEvaluator* anchor_evaluator) { +LogicalSize ComputeReplacedSize(const BlockNode& node, + const ConstraintSpace& space, + const BoxStrut& border_padding, + ReplacedSizeMode mode, + Length::AnchorEvaluator* anchor_evaluator) { DCHECK(node.IsReplaced()); const auto* svg_root = DynamicTo<LayoutSVGRoot>(node.GetLayoutBox());
diff --git a/third_party/blink/renderer/core/layout/length_utils.h b/third_party/blink/renderer/core/layout/length_utils.h index 00f8ac3..2a3d68c 100644 --- a/third_party/blink/renderer/core/layout/length_utils.h +++ b/third_party/blink/renderer/core/layout/length_utils.h
@@ -28,9 +28,9 @@ class Length; inline bool NeedMinMaxSize(const ComputedStyle& style) { - return style.LogicalWidth().IsContentOrIntrinsic() || - style.LogicalMinWidth().IsContentOrIntrinsic() || - style.LogicalMaxWidth().IsContentOrIntrinsic(); + return style.LogicalWidth().HasContentOrIntrinsic() || + style.LogicalMinWidth().HasContentOrIntrinsic() || + style.LogicalMaxWidth().HasContentOrIntrinsic(); } CORE_EXPORT LayoutUnit @@ -71,7 +71,7 @@ const std::optional<MinMaxSizes>&, const Length&, LayoutUnit override_available_size = kIndefiniteSize, - const Length::AnchorEvaluator* anchor_evaluator = nullptr); + Length::AnchorEvaluator* anchor_evaluator = nullptr); // Same as ResolveInlineLengthInternal, except here |intrinsic_size| roughly // plays the part of |MinMaxSizes|. @@ -83,7 +83,7 @@ LayoutUnit intrinsic_size, LayoutUnit override_available_size = kIndefiniteSize, const LayoutUnit* override_percentage_resolution_size = nullptr, - const Length::AnchorEvaluator* anchor_evaluator = nullptr); + Length::AnchorEvaluator* anchor_evaluator = nullptr); // In this file the template parameter MinMaxSizesFunc should have the // following form: @@ -103,13 +103,13 @@ const MinMaxSizesFunc& min_max_sizes_func, const Length& length, LayoutUnit override_available_size = kIndefiniteSize, - const Length::AnchorEvaluator* anchor_evaluator = nullptr) { + Length::AnchorEvaluator* anchor_evaluator = nullptr) { if (LIKELY(length.IsAuto() || InlineLengthUnresolvable(constraint_space, length))) return border_padding.InlineSum(); std::optional<MinMaxSizes> min_max_sizes; - if (length.IsContentOrIntrinsic()) { + if (length.HasContentOrIntrinsic()) { min_max_sizes = min_max_sizes_func(length.IsMinIntrinsic() ? MinMaxSizesType::kIntrinsic : MinMaxSizesType::kContent) @@ -130,13 +130,13 @@ const MinMaxSizesFunc& min_max_sizes_func, const Length& length, LayoutUnit override_available_size = kIndefiniteSize, - const Length::AnchorEvaluator* anchor_evaluator = nullptr) { + Length::AnchorEvaluator* anchor_evaluator = nullptr) { if (LIKELY(length.IsNone() || InlineLengthUnresolvable(constraint_space, length))) return LayoutUnit::Max(); std::optional<MinMaxSizes> min_max_sizes; - if (length.IsContentOrIntrinsic()) { + if (length.HasContentOrIntrinsic()) { min_max_sizes = min_max_sizes_func(length.IsMinIntrinsic() ? MinMaxSizesType::kIntrinsic : MinMaxSizesType::kContent) @@ -157,10 +157,10 @@ const MinMaxSizesFunc& min_max_sizes_func, const Length& length, LayoutUnit override_available_size = kIndefiniteSize, - const Length::AnchorEvaluator* anchor_evaluator = nullptr) { + Length::AnchorEvaluator* anchor_evaluator = nullptr) { DCHECK(!length.IsAuto()); std::optional<MinMaxSizes> min_max_sizes; - if (length.IsContentOrIntrinsic()) { + if (length.HasContentOrIntrinsic()) { min_max_sizes = min_max_sizes_func(length.IsMinIntrinsic() ? MinMaxSizesType::kIntrinsic : MinMaxSizesType::kContent) @@ -180,7 +180,7 @@ const Length& length, LayoutUnit override_available_size = kIndefiniteSize, const LayoutUnit* override_percentage_resolution_size = nullptr, - const Length::AnchorEvaluator* anchor_evaluator = nullptr) { + Length::AnchorEvaluator* anchor_evaluator = nullptr) { if (LIKELY(BlockLengthUnresolvable(constraint_space, length, override_percentage_resolution_size))) return border_padding.BlockSum(); @@ -199,7 +199,7 @@ const Length& length, LayoutUnit override_available_size = kIndefiniteSize, const LayoutUnit* override_percentage_resolution_size = nullptr, - const Length::AnchorEvaluator* anchor_evaluator = nullptr) { + Length::AnchorEvaluator* anchor_evaluator = nullptr) { if (LIKELY(BlockLengthUnresolvable(constraint_space, length, override_percentage_resolution_size))) return LayoutUnit::Max(); @@ -219,12 +219,13 @@ LayoutUnit intrinsic_size, LayoutUnit override_available_size = kIndefiniteSize, const LayoutUnit* override_percentage_resolution_size = nullptr, - const Length::AnchorEvaluator* anchor_evaluator = nullptr) { + Length::AnchorEvaluator* anchor_evaluator = nullptr) { DCHECK(!length.IsAuto()); - if (UNLIKELY((length.IsPercentOrCalc() || length.IsFillAvailable()) && + if (UNLIKELY((length.HasPercent() || length.IsFillAvailable()) && BlockLengthUnresolvable(constraint_space, length, - override_percentage_resolution_size))) + override_percentage_resolution_size))) { return intrinsic_size; + } return ResolveBlockLengthInternal( constraint_space, style, border_padding, length, intrinsic_size, @@ -240,15 +241,17 @@ const Length& length, const IntrinsicBlockSizeFunc& intrinsic_block_size_func, LayoutUnit override_available_size = kIndefiniteSize, - const Length::AnchorEvaluator* anchor_evaluator = nullptr) { + Length::AnchorEvaluator* anchor_evaluator = nullptr) { DCHECK(!length.IsAuto()); - if (UNLIKELY((length.IsPercentOrCalc() || length.IsFillAvailable()) && - BlockLengthUnresolvable(constraint_space, length))) + if (UNLIKELY((length.HasPercent() || length.IsFillAvailable()) && + BlockLengthUnresolvable(constraint_space, length))) { return intrinsic_block_size_func(); + } LayoutUnit intrinsic_block_size = kIndefiniteSize; - if (length.IsContentOrIntrinsic()) + if (length.HasContentOrIntrinsic()) { intrinsic_block_size = intrinsic_block_size_func(); + } return ResolveBlockLengthInternal( constraint_space, style, border_padding, length, intrinsic_block_size, @@ -262,7 +265,7 @@ const ComputedStyle&, const BoxStrut& border_padding, LayoutUnit override_available_size = kIndefiniteSize, - const Length::AnchorEvaluator* anchor_evaluator = nullptr); + Length::AnchorEvaluator* anchor_evaluator = nullptr); MinMaxSizes ComputeTransferredMinMaxInlineSizes( const LogicalSize& ratio, @@ -292,7 +295,7 @@ const MinMaxSizesFunc& min_max_sizes_func, const Length* opt_min_length = nullptr, LayoutUnit override_available_size = kIndefiniteSize, - const Length::AnchorEvaluator* anchor_evaluator = nullptr) { + Length::AnchorEvaluator* anchor_evaluator = nullptr) { const ComputedStyle& style = node.Style(); const Length& min_length = opt_min_length ? *opt_min_length : style.LogicalMinWidth(); @@ -479,12 +482,12 @@ // This will handle both intrinsic, and layout calculations depending on the // space provided. (E.g. if the available inline-size is indefinite it will // return the intrinsic size). -CORE_EXPORT LogicalSize ComputeReplacedSize( - const BlockNode&, - const ConstraintSpace&, - const BoxStrut& border_padding, - ReplacedSizeMode = ReplacedSizeMode::kNormal, - const Length::AnchorEvaluator* anchor_evaluator = nullptr); +CORE_EXPORT LogicalSize +ComputeReplacedSize(const BlockNode&, + const ConstraintSpace&, + const BoxStrut& border_padding, + ReplacedSizeMode = ReplacedSizeMode::kNormal, + Length::AnchorEvaluator* anchor_evaluator = nullptr); // Based on available inline size, CSS computed column-width, CSS computed // column-count and CSS used column-gap, return CSS used column-count.
diff --git a/third_party/blink/renderer/core/layout/out_of_flow_layout_part.cc b/third_party/blink/renderer/core/layout/out_of_flow_layout_part.cc index 60eb484..7c67ca7 100644 --- a/third_party/blink/renderer/core/layout/out_of_flow_layout_part.cc +++ b/third_party/blink/renderer/core/layout/out_of_flow_layout_part.cc
@@ -135,8 +135,11 @@ STACK_ALLOCATED(); public: - explicit OOFCandidateStyleIterator(const LayoutObject& object) - : element_(DynamicTo<Element>(object.GetNode())), style_(object.Style()) { + explicit OOFCandidateStyleIterator(const LayoutObject& object, + Length::AnchorEvaluator* anchor_evaluator) + : element_(DynamicTo<Element>(object.GetNode())), + style_(object.Style()), + anchor_evaluator_(anchor_evaluator) { Initialize(); } @@ -249,7 +252,12 @@ CHECK(element_); if (RuntimeEnabledFeatures::CSSAnchorPositioningCascadeFallbackEnabled()) { StyleEngine& style_engine = element_->GetDocument().GetStyleEngine(); - style_engine.UpdateStyleForOutOfFlow(*element_, try_set); + Length::AnchorEvaluator* anchor_evaluator = + RuntimeEnabledFeatures::CSSAnchorPositioningCascadeFallbackEnabled() + ? anchor_evaluator_ + : nullptr; + style_engine.UpdateStyleForOutOfFlow(*element_, try_set, + anchor_evaluator); } CHECK(element_->GetLayoutObject()); // Returns LayoutObject ComputedStyle instead of element style for layout @@ -264,6 +272,11 @@ // Otherwise, the base style for generating auto anchor fallbacks. const ComputedStyle* style_ = nullptr; + // When CSSAnchorPositioningComputeAnchor is enabled, this evaluator is + // passed to StyleEngine::UpdateStyleForOutOfFlow to evaluate anchor queries + // on the computed style. + Length::AnchorEvaluator* anchor_evaluator_ = nullptr; + // If the current style is created from an `@try` rule, this holds // the parent rule. Otherwise nullptr. const StyleRulePositionFallback* position_fallback_rule_ = nullptr; @@ -1825,7 +1838,8 @@ // If `@position-fallback` exists, let |TryCalculateOffset| check if the // result fits the available space. - OOFCandidateStyleIterator iter(*node_info.node.GetLayoutBox()); + OOFCandidateStyleIterator iter(*node_info.node.GetLayoutBox(), + anchor_evaluator); std::optional<OffsetInfo> offset_info; while (!offset_info) { const bool has_next_fallback_style = iter.HasNextStyle();
diff --git a/third_party/blink/renderer/core/loader/document_loader.cc b/third_party/blink/renderer/core/loader/document_loader.cc index 2a8d1b1e..01a4f9c0 100644 --- a/third_party/blink/renderer/core/loader/document_loader.cc +++ b/third_party/blink/renderer/core/loader/document_loader.cc
@@ -164,7 +164,7 @@ #include "third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.h" #include "third_party/blink/renderer/platform/scheduler/public/event_loop.h" #include "third_party/blink/renderer/platform/scheduler/public/frame_scheduler.h" -#include "third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h" +#include "third_party/blink/renderer/platform/scheduler/public/task_attribution_tracker.h" #include "third_party/blink/renderer/platform/storage/blink_storage_key.h" #include "third_party/blink/renderer/platform/web_test_support.h" #include "third_party/blink/renderer/platform/weborigin/kurl.h" @@ -1035,9 +1035,8 @@ // If `heuristics` exists, it means we're in an outermost main frame; if // `soft_navigation_heuristics_task_id` exists, it means the task state // being propagated was captured in a main world history API call. - CHECK(ThreadScheduler::Current()); - if (auto* tracker = - ThreadScheduler::Current()->GetTaskAttributionTracker()) { + if (auto* tracker = scheduler::TaskAttributionTracker::From( + frame_->DomWindow()->GetIsolate())) { // Get the TaskId from tracker. We're passing that to dispatchEvent // further down, but regardless, we want to get it and previous tasks out // of the tracker's task queue, to enable them to get garbage collected if @@ -2616,8 +2615,8 @@ // Previous same-document navigation tasks are not relevant once a // cross-document navigation has happened. - CHECK(ThreadScheduler::Current()); - if (auto* tracker = ThreadScheduler::Current()->GetTaskAttributionTracker()) { + if (auto* tracker = scheduler::TaskAttributionTracker::From( + frame_->DomWindow()->GetIsolate())) { tracker->ResetSameDocumentNavigationTasks(); }
diff --git a/third_party/blink/renderer/core/messaging/message_port.cc b/third_party/blink/renderer/core/messaging/message_port.cc index 9a12b88..e74064c 100644 --- a/third_party/blink/renderer/core/messaging/message_port.cc +++ b/third_party/blink/renderer/core/messaging/message_port.cc
@@ -53,7 +53,6 @@ #include "third_party/blink/renderer/platform/bindings/thread_debugger.h" #include "third_party/blink/renderer/platform/instrumentation/use_counter.h" #include "third_party/blink/renderer/platform/scheduler/public/task_attribution_tracker.h" -#include "third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h" #include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h" #include "third_party/blink/renderer/platform/wtf/text/atomic_string.h" @@ -137,12 +136,11 @@ // Only pass the parent task ID if we're in the main world, as isolated world // task tracking is not yet supported. Also, only pass the parent task if the // port is still entangled to its initially entangled port. - if (auto* tracker = ThreadScheduler::Current()->GetTaskAttributionTracker(); + if (auto* tracker = + scheduler::TaskAttributionTracker::From(script_state->GetIsolate()); initially_entangled_port_ && tracker && script_state->World().IsMainWorld()) { - scheduler::TaskAttributionInfo* task = - tracker->RunningTask(script_state->GetIsolate()); - if (task) { + if (scheduler::TaskAttributionInfo* task = tracker->RunningTask()) { // Since `initially_entangled_port_` is not nullptr, neither should be // `post_message_task_container_`. CHECK(post_message_task_container_); @@ -377,9 +375,8 @@ // v8::Context may still be empty (and hence // ExecutionContext::GetCurrentWorld returns null). if (ScriptState* script_state = ToScriptStateForMainWorld(context)) { - CHECK(ThreadScheduler::Current()); - if (auto* tracker = - ThreadScheduler::Current()->GetTaskAttributionTracker()) { + if (auto* tracker = scheduler::TaskAttributionTracker::From( + script_state->GetIsolate())) { // Since `initially_entangled_port_` is not nullptr, neither should be // its `post_message_task_container_`. CHECK(entangled_port->post_message_task_container_);
diff --git a/third_party/blink/renderer/core/navigation_api/navigation_api.cc b/third_party/blink/renderer/core/navigation_api/navigation_api.cc index 1bfbef5..56bd2b0 100644 --- a/third_party/blink/renderer/core/navigation_api/navigation_api.cc +++ b/third_party/blink/renderer/core/navigation_api/navigation_api.cc
@@ -47,7 +47,7 @@ #include "third_party/blink/renderer/platform/bindings/exception_context.h" #include "third_party/blink/renderer/platform/bindings/exception_state.h" #include "third_party/blink/renderer/platform/scheduler/public/task_attribution_info.h" -#include "third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h" +#include "third_party/blink/renderer/platform/scheduler/public/task_attribution_tracker.h" namespace blink { @@ -625,9 +625,10 @@ if (SoftNavigationHeuristics* heuristics = SoftNavigationHeuristics::From(*window_)) { heuristics->SameDocumentNavigationStarted(); - auto* tracker = ThreadScheduler::Current()->GetTaskAttributionTracker(); + auto* tracker = + scheduler::TaskAttributionTracker::From(script_state->GetIsolate()); if (tracker && script_state->World().IsMainWorld()) { - task = tracker->RunningTask(script_state->GetIsolate()); + task = tracker->RunningTask(); tracker->AddSameDocumentNavigationTask(task); } } @@ -896,7 +897,8 @@ void NavigationApi::InformAboutCanceledNavigation( CancelNavigationReason reason) { - if (auto* tracker = ThreadScheduler::Current()->GetTaskAttributionTracker(); + if (auto* tracker = + scheduler::TaskAttributionTracker::From(window_->GetIsolate()); tracker && reason != CancelNavigationReason::kNavigateEvent) { tracker->ResetSameDocumentNavigationTasks(); }
diff --git a/third_party/blink/renderer/core/script/pending_script.cc b/third_party/blink/renderer/core/script/pending_script.cc index de07db2..9a100ec8 100644 --- a/third_party/blink/renderer/core/script/pending_script.cc +++ b/third_party/blink/renderer/core/script/pending_script.cc
@@ -42,7 +42,6 @@ #include "third_party/blink/renderer/platform/instrumentation/use_counter.h" #include "third_party/blink/renderer/platform/scheduler/public/frame_scheduler.h" #include "third_party/blink/renderer/platform/scheduler/public/task_attribution_tracker.h" -#include "third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h" namespace blink { @@ -167,9 +166,8 @@ std::unique_ptr<scheduler::TaskAttributionTracker::TaskScope> task_attribution_scope; if (ScriptState* script_state = ToScriptStateForMainWorld(frame)) { - DCHECK(ThreadScheduler::Current()); - if (auto* tracker = - ThreadScheduler::Current()->GetTaskAttributionTracker()) { + if (auto* tracker = scheduler::TaskAttributionTracker::From( + script_state->GetIsolate())) { task_attribution_scope = tracker->CreateTaskScope( script_state, parent_task_, scheduler::TaskAttributionTracker::TaskScopeType::kScriptExecution);
diff --git a/third_party/blink/renderer/core/script/script_loader.cc b/third_party/blink/renderer/core/script/script_loader.cc index 123621d..efb964d 100644 --- a/third_party/blink/renderer/core/script/script_loader.cc +++ b/third_party/blink/renderer/core/script/script_loader.cc
@@ -79,7 +79,7 @@ #include "third_party/blink/renderer/platform/loader/subresource_integrity.h" #include "third_party/blink/renderer/platform/network/mime/mime_type_registry.h" #include "third_party/blink/renderer/platform/runtime_enabled_features.h" -#include "third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h" +#include "third_party/blink/renderer/platform/scheduler/public/task_attribution_tracker.h" #include "third_party/blink/renderer/platform/weborigin/security_origin.h" #include "third_party/blink/renderer/platform/weborigin/security_policy.h" #include "third_party/blink/renderer/platform/wtf/std_lib_extras.h" @@ -87,19 +87,20 @@ #include "third_party/blink/renderer/platform/wtf/text/string_hash.h" #include "third_party/blink/renderer/platform/wtf/text/string_view.h" +namespace blink { + namespace { -blink::scheduler::TaskAttributionInfo* GetRunningTask( - blink::ScriptState* script_state) { + +scheduler::TaskAttributionInfo* GetRunningTask(ScriptState* script_state) { auto* tracker = - blink::ThreadScheduler::Current()->GetTaskAttributionTracker(); + scheduler::TaskAttributionTracker::From(script_state->GetIsolate()); if (!script_state || !script_state->World().IsMainWorld() || !tracker) { return nullptr; } - return tracker->RunningTask(script_state->GetIsolate()); + return tracker->RunningTask(); } } // namespace -namespace blink { ScriptLoader::ScriptLoader(ScriptElementBase* element, const CreateElementFlags flags)
diff --git a/third_party/blink/renderer/core/style/computed_style.cc b/third_party/blink/renderer/core/style/computed_style.cc index 0fb6dea1..4d672cd 100644 --- a/third_party/blink/renderer/core/style/computed_style.cc +++ b/third_party/blink/renderer/core/style/computed_style.cc
@@ -2686,8 +2686,9 @@ bool ComputedStyle::IsInterleavingRoot(const ComputedStyle* style) { const ComputedStyle* unensured = ComputedStyle::NullifyEnsured(style); - return unensured && (unensured->IsContainerForSizeContainerQueries() || - unensured->PositionFallback()); + return unensured && + (unensured->IsContainerForSizeContainerQueries() || + unensured->PositionFallback() || unensured->HasAnchorFunctions()); } bool ComputedStyle::CalculateIsStackingContextWithoutContainment() const {
diff --git a/third_party/blink/renderer/core/style/computed_style_extra_fields.json5 b/third_party/blink/renderer/core/style/computed_style_extra_fields.json5 index 30c5de1..5d2bb33a 100644 --- a/third_party/blink/renderer/core/style/computed_style_extra_fields.json5 +++ b/third_party/blink/renderer/core/style/computed_style_extra_fields.json5
@@ -1199,5 +1199,12 @@ field_group: "*", default_value: "false", }, + { + name: "HasAnchorFunctions", + field_template: "monotonic_flag", + field_group: "surround", + default_value: "false", + custom_compare: true, + }, ], }
diff --git a/third_party/blink/renderer/core/style/grid_track_size.h b/third_party/blink/renderer/core/style/grid_track_size.h index 27752f6..e8b826c 100644 --- a/third_party/blink/renderer/core/style/grid_track_size.h +++ b/third_party/blink/renderer/core/style/grid_track_size.h
@@ -112,8 +112,8 @@ GridTrackSizeType GetType() const { return type_; } bool IsContentSized() const { - return min_track_breadth_.IsAutoOrContentOrIntrinsic() || - max_track_breadth_.IsAutoOrContentOrIntrinsic(); + return min_track_breadth_.HasAutoOrContentOrIntrinsic() || + max_track_breadth_.HasAutoOrContentOrIntrinsic(); } bool IsFitContent() const { return type_ == kFitContentTrackSizing; } bool HasPercentage() const {
diff --git a/third_party/blink/renderer/core/timing/soft_navigation_heuristics.cc b/third_party/blink/renderer/core/timing/soft_navigation_heuristics.cc index d582726..316d703 100644 --- a/third_party/blink/renderer/core/timing/soft_navigation_heuristics.cc +++ b/third_party/blink/renderer/core/timing/soft_navigation_heuristics.cc
@@ -16,7 +16,6 @@ #include "third_party/blink/renderer/core/paint/timing/paint_timing.h" #include "third_party/blink/renderer/core/paint/timing/paint_timing_detector.h" #include "third_party/blink/renderer/core/timing/dom_window_performance.h" -#include "third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h" #include "third_party/blink/renderer/platform/scheduler/public/task_attribution_info.h" #include "third_party/blink/renderer/platform/scheduler/public/task_attribution_tracker.h" @@ -215,12 +214,9 @@ if (potential_soft_navigation_tasks_.empty()) { return std::nullopt; } - ThreadScheduler* scheduler = ThreadScheduler::Current(); - DCHECK(scheduler); - if (scheduler::TaskAttributionTracker* tracker = - scheduler->GetTaskAttributionTracker()) { - scheduler::TaskAttributionInfo* task = - tracker->RunningTask(GetSupplementable()->GetIsolate()); + if (auto* tracker = scheduler::TaskAttributionTracker::From( + GetSupplementable()->GetIsolate())) { + scheduler::TaskAttributionInfo* task = tracker->RunningTask(); if (!task) { return std::nullopt; } @@ -655,7 +651,9 @@ } } - return SoftNavigationHeuristics::EventScope(this); + return SoftNavigationHeuristics::EventScope( + this, scheduler::TaskAttributionTracker::From( + GetSupplementable()->GetIsolate())); } void SoftNavigationHeuristics::OnSoftNavigationEventScopeDestroyed() { @@ -675,16 +673,13 @@ // SoftNavigationHeuristics::EventScope implementation // /////////////////////////////////////////// SoftNavigationHeuristics::EventScope::EventScope( - SoftNavigationHeuristics* heuristics) + SoftNavigationHeuristics* heuristics, + scheduler::TaskAttributionTracker* tracker) : heuristics_(heuristics) { CHECK(heuristics_); - ThreadScheduler* scheduler = ThreadScheduler::Current(); - DCHECK(scheduler); - auto* tracker = scheduler->GetTaskAttributionTracker(); - if (!tracker) { - return; + if (tracker) { + observer_scope_ = tracker->RegisterObserver(heuristics_); } - observer_scope_ = tracker->RegisterObserver(heuristics_); } SoftNavigationHeuristics::EventScope::EventScope(EventScope&& other)
diff --git a/third_party/blink/renderer/core/timing/soft_navigation_heuristics.h b/third_party/blink/renderer/core/timing/soft_navigation_heuristics.h index bfcc34a..0c0a609 100644 --- a/third_party/blink/renderer/core/timing/soft_navigation_heuristics.h +++ b/third_party/blink/renderer/core/timing/soft_navigation_heuristics.h
@@ -76,7 +76,7 @@ private: friend class SoftNavigationHeuristics; - explicit EventScope(SoftNavigationHeuristics*); + EventScope(SoftNavigationHeuristics*, scheduler::TaskAttributionTracker*); SoftNavigationHeuristics* heuristics_; std::optional<scheduler::TaskAttributionTracker::ObserverScope>
diff --git a/third_party/blink/renderer/core/timing/soft_navigation_heuristics_test.cc b/third_party/blink/renderer/core/timing/soft_navigation_heuristics_test.cc index 5e56f2b..d3cff14b 100644 --- a/third_party/blink/renderer/core/timing/soft_navigation_heuristics_test.cc +++ b/third_party/blink/renderer/core/timing/soft_navigation_heuristics_test.cc
@@ -13,7 +13,6 @@ #include "third_party/blink/renderer/platform/heap/thread_state.h" #include "third_party/blink/renderer/platform/scheduler/public/task_attribution_info.h" #include "third_party/blink/renderer/platform/scheduler/public/task_attribution_tracker.h" -#include "third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h" #include "third_party/blink/renderer/platform/testing/task_environment.h" namespace blink { @@ -132,10 +131,12 @@ TEST_F(SoftNavigationHeuristicsTest, ResetHeuristicOnSetBecameEmpty) { auto* heuristics = CreateSoftNavigationHeuristicsForTest(); ASSERT_TRUE(heuristics); - auto* tracker = ThreadScheduler::Current()->GetTaskAttributionTracker(); - ASSERT_TRUE(tracker); auto* script_state = GetScriptStateForTest(); + auto* tracker = + scheduler::TaskAttributionTracker::From(script_state->GetIsolate()); + ASSERT_TRUE(tracker); + Persistent<scheduler::TaskAttributionInfo> root_task = nullptr; // Simulate a click. { @@ -145,7 +146,7 @@ /*is_new_interaction=*/true)); std::unique_ptr<TaskScope> task_scope = tracker->CreateTaskScope( script_state, /*parent_task=*/nullptr, TaskScopeType::kCallback); - root_task = tracker->RunningTask(script_state->GetIsolate()); + root_task = tracker->RunningTask(); } EXPECT_TRUE(root_task); EXPECT_GT(heuristics->GetLastInteractionTaskIdForTest(), 0u); @@ -155,7 +156,7 @@ { std::unique_ptr<TaskScope> task_scope = tracker->CreateTaskScope( script_state, root_task, TaskScopeType::kCallback); - descendant_task = tracker->RunningTask(script_state->GetIsolate()); + descendant_task = tracker->RunningTask(); } EXPECT_TRUE(descendant_task);
diff --git a/third_party/blink/renderer/core/view_transition/view_transition_supplement.cc b/third_party/blink/renderer/core/view_transition/view_transition_supplement.cc index 13401594..fdbb68f 100644 --- a/third_party/blink/renderer/core/view_transition/view_transition_supplement.cc +++ b/third_party/blink/renderer/core/view_transition/view_transition_supplement.cc
@@ -20,7 +20,7 @@ #include "third_party/blink/renderer/core/view_transition/view_transition_utils.h" #include "third_party/blink/renderer/platform/bindings/exception_state.h" #include "third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.h" -#include "third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h" +#include "third_party/blink/renderer/platform/scheduler/public/task_attribution_tracker.h" namespace blink { namespace { @@ -93,15 +93,15 @@ const std::optional<Vector<String>>& types, ExceptionState& exception_state) { DCHECK(script_state); - DCHECK(ThreadScheduler::Current()); auto* supplement = From(document); if (callback) { - auto* tracker = ThreadScheduler::Current()->GetTaskAttributionTracker(); + auto* tracker = + scheduler::TaskAttributionTracker::From(script_state->GetIsolate()); // Set the parent task ID if we're not in an extension task (as extensions // are not currently supported in TaskAttributionTracker). if (tracker && script_state->World().IsMainWorld()) { - callback->SetParentTask(tracker->RunningTask(script_state->GetIsolate())); + callback->SetParentTask(tracker->RunningTask()); } } return supplement->StartTransition(document, callback, types,
diff --git a/third_party/blink/renderer/core/xmlhttprequest/xml_http_request.cc b/third_party/blink/renderer/core/xmlhttprequest/xml_http_request.cc index e8984d6c..2ceec6a 100644 --- a/third_party/blink/renderer/core/xmlhttprequest/xml_http_request.cc +++ b/third_party/blink/renderer/core/xmlhttprequest/xml_http_request.cc
@@ -98,7 +98,6 @@ #include "third_party/blink/renderer/platform/network/parsed_content_type.h" #include "third_party/blink/renderer/platform/runtime_enabled_features.h" #include "third_party/blink/renderer/platform/scheduler/public/task_attribution_tracker.h" -#include "third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h" #include "third_party/blink/renderer/platform/weborigin/scheme_registry.h" #include "third_party/blink/renderer/platform/weborigin/security_origin.h" #include "third_party/blink/renderer/platform/weborigin/security_policy.h" @@ -1063,9 +1062,9 @@ if (async_) { CHECK(!execution_context.IsContextDestroyed()); if (world_ && world_->IsMainWorld()) { - if (auto* tracker = - ThreadScheduler::Current()->GetTaskAttributionTracker()) { - parent_task_ = tracker->RunningTask(execution_context.GetIsolate()); + if (auto* tracker = scheduler::TaskAttributionTracker::From( + execution_context.GetIsolate())) { + parent_task_ = tracker->RunningTask(); } } async_task_context_.Schedule(&execution_context, "XMLHttpRequest.send"); @@ -2133,20 +2132,20 @@ GetExecutionContext()->IsContextDestroyed()) { return nullptr; } - auto* tracker = ThreadScheduler::Current()->GetTaskAttributionTracker(); - if (!tracker) { - return nullptr; - } - // `parent_task_` being non-null implies this object is associated with - // the main world. + // `parent_task_` being non-null implies that task tracking is enabled and + // this object is associated with the main world. auto* script_state = ToScriptStateForMainWorld(GetExecutionContext()); CHECK(script_state); + auto* tracker = + scheduler::TaskAttributionTracker::From(script_state->GetIsolate()); + CHECK(tracker); + // Don't create a new (nested) task scope if we're still in the parent task, // otherwise we risk clobbering other propagated task state. // // TODO(crbug.com/1439971): Make this safe to do or move the logic into the // task attribution implementation. - if (tracker->RunningTask(script_state->GetIsolate()) == parent_task_.Get()) { + if (tracker->RunningTask() == parent_task_.Get()) { return nullptr; } return tracker->CreateTaskScope(
diff --git a/third_party/blink/renderer/modules/accessibility/ax_relation_cache.cc b/third_party/blink/renderer/modules/accessibility/ax_relation_cache.cc index a96ed050..b67f0f1c 100644 --- a/third_party/blink/renderer/modules/accessibility/ax_relation_cache.cc +++ b/third_party/blink/renderer/modules/accessibility/ax_relation_cache.cc
@@ -553,7 +553,7 @@ // - AccessibilityEventsSubtreeReparentedViaAriaOwns2/linux // TODO(crbug.com/1299031) Find out why this is necessary. object_cache_->MarkAXObjectDirtyWithCleanLayout( - original_parent->ParentObject()); + original_parent->CachedParentObject()); } // Now that we're replacing the parent, we need to update cached values // for the added child's subtree, because some cached values are inherited
diff --git a/third_party/blink/renderer/modules/modules_initializer.cc b/third_party/blink/renderer/modules/modules_initializer.cc index 8f0484f3..56f2c2f 100644 --- a/third_party/blink/renderer/modules/modules_initializer.cc +++ b/third_party/blink/renderer/modules/modules_initializer.cc
@@ -236,8 +236,8 @@ OffscreenCanvas::RegisterRenderingContextFactory( std::make_unique<GPUCanvasContext::Factory>()); - ThreadScheduler::Current()->InitializeTaskAttributionTracker( - std::make_unique<scheduler::TaskAttributionTrackerImpl>()); + V8PerIsolateData::SetTaskAttributionTrackerFactory( + &scheduler::TaskAttributionTrackerImpl::Create); } void ModulesInitializer::InitLocalFrame(LocalFrame& frame) const {
diff --git a/third_party/blink/renderer/modules/scheduler/dom_scheduler.cc b/third_party/blink/renderer/modules/scheduler/dom_scheduler.cc index 66a49d1..65966cf2 100644 --- a/third_party/blink/renderer/modules/scheduler/dom_scheduler.cc +++ b/third_party/blink/renderer/modules/scheduler/dom_scheduler.cc
@@ -23,7 +23,6 @@ #include "third_party/blink/renderer/platform/runtime_enabled_features.h" #include "third_party/blink/renderer/platform/scheduler/public/frame_or_worker_scheduler.h" #include "third_party/blink/renderer/platform/scheduler/public/task_attribution_tracker.h" -#include "third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h" #include "third_party/blink/renderer/platform/scheduler/public/web_scheduling_priority.h" #include "third_party/blink/renderer/platform/scheduler/public/web_scheduling_queue_type.h" #include "third_party/blink/renderer/platform/scheduler/public/web_scheduling_task_queue.h" @@ -168,16 +167,13 @@ scheduler::TaskAttributionIdType DOMScheduler::taskId( ScriptState* script_state) { - ThreadScheduler* scheduler = ThreadScheduler::Current(); - DCHECK(scheduler); - auto* tracker = scheduler->GetTaskAttributionTracker(); + auto* tracker = + scheduler::TaskAttributionTracker::From(script_state->GetIsolate()); if (!tracker) { // Can happen when a feature flag disables TaskAttribution. return 0; } - scheduler::TaskAttributionInfo* task = - scheduler->GetTaskAttributionTracker()->RunningTask( - script_state->GetIsolate()); + scheduler::TaskAttributionInfo* task = tracker->RunningTask(); // task cannot be nullptr here, as a task has presumably already ran in order // for this API call to be called. DCHECK(task); @@ -187,15 +183,13 @@ AtomicString DOMScheduler::isAncestor( ScriptState* script_state, scheduler::TaskAttributionIdType parent_id) { - ThreadScheduler* scheduler = ThreadScheduler::Current(); - DCHECK(scheduler); - auto* tracker = scheduler->GetTaskAttributionTracker(); + auto* tracker = + scheduler::TaskAttributionTracker::From(script_state->GetIsolate()); if (!tracker) { // Can happen when a feature flag disables TaskAttribution. return AtomicString("unknown"); } - const scheduler::TaskAttributionInfo* current_task = - tracker->RunningTask(script_state->GetIsolate()); + const scheduler::TaskAttributionInfo* current_task = tracker->RunningTask(); return current_task && tracker->IsAncestor(*current_task, scheduler::TaskAttributionId(parent_id))
diff --git a/third_party/blink/renderer/modules/scheduler/dom_task.cc b/third_party/blink/renderer/modules/scheduler/dom_task.cc index b64af493..846eb32 100644 --- a/third_party/blink/renderer/modules/scheduler/dom_task.cc +++ b/third_party/blink/renderer/modules/scheduler/dom_task.cc
@@ -26,7 +26,6 @@ #include "third_party/blink/renderer/platform/bindings/script_state.h" #include "third_party/blink/renderer/platform/runtime_enabled_features.h" #include "third_party/blink/renderer/platform/scheduler/public/task_attribution_tracker.h" -#include "third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h" #include "third_party/blink/renderer/platform/scheduler/public/web_scheduling_priority.h" #include "third_party/blink/renderer/platform/scheduler/public/web_scheduling_task_queue.h" #include "third_party/blink/renderer/platform/wtf/casting.h" @@ -110,9 +109,9 @@ DCHECK(script_state && script_state->ContextIsValid()); if (script_state->World().IsMainWorld()) { - if (auto* tracker = - ThreadScheduler::Current()->GetTaskAttributionTracker()) { - parent_task_ = tracker->RunningTask(script_state->GetIsolate()); + if (auto* tracker = scheduler::TaskAttributionTracker::From( + script_state->GetIsolate())) { + parent_task_ = tracker->RunningTask(); } } @@ -190,7 +189,8 @@ // For the main thread (tracker exists), create the task scope with the signal // to set up propagation. On workers, set the current context here since there // is no tracker. - if (auto* tracker = ThreadScheduler::Current()->GetTaskAttributionTracker()) { + if (auto* tracker = + scheduler::TaskAttributionTracker::From(script_state->GetIsolate())) { task_attribution_scope = tracker->CreateTaskScope( script_state, parent_task_, scheduler::TaskAttributionTracker::TaskScopeType::kSchedulerPostTask,
diff --git a/third_party/blink/renderer/modules/scheduler/scheduled_action.cc b/third_party/blink/renderer/modules/scheduler/scheduled_action.cc index 7573bea2..3955cd6f 100644 --- a/third_party/blink/renderer/modules/scheduler/scheduled_action.cc +++ b/third_party/blink/renderer/modules/scheduler/scheduled_action.cc
@@ -45,7 +45,7 @@ #include "third_party/blink/renderer/platform/bindings/script_state.h" #include "third_party/blink/renderer/platform/heap/garbage_collected.h" #include "third_party/blink/renderer/platform/instrumentation/use_counter.h" -#include "third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h" +#include "third_party/blink/renderer/platform/scheduler/public/task_attribution_tracker.h" #include "third_party/blink/renderer/platform/wtf/casting.h" namespace blink { @@ -62,10 +62,10 @@ To<LocalDOMWindow>(&target))) { function_ = handler; arguments_ = arguments; - auto* tracker = ThreadScheduler::Current()->GetTaskAttributionTracker(); + auto* tracker = + scheduler::TaskAttributionTracker::From(script_state->GetIsolate()); if (tracker && script_state->World().IsMainWorld()) { - function_->SetParentTask( - tracker->RunningTask(script_state->GetIsolate())); + function_->SetParentTask(tracker->RunningTask()); } } else { UseCounter::Count(target, WebFeature::kScheduledActionIgnored); @@ -82,9 +82,10 @@ EnteredDOMWindow(script_state->GetIsolate()), To<LocalDOMWindow>(&target))) { code_ = handler; - auto* tracker = ThreadScheduler::Current()->GetTaskAttributionTracker(); + auto* tracker = + scheduler::TaskAttributionTracker::From(script_state->GetIsolate()); if (tracker && script_state->World().IsMainWorld()) { - code_parent_task_ = tracker->RunningTask(script_state->GetIsolate()); + code_parent_task_ = tracker->RunningTask(); } } else { UseCounter::Count(target, WebFeature::kScheduledActionIgnored); @@ -157,7 +158,8 @@ // APIs properly track their ancestor as the registering task. std::unique_ptr<scheduler::TaskAttributionTracker::TaskScope> task_attribution_scope; - auto* tracker = ThreadScheduler::Current()->GetTaskAttributionTracker(); + auto* tracker = + scheduler::TaskAttributionTracker::From(script_state->GetIsolate()); if (tracker && script_state->World().IsMainWorld()) { task_attribution_scope = tracker->CreateTaskScope( script_state, code_parent_task_,
diff --git a/third_party/blink/renderer/modules/scheduler/task_attribution_tracker_impl.cc b/third_party/blink/renderer/modules/scheduler/task_attribution_tracker_impl.cc index 2598263..94fccad 100644 --- a/third_party/blink/renderer/modules/scheduler/task_attribution_tracker_impl.cc +++ b/third_party/blink/renderer/modules/scheduler/task_attribution_tracker_impl.cc
@@ -7,6 +7,7 @@ #include <memory> #include <utility> +#include "base/memory/ptr_util.h" #include "base/trace_event/trace_event.h" #include "base/trace_event/typed_macros.h" #include "third_party/blink/renderer/core/execution_context/execution_context.h" @@ -50,12 +51,20 @@ } // namespace -TaskAttributionTrackerImpl::TaskAttributionTrackerImpl() : next_task_id_(0) {} +// static +std::unique_ptr<TaskAttributionTracker> TaskAttributionTrackerImpl::Create( + v8::Isolate* isolate) { + return base::WrapUnique(new TaskAttributionTrackerImpl(isolate)); +} -TaskAttributionInfo* TaskAttributionTrackerImpl::RunningTask( - v8::Isolate* isolate) const { +TaskAttributionTrackerImpl::TaskAttributionTrackerImpl(v8::Isolate* isolate) + : next_task_id_(0), isolate_(isolate) { + CHECK(isolate_); +} + +TaskAttributionInfo* TaskAttributionTrackerImpl::RunningTask() const { ScriptWrappableTaskState* task_state = - ScriptWrappableTaskState::GetCurrent(isolate); + ScriptWrappableTaskState::GetCurrent(isolate_); // V8 embedder state may have no value in the case of a JSPromise that wasn't // yet resolved. @@ -103,9 +112,11 @@ TaskScopeType type, AbortSignal* abort_source, DOMTaskSignal* priority_source) { + CHECK(script_state); + CHECK_EQ(script_state->GetIsolate(), isolate_); TaskAttributionInfo* running_task_to_be_restored = running_task_; ScriptWrappableTaskState* continuation_task_state_to_be_restored = - ScriptWrappableTaskState::GetCurrent(script_state->GetIsolate()); + ScriptWrappableTaskState::GetCurrent(isolate_); // This compresses the task graph when encountering long task chains. // TODO(crbug.com/1501999): Consider compressing the task graph further.
diff --git a/third_party/blink/renderer/modules/scheduler/task_attribution_tracker_impl.h b/third_party/blink/renderer/modules/scheduler/task_attribution_tracker_impl.h index 23d8756b..a5c7845 100644 --- a/third_party/blink/renderer/modules/scheduler/task_attribution_tracker_impl.h +++ b/third_party/blink/renderer/modules/scheduler/task_attribution_tracker_impl.h
@@ -36,9 +36,9 @@ class MODULES_EXPORT TaskAttributionTrackerImpl : public TaskAttributionTracker { public: - TaskAttributionTrackerImpl(); + static std::unique_ptr<TaskAttributionTracker> Create(v8::Isolate*); - TaskAttributionInfo* RunningTask(v8::Isolate*) const override; + TaskAttributionInfo* RunningTask() const override; bool IsAncestor(const TaskAttributionInfo& task, TaskAttributionId ancestor_id) override; @@ -113,6 +113,8 @@ Persistent<ScriptState> script_state_; }; + explicit TaskAttributionTrackerImpl(v8::Isolate*); + void TaskScopeCompleted(const TaskScopeImpl&); void OnObserverScopeDestroyed(const ObserverScope&) override; @@ -125,6 +127,9 @@ // here to ensure the relevant object remains alive (and hence properly // tracked through task attribution). WTF::Deque<Persistent<TaskAttributionInfo>> same_document_navigation_tasks_; + + // The lifetime of this class is tied to the `isolate_`. + raw_ptr<v8::Isolate> isolate_; }; } // namespace blink::scheduler
diff --git a/third_party/blink/renderer/modules/scheduler/window_idle_tasks.cc b/third_party/blink/renderer/modules/scheduler/window_idle_tasks.cc index 856b9a8..3695271 100644 --- a/third_party/blink/renderer/modules/scheduler/window_idle_tasks.cc +++ b/third_party/blink/renderer/modules/scheduler/window_idle_tasks.cc
@@ -19,7 +19,6 @@ #include "third_party/blink/renderer/platform/runtime_enabled_features.h" #include "third_party/blink/renderer/platform/scheduler/public/task_attribution_info.h" #include "third_party/blink/renderer/platform/scheduler/public/task_attribution_tracker.h" -#include "third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h" #include "third_party/blink/renderer/platform/scheduler/public/web_scheduling_priority.h" namespace blink { @@ -36,9 +35,10 @@ explicit V8IdleTask(V8IdleRequestCallback* callback) : callback_(callback) { ScriptState* script_state = callback_->CallbackRelevantScriptState(); - auto* tracker = ThreadScheduler::Current()->GetTaskAttributionTracker(); + auto* tracker = + scheduler::TaskAttributionTracker::From(script_state->GetIsolate()); if (tracker && script_state->World().IsMainWorld()) { - parent_task_ = tracker->RunningTask(script_state->GetIsolate()); + parent_task_ = tracker->RunningTask(); } } @@ -48,8 +48,8 @@ ScriptState* script_state = callback_->CallbackRelevantScriptState(); std::unique_ptr<scheduler::TaskAttributionTracker::TaskScope> task_attribution_scope; - if (auto* tracker = - ThreadScheduler::Current()->GetTaskAttributionTracker()) { + if (auto* tracker = scheduler::TaskAttributionTracker::From( + script_state->GetIsolate())) { DOMTaskSignal* signal = nullptr; if (RuntimeEnabledFeatures::SchedulerYieldEnabled( ExecutionContext::From(script_state))) {
diff --git a/third_party/blink/renderer/modules/webrtc/webrtc_audio_renderer.cc b/third_party/blink/renderer/modules/webrtc/webrtc_audio_renderer.cc index f4faf012..e4ce5d6 100644 --- a/third_party/blink/renderer/modules/webrtc/webrtc_audio_renderer.cc +++ b/third_party/blink/renderer/modules/webrtc/webrtc_audio_renderer.cc
@@ -5,9 +5,9 @@ #include "third_party/blink/renderer/modules/webrtc/webrtc_audio_renderer.h" #include <utility> +#include <vector> #include "base/containers/contains.h" -#include "base/containers/cxx20_erase.h" #include "base/location.h" #include "base/logging.h" #include "base/metrics/histogram_macros.h" @@ -788,7 +788,7 @@ it != source_playing_states_.end();) { PlayingStates& states = it->second; // We cannot use RemovePlayingState as it might invalidate |it|. - base::Erase(states, state); + std::erase(states, state); if (states.empty()) it = source_playing_states_.erase(it); else
diff --git a/third_party/blink/renderer/platform/bindings/v8_per_isolate_data.cc b/third_party/blink/renderer/platform/bindings/v8_per_isolate_data.cc index bbf76f9..5384c97 100644 --- a/third_party/blink/renderer/platform/bindings/v8_per_isolate_data.cc +++ b/third_party/blink/renderer/platform/bindings/v8_per_isolate_data.cc
@@ -30,6 +30,7 @@ #include "base/allocator/partition_allocator/src/partition_alloc/oom.h" #include "base/debug/crash_logging.h" +#include "base/feature_list.h" #include "base/metrics/histogram_macros.h" #include "base/ranges/algorithm.h" #include "base/task/single_thread_task_runner.h" @@ -50,12 +51,18 @@ #include "third_party/blink/renderer/platform/bindings/v8_value_cache.h" #include "third_party/blink/renderer/platform/heap/garbage_collected.h" #include "third_party/blink/renderer/platform/heap/thread_state_scopes.h" +#include "third_party/blink/renderer/platform/scheduler/public/task_attribution_tracker.h" #include "third_party/blink/renderer/platform/scheduler/public/thread.h" #include "third_party/blink/renderer/platform/wtf/leak_annotations.h" namespace blink { +BASE_FEATURE(kTaskAttributionInfrastructureDisabledForTesting, + "TaskAttributionInfrastructureDisabledForTesting", + base::FEATURE_DISABLED_BY_DEFAULT); + namespace { + void AddCrashKey(v8::CrashKeyId id, const std::string& value) { using base::debug::AllocateCrashKeyString; using base::debug::CrashKeySize; @@ -93,6 +100,10 @@ break; } } + +V8PerIsolateData::TaskAttributionTrackerFactoryPtr + task_attribution_tracker_factory = nullptr; + } // namespace static void BeforeCallEnteredCallback(v8::Isolate* isolate) { @@ -146,6 +157,12 @@ main_world_ = DOMWrapperWorld::Create(GetIsolate(), DOMWrapperWorld::WorldType::kMain, /*is_default_world_of_isolate=*/true); + if (!base::FeatureList::IsEnabled( + kTaskAttributionInfrastructureDisabledForTesting)) { + CHECK(task_attribution_tracker_factory); + task_attribution_tracker_ = + task_attribution_tracker_factory(GetIsolate()); + } } } @@ -406,6 +423,13 @@ return password_regexp_; } +void V8PerIsolateData::SetTaskAttributionTrackerFactory( + TaskAttributionTrackerFactoryPtr factory) { + CHECK(!task_attribution_tracker_factory); + CHECK(IsMainThread()); + task_attribution_tracker_factory = factory; +} + void* CreateHistogram(const char* name, int min, int max, size_t buckets) { // Each histogram has an implicit '0' bucket (for underflow), so we can always // bump the minimum to 1.
diff --git a/third_party/blink/renderer/platform/bindings/v8_per_isolate_data.h b/third_party/blink/renderer/platform/bindings/v8_per_isolate_data.h index 512cd71f..cefd4c68 100644 --- a/third_party/blink/renderer/platform/bindings/v8_per_isolate_data.h +++ b/third_party/blink/renderer/platform/bindings/v8_per_isolate_data.h
@@ -50,6 +50,10 @@ class SingleThreadTaskRunner; } // namespace base +namespace blink::scheduler { +class TaskAttributionTracker; +} // namespace blink::scheduler + namespace blink { class DOMWrapperWorld; @@ -218,6 +222,21 @@ void LeaveGC() { gc_callback_depth_--; } + // Set the factory function used to initialize task attribution for the + // isolate upon creating main thread `V8PerIsolateData`. This should be set + // once per process before creating any isolates. + using TaskAttributionTrackerFactoryPtr = + std::unique_ptr<scheduler::TaskAttributionTracker> (*)(v8::Isolate*); + static void SetTaskAttributionTrackerFactory( + TaskAttributionTrackerFactoryPtr factory); + + // Returns the `scheduler::TaskAttributionTracker` associated with the + // associated `v8::Isolate`. Returns null if the + // TaskAttributionInfrastructureDisabledForTesting feature is enabled. + scheduler::TaskAttributionTracker* GetTaskAttributionTracker() { + return task_attribution_tracker_.get(); + } + private: V8PerIsolateData(scoped_refptr<base::SingleThreadTaskRunner>, scoped_refptr<base::SingleThreadTaskRunner>, @@ -292,6 +311,8 @@ size_t gc_callback_depth_ = 0; Persistent<DOMWrapperWorld> main_world_; + + std::unique_ptr<scheduler::TaskAttributionTracker> task_attribution_tracker_; }; // Creates a histogram for V8. The returned value is a base::Histogram, but
diff --git a/third_party/blink/renderer/platform/geometry/calculation_expression_node.cc b/third_party/blink/renderer/platform/geometry/calculation_expression_node.cc index e171b7b..fd7e475e 100644 --- a/third_party/blink/renderer/platform/geometry/calculation_expression_node.cc +++ b/third_party/blink/renderer/platform/geometry/calculation_expression_node.cc
@@ -367,9 +367,22 @@ result_type_ = ResolvedResultType(); DCHECK_NE(result_type_, ResultType::kInvalid); #endif - for (const auto& child : children_) { - if (child->HasContentOrIntrinsicSize()) { - has_content_or_intrinsic_ = true; + if (op == CalculationOperator::kCalcSize) { + // "A calc-size() is treated, in all respects, as if it were its + // calc-size basis." This is particularly relevant for ignoring the + // presence of percentages in the calculation. + DCHECK_EQ(children_.size(), 2u); + const auto& basis = children_[0]; + has_content_or_intrinsic_ = basis->HasContentOrIntrinsicSize(); + has_percent_ = basis->HasPercent(); + } else { + for (const auto& child : children_) { + if (child->HasContentOrIntrinsicSize()) { + has_content_or_intrinsic_ = true; + } + if (child->HasPercent()) { + has_percent_ = true; + } } } }
diff --git a/third_party/blink/renderer/platform/geometry/calculation_expression_node.h b/third_party/blink/renderer/platform/geometry/calculation_expression_node.h index c61996c..33488be 100644 --- a/third_party/blink/renderer/platform/geometry/calculation_expression_node.h +++ b/third_party/blink/renderer/platform/geometry/calculation_expression_node.h
@@ -51,6 +51,11 @@ } bool HasContentOrIntrinsicSize() const { return has_content_or_intrinsic_; } + // HasPercent returns whether this node's value expression should be + // treated as having a percent. Note that this means that percentages + // inside of the calculation part of a calc-size() do not make the + // calc-size() act as though it has a percent. + bool HasPercent() const { return has_percent_; } virtual bool IsNumber() const { return false; } virtual bool IsIdentifier() const { return false; } @@ -77,6 +82,7 @@ virtual bool Equals(const CalculationExpressionNode& other) const = 0; bool has_content_or_intrinsic_ = false; + bool has_percent_ = false; }; class PLATFORM_EXPORT CalculationExpressionNumberNode final @@ -218,6 +224,9 @@ #if DCHECK_IS_ON() result_type_ = ResultType::kPixelsAndPercent; #endif + if (value.has_explicit_percent) { + has_percent_ = true; + } } float Pixels() const { return value_.pixels; }
diff --git a/third_party/blink/renderer/platform/geometry/calculation_value.cc b/third_party/blink/renderer/platform/geometry/calculation_value.cc index 5214265..c37c0d80 100644 --- a/third_party/blink/renderer/platform/geometry/calculation_value.cc +++ b/third_party/blink/renderer/platform/geometry/calculation_value.cc
@@ -144,4 +144,11 @@ return IsExpression() && data_.expression->HasContentOrIntrinsicSize(); } +bool CalculationValue::HasPercent() const { + if (!IsExpression()) { + return HasExplicitPercent(); + } + return data_.expression->HasPercent(); +} + } // namespace blink
diff --git a/third_party/blink/renderer/platform/geometry/calculation_value.h b/third_party/blink/renderer/platform/geometry/calculation_value.h index bcb51cbc..4884411a 100644 --- a/third_party/blink/renderer/platform/geometry/calculation_value.h +++ b/third_party/blink/renderer/platform/geometry/calculation_value.h
@@ -68,6 +68,7 @@ : Length::ValueRange::kAll; } bool HasContentOrIntrinsicSize() const; + bool HasPercent() const; float Pixels() const { DCHECK(!IsExpression());
diff --git a/third_party/blink/renderer/platform/geometry/length.cc b/third_party/blink/renderer/platform/geometry/length.cc index 44a33af..f67d3a5 100644 --- a/third_party/blink/renderer/platform/geometry/length.cc +++ b/third_party/blink/renderer/platform/geometry/length.cc
@@ -203,7 +203,7 @@ return result; } -bool Length::IsContentOrIntrinsic() const { +bool Length::HasContentOrIntrinsic() const { if (GetType() == kCalculated) { return GetCalculationValue().HasContentOrIntrinsicSize(); } @@ -212,6 +212,13 @@ GetType() == kContent; } +bool Length::HasPercent() const { + if (GetType() == kCalculated) { + return GetCalculationValue().HasPercent(); + } + return GetType() == kPercent; +} + bool Length::IsCalculatedEqual(const Length& o) const { return IsCalculated() && (&GetCalculationValue() == &o.GetCalculationValue() ||
diff --git a/third_party/blink/renderer/platform/geometry/length.h b/third_party/blink/renderer/platform/geometry/length.h index 54cc6fd..2f9b344 100644 --- a/third_party/blink/renderer/platform/geometry/length.h +++ b/third_party/blink/renderer/platform/geometry/length.h
@@ -261,19 +261,20 @@ } // For the layout purposes, if this |Length| is a block-axis size, see - // |IsAutoOrContentOrIntrinsic()|, it is usually a better choice. + // |HasAutoOrContentOrIntrinsic()|, it is usually a better choice. bool IsAuto() const { return GetType() == kAuto; } bool IsFixed() const { return GetType() == kFixed; } // For the block axis, intrinsic sizes such as `min-content` behave the same // as `auto`. https://www.w3.org/TR/css-sizing-3/#valdef-width-min-content // This includes content-based sizes in calc-size(). - bool IsContentOrIntrinsic() const; - bool IsAutoOrContentOrIntrinsic() const { + bool HasContentOrIntrinsic() const; + bool HasAutoOrContentOrIntrinsic() const { // TODO(https://crbug.com/313072): Add support for 'auto' in 'calc-size()' // here. - return GetType() == kAuto || IsContentOrIntrinsic(); + return GetType() == kAuto || HasContentOrIntrinsic(); } + bool HasPercent() const; bool IsSpecified() const { return GetType() == kFixed || GetType() == kPercent || @@ -290,9 +291,14 @@ bool IsFitContent() const { return GetType() == kFitContent; } bool IsPercent() const { return GetType() == kPercent; } bool IsPercentOrCalc() const { + // TODO(https://crbug.com/313072): Not all calc()s have percentages; + // many callers may want HasPercent, above. return GetType() == kPercent || GetType() == kCalculated; } bool IsPercentOrCalcOrStretch() const { + // TODO(https://crbug.com/313072): Not all calc()s have percentages; + // many callers may want a function like HasPercent, above (but that + // doesn't exist yet). return GetType() == kPercent || GetType() == kCalculated || GetType() == kFillAvailable; } @@ -374,7 +380,7 @@ // (e.g., no targets or wrong axis.), in which case the fallback should // be used. virtual std::optional<LayoutUnit> Evaluate( - const CalculationExpressionNode&) const = 0; + const CalculationExpressionNode&) = 0; protected: Mode GetMode() const { return mode_; } @@ -420,7 +426,7 @@ STACK_ALLOCATED(); public: - const Length::AnchorEvaluator* anchor_evaluator = nullptr; + Length::AnchorEvaluator* anchor_evaluator = nullptr; std::optional<float> size_keyword_basis = std::nullopt; std::optional<IntrinsicLengthEvaluator> intrinsic_evaluator = std::nullopt; };
diff --git a/third_party/blink/renderer/platform/graphics/memory_managed_paint_canvas.cc b/third_party/blink/renderer/platform/graphics/memory_managed_paint_canvas.cc index d46f010..73f52c7 100644 --- a/third_party/blink/renderer/platform/graphics/memory_managed_paint_canvas.cc +++ b/third_party/blink/renderer/platform/graphics/memory_managed_paint_canvas.cc
@@ -4,13 +4,27 @@ #include "third_party/blink/renderer/platform/graphics/memory_managed_paint_canvas.h" +#include "base/memory/ptr_util.h" + namespace blink { MemoryManagedPaintCanvas::MemoryManagedPaintCanvas(const gfx::Size& size) : cc::InspectableRecordPaintCanvas(size) {} +MemoryManagedPaintCanvas::MemoryManagedPaintCanvas( + CreateChildCanvasTag, + const MemoryManagedPaintCanvas& parent) + : cc::InspectableRecordPaintCanvas(CreateChildCanvasTag(), parent) {} + MemoryManagedPaintCanvas::~MemoryManagedPaintCanvas() = default; +std::unique_ptr<MemoryManagedPaintCanvas> +MemoryManagedPaintCanvas::CreateChildCanvas() { + // Using `new` to access a non-public constructor. + return base::WrapUnique( + new MemoryManagedPaintCanvas(CreateChildCanvasTag(), *this)); +} + cc::PaintRecord MemoryManagedPaintCanvas::ReleaseAsRecord() { cached_image_ids_.clear(); image_bytes_used_ = 0;
diff --git a/third_party/blink/renderer/platform/graphics/memory_managed_paint_canvas.h b/third_party/blink/renderer/platform/graphics/memory_managed_paint_canvas.h index 5a8741e..653c49ed 100644 --- a/third_party/blink/renderer/platform/graphics/memory_managed_paint_canvas.h +++ b/third_party/blink/renderer/platform/graphics/memory_managed_paint_canvas.h
@@ -26,6 +26,8 @@ explicit MemoryManagedPaintCanvas(const cc::RecordPaintCanvas&) = delete; ~MemoryManagedPaintCanvas() override; + std::unique_ptr<MemoryManagedPaintCanvas> CreateChildCanvas(); + cc::PaintRecord ReleaseAsRecord() override; void drawImage(const cc::PaintImage& image, @@ -44,6 +46,12 @@ size_t ImageBytesUsed() const { return image_bytes_used_; } private: + // Creates a child canvas that has the same transform matrix and size as + // `parent`. `CreateChildCanvasTag` is used to differentiate this from a copy + // constructor. + MemoryManagedPaintCanvas(CreateChildCanvasTag, + const MemoryManagedPaintCanvas& parent); + void UpdateMemoryUsage(const cc::PaintImage& image); HashSet<cc::PaintImage::ContentId,
diff --git a/third_party/blink/renderer/platform/graphics/memory_managed_paint_recorder.cc b/third_party/blink/renderer/platform/graphics/memory_managed_paint_recorder.cc index 5fe7dcd8..d726a8ee0 100644 --- a/third_party/blink/renderer/platform/graphics/memory_managed_paint_recorder.cc +++ b/third_party/blink/renderer/platform/graphics/memory_managed_paint_recorder.cc
@@ -85,7 +85,7 @@ void MemoryManagedPaintRecorder::BeginSideRecording() { CHECK(!side_canvas_) << "BeginSideRecording() can't be called when side " "recording is already active."; - side_canvas_ = std::make_unique<MemoryManagedPaintCanvas>(size_); + side_canvas_ = main_canvas_.CreateChildCanvas(); current_canvas_ = side_canvas_.get(); }
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5 index 6ce6737..70c6b962 100644 --- a/third_party/blink/renderer/platform/runtime_enabled_features.json5 +++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -732,6 +732,7 @@ { // Resolve anchor*() functions computed-value time. name: "CSSAnchorPositioningComputeAnchor", + depends_on: ["CSSAnchorPositioningCascadeFallback"], }, { name: "CSSAnimationComposition", @@ -3034,7 +3035,7 @@ }, { name: "ReduceCookieIPCs", - status: "stable", + status: "test", }, { // If enabled, the deviceModel will be reduced to "K" and the
diff --git a/third_party/blink/renderer/platform/scheduler/DEPS b/third_party/blink/renderer/platform/scheduler/DEPS index a622633..9a82c31 100644 --- a/third_party/blink/renderer/platform/scheduler/DEPS +++ b/third_party/blink/renderer/platform/scheduler/DEPS
@@ -32,6 +32,7 @@ "+third_party/blink/renderer/platform/allow_discouraged_type.h", "+third_party/blink/renderer/platform/back_forward_cache_utils.h", "+third_party/blink/renderer/platform/bindings/parkable_string_manager.h", + "+third_party/blink/renderer/platform/bindings/v8_per_isolate_data.h", "+third_party/blink/renderer/platform/heap/collection_support/clear_collection_scope.h", "+third_party/blink/renderer/platform/heap/collection_support/heap_hash_set.h", "+third_party/blink/renderer/platform/heap/collection_support/heap_vector.h",
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.cc b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.cc index a1c8232..a380864 100644 --- a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.cc +++ b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.cc
@@ -41,6 +41,7 @@ #include "third_party/blink/public/platform/scheduler/web_agent_group_scheduler.h" #include "third_party/blink/public/platform/scheduler/web_renderer_process_type.h" #include "third_party/blink/public/platform/web_input_event_result.h" +#include "third_party/blink/renderer/platform/bindings/v8_per_isolate_data.h" #include "third_party/blink/renderer/platform/heap/collection_support/heap_vector.h" #include "third_party/blink/renderer/platform/instrumentation/resource_coordinator/renderer_resource_coordinator.h" #include "third_party/blink/renderer/platform/runtime_enabled_features.h" @@ -71,10 +72,6 @@ namespace blink { namespace scheduler { -BASE_FEATURE(kTaskAttributionInfrastructureDisabledForTesting, - "TaskAttributionInfrastructureDisabledForTesting", - base::FEATURE_DISABLED_BY_DEFAULT); - using base::sequence_manager::TaskQueue; using base::sequence_manager::TaskTimeObserver; using base::sequence_manager::TimeDomain; @@ -2852,19 +2849,6 @@ return true; } -TaskAttributionTracker* MainThreadSchedulerImpl::GetTaskAttributionTracker() { - return base::FeatureList::IsEnabled( - kTaskAttributionInfrastructureDisabledForTesting) - ? nullptr - : main_thread_only().task_attribution_tracker.get(); -} - -void MainThreadSchedulerImpl::InitializeTaskAttributionTracker( - std::unique_ptr<TaskAttributionTracker> tracker) { - DCHECK(!main_thread_only().task_attribution_tracker); - main_thread_only().task_attribution_tracker = std::move(tracker); -} - // static const char* MainThreadSchedulerImpl::UseCaseToString(UseCase use_case) { switch (use_case) {
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h index 8677be7..c17e65b 100644 --- a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h +++ b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h
@@ -52,7 +52,6 @@ #include "third_party/blink/renderer/platform/scheduler/public/frame_scheduler.h" #include "third_party/blink/renderer/platform/scheduler/public/main_thread_scheduler.h" #include "third_party/blink/renderer/platform/scheduler/public/rail_mode_observer.h" -#include "third_party/blink/renderer/platform/scheduler/public/task_attribution_tracker.h" #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h" #include "third_party/blink/renderer/platform/wtf/hash_set.h" #include "third_party/blink/renderer/platform/wtf/vector.h" @@ -211,9 +210,6 @@ void AddTaskObserver(base::TaskObserver* task_observer) override; void RemoveTaskObserver(base::TaskObserver* task_observer) override; void SetV8Isolate(v8::Isolate* isolate) override; - TaskAttributionTracker* GetTaskAttributionTracker() override; - void InitializeTaskAttributionTracker( - std::unique_ptr<TaskAttributionTracker> tracker) override; blink::MainThreadScheduler* ToMainThreadScheduler() override; // ThreadSchedulerBase implementation: @@ -801,7 +797,6 @@ WTF::Vector<AgentGroupSchedulerScope> agent_group_scheduler_scope_stack; - std::unique_ptr<TaskAttributionTracker> task_attribution_tracker; Persistent<HeapHashSet<WeakMember<AgentGroupSchedulerImpl>>> agent_group_schedulers; // Task queues that have been detached from their scheduler and may have
diff --git a/third_party/blink/renderer/platform/scheduler/public/task_attribution_tracker.h b/third_party/blink/renderer/platform/scheduler/public/task_attribution_tracker.h index 623bc9f..9ac6c1db 100644 --- a/third_party/blink/renderer/platform/scheduler/public/task_attribution_tracker.h +++ b/third_party/blink/renderer/platform/scheduler/public/task_attribution_tracker.h
@@ -11,6 +11,7 @@ #include "base/functional/function_ref.h" #include "base/memory/stack_allocated.h" #include "third_party/blink/public/common/scheduler/task_attribution_id.h" +#include "third_party/blink/renderer/platform/bindings/v8_per_isolate_data.h" #include "third_party/blink/renderer/platform/heap/garbage_collected.h" #include "third_party/blink/renderer/platform/platform_export.h" #include "third_party/blink/renderer/platform/wtf/hash_set.h" @@ -98,6 +99,10 @@ Observer* previous_observer_; }; + static TaskAttributionTracker* From(v8::Isolate* isolate) { + return V8PerIsolateData::From(isolate)->GetTaskAttributionTracker(); + } + virtual ~TaskAttributionTracker() = default; // Create a new task scope. @@ -114,7 +119,7 @@ DOMTaskSignal* priority_source) = 0; // Get the `TaskAttributionInfo` for the currently running task. - virtual TaskAttributionInfo* RunningTask(v8::Isolate*) const = 0; + virtual TaskAttributionInfo* RunningTask() const = 0; // Returns true iff `task` has an ancestor task with `ancestor_id`. virtual bool IsAncestor(const TaskAttributionInfo& task,
diff --git a/third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h b/third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h index e70ee0c..5f4ce082 100644 --- a/third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h +++ b/third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h
@@ -5,11 +5,9 @@ #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_PUBLIC_THREAD_SCHEDULER_H_ #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_PUBLIC_THREAD_SCHEDULER_H_ -#include <memory> #include "base/location.h" #include "base/task/single_thread_task_runner.h" #include "base/time/time.h" -#include "third_party/blink/renderer/platform/scheduler/public/task_attribution_tracker.h" #include "third_party/blink/renderer/platform/scheduler/public/thread.h" namespace v8 { @@ -96,19 +94,8 @@ // Associates |isolate| to the scheduler. virtual void SetV8Isolate(v8::Isolate* isolate) = 0; - // Returns the scheduler's TaskAttributionTracker is we're running on the main - // thread, or a nullptr otherwise. - virtual scheduler::TaskAttributionTracker* GetTaskAttributionTracker() { - return nullptr; - } - // Convert this into a MainThreadScheduler if it is one. virtual MainThreadScheduler* ToMainThreadScheduler() { return nullptr; } - - // Test helpers. - - virtual void InitializeTaskAttributionTracker( - std::unique_ptr<scheduler::TaskAttributionTracker>) {} }; } // namespace blink
diff --git a/third_party/blink/renderer/platform/testing/task_environment.cc b/third_party/blink/renderer/platform/testing/task_environment.cc index 3f238cd..c881800 100644 --- a/third_party/blink/renderer/platform/testing/task_environment.cc +++ b/third_party/blink/renderer/platform/testing/task_environment.cc
@@ -5,7 +5,6 @@ #include "third_party/blink/renderer/platform/testing/task_environment.h" #include "third_party/blink/public/platform/platform.h" -#include "third_party/blink/renderer/modules/scheduler/task_attribution_tracker_impl.h" #include "third_party/blink/renderer/platform/scheduler/public/main_thread_scheduler.h" #include "third_party/blink/renderer/platform/wtf/wtf.h" @@ -36,8 +35,6 @@ main_thread_isolate_.emplace(); main_thread_overrider_.emplace(scheduler_->CreateMainThread()); - ThreadScheduler::Current()->InitializeTaskAttributionTracker( - std::make_unique<scheduler::TaskAttributionTrackerImpl>()); } // static
diff --git a/third_party/blink/tools/blinkpy/common/system/filesystem_mock.py b/third_party/blink/tools/blinkpy/common/system/filesystem_mock.py index 4d7c68eae..982a5b1 100644 --- a/third_party/blink/tools/blinkpy/common/system/filesystem_mock.py +++ b/third_party/blink/tools/blinkpy/common/system/filesystem_mock.py
@@ -263,12 +263,15 @@ directories.append(directory) else: files.append(remaining) - file_system_tuples = [(top[:-1], directories, files)] + # The real `os.walk(...)` [0] gives the caller a chance to modify which + # subdirectories to traverse by mutating the `directories` list, so we + # should yield here instead of returning a precomputed list. + # + # [0]: https://docs.python.org/3/library/os.html#os.walk + yield (top[:-1], directories, files) for directory in directories: directory = top + directory - tuples_from_subdirs = self.walk(directory) - file_system_tuples += tuples_from_subdirs - return file_system_tuples + yield from self.walk(directory) def mtime(self, path): if self.exists(path):
diff --git a/third_party/blink/tools/blinkpy/common/system/filesystem_mock_unittest.py b/third_party/blink/tools/blinkpy/common/system/filesystem_mock_unittest.py index 346cfed..7409014 100644 --- a/third_party/blink/tools/blinkpy/common/system/filesystem_mock_unittest.py +++ b/third_party/blink/tools/blinkpy/common/system/filesystem_mock_unittest.py
@@ -113,9 +113,10 @@ mock_files = {'foo/bar/baz': '', 'foo/a': '', 'foo/b': '', 'foo/c': ''} host = MockHost() host.filesystem = MockFileSystem(files=mock_files) - self.assertEquals( - host.filesystem.walk(mock_dir), [('foo', ['bar'], ['a', 'b', 'c']), - ('foo/bar', [], ['baz'])]) + self.assertEquals(list(host.filesystem.walk(mock_dir)), [ + ('foo', ['bar'], ['a', 'b', 'c']), + ('foo/bar', [], ['baz']), + ]) def test_filesystem_walk_deeply_nested(self): mock_dir = 'foo' @@ -131,11 +132,12 @@ mock_files_ordered = OrderedDict(sorted(mock_files.items())) host = MockHost() host.filesystem = MockFileSystem(files=mock_files_ordered) - self.assertEquals(host.filesystem.walk(mock_dir), - [('foo', ['a', 'bar'], ['b', 'c']), - ('foo/a', ['z'], ['x', 'y']), - ('foo/a/z', [], ['lyrics']), - ('foo/bar', [], ['baz', 'quux'])]) + self.assertEquals(list(host.filesystem.walk(mock_dir)), [ + ('foo', ['a', 'bar'], ['b', 'c']), + ('foo/a', ['z'], ['x', 'y']), + ('foo/a/z', [], ['lyrics']), + ('foo/bar', [], ['baz', 'quux']), + ]) def test_relpath_win32(self): # This unit test inherits tests from GenericFileSystemTests, but
diff --git a/third_party/blink/tools/blinkpy/w3c/test_copier.py b/third_party/blink/tools/blinkpy/w3c/test_copier.py index 718f753..1562a862 100644 --- a/third_party/blink/tools/blinkpy/w3c/test_copier.py +++ b/third_party/blink/tools/blinkpy/w3c/test_copier.py
@@ -30,7 +30,9 @@ a local W3C repository source directory into a destination directory. """ +import collections import logging +from typing import Mapping, Sequence, Set, Tuple, TypedDict from blinkpy.w3c.common import is_basename_skipped from blinkpy.common import path_finder @@ -43,7 +45,12 @@ DEST_DIR_NAME = 'external' -class TestCopier(object): +class Copy(TypedDict): + src: str + dest: str + + +class TestCopier: def __init__(self, host, source_repo_path): """Initializes variables to prepare for copying and converting files. @@ -67,8 +74,6 @@ self.source_repo_path == self.destination_directory) self.dir_above_repo = self.filesystem.dirname(self.source_repo_path) - self.import_list = [] - # This is just a FYI list of CSS properties that still need to be prefixed, # which may be output after importing. self._prefixed_properties = {} @@ -76,17 +81,22 @@ def do_import(self): _log.info('Importing %s into %s', self.source_repo_path, self.destination_directory) - self.find_importable_tests() - self.import_tests() + copies_by_dir = self.find_importable_tests() + self.import_tests(copies_by_dir) - def find_importable_tests(self): - """Walks through the source directory to find what tests should be imported. + def find_importable_tests(self) -> Mapping[str, Sequence[Copy]]: + """Walks through the source directory to find what tests should be imported.""" + paths_to_skip, paths_to_import = self._read_import_filter() + copies_by_dir = collections.defaultdict(list) - This function sets self.import_list, which contains information about how many - tests are being imported, and their source and destination paths. - """ - paths_to_skip = self.find_paths_to_skip() - + # TODO(crbug.com/326646909): + # * Path construction here is much more complicated than it needs to + # be, and likely only works on Unix. + # * Use the simple test list format for import inclusions/exclusions + # (https://bit.ly/chromium-test-list-format) instead of the tagged + # test list. + # * Consider handling exclusions/inclusions in one loop going + # file-by-file. for root, dirs, files in self.filesystem.walk(self.source_repo_path): cur_dir = root.replace(self.dir_above_repo + '/', '') + '/' _log.debug('Scanning %s...', cur_dir) @@ -108,8 +118,6 @@ if self.import_in_place: self.filesystem.rmtree(path_full) - copy_list = [] - for filename in files: path_full = self.filesystem.join(root, filename) path_base = path_full.replace(self.source_repo_path + '/', '') @@ -131,17 +139,31 @@ ) continue - copy_list.append({'src': path_full, 'dest': filename}) - - if copy_list: - # Only add this directory to the list if there's something to import - self.import_list.append({ - 'dirname': root, - 'copy_list': copy_list + copies_by_dir[root].append({ + 'src': path_full, + 'dest': filename, }) - def find_paths_to_skip(self): - paths_to_skip = set() + for path in paths_to_import: + path_in_chromium = self.filesystem.join(self.web_tests_dir, path) + path_from_wpt = path_in_chromium.replace( + self.destination_directory + '/', '') + src = self.filesystem.join(self.source_repo_path, path_from_wpt) + if not self.filesystem.isfile(src): + _log.warning( + 'Only regular files can be explicitly allowlisted ' + f'currently. {src!r} is not; skipping.') + continue + copies_by_dir[self.filesystem.dirname(src)].append({ + 'src': + src, + 'dest': + self.filesystem.basename(src) + }) + return copies_by_dir + + def _read_import_filter(self) -> Tuple[Set[str], Set[str]]: + paths_to_skip, paths_to_import = set(), set() port = self.host.port_factory.get() w3c_import_expectations_path = self.path_finder.path_from_web_tests( 'W3CImportExpectations') @@ -150,40 +172,37 @@ expectations = TestExpectations( port, {w3c_import_expectations_path: w3c_import_expectations}) - # get test names that should be skipped for line in expectations.get_updated_lines( w3c_import_expectations_path): + if not line.test: # Comment lines + continue if line.is_glob: _log.warning( 'W3CImportExpectations:%d Globs are not allowed in this file.', line.lineno) continue + if line.tags: + _log.warning( + 'W3CImportExpectations:%d should not have any specifiers', + line.lineno) if ResultType.Skip in line.results: - if line.tags: - _log.warning( - 'W3CImportExpectations:%d should not have any specifiers', - line.lineno) paths_to_skip.add(line.test) + elif ResultType.Pass in line.results: + paths_to_import.add(line.test) - return paths_to_skip + return paths_to_skip, paths_to_import - def import_tests(self): - """Reads |self.import_list|, and converts and copies files to their destination.""" - for dir_to_copy in self.import_list: - if not dir_to_copy['copy_list']: - continue - - orig_path = dir_to_copy['dirname'] - - relative_dir = self.filesystem.relpath(orig_path, + def import_tests(self, copy_by_dir: Mapping[str, Sequence[Copy]]): + """Converts and copies files to their destination.""" + for src_dir, copy_list in copy_by_dir.items(): + assert copy_list, src_dir + relative_dir = self.filesystem.relpath(src_dir, self.source_repo_path) dest_dir = self.filesystem.join(self.destination_directory, relative_dir) - if not self.filesystem.exists(dest_dir): self.filesystem.maybe_make_directory(dest_dir) - - for file_to_copy in dir_to_copy['copy_list']: + for file_to_copy in copy_list: self.copy_file(file_to_copy, dest_dir) _log.info('')
diff --git a/third_party/blink/tools/blinkpy/w3c/test_copier_unittest.py b/third_party/blink/tools/blinkpy/w3c/test_copier_unittest.py index 137f245..a31a63e4 100644 --- a/third_party/blink/tools/blinkpy/w3c/test_copier_unittest.py +++ b/third_party/blink/tools/blinkpy/w3c/test_copier_unittest.py
@@ -25,6 +25,8 @@ # THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF # SUCH DAMAGE. +import textwrap + from blinkpy.common.host_mock import MockHost from blinkpy.common.path_finder import RELATIVE_WEB_TESTS from blinkpy.common.system.executive_mock import MockExecutive, ScriptError @@ -34,7 +36,7 @@ MOCK_WEB_TESTS = '/mock-checkout/' + RELATIVE_WEB_TESTS -FAKE_SOURCE_REPO_DIR = '/blink' +FAKE_SOURCE_REPO_DIR = '/blink/w3c' FAKE_FILES = { MOCK_WEB_TESTS + 'external/OWNERS': b'', @@ -61,41 +63,39 @@ host = MockHost() host.filesystem = MockFileSystem(files=FAKE_FILES) copier = TestCopier(host, FAKE_SOURCE_REPO_DIR) - copier.find_importable_tests() - self.assertEqual(copier.import_list, [{ - 'copy_list': [{ - 'dest': 'run.bat', - 'src': '/blink/w3c/dir/run.bat' - }, { - 'dest': 'has_shebang.txt', - 'src': '/blink/w3c/dir/has_shebang.txt' - }, { - 'dest': 'README.txt', - 'src': '/blink/w3c/dir/README.txt' - }], - 'dirname': - '/blink/w3c/dir', - }]) + copy_by_dir = copier.find_importable_tests() + self.assertEqual( + copy_by_dir, { + '/blink/w3c/dir': [{ + 'dest': 'run.bat', + 'src': '/blink/w3c/dir/run.bat' + }, { + 'dest': 'has_shebang.txt', + 'src': '/blink/w3c/dir/has_shebang.txt' + }, { + 'dest': 'README.txt', + 'src': '/blink/w3c/dir/README.txt' + }], + }) def test_does_not_import_reftestlist_file(self): host = MockHost() host.filesystem = MockFileSystem(files=FAKE_FILES) copier = TestCopier(host, FAKE_SOURCE_REPO_DIR) - copier.find_importable_tests() - self.assertEqual(copier.import_list, [{ - 'copy_list': [{ - 'dest': 'run.bat', - 'src': '/blink/w3c/dir/run.bat' - }, { - 'dest': 'has_shebang.txt', - 'src': '/blink/w3c/dir/has_shebang.txt' - }, { - 'dest': 'README.txt', - 'src': '/blink/w3c/dir/README.txt' - }], - 'dirname': - '/blink/w3c/dir', - }]) + copy_by_dir = copier.find_importable_tests() + self.assertEqual( + copy_by_dir, { + '/blink/w3c/dir': [{ + 'dest': 'run.bat', + 'src': '/blink/w3c/dir/run.bat' + }, { + 'dest': 'has_shebang.txt', + 'src': '/blink/w3c/dir/has_shebang.txt' + }, { + 'dest': 'README.txt', + 'src': '/blink/w3c/dir/README.txt' + }], + }) def test_executable_files(self): # Files with shebangs or .bat files need to be made executable. @@ -105,8 +105,8 @@ copier.do_import() self.assertEqual( host.filesystem.executable_files, { - MOCK_WEB_TESTS + 'external/blink/w3c/dir/run.bat', - MOCK_WEB_TESTS + 'external/blink/w3c/dir/has_shebang.txt' + MOCK_WEB_TESTS + 'external/w3c/dir/run.bat', + MOCK_WEB_TESTS + 'external/w3c/dir/has_shebang.txt' }) def test_ref_test_with_ref_is_copied(self): @@ -121,11 +121,11 @@ b'', }) copier = TestCopier(host, FAKE_SOURCE_REPO_DIR) - copier.find_importable_tests() - self.assertEqual(len(copier.import_list), 1) + copy_by_dir = copier.find_importable_tests() + self.assertEqual(len(copy_by_dir), 1) # The order of copy_list depends on the implementation of # filesystem.walk, so don't check the order - self.assertCountEqual(copier.import_list[0]['copy_list'], + self.assertCountEqual(copy_by_dir['/blink/w3c/dir1'], [{ 'src': '/blink/w3c/dir1/ref-file.html', 'dest': 'ref-file.html' @@ -133,5 +133,23 @@ 'src': '/blink/w3c/dir1/my-ref-test.html', 'dest': 'my-ref-test.html' }]) - self.assertEqual(copier.import_list[0]['dirname'], - '/blink/w3c/dir1') + + def test_skip_dir_but_copy_child_file(self): + host = MockHost() + host.filesystem = MockFileSystem(files=FAKE_FILES) + host.filesystem.write_text_file( + MOCK_WEB_TESTS + 'W3CImportExpectations', + textwrap.dedent("""\ + # results: [ Pass Skip ] + external/w3c/dir [ Skip ] + external/w3c/dir/run.bat [ Pass ] + """)) + copier = TestCopier(host, FAKE_SOURCE_REPO_DIR) + copy_by_dir = copier.find_importable_tests() + self.assertEqual( + copy_by_dir, { + '/blink/w3c/dir': [{ + 'dest': 'run.bat', + 'src': '/blink/w3c/dir/run.bat', + }], + })
diff --git a/third_party/blink/tools/blinkpy/web_tests/merge_results_unittest.py b/third_party/blink/tools/blinkpy/web_tests/merge_results_unittest.py index f97eca0..81a64fb 100644 --- a/third_party/blink/tools/blinkpy/web_tests/merge_results_unittest.py +++ b/third_party/blink/tools/blinkpy/web_tests/merge_results_unittest.py
@@ -1342,58 +1342,6 @@ """ web_test_output_filesystem = { - '/out/layout-test-results/access_log.txt': - output_access_log, - '/out/layout-test-results/error_log.txt': - output_error_log, - '/out/layout-test-results/failing_results.json': - "ADD_RESULTS(" + output_output_json + ");", - '/out/layout-test-results/full_results.json': - output_output_json, - '/out/layout-test-results/stats.json': - output_stats_json, - '/out/layout-test-results/testdir1/test1-actual.png': - '1ap', - '/out/layout-test-results/testdir1/test1-diff.png': - '1dp', - '/out/layout-test-results/testdir1/test1-diffs.html': - '1dh', - '/out/layout-test-results/testdir1/test1-expected-stderr.txt': - '1est', - '/out/layout-test-results/testdir1/test1-expected.png': - '1ep', - '/out/layout-test-results/testdir2/testdir2.1/test3-actual.png': - '3ap', - '/out/layout-test-results/testdir2/testdir2.1/test3-diff.png': - '3dp', - '/out/layout-test-results/testdir2/testdir2.1/test3-diffs.html': - '3dh', - '/out/layout-test-results/testdir2/testdir2.1/test3-expected-stderr.txt': - '3est', - '/out/layout-test-results/testdir2/testdir2.1/test3-expected.png': - '3ep', - '/out/layout-test-results/testdir2/testdir2.1/test4-actual.png': - '4ap', - '/out/layout-test-results/testdir2/testdir2.1/test4-diff.png': - '4dp', - '/out/layout-test-results/testdir2/testdir2.1/test4-diffs.html': - '4dh', - '/out/layout-test-results/testdir2/testdir2.1/test4-expected-stderr.txt': - '4est', - '/out/layout-test-results/testdir2/testdir2.1/test4-expected.png': - '4ep', - '/out/layout-test-results/testdir3/test5-actual.png': - '5ap', - '/out/layout-test-results/testdir3/test5-diff.png': - '5dp', - '/out/layout-test-results/testdir3/test5-diffs.html': - '5dh', - '/out/layout-test-results/testdir3/test5-expected-stderr.txt': - '5est', - '/out/layout-test-results/testdir3/test5-expected.png': - '5ep', - '/out/layout-test-results/times_ms.json': - output_times_ms_json, '/out/output.json': output_output_json, } @@ -1407,7 +1355,8 @@ for fname, expected_contents in self.web_test_output_filesystem.items( ): - self.assertTrue(fs.isfile(fname)) + self.assertTrue(fs.isfile(fname), + f'{fname} should be a regular file') if fname.endswith(".json"): actual_json_str = fs.read_text_file(fname) expected_json_str = expected_contents
diff --git a/third_party/blink/web_tests/W3CImportExpectations b/third_party/blink/web_tests/W3CImportExpectations index 8a547e3..ec49e76 100644 --- a/third_party/blink/web_tests/W3CImportExpectations +++ b/third_party/blink/web_tests/W3CImportExpectations
@@ -1,9 +1,11 @@ -# results: [ Skip ] +# results: [ Pass Skip ] # This file controls which subdirectories of the w3c test repos we import. # -# This file acts as a list of exclusions; directories and files not listed here -# will automatically be found and imported. +# Most lines in this file are exclusions marked with the `Skip` status. +# Directories and files not listed here will automatically be found and +# imported. It's also possible to import specific files in an excluded directory +# by marking them as `Pass`. # # Tests that are imported but not run are listed in NeverFixTests. This can be # used to make it easier to work on getting them running and passing. @@ -376,3 +378,7 @@ external/wpt/css/css-page/page-size-010.xht [ Skip ] external/wpt/css/css-page/page-size-011.xht [ Skip ] external/wpt/css/css-page/page-size-012.xht [ Skip ] + +# This is a reference file for a non-denylisted test, so it should be included +# despite a general exclusion of `css/CSS2/syntax/`. +external/wpt/css/CSS2/syntax/character-encoding-041-ref.xht [ Pass ]
diff --git a/third_party/blink/web_tests/external/wpt/credential-management/fedcm-button-mode-basics.https.html b/third_party/blink/web_tests/external/wpt/credential-management/fedcm-button-mode-basics.https.html index d7b063f..abf46ee7 100644 --- a/third_party/blink/web_tests/external/wpt/credential-management/fedcm-button-mode-basics.https.html +++ b/third_party/blink/web_tests/external/wpt/credential-management/fedcm-button-mode-basics.https.html
@@ -30,4 +30,63 @@ }); }, "Test that the button mode requires user activation."); + fedcm_test(async t => { + let widget_test_options = request_options_with_mediation_required("manifest_with_rp_mode.json"); + let button_test_options = request_options_with_mediation_required("manifest_with_rp_mode.json"); + button_test_options.identity.mode = "button"; + + let first_cred = navigator.credentials.get(widget_test_options); + let rej = promise_rejects_dom(t, 'NetworkError', first_cred); + + return test_driver.bless('initiate FedCM request', async function() { + let second_cred = await fedcm_get_and_select_first_account(t, button_test_options); + assert_equals(second_cred.token, "mode=button"); + await rej; + }); + }, "Test that the button mode can replace widget mode."); + +fedcm_test(async t => { + let widget_test_options = request_options_with_mediation_required(); + let button_test_options = request_options_with_mediation_required("manifest_with_rp_mode.json"); + button_test_options.identity.mode = "button"; + + return test_driver.bless('initiate FedCM request', async function() { + let first_cred = await fedcm_get_and_select_first_account(t, button_test_options); + assert_equals(first_cred.token, "mode=button"); + let second_cred = await fedcm_get_and_select_first_account(t, widget_test_options); + assert_equals(second_cred.token, "token"); + }); +}, "Test that the widget mode can succeed after the button mode."); + +fedcm_test(async t => { + let button_test_options = request_options_with_mediation_required("manifest_with_rp_mode.json"); + button_test_options.identity.mode = "button"; + + return test_driver.bless('initiate FedCM request', async function() { + let first_cred = fedcm_get_and_select_first_account(t, button_test_options); + let second_cred = navigator.credentials.get(button_test_options); + let rej = promise_rejects_dom(t, 'NotAllowedError', second_cred); + + let cred = await first_cred; + assert_equals(cred.token, "mode=button"); + await rej; + }); +}, "Test that the button mode cannot replace button mode."); + +fedcm_test(async t => { + let widget_test_options = request_options_with_mediation_required("manifest_with_rp_mode.json"); + let button_test_options = request_options_with_mediation_required("manifest_with_rp_mode.json"); + button_test_options.identity.mode = "button"; + + return test_driver.bless('initiate FedCM request', async function() { + let first_cred = fedcm_get_and_select_first_account(t, button_test_options); + let second_cred = navigator.credentials.get(widget_test_options); + let rej = promise_rejects_dom(t, 'NotAllowedError', second_cred); + + let cred = await first_cred; + assert_equals(cred.token, "mode=button"); + await rej; + }); +}, "Test that the widget mode cannot replace button mode."); + </script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-values/calc-size/animation/calc-size-height-interpolation.tentative-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-values/calc-size/animation/calc-size-height-interpolation.tentative-expected.txt new file mode 100644 index 0000000..324756dc --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-values/calc-size/animation/calc-size-height-interpolation.tentative-expected.txt
@@ -0,0 +1,132 @@ +This is a testharness.js-based test. +Found 64 FAIL, 0 TIMEOUT, 0 NOTRUN. +[FAIL] CSS Transitions: property <height> from neutral to [calc-size(auto, size * 2)] at (-0.25) should be [75px] + assert_true: 'to' value should be supported expected true got false +[FAIL] CSS Transitions: property <height> from neutral to [calc-size(auto, size * 2)] at (0) should be [100px] + assert_true: 'to' value should be supported expected true got false +[FAIL] CSS Transitions: property <height> from neutral to [calc-size(auto, size * 2)] at (0.25) should be [125px] + assert_true: 'to' value should be supported expected true got false +[FAIL] CSS Transitions: property <height> from neutral to [calc-size(auto, size * 2)] at (0.5) should be [150px] + assert_true: 'to' value should be supported expected true got false +[FAIL] CSS Transitions: property <height> from neutral to [calc-size(auto, size * 2)] at (0.75) should be [175px] + assert_true: 'to' value should be supported expected true got false +[FAIL] CSS Transitions: property <height> from neutral to [calc-size(auto, size * 2)] at (1) should be [200px] + assert_true: 'to' value should be supported expected true got false +[FAIL] CSS Transitions: property <height> from neutral to [calc-size(auto, size * 2)] at (1.25) should be [225px] + assert_true: 'to' value should be supported expected true got false +[FAIL] CSS Transitions with transition: all: property <height> from neutral to [calc-size(auto, size * 2)] at (-0.25) should be [75px] + assert_true: 'to' value should be supported expected true got false +[FAIL] CSS Transitions with transition: all: property <height> from neutral to [calc-size(auto, size * 2)] at (0) should be [100px] + assert_true: 'to' value should be supported expected true got false +[FAIL] CSS Transitions with transition: all: property <height> from neutral to [calc-size(auto, size * 2)] at (0.25) should be [125px] + assert_true: 'to' value should be supported expected true got false +[FAIL] CSS Transitions with transition: all: property <height> from neutral to [calc-size(auto, size * 2)] at (0.5) should be [150px] + assert_true: 'to' value should be supported expected true got false +[FAIL] CSS Transitions with transition: all: property <height> from neutral to [calc-size(auto, size * 2)] at (0.75) should be [175px] + assert_true: 'to' value should be supported expected true got false +[FAIL] CSS Transitions with transition: all: property <height> from neutral to [calc-size(auto, size * 2)] at (1) should be [200px] + assert_true: 'to' value should be supported expected true got false +[FAIL] CSS Transitions with transition: all: property <height> from neutral to [calc-size(auto, size * 2)] at (1.25) should be [225px] + assert_true: 'to' value should be supported expected true got false +[FAIL] CSS Animations: property <height> from neutral to [calc-size(auto, size * 2)] at (-0.25) should be [75px] + assert_true: 'to' value should be supported expected true got false +[FAIL] CSS Animations: property <height> from neutral to [calc-size(auto, size * 2)] at (0) should be [100px] + assert_true: 'to' value should be supported expected true got false +[FAIL] CSS Animations: property <height> from neutral to [calc-size(auto, size * 2)] at (0.25) should be [125px] + assert_true: 'to' value should be supported expected true got false +[FAIL] CSS Animations: property <height> from neutral to [calc-size(auto, size * 2)] at (0.5) should be [150px] + assert_true: 'to' value should be supported expected true got false +[FAIL] CSS Animations: property <height> from neutral to [calc-size(auto, size * 2)] at (0.75) should be [175px] + assert_true: 'to' value should be supported expected true got false +[FAIL] CSS Animations: property <height> from neutral to [calc-size(auto, size * 2)] at (1) should be [200px] + assert_true: 'to' value should be supported expected true got false +[FAIL] CSS Animations: property <height> from neutral to [calc-size(auto, size * 2)] at (1.25) should be [225px] + assert_true: 'to' value should be supported expected true got false +[FAIL] Web Animations: property <height> from neutral to [calc-size(auto, size * 2)] at (-0.25) should be [75px] + assert_true: 'to' value should be supported expected true got false +[FAIL] Web Animations: property <height> from neutral to [calc-size(auto, size * 2)] at (0) should be [100px] + assert_true: 'to' value should be supported expected true got false +[FAIL] Web Animations: property <height> from neutral to [calc-size(auto, size * 2)] at (0.25) should be [125px] + assert_true: 'to' value should be supported expected true got false +[FAIL] Web Animations: property <height> from neutral to [calc-size(auto, size * 2)] at (0.5) should be [150px] + assert_true: 'to' value should be supported expected true got false +[FAIL] Web Animations: property <height> from neutral to [calc-size(auto, size * 2)] at (0.75) should be [175px] + assert_true: 'to' value should be supported expected true got false +[FAIL] Web Animations: property <height> from neutral to [calc-size(auto, size * 2)] at (1) should be [200px] + assert_true: 'to' value should be supported expected true got false +[FAIL] Web Animations: property <height> from neutral to [calc-size(auto, size * 2)] at (1.25) should be [225px] + assert_true: 'to' value should be supported expected true got false +[FAIL] CSS Transitions: property <height> from [calc-size(min-content, 0px)] to [calc-size(min-content, size)] at (-0.25) should be [0] + assert_equals: expected "0px " but got "100px " +[FAIL] CSS Transitions: property <height> from [calc-size(min-content, 0px)] to [calc-size(min-content, size)] at (0) should be [0] + assert_equals: expected "0px " but got "100px " +[FAIL] CSS Transitions: property <height> from [calc-size(min-content, 0px)] to [calc-size(min-content, size)] at (0.25) should be [25px] + assert_equals: expected "25px " but got "100px " +[FAIL] CSS Transitions: property <height> from [calc-size(min-content, 0px)] to [calc-size(min-content, size)] at (0.5) should be [50px] + assert_equals: expected "50px " but got "100px " +[FAIL] CSS Transitions: property <height> from [calc-size(min-content, 0px)] to [calc-size(min-content, size)] at (0.75) should be [75px] + assert_equals: expected "75px " but got "100px " +[FAIL] CSS Transitions: property <height> from [calc-size(min-content, 0px)] to [calc-size(min-content, size)] at (1.25) should be [125px] + assert_equals: expected "125px " but got "100px " +[FAIL] CSS Transitions with transition: all: property <height> from [calc-size(min-content, 0px)] to [calc-size(min-content, size)] at (-0.25) should be [0] + assert_equals: expected "0px " but got "100px " +[FAIL] CSS Transitions with transition: all: property <height> from [calc-size(min-content, 0px)] to [calc-size(min-content, size)] at (0) should be [0] + assert_equals: expected "0px " but got "100px " +[FAIL] CSS Transitions with transition: all: property <height> from [calc-size(min-content, 0px)] to [calc-size(min-content, size)] at (0.25) should be [25px] + assert_equals: expected "25px " but got "100px " +[FAIL] CSS Transitions with transition: all: property <height> from [calc-size(min-content, 0px)] to [calc-size(min-content, size)] at (0.5) should be [50px] + assert_equals: expected "50px " but got "100px " +[FAIL] CSS Transitions with transition: all: property <height> from [calc-size(min-content, 0px)] to [calc-size(min-content, size)] at (0.75) should be [75px] + assert_equals: expected "75px " but got "100px " +[FAIL] CSS Transitions with transition: all: property <height> from [calc-size(min-content, 0px)] to [calc-size(min-content, size)] at (1.25) should be [125px] + assert_equals: expected "125px " but got "100px " +[FAIL] CSS Transitions: property <height> from [0] to [calc-size(max-content, size)] at (-0.25) should be [0] + assert_equals: expected "0px " but got "100px " +[FAIL] CSS Transitions: property <height> from [0] to [calc-size(max-content, size)] at (0) should be [0] + assert_equals: expected "0px " but got "100px " +[FAIL] CSS Transitions: property <height> from [0] to [calc-size(max-content, size)] at (0.25) should be [25px] + assert_equals: expected "25px " but got "100px " +[FAIL] CSS Transitions: property <height> from [0] to [calc-size(max-content, size)] at (0.5) should be [50px] + assert_equals: expected "50px " but got "100px " +[FAIL] CSS Transitions: property <height> from [0] to [calc-size(max-content, size)] at (0.75) should be [75px] + assert_equals: expected "75px " but got "100px " +[FAIL] CSS Transitions: property <height> from [0] to [calc-size(max-content, size)] at (1.25) should be [125px] + assert_equals: expected "125px " but got "100px " +[FAIL] CSS Transitions with transition: all: property <height> from [0] to [calc-size(max-content, size)] at (-0.25) should be [0] + assert_equals: expected "0px " but got "100px " +[FAIL] CSS Transitions with transition: all: property <height> from [0] to [calc-size(max-content, size)] at (0) should be [0] + assert_equals: expected "0px " but got "100px " +[FAIL] CSS Transitions with transition: all: property <height> from [0] to [calc-size(max-content, size)] at (0.25) should be [25px] + assert_equals: expected "25px " but got "100px " +[FAIL] CSS Transitions with transition: all: property <height> from [0] to [calc-size(max-content, size)] at (0.5) should be [50px] + assert_equals: expected "50px " but got "100px " +[FAIL] CSS Transitions with transition: all: property <height> from [0] to [calc-size(max-content, size)] at (0.75) should be [75px] + assert_equals: expected "75px " but got "100px " +[FAIL] CSS Transitions with transition: all: property <height> from [0] to [calc-size(max-content, size)] at (1.25) should be [125px] + assert_equals: expected "125px " but got "100px " +[FAIL] CSS Animations: property <height> from [0] to [calc-size(max-content, size)] at (-0.25) should be [0] + assert_equals: expected "0px " but got "100px " +[FAIL] CSS Animations: property <height> from [0] to [calc-size(max-content, size)] at (0) should be [0] + assert_equals: expected "0px " but got "100px " +[FAIL] CSS Animations: property <height> from [0] to [calc-size(max-content, size)] at (0.25) should be [25px] + assert_equals: expected "25px " but got "100px " +[FAIL] CSS Animations: property <height> from [0] to [calc-size(max-content, size)] at (0.5) should be [50px] + assert_equals: expected "50px " but got "100px " +[FAIL] CSS Animations: property <height> from [0] to [calc-size(max-content, size)] at (0.75) should be [75px] + assert_equals: expected "75px " but got "100px " +[FAIL] CSS Animations: property <height> from [0] to [calc-size(max-content, size)] at (1.25) should be [125px] + assert_equals: expected "125px " but got "100px " +[FAIL] Web Animations: property <height> from [0] to [calc-size(max-content, size)] at (-0.25) should be [0] + assert_equals: expected "0px " but got "100px " +[FAIL] Web Animations: property <height> from [0] to [calc-size(max-content, size)] at (0) should be [0] + assert_equals: expected "0px " but got "100px " +[FAIL] Web Animations: property <height> from [0] to [calc-size(max-content, size)] at (0.25) should be [25px] + assert_equals: expected "25px " but got "100px " +[FAIL] Web Animations: property <height> from [0] to [calc-size(max-content, size)] at (0.5) should be [50px] + assert_equals: expected "50px " but got "100px " +[FAIL] Web Animations: property <height> from [0] to [calc-size(max-content, size)] at (0.75) should be [75px] + assert_equals: expected "75px " but got "100px " +[FAIL] Web Animations: property <height> from [0] to [calc-size(max-content, size)] at (1.25) should be [125px] + assert_equals: expected "125px " but got "100px " +Harness: the test ran to completion. +
diff --git a/third_party/blink/web_tests/external/wpt/css/css-values/calc-size/animation/calc-size-height-interpolation.tentative.html b/third_party/blink/web_tests/external/wpt/css/css-values/calc-size/animation/calc-size-height-interpolation.tentative.html new file mode 100644 index 0000000..525c388 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-values/calc-size/animation/calc-size-height-interpolation.tentative.html
@@ -0,0 +1,81 @@ +<!DOCTYPE html> +<meta charset="UTF-8"> +<title>height: calc-size() animations</title> +<link rel="help" href="https://drafts.csswg.org/css-values-5/#calc-size"> + +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="../../../support/interpolation-testcommon.js"></script> + +<style> +.target { + display: block; +} +.target::before { + display: block; + content: ""; + width: 23px; + height: 100px; +} +</style> + +<body> + +<script> + test_interpolation({ + property: 'height', + from: neutralKeyframe, + to: 'calc-size(auto, size * 2)', + }, [ + { at: -0.25, expect: '75px' }, + { at: 0, expect: '100px' }, + { at: 0.25, expect: '125px' }, + { at: 0.5, expect: '150px' }, + { at: 0.75, expect: '175px' }, + { at: 1, expect: '200px' }, + { at: 1.25, expect: '225px' }, + ]); + + test_interpolation({ + property: 'height', + from: 'calc-size(min-content, 0 * size)', + to: 'calc-size(min-content, size)', + }, [ + { at: -0.25, expect: '0' }, + { at: 0, expect: '0' }, + { at: 0.25, expect: '25px' }, + { at: 0.5, expect: '50px' }, + { at: 0.75, expect: '75px' }, + { at: 1, expect: '100px' }, + { at: 1.25, expect: '125px' }, + ]); + + test_interpolation({ + property: 'height', + from: 'calc-size(min-content, 0px)', + to: 'calc-size(min-content, size)', + }, [ + { at: -0.25, expect: '0' }, + { at: 0, expect: '0' }, + { at: 0.25, expect: '25px' }, + { at: 0.5, expect: '50px' }, + { at: 0.75, expect: '75px' }, + { at: 1, expect: '100px' }, + { at: 1.25, expect: '125px' }, + ]); + + test_interpolation({ + property: 'height', + from: '0', + to: 'calc-size(max-content, size)', + }, [ + { at: -0.25, expect: '0' }, + { at: 0, expect: '0' }, + { at: 0.25, expect: '25px' }, + { at: 0.5, expect: '50px' }, + { at: 0.75, expect: '75px' }, + { at: 1, expect: '100px' }, + { at: 1.25, expect: '125px' }, + ]); + +</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-values/calc-size/animation/calc-size-width-interpolation.tentative.html b/third_party/blink/web_tests/external/wpt/css/css-values/calc-size/animation/calc-size-width-interpolation.tentative.html index 7254951..ff72ba6a 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-values/calc-size/animation/calc-size-width-interpolation.tentative.html +++ b/third_party/blink/web_tests/external/wpt/css/css-values/calc-size/animation/calc-size-width-interpolation.tentative.html
@@ -1,8 +1,7 @@ <!DOCTYPE html> <meta charset="UTF-8"> <title>width: calc-size() animations</title> -<link rel="help" href="https://drafts.csswg.org/css-values-5/"> -<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/626#issuecomment-1800254442"> +<link rel="help" href="https://drafts.csswg.org/css-values-5/#calc-size"> <script src="/resources/testharness.js"></script> <script src="/resources/testharnessreport.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-values/calc-size/calc-size-height.tentative-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-values/calc-size/calc-size-height.tentative-expected.txt new file mode 100644 index 0000000..0931cad --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-values/calc-size/calc-size-height.tentative-expected.txt
@@ -0,0 +1,7 @@ +This is a testharness.js-based test. +[FAIL] resolved height for height in auto height container: calc-size(any, 31%) + assert_equals: expected "0px" but got "10px" +[FAIL] resolved height for height in auto height container: calc-size(max-content, 31%) + assert_equals: expected "0px" but got "10px" +Harness: the test ran to completion. +
diff --git a/third_party/blink/web_tests/external/wpt/css/css-values/calc-size/calc-size-height.tentative.html b/third_party/blink/web_tests/external/wpt/css/css-values/calc-size/calc-size-height.tentative.html new file mode 100644 index 0000000..6f93cc0 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-values/calc-size/calc-size-height.tentative.html
@@ -0,0 +1,61 @@ +<!DOCTYPE html> +<title>calc-size() on height</title> +<link rel="help" href="https://drafts.csswg.org/css-values-5/#calc-size"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> + #container { + font-size: 20px; + } + #child { + width: 123px; + height: 10px; + } +</style> + +<div id="container"> + <div id="target"> + <div id="child"></div> + </div> +</div> + +<script> + +let basic_tests = [ + { value: "calc-size(any, 357px)", expected: "357px" }, + { value: "calc-size(any, 31%)", expected_auto: "0px", expected_definite: "31px" }, + { value: "calc-size(31%, size)", expected_auto: "10px", expected_definite: "31px" }, + { value: "calc-size(max-content, 31%)", expected_auto: "0px", expected_definite: "31px" }, + { value: "calc-size(fit-content, 72px)", expected: "72px" }, + { value: "calc-size(37px, 93px)", expected: "93px" }, + { value: "calc-size(83px, size * 3)", expected: "249px" }, + { value: "calc-size(min-content, size / 2)", expected: "5px" }, + { value: "calc-size(max-content, size * 1.2)", expected: "12px" }, + { value: "calc-size(fit-content, size / 2 + 30px)", expected: "35px" }, + { value: "calc-size(30px, 15em)", expected: "300px" }, + { value: "calc-size(calc-size(any, 30px), 15em)", expected: "300px" }, + { value: "calc-size(calc-size(2in, 30px), 15em)", expected: "300px" }, + { value: "calc-size(calc-size(min-content, 30px), 15em)", expected: "300px" }, + { value: "calc-size(calc-size(min-content, size), size)", expected: "10px" }, +]; +const container = document.getElementById("container"); +const target = document.getElementById("target"); +const target_cs = getComputedStyle(target); +for (const obj of basic_tests) { + test((t) => { + target.style.removeProperty("height"); + target.style.height = obj.value; + container.style.height = "auto"; + let expected = "expected" in obj ? obj.expected : obj.expected_auto; + assert_equals(target_cs.height, expected); + }, `resolved height for height in auto height container: ${obj.value}`); + test((t) => { + target.style.removeProperty("height"); + target.style.height = obj.value; + container.style.height = "100px"; + let expected = "expected" in obj ? obj.expected : obj.expected_definite; + assert_equals(target_cs.height, expected); + }, `resolved height for height in definite height container: ${obj.value}`); +} + +</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-values/calc-size/calc-size-parsing.tentative.html b/third_party/blink/web_tests/external/wpt/css/css-values/calc-size/calc-size-parsing.tentative.html index fec24aa..bc0048f 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-values/calc-size/calc-size-parsing.tentative.html +++ b/third_party/blink/web_tests/external/wpt/css/css-values/calc-size/calc-size-parsing.tentative.html
@@ -1,7 +1,6 @@ <!DOCTYPE html> <title>calc-size() expressions</title> -<link rel="help" href="https://drafts.csswg.org/css-values-5/"> -<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/626#issuecomment-1800254442"> +<link rel="help" href="https://drafts.csswg.org/css-values-5/#calc-size"> <script src="/resources/testharness.js"></script> <script src="/resources/testharnessreport.js"></script> <script src="../../support/parsing-testcommon.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-values/calc-size/calc-size-width.tentative.html b/third_party/blink/web_tests/external/wpt/css/css-values/calc-size/calc-size-width.tentative.html index 228f526..c8000f34 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-values/calc-size/calc-size-width.tentative.html +++ b/third_party/blink/web_tests/external/wpt/css/css-values/calc-size/calc-size-width.tentative.html
@@ -1,7 +1,6 @@ <!DOCTYPE html> <title>calc-size() on width</title> -<link rel="help" href="https://drafts.csswg.org/css-values-5/"> -<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/626#issuecomment-1800254442"> +<link rel="help" href="https://drafts.csswg.org/css-values-5/#calc-size"> <script src="/resources/testharness.js"></script> <script src="/resources/testharnessreport.js"></script> <link rel="stylesheet" href="/fonts/ahem.css">
diff --git a/third_party/blink/web_tests/external/wpt/fledge/tentative/additional-bids.https.window.js b/third_party/blink/web_tests/external/wpt/fledge/tentative/additional-bids.https.window.js new file mode 100644 index 0000000..0e1d22c --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/fledge/tentative/additional-bids.https.window.js
@@ -0,0 +1,146 @@ +// META: script=/resources/testdriver.js +// META: script=/common/utils.js +// META: script=resources/fledge-util.sub.js +// META: script=/common/subset-tests.js +// META: timeout=long +// META: variant=?1-last + +"use strict;" + +// This file contains tests for additional bids and negative targeting. +// +// TODO: +// - test that an negatively targeted additional bid is suppressed. +// - test that an incorrectly signed additional bid is not negative targeted. +// - test that an missing-signature additional bid is not negative targeted. +// - test that an additional bid with some correct signatures can be negative. +// negative targeted for those negative interest groups whose signatures +// match. +// - test an additional bid with multiple negative interest groups. +// - test that multiple negative interest groups with mismatched joining origins +// is not negative targeted. +// - test that additional bids can be fetched using an iframe navigation. +// - test that additional bids are not fetched using an iframe navigation for +// which the `adAuctionHeaders=true` attribute is not specified. +// - test that additional bids are not fetched using a Fetch request for which +// `adAuctionHeaders: true` is not specified. +// - test that an additional bid with an incorrect auction nonce is not used +// included in an auction. Same for seller and top-level seller. +// - test that correctly formatted additional bids are included in an auction +// when fetched alongside malformed additional bid headers by a Fetch +// request. +// - test that correctly formatted additional bids are included in an auction +// when fetched alongside malformed additional bid headers by an iframe +// navigation. +// - test that reportWin is not used for reporting an additional bid win. +// - test that additional bids can *not* be fetched from iframe subresource +// requests. +// - test that an auction nonce can only be used once, and a second auction +// trying to reuse an auction immediately fails. +// - test that an auction nonce must be created in the same window/tab as the +// call to runAdAuction. +// - test reportAdditionalBidWin with each of no metadata, null metadata, and +// an object metadata. +// - test that an auction running in one tab can't see an additional bid loaded +// in a new tab. +// - test that two auctions running with different nonces only get the +// additional bids fetched with their auction nonce. +// - test that two auctions running with different nonces only get the +// additional bids fetched with their auction nonce, when both additional +// bids are retrieved with one fetch. +// - test that a multiseller auction with two component auctions can direct +// additional bids to the correct component auctions. +// - test that two auctions running with different nonces only get the +// additional bids fetched with their auction nonce. +// - test that two auctions running with different nonces only get the +// additional bids fetched with their auction nonce, when both additional +// bids are retrieved with one fetch. +// - test that an additional bid can compete against an interest group bid and +// lose. +// - test that an additional bid can compete against an interest group bid and +// win. +// - test that a malformed additional bid causes that one additional bid to be +// ignored, but the rest of the auction (and other additional bids, even +// from the same fetch) continue on. +// - test (in join-leave-ad-interest-group.https.window.js) that an IG that +// provides `additionalBidKey` fails if the key fails to decode, or if +// that IG also provides `ads`, or if it provides `updateURL`. +// - test that an IG update cannot cause a regular interest group (one that +// does not provide `additionalBidKey`) to become a negative interest +// group (one that does provide `additionalBidKey`). +// - test (in auction-config-passed-to-worklets.https.window.js) that a +// multi-seller auction fails if the top-level auction provides +// a value for `additionalBids`. +// - test (in auction-config-passed-to-worklets.https.window.js) that an auction +// fails if it provides `additionalBids` but not `auctionNonce`, or if it +// provides `additionalBids` but not `interestGroupBuyers`. + +// The auction is run with the seller being the same as the document origin. +// The request to fetch additional bids must be issued to the seller's origin +// for ad auction headers interception to associate it with this auction. +const SINGLE_SELLER_AUCTION_SELLER = window.location.origin; + +// Single-seller auction with a single buyer who places a single additional +// bid. As the only bid, this wins. +subsetTest(promise_test, async test => { + const uuid = generateUuid(test); + const auctionNonce = await navigator.createAuctionNonce(); + const seller = SINGLE_SELLER_AUCTION_SELLER; + + const buyer = OTHER_ORIGIN1; + const additionalBid = createAdditionalBid( + uuid, auctionNonce, seller, buyer, 'horses', 1.99); + + await runAdditionalBidTest( + test, uuid, [buyer], auctionNonce, + fetchAdditionalBids(seller, [additionalBid]), + /*highestScoringOtherBid=*/0, + /*winningAdditionalBidId=*/'horses'); +}, 'single valid additional bid'); + +// Single-seller auction with a two buyers competing with additional bids. +subsetTest(promise_test, async test => { + const uuid = generateUuid(test); + const auctionNonce = await navigator.createAuctionNonce(); + const seller = SINGLE_SELLER_AUCTION_SELLER; + + const buyer1 = OTHER_ORIGIN1; + const additionalBid1 = createAdditionalBid( + uuid, auctionNonce, seller, buyer1, 'horses', 1.99); + + const buyer2 = OTHER_ORIGIN2; + const additionalBid2 = createAdditionalBid( + uuid, auctionNonce, seller, buyer2, 'planes', 2.99); + + await runAdditionalBidTest( + test, uuid, [buyer1, buyer2], auctionNonce, + fetchAdditionalBids(seller, [additionalBid1, additionalBid2]), + /*highestScoringOtherBid=*/1.99, + /*winningAdditionalBidId=*/'planes'); +}, 'two valid additional bids'); + +// Same as the test above, except that this uses two Fetch requests instead of +// one to retrieve the additional bids. +subsetTest(promise_test, async test => { + const uuid = generateUuid(test); + const auctionNonce = await navigator.createAuctionNonce(); + const seller = SINGLE_SELLER_AUCTION_SELLER; + + const buyer1 = OTHER_ORIGIN1; + const additionalBid1 = createAdditionalBid( + uuid, auctionNonce, seller, buyer1, 'horses', 1.99); + + const buyer2 = OTHER_ORIGIN2; + const additionalBid2 = createAdditionalBid( + uuid, auctionNonce, seller, buyer2, 'planes', 2.99); + + + await runAdditionalBidTest( + test, uuid, [buyer1, buyer2], auctionNonce, + Promise.all([ + fetchAdditionalBids(seller, [additionalBid1]), + fetchAdditionalBids(seller, [additionalBid2]) + ]), + /*highestScoringOtherBid=*/1.99, + /*winningAdditionalBidId=*/'planes'); +}, 'two valid additional bids from two distinct Fetch requests');
diff --git a/third_party/blink/web_tests/external/wpt/fledge/tentative/resources/additional-bids.py b/third_party/blink/web_tests/external/wpt/fledge/tentative/resources/additional-bids.py new file mode 100644 index 0000000..060606b4 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/fledge/tentative/resources/additional-bids.py
@@ -0,0 +1,59 @@ +"""Endpoint to return additional bids in the appropriate response header. + +Additional bids are returned using the "Ad-Auction-Additional-Bid" response +header, as described at +https://github.com/WICG/turtledove/blob/main/FLEDGE.md#63-http-response-headers. + +This script generates one of "Ad-Auction-Additional-Bid" response header for +each additional bid provided in a url-encoded `additionalBids` query parameter. + +All requests to this endpoint requires a "Sec-Ad-Auction-Fetch" request header +with a value of b"?1"; this entrypoint otherwise returns a 400 response. +""" +import json +import base64 + +import fledge.tentative.resources.fledge_http_server_util as fledge_http_server_util + + +class BadRequestError(Exception): + pass + + +def main(request, response): + try: + if fledge_http_server_util.handle_cors_headers_and_preflight(request, response): + return + + # Verify that Sec-Ad-Auction-Fetch is present + if (request.headers.get("Sec-Ad-Auction-Fetch", default=b"").decode("utf-8") != "?1"): + raise BadRequestError("Sec-Ad-Auction-Fetch missing or unexpected value; expected '?1'") + + # Return each signed additional bid in its own header + additional_bids = request.GET.get(b"additionalBids", default=b"").decode("utf-8") + if not additional_bids: + raise BadRequestError("Missing 'additionalBids' parameter") + for additional_bid in json.loads(additional_bids): + additional_bid_string = json.dumps(additional_bid) + auction_nonce = additional_bid.get("auctionNonce", None) + if not auction_nonce: + raise BadRequestError("Additional bid missing required 'auctionNonce' field") + signed_additional_bid = json.dumps({ + "bid": additional_bid_string, + "signatures": [] + }) + additional_bid_header_value = (auction_nonce.encode("utf-8") + b":" + + base64.b64encode(signed_additional_bid.encode("utf-8"))) + response.headers.append(b"Ad-Auction-Additional-Bid", additional_bid_header_value) + + response.status = (200, b"OK") + response.headers.set(b"Content-Type", b"text/plain") + + except BadRequestError as error: + response.status = (400, b"Bad Request") + response.headers.set(b"Content-Type", b"text/plain") + response.content = str(error) + + except Exception as exception: + response.status = (500, b"Internal Server Error") + response.content = str(exception)
diff --git a/third_party/blink/web_tests/external/wpt/fledge/tentative/resources/bidding-logic.sub.py b/third_party/blink/web_tests/external/wpt/fledge/tentative/resources/bidding-logic.sub.py index 707e37f..e17f2c2c 100644 --- a/third_party/blink/web_tests/external/wpt/fledge/tentative/resources/bidding-logic.sub.py +++ b/third_party/blink/web_tests/external/wpt/fledge/tentative/resources/bidding-logic.sub.py
@@ -1,71 +1,83 @@ from pathlib import Path +from fledge.tentative.resources import fledge_http_server_util + # General bidding logic script. Depending on query parameters, it can -# simulate a variety of network errors, and its generateBid() and -# reportWin() functions can have arbitrary Javascript code injected -# in them. generateBid() will by default return a bid of 9 for the +# simulate a variety of network errors, and its generateBid(), reportWin(), +# and reportAdditionalBidWin() functions can have arbitrary Javascript code +# injected in them. generateBid() will by default return a bid of 9 for the # first ad. def main(request, response): - error = request.GET.first(b"error", None) + if fledge_http_server_util.handle_cors_headers_and_preflight(request, response): + return - if error == b"close-connection": - # Close connection without writing anything, to simulate a network - # error. The write call is needed to avoid writing the default headers. - response.writer.write("") - response.close_connection = True - return + error = request.GET.first(b"error", None) - if error == b"http-error": - response.status = (404, b"OK") - else: - response.status = (200, b"OK") + if error == b"close-connection": + # Close connection without writing anything, to simulate a network + # error. The write call is needed to avoid writing the default headers. + response.writer.write("") + response.close_connection = True + return - if error == b"wrong-content-type": - response.headers.set(b"Content-Type", b"application/json") - elif error != b"no-content-type": - response.headers.set(b"Content-Type", b"application/javascript") + if error == b"http-error": + response.status = (404, b"OK") + else: + response.status = (200, b"OK") - if error == b"bad-allow-fledge": - response.headers.set(b"Ad-Auction-Allowed", b"sometimes") - elif error == b"fledge-not-allowed": - response.headers.set(b"Ad-Auction-Allowed", b"false") - elif error != b"no-allow-fledge": - response.headers.set(b"Ad-Auction-Allowed", b"true") + if error == b"wrong-content-type": + response.headers.set(b"Content-Type", b"application/json") + elif error != b"no-content-type": + response.headers.set(b"Content-Type", b"application/javascript") - if error == b"no-body": - return b'' + if error == b"bad-allow-fledge": + response.headers.set(b"Ad-Auction-Allowed", b"sometimes") + elif error == b"fledge-not-allowed": + response.headers.set(b"Ad-Auction-Allowed", b"false") + elif error != b"no-allow-fledge": + response.headers.set(b"Ad-Auction-Allowed", b"true") - body = (Path(__file__).parent.resolve() / 'worklet-helpers.js').read_text().encode("ASCII") - if error != b"no-generateBid": - # Use bid query param if present. Otherwise, use a bid of 9. - bid = (request.GET.first(b"bid", None) or b"9").decode("ASCII") + if error == b"no-body": + return b'' - bidCurrency = "" - bidCurrencyParam = request.GET.first(b"bidCurrency", None) - if bidCurrencyParam != None: - bidCurrency = "bidCurrency: '" + bidCurrencyParam.decode("ASCII") + "'," + body = (Path(__file__).parent.resolve() / 'worklet-helpers.js').read_text().encode("ASCII") + if error != b"no-generateBid": + # Use bid query param if present. Otherwise, use a bid of 9. + bid = (request.GET.first(b"bid", None) or b"9").decode("ASCII") - allowComponentAuction = "" - allowComponentAuctionParam = request.GET.first(b"allowComponentAuction", None) - if allowComponentAuctionParam != None: - allowComponentAuction = f"allowComponentAuction: {allowComponentAuctionParam.decode('ASCII')}," + bidCurrency = "" + bidCurrencyParam = request.GET.first(b"bidCurrency", None) + if bidCurrencyParam != None: + bidCurrency = "bidCurrency: '" + bidCurrencyParam.decode("ASCII") + "'," - body += f""" - function generateBid(interestGroup, auctionSignals, perBuyerSignals, - trustedBiddingSignals, browserSignals, - directFromSellerSignals) {{ - {{{{GET[generateBid]}}}}; - return {{ - bid: {bid}, - {bidCurrency} - {allowComponentAuction} - render: interestGroup.ads[0].renderURL - }}; - }}""".encode() - if error != b"no-reportWin": - body += b""" - function reportWin(auctionSignals, perBuyerSignals, sellerSignals, - browserSignals, directFromSellerSignals) { - {{GET[reportWin]}}; - }""" - return body + allowComponentAuction = "" + allowComponentAuctionParam = request.GET.first(b"allowComponentAuction", None) + if allowComponentAuctionParam != None: + allowComponentAuction = f"allowComponentAuction: {allowComponentAuctionParam.decode('ASCII')}," + + body += f""" + function generateBid(interestGroup, auctionSignals, perBuyerSignals, + trustedBiddingSignals, browserSignals, + directFromSellerSignals) {{ + {{{{GET[generateBid]}}}}; + return {{ + bid: {bid}, + {bidCurrency} + {allowComponentAuction} + render: interestGroup.ads[0].renderURL + }}; + }}""".encode() + if error != b"no-reportWin": + body += b""" + function reportWin(auctionSignals, perBuyerSignals, sellerSignals, + browserSignals, directFromSellerSignals) { + {{GET[reportWin]}}; + }""" + if error != b"no-reportAdditionalBidWin": + body += b""" + function reportAdditionalBidWin(auctionSignals, perBuyerSignals, + sellerSignals, browserSignals, + directFromSellerSignals) { + {{GET[reportAdditionalBidWin]}}; + }""" + return body
diff --git a/third_party/blink/web_tests/external/wpt/fledge/tentative/resources/fledge-util.sub.js b/third_party/blink/web_tests/external/wpt/fledge/tentative/resources/fledge-util.sub.js index c78827f4..5819357 100644 --- a/third_party/blink/web_tests/external/wpt/fledge/tentative/resources/fledge-util.sub.js +++ b/third_party/blink/web_tests/external/wpt/fledge/tentative/resources/fledge-util.sub.js
@@ -33,7 +33,7 @@ // on behavior of the script; it only serves to make the URL unique. // `id` will always be the last query parameter. function createTrackerURL(origin, uuid, dispatch, id = null) { - let url = new URL(`${origin}${BASE_PATH}resources/request-tracker.py`); + let url = new URL(`${origin}${RESOURCE_PATH}request-tracker.py`); let search = `uuid=${uuid}&dispatch=${dispatch}`; if (id) search += `&id=${id}`; @@ -59,6 +59,10 @@ return createTrackerURL(origin, uuid, `track_get`, `seller_report_${id}`); } +function createHighestScoringOtherBidReportURL(uuid, highestScoringOtherBid) { + return createSellerReportURL(uuid) + '&highestScoringOtherBid=' + Math.round(highestScoringOtherBid); +} + // Much like above ReportURL methods, except designed for beacons, which // are expected to be POSTs. function createBidderBeaconURL(uuid, id = '1', origin = window.location.origin) { @@ -69,7 +73,7 @@ } function createDirectFromSellerSignalsURL(origin = window.location.origin) { - let url = new URL(`${origin}${BASE_PATH}resources/direct-from-seller-signals.py`); + let url = new URL(`${origin}${RESOURCE_PATH}direct-from-seller-signals.py`); return url.toString(); } @@ -80,7 +84,7 @@ let uuid = token(); test.add_cleanup(async () => { let response = await fetch(createCleanupURL(uuid), - {credentials: 'omit', mode: 'cors'}); + { credentials: 'omit', mode: 'cors' }); assert_equals(await response.text(), 'cleanup complete', `Sever state cleanup failed`); }); @@ -94,7 +98,7 @@ let trackedRequestsURL = createTrackerURL(window.location.origin, uuid, 'tracked_data'); let response = await fetch(trackedRequestsURL, - {credentials: 'omit', mode: 'cors'}); + { credentials: 'omit', mode: 'cors' }); let trackedData = await response.json(); // Fail on fetch error. @@ -179,6 +183,8 @@ url.searchParams.append('generateBid', params.generateBid); if (params.reportWin != null) url.searchParams.append('reportWin', params.reportWin); + if (params.reportAdditionalBidWin != null) + url.searchParams.append('reportAdditionalBidWin', params.reportAdditionalBidWin); if (params.error != null) url.searchParams.append('error', params.error); if (params.bid != null) @@ -270,7 +276,7 @@ await navigator.joinAdInterestGroup(interestGroup, durationSeconds); test.add_cleanup( - async () => {await navigator.leaveAdInterestGroup(interestGroup)}); + async () => { await navigator.leaveAdInterestGroup(interestGroup) }); } // Similar to joinInterestGroup, but leaves the interest group instead. @@ -481,6 +487,24 @@ await waitForObservedRequests(uuid, expectedReportURLs); } +async function runAdditionalBidTest(test, uuid, buyers, auctionNonce, + additionalBidsPromise, + highestScoringOtherBid, + winningAdditionalBidId) { + await runBasicFledgeAuctionAndNavigate( + test, uuid, + { interestGroupBuyers: buyers, + auctionNonce: auctionNonce, + additionalBids: additionalBidsPromise, + decisionLogicURL: createDecisionScriptURL( + uuid, + { reportResult: `sendReportTo("${createSellerReportURL(uuid)}&highestScoringOtherBid=" + Math.round(browserSignals.highestScoringOtherBid));` })}); + + await waitForObservedRequests( + uuid, [createHighestScoringOtherBidReportURL(uuid, highestScoringOtherBid), + createBidderReportURL(uuid, winningAdditionalBidId)]); +} + // Runs "script" in "child_window" via an eval call. The "child_window" must // have been created by calling "createFrame()" below. "param" is passed to the // context "script" is run in, so can be used to pass objects that @@ -671,3 +695,46 @@ `sendReportTo("${createBidderReportURL(uuid)}");`, }; } + +// Creates an additional bid with the given parameters. This additional bid +// specifies a biddingLogicURL that provides an implementation of +// reportAdditionalBidWin that triggers a sendReportTo() to the bidder report +// URL of the winning additional bid. Additional bids are described in more +// detail at +// https://github.com/WICG/turtledove/blob/main/FLEDGE.md#6-additional-bids. +function createAdditionalBid(uuid, auctionNonce, seller, buyer, interestGroupName, bidAmount, + additionalBidOverrides = {}) { + return { + interestGroup: { + name: interestGroupName, + biddingLogicURL: createBiddingScriptURL( + { + origin: buyer, + reportAdditionalBidWin: `sendReportTo("${createBidderReportURL(uuid, interestGroupName)}");` + }), + owner: buyer + }, + bid: { + ad: ['metadata'], + bid: bidAmount, + render: createRenderURL(uuid) + }, + auctionNonce: auctionNonce, + seller: seller, + ...additionalBidOverrides + } +} + +// Fetch some number of additional bid from a seller and verify that the +// 'Ad-Auction-Additional-Bid' header is not visible in this JavaScript context. +// The `additionalBids` parameter is a list of additional bids. +async function fetchAdditionalBids(seller, additionalBids) { + const url = new URL(`${seller}${RESOURCE_PATH}additional-bids.py`); + url.searchParams.append('additionalBids', JSON.stringify(additionalBids)); + const response = await fetch(url.href, {adAuctionHeaders: true}); + + assert_equals(response.status, 200, 'Failed to fetch additional bid: ' + await response.text()); + assert_false( + response.headers.has('Ad-Auction-Additional-Bid'), + 'Header "Ad-Auction-Additional-Bid" should not be available in JavaScript context.'); +}
diff --git a/third_party/blink/web_tests/external/wpt/html/canvas/element/layers/2d.layer.ctm.shadow-in-transformed-layer-expected.html b/third_party/blink/web_tests/external/wpt/html/canvas/element/layers/2d.layer.ctm.shadow-in-transformed-layer-expected.html new file mode 100644 index 0000000..312ca19b --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/html/canvas/element/layers/2d.layer.ctm.shadow-in-transformed-layer-expected.html
@@ -0,0 +1,27 @@ +<!DOCTYPE html> +<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. --> +<title>Canvas test: 2d.layer.ctm.shadow-in-transformed-layer</title> +<h1>2d.layer.ctm.shadow-in-transformed-layer</h1> +<p class="desc">Check shadows inside of a transformed layer.</p> +<canvas id="canvas" width="200" height="200"> + <p class="fallback">FAIL (fallback content)</p> +</canvas> +<script> + const canvas = document.getElementById("canvas"); + const ctx = canvas.getContext('2d'); + + ctx.translate(80, 90); + ctx.scale(2, 2); + ctx.rotate(Math.PI / 2); + + ctx.shadowOffsetX = 10; + ctx.shadowOffsetY = 10; + ctx.shadowColor = 'grey'; + ctx.fillRect(-30, -5, 60, 10); + + const canvas2 = new OffscreenCanvas(100, 100); + const ctx2 = canvas2.getContext('2d'); + ctx2.fillStyle = 'blue'; + ctx2.fillRect(0, 0, 40, 10); + ctx.drawImage(canvas2, -30, -30); +</script>
diff --git a/third_party/blink/web_tests/external/wpt/html/canvas/element/layers/2d.layer.ctm.shadow-in-transformed-layer.html b/third_party/blink/web_tests/external/wpt/html/canvas/element/layers/2d.layer.ctm.shadow-in-transformed-layer.html new file mode 100644 index 0000000..f047bd3 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/html/canvas/element/layers/2d.layer.ctm.shadow-in-transformed-layer.html
@@ -0,0 +1,31 @@ +<!DOCTYPE html> +<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. --> +<link rel="match" href="2d.layer.ctm.shadow-in-transformed-layer-expected.html"> +<title>Canvas test: 2d.layer.ctm.shadow-in-transformed-layer</title> +<h1>2d.layer.ctm.shadow-in-transformed-layer</h1> +<p class="desc">Check shadows inside of a transformed layer.</p> +<canvas id="canvas" width="200" height="200"> + <p class="fallback">FAIL (fallback content)</p> +</canvas> +<script> + const canvas = document.getElementById("canvas"); + const ctx = canvas.getContext('2d'); + + ctx.translate(80, 90); + ctx.scale(2, 2); + ctx.rotate(Math.PI / 2); + + ctx.beginLayer(); + ctx.shadowOffsetX = 10; + ctx.shadowOffsetY = 10; + ctx.shadowColor = 'grey'; + ctx.fillRect(-30, -5, 60, 10); + + const canvas2 = new OffscreenCanvas(100, 100); + const ctx2 = canvas2.getContext('2d'); + ctx2.fillStyle = 'blue'; + ctx2.fillRect(0, 0, 40, 10); + ctx.drawImage(canvas2, -30, -30); + + ctx.endLayer(); +</script>
diff --git a/third_party/blink/web_tests/external/wpt/html/canvas/offscreen/layers/2d.layer.ctm.shadow-in-transformed-layer-expected.html b/third_party/blink/web_tests/external/wpt/html/canvas/offscreen/layers/2d.layer.ctm.shadow-in-transformed-layer-expected.html new file mode 100644 index 0000000..312ca19b --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/html/canvas/offscreen/layers/2d.layer.ctm.shadow-in-transformed-layer-expected.html
@@ -0,0 +1,27 @@ +<!DOCTYPE html> +<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. --> +<title>Canvas test: 2d.layer.ctm.shadow-in-transformed-layer</title> +<h1>2d.layer.ctm.shadow-in-transformed-layer</h1> +<p class="desc">Check shadows inside of a transformed layer.</p> +<canvas id="canvas" width="200" height="200"> + <p class="fallback">FAIL (fallback content)</p> +</canvas> +<script> + const canvas = document.getElementById("canvas"); + const ctx = canvas.getContext('2d'); + + ctx.translate(80, 90); + ctx.scale(2, 2); + ctx.rotate(Math.PI / 2); + + ctx.shadowOffsetX = 10; + ctx.shadowOffsetY = 10; + ctx.shadowColor = 'grey'; + ctx.fillRect(-30, -5, 60, 10); + + const canvas2 = new OffscreenCanvas(100, 100); + const ctx2 = canvas2.getContext('2d'); + ctx2.fillStyle = 'blue'; + ctx2.fillRect(0, 0, 40, 10); + ctx.drawImage(canvas2, -30, -30); +</script>
diff --git a/third_party/blink/web_tests/external/wpt/html/canvas/offscreen/layers/2d.layer.ctm.shadow-in-transformed-layer.html b/third_party/blink/web_tests/external/wpt/html/canvas/offscreen/layers/2d.layer.ctm.shadow-in-transformed-layer.html new file mode 100644 index 0000000..59305076 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/html/canvas/offscreen/layers/2d.layer.ctm.shadow-in-transformed-layer.html
@@ -0,0 +1,34 @@ +<!DOCTYPE html> +<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. --> +<link rel="match" href="2d.layer.ctm.shadow-in-transformed-layer-expected.html"> +<title>Canvas test: 2d.layer.ctm.shadow-in-transformed-layer</title> +<h1>2d.layer.ctm.shadow-in-transformed-layer</h1> +<p class="desc">Check shadows inside of a transformed layer.</p> +<canvas id="canvas" width="200" height="200"> + <p class="fallback">FAIL (fallback content)</p> +</canvas> +<script> + const canvas = new OffscreenCanvas(200, 200); + const ctx = canvas.getContext('2d'); + + ctx.translate(80, 90); + ctx.scale(2, 2); + ctx.rotate(Math.PI / 2); + + ctx.beginLayer(); + ctx.shadowOffsetX = 10; + ctx.shadowOffsetY = 10; + ctx.shadowColor = 'grey'; + ctx.fillRect(-30, -5, 60, 10); + + const canvas2 = new OffscreenCanvas(100, 100); + const ctx2 = canvas2.getContext('2d'); + ctx2.fillStyle = 'blue'; + ctx2.fillRect(0, 0, 40, 10); + ctx.drawImage(canvas2, -30, -30); + + ctx.endLayer(); + + const outputCanvas = document.getElementById("canvas"); + outputCanvas.getContext('2d').drawImage(canvas, 0, 0); +</script>
diff --git a/third_party/blink/web_tests/external/wpt/html/canvas/offscreen/layers/2d.layer.ctm.shadow-in-transformed-layer.w.html b/third_party/blink/web_tests/external/wpt/html/canvas/offscreen/layers/2d.layer.ctm.shadow-in-transformed-layer.w.html new file mode 100644 index 0000000..486a028 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/html/canvas/offscreen/layers/2d.layer.ctm.shadow-in-transformed-layer.w.html
@@ -0,0 +1,48 @@ +<!DOCTYPE html> +<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. --> +<html class="reftest-wait"> +<link rel="match" href="2d.layer.ctm.shadow-in-transformed-layer-expected.html"> +<title>Canvas test: 2d.layer.ctm.shadow-in-transformed-layer</title> +<h1>2d.layer.ctm.shadow-in-transformed-layer</h1> +<p class="desc">Check shadows inside of a transformed layer.</p> +<canvas id="canvas" width="200" height="200"> + <p class="fallback">FAIL (fallback content)</p> +</canvas> +<script id='myWorker' type='text/worker'> + self.onmessage = function(e) { + const canvas = new OffscreenCanvas(200, 200); + const ctx = canvas.getContext('2d'); + + ctx.translate(80, 90); + ctx.scale(2, 2); + ctx.rotate(Math.PI / 2); + + ctx.beginLayer(); + ctx.shadowOffsetX = 10; + ctx.shadowOffsetY = 10; + ctx.shadowColor = 'grey'; + ctx.fillRect(-30, -5, 60, 10); + + const canvas2 = new OffscreenCanvas(100, 100); + const ctx2 = canvas2.getContext('2d'); + ctx2.fillStyle = 'blue'; + ctx2.fillRect(0, 0, 40, 10); + ctx.drawImage(canvas2, -30, -30); + + ctx.endLayer(); + + const bitmap = canvas.transferToImageBitmap(); + self.postMessage(bitmap, bitmap); + }; +</script> +<script> + const blob = new Blob([document.getElementById('myWorker').textContent]); + const worker = new Worker(URL.createObjectURL(blob)); + worker.addEventListener('message', msg => { + const outputCtx = document.getElementById("canvas").getContext('2d'); + outputCtx.drawImage(msg.data, 0, 0); + document.documentElement.classList.remove("reftest-wait"); + }); + worker.postMessage(null); +</script> +</html>
diff --git a/third_party/blink/web_tests/external/wpt/html/canvas/tools/yaml-new/layers.yaml b/third_party/blink/web_tests/external/wpt/html/canvas/tools/yaml-new/layers.yaml index 070f6506..938cc74 100644 --- a/third_party/blink/web_tests/external/wpt/html/canvas/tools/yaml-new/layers.yaml +++ b/third_party/blink/web_tests/external/wpt/html/canvas/tools/yaml-new/layers.yaml
@@ -387,6 +387,43 @@ </g> </svg> +- name: 2d.layer.ctm.shadow-in-transformed-layer + desc: Check shadows inside of a transformed layer. + size: [200, 200] + code: | + ctx.translate(80, 90); + ctx.scale(2, 2); + ctx.rotate(Math.PI / 2); + + ctx.beginLayer(); + ctx.shadowOffsetX = 10; + ctx.shadowOffsetY = 10; + ctx.shadowColor = 'grey'; + ctx.fillRect(-30, -5, 60, 10); + + const canvas2 = new OffscreenCanvas(100, 100); + const ctx2 = canvas2.getContext('2d'); + ctx2.fillStyle = 'blue'; + ctx2.fillRect(0, 0, 40, 10); + ctx.drawImage(canvas2, -30, -30); + + ctx.endLayer(); + reference: | + ctx.translate(80, 90); + ctx.scale(2, 2); + ctx.rotate(Math.PI / 2); + + ctx.shadowOffsetX = 10; + ctx.shadowOffsetY = 10; + ctx.shadowColor = 'grey'; + ctx.fillRect(-30, -5, 60, 10); + + const canvas2 = new OffscreenCanvas(100, 100); + const ctx2 = canvas2.getContext('2d'); + ctx2.fillStyle = 'blue'; + ctx2.fillRect(0, 0, 40, 10); + ctx.drawImage(canvas2, -30, -30); + - name: 2d.layer.ctm.getTransform desc: Tests getTransform inside layers. code: |
diff --git a/third_party/blink/web_tests/fast/canvas/canvas-toBlob-jpeg-medium-quality.html b/third_party/blink/web_tests/fast/canvas/canvas-toBlob-jpeg-medium-quality.html index d4825aa..20bc7b0 100644 --- a/third_party/blink/web_tests/fast/canvas/canvas-toBlob-jpeg-medium-quality.html +++ b/third_party/blink/web_tests/fast/canvas/canvas-toBlob-jpeg-medium-quality.html
@@ -1,4 +1,5 @@ <canvas id="mycanvas"></canvas> +<meta name=fuzzy content="maxDifference=0-1; totalPixels=0-20000"> <img id="result" onload="testDone()"> <script type = 'text/javascript'> if (window.testRunner)
diff --git a/third_party/catapult b/third_party/catapult index 6f4a0d6..7fccada 160000 --- a/third_party/catapult +++ b/third_party/catapult
@@ -1 +1 @@ -Subproject commit 6f4a0d6c8731078f74f3de4fc92748c125da43d6 +Subproject commit 7fccadad2a22e004112191dcd082108eb8297b14
diff --git a/third_party/chromite b/third_party/chromite index 21c56f5..7efba5e 160000 --- a/third_party/chromite +++ b/third_party/chromite
@@ -1 +1 @@ -Subproject commit 21c56f5c330bab014098f49c6d5b9f2f9cba3473 +Subproject commit 7efba5ebf4460a8038d1d8155528d960f8df82ab
diff --git a/third_party/chromium-variations b/third_party/chromium-variations index 1fae85c..d1a16c4 160000 --- a/third_party/chromium-variations +++ b/third_party/chromium-variations
@@ -1 +1 @@ -Subproject commit 1fae85ca70bdf203941f8ac90392e7d471247147 +Subproject commit d1a16c439233c3c7199fcc3f0507b1d012611ab9
diff --git a/third_party/dawn b/third_party/dawn index 996ab52..7b482f4 160000 --- a/third_party/dawn +++ b/third_party/dawn
@@ -1 +1 @@ -Subproject commit 996ab52aeac453060d7677672ce28e0504ff75f3 +Subproject commit 7b482f48e63500c46952160a5b71b5bb91c746f0
diff --git a/third_party/depot_tools b/third_party/depot_tools index 4df6114..d972b83 160000 --- a/third_party/depot_tools +++ b/third_party/depot_tools
@@ -1 +1 @@ -Subproject commit 4df61147ba67316806617f74347f408d2e4ff2f1 +Subproject commit d972b831c3ca2be979c4009a1fb66baca9dc0b77
diff --git a/third_party/devtools-frontend-internal b/third_party/devtools-frontend-internal index 2173cbd..f162658 160000 --- a/third_party/devtools-frontend-internal +++ b/third_party/devtools-frontend-internal
@@ -1 +1 @@ -Subproject commit 2173cbdf60dac9a327293104c19c93e63a3e46ee +Subproject commit f1626580921976b4de940622ede2cdd7a6911c91
diff --git a/third_party/jni_zero/codegen/placeholder_java_type.py b/third_party/jni_zero/codegen/placeholder_java_type.py new file mode 100644 index 0000000..5d0b766 --- /dev/null +++ b/third_party/jni_zero/codegen/placeholder_java_type.py
@@ -0,0 +1,27 @@ +# Copyright 2024 The Chromium Authors +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + + +def Generate(java_class, nested_classes, script_name): + """Generate an empty placeholder java class with a given name and package. + + The placeholder files allow compiling FooJni.java without requiring access to + Foo.java or any of its deps/imports. + """ + sb = [] + sb.append(f"""\ +// +// This file is an empty placeholder. It was generated by {script_name} +// + +package {java_class.package_with_dots}; + +public class {java_class.name} {{ +""") + if nested_classes: + for clazz in nested_classes: + sb.append(f'public class {clazz.nested_name} {{}}\n') + + sb.append('}\n') + return ''.join(sb)
diff --git a/third_party/jni_zero/jni_generator.py b/third_party/jni_zero/jni_generator.py index 02797bc..c3097fc 100644 --- a/third_party/jni_zero/jni_generator.py +++ b/third_party/jni_zero/jni_generator.py
@@ -26,6 +26,7 @@ sys.path.insert(1, _BUILD_ANDROID_GYP) from codegen import placeholder_gen_jni_java +from codegen import placeholder_java_type from codegen import proxy_impl_java import common import java_types @@ -1141,11 +1142,42 @@ zip_path = f'{jni_obj.java_class.class_without_prefix.full_name_with_slashes}Jni.java' common.add_to_zip_hermetic(srcjar, zip_path, data=content) + if gen_jni_class is not None: + content = placeholder_gen_jni_java.Generate(jni_objs, + gen_jni_class=gen_jni_class, + script_name=script_name) + zip_path = f'{gen_jni_class.full_name_with_slashes}.java' + common.add_to_zip_hermetic(srcjar, zip_path, data=content) + + +def _CreatePlaceholderSrcJar(srcjar_path, gen_jni_class, jni_objs, *, + script_name): + with common.atomic_output(srcjar_path) as f: + with zipfile.ZipFile(f, 'w') as srcjar: content = placeholder_gen_jni_java.Generate(jni_objs, gen_jni_class=gen_jni_class, script_name=script_name) zip_path = f'{gen_jni_class.full_name_with_slashes}.java' common.add_to_zip_hermetic(srcjar, zip_path, data=content) + for jni_obj in jni_objs: + if not jni_obj.proxy_natives: + continue + main_class = jni_obj.type_resolver.java_class + zip_path = main_class.class_without_prefix.full_name_with_slashes + '.java' + content = placeholder_java_type.Generate( + main_class, + jni_obj.type_resolver.nested_classes, + script_name=script_name) + common.add_to_zip_hermetic(srcjar, zip_path, data=content) + for java_class in jni_obj.type_resolver.imports: + # java.** are not separate deps and can be assumed to be available in + # any compile and thus we do not need to create placeholders for them. + if java_class.full_name_with_slashes.startswith('java/'): + continue + zip_path = java_class.class_without_prefix.full_name_with_slashes + '.java' + content = placeholder_java_type.Generate(java_class, [], + script_name=script_name) + common.add_to_zip_hermetic(srcjar, zip_path, data=content) def _WriteHeaders(jni_objs, output_names, output_dir): @@ -1180,23 +1212,33 @@ _WriteHeaders(jni_objs, args.output_names, args.output_dir) + jni_objs_with_proxy_natives = [x for x in jni_objs if x.proxy_natives] + if jni_objs_with_proxy_natives: + # module_name is set only for proxy_natives. + gen_jni_class = proxy.get_gen_jni_class( + short=False, + name_prefix=jni_objs_with_proxy_natives[0].module_name, + package_prefix=args.package_prefix) # Write .srcjar if args.srcjar_path: - # module_name is set only for proxy_natives. - jni_objs = [x for x in jni_objs if x.proxy_natives] - if jni_objs: - gen_jni_class = proxy.get_gen_jni_class( - short=False, - name_prefix=jni_objs[0].module_name, - package_prefix=args.package_prefix) + if jni_objs_with_proxy_natives: _CreateSrcJar(args.srcjar_path, - gen_jni_class, - jni_objs, + (None if args.placeholder_srcjar_path else gen_jni_class), + jni_objs_with_proxy_natives, script_name=GetScriptName()) else: # Only @CalledByNatives. zipfile.ZipFile(args.srcjar_path, 'w').close() + if args.placeholder_srcjar_path: + if jni_objs_with_proxy_natives: + _CreatePlaceholderSrcJar(args.placeholder_srcjar_path, + gen_jni_class, + jni_objs_with_proxy_natives, + script_name=GetScriptName()) + else: + zipfile.ZipFile(args.placeholder_srcjar_path, 'w').close() + def GenerateFromJar(parser, args): if not args.javap:
diff --git a/third_party/jni_zero/jni_zero.py b/third_party/jni_zero/jni_zero.py index 43bf7b5..ffe309f 100755 --- a/third_party/jni_zero/jni_zero.py +++ b/third_party/jni_zero/jni_zero.py
@@ -53,6 +53,9 @@ help='Output directory. ' 'Existing .h files in this directory will be assumed ' 'stale and removed.') + group.add_argument('--placeholder-srcjar-path', + help='Path to output srcjar with placeholders for ' + 'all referenced classes in |input_files|') group.add_argument('--header-path', help='Path to output header file.')
diff --git a/third_party/jni_zero/parse.py b/third_party/jni_zero/parse.py index 040d1a4..33203b98 100644 --- a/third_party/jni_zero/parse.py +++ b/third_party/jni_zero/parse.py
@@ -135,7 +135,7 @@ nested_classes = [] for m in _CLASSES_REGEX.finditer(contents): preamble, class_name = m.groups() - # Ignore annoations like @Foo("contains the words class Bar") + # Ignore annotations like @Foo("contains the words class Bar") if preamble.count('"') % 2 != 0: continue if outer_class is None:
diff --git a/third_party/jni_zero/sample/BUILD.gn b/third_party/jni_zero/sample/BUILD.gn index a671cb7..56aba3a3 100644 --- a/third_party/jni_zero/sample/BUILD.gn +++ b/third_party/jni_zero/sample/BUILD.gn
@@ -6,16 +6,14 @@ import("//third_party/jni_zero/jni_zero.gni") generate_jni("sample_header") { - sources = [ - "java/src/org/jni_zero/sample/Sample.java", - ] + sources = [ "java/src/org/jni_zero/sample/Sample.java" ] } android_library("sample_java") { srcjar_deps = [ ":sample_header" ] sources = [ - "java/src/org/jni_zero/sample/SampleActivity.java", "java/src/org/jni_zero/sample/Sample.java", + "java/src/org/jni_zero/sample/SampleActivity.java", ] deps = [ @@ -29,9 +27,7 @@ ":sample_header", "//base", ] - sources = [ - "sample.cc", - ] + sources = [ "sample.cc" ] } shared_library_with_jni("sample_lib") { @@ -79,5 +75,8 @@ # Serves to test that generated bindings compile properly. group("jni_generator_tests") { - deps = [ ":sample_jni_apk" ] + deps = [ + ":sample_jni_apk", + "//third_party/jni_zero/test:compile_only_test_jni_apk", + ] }
diff --git a/third_party/jni_zero/test/BUILD.gn b/third_party/jni_zero/test/BUILD.gn index e4e995a8..ef60bff9 100644 --- a/third_party/jni_zero/test/BUILD.gn +++ b/third_party/jni_zero/test/BUILD.gn
@@ -4,6 +4,9 @@ import("//build/config/android/rules.gni") import("//third_party/jni_zero/jni_zero.gni") +# All targets in this file are meant as compile tests only to make sure the +# generated code compiles for all edgecases. + generate_jni("test_jni") { sources = [ "java/src/org/jni_zero/SampleForAnnotationProcessor.java", @@ -11,6 +14,13 @@ ] } +android_library("dummy_java") { + sources = [ + "java/src/org/dummy/MyClass.java", + "java/src/org/dummy/MyInterface.java", + ] +} + android_library("test_java") { srcjar_deps = [ ":test_jni" ] sources = [ @@ -18,7 +28,10 @@ "java/src/org/jni_zero/SampleForTests.java", ] - deps = [ "//third_party/jni_zero:jni_zero_java" ] + deps = [ + ":dummy_java", + "//third_party/jni_zero:jni_zero_java", + ] } source_set("test_native_side") { @@ -39,11 +52,15 @@ ":test_native_side", "//third_party/jni_zero", ] - java_targets = [ ":test_jni_apk" ] + java_targets = [ ":compile_only_test_jni_apk" ] remove_uncalled_jni = true } -android_apk("test_jni_apk") { +# This apk is meant to be a compile only test to make sure the generated code, +# both java and native, compiles for all the edgecases. APK itself is not meant +# to be run or installed on a device since a lot of internal implementations are +# empty stubs and are just meant for the compiler or linker to see. +android_apk("compile_only_test_jni_apk") { apk_name = "TestJni" android_manifest = "../sample/AndroidManifest.xml" deps = [ ":test_java" ]
diff --git a/third_party/jni_zero/test/golden/testEndToEndProxyHashed-placeholder.srcjar.golden b/third_party/jni_zero/test/golden/testEndToEndProxyHashed-placeholder.srcjar.golden new file mode 100644 index 0000000..0c7f5d01 --- /dev/null +++ b/third_party/jni_zero/test/golden/testEndToEndProxyHashed-placeholder.srcjar.golden
@@ -0,0 +1,99 @@ +This is the concatenated contents of all files inside the placeholder srcjar. + + +## Contents of org/jni_zero/GEN_JNI.java: +// +// This file is a placeholder. It was generated by third_party/jni_zero/jni_generator.py +// + +package org.jni_zero; + +public class GEN_JNI { + public static boolean TESTING_ENABLED; + public static boolean REQUIRE_MOCK; + + public static native Object org_jni_1zero_SampleForAnnotationProcessor_bar(Object sample); + public static native void org_jni_1zero_SampleForAnnotationProcessor_foo(); + public static native boolean org_jni_1zero_SampleForAnnotationProcessor_hasPhalange(); + public static native Class org_jni_1zero_SampleForAnnotationProcessor_returnClass(); + public static native Class[] org_jni_1zero_SampleForAnnotationProcessor_returnClasses(); + public static native String org_jni_1zero_SampleForAnnotationProcessor_returnConvertedString(); + public static native String[] org_jni_1zero_SampleForAnnotationProcessor_returnConvertedStrings(); + public static native Object org_jni_1zero_SampleForAnnotationProcessor_returnObject(); + public static native Object[] org_jni_1zero_SampleForAnnotationProcessor_returnObjects(); + public static native String org_jni_1zero_SampleForAnnotationProcessor_returnString(); + public static native String[] org_jni_1zero_SampleForAnnotationProcessor_returnStrings(); + public static native Object org_jni_1zero_SampleForAnnotationProcessor_returnStruct(); + public static native Object[] org_jni_1zero_SampleForAnnotationProcessor_returnStructs(); + public static native Throwable org_jni_1zero_SampleForAnnotationProcessor_returnThrowable(); + public static native Throwable[] org_jni_1zero_SampleForAnnotationProcessor_returnThrowables(); + public static native String org_jni_1zero_SampleForAnnotationProcessor_revString(String stringToReverse); + public static native Object[] org_jni_1zero_SampleForAnnotationProcessor_sendSamplesToNative(Object[] strs); + public static native String[] org_jni_1zero_SampleForAnnotationProcessor_sendToNative(String[] strs); + public static native int[] org_jni_1zero_SampleForAnnotationProcessor_testAllPrimitives(int zint, int[] ints, long zlong, long[] longs, short zshort, short[] shorts, char zchar, char[] chars, byte zbyte, byte[] bytes, double zdouble, double[] doubles, float zfloat, float[] floats, boolean zbool, boolean[] bools); + public static native void org_jni_1zero_SampleForAnnotationProcessor_testSpecialTypes(Class clazz, Class[] classes, Throwable throwable, Throwable[] throwables, String string, String[] strings, String convertedString, String[] convertedStrings, String optionalString, Object tStruct, Object[] structs, Object obj, Object convertedObj, Object[] objects, Object[] convertedObjects); +} + + + +## Contents of org/jni_zero/SampleForAnnotationProcessor.java: +// +// This file is an empty placeholder. It was generated by third_party/jni_zero/jni_generator.py +// + +package org.jni_zero; + +public class SampleForAnnotationProcessor { +public class TestStruct {} +public class Natives {} +} + + + +## Contents of android/content/Context.java: +// +// This file is an empty placeholder. It was generated by third_party/jni_zero/jni_generator.py +// + +package android.content; + +public class Context { +} + + + +## Contents of android/view/View.java: +// +// This file is an empty placeholder. It was generated by third_party/jni_zero/jni_generator.py +// + +package android.view; + +public class View { +} + + + +## Contents of org/dummy/MyClass.java: +// +// This file is an empty placeholder. It was generated by third_party/jni_zero/jni_generator.py +// + +package org.dummy; + +public class MyClass { +} + + + +## Contents of org/dummy/MyInterface.java: +// +// This file is an empty placeholder. It was generated by third_party/jni_zero/jni_generator.py +// + +package org.dummy; + +public class MyInterface { +} + +
diff --git a/third_party/jni_zero/test/integration_tests.py b/third_party/jni_zero/test/integration_tests.py index 5ebd216..d30a654 100755 --- a/third_party/jni_zero/test/integration_tests.py +++ b/third_party/jni_zero/test/integration_tests.py
@@ -121,7 +121,24 @@ contents = srcjar.read(name).decode('utf-8') self.AssertGoldenTextEquals(contents, name_to_goldens[name]) - def _TestEndToEndGeneration(self, input_file, *, srcjar=False, **kwargs): + def _CheckPlaceholderSrcjarGolden(self, srcjar_path, golden_path): + expected_contents = [ + 'This is the concatenated contents of all files ' + 'inside the placeholder srcjar.\n\n' + ] + with zipfile.ZipFile(srcjar_path, 'r') as srcjar: + for name in srcjar.namelist(): + file_contents = srcjar.read(name).decode('utf-8') + expected_contents += [f'## Contents of {name}:', file_contents, '\n'] + + self.AssertGoldenTextEquals('\n'.join(expected_contents), golden_path) + + def _TestEndToEndGeneration(self, + input_file, + *, + srcjar=False, + generate_placeholders=False, + **kwargs): is_javap = input_file.endswith('.class') golden_name = self._testMethodName options = CliOptions(is_javap=is_javap, **kwargs) @@ -152,6 +169,9 @@ if srcjar: srcjar_path = os.path.join(tdir, 'srcjar.jar') cmd += ['--srcjar-path', srcjar_path] + if generate_placeholders: + placeholder_srcjar_path = os.path.join(tdir, 'placeholders.srcjar') + cmd += ['--placeholder-srcjar-path', placeholder_srcjar_path] logging.info('Running: %s', shlex.join(cmd)) subprocess.check_call(cmd) @@ -163,6 +183,10 @@ if srcjar: self._CheckSrcjarGoldens(srcjar_path, name_to_goldens) + if generate_placeholders: + placeholder_srcjar_golden = f'{golden_name}-placeholder.srcjar.golden' + self._CheckPlaceholderSrcjarGolden(placeholder_srcjar_path, + placeholder_srcjar_golden) def _TestEndToEndRegistration(self, input_files, @@ -310,7 +334,8 @@ self._TestEndToEndGeneration('SampleUniqueAnnotations.java', srcjar=True) def testEndToEndProxyHashed(self): - self._TestEndToEndGeneration('SampleForAnnotationProcessor.java') + self._TestEndToEndGeneration('SampleForAnnotationProcessor.java', + generate_placeholders=True) self._TestEndToEndRegistration(['SampleForAnnotationProcessor.java'], use_proxy_hash=True)
diff --git a/third_party/jni_zero/test/java/src/org/dummy/MyClass.java b/third_party/jni_zero/test/java/src/org/dummy/MyClass.java new file mode 100644 index 0000000..7ffb5a5a --- /dev/null +++ b/third_party/jni_zero/test/java/src/org/dummy/MyClass.java
@@ -0,0 +1,7 @@ +// Copyright 2024 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.dummy; + +public class MyClass<T> {}
diff --git a/third_party/jni_zero/test/java/src/org/dummy/MyInterface.java b/third_party/jni_zero/test/java/src/org/dummy/MyInterface.java new file mode 100644 index 0000000..ca9a5e12 --- /dev/null +++ b/third_party/jni_zero/test/java/src/org/dummy/MyInterface.java
@@ -0,0 +1,7 @@ +// Copyright 2024 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.dummy; + +public interface MyInterface {}
diff --git a/third_party/jni_zero/test/java/src/org/jni_zero/SampleForAnnotationProcessor.java b/third_party/jni_zero/test/java/src/org/jni_zero/SampleForAnnotationProcessor.java index 255c77d6..b610a6d 100644 --- a/third_party/jni_zero/test/java/src/org/jni_zero/SampleForAnnotationProcessor.java +++ b/third_party/jni_zero/test/java/src/org/jni_zero/SampleForAnnotationProcessor.java
@@ -1,9 +1,15 @@ // Copyright 2018 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. - package org.jni_zero; +import android.content.Context; +import android.view.View; + +import org.dummy.MyClass; +import org.dummy.MyInterface; + +import java.util.ArrayList; /** * Sample class that uses the JNI annotation processor for static methods. @@ -87,6 +93,13 @@ } public static void test() { + // Using the imports so they are not removed. + View view = null; + Context context = null; + MyClass myclass = null; + MyInterface myinterface = null; + ArrayList myList = new ArrayList<String>(); + int[] x = new int[] {1, 2, 3, 4, 5}; String[] strs = new String[] {"the", "quick", "brown", "fox"}; strs = SampleForAnnotationProcessorJni.get().sendToNative(strs);
diff --git a/third_party/libc++abi/src b/third_party/libc++abi/src index 5b35c9f..204deaa 160000 --- a/third_party/libc++abi/src +++ b/third_party/libc++abi/src
@@ -1 +1 @@ -Subproject commit 5b35c9f06c3f8f17b43bd3da9527d6fecdf916c2 +Subproject commit 204deaa9c53f76d6f23e0d119fc0110adc3ea6f2
diff --git a/third_party/perfetto b/third_party/perfetto index 6427f36..cd05247e 160000 --- a/third_party/perfetto +++ b/third_party/perfetto
@@ -1 +1 @@ -Subproject commit 6427f365ba48d17a474a837b2596ba683655386e +Subproject commit cd05247e5cb7251177910169dfe482f586f6a5bd
diff --git a/third_party/skia b/third_party/skia index 734953a..170fbb0 160000 --- a/third_party/skia +++ b/third_party/skia
@@ -1 +1 @@ -Subproject commit 734953afbc19e81884e465829685d2574fe40fc9 +Subproject commit 170fbb029e61a116431fd96a35eeeca8ee22c2b2
diff --git a/third_party/webrtc b/third_party/webrtc index 524a06b..a8c4727 160000 --- a/third_party/webrtc +++ b/third_party/webrtc
@@ -1 +1 @@ -Subproject commit 524a06bc5423ef2289bc6102ee5a2b9e747137cc +Subproject commit a8c47276cb6d3188db8b5a4ba77abd07797c0071
diff --git a/tools/metrics/actions/actions.xml b/tools/metrics/actions/actions.xml index 2046775e..1141c5d 100644 --- a/tools/metrics/actions/actions.xml +++ b/tools/metrics/actions/actions.xml
@@ -9512,6 +9512,15 @@ </description> </action> +<action name="DownloadNotificationV2.Button_CopyToClipboard"> + <owner>andrewxu@chromium.org</owner> + <owner>cros-system-ui-productivity-eng@google.com</owner> + <description> + User clicks "Copy to clipboard" button on a download notification + with the downloads integration V2 feature enabled. + </description> +</action> + <action name="DownloadNotificationV2.Button_Pause"> <owner>andrewxu@chromium.org</owner> <owner>cros-system-ui-productivity-eng@google.com</owner>
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml index e619bae..c925b7a 100644 --- a/tools/metrics/histograms/enums.xml +++ b/tools/metrics/histograms/enums.xml
@@ -11676,6 +11676,7 @@ <int value="36" label="OOBE or Login screen"/> <int value="37" label="AI"/> <int value="38" label="Focus Mode"/> + <int value="39" label="Overview Grid"/> </enum> <!-- Keep elements in sync with components/feed/core/feed_logging_metrics.cc and @@ -32337,6 +32338,7 @@ <int value="59" label="UMA_CREATOR_UNFOLLOW_FAILURE"/> <int value="60" label="UMA_QUICK_DELETE"/> <int value="61" label="UMA_AUTO_TRANSLATE"/> + <int value="63" label="UMA_CLEAR_BROWSING_DATA"/> </enum> <enum name="SnapshotItemId">
diff --git a/tools/metrics/histograms/metadata/apps/histograms.xml b/tools/metrics/histograms/metadata/apps/histograms.xml index 24f3791c..ee4a44e6 100644 --- a/tools/metrics/histograms/metadata/apps/histograms.xml +++ b/tools/metrics/histograms/metadata/apps/histograms.xml
@@ -439,19 +439,6 @@ </summary> </histogram> -<histogram name="Apps.AppList.AppLaunched{TabletOrClamshell}" - enum="AppListLaunchedFrom" expires_after="2023-06-19"> - <owner>gzadina@google.com</owner> - <owner>tbarzic@chromium.org</owner> - <summary> - The number of apps launched from different parts of the launcher (apps grid, - search results UI, recent apps), and the shelf. Values are incremented each - time the user launches an app. Each bucket represents where in the launcher - or shelf the app was launched from. Recorded for {TabletOrClamshell}. - </summary> - <token key="TabletOrClamshell" variants="TabletOrClamshellMode"/> -</histogram> - <histogram name="Apps.AppList.AppListSortDiscoveryDurationAfterActivation" units="ms" expires_after="2023-06-25"> <owner>andrewxu@chromium.org</owner> @@ -1410,46 +1397,6 @@ <token key="TabletOrClamshell" variants="TabletOrClamshellMode"/> </histogram> -<histogram name="Apps.AppList.UserEvent.LaunchIndex{LauncherUISurface}" - units="index" expires_after="2023-07-31"> - <owner>tby@chromium.org</owner> - <owner>thanhdng@chromium.org</owner> - <summary> - Emitted on a usage of the launcher, and records overall impressions, - launches, and abandons for a launcher UI view. The bucket proportion is not - meaningful for this metric, because impressions are a superset of launches - and abandons. Instead, the ratio between buckets can be used to calculate - accurate overall CTR. {LauncherUISurface} - - This metric stopped collecting data in November 2021, but has not yet been - marked obsolete because the historical data is still being used for - analysis. - </summary> - <token key="LauncherUISurface" variants="LauncherUISurface"> - <variant name=""/> - </token> -</histogram> - -<histogram name="Apps.AppList.UserEvent.Overall{LauncherUISurface}" - enum="AppListUserEvent" expires_after="2023-07-31"> - <owner>tby@chromium.org</owner> - <owner>thanhdng@chromium.org</owner> - <summary> - Emitted on a usage of the launcher, and records overall impressions, - launches, and abandons for a launcher UI view. The bucket proportion is not - meaningful for this metric, because impressions are a superset of launches - and abandons. Instead, the ratio between buckets can be used to calculate - accurate overall CTR. {LauncherUISurface} - - This metric stopped collecting data in November 2021, but has not yet been - marked obsolete because the historical data is still being used for - analysis. - </summary> - <token key="LauncherUISurface" variants="LauncherUISurface"> - <variant name=""/> - </token> -</histogram> - <histogram name="Apps.AppList.UserEvent.Query" enum="Boolean" expires_after="2023-07-31"> <owner>tby@chromium.org</owner> @@ -1925,24 +1872,6 @@ </summary> </histogram> -<histogram name="Apps.AppListUsageByNewUsers{TabletOrClamshell}" - enum="AppListUsageStateByNewUsers" expires_after="2023-06-19"> - <owner>andrewxu@chromium.org</owner> - <owner>tbarzic@chromium.org</owner> - <summary> - Records the launcher usage state during the session started by a new user - (i.e. the session completing the OOBE flow). This metric is recorded in the - following scenarios: (1) the launcher shows and the current user is new (2) - the launcher has never shown before launcher is destructed. Destruction can - be triggered by loging out accounts, shuting down the device or system - crashes and meanwhile the current user is new (3) the launcher has never - shown when the active user has changed and the previous active user was a - new user. Split depending on whether the device was in tablet or clamshell - mode when the metric was recorded. - </summary> - <token key="TabletOrClamshell" variants="TabletOrClamshellMode"/> -</histogram> - <histogram name="Apps.AppsCount.{AppType}" units="Apps" expires_after="2024-08-04"> <owner>nancylingwang@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/ash/histograms.xml b/tools/metrics/histograms/metadata/ash/histograms.xml index 45cf0cd..4d20321 100644 --- a/tools/metrics/histograms/metadata/ash/histograms.xml +++ b/tools/metrics/histograms/metadata/ash/histograms.xml
@@ -8273,7 +8273,7 @@ </histogram> <histogram name="Ash.WindowCycleView.AnimationSmoothness.Container" units="%" - expires_after="2024-02-22"> + expires_after="2025-02-27"> <owner>yichenz@chromium.org</owner> <owner>chromeos-wmp@google.com</owner> <summary> @@ -8288,7 +8288,7 @@ </histogram> <histogram name="Ash.WindowCycleView.AnimationSmoothness.Show" units="%" - expires_after="2024-02-22"> + expires_after="2025-02-27"> <owner>yichenz@chromium.org</owner> <owner>chromeos-wmp@google.com</owner> <summary>
diff --git a/tools/metrics/histograms/metadata/enterprise/enums.xml b/tools/metrics/histograms/metadata/enterprise/enums.xml index c2d3748..db7b9ca 100644 --- a/tools/metrics/histograms/metadata/enterprise/enums.xml +++ b/tools/metrics/histograms/metadata/enterprise/enums.xml
@@ -2064,6 +2064,7 @@ <int value="1230" label="DefaultDirectSocketsSetting"/> <int value="1231" label="DirectSocketsAllowedForUrls"/> <int value="1232" label="DirectSocketsBlockedForUrls"/> + <int value="1233" label="ProductSpecificationsEnabled"/> </enum> <enum name="EnterprisePoliciesSources">
diff --git a/tools/metrics/structured/sync/structured_chromiumos.xml b/tools/metrics/structured/sync/structured_chromiumos.xml index 7e52165f..a1ff3cc 100644 --- a/tools/metrics/structured/sync/structured_chromiumos.xml +++ b/tools/metrics/structured/sync/structured_chromiumos.xml
@@ -584,6 +584,205 @@ </event> </project> +<project name="CrashReporting"> + <owner>chromeos-data-eng@google.com</owner> + <id>none</id> + <summary> + Project for the crash reporting system to provide metrics on how well + crash reporting is working and whether or not we are losing crashes. + </summary> + <event name="CrashpadDetect"> + <summary> + Chrome's crashpad has detected a crash. + </summary> + <metric name="Dummy" type="int"> + <summary> + Dummy metric to work around b/326452906 (events must have at least one + metric). + </summary> + </metric> + </event> + <event name="CrashReporterStart"> + <summary> + A crash reporter collector has started Collect()ing a crash. Usually + recorded once per invocation, other than --init invocations; may be + recorded twice for "crash reporter failure" results. + </summary> + <metric name="Collector" type="int"> + <summary> + Value from the CrashReporterCollector enum, indicating the collector + that is running. + </summary> + </metric> + <metric name="IsCrashLoop" type="int"> + <summary> + Boolean: 1 if using crash-loop mode. + </summary> + </metric> + </event> + <event name="CrashReporterStatus"> + <summary> + A crash reporter collector has finished Collect()ing a crash, successfully + or otherwise. Usually recorded once per invocation, other than --init + invocations; may be recorded twice for "crash reporter failure" results. + </summary> + <metric name="Status" type="int"> + <summary> + Value from CrashReporterStatus enum, indicating the final result of the + collection attempt. + </summary> + </metric> + <metric name="Collector" type="int"> + <summary> + Value from the CrashReporterCollector enum, indicating the collector + that is running. + </summary> + </metric> + <metric name="IsCrashLoop" type="int"> + <summary> + Boolean: 1 if using crash-loop mode. + </summary> + </metric> + </event> + <event name="CrashReporterInitializationStart"> + <summary> + crash_reporter --init started. Recorded twice per boot (once early and + once late). + </summary> + <metric name="IsEarly" type="int"> + <summary> + Boolean: 1 for early init, 0 for late init + </summary> + </metric> + </event> + <event name="CrashReporterInitializationStatus"> + <summary> + Result of crash_reporter --init. Recorded twice per boot (once early and + once late). + </summary> + <metric name="IsEarly" type="int"> + <summary> + Boolean: 1 for early init, 0 for late init + </summary> + </metric> + <metric name="Status" type="int"> + <summary> + Value from CrashReporterInitStatus enum, indicating the final result of + the initialization attempt. + </summary> + </metric> + </event> + <event name="CrashSenderStart"> + <summary> + crash_sender has started processing a new crash file. Recorded once per + crash send attempt. Not recorded in dry run mode. + </summary> + <metric name="IsCrashLoop" type="int"> + <summary> + Boolean: 1 if crash_sender was invoked by crash_reporter using + crash-loop mode. + </summary> + </metric> + </event> + <event name="CrashSenderStartPerCollector"> + <summary> + crash_sender has started processing a new crash file and has parsed the + .meta file enough to know what collector the crash came from. Recorded + once per crash send attempt, unless we can't parse the .meta file. Not + recorded in dry run mode. + </summary> + <metric name="Collector" type="int"> + <summary> + Value from the CrashReporterCollector enum, indicating the collector + that produced the .meta file. + </summary> + </metric> + <metric name="IsCrashLoop" type="int"> + <summary> + Boolean: 1 if crash_sender was invoked by crash_reporter using + crash-loop mode. + </summary> + </metric> + </event> + <event name="CrashSenderRemovalReason"> + <summary> + If crash_sender removes a crash, records the reason it was removed. + Recorded at most once per crash send attempt. Not recorded in dry run + mode. + </summary> + <metric name="Collector" type="int"> + <summary> + Value from the CrashReporterCollector enum, indicating the collector + that produced the .meta file. kUnknownCollector if we couldn't parse the + .meta file. + </summary> + </metric> + <metric name="Reason" type="int"> + <summary> + Value from CrashRemoveReason enum, indicating the reason crash_sender + removed the crash record. + </summary> + </metric> + <metric name="IsCrashLoop" type="int"> + <summary> + Boolean: 1 if crash_sender was invoked by crash_reporter using + crash-loop mode. + </summary> + </metric> + </event> + <event name="CrashSenderComplete"> + <summary> + crash_sender is finished with a crash send attempt, whether successfully + or otherwise. Recorded once per crash send attempt. Not recorded in dry + run mode. + </summary> + <metric name="Collector" type="int"> + <summary> + Value from the CrashReporterCollector enum, indicating the collector + that produced the .meta file. kUnknownCollector if we couldn't parse the + .meta file. + </summary> + </metric> + <metric name="Removed" type="int"> + <summary> + Boolean: 1 if the crash record was removed, 0 if the crash record was + left for the next time. + </summary> + </metric> + <metric name="IsCrashLoop" type="int"> + <summary> + Boolean: 1 if crash_sender was invoked by crash_reporter using + crash-loop mode. + </summary> + </metric> + </event> + <event name="CrashSenderOrphanFileRemoved"> + <summary> + crash_sender found an old orphaned crash file (a file in the crash + directory that does not correspond to a .meta file and is over 24 hours + old) and attempted to remove it. Recorded once per orphan removal attempt. + </summary> + <metric name="Removed" type="int"> + <summary> + Boolean: 1 if the orphan file was successfully removed, 0 if the orphan + file could not be removed. + </summary> + </metric> + <metric name="IsCrashLoop" type="int"> + <summary> + Boolean: 1 if crash_sender was invoked by crash_reporter using + crash-loop mode. + </summary> + </metric> + <metric name="SeemingCrashFile" type="int"> + <summary> + Boolean: 1 if the removed file matched crash_reporter's normal naming + pattern (execname.date.time.random.pid.extention), 0 otherwise. + </summary> + </metric> + </event> +</project> + <project name="HardwareVerifier"> <owner>kevinptt@chromium.org</owner> <id>per-project</id>
diff --git a/tools/perf/README.md b/tools/perf/README.md index dcc5d15..a119fb7 100644 --- a/tools/perf/README.md +++ b/tools/perf/README.md
@@ -102,19 +102,4 @@ recordings from live websites, replay those to make sure they work, upload them to cloud storage, and finally send a CL to review with the new recordings. -[wpr]: https://github.com/catapult-project/catapult/tree/master/web_page_replay_go - -## `soundwave` - -Allows to fetch data from the [Chrome Performance Dashboard][chromeperf] and -stores it locally on a SQLite database for further analysis and processing. It -also allows defining [studies][], pre-sets of measurements a team is interested -in tracking, and uploads them to cloud storage to visualize with the help of -[Looker Studio][]. This currently backs the [v8][v8_dashboard] and -[health][health_dashboard] dashboards. - -[chromeperf]: https://chromeperf.appspot.com/ -[studies]: https://cs.chromium.org/chromium/src/tools/perf/cli_tools/soundwave/studies/ -[Looker Studio]: https://lookerstudio.google.com/ -[v8_dashboard]: https://lookerstudio.google.com/s/iNcXppkP3DI -[health_dashboard]: https://lookerstudio.google.com/s/jUXfKZXXfT8 +[wpr]: https://github.com/catapult-project/catapult/tree/master/web_page_replay_go \ No newline at end of file
diff --git a/tools/perf/contrib/shared_storage/README.md b/tools/perf/contrib/shared_storage/README.md index c310f4b..8904058 100644 --- a/tools/perf/contrib/shared_storage/README.md +++ b/tools/perf/contrib/shared_storage/README.md
@@ -43,6 +43,8 @@ given number. Default is 10. Maximum allowed is 10. * `--pageset-repeat=PAGESET_REPEAT` Number of times to repeat the entire story set. Default is 10. +* `--xvfb` + Runs tests with Xvfb server if possible. * `--verbose-cpu-metrics` Enables non-UMA CPU metrics. * `--verbose-memory-metrics` @@ -58,5 +60,5 @@ For example, a modified version of the original benchmark command is: ```bash -tools/perf/run_benchmark shared_storage.small --browser=system --story-filter=Append --iterations=5 --pageset-repeat=1 --verbose-cpu-metrics --verbose-memory-metrics --verbose +tools/perf/run_benchmark shared_storage.small --browser=system --story-filter=Append --iterations=5 --pageset-repeat=1 --xvfb --verbose-cpu-metrics --verbose-memory-metrics --verbose ```
diff --git a/tools/perf/contrib/shared_storage/page_set.py b/tools/perf/contrib/shared_storage/page_set.py index 50bafb9b..7dd5320 100644 --- a/tools/perf/contrib/shared_storage/page_set.py +++ b/tools/perf/contrib/shared_storage/page_set.py
@@ -254,8 +254,10 @@ enable_memory_metric, user_agent='desktop', iterations=_PLACEHOLDER_ITERATIONS, - verbosity=0): + verbosity=0, + xvfb_process=None): super(SharedStorageStorySet, self).__init__() + self.xvfb_process = xvfb_process if user_agent == 'mobile': shared_page_state_class = state.SharedStorageSharedMobilePageState elif user_agent == 'desktop':
diff --git a/tools/perf/contrib/shared_storage/shared_storage.py b/tools/perf/contrib/shared_storage/shared_storage.py index 3c8af7f..11f3a9bb 100644 --- a/tools/perf/contrib/shared_storage/shared_storage.py +++ b/tools/perf/contrib/shared_storage/shared_storage.py
@@ -13,6 +13,8 @@ from telemetry.timeline import chrome_trace_category_filter from telemetry.web_perf import timeline_based_measurement +from py_utils import xvfb + # Shared-storage-related histograms to measure. _SHARED_STORAGE_UMA_HISTOGRAMS = [ "Storage.SharedStorage.Document.Timing.AddModule", @@ -66,11 +68,16 @@ verbose_memory_metrics = False iterations = _DEFAULT_NUM_ITERATIONS verbosity = 0 + xvfb_process = None options = {'pageset_repeat': _DEFAULT_NUM_REPEAT} @classmethod def AddBenchmarkCommandLineArgs(cls, parser): + parser.add_option('--xvfb', + action='store_true', + default=False, + help='Run with Xvfb server if possible.') parser.add_option('--user-agent', action='store', type='string', @@ -102,16 +109,21 @@ logging.warning('The maximum allowed number of iterations is 10. ' + 'Increase pageset_repeat instead.') cls.iterations = _MAX_NUM_ITERATIONS + if args.xvfb and xvfb.ShouldStartXvfb(): + cls.xvfb_process = xvfb.StartXvfb() def SetExtraBrowserOptions(self, options): # `options` is an instance of `browser_options.BrowserOptions`. if self.verbose_memory_metrics: memory.SetExtraBrowserOptionsForMemoryMeasurement(options) - options.AppendExtraBrowserArgs([ + extra_args = [ '--enable-features=' + ','.join(_ENABLED_FEATURES), '--enable-privacy-sandbox-ads-apis' - ]) + ] + if self.xvfb_process: + extra_args.append('--disable-gpu') + options.AppendExtraBrowserArgs(extra_args) # Increase the default shutdown timeout due to some long-running tests. os.environ['CHROME_SHUTDOWN_TIMEOUT'] = str(_SHUTDOWN_TIMEOUT) @@ -158,7 +170,8 @@ enable_memory_metric=self.verbose_memory_metrics, user_agent=options.user_agent, iterations=self.iterations, - verbosity=self.verbosity) + verbosity=self.verbosity, + xvfb_process=self.xvfb_process) @benchmark.Info(emails=['cammie@chromium.org'],
diff --git a/tools/perf/contrib/shared_storage/shared_storage_shared_page_state.py b/tools/perf/contrib/shared_storage/shared_storage_shared_page_state.py index 2d257ccc..5106004 100644 --- a/tools/perf/contrib/shared_storage/shared_storage_shared_page_state.py +++ b/tools/perf/contrib/shared_storage/shared_storage_shared_page_state.py
@@ -2,10 +2,25 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. +import logging + +import py_utils + from telemetry.page import shared_page_state class SharedStorageSharedPageState(shared_page_state.SharedPageState): + _xvfb_process = None + + def __init__(self, test, finder_options, story_set, possible_browser): + super(SharedStorageSharedPageState, + self).__init__(test, finder_options, story_set, possible_browser) + + if 'xvfb_process' not in story_set.__dict__: + msg = "story_set is of type %s but expected type " % type(story_set) + msg += "<class 'contrib.shared_storage.page_set.SharedStorageStorySet'>" + raise TypeError(msg) + self._xvfb_process = story_set.xvfb_process def RunStory(self, results): # We use a print statement instead of logging here so that we can display @@ -16,6 +31,34 @@ print('[ RUN #%3d ]' % results.current_story_run.index) super(SharedStorageSharedPageState, self).RunStory(results) + def TearDownState(self): + super(SharedStorageSharedPageState, self).TearDownState() + self._TerminateOrKillXvfbIfNeeded() + + def _TerminateOrKillXvfbIfNeeded(self): + if not self._xvfb_process: + return + + done = False + repr_proc = repr(self._xvfb_process) + try: + self._xvfb_process.terminate() + py_utils.WaitFor(lambda: self._xvfb_process.poll() is not None, 10) + done = True + except py_utils.TimeoutException: + try: + self._xvfb_process.kill() + py_utils.WaitFor(lambda: self._xvfb_process.poll() is not None, 10) + done = True + except py_utils.TimeoutException: + pass + if done: + self._xvfb_process = None + logging.info('Shut down xvfb process: %s' % repr_proc) + else: + msg = 'Failed to terminate/kill the xvfb process %s' % repr_proc + msg += ' after 20 seconds.' + logging.warning(msg) class SharedStorageSharedMobilePageState(SharedStorageSharedPageState): _device_type = 'mobile'
diff --git a/tools/perf/core/find_dependencies.py b/tools/perf/core/find_dependencies.py index 989a6c3..fc9e0b5 100644 --- a/tools/perf/core/find_dependencies.py +++ b/tools/perf/core/find_dependencies.py
@@ -214,25 +214,31 @@ zip_file.writestr(link_info, link_script) -class FindDependenciesCommand(command_line.OptparseCommand): +class FindDependenciesCommand(command_line.Command): """Prints all dependencies""" @classmethod - def AddCommandLineArgs(cls, parser, _): - parser.add_option( - '-v', '--verbose', action='count', dest='verbosity', default=0, - help='Increase verbosity level (repeat as needed).') + def AddCommandLineArgs(cls, parser): + parser.add_argument('-v', + '--verbose', + action='count', + dest='verbosity', + default=0, + help='Increase verbosity level (repeat as needed).') - parser.add_option( - '-e', '--exclude', action='append', default=[], + parser.add_argument( + '-e', + '--exclude', + action='append', + default=[], help='Exclude paths matching EXCLUDE. Can be used multiple times.') - parser.add_option( - '-z', '--zip', - help='Store files in a zip archive at ZIP.') + parser.add_argument('-z', + '--zip', + help='Store files in a zip archive at ZIP.') @classmethod - def ProcessCommandLineArgs(cls, parser, args, _): + def ProcessCommandLineArgs(cls, parser, args): if args.verbosity >= 2: logging.getLogger().setLevel(logging.DEBUG) elif args.verbosity:
diff --git a/tools/perf/experimental/story_clustering/OWNERS b/tools/perf/experimental/story_clustering/OWNERS deleted file mode 100644 index 29a8320..0000000 --- a/tools/perf/experimental/story_clustering/OWNERS +++ /dev/null
@@ -1 +0,0 @@ -behdadb@chromium.org
diff --git a/tools/perf/experimental/story_clustering/README.md b/tools/perf/experimental/story_clustering/README.md deleted file mode 100644 index 419e939..0000000 --- a/tools/perf/experimental/story_clustering/README.md +++ /dev/null
@@ -1,56 +0,0 @@ -# Clustering Benchmark Stories - -The code in this directory provides support for clustering and choosing -representatives for benchmarks. - -Input needed for the clustering methods are: -1. Benchmark name -2. List of metrics to use. - Clustering will be done once for each metric. -3. List of platforms to gather data from. -4. List of test-cases/story-names in the benchmark which should be clustered. - If some stories are recognized as outliers, they can be removed from this - list. The testcases can be provided as a story per line text file. -5. Maximum number of clusters. - The actual number of clusters may be less than this number, as clusters - with only one member are not presented as a cluster. -6. How many days of data history to be used for clustering - -Examples of creating clusters: -```shell -python ./tools/perf/experimental/story_clustering/gather_historical_records_and_cluster_stories.py \ -rendering.desktop \ ---metrics frame_times thread_total_all_cpu_time_per_frame \ ---platforms ChromiumPerf:mac-10_13_laptop_high_end-perf ChromiumPerf:mac-10_12_laptop_low_end-perf \ ---testcases-path //tmp/story_clustering/rendering.desktop/test_cases.txt \ ---days=100 \ ---normalize \ ---processes 20 -``` - -```shell -python ./tools/perf/experimental/story_clustering/gather_historical_records_and_cluster_stories.py \ -rendering.desktop \ ---metrics frame_times thread_total_all_cpu_time_per_frame \ ---platforms 'ChromiumPerf:Win 7 Nvidia GPU Perf' 'ChromiumPerf:Win 7 Perf' ChromiumPerf:win-10-perf \ ---testcases-path //tmp/story_clustering/rendering.desktop/test_cases.txt \ ---days=100 \ ---normalize -``` - -```shell -python ./tools/perf/experimental/story_clustering/gather_historical_records_and_cluster_stories.py \ -rendering.mobile \ ---metrics frame_times thread_total_all_cpu_time_per_frame \ ---platforms 'ChromiumPerf:Android Nexus5 Perf' 'ChromiumPerf:Android Nexus5X WebView Perf' \ -'ChromiumPerf:Android Nexus6 WebView Perf' \ ---testcases-path //tmp/story_clustering/rendering.mobile/test_cases.txt \ ---days=100 \ ---normalize -``` - -Results of the clustering will be written in `clusters.json` file, located in the output directory given to the script - -If the script fails due to a "HTTP Error 429: Rate exceeded" error, try a smaller number for the `--processes` argument (defaulted to 20). - -[Method explanation](https://goto.google.com/chrome-benchmark-clustering) \ No newline at end of file
diff --git a/tools/perf/experimental/story_clustering/__init__.py b/tools/perf/experimental/story_clustering/__init__.py deleted file mode 100644 index e69de29..0000000 --- a/tools/perf/experimental/story_clustering/__init__.py +++ /dev/null
diff --git a/tools/perf/experimental/story_clustering/cluster_stories.py b/tools/perf/experimental/story_clustering/cluster_stories.py deleted file mode 100644 index 3ff47ed..0000000 --- a/tools/perf/experimental/story_clustering/cluster_stories.py +++ /dev/null
@@ -1,156 +0,0 @@ -# Copyright 2019 The Chromium Authors -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -from __future__ import division - -import heapq - - -class Cluster(object): - def __init__(self, members): - """Initializes the cluster instance. - - Args: - members: Set of story names which belong to this cluster. - """ - self._members = frozenset(members) - self._representative = None - - def __len__(self): - return len(self._members) - - def GetDistanceFrom(self, other_cluster, distance_matrix): - """Calculates the distance of two clusters. - - The maximum distance between any story of first cluster to any story of - the second cluster is used as the distance between clusters._members - - Args: - other_cluster: Cluster object to calculate distance from. - distance_matrix: A dataframe containing the distances between any - two stories. - - Returns: - A float number representing the distacne between two clusters. - """ - matrix_slice = distance_matrix.loc[self.members, other_cluster.members] - return matrix_slice.max().max() - - @property - def members(self): - return self._members - - def GetRepresentative(self, distance_matrix=None): - """Finds and sets the representative of cluster. - - The story which its max distance to all other members is minimum is - used as the representative. - - Args: - distance_matrix: A dataframe containing the distances between any - two stories. - - Returns: - A story which is the representative of cluster - """ - if self._representative: - return self._representative - - if distance_matrix is None: - raise Exception('Distance matrix is not set.') - - self._representative = distance_matrix.loc[ - self._members, self._members].sum().idxmin() - return self._representative - - def Merge(self, other_cluster): - """Merges two clusters. - - Returns: - A new cluster object which is a result of merging two clusters. - """ - return Cluster(self.members | other_cluster.members) - - def AsDict(self): - """Creates a dictionary which describes cluster object. - - Returns: - A dictionary containing the members of the cluster and its - representative. The representative will not be listed in members - list. - """ - representative = self.GetRepresentative() - members_list = list(self.members.difference([representative])) - return { - 'members': members_list, - 'representative': self.GetRepresentative() - } - - -def RunHierarchicalClustering( - distance_matrix, - max_cluster_count, - min_cluster_size): - """Clusters stories. - - Runs a hierarchical clustering algorithm based on the similarity measures. - - Args: - distance_matrix: A dataframe containing distance matrix of stories. - max_cluster_count: number representing the maximum number of clusters - needed per metric. - min_cluster_size: number representing the least number of members needed - to make the cluster valid. - - Returns: - A tuple containing: - clusters: A list of cluster objects - coverage: Ratio(float) of stories covered using this clustering - """ - stories = distance_matrix.index.values - remaining_clusters = set([]) - for story in stories: - remaining_clusters.add(Cluster([story])) - - # The hierarchical clustering relies on a sorted list of possible - # cluster merges ordered by the distance between them. - heap = [] - - # Initially each story is a cluster on it's own. And story pairs are - # added all possible merges. - for cluster1 in remaining_clusters: - for cluster2 in remaining_clusters: - if cluster1 == cluster2: - break - heapq.heappush(heap, - (cluster1.GetDistanceFrom(cluster2, distance_matrix), - cluster1, cluster2)) - - # At each step the two clusters will be merged together. - while (len(remaining_clusters) > max_cluster_count and len(heap) > 0): - _, cluster1, cluster2 = heapq.heappop(heap) - if (cluster1 not in remaining_clusters or - cluster2 not in remaining_clusters): - continue - new_cluster = cluster1.Merge(cluster2) - remaining_clusters.discard(cluster1) - remaining_clusters.discard(cluster2) - - # Adding all possible merges to the heap - for cluster in remaining_clusters: - distance = new_cluster.GetDistanceFrom(cluster, distance_matrix) - heapq.heappush(heap, (distance, new_cluster, cluster)) - - remaining_clusters.add(new_cluster) - - final_clusters = [] - number_of_stories_covered = 0 - for cluster in remaining_clusters: - cluster.GetRepresentative(distance_matrix) - if len(cluster) >= min_cluster_size: - final_clusters.append(cluster) - number_of_stories_covered += len(cluster) - coverage = number_of_stories_covered / len(stories) - - return final_clusters, coverage
diff --git a/tools/perf/experimental/story_clustering/create_soundwave_input.py b/tools/perf/experimental/story_clustering/create_soundwave_input.py deleted file mode 100644 index 7567e492..0000000 --- a/tools/perf/experimental/story_clustering/create_soundwave_input.py +++ /dev/null
@@ -1,53 +0,0 @@ -# Copyright 2019 The Chromium Authors -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -import argparse -import json -import sys - - -def CreateInput(test_suite, platforms, metrics, test_cases_path, output_dir): - with open(test_cases_path, 'r') as test_case_file: - test_cases = [line.strip() for line in test_case_file] - json_data = [] - - for platform in platforms: - for metric in metrics: - for test_case in test_cases: - json_data.append({ - 'test_suite': test_suite, - 'bot': platform, - 'measurement': metric, - 'test_case': test_case - }) - - with open(output_dir, 'w') as output: - json.dump(json_data, output) - - -def Main(argv): - parser = argparse.ArgumentParser( - description=('Creates the Json needed for the soundwave')) - parser.add_argument('test_suite', help=('Name of test_suite (example: "' - 'rendering.desktop")')) - parser.add_argument('--platforms', help='Name of platform (example: ' - '"ChromiumPerf:Win 7 Nvidia GPU Perf")', nargs='*') - parser.add_argument('--metrics', help='Name of measurement (example: ' - '"frame_times")', nargs='*') - parser.add_argument('--test-cases-path', type=str, - help='Path for the file having test_cases') - parser.add_argument('--output-dir', type=str, - help='Path for the output file') - - args = parser.parse_args(argv[1:]) - return CreateInput( - args.test_suite, - args.platforms, - args.metrics, - args.test_cases_path, - args.output_dir) - - -if __name__ == '__main__': - sys.exit(Main(sys.argv))
diff --git a/tools/perf/experimental/story_clustering/gather_historical_records_and_cluster_stories.py b/tools/perf/experimental/story_clustering/gather_historical_records_and_cluster_stories.py deleted file mode 100644 index 0c13e3fd..0000000 --- a/tools/perf/experimental/story_clustering/gather_historical_records_and_cluster_stories.py +++ /dev/null
@@ -1,160 +0,0 @@ -# Copyright 2019 The Chromium Authors -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -from __future__ import print_function - -import argparse -import json -import logging -import os -import shutil -import subprocess -import sys -import tempfile - -TOOLS_PERF_PATH = os.path.abspath(os.path.join( - os.path.dirname(__file__), '..', '..')) -sys.path.insert(1, TOOLS_PERF_PATH) - -from experimental.story_clustering import similarity_calculator -from experimental.story_clustering import cluster_stories -from experimental.story_clustering import create_soundwave_input -from core.external_modules import pandas - - -def CalculateDistances( - all_bots_dataframe, - bots, - rolling_window, - metric_name, - normalize = False): - timeseries = [] - - for bot_name, bot_group in all_bots_dataframe.groupby(bots): - temp_dataframe = bot_group.pivot(index='test_case', - columns='commit_pos', values='value') - temp_dataframe_with_solling_avg = temp_dataframe.rolling( - rolling_window, - min_periods=1, - axis=1 - ).mean().stack().rename('value').reset_index() - - temp_dataframe_with_solling_avg['bot'] = bot_name - timeseries.append(temp_dataframe_with_solling_avg) - - all_bots = pandas.concat(timeseries) - distance_matrix = similarity_calculator.CalculateDistances( - all_bots, - metric=metric_name, - normalize=normalize, - ) - print('Similarities are calculated for', metric_name) - - return distance_matrix - - -def Main(argv): - parser = argparse.ArgumentParser( - description=('Gathers the values of each metric and platfrom pair in a' - ' csv file to be used in clustering of stories.')) - parser.add_argument('benchmark', type=str, help='benchmark to be used') - parser.add_argument('--metrics', type=str, nargs='*', - help='List of metrics to use') - parser.add_argument('--platforms', type=str, nargs='*', - help='List of platforms to use') - parser.add_argument('--testcases-path', type=str, help=('Path to the file ' - 'containing a list of all test_cases in the benchmark that needs to ' - 'be clustered')) - parser.add_argument('--days', default=180, help=('Number of days to gather' - ' data about')) - parser.add_argument('--output-path', type=str, help='Output file', - default='//tmp/story_clustering/clusters.json') - parser.add_argument('--max-cluster-count', default='10', - help='Number of not valid clusters needed') - parser.add_argument('--min-cluster-size', default='2', help=('Least number ' - 'of members in cluster, to make cluster valied')) - parser.add_argument('--rolling-window', default='1', help=('Number of ' - 'samples to take average from while calculating the moving average')) - parser.add_argument('--normalize', default=False, - help='Normalize timeseries to calculate similarity', action='store_true') - parser.add_argument('--processes', default='20', help=('Number of ' - 'concurrent workers used by soundwave.')) - args = parser.parse_args(argv[1:]) - - temp_dir = tempfile.mkdtemp('telemetry') - startup_timeseries = os.path.join(temp_dir, 'startup_timeseries.json') - soundwave_output_path = os.path.join(temp_dir, 'data.csv') - soundwave_path = os.path.join(TOOLS_PERF_PATH, 'soundwave') - - try: - output_dir = os.path.dirname(args.output_path) - clusters_json = {} - - if not os.path.isdir(output_dir): - os.makedirs(output_dir) - - # creating the json file needed for soundwave - create_soundwave_input.CreateInput( - test_suite=args.benchmark, - platforms=args.platforms, - metrics=args.metrics, - test_cases_path=args.testcases_path, - output_dir=startup_timeseries) - - subprocess.call([ - soundwave_path, - '-d', args.days, - '--processes', args.processes, - 'timeseries', - '-i', startup_timeseries, - '--output-csv', soundwave_output_path - ]) - - # Processing the data. - dataframe = pandas.read_csv(soundwave_output_path) - dataframe_per_metric = dataframe.groupby(dataframe['measurement']) - for metric_name, all_bots in list(dataframe_per_metric): - clusters_json[metric_name] = [] - - distance_matrix = CalculateDistances( - all_bots_dataframe=all_bots, - bots=dataframe['bot'], - rolling_window=int(args.rolling_window), - metric_name=metric_name, - normalize=args.normalize) - - clusters, coverage = cluster_stories.RunHierarchicalClustering( - distance_matrix, - max_cluster_count=int(args.max_cluster_count), - min_cluster_size=int(args.min_cluster_size), - ) - print() - print(metric_name, ':') - print(format(coverage * 100.0, '.1f'), 'percent coverage.') - print('Stories are grouped into', len(clusters), 'clusters.') - print('representatives:') - for cluster in clusters: - print (cluster.GetRepresentative()) - print() - - for cluster in clusters: - clusters_json[metric_name].append(cluster.AsDict()) - - with open(args.output_path, 'w') as outfile: - json.dump( - clusters_json, - outfile, - separators=(',',': '), - indent=4, - sort_keys=True - ) - - except Exception: - logging.exception('The following exception may have prevented the code' - ' from clustering stories.') - finally: - shutil.rmtree(temp_dir, ignore_errors=True) - -if __name__ == '__main__': - sys.exit(Main(sys.argv))
diff --git a/tools/perf/experimental/story_clustering/similarity_calculator.py b/tools/perf/experimental/story_clustering/similarity_calculator.py deleted file mode 100644 index 4b091fb..0000000 --- a/tools/perf/experimental/story_clustering/similarity_calculator.py +++ /dev/null
@@ -1,71 +0,0 @@ -# Copyright 2019 The Chromium Authors -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -import os - -from core.external_modules import pandas - -HIGHEST_VALID_NAN_RATIO = 0.5 - - -def CalculateDistances( - input_dataframe, - metric, - normalize=False, - output_path=None): - """Calculates the distances of stories. - - If normalize flag is set the values are first normalized using min-max - normalization. Then the similarity measure between every two stories is - calculated using pearson correlation. - - Args: - input_dataframe: A dataframe containing a list of records - having (test_case, commit_pos, bot, value). - metric: String containing name of the metric. - normalize: A flag to determine if normalization is needed. - output_path: Path to write the calculated distances. - - Returns: - A dataframe containing the distance matrix of the stories. - """ - input_by_story = input_dataframe.groupby('test_case')['value'] - total_values_per_story = input_by_story.size() - nan_values_per_story = input_by_story.apply(lambda s: s.isna().sum()) - should_keep = nan_values_per_story < ( - total_values_per_story * HIGHEST_VALID_NAN_RATIO) - valid_stories = total_values_per_story[should_keep].index - - filtered_dataframe = input_dataframe[ - input_dataframe['test_case'].isin(valid_stories)] - - temp_df = filtered_dataframe.copy() - - if normalize: - # Min Max normalization - grouped = temp_df.groupby(['bot', 'test_case'])['value'] - min_value = grouped.transform('min') - max_value = grouped.transform('max') - temp_df['value'] = temp_df['value'] / (1 + max_value - min_value) - - distances = pandas.DataFrame() - grouped_temp = temp_df.groupby(temp_df['bot']) - for _, group in grouped_temp: - sample_df = group.pivot(index='commit_pos', columns='test_case', - values='value') - - if distances.empty: - distances = 1 - sample_df.corr(method='pearson') - else: - distances = distances.add(1 - sample_df.corr(method='pearson'), - fill_value=0) - - if output_path is not None: - if not os.path.isdir(output_path): - os.makedirs(output_path) - distances.to_csv( - os.path.join(output_path, metric + '_distances.csv') - ) - - return distances
diff --git a/tools/perf/export_csv b/tools/perf/export_csv deleted file mode 100755 index 1bbab52..0000000 --- a/tools/perf/export_csv +++ /dev/null
@@ -1,57 +0,0 @@ -#!/usr/bin/env vpython3 -# Copyright 2018 The Chromium Authors -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -import argparse -import contextlib -import csv -import os -import sqlite3 -import sys - - -DEFAULT_DATABASE_PATH = os.path.abspath(os.path.join( - os.path.dirname(__file__), '_cached_data', 'soundwave', 'soundwave.db')) - - -@contextlib.contextmanager -def OutputStream(filename): - if filename is None or filename == '-': - yield sys.stdout - else: - with open(filename, 'w') as f: - yield f - - -def EncodeUnicode(v): - return v.encode('utf-8') if isinstance(v, unicode) else v - - -def main(): - parser = argparse.ArgumentParser() - parser.add_argument( - 'table', help='Name of a table to export') - parser.add_argument( - '--database-file', default=DEFAULT_DATABASE_PATH, - help='File path for database where to store data.') - parser.add_argument( - '--output', '-o', - help='Where to write the csv output, defaults to stdout.') - args = parser.parse_args() - - con = sqlite3.connect(args.database_file) - try: - cur = con.execute('SELECT * FROM %s' % args.table) - header = [c[0] for c in cur.description] - with OutputStream(args.output) as out: - writer = csv.writer(out) - writer.writerow(header) - for row in cur: - writer.writerow([EncodeUnicode(v) for v in row]) - finally: - con.close() - - -if __name__ == '__main__': - sys.exit(main())
diff --git a/tools/perf/process_perf_results.pydeps b/tools/perf/process_perf_results.pydeps index 9136ac6..99ddd4b3 100644 --- a/tools/perf/process_perf_results.pydeps +++ b/tools/perf/process_perf_results.pydeps
@@ -39,6 +39,7 @@ ../../third_party/catapult/common/py_utils/py_utils/tempfile_ext.py ../../third_party/catapult/common/py_utils/py_utils/ts_proxy_server.py ../../third_party/catapult/common/py_utils/py_utils/webpagereplay_go_server.py +../../third_party/catapult/common/py_utils/py_utils/xvfb.py ../../third_party/catapult/common/py_vulcanize/py_vulcanize/__init__.py ../../third_party/catapult/common/py_vulcanize/py_vulcanize/generate.py ../../third_party/catapult/common/py_vulcanize/py_vulcanize/html_generation_controller.py
diff --git a/tools/traffic_annotation/OWNERS b/tools/traffic_annotation/OWNERS index 6f7434c..af3a167 100644 --- a/tools/traffic_annotation/OWNERS +++ b/tools/traffic_annotation/OWNERS
@@ -1,6 +1,3 @@ +crmullins@chromium.org nicolaso@chromium.org rhalavati@chromium.org # emeritus - -per-file safe_list.txt=crmullins@chromium.org -per-file safe_list.txt=nicolaso@chromium.org -per-file safe_list.txt=ramyagopalan@google.com
diff --git a/ui/base/ime/win/input_method_win_base.cc b/ui/base/ime/win/input_method_win_base.cc index e0aeb0d5..d838ade 100644 --- a/ui/base/ime/win/input_method_win_base.cc +++ b/ui/base/ime/win/input_method_win_base.cc
@@ -12,6 +12,7 @@ #include "base/auto_reset.h" #include "base/command_line.h" +#include "base/containers/heap_array.h" #include "base/functional/bind.h" #include "base/memory/ptr_util.h" #include "base/strings/string_util.h" @@ -83,8 +84,8 @@ // Retrieve the keyboard layouts in an array and check if there is an RTL // layout in it. - std::unique_ptr<HKL[]> layouts(new HKL[size]); - ::GetKeyboardLayoutList(size, layouts.get()); + auto layouts = base::HeapArray<HKL>::Uninit(size); + ::GetKeyboardLayoutList(size, layouts.data()); for (int i = 0; i < size; ++i) { if (IsRTLPrimaryLangID( PRIMARYLANGID(reinterpret_cast<uintptr_t>(layouts[i])))) {
diff --git a/ui/display/BUILD.gn b/ui/display/BUILD.gn index fb806ddd..2dbfeb1 100644 --- a/ui/display/BUILD.gn +++ b/ui/display/BUILD.gn
@@ -231,8 +231,8 @@ "win/test/scoped_screen_win.h", "win/test/screen_util_win.cc", "win/test/screen_util_win.h", - "win/test/virtual_display_win_util.cc", - "win/test/virtual_display_win_util.h", + "win/test/virtual_display_util_win.cc", + "win/test/virtual_display_util_win.h", ] deps += [ "//third_party/win_virtual_display/controller", @@ -244,8 +244,8 @@ sources += [ "mac/test/test_screen_mac.h", "mac/test/test_screen_mac.mm", - "mac/test/virtual_display_mac_util.h", - "mac/test/virtual_display_mac_util.mm", + "mac/test/virtual_display_util_mac.h", + "mac/test/virtual_display_util_mac.mm", ] } @@ -373,23 +373,19 @@ } } -# This target is added as a dependency of browser interactive_ui_tests.It must +# This target is added as a dependency of browser interactive_ui_tests. It must # be source_set, otherwise the linker will drop the tests as dead code. source_set("display_interactive_ui_tests") { testonly = true if (is_mac) { - sources = [ "mac/test/virtual_display_mac_util_interactive_uitest.mm" ] - - deps = [ - ":display", - ":test_support", - "//base/test:test_support", - "//testing/gtest", - ] + sources = [ "mac/test/virtual_display_util_mac_interactive_uitest.mm" ] } - if (is_win) { - sources = [ "win/test/virtual_display_win_util_interactive_uitest.cc" ] + if (is_win) { + sources = [ "win/test/virtual_display_util_win_interactive_uitest.cc" ] + } + + if (is_win || is_mac) { deps = [ ":display", ":test_support",
diff --git a/ui/display/mac/test/virtual_display_mac_util.h b/ui/display/mac/test/virtual_display_util_mac.h similarity index 95% rename from ui/display/mac/test/virtual_display_mac_util.h rename to ui/display/mac/test/virtual_display_util_mac.h index d3ad2f2b3..68c4cd6 100644 --- a/ui/display/mac/test/virtual_display_mac_util.h +++ b/ui/display/mac/test/virtual_display_util_mac.h
@@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef UI_DISPLAY_MAC_TEST_VIRTUAL_DISPLAY_MAC_UTIL_H_ -#define UI_DISPLAY_MAC_TEST_VIRTUAL_DISPLAY_MAC_UTIL_H_ +#ifndef UI_DISPLAY_MAC_TEST_VIRTUAL_DISPLAY_UTIL_MAC_H_ +#define UI_DISPLAY_MAC_TEST_VIRTUAL_DISPLAY_UTIL_MAC_H_ #import <IOKit/pwr_mgt/IOPMLib.h> #include <stdint.h> @@ -108,4 +108,4 @@ } // namespace test } // namespace display -#endif // UI_DISPLAY_MAC_TEST_VIRTUAL_DISPLAY_MAC_UTIL_H_ +#endif // UI_DISPLAY_MAC_TEST_VIRTUAL_DISPLAY_UTIL_MAC_H_
diff --git a/ui/display/mac/test/virtual_display_mac_util.mm b/ui/display/mac/test/virtual_display_util_mac.mm similarity index 99% rename from ui/display/mac/test/virtual_display_mac_util.mm rename to ui/display/mac/test/virtual_display_util_mac.mm index 9b1ee91..720324f 100644 --- a/ui/display/mac/test/virtual_display_mac_util.mm +++ b/ui/display/mac/test/virtual_display_util_mac.mm
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "ui/display/mac/test/virtual_display_mac_util.h" +#include "ui/display/mac/test/virtual_display_util_mac.h" #include <CoreGraphics/CoreGraphics.h> #import <Foundation/Foundation.h>
diff --git a/ui/display/mac/test/virtual_display_mac_util_interactive_uitest.mm b/ui/display/mac/test/virtual_display_util_mac_interactive_uitest.mm similarity index 98% rename from ui/display/mac/test/virtual_display_mac_util_interactive_uitest.mm rename to ui/display/mac/test/virtual_display_util_mac_interactive_uitest.mm index cf7a733..a6f2880a 100644 --- a/ui/display/mac/test/virtual_display_mac_util_interactive_uitest.mm +++ b/ui/display/mac/test/virtual_display_util_mac_interactive_uitest.mm
@@ -5,7 +5,7 @@ #include <memory> #include "base/test/task_environment.h" #include "testing/gtest/include/gtest/gtest.h" -#include "ui/display/mac/test/virtual_display_mac_util.h" +#include "ui/display/mac/test/virtual_display_util_mac.h" #include "ui/display/screen.h" #include "ui/display/test/virtual_display_util.h" #include "ui/display/types/display_constants.h"
diff --git a/ui/display/manager/display_configurator.cc b/ui/display/manager/display_configurator.cc index 5bcf366..210f3a66 100644 --- a/ui/display/manager/display_configurator.cc +++ b/ui/display/manager/display_configurator.cc
@@ -114,7 +114,7 @@ MultipleDisplayState new_display_state, chromeos::DisplayPowerState new_power_state, RefreshRateThrottleState new_throttle_state, - bool new_vrr_enabled_state, + const base::flat_set<int64_t>& new_vrr_enabled_state, std::vector<DisplayConfigureRequest>* requests) const override; DisplayStateList GetDisplayStates() const override; bool IsMirroring() const override; @@ -249,7 +249,7 @@ MultipleDisplayState new_display_state, chromeos::DisplayPowerState new_power_state, RefreshRateThrottleState new_throttle_state, - bool new_vrr_enabled_state, + const base::flat_set<int64_t>& new_vrr_enabled_state, std::vector<DisplayConfigureRequest>* requests) const { std::vector<DisplayState> states = ParseDisplays(displays); std::vector<bool> display_power; @@ -263,9 +263,12 @@ gfx::Size size; for (display::DisplaySnapshot* display : displays) { - requests->push_back(DisplayConfigureRequest( - display, display->current_mode(), gfx::Point(), - new_vrr_enabled_state && display->IsVrrCapable())); + const bool enable_vrr = + display->IsVrrCapable() && + (::features::IsVariableRefreshRateAlwaysOn() || + new_vrr_enabled_state.contains(display->display_id())); + requests->emplace_back(display, display->current_mode(), gfx::Point(), + enable_vrr); } switch (new_display_state) { @@ -598,8 +601,7 @@ layout_manager_.get(), base::BindRepeating(&DisplayConfigurator::configurator_disabled, base::Unretained(this)))), - has_unassociated_display_(false), - pending_vrr_state_(::features::IsVariableRefreshRateAlwaysOn()) { + has_unassociated_display_(false) { AddObserver(content_protection_manager_.get()); } @@ -1097,8 +1099,7 @@ const std::vector<raw_ptr<DisplaySnapshot, VectorExperimental>>& unassociated_displays, MultipleDisplayState new_display_state, - chromeos::DisplayPowerState new_power_state, - bool new_vrr_state_) { + chromeos::DisplayPowerState new_power_state) { VLOG(1) << "OnConfigured: success=" << success << " new_display_state=" << MultipleDisplayStateToString(new_display_state) << " new_power_state=" << DisplayPowerStateToString(new_power_state); @@ -1109,7 +1110,6 @@ if (success) { current_display_state_ = new_display_state; UpdatePowerState(new_power_state); - current_vrr_state_ = new_vrr_state_; } configuration_task_.reset(); @@ -1217,34 +1217,52 @@ return current_power_state_ != chromeos::DISPLAY_POWER_ALL_OFF; } -void DisplayConfigurator::SetVrrEnabled(bool enable_vrr) { - if (current_vrr_state_ == enable_vrr) { - return; - } - - pending_vrr_state_ = enable_vrr; - - if (!configure_timer_.IsRunning()) { - RunPendingConfiguration(); - } -} - -bool DisplayConfigurator::GetRequestedVrrState() const { - return pending_vrr_state_.value_or(current_vrr_state_); -} - -bool DisplayConfigurator::ShouldConfigureVrr() const { +void DisplayConfigurator::SetVrrEnabled( + const base::flat_set<int64_t>& display_ids) { + // Filter the provided set for VRR-capable displays only, and determine + // whether a configuration is required given the current state. + base::flat_set<int64_t> filtered_display_ids; + bool requires_configuration = false; for (const display::DisplaySnapshot* display : cached_displays_) { if (!display->IsVrrCapable()) { continue; } - if (display->IsVrrEnabled() != GetRequestedVrrState()) { - return true; + const bool vrr_should_be_enabled = + display_ids.contains(display->display_id()); + if (vrr_should_be_enabled) { + filtered_display_ids.emplace(display->display_id()); + } + requires_configuration |= vrr_should_be_enabled != display->IsVrrEnabled(); + } + + if (requires_configuration) { + pending_vrr_state_.emplace(filtered_display_ids); + + if (!configure_timer_.IsRunning()) { + RunPendingConfiguration(); + } + } +} + +const base::flat_set<int64_t> DisplayConfigurator::GetRequestedVrrState() + const { + if (pending_vrr_state_.has_value()) { + return pending_vrr_state_.value(); + } + + base::flat_set<int64_t> requested_vrr_state; + for (const display::DisplaySnapshot* display : cached_displays_) { + if (display->IsVrrEnabled()) { + requested_vrr_state.emplace(display->display_id()); } } - return false; + return requested_vrr_state; +} + +bool DisplayConfigurator::ShouldConfigureVrr() const { + return pending_vrr_state_.has_value(); } ////////////////////////////////////////////////////////////////////////////////
diff --git a/ui/display/manager/display_configurator.h b/ui/display/manager/display_configurator.h index 4a8a32c..897d33c 100644 --- a/ui/display/manager/display_configurator.h +++ b/ui/display/manager/display_configurator.h
@@ -10,6 +10,7 @@ #include <optional> #include <vector> +#include "base/containers/flat_set.h" #include "base/memory/raw_ptr.h" #include "base/memory/weak_ptr.h" #include "base/observer_list.h" @@ -312,9 +313,10 @@ return requested_power_state_; } - // Requests to enable or disable variable refresh rates across all capable - // displays, and schedules a configuration change as needed. - void SetVrrEnabled(bool enable_vrr); + // Requests to enable variable refresh rates on the specified displays and to + // disable variable refresh rates on all other displays, and schedules a + // configuration change as needed. + void SetVrrEnabled(const base::flat_set<int64_t>& display_ids); private: friend class test::DisplayManagerTestApi; @@ -358,8 +360,7 @@ const std::vector<raw_ptr<DisplaySnapshot, VectorExperimental>>& unassociated_displays, MultipleDisplayState new_display_state, - chromeos::DisplayPowerState new_power_state, - bool new_vrr_state); + chromeos::DisplayPowerState new_power_state); // Updates the current and pending power state and notifies observers. void UpdatePowerState(chromeos::DisplayPowerState new_power_state); @@ -400,8 +401,9 @@ void SendRelinquishDisplayControl(DisplayControlCallback callback, bool success); - // Returns the requested VRR state, or the current state by default. - bool GetRequestedVrrState() const; + // Returns the requested VRR state listing the display ids which should have + // VRR enabled, defaulting to the current state as needed. + const base::flat_set<int64_t> GetRequestedVrrState() const; // Returns whether a configuration should occur on account of a pending VRR // request. @@ -487,10 +489,10 @@ // notification will be created to inform user. bool has_unassociated_display_; - // Stores the current variable refresh rate enabled state. - bool current_vrr_state_ = false; - // Stores the requested variable refresh rate enabled state. - std::optional<bool> pending_vrr_state_; + // Stores the requested variable refresh rate state as a set of display ids + // for which VRR should be enabled. All omitted displays should have VRR + // disabled. Absent if there is no pending state. + std::optional<base::flat_set<int64_t>> pending_vrr_state_ = std::nullopt; // This must be the last variable. base::WeakPtrFactory<DisplayConfigurator> weak_ptr_factory_{this};
diff --git a/ui/display/manager/display_configurator_unittest.cc b/ui/display/manager/display_configurator_unittest.cc index 7404ad3..c91a0d8 100644 --- a/ui/display/manager/display_configurator_unittest.cc +++ b/ui/display/manager/display_configurator_unittest.cc
@@ -2192,7 +2192,7 @@ test_api_.GetDisplayLayoutManager()->GetDisplayLayout( native_display_delegate_->GetOutputs(), MULTIPLE_DISPLAY_STATE_SINGLE, chromeos::DISPLAY_POWER_INTERNAL_OFF_EXTERNAL_ON, - kRefreshRateThrottleEnabled, false, &requests); + kRefreshRateThrottleEnabled, /*new_vrr_enabled_state=*/{}, &requests); bool has_internal_request = false; for (auto& request: requests) { @@ -2213,14 +2213,15 @@ observer_.Reset(); // Set VRR noop. - configurator_.SetVrrEnabled(false); + configurator_.SetVrrEnabled({}); EXPECT_EQ(0, observer_.num_changes()); EXPECT_FALSE(GetOutput(0)->IsVrrEnabled()); EXPECT_FALSE(GetOutput(1)->IsVrrEnabled()); EXPECT_EQ(kNoActions, log_->GetActionsAndClear()); // Set VRR enabled. - configurator_.SetVrrEnabled(true); + configurator_.SetVrrEnabled( + {GetOutput(0)->display_id(), GetOutput(1)->display_id()}); EXPECT_EQ(1, observer_.num_changes()); EXPECT_TRUE(GetOutput(0)->IsVrrEnabled()); EXPECT_FALSE(GetOutput(1)->IsVrrEnabled()); @@ -2249,7 +2250,7 @@ observer_.Reset(); // Set VRR disabled. - configurator_.SetVrrEnabled(false); + configurator_.SetVrrEnabled({}); EXPECT_EQ(1, observer_.num_changes()); EXPECT_FALSE(GetOutput(0)->IsVrrEnabled()); EXPECT_FALSE(GetOutput(1)->IsVrrEnabled()); @@ -2286,7 +2287,8 @@ log_->GetActionsAndClear(); observer_.Reset(); - configurator_.SetVrrEnabled(true); + configurator_.SetVrrEnabled( + {GetOutput(0)->display_id(), GetOutput(1)->display_id()}); EXPECT_EQ(0, observer_.num_changes()); EXPECT_FALSE(GetOutput(0)->IsVrrEnabled()); EXPECT_FALSE(GetOutput(1)->IsVrrEnabled()); @@ -2313,7 +2315,7 @@ state_controller_.set_state(MULTIPLE_DISPLAY_STATE_SINGLE); UpdateOutputs(1, true); // Enable VRR on internal display. - configurator_.SetVrrEnabled(true); + configurator_.SetVrrEnabled({GetOutput(0)->display_id()}); EXPECT_EQ(120.0f, GetOutput(0)->current_mode()->refresh_rate()); EXPECT_TRUE(GetOutput(0)->IsVrrEnabled()); log_->GetActionsAndClear(); @@ -2391,7 +2393,8 @@ state_controller_.set_state(MULTIPLE_DISPLAY_STATE_MULTI_EXTENDED); UpdateOutputs(2, true); // Enable VRR when only the external display is VRR-capable. - configurator_.SetVrrEnabled(true); + configurator_.SetVrrEnabled( + {GetOutput(0)->display_id(), GetOutput(1)->display_id()}); EXPECT_EQ(120.0f, GetOutput(0)->current_mode()->refresh_rate()); EXPECT_EQ(60.0f, GetOutput(1)->current_mode()->refresh_rate()); EXPECT_FALSE(GetOutput(0)->IsVrrEnabled());
diff --git a/ui/display/manager/display_layout_manager.h b/ui/display/manager/display_layout_manager.h index e51782c0..bfbab2b 100644 --- a/ui/display/manager/display_layout_manager.h +++ b/ui/display/manager/display_layout_manager.h
@@ -41,7 +41,7 @@ MultipleDisplayState new_display_state, chromeos::DisplayPowerState new_power_state, RefreshRateThrottleState new_throttle_state, - bool new_vrr_enabled_state, + const base::flat_set<int64_t>& new_vrr_enabled_state, std::vector<DisplayConfigureRequest>* requests) const = 0; virtual std::vector<raw_ptr<DisplaySnapshot, VectorExperimental>>
diff --git a/ui/display/manager/test/test_display_layout_manager.cc b/ui/display/manager/test/test_display_layout_manager.cc index a1357f7..5296c297 100644 --- a/ui/display/manager/test/test_display_layout_manager.cc +++ b/ui/display/manager/test/test_display_layout_manager.cc
@@ -40,7 +40,7 @@ MultipleDisplayState new_display_state, chromeos::DisplayPowerState new_power_state, RefreshRateThrottleState new_throttle_state, - bool new_vrr_state, + const base::flat_set<int64_t>& new_vrr_enabled_state, std::vector<DisplayConfigureRequest>* requests) const { NOTREACHED(); return false;
diff --git a/ui/display/manager/test/test_display_layout_manager.h b/ui/display/manager/test/test_display_layout_manager.h index 7625f3b..8907b22 100644 --- a/ui/display/manager/test/test_display_layout_manager.h +++ b/ui/display/manager/test/test_display_layout_manager.h
@@ -45,7 +45,7 @@ MultipleDisplayState new_display_state, chromeos::DisplayPowerState new_power_state, RefreshRateThrottleState new_throttle_state, - bool new_vrr_enabled_state, + const base::flat_set<int64_t>& new_vrr_enabled_state, std::vector<DisplayConfigureRequest>* requests) const override; std::vector<raw_ptr<DisplaySnapshot, VectorExperimental>> GetDisplayStates() const override;
diff --git a/ui/display/manager/update_display_configuration_task.cc b/ui/display/manager/update_display_configuration_task.cc index 008e0662..e29a156e 100644 --- a/ui/display/manager/update_display_configuration_task.cc +++ b/ui/display/manager/update_display_configuration_task.cc
@@ -74,7 +74,7 @@ chromeos::DisplayPowerState new_power_state, int power_flags, RefreshRateThrottleState refresh_rate_throttle_state, - bool new_vrr_state_, + const base::flat_set<int64_t>& new_vrr_state, bool force_configure, ConfigurationType configuration_type, ResponseCallback callback) @@ -84,7 +84,7 @@ new_power_state_(new_power_state), power_flags_(power_flags), refresh_rate_throttle_state_(refresh_rate_throttle_state), - new_vrr_state_(new_vrr_state_), + new_vrr_state_(new_vrr_state), force_configure_(force_configure), configuration_type_(configuration_type), callback_(std::move(callback)), @@ -131,7 +131,7 @@ << " new_power_state=" << DisplayPowerStateToString(new_power_state_) << " flags=" << power_flags_ << " refresh_rate_throttle_state_=" << RefreshRateThrottleStateToString(refresh_rate_throttle_state_) - << " new_vrr_state=" << new_vrr_state_ + << " new_vrr_state=" << VrrStateToString(new_vrr_state_) << " force_configure=" << force_configure_ << " display_count=" << cached_displays_.size(); if (ShouldConfigure()) { @@ -214,7 +214,7 @@ std::move(callback_).Run(success, cached_displays_, cached_unassociated_displays_, new_display_state_, - new_power_state_, new_vrr_state_); + new_power_state_); } bool UpdateDisplayConfigurationTask::ShouldForceDpms() const { @@ -279,7 +279,8 @@ continue; } - if (display->IsVrrEnabled() != new_vrr_state_) { + if (new_vrr_state_.contains(display->display_id()) != + display->IsVrrEnabled()) { return true; } }
diff --git a/ui/display/manager/update_display_configuration_task.h b/ui/display/manager/update_display_configuration_task.h index 94307c28..98aca83 100644 --- a/ui/display/manager/update_display_configuration_task.h +++ b/ui/display/manager/update_display_configuration_task.h
@@ -33,8 +33,7 @@ /*unassociated_displays=*/ const std::vector<raw_ptr<DisplaySnapshot, VectorExperimental>>&, /*new_display_state=*/MultipleDisplayState, - /*new_power_state=*/chromeos::DisplayPowerState, - /*new_vrr_state=*/bool)>; + /*new_power_state=*/chromeos::DisplayPowerState)>; UpdateDisplayConfigurationTask( NativeDisplayDelegate* delegate, @@ -43,7 +42,7 @@ chromeos::DisplayPowerState new_power_state, int power_flags, RefreshRateThrottleState refresh_rate_throttle_state, - bool new_vrr_state, + const base::flat_set<int64_t>& new_vrr_state, bool force_configure, ConfigurationType configuration_type, ResponseCallback callback); @@ -114,9 +113,9 @@ // for the internal display. RefreshRateThrottleState refresh_rate_throttle_state_; - // The requested VRR enabled state which the configuration task should apply - // to all capable displays. - bool new_vrr_state_; + // The requested VRR state which lists the set of display ids that should have + // VRR enabled, while all omitted displays should have VRR disabled. + const base::flat_set<int64_t> new_vrr_state_; bool force_configure_;
diff --git a/ui/display/manager/update_display_configuration_task_unittest.cc b/ui/display/manager/update_display_configuration_task_unittest.cc index 3270b01..93755c9 100644 --- a/ui/display/manager/update_display_configuration_task_unittest.cc +++ b/ui/display/manager/update_display_configuration_task_unittest.cc
@@ -97,7 +97,7 @@ MultipleDisplayState new_display_state, chromeos::DisplayPowerState new_power_state, RefreshRateThrottleState new_throttle_state, - bool new_vrr_state, + const base::flat_set<int64_t>& new_vrr_state, std::vector<DisplayConfigureRequest>* requests) const override { gfx::Point origin; for (DisplaySnapshot* display : displays) { @@ -110,7 +110,8 @@ const DisplayMode* request_mode = new_power_state == chromeos::DISPLAY_POWER_ALL_ON ? mode : nullptr; - bool request_vrr_state = new_vrr_state && display->IsVrrCapable(); + bool request_vrr_state = new_vrr_state.contains(display->display_id()) && + display->IsVrrCapable(); requests->push_back(DisplayConfigureRequest(display, request_mode, origin, request_vrr_state)); @@ -201,14 +202,12 @@ const std::vector<raw_ptr<DisplaySnapshot, VectorExperimental>>& unassociated_displays, MultipleDisplayState new_display_state, - chromeos::DisplayPowerState new_power_state, - bool new_vrr_state) { + chromeos::DisplayPowerState new_power_state) { configured_ = true; configuration_status_ = success; display_states_ = displays; display_state_ = new_display_state; power_state_ = new_power_state; - vrr_state_ = new_vrr_state; if (success) { layout_manager_.set_display_state(display_state_); @@ -231,7 +230,6 @@ std::vector<raw_ptr<DisplaySnapshot, VectorExperimental>> display_states_; MultipleDisplayState display_state_ = MULTIPLE_DISPLAY_STATE_INVALID; chromeos::DisplayPowerState power_state_ = chromeos::DISPLAY_POWER_ALL_ON; - bool vrr_state_ = false; }; } // namespace @@ -243,8 +241,7 @@ chromeos::DISPLAY_POWER_ALL_ON, DisplayConfigurator::kSetDisplayPowerNoFlags, kRefreshRateThrottleEnabled, - /*new_vrr_state=*/false, /*force_configure=*/false, - kConfigurationTypeFull, + /*new_vrr_state=*/{}, /*force_configure=*/false, kConfigurationTypeFull, base::BindOnce(&UpdateDisplayConfigurationTaskTest::ResponseCallback, base::Unretained(this))); task.Run(); @@ -266,8 +263,7 @@ chromeos::DISPLAY_POWER_ALL_ON, DisplayConfigurator::kSetDisplayPowerNoFlags, kRefreshRateThrottleEnabled, - /*new_vrr_state=*/false, /*force_configure=*/false, - kConfigurationTypeFull, + /*new_vrr_state=*/{}, /*force_configure=*/false, kConfigurationTypeFull, base::BindOnce(&UpdateDisplayConfigurationTaskTest::ResponseCallback, base::Unretained(this))); task.Run(); @@ -298,8 +294,7 @@ chromeos::DISPLAY_POWER_ALL_ON, DisplayConfigurator::kSetDisplayPowerNoFlags, kRefreshRateThrottleEnabled, - /*new_vrr_state=*/false, /*force_configure=*/false, - kConfigurationTypeFull, + /*new_vrr_state=*/{}, /*force_configure=*/false, kConfigurationTypeFull, base::BindOnce(&UpdateDisplayConfigurationTaskTest::ResponseCallback, base::Unretained(this))); task.Run(); @@ -339,8 +334,7 @@ chromeos::DISPLAY_POWER_ALL_ON, DisplayConfigurator::kSetDisplayPowerNoFlags, kRefreshRateThrottleEnabled, - /*new_vrr_state=*/false, /*force_configure=*/false, - kConfigurationTypeFull, + /*new_vrr_state=*/{}, /*force_configure=*/false, kConfigurationTypeFull, base::BindOnce(&UpdateDisplayConfigurationTaskTest::ResponseCallback, base::Unretained(this))); task.Run(); @@ -378,8 +372,7 @@ chromeos::DISPLAY_POWER_ALL_ON, DisplayConfigurator::kSetDisplayPowerNoFlags, kRefreshRateThrottleEnabled, - /*new_vrr_state=*/false, /*force_configure=*/false, - kConfigurationTypeFull, + /*new_vrr_state=*/{}, /*force_configure=*/false, kConfigurationTypeFull, base::BindOnce(&UpdateDisplayConfigurationTaskTest::ResponseCallback, base::Unretained(this))); task.Run(); @@ -400,8 +393,7 @@ chromeos::DISPLAY_POWER_ALL_ON, DisplayConfigurator::kSetDisplayPowerNoFlags, kRefreshRateThrottleEnabled, - /*new_vrr_state=*/false, /*force_configure=*/false, - kConfigurationTypeFull, + /*new_vrr_state=*/{}, /*force_configure=*/false, kConfigurationTypeFull, base::BindOnce(&UpdateDisplayConfigurationTaskTest::ResponseCallback, base::Unretained(this))); task.Run(); @@ -464,8 +456,7 @@ chromeos::DISPLAY_POWER_ALL_ON, DisplayConfigurator::kSetDisplayPowerNoFlags, kRefreshRateThrottleEnabled, - /*new_vrr_state=*/false, /*force_configure=*/false, - kConfigurationTypeFull, + /*new_vrr_state=*/{}, /*force_configure=*/false, kConfigurationTypeFull, base::BindOnce(&UpdateDisplayConfigurationTaskTest::ResponseCallback, base::Unretained(this))); task.Run(); @@ -493,8 +484,7 @@ chromeos::DISPLAY_POWER_ALL_OFF, DisplayConfigurator::kSetDisplayPowerNoFlags, kRefreshRateThrottleEnabled, - /*new_vrr_state=*/false, /*force_configure=*/false, - kConfigurationTypeFull, + /*new_vrr_state=*/{}, /*force_configure=*/false, kConfigurationTypeFull, base::BindOnce(&UpdateDisplayConfigurationTaskTest::ResponseCallback, base::Unretained(this))); task.Run(); @@ -527,8 +517,7 @@ chromeos::DISPLAY_POWER_ALL_ON, DisplayConfigurator::kSetDisplayPowerNoFlags, kRefreshRateThrottleEnabled, - /*new_vrr_state=*/false, /*force_configure=*/false, - kConfigurationTypeFull, + /*new_vrr_state=*/{}, /*force_configure=*/false, kConfigurationTypeFull, base::BindOnce(&UpdateDisplayConfigurationTaskTest::ResponseCallback, base::Unretained(this))); task.Run(); @@ -542,8 +531,7 @@ chromeos::DISPLAY_POWER_ALL_ON, DisplayConfigurator::kSetDisplayPowerNoFlags, kRefreshRateThrottleEnabled, - /*new_vrr_state=*/false, /*force_configure=*/false, - kConfigurationTypeFull, + /*new_vrr_state=*/{}, /*force_configure=*/false, kConfigurationTypeFull, base::BindOnce(&UpdateDisplayConfigurationTaskTest::ResponseCallback, base::Unretained(this))); task.Run(); @@ -569,8 +557,7 @@ chromeos::DISPLAY_POWER_ALL_ON, DisplayConfigurator::kSetDisplayPowerNoFlags, kRefreshRateThrottleEnabled, - /*new_vrr_state=*/false, /*force_configure=*/false, - kConfigurationTypeFull, + /*new_vrr_state=*/{}, /*force_configure=*/false, kConfigurationTypeFull, base::BindOnce(&UpdateDisplayConfigurationTaskTest::ResponseCallback, base::Unretained(this))); task.Run(); @@ -584,8 +571,7 @@ chromeos::DISPLAY_POWER_ALL_ON, DisplayConfigurator::kSetDisplayPowerNoFlags, kRefreshRateThrottleEnabled, - /*new_vrr_state=*/false, /*force_configure=*/true, - kConfigurationTypeFull, + /*new_vrr_state=*/{}, /*force_configure=*/true, kConfigurationTypeFull, base::BindOnce(&UpdateDisplayConfigurationTaskTest::ResponseCallback, base::Unretained(this))); task.Run(); @@ -626,8 +612,7 @@ chromeos::DISPLAY_POWER_ALL_ON, DisplayConfigurator::kSetDisplayPowerNoFlags, kRefreshRateThrottleDisabled, - /*new_vrr_state=*/false, /*force_configure=*/false, - kConfigurationTypeFull, + /*new_vrr_state=*/{}, /*force_configure=*/false, kConfigurationTypeFull, base::BindOnce(&UpdateDisplayConfigurationTaskTest::ResponseCallback, base::Unretained(this))); task.Run(); @@ -644,8 +629,9 @@ chromeos::DISPLAY_POWER_ALL_ON, DisplayConfigurator::kSetDisplayPowerOnlyIfSingleInternalDisplay, kRefreshRateThrottleDisabled, - /*new_vrr_state=*/true, /*force_configure=*/false, - kConfigurationTypeFull, + /*new_vrr_state=*/ + {displays_[0]->display_id(), displays_[1]->display_id()}, + /*force_configure=*/false, kConfigurationTypeFull, base::BindOnce(&UpdateDisplayConfigurationTaskTest::ResponseCallback, base::Unretained(this))); task.Run(); @@ -686,8 +672,7 @@ chromeos::DISPLAY_POWER_ALL_ON, DisplayConfigurator::kSetDisplayPowerNoFlags, kRefreshRateThrottleDisabled, - /*new_vrr_state=*/false, /*force_configure=*/false, - kConfigurationTypeFull, + /*new_vrr_state=*/{}, /*force_configure=*/false, kConfigurationTypeFull, base::BindOnce(&UpdateDisplayConfigurationTaskTest::ResponseCallback, base::Unretained(this))); task.Run(); @@ -704,8 +689,7 @@ chromeos::DISPLAY_POWER_ALL_ON, DisplayConfigurator::kSetDisplayPowerOnlyIfSingleInternalDisplay, kRefreshRateThrottleDisabled, - /*new_vrr_state=*/false, /*force_configure=*/false, - kConfigurationTypeFull, + /*new_vrr_state=*/{}, /*force_configure=*/false, kConfigurationTypeFull, base::BindOnce(&UpdateDisplayConfigurationTaskTest::ResponseCallback, base::Unretained(this))); task.Run();
diff --git a/ui/display/manager/util/display_manager_util.cc b/ui/display/manager/util/display_manager_util.cc index ed3e171f..cfbca0ad 100644 --- a/ui/display/manager/util/display_manager_util.cc +++ b/ui/display/manager/util/display_manager_util.cc
@@ -17,6 +17,7 @@ #include "base/memory/raw_ptr.h" #include "base/notreached.h" #include "base/strings/string_number_conversions.h" +#include "base/strings/string_util.h" #include "build/chromeos_buildflags.h" #include "ui/base/ui_base_switches.h" #include "ui/display/display_switches.h" @@ -60,6 +61,14 @@ ")"; } +std::string VrrStateToString(const base::flat_set<int64_t>& state) { + std::vector<std::string> entries; + for (const int64_t id : state) { + entries.push_back(base::NumberToString(id)); + } + return "{" + base::JoinString(entries, ", ") + "}"; +} + int GetDisplayPower( const std::vector<raw_ptr<DisplaySnapshot, VectorExperimental>>& displays, chromeos::DisplayPowerState state, @@ -193,10 +202,10 @@ const int effective_width = std::round( static_cast<float>(std::max(mode.size().width(), mode.size().height())) / mode.device_scale_factor()); - return GetDisplayZoomFactorsByDsiplayWidth(effective_width); + return GetDisplayZoomFactorsByDisplayWidth(effective_width); } -std::vector<float> GetDisplayZoomFactorsByDsiplayWidth( +std::vector<float> GetDisplayZoomFactorsByDisplayWidth( const int display_width) { std::size_t index = kZoomListBuckets.size() - 1; while (index > 0 && display_width < kZoomListBuckets[index].first) {
diff --git a/ui/display/manager/util/display_manager_util.h b/ui/display/manager/util/display_manager_util.h index af3c1f6..64ae0e4 100644 --- a/ui/display/manager/util/display_manager_util.h +++ b/ui/display/manager/util/display_manager_util.h
@@ -9,6 +9,7 @@ #include <string> #include <vector> +#include "base/containers/flat_set.h" #include "base/memory/raw_ptr.h" #include "build/chromeos_buildflags.h" #include "ui/display/display.h" @@ -41,6 +42,9 @@ // Returns a string describing |state|. std::string RefreshRateThrottleStateToString(RefreshRateThrottleState state); +// Returns a string describing |state|. +std::string VrrStateToString(const base::flat_set<int64_t>& state); + // Returns the number of displays in |displays| that should be turned on, per // |state|. If |display_power| is non-NULL, it is updated to contain the // on/off state of each corresponding entry in |displays|. @@ -76,7 +80,7 @@ const ManagedDisplayMode& mode); // Returns a list of display zooms supported by the given |display_width|. -DISPLAY_MANAGER_EXPORT std::vector<float> GetDisplayZoomFactorsByDsiplayWidth( +DISPLAY_MANAGER_EXPORT std::vector<float> GetDisplayZoomFactorsByDisplayWidth( const int display_width); // Returns a list of display zooms based on the provided |dsf| of the display.
diff --git a/ui/display/manager/util/display_manager_util_unittest.cc b/ui/display/manager/util/display_manager_util_unittest.cc index 0ed2944..a9384a5f 100644 --- a/ui/display/manager/util/display_manager_util_unittest.cc +++ b/ui/display/manager/util/display_manager_util_unittest.cc
@@ -69,7 +69,7 @@ // equal to the |second| of the pair. for (const auto& data : kTestData) { const std::vector<float> zoom_values = - GetDisplayZoomFactorsByDsiplayWidth(data.first); + GetDisplayZoomFactorsByDisplayWidth(data.first); for (std::size_t j = 0; j < kNumOfZoomFactors; j++) { EXPECT_FLOAT_EQ(zoom_values[j], data.second[j]); }
diff --git a/ui/display/win/test/virtual_display_win_util.cc b/ui/display/win/test/virtual_display_util_win.cc similarity index 98% rename from ui/display/win/test/virtual_display_win_util.cc rename to ui/display/win/test/virtual_display_util_win.cc index b211ae0..8864383 100644 --- a/ui/display/win/test/virtual_display_win_util.cc +++ b/ui/display/win/test/virtual_display_util_win.cc
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "ui/display/win/test/virtual_display_win_util.h" +#include "ui/display/win/test/virtual_display_util_win.h" #include "base/containers/flat_tree.h" #include "base/logging.h"
diff --git a/ui/display/win/test/virtual_display_win_util.h b/ui/display/win/test/virtual_display_util_win.h similarity index 92% rename from ui/display/win/test/virtual_display_win_util.h rename to ui/display/win/test/virtual_display_util_win.h index 6670143..13a1038 100644 --- a/ui/display/win/test/virtual_display_win_util.h +++ b/ui/display/win/test/virtual_display_util_win.h
@@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef UI_DISPLAY_WIN_TEST_VIRTUAL_DISPLAY_WIN_UTIL_H_ -#define UI_DISPLAY_WIN_TEST_VIRTUAL_DISPLAY_WIN_UTIL_H_ +#ifndef UI_DISPLAY_WIN_TEST_VIRTUAL_DISPLAY_UTIL_WIN_H_ +#define UI_DISPLAY_WIN_TEST_VIRTUAL_DISPLAY_UTIL_WIN_H_ #include "base/containers/flat_map.h" #include "base/memory/weak_ptr.h" @@ -65,4 +65,4 @@ } // namespace test } // namespace display -#endif // UI_DISPLAY_WIN_TEST_VIRTUAL_DISPLAY_WIN_UTIL_H_ +#endif // UI_DISPLAY_WIN_TEST_VIRTUAL_DISPLAY_UTIL_WIN_H_
diff --git a/ui/display/win/test/virtual_display_win_util_interactive_uitest.cc b/ui/display/win/test/virtual_display_util_win_interactive_uitest.cc similarity index 98% rename from ui/display/win/test/virtual_display_win_util_interactive_uitest.cc rename to ui/display/win/test/virtual_display_util_win_interactive_uitest.cc index 7c6a8449..aba0f5c 100644 --- a/ui/display/win/test/virtual_display_win_util_interactive_uitest.cc +++ b/ui/display/win/test/virtual_display_util_win_interactive_uitest.cc
@@ -7,7 +7,7 @@ #include "testing/gtest/include/gtest/gtest.h" #include "ui/display/types/display_constants.h" #include "ui/display/win/screen_win.h" -#include "ui/display/win/test/virtual_display_win_util.h" +#include "ui/display/win/test/virtual_display_util_win.h" // Flag passed to use the custom display driver to make virtual displays. static constexpr char kSwitchWindowsVirtualDisplayDriver[] =
diff --git a/ui/gl/gl_surface_egl.cc b/ui/gl/gl_surface_egl.cc index a9eda1d..a492a9c7 100644 --- a/ui/gl/gl_surface_egl.cc +++ b/ui/gl/gl_surface_egl.cc
@@ -13,6 +13,7 @@ #include <utility> #include <vector> +#include "base/containers/heap_array.h" #include "base/lazy_instance.h" #include "base/logging.h" #include "base/memory/raw_ptr.h" @@ -235,10 +236,10 @@ continue; } - std::unique_ptr<EGLConfig[]> matching_configs(new EGLConfig[num_configs]); + auto matching_configs = base::HeapArray<EGLConfig>::Uninit(num_configs); if (want_rgb565 || visual_id >= 0) { config_size = num_configs; - config_data = matching_configs.get(); + config_data = matching_configs.data(); } if (!eglChooseConfig(display, choose_attributes, config_data, config_size,
diff --git a/ui/gl/gl_switches.cc b/ui/gl/gl_switches.cc index 7f21bafa..0df2852 100644 --- a/ui/gl/gl_switches.cc +++ b/ui/gl/gl_switches.cc
@@ -258,12 +258,7 @@ // Default to using ANGLE's Metal backend. BASE_FEATURE(kDefaultANGLEMetal, "DefaultANGLEMetal", -#if BUILDFLAG(IS_IOS) || (BUILDFLAG(IS_MAC) && defined(ARCH_CPU_ARM64)) - base::FEATURE_ENABLED_BY_DEFAULT -#else - base::FEATURE_DISABLED_BY_DEFAULT -#endif -); + base::FEATURE_ENABLED_BY_DEFAULT); // Default to using ANGLE's Vulkan backend. BASE_FEATURE(kDefaultANGLEVulkan,
diff --git a/ui/views/layout/animating_layout_manager.cc b/ui/views/layout/animating_layout_manager.cc index 02d6ef9e..d6abe107 100644 --- a/ui/views/layout/animating_layout_manager.cc +++ b/ui/views/layout/animating_layout_manager.cc
@@ -523,7 +523,7 @@ } } - return LayoutManagerBase::OnViewAdded(host, view); + return RecalculateTarget(); } void AnimatingLayoutManager::OnLayoutChanged() {
diff --git a/ui/views/layout/layout_manager_base.cc b/ui/views/layout/layout_manager_base.cc index 013f5e7..21c46ed 100644 --- a/ui/views/layout/layout_manager_base.cc +++ b/ui/views/layout/layout_manager_base.cc
@@ -156,11 +156,8 @@ // During callbacks when a child is removed we can get in a state where a view // in the child list of the host view is not in |child_infos_|. In that case, // the view is being removed and is not part of the layout. - // TODO(crbug.com/1524187): This is a symptom of bad removal handling. Fix, - // then convert this to a CHECK. - if (it == child_infos_.end()) { + if (it == child_infos_.end()) return false; - } return !child->GetProperty(kViewIgnoredByLayoutKey) && !child->GetProperty(kIsDecorativeViewKey) && @@ -173,11 +170,8 @@ // During callbacks when a child is removed we can get in a state where a view // in the child list of the host view is not in |child_infos_|. In that case, // the view is being removed and is not part of the layout. - // TODO(crbug.com/1524187): This is a symptom of bad removal handling. Fix, - // then convert this to a CHECK. - if (it == child_infos_.end()) { + if (it == child_infos_.end()) return false; - } return it->second.can_be_visible; } @@ -253,16 +247,12 @@ } bool LayoutManagerBase::OnViewAdded(View* host, View* view) { - if (IsChildIncludedInLayout(view)) { - OnLayoutChanged(); - } + OnLayoutChanged(); return false; } bool LayoutManagerBase::OnViewRemoved(View* host, View* view) { - if (IsChildIncludedInLayout(view)) { - OnLayoutChanged(); - } + OnLayoutChanged(); return false; } @@ -418,15 +408,14 @@ } bool LayoutManagerBase::PropagateViewRemoved(View* host, View* view) { + child_infos_.erase(view); + bool result = false; for (auto& owned_layout : owned_layouts_) result |= owned_layout->PropagateViewRemoved(host, view); result |= OnViewRemoved(host, view); - - child_infos_.erase(view); - return result; }
diff --git a/ui/webui/resources/cr_components/history_embeddings/BUILD.gn b/ui/webui/resources/cr_components/history_embeddings/BUILD.gn index c18cefa..3b7e7d3 100644 --- a/ui/webui/resources/cr_components/history_embeddings/BUILD.gn +++ b/ui/webui/resources/cr_components/history_embeddings/BUILD.gn
@@ -2,18 +2,31 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. +import("//mojo/public/tools/bindings/mojom.gni") import("//ui/webui/resources/tools/build_webui.gni") assert(!is_android) +mojom("mojo_bindings") { + sources = [ "history_embeddings.mojom" ] + webui_module_path = "" +} + build_webui("build") { - grd_prefix = "cr_components_history_clusters" + grd_prefix = "cr_components_history_embeddings" web_component_files = [ "history_embeddings.ts" ] + non_web_component_files = [ "browser_proxy.ts" ] + + mojo_files_deps = [ ":mojo_bindings_ts__generator" ] + mojo_files = [ "$target_gen_dir/history_embeddings.mojom-webui.ts" ] ts_out_dir = "$root_gen_dir/ui/webui/resources/tsc/cr_components/history_embeddings" ts_composite = true - ts_deps = [ "//third_party/polymer/v3_0:library" ] + ts_deps = [ + "//third_party/polymer/v3_0:library", + "//ui/webui/resources/mojo:build_ts", + ] generate_grdp = true grd_resource_path_prefix = rebase_path(".", "//ui/webui/resources") }
diff --git a/ui/webui/resources/cr_components/history_embeddings/browser_proxy.ts b/ui/webui/resources/cr_components/history_embeddings/browser_proxy.ts new file mode 100644 index 0000000..8967cb9 --- /dev/null +++ b/ui/webui/resources/cr_components/history_embeddings/browser_proxy.ts
@@ -0,0 +1,31 @@ +// Copyright 2024 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import type {PageHandlerRemote} from './history_embeddings.mojom-webui.js'; +import {PageHandler} from './history_embeddings.mojom-webui.js'; + +export interface HistoryEmbeddingsBrowserProxy { + doSomething(): Promise<boolean>; +} + +export class HistoryEmbeddingsBrowserProxyImpl implements + HistoryEmbeddingsBrowserProxy { + static instance: HistoryEmbeddingsBrowserProxy|null = null; + + handler: PageHandlerRemote = PageHandler.getRemote(); + + static getInstance(): HistoryEmbeddingsBrowserProxy { + return HistoryEmbeddingsBrowserProxyImpl.instance || + (HistoryEmbeddingsBrowserProxyImpl.instance = + new HistoryEmbeddingsBrowserProxyImpl()); + } + + static setInstance(newInstance: HistoryEmbeddingsBrowserProxy) { + HistoryEmbeddingsBrowserProxyImpl.instance = newInstance; + } + + doSomething() { + return this.handler.doSomething().then(response => response.success); + } +}
diff --git a/ui/webui/resources/cr_components/history_embeddings/history_embeddings.html b/ui/webui/resources/cr_components/history_embeddings/history_embeddings.html index 3b18e51..1316062 100644 --- a/ui/webui/resources/cr_components/history_embeddings/history_embeddings.html +++ b/ui/webui/resources/cr_components/history_embeddings/history_embeddings.html
@@ -1 +1,2 @@ hello world +[[hasLoaded_]]
diff --git a/ui/webui/resources/cr_components/history_embeddings/history_embeddings.mojom b/ui/webui/resources/cr_components/history_embeddings/history_embeddings.mojom new file mode 100644 index 0000000..45058c0 --- /dev/null +++ b/ui/webui/resources/cr_components/history_embeddings/history_embeddings.mojom
@@ -0,0 +1,11 @@ +// Copyright 2024 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +module history_embeddings.mojom; + +// Browser-side handler for requests from WebUI page. +interface PageHandler { + // Temporary method to see successful interaction between WebUI and handler. + DoSomething() => (bool success); +};
diff --git a/ui/webui/resources/cr_components/history_embeddings/history_embeddings.ts b/ui/webui/resources/cr_components/history_embeddings/history_embeddings.ts index c36b43d0..27e0ec4 100644 --- a/ui/webui/resources/cr_components/history_embeddings/history_embeddings.ts +++ b/ui/webui/resources/cr_components/history_embeddings/history_embeddings.ts
@@ -4,9 +4,10 @@ import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js'; +import {HistoryEmbeddingsBrowserProxyImpl} from './browser_proxy.js'; import {getTemplate} from './history_embeddings.html.js'; -export class HistoryEmbeddingsElement extends PolymerElement { +class HistoryEmbeddingsElement extends PolymerElement { static get is() { return 'cr-history-embeddings'; } @@ -14,6 +15,20 @@ static get template() { return getTemplate(); } + + static get properties() { + return { + hasLoaded_: Boolean, + }; + } + + private browserProxy_ = HistoryEmbeddingsBrowserProxyImpl.getInstance(); + private hasLoaded_ = false; + + override ready() { + super.ready(); + this.browserProxy_.doSomething().then(success => this.hasLoaded_ = success); + } } declare global {
diff --git a/v8 b/v8 index c3a7867..b53eb02 160000 --- a/v8 +++ b/v8
@@ -1 +1 @@ -Subproject commit c3a7867744ac5753621f6ff77120217cf165081d +Subproject commit b53eb026438b102249f055de1af5d8aff5bb0779