diff --git a/DEPS b/DEPS index 5cbe716c5a0494..e23d33993711ee 100644 --- a/DEPS +++ b/DEPS @@ -245,7 +245,7 @@ vars = { # 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': '3f95fd2ed8c4d12315a2f73484e297f69aa37d2e', + 'skia_revision': '44a83926207668ad8de5eae74d68a54812ca55ad', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling V8 # and whatever else without interference from each other. @@ -312,7 +312,7 @@ vars = { # 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': '2c2d76537b02cca4c0df364f14ee6e9b28afaf8b', + 'catapult_revision': 'd6ef4a8af654ca24f8adca2a892f6f7857d57d2f', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling libFuzzer # and whatever else without interference from each other. @@ -408,7 +408,7 @@ vars = { # Three lines of non-changing comments so that # the commit queue can handle CLs rolling feed # and whatever else without interference from each other. - 'libunwind_revision': 'd81cd6236cd771e78d7e1a1807404ef3f1d21820', + 'libunwind_revision': '4ead61094cab5ac7a90198fbe182596c4775183e', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling feed # and whatever else without interference from each other. @@ -1427,7 +1427,7 @@ deps = { }, 'src/third_party/perfetto': - Var('android_git') + '/platform/external/perfetto.git' + '@' + '9c674c8ed6844fb88b545a8df9282a9405f8a072', + Var('android_git') + '/platform/external/perfetto.git' + '@' + 'cefb3e0ec3a0580c996f801e854fe02963c03d5c', 'src/third_party/perl': { 'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3', @@ -1648,7 +1648,7 @@ deps = { Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + 'c843f8d63c8c17acfbb7d48e09059a581ba779b9', 'src/third_party/webrtc': - Var('webrtc_git') + '/src.git' + '@' + '03cb7e5a6124d7475bba4da6e5f093f1c0306c9e', + Var('webrtc_git') + '/src.git' + '@' + '977234879de72cb9df9b191adc9c7520c5c51c35', 'src/third_party/libgifcodec': Var('skia_git') + '/libgifcodec' + '@'+ Var('libgifcodec_revision'), @@ -1730,7 +1730,7 @@ deps = { Var('chromium_git') + '/v8/v8.git' + '@' + Var('v8_revision'), 'src-internal': { - 'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@3f843f82c4b67159e309b74ba45690d27f3eac5c', + 'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@6b2cb689b53fd43f282594b87295a1ebcf675b0b', 'condition': 'checkout_src_internal', }, diff --git a/WATCHLISTS b/WATCHLISTS index 77586042924fd1..d5e3604f0353f8 100644 --- a/WATCHLISTS +++ b/WATCHLISTS @@ -1485,8 +1485,9 @@ 'ui/gl/gl_.*egl.*|'\ 'ui/gl/gl_.*ozone.*' }, - 'ozone_scenic': { - 'filepath': 'ui/ozone/platform/scenic', + 'ozone_fuchsia': { + 'filepath': 'ui/ozone/platform/flatland/|'\ + 'ui/ozone/platform/scenic/' }, 'page_info' : { 'filepath': 'chrome/browser/ui/page_info/'\ @@ -2670,10 +2671,11 @@ 'origin_trials': ['chasej+watch@chromium.org', 'iclelland+watch@chromium.org'], 'ozone': ['ozone-reviews@chromium.org'], - 'ozone_scenic': ['dworsham@google.com', - 'fuchsia-reviews@chromium.org', - 'rjkroege@chromium.org', - 'spang+watch@chromium.org'], + 'ozone_fuchsia': ['dworsham@google.com', + 'emircan@google.com', + 'fuchsia-reviews@chromium.org', + 'rjkroege@chromium.org', + 'spang+watch@chromium.org'], 'page_info' : ['permissions-reviews@chromium.org', 'olesiamarukhno+watch@google.com'], 'page_load_metrics' : ['bmcquade+watch@chromium.org', diff --git a/ash/policy/policy_recommendation_restorer_unittest.cc b/ash/policy/policy_recommendation_restorer_unittest.cc index 52c0bd613dfa74..cb7074d4b21876 100644 --- a/ash/policy/policy_recommendation_restorer_unittest.cc +++ b/ash/policy/policy_recommendation_restorer_unittest.cc @@ -32,6 +32,7 @@ class PolicyRecommendationRestorerTest : public NoSessionAshTestBase { /*managed_prefs=*/new TestingPrefStore, /*supervised_user_prefs=*/new TestingPrefStore, /*extension_prefs=*/new TestingPrefStore, + /*standalone_browser_prefs=*/new TestingPrefStore, /*user_prefs=*/new TestingPrefStore, recommended_prefs_, new user_prefs::PrefRegistrySyncable, diff --git a/ash/shelf/drag_window_from_shelf_controller_unittest.cc b/ash/shelf/drag_window_from_shelf_controller_unittest.cc index 8f67a4640c8b73..fe64b2d7135759 100644 --- a/ash/shelf/drag_window_from_shelf_controller_unittest.cc +++ b/ash/shelf/drag_window_from_shelf_controller_unittest.cc @@ -36,6 +36,7 @@ #include "ui/aura/client/window_parenting_client.h" #include "ui/compositor/layer.h" #include "ui/compositor/scoped_animation_duration_scale_mode.h" +#include "ui/compositor/test/test_utils.h" #include "ui/gfx/geometry/point_f.h" #include "ui/views/widget/widget.h" #include "ui/wm/core/transient_window_manager.h" @@ -107,17 +108,17 @@ class DragWindowFromShelfControllerTest : public AshTestBase { void CancelDrag() { window_drag_controller_->CancelDrag(); } void WaitForHomeLauncherAnimationToFinish() { // Wait until home launcher animation finishes. - while (GetAppListTestHelper() - ->GetAppListView() - ->GetWidget() - ->GetLayer() - ->GetAnimator() - ->is_animating()) { - base::RunLoop run_loop; - base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( - FROM_HERE, run_loop.QuitClosure(), base::Milliseconds(200)); - run_loop.Run(); - } + ui::Layer* layer = + GetAppListTestHelper()->GetAppListView()->GetWidget()->GetLayer(); + ui::Compositor* compositor = layer->GetCompositor(); + + while (layer->GetAnimator()->is_animating()) + EXPECT_TRUE(ui::WaitForNextFrameToBePresented(compositor)); + + // Ensure there is one more frame presented after animation finishes + // to allow animation throughput data is passed from cc to ui. + ignore_result( + ui::WaitForNextFrameToBePresented(compositor, base::Milliseconds(200))); } SplitViewController* split_view_controller() { @@ -1393,4 +1394,4 @@ TEST_F(DragWindowFromShelfControllerTest, EXPECT_FALSE(transient_child_win1->IsVisible()); EXPECT_FALSE(transient_child_win2->IsVisible()); } -} // namespace ash \ No newline at end of file +} // namespace ash diff --git a/ash/system/power/power_prefs_unittest.cc b/ash/system/power/power_prefs_unittest.cc index 6f653190b3a74a..5081c2ee245ce3 100644 --- a/ash/system/power/power_prefs_unittest.cc +++ b/ash/system/power/power_prefs_unittest.cc @@ -220,6 +220,7 @@ class PowerPrefsTest : public NoSessionAshTestBase { auto pref_value_store = std::make_unique( managed_pref_store_.get() /* managed_prefs */, nullptr /* supervised_user_prefs */, nullptr /* extension_prefs */, + nullptr /* standalone_browser_prefs */, nullptr /* command_line_prefs */, user_pref_store_.get(), nullptr /* recommended_prefs */, pref_registry_->defaults().get(), pref_notifier.get()); diff --git a/ash/webui/diagnostics_ui/backend/BUILD.gn b/ash/webui/diagnostics_ui/backend/BUILD.gn index 0de4aa05c728be..99abf73182c952 100644 --- a/ash/webui/diagnostics_ui/backend/BUILD.gn +++ b/ash/webui/diagnostics_ui/backend/BUILD.gn @@ -47,6 +47,7 @@ static_library("backend") { ] deps = [ + "//ash", "//ash/constants:constants", "//ash/public/cpp", "//ash/webui/diagnostics_ui/mojom", @@ -65,6 +66,8 @@ static_library("backend") { "//services/device/public/mojom", "//ui/base", "//ui/base/ime/ash", + "//ui/chromeos/events", + "//ui/events/devices", "//ui/events/ozone", "//ui/events/ozone/evdev:event_device_info", "//ui/events/ozone/layout", @@ -118,9 +121,11 @@ source_set("unit_tests") { "//components/sync_preferences:test_support", "//content/test:test_support", "//dbus", + "//device/udev_linux:test_support", "//services/data_decoder/public/cpp:test_support", "//services/device/public/cpp:test_support", "//testing/gtest", + "//ui/chromeos/events", "//ui/events/ozone", "//ui/events/ozone/evdev:event_device_info_test_utils", "//ui/gfx", diff --git a/ash/webui/diagnostics_ui/backend/DEPS b/ash/webui/diagnostics_ui/backend/DEPS new file mode 100644 index 00000000000000..bb33d0a1a6d699 --- /dev/null +++ b/ash/webui/diagnostics_ui/backend/DEPS @@ -0,0 +1,5 @@ +specific_include_rules = { + "input_data_provider_unittest\.cc": [ + "+device/udev_linux/fake_udev_loader.h", + ] +} diff --git a/ash/webui/diagnostics_ui/backend/input_data_provider.cc b/ash/webui/diagnostics_ui/backend/input_data_provider.cc index 3e93f9bc6db506..04f2b764f8d679 100644 --- a/ash/webui/diagnostics_ui/backend/input_data_provider.cc +++ b/ash/webui/diagnostics_ui/backend/input_data_provider.cc @@ -20,6 +20,8 @@ #include "base/strings/string_util.h" #include "chromeos/system/statistics_provider.h" #include "ui/base/ime/ash/input_method_manager.h" +#include "ui/events/devices/device_util_linux.h" +#include "ui/events/devices/input_device.h" #include "ui/events/event_constants.h" #include "ui/events/keycodes/dom/keycode_converter.h" #include "ui/events/ozone/evdev/event_device_info.h" @@ -33,27 +35,27 @@ bool GetEventNodeId(base::FilePath path, int* id) { const std::string base_name_prefix = "event"; std::string base_name = path.BaseName().value(); - DCHECK(base::StartsWith(base_name, base_name_prefix)); + if (!base::StartsWith(base_name, base_name_prefix)) + return false; base_name.erase(0, base_name_prefix.length()); return base::StringToInt(base_name, id); } -mojom::ConnectionType ConnectionTypeFromInputDeviceType( - ui::InputDeviceType type) { - switch (type) { - case ui::InputDeviceType::INPUT_DEVICE_INTERNAL: - return mojom::ConnectionType::kInternal; - case ui::InputDeviceType::INPUT_DEVICE_USB: - return mojom::ConnectionType::kUsb; - case ui::InputDeviceType::INPUT_DEVICE_BLUETOOTH: - return mojom::ConnectionType::kBluetooth; - case ui::InputDeviceType::INPUT_DEVICE_UNKNOWN: - return mojom::ConnectionType::kUnknown; - } +// Determine if this particular evdev provides touchpad or touchscreen input; +// we do not want stylus devices, which also claim to be touchscreens. +bool IsTouchInputDevice(InputDeviceInformation* device_info) { + return (device_info->event_device_info.HasTouchpad() || + (device_info->event_device_info.HasTouchscreen() && + !device_info->event_device_info.HasStylus())); } + } // namespace -std::unique_ptr InputDeviceInfoHelper::GetDeviceInfo( +// All blockings calls for identifying hardware need to go here: both +// EventDeviceInfo::Initialize and ui::GetInputPathInSys can block in +// base::MakeAbsoluteFilePath. +std::unique_ptr InputDeviceInfoHelper::GetDeviceInfo( + int id, base::FilePath path) { base::ScopedFD fd(open(path.value().c_str(), O_RDWR | O_NONBLOCK)); if (fd.get() < 0) { @@ -61,12 +63,26 @@ std::unique_ptr InputDeviceInfoHelper::GetDeviceInfo( return nullptr; } - auto device_info = std::make_unique(); - if (!device_info->Initialize(fd.get(), path)) { + auto info = std::make_unique(); + + if (!info->event_device_info.Initialize(fd.get(), path)) { LOG(ERROR) << "Failed to get device info for " << path; return nullptr; } - return device_info; + + const base::FilePath sys_path = ui::GetInputPathInSys(path); + + info->path = path; + info->evdev_id = id; + info->connection_type = InputDataProvider::ConnectionTypeFromInputDeviceType( + info->event_device_info.device_type()); + info->input_device = ui::InputDevice( + id, info->event_device_info.device_type(), info->event_device_info.name(), + info->event_device_info.phys(), sys_path, + info->event_device_info.vendor_id(), info->event_device_info.product_id(), + info->event_device_info.version()); + + return info; } InputDataProvider::InputDataProvider() @@ -84,6 +100,21 @@ InputDataProvider::~InputDataProvider() { device_manager_->RemoveObserver(this); } +// static +mojom::ConnectionType InputDataProvider::ConnectionTypeFromInputDeviceType( + ui::InputDeviceType type) { + switch (type) { + case ui::InputDeviceType::INPUT_DEVICE_INTERNAL: + return mojom::ConnectionType::kInternal; + case ui::InputDeviceType::INPUT_DEVICE_USB: + return mojom::ConnectionType::kUsb; + case ui::InputDeviceType::INPUT_DEVICE_BLUETOOTH: + return mojom::ConnectionType::kBluetooth; + case ui::InputDeviceType::INPUT_DEVICE_UNKNOWN: + return mojom::ConnectionType::kUnknown; + } +} + void InputDataProvider::Initialize() { device_manager_->AddObserver(this); device_manager_->ScanDevices(this); @@ -104,6 +135,7 @@ bool InputDataProvider::ReceiverIsBound() { void InputDataProvider::OnBoundInterfaceDisconnect() { receiver_.reset(); } + void InputDataProvider::GetConnectedDevices( GetConnectedDevicesCallback callback) { std::vector keyboard_vector; @@ -158,10 +190,12 @@ void InputDataProvider::OnDeviceEvent(const ui::DeviceEvent& event) { if (event.action_type() == ui::DeviceEvent::ActionType::ADD) { info_helper_.AsyncCall(&InputDeviceInfoHelper::GetDeviceInfo) - .WithArgs(event.path()) + .WithArgs(id, event.path()) .Then(base::BindOnce(&InputDataProvider::ProcessDeviceInfo, - weak_factory_.GetWeakPtr(), id)); + weak_factory_.GetWeakPtr())); + } else { + DCHECK(event.action_type() == ui::DeviceEvent::ActionType::REMOVE); if (keyboards_.contains(id)) { keyboards_.erase(id); for (auto& observer : connected_devices_observers_) { @@ -176,40 +210,39 @@ void InputDataProvider::OnDeviceEvent(const ui::DeviceEvent& event) { } } +InputDeviceInformation::InputDeviceInformation() = default; +InputDeviceInformation::~InputDeviceInformation() = default; + void InputDataProvider::ProcessDeviceInfo( - int id, - std::unique_ptr device_info) { + std::unique_ptr device_info) { if (device_info == nullptr) { return; } - if (device_info->HasTouchpad() || - (device_info->HasTouchscreen() && !device_info->HasStylus())) { - AddTouchDevice(id, device_info.get()); - } else if (device_info->HasKeyboard()) { - AddKeyboard(id, device_info.get()); + if (IsTouchInputDevice(device_info.get())) { + AddTouchDevice(device_info.get()); + } else if (device_info->event_device_info.HasKeyboard()) { + AddKeyboard(device_info.get()); } } -void InputDataProvider::AddTouchDevice(int id, - const ui::EventDeviceInfo* device_info) { - touch_devices_[id] = touch_helper_.ConstructTouchDevice( - id, device_info, - ConnectionTypeFromInputDeviceType(device_info->device_type())); +void InputDataProvider::AddTouchDevice( + const InputDeviceInformation* device_info) { + touch_devices_[device_info->evdev_id] = + touch_helper_.ConstructTouchDevice(device_info); for (auto& observer : connected_devices_observers_) { - observer->OnTouchDeviceConnected(touch_devices_[id]->Clone()); + observer->OnTouchDeviceConnected( + touch_devices_[device_info->evdev_id]->Clone()); } } -void InputDataProvider::AddKeyboard(int id, - const ui::EventDeviceInfo* device_info) { - keyboards_[id] = keyboard_helper_.ConstructKeyboard( - id, device_info, - ConnectionTypeFromInputDeviceType(device_info->device_type())); +void InputDataProvider::AddKeyboard(const InputDeviceInformation* device_info) { + keyboards_[device_info->evdev_id] = + keyboard_helper_.ConstructKeyboard(device_info); for (auto& observer : connected_devices_observers_) { - observer->OnKeyboardConnected(keyboards_[id]->Clone()); + observer->OnKeyboardConnected(keyboards_[device_info->evdev_id]->Clone()); } } diff --git a/ash/webui/diagnostics_ui/backend/input_data_provider.h b/ash/webui/diagnostics_ui/backend/input_data_provider.h index 699e974e44eb95..cf86582d883df4 100644 --- a/ash/webui/diagnostics_ui/backend/input_data_provider.h +++ b/ash/webui/diagnostics_ui/backend/input_data_provider.h @@ -18,6 +18,7 @@ #include "mojo/public/cpp/bindings/pending_remote.h" #include "mojo/public/cpp/bindings/receiver.h" #include "mojo/public/cpp/bindings/remote_set.h" +#include "ui/chromeos/events/event_rewriter_chromeos.h" #include "ui/events/ozone/device/device_event.h" #include "ui/events/ozone/device/device_event_observer.h" #include "ui/events/ozone/device/device_manager.h" @@ -26,14 +27,37 @@ namespace ash { namespace diagnostics { +// Wrapper for tracking several pieces of information about an evdev-backed +// device. +class InputDeviceInformation { + public: + InputDeviceInformation(); + InputDeviceInformation(const InputDeviceInformation& other) = delete; + InputDeviceInformation& operator=(const InputDeviceInformation& other) = + delete; + ~InputDeviceInformation(); + + int evdev_id; + ui::EventDeviceInfo event_device_info; + ui::InputDevice input_device; + mojom::ConnectionType connection_type; + base::FilePath path; +}; + +// Class for running GetDeviceInfo in its own sequence that can block. class InputDeviceInfoHelper { public: + InputDeviceInfoHelper() {} virtual ~InputDeviceInfoHelper() {} - virtual std::unique_ptr GetDeviceInfo( + virtual std::unique_ptr GetDeviceInfo( + int evdev_id, base::FilePath path); }; +// Provides information about input devices connected to the system. Implemented +// in the browser process and called by the Diagnostics SWA (a renderer +// process). class InputDataProvider : public mojom::InputDataProvider, public ui::DeviceEventObserver { public: @@ -48,6 +72,8 @@ class InputDataProvider : public mojom::InputDataProvider, // Handler for when remote attached to |receiver_| disconnects. void OnBoundInterfaceDisconnect(); bool ReceiverIsBound(); + static mojom::ConnectionType ConnectionTypeFromInputDeviceType( + ui::InputDeviceType type); // mojom::InputDataProvider: void GetConnectedDevices(GetConnectedDevicesCallback callback) override; @@ -69,15 +95,15 @@ class InputDataProvider : public mojom::InputDataProvider, private: void Initialize(); - void ProcessDeviceInfo(int id, - std::unique_ptr device_info); + void ProcessDeviceInfo(std::unique_ptr device_info); - void AddTouchDevice(int id, const ui::EventDeviceInfo* device_info); - void AddKeyboard(int id, const ui::EventDeviceInfo* device_info); + void AddTouchDevice(const InputDeviceInformation* device_info); + void AddKeyboard(const InputDeviceInformation* device_info); InputDataProviderKeyboard keyboard_helper_; InputDataProviderTouch touch_helper_; + // Map by evdev ids to information blocks base::flat_map keyboards_; base::flat_map touch_devices_; diff --git a/ash/webui/diagnostics_ui/backend/input_data_provider_keyboard.cc b/ash/webui/diagnostics_ui/backend/input_data_provider_keyboard.cc index 568bad75111ef1..aab7065f5c9836 100644 --- a/ash/webui/diagnostics_ui/backend/input_data_provider_keyboard.cc +++ b/ash/webui/diagnostics_ui/backend/input_data_provider_keyboard.cc @@ -4,10 +4,17 @@ #include "ash/webui/diagnostics_ui/backend/input_data_provider_keyboard.h" +#include +#include #include #include "ash/constants/ash_switches.h" +#include "ash/display/privacy_screen_controller.h" +#include "ash/shell.h" +#include "ash/webui/diagnostics_ui/backend/input_data_provider.h" #include "base/command_line.h" +#include "base/containers/fixed_flat_map.h" +#include "base/files/scoped_file.h" #include "base/logging.h" #include "base/run_loop.h" #include "base/strings/strcat.h" @@ -16,6 +23,9 @@ #include "base/strings/string_util.h" #include "chromeos/system/statistics_provider.h" #include "ui/base/ime/ash/input_method_manager.h" +#include "ui/chromeos/events/event_rewriter_chromeos.h" +#include "ui/events/devices/device_util_linux.h" +#include "ui/events/devices/input_device.h" #include "ui/events/event_constants.h" #include "ui/events/keycodes/dom/keycode_converter.h" #include "ui/events/ozone/evdev/event_device_info.h" @@ -25,6 +35,135 @@ namespace diagnostics { namespace { +enum { + kFKey1 = 0, + kFKey2, + kFKey3, + kFKey4, + kFKey5, + kFKey6, + kFKey7, + kFKey8, + kFKey9, + kFKey10, + kFKey11, + kFKey12, + kFKey13, + kFKey14, + kFKey15 +}; + +// Mapping from keyboard scancodes to TopRowKeys (must be in scancode-sorted +// order). This replicates and should be identical to the mapping behaviour +// of ChromeOS: changes will be needed if new AT scancodes or HID mappings +// are used in a top-row key, likely added in +// ui/events/keycodes/dom/dom_code_data.inc +// +// Note that there are no dedicated scancodes for kScreenMirror. +constexpr auto kScancodeMapping = + base::MakeFixedFlatMap({ + // Vivaldi extended Set-1 AT-style scancodes + {0x90, mojom::TopRowKey::kPreviousTrack}, + {0x91, mojom::TopRowKey::kFullscreen}, + {0x92, mojom::TopRowKey::kOverview}, + {0x93, mojom::TopRowKey::kScreenshot}, + {0x94, mojom::TopRowKey::kScreenBrightnessDown}, + {0x95, mojom::TopRowKey::kScreenBrightnessUp}, + {0x96, mojom::TopRowKey::kPrivacyScreenToggle}, + {0x97, mojom::TopRowKey::kKeyboardBacklightDown}, + {0x98, mojom::TopRowKey::kKeyboardBacklightUp}, + {0x99, mojom::TopRowKey::kNextTrack}, + {0x9A, mojom::TopRowKey::kPlayPause}, + {0xA0, mojom::TopRowKey::kVolumeMute}, + {0xAE, mojom::TopRowKey::kVolumeDown}, + {0xB0, mojom::TopRowKey::kVolumeUp}, + {0xD3, mojom::TopRowKey::kDelete}, // Only relevant for Drallion. + {0xE9, mojom::TopRowKey::kForward}, + {0xEA, mojom::TopRowKey::kBack}, + {0xE7, mojom::TopRowKey::kRefresh}, + + // HID 32-bit usage codes + {0x070046, mojom::TopRowKey::kScreenshot}, + {0x0C00E2, mojom::TopRowKey::kVolumeMute}, + {0x0C00E9, mojom::TopRowKey::kVolumeUp}, + {0x0C00EA, mojom::TopRowKey::kVolumeDown}, + {0x0C006F, mojom::TopRowKey::kScreenBrightnessUp}, + {0x0C0070, mojom::TopRowKey::kScreenBrightnessDown}, + {0x0C0079, mojom::TopRowKey::kKeyboardBacklightUp}, + {0x0C007A, mojom::TopRowKey::kKeyboardBacklightDown}, + {0x0C00B5, mojom::TopRowKey::kNextTrack}, + {0x0C00B6, mojom::TopRowKey::kPreviousTrack}, + {0x0C00CD, mojom::TopRowKey::kPlayPause}, + {0x0C0224, mojom::TopRowKey::kBack}, + {0x0C0225, mojom::TopRowKey::kForward}, + {0x0C0227, mojom::TopRowKey::kRefresh}, + {0x0C0232, mojom::TopRowKey::kFullscreen}, + {0x0C029F, mojom::TopRowKey::kOverview}, + {0x0C02D0, mojom::TopRowKey::kPrivacyScreenToggle}, + }); + +// Hard-coded top-row key mappings. These are intended to match the behaviour of +// EventRewriterChromeOS::RewriteFunctionKeys for historical keyboards. No +// updates should be needed, as all new keyboards are expected to be using +// customizable top row keys (vivaldi). + +constexpr mojom::TopRowKey kSystemKeys1[] = { + mojom::TopRowKey::kBack, + mojom::TopRowKey::kForward, + mojom::TopRowKey::kRefresh, + mojom::TopRowKey::kFullscreen, + mojom::TopRowKey::kOverview, + mojom::TopRowKey::kScreenBrightnessDown, + mojom::TopRowKey::kScreenBrightnessUp, + mojom::TopRowKey::kVolumeMute, + mojom::TopRowKey::kVolumeDown, + mojom::TopRowKey::kVolumeUp}; + +constexpr mojom::TopRowKey kSystemKeys2[] = { + mojom::TopRowKey::kBack, + mojom::TopRowKey::kRefresh, + mojom::TopRowKey::kFullscreen, + mojom::TopRowKey::kOverview, + mojom::TopRowKey::kScreenBrightnessDown, + mojom::TopRowKey::kScreenBrightnessUp, + mojom::TopRowKey::kPlayPause, + mojom::TopRowKey::kVolumeMute, + mojom::TopRowKey::kVolumeDown, + mojom::TopRowKey::kVolumeUp}; + +constexpr mojom::TopRowKey kSystemKeysWilco[] = { + mojom::TopRowKey::kBack, + mojom::TopRowKey::kRefresh, + mojom::TopRowKey::kFullscreen, + mojom::TopRowKey::kOverview, + mojom::TopRowKey::kScreenBrightnessDown, + mojom::TopRowKey::kScreenBrightnessUp, + mojom::TopRowKey::kVolumeMute, + mojom::TopRowKey::kVolumeDown, + mojom::TopRowKey::kVolumeUp, + mojom::TopRowKey::kNone, // F10 + mojom::TopRowKey::kNone, // F11 + mojom::TopRowKey::kScreenMirror, // F12 + mojom::TopRowKey::kDelete // Just a normal Delete key, but in the top row. +}; + +constexpr mojom::TopRowKey kSystemKeysDrallion[] = { + mojom::TopRowKey::kBack, + mojom::TopRowKey::kRefresh, + mojom::TopRowKey::kFullscreen, + mojom::TopRowKey::kOverview, + mojom::TopRowKey::kScreenBrightnessDown, + mojom::TopRowKey::kScreenBrightnessUp, + mojom::TopRowKey::kVolumeMute, + mojom::TopRowKey::kVolumeDown, + mojom::TopRowKey::kVolumeUp, + mojom::TopRowKey::kNone, // F10 + mojom::TopRowKey::kNone, // F11 + mojom::TopRowKey::kNone, // F12 - May be Privacy Screen on some models. + mojom::TopRowKey::kScreenMirror, + mojom::TopRowKey::kDelete // Just a normal Delete key, but in the top row. +}; + mojom::MechanicalLayout GetSystemMechanicalLayout() { chromeos::system::StatisticsProvider* stats_provider = chromeos::system::StatisticsProvider::GetInstance(); @@ -151,17 +290,96 @@ mojom::KeyGlyphSetPtr InputDataProviderKeyboard::LookupGlyphSet( return glyph_set; } +void InputDataProviderKeyboard::ProcessKeyboardTopRowLayout( + const InputDeviceInformation* device_info, + ui::EventRewriterChromeOS::KeyboardTopRowLayout* out_top_row_layout, + std::vector* out_top_row_keys) { + ui::InputDevice input_device = device_info->input_device; + ui::EventRewriterChromeOS::DeviceType device_type; + ui::EventRewriterChromeOS::KeyboardTopRowLayout top_row_layout; + base::flat_map + scan_code_map; + ui::EventRewriterChromeOS::IdentifyKeyboard(input_device, &device_type, + &top_row_layout, &scan_code_map); + + // Simple array in physical order from left to right + std::vector top_row_keys = {}; + + switch (top_row_layout) { + case ui::EventRewriterChromeOS::kKbdTopRowLayoutWilco: + top_row_keys.assign(std::begin(kSystemKeysWilco), + std::end(kSystemKeysWilco)); + break; + + case ui::EventRewriterChromeOS::kKbdTopRowLayoutDrallion: + top_row_keys.assign(std::begin(kSystemKeysDrallion), + std::end(kSystemKeysDrallion)); + + // On some Drallion devices, the F12 key is used for the Privacy Screen. + + // This should be the same logic as in + // EventRewriterControllerImpl::Initialize. This is a historic device, and + // this logic should not need to be updated, as newer devices will use + // custom top row layouts (vivaldi). + if (Shell::Get()->privacy_screen_controller() && + Shell::Get()->privacy_screen_controller()->IsSupported()) { + top_row_keys[kFKey12] = mojom::TopRowKey::kPrivacyScreenToggle; + } + + break; + + case ui::EventRewriterChromeOS::kKbdTopRowLayoutCustom: + + // Process scan-code map generated from custom top-row key layout: it maps + // from physical scan codes to several things, including VKEY key-codes, + // which we will use to produce indexes. + + for (auto iter = scan_code_map.begin(); iter != scan_code_map.end(); + iter++) { + size_t fn_key_number = iter->second.key_code - ui::VKEY_F1; + uint32_t scancode = iter->first; + + if (top_row_keys.size() < fn_key_number + 1) + top_row_keys.resize(fn_key_number + 1, mojom::TopRowKey::kNone); + + if (kScancodeMapping.contains(scancode)) + top_row_keys[fn_key_number] = kScancodeMapping.at(scancode); + else + top_row_keys[fn_key_number] = mojom::TopRowKey::kUnknown; + } + break; + + case ui::EventRewriterChromeOS::kKbdTopRowLayout2: + top_row_keys.assign(std::begin(kSystemKeys2), std::end(kSystemKeys2)); + break; + + case ui::EventRewriterChromeOS::kKbdTopRowLayout1: + default: + top_row_keys.assign(std::begin(kSystemKeys1), std::end(kSystemKeys1)); + } + + *out_top_row_layout = std::move(top_row_layout); + *out_top_row_keys = std::move(top_row_keys); +} + mojom::KeyboardInfoPtr InputDataProviderKeyboard::ConstructKeyboard( - int id, - const ui::EventDeviceInfo* device_info, - mojom::ConnectionType connection_type) { + const InputDeviceInformation* device_info) { mojom::KeyboardInfoPtr result = mojom::KeyboardInfo::New(); - result->id = id; - result->connection_type = connection_type; - result->name = device_info->name(); + + result->id = device_info->evdev_id; + result->connection_type = device_info->connection_type; + result->name = device_info->event_device_info.name(); + + // TODO(crbug.com/1207678): review support for WWCB keyboards, Chromebase + // keyboards, and Dell KM713 Chrome keyboard. + + ui::EventRewriterChromeOS::KeyboardTopRowLayout top_row_layout_type; + + ProcessKeyboardTopRowLayout(device_info, &top_row_layout_type, + &result->top_row_keys); if (result->connection_type == mojom::ConnectionType::kInternal) { - if (device_info->HasKeyEvent(KEY_KBD_LAYOUT_NEXT)) { + if (device_info->event_device_info.HasKeyEvent(KEY_KBD_LAYOUT_NEXT)) { // Only Dell Enterprise devices have this key, marked by a globe icon. result->physical_layout = mojom::PhysicalLayout::kChromeOSDellEnterprise; } else { @@ -176,14 +394,36 @@ mojom::KeyboardInfoPtr InputDataProviderKeyboard::ConstructKeyboard( chromeos::switches::kHasNumberPad) ? mojom::NumberPadPresence::kPresent : mojom::NumberPadPresence::kNotPresent; + + // Log if there is contradictory information. + if (base::CommandLine::ForCurrentProcess()->HasSwitch( + chromeos::switches::kHasNumberPad) && + !device_info->event_device_info.HasNumberpad()) + LOG(ERROR) << "OS believes internal numberpad is implemented, but " + "evdev disagrees."; } else { result->physical_layout = mojom::PhysicalLayout::kUnknown; - result->number_pad_present = mojom::NumberPadPresence::kUnknown; - // TODO(crbug.com/1207678): support WWCB keyboards, Chromebase keyboards, - // and Dell KM713 Chrome keyboard. + + if (top_row_layout_type == ui::EventRewriterChromeOS::KeyboardTopRowLayout:: + kKbdTopRowLayoutCustom) { + // If keyboard has WWCB top row custom layout (vivaldi) then we can trust + // the HID descriptor to be accurate about presence of keys. + result->number_pad_present = + !device_info->event_device_info.HasNumberpad() + ? mojom::NumberPadPresence::kNotPresent + : mojom::NumberPadPresence::kPresent; + } else { + // Without WWCB information, absence of KP keycodes means it definitely + // doesn't have a numberpad, but the presence isn't a reliable indicator. + result->number_pad_present = + !device_info->event_device_info.HasNumberpad() + ? mojom::NumberPadPresence::kNotPresent + : mojom::NumberPadPresence::kUnknown; + } } - result->has_assistant_key = device_info->HasKeyEvent(KEY_ASSISTANT); + result->has_assistant_key = + device_info->event_device_info.HasKeyEvent(KEY_ASSISTANT); return result; } diff --git a/ash/webui/diagnostics_ui/backend/input_data_provider_keyboard.h b/ash/webui/diagnostics_ui/backend/input_data_provider_keyboard.h index 14776d18d35bf3..ea6f94817d8913 100644 --- a/ash/webui/diagnostics_ui/backend/input_data_provider_keyboard.h +++ b/ash/webui/diagnostics_ui/backend/input_data_provider_keyboard.h @@ -7,6 +7,7 @@ #include "ash/webui/diagnostics_ui/mojom/input_data_provider.mojom.h" #include "base/memory/weak_ptr.h" +#include "ui/chromeos/events/event_rewriter_chromeos.h" #include "ui/events/ozone/evdev/event_device_info.h" #include "ui/events/ozone/layout/xkb/xkb_evdev_codes.h" #include "ui/events/ozone/layout/xkb/xkb_keyboard_layout_engine.h" @@ -14,6 +15,8 @@ namespace ash { namespace diagnostics { +class InputDeviceInformation; + // Helper to provide InputDataProvider diagnostic interface with // keyboard-specific logic. class InputDataProviderKeyboard { @@ -29,15 +32,18 @@ class InputDataProviderKeyboard { mojom::InputDataProvider::GetKeyboardVisualLayoutCallback callback); mojom::KeyboardInfoPtr ConstructKeyboard( - int id, - const ui::EventDeviceInfo* device_info, - mojom::ConnectionType connection_type); + const InputDeviceInformation* device_info); private: void ProcessXkbLayout( mojom::InputDataProvider::GetKeyboardVisualLayoutCallback callback); mojom::KeyGlyphSetPtr LookupGlyphSet(uint32_t evdev_code); + void ProcessKeyboardTopRowLayout( + const InputDeviceInformation* device_info, + ui::EventRewriterChromeOS::KeyboardTopRowLayout* out_top_row_layout, + std::vector* out_top_row_keys); + ui::XkbEvdevCodes xkb_evdev_codes_; ui::XkbKeyboardLayoutEngine xkb_layout_engine_; diff --git a/ash/webui/diagnostics_ui/backend/input_data_provider_touch.cc b/ash/webui/diagnostics_ui/backend/input_data_provider_touch.cc index 29335cc5bc38f5..48b05e871db8bb 100644 --- a/ash/webui/diagnostics_ui/backend/input_data_provider_touch.cc +++ b/ash/webui/diagnostics_ui/backend/input_data_provider_touch.cc @@ -3,6 +3,8 @@ // found in the LICENSE file. #include "ash/webui/diagnostics_ui/backend/input_data_provider_touch.h" +#include "ash/webui/diagnostics_ui/backend/input_data_provider.h" +#include "base/logging.h" #include "ui/events/ozone/evdev/event_device_info.h" namespace ash { @@ -12,15 +14,17 @@ InputDataProviderTouch::InputDataProviderTouch() {} InputDataProviderTouch::~InputDataProviderTouch() {} mojom::TouchDeviceInfoPtr InputDataProviderTouch::ConstructTouchDevice( - int id, - const ui::EventDeviceInfo* device_info, - mojom::ConnectionType connection_type) { + const InputDeviceInformation* device_info) { mojom::TouchDeviceInfoPtr result = mojom::TouchDeviceInfo::New(); - result->id = id; - result->connection_type = connection_type; - result->type = device_info->HasTouchpad() ? mojom::TouchDeviceType::kPointer - : mojom::TouchDeviceType::kDirect; - result->name = device_info->name(); + + result->id = device_info->evdev_id; + result->connection_type = device_info->connection_type; + + // TODO(crbug.com/1207678): double-check logic + result->type = device_info->event_device_info.HasTouchpad() + ? mojom::TouchDeviceType::kPointer + : mojom::TouchDeviceType::kDirect; + result->name = device_info->event_device_info.name(); return result; } diff --git a/ash/webui/diagnostics_ui/backend/input_data_provider_touch.h b/ash/webui/diagnostics_ui/backend/input_data_provider_touch.h index 93aaa5655511e5..5fe7db225a3e14 100644 --- a/ash/webui/diagnostics_ui/backend/input_data_provider_touch.h +++ b/ash/webui/diagnostics_ui/backend/input_data_provider_touch.h @@ -11,6 +11,8 @@ namespace ash { namespace diagnostics { +class InputDeviceInformation; + // Helper to provide InputDataProvider diagnostic interface with touch-specific // logic. class InputDataProviderTouch { @@ -21,9 +23,7 @@ class InputDataProviderTouch { ~InputDataProviderTouch(); mojom::TouchDeviceInfoPtr ConstructTouchDevice( - int id, - const ui::EventDeviceInfo* device_info, - mojom::ConnectionType connection_type); + const InputDeviceInformation* device_info); }; } // namespace diagnostics diff --git a/ash/webui/diagnostics_ui/backend/input_data_provider_unittest.cc b/ash/webui/diagnostics_ui/backend/input_data_provider_unittest.cc index a09db7b9717024..e07bf86face01f 100644 --- a/ash/webui/diagnostics_ui/backend/input_data_provider_unittest.cc +++ b/ash/webui/diagnostics_ui/backend/input_data_provider_unittest.cc @@ -8,10 +8,13 @@ #include "base/command_line.h" #include "base/run_loop.h" +#include "base/strings/stringprintf.h" #include "base/test/bind.h" #include "base/test/task_environment.h" +#include "base/test/test_future.h" #include "chromeos/system/fake_statistics_provider.h" #include "chromeos/system/statistics_provider.h" +#include "device/udev_linux/fake_udev_loader.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/events/ozone/device/device_event_observer.h" #include "ui/events/ozone/device/device_manager.h" @@ -20,6 +23,77 @@ namespace ash { namespace diagnostics { +namespace { + +constexpr mojom::TopRowKey kClassicTopRowKeys[] = { + mojom::TopRowKey::kBack, + mojom::TopRowKey::kForward, + mojom::TopRowKey::kRefresh, + mojom::TopRowKey::kFullscreen, + mojom::TopRowKey::kOverview, + mojom::TopRowKey::kScreenBrightnessDown, + mojom::TopRowKey::kScreenBrightnessUp, + mojom::TopRowKey::kVolumeMute, + mojom::TopRowKey::kVolumeDown, + mojom::TopRowKey::kVolumeUp}; + +constexpr mojom::TopRowKey kInternalJinlonTopRowKeys[] = { + mojom::TopRowKey::kBack, + mojom::TopRowKey::kRefresh, + mojom::TopRowKey::kFullscreen, + mojom::TopRowKey::kOverview, + mojom::TopRowKey::kScreenshot, + mojom::TopRowKey::kScreenBrightnessDown, + mojom::TopRowKey::kScreenBrightnessUp, + mojom::TopRowKey::kPrivacyScreenToggle, + mojom::TopRowKey::kKeyboardBacklightDown, + mojom::TopRowKey::kKeyboardBacklightUp, + mojom::TopRowKey::kVolumeMute, + mojom::TopRowKey::kVolumeDown, + mojom::TopRowKey::kVolumeUp}; + +// One possible variant of a Dell configuration +constexpr mojom::TopRowKey kInternalDellTopRowKeys[] = { + mojom::TopRowKey::kBack, + mojom::TopRowKey::kRefresh, + mojom::TopRowKey::kFullscreen, + mojom::TopRowKey::kOverview, + mojom::TopRowKey::kScreenBrightnessDown, + mojom::TopRowKey::kScreenBrightnessUp, + mojom::TopRowKey::kVolumeMute, + mojom::TopRowKey::kVolumeDown, + mojom::TopRowKey::kVolumeUp, + mojom::TopRowKey::kNone, + mojom::TopRowKey::kNone, + mojom::TopRowKey::kScreenMirror, + mojom::TopRowKey::kDelete}; + +constexpr char kKbdTopRowPropertyName[] = "CROS_KEYBOARD_TOP_ROW_LAYOUT"; +constexpr char kKbdTopRowLayoutAttributeName[] = "function_row_physmap"; + +constexpr char kSillyDeviceName[] = "eventWithoutANumber"; + +constexpr char kInvalidMechnicalLayout[] = "Not ANSI, JIS, or ISO"; + +// NOTE: This is only creates a simple ui::InputDevice based on a device +// capabilities report; it is not suitable for subclasses of ui::InputDevice. +ui::InputDevice InputDeviceFromCapabilities( + int device_id, + const ui::DeviceCapabilities& capabilities) { + ui::EventDeviceInfo device_info = {}; + ui::CapabilitiesToDeviceInfo(capabilities, &device_info); + + const std::string sys_path = + base::StringPrintf("/dev/input/event%d-%s", device_id, capabilities.path); + + return ui::InputDevice(device_id, device_info.device_type(), + device_info.name(), device_info.phys(), + base::FilePath(sys_path), device_info.vendor_id(), + device_info.product_id(), device_info.version()); +} + +} // namespace + class FakeDeviceManager : public ui::DeviceManager { public: FakeDeviceManager() {} @@ -60,34 +134,71 @@ class FakeConnectedDevicesObserver : public mojom::ConnectedDevicesObserver { class FakeInputDeviceInfoHelper : public InputDeviceInfoHelper { public: - std::unique_ptr GetDeviceInfo( + FakeInputDeviceInfoHelper() {} + + ~FakeInputDeviceInfoHelper() override {} + + std::unique_ptr GetDeviceInfo( + int id, base::FilePath path) override { - std::unique_ptr dev_info = - std::make_unique(); ui::DeviceCapabilities device_caps; - std::string base_name = path.BaseName().value(); + const std::string base_name = path.BaseName().value(); + if (base_name == "event0") { device_caps = ui::kLinkKeyboard; + EXPECT_EQ(0, id); } else if (base_name == "event1") { device_caps = ui::kLinkTouchpad; + EXPECT_EQ(1, id); } else if (base_name == "event2") { device_caps = ui::kKohakuTouchscreen; + EXPECT_EQ(2, id); } else if (base_name == "event3") { device_caps = ui::kKohakuStylus; + EXPECT_EQ(3, id); } else if (base_name == "event4") { device_caps = ui::kHpUsbKeyboard; + EXPECT_EQ(4, id); } else if (base_name == "event5") { - device_caps = ui::kSarienKeyboard; + device_caps = ui::kSarienKeyboard; // Wilco + EXPECT_EQ(5, id); } else if (base_name == "event6") { device_caps = ui::kEveKeyboard; + EXPECT_EQ(6, id); } else if (base_name == "event7") { + device_caps = ui::kJinlonKeyboard; + EXPECT_EQ(7, id); + } else if (base_name == "event8") { + device_caps = ui::kMicrosoftBluetoothNumberPad; + EXPECT_EQ(8, id); + } else if (base_name == "event9") { + device_caps = ui::kLogitechTouchKeyboardK400; + EXPECT_EQ(9, id); + } else if (base_name == kSillyDeviceName) { + // Simulate a device that is properly described, but has a malformed + // device name. + EXPECT_EQ(98, id); + device_caps = ui::kLinkKeyboard; + } else if (base_name == "event99") { + EXPECT_EQ(99, id); // Simulate a device that couldn't be opened or have its info determined // for whatever reason. return nullptr; } - EXPECT_TRUE(ui::CapabilitiesToDeviceInfo(device_caps, dev_info.get())); - return dev_info; + auto info = std::make_unique(); + + EXPECT_TRUE( + ui::CapabilitiesToDeviceInfo(device_caps, &info->event_device_info)); + info->evdev_id = id; + info->path = path; + info->input_device = + InputDeviceFromCapabilities(info->evdev_id, device_caps); + info->connection_type = + InputDataProvider::ConnectionTypeFromInputDeviceType( + info->event_device_info.device_type()); + + return info; } }; @@ -112,7 +223,46 @@ class InputDataProviderTest : public testing::Test { auto manager = std::make_unique(); manager_ = manager.get(); + fake_udev_ = std::make_unique(); provider_ = std::make_unique(std::move(manager)); + + // Apply these early; delaying until + // FakeInputDeviceInfoHelper::GetDeviceInfo() is not appropriate, as + // fake_udev is not thread safe. (If multiple devices are constructed in a + // row, then GetDeviceInfo() invocation can overlap with + // ProcessInputDataProvider::ProcessDeviceInfo() which reads from udev). + UdevAddFakeDeviceCapabilities("/dev/input/event5", ui::kSarienKeyboard); + UdevAddFakeDeviceCapabilities("/dev/input/event6", ui::kEveKeyboard); + UdevAddFakeDeviceCapabilities("/dev/input/event7", ui::kJinlonKeyboard); + } + + void UdevAddFakeDeviceCapabilities( + const std::string& device_name, + const ui::DeviceCapabilities& device_caps) { + std::map + sysfs_properties; // Old style numeric tags + std::map + sysfs_attributes; // New style vivaldi scancode layouts + + if (device_caps.kbd_function_row_physmap && + strlen(device_caps.kbd_function_row_physmap) > 0) { + sysfs_attributes[kKbdTopRowLayoutAttributeName] = + device_caps.kbd_function_row_physmap; + } + + if (device_caps.kbd_top_row_layout && + strlen(device_caps.kbd_top_row_layout) > 0) { + sysfs_properties[kKbdTopRowPropertyName] = device_caps.kbd_top_row_layout; + } + + // Each device needs a unique sys path + const std::string sys_path = device_name + "-" + device_caps.path; + + fake_udev_->AddFakeDevice(device_caps.name, sys_path.c_str(), + /*subsystem=*/"input", /*devnode=*/absl::nullopt, + /*devtype=*/absl::nullopt, + std::move(sysfs_attributes), + std::move(sysfs_properties)); } ~InputDataProviderTest() override { @@ -123,12 +273,12 @@ class InputDataProviderTest : public testing::Test { protected: base::test::TaskEnvironment task_environment_; FakeDeviceManager* manager_; + std::unique_ptr fake_udev_; chromeos::system::FakeStatisticsProvider statistics_provider_; std::unique_ptr provider_; }; TEST_F(InputDataProviderTest, GetConnectedDevices_DeviceInfoMapping) { - base::RunLoop run_loop; ui::DeviceEvent event0(ui::DeviceEvent::DeviceType::INPUT, ui::DeviceEvent::ActionType::ADD, base::FilePath("/dev/input/event0")); @@ -147,44 +297,48 @@ TEST_F(InputDataProviderTest, GetConnectedDevices_DeviceInfoMapping) { provider_->OnDeviceEvent(event3); task_environment_.RunUntilIdle(); - provider_->GetConnectedDevices(base::BindLambdaForTesting( - [&](std::vector keyboards, - std::vector touch_devices) { - ASSERT_EQ(1ul, keyboards.size()); - // The stylus device should be filtered out, hence only 2 touch devices. - ASSERT_EQ(2ul, touch_devices.size()); - - mojom::KeyboardInfoPtr keyboard = keyboards[0].Clone(); - EXPECT_EQ(0u, keyboard->id); - EXPECT_EQ(mojom::ConnectionType::kInternal, keyboard->connection_type); - EXPECT_EQ("AT Translated Set 2 keyboard", keyboard->name); - - mojom::TouchDeviceInfoPtr touchpad = touch_devices[0].Clone(); - EXPECT_EQ(1u, touchpad->id); - EXPECT_EQ(mojom::ConnectionType::kInternal, touchpad->connection_type); - EXPECT_EQ(mojom::TouchDeviceType::kPointer, touchpad->type); - EXPECT_EQ("Atmel maXTouch Touchpad", touchpad->name); - - mojom::TouchDeviceInfoPtr touchscreen = touch_devices[1].Clone(); - EXPECT_EQ(2u, touchscreen->id); - EXPECT_EQ(mojom::ConnectionType::kInternal, - touchscreen->connection_type); - EXPECT_EQ(mojom::TouchDeviceType::kDirect, touchscreen->type); - EXPECT_EQ("Atmel maXTouch Touchscreen", touchscreen->name); - - run_loop.Quit(); - })); - run_loop.Run(); + base::test::TestFuture, + std::vector> + future; + provider_->GetConnectedDevices(future.GetCallback()); + + const auto& keyboards = future.Get<0>(); + const auto& touch_devices = future.Get<1>(); + + ASSERT_EQ(1ul, keyboards.size()); + // The stylus device should be filtered out, hence only 2 touch devices. + ASSERT_EQ(2ul, touch_devices.size()); + + const mojom::KeyboardInfoPtr& keyboard = keyboards[0]; + EXPECT_EQ(0u, keyboard->id); + EXPECT_EQ(mojom::ConnectionType::kInternal, keyboard->connection_type); + EXPECT_EQ("AT Translated Set 2 keyboard", keyboard->name); + + const mojom::TouchDeviceInfoPtr& touchpad = touch_devices[0]; + EXPECT_EQ(1u, touchpad->id); + EXPECT_EQ(mojom::ConnectionType::kInternal, touchpad->connection_type); + EXPECT_EQ(mojom::TouchDeviceType::kPointer, touchpad->type); + EXPECT_EQ("Atmel maXTouch Touchpad", touchpad->name); + + const mojom::TouchDeviceInfoPtr& touchscreen = touch_devices[1]; + EXPECT_EQ(2u, touchscreen->id); + EXPECT_EQ(mojom::ConnectionType::kInternal, touchscreen->connection_type); + EXPECT_EQ(mojom::TouchDeviceType::kDirect, touchscreen->type); + EXPECT_EQ("Atmel maXTouch Touchscreen", touchscreen->name); } TEST_F(InputDataProviderTest, GetConnectedDevices_AddEventAfterFirstCall) { - base::RunLoop run_loop; - provider_->GetConnectedDevices(base::BindLambdaForTesting( - [&](std::vector keyboards, - std::vector touch_devices) { - ASSERT_EQ(0ul, keyboards.size()); - ASSERT_EQ(0ul, touch_devices.size()); - })); + { + base::test::TestFuture, + std::vector> + future; + provider_->GetConnectedDevices(future.GetCallback()); + + const auto& keyboards = future.Get<0>(); + const auto& touch_devices = future.Get<1>(); + ASSERT_EQ(0ul, keyboards.size()); + ASSERT_EQ(0ul, touch_devices.size()); + } ui::DeviceEvent event(ui::DeviceEvent::DeviceType::INPUT, ui::DeviceEvent::ActionType::ADD, @@ -192,25 +346,60 @@ TEST_F(InputDataProviderTest, GetConnectedDevices_AddEventAfterFirstCall) { provider_->OnDeviceEvent(event); task_environment_.RunUntilIdle(); - provider_->GetConnectedDevices(base::BindLambdaForTesting( - [&](std::vector keyboards, - std::vector touch_devices) { - ASSERT_EQ(1ul, keyboards.size()); - mojom::KeyboardInfoPtr keyboard = keyboards[0].Clone(); - EXPECT_EQ(4u, keyboard->id); - EXPECT_EQ(mojom::ConnectionType::kUsb, keyboard->connection_type); - EXPECT_EQ("Chicony HP Elite USB Keyboard", keyboard->name); + { + base::test::TestFuture, + std::vector> + future; + provider_->GetConnectedDevices(future.GetCallback()); + + const auto& keyboards = future.Get<0>(); + const auto& touch_devices = future.Get<1>(); + + ASSERT_EQ(1ul, keyboards.size()); + const mojom::KeyboardInfoPtr& keyboard = keyboards[0]; + EXPECT_EQ(4u, keyboard->id); + EXPECT_EQ(mojom::ConnectionType::kUsb, keyboard->connection_type); + EXPECT_EQ("Chicony HP Elite USB Keyboard", keyboard->name); + + EXPECT_EQ(0ul, touch_devices.size()); + } +} + +TEST_F(InputDataProviderTest, GetConnectedDevices_AddUnusualDevices) { + ui::DeviceEvent event0(ui::DeviceEvent::DeviceType::INPUT, + ui::DeviceEvent::ActionType::ADD, + base::FilePath("/dev/input/event8")); + ui::DeviceEvent event1(ui::DeviceEvent::DeviceType::INPUT, + ui::DeviceEvent::ActionType::ADD, + base::FilePath("/dev/input/event9")); + provider_->OnDeviceEvent(event0); + provider_->OnDeviceEvent(event1); + task_environment_.RunUntilIdle(); + + base::test::TestFuture, + std::vector> + future; + provider_->GetConnectedDevices(future.GetCallback()); - EXPECT_EQ(0ul, touch_devices.size()); + const auto& keyboards = future.Get<0>(); + const auto& touch_devices = future.Get<1>(); - run_loop.Quit(); - })); + ASSERT_EQ(2ul, keyboards.size()); + // The stylus device should be filtered out, hence only 2 touch devices. + ASSERT_EQ(0ul, touch_devices.size()); - run_loop.Run(); + const mojom::KeyboardInfoPtr& keyboard1 = keyboards[0]; + EXPECT_EQ(8u, keyboard1->id); + EXPECT_EQ(mojom::ConnectionType::kBluetooth, keyboard1->connection_type); + EXPECT_EQ(ui::kMicrosoftBluetoothNumberPad.name, keyboard1->name); + + const mojom::KeyboardInfoPtr& keyboard2 = keyboards[1]; + EXPECT_EQ(9u, keyboard2->id); + EXPECT_EQ(mojom::ConnectionType::kUnknown, keyboard2->connection_type); + EXPECT_EQ(ui::kLogitechTouchKeyboardK400.name, keyboard2->name); } TEST_F(InputDataProviderTest, GetConnectedDevices_Remove) { - base::RunLoop run_loop; ui::DeviceEvent add_touch_event(ui::DeviceEvent::DeviceType::INPUT, ui::DeviceEvent::ActionType::ADD, base::FilePath("/dev/input/event1")); @@ -221,15 +410,21 @@ TEST_F(InputDataProviderTest, GetConnectedDevices_Remove) { provider_->OnDeviceEvent(add_kbd_event); task_environment_.RunUntilIdle(); - provider_->GetConnectedDevices(base::BindLambdaForTesting( - [&](std::vector keyboards, - std::vector touch_devices) { - ASSERT_EQ(1ul, keyboards.size()); - EXPECT_EQ(4u, keyboards[0]->id); + { + base::test::TestFuture, + std::vector> + future; + provider_->GetConnectedDevices(future.GetCallback()); + + const auto& keyboards = future.Get<0>(); + const auto& touch_devices = future.Get<1>(); - ASSERT_EQ(1ul, touch_devices.size()); - EXPECT_EQ(1u, touch_devices[0]->id); - })); + ASSERT_EQ(1ul, keyboards.size()); + EXPECT_EQ(4u, keyboards[0]->id); + + ASSERT_EQ(1ul, touch_devices.size()); + EXPECT_EQ(1u, touch_devices[0]->id); + } ui::DeviceEvent remove_touch_event(ui::DeviceEvent::DeviceType::INPUT, ui::DeviceEvent::ActionType::REMOVE, @@ -241,20 +436,21 @@ TEST_F(InputDataProviderTest, GetConnectedDevices_Remove) { provider_->OnDeviceEvent(remove_kbd_event); task_environment_.RunUntilIdle(); - provider_->GetConnectedDevices(base::BindLambdaForTesting( - [&](std::vector keyboards, - std::vector touch_devices) { - EXPECT_EQ(0ul, keyboards.size()); - EXPECT_EQ(0ul, touch_devices.size()); + { + base::test::TestFuture, + std::vector> + future; + provider_->GetConnectedDevices(future.GetCallback()); - run_loop.Quit(); - })); + const auto& keyboards = future.Get<0>(); + const auto& touch_devices = future.Get<1>(); - run_loop.Run(); + EXPECT_EQ(0ul, keyboards.size()); + EXPECT_EQ(0ul, touch_devices.size()); + } } TEST_F(InputDataProviderTest, KeyboardPhysicalLayoutDetection) { - base::RunLoop run_loop; statistics_provider_.SetMachineStatistic( chromeos::system::kKeyboardMechanicalLayoutKey, "ISO"); @@ -267,50 +463,77 @@ TEST_F(InputDataProviderTest, KeyboardPhysicalLayoutDetection) { ui::DeviceEvent event2(ui::DeviceEvent::DeviceType::INPUT, ui::DeviceEvent::ActionType::ADD, base::FilePath("/dev/input/event5")); + ui::DeviceEvent event3(ui::DeviceEvent::DeviceType::INPUT, + ui::DeviceEvent::ActionType::ADD, + base::FilePath("/dev/input/event7")); provider_->OnDeviceEvent(event0); provider_->OnDeviceEvent(event1); provider_->OnDeviceEvent(event2); + provider_->OnDeviceEvent(event3); task_environment_.RunUntilIdle(); - provider_->GetConnectedDevices(base::BindLambdaForTesting( - [&](std::vector keyboards, - std::vector touch_devices) { - ASSERT_EQ(3ul, keyboards.size()); - - mojom::KeyboardInfoPtr builtin_keyboard = keyboards[0].Clone(); - EXPECT_EQ(0u, builtin_keyboard->id); - EXPECT_EQ(mojom::PhysicalLayout::kChromeOS, - builtin_keyboard->physical_layout); - EXPECT_EQ(mojom::MechanicalLayout::kIso, - builtin_keyboard->mechanical_layout); - EXPECT_EQ(mojom::NumberPadPresence::kNotPresent, - builtin_keyboard->number_pad_present); - - mojom::KeyboardInfoPtr external_keyboard = keyboards[1].Clone(); - EXPECT_EQ(4u, external_keyboard->id); - EXPECT_EQ(mojom::PhysicalLayout::kUnknown, - external_keyboard->physical_layout); - EXPECT_EQ(mojom::MechanicalLayout::kUnknown, - external_keyboard->mechanical_layout); - EXPECT_EQ(mojom::NumberPadPresence::kUnknown, - external_keyboard->number_pad_present); - - mojom::KeyboardInfoPtr dell_internal_keyboard = keyboards[2].Clone(); - EXPECT_EQ(5u, dell_internal_keyboard->id); - EXPECT_EQ(mojom::PhysicalLayout::kChromeOSDellEnterprise, - dell_internal_keyboard->physical_layout); - EXPECT_EQ(mojom::MechanicalLayout::kIso, - dell_internal_keyboard->mechanical_layout); - EXPECT_EQ(mojom::NumberPadPresence::kNotPresent, - dell_internal_keyboard->number_pad_present); - - run_loop.Quit(); - })); - run_loop.Run(); + base::test::TestFuture, + std::vector> + future; + provider_->GetConnectedDevices(future.GetCallback()); + + const auto& keyboards = future.Get<0>(); + + ASSERT_EQ(4ul, keyboards.size()); + + const mojom::KeyboardInfoPtr& builtin_keyboard = keyboards[0]; + EXPECT_EQ(0u, builtin_keyboard->id); + EXPECT_EQ(mojom::PhysicalLayout::kChromeOS, + builtin_keyboard->physical_layout); + EXPECT_EQ(mojom::MechanicalLayout::kIso, builtin_keyboard->mechanical_layout); + EXPECT_EQ(mojom::NumberPadPresence::kNotPresent, + builtin_keyboard->number_pad_present); + EXPECT_EQ( + std::vector(std::begin(kClassicTopRowKeys), std::end(kClassicTopRowKeys)), + builtin_keyboard->top_row_keys); + + const mojom::KeyboardInfoPtr& external_keyboard = keyboards[1]; + EXPECT_EQ(4u, external_keyboard->id); + EXPECT_EQ(mojom::PhysicalLayout::kUnknown, + external_keyboard->physical_layout); + EXPECT_EQ(mojom::MechanicalLayout::kUnknown, + external_keyboard->mechanical_layout); + EXPECT_EQ(mojom::NumberPadPresence::kUnknown, + external_keyboard->number_pad_present); + EXPECT_EQ( + std::vector(std::begin(kClassicTopRowKeys), std::end(kClassicTopRowKeys)), + external_keyboard->top_row_keys); + + const mojom::KeyboardInfoPtr& dell_internal_keyboard = keyboards[2]; + EXPECT_EQ(5u, dell_internal_keyboard->id); + EXPECT_EQ(mojom::PhysicalLayout::kChromeOSDellEnterprise, + dell_internal_keyboard->physical_layout); + EXPECT_EQ(mojom::MechanicalLayout::kIso, + dell_internal_keyboard->mechanical_layout); + EXPECT_EQ(mojom::NumberPadPresence::kNotPresent, + dell_internal_keyboard->number_pad_present); + EXPECT_EQ(std::vector(std::begin(kInternalDellTopRowKeys), + std::end(kInternalDellTopRowKeys)), + dell_internal_keyboard->top_row_keys); + + const mojom::KeyboardInfoPtr& jinlon_internal_keyboard = keyboards[3]; + EXPECT_EQ(7u, jinlon_internal_keyboard->id); + EXPECT_EQ(mojom::PhysicalLayout::kChromeOS, + jinlon_internal_keyboard->physical_layout); + EXPECT_EQ(mojom::MechanicalLayout::kIso, + jinlon_internal_keyboard->mechanical_layout); + EXPECT_EQ(mojom::NumberPadPresence::kNotPresent, + jinlon_internal_keyboard->number_pad_present); + EXPECT_EQ(std::vector(std::begin(kInternalJinlonTopRowKeys), + std::end(kInternalJinlonTopRowKeys)), + jinlon_internal_keyboard->top_row_keys); + + // TODO(b/208729519): We should check a Drallion keyboard, however that + // invokes a check through the global Shell that does not operate in + // this test. } TEST_F(InputDataProviderTest, KeyboardAssistantKeyDetection) { - base::RunLoop run_loop; ui::DeviceEvent link_event(ui::DeviceEvent::DeviceType::INPUT, ui::DeviceEvent::ActionType::ADD, base::FilePath("/dev/input/event0")); @@ -321,43 +544,46 @@ TEST_F(InputDataProviderTest, KeyboardAssistantKeyDetection) { provider_->OnDeviceEvent(eve_event); task_environment_.RunUntilIdle(); - provider_->GetConnectedDevices(base::BindLambdaForTesting( - [&](std::vector keyboards, - std::vector touch_devices) { - ASSERT_EQ(2ul, keyboards.size()); - - mojom::KeyboardInfoPtr link_keyboard = keyboards[0].Clone(); - EXPECT_EQ(0u, link_keyboard->id); - EXPECT_FALSE(link_keyboard->has_assistant_key); - mojom::KeyboardInfoPtr eve_keyboard = keyboards[1].Clone(); - EXPECT_EQ(6u, eve_keyboard->id); - EXPECT_TRUE(eve_keyboard->has_assistant_key); - })); + base::test::TestFuture, + std::vector> + future; + provider_->GetConnectedDevices(future.GetCallback()); + const auto& keyboards = future.Get<0>(); + + ASSERT_EQ(2ul, keyboards.size()); + + const mojom::KeyboardInfoPtr& link_keyboard = keyboards[0]; + EXPECT_EQ(0u, link_keyboard->id); + EXPECT_FALSE(link_keyboard->has_assistant_key); + const mojom::KeyboardInfoPtr& eve_keyboard = keyboards[1]; + EXPECT_EQ(6u, eve_keyboard->id); + EXPECT_TRUE(eve_keyboard->has_assistant_key); } -TEST_F(InputDataProviderTest, KeyboardNumberPadDetection) { +TEST_F(InputDataProviderTest, KeyboardNumberPadDetectionInternal) { + // Detection of internal number pad depends on command-line + // argument, and is not a property of the keyboard device. + base::CommandLine::ForCurrentProcess()->InitFromArgv( {"", "--has-number-pad"}); - base::RunLoop run_loop; ui::DeviceEvent link_event(ui::DeviceEvent::DeviceType::INPUT, ui::DeviceEvent::ActionType::ADD, base::FilePath("/dev/input/event0")); provider_->OnDeviceEvent(link_event); task_environment_.RunUntilIdle(); - provider_->GetConnectedDevices(base::BindLambdaForTesting( - [&](std::vector keyboards, - std::vector touch_devices) { - ASSERT_EQ(1ul, keyboards.size()); + base::test::TestFuture, + std::vector> + future; + provider_->GetConnectedDevices(future.GetCallback()); + const auto& keyboards = future.Get<0>(); - mojom::KeyboardInfoPtr builtin_keyboard = keyboards[0].Clone(); - EXPECT_EQ(0u, builtin_keyboard->id); - EXPECT_EQ(mojom::NumberPadPresence::kPresent, - builtin_keyboard->number_pad_present); + ASSERT_EQ(1ul, keyboards.size()); - run_loop.Quit(); - })); - run_loop.Run(); + const mojom::KeyboardInfoPtr& builtin_keyboard = keyboards[0]; + EXPECT_EQ(0u, builtin_keyboard->id); + EXPECT_EQ(mojom::NumberPadPresence::kPresent, + builtin_keyboard->number_pad_present); } TEST_F(InputDataProviderTest, ObserveConnectedDevices_Keyboards) { @@ -404,14 +630,37 @@ TEST_F(InputDataProviderTest, ObserveConnectedDevices_TouchDevices) { EXPECT_EQ(1u, fake_observer.touch_devices_disconnected[0]); } -TEST_F(InputDataProviderTest, BadDeviceDoesntCrash) { +TEST_F(InputDataProviderTest, ChangeDeviceDoesNotCrash) { + ui::DeviceEvent add_device_event(ui::DeviceEvent::DeviceType::INPUT, + ui::DeviceEvent::ActionType::ADD, + base::FilePath("/dev/input/event1")); + ui::DeviceEvent change_device_event(ui::DeviceEvent::DeviceType::INPUT, + ui::DeviceEvent::ActionType::CHANGE, + base::FilePath("/dev/input/event1")); + provider_->OnDeviceEvent(add_device_event); + task_environment_.RunUntilIdle(); + provider_->OnDeviceEvent(change_device_event); + task_environment_.RunUntilIdle(); +} + +TEST_F(InputDataProviderTest, BadDeviceDoesNotCrash) { + // Try a device that specifically fails to be processed ui::DeviceEvent add_bad_device_event(ui::DeviceEvent::DeviceType::INPUT, ui::DeviceEvent::ActionType::ADD, - base::FilePath("/dev/input/event7")); + base::FilePath("/dev/input/event99")); provider_->OnDeviceEvent(add_bad_device_event); task_environment_.RunUntilIdle(); } +TEST_F(InputDataProviderTest, SillyDeviceDoesNotCrash) { + // Try a device that has data, but has a non-parseable name. + ui::DeviceEvent add_silly_device_event(ui::DeviceEvent::DeviceType::INPUT, + ui::DeviceEvent::ActionType::ADD, + base::FilePath(kSillyDeviceName)); + provider_->OnDeviceEvent(add_silly_device_event); + task_environment_.RunUntilIdle(); +} + TEST_F(InputDataProviderTest, GetKeyboardVisualLayout_AmericanEnglish) { statistics_provider_.SetMachineStatistic(chromeos::system::kKeyboardLayoutKey, "xkb:us::eng,m17n:ar,t13n:ar"); @@ -422,28 +671,25 @@ TEST_F(InputDataProviderTest, GetKeyboardVisualLayout_AmericanEnglish) { provider_->OnDeviceEvent(add_keyboard_event); task_environment_.RunUntilIdle(); - base::RunLoop run_loop; - provider_->GetKeyboardVisualLayout( - 6, base::BindLambdaForTesting( - [&](base::flat_map layout) { - ASSERT_FALSE(layout[KEY_Q].is_null()); - EXPECT_EQ("q", layout[KEY_Q]->main_glyph); - EXPECT_FALSE(layout[KEY_Q]->shift_glyph.has_value()); - - ASSERT_FALSE(layout[KEY_3].is_null()); - EXPECT_EQ("3", layout[KEY_3]->main_glyph); - EXPECT_EQ("#", layout[KEY_3]->shift_glyph); - - // Check all of the essential keys (at least on US QWERTY) have - // glyphs. - for (auto const& entry : layout) { - EXPECT_FALSE(entry.second.is_null()) - << "No glyphs for evdev code " << entry.first; - } - - run_loop.Quit(); - })); - run_loop.Run(); + base::test::TestFuture> + future; + provider_->GetKeyboardVisualLayout(6, future.GetCallback()); + const auto& layout = future.Get<0>(); + + ASSERT_FALSE(layout.at(KEY_Q).is_null()); + EXPECT_EQ("q", layout.at(KEY_Q)->main_glyph); + EXPECT_FALSE(layout.at(KEY_Q)->shift_glyph.has_value()); + + ASSERT_FALSE(layout.at(KEY_3).is_null()); + EXPECT_EQ("3", layout.at(KEY_3)->main_glyph); + EXPECT_EQ("#", layout.at(KEY_3)->shift_glyph); + + // Check all of the essential keys (at least on US QWERTY) have + // glyphs. + for (auto const& entry : layout) { + EXPECT_FALSE(entry.second.is_null()) + << "No glyphs for evdev code " << entry.first; + } } TEST_F(InputDataProviderTest, GetKeyboardVisualLayout_FrenchFrench) { @@ -456,27 +702,85 @@ TEST_F(InputDataProviderTest, GetKeyboardVisualLayout_FrenchFrench) { provider_->OnDeviceEvent(add_keyboard_event); task_environment_.RunUntilIdle(); - base::RunLoop run_loop; - provider_->GetKeyboardVisualLayout( - 6, base::BindLambdaForTesting( - [&](base::flat_map layout) { - ASSERT_FALSE(layout[KEY_Q].is_null()); - EXPECT_EQ("a", layout[KEY_Q]->main_glyph); - EXPECT_FALSE(layout[KEY_Q]->shift_glyph.has_value()); - - ASSERT_FALSE(layout[KEY_3].is_null()); - EXPECT_EQ("\"", layout[KEY_3]->main_glyph); - EXPECT_EQ("3", layout[KEY_3]->shift_glyph); - - // Check all of the essential keys have glyphs. - for (auto const& entry : layout) { - EXPECT_FALSE(entry.second.is_null()) - << "No glyphs for evdev code " << entry.first; - } - - run_loop.Quit(); - })); - run_loop.Run(); + base::test::TestFuture> + future; + provider_->GetKeyboardVisualLayout(6, future.GetCallback()); + const auto& layout = future.Get<0>(); + + ASSERT_FALSE(layout.at(KEY_Q).is_null()); + EXPECT_EQ("a", layout.at(KEY_Q)->main_glyph); + EXPECT_FALSE(layout.at(KEY_Q)->shift_glyph.has_value()); + + ASSERT_FALSE(layout.at(KEY_3).is_null()); + EXPECT_EQ("\"", layout.at(KEY_3)->main_glyph); + EXPECT_EQ("3", layout.at(KEY_3)->shift_glyph); + + // Check all of the essential keys have glyphs. + for (auto const& entry : layout) { + EXPECT_FALSE(entry.second.is_null()) + << "No glyphs for evdev code " << entry.first; + } +} + +TEST_F(InputDataProviderTest, GetKeyboardMechanicalLayout_Unknown1) { + statistics_provider_.ClearMachineStatistic( + chromeos::system::kKeyboardMechanicalLayoutKey); + + ui::DeviceEvent add_keyboard_event(ui::DeviceEvent::DeviceType::INPUT, + ui::DeviceEvent::ActionType::ADD, + base::FilePath("/dev/input/event6")); + provider_->OnDeviceEvent(add_keyboard_event); + task_environment_.RunUntilIdle(); + + { + base::test::TestFuture, + std::vector> + future; + provider_->GetConnectedDevices(future.GetCallback()); + + const auto& keyboards = future.Get<0>(); + + ASSERT_EQ(1ul, keyboards.size()); + + const mojom::KeyboardInfoPtr& builtin_keyboard = keyboards[0]; + EXPECT_EQ(6u, builtin_keyboard->id); + EXPECT_EQ(mojom::PhysicalLayout::kChromeOS, + builtin_keyboard->physical_layout); + EXPECT_EQ(mojom::MechanicalLayout::kUnknown, + builtin_keyboard->mechanical_layout); + EXPECT_EQ(mojom::NumberPadPresence::kNotPresent, + builtin_keyboard->number_pad_present); + } +} + +TEST_F(InputDataProviderTest, GetKeyboardMechanicalLayout_Unknown2) { + statistics_provider_.SetMachineStatistic( + chromeos::system::kKeyboardMechanicalLayoutKey, kInvalidMechnicalLayout); + ui::DeviceEvent add_keyboard_event(ui::DeviceEvent::DeviceType::INPUT, + ui::DeviceEvent::ActionType::ADD, + base::FilePath("/dev/input/event6")); + provider_->OnDeviceEvent(add_keyboard_event); + task_environment_.RunUntilIdle(); + + { + base::test::TestFuture, + std::vector> + future; + provider_->GetConnectedDevices(future.GetCallback()); + + const auto& keyboards = future.Get<0>(); + + ASSERT_EQ(1ul, keyboards.size()); + + const mojom::KeyboardInfoPtr& builtin_keyboard = keyboards[0]; + EXPECT_EQ(6u, builtin_keyboard->id); + EXPECT_EQ(mojom::PhysicalLayout::kChromeOS, + builtin_keyboard->physical_layout); + EXPECT_EQ(mojom::MechanicalLayout::kUnknown, + builtin_keyboard->mechanical_layout); + EXPECT_EQ(mojom::NumberPadPresence::kNotPresent, + builtin_keyboard->number_pad_present); + } } TEST_F(InputDataProviderTest, ResetReceiverOnDisconnect) { diff --git a/ash/webui/diagnostics_ui/mojom/input_data_provider.mojom b/ash/webui/diagnostics_ui/mojom/input_data_provider.mojom index 22cc37ad3abaad..e3a07bda14c148 100644 --- a/ash/webui/diagnostics_ui/mojom/input_data_provider.mojom +++ b/ash/webui/diagnostics_ui/mojom/input_data_provider.mojom @@ -5,13 +5,15 @@ module ash.diagnostics.mojom; enum ConnectionType { - kInternal, // Includes internal USB devices. + // Includes devices connected over USB that are on fully internal busses, as + // well as the keyboards/touchpads for detachables. + kInternal, kUsb, kBluetooth, + // An unknown device is most likely to be internal. kUnknown, }; -// The physical style of a keyboard. enum PhysicalLayout { kUnknown, // A typical Chrome OS keyboard with action keys on the top row, reduced @@ -31,11 +33,40 @@ enum MechanicalLayout { }; enum NumberPadPresence { + // Unknown indicates there is no reliable evidence whether a numberpad is + // present. This is common for external keyboards. kUnknown, kPresent, kNotPresent, }; +// Note that this enumeration will need to be extended if new keys are added. +enum TopRowKey { + // Either no key at all, or no special action key at this position. + kNone, + // Marker for keys which cannot be decoded, but have some action. + kUnknown, + kBack, + kForward, + kRefresh, + kFullscreen, + kOverview, + kScreenshot, + kScreenBrightnessDown, + kScreenBrightnessUp, + kPrivacyScreenToggle, + kVolumeMute, + kVolumeDown, + kVolumeUp, + kKeyboardBacklightDown, + kKeyboardBacklightUp, + kNextTrack, + kPreviousTrack, + kPlayPause, + kScreenMirror, + kDelete, +}; + // Describes a connected keyboard. struct KeyboardInfo { // The number of the keyboard's /dev/input/event* node. @@ -44,8 +75,11 @@ struct KeyboardInfo { string name; PhysicalLayout physical_layout; MechanicalLayout mechanical_layout; - bool has_assistant_key; NumberPadPresence number_pad_present; + // Excludes left-most Escape key, and right-most key (usually Power/Lock). + array top_row_keys; + // Only applicable to CrOS keyboards. + bool has_assistant_key; }; // Describes the glyphs that appear on a single key. diff --git a/ash/webui/diagnostics_ui/resources/diagnostics_types.js b/ash/webui/diagnostics_ui/resources/diagnostics_types.js index e79f6d91a0f714..4b0e3792c03a01 100644 --- a/ash/webui/diagnostics_ui/resources/diagnostics_types.js +++ b/ash/webui/diagnostics_ui/resources/diagnostics_types.js @@ -464,6 +464,12 @@ export const MechanicalLayout = ash.diagnostics.mojom.MechanicalLayout; */ export const NumberPadPresence = ash.diagnostics.mojom.NumberPadPresence; +/** + * Type alias for TopRowKey. + * @typedef {ash.diagnostics.mojom.TopRowKey} + */ +export const TopRowKey = ash.diagnostics.mojom.TopRowKey; + /** * Type alias for KeyboardInfo. * @typedef {ash.diagnostics.mojom.KeyboardInfo} diff --git a/ash/webui/diagnostics_ui/resources/fake_data.js b/ash/webui/diagnostics_ui/resources/fake_data.js index e98de2dc4b457e..1facc4ace1d674 100644 --- a/ash/webui/diagnostics_ui/resources/fake_data.js +++ b/ash/webui/diagnostics_ui/resources/fake_data.js @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import {AuthenticationType, BatteryChargeStatus, BatteryHealth, BatteryInfo, BatteryState, ConnectionType, CpuUsage, ExternalPowerSource, KeyboardInfo, LockType, MechanicalLayout, MemoryUsage, Network, NetworkGuidInfo, NetworkState, NetworkType, NumberPadPresence, PhysicalLayout, PowerRoutineResult, RoamingState, RoutineType, SecurityType, StandardRoutineResult, SystemInfo, TouchDeviceInfo, TouchDeviceType, WiFiStateProperties} from './diagnostics_types.js'; +import {AuthenticationType, BatteryChargeStatus, BatteryHealth, BatteryInfo, BatteryState, ConnectionType, CpuUsage, ExternalPowerSource, KeyboardInfo, LockType, MechanicalLayout, MemoryUsage, Network, NetworkGuidInfo, NetworkState, NetworkType, NumberPadPresence, PhysicalLayout, PowerRoutineResult, RoamingState, RoutineType, SecurityType, StandardRoutineResult, SystemInfo, TopRowKey, TouchDeviceInfo, TouchDeviceType, WiFiStateProperties} from './diagnostics_types.js'; import {stringToMojoString16} from './mojo_utils.js'; /** @type {!Array} */ @@ -629,6 +629,12 @@ export const fakeKeyboards = [ physicalLayout: PhysicalLayout.kChromeOS, mechanicalLayout: MechanicalLayout.kAnsi, hasAssistantKey: true, + topRowKeys: [ + TopRowKey.kBack, TopRowKey.kForward, TopRowKey.kRefresh, + TopRowKey.kFullscreen, TopRowKey.kOverview, + TopRowKey.kScreenBrightnessDown, TopRowKey.kScreenBrightnessUp, + TopRowKey.kVolumeMute, TopRowKey.kVolumeDown, TopRowKey.kVolumeUp + ], numberPadPresent: NumberPadPresence.kPresent, }, ]; diff --git a/ash/webui/shimless_rma/resources/reimaging_provisioning_page.html b/ash/webui/shimless_rma/resources/reimaging_provisioning_page.html index 450611129b9af8..7ffa8e1010d641 100644 --- a/ash/webui/shimless_rma/resources/reimaging_provisioning_page.html +++ b/ash/webui/shimless_rma/resources/reimaging_provisioning_page.html @@ -3,15 +3,27 @@ height: 200px; width: 300px; } + + paper-spinner-lite { + height: 300px; + width: 300px; + }

[[i18n('provisioningPageTitleText')]]

[[statusString_]]
+
+ + [[i18n('provisioningPageFailedRetryButtonLabel')]] + +
- - + +
diff --git a/ash/webui/shimless_rma/resources/reimaging_provisioning_page.js b/ash/webui/shimless_rma/resources/reimaging_provisioning_page.js index ef94348330c5b0..abc9cf226ae634 100644 --- a/ash/webui/shimless_rma/resources/reimaging_provisioning_page.js +++ b/ash/webui/shimless_rma/resources/reimaging_provisioning_page.js @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'chrome://resources/cr_elements/cr_button/cr_button.m.js'; import 'chrome://resources/cr_elements/icons.m.js'; import 'chrome://resources/polymer/v3_0/iron-icon/iron-icon.js'; import './shimless_rma_shared_css.js'; @@ -67,6 +68,18 @@ export class ReimagingProvisioningPage extends ReimagingProvisioningPageBase { type: String, computed: 'getStatusString_(status_, progress_)', }, + + /** @protected {boolean} */ + shouldShowSpinner_: { + type: Boolean, + value: false, + }, + + /** @protected {boolean} */ + shouldShowRetryButton_: { + type: Boolean, + value: false, + }, }; } @@ -119,6 +132,10 @@ export class ReimagingProvisioningPage extends ReimagingProvisioningPageBase { 'disable-next-button', {bubbles: true, composed: true, detail: disabled}, )); + this.shouldShowSpinner_ = this.status_ === ProvisioningStatus.kInProgress; + this.shouldShowRetryButton_ = + this.status_ === ProvisioningStatus.kFailedBlocking || + this.status_ === ProvisioningStatus.kFailedNonBlocking; } /** @return {!Promise} */ @@ -130,6 +147,26 @@ export class ReimagingProvisioningPage extends ReimagingProvisioningPageBase { return Promise.reject(new Error('Provisioning is not complete.')); } } + + /** @private */ + onRetryProvsioningButtonClicked_() { + if (this.status_ !== ProvisioningStatus.kFailedBlocking && + this.status_ !== ProvisioningStatus.kFailedNonBlocking) { + console.error('Provisioning has not failed.'); + return; + } + + this.dispatchEvent(new CustomEvent( + 'transition-state', + { + bubbles: true, + composed: true, + detail: (() => { + return this.shimlessRmaService_.retryProvisioning(); + }) + }, + )); + } } customElements.define(ReimagingProvisioningPage.is, ReimagingProvisioningPage); diff --git a/ash/webui/shimless_rma/shimless_rma.cc b/ash/webui/shimless_rma/shimless_rma.cc index 2ac3c051d3a1cc..7a19f410ab90b9 100644 --- a/ash/webui/shimless_rma/shimless_rma.cc +++ b/ash/webui/shimless_rma/shimless_rma.cc @@ -186,6 +186,8 @@ void AddShimlessRmaStrings(content::WebUIDataSource* html_source) { IDS_SHIMLESS_RMA_PROVISIONING_FAILED_BLOCKING}, {"provisioningPageFailedNonBlockingText", IDS_SHIMLESS_RMA_PROVISIONING_FAILED_NON_BLOCKING}, + {"provisioningPageFailedRetryButtonLabel", + IDS_SHIMLESS_RMA_PROVISIONING_FAILED_RETRY_BUTTON_LABEL}, // Repair complete page {"repairCompletedTitleText", IDS_SHIMLESS_RMA_REPAIR_COMPLETED}, {"repairCompletedDescriptionText", diff --git a/base/logging.cc b/base/logging.cc index f4888b0d10d6e3..174785c9c647a8 100644 --- a/base/logging.cc +++ b/base/logging.cc @@ -804,9 +804,12 @@ LogMessage::~LogMessage() { #endif #elif defined(OS_FUCHSIA) // LogMessage() will silently drop the message if the logger is not valid. - GetScopedFxLogger().LogMessage( - file_, line_, base::StringPiece(str_newline).substr(message_start_), - LogSeverityToFuchsiaLogSeverity(severity_)); + // Skip the final character of |str_newline|, since LogMessage() will add + // a newline. + const auto message = base::StringPiece(str_newline).substr(message_start_); + GetScopedFxLogger().LogMessage(file_, line_, + message.substr(0, message.size() - 1), + LogSeverityToFuchsiaLogSeverity(severity_)); #endif // OS_FUCHSIA } 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 b65a8a56e15ef1..a52e845a844976 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 @@ -730,8 +730,12 @@ public void onShareMenuItemSelected(final boolean shareDirectly, final boolean i if (shareDirectly) { RecordUserAction.record("MobileMenuDirectShare"); + new UkmRecorder.Bridge().recordEventWithBooleanMetric( + tab.getWebContents(), "MobileMenu.DirectShare", "HasOccurred"); } else { RecordUserAction.record("MobileMenuShare"); + new UkmRecorder.Bridge().recordEventWithBooleanMetric( + tab.getWebContents(), "MobileMenu.Share", "HasOccurred"); } shareDelegate.share(tab, shareDirectly, ShareOrigin.OVERFLOW_MENU); } diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd index 748a5b1a6a125b..09f541fe3e41e7 100644 --- a/chrome/app/generated_resources.grd +++ b/chrome/app/generated_resources.grd @@ -6499,6 +6499,12 @@ Keep your key file in a safe place. You will need it to create new versions of y Search tabs + + Scroll towards the first tab + + + Scroll towards the last tab + diff --git a/chrome/app/generated_resources_grd/IDS_ACCNAME_TAB_SCROLL_LEADING.png.sha1 b/chrome/app/generated_resources_grd/IDS_ACCNAME_TAB_SCROLL_LEADING.png.sha1 new file mode 100644 index 00000000000000..0fd4b26dbf145e --- /dev/null +++ b/chrome/app/generated_resources_grd/IDS_ACCNAME_TAB_SCROLL_LEADING.png.sha1 @@ -0,0 +1 @@ +ba7dce18cc93123490aaa6ed96d5ac16d56e0679 \ No newline at end of file diff --git a/chrome/app/generated_resources_grd/IDS_ACCNAME_TAB_SCROLL_TRAILING.png.sha1 b/chrome/app/generated_resources_grd/IDS_ACCNAME_TAB_SCROLL_TRAILING.png.sha1 new file mode 100644 index 00000000000000..0fd4b26dbf145e --- /dev/null +++ b/chrome/app/generated_resources_grd/IDS_ACCNAME_TAB_SCROLL_TRAILING.png.sha1 @@ -0,0 +1 @@ +ba7dce18cc93123490aaa6ed96d5ac16d56e0679 \ No newline at end of file diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn index 08c30085947ae2..d12c53790abda6 100644 --- a/chrome/browser/BUILD.gn +++ b/chrome/browser/BUILD.gn @@ -1989,7 +1989,6 @@ static_library("browser") { "//chrome/browser/touch_to_fill", "//chrome/browser/ui", "//chrome/browser/ui/color:mixers", - "//chrome/browser/ui/webui/app_management:mojo_bindings", "//chrome/browser/ui/webui/app_service_internals:mojo_bindings", "//chrome/browser/ui/webui/bluetooth_internals", "//chrome/browser/ui/webui/bluetooth_internals:mojo_bindings", @@ -2415,6 +2414,7 @@ static_library("browser") { "//ui/surface", "//ui/web_dialogs", "//ui/webui", + "//ui/webui/resources/cr_components/app_management:mojo_bindings", "//ui/webui/resources/cr_components/color_change_listener:mojom", "//ui/webui/resources/cr_components/customize_themes:mojom", "//ui/webui/resources/cr_components/most_visited:mojom", @@ -7390,7 +7390,6 @@ grit("resources") { "//chrome/browser/resources/chromeos/smb_shares:web_components", "//chrome/browser/resources/chromeos/vm:web_components", "//chrome/browser/supervised_user:supervised_user_unscaled_resources", - "//chrome/browser/ui/webui/app_management:mojo_bindings_js", "//chrome/browser/ui/webui/chromeos/add_supervision:mojo_bindings_js", "//chrome/browser/ui/webui/chromeos/crostini_installer:mojo_bindings_js", "//chrome/browser/ui/webui/chromeos/crostini_upgrader:mojo_bindings_js", @@ -7398,6 +7397,7 @@ grit("resources") { "//chrome/browser/ui/webui/chromeos/vm:mojo_bindings_webui_js", "//chrome/browser/ui/webui/settings/chromeos:mojom_js", "//chrome/browser/ui/webui/settings/chromeos/os_apps_page/mojom:mojom_js", + "//ui/webui/resources/cr_components/app_management:mojo_bindings_js", ] } diff --git a/chrome/browser/ash/crosapi/url_handler_ash.cc b/chrome/browser/ash/crosapi/url_handler_ash.cc index 7d48990aea8e5c..bb91c595acb826 100644 --- a/chrome/browser/ash/crosapi/url_handler_ash.cc +++ b/chrome/browser/ash/crosapi/url_handler_ash.cc @@ -64,6 +64,10 @@ void UrlHandlerAsh::BindReceiver( } void UrlHandlerAsh::OpenUrl(const GURL& url) { + OpenUrlInternal(url); +} + +bool UrlHandlerAsh::OpenUrlInternal(const GURL& url) { GURL target_url = crosapi::gurl_os_handler_utils::SanitizeAshURL(url); // Settings will be handled. if (target_url == GURL(chrome::kChromeUIOSSettingsURL)) { @@ -72,7 +76,7 @@ void UrlHandlerAsh::OpenUrl(const GURL& url) { settings_window_manager->ShowChromePageForProfile( ProfileManager::GetPrimaryUserProfile(), target_url, display::kInvalidDisplayId); - return; + return true; } web_app::SystemAppType app_id; @@ -104,10 +108,11 @@ void UrlHandlerAsh::OpenUrl(const GURL& url) { } } else { LOG(ERROR) << "Invalid URL passed to UrlHandlerAsh::OpenUrl:" << url; - return; + return false; } ShowOsAppForProfile(ProfileManager::GetPrimaryUserProfile(), target_url, app_id); + return true; } } // namespace crosapi diff --git a/chrome/browser/ash/crosapi/url_handler_ash.h b/chrome/browser/ash/crosapi/url_handler_ash.h index 5e46526cf2997e..401f9550bc9e7e 100644 --- a/chrome/browser/ash/crosapi/url_handler_ash.h +++ b/chrome/browser/ash/crosapi/url_handler_ash.h @@ -24,6 +24,10 @@ class UrlHandlerAsh : public mojom::UrlHandler { // crosapi::mojom::UrlHandler: void OpenUrl(const GURL& url) override; + // Returns |false| when the URL was invalid and will not get processed and + // |true| when the URL will get processed (synchronous or asynchronously). + bool OpenUrlInternal(const GURL& url); + private: mojo::ReceiverSet receivers_; }; diff --git a/chrome/browser/ash/policy/reporting/metrics_reporting/cros_healthd_metric_sampler.cc b/chrome/browser/ash/policy/reporting/metrics_reporting/cros_healthd_metric_sampler.cc index ed18d27c5c3f54..59033e63938b8f 100644 --- a/chrome/browser/ash/policy/reporting/metrics_reporting/cros_healthd_metric_sampler.cc +++ b/chrome/browser/ash/policy/reporting/metrics_reporting/cros_healthd_metric_sampler.cc @@ -64,9 +64,9 @@ MemoryEncryptionAlgorithm TranslateMemoryEncryptionAlgorithm( void HandleBusResult(MetricCallback callback, CrosHealthdMetricSampler::MetricType metric_type, + MetricData metric_data, cros_healthd::TelemetryInfoPtr result) { bool anything_reported = false; - MetricData metric_data; const auto& bus_result = result->bus_result; if (!bus_result.is_null()) { @@ -99,15 +99,15 @@ void HandleBusResult(MetricCallback callback, } if (anything_reported) { - std::move(callback).Run(metric_data); + std::move(callback).Run(std::move(metric_data)); } } void HandleCpuResult(MetricCallback callback, CrosHealthdMetricSampler::MetricType metric_type, + MetricData metric_data, cros_healthd::TelemetryInfoPtr result) { bool anything_reported = false; - MetricData metric_data; const auto& cpu_result = result->cpu_result; if (!cpu_result.is_null()) { @@ -148,15 +148,15 @@ void HandleCpuResult(MetricCallback callback, } if (anything_reported) { - std::move(callback).Run(metric_data); + std::move(callback).Run(std::move(metric_data)); } } void HandleAudioResult(MetricCallback callback, CrosHealthdMetricSampler::MetricType metric_type, + MetricData metric_data, chromeos::cros_healthd::mojom::TelemetryInfoPtr result) { bool anything_reported = false; - MetricData metric_data; auto* const audio_info_out = metric_data.mutable_telemetry_data()->mutable_audio_telemetry(); const auto& audio_result = result->audio_result; @@ -192,15 +192,15 @@ void HandleAudioResult(MetricCallback callback, } if (anything_reported) { - std::move(callback).Run(metric_data); + std::move(callback).Run(std::move(metric_data)); } } void HandleMemoryResult(MetricCallback callback, CrosHealthdMetricSampler::MetricType metric_type, + MetricData metric_data, cros_healthd::TelemetryInfoPtr result) { bool anything_reported = false; - MetricData metric_data; const auto& memory_result = result->memory_result; if (!memory_result.is_null()) { @@ -251,13 +251,14 @@ void HandleMemoryResult(MetricCallback callback, } if (anything_reported) { - std::move(callback).Run(metric_data); + std::move(callback).Run(std::move(metric_data)); } } void OnHealthdInfoReceived(MetricCallback callback, cros_healthd::ProbeCategoryEnum probe_category, CrosHealthdMetricSampler::MetricType metric_type, + MetricData metric_data, cros_healthd::TelemetryInfoPtr result) { if (!result) { DVLOG(1) << "cros_healthd: null telemetry result"; @@ -266,19 +267,23 @@ void OnHealthdInfoReceived(MetricCallback callback, switch (probe_category) { case cros_healthd::ProbeCategoryEnum::kAudio: { - HandleAudioResult(std::move(callback), metric_type, std::move(result)); + HandleAudioResult(std::move(callback), metric_type, + std::move(metric_data), std::move(result)); break; } case cros_healthd::ProbeCategoryEnum::kBus: { - HandleBusResult(std::move(callback), metric_type, std::move(result)); + HandleBusResult(std::move(callback), metric_type, std::move(metric_data), + std::move(result)); break; } case cros_healthd::ProbeCategoryEnum::kCpu: { - HandleCpuResult(std::move(callback), metric_type, std::move(result)); + HandleCpuResult(std::move(callback), metric_type, std::move(metric_data), + std::move(result)); break; } case cros_healthd::ProbeCategoryEnum::kMemory: { - HandleMemoryResult(std::move(callback), metric_type, std::move(result)); + HandleMemoryResult(std::move(callback), metric_type, + std::move(metric_data), std::move(result)); break; } default: { @@ -299,9 +304,14 @@ CrosHealthdMetricSampler::~CrosHealthdMetricSampler() = default; void CrosHealthdMetricSampler::Collect(MetricCallback callback) { auto healthd_callback = base::BindOnce(OnHealthdInfoReceived, std::move(callback), - probe_category_, metric_type_); + probe_category_, metric_type_, std::move(metric_data_)); + metric_data_.Clear(); chromeos::cros_healthd::ServiceConnection::GetInstance()->ProbeTelemetryInfo( std::vector{probe_category_}, std::move(healthd_callback)); } + +void CrosHealthdMetricSampler::SetMetricData(MetricData metric_data) { + metric_data_ = std::move(metric_data); +} } // namespace reporting diff --git a/chrome/browser/ash/policy/reporting/metrics_reporting/cros_healthd_metric_sampler.h b/chrome/browser/ash/policy/reporting/metrics_reporting/cros_healthd_metric_sampler.h index 1a4291dad1a4da..ecf533cf5affcb 100644 --- a/chrome/browser/ash/policy/reporting/metrics_reporting/cros_healthd_metric_sampler.h +++ b/chrome/browser/ash/policy/reporting/metrics_reporting/cros_healthd_metric_sampler.h @@ -30,7 +30,17 @@ class CrosHealthdMetricSampler : public Sampler { // Collect is called to invoke the healthd probing process. void Collect(MetricCallback callback) override; + // Set the metric data that the sampler will collect on. This can be used if + // part of the info or telemetry collected for the probe category is set + // without the the healthd metric sampler. After one call to Collect(), this + // metric data is cleared. + void SetMetricData(MetricData metric_data); + private: + // The metric data to populate when calling Collect(). This can be set using + // SetMetricData and is cleared after every call to Collect() + MetricData metric_data_; + // probe_category is the category to probe from the health daemon. const chromeos::cros_healthd::mojom::ProbeCategoryEnum probe_category_; diff --git a/chrome/browser/ash/policy/reporting/metrics_reporting/cros_healthd_metric_sampler_unittest.cc b/chrome/browser/ash/policy/reporting/metrics_reporting/cros_healthd_metric_sampler_unittest.cc index b418e2b8392d5b..4cd6cd500e872d 100644 --- a/chrome/browser/ash/policy/reporting/metrics_reporting/cros_healthd_metric_sampler_unittest.cc +++ b/chrome/browser/ash/policy/reporting/metrics_reporting/cros_healthd_metric_sampler_unittest.cc @@ -119,11 +119,12 @@ cros_healthd::TelemetryInfoPtr CreateMemoryResult( MetricData CollectData(cros_healthd::TelemetryInfoPtr telemetry_info, cros_healthd::ProbeCategoryEnum probe_category, - CrosHealthdMetricSampler::MetricType metric_type) { - MetricData data; + CrosHealthdMetricSampler::MetricType metric_type, + MetricData metric_data) { chromeos::cros_healthd::FakeCrosHealthdClient::Get() ->SetProbeTelemetryInfoResponseForTesting(telemetry_info); CrosHealthdMetricSampler sampler(probe_category, metric_type); + sampler.SetMetricData(metric_data); test::TestEvent metric_collect_event; sampler.Collect(metric_collect_event.cb()); @@ -177,7 +178,7 @@ TEST_P(CrosHealthdMetricSamplerMemoryEncryptionTest, test_case.healthd_encryption_state, test_case.max_keys, test_case.key_length, test_case.healthd_encryption_algorithm)), cros_healthd::ProbeCategoryEnum::kMemory, - CrosHealthdMetricSampler::MetricType::kInfo); + CrosHealthdMetricSampler::MetricType::kInfo, MetricData{}); ASSERT_TRUE(result.has_info_data()); ASSERT_TRUE(result.info_data().has_memory_info()); @@ -196,7 +197,7 @@ TEST_P(CrosHealthdMetricSamplerTbtTest, TestTbtSecurityLevels) { MetricData result = CollectData(CreateBusResult(test_case.healthd_security_level), cros_healthd::ProbeCategoryEnum::kBus, - CrosHealthdMetricSampler::MetricType::kInfo); + CrosHealthdMetricSampler::MetricType::kInfo, MetricData{}); ASSERT_TRUE(result.has_info_data()); ASSERT_TRUE(result.info_data().has_bus_device_info()); ASSERT_TRUE(result.info_data().bus_device_info().has_thunderbolt_info()); @@ -205,10 +206,34 @@ TEST_P(CrosHealthdMetricSamplerTbtTest, TestTbtSecurityLevels) { test_case.reporting_security_level); } +TEST_F(CrosHealthdMetricSamplerTest, SetMetricData) { + MetricData metric_data; + auto* const memory_encryption_info_out = metric_data.mutable_info_data() + ->mutable_memory_info() + ->mutable_tme_info(); + memory_encryption_info_out->set_key_length(1); + // Pass a null memory result so that only encryption state is set. + MetricData result = CollectData( + CreateMemoryResult(nullptr), cros_healthd::ProbeCategoryEnum::kMemory, + CrosHealthdMetricSampler::MetricType::kInfo, metric_data); + + ASSERT_TRUE(result.has_info_data()); + ASSERT_TRUE(result.info_data().has_memory_info()); + ASSERT_TRUE(result.info_data().memory_info().has_tme_info()); + + // Since only encryption state should be set by the sampler, we can verify + // that the keylength field set above propagated to the metric data. + const auto& tme_info = result.info_data().memory_info().tme_info(); + EXPECT_EQ(tme_info.key_length(), 1); + EXPECT_EQ(tme_info.encryption_state(), + ::reporting::MEMORY_ENCRYPTION_STATE_DISABLED); +} + TEST_F(CrosHealthdMetricSamplerTest, TestKeylockerConfigured) { - MetricData result = CollectData(CreateCpuResult(CreateKeylockerInfo(true)), - cros_healthd::ProbeCategoryEnum::kCpu, - CrosHealthdMetricSampler::MetricType::kInfo); + MetricData result = + CollectData(CreateCpuResult(CreateKeylockerInfo(true)), + cros_healthd::ProbeCategoryEnum::kCpu, + CrosHealthdMetricSampler::MetricType::kInfo, MetricData{}); ASSERT_TRUE(result.has_info_data()); ASSERT_TRUE(result.info_data().has_cpu_info()); @@ -218,9 +243,10 @@ TEST_F(CrosHealthdMetricSamplerTest, TestKeylockerConfigured) { } TEST_F(CrosHealthdMetricSamplerTest, TestKeylockerUnconfigured) { - MetricData result = CollectData(CreateCpuResult(CreateKeylockerInfo(false)), - cros_healthd::ProbeCategoryEnum::kCpu, - CrosHealthdMetricSampler::MetricType::kInfo); + MetricData result = + CollectData(CreateCpuResult(CreateKeylockerInfo(false)), + cros_healthd::ProbeCategoryEnum::kCpu, + CrosHealthdMetricSampler::MetricType::kInfo, MetricData{}); ASSERT_TRUE(result.has_info_data()); ASSERT_TRUE(result.info_data().has_cpu_info()); @@ -230,9 +256,9 @@ TEST_F(CrosHealthdMetricSamplerTest, TestKeylockerUnconfigured) { } TEST_F(CrosHealthdMetricSamplerTest, TestKeylockerUnsupported) { - MetricData result = CollectData(CreateCpuResult(nullptr), - cros_healthd::ProbeCategoryEnum::kCpu, - CrosHealthdMetricSampler::MetricType::kInfo); + MetricData result = CollectData( + CreateCpuResult(nullptr), cros_healthd::ProbeCategoryEnum::kCpu, + CrosHealthdMetricSampler::MetricType::kInfo, MetricData{}); ASSERT_TRUE(result.has_info_data()); ASSERT_TRUE(result.info_data().has_cpu_info()); @@ -280,7 +306,7 @@ TEST_F(CrosHealthdMetricSamplerTest, TestAudioNormalTest) { /*input_gain=*/50, /*input_device_name=*/"airpods", /*underruns=*/2, /*severe_underruns=*/2)), cros_healthd::ProbeCategoryEnum::kAudio, - CrosHealthdMetricSampler::MetricType::kTelemetry); + CrosHealthdMetricSampler::MetricType::kTelemetry, MetricData{}); ASSERT_TRUE(result.has_telemetry_data()); ASSERT_TRUE(result.telemetry_data().has_audio_telemetry()); @@ -298,7 +324,7 @@ TEST_F(CrosHealthdMetricSamplerTest, TestAudioEmptyTest) { /*input_gain=*/0, /*input_device_name=*/"", /*underruns=*/0, /*severe_underruns=*/0)), cros_healthd::ProbeCategoryEnum::kAudio, - CrosHealthdMetricSampler::MetricType::kTelemetry); + CrosHealthdMetricSampler::MetricType::kTelemetry, MetricData{}); ASSERT_TRUE(result.has_telemetry_data()); ASSERT_TRUE(result.telemetry_data().has_audio_telemetry()); diff --git a/chrome/browser/ash/web_applications/os_url_handler_system_web_app_info.cc b/chrome/browser/ash/web_applications/os_url_handler_system_web_app_info.cc index 46f60bb498580e..54ef5926fd3330 100644 --- a/chrome/browser/ash/web_applications/os_url_handler_system_web_app_info.cc +++ b/chrome/browser/ash/web_applications/os_url_handler_system_web_app_info.cc @@ -20,6 +20,8 @@ namespace { +bool g_enable_delegate_for_testing = false; + SkColor GetBgColor(bool use_dark_mode) { return cros_styles::ResolveColor( cros_styles::ColorName::kBgColor, use_dark_mode, @@ -70,7 +72,8 @@ bool OsUrlHandlerSystemWebAppDelegate::ShouldCaptureNavigations() const { } bool OsUrlHandlerSystemWebAppDelegate::IsAppEnabled() const { - return crosapi::browser_util::IsLacrosEnabled(); + return g_enable_delegate_for_testing || + crosapi::browser_util::IsLacrosEnabled(); } bool OsUrlHandlerSystemWebAppDelegate::ShouldShowInLauncher() const { @@ -108,3 +111,7 @@ bool OsUrlHandlerSystemWebAppDelegate::IsUrlInSystemAppScope( crosapi::gurl_os_handler_utils::GetSystemUrlFromChromeUrl(target_url); return ChromeWebUIControllerFactory::GetInstance()->CanHandleUrl(target_url); } + +void OsUrlHandlerSystemWebAppDelegate::EnableDelegateForTesting(bool enable) { + g_enable_delegate_for_testing = enable; +} diff --git a/chrome/browser/ash/web_applications/os_url_handler_system_web_app_info.h b/chrome/browser/ash/web_applications/os_url_handler_system_web_app_info.h index fd76aa972bfa99..a313bbc0de9849 100644 --- a/chrome/browser/ash/web_applications/os_url_handler_system_web_app_info.h +++ b/chrome/browser/ash/web_applications/os_url_handler_system_web_app_info.h @@ -33,6 +33,9 @@ class OsUrlHandlerSystemWebAppDelegate : public web_app::SystemWebAppDelegate { bool ShouldShowInSearch() const override; bool ShouldReuseExistingWindow() const override; bool IsUrlInSystemAppScope(const GURL& url) const override; + + // Can be called by a test to enforce the app to be enabled. + static void EnableDelegateForTesting(bool enable); }; #endif // CHROME_BROWSER_ASH_WEB_APPLICATIONS_OS_URL_HANDLER_SYSTEM_WEB_APP_INFO_H_ diff --git a/chrome/browser/chrome_browser_interface_binders.cc b/chrome/browser/chrome_browser_interface_binders.cc index b7105dc316985f..6a21319993963e 100644 --- a/chrome/browser/chrome_browser_interface_binders.cc +++ b/chrome/browser/chrome_browser_interface_binders.cc @@ -209,7 +209,6 @@ #include "chrome/browser/apps/digital_goods/digital_goods_factory_impl.h" #include "chrome/browser/nearby_sharing/common/nearby_share_features.h" #include "chrome/browser/speech/cros_speech_recognition_service_factory.h" -#include "chrome/browser/ui/webui/app_management/app_management.mojom.h" #include "chrome/browser/ui/webui/chromeos/add_supervision/add_supervision.mojom.h" #include "chrome/browser/ui/webui/chromeos/add_supervision/add_supervision_ui.h" #include "chrome/browser/ui/webui/chromeos/audio/audio.mojom.h" @@ -252,6 +251,7 @@ #include "chromeos/services/network_health/public/mojom/network_health.mojom.h" // nogncheck #include "media/capture/video/chromeos/mojom/camera_app.mojom.h" #include "third_party/blink/public/mojom/digital_goods/digital_goods.mojom.h" +#include "ui/webui/resources/cr_components/app_management/app_management.mojom.h" #endif // BUILDFLAG(IS_CHROMEOS_ASH) #if defined(OS_WIN) || BUILDFLAG(IS_CHROMEOS_ASH) || defined(OS_MAC) || \ diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc index 5ebad974ffe14d..61250951b0e47b 100644 --- a/chrome/browser/chrome_content_browser_client.cc +++ b/chrome/browser/chrome_content_browser_client.cc @@ -6163,10 +6163,8 @@ ukm::UkmService* ChromeContentBrowserClient::GetUkmService() { void ChromeContentBrowserClient::OnKeepaliveRequestStarted( content::BrowserContext* context) { #if !defined(OS_ANDROID) - // TODO(crbug.com/1161996): Remove this entry once the investigation is - // done. - VLOG(1) << "OnKeepaliveRequestStarted: " << num_keepalive_requests_ << " ==> " - << num_keepalive_requests_ + 1; + DVLOG(1) << "OnKeepaliveRequestStarted: " << num_keepalive_requests_ + << " ==> " << num_keepalive_requests_ + 1; ++num_keepalive_requests_; DCHECK_GT(num_keepalive_requests_, 0u); @@ -6180,10 +6178,8 @@ void ChromeContentBrowserClient::OnKeepaliveRequestStarted( const auto timeout = GetKeepaliveTimerTimeout(context); keepalive_deadline_ = std::max(keepalive_deadline_, now + timeout); if (keepalive_deadline_ > now && !keepalive_timer_.IsRunning()) { - // TODO(crbug.com/1161996): Remove this entry once the investigation is - // done. - VLOG(1) << "Starting a keepalive timer(" << timeout.InSecondsF() - << " seconds)"; + DVLOG(1) << "Starting a keepalive timer(" << timeout.InSecondsF() + << " seconds)"; keepalive_timer_.Start( FROM_HERE, keepalive_deadline_ - now, base::BindOnce( @@ -6198,15 +6194,11 @@ void ChromeContentBrowserClient::OnKeepaliveRequestStarted( void ChromeContentBrowserClient::OnKeepaliveRequestFinished() { #if !defined(OS_ANDROID) DCHECK_GT(num_keepalive_requests_, 0u); - // TODO(crbug.com/1161996): Remove this entry once the investigation is - // done. - VLOG(1) << "OnKeepaliveRequestFinished: " << num_keepalive_requests_ - << " ==> " << num_keepalive_requests_ - 1; + DVLOG(1) << "OnKeepaliveRequestFinished: " << num_keepalive_requests_ + << " ==> " << num_keepalive_requests_ - 1; --num_keepalive_requests_; if (num_keepalive_requests_ == 0) { - // TODO(crbug.com/1161996): Remove this entry once the investigation is - // done. - VLOG(1) << "Stopping the keepalive timer"; + DVLOG(1) << "Stopping the keepalive timer"; keepalive_timer_.Stop(); // This deletes the keep alive handle attached to the timer function and // unblock the shutdown sequence. @@ -6313,14 +6305,9 @@ base::TimeDelta ChromeContentBrowserClient::GetKeepaliveTimerTimeout( void ChromeContentBrowserClient::OnKeepaliveTimerFired( std::unique_ptr keep_alive_handle) { - // TODO(crbug.com/1161996): Remove this entry once the investigation is done. - VLOG(1) << "OnKeepaliveTimerFired"; const auto now = base::TimeTicks::Now(); const auto then = keepalive_deadline_; if (now < then) { - // TODO(crbug.com/1161996): Remove this entry once the investigation is - // done. - VLOG(1) << "Extending keepalive timer"; keepalive_timer_.Start( FROM_HERE, then - now, base::BindOnce(&ChromeContentBrowserClient::OnKeepaliveTimerFired, diff --git a/chrome/browser/devtools/protocol/devtools_protocol_browsertest.cc b/chrome/browser/devtools/protocol/devtools_protocol_browsertest.cc index 771400e429e1f8..ad3eb6b7681a9c 100644 --- a/chrome/browser/devtools/protocol/devtools_protocol_browsertest.cc +++ b/chrome/browser/devtools/protocol/devtools_protocol_browsertest.cc @@ -44,6 +44,7 @@ #include "third_party/blink/public/common/features.h" #include "third_party/boringssl/src/include/openssl/nid.h" #include "third_party/boringssl/src/include/openssl/ssl.h" +#include "url/origin.h" using DevToolsProtocolTest = DevToolsProtocolTestBase; using testing::AllOf; @@ -207,6 +208,68 @@ IN_PROC_BROWSER_TEST_F(DevToolsProtocolTest, EXPECT_EQ(nullptr, DevToolsWindow::FindDevToolsWindow(agent_host_.get())); } +IN_PROC_BROWSER_TEST_F( + DevToolsProtocolTest, + NoPendingUrlShownWhenAttachedToBrowserInitiatedFailedNavigation) { + GURL url("invalid.scheme:for-sure"); + ui_test_utils::AllBrowserTabAddedWaiter tab_added_waiter; + + content::WebContents* web_contents = + browser()->OpenURL(content::OpenURLParams( + url, content::Referrer(), WindowOpenDisposition::NEW_FOREGROUND_TAB, + ui::PAGE_TRANSITION_TYPED, false)); + tab_added_waiter.Wait(); + content::NavigationController& navigation_controller = + web_contents->GetController(); + content::NavigationEntry* pending_entry = + navigation_controller.GetPendingEntry(); + ASSERT_NE(nullptr, pending_entry); + EXPECT_EQ(url, pending_entry->GetURL()); + + EXPECT_EQ(pending_entry, navigation_controller.GetVisibleEntry()); + agent_host_ = content::DevToolsAgentHost::GetOrCreateFor(web_contents); + agent_host_->AttachClient(this); + SendCommandSync("Page.enable"); + + // Ensure that a failed pending entry is cleared when the DevTools protocol + // attaches, so that any modified page content is not attributed to the failed + // URL. (crbug/1192417) + EXPECT_EQ(nullptr, navigation_controller.GetPendingEntry()); + EXPECT_EQ(GURL(""), navigation_controller.GetVisibleEntry()->GetURL()); +} + +IN_PROC_BROWSER_TEST_F(DevToolsProtocolTest, + NoPendingUrlShownForPageNavigateFromChromeExtension) { + GURL url("https://example.com"); + // DevTools protocol use cases that have an initiator origin (e.g., for + // extensions) should use renderer-initiated navigations and be subject to URL + // spoof defenses. + navigation_initiator_origin_ = + url::Origin::Create(GURL("chrome-extension://abc123/")); + + // Attach DevTools and start a navigation but don't wait for it to finish. + Attach(); + SendCommandSync("Page.enable"); + base::DictionaryValue params; + params.SetStringKey("url", url.spec()); + SendCommand("Page.navigate", std::move(params), false); + content::NavigationController& navigation_controller = + web_contents()->GetController(); + content::NavigationEntry* pending_entry = + navigation_controller.GetPendingEntry(); + ASSERT_NE(nullptr, pending_entry); + EXPECT_EQ(url, pending_entry->GetURL()); + + // Attaching the DevTools protocol to the initial empty document of a new tab + // should prevent the pending URL from being visible, since the protocol + // allows modifying the initial empty document in a way that could be useful + // for URL spoofs. + EXPECT_NE(pending_entry, navigation_controller.GetVisibleEntry()); + EXPECT_NE(nullptr, navigation_controller.GetPendingEntry()); + EXPECT_EQ(GURL("about:blank"), + navigation_controller.GetVisibleEntry()->GetURL()); +} + class DevToolsProtocolTest_AppId : public DevToolsProtocolTest { public: DevToolsProtocolTest_AppId() { diff --git a/chrome/browser/devtools/protocol/devtools_protocol_test_support.cc b/chrome/browser/devtools/protocol/devtools_protocol_test_support.cc index 2c9b449c161739..670128f4788b3e 100644 --- a/chrome/browser/devtools/protocol/devtools_protocol_test_support.cc +++ b/chrome/browser/devtools/protocol/devtools_protocol_test_support.cc @@ -146,6 +146,10 @@ void DevToolsProtocolTestBase::AgentHostClosed( bool DevToolsProtocolTestBase::AllowUnsafeOperations() { return allow_unsafe_operations_; } +absl::optional +DevToolsProtocolTestBase::GetNavigationInitiatorOrigin() { + return navigation_initiator_origin_; +} bool DevToolsProtocolTestBase::MaySendInputEventsToBrowser() { return may_send_input_event_to_browser_; diff --git a/chrome/browser/devtools/protocol/devtools_protocol_test_support.h b/chrome/browser/devtools/protocol/devtools_protocol_test_support.h index 3cfba04f5bf2e5..45dc805de742bc 100644 --- a/chrome/browser/devtools/protocol/devtools_protocol_test_support.h +++ b/chrome/browser/devtools/protocol/devtools_protocol_test_support.h @@ -15,6 +15,8 @@ #include "chrome/test/base/in_process_browser_test.h" #include "content/public/browser/devtools_agent_host.h" #include "content/public/browser/devtools_agent_host_client.h" +#include "third_party/abseil-cpp/absl/types/optional.h" +#include "url/origin.h" class DevToolsProtocolTestBase : public InProcessBrowserTest, public content::DevToolsAgentHostClient { @@ -26,6 +28,8 @@ class DevToolsProtocolTestBase : public InProcessBrowserTest, allow_unsafe_operations_ = allow; } + absl::optional GetNavigationInitiatorOrigin() override; + protected: using NotificationMatcher = base::RepeatingCallback; @@ -82,6 +86,7 @@ class DevToolsProtocolTestBase : public InProcessBrowserTest, base::Value waiting_for_notification_params_; bool allow_unsafe_operations_ = true; bool may_send_input_event_to_browser_ = true; + absl::optional navigation_initiator_origin_; }; #endif // CHROME_BROWSER_DEVTOOLS_PROTOCOL_DEVTOOLS_PROTOCOL_TEST_SUPPORT_H_ diff --git a/chrome/browser/extensions/api/debugger/debugger_api.cc b/chrome/browser/extensions/api/debugger/debugger_api.cc index 73e19fb183e551..83c0bbed0b0815 100644 --- a/chrome/browser/extensions/api/debugger/debugger_api.cc +++ b/chrome/browser/extensions/api/debugger/debugger_api.cc @@ -58,6 +58,7 @@ #include "extensions/common/manifest_constants.h" #include "extensions/common/permissions/permissions_data.h" #include "extensions/common/switches.h" +#include "url/origin.h" using content::DevToolsAgentHost; using content::RenderProcessHost; @@ -208,6 +209,7 @@ class ExtensionDevToolsClientHost : public content::DevToolsAgentHostClient, bool MayAttachToBrowser() override; bool MayReadLocalFiles() override; bool MayWriteLocalFiles() override; + absl::optional GetNavigationInitiatorOrigin() override; private: using PendingRequests = @@ -440,6 +442,14 @@ bool ExtensionDevToolsClientHost::MayWriteLocalFiles() { return false; } +absl::optional +ExtensionDevToolsClientHost::GetNavigationInitiatorOrigin() { + // Ensure that navigations started by debugger API are treated as + // renderer-initiated by this extension, so that URL spoof defenses are in + // effect. + return extension_->origin(); +} + // DebuggerFunction ----------------------------------------------------------- DebuggerFunction::DebuggerFunction() : client_host_(nullptr) {} diff --git a/chrome/browser/feature_guide/notifications/internal/feature_notification_guide_service_impl_unittest.cc b/chrome/browser/feature_guide/notifications/internal/feature_notification_guide_service_impl_unittest.cc index 10b33483610e98..5cdbc3c5cfed5b 100644 --- a/chrome/browser/feature_guide/notifications/internal/feature_notification_guide_service_impl_unittest.cc +++ b/chrome/browser/feature_guide/notifications/internal/feature_notification_guide_service_impl_unittest.cc @@ -74,6 +74,14 @@ class TestSegmentationPlatformService OPTIMIZATION_TARGET_SEGMENTATION_CHROME_LOW_USER_ENGAGEMENT; std::move(callback).Run(result); } + segmentation_platform::SegmentSelectionResult GetCachedSegmentResult( + const std::string& segmentation_key) override { + segmentation_platform::SegmentSelectionResult result; + result.is_ready = true; + result.segment = optimization_guide::proto::OptimizationTarget:: + OPTIMIZATION_TARGET_SEGMENTATION_CHROME_LOW_USER_ENGAGEMENT; + return result; + } void EnableMetrics(bool signal_collection_allowed) override {} segmentation_platform::ServiceProxy* GetServiceProxy() override { return nullptr; diff --git a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/CachedFeatureFlagsSafeModeUnitTest.java b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/CachedFeatureFlagsSafeModeUnitTest.java index ec4d2e78e082f5..354661eb736a1f 100644 --- a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/CachedFeatureFlagsSafeModeUnitTest.java +++ b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/CachedFeatureFlagsSafeModeUnitTest.java @@ -12,8 +12,10 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.robolectric.annotation.Config; import org.chromium.base.FeatureList; +import org.chromium.base.task.test.ShadowPostTask; import org.chromium.base.test.BaseRobolectricTestRunner; import org.chromium.chrome.browser.flags.CachedFlagsSafeMode.Behavior; @@ -28,9 +30,10 @@ * {@link CachedFlagsSafeMode}. */ @RunWith(BaseRobolectricTestRunner.class) +@Config(shadows = {ShadowPostTask.class}) public class CachedFeatureFlagsSafeModeUnitTest { - private static final String CRASHY_FEATURE = "FeatureA"; - private static final String OK_FEATURE = "FeatureB"; + private static final String CRASHY_FEATURE = "CrashyFeature"; + private static final String OK_FEATURE = "OkFeature"; Map mDefaultsSwapped; @@ -324,7 +327,12 @@ private void endCrashyRun() { private void endCleanRun(boolean crashyFeatureValue, boolean okFeatureValue) { FeatureList.setTestFeatures(makeFeatureMap(crashyFeatureValue, okFeatureValue)); CachedFeatureFlags.cacheNativeFlags(Arrays.asList(CRASHY_FEATURE, OK_FEATURE)); + CachedFeatureFlags.onEndCheckpoint(); + // Async task writing values should have run synchronously because of ShadowPostTask. + assertTrue(CachedFlagsSafeMode.getSafeValuePreferences().contains( + "Chrome.Flags.CachedFlag.CrashyFeature")); + CachedFeatureFlags.resetFlagsForTesting(); } diff --git a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/CachedFlagsSafeMode.java b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/CachedFlagsSafeMode.java index ce4452cc1156c9..d2b2d794cad846 100644 --- a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/CachedFlagsSafeMode.java +++ b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/CachedFlagsSafeMode.java @@ -16,6 +16,7 @@ import org.chromium.base.Log; import org.chromium.base.TraceEvent; import org.chromium.base.metrics.RecordHistogram; +import org.chromium.base.task.AsyncTask; import org.chromium.chrome.browser.preferences.ChromePreferenceKeys; import org.chromium.chrome.browser.preferences.SharedPreferencesManager; import org.chromium.components.version_info.VersionInfo; @@ -129,9 +130,25 @@ public void onPauseCheckpoint() { void onEndCheckpoint(ValuesReturned safeValuesReturned) { SharedPreferencesManager.getInstance().writeInt( ChromePreferenceKeys.FLAGS_CRASH_STREAK_BEFORE_CACHE, 0); - writeSafeValues(safeValuesReturned); - RecordHistogram.recordEnumeratedHistogram( - "Variations.SafeModeCachedFlags.Cached", mBehavior.get(), Behavior.NUM_ENTRIES); + + new AsyncTask() { + @Override + protected Void doInBackground() { + try { + writeSafeValues(safeValuesReturned); + } catch (Exception e) { + Log.e(TAG, "Exception writing safe values.", e); + cancel(true); + } + return null; + } + + @Override + protected void onPostExecute(Void unused) { + RecordHistogram.recordEnumeratedHistogram("Variations.SafeModeCachedFlags.Cached", + mBehavior.get(), Behavior.NUM_ENTRIES); + } + }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); } private void engageSafeModeInNative() { diff --git a/chrome/browser/optimization_guide/prediction/prediction_manager.cc b/chrome/browser/optimization_guide/prediction/prediction_manager.cc index d53fb10ea7cc4f..99da00a4bee403 100644 --- a/chrome/browser/optimization_guide/prediction/prediction_manager.cc +++ b/chrome/browser/optimization_guide/prediction/prediction_manager.cc @@ -539,7 +539,7 @@ void PredictionManager::UpdatePredictionModels( DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); std::unique_ptr prediction_model_update_data = StoreUpdateData::CreatePredictionModelStoreUpdateData( - clock_->Now() + features::StoredModelsInactiveDuration()); + clock_->Now() + features::StoredModelsValidDuration()); bool has_models_to_update = false; std::string debug_msg; for (const auto& model : prediction_models) { @@ -621,7 +621,7 @@ void PredictionManager::OnModelReady(const proto::PredictionModel& model) { // Store the received model in the store. std::unique_ptr prediction_model_update_data = StoreUpdateData::CreatePredictionModelStoreUpdateData( - clock_->Now() + features::StoredModelsInactiveDuration()); + clock_->Now() + features::StoredModelsValidDuration()); prediction_model_update_data->CopyPredictionModelIntoUpdateData(model); model_and_features_store_->UpdatePredictionModels( std::move(prediction_model_update_data), diff --git a/chrome/browser/prefs/chrome_pref_service_factory.cc b/chrome/browser/prefs/chrome_pref_service_factory.cc index 43e1f8edfae94f..22e37f33785e3e 100644 --- a/chrome/browser/prefs/chrome_pref_service_factory.cc +++ b/chrome/browser/prefs/chrome_pref_service_factory.cc @@ -48,6 +48,7 @@ #include "components/prefs/pref_service.h" #include "components/prefs/pref_store.h" #include "components/prefs/pref_value_store.h" +#include "components/prefs/standalone_browser_pref_store.h" #include "components/safe_browsing/core/common/safe_browsing_prefs.h" #include "components/search_engines/default_search_manager.h" #include "components/search_engines/search_engines_pref_names.h" @@ -313,6 +314,12 @@ void PrepareFactory(sync_preferences::PrefServiceSyncableFactory* factory, } #endif +#if BUILDFLAG(IS_CHROMEOS_ASH) + scoped_refptr standalone_browser_prefs = + base::MakeRefCounted(); + factory->set_standalone_browser_prefs(standalone_browser_prefs); +#endif + factory->set_async(async); factory->set_extension_prefs(std::move(extension_prefs)); factory->set_command_line_prefs( diff --git a/chrome/browser/resources/download_shelf/app.ts b/chrome/browser/resources/download_shelf/app.ts index 1680034ec64f17..c370d495f33b7c 100644 --- a/chrome/browser/resources/download_shelf/app.ts +++ b/chrome/browser/resources/download_shelf/app.ts @@ -24,25 +24,22 @@ export class DownloadShelfAppElement extends CustomElement { constructor() { super(); - /** @private {!DownloadShelfApiProxy} */ this.apiProxy_ = DownloadShelfApiProxyImpl.getInstance(); - const showAllButton = this.$('#show-all-button') as HTMLElement; + const showAllButton = this.$('#show-all-button')!; showAllButton.innerText = loadTimeData.getString('showAll'); showAllButton.addEventListener('click', () => this.onShowAll_()); - const closeButton = this.$('#close-button'); + const closeButton = this.$('#close-button')!; closeButton.setAttribute('aria-label', loadTimeData.getString('close')); closeButton.addEventListener('click', () => this.onClose_()); } - /** @private */ - onShowAll_() { + private onShowAll_() { this.apiProxy_.doShowAll(); } - /** @private */ - onClose_() { + private onClose_() { this.apiProxy_.doClose(); } } diff --git a/chrome/browser/resources/download_shelf/download_item.ts b/chrome/browser/resources/download_shelf/download_item.ts index d19f678ecde219..3003795739901f 100644 --- a/chrome/browser/resources/download_shelf/download_item.ts +++ b/chrome/browser/resources/download_shelf/download_item.ts @@ -44,20 +44,20 @@ export class DownloadItemElement extends CustomElement { this.opened = false; this.apiProxy_ = DownloadShelfApiProxyImpl.getInstance(); - this.$('#shadow-mask') - .addEventListener('click', () => this.onOpenButtonClick_()); - this.$('#dropdown-button') - .addEventListener('click', e => this.onDropdownButtonClick_(e)); + this.$('#shadow-mask')!.addEventListener( + 'click', () => this.onOpenButtonClick_()); + this.$('#dropdown-button')!.addEventListener( + 'click', e => this.onDropdownButtonClick_(e)); const discardButton = this.$('#discard-button') as HTMLElement; discardButton.innerText = loadTimeData.getString('discardButtonText'); discardButton.addEventListener('click', () => this.onDiscardButtonClick_()); - this.$('#keep-button') - .addEventListener('click', () => this.onKeepButtonClick_()); + this.$('#keep-button')!.addEventListener( + 'click', () => this.onKeepButtonClick_()); this.addEventListener('contextmenu', e => this.onContextMenu_(e)); - this.$('.progress-indicator').addEventListener('animationend', () => { - this.$('.progress-indicator') - .classList.remove('download-complete-animation'); + this.$('.progress-indicator')!.addEventListener('animationend', () => { + this.$('.progress-indicator')!.classList.remove( + 'download-complete-animation'); }); } @@ -132,8 +132,8 @@ export class DownloadItemElement extends CustomElement { this.progress = 1; // Only start animation if it's called from OnDownloadUpdated. if (this.downloadUpdated_) { - this.$('.progress-indicator') - .classList.add('download-complete-animation'); + this.$('.progress-indicator')!.classList.add( + 'download-complete-animation'); } break; case DownloadState.kInterrupted: diff --git a/chrome/browser/resources/download_shelf/download_list.ts b/chrome/browser/resources/download_shelf/download_list.ts index 740ceeaf6bfd59..16266ac6561e69 100644 --- a/chrome/browser/resources/download_shelf/download_list.ts +++ b/chrome/browser/resources/download_shelf/download_list.ts @@ -31,7 +31,7 @@ export class DownloadListElement extends CustomElement { super(); this.apiProxy_ = DownloadShelfApiProxyImpl.getInstance(); - this.listElement_ = this.$('#download-list') as HTMLElement; + this.listElement_ = this.$('#download-list')!; this.resizeObserver_ = new ResizeObserver(() => this.updateElements_()); this.resizeObserver_.observe(this.listElement_); @@ -123,7 +123,7 @@ export class DownloadListElement extends CustomElement { private clear_() { while (this.listenerIds_.length) { this.apiProxy_.getCallbackRouter().removeListener( - this.listenerIds_.shift()); + this.listenerIds_.shift()!); } while (this.listElement_.firstChild) { diff --git a/chrome/browser/resources/download_shelf/tsconfig_base.json b/chrome/browser/resources/download_shelf/tsconfig_base.json index 895f3746b7d473..3e71f76307c7b0 100644 --- a/chrome/browser/resources/download_shelf/tsconfig_base.json +++ b/chrome/browser/resources/download_shelf/tsconfig_base.json @@ -4,7 +4,6 @@ "allowJs": true, "noUncheckedIndexedAccess": false, "noUnusedLocals": false, - "strictNullChecks": false, "strictPropertyInitialization": false } } diff --git a/chrome/browser/resources/settings/chromeos/BUILD.gn b/chrome/browser/resources/settings/chromeos/BUILD.gn index bb43b3b9e2cd4e..96623b07e53a04 100644 --- a/chrome/browser/resources/settings/chromeos/BUILD.gn +++ b/chrome/browser/resources/settings/chromeos/BUILD.gn @@ -60,6 +60,11 @@ if (optimize_webui) { "chrome://resources/cr_components/chromeos/bluetooth/cros_bluetooth_config.js", "chrome://resources/cr_components/chromeos/cellular_setup/mojo_interface_provider.m.js", "chrome://resources/cr_components/chromeos/network/mojo_interface_provider.m.js", + "chrome://resources/cr_components/app_management/app_management.mojom-lite.js", + "chrome://resources/cr_components/app_management/file_path.mojom-lite.js", + "chrome://resources/cr_components/app_management/image.mojom-lite.js", + "chrome://resources/cr_components/app_management/safe_base_name.mojom-lite.js", + "chrome://resources/cr_components/app_management/types.mojom-lite.js", "chrome://resources/js/cr.m.js", "chrome://resources/chromeos/colors/cros_styles.css", "chrome://resources/mojo/mojo/public/js/mojo_bindings_lite.js", @@ -104,11 +109,11 @@ preprocess_if_expr("preprocess_mojo_v3") { # Mojo files generated by non-OS-settings targets, not bundled. preprocess_if_expr("preprocess_external_mojo") { deps = [ - "//chrome/browser/ui/webui/app_management:mojo_bindings_js", "//chrome/browser/ui/webui/settings/chromeos/os_apps_page/mojom:mojom_js", "//components/services/app_service/public/mojom:mojom_js", "//mojo/public/mojom/base", "//ui/gfx/image/mojom:mojom_js", + "//ui/webui/resources/cr_components/app_management:mojo_bindings_js", ] in_folder = "$root_gen_dir" @@ -121,7 +126,7 @@ preprocess_if_expr("preprocess_external_mojo") { "mojo/public/mojom/base/file_path.mojom-lite.js", "mojo/public/mojom/base/safe_base_name.mojom-lite.js", "ui/gfx/image/mojom/image.mojom-lite.js", - "chrome/browser/ui/webui/app_management/app_management.mojom-lite.js", + "ui/webui/resources/cr_components/app_management/app_management.mojom-lite.js", "chrome/browser/ui/webui/settings/chromeos/os_apps_page/mojom/app_notification_handler.mojom-lite.js", "components/services/app_service/public/mojom/types.mojom-lite.js", ] @@ -188,7 +193,7 @@ generate_grd("build_grd") { "mojo/public/mojom/base/file_path.mojom-lite.js|app-management/file_path.mojom-lite.js", "mojo/public/mojom/base/safe_base_name.mojom-lite.js|app-management/safe_base_name.mojom-lite.js", "ui/gfx/image/mojom/image.mojom-lite.js|app-management/image.mojom-lite.js", - "chrome/browser/ui/webui/app_management/app_management.mojom-lite.js|app-management/app_management.mojom-lite.js", + "ui/webui/resources/cr_components/app_management/app_management.mojom-lite.js|app-management/app_management.mojom-lite.js", "chrome/browser/ui/webui/settings/chromeos/os_apps_page/mojom/app_notification_handler.mojom-lite.js|os_apps_page/app_notification_handler.mojom-lite.js", "components/services/app_service/public/mojom/types.mojom-lite.js|app-management/types.mojom-lite.js", "../../nearby_share/shared/nearby_share_progress_bar_dark.json|nearby_share_progress_bar_dark.json", @@ -256,17 +261,13 @@ preprocess_if_expr("preprocess_v3") { "chromeos/os_apps_page/app_management_page/actions.js", "chromeos/os_apps_page/app_management_page/api_listener.js", "chromeos/os_apps_page/app_management_page/browser_proxy.js", - "chromeos/os_apps_page/app_management_page/constants.js", "chromeos/os_apps_page/app_management_page/fake_page_handler.js", "chromeos/os_apps_page/app_management_page/reducers.js", "chromeos/os_apps_page/app_management_page/plugin_vm_page/plugin_vm_browser_proxy.js", "chromeos/os_apps_page/app_management_page/store.js", "chromeos/os_apps_page/app_management_page/store_client.js", - "chromeos/os_apps_page/app_management_page/types.js", "chromeos/os_apps_page/app_management_page/util.js", "chromeos/os_apps_page/app_notifications_page/mojo_interface_provider.js", - "chromeos/os_apps_page/permission_constants.js", - "chromeos/os_apps_page/permission_util.js", "chromeos/os_languages_page/input_method_settings.js", "chromeos/os_languages_page/languages_browser_proxy.js", "chromeos/os_languages_page/languages.js", @@ -492,17 +493,13 @@ preprocess_if_expr("preprocess_gen_v3") { "chromeos/os_apps_page/app_management_page/icons.js", "chromeos/os_apps_page/app_management_page/main_view.js", "chromeos/os_apps_page/app_management_page/more_permissions_item.js", - "chromeos/os_apps_page/app_management_page/permission_item.js", "chromeos/os_apps_page/app_management_page/pin_to_shelf_item.js", "chromeos/os_apps_page/app_management_page/plugin_vm_page/plugin_vm_detail_view.js", "chromeos/os_apps_page/app_management_page/pwa_detail_view.js", "chromeos/os_apps_page/app_management_page/resize_lock_item.js", - "chromeos/os_apps_page/app_management_page/shared_style.js", - "chromeos/os_apps_page/app_management_page/shared_vars.js", "chromeos/os_apps_page/app_management_page/supported_links_overlapping_apps_dialog.js", "chromeos/os_apps_page/app_management_page/supported_links_dialog.js", "chromeos/os_apps_page/app_management_page/supported_links_item.js", - "chromeos/os_apps_page/app_management_page/toggle_row.js", "chromeos/os_apps_page/app_management_page/uninstall_button.js", "chromeos/os_apps_page/os_apps_page.js", "chromeos/os_files_page/os_files_page.js", diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/BUILD.gn b/chrome/browser/resources/settings/chromeos/os_apps_page/BUILD.gn index aeb1ba99a91dad..52863611fd1ae3 100644 --- a/chrome/browser/resources/settings/chromeos/os_apps_page/BUILD.gn +++ b/chrome/browser/resources/settings/chromeos/os_apps_page/BUILD.gn @@ -13,8 +13,6 @@ js_type_check("closure_compile_module") { ":android_apps_browser_proxy", ":android_apps_subpage", ":os_apps_page", - ":permission_constants", - ":permission_util", ] } @@ -45,10 +43,10 @@ js_library("os_apps_page") { "..:os_route.m", "..:prefs_behavior", "../../:router", - "./app_management_page:constants", "./app_management_page:store_client", "./app_notifications_page:mojo_interface_provider", "//chrome/browser/ui/webui/settings/chromeos/os_apps_page/mojom:mojom_js_library_for_compile", + "//ui/webui/resources/cr_components/app_management:constants", "//ui/webui/resources/cr_components/chromeos/localized_link:localized_link", "//ui/webui/resources/cr_components/chromeos/localized_link:localized_link", "//ui/webui/resources/js:cr.m", @@ -60,17 +58,6 @@ js_library("os_apps_page") { ] } -js_library("permission_util") { - deps = [ - ":permission_constants", - "//ui/webui/resources/js:assert.m", - ] -} - -js_library("permission_constants") { - deps = [ "//chrome/browser/ui/webui/app_management:mojo_bindings_js_library_for_compile" ] -} - html_to_js("web_components") { js_files = [ "android_apps_subpage.js", diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/BUILD.gn b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/BUILD.gn index 56065b91659789..a79af07f43a507 100644 --- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/BUILD.gn +++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/BUILD.gn @@ -18,26 +18,20 @@ js_type_check("closure_compile_module") { ":arc_detail_view", ":browser_proxy", ":chrome_app_detail_view", - ":constants", ":dom_switch", ":fake_page_handler", ":icons", ":main_view", ":more_permissions_item", - ":permission_item", ":pin_to_shelf_item", ":pwa_detail_view", ":reducers", ":resize_lock_item", - ":shared_style", - ":shared_vars", ":store", ":store_client", ":supported_links_dialog", ":supported_links_item", ":supported_links_overlapping_apps_dialog", - ":toggle_row", - ":types", ":uninstall_button", ":util", ] @@ -45,7 +39,7 @@ js_type_check("closure_compile_module") { js_library("actions") { deps = [ - "//chrome/browser/ui/webui/app_management:mojo_bindings_js_library_for_compile", + "//ui/webui/resources/cr_components/app_management:mojo_bindings_js_library_for_compile", "//ui/webui/resources/js:cr.m", ] } @@ -96,21 +90,21 @@ js_library("app_management_page") { js_library("arc_detail_view") { deps = [ - ":constants", ":fake_page_handler", ":more_permissions_item", - ":permission_item", ":pin_to_shelf_item", ":store_client", ":supported_links_item", ":util", + "//ui/webui/resources/cr_components/app_management:constants", + "//ui/webui/resources/cr_components/app_management:permission_item", ] } js_library("browser_proxy") { deps = [ ":fake_page_handler", - "//chrome/browser/ui/webui/app_management:mojo_bindings_js_library_for_compile", + "//ui/webui/resources/cr_components/app_management:mojo_bindings_js_library_for_compile", ] } @@ -125,10 +119,6 @@ js_library("chrome_app_detail_view") { ] } -js_library("constants") { - deps = [ "//chrome/browser/ui/webui/app_management:mojo_bindings_js_library_for_compile" ] -} - js_library("dom_switch") { deps = [ "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled", @@ -139,7 +129,7 @@ js_library("dom_switch") { js_library("fake_page_handler") { deps = [ - "//chrome/browser/ui/webui/app_management:mojo_bindings_js_library_for_compile", + "//ui/webui/resources/cr_components/app_management:mojo_bindings_js_library_for_compile", "//ui/webui/resources/js:promise_resolver.m", ] } @@ -152,12 +142,12 @@ js_library("main_view") { deps = [ ":app_item", ":browser_proxy", - ":constants", ":store_client", ":util", "../..:os_route.m", "../..:route_observer_behavior", "../../..:router", + "//ui/webui/resources/cr_components/app_management:constants", "//ui/webui/resources/js:assert.m", "//ui/webui/resources/js:load_time_data.m", ] @@ -167,46 +157,35 @@ js_library("more_permissions_item") { deps = [ "//ui/webui/resources/cr_elements/cr_icon_button:cr_icon_button.m" ] } -js_library("permission_item") { - deps = [ - ":browser_proxy", - ":fake_page_handler", - ":store_client", - ":toggle_row", - ":util", - "../..:metrics_recorder.m", - ] -} - js_library("pin_to_shelf_item") { deps = [ ":browser_proxy", - ":constants", - ":toggle_row", - ":types", ":util", "../..:metrics_recorder.m", + "//ui/webui/resources/cr_components/app_management:constants", + "//ui/webui/resources/cr_components/app_management:toggle_row", + "//ui/webui/resources/cr_components/app_management:types", "//ui/webui/resources/js:assert.m", ] } js_library("pwa_detail_view") { deps = [ - ":constants", ":fake_page_handler", ":more_permissions_item", - ":permission_item", ":pin_to_shelf_item", ":store_client", ":supported_links_item", ":util", + "//ui/webui/resources/cr_components/app_management:constants", + "//ui/webui/resources/cr_components/app_management:permission_item", ] } js_library("reducers") { deps = [ - ":types", ":util", + "//ui/webui/resources/cr_components/app_management:types", "//ui/webui/resources/js:cr.m", ] } @@ -214,22 +193,14 @@ js_library("reducers") { js_library("resize_lock_item") { deps = [ ":browser_proxy", - ":constants", - ":toggle_row", - ":types", ":util", + "//ui/webui/resources/cr_components/app_management:constants", + "//ui/webui/resources/cr_components/app_management:toggle_row", + "//ui/webui/resources/cr_components/app_management:types", "//ui/webui/resources/js:assert.m", ] } -js_library("shared_style") { - deps = [] -} - -js_library("shared_vars") { - deps = [] -} - js_library("store") { deps = [ "//ui/webui/resources/js:cr.m", @@ -240,7 +211,7 @@ js_library("store") { js_library("store_client") { deps = [ ":store", - ":types", + "//ui/webui/resources/cr_components/app_management:types", "//ui/webui/resources/js:cr.m", "//ui/webui/resources/js/cr/ui:store", "//ui/webui/resources/js/cr/ui:store_client", @@ -268,13 +239,12 @@ js_library("supported_links_dialog") { js_library("supported_links_item") { deps = [ ":browser_proxy", - ":constants", ":store_client", ":supported_links_dialog", ":supported_links_overlapping_apps_dialog", - ":types", ":util", "../..:metrics_recorder.m", + "//ui/webui/resources/cr_components/app_management:constants", "//ui/webui/resources/cr_components/chromeos/localized_link:localized_link", "//ui/webui/resources/cr_elements/cr_radio_button:cr_radio_button.m", "//ui/webui/resources/cr_elements/cr_radio_group:cr_radio_group.m", @@ -282,20 +252,6 @@ js_library("supported_links_item") { ] } -js_library("toggle_row") { - deps = [ - ":browser_proxy", - ":store_client", - ":types", - "//ui/webui/resources/cr_elements/cr_toggle:cr_toggle.m", - "//ui/webui/resources/cr_elements/policy:cr_policy_indicator.m", - ] -} - -js_library("types") { - deps = [ "//chrome/browser/ui/webui/app_management:mojo_bindings_js_library_for_compile" ] -} - js_library("uninstall_button") { deps = [ ":store_client", @@ -309,7 +265,6 @@ js_library("uninstall_button") { js_library("util") { deps = [ - "../:permission_constants", "../..:os_route.m", "../../..:router", ] @@ -326,16 +281,12 @@ html_to_js("web_components") { "icons.js", "main_view.js", "more_permissions_item.js", - "permission_item.js", "pin_to_shelf_item.js", "pwa_detail_view.js", "resize_lock_item.js", - "shared_style.js", - "shared_vars.js", "supported_links_overlapping_apps_dialog.js", "supported_links_dialog.js", "supported_links_item.js", - "toggle_row.js", "uninstall_button.js", ] } diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/api_listener.js b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/api_listener.js index eac82664ffb8ce..8aed5e4f0f520c 100644 --- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/api_listener.js +++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/api_listener.js @@ -2,13 +2,13 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import {createInitialState} from '//resources/cr_components/app_management/util.js'; import {assert} from 'chrome://resources/js/assert.m.js'; import {Action} from 'chrome://resources/js/cr/ui/store.js'; import {addApp, changeApp, removeApp} from './actions.js'; import {BrowserProxy} from './browser_proxy.js'; import {AppManagementStore} from './store.js'; -import {createInitialState} from './util.js'; let initialized = false; diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/app_detail_view.js b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/app_detail_view.js index 590e42f337b194..59195920f6bd75 100644 --- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/app_detail_view.js +++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/app_detail_view.js @@ -10,6 +10,8 @@ import './plugin_vm_page/plugin_vm_detail_view.js'; import './borealis_page/borealis_detail_view.js'; import '../../../settings_shared_css.js'; +import {AppManagementUserAction, AppType} from '//resources/cr_components/app_management/constants.js'; +import {getSelectedApp, recordAppManagementUserAction} from '//resources/cr_components/app_management/util.js'; import {assert, assertNotReached} from '//resources/js/assert.m.js'; import {afterNextRender, flush, html, Polymer, TemplateInstanceBase, Templatizer} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js'; @@ -18,9 +20,8 @@ import {routes} from '../../os_route.m.js'; import {RouteObserverBehavior} from '../../route_observer_behavior.js'; import {updateSelectedAppId} from './actions.js'; -import {AppManagementUserAction, AppType} from './constants.js'; import {AppManagementStoreClient} from './store_client.js'; -import {getSelectedApp, openMainPage, recordAppManagementUserAction} from './util.js'; +import {openMainPage} from './util.js'; Polymer({ _template: html`{__html_template__}`, diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/app_item.js b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/app_item.js index d903c745758d2b..bd5f8ecc4444a8 100644 --- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/app_item.js +++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/app_item.js @@ -1,17 +1,18 @@ // Copyright 2018 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import './shared_style.js'; -import './shared_vars.js'; +import '//resources/cr_components/app_management/shared_style.js'; +import '//resources/cr_components/app_management/shared_vars.js'; import '//resources/cr_elements/cr_icons_css.m.js'; +import {AppManagementEntryPoint, AppManagementEntryPointsHistogramName, AppType} from '//resources/cr_components/app_management/constants.js'; +import {getAppIcon} from '//resources/cr_components/app_management/util.js'; import {assert, assertNotReached} from '//resources/js/assert.m.js'; import {afterNextRender, flush, html, Polymer, TemplateInstanceBase, Templatizer} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js'; import {updateSelectedAppId} from './actions.js'; -import {AppManagementEntryPoint, AppManagementEntryPointsHistogramName, AppType} from './constants.js'; import {AppManagementStoreClient} from './store_client.js'; -import {getAppIcon, openAppDetailPage} from './util.js'; +import {openAppDetailPage} from './util.js'; Polymer({ _template: html`{__html_template__}`, diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/arc_detail_view.html b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/arc_detail_view.html index c03a73edc602c4..386b7de944debe 100644 --- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/arc_detail_view.html +++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/arc_detail_view.html @@ -11,6 +11,7 @@ @@ -23,26 +24,31 @@
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/arc_detail_view.js b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/arc_detail_view.js index fb816bb28b98c6..00f14d99a77e4a 100644 --- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/arc_detail_view.js +++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/arc_detail_view.js @@ -3,18 +3,18 @@ // found in the LICENSE file. import './icons.js'; import './more_permissions_item.js'; -import './permission_item.js'; import './pin_to_shelf_item.js'; import './resize_lock_item.js'; -import './shared_style.js'; import './supported_links_item.js'; +import '//resources/cr_components/app_management/shared_style.js'; +import '//resources/cr_components/app_management/permission_item.js'; import '//resources/cr_elements/icons.m.js'; import {afterNextRender, flush, html, Polymer, TemplateInstanceBase, Templatizer} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js'; +import {getAppIcon, getPermission, getSelectedApp} from 'chrome://resources/cr_components/app_management/util.js'; import {BrowserProxy} from './browser_proxy.js'; import {AppManagementStoreClient} from './store_client.js'; -import {getAppIcon, getPermission, getSelectedApp} from './util.js'; Polymer({ _template: html`{__html_template__}`, diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/borealis_page/BUILD.gn b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/borealis_page/BUILD.gn index c9541da12d0435..1124ca5174b735 100644 --- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/borealis_page/BUILD.gn +++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/borealis_page/BUILD.gn @@ -14,12 +14,12 @@ js_type_check("closure_compile_module") { js_library("borealis_detail_view") { deps = [ - "../:permission_item", "../:pin_to_shelf_item", "../:store_client", "../:util", "../../..:os_route.m", "../../../..:router", + "//ui/webui/resources/cr_components/app_management:permission_item", ] } diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/borealis_page/borealis_detail_view.html b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/borealis_page/borealis_detail_view.html index 14534897c55eaf..5011fcda51040e 100644 --- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/borealis_page/borealis_detail_view.html +++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/borealis_page/borealis_detail_view.html @@ -45,6 +45,7 @@
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/borealis_page/borealis_detail_view.js b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/borealis_page/borealis_detail_view.js index 416af2124fc27e..5713205e73d297 100644 --- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/borealis_page/borealis_detail_view.js +++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/borealis_page/borealis_detail_view.js @@ -7,13 +7,13 @@ const kBorealisMainAppId = 'epfhbkiklgmlkhfpbcdleadnhcfdjfmo'; import {Polymer, html} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js'; import '../icons.js'; -import '../permission_item.js'; import '../pin_to_shelf_item.js'; -import '../shared_style.js'; +import '//resources/cr_components/app_management/shared_style.js'; +import '//resources/cr_components/app_management/permission_item.js'; import '//resources/cr_elements/icons.m.js'; import {AppManagementStoreClient} from '../store_client.js'; -import {getSelectedApp} from '../util.js'; +import {getSelectedApp} from 'chrome://resources/cr_components/app_management/util.js'; import {routes} from '../../../os_route.m.js'; import {Router} from '../../../../router.js'; diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/browser_proxy.js b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/browser_proxy.js index 79d9b0d4c3f90a..79b4d7730e1c90 100644 --- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/browser_proxy.js +++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/browser_proxy.js @@ -12,12 +12,12 @@ import '/app-management/safe_base_name.mojom-lite.js'; import '/app-management/types.mojom-lite.js'; import '/app-management/app_management.mojom-lite.js'; +import {BrowserProxy as ComponentBrowserProxy} from '//resources/cr_components/app_management/browser_proxy.js'; +import {AppType, InstallReason} from '//resources/cr_components/app_management/constants.js'; +import {PermissionType, TriState} from '//resources/cr_components/app_management/permission_constants.js'; import {addSingletonGetter} from 'chrome://resources/js/cr.m.js'; import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js'; -import {PermissionType, TriState} from '../permission_constants.js'; - -import {AppType, InstallReason} from './constants.js'; import {FakePageHandler} from './fake_page_handler.js'; export class BrowserProxy { diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/chrome_app_detail_view.js b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/chrome_app_detail_view.js index 74228c2dbaa8eb..8024342aa1d571 100644 --- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/chrome_app_detail_view.js +++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/chrome_app_detail_view.js @@ -3,13 +3,13 @@ // found in the LICENSE file. import './more_permissions_item.js'; import './pin_to_shelf_item.js'; -import './shared_style.js'; +import '//resources/cr_components/app_management/shared_style.js'; import {afterNextRender, flush, html, Polymer, TemplateInstanceBase, Templatizer} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js'; +import {getSelectedApp} from 'chrome://resources/cr_components/app_management/util.js'; import {BrowserProxy} from './browser_proxy.js'; import {AppManagementStoreClient} from './store_client.js'; -import {getSelectedApp} from './util.js'; Polymer({ _template: html`{__html_template__}`, diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/fake_page_handler.js b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/fake_page_handler.js index ae981926556187..cfb5bbf534db64 100644 --- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/fake_page_handler.js +++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/fake_page_handler.js @@ -2,13 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import {AppType, OptionalBool} from '//resources/cr_components/app_management/constants.js'; +import {PermissionType, PermissionValue, TriState} from '//resources/cr_components/app_management/permission_constants.js'; +import {createBoolPermission, createTriStatePermission, getTriStatePermissionValue} from '//resources/cr_components/app_management/permission_util.js'; import {assert} from 'chrome://resources/js/assert.m.js'; import {PromiseResolver} from 'chrome://resources/js/promise_resolver.m.js'; -import {PermissionType, PermissionValue, TriState} from '../permission_constants.js'; -import {createBoolPermission, createTriStatePermission, getTriStatePermissionValue} from '../permission_util.js'; - -import {AppType, OptionalBool} from './constants.js'; import {AppManagementStore} from './store.js'; /** diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/main_view.js b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/main_view.js index 0a22ee16084c30..4940c7ec8e19d8 100644 --- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/main_view.js +++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/main_view.js @@ -3,13 +3,14 @@ // found in the LICENSE file. import './app_item.js'; -import './shared_style.js'; +import '//resources/cr_components/app_management/shared_style.js'; import '//resources/cr_elements/cr_icon_button/cr_icon_button.m.js'; import '//resources/cr_elements/shared_style_css.m.js'; import {assert, assertNotReached} from '//resources/js/assert.m.js'; import {focusWithoutInk} from '//resources/js/cr/ui/focus_without_ink.m.js'; import {afterNextRender, flush, html, Polymer, TemplateInstanceBase, Templatizer} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js'; +import {alphabeticalSort} from 'chrome://resources/cr_components/app_management/util.js'; import {Route, Router} from '../../../router.js'; import {routes} from '../../os_route.m.js'; @@ -17,7 +18,6 @@ import {RouteObserverBehavior} from '../../route_observer_behavior.js'; import {AppManagementStore} from './store.js'; import {AppManagementStoreClient} from './store_client.js'; -import {alphabeticalSort} from './util.js'; Polymer({ _template: html`{__html_template__}`, diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/more_permissions_item.js b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/more_permissions_item.js index 9051707e70fa9c..991544b4bb67b0 100644 --- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/more_permissions_item.js +++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/more_permissions_item.js @@ -2,14 +2,14 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import './shared_style.js'; +import '//resources/cr_components/app_management/shared_style.js'; import '//resources/cr_elements/cr_icon_button/cr_icon_button.m.js'; +import {AppManagementUserAction} from '//resources/cr_components/app_management/constants.js'; import {afterNextRender, flush, html, Polymer, TemplateInstanceBase, Templatizer} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js'; +import {recordAppManagementUserAction} from 'chrome://resources/cr_components/app_management/util.js'; import {BrowserProxy} from './browser_proxy.js'; -import {AppManagementUserAction} from './constants.js'; -import {recordAppManagementUserAction} from './util.js'; Polymer({ _template: html`{__html_template__}`, diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/pin_to_shelf_item.js b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/pin_to_shelf_item.js index 1fcbb6af8822b1..5dea01fdd4fce1 100644 --- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/pin_to_shelf_item.js +++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/pin_to_shelf_item.js @@ -1,16 +1,16 @@ // Copyright 2019 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import './toggle_row.js'; +import '//resources/cr_components/app_management/toggle_row.js'; +import {AppManagementUserAction, OptionalBool} from '//resources/cr_components/app_management/constants.js'; import {assert, assertNotReached} from '//resources/js/assert.m.js'; import {afterNextRender, flush, html, Polymer, TemplateInstanceBase, Templatizer} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js'; +import {convertOptionalBoolToBool, recordAppManagementUserAction, toggleOptionalBool} from 'chrome://resources/cr_components/app_management/util.js'; import {recordClick, recordNavigation, recordPageBlur, recordPageFocus, recordSearch, recordSettingChange, setUserActionRecorderForTesting} from '../../metrics_recorder.m.js'; import {BrowserProxy} from './browser_proxy.js'; -import {AppManagementUserAction, OptionalBool} from './constants.js'; -import {convertOptionalBoolToBool, recordAppManagementUserAction, toggleOptionalBool} from './util.js'; Polymer({ _template: html`{__html_template__}`, diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/plugin_vm_page/BUILD.gn b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/plugin_vm_page/BUILD.gn index be8d913b746a00..4b517b659a423b 100644 --- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/plugin_vm_page/BUILD.gn +++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/plugin_vm_page/BUILD.gn @@ -22,12 +22,12 @@ js_library("plugin_vm_browser_proxy") { js_library("plugin_vm_detail_view") { deps = [ ":plugin_vm_browser_proxy", - "../:constants", - "../:permission_item", "../:store_client", "../:util", "../../..:os_route.m", "../../../..:router", + "//ui/webui/resources/cr_components/app_management:constants", + "//ui/webui/resources/cr_components/app_management:permission_item", "//ui/webui/resources/js:assert.m", "//ui/webui/resources/js:load_time_data.m", "//ui/webui/resources/js:web_ui_listener_behavior.m", diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/plugin_vm_page/plugin_vm_detail_view.html b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/plugin_vm_page/plugin_vm_detail_view.html index fb39c37417d99e..4993af83a4461a 100644 --- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/plugin_vm_page/plugin_vm_detail_view.html +++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/plugin_vm_page/plugin_vm_detail_view.html @@ -10,18 +10,21 @@
@@ -64,4 +67,4 @@
- \ No newline at end of file + diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/plugin_vm_page/plugin_vm_detail_view.js b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/plugin_vm_page/plugin_vm_detail_view.js index c5414cd3cabd98..3ea51cd3352a3e 100644 --- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/plugin_vm_page/plugin_vm_detail_view.js +++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/plugin_vm_page/plugin_vm_detail_view.js @@ -3,9 +3,9 @@ // found in the LICENSE file. import '../icons.js'; -import '../permission_item.js'; import '../pin_to_shelf_item.js'; -import '../shared_style.js'; +import '//resources/cr_components/app_management/permission_item.js'; +import '//resources/cr_components/app_management/shared_style.js'; import '//resources/cr_elements/cr_icon_button/cr_icon_button.m.js'; import '//resources/cr_elements/icons.m.js'; @@ -13,11 +13,11 @@ import {assertNotReached} from '//resources/js/assert.m.js'; import {loadTimeData} from '//resources/js/load_time_data.m.js'; import {WebUIListenerBehavior} from '//resources/js/web_ui_listener_behavior.m.js'; import {html, Polymer} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js'; +import {getSelectedApp} from 'chrome://resources/cr_components/app_management/util.js'; import {Router} from '../../../../router.js'; import {routes} from '../../../os_route.m.js'; import {AppManagementStoreClient} from '../store_client.js'; -import {getSelectedApp} from '../util.js'; import {PluginVmBrowserProxyImpl} from './plugin_vm_browser_proxy.js'; diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/pwa_detail_view.html b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/pwa_detail_view.html index 3fa5ee8dc6e48c..57bbb0d2ac534f 100644 --- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/pwa_detail_view.html +++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/pwa_detail_view.html @@ -8,6 +8,7 @@ @@ -18,16 +19,19 @@
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/pwa_detail_view.js b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/pwa_detail_view.js index fb81312cfe0889..6ba3ce7ed7d048 100644 --- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/pwa_detail_view.js +++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/pwa_detail_view.js @@ -3,17 +3,17 @@ // found in the LICENSE file. import './icons.js'; import './more_permissions_item.js'; -import './permission_item.js'; import './pin_to_shelf_item.js'; -import './shared_style.js'; import './supported_links_item.js'; +import '//resources/cr_components/app_management/shared_style.js'; +import '//resources/cr_components/app_management/permission_item.js'; import '//resources/cr_elements/icons.m.js'; import {afterNextRender, flush, html, Polymer, TemplateInstanceBase, Templatizer} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js'; +import {getAppIcon, getSelectedApp} from 'chrome://resources/cr_components/app_management/util.js'; import {BrowserProxy} from './browser_proxy.js'; import {AppManagementStoreClient} from './store_client.js'; -import {getAppIcon, getSelectedApp} from './util.js'; Polymer({ _template: html`{__html_template__}`, diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/resize_lock_item.js b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/resize_lock_item.js index dd704f8634c3e3..5cd6a8870a1958 100644 --- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/resize_lock_item.js +++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/resize_lock_item.js @@ -1,16 +1,16 @@ // Copyright 2021 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import './toggle_row.js'; +import '//resources/cr_components/app_management/toggle_row.js'; +import {AppManagementUserAction} from '//resources/cr_components/app_management/constants.js'; import {assert, assertNotReached} from '//resources/js/assert.m.js'; import {afterNextRender, flush, html, Polymer, TemplateInstanceBase, Templatizer} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js'; +import {recordAppManagementUserAction} from 'chrome://resources/cr_components/app_management/util.js'; import {recordClick, recordNavigation, recordPageBlur, recordPageFocus, recordSearch, recordSettingChange, setUserActionRecorderForTesting} from '../../metrics_recorder.m.js'; import {BrowserProxy} from './browser_proxy.js'; -import {AppManagementUserAction} from './constants.js'; -import {recordAppManagementUserAction} from './util.js'; Polymer({ _template: html`{__html_template__}`, diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/store.js b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/store.js index 0a862359a67126..544102e7ceea47 100644 --- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/store.js +++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/store.js @@ -2,11 +2,11 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import {createEmptyState} from 'chrome://resources/cr_components/app_management/util.js'; import {addSingletonGetter} from 'chrome://resources/js/cr.m.js'; import {Store} from 'chrome://resources/js/cr/ui/store.js'; import {reduceAction} from './reducers.js'; -import {createEmptyState} from './util.js'; /** * @fileoverview A singleton datastore for the App Management page. Page state diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/supported_links_item.js b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/supported_links_item.js index 64e3d22e08530e..bcd15c5dae8e65 100644 --- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/supported_links_item.js +++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/supported_links_item.js @@ -8,18 +8,18 @@ import '//resources/cr_components/chromeos/localized_link/localized_link.js'; import '//resources/cr_elements/cr_radio_button/cr_radio_button.m.js'; import '//resources/cr_elements/cr_radio_group/cr_radio_group.m.js'; +import {AppManagementUserAction, AppType} from '//resources/cr_components/app_management/constants.js'; import {assert} from '//resources/js/assert.m.js'; import {focusWithoutInk} from '//resources/js/cr/ui/focus_without_ink.m.js'; import {html, Polymer} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js'; +import {recordAppManagementUserAction} from 'chrome://resources/cr_components/app_management/util.js'; import {I18nBehavior} from 'chrome://resources/js/i18n_behavior.m.js'; import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js'; import {recordSettingChange} from '../../metrics_recorder.m.js'; import {BrowserProxy} from './browser_proxy.js'; -import {AppManagementUserAction, AppType} from './constants.js'; import {AppManagementStoreClient} from './store_client.js'; -import {recordAppManagementUserAction} from './util.js'; const PREFERRED_APP_PREF = 'preferred'; diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/uninstall_button.js b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/uninstall_button.js index e279bbf9efdd5d..92157943a10270 100644 --- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/uninstall_button.js +++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/uninstall_button.js @@ -2,19 +2,19 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import './shared_style.js'; +import '//resources/cr_components/app_management/shared_style.js'; import '//resources/cr_elements/cr_button/cr_button.m.js'; import '//resources/cr_elements/policy/cr_tooltip_icon.m.js'; +import {AppManagementUserAction, InstallReason} from '//resources/cr_components/app_management/constants.js'; import {assert, assertNotReached} from '//resources/js/assert.m.js'; import {afterNextRender, flush, html, Polymer, TemplateInstanceBase, Templatizer} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js'; +import {getSelectedApp, recordAppManagementUserAction} from 'chrome://resources/cr_components/app_management/util.js'; import {recordClick, recordNavigation, recordPageBlur, recordPageFocus, recordSearch, recordSettingChange, setUserActionRecorderForTesting} from '../../metrics_recorder.m.js'; import {BrowserProxy} from './browser_proxy.js'; -import {AppManagementUserAction, InstallReason} from './constants.js'; import {AppManagementStoreClient} from './store_client.js'; -import {getSelectedApp, recordAppManagementUserAction} from './util.js'; Polymer({ _template: html`{__html_template__}`, diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/util.js b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/util.js index 428591b29820e7..d6f7c1dac5241f 100644 --- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/util.js +++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/util.js @@ -2,159 +2,8 @@ // 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.m.js'; -import {assertNotReached} from 'chrome://resources/js/assert.m.js'; -import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js'; - import {Route, Router} from '../../../router.js'; import {routes} from '../../os_route.m.js'; -import {PermissionType, PermissionValue, TriState} from '../permission_constants.js'; -import {getBoolPermissionValue, getTriStatePermissionValue, isPermissionEnabled} from '../permission_util.js'; - -import {AppManagementUserAction, AppType, OptionalBool, WindowMode} from './constants.js'; - - -/** - * @fileoverview Utility functions for the App Management page. - */ - -/** - * @return {!AppManagementPageState} - */ -export function createEmptyState() { - return { - apps: {}, - selectedAppId: null, - }; -} - -/** - * @param {!Array} apps - * @return {!AppManagementPageState} - */ -export function createInitialState(apps) { - const initialState = createEmptyState(); - - for (const app of apps) { - initialState.apps[app.id] = app; - } - - return initialState; -} - -/** - * @param {App} app - * @return {string} - */ -export function getAppIcon(app) { - return `chrome://app-icon/${app.id}/64`; -} - -/** - * If the given value is not in the set, returns a new set with the value - * added, otherwise returns the old set. - * @template T - * @param {!Set} set - * @param {T} value - * @return {!Set} - */ -export function addIfNeeded(set, value) { - if (!set.has(value)) { - set = new Set(set); - set.add(value); - } - return set; -} - -/** - * If the given value is in the set, returns a new set without the value, - * otherwise returns the old set. - * @template T - * @param {!Set} set - * @param {T} value - * @return {!Set} - */ -export function removeIfNeeded(set, value) { - if (set.has(value)) { - set = new Set(set); - set.delete(value); - } - return set; -} - -/** - * @param {App} app - * @param {string} permissionType - * @return {boolean} - */ -export function getPermissionValueBool(app, permissionType) { - const permission = getPermission(app, permissionType); - assert(permission); - - return isPermissionEnabled(permission.value); -} - -/** - * Undefined is returned when the app does not request a permission. - * - * @param {App} app - * @param {string} permissionType - * @return {Permission|undefined} - */ -export function getPermission(app, permissionType) { - return app.permissions[PermissionType[permissionType]]; -} - -/** - * @param {AppManagementPageState} state - * @return {?App} - */ -export function getSelectedApp(state) { - const selectedAppId = state.selectedAppId; - return selectedAppId ? state.apps[selectedAppId] : null; -} - -/** - * A comparator function to sort strings alphabetically. - * - * @param {string} a - * @param {string} b - */ -export function alphabeticalSort(a, b) { - return a.localeCompare(b); -} - -/** - * Toggles an OptionalBool - * - * @param {OptionalBool} bool - * @return {OptionalBool} - */ -export function toggleOptionalBool(bool) { - switch (bool) { - case OptionalBool.kFalse: - return OptionalBool.kTrue; - case OptionalBool.kTrue: - return OptionalBool.kFalse; - default: - assertNotReached(); - } -} - -/** - * @param {OptionalBool} optionalBool - * @returns {boolean} - */ -export function convertOptionalBoolToBool(optionalBool) { - switch (optionalBool) { - case OptionalBool.kTrue: - return true; - case OptionalBool.kFalse: - return false; - default: - assertNotReached(); - } -} /** * Navigates to the App Detail page. @@ -173,40 +22,3 @@ export function openAppDetailPage(appId) { export function openMainPage() { Router.getInstance().navigateTo(routes.APP_MANAGEMENT); } - -/** - * @param {AppType} appType - * @return {string} - * @private - */ -export function getUserActionHistogramNameForAppType_(appType) { - switch (appType) { - case AppType.kArc: - return 'AppManagement.AppDetailViews.ArcApp'; - case AppType.kChromeApp: - case AppType.kStandaloneBrowser: - case AppType.kStandaloneBrowserChromeApp: - // TODO(https://crbug.com/1225848): Figure out appropriate behavior for - // Lacros-hosted chrome-apps. - return 'AppManagement.AppDetailViews.ChromeApp'; - case AppType.kWeb: - return 'AppManagement.AppDetailViews.WebApp'; - case AppType.kPluginVm: - return 'AppManagement.AppDetailViews.PluginVmApp'; - case AppType.kBorealis: - return 'AppManagement.AppDetailViews.BorealisApp'; - default: - assertNotReached(); - } -} - -/** - * @param {AppType} appType - * @param {AppManagementUserAction} userAction - */ -export function recordAppManagementUserAction(appType, userAction) { - const histogram = getUserActionHistogramNameForAppType_(appType); - const enumLength = Object.keys(AppManagementUserAction).length; - chrome.metricsPrivate.recordEnumerationValue( - histogram, userAction, enumLength); -} diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_notifications_page/BUILD.gn b/chrome/browser/resources/settings/chromeos/os_apps_page/app_notifications_page/BUILD.gn index 3e27d53d31a5ea..8c5df2194c75c5 100644 --- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_notifications_page/BUILD.gn +++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_notifications_page/BUILD.gn @@ -40,10 +40,10 @@ js_library("app_notifications_subpage") { js_library("app_notification_row") { deps = [ - "../:permission_constants", - "../:permission_util", "../..:metrics_recorder.m", "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled", + "//ui/webui/resources/cr_components/app_management:permission_constants", + "//ui/webui/resources/cr_components/app_management:permission_util", ] externs_list = [ "$externs_path/metrics_private.js" ] } diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_notifications_page/app_notification_row.js b/chrome/browser/resources/settings/chromeos/os_apps_page/app_notifications_page/app_notification_row.js index 9e65c43e60ad7d..10af3c65afb487 100644 --- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_notifications_page/app_notification_row.js +++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_notifications_page/app_notification_row.js @@ -12,10 +12,10 @@ import '/app-management/safe_base_name.mojom-lite.js'; import '/app-management/types.mojom-lite.js'; import '/os_apps_page/app_notification_handler.mojom-lite.js'; +import {createBoolPermissionValue, createTriStatePermissionValue, isBoolValue, isPermissionEnabled, isTriStateValue} from '//resources/cr_components/app_management/permission_util.js'; import {html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js'; import {recordSettingChange} from '../../metrics_recorder.m.js'; -import {createBoolPermissionValue, createTriStatePermissionValue, isBoolValue, isPermissionEnabled, isTriStateValue} from '../permission_util.js'; import {getAppNotificationProvider} from './mojo_interface_provider.js'; diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/os_apps_page.js b/chrome/browser/resources/settings/chromeos/os_apps_page/os_apps_page.js index a65019bd62f4b7..caecdefad300f6 100644 --- a/chrome/browser/resources/settings/chromeos/os_apps_page/os_apps_page.js +++ b/chrome/browser/resources/settings/chromeos/os_apps_page/os_apps_page.js @@ -25,6 +25,8 @@ import './app_management_page/app_detail_view.js'; import './app_management_page/uninstall_button.js'; import '../../controls/settings_dropdown_menu.js'; +import {AppManagementEntryPoint, AppManagementEntryPointsHistogramName} from '//resources/cr_components/app_management/constants.js'; +import {getAppIcon, getSelectedApp} from '//resources/cr_components/app_management/util.js'; import {I18nBehavior} from '//resources/js/i18n_behavior.m.js'; import {afterNextRender, flush, html, Polymer, TemplateInstanceBase, Templatizer} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js'; import {assert, assertNotReached} from 'chrome://resources/js/assert.m.js'; @@ -37,9 +39,7 @@ import {PrefsBehavior} from '../prefs_behavior.js'; import {RouteObserverBehavior} from '../route_observer_behavior.js'; import {AndroidAppsBrowserProxyImpl, AndroidAppsInfo} from './android_apps_browser_proxy.js'; -import {AppManagementEntryPoint, AppManagementEntryPointsHistogramName} from './app_management_page/constants.js'; import {AppManagementStoreClient} from './app_management_page/store_client.js'; -import {getAppIcon, getSelectedApp} from './app_management_page/util.js'; import {getAppNotificationProvider} from './app_notifications_page/mojo_interface_provider.js'; /** diff --git a/chrome/browser/resources/settings/chromeos/os_settings.js b/chrome/browser/resources/settings/chromeos/os_settings.js index 92743b90fcfd65..ed985af503bedb 100644 --- a/chrome/browser/resources/settings/chromeos/os_settings.js +++ b/chrome/browser/resources/settings/chromeos/os_settings.js @@ -56,16 +56,15 @@ import './os_apps_page/app_management_page/chrome_app_detail_view.js'; import './os_apps_page/app_management_page/dom_switch.js'; import './os_apps_page/app_management_page/icons.js'; import './os_apps_page/app_management_page/main_view.js'; -import './os_apps_page/app_management_page/permission_item.js'; import './os_apps_page/app_management_page/pin_to_shelf_item.js'; import './os_apps_page/app_management_page/plugin_vm_page/plugin_vm_detail_view.js'; import './os_apps_page/app_management_page/pwa_detail_view.js'; -import './os_apps_page/app_management_page/shared_style.js'; -import './os_apps_page/app_management_page/shared_vars.js'; +import '//resources/cr_components/app_management/shared_style.js'; +import '//resources/cr_components/app_management/shared_vars.js'; import './os_apps_page/app_management_page/supported_links_overlapping_apps_dialog.js'; import './os_apps_page/app_management_page/supported_links_dialog.js'; import './os_apps_page/app_management_page/supported_links_item.js'; -import './os_apps_page/app_management_page/toggle_row.js'; +import '//resources/cr_components/app_management/toggle_row.js'; import './os_apps_page/app_management_page/uninstall_button.js'; import './os_apps_page/app_notifications_page/mojo_interface_provider.js'; import './os_apps_page/os_apps_page.js'; @@ -94,6 +93,11 @@ import './os_toolbar/os_toolbar.js'; import './parental_controls_page/parental_controls_page.js'; import './settings_scheduler_slider/settings_scheduler_slider.js'; +export {BrowserProxy as AppManagementComponentBrowserProxy} from '//resources/cr_components/app_management/browser_proxy.js'; +export {PageType, WindowMode} from '//resources/cr_components/app_management/constants.js'; +export {PermissionType, TriState} from '//resources/cr_components/app_management/permission_constants.js'; +export {createBoolPermission, createTriStatePermission, getBoolPermissionValue, isBoolValue} from '//resources/cr_components/app_management/permission_util.js'; +export {convertOptionalBoolToBool, createEmptyState, createInitialState, getPermissionValueBool} from '//resources/cr_components/app_management/util.js'; export {LifetimeBrowserProxyImpl} from '../lifetime_browser_proxy.js'; export {ProfileInfoBrowserProxyImpl} from '../people_page/profile_info_browser_proxy.js'; export {PageStatus, StatusAction, SyncBrowserProxyImpl} from '../people_page/sync_browser_proxy.js'; @@ -129,16 +133,12 @@ export {DeviceNameState, SetDeviceNameResult} from './os_about_page/device_name_ export {AndroidAppsBrowserProxyImpl} from './os_apps_page/android_apps_browser_proxy.js'; export {addApp, changeApp, removeApp, updateSelectedAppId} from './os_apps_page/app_management_page/actions.js'; export {BrowserProxy} from './os_apps_page/app_management_page/browser_proxy.js'; -export {PageType, WindowMode} from './os_apps_page/app_management_page/constants.js'; export {FakePageHandler} from './os_apps_page/app_management_page/fake_page_handler.js'; export {PluginVmBrowserProxyImpl} from './os_apps_page/app_management_page/plugin_vm_page/plugin_vm_browser_proxy.js'; export {AppState, reduceAction} from './os_apps_page/app_management_page/reducers.js'; export {AppManagementStore} from './os_apps_page/app_management_page/store.js'; export {AppManagementStoreClientImpl} from './os_apps_page/app_management_page/store_client.js'; -export {convertOptionalBoolToBool, createEmptyState, createInitialState, getPermissionValueBool} from './os_apps_page/app_management_page/util.js'; export {setAppNotificationProviderForTesting} from './os_apps_page/app_notifications_page/mojo_interface_provider.js'; -export {PermissionType, TriState} from './os_apps_page/permission_constants.js'; -export {createBoolPermission, createTriStatePermission, getBoolPermissionValue, isBoolValue} from './os_apps_page/permission_util.js'; export {osPageVisibility} from './os_page_visibility.m.js'; export {AccountManagerBrowserProxy, AccountManagerBrowserProxyImpl} from './os_people_page/account_manager_browser_proxy.js'; export {FingerprintBrowserProxyImpl, FingerprintResultType} from './os_people_page/fingerprint_browser_proxy.m.js'; diff --git a/chrome/browser/resources/tab_strip/alert_indicators.ts b/chrome/browser/resources/tab_strip/alert_indicators.ts index 5df1ad38831d57..e930c00de0ae2c 100644 --- a/chrome/browser/resources/tab_strip/alert_indicators.ts +++ b/chrome/browser/resources/tab_strip/alert_indicators.ts @@ -20,7 +20,7 @@ export class AlertIndicatorsElement extends CustomElement { constructor() { super(); - this.containerEl_ = this.$('#container') as HTMLElement; + this.containerEl_ = this.$('#container')!; const audioIndicator = new AlertIndicatorElement(); const recordingIndicator = new AlertIndicatorElement(); diff --git a/chrome/browser/resources/tab_strip/tab.ts b/chrome/browser/resources/tab_strip/tab.ts index 7c6f7bd7c305f5..ffeb6724a8f5f2 100644 --- a/chrome/browser/resources/tab_strip/tab.ts +++ b/chrome/browser/resources/tab_strip/tab.ts @@ -61,29 +61,29 @@ export class TabElement extends CustomElement { super(); this.alertIndicatorsEl_ = - this.$('tabstrip-alert-indicators') as AlertIndicatorsElement; - // Normally, custom elements will get upgraded automatically once added to - // the DOM, but TabElement may need to update properties on + this.$('tabstrip-alert-indicators')!; + // Normally, custom elements will get upgraded automatically once added + // to the DOM, but TabElement may need to update properties on // AlertIndicatorElement before this happens, so upgrade it manually. customElements.upgrade(this.alertIndicatorsEl_); - this.closeButtonEl_ = this.$('#close') as HTMLElement; + this.closeButtonEl_ = this.$('#close')!; this.closeButtonEl_.setAttribute( 'aria-label', loadTimeData.getString('closeTab')); - this.dragImageEl_ = this.$('#dragImage') as HTMLElement; + this.dragImageEl_ = this.$('#dragImage')!; - this.tabEl_ = this.$('#tab') as HTMLElement; + this.tabEl_ = this.$('#tab')!; - this.faviconEl_ = this.$('#favicon') as HTMLElement; + this.faviconEl_ = this.$('#favicon')!; - this.thumbnailContainer_ = this.$('#thumbnail') as HTMLElement; + this.thumbnailContainer_ = this.$('#thumbnail')!; - this.thumbnail_ = this.$('#thumbnailImg') as HTMLImageElement; + this.thumbnail_ = this.$('#thumbnailImg')!; this.tabsApi_ = TabsApiProxyImpl.getInstance(); - this.titleTextEl_ = this.$('#titleText') as HTMLElement; + this.titleTextEl_ = this.$('#titleText')!; /** * Flag indicating if this TabElement can accept dragover events. This diff --git a/chrome/browser/resources/tab_strip/tab_group.ts b/chrome/browser/resources/tab_strip/tab_group.ts index bb0be405606676..c2a9314008b89e 100644 --- a/chrome/browser/resources/tab_strip/tab_group.ts +++ b/chrome/browser/resources/tab_strip/tab_group.ts @@ -22,7 +22,7 @@ export class TabGroupElement extends CustomElement { this.tabsApi_ = TabsApiProxyImpl.getInstance(); - this.chip_ = this.$('#chip') as HTMLElement; + this.chip_ = this.$('#chip')!; this.chip_.addEventListener('click', () => this.onClickChip_()); this.chip_.addEventListener( 'keydown', e => this.onKeydownChip_(/** @type {!KeyboardEvent} */ (e))); @@ -43,13 +43,13 @@ export class TabGroupElement extends CustomElement { } getDragImage(): HTMLElement { - return this.$('#dragImage') as HTMLElement; + return this.$('#dragImage')!; } getDragImageCenter(): HTMLElement { // Since the drag handle is #dragHandle, the drag image should be // centered relatively to it. - return this.$('#dragHandle') as HTMLElement; + return this.$('#dragHandle')!; } private onClickChip_() { @@ -96,7 +96,7 @@ export class TabGroupElement extends CustomElement { } updateVisuals(visualData: TabGroupVisualData) { - (this.$('#title') as HTMLElement).innerText = visualData.title; + this.$('#title')!.innerText = visualData.title; this.style.setProperty('--tabstrip-tab-group-color-rgb', visualData.color); this.style.setProperty( '--tabstrip-tab-group-text-color-rgb', visualData.textColor); diff --git a/chrome/browser/resources/tab_strip/tab_list.ts b/chrome/browser/resources/tab_strip/tab_list.ts index 5dee17189aafb2..8487eb9d64ffae 100644 --- a/chrome/browser/resources/tab_strip/tab_list.ts +++ b/chrome/browser/resources/tab_strip/tab_list.ts @@ -388,13 +388,12 @@ export class TabListElement extends CustomElement implements } private findTabElement_(tabId: number): TabElement|null { - return this.$(`tabstrip-tab[data-tab-id="${tabId}"]`) as TabElement | null; + return this.$(`tabstrip-tab[data-tab-id="${tabId}"]`); } private findTabGroupElement_(groupId: string): TabGroupElement|null { - return this.$(`tabstrip-tab-group[data-group-id="${groupId}"]`) as - TabGroupElement | - null; + return this.$( + `tabstrip-tab-group[data-group-id="${groupId}"]`); } private fetchAndUpdateColors_() { @@ -403,8 +402,7 @@ export class TabListElement extends CustomElement implements } private fetchAndUpdateGroupData_() { - const tabGroupElements = - this.$all('tabstrip-tab-group') as NodeListOf; + const tabGroupElements = this.$all('tabstrip-tab-group'); this.tabsApi_.getGroupVisualData().then(({data}) => { tabGroupElements.forEach(tabGroupElement => { tabGroupElement.updateVisuals( @@ -420,7 +418,7 @@ export class TabListElement extends CustomElement implements } private getActiveTab_(): TabElement|null { - return this.$('tabstrip-tab[active]') as TabElement | null; + return this.$('tabstrip-tab[active]'); } getIndexOfTab(tabElement: TabElement): number { @@ -473,7 +471,7 @@ export class TabListElement extends CustomElement implements // document. When the tab strip first gains keyboard focus, no such event // exists yet, so the outline needs to be explicitly set to visible. this.focusOutlineManager_.visible = true; - (this.$('tabstrip-tab') as HTMLElement).focus(); + this.$('tabstrip-tab')!.focus(); } private onTabActivated_(tabId: number) { @@ -488,7 +486,7 @@ export class TabListElement extends CustomElement implements // have updated a Tab to have an active state. For example, if a // tab is created with an already active state, there may be 2 active // TabElements: the newly created tab and the previously active tab. - (this.$all('tabstrip-tab[active]') as NodeListOf) + this.$all('tabstrip-tab[active]') .forEach((previouslyActiveTab) => { if (previouslyActiveTab.tab.id !== tabId) { previouslyActiveTab.tab = /** @type {!Tab} */ ( @@ -705,7 +703,7 @@ export class TabListElement extends CustomElement implements index++; } - let elementAtIndex = this.$all('tabstrip-tab')[index]; + let elementAtIndex = this.$all('tabstrip-tab')[index]!; if (elementAtIndex && elementAtIndex.parentElement && isTabGroupElement(elementAtIndex.parentElement)) { elementAtIndex = elementAtIndex.parentElement; @@ -796,7 +794,8 @@ export class TabListElement extends CustomElement implements element, this.pinnedTabsElement_.childNodes[index]!); } else { let elementToInsert: TabElement|TabGroupElement = element; - let elementAtIndex = this.$all('tabstrip-tab').item(index); + let elementAtIndex: TabElement|TabGroupElement = + this.$all('tabstrip-tab').item(index); let parentElement = this.unpinnedTabsElement_; if (groupId) { @@ -824,7 +823,7 @@ export class TabListElement extends CustomElement implements // TabElement is being sandwiched between two TabElements in a group, it // can be assumed that the tab will eventually be inserted into the // group as well. - elementAtIndex = elementAtIndex.parentElement; + elementAtIndex = elementAtIndex.parentElement as TabGroupElement; } if (elementAtIndex && elementAtIndex.parentElement === parentElement) { diff --git a/chrome/browser/resources/tools/rollup_plugin.js b/chrome/browser/resources/tools/rollup_plugin.js index 1717bb41e5cca3..345d74325e7aff 100644 --- a/chrome/browser/resources/tools/rollup_plugin.js +++ b/chrome/browser/resources/tools/rollup_plugin.js @@ -62,6 +62,7 @@ function getPathForUrl(source, origin, urlPrefix, urlSrcPath, excludes) { } const fullUrl = new URL(pathFromUrl, urlPrefix); + console.log(fullUrl.href); if (excludes.includes(fullUrl.href)) { return fullUrl.href; } diff --git a/chrome/browser/segmentation_platform/segmentation_platform_profile_observer_unittest.cc b/chrome/browser/segmentation_platform/segmentation_platform_profile_observer_unittest.cc index 42529cffe801ce..7f5c039ff23c8f 100644 --- a/chrome/browser/segmentation_platform/segmentation_platform_profile_observer_unittest.cc +++ b/chrome/browser/segmentation_platform/segmentation_platform_profile_observer_unittest.cc @@ -8,6 +8,7 @@ #include "chrome/test/base/testing_browser_process.h" #include "chrome/test/base/testing_profile.h" #include "chrome/test/base/testing_profile_manager.h" +#include "components/segmentation_platform/public/segment_selection_result.h" #include "components/segmentation_platform/public/segmentation_platform_service.h" #include "content/public/test/browser_task_environment.h" #include "testing/gmock/include/gmock/gmock.h" @@ -29,6 +30,9 @@ class MockSegmentationPlatformService : public SegmentationPlatformService { MOCK_METHOD(void, GetSelectedSegment, (const std::string&, SegmentSelectionCallback)); + MOCK_METHOD(SegmentSelectionResult, + GetCachedSegmentResult, + (const std::string&)); MOCK_METHOD(void, EnableMetrics, (bool)); MOCK_METHOD(void, GetServiceStatus, ()); }; diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn index e621e553d760dc..124789636aef12 100644 --- a/chrome/browser/ui/BUILD.gn +++ b/chrome/browser/ui/BUILD.gn @@ -1705,7 +1705,6 @@ static_library("ui") { "//chrome/browser/ui/color:color_headers", "//chrome/browser/ui/commander:fuzzy_finder", "//chrome/browser/ui/webui/access_code_cast:mojo_bindings", - "//chrome/browser/ui/webui/app_management:mojo_bindings", "//chrome/browser/ui/webui/app_service_internals:mojo_bindings", "//chrome/browser/ui/webui/image_editor:mojo_bindings", "//chrome/browser/ui/webui/internals/user_education:mojo_bindings", @@ -1752,6 +1751,7 @@ static_library("ui") { "//ui/base/dragdrop:types", "//ui/base/dragdrop/mojom", "//ui/events", + "//ui/webui/resources/cr_components/app_management:mojo_bindings", "//ui/webui/resources/js/browser_command:mojo_bindings", ] public_deps += [ @@ -2967,7 +2967,6 @@ static_library("ui") { "//chrome/browser/ui/app_list/search/search_result_ranker:app_launch_predictor_proto", "//chrome/browser/ui/app_list/search/search_result_ranker:recurrence_ranker_proto", "//chrome/browser/ui/app_list/search/util:proto", - "//chrome/browser/ui/webui/app_management:mojo_bindings", "//chrome/browser/ui/webui/chromeos/add_supervision:mojo_bindings", "//chrome/browser/ui/webui/chromeos/audio:mojo_bindings", "//chrome/browser/ui/webui/chromeos/crostini_upgrader:mojo_bindings", @@ -3083,6 +3082,7 @@ static_library("ui") { "//ui/events/ozone/layout:layout", "//ui/file_manager:file_manager", "//ui/ozone", + "//ui/webui/resources/cr_components/app_management:mojo_bindings", ] public_deps += [ "//chrome/browser/ui/webui/chromeos/crostini_installer:mojo_bindings", diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/AutocompleteEditTextTest.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/AutocompleteEditTextTest.java index 854e45f83566d0..4b7a555811a8d2 100644 --- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/AutocompleteEditTextTest.java +++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/AutocompleteEditTextTest.java @@ -20,6 +20,7 @@ import android.view.inputmethod.BaseInputConnection; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputConnection; +import android.widget.EditText; import android.widget.LinearLayout; import org.junit.Before; @@ -74,6 +75,67 @@ public class AutocompleteEditTextTest { private ShadowAccessibilityManager mShadowAccessibilityManager; private boolean mIsShown; + /** + * A flag to tweak test expectations to deal with an OS bug. + * + *

{@code EditableInputConnection}, which {@link AutocompleteEditText} internally rely on, + * has had a bug that it still + * returns {@code true} from {@link InputConnection#endBatchEdit()} when its internal batch edit + * count becomes {@code 0} as a result of invocation, which clearly conflicted with the spec. + * There are several tests in this file that are unfortunately affected by this bug. In order + * to abstract out such an OS issue from the actual test expectations, we will dynamically test + * if the bug still exists or not in the test execution environment or not, and set {@code true} + * to this flag if it is still there.

+ * + *

Until a new version of Android OS with + * the + * fix and a corresponding version of Robolectric become available in Chromium, this flag is + * expected to be always {@code true}. Once they become available, you can either remove this + * flag with assuming it's always {@code false} or test two different OS behaviors at the same + * time by specifying multiple SDK versions to + * the test runner.

+ * + * @see #testEditableInputConnectionEndBatchEditBug(Context) + * @see #assertLastBatchEdit(boolean) + */ + private boolean mHasEditableInputConnectionEndBatchEditBug; + + /** + * Test if {@code EditableInputConnection} has a bug that it still returns {@code true} from + * {@link InputConnection#endBatchEdit()} when its internal batch edit count becomes {@code 0} + * as a result of invocation. + * + *

See https://issuetracker.google.com/issues/209958658 for details.

+ * + * @param context The {@link Context} to be used to initialize {@link EditText}. + * @return {@code true} if the bug still exists. {@code false} otherwise. + */ + private static boolean testEditableInputConnectionEndBatchEditBug(Context context) { + EditText editText = new EditText(context); + EditorInfo editorInfo = new EditorInfo(); + InputConnection editableInputConnection = editText.onCreateInputConnection(editorInfo); + editableInputConnection.beginBatchEdit(); + // If this returns true, yes, the bug is still there! + return editableInputConnection.endBatchEdit(); + } + + /** + * A convenient helper method to assert the return value of + * {@link InputConnection#endBatchEdit()} when its internal batch edit count becomes {@code 0}. + * + * @param result The return value of {@link InputConnection#endBatchEdit()}. + * + * @see #mHasEditableInputConnectionEndBatchEditBug + * @see #testEditableInputConnectionEndBatchEditBug(Context) + */ + private void assertLastBatchEdit(boolean result) { + if (mHasEditableInputConnectionEndBatchEditBug) { + assertTrue(result); + } else { + assertFalse(result); + } + } + // Limits the target of InOrder#verify. private static class Verifier { public void onAutocompleteTextStateChanged(boolean updateDisplay) { @@ -176,6 +238,9 @@ public void setUp() { MockitoAnnotations.initMocks(this); mContext = RuntimeEnvironment.application; + mHasEditableInputConnectionEndBatchEditBug = + testEditableInputConnectionEndBatchEditBug(mContext); + mVerifier = spy(new Verifier()); mAutocomplete = new TestAutocompleteEditText(mContext, null); mFocusPlaceHolder = new LinearLayout(mContext); @@ -388,7 +453,7 @@ private void internalTestAppend_CommitText() { assertVerifierCallCounts(0, 2); } mInOrder.verifyNoMoreInteractions(); - assertTrue(mInputConnection.endBatchEdit()); + assertLastBatchEdit(mInputConnection.endBatchEdit()); // Autocomplete text gets redrawn. assertTexts("hello ", "world"); @@ -533,7 +598,7 @@ private void internalTestAppend_SetComposingText() { } mInOrder.verifyNoMoreInteractions(); - assertTrue(mInputConnection.endBatchEdit()); + assertLastBatchEdit(mInputConnection.endBatchEdit()); if (isUsingSpannableModel()) { mInOrder.verify(mVerifier).onUpdateSelection(6, 6); @@ -905,7 +970,7 @@ public void testDelete_SetComposingTextInBatchEditWithSpannableModel() { mInOrder.verifyNoMoreInteractions(); assertVerifierCallCounts(0, 0); - assertTrue(mInputConnection.endBatchEdit()); + assertLastBatchEdit(mInputConnection.endBatchEdit()); mInOrder.verify(mVerifier).onUpdateSelection(4, 4); mInOrder.verify(mVerifier).onUpdateSelection(5, 5); verifyOnPopulateAccessibilityEvent( diff --git a/chrome/browser/ui/sync/profile_signin_confirmation_helper_unittest.cc b/chrome/browser/ui/sync/profile_signin_confirmation_helper_unittest.cc index 41e7f7fceb7813..90ef3931d33302 100644 --- a/chrome/browser/ui/sync/profile_signin_confirmation_helper_unittest.cc +++ b/chrome/browser/ui/sync/profile_signin_confirmation_helper_unittest.cc @@ -135,7 +135,8 @@ class ProfileSigninConfirmationHelperTest : public testing::Test { new sync_preferences::TestingPrefServiceSyncable( /*managed_prefs=*/new TestingPrefStore(), /*supervised_user_prefs=*/new TestingPrefStore(), - /*extension_prefs=*/new TestingPrefStore(), user_prefs_, + /*extension_prefs=*/new TestingPrefStore(), + /*standalone_browser_prefs=*/new TestingPrefStore(), user_prefs_, /*recommended_prefs=*/new TestingPrefStore(), new user_prefs::PrefRegistrySyncable(), new PrefNotifierImpl()); RegisterUserProfilePrefs(pref_service->registry()); diff --git a/chrome/browser/ui/views/sharing_hub/sharing_hub_icon_view.cc b/chrome/browser/ui/views/sharing_hub/sharing_hub_icon_view.cc index 204d1f26396c8d..d860a1139dd81a 100644 --- a/chrome/browser/ui/views/sharing_hub/sharing_hub_icon_view.cc +++ b/chrome/browser/ui/views/sharing_hub/sharing_hub_icon_view.cc @@ -18,11 +18,18 @@ #include "components/omnibox/browser/vector_icons.h" #include "ui/base/l10n/l10n_util.h" #include "ui/base/metadata/metadata_impl_macros.h" +#include "ui/strings/grit/ui_strings.h" namespace sharing_hub { namespace { +send_tab_to_self::SendTabToSelfBubbleController* GetSendTabToSelfController( + content::WebContents* web_contents) { + return send_tab_to_self::SendTabToSelfBubbleController:: + CreateOrGetFromWebContents(web_contents); +} + bool IsQRCodeDialogOpen(content::WebContents* web_contents) { qrcode_generator::QRCodeGeneratorBubbleController* controller = qrcode_generator::QRCodeGeneratorBubbleController::Get(web_contents); @@ -31,8 +38,7 @@ bool IsQRCodeDialogOpen(content::WebContents* web_contents) { bool IsSendTabToSelfDialogOpen(content::WebContents* web_contents) { send_tab_to_self::SendTabToSelfBubbleController* controller = - send_tab_to_self::SendTabToSelfBubbleController:: - CreateOrGetFromWebContents(web_contents); + GetSendTabToSelfController(web_contents); return controller && controller->IsBubbleShown(); } @@ -47,6 +53,9 @@ SharingHubIconView::SharingHubIconView( icon_label_bubble_delegate, page_action_icon_delegate) { SetVisible(false); + SetLabel( + l10n_util::GetStringUTF16(IDS_BROWSER_SHARING_OMNIBOX_SENDING_LABEL)); + SetUpForInOutAnimation(); } SharingHubIconView::~SharingHubIconView() = default; @@ -78,6 +87,10 @@ void SharingHubIconView::UpdateImpl() { IsSendTabToSelfDialogOpen(web_contents)) { SetHighlighted(true); } + + if (enabled) { + MaybeAnimateSendingToast(); + } } void SharingHubIconView::OnExecuting( @@ -87,10 +100,6 @@ const gfx::VectorIcon& SharingHubIconView::GetVectorIcon() const { return GetSharingHubVectorIcon(); } -bool SharingHubIconView::ShouldShowLabel() const { - return false; -} - std::u16string SharingHubIconView::GetTextForTooltipAndAccessibleName() const { return l10n_util::GetStringUTF16(IDS_SHARING_HUB_TOOLTIP); } @@ -103,6 +112,20 @@ SharingHubBubbleController* SharingHubIconView::GetController() const { return SharingHubBubbleController::CreateOrGetFromWebContents(web_contents); } +void SharingHubIconView::MaybeAnimateSendingToast() { + content::WebContents* web_contents = GetWebContents(); + if (!web_contents) { + return; + } + send_tab_to_self::SendTabToSelfBubbleController* controller = + GetSendTabToSelfController(web_contents); + + if (controller && controller->show_message()) { + controller->set_show_message(false); + AnimateIn(absl::nullopt); + } +} + BEGIN_METADATA(SharingHubIconView, PageActionIconView) END_METADATA diff --git a/chrome/browser/ui/views/sharing_hub/sharing_hub_icon_view.h b/chrome/browser/ui/views/sharing_hub/sharing_hub_icon_view.h index 2d895db57cf639..d807e9cdf8e32c 100644 --- a/chrome/browser/ui/views/sharing_hub/sharing_hub_icon_view.h +++ b/chrome/browser/ui/views/sharing_hub/sharing_hub_icon_view.h @@ -30,7 +30,6 @@ class SharingHubIconView : public PageActionIconView { // PageActionIconView: views::BubbleDialogDelegate* GetBubble() const override; void UpdateImpl() override; - bool ShouldShowLabel() const override; std::u16string GetTextForTooltipAndAccessibleName() const override; protected: @@ -40,6 +39,9 @@ class SharingHubIconView : public PageActionIconView { private: SharingHubBubbleController* GetController() const; + // Shows a "Sending..." animation if a device was selected in the send tab to + // self dialog. + void MaybeAnimateSendingToast(); }; } // namespace sharing_hub diff --git a/chrome/browser/ui/views/tabs/tab_strip_scroll_container.cc b/chrome/browser/ui/views/tabs/tab_strip_scroll_container.cc index 82b88e65f886a8..c40a55a401ebbe 100644 --- a/chrome/browser/ui/views/tabs/tab_strip_scroll_container.cc +++ b/chrome/browser/ui/views/tabs/tab_strip_scroll_container.cc @@ -9,6 +9,8 @@ #include "chrome/app/vector_icons/vector_icons.h" #include "chrome/browser/ui/views/tabs/tab_strip.h" #include "chrome/browser/ui/views/tabs/tab_strip_controller.h" +#include "chrome/grit/generated_resources.h" +#include "ui/base/l10n/l10n_util.h" #include "ui/base/metadata/metadata_header_macros.h" #include "ui/base/metadata/metadata_impl_macros.h" #include "ui/gfx/canvas.h" @@ -185,14 +187,19 @@ TabStripScrollContainer::TabStripScrollContainer( scroll_button_container->SetLayoutManager( std::make_unique()); scroll_button_layout->SetOrientation(views::LayoutOrientation::kHorizontal); + leading_scroll_button_ = scroll_button_container->AddChildView(CreateScrollButton( base::BindRepeating(&TabStripScrollContainer::ScrollTowardsLeadingTab, base::Unretained(this)))); + leading_scroll_button_->SetAccessibleName( + l10n_util::GetStringUTF16(IDS_ACCNAME_TAB_SCROLL_LEADING)); trailing_scroll_button_ = scroll_button_container->AddChildView( CreateScrollButton(base::BindRepeating( &TabStripScrollContainer::ScrollTowardsTrailingTab, base::Unretained(this)))); + trailing_scroll_button_->SetAccessibleName( + l10n_util::GetStringUTF16(IDS_ACCNAME_TAB_SCROLL_TRAILING)); // The space in dips between the scroll buttons and the NTB. constexpr int kScrollButtonsTrailingMargin = 8; diff --git a/chrome/browser/ui/web_applications/test/DEPS b/chrome/browser/ui/web_applications/test/DEPS new file mode 100644 index 00000000000000..545cfc0a0fef1c --- /dev/null +++ b/chrome/browser/ui/web_applications/test/DEPS @@ -0,0 +1,6 @@ +specific_include_rules = { + "system_web_app_interactive_uitest\.cc": [ + "+ash/shell.h", + "+ash/wm/window_util.h", + ], +} diff --git a/chrome/browser/ui/web_applications/test/system_web_app_interactive_uitest.cc b/chrome/browser/ui/web_applications/test/system_web_app_interactive_uitest.cc index adb5f90c2bd284..48379b84e4a00a 100644 --- a/chrome/browser/ui/web_applications/test/system_web_app_interactive_uitest.cc +++ b/chrome/browser/ui/web_applications/test/system_web_app_interactive_uitest.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 #include #include #include @@ -46,15 +47,26 @@ #include "ash/public/cpp/app_menu_constants.h" #include "ash/public/cpp/shelf_item_delegate.h" #include "ash/public/cpp/shelf_model.h" +#include "ash/shell.h" +#include "ash/wm/window_util.h" +#include "base/run_loop.h" +#include "base/scoped_observation.h" #include "chrome/browser/apps/app_service/app_service_proxy.h" #include "chrome/browser/apps/app_service/app_service_proxy_factory.h" +#include "chrome/browser/ash/crosapi/url_handler_ash.h" #include "chrome/browser/ash/login/login_manager_test.h" #include "chrome/browser/ash/login/test/login_manager_mixin.h" #include "chrome/browser/ash/login/ui/user_adding_screen.h" +#include "chrome/browser/ash/web_applications/os_url_handler_system_web_app_info.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/profiles/profile_manager.h" #include "chrome/browser/ui/ash/multi_user/multi_user_util.h" #include "chrome/browser/ui/ash/multi_user/multi_user_window_manager_helper.h" +#include "chrome/browser/ui/webui/chrome_web_ui_controller_factory.h" +#include "chrome/common/webui_url_constants.h" +#include "content/public/test/test_utils.h" +#include "ui/wm/public/activation_change_observer.h" // nogncheck +#include "ui/wm/public/activation_client.h" // nogncheck #endif namespace web_app { @@ -803,6 +815,145 @@ IN_PROC_BROWSER_TEST_P(SystemWebAppLaunchOmniboxNavigateBrowsertest, } #endif // BUILDFLAG(IS_CHROMEOS_ASH) +#if BUILDFLAG(IS_CHROMEOS_ASH) + +// A one shot observer which waits for an activation of any window. +class TestActivationObserver : public wm::ActivationChangeObserver { + public: + TestActivationObserver(const TestActivationObserver&) = delete; + TestActivationObserver& operator=(const TestActivationObserver&) = delete; + + TestActivationObserver() { + activation_observer_.Observe(ash::Shell::Get()->activation_client()); + } + + ~TestActivationObserver() override = default; + + void Wait() { run_loop_.Run(); } + + // wm::ActivationChangeObserver: + void OnWindowActivated(ActivationReason reason, + aura::Window* gained_active, + aura::Window* lost_active) override { + Browser* browser = chrome::FindBrowserWithWindow(gained_active); + // Check that the activated window is actually a browser. + EXPECT_TRUE(browser); + // Check also that the browser is actually an app. + EXPECT_TRUE(browser->is_type_app()); + run_loop_.Quit(); + } + + private: + // The MessageLoopRunner used to spin the message loop. + base::RunLoop run_loop_; + base::ScopedObservation + activation_observer_{this}; +}; + +// Tests which are exercising OpenUrl called by Lacros in Ash. +class SystemWebAppOpenInAshFromLacrosTests + : public SystemWebAppManagerBrowserTest { + public: + SystemWebAppOpenInAshFromLacrosTests() + : SystemWebAppManagerBrowserTest(/*install_mock=*/false) { + OsUrlHandlerSystemWebAppDelegate::EnableDelegateForTesting(true); + url_handler_ = std::make_unique(); + } + + ~SystemWebAppOpenInAshFromLacrosTests() { + OsUrlHandlerSystemWebAppDelegate::EnableDelegateForTesting(false); + } + + // A function to wait until a window activation change was observed. + void LaunchAndWaitForActivationChange(const GURL& url) { + TestActivationObserver observer; + url_handler_->OpenUrl(url); + observer.Wait(); + } + + protected: + std::unique_ptr url_handler_; +}; + +// This test will make sure that only accepted URLs will be allowed to create +// applications. +IN_PROC_BROWSER_TEST_P(SystemWebAppOpenInAshFromLacrosTests, + LaunchOnlyAllowedUrls) { + WaitForTestSystemAppInstall(); + + // There might be an initial browser from the testing framework. + int initial_browser_count = BrowserList::GetInstance()->size(); + + // Test that a non descript URL gets rejected. + GURL url1 = GURL("http://www.foo.bar"); + EXPECT_FALSE(ChromeWebUIControllerFactory::GetInstance()->CanHandleUrl(url1)); + EXPECT_FALSE(url_handler_->OpenUrlInternal(url1)); + + // Test that an unknown internal os url gets rejected. + GURL url2 = GURL("os://foo-bar"); + EXPECT_FALSE(ChromeWebUIControllerFactory::GetInstance()->CanHandleUrl(url2)); + EXPECT_FALSE(url_handler_->OpenUrlInternal(url2)); + + // Test that an unknown internal chrome url gets rejected. + GURL url3 = GURL("chrome://foo-bar"); + EXPECT_FALSE(ChromeWebUIControllerFactory::GetInstance()->CanHandleUrl(url3)); + EXPECT_FALSE(url_handler_->OpenUrlInternal(url3)); + + // Test that a known internal url gets accepted. + GURL url4 = GURL("os://version"); + EXPECT_TRUE(ChromeWebUIControllerFactory::GetInstance()->CanHandleUrl(url4)); + LaunchAndWaitForActivationChange(url4); + EXPECT_EQ(initial_browser_count + 1, BrowserList::GetInstance()->size()); + EXPECT_EQ(u"ChromeOS-URLs", ash::window_util::GetActiveWindow()->GetTitle()); +} + +// This test will make sure that opening the same system URL multiple times will +// re-use the existing app. +IN_PROC_BROWSER_TEST_P(SystemWebAppOpenInAshFromLacrosTests, + LaunchLacrosDeDuplicationtest) { + WaitForTestSystemAppInstall(); + + // There might be an initial browser from the testing framework. + int initial_browser_count = BrowserList::GetInstance()->size(); + + // Start an application which uses the OS url handler. + LaunchAndWaitForActivationChange(GURL(chrome::kOsUICreditsURL)); + EXPECT_EQ(initial_browser_count + 1, BrowserList::GetInstance()->size()); + EXPECT_EQ(u"ChromeOS-URLs", ash::window_util::GetActiveWindow()->GetTitle()); + + // Start another application. + LaunchAndWaitForActivationChange(GURL(chrome::kOsUIFlagsURL)); + EXPECT_EQ(initial_browser_count + 2, BrowserList::GetInstance()->size()); + EXPECT_EQ(u"Flags", ash::window_util::GetActiveWindow()->GetTitle()); + + // Start an application of the first type and see that no new app got created. + LaunchAndWaitForActivationChange(GURL(chrome::kOsUICreditsURL)); + EXPECT_EQ(initial_browser_count + 2, BrowserList::GetInstance()->size()); + EXPECT_EQ(u"ChromeOS-URLs", ash::window_util::GetActiveWindow()->GetTitle()); +} + +// This test will make sure that opening a different system URL (other than +// flags) will open different windows. +IN_PROC_BROWSER_TEST_P(SystemWebAppOpenInAshFromLacrosTests, + LaunchLacrosCreateNewAppForNewSystemUrl) { + WaitForTestSystemAppInstall(); + + // There might be an initial browser from the testing framework. + int initial_browser_count = BrowserList::GetInstance()->size(); + + // Start an application using the OS Url handler. + LaunchAndWaitForActivationChange(GURL(chrome::kOsUICreditsURL)); + EXPECT_EQ(initial_browser_count + 1, BrowserList::GetInstance()->size()); + EXPECT_EQ(u"ChromeOS-URLs", ash::window_util::GetActiveWindow()->GetTitle()); + + // Start another application using the OS Url handler. + LaunchAndWaitForActivationChange(GURL(chrome::kOsUIComponentsUrl)); + EXPECT_EQ(initial_browser_count + 2, BrowserList::GetInstance()->size()); + EXPECT_EQ(u"ChromeOS-URLs", ash::window_util::GetActiveWindow()->GetTitle()); +} + +#endif // BUILDFLAG(IS_CHROMEOS_ASH) + class SystemWebAppManagerCloseFromScriptsTest : public SystemWebAppManagerBrowserTest { public: @@ -979,4 +1130,8 @@ INSTANTIATE_SYSTEM_WEB_APP_MANAGER_TEST_SUITE_REGULAR_PROFILE_P( INSTANTIATE_SYSTEM_WEB_APP_MANAGER_TEST_SUITE_REGULAR_PROFILE_P( SystemWebAppNewWindowMenuItemTest); #endif +#if BUILDFLAG(IS_CHROMEOS_ASH) +INSTANTIATE_SYSTEM_WEB_APP_MANAGER_TEST_SUITE_REGULAR_PROFILE_P( + SystemWebAppOpenInAshFromLacrosTests); +#endif } // namespace web_app diff --git a/chrome/browser/ui/webui/app_management/BUILD.gn b/chrome/browser/ui/webui/app_management/BUILD.gn deleted file mode 100644 index d96d406038e1ce..00000000000000 --- a/chrome/browser/ui/webui/app_management/BUILD.gn +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright 2018 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -import("//mojo/public/tools/bindings/mojom.gni") - -mojom("mojo_bindings") { - sources = [ "app_management.mojom" ] - - public_deps = [ "//components/services/app_service/public/mojom" ] -} diff --git a/chrome/browser/ui/webui/app_management/app_management_page_handler.cc b/chrome/browser/ui/webui/app_management/app_management_page_handler.cc index 3e4477166cc787..5801a25ec1f7e0 100644 --- a/chrome/browser/ui/webui/app_management/app_management_page_handler.cc +++ b/chrome/browser/ui/webui/app_management/app_management_page_handler.cc @@ -16,7 +16,6 @@ #include "chrome/browser/apps/app_service/app_service_proxy_factory.h" #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/profiles/profile.h" -#include "chrome/browser/ui/webui/app_management/app_management.mojom.h" #include "components/services/app_service/public/cpp/app_registry_cache.h" #include "components/services/app_service/public/cpp/intent_constants.h" #include "components/services/app_service/public/cpp/intent_filter_util.h" @@ -32,6 +31,7 @@ #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/resources/cr_components/app_management/app_management.mojom.h" #if BUILDFLAG(IS_CHROMEOS_ASH) #include "ash/components/arc/session/connection_holder.h" @@ -210,6 +210,7 @@ void AppManagementPageHandler::SetPinned(const std::string& app_id, void AppManagementPageHandler::SetPermission( const std::string& app_id, apps::mojom::PermissionPtr permission) { + DLOG(ERROR) << "set permission"; apps::AppServiceProxyFactory::GetForProfile(profile_)->SetPermission( app_id, std::move(permission)); } diff --git a/chrome/browser/ui/webui/app_management/app_management_page_handler.h b/chrome/browser/ui/webui/app_management/app_management_page_handler.h index 81e50cf1e4fd4b..60047d34ff114b 100644 --- a/chrome/browser/ui/webui/app_management/app_management_page_handler.h +++ b/chrome/browser/ui/webui/app_management/app_management_page_handler.h @@ -8,7 +8,6 @@ #include "base/memory/raw_ptr.h" #include "base/scoped_observation.h" #include "build/chromeos_buildflags.h" -#include "chrome/browser/ui/webui/app_management/app_management.mojom-forward.h" #include "chrome/browser/ui/webui/app_management/app_management_shelf_delegate_chromeos.h" #include "components/services/app_service/public/cpp/app_registry_cache.h" #include "components/services/app_service/public/cpp/preferred_apps_list_handle.h" @@ -16,6 +15,7 @@ #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/resources/cr_components/app_management/app_management.mojom-forward.h" class Profile; diff --git a/chrome/browser/ui/webui/app_management/app_management_shelf_delegate_chromeos.h b/chrome/browser/ui/webui/app_management/app_management_shelf_delegate_chromeos.h index a8c16ffd8501a5..1af9a2d3a75a1a 100644 --- a/chrome/browser/ui/webui/app_management/app_management_shelf_delegate_chromeos.h +++ b/chrome/browser/ui/webui/app_management/app_management_shelf_delegate_chromeos.h @@ -9,7 +9,7 @@ #include "ash/public/cpp/shelf_model_observer.h" #include "base/memory/raw_ptr.h" -#include "chrome/browser/ui/webui/app_management/app_management.mojom.h" +#include "ui/webui/resources/cr_components/app_management/app_management.mojom.h" class AppManagementPageHandler; class ShelfControllerHelper; diff --git a/chrome/browser/ui/webui/settings/chromeos/BUILD.gn b/chrome/browser/ui/webui/settings/chromeos/BUILD.gn index 7452b0526a7d09..c86ea049cc46ce 100644 --- a/chrome/browser/ui/webui/settings/chromeos/BUILD.gn +++ b/chrome/browser/ui/webui/settings/chromeos/BUILD.gn @@ -6,7 +6,7 @@ group("mojom_js") { public_deps = [ "constants:mojom_js", "search:mojo_bindings_js", - "//chrome/browser/ui/webui/app_management:mojo_bindings_js", "//chrome/browser/ui/webui/nearby_share/public/mojom:mojom_js", + "//ui/webui/resources/cr_components/app_management:mojo_bindings_js", ] } diff --git a/chrome/browser/ui/webui/settings/chromeos/app_management/app_management_page_handler_factory.cc b/chrome/browser/ui/webui/settings/chromeos/app_management/app_management_page_handler_factory.cc index d4f851d65915de..fb66843af37315 100644 --- a/chrome/browser/ui/webui/settings/chromeos/app_management/app_management_page_handler_factory.cc +++ b/chrome/browser/ui/webui/settings/chromeos/app_management/app_management_page_handler_factory.cc @@ -11,7 +11,6 @@ #include "base/feature_list.h" #include "chrome/browser/apps/app_service/app_icon/app_icon_source.h" #include "chrome/browser/profiles/profile.h" -#include "chrome/browser/ui/webui/app_management/app_management.mojom.h" #include "chrome/browser/ui/webui/app_management/app_management_page_handler.h" #include "chrome/grit/browser_resources.h" #include "chrome/grit/chromium_strings.h" @@ -24,6 +23,7 @@ #include "mojo/public/cpp/bindings/pending_remote.h" #include "mojo/public/cpp/bindings/receiver.h" #include "ui/base/resource/resource_bundle.h" +#include "ui/webui/resources/cr_components/app_management/app_management.mojom.h" AppManagementPageHandlerFactory::AppManagementPageHandlerFactory( Profile* profile) diff --git a/chrome/browser/ui/webui/settings/chromeos/app_management/app_management_page_handler_factory.h b/chrome/browser/ui/webui/settings/chromeos/app_management/app_management_page_handler_factory.h index 948daf715b388f..38af42af97f398 100644 --- a/chrome/browser/ui/webui/settings/chromeos/app_management/app_management_page_handler_factory.h +++ b/chrome/browser/ui/webui/settings/chromeos/app_management/app_management_page_handler_factory.h @@ -7,10 +7,10 @@ #include -#include "chrome/browser/ui/webui/app_management/app_management.mojom.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 "ui/webui/resources/cr_components/app_management/app_management.mojom.h" class Profile; diff --git a/chrome/browser/ui/webui/settings/chromeos/os_settings_ui.h b/chrome/browser/ui/webui/settings/chromeos/os_settings_ui.h index fc3ecec849a55b..67ed6ed74c9e22 100644 --- a/chrome/browser/ui/webui/settings/chromeos/os_settings_ui.h +++ b/chrome/browser/ui/webui/settings/chromeos/os_settings_ui.h @@ -8,7 +8,6 @@ #include #include "base/time/time.h" -#include "chrome/browser/ui/webui/app_management/app_management.mojom-forward.h" #include "chrome/browser/ui/webui/nearby_share/nearby_share.mojom.h" #include "chrome/browser/ui/webui/nearby_share/public/mojom/nearby_share_settings.mojom.h" #include "chrome/browser/ui/webui/settings/chromeos/app_management/app_management_page_handler_factory.h" @@ -22,6 +21,7 @@ #include "mojo/public/cpp/bindings/pending_receiver.h" #include "mojo/public/cpp/bindings/self_owned_receiver.h" #include "ui/webui/mojo_web_ui_controller.h" +#include "ui/webui/resources/cr_components/app_management/app_management.mojom-forward.h" namespace user_prefs { class PrefRegistrySyncable; diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn index 82f83adf0348e4..7e90011db26f76 100644 --- a/chrome/test/BUILD.gn +++ b/chrome/test/BUILD.gn @@ -8612,7 +8612,13 @@ if (!is_android && !is_fuchsia) { "../browser/ui/views/keyboard_access_browsertest.cc", ] } - if (!is_chromeos_ash) { + if (is_chromeos_ash) { + deps += [ + "//ash:ash", + "//chrome/browser/ash/crosapi:crosapi", + "//ui/wm/public:public", + ] + } else { sources += [ "../browser/ui/views/global_media_controls/media_dialog_view_interactive_browsertest.cc" ] deps += [ "../browser/media/router:test_support", @@ -8623,7 +8629,6 @@ if (!is_android && !is_fuchsia) { if (!is_android) { sources += [ "../browser/ui/web_applications/test/system_web_app_interactive_uitest.cc" ] } - if (use_aura) { sources += [ "../browser/ui/views/tooltip/tooltip_browsertest.cc" ] } diff --git a/chrome/test/base/testing_profile.cc b/chrome/test/base/testing_profile.cc index 6abe13ed6be564..c7dadb25c65789 100644 --- a/chrome/test/base/testing_profile.cc +++ b/chrome/test/base/testing_profile.cc @@ -750,6 +750,7 @@ void TestingProfile::CreatePrefServiceForSupervisedUser() { testing_prefs_ = new sync_preferences::TestingPrefServiceSyncable( /*managed_prefs=*/new TestingPrefStore, supervised_user_pref_store_, /*extension_prefs=*/new TestingPrefStore, + /*standalone_browser_prefs=*/new TestingPrefStore, /*user_prefs=*/new TestingPrefStore, /*recommended_prefs=*/new TestingPrefStore, new user_prefs::PrefRegistrySyncable, new PrefNotifierImpl); diff --git a/chrome/test/data/webui/chromeos/diagnostics/input_list_test.js b/chrome/test/data/webui/chromeos/diagnostics/input_list_test.js index b0919b7e475670..5e73a2c535aa6a 100644 --- a/chrome/test/data/webui/chromeos/diagnostics/input_list_test.js +++ b/chrome/test/data/webui/chromeos/diagnostics/input_list_test.js @@ -4,7 +4,7 @@ import 'chrome://diagnostics/input_list.js'; -import {ConnectionType, KeyboardInfo, MechanicalLayout, NumberPadPresence, PhysicalLayout, TouchDeviceInfo, TouchDeviceType} from 'chrome://diagnostics/diagnostics_types.js'; +import {ConnectionType, KeyboardInfo, MechanicalLayout, NumberPadPresence, PhysicalLayout, TopRowKey, TouchDeviceInfo, TouchDeviceType} from 'chrome://diagnostics/diagnostics_types.js'; import {fakeKeyboards, fakeTouchDevices} from 'chrome://diagnostics/fake_data.js'; import {FakeInputDataProvider} from 'chrome://diagnostics/fake_input_data_provider.js'; import {setInputDataProviderForTesting} from 'chrome://diagnostics/mojo_interface_provider.js'; @@ -87,6 +87,12 @@ export function inputListTestSuite() { mechanicalLayout: MechanicalLayout.kUnknown, hasAssistantKey: false, numberPadPresent: NumberPadPresence.kUnknown, + topRowKeys: [ + TopRowKey.kBack, TopRowKey.kForward, TopRowKey.kRefresh, + TopRowKey.kFullscreen, TopRowKey.kOverview, + TopRowKey.kScreenBrightnessDown, TopRowKey.kScreenBrightnessUp, + TopRowKey.kVolumeMute, TopRowKey.kVolumeDown, TopRowKey.kVolumeUp + ], }; let keyboardCard; return initializeInputList() diff --git a/chrome/test/data/webui/chromeos/shimless_rma/reimaging_provisioning_page_test.js b/chrome/test/data/webui/chromeos/shimless_rma/reimaging_provisioning_page_test.js index 4b2b57be4d4d4b..78320d8a31f645 100644 --- a/chrome/test/data/webui/chromeos/shimless_rma/reimaging_provisioning_page_test.js +++ b/chrome/test/data/webui/chromeos/shimless_rma/reimaging_provisioning_page_test.js @@ -6,11 +6,19 @@ import {PromiseResolver} from 'chrome://resources/js/promise_resolver.m.js'; import {FakeShimlessRmaService} from 'chrome://shimless-rma/fake_shimless_rma_service.js'; import {setShimlessRmaServiceForTesting} from 'chrome://shimless-rma/mojo_interface_provider.js'; import {ReimagingProvisioningPage} from 'chrome://shimless-rma/reimaging_provisioning_page.js'; +import {ShimlessRma} from 'chrome://shimless-rma/shimless_rma.js'; import {ProvisioningStatus} from 'chrome://shimless-rma/shimless_rma_types.js'; import {assertDeepEquals, assertEquals, assertFalse, assertTrue} from '../../chai_assert.js'; import {flushTasks} from '../../test_util.js'; export function reimagingProvisioningPageTest() { + /** + * ShimlessRma is needed to handle the 'transition-state' event used + * when handling calibration overall progress signals. + * @type {?ShimlessRma} + */ + let shimless_rma_component = null; + /** @type {?ReimagingProvisioningPage} */ let component = null; @@ -27,6 +35,8 @@ export function reimagingProvisioningPageTest() { }); teardown(() => { + shimless_rma_component.remove(); + shimless_rma_component = null; component.remove(); component = null; service.reset(); @@ -38,6 +48,11 @@ export function reimagingProvisioningPageTest() { function initializeWaitForProvisioningPage() { assertFalse(!!component); + shimless_rma_component = + /** @type {!ShimlessRma} */ (document.createElement('shimless-rma')); + assertTrue(!!shimless_rma_component); + document.body.appendChild(shimless_rma_component); + component = /** @type {!ReimagingProvisioningPage} */ ( document.createElement('reimaging-provisioning-page')); assertTrue(!!component); @@ -103,4 +118,52 @@ export function reimagingProvisioningPageTest() { assertDeepEquals(savedResult, expectedResult); }); + + test('ProvisioningFailedBlockingRetry', async () => { + const resolver = new PromiseResolver(); + await initializeWaitForProvisioningPage(); + + const retryButton = + component.shadowRoot.querySelector('#retryProvisioningButton'); + assertTrue(retryButton.hidden); + + let callCount = 0; + service.retryProvisioning = () => { + callCount++; + return resolver.promise; + }; + service.triggerProvisioningObserver( + ProvisioningStatus.kFailedBlocking, 1.0, 0); + await flushTasks(); + + assertFalse(retryButton.hidden); + retryButton.click(); + + await flushTasks(); + assertEquals(1, callCount); + }); + + test('ProvisioningFailedNonBlockingRetry', async () => { + const resolver = new PromiseResolver(); + await initializeWaitForProvisioningPage(); + + const retryButton = + component.shadowRoot.querySelector('#retryProvisioningButton'); + assertTrue(retryButton.hidden); + + let callCount = 0; + service.retryProvisioning = () => { + callCount++; + return resolver.promise; + }; + service.triggerProvisioningObserver( + ProvisioningStatus.kFailedNonBlocking, 1.0, 0); + await flushTasks(); + + assertFalse(retryButton.hidden); + retryButton.click(); + + await flushTasks(); + assertEquals(1, callCount); + }); } diff --git a/chrome/test/data/webui/settings/chromeos/app_management/permission_item_test.js b/chrome/test/data/webui/settings/chromeos/app_management/permission_item_test.js index a6289d31ba49e8..73ede3af180d7c 100644 --- a/chrome/test/data/webui/settings/chromeos/app_management/permission_item_test.js +++ b/chrome/test/data/webui/settings/chromeos/app_management/permission_item_test.js @@ -31,12 +31,11 @@ suite('', () => { ]) }; - // Add an arc app, and make it the currently selected app. + // Add an arc app, and pass it to permissionItem. const app = await fakeHandler.addApp(null, arcOptions); - app_management.AppManagementStore.getInstance().dispatch( - app_management.actions.updateSelectedAppId(app.id)); permissionItem = document.createElement('app-management-permission-item'); + permissionItem.app_ = app; }); test('Toggle permission', async () => { @@ -44,13 +43,15 @@ suite('', () => { replaceBody(permissionItem); await fakeHandler.flushPipesForTesting(); - assertTrue(app_management.util.getPermissionValueBool( + assertTrue(getPermissionValueBool( permissionItem.app_, permissionItem.permissionType)); permissionItem.click(); await test_util.flushTasks(); await fakeHandler.flushPipesForTesting(); - assertFalse(app_management.util.getPermissionValueBool( - permissionItem.app_, permissionItem.permissionType)); + // Store gets updated permission. + const storeData = app_management.AppManagementStore.getInstance().data; + assertFalse(getPermissionValueBool( + storeData.apps[permissionItem.app_.id], permissionItem.permissionType)); }); }); diff --git a/chrome/test/data/webui/settings/chromeos/app_management/test_util.js b/chrome/test/data/webui/settings/chromeos/app_management/test_util.js index ed67cae484d5f4..27ae275fba57c1 100644 --- a/chrome/test/data/webui/settings/chromeos/app_management/test_util.js +++ b/chrome/test/data/webui/settings/chromeos/app_management/test_util.js @@ -3,7 +3,7 @@ // found in the LICENSE file. // clang-format off -// #import {BrowserProxy, FakePageHandler} from 'chrome://os-settings/chromeos/os_settings.js'; +// #import {BrowserProxy, FakePageHandler, AppManagementComponentBrowserProxy} from 'chrome://os-settings/chromeos/os_settings.js'; // #import {TestAppManagementStore} from './test_store.m.js'; // clang-format on @@ -28,6 +28,9 @@ browserProxy.callbackRouter.$.bindNewPipeAndPassRemote()); browserProxy.handler = fakeHandler.getRemote(); + const componentBrowserProxy = + AppManagementComponentBrowserProxy.getInstance(); + componentBrowserProxy.handler = fakeHandler; return fakeHandler; } @@ -120,4 +123,4 @@ async function navigateTo(route) { } // The element is rendered and display !== 'none' return false; -} \ No newline at end of file +} diff --git a/chromeos/chromeos_strings.grd b/chromeos/chromeos_strings.grd index d58b1c8e9ce3d5..22a5581f94b7a8 100644 --- a/chromeos/chromeos_strings.grd +++ b/chromeos/chromeos_strings.grd @@ -2264,6 +2264,9 @@ Try tapping the mic to ask me anything. Failed, non blocking. + + Retry provisioning + Repair completed diff --git a/chromeos/profiles/orderfile.newest.txt b/chromeos/profiles/orderfile.newest.txt index 05233f5313e14d..ef4ad5dc450551 100644 --- a/chromeos/profiles/orderfile.newest.txt +++ b/chromeos/profiles/orderfile.newest.txt @@ -1 +1 @@ -chromeos-chrome-orderfile-field-98-4744.1-1639393599-benchmark-98.0.4758.7-r1.orderfile.xz +chromeos-chrome-orderfile-field-98-4744.1-1639393599-benchmark-98.0.4758.8-r1.orderfile.xz diff --git a/chromeos/services/bluetooth_config/BUILD.gn b/chromeos/services/bluetooth_config/BUILD.gn index 1e23d453bea3ad..c8ffa64f883c4a 100644 --- a/chromeos/services/bluetooth_config/BUILD.gn +++ b/chromeos/services/bluetooth_config/BUILD.gn @@ -157,6 +157,7 @@ source_set("unit_tests") { "//ash/constants", "//base", "//base/test:test_support", + "//chromeos/services/bluetooth_config/public/cpp:unit_tests", "//components/session_manager/core", "//components/sync_preferences:test_support", "//components/user_manager:test_support", diff --git a/chromeos/services/bluetooth_config/device_pairing_handler.cc b/chromeos/services/bluetooth_config/device_pairing_handler.cc index 39002a0b0b9a22..878c39d168bb5c 100644 --- a/chromeos/services/bluetooth_config/device_pairing_handler.cc +++ b/chromeos/services/bluetooth_config/device_pairing_handler.cc @@ -6,7 +6,10 @@ #include "base/strings/strcat.h" #include "base/strings/string_number_conversions.h" +#include "base/time/default_clock.h" #include "components/device_event_log/device_event_log.h" +#include "device/bluetooth/bluetooth_common.h" +#include "device/bluetooth/chromeos/bluetooth_utils.h" namespace chromeos { namespace bluetooth_config { @@ -29,6 +32,47 @@ std::string PasskeyToString(uint32_t passkey) { passkey_string}); } +device::BluetoothTransport GetBluetoothTransport( + device::BluetoothTransport type) { + switch (type) { + case device::BLUETOOTH_TRANSPORT_CLASSIC: + return device::BLUETOOTH_TRANSPORT_CLASSIC; + case device::BLUETOOTH_TRANSPORT_LE: + return device::BLUETOOTH_TRANSPORT_LE; + case device::BLUETOOTH_TRANSPORT_DUAL: + return device::BLUETOOTH_TRANSPORT_DUAL; + default: + return device::BLUETOOTH_TRANSPORT_INVALID; + } +} + +mojom::PairingResult GetPairingResult( + absl::optional failure_reason) { + if (!failure_reason) { + return mojom::PairingResult::kSuccess; + } + + switch (failure_reason.value()) { + case device::ConnectionFailureReason::kAuthTimeout: + FALLTHROUGH; + case device::ConnectionFailureReason::kAuthFailed: + return mojom::PairingResult::kAuthFailed; + + case device::ConnectionFailureReason::kUnknownError: + FALLTHROUGH; + case device::ConnectionFailureReason::kSystemError: + FALLTHROUGH; + case device::ConnectionFailureReason::kFailed: + FALLTHROUGH; + case device::ConnectionFailureReason::kUnknownConnectionError: + FALLTHROUGH; + case device::ConnectionFailureReason::kUnsupportedDevice: + FALLTHROUGH; + case device::ConnectionFailureReason::kNotConnectable: + return mojom::PairingResult::kNonAuthFailure; + } +} + } // namespace DevicePairingHandler::DevicePairingHandler( @@ -50,7 +94,7 @@ void DevicePairingHandler::CancelPairing() { << "Could not cancel pairing for device to due device no longer being " "found, identifier: " << current_pairing_device_id_; - FinishCurrentPairingRequest(mojom::PairingResult::kAuthFailed); + FinishCurrentPairingRequest(device::ConnectionFailureReason::kAuthFailed); return; } device->CancelPairing(); @@ -72,6 +116,7 @@ void DevicePairingHandler::PairDevice( // There should only be one PairDevice request at a time. CHECK(current_pairing_device_id_.empty()); + pairing_start_timestamp_ = base::Time(); pair_device_callback_ = std::move(callback); delegate_.reset(); @@ -84,7 +129,7 @@ void DevicePairingHandler::PairDevice( BLUETOOTH_LOG(ERROR) << "Pairing failed due to Bluetooth not being " "enabled, device identifier: " << device_id; - FinishCurrentPairingRequest(mojom::PairingResult::kNonAuthFailure); + FinishCurrentPairingRequest(device::ConnectionFailureReason::kFailed); return; } @@ -95,7 +140,7 @@ void DevicePairingHandler::PairDevice( BLUETOOTH_LOG(ERROR) << "Pairing failed due to device not being " "found, identifier: " << device_id; - FinishCurrentPairingRequest(mojom::PairingResult::kNonAuthFailure); + FinishCurrentPairingRequest(device::ConnectionFailureReason::kFailed); return; } @@ -168,7 +213,7 @@ void DevicePairingHandler::OnAdapterStateChanged() { void DevicePairingHandler::OnDeviceConnect( absl::optional error_code) { if (!error_code.has_value()) { - FinishCurrentPairingRequest(mojom::PairingResult::kSuccess); + FinishCurrentPairingRequest(absl::nullopt); NotifyFinished(); return; } @@ -182,21 +227,26 @@ void DevicePairingHandler::OnDeviceConnect( case ErrorCode::ERROR_AUTH_FAILED: FALLTHROUGH; case ErrorCode::ERROR_AUTH_REJECTED: - FALLTHROUGH; + FinishCurrentPairingRequest(device::ConnectionFailureReason::kAuthFailed); + return; case ErrorCode::ERROR_AUTH_TIMEOUT: - FinishCurrentPairingRequest(mojom::PairingResult::kAuthFailed); + FinishCurrentPairingRequest( + device::ConnectionFailureReason::kAuthTimeout); return; case ErrorCode::ERROR_FAILED: FALLTHROUGH; case ErrorCode::ERROR_INPROGRESS: - FALLTHROUGH; + FinishCurrentPairingRequest(device::ConnectionFailureReason::kFailed); + return; case ErrorCode::ERROR_UNKNOWN: - FALLTHROUGH; + FinishCurrentPairingRequest( + device::ConnectionFailureReason::kUnknownError); + return; case ErrorCode::ERROR_UNSUPPORTED_DEVICE: - FinishCurrentPairingRequest(mojom::PairingResult::kNonAuthFailure); + FinishCurrentPairingRequest( + device::ConnectionFailureReason::kUnsupportedDevice); return; - default: BLUETOOTH_LOG(ERROR) << "Error code is invalid."; break; @@ -210,7 +260,7 @@ void DevicePairingHandler::OnRequestPinCode(const std::string& pin_code) { << "OnRequestPinCode failed due to device no longer being " "found, identifier: " << current_pairing_device_id_; - FinishCurrentPairingRequest(mojom::PairingResult::kNonAuthFailure); + FinishCurrentPairingRequest(device::ConnectionFailureReason::kFailed); return; } @@ -224,7 +274,7 @@ void DevicePairingHandler::OnRequestPasskey(const std::string& passkey) { << "OnRequestPasskey failed due to device no longer being " "found, identifier: " << current_pairing_device_id_; - FinishCurrentPairingRequest(mojom::PairingResult::kNonAuthFailure); + FinishCurrentPairingRequest(device::ConnectionFailureReason::kFailed); return; } @@ -245,7 +295,7 @@ void DevicePairingHandler::OnConfirmPairing(bool confirmed) { << "OnConfirmPairing failed due to device no longer being " "found, identifier: " << current_pairing_device_id_; - FinishCurrentPairingRequest(mojom::PairingResult::kNonAuthFailure); + FinishCurrentPairingRequest(device::ConnectionFailureReason::kFailed); return; } @@ -256,9 +306,19 @@ void DevicePairingHandler::OnConfirmPairing(bool confirmed) { } void DevicePairingHandler::FinishCurrentPairingRequest( - mojom::PairingResult result) { + absl::optional failure_reason) { + device::BluetoothDevice* device = FindDevice(current_pairing_device_id_); current_pairing_device_id_.clear(); - std::move(pair_device_callback_).Run(result); + + device::BluetoothTransport transport = + device ? device->GetType() + : device::BluetoothTransport::BLUETOOTH_TRANSPORT_INVALID; + + device::RecordPairingResult( + failure_reason, GetBluetoothTransport(transport), + base::DefaultClock::GetInstance()->Now() - pairing_start_timestamp_); + + std::move(pair_device_callback_).Run(GetPairingResult(failure_reason)); } void DevicePairingHandler::OnDelegateDisconnect() { diff --git a/chromeos/services/bluetooth_config/device_pairing_handler.h b/chromeos/services/bluetooth_config/device_pairing_handler.h index 61e557208ed18f..c9d0e42a5c9c88 100644 --- a/chromeos/services/bluetooth_config/device_pairing_handler.h +++ b/chromeos/services/bluetooth_config/device_pairing_handler.h @@ -11,6 +11,7 @@ #include "chromeos/services/bluetooth_config/adapter_state_controller.h" #include "chromeos/services/bluetooth_config/public/mojom/cros_bluetooth_config.mojom.h" #include "device/bluetooth/bluetooth_device.h" +#include "device/bluetooth/chromeos/bluetooth_utils.h" #include "mojo/public/cpp/bindings/remote.h" namespace chromeos { @@ -86,9 +87,10 @@ class DevicePairingHandler : public mojom::DevicePairingHandler, void OnRequestPasskey(const std::string& passkey); void OnConfirmPairing(bool confirmed); - // Invokes |pair_device_callback_| with |result| and resets - // this class' state to be ready for another pairing request. - void FinishCurrentPairingRequest(mojom::PairingResult result); + // Invokes |pair_device_callback_| and resets this class' state to be ready + // for another pairing request. + void FinishCurrentPairingRequest( + absl::optional failure_reason); void OnDelegateDisconnect(); @@ -97,6 +99,8 @@ class DevicePairingHandler : public mojom::DevicePairingHandler, // Flushes queued Mojo messages in unit tests. void FlushForTesting(); + base::Time pairing_start_timestamp_; + // The identifier of the device currently being paired with. This is null if // there is no in-progress pairing attempt. std::string current_pairing_device_id_; diff --git a/chromeos/services/bluetooth_config/device_pairing_handler_impl_unittest.cc b/chromeos/services/bluetooth_config/device_pairing_handler_impl_unittest.cc index 2cd20d4831a070..171f03921d9693 100644 --- a/chromeos/services/bluetooth_config/device_pairing_handler_impl_unittest.cc +++ b/chromeos/services/bluetooth_config/device_pairing_handler_impl_unittest.cc @@ -6,10 +6,12 @@ #include "base/strings/strcat.h" #include "base/strings/string_number_conversions.h" +#include "base/test/metrics/histogram_tester.h" #include "base/test/task_environment.h" #include "chromeos/services/bluetooth_config/fake_adapter_state_controller.h" #include "chromeos/services/bluetooth_config/fake_device_pairing_delegate.h" #include "chromeos/services/bluetooth_config/fake_key_entered_handler.h" +#include "device/bluetooth/chromeos/bluetooth_utils.h" #include "device/bluetooth/test/mock_bluetooth_adapter.h" #include "device/bluetooth/test/mock_bluetooth_device.h" #include "testing/gmock/include/gmock/gmock.h" @@ -70,6 +72,18 @@ class DevicePairingHandlerImplTest : public testing::Test { void DestroyHandler() { device_pairing_handler_.reset(); } + void CheckPairingHistograms(device::BluetoothTransportType type, + int type_count, + int failure_count, + int success_count) { + histogram_tester.ExpectBucketCount( + "Bluetooth.ChromeOS.Pairing.TransportType", type, type_count); + histogram_tester.ExpectBucketCount("Bluetooth.ChromeOS.Pairing.Result", + false, failure_count); + histogram_tester.ExpectBucketCount("Bluetooth.ChromeOS.Pairing.Result", + true, success_count); + } + void AddDevice(std::string* id_out, AuthType auth_type, uint32_t passkey = kDefaultPinCodeNum) { @@ -141,6 +155,10 @@ class DevicePairingHandlerImplTest : public testing::Test { .WillByDefault(testing::Invoke( [this](const uint32_t passkey) { received_passkey_ = passkey; })); + ON_CALL(*mock_device, GetType()).WillByDefault(testing::Invoke([]() { + return device::BluetoothTransport::BLUETOOTH_TRANSPORT_CLASSIC; + })); + mock_devices_.push_back(std::move(mock_device)); } @@ -200,6 +218,7 @@ class DevicePairingHandlerImplTest : public testing::Test { } std::string received_pin_code() const { return received_pin_code_; } uint32_t received_passkey() const { return received_passkey_; } + base::HistogramTester histogram_tester; private: std::vector GetMockDevices() { @@ -256,6 +275,10 @@ TEST_F(DevicePairingHandlerImplTest, MultipleDevicesPairAuthNone) { EXPECT_EQ(pairing_result(), mojom::PairingResult::kNonAuthFailure); EXPECT_EQ(num_pairing_attempt_finished_calls(), 0u); + CheckPairingHistograms(device::BluetoothTransportType::kClassic, + /*type_count=*/1, /*failure_count=*/1, + /*success_count=*/0); + std::unique_ptr delegate2 = PairDevice(device_id2); EXPECT_TRUE(delegate2->IsMojoPipeConnected()); @@ -263,6 +286,10 @@ TEST_F(DevicePairingHandlerImplTest, MultipleDevicesPairAuthNone) { InvokePendingConnectCallback(/*success=*/true); EXPECT_EQ(pairing_result(), mojom::PairingResult::kSuccess); EXPECT_EQ(num_pairing_attempt_finished_calls(), 1u); + + CheckPairingHistograms(device::BluetoothTransportType::kClassic, + /*type_count=*/2, /*failure_count=*/1, + /*success_count=*/1); } TEST_F(DevicePairingHandlerImplTest, DisableBluetoothBeforePairing) { @@ -278,6 +305,11 @@ TEST_F(DevicePairingHandlerImplTest, DisableBluetoothBeforePairing) { EXPECT_FALSE(HasPendingConnectCallback()); EXPECT_EQ(pairing_result(), mojom::PairingResult::kNonAuthFailure); EXPECT_EQ(num_pairing_attempt_finished_calls(), 0u); + + // Pairing result metric is only recorded for valid transport types. + CheckPairingHistograms(device::BluetoothTransportType::kInvalid, + /*type_count=*/1, /*failure_count=*/0, + /*success_count=*/0); } TEST_F(DevicePairingHandlerImplTest, DisableBluetoothDuringPairing) { @@ -295,6 +327,9 @@ TEST_F(DevicePairingHandlerImplTest, DisableBluetoothDuringPairing) { EXPECT_EQ(num_cancel_pairing_calls(), 1u); EXPECT_EQ(pairing_result(), mojom::PairingResult::kAuthFailed); EXPECT_EQ(num_pairing_attempt_finished_calls(), 0u); + CheckPairingHistograms(device::BluetoothTransportType::kClassic, + /*type_count=*/1, /*failure_count=*/1, + /*success_count=*/0); } TEST_F(DevicePairingHandlerImplTest, DestroyHandlerBeforeConnectFinishes) { @@ -311,6 +346,9 @@ TEST_F(DevicePairingHandlerImplTest, DestroyHandlerBeforeConnectFinishes) { // Destroying the handler should call OnPairingAttemptFinished(); EXPECT_EQ(num_pairing_attempt_finished_calls(), 1u); + CheckPairingHistograms(device::BluetoothTransportType::kClassic, + /*type_count=*/1, /*failure_count=*/1, + /*success_count=*/0); } TEST_F(DevicePairingHandlerImplTest, DestroyHandlerAfterConnectFinishes) { @@ -332,6 +370,9 @@ TEST_F(DevicePairingHandlerImplTest, DestroyHandlerAfterConnectFinishes) { // Destroying the handler shouldn't call OnPairingAttemptFinished(); EXPECT_EQ(num_pairing_attempt_finished_calls(), 1u); + CheckPairingHistograms(device::BluetoothTransportType::kClassic, + /*type_count=*/1, /*failure_count=*/0, + /*success_count=*/1); } TEST_F(DevicePairingHandlerImplTest, DisconnectDelegateBeforeConnectFinishes) { @@ -349,6 +390,9 @@ TEST_F(DevicePairingHandlerImplTest, DisconnectDelegateBeforeConnectFinishes) { // Disconnecting the pipe should not call OnPairingAttemptFinished(). EXPECT_EQ(num_pairing_attempt_finished_calls(), 0u); + CheckPairingHistograms(device::BluetoothTransportType::kClassic, + /*type_count=*/1, /*failure_count=*/1, + /*success_count=*/0); } TEST_F(DevicePairingHandlerImplTest, @@ -369,6 +413,9 @@ TEST_F(DevicePairingHandlerImplTest, // Disconnecting the pipe should not call OnPairingAttemptFinished(). EXPECT_EQ(num_pairing_attempt_finished_calls(), 0u); + CheckPairingHistograms(device::BluetoothTransportType::kInvalid, + /*type_count=*/1, /*failure_count=*/0, + /*success_count=*/0); } TEST_F(DevicePairingHandlerImplTest, @@ -389,6 +436,9 @@ TEST_F(DevicePairingHandlerImplTest, // Disconnecting the pipe should not call OnPairingAttemptFinished(). EXPECT_EQ(num_pairing_attempt_finished_calls(), 0u); + CheckPairingHistograms(device::BluetoothTransportType::kClassic, + /*type_count=*/1, /*failure_count=*/1, + /*success_count=*/0); } TEST_F(DevicePairingHandlerImplTest, PairDeviceNotFound) { @@ -396,6 +446,9 @@ TEST_F(DevicePairingHandlerImplTest, PairDeviceNotFound) { EXPECT_FALSE(HasPendingConnectCallback()); EXPECT_EQ(pairing_result(), mojom::PairingResult::kNonAuthFailure); + CheckPairingHistograms(device::BluetoothTransportType::kInvalid, + /*type_count=*/1, /*failure_count=*/0, + /*success_count=*/0); } TEST_F(DevicePairingHandlerImplTest, PairAuthRequestPinCode) { @@ -410,6 +463,9 @@ TEST_F(DevicePairingHandlerImplTest, PairAuthRequestPinCode) { InvokePendingConnectCallback(/*success=*/false); EXPECT_EQ(pairing_result(), mojom::PairingResult::kAuthFailed); + CheckPairingHistograms(device::BluetoothTransportType::kClassic, + /*type_count=*/1, /*failure_count=*/1, + /*success_count=*/0); } TEST_F(DevicePairingHandlerImplTest, PairAuthRequestPinCodeRemoveDevice) { @@ -427,6 +483,9 @@ TEST_F(DevicePairingHandlerImplTest, PairAuthRequestPinCodeRemoveDevice) { // Failure result should be returned. EXPECT_TRUE(received_pin_code().empty()); EXPECT_EQ(pairing_result(), mojom::PairingResult::kNonAuthFailure); + CheckPairingHistograms(device::BluetoothTransportType::kInvalid, + /*type_count=*/1, /*failure_count=*/0, + /*success_count=*/0); } TEST_F(DevicePairingHandlerImplTest, PairAuthRequestPasskey) { @@ -441,6 +500,9 @@ TEST_F(DevicePairingHandlerImplTest, PairAuthRequestPasskey) { InvokePendingConnectCallback(/*success=*/false); EXPECT_EQ(pairing_result(), mojom::PairingResult::kAuthFailed); + CheckPairingHistograms(device::BluetoothTransportType::kClassic, + /*type_count=*/1, /*failure_count=*/1, + /*success_count=*/0); } TEST_F(DevicePairingHandlerImplTest, PairAuthRequestPasskeyRemoveDevice) { @@ -458,6 +520,9 @@ TEST_F(DevicePairingHandlerImplTest, PairAuthRequestPasskeyRemoveDevice) { // Failure result should be returned. EXPECT_EQ(received_passkey(), kUninitializedPasskey); EXPECT_EQ(pairing_result(), mojom::PairingResult::kNonAuthFailure); + CheckPairingHistograms(device::BluetoothTransportType::kInvalid, + /*type_count=*/1, /*failure_count=*/0, + /*success_count=*/0); } TEST_F(DevicePairingHandlerImplTest, PairAuthRequestPasskeyInvalidKey) { @@ -479,6 +544,9 @@ TEST_F(DevicePairingHandlerImplTest, PairAuthRequestPasskeyInvalidKey) { // CancelPairing() should not be called again since we already cancelled the // pairing. EXPECT_EQ(num_cancel_pairing_calls(), 1u); + CheckPairingHistograms(device::BluetoothTransportType::kClassic, + /*type_count=*/1, /*failure_count=*/1, + /*success_count=*/0); } TEST_F(DevicePairingHandlerImplTest, PairAuthDisplayPinCode) { @@ -497,6 +565,9 @@ TEST_F(DevicePairingHandlerImplTest, PairAuthDisplayPinCode) { InvokePendingConnectCallback(/*success=*/false); EXPECT_EQ(pairing_result(), mojom::PairingResult::kAuthFailed); + CheckPairingHistograms(device::BluetoothTransportType::kClassic, + /*type_count=*/1, /*failure_count=*/1, + /*success_count=*/0); } TEST_F(DevicePairingHandlerImplTest, PairAuthDisplayPinCodeDisconnectHandler) { @@ -515,6 +586,11 @@ TEST_F(DevicePairingHandlerImplTest, PairAuthDisplayPinCodeDisconnectHandler) { EnterKeys(device_id, /*num_keys_entered=*/6u); // Number of keys entered should still be zero since pipe is disconnected. EXPECT_EQ(delegate->key_entered_handler()->num_keys_entered(), 0); + + // Metrics is not recorded because pairing did not finish. + CheckPairingHistograms(device::BluetoothTransportType::kInvalid, + /*type_count=*/0, /*failure_count=*/0, + /*success_count=*/0); } TEST_F(DevicePairingHandlerImplTest, PairAuthDisplayPasskey) { @@ -533,6 +609,9 @@ TEST_F(DevicePairingHandlerImplTest, PairAuthDisplayPasskey) { InvokePendingConnectCallback(/*success=*/false); EXPECT_EQ(pairing_result(), mojom::PairingResult::kAuthFailed); + CheckPairingHistograms(device::BluetoothTransportType::kClassic, + /*type_count=*/1, /*failure_count=*/1, + /*success_count=*/0); } TEST_F(DevicePairingHandlerImplTest, PairAuthDisplayPasskeyPadZeroes) { @@ -547,6 +626,9 @@ TEST_F(DevicePairingHandlerImplTest, PairAuthDisplayPasskeyPadZeroes) { InvokePendingConnectCallback(/*success=*/false); EXPECT_EQ(pairing_result(), mojom::PairingResult::kAuthFailed); + CheckPairingHistograms(device::BluetoothTransportType::kClassic, + /*type_count=*/1, /*failure_count=*/1, + /*success_count=*/0); // Pair a new device. std::string device_id2; @@ -557,6 +639,10 @@ TEST_F(DevicePairingHandlerImplTest, PairAuthDisplayPasskeyPadZeroes) { // Passkey displayed should be a 6-digit number, padded with zeroes if needed. EXPECT_EQ(delegate2->displayed_passkey(), "000000"); EXPECT_TRUE(delegate2->key_entered_handler()->IsMojoPipeConnected()); + // Expect value to be 1 since pairing was not finished. + CheckPairingHistograms(device::BluetoothTransportType::kClassic, + /*type_count=*/1, /*failure_count=*/1, + /*success_count=*/0); } TEST_F(DevicePairingHandlerImplTest, PairAuthConfirmPasskey) { @@ -574,6 +660,9 @@ TEST_F(DevicePairingHandlerImplTest, PairAuthConfirmPasskey) { InvokePendingConnectCallback(/*success=*/false); EXPECT_EQ(pairing_result(), mojom::PairingResult::kAuthFailed); + CheckPairingHistograms(device::BluetoothTransportType::kClassic, + /*type_count=*/1, /*failure_count=*/1, + /*success_count=*/0); // Pair a new device. std::string device_id2; @@ -592,6 +681,9 @@ TEST_F(DevicePairingHandlerImplTest, PairAuthConfirmPasskey) { // ConfirmPairing() should not be called again, CancelPairing() should. EXPECT_EQ(num_confirm_pairing_calls(), 1u); EXPECT_EQ(num_cancel_pairing_calls(), 1u); + CheckPairingHistograms(device::BluetoothTransportType::kClassic, + /*type_count=*/2, /*failure_count=*/2, + /*success_count=*/0); } TEST_F(DevicePairingHandlerImplTest, PairAuthConfirmPasskeyRemoveDevice) { @@ -611,6 +703,9 @@ TEST_F(DevicePairingHandlerImplTest, PairAuthConfirmPasskeyRemoveDevice) { // Failure result should be returned. EXPECT_EQ(num_confirm_pairing_calls(), 0u); EXPECT_EQ(pairing_result(), mojom::PairingResult::kNonAuthFailure); + CheckPairingHistograms(device::BluetoothTransportType::kInvalid, + /*type_count=*/1, /*failure_count=*/0, + /*success_count=*/0); } TEST_F(DevicePairingHandlerImplTest, PairAuthAuthorizePairing) { @@ -626,6 +721,9 @@ TEST_F(DevicePairingHandlerImplTest, PairAuthAuthorizePairing) { InvokePendingConnectCallback(/*success=*/false); EXPECT_EQ(pairing_result(), mojom::PairingResult::kAuthFailed); + CheckPairingHistograms(device::BluetoothTransportType::kClassic, + /*type_count=*/1, /*failure_count=*/1, + /*success_count=*/0); } } // namespace bluetooth_config diff --git a/chromeos/services/bluetooth_config/public/cpp/BUILD.gn b/chromeos/services/bluetooth_config/public/cpp/BUILD.gn index 99e7b17054813b..21e33f09bc7da8 100644 --- a/chromeos/services/bluetooth_config/public/cpp/BUILD.gn +++ b/chromeos/services/bluetooth_config/public/cpp/BUILD.gn @@ -8,6 +8,8 @@ source_set("cpp") { sources = [ "cros_bluetooth_config_util.cc", "cros_bluetooth_config_util.h", + "device_image_info.cc", + "device_image_info.h", ] deps = [ @@ -15,3 +17,16 @@ source_set("cpp") { "//chromeos/services/bluetooth_config/public/mojom", ] } + +source_set("unit_tests") { + testonly = true + + sources = [ "device_image_info_unittest.cc" ] + + deps = [ + ":cpp", + "//base", + "//base/test:test_support", + "//testing/gtest", + ] +} diff --git a/chromeos/services/bluetooth_config/public/cpp/device_image_info.cc b/chromeos/services/bluetooth_config/public/cpp/device_image_info.cc new file mode 100644 index 00000000000000..d33376950aad9f --- /dev/null +++ b/chromeos/services/bluetooth_config/public/cpp/device_image_info.cc @@ -0,0 +1,75 @@ +// Copyright 2021 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chromeos/services/bluetooth_config/public/cpp/device_image_info.h" + +#include "base/values.h" +#include "third_party/abseil-cpp/absl/types/optional.h" + +namespace chromeos { +namespace bluetooth_config { + +namespace { + +constexpr char kDefaultImageKey[] = "Default"; +constexpr char kLeftBudImageKey[] = "LeftBud"; +constexpr char kRightBudImageKey[] = "RightBud"; +constexpr char kCaseImageKey[] = "Case"; + +} // namespace + +DeviceImageInfo::DeviceImageInfo(const std::string& default_image, + const std::string& left_bud_image, + const std::string& right_bud_image, + const std::string& case_image) + : default_image_(default_image), + left_bud_image_(left_bud_image), + right_bud_image_(right_bud_image), + case_image_(case_image) {} + +DeviceImageInfo::DeviceImageInfo() = default; + +DeviceImageInfo::DeviceImageInfo(const DeviceImageInfo&) = default; + +DeviceImageInfo& DeviceImageInfo::operator=(const DeviceImageInfo&) = default; + +DeviceImageInfo::~DeviceImageInfo() = default; + +// static +absl::optional DeviceImageInfo::FromDictionaryValue( + const base::Value& value) { + if (!value.is_dict()) + return absl::nullopt; + + const std::string* default_image = value.FindStringKey(kDefaultImageKey); + if (!default_image) + return absl::nullopt; + + const std::string* left_bud_image = value.FindStringKey(kLeftBudImageKey); + if (!left_bud_image) + return absl::nullopt; + + const std::string* right_bud_image = value.FindStringKey(kRightBudImageKey); + if (!right_bud_image) + return absl::nullopt; + + const std::string* case_image = value.FindStringKey(kCaseImageKey); + if (!case_image) + return absl::nullopt; + + return DeviceImageInfo(*default_image, *left_bud_image, *right_bud_image, + *case_image); +} + +base::Value DeviceImageInfo::ToDictionaryValue() const { + base::Value dictionary(base::Value::Type::DICTIONARY); + dictionary.SetKey(kDefaultImageKey, base::Value(default_image_)); + dictionary.SetKey(kLeftBudImageKey, base::Value(left_bud_image_)); + dictionary.SetKey(kRightBudImageKey, base::Value(right_bud_image_)); + dictionary.SetKey(kCaseImageKey, base::Value(case_image_)); + return dictionary; +} + +} // namespace bluetooth_config +} // namespace chromeos \ No newline at end of file diff --git a/chromeos/services/bluetooth_config/public/cpp/device_image_info.h b/chromeos/services/bluetooth_config/public/cpp/device_image_info.h new file mode 100644 index 00000000000000..694484a709fa2d --- /dev/null +++ b/chromeos/services/bluetooth_config/public/cpp/device_image_info.h @@ -0,0 +1,62 @@ +// Copyright 2021 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROMEOS_SERVICES_BLUETOOTH_CONFIG_PUBLIC_CPP_DEVICE_IMAGE_INFO_H_ +#define CHROMEOS_SERVICES_BLUETOOTH_CONFIG_PUBLIC_CPP_DEVICE_IMAGE_INFO_H_ + +#include + +#include "base/values.h" +#include "third_party/abseil-cpp/absl/types/optional.h" + +namespace ash { +namespace quick_pair { + +class DeviceImageStore; + +} // namespace quick_pair +} // namespace ash + +namespace chromeos { +namespace bluetooth_config { + +// Stores images as base64 encoded data URLs that can be displayed in UX. +// Provides convenience methods to convert to and from a dictionary so that +// it can be stored on disk. +class DeviceImageInfo { + public: + // Returns null if the provided value does not have the required dictionary + // properties. Should be provided a dictionary created via + // ToDictionaryValue(). + static absl::optional FromDictionaryValue( + const base::Value& value); + + DeviceImageInfo(const std::string& default_image, + const std::string& left_bud_image, + const std::string& right_bud_image, + const std::string& case_image); + DeviceImageInfo(); + DeviceImageInfo(const DeviceImageInfo&); + DeviceImageInfo& operator=(const DeviceImageInfo&); + ~DeviceImageInfo(); + + base::Value ToDictionaryValue() const; + + const std::string& default_image() const { return default_image_; } + const std::string& left_bud_image() const { return left_bud_image_; } + const std::string& right_bud_image() const { return right_bud_image_; } + const std::string& case_image() const { return case_image_; } + + private: + friend class ash::quick_pair::DeviceImageStore; + std::string default_image_; + std::string left_bud_image_; + std::string right_bud_image_; + std::string case_image_; +}; + +} // namespace bluetooth_config +} // namespace chromeos + +#endif // CHROMEOS_SERVICES_BLUETOOTH_CONFIG_PUBLIC_CPP_DEVICE_IMAGE_INFO_H_ \ No newline at end of file diff --git a/chromeos/services/bluetooth_config/public/cpp/device_image_info_unittest.cc b/chromeos/services/bluetooth_config/public/cpp/device_image_info_unittest.cc new file mode 100644 index 00000000000000..443fe9ac205dbc --- /dev/null +++ b/chromeos/services/bluetooth_config/public/cpp/device_image_info_unittest.cc @@ -0,0 +1,65 @@ +// Copyright 2021 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chromeos/services/bluetooth_config/public/cpp/device_image_info.h" + +#include "base/values.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/abseil-cpp/absl/types/optional.h" + +namespace chromeos { +namespace bluetooth_config { + +namespace { + +constexpr char kTestDefaultImage[] = "default"; +constexpr char kTestLeftBudImage[] = "left_bud"; +constexpr char kTestRightBudImage[] = "right_bud"; +constexpr char kTestCaseImage[] = "case_image"; + +} // namespace + +TEST(DeviceImageInfoTest, ToAndFromDictionaryValueValid) { + DeviceImageInfo image_info(kTestDefaultImage, kTestLeftBudImage, + kTestRightBudImage, kTestCaseImage); + + base::Value image_info_dict = image_info.ToDictionaryValue(); + absl::optional image_info_copy = + DeviceImageInfo::FromDictionaryValue(image_info_dict); + EXPECT_TRUE(image_info_copy); + + EXPECT_EQ(kTestDefaultImage, image_info_copy->default_image()); + EXPECT_EQ(kTestLeftBudImage, image_info_copy->left_bud_image()); + EXPECT_EQ(kTestRightBudImage, image_info_copy->right_bud_image()); + EXPECT_EQ(kTestCaseImage, image_info_copy->case_image()); +} + +TEST(DeviceImageInfoTest, ToAndFromDictionaryValueValidDefaultConstructor) { + // Ensure the default construction of DeviceImageInfo works with the + // to/from dictionary methods. + DeviceImageInfo image_info; + + base::Value image_info_dict = image_info.ToDictionaryValue(); + absl::optional image_info_copy = + DeviceImageInfo::FromDictionaryValue(image_info_dict); + EXPECT_TRUE(image_info_copy); + + EXPECT_TRUE(image_info_copy->default_image().empty()); + EXPECT_TRUE(image_info_copy->left_bud_image().empty()); + EXPECT_TRUE(image_info_copy->right_bud_image().empty()); + EXPECT_TRUE(image_info_copy->case_image().empty()); +} + +TEST(DeviceImageInfoTest, FromDictionaryValueInvalid) { + // Should correctly handle dictionaries with missing fields. + base::Value invalid_dict(base::Value::Type::DICTIONARY); + EXPECT_FALSE(DeviceImageInfo::FromDictionaryValue(invalid_dict)); + + // Should correctly handle non-dictionary values. + base::Value not_a_dict(777); + EXPECT_FALSE(DeviceImageInfo::FromDictionaryValue(not_a_dict)); +} + +} // namespace bluetooth_config +} // namespace chromeos \ No newline at end of file diff --git a/components/content_creation/reactions/core/reaction_list_factory.cc b/components/content_creation/reactions/core/reaction_list_factory.cc index 8ade3acae89353..3e33692d9308e4 100644 --- a/components/content_creation/reactions/core/reaction_list_factory.cc +++ b/components/content_creation/reactions/core/reaction_list_factory.cc @@ -31,9 +31,13 @@ namespace content_creation { std::vector BuildReactionMetadata() { return { + ReactionMetadata( + ReactionType::LAUGH_CRY, + l10n_util::GetStringUTF8(IDS_LIGHTWEIGHT_REACTIONS_TEARS_OF_JOY), + MakeThumbnailUrl("laughcry"), MakeReactionUrl("laughcry"), 48), ReactionMetadata( ReactionType::HEART, - l10n_util::GetStringUTF8(IDS_LIGHTWEIGHT_REACTIONS_HEART), + l10n_util::GetStringUTF8(IDS_LIGHTWEIGHT_REACTIONS_BEATING_HEART), MakeThumbnailUrl("heart"), MakeReactionUrl("heart"), 48), ReactionMetadata( ReactionType::EMOTIONAL, diff --git a/components/content_creation/reactions/core/reaction_types.h b/components/content_creation/reactions/core/reaction_types.h index d8cd5c72284a29..b2a00f651da44f 100644 --- a/components/content_creation/reactions/core/reaction_types.h +++ b/components/content_creation/reactions/core/reaction_types.h @@ -23,8 +23,9 @@ enum class ReactionType { THANKS = 6, UNSURE = 7, HEART = 8, + LAUGH_CRY = 9, - MAX_VALUE = HEART + MAX_VALUE = LAUGH_CRY }; } // namespace content_creation diff --git a/components/content_creation_strings.grdp b/components/content_creation_strings.grdp index d01cad0fce2b95..e6d92e6a4535ae 100644 --- a/components/content_creation_strings.grdp +++ b/components/content_creation_strings.grdp @@ -35,8 +35,12 @@ - - Heart + + Tears of Joy + + + + Beating Heart diff --git a/components/optimization_guide/core/optimization_guide_features.cc b/components/optimization_guide/core/optimization_guide_features.cc index 66f809057fb7e3..354fc064776929 100644 --- a/components/optimization_guide/core/optimization_guide_features.cc +++ b/components/optimization_guide/core/optimization_guide_features.cc @@ -258,14 +258,15 @@ base::TimeDelta StoredHostModelFeaturesFreshnessDuration() { "max_store_duration_for_host_model_features_in_days", 7)); } -base::TimeDelta StoredModelsInactiveDuration() { +base::TimeDelta StoredModelsValidDuration() { // TODO(crbug.com/1234054) This field should not be changed without VERY - // careful consideration. Any model that is on device and expires will be - // removed and triggered to refetch so any feature relying on the model could - // have a period of time without a valid model. + // careful consideration. This is the default duration for models that do not + // specify retention, so changing this can cause models to be removed and + // refetch would only apply to newer models. Any feature relying on the model + // would have a period of time without a valid model, and would need to push a + // new version. return base::Days(GetFieldTrialParamByFeatureAsInt( - kOptimizationTargetPrediction, "inactive_duration_for_models_in_days", - 30)); + kOptimizationTargetPrediction, "valid_duration_for_models_in_days", 30)); } base::TimeDelta URLKeyedHintValidCacheDuration() { diff --git a/components/optimization_guide/core/optimization_guide_features.h b/components/optimization_guide/core/optimization_guide_features.h index 80010d30f23ab7..79da969ed9bf66 100644 --- a/components/optimization_guide/core/optimization_guide_features.h +++ b/components/optimization_guide/core/optimization_guide_features.h @@ -130,7 +130,7 @@ base::TimeDelta StoredHostModelFeaturesFreshnessDuration(); // The maximum duration for which models can remain in the // OptimizationGuideStore without being loaded. -base::TimeDelta StoredModelsInactiveDuration(); +base::TimeDelta StoredModelsValidDuration(); // The amount of time URL-keyed hints within the hint cache will be // allowed to be used and not be purged. diff --git a/components/optimization_guide/core/optimization_guide_store.cc b/components/optimization_guide/core/optimization_guide_store.cc index 97a50f7812ae99..635cac804cc0b8 100644 --- a/components/optimization_guide/core/optimization_guide_store.cc +++ b/components/optimization_guide/core/optimization_guide_store.cc @@ -3,10 +3,13 @@ // found in the LICENSE file. #include "components/optimization_guide/core/optimization_guide_store.h" +#include +#include #include "base/bind.h" #include "base/callback_helpers.h" #include "base/files/file_util.h" +#include "base/logging.h" #include "base/metrics/histogram_functions.h" #include "base/metrics/histogram_macros.h" #include "base/sequence_checker.h" @@ -20,6 +23,7 @@ #include "components/optimization_guide/core/optimization_guide_features.h" #include "components/optimization_guide/core/optimization_guide_util.h" #include "components/optimization_guide/proto/hint_cache.pb.h" +#include "third_party/abseil-cpp/absl/types/optional.h" namespace optimization_guide { @@ -62,9 +66,7 @@ enum class OptimizationGuideHintCacheLevelDBStoreLoadMetadataResult { // recorded when it goes out of scope and its destructor is called. class ScopedLoadMetadataResultRecorder { public: - ScopedLoadMetadataResultRecorder() - : result_(OptimizationGuideHintCacheLevelDBStoreLoadMetadataResult:: - kSuccess) {} + ScopedLoadMetadataResultRecorder() = default; ~ScopedLoadMetadataResultRecorder() { UMA_HISTOGRAM_ENUMERATION( "OptimizationGuide.HintCacheLevelDBStore.LoadMetadataResult", result_); @@ -76,7 +78,8 @@ class ScopedLoadMetadataResultRecorder { } private: - OptimizationGuideHintCacheLevelDBStoreLoadMetadataResult result_; + OptimizationGuideHintCacheLevelDBStoreLoadMetadataResult result_ = + OptimizationGuideHintCacheLevelDBStoreLoadMetadataResult::kSuccess; }; void RecordStatusChange(OptimizationGuideStore::Status status) { @@ -963,14 +966,30 @@ void OptimizationGuideStore::OnLoadModelsToBeUpdated( bool had_entries_to_update_or_remove = !update_vector->empty() || !remove_vector->empty(); for (const auto& entry : *entries) { - bool should_delete_download_file = had_entries_to_update_or_remove; + absl::optional delete_download_file; + if (had_entries_to_update_or_remove && + entry.second.has_prediction_model() && + !entry.second.prediction_model().model().download_url().empty()) { + delete_download_file = + entry.second.prediction_model().model().download_url(); + } + // Only check expiry if we weren't explicitly passed in entries to update or // remove. if (!had_entries_to_update_or_remove) { + if (entry.second.keep_beyond_valid_duration()) { + continue; + } if (entry.second.has_expiry_time_secs()) { if (entry.second.expiry_time_secs() <= now_since_epoch) { + // Update the entry to remove the model. + if (entry.second.has_prediction_model() && + !entry.second.prediction_model().model().download_url().empty()) { + delete_download_file = + entry.second.prediction_model().model().download_url(); + } + remove_vector->push_back(entry.first); - should_delete_download_file = true; proto::OptimizationTarget optimization_target = GetOptimizationTargetFromPredictionModelEntryKey(entry.first); base::UmaHistogramBoolean( @@ -984,20 +1003,17 @@ void OptimizationGuideStore::OnLoadModelsToBeUpdated( update_vector->push_back(entry); update_vector->back().second.set_expiry_time_secs( now_since_epoch + - features::StoredModelsInactiveDuration().InSeconds()); + features::StoredModelsValidDuration().InSeconds()); } } // Delete files (the model itself and any additional files) that are // provided by the model in its directory. - if (should_delete_download_file && entry.second.has_prediction_model() && - !entry.second.prediction_model().model().download_url().empty()) { - // |GetFilePathFromPredictionModel| only returns nullopt when - // |model().download_url()| is empty. + if (delete_download_file) { + // |StringToFilePath| only returns nullopt when + // |delete_download_file| is empty. base::FilePath model_file_path = - StringToFilePath( - entry.second.prediction_model().model().download_url()) - .value(); + StringToFilePath(*delete_download_file).value(); base::FilePath path_to_delete; // Backwards compatibility: Once upon a time ((optimization_target)); - if (entry_keys_->find(*out_prediction_model_entry_key) != entry_keys_->end()) - return true; - return false; + return entry_keys_->find(*out_prediction_model_entry_key) != + entry_keys_->end(); } bool OptimizationGuideStore::RemovePredictionModelFromEntryKey( @@ -1107,6 +1122,17 @@ void OptimizationGuideStore::OnLoadPredictionModel( std::move(callback).Run(std::move(loaded_prediction_model)); return; } + // Also ensure that nothing is returned if the model is expired. + int64_t now_since_epoch = + base::Time::Now().ToDeltaSinceWindowsEpoch().InSeconds(); + if (!entry->keep_beyond_valid_duration() && + entry->expiry_time_secs() <= now_since_epoch) { + // Leave the actual model deletions to |PurgeInactiveModels| and return + // early. + std::unique_ptr loaded_prediction_model(nullptr); + std::move(callback).Run(std::move(loaded_prediction_model)); + return; + } std::unique_ptr loaded_prediction_model( entry->release_prediction_model()); diff --git a/components/optimization_guide/core/optimization_guide_store.h b/components/optimization_guide/core/optimization_guide_store.h index 76c36cedf6ff2d..3aebca22a7f29e 100644 --- a/components/optimization_guide/core/optimization_guide_store.h +++ b/components/optimization_guide/core/optimization_guide_store.h @@ -179,6 +179,7 @@ class OptimizationGuideStore { // Removes all models that have not been loaded in the max inactive duration // configured. |entry_keys| is updated after the inactive models are removed. + // Respects models' |keep_beyond_valid_duration| setting. void PurgeInactiveModels(); // Creates and returns a StoreUpdateData object for Prediction Models. This @@ -196,9 +197,9 @@ class OptimizationGuideStore { std::unique_ptr prediction_models_update_data, base::OnceClosure callback); - // Finds the entry key for the prediction model if it is known to the store. - // Returns true if an entry key is found and |out_prediction_model_entry_key| - // is populated with the matching key. + // Finds the entry key for the prediction model if it is still valid in the + // store. Returns true if an entry key is valid and + // |out_prediction_model_entry_key| is populated with any matching key. // Virtualized for testing. virtual bool FindPredictionModelEntryKey( proto::OptimizationTarget optimization_target, diff --git a/components/optimization_guide/core/optimization_guide_store_unittest.cc b/components/optimization_guide/core/optimization_guide_store_unittest.cc index b1ea475a75ccea..9bd9d7ea190b37 100644 --- a/components/optimization_guide/core/optimization_guide_store_unittest.cc +++ b/components/optimization_guide/core/optimization_guide_store_unittest.cc @@ -176,20 +176,24 @@ class OptimizationGuideStoreTest : public testing::Test { StoreUpdateData* update_data, optimization_guide::proto::OptimizationTarget optimization_target, absl::optional model_file_path = absl::nullopt, - base::flat_set additional_file_paths = {}) { + absl::optional info = absl::nullopt, + absl::optional expiry_time = absl::nullopt) { std::unique_ptr prediction_model = CreatePredictionModel(); + if (info) + prediction_model->mutable_model_info()->MergeFrom(*info); prediction_model->mutable_model_info()->set_optimization_target( optimization_target); + if (expiry_time) { + auto diff = expiry_time.value() - base::Time::Now(); + prediction_model->mutable_model_info() + ->mutable_valid_duration() + ->set_seconds(diff.InSeconds()); + } if (model_file_path) { prediction_model->mutable_model()->set_download_url( FilePathToString(*model_file_path)); } - for (const base::FilePath& additional_file : additional_file_paths) { - prediction_model->mutable_model_info() - ->add_additional_files() - ->set_file_path(FilePathToString(additional_file)); - } update_data->CopyPredictionModelIntoUpdateData(*prediction_model); } @@ -1945,7 +1949,7 @@ TEST_F(OptimizationGuideStoreTest, FindPredictionModelEntryKey) { std::unique_ptr update_data = guide_store()->CreateUpdateDataForPredictionModels( update_time + - optimization_guide::features::StoredModelsInactiveDuration()); + optimization_guide::features::StoredModelsValidDuration()); ASSERT_TRUE(update_data); SeedPredictionModelUpdateData(update_data.get(), proto::OPTIMIZATION_TARGET_UNKNOWN); @@ -1971,7 +1975,7 @@ TEST_F(OptimizationGuideStoreTest, std::unique_ptr update_data = guide_store()->CreateUpdateDataForPredictionModels( update_time + - optimization_guide::features::StoredModelsInactiveDuration()); + optimization_guide::features::StoredModelsValidDuration()); ASSERT_TRUE(update_data); SeedPredictionModelUpdateData(update_data.get(), proto::OPTIMIZATION_TARGET_UNKNOWN); @@ -1994,7 +1998,7 @@ TEST_F(OptimizationGuideStoreTest, FindAndRemovePredictionModelEntryKey) { std::unique_ptr update_data = guide_store()->CreateUpdateDataForPredictionModels( update_time + - optimization_guide::features::StoredModelsInactiveDuration()); + optimization_guide::features::StoredModelsValidDuration()); ASSERT_TRUE(update_data); SeedPredictionModelUpdateData(update_data.get(), proto::OPTIMIZATION_TARGET_UNKNOWN); @@ -2034,7 +2038,7 @@ TEST_F(OptimizationGuideStoreTest, LoadPredictionModel) { std::unique_ptr update_data = guide_store()->CreateUpdateDataForPredictionModels( update_time + - optimization_guide::features::StoredModelsInactiveDuration()); + optimization_guide::features::StoredModelsValidDuration()); ASSERT_TRUE(update_data); SeedPredictionModelUpdateData(update_data.get(), proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD); @@ -2076,6 +2080,46 @@ TEST_F(OptimizationGuideStoreTest, LoadPredictionModelOnUnavailableStore) { EXPECT_FALSE(last_loaded_prediction_model()); } +TEST_F(OptimizationGuideStoreTest, LoadPredictionModelOnExpiredModel) { + base::HistogramTester histogram_tester; + size_t initial_hint_count = 10; + MetadataSchemaState schema_state = MetadataSchemaState::kValid; + SeedInitialData(schema_state, initial_hint_count); + CreateDatabase(); + InitializeStore(schema_state); + + // Add an update with models that are "inactive". + base::Time update_time = base::Time().Now(); + std::unique_ptr update_data = + guide_store()->CreateUpdateDataForPredictionModels( + update_time - + optimization_guide::features::StoredModelsValidDuration()); + ASSERT_TRUE(update_data); + SeedPredictionModelUpdateData( + update_data.get(), proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, + /*model_file_path=*/absl::nullopt, + /*info=*/{}, + update_time - optimization_guide::features::StoredModelsValidDuration()); + UpdatePredictionModels(std::move(update_data)); + // Since models haven't been purged yet it will initially show up as valid. + OptimizationGuideStore::EntryKey entry_key; + EXPECT_TRUE(guide_store()->FindPredictionModelEntryKey( + proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, &entry_key)); + guide_store()->LoadPredictionModel( + entry_key, + base::BindOnce(&OptimizationGuideStoreTest::OnPredictionModelLoaded, + base::Unretained(this))); + // OnPredictionModelLoaded callback + db()->GetCallback(true); + // After load completes, the key will still exist until after purge. + EXPECT_TRUE(guide_store()->FindPredictionModelEntryKey( + proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, &entry_key)); + + // Verify that the OnPredictionModelLoaded callback runs when the store is + // unavailable and that the prediction model was correctly not available. + EXPECT_FALSE(last_loaded_prediction_model()); +} + TEST_F(OptimizationGuideStoreTest, LoadPredictionModelWithUpdateInFlight) { base::HistogramTester histogram_tester; MetadataSchemaState schema_state = MetadataSchemaState::kValid; @@ -2087,7 +2131,7 @@ TEST_F(OptimizationGuideStoreTest, LoadPredictionModelWithUpdateInFlight) { std::unique_ptr update_data = guide_store()->CreateUpdateDataForPredictionModels( update_time + - optimization_guide::features::StoredModelsInactiveDuration()); + optimization_guide::features::StoredModelsValidDuration()); ASSERT_TRUE(update_data); SeedPredictionModelUpdateData(update_data.get(), proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD); @@ -2117,7 +2161,7 @@ TEST_F(OptimizationGuideStoreTest, std::unique_ptr update_data = guide_store()->CreateUpdateDataForPredictionModels( update_time + - optimization_guide::features::StoredModelsInactiveDuration()); + optimization_guide::features::StoredModelsValidDuration()); ASSERT_TRUE(update_data); SeedPredictionModelUpdateData(update_data.get(), proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, @@ -2164,7 +2208,7 @@ TEST_F(OptimizationGuideStoreTest, std::unique_ptr update_data = guide_store()->CreateUpdateDataForPredictionModels( update_time + - optimization_guide::features::StoredModelsInactiveDuration()); + optimization_guide::features::StoredModelsValidDuration()); ASSERT_TRUE(update_data); base::FilePath model_file_path = temp_dir().AppendASCII("model.tflite"); @@ -2173,9 +2217,12 @@ TEST_F(OptimizationGuideStoreTest, base::FilePath additional_file_path = temp_dir().AppendASCII("doesntexist"); - SeedPredictionModelUpdateData( - update_data.get(), proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, - temp_dir().AppendASCII("doesntexist"), {additional_file_path}); + proto::ModelInfo info; + info.add_additional_files()->set_file_path( + FilePathToString(additional_file_path)); + SeedPredictionModelUpdateData(update_data.get(), + proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, + temp_dir().AppendASCII("doesntexist"), info); UpdatePredictionModels(std::move(update_data)); OptimizationGuideStore::EntryKey entry_key; @@ -2221,7 +2268,7 @@ TEST_F(OptimizationGuideStoreTest, std::unique_ptr update_data = guide_store()->CreateUpdateDataForPredictionModels( update_time + - optimization_guide::features::StoredModelsInactiveDuration()); + optimization_guide::features::StoredModelsValidDuration()); ASSERT_TRUE(update_data); base::FilePath model_file_path = temp_dir().AppendASCII("model.tflite"); @@ -2232,10 +2279,12 @@ TEST_F(OptimizationGuideStoreTest, temp_dir().AppendASCII("additional_file.txt"); ASSERT_EQ(static_cast(3), base::WriteFile(additional_file_path, "ah!", 3)); - + proto::ModelInfo info; + info.add_additional_files()->set_file_path( + FilePathToString(additional_file_path)); SeedPredictionModelUpdateData(update_data.get(), proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, - model_file_path, {additional_file_path}); + model_file_path, info); UpdatePredictionModels(std::move(update_data)); OptimizationGuideStore::EntryKey entry_key; @@ -2277,7 +2326,7 @@ TEST_F(OptimizationGuideStoreTest, std::unique_ptr update_data = guide_store()->CreateUpdateDataForPredictionModels( update_time + - optimization_guide::features::StoredModelsInactiveDuration()); + optimization_guide::features::StoredModelsValidDuration()); ASSERT_TRUE(update_data); base::FilePath file_path = temp_dir().AppendASCII("file"); ASSERT_EQ(static_cast(3), base::WriteFile(file_path, "boo", 3)); @@ -2324,7 +2373,7 @@ TEST_F(OptimizationGuideStoreTest, UpdatePredictionModelsDeletesOldFile) { std::unique_ptr update_data = guide_store()->CreateUpdateDataForPredictionModels( update_time + - optimization_guide::features::StoredModelsInactiveDuration()); + optimization_guide::features::StoredModelsValidDuration()); ASSERT_TRUE(update_data); base::FilePath old_file_path = old_dir.AppendASCII("model.tflite"); ASSERT_EQ(static_cast(3), base::WriteFile(old_file_path, "boo", 3)); @@ -2341,7 +2390,7 @@ TEST_F(OptimizationGuideStoreTest, UpdatePredictionModelsDeletesOldFile) { std::unique_ptr update_data2 = guide_store()->CreateUpdateDataForPredictionModels( update_time + - optimization_guide::features::StoredModelsInactiveDuration()); + optimization_guide::features::StoredModelsValidDuration()); ASSERT_TRUE(update_data2); base::FilePath new_file_path = new_dir.AppendASCII("model.tflite"); ASSERT_EQ(static_cast(3), base::WriteFile(new_file_path, "boo", 3)); @@ -2380,7 +2429,7 @@ TEST_F(OptimizationGuideStoreTest, std::unique_ptr update_data = guide_store()->CreateUpdateDataForPredictionModels( update_time + - optimization_guide::features::StoredModelsInactiveDuration()); + optimization_guide::features::StoredModelsValidDuration()); ASSERT_TRUE(update_data); base::FilePath old_file_path = old_dir.AppendASCII("model_v1.tflite"); ASSERT_EQ(static_cast(3), base::WriteFile(old_file_path, "boo", 3)); @@ -2397,7 +2446,7 @@ TEST_F(OptimizationGuideStoreTest, std::unique_ptr update_data2 = guide_store()->CreateUpdateDataForPredictionModels( update_time + - optimization_guide::features::StoredModelsInactiveDuration()); + optimization_guide::features::StoredModelsValidDuration()); ASSERT_TRUE(update_data2); base::FilePath new_file_path = new_dir.Append(GetBaseFileNameForModels()); ASSERT_EQ(static_cast(3), base::WriteFile(new_file_path, "boo", 3)); @@ -2429,7 +2478,7 @@ TEST_F(OptimizationGuideStoreTest, RemovePredictionModelEntryKeyDeletesFile) { std::unique_ptr update_data = guide_store()->CreateUpdateDataForPredictionModels( update_time + - optimization_guide::features::StoredModelsInactiveDuration()); + optimization_guide::features::StoredModelsValidDuration()); ASSERT_TRUE(update_data); base::FilePath file_path = temp_dir().AppendASCII("file"); ASSERT_EQ(static_cast(3), base::WriteFile(file_path, "boo", 3)); @@ -2669,17 +2718,22 @@ TEST_F(OptimizationGuideStoreTest, PurgeInactiveModels) { std::unique_ptr update_data = guide_store()->CreateUpdateDataForPredictionModels( update_time - - optimization_guide::features::StoredModelsInactiveDuration()); + optimization_guide::features::StoredModelsValidDuration()); ASSERT_TRUE(update_data); - SeedPredictionModelUpdateData(update_data.get(), - proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD); + base::FilePath old_file_path = temp_dir().AppendASCII("model_v1.tflite"); + ASSERT_EQ(static_cast(3), base::WriteFile(old_file_path, "boo", 3)); + SeedPredictionModelUpdateData( + update_data.get(), proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, + old_file_path, + /*info=*/{}, + update_time - optimization_guide::features::StoredModelsValidDuration()); UpdatePredictionModels(std::move(update_data)); // Add an update with models that are "active". std::unique_ptr update_data2 = guide_store()->CreateUpdateDataForPredictionModels( update_time + - optimization_guide::features::StoredModelsInactiveDuration()); + optimization_guide::features::StoredModelsValidDuration()); ASSERT_TRUE(update_data2); SeedPredictionModelUpdateData(update_data2.get(), proto::OPTIMIZATION_TARGET_LANGUAGE_DETECTION); @@ -2695,9 +2749,12 @@ TEST_F(OptimizationGuideStoreTest, PurgeInactiveModels) { ASSERT_TRUE(success); PurgeInactiveModels(); - + RunUntilIdle(); + // The expired model should be removed. EXPECT_FALSE(guide_store()->FindPredictionModelEntryKey( proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, &entry_key)); + EXPECT_FALSE(base::PathExists(old_file_path)); + // Should not purge models that are still active. EXPECT_TRUE(guide_store()->FindPredictionModelEntryKey( proto::OPTIMIZATION_TARGET_LANGUAGE_DETECTION, &entry_key)); @@ -2708,4 +2765,121 @@ TEST_F(OptimizationGuideStoreTest, PurgeInactiveModels) { "OptimizationGuide.PredictionModelExpired.LanguageDetection", 0); } +struct ValidityTestCase { + std::string test_name; + bool keep_beyond_valid_duration; + bool initially_expired; + bool expect_kept; +}; + +class OptimizationGuideStoreValidityTest + : public OptimizationGuideStoreTest, + public ::testing::WithParamInterface {}; + +TEST_P(OptimizationGuideStoreValidityTest, PurgeInactiveModels) { + const ValidityTestCase& test_case = GetParam(); + base::HistogramTester histogram_tester; + + MetadataSchemaState schema_state = MetadataSchemaState::kValid; + SeedInitialData(schema_state, 0); + CreateDatabase(); + InitializeStore(schema_state); + + // Add an update with one model according to ValidityTestCase settings. + base::Time update_time = base::Time().Now(); + if (test_case.initially_expired) { + update_time -= optimization_guide::features::StoredModelsValidDuration(); + } else { + update_time += optimization_guide::features::StoredModelsValidDuration(); + } + std::unique_ptr update_data = + guide_store()->CreateUpdateDataForPredictionModels(update_time); + ASSERT_TRUE(update_data); + proto::ModelInfo info; + info.set_keep_beyond_valid_duration(test_case.keep_beyond_valid_duration); + base::FilePath old_file_path = temp_dir().AppendASCII("model_v1.tflite"); + ASSERT_EQ(static_cast(3), base::WriteFile(old_file_path, "boo", 3)); + SeedPredictionModelUpdateData(update_data.get(), + proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, + old_file_path, info, update_time); + UpdatePredictionModels(std::move(update_data)); + + // Add an update with models that are "active" and should be unaffected. + std::unique_ptr update_data2 = + guide_store()->CreateUpdateDataForPredictionModels( + base::Time().Now() + + optimization_guide::features::StoredModelsValidDuration()); + ASSERT_TRUE(update_data2); + SeedPredictionModelUpdateData(update_data2.get(), + proto::OPTIMIZATION_TARGET_LANGUAGE_DETECTION); + UpdatePredictionModels(std::move(update_data2)); + + // Make sure both models are in the store. + OptimizationGuideStore::EntryKey entry_key; + bool success = guide_store()->FindPredictionModelEntryKey( + proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, &entry_key); + ASSERT_TRUE(success); + EXPECT_TRUE(base::PathExists(old_file_path)); + + success = guide_store()->FindPredictionModelEntryKey( + proto::OPTIMIZATION_TARGET_LANGUAGE_DETECTION, &entry_key); + ASSERT_TRUE(success); + + PurgeInactiveModels(); + RunUntilIdle(); + // Verify that the model file, entry key and histogram match expectations for + // PageLoad. + EXPECT_EQ(test_case.expect_kept, + guide_store()->FindPredictionModelEntryKey( + proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, &entry_key)); + EXPECT_EQ(test_case.expect_kept, base::PathExists(old_file_path)); + + if (test_case.expect_kept) { + histogram_tester.ExpectTotalCount( + "OptimizationGuide.PredictionModelExpired.PainfulPageLoad", 0); + } else { + histogram_tester.ExpectTotalCount( + "OptimizationGuide.PredictionModelExpired.PainfulPageLoad", 1); + } + // Verify that the other model is not deleted. + EXPECT_TRUE(guide_store()->FindPredictionModelEntryKey( + proto::OPTIMIZATION_TARGET_LANGUAGE_DETECTION, &entry_key)); + histogram_tester.ExpectTotalCount( + "OptimizationGuide.PredictionModelExpired.LanguageDetection", 0); +} +INSTANTIATE_TEST_SUITE_P( + OptimizationGuideStoreValidityTests, + OptimizationGuideStoreValidityTest, + testing::ValuesIn({ + { + "KeepDespiteInvalidModel", + /*keep_beyond_valid_duration=*/true, + /*initially_expired=*/true, + /*expect_kept=*/true, + }, + { + "KeepAndInitiallyValid", + /*keep_beyond_valid_duration=*/true, + /*initially_expired=*/false, + /*expect_kept=*/true, + }, + { + "DeleteAndInitiallyValid", + /*keep_beyond_valid_duration=*/false, + /*initially_expired=*/false, + /*expect_kept=*/true, + }, + // Only in this case should the model be removed. + { + "DeleteAndInvalidModel", + /*keep_beyond_valid_duration=*/false, + /*initially_expired=*/true, + /*expect_kept=*/false, + }, + }), + [](const testing::TestParamInfo< + OptimizationGuideStoreValidityTest::ParamType>& info) { + return info.param.test_name; + }); + } // namespace optimization_guide diff --git a/components/optimization_guide/core/store_update_data.cc b/components/optimization_guide/core/store_update_data.cc index 0afd12d3e80aa0..74dcc732687d53 100644 --- a/components/optimization_guide/core/store_update_data.cc +++ b/components/optimization_guide/core/store_update_data.cc @@ -200,8 +200,19 @@ void StoreUpdateData::CopyPredictionModelIntoUpdateData( proto::StoreEntry entry_proto; entry_proto.set_entry_type(static_cast( OptimizationGuideStore::StoreEntryType::kPredictionModel)); + + base::TimeDelta expiry_duration; + if (prediction_model.model_info().has_valid_duration()) { + expiry_duration = + base::Seconds(prediction_model.model_info().valid_duration().seconds()); + } else { + expiry_duration = features::StoredFetchedHintsFreshnessDuration(); + } + expiry_time_ = base::Time::Now() + expiry_duration; entry_proto.set_expiry_time_secs( - expiry_time_->ToDeltaSinceWindowsEpoch().InSeconds()); + expiry_time_.value().ToDeltaSinceWindowsEpoch().InSeconds()); + entry_proto.set_keep_beyond_valid_duration( + prediction_model.model_info().keep_beyond_valid_duration()); entry_proto.mutable_prediction_model()->CopyFrom(prediction_model); entries_to_save_->emplace_back(std::move(prediction_model_entry_key), std::move(entry_proto)); diff --git a/components/optimization_guide/core/store_update_data_unittest.cc b/components/optimization_guide/core/store_update_data_unittest.cc index edac180f8819a1..9a38940d58d9cb 100644 --- a/components/optimization_guide/core/store_update_data_unittest.cc +++ b/components/optimization_guide/core/store_update_data_unittest.cc @@ -122,9 +122,11 @@ TEST(StoreUpdateDataTest, BuildPredictionModelUpdateData) { proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD); model_info->add_supported_model_engine_versions( proto::ModelEngineVersion::MODEL_ENGINE_VERSION_DECISION_TREE); + model_info->set_keep_beyond_valid_duration(false); - base::Time expected_expiry_time = - base::Time::Now() + features::StoredModelsInactiveDuration(); + model_info->mutable_valid_duration()->set_seconds(3); + + base::Time expected_expiry_time = base::Time::Now() + base::Seconds(3); std::unique_ptr prediction_model_update = StoreUpdateData::CreatePredictionModelStoreUpdateData( expected_expiry_time); @@ -143,6 +145,8 @@ TEST(StoreUpdateDataTest, BuildPredictionModelUpdateData) { found_prediction_model_entry = true; EXPECT_EQ(expected_expiry_time.ToDeltaSinceWindowsEpoch().InSeconds(), store_entry.expiry_time_secs()); + EXPECT_EQ(store_entry.keep_beyond_valid_duration(), + model_info->keep_beyond_valid_duration()); break; } } diff --git a/components/optimization_guide/proto/hint_cache.proto b/components/optimization_guide/proto/hint_cache.proto index a27430d3e53f59..e89dec22aa3f26 100644 --- a/components/optimization_guide/proto/hint_cache.proto +++ b/components/optimization_guide/proto/hint_cache.proto @@ -59,4 +59,6 @@ message StoreEntry { optional PredictionModel prediction_model = 6; // The actual HostModelFeature data. optional HostModelFeatures host_model_features = 7; + // Whether to delete a model once expiry_time_secs is past. + optional bool keep_beyond_valid_duration = 8; } diff --git a/components/optimization_guide/proto/models.proto b/components/optimization_guide/proto/models.proto index ea953ccb55178c..e36ff0cb615d6d 100644 --- a/components/optimization_guide/proto/models.proto +++ b/components/optimization_guide/proto/models.proto @@ -200,7 +200,7 @@ message AdditionalModelFile { // Metadata for a prediction model for a specific optimization target. // -// Next ID: 8 +// Next ID: 10 message ModelInfo { reserved 3; @@ -223,6 +223,11 @@ message ModelInfo { // This does not need to be sent to the server in the request for an update to // this model. The server will ignore this if sent. repeated AdditionalModelFile additional_files = 7; + // How long the model will remain valid in client storage. If + // |keep_beyond_valid_duration| is true, will be ignored. + optional Duration valid_duration = 8; + // Whether to delete the model once valid_duration has passed. + optional bool keep_beyond_valid_duration = 9; // Mechanism used for model owners to attach metadata to the request or // response. // diff --git a/components/prefs/BUILD.gn b/components/prefs/BUILD.gn index 0dd1a50a3963c8..e8d2069ff75a52 100644 --- a/components/prefs/BUILD.gn +++ b/components/prefs/BUILD.gn @@ -2,6 +2,8 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. +import("//build/config/chromeos/ui_mode.gni") + component("prefs") { sources = [ "command_line_pref_store.cc", @@ -63,6 +65,13 @@ component("prefs") { ] deps += [ "android:jni_headers" ] } + + if (is_chromeos_ash) { + sources += [ + "standalone_browser_pref_store.cc", + "standalone_browser_pref_store.h", + ] + } } static_library("test_support") { diff --git a/components/prefs/pref_service.cc b/components/prefs/pref_service.cc index ae6cdad1aaed2b..3644899d25165d 100644 --- a/components/prefs/pref_service.cc +++ b/components/prefs/pref_service.cc @@ -435,8 +435,8 @@ void PrefService::ChangePrefValueStore( pref_value_store_ = pref_value_store_->CloneAndSpecialize( managed_prefs, supervised_user_prefs, extension_prefs, nullptr /* command_line_prefs */, nullptr /* user_prefs */, - recommended_prefs, nullptr /* default_prefs */, pref_notifier_.get(), - std::move(delegate)); + nullptr /* standalone_browser_prefs */, recommended_prefs, + nullptr /* default_prefs */, pref_notifier_.get(), std::move(delegate)); // Notify |pref_notifier_| on all changed values. for (const auto& kv : pref_changed_map) { diff --git a/components/prefs/pref_service_factory.cc b/components/prefs/pref_service_factory.cc index 2d4cad04124362..db6fcfebd23ce6 100644 --- a/components/prefs/pref_service_factory.cc +++ b/components/prefs/pref_service_factory.cc @@ -34,9 +34,10 @@ std::unique_ptr PrefServiceFactory::Create( auto pref_notifier = std::make_unique(); auto pref_value_store = std::make_unique( managed_prefs_.get(), supervised_user_prefs_.get(), - extension_prefs_.get(), command_line_prefs_.get(), user_prefs_.get(), - recommended_prefs_.get(), pref_registry->defaults().get(), - pref_notifier.get(), std::move(delegate)); + extension_prefs_.get(), standalone_browser_prefs_.get(), + command_line_prefs_.get(), user_prefs_.get(), recommended_prefs_.get(), + pref_registry->defaults().get(), pref_notifier.get(), + std::move(delegate)); return std::make_unique( std::move(pref_notifier), std::move(pref_value_store), user_prefs_.get(), std::move(pref_registry), read_error_callback_, async_); diff --git a/components/prefs/pref_service_factory.h b/components/prefs/pref_service_factory.h index 2683d365f931f7..8c5ca7f2eb97b7 100644 --- a/components/prefs/pref_service_factory.h +++ b/components/prefs/pref_service_factory.h @@ -43,6 +43,10 @@ class COMPONENTS_PREFS_EXPORT PrefServiceFactory { extension_prefs_.swap(prefs); } + void set_standalone_browser_prefs(scoped_refptr prefs) { + standalone_browser_prefs_.swap(prefs); + } + void set_command_line_prefs(scoped_refptr prefs) { command_line_prefs_.swap(prefs); } @@ -87,6 +91,7 @@ class COMPONENTS_PREFS_EXPORT PrefServiceFactory { scoped_refptr managed_prefs_; scoped_refptr supervised_user_prefs_; scoped_refptr extension_prefs_; + scoped_refptr standalone_browser_prefs_; scoped_refptr command_line_prefs_; scoped_refptr user_prefs_; scoped_refptr recommended_prefs_; diff --git a/components/prefs/pref_service_unittest.cc b/components/prefs/pref_service_unittest.cc index b31ed70240afca..4af2d84bd933ba 100644 --- a/components/prefs/pref_service_unittest.cc +++ b/components/prefs/pref_service_unittest.cc @@ -499,9 +499,10 @@ class PrefValueStoreChangeTest : public testing::Test { auto pref_notifier = std::make_unique(); auto pref_value_store = std::make_unique( nullptr /* managed_prefs */, nullptr /* supervised_user_prefs */, - nullptr /* extension_prefs */, new TestingPrefStore(), - user_pref_store_.get(), nullptr /* recommended_prefs */, - pref_registry_->defaults().get(), pref_notifier.get()); + nullptr /* extension_prefs */, nullptr /* standalone_browser_prefs */, + new TestingPrefStore(), user_pref_store_.get(), + nullptr /* recommended_prefs */, pref_registry_->defaults().get(), + pref_notifier.get()); pref_service_ = std::make_unique( std::move(pref_notifier), std::move(pref_value_store), user_pref_store_, pref_registry_, base::DoNothing(), false); diff --git a/components/prefs/pref_value_store.cc b/components/prefs/pref_value_store.cc index 4a238d06b169b8..efa40276caa91c 100644 --- a/components/prefs/pref_value_store.cc +++ b/components/prefs/pref_value_store.cc @@ -49,6 +49,7 @@ void PrefValueStore::PrefStoreKeeper::OnInitializationCompleted( PrefValueStore::PrefValueStore(PrefStore* managed_prefs, PrefStore* supervised_user_prefs, PrefStore* extension_prefs, + PrefStore* standalone_browser_prefs, PrefStore* command_line_prefs, PrefStore* user_prefs, PrefStore* recommended_prefs, @@ -61,6 +62,7 @@ PrefValueStore::PrefValueStore(PrefStore* managed_prefs, InitPrefStore(MANAGED_STORE, managed_prefs); InitPrefStore(SUPERVISED_USER_STORE, supervised_user_prefs); InitPrefStore(EXTENSION_STORE, extension_prefs); + InitPrefStore(STANDALONE_BROWSER_STORE, standalone_browser_prefs); InitPrefStore(COMMAND_LINE_STORE, command_line_prefs); InitPrefStore(USER_STORE, user_prefs); InitPrefStore(RECOMMENDED_STORE, recommended_prefs); @@ -69,8 +71,8 @@ PrefValueStore::PrefValueStore(PrefStore* managed_prefs, CheckInitializationCompleted(); if (delegate_) { delegate_->Init(managed_prefs, supervised_user_prefs, extension_prefs, - command_line_prefs, user_prefs, recommended_prefs, - default_prefs, pref_notifier); + standalone_browser_prefs, command_line_prefs, user_prefs, + recommended_prefs, default_prefs, pref_notifier); } } @@ -80,6 +82,7 @@ std::unique_ptr PrefValueStore::CloneAndSpecialize( PrefStore* managed_prefs, PrefStore* supervised_user_prefs, PrefStore* extension_prefs, + PrefStore* standalone_browser_prefs, PrefStore* command_line_prefs, PrefStore* user_prefs, PrefStore* recommended_prefs, @@ -93,6 +96,8 @@ std::unique_ptr PrefValueStore::CloneAndSpecialize( supervised_user_prefs = GetPrefStore(SUPERVISED_USER_STORE); if (!extension_prefs) extension_prefs = GetPrefStore(EXTENSION_STORE); + if (!standalone_browser_prefs) + standalone_browser_prefs = GetPrefStore(STANDALONE_BROWSER_STORE); if (!command_line_prefs) command_line_prefs = GetPrefStore(COMMAND_LINE_STORE); if (!user_prefs) @@ -103,9 +108,9 @@ std::unique_ptr PrefValueStore::CloneAndSpecialize( default_prefs = GetPrefStore(DEFAULT_STORE); return std::make_unique( - managed_prefs, supervised_user_prefs, extension_prefs, command_line_prefs, - user_prefs, recommended_prefs, default_prefs, pref_notifier, - std::move(delegate)); + managed_prefs, supervised_user_prefs, extension_prefs, + standalone_browser_prefs, command_line_prefs, user_prefs, + recommended_prefs, default_prefs, pref_notifier, std::move(delegate)); } bool PrefValueStore::GetValue(const std::string& name, diff --git a/components/prefs/pref_value_store.h b/components/prefs/pref_value_store.h index 9808056b6af267..ea48380760047a 100644 --- a/components/prefs/pref_value_store.h +++ b/components/prefs/pref_value_store.h @@ -45,6 +45,7 @@ class COMPONENTS_PREFS_EXPORT PrefValueStore { virtual void Init(PrefStore* managed_prefs, PrefStore* supervised_user_prefs, PrefStore* extension_prefs, + PrefStore* standalone_browser_prefs, PrefStore* command_line_prefs, PrefStore* user_prefs, PrefStore* recommended_prefs, @@ -64,15 +65,19 @@ class COMPONENTS_PREFS_EXPORT PrefValueStore { }; // PrefStores must be listed here in order from highest to lowest priority. - // MANAGED contains all managed preference values that are provided by + // MANAGED contains all managed preferences that are provided by // mandatory policies (e.g. Windows Group Policy or cloud policy). // SUPERVISED_USER contains preferences that are valid for supervised users. - // EXTENSION contains preference values set by extensions. - // COMMAND_LINE contains preference values set by command-line switches. - // USER contains all user-set preference values. + // EXTENSION contains preferences set by extensions. + // STANDALONE_BROWSER contains system preferences inherited from a separate + // Chrome instance. One relevant source is extension prefs in lacros + // passed to ash, so these prefs have similar precedence to extension + // prefs. + // COMMAND_LINE contains preferences set by command-line switches. + // USER contains all user-set preferences. // RECOMMENDED contains all preferences that are provided by recommended // policies. - // DEFAULT contains all application default preference values. + // DEFAULT contains all application default preferences. enum PrefStoreType { // INVALID_STORE is not associated with an actual PrefStore but used as // an invalid marker, e.g. as a return value. @@ -80,6 +85,7 @@ class COMPONENTS_PREFS_EXPORT PrefValueStore { MANAGED_STORE = 0, SUPERVISED_USER_STORE, EXTENSION_STORE, + STANDALONE_BROWSER_STORE, COMMAND_LINE_STORE, USER_STORE, RECOMMENDED_STORE, @@ -105,6 +111,7 @@ class COMPONENTS_PREFS_EXPORT PrefValueStore { PrefValueStore(PrefStore* managed_prefs, PrefStore* supervised_user_prefs, PrefStore* extension_prefs, + PrefStore* standalone_browser_prefs, PrefStore* command_line_prefs, PrefStore* user_prefs, PrefStore* recommended_prefs, @@ -125,6 +132,7 @@ class COMPONENTS_PREFS_EXPORT PrefValueStore { PrefStore* managed_prefs, PrefStore* supervised_user_prefs, PrefStore* extension_prefs, + PrefStore* standalone_browser_prefs, PrefStore* command_line_prefs, PrefStore* user_prefs, PrefStore* recommended_prefs, diff --git a/components/prefs/pref_value_store_unittest.cc b/components/prefs/pref_value_store_unittest.cc index 39648139c4fd5a..287196461fb931 100644 --- a/components/prefs/pref_value_store_unittest.cc +++ b/components/prefs/pref_value_store_unittest.cc @@ -41,6 +41,7 @@ const char kManagedPref[] = "this.pref.managed"; const char kSupervisedUserPref[] = "this.pref.supervised_user"; const char kCommandLinePref[] = "this.pref.command_line"; const char kExtensionPref[] = "this.pref.extension"; +const char kStandaloneBrowserPref[] = "this.pref.standalone_browser"; const char kUserPref[] = "this.pref.user"; const char kRecommendedPref[] = "this.pref.recommended"; const char kDefaultPref[] = "this.pref.default"; @@ -63,10 +64,18 @@ const char kSupervisedUserValue[] = "extension:supervised_user"; const char kExtensionValue[] = "extension:extension"; } +namespace standalone_browser_pref { +const char kManagedValue[] = "standalone_browser:managed"; +const char kSupervisedUserValue[] = "standalone_browser:supervised_user"; +const char kExtensionValue[] = "standalone_browser:extension"; +const char kStandaloneBrowserValue[] = "standalone_browser:standalone_browser"; +} // namespace standalone_browser_pref + namespace command_line_pref { const char kManagedValue[] = "command_line:managed"; const char kSupervisedUserValue[] = "command_line:supervised_user"; const char kExtensionValue[] = "command_line:extension"; +const char kStandaloneBrowserValue[] = "command_line:standalone_browser"; const char kCommandLineValue[] = "command_line:command_line"; } @@ -74,6 +83,7 @@ namespace user_pref { const char kManagedValue[] = "user:managed"; const char kSupervisedUserValue[] = "supervised_user:supervised_user"; const char kExtensionValue[] = "user:extension"; +const char kStandaloneBrowserValue[] = "user:standalone_browser"; const char kCommandLineValue[] = "user:command_line"; const char kUserValue[] = "user:user"; } @@ -82,6 +92,7 @@ namespace recommended_pref { const char kManagedValue[] = "recommended:managed"; const char kSupervisedUserValue[] = "recommended:supervised_user"; const char kExtensionValue[] = "recommended:extension"; +const char kStandaloneBrowserValue[] = "recommended:standalone_browser"; const char kCommandLineValue[] = "recommended:command_line"; const char kUserValue[] = "recommended:user"; const char kRecommendedValue[] = "recommended:recommended"; @@ -91,6 +102,7 @@ namespace default_pref { const char kManagedValue[] = "default:managed"; const char kSupervisedUserValue[] = "default:supervised_user"; const char kExtensionValue[] = "default:extension"; +const char kStandaloneBrowserValue[] = "default:standalone_browser"; const char kCommandLineValue[] = "default:command_line"; const char kUserValue[] = "default:user"; const char kRecommendedValue[] = "default:recommended"; @@ -104,6 +116,7 @@ class PrefValueStoreTest : public testing::Test { CreateManagedPrefs(); CreateSupervisedUserPrefs(); CreateExtensionPrefs(); + CreateStandaloneBrowserPrefs(); CreateCommandLinePrefs(); CreateUserPrefs(); CreateRecommendedPrefs(); @@ -113,9 +126,10 @@ class PrefValueStoreTest : public testing::Test { // Create a fresh PrefValueStore. pref_value_store_ = std::make_unique( managed_pref_store_.get(), supervised_user_pref_store_.get(), - extension_pref_store_.get(), command_line_pref_store_.get(), - user_pref_store_.get(), recommended_pref_store_.get(), - default_pref_store_.get(), &pref_notifier_); + extension_pref_store_.get(), standalone_browser_pref_store_.get(), + command_line_pref_store_.get(), user_pref_store_.get(), + recommended_pref_store_.get(), default_pref_store_.get(), + &pref_notifier_); pref_value_store_->set_callback( base::BindRepeating(&MockPrefModelAssociator::ProcessPrefChange, @@ -152,6 +166,20 @@ class PrefValueStoreTest : public testing::Test { extension_pref::kExtensionValue); } + void CreateStandaloneBrowserPrefs() { + standalone_browser_pref_store_ = new TestingPrefStore; + standalone_browser_pref_store_->SetString( + prefs::kManagedPref, standalone_browser_pref::kManagedValue); + standalone_browser_pref_store_->SetString( + prefs::kSupervisedUserPref, + standalone_browser_pref::kSupervisedUserValue); + standalone_browser_pref_store_->SetString( + prefs::kExtensionPref, standalone_browser_pref::kExtensionValue); + standalone_browser_pref_store_->SetString( + prefs::kStandaloneBrowserPref, + standalone_browser_pref::kStandaloneBrowserValue); + } + void CreateCommandLinePrefs() { command_line_pref_store_ = new TestingPrefStore; command_line_pref_store_->SetString( @@ -163,6 +191,9 @@ class PrefValueStoreTest : public testing::Test { command_line_pref_store_->SetString( prefs::kExtensionPref, command_line_pref::kExtensionValue); + command_line_pref_store_->SetString( + prefs::kStandaloneBrowserPref, + command_line_pref::kStandaloneBrowserValue); command_line_pref_store_->SetString( prefs::kCommandLinePref, command_line_pref::kCommandLineValue); @@ -182,6 +213,8 @@ class PrefValueStoreTest : public testing::Test { user_pref_store_->SetString( prefs::kExtensionPref, user_pref::kExtensionValue); + user_pref_store_->SetString(prefs::kStandaloneBrowserPref, + user_pref::kStandaloneBrowserValue); user_pref_store_->SetString( prefs::kUserPref, user_pref::kUserValue); @@ -201,6 +234,9 @@ class PrefValueStoreTest : public testing::Test { recommended_pref_store_->SetString( prefs::kExtensionPref, recommended_pref::kExtensionValue); + recommended_pref_store_->SetString( + prefs::kStandaloneBrowserPref, + recommended_pref::kStandaloneBrowserValue); recommended_pref_store_->SetString( prefs::kUserPref, recommended_pref::kUserValue); @@ -223,6 +259,8 @@ class PrefValueStoreTest : public testing::Test { default_pref_store_->SetString( prefs::kExtensionPref, default_pref::kExtensionValue); + default_pref_store_->SetString(prefs::kStandaloneBrowserPref, + default_pref::kStandaloneBrowserValue); default_pref_store_->SetString( prefs::kUserPref, default_pref::kUserValue); @@ -251,6 +289,7 @@ class PrefValueStoreTest : public testing::Test { scoped_refptr managed_pref_store_; scoped_refptr supervised_user_pref_store_; scoped_refptr extension_pref_store_; + scoped_refptr standalone_browser_pref_store_; scoped_refptr command_line_pref_store_; scoped_refptr user_pref_store_; scoped_refptr recommended_pref_store_; @@ -480,6 +519,7 @@ TEST_F(PrefValueStoreTest, OnInitializationCompleted) { managed_pref_store_->SetInitializationCompleted(); supervised_user_pref_store_->SetInitializationCompleted(); extension_pref_store_->SetInitializationCompleted(); + standalone_browser_pref_store_->SetInitializationCompleted(); command_line_pref_store_->SetInitializationCompleted(); recommended_pref_store_->SetInitializationCompleted(); default_pref_store_->SetInitializationCompleted(); @@ -498,6 +538,8 @@ TEST_F(PrefValueStoreTest, PrefValueInManagedStore) { prefs::kSupervisedUserPref)); EXPECT_FALSE(pref_value_store_->PrefValueInManagedStore( prefs::kExtensionPref)); + EXPECT_FALSE(pref_value_store_->PrefValueInManagedStore( + prefs::kStandaloneBrowserPref)); EXPECT_FALSE(pref_value_store_->PrefValueInManagedStore( prefs::kCommandLinePref)); EXPECT_FALSE(pref_value_store_->PrefValueInManagedStore( @@ -517,6 +559,8 @@ TEST_F(PrefValueStoreTest, PrefValueInExtensionStore) { prefs::kSupervisedUserPref)); EXPECT_TRUE(pref_value_store_->PrefValueInExtensionStore( prefs::kExtensionPref)); + EXPECT_FALSE(pref_value_store_->PrefValueInExtensionStore( + prefs::kStandaloneBrowserPref)); EXPECT_FALSE(pref_value_store_->PrefValueInExtensionStore( prefs::kCommandLinePref)); EXPECT_FALSE(pref_value_store_->PrefValueInExtensionStore( @@ -536,6 +580,8 @@ TEST_F(PrefValueStoreTest, PrefValueInUserStore) { prefs::kSupervisedUserPref)); EXPECT_TRUE(pref_value_store_->PrefValueInUserStore( prefs::kExtensionPref)); + EXPECT_TRUE( + pref_value_store_->PrefValueInUserStore(prefs::kStandaloneBrowserPref)); EXPECT_TRUE(pref_value_store_->PrefValueInUserStore( prefs::kCommandLinePref)); EXPECT_TRUE(pref_value_store_->PrefValueInUserStore( @@ -555,6 +601,8 @@ TEST_F(PrefValueStoreTest, PrefValueFromExtensionStore) { prefs::kSupervisedUserPref)); EXPECT_TRUE(pref_value_store_->PrefValueFromExtensionStore( prefs::kExtensionPref)); + EXPECT_FALSE(pref_value_store_->PrefValueFromExtensionStore( + prefs::kStandaloneBrowserPref)); EXPECT_FALSE(pref_value_store_->PrefValueFromExtensionStore( prefs::kCommandLinePref)); EXPECT_FALSE(pref_value_store_->PrefValueFromExtensionStore( @@ -574,6 +622,8 @@ TEST_F(PrefValueStoreTest, PrefValueFromUserStore) { prefs::kSupervisedUserPref)); EXPECT_FALSE(pref_value_store_->PrefValueFromUserStore( prefs::kExtensionPref)); + EXPECT_FALSE( + pref_value_store_->PrefValueFromUserStore(prefs::kStandaloneBrowserPref)); EXPECT_FALSE(pref_value_store_->PrefValueFromUserStore( prefs::kCommandLinePref)); EXPECT_TRUE(pref_value_store_->PrefValueFromUserStore( @@ -593,6 +643,8 @@ TEST_F(PrefValueStoreTest, PrefValueFromRecommendedStore) { prefs::kSupervisedUserPref)); EXPECT_FALSE(pref_value_store_->PrefValueFromRecommendedStore( prefs::kExtensionPref)); + EXPECT_FALSE(pref_value_store_->PrefValueFromRecommendedStore( + prefs::kStandaloneBrowserPref)); EXPECT_FALSE(pref_value_store_->PrefValueFromRecommendedStore( prefs::kCommandLinePref)); EXPECT_FALSE(pref_value_store_->PrefValueFromRecommendedStore( @@ -612,6 +664,8 @@ TEST_F(PrefValueStoreTest, PrefValueFromDefaultStore) { prefs::kSupervisedUserPref)); EXPECT_FALSE(pref_value_store_->PrefValueFromDefaultStore( prefs::kExtensionPref)); + EXPECT_FALSE(pref_value_store_->PrefValueFromDefaultStore( + prefs::kStandaloneBrowserPref)); EXPECT_FALSE(pref_value_store_->PrefValueFromDefaultStore( prefs::kCommandLinePref)); EXPECT_FALSE(pref_value_store_->PrefValueFromDefaultStore( @@ -631,6 +685,8 @@ TEST_F(PrefValueStoreTest, PrefValueUserModifiable) { prefs::kSupervisedUserPref)); EXPECT_FALSE(pref_value_store_->PrefValueUserModifiable( prefs::kExtensionPref)); + EXPECT_FALSE(pref_value_store_->PrefValueUserModifiable( + prefs::kStandaloneBrowserPref)); EXPECT_FALSE(pref_value_store_->PrefValueUserModifiable( prefs::kCommandLinePref)); EXPECT_TRUE(pref_value_store_->PrefValueUserModifiable( @@ -650,6 +706,8 @@ TEST_F(PrefValueStoreTest, PrefValueExtensionModifiable) { prefs::kSupervisedUserPref)); EXPECT_TRUE(pref_value_store_->PrefValueExtensionModifiable( prefs::kExtensionPref)); + EXPECT_TRUE(pref_value_store_->PrefValueExtensionModifiable( + prefs::kStandaloneBrowserPref)); EXPECT_TRUE(pref_value_store_->PrefValueExtensionModifiable( prefs::kCommandLinePref)); EXPECT_TRUE(pref_value_store_->PrefValueExtensionModifiable( diff --git a/components/prefs/standalone_browser_pref_store.cc b/components/prefs/standalone_browser_pref_store.cc new file mode 100644 index 00000000000000..08f3194215dd89 --- /dev/null +++ b/components/prefs/standalone_browser_pref_store.cc @@ -0,0 +1,7 @@ +// Copyright 2021 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/prefs/standalone_browser_pref_store.h" + +StandaloneBrowserPrefStore::~StandaloneBrowserPrefStore() = default; diff --git a/components/prefs/standalone_browser_pref_store.h b/components/prefs/standalone_browser_pref_store.h new file mode 100644 index 00000000000000..dc1e8b79d9a919 --- /dev/null +++ b/components/prefs/standalone_browser_pref_store.h @@ -0,0 +1,27 @@ +// Copyright 2021 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_PREFS_STANDALONE_BROWSER_PREF_STORE_H_ +#define COMPONENTS_PREFS_STANDALONE_BROWSER_PREF_STORE_H_ + +#include "components/prefs/prefs_export.h" +#include "components/prefs/value_map_pref_store.h" + +// A PrefStore implementation that holds preferences sent by another browser +// instance. For example, in ash, this prefstore holds the value of +// extension-controlled system prefs set in lacros (e.g. the screen magnifier) +// TODO(crbug.com/1218145): Implement persistence. +class COMPONENTS_PREFS_EXPORT StandaloneBrowserPrefStore + : public ValueMapPrefStore { + public: + StandaloneBrowserPrefStore() = default; + StandaloneBrowserPrefStore(const StandaloneBrowserPrefStore&) = delete; + StandaloneBrowserPrefStore& operator=(const StandaloneBrowserPrefStore&) = + delete; + + protected: + ~StandaloneBrowserPrefStore() override; +}; + +#endif // COMPONENTS_PREFS_STANDALONE_BROWSER_PREF_STORE_H_ diff --git a/components/prefs/testing_pref_service.cc b/components/prefs/testing_pref_service.cc index fb0679075b0f53..998a73708e8ad8 100644 --- a/components/prefs/testing_pref_service.cc +++ b/components/prefs/testing_pref_service.cc @@ -19,6 +19,7 @@ TestingPrefServiceBase::TestingPrefServiceBase( TestingPrefStore* managed_prefs, TestingPrefStore* supervised_user_prefs, TestingPrefStore* extension_prefs, + TestingPrefStore* standalone_browser_prefs, TestingPrefStore* user_prefs, TestingPrefStore* recommended_prefs, PrefRegistry* pref_registry, @@ -28,6 +29,7 @@ TestingPrefServiceBase::TestingPrefServiceBase( std::make_unique(managed_prefs, supervised_user_prefs, extension_prefs, + standalone_browser_prefs, /*command_line_prefs=*/nullptr, user_prefs, recommended_prefs, @@ -42,6 +44,7 @@ TestingPrefServiceBase::TestingPrefServiceBase( managed_prefs_(managed_prefs), supervised_user_prefs_(supervised_user_prefs), extension_prefs_(extension_prefs), + standalone_browser_prefs_(standalone_browser_prefs), user_prefs_(user_prefs), recommended_prefs_(recommended_prefs) {} @@ -50,6 +53,7 @@ TestingPrefServiceSimple::TestingPrefServiceSimple() /*managed_prefs=*/new TestingPrefStore(), /*supervised_user_prefs=*/new TestingPrefStore(), /*extension_prefs=*/new TestingPrefStore(), + /*standalone_browser_prefs=*/new TestingPrefStore(), /*user_prefs=*/new TestingPrefStore(), /*recommended_prefs=*/new TestingPrefStore(), new PrefRegistrySimple(), diff --git a/components/prefs/testing_pref_service.h b/components/prefs/testing_pref_service.h index 7ec1954d6871a5..cd05b487364654 100644 --- a/components/prefs/testing_pref_service.h +++ b/components/prefs/testing_pref_service.h @@ -79,6 +79,7 @@ class TestingPrefServiceBase : public SuperPrefService { TestingPrefServiceBase(TestingPrefStore* managed_prefs, TestingPrefStore* supervised_user_prefs, TestingPrefStore* extension_prefs, + TestingPrefStore* standalone_browser_prefs, TestingPrefStore* user_prefs, TestingPrefStore* recommended_prefs, ConstructionPrefRegistry* pref_registry, @@ -102,6 +103,7 @@ class TestingPrefServiceBase : public SuperPrefService { scoped_refptr managed_prefs_; scoped_refptr supervised_user_prefs_; scoped_refptr extension_prefs_; + scoped_refptr standalone_browser_prefs_; scoped_refptr user_prefs_; scoped_refptr recommended_prefs_; }; @@ -130,6 +132,7 @@ TestingPrefServiceBase::TestingPrefServiceBase( TestingPrefStore* managed_prefs, TestingPrefStore* supervised_user_prefs, TestingPrefStore* extension_prefs, + TestingPrefStore* standalone_browser_prefs, TestingPrefStore* user_prefs, TestingPrefStore* recommended_prefs, PrefRegistry* pref_registry, @@ -269,6 +272,7 @@ void TestingPrefServiceBase:: managed_prefs_->SetInitializationCompleted(); supervised_user_prefs_->SetInitializationCompleted(); extension_prefs_->SetInitializationCompleted(); + standalone_browser_prefs_->SetInitializationCompleted(); recommended_prefs_->SetInitializationCompleted(); // |user_prefs_| is initialized in PrefService constructor so no need to // set initialization status again. diff --git a/components/segmentation_platform/internal/android/java/src/org/chromium/components/segmentation_platform/SegmentationPlatformServiceImpl.java b/components/segmentation_platform/internal/android/java/src/org/chromium/components/segmentation_platform/SegmentationPlatformServiceImpl.java index 76c27ba5f0b3ee..38f34d5d8e4625 100644 --- a/components/segmentation_platform/internal/android/java/src/org/chromium/components/segmentation_platform/SegmentationPlatformServiceImpl.java +++ b/components/segmentation_platform/internal/android/java/src/org/chromium/components/segmentation_platform/SegmentationPlatformServiceImpl.java @@ -35,6 +35,12 @@ public void getSelectedSegment( mNativePtr, this, segmentationKey, callback); } + @Override + public SegmentSelectionResult getCachedSegmentResult(String segmentationKey) { + return SegmentationPlatformServiceImplJni.get().getCachedSegmentResult( + mNativePtr, this, segmentationKey); + } + @CalledByNative private void clearNativePtr() { mNativePtr = 0; @@ -53,5 +59,7 @@ interface Natives { void getSelectedSegment(long nativeSegmentationPlatformServiceAndroid, SegmentationPlatformServiceImpl caller, String segmentationKey, Callback callback); + SegmentSelectionResult getCachedSegmentResult(long nativeSegmentationPlatformServiceAndroid, + SegmentationPlatformServiceImpl caller, String segmentationKey); } } diff --git a/components/segmentation_platform/internal/android/segmentation_platform_service_android.cc b/components/segmentation_platform/internal/android/segmentation_platform_service_android.cc index 738d40fcecfea9..d580aa7d579038 100644 --- a/components/segmentation_platform/internal/android/segmentation_platform_service_android.cc +++ b/components/segmentation_platform/internal/android/segmentation_platform_service_android.cc @@ -84,6 +84,16 @@ void SegmentationPlatformServiceAndroid::GetSelectedSegment( ScopedJavaGlobalRef(jcallback))); } +ScopedJavaLocalRef +SegmentationPlatformServiceAndroid::GetCachedSegmentResult( + JNIEnv* env, + const JavaParamRef& jcaller, + const JavaParamRef& j_segmentation_key) { + return CreateJavaSegmentSelectionResult( + env, segmentation_platform_service_->GetCachedSegmentResult( + ConvertJavaStringToUTF8(env, j_segmentation_key))); +} + ScopedJavaLocalRef SegmentationPlatformServiceAndroid::GetJavaObject() { return ScopedJavaLocalRef(java_obj_); diff --git a/components/segmentation_platform/internal/android/segmentation_platform_service_android.h b/components/segmentation_platform/internal/android/segmentation_platform_service_android.h index c9225b01cdd4c1..86ad117a3e0a03 100644 --- a/components/segmentation_platform/internal/android/segmentation_platform_service_android.h +++ b/components/segmentation_platform/internal/android/segmentation_platform_service_android.h @@ -30,6 +30,11 @@ class SegmentationPlatformServiceAndroid : public base::SupportsUserData::Data { const JavaParamRef& j_segmentation_key, const JavaParamRef& j_callback); + ScopedJavaLocalRef GetCachedSegmentResult( + JNIEnv* env, + const JavaParamRef& jcaller, + const JavaParamRef& j_segmentation_key); + ScopedJavaLocalRef GetJavaObject(); private: diff --git a/components/segmentation_platform/internal/dummy_segmentation_platform_service.cc b/components/segmentation_platform/internal/dummy_segmentation_platform_service.cc index 3e7c2a5aaaf84c..7cbce72e9163c9 100644 --- a/components/segmentation_platform/internal/dummy_segmentation_platform_service.cc +++ b/components/segmentation_platform/internal/dummy_segmentation_platform_service.cc @@ -22,6 +22,11 @@ void DummySegmentationPlatformService::GetSelectedSegment( FROM_HERE, base::BindOnce(std::move(callback), SegmentSelectionResult())); } +SegmentSelectionResult DummySegmentationPlatformService::GetCachedSegmentResult( + const std::string& segmentation_key) { + return SegmentSelectionResult(); +} + void DummySegmentationPlatformService::EnableMetrics( bool signal_collection_allowed) {} } // namespace segmentation_platform diff --git a/components/segmentation_platform/internal/dummy_segmentation_platform_service.h b/components/segmentation_platform/internal/dummy_segmentation_platform_service.h index 7102adc7322a5d..67df2fdcf59737 100644 --- a/components/segmentation_platform/internal/dummy_segmentation_platform_service.h +++ b/components/segmentation_platform/internal/dummy_segmentation_platform_service.h @@ -27,6 +27,8 @@ class DummySegmentationPlatformService : public SegmentationPlatformService { // SegmentationPlatformService overrides. void GetSelectedSegment(const std::string& segmentation_key, SegmentSelectionCallback callback) override; + SegmentSelectionResult GetCachedSegmentResult( + const std::string& segmentation_key) override; void EnableMetrics(bool signal_collection_allowed) override; }; diff --git a/components/segmentation_platform/internal/dummy_segmentation_platform_service_unittest.cc b/components/segmentation_platform/internal/dummy_segmentation_platform_service_unittest.cc index accf532976c0d0..645967d8e62a7d 100644 --- a/components/segmentation_platform/internal/dummy_segmentation_platform_service_unittest.cc +++ b/components/segmentation_platform/internal/dummy_segmentation_platform_service_unittest.cc @@ -47,6 +47,8 @@ TEST_F(DummySegmentationPlatformServiceTest, GetSelectedSegment) { &DummySegmentationPlatformServiceTest::OnGetSelectedSegment, base::Unretained(this), loop.QuitClosure(), expected)); loop.Run(); + ASSERT_EQ(expected, + segmentation_platform_service_->GetCachedSegmentResult("some_key")); } } // namespace segmentation_platform diff --git a/components/segmentation_platform/internal/segmentation_platform_service_impl.cc b/components/segmentation_platform/internal/segmentation_platform_service_impl.cc index 6fde32fa1204fa..1150f3e2bc29d3 100644 --- a/components/segmentation_platform/internal/segmentation_platform_service_impl.cc +++ b/components/segmentation_platform/internal/segmentation_platform_service_impl.cc @@ -157,6 +157,13 @@ void SegmentationPlatformServiceImpl::GetSelectedSegment( selector->GetSelectedSegment(std::move(callback)); } +SegmentSelectionResult SegmentationPlatformServiceImpl::GetCachedSegmentResult( + const std::string& segmentation_key) { + CHECK(segment_selectors_.find(segmentation_key) != segment_selectors_.end()); + auto& selector = segment_selectors_.at(segmentation_key); + return selector->GetCachedSegmentResult(); +} + void SegmentationPlatformServiceImpl::EnableMetrics( bool signal_collection_allowed) { signal_filter_processor_->EnableMetrics(signal_collection_allowed); diff --git a/components/segmentation_platform/internal/segmentation_platform_service_impl.h b/components/segmentation_platform/internal/segmentation_platform_service_impl.h index fca4bd55c1463e..85bb2e0e74d628 100644 --- a/components/segmentation_platform/internal/segmentation_platform_service_impl.h +++ b/components/segmentation_platform/internal/segmentation_platform_service_impl.h @@ -112,6 +112,8 @@ class SegmentationPlatformServiceImpl : public SegmentationPlatformService { // SegmentationPlatformService overrides. void GetSelectedSegment(const std::string& segmentation_key, SegmentSelectionCallback callback) override; + SegmentSelectionResult GetCachedSegmentResult( + const std::string& segmentation_key) override; void EnableMetrics(bool signal_collection_allowed) override; ServiceProxy* GetServiceProxy() override; diff --git a/components/segmentation_platform/internal/segmentation_platform_service_impl_unittest.cc b/components/segmentation_platform/internal/segmentation_platform_service_impl_unittest.cc index 6e0f7f98ca68f7..24243366977494 100644 --- a/components/segmentation_platform/internal/segmentation_platform_service_impl_unittest.cc +++ b/components/segmentation_platform/internal/segmentation_platform_service_impl_unittest.cc @@ -175,6 +175,20 @@ class SegmentationPlatformServiceImplTest : public testing::Test { loop.Run(); } + void AssertCachedSegment( + const std::string& segmentation_key, + bool is_ready, + OptimizationTarget expected = + OptimizationTarget::OPTIMIZATION_TARGET_UNKNOWN) { + SegmentSelectionResult result; + result.is_ready = is_ready; + if (is_ready) + result.segment = expected; + ASSERT_EQ(result, + segmentation_platform_service_impl_->GetCachedSegmentResult( + segmentation_key)); + } + protected: base::test::TaskEnvironment task_environment_{ base::test::TaskEnvironment::TimeSource::MOCK_TIME}; @@ -253,6 +267,11 @@ TEST_F(SegmentationPlatformServiceImplTest, InitializationFlow) { OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE); AssertSelectedSegment(kTestSegmentationKey2, false); AssertSelectedSegment(kTestSegmentationKey3, false); + AssertCachedSegment( + kTestSegmentationKey1, true, + OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE); + AssertCachedSegment(kTestSegmentationKey2, false); + AssertCachedSegment(kTestSegmentationKey3, false); mem_impl->OnSegmentationModelUpdated( OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_VOICE, metadata); @@ -278,6 +297,11 @@ TEST_F(SegmentationPlatformServiceImplTest, InitializationFlow) { OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE); AssertSelectedSegment(kTestSegmentationKey2, false); AssertSelectedSegment(kTestSegmentationKey3, false); + AssertCachedSegment( + kTestSegmentationKey1, true, + OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE); + AssertCachedSegment(kTestSegmentationKey2, false); + AssertCachedSegment(kTestSegmentationKey3, false); } TEST_F(SegmentationPlatformServiceImplTest, @@ -352,6 +376,13 @@ TEST_F(SegmentationPlatformServiceImplMultiClientTest, InitializationFlow) { kTestSegmentationKey2, true, OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_VOICE); AssertSelectedSegment(kTestSegmentationKey3, false); + AssertCachedSegment( + kTestSegmentationKey1, true, + OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE); + AssertCachedSegment( + kTestSegmentationKey2, true, + OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_VOICE); + AssertCachedSegment(kTestSegmentationKey3, false); } } // namespace segmentation_platform diff --git a/components/segmentation_platform/internal/selection/segment_selector.h b/components/segmentation_platform/internal/selection/segment_selector.h index e86615cba4f91d..af20d2ab023771 100644 --- a/components/segmentation_platform/internal/selection/segment_selector.h +++ b/components/segmentation_platform/internal/selection/segment_selector.h @@ -28,9 +28,13 @@ class SegmentSelector : public ModelExecutionScheduler::Observer { using SegmentSelectionCallback = base::OnceCallback; - // Client API. Returns the selected segment from the last session. If none, - // returns empty result. + // Client API. Returns the selected segment from the last session + // asynchronously. If none, returns empty result. virtual void GetSelectedSegment(SegmentSelectionCallback callback) = 0; + + // Client API. Returns the cached selected segment from the last session + // synchronously. + virtual SegmentSelectionResult GetCachedSegmentResult() = 0; }; } // namespace segmentation_platform diff --git a/components/segmentation_platform/internal/selection/segment_selector_impl.cc b/components/segmentation_platform/internal/selection/segment_selector_impl.cc index d444276a595a40..73a6f606c0a436 100644 --- a/components/segmentation_platform/internal/selection/segment_selector_impl.cc +++ b/components/segmentation_platform/internal/selection/segment_selector_impl.cc @@ -54,6 +54,10 @@ void SegmentSelectorImpl::GetSelectedSegment( base::BindOnce(std::move(callback), selected_segment_last_session_)); } +SegmentSelectionResult SegmentSelectorImpl::GetCachedSegmentResult() { + return selected_segment_last_session_; +} + void SegmentSelectorImpl::OnModelExecutionCompleted( OptimizationTarget segment_id) { // If the |segment_id| is not in config, then skip any updates early. diff --git a/components/segmentation_platform/internal/selection/segment_selector_impl.h b/components/segmentation_platform/internal/selection/segment_selector_impl.h index d48bd834f42237..70f665df1e77bc 100644 --- a/components/segmentation_platform/internal/selection/segment_selector_impl.h +++ b/components/segmentation_platform/internal/selection/segment_selector_impl.h @@ -41,6 +41,7 @@ class SegmentSelectorImpl : public SegmentSelector { // SegmentSelector overrides. void GetSelectedSegment(SegmentSelectionCallback callback) override; + SegmentSelectionResult GetCachedSegmentResult() override; // ModelExecutionScheduler::Observer overrides. diff --git a/components/segmentation_platform/internal/selection/segment_selector_unittest.cc b/components/segmentation_platform/internal/selection/segment_selector_unittest.cc index 63bf8d35340900..11ba9bf5364a58 100644 --- a/components/segmentation_platform/internal/selection/segment_selector_unittest.cc +++ b/components/segmentation_platform/internal/selection/segment_selector_unittest.cc @@ -301,6 +301,7 @@ TEST_F(SegmentSelectorTest, result.segment = segment_id0; result.is_ready = true; GetSelectedSegment(result); + ASSERT_EQ(result, segment_selector_->GetCachedSegmentResult()); // Add results for a new segment. base::Time result_timestamp = base::Time::Now(); @@ -312,6 +313,7 @@ TEST_F(SegmentSelectorTest, // GetSelectedSegment should still return value from previous session. GetSelectedSegment(result); + ASSERT_EQ(result, segment_selector_->GetCachedSegmentResult()); } } // namespace segmentation_platform diff --git a/components/segmentation_platform/public/android/java/src/org/chromium/components/segmentation_platform/SegmentationPlatformService.java b/components/segmentation_platform/public/android/java/src/org/chromium/components/segmentation_platform/SegmentationPlatformService.java index e3378e953f9b36..c300ec66c5fc34 100644 --- a/components/segmentation_platform/public/android/java/src/org/chromium/components/segmentation_platform/SegmentationPlatformService.java +++ b/components/segmentation_platform/public/android/java/src/org/chromium/components/segmentation_platform/SegmentationPlatformService.java @@ -12,10 +12,18 @@ */ public interface SegmentationPlatformService { /** - * Called to get the segment selection result from the backend. + * Called to get the segment selection result asynchronously from the backend. * @param segmentationKey The key to be used to distinguish between different segmentation - * usages. Currently unused. + * usages. * @param callback The callback that contains the result of segmentation. */ void getSelectedSegment(String segmentationKey, Callback callback); + + /** + * Called to get the segment selection result synchronously from the backend. + * @param segmentationKey The key to be used to distinguish between different segmentation + * usages. + * @return The result of segment selection + */ + SegmentSelectionResult getCachedSegmentResult(String segmentationKey); } \ No newline at end of file diff --git a/components/segmentation_platform/public/segmentation_platform_service.h b/components/segmentation_platform/public/segmentation_platform_service.h index cb74dbc4bba416..7b93a8d5a2f5a3 100644 --- a/components/segmentation_platform/public/segmentation_platform_service.h +++ b/components/segmentation_platform/public/segmentation_platform_service.h @@ -50,10 +50,16 @@ class SegmentationPlatformService : public KeyedService, using SegmentSelectionCallback = base::OnceCallback; - // Called to get the selected segment. If none, returns empty result. + // Called to get the selected segment asynchronously. If none, returns empty + // result. virtual void GetSelectedSegment(const std::string& segmentation_key, SegmentSelectionCallback callback) = 0; + // Called to get the selected segment synchronously. If none, returns empty + // result. + virtual SegmentSelectionResult GetCachedSegmentResult( + const std::string& segmentation_key) = 0; + // Called to enable or disable metrics collection. Must be explicitly called // on startup. virtual void EnableMetrics(bool signal_collection_allowed) = 0; diff --git a/components/sync_preferences/pref_service_syncable.cc b/components/sync_preferences/pref_service_syncable.cc index 3db63fd9d35467..84e41263540f07 100644 --- a/components/sync_preferences/pref_service_syncable.cc +++ b/components/sync_preferences/pref_service_syncable.cc @@ -114,6 +114,7 @@ PrefServiceSyncable::CreateIncognitoPrefService( nullptr, // managed nullptr, // supervised_user incognito_extension_pref_store, + nullptr, // standalone_browser_prefs nullptr, // command_line_prefs incognito_pref_store.get(), nullptr, // recommended diff --git a/components/sync_preferences/pref_service_syncable_factory.cc b/components/sync_preferences/pref_service_syncable_factory.cc index 9f7b4f0117d792..e6c60540c60afc 100644 --- a/components/sync_preferences/pref_service_syncable_factory.cc +++ b/components/sync_preferences/pref_service_syncable_factory.cc @@ -51,9 +51,10 @@ std::unique_ptr PrefServiceSyncableFactory::CreateSyncable( auto pref_notifier = std::make_unique(); auto pref_value_store = std::make_unique( managed_prefs_.get(), supervised_user_prefs_.get(), - extension_prefs_.get(), command_line_prefs_.get(), user_prefs_.get(), - recommended_prefs_.get(), pref_registry->defaults().get(), - pref_notifier.get(), /*delegate=*/nullptr); + extension_prefs_.get(), standalone_browser_prefs_.get(), + command_line_prefs_.get(), user_prefs_.get(), recommended_prefs_.get(), + pref_registry->defaults().get(), pref_notifier.get(), + /*delegate=*/nullptr); return std::make_unique( std::move(pref_notifier), std::move(pref_value_store), user_prefs_.get(), std::move(pref_registry), pref_model_associator_client_, diff --git a/components/sync_preferences/pref_service_syncable_unittest.cc b/components/sync_preferences/pref_service_syncable_unittest.cc index a9658bf0679f0c..64fac4cf16c0e9 100644 --- a/components/sync_preferences/pref_service_syncable_unittest.cc +++ b/components/sync_preferences/pref_service_syncable_unittest.cc @@ -403,6 +403,7 @@ class PrefServiceSyncableMergeTest : public testing::Test { new TestingPrefStore, new TestingPrefStore, new TestingPrefStore, + new TestingPrefStore, user_prefs_.get(), new TestingPrefStore, pref_registry_->defaults().get(), @@ -933,8 +934,9 @@ class PrefServiceSyncableChromeOsTest : public testing::Test { std::unique_ptr(pref_notifier_), std::make_unique( new TestingPrefStore, new TestingPrefStore, new TestingPrefStore, - new TestingPrefStore, user_prefs_.get(), new TestingPrefStore, - pref_registry_->defaults().get(), pref_notifier_), + new TestingPrefStore, new TestingPrefStore, user_prefs_.get(), + new TestingPrefStore, pref_registry_->defaults().get(), + pref_notifier_), user_prefs_, pref_registry_, &client_, /*read_error_callback=*/base::DoNothing(), /*async=*/false); diff --git a/components/sync_preferences/testing_pref_service_syncable.cc b/components/sync_preferences/testing_pref_service_syncable.cc index e2fd19c7f9c1eb..454bbf8ebca38e 100644 --- a/components/sync_preferences/testing_pref_service_syncable.cc +++ b/components/sync_preferences/testing_pref_service_syncable.cc @@ -18,6 +18,7 @@ TestingPrefServiceBase(managed_prefs, supervised_user_prefs, extension_prefs, + standalone_browser_prefs, /*command_line_prefs=*/nullptr, user_prefs, recommended_prefs, @@ -42,6 +44,7 @@ TestingPrefServiceBase( &gpu::CreateGraphicsPipelinesHook); + } else if (std::strcmp("vkQueueSubmit", proc_name) == 0) { + return reinterpret_cast( + &gpu::VulkanQueueSubmitHook); + } else if (std::strcmp("vkQueueWaitIdle", proc_name) == 0) { + return reinterpret_cast( + &gpu::VulkanQueueWaitIdleHook); + } else if (std::strcmp("vkQueuePresentKHR", proc_name) == 0) { + return reinterpret_cast( + &gpu::VulkanQueuePresentKHRHook); + } return vkGetDeviceProcAddr(device, proc_name); } return vkGetInstanceProcAddr(instance, proc_name); diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn index 2dc8367922db75..626b8f54d78889 100644 --- a/content/browser/BUILD.gn +++ b/content/browser/BUILD.gn @@ -414,8 +414,7 @@ source_set("browser") { "attribution_reporting/attribution_utils.h", "attribution_reporting/rate_limit_table.cc", "attribution_reporting/rate_limit_table.h", - "attribution_reporting/sent_report.cc", - "attribution_reporting/sent_report.h", + "attribution_reporting/send_result.h", "attribution_reporting/sql_utils.cc", "attribution_reporting/sql_utils.h", "attribution_reporting/storable_source.cc", diff --git a/content/browser/attribution_reporting/attribution_internals_browsertest.cc b/content/browser/attribution_reporting/attribution_internals_browsertest.cc index 26147bf084d1b6..1cc378c089e633 100644 --- a/content/browser/attribution_reporting/attribution_internals_browsertest.cc +++ b/content/browser/attribution_reporting/attribution_internals_browsertest.cc @@ -17,6 +17,7 @@ #include "content/browser/attribution_reporting/attribution_report.h" #include "content/browser/attribution_reporting/attribution_storage.h" #include "content/browser/attribution_reporting/attribution_test_utils.h" +#include "content/browser/attribution_reporting/send_result.h" #include "content/public/browser/web_contents.h" #include "content/public/browser/web_ui.h" #include "content/public/browser/web_ui_controller.h" @@ -337,22 +338,22 @@ IN_PROC_BROWSER_TEST_F(AttributionInternalsWebUiBrowserTest, OverrideWebUIAttributionManager(); - manager_.NotifyReportSent(SentReport(ReportBuilder(SourceBuilder(now).Build()) - .SetReportTime(now + base::Hours(3)) - .Build(), - SentReport::Status::kSent, + manager_.NotifyReportSent(ReportBuilder(SourceBuilder(now).Build()) + .SetReportTime(now + base::Hours(3)) + .Build(), + SendResult(SendResult::Status::kSent, /*http_response_code=*/200)); - manager_.NotifyReportSent(SentReport(ReportBuilder(SourceBuilder(now).Build()) - .SetReportTime(now + base::Hours(4)) - .SetPriority(-1) - .Build(), - SentReport::Status::kDropped, + manager_.NotifyReportSent(ReportBuilder(SourceBuilder(now).Build()) + .SetReportTime(now + base::Hours(4)) + .SetPriority(-1) + .Build(), + SendResult(SendResult::Status::kDropped, /*http_response_code=*/0)); - manager_.NotifyReportSent(SentReport(ReportBuilder(SourceBuilder(now).Build()) - .SetReportTime(now + base::Hours(5)) - .SetPriority(-2) - .Build(), - SentReport::Status::kFailure, + manager_.NotifyReportSent(ReportBuilder(SourceBuilder(now).Build()) + .SetReportTime(now + base::Hours(5)) + .SetPriority(-2) + .Build(), + SendResult(SendResult::Status::kFailure, /*http_response_code=*/0)); ON_CALL(manager_, GetPendingReportsForWebUI) .WillByDefault(InvokeCallback>( @@ -492,8 +493,8 @@ IN_PROC_BROWSER_TEST_F(AttributionInternalsWebUiBrowserTest, .WillOnce(InvokeCallback>({report})); report.set_report_time(report.report_time() + base::Hours(1)); - manager_.NotifyReportSent(SentReport(report, SentReport::Status::kSent, - /*http_response_code=*/200)); + manager_.NotifyReportSent(report, SendResult(SendResult::Status::kSent, + /*http_response_code=*/200)); EXPECT_CALL(manager_, ClearData) .WillOnce([](base::Time delete_begin, base::Time delete_end, diff --git a/content/browser/attribution_reporting/attribution_internals_handler_impl.cc b/content/browser/attribution_reporting/attribution_internals_handler_impl.cc index f8edee2ba9a8c6..34c1bea4611ff3 100644 --- a/content/browser/attribution_reporting/attribution_internals_handler_impl.cc +++ b/content/browser/attribution_reporting/attribution_internals_handler_impl.cc @@ -15,7 +15,7 @@ #include "content/browser/attribution_reporting/attribution_manager_impl.h" #include "content/browser/attribution_reporting/attribution_report.h" #include "content/browser/attribution_reporting/attribution_storage.h" -#include "content/browser/attribution_reporting/sent_report.h" +#include "content/browser/attribution_reporting/send_result.h" #include "content/browser/attribution_reporting/storable_source.h" #include "content/browser/storage_partition_impl.h" #include "content/public/browser/browser_context.h" @@ -212,31 +212,33 @@ void AttributionInternalsHandlerImpl::OnSourceDeactivated( } } -void AttributionInternalsHandlerImpl::OnReportSent(const SentReport& info) { +void AttributionInternalsHandlerImpl::OnReportSent( + const AttributionReport& report, + const SendResult& info) { mojom::WebUIAttributionReport::Status status; switch (info.status) { - case SentReport::Status::kSent: + case SendResult::Status::kSent: status = mojom::WebUIAttributionReport::Status::kSent; break; - case SentReport::Status::kDropped: + case SendResult::Status::kDropped: status = mojom::WebUIAttributionReport::Status::kProhibitedByBrowserPolicy; break; - case SentReport::Status::kFailure: + case SendResult::Status::kFailure: status = mojom::WebUIAttributionReport::Status::kNetworkError; break; - case SentReport::Status::kTransientFailure: - case SentReport::Status::kOffline: - case SentReport::Status::kRemovedFromQueue: + case SendResult::Status::kTransientFailure: + case SendResult::Status::kOffline: + case SendResult::Status::kRemovedFromQueue: NOTREACHED(); return; } - auto report = - WebUIAttributionReport(info.report, info.http_response_code, status); + auto web_report = + WebUIAttributionReport(report, info.http_response_code, status); for (auto& observer : observers_) { - observer->OnReportSent(report.Clone()); + observer->OnReportSent(web_report.Clone()); } } diff --git a/content/browser/attribution_reporting/attribution_internals_handler_impl.h b/content/browser/attribution_reporting/attribution_internals_handler_impl.h index 7198ed9785c14c..7d0fc9d6ecb334 100644 --- a/content/browser/attribution_reporting/attribution_internals_handler_impl.h +++ b/content/browser/attribution_reporting/attribution_internals_handler_impl.h @@ -68,7 +68,8 @@ class AttributionInternalsHandlerImpl void OnReportsChanged() override; void OnSourceDeactivated( const AttributionStorage::DeactivatedSource& deactivated_source) override; - void OnReportSent(const SentReport& info) override; + void OnReportSent(const AttributionReport& report, + const SendResult& info) override; void OnReportDropped( const AttributionStorage::CreateReportResult& result) override; diff --git a/content/browser/attribution_reporting/attribution_manager.h b/content/browser/attribution_reporting/attribution_manager.h index 196b76418c0f9e..570e54d72211eb 100644 --- a/content/browser/attribution_reporting/attribution_manager.h +++ b/content/browser/attribution_reporting/attribution_manager.h @@ -12,7 +12,6 @@ #include "base/observer_list_types.h" #include "content/browser/attribution_reporting/attribution_report.h" #include "content/browser/attribution_reporting/attribution_storage.h" -#include "content/browser/attribution_reporting/sent_report.h" namespace base { class Time; @@ -29,6 +28,8 @@ class StorableTrigger; class StorableSource; class WebContents; +struct SendResult; + // Interface that mediates data flow between the network, storage layer, and // blink. class AttributionManager { @@ -58,7 +59,8 @@ class AttributionManager { virtual void OnSourceDeactivated( const AttributionStorage::DeactivatedSource& source) {} - virtual void OnReportSent(const SentReport& info) {} + virtual void OnReportSent(const AttributionReport& report, + const SendResult& info) {} virtual void OnReportDropped( const AttributionStorage::CreateReportResult& result) {} diff --git a/content/browser/attribution_reporting/attribution_manager_impl.cc b/content/browser/attribution_reporting/attribution_manager_impl.cc index 745cb2809ab272..f3fd8d3ffee9d3 100644 --- a/content/browser/attribution_reporting/attribution_manager_impl.cc +++ b/content/browser/attribution_reporting/attribution_manager_impl.cc @@ -21,6 +21,7 @@ #include "content/browser/attribution_reporting/attribution_reporter_impl.h" #include "content/browser/attribution_reporting/attribution_storage_delegate_impl.h" #include "content/browser/attribution_reporting/attribution_storage_sql.h" +#include "content/browser/attribution_reporting/send_result.h" #include "content/browser/attribution_reporting/storable_source.h" #include "content/browser/attribution_reporting/storable_trigger.h" #include "content/browser/storage_partition_impl.h" @@ -84,20 +85,20 @@ void RecordDeleteEvent(AttributionManagerImpl::DeleteEvent event) { } ConversionReportSendOutcome ConvertToConversionReportSendOutcome( - SentReport::Status status) { + SendResult::Status status) { switch (status) { - case SentReport::Status::kSent: + case SendResult::Status::kSent: return ConversionReportSendOutcome::kSent; - case SentReport::Status::kTransientFailure: - case SentReport::Status::kFailure: + case SendResult::Status::kTransientFailure: + case SendResult::Status::kFailure: return ConversionReportSendOutcome::kFailed; - case SentReport::Status::kOffline: - case SentReport::Status::kRemovedFromQueue: + case SendResult::Status::kOffline: + case SendResult::Status::kRemovedFromQueue: // Offline reports and reports removed from the queue before being sent // should never record an outcome. NOTREACHED(); return ConversionReportSendOutcome::kFailed; - case SentReport::Status::kDropped: + case SendResult::Status::kDropped: return ConversionReportSendOutcome::kDropped; } } @@ -425,23 +426,23 @@ void AttributionManagerImpl::AddReportsToReporter( reporter_->AddReportsToQueue(std::move(reports)); } -void AttributionManagerImpl::OnReportSent(SentReport info) { - DCHECK(info.report.report_id().has_value()); +void AttributionManagerImpl::OnReportSent(AttributionReport report, + SendResult info) { + DCHECK(report.report_id().has_value()); // If there was a transient failure, and another attempt is allowed, // update the report's DB state to reflect that. Otherwise, delete the report // from storage if it wasn't skipped due to the browser being offline. bool should_retry = false; - if (info.status == SentReport::Status::kTransientFailure) { - info.report.set_failed_send_attempts(info.report.failed_send_attempts() + - 1); + if (info.status == SendResult::Status::kTransientFailure) { + report.set_failed_send_attempts(report.failed_send_attempts() + 1); const absl::optional delay = attribution_policy_->GetFailedReportDelay( - info.report.failed_send_attempts()); + report.failed_send_attempts()); if (delay.has_value()) { should_retry = true; - info.report.set_report_time(info.report.report_time() + *delay); + report.set_report_time(report.report_time() + *delay); } } @@ -452,7 +453,7 @@ void AttributionManagerImpl::OnReportSent(SentReport info) { // occur. attribution_storage_ .AsyncCall(&AttributionStorage::UpdateReportForSendFailure) - .WithArgs(*info.report.report_id(), info.report.report_time()) + .WithArgs(*report.report_id(), report.report_time()) .Then(base::BindOnce( [](base::WeakPtr manager, AttributionReport report, bool success) { @@ -465,17 +466,17 @@ void AttributionManagerImpl::OnReportSent(SentReport info) { manager->NotifyReportsChanged(); }, - weak_factory_.GetWeakPtr(), info.report)); - } else if (info.status == SentReport::Status::kOffline || - info.status == SentReport::Status::kRemovedFromQueue) { + weak_factory_.GetWeakPtr(), report)); + } else if (info.status == SendResult::Status::kOffline || + info.status == SendResult::Status::kRemovedFromQueue) { // Remove the ID from the set so that subsequent attempts will not be // deduplicated. - size_t num_removed = queued_reports_.erase(*info.report.report_id()); + size_t num_removed = queued_reports_.erase(*report.report_id()); DCHECK_EQ(num_removed, 1u); } else { RecordDeleteEvent(DeleteEvent::kStarted); attribution_storage_.AsyncCall(&AttributionStorage::DeleteReport) - .WithArgs(*info.report.report_id()) + .WithArgs(*report.report_id()) .Then(base::BindOnce( [](base::WeakPtr manager, AttributionReport::Id report_id, bool succeeded) { @@ -491,7 +492,7 @@ void AttributionManagerImpl::OnReportSent(SentReport info) { manager->NotifyReportsChanged(); } }, - weak_factory_.GetWeakPtr(), *info.report.report_id())); + weak_factory_.GetWeakPtr(), *report.report_id())); base::UmaHistogramEnumeration( "Conversion.ReportSendOutcome", @@ -505,21 +506,20 @@ void AttributionManagerImpl::OnReportSent(SentReport info) { // ID, remove the ID from the wait-set; if it was the last such ID, // run the callback. if (!send_reports_for_web_ui_callback_.is_null() && - pending_report_ids_for_internals_ui_.erase(*info.report.report_id()) > - 0 && + pending_report_ids_for_internals_ui_.erase(*report.report_id()) > 0 && pending_report_ids_for_internals_ui_.empty()) { std::move(send_reports_for_web_ui_callback_).Run(); } // TODO(apaseltiner): Consider surfacing retry attempts in internals UI. - if (info.status != SentReport::Status::kSent && - info.status != SentReport::Status::kFailure && - info.status != SentReport::Status::kDropped) { + if (info.status != SendResult::Status::kSent && + info.status != SendResult::Status::kFailure && + info.status != SendResult::Status::kDropped) { return; } for (Observer& observer : observers_) - observer.OnReportSent(info); + observer.OnReportSent(report, info); } void AttributionManagerImpl::NotifySourcesChanged() { diff --git a/content/browser/attribution_reporting/attribution_manager_impl.h b/content/browser/attribution_reporting/attribution_manager_impl.h index 0cdcf9840bf2b6..8661c494bc4f25 100644 --- a/content/browser/attribution_reporting/attribution_manager_impl.h +++ b/content/browser/attribution_reporting/attribution_manager_impl.h @@ -20,7 +20,6 @@ #include "content/browser/attribution_reporting/attribution_manager.h" #include "content/browser/attribution_reporting/attribution_report.h" #include "content/browser/attribution_reporting/attribution_storage.h" -#include "content/browser/attribution_reporting/sent_report.h" #include "content/common/content_export.h" #include "storage/browser/quota/special_storage_policy.h" #include "third_party/abseil-cpp/absl/types/optional.h" @@ -38,6 +37,8 @@ extern CONTENT_EXPORT const base::TimeDelta class StoragePartitionImpl; +struct SendResult; + // Provides access to the manager owned by the default StoragePartition. class AttributionManagerProviderImpl : public AttributionManager::Provider { public: @@ -153,7 +154,7 @@ class CONTENT_EXPORT AttributionManagerImpl : public AttributionManager { void OnGetReportsToSendFromWebUI(base::OnceClosure done, std::vector reports); - void OnReportSent(SentReport info); + void OnReportSent(AttributionReport report, SendResult info); void OnReportStored(AttributionStorage::CreateReportResult result); diff --git a/content/browser/attribution_reporting/attribution_manager_impl_unittest.cc b/content/browser/attribution_reporting/attribution_manager_impl_unittest.cc index aa684800f9f93d..be71091792e67d 100644 --- a/content/browser/attribution_reporting/attribution_manager_impl_unittest.cc +++ b/content/browser/attribution_reporting/attribution_manager_impl_unittest.cc @@ -28,7 +28,7 @@ #include "content/browser/attribution_reporting/attribution_report.h" #include "content/browser/attribution_reporting/attribution_storage.h" #include "content/browser/attribution_reporting/attribution_test_utils.h" -#include "content/browser/attribution_reporting/sent_report.h" +#include "content/browser/attribution_reporting/send_result.h" #include "content/browser/attribution_reporting/storable_source.h" #include "content/browser/attribution_reporting/storable_trigger.h" #include "content/public/test/browser_task_environment.h" @@ -79,7 +79,10 @@ class MockAttributionManagerObserver : public AttributionManager::Observer { (const DeactivatedSource& source), (override)); - MOCK_METHOD(void, OnReportSent, (const SentReport& info), (override)); + MOCK_METHOD(void, + OnReportSent, + (const AttributionReport& report, const SendResult& info), + (override)); MOCK_METHOD(void, OnReportDropped, @@ -91,6 +94,11 @@ class MockAttributionManagerObserver : public AttributionManager::Observer { class TestAttributionReporter : public AttributionManagerImpl::AttributionReporter { public: + struct CallbackData { + AttributionReport report; + SendResult info; + }; + TestAttributionReporter() = default; ~TestAttributionReporter() override = default; @@ -101,13 +109,14 @@ class TestAttributionReporter for (auto& report : reports) { added_reports_.push_back(report); - SentReport info(std::move(report), sent_report_status_, + SendResult info(send_result_status_, /*http_response_code=*/0); if (should_run_report_sent_callbacks_) { - report_sent_callback_.Run(std::move(info)); + report_sent_callback_.Run(std::move(report), std::move(info)); } else { - deferred_callbacks_.push_back(std::move(info)); + deferred_callbacks_.push_back( + {.report = std::move(report), .info = std::move(info)}); } } @@ -117,14 +126,15 @@ class TestAttributionReporter void RunDeferredCallbacks() { for (auto& deferred_callback : deferred_callbacks_) { - report_sent_callback_.Run(std::move(deferred_callback)); + report_sent_callback_.Run(std::move(deferred_callback.report), + std::move(deferred_callback.info)); } deferred_callbacks_.clear(); } void RemoveAllReportsFromQueue() override { for (auto& deferred_callback : deferred_callbacks_) { - deferred_callback.status = SentReport::Status::kRemovedFromQueue; + deferred_callback.info.status = SendResult::Status::kRemovedFromQueue; } RunDeferredCallbacks(); } @@ -133,8 +143,8 @@ class TestAttributionReporter should_run_report_sent_callbacks_ = should_run_report_sent_callbacks; } - void SetSentReportStatus(SentReport::Status status) { - sent_report_status_ = status; + void SetSendResultStatus(SendResult::Status status) { + send_result_status_ = status; } const std::vector& added_reports() const { @@ -152,18 +162,20 @@ class TestAttributionReporter } void SetReportSentCallback( - base::RepeatingCallback report_sent_callback) { + base::RepeatingCallback + report_sent_callback) { report_sent_callback_ = std::move(report_sent_callback); } private: - base::RepeatingCallback report_sent_callback_; + base::RepeatingCallback + report_sent_callback_; bool should_run_report_sent_callbacks_ = false; - SentReport::Status sent_report_status_ = SentReport::Status::kSent; + SendResult::Status send_result_status_ = SendResult::Status::kSent; size_t expected_num_reports_ = 0u; std::vector added_reports_; base::OnceClosure quit_closure_; - std::vector deferred_callbacks_; + std::vector deferred_callbacks_; }; // Time after impression that a conversion can first be sent. See @@ -310,7 +322,7 @@ TEST_F(AttributionManagerImplTest, QueuedReportFailedWithShouldRetry_QueuedAgain) { base::HistogramTester histograms; test_reporter_->ShouldRunReportSentCallbacks(true); - test_reporter_->SetSentReportStatus(SentReport::Status::kTransientFailure); + test_reporter_->SetSendResultStatus(SendResult::Status::kTransientFailure); attribution_manager_->HandleSource( SourceBuilder(clock().Now()).SetExpiry(kImpressionExpiry).Build()); @@ -330,7 +342,7 @@ TEST_F(AttributionManagerImplTest, QueuedReportFailedWithoutShouldRetry_NotQueuedAgain) { base::HistogramTester histograms; test_reporter_->ShouldRunReportSentCallbacks(true); - test_reporter_->SetSentReportStatus(SentReport::Status::kFailure); + test_reporter_->SetSendResultStatus(SendResult::Status::kFailure); attribution_manager_->HandleSource( SourceBuilder(clock().Now()).SetExpiry(kImpressionExpiry).Build()); @@ -361,7 +373,7 @@ TEST_F(AttributionManagerImplTest, TEST_F(AttributionManagerImplTest, QueuedReportAlwaysFails_StopsSending) { base::HistogramTester histograms; test_reporter_->ShouldRunReportSentCallbacks(false); - test_reporter_->SetSentReportStatus(SentReport::Status::kTransientFailure); + test_reporter_->SetSendResultStatus(SendResult::Status::kTransientFailure); attribution_manager_->HandleSource( SourceBuilder(clock().Now()).SetExpiry(kImpressionExpiry).Build()); @@ -426,7 +438,7 @@ TEST_F(AttributionManagerImplTest, QueuedReportAlwaysFails_StopsSending) { TEST_F(AttributionManagerImplTest, QueuedReportOffline_NoFailureIncrement) { base::HistogramTester histograms; test_reporter_->ShouldRunReportSentCallbacks(true); - test_reporter_->SetSentReportStatus(SentReport::Status::kTransientFailure); + test_reporter_->SetSendResultStatus(SendResult::Status::kTransientFailure); attribution_manager_->HandleSource( SourceBuilder(clock().Now()).SetExpiry(kImpressionExpiry).Build()); @@ -438,7 +450,7 @@ TEST_F(AttributionManagerImplTest, QueuedReportOffline_NoFailureIncrement) { // into the queue 2 times. EXPECT_THAT(test_reporter_->added_reports(), SizeIs(3)); - test_reporter_->SetSentReportStatus(SentReport::Status::kOffline); + test_reporter_->SetSendResultStatus(SendResult::Status::kOffline); task_environment_.FastForwardBy(base::Minutes(30)); EXPECT_THAT(test_reporter_->added_reports(), SizeIs(3)); @@ -495,23 +507,23 @@ TEST_F(AttributionManagerImplTest, QueuedReportSent_ObserversNotified) { observation(&observer); observation.Observe(attribution_manager_.get()); - EXPECT_CALL(observer, - OnReportSent(Field( - &SentReport::report, - Property(&AttributionReport::impression, - Property(&StorableSource::source_event_id, 1u))))); - EXPECT_CALL(observer, - OnReportSent(Field( - &SentReport::report, - Property(&AttributionReport::impression, - Property(&StorableSource::source_event_id, 2u))))); - EXPECT_CALL(observer, - OnReportSent(Field( - &SentReport::report, - Property(&AttributionReport::impression, - Property(&StorableSource::source_event_id, 3u))))); - - test_reporter_->SetSentReportStatus(SentReport::Status::kSent); + EXPECT_CALL( + observer, + OnReportSent(Property(&AttributionReport::impression, + Property(&StorableSource::source_event_id, 1u)), + _)); + EXPECT_CALL( + observer, + OnReportSent(Property(&AttributionReport::impression, + Property(&StorableSource::source_event_id, 2u)), + _)); + EXPECT_CALL( + observer, + OnReportSent(Property(&AttributionReport::impression, + Property(&StorableSource::source_event_id, 3u)), + _)); + + test_reporter_->SetSendResultStatus(SendResult::Status::kSent); attribution_manager_->HandleSource(SourceBuilder(clock().Now()) .SetSourceEventId(1) .SetExpiry(kImpressionExpiry) @@ -521,7 +533,7 @@ TEST_F(AttributionManagerImplTest, QueuedReportSent_ObserversNotified) { kAttributionManagerQueueReportsInterval); // This one should be stored, as its status is `kDropped`. - test_reporter_->SetSentReportStatus(SentReport::Status::kDropped); + test_reporter_->SetSendResultStatus(SendResult::Status::kDropped); attribution_manager_->HandleSource(SourceBuilder(clock().Now()) .SetSourceEventId(2) .SetExpiry(kImpressionExpiry) @@ -530,7 +542,7 @@ TEST_F(AttributionManagerImplTest, QueuedReportSent_ObserversNotified) { task_environment_.FastForwardBy(kFirstReportingWindow - kAttributionManagerQueueReportsInterval); - test_reporter_->SetSentReportStatus(SentReport::Status::kSent); + test_reporter_->SetSendResultStatus(SendResult::Status::kSent); attribution_manager_->HandleSource(SourceBuilder(clock().Now()) .SetSourceEventId(3) .SetExpiry(kImpressionExpiry) @@ -540,7 +552,7 @@ TEST_F(AttributionManagerImplTest, QueuedReportSent_ObserversNotified) { kAttributionManagerQueueReportsInterval); // This one shouldn't be stored, as it will be retried. - test_reporter_->SetSentReportStatus(SentReport::Status::kTransientFailure); + test_reporter_->SetSendResultStatus(SendResult::Status::kTransientFailure); attribution_manager_->HandleSource(SourceBuilder(clock().Now()) .SetSourceEventId(4) .SetExpiry(kImpressionExpiry) diff --git a/content/browser/attribution_reporting/attribution_network_sender_impl.cc b/content/browser/attribution_reporting/attribution_network_sender_impl.cc index d0caf9d9503ea3..d8ed198337a21c 100644 --- a/content/browser/attribution_reporting/attribution_network_sender_impl.cc +++ b/content/browser/attribution_reporting/attribution_network_sender_impl.cc @@ -10,11 +10,7 @@ #include "base/bind.h" #include "base/check.h" #include "base/metrics/histogram_functions.h" -#include "base/metrics/histogram_macros.h" -#include "base/time/time.h" -#include "content/browser/attribution_reporting/attribution_report.h" -#include "content/browser/attribution_reporting/attribution_utils.h" -#include "content/browser/attribution_reporting/sent_report.h" +#include "content/browser/attribution_reporting/send_result.h" #include "content/public/browser/storage_partition.h" #include "net/base/isolation_info.h" #include "net/base/load_flags.h" @@ -43,29 +39,6 @@ enum class Status { kMaxValue = kExternalError }; -// Called when a network request is started for |report|, for logging metrics. -void LogMetricsOnReportSend(const AttributionReport& report) { - // Reports sent from the WebUI should not log metrics. - if (report.report_time() == base::Time::Min()) - return; - - // Use a large time range to capture users that might not open the browser for - // a long time while a conversion report is pending. Revisit this range if it - // is non-ideal for real world data. - base::Time now = base::Time::Now(); - base::Time original_report_time = - ComputeReportTime(report.impression(), report.conversion_time()); - base::TimeDelta time_since_original_report_time = now - original_report_time; - base::UmaHistogramCustomTimes( - "Conversions.ExtraReportDelay2", time_since_original_report_time, - base::Seconds(1), base::Days(24), /*buckets=*/100); - - base::TimeDelta time_from_conversion_to_report_send = - report.report_time() - report.conversion_time(); - UMA_HISTOGRAM_COUNTS_1000("Conversions.TimeFromConversionToReportSend", - time_from_conversion_to_report_send.InHours()); -} - } // namespace AttributionNetworkSenderImpl::AttributionNetworkSenderImpl( @@ -75,7 +48,8 @@ AttributionNetworkSenderImpl::AttributionNetworkSenderImpl( AttributionNetworkSenderImpl::~AttributionNetworkSenderImpl() = default; void AttributionNetworkSenderImpl::SendReport( - AttributionReport report, + GURL report_url, + std::string report_body, ReportSentCallback sent_callback) { // The browser process URLLoaderFactory is not created by default, so don't // create it until it is directly needed. @@ -85,7 +59,7 @@ void AttributionNetworkSenderImpl::SendReport( } auto resource_request = std::make_unique(); - resource_request->url = report.ReportURL(); + resource_request->url = std::move(report_url); resource_request->method = net::HttpRequestHeaders::kPostMethod; resource_request->credentials_mode = network::mojom::CredentialsMode::kOmit; resource_request->load_flags = @@ -129,7 +103,6 @@ void AttributionNetworkSenderImpl::SendReport( std::move(simple_url_loader)); simple_url_loader_ptr->SetTimeoutDuration(base::Seconds(30)); - std::string report_body = report.ReportBody(); simple_url_loader_ptr->AttachStringForUpload(report_body, "application/json"); // Retry once on network change. A network change during DNS resolution @@ -141,14 +114,12 @@ void AttributionNetworkSenderImpl::SendReport( network::SimpleURLLoader::RETRY_ON_NAME_NOT_RESOLVED; simple_url_loader_ptr->SetRetryOptions(/*max_retries=*/1, retry_mode); - LogMetricsOnReportSend(report); - // Unretained is safe because the URLLoader is owned by |this| and will be // deleted before |this|. simple_url_loader_ptr->DownloadHeadersOnly( url_loader_factory_.get(), base::BindOnce(&AttributionNetworkSenderImpl::OnReportSent, - base::Unretained(this), std::move(it), std::move(report), + base::Unretained(this), std::move(it), std::move(sent_callback))); } @@ -159,7 +130,6 @@ void AttributionNetworkSenderImpl::SetURLLoaderFactoryForTesting( void AttributionNetworkSenderImpl::OnReportSent( UrlLoaderList::iterator it, - AttributionReport report, ReportSentCallback sent_callback, scoped_refptr headers) { network::SimpleURLLoader* loader = it->get(); @@ -201,15 +171,14 @@ void AttributionNetworkSenderImpl::OnReportSent( net_error == net::ERR_CONNECTION_ABORTED || net_error == net::ERR_CONNECTION_RESET); - SentReport::Status report_status = + SendResult::Status report_status = (status == Status::kOk) - ? SentReport::Status::kSent - : (should_retry ? SentReport::Status::kTransientFailure - : SentReport::Status::kFailure); + ? SendResult::Status::kSent + : (should_retry ? SendResult::Status::kTransientFailure + : SendResult::Status::kFailure); std::move(sent_callback) - .Run(SentReport(std::move(report), report_status, - headers ? headers->response_code() : 0)); + .Run(SendResult(report_status, headers ? headers->response_code() : 0)); } } // namespace content diff --git a/content/browser/attribution_reporting/attribution_network_sender_impl.h b/content/browser/attribution_reporting/attribution_network_sender_impl.h index 5dfa4854e12450..958398f8144bc3 100644 --- a/content/browser/attribution_reporting/attribution_network_sender_impl.h +++ b/content/browser/attribution_reporting/attribution_network_sender_impl.h @@ -26,8 +26,6 @@ namespace content { class StoragePartition; -class AttributionReport; - // Implemented a NetworkSender capable of issuing POST requests for complete // conversions. Maintains a set of all ongoing UrlLoaders used for posting // conversion reports. Created and owned by AttributionReporterImpl. @@ -48,7 +46,8 @@ class CONTENT_EXPORT AttributionNetworkSenderImpl // seconds. // |sent_callback| is run after the request finishes, whether or not it // succeeded, - void SendReport(AttributionReport report, + void SendReport(GURL report_url, + std::string report_body, ReportSentCallback sent_callback) override; // Tests inject a TestURLLoaderFactory so they can mock the network response. @@ -61,7 +60,6 @@ class CONTENT_EXPORT AttributionNetworkSenderImpl // Called when headers are available for a sent report. void OnReportSent(UrlLoaderList::iterator it, - AttributionReport report, ReportSentCallback sent_callback, scoped_refptr headers); diff --git a/content/browser/attribution_reporting/attribution_network_sender_impl_unittest.cc b/content/browser/attribution_reporting/attribution_network_sender_impl_unittest.cc index 565f333a79bcfa..de6a54dbb974a6 100644 --- a/content/browser/attribution_reporting/attribution_network_sender_impl_unittest.cc +++ b/content/browser/attribution_reporting/attribution_network_sender_impl_unittest.cc @@ -13,7 +13,7 @@ #include "base/test/mock_callback.h" #include "base/time/time.h" #include "content/browser/attribution_reporting/attribution_test_utils.h" -#include "content/browser/attribution_reporting/sent_report.h" +#include "content/browser/attribution_reporting/send_result.h" #include "content/public/browser/browser_context.h" #include "content/public/test/browser_task_environment.h" #include "content/public/test/test_browser_context.h" @@ -67,7 +67,7 @@ class AttributionNetworkSenderTest : public testing::Test { // |task_environment_| must be initialized first. content::BrowserTaskEnvironment task_environment_; - base::MockCallback> callback_; + base::MockCallback> callback_; // Unique ptr so it can be reset during testing. std::unique_ptr network_sender_; @@ -79,14 +79,18 @@ class AttributionNetworkSenderTest : public testing::Test { TEST_F(AttributionNetworkSenderTest, ConversionReportReceived_NetworkRequestMade) { - network_sender_->SendReport(DefaultReport(), base::DoNothing()); + auto report = DefaultReport(); + network_sender_->SendReport(report.ReportURL(), report.ReportBody(), + base::DoNothing()); EXPECT_EQ(1, test_url_loader_factory_.NumPending()); EXPECT_TRUE(test_url_loader_factory_.SimulateResponseForPendingRequest( kReportUrl, "")); } TEST_F(AttributionNetworkSenderTest, LoadFlags) { - network_sender_->SendReport(DefaultReport(), base::DoNothing()); + auto report = DefaultReport(); + network_sender_->SendReport(report.ReportURL(), report.ReportBody(), + base::DoNothing()); int load_flags = test_url_loader_factory_.GetPendingRequest(0)->request.load_flags; EXPECT_TRUE(load_flags & net::LOAD_BYPASS_CACHE); @@ -94,8 +98,11 @@ TEST_F(AttributionNetworkSenderTest, LoadFlags) { } TEST_F(AttributionNetworkSenderTest, Isolation) { - network_sender_->SendReport(DefaultReport(), base::DoNothing()); - network_sender_->SendReport(DefaultReport(), base::DoNothing()); + auto report = DefaultReport(); + network_sender_->SendReport(report.ReportURL(), report.ReportBody(), + base::DoNothing()); + network_sender_->SendReport(report.ReportURL(), report.ReportBody(), + base::DoNothing()); const network::ResourceRequest& request1 = test_url_loader_factory_.GetPendingRequest(0)->request; @@ -142,7 +149,8 @@ TEST_F(AttributionNetworkSenderTest, ReportSent_ReportBodySetCorrectly) { .Build(); AttributionReport report = ReportBuilder(impression).SetTriggerData(5).Build(); - network_sender_->SendReport(report, base::DoNothing()); + network_sender_->SendReport(report.ReportURL(), report.ReportBody(), + base::DoNothing()); const network::ResourceRequest* pending_request; EXPECT_TRUE( @@ -161,7 +169,8 @@ TEST_F(AttributionNetworkSenderTest, ReportSent_RequestAttributesSet) { .SetConversionOrigin(url::Origin::Create(GURL("https://sub.b.com"))) .Build(); AttributionReport report = ReportBuilder(impression).Build(); - network_sender_->SendReport(report, base::DoNothing()); + network_sender_->SendReport(report.ReportURL(), report.ReportBody(), + base::DoNothing()); const network::ResourceRequest* pending_request; EXPECT_TRUE(test_url_loader_factory_.IsPending( @@ -178,10 +187,11 @@ TEST_F(AttributionNetworkSenderTest, ReportSent_RequestAttributesSet) { TEST_F(AttributionNetworkSenderTest, ReportSent_CallbackFired) { auto report = DefaultReport(); - EXPECT_CALL(callback_, Run(SentReport(report, SentReport::Status::kSent, + EXPECT_CALL(callback_, Run(SendResult(SendResult::Status::kSent, net::HttpStatusCode::HTTP_OK))); - network_sender_->SendReport(std::move(report), callback_.Get()); + network_sender_->SendReport(report.ReportURL(), report.ReportBody(), + callback_.Get()); EXPECT_EQ(1, test_url_loader_factory_.NumPending()); EXPECT_TRUE(test_url_loader_factory_.SimulateResponseForPendingRequest( kReportUrl, "")); @@ -190,7 +200,9 @@ TEST_F(AttributionNetworkSenderTest, ReportSent_CallbackFired) { TEST_F(AttributionNetworkSenderTest, SenderDeletedDuringRequest_NoCrash) { EXPECT_CALL(callback_, Run).Times(0); - network_sender_->SendReport(DefaultReport(), callback_.Get()); + auto report = DefaultReport(); + network_sender_->SendReport(report.ReportURL(), report.ReportBody(), + callback_.Get()); EXPECT_EQ(1, test_url_loader_factory_.NumPending()); network_sender_.reset(); EXPECT_FALSE(test_url_loader_factory_.SimulateResponseForPendingRequest( @@ -201,12 +213,12 @@ TEST_F(AttributionNetworkSenderTest, ReportRequestHangs_TimesOut) { auto report = DefaultReport(); // Verify that the sent callback runs if the request times out. - // TODO(apaseltiner): Should we propagate the timeout via the SentReport + // TODO(apaseltiner): Should we propagate the timeout via the SendResult // instead of just setting |http_response_code = 0|? - EXPECT_CALL(callback_, - Run(SentReport(report, SentReport::Status::kTransientFailure, - /*http_response_code=*/0))); - network_sender_->SendReport(std::move(report), callback_.Get()); + EXPECT_CALL(callback_, Run(SendResult(SendResult::Status::kTransientFailure, + /*http_response_code=*/0))); + network_sender_->SendReport(report.ReportURL(), report.ReportBody(), + callback_.Get()); EXPECT_EQ(1, test_url_loader_factory_.NumPending()); // The request should time out after 30 seconds. @@ -219,22 +231,24 @@ TEST_F(AttributionNetworkSenderTest, ReportRequestFailsWithTargetedError_ShouldRetrySet) { struct { int net_error; - SentReport::Status expected_status; + SendResult::Status expected_status; } kTestCases[] = { - {net::ERR_INTERNET_DISCONNECTED, SentReport::Status::kTransientFailure}, - {net::ERR_TIMED_OUT, SentReport::Status::kTransientFailure}, - {net::ERR_CONNECTION_ABORTED, SentReport::Status::kTransientFailure}, - {net::ERR_CONNECTION_TIMED_OUT, SentReport::Status::kTransientFailure}, - {net::ERR_CONNECTION_REFUSED, SentReport::Status::kFailure}, - {net::ERR_CERT_DATE_INVALID, SentReport::Status::kFailure}, - {net::OK, SentReport::Status::kFailure}, + {net::ERR_INTERNET_DISCONNECTED, SendResult::Status::kTransientFailure}, + {net::ERR_TIMED_OUT, SendResult::Status::kTransientFailure}, + {net::ERR_CONNECTION_ABORTED, SendResult::Status::kTransientFailure}, + {net::ERR_CONNECTION_TIMED_OUT, SendResult::Status::kTransientFailure}, + {net::ERR_CONNECTION_REFUSED, SendResult::Status::kFailure}, + {net::ERR_CERT_DATE_INVALID, SendResult::Status::kFailure}, + {net::OK, SendResult::Status::kFailure}, }; for (const auto& test_case : kTestCases) { EXPECT_CALL(callback_, - Run(Field(&SentReport::status, test_case.expected_status))); + Run(Field(&SendResult::status, test_case.expected_status))); - network_sender_->SendReport(DefaultReport(), callback_.Get()); + auto report = DefaultReport(); + network_sender_->SendReport(report.ReportURL(), report.ReportBody(), + callback_.Get()); EXPECT_EQ(1, test_url_loader_factory_.NumPending()); // By default, headers are not sent for network errors. @@ -261,9 +275,10 @@ TEST_F(AttributionNetworkSenderTest, ReportRequestFailsWithHeaders_NotRetried) { kSendHeadersOnNetworkError); auto report = DefaultReport(); - EXPECT_CALL(callback_, Run(SentReport(report, SentReport::Status::kFailure, + EXPECT_CALL(callback_, Run(SendResult(SendResult::Status::kFailure, net::HttpStatusCode::HTTP_OK))); - network_sender_->SendReport(std::move(report), callback_.Get()); + network_sender_->SendReport(report.ReportURL(), report.ReportBody(), + callback_.Get()); // Ensure the request was replied to. EXPECT_EQ(0, test_url_loader_factory_.NumPending()); @@ -273,10 +288,11 @@ TEST_F(AttributionNetworkSenderTest, ReportRequestFailsWithHttpError_ShouldRetryNotSet) { auto report = DefaultReport(); EXPECT_CALL(callback_, - Run(SentReport(report, SentReport::Status::kFailure, + Run(SendResult(SendResult::Status::kFailure, net::HttpStatusCode::HTTP_BAD_REQUEST))); - network_sender_->SendReport(std::move(report), callback_.Get()); + network_sender_->SendReport(report.ReportURL(), report.ReportBody(), + callback_.Get()); EXPECT_EQ(1, test_url_loader_factory_.NumPending()); EXPECT_TRUE(test_url_loader_factory_.SimulateResponseForPendingRequest( @@ -291,7 +307,9 @@ TEST_F(AttributionNetworkSenderTest, EXPECT_CALL(callback_, Run); - network_sender_->SendReport(DefaultReport(), callback_.Get()); + auto report = DefaultReport(); + network_sender_->SendReport(report.ReportURL(), report.ReportBody(), + callback_.Get()); EXPECT_EQ(1, test_url_loader_factory_.NumPending()); // Simulate the request failing due to network change. @@ -321,7 +339,9 @@ TEST_F(AttributionNetworkSenderTest, { base::HistogramTester histograms; - network_sender_->SendReport(DefaultReport(), base::DoNothing()); + auto report = DefaultReport(); + network_sender_->SendReport(report.ReportURL(), report.ReportBody(), + base::DoNothing()); EXPECT_EQ(1, test_url_loader_factory_.NumPending()); // Simulate the request failing due to network change. @@ -351,11 +371,12 @@ TEST_F(AttributionNetworkSenderTest, EXPECT_CALL(callback_, Run).Times(0); EXPECT_CALL(checkpoint, Call(1)); EXPECT_CALL(callback_, - Run(SentReport(report, SentReport::Status::kFailure, + Run(SendResult(SendResult::Status::kFailure, net::HttpStatusCode::HTTP_BAD_REQUEST))); } - network_sender_->SendReport(std::move(report), callback_.Get()); + network_sender_->SendReport(report.ReportURL(), report.ReportBody(), + callback_.Get()); checkpoint.Call(1); // We should run the sent callback even if there is an http error. @@ -367,7 +388,9 @@ TEST_F(AttributionNetworkSenderTest, ManyReports_AllSentSuccessfully) { EXPECT_CALL(callback_, Run).Times(10); for (int i = 0; i < 10; i++) { - network_sender_->SendReport(DefaultReport(), callback_.Get()); + auto report = DefaultReport(); + network_sender_->SendReport(report.ReportURL(), report.ReportBody(), + callback_.Get()); } EXPECT_EQ(10, test_url_loader_factory_.NumPending()); @@ -384,7 +407,9 @@ TEST_F(AttributionNetworkSenderTest, ErrorHistogram) { // All OK. { base::HistogramTester histograms; - network_sender_->SendReport(DefaultReport(), base::DoNothing()); + auto report = DefaultReport(); + network_sender_->SendReport(report.ReportURL(), report.ReportBody(), + base::DoNothing()); EXPECT_TRUE(test_url_loader_factory_.SimulateResponseForPendingRequest( kReportUrl, "")); // kOk = 0. @@ -395,7 +420,9 @@ TEST_F(AttributionNetworkSenderTest, ErrorHistogram) { // Internal error. { base::HistogramTester histograms; - network_sender_->SendReport(DefaultReport(), base::DoNothing()); + auto report = DefaultReport(); + network_sender_->SendReport(report.ReportURL(), report.ReportBody(), + base::DoNothing()); network::URLLoaderCompletionStatus completion_status(net::ERR_FAILED); EXPECT_TRUE(test_url_loader_factory_.SimulateResponseForPendingRequest( GURL(kReportUrl), completion_status, @@ -407,7 +434,9 @@ TEST_F(AttributionNetworkSenderTest, ErrorHistogram) { } { base::HistogramTester histograms; - network_sender_->SendReport(DefaultReport(), base::DoNothing()); + auto report = DefaultReport(); + network_sender_->SendReport(report.ReportURL(), report.ReportBody(), + base::DoNothing()); EXPECT_TRUE(test_url_loader_factory_.SimulateResponseForPendingRequest( kReportUrl, "", net::HTTP_UNAUTHORIZED)); // kExternalError = 2. @@ -418,15 +447,4 @@ TEST_F(AttributionNetworkSenderTest, ErrorHistogram) { } } -TEST_F(AttributionNetworkSenderTest, TimeFromConversionToReportSendHistogram) { - base::HistogramTester histograms; - auto report = DefaultReport(); - report.set_report_time(base::Time() + base::Hours(5)); - network_sender_->SendReport(std::move(report), base::DoNothing()); - EXPECT_TRUE(test_url_loader_factory_.SimulateResponseForPendingRequest( - kReportUrl, "")); - histograms.ExpectUniqueSample("Conversions.TimeFromConversionToReportSend", 5, - 1); -} - } // namespace content diff --git a/content/browser/attribution_reporting/attribution_reporter_impl.cc b/content/browser/attribution_reporting/attribution_reporter_impl.cc index 88e79ee03222c5..c8600306aa6ad9 100644 --- a/content/browser/attribution_reporting/attribution_reporter_impl.cc +++ b/content/browser/attribution_reporting/attribution_reporter_impl.cc @@ -4,22 +4,56 @@ #include "content/browser/attribution_reporting/attribution_reporter_impl.h" +#include + #include "base/bind.h" #include "base/callback.h" +#include "base/metrics/histogram_functions.h" +#include "base/metrics/histogram_macros.h" #include "base/rand_util.h" #include "base/time/clock.h" +#include "base/time/time.h" #include "content/browser/attribution_reporting/attribution_manager.h" #include "content/browser/attribution_reporting/attribution_network_sender_impl.h" #include "content/browser/attribution_reporting/attribution_report.h" -#include "content/browser/attribution_reporting/sent_report.h" +#include "content/browser/attribution_reporting/attribution_utils.h" +#include "content/browser/attribution_reporting/send_result.h" #include "content/browser/storage_partition_impl.h" #include "content/public/browser/content_browser_client.h" #include "content/public/browser/network_service_instance.h" #include "content/public/common/content_client.h" #include "services/network/public/cpp/network_connection_tracker.h" +#include "url/gurl.h" namespace content { +namespace { + +// Called when |report| is to be sent over network, for logging metrics. +void LogMetricsOnReportSend(const AttributionReport& report) { + // Reports sent from the WebUI should not log metrics. + if (report.report_time() == base::Time::Min()) + return; + + // Use a large time range to capture users that might not open the browser for + // a long time while a conversion report is pending. Revisit this range if it + // is non-ideal for real world data. + base::Time now = base::Time::Now(); + base::Time original_report_time = + ComputeReportTime(report.impression(), report.conversion_time()); + base::TimeDelta time_since_original_report_time = now - original_report_time; + base::UmaHistogramCustomTimes( + "Conversions.ExtraReportDelay2", time_since_original_report_time, + base::Seconds(1), base::Days(24), /*buckets=*/100); + + base::TimeDelta time_from_conversion_to_report_send = + report.report_time() - report.conversion_time(); + UMA_HISTOGRAM_COUNTS_1000("Conversions.TimeFromConversionToReportSend", + time_from_conversion_to_report_send.InHours()); +} + +} // namespace + AttributionReporterImpl::AttributionReporterImpl( StoragePartitionImpl* storage_partition, const base::Clock* clock, @@ -59,8 +93,8 @@ void AttributionReporterImpl::RemoveAllReportsFromQueue() { while (!report_queue_.empty()) { AttributionReport report = report_queue_.top(); report_queue_.pop(); - callback_.Run(SentReport(std::move(report), - SentReport::Status::kRemovedFromQueue, + callback_.Run(std::move(report), + SendResult(SendResult::Status::kRemovedFromQueue, /*http_response_code=*/0)); } } @@ -103,18 +137,23 @@ void AttributionReporterImpl::SendNextReport() { // If there's no network connection, drop the report and tell the manager to // retry it later. if (offline_) { - callback_.Run(SentReport(std::move(report), SentReport::Status::kOffline, - /*http_response_code=*/0)); + callback_.Run(std::move(report), SendResult(SendResult::Status::kOffline, + /*http_response_code=*/0)); } else { - network_sender_->SendReport(std::move(report), callback_); + LogMetricsOnReportSend(report); + + GURL report_url = report.ReportURL(); + std::string report_body = report.ReportBody(); + network_sender_->SendReport(std::move(report_url), std::move(report_body), + base::BindOnce(callback_, std::move(report))); } } else { // If measurement is disallowed, just drop the report on the floor. We need // to make sure we forward that the report was "sent" to ensure it is // deleted from storage, etc. This simulates sending the report through a // null channel. - callback_.Run(SentReport(std::move(report), SentReport::Status::kDropped, - /*http_response_code=*/0)); + callback_.Run(std::move(report), SendResult(SendResult::Status::kDropped, + /*http_response_code=*/0)); } MaybeScheduleNextReport(); } diff --git a/content/browser/attribution_reporting/attribution_reporter_impl.h b/content/browser/attribution_reporting/attribution_reporter_impl.h index 1877b236765b86..ca0719cb511414 100644 --- a/content/browser/attribution_reporting/attribution_reporter_impl.h +++ b/content/browser/attribution_reporting/attribution_reporter_impl.h @@ -8,6 +8,7 @@ #include #include #include +#include #include #include "base/callback_forward.h" @@ -19,6 +20,8 @@ #include "services/network/public/cpp/network_connection_tracker.h" #include "services/network/public/cpp/shared_url_loader_factory.h" +class GURL; + namespace base { class Clock; } // namespace base @@ -27,7 +30,7 @@ namespace content { class StoragePartitionImpl; -struct SentReport; +struct SendResult; // This class is responsible for managing the dispatch of conversion reports to // an AttributionReporterImpl::NetworkSender. It maintains a queue of reports @@ -45,15 +48,16 @@ class CONTENT_EXPORT AttributionReporterImpl virtual ~NetworkSender() = default; // Callback used to notify caller that the requested report has been sent. - using ReportSentCallback = base::OnceCallback; + using ReportSentCallback = base::OnceCallback; // Generates and sends a conversion report matching |report|. This should // generate a secure POST request with no-credentials. - virtual void SendReport(AttributionReport report, + virtual void SendReport(GURL report_url, + std::string report_body, ReportSentCallback sent_callback) = 0; }; - using Callback = base::RepeatingCallback; + using Callback = base::RepeatingCallback; AttributionReporterImpl(StoragePartitionImpl* storage_partition, const base::Clock* clock, diff --git a/content/browser/attribution_reporting/attribution_reporter_impl_unittest.cc b/content/browser/attribution_reporting/attribution_reporter_impl_unittest.cc index 50cba88587fb5b..95a1df3cf034fd 100644 --- a/content/browser/attribution_reporting/attribution_reporter_impl_unittest.cc +++ b/content/browser/attribution_reporting/attribution_reporter_impl_unittest.cc @@ -5,9 +5,12 @@ #include "content/browser/attribution_reporting/attribution_reporter_impl.h" #include "base/memory/raw_ptr.h" +#include "base/strings/stringprintf.h" +#include "base/test/metrics/histogram_tester.h" #include "base/test/mock_callback.h" +#include "base/time/time.h" #include "content/browser/attribution_reporting/attribution_test_utils.h" -#include "content/browser/attribution_reporting/sent_report.h" +#include "content/browser/attribution_reporting/send_result.h" #include "content/browser/storage_partition_impl.h" #include "content/public/browser/browser_context.h" #include "content/public/browser/network_service_instance.h" @@ -30,15 +33,23 @@ using ::testing::Return; using Checkpoint = ::testing::MockFunction; +const char kDefaultReportOrigin[] = "https://report.test/"; + // Create a report which should be sent at |report_time|. Impression // data/conversion data/conversion id are all the same for simplicity. AttributionReport GetReport(base::Time report_time, - AttributionReport::Id conversion_id) { + AttributionReport::Id conversion_id, + base::Time conversion_time = base::Time(), + url::Origin reporting_origin = url::Origin::Create( + GURL(kDefaultReportOrigin))) { // Construct impressions with a null impression time as it is not used for // reporting. - return ReportBuilder(SourceBuilder(base::Time()).Build()) + return ReportBuilder(SourceBuilder(base::Time()) + .SetReportingOrigin(std::move(reporting_origin)) + .Build()) .SetReportTime(report_time) .SetReportId(conversion_id) + .SetConversionTime(conversion_time) .Build(); } @@ -46,17 +57,16 @@ class MockNetworkSender : public AttributionReporterImpl::NetworkSender { public: MOCK_METHOD(void, SendReport, - (AttributionReport report, ReportSentCallback callback), + (GURL url, std::string report_body, ReportSentCallback callback), (override)); }; -auto InvokeCallbackWith(SentReport::Status status, +auto InvokeCallbackWith(SendResult::Status status, int http_response_code = 200) { return - [=](AttributionReport report, + [=](GURL url, std::string report_body, AttributionReporterImpl::NetworkSender::ReportSentCallback callback) { - std::move(callback).Run( - SentReport(std::move(report), status, http_response_code)); + std::move(callback).Run(SendResult(status, http_response_code)); }; } @@ -103,11 +113,11 @@ TEST_F(AttributionReporterImplTest, ReportAddedWithImmediateReportTime_ReportSent) { const auto report = GetReport(clock().Now(), AttributionReport::Id(1)); - EXPECT_CALL(*sender_, SendReport(report, _)) - .WillOnce(InvokeCallbackWith(SentReport::Status::kSent)); + EXPECT_CALL(*sender_, SendReport(report.ReportURL(), report.ReportBody(), _)) + .WillOnce(InvokeCallbackWith(SendResult::Status::kSent)); - EXPECT_CALL(callback_, Run(SentReport(report, SentReport::Status::kSent, - /*http_response_code=*/200))); + EXPECT_CALL(callback_, Run(report, SendResult(SendResult::Status::kSent, + /*http_response_code=*/200))); reporter_->AddReportsToQueue({report}); @@ -121,11 +131,11 @@ TEST_F(AttributionReporterImplTest, const auto report = GetReport(clock().Now() - base::Hours(10), AttributionReport::Id(1)); - EXPECT_CALL(*sender_, SendReport(report, _)) - .WillOnce(InvokeCallbackWith(SentReport::Status::kSent)); + EXPECT_CALL(*sender_, SendReport(report.ReportURL(), report.ReportBody(), _)) + .WillOnce(InvokeCallbackWith(SendResult::Status::kSent)); - EXPECT_CALL(callback_, Run(SentReport(report, SentReport::Status::kSent, - /*http_response_code=*/200))); + EXPECT_CALL(callback_, Run(report, SendResult(SendResult::Status::kSent, + /*http_response_code=*/200))); reporter_->AddReportsToQueue({report}); @@ -141,8 +151,8 @@ TEST_F(AttributionReporterImplTest, EXPECT_CALL(*sender_, SendReport).Times(0); EXPECT_CALL(callback_, - Run(SentReport(report, SentReport::Status::kRemovedFromQueue, - /*http_response_code=*/0))); + Run(report, SendResult(SendResult::Status::kRemovedFromQueue, + /*http_response_code=*/0))); reporter_->AddReportsToQueue({report}); @@ -167,7 +177,8 @@ TEST_F(AttributionReporterImplTest, EXPECT_CALL(checkpoint, Call(1)); EXPECT_CALL(*sender_, SendReport).Times(0); EXPECT_CALL(checkpoint, Call(2)); - EXPECT_CALL(*sender_, SendReport(report, _)); + EXPECT_CALL(*sender_, + SendReport(report.ReportURL(), report.ReportBody(), _)); } reporter_->AddReportsToQueue({report}); @@ -186,7 +197,8 @@ TEST_F(AttributionReporterImplTest, DuplicateReportScheduled_Sent) { // A duplicate report should be scheduled, as it is up to the manager to // perform deduplication. - EXPECT_CALL(*sender_, SendReport(report, _)).Times(2); + EXPECT_CALL(*sender_, SendReport(report.ReportURL(), report.ReportBody(), _)) + .Times(2); reporter_->AddReportsToQueue({report}); reporter_->AddReportsToQueue({report}); @@ -202,9 +214,11 @@ TEST_F(AttributionReporterImplTest, { InSequence seq; - EXPECT_CALL(*sender_, SendReport(report, _)); + EXPECT_CALL(*sender_, + SendReport(report.ReportURL(), report.ReportBody(), _)); EXPECT_CALL(checkpoint, Call(1)); - EXPECT_CALL(*sender_, SendReport(report, _)); + EXPECT_CALL(*sender_, + SendReport(report.ReportURL(), report.ReportBody(), _)); } reporter_->AddReportsToQueue({report}); @@ -227,14 +241,20 @@ TEST_F(AttributionReporterImplTest, ManyReportsAddedAtOnce_SentInOrder) { for (int i = 1; i < 10; i++) { EXPECT_CALL(checkpoint, Call(i)); + auto origin = + url::Origin::Create(GURL(base::StringPrintf("https://%d.com/", i))); + auto report = - GetReport(clock().Now() + base::Minutes(i), AttributionReport::Id(i)); + GetReport(clock().Now() + base::Minutes(i), AttributionReport::Id(i), + base::Time(), std::move(origin)); - EXPECT_CALL(*sender_, SendReport(report, _)) - .WillOnce(InvokeCallbackWith(SentReport::Status::kSent)); + EXPECT_CALL(*sender_, + SendReport(report.ReportURL(), report.ReportBody(), _)) + .WillOnce(InvokeCallbackWith(SendResult::Status::kSent)); - EXPECT_CALL(callback_, Run(SentReport(report, SentReport::Status::kSent, - /*http_response_code=*/200))); + EXPECT_CALL(callback_, + Run(report, SendResult(SendResult::Status::kSent, + /*http_response_code=*/200))); reports.push_back(std::move(report)); } @@ -259,14 +279,20 @@ TEST_F(AttributionReporterImplTest, ManyReportsAddedSeparately_SentInOrder) { for (int i = 1; i < 10; i++) { EXPECT_CALL(checkpoint, Call(i)); + auto origin = + url::Origin::Create(GURL(base::StringPrintf("https://%d.com/", i))); + auto report = - GetReport(clock().Now() + base::Minutes(i), AttributionReport::Id(i)); + GetReport(clock().Now() + base::Minutes(i), AttributionReport::Id(i), + base::Time(), std::move(origin)); - EXPECT_CALL(*sender_, SendReport(report, _)) - .WillOnce(InvokeCallbackWith(SentReport::Status::kSent)); + EXPECT_CALL(*sender_, + SendReport(report.ReportURL(), report.ReportBody(), _)) + .WillOnce(InvokeCallbackWith(SendResult::Status::kSent)); - EXPECT_CALL(callback_, Run(SentReport(report, SentReport::Status::kSent, - /*http_response_code=*/200))); + EXPECT_CALL(callback_, + Run(report, SendResult(SendResult::Status::kSent, + /*http_response_code=*/200))); reports.push_back(std::move(report)); } @@ -300,8 +326,8 @@ TEST_F(AttributionReporterImplTest, EmbedderDisallowsReporting_ReportNotSent) { EXPECT_CALL(*sender_, SendReport).Times(0); - EXPECT_CALL(callback_, Run(SentReport(report, SentReport::Status::kDropped, - /*http_response_code=*/0))); + EXPECT_CALL(callback_, Run(report, SendResult(SendResult::Status::kDropped, + /*http_response_code=*/0))); reporter_->AddReportsToQueue({report}); @@ -327,23 +353,24 @@ TEST_F(AttributionReporterImplTest, NetworkConnectionTrackerSkipsSends) { EXPECT_CALL(*sender_, SendReport).Times(0); EXPECT_CALL(callback_, - Run(SentReport(report1_1, SentReport::Status::kOffline, - /*http_response_code=*/0))); + Run(report1_1, SendResult(SendResult::Status::kOffline, + /*http_response_code=*/0))); EXPECT_CALL(checkpoint, Call(1)); EXPECT_CALL(*sender_, SendReport).Times(0); EXPECT_CALL(callback_, - Run(SentReport(report2_1, SentReport::Status::kOffline, - /*http_response_code=*/0))); + Run(report2_1, SendResult(SendResult::Status::kOffline, + /*http_response_code=*/0))); EXPECT_CALL(checkpoint, Call(2)); EXPECT_CALL(*sender_, SendReport) - .WillOnce(InvokeCallbackWith(SentReport::Status::kSent)); - EXPECT_CALL(callback_, Run(SentReport(report1_2, SentReport::Status::kSent, + .WillOnce(InvokeCallbackWith(SendResult::Status::kSent)); + EXPECT_CALL(callback_, + Run(report1_2, SendResult(SendResult::Status::kSent, /*http_response_code=*/200))); EXPECT_CALL(checkpoint, Call(3)); EXPECT_CALL(*sender_, SendReport).Times(0); EXPECT_CALL(callback_, - Run(SentReport(report2_2, SentReport::Status::kOffline, - /*http_response_code=*/0))); + Run(report2_2, SendResult(SendResult::Status::kOffline, + /*http_response_code=*/0))); } SetOffline(true); @@ -368,4 +395,21 @@ TEST_F(AttributionReporterImplTest, NetworkConnectionTrackerSkipsSends) { task_environment_.FastForwardBy(base::Minutes(1)); } +TEST_F(AttributionReporterImplTest, TimeFromConversionToReportSendHistogram) { + base::HistogramTester histograms; + + const base::TimeDelta delay = base::Hours(5); + const auto report = + GetReport(/*report_time=*/clock().Now() + delay, AttributionReport::Id(1), + /*conversion_time=*/clock().Now()); + + EXPECT_CALL(*sender_, SendReport(report.ReportURL(), report.ReportBody(), _)); + + reporter_->AddReportsToQueue({report}); + task_environment_.FastForwardBy(delay); + + histograms.ExpectUniqueSample("Conversions.TimeFromConversionToReportSend", 5, + 1); +} + } // namespace content diff --git a/content/browser/attribution_reporting/attribution_test_utils.cc b/content/browser/attribution_reporting/attribution_test_utils.cc index 3208cf018a2a92..5b372db78e5140 100644 --- a/content/browser/attribution_reporting/attribution_test_utils.cc +++ b/content/browser/attribution_reporting/attribution_test_utils.cc @@ -145,9 +145,10 @@ void MockAttributionManager::NotifySourceDeactivated( observer.OnSourceDeactivated(source); } -void MockAttributionManager::NotifyReportSent(const SentReport& info) { +void MockAttributionManager::NotifyReportSent(const AttributionReport& report, + const SendResult& info) { for (Observer& observer : observers_) - observer.OnReportSent(info); + observer.OnReportSent(report, info); } void MockAttributionManager::NotifyReportDropped( @@ -352,9 +353,9 @@ bool operator==(const AttributionReport& a, const AttributionReport& b) { return tie(a) == tie(b); } -bool operator==(const SentReport& a, const SentReport& b) { - const auto tie = [](const SentReport& info) { - return std::make_tuple(info.report, info.status, info.http_response_code); +bool operator==(const SendResult& a, const SendResult& b) { + const auto tie = [](const SendResult& info) { + return std::make_tuple(info.status, info.http_response_code); }; return tie(a) == tie(b); } @@ -509,32 +510,32 @@ std::ostream& operator<<(std::ostream& out, const AttributionReport& report) { << "}"; } -std::ostream& operator<<(std::ostream& out, SentReport::Status status) { +std::ostream& operator<<(std::ostream& out, SendResult::Status status) { switch (status) { - case SentReport::Status::kSent: + case SendResult::Status::kSent: out << "kSent"; break; - case SentReport::Status::kTransientFailure: + case SendResult::Status::kTransientFailure: out << "kTransientFailure"; break; - case SentReport::Status::kFailure: + case SendResult::Status::kFailure: out << "kFailure"; break; - case SentReport::Status::kDropped: + case SendResult::Status::kDropped: out << "kDropped"; break; - case SentReport::Status::kOffline: + case SendResult::Status::kOffline: out << "kOffline"; break; - case SentReport::Status::kRemovedFromQueue: + case SendResult::Status::kRemovedFromQueue: out << "kRemovedFromQueue"; break; } return out; } -std::ostream& operator<<(std::ostream& out, const SentReport& info) { - return out << "{report=" << info.report << ",status=" << info.status +std::ostream& operator<<(std::ostream& out, const SendResult& info) { + return out << "{status=" << info.status << ",http_response_code=" << info.http_response_code << "}"; } diff --git a/content/browser/attribution_reporting/attribution_test_utils.h b/content/browser/attribution_reporting/attribution_test_utils.h index c5bc8b59e2a692..b927d706f1d577 100644 --- a/content/browser/attribution_reporting/attribution_test_utils.h +++ b/content/browser/attribution_reporting/attribution_test_utils.h @@ -24,7 +24,7 @@ #include "content/browser/attribution_reporting/attribution_report.h" #include "content/browser/attribution_reporting/attribution_storage.h" #include "content/browser/attribution_reporting/rate_limit_table.h" -#include "content/browser/attribution_reporting/sent_report.h" +#include "content/browser/attribution_reporting/send_result.h" #include "content/browser/attribution_reporting/storable_source.h" #include "content/browser/attribution_reporting/storable_trigger.h" #include "content/test/test_content_browser_client.h" @@ -196,7 +196,8 @@ class MockAttributionManager : public AttributionManager { void NotifyReportsChanged(); void NotifySourceDeactivated( const AttributionStorage::DeactivatedSource& source); - void NotifyReportSent(const SentReport& info); + void NotifyReportSent(const AttributionReport& report, + const SendResult& info); void NotifyReportDropped( const AttributionStorage::CreateReportResult& result); @@ -331,7 +332,7 @@ bool operator==(const StorableSource& a, const StorableSource& b); bool operator==(const AttributionReport& a, const AttributionReport& b); -bool operator==(const SentReport& a, const SentReport& b); +bool operator==(const SendResult& a, const SendResult& b); bool operator==(const AttributionStorage::DeactivatedSource& a, const AttributionStorage::DeactivatedSource& b); @@ -354,9 +355,9 @@ std::ostream& operator<<(std::ostream& out, const StorableSource& impression); std::ostream& operator<<(std::ostream& out, const AttributionReport& report); -std::ostream& operator<<(std::ostream& out, SentReport::Status status); +std::ostream& operator<<(std::ostream& out, SendResult::Status status); -std::ostream& operator<<(std::ostream& out, const SentReport& info); +std::ostream& operator<<(std::ostream& out, const SendResult& info); std::ostream& operator<<(std::ostream& out, StorableSource::AttributionLogic attribution_logic); diff --git a/content/browser/attribution_reporting/sent_report.h b/content/browser/attribution_reporting/send_result.h similarity index 65% rename from content/browser/attribution_reporting/sent_report.h rename to content/browser/attribution_reporting/send_result.h index 9cd78d532e6da5..d4d7732f3c73bc 100644 --- a/content/browser/attribution_reporting/sent_report.h +++ b/content/browser/attribution_reporting/send_result.h @@ -2,18 +2,16 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef CONTENT_BROWSER_ATTRIBUTION_REPORTING_SENT_REPORT_H_ -#define CONTENT_BROWSER_ATTRIBUTION_REPORTING_SENT_REPORT_H_ +#ifndef CONTENT_BROWSER_ATTRIBUTION_REPORTING_SEND_RESULT_H_ +#define CONTENT_BROWSER_ATTRIBUTION_REPORTING_SEND_RESULT_H_ -#include "base/time/time.h" -#include "content/browser/attribution_reporting/attribution_report.h" #include "content/common/content_export.h" namespace content { // Struct that contains data about sent reports. Some info is displayed in the // Conversion Internals WebUI. -struct CONTENT_EXPORT SentReport { +struct CONTENT_EXPORT SendResult { enum class Status { kSent, // The report failed without receiving response headers. @@ -30,14 +28,13 @@ struct CONTENT_EXPORT SentReport { kRemovedFromQueue, }; - SentReport(AttributionReport report, Status status, int http_response_code); - SentReport(const SentReport& other); - SentReport& operator=(const SentReport& other); - SentReport(SentReport&& other); - SentReport& operator=(SentReport&& other); - ~SentReport(); - - AttributionReport report; + SendResult(Status status, int http_response_code) + : status(status), http_response_code(http_response_code) {} + SendResult(const SendResult& other) = default; + SendResult& operator=(const SendResult& other) = default; + SendResult(SendResult&& other) = default; + SendResult& operator=(SendResult&& other) = default; + ~SendResult() = default; Status status; @@ -50,4 +47,4 @@ struct CONTENT_EXPORT SentReport { } // namespace content -#endif // CONTENT_BROWSER_ATTRIBUTION_REPORTING_SENT_REPORT_H_ +#endif // CONTENT_BROWSER_ATTRIBUTION_REPORTING_SEND_RESULT_H_ diff --git a/content/browser/attribution_reporting/sent_report.cc b/content/browser/attribution_reporting/sent_report.cc deleted file mode 100644 index 0f77409bb8cbf2..00000000000000 --- a/content/browser/attribution_reporting/sent_report.cc +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2021 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "content/browser/attribution_reporting/sent_report.h" - -namespace content { - -SentReport::SentReport(AttributionReport report, - Status status, - int http_response_code) - : report(std::move(report)), - status(status), - http_response_code(http_response_code) {} - -SentReport::SentReport(const SentReport& other) = default; -SentReport& SentReport::operator=(const SentReport& other) = default; -SentReport::SentReport(SentReport&& other) = default; -SentReport& SentReport::operator=(SentReport&& other) = default; - -SentReport::~SentReport() = default; - -} // namespace content diff --git a/content/browser/back_forward_cache_features_browsertest.cc b/content/browser/back_forward_cache_features_browsertest.cc index 39ce9992abfc89..62aea2506cd3c5 100644 --- a/content/browser/back_forward_cache_features_browsertest.cc +++ b/content/browser/back_forward_cache_features_browsertest.cc @@ -2321,8 +2321,16 @@ IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest, // Tests that the short vibration sequence on the page stops after it enters // bfcache. +// http://crbug.com/1280741 +#if defined(OS_MAC) +#define MAYBE_ShortVibrationSequenceStopsAfterEnteringCache \ + DISABLED_ShortVibrationSequenceStopsAfterEnteringCache +#else +#define MAYBE_ShortVibrationSequenceStopsAfterEnteringCache \ + ShortVibrationSequenceStopsAfterEnteringCache +#endif IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest, - ShortVibrationSequenceStopsAfterEnteringCache) { + MAYBE_ShortVibrationSequenceStopsAfterEnteringCache) { ASSERT_TRUE(embedded_test_server()->Start()); TestVibrationManager vibration_manager; diff --git a/content/browser/devtools/protocol/devtools_protocol_browsertest.cc b/content/browser/devtools/protocol/devtools_protocol_browsertest.cc index cddf902532e10a..963313e0056eba 100644 --- a/content/browser/devtools/protocol/devtools_protocol_browsertest.cc +++ b/content/browser/devtools/protocol/devtools_protocol_browsertest.cc @@ -1022,7 +1022,10 @@ IN_PROC_BROWSER_TEST_F(DevToolsProtocolTest, PageCrashClearsPendingCommands) { EXPECT_THAT(console_messages_, ElementsAre("first page", "second page")); } -IN_PROC_BROWSER_TEST_F(DevToolsProtocolTest, NavigationPreservesMessages) { +// TODO(crbug.com/1280531): Disabled due to flakiness. Flaky on mac and linux +// la-cros +IN_PROC_BROWSER_TEST_F(DevToolsProtocolTest, + DISABLED_NavigationPreservesMessages) { ASSERT_TRUE(embedded_test_server()->Start()); GURL test_url = embedded_test_server()->GetURL("/devtools/navigation.html"); NavigateToURLBlockUntilNavigationsComplete(shell(), test_url, 1); diff --git a/content/browser/devtools/protocol/page_handler.cc b/content/browser/devtools/protocol/page_handler.cc index d48b48718e5eb3..b92684a51ab3f6 100644 --- a/content/browser/devtools/protocol/page_handler.cc +++ b/content/browser/devtools/protocol/page_handler.cc @@ -194,11 +194,14 @@ bool CanExecuteGlobalCommands( } // namespace -PageHandler::PageHandler(EmulationHandler* emulation_handler, - BrowserHandler* browser_handler, - bool allow_unsafe_operations) +PageHandler::PageHandler( + EmulationHandler* emulation_handler, + BrowserHandler* browser_handler, + bool allow_unsafe_operations, + absl::optional navigation_initiator_origin) : DevToolsDomainHandler(Page::Metainfo::domainName), allow_unsafe_operations_(allow_unsafe_operations), + navigation_initiator_origin_(navigation_initiator_origin), enabled_(false), screencast_enabled_(false), screencast_quality_(kDefaultScreenshotQuality), @@ -520,6 +523,15 @@ void PageHandler::Navigate(const std::string& url, params.referrer = Referrer(GURL(referrer.fromMaybe("")), policy); params.transition_type = type; params.frame_tree_node_id = frame_tree_node->frame_tree_node_id(); + if (navigation_initiator_origin_.has_value()) { + // When this agent has an initiator origin defined, ensure that its + // navigations are considered renderer-initiated by that origin, such that + // URL spoof defenses are in effect. (crbug.com/1192417) + params.is_renderer_initiated = true; + params.initiator_origin = *navigation_initiator_origin_; + params.source_site_instance = SiteInstance::CreateForURL( + host_->GetBrowserContext(), navigation_initiator_origin_->GetURL()); + } // Handler may be destroyed while navigating if the session // gets disconnected as a result of access checks. base::WeakPtr weak_self = weak_factory_.GetWeakPtr(); diff --git a/content/browser/devtools/protocol/page_handler.h b/content/browser/devtools/protocol/page_handler.h index e8a0df8ec17c52..1b7edbdc08243d 100644 --- a/content/browser/devtools/protocol/page_handler.h +++ b/content/browser/devtools/protocol/page_handler.h @@ -63,7 +63,8 @@ class PageHandler : public DevToolsDomainHandler, public: PageHandler(EmulationHandler* emulation_handler, BrowserHandler* browser_handler, - bool allow_unsafe_operations); + bool allow_unsafe_operations, + absl::optional navigation_initiator_origin); PageHandler(const PageHandler&) = delete; PageHandler& operator=(const PageHandler&) = delete; @@ -209,6 +210,7 @@ class PageHandler : public DevToolsDomainHandler, void OnDownloadDestroyed(download::DownloadItem* item) override; const bool allow_unsafe_operations_; + const absl::optional navigation_initiator_origin_; bool enabled_; bool bypass_csp_ = false; diff --git a/content/browser/devtools/render_frame_devtools_agent_host.cc b/content/browser/devtools/render_frame_devtools_agent_host.cc index 9aaf942476680b..fdb375746a766e 100644 --- a/content/browser/devtools/render_frame_devtools_agent_host.cc +++ b/content/browser/devtools/render_frame_devtools_agent_host.cc @@ -294,6 +294,14 @@ bool RenderFrameDevToolsAgentHost::AttachSession(DevToolsSession* session, if (!ShouldAllowSession(session)) return false; + if (frame_tree_node_ && !frame_tree_node_->parent() && + frame_tree_node_->is_on_initial_empty_document()) { + // Since the DevTools protocol can be used to modify the initial empty + // document of a tab, notify the browser that the pending URL shouldn't be + // displayed anymore to eliminate a URL spoof risk. + frame_host_->DidAccessInitialMainDocument(); + } + auto emulation_handler = std::make_unique(); protocol::EmulationHandler* emulation_handler_ptr = emulation_handler.get(); @@ -348,7 +356,8 @@ bool RenderFrameDevToolsAgentHost::AttachSession(DevToolsSession* session, GetId(), auto_attacher_.get(), session->GetRootSession())); session->AddHandler(std::make_unique( emulation_handler_ptr, browser_handler_ptr, - session->GetClient()->AllowUnsafeOperations())); + session->GetClient()->AllowUnsafeOperations(), + session->GetClient()->GetNavigationInitiatorOrigin())); session->AddHandler(std::make_unique()); if (!frame_tree_node_ || !frame_tree_node_->parent()) { session->AddHandler( diff --git a/content/browser/renderer_host/render_view_host_impl.cc b/content/browser/renderer_host/render_view_host_impl.cc index 2d918fcf473d6c..de1ab4c9a62add 100644 --- a/content/browser/renderer_host/render_view_host_impl.cc +++ b/content/browser/renderer_host/render_view_host_impl.cc @@ -678,10 +678,6 @@ RenderFrameHostImpl* RenderViewHostImpl::GetMainRenderFrameHost() { } void RenderViewHostImpl::ClosePage() { - // TODO(crbug.com/1161996): Remove this VLOG once the investigation is done. - VLOG(1) << "RenderViewHostImpl::ClosePage() IsRenderViewLive() = " - << IsRenderViewLive() - << ", SuddenTerminationAllowed() = " << SuddenTerminationAllowed(); is_waiting_for_page_close_completion_ = true; if (IsRenderViewLive() && !SuddenTerminationAllowed()) { diff --git a/content/public/browser/devtools_agent_host_client.cc b/content/public/browser/devtools_agent_host_client.cc index e6f7cd5ff78fd8..8d5455eab12f4f 100644 --- a/content/public/browser/devtools_agent_host_client.cc +++ b/content/public/browser/devtools_agent_host_client.cc @@ -39,4 +39,9 @@ bool DevToolsAgentHostClient::AllowUnsafeOperations() { return false; } +absl::optional +DevToolsAgentHostClient::GetNavigationInitiatorOrigin() { + return absl::nullopt; +} + } // namespace content diff --git a/content/public/browser/devtools_agent_host_client.h b/content/public/browser/devtools_agent_host_client.h index 221e9dafbc8ceb..cb13d3c6a19685 100644 --- a/content/public/browser/devtools_agent_host_client.h +++ b/content/public/browser/devtools_agent_host_client.h @@ -7,6 +7,8 @@ #include "base/containers/span.h" #include "content/common/content_export.h" +#include "third_party/abseil-cpp/absl/types/optional.h" +#include "url/origin.h" class GURL; namespace content { @@ -52,6 +54,13 @@ class CONTENT_EXPORT DevToolsAgentHostClient { // that are already privileged, such as local automation clients. virtual bool AllowUnsafeOperations(); + // A value to use as NavigationController::LoadURLParams::initiator_origin. + // If set, navigations would also be treated as renderer-initiated. + // This is useful e.g. for Chrome Extensions so that their calls to + // Page.navigate would be treated as renderer-initiated naviation subject to + // URL spoofing protection. + virtual absl::optional GetNavigationInitiatorOrigin(); + // Determines protocol message format. virtual bool UsesBinaryProtocol(); }; diff --git a/device/bluetooth/chromeos/bluetooth_utils.cc b/device/bluetooth/chromeos/bluetooth_utils.cc index b0ab4d9a449f27..02c5939c2d6b15 100644 --- a/device/bluetooth/chromeos/bluetooth_utils.cc +++ b/device/bluetooth/chromeos/bluetooth_utils.cc @@ -39,18 +39,6 @@ const char kSecurityKeyServiceUUID[] = "FFFD"; constexpr base::TimeDelta kMaxDeviceSelectionDuration = base::Seconds(30); -// This enum is tied directly to a UMA enum defined in -// //tools/metrics/histograms/enums.xml, and should always reflect it (do not -// change one without changing the other). -enum class BluetoothTransportType { - kUnknown = 0, - kClassic = 1, - kLE = 2, - kDual = 3, - kInvalid = 4, - kMaxValue = kInvalid -}; - // Get limited number of devices from |devices| and // prioritize paired/connecting devices over other devices. BluetoothAdapter::DeviceList GetLimitedNumDevices( diff --git a/device/bluetooth/chromeos/bluetooth_utils.h b/device/bluetooth/chromeos/bluetooth_utils.h index b78a3e4fce8ab0..d1ec8ae1917e01 100644 --- a/device/bluetooth/chromeos/bluetooth_utils.h +++ b/device/bluetooth/chromeos/bluetooth_utils.h @@ -98,6 +98,18 @@ enum class SetNicknameResult { kMaxValue = kSuccess, }; +// This enum is tied directly to a UMA enum defined in +// //tools/metrics/histograms/enums.xml, and should always reflect it (do not +// change one without changing the other). +enum class BluetoothTransportType { + kUnknown = 0, + kClassic = 1, + kLE = 2, + kDual = 3, + kInvalid = 4, + kMaxValue = kInvalid +}; + // Return filtered devices based on the filter type and max number of devices. DEVICE_BLUETOOTH_EXPORT device::BluetoothAdapter::DeviceList FilterBluetoothDeviceList(const BluetoothAdapter::DeviceList& devices, diff --git a/gin/gin_features.cc b/gin/gin_features.cc index ab83bb99fb3a19..84e42279760054 100644 --- a/gin/gin_features.cc +++ b/gin/gin_features.cc @@ -119,15 +119,10 @@ const base::Feature kV8SlowHistogramsSparkplugAndroid{ const base::Feature kV8SlowHistogramsScriptAblation{ "V8SlowHistogramsScriptAblation", base::FEATURE_DISABLED_BY_DEFAULT}; -// Enables the V8 virtual memory cage. -const base::Feature kV8VirtualMemoryCage { - "V8VirtualMemoryCage", -#if defined(V8_HEAP_SANDBOX) - // The cage is required for the V8 Heap Sandbox. - base::FEATURE_ENABLED_BY_DEFAULT -#else - base::FEATURE_DISABLED_BY_DEFAULT -#endif -}; +// Enables the experimental V8 sandbox. This is called V8VirtualMemoryCage +// instead of V8Sandbox for historical reasons. +// TODO(1218005) remove this once the finch trial has ended. +const base::Feature kV8VirtualMemoryCage{"V8VirtualMemoryCage", + base::FEATURE_DISABLED_BY_DEFAULT}; } // namespace features diff --git a/gin/v8_initializer.cc b/gin/v8_initializer.cc index f576fc909eed41..63992ebcd55890 100644 --- a/gin/v8_initializer.cc +++ b/gin/v8_initializer.cc @@ -352,29 +352,40 @@ void V8Initializer::Initialize(IsolateHolder::ScriptMode mode, // of the virtual memory cage, already use V8's random number generator. v8::V8::SetEntropySource(&GenerateEntropy); -#if defined(V8_VIRTUAL_MEMORY_CAGE) - static_assert(ARCH_CPU_64_BITS, - "V8 virtual memory cage can only work in 64-bit builds"); - // For now, creating the virtual memory cage is optional, and we only do it - // if the correpsonding feature is enabled. In the future, it will be - // mandatory when compiling with V8_VIRTUAL_MEMORY_CAGE. - bool v8_cage_is_initialized = false; - if (base::FeatureList::IsEnabled(features::kV8VirtualMemoryCage)) { - v8_cage_is_initialized = v8::V8::InitializeVirtualMemoryCage(); - - // Record the size of the virtual memory cage, in GB. The size will always - // be a power of two, so we use a sparse histogram to capture it. - // If the initialization failed, this API will return zero. - // The main reason for capturing this histogram here instead of having V8 - // do it is that there are no Isolates available yet, which are required - // for recording histograms in V8. - size_t size = v8::V8::GetVirtualMemoryCageSizeInBytes(); +#if defined(V8_SANDBOX) + static_assert(ARCH_CPU_64_BITS, "V8 sandbox can only work in 64-bit builds"); + // For now, initializing the sandbox is optional, and we only do it if the + // correpsonding feature is enabled. In the future, it will be mandatory when + // compiling with V8_SANDBOX. + // However, if V8 uses sandboxed pointers, then the sandbox must be + // initialized as sandboxed pointers are simply offsets inside the sandbox. +#if defined(V8_SANDBOXED_POINTERS) + bool must_initialize_sandbox = true; +#else + bool must_initialize_sandbox = false; +#endif + + bool v8_sandbox_is_initialized = false; + if (must_initialize_sandbox || + base::FeatureList::IsEnabled(features::kV8VirtualMemoryCage)) { + v8_sandbox_is_initialized = v8::V8::InitializeSandbox(); + CHECK(!must_initialize_sandbox || v8_sandbox_is_initialized); + + // Record the size of the sandbox, in GB. The size will always be a power + // of two, so we use a sparse histogram to capture it. If the + // initialization failed, this API will return zero. The main reason for + // capturing this histogram here instead of having V8 do it is that there + // are no Isolates available yet, which are required for recording + // histograms in V8. + size_t size = v8::V8::GetSandboxSizeInBytes(); int sizeInGB = size >> 30; DCHECK(base::bits::IsPowerOfTwo(size)); DCHECK(size == 0 || sizeInGB > 0); + // This uses the term "cage" instead of "sandbox" for historical reasons. + // TODO(1218005) remove this once the finch trial has ended. base::UmaHistogramSparse("V8.VirtualMemoryCageSizeGB", sizeInGB); } -#endif +#endif // V8_SANDBOX SetFlags(mode, js_command_line_flags); @@ -390,27 +401,28 @@ void V8Initializer::Initialize(IsolateHolder::ScriptMode mode, v8_is_initialized = true; -#if defined(V8_VIRTUAL_MEMORY_CAGE) - if (v8_cage_is_initialized) { +#if defined(V8_SANDBOX) + if (v8_sandbox_is_initialized) { // These values are persisted to logs. Entries should not be renumbered and // numeric values should never be reused. This should match enum // V8VirtualMemoryCageMode in \tools\metrics\histograms\enums.xml + // This uses the term "cage" instead of "sandbox" for historical reasons. + // TODO(1218005) remove this once the finch trial has ended. enum class VirtualMemoryCageMode { kSecure = 0, kInsecure = 1, kMaxValue = kInsecure, }; base::UmaHistogramEnumeration("V8.VirtualMemoryCageMode", - v8::V8::IsUsingSecureVirtualMemoryCage() + v8::V8::IsSandboxConfiguredSecurely() ? VirtualMemoryCageMode::kSecure : VirtualMemoryCageMode::kInsecure); - // When the virtual memory cage is enabled, ArrayBuffers must be located - // inside the cage. To achieve that, PA's ConfigurablePool is created inside - // the cage and Blink will create the ArrayBuffer partition inside that - // Pool if it is enabled. - v8::PageAllocator* cage_page_allocator = - v8::V8::GetVirtualMemoryCagePageAllocator(); + // When the sandbox is enabled, ArrayBuffers must be allocated inside of + // it. To achieve that, PA's ConfigurablePool is created inside the sandbox + // and Blink then creates the ArrayBuffer partition in that Pool. + v8::VirtualAddressSpace* sandbox_address_space = + v8::V8::GetSandboxAddressSpace(); const size_t max_pool_size = base::internal::PartitionAddressSpace::ConfigurablePoolMaxSize(); const size_t min_pool_size = @@ -421,7 +433,7 @@ void V8Initializer::Initialize(IsolateHolder::ScriptMode mode, // virtual memory is expensive on these OSes. if (base::win::GetVersion() < base::win::Version::WIN8_1) { // The size chosen here should be synchronized with the size of the - // virtual memory reservation for the V8 cage on these platforms. + // virtual memory reservation for the V8 sandbox on these platforms. // Currently, that is 8GB, of which 4GB are used for V8's pointer // compression region. // TODO(saelo) give this constant a proper name and maybe move it @@ -436,19 +448,19 @@ void V8Initializer::Initialize(IsolateHolder::ScriptMode mode, // the size on failure until it succeeds. void* pool_base = nullptr; while (!pool_base && pool_size >= min_pool_size) { - pool_base = cage_page_allocator->AllocatePages( - nullptr, pool_size, pool_size, v8::PageAllocator::kNoAccess); + pool_base = reinterpret_cast(sandbox_address_space->AllocatePages( + 0, pool_size, pool_size, v8::PagePermissions::kNoAccess)); if (!pool_base) { pool_size /= 2; } } - // The V8 cage is guaranteed to be large enough to host the pool. + // The V8 sandbox is guaranteed to be large enough to host the pool. CHECK(pool_base); base::internal::PartitionAddressSpace::InitConfigurablePool(pool_base, pool_size); // TODO(saelo) maybe record the size of the Pool into UMA. } -#endif +#endif // V8_SANDBOX } // static diff --git a/gpu/vulkan/generate_bindings.py b/gpu/vulkan/generate_bindings.py index 9c6b23d3d1926a..f62f5712914ada 100755 --- a/gpu/vulkan/generate_bindings.py +++ b/gpu/vulkan/generate_bindings.py @@ -345,7 +345,14 @@ def gen_content(func, suffix=''): pdecl += text + tail n = len(params) - callstat = 'return gpu::GetVulkanFunctionPointers()->%s(' % func + callstat = '' + if (func == 'vkQueueSubmit' or func == 'vkQueueWaitIdle' + or func == 'vkQueuePresentKHR'): + callstat = '''base::AutoLockMaybe auto_lock + (gpu::GetVulkanFunctionPointers()->per_queue_lock_map[queue].get()); + \n''' + + callstat += 'return gpu::GetVulkanFunctionPointers()->%s(' % func paramdecl = '(' if n > 0: paramnames = (''.join(t for t in p.itertext()) @@ -376,7 +383,9 @@ def GenerateHeaderFile(out_file): #include "base/compiler_specific.h" #include "base/component_export.h" +#include "base/containers/flat_map.h" #include "base/native_library.h" +#include "base/synchronization/lock.h" #include "build/build_config.h" #include "ui/gfx/extension_set.h" @@ -431,6 +440,12 @@ def GenerateHeaderFile(out_file): base::NativeLibrary vulkan_loader_library = nullptr; + // This is used to allow thread safe access to a given vulkan queue when + // multiple gpu threads are accessing it. Note that this map will be only + // accessed by multiple gpu threads concurrently to read the data, so it + // should be thread safe to use this map by multiple threads. + base::flat_map> per_queue_lock_map; + template class VulkanFunction; template diff --git a/gpu/vulkan/vulkan_device_queue.cc b/gpu/vulkan/vulkan_device_queue.cc index 19b824269cb098..663cf99b82cf4f 100644 --- a/gpu/vulkan/vulkan_device_queue.cc +++ b/gpu/vulkan/vulkan_device_queue.cc @@ -392,6 +392,20 @@ bool VulkanDeviceQueue::InitializeForCompositorGpuThread( VkQueue vk_queue, uint32_t vk_queue_index, gfx::ExtensionSet enabled_extensions) { + // Currently VulkanDeviceQueue for drdc thread(aka CompositorGpuThread) uses + // the same vulkan queue as the gpu main thread. Now since both gpu main and + // drdc threads would be accessing/submitting work to the same queue, all the + // queue access should be made thread safe. This is done by using locks. This + // lock is per |vk_queue|. Note that we are intentionally overwriting a + // previous lock if any. + // Since the map itself would be accessed by multiple gpu threads, we need to + // ensure that the access are thread safe. Here the locks are created and + // written into the map only when drdc thread is initialized which happens + // during GpuServiceImpl init. At this point none of the gpu threads would be + // doing read access until GpuServiceImpl init completed. Hence its safe to + // access map here. + GetVulkanFunctionPointers()->per_queue_lock_map[vk_queue] = + std::make_unique(); return InitCommon(vk_physical_device, vk_device, vk_queue, vk_queue_index, enabled_extensions); } @@ -410,6 +424,15 @@ void VulkanDeviceQueue::Destroy() { if (VK_NULL_HANDLE != owned_vk_device_) { vkDestroyDevice(owned_vk_device_, nullptr); owned_vk_device_ = VK_NULL_HANDLE; + + // Clear all the entries from this map since the device and hence all the + // generated queue(and their corresponding lock) from this device is + // destroyed. + // This happens when VulkanDeviceQueue is destroyed on gpu main thread + // during GpuServiceImpl destruction which happens after CompositorGpuThread + // is destroyed. Hence CompositorGpuThread would not be accessing the map at + // this point and its thread safe to delete map entries here. + GetVulkanFunctionPointers()->per_queue_lock_map.clear(); } vk_device_ = VK_NULL_HANDLE; vk_queue_ = VK_NULL_HANDLE; diff --git a/gpu/vulkan/vulkan_function_pointers.h b/gpu/vulkan/vulkan_function_pointers.h index 79716f25f5bd83..7df12786bcfc85 100644 --- a/gpu/vulkan/vulkan_function_pointers.h +++ b/gpu/vulkan/vulkan_function_pointers.h @@ -15,7 +15,9 @@ #include "base/compiler_specific.h" #include "base/component_export.h" +#include "base/containers/flat_map.h" #include "base/native_library.h" +#include "base/synchronization/lock.h" #include "build/build_config.h" #include "ui/gfx/extension_set.h" @@ -69,6 +71,12 @@ struct COMPONENT_EXPORT(VULKAN) VulkanFunctionPointers { base::NativeLibrary vulkan_loader_library = nullptr; + // This is used to allow thread safe access to a given vulkan queue when + // multiple gpu threads are accessing it. Note that this map will be only + // accessed by multiple gpu threads concurrently to read the data, so it + // should be thread safe to use this map by multiple threads. + base::flat_map> per_queue_lock_map; + template class VulkanFunction; template @@ -959,10 +967,16 @@ ALWAYS_INLINE VkResult vkQueueSubmit(VkQueue queue, uint32_t submitCount, const VkSubmitInfo* pSubmits, VkFence fence) { + base::AutoLockMaybe auto_lock( + gpu::GetVulkanFunctionPointers()->per_queue_lock_map[queue].get()); + return gpu::GetVulkanFunctionPointers()->vkQueueSubmit(queue, submitCount, pSubmits, fence); } ALWAYS_INLINE VkResult vkQueueWaitIdle(VkQueue queue) { + base::AutoLockMaybe auto_lock( + gpu::GetVulkanFunctionPointers()->per_queue_lock_map[queue].get()); + return gpu::GetVulkanFunctionPointers()->vkQueueWaitIdle(queue); } ALWAYS_INLINE VkResult vkResetCommandBuffer(VkCommandBuffer commandBuffer, @@ -1148,6 +1162,9 @@ ALWAYS_INLINE VkResult vkGetSwapchainImagesKHR(VkDevice device, } ALWAYS_INLINE VkResult vkQueuePresentKHR(VkQueue queue, const VkPresentInfoKHR* pPresentInfo) { + base::AutoLockMaybe auto_lock( + gpu::GetVulkanFunctionPointers()->per_queue_lock_map[queue].get()); + return gpu::GetVulkanFunctionPointers()->vkQueuePresentKHR(queue, pPresentInfo); } diff --git a/gpu/vulkan/vulkan_util.cc b/gpu/vulkan/vulkan_util.cc index eaf2533ac2bd67..cd9961b67151fd 100644 --- a/gpu/vulkan/vulkan_util.cc +++ b/gpu/vulkan/vulkan_util.cc @@ -11,6 +11,7 @@ #include "base/strings/string_split.h" #include "base/strings/string_util.h" #include "base/strings/stringprintf.h" +#include "base/trace_event/trace_event.h" #include "build/build_config.h" #include "gpu/config/gpu_info.h" // nogncheck #include "gpu/config/vulkan_info.h" @@ -32,6 +33,7 @@ #define GL_LAYOUT_DEPTH_ATTACHMENT_STENCIL_READ_ONLY_EXT 0x9531 namespace gpu { + namespace { #if defined(OS_ANDROID) @@ -147,6 +149,25 @@ VkResult CreateGraphicsPipelinesHook( pCreateInfos, pAllocator, pPipelines); } +VkResult VulkanQueueSubmitHook(VkQueue queue, + uint32_t submitCount, + const VkSubmitInfo* pSubmits, + VkFence fence) { + TRACE_EVENT0("gpu", "VulkanQueueSubmitHook"); + return vkQueueSubmit(queue, submitCount, pSubmits, fence); +} + +VkResult VulkanQueueWaitIdleHook(VkQueue queue) { + TRACE_EVENT0("gpu", "VulkanQueueWaitIdleHook"); + return vkQueueWaitIdle(queue); +} + +VkResult VulkanQueuePresentKHRHook(VkQueue queue, + const VkPresentInfoKHR* pPresentInfo) { + TRACE_EVENT0("gpu", "VulkanQueuePresentKHRHook"); + return vkQueuePresentKHR(queue, pPresentInfo); +} + bool CheckVulkanCompabilities(const VulkanInfo& vulkan_info, const GPUInfo& gpu_info, std::string enable_by_device_name) { diff --git a/gpu/vulkan/vulkan_util.h b/gpu/vulkan/vulkan_util.h index c2be5c90fcd0b5..f7f8b8c437c7bb 100644 --- a/gpu/vulkan/vulkan_util.h +++ b/gpu/vulkan/vulkan_util.h @@ -87,6 +87,23 @@ CreateGraphicsPipelinesHook(VkDevice device, const VkAllocationCallbacks* pAllocator, VkPipeline* pPipelines); +// Below vulkanQueue*Hook methods are used to ensure that Skia calls the correct +// version of those methods which are made thread safe by using locks. See +// vulkan_function_pointers.h vkQueue* method references for more details. +COMPONENT_EXPORT(VULKAN) +VKAPI_ATTR VkResult VKAPI_CALL +VulkanQueueSubmitHook(VkQueue queue, + uint32_t submitCount, + const VkSubmitInfo* pSubmits, + VkFence fence); + +COMPONENT_EXPORT(VULKAN) +VKAPI_ATTR VkResult VKAPI_CALL VulkanQueueWaitIdleHook(VkQueue queue); + +COMPONENT_EXPORT(VULKAN) +VKAPI_ATTR VkResult VKAPI_CALL +VulkanQueuePresentKHRHook(VkQueue queue, const VkPresentInfoKHR* pPresentInfo); + COMPONENT_EXPORT(VULKAN) bool CheckVulkanCompabilities(const VulkanInfo& vulkan_info, const GPUInfo& gpu_info, diff --git a/ios/chrome/app/memory_monitor.mm b/ios/chrome/app/memory_monitor.mm index fec47148d49030..af110b149a2a27 100644 --- a/ios/chrome/app/memory_monitor.mm +++ b/ios/chrome/app/memory_monitor.mm @@ -35,7 +35,6 @@ void UpdateMemoryValues() { base::BlockingType::WILL_BLOCK); const int free_memory = static_cast(base::SysInfo::AmountOfAvailablePhysicalMemory() / 1024); - crash_keys::SetCurrentFreeMemoryInKB(free_memory); NSURL* fileURL = [[NSURL alloc] initFileURLWithPath:NSHomeDirectory()]; NSDictionary* results = [fileURL resourceValuesForKeys:@[ @@ -48,9 +47,14 @@ void UpdateMemoryValues() { results[NSURLVolumeAvailableCapacityForImportantUsageKey]; free_disk_space_kilobytes = [available_bytes integerValue] / 1024; } - crash_keys::SetCurrentFreeDiskInKB(free_disk_space_kilobytes); - [[PreviousSessionInfo sharedInstance] - updateAvailableDeviceStorage:(NSInteger)free_disk_space_kilobytes]; + + // As a workaround to crbug.com/1247282, dispatch back to the main thread. + dispatch_async(dispatch_get_main_queue(), ^{ + crash_keys::SetCurrentFreeMemoryInKB(free_memory); + crash_keys::SetCurrentFreeDiskInKB(free_disk_space_kilobytes); + [[PreviousSessionInfo sharedInstance] + updateAvailableDeviceStorage:(NSInteger)free_disk_space_kilobytes]; + }); } // Invokes |UpdateMemoryValues| and schedules itself to be called after diff --git a/ios/chrome/browser/omaha/omaha_service.mm b/ios/chrome/browser/omaha/omaha_service.mm index 33b1f587ac9449..281c550cdcc09f 100644 --- a/ios/chrome/browser/omaha/omaha_service.mm +++ b/ios/chrome/browser/omaha/omaha_service.mm @@ -710,21 +710,24 @@ - (void)parser:(NSXMLParser*)parser } void OmahaService::PersistStates() { - NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults]; - - [defaults setDouble:next_tries_time_.ToCFAbsoluteTime() - forKey:kNextTriesTimesKey]; - [defaults setDouble:current_ping_time_.ToCFAbsoluteTime() - forKey:kCurrentPingKey]; - [defaults setDouble:last_sent_time_.ToCFAbsoluteTime() - forKey:kLastSentTimeKey]; - [defaults setInteger:number_of_tries_ forKey:kNumberTriesKey]; - [defaults setObject:base::SysUTF8ToNSString(last_sent_version_.GetString()) - forKey:kLastSentVersionKey]; - [defaults setInteger:last_server_date_ forKey:kLastServerDateKey]; - - // Save critical state information for usage reporting. - [defaults synchronize]; + // As a workaround to crbug.com/1247282, dispatch back to the main thread. + dispatch_async(dispatch_get_main_queue(), ^{ + NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults]; + + [defaults setDouble:next_tries_time_.ToCFAbsoluteTime() + forKey:kNextTriesTimesKey]; + [defaults setDouble:current_ping_time_.ToCFAbsoluteTime() + forKey:kCurrentPingKey]; + [defaults setDouble:last_sent_time_.ToCFAbsoluteTime() + forKey:kLastSentTimeKey]; + [defaults setInteger:number_of_tries_ forKey:kNumberTriesKey]; + [defaults setObject:base::SysUTF8ToNSString(last_sent_version_.GetString()) + forKey:kLastSentVersionKey]; + [defaults setInteger:last_server_date_ forKey:kLastServerDateKey]; + + // Save critical state information for usage reporting. + [defaults synchronize]; + }); } void OmahaService::OnURLLoadComplete( diff --git a/ios/chrome/browser/omaha/omaha_service_unittest.mm b/ios/chrome/browser/omaha/omaha_service_unittest.mm index d349e42ebd4e52..8b34c85a361c9a 100644 --- a/ios/chrome/browser/omaha/omaha_service_unittest.mm +++ b/ios/chrome/browser/omaha/omaha_service_unittest.mm @@ -12,6 +12,7 @@ #include "base/cxx17_backports.h" #include "base/run_loop.h" #include "base/strings/stringprintf.h" +#import "base/test/ios/wait_util.h" #include "components/metrics/metrics_pref_names.h" #include "components/prefs/pref_registry_simple.h" #include "components/version_info/version_info.h" @@ -701,6 +702,7 @@ void CleanService(OmahaService* service, base::Time now = base::Time::Now(); OmahaService service(false); service.StartInternal(); + base::test::ios::SpinRunLoopWithMinDelay(base::Milliseconds(1)); service.set_upgrade_recommended_callback(base::BindRepeating( &OmahaServiceTest::OnNeedUpdate, base::Unretained(this))); @@ -710,9 +712,11 @@ void CleanService(OmahaService* service, service.current_ping_time_ = now + base::Seconds(3); service.last_sent_version_ = base::Version(version_string); service.PersistStates(); + base::test::ios::SpinRunLoopWithMinDelay(base::Milliseconds(1)); OmahaService service2(false); service2.StartInternal(); + base::test::ios::SpinRunLoopWithMinDelay(base::Milliseconds(1)); EXPECT_EQ(service.number_of_tries_, 5); EXPECT_EQ(service2.last_sent_time_, now - base::Seconds(1)); diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_updater.h b/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_updater.h index 02fc22ba515c5f..4360bdc6b66bc6 100644 --- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_updater.h +++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_updater.h @@ -11,7 +11,6 @@ @class ContentSuggestionsSectionInformation; @class ContentSuggestionsViewController; @protocol ContentSuggestionsDataSource; -@protocol SnackbarCommands; @protocol SuggestedContent; // Enum defining the type of a ContentSuggestions. @@ -37,8 +36,6 @@ typedef NS_ENUM(NSInteger, ContentSuggestionType) { @property(nonatomic, weak) ContentSuggestionsViewController* collectionViewController; -@property(nonatomic, weak) id dispatcher; - // Returns whether the section should use the default, non-card style. - (BOOL)shouldUseCustomStyleForSection:(NSInteger)section; diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_updater.mm b/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_updater.mm index f44831380368f5..d8ecbcbed24379 100644 --- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_updater.mm +++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_updater.mm @@ -11,7 +11,6 @@ #include "components/strings/grit/components_strings.h" #import "ios/chrome/browser/ui/collection_view/collection_view_controller.h" #import "ios/chrome/browser/ui/collection_view/collection_view_model.h" -#import "ios/chrome/browser/ui/commands/snackbar_commands.h" #import "ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_header_item.h" #import "ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_text_item.h" #import "ios/chrome/browser/ui/content_suggestions/cells/suggested_content.h" @@ -105,9 +104,6 @@ SectionIdentifier SectionIdentifierForInfo( } } -NSString* const kContentSuggestionsCollectionUpdaterSnackbarCategory = - @"ContentSuggestionsCollectionUpdaterSnackbarCategory"; - } // namespace @interface ContentSuggestionsCollectionUpdater () @@ -134,7 +130,6 @@ @implementation ContentSuggestionsCollectionUpdater @synthesize promoAdded = _promoAdded; @synthesize sectionIdentifiersFromContentSuggestions = _sectionIdentifiersFromContentSuggestions; -@synthesize dispatcher = _dispatcher; - (instancetype)init { self = [super init]; diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_coordinator.mm b/ios/chrome/browser/ui/content_suggestions/content_suggestions_coordinator.mm index 4718e8c4108448..87b682ce3e0d1e 100644 --- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_coordinator.mm +++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_coordinator.mm @@ -225,9 +225,6 @@ - (void)start { setDataSource:self.contentSuggestionsMediator]; self.suggestionsViewController.suggestionCommandHandler = self.ntpMediator; self.suggestionsViewController.audience = self; - id dispatcher = - static_cast>(self.browser->GetCommandDispatcher()); - self.suggestionsViewController.dispatcher = dispatcher; self.suggestionsViewController.contentSuggestionsEnabled = self.contentSuggestionsEnabled; diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.h b/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.h index b406ab077b05dc..d9766a882b11be 100644 --- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.h +++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.h @@ -16,7 +16,6 @@ @protocol ContentSuggestionsHeaderControlling; @protocol ContentSuggestionsMenuProvider; @protocol ContentSuggestionsViewControllerAudience; -@protocol SnackbarCommands; @protocol SuggestedContent; // CollectionViewController to display the suggestions items. @@ -53,7 +52,6 @@ @property(nonatomic, weak) id menuProvider; - (void)setDataSource:(id)dataSource; -- (void)setDispatcher:(id)dispatcher; // Removes the entry at |indexPath|, from the collection and its model. - (void)dismissEntryAtIndexPath:(NSIndexPath*)indexPath; diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.mm b/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.mm index ca086bc3996cbe..e8ddcc6f6a9759 100644 --- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.mm +++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.mm @@ -74,10 +74,6 @@ - (void)setDataSource:(id)dataSource { self.collectionUpdater.dataSource = dataSource; } -- (void)setDispatcher:(id)dispatcher { - self.collectionUpdater.dispatcher = dispatcher; -} - - (void)dismissEntryAtIndexPath:(NSIndexPath*)indexPath { if (!indexPath || ![self.collectionViewModel hasItemAtIndexPath:indexPath]) { return; diff --git a/ios/chrome/browser/ui/follow/BUILD.gn b/ios/chrome/browser/ui/follow/BUILD.gn new file mode 100644 index 00000000000000..0e3c60589aea40 --- /dev/null +++ b/ios/chrome/browser/ui/follow/BUILD.gn @@ -0,0 +1,25 @@ +# Copyright 2021 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +source_set("utils") { + configs += [ "//build/config/compiler:enable_arc" ] + sources = [ + "follow_util.h", + "follow_util.mm", + ] + deps = [ + ":enums", + "//ios/chrome/browser/browser_state", + "//ios/chrome/browser/signin", + "//ios/chrome/browser/ui/ntp:feature_flags", + "//ios/web/public", + "//url", + ] +} + +source_set("enums") { + configs += [ "//build/config/compiler:enable_arc" ] + sources = [ "follow_action_state.h" ] + deps = [] +} diff --git a/ios/chrome/browser/ui/follow/follow_action_state.h b/ios/chrome/browser/ui/follow/follow_action_state.h new file mode 100644 index 00000000000000..dcc45ddf9be003 --- /dev/null +++ b/ios/chrome/browser/ui/follow/follow_action_state.h @@ -0,0 +1,21 @@ +// Copyright 2021 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef IOS_CHROME_BROWSER_UI_FOLLOW_FOLLOW_ACTION_STATE_H_ +#define IOS_CHROME_BROWSER_UI_FOLLOW_FOLLOW_ACTION_STATE_H_ + +#import + +// The state of the "Follow" action. e.g. The state the Follow button in the +// Overflow menu. +typedef NS_ENUM(NSInteger, FollowActionState) { + // "Follow" action is hidden. + FollowActionStateHidden, + // "Follow" action is shown but disabled. + FollowActionStateDisabled, + // "Follow" action is shown and enabled. + FollowActionStateEnabld, +}; + +#endif // IOS_CHROME_BROWSER_UI_FOLLOW_FOLLOW_ACTION_STATE_H_ diff --git a/ios/chrome/browser/ui/follow/follow_util.h b/ios/chrome/browser/ui/follow/follow_util.h new file mode 100644 index 00000000000000..b1f48f643d4c28 --- /dev/null +++ b/ios/chrome/browser/ui/follow/follow_util.h @@ -0,0 +1,20 @@ +// Copyright 2021 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef IOS_CHROME_BROWSER_UI_FOLLOW_FOLLOW_UTIL_H_ +#define IOS_CHROME_BROWSER_UI_FOLLOW_FOLLOW_UTIL_H_ + +#import "ios/chrome/browser/ui/follow/follow_action_state.h" + +namespace web { +class WebState; +} + +class ChromeBrowserState; + +// Returns the Follow action state for |webState| and |browserState|. +FollowActionState GetFollowActionState(web::WebState* webState, + ChromeBrowserState* browserState); + +#endif // IOS_CHROME_BROWSER_UI_FOLLOW_FOLLOW_UTIL_H_ diff --git a/ios/chrome/browser/ui/follow/follow_util.mm b/ios/chrome/browser/ui/follow/follow_util.mm new file mode 100644 index 00000000000000..e362b41a1d841f --- /dev/null +++ b/ios/chrome/browser/ui/follow/follow_util.mm @@ -0,0 +1,45 @@ +// Copyright 2021 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import "ios/chrome/browser/ui/follow/follow_util.h" + +#include "ios/chrome/browser/browser_state/chrome_browser_state.h" +#import "ios/chrome/browser/signin/authentication_service.h" +#import "ios/chrome/browser/signin/authentication_service_factory.h" +#import "ios/chrome/browser/ui/ntp/new_tab_page_feature.h" +#include "ios/web/public/web_client.h" +#import "ios/web/public/web_state.h" +#include "url/gurl.h" + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +FollowActionState GetFollowActionState(web::WebState* webState, + ChromeBrowserState* browserState) { + // This method should be called only if the feature flag has been enabled. + DCHECK(IsWebChannelsEnabled()); + + if (!webState) { + return FollowActionStateHidden; + } + const GURL& URL = webState->GetLastCommittedURL(); + // Show the follow action when: + // 1. The page url is valid; + // 2. Users are not on NTP or Chrome internal pages. + if (URL.is_valid() && !web::GetWebClient()->IsAppSpecificURL(URL)) { + AuthenticationService* authenticationService = + AuthenticationServiceFactory::GetForBrowserState(browserState); + // Enable the follow action when: + // 1. Users are not in incognito mode; + // 2. Users have signed in. + if (!browserState->IsOffTheRecord() && + authenticationService->GetPrimaryIdentity( + signin::ConsentLevel::kSignin)) { + return FollowActionStateEnabld; + } + return FollowActionStateDisabled; + } + return FollowActionStateHidden; +} diff --git a/media/BUILD.gn b/media/BUILD.gn index 81e70ad96eb331..732da9826667f0 100644 --- a/media/BUILD.gn +++ b/media/BUILD.gn @@ -37,6 +37,7 @@ buildflag_header("media_buildflags") { "ENABLE_HLS_DEMUXER=$enable_hls_demuxer", "ENABLE_LIBGAV1_DECODER=$enable_libgav1_decoder", "ENABLE_LIBRARY_CDMS=$enable_library_cdms", + "ENABLE_LIBAOM=$enable_libaom", "ENABLE_LIBVPX=$media_use_libvpx", "ENABLE_LOGGING_OVERRIDE=$enable_logging_override", "ENABLE_MEDIA_DRM_STORAGE=$enable_media_drm_storage", diff --git a/media/DEPS b/media/DEPS index 380bdc8ebf4edd..0cd29bc8f59373 100644 --- a/media/DEPS +++ b/media/DEPS @@ -17,6 +17,7 @@ include_rules = [ "+third_party/re2", "+third_party/dav1d", "+third_party/ffmpeg", + "+third_party/libaom", "+third_party/libdrm", "+third_party/libgav1", "+third_party/libvpx", diff --git a/media/gpu/v4l2/test/v4l2_ioctl_shim.cc b/media/gpu/v4l2/test/v4l2_ioctl_shim.cc index 861e22ca97ae9d..4a535a99abd4ec 100644 --- a/media/gpu/v4l2/test/v4l2_ioctl_shim.cc +++ b/media/gpu/v4l2/test/v4l2_ioctl_shim.cc @@ -49,7 +49,8 @@ static const std::unordered_map V4L2_REQUEST_CODE_AND_STRING(VIDIOC_STREAMON), V4L2_REQUEST_CODE_AND_STRING(VIDIOC_S_EXT_CTRLS), V4L2_REQUEST_CODE_AND_STRING(MEDIA_IOC_REQUEST_ALLOC), - V4L2_REQUEST_CODE_AND_STRING(MEDIA_REQUEST_IOC_QUEUE)}; + V4L2_REQUEST_CODE_AND_STRING(MEDIA_REQUEST_IOC_QUEUE), + V4L2_REQUEST_CODE_AND_STRING(MEDIA_REQUEST_IOC_REINIT)}; // Finds corresponding defined V4L2 request code name // for a given V4L2 request code value. @@ -251,7 +252,8 @@ bool V4L2IoctlShim::Ioctl(int request_code, int* arg) const { template <> bool V4L2IoctlShim::Ioctl(int request_code, int arg) const { - DCHECK(request_code == static_cast(MEDIA_REQUEST_IOC_QUEUE)); + DCHECK(request_code == static_cast(MEDIA_REQUEST_IOC_QUEUE) || + request_code == static_cast(MEDIA_REQUEST_IOC_REINIT)); const int ret = ioctl(arg, request_code); @@ -471,8 +473,6 @@ bool V4L2IoctlShim::SetExtCtrls( } bool V4L2IoctlShim::MediaIocRequestAlloc(int* media_request_fd) const { - // TODO(stevecho): need to use the file descriptor representing the request - // for MEDIA_REQUEST_IOC_QUEUE() call to queue to the request. LOG_ASSERT(media_request_fd != nullptr) << "|media_request_fd| check failed.\n"; @@ -495,6 +495,15 @@ bool V4L2IoctlShim::MediaRequestIocQueue( return ret; } +bool V4L2IoctlShim::MediaRequestIocReinit( + const std::unique_ptr& queue) const { + int req_fd = queue->media_request_fd(); + + const bool ret = Ioctl(MEDIA_REQUEST_IOC_REINIT, req_fd); + + return ret; +} + bool V4L2IoctlShim::QueryFormat(enum v4l2_buf_type type, uint32_t fourcc) const { struct v4l2_fmtdesc fmtdesc; diff --git a/media/gpu/v4l2/test/v4l2_ioctl_shim.h b/media/gpu/v4l2/test/v4l2_ioctl_shim.h index 4ce56c256dadbd..cc9dcfcfb4175b 100644 --- a/media/gpu/v4l2/test/v4l2_ioctl_shim.h +++ b/media/gpu/v4l2/test/v4l2_ioctl_shim.h @@ -174,6 +174,10 @@ class V4L2IoctlShim { bool MediaRequestIocQueue(const std::unique_ptr& queue) const WARN_UNUSED_RESULT; + // Re-initializes the previously allocated request for reuse. + bool MediaRequestIocReinit(const std::unique_ptr& queue) const + WARN_UNUSED_RESULT; + // Verifies |v4l_fd| supports |compressed_format| for OUTPUT queues // and |uncompressed_format| for CAPTURE queues, respectively. bool VerifyCapabilities(uint32_t compressed_format, diff --git a/media/gpu/v4l2/test/vp9_decoder.cc b/media/gpu/v4l2/test/vp9_decoder.cc index f50e2f191b0771..4e66fadf07c9ce 100644 --- a/media/gpu/v4l2/test/vp9_decoder.cc +++ b/media/gpu/v4l2/test/vp9_decoder.cc @@ -418,6 +418,9 @@ Vp9Decoder::Result Vp9Decoder::DecodeNextFrame() { // TODO(stevecho): call RefreshReferenceSlots() once decoded buffer is ready. + if (!v4l2_ioctl_->MediaRequestIocReinit(OUTPUT_queue_)) + LOG(ERROR) << "MEDIA_REQUEST_IOC_REINIT failed."; + return Vp9Decoder::kOk; } diff --git a/media/media_options.gni b/media/media_options.gni index 2f3ad542c22797..7462349a0f3a7f 100644 --- a/media/media_options.gni +++ b/media/media_options.gni @@ -9,6 +9,7 @@ import("//build/config/chromeos/ui_mode.gni") import("//build/config/features.gni") import("//media/gpu/args.gni") import("//testing/libfuzzer/fuzzer_test.gni") +import("//third_party/libaom/options.gni") import("//third_party/libgav1/options.gni") declare_args() { diff --git a/media/renderers/BUILD.gn b/media/renderers/BUILD.gn index 00083feb2db26d..e240678c18f007 100644 --- a/media/renderers/BUILD.gn +++ b/media/renderers/BUILD.gn @@ -76,6 +76,8 @@ source_set("renderers") { "win/media_foundation_source_wrapper.h", "win/media_foundation_stream_wrapper.cc", "win/media_foundation_stream_wrapper.h", + "win/media_foundation_texture_pool.cc", + "win/media_foundation_texture_pool.h", "win/media_foundation_video_stream.cc", "win/media_foundation_video_stream.h", ] @@ -146,6 +148,7 @@ source_set("unit_tests") { sources += [ "win/media_foundation_renderer_integration_test.cc", "win/media_foundation_renderer_unittest.cc", + "win/media_foundation_texture_pool_unittest.cc", ] deps += [ "//media/test:pipeline_integration_test_base" ] libs = [ diff --git a/media/renderers/win/media_foundation_renderer.cc b/media/renderers/win/media_foundation_renderer.cc index 3180b369a7266e..f0c8be50dadca3 100644 --- a/media/renderers/win/media_foundation_renderer.cc +++ b/media/renderers/win/media_foundation_renderer.cc @@ -27,6 +27,7 @@ #include "media/base/cdm_context.h" #include "media/base/media_log.h" #include "media/base/timestamp_constants.h" +#include "media/base/win/dxgi_device_manager.h" #include "media/base/win/mf_helpers.h" #include "media/base/win/mf_initializer.h" @@ -184,6 +185,30 @@ HRESULT MediaFoundationRenderer::CreateMediaEngine( if (dxgi_device_manager_) { RETURN_IF_FAILED(creation_attributes->SetUnknown( MF_MEDIA_ENGINE_DXGI_MANAGER, dxgi_device_manager_.Get())); + + // TODO(crbug.com/1276067): We'll investigate scenarios to see if we can use + // the on-screen video window size and not the native video size. + if (in_frame_server_mode_) { + gfx::Size max_video_size; + bool has_video = false; + for (auto* stream : media_resource->GetAllStreams()) { + if (stream->type() == media::DemuxerStream::VIDEO) { + has_video = true; + gfx::Size video_size = stream->video_decoder_config().natural_size(); + if (video_size.height() > max_video_size.height()) { + max_video_size.set_height(video_size.height()); + } + + if (video_size.width() > max_video_size.width()) { + max_video_size.set_width(video_size.width()); + } + } + } + + if (has_video) { + RETURN_IF_FAILED(InitializeTexturePool(max_video_size)); + } + } } RETURN_IF_FAILED( @@ -492,6 +517,29 @@ void MediaFoundationRenderer::SetOutputRect(const gfx::Rect& output_rect, std::move(callback).Run(true); } +HRESULT MediaFoundationRenderer::InitializeTexturePool(const gfx::Size& size) { + DXGIDeviceScopedHandle dxgi_device_handle(dxgi_device_manager_.Get()); + ComPtr d3d11_device = dxgi_device_handle.GetDevice(); + + if (d3d11_device.Get() == nullptr) { + return E_UNEXPECTED; + } + + // TODO(crbug.com/1276067): change |size| to instead use the required + // size of the output (for example if the video is only 1280x720 instead + // of a source frame of 1920x1080 we'd use the 1280x720 texture size). + // However we also need to investigate the scenario of WebGL and 360 video + // where they need the original frame size instead of the window size due + // to later image processing. + auto callback = [](std::vector frame_textures, + const gfx::Size& texture_size) {}; + + RETURN_IF_FAILED(texture_pool_.Initialize( + d3d11_device.Get(), base::BindRepeating(callback), size)); + + return S_OK; +} + HRESULT MediaFoundationRenderer::UpdateVideoStream(const gfx::Rect& rect) { ComPtr mf_media_engine_ex; RETURN_IF_FAILED(mf_media_engine_.As(&mf_media_engine_ex)); @@ -734,6 +782,10 @@ void MediaFoundationRenderer::OnVideoNaturalSizeChange() { ignore_result(UpdateVideoStream(test_rect)); } + if (in_frame_server_mode_) { + InitializeTexturePool(native_video_size_); + } + renderer_client_->OnVideoNaturalSizeChange(native_video_size_); } diff --git a/media/renderers/win/media_foundation_renderer.h b/media/renderers/win/media_foundation_renderer.h index 649336572bdc1a..64b20d7c04a900 100644 --- a/media/renderers/win/media_foundation_renderer.h +++ b/media/renderers/win/media_foundation_renderer.h @@ -29,6 +29,7 @@ #include "media/renderers/win/media_foundation_protection_manager.h" #include "media/renderers/win/media_foundation_renderer_extension.h" #include "media/renderers/win/media_foundation_source_wrapper.h" +#include "media/renderers/win/media_foundation_texture_pool.h" namespace media { @@ -119,7 +120,7 @@ class MEDIA_EXPORT MediaFoundationRenderer HRESULT SetSourceOnMediaEngine(); HRESULT UpdateVideoStream(const gfx::Rect& rect); HRESULT PauseInternal(); - + HRESULT InitializeTexturePool(const gfx::Size& size); void OnVideoNaturalSizeChange(); // Renderer methods are running in the same sequence. @@ -139,7 +140,7 @@ class MEDIA_EXPORT MediaFoundationRenderer Microsoft::WRL::ComPtr mf_media_engine_extension_; Microsoft::WRL::ComPtr mf_source_; // This enables MFMediaEngine to use hardware acceleration for video decoding - // and vdieo processing. + // and video processing. Microsoft::WRL::ComPtr dxgi_device_manager_; // Current duration of the media. @@ -172,6 +173,16 @@ class MEDIA_EXPORT MediaFoundationRenderer Microsoft::WRL::ComPtr content_protection_manager_; + // Texture pool of ID3D11Texture2D for the media engine to draw video frames + // when the media engine is in frame server mode instead of Direct + // Composition mode. + MediaFoundationTexturePool texture_pool_; + + // When in frame server mode we need to manage the DX textures and provide + // frames to the renderer. + // Disabled until we move + bool in_frame_server_mode_ = false; + // NOTE: Weak pointers must be invalidated before all other member variables. base::WeakPtrFactory weak_factory_{this}; }; diff --git a/media/renderers/win/media_foundation_texture_pool.cc b/media/renderers/win/media_foundation_texture_pool.cc new file mode 100644 index 00000000000000..8aeead3052bfb2 --- /dev/null +++ b/media/renderers/win/media_foundation_texture_pool.cc @@ -0,0 +1,130 @@ +// Copyright 2021 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include + +#include "media/base/win/mf_helpers.h" +#include "media/renderers/win/media_foundation_texture_pool.h" + +namespace { + +using Microsoft::WRL::ComPtr; + +// The Texture Count was determined empirically initially having a count of 30 +// and running many different video presentations in frame server mode and +// recording the number of textures in use and the count never exceeded 2. +// Therefore for a max of 2 in flight with the 3 being written requires that +// we allocate 3 textures. +constexpr int kTexturePoolCount = 3; + +} // namespace + +namespace media { + +MediaFoundationFrameInfo::MediaFoundationFrameInfo() = default; +MediaFoundationFrameInfo::~MediaFoundationFrameInfo() = default; +MediaFoundationFrameInfo::MediaFoundationFrameInfo( + MediaFoundationFrameInfo&& other) = default; + +MediaFoundationTexturePool::TextureInfo::TextureInfo() + : texture_in_use_(false) {} +MediaFoundationTexturePool::TextureInfo::~TextureInfo() = default; +MediaFoundationTexturePool::TextureInfo::TextureInfo(const TextureInfo& other) = + default; +MediaFoundationTexturePool::TextureInfo& +MediaFoundationTexturePool::TextureInfo::operator=( + const MediaFoundationTexturePool::TextureInfo& other) = default; + +MediaFoundationTexturePool::MediaFoundationTexturePool() = default; +MediaFoundationTexturePool::~MediaFoundationTexturePool() = default; + +// TODO(crbug.com/1278157): The pool should release the textures when the media +// engine is idling to save resources. +HRESULT MediaFoundationTexturePool::Initialize( + ID3D11Device* device, + FramePoolInitializedCallback frame_pool_cb, + const gfx::Size& frame_size) { + D3D11_TEXTURE2D_DESC desc{ + static_cast(frame_size.width()), + static_cast(frame_size.height()), + 1, + 1, + // TODO(crbug.com/1276134): Need to handle higher bit-depths like HDR. + DXGI_FORMAT_R8G8B8A8_UNORM, + {1, 0}, + D3D11_USAGE_DEFAULT, + D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE, + 0, + D3D11_RESOURCE_MISC_SHARED_NTHANDLE | + D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX}; + + std::vector frame_infos; + bool callback_is_valid = !frame_pool_cb.is_null(); + if (callback_is_valid) { + frame_infos.reserve(kTexturePoolCount); + } + + // We can be reinitialized so remove all the previous textures from our + // pool. + texture_pool_.clear(); + + for (int i = 0; i < kTexturePoolCount; ++i) { + auto texture_info_element = std::make_unique(); + auto texture_token = base::UnguessableToken::Create(); + + ComPtr d3d11_video_frame; + RETURN_IF_FAILED( + device->CreateTexture2D(&desc, nullptr, &d3d11_video_frame)); + SetDebugName(d3d11_video_frame.Get(), "Media_MFFrameServerMode_Pool"); + + ComPtr d3d11_video_frame_resource; + RETURN_IF_FAILED(d3d11_video_frame.As(&d3d11_video_frame_resource)); + + HANDLE shared_texture_handle; + RETURN_IF_FAILED(d3d11_video_frame_resource->CreateSharedHandle( + nullptr, DXGI_SHARED_RESOURCE_READ | DXGI_SHARED_RESOURCE_WRITE, + nullptr, &shared_texture_handle)); + + base::win::ScopedHandle scoped_shared_texture_handle; + scoped_shared_texture_handle.Set(shared_texture_handle); + shared_texture_handle = nullptr; + texture_pool_[texture_token].texture_ = std::move(d3d11_video_frame); + texture_pool_[texture_token].texture_in_use_ = false; + + if (callback_is_valid) { + MediaFoundationFrameInfo frame_info; + frame_info.dxgi_handle = std::move(scoped_shared_texture_handle); + frame_info.token = texture_token; + frame_infos.emplace_back(std::move(frame_info)); + } + } + + if (callback_is_valid) { + frame_pool_cb.Run(std::move(frame_infos), frame_size); + } + + return S_OK; +} + +ComPtr MediaFoundationTexturePool::AcquireTexture( + base::UnguessableToken* texture_token) { + for (auto& texture_item : texture_pool_) { + if (!texture_item.second.texture_in_use_) { + *texture_token = texture_item.first; + texture_item.second.texture_in_use_ = true; + return texture_item.second.texture_; + } + } + + return nullptr; +} + +void MediaFoundationTexturePool::ReleaseTexture( + const base::UnguessableToken& texture_token) { + if (texture_pool_.count(texture_token) > 0) { + texture_pool_.at(texture_token).texture_in_use_ = false; + } +} + +} // namespace media \ No newline at end of file diff --git a/media/renderers/win/media_foundation_texture_pool.h b/media/renderers/win/media_foundation_texture_pool.h new file mode 100644 index 00000000000000..fe0105554af6ea --- /dev/null +++ b/media/renderers/win/media_foundation_texture_pool.h @@ -0,0 +1,79 @@ +// Copyright 2021 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MEDIA_RENDERERS_WIN_MEDIA_FOUNDATION_TEXTURE_POOL_H_ +#define MEDIA_RENDERERS_WIN_MEDIA_FOUNDATION_TEXTURE_POOL_H_ + +#include +#include +#include + +#include "base/callback.h" +#include "base/containers/flat_map.h" +#include "base/unguessable_token.h" +#include "base/win/scoped_handle.h" +#include "media/base/media_export.h" +#include "ui/gfx/geometry/size.h" + +namespace media { + +struct MEDIA_EXPORT MediaFoundationFrameInfo { + MediaFoundationFrameInfo(); + ~MediaFoundationFrameInfo(); + MediaFoundationFrameInfo(MediaFoundationFrameInfo&& other); + base::win::ScopedHandle dxgi_handle; + base::UnguessableToken token; +}; + +using FramePoolInitializedCallback = base::RepeatingCallback frame_textures, + const gfx::Size& texture_size)>; + +// This object will create a pool D3D11Texture2Ds that the video frames in the +// Media Foundation Media Engine will draw to. By having this pool we don't +// need to create a D3D11Texture2D and associated Shared Image mailbox for +// each video frame. To coordinate the Shared Images in the +// MediaFoundationRendererClient each texture has a |texture_token| to signal +// which texture is ready to be displayed and which texture is ready for +// reuse. +class MEDIA_EXPORT MediaFoundationTexturePool { + public: + MediaFoundationTexturePool(); + ~MediaFoundationTexturePool(); + MediaFoundationTexturePool(const MediaFoundationTexturePool& other) = delete; + MediaFoundationTexturePool& operator=( + const MediaFoundationTexturePool& other) = delete; + + // Initializes the texture pool with a specific size. Once the textures are + // created the callback will be called passing the information about the + // textures. The method can be called multiple times, which will release the + // previously allocated textures and create a new set of textures on the + // device with the frame size. + // Any event that changes the frame size will be calling this method to + // change the texture size. The callback will eventually call into the Media + // Foundation Renderer which will create the Shared Images with the DX shared + // handle. Examples of callers are CreateMediaEngine and + // OnVideoNaturalSizeChange in the MediaFoundationRenderer + HRESULT Initialize(ID3D11Device* device, + FramePoolInitializedCallback frame_pool_cb, + const gfx::Size& frame_size); + Microsoft::WRL::ComPtr AcquireTexture( + base::UnguessableToken* texture_token); + void ReleaseTexture(const base::UnguessableToken& texture_token); + + private: + struct TextureInfo { + TextureInfo(); + ~TextureInfo(); + TextureInfo(const TextureInfo& other); + TextureInfo& operator=(const TextureInfo& other); + + Microsoft::WRL::ComPtr texture_; + bool texture_in_use_; + }; + + base::flat_map texture_pool_; +}; +} // namespace media +#endif \ No newline at end of file diff --git a/media/renderers/win/media_foundation_texture_pool_unittest.cc b/media/renderers/win/media_foundation_texture_pool_unittest.cc new file mode 100644 index 00000000000000..f4b7939a86f8ab --- /dev/null +++ b/media/renderers/win/media_foundation_texture_pool_unittest.cc @@ -0,0 +1,542 @@ +// Copyright 2021 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "media/renderers/win/media_foundation_texture_pool.h" + +#include "base/test/mock_callback.h" +#include "base/test/task_environment.h" +#include "media/base/mock_filters.h" +#include "media/base/test_helpers.h" +#include "media/base/win/test_utils.h" + +#include +#include +#include + +using Microsoft::WRL::ComPtr; + +namespace media { +class MockD3D11Texture2D; + +class MockD3D11Resource final : public IDXGIResource1 { + public: + MockD3D11Resource() {} + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, + void** ppvObject) override; + ULONG STDMETHODCALLTYPE AddRef(void) override { + return InterlockedIncrement(&refcount_); + } + + ULONG STDMETHODCALLTYPE Release(void) override { + ULONG refcount = InterlockedDecrement(&refcount_); + if (refcount == 0) { + refcount_ = 0xBAADF00D; + delete this; + } + return refcount; + } + + // IDXGIResource1 + HRESULT STDMETHODCALLTYPE + CreateSubresourceSurface(UINT index, IDXGISurface2** ppSurface) override { + return E_NOTIMPL; + } + HRESULT STDMETHODCALLTYPE + CreateSharedHandle(const SECURITY_ATTRIBUTES* pAttributes, + DWORD dwAccess, + LPCWSTR lpName, + HANDLE* pHandle) override; + + // IDXGIResource + HRESULT STDMETHODCALLTYPE GetSharedHandle(HANDLE* pSharedHandle) override { + return E_NOTIMPL; + } + HRESULT STDMETHODCALLTYPE GetUsage(DXGI_USAGE* pUsage) override { + return E_NOTIMPL; + } + HRESULT STDMETHODCALLTYPE + SetEvictionPriority(UINT eviction_priority) override; + HRESULT STDMETHODCALLTYPE + GetEvictionPriority(UINT* eviction_priority) override; + + // IDXGIDeviceSubObject + HRESULT STDMETHODCALLTYPE GetDevice(REFIID riid, void** ppDevice) override { + return E_NOTIMPL; + } + + // IDXGIObject + HRESULT STDMETHODCALLTYPE GetParent(REFIID riid, void** ppParent) override { + return E_NOTIMPL; + } + HRESULT STDMETHODCALLTYPE GetPrivateData(REFGUID guid, + UINT* pDataSize, + void* pData) override { + return E_NOTIMPL; + } + HRESULT STDMETHODCALLTYPE SetPrivateData(REFGUID guid, + UINT DataSize, + const void* pData) override { + return E_NOTIMPL; + } + HRESULT STDMETHODCALLTYPE + SetPrivateDataInterface(REFGUID guid, const IUnknown* pData) override; + + private: + MockD3D11Texture2D* parent_; + volatile ULONG refcount_ = 1; +}; + +class MockD3D11Texture2D final : public ID3D11Texture2D { + private: + MockD3D11Texture2D(const D3D11_TEXTURE2D_DESC* texture_description) + : resource_(new MockD3D11Resource()) { + memcpy(&texture_description_, texture_description, + sizeof(D3D11_TEXTURE2D_DESC)); + } + + public: + static HRESULT CreateInstance(const D3D11_TEXTURE2D_DESC* texture_description, + ID3D11Texture2D** texture2D); + // IUnknown + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, + void** ppvObject) override { + if (ppvObject == nullptr) { + return E_POINTER; + } + if (FAILED(QueryInterfaceInternal(riid, ppvObject))) { + if (resource_ != nullptr) + return resource_->QueryInterface(riid, ppvObject); + else + return E_NOINTERFACE; + } + + AddRef(); + return S_OK; + } + + HRESULT STDMETHODCALLTYPE QueryInterfaceInternal(REFIID riid, + void** ppvObject) { + if (ppvObject == nullptr) { + return E_POINTER; + } + + if (riid == IID_ID3D11Texture2D || riid == IID_IUnknown) { + *ppvObject = static_cast(this); + } else if (riid == IID_ID3D11Resource) { + *ppvObject = static_cast(this); + } else if (riid == IID_ID3D11DeviceChild) { + *ppvObject = static_cast(this); + } else { + return E_NOINTERFACE; + } + + AddRef(); + return S_OK; + } + + ULONG STDMETHODCALLTYPE AddRef(void) override { + return InterlockedIncrement(&refcount_); + } + + ULONG STDMETHODCALLTYPE Release(void) override { + ULONG refcount = InterlockedDecrement(&refcount_); + if (refcount == 0) { + refcount_ = 0xBAADF00D; + delete this; + } + return refcount; + } + + // ID3D11Texture2D + void STDMETHODCALLTYPE GetDesc(D3D11_TEXTURE2D_DESC* description) override { + memset(description, 0, sizeof(D3D11_TEXTURE2D_DESC)); + } + + // ID3D11Resource + void STDMETHODCALLTYPE + GetType(D3D11_RESOURCE_DIMENSION* resource_dimension) override { + *resource_dimension = + D3D11_RESOURCE_DIMENSION::D3D11_RESOURCE_DIMENSION_TEXTURE2D; + } + void STDMETHODCALLTYPE SetEvictionPriority(UINT eviction_priority) override { + eviction_priority_ = eviction_priority; + } + UINT STDMETHODCALLTYPE GetEvictionPriority() override { + return eviction_priority_; + } + + // ID3D11DeviceChild + void STDMETHODCALLTYPE GetDevice(ID3D11Device** ppDevice) override { + device_.CopyTo(ppDevice); + } + HRESULT STDMETHODCALLTYPE GetPrivateData(REFGUID guid, + UINT* pDataSize, + void* pData) override { + return E_NOTIMPL; + } + HRESULT STDMETHODCALLTYPE SetPrivateData(REFGUID guid, + UINT DataSize, + const void* pData) override { + return E_NOTIMPL; + } + HRESULT STDMETHODCALLTYPE + SetPrivateDataInterface(REFGUID guid, const IUnknown* pData) override { + // Our tests aren't checking for this right now + return S_OK; + } + + private: + volatile ULONG refcount_ = 1; + UINT eviction_priority_ = 0; + D3D11_TEXTURE2D_DESC texture_description_; + ComPtr device_; + ComPtr resource_; +}; + +class MockD3D11Device : public ID3D11Device { + public: + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, + void** ppvObject) override { + if (ppvObject == nullptr) { + return E_POINTER; + } + + if (riid == IID_ID3D11Device || riid == IID_IUnknown) { + *ppvObject = this; + return S_OK; + } + + return E_NOINTERFACE; + } + + ULONG STDMETHODCALLTYPE AddRef(void) override { return 1; } + ULONG STDMETHODCALLTYPE Release(void) override { return 1; } + + HRESULT STDMETHODCALLTYPE + CreateBuffer(const D3D11_BUFFER_DESC* pDesc, + const D3D11_SUBRESOURCE_DATA* pInitialData, + ID3D11Buffer** ppBuffer) override { + return E_NOTIMPL; + } + HRESULT STDMETHODCALLTYPE + CreateTexture1D(const D3D11_TEXTURE1D_DESC* pDesc, + const D3D11_SUBRESOURCE_DATA* pInitialData, + ID3D11Texture1D** ppTexture1D) override { + return E_NOTIMPL; + } + HRESULT STDMETHODCALLTYPE + CreateTexture2D(const D3D11_TEXTURE2D_DESC* pDesc, + const D3D11_SUBRESOURCE_DATA* pInitialData, + ID3D11Texture2D** ppTexture2D) override; + HRESULT STDMETHODCALLTYPE + CreateTexture3D(const D3D11_TEXTURE3D_DESC* pDesc, + const D3D11_SUBRESOURCE_DATA* pInitialData, + ID3D11Texture3D** ppTexture3D) override { + return E_NOTIMPL; + } + HRESULT STDMETHODCALLTYPE + CreateShaderResourceView(ID3D11Resource* pResource, + const D3D11_SHADER_RESOURCE_VIEW_DESC* pDesc, + ID3D11ShaderResourceView** ppSRView) override { + return E_NOTIMPL; + } + HRESULT STDMETHODCALLTYPE + CreateUnorderedAccessView(ID3D11Resource* pResource, + const D3D11_UNORDERED_ACCESS_VIEW_DESC* pDesc, + ID3D11UnorderedAccessView** ppUAView) override { + return E_NOTIMPL; + } + HRESULT STDMETHODCALLTYPE + CreateRenderTargetView(ID3D11Resource* pResource, + const D3D11_RENDER_TARGET_VIEW_DESC* pDesc, + ID3D11RenderTargetView** ppRTView) override { + return E_NOTIMPL; + } + HRESULT STDMETHODCALLTYPE + CreateDepthStencilView(ID3D11Resource* pResource, + const D3D11_DEPTH_STENCIL_VIEW_DESC* pDesc, + ID3D11DepthStencilView** ppDepthStencilView) override { + return E_NOTIMPL; + } + HRESULT STDMETHODCALLTYPE + CreateInputLayout(const D3D11_INPUT_ELEMENT_DESC* pInputElementDescs, + UINT NumElements, + const void* pShaderBytecodeWithInputSignature, + SIZE_T BytecodeLength, + ID3D11InputLayout** ppInputLayout) override { + return E_NOTIMPL; + } + HRESULT STDMETHODCALLTYPE + CreateVertexShader(const void* pShaderBytecode, + SIZE_T BytecodeLength, + ID3D11ClassLinkage* pClassLinkage, + ID3D11VertexShader** ppVertexShader) override { + return E_NOTIMPL; + } + HRESULT STDMETHODCALLTYPE + CreateGeometryShader(const void* pShaderBytecode, + SIZE_T BytecodeLength, + ID3D11ClassLinkage* pClassLinkage, + ID3D11GeometryShader** ppGeometryShader) override { + return E_NOTIMPL; + } + HRESULT STDMETHODCALLTYPE CreateGeometryShaderWithStreamOutput( + const void* pShaderBytecode, + SIZE_T BytecodeLength, + const D3D11_SO_DECLARATION_ENTRY* pSODeclaration, + UINT NumEntries, + const UINT* pBufferStrides, + UINT NumStrides, + UINT RasterizedStream, + ID3D11ClassLinkage* pClassLinkage, + ID3D11GeometryShader** ppGeometryShader) override { + return E_NOTIMPL; + } + HRESULT STDMETHODCALLTYPE + CreatePixelShader(const void* pShaderBytecode, + SIZE_T BytecodeLength, + ID3D11ClassLinkage* pClassLinkage, + ID3D11PixelShader** ppPixelShader) override { + return E_NOTIMPL; + } + HRESULT STDMETHODCALLTYPE + CreateHullShader(const void* pShaderBytecode, + SIZE_T BytecodeLength, + ID3D11ClassLinkage* pClassLinkage, + ID3D11HullShader** ppHullShader) override { + return E_NOTIMPL; + } + HRESULT STDMETHODCALLTYPE + CreateDomainShader(const void* pShaderBytecode, + SIZE_T BytecodeLength, + ID3D11ClassLinkage* pClassLinkage, + ID3D11DomainShader** ppDomainShader) override { + return E_NOTIMPL; + } + HRESULT STDMETHODCALLTYPE + CreateComputeShader(const void* pShaderBytecode, + SIZE_T BytecodeLength, + ID3D11ClassLinkage* pClassLinkage, + ID3D11ComputeShader** ppComputeShader) override { + return E_NOTIMPL; + } + HRESULT STDMETHODCALLTYPE + CreateClassLinkage(ID3D11ClassLinkage** ppLinkage) override { + return E_NOTIMPL; + } + HRESULT STDMETHODCALLTYPE + CreateBlendState(const D3D11_BLEND_DESC* pBlendStateDesc, + ID3D11BlendState** ppBlendState) override { + return E_NOTIMPL; + } + HRESULT STDMETHODCALLTYPE CreateDepthStencilState( + const D3D11_DEPTH_STENCIL_DESC* pDepthStencilDesc, + ID3D11DepthStencilState** ppDepthStencilState) override { + return E_NOTIMPL; + } + HRESULT STDMETHODCALLTYPE + CreateRasterizerState(const D3D11_RASTERIZER_DESC* pRasterizerDesc, + ID3D11RasterizerState** ppRasterizerState) override { + return E_NOTIMPL; + } + HRESULT STDMETHODCALLTYPE + CreateSamplerState(const D3D11_SAMPLER_DESC* pSamplerDesc, + ID3D11SamplerState** ppSamplerState) override { + return E_NOTIMPL; + } + HRESULT STDMETHODCALLTYPE CreateQuery(const D3D11_QUERY_DESC* pQueryDesc, + ID3D11Query** ppQuery) override { + return E_NOTIMPL; + } + HRESULT STDMETHODCALLTYPE + CreatePredicate(const D3D11_QUERY_DESC* pPredicateDesc, + ID3D11Predicate** ppPredicate) override { + return E_NOTIMPL; + } + HRESULT STDMETHODCALLTYPE + CreateCounter(const D3D11_COUNTER_DESC* pCounterDesc, + ID3D11Counter** ppCounter) override { + return E_NOTIMPL; + } + HRESULT STDMETHODCALLTYPE + CreateDeferredContext(UINT ContextFlags, + ID3D11DeviceContext** ppDeferredContext) override { + return E_NOTIMPL; + } + HRESULT STDMETHODCALLTYPE OpenSharedResource(HANDLE hResource, + REFIID ReturnedInterface, + void** ppResource) override { + return E_NOTIMPL; + } + HRESULT STDMETHODCALLTYPE CheckFormatSupport(DXGI_FORMAT Format, + UINT* pFormatSupport) override { + return E_NOTIMPL; + } + HRESULT STDMETHODCALLTYPE + CheckMultisampleQualityLevels(DXGI_FORMAT Format, + UINT SampleCount, + UINT* pNumQualityLevels) override { + return E_NOTIMPL; + } + void STDMETHODCALLTYPE + CheckCounterInfo(D3D11_COUNTER_INFO* pCounterInfo) override {} + HRESULT STDMETHODCALLTYPE CheckCounter(const D3D11_COUNTER_DESC* pDesc, + D3D11_COUNTER_TYPE* pType, + UINT* pActiveCounters, + LPSTR szName, + UINT* pNameLength, + LPSTR szUnits, + UINT* pUnitsLength, + LPSTR szDescription, + UINT* pDescriptionLength) override { + return E_NOTIMPL; + } + HRESULT STDMETHODCALLTYPE + CheckFeatureSupport(D3D11_FEATURE Feature, + void* pFeatureSupportData, + UINT FeatureSupportDataSize) override { + return E_NOTIMPL; + } + HRESULT STDMETHODCALLTYPE GetPrivateData(REFGUID guid, + UINT* pDataSize, + void* pData) override { + return E_NOTIMPL; + } + HRESULT STDMETHODCALLTYPE SetPrivateData(REFGUID guid, + UINT DataSize, + const void* pData) override { + return E_NOTIMPL; + } + HRESULT STDMETHODCALLTYPE + SetPrivateDataInterface(REFGUID guid, const IUnknown* pData) override { + return E_NOTIMPL; + } + D3D_FEATURE_LEVEL STDMETHODCALLTYPE GetFeatureLevel(void) override { + return D3D_FEATURE_LEVEL_11_1; + } + UINT STDMETHODCALLTYPE GetCreationFlags(void) override { return 0; } + HRESULT STDMETHODCALLTYPE GetDeviceRemovedReason(void) override { + return E_NOTIMPL; + } + void STDMETHODCALLTYPE + GetImmediateContext(ID3D11DeviceContext** ppImmediateContext) override {} + HRESULT STDMETHODCALLTYPE SetExceptionMode(UINT RaiseFlags) override { + return E_NOTIMPL; + } + UINT STDMETHODCALLTYPE GetExceptionMode(void) override { return 0; } +}; + +// MockD3D11Resource +HRESULT STDMETHODCALLTYPE MockD3D11Resource::QueryInterface(REFIID riid, + void** ppvObject) { + if (ppvObject == nullptr) { + return E_POINTER; + } + + if (riid == IID_IDXGIResource1) { + *ppvObject = static_cast(this); + } else if (riid == IID_IDXGIResource) { + *ppvObject = static_cast(this); + } else if (riid == IID_IDXGIDeviceSubObject) { + *ppvObject = static_cast(this); + } else if (riid == IID_IDXGIObject) { + *ppvObject = static_cast(this); + } else if (parent_ != nullptr) { + return parent_->QueryInterfaceInternal(riid, ppvObject); + } else { + return E_NOINTERFACE; + } + + AddRef(); + return S_OK; +} + +HRESULT STDMETHODCALLTYPE +MockD3D11Resource::SetEvictionPriority(UINT eviction_priority) { + parent_->SetEvictionPriority(eviction_priority); + return S_OK; +} +HRESULT STDMETHODCALLTYPE +MockD3D11Resource::GetEvictionPriority(UINT* eviction_priority) { + *eviction_priority = parent_->GetEvictionPriority(); + return S_OK; +} + +HRESULT STDMETHODCALLTYPE +MockD3D11Resource::SetPrivateDataInterface(REFGUID guid, + const IUnknown* pData) { + return parent_->SetPrivateDataInterface(guid, pData); +} + +HRESULT STDMETHODCALLTYPE +MockD3D11Resource::CreateSharedHandle(const SECURITY_ATTRIBUTES* pAttributes, + DWORD dwAccess, + LPCWSTR lpName, + HANDLE* pHandle) { + // Using an event to create a valid nt handle + *pHandle = CreateEvent(nullptr, FALSE, FALSE, nullptr); + if (*pHandle == nullptr) { + return HRESULT_FROM_WIN32(GetLastError()); + } + return S_OK; +} + +HRESULT MockD3D11Texture2D::CreateInstance( + const D3D11_TEXTURE2D_DESC* texture_description, + ID3D11Texture2D** texture2D) { + MockD3D11Texture2D* mock_texture = + new MockD3D11Texture2D(texture_description); + if (!mock_texture) { + return E_OUTOFMEMORY; + } + + *texture2D = mock_texture; + return S_OK; +} + +HRESULT STDMETHODCALLTYPE +MockD3D11Device::CreateTexture2D(const D3D11_TEXTURE2D_DESC* pDesc, + const D3D11_SUBRESOURCE_DATA* pInitialData, + ID3D11Texture2D** ppTexture2D) { + return MockD3D11Texture2D::CreateInstance(pDesc, ppTexture2D); +} + +class MediaFoundationTexturePoolTest : public testing::Test { + public: + MediaFoundationTexturePoolTest() {} + base::WeakPtrFactory weak_factory_{this}; +}; + +TEST_F(MediaFoundationTexturePoolTest, VerifyTextureInitialization) { + MockD3D11Device mock_d3d_device; + media::MediaFoundationTexturePool test; + base::WaitableEvent wait_event; + gfx::Size frame_size(1920, 1080); + + class SpecialCallback { + private: + base::WaitableEvent* wait_event_; + gfx::Size* frame_size_; + + public: + SpecialCallback(base::WaitableEvent* wait_event, gfx::Size* frame_size) + : wait_event_(wait_event), frame_size_(frame_size) {} + + void Invoke(std::vector frame_textures, + const gfx::Size& texture_size) { + EXPECT_EQ(texture_size.width(), frame_size_->width()); + EXPECT_EQ(texture_size.height(), frame_size_->height()); + wait_event_->Signal(); + } + } callback(&wait_event, &frame_size); + + EXPECT_HRESULT_SUCCEEDED( + test.Initialize(&mock_d3d_device, + base::BindRepeating(&SpecialCallback::Invoke, + base::Unretained(&callback)), + frame_size)); + wait_event.Wait(); +} + +} // namespace media \ No newline at end of file diff --git a/media/video/BUILD.gn b/media/video/BUILD.gn index a17b1df6569805..e2673903503fa6 100644 --- a/media/video/BUILD.gn +++ b/media/video/BUILD.gn @@ -76,6 +76,15 @@ source_set("video") { configs += [ "//media:subcomponent_config" ] + if (enable_libaom) { + sources += [ + "av1_video_encoder.cc", + "av1_video_encoder.h", + ] + + public_deps += [ "//third_party/libaom" ] + } + if (media_use_libvpx) { sources += [ "vpx_video_encoder.cc", diff --git a/media/video/av1_video_encoder.cc b/media/video/av1_video_encoder.cc new file mode 100644 index 00000000000000..c1a669f2d685b7 --- /dev/null +++ b/media/video/av1_video_encoder.cc @@ -0,0 +1,406 @@ +// Copyright 2021 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "media/video/av1_video_encoder.h" + +#include + +#include "base/cxx17_backports.h" +#include "base/logging.h" +#include "base/numerics/checked_math.h" +#include "base/strings/stringprintf.h" +#include "base/system/sys_info.h" +#include "base/time/time.h" +#include "base/trace_event/trace_event.h" +#include "media/base/bind_to_current_loop.h" +#include "media/base/svc_scalability_mode.h" +#include "media/base/timestamp_constants.h" +#include "media/base/video_frame.h" +#include "media/base/video_util.h" +#include "third_party/libaom/source/libaom/aom/aomcx.h" +#include "third_party/libyuv/include/libyuv/convert.h" + +namespace media { + +namespace { + +void FreeCodecCtx(aom_codec_ctx_t* codec_ctx) { + if (codec_ctx->name) { + // Codec has been initialized, we need to destroy it. + auto error = aom_codec_destroy(codec_ctx); + DCHECK_EQ(error, AOM_CODEC_OK); + } + delete codec_ctx; +} + +int GetNumberOfThreads(int width) { + // Default to 1 thread for less than VGA. + int desired_threads = 1; + + if (width >= 3840) + desired_threads = 16; + else if (width >= 2560) + desired_threads = 8; + else if (width >= 1280) + desired_threads = 4; + else if (width >= 640) + desired_threads = 2; + + // Clamp to the number of available logical processors/cores. + desired_threads = + std::min(desired_threads, base::SysInfo::NumberOfProcessors()); + + return desired_threads; +} + +Status SetUpAomConfig(const VideoEncoder::Options& opts, + aom_codec_enc_cfg_t* config) { + if (opts.frame_size.width() <= 0 || opts.frame_size.height() <= 0) + return Status(StatusCode::kEncoderUnsupportedConfig, + "Negative width or height values."); + + if (!opts.frame_size.GetCheckedArea().IsValid()) + return Status(StatusCode::kEncoderUnsupportedConfig, "Frame is too large."); + + config->g_profile = 0; // main + config->g_input_bit_depth = 8; + config->g_pass = AOM_RC_ONE_PASS; + config->g_lag_in_frames = 0; + config->rc_max_quantizer = 56; + config->rc_min_quantizer = 10; + config->rc_dropframe_thresh = 0; // Don't drop frames + + config->rc_undershoot_pct = 50; + config->rc_overshoot_pct = 50; + config->rc_buf_initial_sz = 600; + config->rc_buf_optimal_sz = 600; + config->rc_buf_sz = 1000; + config->g_error_resilient = 0; + + config->g_timebase.num = 1; + config->g_timebase.den = base::Time::kMicrosecondsPerSecond; + + // Set the number of threads based on the image width and num of cores. + config->g_threads = GetNumberOfThreads(opts.frame_size.width()); + + // Insert keyframes at will with a given max interval + if (opts.keyframe_interval.has_value()) { + config->kf_mode = AOM_KF_AUTO; + config->kf_min_dist = 0; + config->kf_max_dist = opts.keyframe_interval.value(); + } + + if (opts.bitrate.has_value()) { + auto& bitrate = opts.bitrate.value(); + config->rc_target_bitrate = bitrate.target() / 1000; + switch (bitrate.mode()) { + case Bitrate::Mode::kVariable: + config->rc_end_usage = AOM_VBR; + break; + case Bitrate::Mode::kConstant: + config->rc_end_usage = AOM_CBR; + break; + } + } else { + // Default that gives about 2mbps to HD video + config->rc_end_usage = AOM_VBR; + config->rc_target_bitrate = + int{(opts.frame_size.GetCheckedArea() * 2) + .ValueOrDefault(std::numeric_limits::max())}; + } + + config->g_w = opts.frame_size.width(); + config->g_h = opts.frame_size.height(); + + if (opts.scalability_mode.has_value()) { + return Status(StatusCode::kEncoderUnsupportedConfig, + "Unsupported number of temporal layers."); + } + return OkStatus(); +} + +} // namespace + +Av1VideoEncoder::Av1VideoEncoder() : codec_(nullptr, FreeCodecCtx) {} + +void Av1VideoEncoder::Initialize(VideoCodecProfile profile, + const Options& options, + OutputCB output_cb, + StatusCB done_cb) { + done_cb = BindToCurrentLoop(std::move(done_cb)); + if (codec_) { + std::move(done_cb).Run(StatusCode::kEncoderInitializeTwice); + return; + } + profile_ = profile; + if (profile < AV1PROFILE_MIN || profile > AV1PROFILE_MAX) { + auto status = Status(StatusCode::kEncoderUnsupportedProfile) + .WithData("profile", profile); + std::move(done_cb).Run(status); + return; + } + + // libaom is compiled with CONFIG_REALTIME_ONLY, so we can't use anything + // but AOM_USAGE_REALTIME. + auto error = aom_codec_enc_config_default(aom_codec_av1_cx(), &config_, + AOM_USAGE_REALTIME); + if (error != AOM_CODEC_OK) { + auto status = Status(StatusCode::kEncoderInitializationError, + "Failed to get default AOM config.") + .WithData("error_code", error); + std::move(done_cb).Run(status); + return; + } + + Status status = SetUpAomConfig(options, &config_); + if (!status.is_ok()) { + std::move(done_cb).Run(status); + return; + } + + // Initialize an encoder instance. + aom_codec_unique_ptr codec(new aom_codec_ctx_t, FreeCodecCtx); + codec->name = nullptr; + aom_codec_flags_t flags = 0; + error = aom_codec_enc_init(codec.get(), aom_codec_av1_cx(), &config_, flags); + if (error != AOM_CODEC_OK) { + status = Status(StatusCode::kEncoderInitializationError, + "aom_codec_enc_init() failed.") + .WithData("error_code", error) + .WithData("error_message", aom_codec_err_to_string(error)); + std::move(done_cb).Run(status); + return; + } + DCHECK_NE(codec->name, nullptr); + +#define CALL_AOM_CONTROL(key, value) \ + do { \ + error = aom_codec_control(codec.get(), (key), (value)); \ + if (error != AOM_CODEC_OK) { \ + status = Status(StatusCode::kEncoderInitializationError, \ + "Setting " #key " failed.") \ + .WithData("error_code", error) \ + .WithData("error_message", aom_codec_err_to_string(error)); \ + std::move(done_cb).Run(status); \ + return; \ + } \ + } while (false) + + CALL_AOM_CONTROL(AV1E_SET_ROW_MT, 1); + CALL_AOM_CONTROL(AV1E_SET_COEFF_COST_UPD_FREQ, 3); + CALL_AOM_CONTROL(AV1E_SET_MODE_COST_UPD_FREQ, 3); + CALL_AOM_CONTROL(AV1E_SET_MV_COST_UPD_FREQ, 3); + + CALL_AOM_CONTROL(AV1E_SET_ENABLE_TPL_MODEL, 0); + CALL_AOM_CONTROL(AV1E_SET_DELTAQ_MODE, 0); + CALL_AOM_CONTROL(AV1E_SET_ENABLE_ORDER_HINT, 0); + CALL_AOM_CONTROL(AV1E_SET_ENABLE_OBMC, 0); + CALL_AOM_CONTROL(AV1E_SET_ENABLE_WARPED_MOTION, 0); + CALL_AOM_CONTROL(AV1E_SET_ENABLE_GLOBAL_MOTION, 0); + CALL_AOM_CONTROL(AV1E_SET_ENABLE_REF_FRAME_MVS, 0); + + CALL_AOM_CONTROL(AV1E_SET_ENABLE_CFL_INTRA, 0); + CALL_AOM_CONTROL(AV1E_SET_ENABLE_SMOOTH_INTRA, 0); + CALL_AOM_CONTROL(AV1E_SET_ENABLE_ANGLE_DELTA, 0); + CALL_AOM_CONTROL(AV1E_SET_ENABLE_FILTER_INTRA, 0); + CALL_AOM_CONTROL(AV1E_SET_INTRA_DEFAULT_TX_ONLY, 1); + + if (config_.rc_end_usage == AOM_CBR) + CALL_AOM_CONTROL(AV1E_SET_AQ_MODE, 3); + + CALL_AOM_CONTROL(AV1E_SET_TILE_COLUMNS, + static_cast(std::log2(config_.g_threads))); + + // AOME_SET_CPUUSED determines tradeoff between video quality and compression + // time. Valid range: 0..10. 0 runs the slowest, and 10 runs the fastest. + // Values 6 to 9 are usually used for realtime applications. Here we choose + // two sides of realtime range for our 'realtime' and 'quality' modes + // because we don't want encoding speed to drop into single digit fps + // even in quality mode. + const int cpu_speed = + (options.latency_mode == VideoEncoder::LatencyMode::Realtime) ? 9 : 7; + CALL_AOM_CONTROL(AOME_SET_CPUUSED, cpu_speed); +#undef CALL_AOM_CONTROL + + options_ = options; + originally_configured_size_ = options.frame_size; + output_cb_ = BindToCurrentLoop(std::move(output_cb)); + codec_ = std::move(codec); + std::move(done_cb).Run(OkStatus()); +} + +void Av1VideoEncoder::Encode(scoped_refptr frame, + bool key_frame, + StatusCB done_cb) { + done_cb = BindToCurrentLoop(std::move(done_cb)); + if (!codec_) { + std::move(done_cb).Run(StatusCode::kEncoderInitializeNeverCompleted); + return; + } + + if (!frame) { + std::move(done_cb).Run(Status(StatusCode::kEncoderFailedEncode, + "No frame provided for encoding.")); + return; + } + + bool supported_format = frame->format() == PIXEL_FORMAT_NV12 || + frame->format() == PIXEL_FORMAT_I420 || + frame->format() == PIXEL_FORMAT_XBGR || + frame->format() == PIXEL_FORMAT_XRGB || + frame->format() == PIXEL_FORMAT_ABGR || + frame->format() == PIXEL_FORMAT_ARGB; + if ((!frame->IsMappable() && !frame->HasGpuMemoryBuffer()) || + !supported_format) { + Status status = + Status(StatusCode::kEncoderFailedEncode, "Unexpected frame format.") + .WithData("IsMappable", frame->IsMappable()) + .WithData("HasGpuMemoryBuffer", frame->HasGpuMemoryBuffer()) + .WithData("format", frame->format()); + std::move(done_cb).Run(std::move(status)); + return; + } + + if (frame->HasGpuMemoryBuffer()) { + frame = ConvertToMemoryMappedFrame(frame); + if (!frame) { + std::move(done_cb).Run( + Status(StatusCode::kEncoderFailedEncode, + "Convert GMB frame to MemoryMappedFrame failed.")); + return; + } + } + + const bool is_yuv = IsYuvPlanar(frame->format()); + if (frame->visible_rect().size() != options_.frame_size || !is_yuv) { + auto resized_frame = frame_pool_.CreateFrame( + is_yuv ? frame->format() : PIXEL_FORMAT_I420, options_.frame_size, + gfx::Rect(options_.frame_size), options_.frame_size, + frame->timestamp()); + Status status; + if (resized_frame) { + status = ConvertAndScaleFrame(*frame, *resized_frame, resize_buf_); + } else { + status = Status(StatusCode::kEncoderFailedEncode, + "Can't allocate a resized frame."); + } + if (!status.is_ok()) { + std::move(done_cb).Run(std::move(status)); + return; + } + frame = std::move(resized_frame); + } + + aom_image_t* image = aom_img_wrap( + &image_, AOM_IMG_FMT_I420, options_.frame_size.width(), + options_.frame_size.height(), 1, frame->data(VideoFrame::kYPlane)); + DCHECK_EQ(image, &image_); + + image->planes[AOM_PLANE_Y] = frame->visible_data(VideoFrame::kYPlane); + image->planes[AOM_PLANE_U] = frame->visible_data(VideoFrame::kUPlane); + image->planes[AOM_PLANE_V] = frame->visible_data(VideoFrame::kVPlane); + image->stride[AOM_PLANE_Y] = frame->stride(VideoFrame::kYPlane); + image->stride[AOM_PLANE_U] = frame->stride(VideoFrame::kUPlane); + image->stride[AOM_PLANE_V] = frame->stride(VideoFrame::kVPlane); + + auto duration_us = GetFrameDuration(*frame).InMicroseconds(); + last_frame_timestamp_ = frame->timestamp(); + if (last_frame_color_space_ != frame->ColorSpace()) { + last_frame_color_space_ = frame->ColorSpace(); + key_frame = true; + } + + TRACE_EVENT0("media", "aom_codec_encode"); + // Use artificial timestamps, so the encoder will not be misled by frame's + // fickle timestamps when doing rate control. + auto error = + aom_codec_encode(codec_.get(), image, artificial_timestamp_, duration_us, + key_frame ? AOM_EFLAG_FORCE_KF : 0); + artificial_timestamp_ += duration_us; + + if (error != AOM_CODEC_OK) { + auto msg = + base::StringPrintf("AOM encoding error: %s (%d)", + aom_codec_error_detail(codec_.get()), codec_->err); + DLOG(ERROR) << msg; + std::move(done_cb).Run(Status(StatusCode::kEncoderFailedEncode, msg)); + return; + } + DrainOutputs(frame->timestamp(), frame->ColorSpace()); + std::move(done_cb).Run(OkStatus()); +} + +void Av1VideoEncoder::ChangeOptions(const Options& options, + OutputCB output_cb, + StatusCB done_cb) { + done_cb = BindToCurrentLoop(std::move(done_cb)); + if (!codec_) { + std::move(done_cb).Run(StatusCode::kEncoderInitializeNeverCompleted); + return; + } + // TODO(crbug.com/1208280) Try to actually adjust setting instead of + // immediately dismissing configuration change. + std::move(done_cb).Run(StatusCode::kEncoderUnsupportedConfig); +} + +base::TimeDelta Av1VideoEncoder::GetFrameDuration(const VideoFrame& frame) { + // Frame has duration in metadata, use it. + if (frame.metadata().frame_duration.has_value()) + return frame.metadata().frame_duration.value(); + + // Options have framerate specified, use it. + if (options_.framerate.has_value()) + return base::Seconds(1.0 / options_.framerate.value()); + + // No real way to figure out duration, use time passed since the last frame + // as an educated guess, but clamp it within reasonable limits. + constexpr auto min_duration = base::Seconds(1.0 / 60.0); + constexpr auto max_duration = base::Seconds(1.0 / 24.0); + auto duration = frame.timestamp() - last_frame_timestamp_; + return base::clamp(duration, min_duration, max_duration); +} + +void Av1VideoEncoder::DrainOutputs(base::TimeDelta ts, + gfx::ColorSpace color_space) { + const aom_codec_cx_pkt_t* pkt = nullptr; + aom_codec_iter_t iter = nullptr; + while ((pkt = aom_codec_get_cx_data(codec_.get(), &iter)) != nullptr) { + if (pkt->kind != AOM_CODEC_CX_FRAME_PKT) + continue; + + VideoEncoderOutput result; + result.key_frame = (pkt->data.frame.flags & AOM_FRAME_IS_KEY) != 0; + result.timestamp = ts; + result.color_space = color_space; + result.size = pkt->data.frame.sz; + result.data.reset(new uint8_t[result.size]); + memcpy(result.data.get(), pkt->data.frame.buf, result.size); + output_cb_.Run(std::move(result), {}); + } +} + +Av1VideoEncoder::~Av1VideoEncoder() = default; + +void Av1VideoEncoder::Flush(StatusCB done_cb) { + done_cb = BindToCurrentLoop(std::move(done_cb)); + if (!codec_) { + std::move(done_cb).Run(StatusCode::kEncoderInitializeNeverCompleted); + return; + } + + auto error = aom_codec_encode(codec_.get(), nullptr, 0, 0, 0); + + if (error != AOM_CODEC_OK) { + auto msg = + base::StringPrintf("AOM encoding error: %s (%d)", + aom_codec_error_detail(codec_.get()), codec_->err); + DLOG(ERROR) << msg; + std::move(done_cb).Run(Status(StatusCode::kEncoderFailedEncode, msg)); + return; + } + DrainOutputs(base::TimeDelta(), gfx::ColorSpace()); + std::move(done_cb).Run(OkStatus()); +} + +} // namespace media diff --git a/media/video/av1_video_encoder.h b/media/video/av1_video_encoder.h new file mode 100644 index 00000000000000..3474ecc6c09b2a --- /dev/null +++ b/media/video/av1_video_encoder.h @@ -0,0 +1,67 @@ +// Copyright 2021 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MEDIA_VIDEO_AV1_VIDEO_ENCODER_H_ +#define MEDIA_VIDEO_AV1_VIDEO_ENCODER_H_ + +#include +#include + +#include "base/time/time.h" +#include "media/base/media_export.h" +#include "media/base/video_encoder.h" +#include "media/base/video_frame_pool.h" +#include "third_party/libaom/source/libaom/aom/aom_encoder.h" +#include "ui/gfx/color_space.h" +#include "ui/gfx/geometry/size.h" + +namespace media { + +class MEDIA_EXPORT Av1VideoEncoder : public VideoEncoder { + public: + Av1VideoEncoder(); + ~Av1VideoEncoder() override; + + // VideoDecoder implementation. + void Initialize(VideoCodecProfile profile, + const Options& options, + OutputCB output_cb, + StatusCB done_cb) override; + void Encode(scoped_refptr frame, + bool key_frame, + StatusCB done_cb) override; + void ChangeOptions(const Options& options, + OutputCB output_cb, + StatusCB done_cb) override; + void Flush(StatusCB done_cb) override; + + private: + base::TimeDelta GetFrameDuration(const VideoFrame& frame); + void DrainOutputs(base::TimeDelta ts, gfx::ColorSpace color_space); + + using aom_codec_unique_ptr = + std::unique_ptr; + + aom_codec_unique_ptr codec_; + aom_codec_enc_cfg_t config_; + aom_image_t image_ = {}; + + // This is a timestamp that is always increasing by frame's duration. + // It's used only for rate control and has nothing to do with timestamps + // coming from real frames. + aom_codec_pts_t artificial_timestamp_ = 0; + + gfx::Size originally_configured_size_; + base::TimeDelta last_frame_timestamp_; + gfx::ColorSpace last_frame_color_space_; + + VideoCodecProfile profile_ = VIDEO_CODEC_PROFILE_UNKNOWN; + VideoFramePool frame_pool_; + std::vector resize_buf_; + Options options_; + OutputCB output_cb_; +}; + +} // namespace media +#endif // MEDIA_VIDEO_AV1_VIDEO_ENCODER_H_ diff --git a/media/video/software_video_encoder_test.cc b/media/video/software_video_encoder_test.cc index 8e4ed08d1aaf38..70c9155364702c 100644 --- a/media/video/software_video_encoder_test.cc +++ b/media/video/software_video_encoder_test.cc @@ -39,6 +39,14 @@ #include "media/video/vpx_video_encoder.h" #endif +#if BUILDFLAG(ENABLE_LIBAOM) +#include "media/video/av1_video_encoder.h" +#endif + +#if BUILDFLAG(ENABLE_DAV1D_DECODER) +#include "media/filters/dav1d_video_decoder.h" +#endif + namespace media { struct SwVideoTestParams { @@ -84,6 +92,10 @@ class SoftwareVideoEncoderTest } else if (codec_ == VideoCodec::kVP9) { #if BUILDFLAG(ENABLE_LIBVPX) decoder_ = std::make_unique(); +#endif + } else if (codec_ == VideoCodec::kAV1) { +#if BUILDFLAG(ENABLE_DAV1D_DECODER) + decoder_ = std::make_unique(&media_log_); #endif } @@ -151,6 +163,12 @@ class SoftwareVideoEncoderTest std::unique_ptr CreateEncoder(VideoCodec codec) { switch (codec) { + case media::VideoCodec::kAV1: +#if BUILDFLAG(ENABLE_LIBAOM) + return std::make_unique(); +#else + return nullptr; +#endif case media::VideoCodec::kVP8: case media::VideoCodec::kVP9: #if BUILDFLAG(ENABLE_LIBVPX) @@ -844,6 +862,17 @@ INSTANTIATE_TEST_SUITE_P(VpxTemporalSvc, PrintTestParams); #endif // ENABLE_LIBVPX +#if BUILDFLAG(ENABLE_LIBAOM) +SwVideoTestParams kAv1Params[] = { + {VideoCodec::kAV1, AV1PROFILE_PROFILE_MAIN, PIXEL_FORMAT_I420}, + {VideoCodec::kAV1, AV1PROFILE_PROFILE_MAIN, PIXEL_FORMAT_XRGB}}; + +INSTANTIATE_TEST_SUITE_P(Av1Generic, + SoftwareVideoEncoderTest, + ::testing::ValuesIn(kAv1Params), + PrintTestParams); +#endif // ENABLE_LIBAOM + GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(H264VideoEncoderTest); GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(SVCVideoEncoderTest); GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(SoftwareVideoEncoderTest); diff --git a/services/network/public/cpp/corb/corb_impl.h b/services/network/public/cpp/corb/corb_impl.h index ea518d26aca4c3..c9b4a5f45a1513 100644 --- a/services/network/public/cpp/corb/corb_impl.h +++ b/services/network/public/cpp/corb/corb_impl.h @@ -119,6 +119,25 @@ class COMPONENT_EXPORT(NETWORK_CPP) CrossOriginReadBlocking { Decision HandleEndOfSniffableResponseBody() override; bool ShouldReportBlockedResponse() const override; + class ConfirmationSniffer; + class SimpleConfirmationSniffer; + + // Returns true if the response has a nosniff header. + static bool HasNoSniff(const network::mojom::URLResponseHead& response); + + private: + FRIEND_TEST_ALL_PREFIXES(CrossOriginReadBlockingTest, + SeemsSensitiveFromCORSHeuristic); + FRIEND_TEST_ALL_PREFIXES(CrossOriginReadBlockingTest, + SeemsSensitiveFromCacheHeuristic); + FRIEND_TEST_ALL_PREFIXES(CrossOriginReadBlockingTest, + SeemsSensitiveWithBothHeuristics); + FRIEND_TEST_ALL_PREFIXES(CrossOriginReadBlockingTest, + SupportsRangeRequests); + FRIEND_TEST_ALL_PREFIXES(content::CrossSiteDocumentResourceHandlerTest, + CORBProtectionLogging); + FRIEND_TEST_ALL_PREFIXES(ResponseAnalyzerTest, CORBProtectionLogging); + // true if either 1) ShouldBlockBasedOnHeaders decided to allow the response // based on headers alone or 2) ShouldBlockBasedOnHeaders decided to sniff // the response body and SniffResponseBody decided to allow the response @@ -142,31 +161,6 @@ class COMPONENT_EXPORT(NETWORK_CPP) CrossOriginReadBlocking { corb_protection_logging_needs_sniffing_; } - // The MIME type determined by ShouldBlockBasedOnHeaders. - const CrossOriginReadBlocking::MimeType& canonical_mime_type_for_testing() - const { - return canonical_mime_type_; - } - - class ConfirmationSniffer; - class SimpleConfirmationSniffer; - - // Returns true if the response has a nosniff header. - static bool HasNoSniff(const network::mojom::URLResponseHead& response); - - private: - FRIEND_TEST_ALL_PREFIXES(CrossOriginReadBlockingTest, - SeemsSensitiveFromCORSHeuristic); - FRIEND_TEST_ALL_PREFIXES(CrossOriginReadBlockingTest, - SeemsSensitiveFromCacheHeuristic); - FRIEND_TEST_ALL_PREFIXES(CrossOriginReadBlockingTest, - SeemsSensitiveWithBothHeuristics); - FRIEND_TEST_ALL_PREFIXES(CrossOriginReadBlockingTest, - SupportsRangeRequests); - FRIEND_TEST_ALL_PREFIXES(content::CrossSiteDocumentResourceHandlerTest, - CORBProtectionLogging); - FRIEND_TEST_ALL_PREFIXES(ResponseAnalyzerTest, CORBProtectionLogging); - // Helper for translating ShouldAllow(), ShouldBlock() and needs_sniffing() // into corb::Decision. Decision GetCorbDecision(); diff --git a/services/network/public/cpp/corb/corb_impl_unittest.cc b/services/network/public/cpp/corb/corb_impl_unittest.cc index 99faf46e1ae582..b06100aff65b9c 100644 --- a/services/network/public/cpp/corb/corb_impl_unittest.cc +++ b/services/network/public/cpp/corb/corb_impl_unittest.cc @@ -170,6 +170,7 @@ ::std::ostream& operator<<(::std::ostream& os, const TestScenario& scenario) { } return os << "\n description = " << scenario.description + << "\n source_line = " << scenario.source_line << "\n target_url = " << scenario.target_url << "\n initiator_origin = " << scenario.initiator_origin << "\n response_headers = " << response_headers_formatted @@ -1885,10 +1886,11 @@ class ResponseAnalyzerTest : public testing::Test, return response; } - // Instantiate and run CORB analyzer on the current scenario. Allow the - // analyzer to sniff the response body if needed and confirm it correctly - // decides to block or allow. - void RunAnalyzerOnScenario(const mojom::URLResponseHead& response) { + // Take and run ResponseAnalyzer on the current scenario. Allow the analyzer + // to sniff the response body if needed and confirm it correctly decides to + // block or allow. + void RunAnalyzerOnScenario(const mojom::URLResponseHead& response, + std::unique_ptr analyzer) { TestScenario scenario = GetParam(); // Initialize |request| from the parameters. std::unique_ptr request = @@ -1904,23 +1906,13 @@ class ResponseAnalyzerTest : public testing::Test, auto request_mode = cors_header_value == "" ? mojom::RequestMode::kNoCors : mojom::RequestMode::kCors; - // Create a ResponseAnalyzer to test. + // Initialize the `analyzer`. // - // The ResponseAnalyzer will be destructed when `analyzer` goes out of scope - // (the destructor triggers logging of UMAs that some callers of + // Note that the `analyzer` will be destructed when `analyzer` goes out of + // scope (the destructor may trigger logging of UMAs that some callers of // RunAnalyzerOnScenario attempt to verify). - auto analyzer = std::make_unique(); - analyzer->Init(request->url(), request->initiator(), request_mode, - response); - - // Verify MIME type was classified correctly. - EXPECT_EQ(scenario.canonical_mime_type, - analyzer->canonical_mime_type_for_testing()); - - // Verify that the verdict packet is >= 0 if CORB expects to sniff. - bool expected_to_sniff = - scenario.verdict_packet != kVerdictPacketForHeadersBasedVerdict; - ASSERT_EQ(expected_to_sniff, analyzer->needs_sniffing()); + ResponseAnalyzer::Decision decision = analyzer->Init( + request->url(), request->initiator(), request_mode, response); // This vector holds the packets to be delivered. std::vector packets_vector(scenario.packets); @@ -1931,84 +1923,78 @@ class ResponseAnalyzerTest : public testing::Test, // then the sniffing loop below will be skipped. EXPECT_LT(scenario.verdict_packet, static_cast(packets_vector.size())); - // If we don't expect to sniff then CORB should have already made a blockng - // decision based on the headers. - if (!expected_to_sniff) { - EXPECT_FALSE(analyzer->needs_sniffing()); + // Verify that the ResponseAnalyzer asks for sniffing if this is what the + // testcase expects. + bool expected_to_sniff = + scenario.verdict_packet != kVerdictPacketForHeadersBasedVerdict; + if (expected_to_sniff) { + EXPECT_EQ(decision, ResponseAnalyzer::Decision::kSniffMore); + } else { + // If we don't expect to sniff then ResponseAnalyzer should have already + // made a blockng decision based on the headers. if (scenario.verdict == Verdict::kBlock) { - ASSERT_FALSE(analyzer->ShouldAllow()); - ASSERT_TRUE(analyzer->ShouldBlock()); + EXPECT_EQ(decision, ResponseAnalyzer::Decision::kBlock); } else { - ASSERT_FALSE(analyzer->ShouldBlock()); - ASSERT_TRUE(analyzer->ShouldAllow()); + EXPECT_EQ(decision, ResponseAnalyzer::Decision::kAllow); } - return; } // Simulate the behaviour of the URLLoader by appending the packets into // |data_buffer| and feeding this to |analyzer|. - std::string data_buffer; - size_t data_offset = 0; - bool reached_final_packet = false; - for (int packet_index = 0; packet_index <= scenario.verdict_packet; - packet_index++) { - SCOPED_TRACE(testing::Message() - << "While delivering packet #" << packet_index); - - // At each iteration of the loop we feed a new packet to |analyzer|, - // breaking at the |verdict_packet|. Since we haven't given the next - // packet to |analyzer| yet at this point in the loop, it shouldn't have - // made a decision yet. - EXPECT_TRUE(analyzer->needs_sniffing()); - EXPECT_FALSE(analyzer->ShouldBlock()); - EXPECT_FALSE(analyzer->ShouldAllow()); - - // Append the next packet of the response body. If appending the entire - // packet would exceed net::kMaxBytesToSniff we truncate the data. - size_t bytes_to_append = strlen(packets_vector[packet_index]); - if (data_offset + bytes_to_append > net::kMaxBytesToSniff) - bytes_to_append = net::kMaxBytesToSniff - data_offset; - data_buffer.append(packets_vector[packet_index], bytes_to_append); - - // Hand |analyzer_| the data to sniff. - analyzer->Sniff(data_buffer); - data_offset += bytes_to_append; - - // If the latest packet was empty, or we reached net::kMaxBytesToSniff - // then sniffing should be over. Furthermore, if the |analyzer| hasn't - // decided to block or allow, then (in the real implementation) URLLoader - // will default to allowing. We check here that this occurs only when it - // is supposed to. - if ((bytes_to_append == 0 || data_offset == net::kMaxBytesToSniff)) { - reached_final_packet = true; - // Sanity check sniffing is over. - EXPECT_EQ(packet_index, scenario.verdict_packet); - // Check we have run out of data if and only if we expected to. - bool expected_to_run_out_of_data = - scenario.verdict == Verdict::kAllowBecauseOutOfData; - bool did_run_out_of_data = - !analyzer->ShouldAllow() && !analyzer->ShouldBlock(); - EXPECT_EQ(expected_to_run_out_of_data, did_run_out_of_data); + bool run_out_of_data_to_sniff = false; + if (decision == ResponseAnalyzer::Decision::kSniffMore) { + std::string data_buffer; + size_t data_offset = 0; + for (int packet_index = 0; packet_index <= scenario.verdict_packet; + packet_index++) { + SCOPED_TRACE(testing::Message() + << "While delivering packet #" << packet_index); + + // At each iteration of the loop we feed a new packet to |analyzer|, + // breaking at the |verdict_packet|. Since we haven't given the next + // packet to |analyzer| yet at this point in the loop, it shouldn't have + // made a decision yet. + EXPECT_EQ(decision, ResponseAnalyzer::Decision::kSniffMore); + + // Append the next packet of the response body. If appending the entire + // packet would exceed net::kMaxBytesToSniff we truncate the data. + size_t bytes_to_append = strlen(packets_vector[packet_index]); + if (data_offset + bytes_to_append > net::kMaxBytesToSniff) + bytes_to_append = net::kMaxBytesToSniff - data_offset; + data_buffer.append(packets_vector[packet_index], bytes_to_append); + + // Hand |analyzer_| the data to sniff. + decision = analyzer->Sniff(data_buffer); + data_offset += bytes_to_append; + if (decision != ResponseAnalyzer::Decision::kSniffMore) + break; } } + // Handle scenarios where no decision can be made before running out of data + // to sniff. + if (decision == ResponseAnalyzer::Decision::kSniffMore) { + run_out_of_data_to_sniff = true; + decision = analyzer->HandleEndOfSniffableResponseBody(); + + // HandleEndOfSniffableResponseBody should never return kSniffMore. + EXPECT_NE(decision, ResponseAnalyzer::Decision::kSniffMore); + } + // Confirm the analyzer is blocking or allowing correctly (now that we have // performed any needed sniffing). if (scenario.verdict == Verdict::kBlock) { - ASSERT_FALSE(analyzer->ShouldAllow()); - ASSERT_TRUE(analyzer->ShouldBlock()); + EXPECT_EQ(decision, ResponseAnalyzer::Decision::kBlock); } else { + EXPECT_EQ(decision, ResponseAnalyzer::Decision::kAllow); + // In this case either the |analyzer| has decided to allow the response, // or run out of data and so the response will be allowed by default. - ASSERT_FALSE(analyzer->ShouldBlock()); if (scenario.verdict == Verdict::kAllow) { - ASSERT_TRUE(analyzer->ShouldAllow()); + EXPECT_FALSE(run_out_of_data_to_sniff); } else { - // In this case |scenario.verdict| == Verdict::kAllowBecauseOutOfData, - // so double-check that sniffing actually occurred and failed. EXPECT_EQ(Verdict::kAllowBecauseOutOfData, scenario.verdict); - ASSERT_FALSE(analyzer->ShouldAllow()); - EXPECT_TRUE(reached_final_packet); + EXPECT_TRUE(run_out_of_data_to_sniff); } } } @@ -2039,7 +2025,7 @@ TEST_P(ResponseAnalyzerTest, ResponseBlocking) { scenario.initiator_origin); // Run the analyzer and confirm it allows/blocks correctly. - RunAnalyzerOnScenario(*response); + RunAnalyzerOnScenario(*response, std::make_unique()); // Verify that histograms are correctly incremented. base::HistogramTester::CountsMap expected_counts; @@ -2125,7 +2111,7 @@ TEST_P(ResponseAnalyzerTest, CORBProtectionLogging) { const bool expect_nosniff = CorbResponseAnalyzer::HasNoSniff(*response); // Run the analyzer and confirm it allows/blocks correctly. - RunAnalyzerOnScenario(*response); + RunAnalyzerOnScenario(*response, std::make_unique()); base::HistogramTester::CountsMap expected_counts; expected_counts["SiteIsolation.CORBProtection.SensitiveResource"] = 1; diff --git a/third_party/blink/renderer/core/css/resolver/style_adjuster.cc b/third_party/blink/renderer/core/css/resolver/style_adjuster.cc index 03f8dd081de1e2..fc69f68cab1eb6 100644 --- a/third_party/blink/renderer/core/css/resolver/style_adjuster.cc +++ b/third_party/blink/renderer/core/css/resolver/style_adjuster.cc @@ -874,14 +874,6 @@ void StyleAdjuster::AdjustComputedStyle(StyleResolverState& state, style.OverflowY() != EOverflow::kVisible) AdjustOverflow(style, element); - // overflow-clip-margin only applies if 'overflow: clip' is set along both - // axis or 'contain: paint'. - if (!style.ContainsPaint() && !(style.OverflowX() == EOverflow::kClip && - style.OverflowY() == EOverflow::kClip)) { - style.SetOverflowClipMargin( - ComputedStyleInitialValues::InitialOverflowClipMargin()); - } - if (StopPropagateTextDecorations(style, element)) style.ClearAppliedTextDecorations(); else diff --git a/third_party/blink/renderer/core/css/resolver/style_adjuster_test.cc b/third_party/blink/renderer/core/css/resolver/style_adjuster_test.cc index 10bb5dd920f315..0783d5c0983116 100644 --- a/third_party/blink/renderer/core/css/resolver/style_adjuster_test.cc +++ b/third_party/blink/renderer/core/css/resolver/style_adjuster_test.cc @@ -110,77 +110,6 @@ TEST_F(StyleAdjusterTest, TouchActionRestrictedByLowerAncestor) { target->GetComputedStyle()->GetEffectiveTouchAction()); } -TEST_F(StyleAdjusterTest, AdjustOverflow) { - ScopedOverflowClipForTest overflow_clip_feature_enabler(true); - GetDocument().SetBaseURLOverride(KURL("http://test.com")); - SetBodyInnerHTML(R"HTML( -
-
-
-
-
-
-
-
-
- )HTML"); - UpdateAllLifecyclePhasesForTest(); - - Element* target = GetDocument().getElementById("clipauto"); - ASSERT_TRUE(target); - EXPECT_EQ(EOverflow::kHidden, target->GetComputedStyle()->OverflowX()); - EXPECT_EQ(EOverflow::kAuto, target->GetComputedStyle()->OverflowY()); - EXPECT_EQ(LayoutUnit(), target->GetComputedStyle()->OverflowClipMargin()); - - target = GetDocument().getElementById("autoclip"); - ASSERT_TRUE(target); - EXPECT_EQ(EOverflow::kAuto, target->GetComputedStyle()->OverflowX()); - EXPECT_EQ(EOverflow::kHidden, target->GetComputedStyle()->OverflowY()); - EXPECT_EQ(LayoutUnit(), target->GetComputedStyle()->OverflowClipMargin()); - - target = GetDocument().getElementById("clipclip"); - ASSERT_TRUE(target); - EXPECT_EQ(EOverflow::kClip, target->GetComputedStyle()->OverflowX()); - EXPECT_EQ(EOverflow::kClip, target->GetComputedStyle()->OverflowY()); - EXPECT_EQ(LayoutUnit(1), target->GetComputedStyle()->OverflowClipMargin()); - - target = GetDocument().getElementById("visclip"); - ASSERT_TRUE(target); - EXPECT_EQ(EOverflow::kVisible, target->GetComputedStyle()->OverflowX()); - EXPECT_EQ(EOverflow::kClip, target->GetComputedStyle()->OverflowY()); - EXPECT_EQ(LayoutUnit(), target->GetComputedStyle()->OverflowClipMargin()); - - target = GetDocument().getElementById("clipvis"); - ASSERT_TRUE(target); - EXPECT_EQ(EOverflow::kClip, target->GetComputedStyle()->OverflowX()); - EXPECT_EQ(EOverflow::kVisible, target->GetComputedStyle()->OverflowY()); - EXPECT_EQ(LayoutUnit(), target->GetComputedStyle()->OverflowClipMargin()); - - target = GetDocument().getElementById("vishidden"); - ASSERT_TRUE(target); - EXPECT_EQ(EOverflow::kAuto, target->GetComputedStyle()->OverflowX()); - EXPECT_EQ(EOverflow::kHidden, target->GetComputedStyle()->OverflowY()); - EXPECT_EQ(LayoutUnit(), target->GetComputedStyle()->OverflowClipMargin()); - - target = GetDocument().getElementById("hiddenvis"); - ASSERT_TRUE(target); - EXPECT_EQ(EOverflow::kHidden, target->GetComputedStyle()->OverflowX()); - EXPECT_EQ(EOverflow::kAuto, target->GetComputedStyle()->OverflowY()); - EXPECT_EQ(LayoutUnit(), target->GetComputedStyle()->OverflowClipMargin()); - - target = GetDocument().getElementById("containpaint"); - ASSERT_TRUE(target); - EXPECT_TRUE(target->GetComputedStyle()->ContainsPaint()); - EXPECT_EQ(LayoutUnit(1), target->GetComputedStyle()->OverflowClipMargin()); -} - TEST_F(StyleAdjusterTest, TouchActionContentEditableArea) { base::test::ScopedFeatureList feature_list; feature_list.InitWithFeatures({::features::kSwipeToMoveCursor}, {}); diff --git a/third_party/blink/renderer/core/dom/document.cc b/third_party/blink/renderer/core/dom/document.cc index 3f1e73279435e7..6974ba265dbe9d 100644 --- a/third_party/blink/renderer/core/dom/document.cc +++ b/third_party/blink/renderer/core/dom/document.cc @@ -3740,9 +3740,6 @@ bool Document::DispatchBeforeUnloadEvent(ChromeClient* chrome_client, void Document::DispatchUnloadEvents( SecurityOrigin* committing_origin, absl::optional* unload_timing) { - // TODO(crbug.com/1161996): Remove this VLOG once the investigation is done. - VLOG(1) << "Document::DispatchUnloadEvents() URL = " << Url(); - PluginScriptForbiddenScope forbid_plugin_destructor_scripting; PageDismissalScope in_page_dismissal; if (parser_) @@ -3818,9 +3815,6 @@ void Document::DispatchUnloadEvents( GetFrame()->Loader().SaveScrollAnchor(); - // TODO(crbug.com/1161996): Remove this VLOG once the investigation is done. - VLOG(1) << "Actually dispatching an UnloadEvent: URL = " << Url(); - load_event_progress_ = kUnloadEventInProgress; Event& unload_event = *Event::Create(event_type_names::kUnload); const base::TimeTicks unload_event_start = base::TimeTicks::Now(); diff --git a/third_party/blink/renderer/core/frame/local_frame_mojo_handler.cc b/third_party/blink/renderer/core/frame/local_frame_mojo_handler.cc index 9c780940d821f3..2736937e6f7f57 100644 --- a/third_party/blink/renderer/core/frame/local_frame_mojo_handler.cc +++ b/third_party/blink/renderer/core/frame/local_frame_mojo_handler.cc @@ -1109,9 +1109,6 @@ void LocalFrameMojoHandler::ClosePage( mojom::blink::LocalMainFrame::ClosePageCallback completion_callback) { SECURITY_CHECK(frame_->IsMainFrame()); - // TODO(crbug.com/1161996): Remove this VLOG once the investigation is done. - VLOG(1) << "LocalFrame::ClosePage() URL = " << frame_->GetDocument()->Url(); - // There are two ways to close a page: // // 1/ Via webview()->Close() that currently sets the WebView's delegate_ to diff --git a/third_party/blink/renderer/core/layout/layout_inline_test.cc b/third_party/blink/renderer/core/layout/layout_inline_test.cc index 1000e0c3f36609..df1490cf503796 100644 --- a/third_party/blink/renderer/core/layout/layout_inline_test.cc +++ b/third_party/blink/renderer/core/layout/layout_inline_test.cc @@ -719,7 +719,9 @@ TEST_P(ParameterizedLayoutInlineTest, GetLayoutObjectByElementId("target5") ->AbsoluteBoundingBoxRectHandlingEmptyInline()); // This rect covers the overflowing images and continuations. - EXPECT_EQ(PhysicalRect(390, 70, 160, 100), + const int height = + RuntimeEnabledFeatures::LayoutNGBlockInInlineEnabled() ? 400 : 100; + EXPECT_EQ(PhysicalRect(390, 70, 160, height), GetLayoutObjectByElementId("target6") ->AbsoluteBoundingBoxRectHandlingEmptyInline()); } diff --git a/third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm.cc index 83d0d6b5f1aec9..c48f09394e5d33 100644 --- a/third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm.cc +++ b/third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm.cc @@ -3433,7 +3433,7 @@ void NGGridLayoutAlgorithm::PlaceGridItemsForFragmentation( wtf_size_t breakpoint_row_set_index; bool has_subsequent_children; - const LayoutUnit fragmentainer_space = + LayoutUnit fragmentainer_space = FragmentainerSpaceAtBfcStart(ConstraintSpace()); const LayoutUnit previous_consumed_block_size = BreakToken() ? BreakToken()->ConsumedBlockSize() : LayoutUnit(); @@ -3633,12 +3633,20 @@ void NGGridLayoutAlgorithm::PlaceGridItemsForFragmentation( auto ShiftBreakpointIntoNextFragmentainer = [&]() -> bool { if (breakpoint_row_set_index == kNotFound) return false; - DCHECK_NE(fragmentainer_space, kIndefiniteSize); const LayoutUnit fragment_relative_row_offset = grid_geometry->row_geometry.sets[breakpoint_row_set_index].offset + (*row_offset_adjustments)[breakpoint_row_set_index] - previous_consumed_block_size; + + // We may be within the initial column-balancing pass (where we have an + // indefinite fragmentainer size). If we have a forced break, re-run + // |PlaceItems()| assuming the breakpoint offset is the fragmentainer size. + if (fragmentainer_space == kIndefiniteSize) { + fragmentainer_space = fragment_relative_row_offset; + return true; + } + const LayoutUnit row_offset_delta = fragmentainer_space - fragment_relative_row_offset; diff --git a/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.cc b/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.cc index ad8c08aa00aa3c..4d6f909d8bc02b 100644 --- a/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.cc +++ b/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.cc @@ -713,9 +713,6 @@ PhysicalRect NGPhysicalBoxFragment::InkOverflow() const { return self_rect; const OverflowClipAxes overflow_clip_axes = GetOverflowClipAxes(); - // overflow_clip_margin should only be set if we clip both axes. - DCHECK(overflow_clip_axes == kOverflowClipBothAxis || - !style.OverflowClipMargin()); if (overflow_clip_axes == kNoOverflowClip) { return UnionRect(self_rect, ink_overflow_.Contents(InkOverflowType(), Size())); diff --git a/third_party/blink/renderer/modules/beacon/navigator_beacon.cc b/third_party/blink/renderer/modules/beacon/navigator_beacon.cc index f2e4e2351bd137..003327ed9edec8 100644 --- a/third_party/blink/renderer/modules/beacon/navigator_beacon.cc +++ b/third_party/blink/renderer/modules/beacon/navigator_beacon.cc @@ -78,16 +78,9 @@ bool NavigatorBeacon::SendBeaconImpl( ExecutionContext* execution_context = ExecutionContext::From(script_state); KURL url = execution_context->CompleteURL(url_string); if (!CanSendBeacon(execution_context, url, exception_state)) { - // TODO(crbug.com/1161996): Remove this VLOG once the investigation is done. - VLOG(1) << "Cannot send a beacon to " << url.ElidedString() - << ", initiator = " << execution_context->Url(); return false; } - // TODO(crbug.com/1161996): Remove this VLOG once the investigation is done. - VLOG(1) << "Send a beacon to " << url.ElidedString() - << ", initiator = " << execution_context->Url(); - bool allowed; LocalFrame* frame = GetSupplementable()->DomWindow()->GetFrame(); if (data) { diff --git a/third_party/blink/renderer/modules/webcodecs/video_encoder.cc b/third_party/blink/renderer/modules/webcodecs/video_encoder.cc index a8d1bd2dc51a89..27c03676e317c3 100644 --- a/third_party/blink/renderer/modules/webcodecs/video_encoder.cc +++ b/third_party/blink/renderer/modules/webcodecs/video_encoder.cc @@ -69,6 +69,10 @@ #include "third_party/blink/renderer/platform/scheduler/public/thread.h" #include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h" +#if BUILDFLAG(ENABLE_LIBAOM) +#include "media/video/av1_video_encoder.h" +#endif + #if BUILDFLAG(ENABLE_OPENH264) #include "media/video/openh264_video_encoder.h" #endif @@ -287,9 +291,41 @@ VideoEncoderTraits::ParsedConfig* ParseConfigStatic( return result; } +const base::Feature kWebCodecsAv1Encoding{"WebCodecsAv1Encoding", + base::FEATURE_DISABLED_BY_DEFAULT}; + bool VerifyCodecSupportStatic(VideoEncoderTraits::ParsedConfig* config, ExceptionState* exception_state) { switch (config->codec) { + case media::VideoCodec::kAV1: + if (!base::FeatureList::IsEnabled(kWebCodecsAv1Encoding)) { + if (exception_state) { + exception_state->ThrowDOMException( + DOMExceptionCode::kNotSupportedError, + "AV1 encoding is not supported yet."); + } + return false; + } + + if (config->options.scalability_mode.has_value()) { + if (exception_state) { + exception_state->ThrowDOMException( + DOMExceptionCode::kNotSupportedError, + "SVC encoding is not supported for AV1 yet."); + } + return false; + } + if (config->profile != + media::VideoCodecProfile::AV1PROFILE_PROFILE_MAIN) { + if (exception_state) { + exception_state->ThrowDOMException( + DOMExceptionCode::kNotSupportedError, "Unsupported av1 profile."); + } + return false; + } + // TODO(crbug.com/1208280): Check for supported AV1 levels + break; + case media::VideoCodec::kVP8: break; @@ -455,6 +491,14 @@ VideoEncoder::CreateAcceleratedVideoEncoder( callback_runner_)); } +std::unique_ptr CreateAv1VideoEncoder() { +#if BUILDFLAG(ENABLE_LIBAOM) + return std::make_unique(); +#else + return nullptr; +#endif // BUILDFLAG(ENABLE_LIBAOM) +} + std::unique_ptr CreateVpxVideoEncoder() { #if BUILDFLAG(ENABLE_LIBVPX) return std::make_unique(); @@ -480,6 +524,10 @@ std::unique_ptr VideoEncoder::CreateSoftwareVideoEncoder( return nullptr; std::unique_ptr result; switch (codec) { + case media::VideoCodec::kAV1: + result = CreateAv1VideoEncoder(); + self->UpdateEncoderLog("Av1VideoEncoder", false); + break; case media::VideoCodec::kVP8: case media::VideoCodec::kVP9: result = CreateVpxVideoEncoder(); diff --git a/third_party/blink/web_tests/FlagExpectations/disable-layout-ng b/third_party/blink/web_tests/FlagExpectations/disable-layout-ng index 0bdb559cab2066..a8925005d78807 100644 --- a/third_party/blink/web_tests/FlagExpectations/disable-layout-ng +++ b/third_party/blink/web_tests/FlagExpectations/disable-layout-ng @@ -13,6 +13,7 @@ # Tests that fail in legacy but pass in NG # ====== New tests from wpt-importer added here ====== +crbug.com/626703 external/wpt/css/css-overflow/scrollbar-gutter-vertical-lr-002.html [ Failure ] crbug.com/626703 external/wpt/css/css-writing-modes/inline-box-orthogonal-child-with-margins.html [ Failure ] crbug.com/626703 virtual/system-color-compute/external/wpt/css/css-color/oklch-008.html [ Failure ] crbug.com/626703 external/wpt/selection/textcontrols/onselectionchange-content-attribute.html [ Timeout ] diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations index 2cffe2e1d800d2..cd4f6320fd7c7b 100644 --- a/third_party/blink/web_tests/TestExpectations +++ b/third_party/blink/web_tests/TestExpectations @@ -2812,6 +2812,40 @@ crbug.com/626703 [ Mac ] external/wpt/css/css-text/line-breaking/segment-break-t crbug.com/626703 [ Fuchsia ] external/wpt/dom/historical.html [ Skip ] # ====== New tests from wpt-importer added here ====== +crbug.com/626703 [ Mac11-arm64 ] external/wpt/content-security-policy/inside-worker/dedicatedworker-script-src.html [ Timeout ] +crbug.com/626703 external/wpt/css/css-color/color-mix-percents-01.html [ Failure ] +crbug.com/626703 external/wpt/css/css-color/color-mix-percents-02.html [ Failure ] +crbug.com/626703 [ Mac10.14 ] external/wpt/css/css-overflow/scrollbar-gutter-002.html [ Failure ] +crbug.com/626703 [ Mac10.15 ] external/wpt/css/css-overflow/scrollbar-gutter-002.html [ Failure ] +crbug.com/626703 [ Mac11 ] external/wpt/css/css-overflow/scrollbar-gutter-002.html [ Failure ] +crbug.com/626703 [ Mac11-arm64 ] external/wpt/css/css-overflow/scrollbar-gutter-002.html [ Failure ] +crbug.com/626703 [ Mac10.14 ] external/wpt/css/css-overflow/scrollbar-gutter-vertical-lr-002.html [ Failure ] +crbug.com/626703 [ Mac10.15 ] external/wpt/css/css-overflow/scrollbar-gutter-vertical-lr-002.html [ Failure ] +crbug.com/626703 [ Mac11 ] external/wpt/css/css-overflow/scrollbar-gutter-vertical-lr-002.html [ Failure ] +crbug.com/626703 [ Mac11-arm64 ] external/wpt/css/css-overflow/scrollbar-gutter-vertical-lr-002.html [ Failure ] +crbug.com/626703 [ Mac11 ] external/wpt/css/css-overflow/scrollbar-gutter-vertical-rl-002.html [ Failure Crash ] +crbug.com/626703 [ Mac10.14 ] external/wpt/css/css-overflow/scrollbar-gutter-vertical-rl-002.html [ Failure ] +crbug.com/626703 [ Mac10.15 ] external/wpt/css/css-overflow/scrollbar-gutter-vertical-rl-002.html [ Failure ] +crbug.com/626703 [ Mac11-arm64 ] external/wpt/css/css-overflow/scrollbar-gutter-vertical-rl-002.html [ Failure ] +crbug.com/626703 [ Mac11-arm64 ] external/wpt/css/css-ui/compute-kind-widget-fallback-props-revert-001.html [ Failure ] +crbug.com/626703 [ Mac11-arm64 ] external/wpt/css/css-ui/compute-kind-widget-generated/kind-of-widget-fallback-background-color-001.html [ Failure ] +crbug.com/626703 [ Mac11-arm64 ] external/wpt/css/css-ui/compute-kind-widget-generated/kind-of-widget-fallback-background-position-001.html [ Failure ] +crbug.com/626703 [ Mac11-arm64 ] external/wpt/css/css-ui/compute-kind-widget-generated/kind-of-widget-fallback-border-block-start-color-001.html [ Failure ] +crbug.com/626703 [ Mac11-arm64 ] external/wpt/css/css-ui/compute-kind-widget-generated/kind-of-widget-fallback-border-bottom-left-radius-001.html [ Failure ] +crbug.com/626703 [ Mac11-arm64 ] external/wpt/css/css-ui/compute-kind-widget-generated/kind-of-widget-fallback-border-bottom-width-001.html [ Failure ] +crbug.com/626703 [ Mac11-arm64 ] external/wpt/css/css-ui/compute-kind-widget-generated/kind-of-widget-fallback-border-end-start-radius-001.html [ Failure ] +crbug.com/626703 [ Mac11-arm64 ] external/wpt/css/css-ui/compute-kind-widget-generated/kind-of-widget-fallback-border-image-source-001.html [ Failure ] +crbug.com/626703 [ Mac11-arm64 ] external/wpt/css/css-ui/compute-kind-widget-generated/kind-of-widget-fallback-border-inline-end-width-001.html [ Failure ] +crbug.com/626703 [ Mac11-arm64 ] external/wpt/css/css-ui/compute-kind-widget-generated/kind-of-widget-fallback-border-inline-start-style-001.html [ Failure ] +crbug.com/626703 [ Mac11-arm64 ] external/wpt/css/css-ui/compute-kind-widget-generated/kind-of-widget-fallback-border-left-style-001.html [ Failure ] +crbug.com/626703 [ Mac11-arm64 ] external/wpt/css/css-ui/compute-kind-widget-generated/kind-of-widget-fallback-border-top-right-radius-001.html [ Failure ] +crbug.com/626703 [ Mac11-arm64 ] external/wpt/css/css-ui/compute-kind-widget-generated/kind-of-widget-fallback-border-top-style-001.html [ Failure ] +crbug.com/626703 [ Mac11-arm64 ] external/wpt/css/css-ui/compute-kind-widget-generated/kind-of-widget-fallback-border-top-width-001.html [ Failure ] +crbug.com/626703 [ Mac11-arm64 ] virtual/no-auto-wpt-origin-isolation/external/wpt/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-no-child1-no-subdomain-child2-yes-subdomainport.sub.https.html [ Failure Crash ] +crbug.com/626703 [ Mac11-arm64 ] virtual/no-auto-wpt-origin-isolation/external/wpt/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-no-child1-yes-subdomain-child2-no-subdomain.sub.https.html [ Failure Crash ] +crbug.com/626703 [ Mac11-arm64 ] virtual/plz-dedicated-worker/external/wpt/content-security-policy/inside-worker/serviceworker-report-only.https.sub.html [ Timeout ] +crbug.com/626703 virtual/system-color-compute/external/wpt/css/css-color/color-mix-percents-01.html [ Failure ] +crbug.com/626703 virtual/system-color-compute/external/wpt/css/css-color/color-mix-percents-02.html [ Failure ] crbug.com/626703 [ Linux ] external/wpt/css/css-ui/compute-kind-widget-fallback-props-revert-001.html [ Failure ] crbug.com/626703 [ Mac10.12 ] external/wpt/css/css-ui/compute-kind-widget-fallback-props-revert-001.html [ Failure ] crbug.com/626703 [ Mac10.13 ] external/wpt/css/css-ui/compute-kind-widget-fallback-props-revert-001.html [ Failure ] @@ -2931,7 +2965,7 @@ crbug.com/626703 [ Mac10.14 ] external/wpt/css/css-ui/compute-kind-widget-genera crbug.com/626703 [ Mac10.15 ] external/wpt/css/css-ui/compute-kind-widget-generated/kind-of-widget-fallback-border-start-start-radius-001.html [ Failure ] crbug.com/626703 [ Mac11-arm64 ] external/wpt/css/css-ui/compute-kind-widget-generated/kind-of-widget-fallback-border-start-start-radius-001.html [ Failure ] crbug.com/626703 [ Win ] external/wpt/css/css-ui/compute-kind-widget-generated/kind-of-widget-fallback-border-start-start-radius-001.html [ Failure ] -crbug.com/626703 [ Mac11 ] external/wpt/css/css-ui/compute-kind-widget-generated/kind-of-widget-fallback-border-start-start-radius-001.html [ Timeout Failure ] +crbug.com/626703 [ Mac11 ] external/wpt/css/css-ui/compute-kind-widget-generated/kind-of-widget-fallback-border-start-start-radius-001.html [ Failure Timeout ] crbug.com/626703 external/wpt/css/css-ui/compute-kind-widget-generated/kind-of-widget-fallback-border-top-color-001.html [ Failure ] crbug.com/626703 external/wpt/css/css-ui/compute-kind-widget-generated/kind-of-widget-fallback-border-top-left-radius-001.html [ Failure ] crbug.com/626703 [ Linux ] external/wpt/css/css-ui/compute-kind-widget-generated/kind-of-widget-fallback-border-top-right-radius-001.html [ Failure ] @@ -4904,6 +4938,7 @@ crbug.com/614667 external/wpt/css/css-break/grid/grid-item-fragmentation-033.htm crbug.com/614667 external/wpt/css/css-break/grid/grid-item-fragmentation-034.html [ Failure ] crbug.com/614667 external/wpt/css/css-break/grid/grid-item-fragmentation-037.html [ Failure ] crbug.com/614667 external/wpt/css/css-break/grid/grid-item-fragmentation-038.html [ Failure ] +crbug.com/614667 external/wpt/css/css-break/grid/grid-item-fragmentation-039.html [ Failure ] crbug.com/1066629 external/wpt/css/css-break/hit-test-transformed.html [ Failure ] crbug.com/1058792 external/wpt/css/css-break/transform-007.html [ Failure ] crbug.com/1224888 external/wpt/css/css-break/transform-009.html [ Failure ] diff --git a/third_party/blink/web_tests/android/ChromeWPTOverrideExpectations b/third_party/blink/web_tests/android/ChromeWPTOverrideExpectations index fa5feb01a014c6..c33e20cc62aa71 100644 --- a/third_party/blink/web_tests/android/ChromeWPTOverrideExpectations +++ b/third_party/blink/web_tests/android/ChromeWPTOverrideExpectations @@ -396,7 +396,7 @@ crbug.com/1050754 external/wpt/bluetooth/requestDevice/acceptAllDevices/device-w crbug.com/1050754 external/wpt/bluetooth/requestDevice/acceptAllDevices/device-with-name.https.window.html [ Failure ] crbug.com/1050754 external/wpt/bluetooth/requestDevice/acceptAllDevices/optional-services-missing.https.window.html [ Failure ] crbug.com/1050754 external/wpt/bluetooth/requestDevice/acceptAllDevices/optional-services-present.https.window.html [ Failure ] -crbug.com/1050754 external/wpt/bluetooth/requestDevice/blocklisted-service-in-filter.https.html [ Failure ] +crbug.com/1050754 external/wpt/bluetooth/requestDevice/blocklisted-service-in-filter.https.window.html [ Failure ] crbug.com/1050754 external/wpt/bluetooth/requestDevice/blocklisted-service-in-optionalServices.https.window.html [ Failure ] crbug.com/1050754 external/wpt/bluetooth/requestDevice/canonicalizeFilter/device-name-longer-than-29-bytes.https.window.html [ Failure ] crbug.com/1050754 external/wpt/bluetooth/requestDevice/canonicalizeFilter/empty-filter.https.window.html [ Failure ] diff --git a/third_party/blink/web_tests/android/WebLayerWPTOverrideExpectations b/third_party/blink/web_tests/android/WebLayerWPTOverrideExpectations index 7e3e98dd97619d..6516bbfef88709 100644 --- a/third_party/blink/web_tests/android/WebLayerWPTOverrideExpectations +++ b/third_party/blink/web_tests/android/WebLayerWPTOverrideExpectations @@ -457,7 +457,7 @@ crbug.com/1050754 external/wpt/bluetooth/requestDevice/acceptAllDevices/device-w crbug.com/1050754 external/wpt/bluetooth/requestDevice/acceptAllDevices/device-with-name.https.window.html [ Failure ] crbug.com/1050754 external/wpt/bluetooth/requestDevice/acceptAllDevices/optional-services-missing.https.window.html [ Failure ] crbug.com/1050754 external/wpt/bluetooth/requestDevice/acceptAllDevices/optional-services-present.https.window.html [ Failure ] -crbug.com/1050754 external/wpt/bluetooth/requestDevice/blocklisted-service-in-filter.https.html [ Failure ] +crbug.com/1050754 external/wpt/bluetooth/requestDevice/blocklisted-service-in-filter.https.window.html [ Failure ] crbug.com/1050754 external/wpt/bluetooth/requestDevice/blocklisted-service-in-optionalServices.https.window.html [ Failure ] crbug.com/1050754 external/wpt/bluetooth/requestDevice/canonicalizeFilter/data-prefix-and-mask-size.https.window.html [ Failure ] crbug.com/1050754 external/wpt/bluetooth/requestDevice/canonicalizeFilter/dataPrefix-buffer-is-detached.https.window.html [ Failure ] diff --git a/third_party/blink/web_tests/android/WebviewWPTExpectations b/third_party/blink/web_tests/android/WebviewWPTExpectations index 56fe9fac22bf54..070b3ab0333e86 100644 --- a/third_party/blink/web_tests/android/WebviewWPTExpectations +++ b/third_party/blink/web_tests/android/WebviewWPTExpectations @@ -355,7 +355,7 @@ crbug.com/1050754 external/wpt/bluetooth/requestDevice/acceptAllDevices/device-w crbug.com/1050754 external/wpt/bluetooth/requestDevice/acceptAllDevices/device-with-name.https.window.html [ Failure ] crbug.com/1050754 external/wpt/bluetooth/requestDevice/acceptAllDevices/optional-services-missing.https.window.html [ Failure ] crbug.com/1050754 external/wpt/bluetooth/requestDevice/acceptAllDevices/optional-services-present.https.window.html [ Failure ] -crbug.com/1050754 external/wpt/bluetooth/requestDevice/blocklisted-service-in-filter.https.html [ Failure ] +crbug.com/1050754 external/wpt/bluetooth/requestDevice/blocklisted-service-in-filter.https.window.html [ Failure ] crbug.com/1050754 external/wpt/bluetooth/requestDevice/blocklisted-service-in-optionalServices.https.window.html [ Failure ] crbug.com/1050754 external/wpt/bluetooth/requestDevice/canonicalizeFilter/device-name-longer-than-29-bytes.https.window.html [ Failure ] crbug.com/1050754 external/wpt/bluetooth/requestDevice/canonicalizeFilter/empty-filter.https.window.html [ Failure ] diff --git a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json index 87d65c78b2231e..ab6a072658a322 100644 --- a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json +++ b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json @@ -1685,6 +1685,13 @@ {} ] ], + "preserve3d-svg-foreign-object-hit-test.svg": [ + "14c772cb2d24ae89cc2bce0e2ed2dad83d3ae6d9", + [ + null, + {} + ] + ], "transform-marquee-resize-div-image-001.html": [ "8bdbb984f3a1cb8a500a93faa4efa4a633b81826", [ @@ -82565,6 +82572,32 @@ {} ] ], + "color-mix-percents-01.html": [ + "425ef9a63621ef635fc3e800ee92a59dd32978d0", + [ + null, + [ + [ + "/css/css-color/color-mix-percents-01-ref.html", + "==" + ] + ], + {} + ] + ], + "color-mix-percents-02.html": [ + "5b1a4cfd9d92f311b7a6619017f29fb42c62f269", + [ + null, + [ + [ + "/css/css-color/color-mix-percents-01-ref.html", + "==" + ] + ], + {} + ] + ], "composited-filters-under-opacity.html": [ "f613748af3ac5bb59c9fa207937ae6207d71062e", [ @@ -135907,6 +135940,19 @@ {} ] ], + "overflow-clip-margin-008.html": [ + "fabe669a49c69319c9fc689cc9f22af6b6fe41f9", + [ + null, + [ + [ + "/css/css-overflow/overflow-clip-margin-008-ref.html", + "==" + ] + ], + {} + ] + ], "overflow-clip-margin-invalidation.html": [ "d9c87a34a53f17451b0d3ae8c2071971d1df3a94", [ @@ -136089,6 +136135,71 @@ {} ] ], + "scrollbar-gutter-002.html": [ + "129eb2c08590c98505e36f63da173d1c8a4bf98d", + [ + null, + [ + [ + "/css/css-overflow/scrollbar-gutter-002-ref.html", + "==" + ] + ], + {} + ] + ], + "scrollbar-gutter-dynamic-001.html": [ + "3dcb32048cd7a1b94eb7cd30be4a381a5f168eab", + [ + null, + [ + [ + "/css/css-overflow/scrollbar-gutter-dynamic-001-ref.html", + "==" + ] + ], + {} + ] + ], + "scrollbar-gutter-rtl-002.html": [ + "8d0376defdfc69ecb44fc2134c81573e553db2bb", + [ + null, + [ + [ + "/css/css-overflow/scrollbar-gutter-rtl-002-ref.html", + "==" + ] + ], + {} + ] + ], + "scrollbar-gutter-vertical-lr-002.html": [ + "9e5dcd299eba99e4ab6f077149a841011805c0a5", + [ + null, + [ + [ + "/css/css-overflow/scrollbar-gutter-vertical-lr-002-ref.html", + "==" + ] + ], + {} + ] + ], + "scrollbar-gutter-vertical-rl-002.html": [ + "d68d4e5d3cba0353da274f9c75c558c0a8324092", + [ + null, + [ + [ + "/css/css-overflow/scrollbar-gutter-vertical-rl-002-ref.html", + "==" + ] + ], + {} + ] + ], "text-overflow-ellipsis-001.html": [ "41c11d581e982c7de2d51d38b18decd99e8a60b3", [ @@ -231585,7 +231696,7 @@ [] ], "fedcm-mock.js": [ - "2fa351446c2601580c525f4ef75b01f2ba2a19c8", + "407ca8d7702adbf6d7dbc100323eb46267048631", [] ], "federatedcredential-get.html": [ @@ -241348,6 +241459,10 @@ "77ccec0d3adfc6bb9840f529ad5e574792c96cef", [] ], + "color-mix-percents-01-ref.html": [ + "2d9a1380b3889a446356725b75db485e55992cc1", + [] + ], "color-resolving-hwb-expected.txt": [ "4c702545c17052ddbdda669b4a8191116b9306f8", [] @@ -258742,6 +258857,10 @@ "9f562d67fe4249d09c98062a74dcb2b656123056", [] ], + "overflow-clip-margin-008-ref.html": [ + "3af9e4e951d9d35eafc72fd7593be20a7c94dbde", + [] + ], "overflow-clip-margin-invalidation-ref.html": [ "1ec2a5ce0a21c8dd578b3fcfde702307e4e2a9a8", [] @@ -258986,10 +259105,26 @@ "782ffab9da1c1cb55b4488e7fc8f913dd18857f3", [] ], + "scrollbar-gutter-002-ref.html": [ + "ae51682c75920743d4ae69fffe8b566a3758c7a4", + [] + ], "scrollbar-gutter-dynamic-001-ref.html": [ "5f2f7bff26420e949093a822d8995d9fcc3f1f51", [] ], + "scrollbar-gutter-rtl-002-ref.html": [ + "e6510a081353db996c4ead14ad6a167b90d48297", + [] + ], + "scrollbar-gutter-vertical-lr-002-ref.html": [ + "cb7c647da43c842e68c9ca8e9ca24cd9708324dd", + [] + ], + "scrollbar-gutter-vertical-rl-002-ref.html": [ + "7dbadf995ed89bc3032c4ca1b53e1cebecd6237e", + [] + ], "text-overflow-ellipsis-editing-input-ref.html": [ "3902072bc58b7bf62290edafbce1b735288af716", [] @@ -354116,6 +354251,13 @@ {} ] ], + "fedcm-logout.https.html": [ + "e4f83b0f90e1a684c254235e6222b299b8647cbc", + [ + null, + {} + ] + ], "fedcm-revoke.https.html": [ "c014724b8173e47e9d9ffb00a08537055c3f8003", [ @@ -354124,7 +354266,7 @@ ] ], "fedcm.https.html": [ - "71ff0ab70f49258f5e6d701b8e7034933ae1ae42", + "618f0ec3c24e5cca8512748d972357e03590d77e", [ null, {} @@ -559606,15 +559748,6 @@ ] ] }, - "css-overflow": { - "scrollbar-gutter-dynamic-001.html": [ - "d6451ff995f5b522626e8e05eaed33d3e49e5f9b", - [ - null, - {} - ] - ] - }, "css-ruby": { "rbc-rtc-basic-001.html": [ "e086181ab938b54ff40d2363d801380d1b9ada9b", diff --git a/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/blocklisted-service-in-filter.https.html b/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/blocklisted-service-in-filter.https.html deleted file mode 100644 index cb2f989a47778b..00000000000000 --- a/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/blocklisted-service-in-filter.https.html +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - diff --git a/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/blocklisted-service-in-filter.https.window.js b/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/blocklisted-service-in-filter.https.window.js new file mode 100644 index 00000000000000..48f3d1309da077 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/blocklisted-service-in-filter.https.window.js @@ -0,0 +1,23 @@ +// META: script=/resources/testharness.js +// META: script=/resources/testharnessreport.js +// META: script=/resources/testdriver.js +// META: script=/resources/testdriver-vendor.js +// META: script=/bluetooth/resources/bluetooth-test.js +// META: script=/bluetooth/resources/bluetooth-fake-devices.js +'use strict'; +const test_desc = 'Reject with SecurityError if requesting a blocklisted ' + + 'service.'; +const expected = new DOMException( + 'requestDevice() called with a filter containing a blocklisted UUID. ' + + 'https://goo.gl/4NeimX', + 'SecurityError'); + +bluetooth_test(async () => { + await assert_promise_rejects_with_message( + setUpPreconnectedFakeDevice({ + fakeDeviceOptions: {knownServiceUUIDs: ['human_interface_device']}, + requestDeviceOptions: + {filters: [{services: ['human_interface_device']}]} + }), + expected, 'Requesting blocklisted service rejects.'); +}, test_desc); diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/grid/grid-item-fragmentation-039.html b/third_party/blink/web_tests/external/wpt/css/css-break/grid/grid-item-fragmentation-039.html new file mode 100644 index 00000000000000..5201c49fa42ff8 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-break/grid/grid-item-fragmentation-039.html @@ -0,0 +1,10 @@ + + + +

Test passes if there is a filled green square and no red.

+
+
+
+
+
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-color/color-mix-percents-01-ref.html b/third_party/blink/web_tests/external/wpt/css/css-color/color-mix-percents-01-ref.html new file mode 100644 index 00000000000000..2d9a1380b3889a --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-color/color-mix-percents-01-ref.html @@ -0,0 +1,11 @@ + + +CSS Color 5: color-mix + + + +

Test passes if you see a purple square, and no red.

+
+ \ No newline at end of file diff --git a/third_party/blink/web_tests/external/wpt/css/css-color/color-mix-percents-01.html b/third_party/blink/web_tests/external/wpt/css/css-color/color-mix-percents-01.html new file mode 100644 index 00000000000000..425ef9a63621ef --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-color/color-mix-percents-01.html @@ -0,0 +1,29 @@ + + +CSS Color 5: color-mix + + + + + + + + +

Test passes if you see a purple square, and no red.

+
+
+
+
+
+
+
+ \ No newline at end of file diff --git a/third_party/blink/web_tests/external/wpt/css/css-color/color-mix-percents-02.html b/third_party/blink/web_tests/external/wpt/css/css-color/color-mix-percents-02.html new file mode 100644 index 00000000000000..5b1a4cfd9d92f3 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-color/color-mix-percents-02.html @@ -0,0 +1,29 @@ + + +CSS Color 5: color-mix + + + + + + + + +

Test passes if you see a purple square, and no red.

+
+
+
+
+
+
+
+ \ No newline at end of file diff --git a/third_party/blink/web_tests/external/wpt/css/css-overflow/overflow-clip-margin-009-ref.html b/third_party/blink/web_tests/external/wpt/css/css-overflow/overflow-clip-margin-009-ref.html new file mode 100644 index 00000000000000..efbf8985e56728 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-overflow/overflow-clip-margin-009-ref.html @@ -0,0 +1,22 @@ + + + +Overflow-clip-margin can be inherited even if it has no effect on specified element + + + +
+
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-overflow/overflow-clip-margin-009.html b/third_party/blink/web_tests/external/wpt/css/css-overflow/overflow-clip-margin-009.html new file mode 100644 index 00000000000000..6ba9a5a3e60735 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-overflow/overflow-clip-margin-009.html @@ -0,0 +1,28 @@ + + + +Overflow-clip-margin can be inherited even if it has no effect on specified element + + + + +
+
+
+
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-overflow/scrollbar-gutter-002-ref.html b/third_party/blink/web_tests/external/wpt/css/css-overflow/scrollbar-gutter-002-ref.html new file mode 100644 index 00000000000000..ae51682c759207 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-overflow/scrollbar-gutter-002-ref.html @@ -0,0 +1,73 @@ + + + + CSS Overflow Reference: test scrollbar-gutter with horizontal left to right content + + + + + +
+
+
+
+ +
+
+
+ +
+
+
+
+ +
+
+
+
+ +
+
+
+ +
+
+
+
+ + + diff --git a/third_party/blink/web_tests/external/wpt/css/css-overflow/scrollbar-gutter-002.html b/third_party/blink/web_tests/external/wpt/css/css-overflow/scrollbar-gutter-002.html new file mode 100644 index 00000000000000..129eb2c08590c9 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-overflow/scrollbar-gutter-002.html @@ -0,0 +1,60 @@ + + + + CSS Overflow: test scrollbar-gutter with horizontal left to right content + + + + + + + +
+
+
+
+ +
+
+
+ +
+
+
+
+ +
+
+
+
+ +
+
+
+ +
+
+
+
+ diff --git a/third_party/blink/web_tests/external/wpt/css/css-overflow/scrollbar-gutter-dynamic-001.html b/third_party/blink/web_tests/external/wpt/css/css-overflow/scrollbar-gutter-dynamic-001.html index d6451ff995f5b5..3dcb32048cd7a1 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-overflow/scrollbar-gutter-dynamic-001.html +++ b/third_party/blink/web_tests/external/wpt/css/css-overflow/scrollbar-gutter-dynamic-001.html @@ -3,6 +3,8 @@ CSS Overflow: scrollbar-gutter changing dynamically + + + +
+
+
+
+ +
+
+
+ +
+
+
+
+ +
+
+
+
+ +
+
+
+ +
+
+
+
+ + + diff --git a/third_party/blink/web_tests/external/wpt/css/css-overflow/scrollbar-gutter-rtl-002.html b/third_party/blink/web_tests/external/wpt/css/css-overflow/scrollbar-gutter-rtl-002.html new file mode 100644 index 00000000000000..8d0376defdfc69 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-overflow/scrollbar-gutter-rtl-002.html @@ -0,0 +1,60 @@ + + + + CSS Overflow: test scrollbar-gutter with horizontal right to left content + + + + + + + +
+
+
+
+ +
+
+
+ +
+
+
+
+ +
+
+
+
+ +
+
+
+ +
+
+
+
+ diff --git a/third_party/blink/web_tests/external/wpt/css/css-overflow/scrollbar-gutter-vertical-lr-002-ref.html b/third_party/blink/web_tests/external/wpt/css/css-overflow/scrollbar-gutter-vertical-lr-002-ref.html new file mode 100644 index 00000000000000..cb7c647da43c84 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-overflow/scrollbar-gutter-vertical-lr-002-ref.html @@ -0,0 +1,72 @@ + + + + CSS Overflow Reference: test scrollbar-gutter with vertical left to right content + + + + + +
+
+
+
+ +
+
+
+ +
+
+
+
+ +
+
+
+
+ +
+
+
+ +
+
+
+
+ + + diff --git a/third_party/blink/web_tests/external/wpt/css/css-overflow/scrollbar-gutter-vertical-lr-002.html b/third_party/blink/web_tests/external/wpt/css/css-overflow/scrollbar-gutter-vertical-lr-002.html new file mode 100644 index 00000000000000..9e5dcd299eba99 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-overflow/scrollbar-gutter-vertical-lr-002.html @@ -0,0 +1,59 @@ + + + + CSS Overflow: test scrollbar-gutter with vertical left to right content + + + + + + + +
+
+
+
+ +
+
+
+ +
+
+
+
+ +
+
+
+
+ +
+
+
+ +
+
+
+
+ diff --git a/third_party/blink/web_tests/external/wpt/css/css-overflow/scrollbar-gutter-vertical-rl-002-ref.html b/third_party/blink/web_tests/external/wpt/css/css-overflow/scrollbar-gutter-vertical-rl-002-ref.html new file mode 100644 index 00000000000000..7dbadf995ed89b --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-overflow/scrollbar-gutter-vertical-rl-002-ref.html @@ -0,0 +1,73 @@ + + + + CSS Overflow Reference: test scrollbar-gutter with vertical right to left content + + + + + +
+
+
+
+ +
+
+
+ +
+
+
+
+ +
+
+
+
+ +
+
+
+ +
+
+
+
+ + + diff --git a/third_party/blink/web_tests/external/wpt/css/css-overflow/scrollbar-gutter-vertical-rl-002.html b/third_party/blink/web_tests/external/wpt/css/css-overflow/scrollbar-gutter-vertical-rl-002.html new file mode 100644 index 00000000000000..d68d4e5d3cba03 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-overflow/scrollbar-gutter-vertical-rl-002.html @@ -0,0 +1,59 @@ + + + + CSS Overflow: test scrollbar-gutter with vertical right to left content + + + + + + + +
+
+
+
+ +
+
+
+ +
+
+
+
+ +
+
+
+
+ +
+
+
+ +
+
+
+
+ diff --git a/third_party/distributed_point_functions/DEPS b/third_party/distributed_point_functions/DEPS index fa43b60771c1a2..fa1f248b156aac 100644 --- a/third_party/distributed_point_functions/DEPS +++ b/third_party/distributed_point_functions/DEPS @@ -1,7 +1,6 @@ include_rules = [ "+absl", "+benchmark", - "+dcf", "+dpf", "+google/protobuf", ] diff --git a/third_party/distributed_point_functions/README.chromium b/third_party/distributed_point_functions/README.chromium index e1212d0d89931b..3fced6a574a2b9 100644 --- a/third_party/distributed_point_functions/README.chromium +++ b/third_party/distributed_point_functions/README.chromium @@ -14,10 +14,11 @@ This library contains an implementation of incremental distributed point functions, based on the paper by Boneh et al. Local Modifications: -The directory code/ is an unchanged copy of the source code other than the -addition of a .clang-format file to disable automatic code formatting. Parts of -code/dpf/distributed_point_function_test.cc are also adapted for fuzzing in -fuzz/dpf_fuzzer.cc. +The directory code/ is a copy of the source code, modified in two ways. First, +all top-level directories other than dpf/ have been removed as they are unused. +Second, a .clang-format file has been added to disable automatic code +formatting. Parts of code/dpf/distributed_point_function_test.cc are also +adapted for fuzzing in fuzz/dpf_fuzzer.cc. The source code pulled in depends on "glog/logging.h" which is not accessible from chromium. As a workaround, we create a simple glog/logging.h that includes diff --git a/third_party/distributed_point_functions/code/.bazelci/presubmit.yml b/third_party/distributed_point_functions/code/.bazelci/presubmit.yml deleted file mode 100644 index 02fccaf8cd51be..00000000000000 --- a/third_party/distributed_point_functions/code/.bazelci/presubmit.yml +++ /dev/null @@ -1,22 +0,0 @@ -# Copyright 2021 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -tasks: - ubuntu2004: - build_targets: - - "//..." - test_flags: - - "--test_tag_filters=-benchmark" - test_targets: - - "//..." diff --git a/third_party/distributed_point_functions/code/dcf/BUILD b/third_party/distributed_point_functions/code/dcf/BUILD deleted file mode 100644 index a62f03fbf1ba62..00000000000000 --- a/third_party/distributed_point_functions/code/dcf/BUILD +++ /dev/null @@ -1,48 +0,0 @@ -load("@io_bazel_rules_go//proto:def.bzl", "go_proto_library") -load("@rules_cc//cc:defs.bzl", "cc_library") -load("@rules_proto//proto:defs.bzl", "proto_library") - -package( - default_visibility = ["//visibility:public"], -) - -licenses(["notice"]) - -cc_library( - name = "distributed_comparison_function", - srcs = ["distributed_comparison_function.cc"], - hdrs = ["distributed_comparison_function.h"], - deps = [ - ":distributed_comparison_function_cc_proto", - "//dpf:distributed_point_function", - "//dpf:distributed_point_function_cc_proto", - "//dpf:status_macros", - "@com_google_absl//absl/meta:type_traits", - "@com_google_absl//absl/status:statusor", - ], -) - -proto_library( - name = "distributed_comparison_function_proto", - srcs = ["distributed_comparison_function.proto"], - deps = [ - "//dpf:distributed_point_function_proto", - ], -) - -cc_proto_library( - name = "distributed_comparison_function_cc_proto", - deps = [":distributed_comparison_function_proto"], -) - -cc_test( - name = "distributed_comparison_function_test", - srcs = ["distributed_comparison_function_test.cc"], - deps = [ - ":distributed_comparison_function", - "//dpf/internal:status_matchers", - "@com_github_google_googletest//:gtest_main", - "@com_google_absl//absl/random", - "@com_google_absl//absl/utility", - ], -) diff --git a/third_party/distributed_point_functions/code/dcf/distributed_comparison_function.cc b/third_party/distributed_point_functions/code/dcf/distributed_comparison_function.cc deleted file mode 100644 index 20db2bb2daeaa0..00000000000000 --- a/third_party/distributed_point_functions/code/dcf/distributed_comparison_function.cc +++ /dev/null @@ -1,102 +0,0 @@ -// Copyright 2021 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "dcf/distributed_comparison_function.h" - -#include "dpf/status_macros.h" - -namespace distributed_point_functions { - -namespace { - -void SetToZero(Value& value) { - if (value.value_case() == Value::kInteger) { - value.mutable_integer()->set_value_uint64(0); - } else if (value.value_case() == Value::kIntModN) { - value.mutable_int_mod_n()->set_value_uint64(0); - } else if (value.value_case() == Value::kTuple) { - for (int i = 0; i < value.tuple().elements_size(); ++i) { - SetToZero(*(value.mutable_tuple()->mutable_elements(i))); - } - } -} - -} // namespace - -DistributedComparisonFunction::DistributedComparisonFunction( - DcfParameters parameters, std::unique_ptr dpf) - : parameters_(std::move(parameters)), dpf_(std::move(dpf)) {} - -absl::StatusOr> -DistributedComparisonFunction::Create(const DcfParameters& parameters) { - // A DCF with a single-element domain doesn't make sense. - if (parameters.parameters().log_domain_size() < 1) { - return absl::InvalidArgumentError("A DCF must have log_domain_size >= 1"); - } - - // We don't support the legacy element_bitsize field in DCFs. - if (!parameters.parameters().has_value_type()) { - return absl::InvalidArgumentError( - "parameters.value_type must be set for " - "DistributedComparisonFunction::Create"); - } - - // Create parameter vector for the incremental DPF. - std::vector dpf_parameters( - parameters.parameters().log_domain_size()); - for (int i = 0; i < static_cast(dpf_parameters.size()); ++i) { - dpf_parameters[i].set_log_domain_size(i); - *(dpf_parameters[i].mutable_value_type()) = - parameters.parameters().value_type(); - } - - // Check that parameters are valid. We can use the DPF proto validator - // directly. - DPF_RETURN_IF_ERROR( - dpf_internal::ProtoValidator::ValidateParameters(dpf_parameters)); - - // Create incremental DPF. - DPF_ASSIGN_OR_RETURN( - std::unique_ptr dpf, - DistributedPointFunction::CreateIncremental(dpf_parameters)); - - return absl::WrapUnique( - new DistributedComparisonFunction(parameters, std::move(dpf))); -} - -absl::StatusOr> -DistributedComparisonFunction::GenerateKeys(absl::uint128 alpha, - const Value& beta) { - const int log_domain_size = parameters_.parameters().log_domain_size(); - std::vector dpf_values(log_domain_size, beta); - for (int i = 0; i < log_domain_size; ++i) { - // beta_i = 0 if alpha_i == 0, and beta otherwise. - bool current_bit = - (alpha & (absl::uint128{1} << (log_domain_size - i - 1))) != 0; - if (!current_bit) { - SetToZero(dpf_values[i]); - } - } - - std::pair result; - DPF_ASSIGN_OR_RETURN( - std::tie(*(result.first.mutable_key()), *(result.second.mutable_key())), - dpf_->GenerateKeysIncremental( - alpha >> 1, // We can ignore the last bit of alpha, since it is - // encoded in dpf_values.back(). - dpf_values)); - return result; -} - -} // namespace distributed_point_functions diff --git a/third_party/distributed_point_functions/code/dcf/distributed_comparison_function.h b/third_party/distributed_point_functions/code/dcf/distributed_comparison_function.h deleted file mode 100644 index 36527e3ab1c198..00000000000000 --- a/third_party/distributed_point_functions/code/dcf/distributed_comparison_function.h +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright 2021 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef DISTRIBUTED_POINT_FUNCTIONS_DCF_DISTRIBUTED_COMPARISON_FUNCTION_H_ -#define DISTRIBUTED_POINT_FUNCTIONS_DCF_DISTRIBUTED_COMPARISON_FUNCTION_H_ - -#include - -#include "absl/meta/type_traits.h" -#include "absl/status/statusor.h" -#include "dcf/distributed_comparison_function.pb.h" -#include "dpf/distributed_point_function.h" -#include "dpf/distributed_point_function.pb.h" - -namespace distributed_point_functions { - -class DistributedComparisonFunction { - public: - static absl::StatusOr> Create( - const DcfParameters& parameters); - - // Creates keys for a DCF that evaluates to shares of `beta` on any input x < - // `alpha`, and shares of 0 otherwise. - // - // Returns INVALID_ARGUMENT if `alpha` or `beta` do not match the - // DcfParameters passed at construction. - // - // Overload for explicit Value proto. - absl::StatusOr> GenerateKeys(absl::uint128 alpha, - const Value& beta); - - // Template for automatic conversion to Value proto. Disabled if the argument - // is convertible to `absl::uint128` or `Value` to make overloading - // unambiguous. - template ::value && - is_supported_type_v>> - absl::StatusOr> GenerateKeys(absl::uint128 alpha, - const T& beta) { - absl::StatusOr value = dpf_->ToValue(beta); - if (!value.ok()) { - return value.status(); - } - return GenerateKeys(alpha, *value); - } - - // Evaluates a DcfKey at the given point `x`. - // - // Returns INVALID_ARGUMENT if `key` or `x` do not match the parameters passed - // at construction. - template - absl::StatusOr Evaluate(const DcfKey& key, absl::uint128 x); - - // DistributedComparisonFunction is neither copyable nor movable. - DistributedComparisonFunction(const DistributedComparisonFunction&) = delete; - DistributedComparisonFunction& operator=( - const DistributedComparisonFunction&) = delete; - - private: - DistributedComparisonFunction(DcfParameters parameters, - std::unique_ptr dpf); - - const DcfParameters parameters_; - const std::unique_ptr dpf_; -}; - -// Implementation details. - -template -absl::StatusOr DistributedComparisonFunction::Evaluate(const DcfKey& key, - absl::uint128 x) { - const int log_domain_size = parameters_.parameters().log_domain_size(); - T result{}; - - absl::StatusOr ctx = - dpf_->CreateEvaluationContext(key.key()); - if (!ctx.ok()) { - return ctx.status(); - } - - int previous_bit = 0; - for (int i = 0; i < log_domain_size; ++i) { - absl::StatusOr> expansion_i; - if (i == 0) { - expansion_i = dpf_->EvaluateNext({}, *ctx); - } else { - absl::uint128 prefix = 0; - if (log_domain_size < 128) { - prefix = x >> (log_domain_size - i + 1); - } - expansion_i = - dpf_->EvaluateNext(absl::MakeConstSpan(&prefix, 1), *ctx); - } - if (!expansion_i.ok()) { - return expansion_i.status(); - } - - int current_bit = static_cast( - (x & (absl::uint128{1} << (log_domain_size - i - 1))) != 0); - // We only add the current value along the path if the current bit of x is - // 0. - if (current_bit == 0) { - result += (*expansion_i)[previous_bit]; - } - previous_bit = current_bit; - } - return result; -} - -} // namespace distributed_point_functions - -#endif // DISTRIBUTED_POINT_FUNCTIONS_DCF_DISTRIBUTED_COMPARISON_FUNCTION_H_ diff --git a/third_party/distributed_point_functions/code/dcf/distributed_comparison_function.proto b/third_party/distributed_point_functions/code/dcf/distributed_comparison_function.proto deleted file mode 100644 index dd690437c8f860..00000000000000 --- a/third_party/distributed_point_functions/code/dcf/distributed_comparison_function.proto +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2021 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -syntax = "proto3"; - -package distributed_point_functions; - -import "dpf/distributed_point_function.proto"; - -// For faster allocations of sub-messages. -option cc_enable_arenas = true; - -// The parameters for a DCF have the same form as for a DPF. -message DcfParameters { - DpfParameters parameters = 1; -} - -// A DCF key is just a special DpfKey. -message DcfKey { - DpfKey key = 1; -} \ No newline at end of file diff --git a/third_party/distributed_point_functions/code/dcf/distributed_comparison_function_test.cc b/third_party/distributed_point_functions/code/dcf/distributed_comparison_function_test.cc deleted file mode 100644 index 7d12894cb5d589..00000000000000 --- a/third_party/distributed_point_functions/code/dcf/distributed_comparison_function_test.cc +++ /dev/null @@ -1,180 +0,0 @@ -// Copyright 2021 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "dcf/distributed_comparison_function.h" - -#include -#include - -#include "absl/random/random.h" -#include "absl/utility/utility.h" -#include "dpf/internal/status_matchers.h" - -namespace distributed_point_functions { - -namespace { - -// Helper function that recursively sets all elements of a tuple to 42. -template -static void SetTo42(T0& x) { - x = T0(42); -} -template -static void SetTo42(T0& x0, Tn&... xn) { - SetTo42(x0); - SetTo42(xn...); -} -template -static void SetTo42(Tuple& x) { - absl::apply([](auto&... in) { SetTo42(in...); }, x.value()); -} - -TEST(DcfTest, CreateFailsWithZeroLogDomainSize) { - DcfParameters parameters; - parameters.mutable_parameters() - ->mutable_value_type() - ->mutable_integer() - ->set_bitsize(32); - - parameters.mutable_parameters()->set_log_domain_size(0); - - EXPECT_THAT(DistributedComparisonFunction::Create(parameters), - dpf_internal::StatusIs(absl::StatusCode::kInvalidArgument, - "A DCF must have log_domain_size >= 1")); -} - -template -class DcfTestParameters { - public: - using ValueType = T; - static constexpr int kLogDomainSize = log_domain_size; -}; - -template -struct DcfTest : public testing::Test { - void SetUp() { - DcfParameters parameters; - parameters.mutable_parameters()->set_log_domain_size(T::kLogDomainSize); - *(parameters.mutable_parameters()->mutable_value_type()) = - ToValueType(); - - DPF_ASSERT_OK_AND_ASSIGN(dcf_, - DistributedComparisonFunction::Create(parameters)); - } - - std::unique_ptr dcf_; -}; - -using MyIntModN = IntModN; // 2**32 - 5. -using DcfTestTypes = ::testing::Types< - DcfTestParameters, DcfTestParameters, - DcfTestParameters, DcfTestParameters, - DcfTestParameters, 5>, - DcfTestParameters, 5>, - DcfTestParameters, 5> >; - -TYPED_TEST_SUITE(DcfTest, DcfTestTypes); - -TYPED_TEST(DcfTest, CreateWorks) { - EXPECT_THAT(this->dcf_, testing::Ne(nullptr)); -} - -TYPED_TEST(DcfTest, GenEval) { - using ValueType = typename TypeParam::ValueType; - const absl::uint128 domain_size = absl::uint128{1} - << TypeParam::kLogDomainSize; - ValueType beta; - SetTo42(beta); - for (absl::uint128 alpha = 0; alpha < domain_size; ++alpha) { - // Generate keys. - DcfKey key_0, key_1; - DPF_ASSERT_OK_AND_ASSIGN(std::tie(key_0, key_1), - this->dcf_->GenerateKeys(alpha, beta)); - - // Evaluate on every point in the domain. - for (absl::uint128 x = 0; x < domain_size; ++x) { - DPF_ASSERT_OK_AND_ASSIGN( - ValueType result_0, - this->dcf_->template Evaluate(key_0, x)); - DPF_ASSERT_OK_AND_ASSIGN( - ValueType result_1, - this->dcf_->template Evaluate(key_1, x)); - if (x < alpha) { - EXPECT_EQ(ValueType(result_0 + result_1), beta) - << "x=" << x << ", alpha=" << alpha; - } else { - EXPECT_EQ(ValueType(result_0 + result_1), ValueType{}) - << "x=" << x << ", alpha=" << alpha; - } - } - } -} - -TEST(DcfTest, WorksCorrectlyOnUint64TWithLargeDomain) { - using ValueType = uint64_t; - const absl::uint128 domain_size = absl::uint128{1} << 64; - ValueType beta; - SetTo42(beta); - absl::uint128 alpha = 50; - - DcfParameters parameters; - parameters.mutable_parameters()->set_log_domain_size(64); - *(parameters.mutable_parameters()->mutable_value_type()) = - ToValueType(); - - DPF_ASSERT_OK_AND_ASSIGN(auto dcf, - DistributedComparisonFunction::Create(parameters)); - - // Generate keys. - DcfKey key_0, key_1; - DPF_ASSERT_OK_AND_ASSIGN(std::tie(key_0, key_1), - dcf->GenerateKeys(alpha, beta)); - - // Evaluate on every point in the domain smaller than alpha. - for (absl::uint128 x = 0; x < alpha; ++x) { - DPF_ASSERT_OK_AND_ASSIGN(ValueType result_0, - dcf->template Evaluate(key_0, x)); - DPF_ASSERT_OK_AND_ASSIGN(ValueType result_1, - dcf->template Evaluate(key_1, x)); - EXPECT_EQ(ValueType(result_0 + result_1), beta) - << "x=" << x << ", alpha=" << alpha; - } - - // Evaluate on 100 random points in the domain. - absl::BitGen rng; - absl::uniform_int_distribution dist; - const int kNumEvaluationPoints = 100; - std::vector evaluation_points(kNumEvaluationPoints); - for (int i = 0; i < kNumEvaluationPoints - 1; ++i) { - evaluation_points[i] = - absl::MakeUint128(dist(rng), dist(rng)) % domain_size; - DPF_ASSERT_OK_AND_ASSIGN( - uint64_t result_0, - dcf->template Evaluate(key_0, evaluation_points[i])); - DPF_ASSERT_OK_AND_ASSIGN( - ValueType result_1, - dcf->template Evaluate(key_1, evaluation_points[i])); - if (evaluation_points[i] < alpha) { - EXPECT_EQ(ValueType(result_0 + result_1), beta) - << "x=" << evaluation_points[i] << ", alpha=" << alpha; - } else { - EXPECT_EQ(ValueType(result_0 + result_1), ValueType{}) - << "x=" << evaluation_points[i] << ", alpha=" << alpha; - } - } -} - -} // namespace - -} // namespace distributed_point_functions diff --git a/third_party/distributed_point_functions/code/dcf/fss_gates/BUILD b/third_party/distributed_point_functions/code/dcf/fss_gates/BUILD deleted file mode 100644 index b62be84bbefbb6..00000000000000 --- a/third_party/distributed_point_functions/code/dcf/fss_gates/BUILD +++ /dev/null @@ -1,62 +0,0 @@ -# This package contains implementation of various Function Secret Sharing (FSS) -# gates as specified in https://eprint.iacr.org/2020/1392. The implementation -# uses the Distributed Comparison Function (as implemented in -# distributed_comparison_function.cc) as a central component. - -load("@io_bazel_rules_go//proto:def.bzl", "go_proto_library") -load("@rules_cc//cc:defs.bzl", "cc_library") -load("@rules_proto//proto:defs.bzl", "proto_library") - -package( - default_visibility = ["//visibility:public"], -) - -licenses(["notice"]) - -# Multiple Interval Containment - -cc_library( - name = "multiple_interval_containment", - srcs = ["multiple_interval_containment.cc"], - hdrs = ["multiple_interval_containment.h"], - deps = [ - ":multiple_interval_containment_cc_proto", - "//dcf:distributed_comparison_function", - "//dcf/fss_gates/prng:basic_rng", - "//dpf:distributed_point_function_cc_proto", - "//dpf:status_macros", - "@com_google_absl//absl/numeric:int128", - "@com_google_absl//absl/status", - "@com_google_absl//absl/status:statusor", - ], -) - -proto_library( - name = "multiple_interval_containment_proto", - srcs = ["multiple_interval_containment.proto"], - deps = [ - "//dcf:distributed_comparison_function_proto", - "//dpf:distributed_point_function_proto", - ], -) - -cc_proto_library( - name = "multiple_interval_containment_cc_proto", - deps = [":multiple_interval_containment_proto"], -) - -cc_test( - name = "multiple_interval_containment_test", - srcs = ["multiple_interval_containment_test.cc"], - deps = [ - ":multiple_interval_containment", - ":multiple_interval_containment_cc_proto", - "//dcf/fss_gates/prng:basic_rng", - "//dpf:distributed_point_function_cc_proto", - "//dpf:status_macros", - "//dpf/internal:status_matchers", - "//dpf/internal:value_type_helpers", - "@com_github_google_googletest//:gtest_main", - "@com_google_absl//absl/numeric:int128", - ], -) diff --git a/third_party/distributed_point_functions/code/dcf/fss_gates/multiple_interval_containment.cc b/third_party/distributed_point_functions/code/dcf/fss_gates/multiple_interval_containment.cc deleted file mode 100644 index e47b436af1a0d1..00000000000000 --- a/third_party/distributed_point_functions/code/dcf/fss_gates/multiple_interval_containment.cc +++ /dev/null @@ -1,278 +0,0 @@ -// Copyright 2021 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "dcf/fss_gates/multiple_interval_containment.h" - -#include - -#include "absl/numeric/int128.h" -#include "absl/status/status.h" -#include "absl/status/statusor.h" -#include "dcf/distributed_comparison_function.h" -#include "dcf/fss_gates/multiple_interval_containment.pb.h" -#include "dcf/fss_gates/prng/basic_rng.h" -#include "dpf/distributed_point_function.pb.h" -#include "dpf/status_macros.h" - -namespace distributed_point_functions { -namespace fss_gates { - -absl::StatusOr> -MultipleIntervalContainmentGate::Create(const MicParameters& mic_parameters) { - // Declaring the parameters for a Distributed Comparison Function (DCF). - DcfParameters dcf_parameters; - - // Return error if log_group_size is not between 0 and 127. - if (mic_parameters.log_group_size() < 0 || - mic_parameters.log_group_size() > 127) { - return absl::InvalidArgumentError( - "log_group_size should be in > 0 and < 128"); - } - - // Setting N = 2 ^ log_group_size. - absl::uint128 N = absl::uint128(1) << mic_parameters.log_group_size(); - - for (int i = 0; i < mic_parameters.intervals_size(); i++) { - // Return error if some interval is empty. - if (!mic_parameters.intervals(i).has_lower_bound() || - !mic_parameters.intervals(i).has_upper_bound()) { - return absl::InvalidArgumentError("Intervals should be non-empty"); - } - - absl::uint128 p = absl::MakeUint128( - mic_parameters.intervals(i).lower_bound().value_uint128().high(), - mic_parameters.intervals(i).lower_bound().value_uint128().low()); - - absl::uint128 q = absl::MakeUint128( - mic_parameters.intervals(i).upper_bound().value_uint128().high(), - mic_parameters.intervals(i).upper_bound().value_uint128().low()); - - // Return error if the intervals are not valid group elements. - if (p < 0 || p >= N) { - return absl::InvalidArgumentError( - "Interval bounds should be between 0 and 2^log_group_size"); - } - - // Return error if the intervals are not valid group elements. - if (q < 0 || q >= N) { - return absl::InvalidArgumentError( - "Interval bounds should be between 0 and 2^log_group_size"); - } - - // Return error if lower bound of an interval is less that its - // upper bound. - if (p > q) { - return absl::InvalidArgumentError( - "Interval upper bounds should be >= lower bound"); - } - } - - // Setting the `log_domain_size` of the DCF to be same as the - // `log_group_size` of the Multiple Interval Containment Gate. - dcf_parameters.mutable_parameters()->set_log_domain_size( - mic_parameters.log_group_size()); - - // Setting the output ValueType of the DCF so that it can store 128 bit - // integers. - *(dcf_parameters.mutable_parameters()->mutable_value_type()) = - ToValueType(); - - // Creating a DCF with appropriate parameters. - DPF_ASSIGN_OR_RETURN(std::unique_ptr dcf, - DistributedComparisonFunction::Create(dcf_parameters)); - - return absl::WrapUnique( - new MultipleIntervalContainmentGate(mic_parameters, std::move(dcf))); -} - -MultipleIntervalContainmentGate::MultipleIntervalContainmentGate( - MicParameters mic_parameters, - std::unique_ptr dcf) - : mic_parameters_(std::move(mic_parameters)), dcf_(std::move(dcf)) {} - -absl::StatusOr> MultipleIntervalContainmentGate::Gen( - absl::uint128 r_in, std::vector r_out) { - if (r_out.size() != mic_parameters_.intervals_size()) { - return absl::InvalidArgumentError( - "Count of output masks should be equal to the number of intervals"); - } - - // Setting N = 2 ^ log_group_size. - absl::uint128 N = absl::uint128(1) << mic_parameters_.log_group_size(); - - // Checking whether r_in is a group element. - if (r_in < 0 || r_in >= N) { - return absl::InvalidArgumentError( - "Input mask should be between 0 and 2^log_group_size"); - } - - // Checking whether each element of r_out is a group element. - for (int i = 0; i < r_out.size(); i++) { - if (r_out[i] < 0 || r_out[i] >= N) { - return absl::InvalidArgumentError( - "Output mask should be between 0 and 2^log_group_size"); - } - } - - // The following code is commented using their Line numbering in - // https://eprint.iacr.org/2020/1392 Fig. 14 Gen procedure. - - // Line 1 - absl::uint128 gamma = (N - 1 + r_in) % N; - - // Line 2 - - DcfKey key_0, key_1; - - absl::uint128 alpha = gamma; - absl::uint128 beta = 1; - - DPF_ASSIGN_OR_RETURN(std::tie(key_0, key_1), - this->dcf_->GenerateKeys(alpha, beta)); - - MicKey k0, k1; - - // Part of Line 7 - *(k0.mutable_dcfkey()) = key_0; - *(k1.mutable_dcfkey()) = key_1; - - // Line 3 - for (int i = 0; i < mic_parameters_.intervals_size(); i++) { - absl::uint128 p = absl::MakeUint128( - mic_parameters_.intervals(i).lower_bound().value_uint128().high(), - mic_parameters_.intervals(i).lower_bound().value_uint128().low()); - - absl::uint128 q = absl::MakeUint128( - mic_parameters_.intervals(i).upper_bound().value_uint128().high(), - mic_parameters_.intervals(i).upper_bound().value_uint128().low()); - - // Line 4 - absl::uint128 q_prime = (q + 1) % N; - - absl::uint128 alpha_p = (p + r_in) % N; - - absl::uint128 alpha_q = (q + r_in) % N; - - absl::uint128 alpha_q_prime = (q + 1 + r_in) % N; - - // Line 5 - This computes the correction term for the output of the gate, - // and the logic and proof of its correctness is described in - // https://eprint.iacr.org/2020/1392 Lemma 1, Lemma 2 and Theorem 3. - absl::uint128 z = - r_out[i] + (alpha_p > alpha_q ? 1 : 0) + (alpha_p > p ? -1 : 0) + - (alpha_q_prime > q_prime ? 1 : 0) + (alpha_q == (N - 1) ? 1 : 0); - z = z % N; - - const absl::string_view kSampleSeed = absl::string_view(); - DPF_ASSIGN_OR_RETURN( - auto rng, distributed_point_functions::BasicRng::Create(kSampleSeed)); - DPF_ASSIGN_OR_RETURN(absl::uint128 z_0, rng->Rand128()); - - z_0 = z_0 % N; - - absl::uint128 z_1 = (z - z_0) % N; - - // Part of Line 7 - Value_Integer* k0_output_mask_share = k0.add_output_mask_share(); - - k0_output_mask_share->mutable_value_uint128()->set_high( - absl::Uint128High64(z_0)); - k0_output_mask_share->mutable_value_uint128()->set_low( - absl::Uint128Low64(z_0)); - - Value_Integer* k1_output_mask_share = k1.add_output_mask_share(); - - k1_output_mask_share->mutable_value_uint128()->set_high( - absl::Uint128High64(z_1)); - k1_output_mask_share->mutable_value_uint128()->set_low( - absl::Uint128Low64(z_1)); - } - - // Line 8 - return std::pair(k0, k1); -} - -absl::StatusOr> -MultipleIntervalContainmentGate::Eval(MicKey k, absl::uint128 x) { - // Setting N = 2 ^ log_group_size - absl::uint128 N = absl::uint128(1) << mic_parameters_.log_group_size(); - - // Checking whether x is a group element - if (x < 0 || x >= N) { - return absl::InvalidArgumentError( - "Masked input should be between 0 and 2^log_group_size"); - } - - std::vector res; - - // The following code is commented using their Line numbering in - // https://eprint.iacr.org/2020/1392 Fig. 14 Eval procedure. - - // Line 2 - for (int i = 0; i < mic_parameters_.intervals_size(); i++) { - absl::uint128 p = absl::MakeUint128( - mic_parameters_.intervals(i).lower_bound().value_uint128().high(), - mic_parameters_.intervals(i).lower_bound().value_uint128().low()); - - absl::uint128 q = absl::MakeUint128( - mic_parameters_.intervals(i).upper_bound().value_uint128().high(), - mic_parameters_.intervals(i).upper_bound().value_uint128().low()); - - // Line 3 - - absl::uint128 q_prime = (q + 1) % N; - - // Line 4 - absl::uint128 x_p = (x + N - 1 - p) % N; - - absl::uint128 x_q_prime = (x + N - 1 - q_prime) % N; - - // Line 5 - - absl::uint128 s_p; - - DPF_ASSIGN_OR_RETURN(s_p, dcf_->Evaluate(k.dcfkey(), x_p)); - - s_p = s_p % N; - - // Line 6 - - absl::uint128 s_q_prime; - - DPF_ASSIGN_OR_RETURN(s_q_prime, - dcf_->Evaluate(k.dcfkey(), x_q_prime)); - - s_q_prime = s_q_prime % N; - - // Line 7 - - absl::uint128 y; - - absl::uint128 z = - absl::MakeUint128(k.output_mask_share(i).value_uint128().high(), - k.output_mask_share(i).value_uint128().low()); - - y = (k.dcfkey().key().party() ? ((x > p ? 1 : 0) - (x > q_prime ? 1 : 0)) - : 0) - - s_p + s_q_prime + z; - - res.push_back(y % N); - } - - // Line 9 - return res; -} - -} // namespace fss_gates -} // namespace distributed_point_functions diff --git a/third_party/distributed_point_functions/code/dcf/fss_gates/multiple_interval_containment.h b/third_party/distributed_point_functions/code/dcf/fss_gates/multiple_interval_containment.h deleted file mode 100644 index cb8c203bc1012c..00000000000000 --- a/third_party/distributed_point_functions/code/dcf/fss_gates/multiple_interval_containment.h +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright 2021 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef DISTRIBUTED_POINT_FUNCTIONS_DCF_FSS_GATES_MULTIPLE_INTERVAL_CONTAINMENT_H_ -#define DISTRIBUTED_POINT_FUNCTIONS_DCF_FSS_GATES_MULTIPLE_INTERVAL_CONTAINMENT_H_ -#include - -#include "absl/numeric/int128.h" -#include "absl/status/statusor.h" -#include "dcf/distributed_comparison_function.h" -#include "dcf/fss_gates/multiple_interval_containment.pb.h" -#include "dpf/status_macros.h" - -namespace distributed_point_functions { -namespace fss_gates { - -// Implements the Multiple Interval Containment gate as specified in -// https://eprint.iacr.org/2020/1392 (Fig. 14). Such a gate is specified by -// input and output group Z_{2 ^ n} where n is the bit length and a sequence -// of `m` public intervals {p_i, q_i}_{i \in [m]}. The Key generation procedure -// produces two keys, k_0 and k_1, corresponding to Party 0 and Party 1 -// respectively. Evaluating each key on any point `x` in the input group results -// in an additive secret share of m values {y_i}_{i \in [m]} where y_i = `1`, if -// `p_i <= x <= q_i`, and 0 otherwise. - -class MultipleIntervalContainmentGate { - public: - // Factory method : creates and returns a MultipleIntervalContainmentGate - // initialized with appropriate parameters. - static absl::StatusOr> - Create(const MicParameters& mic_parameters); - - // MultipleIntervalContainmentGate is neither copyable nor movable. - MultipleIntervalContainmentGate(const MultipleIntervalContainmentGate&) = - delete; - MultipleIntervalContainmentGate& operator=( - const MultipleIntervalContainmentGate&) = delete; - - // This method generates Multiple Interval Containment Gate a pair of keys - // using `r_in` and `r_out` as the input mask and output masks respectively. - // The implementation of this method is identical to Gen procedure specified - // in https://eprint.iacr.org/2020/1392 (Fig. 14). Note that although the - // datatype of r_in and r_out is absl::uint128, but they will be interpreted - // as an element in the input and output group Z_{2 ^ n} respectively. This - // reinterpretion in the group is achieved simply by taking mod of r_in and - // r_out with the size of group i.e. 2^{log_group_size}. - - // This method expects that the size of r_out vector be exactly same as the - // number of public intervals in the MicParameters. This is because for - // each interval in MIC, we will need an independent output mask to hide - // the actual cleartext output of the MIC on that interval. This method - // will return InvalidArgumentError. - - // Returns INVALID_ARGUMENT if the size of r_out vector does not match the - // number of intervals specified during construction. - absl::StatusOr> Gen( - absl::uint128 r_in, std::vector r_out); - - // This method evaluates the Multiple Interval Containment Gate key k - // on input domain point `x`. The output is returned as a 128 bit string - // and needs to be interpreted as an element in the output group Z_{2 ^ n}. - absl::StatusOr> Eval(MicKey k, absl::uint128 x); - - private: - // Parameters needed for specifying a Multiple Interval Containment Gate. - const MicParameters mic_parameters_; - - // Private constructor, called by `Create` - MultipleIntervalContainmentGate( - MicParameters mic_parameters, - std::unique_ptr dcf); - - // Pointer to a Distributed Comparison Function which will be internally - // invoked by Gen and Eval. - std::unique_ptr dcf_; -}; - -} // namespace fss_gates -} // namespace distributed_point_functions - -#endif // DISTRIBUTED_POINT_FUNCTIONS_DCF_FSS_GATES_MULTIPLE_INTERVAL_CONTAINMENT_H_ diff --git a/third_party/distributed_point_functions/code/dcf/fss_gates/multiple_interval_containment.proto b/third_party/distributed_point_functions/code/dcf/fss_gates/multiple_interval_containment.proto deleted file mode 100644 index 54551fdd7d344b..00000000000000 --- a/third_party/distributed_point_functions/code/dcf/fss_gates/multiple_interval_containment.proto +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright 2021 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -syntax = "proto3"; - -package distributed_point_functions.fss_gates; - -import "dcf/distributed_comparison_function.proto"; -import "dpf/distributed_point_function.proto"; - -// Represents an interval on the group G = Z_N. -message Interval { - // Represents the lower limit of the interval. This corresponds to `p_i` - // used in https://eprint.iacr.org/2020/1392 (Fig. 14). - Value.Integer lower_bound = 1; - - // Represents the upper limit of the interval. This corresponds to `q_i` - // used in https://eprint.iacr.org/2020/1392 (Fig. 14). - Value.Integer upper_bound = 2; -} - -message MicParameters { - // Represents the bit length of the input to the Multiple Interval Containment - // gate. This corresponds to `n` used in https://eprint.iacr.org/2020/1392 - // (Fig. 14). Here we assume that if `n` is the input bit-length, then the - // input and output group of the gate is implicitly Z_N where N = 2^n, and - // hence the variable name "log_group_size". Maximum allowed log_group_size - // is 127 and minimum value should be at least the number of bits required to - // store each interval boundary. - int32 log_group_size = 1; - - // Represents a sequence of public intervals. This corresponds to `{p_i, q_i}` - // used in https://eprint.iacr.org/2020/1392 (Fig. 14). - repeated Interval intervals = 2; -} - -// Represents a key for Multiple Interval Containment gate. This corresponds to -//`k_b` used in https://eprint.iacr.org/2020/1392 (Fig. 14). The key implicitly -// corresponds to the MicParameters used to generate this key. -message MicKey { - // Represents a Distributed Comparison Function Key. This corresponds to - //`k_b^(N - 1)` used in https://eprint.iacr.org/2020/1392 (Fig. 14). - DcfKey dcfkey = 1; - - // Represents output mask shares corresponding to each of the m different - // intervals. This corresponds to `{z_i,b}_i` used in - // https://eprint.iacr.org/2020/1392 (Fig. 14). - repeated Value.Integer output_mask_share = 2; -} diff --git a/third_party/distributed_point_functions/code/dcf/fss_gates/multiple_interval_containment_test.cc b/third_party/distributed_point_functions/code/dcf/fss_gates/multiple_interval_containment_test.cc deleted file mode 100644 index b24904a5b71321..00000000000000 --- a/third_party/distributed_point_functions/code/dcf/fss_gates/multiple_interval_containment_test.cc +++ /dev/null @@ -1,543 +0,0 @@ -// Copyright 2021 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "dcf/fss_gates/multiple_interval_containment.h" - -#include -#include - -#include -#include - -#include "absl/numeric/int128.h" -#include "dcf/fss_gates/multiple_interval_containment.pb.h" -#include "dcf/fss_gates/prng/basic_rng.h" -#include "dpf/distributed_point_function.pb.h" -#include "dpf/internal/status_matchers.h" -#include "dpf/internal/value_type_helpers.h" -#include "dpf/status_macros.h" - -namespace distributed_point_functions { -namespace fss_gates { -namespace { - -using ::testing::Test; - -TEST(MICTest, GenAndEvalSucceedsForSmallGroup) { - MicParameters mic_parameters; - const int group_size = 64; - const uint64_t interval_count = 5; - - // Setting input and output group to be Z_{2^64} - mic_parameters.set_log_group_size(group_size); - - // Setting up the lower bound and upper bounds for intervals - std::vector ps{10, 23, 45, 66, 15}; - std::vector qs{45, 30, 100, 250, 15}; - - for (int i = 0; i < interval_count; ++i) { - Interval* interval = mic_parameters.add_intervals(); - - interval->mutable_lower_bound()->mutable_value_uint128()->set_low( - absl::Uint128Low64(ps[i])); - - interval->mutable_upper_bound()->mutable_value_uint128()->set_low( - absl::Uint128Low64(qs[i])); - } - - // Creating a MIC gate - DPF_ASSERT_OK_AND_ASSIGN( - std::unique_ptr MicGate, - MultipleIntervalContainmentGate::Create(mic_parameters)); - - MicKey key_0, key_1; - - // Initializing the input and output masks uniformly at random; - const absl::string_view kSampleSeed = absl::string_view(); - DPF_ASSERT_OK_AND_ASSIGN( - auto rng, distributed_point_functions::BasicRng::Create(kSampleSeed)); - - absl::uint128 N = absl::uint128(1) << mic_parameters.log_group_size(); - - DPF_ASSERT_OK_AND_ASSIGN(absl::uint128 r_in, rng->Rand64()); - r_in = r_in % N; - - std::vector r_outs; - - for (int i = 0; i < interval_count; ++i) { - DPF_ASSERT_OK_AND_ASSIGN(absl::uint128 r_out, rng->Rand64()); - r_out = r_out % N; - r_outs.push_back(r_out); - } - - // Generating MIC gate keys - DPF_ASSERT_OK_AND_ASSIGN(std::tie(key_0, key_1), MicGate->Gen(r_in, r_outs)); - - // Inside this loop we will test the Evaluation of the MIC gate on - // input values ranging between [0, 400) - for (uint64_t i = 0; i < 400; i++) { - std::vector res_0, res_1; - - // Evaluating MIC gate key_0 on masked input i + r_in - DPF_ASSERT_OK_AND_ASSIGN(res_0, MicGate->Eval(key_0, (i + r_in) % N)); - - // Evaluating MIC gate key_1 on masked input i + r_in - DPF_ASSERT_OK_AND_ASSIGN(res_1, MicGate->Eval(key_1, (i + r_in) % N)); - - // Reconstructing the actual output of the MIC gate by adding together - // the secret shared output res_0 and res_1, and then subtracting out - // the output mask r_out - for (int j = 0; j < interval_count; j++) { - absl::uint128 result = (res_0[j] + res_1[j] - r_outs[j]) % N; - - // If the input i lies inside the j^th interval, then the expected - // output of MIC gate is 1, and 0 otherwise - if (i >= ps[j] && i <= qs[j]) { - EXPECT_EQ(result, 1); - } else { - EXPECT_EQ(result, 0); - } - } - } -} - -TEST(MICTest, GenAndEvalSucceedsForLargeGroup) { - MicParameters mic_parameters; - const int group_size = 127; - const uint64_t interval_count = 3; - const absl::uint128 two_power_127 = absl::uint128(1) << 127; - const absl::uint128 two_power_126 = absl::uint128(1) << 126; - - // Setting input and output group to be Z_{2^127} - mic_parameters.set_log_group_size(group_size); - - // Setting up the lower bound and upper bounds for intervals - - std::vector ps{two_power_126, two_power_127 - 1, - two_power_127 - 3}; - std::vector qs{two_power_126 + 3, two_power_127 - 1, - two_power_127 - 2}; - - std::vector x{two_power_126 - 1, two_power_126, - two_power_126 + 1, two_power_126 + 2, - two_power_126 + 3, two_power_126 + 4, - two_power_127 - 4, two_power_127 - 3, - two_power_127 - 2, two_power_127 - 1}; - - for (int i = 0; i < interval_count; ++i) { - Interval* interval = mic_parameters.add_intervals(); - - interval->mutable_lower_bound()->mutable_value_uint128()->set_high( - absl::Uint128High64(ps[i])); - interval->mutable_lower_bound()->mutable_value_uint128()->set_low( - absl::Uint128Low64(ps[i])); - - interval->mutable_upper_bound()->mutable_value_uint128()->set_high( - absl::Uint128High64(qs[i])); - interval->mutable_upper_bound()->mutable_value_uint128()->set_low( - absl::Uint128Low64(qs[i])); - } - - // Creating a MIC gate - DPF_ASSERT_OK_AND_ASSIGN( - std::unique_ptr MicGate, - MultipleIntervalContainmentGate::Create(mic_parameters)); - - MicKey key_0, key_1; - - absl::uint128 N = absl::uint128(1) << mic_parameters.log_group_size(); - - // Initializing the input and output masks uniformly at random; - const absl::string_view kSampleSeed = absl::string_view(); - DPF_ASSERT_OK_AND_ASSIGN( - auto rng, distributed_point_functions::BasicRng::Create(kSampleSeed)); - - DPF_ASSERT_OK_AND_ASSIGN(absl::uint128 r_in, rng->Rand128()); - - r_in = r_in % N; - - std::vector r_outs; - - for (int i = 0; i < interval_count; ++i) { - DPF_ASSERT_OK_AND_ASSIGN(absl::uint128 r_out, rng->Rand128()); - r_out = r_out % N; - r_outs.push_back(r_out); - } - - // Generating MIC gate keys - DPF_ASSERT_OK_AND_ASSIGN(std::tie(key_0, key_1), MicGate->Gen(r_in, r_outs)); - - // Inside this loop we will test the Evaluation of the MIC gate on - // input values in the vicinity of interval boundaries which are hardcoded - // in the vector x. - for (uint64_t i = 0; i < x.size(); i++) { - std::vector res_0, res_1; - - // Evaluating MIC gate key_0 on masked input - DPF_ASSERT_OK_AND_ASSIGN(res_0, MicGate->Eval(key_0, (x[i] + r_in) % N)); - - // Evaluating MIC gate key_1 on masked input - DPF_ASSERT_OK_AND_ASSIGN(res_1, MicGate->Eval(key_1, (x[i] + r_in) % N)); - - // Reconstructing the actual output of the MIC gate by adding together - // the secret shared output res_0 and res_1, and then subtracting out - // the output mask r_out - for (int j = 0; j < interval_count; j++) { - absl::uint128 result = (res_0[j] + res_1[j] - r_outs[j]) % N; - - // If the input lies inside the j^th interval, then the expected - // output of MIC gate is 1, and 0 otherwise - if (x[i] >= ps[j] && x[i] <= qs[j]) { - EXPECT_EQ(result, 1); - } else { - EXPECT_EQ(result, 0); - } - } - } -} - -TEST(MICTest, CreateFailsWith128bitGroup) { - MicParameters mic_parameters; - const int group_size = 128; - const uint64_t interval_count = 5; - - // Setting input and output group to be Z_{2^128} - mic_parameters.set_log_group_size(group_size); - - // Setting up the lower bound and upper bounds for intervals - std::vector ps{10, 23, 45, 66, 15}; - std::vector qs{45, 30, 100, 250, 15}; - - for (int i = 0; i < interval_count; ++i) { - Interval* interval = mic_parameters.add_intervals(); - - interval->mutable_lower_bound()->mutable_value_uint128()->set_low( - absl::Uint128Low64(ps[i])); - - interval->mutable_upper_bound()->mutable_value_uint128()->set_low( - absl::Uint128Low64(qs[i])); - } - - EXPECT_THAT( - MultipleIntervalContainmentGate::Create(mic_parameters), - dpf_internal::StatusIs(absl::StatusCode::kInvalidArgument, - "log_group_size should be in > 0 and < 128")); -} - -TEST(MICTest, CreateFailsForIntervalBoundariesOutsideGroup) { - MicParameters mic_parameters; - const int group_size = 20; - const uint64_t interval_count = 1; - const absl::uint128 two_power_20 = absl::uint128(1) << 20; - - // Setting input and output group to be Z_{2^20} - mic_parameters.set_log_group_size(group_size); - - // Setting up the lower bound and upper bounds for intervals - std::vector ps{4}; - std::vector qs{two_power_20}; - - for (int i = 0; i < interval_count; ++i) { - Interval* interval = mic_parameters.add_intervals(); - - interval->mutable_lower_bound()->mutable_value_uint128()->set_low( - absl::Uint128Low64(ps[i])); - - interval->mutable_upper_bound()->mutable_value_uint128()->set_low( - absl::Uint128Low64(qs[i])); - } - - EXPECT_THAT(MultipleIntervalContainmentGate::Create(mic_parameters), - dpf_internal::StatusIs( - absl::StatusCode::kInvalidArgument, - "Interval bounds should be between 0 and 2^log_group_size")); -} - -TEST(MICTest, CreateFailsForInvalidIntervalBoundaries) { - MicParameters mic_parameters; - const int group_size = 20; - const uint64_t interval_count = 1; - - // Setting input and output group to be Z_{2^20} - mic_parameters.set_log_group_size(group_size); - - // Setting up the lower bound and upper bounds for intervals - std::vector ps{4}; - std::vector qs{3}; - - for (int i = 0; i < interval_count; ++i) { - Interval* interval = mic_parameters.add_intervals(); - - interval->mutable_lower_bound()->mutable_value_uint128()->set_low( - absl::Uint128Low64(ps[i])); - - interval->mutable_upper_bound()->mutable_value_uint128()->set_low( - absl::Uint128Low64(qs[i])); - } - - EXPECT_THAT( - MultipleIntervalContainmentGate::Create(mic_parameters), - dpf_internal::StatusIs(absl::StatusCode::kInvalidArgument, - "Interval upper bounds should be >= lower bound")); -} - -TEST(MICTest, CreateFailsForEmptyInterval) { - MicParameters mic_parameters; - const int group_size = 20; - const uint64_t interval_count = 1; - - // Setting input and output group to be Z_{2^20} - mic_parameters.set_log_group_size(group_size); - - // Setting up the lower bound and upper bounds for intervals - std::vector ps{}; - std::vector qs{3}; - - for (int i = 0; i < interval_count; ++i) { - Interval* interval = mic_parameters.add_intervals(); - - // Only setting upper bound (and skipping lower bound) - interval->mutable_upper_bound()->mutable_value_uint128()->set_low( - absl::Uint128Low64(qs[i])); - } - - EXPECT_THAT(MultipleIntervalContainmentGate::Create(mic_parameters), - dpf_internal::StatusIs(absl::StatusCode::kInvalidArgument, - "Intervals should be non-empty")); -} - -TEST(MICTest, GenFailsForIncorrectNumberOfOutputMasks) { - MicParameters mic_parameters; - const int group_size = 64; - const uint64_t interval_count = 5; - - // Setting input and output group to be Z_{2^64} - mic_parameters.set_log_group_size(group_size); - - // Setting up the lower bound and upper bounds for intervals - std::vector ps{10, 23, 45, 66, 15}; - std::vector qs{45, 30, 100, 250, 15}; - - for (int i = 0; i < interval_count; ++i) { - Interval* interval = mic_parameters.add_intervals(); - - interval->mutable_lower_bound()->mutable_value_uint128()->set_low( - absl::Uint128Low64(ps[i])); - - interval->mutable_upper_bound()->mutable_value_uint128()->set_low( - absl::Uint128Low64(qs[i])); - } - - // Creating a MIC gate - DPF_ASSERT_OK_AND_ASSIGN( - std::unique_ptr MicGate, - MultipleIntervalContainmentGate::Create(mic_parameters)); - - MicKey key_0, key_1; - - absl::uint128 N = absl::uint128(1) << mic_parameters.log_group_size(); - - // Initializing the input and output masks uniformly at random; - const absl::string_view kSampleSeed = absl::string_view(); - DPF_ASSERT_OK_AND_ASSIGN( - auto rng, distributed_point_functions::BasicRng::Create(kSampleSeed)); - - DPF_ASSERT_OK_AND_ASSIGN(absl::uint128 r_in, rng->Rand64()); - r_in = r_in % N; - - std::vector r_outs; - - // Setting only (interval_count - 1) many output masks - for (int i = 0; i < interval_count - 1; ++i) { - DPF_ASSERT_OK_AND_ASSIGN(absl::uint128 r_out, rng->Rand64()); - r_out = r_out % N; - r_outs.push_back(r_out); - } - - // Generating MIC gate keys - EXPECT_THAT( - MicGate->Gen(r_in, r_outs), - dpf_internal::StatusIs( - absl::StatusCode::kInvalidArgument, - "Count of output masks should be equal to the number of intervals")); -} - -TEST(MICTest, GenFailsForInputMaskOutsideGroup) { - MicParameters mic_parameters; - const int group_size = 10; - const uint64_t interval_count = 1; - - // Setting input and output group to be Z_{2^10} - mic_parameters.set_log_group_size(group_size); - - // Setting up the lower bound and upper bounds for intervals - std::vector ps{10}; - std::vector qs{45}; - - for (int i = 0; i < interval_count; ++i) { - Interval* interval = mic_parameters.add_intervals(); - - interval->mutable_lower_bound()->mutable_value_uint128()->set_low( - absl::Uint128Low64(ps[i])); - - interval->mutable_upper_bound()->mutable_value_uint128()->set_low( - absl::Uint128Low64(qs[i])); - } - - // Creating a MIC gate - DPF_ASSERT_OK_AND_ASSIGN( - std::unique_ptr MicGate, - MultipleIntervalContainmentGate::Create(mic_parameters)); - - MicKey key_0, key_1; - - absl::uint128 N = absl::uint128(1) << mic_parameters.log_group_size(); - - const absl::string_view kSampleSeed = absl::string_view(); - DPF_ASSERT_OK_AND_ASSIGN( - auto rng, distributed_point_functions::BasicRng::Create(kSampleSeed)); - - // Fixing r_in to be an element outside group - absl::uint128 r_in = 2048; - - std::vector r_outs; - - // Initializing the output masks uniformly at random; - for (int i = 0; i < interval_count; ++i) { - DPF_ASSERT_OK_AND_ASSIGN(absl::uint128 r_out, rng->Rand64()); - r_out = r_out % N; - r_outs.push_back(r_out); - } - - // Generating MIC gate keys - EXPECT_THAT(MicGate->Gen(r_in, r_outs), - dpf_internal::StatusIs( - absl::StatusCode::kInvalidArgument, - "Input mask should be between 0 and 2^log_group_size")); -} - -TEST(MICTest, GenFailsForOutputMaskOutsideGroup) { - MicParameters mic_parameters; - const int group_size = 10; - const uint64_t interval_count = 1; - - // Setting input and output group to be Z_{2^10} - mic_parameters.set_log_group_size(group_size); - - // Setting up the lower bound and upper bounds for intervals - std::vector ps{10}; - std::vector qs{45}; - - for (int i = 0; i < interval_count; ++i) { - Interval* interval = mic_parameters.add_intervals(); - - interval->mutable_lower_bound()->mutable_value_uint128()->set_low( - absl::Uint128Low64(ps[i])); - - interval->mutable_upper_bound()->mutable_value_uint128()->set_low( - absl::Uint128Low64(qs[i])); - } - - // Creating a MIC gate - DPF_ASSERT_OK_AND_ASSIGN( - std::unique_ptr MicGate, - MultipleIntervalContainmentGate::Create(mic_parameters)); - - MicKey key_0, key_1; - - absl::uint128 N = absl::uint128(1) << mic_parameters.log_group_size(); - - const absl::string_view kSampleSeed = absl::string_view(); - DPF_ASSERT_OK_AND_ASSIGN( - auto rng, distributed_point_functions::BasicRng::Create(kSampleSeed)); - - // Initializing the input masks uniformly at random; - DPF_ASSERT_OK_AND_ASSIGN(absl::uint128 r_in, rng->Rand64()); - r_in = r_in % N; - - std::vector r_outs; - - // Fixing the output masks to be elements outside group; - for (int i = 0; i < interval_count; ++i) { - absl::uint128 r_out = 2048; - r_outs.push_back(r_out); - } - - // Generating MIC gate keys - EXPECT_THAT(MicGate->Gen(r_in, r_outs), - dpf_internal::StatusIs( - absl::StatusCode::kInvalidArgument, - "Output mask should be between 0 and 2^log_group_size")); -} - -TEST(MICTest, EvalFailsForMaskedInputOutsideGroup) { - MicParameters mic_parameters; - const int group_size = 64; - const uint64_t interval_count = 1; - - // Setting input and output group to be Z_{2^64} - mic_parameters.set_log_group_size(group_size); - - // Setting up the lower bound and upper bounds for intervals - std::vector ps{10}; - std::vector qs{45}; - - for (int i = 0; i < interval_count; ++i) { - Interval* interval = mic_parameters.add_intervals(); - - interval->mutable_lower_bound()->mutable_value_uint128()->set_low( - absl::Uint128Low64(ps[i])); - - interval->mutable_upper_bound()->mutable_value_uint128()->set_low( - absl::Uint128Low64(qs[i])); - } - - // Creating a MIC gate - DPF_ASSERT_OK_AND_ASSIGN( - std::unique_ptr MicGate, - MultipleIntervalContainmentGate::Create(mic_parameters)); - - MicKey key_0, key_1; - - // Initializing the input and output masks uniformly at random; - const absl::string_view kSampleSeed = absl::string_view(); - DPF_ASSERT_OK_AND_ASSIGN( - auto rng, distributed_point_functions::BasicRng::Create(kSampleSeed)); - - absl::uint128 N = absl::uint128(1) << mic_parameters.log_group_size(); - - DPF_ASSERT_OK_AND_ASSIGN(absl::uint128 r_in, rng->Rand64()); - r_in = r_in % N; - - std::vector r_outs; - - for (int i = 0; i < interval_count; ++i) { - DPF_ASSERT_OK_AND_ASSIGN(absl::uint128 r_out, rng->Rand64()); - r_out = r_out % N; - r_outs.push_back(r_out); - } - - // Generating MIC gate keys - DPF_ASSERT_OK_AND_ASSIGN(std::tie(key_0, key_1), MicGate->Gen(r_in, r_outs)); - - // Calling Eval on a masked input which is not a group element - EXPECT_THAT(MicGate->Eval(key_0, absl::uint128(1) << 72), - dpf_internal::StatusIs( - absl::StatusCode::kInvalidArgument, - "Masked input should be between 0 and 2^log_group_size")); -} - -} // namespace -} // namespace fss_gates -} // namespace distributed_point_functions diff --git a/third_party/distributed_point_functions/code/dcf/fss_gates/prng/BUILD b/third_party/distributed_point_functions/code/dcf/fss_gates/prng/BUILD deleted file mode 100644 index 160fa9a9af707e..00000000000000 --- a/third_party/distributed_point_functions/code/dcf/fss_gates/prng/BUILD +++ /dev/null @@ -1,42 +0,0 @@ -load("@io_bazel_rules_go//proto:def.bzl", "go_proto_library") - -package( - default_visibility = ["//:__subpackages__"], -) - -licenses(["notice"]) - -cc_library( - name = "prng", - hdrs = ["prng.h"], - deps = [ - "//dpf:status_macros", - "@com_google_absl//absl/numeric:int128", - "@com_google_absl//absl/status:statusor", - "@com_google_absl//absl/strings", - ], -) - -cc_library( - name = "basic_rng", - hdrs = ["basic_rng.h"], - deps = [ - ":prng", - "@boringssl//:crypto", - "@com_google_absl//absl/base", - "@com_google_absl//absl/memory", - "@com_google_absl//absl/numeric:int128", - "@com_google_absl//absl/strings", - ], -) - -cc_test( - name = "basic_rng_test", - srcs = ["basic_rng_test.cc"], - deps = [ - ":basic_rng", - "//dpf/internal:status_matchers", - "@com_github_google_googletest//:gtest_main", - "@com_google_absl//absl/numeric:int128", - ], -) diff --git a/third_party/distributed_point_functions/code/dcf/fss_gates/prng/basic_rng.h b/third_party/distributed_point_functions/code/dcf/fss_gates/prng/basic_rng.h deleted file mode 100644 index 13b0da8b0dd9db..00000000000000 --- a/third_party/distributed_point_functions/code/dcf/fss_gates/prng/basic_rng.h +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright 2021 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef DISTRIBUTED_POINT_FUNCTIONS_PRNG_BASIC_RNG_H_ -#define DISTRIBUTED_POINT_FUNCTIONS_PRNG_BASIC_RNG_H_ - -#include - -#include "absl/base/casts.h" -#include "absl/memory/memory.h" -#include "absl/numeric/int128.h" -#include "absl/strings/string_view.h" -#include "dcf/fss_gates/prng/prng.h" - -namespace distributed_point_functions { - -// Basic RNG class that uses RAND_bytes from OpenSSL to sample randomness. -// BasicRng does not require a seed internally. -class BasicRng : public SecurePrng { - public: - // Create a BasicRng object. - // Returns an INTERNAL error code if the creation fails. - static absl::StatusOr> Create( - absl::string_view seed) { - return absl::make_unique(); - } - - // Sample 8 bits of randomness using OpenSSL RAND_bytes. - // Returns an INTERNAL error code if the sampling fails. - inline absl::StatusOr Rand8() override { return Rand(); } - - // Sample 64 bits of randomness using OPENSSL RAND_bytes. - // Returns an INTERNAL error code if the sampling fails. - inline absl::StatusOr Rand64() override { return Rand(); } - - // Sample 128 bits of randomness using OPENSSL RAND_bytes. - // Returns an INTERNAL error code if the sampling fails. - inline absl::StatusOr Rand128() override { - return Rand(); - } - - // BasicRng does not use seeds. - static absl::StatusOr GenerateSeed() { return std::string(); } - static int SeedLength() { return 0; } - - private: - template - absl::StatusOr Rand() { - std::array rand; - int success = RAND_bytes(rand.data(), rand.size()); - if (!success) { - return absl::InternalError( - "BasicRng::Rand - Failed to create randomness"); - } - return absl::bit_cast(rand); - } -}; - -} // namespace distributed_point_functions - -#endif // DISTRIBUTED_POINT_FUNCTIONS_PRNG_BASIC_RNG_H_ diff --git a/third_party/distributed_point_functions/code/dcf/fss_gates/prng/basic_rng_test.cc b/third_party/distributed_point_functions/code/dcf/fss_gates/prng/basic_rng_test.cc deleted file mode 100644 index 5d955210875e46..00000000000000 --- a/third_party/distributed_point_functions/code/dcf/fss_gates/prng/basic_rng_test.cc +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright 2021 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "dcf/fss_gates/prng/basic_rng.h" - -#include -#include - -#include "absl/numeric/int128.h" -#include "dpf/internal/status_matchers.h" - -namespace distributed_point_functions { -namespace { - -using ::testing::Test; - -const absl::string_view kSampleSeed = absl::string_view(); -constexpr int kNumSamples = 10; - -class BasicRngTest : public Test { - protected: - void SetUp() { - DPF_ASSERT_OK_AND_ASSIGN(rng_, BasicRng::Create(kSampleSeed)); - } - - std::unique_ptr rng_; -}; - -TEST_F(BasicRngTest, Test8BitRand) { - // Two random 8 bit strings have 1/256 probability of being equal. Instead, - // we check that 8 consecutively generated strings are not all equal. - bool equal = true; - DPF_ASSERT_OK_AND_ASSIGN(uint8_t prev, rng_->Rand8()); - for (int i = 0; i < 8; ++i) { - DPF_ASSERT_OK_AND_ASSIGN(uint8_t next, rng_->Rand8()); - if (next != prev) { - equal = false; - } - prev = next; - } - EXPECT_FALSE(equal); -} - -TEST_F(BasicRngTest, Test64BitRand) { - DPF_ASSERT_OK_AND_ASSIGN(uint64_t r1, rng_->Rand64()); - DPF_ASSERT_OK_AND_ASSIGN(uint64_t r2, rng_->Rand64()); - EXPECT_NE(r1, r2); -} - -TEST_F(BasicRngTest, Test128BitRand) { - DPF_ASSERT_OK_AND_ASSIGN(absl::uint128 r1, rng_->Rand128()); - DPF_ASSERT_OK_AND_ASSIGN(absl::uint128 r2, rng_->Rand128()); - EXPECT_NE(r1, r2); -} - -TEST_F(BasicRngTest, BytesAreDifferent64) { - std::vector rand(kNumSamples); - for (size_t i = 0; i < kNumSamples; ++i) { - DPF_ASSERT_OK_AND_ASSIGN(rand[i], rng_->Rand64()); - } - - for (int i = 0; i < sizeof(uint64_t); ++i) { - bool not_all_equal = false; - for (int j = 1; j < kNumSamples; ++j) { - auto byte1 = static_cast(rand[j - 1] >> (8 * i)); - auto byte2 = static_cast(rand[j] >> (8 * i)); - if (byte1 != byte2) { - not_all_equal = true; - } - } - EXPECT_TRUE(not_all_equal); - } -} - -TEST_F(BasicRngTest, BytesAreDifferent128) { - std::vector rand(kNumSamples); - for (int i = 0; i < kNumSamples; ++i) { - DPF_ASSERT_OK_AND_ASSIGN(rand[i], rng_->Rand128()); - } - - for (size_t i = 0; i < sizeof(absl::uint128); ++i) { - bool not_all_equal = false; - for (int j = 1; j < kNumSamples; ++j) { - auto byte1 = static_cast(rand[j - 1] >> (8 * i)); - auto byte2 = static_cast(rand[j] >> (8 * i)); - if (byte1 != byte2) { - not_all_equal = true; - } - } - EXPECT_TRUE(not_all_equal); - } -} - -} // namespace -} // namespace distributed_point_functions diff --git a/third_party/distributed_point_functions/code/dcf/fss_gates/prng/prng.h b/third_party/distributed_point_functions/code/dcf/fss_gates/prng/prng.h deleted file mode 100644 index 1920deb57d8e83..00000000000000 --- a/third_party/distributed_point_functions/code/dcf/fss_gates/prng/prng.h +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2021 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef DISTRIBUTED_POINT_FUNCTIONS_PRNG_PRNG_H_ -#define DISTRIBUTED_POINT_FUNCTIONS_PRNG_PRNG_H_ - -#include "absl/numeric/int128.h" -#include "absl/status/statusor.h" -#include "absl/strings/string_view.h" -#include "dpf/status_macros.h" - -namespace distributed_point_functions { - -// An interface for a secure pseudo-random number generator. -class SecurePrng { - public: - virtual absl::StatusOr Rand8() = 0; - virtual absl::StatusOr Rand64() = 0; - virtual absl::StatusOr Rand128() = 0; - virtual ~SecurePrng() = default; - static absl::StatusOr> Create( - absl::string_view seed); - static absl::StatusOr GenerateSeed(); - static int SeedLength(); -}; - -} // namespace distributed_point_functions - -#endif // DISTRIBUTED_POINT_FUNCTIONS_PRNG_PRNG_H_ diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml index a5d5bcacb0b731..b75dda8258831a 100644 --- a/tools/metrics/histograms/enums.xml +++ b/tools/metrics/histograms/enums.xml @@ -73906,6 +73906,7 @@ https://www.dmtf.org/sites/default/files/standards/documents/DSP0134_2.7.1.pdf + diff --git a/ui/events/keycodes/dom/dom_code_data.inc b/ui/events/keycodes/dom/dom_code_data.inc index c96987d0a5a01c..dd8ac4938881a3 100644 --- a/ui/events/keycodes/dom/dom_code_data.inc +++ b/ui/events/keycodes/dom/dom_code_data.inc @@ -76,6 +76,13 @@ // Apple keyboards with USB 0x070049 [Insert] labelled "Help" have not // been made since 2007. +// ChromeOS notes: +// +// Any keys that are added or updated specifically for use in the ChromeOS +// top-row should also be updated in sections of Input Diagnostics: +// ash/webui/diagnostics_ui/mojom/input_data_provider.mojom: enum TopRowKey +// ash/webui/diagnostics_ui/backend/input_data_provider_keyboard.cc: kScancodeMapping + DOM_CODE_DECLARATION { // USB evdev XKB Win Mac Code diff --git a/ui/gtk/window_frame_provider_gtk.cc b/ui/gtk/window_frame_provider_gtk.cc index 4c7773f4a997a1..e9b7a9837afb06 100644 --- a/ui/gtk/window_frame_provider_gtk.cc +++ b/ui/gtk/window_frame_provider_gtk.cc @@ -8,6 +8,7 @@ #include "ui/gfx/canvas.h" #include "ui/gfx/geometry/insets.h" #include "ui/gfx/geometry/rect.h" +#include "ui/gfx/geometry/skia_conversions.h" #include "ui/gfx/scoped_canvas.h" #include "ui/gtk/gtk_compat.h" #include "ui/gtk/gtk_util.h" @@ -47,7 +48,9 @@ GtkCssContext WindowContext(bool solid_frame, bool focused) { GtkCssContext DecorationContext(bool solid_frame, bool focused) { auto context = WindowContext(solid_frame, focused); - context = AppendCssNodeToStyleContext(context, "#decoration"); + // GTK4 renders the decoration directly on the window. + if (!GtkCheckVersion(4)) + context = AppendCssNodeToStyleContext(context, "#decoration"); if (!focused) gtk_style_context_set_state(context, GTK_STATE_FLAG_BACKDROP); @@ -105,8 +108,11 @@ SkBitmap PaintHeaderbar(const gfx::Size& size, int ComputeTopCornerRadius() { // In GTK4, there's no way to directly obtain CSS values for a context, so we // need to experimentally determine the corner radius by rendering a sample. - auto context = HeaderContext(false, false); - ApplyCssToContext(context, R"(headerbar { + // Additionally, in GTK4, the headerbar corners get clipped by the window + // rather than the headerbar having its own rounded corners. + auto context = GtkCheckVersion(4) ? DecorationContext(false, false) + : HeaderContext(false, false); + ApplyCssToContext(context, R"(window, headerbar { background-image: none; background-color: black; box-shadow: none; @@ -115,8 +121,10 @@ int ComputeTopCornerRadius() { border-bottom-right-radius: 0; border-top-right-radius: 0; })"); - auto bitmap = - PaintHeaderbar({kMaxCornerRadiusDip, kMaxCornerRadiusDip}, context, 1); + gfx::Size size_dip{kMaxCornerRadiusDip, kMaxCornerRadiusDip}; + auto bitmap = GtkCheckVersion(4) + ? PaintBitmap(size_dip, {{0, 0}, size_dip}, context, 1) + : PaintHeaderbar(size_dip, context, 1); DCHECK_EQ(bitmap.width(), bitmap.height()); for (int i = 0; i < bitmap.width(); ++i) { if (SkColorGetA(bitmap.getColor(0, i)) == 255 && @@ -258,6 +266,17 @@ void WindowFrameProviderGtk::PaintWindowFrame(gfx::Canvas* canvas, auto header = PaintHeaderbar({client_bounds_px.width(), top_area_height_px}, HeaderContext(solid_frame_, focused), scale); image = gfx::ImageSkia::CreateFrom1xBitmap(header); + // In GTK4, the headerbar gets clipped by the window. + if (GtkCheckVersion(4)) { + gfx::RectF bounds_px = + gfx::RectF(client_bounds_px.x(), client_bounds_px.y(), header.width(), + header.height()); + float radius_px = scale * top_corner_radius_dip_; + SkVector radii[4]{{radius_px, radius_px}, {radius_px, radius_px}, {}, {}}; + SkRRect clip; + clip.setRectRadii(gfx::RectFToSkRect(bounds_px), radii); + canvas->sk_canvas()->clipRRect(clip, SkClipOp::kIntersect, true); + } draw_image(0, 0, header.width(), header.height(), client_bounds_px.x(), client_bounds_px.y(), header.width(), header.height()); } diff --git a/ui/webui/resources/BUILD.gn b/ui/webui/resources/BUILD.gn index 830ee0f736fa72..9d9dcab5be1a3d 100644 --- a/ui/webui/resources/BUILD.gn +++ b/ui/webui/resources/BUILD.gn @@ -215,6 +215,7 @@ ts_library("library") { "cr_elements/mwb_shared_style.ts", "cr_elements/mwb_shared_vars.ts", "cr_elements/search_highlight_style_css.ts", + "js/custom_element.ts", "js/i18n_mixin.ts", "js/list_property_update_mixin.ts", "js/web_ui_listener_mixin.ts", @@ -260,7 +261,6 @@ ts_library("library") { "$root_dir/js/cr/ui/focus_outline_manager.m.d.ts", "$root_dir/js/cr/ui/focus_row.m.d.ts", "$root_dir/js/cr/ui/keyboard_shortcut_list.m.d.ts", - "$root_dir/js/custom_element.d.ts", "$root_dir/js/event_tracker.m.d.ts", "$root_dir/js/load_time_data.m.d.ts", "$root_dir/js/plural_string_proxy.d.ts", @@ -323,7 +323,6 @@ ts_definitions("generate_definitions") { "js/cr/ui/focus_row.m.js", "js/cr/ui/keyboard_shortcut_list.m.js", "js/cr/ui/store.js", - "js/custom_element.js", "js/event_tracker.m.js", "js/icon.js", "js/load_time_data.m.js", diff --git a/ui/webui/resources/cr_components/BUILD.gn b/ui/webui/resources/cr_components/BUILD.gn index c2fefdb34ae654..c66ad39f2fb67e 100644 --- a/ui/webui/resources/cr_components/BUILD.gn +++ b/ui/webui/resources/cr_components/BUILD.gn @@ -12,6 +12,7 @@ preprocess_folder = "$root_gen_dir/ui/webui/resources/preprocessed/cr_components" preprocess_gen_manifest = "preprocessed_gen_manifest.json" preprocess_mojom_manifest = "preprocessed_mojom_manifest.json" +preprocess_external_mojo_manifest = "preprocessed_external_mojo_manifest.json" if (is_chromeos_ash) { preprocess_polymer2_manifest = "preprocessed_polymer2_manifest.json" } @@ -20,7 +21,10 @@ preprocess_src_manifest = "preprocessed_src_manifest.json" generate_grd("build_grdp") { grd_prefix = "cr_components" out_grd = "$target_gen_dir/${grd_prefix}_resources.grdp" - deps = [ ":preprocess" ] + deps = [ + ":preprocess", + ":preprocess_external_mojo", + ] if (is_chromeos_ash) { input_files_base_dir = rebase_path(".", "//") input_files = [ @@ -64,6 +68,7 @@ generate_grd("build_grdp") { "$target_gen_dir/$preprocess_gen_manifest", "$target_gen_dir/$preprocess_mojom_manifest", "$target_gen_dir/$preprocess_src_manifest", + "$target_gen_dir/$preprocess_external_mojo_manifest", ] # TODO(crbug.com/1184053): Fully remove once no longer used by CrOS. @@ -72,6 +77,13 @@ generate_grd("build_grdp") { } resource_path_prefix = "cr_components" + + resource_path_rewrites = [ + "mojo/public/mojom/base/file_path.mojom-lite.js|app_management/file_path.mojom-lite.js", + "mojo/public/mojom/base/safe_base_name.mojom-lite.js|app_management/safe_base_name.mojom-lite.js", + "ui/gfx/image/mojom/image.mojom-lite.js|app_management/image.mojom-lite.js", + "components/services/app_service/public/mojom/types.mojom-lite.js|app_management/types.mojom-lite.js", + ] } group("preprocess") { @@ -103,6 +115,12 @@ preprocess_if_expr("preprocess_src") { "most_visited/window_proxy.js", "color_change_listener/browser_proxy.js", "color_change_listener/colors_css_updater.js", + "app_management/permission_constants.js", + "app_management/permission_util.js", + "app_management/browser_proxy.js", + "app_management/constants.js", + "app_management/types.js", + "app_management/util.js", ] } @@ -121,7 +139,10 @@ preprocess_if_expr("preprocess_mojom") { } preprocess_if_expr("preprocess_generated") { - deps = [ ":polymer3_elements" ] + deps = [ + ":polymer3_elements", + "//ui/webui/resources/cr_components/app_management:mojo_bindings_js__generator", + ] in_folder = target_gen_dir out_folder = preprocess_folder out_manifest = "$target_gen_dir/$preprocess_gen_manifest" @@ -131,6 +152,11 @@ preprocess_if_expr("preprocess_generated") { "managed_dialog/managed_dialog.js", "managed_footnote/managed_footnote.js", "omnibox/cr_autocomplete_match_list.js", + "app_management/permission_item.js", + "app_management/shared_style.js", + "app_management/shared_vars.js", + "app_management/toggle_row.js", + "app_management/app_management.mojom-lite.js", ] if (is_chromeos_ash) { @@ -402,6 +428,7 @@ if (is_chromeos_ash) { group("closure_compile") { deps = [ + "app_management:closure_compile", "color_change_listener:closure_compile", "managed_dialog:closure_compile", "managed_footnote:closure_compile", @@ -416,6 +443,7 @@ group("closure_compile") { group("polymer3_elements") { public_deps = [ + "app_management:web_components", "customize_themes:web_components", "iph_bubble:web_components", "managed_dialog:web_components", @@ -428,3 +456,25 @@ group("polymer3_elements") { public_deps += [ "chromeos:polymer3_elements" ] } } + +preprocess_if_expr("preprocess_external_mojo") { + deps = [ + "//components/services/app_service/public/mojom:mojom_js", + "//mojo/public/js:bindings_lite", + "//mojo/public/mojom/base", + "//ui/gfx/image/mojom:mojom_js", + ] + in_folder = "$root_gen_dir" + + # It does not matter which preprocess folder these files are pasted into, as + # they are not used for bundling; the purpose of this build rule is to + # include them in the generated grd file. + out_folder = "$preprocess_folder" + out_manifest = "$target_gen_dir/$preprocess_external_mojo_manifest" + in_files = [ + "mojo/public/mojom/base/file_path.mojom-lite.js", + "mojo/public/mojom/base/safe_base_name.mojom-lite.js", + "ui/gfx/image/mojom/image.mojom-lite.js", + "components/services/app_service/public/mojom/types.mojom-lite.js", + ] +} diff --git a/ui/webui/resources/cr_components/app_management/BUILD.gn b/ui/webui/resources/cr_components/app_management/BUILD.gn new file mode 100644 index 00000000000000..e6bd6b82febbb1 --- /dev/null +++ b/ui/webui/resources/cr_components/app_management/BUILD.gn @@ -0,0 +1,97 @@ +# Copyright 2021 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import("//mojo/public/tools/bindings/mojom.gni") +import("//third_party/closure_compiler/compile_js.gni") +import("//tools/polymer/html_to_js.gni") + +js_type_check("closure_compile") { + is_polymer3 = true + deps = [ + ":browser_proxy", + ":constants", + ":permission_constants", + ":permission_item", + ":permission_util", + ":shared_style", + ":shared_vars", + ":toggle_row", + ":types", + ":util", + ] +} + +js_library("permission_item") { + deps = [ + ":browser_proxy", + ":permission_constants", + ":toggle_row", + "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled", + ] +} + +js_library("shared_style") { + deps = [] +} + +js_library("shared_vars") { + deps = [] +} + +js_library("permission_constants") { + deps = [ ":mojo_bindings_js_library_for_compile" ] +} + +js_library("permission_util") { + deps = [ + ":permission_constants", + "//ui/webui/resources/js:assert.m", + ] +} + +js_library("browser_proxy") { + deps = [ + ":mojo_bindings_js_library_for_compile", + "//ui/webui/resources/js:cr.m", + ] +} + +js_library("toggle_row") { + deps = [ + ":types", + "//ui/webui/resources/cr_elements/cr_toggle:cr_toggle.m", + "//ui/webui/resources/cr_elements/policy:cr_policy_indicator.m", + ] +} + +js_library("constants") { + deps = [ ":mojo_bindings_js_library_for_compile" ] +} + +js_library("types") { + deps = [] +} + +js_library("util") { + deps = [ ":permission_constants" ] + externs_list = [ "//third_party/closure_compiler/externs/metrics_private.js" ] +} + +html_to_js("web_components") { + js_files = [ + "permission_item.js", + "shared_style.js", + "shared_vars.js", + "toggle_row.js", + ] +} + +mojom("mojo_bindings") { + sources = [ "app_management.mojom" ] + + public_deps = [ + "//components/services/app_service/public/mojom", + "//mojo/public/mojom/base", + ] +} diff --git a/ui/webui/resources/cr_components/app_management/OWNERS b/ui/webui/resources/cr_components/app_management/OWNERS new file mode 100644 index 00000000000000..d3f5d596fd69a0 --- /dev/null +++ b/ui/webui/resources/cr_components/app_management/OWNERS @@ -0,0 +1,4 @@ +file://chrome/browser/resources/settings/chromeos/os_apps_page/OWNERS + +per-file *.mojom=set noparent +per-file *.mojom=file://ipc/SECURITY_OWNERS diff --git a/chrome/browser/ui/webui/app_management/app_management.mojom b/ui/webui/resources/cr_components/app_management/app_management.mojom similarity index 100% rename from chrome/browser/ui/webui/app_management/app_management.mojom rename to ui/webui/resources/cr_components/app_management/app_management.mojom diff --git a/ui/webui/resources/cr_components/app_management/browser_proxy.js b/ui/webui/resources/cr_components/app_management/browser_proxy.js new file mode 100644 index 00000000000000..983f19aa1df578 --- /dev/null +++ b/ui/webui/resources/cr_components/app_management/browser_proxy.js @@ -0,0 +1,30 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'chrome://resources/mojo/mojo/public/js/mojo_bindings_lite.js'; +import 'chrome://resources/mojo/skia/public/mojom/image_info.mojom-lite.js'; +import 'chrome://resources/mojo/skia/public/mojom/bitmap.mojom-lite.js'; +import 'chrome://resources/mojo/url/mojom/url.mojom-lite.js'; +import './file_path.mojom-lite.js'; +import './image.mojom-lite.js'; +import './types.mojom-lite.js'; +import './app_management.mojom-lite.js'; + +import {addSingletonGetter} from 'chrome://resources/js/cr.m.js'; + +export class BrowserProxy { + constructor() { + /** @type {appManagement.mojom.PageCallbackRouter} */ + this.callbackRouter = new appManagement.mojom.PageCallbackRouter(); + + /** @type {appManagement.mojom.PageHandlerRemote} */ + this.handler = new appManagement.mojom.PageHandlerRemote(); + const factory = appManagement.mojom.PageHandlerFactory.getRemote(); + factory.createPageHandler( + this.callbackRouter.$.bindNewPipeAndPassRemote(), + this.handler.$.bindNewPipeAndPassReceiver()); + } +} + +addSingletonGetter(BrowserProxy); diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/constants.js b/ui/webui/resources/cr_components/app_management/constants.js similarity index 91% rename from chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/constants.js rename to ui/webui/resources/cr_components/app_management/constants.js index 6a95b0f8b0d266..6130d23eff2598 100644 --- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/constants.js +++ b/ui/webui/resources/cr_components/app_management/constants.js @@ -7,11 +7,11 @@ import 'chrome://resources/mojo/mojo/public/js/mojo_bindings_lite.js'; import 'chrome://resources/mojo/skia/public/mojom/image_info.mojom-lite.js'; import 'chrome://resources/mojo/skia/public/mojom/bitmap.mojom-lite.js'; import 'chrome://resources/mojo/url/mojom/url.mojom-lite.js'; -import '/app-management/file_path.mojom-lite.js'; -import '/app-management/image.mojom-lite.js'; -import '/app-management/safe_base_name.mojom-lite.js'; -import '/app-management/types.mojom-lite.js'; -import '/app-management/app_management.mojom-lite.js'; +import './file_path.mojom-lite.js'; +import './image.mojom-lite.js'; +import './safe_base_name.mojom-lite.js'; +import './types.mojom-lite.js'; +import './app_management.mojom-lite.js'; /** * The number of apps displayed in app list in the main view before expanding. diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/permission_constants.js b/ui/webui/resources/cr_components/app_management/permission_constants.js similarity index 67% rename from chrome/browser/resources/settings/chromeos/os_apps_page/permission_constants.js rename to ui/webui/resources/cr_components/app_management/permission_constants.js index 37e2e580ac794b..26d8bd849b871b 100644 --- a/chrome/browser/resources/settings/chromeos/os_apps_page/permission_constants.js +++ b/ui/webui/resources/cr_components/app_management/permission_constants.js @@ -4,10 +4,10 @@ import 'chrome://resources/mojo/mojo/public/js/mojo_bindings_lite.js'; -import '/app-management/file_path.mojom-lite.js'; -import '/app-management/image.mojom-lite.js'; -import '/app-management/safe_base_name.mojom-lite.js'; -import '/app-management/types.mojom-lite.js'; +import './file_path.mojom-lite.js'; +import './image.mojom-lite.js'; +import './safe_base_name.mojom-lite.js'; +import './types.mojom-lite.js'; export const TriState = apps.mojom.TriState; diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/permission_item.html b/ui/webui/resources/cr_components/app_management/permission_item.html similarity index 100% rename from chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/permission_item.html rename to ui/webui/resources/cr_components/app_management/permission_item.html diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/permission_item.js b/ui/webui/resources/cr_components/app_management/permission_item.js similarity index 93% rename from chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/permission_item.js rename to ui/webui/resources/cr_components/app_management/permission_item.js index ac3489d5cdfd9f..4bfade166313c0 100644 --- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/permission_item.js +++ b/ui/webui/resources/cr_components/app_management/permission_item.js @@ -7,22 +7,16 @@ import './toggle_row.js'; import {assert, assertNotReached} from '//resources/js/assert.m.js'; import {afterNextRender, flush, html, Polymer, TemplateInstanceBase, Templatizer} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js'; -import {recordClick, recordNavigation, recordPageBlur, recordPageFocus, recordSearch, recordSettingChange, setUserActionRecorderForTesting} from '../../metrics_recorder.m.js'; -import {PermissionType, PermissionValue, TriState} from '../permission_constants.js'; -import {createBoolPermission, createTriStatePermission, getBoolPermissionValue, getTriStatePermissionValue, isBoolValue, isTriStateValue} from '../permission_util.js'; - import {BrowserProxy} from './browser_proxy.js'; import {AppManagementUserAction} from './constants.js'; -import {AppManagementStoreClient} from './store_client.js'; +import {PermissionType, PermissionValue, TriState} from './permission_constants.js'; +import {createBoolPermission, createTriStatePermission, getBoolPermissionValue, getTriStatePermissionValue, isBoolValue, isTriStateValue} from './permission_util.js'; import {getPermission, getPermissionValueBool, getSelectedApp, recordAppManagementUserAction} from './util.js'; Polymer({ _template: html`{__html_template__}`, is: 'app-management-permission-item', - behaviors: [ - AppManagementStoreClient, - ], properties: { /** @@ -82,11 +76,6 @@ Polymer({ listeners: {click: 'onClick_', change: 'togglePermission_'}, - attached() { - this.watch('app_', state => getSelectedApp(state)); - this.updateFromStore(); - }, - /** * Returns true if the permission type is available for the app. * @@ -186,7 +175,6 @@ Polymer({ BrowserProxy.getInstance().handler.setPermission( this.app_.id, newPermission); - recordSettingChange(); recordAppManagementUserAction( this.app_.type, this.getUserMetricActionForPermission_( diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/permission_util.js b/ui/webui/resources/cr_components/app_management/permission_util.js similarity index 100% rename from chrome/browser/resources/settings/chromeos/os_apps_page/permission_util.js rename to ui/webui/resources/cr_components/app_management/permission_util.js diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/shared_style.html b/ui/webui/resources/cr_components/app_management/shared_style.html similarity index 100% rename from chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/shared_style.html rename to ui/webui/resources/cr_components/app_management/shared_style.html diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/shared_style.js b/ui/webui/resources/cr_components/app_management/shared_style.js similarity index 100% rename from chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/shared_style.js rename to ui/webui/resources/cr_components/app_management/shared_style.js diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/shared_vars.html b/ui/webui/resources/cr_components/app_management/shared_vars.html similarity index 100% rename from chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/shared_vars.html rename to ui/webui/resources/cr_components/app_management/shared_vars.html diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/shared_vars.js b/ui/webui/resources/cr_components/app_management/shared_vars.js similarity index 100% rename from chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/shared_vars.js rename to ui/webui/resources/cr_components/app_management/shared_vars.js diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/toggle_row.html b/ui/webui/resources/cr_components/app_management/toggle_row.html similarity index 100% rename from chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/toggle_row.html rename to ui/webui/resources/cr_components/app_management/toggle_row.html diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/toggle_row.js b/ui/webui/resources/cr_components/app_management/toggle_row.js similarity index 92% rename from chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/toggle_row.js rename to ui/webui/resources/cr_components/app_management/toggle_row.js index 54f4457622b608..6bc6fd00796a4f 100644 --- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/toggle_row.js +++ b/ui/webui/resources/cr_components/app_management/toggle_row.js @@ -7,9 +7,6 @@ import '//resources/cr_elements/policy/cr_policy_indicator.m.js'; import {afterNextRender, flush, html, Polymer, TemplateInstanceBase, Templatizer} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js'; -import {BrowserProxy} from './browser_proxy.js'; -import {AppManagementStoreClient} from './store_client.js'; - Polymer({ _template: html`{__html_template__}`, is: 'app-management-toggle-row', diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/types.js b/ui/webui/resources/cr_components/app_management/types.js similarity index 100% rename from chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/types.js rename to ui/webui/resources/cr_components/app_management/types.js diff --git a/ui/webui/resources/cr_components/app_management/util.js b/ui/webui/resources/cr_components/app_management/util.js new file mode 100644 index 00000000000000..2f23b5fb0208ea --- /dev/null +++ b/ui/webui/resources/cr_components/app_management/util.js @@ -0,0 +1,190 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import {assert} from 'chrome://resources/js/assert.m.js'; +import {assertNotReached} from 'chrome://resources/js/assert.m.js'; + +import {AppManagementUserAction, AppType, OptionalBool} from './constants.js'; +import {PermissionType} from './permission_constants.js'; +import {isPermissionEnabled} from './permission_util.js'; + + +/** + * @fileoverview Utility functions for the App Management page. + */ + +/** + * @return {!AppManagementPageState} + */ +export function createEmptyState() { + return { + apps: {}, + selectedAppId: null, + }; +} + +/** + * @param {!Array} apps + * @return {!AppManagementPageState} + */ +export function createInitialState(apps) { + const initialState = createEmptyState(); + + for (const app of apps) { + initialState.apps[app.id] = app; + } + + return initialState; +} + +/** + * @param {App} app + * @return {string} + */ +export function getAppIcon(app) { + return `chrome://app-icon/${app.id}/64`; +} + +/** + * If the given value is not in the set, returns a new set with the value + * added, otherwise returns the old set. + * @template T + * @param {!Set} set + * @param {T} value + * @return {!Set} + */ +export function addIfNeeded(set, value) { + if (!set.has(value)) { + set = new Set(set); + set.add(value); + } + return set; +} + +/** + * If the given value is in the set, returns a new set without the value, + * otherwise returns the old set. + * @template T + * @param {!Set} set + * @param {T} value + * @return {!Set} + */ +export function removeIfNeeded(set, value) { + if (set.has(value)) { + set = new Set(set); + set.delete(value); + } + return set; +} + +/** + * @param {App} app + * @param {string} permissionType + * @return {boolean} + */ +export function getPermissionValueBool(app, permissionType) { + const permission = getPermission(app, permissionType); + assert(permission); + + return isPermissionEnabled(permission.value); +} + +/** + * Undefined is returned when the app does not request a permission. + * + * @param {App} app + * @param {string} permissionType + * @return {Permission|undefined} + */ +export function getPermission(app, permissionType) { + return app.permissions[PermissionType[permissionType]]; +} + +/** + * @param {AppManagementPageState} state + * @return {?App} + */ +export function getSelectedApp(state) { + const selectedAppId = state.selectedAppId; + return selectedAppId ? state.apps[selectedAppId] : null; +} + +/** + * A comparator function to sort strings alphabetically. + * + * @param {string} a + * @param {string} b + */ +export function alphabeticalSort(a, b) { + return a.localeCompare(b); +} + +/** + * Toggles an OptionalBool + * + * @param {OptionalBool} bool + * @return {OptionalBool} + */ +export function toggleOptionalBool(bool) { + switch (bool) { + case OptionalBool.kFalse: + return OptionalBool.kTrue; + case OptionalBool.kTrue: + return OptionalBool.kFalse; + default: + assertNotReached(); + } +} + +/** + * @param {OptionalBool} optionalBool + * @returns {boolean} + */ +export function convertOptionalBoolToBool(optionalBool) { + switch (optionalBool) { + case OptionalBool.kTrue: + return true; + case OptionalBool.kFalse: + return false; + default: + assertNotReached(); + } +} + +/** + * @param {AppType} appType + * @return {string} + * @private + */ +export function getUserActionHistogramNameForAppType_(appType) { + switch (appType) { + case AppType.kArc: + return 'AppManagement.AppDetailViews.ArcApp'; + case AppType.kChromeApp: + case AppType.kStandaloneBrowser: + case AppType.kStandaloneBrowserChromeApp: + // TODO(https://crbug.com/1225848): Figure out appropriate behavior for + // Lacros-hosted chrome-apps. + return 'AppManagement.AppDetailViews.ChromeApp'; + case AppType.kWeb: + return 'AppManagement.AppDetailViews.WebApp'; + case AppType.kPluginVm: + return 'AppManagement.AppDetailViews.PluginVmApp'; + case AppType.kBorealis: + return 'AppManagement.AppDetailViews.BorealisApp'; + default: + assertNotReached(); + } +} + +/** + * @param {AppType} appType + * @param {AppManagementUserAction} userAction + */ +export function recordAppManagementUserAction(appType, userAction) { + const histogram = getUserActionHistogramNameForAppType_(appType); + const enumLength = Object.keys(AppManagementUserAction).length; + chrome.metricsPrivate.recordEnumerationValue( + histogram, userAction, enumLength); +} diff --git a/ui/webui/resources/js/BUILD.gn b/ui/webui/resources/js/BUILD.gn index 8bb2f10b60c553..29fe46665db5ba 100644 --- a/ui/webui/resources/js/BUILD.gn +++ b/ui/webui/resources/js/BUILD.gn @@ -47,6 +47,7 @@ preprocess_if_expr("preprocess_src_ts") { in_folder = "./" out_folder = "$preprocess_folder" in_files = [ + "custom_element.ts", "i18n_mixin.ts", "list_property_update_mixin.ts", "web_ui_listener_mixin.ts", @@ -62,7 +63,6 @@ preprocess_if_expr("preprocess_src") { "assert.js", "color_utils.js", "cr.m.js", - "custom_element.js", "i18n_template_no_process.js", "icon.js", "load_time_data.m.js", @@ -292,7 +292,6 @@ js_type_check("js_resources_modules") { deps = [ ":assert.m", ":cr.m", - ":custom_element", ":event_tracker.m", ":i18n_behavior.m", ":icon", @@ -328,9 +327,6 @@ js_library("cr.m") { externs_list = [ "$externs_path/chrome_send.js" ] } -js_library("custom_element") { -} - js_library("event_tracker.m") { sources = [ "$root_gen_dir/ui/webui/resources/js/event_tracker.m.js" ] extra_deps = [ ":modulize_local" ] diff --git a/ui/webui/resources/js/custom_element.js b/ui/webui/resources/js/custom_element.ts similarity index 53% rename from ui/webui/resources/js/custom_element.js rename to ui/webui/resources/js/custom_element.ts index 9d745688bb614d..b2f0f1a2d24081 100644 --- a/ui/webui/resources/js/custom_element.js +++ b/ui/webui/resources/js/custom_element.ts @@ -7,29 +7,27 @@ * See the following file for usage: * chrome/test/data/webui/js/custom_element_test.js */ + export class CustomElement extends HTMLElement { + static get template(): string { + return ''; + } + constructor() { super(); this.attachShadow({mode: 'open'}); const template = document.createElement('template'); - template.innerHTML = this.constructor.template || ''; - this.shadowRoot.appendChild(template.content.cloneNode(true)); + template.innerHTML = + (this.constructor as typeof CustomElement).template || ''; + this.shadowRoot!.appendChild(template.content.cloneNode(true)); } - /** - * @param {string} query - * @return {?Element} - */ - $(query) { - return this.shadowRoot.querySelector(query); + $(query: string): E|null { + return this.shadowRoot!.querySelector(query); } - /** - * @param {string} query - * @return {!NodeList} - */ - $all(query) { - return this.shadowRoot.querySelectorAll(query); + $all(query: string): NodeListOf { + return this.shadowRoot!.querySelectorAll(query); } }