diff --git a/DEPS b/DEPS index e2a52ba..1d99a817 100644 --- a/DEPS +++ b/DEPS
@@ -239,7 +239,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling Skia # and whatever else without interference from each other. - 'skia_revision': '493d7910a7f5553f9a2fa376ec3c6acfe942c2ff', + 'skia_revision': 'f333f5614a9b55fa478222ec1994cea699460892', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling V8 # and whatever else without interference from each other. @@ -247,7 +247,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling ANGLE # and whatever else without interference from each other. - 'angle_revision': '10e5f34d1439f0bcd5b30bea5bfbf6bdaafd4935', + 'angle_revision': '60e457b8badc184adc8d36c7bdbac851e8d3073c', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling SwiftShader # and whatever else without interference from each other. @@ -306,7 +306,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling catapult # and whatever else without interference from each other. - 'catapult_revision': '32ccf21284278a0aec08f116d838e89a8da7e34d', + 'catapult_revision': '02439f647cbdebb896ed7bb50b841859f7d218a6', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling libFuzzer # and whatever else without interference from each other. @@ -314,7 +314,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling devtools-frontend # and whatever else without interference from each other. - 'devtools_frontend_revision': 'fa45ccaa0ef25b38b83800b6e9e6ad6227741326', + 'devtools_frontend_revision': '7565b27cbf4b013e56f8a51947d73cb1cbdc5905', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling libprotobuf-mutator # and whatever else without interference from each other. @@ -354,7 +354,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling feed # and whatever else without interference from each other. - 'dawn_revision': '3a464767a5301d85380a0e9256cde1deaaa18d42', + 'dawn_revision': '13d6af93fbf5ed3436b7ad701769992d842848c8', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling feed # and whatever else without interference from each other. @@ -796,7 +796,7 @@ 'packages': [ { 'package': 'chromium/third_party/androidx', - 'version': 'VcOuel-N-Hgl9PTt4swv-1od43yNDaMT1_HTpbIPAQAC', + 'version': '1kq1iXXaUuk31wMfTFdArbZ_DsnzladyCDH9sHQJNp0C', }, ], 'condition': 'checkout_android', @@ -1035,7 +1035,7 @@ }, 'src/third_party/depot_tools': - Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '203a6447ad7a3d534829525e1daa3dde23455451', + Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '440d06ad1a2160bdb10a83c30f5c5264814ccbb4', 'src/third_party/devtools-frontend/src': Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'), @@ -1418,7 +1418,7 @@ }, 'src/third_party/perfetto': - Var('android_git') + '/platform/external/perfetto.git' + '@' + 'e9e81c01529610f02f63a87122c5bd878972ef7b', + Var('android_git') + '/platform/external/perfetto.git' + '@' + 'b54e61f0f24427ef4b48fe9510b423624cf8a60e', 'src/third_party/perl': { 'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3', @@ -1738,7 +1738,7 @@ 'packages': [ { 'package': 'chromeos_internal/apps/projector_app/app', - 'version': 'ufCjpiqK8yNrlWKLV6DKEJRFgC52zxfpbyXRtYpweZgC', + 'version': 'i7EK97odXDVeEHF291--ccszH_Vpi_gJmwR1xWlKrL8C', }, ], 'condition': 'checkout_chromeos and checkout_src_internal',
diff --git a/ash/BUILD.gn b/ash/BUILD.gn index 0bef007..431d21e4 100644 --- a/ash/BUILD.gn +++ b/ash/BUILD.gn
@@ -407,8 +407,6 @@ "display/unified_mouse_warp_controller.h", "display/window_tree_host_manager.cc", "display/window_tree_host_manager.h", - "drag_drop/drag_drop_capture_delegate.cc", - "drag_drop/drag_drop_capture_delegate.h", "drag_drop/drag_drop_controller.cc", "drag_drop/drag_drop_controller.h", "drag_drop/drag_drop_tracker.cc", @@ -767,10 +765,10 @@ "scoped_animation_disabler.h", "screen_util.cc", "screen_util.h", - "session/fullscreen_alert_bubble.cc", - "session/fullscreen_alert_bubble.h", "session/fullscreen_controller.cc", "session/fullscreen_controller.h", + "session/fullscreen_notification_bubble.cc", + "session/fullscreen_notification_bubble.h", "session/multiprofiles_intro_dialog.cc", "session/multiprofiles_intro_dialog.h", "session/session_aborted_dialog.cc", @@ -2075,6 +2073,7 @@ "//components/device_event_log", "//components/discardable_memory/service", "//components/favicon_base:favicon_base", + "//components/fullscreen_control", "//components/global_media_controls", "//components/language/core/browser:browser", "//components/live_caption", @@ -2316,7 +2315,6 @@ "display/touch_calibrator_controller_unittest.cc", "display/unified_mouse_warp_controller_unittest.cc", "display/window_tree_host_manager_unittest.cc", - "drag_drop/drag_drop_capture_delegate_unittest.cc", "drag_drop/drag_drop_controller_unittest.cc", "drag_drop/drag_drop_tracker_unittest.cc", "drag_drop/drag_drop_unittest.cc", @@ -2402,6 +2400,7 @@ "rotator/screen_rotation_animation_unittest.cc", "rotator/screen_rotation_animator_unittest.cc", "screen_util_unittest.cc", + "session/fullscreen_notification_bubble_unittest.cc", "session/session_controller_impl_unittest.cc", "shelf/assistant_overlay_unittest.cc", "shelf/back_button_unittest.cc",
diff --git a/ash/DEPS b/ash/DEPS index b4c418d..7e24f9b 100644 --- a/ash/DEPS +++ b/ash/DEPS
@@ -9,6 +9,7 @@ "+components/desks_storage", "+components/discardable_memory/public", "+components/discardable_memory/service/discardable_shared_memory_manager.h", + "+components/fullscreen_control", "+components/language/core/browser/pref_names.h", "+components/live_caption", "+components/media_message_center",
diff --git a/ash/ash_strings.grd b/ash/ash_strings.grd index 59c3b8adf9..a37e9b1 100644 --- a/ash/ash_strings.grd +++ b/ash/ash_strings.grd
@@ -2769,17 +2769,6 @@ Full-screen Magnifier enabled. Press Ctrl+Search+M again to toggle it off. </message> - <!-- Alert bubble that when the device returns from sleep or display off without a lock screen and an active window is in fullscreen mode --> - <message name="IDS_FULLSCREEN_ALERT" desc="Text on an alert bubble to tell users that the active window is in fullscreen mode and recommend to exit fullscreen before entering password."> - Your current app is full screen. If the app asks for your password exit full screen first. - </message> - <message name="IDS_EXIT_FULLSCREEN_BUTTON" desc="Text on the exit fullscreen button."> - Exit fullscreen - </message> - <message name="IDS_DISMISS_BUTTON" desc="Text on the dismiss button."> - Dismiss - </message> - <!-- Tray scale strings --> <message name="IDS_ASH_STATUS_TRAY_SCALE" desc="The label used in scale setting detailed page of ash tray popup."> Display scale settings
diff --git a/ash/ash_strings_grd/IDS_DISMISS_BUTTON.png.sha1 b/ash/ash_strings_grd/IDS_DISMISS_BUTTON.png.sha1 deleted file mode 100644 index cde80b3..0000000 --- a/ash/ash_strings_grd/IDS_DISMISS_BUTTON.png.sha1 +++ /dev/null
@@ -1 +0,0 @@ -dd2b362e5c00cff4e1abf64f65305d4ce77ae9e1 \ No newline at end of file
diff --git a/ash/ash_strings_grd/IDS_EXIT_FULLSCREEN_BUTTON.png.sha1 b/ash/ash_strings_grd/IDS_EXIT_FULLSCREEN_BUTTON.png.sha1 deleted file mode 100644 index cde80b3..0000000 --- a/ash/ash_strings_grd/IDS_EXIT_FULLSCREEN_BUTTON.png.sha1 +++ /dev/null
@@ -1 +0,0 @@ -dd2b362e5c00cff4e1abf64f65305d4ce77ae9e1 \ No newline at end of file
diff --git a/ash/ash_strings_grd/IDS_FULLSCREEN_ALERT.png.sha1 b/ash/ash_strings_grd/IDS_FULLSCREEN_ALERT.png.sha1 deleted file mode 100644 index cde80b3..0000000 --- a/ash/ash_strings_grd/IDS_FULLSCREEN_ALERT.png.sha1 +++ /dev/null
@@ -1 +0,0 @@ -dd2b362e5c00cff4e1abf64f65305d4ce77ae9e1 \ No newline at end of file
diff --git a/ash/components/phonehub/BUILD.gn b/ash/components/phonehub/BUILD.gn index 5f7ce04..0d35db3 100644 --- a/ash/components/phonehub/BUILD.gn +++ b/ash/components/phonehub/BUILD.gn
@@ -95,6 +95,8 @@ "recent_app_click_observer.h", "recent_apps_interaction_handler.cc", "recent_apps_interaction_handler.h", + "recent_apps_interaction_handler_impl.cc", + "recent_apps_interaction_handler_impl.h", "screen_lock_manager.cc", "screen_lock_manager.h", "screen_lock_manager_impl.cc", @@ -235,7 +237,7 @@ "onboarding_ui_tracker_impl_unittest.cc", "phone_status_model_unittest.cc", "phone_status_processor_unittest.cc", - "recent_apps_interaction_handler_unittest.cc", + "recent_apps_interaction_handler_impl_unittest.cc", "screen_lock_manager_impl_unittest.cc", "tether_controller_impl_unittest.cc", "user_action_recorder_impl_unittest.cc",
diff --git a/ash/components/phonehub/notification.cc b/ash/components/phonehub/notification.cc index ce4cc0f9..ad4aa4e 100644 --- a/ash/components/phonehub/notification.cc +++ b/ash/components/phonehub/notification.cc
@@ -6,12 +6,18 @@ #include <tuple> +#include "base/base64.h" #include "base/containers/flat_map.h" #include "base/logging.h" namespace ash { namespace phonehub { +const char kVisibleAppName[] = "visible_app_name"; +const char kPackageName[] = "package_name"; +const char kUserId[] = "user_id"; +const char kIcon[] = "icon"; + Notification::AppMetadata::AppMetadata(const std::u16string& visible_app_name, const std::string& package_name, const gfx::Image& icon, @@ -36,6 +42,44 @@ return !(*this == other); } +base::Value Notification::AppMetadata::ToValue() const { + scoped_refptr<base::RefCountedMemory> png_data = icon.As1xPNGBytes(); + + base::Value val(base::Value::Type::DICTIONARY); + val.SetKey(kVisibleAppName, base::Value(visible_app_name)); + val.SetKey(kPackageName, base::Value(package_name)); + val.SetDoubleKey(kUserId, user_id); + val.SetKey(kIcon, base::Value(base::Base64Encode(*png_data))); + return val; +} + +// static +Notification::AppMetadata Notification::AppMetadata::FromValue( + const base::Value& value) { + DCHECK(value.is_dict()); + DCHECK(value.FindKey(kVisibleAppName)); + DCHECK(value.FindKey(kVisibleAppName)->is_string()); + DCHECK(value.FindKey(kPackageName)); + DCHECK(value.FindKey(kPackageName)->is_string()); + DCHECK(value.FindKey(kUserId)); + DCHECK(value.FindKey(kUserId)->is_double()); + DCHECK(value.FindKey(kIcon)); + DCHECK(value.FindKey(kIcon)->is_string()); + + const base::Value* visible_app_name_value = value.FindPath(kVisibleAppName); + std::u16string visible_app_name_string_value; + visible_app_name_value->GetAsString(&visible_app_name_string_value); + + std::string icon_str; + base::Base64Decode(*(value.FindStringPath(kIcon)), &icon_str); + gfx::Image decode_icon = gfx::Image::CreateFrom1xPNGBytes( + base::RefCountedString::TakeString(&icon_str)); + + return Notification::AppMetadata( + visible_app_name_string_value, *(value.FindStringPath(kPackageName)), + decode_icon, *(value.FindDoublePath(kUserId))); +} + Notification::Notification( int64_t id, const AppMetadata& app_metadata,
diff --git a/ash/components/phonehub/notification.h b/ash/components/phonehub/notification.h index 0ae86724..b9452265 100644 --- a/ash/components/phonehub/notification.h +++ b/ash/components/phonehub/notification.h
@@ -12,6 +12,7 @@ #include "base/containers/flat_map.h" #include "base/time/time.h" +#include "base/values.h" #include "third_party/abseil-cpp/absl/types/optional.h" #include "ui/gfx/image/image.h" @@ -35,6 +36,9 @@ bool operator==(const AppMetadata& other) const; bool operator!=(const AppMetadata& other) const; + static AppMetadata FromValue(const base::Value& value); + base::Value ToValue() const; + std::u16string visible_app_name; std::string package_name; gfx::Image icon;
diff --git a/ash/components/phonehub/phone_hub_manager_impl.cc b/ash/components/phonehub/phone_hub_manager_impl.cc index c22c02e..b66235bb 100644 --- a/ash/components/phonehub/phone_hub_manager_impl.cc +++ b/ash/components/phonehub/phone_hub_manager_impl.cc
@@ -26,7 +26,7 @@ #include "ash/components/phonehub/onboarding_ui_tracker_impl.h" #include "ash/components/phonehub/phone_model.h" #include "ash/components/phonehub/phone_status_processor.h" -#include "ash/components/phonehub/recent_apps_interaction_handler.h" +#include "ash/components/phonehub/recent_apps_interaction_handler_impl.h" #include "ash/components/phonehub/screen_lock_manager_impl.h" #include "ash/components/phonehub/tether_controller_impl.h" #include "ash/components/phonehub/user_action_recorder_impl.h" @@ -125,7 +125,7 @@ phone_model_.get())), recent_apps_interaction_handler_( features::IsPhoneHubRecentAppsEnabled() - ? std::make_unique<RecentAppsInteractionHandler>() + ? std::make_unique<RecentAppsInteractionHandlerImpl>(pref_service) : nullptr), tether_controller_( std::make_unique<TetherControllerImpl>(phone_model_.get(),
diff --git a/ash/components/phonehub/pref_names.cc b/ash/components/phonehub/pref_names.cc index c8117a8..b71ddff3 100644 --- a/ash/components/phonehub/pref_names.cc +++ b/ash/components/phonehub/pref_names.cc
@@ -53,6 +53,10 @@ // the numerical value associated with the ScreenLockManager::LockStatus enum. const char kScreenLockStatus[] = "cros.phonehub.screen_lock_status"; +// The last provided recent app information before the Eche disconnects. The +// pref stores the vector value associated with Notification::AppMetadata. +const char kRecentAppsHistory[] = "cros.phonehub.recent_apps_history"; + } // namespace prefs } // namespace phonehub } // namespace ash
diff --git a/ash/components/phonehub/pref_names.h b/ash/components/phonehub/pref_names.h index df4ce3b9..977d263 100644 --- a/ash/components/phonehub/pref_names.h +++ b/ash/components/phonehub/pref_names.h
@@ -16,6 +16,7 @@ extern const char kHasDismissedSetupRequiredUi[]; extern const char kNeedsOneTimeNotificationAccessUpdate[]; extern const char kScreenLockStatus[]; +extern const char kRecentAppsHistory[]; } // namespace prefs } // namespace phonehub
diff --git a/ash/components/phonehub/recent_apps_interaction_handler.cc b/ash/components/phonehub/recent_apps_interaction_handler.cc index 6dbaa49..3fb4f4d2 100644 --- a/ash/components/phonehub/recent_apps_interaction_handler.cc +++ b/ash/components/phonehub/recent_apps_interaction_handler.cc
@@ -8,8 +8,6 @@ namespace ash { namespace phonehub { -const size_t kMaxMostRecentApps = 5; - RecentAppsInteractionHandler::RecentAppsInteractionHandler() = default; RecentAppsInteractionHandler::~RecentAppsInteractionHandler() = default; @@ -24,50 +22,5 @@ observer_list_.RemoveObserver(observer); } -void RecentAppsInteractionHandler::NotifyRecentAppClicked( - const Notification::AppMetadata& app_metadata) { - for (auto& observer : observer_list_) - observer.OnRecentAppClicked(app_metadata); -} - -void RecentAppsInteractionHandler::NotifyRecentAppAddedOrUpdated( - const Notification::AppMetadata& app_metadata, - base::Time last_accessed_timestamp) { - // Each element of |recent_app_metadata_list_| has a unique |package_name| and - // |user_id|. - for (auto it = recent_app_metadata_list_.begin(); - it != recent_app_metadata_list_.end(); ++it) { - if (it->first.package_name == app_metadata.package_name && - it->first.user_id == app_metadata.user_id) { - recent_app_metadata_list_.erase(it); - break; - } - } - - recent_app_metadata_list_.emplace_back(app_metadata, last_accessed_timestamp); -} - -std::vector<Notification::AppMetadata> -RecentAppsInteractionHandler::FetchRecentAppMetadataList() { - // Sort |recent_app_metadata_list_| from most recently visited to least - // recently visited. - std::sort(recent_app_metadata_list_.begin(), recent_app_metadata_list_.end(), - [](const std::pair<Notification::AppMetadata, base::Time>& a, - const std::pair<Notification::AppMetadata, base::Time>& b) { - // More recently visited apps should come before earlier visited - // apps. - return a.second > b.second; - }); - - // At most |kMaxMostRecentApps| recent apps can be displayed. - size_t num_recent_apps_to_display = - std::min(recent_app_metadata_list_.size(), kMaxMostRecentApps); - std::vector<Notification::AppMetadata> app_metadata_list; - for (size_t i = 0; i < num_recent_apps_to_display; ++i) { - app_metadata_list.push_back(recent_app_metadata_list_[i].first); - } - return app_metadata_list; -} - } // namespace phonehub } // namespace ash
diff --git a/ash/components/phonehub/recent_apps_interaction_handler.h b/ash/components/phonehub/recent_apps_interaction_handler.h index ba0affa..846d18d4 100644 --- a/ash/components/phonehub/recent_apps_interaction_handler.h +++ b/ash/components/phonehub/recent_apps_interaction_handler.h
@@ -21,28 +21,27 @@ // observer via this handler. class RecentAppsInteractionHandler { public: - RecentAppsInteractionHandler(); RecentAppsInteractionHandler(const RecentAppsInteractionHandler&) = delete; - RecentAppsInteractionHandler* operator=(const RecentAppsInteractionHandler&) = + RecentAppsInteractionHandler& operator=(const RecentAppsInteractionHandler&) = delete; virtual ~RecentAppsInteractionHandler(); - virtual void NotifyRecentAppClicked( - const Notification::AppMetadata& app_metadata); virtual void AddRecentAppClickObserver(RecentAppClickObserver* observer); virtual void RemoveRecentAppClickObserver(RecentAppClickObserver* observer); + virtual void NotifyRecentAppClicked( + const Notification::AppMetadata& app_metadata) = 0; virtual void NotifyRecentAppAddedOrUpdated( const Notification::AppMetadata& app_metadata, - base::Time last_accessed_timestamp); - virtual std::vector<Notification::AppMetadata> FetchRecentAppMetadataList(); + base::Time last_accessed_timestamp) = 0; + virtual std::vector<Notification::AppMetadata> + FetchRecentAppMetadataList() = 0; + + protected: + RecentAppsInteractionHandler(); private: - FRIEND_TEST_ALL_PREFIXES(RecentAppsInteractionHandlerTest, RecentAppsUpdated); - base::ObserverList<RecentAppClickObserver> observer_list_; - std::vector<std::pair<Notification::AppMetadata, base::Time>> - recent_app_metadata_list_; }; } // namespace phonehub
diff --git a/ash/components/phonehub/recent_apps_interaction_handler_impl.cc b/ash/components/phonehub/recent_apps_interaction_handler_impl.cc new file mode 100644 index 0000000..75e3ce1 --- /dev/null +++ b/ash/components/phonehub/recent_apps_interaction_handler_impl.cc
@@ -0,0 +1,123 @@ +// 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 "ash/components/phonehub/recent_apps_interaction_handler_impl.h" + +#include "ash/components/phonehub/notification.h" +#include "ash/components/phonehub/pref_names.h" +#include "components/prefs/pref_registry_simple.h" +#include "components/prefs/pref_service.h" + +namespace ash { +namespace phonehub { + +const size_t kMaxMostRecentApps = 5; + +// static +void RecentAppsInteractionHandlerImpl::RegisterPrefs( + PrefRegistrySimple* registry) { + registry->RegisterListPref(prefs::kRecentAppsHistory); +} + +RecentAppsInteractionHandlerImpl::RecentAppsInteractionHandlerImpl( + PrefService* pref_service) + : pref_service_(pref_service) {} + +RecentAppsInteractionHandlerImpl::~RecentAppsInteractionHandlerImpl() = default; + +void RecentAppsInteractionHandlerImpl::AddRecentAppClickObserver( + RecentAppClickObserver* observer) { + observer_list_.AddObserver(observer); +} + +void RecentAppsInteractionHandlerImpl::RemoveRecentAppClickObserver( + RecentAppClickObserver* observer) { + observer_list_.RemoveObserver(observer); +} + +void RecentAppsInteractionHandlerImpl::NotifyRecentAppClicked( + const Notification::AppMetadata& app_metadata) { + for (auto& observer : observer_list_) + observer.OnRecentAppClicked(app_metadata); +} + +// Load the |recent_app_metadata_list_| from |pref_service_| if there is a +// history of |recent_app_metadata_list_| exist in |pref_service_|. Then add or +// update |app_metadata| into |recent_app_metadata_list_| and sort +// |recent_app_metadata_list_| based on |last_accessed_timestamp|. Also update +// this |app_metadata| back to |pref_service_|. +void RecentAppsInteractionHandlerImpl::NotifyRecentAppAddedOrUpdated( + const Notification::AppMetadata& app_metadata, + base::Time last_accessed_timestamp) { + LoadRecentAppMetadataListFromPrefIfNeed(); + + // Each element of |recent_app_metadata_list_| has a unique |package_name| and + // |user_id|. + for (auto it = recent_app_metadata_list_.begin(); + it != recent_app_metadata_list_.end(); ++it) { + if (it->first.package_name == app_metadata.package_name && + it->first.user_id == app_metadata.user_id) { + recent_app_metadata_list_.erase(it); + break; + } + } + + recent_app_metadata_list_.emplace_back(app_metadata, last_accessed_timestamp); + + // Sort |recent_app_metadata_list_| from most recently visited to least + // recently visited. + std::sort(recent_app_metadata_list_.begin(), recent_app_metadata_list_.end(), + [](const std::pair<Notification::AppMetadata, base::Time>& a, + const std::pair<Notification::AppMetadata, base::Time>& b) { + // More recently visited apps should come before earlier visited + // apps. + return a.second > b.second; + }); + + SaveRecentAppMetadataListToPref(); +} + +std::vector<Notification::AppMetadata> +RecentAppsInteractionHandlerImpl::FetchRecentAppMetadataList() { + LoadRecentAppMetadataListFromPrefIfNeed(); + + // At most |kMaxMostRecentApps| recent apps can be displayed. + size_t num_recent_apps_to_display = + std::min(recent_app_metadata_list_.size(), kMaxMostRecentApps); + std::vector<Notification::AppMetadata> app_metadata_list; + for (size_t i = 0; i < num_recent_apps_to_display; ++i) { + app_metadata_list.push_back(recent_app_metadata_list_[i].first); + } + return app_metadata_list; +} + +void RecentAppsInteractionHandlerImpl:: + LoadRecentAppMetadataListFromPrefIfNeed() { + if (!has_loaded_prefs_) { + const base::Value* recent_apps_history_pref = + pref_service_->GetList(prefs::kRecentAppsHistory); + for (const auto& value : recent_apps_history_pref->GetList()) { + DCHECK(value.is_dict()); + recent_app_metadata_list_.emplace_back( + Notification::AppMetadata::FromValue(value), + base::Time::FromDoubleT(0)); + } + has_loaded_prefs_ = true; + } +} + +void RecentAppsInteractionHandlerImpl::SaveRecentAppMetadataListToPref() { + size_t num_recent_apps_to_display = + std::min(recent_app_metadata_list_.size(), kMaxMostRecentApps); + std::vector<base::Value> app_metadata_value_list; + for (size_t i = 0; i < num_recent_apps_to_display; ++i) { + app_metadata_value_list.push_back( + recent_app_metadata_list_[i].first.ToValue()); + } + pref_service_->Set(prefs::kRecentAppsHistory, + base::Value(std::move(app_metadata_value_list))); +} + +} // namespace phonehub +} // namespace ash
diff --git a/ash/components/phonehub/recent_apps_interaction_handler_impl.h b/ash/components/phonehub/recent_apps_interaction_handler_impl.h new file mode 100644 index 0000000..4421658 --- /dev/null +++ b/ash/components/phonehub/recent_apps_interaction_handler_impl.h
@@ -0,0 +1,60 @@ +// 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 ASH_COMPONENTS_PHONEHUB_RECENT_APPS_INTERACTION_HANDLER_IMPL_H_ +#define ASH_COMPONENTS_PHONEHUB_RECENT_APPS_INTERACTION_HANDLER_IMPL_H_ + +#include <stdint.h> + +#include "ash/components/phonehub/notification.h" +#include "ash/components/phonehub/recent_app_click_observer.h" +#include "ash/components/phonehub/recent_apps_interaction_handler.h" +#include "base/gtest_prod_util.h" +#include "base/observer_list.h" +#include "base/observer_list_types.h" + +class PrefRegistrySimple; +class PrefService; + +namespace ash { +namespace phonehub { + +// The handler that exposes APIs to interact with Phone Hub Recent Apps. +class RecentAppsInteractionHandlerImpl : public RecentAppsInteractionHandler { + public: + static void RegisterPrefs(PrefRegistrySimple* registry); + + explicit RecentAppsInteractionHandlerImpl(PrefService* pref_service); + ~RecentAppsInteractionHandlerImpl() override; + + // RecentAppsInteractionHandler: + void NotifyRecentAppClicked( + const Notification::AppMetadata& app_metadata) override; + void AddRecentAppClickObserver(RecentAppClickObserver* observer) override; + void RemoveRecentAppClickObserver(RecentAppClickObserver* observer) override; + void NotifyRecentAppAddedOrUpdated( + const Notification::AppMetadata& app_metadata, + base::Time last_accessed_timestamp) override; + std::vector<Notification::AppMetadata> FetchRecentAppMetadataList() override; + + private: + FRIEND_TEST_ALL_PREFIXES(RecentAppsInteractionHandlerTest, RecentAppsUpdated); + + void LoadRecentAppMetadataListFromPrefIfNeed(); + void SaveRecentAppMetadataListToPref(); + + // Whether this class has finished loading |recent_app_metadata_list_| from + // pref. + bool has_loaded_prefs_ = false; + + base::ObserverList<RecentAppClickObserver> observer_list_; + std::vector<std::pair<Notification::AppMetadata, base::Time>> + recent_app_metadata_list_; + PrefService* pref_service_; +}; + +} // namespace phonehub +} // namespace ash + +#endif // ASH_COMPONENTS_PHONEHUB_RECENT_APPS_INTERACTION_HANDLER_IMPL_H_
diff --git a/ash/components/phonehub/recent_apps_interaction_handler_unittest.cc b/ash/components/phonehub/recent_apps_interaction_handler_impl_unittest.cc similarity index 76% rename from ash/components/phonehub/recent_apps_interaction_handler_unittest.cc rename to ash/components/phonehub/recent_apps_interaction_handler_impl_unittest.cc index f43f1e7..29ac6e8 100644 --- a/ash/components/phonehub/recent_apps_interaction_handler_unittest.cc +++ b/ash/components/phonehub/recent_apps_interaction_handler_impl_unittest.cc
@@ -2,11 +2,13 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "ash/components/phonehub/recent_apps_interaction_handler.h" +#include "ash/components/phonehub/recent_apps_interaction_handler_impl.h" #include <memory> #include "ash/components/phonehub/notification.h" +#include "ash/components/phonehub/pref_names.h" +#include "components/prefs/testing_pref_service.h" #include "testing/gtest/include/gtest/gtest.h" namespace ash { @@ -42,7 +44,9 @@ // testing::Test: void SetUp() override { - interaction_handler_ = std::make_unique<RecentAppsInteractionHandler>(); + RecentAppsInteractionHandlerImpl::RegisterPrefs(pref_service_.registry()); + interaction_handler_ = + std::make_unique<RecentAppsInteractionHandlerImpl>(&pref_service_); interaction_handler_->AddRecentAppClickObserver(&fake_click_handler_); } @@ -50,15 +54,37 @@ interaction_handler_->RemoveRecentAppClickObserver(&fake_click_handler_); } + void Initialize() { + const char16_t app_visible_name1[] = u"Fake App"; + const char package_name1[] = "com.fakeapp"; + const int64_t expected_user_id1 = 1; + auto app_metadata1 = Notification::AppMetadata( + app_visible_name1, package_name1, gfx::Image(), expected_user_id1); + + const char16_t app_visible_name2[] = u"Fake App2"; + const char package_name2[] = "com.fakeapp2"; + const int64_t expected_user_id2 = 2; + auto app_metadata2 = Notification::AppMetadata( + app_visible_name2, package_name2, gfx::Image(), expected_user_id2); + + std::vector<base::Value> app_metadata_value_list; + app_metadata_value_list.push_back(app_metadata1.ToValue()); + app_metadata_value_list.push_back(app_metadata2.ToValue()); + + pref_service_.Set(prefs::kRecentAppsHistory, + base::Value(std::move(app_metadata_value_list))); + } + std::string GetPackageName() { return fake_click_handler_.get_package_name(); } - RecentAppsInteractionHandler& handler() { return *interaction_handler_; } + RecentAppsInteractionHandlerImpl& handler() { return *interaction_handler_; } private: FakeClickHandler fake_click_handler_; - std::unique_ptr<RecentAppsInteractionHandler> interaction_handler_; + std::unique_ptr<RecentAppsInteractionHandlerImpl> interaction_handler_; + TestingPrefServiceSimple pref_service_; }; TEST_F(RecentAppsInteractionHandlerTest, RecentAppsClicked) { @@ -175,5 +201,23 @@ EXPECT_EQ(package_name2, recent_apps_metadata_result[4].package_name); } +TEST_F(RecentAppsInteractionHandlerTest, + FetchRecentAppMetadataListFromPreference) { + Initialize(); + + const char package_name1[] = "com.fakeapp"; + const char package_name2[] = "com.fakeapp2"; + std::vector<Notification::AppMetadata> recent_apps_metadata_result = + handler().FetchRecentAppMetadataList(); + + const size_t number_of_recent_apps_in_preference = 2; + recent_apps_metadata_result = handler().FetchRecentAppMetadataList(); + EXPECT_EQ(number_of_recent_apps_in_preference, + recent_apps_metadata_result.size()); + + EXPECT_EQ(package_name1, recent_apps_metadata_result[0].package_name); + EXPECT_EQ(package_name2, recent_apps_metadata_result[1].package_name); +} + } // namespace phonehub } // namespace ash
diff --git a/ash/constants/ash_features.cc b/ash/constants/ash_features.cc index 479648e..edac159 100644 --- a/ash/constants/ash_features.cc +++ b/ash/constants/ash_features.cc
@@ -1267,9 +1267,9 @@ const base::Feature kWakeOnWifiAllowed{"WakeOnWifiAllowed", base::FEATURE_DISABLED_BY_DEFAULT}; -// Enable new wallpaper experience SWA. +// Enable new wallpaper experience in WebUI inside system settings. const base::Feature kWallpaperWebUI{"WallpaperWebUI", - base::FEATURE_ENABLED_BY_DEFAULT}; + base::FEATURE_DISABLED_BY_DEFAULT}; // Enable full screen wallpaper preview in new wallpaper experience. Requires // |kWallpaperWebUI| to also be enabled.
diff --git a/ash/drag_drop/drag_drop_capture_delegate.cc b/ash/drag_drop/drag_drop_capture_delegate.cc deleted file mode 100644 index 364473b8495..0000000 --- a/ash/drag_drop/drag_drop_capture_delegate.cc +++ /dev/null
@@ -1,70 +0,0 @@ -// Copyright (c) 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 "ash/drag_drop/drag_drop_capture_delegate.h" -#include "ash/drag_drop/drag_drop_tracker.h" -#include "ui/aura/env.h" -#include "ui/aura/window.h" -#include "ui/aura/window_delegate.h" -#include "ui/aura/window_event_dispatcher.h" -#include "ui/aura/window_observer.h" -#include "ui/base/hit_test.h" -#include "ui/events/event.h" -#include "ui/events/event_target.h" -#include "ui/events/event_utils.h" -#include "ui/events/types/event_type.h" - -namespace ash { -namespace { - -void DispatchGestureEndToWindow(aura::Window* window) { - if (window && window->delegate()) { - ui::GestureEventDetails details(ui::ET_GESTURE_END); - details.set_device_type(ui::GestureDeviceType::DEVICE_TOUCHSCREEN); - ui::GestureEvent gesture_end(0, 0, 0, ui::EventTimeForNow(), details); - window->delegate()->OnGestureEvent(&gesture_end); - } -} -} // namespace - -DragDropCaptureDelegate::DragDropCaptureDelegate() {} -DragDropCaptureDelegate::~DragDropCaptureDelegate() { - drag_drop_tracker_.reset(); -} - -bool DragDropCaptureDelegate::TakeCapture( - aura::Window* root_window, - aura::Window* source_window, - CancelDragDropCallback callback, - ui::TransferTouchesBehavior behavior) { - drag_drop_tracker_.reset(new DragDropTracker(root_window, callback)); - // We need to transfer the current gesture sequence and the GR's touch event - // queue to the |drag_drop_tracker_|'s capture window so that when it takes - // capture, it still gets a valid gesture state. - aura::Env::GetInstance()->gesture_recognizer()->TransferEventsTo( - source_window, drag_drop_tracker_->capture_window(), behavior); - // We also send a gesture end to the source window so it can clear state. - // TODO(varunjain): Remove this whole block when gesture sequence - // transferring is properly done in the GR (http://crbug.com/160558) - DispatchGestureEndToWindow(source_window); - drag_drop_tracker_->TakeCapture(); - return true; -} - -aura::Window* DragDropCaptureDelegate::GetTarget( - const ui::LocatedEvent& event) { - return drag_drop_tracker_->GetTarget(event); -} - -ui::LocatedEvent* DragDropCaptureDelegate::ConvertEvent( - aura::Window* target, - const ui::LocatedEvent& event) { - return drag_drop_tracker_->ConvertEvent(target, event); -} - -aura::Window* DragDropCaptureDelegate::capture_window() { - return drag_drop_tracker_->capture_window(); -} - -} // namespace ash
diff --git a/ash/drag_drop/drag_drop_capture_delegate.h b/ash/drag_drop/drag_drop_capture_delegate.h deleted file mode 100644 index 1d437e5..0000000 --- a/ash/drag_drop/drag_drop_capture_delegate.h +++ /dev/null
@@ -1,63 +0,0 @@ -// Copyright (c) 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 ASH_DRAG_DROP_DRAG_DROP_CAPTURE_DELEGATE_H_ -#define ASH_DRAG_DROP_DRAG_DROP_CAPTURE_DELEGATE_H_ - -#include "ash/ash_export.h" -#include "base/bind.h" -#include "ui/events/gestures/gesture_types.h" - -namespace aura { -class Window; -} - -namespace ui { -class LocatedEvent; -} - -namespace ash { -class DragDropTracker; - -class ASH_EXPORT DragDropCaptureDelegate { - public: - using CancelDragDropCallback = base::RepeatingCallback<void(void)>; - - DragDropCaptureDelegate(); - - ~DragDropCaptureDelegate(); - - // Conditionally takes capture of top level touch events, returning whether - // this was successful. - bool TakeCapture(aura::Window* root_window, - aura::Window* source_window, - CancelDragDropCallback callback, - ui::TransferTouchesBehavior behavior); - - // Converts an event target that was dispatched against a capture window to - // once that can be processed by the drag and drop controller. - // - // This should only be called on events if TakeCapture returned true at the - // start of a drag and drop session. - aura::Window* GetTarget(const ui::LocatedEvent& event); - - // Converts an event that was dispatched against a capture window to once - // that can be processed by the drag and drop controller, using the target - // returned via GetTarget. - // - // This should only be called on events if TakeCapture returned true at the - // start of a drag and drop session. - ui::LocatedEvent* ConvertEvent(aura::Window* target, - const ui::LocatedEvent& event); - - // Return the capture window used if TakeCapture returns true. - aura::Window* capture_window(); - - private: - std::unique_ptr<DragDropTracker> drag_drop_tracker_; -}; - -} // namespace ash - -#endif // ASH_DRAG_DROP_DRAG_DROP_CAPTURE_DELEGATE_H_
diff --git a/ash/drag_drop/drag_drop_capture_delegate_unittest.cc b/ash/drag_drop/drag_drop_capture_delegate_unittest.cc deleted file mode 100644 index 42683483..0000000 --- a/ash/drag_drop/drag_drop_capture_delegate_unittest.cc +++ /dev/null
@@ -1,70 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "ash/drag_drop/drag_drop_capture_delegate.h" - -#include "ash/shell.h" -#include "ash/test/ash_test_base.h" -#include "ash/test_shell_delegate.h" -#include "base/bind.h" -#include "base/test/bind.h" -#include "ui/aura/test/test_window_delegate.h" -#include "ui/events/event.h" -#include "ui/events/event_utils.h" -#include "ui/events/gesture_detection/gesture_configuration.h" -#include "ui/events/gestures/gesture_types.h" - -namespace ash { -namespace { - -class DragDropCaptureDelegateTest : public AshTestBase { - public: - DragDropCaptureDelegateTest() = default; - - DragDropCaptureDelegateTest(const DragDropCaptureDelegateTest&) = delete; - DragDropCaptureDelegateTest& operator=(const DragDropCaptureDelegateTest&) = - delete; - - ~DragDropCaptureDelegateTest() override = default; - - // AshTestBase: - void SetUp() override { - drag_drop_capture_delegate_.reset(new DragDropCaptureDelegate()); - AshTestBase::SetUp(std::make_unique<TestShellDelegate>()); - } - - void TearDown() override { - drag_drop_capture_delegate_.reset(); - AshTestBase::TearDown(); - } - - protected: - std::unique_ptr<DragDropCaptureDelegate> drag_drop_capture_delegate_; -}; - -} // namespace - -TEST_F(DragDropCaptureDelegateTest, CanTakeCaptureAndConvertToOriginalWindow) { - std::unique_ptr<aura::Window> window(CreateTestWindowInShellWithDelegate( - aura::test::TestWindowDelegate::CreateSelfDestroyingDelegate(), -1, - gfx::Rect(0, 0, 100, 100))); - - drag_drop_capture_delegate_->TakeCapture( - window->GetRootWindow(), window.get(), - base::BindLambdaForTesting([]() {}), - ui::TransferTouchesBehavior::kCancel); - - EXPECT_TRUE(drag_drop_capture_delegate_->capture_window()->HasCapture()); - - ui::GestureEventDetails event_details(ui::ET_GESTURE_SCROLL_UPDATE); - ui::GestureEvent gesture_event(0, 0, 0, ui::EventTimeForNow(), event_details); - ui::Event::DispatcherApi(&gesture_event) - .set_target(drag_drop_capture_delegate_->capture_window()); - auto* converted_target = - drag_drop_capture_delegate_->GetTarget(gesture_event); - - EXPECT_EQ(converted_target, window.get()); -} - -} // namespace ash
diff --git a/ash/drag_drop/drag_drop_controller.cc b/ash/drag_drop/drag_drop_controller.cc index 14e19d2..fa226f5 100644 --- a/ash/drag_drop/drag_drop_controller.cc +++ b/ash/drag_drop/drag_drop_controller.cc
@@ -170,19 +170,16 @@ operation_ = DragOperation::kNone; current_drag_event_source_ = source; - capture_delegate_ = nullptr; // When an extended drag is started, a capture window will be created to // handle moving gestures between different wl surfaces to support dragging // chrome tabs into and out of browsers. if (source == ui::mojom::DragEventSource::kTouch && toplevel_window_drag_delegate_) { - toplevel_window_drag_delegate_->TakeCapture( + using_drag_capture_ = toplevel_window_drag_delegate_->TakeCapture( root_window, source_window, base::BindRepeating(&DragDropController::CancelIfInProgress, - base::Unretained(this)), - ui::TransferTouchesBehavior::kCancel); - capture_delegate_ = toplevel_window_drag_delegate_; + base::Unretained(this))); } drag_source_window_ = source_window; @@ -219,16 +216,6 @@ root_window, drag_source_window_, start_location_); static_cast<DragImageView*>(drag_image_widget_->GetContentsView()) ->SetTouchDragOperationHintOff(); - // Avoid taking capture twice if the toplevel drag delegate is being used. - if (!toplevel_window_drag_delegate_ && - source == ui::mojom::DragEventSource::kTouch) { - tab_drag_drop_delegate_->TakeCapture( - root_window, source_window, - base::BindRepeating(&DragDropController::CancelIfInProgress, - base::Unretained(this)), - ui::TransferTouchesBehavior::kDontCancel); - capture_delegate_ = tab_drag_drop_delegate_.get(); - } } if (should_block_during_drag_drop_) { @@ -404,8 +391,9 @@ } aura::Window* translated_target; - if (capture_delegate_) { - translated_target = capture_delegate_->GetTarget(touch_offset_event); + if (using_drag_capture_) { + translated_target = + toplevel_window_drag_delegate_->GetTarget(touch_offset_event); } else { ui::Event::DispatcherApi(&touch_offset_event).set_target(event->target()); translated_target = GetTarget(touch_offset_event); @@ -418,9 +406,9 @@ } ui::LocatedEvent* translated_event; - if (capture_delegate_) { - translated_event = - capture_delegate_->ConvertEvent(translated_target, touch_offset_event); + if (using_drag_capture_) { + translated_event = toplevel_window_drag_delegate_->ConvertEvent( + translated_target, touch_offset_event); } else { translated_event = ConvertEvent(translated_target, touch_offset_event).release(); @@ -440,10 +428,11 @@ // drag drop is still in progress. The drag drop ends only when the nested // message loop ends. Due to this, we have to defer forwarding // the long tap. - if (capture_delegate_) { + if (using_drag_capture_) { pending_long_tap_ = std::make_unique<ui::GestureEvent>( *event, - static_cast<aura::Window*>(capture_delegate_->capture_window()), + static_cast<aura::Window*>( + toplevel_window_drag_delegate_->capture_window()), static_cast<aura::Window*>(drag_source_window_)); } else { pending_long_tap_ = ui::Event::Clone(*event); @@ -710,7 +699,7 @@ drag_data_.reset(); allowed_operations_ = 0; tab_drag_drop_delegate_.reset(); - capture_delegate_ = nullptr; + using_drag_capture_ = false; } void DragDropController::PerformDrop(
diff --git a/ash/drag_drop/drag_drop_controller.h b/ash/drag_drop/drag_drop_controller.h index 072b9eb..aff369b 100644 --- a/ash/drag_drop/drag_drop_controller.h +++ b/ash/drag_drop/drag_drop_controller.h
@@ -9,7 +9,6 @@ #include "ash/ash_export.h" #include "ash/display/window_tree_host_manager.h" -#include "ash/drag_drop/drag_drop_capture_delegate.h" #include "ash/drag_drop/tab_drag_drop_delegate.h" #include "base/callback.h" #include "base/memory/weak_ptr.h" @@ -102,9 +101,6 @@ // Actual implementation of |DragCancel()|. protected for testing. virtual void DoDragCancel(base::TimeDelta drag_cancel_animation_duration); - // Exposed for test assertions. - DragDropCaptureDelegate* get_capture_delegate() { return capture_delegate_; } - private: friend class DragDropControllerTest; friend class DragDropControllerTestApi; @@ -169,8 +165,8 @@ // Closure for quitting nested run loop. base::OnceClosure quit_closure_; - // If non-null, a drag is active which required a capture window. - DragDropCaptureDelegate* capture_delegate_; + // Whether a top level drag is active which required a capture window. + bool using_drag_capture_ = false; ui::mojom::DragEventSource current_drag_event_source_ = ui::mojom::DragEventSource::kMouse;
diff --git a/ash/drag_drop/drag_drop_controller_unittest.cc b/ash/drag_drop/drag_drop_controller_unittest.cc index c2c57b49..298df6c 100644 --- a/ash/drag_drop/drag_drop_controller_unittest.cc +++ b/ash/drag_drop/drag_drop_controller_unittest.cc
@@ -198,10 +198,6 @@ allowed_operations, source); } - DragDropCaptureDelegate* get_capture_delegate() { - return DragDropController::get_capture_delegate(); - } - void DragUpdate(aura::Window* target, const ui::LocatedEvent& event) override { DragDropController::DragUpdate(target, event); @@ -418,6 +414,29 @@ events_forwarded_++; } + bool TakeCapture(aura::Window* root_window, + aura::Window* source_window, + ash::ToplevelWindowDragDelegate::CancelDragDropCallback + callback) override { + return false; + } + + aura::Window* GetTarget(const ui::LocatedEvent& event) override { + NOTREACHED(); + return nullptr; + } + + ui::LocatedEvent* ConvertEvent(aura::Window* target, + const ui::LocatedEvent& event) override { + NOTREACHED(); + return nullptr; + } + + aura::Window* capture_window() override { + NOTREACHED(); + return nullptr; + } + private: State state_ = State::kNotInvoked; int events_forwarded_ = 0; @@ -1514,23 +1533,6 @@ EXPECT_EQ(6, delegate.events_forwarded()); } -TEST_F(DragDropControllerTest, DragWithChromeTabDelegateTakesCapture) { - base::test::ScopedFeatureList feature_list; - feature_list.InitAndEnableFeature(features::kWebUITabStripTabDragIntegration); - - std::unique_ptr<aura::Window> window(CreateTestWindowInShellWithDelegate( - aura::test::TestWindowDelegate::CreateSelfDestroyingDelegate(), -1, - gfx::Rect(0, 0, 100, 100))); - - auto data(std::make_unique<ui::OSExchangeData>()); - drag_drop_controller_->StartDragAndDrop( - std::move(data), window->GetRootWindow(), window.get(), gfx::Point(5, 5), - ui::DragDropTypes::DRAG_MOVE, ui::mojom::DragEventSource::kTouch); - - // Should create a captue delegate which takes capture from the window. - EXPECT_TRUE(drag_drop_controller_->get_capture_delegate()); -} - namespace { class MockDataTransferPolicyController
diff --git a/ash/drag_drop/tab_drag_drop_delegate.h b/ash/drag_drop/tab_drag_drop_delegate.h index cccb8ab..67ceeeb7 100644 --- a/ash/drag_drop/tab_drag_drop_delegate.h +++ b/ash/drag_drop/tab_drag_drop_delegate.h
@@ -8,7 +8,6 @@ #include <memory> #include "ash/ash_export.h" -#include "ash/drag_drop/drag_drop_capture_delegate.h" #include "ash/wm/splitview/split_view_controller.h" #include "ui/gfx/geometry/point.h" @@ -29,7 +28,7 @@ // Provides special handling for Chrome tab drags on behalf of // DragDropController. This must be created at the beginning of a tab drag and // destroyed at the end. -class ASH_EXPORT TabDragDropDelegate : public DragDropCaptureDelegate { +class ASH_EXPORT TabDragDropDelegate { public: // Determines whether |drag_data| indicates a tab drag from a WebUI tab strip // (or simply returns false if the integration is disabled).
diff --git a/ash/drag_drop/toplevel_window_drag_delegate.h b/ash/drag_drop/toplevel_window_drag_delegate.h index 3eaf9ab..ffa578b 100644 --- a/ash/drag_drop/toplevel_window_drag_delegate.h +++ b/ash/drag_drop/toplevel_window_drag_delegate.h
@@ -5,7 +5,6 @@ #ifndef ASH_DRAG_DROP_TOPLEVEL_WINDOW_DRAG_DELEGATE_H_ #define ASH_DRAG_DROP_TOPLEVEL_WINDOW_DRAG_DELEGATE_H_ -#include "ash/drag_drop/drag_drop_capture_delegate.h" #include "base/bind.h" #include "ui/base/dragdrop/mojom/drag_drop_types.mojom-forward.h" @@ -25,7 +24,7 @@ // Interface that makes it possible to implement toplevel window drag handling // during Drag & Drop sessions. -class ToplevelWindowDragDelegate : public DragDropCaptureDelegate { +class ToplevelWindowDragDelegate { public: virtual void OnToplevelWindowDragStarted(const gfx::PointF& start_location, ui::mojom::DragEventSource source, @@ -37,6 +36,33 @@ virtual void OnToplevelWindowDragEvent(ui::LocatedEvent* event) = 0; + using CancelDragDropCallback = base::RepeatingCallback<void(void)>; + + // Conditionally takes capture of top level touch events, returning whether + // this was successful. + virtual bool TakeCapture(aura::Window* root_window, + aura::Window* source_window, + CancelDragDropCallback callback) = 0; + + // Converts an event target that was dispatched against a capture window to + // once that can be processed by the drag and drop controller. + // + // This should only be called on events if TakeCapture returned true at the + // start of a drag and drop session. + virtual aura::Window* GetTarget(const ui::LocatedEvent& event) = 0; + + // Converts an event that was dispatched against a capture window to once + // that can be processed by the drag and drop controller, using the target + // returned via GetTarget. + // + // This should only be called on events if TakeCapture returned true at the + // start of a drag and drop session. + virtual ui::LocatedEvent* ConvertEvent(aura::Window* target, + const ui::LocatedEvent& event) = 0; + + // Return the capture window used if TakeCapture returns true. + virtual aura::Window* capture_window() = 0; + protected: virtual ~ToplevelWindowDragDelegate() = default; };
diff --git a/ash/session/fullscreen_alert_bubble.cc b/ash/session/fullscreen_alert_bubble.cc deleted file mode 100644 index 49b6d45..0000000 --- a/ash/session/fullscreen_alert_bubble.cc +++ /dev/null
@@ -1,185 +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. - -#include "ash/session/fullscreen_alert_bubble.h" - -#include <memory> - -#include "ash/login/ui/system_label_button.h" -#include "ash/public/cpp/shell_window_ids.h" -#include "ash/session/fullscreen_controller.h" -#include "ash/shell.h" -#include "ash/strings/grit/ash_strings.h" -#include "ash/style/ash_color_provider.h" -#include "ash/wm/work_area_insets.h" -#include "base/bind.h" -#include "base/location.h" -#include "base/time/time.h" -#include "ui/accessibility/ax_enums.mojom.h" -#include "ui/accessibility/ax_node_data.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/compositor/layer.h" -#include "ui/events/event.h" -#include "ui/gfx/geometry/insets.h" -#include "ui/views/background.h" -#include "ui/views/controls/button/button.h" -#include "ui/views/layout/box_layout.h" -#include "ui/views/view.h" -#include "ui/wm/core/visibility_controller.h" -#include "ui/wm/core/window_animations.h" - -namespace ash { - -namespace { - -constexpr int kAlertBubbleWidthDp = 384; -constexpr int kBubblePaddingDp = 16; -constexpr int kBubbleBetweenChildSpacingDp = 16; -constexpr int kBubbleBorderRadius = 8; -constexpr int kButtonPaddingDp = 8; -constexpr int kOffsetFromEdge = 32; -constexpr base::TimeDelta kAlertDuration = base::Seconds(4); -constexpr base::TimeDelta kBubbleAnimationDuration = base::Milliseconds(300); - -constexpr SkColor kAlertTextColor = - SkColorSetA(gfx::kGoogleGrey200, SK_AlphaOPAQUE); - -} // namespace - -class FullscreenAlertBubbleView : public views::View { - public: - METADATA_HEADER(FullscreenAlertBubbleView); - - FullscreenAlertBubbleView(views::Button::PressedCallback on_dismiss, - views::Button::PressedCallback on_exit_fullscreen) { - SetPaintToLayer(); - SkColor background_color = AshColorProvider::Get()->GetBaseLayerColor( - AshColorProvider::BaseLayerType::kTransparent80); - layer()->SetBackgroundBlur(ColorProvider::kBackgroundBlurSigma); - SetBackground(views::CreateRoundedRectBackground(background_color, - kBubbleBorderRadius)); - layer()->SetFillsBoundsOpaquely(false); - - auto* main_layout = SetLayoutManager(std::make_unique<views::BoxLayout>( - views::BoxLayout::Orientation::kVertical, gfx::Insets(kBubblePaddingDp), - kBubbleBetweenChildSpacingDp)); - main_layout->set_cross_axis_alignment( - views::BoxLayout::CrossAxisAlignment::kCenter); - - alert_text_ = AddChildView(std::make_unique<views::Label>( - l10n_util::GetStringUTF16(IDS_FULLSCREEN_ALERT))); - alert_text_->SetEnabledColor(kAlertTextColor); - alert_text_->SetAutoColorReadabilityEnabled(false); - alert_text_->SetMultiLine(true); - - auto* button_container = AddChildView(std::make_unique<views::View>()); - auto* button_layout = - button_container->SetLayoutManager(std::make_unique<views::BoxLayout>( - views::BoxLayout::Orientation::kHorizontal, gfx::Insets(), - kButtonPaddingDp)); - button_layout->set_cross_axis_alignment( - views::BoxLayout::CrossAxisAlignment::kCenter); - - auto* dismiss = - button_container->AddChildView(std::make_unique<SystemLabelButton>( - views::Button::PressedCallback(), - l10n_util::GetStringUTF16(IDS_DISMISS_BUTTON))); - dismiss->SetCallback(on_dismiss); - - auto* exit_fullscreen = - button_container->AddChildView(std::make_unique<SystemLabelButton>( - views::Button::PressedCallback(), - l10n_util::GetStringUTF16(IDS_EXIT_FULLSCREEN_BUTTON))); - exit_fullscreen->SetBackgroundAndFont(/*alert_mode=*/true); - exit_fullscreen->SetCallback(on_exit_fullscreen); - } - - // views::View: - gfx::Size CalculatePreferredSize() const override { - gfx::Size size; - size.set_width(kAlertBubbleWidthDp); - size.set_height(GetHeightForWidth(kAlertBubbleWidthDp)); - return size; - } - - void GetAccessibleNodeData(ui::AXNodeData* node_data) override { - node_data->role = ax::mojom::Role::kAlertDialog; - node_data->SetName(alert_text_->GetText()); - } - - private: - views::Label* alert_text_ = nullptr; -}; - -BEGIN_METADATA(FullscreenAlertBubbleView, views::View) -END_METADATA - -FullscreenAlertBubble::FullscreenAlertBubble() - : bubble_widget_(std::make_unique<views::Widget>()), - timer_(std::make_unique<base::OneShotTimer>()) { - bubble_view_ = std::make_unique<FullscreenAlertBubbleView>( - views::Button::PressedCallback(base::BindRepeating( - &FullscreenAlertBubble::Dismiss, base::Unretained(this))), - views::Button::PressedCallback(base::BindRepeating( - &FullscreenAlertBubble::ExitFullscreen, base::Unretained(this)))); - - views::Widget::InitParams params; - params.type = views::Widget::InitParams::TYPE_POPUP; - params.name = "FullscreenAlertBubble"; - params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; - params.z_order = ui::ZOrderLevel::kFloatingUIElement; - params.opacity = views::Widget::InitParams::WindowOpacity::kTranslucent; - params.parent = Shell::GetPrimaryRootWindow()->GetChildById( - kShellWindowId_SettingBubbleContainer); - params.bounds = CalculateBubbleBounds(); - - bubble_widget_->Init(std::move(params)); - - bubble_view_->set_owned_by_client(); - bubble_widget_->SetContentsView(bubble_view_.get()); - - bubble_widget_->SetVisibilityChangedAnimationsEnabled(true); - - aura::Window* native_window = bubble_widget_->GetNativeWindow(); - wm::SetWindowVisibilityChangesAnimated(native_window); - wm::SetWindowVisibilityAnimationType( - native_window, wm::WINDOW_VISIBILITY_ANIMATION_TYPE_FADE); - wm::SetWindowVisibilityAnimationDuration(native_window, - kBubbleAnimationDuration); -} - -FullscreenAlertBubble::~FullscreenAlertBubble() = default; - -void FullscreenAlertBubble::Show() { - bubble_widget_->Show(); - - timer_->Start(FROM_HERE, kAlertDuration, - base::BindOnce(&FullscreenAlertBubble::Hide, - weak_ptr_factory_.GetWeakPtr())); -} - -void FullscreenAlertBubble::Hide() { - bubble_widget_->Hide(); -} - -void FullscreenAlertBubble::Dismiss(const ui::Event& event) { - Hide(); -} - -void FullscreenAlertBubble::ExitFullscreen(const ui::Event& event) { - FullscreenController::MaybeExitFullscreen(); - Hide(); -} - -gfx::Rect FullscreenAlertBubble::CalculateBubbleBounds() { - gfx::Rect work_area = WorkAreaInsets::ForWindow(Shell::GetPrimaryRootWindow()) - ->user_work_area_bounds(); - int x = work_area.x() + (work_area.width() - kAlertBubbleWidthDp) / 2; - int y = work_area.y() + kOffsetFromEdge; - return gfx::Rect(gfx::Point(x, y), bubble_view_->CalculatePreferredSize()); -} - -} // namespace ash
diff --git a/ash/session/fullscreen_alert_bubble.h b/ash/session/fullscreen_alert_bubble.h deleted file mode 100644 index 1558a97..0000000 --- a/ash/session/fullscreen_alert_bubble.h +++ /dev/null
@@ -1,63 +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. - -#ifndef ASH_SESSION_FULLSCREEN_ALERT_BUBBLE_H_ -#define ASH_SESSION_FULLSCREEN_ALERT_BUBBLE_H_ - -#include <memory> - -#include "ash/ash_export.h" -#include "base/memory/weak_ptr.h" -#include "base/timer/timer.h" -#include "ui/views/controls/button/button.h" -#include "ui/views/widget/widget.h" - -namespace views { -class Button; -class Widget; -} // namespace views - -namespace gfx { -class Rect; -} - -namespace base { -class OneShotTimer; -} - -namespace ash { - -class FullscreenAlertBubbleView; - -// When the device returns back from sleep or low brightness without a lock -// screen, remind the user to exit fullscreen before entering password. -class ASH_EXPORT FullscreenAlertBubble { - public: - FullscreenAlertBubble(); - ~FullscreenAlertBubble(); - - FullscreenAlertBubble(const FullscreenAlertBubble&) = delete; - FullscreenAlertBubble& operator=(const FullscreenAlertBubble&) = delete; - - void Show(); - void Hide(); - - void Dismiss(const ui::Event& event); - void ExitFullscreen(const ui::Event& event); - - private: - gfx::Rect CalculateBubbleBounds(); - - std::unique_ptr<FullscreenAlertBubbleView> bubble_view_; - - std::unique_ptr<views::Widget> bubble_widget_; - - std::unique_ptr<base::OneShotTimer> timer_; - - base::WeakPtrFactory<FullscreenAlertBubble> weak_ptr_factory_{this}; -}; - -} // namespace ash - -#endif // ASH_SESSION_FULLSCREEN_ALERT_BUBBLE_H_
diff --git a/ash/session/fullscreen_controller.cc b/ash/session/fullscreen_controller.cc index 2423546..a012772 100644 --- a/ash/session/fullscreen_controller.cc +++ b/ash/session/fullscreen_controller.cc
@@ -8,7 +8,7 @@ #include "ash/constants/ash_features.h" #include "ash/constants/ash_pref_names.h" -#include "ash/session/fullscreen_alert_bubble.h" +#include "ash/session/fullscreen_notification_bubble.h" #include "ash/session/session_controller_impl.h" #include "ash/shelf/shelf.h" #include "ash/shell.h" @@ -59,19 +59,19 @@ active_window_state->OnWMEvent(&event); } -void FullscreenController::MaybeShowAlert() { +void FullscreenController::MaybeShowNotification() { if (!features::IsFullscreenAlertBubbleEnabled()) return; - auto* session_controler = Shell::Get()->session_controller(); + auto* session_controller = Shell::Get()->session_controller(); // Check if a user session is active to exclude OOBE process. - if (session_controler->GetSessionState() != + if (session_controller->GetSessionState() != session_manager::SessionState::ACTIVE) { return; } - auto* prefs = session_controler->GetPrimaryUserPrefService(); + auto* prefs = session_controller->GetPrimaryUserPrefService(); if (!prefs->GetBoolean(prefs::kFullscreenAlertEnabled)) return; @@ -90,9 +90,9 @@ return; if (!bubble_) - bubble_ = std::make_unique<FullscreenAlertBubble>(); + bubble_ = std::make_unique<FullscreenNotificationBubble>(); - bubble_->Show(); + bubble_->ShowForWindowState(active_window_state); } // static @@ -132,7 +132,7 @@ device_in_dark_ = true; } else { if (device_in_dark_) - MaybeShowAlert(); + MaybeShowNotification(); device_in_dark_ = false; } } @@ -141,9 +141,9 @@ chromeos::PowerManagerClient::LidState state, base::TimeTicks timestamp) { // Show alert when the lid is opened. This also covers the case when the user - // turn off "Sleep when cover is closed". + // turns off "Sleep when cover is closed". if (state == chromeos::PowerManagerClient::LidState::OPEN) - MaybeShowAlert(); + MaybeShowNotification(); } } // namespace ash
diff --git a/ash/session/fullscreen_controller.h b/ash/session/fullscreen_controller.h index 11f5bca6..9938b50 100644 --- a/ash/session/fullscreen_controller.h +++ b/ash/session/fullscreen_controller.h
@@ -15,7 +15,7 @@ namespace ash { class SessionControllerImpl; -class FullscreenAlertBubble; +class FullscreenNotificationBubble; class FullscreenController : public chromeos::PowerManagerClient::Observer { public: @@ -27,8 +27,6 @@ static void MaybeExitFullscreen(); - void MaybeShowAlert(); - static void RegisterProfilePrefs(PrefRegistrySimple* registry); private: @@ -41,9 +39,11 @@ void LidEventReceived(chromeos::PowerManagerClient::LidState state, base::TimeTicks timestamp) override; + void MaybeShowNotification(); + const SessionControllerImpl* const session_controller_; - std::unique_ptr<FullscreenAlertBubble> bubble_; + std::unique_ptr<FullscreenNotificationBubble> bubble_; // Whether the screen brightness is low enough to make display dark. bool device_in_dark_ = false;
diff --git a/ash/session/fullscreen_notification_bubble.cc b/ash/session/fullscreen_notification_bubble.cc new file mode 100644 index 0000000..8c9a3bb0 --- /dev/null +++ b/ash/session/fullscreen_notification_bubble.cc
@@ -0,0 +1,120 @@ +// 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 "ash/session/fullscreen_notification_bubble.h" + +#include <memory> + +#include "ash/public/cpp/shell_window_ids.h" +#include "ash/shell.h" +#include "ash/wm/window_state.h" +#include "base/bind.h" +#include "base/location.h" +#include "base/time/time.h" +#include "components/fullscreen_control/subtle_notification_view.h" +#include "components/strings/grit/components_strings.h" +#include "ui/base/l10n/l10n_util.h" +#include "ui/gfx/geometry/insets.h" +#include "ui/strings/grit/ui_strings.h" +#include "ui/views/view.h" +#include "ui/views/widget/widget.h" + +namespace ash { + +namespace { + +constexpr base::TimeDelta kAutoHideDelay = base::Seconds(4); +constexpr int kNotificationViewTopDp = 45; + +} // namespace + +FullscreenNotificationBubble::FullscreenNotificationBubble() + : timer_(std::make_unique<base::OneShotTimer>()) { + // Create the notification view. + auto subtle_notification_view = std::make_unique<SubtleNotificationView>(); + view_ = subtle_notification_view.get(); + + // Set the text displayed in the bubble, including the keyboard key. + view_->UpdateContent(l10n_util::GetStringFUTF16( + IDS_FULLSCREEN_PRESS_ESC_TO_EXIT_FULLSCREEN, + l10n_util::GetStringUTF16(IDS_APP_FULLSCREEN_KEY))); + + // The view used to parent the bubble widget + const gfx::NativeView parent_view = + Shell::GetPrimaryRootWindow()->GetChildById( + kShellWindowId_SettingBubbleContainer); + + // Create the widget containing the SubtleNotificationView. + widget_ = SubtleNotificationView::CreatePopupWidget( + parent_view, std::move(subtle_notification_view)); + + gfx::Rect rect = GetBubbleBounds(); + widget_->SetBounds(rect); + widget_->SetZOrderLevel(ui::ZOrderLevel::kSecuritySurface); +} + +FullscreenNotificationBubble::~FullscreenNotificationBubble() = default; + +void FullscreenNotificationBubble::ShowForWindowState( + WindowState* window_state) { + // Early out if a WindowState is already tracked. The bubble should already be + // visible in this case. + if (window_state_) { + // `window_state` arg should match the tracked `window_state_`. + DCHECK_EQ(window_state_, window_state); + return; + } + + window_state_ = window_state; + window_state_->AddObserver(this); + + Show(); +} + +void FullscreenNotificationBubble::Show() { + if (widget_->IsVisible()) + return; + + widget_->Show(); + + timer_->Start(FROM_HERE, kAutoHideDelay, + base::BindOnce(&FullscreenNotificationBubble::Hide, + weak_ptr_factory_.GetWeakPtr())); +} + +void FullscreenNotificationBubble::Hide() { + if (window_state_) { + window_state_->RemoveObserver(this); + window_state_ = nullptr; + } + + if (!widget_->IsVisible()) + return; + + widget_->Hide(); +} + +void FullscreenNotificationBubble::OnPreWindowStateTypeChange( + WindowState* window_state, + chromeos::WindowStateType old_type) { + bool is_exiting_fullscreen = + old_type == chromeos::WindowStateType::kFullscreen && + !window_state->IsFullscreen(); + if (is_exiting_fullscreen) + Hide(); +} + +gfx::Rect FullscreenNotificationBubble::GetBubbleBounds() { + gfx::Size size(view_->GetPreferredSize()); + gfx::Rect widget_bounds = Shell::GetPrimaryRootWindow()->GetBoundsInScreen(); + int x = widget_bounds.x() + (widget_bounds.width() - size.width()) / 2; + + // |desired_top| is the top of the bubble area including the shadow. + const int desired_top = kNotificationViewTopDp - view_->GetInsets().top(); + const int y = widget_bounds.y() + desired_top; + + return gfx::Rect(gfx::Point(x, y), size); +} + +} // namespace ash
diff --git a/ash/session/fullscreen_notification_bubble.h b/ash/session/fullscreen_notification_bubble.h new file mode 100644 index 0000000..aa10815 --- /dev/null +++ b/ash/session/fullscreen_notification_bubble.h
@@ -0,0 +1,78 @@ +// 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 ASH_SESSION_FULLSCREEN_NOTIFICATION_BUBBLE_H_ +#define ASH_SESSION_FULLSCREEN_NOTIFICATION_BUBBLE_H_ + +#include <memory> + +#include "ash/ash_export.h" +#include "ash/wm/window_state.h" +#include "ash/wm/window_state_observer.h" +#include "base/memory/weak_ptr.h" + +class SubtleNotificationView; + +namespace base { +class OneShotTimer; +} + +namespace gfx { +class Rect; +} + +namespace views { +class Widget; +} + +namespace ash { + +// A notification bubble shown when the device returns from sleep, low +// brightness or the lock screen to remind the user that full screen mode is +// active. +class ASH_EXPORT FullscreenNotificationBubble : public WindowStateObserver { + public: + FullscreenNotificationBubble(); + + FullscreenNotificationBubble(const FullscreenNotificationBubble&) = delete; + FullscreenNotificationBubble& operator=(const FullscreenNotificationBubble&) = + delete; + + ~FullscreenNotificationBubble() override; + + void ShowForWindowState(WindowState* window_state); + + // Returns the underlying widget for testing purposes. + views::Widget* widget_for_test() { return widget_; } + + private: + // WindowStateObserver: + void OnPreWindowStateTypeChange(WindowState* window_state, + chromeos::WindowStateType old_type) override; + + void Show(); + void Hide(); + + gfx::Rect GetBubbleBounds(); + + // The contents of the widget. + SubtleNotificationView* view_ = nullptr; + + // The widget containing the bubble. + views::Widget* widget_ = nullptr; + + // The window state currently observed in order to hide the bubble if the user + // exits full screen mode before the timer is elapsed. It is set when the + // bubble is shown and reset when it is hidden. + WindowState* window_state_ = nullptr; + + // A timer to auto-dismiss the bubble after a short period of time. + std::unique_ptr<base::OneShotTimer> timer_; + + base::WeakPtrFactory<FullscreenNotificationBubble> weak_ptr_factory_{this}; +}; + +} // namespace ash + +#endif // ASH_SESSION_FULLSCREEN_NOTIFICATION_BUBBLE_H_
diff --git a/ash/session/fullscreen_notification_bubble_unittest.cc b/ash/session/fullscreen_notification_bubble_unittest.cc new file mode 100644 index 0000000..cff1fd3 --- /dev/null +++ b/ash/session/fullscreen_notification_bubble_unittest.cc
@@ -0,0 +1,90 @@ +// 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 "ash/session/fullscreen_notification_bubble.h" + +#include <memory> + +#include "ash/test/ash_test_base.h" +#include "base/time/time.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/aura/client/aura_constants.h" +#include "ui/aura/window.h" +#include "ui/views/widget/widget.h" + +namespace ash { +namespace { + +constexpr int kExpectedAutoHideDelayInSeconds = 4; + +class FullscreenNotificationBubbleTest : public AshTestBase { + public: + FullscreenNotificationBubbleTest() + : AshTestBase(base::test::TaskEnvironment::TimeSource::MOCK_TIME) {} + + FullscreenNotificationBubbleTest(const FullscreenNotificationBubbleTest&) = + delete; + FullscreenNotificationBubbleTest& operator=( + const FullscreenNotificationBubbleTest&) = delete; + + ~FullscreenNotificationBubbleTest() override {} + + // AshTestBase: + void SetUp() override { + AshTestBase::SetUp(); + + // Create a test window in full screen mode. + window_ = CreateTestWindow(); + window_->SetProperty(aura::client::kShowStateKey, + ui::SHOW_STATE_FULLSCREEN); + window_state_ = WindowState::Get(window_.get()); + + bubble_ = std::make_unique<FullscreenNotificationBubble>(); + } + + void TearDown() override { + window_.reset(); + bubble_.reset(); + AshTestBase::TearDown(); + } + + protected: + std::unique_ptr<aura::Window> window_; + std::unique_ptr<FullscreenNotificationBubble> bubble_; + + WindowState* window_state_ = nullptr; +}; + +TEST_F(FullscreenNotificationBubbleTest, AutoHideBubbleAfterDelay) { + views::Widget* widget = bubble_->widget_for_test(); + EXPECT_FALSE(widget->IsVisible()); + + bubble_->ShowForWindowState(window_state_); + EXPECT_TRUE(widget->IsVisible()); + + // The bubble is still visible if the timer has not yet elapsed. + task_environment()->FastForwardBy( + base::Seconds(kExpectedAutoHideDelayInSeconds - 1)); + EXPECT_TRUE(widget->IsVisible()); + + // The bubble is automatically hidden after the timer has elapsed. + task_environment()->FastForwardBy(base::Seconds(1)); + EXPECT_FALSE(widget->IsVisible()); +} + +TEST_F(FullscreenNotificationBubbleTest, HideBubbleOnExitFullscreen) { + views::Widget* widget = bubble_->widget_for_test(); + EXPECT_FALSE(widget->IsVisible()); + + bubble_->ShowForWindowState(window_state_); + EXPECT_TRUE(widget->IsVisible()); + + // The bubble is hidden early if the user exits full screen mode via full + // screen key. + PressAndReleaseKey(ui::VKEY_ZOOM); + EXPECT_FALSE(widget->IsVisible()); +} + +} // namespace +} // namespace ash
diff --git a/ash/strings/ash_strings_km.xtb b/ash/strings/ash_strings_km.xtb index 37b1a45..02bd4fa0 100644 --- a/ash/strings/ash_strings_km.xtb +++ b/ash/strings/ash_strings_km.xtb
@@ -280,6 +280,7 @@ <translation id="2942350706960889382">កែវពង្រីកដាក់ខាងលើបំផុត</translation> <translation id="2942516765047364088">ទីតាំងធ្នើ</translation> <translation id="2946119680249604491">បន្ថែមការភ្ជាប់</translation> +<translation id="2960314608273155470">មុខងារថត, លំនាំដើមគឺ <ph name="TYPE" /><ph name="SOURCE" />។ ចុច tab សម្រាប់ការរុករកដោយប្រើក្ដារចុច។</translation> <translation id="2961963223658824723">មានអ្វីមួយខុសប្រក្រតី សូមព្យាយាមម្ដងទៀតក្នុងពេលបន្តិចទៀត។</translation> <translation id="2963773877003373896">mod3</translation> <translation id="296762781903199866">មិនអាចទាញយកឯកសារនៃការនិយាយជាភាសា<ph name="LANGUAGE" />បានទេ</translation> @@ -886,6 +887,7 @@ <translation id="7042322267639375032">បង្រួមតំបន់ស្ថានភាព</translation> <translation id="7045033600005038336">ប្ដូរទម្រង់គំរូឬ?</translation> <translation id="7045595904618419789">ចាប់ផ្ដើមកែវពង្រីក</translation> +<translation id="7051244143160304048"><ph name="DEVICE_NAME" /> បានដាច់</translation> <translation id="7055381872777910864">ព</translation> <translation id="7055910611768509537">មិនបានប្រើប៊ិកជាងមួយសប្ដាហ៍ហើយ</translation> <translation id="7066646422045619941">បណ្តាញនេះត្រូវបានបិទដំណើរការដោយអ្នកគ្រប់គ្រងរបស់អ្នក។</translation> @@ -1079,6 +1081,7 @@ <translation id="8412677897383510995">បង្ហាញការកំណត់ផ្ទាំងអេក្រង់</translation> <translation id="8413272770729657668">ការថតចាប់ផ្ដើមក្នុងរយៈពេល 3, 2, 1</translation> <translation id="8416730306157376817"><ph name="BATTERY_PERCENTAGE" />% (ប្រអប់កាស)</translation> +<translation id="8421270167862077762"><ph name="UNAVAILABLE_APPS" /> មិនមាននៅលើឧបករណ៍នេះទេ។</translation> <translation id="8425213833346101688">ប្តូរ</translation> <translation id="8426708595819210923">សាយណ្ហសួស្ដី <ph name="GIVEN_NAME" /></translation> <translation id="8428213095426709021">ការកំណត់</translation>
diff --git a/ash/strings/ash_strings_tr.xtb b/ash/strings/ash_strings_tr.xtb index 8b861f6..a9b115e5 100644 --- a/ash/strings/ash_strings_tr.xtb +++ b/ash/strings/ash_strings_tr.xtb
@@ -280,6 +280,7 @@ <translation id="2942350706960889382">Yerleştirilmiş Büyüteç</translation> <translation id="2942516765047364088">Raf konumu</translation> <translation id="2946119680249604491">Bağlantı ekle</translation> +<translation id="2960314608273155470">Ekran Görüntüsü Alma Modu, varsayılan <ph name="SOURCE" /> <ph name="TYPE" />. Klavyeyle gezinmek için sekme tuşuna basın.</translation> <translation id="2961963223658824723">Bir sorun oluştu. Birkaç saniye sonra tekrar deneyin.</translation> <translation id="2963773877003373896">mod3</translation> <translation id="296762781903199866"><ph name="LANGUAGE" /> konuşma dosyaları indirilemedi</translation> @@ -887,6 +888,7 @@ <translation id="7042322267639375032">Durum alanını daralt</translation> <translation id="7045033600005038336">Şablon değiştirilsin mi?</translation> <translation id="7045595904618419789">Büyüteci başlat</translation> +<translation id="7051244143160304048"><ph name="DEVICE_NAME" /> cihazının bağlantısı kesildi</translation> <translation id="7055381872777910864">Ç</translation> <translation id="7055910611768509537">Ekran kalemi bir haftadan uzun süredir kullanılmamış</translation> <translation id="7066646422045619941">Bu ağ, yöneticiniz tarafından devre dışı bırakıldı.</translation> @@ -1080,6 +1082,7 @@ <translation id="8412677897383510995">Ekran ayarlarını göster</translation> <translation id="8413272770729657668">Kayıt başlıyor 3, 2, 1</translation> <translation id="8416730306157376817">%<ph name="BATTERY_PERCENTAGE" /> (Kılıf)</translation> +<translation id="8421270167862077762"><ph name="UNAVAILABLE_APPS" /> bu cihazda yok.</translation> <translation id="8425213833346101688">Değiştir</translation> <translation id="8426708595819210923">İyi akşamlar <ph name="GIVEN_NAME" />,</translation> <translation id="8428213095426709021">Ayarlar</translation>
diff --git a/ash/system/time/calendar_unittest_utils.h b/ash/system/time/calendar_unittest_utils.h index 538e155..ec7f405c 100644 --- a/ash/system/time/calendar_unittest_utils.h +++ b/ash/system/time/calendar_unittest_utils.h
@@ -14,10 +14,6 @@ namespace calendar_test_utils { -// A duration to let the animation finish and pass the cool down duration in -// tests. -constexpr base::TimeDelta kAnimationSettleDownDuration = base::Seconds(3); - // Creates a `google_apis::calendar::CalendarEvent` for testing. std::unique_ptr<google_apis::calendar::CalendarEvent> CreateEvent( const char* id,
diff --git a/ash/system/time/calendar_utils.h b/ash/system/time/calendar_utils.h index 5e15363..ca5b638 100644 --- a/ash/system/time/calendar_utils.h +++ b/ash/system/time/calendar_utils.h
@@ -32,13 +32,6 @@ constexpr gfx::Insets kDateCellInsets{kDateVerticalPadding, kDateHorizontalPadding}; -// Duration of opacity animation for visibility changes. -constexpr base::TimeDelta kAnimationDurationForVisibility = - base::Milliseconds(100); - -// Duration of moving animation. -constexpr base::TimeDelta kAnimationDurationForMoving = base::Milliseconds(300); - // Checks if the `selected_date` is local time today. bool IsToday(const base::Time::Exploded& selected_date);
diff --git a/ash/system/time/calendar_view.cc b/ash/system/time/calendar_view.cc index b27b442..0f83c8e 100644 --- a/ash/system/time/calendar_view.cc +++ b/ash/system/time/calendar_view.cc
@@ -57,6 +57,13 @@ // we wait before fetchiung more events. constexpr base::TimeDelta kScrollingSettledTimeout = base::Milliseconds(100); +// Duration of opacity animation for visibility changes. +constexpr base::TimeDelta kAnimationDurationForVisibility = + base::Milliseconds(100); + +// Duration of moving animation. +constexpr base::TimeDelta kAnimationDurationForMoving = base::Milliseconds(300); + // Duration of the delay for modifying opacity. constexpr base::TimeDelta kDelayVisibilityAnimationDuration = base::Milliseconds(200); @@ -240,26 +247,16 @@ kScrollingSettledTimeout, base::BindRepeating(&CalendarView::OnScrollingSettledTimerFired, base::Unretained(this))), - header_animation_restart_timer_( - FROM_HERE, - kAnimationDisablingTimeout, - base::BindRepeating( - [](CalendarView* calendar_view) { - if (!calendar_view) - return; - calendar_view->set_should_header_animate(true); - }, - base::Unretained(this))), - months_animation_restart_timer_( - FROM_HERE, - kAnimationDisablingTimeout, - base::BindRepeating( - [](CalendarView* calendar_view) { - if (!calendar_view) - return; - calendar_view->set_should_months_animate(true); - }, - base::Unretained(this))) { + animation_restart_timer_(FROM_HERE, + kAnimationDisablingTimeout, + base::BindRepeating( + [](CalendarView* calendar_view) { + if (!calendar_view) + return; + calendar_view->should_show_animation_ = + true; + }, + base::Unretained(this))) { CreateTitleRow(IDS_ASH_CALENDAR_TITLE); // Add the header. @@ -452,8 +449,8 @@ header_->layer()->SetOpacity(1.0f); header_->layer()->SetTransform(gfx::Transform()); scrolling_settled_timer_.Reset(); - if (!should_header_animate_) - header_animation_restart_timer_.Reset(); + if (!should_show_animation_) + animation_restart_timer_.Reset(); } void CalendarView::RestoreMonthStatus(bool is_scrolling_up) { @@ -476,8 +473,8 @@ next_month_->layer()->SetOpacity(1.0f); next_month_->layer()->SetTransform(gfx::Transform()); } - if (!should_months_animate_) - months_animation_restart_timer_.Reset(); + if (!should_show_animation_) + animation_restart_timer_.Reset(); } void CalendarView::ScrollToToday() { @@ -591,54 +588,95 @@ } void CalendarView::OnMonthChanged(const base::Time::Exploded current_month) { - if (!should_header_animate_) { + if (!should_show_animation_) { UpdateHeaders(); RestoreHeadersStatus(); return; } - - header_->layer()->SetTransform(gfx::Transform()); - header_->layer()->SetOpacity(0.0f); - UpdateHeaders(); - const int header_height = header_->GetPreferredSize().height(); - gfx::Vector2dF moving_location = gfx::Vector2dF( + const gfx::Vector2dF moving_location = gfx::Vector2dF( 0, calendar_view_controller_->was_on_later_month() ? -header_height / 2 : header_height / 2); - gfx::Transform initial_state = gfx::TransformAboutPivot( + gfx::Transform header_moving = gfx::TransformAboutPivot( header_->GetLocalBounds().CenterPoint(), gfx::Transform()); - initial_state.Translate(moving_location); - set_should_header_animate(false); + header_moving.Translate(moving_location); + + should_show_animation_ = false; + + auto on_animation_aborted = [](base::WeakPtr<CalendarView> calendar_view) { + if (!calendar_view) + return; + calendar_view->UpdateHeaders(); + calendar_view->RestoreHeadersStatus(); + }; + + auto on_animation_ended = [](base::WeakPtr<CalendarView> calendar_view) { + if (!calendar_view) + return; + DCHECK(!calendar_view->header_->layer()->GetAnimator()->is_animating()); + + calendar_view->header_->layer()->SetTransform(gfx::Transform()); + calendar_view->header_->layer()->SetOpacity(0.0f); + calendar_view->UpdateHeaders(); + const int header_height = + calendar_view->header_->GetPreferredSize().height(); + gfx::Vector2dF moving_location = gfx::Vector2dF( + 0, calendar_view->calendar_view_controller_->was_on_later_month() + ? header_height / 2 + : -header_height / 2); + gfx::Transform initial_state = gfx::TransformAboutPivot( + calendar_view->header_->GetLocalBounds().CenterPoint(), + gfx::Transform()); + initial_state.Translate(moving_location); + views::AnimationBuilder() + .SetPreemptionStrategy( + ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET) + .OnEnded(base::BindOnce( + [](base::WeakPtr<CalendarView> calendar_view) { + if (!calendar_view) + return; + calendar_view->should_show_animation_ = true; + calendar_view->scrolling_settled_timer_.Reset(); + }, + calendar_view->weak_factory_.GetWeakPtr())) + .OnAborted(base::BindOnce( + [](base::WeakPtr<CalendarView> calendar_view) { + if (!calendar_view) + return; + calendar_view->UpdateHeaders(); + calendar_view->RestoreHeadersStatus(); + }, + calendar_view->weak_factory_.GetWeakPtr())) + .Once() + .SetDuration(base::TimeDelta()) + .SetTransform(calendar_view->header_, std::move(initial_state)) + .Then() + .SetDuration(kAnimationDurationForMoving) + .SetTransform(calendar_view->header_, gfx::Transform(), + gfx::Tween::EASE_OUT_2) + .At(base::Milliseconds(0)) + .SetDuration(kDelayVisibilityAnimationDuration) + .Then() + .SetDuration(kAnimationDurationForVisibility) + .SetOpacity(calendar_view->header_, 1.0); + calendar_view->should_show_animation_ = true; + calendar_view->scrolling_settled_timer_.Reset(); + }; views::AnimationBuilder() .SetPreemptionStrategy( ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET) - .OnEnded(base::BindOnce( - [](base::WeakPtr<CalendarView> calendar_view) { - if (!calendar_view) - return; - calendar_view->set_should_header_animate(true); - calendar_view->reset_scrolling_settled_timer(); - }, - weak_factory_.GetWeakPtr())) - .OnAborted(base::BindOnce( - [](base::WeakPtr<CalendarView> calendar_view) { - if (!calendar_view) - return; - calendar_view->UpdateHeaders(); - calendar_view->RestoreHeadersStatus(); - }, - weak_factory_.GetWeakPtr())) + .OnEnded(base::BindOnce(on_animation_ended, weak_factory_.GetWeakPtr())) + .OnAborted( + base::BindOnce(on_animation_aborted, weak_factory_.GetWeakPtr())) .Once() - .SetTransform(header_, std::move(initial_state)) - .Then() - .SetDuration(calendar_utils::kAnimationDurationForMoving) - .SetTransform(header_, gfx::Transform(), gfx::Tween::EASE_OUT_2) + .SetDuration(kAnimationDurationForMoving) + .SetTransform(header_, std::move(header_moving), gfx::Tween::EASE_OUT_2) .At(base::Milliseconds(0)) .SetDuration(kDelayVisibilityAnimationDuration) .Then() - .SetDuration(calendar_utils::kAnimationDurationForVisibility) - .SetOpacity(header_, 1.0); + .SetDuration(kAnimationDurationForVisibility) + .SetOpacity(header_, 0.0); } void CalendarView::OnEventsFetched( @@ -776,7 +814,7 @@ // If there's already an existing animation, restores each layer's visibility // and position. - if (!should_months_animate_) { + if (!should_show_animation_) { if (is_scrolling_up) { ScrollUpOneMonthAndAutoScroll(); return; @@ -785,15 +823,13 @@ return; } - set_should_months_animate(false); - gfx::Vector2dF moving_up_location = gfx::Vector2dF( - 0, previous_month_->GetPreferredSize().height() + - current_label_->GetPreferredSize().height() + - (scroll_view_->GetVisibleRect().y() - current_month_->y())); - gfx::Vector2dF moving_down_location = gfx::Vector2dF( - 0, -current_month_->GetPreferredSize().height() - - next_label_->GetPreferredSize().height() + - (scroll_view_->GetVisibleRect().y() - current_month_->y())); + should_show_animation_ = false; + gfx::Vector2dF moving_up_location = + gfx::Vector2dF(0, previous_month_->GetPreferredSize().height() + + current_label_->GetPreferredSize().height()); + gfx::Vector2dF moving_down_location = + gfx::Vector2dF(0, -current_month_->GetPreferredSize().height() - + next_label_->GetPreferredSize().height()); gfx::Transform current_month_moving = gfx::TransformAboutPivot( current_month_->GetLocalBounds().CenterPoint(), gfx::Transform()); @@ -813,14 +849,6 @@ next_month_->GetLocalBounds().CenterPoint(), gfx::Transform()); next_month_moving.Translate(moving_down_location); - const int header_height = header_->GetPreferredSize().height(); - const gfx::Vector2dF moving_location = gfx::Vector2dF( - 0, calendar_view_controller_->was_on_later_month() ? header_height / 2 - : -header_height / 2); - gfx::Transform header_moving = gfx::TransformAboutPivot( - header_->GetLocalBounds().CenterPoint(), gfx::Transform()); - header_moving.Translate(moving_location); - views::AnimationBuilder() .SetPreemptionStrategy( ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET) @@ -828,7 +856,7 @@ [](base::WeakPtr<CalendarView> calendar_view, bool is_scrolling_up) { if (!calendar_view) return; - calendar_view->set_should_months_animate(true); + calendar_view->should_show_animation_ = true; is_scrolling_up ? calendar_view->ScrollUpOneMonthAndAutoScroll() : calendar_view->ScrollDownOneMonthAndAutoScroll(); }, @@ -842,7 +870,7 @@ }, weak_factory_.GetWeakPtr(), is_scrolling_up)) .Once() - .SetDuration(calendar_utils::kAnimationDurationForMoving * 2) + .SetDuration(kAnimationDurationForMoving * 2) .SetTransform(current_month_, std::move(current_month_moving), gfx::Tween::EASE_OUT_2) .SetTransform( @@ -853,14 +881,7 @@ std::move(is_scrolling_up ? previous_month_moving : next_month_moving), gfx::Tween::EASE_OUT_2) - .At(calendar_utils::kAnimationDurationForMoving) - .SetDuration(calendar_utils::kAnimationDurationForMoving) - .SetTransform(header_, std::move(header_moving), gfx::Tween::EASE_OUT_2) - .At(calendar_utils::kAnimationDurationForMoving) - .SetDuration(kDelayVisibilityAnimationDuration) - .Then() - .SetDuration(calendar_utils::kAnimationDurationForVisibility) - .SetOpacity(header_, 0.0); + .SetOpacity(current_month_, 0.0); } void CalendarView::ScrollOneRowWithAnimation(bool is_scrolling_up) { @@ -1021,8 +1042,6 @@ if (is_resetting_scroll_) return; - base::AutoReset<bool> disable_header_animation(&should_header_animate_, - false); // Scrolls to the previous month if the current label is moving down and // passing the top of the visible area. if (scroll_view_->GetVisibleRect().y() <= current_label_->y()) {
diff --git a/ash/system/time/calendar_view.h b/ash/system/time/calendar_view.h index 3de16a9..74a4593 100644 --- a/ash/system/time/calendar_view.h +++ b/ash/system/time/calendar_view.h
@@ -153,7 +153,7 @@ void UpdateHeaders(); // Resets the `header_`'s opacity and position. Also resets - // `scrolling_settled_timer_` and `header_animation_restart_timer_`. + // `scrolling_settled_timer_` and `animation_restart_timer_`. void RestoreHeadersStatus(); // Resets the the month views' opacity and position. In case the animation is @@ -182,17 +182,6 @@ std::unique_ptr<CalendarViewController> calendar_view_controller_; - // Setters for animation flags. - void set_should_header_animate(bool should_animate) { - should_header_animate_ = should_animate; - } - void set_should_months_animate(bool should_animate) { - should_months_animate_ = should_animate; - } - - // Reset `scrolling_settled_timer_`. - void reset_scrolling_settled_timer() { scrolling_settled_timer_.Reset(); } - // The content of the `scroll_view_`, which carries months and month labels. // Owned by `CalendarView`. views::View* content_view_ = nullptr; @@ -216,24 +205,18 @@ // don't need to check if we need to update the month or not. bool is_resetting_scroll_ = false; - // It's true if the header should animate, but false when it is currently - // animating, or header changing from mouse scroll (not from the buttons) or + // It's true if it should animate, but false when it is currently animating or // cooling down from the last animation. - bool should_header_animate_ = true; - - // It's true if the month views should animate, but false when it is currently - // animating, or cooling down from the last animation. - bool should_months_animate_ = true; + bool should_show_animation_ = true; // Timer that fires when we've "settled" on, i.e. finished scrolling to, a // currently-visible month base::RetainingOneShotTimer scrolling_settled_timer_; - // Timers that enable the updating month/header animations. When the month - // keeps getting changed, the animation will be disabled and the cool-down - // duration is `kAnimationDisablingTimeout` ms to enable the next animation. - base::RetainingOneShotTimer header_animation_restart_timer_; - base::RetainingOneShotTimer months_animation_restart_timer_; + // Timer that enables the updating month animation. When the month keeps + // getting changed, the animation will be disabled and the cool-down duration + // is `kAnimationDisablingTimeout` ms to enable the next animation. + base::RetainingOneShotTimer animation_restart_timer_; base::CallbackListSubscription on_contents_scrolled_subscription_; base::ScopedObservation<CalendarViewController,
diff --git a/ash/system/time/calendar_view_unittest.cc b/ash/system/time/calendar_view_unittest.cc index 9b38674..2fb54d4 100644 --- a/ash/system/time/calendar_view_unittest.cc +++ b/ash/system/time/calendar_view_unittest.cc
@@ -7,8 +7,6 @@ #include "ash/shell.h" #include "ash/style/icon_button.h" #include "ash/system/time/calendar_month_view.h" -#include "ash/system/time/calendar_unittest_utils.h" -#include "ash/system/time/calendar_utils.h" #include "ash/system/time/calendar_view_controller.h" #include "ash/system/tray/detailed_view_delegate.h" #include "ash/test/ash_test_base.h" @@ -599,31 +597,11 @@ delegate_.get(), tray_controller_.get())); } - void UpdateMonth(base::Time date) { - calendar_view_->calendar_view_controller()->UpdateMonth(date); - calendar_view_->SetMonthViews(); - } - CalendarView* calendar_view() { return calendar_view_; } views::Label* month_header() { return calendar_view_->header_->header_; } views::Label* header_year() { return calendar_view_->header_->header_year_; } CalendarHeaderView* header() { return calendar_view_->header_; } - CalendarMonthView* current_month() { return calendar_view_->current_month_; } - CalendarMonthView* previous_month() { - return calendar_view_->previous_month_; - } - CalendarMonthView* next_month() { return calendar_view_->next_month_; } - views::View* previous_label() { return calendar_view_->previous_label_; } - views::View* current_label() { return calendar_view_->current_label_; } - views::View* next_label() { return calendar_view_->next_label_; } - - void ScrollUpOneMonth() { - calendar_view_->ScrollOneMonthWithAnimation(/*is_scrolling_up=*/true); - } - void ScrollDownOneMonth() { - calendar_view_->ScrollOneMonthWithAnimation(/*is_scrolling_up=*/false); - } private: std::unique_ptr<views::Widget> widget_; @@ -635,7 +613,6 @@ static base::Time fake_time_; }; -// The header should show the new header with animation when there's an update. TEST_F(CalendarViewAnimationTest, HeaderAnimation) { ui::ScopedAnimationDurationScaleMode test_duration_mode( ui::ScopedAnimationDurationScaleMode::NORMAL_DURATION); @@ -646,11 +623,9 @@ CreateCalendarView(); // Gives it a duration to let the animation finish and pass the cool down // duration. The same for the other 3s duration. - task_environment()->FastForwardBy( - calendar_test_utils::kAnimationSettleDownDuration); + task_environment()->FastForwardBy(base::Seconds(3)); calendar_view()->calendar_view_controller()->UpdateMonth(date); - task_environment()->FastForwardBy( - calendar_test_utils::kAnimationSettleDownDuration); + task_environment()->FastForwardBy(base::Seconds(3)); EXPECT_EQ(u"October", month_header()->GetText()); EXPECT_EQ(u"2021", header_year()->GetText()); @@ -659,11 +634,18 @@ calendar_view()->calendar_view_controller()->UpdateMonth(date + base::Days(33)); + // The Opacity is updated from 1.0f to 0.0f after 200ms delay duration. + EXPECT_EQ(1.0f, header()->layer()->opacity()); + task_environment()->FastForwardBy(base::Milliseconds(100)); + EXPECT_TRUE(header()->layer()->GetAnimator()->is_animating()); + EXPECT_EQ(1.0f, header()->layer()->opacity()); + task_environment()->FastForwardBy(base::Milliseconds(200)); + // To prevent flakiness, fast forward until the header changes (see // crbug/1270161). The second animation starts after the header is updated to // the new month. while (u"November" != month_header()->GetText()) { - task_environment()->FastForwardBy(base::Milliseconds(30)); + task_environment()->FastForwardBy(base::Milliseconds(80)); } // The opacity is updated to 0 after the first animation ends. EXPECT_EQ(0.0f, header()->layer()->opacity()); @@ -674,13 +656,11 @@ EXPECT_EQ(u"2021", header_year()->GetText()); // The Opacity is back from 0.0f to 1.0 after 200ms delay duration. - task_environment()->FastForwardBy( - calendar_utils::kAnimationDurationForVisibility); + task_environment()->FastForwardBy(base::Milliseconds(100)); EXPECT_EQ(0.0f, header()->layer()->opacity()); // Gives it a duration to let the animation finish. - task_environment()->FastForwardBy( - calendar_test_utils::kAnimationSettleDownDuration); + task_environment()->FastForwardBy(base::Milliseconds(1000)); EXPECT_EQ(1.0f, header()->layer()->opacity()); // The header is still with the updated new month after all animation @@ -689,109 +669,4 @@ EXPECT_EQ(u"2021", header_year()->GetText()); } -// The month views and header should animate when scrolling up or down. -TEST_F(CalendarViewAnimationTest, MonthAndHeaderAnimation) { - ui::ScopedAnimationDurationScaleMode test_duration_mode( - ui::ScopedAnimationDurationScaleMode::NORMAL_DURATION); - - base::Time date; - ASSERT_TRUE(base::Time::FromString("24 Oct 2021 10:00 GMT", &date)); - - CreateCalendarView(); - // Gives it a duration to let the animation finish and pass the cool down - // duration. - task_environment()->FastForwardBy( - calendar_test_utils::kAnimationSettleDownDuration); - UpdateMonth(date); - task_environment()->FastForwardBy( - calendar_test_utils::kAnimationSettleDownDuration); - - EXPECT_EQ(u"October", month_header()->GetText()); - EXPECT_EQ(u"2021", header_year()->GetText()); - - // Scrolls to the next month. - ScrollDownOneMonth(); - - // If scrolls down, the month views that will animate are `current_month_`, - // `next_month_` and 'next_label_`. - EXPECT_EQ(1.0f, header()->layer()->opacity()); - task_environment()->FastForwardBy( - calendar_utils::kAnimationDurationForVisibility); - EXPECT_TRUE(current_month()->layer()->GetAnimator()->is_animating()); - EXPECT_TRUE(next_month()->layer()->GetAnimator()->is_animating()); - EXPECT_TRUE(next_label()->layer()->GetAnimator()->is_animating()); - EXPECT_FALSE(previous_month()->layer()->GetAnimator()->is_animating()); - EXPECT_FALSE(previous_label()->layer()->GetAnimator()->is_animating()); - EXPECT_FALSE(current_label()->layer()->GetAnimator()->is_animating()); - EXPECT_EQ(u"October", month_header()->GetText()); - EXPECT_EQ(u"2021", header_year()->GetText()); - - // The header animation starts from 300ms. Its Opacity is updated from 1.0f to - // 0.0f after 300+200ms delay duration. - task_environment()->FastForwardBy( - calendar_utils::kAnimationDurationForMoving); - EXPECT_TRUE(header()->layer()->GetAnimator()->is_animating()); - EXPECT_EQ(1.0f, header()->layer()->opacity()); - - // To prevent flakiness, fast forward until the header changes (see - // crbug/1270161). The second animation starts after the header is updated to - // the new month. - while (u"November" != month_header()->GetText()) { - task_environment()->FastForwardBy(base::Milliseconds(30)); - } - - // The opacity is updated to 0 after the first animation ends. - EXPECT_EQ(0.0f, header()->layer()->opacity()); - - // Now the header is updated to the new month and year before it starts - // showing up. - EXPECT_EQ(u"November", month_header()->GetText()); - EXPECT_EQ(u"2021", header_year()->GetText()); - - // The Opacity is back from 0.0f to 1.0 after 200ms delay duration. - task_environment()->FastForwardBy( - calendar_utils::kAnimationDurationForVisibility); - EXPECT_EQ(0.0f, header()->layer()->opacity()); - - // Gives it a duration to let the animation finish. - task_environment()->FastForwardBy( - calendar_test_utils::kAnimationSettleDownDuration); - EXPECT_EQ(1.0f, header()->layer()->opacity()); - - // The header is still with the updated new month after all animation - // finished. - EXPECT_EQ(u"November", month_header()->GetText()); - EXPECT_EQ(u"2021", header_year()->GetText()); - - // Scrolls to the previous month. - ScrollUpOneMonth(); - - // If scrolls up, the month views that will animate are `current_month_`, - // `previous_month_` and 'current_label_`. - EXPECT_EQ(1.0f, header()->layer()->opacity()); - task_environment()->FastForwardBy( - calendar_utils::kAnimationDurationForVisibility); - EXPECT_TRUE(current_month()->layer()->GetAnimator()->is_animating()); - EXPECT_TRUE(current_label()->layer()->GetAnimator()->is_animating()); - EXPECT_TRUE(previous_month()->layer()->GetAnimator()->is_animating()); - EXPECT_FALSE(next_month()->layer()->GetAnimator()->is_animating()); - EXPECT_FALSE(previous_label()->layer()->GetAnimator()->is_animating()); - EXPECT_FALSE(next_label()->layer()->GetAnimator()->is_animating()); - EXPECT_EQ(u"November", month_header()->GetText()); - EXPECT_EQ(u"2021", header_year()->GetText()); - - // The header animation starts from 300ms. Its Opacity is updated from 1.0f to - // 0.0f after 300+200ms delay duration. - task_environment()->FastForwardBy( - calendar_utils::kAnimationDurationForMoving); - EXPECT_TRUE(header()->layer()->GetAnimator()->is_animating()); - EXPECT_EQ(1.0f, header()->layer()->opacity()); - - task_environment()->FastForwardBy( - calendar_test_utils::kAnimationSettleDownDuration); - - EXPECT_EQ(u"October", month_header()->GetText()); - EXPECT_EQ(u"2021", header_year()->GetText()); -} - } // namespace ash
diff --git a/ash/webui/BUILD.gn b/ash/webui/BUILD.gn index f63af95..a9526e4 100644 --- a/ash/webui/BUILD.gn +++ b/ash/webui/BUILD.gn
@@ -61,7 +61,6 @@ group("closure_compile") { testonly = true deps = [ - "//ash/webui/camera_app_ui:closure_compile", "//ash/webui/connectivity_diagnostics:closure_compile", "//ash/webui/diagnostics_ui:closure_compile", "//ash/webui/eche_app_ui:closure_compile",
diff --git a/ash/webui/camera_app_ui/BUILD.gn b/ash/webui/camera_app_ui/BUILD.gn index da705e1..547998d 100644 --- a/ash/webui/camera_app_ui/BUILD.gn +++ b/ash/webui/camera_app_ui/BUILD.gn
@@ -9,7 +9,6 @@ import("//ash/webui/camera_app_ui/resources/views/views.gni") import("//build/config/chromeos/ui_mode.gni") import("//mojo/public/tools/bindings/mojom.gni") -import("//third_party/closure_compiler/compile_js.gni") import("//ui/webui/resources/tools/generate_grd.gni") assert(is_chromeos_ash, "Camera App is ash-chrome only") @@ -103,11 +102,6 @@ ] } -group("closure_compile") { - testonly = true - deps = [ "resources/js:closure_compile" ] -} - mojom("mojo_bindings") { sources = [ "camera_app_helper.mojom" ] @@ -129,6 +123,7 @@ deps = [ ":build_generated_grdp", ":build_mojo_grdp", + "resources/js:build_ts", ] grd_prefix = cca_grd_prefix @@ -137,6 +132,8 @@ mojo_grdp_file, generated_grdp_file, ] + + manifest_files = [ "$target_gen_dir/resources/js/tsconfig.manifest" ] input_files_base_dir = rebase_path("resources", "//") input_files = [] @@ -159,9 +156,6 @@ } # JS Files. - foreach(js, compile_js_files) { - input_files += [ "js/$js" ] - } foreach(js, no_compile_js_files) { input_files += [ "js/$js" ] } @@ -192,25 +186,18 @@ ] grd_prefix = "ash_camera_app_js_mojo" - input_files_base_dir = - rebase_path("$root_gen_dir/mojom-webui", root_build_dir) + input_files_base_dir = rebase_path("$root_gen_dir/", root_build_dir) - # TODO(pihsun): Move all imports of mojom types into the mojo/ folder, and - # re-export needed types, so we don't need to have long import paths - # scattered in multiple files. - # Since closure compiler don't support re-exported types - # (https://github.com/google/closure-compiler/issues/2257), this clean-up is - # blocked by migration to TypeScript. input_files = [ - "ash/components/arc/mojom/camera_intent.mojom-webui.js", - "ash/webui/camera_app_ui/camera_app_helper.mojom-webui.js", - "chromeos/services/machine_learning/public/mojom/document_scanner_param_types.mojom-webui.js", - "media/capture/mojom/image_capture.mojom-webui.js", - "media/capture/video/chromeos/mojom/camera3.mojom-webui.js", - "media/capture/video/chromeos/mojom/camera_app.mojom-webui.js", - "media/capture/video/chromeos/mojom/camera_common.mojom-webui.js", - "media/capture/video/chromeos/mojom/camera_metadata.mojom-webui.js", - "media/capture/video/chromeos/mojom/camera_metadata_tags.mojom-webui.js", + "mojom-webui/ash/components/arc/mojom/camera_intent.mojom-webui.js", + "mojom-webui/ash/webui/camera_app_ui/camera_app_helper.mojom-webui.js", + "mojom-webui/chromeos/services/machine_learning/public/mojom/document_scanner_param_types.mojom-webui.js", + "mojom-webui/media/capture/mojom/image_capture.mojom-webui.js", + "mojom-webui/media/capture/video/chromeos/mojom/camera3.mojom-webui.js", + "mojom-webui/media/capture/video/chromeos/mojom/camera_app.mojom-webui.js", + "mojom-webui/media/capture/video/chromeos/mojom/camera_common.mojom-webui.js", + "mojom-webui/media/capture/video/chromeos/mojom/camera_metadata.mojom-webui.js", + "mojom-webui/media/capture/video/chromeos/mojom/camera_metadata_tags.mojom-webui.js", ] out_grd = mojo_grdp_file
diff --git a/ash/webui/camera_app_ui/resources/.eslintrc.js b/ash/webui/camera_app_ui/resources/.eslintrc.js index fc1a430..aedb5fb5 100644 --- a/ash/webui/camera_app_ui/resources/.eslintrc.js +++ b/ash/webui/camera_app_ui/resources/.eslintrc.js
@@ -422,5 +422,24 @@ 'plugins': ['@typescript-eslint'], 'parser': `${typescriptEslintDir}/parser`, 'extends': ['eslint:recommended', 'plugin:@typescript-eslint/recommended'], + 'rules': { + // TODO(pihsun): We should use eslint-plugin-jsdoc for jsdoc in + // TypeScript, since the Google TypeScript style guide states that + // redundant type or trivial arguments for params and returns can be + // omitted in jsdoc for TypeScript, but eslint builtin valid-jsdoc rule + // doesn't have fine-grained control to disable those checks (and is also + // deprecated). + // (TypeScript doesn't yet support getting types from jsdoc, + // https://github.com/microsoft/TypeScript/issues/42048) + 'valid-jsdoc': 'off', + // TODO(pihsun): Currently there are many existing js files that have + // jsdoc which only contains type information and nothing else, which is + // all removed while converting to ts. Disabling the requirement for + // jsdoc for now. Note that the style guide suggest to document all + // properties and methods whose purpose is not immediately obvious from + // their name, which means that we should be able to skip jsdoc for some + // of the constructors and trivial structs. + 'require-jsdoc': 'off', + }, }], };
diff --git a/ash/webui/camera_app_ui/resources/.gitignore b/ash/webui/camera_app_ui/resources/.gitignore new file mode 100644 index 0000000..f299549 --- /dev/null +++ b/ash/webui/camera_app_ui/resources/.gitignore
@@ -0,0 +1,3 @@ +# tsconfig.json is generated by `cca.py tsc <board>`. Since the file contains +# reference to mojom ts types from the generated folder, it's excluded here. +tsconfig.json
diff --git a/ash/webui/camera_app_ui/resources/js/BUILD.gn b/ash/webui/camera_app_ui/resources/js/BUILD.gn index 860ba86b..aac1096 100644 --- a/ash/webui/camera_app_ui/resources/js/BUILD.gn +++ b/ash/webui/camera_app_ui/resources/js/BUILD.gn
@@ -7,31 +7,11 @@ import("//build/config/chromeos/ui_mode.gni") import("//third_party/closure_compiler/closure_args.gni") import("//third_party/closure_compiler/compile_js.gni") +import("//tools/typescript/ts_definitions.gni") +import("//tools/typescript/ts_library.gni") assert(is_chromeos_ash) -js_type_check("closure_compile") { - closure_flags = - default_closure_args + mojom_js_args + [ - "language_in=ECMASCRIPT_2020", - "language_out=ECMASCRIPT_2020", - "jscomp_error=strictCheckTypes", - "conformance_configs=" + - rebase_path("externs/conformance_config.textproto", root_build_dir), - "jscomp_error=conformanceViolations", - "hide_warnings_for=mojo/public/js/", - "hide_warnings_for=gen/", - "hide_warnings_for=js/lib/", - - # For all js module in source tree. - "js_module_root=" + rebase_path(".", root_build_dir), - - # For all dynamic generated js module. - "js_module_root=" + rebase_path(target_gen_dir, root_build_dir), - ] - deps = [ ":compile_resources" ] -} - action("gen_preload_images_js") { script = "../utils/gen_preload_images_js.py" inputs = [] @@ -54,26 +34,103 @@ deps = [] } -js_library("compile_resources") { +copy("copy_sources") { sources = compile_js_files + outputs = [ "{{source_gen_dir}}/{{source_file_part}}" ] +} - deps = [ +template("copy_generated_mojo") { + forward_variables_from(invoker, [ "deps" ]) + + copy(target_name) { + dir = invoker.dir + sources = [] + foreach(file, invoker.mojo_files) { + sources += [ "$root_gen_dir/mojom-webui/$dir/$file" ] + } + outputs = [ "$target_gen_dir/mojom-webui/$dir/{{source_file_part}}" ] + } +} + +copy_generated_mojo("copy_mojo_camera_app_ui") { + deps = [ "//ash/webui/camera_app_ui:mojo_bindings_js" ] + dir = "ash/webui/camera_app_ui" + mojo_files = [ "camera_app_helper.mojom-webui.js" ] +} + +copy_generated_mojo("copy_mojo_arc") { + deps = [ "//ash/components/arc/mojom:camera_intent_js" ] + dir = "ash/components/arc/mojom" + mojo_files = [ "camera_intent.mojom-webui.js" ] +} + +copy_generated_mojo("copy_mojo_image_caputre") { + deps = [ "//media/capture/mojom:image_capture_js" ] + dir = "media/capture/mojom" + mojo_files = [ "/image_capture.mojom-webui.js" ] +} + +copy_generated_mojo("copy_mojo_camera_common") { + deps = [ "//media/capture/video/chromeos/mojom:cros_camera_common_js" ] + dir = "media/capture/video/chromeos/mojom" + mojo_files = [ + "camera3.mojom-webui.js", + "camera_app.mojom-webui.js", + "camera_common.mojom-webui.js", + "camera_metadata.mojom-webui.js", + "camera_metadata_tags.mojom-webui.js", + ] +} + +copy_generated_mojo("copy_mojo_document_scanner") { + deps = [ "//chromeos/services/machine_learning/public/mojom:document_scanner_param_types_js" ] + dir = "chromeos/services/machine_learning/public/mojom" + mojo_files = [ "document_scanner_param_types.mojom-webui.js" ] +} + +ts_definitions("build_mojo_dts") { + extra_deps = [ + ":copy_mojo_arc", + ":copy_mojo_camera_app_ui", + ":copy_mojo_camera_common", + ":copy_mojo_document_scanner", + ":copy_mojo_image_caputre", + ] + + root_dir = target_gen_dir + out_dir = root_dir + + js_files = [] + foreach(dep, extra_deps) { + js_files += rebase_path(get_target_outputs(dep), root_dir, root_build_dir) + } +} + +ts_library("build_ts") { + deps = [ "//ui/webui/resources/mojo:library" ] + + tsconfig_base = "../tsconfig_base.json" + + root_dir = "$target_gen_dir/.." + out_dir = "$target_gen_dir/tsc" + + extra_deps = [ + ":build_mojo_dts", + ":copy_sources", ":preload_images", - "//ash/components/arc/mojom:camera_intent_webui_js", - "//ash/webui/camera_app_ui:mojo_bindings_webui_js", - "//media/capture/mojom:image_capture_webui_js", - "//media/capture/video/chromeos/mojom:cros_camera_common_webui_js", ] - externs_list = [ - "$externs_path/chrome_extensions.js", - "$externs_path/crash_report_private.js", - "$externs_path/file_manager_private.js", - "$externs_path/file_system_provider.js", - "$externs_path/metrics_private.js", - # TODO(crbug.com/980846): Remove it once it is upstreamed to the closure - # compiler. - "//ash/webui/web_applications/externs/file_handling.externs.js", - ] - externs_list += externs_files + definitions = [ "externs/types.d.ts" ] + foreach(file, get_target_outputs(":build_mojo_dts")) { + # Only take the .d.ts files, and ignoring the manifest.json that is also + # listed as output file. + if (get_path_info(file, "extension") == "ts") { + definitions += [ rebase_path(file) ] + } + } + + in_files = + rebase_path(get_target_outputs(":copy_sources"), root_dir, root_build_dir) + + path_mappings = [ "/mojom-webui/*|mojom-webui/*" ] }
diff --git a/ash/webui/camera_app_ui/resources/js/chrome_util.js b/ash/webui/camera_app_ui/resources/js/chrome_util.js index 7da6731..958ebcf9 100644 --- a/ash/webui/camera_app_ui/resources/js/chrome_util.js +++ b/ash/webui/camera_app_ui/resources/js/chrome_util.js
@@ -2,6 +2,11 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// TODO(pihsun): The jsdoc for assert and assertNotReached is in a state that +// is recognizable by TypeScript but not by ESLint, disable ESLint checks for +// now before we migrate this file fully to TypeScript. +/* eslint-disable valid-jsdoc */ + /** * Verify |condition| is truthy and return |condition| if so. * @template T @@ -9,8 +14,7 @@ * may be used to test whether a value is defined or not, and we don't want * to force a cast to Boolean. * @param {string=} optMessage A message to show on failure. - * @return {T} A non-null |condition|. - * @closurePrimitive {asserts.truthy} + * @return {asserts condition} A non-null |condition|. */ export function assert(condition, optMessage) { if (!condition) { @@ -20,7 +24,6 @@ } throw new Error(message); } - return condition; } /** @@ -43,7 +46,7 @@ * unexpected input. * * @param {string=} optMessage A message to show when this is hit. - * @closurePrimitive {asserts.fail} + * @return {never} */ export function assertNotReached(optMessage) { assert(false, optMessage || 'Unreachable code hit');
diff --git a/ash/webui/camera_app_ui/resources/js/device/constraints_preferrer.js b/ash/webui/camera_app_ui/resources/js/device/constraints_preferrer.js index ac87ed84..56b3b61 100644 --- a/ash/webui/camera_app_ui/resources/js/device/constraints_preferrer.js +++ b/ash/webui/camera_app_ui/resources/js/device/constraints_preferrer.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 {assert} from '../chrome_util.js'; +import {assert, assertNotReached} from '../chrome_util.js'; import * as dom from '../dom.js'; import * as localStorage from '../models/local_storage.js'; import * as state from '../state.js'; @@ -117,7 +117,9 @@ * @param {!Array<!Camera3DeviceInfo>} devices * @abstract */ - updateDevicesInfo(devices) {} + updateDevicesInfo(devices) { + assertNotReached(); + } /** * Updates values according to currently working video device and capture @@ -128,7 +130,9 @@ * @param {!Resolution} resolution Resolution to be updated to. * @abstract */ - updateValues(deviceId, stream, facing, resolution) {} + updateValues(deviceId, stream, facing, resolution) { + assertNotReached(); + } /** * Gets all available candidates for capturing under this controller and its @@ -140,7 +144,9 @@ * @return {!Array<!CaptureCandidate>} Capture resolution and its preview * constraints-candidates. */ - getSortedCandidates(deviceId) {} + getSortedCandidates(deviceId) { + assertNotReached(); + } /** * Gets capture resolution supported by video device with given device id. @@ -159,7 +165,9 @@ * @param {string} deviceId Device id of the video device to be changed. * @param {!Resolution} resolution Preferred capture resolution. */ - changePreferredResolution(deviceId, resolution) {} + changePreferredResolution(deviceId, resolution) { + assertNotReached(); + } /** * Sets listener for changes of preferred resolution used in taking photo on
diff --git a/ash/webui/camera_app_ui/resources/js/externs/types.d.ts b/ash/webui/camera_app_ui/resources/js/externs/types.d.ts index eb522d8..4f4c95df 100644 --- a/ash/webui/camera_app_ui/resources/js/externs/types.d.ts +++ b/ash/webui/camera_app_ui/resources/js/externs/types.d.ts
@@ -8,10 +8,6 @@ // ESLint doesn't like "declare class" without jsdoc. /* eslint-disable require-jsdoc */ -// TODO(b/172340451): Use the types generated by Mojo TypeScript binding -// generator once https://crbug.com/1002798 is finished. -declare module '*.mojom-webui.js'; - // This is currently a Chrome only API, and the spec is still in working draft // stage. // https://developer.mozilla.org/en-US/docs/Web/API/UIEvent/sourceCapabilities @@ -291,3 +287,15 @@ interface Navigator { canShare: (data: ShareData) => boolean; } + +// Web Workers API interface. This is included in lib.webworker.d.ts and +// available if we enable lib: ["webworker"] in tsconfig.json, but it conflicts +// with the "dom" lib that we also need. For simplicity we're providing a +// simplified interface for the only thing we've used in the +// SharedWorkerGlobalScope to satisfy TypeScript. +// +// TODO(pihsun): Consider splitting the webworker part into another +// tsconfig.json, see https://github.com/microsoft/TypeScript/issues/20595 +interface SharedWorkerGlobalScope { + onconnect?: ((this: SharedWorkerGlobalScope, ev: MessageEvent) => any)|null; +}
diff --git a/ash/webui/camera_app_ui/resources/js/face.js b/ash/webui/camera_app_ui/resources/js/face.js index 7c09e38d0..dbc4858 100644 --- a/ash/webui/camera_app_ui/resources/js/face.js +++ b/ash/webui/camera_app_ui/resources/js/face.js
@@ -79,7 +79,6 @@ this.ctx_.clearRect(0, 0, this.canvas_.width, this.canvas_.height); // TODO(b/178344897): Handle zoomed preview. - // TODO(b/178344897): Handle cropped preview. // TODO(b/178344897): Handle screen orientation dynamically. this.ctx_.strokeStyle = RECT_COLOR; @@ -91,10 +90,36 @@ y2 /= this.activeArraySize_.height; [x1, y1] = rotate(x1, y1, this.orientation_); [x2, y2] = rotate(x2, y2, this.orientation_); - x1 *= this.canvas_.width; - y1 *= this.canvas_.height; - x2 *= this.canvas_.width; - y2 *= this.canvas_.height; + const canvasAspectRatio = this.canvas_.width / this.canvas_.height; + const sensorAspectRatio = + this.activeArraySize_.width / this.activeArraySize_.height; + if (canvasAspectRatio > sensorAspectRatio) { + // Canvas has wider aspect than the sensor, e.g. when we're showing a + // 16:9 stream captured from a 4:3 sensor. Based on our hardware + // requirement, we assume the stream is cropped into letterbox from the + // active array. + const normalizedCanvasHeight = sensorAspectRatio / canvasAspectRatio; + const clipped = (1 - normalizedCanvasHeight) / 2; + x1 *= this.canvas_.width; + y1 = (Math.max(y1 - clipped, 0) / normalizedCanvasHeight) * + this.canvas_.height; + x2 *= this.canvas_.width; + y2 = (Math.max(y2 - clipped, 0) / normalizedCanvasHeight) * + this.canvas_.height; + } else if (canvasAspectRatio < sensorAspectRatio) { + // Canvas has taller aspect than the sensor, e.g. when we're showing a + // 4:3 stream captured from a 16:9 sensor. Based on our hardware + // requirement, we assume the stream is cropped into pillarbox from the + // active array. + const normalizedCanvasWidth = canvasAspectRatio / sensorAspectRatio; + const clipped = (1 - normalizedCanvasWidth) / 2; + x1 = (Math.max(x1 - clipped, 0) * normalizedCanvasWidth) * + this.canvas_.width; + y1 *= this.canvas_.height; + x2 = (Math.max(x2 - clipped, 0) * normalizedCanvasWidth) * + this.canvas_.width; + y2 *= this.canvas_.height; + } this.ctx_.strokeRect(x1, y1, x2 - x1, y2 - y1); } }
diff --git a/ash/webui/camera_app_ui/resources/js/intent.js b/ash/webui/camera_app_ui/resources/js/intent.js index 0ab8d8d..c037e41 100644 --- a/ash/webui/camera_app_ui/resources/js/intent.js +++ b/ash/webui/camera_app_ui/resources/js/intent.js
@@ -54,9 +54,8 @@ /** * Capture mode of intent. * @const {!Mode} - * @private */ - this.mode_ = mode; + this.mode = mode; /** * Whether the intent should return with the captured result. @@ -143,11 +142,8 @@ */ logResult(result) { metrics.sendIntentEvent({ - mode: this.mode_, + intent: this, result, - shouldHandleResult: this.shouldHandleResult, - shouldDownScale: this.shouldDownScale, - isSecure: this.isSecure, }); }
diff --git a/ash/webui/camera_app_ui/resources/js/js.gni b/ash/webui/camera_app_ui/resources/js/js.gni index 85728f77..ed8a1402c5 100644 --- a/ash/webui/camera_app_ui/resources/js/js.gni +++ b/ash/webui/camera_app_ui/resources/js/js.gni
@@ -29,7 +29,7 @@ "lib/comlink.js", "lib/ffmpeg.js", "main.js", - "metrics.js", + "metrics.ts", "models/async_interval.js", "models/async_writer.js", "models/barcode.js", @@ -47,10 +47,11 @@ "models/result_saver.js", "models/video_processor_interface.js", "models/video_saver.js", - "mojo/chrome_helper.js", + "mojo/chrome_helper.ts", "mojo/device_operator.js", "mojo/image_capture.js", - "mojo/util.js", + "mojo/type.ts", + "mojo/util.ts", "nav.js", "new_feature_toast.js", "perf.js", @@ -90,7 +91,7 @@ "views/crop_document.js", "views/dialog.js", "views/ptz_panel.js", - "views/review.js", + "views/review.ts", "views/settings.js", "views/view.js", "views/warning.js",
diff --git a/ash/webui/camera_app_ui/resources/js/metrics.js b/ash/webui/camera_app_ui/resources/js/metrics.js deleted file mode 100644 index 3a7e244..0000000 --- a/ash/webui/camera_app_ui/resources/js/metrics.js +++ /dev/null
@@ -1,519 +0,0 @@ -// Copyright 2019 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import {assert} from './chrome_util.js'; -import * as Comlink from './lib/comlink.js'; -import * as loadTimeData from './models/load_time_data.js'; -import * as localStorage from './models/local_storage.js'; -import {ChromeHelper} from './mojo/chrome_helper.js'; -import * as state from './state.js'; -import { - Facing, // eslint-disable-line no-unused-vars - Mode, - PerfEvent, // eslint-disable-line no-unused-vars - PerfInformation, // eslint-disable-line no-unused-vars - Resolution, // eslint-disable-line no-unused-vars -} from './type.js'; -// eslint-disable-next-line no-unused-vars -import {GAHelperInterface} from './untrusted_helper_interfaces.js'; -import * as util from './util.js'; -import {WaitableEvent} from './waitable_event.js'; - -/** - * The tracker ID of the GA metrics. - * @type {string} - */ -const GA_ID = 'UA-134822711-1'; - -/** - * @type {?Map<number, !Object>} - */ -let baseDimen = null; - -/** - * @type {!WaitableEvent} - */ -const ready = new WaitableEvent(); - -/** - * @type {!Promise<!GAHelperInterface>} - */ -const gaHelper = (async () => { - return /** @type {!GAHelperInterface} */ ( - await util.createUntrustedJSModule('/js/untrusted_ga_helper.js')); -})(); - -/** - * Send the event to GA backend. - * @param {!UniversalAnalytics.FieldsObject} event The event to send. - * @param {?Map<number, !Object>=} dimen Optional object contains dimension - * information. - */ -async function sendEvent(event, dimen = null) { - const assignDimension = (e, d) => { - d.forEach((value, key) => e[`dimension${key}`] = value); - }; - - assert(baseDimen !== null); - assignDimension(event, baseDimen); - if (dimen !== null) { - assignDimension(event, dimen); - } - - await ready.wait(); - - // This value reflects the logging constent option in OS settings. - const canSendMetrics = - await ChromeHelper.getInstance().isMetricsAndCrashReportingEnabled(); - if (canSendMetrics) { - (await gaHelper).sendGAEvent(event); - } -} - -/** - * Set if the metrics is enabled. Note that the metrics will only be sent if it - * is enabled AND the logging consent option is enabled in OS settings. - * @param {boolean} enabled True if the metrics is enabled. - */ -export async function setMetricsEnabled(enabled) { - await ready.wait(); - await (await gaHelper).setMetricsEnabled(GA_ID, enabled); -} - -const SCHEMA_VERSION = 2; - -/** - * Initializes metrics with parameters. - */ -export async function initMetrics() { - const board = loadTimeData.getBoard(); - const boardName = /^(x86-)?(\w*)/.exec(board)[0]; - const match = navigator.appVersion.match(/CrOS\s+\S+\s+([\d.]+)/); - const osVer = match ? match[1] : ''; - baseDimen = new Map([ - [1, boardName], - [2, osVer], - [31, SCHEMA_VERSION], - ]); - - const GA_LOCAL_STORAGE_KEY = 'google-analytics.analytics.user-id'; - const clientId = localStorage.getString(GA_LOCAL_STORAGE_KEY); - - const setClientId = (id) => { - localStorage.set(GA_LOCAL_STORAGE_KEY, id); - }; - - await (await gaHelper).initGA(GA_ID, clientId, Comlink.proxy(setClientId)); - ready.signal(); -} - -/** - * Types of different ways to launch CCA. - * @enum {string} - */ -export const LaunchType = { - DEFAULT: 'default', - ASSISTANT: 'assistant', -}; - -/** - * Parameters for logging launch event. |launchType| stands for how CCA is - * launched. - * @typedef {{launchType: !LaunchType}} - */ -export let LaunchEventParam; - -/** - * Sends launch type event. - * @param {!LaunchEventParam} param - */ -export function sendLaunchEvent({launchType}) { - sendEvent( - { - eventCategory: 'launch', - eventAction: 'start', - eventLabel: '', - }, - new Map([ - [32, launchType], - ])); -} - -/** - * Types of intent result dimension. - * @enum {string} - */ -export const IntentResultType = { - NOT_INTENT: '', - CANCELED: 'canceled', - CONFIRMED: 'confirmed', -}; - -/** - * Types of document scanning result dimension. - * @enum {string} - */ -export const DocResultType = { - NOT_DOCUMENT: '', - CANCELED: 'canceled', - SAVE_AS_PHOTO: 'save-as-photo', - SAVE_AS_PDF: 'save-as-pdf', - SHARE: 'share', -}; - -/** - * Types of user interaction with fix document page. - * @enum {number} - */ -export const DocFixType = { - NONE: 0, - NO_FIX: 1, - FIX_ROTATION: 2, - FIX_POSITION: 3, - FIX_BOTH: 4, -}; - -/** - * Types of gif recording result dimension. - * @enum {number} - */ -export const GifResultType = { - NOT_GIF_RESULT: 0, - RETAKE: 1, - SHARE: 2, - SAVE: 3, -}; - -/** - * Types of recording in video mode. - * @enum {number} - */ -export const RecordType = { - NOT_RECORDING: 0, - NORMAL_VIDEO: 1, - GIF: 2, -}; - -/** - * Types of different ways to trigger shutter button. - * @enum {string} - */ -export const ShutterType = { - UNKNOWN: 'unknown', - MOUSE: 'mouse', - KEYBOARD: 'keyboard', - TOUCH: 'touch', - VOLUME_KEY: 'volume-key', - ASSISTANT: 'assistant', -}; - -/** - * Parameters of capture metrics event. - * @record - */ -export class CaptureEventParam { - /** - * @public - */ - constructor() { - /** - * @type {!Facing} Camera facing of the capture. - */ - this.facing; - - /** - * @type {(number|undefined)} Length of duration for captured motion result - * in milliseconds. - */ - this.duration; - - /** - * @type {!Resolution} Capture resolution. - */ - this.resolution; - - /** - * @type {!IntentResultType|undefined} - */ - this.intentResult; - - /** - * @type {!ShutterType} - */ - this.shutterType; - - /** - * Whether the event is for video snapshot. - * @type {boolean|undefined} - */ - this.isVideoSnapshot; - - /** - * Whether the video have ever paused and resumed in the recording. - * @type {boolean|undefined} - */ - this.everPaused; - - /** - * @type {!DocResultType|undefined} - */ - this.docResult; - - /** - * @type {!DocFixType|undefined} - */ - this.docFixType; - - /** - * @type {!GifResultType|undefined} - */ - this.gifResult; - - /** - * @type {!RecordType|undefined} - */ - this.recordType; - } -} - -/** - * Sends capture type event. - * @param {!CaptureEventParam} param - */ -export function sendCaptureEvent({ - facing, - duration = 0, - resolution, - intentResult = IntentResultType.NOT_INTENT, - shutterType, - isVideoSnapshot = false, - everPaused = false, - docResult = DocResultType.NOT_DOCUMENT, - docFixType, - recordType = RecordType.NOT_RECORDING, - gifResult = GifResultType.NOT_GIF_RESULT, -}) { - /** - * @param {!Array<!state.StateUnion>} states - * @param {!state.StateUnion=} cond - * @param {boolean=} strict - * @return {string} - */ - const condState = (states, cond = undefined, strict = undefined) => { - // Return the first existing state among the given states only if there is - // no gate condition or the condition is met. - const prerequisite = !cond || state.get(cond); - if (strict && !prerequisite) { - return ''; - } - return prerequisite && states.find((s) => state.get(s)) || 'n/a'; - }; - - const State = state.State; - sendEvent( - { - eventCategory: 'capture', - eventAction: condState(Object.values(Mode)), - eventLabel: facing, - eventValue: duration, - }, - new Map([ - // Skips 3rd dimension for obsolete 'sound' state. - [4, condState([State.MIRROR])], - [ - 5, - condState( - [State.GRID_3x3, State.GRID_4x4, State.GRID_GOLDEN], State.GRID), - ], - [6, condState([State.TIMER_3SEC, State.TIMER_10SEC], State.TIMER)], - [7, condState([State.MIC], Mode.VIDEO, true)], - [8, condState([State.MAX_WND])], - [9, condState([State.TALL])], - [10, resolution.toString()], - [11, condState([State.FPS_30, State.FPS_60], Mode.VIDEO, true)], - [12, intentResult], - [21, shutterType], - [22, isVideoSnapshot], - [23, everPaused], - [27, docResult], - [28, recordType], - [29, gifResult], - [30, duration], - // [31, SCHEMA_VERSION] - [32, docFixType ?? ''], - ])); -} - - -/** - * Parameters for logging perf event. - * @record - */ -export class PerfEventParam { - /** - * @public - */ - constructor() { - /** - * @type {!PerfEvent} Target event type. - */ - this.event; - - /** - * @type {number} Duration of the event in ms. - */ - this.duration; - - /** - * @type {!PerfInformation|undefined} Optional information for the event. - */ - this.perfInfo; - } -} - -/** - * Sends perf type event. - * @param {!PerfEventParam} param - */ -export function sendPerfEvent({event, duration, perfInfo = {}}) { - const resolution = perfInfo['resolution'] || ''; - const facing = perfInfo['facing'] || ''; - sendEvent( - { - eventCategory: 'perf', - eventAction: event, - eventLabel: facing, - // Round the duration here since GA expects that the value is an - // integer. Reference: - // https://support.google.com/analytics/answer/1033068 - eventValue: Math.round(duration), - }, - new Map([ - [10, `${resolution}`], - ])); -} - -/** - * See Intent class in intent.js for the descriptions of each field. - * TODO(b/131133953): Pass an Intent directly once the type-only import feature - * is implemented in Closure Compiler. - * @typedef {{ - * mode: !Mode, - * result: !IntentResultType, - * shouldHandleResult: boolean, - * shouldDownScale: boolean, - * isSecure: boolean, - * }} - */ -export let IntentEventParam; - -/** - * Sends intent type event. - * @param {!IntentEventParam} param - */ -export function sendIntentEvent( - {mode, result, shouldHandleResult, shouldDownScale, isSecure}) { - const getBoolValue = (b) => b ? '1' : '0'; - sendEvent( - { - eventCategory: 'intent', - eventAction: mode, - eventLabel: result, - }, - new Map([ - [12, result], - [13, getBoolValue(shouldHandleResult)], - [14, getBoolValue(shouldDownScale)], - [15, getBoolValue(isSecure)], - ])); -} - -/** - * @typedef {{ - * type: string, - * level: string, - * errorName: string, - * fileName: string, - * funcName: string, - * lineNo: string, - * colNo: string, - * }} - */ -export let ErrorEventParam; - -/** - * Sends error type event. - * @param {!ErrorEventParam} param - */ -export function sendErrorEvent( - {type, level, errorName, fileName, funcName, lineNo, colNo}) { - sendEvent( - { - eventCategory: 'error', - eventAction: type, - eventLabel: level, - }, - new Map([ - [16, errorName], - [17, fileName], - [18, funcName], - [19, lineNo], - [20, colNo], - ])); -} - -/** - * Sends the barcode enabled event. - */ -export function sendBarcodeEnabledEvent() { - sendEvent({ - eventCategory: 'barcode', - eventAction: 'enable', - }); -} - -/** - * Types of the decoded barcode content. - * @enum {string} - */ -export const BarcodeContentType = { - TEXT: 'text', - URL: 'url', -}; - -/** - * @typedef {{ - * contentType: !BarcodeContentType, - * }} - */ -export let BarcodeDetectedEventParam; - -/** - * Sends the barcode detected event. - * @param {!BarcodeDetectedEventParam} param - */ -export function sendBarcodeDetectedEvent({contentType}) { - sendEvent({ - eventCategory: 'barcode', - eventAction: 'detect', - eventLabel: contentType, - }); -} - -/** - * Sends the open ptz panel event. - * @param {{pan: boolean, tilt: boolean, zoom: boolean}} capabilities - */ -export function sendOpenPTZPanelEvent(capabilities) { - sendEvent( - { - eventCategory: 'ptz', - eventAction: 'open-panel', - }, - new Map([ - [24, capabilities.pan], - [25, capabilities.tilt], - [26, capabilities.zoom], - ])); -}
diff --git a/ash/webui/camera_app_ui/resources/js/metrics.ts b/ash/webui/camera_app_ui/resources/js/metrics.ts new file mode 100644 index 0000000..cda41c1 --- /dev/null +++ b/ash/webui/camera_app_ui/resources/js/metrics.ts
@@ -0,0 +1,423 @@ +// 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 {assert} from './chrome_util.js'; +import {Intent} from './intent.js'; +import * as Comlink from './lib/comlink.js'; +import * as loadTimeData from './models/load_time_data.js'; +import * as localStorage from './models/local_storage.js'; +import {ChromeHelper} from './mojo/chrome_helper.js'; +import * as state from './state.js'; +import { + Facing, + Mode, + PerfEvent, + PerfInformation, + Resolution, +} from './type.js'; +import {GAHelperInterface} from './untrusted_helper_interfaces.js'; +import * as util from './util.js'; +import {WaitableEvent} from './waitable_event.js'; + +/** + * The tracker ID of the GA metrics. + */ +const GA_ID = 'UA-134822711-1'; + +let baseDimen: Map<number, string|number>|null = null; + +const ready = new WaitableEvent(); + +const gaHelper = util.createUntrustedJSModule('/js/untrusted_ga_helper.js') as + Promise<GAHelperInterface>; + +/** + * Send the event to GA backend. + * @param event The event to send. + * @param dimen Optional object contains dimension information. + */ +async function sendEvent( + event: UniversalAnalytics.FieldsObject, dimen?: Map<number, unknown>) { + const assignDimension = (e, d) => { + for (const [key, value] of d.entries()) { + e[`dimension${key}`] = value; + } + }; + + assert(baseDimen !== null); + assignDimension(event, baseDimen); + if (dimen !== undefined) { + assignDimension(event, dimen); + } + + await ready.wait(); + + // This value reflects the logging consent option in OS settings. + const canSendMetrics = + await ChromeHelper.getInstance().isMetricsAndCrashReportingEnabled(); + if (canSendMetrics) { + (await gaHelper).sendGAEvent(event); + } +} + +/** + * Set if the metrics is enabled. Note that the metrics will only be sent if it + * is enabled AND the logging consent option is enabled in OS settings. + * @param enabled True if the metrics is enabled. + */ +export async function setMetricsEnabled(enabled: boolean): Promise<void> { + await ready.wait(); + await (await gaHelper).setMetricsEnabled(GA_ID, enabled); +} + +const SCHEMA_VERSION = 2; + +/** + * Initializes metrics with parameters. + */ +export async function initMetrics(): Promise<void> { + const board = loadTimeData.getBoard(); + const boardName = /^(x86-)?(\w*)/.exec(board)[0]; + const match = navigator.appVersion.match(/CrOS\s+\S+\s+([\d.]+)/); + const osVer = match ? match[1] : ''; + baseDimen = new Map<number, string|number>([ + [1, boardName], + [2, osVer], + [31, SCHEMA_VERSION], + ]); + + const GA_LOCAL_STORAGE_KEY = 'google-analytics.analytics.user-id'; + const clientId = localStorage.getString(GA_LOCAL_STORAGE_KEY); + + const setClientId = (id) => { + localStorage.set(GA_LOCAL_STORAGE_KEY, id); + }; + + await (await gaHelper).initGA(GA_ID, clientId, Comlink.proxy(setClientId)); + ready.signal(); +} + +/** + * Types of different ways to launch CCA. + */ +export enum LaunchType { + DEFAULT = 'default', + ASSISTANT = 'assistant', +} + +/** + * Parameters for logging launch event. |launchType| stands for how CCA is + * launched. + */ +export interface LaunchEventParam { + launchType: LaunchType; +} + +/** + * Sends launch type event. + */ +export function sendLaunchEvent({launchType}: LaunchEventParam): void { + sendEvent( + { + eventCategory: 'launch', + eventAction: 'start', + eventLabel: '', + }, + new Map([ + [32, launchType], + ])); +} + +/** + * Types of intent result dimension. + */ +export enum IntentResultType { + NOT_INTENT = '', + CANCELED = 'canceled', + CONFIRMED = 'confirmed', +} + +/** + * Types of document scanning result dimension. + */ +export enum DocResultType { + NOT_DOCUMENT = '', + CANCELED = 'canceled', + SAVE_AS_PHOTO = 'save-as-photo', + SAVE_AS_PDF = 'save-as-pdf', + SHARE = 'share', +} + +/** + * Types of user interaction with fix document page. + */ +export enum DocFixType { + NONE = 0, + NO_FIX = 1, + FIX_ROTATION = 2, + FIX_POSITION = 3, + FIX_BOTH = 4, +} + +/** + * Types of gif recording result dimension. + */ +export enum GifResultType { + NOT_GIF_RESULT = 0, + RETAKE = 1, + SHARE = 2, + SAVE = 3, +} + +/** + * Types of recording in video mode. + */ +export enum RecordType { + NOT_RECORDING = 0, + NORMAL_VIDEO = 1, + GIF = 2, +} + +/** + * Types of different ways to trigger shutter button. + */ +export enum ShutterType { + UNKNOWN = 'unknown', + MOUSE = 'mouse', + KEYBOARD = 'keyboard', + TOUCH = 'touch', + VOLUME_KEY = 'volume-key', + ASSISTANT = 'assistant', +} + +/** + * Parameters of capture metrics event. + */ +export interface CaptureEventParam { + /** Camera facing of the capture. */ + facing: Facing; + /** Length of duration for captured motion result in milliseconds. */ + duration?: number; + /** Capture resolution. */ + resolution: Resolution; + intentResult?: IntentResultType; + shutterType: ShutterType; + /** Whether the event is for video snapshot. */ + isVideoSnapshot?: boolean; + /** Whether the video have ever paused and resumed in the recording. */ + everPaused?: boolean; + docResult?: DocResultType; + docFixType?: DocFixType; + gifResult?: GifResultType; + recordType?: RecordType; +} + +/** + * Sends capture type event. + */ +export function sendCaptureEvent({ + facing, + duration = 0, + resolution, + intentResult = IntentResultType.NOT_INTENT, + shutterType, + isVideoSnapshot = false, + everPaused = false, + docResult = DocResultType.NOT_DOCUMENT, + docFixType, + recordType = RecordType.NOT_RECORDING, + gifResult = GifResultType.NOT_GIF_RESULT, +}: CaptureEventParam): void { + const condState = + (states: state.StateUnion[], cond?: state.StateUnion, strict?: boolean): + string => { + // Return the first existing state among the given states only if + // there is no gate condition or the condition is met. + const prerequisite = !cond || state.get(cond); + if (strict && !prerequisite) { + return ''; + } + return prerequisite && states.find((s) => state.get(s)) || 'n/a'; + }; + + const State = state.State; + sendEvent( + { + eventCategory: 'capture', + eventAction: condState(Object.values(Mode)), + eventLabel: facing, + eventValue: duration, + }, + new Map<number, unknown>([ + // Skips 3rd dimension for obsolete 'sound' state. + [4, condState([State.MIRROR])], + [ + 5, + condState( + [State.GRID_3x3, State.GRID_4x4, State.GRID_GOLDEN], State.GRID), + ], + [6, condState([State.TIMER_3SEC, State.TIMER_10SEC], State.TIMER)], + [7, condState([State.MIC], Mode.VIDEO, true)], + [8, condState([State.MAX_WND])], + [9, condState([State.TALL])], + [10, resolution.toString()], + [11, condState([State.FPS_30, State.FPS_60], Mode.VIDEO, true)], + [12, intentResult], + [21, shutterType], + [22, isVideoSnapshot], + [23, everPaused], + [27, docResult], + [28, recordType], + [29, gifResult], + [30, duration], + // This is included in baseDimen. + // [31, SCHEMA_VERSION] + [32, docFixType ?? ''], + ])); +} + + +/** + * Parameters for logging perf event. + */ +interface PerfEventParam { + /** Target event type. */ + event: PerfEvent; + /** Duration of the event in ms. */ + duration: number; + /** Optional information for the event. */ + perfInfo?: PerfInformation; +} + +/** + * Sends perf type event. + */ +export function sendPerfEvent({event, duration, perfInfo = {}}: PerfEventParam): + void { + const resolution = perfInfo['resolution'] || ''; + const facing = perfInfo['facing'] || ''; + sendEvent( + { + eventCategory: 'perf', + eventAction: event, + eventLabel: facing, + // Round the duration here since GA expects that the value is an + // integer. Reference: + // https://support.google.com/analytics/answer/1033068 + eventValue: Math.round(duration), + }, + new Map([ + [10, `${resolution}`], + ])); +} + +/** + * See Intent class in intent.js for the descriptions of each field. + */ +export interface IntentEventParam { + intent: Intent; + result: IntentResultType; +} + +/** + * Sends intent type event. + */ +export function sendIntentEvent({intent, result}: IntentEventParam): void { + const {mode, shouldHandleResult, shouldDownScale, isSecure} = intent; + const getBoolValue = (b) => b ? '1' : '0'; + sendEvent( + { + eventCategory: 'intent', + eventAction: mode, + eventLabel: result, + }, + new Map([ + [12, result], + [13, getBoolValue(shouldHandleResult)], + [14, getBoolValue(shouldDownScale)], + [15, getBoolValue(isSecure)], + ])); +} + +export interface ErrorEventParam { + type: string; + level: string; + errorName: string; + fileName: string; + funcName: string; + lineNo: string; + colNo: string; +} + +/** + * Sends error type event. + */ +export function sendErrorEvent( + {type, level, errorName, fileName, funcName, lineNo, colNo}: + ErrorEventParam): void { + sendEvent( + { + eventCategory: 'error', + eventAction: type, + eventLabel: level, + }, + new Map([ + [16, errorName], + [17, fileName], + [18, funcName], + [19, lineNo], + [20, colNo], + ])); +} + +/** + * Sends the barcode enabled event. + */ +export function sendBarcodeEnabledEvent(): void { + sendEvent({ + eventCategory: 'barcode', + eventAction: 'enable', + }); +} + +/** + * Types of the decoded barcode content. + */ +export enum BarcodeContentType { + TEXT = 'text', + URL = 'url', +} + +interface BarcodeDetectedEventParam { + contentType: BarcodeContentType; +} + +/** + * Sends the barcode detected event. + */ +export function sendBarcodeDetectedEvent( + {contentType}: BarcodeDetectedEventParam): void { + sendEvent({ + eventCategory: 'barcode', + eventAction: 'detect', + eventLabel: contentType, + }); +} + +/** + * Sends the open ptz panel event. + */ +export function sendOpenPTZPanelEvent( + capabilities: {pan: boolean, tilt: boolean, zoom: boolean}): void { + sendEvent( + { + eventCategory: 'ptz', + eventAction: 'open-panel', + }, + new Map([ + [24, capabilities.pan], + [25, capabilities.tilt], + [26, capabilities.zoom], + ])); +}
diff --git a/ash/webui/camera_app_ui/resources/js/models/barcode_worker_interface.js b/ash/webui/camera_app_ui/resources/js/models/barcode_worker_interface.js index 34885d4..48bf04e 100644 --- a/ash/webui/camera_app_ui/resources/js/models/barcode_worker_interface.js +++ b/ash/webui/camera_app_ui/resources/js/models/barcode_worker_interface.js
@@ -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 {assertNotReached} from '../chrome_util.js'; + /** * The interface for a barcode worker. All methods are marked as async since * it will be used with Comlink and Web Workers. @@ -15,5 +17,7 @@ * barcode is detected. * @abstract */ - async detect(bitmap) {} + async detect(bitmap) { + assertNotReached(); + } }
diff --git a/ash/webui/camera_app_ui/resources/js/models/file_system_access_entry.js b/ash/webui/camera_app_ui/resources/js/models/file_system_access_entry.js index 8e27c2f1..d2e7afc2 100644 --- a/ash/webui/camera_app_ui/resources/js/models/file_system_access_entry.js +++ b/ash/webui/camera_app_ui/resources/js/models/file_system_access_entry.js
@@ -3,7 +3,7 @@ // found in the LICENSE file. import {AsyncJobQueue} from '../async_job_queue.js'; -import {assert} from '../chrome_util.js'; +import {assert, assertNotReached} from '../chrome_util.js'; import {AsyncWriter} from './async_writer.js'; @@ -114,7 +114,9 @@ * @return {string} * @abstract */ - get name() {} + get name() { + return assertNotReached(); + } /* eslint-enable getter-return */ @@ -123,14 +125,18 @@ * @return {!Promise<!Array<!FileAccessEntry>>} * @abstract */ - async getFiles() {} + async getFiles() { + assertNotReached(); + } /** * Gets directories in this directory. * @return {!Promise<!Array<!DirectoryAccessEntry>>} * @abstract */ - async getDirectories() {} + async getDirectories() { + assertNotReached(); + } /** * Gets the file given by its |name|. @@ -138,7 +144,9 @@ * @return {!Promise<?FileAccessEntry>} The entry of the found file. * @abstract */ - async getFile(name) {} + async getFile(name) { + assertNotReached(); + } /** * Checks if file or directory with the target name exists. @@ -146,7 +154,9 @@ * @return {!Promise<boolean>} * @abstract */ - async isExist(name) {} + async isExist(name) { + assertNotReached(); + } /** * Create the file given by its |name|. If there is already a file with same @@ -156,7 +166,9 @@ * @return {!Promise<!FileAccessEntry>} The entry of the created file. * @abstract */ - async createFile(name) {} + async createFile(name) { + assertNotReached(); + } /** * Gets the directory given by its |name|. If the directory is not found, @@ -167,14 +179,18 @@ * @return {!Promise<?DirectoryAccessEntry>} The entry of the found/created * directory. */ - async getDirectory({name, createIfNotExist}) {} + async getDirectory({name, createIfNotExist}) { + assertNotReached(); + } /** * Removes file by given |name| from the directory. * @param {string} name The name of the file. * @return {!Promise} */ - async removeEntry(name) {} + async removeEntry(name) { + assertNotReached(); + } } /**
diff --git a/ash/webui/camera_app_ui/resources/js/models/result_saver.js b/ash/webui/camera_app_ui/resources/js/models/result_saver.js index 60364eff..0b0227b 100644 --- a/ash/webui/camera_app_ui/resources/js/models/result_saver.js +++ b/ash/webui/camera_app_ui/resources/js/models/result_saver.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 {assertNotReached} from '../chrome_util.js'; // eslint-disable-next-line no-unused-vars import {Resolution} from '../type.js'; @@ -20,7 +21,9 @@ * @param {string} name Name of the photo to be saved. * @return {!Promise} */ - async savePhoto(blob, name) {} + async savePhoto(blob, name) { + assertNotReached(); + } /** * Saves gif capture result. @@ -28,7 +31,9 @@ * @param {string} name Name of the gif to be saved. * @return {!Promise} */ - async saveGif(blob, name) {} + async saveGif(blob, name) { + assertNotReached(); + } /** * Returns a video saver to save captured result video. @@ -37,7 +42,9 @@ * orientation. * @return {!Promise<!VideoSaver>} */ - async startSaveVideo(videoRotation) {} + async startSaveVideo(videoRotation) { + assertNotReached(); + } /** * Saves captured video result. @@ -45,5 +52,7 @@ * saved. * @return {!Promise} */ - async finishSaveVideo(video) {} + async finishSaveVideo(video) { + assertNotReached(); + } }
diff --git a/ash/webui/camera_app_ui/resources/js/models/video_processor_interface.js b/ash/webui/camera_app_ui/resources/js/models/video_processor_interface.js index cb5ebbe..2f2cec0b 100644 --- a/ash/webui/camera_app_ui/resources/js/models/video_processor_interface.js +++ b/ash/webui/camera_app_ui/resources/js/models/video_processor_interface.js
@@ -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 {assertNotReached} from '../chrome_util.js'; + /** * The interface for a video processor. All methods are marked as async since * it will be used with Comlink and Web Workers. @@ -14,19 +16,25 @@ * @return {!Promise} * @abstract */ - async write(blob) {} + async write(blob) { + assertNotReached(); + } /** * Closes the processor. No more write operations are allowed. * @return {!Promise} Resolved when all the data are processed and flushed. * @abstract */ - async close() {} + async close() { + assertNotReached(); + } /** * Cancels the remaining tasks. No more write operations are allowed. * @return {!Promise} * @abstract */ - async cancel() {} + async cancel() { + assertNotReached(); + } }
diff --git a/ash/webui/camera_app_ui/resources/js/mojo/chrome_helper.js b/ash/webui/camera_app_ui/resources/js/mojo/chrome_helper.js deleted file mode 100644 index 80b5330..0000000 --- a/ash/webui/camera_app_ui/resources/js/mojo/chrome_helper.js +++ /dev/null
@@ -1,393 +0,0 @@ -// Copyright 2019 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import { - CameraAppHelper, - CameraAppHelperRemote, // eslint-disable-line no-unused-vars - CameraUsageOwnershipMonitorCallbackRouter, - DocumentOutputFormat, - ExternalScreenMonitorCallbackRouter, - FileMonitorResult, - ScreenState, // eslint-disable-line no-unused-vars - ScreenStateMonitorCallbackRouter, - TabletModeMonitorCallbackRouter, -} from '/ash/webui/camera_app_ui/camera_app_helper.mojom-webui.js'; -/* eslint-disable max-len */ -// All long mojo import path will be consolidated by crrev.com/c/3090445. -import { - Rotation, -} from - '/chromeos/services/machine_learning/public/mojom/document_scanner_param_types.mojom-webui.js'; -/* eslint-enable max-len */ -import { - CameraIntentAction, -} from '/ash/components/arc/mojom/camera_intent.mojom-webui.js'; -import { - PointF, // eslint-disable-line no-unused-vars -} from 'chrome://resources/mojo/ui/gfx/geometry/mojom/geometry.mojom-webui.js'; - -import {assert, assertNotReached} from '../chrome_util.js'; -import {reportError} from '../error.js'; -import {Point} from '../geometry.js'; -import { - ErrorLevel, - ErrorType, - MimeType, // eslint-disable-line no-unused-vars -} from '../type.js'; -import {windowController} from '../window_controller.js'; - -import {wrapEndpoint} from './util.js'; - -/** - * The singleton instance of ChromeHelper. Initialized by the first - * invocation of getInstance(). - * @type {?ChromeHelper} - */ -let instance = null; - -/** - * Forces casting type from Uint8Array to !Array<number>. - * @param {!Uint8Array} data - * @return {!Array<number>} - * @suppress {checkTypes} - * @private - */ -function castToNumberArray(data) { - return data; -} - -/** - * @const {!Array<!Rotation>} - */ -const toMojoRotation = [ - Rotation.ROTATION_0, - Rotation.ROTATION_90, - Rotation.ROTATION_180, - Rotation.ROTATION_270, -]; - -/** - * Casts from rotation degrees to mojo rotation. - * @param {number} rotation - * @return {!Rotation} - */ -function castToMojoRotation(rotation) { - switch (rotation) { - case 0: - case 90: - case 180: - case 270: - return toMojoRotation[rotation / 90]; - } - assertNotReached(`Invalid rotation ${rotation}`); -} - -/** - * Communicates with Chrome. - */ -export class ChromeHelper { - /** - * @public - */ - constructor() { - /** - * An interface remote that is used to communicate with Chrome. - * @type {!CameraAppHelperRemote} - */ - this.remote_ = wrapEndpoint(CameraAppHelper.getRemote()); - } - - /** - * Starts tablet mode monitor monitoring tablet mode state of device. - * @param {function(boolean): void} onChange Callback called each time when - * tablet mode state of device changes with boolean parameter indecating - * whether device is entering tablet mode. - * @return {!Promise<boolean>} Resolved to initial state of whether device is - * is in tablet mode. - */ - async initTabletModeMonitor(onChange) { - const monitorCallbackRouter = - wrapEndpoint(new TabletModeMonitorCallbackRouter()); - monitorCallbackRouter.update.addListener(onChange); - - const {isTabletMode} = await this.remote_.setTabletMonitor( - monitorCallbackRouter.$.bindNewPipeAndPassRemote()); - return isTabletMode; - } - - /** - * Starts monitor monitoring system screen state of device. - * @param {function(!ScreenState): void} onChange - * Callback called each time when device screen state changes with - * parameter of newly changed value. - * @return {!Promise<!ScreenState>} Resolved to initial - * system screen state. - */ - async initScreenStateMonitor(onChange) { - const monitorCallbackRouter = - wrapEndpoint(new ScreenStateMonitorCallbackRouter()); - monitorCallbackRouter.update.addListener(onChange); - - const {initialState} = await this.remote_.setScreenStateMonitor( - monitorCallbackRouter.$.bindNewPipeAndPassRemote()); - return initialState; - } - - /** - * Starts monitor monitoring the existence of external screens. - * @param {function(boolean): void} onChange Callback called when the - * existence of external screens changes. - * @return {!Promise<boolean>} Resolved to the initial state. - */ - async initExternalScreenMonitor(onChange) { - const monitorCallbackRouter = - wrapEndpoint(new ExternalScreenMonitorCallbackRouter()); - monitorCallbackRouter.update.addListener(onChange); - - const {hasExternalScreen} = await this.remote_.setExternalScreenMonitor( - monitorCallbackRouter.$.bindNewPipeAndPassRemote()); - return hasExternalScreen; - } - - /** - * Checks if the device is under tablet mode currently. - * @return {!Promise<boolean>} - */ - async isTabletMode() { - const {isTabletMode} = await this.remote_.isTabletMode(); - return isTabletMode; - } - - /** - * Starts camera usage monitor. - * @param {function(): !Promise} exploitUsage - * @param {function(): !Promise} releaseUsage - * @return {!Promise} - */ - async initCameraUsageMonitor(exploitUsage, releaseUsage) { - const usageCallbackRouter = - wrapEndpoint(new CameraUsageOwnershipMonitorCallbackRouter()); - - usageCallbackRouter.onCameraUsageOwnershipChanged.addListener( - async (hasUsage) => { - if (hasUsage) { - await exploitUsage(); - } else { - await releaseUsage(); - } - }); - - const {isSuccess} = await this.remote_.setCameraUsageMonitor( - usageCallbackRouter.$.bindNewPipeAndPassRemote()); - if (!isSuccess) { - throw new Error('Failed to set camera usage monitor'); - } - - let {controller} = await this.remote_.getWindowStateController(); - controller = wrapEndpoint(controller); - await windowController.bind(controller); - } - - /** - * Triggers the begin of event tracing in Chrome. - * @param {string} event Name of the event. - */ - startTracing(event) { - this.remote_.startPerfEventTrace(event); - } - - /** - * Triggers the end of event tracing in Chrome. - * @param {string} event Name of the event. - */ - stopTracing(event) { - this.remote_.stopPerfEventTrace(event); - } - - /** - * Opens the file in Downloads folder by its |name| in gallery. - * @param {string} name Name of the target file. - */ - openFileInGallery(name) { - this.remote_.openFileInGallery(name); - } - - /** - * Opens the chrome feedback dialog. - * @param {string} placeholder The text of the placeholder in the description - * field. - */ - openFeedbackDialog(placeholder) { - this.remote_.openFeedbackDialog(placeholder); - } - - /** - * Checks return value from |handleCameraResult|. - * @param {string} caller Caller identifier. - * @param {!Promise<{isSuccess: boolean}>} value - * @return {!Promise} - */ - async checkReturn_(caller, value) { - const {isSuccess} = await value; - if (!isSuccess) { - reportError( - ErrorType.HANDLE_CAMERA_RESULT_FAILURE, ErrorLevel.ERROR, - new Error(`Return not isSuccess from calling intent ${caller}.`)); - } - } - - /** - * Notifies ARC++ to finish the intent. - * @param {number} intentId Intent id of the intent to be finished. - * @return {!Promise} - */ - async finish(intentId) { - const ret = this.remote_.handleCameraResult( - intentId, CameraIntentAction.FINISH, []); - await this.checkReturn_('finish()', ret); - } - - /** - * Notifies ARC++ to append data to intent result. - * @param {number} intentId Intent id of the intent to be appended data to. - * @param {!Uint8Array} data The data to be appended to intent result. - * @return {!Promise} - */ - async appendData(intentId, data) { - const ret = this.remote_.handleCameraResult( - intentId, CameraIntentAction.APPEND_DATA, castToNumberArray(data)); - await this.checkReturn_('appendData()', ret); - } - - /** - * Notifies ARC++ to clear appended intent result data. - * @param {number} intentId Intent id of the intent to be cleared its result. - * @return {!Promise} - */ - async clearData(intentId) { - const ret = this.remote_.handleCameraResult( - intentId, CameraIntentAction.CLEAR_DATA, []); - await this.checkReturn_('clearData()', ret); - } - - /** - * Checks if the logging consent option is enabled. - * @return {!Promise<boolean>} - */ - async isMetricsAndCrashReportingEnabled() { - const {isEnabled} = await this.remote_.isMetricsAndCrashReportingEnabled(); - return isEnabled; - } - - /** - * Sends the broadcast to ARC to notify the new photo/video is captured. - * @param {{isVideo: boolean, name: string}} info - * @return {!Promise} - */ - async sendNewCaptureBroadcast({isVideo, name}) { - this.remote_.sendNewCaptureBroadcast(isVideo, name); - } - - /** - * Monitors for the file deletion of the file given by its |name| and triggers - * |callback| when the file is deleted. Note that a previous monitor request - * will be canceled once another monitor request is sent. - * @param {string} name The name of the file to monitor. - * @param {function(): void} callback Function to trigger when deletion. - * @return {!Promise} Resolved when the file is deleted or the current monitor - * is canceled by future monitor call. - * @throws {!Error} When error occurs during monitor. - */ - async monitorFileDeletion(name, callback) { - const {result} = await this.remote_.monitorFileDeletion(name); - switch (result) { - case FileMonitorResult.DELETED: - callback(); - return; - case FileMonitorResult.CANCELED: - // Do nothing if it is canceled by another monitor call. - return; - case FileMonitorResult.ERROR: - throw new Error('Error happens when monitoring file deletion'); - } - } - - /** - * Returns true if the document mode is supported on the device. - * @return {!Promise<boolean>} - */ - async isDocumentModeSupported() { - const {isSupported} = await this.remote_.isDocumentModeSupported(); - return isSupported; - } - - /** - * Scans the blob data and returns the detected document corners. - * @param {!Blob} blob - * @return {!Promise<?Array<!Point>>} Promise resolve to positions of document - * corner. Null for failing to detected corner positions. - */ - async scanDocumentCorners(blob) { - const buffer = new Uint8Array(await blob.arrayBuffer()); - - const {corners} = - await this.remote_.scanDocumentCorners(castToNumberArray(buffer)); - if (corners.length === 0) { - return null; - } - return corners.map(({x, y}) => new Point(x, y)); - } - - /** - * Converts the blob to document given by its |blob| data, |resolution| and - * target |corners| to crop. The output will be converted according to given - * |mimeType|. - * @param {!Blob} blob - * @param {!Array<!Point>} corners - * @param {number} rotation - * @param {!MimeType} mimeType - * @return {!Promise<!Blob>} - */ - async convertToDocument(blob, corners, rotation, mimeType) { - assert(corners.length === 4, 'Unexpected amount of corners'); - const buffer = new Uint8Array(await blob.arrayBuffer()); - let outputFormat; - if (mimeType === MimeType.JPEG) { - outputFormat = DocumentOutputFormat.JPEG; - } else if (mimeType === MimeType.PDF) { - outputFormat = DocumentOutputFormat.PDF; - } else { - throw new Error(`Output mimetype unsupported: ${mimeType}`); - } - - const {docData} = await this.remote_.convertToDocument( - castToNumberArray(buffer), corners, castToMojoRotation(rotation), - outputFormat); - return new Blob([new Uint8Array(docData)], {type: mimeType}); - } - - /** - * Converts given |jpegData| to PDF format. - * @param {!Blob} jpegBlob Blob in JPEG format. - * @return {!Promise<!Blob>} Blob in PDF format. - */ - async convertToPdf(jpegBlob) { - const buffer = new Uint8Array(await jpegBlob.arrayBuffer()); - const {pdfData} = - await this.remote_.convertToPdf(castToNumberArray(buffer)); - return new Blob([new Uint8Array(pdfData)], {type: MimeType.PDF}); - } - - /** - * Creates a new instance of ChromeHelper if it is not set. Returns the - * exist instance. - * @return {!ChromeHelper} The singleton instance. - */ - static getInstance() { - if (instance === null) { - instance = new ChromeHelper(); - } - return instance; - } -}
diff --git a/ash/webui/camera_app_ui/resources/js/mojo/chrome_helper.ts b/ash/webui/camera_app_ui/resources/js/mojo/chrome_helper.ts new file mode 100644 index 0000000..2c1ca18 --- /dev/null +++ b/ash/webui/camera_app_ui/resources/js/mojo/chrome_helper.ts
@@ -0,0 +1,351 @@ +// 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 {assert, assertNotReached} from '../chrome_util.js'; +import {reportError} from '../error.js'; +import {Point} from '../geometry.js'; +import { + ErrorLevel, + ErrorType, + MimeType, +} from '../type.js'; +import {windowController} from '../window_controller.js'; + +import { + CameraAppHelper, + CameraAppHelperRemote, + CameraIntentAction, + CameraUsageOwnershipMonitorCallbackRouter, + DocumentOutputFormat, + ExternalScreenMonitorCallbackRouter, + FileMonitorResult, + Rotation, + ScreenState, + ScreenStateMonitorCallbackRouter, + TabletModeMonitorCallbackRouter, +} from './type.js'; +import {wrapEndpoint} from './util.js'; + +/** + * The singleton instance of ChromeHelper. Initialized by the first + * invocation of getInstance(). + */ +let instance: ChromeHelper|null = null; + +/** + * Forces casting type from Uint8Array to number[]. + */ +function castToNumberArray(data: Uint8Array): number[] { + return data as unknown as number[]; +} + +/** + * Casts from rotation degrees to mojo rotation. + */ +function castToMojoRotation(rotation: number): Rotation { + switch (rotation) { + case 0: + return Rotation.ROTATION_0; + case 90: + return Rotation.ROTATION_90; + case 180: + return Rotation.ROTATION_180; + case 270: + return Rotation.ROTATION_270; + } + assertNotReached(`Invalid rotation ${rotation}`); +} + +/** + * Communicates with Chrome. + */ +export class ChromeHelper { + /** + * An interface remote that is used to communicate with Chrome. + */ + private remote: CameraAppHelperRemote = + wrapEndpoint(CameraAppHelper.getRemote()); + + /** + * Starts tablet mode monitor monitoring tablet mode state of device. + * + * @param onChange Callback called each time when tablet mode state of device + * changes with boolean parameter indicating whether device is entering + * tablet mode. + * @return Resolved to initial state of whether device is is in tablet mode. + */ + async initTabletModeMonitor(onChange: (isTablet: boolean) => void): + Promise<boolean> { + const monitorCallbackRouter = + wrapEndpoint(new TabletModeMonitorCallbackRouter()); + monitorCallbackRouter.update.addListener(onChange); + + const {isTabletMode} = await this.remote.setTabletMonitor( + monitorCallbackRouter.$.bindNewPipeAndPassRemote()); + return isTabletMode; + } + + /** + * Starts monitor monitoring system screen state of device. + * + * @param onChange Callback called each time when device screen state changes + * with parameter of newly changed value. + * @return Resolved to initial system screen state. + */ + async initScreenStateMonitor(onChange: (state: ScreenState) => void): + Promise<ScreenState> { + const monitorCallbackRouter = + wrapEndpoint(new ScreenStateMonitorCallbackRouter()); + monitorCallbackRouter.update.addListener(onChange); + + const {initialState} = await this.remote.setScreenStateMonitor( + monitorCallbackRouter.$.bindNewPipeAndPassRemote()); + return initialState; + } + + /** + * Starts monitor monitoring the existence of external screens. + * @param onChange Callback called when the existence of external screens + * changes. + * @return Resolved to the initial state. + */ + async initExternalScreenMonitor( + onChange: (hasExternalScreen: boolean) => void): Promise<boolean> { + const monitorCallbackRouter = + wrapEndpoint(new ExternalScreenMonitorCallbackRouter()); + monitorCallbackRouter.update.addListener(onChange); + + const {hasExternalScreen} = await this.remote.setExternalScreenMonitor( + monitorCallbackRouter.$.bindNewPipeAndPassRemote()); + return hasExternalScreen; + } + + /** + * Checks if the device is under tablet mode currently. + */ + async isTabletMode(): Promise<boolean> { + const {isTabletMode} = await this.remote.isTabletMode(); + return isTabletMode; + } + + /** + * Starts camera usage monitor. + */ + async initCameraUsageMonitor( + exploitUsage: () => Promise<void>, + releaseUsage: () => Promise<void>): Promise<void> { + const usageCallbackRouter = + wrapEndpoint(new CameraUsageOwnershipMonitorCallbackRouter()); + + usageCallbackRouter.onCameraUsageOwnershipChanged.addListener( + async (hasUsage) => { + if (hasUsage) { + await exploitUsage(); + } else { + await releaseUsage(); + } + }); + + const {isSuccess} = await this.remote.setCameraUsageMonitor( + usageCallbackRouter.$.bindNewPipeAndPassRemote()); + if (!isSuccess) { + throw new Error('Failed to set camera usage monitor'); + } + + let {controller} = await this.remote.getWindowStateController(); + controller = wrapEndpoint(controller); + await windowController.bind(controller); + } + + /** + * Triggers the begin of event tracing in Chrome. + * @param event Name of the event. + */ + startTracing(event: string): void { + this.remote.startPerfEventTrace(event); + } + + /** + * Triggers the end of event tracing in Chrome. + * @param event Name of the event. + */ + stopTracing(event: string): void { + this.remote.stopPerfEventTrace(event); + } + + /** + * Opens the file in Downloads folder by its |name| in gallery. + * @param name Name of the target file. + */ + openFileInGallery(name: string): void { + this.remote.openFileInGallery(name); + } + + /** + * Opens the chrome feedback dialog. + * @param placeholder The text of the placeholder in the description + * field. + */ + openFeedbackDialog(placeholder: string): void { + this.remote.openFeedbackDialog(placeholder); + } + + /** + * Checks return value from |handleCameraResult|. + * @param caller Caller identifier. + */ + private async checkReturn( + caller: string, value: Promise<{isSuccess: boolean}>): Promise<void> { + const {isSuccess} = await value; + if (!isSuccess) { + reportError( + ErrorType.HANDLE_CAMERA_RESULT_FAILURE, ErrorLevel.ERROR, + new Error(`Return not isSuccess from calling intent ${caller}.`)); + } + } + + /** + * Notifies ARC++ to finish the intent. + * @param intentId Intent id of the intent to be finished. + */ + async finish(intentId: number): Promise<void> { + const ret = + this.remote.handleCameraResult(intentId, CameraIntentAction.FINISH, []); + await this.checkReturn('finish()', ret); + } + + /** + * Notifies ARC++ to append data to intent result. + * @param intentId Intent id of the intent to be appended data to. + * @param data The data to be appended to intent result. + */ + async appendData(intentId: number, data: Uint8Array): Promise<void> { + const ret = this.remote.handleCameraResult( + intentId, CameraIntentAction.APPEND_DATA, castToNumberArray(data)); + await this.checkReturn('appendData()', ret); + } + + /** + * Notifies ARC++ to clear appended intent result data. + * @param intentId Intent id of the intent to be cleared its result. + */ + async clearData(intentId: number): Promise<void> { + const ret = this.remote.handleCameraResult( + intentId, CameraIntentAction.CLEAR_DATA, []); + await this.checkReturn('clearData()', ret); + } + + /** + * Checks if the logging consent option is enabled. + */ + async isMetricsAndCrashReportingEnabled(): Promise<boolean> { + const {isEnabled} = await this.remote.isMetricsAndCrashReportingEnabled(); + return isEnabled; + } + + /** + * Sends the broadcast to ARC to notify the new photo/video is captured. + */ + async sendNewCaptureBroadcast( + {isVideo, name}: {isVideo: boolean, name: string}): Promise<void> { + this.remote.sendNewCaptureBroadcast(isVideo, name); + } + + /** + * Monitors for the file deletion of the file given by its |name| and triggers + * |callback| when the file is deleted. Note that a previous monitor request + * will be canceled once another monitor request is sent. + * @param name The name of the file to monitor. + * @param callback Function to trigger when deletion. + * @return Resolved when the file is deleted or the current monitor is + * canceled by future monitor call. + * @throws {!Error} When error occurs during monitor. + */ + async monitorFileDeletion(name: string, callback: () => void): Promise<void> { + const {result} = await this.remote.monitorFileDeletion(name); + switch (result) { + case FileMonitorResult.DELETED: + callback(); + return; + case FileMonitorResult.CANCELED: + // Do nothing if it is canceled by another monitor call. + return; + case FileMonitorResult.ERROR: + throw new Error('Error happens when monitoring file deletion'); + } + } + + /** + * Returns true if the document mode is supported on the device. + */ + async isDocumentModeSupported(): Promise<boolean> { + const {isSupported} = await this.remote.isDocumentModeSupported(); + return isSupported; + } + + /** + * Scans the blob data and returns the detected document corners. + * @param blob + * @return Promise resolve to positions of document corner. Null for failing + * to detected corner positions. + */ + async scanDocumentCorners(blob: Blob): Promise<Point[]|null> { + const buffer = new Uint8Array(await blob.arrayBuffer()); + + const {corners} = + await this.remote.scanDocumentCorners(castToNumberArray(buffer)); + if (corners.length === 0) { + return null; + } + return corners.map(({x, y}) => new Point(x, y)); + } + + /** + * Converts the blob to document given by its |blob| data, |resolution| and + * target |corners| to crop. The output will be converted according to given + * |mimeType|. + */ + async convertToDocument( + blob: Blob, corners: Point[], rotation: number, + mimeType: MimeType): Promise<Blob> { + assert(corners.length === 4, 'Unexpected amount of corners'); + const buffer = new Uint8Array(await blob.arrayBuffer()); + let outputFormat; + if (mimeType === MimeType.JPEG) { + outputFormat = DocumentOutputFormat.JPEG; + } else if (mimeType === MimeType.PDF) { + outputFormat = DocumentOutputFormat.PDF; + } else { + throw new Error(`Output mimetype unsupported: ${mimeType}`); + } + + const {docData} = await this.remote.convertToDocument( + castToNumberArray(buffer), corners, castToMojoRotation(rotation), + outputFormat); + return new Blob([new Uint8Array(docData)], {type: mimeType}); + } + + /** + * Converts given |jpegData| to PDF format. + * @param jpegBlob Blob in JPEG format. + * @return Blob in PDF format. + */ + async convertToPdf(jpegBlob: Blob): Promise<Blob> { + const buffer = new Uint8Array(await jpegBlob.arrayBuffer()); + const {pdfData} = await this.remote.convertToPdf(castToNumberArray(buffer)); + return new Blob([new Uint8Array(pdfData)], {type: MimeType.PDF}); + } + + /** + * Creates a new instance of ChromeHelper if it is not set. Returns the + * exist instance. + * @return The singleton instance. + */ + static getInstance(): ChromeHelper { + if (instance === null) { + instance = new ChromeHelper(); + } + return instance; + } +}
diff --git a/ash/webui/camera_app_ui/resources/js/mojo/device_operator.js b/ash/webui/camera_app_ui/resources/js/mojo/device_operator.js index 2189c78..02fde48 100644 --- a/ash/webui/camera_app_ui/resources/js/mojo/device_operator.js +++ b/ash/webui/camera_app_ui/resources/js/mojo/device_operator.js
@@ -2,33 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import { - CameraAppDeviceProvider, - CameraAppDeviceProviderRemote, // eslint-disable-line no-unused-vars - CameraAppDeviceRemote, // eslint-disable-line no-unused-vars - CameraEventObserverCallbackRouter, - CaptureIntent, // eslint-disable-line no-unused-vars - DocumentCornersObserverCallbackRouter, - Effect, // eslint-disable-line no-unused-vars - GetCameraAppDeviceStatus, - ReprocessResultListenerCallbackRouter, - ResultMetadataObserverCallbackRouter, - StreamType, // eslint-disable-line no-unused-vars -} from '/media/capture/video/chromeos/mojom/camera_app.mojom-webui.js'; -import { - CameraFacing, -} from '/media/capture/video/chromeos/mojom/camera_common.mojom-webui.js'; -import { - CameraMetadata, // eslint-disable-line no-unused-vars - CameraMetadataEntry, // eslint-disable-line no-unused-vars - EntryType, -} from '/media/capture/video/chromeos/mojom/camera_metadata.mojom-webui.js'; -import { - AndroidInfoSupportedHardwareLevel, - CameraMetadataTag, -} from - '/media/capture/video/chromeos/mojom/camera_metadata_tags.mojom-webui.js'; - import {AsyncJobQueue} from '../async_job_queue.js'; import {assert, assertNotReached} from '../chrome_util.js'; import {reportError} from '../error.js'; @@ -44,6 +17,26 @@ } from '../type.js'; import {WaitableEvent} from '../waitable_event.js'; + +import { + AndroidInfoSupportedHardwareLevel, + CameraAppDeviceProvider, + CameraAppDeviceProviderRemote, // eslint-disable-line no-unused-vars + CameraAppDeviceRemote, // eslint-disable-line no-unused-vars + CameraEventObserverCallbackRouter, + CameraFacing, + CameraMetadata, // eslint-disable-line no-unused-vars + CameraMetadataEntry, // eslint-disable-line no-unused-vars + CameraMetadataTag, + CaptureIntent, // eslint-disable-line no-unused-vars + DocumentCornersObserverCallbackRouter, + Effect, // eslint-disable-line no-unused-vars + EntryType, + GetCameraAppDeviceStatus, + ReprocessResultListenerCallbackRouter, + ResultMetadataObserverCallbackRouter, + StreamType, // eslint-disable-line no-unused-vars +} from './type.js'; import { closeEndpoint, MojoEndpoint, // eslint-disable-line no-unused-vars
diff --git a/ash/webui/camera_app_ui/resources/js/mojo/image_capture.js b/ash/webui/camera_app_ui/resources/js/mojo/image_capture.js index 50c1f17..1da0af0 100644 --- a/ash/webui/camera_app_ui/resources/js/mojo/image_capture.js +++ b/ash/webui/camera_app_ui/resources/js/mojo/image_capture.js
@@ -2,14 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import { - Effect, // eslint-disable-line no-unused-vars -} from '/media/capture/video/chromeos/mojom/camera_app.mojom-webui.js'; - import {bitmapToJpegBlob} from '../util.js'; import {WaitableEvent} from '../waitable_event.js'; import {DeviceOperator} from './device_operator.js'; +// eslint-disable-next-line no-unused-vars +import {Effect} from './type.js'; import {closeEndpoint} from './util.js'; /**
diff --git a/ash/webui/camera_app_ui/resources/js/mojo/type.ts b/ash/webui/camera_app_ui/resources/js/mojo/type.ts new file mode 100644 index 0000000..f4d4c1b1 --- /dev/null +++ b/ash/webui/camera_app_ui/resources/js/mojo/type.ts
@@ -0,0 +1,71 @@ +// 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. + +// This file contains many long export lines that exceed the max-len limit */ +/* eslint-disable max-len */ + +export { + CameraIntentAction, +} from '/mojom-webui/ash/components/arc/mojom/camera_intent.mojom-webui.js'; +export { + CameraAppHelper, + CameraAppHelperRemote, + CameraUsageOwnershipMonitorCallbackRouter, + DocumentOutputFormat, + ExternalScreenMonitorCallbackRouter, + FileMonitorResult, + ScreenState, + ScreenStateMonitorCallbackRouter, + TabletModeMonitorCallbackRouter, + WindowStateControllerRemote, + WindowStateMonitorCallbackRouter, + WindowStateType, +} from '/mojom-webui/ash/webui/camera_app_ui/camera_app_helper.mojom-webui.js'; +export { + Rotation, +} from + '/mojom-webui/chromeos/services/machine_learning/public/mojom/document_scanner_param_types.mojom-webui.js'; +export { + Blob as MojoBlob, +} from '/mojom-webui/media/capture/mojom/image_capture.mojom-webui.js'; +export { + CameraAppDeviceProvider, + CameraAppDeviceProviderRemote, + CameraAppDeviceRemote, + CameraEventObserverCallbackRouter, + CaptureIntent, + DocumentCornersObserverCallbackRouter, + Effect, + GetCameraAppDeviceStatus, + ReprocessResultListenerCallbackRouter, + ResultMetadataObserverCallbackRouter, + StreamType, +} from + '/mojom-webui/media/capture/video/chromeos/mojom/camera_app.mojom-webui.js'; +export { + CameraFacing, +} from + '/mojom-webui/media/capture/video/chromeos/mojom/camera_common.mojom-webui.js'; +export { + CameraMetadata, + CameraMetadataEntry, + EntryType, +} from + '/mojom-webui/media/capture/video/chromeos/mojom/camera_metadata.mojom-webui.js'; +export { + AndroidControlAeAntibandingMode, + AndroidControlAeMode, + AndroidControlAeState, + AndroidControlAfMode, + AndroidControlAfState, + AndroidControlAwbMode, + AndroidControlAwbState, + AndroidInfoSupportedHardwareLevel, + AndroidStatisticsFaceDetectMode, + CameraMetadataTag, +} from + '/mojom-webui/media/capture/video/chromeos/mojom/camera_metadata_tags.mojom-webui.js'; +export { + PointF, +} from 'chrome://resources/mojo/ui/gfx/geometry/mojom/geometry.mojom-webui.js';
diff --git a/ash/webui/camera_app_ui/resources/js/mojo/util.js b/ash/webui/camera_app_ui/resources/js/mojo/util.ts similarity index 61% rename from ash/webui/camera_app_ui/resources/js/mojo/util.js rename to ash/webui/camera_app_ui/resources/js/mojo/util.ts index db149fd..a9f0ece 100644 --- a/ash/webui/camera_app_ui/resources/js/mojo/util.js +++ b/ash/webui/camera_app_ui/resources/js/mojo/util.ts
@@ -7,30 +7,34 @@ const windowUnload = new WaitableEvent(); -/** - * @typedef {{$: {close: function(): void}}} - */ -export let MojoEndpoint; - +export interface MojoEndpoint { + $: {close: () => void}; +} addUnloadCallback(() => { windowUnload.signal(); }); +const NEVER_SETTLED_PROMISE: Promise<never> = new Promise( + () => { + // This doesn't call the resolve function so the Promise will never + // be resolved or rejected. + }); + /** * Wraps a mojo response promise so that we can handle the situation when the * call is dropped by window unload gracefully. - * @param {!Promise|undefined} call - * @return {!Promise} Returns the mojo response which will be resolved when - * getting response or will never be resolved if the window unload is about - * to happen. + * + * @return Returns the mojo response which will be resolved when getting + * response or will never be resolved if the window unload is about to + * happen. */ -async function wrapMojoResponse(call) { +async function wrapMojoResponse<T>(call: Promise<T>|undefined): Promise<T> { const result = await Promise.race([windowUnload.wait(), call]); if (windowUnload.isSignaled()) { - return new Promise(() => {}); + return NEVER_SETTLED_PROMISE; } - return result; + return result as T; } const mojoResponseHandler = { @@ -41,7 +45,7 @@ // Don't try to call the mojo function if window is already unloaded, // since the connection would have already been closed, and there // would be uncaught exception if we try to call the mojo function. - return new Promise(() => {}); + return NEVER_SETTLED_PROMISE; } return wrapMojoResponse(target[property](...args)); }; @@ -53,27 +57,24 @@ /** * Closes the given mojo endpoint once the page is unloaded. * Reference b/176139064. - * @param {!MojoEndpoint} endpoint The mojo endpoint. + * @param endpoint The mojo endpoint. */ -function closeWhenUnload(endpoint) { +function closeWhenUnload(endpoint: MojoEndpoint) { addUnloadCallback(() => closeEndpoint(endpoint)); } /** * Returns a mojo |endpoint| and returns a proxy of it. - * @param {!T} endpoint - * @return {!T} The proxy of the given endoiubt. - * @template T + * @return The proxy of the given endpoint. */ -export function wrapEndpoint(endpoint) { +export function wrapEndpoint<T extends MojoEndpoint>(endpoint: T): T { closeWhenUnload(endpoint); - return /** @type {!T} */ (new Proxy(endpoint, mojoResponseHandler)); + return new Proxy(endpoint, mojoResponseHandler); } /** * Returns the target mojo endpoint. - * @param {!MojoEndpoint} endpoint */ -export function closeEndpoint(endpoint) { +export function closeEndpoint(endpoint: MojoEndpoint): void { endpoint.$.close(); }
diff --git a/ash/webui/camera_app_ui/resources/js/test_bridge.js b/ash/webui/camera_app_ui/resources/js/test_bridge.js index 3c68783..3a070c3c 100644 --- a/ash/webui/camera_app_ui/resources/js/test_bridge.js +++ b/ash/webui/camera_app_ui/resources/js/test_bridge.js
@@ -62,10 +62,10 @@ /** * Triggers when the Shared Worker is connected. - * @param {!Event} event + * @param {!MessageEvent} event */ sharedWorkerScope.onconnect = (event) => { - const port = /** @type {!MessageEvent} */ (event).ports[0]; + const port = event.ports[0]; Comlink.expose( { bindWindow,
diff --git a/ash/webui/camera_app_ui/resources/js/type.js b/ash/webui/camera_app_ui/resources/js/type.js index 93c59c5..74019be7 100644 --- a/ash/webui/camera_app_ui/resources/js/type.js +++ b/ash/webui/camera_app_ui/resources/js/type.js
@@ -229,8 +229,9 @@ /** * @typedef {{ - * hasError: (boolean|undefined), - * resolution: (!Resolution|undefined), + * hasError?: boolean, + * resolution?: !Resolution, + * facing?: !Facing, * }} */ export let PerfInformation; @@ -239,7 +240,7 @@ * @typedef {{ * event: !PerfEvent, * duration: number, - * perfInfo: (!PerfInformation|undefined), + * perfInfo?: !PerfInformation, * }} */ export let PerfEntry;
diff --git a/ash/webui/camera_app_ui/resources/js/util.js b/ash/webui/camera_app_ui/resources/js/util.js index f2855bf5..d0213dc6 100644 --- a/ash/webui/camera_app_ui/resources/js/util.js +++ b/ash/webui/camera_app_ui/resources/js/util.js
@@ -258,7 +258,7 @@ */ export class DelayInterval { /** - * @param {function()} callback + * @param {function(): void} callback * @param {number} delayMs Delay milliseconds at start. * @param {number} intervalMs Interval in milliseconds. * @public
diff --git a/ash/webui/camera_app_ui/resources/js/views/camera.js b/ash/webui/camera_app_ui/resources/js/views/camera.js index cd821c0..5d89a65 100644 --- a/ash/webui/camera_app_ui/resources/js/views/camera.js +++ b/ash/webui/camera_app_ui/resources/js/views/camera.js
@@ -2,10 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import { - ScreenState, -} from '/ash/webui/camera_app_ui/camera_app_helper.mojom-webui.js'; - import * as animate from '../animation.js'; import { assert, @@ -31,6 +27,7 @@ import {ResultSaver} from '../models/result_saver.js'; import {ChromeHelper} from '../mojo/chrome_helper.js'; import {DeviceOperator} from '../mojo/device_operator.js'; +import {ScreenState} from '../mojo/type.js'; import * as nav from '../nav.js'; import * as newFeatureToast from '../new_feature_toast.js'; // eslint-disable-next-line no-unused-vars
diff --git a/ash/webui/camera_app_ui/resources/js/views/camera/document_corner_overlay.js b/ash/webui/camera_app_ui/resources/js/views/camera/document_corner_overlay.js index d75356a..f77c236 100644 --- a/ash/webui/camera_app_ui/resources/js/views/camera/document_corner_overlay.js +++ b/ash/webui/camera_app_ui/resources/js/views/camera/document_corner_overlay.js
@@ -25,6 +25,15 @@ const BASE_LENGTH = 100; /** + * @typedef {{ + * position?: !Point, + * angle?: number, + * length?: number + * }} + */ +let PlaceParams; // eslint-disable-line no-unused-vars + +/** * Controller for placing line-like element. */ class Line { @@ -40,10 +49,9 @@ } /** - * @param {{position: (!Point|undefined), angle: (number|undefined), length: - * (number|undefined)}} params 'position' is the x, y coordinates of start - * endpoint in px. 'angle' is the rotate angle in rad. 'length' is the - * length of the line. + * @param {PlaceParams} params + * 'position' is the x, y coordinates of start endpoint in px. 'angle' is + * the rotate angle in rad. 'length' is the length of the line. */ place({position, angle, length}) { const transforms = [];
diff --git a/ash/webui/camera_app_ui/resources/js/views/camera/mode/index.js b/ash/webui/camera_app_ui/resources/js/views/camera/mode/index.js index 03e99e7..ae5ddea 100644 --- a/ash/webui/camera_app_ui/resources/js/views/camera/mode/index.js +++ b/ash/webui/camera_app_ui/resources/js/views/camera/mode/index.js
@@ -3,12 +3,9 @@ // found in the LICENSE file. import { - CaptureIntent, -} from '/media/capture/video/chromeos/mojom/camera_app.mojom-webui.js'; - -import { assert, assertInstanceof, + assertNotReached, } from '../../../chrome_util.js'; import { CaptureCandidate, // eslint-disable-line no-unused-vars @@ -21,6 +18,7 @@ import * as dom from '../../../dom.js'; // eslint-disable-next-line no-unused-vars import {DeviceOperator} from '../../../mojo/device_operator.js'; +import {CaptureIntent} from '../../../mojo/type.js'; import * as state from '../../../state.js'; import { Facing, // eslint-disable-line no-unused-vars @@ -82,7 +80,9 @@ * is supported by video device with specified device id. * @abstract */ - async isSupported(deviceId) {} + async isSupported(deviceId) { + assertNotReached(); + } /** * @param {!Resolution} captureResolution @@ -90,7 +90,9 @@ * @return {boolean} * @abstract */ - isSupportPTZ(captureResolution, previewResolution) {} + isSupportPTZ(captureResolution, previewResolution) { + assertNotReached(); + } /** * Makes video capture device prepared for capturing in this mode. @@ -100,7 +102,9 @@ * @return {!Promise} * @abstract */ - async prepareDevice(constraints, captureResolution) {} + async prepareDevice(constraints, captureResolution) { + assertNotReached(); + } /** * Get general stream constraints of this mode for fake cameras. @@ -108,7 +112,9 @@ * @return {!Array<!StreamConstraints>} * @abstract */ - getConstraintsForFakeCamera(deviceId) {} + getConstraintsForFakeCamera(deviceId) { + assertNotReached(); + } /* eslint-disable getter-return */ @@ -117,21 +123,27 @@ * @return {!ModeFactory} * @abstract */ - getCaptureFactory() {} + getCaptureFactory() { + assertNotReached(); + } /** * HALv3 constraints preferrer for this mode. * @return {!ConstraintsPreferrer} * @abstract */ - get constraintsPreferrer() {} + get constraintsPreferrer() { + return assertNotReached(); + } /** * Mode to be fallbacked to when fail to configure this mode. * @return {!Mode} * @abstract */ - get fallbackMode() {} + get fallbackMode() { + return assertNotReached(); + } /* eslint-enable getter-return */ }
diff --git a/ash/webui/camera_app_ui/resources/js/views/camera/mode/mode_base.js b/ash/webui/camera_app_ui/resources/js/views/camera/mode/mode_base.js index 8012843..98b1a5f 100644 --- a/ash/webui/camera_app_ui/resources/js/views/camera/mode/mode_base.js +++ b/ash/webui/camera_app_ui/resources/js/views/camera/mode/mode_base.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 {assertInstanceof} from '../../../chrome_util.js'; +import {assertInstanceof, assertNotReached} from '../../../chrome_util.js'; // eslint-disable-next-line no-unused-vars import {StreamConstraints} from '../../../device/stream_constraints.js'; import * as error from '../../../error.js'; @@ -110,7 +110,9 @@ * @protected * @abstract */ - async start_() {} + async start_() { + assertNotReached(); + } /** * Stops the ongoing capture operation under this mode. @@ -185,5 +187,7 @@ * @return {!ModeBase} * @abstract */ - produce() {} + produce() { + assertNotReached(); + } }
diff --git a/ash/webui/camera_app_ui/resources/js/views/camera/mode/photo.js b/ash/webui/camera_app_ui/resources/js/views/camera/mode/photo.js index afcea0d5..a222744 100644 --- a/ash/webui/camera_app_ui/resources/js/views/camera/mode/photo.js +++ b/ash/webui/camera_app_ui/resources/js/views/camera/mode/photo.js
@@ -2,14 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import { - StreamType, -} from '/media/capture/video/chromeos/mojom/camera_app.mojom-webui.js'; -import { - CameraMetadataTag, -} from - '/media/capture/video/chromeos/mojom/camera_metadata_tags.mojom-webui.js'; - +import {assertNotReached} from '../../../chrome_util.js'; // eslint-disable-next-line no-unused-vars import {StreamConstraints} from '../../../device/stream_constraints.js'; import {I18nString} from '../../../i18n_string.js'; @@ -18,6 +11,10 @@ import {DeviceOperator, parseMetadata} from '../../../mojo/device_operator.js'; import {CrosImageCapture} from '../../../mojo/image_capture.js'; import { + CameraMetadataTag, + StreamType, +} from '../../../mojo/type.js'; +import { closeEndpoint, MojoEndpoint, // eslint-disable-line no-unused-vars } from '../../../mojo/util.js'; @@ -38,7 +35,7 @@ * @typedef {{ * resolution: !Resolution, * blob: !Blob, - * isVideoSnapshot: (boolean|undefined), + * isVideoSnapshot?: boolean, * }} */ export let PhotoResult; @@ -56,7 +53,9 @@ * @return {!Promise} * @abstract */ - handleResultPhoto(photo, name) {} + handleResultPhoto(photo, name) { + assertNotReached(); + } /** * Plays UI effect when taking photo. @@ -67,7 +66,9 @@ * @return {!Promise} * @abstract */ - waitPreviewReady() {} + waitPreviewReady() { + assertNotReached(); + } } /**
diff --git a/ash/webui/camera_app_ui/resources/js/views/camera/mode/portrait.js b/ash/webui/camera_app_ui/resources/js/views/camera/mode/portrait.js index d808815..2ef5166b 100644 --- a/ash/webui/camera_app_ui/resources/js/views/camera/mode/portrait.js +++ b/ash/webui/camera_app_ui/resources/js/views/camera/mode/portrait.js
@@ -2,13 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import { - Effect, -} from '/media/capture/video/chromeos/mojom/camera_app.mojom-webui.js'; - import {I18nString} from '../../../i18n_string.js'; import {Filenamer} from '../../../models/file_namer.js'; import {CrosImageCapture} from '../../../mojo/image_capture.js'; +import {Effect} from '../../../mojo/type.js'; import * as state from '../../../state.js'; import * as toast from '../../../toast.js'; import {
diff --git a/ash/webui/camera_app_ui/resources/js/views/camera/mode/record_time.js b/ash/webui/camera_app_ui/resources/js/views/camera/mode/record_time.js index a6559144..3a4598d 100644 --- a/ash/webui/camera_app_ui/resources/js/views/camera/mode/record_time.js +++ b/ash/webui/camera_app_ui/resources/js/views/camera/mode/record_time.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 {assertNotReached} from '../../../chrome_util.js'; import * as dom from '../../../dom.js'; import {I18nString} from '../../../i18n_string.js'; import * as loadTimeData from '../../../models/load_time_data.js'; @@ -73,7 +74,9 @@ * @protected * @abstract */ - getTimeInterval_() {} + getTimeInterval_() { + assertNotReached(); + } /** * @param {number} ticks Aggregated time ticks during the record time. @@ -82,7 +85,9 @@ * @protected * @abstract */ - getTimeMessage_(ticks) {} + getTimeMessage_(ticks) { + assertNotReached(); + } /** * Updates UI by the elapsed recording time.
diff --git a/ash/webui/camera_app_ui/resources/js/views/camera/mode/scan.js b/ash/webui/camera_app_ui/resources/js/views/camera/mode/scan.js index cf70c57..3bfbaed4 100644 --- a/ash/webui/camera_app_ui/resources/js/views/camera/mode/scan.js +++ b/ash/webui/camera_app_ui/resources/js/views/camera/mode/scan.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 {assertNotReached} from '../../../chrome_util.js'; // eslint-disable-next-line no-unused-vars import {StreamConstraints} from '../../../device/stream_constraints.js'; import {Point} from '../../../geometry.js'; @@ -60,8 +61,11 @@ export class ScanHandler { /** * Plays UI effect when taking photo. + * @abstract */ - playShutterEffect() {} + playShutterEffect() { + assertNotReached(); + } /** * @param {!ImageBlob} originImage Original photo to be cropped document from. @@ -71,8 +75,11 @@ * @return {!Promise<?{docBlob: !Blob, mimeType: !MimeType}>} Returns the * processed document blob and which mime type user choose to save. Null * for cancel document. + * @abstract */ - async reviewDocument(originImage, refCorners) {} + async reviewDocument(originImage, refCorners) { + assertNotReached(); + } /** * Handles the result document. @@ -81,13 +88,17 @@ * @return {!Promise} * @abstract */ - handleResultDocument(result, name) {} + handleResultDocument(result, name) { + assertNotReached(); + } /** * @return {!Promise} * @abstract */ - waitPreviewReady() {} + waitPreviewReady() { + assertNotReached(); + } } /**
diff --git a/ash/webui/camera_app_ui/resources/js/views/camera/mode/video.js b/ash/webui/camera_app_ui/resources/js/views/camera/mode/video.js index 2ac44ff..9345655 100644 --- a/ash/webui/camera_app_ui/resources/js/views/camera/mode/video.js +++ b/ash/webui/camera_app_ui/resources/js/views/camera/mode/video.js
@@ -3,7 +3,11 @@ // found in the LICENSE file. import {AsyncJobQueue} from '../../../async_job_queue.js'; -import {assert, assertInstanceof} from '../../../chrome_util.js'; +import { + assert, + assertInstanceof, + assertNotReached, +} from '../../../chrome_util.js'; // eslint-disable-next-line no-unused-vars import {StreamConstraints} from '../../../device/stream_constraints.js'; import { @@ -170,7 +174,9 @@ * @return {!Promise<!VideoSaver>} * @abstract */ - createVideoSaver() {} + createVideoSaver() { + assertNotReached(); + } /** * Handles the result video. @@ -178,7 +184,9 @@ * @return {!Promise} * @abstract */ - handleResultVideo(video) {} + handleResultVideo(video) { + assertNotReached(); + } /** * Handles the result gif video. @@ -186,7 +194,9 @@ * @return {!Promise} * @abstract */ - handleResultGif(result) {} + handleResultGif(result) { + assertNotReached(); + } /** * Handles the result video snapshot. @@ -195,19 +205,25 @@ * @return {!Promise} * @abstract */ - handleResultPhoto(photo, name) {} + handleResultPhoto(photo, name) { + assertNotReached(); + } /** * Plays UI effect when doing video snapshot. */ - playShutterEffect() {} + playShutterEffect() { + assertNotReached(); + } /** * Gets preview video element. * @return {!HTMLVideoElement} * @abstract */ - getPreviewVideo() {} + getPreviewVideo() { + assertNotReached(); + } } /**
diff --git a/ash/webui/camera_app_ui/resources/js/views/camera/preview.js b/ash/webui/camera_app_ui/resources/js/views/camera/preview.js index 10f7200..70ca31c 100644 --- a/ash/webui/camera_app_ui/resources/js/views/camera/preview.js +++ b/ash/webui/camera_app_ui/resources/js/views/camera/preview.js
@@ -2,9 +2,15 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import {assert, assertInstanceof} from '../../chrome_util.js'; import { - StreamType, -} from '/media/capture/video/chromeos/mojom/camera_app.mojom-webui.js'; + StreamConstraints, // eslint-disable-line no-unused-vars + toMediaStreamConstraints, +} from '../../device/stream_constraints.js'; +import * as dom from '../../dom.js'; +import {reportError} from '../../error.js'; +import {FaceOverlay} from '../../face.js'; +import {DeviceOperator, parseMetadata} from '../../mojo/device_operator.js'; import { AndroidControlAeAntibandingMode, AndroidControlAeMode, @@ -15,18 +21,8 @@ AndroidControlAwbState, AndroidStatisticsFaceDetectMode, CameraMetadataTag, -} from - '/media/capture/video/chromeos/mojom/camera_metadata_tags.mojom-webui.js'; - -import {assert, assertInstanceof} from '../../chrome_util.js'; -import { - StreamConstraints, // eslint-disable-line no-unused-vars - toMediaStreamConstraints, -} from '../../device/stream_constraints.js'; -import * as dom from '../../dom.js'; -import {reportError} from '../../error.js'; -import {FaceOverlay} from '../../face.js'; -import {DeviceOperator, parseMetadata} from '../../mojo/device_operator.js'; + StreamType, +} from '../../mojo/type.js'; import { closeEndpoint, MojoEndpoint, // eslint-disable-line no-unused-vars @@ -543,6 +539,15 @@ AndroidControlAeAntibandingMode, 'ANDROID_CONTROL_AE_ANTIBANDING_MODE_'); + let sensorSensitivity = null; + let sensorSensitivityBoost = 100; + const getSensitivity = () => { + if (sensorSensitivity === null) { + return 'N/A'; + } + return sensorSensitivity * sensorSensitivityBoost / 100; + }; + const tag = CameraMetadataTag; /** @type {!Object<string, function(!Array<number>): void>} */ const metadataEntryHandlers = { @@ -558,7 +563,13 @@ showValue('#preview-af-state', afStateName.get(value)); }, [tag.ANDROID_SENSOR_SENSITIVITY]: ([value]) => { - const sensitivity = value; + sensorSensitivity = value; + const sensitivity = getSensitivity(); + showValue('#preview-sensitivity', `ISO ${sensitivity}`); + }, + [tag.ANDROID_CONTROL_POST_RAW_SENSITIVITY_BOOST]: ([value]) => { + sensorSensitivityBoost = value; + const sensitivity = getSensitivity(); showValue('#preview-sensitivity', `ISO ${sensitivity}`); }, [tag.ANDROID_SENSOR_EXPOSURE_TIME]: ([value]) => {
diff --git a/ash/webui/camera_app_ui/resources/js/views/camera_intent.js b/ash/webui/camera_app_ui/resources/js/views/camera_intent.js index 515ab38..6421c55a 100644 --- a/ash/webui/camera_app_ui/resources/js/views/camera_intent.js +++ b/ash/webui/camera_app_ui/resources/js/views/camera_intent.js
@@ -74,6 +74,9 @@ finishSaveVideo: async (video) => { this.videoResultFile_ = await video.endWrite(); }, + saveGif: () => { + assertNotReached(); + }, }); super( resultSaver, infoUpdater, photoPreferrer, videoPreferrer, mode,
diff --git a/ash/webui/camera_app_ui/resources/js/views/crop_document.js b/ash/webui/camera_app_ui/resources/js/views/crop_document.js index 6ebb9bf..eab8acd 100644 --- a/ash/webui/camera_app_ui/resources/js/views/crop_document.js +++ b/ash/webui/camera_app_ui/resources/js/views/crop_document.js
@@ -185,7 +185,7 @@ * @const {!SVGPolygonElement} * @private */ - this.cropArea_ = dom.getFrom(this.image_, '.crop-area', SVGPolygonElement); + this.cropArea_ = dom.getFrom(this.image, '.crop-area', SVGPolygonElement); /** * Index of |ROTATION| as current photo rotation. @@ -214,7 +214,7 @@ pt: new Point(0, 0), pointerId: null, }); - this.image_.appendChild(tpl); + this.image.appendChild(tpl); } return ret; })(); @@ -334,14 +334,14 @@ // Stop dragging. for (const eventName of ['pointerup', 'pointerleave', 'pointercancel']) { - this.image_.addEventListener(eventName, (e) => { + this.image.addEventListener(eventName, (e) => { e.preventDefault(); this.clearDragging_(assertInstanceof(e, PointerEvent).pointerId); }); } // Move drag corner. - this.image_.addEventListener('pointermove', (e) => { + this.image.addEventListener('pointermove', (e) => { e.preventDefault(); const pointerId = assertInstanceof(e, PointerEvent).pointerId; @@ -371,7 +371,7 @@ }); // Prevent contextmenu popup triggered by long touch. - this.image_.addEventListener('contextmenu', (e) => { + this.image.addEventListener('contextmenu', (e) => { if (e['pointerType'] === 'touch') { e.preventDefault(); } @@ -582,7 +582,7 @@ updateImage_() { const {width: frameW, height: frameH} = this.frameSize_; const {width: rawImageW, height: rawImageH} = this.imageOriginalSize_; - const style = this.image_.attributeStyleMap; + const style = this.image.attributeStyleMap; let rotatedW = rawImageW; let rotatedH = rawImageH; @@ -634,9 +634,9 @@ */ async setReviewPhoto(blob) { const image = new Image(); - await this.loadImage_(image, blob); + await this.loadImage(image, blob); this.imageOriginalSize_ = new Size(image.width, image.height); - const style = this.image_.attributeStyleMap; + const style = this.image.attributeStyleMap; if (style.has('background-image')) { const oldUrl = style.get('background-image') .toString()
diff --git a/ash/webui/camera_app_ui/resources/js/views/dialog.js b/ash/webui/camera_app_ui/resources/js/views/dialog.js index 679f2ac4..1432f43 100644 --- a/ash/webui/camera_app_ui/resources/js/views/dialog.js +++ b/ash/webui/camera_app_ui/resources/js/views/dialog.js
@@ -53,7 +53,7 @@ /** * @override */ - entering({message, cancellable = false} = {}) { + entering({message = undefined, cancellable = false} = {}) { if (message !== undefined) { this.messageHolder_.textContent = assertString(message); }
diff --git a/ash/webui/camera_app_ui/resources/js/views/review.js b/ash/webui/camera_app_ui/resources/js/views/review.js deleted file mode 100644 index fdc5457..0000000 --- a/ash/webui/camera_app_ui/resources/js/views/review.js +++ /dev/null
@@ -1,196 +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. - -import {assertInstanceof} from '../chrome_util.js'; -import * as dom from '../dom.js'; -// eslint-disable-next-line no-unused-vars -import {I18nString} from '../i18n_string.js'; -import * as nav from '../nav.js'; -import {ViewName} from '../type.js'; -import {instantiateTemplate, setupI18nElements} from '../util.js'; -import {WaitableEvent} from '../waitable_event.js'; - -import {View} from './view.js'; - -/** - * Available option show in this view. - * @template T - */ -export class Option { - /** - * @param {!I18nString} text Text string show on the option button. - * @param {{ - * exitValue: (?T|undefined), - * callback: (function()|undefined), - * hasPopup: (boolean|undefined), - * }} handlerParams Sets |exitValue| if the review page will exit with this - * value when option selected. Sets |callback| for the function get executed - * when option selected. - */ - constructor(text, {exitValue, callback, hasPopup}) { - /** - * @const {!I18nString} - */ - this.text = text; - - /** - * @const {(?T|undefined)} - */ - this.exitValue = exitValue; - - /** - * @const {?boolean} - */ - this.hasPopup = hasPopup ?? null; - - /** - * @const {?function()} - */ - this.callback = callback || null; - } -} - -/** - * Options for reviewing. - * @template T - */ -export class Options { - /** - * @param {...!Option<?T>} options - */ - constructor(...options) { - /** - * @const {!Array<!Option<?T>>} - */ - this.options = options; - } -} - -/** - * View controller for review page. - */ -export class Review extends View { - /** - * @param {!ViewName=} viewName - * @public - */ - constructor(viewName = ViewName.REVIEW) { - super(viewName, {defaultFocusSelector: '.primary'}); - - /** - * @private {!ViewName} - * @const - */ - this.viewName_ = viewName; - - /** - * @protected {!HTMLElement} - * @const - */ - this.image_ = dom.getFrom(this.root, '.review-image', HTMLElement); - - /** - * @private {!HTMLDivElement} - * @const - */ - this.positiveBtns_ = - dom.getFrom(this.root, '.positive.button-group', HTMLDivElement); - - /** - * @private {!HTMLDivElement} - * @const - */ - this.negativeBtns_ = - dom.getFrom(this.root, '.negative.button-group', HTMLDivElement); - - /** - * @private {?HTMLButtonElement} - */ - this.primaryBtn_ = null; - } - - /** - * @param {!Image|!HTMLImageElement} image - * @param {!Blob} blob - * @protected - */ - async loadImage_(image, blob) { - try { - await new Promise((resolve, reject) => { - image.onload = () => resolve(); - image.onerror = (e) => - reject(new Error(`Failed to load review document image: ${e}`)); - image.src = URL.createObjectURL(blob); - }); - } catch (e) { - URL.revokeObjectURL(image.src); - throw e; - } - } - - /** - * @param {!Blob} blob - * @return {!Promise} - */ - async setReviewPhoto(blob) { - const image = assertInstanceof(this.image_, HTMLImageElement); - await this.loadImage_(image, blob); - URL.revokeObjectURL(image.src); - } - - /** - * @template T - * @param {{positive: !Options<?T>, negative: !Options<?T>}} options - * @return {!Promise<?T>} - */ - async startReview({positive, negative}) { - // Remove all existing buttons. - for (const btnGroup of [this.positiveBtns_, this.negativeBtns_]) { - while (btnGroup.firstChild) { - btnGroup.removeChild(btnGroup.lastChild); - } - } - - this.primaryBtn_ = null; - const onSelected = new WaitableEvent(); - for (const btnGroup of [this.positiveBtns_, this.negativeBtns_]) { - const options = (btnGroup === this.positiveBtns_ ? positive : negative); - /** - * @param {!Option<?T>} option - */ - const addButton = ({text, exitValue, callback, hasPopup}) => { - const templ = instantiateTemplate('#text-button-template'); - const btn = dom.getFrom(templ, 'button', HTMLButtonElement); - btn.setAttribute('i18n-text', text); - if (this.primaryBtn_ === null) { - btn.classList.add('primary'); - this.primaryBtn_ = btn; - } else { - btn.classList.add('secondary'); - } - if (hasPopup !== null) { - btn.setAttribute('aria-haspopup', hasPopup); - } - btn.onclick = () => { - if (callback !== null) { - callback(); - } - if (exitValue !== undefined) { - onSelected.signal(exitValue); - } - }; - btnGroup.appendChild(templ); - }; - for (const opt of options.options) { - addButton(opt); - } - setupI18nElements(btnGroup); - } - - nav.open(this.viewName_); - const result = await onSelected.wait(); - nav.close(this.viewName_); - return result; - } -}
diff --git a/ash/webui/camera_app_ui/resources/js/views/review.ts b/ash/webui/camera_app_ui/resources/js/views/review.ts new file mode 100644 index 0000000..537b59bf --- /dev/null +++ b/ash/webui/camera_app_ui/resources/js/views/review.ts
@@ -0,0 +1,153 @@ +// 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 {assertInstanceof} from '../chrome_util.js'; +import * as dom from '../dom.js'; +import {I18nString} from '../i18n_string.js'; +import * as nav from '../nav.js'; +import {ViewName} from '../type.js'; +import {instantiateTemplate, setupI18nElements} from '../util.js'; +import {WaitableEvent} from '../waitable_event.js'; + +import {View} from './view.js'; + +/** + * Available option show in this view. + */ +export class Option<T> { + readonly exitValue: T|null|undefined; + readonly hasPopup: boolean|null; + readonly callback: (() => void)|null; + /** + * @param text Text string show on the option button. + * @param handlerParams Sets |exitValue| if the review page will exit with + * this value when option selected. Sets |callback| for the function get + * executed when option selected. + */ + constructor(readonly text: I18nString, {exitValue, callback, hasPopup}: { + exitValue?: (T|null); + callback?: (() => void); + hasPopup?: boolean; + }) { + this.exitValue = exitValue; + this.hasPopup = hasPopup ?? null; + this.callback = callback || null; + } +} + +/** + * Options for reviewing. + */ +export class Options<T> { + readonly options: Option<T|null>[]; + + /** Constructs Options. */ + constructor(...options: Option<T|null>[]) { + this.options = options; + } +} + +/** + * View controller for review page. + */ +export class Review extends View { + protected readonly image: HTMLElement; + private readonly positiveBtns: HTMLDivElement; + private readonly negativeBtns: HTMLDivElement; + private primaryBtn: HTMLButtonElement|null; + + /** + * Constructs the review view. + */ + constructor(private readonly viewName: ViewName = ViewName.REVIEW) { + super(viewName, {defaultFocusSelector: '.primary'}); + + this.image = dom.getFrom(this.root, '.review-image', HTMLElement); + this.positiveBtns = + dom.getFrom(this.root, '.positive.button-group', HTMLDivElement); + this.negativeBtns = + dom.getFrom(this.root, '.negative.button-group', HTMLDivElement); + this.primaryBtn = null; + } + + /** + * Load the image element with given blob. + */ + protected async loadImage(image: HTMLImageElement, blob: Blob): + Promise<void> { + try { + await new Promise<void>((resolve, reject) => { + image.onload = () => resolve(); + image.onerror = (e) => + reject(new Error(`Failed to load review document image: ${e}`)); + image.src = URL.createObjectURL(blob); + }); + } catch (e) { + URL.revokeObjectURL(image.src); + throw e; + } + } + + /** + * Sets the photo to be reviewed. + */ + async setReviewPhoto(blob: Blob): Promise<void> { + const image = assertInstanceof(this.image, HTMLImageElement); + await this.loadImage(image, blob); + URL.revokeObjectURL(image.src); + } + + /** + * Starts review. + */ + async startReview<T>({positive, negative}: { + positive: Options<T|null>; negative: Options<T|null>; + }): Promise<T|null> { + // Remove all existing buttons. + for (const btnGroup of [this.positiveBtns, this.negativeBtns]) { + while (btnGroup.lastChild !== null) { + btnGroup.removeChild(btnGroup.lastChild); + } + } + + this.primaryBtn = null; + const onSelected = new WaitableEvent<T|null>(); + for (const btnGroup of [this.positiveBtns, this.negativeBtns]) { + const options = (btnGroup === this.positiveBtns ? positive : negative); + const addButton = + ({text, exitValue, callback, hasPopup}: Option<T|null>) => { + const templ = instantiateTemplate('#text-button-template'); + const btn = dom.getFrom(templ, 'button', HTMLButtonElement); + btn.setAttribute('i18n-text', text); + if (this.primaryBtn === null) { + btn.classList.add('primary'); + this.primaryBtn = btn; + } else { + btn.classList.add('secondary'); + } + if (hasPopup !== null) { + btn.setAttribute('aria-haspopup', hasPopup.toString()); + } + btn.onclick = () => { + if (callback !== null) { + callback(); + } + if (exitValue !== undefined) { + onSelected.signal(exitValue); + } + }; + btnGroup.appendChild(templ); + }; + for (const opt of options.options) { + addButton(opt); + } + setupI18nElements(btnGroup); + } + + nav.open(this.viewName); + const result = await onSelected.wait(); + nav.close(this.viewName); + return result; + } +}
diff --git a/ash/webui/camera_app_ui/resources/js/views/view.js b/ash/webui/camera_app_ui/resources/js/views/view.js index e27d69f..e745c1d 100644 --- a/ash/webui/camera_app_ui/resources/js/views/view.js +++ b/ash/webui/camera_app_ui/resources/js/views/view.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 {assertInstanceof} from '../chrome_util.js'; import * as dom from '../dom.js'; import {ViewName} from '../type.js'; // eslint-disable-line no-unused-vars import {WaitableEvent} from '../waitable_event.js'; @@ -12,8 +13,8 @@ * message for message of the dialog view, cancellable for whether the dialog * view is cancellable. * @typedef {{ - * message: (string|undefined), - * cancellable: (boolean|undefined), + * message?: string, + * cancellable?: boolean, * }} */ let DialogEnterOptions; @@ -68,13 +69,14 @@ * Base controller of a view for views' navigation sessions (nav.js). */ export class View { + /* eslint-disable-next-line valid-jsdoc */ /** * @param {!ViewName} name Unique name of view which should be same as its DOM * element id. * @param {{ - * dismissByEsc: (boolean|undefined), - * dismissByBackgroundClick: (boolean|undefined), - * defaultFocusSelector: (string|undefined), + * dismissByEsc?: boolean, + * dismissByBackgroundClick?: boolean, + * defaultFocusSelector?: string, * }=} params * |dismissByEsc| enables dismissible by Esc-key. * |dismissByBackgroundClick| enables dismissible by background-click. @@ -157,7 +159,10 @@ * Focuses the default element on the view if applicable. */ focus() { - this.root.querySelector(this.defaultFocusSelector_)?.focus(); + const el = this.root.querySelector(this.defaultFocusSelector_); + if (el !== null) { + assertInstanceof(el, HTMLElement).focus(); + } } /**
diff --git a/ash/webui/camera_app_ui/resources/js/window_controller.js b/ash/webui/camera_app_ui/resources/js/window_controller.js index 704fef0..6dcf4d3 100644 --- a/ash/webui/camera_app_ui/resources/js/window_controller.js +++ b/ash/webui/camera_app_ui/resources/js/window_controller.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 {assertInstanceof} from './chrome_util.js'; import { WindowStateControllerRemote, WindowStateMonitorCallbackRouter, WindowStateType, -} from '/ash/webui/camera_app_ui/camera_app_helper.mojom-webui.js'; - -import {assertInstanceof} from './chrome_util.js'; +} from './mojo/type.js'; import {wrapEndpoint} from './mojo/util.js'; /**
diff --git a/ash/webui/camera_app_ui/resources/tsconfig.json b/ash/webui/camera_app_ui/resources/tsconfig.json deleted file mode 100644 index 4e47884..0000000 --- a/ash/webui/camera_app_ui/resources/tsconfig.json +++ /dev/null
@@ -1,27 +0,0 @@ -{ - "compilerOptions": { - "allowJs": true, - "checkJs": true, - "noEmit": true, - "module": "esnext", - "target": "esnext", - "typeRoots": ["../../../../third_party/node/node_modules/@types"], - "types": [ - "dom-mediacapture-record", - "google.analytics", - "w3c-css-typed-object-model-level-1", - "w3c-image-capture" - ] - }, - "files": [ - "js/externs/types.d.ts", - "js/init.js", - "js/main.js", - "js/models/barcode_worker.js", - "js/models/ffmpeg/video_processor.js", - "js/test_bridge.js", - "js/untrusted_ga_helper.js", - "js/untrusted_script_loader.js", - "js/untrusted_video_processor_helper.js" - ] -}
diff --git a/ash/webui/camera_app_ui/resources/tsconfig_base.json b/ash/webui/camera_app_ui/resources/tsconfig_base.json new file mode 100644 index 0000000..1c6b8131 --- /dev/null +++ b/ash/webui/camera_app_ui/resources/tsconfig_base.json
@@ -0,0 +1,22 @@ +{ + "extends": "../../../../tools/typescript/tsconfig_base.json", + "compilerOptions": { + "allowJs": true, + "checkJs": true, + "target": "esnext", + "typeRoots": ["../../../../third_party/node/node_modules/@types"], + "types": [ + "dom-mediacapture-record", + "google.analytics", + "w3c-css-typed-object-model-level-1", + "w3c-image-capture" + ], + "strict": false, + "noImplicitReturns": false, + "noUnusedLocals": false, + "noUnusedParameters": false + }, + "exclude": [ + "./js/lib" + ] +}
diff --git a/ash/webui/camera_app_ui/resources/utils/cca.py b/ash/webui/camera_app_ui/resources/utils/cca.py index 74a8763..19a005b 100755 --- a/ash/webui/camera_app_ui/resources/utils/cca.py +++ b/ash/webui/camera_app_ui/resources/utils/cca.py
@@ -6,6 +6,7 @@ import ast import argparse import functools +import json import logging import os import re @@ -56,19 +57,49 @@ subprocess.check_call(cmd) +def gen_files_are_hard_links(gen_dir): + cca_root = os.getcwd() + + util_js = os.path.join(cca_root, 'js/util.js') + util_js_in_gen = os.path.join(gen_dir, 'js/util.js') + return os.stat(util_js).st_ino == os.stat(util_js_in_gen).st_ino + + def deploy(args): + root_dir = get_chromium_root() cca_root = os.getcwd() target_dir = os.path.join(get_chromium_root(), f'out_{args.board}/Release') - build_preload_images_js( - os.path.join(target_dir, 'gen/ash/webui/camera_app_ui/resources/js')) + src_relative_dir = os.path.relpath(cca_root, root_dir) + gen_dir = os.path.join(target_dir, 'gen', src_relative_dir) + + # Since CCA copy source to gen directory and place it together with other + # generated files for TypeScript compilation, and GN use hard links when + # possible to copy files from source to gen directory, we do a check here + # that the file in gen directory is indeed hard linked to the source file + # (which should be the case when the two directory are in the same file + # system), so we don't need to emulate what GN does here and skip copying + # the files, and just call tsc on the gen directory. + # TODO(pihsun): Support this case if there's some common scenario that + # would cause this. + assert gen_files_are_hard_links(gen_dir), ( + 'The generated files are not hard linked.') + + build_preload_images_js(os.path.join(gen_dir, 'js')) + + run_node([ + 'typescript/bin/tsc', + '--project', + os.path.join(gen_dir, 'js/tsconfig.json'), + # For better debugging experience on DUT. + '--inlineSourceMap', + '--inlineSources', + ]) build_pak_cmd = [ 'tools/grit/grit.py', '-i', - os.path.join( - target_dir, - 'gen/ash/webui/camera_app_ui/' + 'ash_camera_app_resources.grd'), + os.path.join(gen_dir, '../ash_camera_app_resources.grd'), 'build', '-o', os.path.join(target_dir, 'gen/ash'), @@ -151,7 +182,60 @@ print('ESLint check failed, return code =', e.returncode) +# List of files of entrypoints to TypeScript compilation. +# +# Since TypeScript can recognize ES6 module imports to find files to be +# compiled, this list includes files that are not directly imported with ES6 +# module import, for example: +# * files running in web worker +# * files loaded in iframe by util.createUntrustedJSModule +# * TypeScript type definitions (.d.ts) +# * files directly referenced by <script> tag in HTML +TS_ENTRY_FILES = [ + "js/externs/types.d.ts", + "js/init.js", + "js/main.js", + "js/models/barcode_worker.js", + "js/models/ffmpeg/video_processor.js", + "js/test_bridge.js", + "js/untrusted_ga_helper.js", + "js/untrusted_script_loader.js", + "js/untrusted_video_processor_helper.js", +] + + +def get_tsc_paths(board): + root_dir = get_chromium_root() + target_gen_dir = os.path.join(root_dir, f'out_{board}/Release/gen') + + cca_root = os.getcwd() + src_relative_dir = os.path.relpath(cca_root, root_dir) + + webui_dir = os.path.join(target_gen_dir, src_relative_dir, + 'js/mojom-webui/*') + resources_dir = os.path.join(target_gen_dir, + 'ui/webui/resources/preprocessed/*') + + return { + '/mojom-webui/*': [os.path.relpath(webui_dir)], + '//resources/*': [os.path.relpath(resources_dir)], + 'chrome://resources/*': [os.path.relpath(resources_dir)], + } + + def tsc(args): + cca_root = os.getcwd() + + with open(os.path.join(cca_root, 'tsconfig_base.json')) as f: + tsconfig = json.load(f) + + tsconfig['files'] = TS_ENTRY_FILES + tsconfig['compilerOptions']['noEmit'] = True + tsconfig['compilerOptions']['paths'] = get_tsc_paths(args.board) + + with open(os.path.join(cca_root, 'tsconfig.json'), 'w') as f: + json.dump(tsconfig, f) + try: run_node(['typescript/bin/tsc']) except subprocess.CalledProcessError as e: @@ -192,8 +276,10 @@ tsc_parser = subparsers.add_parser('tsc', help='check code with tsc', - description='Check types with tsc.') + description='''Check types with tsc. + Please build Chrome at least once before running the command.''') tsc_parser.set_defaults(func=tsc) + tsc_parser.add_argument('board') parser.set_defaults(func=lambda _args: parser.print_help())
diff --git a/ash/webui/media_app_ui/resources/js/receiver.js b/ash/webui/media_app_ui/resources/js/receiver.js index b0a6349..35a74ab 100644 --- a/ash/webui/media_app_ui/resources/js/receiver.js +++ b/ash/webui/media_app_ui/resources/js/receiver.js
@@ -229,11 +229,12 @@ */ async openFilesWithFilePicker(acceptTypeKeys, startInFolder) { // AbstractFile doesn't guarantee tokens. Use one from a ReceivedFile if - // there is one. + // there is one, after ensuring it is valid. const fileRep = /** @type {{token: (number|undefined)}} */ (startInFolder); + const startInToken = startInFolder ? (fileRep.token || 0) : 0; /** @type {!OpenFilesWithPickerMessage} */ const msg = { - startInToken: startInFolder ? (fileRep.token || 0) : 0, + startInToken: startInToken > 0 ? startInToken : 0, accept: acceptTypeKeys, }; await parentMessagePipe.sendMessage(Message.OPEN_FILES_WITH_PICKER, msg);
diff --git a/ash/webui/media_app_ui/test/guest_query_receiver.js b/ash/webui/media_app_ui/test/guest_query_receiver.js index 59025ecd..0b5679d 100644 --- a/ash/webui/media_app_ui/test/guest_query_receiver.js +++ b/ash/webui/media_app_ui/test/guest_query_receiver.js
@@ -131,9 +131,20 @@ return 'opened and updated'; }, openFilesWithFilePicker: async (data, resultData) => { - const existingFile = assertLastReceivedFileList().item(0) || null; + /** + * @typedef {{ + * acceptTypeKeys: !Array<string>, + * explicitToken: (number|undefined) + * }} + */ + let Args; + const args = /** @type {Args} */ (data.simpleArgs); + let existingFile = assertLastReceivedFileList().item(0) || null; + if (args.explicitToken) { + existingFile = {token: args.explicitToken}; + } await assertLastReceivedFileList().openFilesWithFilePicker( - data.simpleArgs, existingFile); + args.acceptTypeKeys, existingFile); return 'openFilesWithFilePicker resolved'; }, };
diff --git a/ash/webui/media_app_ui/test/media_app_ui_browsertest.js b/ash/webui/media_app_ui/test/media_app_ui_browsertest.js index 8758256..afa4efb 100644 --- a/ash/webui/media_app_ui/test/media_app_ui_browsertest.js +++ b/ash/webui/media_app_ui/test/media_app_ui_browsertest.js
@@ -1261,6 +1261,7 @@ new FakeFileSystemFileHandle('picked_file1.jpg'), new FakeFileSystemFileHandle('picked_file2.jpg'), ]; + /** @type {!OpenFilePickerOptions|!DraftFilePickerOptions|undefined} */ let lastPickerOptions; window.showOpenFilePicker = (pickerOptions) => { lastPickerOptions = pickerOptions; @@ -1269,8 +1270,9 @@ const directory = await launchWithFiles( [await createTestImageFile(10, 10, 'original_file.jpg')]); - let testResponse = await sendTestMessage( - {simple: 'openFilesWithFilePicker', simpleArgs: ['VIDEO', 'IMAGE']}); + const simpleArgs = {acceptTypeKeys: ['VIDEO', 'IMAGE']}; + let testResponse = + await sendTestMessage({simple: 'openFilesWithFilePicker', simpleArgs}); assertEquals( testResponse.testQueryResult, 'openFilesWithFilePicker resolved'); @@ -1290,6 +1292,15 @@ assertEquals(clientFiles[0].name, 'picked_file1.jpg'); assertEquals(clientFiles[1].name, 'picked_file2.jpg'); + + // Test to handle invalid tokens (b/209342852). These should leave the + // `startIn` option unspecified. + simpleArgs.explicitToken = -1; + testResponse = + await sendTestMessage({simple: 'openFilesWithFilePicker', simpleArgs}); + assertEquals( + testResponse.testQueryResult, 'openFilesWithFilePicker resolved'); + assertEquals(lastPickerOptions.startIn, undefined); }; MediaAppUIBrowserTest.RelatedFiles = async () => {
diff --git a/base/values.cc b/base/values.cc index 5be8a51f..a4c32ec 100644 --- a/base/values.cc +++ b/base/values.cc
@@ -1399,18 +1399,6 @@ return as_const(*this).Get(index, const_cast<const Value**>(out_value)); } -bool ListValue::GetBoolean(size_t index, bool* bool_value) const { - const Value* value; - if (!Get(index, &value)) - return false; - - if (bool_value && value->is_bool()) { - *bool_value = value->GetBool(); - return true; - } - return value->is_bool(); -} - bool ListValue::GetString(size_t index, std::string* out_value) const { const Value* value; if (!Get(index, &value))
diff --git a/base/values.h b/base/values.h index 76f30c7..c62a8f7 100644 --- a/base/values.h +++ b/base/values.h
@@ -813,8 +813,6 @@ // only if the index is valid and the Value at that index can be returned // in the specified form. // `out_value` is optional and will only be set if non-NULL. - // DEPRECATED, use `GetList()::operator[]::GetBool()` instead. - bool GetBoolean(size_t index, bool* out_value) const; // DEPRECATED, use `GetList()::operator[]::GetString()` instead. bool GetString(size_t index, std::string* out_value) const; bool GetString(size_t index, std::u16string* out_value) const;
diff --git a/base/values_unittest.cc b/base/values_unittest.cc index 515e2925..41216ce7 100644 --- a/base/values_unittest.cc +++ b/base/values_unittest.cc
@@ -2293,15 +2293,6 @@ EXPECT_TRUE(main_list.Get(6, nullptr)); EXPECT_FALSE(main_list.Get(7, nullptr)); - EXPECT_TRUE(main_list.GetBoolean(0, nullptr)); - EXPECT_FALSE(main_list.GetBoolean(1, nullptr)); - EXPECT_FALSE(main_list.GetBoolean(2, nullptr)); - EXPECT_FALSE(main_list.GetBoolean(3, nullptr)); - EXPECT_FALSE(main_list.GetBoolean(4, nullptr)); - EXPECT_FALSE(main_list.GetBoolean(5, nullptr)); - EXPECT_FALSE(main_list.GetBoolean(6, nullptr)); - EXPECT_FALSE(main_list.GetBoolean(7, nullptr)); - EXPECT_FALSE(main_list.GetString(0, static_cast<std::string*>(nullptr))); EXPECT_FALSE(main_list.GetString(1, static_cast<std::string*>(nullptr))); EXPECT_FALSE(main_list.GetString(2, static_cast<std::string*>(nullptr)));
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1 index 37f904b..a172c0775 100644 --- a/build/fuchsia/linux.sdk.sha1 +++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@ -7.20211205.2.1 +7.20211205.3.1
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1 index 37f904b..a172c0775 100644 --- a/build/fuchsia/mac.sdk.sha1 +++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@ -7.20211205.2.1 +7.20211205.3.1
diff --git a/chrome/VERSION b/chrome/VERSION index 0aaa425..d620960 100644 --- a/chrome/VERSION +++ b/chrome/VERSION
@@ -1,4 +1,4 @@ MAJOR=98 MINOR=0 -BUILD=4750 +BUILD=4751 PATCH=0
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantClient.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantClient.java index 016c192..2f7d2f8a 100644 --- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantClient.java +++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantClient.java
@@ -7,14 +7,11 @@ import android.accessibilityservice.AccessibilityServiceInfo; import android.accounts.Account; import android.app.Activity; -import android.content.Context; import android.os.Build; -import android.telephony.TelephonyManager; import androidx.annotation.Nullable; import org.chromium.base.Callback; -import org.chromium.base.ContextUtils; import org.chromium.base.ThreadUtils; import org.chromium.base.annotations.CalledByNative; import org.chromium.base.annotations.JNINamespace; @@ -359,27 +356,6 @@ }); } - /** - * Returns the country that the device is currently located in. This currently only works - * for devices with active SIM cards. For a more general solution, we should probably use - * the LocationManager together with the Geocoder. - */ - @CalledByNative - @Nullable - private String getCountryCode() { - TelephonyManager telephonyManager = - (TelephonyManager) ContextUtils.getApplicationContext().getSystemService( - Context.TELEPHONY_SERVICE); - - // According to API, location for CDMA networks is unreliable - if (telephonyManager != null - && telephonyManager.getPhoneType() != TelephonyManager.PHONE_TYPE_CDMA) { - return telephonyManager.getNetworkCountryIso(); - } - - return null; - } - /** Returns the android version of the device. */ @CalledByNative private int getSdkInt() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/metrics/UmaUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/metrics/UmaUtils.java index 4bf508d..2cab87e 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/metrics/UmaUtils.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/metrics/UmaUtils.java
@@ -48,6 +48,13 @@ private static long sForegroundStartTimeMs; private static long sBackgroundTimeMs; + private static boolean sSkipRecordingNextForegroundStartTimeForTesting; + + // Will short-circuit out of the next recordForegroundStartTime() call. + public static void skipRecordingNextForegroundStartTimeForTesting() { + sSkipRecordingNextForegroundStartTimeForTesting = true; + } + /** * Record the time in the application lifecycle at which Chrome code first runs * (Application.attachBaseContext()). @@ -64,6 +71,11 @@ * Record the time at which Chrome was brought to foreground. */ public static void recordForegroundStartTime() { + if (sSkipRecordingNextForegroundStartTimeForTesting) { + sSkipRecordingNextForegroundStartTimeForTesting = false; + return; + } + // Since this can be called from multiple places (e.g. ChromeActivitySessionTracker // and FirstRunActivity), only set the time if it hasn't been set previously or if // Chrome has been sent to background since the last foreground time.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/multiwindow/MultiInstanceManagerApi31.java b/chrome/android/java/src/org/chromium/chrome/browser/multiwindow/MultiInstanceManagerApi31.java index f840062..e21f6e1e 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/multiwindow/MultiInstanceManagerApi31.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/multiwindow/MultiInstanceManagerApi31.java
@@ -260,6 +260,15 @@ installTabModelObserver(); recordInstanceCountHistogram(); recordActivityCountHistogram(); + ActivityManager activityManager = + (ActivityManager) mActivity.getSystemService(Context.ACTIVITY_SERVICE); + String launchActivityName = ChromeTabbedActivity.MAIN_LAUNCHER_ACTIVITY_NAME; + if (activityManager != null) { + MultiInstanceState.maybeCreate(activityManager::getAppTasks, + (activityName) + -> TextUtils.equals(activityName, ChromeTabbedActivity.class.getName()) + || TextUtils.equals(activityName, launchActivityName)); + } } @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabImpl.java index 9d58491..f37482cfd 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabImpl.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabImpl.java
@@ -13,6 +13,7 @@ import android.view.View.OnAttachStateChangeListener; import android.view.accessibility.AccessibilityEvent; +import androidx.annotation.IntDef; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; @@ -69,6 +70,8 @@ import org.chromium.ui.util.ColorUtils; import org.chromium.url.GURL; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.nio.ByteBuffer; /** @@ -211,6 +214,22 @@ private int mThemeColor; private boolean mUsedCriticalPersistedTabData; + /** Tab level Request Desktop Site setting. */ + private @TabUserAgent int mTabUserAgent; + + // TODO(https://crbug.com/1251794): Determine if this should be defined somewhere like TabState + // for when we persist to disk. + /** + * Defines the tab level Request Desktop Site settings. + */ + @IntDef({TabUserAgent.DEFAULT, TabUserAgent.MOBILE, TabUserAgent.DESKTOP}) + @Retention(RetentionPolicy.SOURCE) + public @interface TabUserAgent { + int DEFAULT = 0; /* No tab level setting */ + int MOBILE = 1; /* Tab level setting is mobile layout */ + int DESKTOP = 2; /* Tab level setting is desktop layout */ + } + /** * Creates an instance of a {@link TabImpl}. * @@ -964,7 +983,6 @@ CriticalPersistedTabData.from(this).setLaunchTypeAtCreation(state.tabLaunchTypeAtCreation); CriticalPersistedTabData.from(this).setRootId( state.rootId == Tab.INVALID_TAB_ID ? mId : state.rootId); - CriticalPersistedTabData.from(this).setUserAgent(state.userAgent); } /** @@ -1634,27 +1652,13 @@ } private @UserAgentOverrideOption int calculateUserAgentOverrideOption() { - WebContents webContents = getWebContents(); - boolean currentRequestDesktopSite = webContents == null + boolean currentRequestDesktopSite = getWebContents() == null ? false - : webContents.getNavigationController().getUseDesktopUserAgent(); + : getWebContents().getNavigationController().getUseDesktopUserAgent(); - @TabUserAgent - int tabUserAgent = CriticalPersistedTabData.from(this).getUserAgent(); - // TabUserAgent.UNSET means this is a pre-existing tab from an earlier build. In this case - // we set the TabUserAgent bit based on last committed entry's user agent. If webContents is - // null, this method is triggered too early, and we cannot read the last committed entry's - // user agent yet. We will skip for now and let the following call set the TabUserAgent bit. - if (webContents != null && tabUserAgent == TabUserAgent.UNSET) { - if (currentRequestDesktopSite) { - tabUserAgent = TabUserAgent.DESKTOP; - } else { - tabUserAgent = TabUserAgent.DEFAULT; - } - CriticalPersistedTabData.from(this).setUserAgent(tabUserAgent); - } // We only calculate the user agent when users did not manually choose one. - if (tabUserAgent == TabUserAgent.DEFAULT + // TODO(crbug.com/1251794): Desktop site setting in app menu does not persist after restart. + if (mTabUserAgent == TabUserAgent.DEFAULT && ContentFeatureList.isEnabled(ContentFeatureList.REQUEST_DESKTOP_SITE_GLOBAL)) { // We only do the following logic to choose the desktop/mobile user agent if: // 1. User never manually made a choice in the app menu for requesting desktop site. @@ -1689,6 +1693,10 @@ return UserAgentOverrideOption.INHERIT; } + void setTabUserAgent(@TabUserAgent int tabUserAgent) { + mTabUserAgent = tabUserAgent; + } + private void switchUserAgentIfNeeded() { if (calculateUserAgentOverrideOption() == UserAgentOverrideOption.INHERIT || getWebContents() == null) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabStateExtractor.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabStateExtractor.java index 9d201de..11139264 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabStateExtractor.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabStateExtractor.java
@@ -43,7 +43,6 @@ ? tab.getThemeColor() : TabState.UNSPECIFIED_THEME_COLOR; tabState.rootId = CriticalPersistedTabData.from(tab).getRootId(); - tabState.userAgent = CriticalPersistedTabData.from(tab).getUserAgent(); return tabState; }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabUtils.java index 5b5aaaec..7bd90cef 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabUtils.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabUtils.java
@@ -17,7 +17,7 @@ import org.chromium.chrome.R; import org.chromium.chrome.browser.flags.ChromeFeatureList; import org.chromium.chrome.browser.profiles.Profile; -import org.chromium.chrome.browser.tab.state.CriticalPersistedTabData; +import org.chromium.chrome.browser.tab.TabImpl.TabUserAgent; import org.chromium.components.browser_ui.site_settings.WebsitePreferenceBridge; import org.chromium.components.content_settings.ContentSettingValues; import org.chromium.components.content_settings.ContentSettingsType; @@ -101,7 +101,7 @@ == switchToDesktop) { tabUserAgent = TabUserAgent.DEFAULT; } - CriticalPersistedTabData.from(tab).setUserAgent(tabUserAgent); + ((TabImpl) tab).setTabUserAgent(tabUserAgent); } }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/metrics/StartupLoadingMetricsTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/metrics/StartupLoadingMetricsTest.java index aebaacb..8b5961f 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/metrics/StartupLoadingMetricsTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/metrics/StartupLoadingMetricsTest.java
@@ -24,6 +24,7 @@ import org.chromium.base.test.util.DisableIf; import org.chromium.base.test.util.DisabledTest; import org.chromium.chrome.browser.ChromeTabbedActivity; +import org.chromium.chrome.browser.flags.ChromeFeatureList; import org.chromium.chrome.browser.flags.ChromeSwitches; import org.chromium.chrome.browser.tab.Tab; import org.chromium.chrome.browser.webapps.WebApkActivityLifecycleUmaTracker; @@ -33,6 +34,7 @@ import org.chromium.chrome.test.ChromeTabbedActivityTestRule; import org.chromium.chrome.test.util.ChromeApplicationTestUtils; import org.chromium.chrome.test.util.ChromeTabUtils; +import org.chromium.chrome.test.util.browser.Features.DisableFeatures; import org.chromium.content_public.browser.test.util.TestThreadUtils; import org.chromium.net.test.EmbeddedTestServer; @@ -55,6 +57,8 @@ "Startup.Android.Cold.TimeToFirstVisibleContent"; private static final String VISIBLE_CONTENT_HISTOGRAM = "Startup.Android.Cold.TimeToVisibleContent"; + private static final String FIRST_COMMIT_OCCURRED_PRE_FOREGROUND_HISTOGRAM = + "Startup.Android.Cold.FirstNavigationCommitOccurredPreForeground"; private static final String TABBED_SUFFIX = ChromeTabbedActivity.STARTUP_UMA_HISTOGRAM_SUFFIX; private static final String WEBAPK_SUFFIX = @@ -123,6 +127,24 @@ Assert.assertEquals(expectedCount, RecordHistogram.getHistogramTotalCountForTesting(VISIBLE_CONTENT_HISTOGRAM)); } + + if (expectedCount > 0) { + // If the first nav commit was recorded, it should have also been registered as having + // occurred post-foregrounding (since otherwise it would not have been recorded). + Assert.assertEquals(expectedCount, + RecordHistogram.getHistogramValueCountForTesting( + FIRST_COMMIT_OCCURRED_PRE_FOREGROUND_HISTOGRAM, 0)); + Assert.assertEquals(0, + RecordHistogram.getHistogramValueCountForTesting( + FIRST_COMMIT_OCCURRED_PRE_FOREGROUND_HISTOGRAM, 1)); + } else { + // Note that the first commit might or might not have occurred in this case depending on + // the test. However, if it occurred it must have occurred pre-foregrounding (since + // otherwise the core metric would have been recorded). + Assert.assertEquals(0, + RecordHistogram.getHistogramValueCountForTesting( + FIRST_COMMIT_OCCURRED_PRE_FOREGROUND_HISTOGRAM, 0)); + } } /** @@ -249,4 +271,51 @@ }); assertHistogramsRecorded(0, TABBED_SUFFIX); } + + @Test + @LargeTest + @DisableFeatures(ChromeFeatureList.ELIDE_TAB_PRELOAD_AT_STARTUP) + public void testRecordingOfFirstNavigationCommitPreForeground() throws Exception { + UmaUtils.skipRecordingNextForegroundStartTimeForTesting(); + + runAndWaitForPageLoadMetricsRecorded(() -> { + Intent intent = new Intent(Intent.ACTION_VIEW); + intent.addCategory(Intent.CATEGORY_LAUNCHER); + mTabbedActivityTestRule.startMainActivityFromIntent(intent, mTestPage); + }); + + ActivityTabStartupMetricsTracker startupMetricsTracker = + mTabbedActivityTestRule.getActivity().getActivityTabStartupMetricsTracker(); + + // Startup metrics should not have been recorded since the browser is not in the + // foreground. + Assert.assertEquals(0, + RecordHistogram.getHistogramTotalCountForTesting( + FIRST_COMMIT_HISTOGRAM + TABBED_SUFFIX)); + Assert.assertEquals(0, + RecordHistogram.getHistogramTotalCountForTesting(FIRST_VISIBLE_CONTENT_HISTOGRAM)); + + // The metric for the first navigation commit having occurred + // pre-foregrounding should also not have been recorded at this point, as there hasn't yet + // been a notification that the browser has come to the foreground. + Assert.assertEquals(0, + RecordHistogram.getHistogramValueCountForTesting( + FIRST_COMMIT_OCCURRED_PRE_FOREGROUND_HISTOGRAM, 1)); + + // Trigger the come-to-foreground event. + TestThreadUtils.runOnUiThreadBlocking(() -> UmaUtils.recordForegroundStartTime()); + + // Startup metrics should still not have been recorded... + Assert.assertEquals(0, + RecordHistogram.getHistogramTotalCountForTesting( + FIRST_COMMIT_HISTOGRAM + TABBED_SUFFIX)); + Assert.assertEquals(0, + RecordHistogram.getHistogramTotalCountForTesting(FIRST_VISIBLE_CONTENT_HISTOGRAM)); + + // ...but the metric for the first navigation commit having occurred + // pre-foregrounding *should* now have been recorded. + Assert.assertEquals(1, + RecordHistogram.getHistogramValueCountForTesting( + FIRST_COMMIT_OCCURRED_PRE_FOREGROUND_HISTOGRAM, 1)); + } }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/tab/state/CriticalPersistedTabDataTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/tab/state/CriticalPersistedTabDataTest.java index b94fcafb..49b4c9d7 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/tab/state/CriticalPersistedTabDataTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/tab/state/CriticalPersistedTabDataTest.java
@@ -31,7 +31,6 @@ import org.chromium.chrome.browser.tab.TabImpl; import org.chromium.chrome.browser.tab.TabLaunchType; import org.chromium.chrome.browser.tab.TabStateExtractor; -import org.chromium.chrome.browser.tab.TabUserAgent; import org.chromium.chrome.browser.tab.WebContentsState; import org.chromium.chrome.browser.tab.proto.CriticalPersistedTabData.CriticalPersistedTabDataProto; import org.chromium.chrome.test.ChromeTabbedActivityTestRule; @@ -69,8 +68,6 @@ private static final String OPENER_APP_ID = "OpenerAppId"; private static final int THEME_COLOR = 5; private static final Integer LAUNCH_TYPE_AT_CREATION = 3; - private static final @TabUserAgent int USER_AGENT_A = TabUserAgent.MOBILE; - private static final @TabUserAgent int USER_AGENT_B = TabUserAgent.DESKTOP; private static final String TITLE_A = "original title"; private static final String TITLE_B = "new title"; private static final GURL URL_A = new GURL("https://a.com"); @@ -152,7 +149,7 @@ CriticalPersistedTabData criticalPersistedTabData = new CriticalPersistedTabData(mockTab(TAB_ID, isEncrypted), "", "", PARENT_ID, ROOT_ID, TIMESTAMP, WEB_CONTENTS_STATE, CONTENT_STATE_VERSION, - OPENER_APP_ID, THEME_COLOR, LAUNCH_TYPE_AT_CREATION, USER_AGENT_A); + OPENER_APP_ID, THEME_COLOR, LAUNCH_TYPE_AT_CREATION); criticalPersistedTabData.setShouldSaveForTesting(true); mStorage.setSemaphore(saveSemaphore); ObservableSupplierImpl<Boolean> supplier = new ObservableSupplierImpl<>(); @@ -176,7 +173,6 @@ Assert.assertArrayEquals(CriticalPersistedTabData.getContentStateByteArray( mCriticalPersistedTabData.getWebContentsState().buffer()), WEB_CONTENTS_STATE_BYTES); - Assert.assertEquals(mCriticalPersistedTabData.getUserAgent(), USER_AGENT_A); Semaphore deleteSemaphore = new Semaphore(0); ThreadUtils.runOnUiThreadBlocking(() -> { mStorage.setSemaphore(deleteSemaphore); @@ -288,7 +284,7 @@ Tab tab = mockTab(TAB_ID, false); CriticalPersistedTabData criticalPersistedTabData = new CriticalPersistedTabData(tab, "", "", PARENT_ID, ROOT_ID, TIMESTAMP, WEB_CONTENTS_STATE, CONTENT_STATE_VERSION, - OPENER_APP_ID, THEME_COLOR, LAUNCH_TYPE_AT_CREATION, USER_AGENT_A); + OPENER_APP_ID, THEME_COLOR, LAUNCH_TYPE_AT_CREATION); ByteBuffer serialized = criticalPersistedTabData.getSerializeSupplier().get(); PersistedTabDataConfiguration config = PersistedTabDataConfiguration.get( ShoppingPersistedTabData.class, tab.isIncognito()); @@ -305,7 +301,6 @@ Assert.assertArrayEquals(WEB_CONTENTS_STATE_BYTES, CriticalPersistedTabData.getContentStateByteArray( deserialized.getWebContentsState().buffer())); - Assert.assertEquals(USER_AGENT_A, deserialized.getUserAgent()); } @SmallTest @@ -317,7 +312,7 @@ CriticalPersistedTabData criticalPersistedTabData = new CriticalPersistedTabData(tab, "", "", PARENT_ID, ROOT_ID, TIMESTAMP, TabStateExtractor.getWebContentsState(tab), CONTENT_STATE_VERSION, - OPENER_APP_ID, THEME_COLOR, LAUNCH_TYPE_AT_CREATION, USER_AGENT_A); + OPENER_APP_ID, THEME_COLOR, LAUNCH_TYPE_AT_CREATION); ByteBuffer serialized = criticalPersistedTabData.getSerializeSupplier().get(); PersistedTabDataConfiguration config = PersistedTabDataConfiguration.get( ShoppingPersistedTabData.class, tab.isIncognito()); @@ -336,7 +331,7 @@ Tab tab = mockTab(TAB_ID, false); CriticalPersistedTabData criticalPersistedTabData = new CriticalPersistedTabData(tab, "", "", PARENT_ID, ROOT_ID, TIMESTAMP, WEB_CONTENTS_STATE, CONTENT_STATE_VERSION, null, - THEME_COLOR, LAUNCH_TYPE_AT_CREATION, USER_AGENT_A); + THEME_COLOR, LAUNCH_TYPE_AT_CREATION); ByteBuffer serialized = criticalPersistedTabData.getSerializeSupplier().get(); PersistedTabDataConfiguration config = PersistedTabDataConfiguration.get( ShoppingPersistedTabData.class, tab.isIncognito()); @@ -544,31 +539,6 @@ } } - @UiThreadTest - @SmallTest - @Test - public void testUserAgentSavedWhenNecessary() { - try (StrictModeContext ignored = StrictModeContext.allowAllThreadPolicies()) { - CriticalPersistedTabData spyCriticalPersistedTabData = - spy(CriticalPersistedTabData.from(mockTab(TAB_ID, false))); - spyCriticalPersistedTabData.setUserAgent(USER_AGENT_A); - Assert.assertEquals(USER_AGENT_A, spyCriticalPersistedTabData.getUserAgent()); - verify(spyCriticalPersistedTabData, times(1)).save(); - - spyCriticalPersistedTabData.setUserAgent(USER_AGENT_A); - Assert.assertEquals(USER_AGENT_A, spyCriticalPersistedTabData.getUserAgent()); - verify(spyCriticalPersistedTabData, times(1)).save(); - - spyCriticalPersistedTabData.setUserAgent(USER_AGENT_B); - Assert.assertEquals(USER_AGENT_B, spyCriticalPersistedTabData.getUserAgent()); - verify(spyCriticalPersistedTabData, times(2)).save(); - - spyCriticalPersistedTabData.setUserAgent(USER_AGENT_A); - Assert.assertEquals(USER_AGENT_A, spyCriticalPersistedTabData.getUserAgent()); - verify(spyCriticalPersistedTabData, times(3)).save(); - } - } - @SmallTest @Test public void testConvertTabLaunchTypeToProtoLaunchType() { @@ -587,35 +557,4 @@ CriticalPersistedTabData.getLaunchType(type); } } - - @SmallTest - @Test - public void testConvertTabUserAgentToProtoUserAgentType() { - for (@TabUserAgent Integer tabUserAgent = 0; tabUserAgent <= TabUserAgent.SIZE; - tabUserAgent++) { - CriticalPersistedTabDataProto.UserAgentType protoUserAgentType = - CriticalPersistedTabData.getUserAgentType(tabUserAgent); - Assert.assertNotEquals("TabUserAgent value is invalid.", protoUserAgentType, - CriticalPersistedTabDataProto.UserAgentType.USER_AGENT_UNKNOWN); - if (tabUserAgent != TabUserAgent.SIZE) continue; - Assert.assertEquals("TabUserAgent and ProtoUserAgentType should have the same size.", - protoUserAgentType, - CriticalPersistedTabDataProto.UserAgentType.USER_AGENT_SIZE); - } - } - - @SmallTest - @Test - public void testConvertProtoUserAgentTypeToTabUserAgent() { - for (CriticalPersistedTabDataProto.UserAgentType type : - CriticalPersistedTabDataProto.UserAgentType.values()) { - if (type == CriticalPersistedTabDataProto.UserAgentType.USER_AGENT_UNKNOWN) continue; - @TabUserAgent - Integer tabUserAgent = CriticalPersistedTabData.getUserAgentType(type); - Assert.assertNotNull("ProtoUserAgentType value is invalid.", tabUserAgent); - if (type != CriticalPersistedTabDataProto.UserAgentType.USER_AGENT_SIZE) continue; - Assert.assertEquals("TabUserAgent and ProtoUserAgentType should have the same size.", - (int) tabUserAgent, TabUserAgent.SIZE); - } - } }
diff --git a/chrome/app/chromium_strings.grd b/chrome/app/chromium_strings.grd index 22285b8..0de03b4 100644 --- a/chrome/app/chromium_strings.grd +++ b/chrome/app/chromium_strings.grd
@@ -826,7 +826,7 @@ </message> </if> - <if expr="not chromeos and not is_android"> + <if expr="not chromeos and not is_android and not lacros"> <!-- Dice Web Signin Interception Bubble--> <message name="IDS_SIGNIN_DICE_WEB_INTERCEPT_CREATE_BUBBLE_TITLE" desc="Title of the web signin interception bubble"> Continue in a new Chromium profile? @@ -846,7 +846,9 @@ <message name="IDS_SIGNIN_DICE_WEB_INTERCEPT_ENTERPRISE_BUBBLE_DESC_MANAGED_DEVICE" desc="Body of the web signin interception bubble when the new account is personal and the existing account is managed on a managed device"> This will create a new Chromium profile for <ph name="USER_EMAIL_ADDRESS">$1<ex>foo@gmail.com</ex></ph> </message> + </if> + <if expr="not chromeos and not is_android"> <!-- Profile Customization Bubble--> <message name="IDS_PROFILE_CUSTOMIZATION_TEXT" desc="Text of the profile customization bubble"> Customize your new Chromium profile
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd index 1bed8fc..b9d53a3 100644 --- a/chrome/app/generated_resources.grd +++ b/chrome/app/generated_resources.grd
@@ -8751,7 +8751,7 @@ </message> <!-- Dice Web Signin Interception Bubble--> - <if expr="not chromeos and not is_android"> + <if expr="not chromeos and not is_android and not lacros"> <message name="IDS_SIGNIN_DICE_WEB_INTERCEPT_BUBBLE_NEW_PROFILE_BUTTON_LABEL" desc="Label of the confirmation button in the web signin interception bubble"> Ok </message> @@ -8776,14 +8776,17 @@ <message name="IDS_SIGNIN_DICE_WEB_INTERCEPT_ENTERPRISE_BUBBLE_DESC" desc="Body of the web signin interception bubble when the new account is personal and the existing account is managed"> This will separate your browsing from <ph name="EXISTING_USER">$1<ex>bob@example.com</ex></ph> </message> - <message name="IDS_SIGNIN_DICE_WEB_INTERCEPT_ENTERPRISE_PROFILE_NAME" desc="Default name for work profiles created after signin interception"> - Work - </message> <message name="IDS_SIGNIN_DICE_WEB_INTERCEPT_SWITCH_BUBBLE_DESC_V2" desc="Description for the profile switch interception bubble"> <ph name="NAME">$2<ex>Bob</ex></ph>'s profile is linked to <ph name="EMAIL">$1<ex>bob@gmail.com</ex></ph> </message> </if> + <if expr="not chromeos and not is_android"> + <message name="IDS_SIGNIN_DICE_WEB_INTERCEPT_ENTERPRISE_PROFILE_NAME" desc="Default name for work profiles created after signin interception"> + Work + </message> + </if> + <!-- Signin Error tab modal dialog --> <message name="IDS_SIGNIN_ERROR_TITLE" desc="Title of the signin error tab modal dialog."> Can't sign in
diff --git a/chrome/app/google_chrome_strings.grd b/chrome/app/google_chrome_strings.grd index c6de1f5..cbe96ae 100644 --- a/chrome/app/google_chrome_strings.grd +++ b/chrome/app/google_chrome_strings.grd
@@ -834,7 +834,7 @@ </message> </if> - <if expr="not chromeos and not is_android"> + <if expr="not chromeos and not is_android and not lacros"> <!-- Dice Web Signin Interception Bubble--> <message name="IDS_SIGNIN_DICE_WEB_INTERCEPT_CREATE_BUBBLE_TITLE" desc="Title of the web signin interception bubble"> Continue in a new Chrome profile? @@ -854,7 +854,9 @@ <message name="IDS_SIGNIN_DICE_WEB_INTERCEPT_ENTERPRISE_BUBBLE_DESC_MANAGED_DEVICE" desc="Body of the web signin interception bubble when the new account is personal and the existing account is managed on a managed device"> This will create a new Chrome profile for <ph name="USER_EMAIL_ADDRESS">$1<ex>foo@gmail.com</ex></ph> </message> + </if> + <if expr="not chromeos and not is_android"> <!-- Profile Customization Bubble--> <message name="IDS_PROFILE_CUSTOMIZATION_TEXT" desc="Text of the profile customization bubble"> Customize your new Chrome profile
diff --git a/chrome/app/resources/generated_resources_en-GB.xtb b/chrome/app/resources/generated_resources_en-GB.xtb index 8d506854..2fd949c8 100644 --- a/chrome/app/resources/generated_resources_en-GB.xtb +++ b/chrome/app/resources/generated_resources_en-GB.xtb
@@ -162,6 +162,7 @@ <translation id="1152346050262092795">Enter your password again to verify your account.</translation> <translation id="1153356358378277386">Paired devices</translation> <translation id="1153636665119721804">Google Advanced Protection programme</translation> +<translation id="1155545602507378023">No, this device only</translation> <translation id="1155816283571436363">Connecting to your phone</translation> <translation id="1158238185437008462">See memories</translation> <translation id="1161575384898972166">Please sign in to <ph name="TOKEN_NAME" /> to export the client certificate.</translation> @@ -395,6 +396,7 @@ <translation id="1410197035576869800">App Icon</translation> <translation id="1410616244180625362">Continue allowing <ph name="HOST" /> to access your camera</translation> <translation id="1410806973194718079">Unable to check policies</translation> +<translation id="1412681350727866021">Additional extensions</translation> <translation id="1414315029670184034">Don't allow sites to use your camera</translation> <translation id="1414648216875402825">You are updating to an unstable version of <ph name="PRODUCT_NAME" /> which contains features that are in progress. Crashes and unexpected bugs will occur. Please proceed with caution.</translation> <translation id="1415708812149920388">Clipboard read access denied</translation> @@ -948,6 +950,7 @@ <translation id="1937774647013465102">Cannot import container architecture type <ph name="ARCHITECTURE_CONTAINER" /> with this device which is <ph name="ARCHITECTURE_DEVICE" />. You can try restoring this container into a different device, or you can access the files inside this container image by opening in Files app.</translation> <translation id="1938351510777341717">External command</translation> <translation id="1940546824932169984">Connected devices</translation> +<translation id="1941410638996203291">Start time <ph name="TIME" /></translation> <translation id="1942128823046546853">Read and change all your data on all websites</translation> <translation id="1942600407708803723">Shut down when cover is closed</translation> <translation id="1944528062465413897">Bluetooth pairing code</translation> @@ -1326,6 +1329,7 @@ <translation id="2320295602967756579">Enable Light theme</translation> <translation id="2322193970951063277">Headers and footers</translation> <translation id="2322318151094136999">Ask when a site wants to access serial ports (recommended)</translation> +<translation id="2322622365472107569">End time <ph name="TIME" /></translation> <translation id="2323018538045954000">Saved Wi-Fi networks</translation> <translation id="2325444234681128157">Remember password</translation> <translation id="2326188115274135041">Confirm PIN to turn on automatic unlock</translation> @@ -1431,6 +1435,7 @@ <translation id="2435248616906486374">Network disconnected</translation> <translation id="2435457462613246316">Show password</translation> <translation id="2436186046335138073">Allow <ph name="HANDLER_HOSTNAME" /> to open all <ph name="PROTOCOL" /> links?</translation> +<translation id="2439626940657133600">Loading <ph name="WINDOW_TITLE" /></translation> <translation id="2440604414813129000">View s&ource</translation> <translation id="244231003699905658">Invalid address. Please check the address and try again.</translation> <translation id="2442916515643169563">Text shadow</translation> @@ -1936,6 +1941,7 @@ <translation id="2935654492420446828">Add a school account later</translation> <translation id="2936851848721175671">Backup & restore</translation> <translation id="2938225289965773019">Open <ph name="PROTOCOL" /> links</translation> +<translation id="2939908794993783865">Additional inactive sites</translation> <translation id="2939938020978911855">Show available Bluetooth devices</translation> <translation id="2941112035454246133">Low</translation> <translation id="2942279350258725020">Android messages</translation> @@ -2081,6 +2087,7 @@ <translation id="3090589793601454425">Don't move</translation> <translation id="3090819949319990166">Can't copy external crx file to <ph name="TEMP_CRX_FILE" />.</translation> <translation id="3090871774332213558">"<ph name="DEVICE_NAME" />" paired</translation> +<translation id="3093362725605442088">Read Chrome OS device and component serial numbers.</translation> <translation id="3093714882666365141">Don't allow sites to install payment handlers</translation> <translation id="3094141017404513551">This will separate your browsing from <ph name="EXISTING_USER" /></translation> <translation id="3095871294753148861">Bookmarks, passwords and other browser data are synced with the primary account.</translation> @@ -2268,6 +2275,7 @@ <translation id="3308852433423051161">Loading Google Assistant...</translation> <translation id="3309330461362844500">Certificate profile ID</translation> <translation id="3311445899360743395">Data associated with this app may be removed from this device.</translation> +<translation id="3312883087018430408">To search a specific site or part of Chrome, type its shortcut in the address bar, followed by your preferred keyboard shortcut. For example, to search only Bookmarks, type '@bookmarks', then press Tab or Space.</translation> <translation id="3313622045786997898">Certificate Signature Value</translation> <translation id="3313950410573257029">Check connection</translation> <translation id="3315158641124845231">Hide <ph name="PRODUCT_NAME" /></translation> @@ -2688,6 +2696,7 @@ <translation id="3747077776423672805">To remove apps, go to Settings > Google Play Store > Manage Android preferences > Apps or Application manager. Then tap the app that you want to uninstall (you may need to swipe right or left to find the app). Then tap Uninstall or Disable.</translation> <translation id="3747220812138541072">Show inline writing suggestions that appear as you type</translation> <translation id="3748706263662799310">Report a bug</translation> +<translation id="3750562496035670393">Chrome saved your password to this device, but you can save it to your Google Account instead. Then, all passwords in your Google Account will also be available while you're signed in.</translation> <translation id="3752253558646317685">Get your child to keep lifting their finger to save the fingerprint</translation> <translation id="3752582316358263300">OK...</translation> <translation id="3753033997400164841">Store once. Use everywhere</translation> @@ -3129,6 +3138,7 @@ <translation id="4194570336751258953">Enable tap-to-click</translation> <translation id="4195643157523330669">Open in new tab</translation> <translation id="4195814663415092787">Continue where you left off</translation> +<translation id="4198268995694216131">Additional sites</translation> <translation id="4200689466366162458">Customised words</translation> <translation id="4200983522494130825">New &tab</translation> <translation id="4201546031411513170">You can always choose what to sync in settings.</translation> @@ -3250,6 +3260,7 @@ <translation id="4341577178275615435">To turn caret browsing on or off, use the shortcut F7</translation> <translation id="4341905082470253054">Checking TPM status…</translation> <translation id="434198521554309404">Fast. Secure. Effortless.</translation> +<translation id="4343250402091037179">To search a specific site or part of Chrome, type its shortcut in the address bar, followed by your preferred keyboard shortcut.</translation> <translation id="434404122609091467">With your current service provider</translation> <translation id="4345587454538109430">Configure...</translation> <translation id="4345732373643853732">Username not known to server</translation> @@ -4001,6 +4012,7 @@ <translation id="5153234146675181447">Forget phone</translation> <translation id="5154108062446123722">Advanced settings for <ph name="PRINTING_DESTINATION" /></translation> <translation id="5154702632169343078">Subject</translation> +<translation id="5155327081870541046">In the address bar, enter the shortcut for the site that you want to search, such as '@bookmarks'. Then, press your preferred keyboard shortcut, and enter your search term.</translation> <translation id="5157635116769074044">Pin this Page to Start Screen...</translation> <translation id="5159094275429367735">Set up Crostini</translation> <translation id="5159419673777902220">Your parent has disabled extension permissions</translation> @@ -4619,6 +4631,7 @@ <translation id="5816434091619127343">Requested printer changes would make the printer unusable.</translation> <translation id="5817069030404929329">Move passwords from this device to your Google Account?</translation> <translation id="5817918615728894473">Pair</translation> +<translation id="581840385858998009">Customise wallpaper, avatar, screensaver and more</translation> <translation id="5821565227679781414">Create Shortcut</translation> <translation id="5822095611691580107">Left bud battery level <ph name="BATTERY_PERCENTAGE" />%.</translation> <translation id="5825412242012995131">On (recommended)</translation> @@ -4908,6 +4921,7 @@ <translation id="6116921718742659598">Change language and input settings</translation> <translation id="6119927814891883061">Name device to <ph name="DEVICE_NAME" /></translation> <translation id="6120205520491252677">Pin this page to Start screen...</translation> +<translation id="6121773125605585883">View password with username <ph name="USERNAME" /> for <ph name="WEBSITE" /></translation> <translation id="6122081475643980456">Your Internet connection is being controlled</translation> <translation id="6122093587541546701">Email (optional):</translation> <translation id="6122095009389448667">Continue blocking this site from seeing the clipboard</translation> @@ -4984,6 +4998,7 @@ <translation id="6208725777148613371">Failed to save to <ph name="WEB_DRIVE" /> – <ph name="INTERRUPT_REASON" /></translation> <translation id="6209838773933913227">Component updating</translation> <translation id="6209908325007204267">Your device includes a Chrome Enterprise Upgrade, but your username is not associated with an enterprise account. Please create an enterprise account by visiting g.co/ChromeEnterpriseAccount on a secondary device.</translation> +<translation id="6210282067670792090">In the address bar, use this keyboard shortcut with shortcuts for search engines and site search</translation> <translation id="621172521139737651">{COUNT,plural, =0{Open all in &new tab group}=1{Open in &new tab group}other{Open all ({COUNT}) in &new tab group}}</translation> <translation id="6212039847102026977">Show advanced network properties</translation> <translation id="6212168817037875041">Turn off display</translation> @@ -5467,6 +5482,7 @@ <translation id="6709357832553498500">Connect using <ph name="EXTENSIONNAME" /></translation> <translation id="6710213216561001401">Previous</translation> <translation id="6711146141291425900">Link <ph name="WEB_DRIVE" /> account for downloads</translation> +<translation id="6712943853047024245">You already saved a password with this username for <ph name="WEBSITE" /></translation> <translation id="6713233729292711163">Add work profile</translation> <translation id="6715803357256707211">An error occurred during installation of your Linux application. Click on the notification for details.</translation> <translation id="671619610707606484">This will clear <ph name="TOTAL_USAGE" /> of data stored by sites</translation> @@ -6094,6 +6110,7 @@ <translation id="7385854874724088939">Something went wrong when trying to print. Please check your printer and try again.</translation> <translation id="7385896526023870365">This extension has no additional site access.</translation> <translation id="7387273928653486359">Acceptable</translation> +<translation id="7387951778417998929">To use a search engine other than the default, type its shortcut in the address bar followed by your preferred keyboard shortcut. You can also change your default search engine here.</translation> <translation id="7388209873137778229">Only supported devices are shown.</translation> <translation id="7392118418926456391">Virus scan failed</translation> <translation id="7392915005464253525">R&eopen closed window</translation> @@ -6271,6 +6288,7 @@ <translation id="7559719679815339381">Please wait... Kiosk app is in the process of being updated. Do not remove the USB stick.</translation> <translation id="7560756177962144929">Sync your <ph name="DEVICE_TYPE" /></translation> <translation id="7561196759112975576">Always</translation> +<translation id="7562099761826673163">Personalise your device</translation> <translation id="756445078718366910">Open Browser Window</translation> <translation id="7564847347806291057">End process</translation> <translation id="756503097602602175">You can manage signed-in Google Accounts from <ph name="LINK_BEGIN" />Settings<ph name="LINK_END" />. Permissions that you have granted to websites and apps may apply to all accounts. If you don't want sites or apps to access your account info, you can sign in to your <ph name="DEVICE_TYPE" /> as a guest or browse the web in an <ph name="LINK_2_BEGIN" />Incognito window<ph name="LINK_2_END" />.</translation> @@ -6589,6 +6607,7 @@ <translation id="78526636422538552">Addition of more Google accounts is disabled</translation> <translation id="7853747251428735">More Too&ls</translation> <translation id="7855678561139483478">Move tab to new window</translation> +<translation id="7856654138655787862">Run Chrome OS diagnostic tests.</translation> <translation id="7857093393627376423">Text suggestions</translation> <translation id="7857949311770343000">Is this the new tab page that you were expecting?</translation> <translation id="7858328180167661092"><ph name="APP_NAME" /> (Windows)</translation> @@ -7239,6 +7258,7 @@ <translation id="8551588720239073785">Date and time settings</translation> <translation id="8553342806078037065">Manage other people</translation> <translation id="8554899698005018844">No language</translation> +<translation id="855604308879080518">Let Android apps access USB devices on this Chromebook. Permission will be requested each time that you plug in a USB device. Individual Android apps will ask for additional permissions.</translation> <translation id="8557022314818157177">Keep touching your security key until your fingerprint is captured</translation> <translation id="8557180006508471423">Turn on 'Google Chrome' in Location Services on your Mac</translation> <translation id="8560327176991673955">{COUNT,plural, =0{Open all in &new window}=1{Open in &new window}other{Open all ({COUNT}) in &new window}}</translation>
diff --git a/chrome/app/resources/generated_resources_eu.xtb b/chrome/app/resources/generated_resources_eu.xtb index cb73cbe..ae65e14 100644 --- a/chrome/app/resources/generated_resources_eu.xtb +++ b/chrome/app/resources/generated_resources_eu.xtb
@@ -687,6 +687,7 @@ <translation id="1692115862433274081">Erabili beste kontu bat</translation> <translation id="1692118695553449118">Sinkronizazioa aktibatuta dago</translation> <translation id="1692210323591458290">More iluna</translation> +<translation id="1695487653372841667">Google-rekin zer datu partekatzen diren kontrola dezakezu. Ezarpenetan alda dezakezu aukera hori.</translation> <translation id="169675691788639886">Gailuak SSH zerbitzari bat dauka konfiguratuta. Ez hasi saioa kontuzko informazioa duten kontuekin.</translation> <translation id="1697150536837697295">Artea</translation> <translation id="1697686431566694143">Editatu fitxategia</translation>
diff --git a/chrome/app/resources/generated_resources_km.xtb b/chrome/app/resources/generated_resources_km.xtb index 70a98de..b09001b9 100644 --- a/chrome/app/resources/generated_resources_km.xtb +++ b/chrome/app/resources/generated_resources_km.xtb
@@ -1754,6 +1754,7 @@ <translation id="2757338480560142065">សូមប្រាកដថា ពាក្យសម្ងាត់ដែលអ្នកកំពុងរក្សាទុកត្រូវគ្នានឹងពាក្យសម្ងាត់របស់អ្នកសម្រាប់ <ph name="WEBSITE" /></translation> <translation id="2762441749940182211">បានទប់ស្កាត់កាមេរ៉ា</translation> <translation id="2764786626780673772">ព័ត៌មានលម្អិតអំពី VPN</translation> +<translation id="2765100602267695013">សូមទាក់ទងទៅក្រុមហ៊ុនផ្ដល់សេវាទូរសព្ទចល័តរបស់អ្នក</translation> <translation id="2765217105034171413">តូច</translation> <translation id="2766006623206032690">បិទភ្ជាប់ និងទៅ</translation> <translation id="2766161002040448006">ស្នើសុំមាតាបិតា</translation> @@ -2316,6 +2317,7 @@ <translation id="3370260763947406229">ការកែស្វ័យប្រវត្តិ</translation> <translation id="3371140690572404006">ឧបករណ៍ USB-C (រន្ធមុខផ្នែកខាងស្តាំ)</translation> <translation id="337286756654493126">អានថតឯកសារដែលអ្នកបើកនៅក្នុងកម្មវិធីនេះ</translation> +<translation id="3374294321938930390">បានផ្លាស់ទី '<ph name="BOOKMARK_TITLE" />' ទៅក្នុង '<ph name="NEW_FOLDER_TITLE" />'។</translation> <translation id="3378572629723696641">កម្មវិធីបន្ថែមនេះអាចមានបញ្ហា។</translation> <translation id="337920581046691015"><ph name="PRODUCT_NAME" /> នឹងត្រូវបានតំឡើង។</translation> <translation id="3380365263193509176">កំហុសឆ្គងមិនស្គាល់</translation> @@ -5156,6 +5158,7 @@ <translation id="6385994920693662133">ការព្រមាន - ការធ្វើកំណត់ហេតុលម្អិតត្រូវបានបើក។ កំណត់ហេតុខាងក្រោមអាចរួមបញ្ចូល URL ឬព័ត៌មានរសើបផ្សេងទៀត។ សូមពិនិត្យមើល ហើយប្រាកដថាអ្នកពេញចិត្តក្នុងការដាក់បញ្ជូនព័ត៌មាននេះ។</translation> <translation id="6387674443318562538">បំបែកបញ្ឈរ</translation> <translation id="6388429472088318283">ភាសាសម្រាប់ការស្វែងរក</translation> +<translation id="6388577073199278153">មិនអាចចូលប្រើគណនីឧបករណ៍ចល័តរបស់អ្នកបានទេ</translation> <translation id="6390020764191254941">ផ្លាស់ទីផ្ទាំងទៅវិនដូថ្មី</translation> <translation id="6393156038355142111">ណែនាំពាក្យសម្ងាត់ខ្លាំង</translation> <translation id="6393550101331051049">អនុញ្ញាតឱ្យបង្ហាញខ្លឹមសារគ្មានសុវត្ថិភាព</translation> @@ -5482,6 +5485,7 @@ <translation id="672609503628871915">មើលអ្វីដែលថ្មី</translation> <translation id="67269783048918309">បញ្ជូនទិន្នន័យប្រើប្រាស់ និងវិភាគ។ បច្ចុប្បន្ននេះ ឧបករណ៍នេះកំពុងបញ្ជូនទិន្នន័យនៃការវិភាគ ឧបករណ៍ និងការប្រើប្រាស់កម្មវិធីទៅ Google ដោយស្វ័យប្រវត្តិ។ សកម្មភាពនេះនឹងមិនត្រូវបានធ្វើឡើង ដើម្បីកំណត់អត្តសញ្ញាណកូនរបស់អ្នកនោះទេ តែការធ្វើបែបនេះនឹងជួយដល់ស្ថិរភាពកម្មវិធី និងប្រព័ន្ធ ព្រមទាំងការកែលម្អផ្សេងទៀត។ ទិន្នន័យប្រមូលបានមួយចំនួនក៏នឹងជួយដល់កម្មវិធី និងដៃគូ Google ដូចជាអ្នកអភិវឌ្ឍន៍ Android ផងដែរ។ <ph name="BEGIN_LINK1" />ការកំណត់<ph name="END_LINK1" />នេះត្រូវបានអនុវត្តដោយម្ចាស់។ ប្រសិនបើការកំណត់សកម្មភាពកម្មវិធី និងគេហទំព័របន្ថែមត្រូវបានបើកសម្រាប់កូនរបស់អ្នក នោះទិន្នន័យនេះអាចត្រូវបានរក្សាទុកទៅក្នុងគណនី Google របស់គាត់។ <ph name="BEGIN_LINK2" />ស្វែងយល់បន្ថែម<ph name="END_LINK2" /></translation> <translation id="6727969043791803658">បានភ្ជាប់ ថ្ម <ph name="BATTERY_PERCENTAGE" /> %</translation> +<translation id="6733620523445262364">បានបង្កើត '<ph name="BOOKMARK_TITLE" />'។</translation> <translation id="6735304988756581115">បង្ហាញខុកឃី និងទិន្នន័យគេហទំព័រដ៏ទៃទៀត...</translation> <translation id="6736243959894955139">អាសយដ្ឋាន</translation> <translation id="6737663862851963468">លុបសំបុត្រ Kerberos</translation> @@ -6737,6 +6741,7 @@ <translation id="8006630792898017994">Space ឬ Tab</translation> <translation id="8008356846765065031">បានផ្តាច់អ៊ីនធឺណិត។ សូមពិនិត្យការភ្ជាប់អ៊ីនធឺណិតរបស់អ្នក។</translation> <translation id="8009225694047762179">គ្រប់គ្រងពាក្យសម្ងាត់</translation> +<translation id="8011372169388649948">បានផ្លាស់ទី '<ph name="BOOKMARK_TITLE" />'។</translation> <translation id="8012647001091218357">យើងមិនអាចទាក់ទងឪពុកម្តាយរបស់អ្នកទេនាពេលបច្ចុប្បន្ន។ សូមព្យាយាមម្តងទៀត។</translation> <translation id="8013993649590906847">ប្រសិនបើរូបភាពមិនមានការពណ៌នាដែលមានប្រយោជន៍ Chrome នឹងព្យាយាមផ្ដល់ការពណ៌នាដែលមានប្រយោជន៍សម្រាប់អ្នក។ រូបភាពត្រូវបានផ្ញើទៅ Google ដើម្បីបង្កើតការពណ៌នា។</translation> <translation id="8014154204619229810">កម្មវិធីអាប់ដេតកំពុងដំណើរការនៅពេលឥឡូវនេះ។ ធ្វើឲ្យថ្មីឡើងវិញក្នុងរយៈពេលមួយនាទីទៀតដើម្បីពិនិត្យម្តងទៀត។</translation>
diff --git a/chrome/app/resources/generated_resources_ko.xtb b/chrome/app/resources/generated_resources_ko.xtb index 2f74e78..310de2b3 100644 --- a/chrome/app/resources/generated_resources_ko.xtb +++ b/chrome/app/resources/generated_resources_ko.xtb
@@ -690,6 +690,7 @@ <translation id="1692115862433274081">다른 계정 사용</translation> <translation id="1692118695553449118">동기화 사용 중</translation> <translation id="1692210323591458290">진한 보라색</translation> +<translation id="1695487653372841667">사용자는 Google과 어떤 데이터를 공유할지 관리할 수 있으며 언제든지 설정에서 변경할 수 있습니다</translation> <translation id="169675691788639886">기기에 SSH 서버가 구성되어 있습니다. 민감한 계정을 사용하여 로그인하지 마세요.</translation> <translation id="1697150536837697295">예술</translation> <translation id="1697686431566694143">파일 수정</translation>
diff --git a/chrome/app/resources/generated_resources_ms.xtb b/chrome/app/resources/generated_resources_ms.xtb index e59c7dd..7bb4f7a 100644 --- a/chrome/app/resources/generated_resources_ms.xtb +++ b/chrome/app/resources/generated_resources_ms.xtb
@@ -163,6 +163,7 @@ <translation id="1153356358378277386">Peranti digandingkan</translation> <translation id="1153636665119721804">Program Perlindungan Lanjutan Google</translation> <translation id="1155816283571436363">Menyambung kepada telefon anda</translation> +<translation id="1158080958325422608">Jadikan Huruf Besar</translation> <translation id="1158238185437008462">Lihat kenangan</translation> <translation id="1161575384898972166">Sila log masuk ke <ph name="TOKEN_NAME" /> untuk mengeksport sijil klien.</translation> <translation id="116173250649946226">Pentadbir anda telah menetapkan tema lalai yang tidak boleh ditukar.</translation> @@ -697,6 +698,7 @@ <translation id="1700079447639026019">Tapak yang tidak boleh menggunakan kuki buat selama-lamanya</translation> <translation id="1703331064825191675">Jangan bimbang tentang kata laluan anda</translation> <translation id="1703666494654169921">Jangan benarkan laman menggunakan peranti atau data realiti maya</translation> +<translation id="1704097193565924901">Menggunakan huruf besar</translation> <translation id="1704230497453185209">Jangan benarkan laman memainkan bunyi</translation> <translation id="1704970325597567340">Semakan keselamatan dijalankan pada <ph name="DATE" /></translation> <translation id="1706586824377653884">Ditambahkan oleh pentadbir anda</translation> @@ -3581,6 +3583,7 @@ <translation id="4681453295291708042">Lumpuhkan Kongsi Berdekatan</translation> <translation id="4681930562518940301">Buka &imej asal dalam tab baharu</translation> <translation id="4682551433947286597">Kertas dinding dipaparkan pada Skrin Log Masuk.</translation> +<translation id="4683629100208651599">Jadikan Huruf Kecil</translation> <translation id="4683947955326903992"><ph name="PERCENTAGE" />% (lalai)</translation> <translation id="4684427112815847243">Segerakkan semua</translation> <translation id="4684471265911890182"><ph name="APP_NAME" /> sedang cuba mengakses kamera. Matikan suis privasi kamera untuk membenarkan akses.</translation> @@ -5976,6 +5979,7 @@ <translation id="7257173066616499747">Rangkaian Wi-Fi</translation> <translation id="725758059478686223">Perkhidmatan Cetak</translation> <translation id="7257666756905341374">Baca data yang anda salin dan tampal</translation> +<translation id="7258192266780953209">Perubahan</translation> <translation id="7258225044283673131">Aplikasi tidak memberikan respons. Pilih "Paksa tutup" untuk menutup apl.</translation> <translation id="7262004276116528033">Perkhidmatan log masuk ini dihoskan oleh <ph name="SAML_DOMAIN" /></translation> <translation id="7264564921322372728"><ph name="BEGIN_PARAGRAPH1" />Cuba langkah penyelesaian masalah berikut:
diff --git a/chrome/app/resources/generated_resources_pl.xtb b/chrome/app/resources/generated_resources_pl.xtb index 6173553..ef501fb 100644 --- a/chrome/app/resources/generated_resources_pl.xtb +++ b/chrome/app/resources/generated_resources_pl.xtb
@@ -1427,7 +1427,7 @@ <translation id="2448810255793562605">Automatyczne skanowanie Switch Access</translation> <translation id="2450223707519584812">Nie będzie można dodawać użytkowników, ponieważ brakuje kluczy Google API. Więcej szczegółów znajdziesz na <ph name="DETAILS_URL" />.</translation> <translation id="2450849356604136918">Brak aktywnych widoków</translation> -<translation id="2451298179137331965">2x</translation> +<translation id="2451298179137331965">2 x</translation> <translation id="245322989586167203">Strony zwykle łączą się z portami szeregowymi w celu obsługi funkcji związanych z przesyłaniem danych, na przykład aby skonfigurować sieć</translation> <translation id="2453706416476934374">Aby otrzymywać dostosowane odpowiedzi, gdy <ph name="SUPERVISED_USER_NAME" /> zadaje pytania, zezwól Asystentowi na dostęp do zrzutu bieżącej zawartości ekranu użytkownika <ph name="SUPERVISED_USER_NAME" />. Mogą tam znajdować się też informacje o odtwarzanych piosenkach i filmach.</translation> <translation id="2453860139492968684">Zakończ</translation> @@ -7680,7 +7680,7 @@ <translation id="9019062154811256702">Odczytywanie i zmienianie ustawień autouzupełniania</translation> <translation id="9019894137004772119">Używaj lokalizacji. Zezwól na używanie lokalizacji Twojego urządzenia przez aplikacje i usługi, które mają dostęp do lokalizacji. Google może okresowo gromadzić dane o lokalizacji i używać ich anonimowo, by zwiększyć dokładność lokalizacji oraz usprawnić działanie związanych z nią usług.</translation> <translation id="9019956081903586892">Nie udało się pobrać słownika do sprawdzania pisowni</translation> -<translation id="9020362265352758658">4x</translation> +<translation id="9020362265352758658">4 x</translation> <translation id="9021662811137657072">Wykryto wirusa</translation> <translation id="902236149563113779">Strony zwykle śledzą pozycję kamery na potrzeby funkcji AR, na przykład w grach czy do wyświetlania trasy</translation> <translation id="9022847679183471841">Tego konta na tym komputerze używa już <ph name="AVATAR_NAME" />.</translation>
diff --git a/chrome/app/resources/generated_resources_tr.xtb b/chrome/app/resources/generated_resources_tr.xtb index 71042fe..aa64d22 100644 --- a/chrome/app/resources/generated_resources_tr.xtb +++ b/chrome/app/resources/generated_resources_tr.xtb
@@ -1737,6 +1737,7 @@ <translation id="2757338480560142065">Kaydettiğiniz şifrenin <ph name="WEBSITE" /> için kullandığınız şifreyle eşleştiğinden emin olun</translation> <translation id="2762441749940182211">Kamera engellendi</translation> <translation id="2764786626780673772">VPN ayrıntıları</translation> +<translation id="2765100602267695013">Lütfen mobil sağlayıcınızla iletişime geçin</translation> <translation id="2765217105034171413">Küçük</translation> <translation id="2766006623206032690">Ya&pıştır ve git</translation> <translation id="2766161002040448006">Ebeveyninize sorun</translation> @@ -2299,6 +2300,7 @@ <translation id="3370260763947406229">Otomatik düzelt</translation> <translation id="3371140690572404006">USB-C cihaz (sağ ön bağlantı noktası)</translation> <translation id="337286756654493126">Uygulamada açtığınız klasörleri okuma</translation> +<translation id="3374294321938930390">"<ph name="BOOKMARK_TITLE" />", "<ph name="NEW_FOLDER_TITLE" />" klasörüne taşındı.</translation> <translation id="3378572629723696641">Bu uzantı bozuk olabilir.</translation> <translation id="337920581046691015"><ph name="PRODUCT_NAME" /> yüklenecek.</translation> <translation id="3380365263193509176">Bilinmeyen hata</translation> @@ -5138,6 +5140,7 @@ <translation id="6385994920693662133">Uyarı: Ayrıntılı günlük kaydı etkin. Aşağıdaki günlüklerde URL'ler veya diğer hassas bilgiler bulunabilir. Lütfen bu bilgileri inceleyin ve göndermenizde sakınca olmadığından emin olun.</translation> <translation id="6387674443318562538">Dikey Böl</translation> <translation id="6388429472088318283">Dil ara</translation> +<translation id="6388577073199278153">Mobil hesabınıza erişilemiyor</translation> <translation id="6390020764191254941">Sekmeyi Yeni Pencereye Taşı</translation> <translation id="6393156038355142111">Güçlü şifre öner</translation> <translation id="6393550101331051049">Güvenli olmayan içerik göstermesine izin verilen siteler</translation> @@ -5464,6 +5467,7 @@ <translation id="672609503628871915">Yenilikleri görüntüle</translation> <translation id="67269783048918309">Kullanım ve teşhis verilerini gönder. Bu cihaz şu anda teşhis, cihaz ve uygulama kullanımı verilerini otomatik olarak Google'a gönderiyor. Bu veriler, çocuğunuzun kimliğini tespit etmek için kullanılmaz; sistem ve uygulama kararlılığı ile diğer özellikleri iyileştirmeye yardımcı olur. Bazı birleştirilmiş veriler, Google uygulamalarına ve iş ortaklarına da (ör. Android geliştiricileri) yardımcı olur. Bu <ph name="BEGIN_LINK1" />ayar<ph name="END_LINK1" />, cihazın sahibi tarafından zorunlu kılınmıştır. Çocuğunuz için ek Web ve Uygulama Etkinliği açıksa bu veriler çocuğunuzun Google hesabına kaydedilebilir. <ph name="BEGIN_LINK2" />Daha Fazla Bilgi<ph name="END_LINK2" /></translation> <translation id="6727969043791803658">Bağlandı, %<ph name="BATTERY_PERCENTAGE" /> pil</translation> +<translation id="6733620523445262364">'<ph name="BOOKMARK_TITLE" />' oluşturuldu.</translation> <translation id="6735304988756581115">Çerezleri ve diğer site verilerini göster...</translation> <translation id="6736243959894955139">Adres</translation> <translation id="6737663862851963468">Kerberos biletini kaldır</translation> @@ -6717,6 +6721,7 @@ <translation id="8006630792898017994">Boşluk veya Sekme tuşu</translation> <translation id="8008356846765065031">İnternet bağlantısı kesildi. Lütfen İnternet bağlantınızı kontrol edin.</translation> <translation id="8009225694047762179">Şifreleri Yönet</translation> +<translation id="8011372169388649948">'<ph name="BOOKMARK_TITLE" />' taşındı.</translation> <translation id="8012647001091218357">Şu anda ebeveynlerinize erişemedik. Lütfen tekrar deneyin.</translation> <translation id="8013993649590906847">Bir resmin işe yarar bir açıklaması yoksa, Chrome sizin için bir açıklama sağlamaya çalışır. Açıklama oluşturmak için resimler Google'a gönderilir.</translation> <translation id="8014154204619229810">Güncelleyici şu anda çalışıyor. Tekrar kontrol etmek için bir dakika içinde yenileyin.</translation>
diff --git a/chrome/app/resources/generated_resources_uz.xtb b/chrome/app/resources/generated_resources_uz.xtb index 473e3ba..d375cf0 100644 --- a/chrome/app/resources/generated_resources_uz.xtb +++ b/chrome/app/resources/generated_resources_uz.xtb
@@ -160,6 +160,7 @@ <translation id="1152346050262092795">Hisobingizni tasdiqlash uchun parolingizni qayta kiriting.</translation> <translation id="1153356358378277386">Ulangan qurilmalar</translation> <translation id="1153636665119721804">Google Kengaytirilgan himoya dasturi</translation> +<translation id="1155545602507378023">Yoʻq, faqat shu qurilmada saqlansin</translation> <translation id="1155816283571436363">Telefonga ulanilmoqda</translation> <translation id="1158080958325422608">Katta harflarda boʻlsin</translation> <translation id="1158238185437008462">Xotiralarni koʻrish</translation> @@ -394,6 +395,7 @@ <translation id="1410197035576869800">Ilova ikonkasi</translation> <translation id="1410616244180625362"><ph name="HOST" /> saytiga kamerangizdan foydalanishga ruxsat berish</translation> <translation id="1410806973194718079">Qoidalar tekshirilmadi</translation> +<translation id="1412681350727866021">Qoʻshimcha kengaytmalar</translation> <translation id="1414315029670184034">Saytlarga kameradan foydalanishni taqiqlash</translation> <translation id="1414648216875402825"><ph name="PRODUCT_NAME" />’ning beqaror versiyasiga yangilanmoqdasiz, unda yakunlanmagan xususiyatlar bor. Qotib qolishlar va kamchiliklar uchraydi. Ehtiyotkorlik bilan davom eting.</translation> <translation id="1415708812149920388">Klipborddan o‘qishga ruxsat berilmagan</translation> @@ -938,6 +940,7 @@ <translation id="1937774647013465102"><ph name="ARCHITECTURE_DEVICE" /> protsessorli qurilmangizga <ph name="ARCHITECTURE_CONTAINER" /> turidagi zaxiraviy nusxasi tiklanmaydi. Arxivni boshqa turdagi protsessorli qurilmada tiklash yoki Fayllar ilovasi orqali tarkibidagi zaxiralangan fayllarini ochish mumkin.</translation> <translation id="1938351510777341717">Tashqi buyruq</translation> <translation id="1940546824932169984">Ulangan qurilmalar</translation> +<translation id="1941410638996203291">Boshlanish vaqti: <ph name="TIME" /></translation> <translation id="1942128823046546853">Barcha saytlardagi maʼlumotlaringizni oʻqish va oʻzgartirish</translation> <translation id="1942600407708803723">Muqova yopilganda ishni yakunlash</translation> <translation id="1944528062465413897">Bluetooth orqali ulanish kodi:</translation> @@ -1316,6 +1319,7 @@ <translation id="2320295602967756579">Kunduzgi mavzuni yoqish</translation> <translation id="2322193970951063277">Sarlavha va taglavhalar</translation> <translation id="2322318151094136999">Sayt ketma-ket portlardan foydalanish oldidan ruxsat olinsin (tavsiya etiladi)</translation> +<translation id="2322622365472107569">Tugash vaqti: <ph name="TIME" /></translation> <translation id="2323018538045954000">Saqlangan Wi-Fi tarmoqlar</translation> <translation id="2325444234681128157">Parol eslab qolinsin</translation> <translation id="2326188115274135041">Qulfni avtomatik yechish funksiyasini yoqish uchun PIN kodni tasdiqlang</translation> @@ -1421,6 +1425,7 @@ <translation id="2435248616906486374">Tarmoqqa ulanish yo‘q</translation> <translation id="2435457462613246316">Parolni ko‘rsatish</translation> <translation id="2436186046335138073"><ph name="HANDLER_HOSTNAME" /> saytiga “<ph name="PROTOCOL" />” kabi havolalarga ishlov berishga ruxsat berilsinmi?</translation> +<translation id="2439626940657133600"><ph name="WINDOW_TITLE" /> yuklanmoqda</translation> <translation id="2440604414813129000">Sahifa &kodini ko‘rish</translation> <translation id="244231003699905658">Xato manzil. Manzilni tekshirib, qaytadan urining.</translation> <translation id="2442916515643169563">Matn soyasi</translation> @@ -1926,6 +1931,7 @@ <translation id="2935654492420446828">Keyinroq maktab hisobini kiritish</translation> <translation id="2936851848721175671">Zaxiralash va tiklash</translation> <translation id="2938225289965773019"><ph name="PROTOCOL" /> havolalarini ochish</translation> +<translation id="2939908794993783865">Qoʻshimcha nofaol saytlar</translation> <translation id="2939938020978911855">Mavjud Bluetooth qurilmalar ko‘rsatilsin</translation> <translation id="2941112035454246133">Past</translation> <translation id="2942279350258725020">Android Xabarlar</translation> @@ -2071,6 +2077,7 @@ <translation id="3090589793601454425">Olinmasin</translation> <translation id="3090819949319990166">Tashqi CRX faylni <ph name="TEMP_CRX_FILE" /> jildiga nusxalab bo‘lmadi.</translation> <translation id="3090871774332213558">“<ph name="DEVICE_NAME" />” qurilmasi ulandi</translation> +<translation id="3093362725605442088">Chrome OS qurilmasi va komponenti seriya raqamlarini oʻqish.</translation> <translation id="3093714882666365141">Saytlarga toʻlov vositalarini oʻrnatishni taqiqlash</translation> <translation id="3094141017404513551">Brauzer axborotlari <ph name="EXISTING_USER" /> hisobidan ajratiladi</translation> <translation id="3095871294753148861">Bukmarklar, parollar va boshqa brauzer maʼlumotlari asosiy hisobga sinxronlanadi.</translation> @@ -2258,6 +2265,7 @@ <translation id="3308852433423051161">Google Assistent yuklanmoqda...</translation> <translation id="3309330461362844500">Sertifikat profili identifikatori</translation> <translation id="3311445899360743395">Bu ilovaga aloqador maʼlumotlar ushbu qurilmadan tozalanadi.</translation> +<translation id="3312883087018430408">Muayyan sayt yoki Chrome qismini qidirish uchun manzillar qatorida buyruqni kiritib, keyin oldindan belgilangan tezkor tugmani bosing. Masalan, faqat Bukmarklardan qidirish uchun “@bookmarks” deb kiritib, keyin Tab yoki Boʻsh joy tugmasini bosing.</translation> <translation id="3313622045786997898">Sertifikat imzosi qiymati</translation> <translation id="3313950410573257029">Aloqani tekshirish</translation> <translation id="3315158641124845231"><ph name="PRODUCT_NAME" />‘ni yashirish</translation> @@ -2678,6 +2686,7 @@ <translation id="3747077776423672805">Ilovalarni oʻchirib tashlash uchun Sozlamalar > Google Play Market > Android sozlamalari boshqaruvi > Ilovalar yoki Ilovalar menejeri menyusiga kiring. Kerakli ilovani tanlab (ilovani topish uchun ekranni oʻng yoki chapga suring), “Oʻchirib tashlash” yoki “Faolsizlantirish” tugmasini bosing.</translation> <translation id="3747220812138541072">Takliflar yozish vaqtida qator ichida chiqsin</translation> <translation id="3748706263662799310">Xatolik haqida xabar berish</translation> +<translation id="3750562496035670393">Chrome parolni bu qurilmaga saqlagan, lekin uni Google hisobingizga saqlashingiz ham mumkin. Keyin hisobingizga kirgan qurilmalarda Google hisobingizda saqlangan barcha parollardan foydalana olasiz.</translation> <translation id="3752253558646317685">Barmoq izini saqlash uchun farzandingiz barmoqlarini koʻtarib, qayta bosib kiritsin</translation> <translation id="3752582316358263300">OK...</translation> <translation id="3753033997400164841">Bir marta saqlash. Doim ishlatish</translation> @@ -3119,6 +3128,7 @@ <translation id="4194570336751258953">Teginib bosish xususiyatini yoqish</translation> <translation id="4195643157523330669">Yangi varaqda ochish</translation> <translation id="4195814663415092787">Avval ochilgan ichki oynalar</translation> +<translation id="4198268995694216131">Qoʻshimcha saytlar</translation> <translation id="4200689466366162458">Boshqa so‘zlar</translation> <translation id="4200983522494130825">Yangi &ichki oyna</translation> <translation id="4201546031411513170">Nimani sinxronlashni istalgan vaqtda sozlamalar orqali tanlash mumkin.</translation> @@ -3240,6 +3250,7 @@ <translation id="4341577178275615435">Faol kursor rejimini yoqish yoki oʻchirish uchun F7 tugmasidan foydalaning</translation> <translation id="4341905082470253054">TPM holati tekshirilmoqda...</translation> <translation id="434198521554309404">Tez. Xavfsiz. Qulay.</translation> +<translation id="4343250402091037179">Muayyan sayt yoki Chrome qismini qidirish uchun manzillar qatorida buyruqni kiritib, keyin oldindan belgilangan tezkor tugmani bosing.</translation> <translation id="434404122609091467">Joriy xizmat provayderingiz orqali</translation> <translation id="4345587454538109430">Sozlash...</translation> <translation id="4345732373643853732">Bu login serverga notanish</translation> @@ -3992,6 +4003,7 @@ <translation id="5153234146675181447">Telefonni unutish</translation> <translation id="5154108062446123722"><ph name="PRINTING_DESTINATION" /> – kengaytirilgan sozlamalar</translation> <translation id="5154702632169343078">Subyekt</translation> +<translation id="5155327081870541046">Manzil qatorida qidiriladigan sayt uchun “@bookmarks” kabi buyruqni kiriting. Keyin oldindan belgilangan tezkor tugmani bosib, qidiruv iborasini kiriting.</translation> <translation id="5157635116769074044">Ushbu sahifani bosh ekranga qadab qo‘yish...</translation> <translation id="5159094275429367735">Crostini taʼminotini sozlash</translation> <translation id="5159419673777902220">Ota-onangiz bu kengaytma ruxsatlarini olib tashlagan</translation> @@ -4610,6 +4622,7 @@ <translation id="5816434091619127343">So‘ralgan o‘zgartirishlar printerni ishdan chiqarishi mumkin.</translation> <translation id="5817069030404929329">Parollar bu qurilmadan Google hisobignizga olinsinmi?</translation> <translation id="5817918615728894473">Ulanish</translation> +<translation id="581840385858998009">Fon rasmi, avatar, ekran lavhasi va boshqalarni moslash</translation> <translation id="5821565227679781414">Yorliq yaratish</translation> <translation id="5822095611691580107">Chap quloqlik batareya quvvati: <ph name="BATTERY_PERCENTAGE" />%.</translation> <translation id="5825412242012995131">Yoqilgan (tavsiya etiladi)</translation> @@ -4899,6 +4912,7 @@ <translation id="6116921718742659598">Til va kiritish usuli sozlamalarini o‘zgartirish</translation> <translation id="6119927814891883061">Qurilmani nomlash: <ph name="DEVICE_NAME" /></translation> <translation id="6120205520491252677">Ushbu sahifani bosh ekranga qadab qo‘yish...</translation> +<translation id="6121773125605585883"><ph name="WEBSITE" /> saytining <ph name="USERNAME" /> foydalanuvchisi parolini koʻrish</translation> <translation id="6122081475643980456">Internet aloqangiz nazorat qilinmoqda</translation> <translation id="6122093587541546701">Email (ixtiyoriy):</translation> <translation id="6122095009389448667">Saytning klipbordga ruxsati taqiqlanaversin</translation> @@ -4975,6 +4989,7 @@ <translation id="6208725777148613371"><ph name="WEB_DRIVE" /> omboriga saqlanmadi, sababi: <ph name="INTERRUPT_REASON" /></translation> <translation id="6209838773933913227">Komponent yangilanmoqda</translation> <translation id="6209908325007204267">Bu qurilmada Chrome korporativ litsenziyasi bor, lekin qurilmadagi foydalanuvchi nomi korporativ hisobga tegishli emas. Boshqa qurilma orqali g.co/ChromeEnterpriseAccount sahifasidan korporativ hisob oching.</translation> +<translation id="6210282067670792090">Qidiruv tizimlari yoki sayt orqali qidirish uchun manzillar qatorida quyidagi buyruq va tezkor tugmalardan foydalaning</translation> <translation id="621172521139737651">{COUNT,plural, =0{Barchasini &yangi varaqlar guruhida ochish}=1{&Yangi varaqlar guruhida ochish}other{Barchasini ({COUNT}) &yangi varaqlar guruhida ochish}}</translation> <translation id="6212039847102026977">Qo‘shimcha tarmoq parametrlari</translation> <translation id="6212168817037875041">Ekranni o‘chirish</translation> @@ -5459,6 +5474,7 @@ <translation id="6709357832553498500"><ph name="EXTENSIONNAME" /> orqali ulanish</translation> <translation id="6710213216561001401">Avvalgi</translation> <translation id="6711146141291425900">Yuklanmalar uchun <ph name="WEB_DRIVE" /> hisobini ulash</translation> +<translation id="6712943853047024245"><ph name="WEBSITE" /> uchun parol bu foydalanuvchi nomi bilan allaqachon saqlangan</translation> <translation id="6713233729292711163">Ish profilini kiritish</translation> <translation id="6715803357256707211">Linux ilovasini oʻrnatishda xatolik yuz berdi. Batafsil axborot olish uchun bildirishnoma ustiga bosing.</translation> <translation id="671619610707606484">Saytlar joylagan <ph name="TOTAL_USAGE" /> maʼlumot tozalab tashlanadi</translation> @@ -6087,6 +6103,7 @@ <translation id="7385854874724088939">Chop etish vaqtida xatolik yuz berdi. Printeringizni tekshiring va qaytadan urinib ko‘ring.</translation> <translation id="7385896526023870365">Bu kengaytmaning saytga qoʻshimcha ruxsati yoʻq.</translation> <translation id="7387273928653486359">Qoniqarli</translation> +<translation id="7387951778417998929">Boshqa qidiruv vositasidan foydalanish uchun manzillar qatorida buyruqni kiritib, keyin oldindan belgilangan tezkor tugmani bosing. Shuningdek, asosiy qidiruv tizimini shu yerdan almashtirish mumkin.</translation> <translation id="7388209873137778229">Faqat quyidagi qurilmalarda ishlaydi.</translation> <translation id="7392118418926456391">Virusdan tekshirish amalga oshmadi</translation> <translation id="7392915005464253525">Yopilgan oynani &qayta ochish</translation> @@ -6264,6 +6281,7 @@ <translation id="7559719679815339381">Kiosk ilovasi yangilanmoqda... Tashqi USB xotira qurilmasini chiqarib olmang.</translation> <translation id="7560756177962144929"><ph name="DEVICE_TYPE" /> qurilmangizni sinxronlang</translation> <translation id="7561196759112975576">Har doim</translation> +<translation id="7562099761826673163">Qurilmani oʻzingizga moslang</translation> <translation id="756445078718366910">Brauzer oynasini ochish</translation> <translation id="7564847347806291057">Jarayonni yakunlash</translation> <translation id="756503097602602175">Google hisoblarini <ph name="LINK_BEGIN" />Sozlamalar<ph name="LINK_END" /> orqali boshqarishingiz mumkin. Sayt va ilovalarga berilgan ruxsatlar barcha hisoblarga tatbiq etiladi. Sayt va ilovalarni hisob axborotingizga kirishini istamasangiz, <ph name="DEVICE_TYPE" /> qurilmangizga mehmon sifatida kiring yoki internetda <ph name="LINK_2_BEGIN" />inkognito oynasida<ph name="LINK_2_END" /> ishlang.</translation> @@ -6581,6 +6599,7 @@ <translation id="78526636422538552">Boshqa Google hisoblarini qoʻshish xususiyati oʻchiq.</translation> <translation id="7853747251428735">Qo‘shimcha &vositalar</translation> <translation id="7855678561139483478">Varaqni yangi oynaga olish</translation> +<translation id="7856654138655787862">Chrome OS diagnostika testlarini ishga tushirish.</translation> <translation id="7857093393627376423">Matn takliflari</translation> <translation id="7857949311770343000">Bu boshqa tezkor havolalar sahifasimi?</translation> <translation id="7858328180167661092"><ph name="APP_NAME" /> (Windows)</translation> @@ -7230,6 +7249,7 @@ <translation id="8551588720239073785">Sana va vaqt sozlamalari</translation> <translation id="8553342806078037065">Boshqa foydalanuvchilarni boshqarish</translation> <translation id="8554899698005018844">Til tanlanmagan</translation> +<translation id="855604308879080518">Android ilovalarga bu Chromebook USB qurilmalaridan foydalanishiga ruxsat berish. Har safar USB qurilma ulanganda ruxsat soʻraladi. Ayrim Android ilovalar qoʻshimcha ruxsatlar soʻrashi ham mumkin.</translation> <translation id="8557022314818157177">Barmoq izi yozib olinguncha elektron kalitga teginib turing</translation> <translation id="8557180006508471423">Mac qurilmangizda “Location Services” orqali “Google Chrome” brauzerini yoqing</translation> <translation id="8560327176991673955">{COUNT,plural, =0{Barchasini &yangi oynada ochish}=1{&Yangi oynada ochish}other{Barchasini ({COUNT}) &yangi oynada ochish}}</translation>
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn index 436cf76..75f223b 100644 --- a/chrome/browser/BUILD.gn +++ b/chrome/browser/BUILD.gn
@@ -6366,15 +6366,6 @@ "signin/signin_util_win.h", ] } - - # TODO(https://crbug.com/1198523: Remove this once enable_dice_support is no - # longer defined on Lacros. - if (is_chromeos_lacros) { - sources -= [ - "signin/signin_manager_factory.cc", - "signin/signin_manager_factory.h", - ] - } } if (enable_media_remoting) {
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc index f4dc686f..632c9060 100644 --- a/chrome/browser/about_flags.cc +++ b/chrome/browser/about_flags.cc
@@ -2345,6 +2345,13 @@ media::switches::kHdrNetForceEnabled}, {"Force disabled", media::switches::kHdrNetOverride, media::switches::kHdrNetForceDisabled}}; + +const FeatureEntry::Choice kAutoFramingOverrideChoices[] = { + {"Default", "", ""}, + {"Force enabled", media::switches::kAutoFramingOverride, + media::switches::kAutoFramingForceEnabled}, + {"Force disabled", media::switches::kAutoFramingOverride, + media::switches::kAutoFramingForceDisabled}}; #endif #if BUILDFLAG(IS_CHROMEOS_ASH) @@ -4382,6 +4389,9 @@ {"hdrnet-override", flag_descriptions::kHdrNetOverrideName, flag_descriptions::kHdrNetOverrideDescription, kOsCrOS, MULTI_VALUE_TYPE(kHdrNetOverrideChoices)}, + {"auto-framing-override", flag_descriptions::kAutoFramingOverrideName, + flag_descriptions::kAutoFramingOverrideDescription, kOsCrOS, + MULTI_VALUE_TYPE(kAutoFramingOverrideChoices)}, {"camera-app-document-manual-crop", flag_descriptions::kCameraAppDocumentManualCropName, flag_descriptions::kCameraAppDocumentManualCropDescription, kOsCrOS,
diff --git a/chrome/browser/android/autofill_assistant/client_android.cc b/chrome/browser/android/autofill_assistant/client_android.cc index db02b2d..63a9784 100644 --- a/chrome/browser/android/autofill_assistant/client_android.cc +++ b/chrome/browser/android/autofill_assistant/client_android.cc
@@ -23,6 +23,7 @@ #include "chrome/browser/android/autofill_assistant/ui_controller_android_utils.h" #include "chrome/browser/autofill/android/personal_data_manager_android.h" #include "chrome/browser/autofill/personal_data_manager_factory.h" +#include "chrome/browser/browser_process.h" #include "chrome/browser/metrics/chrome_metrics_service_accessor.h" #include "chrome/browser/password_manager/chrome_password_manager_client.h" #include "chrome/browser/profiles/profile.h" @@ -40,6 +41,7 @@ #include "components/password_manager/core/browser/password_manager_client.h" #include "components/signin/public/identity_manager/account_info.h" #include "components/signin/public/identity_manager/identity_manager.h" +#include "components/variations/service/variations_service.h" #include "components/version_info/android/channel_getter.h" #include "content/public/browser/browser_task_traits.h" #include "content/public/browser/browser_thread.h" @@ -532,12 +534,12 @@ } std::string ClientAndroid::GetCountryCode() const { - JNIEnv* env = AttachCurrentThread(); - auto code = Java_AutofillAssistantClient_getCountryCode(env, java_object_); - // Use fallback "ZZ". It is an unused country code. - if (!code) + variations::VariationsService* variations_service = + g_browser_process->variations_service(); + // Use fallback "ZZ" if no country is available. + if (!variations_service || variations_service->GetLatestCountry().empty()) return "ZZ"; - return base::android::ConvertJavaStringToUTF8(env, code); + return base::ToUpperASCII(variations_service->GetLatestCountry()); } DeviceContext ClientAndroid::GetDeviceContext() const {
diff --git a/chrome/browser/apps/guest_view/web_view_browsertest.cc b/chrome/browser/apps/guest_view/web_view_browsertest.cc index 3fd6e20..2285838 100644 --- a/chrome/browser/apps/guest_view/web_view_browsertest.cc +++ b/chrome/browser/apps/guest_view/web_view_browsertest.cc
@@ -4609,10 +4609,7 @@ } }; -// TODO(crbug.com/1052397): Revisit the macro expression once build flag switch -// of lacros-chrome is complete. -#if (defined(OS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)) || defined(OS_MAC) || \ - defined(OS_WIN) +#if defined(OS_LINUX) || defined(OS_MAC) || defined(OS_WIN) // This verifies the fix for http://crbug.com/667708. IN_PROC_BROWSER_TEST_F(ChromeSignInWebViewTest, ClosingChromeSignInShouldNotCrash) {
diff --git a/chrome/browser/apps/intent_helper/chromeos_intent_picker_helpers.cc b/chrome/browser/apps/intent_helper/chromeos_intent_picker_helpers.cc index 9f537ac..a959522 100644 --- a/chrome/browser/apps/intent_helper/chromeos_intent_picker_helpers.cc +++ b/chrome/browser/apps/intent_helper/chromeos_intent_picker_helpers.cc
@@ -116,7 +116,7 @@ /*show_stay_in_chrome=*/true, /*show_remember_selection=*/true, base::BindOnce(&OnIntentPickerClosedChromeOs, web_contents, - ui_auto_display_service, url)); + ui_auto_display_service, PickerShowState::kPopOut, url)); } } // namespace @@ -150,6 +150,7 @@ void OnIntentPickerClosedChromeOs( content::WebContents* web_contents, IntentPickerAutoDisplayService* ui_auto_display_service, + PickerShowState show_state, const GURL& url, const std::string& launch_name, PickerEntryType entry_type, @@ -216,12 +217,9 @@ CloseOrGoBack(web_contents); } } - IntentHandlingMetrics::PickerAction action = - IntentHandlingMetrics::GetPickerAction(entry_type, close_reason, - should_persist); - IntentHandlingMetrics::Platform platform = - IntentHandlingMetrics::GetDestinationPlatform(action); - IntentHandlingMetrics::RecordIntentPickerMetrics(action, platform); + + IntentHandlingMetrics::RecordIntentPickerMetrics(entry_type, close_reason, + should_persist, show_state); } } // namespace apps
diff --git a/chrome/browser/apps/intent_helper/chromeos_intent_picker_helpers.h b/chrome/browser/apps/intent_helper/chromeos_intent_picker_helpers.h index 63dc216..b9aa90728 100644 --- a/chrome/browser/apps/intent_helper/chromeos_intent_picker_helpers.h +++ b/chrome/browser/apps/intent_helper/chromeos_intent_picker_helpers.h
@@ -35,6 +35,7 @@ void OnIntentPickerClosedChromeOs( content::WebContents* web_contents, IntentPickerAutoDisplayService* ui_auto_display_service, + PickerShowState show_state, const GURL& url, const std::string& launch_name, PickerEntryType entry_type,
diff --git a/chrome/browser/apps/intent_helper/common_apps_navigation_throttle.cc b/chrome/browser/apps/intent_helper/common_apps_navigation_throttle.cc index 9be0c36..abb4a3a 100644 --- a/chrome/browser/apps/intent_helper/common_apps_navigation_throttle.cc +++ b/chrome/browser/apps/intent_helper/common_apps_navigation_throttle.cc
@@ -126,6 +126,18 @@ return url; } +IntentHandlingMetrics::Platform GetMetricsPlatform(mojom::AppType app_type) { + switch (app_type) { + case mojom::AppType::kArc: + return IntentHandlingMetrics::Platform::ARC; + case mojom::AppType::kWeb: + return IntentHandlingMetrics::Platform::PWA; + default: + NOTREACHED(); + return IntentHandlingMetrics::Platform::ARC; + } +} + } // namespace // static @@ -231,10 +243,8 @@ if (!last_committed_url.is_valid() || last_committed_url.IsAboutBlank()) web_contents->ClosePage(); - IntentHandlingMetrics::RecordIntentPickerUserInteractionMetrics( - GetPickerEntryType(app_type), - apps::IntentPickerCloseReason::PREFERRED_APP_FOUND, - /*should_persist=*/false); + IntentHandlingMetrics::RecordPreferredAppLinkClickMetrics( + GetMetricsPlatform(app_type)); return true; }
diff --git a/chrome/browser/apps/intent_helper/intent_picker_helpers.cc b/chrome/browser/apps/intent_helper/intent_picker_helpers.cc index 12dc984..5e9a21f 100644 --- a/chrome/browser/apps/intent_helper/intent_picker_helpers.cc +++ b/chrome/browser/apps/intent_helper/intent_picker_helpers.cc
@@ -66,9 +66,9 @@ IntentPickerCloseReason close_reason, bool should_persist) { #if defined(OS_CHROMEOS) - OnIntentPickerClosedChromeOs(web_contents, ui_auto_display_service, url, - launch_name, entry_type, close_reason, - should_persist); + OnIntentPickerClosedChromeOs(web_contents, ui_auto_display_service, + PickerShowState::kOmnibox, url, launch_name, + entry_type, close_reason, should_persist); #else const bool should_launch_app = close_reason == apps::IntentPickerCloseReason::OPEN_APP;
diff --git a/chrome/browser/apps/intent_helper/metrics/intent_handling_metrics.cc b/chrome/browser/apps/intent_helper/metrics/intent_handling_metrics.cc index b10b66a..dd121fad 100644 --- a/chrome/browser/apps/intent_helper/metrics/intent_handling_metrics.cc +++ b/chrome/browser/apps/intent_helper/metrics/intent_handling_metrics.cc
@@ -6,30 +6,167 @@ #include "ash/components/arc/metrics/arc_metrics_constants.h" #include "base/metrics/histogram_macros.h" +#include "base/notreached.h" +#include "chrome/browser/apps/intent_helper/chromeos_intent_picker_helpers.h" #if BUILDFLAG(IS_CHROMEOS_ASH) #include "ash/components/arc/metrics/arc_metrics_service.h" #endif // BUILDFLAG(IS_CHROMEOS_ASH) -namespace apps { +namespace { +using PickerAction = apps::IntentHandlingMetrics::PickerAction; +using IntentPickerAction = apps::IntentHandlingMetrics::IntentPickerAction; +using Platform = apps::IntentHandlingMetrics::Platform; -IntentHandlingMetrics::IntentHandlingMetrics() = default; - -void IntentHandlingMetrics::RecordIntentPickerMetrics(PickerAction action, - Platform platform) { - UMA_HISTOGRAM_ENUMERATION("ChromeOS.Apps.IntentPickerAction", action); +void RecordDestinationPlatformMetric( + apps::IntentHandlingMetrics::Platform platform) { UMA_HISTOGRAM_ENUMERATION("ChromeOS.Apps.IntentPickerDestinationPlatform", platform); } -void IntentHandlingMetrics::RecordIntentPickerUserInteractionMetrics( +IntentPickerAction GetIntentPickerAction( + apps::PickerEntryType entry_type, + apps::IntentPickerCloseReason close_reason, + bool should_persist) { + switch (close_reason) { + case apps::IntentPickerCloseReason::ERROR_BEFORE_PICKER: + case apps::IntentPickerCloseReason::ERROR_AFTER_PICKER: + return IntentPickerAction::kError; + case apps::IntentPickerCloseReason::DIALOG_DEACTIVATED: + return IntentPickerAction::kDialogDeactivated; + case apps::IntentPickerCloseReason::STAY_IN_CHROME: + return should_persist ? IntentPickerAction::kChromeSelectedAndPreferred + : IntentPickerAction::kChromeSelected; + case apps::IntentPickerCloseReason::OPEN_APP: + switch (entry_type) { + case apps::PickerEntryType::kArc: + return should_persist + ? IntentPickerAction::kArcAppSelectedAndPreferred + : IntentPickerAction::kArcAppSelected; + case apps::PickerEntryType::kWeb: + return should_persist ? IntentPickerAction::kPwaSelectedAndPreferred + : IntentPickerAction::kPwaSelected; + case apps::PickerEntryType::kDevice: + case apps::PickerEntryType::kMacOs: + case apps::PickerEntryType::kUnknown: + NOTREACHED(); + return IntentPickerAction::kInvalid; + } + case apps::IntentPickerCloseReason::PREFERRED_APP_FOUND: + // For the HTTP/HTTPS Intent Picker, preferred app metrics are recorded + // separately in RecordPreferredAppLinkClickMetrics. + NOTREACHED(); + return IntentPickerAction::kInvalid; + } +} + +Platform GetIntentPickerDestinationPlatform(IntentPickerAction action) { + switch (action) { + case IntentPickerAction::kArcAppSelected: + case IntentPickerAction::kArcAppSelectedAndPreferred: + return Platform::ARC; + case IntentPickerAction::kPwaSelected: + case IntentPickerAction::kPwaSelectedAndPreferred: + return Platform::PWA; + case IntentPickerAction::kChromeSelected: + case IntentPickerAction::kChromeSelectedAndPreferred: + case IntentPickerAction::kDialogDeactivated: + case IntentPickerAction::kError: + return Platform::CHROME; + case IntentPickerAction::kInvalid: + NOTREACHED(); + return Platform::CHROME; + } +} + +#if BUILDFLAG(IS_CHROMEOS_ASH) + +// Converts the provided |entry_type|, |close_reason| and |should_persist| +// boolean to a PickerAction value for recording in UMA. +PickerAction GetExternalProtocolPickerAction( + apps::PickerEntryType entry_type, + apps::IntentPickerCloseReason close_reason, + bool should_persist) { + switch (close_reason) { + case apps::IntentPickerCloseReason::ERROR_BEFORE_PICKER: + return PickerAction::ERROR_BEFORE_PICKER; + case apps::IntentPickerCloseReason::ERROR_AFTER_PICKER: + return PickerAction::ERROR_AFTER_PICKER; + case apps::IntentPickerCloseReason::DIALOG_DEACTIVATED: + return PickerAction::DIALOG_DEACTIVATED; + case apps::IntentPickerCloseReason::PREFERRED_APP_FOUND: + switch (entry_type) { + case apps::PickerEntryType::kUnknown: + return PickerAction::PREFERRED_CHROME_BROWSER_FOUND; + case apps::PickerEntryType::kArc: + return PickerAction::PREFERRED_ARC_ACTIVITY_FOUND; + case apps::PickerEntryType::kWeb: + return PickerAction::PREFERRED_PWA_FOUND; + case apps::PickerEntryType::kDevice: + case apps::PickerEntryType::kMacOs: + NOTREACHED(); + return PickerAction::INVALID; + } + case apps::IntentPickerCloseReason::STAY_IN_CHROME: + return should_persist ? PickerAction::CHROME_PREFERRED_PRESSED + : PickerAction::CHROME_PRESSED; + case apps::IntentPickerCloseReason::OPEN_APP: + switch (entry_type) { + case apps::PickerEntryType::kUnknown: + NOTREACHED(); + return PickerAction::INVALID; + case apps::PickerEntryType::kArc: + return should_persist ? PickerAction::ARC_APP_PREFERRED_PRESSED + : PickerAction::ARC_APP_PRESSED; + case apps::PickerEntryType::kWeb: + return should_persist ? PickerAction::PWA_APP_PREFERRED_PRESSED + : PickerAction::PWA_APP_PRESSED; + case apps::PickerEntryType::kDevice: + return PickerAction::DEVICE_PRESSED; + case apps::PickerEntryType::kMacOs: + return PickerAction::MAC_OS_APP_PRESSED; + } + } + + NOTREACHED(); + return PickerAction::INVALID; +} + +#endif // BUILDFLAG(IS_CHROMEOS_ASH) + +} // namespace + +namespace apps { + +IntentHandlingMetrics::IntentHandlingMetrics() = default; + +void IntentHandlingMetrics::RecordIntentPickerMetrics( PickerEntryType entry_type, IntentPickerCloseReason close_reason, - bool should_persist) { - PickerAction action = - GetPickerAction(entry_type, close_reason, should_persist); - Platform platform = GetDestinationPlatform(action); - RecordIntentPickerMetrics(action, platform); + bool should_persist, + PickerShowState show_state) { + IntentPickerAction action = + GetIntentPickerAction(entry_type, close_reason, should_persist); + Platform platform = GetIntentPickerDestinationPlatform(action); + + UMA_HISTOGRAM_ENUMERATION("ChromeOS.Intents.IntentPickerAction", action); + switch (show_state) { + case PickerShowState::kOmnibox: + UMA_HISTOGRAM_ENUMERATION( + "ChromeOS.Intents.IntentPickerAction.FromOmniboxIcon", action); + break; + case PickerShowState::kPopOut: + UMA_HISTOGRAM_ENUMERATION( + "ChromeOS.Intents.IntentPickerAction.FromAutoPopOut", action); + break; + } + + RecordDestinationPlatformMetric(platform); +} + +void IntentHandlingMetrics::RecordPreferredAppLinkClickMetrics( + Platform platform) { + RecordDestinationPlatformMetric(platform); } #if BUILDFLAG(IS_CHROMEOS_ASH) @@ -64,7 +201,7 @@ // TODO(crbug.com/985233) For now External Protocol Dialog is only querying // ARC apps, so there's no need to record a destination platform. PickerAction action = - GetPickerAction(entry_type, close_reason, should_persist); + GetExternalProtocolPickerAction(entry_type, close_reason, should_persist); UMA_HISTOGRAM_ENUMERATION("ChromeOS.Apps.ExternalProtocolDialog", action); } @@ -74,86 +211,4 @@ UMA_HISTOGRAM_ENUMERATION("ChromeOS.Apps.OpenBrowser", type); } -// static -IntentHandlingMetrics::Platform IntentHandlingMetrics::GetDestinationPlatform( - PickerAction picker_action) { - switch (picker_action) { - case PickerAction::ARC_APP_PRESSED: - case PickerAction::ARC_APP_PREFERRED_PRESSED: - case PickerAction::PREFERRED_ARC_ACTIVITY_FOUND: - return Platform::ARC; - case PickerAction::PWA_APP_PRESSED: - case PickerAction::PWA_APP_PREFERRED_PRESSED: - case PickerAction::PREFERRED_PWA_FOUND: - return Platform::PWA; - case PickerAction::MAC_OS_APP_PRESSED: - return Platform::MAC_OS; - case PickerAction::ERROR_BEFORE_PICKER: - case PickerAction::ERROR_AFTER_PICKER: - case PickerAction::DIALOG_DEACTIVATED: - case PickerAction::CHROME_PRESSED: - case PickerAction::CHROME_PREFERRED_PRESSED: - case PickerAction::PREFERRED_CHROME_BROWSER_FOUND: - return Platform::CHROME; - case PickerAction::DEVICE_PRESSED: - return Platform::DEVICE; - case PickerAction::OBSOLETE_ALWAYS_PRESSED: - case PickerAction::OBSOLETE_JUST_ONCE_PRESSED: - case PickerAction::INVALID: - break; - } - NOTREACHED(); - return Platform::ARC; -} - -// static -IntentHandlingMetrics::PickerAction IntentHandlingMetrics::GetPickerAction( - PickerEntryType entry_type, - IntentPickerCloseReason close_reason, - bool should_persist) { - switch (close_reason) { - case IntentPickerCloseReason::ERROR_BEFORE_PICKER: - return PickerAction::ERROR_BEFORE_PICKER; - case IntentPickerCloseReason::ERROR_AFTER_PICKER: - return PickerAction::ERROR_AFTER_PICKER; - case IntentPickerCloseReason::DIALOG_DEACTIVATED: - return PickerAction::DIALOG_DEACTIVATED; - case IntentPickerCloseReason::PREFERRED_APP_FOUND: - switch (entry_type) { - case PickerEntryType::kUnknown: - return PickerAction::PREFERRED_CHROME_BROWSER_FOUND; - case PickerEntryType::kArc: - return PickerAction::PREFERRED_ARC_ACTIVITY_FOUND; - case PickerEntryType::kWeb: - return PickerAction::PREFERRED_PWA_FOUND; - case PickerEntryType::kDevice: - case PickerEntryType::kMacOs: - NOTREACHED(); - return PickerAction::INVALID; - } - case IntentPickerCloseReason::STAY_IN_CHROME: - return should_persist ? PickerAction::CHROME_PREFERRED_PRESSED - : PickerAction::CHROME_PRESSED; - case IntentPickerCloseReason::OPEN_APP: - switch (entry_type) { - case PickerEntryType::kUnknown: - NOTREACHED(); - return PickerAction::INVALID; - case PickerEntryType::kArc: - return should_persist ? PickerAction::ARC_APP_PREFERRED_PRESSED - : PickerAction::ARC_APP_PRESSED; - case PickerEntryType::kWeb: - return should_persist ? PickerAction::PWA_APP_PREFERRED_PRESSED - : PickerAction::PWA_APP_PRESSED; - case PickerEntryType::kDevice: - return PickerAction::DEVICE_PRESSED; - case PickerEntryType::kMacOs: - return PickerAction::MAC_OS_APP_PRESSED; - } - } - - NOTREACHED(); - return PickerAction::INVALID; -} - } // namespace apps
diff --git a/chrome/browser/apps/intent_helper/metrics/intent_handling_metrics.h b/chrome/browser/apps/intent_helper/metrics/intent_handling_metrics.h index 58b293e..69ea7d0 100644 --- a/chrome/browser/apps/intent_helper/metrics/intent_handling_metrics.h +++ b/chrome/browser/apps/intent_helper/metrics/intent_handling_metrics.h
@@ -17,6 +17,8 @@ namespace apps { +enum class PickerShowState; + class IntentHandlingMetrics { public: // The type of app the link came from, used for intent handling metrics. @@ -28,6 +30,22 @@ kMaxValue = kWeb, }; + // An action taken by the user in the HTTP/HTTPS Intent Picker dialog. + // These values are persisted to logs. Entries should not be renumbered and + // numeric values should never be reused. + enum class IntentPickerAction { + kInvalid = 0, + kError = 1, + kDialogDeactivated = 2, + kChromeSelected = 3, + kChromeSelectedAndPreferred = 4, + kArcAppSelected = 5, + kArcAppSelectedAndPreferred = 6, + kPwaSelected = 7, + kPwaSelectedAndPreferred = 8, + kMaxValue = kPwaSelectedAndPreferred + }; + // These enums are used to define the buckets for an enumerated UMA histogram // and need to be synced with the ArcIntentHandlerAction enum in enums.xml. // This enum class should also be treated as append-only. @@ -79,24 +97,18 @@ IntentHandlingMetrics(); - // Determines the destination of the current navigation. We know that if the - // |picker_action| is either ERROR or DIALOG_DEACTIVATED the navigation MUST - // stay in Chrome, otherwise the platform is directly encoded in the action - // (like ARC_APP_PRESSED). - static Platform GetDestinationPlatform(PickerAction picker_action); + // Records metrics for the outcome of a user selection in the http/https + // Intent Picker UI. |entry_type| is the type of the selected app, + // |close_reason| is the reason why the bubble closed, and |should_persist| is + // whether the persistence checkbox was checked. + static void RecordIntentPickerMetrics(PickerEntryType entry_type, + IntentPickerCloseReason close_reason, + bool should_persist, + PickerShowState show_state); - // Converts the provided |entry_type|, |close_reason| and |should_persist| - // boolean to a PickerAction value for recording in UMA. - static PickerAction GetPickerAction(PickerEntryType entry_type, - IntentPickerCloseReason close_reason, - bool should_persist); - - static void RecordIntentPickerMetrics(PickerAction action, Platform platform); - - static void RecordIntentPickerUserInteractionMetrics( - PickerEntryType entry_type, - IntentPickerCloseReason close_reason, - bool should_persist); + // Records metrics for when a link is clicked which can handle a preferred + // app, as the result of a user previously setting a preference for that app. + static void RecordPreferredAppLinkClickMetrics(Platform platform); #if BUILDFLAG(IS_CHROMEOS_ASH) static void RecordExternalProtocolMetrics(arc::Scheme scheme,
diff --git a/chrome/browser/apps/intent_helper/metrics/intent_handling_metrics_unittest.cc b/chrome/browser/apps/intent_helper/metrics/intent_handling_metrics_unittest.cc index bc36f200..639631b 100644 --- a/chrome/browser/apps/intent_helper/metrics/intent_handling_metrics_unittest.cc +++ b/chrome/browser/apps/intent_helper/metrics/intent_handling_metrics_unittest.cc
@@ -7,6 +7,8 @@ #include "base/metrics/histogram_macros.h" #include "base/test/gtest_util.h" #include "base/test/metrics/histogram_tester.h" +#include "chrome/browser/apps/intent_helper/apps_navigation_types.h" +#include "chrome/browser/apps/intent_helper/chromeos_intent_picker_helpers.h" #include "chrome/test/base/testing_profile.h" #include "components/prefs/testing_pref_service.h" #include "content/public/test/browser_task_environment.h" @@ -24,21 +26,119 @@ namespace apps { TEST(IntentHandlingMetricsTest, TestRecordIntentPickerMetrics) { + struct TestCase { + PickerEntryType entry_type; + IntentPickerCloseReason close_reason; + bool should_persist; + + IntentHandlingMetrics::IntentPickerAction expected_action; + IntentHandlingMetrics::Platform expected_platform; + }; + + const TestCase kTestCases[]{ + // Open in ARC: + {PickerEntryType::kArc, IntentPickerCloseReason::OPEN_APP, true, + IntentHandlingMetrics::IntentPickerAction::kArcAppSelectedAndPreferred, + IntentHandlingMetrics::Platform::ARC}, + + {PickerEntryType::kArc, IntentPickerCloseReason::OPEN_APP, false, + IntentHandlingMetrics::IntentPickerAction::kArcAppSelected, + IntentHandlingMetrics::Platform::ARC}, + + // Open in PWA: + {PickerEntryType::kWeb, IntentPickerCloseReason::OPEN_APP, true, + IntentHandlingMetrics::IntentPickerAction::kPwaSelectedAndPreferred, + IntentHandlingMetrics::Platform::PWA}, + + {PickerEntryType::kWeb, IntentPickerCloseReason::OPEN_APP, false, + IntentHandlingMetrics::IntentPickerAction::kPwaSelected, + IntentHandlingMetrics::Platform::PWA}, + + // Stay in Chrome: + {PickerEntryType::kWeb, IntentPickerCloseReason::STAY_IN_CHROME, true, + IntentHandlingMetrics::IntentPickerAction::kChromeSelectedAndPreferred, + IntentHandlingMetrics::Platform::CHROME}, + + {PickerEntryType::kWeb, IntentPickerCloseReason::STAY_IN_CHROME, false, + IntentHandlingMetrics::IntentPickerAction::kChromeSelected, + IntentHandlingMetrics::Platform::CHROME}, + + // Dismiss/error: + {PickerEntryType::kWeb, IntentPickerCloseReason::DIALOG_DEACTIVATED, true, + IntentHandlingMetrics::IntentPickerAction::kDialogDeactivated, + IntentHandlingMetrics::Platform::CHROME}, + + {PickerEntryType::kWeb, IntentPickerCloseReason::ERROR_AFTER_PICKER, true, + IntentHandlingMetrics::IntentPickerAction::kError, + IntentHandlingMetrics::Platform::CHROME}, + }; + + for (const auto& test : kTestCases) { + base::HistogramTester histogram_tester; + + IntentHandlingMetrics::RecordIntentPickerMetrics( + test.entry_type, test.close_reason, test.should_persist, + PickerShowState::kOmnibox); + + histogram_tester.ExpectBucketCount("ChromeOS.Intents.IntentPickerAction", + test.expected_action, 1); + histogram_tester.ExpectBucketCount( + "ChromeOS.Apps.IntentPickerDestinationPlatform", test.expected_platform, + 1); + } +} + +TEST(IntentHandlingMetricsTest, TestRecordIntentPickerMetricsWithSource) { base::HistogramTester histogram_tester; - IntentHandlingMetrics test = IntentHandlingMetrics(); - test.RecordIntentPickerMetrics( - IntentHandlingMetrics::PickerAction::ARC_APP_PREFERRED_PRESSED, + IntentHandlingMetrics::RecordIntentPickerMetrics( + PickerEntryType::kArc, IntentPickerCloseReason::OPEN_APP, true, + PickerShowState::kOmnibox); + + histogram_tester.ExpectBucketCount( + "ChromeOS.Intents.IntentPickerAction", + IntentHandlingMetrics::IntentPickerAction::kArcAppSelectedAndPreferred, + 1); + histogram_tester.ExpectBucketCount( + "ChromeOS.Intents.IntentPickerAction.FromOmniboxIcon", + IntentHandlingMetrics::IntentPickerAction::kArcAppSelectedAndPreferred, + 1); + + IntentHandlingMetrics::RecordIntentPickerMetrics( + PickerEntryType::kArc, IntentPickerCloseReason::OPEN_APP, true, + PickerShowState::kPopOut); + + histogram_tester.ExpectBucketCount( + "ChromeOS.Intents.IntentPickerAction", + IntentHandlingMetrics::IntentPickerAction::kArcAppSelectedAndPreferred, + 2); + histogram_tester.ExpectBucketCount( + "ChromeOS.Intents.IntentPickerAction.FromAutoPopOut", + IntentHandlingMetrics::IntentPickerAction::kArcAppSelectedAndPreferred, + 1); +} + +TEST(IntentHandlingMetricsTest, TestRecordPreferredAppLinkClickMetrics) { + base::HistogramTester histogram_tester; + + IntentHandlingMetrics::RecordPreferredAppLinkClickMetrics( IntentHandlingMetrics::Platform::ARC); histogram_tester.ExpectBucketCount( - "ChromeOS.Apps.IntentPickerAction", - IntentHandlingMetrics::PickerAction::ARC_APP_PREFERRED_PRESSED, 1); - histogram_tester.ExpectBucketCount( "ChromeOS.Apps.IntentPickerDestinationPlatform", IntentHandlingMetrics::Platform::ARC, 1); } +TEST(IntentHandlingMetricsTest, TestRecordOpenBrowserMetrics) { + base::HistogramTester histogram_tester; + + IntentHandlingMetrics test; + test.RecordOpenBrowserMetrics(IntentHandlingMetrics::AppType::kArc); + + histogram_tester.ExpectBucketCount("ChromeOS.Apps.OpenBrowser", + IntentHandlingMetrics::AppType::kArc, 1); +} + #if BUILDFLAG(IS_CHROMEOS_ASH) // A fixture class that sets up arc::ArcMetricsService. @@ -68,20 +168,58 @@ TEST_F(IntentHandlingMetricsTestWithMetricsService, TestRecordExternalProtocolUserInteractionMetrics) { - base::HistogramTester histogram_tester; + struct TestCase { + PickerEntryType entry_type; + IntentPickerCloseReason close_reason; + bool should_persist; - IntentHandlingMetrics test = IntentHandlingMetrics(); - test.RecordExternalProtocolUserInteractionMetrics( - profile(), PickerEntryType::kArc, IntentPickerCloseReason::OPEN_APP, - true); + IntentHandlingMetrics::PickerAction expected_action; + bool expected_link_click; + }; - histogram_tester.ExpectBucketCount( - "Arc.UserInteraction", arc::UserInteractionType::APP_STARTED_FROM_LINK, - 1); + const TestCase kTestCases[]{ + // Arc apps: + {PickerEntryType::kArc, IntentPickerCloseReason::OPEN_APP, + /*should_persist=*/true, + IntentHandlingMetrics::PickerAction::ARC_APP_PREFERRED_PRESSED, + /*expected_link_click=*/true}, - histogram_tester.ExpectBucketCount( - "ChromeOS.Apps.ExternalProtocolDialog", - IntentHandlingMetrics::PickerAction::ARC_APP_PREFERRED_PRESSED, 1); + {PickerEntryType::kArc, IntentPickerCloseReason::OPEN_APP, + /*should_persist=*/false, + IntentHandlingMetrics::PickerAction::ARC_APP_PRESSED, + /*expected_link_click=*/true}, + + {PickerEntryType::kArc, IntentPickerCloseReason::PREFERRED_APP_FOUND, + /*should_persist=*/false, + IntentHandlingMetrics::PickerAction::PREFERRED_ARC_ACTIVITY_FOUND, + /*expected_link_click=*/true}, + + // Device: + {PickerEntryType::kDevice, IntentPickerCloseReason::OPEN_APP, + /*should_persist=*/false, + IntentHandlingMetrics::PickerAction::DEVICE_PRESSED, + /*expected_link_click=*/false}, + + // Dismiss: + {PickerEntryType::kArc, IntentPickerCloseReason::DIALOG_DEACTIVATED, + /*should_persist=*/false, + IntentHandlingMetrics::PickerAction::DIALOG_DEACTIVATED, + /*expected_link_click=*/false}, + }; + + for (const auto& test : kTestCases) { + base::HistogramTester histogram_tester; + + IntentHandlingMetrics::RecordExternalProtocolUserInteractionMetrics( + profile(), test.entry_type, test.close_reason, test.should_persist); + + histogram_tester.ExpectBucketCount( + "Arc.UserInteraction", arc::UserInteractionType::APP_STARTED_FROM_LINK, + test.expected_link_click); + + histogram_tester.ExpectBucketCount("ChromeOS.Apps.ExternalProtocolDialog", + test.expected_action, 1); + } } TEST(IntentHandlingMetricsTest, TestRecordExternalProtocolMetrics) { @@ -97,288 +235,4 @@ } #endif // BUILDFLAG(IS_CHROMEOS_ASH) -TEST(IntentHandlingMetricsTest, TestRecordOpenBrowserMetrics) { - base::HistogramTester histogram_tester; - - IntentHandlingMetrics test; - test.RecordOpenBrowserMetrics(IntentHandlingMetrics::AppType::kArc); - - histogram_tester.ExpectBucketCount("ChromeOS.Apps.OpenBrowser", - IntentHandlingMetrics::AppType::kArc, 1); -} - -TEST(IntentHandlingMetricsTest, TestGetPickerAction) { - EXPECT_EQ(IntentHandlingMetrics::PickerAction::ERROR_BEFORE_PICKER, - IntentHandlingMetrics::GetPickerAction( - PickerEntryType::kUnknown, - IntentPickerCloseReason::ERROR_BEFORE_PICKER, - /*should_persist=*/true)); - - EXPECT_EQ( - IntentHandlingMetrics::PickerAction::ERROR_BEFORE_PICKER, - IntentHandlingMetrics::GetPickerAction( - PickerEntryType::kArc, IntentPickerCloseReason::ERROR_BEFORE_PICKER, - /*should_persist=*/true)); - - EXPECT_EQ(IntentHandlingMetrics::PickerAction::ERROR_BEFORE_PICKER, - IntentHandlingMetrics::GetPickerAction( - PickerEntryType::kUnknown, - IntentPickerCloseReason::ERROR_BEFORE_PICKER, - /*should_persist=*/false)); - - EXPECT_EQ( - IntentHandlingMetrics::PickerAction::ERROR_BEFORE_PICKER, - IntentHandlingMetrics::GetPickerAction( - PickerEntryType::kArc, IntentPickerCloseReason::ERROR_BEFORE_PICKER, - /*should_persist=*/false)); - - EXPECT_EQ(IntentHandlingMetrics::PickerAction::ERROR_AFTER_PICKER, - IntentHandlingMetrics::GetPickerAction( - PickerEntryType::kUnknown, - IntentPickerCloseReason::ERROR_AFTER_PICKER, - /*should_persist=*/true)); - - EXPECT_EQ( - IntentHandlingMetrics::PickerAction::ERROR_AFTER_PICKER, - IntentHandlingMetrics::GetPickerAction( - PickerEntryType::kArc, IntentPickerCloseReason::ERROR_AFTER_PICKER, - /*should_persist=*/true)); - - EXPECT_EQ(IntentHandlingMetrics::PickerAction::ERROR_AFTER_PICKER, - IntentHandlingMetrics::GetPickerAction( - PickerEntryType::kUnknown, - IntentPickerCloseReason::ERROR_AFTER_PICKER, - /*should_persist=*/false)); - - EXPECT_EQ( - IntentHandlingMetrics::PickerAction::ERROR_AFTER_PICKER, - IntentHandlingMetrics::GetPickerAction( - PickerEntryType::kArc, IntentPickerCloseReason::ERROR_AFTER_PICKER, - /*should_persist=*/false)); - - // Expect PickerAction::DIALOG_DEACTIVATED if the close_reason is - // DIALOG_DEACTIVATED. - EXPECT_EQ(IntentHandlingMetrics::PickerAction::DIALOG_DEACTIVATED, - IntentHandlingMetrics::GetPickerAction( - PickerEntryType::kUnknown, - IntentPickerCloseReason::DIALOG_DEACTIVATED, - /*should_persist=*/true)); - - EXPECT_EQ( - IntentHandlingMetrics::PickerAction::DIALOG_DEACTIVATED, - IntentHandlingMetrics::GetPickerAction( - PickerEntryType::kArc, IntentPickerCloseReason::DIALOG_DEACTIVATED, - /*should_persist=*/true)); - - EXPECT_EQ(IntentHandlingMetrics::PickerAction::DIALOG_DEACTIVATED, - IntentHandlingMetrics::GetPickerAction( - PickerEntryType::kUnknown, - IntentPickerCloseReason::DIALOG_DEACTIVATED, - /*should_persist=*/false)); - - EXPECT_EQ( - IntentHandlingMetrics::PickerAction::DIALOG_DEACTIVATED, - IntentHandlingMetrics::GetPickerAction( - PickerEntryType::kArc, IntentPickerCloseReason::DIALOG_DEACTIVATED, - /*should_persist=*/false)); - - // Expect PREFERRED FOUND depending on entry type if the close_reason is - // PREFERRED_APP_FOUND. - EXPECT_EQ(IntentHandlingMetrics::PickerAction::PREFERRED_CHROME_BROWSER_FOUND, - IntentHandlingMetrics::GetPickerAction( - PickerEntryType::kUnknown, - IntentPickerCloseReason::PREFERRED_APP_FOUND, - /*should_persist=*/true)); - - EXPECT_EQ( - IntentHandlingMetrics::PickerAction::PREFERRED_ARC_ACTIVITY_FOUND, - IntentHandlingMetrics::GetPickerAction( - PickerEntryType::kArc, IntentPickerCloseReason::PREFERRED_APP_FOUND, - /*should_persist=*/true)); - - EXPECT_EQ( - IntentHandlingMetrics::PickerAction::PREFERRED_PWA_FOUND, - IntentHandlingMetrics::GetPickerAction( - PickerEntryType::kWeb, IntentPickerCloseReason::PREFERRED_APP_FOUND, - /*should_persist=*/true)); - - EXPECT_DCHECK_DEATH(IntentHandlingMetrics::GetPickerAction( - PickerEntryType::kDevice, IntentPickerCloseReason::PREFERRED_APP_FOUND, - /*should_persist=*/true)); - - EXPECT_DCHECK_DEATH(IntentHandlingMetrics::GetPickerAction( - PickerEntryType::kMacOs, IntentPickerCloseReason::PREFERRED_APP_FOUND, - /*should_persist=*/true)); - - EXPECT_EQ(IntentHandlingMetrics::PickerAction::PREFERRED_CHROME_BROWSER_FOUND, - IntentHandlingMetrics::GetPickerAction( - PickerEntryType::kUnknown, - IntentPickerCloseReason::PREFERRED_APP_FOUND, - /*should_persist=*/false)); - - EXPECT_EQ( - IntentHandlingMetrics::PickerAction::PREFERRED_ARC_ACTIVITY_FOUND, - IntentHandlingMetrics::GetPickerAction( - PickerEntryType::kArc, IntentPickerCloseReason::PREFERRED_APP_FOUND, - /*should_persist=*/false)); - - EXPECT_EQ( - IntentHandlingMetrics::PickerAction::PREFERRED_PWA_FOUND, - IntentHandlingMetrics::GetPickerAction( - PickerEntryType::kWeb, IntentPickerCloseReason::PREFERRED_APP_FOUND, - /*should_persist=*/false)); - - EXPECT_DCHECK_DEATH(IntentHandlingMetrics::GetPickerAction( - PickerEntryType::kDevice, IntentPickerCloseReason::PREFERRED_APP_FOUND, - /*should_persist=*/false)); - - EXPECT_DCHECK_DEATH(IntentHandlingMetrics::GetPickerAction( - PickerEntryType::kMacOs, IntentPickerCloseReason::PREFERRED_APP_FOUND, - /*should_persist=*/false)); - - // Expect PREFERRED depending on the value of |should_persist|, and - // |entry_type| to be ignored if reason is STAY_IN_CHROME. - EXPECT_EQ( - IntentHandlingMetrics::PickerAction::CHROME_PREFERRED_PRESSED, - IntentHandlingMetrics::GetPickerAction( - PickerEntryType::kUnknown, IntentPickerCloseReason::STAY_IN_CHROME, - /*should_persist=*/true)); - - EXPECT_EQ(IntentHandlingMetrics::PickerAction::CHROME_PREFERRED_PRESSED, - IntentHandlingMetrics::GetPickerAction( - PickerEntryType::kArc, IntentPickerCloseReason::STAY_IN_CHROME, - /*should_persist=*/true)); - - EXPECT_EQ( - IntentHandlingMetrics::PickerAction::CHROME_PRESSED, - IntentHandlingMetrics::GetPickerAction( - PickerEntryType::kUnknown, IntentPickerCloseReason::STAY_IN_CHROME, - /*should_persist=*/false)); - - EXPECT_EQ(IntentHandlingMetrics::PickerAction::CHROME_PRESSED, - IntentHandlingMetrics::GetPickerAction( - PickerEntryType::kArc, IntentPickerCloseReason::STAY_IN_CHROME, - /*should_persist=*/false)); - - // Expect PREFERRED depending on the value of |should_persist|, and - // different entry type to be chosen if reason is OPEN_APP. - EXPECT_DCHECK_DEATH(IntentHandlingMetrics::GetPickerAction( - PickerEntryType::kUnknown, IntentPickerCloseReason::OPEN_APP, - /*should_persist=*/true)); - - EXPECT_EQ(IntentHandlingMetrics::PickerAction::ARC_APP_PREFERRED_PRESSED, - IntentHandlingMetrics::GetPickerAction( - PickerEntryType::kArc, IntentPickerCloseReason::OPEN_APP, - /*should_persist=*/true)); - - EXPECT_EQ(IntentHandlingMetrics::PickerAction::PWA_APP_PREFERRED_PRESSED, - IntentHandlingMetrics::GetPickerAction( - PickerEntryType::kWeb, IntentPickerCloseReason::OPEN_APP, - /*should_persist=*/true)); - - EXPECT_EQ(IntentHandlingMetrics::PickerAction::DEVICE_PRESSED, - IntentHandlingMetrics::GetPickerAction( - PickerEntryType::kDevice, IntentPickerCloseReason::OPEN_APP, - /*should_persist=*/true)); - - EXPECT_EQ(IntentHandlingMetrics::PickerAction::MAC_OS_APP_PRESSED, - IntentHandlingMetrics::GetPickerAction( - PickerEntryType::kMacOs, IntentPickerCloseReason::OPEN_APP, - /*should_persist=*/true)); - - EXPECT_DCHECK_DEATH(IntentHandlingMetrics::GetPickerAction( - PickerEntryType::kUnknown, IntentPickerCloseReason::OPEN_APP, - /*should_persist=*/false)); - - EXPECT_EQ(IntentHandlingMetrics::PickerAction::ARC_APP_PRESSED, - IntentHandlingMetrics::GetPickerAction( - PickerEntryType::kArc, IntentPickerCloseReason::OPEN_APP, - /*should_persist=*/false)); - - EXPECT_EQ(IntentHandlingMetrics::PickerAction::PWA_APP_PRESSED, - IntentHandlingMetrics::GetPickerAction( - PickerEntryType::kWeb, IntentPickerCloseReason::OPEN_APP, - /*should_persist=*/false)); - - EXPECT_EQ(IntentHandlingMetrics::PickerAction::DEVICE_PRESSED, - IntentHandlingMetrics::GetPickerAction( - PickerEntryType::kDevice, IntentPickerCloseReason::OPEN_APP, - /*should_persist=*/false)); - - EXPECT_EQ(IntentHandlingMetrics::PickerAction::MAC_OS_APP_PRESSED, - IntentHandlingMetrics::GetPickerAction( - PickerEntryType::kMacOs, IntentPickerCloseReason::OPEN_APP, - /*should_persist=*/false)); -} - -TEST(IntentHandlingMetricsTest, TestGetDestinationPlatform) { - const std::string app_id = "fake_package"; - - // When the PickerAction is either ERROR or DIALOG_DEACTIVATED we MUST stay - // in Chrome not taking into account the selected_app_package. - EXPECT_EQ(IntentHandlingMetrics::Platform::CHROME, - IntentHandlingMetrics::GetDestinationPlatform( - IntentHandlingMetrics::PickerAction::ERROR_BEFORE_PICKER)); - EXPECT_EQ(IntentHandlingMetrics::Platform::CHROME, - IntentHandlingMetrics::GetDestinationPlatform( - IntentHandlingMetrics::PickerAction::ERROR_AFTER_PICKER)); - EXPECT_EQ(IntentHandlingMetrics::Platform::CHROME, - IntentHandlingMetrics::GetDestinationPlatform( - IntentHandlingMetrics::PickerAction::DIALOG_DEACTIVATED)); - - // When stay in chrome is selected, or when user remember the stay in - // chrome selection, always expect the platform to be CHROME. - EXPECT_EQ(IntentHandlingMetrics::Platform::CHROME, - IntentHandlingMetrics::GetDestinationPlatform( - IntentHandlingMetrics::PickerAction::CHROME_PRESSED)); - EXPECT_EQ(IntentHandlingMetrics::Platform::CHROME, - IntentHandlingMetrics::GetDestinationPlatform( - - IntentHandlingMetrics::PickerAction::CHROME_PREFERRED_PRESSED)); - EXPECT_EQ( - IntentHandlingMetrics::Platform::CHROME, - IntentHandlingMetrics::GetDestinationPlatform( - - IntentHandlingMetrics::PickerAction::PREFERRED_CHROME_BROWSER_FOUND)); - - // When user select PWA or PWA auto launched, always expect the platform to - // be PWA. - EXPECT_EQ(IntentHandlingMetrics::Platform::PWA, - IntentHandlingMetrics::GetDestinationPlatform( - IntentHandlingMetrics::PickerAction::PWA_APP_PRESSED)); - - EXPECT_EQ( - IntentHandlingMetrics::Platform::PWA, - IntentHandlingMetrics::GetDestinationPlatform( - IntentHandlingMetrics::PickerAction::PWA_APP_PREFERRED_PRESSED)); - EXPECT_EQ(IntentHandlingMetrics::Platform::PWA, - IntentHandlingMetrics::GetDestinationPlatform( - IntentHandlingMetrics::PickerAction::PREFERRED_PWA_FOUND)); - - // When user select ARC app or ARC app auto launched, always expect the - // platform to be ARC. - EXPECT_EQ(IntentHandlingMetrics::Platform::ARC, - IntentHandlingMetrics::GetDestinationPlatform( - IntentHandlingMetrics::PickerAction::ARC_APP_PRESSED)); - - EXPECT_EQ( - IntentHandlingMetrics::Platform::ARC, - IntentHandlingMetrics::GetDestinationPlatform( - IntentHandlingMetrics::PickerAction::ARC_APP_PREFERRED_PRESSED)); - EXPECT_EQ( - IntentHandlingMetrics::Platform::ARC, - IntentHandlingMetrics::GetDestinationPlatform( - IntentHandlingMetrics::PickerAction::PREFERRED_ARC_ACTIVITY_FOUND)); - - // When user select Mac OS app, expect the platform to be MAC_OS. - EXPECT_EQ(IntentHandlingMetrics::Platform::MAC_OS, - IntentHandlingMetrics::GetDestinationPlatform( - IntentHandlingMetrics::PickerAction::MAC_OS_APP_PRESSED)); - - // When user select a device, expect the platform to be DEVICE. - EXPECT_EQ(IntentHandlingMetrics::Platform::DEVICE, - IntentHandlingMetrics::GetDestinationPlatform( - IntentHandlingMetrics::PickerAction::DEVICE_PRESSED)); -} - } // namespace apps
diff --git a/chrome/browser/ash/android_sms/android_sms_app_setup_controller_impl_unittest.cc b/chrome/browser/ash/android_sms/android_sms_app_setup_controller_impl_unittest.cc index 26d38c9..1825cc71 100644 --- a/chrome/browser/ash/android_sms/android_sms_app_setup_controller_impl_unittest.cc +++ b/chrome/browser/ash/android_sms/android_sms_app_setup_controller_impl_unittest.cc
@@ -212,9 +212,9 @@ &controller().registrar(), nullptr, nullptr, nullptr, nullptr); fake_externally_managed_app_manager_->SetHandleInstallRequestCallback( base::BindLambdaForTesting( - [this](const web_app::ExternalInstallOptions& install_options) - -> web_app::ExternallyManagedAppManager::InstallResult { - return {.code = install_result_code_}; + [this](const web_app::ExternalInstallOptions& install_options) { + return web_app::ExternallyManagedAppManager::InstallResult( + install_result_code_); })); setup_controller_ = base::WrapUnique(new AndroidSmsAppSetupControllerImpl(
diff --git a/chrome/browser/ash/app_restore/arc_app_launch_handler.cc b/chrome/browser/ash/app_restore/arc_app_launch_handler.cc index 0fedc85..4f052b3d 100644 --- a/chrome/browser/ash/app_restore/arc_app_launch_handler.cc +++ b/chrome/browser/ash/app_restore/arc_app_launch_handler.cc
@@ -807,10 +807,6 @@ base::UmaHistogramEnumeration(kNoGhostWindowReasonHistogram, NoGhostWindowReason::kFlagDisabled); } - if (!arc::IsArcVmEnabled()) { - base::UmaHistogramEnumeration(kNoGhostWindowReasonHistogram, - NoGhostWindowReason::kNotARCVM); - } if (!exo::WMHelper::HasInstance()) { base::UmaHistogramEnumeration(kNoGhostWindowReasonHistogram, NoGhostWindowReason::kNoExoHelper);
diff --git a/chrome/browser/ash/borealis/borealis_util.cc b/chrome/browser/ash/borealis/borealis_util.cc index ff42ce8..0f2771e 100644 --- a/chrome/browser/ash/borealis/borealis_util.cc +++ b/chrome/browser/ash/borealis/borealis_util.cc
@@ -65,8 +65,7 @@ }; ProtonVersionInfo GetProtonVersionInfo(absl::optional<int> game_id, - Profile* const profile) { - std::string owner_id = ash::ProfileHelper::GetUserIdHashFromProfile(profile); + const std::string& owner_id) { std::vector<std::string> command = {"/usr/bin/vsh", "--owner_id=" + owner_id, "--vm_name=borealis", "--", "/usr/bin/get_proton_version.py"}; @@ -108,7 +107,7 @@ } // If the app id is known and doesn't match, return the version "UNKNOWN" - if (game_id.has_value() && + if (game_id.has_value() && !parsed_game_id.empty() && parsed_game_id.compare(base::StringPrintf("%d", game_id.value())) != 0) { LOG(WARNING) << "Expected GameID " << game_id.value() << " got " << parsed_game_id; @@ -129,7 +128,7 @@ GURL GetSysInfoForUrlAsync(GURL url, absl::optional<int> game_id, - Profile* const profile) { + std::string owner_id) { url = net::AppendQueryParameter(url, kBoardKey, base::SysInfo::HardwareModelName()); url = net::AppendQueryParameter( @@ -141,7 +140,7 @@ url = net::AppendQueryParameter(url, kPlatformVersionKey, base::SysInfo::OperatingSystemVersion()); - ProtonVersionInfo version_info = GetProtonVersionInfo(game_id, profile); + ProtonVersionInfo version_info = GetProtonVersionInfo(game_id, owner_id); if (!version_info.proton.empty()) { url = net::AppendQueryParameter(url, kProtonVersionKey, version_info.proton); @@ -215,7 +214,8 @@ base::ThreadPool::PostTaskAndReplyWithResult( FROM_HERE, base::MayBlock(), - base::BindOnce(&GetSysInfoForUrlAsync, url, borealis_app_id, profile), + base::BindOnce(&GetSysInfoForUrlAsync, url, borealis_app_id, + ash::ProfileHelper::GetUserIdHashFromProfile(profile)), std::move(url_callback)); }
diff --git a/chrome/browser/ash/crostini/crostini_disk.cc b/chrome/browser/ash/crostini/crostini_disk.cc index 9014bb9f..e58a062 100644 --- a/chrome/browser/ash/crostini/crostini_disk.cc +++ b/chrome/browser/ash/crostini/crostini_disk.cc
@@ -247,6 +247,7 @@ case vm_tools::concierge::DiskImageStatus::DISK_STATUS_RESIZED: EmitResizeResultMetric(signal.status()); std::move(callback_).Run(true); + delete this; break; default: LOG(ERROR) << "Failed or unrecognised status when resizing: " @@ -309,6 +310,7 @@ std::move(callback).Run(true); } else if (response->status() == vm_tools::concierge::DiskImageStatus::DISK_STATUS_IN_PROGRESS) { + // The newly created Observer is self-deleting. GetConciergeClient()->AddDiskImageObserver( new Observer(response->command_uuid(), std::move(callback))); } else {
diff --git a/chrome/browser/ash/file_manager/path_util.h b/chrome/browser/ash/file_manager/path_util.h index fae52a7..b318be77 100644 --- a/chrome/browser/ash/file_manager/path_util.h +++ b/chrome/browser/ash/file_manager/path_util.h
@@ -31,7 +31,7 @@ // Absolute path for the folder containing archive mounts. extern const base::FilePath::CharType kArchiveMountPath[]; -// Name of the mount point used to story temporary files for sharing. +// Name of the mount point used to store temporary files for sharing. extern const char kShareCacheMountPointName[]; // Returns FilesApp origin chrome-extension://hhaomjibdihmijegdhdafkllkbggdgoj.
diff --git a/chrome/browser/ash/input_method/assistive_window_controller.cc b/chrome/browser/ash/input_method/assistive_window_controller.cc index 9b50ba0f..13ff0ba 100644 --- a/chrome/browser/ash/input_method/assistive_window_controller.cc +++ b/chrome/browser/ash/input_method/assistive_window_controller.cc
@@ -196,6 +196,7 @@ switch (button.window_type) { case ui::ime::AssistiveWindowType::kEmojiSuggestion: case ui::ime::AssistiveWindowType::kPersonalInfoSuggestion: + case ui::ime::AssistiveWindowType::kMultiWordSuggestion: if (!suggestion_window_view_) return; @@ -251,6 +252,7 @@ break; case ui::ime::AssistiveWindowType::kEmojiSuggestion: case ui::ime::AssistiveWindowType::kPersonalInfoSuggestion: + case ui::ime::AssistiveWindowType::kMultiWordSuggestion: if (!suggestion_window_view_) InitSuggestionWindow(); if (window_.visible) {
diff --git a/chrome/browser/ash/input_method/assistive_window_controller_unittest.cc b/chrome/browser/ash/input_method/assistive_window_controller_unittest.cc index ebca480..5e4e52a 100644 --- a/chrome/browser/ash/input_method/assistive_window_controller_unittest.cc +++ b/chrome/browser/ash/input_method/assistive_window_controller_unittest.cc
@@ -61,9 +61,9 @@ wm::ActivateWindow(window.get()); profile_ = std::make_unique<TestingProfile>(); - accessibility_view_ = new TestAccessibilityView(); + accessibility_view_ = std::make_unique<TestAccessibilityView>(); controller_ = std::make_unique<AssistiveWindowController>( - delegate_.get(), profile_.get(), accessibility_view_); + delegate_.get(), profile_.get(), accessibility_view_.get()); ui::IMEBridge::Get()->SetAssistiveWindowHandler(controller_.get()); // TODO(crbug/1102283): Create MockSuggestionWindowView to be independent of @@ -105,7 +105,7 @@ const std::u16string suggestion_ = u"test"; ui::ime::AssistiveWindowButton emoji_button_; AssistiveWindowProperties emoji_window_; - TestAccessibilityView* accessibility_view_; + std::unique_ptr<TestAccessibilityView> accessibility_view_; base::test::ScopedFeatureList feature_list_; void TearDown() override {
diff --git a/chrome/browser/ash/input_method/fake_suggestion_handler.cc b/chrome/browser/ash/input_method/fake_suggestion_handler.cc index 1095b76..20eb296 100644 --- a/chrome/browser/ash/input_method/fake_suggestion_handler.cc +++ b/chrome/browser/ash/input_method/fake_suggestion_handler.cc
@@ -48,6 +48,7 @@ const ui::ime::AssistiveWindowButton& button, bool highlighted, std::string* error) { + highlighted_suggestion_ = highlighted; return false; }
diff --git a/chrome/browser/ash/input_method/fake_suggestion_handler.h b/chrome/browser/ash/input_method/fake_suggestion_handler.h index c4dd65e..4202c4f 100644 --- a/chrome/browser/ash/input_method/fake_suggestion_handler.h +++ b/chrome/browser/ash/input_method/fake_suggestion_handler.h
@@ -55,6 +55,7 @@ bool GetAcceptedSuggestion() { return accepted_suggestion_; } bool GetDismissedSuggestion() { return dismissed_suggestion_; } std::vector<std::u16string> GetAnnouncements() { return announcements_; } + bool GetHighlightedSuggestion() { return highlighted_suggestion_; } private: int context_id_ = 0; @@ -63,6 +64,7 @@ bool showing_suggestion_ = false; bool accepted_suggestion_ = false; bool dismissed_suggestion_ = false; + bool highlighted_suggestion_ = false; std::vector<std::u16string> announcements_; };
diff --git a/chrome/browser/ash/input_method/multi_word_suggester.cc b/chrome/browser/ash/input_method/multi_word_suggester.cc index 6704778..74a3288 100644 --- a/chrome/browser/ash/input_method/multi_word_suggester.cc +++ b/chrome/browser/ash/input_method/multi_word_suggester.cc
@@ -70,7 +70,12 @@ MultiWordSuggester::MultiWordSuggester( SuggestionHandlerInterface* suggestion_handler) - : suggestion_handler_(suggestion_handler), state_(this) {} + : suggestion_handler_(suggestion_handler), state_(this) { + suggestion_button_.id = ui::ime::ButtonId::kSuggestion; + suggestion_button_.window_type = + ui::ime::AssistiveWindowType::kMultiWordSuggestion; + suggestion_button_.index = 0; +} MultiWordSuggester::~MultiWordSuggester() = default; @@ -124,6 +129,23 @@ case ui::DomCode::TAB: AcceptSuggestion(); return SuggestionStatus::kAccept; + case ui::DomCode::ARROW_DOWN: + if (state_.IsSuggestionHighlighted()) + return SuggestionStatus::kNotHandled; + state_.ToggleSuggestionHighlight(); + SetSuggestionHighlight(true); + return SuggestionStatus::kBrowsing; + case ui::DomCode::ARROW_UP: + if (!state_.IsSuggestionHighlighted()) + return SuggestionStatus::kNotHandled; + state_.ToggleSuggestionHighlight(); + SetSuggestionHighlight(false); + return SuggestionStatus::kBrowsing; + case ui::DomCode::ENTER: + if (!state_.IsSuggestionHighlighted()) + return SuggestionStatus::kNotHandled; + AcceptSuggestion(); + return SuggestionStatus::kAccept; default: return SuggestionStatus::kNotHandled; } @@ -205,6 +227,15 @@ } } +void MultiWordSuggester::SetSuggestionHighlight(bool highlighted) { + std::string error; + suggestion_handler_->SetButtonHighlighted( + focused_context_id_, suggestion_button_, highlighted, &error); + if (!error.empty()) { + LOG(ERROR) << "Failed to set button highlighted. " << error; + } +} + void MultiWordSuggester::Announce(const std::u16string& message) { if (suggestion_handler_) { suggestion_handler_->Announce(message); @@ -299,6 +330,18 @@ .time_first_shown = suggestion_->time_first_shown}; } +void MultiWordSuggester::SuggestionState::ToggleSuggestionHighlight() { + if (!suggestion_) + return; + suggestion_->highlighted = !suggestion_->highlighted; +} + +bool MultiWordSuggester::SuggestionState::IsSuggestionHighlighted() { + if (!suggestion_) + return false; + return suggestion_->highlighted; +} + bool MultiWordSuggester::SuggestionState::IsSuggestionShowing() { return (state_ == State::kPredictionSuggestionShown || state_ == State::kCompletionSuggestionShown ||
diff --git a/chrome/browser/ash/input_method/multi_word_suggester.h b/chrome/browser/ash/input_method/multi_word_suggester.h index ae7b6ca..18c0718 100644 --- a/chrome/browser/ash/input_method/multi_word_suggester.h +++ b/chrome/browser/ash/input_method/multi_word_suggester.h
@@ -65,6 +65,7 @@ size_t confirmed_length; size_t initial_confirmed_length; base::TimeTicks time_first_shown; + bool highlighted = false; }; struct SurroundingText { @@ -89,6 +90,9 @@ // confirmed length or any other suggestion details are correct. void ReconcileSuggestionWithText(); + // Toggles the highlight state of the current suggeston. + void ToggleSuggestionHighlight(); + // As the name suggests, is there a suggestion currently shown to the user? bool IsSuggestionShowing(); @@ -96,6 +100,9 @@ // editing? bool IsCursorAtEndOfText(); + // Has the user highlighted the current suggestion showing? + bool IsSuggestionHighlighted(); + // Returns the current suggestion state if there is any available. absl::optional<Suggestion> GetSuggestion(); @@ -128,6 +135,9 @@ void ResetSuggestionState(); void ResetTextState(); + // Visibly highlight the suggestion in the ui shown to the user. + void SetSuggestionHighlight(bool highlighted); + // Announce the given message to the user. void Announce(const std::u16string& message); @@ -139,6 +149,8 @@ // Current suggestion state SuggestionState state_; + + ui::ime::AssistiveWindowButton suggestion_button_; }; } // namespace input_method
diff --git a/chrome/browser/ash/input_method/multi_word_suggester_unittest.cc b/chrome/browser/ash/input_method/multi_word_suggester_unittest.cc index c62bd43..f417c735 100644 --- a/chrome/browser/ash/input_method/multi_word_suggester_unittest.cc +++ b/chrome/browser/ash/input_method/multi_word_suggester_unittest.cc
@@ -114,6 +114,26 @@ suggester.OnFocus(kFocusedContextId); suggester.OnSurroundingTextChanged(u"", 0, 0); suggester.OnExternalSuggestionsUpdated(suggestions); + SendKeyEvent(&suggester, ui::DomCode::ARROW_UP); + + EXPECT_TRUE(suggestion_handler.GetShowingSuggestion()); + EXPECT_FALSE(suggestion_handler.GetAcceptedSuggestion()); + EXPECT_EQ(suggestion_handler.GetSuggestionText(), u"hi there!"); +} + +TEST(MultiWordSuggesterTest, DoesNotAcceptSuggestionOnArrowDownKeypress) { + FakeSuggestionHandler suggestion_handler; + MultiWordSuggester suggester(&suggestion_handler); + + std::vector<TextSuggestion> suggestions = { + TextSuggestion{.mode = TextSuggestionMode::kPrediction, + .type = TextSuggestionType::kMultiWord, + .text = "hi there!"}, + }; + + suggester.OnFocus(kFocusedContextId); + suggester.OnSurroundingTextChanged(u"", 0, 0); + suggester.OnExternalSuggestionsUpdated(suggestions); SendKeyEvent(&suggester, ui::DomCode::ARROW_DOWN); EXPECT_TRUE(suggestion_handler.GetShowingSuggestion()); @@ -121,6 +141,142 @@ EXPECT_EQ(suggestion_handler.GetSuggestionText(), u"hi there!"); } +TEST(MultiWordSuggesterTest, DoesNotAcceptSuggestionOnEnterKeypress) { + FakeSuggestionHandler suggestion_handler; + MultiWordSuggester suggester(&suggestion_handler); + + std::vector<TextSuggestion> suggestions = { + TextSuggestion{.mode = TextSuggestionMode::kPrediction, + .type = TextSuggestionType::kMultiWord, + .text = "hi there!"}, + }; + + suggester.OnFocus(kFocusedContextId); + suggester.OnSurroundingTextChanged(u"", 0, 0); + suggester.OnExternalSuggestionsUpdated(suggestions); + SendKeyEvent(&suggester, ui::DomCode::ENTER); + + EXPECT_TRUE(suggestion_handler.GetShowingSuggestion()); + EXPECT_FALSE(suggestion_handler.GetAcceptedSuggestion()); + EXPECT_EQ(suggestion_handler.GetSuggestionText(), u"hi there!"); +} + +TEST(MultiWordSuggesterTest, AcceptsSuggestionOnDownPlusEnterPress) { + FakeSuggestionHandler suggestion_handler; + MultiWordSuggester suggester(&suggestion_handler); + + std::vector<TextSuggestion> suggestions = { + TextSuggestion{.mode = TextSuggestionMode::kPrediction, + .type = TextSuggestionType::kMultiWord, + .text = "hi there!"}, + }; + + suggester.OnFocus(kFocusedContextId); + suggester.OnSurroundingTextChanged(u"", 0, 0); + suggester.OnExternalSuggestionsUpdated(suggestions); + SendKeyEvent(&suggester, ui::DomCode::ARROW_DOWN); + SendKeyEvent(&suggester, ui::DomCode::ENTER); + + EXPECT_FALSE(suggestion_handler.GetShowingSuggestion()); + EXPECT_FALSE(suggestion_handler.GetDismissedSuggestion()); + EXPECT_TRUE(suggestion_handler.GetAcceptedSuggestion()); + EXPECT_EQ(suggestion_handler.GetSuggestionText(), u""); +} + +TEST(MultiWordSuggesterTest, HighlightsSuggestionOnDownArrow) { + FakeSuggestionHandler suggestion_handler; + MultiWordSuggester suggester(&suggestion_handler); + + std::vector<TextSuggestion> suggestions = { + TextSuggestion{.mode = TextSuggestionMode::kPrediction, + .type = TextSuggestionType::kMultiWord, + .text = "hi there!"}, + }; + + suggester.OnFocus(kFocusedContextId); + suggester.OnSurroundingTextChanged(u"", 0, 0); + suggester.OnExternalSuggestionsUpdated(suggestions); + SendKeyEvent(&suggester, ui::DomCode::ARROW_DOWN); + + EXPECT_TRUE(suggestion_handler.GetHighlightedSuggestion()); +} + +TEST(MultiWordSuggesterTest, MaintainsHighlightOnMultipleDownArrow) { + FakeSuggestionHandler suggestion_handler; + MultiWordSuggester suggester(&suggestion_handler); + + std::vector<TextSuggestion> suggestions = { + TextSuggestion{.mode = TextSuggestionMode::kPrediction, + .type = TextSuggestionType::kMultiWord, + .text = "hi there!"}, + }; + + suggester.OnFocus(kFocusedContextId); + suggester.OnSurroundingTextChanged(u"", 0, 0); + suggester.OnExternalSuggestionsUpdated(suggestions); + SendKeyEvent(&suggester, ui::DomCode::ARROW_DOWN); + SendKeyEvent(&suggester, ui::DomCode::ARROW_DOWN); + SendKeyEvent(&suggester, ui::DomCode::ARROW_DOWN); + + EXPECT_TRUE(suggestion_handler.GetHighlightedSuggestion()); +} + +TEST(MultiWordSuggesterTest, RemovesHighlightOnDownThenUpArrow) { + FakeSuggestionHandler suggestion_handler; + MultiWordSuggester suggester(&suggestion_handler); + + std::vector<TextSuggestion> suggestions = { + TextSuggestion{.mode = TextSuggestionMode::kPrediction, + .type = TextSuggestionType::kMultiWord, + .text = "hi there!"}, + }; + + suggester.OnFocus(kFocusedContextId); + suggester.OnSurroundingTextChanged(u"", 0, 0); + suggester.OnExternalSuggestionsUpdated(suggestions); + SendKeyEvent(&suggester, ui::DomCode::ARROW_DOWN); + SendKeyEvent(&suggester, ui::DomCode::ARROW_UP); + + EXPECT_FALSE(suggestion_handler.GetHighlightedSuggestion()); +} + +TEST(MultiWordSuggesterTest, HighlightIsNotShownWithUpArrow) { + FakeSuggestionHandler suggestion_handler; + MultiWordSuggester suggester(&suggestion_handler); + + std::vector<TextSuggestion> suggestions = { + TextSuggestion{.mode = TextSuggestionMode::kPrediction, + .type = TextSuggestionType::kMultiWord, + .text = "hi there!"}, + }; + + suggester.OnFocus(kFocusedContextId); + suggester.OnSurroundingTextChanged(u"", 0, 0); + suggester.OnExternalSuggestionsUpdated(suggestions); + SendKeyEvent(&suggester, ui::DomCode::ARROW_UP); + + EXPECT_FALSE(suggestion_handler.GetHighlightedSuggestion()); +} + +TEST(MultiWordSuggesterTest, HighlightIsNotShownWithMultipleUpArrow) { + FakeSuggestionHandler suggestion_handler; + MultiWordSuggester suggester(&suggestion_handler); + + std::vector<TextSuggestion> suggestions = { + TextSuggestion{.mode = TextSuggestionMode::kPrediction, + .type = TextSuggestionType::kMultiWord, + .text = "hi there!"}, + }; + + suggester.OnFocus(kFocusedContextId); + suggester.OnSurroundingTextChanged(u"", 0, 0); + suggester.OnExternalSuggestionsUpdated(suggestions); + SendKeyEvent(&suggester, ui::DomCode::ARROW_UP); + SendKeyEvent(&suggester, ui::DomCode::ARROW_UP); + + EXPECT_FALSE(suggestion_handler.GetHighlightedSuggestion()); +} + TEST(MultiWordSuggesterTest, CalculatesConfirmedLengthForOneWord) { FakeSuggestionHandler suggestion_handler; MultiWordSuggester suggester(&suggestion_handler);
diff --git a/chrome/browser/ash/input_method/ui/assistive_delegate.h b/chrome/browser/ash/input_method/ui/assistive_delegate.h index 9b71267..430f3fa4 100644 --- a/chrome/browser/ash/input_method/ui/assistive_delegate.h +++ b/chrome/browser/ash/input_method/ui/assistive_delegate.h
@@ -28,6 +28,7 @@ kEmojiSuggestion, kPersonalInfoSuggestion, kGrammarSuggestion, + kMultiWordSuggestion, }; struct AssistiveWindowButton {
diff --git a/chrome/browser/ash/phonehub/phone_hub_manager_factory.cc b/chrome/browser/ash/phonehub/phone_hub_manager_factory.cc index 0e9bb0e..6bda3d6 100644 --- a/chrome/browser/ash/phonehub/phone_hub_manager_factory.cc +++ b/chrome/browser/ash/phonehub/phone_hub_manager_factory.cc
@@ -9,6 +9,7 @@ #include "ash/components/phonehub/notification_access_manager_impl.h" #include "ash/components/phonehub/onboarding_ui_tracker_impl.h" #include "ash/components/phonehub/phone_hub_manager_impl.h" +#include "ash/components/phonehub/recent_apps_interaction_handler_impl.h" #include "ash/components/phonehub/screen_lock_manager_impl.h" #include "ash/components/phonehub/user_action_recorder_impl.h" #include "ash/constants/ash_features.h" @@ -168,6 +169,7 @@ NotificationAccessManagerImpl::RegisterPrefs(registry); OnboardingUiTrackerImpl::RegisterPrefs(registry); ScreenLockManagerImpl::RegisterPrefs(registry); + RecentAppsInteractionHandlerImpl::RegisterPrefs(registry); } } // namespace phonehub
diff --git a/chrome/browser/ash/sharesheet/copy_to_clipboard_share_action.cc b/chrome/browser/ash/sharesheet/copy_to_clipboard_share_action.cc index 0d6dafb7..495dc59 100644 --- a/chrome/browser/ash/sharesheet/copy_to_clipboard_share_action.cc +++ b/chrome/browser/ash/sharesheet/copy_to_clipboard_share_action.cc
@@ -90,7 +90,7 @@ kToastDurationMs, /*dismiss_text=*/absl::nullopt, /*visible_on_lock_screen=*/false); - ToastManager::Get()->Show(toast); + ShowToast(toast); } void CopyToClipboardShareAction::OnClosing( @@ -119,5 +119,9 @@ ShareAction::ShouldShowAction(intent, contains_hosted_document); } +void CopyToClipboardShareAction::ShowToast(const ash::ToastData& toast_data) { + ToastManager::Get()->Show(toast_data); +} + } // namespace sharesheet } // namespace ash
diff --git a/chrome/browser/ash/sharesheet/copy_to_clipboard_share_action.h b/chrome/browser/ash/sharesheet/copy_to_clipboard_share_action.h index cd47c8ae..258c364 100644 --- a/chrome/browser/ash/sharesheet/copy_to_clipboard_share_action.h +++ b/chrome/browser/ash/sharesheet/copy_to_clipboard_share_action.h
@@ -10,6 +10,9 @@ class Profile; namespace ash { + +struct ToastData; + namespace sharesheet { class CopyToClipboardShareAction : public ::sharesheet::ShareAction { @@ -31,6 +34,9 @@ bool contains_hosted_document) override; private: + // Virtual so that it can be overridden in testing. + virtual void ShowToast(const ash::ToastData& toast_data); + Profile* profile_; };
diff --git a/chrome/browser/ash/sharesheet/copy_to_clipboard_share_action_unittest.cc b/chrome/browser/ash/sharesheet/copy_to_clipboard_share_action_unittest.cc index 8476a29..b72e18f 100644 --- a/chrome/browser/ash/sharesheet/copy_to_clipboard_share_action_unittest.cc +++ b/chrome/browser/ash/sharesheet/copy_to_clipboard_share_action_unittest.cc
@@ -12,6 +12,7 @@ #include "chrome/test/base/chrome_ash_test_base.h" #include "chrome/test/base/testing_profile.h" #include "components/services/app_service/public/cpp/intent_util.h" +#include "testing/gmock/include/gmock/gmock.h" #include "ui/base/clipboard/clipboard.h" #include "ui/base/clipboard/file_info.h" #include "ui/base/clipboard/test/clipboard_test_util.h" @@ -23,6 +24,18 @@ namespace ash { namespace sharesheet { +namespace { + +class MockCopyToClipboardShareAction : public CopyToClipboardShareAction { + public: + explicit MockCopyToClipboardShareAction(Profile* profile) + : CopyToClipboardShareAction(profile) {} + + MOCK_METHOD(void, ShowToast, (const ash::ToastData& toast_data), (override)); +}; + +} // namespace + class CopyToClipboardShareActionTest : public ChromeAshTestBase { public: CopyToClipboardShareActionTest() = default; @@ -155,5 +168,32 @@ /* contains_hosted_document= */ true)); } +TEST_F(CopyToClipboardShareActionTest, CopyTextShowsToast) { + ::testing::StrictMock<MockCopyToClipboardShareAction> copy_action(profile()); + EXPECT_CALL(copy_action, ShowToast); + + storage::FileSystemURL url = ::sharesheet::FileInDownloads( + profile(), base::FilePath(::sharesheet::kTestTextFile)); + copy_action.LaunchAction( + /*controller=*/nullptr, /*root_view=*/nullptr, + apps_util::CreateShareIntentFromFiles({url.ToGURL()}, + {::sharesheet::kMimeTypeText})); +} + +TEST_F(CopyToClipboardShareActionTest, CopyFilesShowsToast) { + ::testing::StrictMock<MockCopyToClipboardShareAction> copy_action(profile()); + EXPECT_CALL(copy_action, ShowToast); + + storage::FileSystemURL url1 = ::sharesheet::FileInDownloads( + profile(), base::FilePath(::sharesheet::kTestPdfFile)); + storage::FileSystemURL url2 = ::sharesheet::FileInDownloads( + profile(), base::FilePath(::sharesheet::kTestTextFile)); + copy_action.LaunchAction( + /*controller=*/nullptr, /*root_view=*/nullptr, + apps_util::CreateShareIntentFromFiles( + {url1.ToGURL(), url2.ToGURL()}, + {::sharesheet::kMimeTypePdf, ::sharesheet::kMimeTypeText})); +} + } // namespace sharesheet } // namespace ash
diff --git a/chrome/browser/ash/smb_client/smb_service_unittest.cc b/chrome/browser/ash/smb_client/smb_service_unittest.cc index fe610fe..6626467 100644 --- a/chrome/browser/ash/smb_client/smb_service_unittest.cc +++ b/chrome/browser/ash/smb_client/smb_service_unittest.cc
@@ -177,7 +177,7 @@ std::make_unique<FakeSmbProviderClient>()); chromeos::ConciergeClient::InitializeFake(/*fake_cicerone_client=*/nullptr); - // Takes ownership of |disk_mount_manager_|. + // Takes ownership of |disk_mount_manager_|, but Shutdown() must be called. disks::DiskMountManager::InitializeForTesting(disk_mount_manager_); } @@ -185,6 +185,7 @@ smb_service_.reset(); user_manager_enabler_.reset(); profile_manager_.reset(); + disks::DiskMountManager::Shutdown(); chromeos::ConciergeClient::Shutdown(); chromeos::DBusThreadManager::Shutdown(); }
diff --git a/chrome/browser/ash/web_applications/scanning_system_web_app_info.cc b/chrome/browser/ash/web_applications/scanning_system_web_app_info.cc index e7ece9b..d98ad7dd 100644 --- a/chrome/browser/ash/web_applications/scanning_system_web_app_info.cc +++ b/chrome/browser/ash/web_applications/scanning_system_web_app_info.cc
@@ -33,8 +33,12 @@ {"scanning_app_icon_256.png", 256, IDR_SCANNING_APP_ICON_256}, }, *info); - info->theme_color = 0xFFFFFFFF; - info->background_color = 0xFFFFFFFF; + info->theme_color = + web_app::GetDefaultBackgroundColor(/*use_dark_mode=*/false); + info->dark_mode_theme_color = + web_app::GetDefaultBackgroundColor(/*use_dark_mode=*/true); + info->background_color = info->theme_color; + info->dark_mode_background_color = info->dark_mode_theme_color; info->display_mode = blink::mojom::DisplayMode::kStandalone; info->user_display_mode = blink::mojom::DisplayMode::kStandalone;
diff --git a/chrome/browser/ash/web_applications/shortcut_customization_system_web_app_info.cc b/chrome/browser/ash/web_applications/shortcut_customization_system_web_app_info.cc index fec1457..c5be6c8 100644 --- a/chrome/browser/ash/web_applications/shortcut_customization_system_web_app_info.cc +++ b/chrome/browser/ash/web_applications/shortcut_customization_system_web_app_info.cc
@@ -23,6 +23,12 @@ info->scope = GURL(ash::kChromeUIShortcutCustomizationAppURL); info->title = l10n_util::GetStringUTF16(IDS_ASH_SHORTCUT_CUSTOMIZATION_APP_TITLE); + info->theme_color = + web_app::GetDefaultBackgroundColor(/*use_dark_mode=*/false); + info->dark_mode_theme_color = + web_app::GetDefaultBackgroundColor(/*use_dark_mode=*/true); + info->background_color = info->theme_color; + info->dark_mode_background_color = info->dark_mode_theme_color; web_app::CreateIconInfoForSystemWebApp( info->start_url, {{"app_icon_192.png", 192,
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn index 825dae74..52df858 100644 --- a/chrome/browser/chromeos/BUILD.gn +++ b/chrome/browser/chromeos/BUILD.gn
@@ -4588,6 +4588,7 @@ "extensions/login_screen/login/cleanup/extension_cleanup_handler_unittest.cc", "extensions/login_screen/login/cleanup/mock_cleanup_handler.cc", "extensions/login_screen/login/cleanup/mock_cleanup_handler.h", + "extensions/login_screen/login/cleanup/print_jobs_cleanup_handler_unittest.cc", "extensions/login_screen/login/login_api_unittest.cc", "extensions/login_screen/login_screen_ui/ui_handler_unittest.cc", "extensions/permissions_updater_delegate_chromeos_unittest.cc",
diff --git a/chrome/browser/chromeos/extensions/login_screen/login/cleanup/print_jobs_cleanup_handler_unittest.cc b/chrome/browser/chromeos/extensions/login_screen/login/cleanup/print_jobs_cleanup_handler_unittest.cc new file mode 100644 index 0000000..9888f7d --- /dev/null +++ b/chrome/browser/chromeos/extensions/login_screen/login/cleanup/print_jobs_cleanup_handler_unittest.cc
@@ -0,0 +1,182 @@ +// 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 "chrome/browser/chromeos/extensions/login_screen/login/cleanup/print_jobs_cleanup_handler.h" + +#include <memory> +#include <utility> + +#include "base/bind.h" +#include "base/files/scoped_temp_dir.h" +#include "base/test/bind.h" +#include "chrome/browser/ash/login/users/fake_chrome_user_manager.h" +#include "chrome/browser/ash/printing/history/print_job_history_service.h" +#include "chrome/browser/ash/printing/history/print_job_history_service_impl.h" +#include "chrome/browser/ash/printing/history/test_print_job_database.h" +#include "chrome/browser/ash/printing/print_management/printing_manager.h" +#include "chrome/browser/ash/printing/print_management/printing_manager_factory.h" +#include "chrome/browser/chromeos/printing/test_cups_print_job_manager.h" +#include "chrome/common/pref_names.h" +#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/history/core/test/history_service_test_util.h" +#include "components/keyed_service/core/keyed_service.h" +#include "components/prefs/pref_registry_simple.h" +#include "components/prefs/testing_pref_service.h" +#include "components/user_manager/scoped_user_manager.h" +#include "content/public/browser/browser_context.h" +#include "content/public/test/browser_task_environment.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace chromeos { + +using testing::_; +using testing::Invoke; +using testing::WithArg; + +namespace { + +class MockPrintingManager + : public ash::printing::print_management::PrintingManager { + public: + MockPrintingManager(PrintJobHistoryService* print_job_history_service, + history::HistoryService* history_service, + chromeos::CupsPrintJobManager* cups_print_job_manager, + PrefService* pref_service) + : ash::printing::print_management::PrintingManager( + print_job_history_service, + history_service, + cups_print_job_manager, + pref_service) {} + + MockPrintingManager(const MockPrintingManager&) = delete; + MockPrintingManager& operator=(const MockPrintingManager&) = delete; + + ~MockPrintingManager() override {} + + MOCK_METHOD(void, + DeleteAllPrintJobs, + (DeleteAllPrintJobsCallback), + (override)); +}; + +} // namespace + +class PrintJobsCleanupHandlerUnittest : public testing::Test { + protected: + PrintJobsCleanupHandlerUnittest() + : testing_profile_manager_(TestingBrowserProcess::GetGlobal()) {} + + void SetUp() override { + ASSERT_TRUE(testing_profile_manager_.SetUp()); + + // Add a user. + testing_profile_ = testing_profile_manager_.CreateTestingProfile( + account_id.GetUserEmail()); + + // Log in to set active profile. + std::unique_ptr<FakeChromeUserManager> fake_user_manager = + std::make_unique<FakeChromeUserManager>(); + fake_user_manager->AddUser(account_id); + fake_user_manager->LoginUser(account_id); + + // Set up `MockPrintingManager`. + print_job_manager_ = + std::make_unique<chromeos::TestCupsPrintJobManager>(testing_profile_); + auto print_job_database = std::make_unique<TestPrintJobDatabase>(); + print_job_history_service_ = std::make_unique<PrintJobHistoryServiceImpl>( + std::move(print_job_database), print_job_manager_.get(), &test_prefs_); + test_prefs_.registry()->RegisterBooleanPref( + prefs::kDeletePrintJobHistoryAllowed, true); + test_prefs_.registry()->RegisterIntegerPref( + prefs::kPrintJobHistoryExpirationPeriod, 1); + EXPECT_TRUE(history_dir_.CreateUniqueTempDir()); + history_service_ = + history::CreateHistoryService(history_dir_.GetPath(), true); + + ash::printing::print_management::PrintingManagerFactory::GetInstance() + ->SetTestingFactory( + testing_profile_, + base::BindRepeating( + &PrintJobsCleanupHandlerUnittest::MockPrintingManagerFactory, + base::Unretained(this))); + + scoped_user_manager_ = std::make_unique<user_manager::ScopedUserManager>( + std::move(fake_user_manager)); + } + + std::unique_ptr<KeyedService> MockPrintingManagerFactory( + content::BrowserContext* context) { + return std::make_unique<MockPrintingManager>( + print_job_history_service_.get(), history_service_.get(), + print_job_manager_.get(), &test_prefs_); + } + + void TearDown() override { + scoped_user_manager_.reset(); + testing_profile_manager_.DeleteTestingProfile(account_id.GetUserEmail()); + testing::Test::TearDown(); + } + + void SetUpDeleteAllPrintJobsMock(bool success) { + auto* mock = static_cast<MockPrintingManager*>( + ash::printing::print_management::PrintingManagerFactory::GetForProfile( + testing_profile_)); + EXPECT_CALL(*mock, DeleteAllPrintJobs(_)) + .WillOnce(WithArg<0>( + Invoke([success](ash::printing::print_management::PrintingManager:: + DeleteAllPrintJobsCallback callback) { + std::move(callback).Run(success); + }))); + } + + content::BrowserTaskEnvironment task_environment_; + AccountId account_id = AccountId::FromUserEmail("test-user@example.com"); + std::unique_ptr<user_manager::ScopedUserManager> scoped_user_manager_; + TestingPrefServiceSimple test_prefs_; + TestingProfileManager testing_profile_manager_; + TestingProfile* testing_profile_; + base::ScopedTempDir history_dir_; + std::unique_ptr<chromeos::TestCupsPrintJobManager> print_job_manager_; + std::unique_ptr<history::HistoryService> history_service_; + std::unique_ptr<PrintJobHistoryService> print_job_history_service_; +}; + +TEST_F(PrintJobsCleanupHandlerUnittest, Cleanup) { + SetUpDeleteAllPrintJobsMock(/* success =*/true); + + PrintJobsCleanupHandler handler; + + base::RunLoop run_loop; + + CleanupHandler::CleanupHandlerCallback callback = base::BindLambdaForTesting( + [&](const absl::optional<std::string>& error_message) { + ASSERT_FALSE(error_message); + run_loop.QuitClosure().Run(); + }); + + handler.Cleanup(std::move(callback)); + run_loop.Run(); +} + +TEST_F(PrintJobsCleanupHandlerUnittest, CleanupWithError) { + SetUpDeleteAllPrintJobsMock(/* success =*/false); + + PrintJobsCleanupHandler handler; + + base::RunLoop run_loop; + + CleanupHandler::CleanupHandlerCallback callback = base::BindLambdaForTesting( + [&](const absl::optional<std::string>& error_message) { + ASSERT_EQ(error_message, "Failed to delete all print jobs"); + run_loop.QuitClosure().Run(); + }); + + handler.Cleanup(std::move(callback)); + run_loop.Run(); +} + +} // namespace chromeos
diff --git a/chrome/browser/chromeos/extensions/telemetry/api/base_telemetry_extension_api_guard_function_browsertest.cc b/chrome/browser/chromeos/extensions/telemetry/api/base_telemetry_extension_api_guard_function_browsertest.cc index 35faec9..b4b5c1b 100644 --- a/chrome/browser/chromeos/extensions/telemetry/api/base_telemetry_extension_api_guard_function_browsertest.cc +++ b/chrome/browser/chromeos/extensions/telemetry/api/base_telemetry_extension_api_guard_function_browsertest.cc
@@ -162,7 +162,6 @@ // Make sure that current user is not a device owner. auto* const user_manager = GetFakeUserManager(); const AccountId regular_user = AccountId::FromUserEmail("regular@gmail.com"); - user_manager->AddUser(regular_user); user_manager->SetOwnerId(regular_user); CreateExtensionAndRunServiceWorker(GetServiceWorkerForError(
diff --git a/chrome/browser/custom_handlers/protocol_handler_registry_browsertest.cc b/chrome/browser/custom_handlers/protocol_handler_registry_browsertest.cc index c9c06b8..e138ca40 100644 --- a/chrome/browser/custom_handlers/protocol_handler_registry_browsertest.cc +++ b/chrome/browser/custom_handlers/protocol_handler_registry_browsertest.cc
@@ -24,6 +24,7 @@ #include "content/public/common/content_features.h" #include "content/public/test/browser_test.h" #include "content/public/test/browser_test_utils.h" +#include "content/public/test/fenced_frame_test_util.h" #include "net/test/embedded_test_server/embedded_test_server.h" #include "third_party/blink/public/mojom/context_menu/context_menu.mojom.h" @@ -103,6 +104,7 @@ registry->is_loading_ = false; ASSERT_TRUE(registry->IsHandledProtocol(protocol)); } + void RemoveProtocolHandler(const std::string& protocol, const GURL& url) { ProtocolHandler handler = ProtocolHandler::CreateProtocolHandler(protocol, @@ -113,6 +115,14 @@ registry->RemoveHandler(handler); ASSERT_FALSE(registry->IsHandledProtocol(protocol)); } + + protected: + content::test::FencedFrameTestHelper& fenced_frame_test_helper() { + return fenced_frame_helper_; + } + + private: + content::test::FencedFrameTestHelper fenced_frame_helper_; }; IN_PROC_BROWSER_TEST_F(RegisterProtocolHandlerBrowserTest, @@ -177,6 +187,37 @@ ->GetLastCommittedURL()); } +// FencedFrames can not register to handle any protocols. +IN_PROC_BROWSER_TEST_F(RegisterProtocolHandlerBrowserTest, FencedFrame) { + ASSERT_TRUE(embedded_test_server()->Start()); + ASSERT_TRUE(ui_test_utils::NavigateToURL( + browser(), embedded_test_server()->GetURL("/title1.html"))); + + // Create a FencedFrame. + content::RenderFrameHost* fenced_frame_host = + fenced_frame_test_helper().CreateFencedFrame( + browser()->tab_strip_model()->GetActiveWebContents()->GetMainFrame(), + embedded_test_server()->GetURL("/fenced_frames/title1.html")); + ASSERT_TRUE(fenced_frame_host); + + // Ensure the registry is currently empty. + GURL url("web+search:testing"); + ProtocolHandlerRegistry* registry = + ProtocolHandlerRegistryFactory::GetForBrowserContext( + browser()->profile()); + ASSERT_EQ(0u, registry->GetHandlersFor(url.scheme()).size()); + + // Attempt to add an entry. + ProtocolHandlerChangeWaiter waiter(registry); + ASSERT_TRUE(content::ExecuteScript(fenced_frame_host, + "navigator.registerProtocolHandler('web+" + "search', 'test.html?%s', 'test');")); + waiter.Wait(); + + // Ensure the registry is still empty. + ASSERT_EQ(0u, registry->GetHandlersFor(url.scheme()).size()); +} + class RegisterProtocolHandlerSubresourceWebBundlesBrowserTest : public RegisterProtocolHandlerBrowserTest { public:
diff --git a/chrome/browser/devtools/devtools_browsertest.cc b/chrome/browser/devtools/devtools_browsertest.cc index 9acc596..60be90081 100644 --- a/chrome/browser/devtools/devtools_browsertest.cc +++ b/chrome/browser/devtools/devtools_browsertest.cc
@@ -2912,3 +2912,8 @@ EXPECT_TRUE(*result.value.FindBoolKey("arePreferencesSynced")); EXPECT_EQ(*result.value.FindStringKey("accountEmail"), "user@gmail.com"); } + +// Regression test for https://crbug.com/1270184. +IN_PROC_BROWSER_TEST_F(DevToolsTest, NoCrashFor1270184) { + OpenDevToolsWindow("/devtools/regress-crbug-1270184.html", true); +}
diff --git a/chrome/browser/extensions/api/input_ime/input_ime_api.cc b/chrome/browser/extensions/api/input_ime/input_ime_api.cc index 2e00426..eb180c0 100644 --- a/chrome/browser/extensions/api/input_ime/input_ime_api.cc +++ b/chrome/browser/extensions/api/input_ime/input_ime_api.cc
@@ -13,7 +13,6 @@ #include "extensions/browser/extension_registry.h" #include "ui/base/ime/ash/ime_bridge.h" #include "ui/base/ime/ash/ime_keymap.h" -#include "ui/base/ime/constants.h" #include "ui/events/base_event_utils.h" #include "ui/events/keycodes/dom/dom_code.h" #include "ui/events/keycodes/dom/keycode_converter.h" @@ -43,68 +42,6 @@ return engine; } -std::string GetKeyFromEvent(const ui::KeyEvent& event) { - const std::string code = event.GetCodeString(); - if (base::StartsWith(code, "Control", base::CompareCase::SENSITIVE)) - return "Ctrl"; - if (base::StartsWith(code, "Shift", base::CompareCase::SENSITIVE)) - return "Shift"; - if (base::StartsWith(code, "Alt", base::CompareCase::SENSITIVE)) - return "Alt"; - if (base::StartsWith(code, "Arrow", base::CompareCase::SENSITIVE)) - return code.substr(5); - if (code == "Escape") - return "Esc"; - if (code == "Backspace" || code == "Tab" || code == "Enter" || - code == "CapsLock" || code == "Power") - return code; - // Cases for media keys. - switch (event.key_code()) { - case ui::VKEY_BROWSER_BACK: - case ui::VKEY_F1: - return "HistoryBack"; - case ui::VKEY_BROWSER_FORWARD: - case ui::VKEY_F2: - return "HistoryForward"; - case ui::VKEY_BROWSER_REFRESH: - case ui::VKEY_F3: - return "BrowserRefresh"; - case ui::VKEY_ZOOM: - case ui::VKEY_F4: - return "ChromeOSFullscreen"; - case ui::VKEY_MEDIA_LAUNCH_APP1: - case ui::VKEY_F5: - return "ChromeOSSwitchWindow"; - case ui::VKEY_BRIGHTNESS_DOWN: - case ui::VKEY_F6: - return "BrightnessDown"; - case ui::VKEY_BRIGHTNESS_UP: - case ui::VKEY_F7: - return "BrightnessUp"; - case ui::VKEY_VOLUME_MUTE: - case ui::VKEY_F8: - return "AudioVolumeMute"; - case ui::VKEY_VOLUME_DOWN: - case ui::VKEY_F9: - return "AudioVolumeDown"; - case ui::VKEY_VOLUME_UP: - case ui::VKEY_F10: - return "AudioVolumeUp"; - default: - break; - } - uint16_t ch = 0; - // Ctrl+? cases, gets key value for Ctrl is not down. - if (event.flags() & ui::EF_CONTROL_DOWN) { - ui::KeyEvent event_no_ctrl(event.type(), event.key_code(), - event.flags() ^ ui::EF_CONTROL_DOWN); - ch = event_no_ctrl.GetCharacter(); - } else { - ch = event.GetCharacter(); - } - return base::UTF16ToUTF8(std::u16string(1, ch)); -} - ui::KeyEvent ConvertKeyboardEventToUIKeyEvent( const input_ime::KeyboardEvent& event) { const ui::EventType type = @@ -136,260 +73,6 @@ } // namespace -namespace ui { - -ImeObserver::ImeObserver(const std::string& extension_id, Profile* profile) - : extension_id_(extension_id), profile_(profile) {} - -void ImeObserver::OnActivate(const std::string& component_id) { - // Don't check whether the extension listens on onActivate event here. - // Send onActivate event to give the IME a chance to add their listeners. - if (extension_id_.empty()) - return; - - auto args(input_ime::OnActivate::Create( - component_id, input_ime::ParseScreenType(GetCurrentScreenType()))); - - DispatchEventToExtension(extensions::events::INPUT_IME_ON_ACTIVATE, - input_ime::OnActivate::kEventName, - std::move(args)); -} - -void ImeObserver::OnFocus( - const std::string& engine_id, - int context_id, - const IMEEngineHandlerInterface::InputContext& context) { - if (extension_id_.empty() || !HasListener(input_ime::OnFocus::kEventName)) - return; - - input_ime::InputContext context_value; - context_value.context_id = context_id; - context_value.type = - input_ime::ParseInputContextType(ConvertInputContextType(context)); - context_value.auto_correct = ConvertInputContextAutoCorrect(context); - context_value.auto_complete = ConvertInputContextAutoComplete(context); - context_value.auto_capitalize = ConvertInputContextAutoCapitalize(context); - context_value.spell_check = ConvertInputContextSpellCheck(context); - context_value.should_do_learning = context.should_do_learning; - - auto args(input_ime::OnFocus::Create(context_value)); - - DispatchEventToExtension(extensions::events::INPUT_IME_ON_FOCUS, - input_ime::OnFocus::kEventName, std::move(args)); -} - -void ImeObserver::OnBlur(const std::string& engine_id, int context_id) { - if (extension_id_.empty() || !HasListener(input_ime::OnBlur::kEventName)) - return; - - auto args(input_ime::OnBlur::Create(context_id)); - - DispatchEventToExtension(extensions::events::INPUT_IME_ON_BLUR, - input_ime::OnBlur::kEventName, std::move(args)); -} - -void ImeObserver::OnKeyEvent( - const std::string& component_id, - const ui::KeyEvent& event, - IMEEngineHandlerInterface::KeyEventDoneCallback callback) { - if (extension_id_.empty()) - return; - - // If there is no listener for the event, no need to dispatch the event to - // extension. Instead, releases the key event for default system behavior. - if (!ShouldForwardKeyEvent()) { - // Continue processing the key event so that the physical keyboard can - // still work. - std::move(callback).Run(false); - return; - } - - std::string error; - InputMethodEngine* engine = - GetEngineIfActive(profile_, extension_id_, &error); - if (!engine) - return; - const std::string request_id = - engine->AddPendingKeyEvent(component_id, std::move(callback)); - - input_ime::KeyboardEvent keyboard_event; - keyboard_event.type = (event.type() == ui::ET_KEY_RELEASED) - ? input_ime::KEYBOARD_EVENT_TYPE_KEYUP - : input_ime::KEYBOARD_EVENT_TYPE_KEYDOWN; - - // For legacy reasons, we still put a |requestID| into the keyData, even - // though there is already a |requestID| argument in OnKeyEvent. - keyboard_event.request_id = std::make_unique<std::string>(request_id); - - // If the given key event is from VK, it means the key event was simulated. - // Sets the |extension_id| value so that the IME extension can ignore it. - auto* properties = event.properties(); - if (properties && properties->find(ui::kPropertyFromVK) != properties->end()) - keyboard_event.extension_id = std::make_unique<std::string>(extension_id_); - - keyboard_event.key = GetKeyFromEvent(event); - keyboard_event.code = event.code() == ui::DomCode::NONE - ? ui::KeyboardCodeToDomKeycode(event.key_code()) - : event.GetCodeString(); - keyboard_event.alt_key = std::make_unique<bool>(event.IsAltDown()); - keyboard_event.altgr_key = std::make_unique<bool>(event.IsAltGrDown()); - keyboard_event.ctrl_key = std::make_unique<bool>(event.IsControlDown()); - keyboard_event.shift_key = std::make_unique<bool>(event.IsShiftDown()); - keyboard_event.caps_lock = std::make_unique<bool>(event.IsCapsLockOn()); - - auto args( - input_ime::OnKeyEvent::Create(component_id, keyboard_event, request_id)); - - DispatchEventToExtension(extensions::events::INPUT_IME_ON_KEY_EVENT, - input_ime::OnKeyEvent::kEventName, std::move(args)); -} - -void ImeObserver::OnReset(const std::string& component_id) { - if (extension_id_.empty() || !HasListener(input_ime::OnReset::kEventName)) - return; - - auto args(input_ime::OnReset::Create(component_id)); - - DispatchEventToExtension(extensions::events::INPUT_IME_ON_RESET, - input_ime::OnReset::kEventName, std::move(args)); -} - -void ImeObserver::OnDeactivated(const std::string& component_id) { - if (extension_id_.empty() || - !HasListener(input_ime::OnDeactivated::kEventName)) - return; - - auto args(input_ime::OnDeactivated::Create(component_id)); - - DispatchEventToExtension(extensions::events::INPUT_IME_ON_DEACTIVATED, - input_ime::OnDeactivated::kEventName, - std::move(args)); -} - -// TODO(azurewei): This function implementation should be shared on all -// platforms, while with some changing on the current code on ChromeOS. -void ImeObserver::OnCompositionBoundsChanged( - const std::vector<gfx::Rect>& bounds) {} - -void ImeObserver::OnSurroundingTextChanged(const std::string& component_id, - const std::u16string& text, - int cursor_pos, - int anchor_pos, - int offset_pos) { - if (extension_id_.empty() || - !HasListener(input_ime::OnSurroundingTextChanged::kEventName)) - return; - - input_ime::OnSurroundingTextChanged::SurroundingInfo info; - // |info.text| is encoded in UTF8 here so |info.focus| etc may not match the - // index in |info.text|, the javascript code on the extension side should - // handle it. - info.text = base::UTF16ToUTF8(text); - info.focus = cursor_pos; - info.anchor = anchor_pos; - info.offset = offset_pos; - auto args(input_ime::OnSurroundingTextChanged::Create(component_id, info)); - - DispatchEventToExtension( - extensions::events::INPUT_IME_ON_SURROUNDING_TEXT_CHANGED, - input_ime::OnSurroundingTextChanged::kEventName, std::move(args)); -} - -bool ImeObserver::ShouldForwardKeyEvent() const { - // Only forward key events to extension if there are non-lazy listeners - // for onKeyEvent. Because if something wrong with the lazy background - // page which doesn't register listener for onKeyEvent, it will not handle - // the key events, and therefore, all key events will be eaten. - // This is for error-tolerance, and it means that onKeyEvent will never wake - // up lazy background page. - const extensions::EventListenerMap::ListenerList& listeners = - extensions::EventRouter::Get(profile_) - ->listeners() - .GetEventListenersByName(input_ime::OnKeyEvent::kEventName); - for (const std::unique_ptr<extensions::EventListener>& listener : listeners) { - if (listener->extension_id() == extension_id_ && !listener->IsLazy()) - return true; - } - return false; -} - -bool ImeObserver::HasListener(const std::string& event_name) const { - return extensions::EventRouter::Get(profile_)->HasEventListener(event_name); -} - -bool ImeObserver::ExtensionHasListener(const std::string& event_name) const { - return extensions::EventRouter::Get(profile_)->ExtensionHasEventListener( - extension_id_, event_name); -} - -std::string ImeObserver::ConvertInputContextType( - ui::IMEEngineHandlerInterface::InputContext input_context) { - std::string input_context_type = "text"; - switch (input_context.type) { - case ui::TEXT_INPUT_TYPE_SEARCH: - input_context_type = "search"; - break; - case ui::TEXT_INPUT_TYPE_TELEPHONE: - input_context_type = "tel"; - break; - case ui::TEXT_INPUT_TYPE_URL: - input_context_type = "url"; - break; - case ui::TEXT_INPUT_TYPE_EMAIL: - input_context_type = "email"; - break; - case ui::TEXT_INPUT_TYPE_NUMBER: - input_context_type = "number"; - break; - case ui::TEXT_INPUT_TYPE_PASSWORD: - input_context_type = "password"; - break; - case ui::TEXT_INPUT_TYPE_NULL: - input_context_type = "null"; - break; - default: - input_context_type = "text"; - break; - } - return input_context_type; -} - -bool ImeObserver::ConvertInputContextAutoCorrect( - ui::IMEEngineHandlerInterface::InputContext input_context) { - return !(input_context.flags & ui::TEXT_INPUT_FLAG_AUTOCORRECT_OFF); -} - -bool ImeObserver::ConvertInputContextAutoComplete( - ui::IMEEngineHandlerInterface::InputContext input_context) { - return !(input_context.flags & ui::TEXT_INPUT_FLAG_AUTOCOMPLETE_OFF); -} - -input_ime::AutoCapitalizeType ImeObserver::ConvertInputContextAutoCapitalize( - ui::IMEEngineHandlerInterface::InputContext input_context) { - // NOTE: ui::TEXT_INPUT_FLAG_AUTOCAPITALIZE_NONE corresponds to Blink's "none" - // that's a synonym for "off", while input_ime::AUTO_CAPITALIZE_TYPE_NONE - // auto-generated via API specs means "unspecified" and translates to empty - // string. The latter should not be emitted as the API specifies a non-falsy - // enum. So technically there's a bug here; either this impl or the API needs - // fixing. However, as a public API, the behaviour is left intact for now. - if (input_context.flags & ui::TEXT_INPUT_FLAG_AUTOCAPITALIZE_NONE) - return input_ime::AUTO_CAPITALIZE_TYPE_NONE; - - if (input_context.flags & ui::TEXT_INPUT_FLAG_AUTOCAPITALIZE_CHARACTERS) - return input_ime::AUTO_CAPITALIZE_TYPE_CHARACTERS; - if (input_context.flags & ui::TEXT_INPUT_FLAG_AUTOCAPITALIZE_WORDS) - return input_ime::AUTO_CAPITALIZE_TYPE_WORDS; - // The default value is "sentences". - return input_ime::AUTO_CAPITALIZE_TYPE_SENTENCES; -} - -bool ImeObserver::ConvertInputContextSpellCheck( - ui::IMEEngineHandlerInterface::InputContext input_context) { - return !(input_context.flags & ui::TEXT_INPUT_FLAG_SPELLCHECK_OFF); -} - -} // namespace ui - namespace extensions { InputImeEventRouterFactory* InputImeEventRouterFactory::GetInstance() {
diff --git a/chrome/browser/extensions/api/input_ime/input_ime_api.h b/chrome/browser/extensions/api/input_ime/input_ime_api.h index 3411048c..139039b8 100644 --- a/chrome/browser/extensions/api/input_ime/input_ime_api.h +++ b/chrome/browser/extensions/api/input_ime/input_ime_api.h
@@ -36,82 +36,6 @@ class Profile; -namespace ui { -class IMEEngineHandlerInterface; - -class ImeObserver : public ash::input_method::InputMethodEngineBase::Observer { - public: - ImeObserver(const std::string& extension_id, Profile* profile); - - ImeObserver(const ImeObserver&) = delete; - ImeObserver& operator=(const ImeObserver&) = delete; - - ~ImeObserver() override = default; - - // InputMethodEngineBase::Observer overrides. - void OnActivate(const std::string& component_id) override; - void OnFocus(const std::string& engine_id, - int context_id, - const IMEEngineHandlerInterface::InputContext& context) override; - void OnTouch(ui::EventPointerType pointerType) override {} - void OnBlur(const std::string& engine_id, int context_id) override; - void OnKeyEvent( - const std::string& component_id, - const ui::KeyEvent& event, - IMEEngineHandlerInterface::KeyEventDoneCallback key_data) override; - void OnReset(const std::string& component_id) override; - void OnDeactivated(const std::string& component_id) override; - void OnCompositionBoundsChanged( - const std::vector<gfx::Rect>& bounds) override; - void OnSurroundingTextChanged(const std::string& component_id, - const std::u16string& text, - int cursor_pos, - int anchor_pos, - int offset_pos) override; - - protected: - // Helper function used to forward the given event to the |profile_|'s event - // router, which dipatches the event the extension with |extension_id_|. - virtual void DispatchEventToExtension( - extensions::events::HistogramValue histogram_value, - const std::string& event_name, - std::vector<base::Value> args) = 0; - - // Returns the type of the current screen. - virtual std::string GetCurrentScreenType() = 0; - - // Returns true if the extension is ready to accept key event, otherwise - // returns false. - bool ShouldForwardKeyEvent() const; - - // Returns true if there are any listeners on the given event. - // TODO(https://crbug.com/835699): Merge this with |ExtensionHasListener|. - bool HasListener(const std::string& event_name) const; - - // Returns true if the extension has any listeners on the given event. - bool ExtensionHasListener(const std::string& event_name) const; - - // Functions used to convert InputContext struct to string - std::string ConvertInputContextType( - IMEEngineHandlerInterface::InputContext input_context); - virtual bool ConvertInputContextAutoCorrect( - IMEEngineHandlerInterface::InputContext input_context); - virtual bool ConvertInputContextAutoComplete( - IMEEngineHandlerInterface::InputContext input_context); - virtual bool ConvertInputContextSpellCheck( - IMEEngineHandlerInterface::InputContext input_context); - - std::string extension_id_; - raw_ptr<Profile> profile_; - - private: - extensions::api::input_ime::AutoCapitalizeType - ConvertInputContextAutoCapitalize( - IMEEngineHandlerInterface::InputContext input_context); -}; - -} // namespace ui - namespace extensions { class InputImeEventRouter; class ExtensionRegistry;
diff --git a/chrome/browser/extensions/api/input_ime/input_ime_api_chromeos.cc b/chrome/browser/extensions/api/input_ime/input_ime_api_chromeos.cc index 81de37a..852469f 100644 --- a/chrome/browser/extensions/api/input_ime/input_ime_api_chromeos.cc +++ b/chrome/browser/extensions/api/input_ime/input_ime_api_chromeos.cc
@@ -29,7 +29,9 @@ #include "ui/base/ime/ash/component_extension_ime_manager.h" #include "ui/base/ime/ash/extension_ime_util.h" #include "ui/base/ime/ash/ime_engine_handler_interface.h" +#include "ui/base/ime/ash/ime_keymap.h" #include "ui/base/ime/ash/input_method_manager.h" +#include "ui/base/ime/constants.h" #include "ui/base/ui_base_features.h" namespace { @@ -145,23 +147,97 @@ case ui::ime::AssistiveWindowType::kEmojiSuggestion: case ui::ime::AssistiveWindowType::kPersonalInfoSuggestion: case ui::ime::AssistiveWindowType::kGrammarSuggestion: + case ui::ime::AssistiveWindowType::kMultiWordSuggestion: return input_ime::AssistiveWindowType::ASSISTIVE_WINDOW_TYPE_NONE; case ui::ime::AssistiveWindowType::kUndoWindow: return input_ime::AssistiveWindowType::ASSISTIVE_WINDOW_TYPE_UNDO; } } -class ImeObserverChromeOS : public ui::ImeObserver { +std::string GetKeyFromEvent(const ui::KeyEvent& event) { + const std::string code = event.GetCodeString(); + if (base::StartsWith(code, "Control", base::CompareCase::SENSITIVE)) + return "Ctrl"; + if (base::StartsWith(code, "Shift", base::CompareCase::SENSITIVE)) + return "Shift"; + if (base::StartsWith(code, "Alt", base::CompareCase::SENSITIVE)) + return "Alt"; + if (base::StartsWith(code, "Arrow", base::CompareCase::SENSITIVE)) + return code.substr(5); + if (code == "Escape") + return "Esc"; + if (code == "Backspace" || code == "Tab" || code == "Enter" || + code == "CapsLock" || code == "Power") + return code; + // Cases for media keys. + switch (event.key_code()) { + case ui::VKEY_BROWSER_BACK: + case ui::VKEY_F1: + return "HistoryBack"; + case ui::VKEY_BROWSER_FORWARD: + case ui::VKEY_F2: + return "HistoryForward"; + case ui::VKEY_BROWSER_REFRESH: + case ui::VKEY_F3: + return "BrowserRefresh"; + case ui::VKEY_ZOOM: + case ui::VKEY_F4: + return "ChromeOSFullscreen"; + case ui::VKEY_MEDIA_LAUNCH_APP1: + case ui::VKEY_F5: + return "ChromeOSSwitchWindow"; + case ui::VKEY_BRIGHTNESS_DOWN: + case ui::VKEY_F6: + return "BrightnessDown"; + case ui::VKEY_BRIGHTNESS_UP: + case ui::VKEY_F7: + return "BrightnessUp"; + case ui::VKEY_VOLUME_MUTE: + case ui::VKEY_F8: + return "AudioVolumeMute"; + case ui::VKEY_VOLUME_DOWN: + case ui::VKEY_F9: + return "AudioVolumeDown"; + case ui::VKEY_VOLUME_UP: + case ui::VKEY_F10: + return "AudioVolumeUp"; + default: + break; + } + uint16_t ch = 0; + // Ctrl+? cases, gets key value for Ctrl is not down. + if (event.flags() & ui::EF_CONTROL_DOWN) { + ui::KeyEvent event_no_ctrl(event.type(), event.key_code(), + event.flags() ^ ui::EF_CONTROL_DOWN); + ch = event_no_ctrl.GetCharacter(); + } else { + ch = event.GetCharacter(); + } + return base::UTF16ToUTF8(std::u16string(1, ch)); +} + +InputMethodEngine* GetEngineIfActive(Profile* profile, + const std::string& extension_id, + std::string* error) { + extensions::InputImeEventRouter* event_router = + extensions::GetInputImeEventRouter(profile); + DCHECK(event_router) << kErrorRouterNotAvailable; + InputMethodEngine* engine = static_cast<InputMethodEngine*>( + event_router->GetEngineIfActive(extension_id, error)); + return engine; +} + +class ImeObserverChromeOS + : public ash::input_method::InputMethodEngineBase::Observer { public: ImeObserverChromeOS(const std::string& extension_id, Profile* profile) - : ImeObserver(extension_id, profile) {} + : extension_id_(extension_id), profile_(profile) {} ImeObserverChromeOS(const ImeObserverChromeOS&) = delete; ImeObserverChromeOS& operator=(const ImeObserverChromeOS&) = delete; ~ImeObserverChromeOS() override = default; - // ash::InputMethodEngineBase::Observer overrides. void OnCandidateClicked( const std::string& component_id, int candidate_id, @@ -222,6 +298,111 @@ OnScreenProjectionChanged::kEventName, std::move(args)); } + void OnActivate(const std::string& component_id) override { + // Don't check whether the extension listens on onActivate event here. + // Send onActivate event to give the IME a chance to add their listeners. + if (extension_id_.empty()) + return; + + auto args(input_ime::OnActivate::Create( + component_id, input_ime::ParseScreenType(GetCurrentScreenType()))); + + DispatchEventToExtension(extensions::events::INPUT_IME_ON_ACTIVATE, + input_ime::OnActivate::kEventName, + std::move(args)); + } + + void OnBlur(const std::string& engine_id, int context_id) override { + if (extension_id_.empty() || !HasListener(input_ime::OnBlur::kEventName)) + return; + + auto args(input_ime::OnBlur::Create(context_id)); + + DispatchEventToExtension(extensions::events::INPUT_IME_ON_BLUR, + input_ime::OnBlur::kEventName, std::move(args)); + } + + void OnKeyEvent( + const std::string& component_id, + const ui::KeyEvent& event, + IMEEngineHandlerInterface::KeyEventDoneCallback callback) override { + if (extension_id_.empty()) + return; + + // If there is no listener for the event, no need to dispatch the event to + // extension. Instead, releases the key event for default system behavior. + if (!ShouldForwardKeyEvent()) { + // Continue processing the key event so that the physical keyboard can + // still work. + std::move(callback).Run(false); + return; + } + + std::string error; + InputMethodEngine* engine = + GetEngineIfActive(profile_, extension_id_, &error); + if (!engine) + return; + const std::string request_id = + engine->AddPendingKeyEvent(component_id, std::move(callback)); + + input_ime::KeyboardEvent keyboard_event; + keyboard_event.type = (event.type() == ui::ET_KEY_RELEASED) + ? input_ime::KEYBOARD_EVENT_TYPE_KEYUP + : input_ime::KEYBOARD_EVENT_TYPE_KEYDOWN; + + // For legacy reasons, we still put a |requestID| into the keyData, even + // though there is already a |requestID| argument in OnKeyEvent. + keyboard_event.request_id = std::make_unique<std::string>(request_id); + + // If the given key event is from VK, it means the key event was simulated. + // Sets the |extension_id| value so that the IME extension can ignore it. + auto* properties = event.properties(); + if (properties && + properties->find(ui::kPropertyFromVK) != properties->end()) + keyboard_event.extension_id = + std::make_unique<std::string>(extension_id_); + + keyboard_event.key = GetKeyFromEvent(event); + keyboard_event.code = event.code() == ui::DomCode::NONE + ? ui::KeyboardCodeToDomKeycode(event.key_code()) + : event.GetCodeString(); + keyboard_event.alt_key = std::make_unique<bool>(event.IsAltDown()); + keyboard_event.altgr_key = std::make_unique<bool>(event.IsAltGrDown()); + keyboard_event.ctrl_key = std::make_unique<bool>(event.IsControlDown()); + keyboard_event.shift_key = std::make_unique<bool>(event.IsShiftDown()); + keyboard_event.caps_lock = std::make_unique<bool>(event.IsCapsLockOn()); + + auto args(input_ime::OnKeyEvent::Create(component_id, keyboard_event, + request_id)); + + DispatchEventToExtension(extensions::events::INPUT_IME_ON_KEY_EVENT, + input_ime::OnKeyEvent::kEventName, + std::move(args)); + } + + void OnReset(const std::string& component_id) override { + if (extension_id_.empty() || !HasListener(input_ime::OnReset::kEventName)) + return; + + auto args(input_ime::OnReset::Create(component_id)); + + DispatchEventToExtension(extensions::events::INPUT_IME_ON_RESET, + input_ime::OnReset::kEventName, std::move(args)); + } + + void OnDeactivated(const std::string& component_id) override { + if (extension_id_.empty() || + !HasListener(input_ime::OnDeactivated::kEventName)) + return; + + auto args(input_ime::OnDeactivated::Create(component_id)); + + DispatchEventToExtension(extensions::events::INPUT_IME_ON_DEACTIVATED, + input_ime::OnDeactivated::kEventName, + std::move(args)); + } + void OnCompositionBoundsChanged( const std::vector<gfx::Rect>& bounds) override { if (bounds.empty() || extension_id_.empty() || @@ -273,7 +454,7 @@ input_context.auto_correct = ConvertInputContextAutoCorrect(context); input_context.auto_complete = ConvertInputContextAutoComplete(context); input_context.auto_capitalize = - ConvertInputContextAutoCapitalize(context.flags); + ConvertInputContextAutoCapitalizePrivate(context.flags); input_context.spell_check = ConvertInputContextSpellCheck(context); input_context.has_been_password = ConvertHasBeenPassword(context); input_context.should_do_learning = context.should_do_learning; @@ -294,7 +475,48 @@ return; } - ImeObserver::OnFocus(engine_id, context_id, context); + if (extension_id_.empty() || !HasListener(input_ime::OnFocus::kEventName)) + return; + + input_ime::InputContext context_value; + context_value.context_id = context_id; + context_value.type = + input_ime::ParseInputContextType(ConvertInputContextType(context)); + context_value.auto_correct = ConvertInputContextAutoCorrect(context); + context_value.auto_complete = ConvertInputContextAutoComplete(context); + context_value.auto_capitalize = + ConvertInputContextAutoCapitalizePublic(context); + context_value.spell_check = ConvertInputContextSpellCheck(context); + context_value.should_do_learning = context.should_do_learning; + + auto args(input_ime::OnFocus::Create(context_value)); + + DispatchEventToExtension(extensions::events::INPUT_IME_ON_FOCUS, + input_ime::OnFocus::kEventName, std::move(args)); + } + + void OnSurroundingTextChanged(const std::string& component_id, + const std::u16string& text, + int cursor_pos, + int anchor_pos, + int offset_pos) override { + if (extension_id_.empty() || + !HasListener(input_ime::OnSurroundingTextChanged::kEventName)) + return; + + input_ime::OnSurroundingTextChanged::SurroundingInfo info; + // |info.text| is encoded in UTF8 here so |info.focus| etc may not match the + // index in |info.text|, the javascript code on the extension side should + // handle it. + info.text = base::UTF16ToUTF8(text); + info.focus = cursor_pos; + info.anchor = anchor_pos; + info.offset = offset_pos; + auto args(input_ime::OnSurroundingTextChanged::Create(component_id, info)); + + DispatchEventToExtension( + extensions::events::INPUT_IME_ON_SURROUNDING_TEXT_CHANGED, + input_ime::OnSurroundingTextChanged::kEventName, std::move(args)); } void OnTouch(ui::EventPointerType pointerType) override { if (extension_id_.empty() || @@ -360,11 +582,12 @@ } private: - // ui::ImeObserver overrides. + // Helper function used to forward the given event to the |profile_|'s event + // router, which dipatches the event the extension with |extension_id_|. void DispatchEventToExtension( extensions::events::HistogramValue histogram_value, const std::string& event_name, - std::vector<base::Value> args) override { + std::vector<base::Value> args) { if (event_name == input_ime::OnActivate::kEventName) { // Send onActivate event regardless of it's listened by the IME. auto event = std::make_unique<extensions::Event>( @@ -404,7 +627,7 @@ // The component IME extensions need to know the current screen type (e.g. // lock screen, login screen, etc.) so that its on-screen keyboard page // won't open new windows/pages. See crbug.com/395621. - std::string GetCurrentScreenType() override { + std::string GetCurrentScreenType() { switch (ash::input_method::InputMethodManager::Get() ->GetActiveIMEState() ->GetUIStyle()) { @@ -419,6 +642,39 @@ } } + // Returns true if the extension is ready to accept key event, otherwise + // returns false. + bool ShouldForwardKeyEvent() const { + // Only forward key events to extension if there are non-lazy listeners + // for onKeyEvent. Because if something wrong with the lazy background + // page which doesn't register listener for onKeyEvent, it will not handle + // the key events, and therefore, all key events will be eaten. + // This is for error-tolerance, and it means that onKeyEvent will never wake + // up lazy background page. + const extensions::EventListenerMap::ListenerList& listeners = + extensions::EventRouter::Get(profile_) + ->listeners() + .GetEventListenersByName(input_ime::OnKeyEvent::kEventName); + for (const std::unique_ptr<extensions::EventListener>& listener : + listeners) { + if (listener->extension_id() == extension_id_ && !listener->IsLazy()) + return true; + } + return false; + } + + // Returns true if there are any listeners on the given event. + // TODO(https://crbug.com/835699): Merge this with |ExtensionHasListener|. + bool HasListener(const std::string& event_name) const { + return extensions::EventRouter::Get(profile_)->HasEventListener(event_name); + } + + // Returns true if the extension has any listeners on the given event. + bool ExtensionHasListener(const std::string& event_name) const { + return extensions::EventRouter::Get(profile_)->ExtensionHasEventListener( + extension_id_, event_name); + } + std::string ConvertInputContextFocusReason( ui::IMEEngineHandlerInterface::InputContext input_context) { switch (input_context.focus_reason) { @@ -436,21 +692,21 @@ } bool ConvertInputContextAutoCorrect( - ui::IMEEngineHandlerInterface::InputContext input_context) override { + ui::IMEEngineHandlerInterface::InputContext input_context) { if (!GetKeyboardConfig().auto_correct) return false; - return ImeObserver::ConvertInputContextAutoCorrect(input_context); + return !(input_context.flags & ui::TEXT_INPUT_FLAG_AUTOCORRECT_OFF); } bool ConvertInputContextAutoComplete( - ui::IMEEngineHandlerInterface::InputContext input_context) override { + ui::IMEEngineHandlerInterface::InputContext input_context) { if (!GetKeyboardConfig().auto_complete) return false; - return ImeObserver::ConvertInputContextAutoComplete(input_context); + return !(input_context.flags & ui::TEXT_INPUT_FLAG_AUTOCOMPLETE_OFF); } - input_method_private::AutoCapitalizeType ConvertInputContextAutoCapitalize( - int flags) { + input_method_private::AutoCapitalizeType + ConvertInputContextAutoCapitalizePrivate(int flags) { if (!GetKeyboardConfig().auto_capitalize) return input_method_private::AUTO_CAPITALIZE_TYPE_OFF; if (flags & ui::TEXT_INPUT_FLAG_AUTOCAPITALIZE_NONE) @@ -473,10 +729,10 @@ } bool ConvertInputContextSpellCheck( - ui::IMEEngineHandlerInterface::InputContext input_context) override { + ui::IMEEngineHandlerInterface::InputContext input_context) { if (!GetKeyboardConfig().spell_check) return false; - return ImeObserver::ConvertInputContextSpellCheck(input_context); + return !(input_context.flags & ui::TEXT_INPUT_FLAG_SPELLCHECK_OFF); } bool ConvertHasBeenPassword( @@ -518,22 +774,67 @@ } return input_mode_type; } + + std::string ConvertInputContextType( + ui::IMEEngineHandlerInterface::InputContext input_context) { + std::string input_context_type = "text"; + switch (input_context.type) { + case ui::TEXT_INPUT_TYPE_SEARCH: + input_context_type = "search"; + break; + case ui::TEXT_INPUT_TYPE_TELEPHONE: + input_context_type = "tel"; + break; + case ui::TEXT_INPUT_TYPE_URL: + input_context_type = "url"; + break; + case ui::TEXT_INPUT_TYPE_EMAIL: + input_context_type = "email"; + break; + case ui::TEXT_INPUT_TYPE_NUMBER: + input_context_type = "number"; + break; + case ui::TEXT_INPUT_TYPE_PASSWORD: + input_context_type = "password"; + break; + case ui::TEXT_INPUT_TYPE_NULL: + input_context_type = "null"; + break; + default: + input_context_type = "text"; + break; + } + return input_context_type; + } + + input_ime::AutoCapitalizeType ConvertInputContextAutoCapitalizePublic( + ui::IMEEngineHandlerInterface::InputContext input_context) { + // NOTE: ui::TEXT_INPUT_FLAG_AUTOCAPITALIZE_NONE corresponds to Blink's + // "none" that's a synonym for "off", while + // input_ime::AUTO_CAPITALIZE_TYPE_NONE auto-generated via API specs means + // "unspecified" and translates to empty string. The latter should not be + // emitted as the API specifies a non-falsy enum. So technically there's a + // bug here; either this impl or the API needs fixing. However, as a public + // API, the behaviour is left intact for now. + if (input_context.flags & ui::TEXT_INPUT_FLAG_AUTOCAPITALIZE_NONE) + return input_ime::AUTO_CAPITALIZE_TYPE_NONE; + + if (input_context.flags & ui::TEXT_INPUT_FLAG_AUTOCAPITALIZE_CHARACTERS) + return input_ime::AUTO_CAPITALIZE_TYPE_CHARACTERS; + if (input_context.flags & ui::TEXT_INPUT_FLAG_AUTOCAPITALIZE_WORDS) + return input_ime::AUTO_CAPITALIZE_TYPE_WORDS; + // The default value is "sentences". + return input_ime::AUTO_CAPITALIZE_TYPE_SENTENCES; + } + + std::string extension_id_; + raw_ptr<Profile> profile_; }; } // namespace namespace extensions { -InputMethodEngine* GetEngineIfActive(Profile* profile, - const std::string& extension_id, - std::string* error) { - InputImeEventRouter* event_router = GetInputImeEventRouter(profile); - DCHECK(event_router) << kErrorRouterNotAvailable; - InputMethodEngine* engine = static_cast<InputMethodEngine*>( - event_router->GetEngineIfActive(extension_id, error)); - return engine; -} - InputMethodEngine* GetEngine(content::BrowserContext* browser_context, const std::string& extension_id, std::string* error) {
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json index f593460..259d503 100644 --- a/chrome/browser/flag-metadata.json +++ b/chrome/browser/flag-metadata.json
@@ -353,6 +353,11 @@ "expiry_milestone": 98 }, { + "name": "auto-framing-override", + "owners": [ "kamesan", "chromeos-camera-eng@google.com" ], + "expiry_milestone": 100 + }, + { "name": "auto-screen-brightness", "owners": [ "jiameng", "napper" ], "expiry_milestone": 90 @@ -619,7 +624,7 @@ }, { "name": "camera-app-document-manual-crop", - "owners": [ "inker", "chromeos-canera-eng@google.com" ], + "owners": [ "inker", "chromeos-camera-eng@google.com" ], "expiry_milestone": 99 }, { @@ -3384,7 +3389,7 @@ }, { "name": "hdrnet-override", - "owners": [ "jcliang", "chromeos-canera-eng@google.com" ], + "owners": [ "jcliang", "chromeos-camera-eng@google.com" ], "expiry_milestone": 100 }, {
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc index decda94..3711054e 100644 --- a/chrome/browser/flag_descriptions.cc +++ b/chrome/browser/flag_descriptions.cc
@@ -2690,7 +2690,8 @@ "Use the new permissions backend for Web Bluetooth"; const char kWebBluetoothNewPermissionsBackendDescription[] = "Enables the new permissions backend for Web Bluetooth. This will enable " - "persistent storage of device permissions."; + "persistent storage of device permissions and Web Bluetooth features such " + "as BluetoothDevice.watchAdvertisements() and Bluetooth.getDevices()"; const char kWebBundlesName[] = "Web Bundles"; const char kWebBundlesDescription[] = @@ -4141,6 +4142,11 @@ "Enable chrome://audio that is designed for debugging ChromeOS audio " "issues"; +const char kAutoFramingOverrideName[] = "Auto-framing control override"; +const char kAutoFramingOverrideDescription[] = + "Overrides the default to forcibly enable or disable the auto-framing " + "feature"; + const char kBluetoothFixA2dpPacketSizeName[] = "Bluetooth fix A2DP packet size"; const char kBluetoothFixA2dpPacketSizeDescription[] = "Fixes Bluetooth A2DP packet size to a smaller default value to improve "
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h index 84eb235..b95df736 100644 --- a/chrome/browser/flag_descriptions.h +++ b/chrome/browser/flag_descriptions.h
@@ -2386,6 +2386,9 @@ extern const char kAudioUrlName[]; extern const char kAudioUrlDescription[]; +extern const char kAutoFramingOverrideName[]; +extern const char kAutoFramingOverrideDescription[]; + extern const char kBluetoothFixA2dpPacketSizeName[]; extern const char kBluetoothFixA2dpPacketSizeDescription[];
diff --git a/chrome/browser/lacros/lacros_extension_apps_controller.cc b/chrome/browser/lacros/lacros_extension_apps_controller.cc index 8cca3d60..600cc5e 100644 --- a/chrome/browser/lacros/lacros_extension_apps_controller.cc +++ b/chrome/browser/lacros/lacros_extension_apps_controller.cc
@@ -182,22 +182,9 @@ return; } - apps::mojom::IntentPtr intent = apps::mojom::Intent::New(); - if (launch_params->intent) { - intent = apps_util::ConvertCrosapiToAppServiceIntent(launch_params->intent, - profile); - } + auto params = apps::ConvertCrosapiToLaunchParams(launch_params, profile); + params.app_id = extension->id(); - extensions::LaunchContainer launch_container = extensions::GetLaunchContainer( - extensions::ExtensionPrefs::Get(profile), extension); - auto params = apps::CreateAppLaunchParamsForIntent( - extension->id(), ui::EF_NONE, launch_params->launch_source, - display::kInvalidDisplayId, launch_container, std::move(intent), profile); - if (launch_params->intent && launch_params->intent->files.has_value()) { - for (const auto& file : launch_params->intent->files.value()) { - params.launch_files.push_back(file->file_path); - } - } OpenApplication(profile, std::move(params)); // TODO(https://crbug.com/1225848): Store the resulting instance token, which
diff --git a/chrome/browser/media/media_engagement_score_unittest.cc b/chrome/browser/media/media_engagement_score_unittest.cc index 2bcaaf3f..077f6223 100644 --- a/chrome/browser/media/media_engagement_score_unittest.cc +++ b/chrome/browser/media/media_engagement_score_unittest.cc
@@ -442,7 +442,14 @@ EXPECT_TRUE(score_->high_score()); } -TEST_F(MediaEngagementScoreWithOverrideFieldTrialsTest, OverrideFieldTrial) { +// TODO(crbug.com/1276910): Consistently failing on Linux TSan Tests. +#if defined(THREAD_SANITIZER) +#define MAYBE_OverrideFieldTrial DISABLED_OverrideFieldTrial +#else +#define MAYBE_OverrideFieldTrial OverrideFieldTrial +#endif +TEST_F(MediaEngagementScoreWithOverrideFieldTrialsTest, + MAYBE_OverrideFieldTrial) { EXPECT_TRUE(score_->high_score()); EXPECT_EQ(0.7, MediaEngagementScore::GetHighScoreLowerThreshold()); EXPECT_EQ(0.9, MediaEngagementScore::GetHighScoreUpperThreshold());
diff --git a/chrome/browser/password_manager/password_manager_browsertest.cc b/chrome/browser/password_manager/password_manager_browsertest.cc index 19de1be..faff392 100644 --- a/chrome/browser/password_manager/password_manager_browsertest.cc +++ b/chrome/browser/password_manager/password_manager_browsertest.cc
@@ -97,7 +97,7 @@ #include "chrome/browser/signin/identity_manager_factory.h" #include "components/signin/public/identity_manager/identity_manager.h" #include "components/signin/public/identity_manager/primary_account_mutator.h" -#endif // ENABLE_DICE_SUPPORT +#endif // BUIDLFLAG(ENABLE_DICE_SUPPORT) using autofill::ParsingResult; using base::ASCIIToUTF16; @@ -4052,7 +4052,7 @@ EXPECT_TRUE(prompt_observer.IsSavePromptAvailable()); } -#if BUILDFLAG(ENABLE_DICE_SUPPORT) && !BUILDFLAG(IS_CHROMEOS_LACROS) +#if BUILDFLAG(ENABLE_DICE_SUPPORT) // This test suite only applies to Gaia signin page, and checks that the // signin interception bubble and the password bubbles never conflict. class PasswordManagerBrowserTestWithSigninInterception @@ -4225,7 +4225,7 @@ FillAndSubmitGaiaPassword(); EXPECT_FALSE(prompt_observer.IsSavePromptShownAutomatically()); } -#endif // ENABLE_DICE_SUPPORT && !BUILDFLAG(IS_CHROMEOS_LACROS) +#endif // BUIDLFLAG(ENABLE_DICE_SUPPORT) class TestPasswordManagerClient : public ChromePasswordManagerClient { public:
diff --git a/chrome/browser/password_manager/password_manager_interactive_uitest.cc b/chrome/browser/password_manager/password_manager_interactive_uitest.cc index 1fcd15c..d531cc13 100644 --- a/chrome/browser/password_manager/password_manager_interactive_uitest.cc +++ b/chrome/browser/password_manager/password_manager_interactive_uitest.cc
@@ -29,14 +29,14 @@ #include "content/public/test/browser_test_utils.h" #include "third_party/blink/public/common/switches.h" -#if BUILDFLAG(ENABLE_DICE_SUPPORT) && !BUILDFLAG(IS_CHROMEOS_LACROS) +#if BUILDFLAG(ENABLE_DICE_SUPPORT) #include "chrome/browser/password_manager/password_manager_signin_intercept_test_helper.h" #include "chrome/browser/signin/dice_web_signin_interceptor.h" -#endif // ENABLE_DICE_SUPPORT +#endif // BUILDFLAG(ENABLE_DICE_SUPPORT) namespace { -#if BUILDFLAG(ENABLE_DICE_SUPPORT) && !BUILDFLAG(IS_CHROMEOS_LACROS) +#if BUILDFLAG(ENABLE_DICE_SUPPORT) // Wait until |condition| returns true. void WaitForCondition(base::RepeatingCallback<bool()> condition) { while (!condition.Run()) { @@ -46,7 +46,7 @@ run_loop.Run(); } } -#endif // ENABLE_DICE_SUPPORT +#endif // BUILDFLAG(ENABLE_DICE_SUPPORT) } // namespace @@ -494,9 +494,7 @@ } } -// TODO(crbug.com/1198490): Remove explicit !BUILDFLAG(IS_CHROMEOS_LACROS) when -// DICE is not enabled on Lacros. -#if BUILDFLAG(ENABLE_DICE_SUPPORT) && !BUILDFLAG(IS_CHROMEOS_LACROS) +#if BUILDFLAG(ENABLE_DICE_SUPPORT) // This test suite only applies to Gaia signin page, and checks that the // signin interception bubble and the password bubbles never conflict. class PasswordManagerInteractiveTestWithSigninInterception @@ -580,6 +578,6 @@ navigation_observer.Wait(); EXPECT_TRUE(prompt_observer.IsUpdatePromptShownAutomatically()); } -#endif // ENABLE_DICE_SUPPORT +#endif // BUILDFLAG(ENABLE_DICE_SUPPORT) } // namespace password_manager
diff --git a/chrome/browser/policy/extension_policy_browsertest.cc b/chrome/browser/policy/extension_policy_browsertest.cc index 4b1ec22..27218f42 100644 --- a/chrome/browser/policy/extension_policy_browsertest.cc +++ b/chrome/browser/policy/extension_policy_browsertest.cc
@@ -8,6 +8,7 @@ #include "base/files/file_util.h" #include "base/files/scoped_temp_dir.h" #include "base/memory/raw_ptr.h" +#include "base/path_service.h" #include "base/run_loop.h" #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" @@ -684,6 +685,51 @@ raw_ptr<const content::BrowserContext> context_; }; +std::string GetUpdateManifestBody(const std::string& id, + const std::string& crx_name, + const std::string& version) { + // "example.com" is a placeholder that gets substituted with the test + // server address at runtime. + std::string crx_path = "http://example.com/" + crx_name; + return "<?xml version='1.0' encoding='UTF-8'?>" + "<gupdate xmlns='http://www.google.com/update2/response' " + "protocol='2.0'>" + " <app appid='" + + id + + "'>" + " <updatecheck status='ok' codebase='" + + crx_path + "' version='" + version + + "' />" + " </app>" + "</gupdate>"; +} + +std::string GetUpdateManifestHeader() { + return "HTTP/1.1 200 OK\nContent-Type: application/json; " + "charset=utf-8\n"; +} + +bool WriteManifestResponse(content::URLLoaderInterceptor::RequestParams* params, + const std::string& id, + const std::string& update_manifest_name, + const std::string& crx_name, + const std::string& version, + const base::FilePath& install_crx_path) { + if (params->url_request.url.path() == update_manifest_name) { + content::URLLoaderInterceptor::WriteResponse( + GetUpdateManifestHeader(), GetUpdateManifestBody(id, crx_name, version), + params->client.get()); + return true; + } + + if (params->url_request.url.path() == "/" + crx_name) { + content::URLLoaderInterceptor::WriteResponse(install_crx_path, + params->client.get()); + return true; + } + return false; +} + } // namespace // Verifies that if extension is installed manually by user and then added to @@ -1177,30 +1223,6 @@ BrowserPolicyConnector::SetPolicyProviderForTesting(&provider_); } - std::string GetUpdateManifestBody(const std::string& id, - const std::string& crx_name, - const std::string& version) { - // "example.com" is a placeholder that gets substituted with the test - // server address at runtime. - std::string crx_path = "http://example.com/" + crx_name; - return "<?xml version='1.0' encoding='UTF-8'?>" - "<gupdate xmlns='http://www.google.com/update2/response' " - "protocol='2.0'>" - " <app appid='" + - id + - "'>" - " <updatecheck status='ok' codebase='" + - crx_path + "' version='" + version + - "' />" - " </app>" - "</gupdate>"; - } - - std::string GetUpdateManifestHeader() { - return "HTTP/1.1 200 OK\nContent-Type: application/json; " - "charset=utf-8\n"; - } - base::FilePath PackLocalExtension(const std::string& relative_dir_path, const std::string& relative_pem_path, const base::FilePath& crx_path) { @@ -1237,24 +1259,11 @@ // unexpected request. ExtensionRequestInterceptor interceptor; ASSERT_TRUE(embedded_test_server()->Start()); - // TODO(b/172205862): Find a way to move these requests out of the test. interceptor.set_interceptor_hook(base::BindLambdaForTesting( [&](content::URLLoaderInterceptor::RequestParams* params) { - if (params->url_request.url.path() == "/update_manifest_v1.xml") { - content::URLLoaderInterceptor::WriteResponse( - GetUpdateManifestHeader(), - GetUpdateManifestBody(kPinnedExtensionCrxId, "v1.crx", - /*version=*/"1"), - params->client.get()); - return true; - } - - if (params->url_request.url.path() == "/v1.crx") { - content::URLLoaderInterceptor::WriteResponse(install_crx_path, - params->client.get()); - return true; - } - return false; + return WriteManifestResponse(params, kPinnedExtensionCrxId, + "/update_manifest_v1.xml", "v1.crx", + /*version=*/"1", install_crx_path); })); extensions::TestExtensionRegistryObserver observer(extension_registry(), @@ -1275,21 +1284,9 @@ // extension. interceptor.set_interceptor_hook(base::BindLambdaForTesting( [&](content::URLLoaderInterceptor::RequestParams* params) { - if (params->url_request.url.path() == "/update_manifest_v2.xml") { - content::URLLoaderInterceptor::WriteResponse( - GetUpdateManifestHeader(), - GetUpdateManifestBody(kPinnedExtensionCrxId, "v2.crx", - /*version=*/"2"), - params->client.get()); - return true; - } - - if (params->url_request.url.path() == "/v2.crx") { - content::URLLoaderInterceptor::WriteResponse(updated_crx_path, - params->client.get()); - return true; - } - return false; + return WriteManifestResponse(params, kPinnedExtensionCrxId, + "/update_manifest_v2.xml", "v2.crx", + /*version=*/"2", updated_crx_path); })); // Change |update_url| to point to version 2 of the extension. SetExtensionSettingsPolicy("/update_manifest_v2.xml", kPinnedExtensionCrxId); @@ -1318,24 +1315,11 @@ // unexpected request. ExtensionRequestInterceptor interceptor; ASSERT_TRUE(embedded_test_server()->Start()); - // TODO(b/172205862): Find a way to move these requests out of the test. interceptor.set_interceptor_hook(base::BindLambdaForTesting( [&](content::URLLoaderInterceptor::RequestParams* params) { - if (params->url_request.url.path() == "/update_manifest_v1.xml") { - content::URLLoaderInterceptor::WriteResponse( - GetUpdateManifestHeader(), - GetUpdateManifestBody(kPinnedExtensionCrxId, "v1.crx", - /*version=*/"1"), - params->client.get()); - return true; - } - - if (params->url_request.url.path() == "/v1.crx") { - content::URLLoaderInterceptor::WriteResponse(install_crx_path, - params->client.get()); - return true; - } - return false; + return WriteManifestResponse(params, kPinnedExtensionCrxId, + "/update_manifest_v1.xml", "v1.crx", + /*version=*/"1", install_crx_path); })); extensions::TestExtensionRegistryObserver observer(extension_registry(), @@ -1355,21 +1339,9 @@ // extension. interceptor.set_interceptor_hook(base::BindLambdaForTesting( [&](content::URLLoaderInterceptor::RequestParams* params) { - if (params->url_request.url.path() == "/update_manifest_v2.xml") { - content::URLLoaderInterceptor::WriteResponse( - GetUpdateManifestHeader(), - GetUpdateManifestBody(kPinnedExtensionCrxId, "v2.crx", - /*version=*/"2"), - params->client.get()); - return true; - } - - if (params->url_request.url.path() == "/v2.crx") { - content::URLLoaderInterceptor::WriteResponse(updated_crx_path, - params->client.get()); - return true; - } - return false; + return WriteManifestResponse(params, kPinnedExtensionCrxId, + "/update_manifest_v2.xml", "v2.crx", + /*version=*/"2", updated_crx_path); })); // Change |update_url| to point to version 2 of the extension. SetExtensionSettingsPolicy("/update_manifest_v2.xml", kPinnedExtensionCrxId); @@ -1397,24 +1369,11 @@ // unexpected request. ExtensionRequestInterceptor interceptor; ASSERT_TRUE(embedded_test_server()->Start()); - // TODO(b/172205862): Find a way to move these requests out of the test. interceptor.set_interceptor_hook(base::BindLambdaForTesting( [&](content::URLLoaderInterceptor::RequestParams* params) { - if (params->url_request.url.path() == "/update_manifest_v1.xml") { - content::URLLoaderInterceptor::WriteResponse( - GetUpdateManifestHeader(), - GetUpdateManifestBody(kPinnedExtensionCrxId, "v1.crx", - /*version=*/"1"), - params->client.get()); - return true; - } - - if (params->url_request.url.path() == "/v1.crx") { - content::URLLoaderInterceptor::WriteResponse(install_crx_path, - params->client.get()); - return true; - } - return false; + return WriteManifestResponse(params, kPinnedExtensionCrxId, + "/update_manifest_v1.xml", "v1.crx", + /*version=*/"1", install_crx_path); })); extensions::TestExtensionRegistryObserver observer(extension_registry(), @@ -1450,26 +1409,17 @@ // unexpected request. ExtensionRequestInterceptor interceptor; ASSERT_TRUE(embedded_test_server()->Start()); - // TODO(b/172205862): Find a way to move these requests out of the test. + interceptor.set_interceptor_hook(base::BindLambdaForTesting( [&](content::URLLoaderInterceptor::RequestParams* params) { - if (params->url_request.url.path() == "/update_manifest.xml") { - content::URLLoaderInterceptor::WriteResponse( - GetUpdateManifestHeader(), - GetUpdateManifestBody(kGoogleCalendarCrxId, "calendar.crx", - "3.1.0"), - params->client.get()); - return true; - } - - if (params->url_request.url.path() == "/calendar.crx") { - content::URLLoaderInterceptor::WriteResponse( - "chrome/test/data/extensions/pinning/calendar/" - "gmbgaklkmjakoegficnlkhebmhkjfich-google-calendar-3.1.0.crx", - params->client.get()); - return true; - } - return false; + base::FilePath path; + base::PathService::Get(base::DIR_SRC_TEST_DATA_ROOT, &path); + path = path.AppendASCII( + "chrome/test/data/extensions/pinning/calendar/" + "gmbgaklkmjakoegficnlkhebmhkjfich-google-calendar-3.1.0.crx"); + return WriteManifestResponse(params, kGoogleCalendarCrxId, + "/update_manifest.xml", "calendar.crx", + "3.1.0", path); })); extensions::TestExtensionRegistryObserver observer(extension_registry(),
diff --git a/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc b/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc index 20032900..01cc5f1 100644 --- a/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc +++ b/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
@@ -489,9 +489,7 @@ SigninProfileAttributesUpdaterFactory::GetInstance(); if (site_engagement::SiteEngagementService::IsEnabled()) site_engagement::SiteEngagementServiceFactory::GetInstance(); -// TODO(https://crbug.com/1198523: Remove Lacros check once Dice is no longer -// supported on Lacros. -#if BUILDFLAG(ENABLE_DICE_SUPPORT) && !BUILDFLAG(IS_CHROMEOS_LACROS) +#if BUILDFLAG(ENABLE_DICE_SUPPORT) SigninManagerFactory::GetInstance(); #endif #if BUILDFLAG(ENABLE_SPELLCHECK)
diff --git a/chrome/browser/resources/BUILD.gn b/chrome/browser/resources/BUILD.gn index 2a525b3a..8091d44 100644 --- a/chrome/browser/resources/BUILD.gn +++ b/chrome/browser/resources/BUILD.gn
@@ -143,7 +143,6 @@ "quota_internals:closure_compile", "settings:closure_compile", "sync_file_system_internals:closure_compile", - "tab_search:closure_compile", "usb_internals:closure_compile", ] }
diff --git a/chrome/browser/resources/chromeos/accessibility/strings/accessibility_strings_en-GB.xtb b/chrome/browser/resources/chromeos/accessibility/strings/accessibility_strings_en-GB.xtb index c940373..684cda18 100644 --- a/chrome/browser/resources/chromeos/accessibility/strings/accessibility_strings_en-GB.xtb +++ b/chrome/browser/resources/chromeos/accessibility/strings/accessibility_strings_en-GB.xtb
@@ -30,6 +30,7 @@ <translation id="1189258430971676908">Practice area: drop-down lists</translation> <translation id="1195238899008218998">Afterword</translation> <translation id="1197088940767939838">Orange</translation> +<translation id="1198865190323699001">Touch gestures</translation> <translation id="1201402288615127009">Next</translation> <translation id="1206619573307042055">marquee</translation> <translation id="1207086294218137981">No next level 4 heading</translation>
diff --git a/chrome/browser/resources/chromeos/accessibility/strings/accessibility_strings_uz.xtb b/chrome/browser/resources/chromeos/accessibility/strings/accessibility_strings_uz.xtb index 9521997e..dea80d1c 100644 --- a/chrome/browser/resources/chromeos/accessibility/strings/accessibility_strings_uz.xtb +++ b/chrome/browser/resources/chromeos/accessibility/strings/accessibility_strings_uz.xtb
@@ -30,6 +30,7 @@ <translation id="1189258430971676908">Mashq hududi: Pastga ochiladigan roʻyxatlar</translation> <translation id="1195238899008218998">Xotima</translation> <translation id="1197088940767939838">Apelsinrang</translation> +<translation id="1198865190323699001">Sensorli ishoralar</translation> <translation id="1201402288615127009">Keyingisi</translation> <translation id="1206619573307042055">marquee</translation> <translation id="1207086294218137981">Bundan keyin boshqa 2-darajali sarlavha mavjud emas</translation>
diff --git a/chrome/browser/resources/settings/chromeos/crostini_page/crostini_page.html b/chrome/browser/resources/settings/chromeos/crostini_page/crostini_page.html index eb22138..c743998 100644 --- a/chrome/browser/resources/settings/chromeos/crostini_page/crostini_page.html +++ b/chrome/browser/resources/settings/chromeos/crostini_page/crostini_page.html
@@ -30,7 +30,6 @@ <cr-button id="enable" disabled$="[[disableCrostiniInstall_]]" on-click="onEnableTap_" - aria-label="$i18n{crostiniPageTitle}" aria-describedby="secondaryText" deep-link-focus-id$="[[Setting.kSetUpCrostini]]"> $i18n{crostiniEnable}
diff --git a/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_permissions_setup_dialog.html b/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_permissions_setup_dialog.html index 5a705dc..64b5abb9 100644 --- a/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_permissions_setup_dialog.html +++ b/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_permissions_setup_dialog.html
@@ -1,21 +1,21 @@ <link rel="import" href="chrome://resources/html/polymer.html"> +<link rel="import" href="chrome://resources/cr_components/chromeos/localized_link/localized_link.html"> +<link rel="import" href="chrome://resources/cr_elements/cr_button/cr_button.html"> +<link rel="import" href="chrome://resources/cr_elements/cr_dialog/cr_dialog.html"> +<link rel="import" href="chrome://resources/cr_elements/shared_style_css.html"> +<link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html"> <link rel="import" href="chrome://resources/html/assert.html"> <link rel="import" href="chrome://resources/html/i18n_behavior.html"> <link rel="import" href="chrome://resources/html/load_time_data.html"> <link rel="import" href="chrome://resources/html/web_ui_listener_behavior.html"> -<link rel="import" href="chrome://resources/cr_elements/cr_button/cr_button.html"> -<link rel="import" href="chrome://resources/cr_elements/cr_dialog/cr_dialog.html"> -<link rel="import" href="chrome://resources/cr_elements/shared_style_css.html"> <link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html"> <link rel="import" href="multidevice_browser_proxy.html"> <link rel="import" href="multidevice_constants.html"> <link rel="import" href="multidevice_screen_lock_subpage.html"> -<link rel="import" href="chrome://resources/cr_components/chromeos/localized_link/localized_link.html"> <link rel="import" href="../os_icons.html"> <link rel="import" href="../../settings_shared_css.html"> -<!-- TODO(crbug.com/1272362): The layout needs to be finalized .--> <dom-module id="settings-multidevice-permissions-setup-dialog"> <template> <style include="cr-shared-style settings-shared"> @@ -29,24 +29,32 @@ } div[slot='title'] { + color: var(--cr-primary-text-color); flex-direction: column; - font-weight: bold; + font-family: 'Google Sans'; + font-size: 16px; + font-weight: medium; height: auto; + line-height: 24px; + padding-bottom: 24px; + padding-inline-end: 24px; + padding-inline-start: 24px; + padding-top: 24px; } #subtitle { color: var(--cr-secondary-text-color); - font-size: 13px; - font-weight: normal; - padding-top: 13px; + font-family: 'Roboto'; + font-size: 14px; + font-weight: medium; + line-height: 20px; } div[slot='body'] { display: flex; flex-direction: column; - height: auto; - justify-content: center; - width: 464px; + padding-inline-end: 24px; + padding-inline-start: 24px; } div[slot='button-container'] { @@ -54,6 +62,10 @@ display: flex; flex-direction: row; justify-content: space-between; + padding-bottom: 20px; + padding-inline-end: 24px; + padding-inline-start: 24px; + padding-top: 40px; } #failure-icon { @@ -205,13 +217,13 @@ </template> <template is="dom-if" if="[[shouldShowSetupInstructionsSeparately_]]" restamp> - <div id="instruction"> - <template is="dom-if" if="[[!shouldShowScreenLockInstructions_(flowState_)]]" restamp> + <template is="dom-if" if="[[!shouldShowScreenLockInstructions_(flowState_)]]" restamp> + <div id="instruction"> <iron-icon id="instruction-icon" icon="os-settings:failure-alert"> </iron-icon> $i18n{multideviceNotificationAccessSetupInstructions} - </template> - </div> + </div> + </template> </template> </div> <div id="buttonContainer" slot="button-container">
diff --git a/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_screen_lock_subpage.html b/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_screen_lock_subpage.html index c5f0b14..ee3fa69 100644 --- a/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_screen_lock_subpage.html +++ b/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_screen_lock_subpage.html
@@ -1,6 +1,7 @@ <link rel="import" href="chrome://resources/html/polymer.html"> <link rel="import" href="chrome://resources/cr_components/chromeos/quick_unlock/lock_screen_constants.html"> +<link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html"> <link rel="import" href="chrome://resources/html/assert.html"> <link rel="import" href="chrome://resources/html/cr/ui/focus_without_ink.html"> <link rel="import" href="chrome://resources/html/i18n_behavior.html"> @@ -19,8 +20,12 @@ display: flex; flex-direction: row; height: auto; - justify-content: flex-start; - width: 464px; + justify-content: center; + } + + #half-container { + flex: 1; + height: 216px; } #illustration { @@ -30,31 +35,64 @@ background-repeat: no-repeat; background-size: contain; height: 200px; - margin-bottom: 24px; - margin-top: 24px; - max-width: 200px; + margin-bottom: 8px; + margin-top: 8px; width: 100%; } + #radio-button-container { + padding-top: 20px; + } + + #passwordRadioButton { + --cr-radio-button-label-spacing: 20px; + --cr-radio-button-size: 20px; + color: var(--cr-primary-text-color); + font-family: 'Roboto'; + font-size: 13px; + font-weight: medium; + line-height: 20px; + min-height: 20px; + padding-inline-start: 8px; + padding-top: 24px; + } + + #pinRadioButton { + --cr-radio-button-label-spacing: 20px; + --cr-radio-button-size: 20px; + color: var(--cr-primary-text-color); + font-family: 'Roboto'; + font-size: 13px; + font-weight: medium; + line-height: 20px; + min-height: 20px; + padding-inline-start: 8px; + padding-top: 44px; + } + #subtext { color: var(--cr-secondary-text-color); + font-family: 'Roboto'; font-size: 13px; - font-weight: normal; - padding-inline-start: 36px; + font-weight: medium; + line-height: 20px; + padding-inline-start: 48px; } </style> <div id="screen-lock-description"> - <div id="illustration"></div> - <template is="dom-if" if="[[authToken_]]"> - <div class="list-frame" > - <cr-radio-group id="unlockType" + <div id="half-container"> + <div id="illustration"></div> + </div> + <div id="half-container"> + <template is="dom-if" if="[[authToken_]]"> + <cr-radio-group id=radio-button-container disabled$="[[quickUnlockDisabledByPolicy_]]" selected="{{selectedUnlockType}}" deep-link-focus-id$="[[Setting.kChangeAuthPinV2]]"> - <cr-radio-button name="password" + <cr-radio-button id="passwordRadioButton" name="password" label=$i18n{lockScreenPasswordOnly}> </cr-radio-button> - <cr-radio-button name="pin+password" + <cr-radio-button id="pinRadioButton" name="pin+password" label=$i18n{lockScreenPinOrPassword}> </cr-radio-button> <div id="subtext"> @@ -76,8 +114,8 @@ </div> </template> </cr-radio-group> - </div> - </template> + </template> + </div> </div> <template is="dom-if" if="[[shouldPromptPasswordDialog_]]" restamp> <settings-lock-screen-password-prompt-dialog id="passwordDialog"
diff --git a/chrome/browser/resources/settings/chromeos/os_languages_page/os_edit_dictionary_page.html b/chrome/browser/resources/settings/chromeos/os_languages_page/os_edit_dictionary_page.html index 4ae332a..16984a7 100644 --- a/chrome/browser/resources/settings/chromeos/os_languages_page/os_edit_dictionary_page.html +++ b/chrome/browser/resources/settings/chromeos/os_languages_page/os_edit_dictionary_page.html
@@ -58,7 +58,8 @@ <cr-input id="newWord" value="{{newWordValue_}}" invalid="[[isNewWordInvalid_(newWordState_)]]" error-message="[[getErrorMessage_(newWordState_)]]" - spellcheck="false"> + spellcheck="false" + aria-label="$i18n{editDictionaryDescription}"> <cr-button on-click="onAddWordTap_" id="addWord" slot="suffix" disabled="[[disableAddButton_]]"> $i18n{addDictionaryWordButtonLabel}
diff --git a/chrome/browser/resources/settings/chromeos/os_privacy_page/os_privacy_page.js b/chrome/browser/resources/settings/chromeos/os_privacy_page/os_privacy_page.js index e335fb9..a4830ad 100644 --- a/chrome/browser/resources/settings/chromeos/os_privacy_page/os_privacy_page.js +++ b/chrome/browser/resources/settings/chromeos/os_privacy_page/os_privacy_page.js
@@ -137,7 +137,8 @@ isSmartPrivacyEnabled_: { type: Boolean, value() { - return loadTimeData.getBoolean('isSmartPrivacyEnabled'); + return loadTimeData.getBoolean('isSnoopingProtectionEnabled') || + loadTimeData.getBoolean('isQuickDimEnabled'); }, readOnly: true, },
diff --git a/chrome/browser/resources/settings/chromeos/os_privacy_page/smart_privacy_page.html b/chrome/browser/resources/settings/chromeos/os_privacy_page/smart_privacy_page.html index f781c26..2161115 100644 --- a/chrome/browser/resources/settings/chromeos/os_privacy_page/smart_privacy_page.html +++ b/chrome/browser/resources/settings/chromeos/os_privacy_page/smart_privacy_page.html
@@ -42,30 +42,40 @@ $i18n{smartPrivacyDesc} </div> </div> -<settings-toggle-button class="underbar" - pref="{{prefs.power.quick_dim_enabled}}" - id="quickDimToggle" - label="$i18n{smartPrivacyQuickDimTitle}" - sub-label="$i18n{smartPrivacyQuickDimSubtext}" - deep-link-focus-id$="[[Setting.kQuickDim]]"> -</settings-toggle-button> -<settings-toggle-button - pref="{{prefs.ash.privacy.snooping_protection_enabled}}" - id="snoopingProtectionToggle" - label="$i18n{smartPrivacySnoopingTitle}" - sub-label="$i18n{smartPrivacySnoopingSubtext}" - deep-link-focus-id$="[[Setting.kSnoopingProtection]]"> -</settings-toggle-button> -<iron-collapse id="snoopingProtectionOptions" - opened="[[prefs.ash.privacy.snooping_protection_enabled.value]]"> - <div class="settings-box continuation embedded"> - <cr-radio-group> - <cr-radio-button name="dim" class="list-item" - label="$i18n{smartPrivacySnoopingDim}"> - </cr-radio-button> - <cr-radio-button name="icon" class="list-item" - label="$i18n{smartPrivacySnoopingIcon}"> - </cr-radio-button> - </cr-radio-group> - </div> -</iron-collapse> +<template is="dom-if" if="[[isQuickDimEnabled_]]" restamp> + <settings-toggle-button + pref="{{prefs.power.quick_dim_enabled}}" + id="quickDimToggle" + label="$i18n{smartPrivacyQuickDimTitle}" + sub-label="$i18n{smartPrivacyQuickDimSubtext}" + deep-link-focus-id$="[[Setting.kQuickDim]]"> + </settings-toggle-button> + + <!-- Underbar is only needed if we need to separate the two + settings. --> + <template is="dom-if" if="[[isSnoopingProtectionEnabled_]]" restamp> + <div class="underbar"></div> + </template> +</template> +<template is="dom-if" if="[[isSnoopingProtectionEnabled_]]" restamp> + <settings-toggle-button + pref="{{prefs.ash.privacy.snooping_protection_enabled}}" + id="snoopingProtectionToggle" + label="$i18n{smartPrivacySnoopingTitle}" + sub-label="$i18n{smartPrivacySnoopingSubtext}" + deep-link-focus-id$="[[Setting.kSnoopingProtection]]"> + </settings-toggle-button> + <iron-collapse id="snoopingProtectionOptions" + opened="[[prefs.ash.privacy.snooping_protection_enabled.value]]"> + <div class="settings-box continuation embedded"> + <cr-radio-group> + <cr-radio-button name="dim" class="list-item" + label="$i18n{smartPrivacySnoopingDim}"> + </cr-radio-button> + <cr-radio-button name="icon" class="list-item" + label="$i18n{smartPrivacySnoopingIcon}"> + </cr-radio-button> + </cr-radio-group> + </div> + </iron-collapse> +</template>
diff --git a/chrome/browser/resources/settings/chromeos/os_privacy_page/smart_privacy_page.js b/chrome/browser/resources/settings/chromeos/os_privacy_page/smart_privacy_page.js index a9180e0..00651c12 100644 --- a/chrome/browser/resources/settings/chromeos/os_privacy_page/smart_privacy_page.js +++ b/chrome/browser/resources/settings/chromeos/os_privacy_page/smart_privacy_page.js
@@ -15,6 +15,7 @@ import '../../settings_shared_css.js'; import '../../settings_vars_css.js'; +import {loadTimeData} from '//resources/js/load_time_data.m.js'; import {html, mixinBehaviors, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js'; import {Route, Router} from '../../router.js'; @@ -54,6 +55,29 @@ notify: true, }, + + /** + * Whether or not quick dim is enabled. + * @private {boolean} + */ + isQuickDimEnabled_: { + type: Boolean, + value() { + return loadTimeData.getBoolean('isQuickDimEnabled'); + }, + }, + + /** + * Whether or not snooping protection is enabled. + * @private {boolean} + */ + isSnoopingProtectionEnabled_: { + type: Boolean, + value() { + return loadTimeData.getBoolean('isSnoopingProtectionEnabled'); + }, + }, + /** * Used by DeepLinkingBehavior to focus this page's deep links. * @type {!Set<!chromeos.settings.mojom.Setting>} @@ -68,7 +92,6 @@ }; } - /** * RouteObserverBehavior * @param {!Route} route
diff --git a/chrome/browser/resources/signin/BUILD.gn b/chrome/browser/resources/signin/BUILD.gn index cba4d981a..b188527 100644 --- a/chrome/browser/resources/signin/BUILD.gn +++ b/chrome/browser/resources/signin/BUILD.gn
@@ -4,6 +4,7 @@ import("//build/config/chromeos/ui_mode.gni") import("//chrome/common/features.gni") +import("//components/signin/features.gni") import("//tools/grit/grit_rule.gni") import("//tools/grit/preprocess_if_expr.gni") import("//tools/polymer/html_to_js.gni") @@ -21,11 +22,15 @@ ] if (!is_chromeos_ash) { public_deps += [ - "dice_web_signin_intercept:web_components", "enterprise_profile_welcome:web_components", "profile_customization:web_components", "signin_email_confirmation:web_components", "signin_error:web_components", + ] + } + if (enable_dice_support) { + public_deps += [ + "dice_web_signin_intercept:web_components", "signin_reauth:web_components", ] } @@ -70,12 +75,16 @@ ] if (!is_chromeos_ash) { input_files += [ - "dice_web_signin_intercept/dice_web_signin_intercept.html", "enterprise_profile_welcome/enterprise_profile_welcome.html", "enterprise_profile_welcome/images/enterprise_profile_welcome_illustration.svg", "profile_customization/profile_customization.html", "signin_email_confirmation/signin_email_confirmation.html", "signin_error/signin_error.html", + ] + } + if (enable_dice_support) { + input_files += [ + "dice_web_signin_intercept/dice_web_signin_intercept.html", "signin_reauth/images/account_passwords_reauth_illustration_dark.svg", "signin_reauth/images/account_passwords_reauth_illustration.svg", "signin_reauth/signin_reauth.html", @@ -97,10 +106,14 @@ if (!is_chromeos_ash) { in_files += [ - "dice_web_signin_intercept/dice_web_signin_intercept_browser_proxy.ts", "enterprise_profile_welcome/enterprise_profile_welcome_browser_proxy.ts", "profile_customization/profile_customization_browser_proxy.ts", "signin_error/signin_error.ts", + ] + } + if (enable_dice_support) { + in_files += [ + "dice_web_signin_intercept/dice_web_signin_intercept_browser_proxy.ts", "signin_reauth/signin_reauth_browser_proxy.ts", ] } @@ -118,11 +131,15 @@ if (!is_chromeos_ash) { in_files += [ - "dice_web_signin_intercept/dice_web_signin_intercept_app.ts", "enterprise_profile_welcome/enterprise_profile_welcome_app.ts", "profile_customization/profile_customization_app.ts", "signin_email_confirmation/signin_email_confirmation_app.ts", "signin_error/signin_error_app.ts", + ] + } + if (enable_dice_support) { + in_files += [ + "dice_web_signin_intercept/dice_web_signin_intercept_app.ts", "signin_reauth/signin_reauth_app.ts", ] } @@ -146,8 +163,6 @@ if (!is_chromeos_ash) { in_files += [ - "dice_web_signin_intercept/dice_web_signin_intercept_app.ts", - "dice_web_signin_intercept/dice_web_signin_intercept_browser_proxy.ts", "enterprise_profile_welcome/enterprise_profile_welcome_app.ts", "enterprise_profile_welcome/enterprise_profile_welcome_browser_proxy.ts", "profile_customization/profile_customization_app.ts", @@ -155,6 +170,12 @@ "signin_email_confirmation/signin_email_confirmation_app.ts", "signin_error/signin_error_app.ts", "signin_error/signin_error.ts", + ] + } + if (enable_dice_support) { + in_files += [ + "dice_web_signin_intercept/dice_web_signin_intercept_app.ts", + "dice_web_signin_intercept/dice_web_signin_intercept_browser_proxy.ts", "signin_reauth/signin_reauth_app.ts", "signin_reauth/signin_reauth_browser_proxy.ts", ]
diff --git a/chrome/browser/resources/tab_search/BUILD.gn b/chrome/browser/resources/tab_search/BUILD.gn index b878c8fc..31be15a 100644 --- a/chrome/browser/resources/tab_search/BUILD.gn +++ b/chrome/browser/resources/tab_search/BUILD.gn
@@ -4,7 +4,6 @@ import("//chrome/browser/resources/tools/optimize_webui.gni") import("//chrome/common/features.gni") -import("//third_party/closure_compiler/compile_js.gni") import("//tools/grit/grit_rule.gni") import("//tools/grit/preprocess_if_expr.gni") import("//tools/polymer/html_to_js.gni") @@ -12,6 +11,8 @@ import("//ui/webui/resources/tools/generate_grd.gni") import("//ui/webui/webui_features.gni") +assert(is_linux || is_chromeos || is_win || is_mac || is_fuchsia) + preprocess_folder = "preprocessed" if (optimize_webui) { @@ -82,14 +83,14 @@ in_folder = "./" out_folder = "$target_gen_dir/$preprocess_folder" in_files = [ - "bimap.js", - "fuzzy_search.js", - "tab_data.js", - "tab_group_color_helper.js", - "tab_search.js", - "tab_search_api_proxy.js", - "tab_search_utils.js", - "title_item.js", + "bimap.ts", + "fuzzy_search.ts", + "tab_data.ts", + "tab_group_color_helper.ts", + "tab_search.ts", + "tab_search_api_proxy.ts", + "tab_search_utils.ts", + "title_item.ts", ] } @@ -107,7 +108,7 @@ in_files = [ "app.js", "infinite_list.js", - "tab_group_shared_vars.js", + "tab_group_shared_vars.ts", "tab_search_item.js", "tab_search_group_item.js", "tab_search_search_field.js", @@ -119,6 +120,11 @@ outputs = [ "$target_gen_dir/$preprocess_folder/fuse.js" ] } +copy("copy_fuse_dts") { + sources = [ "//third_party/fusejs/dist/fuse.d.ts" ] + outputs = [ "$target_gen_dir/$preprocess_folder/{{source_file_part}}" ] +} + preprocess_if_expr("preprocess_mojo_tab_groups") { deps = [ "//components/tab_groups/public/mojom:mojo_bindings_webui_js" ] in_folder = "$root_gen_dir/mojom-webui/components/tab_groups/public/mojom/" @@ -150,134 +156,11 @@ output_dir = "$root_gen_dir/chrome" } -js_type_check("closure_compile") { - is_polymer3 = true - closure_flags = - default_closure_args + mojom_js_args + [ - "js_module_root=" + rebase_path(".", root_build_dir), - "js_module_root=" + rebase_path( - "$root_gen_dir/mojom-webui/chrome/browser/ui/webui/tab_search", - root_build_dir), - "js_module_root=" + rebase_path( - "$root_gen_dir/mojom-webui/components/tab_groups/public/mojom", - root_build_dir), - "js_module_root=" + rebase_path( - "$root_gen_dir/mojom-webui/chrome/browser/ui/webui/tabs", - root_build_dir), - ] - deps = [ - ":app", - ":bimap", - ":fuzzy_search", - ":infinite_list", - ":tab_data", - ":tab_search", - ":tab_search_api_proxy", - ":tab_search_group_item", - ":tab_search_item", - ":tab_search_search_field", - ":title_item", - ] -} - -js_library("app") { - deps = [ - ":fuzzy_search", - ":infinite_list", - ":tab_data", - ":tab_search_api_proxy", - ":tab_search_group_item", - ":tab_search_item", - ":tab_search_search_field", - ":title_item", - "//third_party/polymer/v3_0/components-chromium/iron-a11y-announcer", - "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled", - "//ui/webui/resources/cr_elements/cr_expand_button:cr_expand_button.m", - "//ui/webui/resources/js:cr.m", - "//ui/webui/resources/js:load_time_data.m", - "//ui/webui/resources/js:util.m", - ] - externs_list = [ "$externs_path/metrics_private.js" ] -} - -js_library("bimap") { - deps = [] -} - -js_library("fuzzy_search") { - deps = [ - ":tab_data", - "//ui/webui/resources/js:util.m", - ] -} - -js_library("infinite_list") { - deps = [ - ":bimap", - "//third_party/polymer/v3_0/components-chromium/iron-selector:iron-selector", - "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled", - ] -} - -js_library("tab_data") { - deps = [] -} - -js_library("tab_group_color_helper") { - deps = [ "//components/tab_groups/public/mojom:mojo_bindings_webui_js" ] -} - -js_library("tab_search") { - deps = [ ":app" ] -} - -js_library("tab_search_api_proxy") { - deps = [ - "//chrome/browser/ui/webui/tab_search:mojo_bindings_webui_js", - "//ui/webui/resources/js:cr.m", - ] - externs_list = [ "$externs_path/metrics_private.js" ] -} - -js_library("tab_search_item") { - deps = [ - ":tab_data", - ":tab_group_color_helper", - "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled", - "//ui/webui/resources/cr_elements:mouse_hoverable_mixin", - "//ui/webui/resources/cr_elements/cr_icon_button:cr_icon_button.m", - "//ui/webui/resources/js:icon", - "//ui/webui/resources/js:load_time_data.m", - ] -} - -js_library("tab_search_group_item") { - deps = [ - ":tab_group_color_helper", - "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled", - "//ui/webui/resources/cr_elements:mouse_hoverable_mixin", - "//ui/webui/resources/cr_elements/cr_icon_button:cr_icon_button.m", - "//ui/webui/resources/js:icon", - "//ui/webui/resources/js:load_time_data.m", - ] -} - -js_library("tab_search_search_field") { - deps = [ - "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled", - "//ui/webui/resources/cr_elements/cr_search_field:cr_search_field_behavior", - ] -} - -js_library("title_item") { - deps = [] -} - html_to_js("web_components") { js_files = [ "app.js", "infinite_list.js", - "tab_group_shared_vars.js", + "tab_group_shared_vars.ts", "tab_search_item.js", "tab_search_group_item.js", "tab_search_search_field.js", @@ -290,31 +173,34 @@ tsconfig_base = "tsconfig_base.json" in_files = [ "app.js", - "bimap.js", + "bimap.ts", "fuse.js", - "fuzzy_search.js", + "fuzzy_search.ts", "infinite_list.js", - "tab_data.js", - "tab_group_color_helper.js", - "tab_group_shared_vars.js", + "tab_data.ts", + "tab_group_color_helper.ts", + "tab_group_shared_vars.ts", "tab_group_types.mojom-webui.js", - "tab_search_api_proxy.js", + "tab_search_api_proxy.ts", "tab_search_group_item.js", "tab_search_item.js", - "tab_search.js", + "tab_search.ts", "tab_search.mojom-webui.js", "tab_search_search_field.js", - "tab_search_utils.js", + "tab_search_utils.ts", "tabs.mojom-webui.js", - "title_item.js", + "title_item.ts", ] + definitions = [ "//tools/typescript/definitions/metrics_private.d.ts" ] deps = [ "//third_party/polymer/v3_0:library", "//ui/webui/resources:library", + "//ui/webui/resources/mojo:library", ] extra_deps = [ ":copy_fuse", + ":copy_fuse_dts", ":preprocess", ":preprocess_mojo", ":preprocess_mojo_tab_groups",
diff --git a/chrome/browser/resources/tab_search/bimap.js b/chrome/browser/resources/tab_search/bimap.js deleted file mode 100644 index d6c7495..0000000 --- a/chrome/browser/resources/tab_search/bimap.js +++ /dev/null
@@ -1,76 +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. - -/** - * @fileoverview 'BiMap' is an implementation of a bidirectional map. It - * facilitates looking up for associated pairs in either direction. - */ - -// TODO(romanarora): Investigate leveraging an existing data structures third -// party library or moving this class to a shareable location. - -/** - * @template K, V - */ -export class BiMap { - /** - * @param {!Object=} map - */ - constructor(map) { - /** - * @private {!Map<K, V>} - */ - this.map_ = new Map(); - - /** - * @private {!Map<V, K>} - */ - this.inverseMap_ = new Map(); - - for (const key in map) { - const value = map[key]; - this.map_.set(key, value); - this.inverseMap_.set(value, key); - } - } - - /** - * @param {K} key - * @return {V} - */ - get(key) { - return this.map_.get(key); - } - - /** - * @param {V} key - * @return {K} - */ - invGet(key) { - return this.inverseMap_.get(key); - } - - /** - * @param {K} key - * @param {V} value - */ - set(key, value) { - this.map_.set(key, value); - this.inverseMap_.set(value, key); - } - - /** - * @param {V} key - * @param {K} value - */ - invSet(key, value) { - this.inverseMap_.set(key, value); - this.map_.set(value, key); - } - - /** @return {number} */ - size() { - return this.map_.size; - } -}
diff --git a/chrome/browser/resources/tab_search/bimap.ts b/chrome/browser/resources/tab_search/bimap.ts new file mode 100644 index 0000000..b482acd --- /dev/null +++ b/chrome/browser/resources/tab_search/bimap.ts
@@ -0,0 +1,43 @@ +// 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. + +/** + * @fileoverview 'BiMap' is an implementation of a bidirectional map. It + * facilitates looking up for associated pairs in either direction. + */ + +// TODO(romanarora): Investigate leveraging an existing data structures third +// party library or moving this class to a shareable location. + +export class BiMap<K, V> { + private map_: Map<K, V>; + private inverseMap_: Map<V, K>; + + constructor() { + this.map_ = new Map<K, V>(); + this.inverseMap_ = new Map<V, K>(); + } + + get(key: K): V|undefined { + return this.map_.get(key); + } + + invGet(key: V): K|undefined { + return this.inverseMap_.get(key); + } + + set(key: K, value: V) { + this.map_.set(key, value); + this.inverseMap_.set(value, key); + } + + invSet(key: V, value: K) { + this.inverseMap_.set(key, value); + this.map_.set(value, key); + } + + size(): number { + return this.map_.size; + } +}
diff --git a/chrome/browser/resources/tab_search/fuzzy_search.js b/chrome/browser/resources/tab_search/fuzzy_search.ts similarity index 73% rename from chrome/browser/resources/tab_search/fuzzy_search.js rename to chrome/browser/resources/tab_search/fuzzy_search.ts index 781683f..855a588 100644 --- a/chrome/browser/resources/tab_search/fuzzy_search.js +++ b/chrome/browser/resources/tab_search/fuzzy_search.ts
@@ -6,16 +6,18 @@ import {get as deepGet} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js'; import Fuse from './fuse.js'; -import {TabData} from './tab_data.js'; +import {ItemData} from './tab_data.js'; + +export type FuzzySearchOptions = + Fuse.IFuseOptions<ItemData>&{useFuzzySearch: boolean}; /** - * @param {string} input - * @param {!Array<!TabData>} records - * @param {!Object} options - * @return {!Array<!TabData>} A new array of entries satisfying the input. If no - * search input is present, returns a shallow copy of the records. + * @return A new array of entries satisfying the input. If no search input is + * present, returns a shallow copy of the records. */ -export function fuzzySearch(input, records, options) { +export function fuzzySearch( + input: string, records: ItemData[], + options: FuzzySearchOptions): ItemData[] { if (input.length === 0) { return [...records]; } @@ -31,20 +33,21 @@ const searchStartTime = Date.now(); let result; if (options.useFuzzySearch) { - const keyNames = options.keys.reduce((acc, {name}) => { - acc.push(name); - return acc; - }, []); - result = new Fuse(records, options).search(input).map(result => { + const keyNames = + (options.keys as Fuse.FuseOptionKeyObject[]).reduce((acc, {name}) => { + acc.push(name as string); + return acc; + }, [] as string[]); + result = new Fuse<ItemData>(records, options).search(input).map(result => { const item = cloneTabDataObj(result.item); item.highlightRanges = keyNames.reduce((acc, key) => { - const match = result.matches.find(e => e.key === key); + const match = result.matches!.find(e => e.key === key); if (match) { acc[key] = convertToRanges(match.indices); } return acc; - }, {}); + }, {} as {[key: string]: Array<{start: number, length: number}>}); return item; }); @@ -60,26 +63,21 @@ return result; } -/** - * @param {!TabData} tabData - * @return {!TabData} - */ -function cloneTabDataObj(tabData) { +function cloneTabDataObj(tabData: ItemData): ItemData { const clone = Object.assign({}, tabData); clone.highlightRanges = {}; Object.setPrototypeOf(clone, Object.getPrototypeOf(tabData)); - return /** @type {!TabData} */ (clone); + return clone; } /** * Convert fuse.js matches [start1, end1], [start2, end2] ... to * ranges {start:start1, length:length1}, {start:start2, length:length2} ... * to be used by search_highlight_utils.js - * @param {!Array<!Array<number>>} matches - * @return {!Array<!{start: number, length: number}>} */ -function convertToRanges(matches) { +function convertToRanges(matches: ReadonlyArray<Fuse.RangeTuple>): + Array<{start: number, length: number}> { return matches.map( ([start, end]) => ({start: start, length: end - start + 1})); } @@ -93,12 +91,10 @@ * first) and sorted by score within the same priority. See `scoringFunction` * for how to calculate score and `prioritizeMatchResult` for how to calculate * priority. - * @param {string} searchText - * @param {!Array<!TabData>} records - * @param {!Object} options - * @return {!Array<!TabData>} */ -function exactSearch(searchText, records, options) { +function exactSearch( + searchText: string, records: ItemData[], + options: Fuse.IFuseOptions<ItemData>): ItemData[] { if (searchText.length === 0) { return records; } @@ -110,10 +106,11 @@ // Controls how heavily weighted the search field weights are relative to each // other in the scoring function. - const searchFieldWeights = options.keys.reduce((acc, {name, weight}) => { - acc[name] = weight; - return acc; - }, {}); + const searchFieldWeights = (options.keys as Fuse.FuseOptionKeyObject[]) + .reduce((acc, {name, weight}) => { + acc[name as string] = weight; + return acc; + }, {} as {[key: string]: number}); // Perform an exact match search with range discovery. const exactMatches = []; @@ -122,8 +119,7 @@ const matchedRecord = cloneTabDataObj(tabDataRecord); // Searches for fields or nested fields in the record. for (const fieldPath in searchFieldWeights) { - const text = - /** @type {string} */ (deepGet(tabDataRecord, fieldPath)); + const text = deepGet(tabDataRecord, fieldPath); if (text) { const ranges = getRanges(text, searchText); if (ranges.length !== 0) { @@ -153,12 +149,9 @@ /** * Determines whether the given tab has a search field with identified matches * at the beginning of the string. - * @param {!TabData} tab - * @param {string} searchText - * @param {!Array<string>} keys - * @return {boolean} */ -function hasMatchStringStart(tab, searchText, keys) { +function hasMatchStringStart( + tab: ItemData, searchText: string, keys: string[]): boolean { return keys.some((key) => { const value = deepGet(tab, key); return value !== undefined && value.startsWith(searchText); @@ -168,12 +161,8 @@ /** * Determines whether the given tab has a match for the given regexp in its * search fields. - * @param {!TabData} tab - * @param {RegExp} regexp - * @param {!Array<string>} keys - * @return {boolean} */ -function hasRegexMatch(tab, regexp, keys) { +function hasRegexMatch(tab: ItemData, regexp: RegExp, keys: string[]): boolean { return keys.some((key) => { const value = deepGet(tab, key); return value !== undefined && value.search(regexp) !== -1; @@ -184,18 +173,16 @@ * Returns an array of matches that indicate where in the target string the * searchText appears. If there are no identified matches an empty array is * returned. - * @param {string} target - * @param {string} searchText - * @return {!Array<!{start: number, length: number}>} */ -function getRanges(target, searchText) { +function getRanges(target: string, searchText: string): + Array<{start: number, length: number}> { const escapedText = quoteString(searchText); const ranges = []; let match = null; for (const re = new RegExp(escapedText, 'gi'); match = re.exec(target);) { ranges.push({ - start : match.index, - length : searchText.length, + start: match.index, + length: searchText.length, }); } return ranges; @@ -206,19 +193,18 @@ * Matches near the beginning of the string will have a higher score than * matches near the end of the string. Multiple matches will have a higher score * than single matches. - * @param {!TabData} tabData - * @param {number} distance - * @param {!Object} searchFieldWeights */ -function scoringFunction(tabData, distance, searchFieldWeights) { +function scoringFunction( + tabData: ItemData, distance: number, + searchFieldWeights: {[key: string]: number}) { let score = 0; // For every match, map the match index in [0, distance] to a scalar value in // [1, 0]. for (const key in searchFieldWeights) { if (tabData.highlightRanges[key]) { - for (const {start} of tabData.highlightRanges[key]) { + for (const {start} of tabData.highlightRanges[key]!) { score += Math.max((distance - start) / distance, 0) * - searchFieldWeights[key]; + searchFieldWeights[key]!; } } } @@ -234,12 +220,9 @@ * word in the string. * 3. All remaining items with a search key matching the searchText elsewhere in * the string. - * @param {string} searchText - * @param {!Array<string>} keys - * @param {!Array<!TabData>} result - * @return {!Array<!TabData>} */ -function prioritizeMatchResult(searchText, keys, result) { +function prioritizeMatchResult( + searchText: string, keys: string[], result: ItemData[]): ItemData[] { const itemsMatchingStringStart = []; const itemsMatchingWordStart = []; const others = []; @@ -255,4 +238,4 @@ } } return itemsMatchingStringStart.concat(itemsMatchingWordStart, others); -} \ No newline at end of file +}
diff --git a/chrome/browser/resources/tab_search/tab_data.js b/chrome/browser/resources/tab_search/tab_data.ts similarity index 60% rename from chrome/browser/resources/tab_search/tab_data.js rename to chrome/browser/resources/tab_search/tab_data.ts index 39048f4..b66f8d8 100644 --- a/chrome/browser/resources/tab_search/tab_data.js +++ b/chrome/browser/resources/tab_search/tab_data.ts
@@ -7,30 +7,18 @@ import {RecentlyClosedTab, RecentlyClosedTabGroup, Tab, TabGroup} from './tab_search.mojom-webui.js'; -/** @enum {number} */ -export const TabItemType = { - OPEN_TAB: 1, - RECENTLY_CLOSED_TAB: 2, - RECENTLY_CLOSED_TAB_GROUP: 3, -}; +export enum TabItemType { + OPEN_TAB = 1, + RECENTLY_CLOSED_TAB = 2, + RECENTLY_CLOSED_TAB_GROUP = 3, +} export class ItemData { - constructor() { - /** @type {boolean} */ - this.inActiveWindow; - - /** @type {!TabItemType} */ - this.type; - - /** @type {string} */ - this.a11yTypeText; - - /** @type {?TabGroup|?RecentlyClosedTabGroup} */ - this.tabGroup; - - /** @type {!Object} */ - this.highlightRanges = {}; - } + inActiveWindow: boolean; + type: TabItemType; + a11yTypeText: string; + tabGroup?: TabGroup|RecentlyClosedTabGroup; + highlightRanges: {[key: string]: Array<{start: number, length: number}>} = {}; } /** @@ -39,25 +27,18 @@ * type checking. */ export class TabData extends ItemData { - /** - * @param {!Tab|!RecentlyClosedTab} tab - * @param {!TabItemType} type - */ - constructor(tab, type) { + tab: Tab|RecentlyClosedTab; + hostname: string + + constructor(tab: Tab|RecentlyClosedTab, type: TabItemType) { super(); - /** @type {!Tab|!RecentlyClosedTab} */ this.tab = tab; - this.type = type; - - /** @type {string} */ - this.hostname; } } export class TabGroupData extends ItemData { - /** @param {!TabGroup|!RecentlyClosedTabGroup} tabGroup */ - constructor(tabGroup) { + constructor(tabGroup: TabGroup|RecentlyClosedTabGroup) { super(); this.tabGroup = tabGroup; this.type = TabItemType.RECENTLY_CLOSED_TAB_GROUP; @@ -67,32 +48,19 @@ /** * Converts a token to a string by combining the high and low values as strings * with a hashtag as the separator. - * @param {!Token} token - * @return {string} */ -export function tokenToString(token) { +export function tokenToString(token: Token): string { return `${token.high.toString()}#${token.low.toString()}`; } -/** - * @param {!Token} a - * @param {!Token} b - * @returns {boolean} - */ -export function tokenEquals(a, b) { +export function tokenEquals(a: Token, b: Token): boolean { return a.high === b.high && a.low === b.low; } -/** - * @param {!ItemData} itemData - * @return {string} - * @throws {Error} - */ -export function ariaLabel(itemData) { +export function ariaLabel(itemData: ItemData): string { if (itemData instanceof TabGroupData && itemData.type === TabItemType.RECENTLY_CLOSED_TAB_GROUP) { - const tabGroup = - /** @type {!RecentlyClosedTabGroup} */ (itemData.tabGroup); + const tabGroup = itemData.tabGroup as RecentlyClosedTabGroup; const tabCountText = loadTimeData.getStringF( tabGroup.tabCount == 1 ? 'oneTab' : 'tabCount', tabGroup.tabCount); return `${tabGroup.title} ${tabCountText} ${ @@ -100,7 +68,7 @@ } if (itemData instanceof TabData) { - const tabData = /** @type {TabData} */ (itemData); + const tabData = itemData; const groupTitleOrEmpty = tabData.tabGroup ? tabData.tabGroup.title : ''; return `${tabData.tab.title} ${groupTitleOrEmpty} ${tabData.hostname} ${ tabData.tab.lastActiveElapsedText} ${tabData.a11yTypeText}`;
diff --git a/chrome/browser/resources/tab_search/tab_group_color_helper.js b/chrome/browser/resources/tab_search/tab_group_color_helper.ts similarity index 73% rename from chrome/browser/resources/tab_search/tab_group_color_helper.js rename to chrome/browser/resources/tab_search/tab_group_color_helper.ts index a880bd6a..990aa577 100644 --- a/chrome/browser/resources/tab_search/tab_group_color_helper.js +++ b/chrome/browser/resources/tab_search/tab_group_color_helper.ts
@@ -6,8 +6,7 @@ import {Color} from './tab_group_types.mojom-webui.js'; -/** @type Map<Color, string> */ -const colorMap = new Map([ +const colorMap = new Map<Color, string>([ [Color.kGrey, 'grey'], [Color.kBlue, 'blue'], [Color.kRed, 'red'], @@ -18,15 +17,10 @@ [Color.kCyan, 'cyan'], ]); -/** - * @param {Color} color - * @return {string} - * @throws {Error} - */ -export function colorName(color) { +export function colorName(color: Color): string { if (!colorMap.has(color)) { throw Error('Undefined color id'); } - return colorMap.get(color); + return colorMap.get(color)!; }
diff --git a/chrome/browser/resources/tab_search/tab_group_shared_vars.js b/chrome/browser/resources/tab_search/tab_group_shared_vars.ts similarity index 100% rename from chrome/browser/resources/tab_search/tab_group_shared_vars.js rename to chrome/browser/resources/tab_search/tab_group_shared_vars.ts
diff --git a/chrome/browser/resources/tab_search/tab_search.js b/chrome/browser/resources/tab_search/tab_search.ts similarity index 100% rename from chrome/browser/resources/tab_search/tab_search.js rename to chrome/browser/resources/tab_search/tab_search.ts
diff --git a/chrome/browser/resources/tab_search/tab_search_api_proxy.js b/chrome/browser/resources/tab_search/tab_search_api_proxy.js deleted file mode 100644 index 468e409..0000000 --- a/chrome/browser/resources/tab_search/tab_search_api_proxy.js +++ /dev/null
@@ -1,141 +0,0 @@ -// Copyright 2020 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import {addSingletonGetter} from 'chrome://resources/js/cr.m.js'; - -import {PageCallbackRouter, PageHandlerFactory, PageHandlerRemote, ProfileData, SwitchToTabInfo} from './tab_search.mojom-webui.js'; - -/** - * These values are persisted to logs and should not be renumbered or re-used. - * See tools/metrics/histograms/enums.xml. - * @enum {number} - */ -export const RecentlyClosedItemOpenAction = { - WITHOUT_SEARCH: 0, - WITH_SEARCH: 1, -}; - -/** - * These values are persisted to logs and should not be renumbered or re-used. - * See tools/metrics/histograms/enums.xml. - * @enum {number} - */ -export const TabSwitchAction = { - WITHOUT_SEARCH : 0, - WITH_SEARCH : 1, -}; - -/** @interface */ -export class TabSearchApiProxy { - /** - * @param {number} tabId - * @param {boolean} withSearch - * @param {number} closedTabIndex - */ - closeTab(tabId, withSearch, closedTabIndex) {} - - /** @return {Promise<{profileData: ProfileData}>} */ - getProfileData() {} - - /** - * @param {number} id - * @param {boolean} withSearch - * @param {boolean} isTab - * @param {number} index - */ - openRecentlyClosedEntry(id, withSearch, isTab, index) {} - - /** - * @param {!SwitchToTabInfo} info - * @param {boolean} withSearch - * @param {number} switchedTabIndex - */ - switchToTab(info, withSearch, switchedTabIndex) {} - - /** @return {!PageCallbackRouter} */ - getCallbackRouter() {} - - /** @param {boolean} expanded */ - saveRecentlyClosedExpandedPref(expanded) {} - - showUI() {} -} - -/** @implements {TabSearchApiProxy} */ -export class TabSearchApiProxyImpl { - constructor() { - /** @type {!PageCallbackRouter} */ - this.callbackRouter = new PageCallbackRouter(); - - /** @type {!PageHandlerRemote} */ - this.handler = new PageHandlerRemote(); - - const factory = PageHandlerFactory.getRemote(); - factory.createPageHandler( - this.callbackRouter.$.bindNewPipeAndPassRemote(), - this.handler.$.bindNewPipeAndPassReceiver()); - } - - /** @override */ - closeTab(tabId, withSearch, closedTabIndex) { - chrome.metricsPrivate.recordSmallCount( - withSearch ? 'Tabs.TabSearch.WebUI.IndexOfCloseTabInFilteredList' : - 'Tabs.TabSearch.WebUI.IndexOfCloseTabInUnfilteredList', - closedTabIndex); - this.handler.closeTab(tabId); - } - - /** @override */ - getProfileData() { - return this.handler.getProfileData(); - } - - /** @override */ - openRecentlyClosedEntry(id, withSearch, isTab, index) { - chrome.metricsPrivate.recordEnumerationValue( - isTab ? 'Tabs.TabSearch.WebUI.RecentlyClosedTabOpenAction' : - 'Tabs.TabSearch.WebUI.RecentlyClosedGroupOpenAction', - withSearch ? RecentlyClosedItemOpenAction.WITH_SEARCH : - RecentlyClosedItemOpenAction.WITHOUT_SEARCH, - Object.keys(RecentlyClosedItemOpenAction).length); - chrome.metricsPrivate.recordSmallCount( - withSearch ? - 'Tabs.TabSearch.WebUI.IndexOfOpenRecentlyClosedEntryInFilteredList' : - 'Tabs.TabSearch.WebUI.IndexOfOpenRecentlyClosedEntryInUnfilteredList', - index); - this.handler.openRecentlyClosedEntry(id); - } - - /** @override */ - switchToTab(info, withSearch, switchedTabIndex) { - chrome.metricsPrivate.recordEnumerationValue( - 'Tabs.TabSearch.WebUI.TabSwitchAction', - withSearch ? TabSwitchAction.WITH_SEARCH - : TabSwitchAction.WITHOUT_SEARCH, - Object.keys(TabSwitchAction).length); - chrome.metricsPrivate.recordSmallCount( - withSearch ? 'Tabs.TabSearch.WebUI.IndexOfSwitchTabInFilteredList' : - 'Tabs.TabSearch.WebUI.IndexOfSwitchTabInUnfilteredList', - switchedTabIndex); - - this.handler.switchToTab(info); - } - - /** @override */ - getCallbackRouter() { - return this.callbackRouter; - } - - /** @override */ - saveRecentlyClosedExpandedPref(expanded) { - this.handler.saveRecentlyClosedExpandedPref(expanded); - } - - /** @override */ - showUI() { - this.handler.showUI(); - } -} - -addSingletonGetter(TabSearchApiProxyImpl);
diff --git a/chrome/browser/resources/tab_search/tab_search_api_proxy.ts b/chrome/browser/resources/tab_search/tab_search_api_proxy.ts new file mode 100644 index 0000000..bd648b9 --- /dev/null +++ b/chrome/browser/resources/tab_search/tab_search_api_proxy.ts
@@ -0,0 +1,119 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import {PageCallbackRouter, PageHandlerFactory, PageHandlerRemote, ProfileData, SwitchToTabInfo} from './tab_search.mojom-webui.js'; + +/** + * These values are persisted to logs and should not be renumbered or re-used. + * See tools/metrics/histograms/enums.xml. + */ +export enum RecentlyClosedItemOpenAction { + WITHOUT_SEARCH = 0, + WITH_SEARCH = 1, +} + +/** + * These values are persisted to logs and should not be renumbered or re-used. + * See tools/metrics/histograms/enums.xml. + */ +export enum TabSwitchAction { + WITHOUT_SEARCH = 0, + WITH_SEARCH = 1, +} + +export interface TabSearchApiProxy { + closeTab(tabId: number, withSearch: boolean, closedTabIndex: number): void; + + getProfileData(): Promise<{profileData: ProfileData}>; + + openRecentlyClosedEntry( + id: number, withSearch: boolean, isTab: boolean, index: number): void; + + switchToTab( + info: SwitchToTabInfo, withSearch: boolean, + switchedTabIndex: number): void; + + getCallbackRouter(): PageCallbackRouter; + + saveRecentlyClosedExpandedPref(expanded: boolean): void; + + showUI(): void; +} + +export class TabSearchApiProxyImpl implements TabSearchApiProxy { + callbackRouter: PageCallbackRouter = new PageCallbackRouter(); + handler: PageHandlerRemote = new PageHandlerRemote(); + + constructor() { + const factory = PageHandlerFactory.getRemote(); + factory.createPageHandler( + this.callbackRouter.$.bindNewPipeAndPassRemote(), + this.handler.$.bindNewPipeAndPassReceiver()); + } + + closeTab(tabId: number, withSearch: boolean, closedTabIndex: number) { + chrome.metricsPrivate.recordSmallCount( + withSearch ? 'Tabs.TabSearch.WebUI.IndexOfCloseTabInFilteredList' : + 'Tabs.TabSearch.WebUI.IndexOfCloseTabInUnfilteredList', + closedTabIndex); + this.handler.closeTab(tabId); + } + + getProfileData() { + return this.handler.getProfileData(); + } + + openRecentlyClosedEntry( + id: number, withSearch: boolean, isTab: boolean, index: number) { + chrome.metricsPrivate.recordEnumerationValue( + isTab ? 'Tabs.TabSearch.WebUI.RecentlyClosedTabOpenAction' : + 'Tabs.TabSearch.WebUI.RecentlyClosedGroupOpenAction', + withSearch ? RecentlyClosedItemOpenAction.WITH_SEARCH : + RecentlyClosedItemOpenAction.WITHOUT_SEARCH, + Object.keys(RecentlyClosedItemOpenAction).length); + chrome.metricsPrivate.recordSmallCount( + withSearch ? + 'Tabs.TabSearch.WebUI.IndexOfOpenRecentlyClosedEntryInFilteredList' : + 'Tabs.TabSearch.WebUI.IndexOfOpenRecentlyClosedEntryInUnfilteredList', + index); + this.handler.openRecentlyClosedEntry(id); + } + + switchToTab( + info: SwitchToTabInfo, withSearch: boolean, switchedTabIndex: number) { + chrome.metricsPrivate.recordEnumerationValue( + 'Tabs.TabSearch.WebUI.TabSwitchAction', + withSearch ? TabSwitchAction.WITH_SEARCH : + TabSwitchAction.WITHOUT_SEARCH, + Object.keys(TabSwitchAction).length); + chrome.metricsPrivate.recordSmallCount( + withSearch ? 'Tabs.TabSearch.WebUI.IndexOfSwitchTabInFilteredList' : + 'Tabs.TabSearch.WebUI.IndexOfSwitchTabInUnfilteredList', + switchedTabIndex); + + this.handler.switchToTab(info); + } + + getCallbackRouter() { + return this.callbackRouter; + } + + saveRecentlyClosedExpandedPref(expanded: boolean) { + this.handler.saveRecentlyClosedExpandedPref(expanded); + } + + showUI() { + this.handler.showUI(); + } + + static getInstance(): TabSearchApiProxy { + return instance || (instance = new TabSearchApiProxyImpl()); + } + + static setInstance(obj: TabSearchApiProxy) { + instance = obj; + } +} + +let instance: TabSearchApiProxy|null = null;
diff --git a/chrome/browser/resources/tab_search/tab_search_utils.js b/chrome/browser/resources/tab_search/tab_search_utils.ts similarity index 66% rename from chrome/browser/resources/tab_search/tab_search_utils.js rename to chrome/browser/resources/tab_search/tab_search_utils.ts index a00ed19..edd04fb 100644 --- a/chrome/browser/resources/tab_search/tab_search_utils.js +++ b/chrome/browser/resources/tab_search/tab_search_utils.ts
@@ -4,13 +4,13 @@ import {highlight} from 'chrome://resources/js/search_highlight_utils.js'; -/** - * @param {!HTMLElement} container - * @param {string} text - * @param {!Array<!{start:number, length:number}>|undefined} ranges - * @private - */ -export function highlightText(container, text, ranges) { +type Range = { + start: number, + length: number +}; + +export function highlightText( + container: HTMLElement, text: string, ranges: Range[]|undefined) { container.textContent = ''; const node = document.createTextNode(text); container.appendChild(node);
diff --git a/chrome/browser/resources/tab_search/title_item.js b/chrome/browser/resources/tab_search/title_item.js deleted file mode 100644 index cc34631..0000000 --- a/chrome/browser/resources/tab_search/title_item.js +++ /dev/null
@@ -1,21 +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. - -export class TitleItem { - /** - * @param {string} title - * @param {boolean} expandable - * @param {boolean} expanded - */ - constructor(title, expandable = false, expanded = false) { - /** @type {string} */ - this.title = title; - - /** @type {boolean} */ - this.expandable = expandable; - - /** @type {boolean} */ - this.expanded = expanded; - } -}
diff --git a/chrome/browser/resources/tab_search/title_item.ts b/chrome/browser/resources/tab_search/title_item.ts new file mode 100644 index 0000000..9b0dcdb --- /dev/null +++ b/chrome/browser/resources/tab_search/title_item.ts
@@ -0,0 +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. + +export class TitleItem { + title: string; + expandable: boolean; + expanded: boolean; + + constructor( + title: string, expandable: boolean = false, expanded: boolean = false) { + this.title = title; + this.expandable = expandable; + this.expanded = expanded; + } +}
diff --git a/chrome/browser/safe_browsing/chrome_password_protection_service_unittest.cc b/chrome/browser/safe_browsing/chrome_password_protection_service_unittest.cc index 620cf0179..9b767db 100644 --- a/chrome/browser/safe_browsing/chrome_password_protection_service_unittest.cc +++ b/chrome/browser/safe_browsing/chrome_password_protection_service_unittest.cc
@@ -500,8 +500,16 @@ reused_password_type)); } +// TODO(crbug.com/1276906): Flaky on Linux TSan Tests. +#if defined(THREAD_SANITIZER) +#define MAYBE_VerifyUserPopulationForSyncPasswordEntryPing \ + DISABLED_VerifyUserPopulationForSyncPasswordEntryPing +#else +#define MAYBE_VerifyUserPopulationForSyncPasswordEntryPing \ + VerifyUserPopulationForSyncPasswordEntryPing +#endif TEST_F(ChromePasswordProtectionServiceTest, - VerifyUserPopulationForSyncPasswordEntryPing) { + MAYBE_VerifyUserPopulationForSyncPasswordEntryPing) { // Sets up the account as a gmail account as there is no hosted domain. ReusedPasswordAccountType reused_password_type; reused_password_type.set_account_type(ReusedPasswordAccountType::GMAIL);
diff --git a/chrome/browser/signin/account_consistency_mode_manager.cc b/chrome/browser/signin/account_consistency_mode_manager.cc index 0093a0e..6c1f0f0 100644 --- a/chrome/browser/signin/account_consistency_mode_manager.cc +++ b/chrome/browser/signin/account_consistency_mode_manager.cc
@@ -92,12 +92,7 @@ DCHECK(profile_); DCHECK(ShouldBuildServiceForProfile(profile)); -#if BUILDFLAG(IS_CHROMEOS_LACROS) - // Lacros doesn't support account inconsistency. - // TODO(crbug.com/1220066): Remove this section when Lacros stops building - // with DICE. - profile->GetPrefs()->SetBoolean(prefs::kSigninAllowed, true); -#elif BUILDFLAG(ENABLE_DICE_SUPPORT) +#if BUILDFLAG(ENABLE_DICE_SUPPORT) PrefService* prefs = profile->GetPrefs(); // Propagate settings changes from the previous launch to the signin-allowed // pref. @@ -188,17 +183,10 @@ #endif #if BUILDFLAG(IS_CHROMEOS_LACROS) - bool is_account_manager_available = true; // Account consistency is unavailable on Managed Guest Sessions and Public // Sessions. if (profiles::IsPublicSession()) - is_account_manager_available = false; - - if (is_account_manager_available) - return AccountConsistencyMethod::kMirror; - // else: Fall through to ENABLE_DICE_SUPPORT section below. - // TODO(crbug.com/1198490): Return `AccountConsistencyMethod::kDisabled` if - // AccountManager is not available, when DICE has been disabled on Lacros. + return AccountConsistencyMethod::kDisabled; #endif #if BUILDFLAG(ENABLE_MIRROR)
diff --git a/chrome/browser/signin/account_consistency_mode_manager_unittest.cc b/chrome/browser/signin/account_consistency_mode_manager_unittest.cc index 53912100..d0a4ee9 100644 --- a/chrome/browser/signin/account_consistency_mode_manager_unittest.cc +++ b/chrome/browser/signin/account_consistency_mode_manager_unittest.cc
@@ -13,7 +13,6 @@ #include "build/buildflag.h" #include "build/chromeos_buildflags.h" #include "chrome/browser/prefs/browser_prefs.h" -#include "chrome/browser/supervised_user/supervised_user_constants.h" #include "chrome/common/pref_names.h" #include "chrome/test/base/testing_profile.h" #include "components/prefs/pref_notifier_impl.h" @@ -44,7 +43,7 @@ BuildTestingProfile(/*is_new_profile=*/false); signin::AccountConsistencyMethod method = -#if BUILDFLAG(ENABLE_MIRROR) || BUILDFLAG(IS_CHROMEOS_LACROS) +#if BUILDFLAG(ENABLE_MIRROR) signin::AccountConsistencyMethod::kMirror; #elif BUILDFLAG(ENABLE_DICE_SUPPORT) signin::AccountConsistencyMethod::kDice; @@ -62,9 +61,7 @@ AccountConsistencyModeManager::IsDiceEnabledForProfile(profile.get())); } -// TODO(crbug.com/1220066): Remove the lacros exclusion when DICE is disabled on -// Lacros. -#if BUILDFLAG(ENABLE_DICE_SUPPORT) && !BUILDFLAG(IS_CHROMEOS_LACROS) +#if BUILDFLAG(ENABLE_DICE_SUPPORT) // Checks that changing the signin-allowed pref changes the Dice state on next // startup. TEST(AccountConsistencyModeManagerTest, SigninAllowedChangesDiceState) { @@ -202,9 +199,9 @@ profile.get())); } } -#endif // BUILDFLAG(ENABLE_DICE_SUPPORT) && !BUILDFLAG(IS_CHROMEOS_LACROS) +#endif // BUILDFLAG(ENABLE_DICE_SUPPORT) -#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS) +#if BUILDFLAG(ENABLE_MIRROR) // Mirror is enabled by default on Chrome OS, unless specified otherwise. TEST(AccountConsistencyModeManagerTest, MirrorEnabledByDefault) { // Creation of this object sets the current thread's id as UI thread. @@ -258,19 +255,5 @@ EXPECT_EQ(signin::AccountConsistencyMethod::kDisabled, AccountConsistencyModeManager::GetMethodForProfile(otr_profile)); } -#endif // BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS) -#if BUILDFLAG(ENABLE_MIRROR) -// Test that Mirror is enabled for child accounts. -TEST(AccountConsistencyModeManagerTest, MirrorChildAccount) { - content::BrowserTaskEnvironment task_environment; - TestingProfile profile; - profile.SetSupervisedUserId(supervised_users::kChildAccountSUID); - EXPECT_TRUE( - AccountConsistencyModeManager::IsMirrorEnabledForProfile(&profile)); - EXPECT_FALSE( - AccountConsistencyModeManager::IsDiceEnabledForProfile(&profile)); - EXPECT_EQ(signin::AccountConsistencyMethod::kMirror, - AccountConsistencyModeManager::GetMethodForProfile(&profile)); -} #endif // BUILDFLAG(ENABLE_MIRROR)
diff --git a/chrome/browser/signin/chrome_signin_helper.cc b/chrome/browser/signin/chrome_signin_helper.cc index 00a7ce5a..b85dde4 100644 --- a/chrome/browser/signin/chrome_signin_helper.cc +++ b/chrome/browser/signin/chrome_signin_helper.cc
@@ -207,14 +207,13 @@ class ManageAccountsHeaderReceivedUserData : public base::SupportsUserData::Data {}; +#if BUILDFLAG(ENABLE_MIRROR) // Processes the mirror response header on the UI thread. Currently depending // on the value of |header_value|, it either shows the profile avatar menu, or // opens an incognito window/tab. void ProcessMirrorHeader( ManageAccountsParams manage_accounts_params, const content::WebContents::Getter& web_contents_getter) { -#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS) || \ - defined(OS_ANDROID) DCHECK_CURRENTLY_ON(content::BrowserThread::UI); GAIAServiceType service_type = manage_accounts_params.service_type; @@ -232,8 +231,6 @@ AccountReconcilor* account_reconcilor = AccountReconcilorFactory::GetForProfile(profile); account_reconcilor->OnReceivedManageAccountsResponse(service_type); -#endif // BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS) || - // defined(OS_ANDROID) #if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS) signin_metrics::LogAccountReconcilorStateOnGaiaResponse( @@ -392,6 +389,7 @@ } #endif // BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS) } +#endif // BUILDFLAG(ENABLE_MIRROR) #if BUILDFLAG(ENABLE_DICE_SUPPORT) @@ -452,6 +450,7 @@ } #endif // BUILDFLAG(ENABLE_DICE_SUPPORT) +#if BUILDFLAG(ENABLE_MIRROR) // Looks for the X-Chrome-Manage-Accounts response header, and if found, // tries to show the avatar bubble in the browser identified by the // child/route id. Must be called on IO thread. @@ -502,6 +501,7 @@ FROM_HERE, base::BindOnce(ProcessMirrorHeader, params, response->GetWebContentsGetter())); } +#endif #if BUILDFLAG(ENABLE_DICE_SUPPORT) void ProcessDiceResponseHeaderIfExists(ResponseAdapter* response, @@ -635,6 +635,10 @@ if (is_off_the_record) return; // Account consistency is disabled in incognito. + // If new url is eligible to have the header, add it, otherwise remove it. + +// Mirror header: +#if BUILDFLAG(ENABLE_MIRROR) int profile_mode_mask = PROFILE_MODE_DEFAULT; if (incognito_availibility == static_cast<int>(IncognitoModePrefs::Availability::kDisabled) || @@ -650,10 +654,14 @@ } #endif - // If new url is eligible to have the header, add it, otherwise remove it. + AppendOrRemoveMirrorRequestHeader( + request, redirect_url, gaia_id, is_child_account, account_consistency, + cookie_settings, profile_mode_mask, kChromeMirrorHeaderSource, + /*force_account_consistency=*/false); +#endif // BUILDFLAG(ENABLE_MIRROR) +// Dice header: #if BUILDFLAG(ENABLE_DICE_SUPPORT) - // Dice header: bool dice_header_added = AppendOrRemoveDiceRequestHeader( request, redirect_url, gaia_id, is_sync_enabled, account_consistency, cookie_settings, signin_scoped_device_id); @@ -669,12 +677,6 @@ &AccountReconcilorLockWrapper::DestroyAfterDelay, lock_wrapper)); } #endif - - // Mirror header: - AppendOrRemoveMirrorRequestHeader( - request, redirect_url, gaia_id, is_child_account, account_consistency, - cookie_settings, profile_mode_mask, kChromeMirrorHeaderSource, - /*force_account_consistency=*/false); } void ProcessAccountConsistencyResponseHeaders(ResponseAdapter* response, @@ -683,10 +685,12 @@ if (!gaia::IsGaiaSignonRealm(response->GetOrigin())) return; +#if BUILDFLAG(ENABLE_MIRROR) // See if the response contains the X-Chrome-Manage-Accounts header. If so // show the profile avatar bubble so that user can complete signin/out // action the native UI. ProcessMirrorResponseHeaderIfExists(response, is_off_the_record); +#endif #if BUILDFLAG(ENABLE_DICE_SUPPORT) // Process the Dice header: on sign-in, exchange the authorization code for a
diff --git a/chrome/browser/signin/signin_ui_util_unittest.cc b/chrome/browser/signin/signin_ui_util_unittest.cc index 59397971..43be4319f 100644 --- a/chrome/browser/signin/signin_ui_util_unittest.cc +++ b/chrome/browser/signin/signin_ui_util_unittest.cc
@@ -71,9 +71,7 @@ EXPECT_EQ("example-1.com", GetAllowedDomain("email@example-1.com")); } -// TODO(https://crbug.com/1198523: Remove Lacros check once Dice is no longer -// supported on Lacros. -#if BUILDFLAG(ENABLE_DICE_SUPPORT) && !BUILDFLAG(IS_CHROMEOS_LACROS) +#if BUILDFLAG(ENABLE_DICE_SUPPORT) namespace {
diff --git a/chrome/browser/signin/signin_util_unittest.cc b/chrome/browser/signin/signin_util_unittest.cc index ca18cbbb..8086257 100644 --- a/chrome/browser/signin/signin_util_unittest.cc +++ b/chrome/browser/signin/signin_util_unittest.cc
@@ -7,6 +7,8 @@ #include <memory> #include "base/feature_list.h" +#include "build/buildflag.h" +#include "build/chromeos_buildflags.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/prefs/browser_prefs.h" #include "chrome/browser/signin/signin_features.h" @@ -43,6 +45,7 @@ EXPECT_FALSE(signin_util::IsForceSigninEnabled()); } +#if !BUILDFLAG(IS_CHROMEOS_LACROS) class SigninUtilEnterpriseTest : public BrowserWithTestWindowTest { public: SigninUtilEnterpriseTest() @@ -121,3 +124,4 @@ EXPECT_TRUE(signin_util::ProfileSeparationEnforcedByPolicy( profile.get(), "primary_account_strict")); } +#endif
diff --git a/chrome/browser/supervised_user/supervised_user_model_type_controller_unittest.cc b/chrome/browser/supervised_user/supervised_user_model_type_controller_unittest.cc index d2abed3..74f931f 100644 --- a/chrome/browser/supervised_user/supervised_user_model_type_controller_unittest.cc +++ b/chrome/browser/supervised_user/supervised_user_model_type_controller_unittest.cc
@@ -25,7 +25,7 @@ TestingProfile::Builder builder; builder.SetSupervisedUserId(supervised_users::kChildAccountSUID); std::unique_ptr<Profile> child_profile = builder.Build(); - ASSERT_TRUE(child_profile->IsSupervised()); + ASSERT_TRUE(child_profile->IsChild()); SupervisedUserSyncModelTypeController controller( syncer::SUPERVISED_USER_SETTINGS, child_profile.get(), @@ -40,7 +40,7 @@ NonSupervisedUserDoesNotMeetPreconditions) { TestingProfile::Builder builder; std::unique_ptr<Profile> non_child_profile = builder.Build(); - ASSERT_FALSE(non_child_profile->IsSupervised()); + ASSERT_FALSE(non_child_profile->IsChild()); SupervisedUserSyncModelTypeController controller( syncer::SUPERVISED_USER_SETTINGS, non_child_profile.get(), @@ -55,7 +55,7 @@ TestingProfile::Builder builder; builder.SetSupervisedUserId(supervised_users::kChildAccountSUID); std::unique_ptr<Profile> child_profile = builder.Build(); - ASSERT_TRUE(child_profile->IsSupervised()); + ASSERT_TRUE(child_profile->IsChild()); SupervisedUserSyncModelTypeController controller( syncer::SUPERVISED_USER_SETTINGS, child_profile.get(),
diff --git a/chrome/browser/supervised_user/supervised_user_navigation_throttle.cc b/chrome/browser/supervised_user/supervised_user_navigation_throttle.cc index cdb29bb8..cdca509 100644 --- a/chrome/browser/supervised_user/supervised_user_navigation_throttle.cc +++ b/chrome/browser/supervised_user/supervised_user_navigation_throttle.cc
@@ -125,7 +125,7 @@ Profile* profile = Profile::FromBrowserContext( navigation_handle->GetWebContents()->GetBrowserContext()); - if (!profile->IsSupervised()) + if (!profile->IsChild()) return nullptr; // Can't use std::make_unique because the constructor is private.
diff --git a/chrome/browser/supervised_user/supervised_user_service.h b/chrome/browser/supervised_user/supervised_user_service.h index 2f1752c..4eb1580 100644 --- a/chrome/browser/supervised_user/supervised_user_service.h +++ b/chrome/browser/supervised_user/supervised_user_service.h
@@ -170,8 +170,8 @@ static std::string GetEduCoexistenceLoginUrl(); - // Returns true if the user is a type of Family Link Child account, - // but will not return true for a Legacy Supervised user (or non child users). + // Returns true if the user is a type of Family Link supervised account, this + // includes Unicorn, Geller, and Griffin accounts. bool IsChild() const; bool IsSupervisedUserExtensionInstallEnabled() const;
diff --git a/chrome/browser/supervised_user/supervised_user_service_unittest.cc b/chrome/browser/supervised_user/supervised_user_service_unittest.cc index 00fc6bf..8b63a0c 100644 --- a/chrome/browser/supervised_user/supervised_user_service_unittest.cc +++ b/chrome/browser/supervised_user/supervised_user_service_unittest.cc
@@ -223,7 +223,7 @@ ->SetSupervisedUserExtensionsMayRequestPermissionsPrefForTesting(false); EXPECT_FALSE(supervised_user_service ->GetSupervisedUserExtensionsMayRequestPermissionsPref()); - EXPECT_TRUE(profile_->IsSupervised()); + EXPECT_TRUE(profile_->IsChild()); // Check that a supervised user can install and uninstall a theme even if // they are not allowed to install extensions. @@ -276,7 +276,7 @@ ->SetSupervisedUserExtensionsMayRequestPermissionsPrefForTesting(true); EXPECT_TRUE(supervised_user_service ->GetSupervisedUserExtensionsMayRequestPermissionsPref()); - EXPECT_TRUE(profile_->IsSupervised()); + EXPECT_TRUE(profile_->IsChild()); // The supervised user should be able to load and uninstall the extensions // they install.
diff --git a/chrome/browser/supervised_user/supervised_user_sync_model_type_controller.cc b/chrome/browser/supervised_user/supervised_user_sync_model_type_controller.cc index 93c1c3f..1680161 100644 --- a/chrome/browser/supervised_user/supervised_user_sync_model_type_controller.cc +++ b/chrome/browser/supervised_user/supervised_user_sync_model_type_controller.cc
@@ -30,6 +30,6 @@ syncer::DataTypeController::PreconditionState SupervisedUserSyncModelTypeController::GetPreconditionState() const { DCHECK(CalledOnValidThread()); - return profile_->IsSupervised() ? PreconditionState::kPreconditionsMet - : PreconditionState::kMustStopAndClearData; + return profile_->IsChild() ? PreconditionState::kPreconditionsMet + : PreconditionState::kMustStopAndClearData; }
diff --git a/chrome/browser/supervised_user/supervised_user_test_util.cc b/chrome/browser/supervised_user/supervised_user_test_util.cc index 75bd56ef..bd3b3ec2 100644 --- a/chrome/browser/supervised_user/supervised_user_test_util.cc +++ b/chrome/browser/supervised_user/supervised_user_test_util.cc
@@ -11,7 +11,7 @@ namespace supervised_user_test_util { void AddCustodians(Profile* profile) { - DCHECK(profile->IsSupervised()); + DCHECK(profile->IsChild()); PrefService* prefs = profile->GetPrefs(); prefs->SetString(prefs::kSupervisedUserCustodianEmail, "test_parent_0@google.com");
diff --git a/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/TabState.java b/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/TabState.java index 00654c5..644180e6f 100644 --- a/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/TabState.java +++ b/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/TabState.java
@@ -39,9 +39,6 @@ /** Whether this TabState was created from a file containing info about an incognito Tab. */ public boolean isIncognito; - /** Tab level Request Desktop Site setting. */ - public @TabUserAgent int userAgent; - public boolean isIncognito() { return isIncognito; }
diff --git a/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/state/CriticalPersistedTabData.java b/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/state/CriticalPersistedTabData.java index 6c03652..01f0f01 100644 --- a/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/state/CriticalPersistedTabData.java +++ b/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/state/CriticalPersistedTabData.java
@@ -20,7 +20,6 @@ import org.chromium.base.supplier.Supplier; import org.chromium.chrome.browser.tab.Tab; import org.chromium.chrome.browser.tab.TabLaunchType; -import org.chromium.chrome.browser.tab.TabUserAgent; import org.chromium.chrome.browser.tab.WebContentsState; import org.chromium.chrome.browser.tab.WebContentsStateBridge; import org.chromium.chrome.browser.tab.proto.CriticalPersistedTabData.CriticalPersistedTabDataProto; @@ -76,8 +75,6 @@ private ObserverList<CriticalPersistedTabDataObserver> mObservers = new ObserverList<CriticalPersistedTabDataObserver>(); private boolean mShouldSaveForTesting; - /** Tab level Request Desktop Site setting. */ - private @Nullable @TabUserAgent Integer mUserAgent; @VisibleForTesting protected CriticalPersistedTabData(Tab tab) { @@ -93,17 +90,19 @@ * @param parentId parent identiifer for the {@link Tab} * @param rootId root identifier for the {@link Tab} * @param timestampMillis creation timestamp for the {@link Tab} + * @param contentStateBytes content state bytes for the {@link Tab} * @param contentStateVersion content state version for the {@link Tab} * @param openerAppId identifier for app opener * @param themeColor theme color * @param launchTypeAtCreation launch type at creation - * @param userAgent user agent for the {@link Tab} + * @param persistedTabDataStorage storage for {@link PersistedTabData} + * @param persistedTabDataId identifier for {@link PersistedTabData} in storage */ @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) CriticalPersistedTabData(Tab tab, String url, String title, int parentId, int rootId, long timestampMillis, WebContentsState webContentsState, int contentStateVersion, String openerAppId, int themeColor, - @Nullable @TabLaunchType Integer launchTypeAtCreation, @TabUserAgent int userAgent) { + @Nullable @TabLaunchType Integer launchTypeAtCreation) { this(tab); mUrl = url == null || url.isEmpty() ? GURL.emptyGURL() : new GURL(url); mTitle = title; @@ -115,7 +114,6 @@ mOpenerAppId = openerAppId; mThemeColor = themeColor; mTabLaunchTypeAtCreation = launchTypeAtCreation; - mUserAgent = userAgent; } /** @@ -204,9 +202,9 @@ @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) public static CriticalPersistedTabData build(Tab tab) { // CriticalPersistedTabData is initialized with default values - CriticalPersistedTabData criticalPersistedTabData = new CriticalPersistedTabData(tab, "", - "", Tab.INVALID_TAB_ID, tab.getId(), INVALID_TIMESTAMP, null, -1, "", - UNSPECIFIED_THEME_COLOR, null, TabUserAgent.DEFAULT); + CriticalPersistedTabData criticalPersistedTabData = + new CriticalPersistedTabData(tab, "", "", Tab.INVALID_TAB_ID, tab.getId(), + INVALID_TIMESTAMP, null, -1, "", UNSPECIFIED_THEME_COLOR, null); criticalPersistedTabData.save(); return criticalPersistedTabData; } @@ -236,11 +234,6 @@ mThemeColor = criticalPersistedTabDataProto.getThemeColor(); mTabLaunchTypeAtCreation = getLaunchType(criticalPersistedTabDataProto.getLaunchTypeAtCreation()); - if (criticalPersistedTabDataProto.hasUserAgent()) { - mUserAgent = getUserAgentType(criticalPersistedTabDataProto.getUserAgent()); - } else { - mUserAgent = TabUserAgent.UNSET; - } return true; } catch (InvalidProtocolBufferException e) { Log.e(TAG, @@ -354,53 +347,6 @@ } } - @VisibleForTesting - static @Nullable @TabUserAgent Integer getUserAgentType( - CriticalPersistedTabDataProto.UserAgentType protoUserAgent) { - switch (protoUserAgent) { - case DEFAULT: - return TabUserAgent.DEFAULT; - case MOBILE: - return TabUserAgent.MOBILE; - case DESKTOP: - return TabUserAgent.DESKTOP; - case UNSET: - return TabUserAgent.UNSET; - case USER_AGENT_SIZE: - return TabUserAgent.SIZE; - default: - assert false : "Unexpected deserialization of UserAgentType: " + protoUserAgent; - // shouldn't happen - return null; - } - } - - @VisibleForTesting - static CriticalPersistedTabDataProto.UserAgentType getUserAgentType( - @Nullable @TabUserAgent Integer protoUserAgent) { - if (protoUserAgent == null) { - assert false : "TabUserAgent should never be null."; - // shouldn't happen - return CriticalPersistedTabDataProto.UserAgentType.USER_AGENT_UNKNOWN; - } - switch (protoUserAgent) { - case TabUserAgent.DEFAULT: - return CriticalPersistedTabDataProto.UserAgentType.DEFAULT; - case TabUserAgent.MOBILE: - return CriticalPersistedTabDataProto.UserAgentType.MOBILE; - case TabUserAgent.DESKTOP: - return CriticalPersistedTabDataProto.UserAgentType.DESKTOP; - case TabUserAgent.UNSET: - return CriticalPersistedTabDataProto.UserAgentType.UNSET; - case TabUserAgent.SIZE: - return CriticalPersistedTabDataProto.UserAgentType.USER_AGENT_SIZE; - default: - assert false : "Unexpected serialization of UserAgentType: " + protoUserAgent; - // shouldn't happen - return CriticalPersistedTabDataProto.UserAgentType.USER_AGENT_UNKNOWN; - } - } - private static WebContentsState getWebContentsStateFromTab(Tab tab) { // Native call returns null when buffer allocation needed to serialize the state failed. ByteBuffer buffer = getWebContentsStateAsByteBuffer(tab); @@ -444,8 +390,7 @@ .setContentStateVersion(mContentStateVersion) .setOpenerAppId(mOpenerAppId == null ? "" : mOpenerAppId) .setThemeColor(mThemeColor) - .setLaunchTypeAtCreation(getLaunchType(mTabLaunchTypeAtCreation)) - .setUserAgent(getUserAgentType(mUserAgent)); + .setLaunchTypeAtCreation(getLaunchType(mTabLaunchTypeAtCreation)); } return () -> { try (TraceEvent e = TraceEvent.scoped("CriticalPersistedTabData.Serialize")) { @@ -674,24 +619,6 @@ } /** - * @return user agent type for the {@link Tab} - */ - public @TabUserAgent int getUserAgent() { - return mUserAgent; - } - - /** - * Set user agent type for the {@link Tab} - */ - public void setUserAgent(@TabUserAgent int userAgent) { - if (mUserAgent == userAgent) { - return; - } - mUserAgent = userAgent; - save(); - } - - /** * Add a {@link CriticalPersistedTabDataObserver} * @param criticalPersistedTabDataObserver the observer */
diff --git a/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/state/proto/critical_persisted_tab_data.proto b/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/state/proto/critical_persisted_tab_data.proto index b4e5c24..1b3d2d0 100644 --- a/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/state/proto/critical_persisted_tab_data.proto +++ b/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/state/proto/critical_persisted_tab_data.proto
@@ -53,16 +53,4 @@ UNKNOWN = 17; } optional LaunchTypeAtCreation launch_type_at_creation = 8; - - // User agent. - // Cannot define two same enum name in the same proto file. - enum UserAgentType { - DEFAULT = 0; - MOBILE = 1; - DESKTOP = 2; - UNSET = 3; - USER_AGENT_SIZE = 4; - USER_AGENT_UNKNOWN = 5; - } - optional UserAgentType user_agent = 9; }
diff --git a/chrome/browser/tabpersistence/android/java/src/org/chromium/chrome/browser/tabpersistence/TabStateFileManager.java b/chrome/browser/tabpersistence/android/java/src/org/chromium/chrome/browser/tabpersistence/TabStateFileManager.java index fc5d605..719d6ec 100644 --- a/chrome/browser/tabpersistence/android/java/src/org/chromium/chrome/browser/tabpersistence/TabStateFileManager.java +++ b/chrome/browser/tabpersistence/android/java/src/org/chromium/chrome/browser/tabpersistence/TabStateFileManager.java
@@ -16,7 +16,6 @@ import org.chromium.chrome.browser.tab.Tab; import org.chromium.chrome.browser.tab.TabLaunchType; import org.chromium.chrome.browser.tab.TabState; -import org.chromium.chrome.browser.tab.TabUserAgent; import org.chromium.chrome.browser.tab.WebContentsState; import org.chromium.chrome.browser.version.ChromeVersionInfo; @@ -208,14 +207,6 @@ "Failed to read tab root id from tab state. " + "Assuming root id is Tab.INVALID_TAB_ID"); } - try { - tabState.userAgent = stream.readInt(); - } catch (EOFException eof) { - tabState.userAgent = TabUserAgent.UNSET; - Log.w(TAG, - "Failed to read tab user agent from tab state. " - + "Assuming user agent is TabUserAgent.UNSET"); - } return tabState; } finally { stream.close(); @@ -277,7 +268,6 @@ dataOutputStream.writeInt( state.tabLaunchTypeAtCreation != null ? state.tabLaunchTypeAtCreation : -1); dataOutputStream.writeInt(state.rootId); - dataOutputStream.writeInt(state.userAgent); RecordHistogram.recordTimesHistogram( "Tabs.TabState.SaveTime", SystemClock.elapsedRealtime() - startTime); } catch (FileNotFoundException e) {
diff --git a/chrome/browser/tabpersistence/android/java/src/org/chromium/chrome/browser/tabpersistence/TabStateFileManagerUnitTest.java b/chrome/browser/tabpersistence/android/java/src/org/chromium/chrome/browser/tabpersistence/TabStateFileManagerUnitTest.java index a35eef2e..7a87e63 100644 --- a/chrome/browser/tabpersistence/android/java/src/org/chromium/chrome/browser/tabpersistence/TabStateFileManagerUnitTest.java +++ b/chrome/browser/tabpersistence/android/java/src/org/chromium/chrome/browser/tabpersistence/TabStateFileManagerUnitTest.java
@@ -18,7 +18,6 @@ import org.chromium.base.test.BaseRobolectricTestRunner; import org.chromium.chrome.browser.tab.TabLaunchType; import org.chromium.chrome.browser.tab.TabState; -import org.chromium.chrome.browser.tab.TabUserAgent; import org.chromium.chrome.browser.tab.WebContentsState; import java.io.DataOutputStream; @@ -42,7 +41,6 @@ private static final String OPENER_APP_ID = "test"; private static final @Nullable @TabLaunchType Integer LAUNCH_TYPE_AT_CREATION = null; private static final int ROOT_ID = 1; - private static final @TabUserAgent int USER_AGENT = TabUserAgent.MOBILE; @Rule public TemporaryFolder temporaryFolder = new TemporaryFolder(); @@ -72,7 +70,6 @@ state.openerAppId = OPENER_APP_ID; state.tabLaunchTypeAtCreation = LAUNCH_TYPE_AT_CREATION; state.rootId = ROOT_ID; - state.userAgent = USER_AGENT; } finally { StreamUtil.closeQuietly(fileInputStream); } @@ -88,7 +85,6 @@ assertEquals(LAUNCH_TYPE_AT_CREATION, state.tabLaunchTypeAtCreation); assertEquals(ROOT_ID, state.rootId); assertEquals(CONTENTS_STATE_BYTES.length, state.contentsState.buffer().remaining()); - assertEquals(USER_AGENT, state.userAgent); byte[] bytesFromFile = new byte[CONTENTS_STATE_BYTES.length]; state.contentsState.buffer().get(bytesFromFile);
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn index 4e61a7e4..884e0de 100644 --- a/chrome/browser/ui/BUILD.gn +++ b/chrome/browser/ui/BUILD.gn
@@ -3214,8 +3214,6 @@ "webui/discards/site_data_provider_impl.h", "webui/signin/inline_login_handler.cc", "webui/signin/inline_login_handler.h", - "webui/signin/inline_login_ui.cc", - "webui/signin/inline_login_ui.h", "webui/signin/signin_web_dialog_ui.cc", "webui/signin/signin_web_dialog_ui.h", "webui/signin/sync_confirmation_handler.cc", @@ -3229,6 +3227,13 @@ ] } + if (is_win || is_mac || is_linux || is_chromeos_ash || is_fuchsia) { + sources += [ + "webui/signin/inline_login_ui.cc", + "webui/signin/inline_login_ui.h", + ] + } + if (enable_one_click_signin) { sources += [ "sync/one_click_signin_links_delegate.h", @@ -3252,14 +3257,17 @@ sources += [ "profile_picker.cc", "profile_picker.h", - "signin_reauth_view_controller.cc", - "signin_reauth_view_controller.h", + "signin/profile_customization_bubble_sync_controller.h", "startup/default_browser_infobar_delegate.cc", "startup/default_browser_infobar_delegate.h", "startup/default_browser_prompt.cc", "startup/default_browser_prompt.h", "views/profiles/badged_profile_photo.cc", "views/profiles/badged_profile_photo.h", + "views/profiles/profile_customization_bubble_sync_controller.cc", + "views/profiles/profile_customization_bubble_sync_controller.h", + "views/profiles/profile_customization_bubble_view.cc", + "views/profiles/profile_customization_bubble_view.h", "views/profiles/profile_menu_view.cc", "views/profiles/profile_menu_view.h", "views/profiles/profile_picker_force_signin_dialog_delegate.cc", @@ -3283,16 +3291,16 @@ "webui/settings/system_handler.h", "webui/signin/dice_turn_sync_on_helper.cc", "webui/signin/dice_turn_sync_on_helper.h", - "webui/signin/dice_turn_sync_on_helper_delegate_impl.cc", - "webui/signin/dice_turn_sync_on_helper_delegate_impl.h", "webui/signin/enterprise_profile_welcome_handler.cc", "webui/signin/enterprise_profile_welcome_handler.h", "webui/signin/enterprise_profile_welcome_ui.cc", "webui/signin/enterprise_profile_welcome_ui.h", - "webui/signin/inline_login_handler_impl.cc", - "webui/signin/inline_login_handler_impl.h", "webui/signin/profile_creation_customize_themes_handler.cc", "webui/signin/profile_creation_customize_themes_handler.h", + "webui/signin/profile_customization_handler.cc", + "webui/signin/profile_customization_handler.h", + "webui/signin/profile_customization_ui.cc", + "webui/signin/profile_customization_ui.h", "webui/signin/profile_picker_handler.cc", "webui/signin/profile_picker_handler.h", "webui/signin/profile_picker_ui.cc", @@ -3305,32 +3313,10 @@ "webui/signin/signin_error_handler.h", "webui/signin/signin_error_ui.cc", "webui/signin/signin_error_ui.h", - "webui/signin/signin_reauth_handler.cc", - "webui/signin/signin_reauth_handler.h", - "webui/signin/signin_reauth_ui.cc", - "webui/signin/signin_reauth_ui.h", "webui/signin/signin_ui_error.cc", "webui/signin/signin_ui_error.h", "webui/signin/signin_utils_desktop.cc", "webui/signin/signin_utils_desktop.h", - "webui/welcome/bookmark_handler.cc", - "webui/welcome/bookmark_handler.h", - "webui/welcome/bookmark_item.cc", - "webui/welcome/bookmark_item.h", - "webui/welcome/google_apps_handler.cc", - "webui/welcome/google_apps_handler.h", - "webui/welcome/helpers.cc", - "webui/welcome/helpers.h", - "webui/welcome/ntp_background_fetcher.cc", - "webui/welcome/ntp_background_fetcher.h", - "webui/welcome/ntp_background_handler.cc", - "webui/welcome/ntp_background_handler.h", - "webui/welcome/set_as_default_handler.cc", - "webui/welcome/set_as_default_handler.h", - "webui/welcome/welcome_handler.cc", - "webui/welcome/welcome_handler.h", - "webui/welcome/welcome_ui.cc", - "webui/welcome/welcome_ui.h", ] deps += [ @@ -3346,13 +3332,10 @@ "passwords/account_storage_auth_helper.h", "signin/dice_web_signin_interceptor_delegate.cc", "signin/dice_web_signin_interceptor_delegate.h", - "signin/profile_customization_bubble_sync_controller.h", + "signin_reauth_view_controller.cc", + "signin_reauth_view_controller.h", "views/profiles/dice_web_signin_interception_bubble_view.cc", "views/profiles/dice_web_signin_interception_bubble_view.h", - "views/profiles/profile_customization_bubble_sync_controller.cc", - "views/profiles/profile_customization_bubble_sync_controller.h", - "views/profiles/profile_customization_bubble_view.cc", - "views/profiles/profile_customization_bubble_view.h", "views/profiles/profile_picker_dice_sign_in_provider.cc", "views/profiles/profile_picker_dice_sign_in_provider.h", "views/profiles/profile_picker_dice_sign_in_toolbar.cc", @@ -3361,14 +3344,36 @@ "views/sync/dice_bubble_sync_promo_view.h", "views/sync/dice_signin_button_view.cc", "views/sync/dice_signin_button_view.h", + "webui/signin/dice_turn_sync_on_helper_delegate_impl.cc", + "webui/signin/dice_turn_sync_on_helper_delegate_impl.h", "webui/signin/dice_web_signin_intercept_handler.cc", "webui/signin/dice_web_signin_intercept_handler.h", "webui/signin/dice_web_signin_intercept_ui.cc", "webui/signin/dice_web_signin_intercept_ui.h", - "webui/signin/profile_customization_handler.cc", - "webui/signin/profile_customization_handler.h", - "webui/signin/profile_customization_ui.cc", - "webui/signin/profile_customization_ui.h", + "webui/signin/inline_login_handler_impl.cc", + "webui/signin/inline_login_handler_impl.h", + "webui/signin/signin_reauth_handler.cc", + "webui/signin/signin_reauth_handler.h", + "webui/signin/signin_reauth_ui.cc", + "webui/signin/signin_reauth_ui.h", + "webui/welcome/bookmark_handler.cc", + "webui/welcome/bookmark_handler.h", + "webui/welcome/bookmark_item.cc", + "webui/welcome/bookmark_item.h", + "webui/welcome/google_apps_handler.cc", + "webui/welcome/google_apps_handler.h", + "webui/welcome/helpers.cc", + "webui/welcome/helpers.h", + "webui/welcome/ntp_background_fetcher.cc", + "webui/welcome/ntp_background_fetcher.h", + "webui/welcome/ntp_background_handler.cc", + "webui/welcome/ntp_background_handler.h", + "webui/welcome/set_as_default_handler.cc", + "webui/welcome/set_as_default_handler.h", + "webui/welcome/welcome_handler.cc", + "webui/welcome/welcome_handler.h", + "webui/welcome/welcome_ui.cc", + "webui/welcome/welcome_ui.h", ] } }
diff --git a/chrome/browser/ui/android/multiwindow/BUILD.gn b/chrome/browser/ui/android/multiwindow/BUILD.gn index 5eb7ef5..7d6eab7f 100644 --- a/chrome/browser/ui/android/multiwindow/BUILD.gn +++ b/chrome/browser/ui/android/multiwindow/BUILD.gn
@@ -12,6 +12,7 @@ "java/src/org/chromium/chrome/browser/multiwindow/InstanceSwitcherItemProperties.java", "java/src/org/chromium/chrome/browser/multiwindow/InstanceSwitcherItemViewBinder.java", "java/src/org/chromium/chrome/browser/multiwindow/MultiInstanceIphController.java", + "java/src/org/chromium/chrome/browser/multiwindow/MultiInstanceState.java", "java/src/org/chromium/chrome/browser/multiwindow/TargetSelectorCoordinator.java", "java/src/org/chromium/chrome/browser/multiwindow/TargetSelectorItemProperties.java", "java/src/org/chromium/chrome/browser/multiwindow/TargetSelectorItemViewBinder.java", @@ -26,6 +27,7 @@ "//chrome/browser/ui/android/appmenu:java", "//chrome/browser/ui/android/favicon:java", "//chrome/browser/user_education:java", + "//chrome/browser/util:java", "//components/browser_ui/modaldialog/android:java", "//components/browser_ui/styles/android:java", "//components/browser_ui/widget/android:java", @@ -71,6 +73,7 @@ testonly = true sources = [ + "java/src/org/chromium/chrome/browser/multiwindow/MultiInstanceStateUnitTest.java", "java/src/org/chromium/chrome/browser/multiwindow/UiUtilsUnitTest.java", ] @@ -80,6 +83,7 @@ "//base:base_java_test_support", "//base:base_junit_test_support", "//chrome/browser/profiles/android:java", + "//chrome/browser/util:java", "//components/favicon/android:java", "//third_party/android_deps:robolectric_all_java", "//third_party/android_support_test_runner:runner_java",
diff --git a/chrome/browser/ui/android/multiwindow/java/src/org/chromium/chrome/browser/multiwindow/MultiInstanceState.java b/chrome/browser/ui/android/multiwindow/java/src/org/chromium/chrome/browser/multiwindow/MultiInstanceState.java new file mode 100644 index 0000000..8a60d42 --- /dev/null +++ b/chrome/browser/ui/android/multiwindow/java/src/org/chromium/chrome/browser/multiwindow/MultiInstanceState.java
@@ -0,0 +1,147 @@ +// 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. + +package org.chromium.chrome.browser.multiwindow; + +import android.app.Activity; +import android.app.ActivityManager; +import android.app.ActivityManager.AppTask; +import android.util.SparseBooleanArray; + +import org.chromium.base.ActivityState; +import org.chromium.base.ApplicationStatus; +import org.chromium.base.ApplicationStatus.ActivityStateListener; +import org.chromium.base.ObserverList; +import org.chromium.base.supplier.Supplier; +import org.chromium.chrome.browser.util.AndroidTaskUtils; + +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; + +/** + * Tracks multi-instance mode of Chrome browser app. + * + * <p>The class works by observing the lifecycle of application's various Activity objects. Chrome + * browser tasks have {@link ChromeTabbedActivity} at the bottom of the stack and other activities + * on top of it. Their state is tracked to keep the multi-instance state up to date. + */ +public class MultiInstanceState implements ActivityStateListener { + private static MultiInstanceState sInstance; + + /** Observer used to notify multi-instance state change. */ + public interface MultiInstanceStateObserver { + /** + * Called whenever multi-instance state is flipped. + * @param inMultiInstanceMode Whether multiple instances are visible on screen. + */ + void onMultiInstanceStateChanged(boolean inMultiInstanceMode); + } + + /** + * Predicate returning true if a given activity can be the base activity for Chrome task. + */ + public interface BaseActivityName { + boolean is(String baseActivity); + } + + private final Supplier<List<AppTask>> mAppTaskSupplier; + private final BaseActivityName mBaseActivityName; + + // Task visibility observer list. + private final ObserverList<MultiInstanceStateObserver> mObservers = new ObserverList<>(); + + // Stores the current multi-instance state. + private boolean mIsInMultiInstanceMode; + + /** + * Create a singleton instance of {@link MultiInstanceState} object, if not already available. + * @param appTaskSupplier Supplier of a list of the current Chrome app tasks. + * @param baseActivityName Predicate that tells if a given string is a legitimate name of + * the base activity of Chrome task. + */ + public static void maybeCreate( + Supplier<List<AppTask>> appTaskSupplier, BaseActivityName baseActivityName) { + if (sInstance == null) { + sInstance = new MultiInstanceState(appTaskSupplier, baseActivityName); + } + } + + private MultiInstanceState( + Supplier<List<AppTask>> appTaskSupplier, BaseActivityName baseActivityName) { + ApplicationStatus.registerStateListenerForAllActivities(this); + mAppTaskSupplier = appTaskSupplier; + mBaseActivityName = baseActivityName; + } + + @Override + public void onActivityStateChange(Activity activity, @ActivityState int newState) { + if (newState != ActivityState.RESUMED && newState != ActivityState.PAUSED + && newState != ActivityState.STOPPED) { + return; + } + boolean isInMultiInstanceMode = isInMultiInstanceMode(); + if (isInMultiInstanceMode != mIsInMultiInstanceMode) { + mIsInMultiInstanceMode = isInMultiInstanceMode; + Iterator<MultiInstanceStateObserver> it = mObservers.iterator(); + while (it.hasNext()) it.next().onMultiInstanceStateChanged(mIsInMultiInstanceMode); + } + } + + private Set<Integer> getAllTaskIds() { + Set<Integer> results = new HashSet<>(); + for (AppTask task : mAppTaskSupplier.get()) { + ActivityManager.RecentTaskInfo taskInfo = AndroidTaskUtils.getTaskInfoFromTask(task); + if (taskInfo == null || taskInfo.baseActivity == null) continue; + String baseActivity = taskInfo.baseActivity.getClassName(); + if (mBaseActivityName.is(baseActivity)) results.add(taskInfo.id); + } + return results; + } + + public boolean isInMultiInstanceMode() { + Set<Integer> tasks = getAllTaskIds(); + if (tasks.size() < 2) return false; + + SparseBooleanArray visibleTasks = new SparseBooleanArray(); + List<Activity> activities = ApplicationStatus.getRunningActivities(); + for (Activity a : activities) { + int taskId = a.getTaskId(); + if (!tasks.contains(taskId)) continue; + int state = ApplicationStatus.getStateForActivity(a); + if (state == ActivityState.RESUMED || state == ActivityState.PAUSED) { + visibleTasks.put(taskId, true); + } + } + return visibleTasks.size() > 1; + } + + /** + * Add an observer that monitors the multi-instance state of Chrome app. + * @param o {@link MultiInstanceStateObserver} object. + */ + public void addObserver(MultiInstanceStateObserver o) { + mObservers.addObserver(o); + } + + /** + * Removes an observer that monitors the multi-instance state of Chrome app. + * @param o {@link MultiInstanceStateObserver} object. + */ + public void removeObserver(MultiInstanceStateObserver o) { + mObservers.removeObserver(o); + } + + void clear() { + // TODO(jinsukkim): Do the cleanup when the last base activity is destroyed. + ApplicationStatus.unregisterActivityStateListener(this); + mObservers.clear(); + sInstance = null; + } + + public static MultiInstanceState getInstanceForTesting() { + return sInstance; + } +}
diff --git a/chrome/browser/ui/android/multiwindow/java/src/org/chromium/chrome/browser/multiwindow/MultiInstanceStateUnitTest.java b/chrome/browser/ui/android/multiwindow/java/src/org/chromium/chrome/browser/multiwindow/MultiInstanceStateUnitTest.java new file mode 100644 index 0000000..a96e151 --- /dev/null +++ b/chrome/browser/ui/android/multiwindow/java/src/org/chromium/chrome/browser/multiwindow/MultiInstanceStateUnitTest.java
@@ -0,0 +1,185 @@ +// 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. + +package org.chromium.chrome.browser.multiwindow; + +import android.app.Activity; +import android.app.ActivityManager.AppTask; +import android.app.ActivityManager.RecentTaskInfo; +import android.content.ComponentName; +import android.text.TextUtils; + +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.annotation.Config; +import org.robolectric.annotation.Implementation; +import org.robolectric.annotation.Implements; + +import org.chromium.base.ActivityState; +import org.chromium.base.ApplicationStatus; +import org.chromium.base.test.BaseRobolectricTestRunner; +import org.chromium.base.test.util.CallbackHelper; +import org.chromium.chrome.browser.util.AndroidTaskUtils; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Unit tests for MultiInstanceState. + */ +@RunWith(BaseRobolectricTestRunner.class) +@Config(manifest = Config.NONE, shadows = {MultiInstanceStateUnitTest.ShadowAndroidTaskUtils.class}) +public class MultiInstanceStateUnitTest { + @Implements(AndroidTaskUtils.class) + static class ShadowAndroidTaskUtils { + @Implementation + public static RecentTaskInfo getTaskInfoFromTask(AppTask task) { + return sTasks.get(task); + } + } + + private static Map<AppTask, RecentTaskInfo> sTasks = new HashMap<>(); + + private MultiInstanceState mMultiInstanceState; + + private String mBaseActivityClassName = BrowserActivity.class.getName(); + + private static class BaseActivity extends Activity { + private int mTaskId; + + private void setTaskId(int taskId) { + mTaskId = taskId; + } + + @Override + public int getTaskId() { + return mTaskId; + } + + public void stop() { + ApplicationStatus.onStateChangeForTesting(this, ActivityState.STOPPED); + } + + public void destroy() { + AppTask appTask = null; + for (AppTask task : sTasks.keySet()) { + if (AndroidTaskUtils.getTaskInfoFromTask(task).id == mTaskId) appTask = task; + } + sTasks.remove(appTask); + ApplicationStatus.onStateChangeForTesting(this, ActivityState.DESTROYED); + } + } + + // Base activity for browser app. + private static class BrowserActivity extends BaseActivity {} + + // Base activity for custom tab. + private static class CustomTabActivity extends BaseActivity {} + + private static class ObserverHelper extends CallbackHelper { + private int mCount; + + private void assertObserverCalled(String msg) { + assertObserver(true, "Observer was not called: " + msg); + } + + private void assertObserverNotCalled(String msg) { + assertObserver(false, "Observer should not have been called: " + msg); + } + + private void assertObserver(boolean called, String message) { + try { + if (called) { + Assert.assertTrue(message, getCallCount() == mCount + 1); + } else { + Assert.assertFalse(message, getCallCount() == mCount + 1); + } + } finally { + mCount = getCallCount(); + } + } + } + + @Before + public void setUp() { + MultiInstanceState.maybeCreate(this::getChromeTasks, this::matchesBaseActivity); + mMultiInstanceState = MultiInstanceState.getInstanceForTesting(); + } + + @After + public void tearDown() { + ApplicationStatus.destroyForJUnitTests(); + sTasks.clear(); + mMultiInstanceState.clear(); + mMultiInstanceState = null; + } + + private BaseActivity createTaskAndLaunchActivity(int taskId, BaseActivity activity) { + activity.setTaskId(taskId); + RecentTaskInfo taskInfo = new RecentTaskInfo(); + taskInfo.id = taskId; + taskInfo.baseActivity = new ComponentName( + activity.getClass().getPackageName(), activity.getClass().getName()); + sTasks.put(new AppTask(null), taskInfo); + ApplicationStatus.onStateChangeForTesting(activity, ActivityState.CREATED); + ApplicationStatus.onStateChangeForTesting(activity, ActivityState.RESUMED); + return activity; + } + + private List<AppTask> getChromeTasks() { + return new ArrayList<AppTask>(sTasks.keySet()); + } + + private boolean matchesBaseActivity(String name) { + return TextUtils.equals(name, mBaseActivityClassName); + } + + @Test + public void testVisibilityObserverForMultiInstance() { + ObserverHelper helper = new ObserverHelper(); + mMultiInstanceState.addObserver((visible) -> helper.notifyCalled()); + + BaseActivity baseActivity1 = createTaskAndLaunchActivity(29, new BrowserActivity()); + assertInSingleInstanceMode("initial state"); + + BaseActivity baseActivity2 = createTaskAndLaunchActivity(31, new BrowserActivity()); + helper.assertObserverCalled("a resume event for an activity of the 2nd task triggered"); + assertInMultiInstanceMode("the new task's base activity was resumed"); + + BaseActivity baseActivity3 = createTaskAndLaunchActivity(37, new BrowserActivity()); + helper.assertObserverNotCalled("already in multi-instance mode"); + assertInMultiInstanceMode("already in multi-instance mode"); + + baseActivity3.stop(); + helper.assertObserverNotCalled("already in multi-instance mode"); + assertInMultiInstanceMode("already in multi-instance mode"); + + baseActivity2.stop(); + // Only task29 (BaseActivity1) is visible now. Multi-instance state goes off. + helper.assertObserverCalled("A single visible task left"); + assertInSingleInstanceMode("A single visible task left"); + } + + @Test + public void testRuleOutOtherBaseActivityTasks() { + BaseActivity baseActivity1 = createTaskAndLaunchActivity(29, new CustomTabActivity()); + BaseActivity baseActivity2 = createTaskAndLaunchActivity(31, new CustomTabActivity()); + assertInSingleInstanceMode("Base activity is not legit: " + baseActivity1); + } + + private void assertInMultiInstanceMode(String msg) { + Assert.assertTrue("Should be in multi-instance mode: " + msg, + mMultiInstanceState.isInMultiInstanceMode()); + } + + private void assertInSingleInstanceMode(String msg) { + Assert.assertFalse("Should be in single-instance mode: " + msg, + mMultiInstanceState.isInMultiInstanceMode()); + } +}
diff --git a/chrome/browser/ui/android/strings/translations/android_chrome_strings_ms.xtb b/chrome/browser/ui/android/strings/translations/android_chrome_strings_ms.xtb index 20bd98fb..602d145 100644 --- a/chrome/browser/ui/android/strings/translations/android_chrome_strings_ms.xtb +++ b/chrome/browser/ui/android/strings/translations/android_chrome_strings_ms.xtb
@@ -132,6 +132,7 @@ <translation id="1807246157184219062">Cahaya</translation> <translation id="1810845389119482123">Penyediaan penyegerakan permulaan belum selesai</translation> <translation id="1829244130665387512">Cari dalam halaman</translation> +<translation id="1832459821645506983">Ya, saya setuju</translation> <translation id="1843805151597803366">Untuk mendapatkan terjemahan yang lebih baik, benarkan Google Search menggunakan halaman semasa</translation> <translation id="1856325424225101786">Tetapkan semula mod Ringkas?</translation> <translation id="1868024384445905608">Chrome kini memuat turun fail dengan lebih pantas</translation> @@ -289,6 +290,7 @@ <translation id="2718846868787000099">Untuk memaparkan kandungan dalam bahasa pilihan anda, tapak yang anda lawati dapat melihat pilihan anda</translation> <translation id="2723001399770238859">audio</translation> <translation id="2728754400939377704">Isih mengikut tapak</translation> +<translation id="2732063072010454421">Dapatkan pengalaman suara yang lebih baik</translation> <translation id="2739256783402597439">2G</translation> <translation id="2744248271121720757">Ketik perkataan untuk mencari dengan serta-merta atau melihat tindakan yang berkaitan</translation> <translation id="2760989362628427051">Hidupkan tema gelap apabila tema gelap atau Penjimat Bateri peranti anda dihidupkan</translation> @@ -835,6 +837,7 @@ <translation id="587735546353481577">Untuk mengikuti laman, pergi ke laman tersebut, buka menu Chrome dan ketik Ikut.</translation> <translation id="5880748256563468367">Pergi ke suapan</translation> <translation id="5884076754568147479">Untuk membantu anda menyelesaikan tugasan, Google akan menerima URL dan kandungan tapak tempat anda menggunakan Assistant, serta maklumat yang anda serahkan melalui Assistant</translation> +<translation id="5906513782029855931">Mengetahui URL bagi laman membolehkan Google Assistant membantu anda menyelesaikan tugas. Anda boleh mematikan Assistant dalam tetapan Chrome.</translation> <translation id="5916664084637901428">Hidupkan</translation> <translation id="5919204609460789179">Kemas kini <ph name="PRODUCT_NAME" /> untuk memulakan penyegerakan</translation> <translation id="5937580074298050696"><ph name="AMOUNT" /> disimpan</translation>
diff --git a/chrome/browser/ui/android/tab_model/tab_model.h b/chrome/browser/ui/android/tab_model/tab_model.h index 675b4902..6aaadec3 100644 --- a/chrome/browser/ui/android/tab_model/tab_model.h +++ b/chrome/browser/ui/android/tab_model/tab_model.h
@@ -120,23 +120,6 @@ SIZE }; - // Various types of user agent. - // Values must be numbered from 0 and can't have gaps. - // GENERATED_JAVA_ENUM_PACKAGE: org.chromium.chrome.browser.tab - enum class TabUserAgent { - // Choose user agent based on default setting. - DEFAULT, - // Use mobile user agent. - MOBILE, - // Use desktop user agent. - DESKTOP, - // User agent not set, due to an earlier version not having the user agent - // bit. - UNSET, - // Must be last. - SIZE - }; - TabModel(const TabModel&) = delete; TabModel& operator=(const TabModel&) = delete;
diff --git a/chrome/browser/ui/app_list/search/mixer.cc b/chrome/browser/ui/app_list/search/mixer.cc index dfd2896..7b777eb 100644 --- a/chrome/browser/ui/app_list/search/mixer.cc +++ b/chrome/browser/ui/app_list/search/mixer.cc
@@ -122,7 +122,7 @@ void Mixer::InitializeRankers(Profile* profile) { search_result_ranker_ = std::make_unique<SearchResultRanker>(profile); - search_result_ranker_->InitializeRankers(search_controller_); + search_result_ranker_->InitializeRankers(); if (app_list_features::IsSuggestedFilesEnabled() || app_list_features::IsSuggestedLocalFilesEnabled()) {
diff --git a/chrome/browser/ui/app_list/search/search_result_ranker/search_result_ranker.cc b/chrome/browser/ui/app_list/search/search_result_ranker/search_result_ranker.cc index 634d1b1f..5ae16c7b 100644 --- a/chrome/browser/ui/app_list/search/search_result_ranker/search_result_ranker.cc +++ b/chrome/browser/ui/app_list/search/search_result_ranker/search_result_ranker.cc
@@ -27,7 +27,6 @@ #include "chrome/browser/profiles/profile_manager.h" #include "chrome/browser/ui/app_list/search/chrome_search_result.h" #include "chrome/browser/ui/app_list/search/cros_action_history/cros_action_recorder.h" -#include "chrome/browser/ui/app_list/search/search_controller.h" #include "chrome/browser/ui/app_list/search/search_result_ranker/app_search_result_ranker.h" #include "chrome/browser/ui/app_list/search/search_result_ranker/histogram_util.h" #include "chrome/browser/ui/app_list/search/search_result_ranker/ranking_item_util.h" @@ -144,8 +143,7 @@ } } -void SearchResultRanker::InitializeRankers( - SearchController* search_controller) { +void SearchResultRanker::InitializeRankers() { if (app_list_features::IsZeroStateMixedTypesRankerEnabled()) { zero_state_item_coeff_ = base::GetFieldTrialParamByFeatureAsDouble( app_list_features::kEnableZeroStateMixedTypesRanker, "item_coeff",
diff --git a/chrome/browser/ui/app_list/search/search_result_ranker/search_result_ranker.h b/chrome/browser/ui/app_list/search/search_result_ranker/search_result_ranker.h index a5f3e3f..8772d51c 100644 --- a/chrome/browser/ui/app_list/search/search_result_ranker/search_result_ranker.h +++ b/chrome/browser/ui/app_list/search/search_result_ranker/search_result_ranker.h
@@ -27,7 +27,6 @@ namespace app_list { class RecurrenceRanker; -class SearchController; enum class RankingItemType; // SearchResultRanker re-ranks launcher search and zero-state results using a @@ -43,7 +42,7 @@ // Performs all setup of rankers. This is separated from the constructor for // testing reasons. - void InitializeRankers(SearchController* search_controller); + void InitializeRankers(); // Queries each model contained with the SearchResultRanker for its results, // and saves them for use on subsequent calls to Rank(). The given query may
diff --git a/chrome/browser/ui/app_list/search/search_result_ranker/search_result_ranker_unittest.cc b/chrome/browser/ui/app_list/search/search_result_ranker/search_result_ranker_unittest.cc index 070a44d..d71124f 100644 --- a/chrome/browser/ui/app_list/search/search_result_ranker/search_result_ranker_unittest.cc +++ b/chrome/browser/ui/app_list/search/search_result_ranker/search_result_ranker_unittest.cc
@@ -119,12 +119,6 @@ return nullptr; } -class SearchControllerFake : public SearchControllerImpl { - public: - explicit SearchControllerFake(Profile* profile) - : SearchControllerImpl(nullptr, nullptr, nullptr, profile) {} -}; - } // namespace class SearchResultRankerTest : public testing::Test { @@ -181,10 +175,6 @@ return results; } - SearchController* MakeSearchController() { - return new SearchControllerFake(profile_.get()); - } - void Wait() { task_environment_.RunUntilIdle(); } // This is used only to make the ownership clear for the TestSearchResult @@ -213,7 +203,7 @@ TEST_F(SearchResultRankerTest, MixedTypesRankersAreDisabledWithFlag) { DisableAllFeatures(); auto ranker = MakeRanker(); - ranker->InitializeRankers(MakeSearchController()); + ranker->InitializeRankers(); Wait(); LaunchData launch_data; @@ -253,7 +243,7 @@ EnableOneFeature(app_list_features::kEnableAppRanker, {{"use_recurrence_ranker", "true"}, {"config", json}}); auto ranker = MakeRanker(); - ranker->InitializeRankers(MakeSearchController()); + ranker->InitializeRankers(); Wait(); LaunchData app_A; @@ -287,7 +277,7 @@ TEST_F(SearchResultRankerTest, ZeroStateGroupModelDisabledWithFlag) { DisableAllFeatures(); auto ranker = MakeRanker(); - ranker->InitializeRankers(MakeSearchController()); + ranker->InitializeRankers(); Wait(); // TODO(959679): Update the types used in this test once zero-state-related @@ -323,7 +313,7 @@ {"paired_coeff", "0.0"}, }); auto ranker = MakeRanker(); - ranker->InitializeRankers(MakeSearchController()); + ranker->InitializeRankers(); Wait(); LaunchData launch; @@ -358,7 +348,7 @@ {"paired_coeff", "0.0"}, }); auto ranker = MakeRanker(); - ranker->InitializeRankers(MakeSearchController()); + ranker->InitializeRankers(); Wait(); ranker->FetchRankings(std::u16string()); @@ -383,7 +373,7 @@ {"paired_coeff", "0.0"}, }); auto ranker = MakeRanker(); - ranker->InitializeRankers(MakeSearchController()); + ranker->InitializeRankers(); Wait(); auto results = MakeSearchResults( @@ -410,7 +400,7 @@ {"paired_coeff", "0.0"}, }); auto ranker = MakeRanker(); - ranker->InitializeRankers(MakeSearchController()); + ranker->InitializeRankers(); Wait(); // Train on files enough that they should dominate the zero state results. @@ -450,7 +440,7 @@ {"paired_coeff", "0.0"}, }); auto ranker = MakeRanker(); - ranker->InitializeRankers(MakeSearchController()); + ranker->InitializeRankers(); Wait(); // Train on files enough that they should dominate the zero state results. @@ -487,7 +477,7 @@ {"paired_coeff", "0.0"}, }); auto ranker = MakeRanker(); - ranker->InitializeRankers(MakeSearchController()); + ranker->InitializeRankers(); Wait(); // Train on files enough that they should dominate the zero state results. @@ -539,7 +529,7 @@ {"paired_coeff", "0.0"}, }); auto ranker = MakeRanker(); - ranker->InitializeRankers(MakeSearchController()); + ranker->InitializeRankers(); Wait(); // Train on files enough that they should dominate the zero state results. @@ -620,7 +610,7 @@ {{"config", json}}); auto ranker = MakeRanker(); - ranker->InitializeRankers(MakeSearchController()); + ranker->InitializeRankers(); Wait(); // We expect a FakePredictor to have been loaded because predictor_type is set @@ -640,7 +630,7 @@ {"default_group_score", "0.1"}, }); auto ranker = MakeRanker(); - ranker->InitializeRankers(MakeSearchController()); + ranker->InitializeRankers(); Wait(); // Zero state types should be logged during training.
diff --git a/chrome/browser/ui/serial/serial_chooser_controller_unittest.cc b/chrome/browser/ui/serial/serial_chooser_controller_unittest.cc index acdcceb..d4f5422 100644 --- a/chrome/browser/ui/serial/serial_chooser_controller_unittest.cc +++ b/chrome/browser/ui/serial/serial_chooser_controller_unittest.cc
@@ -285,7 +285,13 @@ } } -TEST_F(SerialChooserControllerTest, Blocklist) { +// TODO(crbug.com/1276915): Flaky on Linux TSan Tests. +#if defined(THREAD_SANITIZER) +#define MAYBE_Blocklist DISABLED_Blocklist +#else +#define MAYBE_Blocklist Blocklist +#endif +TEST_F(SerialChooserControllerTest, MAYBE_Blocklist) { base::HistogramTester histogram_tester; // Create two ports from the same vendor with different product IDs.
diff --git a/chrome/browser/ui/startup/startup_browser_creator_browsertest.cc b/chrome/browser/ui/startup/startup_browser_creator_browsertest.cc index 2b9a384..0641f3e 100644 --- a/chrome/browser/ui/startup/startup_browser_creator_browsertest.cc +++ b/chrome/browser/ui/startup/startup_browser_creator_browsertest.cc
@@ -25,6 +25,7 @@ #include "base/threading/thread_restrictions.h" #include "build/branding_buildflags.h" #include "build/build_config.h" +#include "build/buildflag.h" #include "build/chromeos_buildflags.h" #include "chrome/app/chrome_command_ids.h" #include "chrome/browser/browser_process.h" @@ -95,6 +96,7 @@ #include "components/policy/core/common/mock_configuration_policy_provider.h" #include "components/policy/policy_constants.h" #include "components/prefs/pref_service.h" +#include "components/signin/public/base/signin_buildflags.h" #include "components/webapps/browser/installable/installable_metrics.h" #include "content/public/browser/web_contents.h" #include "content/public/common/content_switches.h" @@ -121,7 +123,6 @@ #include "chrome/browser/ui/profile_picker.h" #include "chrome/browser/ui/webui/signin/profile_picker_handler.h" #include "chrome/browser/ui/webui/signin/profile_picker_ui.h" -#include "chrome/browser/ui/webui/welcome/helpers.h" #include "components/policy/core/common/external_data_fetcher.h" #include "components/policy/core/common/policy_map.h" #include "components/policy/core/common/policy_types.h" @@ -139,6 +140,10 @@ #include "ui/views/widget/widget.h" #endif +#if BUILDFLAG(ENABLE_DICE_SUPPORT) +#include "chrome/browser/ui/webui/welcome/helpers.h" +#endif + #if defined(OS_WIN) || defined(OS_MAC) || \ (defined(OS_LINUX) && !BUILDFLAG(IS_CHROMEOS_LACROS)) #include "chrome/browser/ui/startup/web_app_url_handling_startup_test_utils.h" @@ -2930,7 +2935,9 @@ class StartupBrowserCreatorFirstRunTest : public InProcessBrowserTest { public: StartupBrowserCreatorFirstRunTest() { +#if BUILDFLAG(ENABLE_DICE_SUPPORT) scoped_feature_list_.InitWithFeatures({welcome::kForceEnabled}, {}); +#endif } StartupBrowserCreatorFirstRunTest(const StartupBrowserCreatorFirstRunTest&) = delete;
diff --git a/chrome/browser/ui/startup/startup_browser_creator_impl.cc b/chrome/browser/ui/startup/startup_browser_creator_impl.cc index 69c7c8ec..c4eadbf3 100644 --- a/chrome/browser/ui/startup/startup_browser_creator_impl.cc +++ b/chrome/browser/ui/startup/startup_browser_creator_impl.cc
@@ -346,16 +346,16 @@ !SessionStartupPref::TypeHasRecommendedValue(profile_->GetPrefs()); } + // TODO(https://crbug.com/1276034): Cleanup this code, in particular on Ash + // where the welcome flow is never shown. bool welcome_enabled = true; -#if !BUILDFLAG(IS_CHROMEOS_ASH) - welcome_enabled = - welcome::IsEnabled(profile_) && welcome::HasModulesToShow(profile_); -#endif // !BUILDFLAG(IS_CHROMEOS_ASH) - #if BUILDFLAG(IS_CHROMEOS_LACROS) if (AccountConsistencyModeManager::IsMirrorEnabledForProfile(profile_)) welcome_enabled = false; -#endif // BUILDFLAG(IS_CHROMEOS_LACROS) +#elif !BUILDFLAG(IS_CHROMEOS_ASH) + welcome_enabled = + welcome::IsEnabled(profile_) && welcome::HasModulesToShow(profile_); +#endif // !BUILDFLAG(IS_CHROMEOS_ASH) const bool whats_new_enabled = promotional_tabs_enabled && whats_new::ShouldShowForState(local_state);
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_bubble_view_unittest.cc b/chrome/browser/ui/views/bookmarks/bookmark_bubble_view_unittest.cc index bb610f8..2cc8742 100644 --- a/chrome/browser/ui/views/bookmarks/bookmark_bubble_view_unittest.cc +++ b/chrome/browser/ui/views/bookmarks/bookmark_bubble_view_unittest.cc
@@ -105,6 +105,8 @@ BookmarkBubbleView::bookmark_bubble()->GetFootnoteViewForTesting()); } +// TODO(https://crbug.com/1260291): Add support for Lacros. +#if !BUILDFLAG(IS_CHROMEOS_LACROS) // Verifies that the sync promo is displayed for a user that is not signed in. TEST_F(BookmarkBubbleViewTest, SyncPromoNotSignedIn) { CreateBubbleView(); @@ -116,3 +118,4 @@ EXPECT_TRUE(footnote); #endif } +#endif // !BUILDFLAG(IS_CHROMEOS_LACROS)
diff --git a/chrome/browser/ui/views/frame/browser_view.cc b/chrome/browser/ui/views/frame/browser_view.cc index a2bef32..e3ae78e 100644 --- a/chrome/browser/ui/views/frame/browser_view.cc +++ b/chrome/browser/ui/views/frame/browser_view.cc
@@ -3887,7 +3887,8 @@ profiles::BubbleViewMode bubble_view_mode; profiles::BubbleViewModeFromAvatarBubbleMode(mode, GetProfile(), &bubble_view_mode); -#if !BUILDFLAG(IS_CHROMEOS_ASH) +// TODO(https://crbug.com/1260291): Add support for Lacros. +#if !BUILDFLAG(IS_CHROMEOS_ASH) && !BUILDFLAG(IS_CHROMEOS_LACROS) if (SigninViewController::ShouldShowSigninForMode(bubble_view_mode)) { browser_->signin_view_controller()->ShowSignin(bubble_view_mode, access_point);
diff --git a/chrome/browser/ui/webui/chrome_url_data_manager_browsertest.cc b/chrome/browser/ui/webui/chrome_url_data_manager_browsertest.cc index ad7c67b2..02517ef 100644 --- a/chrome/browser/ui/webui/chrome_url_data_manager_browsertest.cc +++ b/chrome/browser/ui/webui/chrome_url_data_manager_browsertest.cc
@@ -149,7 +149,7 @@ public: ChromeURLDataManagerWebUITrustedTypesTest() { std::vector<base::Feature> enabled_features; -#if !BUILDFLAG(IS_CHROMEOS_ASH) +#if !BUILDFLAG(IS_CHROMEOS_ASH) && !BUILDFLAG(IS_CHROMEOS_LACROS) if (GetParam() == std::string("chrome://welcome")) enabled_features.push_back(welcome::kForceEnabled); #endif @@ -331,10 +331,10 @@ #if !defined(OS_CHROMEOS) "chrome://apps", "chrome://browser-switch", + "chrome://welcome", #endif #if !BUILDFLAG(IS_CHROMEOS_ASH) "chrome://signin-email-confirmation", - "chrome://welcome", #endif #if !defined(OS_MAC) "chrome://sandbox",
diff --git a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc index fb7b4c05..81da8ef6 100644 --- a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc +++ b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
@@ -143,7 +143,6 @@ #include "chrome/browser/ui/webui/read_later/read_later_ui.h" #include "chrome/browser/ui/webui/settings/settings_ui.h" #include "chrome/browser/ui/webui/settings/settings_utils.h" -#include "chrome/browser/ui/webui/signin/inline_login_ui.h" #include "chrome/browser/ui/webui/signin/sync_confirmation_ui.h" #include "chrome/browser/ui/webui/sync_file_system_internals/sync_file_system_internals_ui.h" #include "chrome/browser/ui/webui/system_info_ui.h" @@ -287,6 +286,8 @@ #if !defined(OS_CHROMEOS) && !defined(OS_ANDROID) #include "chrome/browser/ui/webui/browser_switch/browser_switch_ui.h" +#include "chrome/browser/ui/webui/welcome/helpers.h" +#include "chrome/browser/ui/webui/welcome/welcome_ui.h" #endif #if !BUILDFLAG(IS_CHROMEOS_ASH) && !defined(OS_ANDROID) @@ -296,9 +297,10 @@ #include "chrome/browser/ui/webui/signin/profile_picker_ui.h" #include "chrome/browser/ui/webui/signin/signin_email_confirmation_ui.h" #include "chrome/browser/ui/webui/signin/signin_error_ui.h" -#include "chrome/browser/ui/webui/signin/signin_reauth_ui.h" -#include "chrome/browser/ui/webui/welcome/helpers.h" -#include "chrome/browser/ui/webui/welcome/welcome_ui.h" +#endif + +#if !BUILDFLAG(IS_CHROMEOS_LACROS) && !defined(OS_ANDROID) +#include "chrome/browser/ui/webui/signin/inline_login_ui.h" #endif #if defined(OS_WIN) @@ -346,6 +348,7 @@ #if BUILDFLAG(ENABLE_DICE_SUPPORT) #include "chrome/browser/ui/webui/signin/dice_web_signin_intercept_ui.h" +#include "chrome/browser/ui/webui/signin/signin_reauth_ui.h" #endif #if BUILDFLAG(GOOGLE_CHROME_BRANDING) @@ -574,7 +577,7 @@ } #endif // BUILDFLAG(IS_CHROMEOS_ASH) -#if !defined(OS_ANDROID) && !BUILDFLAG(IS_CHROMEOS_ASH) +#if !defined(OS_ANDROID) && !defined(OS_CHROMEOS) template <> WebUIController* NewWebUI<WelcomeUI>(WebUI* web_ui, const GURL& url) { return new WelcomeUI(web_ui, url); @@ -711,6 +714,10 @@ !profile->IsGuestSession()) { return &NewWebUI<AppLauncherPageUI>; } + if (url.host_piece() == chrome::kChromeUIWelcomeHost && + welcome::IsEnabled(profile)) { + return &NewWebUI<WelcomeUI>; + } #endif // !defined(OS_CHROMEOS) if (profile->IsGuestSession() && (url.host_piece() == chrome::kChromeUIAppLauncherPageHost || @@ -765,9 +772,11 @@ return &NewWebUI<SyncFileSystemInternalsUI>; if (url.host_piece() == chrome::kChromeUISystemInfoHost) return &NewWebUI<SystemInfoUI>; - // Inline login UI is available on all platforms except Android. +#if !BUILDFLAG(IS_CHROMEOS_LACROS) + // Inline login UI is available on all platforms except Android and Lacros. if (url.host_piece() == chrome::kChromeUIChromeSigninHost) return &NewWebUI<InlineLoginUI>; +#endif if (base::FeatureList::IsEnabled(features::kAccessCodeCastUI)) { if (url.host_piece() == chrome::kChromeUIAccessCodeCastHost) return &NewWebUI<AccessCodeCastUI>; @@ -1035,13 +1044,6 @@ if (url.host_piece() == chrome::kChromeUISigninEmailConfirmationHost && !profile->IsOffTheRecord()) return &NewWebUI<SigninEmailConfirmationUI>; - if (url.host_piece() == chrome::kChromeUISigninReauthHost && - !profile->IsOffTheRecord()) { - return &NewWebUI<SigninReauthUI>; - } - if (url.host_piece() == chrome::kChromeUIWelcomeHost && - welcome::IsEnabled(profile)) - return &NewWebUI<WelcomeUI>; #endif #if BUILDFLAG(ENABLE_NACL) @@ -1158,6 +1160,10 @@ #if BUILDFLAG(ENABLE_DICE_SUPPORT) if (url.host_piece() == chrome::kChromeUIDiceWebSigninInterceptHost) return &NewWebUI<DiceWebSigninInterceptUI>; + if (url.host_piece() == chrome::kChromeUISigninReauthHost && + !profile->IsOffTheRecord()) { + return &NewWebUI<SigninReauthUI>; + } #endif #if BUILDFLAG(PLATFORM_CFM)
diff --git a/chrome/browser/ui/webui/chromeos/emulator/device_emulator_message_handler.cc b/chrome/browser/ui/webui/chromeos/emulator/device_emulator_message_handler.cc index beefa2c3..3a29f71 100644 --- a/chrome/browser/ui/webui/chromeos/emulator/device_emulator_message_handler.cc +++ b/chrome/browser/ui/webui/chromeos/emulator/device_emulator_message_handler.cc
@@ -309,11 +309,11 @@ CHECK(device_value.is_dict()); const base::DictionaryValue& device_dict = base::Value::AsDictionaryValue(device_value); - CHECK(device_dict.GetBoolean("isInput", &audio_node.is_input)); + audio_node.is_input = device_dict.FindBoolKey("isInput").value(); CHECK(device_dict.GetString("deviceName", &audio_node.device_name)); CHECK(device_dict.GetString("type", &audio_node.type)); CHECK(device_dict.GetString("name", &audio_node.name)); - CHECK(device_dict.GetBoolean("active", &audio_node.active)); + audio_node.active = device_dict.FindBoolKey("active").value(); std::string tmp_id; CHECK(device_dict.GetString("id", &tmp_id)); @@ -583,8 +583,8 @@ CHECK(class_value); props.device_class = *class_value; - CHECK(device_dict.GetBoolean("isTrusted", &props.is_trusted)); - CHECK(device_dict.GetBoolean("incoming", &props.incoming)); + props.is_trusted = device_dict.FindBoolKey("isTrusted").value(); + props.incoming = device_dict.FindBoolKey("incoming").value(); // Create the device and store it in the FakeBluetoothDeviceClient's observed // list of devices.
diff --git a/chrome/browser/ui/webui/chromeos/sync/os_sync_handler.cc b/chrome/browser/ui/webui/chromeos/sync/os_sync_handler.cc index a240aab..dae3f19 100644 --- a/chrome/browser/ui/webui/chromeos/sync/os_sync_handler.cc +++ b/chrome/browser/ui/webui/chromeos/sync/os_sync_handler.cc
@@ -108,8 +108,7 @@ base::Value::AsDictionaryValue(result_value); // Wallpaper sync status is stored directly to the profile's prefs. - bool wallpaper_synced; - CHECK(result.GetBoolean(kWallpaperEnabledKey, &wallpaper_synced)); + bool wallpaper_synced = result.FindBoolPath(kWallpaperEnabledKey).value(); profile_->GetPrefs()->SetBoolean(chromeos::settings::prefs::kSyncOsWallpaper, wallpaper_synced); @@ -121,16 +120,15 @@ if (!service || !service->IsEngineInitialized()) return; - bool sync_all_os_types; - CHECK(result.GetBoolean("syncAllOsTypes", &sync_all_os_types)); + bool sync_all_os_types = result.FindBoolKey("syncAllOsTypes").value(); UserSelectableOsTypeSet selected_types; for (UserSelectableOsType type : UserSelectableOsTypeSet::All()) { std::string key = syncer::GetUserSelectableOsTypeName(type) + std::string("Synced"); - bool sync_value; - CHECK(result.GetBoolean(key, &sync_value)) << key; - if (sync_value) + absl::optional<bool> sync_value = result.FindBoolPath(key); + CHECK(sync_value.has_value()) << key; + if (sync_value.value()) selected_types.Put(type); }
diff --git a/chrome/browser/ui/webui/chromeos/sync/os_sync_handler_unittest.cc b/chrome/browser/ui/webui/chromeos/sync/os_sync_handler_unittest.cc index 1866bcb7..5fec615 100644 --- a/chrome/browser/ui/webui/chromeos/sync/os_sync_handler_unittest.cc +++ b/chrome/browser/ui/webui/chromeos/sync/os_sync_handler_unittest.cc
@@ -21,6 +21,7 @@ #include "components/sync/driver/test_sync_service.h" #include "content/public/browser/web_ui_controller.h" #include "content/public/test/test_web_ui.h" +#include "testing/gmock/include/gmock/gmock-matchers.h" #include "testing/gtest/include/gtest/gtest.h" using base::DictionaryValue; @@ -36,6 +37,8 @@ namespace { +using ::testing::Optional; + enum SyncAllConfig { SYNC_ALL_OS_TYPES, CHOOSE_WHAT_TO_SYNC }; // Creates a dictionary with the key/value pairs appropriate for a call to @@ -61,10 +64,8 @@ void CheckBool(const DictionaryValue* dictionary, const std::string& key, bool expected_value) { - bool actual_value; - EXPECT_TRUE(dictionary->GetBoolean(key, &actual_value)) - << "No value found for " << key; - EXPECT_EQ(expected_value, actual_value) << "Mismatch found for " << key; + EXPECT_THAT(dictionary->FindBoolPath(key), Optional(expected_value)) + << "Key: " << key; } // Checks to make sure that the values stored in |dictionary| match the values
diff --git a/chrome/browser/ui/webui/settings/chromeos/privacy_section.cc b/chrome/browser/ui/webui/settings/chromeos/privacy_section.cc index ede5dfd..f2373e8 100644 --- a/chrome/browser/ui/webui/settings/chromeos/privacy_section.cc +++ b/chrome/browser/ui/webui/settings/chromeos/privacy_section.cc
@@ -166,16 +166,12 @@ return *tags; } -bool IsSmartPrivacyEnabled() { - return ash::features::IsSnoopingProtectionEnabled() || - ash::features::IsQuickDimEnabled(); -} - const std::vector<SearchConcept>& GetSmartPrivacySearchConcepts() { static const base::NoDestructor<std::vector<SearchConcept>> tags([] { std::vector<SearchConcept> init_tags; - if (IsSmartPrivacyEnabled()) { + if (ash::features::IsSnoopingProtectionEnabled() || + ash::features::IsQuickDimEnabled()) { init_tags.push_back({IDS_OS_SETTINGS_TAG_SMART_PRIVACY, mojom::kSmartPrivacySubpagePath, mojom::SearchResultIcon::kShield, @@ -328,7 +324,10 @@ }; html_source->AddLocalizedStrings(kLocalizedStrings); - html_source->AddBoolean("isSmartPrivacyEnabled", IsSmartPrivacyEnabled()); + html_source->AddBoolean("isSnoopingProtectionEnabled", + ash::features::IsSnoopingProtectionEnabled()); + html_source->AddBoolean("isQuickDimEnabled", + ash::features::IsQuickDimEnabled()); html_source->AddString("suggestedContentLearnMoreURL", chrome::kSuggestedContentLearnMoreURL);
diff --git a/chrome/browser/ui/webui/settings/metrics_reporting_handler.cc b/chrome/browser/ui/webui/settings/metrics_reporting_handler.cc index a491469..69420ca 100644 --- a/chrome/browser/ui/webui/settings/metrics_reporting_handler.cc +++ b/chrome/browser/ui/webui/settings/metrics_reporting_handler.cc
@@ -106,8 +106,7 @@ return; } - bool enabled; - CHECK(args->GetBoolean(0, &enabled)); + bool enabled = args->GetList()[0].GetBool(); ChangeMetricsReportingState(enabled); #if BUILDFLAG(IS_CHROMEOS_LACROS)
diff --git a/chrome/browser/ui/webui/settings/people_handler_unittest.cc b/chrome/browser/ui/webui/settings/people_handler_unittest.cc index 34b10657..ade8543 100644 --- a/chrome/browser/ui/webui/settings/people_handler_unittest.cc +++ b/chrome/browser/ui/webui/settings/people_handler_unittest.cc
@@ -1247,9 +1247,7 @@ handler_->OnDidClosePage(&did_abort); } -// TODO(crbug.com/1220066): Remove the lacros exclusion when DICE is disabled on -// Lacros. -#if BUILDFLAG(ENABLE_DICE_SUPPORT) && !BUILDFLAG(IS_CHROMEOS_LACROS) +#if BUILDFLAG(ENABLE_DICE_SUPPORT) TEST(PeopleHandlerDiceUnifiedConsentTest, StoredAccountsList) { ScopedTestingLocalState local_state(TestingBrowserProcess::GetGlobal()); @@ -1291,7 +1289,7 @@ EXPECT_EQ("a@gmail.com", accounts_list[0].FindKey("email")->GetString()); EXPECT_EQ("b@gmail.com", accounts_list[1].FindKey("email")->GetString()); } -#endif // BUILDFLAG(ENABLE_DICE_SUPPORT) && !BUILDFLAG(IS_CHROMEOS_LACROS) +#endif // BUILDFLAG(ENABLE_DICE_SUPPORT) #if BUILDFLAG(IS_CHROMEOS_ASH) // Regression test for crash in guest mode. https://crbug.com/1040476
diff --git a/chrome/browser/ui/webui/signin/dice_turn_sync_on_helper.cc b/chrome/browser/ui/webui/signin/dice_turn_sync_on_helper.cc index b4fbc9b..c007cc7 100644 --- a/chrome/browser/ui/webui/signin/dice_turn_sync_on_helper.cc +++ b/chrome/browser/ui/webui/signin/dice_turn_sync_on_helper.cc
@@ -31,8 +31,8 @@ #include "chrome/browser/signin/signin_util.h" #include "chrome/browser/sync/sync_service_factory.h" #include "chrome/browser/ui/browser.h" +#include "chrome/browser/ui/sync/profile_signin_confirmation_helper.h" #include "chrome/browser/ui/tab_dialogs.h" -#include "chrome/browser/ui/webui/signin/dice_turn_sync_on_helper_delegate_impl.h" #include "chrome/browser/ui/webui/signin/login_ui_service_factory.h" #include "chrome/browser/ui/webui/signin/signin_ui_error.h" #include "chrome/browser/ui/webui/signin/signin_utils_desktop.h" @@ -56,6 +56,7 @@ #if BUILDFLAG(ENABLE_DICE_SUPPORT) #include "chrome/browser/signin/dice_signed_in_profile_creator.h" +#include "chrome/browser/ui/webui/signin/dice_turn_sync_on_helper_delegate_impl.h" #endif namespace { @@ -255,6 +256,7 @@ weak_pointer_factory_.GetWeakPtr())); } +#if BUILDFLAG(ENABLE_DICE_SUPPORT) DiceTurnSyncOnHelper::DiceTurnSyncOnHelper( Profile* profile, Browser* browser, @@ -272,6 +274,7 @@ signin_aborted_mode, std::make_unique<DiceTurnSyncOnHelperDelegateImpl>(browser), base::OnceClosure()) {} +#endif DiceTurnSyncOnHelper::~DiceTurnSyncOnHelper() { DCHECK_EQ(this, GetCurrentDiceTurnSyncOnHelper(profile_)); @@ -717,6 +720,7 @@ kDiceTurnOnSyncHelper_Abort); } #else + // TODO(https://crbug.com/1260291): Implement on Lacros. NOTIMPLEMENTED() << "Profiles without accounts are not yet supported on lacros."; #endif
diff --git a/chrome/browser/ui/webui/signin/dice_turn_sync_on_helper.h b/chrome/browser/ui/webui/signin/dice_turn_sync_on_helper.h index 87d4a53..af1aaf2 100644 --- a/chrome/browser/ui/webui/signin/dice_turn_sync_on_helper.h +++ b/chrome/browser/ui/webui/signin/dice_turn_sync_on_helper.h
@@ -158,6 +158,7 @@ std::unique_ptr<Delegate> delegate, base::OnceClosure callback); +#if BUILDFLAG(ENABLE_DICE_SUPPORT) // Convenience constructor using the default delegate and empty callback. DiceTurnSyncOnHelper(Profile* profile, Browser* browser, @@ -166,6 +167,7 @@ signin_metrics::Reason signin_reason, const CoreAccountId& account_id, SigninAbortedMode signin_aborted_mode); +#endif DiceTurnSyncOnHelper(const DiceTurnSyncOnHelper&) = delete; DiceTurnSyncOnHelper& operator=(const DiceTurnSyncOnHelper&) = delete;
diff --git a/chrome/browser/ui/webui/signin/dice_turn_sync_on_helper_unittest.cc b/chrome/browser/ui/webui/signin/dice_turn_sync_on_helper_unittest.cc index 5e0509a2..6437e2f 100644 --- a/chrome/browser/ui/webui/signin/dice_turn_sync_on_helper_unittest.cc +++ b/chrome/browser/ui/webui/signin/dice_turn_sync_on_helper_unittest.cc
@@ -611,6 +611,8 @@ CheckDelegateCalls(); } +// TODO(https://crbug.com/1260291): Enable this test on Lacros. +#if !BUILDFLAG(IS_CHROMEOS_LACROS) // Tests that the login error is displayed and that the account is removed. TEST_F(DiceTurnSyncOnHelperTest, CanOfferSigninErrorRemoveAccount) { // Set expectations. @@ -627,6 +629,7 @@ EXPECT_FALSE(identity_manager()->HasAccountWithRefreshToken(account_id())); CheckDelegateCalls(); } +#endif // Tests that the sync disabled message is displayed and that the account is // removed upon the ABORT_SYNC action. @@ -718,6 +721,8 @@ CheckDelegateCalls(); } +// TODO(https://crbug.com/1260291): Enable this test on Lacros. +#if !BUILDFLAG(IS_CHROMEOS_LACROS) // Aborts the flow after the cross account dialog. TEST_F(DiceTurnSyncOnHelperTest, CrossAccountAbort) { // Set expectations. @@ -735,6 +740,7 @@ EXPECT_FALSE(identity_manager()->HasAccountWithRefreshToken(account_id())); CheckDelegateCalls(); } +#endif // Merge data after the cross account dialog. TEST_F(DiceTurnSyncOnHelperTest, CrossAccountContinue) { @@ -755,6 +761,8 @@ CheckDelegateCalls(); } +// TODO(https://crbug.com/1260291): Enable these tests on Lacros. +#if !BUILDFLAG(IS_CHROMEOS_LACROS) // Create a new profile after the cross account dialog and show the signin page. TEST_F(DiceTurnSyncOnHelperTest, CrossAccountNewProfile) { // Set expectations. @@ -796,6 +804,7 @@ EXPECT_FALSE(identity_manager()->HasAccountWithRefreshToken(account_id())); CheckDelegateCalls(); } +#endif // Continue after the enterprise confirmation prompt. TEST_F(DiceTurnSyncOnHelperTest, DISABLED_EnterpriseConfirmationContinue) { @@ -816,6 +825,8 @@ CheckDelegateCalls(); } +// TODO(https://crbug.com/1260291): Enable this test on Lacros. +#if !BUILDFLAG(IS_CHROMEOS_LACROS) // Continue with a new profile after the enterprise confirmation prompt. TEST_F(DiceTurnSyncOnHelperTest, EnterpriseConfirmationNewProfile) { // Set expectations. @@ -873,6 +884,7 @@ signin::ConsentLevel::kSignin)); CheckDelegateCalls(); } +#endif // Tests that the sync confirmation is shown and the user can abort. TEST_F(DiceTurnSyncOnHelperTest, UndoSync) {
diff --git a/chrome/browser/ui/webui/signin/login_ui_test_utils.cc b/chrome/browser/ui/webui/signin/login_ui_test_utils.cc index 89525dd..467bbc5 100644 --- a/chrome/browser/ui/webui/signin/login_ui_test_utils.cc +++ b/chrome/browser/ui/webui/signin/login_ui_test_utils.cc
@@ -442,7 +442,7 @@ bool SignInWithUI(Browser* browser, const std::string& username, const std::string& password) { -#if BUILDFLAG(IS_CHROMEOS_ASH) +#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS) NOTREACHED(); return false; #else
diff --git a/chrome/browser/ui/webui/signin/profile_picker_ui.cc b/chrome/browser/ui/webui/signin/profile_picker_ui.cc index ff60fa67..bcf819c 100644 --- a/chrome/browser/ui/webui/signin/profile_picker_ui.cc +++ b/chrome/browser/ui/webui/signin/profile_picker_ui.cc
@@ -33,6 +33,7 @@ #include "components/policy/core/common/policy_service.h" #include "components/policy/policy_constants.h" #include "components/prefs/pref_service.h" +#include "components/signin/public/base/signin_buildflags.h" #include "components/strings/grit/components_strings.h" #include "content/public/browser/web_contents.h" #include "content/public/browser/web_ui_data_source.h" @@ -201,7 +202,11 @@ g_browser_process->local_state()->GetBoolean( prefs::kBrowserShowProfilePickerOnStartup)); html_source->AddBoolean("signInProfileCreationFlowSupported", +#if BUILDFLAG(ENABLE_DICE_SUPPORT) AccountConsistencyModeManager::IsDiceSignInAllowed()); +#else + true); +#endif html_source->AddString("minimumPickerSize", base::StringPrintf("%ipx", kMinimumPickerSizePx));
diff --git a/chrome/browser/ui/zoom/zoom_controller_browsertest.cc b/chrome/browser/ui/zoom/zoom_controller_browsertest.cc index c768aa5..a80918d 100644 --- a/chrome/browser/ui/zoom/zoom_controller_browsertest.cc +++ b/chrome/browser/ui/zoom/zoom_controller_browsertest.cc
@@ -340,7 +340,8 @@ } #endif // !defined(OS_MAC) -#if !BUILDFLAG(IS_CHROMEOS_ASH) +// TODO(https://crbug.com/1260291): Add support for Lacros. +#if !BUILDFLAG(IS_CHROMEOS_ASH) && !BUILDFLAG(IS_CHROMEOS_LACROS) // Regression test: crbug.com/438979. IN_PROC_BROWSER_TEST_F(ZoomControllerBrowserTest, SettingsZoomAfterSigninWorks) { @@ -394,7 +395,7 @@ zoom_controller->SetZoomLevel(new_zoom_level); zoom_change_watcher.Wait(); } -#endif // !BUILDFLAG(IS_CHROMEOS_ASH) +#endif // !BUILDFLAG(IS_CHROMEOS_ASH) && !BUILDFLAG(IS_CHROMEOS_LACROS) class ZoomControllerForPrerenderingTest : public ZoomControllerBrowserTest, public zoom::ZoomObserver {
diff --git a/chrome/browser/usb/web_usb_chooser.cc b/chrome/browser/usb/web_usb_chooser.cc index 1bd99cb..efd0f7f8 100644 --- a/chrome/browser/usb/web_usb_chooser.cc +++ b/chrome/browser/usb/web_usb_chooser.cc
@@ -26,6 +26,7 @@ void WebUsbChooser::GetPermission( std::vector<device::mojom::UsbDeviceFilterPtr> device_filters, blink::mojom::WebUsbService::GetPermissionCallback callback) { + DCHECK(!render_frame_host_->IsNestedWithinFencedFrame()); url::Origin origin = render_frame_host_->GetMainFrame()->GetLastCommittedOrigin(); auto* profile =
diff --git a/chrome/browser/web_applications/extensions/externally_managed_app_install_task_unittest.cc b/chrome/browser/web_applications/extensions/externally_managed_app_install_task_unittest.cc index 1f86810..11ed93b 100644 --- a/chrome/browser/web_applications/extensions/externally_managed_app_install_task_unittest.cc +++ b/chrome/browser/web_applications/extensions/externally_managed_app_install_task_unittest.cc
@@ -420,18 +420,17 @@ task->Install( web_contents(), base::BindLambdaForTesting( - [&](absl::optional<AppId> app_id, - ExternallyManagedAppManager::InstallResult result) { + [&](ExternallyManagedAppManager::InstallResult result) { absl::optional<AppId> id = ExternallyInstalledWebAppPrefs(profile()->GetPrefs()) .LookupAppId(kWebAppUrl); EXPECT_EQ(InstallResultCode::kSuccessNewInstall, result.code); - EXPECT_TRUE(app_id.has_value()); + EXPECT_TRUE(result.app_id.has_value()); EXPECT_FALSE(IsPlaceholderApp(profile(), kWebAppUrl)); - EXPECT_EQ(app_id.value(), id.value()); + EXPECT_EQ(result.app_id.value(), id.value()); EXPECT_EQ(1u, os_integration_manager()->num_create_shortcuts_calls()); @@ -465,15 +464,14 @@ task->Install(web_contents(), base::BindLambdaForTesting( - [&](absl::optional<AppId> app_id, - ExternallyManagedAppManager::InstallResult result) { + [&](ExternallyManagedAppManager::InstallResult result) { absl::optional<AppId> id = ExternallyInstalledWebAppPrefs(profile()->GetPrefs()) .LookupAppId(kWebAppUrl); EXPECT_EQ(InstallResultCode::kGetWebApplicationInfoFailed, result.code); - EXPECT_FALSE(app_id.has_value()); + EXPECT_FALSE(result.app_id.has_value()); EXPECT_FALSE(id.has_value()); @@ -498,11 +496,10 @@ task->Install( web_contents(), - base::BindLambdaForTesting([&](absl::optional<AppId> app_id, - ExternallyManagedAppManager::InstallResult + base::BindLambdaForTesting([&](ExternallyManagedAppManager::InstallResult result) { EXPECT_EQ(InstallResultCode::kSuccessNewInstall, result.code); - EXPECT_TRUE(app_id.has_value()); + EXPECT_TRUE(result.app_id.has_value()); EXPECT_EQ(1u, os_integration_manager()->num_create_shortcuts_calls()); EXPECT_FALSE(os_integration_manager()->did_add_to_desktop().value()); @@ -533,10 +530,9 @@ task->Install( web_contents(), base::BindLambdaForTesting( - [&](absl::optional<AppId> app_id, - ExternallyManagedAppManager::InstallResult result) { + [&](ExternallyManagedAppManager::InstallResult result) { EXPECT_EQ(InstallResultCode::kSuccessNewInstall, result.code); - EXPECT_TRUE(app_id.has_value()); + EXPECT_TRUE(result.app_id.has_value()); EXPECT_EQ(1u, os_integration_manager()->num_create_shortcuts_calls()); @@ -568,11 +564,10 @@ base::RunLoop run_loop; task->Install( web_contents(), - base::BindLambdaForTesting([&](absl::optional<AppId> app_id, - ExternallyManagedAppManager::InstallResult + base::BindLambdaForTesting([&](ExternallyManagedAppManager::InstallResult result) { EXPECT_EQ(InstallResultCode::kSuccessNewInstall, result.code); - EXPECT_TRUE(app_id.has_value()); + EXPECT_TRUE(result.app_id.has_value()); EXPECT_EQ(1u, os_integration_manager()->num_create_shortcuts_calls()); EXPECT_FALSE(os_integration_manager()->did_add_to_desktop().value()); @@ -601,11 +596,10 @@ base::RunLoop run_loop; task->Install(web_contents(), base::BindLambdaForTesting( - [&](absl::optional<AppId> app_id, - ExternallyManagedAppManager::InstallResult result) { + [&](ExternallyManagedAppManager::InstallResult result) { EXPECT_EQ(InstallResultCode::kSuccessNewInstall, result.code); - EXPECT_TRUE(app_id.has_value()); + EXPECT_TRUE(result.app_id.has_value()); EXPECT_EQ(web_app_info().user_display_mode, DisplayMode::kStandalone); run_loop.Quit(); @@ -627,11 +621,10 @@ base::RunLoop run_loop; task->Install(web_contents(), base::BindLambdaForTesting( - [&](absl::optional<AppId> app_id, - ExternallyManagedAppManager::InstallResult result) { + [&](ExternallyManagedAppManager::InstallResult result) { EXPECT_EQ(InstallResultCode::kSuccessNewInstall, result.code); - EXPECT_TRUE(app_id.has_value()); + EXPECT_TRUE(result.app_id.has_value()); EXPECT_EQ(web_app_info().user_display_mode, DisplayMode::kBrowser); run_loop.Quit(); @@ -653,11 +646,10 @@ base::RunLoop run_loop; task->Install(web_contents(), base::BindLambdaForTesting( - [&](absl::optional<AppId> app_id, - ExternallyManagedAppManager::InstallResult result) { + [&](ExternallyManagedAppManager::InstallResult result) { EXPECT_EQ(InstallResultCode::kSuccessNewInstall, result.code); - EXPECT_TRUE(app_id.has_value()); + EXPECT_TRUE(result.app_id.has_value()); EXPECT_EQ(webapps::WebappInstallSource::INTERNAL_DEFAULT, finalize_options().install_source); @@ -680,11 +672,10 @@ base::RunLoop run_loop; task->Install(web_contents(), base::BindLambdaForTesting( - [&](absl::optional<AppId> app_id, - ExternallyManagedAppManager::InstallResult result) { + [&](ExternallyManagedAppManager::InstallResult result) { EXPECT_EQ(InstallResultCode::kSuccessNewInstall, result.code); - EXPECT_TRUE(app_id.has_value()); + EXPECT_TRUE(result.app_id.has_value()); EXPECT_EQ(webapps::WebappInstallSource::EXTERNAL_POLICY, finalize_options().install_source); @@ -708,10 +699,9 @@ task->Install( web_contents(), base::BindLambdaForTesting( - [&](absl::optional<AppId> app_id, - ExternallyManagedAppManager::InstallResult result) { + [&](ExternallyManagedAppManager::InstallResult result) { EXPECT_EQ(InstallResultCode::kSuccessNewInstall, result.code); - EXPECT_TRUE(app_id.has_value()); + EXPECT_TRUE(result.app_id.has_value()); EXPECT_TRUE(IsPlaceholderApp(profile(), kWebAppUrl)); @@ -748,10 +738,9 @@ task->Install( web_contents(), base::BindLambdaForTesting( - [&](absl::optional<AppId> app_id, - ExternallyManagedAppManager::InstallResult result) { + [&](ExternallyManagedAppManager::InstallResult result) { EXPECT_EQ(InstallResultCode::kSuccessNewInstall, result.code); - EXPECT_TRUE(app_id.has_value()); + EXPECT_TRUE(result.app_id.has_value()); EXPECT_TRUE(IsPlaceholderApp(profile(), kWebAppUrl)); @@ -792,10 +781,9 @@ task->Install( web_contents(), base::BindLambdaForTesting( - [&](absl::optional<AppId> app_id, - ExternallyManagedAppManager::InstallResult result) { + [&](ExternallyManagedAppManager::InstallResult result) { EXPECT_EQ(InstallResultCode::kSuccessNewInstall, result.code); - EXPECT_TRUE(app_id.has_value()); + EXPECT_TRUE(result.app_id.has_value()); EXPECT_TRUE(IsPlaceholderApp(profile(), kWebAppUrl)); @@ -836,10 +824,9 @@ task->Install( web_contents(), base::BindLambdaForTesting( - [&](absl::optional<AppId> app_id, - ExternallyManagedAppManager::InstallResult result) { + [&](ExternallyManagedAppManager::InstallResult result) { EXPECT_EQ(InstallResultCode::kSuccessNewInstall, result.code); - placeholder_app_id = app_id.value(); + placeholder_app_id = result.app_id.value(); EXPECT_EQ(1u, finalizer()->finalize_options_list().size()); run_loop.Quit(); @@ -857,10 +844,9 @@ task->Install( web_contents(), base::BindLambdaForTesting( - [&](absl::optional<AppId> app_id, - ExternallyManagedAppManager::InstallResult result) { + [&](ExternallyManagedAppManager::InstallResult result) { EXPECT_EQ(InstallResultCode::kSuccessNewInstall, result.code); - EXPECT_EQ(placeholder_app_id, app_id.value()); + EXPECT_EQ(placeholder_app_id, result.app_id.value()); // There shouldn't be a second call to the finalizer. EXPECT_EQ(1u, finalizer()->finalize_options_list().size()); @@ -888,10 +874,9 @@ task->Install( web_contents(), base::BindLambdaForTesting( - [&](absl::optional<AppId> app_id, - ExternallyManagedAppManager::InstallResult result) { + [&](ExternallyManagedAppManager::InstallResult result) { EXPECT_EQ(InstallResultCode::kSuccessNewInstall, result.code); - placeholder_app_id = app_id.value(); + placeholder_app_id = result.app_id.value(); EXPECT_EQ(1u, finalizer()->finalize_options_list().size()); run_loop.Quit(); @@ -911,10 +896,9 @@ task->Install( web_contents(), base::BindLambdaForTesting( - [&](absl::optional<AppId> app_id, - ExternallyManagedAppManager::InstallResult result) { + [&](ExternallyManagedAppManager::InstallResult result) { EXPECT_EQ(InstallResultCode::kSuccessNewInstall, result.code); - EXPECT_TRUE(app_id.has_value()); + EXPECT_TRUE(result.app_id.has_value()); EXPECT_FALSE(IsPlaceholderApp(profile(), kWebAppUrl)); EXPECT_EQ(1u, @@ -945,10 +929,9 @@ task->Install( web_contents(), base::BindLambdaForTesting( - [&](absl::optional<AppId> app_id, - ExternallyManagedAppManager::InstallResult result) { + [&](ExternallyManagedAppManager::InstallResult result) { EXPECT_EQ(InstallResultCode::kSuccessNewInstall, result.code); - placeholder_app_id = app_id.value(); + placeholder_app_id = result.app_id.value(); EXPECT_EQ(1u, finalizer()->finalize_options_list().size()); @@ -970,11 +953,10 @@ task->Install( web_contents(), base::BindLambdaForTesting( - [&](absl::optional<AppId> app_id, - ExternallyManagedAppManager::InstallResult result) { + [&](ExternallyManagedAppManager::InstallResult result) { EXPECT_EQ(InstallResultCode::kFailedPlaceholderUninstall, result.code); - EXPECT_FALSE(app_id.has_value()); + EXPECT_FALSE(result.app_id.has_value()); EXPECT_TRUE(IsPlaceholderApp(profile(), kWebAppUrl)); EXPECT_EQ(1u, @@ -1008,12 +990,11 @@ task->Install( web_contents(), base::BindLambdaForTesting( - [&](absl::optional<AppId> installed_app_id, - ExternallyManagedAppManager::InstallResult result) { - app_id = *installed_app_id; + [&](ExternallyManagedAppManager::InstallResult result) { + app_id = result.app_id.value(); EXPECT_EQ(InstallResultCode::kSuccessNewInstall, result.code); - EXPECT_EQ(app_id, + EXPECT_EQ(result.app_id, *ExternallyInstalledWebAppPrefs(profile()->GetPrefs()) .LookupAppId(kWebAppUrl)); @@ -1037,10 +1018,9 @@ task->Install( web_contents(), base::BindLambdaForTesting( - [&](absl::optional<AppId> installed_app_id, - ExternallyManagedAppManager::InstallResult result) { + [&](ExternallyManagedAppManager::InstallResult result) { EXPECT_EQ(InstallResultCode::kSuccessNewInstall, result.code); - EXPECT_EQ(app_id, *installed_app_id); + EXPECT_EQ(app_id, result.app_id.value()); EXPECT_TRUE(ui_manager()->DidUninstallAndReplace("app3", app_id)); @@ -1076,8 +1056,7 @@ install_task.Install( web_contents(), base::BindLambdaForTesting( - [&](absl::optional<AppId> app_id, - ExternallyManagedAppManager::InstallResult result) { + [&](ExternallyManagedAppManager::InstallResult result) { EXPECT_EQ(result.code, result_pair.install_result); run_loop.Quit(); })); @@ -1100,8 +1079,7 @@ install_task.Install( web_contents(), base::BindLambdaForTesting( - [&](absl::optional<AppId>, - ExternallyManagedAppManager::InstallResult) { NOTREACHED(); })); + [&](ExternallyManagedAppManager::InstallResult) { NOTREACHED(); })); base::RunLoop().RunUntilIdle(); } @@ -1129,18 +1107,17 @@ base::RunLoop run_loop; task.Install( /*web_contents=*/nullptr, - base::BindLambdaForTesting([&](absl::optional<AppId> app_id, - ExternallyManagedAppManager::InstallResult + base::BindLambdaForTesting([&](ExternallyManagedAppManager::InstallResult result) { absl::optional<AppId> id = ExternallyInstalledWebAppPrefs(profile()->GetPrefs()) .LookupAppId(kWebAppUrl); EXPECT_EQ(InstallResultCode::kSuccessOfflineOnlyInstall, result.code); - EXPECT_TRUE(app_id.has_value()); + EXPECT_TRUE(result.app_id.has_value()); EXPECT_FALSE(IsPlaceholderApp(profile(), kWebAppUrl)); - EXPECT_EQ(app_id.value(), id.value()); + EXPECT_EQ(result.app_id.value(), id.value()); // Installing with an App Info doesn't call into OS Integration Manager. // This might be an issue for default apps. @@ -1182,15 +1159,14 @@ task.Install(web_contents(), base::BindLambdaForTesting( - [&](absl::optional<AppId> app_id, - ExternallyManagedAppManager::InstallResult result) { + [&](ExternallyManagedAppManager::InstallResult result) { absl::optional<AppId> id = ExternallyInstalledWebAppPrefs(profile()->GetPrefs()) .LookupAppId(kWebAppUrl); EXPECT_EQ(InstallResultCode::kWriteDataFailed, result.code); - EXPECT_FALSE(app_id.has_value()); + EXPECT_FALSE(result.app_id.has_value()); EXPECT_FALSE(id.has_value()); @@ -1217,11 +1193,10 @@ task->Install( web_contents(), - base::BindLambdaForTesting([&](absl::optional<AppId> app_id, - ExternallyManagedAppManager::InstallResult + base::BindLambdaForTesting([&](ExternallyManagedAppManager::InstallResult result) { EXPECT_EQ(InstallResultCode::kSuccessNewInstall, result.code); - EXPECT_TRUE(app_id.has_value()); + EXPECT_TRUE(result.app_id.has_value()); EXPECT_EQ(1u, os_integration_manager()->num_create_shortcuts_calls()); EXPECT_TRUE(os_integration_manager()->did_add_to_desktop().value()); @@ -1256,11 +1231,10 @@ task->Install( web_contents(), - base::BindLambdaForTesting([&](absl::optional<AppId> app_id, - ExternallyManagedAppManager::InstallResult + base::BindLambdaForTesting([&](ExternallyManagedAppManager::InstallResult result) { EXPECT_EQ(InstallResultCode::kSuccessNewInstall, result.code); - EXPECT_TRUE(app_id.has_value()); + EXPECT_TRUE(result.app_id.has_value()); EXPECT_EQ(1u, os_integration_manager()->num_create_shortcuts_calls()); EXPECT_TRUE(os_integration_manager()->did_add_to_desktop().value());
diff --git a/chrome/browser/web_applications/extensions/web_app_policy_manager_unittest.cc b/chrome/browser/web_applications/extensions/web_app_policy_manager_unittest.cc index 01530e39..cf7a582 100644 --- a/chrome/browser/web_applications/extensions/web_app_policy_manager_unittest.cc +++ b/chrome/browser/web_applications/extensions/web_app_policy_manager_unittest.cc
@@ -274,8 +274,7 @@ nullptr, nullptr); externally_managed_app_manager().SetHandleInstallRequestCallback( base::BindLambdaForTesting( - [this](const ExternalInstallOptions& install_options) - -> ExternallyManagedAppManager::InstallResult { + [this](const ExternalInstallOptions& install_options) { const GURL& install_url = install_options.install_url; if (!app_registrar().GetAppById(GenerateAppId( /*manifest_id=*/absl::nullopt, install_url))) { @@ -290,7 +289,8 @@ GenerateAppId(/*manifest_id=*/absl::nullopt, install_url), install_source); } - return {.code = install_result_code_}; + return ExternallyManagedAppManager::InstallResult( + install_result_code_); })); externally_managed_app_manager().SetHandleUninstallRequestCallback( base::BindLambdaForTesting(
diff --git a/chrome/browser/web_applications/externally_managed_app_install_task.cc b/chrome/browser/web_applications/externally_managed_app_install_task.cc index 39ad94d..3e699e5 100644 --- a/chrome/browser/web_applications/externally_managed_app_install_task.cc +++ b/chrome/browser/web_applications/externally_managed_app_install_task.cc
@@ -143,8 +143,8 @@ base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, - base::BindOnce(std::move(retry_on_failure), /*app_id=*/absl::nullopt, - ExternallyManagedAppManager::InstallResult{.code = code})); + base::BindOnce(std::move(retry_on_failure), + ExternallyManagedAppManager::InstallResult(code))); } void ExternallyManagedAppInstallTask::InstallFromInfo( @@ -196,8 +196,8 @@ LOG(ERROR) << "Failed to uninstall placeholder for: " << install_options_.install_url; std::move(result_callback) - .Run(/*app_id=*/absl::nullopt, - {.code = InstallResultCode::kFailedPlaceholderUninstall}); + .Run(ExternallyManagedAppManager::InstallResult( + InstallResultCode::kFailedPlaceholderUninstall)); return; } ContinueWebAppInstall(web_contents, std::move(result_callback)); @@ -228,9 +228,9 @@ // No need to install a placeholder app again. base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, - base::BindOnce(std::move(callback), app_id, - ExternallyManagedAppManager::InstallResult{ - .code = InstallResultCode::kSuccessNewInstall})); + base::BindOnce(std::move(callback), + ExternallyManagedAppManager::InstallResult( + InstallResultCode::kSuccessNewInstall, app_id))); return; } @@ -264,7 +264,8 @@ const AppId& app_id, InstallResultCode code) { if (!IsNewInstall(code)) { - std::move(result_callback).Run(/*app_id=*/absl::nullopt, {.code = code}); + std::move(result_callback) + .Run(ExternallyManagedAppManager::InstallResult(code)); return; } @@ -282,11 +283,10 @@ ? InstallResultCode::kSuccessOfflineOnlyInstall : InstallResultCode::kSuccessOfflineFallbackInstall; } - base::ScopedClosureRunner scoped_closure(base::BindOnce( - std::move(result_callback), app_id, - ExternallyManagedAppManager::InstallResult{ - .code = code, - .did_uninstall_and_replace = uninstall_and_replace_triggered})); + base::ScopedClosureRunner scoped_closure( + base::BindOnce(std::move(result_callback), + ExternallyManagedAppManager::InstallResult( + code, app_id, uninstall_and_replace_triggered))); if (!is_placeholder) { registrar_->NotifyWebAppInstalledWithOsHooks(app_id); @@ -337,13 +337,12 @@ void ExternallyManagedAppInstallTask::TryAppInfoFactoryOnFailure( ResultCallback result_callback, - absl::optional<AppId> app_id, ExternallyManagedAppManager::InstallResult result) { if (!IsSuccess(result.code) && install_options().app_info_factory) { InstallFromInfo(std::move(result_callback)); return; } - std::move(result_callback).Run(std::move(app_id), std::move(result)); + std::move(result_callback).Run(std::move(result)); } } // namespace web_app
diff --git a/chrome/browser/web_applications/externally_managed_app_install_task.h b/chrome/browser/web_applications/externally_managed_app_install_task.h index 437f2d7..60bb620 100644 --- a/chrome/browser/web_applications/externally_managed_app_install_task.h +++ b/chrome/browser/web_applications/externally_managed_app_install_task.h
@@ -40,7 +40,6 @@ class ExternallyManagedAppInstallTask { public: using ResultCallback = base::OnceCallback<void( - absl::optional<AppId> app_id, ExternallyManagedAppManager::InstallResult result)>; // Constructs a task that will install a Web App for |profile|. @@ -99,7 +98,6 @@ InstallResultCode code); void TryAppInfoFactoryOnFailure( ResultCallback result_callback, - absl::optional<AppId> app_id, ExternallyManagedAppManager::InstallResult result); void OnOsHooksCreated(const AppId& app_id, base::ScopedClosureRunner scoped_closure,
diff --git a/chrome/browser/web_applications/externally_managed_app_manager.cc b/chrome/browser/web_applications/externally_managed_app_manager.cc index 91bdc62ac..e836c5a 100644 --- a/chrome/browser/web_applications/externally_managed_app_manager.cc +++ b/chrome/browser/web_applications/externally_managed_app_manager.cc
@@ -18,10 +18,25 @@ namespace web_app { +ExternallyManagedAppManager::InstallResult::InstallResult() = default; + +ExternallyManagedAppManager::InstallResult::InstallResult( + InstallResultCode code, + absl::optional<AppId> app_id, + bool did_uninstall_and_replace) + : code(code), + app_id(std::move(app_id)), + did_uninstall_and_replace(did_uninstall_and_replace) {} + +ExternallyManagedAppManager::InstallResult::InstallResult( + const InstallResult&) = default; + +ExternallyManagedAppManager::InstallResult::~InstallResult() = default; + bool ExternallyManagedAppManager::InstallResult::operator==( const InstallResult& other) const { - return std::tie(code, did_uninstall_and_replace) == - std::tie(other.code, other.did_uninstall_and_replace); + return std::tie(code, app_id, did_uninstall_and_replace) == + std::tie(other.code, other.app_id, other.did_uninstall_and_replace); } ExternallyManagedAppManager::SynchronizeRequest::SynchronizeRequest(
diff --git a/chrome/browser/web_applications/externally_managed_app_manager.h b/chrome/browser/web_applications/externally_managed_app_manager.h index b917114..59ff6488 100644 --- a/chrome/browser/web_applications/externally_managed_app_manager.h +++ b/chrome/browser/web_applications/externally_managed_app_manager.h
@@ -53,9 +53,18 @@ class ExternallyManagedAppManager { public: struct InstallResult { - InstallResultCode code; - bool did_uninstall_and_replace = false; + InstallResult(); + explicit InstallResult(InstallResultCode code, + absl::optional<AppId> app_id = absl::nullopt, + bool did_uninstall_and_replace = false); + InstallResult(const InstallResult&); + ~InstallResult(); + bool operator==(const InstallResult& other) const; + + InstallResultCode code; + absl::optional<AppId> app_id; + bool did_uninstall_and_replace = false; }; using OnceInstallCallback =
diff --git a/chrome/browser/web_applications/externally_managed_app_manager_impl.cc b/chrome/browser/web_applications/externally_managed_app_manager_impl.cc index b2038ae..c4c411c 100644 --- a/chrome/browser/web_applications/externally_managed_app_manager_impl.cc +++ b/chrome/browser/web_applications/externally_managed_app_manager_impl.cc
@@ -195,8 +195,8 @@ // Otherwise no need to do anything. std::move(front->callback) .Run(install_options.install_url, - {.code = InstallResultCode::kSuccessAlreadyInstalled, - .did_uninstall_and_replace = false}); + ExternallyManagedAppManager::InstallResult( + InstallResultCode::kSuccessAlreadyInstalled, app_id)); continue; } @@ -207,8 +207,8 @@ !install_options.override_previous_user_uninstall) { std::move(front->callback) .Run(install_options.install_url, - {.code = InstallResultCode::kPreviouslyUninstalled, - .did_uninstall_and_replace = false}); + ExternallyManagedAppManager::InstallResult( + InstallResultCode::kPreviouslyUninstalled, app_id)); continue; } @@ -266,9 +266,8 @@ } void ExternallyManagedAppManagerImpl::OnInstalled( - absl::optional<AppId> app_id, ExternallyManagedAppManager::InstallResult result) { - if (app_id && IsSuccess(result.code)) { + if (result.app_id && IsSuccess(result.code)) { MaybeEnqueueServiceWorkerRegistration( current_install_->task->install_options()); }
diff --git a/chrome/browser/web_applications/externally_managed_app_manager_impl.h b/chrome/browser/web_applications/externally_managed_app_manager_impl.h index 48e854d..d886ee4 100644 --- a/chrome/browser/web_applications/externally_managed_app_manager_impl.h +++ b/chrome/browser/web_applications/externally_managed_app_manager_impl.h
@@ -82,8 +82,7 @@ void CreateWebContentsIfNecessary(); - void OnInstalled(absl::optional<AppId> app_id, - ExternallyManagedAppManager::InstallResult result); + void OnInstalled(ExternallyManagedAppManager::InstallResult result); void MaybeEnqueueServiceWorkerRegistration( const ExternalInstallOptions& install_options);
diff --git a/chrome/browser/web_applications/externally_managed_app_manager_impl_unittest.cc b/chrome/browser/web_applications/externally_managed_app_manager_impl_unittest.cc index b3c15f0..6d9a6d86 100644 --- a/chrome/browser/web_applications/externally_managed_app_manager_impl_unittest.cc +++ b/chrome/browser/web_applications/externally_managed_app_manager_impl_unittest.cc
@@ -311,7 +311,8 @@ install_url, result.did_install_placeholder); } } - std::move(callback).Run(app_id, {.code = result.code}); + std::move(callback).Run( + ExternallyManagedAppManager::InstallResult(result.code, app_id)); } void Install(content::WebContents* web_contents,
diff --git a/chrome/browser/web_applications/externally_managed_app_manager_unittest.cc b/chrome/browser/web_applications/externally_managed_app_manager_unittest.cc index a892bfe..2c45af0 100644 --- a/chrome/browser/web_applications/externally_managed_app_manager_unittest.cc +++ b/chrome/browser/web_applications/externally_managed_app_manager_unittest.cc
@@ -56,7 +56,8 @@ install_options.install_source); ++deduped_install_count_; } - return {.code = InstallResultCode::kSuccessNewInstall}; + return ExternallyManagedAppManager::InstallResult( + InstallResultCode::kSuccessNewInstall); })); externally_managed_app_manager().SetHandleUninstallRequestCallback( base::BindLambdaForTesting(
diff --git a/chrome/browser/web_applications/system_web_apps/test/system_web_app_manager_unittest.cc b/chrome/browser/web_applications/system_web_apps/test/system_web_app_manager_unittest.cc index e97413a..ac200024 100644 --- a/chrome/browser/web_applications/system_web_apps/test/system_web_app_manager_unittest.cc +++ b/chrome/browser/web_applications/system_web_apps/test/system_web_app_manager_unittest.cc
@@ -570,7 +570,8 @@ base::BindLambdaForTesting( [](const ExternalInstallOptions&) -> ExternallyManagedAppManager::InstallResult { - return {.code = InstallResultCode::kWebAppDisabled}; + return ExternallyManagedAppManager::InstallResult( + InstallResultCode::kWebAppDisabled); })); { @@ -673,8 +674,10 @@ [](const ExternalInstallOptions& opts) -> ExternallyManagedAppManager::InstallResult { if (opts.install_url == AppUrl1()) - return {.code = InstallResultCode::kSuccessAlreadyInstalled}; - return {.code = InstallResultCode::kSuccessNewInstall}; + return ExternallyManagedAppManager::InstallResult( + InstallResultCode::kSuccessAlreadyInstalled); + return ExternallyManagedAppManager::InstallResult( + InstallResultCode::kSuccessNewInstall); })); StartAndWaitForAppsToSynchronize(); @@ -711,8 +714,10 @@ [](const ExternalInstallOptions& opts) -> ExternallyManagedAppManager::InstallResult { if (opts.install_url == AppUrl1()) - return {.code = InstallResultCode::kWriteDataFailed}; - return {.code = InstallResultCode::kSuccessNewInstall}; + return ExternallyManagedAppManager::InstallResult( + InstallResultCode::kWriteDataFailed); + return ExternallyManagedAppManager::InstallResult( + InstallResultCode::kSuccessNewInstall); })); StartAndWaitForAppsToSynchronize(); @@ -729,8 +734,10 @@ [](const ExternalInstallOptions& opts) -> ExternallyManagedAppManager::InstallResult { if (opts.install_url == AppUrl1()) - return {.code = InstallResultCode::kSuccessNewInstall}; - return {.code = InstallResultCode::kSuccessAlreadyInstalled}; + return ExternallyManagedAppManager::InstallResult( + InstallResultCode::kSuccessNewInstall); + return ExternallyManagedAppManager::InstallResult( + InstallResultCode::kSuccessAlreadyInstalled); })); StartAndWaitForAppsToSynchronize();
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt index 71f2319..32a5aca 100644 --- a/chrome/build/linux.pgo.txt +++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@ -chrome-linux-main-1638724060-e6f373a05908b00f1c2f316f0b19f0ec63b876c4.profdata +chrome-linux-main-1638791758-710584749cfa00b31ebd36c723626f0698441c55.profdata
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt index aa08d63c..a5c6a801 100644 --- a/chrome/build/mac.pgo.txt +++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@ -chrome-mac-main-1638724060-358ec6b74dda05eaefd8ed9d48ef10fa35093a41.profdata +chrome-mac-main-1638770344-9dbbf7c9a76542cc8e8c5ea3a9071779318668c9.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt index 6098372..5c0a427d 100644 --- a/chrome/build/win32.pgo.txt +++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@ -chrome-win32-main-1638724060-37a6a09224f456939feefe4695fe1c8e0de62760.profdata +chrome-win32-main-1638770344-40f9a255708f8198ddcfdaa19d2b66fecaf16a3b.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt index 8e9b894bb..225c433d 100644 --- a/chrome/build/win64.pgo.txt +++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@ -chrome-win64-main-1638724060-67a80ed6fb84adc51ce8704737b991f4b6802506.profdata +chrome-win64-main-1638791758-eb339a4119ba192eb551d5cc7bcb131e21125867.profdata
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn index f97ad88d..b165c09 100644 --- a/chrome/test/BUILD.gn +++ b/chrome/test/BUILD.gn
@@ -2055,7 +2055,6 @@ "../browser/ui/webui/support_tool_ui_browsertest.cc", "../browser/ui/webui/tab_search/tab_search_ui_browsertest.cc", "../browser/ui/webui/webui_load_timer_browsertest.cc", - "../browser/ui/webui/webui_webview_browsertest.cc", "../browser/ui/zoom/zoom_controller_browsertest.cc", "../browser/ukm_worker_browsertest.cc", "../browser/unload_browsertest.cc", @@ -2137,6 +2136,9 @@ # https://crbug.com/1252812 The intent picker (launch icon) actions # are not working on Lacros. "../browser/ui/views/web_apps/web_app_integration_browsertest.cc", + + # Lacros does not seem to have any actual WebView-based UI to test. + "../browser/ui/webui/webui_webview_browsertest.cc", ] } @@ -2356,7 +2358,6 @@ "../browser/policy/test/hardware_acceleration_mode_enabled_browsertest.cc", "../browser/policy/test/variation_restrict_parameter_policy_browsertest.cc", "../browser/profiles/profile_window_browsertest.cc", - "../browser/ui/signin_reauth_view_controller_browsertest.cc", "../browser/ui/views/accessibility/accessibility_focus_highlight_browsertest.cc", "../browser/ui/views/profiles/signin_view_controller_delegate_views_browsertest.cc", ] @@ -2447,17 +2448,13 @@ "../browser/signin/dice_browsertest.cc", "../browser/signin/dice_web_signin_interceptor_browsertest.cc", "../browser/signin/signin_ui_util_browsertest.cc", + "../browser/ui/signin_reauth_view_controller_browsertest.cc", + "../browser/ui/views/sync/inline_login_ui_browsertest.cc", "../browser/unified_consent/unified_consent_browsertest.cc", ] if (is_win) { sources += [ "../browser/signin/signin_util_win_browsertest.cc" ] } - - # TODO(https://crbug.com/1198523: Remove this once enable_dice_support is - # no longer defined on Lacros. - if (is_chromeos_lacros) { - sources -= [ "../browser/signin/signin_ui_util_browsertest.cc" ] - } } else { sources += [ "../browser/signin/mirror_browsertest.cc" ] } @@ -2986,7 +2983,6 @@ "../browser/ui/views/send_tab_to_self/send_tab_to_self_bubble_view_impl_browsertest.cc", "../browser/ui/views/session_crashed_bubble_view_browsertest.cc", "../browser/ui/views/status_bubble_views_browsertest.cc", - "../browser/ui/views/sync/inline_login_ui_browsertest.cc", "../browser/ui/views/sync/profile_signin_confirmation_dialog_views_browsertest.cc", "../browser/ui/views/tab_sharing/tab_sharing_ui_views_browsertest.cc", "../browser/ui/views/tabs/tab_group_editor_bubble_view_browsertest.cc", @@ -3655,9 +3651,6 @@ # chromeos does not use the profile chooser view "../browser/ui/views/profiles/profile_menu_view_browsertest.cc", - - # inline login UI is disabled on chromeos - "../browser/ui/views/sync/inline_login_ui_browsertest.cc", "../browser/ui/views/sync/profile_signin_confirmation_dialog_views_browsertest.cc", "../browser/ui/webui/profile_helper_browsertest.cc", @@ -5722,19 +5715,10 @@ "../browser/signin/process_dice_header_delegate_impl_unittest.cc", "../browser/signin/signin_manager_unittest.cc", "../browser/ui/passwords/account_storage_auth_helper_unittest.cc", + "../browser/ui/startup/startup_browser_policy_unittest.cc", "../browser/ui/views/profiles/dice_web_signin_interception_bubble_view_unittest.cc", "../browser/ui/views/profiles/profile_customization_bubble_sync_controller_unittest.cc", ] - - # TODO(https://crbug.com/1198523: Remove this once enable_dice_support is no - # longer defined on Lacros. - if (is_chromeos_lacros) { - sources -= [ - "../browser/password_manager/multi_profile_credentials_filter_unittest.cc", - "../browser/signin/dice_web_signin_interceptor_unittest.cc", - "../browser/signin/signin_manager_unittest.cc", - ] - } } if (is_win || is_mac || (is_linux || is_chromeos_lacros)) { @@ -7310,9 +7294,6 @@ "../browser/ui/startup/startup_tab_provider_unittest.cc", ] } - if (!is_android && !is_chromeos_ash) { - sources += [ "../browser/ui/startup/startup_browser_policy_unittest.cc" ] - } if (use_gio) { configs += [ "//build/linux:gio_config" ]
diff --git a/chrome/test/data/devtools/regress-crbug-1270184.html b/chrome/test/data/devtools/regress-crbug-1270184.html new file mode 100644 index 0000000..2f633d60 --- /dev/null +++ b/chrome/test/data/devtools/regress-crbug-1270184.html
@@ -0,0 +1,9 @@ +<script> + print(); +</script> +<iframe srcdoc="Hi"></iframe> +<script> + frames[0].onunload = function () { + document.open(); + }; +</script> \ No newline at end of file
diff --git a/chrome/test/data/webui/BUILD.gn b/chrome/test/data/webui/BUILD.gn index 14629b6..330927ce 100644 --- a/chrome/test/data/webui/BUILD.gn +++ b/chrome/test/data/webui/BUILD.gn
@@ -8,6 +8,7 @@ import("//chrome/test/base/js2gtest.gni") import("//chrome/test/include_js_tests.gni") import("//chromeos/components/chromebox_for_meetings/buildflags/buildflags.gni") +import("//components/signin/features.gni") import("//printing/buildflags/buildflags.gni") import("//third_party/closure_compiler/compile_js.gni") import("//tools/grit/grit_rule.gni") @@ -161,8 +162,11 @@ "webview_manager/webview_manager_browsertest.js", ] } else { + sources += [ "signin/signin_browsertest.js" ] + } + + if (enable_dice_support) { sources += [ - "signin/signin_browsertest.js", "welcome/a11y_tests.js", "welcome/welcome_browsertest.js", ] @@ -186,7 +190,7 @@ if (!is_android) { sources += [ "access_code_cast/access_code_cast_browsertest.js", - "commander/commander_browsertest.js" + "commander/commander_browsertest.js", ] } if (is_cfm) { @@ -507,7 +511,6 @@ "js/cr:closure_compile", "media_router:closure_compile", "new_tab_page:closure_compile", - "tab_search:closure_compile", # TODO(crbug.com/1000989): Add page specific targets here. ]
diff --git a/chrome/test/data/webui/settings/chromeos/os_privacy_page_test.js b/chrome/test/data/webui/settings/chromeos/os_privacy_page_test.js index 7b08a9a..534e29b 100644 --- a/chrome/test/data/webui/settings/chromeos/os_privacy_page_test.js +++ b/chrome/test/data/webui/settings/chromeos/os_privacy_page_test.js
@@ -241,6 +241,10 @@ }); test('Deep link to snooping protection on smart privacy page', async () => { + loadTimeData.overrideValues({ + isSnoopingProtectionEnabled: true, + }); + const params = new URLSearchParams; params.append('settingId', '1114'); settings.Router.getInstance().navigateTo( @@ -259,6 +263,10 @@ }); test('Deep link to quick dim on smart privacy page', async () => { + loadTimeData.overrideValues({ + isQuickDimEnabled: true, + }); + const params = new URLSearchParams; params.append('settingId', '1115'); settings.Router.getInstance().navigateTo( @@ -330,9 +338,10 @@ }); - test('Smart privacy hidden when feature disabled', async () => { + test('Smart privacy hidden when both features disabled', async () => { loadTimeData.overrideValues({ - isSmartPrivacyEnabled: false, + isSnoopingProtectionEnabled: false, + isQuickDimEnabled: false, }); privacyPage = document.createElement('os-settings-privacy-page'); @@ -343,9 +352,24 @@ assertFalse(elementExists('#smartPrivacySubpageTrigger')); }); - test('Smart privacy shown when feature enabled', async () => { + test('Smart privacy shown when only quick dim enabled', async () => { loadTimeData.overrideValues({ - isSmartPrivacyEnabled: true, + isSnoopingProtectionEnabled: false, + isQuickDimEnabled: true, + }); + + privacyPage = document.createElement('os-settings-privacy-page'); + document.body.appendChild(privacyPage); + + await test_util.waitAfterNextRender(privacyPage); + + assertTrue(elementExists('#smartPrivacySubpageTrigger')); + }); + + test('Smart privacy shown if only snooping protection enabled', async () => { + loadTimeData.overrideValues({ + isSnoopingProtectionEnabled: true, + isQuickDimEnabled: false, }); privacyPage = document.createElement('os-settings-privacy-page');
diff --git a/chrome/test/data/webui/settings/chromeos/smart_privacy_subpage_tests.js b/chrome/test/data/webui/settings/chromeos/smart_privacy_subpage_tests.js index 70f995f..05fb815f 100644 --- a/chrome/test/data/webui/settings/chromeos/smart_privacy_subpage_tests.js +++ b/chrome/test/data/webui/settings/chromeos/smart_privacy_subpage_tests.js
@@ -5,6 +5,7 @@ // clang-format off // #import 'chrome://os-settings/chromeos/lazy_load.js'; +// #import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js'; // #import {CrSettingsPrefs, Router} from 'chrome://os-settings/chromeos/os_settings.js'; // #import {flush} from'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js'; // #import {assertFalse, assertTrue} from '../../chai_assert.js'; @@ -36,8 +37,12 @@ } setup(async () => { - PolymerTest.clearBody(); + // Options aren't shown unless the snooping protection feature is enabled. + loadTimeData.overrideValues({ + isSnoopingProtectionEnabled: true, + }); + PolymerTest.clearBody(); smartPrivacySubpage = document.createElement('settings-smart-privacy-page'); assertTrue(!!smartPrivacySubpage); smartPrivacySubpage.prefs = makePrefs(false); @@ -50,8 +55,10 @@ }); test('Snooping radio list visibility tied to pref', async () => { + // The $ method won't find elements inside templates. /** @type {?HTMLElement} */ - const collapse = assert(smartPrivacySubpage.$.snoopingProtectionOptions); + const collapse = smartPrivacySubpage.shadowRoot.querySelector( + '#snoopingProtectionOptions'); // Default pref value is false. assertFalse(collapse.opened);
diff --git a/chrome/test/data/webui/signin/signin_browsertest.js b/chrome/test/data/webui/signin/signin_browsertest.js index 6f81bce..1108432 100644 --- a/chrome/test/data/webui/signin/signin_browsertest.js +++ b/chrome/test/data/webui/signin/signin_browsertest.js
@@ -38,6 +38,7 @@ mocha.run(); }); +GEN('#if !BUILDFLAG(IS_CHROMEOS_LACROS)'); /** * Test fixture for * chrome/browser/resources/signin/signin_reauth/signin_reauth.html. @@ -70,6 +71,7 @@ TEST_F('DiceWebSigninInterceptTest', 'Bubble', function() { mocha.run(); }); +GEN('#endif // !BUILDFLAG(IS_CHROMEOS_LACROS)'); /** * Test fixture for
diff --git a/chrome/test/data/webui/tab_search/BUILD.gn b/chrome/test/data/webui/tab_search/BUILD.gn deleted file mode 100644 index be58239..0000000 --- a/chrome/test/data/webui/tab_search/BUILD.gn +++ /dev/null
@@ -1,93 +0,0 @@ -# Copyright 2020 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -import("//third_party/closure_compiler/compile_js.gni") - -js_type_check("closure_compile") { - is_polymer3 = true - closure_flags = default_closure_args + mojom_js_args + [ - "browser_resolver_prefix_replacements=\"chrome://tab-search.top-chrome/=./\"", - "js_module_root=" + - rebase_path("//chrome/browser/resources/tab_search", - root_build_dir), - "js_module_root=" + - rebase_path("//chrome/test/data/webui", root_build_dir), - "js_module_root=" + - rebase_path("$root_gen_dir/chrome/test/data/webui", - root_build_dir), - "js_module_root=" + rebase_path( - "$root_gen_dir/mojom-webui/chrome/browser/ui/webui/tab_search", - root_build_dir), - "js_module_root=" + rebase_path( - "$root_gen_dir/mojom-webui/components/tab_groups/public/mojom", - root_build_dir), - "js_module_root=" + rebase_path( - "$root_gen_dir/mojom-webui/chrome/browser/ui/webui/tabs", - root_build_dir), - ] - deps = [ - ":bimap_test", - ":fuzzy_search_test", - ":infinite_list_test", - ":tab_search_app_focus_test", - ":tab_search_app_test", - ":tab_search_item_test", - ] -} - -js_library("bimap_test") { - deps = [ "..:chai_assert" ] - externs_list = [ "$externs_path/mocha-2.5.js" ] -} - -js_library("fuzzy_search_test") { - deps = [ - ":test_tab_search_api_proxy", - "..:chai_assert", - ] - externs_list = [ "$externs_path/mocha-2.5.js" ] -} - -js_library("infinite_list_test") { - deps = [ - "..:chai_assert", - "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled", - ] - externs_list = [ "$externs_path/mocha-2.5.js" ] -} - -js_library("tab_search_app_test") { - deps = [ - ":test_tab_search_api_proxy", - "..:chai_assert", - "//chrome/browser/resources/tab_search:tab_search", - ] - externs_list = [ "$externs_path/mocha-2.5.js" ] -} - -js_library("tab_search_app_focus_test") { - deps = [ - ":test_tab_search_api_proxy", - "..:chai_assert", - "//chrome/browser/resources/tab_search:tab_search", - ] - externs_list = [ "$externs_path/mocha-2.5.js" ] -} - -js_library("test_tab_search_api_proxy") { - deps = [ - "..:test_browser_proxy", - "//chrome/browser/resources/tab_search:tab_search", - "//chrome/browser/ui/webui/tab_search:mojo_bindings_js_library_for_compile", - ] -} - -js_library("tab_search_item_test") { - deps = [ - "..:chai_assert", - "//chrome/browser/resources/tab_search:tab_search", - "//chrome/browser/ui/webui/tabs:mojo_bindings_webui_js", - ] - externs_list = [ "$externs_path/mocha-2.5.js" ] -}
diff --git a/chrome/test/data/webui/tab_search/bimap_test.js b/chrome/test/data/webui/tab_search/bimap_test.js index 1216956f..edc62bc 100644 --- a/chrome/test/data/webui/tab_search/bimap_test.js +++ b/chrome/test/data/webui/tab_search/bimap_test.js
@@ -7,13 +7,7 @@ suite('BiMapTest', () => { test('Base', async () => { - let biMap = new BiMap({ - a: 1, - b: 2, - }); - assertEquals(2, biMap.size()); - - biMap = new BiMap(); + const biMap = new BiMap(); biMap.set('a', 1); biMap.set('b', 2); assertEquals(2, biMap.size());
diff --git a/chrome/test/data/webui/tab_search/tab_search_app_focus_test.js b/chrome/test/data/webui/tab_search/tab_search_app_focus_test.js index 65365ae..1002ee71 100644 --- a/chrome/test/data/webui/tab_search/tab_search_app_focus_test.js +++ b/chrome/test/data/webui/tab_search/tab_search_app_focus_test.js
@@ -29,7 +29,7 @@ async function setupTest(sampleData, loadTimeOverriddenData) { testProxy = new TestTabSearchApiProxy(); testProxy.setProfileData(sampleData); - TabSearchApiProxyImpl.instance_ = testProxy; + TabSearchApiProxyImpl.setInstance(testProxy); initLoadTimeDataWithDefaults(loadTimeOverriddenData);
diff --git a/chrome/test/data/webui/tab_search/tab_search_app_test.js b/chrome/test/data/webui/tab_search/tab_search_app_test.js index 2de92579..b6971c77 100644 --- a/chrome/test/data/webui/tab_search/tab_search_app_test.js +++ b/chrome/test/data/webui/tab_search/tab_search_app_test.js
@@ -48,7 +48,7 @@ testProxy = new TestTabSearchApiProxy(); testProxy.setProfileData(/** @type {ProfileData} */ (sampleData)); - TabSearchApiProxyImpl.instance_ = testProxy; + TabSearchApiProxyImpl.setInstance(testProxy); tabSearchApp = /** @type {!TabSearchAppElement} */ (document.createElement('tab-search-app'));
diff --git a/chrome/test/data/webui/tab_search/test_tab_search_api_proxy.js b/chrome/test/data/webui/tab_search/test_tab_search_api_proxy.js index 4612b67..e77f829 100644 --- a/chrome/test/data/webui/tab_search/test_tab_search_api_proxy.js +++ b/chrome/test/data/webui/tab_search/test_tab_search_api_proxy.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 {PageCallbackRouter, PageRemote, ProfileData, TabSearchApiProxy} from 'chrome://tab-search.top-chrome/tab_search.js'; +import {PageCallbackRouter, PageRemote, ProfileData} from 'chrome://tab-search.top-chrome/tab_search.js'; import {TestBrowserProxy} from '../test_browser_proxy.js';
diff --git a/chromeos/CHROMEOS_LKGM b/chromeos/CHROMEOS_LKGM index 3af709f..796b9e1 100644 --- a/chromeos/CHROMEOS_LKGM +++ b/chromeos/CHROMEOS_LKGM
@@ -1 +1 @@ -14380.0.0 \ No newline at end of file +14381.0.0 \ No newline at end of file
diff --git a/chromeos/strings/chromeos_strings_eu.xtb b/chromeos/strings/chromeos_strings_eu.xtb index 130c2ce..4ea8f603 100644 --- a/chromeos/strings/chromeos_strings_eu.xtb +++ b/chromeos/strings/chromeos_strings_eu.xtb
@@ -17,6 +17,7 @@ <translation id="1175951029573070619">Normala (<ph name="SIGNAL_STRENGTH" />)</translation> <translation id="1181037720776840403">Kendu</translation> <translation id="1195447618553298278">Errore ezezaguna.</translation> +<translation id="1196959502276349371"><ph name="VERSION" /> bertsioa</translation> <translation id="1201402288615127009">Hurrengoa</translation> <translation id="1204296502688602597">DNSaren latentzia</translation> <translation id="123124571410524056">Atari bat dagoela dirudi</translation>
diff --git a/chromeos/strings/chromeos_strings_km.xtb b/chromeos/strings/chromeos_strings_km.xtb index d3314b9..b98fce8 100644 --- a/chromeos/strings/chromeos_strings_km.xtb +++ b/chromeos/strings/chromeos_strings_km.xtb
@@ -183,6 +183,7 @@ <translation id="3858860766373142691">ឈ្មោះ</translation> <translation id="3865414814144988605">គុណភាពបង្ហាញ</translation> <translation id="387301095347517405">ចំនួនដងដែលថ្មរបស់អ្នកបានសាកពេញ</translation> +<translation id="3924044641767672375">ការតភ្ជាប់ Wi-Fi ត្រូវបានណែនាំ ដើម្បីជួយឱ្យដំណើរការជួសជុលកាន់តែងាយស្រួល។ បណ្ដាញរបស់អ្នកនឹងត្រូវបានរក្សាទុក។</translation> <translation id="3932043219784172185">មិនបានភ្ជាប់ឧបករណ៍ណាមួយទេ</translation> <translation id="3941014780699102620">មិនអាចដោះស្រាយម៉ាស៊ីនបានទេ</translation> <translation id="3942420633017001071">ការវិភាគ</translation> @@ -227,6 +228,7 @@ <translation id="4472575034687746823">ចាប់ផ្ដើម</translation> <translation id="4479639480957787382">ខ្សែអ៊ីនធឺណិត</translation> <translation id="4483049906298469269">មិនអាចភីងច្រកបណ្ដាញមិនមែនលំនាំដើមបានទេ</translation> +<translation id="4485626319513081846">ជ្រើសរើសជម្រើសនេះ ប្រសិនបើឧបករណ៍នេះកំពុងត្រូវបានបញ្ចូលស្តុកវិញ ឬត្រូវបានកែលម្អថ្មីសម្រាប់ចែកចាយទៅអតិថិជនថ្មី។</translation> <translation id="4511264077854731334">ច្រក</translation> <translation id="4521826082652183069">ភាពត្រូវគ្នានៃឈ្មោះជំនួសនៃប្រធានបទ</translation> <translation id="4536864596629708641">ការកំណត់រចនាសម្ព័ន្ធ IP</translation> @@ -415,6 +417,7 @@ <translation id="6957231940976260713">ឈ្មោះសេវាកម្ម</translation> <translation id="6961170852793647506">ដើម្បីចាប់ផ្ដើម សូមដាក់ឯកសាររបស់អ្នកនៅលើម៉ាស៊ីនស្កេន</translation> <translation id="6977381486153291903">ការកែប្រែកម្មវិធីបង្កប់</translation> +<translation id="6992266763844448459">ជ្រើសរើសជម្រើសនេះ ប្រសិនបើកម្មសិទ្ធិឧបករណ៍នេះមិនផ្លាស់ប្ដូរទេ។ ឧទាហរណ៍ ឧបករណ៍នេះកំពុងត្រូវបានផ្ទេរទៅបុគ្គលផ្សេងនៅក្នុងស្ថាប័ន។</translation> <translation id="7028979494427204405"><ph name="MANAGER" /> គ្រប់គ្រងឧបករណ៍នេះ និងមានសិទ្ធិចូលមើលសកម្មភាពអ្នកប្រើប្រាស់ទាំងអស់ រួមទាំងទំព័របណ្ដាញដែលបានចូលមើល ពាក្យសម្ងាត់ និងអ៊ីមែលផងដែរ។</translation> <translation id="7040230719604914234">ប្រតិបត្តិករ</translation> <translation id="7058278511608979688">បញ្ចប់ និងរក្សាទុក</translation>
diff --git a/chromeos/strings/chromeos_strings_ko.xtb b/chromeos/strings/chromeos_strings_ko.xtb index e696429..0471b495 100644 --- a/chromeos/strings/chromeos_strings_ko.xtb +++ b/chromeos/strings/chromeos_strings_ko.xtb
@@ -17,6 +17,7 @@ <translation id="1175951029573070619">평균(<ph name="SIGNAL_STRENGTH" />)</translation> <translation id="1181037720776840403">삭제</translation> <translation id="1195447618553298278">알 수 없는 오류</translation> +<translation id="1196959502276349371">버전 <ph name="VERSION" /></translation> <translation id="1201402288615127009">다음</translation> <translation id="1204296502688602597">DNS 지연 시간</translation> <translation id="123124571410524056">포털 의심됨</translation>
diff --git a/chromeos/strings/chromeos_strings_tr.xtb b/chromeos/strings/chromeos_strings_tr.xtb index c719beb..f3514f9 100644 --- a/chromeos/strings/chromeos_strings_tr.xtb +++ b/chromeos/strings/chromeos_strings_tr.xtb
@@ -183,6 +183,7 @@ <translation id="3858860766373142691">Ad</translation> <translation id="3865414814144988605">Çözünürlük</translation> <translation id="387301095347517405">Pilinizin tam şarj döngüsünden geçme sayısı</translation> +<translation id="3924044641767672375">Onarım sürecini kolaylaştırmak için kablosuz ağa bağlanmanız önerilir. Ağınız kaydedilir.</translation> <translation id="3932043219784172185">Bağlı cihaz yok</translation> <translation id="3941014780699102620">Ana makine çözümlenemedi</translation> <translation id="3942420633017001071">Teşhis</translation> @@ -227,6 +228,7 @@ <translation id="4472575034687746823">Başlayın</translation> <translation id="4479639480957787382">Ethernet</translation> <translation id="4483049906298469269">Varsayılan olmayan ağ geçidi pinglenemedi</translation> +<translation id="4485626319513081846">Cihaz yeni bir müşteriye dağıtılmak üzere yenileniyor veya tekrar stoklara ekleniyorsa bu seçeneği belirleyin.</translation> <translation id="4511264077854731334">Portal</translation> <translation id="4521826082652183069">Alternatif konu adı eşleşmesi</translation> <translation id="4536864596629708641">IP Yapılandırması</translation> @@ -416,6 +418,7 @@ <translation id="6957231940976260713">Hizmet adı</translation> <translation id="6961170852793647506">Başlamak için dokümanınızı tarayıcıya yerleştirin</translation> <translation id="6977381486153291903">Donanım yazılımı düzeltmesi</translation> +<translation id="6992266763844448459">Cihazın sahibi değişmiyorsa, örneğin cihaz aynı kuruluştaki başka bir kişiye devrediliyorsa bu seçeneği belirleyin.</translation> <translation id="7028979494427204405"><ph name="MANAGER" /> bu cihazı yönetir ve ziyaret edilen web sayfaları, şifreler ve e-posta dahil tüm kullanıcı etkinliğine erişebilir.</translation> <translation id="7040230719604914234">Operatör</translation> <translation id="7058278511608979688">Bitir ve kaydet</translation>
diff --git a/chromeos/system/name_value_pairs_parser.cc b/chromeos/system/name_value_pairs_parser.cc index 76356322..d311c81 100644 --- a/chromeos/system/name_value_pairs_parser.cc +++ b/chromeos/system/name_value_pairs_parser.cc
@@ -5,13 +5,15 @@ #include "chromeos/system/name_value_pairs_parser.h" #include <stddef.h> +#include <unistd.h> #include "base/command_line.h" #include "base/files/file_path.h" #include "base/files/file_util.h" #include "base/logging.h" #include "base/process/launch.h" -#include "base/strings/string_tokenizer.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/string_split.h" #include "base/strings/string_util.h" #include "base/system/sys_info.h" @@ -20,6 +22,12 @@ namespace { +// The name of the stable device secret VPD key. +const char kStableDeviceSecretDoNotShare[] = + "stable_device_secret_DO_NOT_SHARE"; + +// Runs a tool and capture its standard output into |output|. Returns false +// if the tool cannot be run. bool GetToolOutput(int argc, const char* argv[], std::string* output) { DCHECK_GE(argc, 1); @@ -39,19 +47,110 @@ return true; } +// Assigns a non quoted version of |input| to |unquoted|, and returns +// whether |input| was actually quoted or not. +bool GetUnquotedString(const std::string& input, std::string* unquoted) { + const size_t input_size = input.size(); + + if (input_size >= 2 && input[0] == '"' && input[input_size - 1] == '"') + unquoted->assign(input, 1, input_size - 2); + else + unquoted->assign(input); + + return unquoted->size() != input_size; +} + +// Assigns a non commented version of |input| to |uncommented|. Whitespace +// before the comment is trimmed. +void GetUncommentedString(const std::string& input, std::string* uncommented) { + const size_t comment_pos = input.find('#'); + if (comment_pos == std::string::npos) { + uncommented->assign(input); + } else { + uncommented->assign(input, 0, comment_pos); + base::TrimWhitespaceASCII(*uncommented, base::TRIM_TRAILING, uncommented); + } +} + +// Parse a name from |input|, validating that it is in |format|, and assign it +// to |name|. +bool ParseName(const std::string& input, + system::NameValuePairsFormat format, + std::string* name) { + bool parsed_ok = false; + switch (format) { + case NameValuePairsFormat::kVpdDump: + // The name must be quoted, and we unquote it. + parsed_ok = GetUnquotedString(input, name); + break; + case NameValuePairsFormat::kMachineInfo: + // The name may be quoted, and we unquote it. + GetUnquotedString(input, name); + parsed_ok = true; + break; + case NameValuePairsFormat::kCrossystem: + // We trim all ASCII whitespace and the name then must not be quoted. + base::TrimWhitespaceASCII(input, base::TRIM_ALL, name); + parsed_ok = !GetUnquotedString(*name, name); + break; + } + + // Names must not be empty in addition to having parsed successfully. + return parsed_ok && !name->empty(); +} + +// Parse a value from |input|, validating that it is in |format|, and assign it +// to |name|. +bool ParseValue(const std::string& input, + system::NameValuePairsFormat format, + std::string* value) { + if (format == NameValuePairsFormat::kCrossystem) { + // The crossystem format allows for comments, remove them. + GetUncommentedString(input, value); + // We trim all ASCII whitespace and preserve the rest as is. + base::TrimWhitespaceASCII(*value, base::TRIM_ALL, value); + return true; + } else { + // The value must be quoted, and we unquote it. + return GetUnquotedString(input, value); + } +} + +// Return a string for logging a value that does not expose data that should +// not be visible in the logs. +std::string GetLoggingStringForValue(const std::string& name, + const std::string& value) { + // TODO(crbug.com/1250037): Delete special casing of secret after fixing. + if (name == kStableDeviceSecretDoNotShare) + return "value edited out for security"; + return "value: " + value; +} + +const char* GetNameValuePairsFormatName(NameValuePairsFormat format) { + switch (format) { + case NameValuePairsFormat::kVpdDump: + return "VPD dump"; + case NameValuePairsFormat::kMachineInfo: + return "machine info"; + case NameValuePairsFormat::kCrossystem: + return "crossystem"; + } + return "unknown"; +} + } // namespace NameValuePairsParser::NameValuePairsParser(NameValueMap* map) : map_(map) { } -bool NameValuePairsParser::GetNameValuePairsFromFile( +bool NameValuePairsParser::ParseNameValuePairsFromFile( const base::FilePath& file_path, - const std::string& eq, - const std::string& delim) { - std::string contents; - if (base::ReadFileToString(file_path, &contents)) { - return ParseNameValuePairs(contents, eq, delim); + NameValuePairsFormat format) { + std::string file_contents; + if (base::ReadFileToString(file_path, &file_contents)) { + return ParseNameValuePairs(file_contents, format, + /*debug_source=*/file_path.value()); } else { if (base::SysInfo::IsRunningOnChromeOS()) VLOG(1) << "Statistics file not present: " << file_path.value(); @@ -62,15 +161,14 @@ bool NameValuePairsParser::ParseNameValuePairsFromTool( int argc, const char* argv[], - const std::string& eq, - const std::string& delim, - const std::string& comment_delim) { + NameValuePairsFormat format) { + DCHECK_GE(argc, 1); + std::string output_string; if (!GetToolOutput(argc, argv, &output_string)) return false; - return ParseNameValuePairsWithComments(output_string, eq, delim, - comment_delim); + return ParseNameValuePairs(output_string, format, /*debug_source=*/argv[0]); } void NameValuePairsParser::DeletePairsWithValue(const std::string& value) { @@ -84,65 +182,61 @@ } } -void NameValuePairsParser::AddNameValuePair(const std::string& key, +void NameValuePairsParser::AddNameValuePair(const std::string& name, const std::string& value) { - const auto it = map_->find(key); + const auto it = map_->find(name); if (it == map_->end()) { - (*map_)[key] = value; - VLOG(1) << "name: " << key << ", value: " << value; + (*map_)[name] = value; + VLOG(1) << "Name: " << name << ", " + << GetLoggingStringForValue(name, value); } else if (it->second != value) { - LOG(WARNING) << "Key " << key << " already has value " << it->second - << ", ignoring new value: " << value; + LOG(WARNING) << "Name: " << name << " already has " + << GetLoggingStringForValue(name, it->second) + << ", ignoring new " << GetLoggingStringForValue(name, value); } } -bool NameValuePairsParser::ParseNameValuePairs(const std::string& in_string, - const std::string& eq, - const std::string& delim) { - return ParseNameValuePairsWithComments(in_string, eq, delim, ""); -} - -bool NameValuePairsParser::ParseNameValuePairsWithComments( - const std::string& in_string, - const std::string& eq, - const std::string& delim, - const std::string& comment_delim) { +bool NameValuePairsParser::ParseNameValuePairs( + const std::string& input, + NameValuePairsFormat format, + const std::string& debug_source) { bool all_valid = true; - // Set up the pair tokenizer. - base::StringTokenizer pair_toks(in_string, delim); - pair_toks.set_quote_chars("\""); - // Process token pairs. - while (pair_toks.GetNext()) { - std::string pair(pair_toks.token()); - // Anything before the first |eq| is the key, anything after is the value. - // |eq| must exist. - size_t eq_pos = pair.find(eq); - if (eq_pos != std::string::npos) { - // First |comment_delim| after |eq_pos| starts the comment. - // A value of |std::string::npos| means that the value spans to the end - // of |pair|. - size_t value_size = std::string::npos; - if (!comment_delim.empty()) { - size_t comment_pos = pair.find(comment_delim, eq_pos + 1); - if (comment_pos != std::string::npos) - value_size = comment_pos - eq_pos - 1; - } - static const char kTrimChars[] = "\" "; - std::string key; - std::string value; - base::TrimString(pair.substr(0, eq_pos), kTrimChars, &key); - base::TrimString(pair.substr(eq_pos + 1, value_size), kTrimChars, &value); + // We use StringPairs to parse pairs since this is the class that is also + // used to parse machine-info for server backed state keys in Chromium OS. + base::StringPairs pairs; + // This gives us somewhat more lenient parsing than strictly respecting the + // formats we want. For example, whitespace will be removed around the equal + // sign regardless of format. That's okay as we care more about consistency + // with the server backed state keys parser than the strictness of the format, + // and we still preserve our ability to handle the quoted values as we wish. + base::SplitStringIntoKeyValuePairs(input, '=', '\n', &pairs); - if (!key.empty()) { - AddNameValuePair(key, value); - continue; - } + for (const auto& pair : pairs) { + std::string name; + if (!ParseName(pair.first, format, &name)) { + LOG(WARNING) << "Could not parse " << GetNameValuePairsFormatName(format) + << " name-value name from: " << pair.first; + all_valid = false; + continue; } - LOG(WARNING) << "Invalid token pair: " << pair << ". Ignoring."; - all_valid = false; + std::string value; + if (!ParseValue(pair.second, format, &value)) { + LOG(WARNING) << "Could not parse " << GetNameValuePairsFormatName(format) + << " name-value value from: " << pair.second; + all_valid = false; + continue; + } + + // TODO(crbug.com/1250037): Delete logging after the bug is fixed. + LOG_IF(WARNING, name == kStableDeviceSecretDoNotShare) + << "Statistic " << kStableDeviceSecretDoNotShare << " exposed in " + << debug_source; + + AddNameValuePair(name, value); } + return all_valid; }
diff --git a/chromeos/system/name_value_pairs_parser.h b/chromeos/system/name_value_pairs_parser.h index 390e6fd..2783770a 100644 --- a/chromeos/system/name_value_pairs_parser.h +++ b/chromeos/system/name_value_pairs_parser.h
@@ -18,6 +18,24 @@ namespace chromeos { namespace system { +// The name value pairs formats the parser understands. +enum class NameValuePairsFormat { + // Values produced by the dump_vpd_log tool. + // Each key and value is surrounded by double quotes ('"'), and separated by + // an equal ('=') sign. There is no whitespace around the quoted key or value. + kVpdDump, + // Values produced by write-machine-info in the machine-info file. + // The base for this format is that of |kVpdDump|, with the additional + // provision that keys may be unquoted. + kMachineInfo, + // Values produced by the crossystem tool. + // Each key and value is unquoted, and separated by an equal ('=') sign. + // Whitespace is allowed, and used, around key and value, and is not part of + // either. Comments are supported and start with a sharp ('#') character and + // run to the end of the line. + kCrossystem +}; + // The parser is used to get machine info as name-value pairs. Defined here to // be accessible by tests. class COMPONENT_EXPORT(CHROMEOS_SYSTEM) NameValuePairsParser { @@ -30,51 +48,42 @@ NameValuePairsParser(const NameValuePairsParser&) = delete; NameValuePairsParser& operator=(const NameValuePairsParser&) = delete; - // Parses name-value pairs from the file. - // Returns false if there was any error in the file. Valid pairs will still be - // added to the map. - bool GetNameValuePairsFromFile(const base::FilePath& file_path, - const std::string& eq, - const std::string& delim); + // Parses name-value pairs in the specified |format| from a file. + // + // Returns false if there was any error when parsing the file. Valid pairs + // will still be added to the map. + bool ParseNameValuePairsFromFile(const base::FilePath& file_path, + NameValuePairsFormat format); - // Same as ParseNameValuePairsWithComments(), but uses the output of the given - // tool as the input to parse. + // Parses name-value pairs in the specified |format| from the standard output + // of a tool invocation specified by |argc| and |argv|. + // + // Returns false if there was any error in the command invocation or when + // parsing its output. Valid pairs will still be added to the map. bool ParseNameValuePairsFromTool(int argc, const char* argv[], - const std::string& eq, - const std::string& delim, - const std::string& comment_delim); + NameValuePairsFormat format); // Delete all pairs with |value|. void DeletePairsWithValue(const std::string& value); private: - FRIEND_TEST_ALL_PREFIXES(NameValuePairsParser, TestParseNameValuePairs); + FRIEND_TEST_ALL_PREFIXES(VpdDumpNameValuePairsParserTest, + TestParseNameValuePairs); FRIEND_TEST_ALL_PREFIXES(NameValuePairsParser, - TestParseNameValuePairsWithComments); + TestParseNameValuePairsInVpdDumpFormat); + FRIEND_TEST_ALL_PREFIXES(NameValuePairsParser, + TestParseNameValuePairsInMachineInfoFormat); + FRIEND_TEST_ALL_PREFIXES(NameValuePairsParser, + TestParseNameValuePairsFromCrossytemTool); friend class NameValuePairsParserFuzzer; void AddNameValuePair(const std::string& key, const std::string& value); - // These will parse strings with output in the format: - // <key><EQ><value><DELIM>[<key><EQ><value>][...] - // e.g. ParseNameValuePairs("key1=value1 key2=value2", "=", " ") - // Returns false if there was any error in in_string. Valid pairs will still - // be added to the map. - bool ParseNameValuePairs(const std::string& in_string, - const std::string& eq, - const std::string& delim); - - // This version allows for values which end with a comment beginning with - // |comment_delim|. - // e.g. "key2=value2 # Explanation of value\n" - // Returns false if there was any error in in_string. Valid pairs will still - // be added to the map. - bool ParseNameValuePairsWithComments(const std::string& in_string, - const std::string& eq, - const std::string& delim, - const std::string& comment_delim); + bool ParseNameValuePairs(const std::string& input, + NameValuePairsFormat format, + const std::string& debug_source); NameValueMap* map_; };
diff --git a/chromeos/system/name_value_pairs_parser_unittest.cc b/chromeos/system/name_value_pairs_parser_unittest.cc index bc04906..c04f82ef5 100644 --- a/chromeos/system/name_value_pairs_parser_unittest.cc +++ b/chromeos/system/name_value_pairs_parser_unittest.cc
@@ -10,92 +10,140 @@ namespace chromeos { namespace system { -TEST(NameValuePairsParser, TestParseNameValuePairs) { +// A parameterized test class running tests for all the formats that are +// compatible with VPD dumps. +class VpdDumpNameValuePairsParserTest + : public testing::TestWithParam<NameValuePairsFormat> { + protected: NameValuePairsParser::NameValueMap map; +}; + +TEST_P(VpdDumpNameValuePairsParserTest, TestParseNameValuePairs) { + const NameValuePairsFormat format = GetParam(); NameValuePairsParser parser(&map); - const std::string contents1 = "foo=Foo bar=Bar\nfoobar=FooBar\n"; - EXPECT_TRUE(parser.ParseNameValuePairs(contents1, "=", " \n")); - EXPECT_EQ(3U, map.size()); - EXPECT_EQ("Foo", map["foo"]); - EXPECT_EQ("Bar", map["bar"]); - EXPECT_EQ("FooBar", map["foobar"]); - map.clear(); - const std::string contents2 = "foo=Foo,bar=Bar"; - EXPECT_TRUE(parser.ParseNameValuePairs(contents2, "=", ",\n")); - EXPECT_EQ(2U, map.size()); - EXPECT_EQ("Foo", map["foo"]); - EXPECT_EQ("Bar", map["bar"]); - - map.clear(); - const std::string contents3 = "foo=Foo=foo,bar=Bar"; - EXPECT_TRUE(parser.ParseNameValuePairs(contents3, "=", ",\n")); - EXPECT_EQ(2U, map.size()); - EXPECT_EQ("Foo=foo", map["foo"]); - EXPECT_EQ("Bar", map["bar"]); - - map.clear(); - const std::string contents4 = "foo=Foo,=Bar"; - EXPECT_FALSE(parser.ParseNameValuePairs(contents4, "=", ",\n")); - EXPECT_EQ(1U, map.size()); - EXPECT_EQ("Foo", map["foo"]); - - map.clear(); - const std::string contents5 = - "\"initial_locale\"=\"ja\"\n" - "\"initial_timezone\"=\"Asia/Tokyo\"\n" - "\"keyboard_layout\"=\"mozc-jp\"\n"; - EXPECT_TRUE(parser.ParseNameValuePairs(contents5, "=", "\n")); + const std::string contents1 = R"( +"initial_locale"="ja" +"keyboard_layout"="mozc-jp" +"empty"="" +)"; + EXPECT_TRUE(parser.ParseNameValuePairs(contents1, format, + /*debug_source=*/"unit test")); EXPECT_EQ(3U, map.size()); EXPECT_EQ("ja", map["initial_locale"]); - EXPECT_EQ("Asia/Tokyo", map["initial_timezone"]); EXPECT_EQ("mozc-jp", map["keyboard_layout"]); + EXPECT_EQ("", map["empty"]); + + map.clear(); + const std::string contents2 = R"( +"quoted"=""quote"" +"serial_number"="BADBADBAD"" +"model_name"="15" Chromebook" +)"; + EXPECT_TRUE(parser.ParseNameValuePairs(contents2, format, + /*debug_source=*/"unit test")); + EXPECT_EQ(3U, map.size()); + EXPECT_EQ("\"quote\"", map["quoted"]); + EXPECT_EQ("BADBADBAD\"", map["serial_number"]); + EXPECT_EQ("15\" Chromebook", map["model_name"]); + + map.clear(); + const std::string contents3 = R"( +"a"=" +"a"= +"a=b"="c" +""="value" +)"; + EXPECT_FALSE(parser.ParseNameValuePairs(contents3, format, + /*debug_source=*/"unit test")); + EXPECT_EQ(0U, map.size()); + + // This is just for visual confirmations that we have warnings logged and + // that we don't expose the value of the device stable secret in logs. + // TODO(crbug.com/1250037): Delete after logging is fixed. + map.clear(); + const std::string contents5 = R"( +"stable_device_secret_DO_NOT_SHARE"="prettybadtobehere" +"stable_device_secret_DO_NOT_SHARE"="stillprettybadtobehere" +)"; + EXPECT_TRUE(parser.ParseNameValuePairs(contents5, format, + /*debug_source=*/"unit test")); + EXPECT_EQ(1U, map.size()); } -TEST(NameValuePairsParser, TestParseNameValuePairsWithComments) { +INSTANTIATE_TEST_SUITE_P(NameValuePairs, + VpdDumpNameValuePairsParserTest, + testing::Values(NameValuePairsFormat::kVpdDump, + NameValuePairsFormat::kMachineInfo)); + +TEST(NameValuePairsParser, TestParseNameValuePairsInVpdDumpFormat) { + constexpr NameValuePairsFormat format = NameValuePairsFormat::kVpdDump; NameValuePairsParser::NameValueMap map; NameValuePairsParser parser(&map); - const std::string contents1 = "foo=Foo,bar=#Bar,baz= 0 #Baz"; - EXPECT_TRUE( - parser.ParseNameValuePairsWithComments(contents1, "=", ",\n", "#")); - EXPECT_EQ(3U, map.size()); - EXPECT_EQ("Foo", map["foo"]); - EXPECT_EQ("", map["bar"]); - EXPECT_EQ("0", map["baz"]); + // Names must be quoted in VPD dump format. Unquoted names are ignored. + const std::string contents1 = R"( +"name1"="value1" +name2="value2" +"name3"="value3" +name4="value4" +)"; + EXPECT_FALSE(parser.ParseNameValuePairs(contents1, format, + /*debug_source=*/"unit test")); + EXPECT_EQ(2U, map.size()); + EXPECT_EQ("value1", map["name1"]); + EXPECT_EQ("value3", map["name3"]); +} + +TEST(NameValuePairsParser, TestParseNameValuePairsInMachineInfoFormat) { + constexpr NameValuePairsFormat format = NameValuePairsFormat::kMachineInfo; + NameValuePairsParser::NameValueMap map; + NameValuePairsParser parser(&map); + + // Names do not have to be quoted in machine info format. + const std::string contents1 = R"( +"name1"="value1" +name2="value2" +"name3"="value3" +name4="value4" +)"; + EXPECT_TRUE(parser.ParseNameValuePairs(contents1, format, + /*debug_source=*/"unit test")); + EXPECT_EQ(4U, map.size()); + EXPECT_EQ("value1", map["name1"]); + EXPECT_EQ("value2", map["name2"]); + EXPECT_EQ("value3", map["name3"]); + EXPECT_EQ("value4", map["name4"]); map.clear(); - const std::string contents2 = "foo="; - EXPECT_TRUE( - parser.ParseNameValuePairsWithComments(contents2, "=", ",\n", "#")); - EXPECT_EQ(1U, map.size()); - EXPECT_EQ("", map["foo"]); - - map.clear(); - const std::string contents3 = " \t ,,#all empty,"; - EXPECT_FALSE( - parser.ParseNameValuePairsWithComments(contents3, "=", ",\n", "#")); + const std::string contents2 = R"( +="value" +)"; + EXPECT_FALSE(parser.ParseNameValuePairs(contents2, format, + /*debug_source=*/"unit test")); EXPECT_EQ(0U, map.size()); } -TEST(NameValuePairsParser, TestParseNameValuePairsFromTool) { +TEST(NameValuePairsParser, TestParseNameValuePairsFromCrossystemTool) { // Sample output is taken from the /usr/bin/crosssytem tool. - const char* command[] = { "/bin/echo", - "arch = x86 # Platform architecture\n" \ - "cros_debug = 1 # OS should allow debug\n" \ - "dbg_reset = (error) # Debug reset mode request\n" \ - "key#with_comment = some value # Multiple # comment # delims\n" \ - "key = # No value.\n" \ - "vdat_timers = " \ - "LFS=0,0 LF=1784220250,2971030570 LK=9064076660,9342689170 " \ - "# Timer values from VbSharedData\n" - }; + const char* command[] = { + "/bin/echo", + "arch = x86 # Platform architecture\n" + "cros_debug = 1 # OS should allow debug\n" + "dbg_reset = (error) # Debug reset mode request\n" + "key#with_comment = some value # Multiple # comment # delims\n" + "key = # No value.\n" + "vdat_timers = " + "LFS=0,0 LF=1784220250,2971030570 LK=9064076660,9342689170 " + "# Timer values from VbSharedData\n" + "wpsw_cur = 1 # Firmware hardware WP switch " + "pos\n"}; NameValuePairsParser::NameValueMap map; NameValuePairsParser parser(&map); - parser.ParseNameValuePairsFromTool(base::size(command), command, "=", "\n", - "#"); - EXPECT_EQ(6u, map.size()); + parser.ParseNameValuePairsFromTool(base::size(command), command, + NameValuePairsFormat::kCrossystem); + EXPECT_EQ(7u, map.size()); EXPECT_EQ("x86", map["arch"]); EXPECT_EQ("1", map["cros_debug"]); EXPECT_EQ("(error)", map["dbg_reset"]); @@ -103,6 +151,7 @@ EXPECT_EQ("", map["key"]); EXPECT_EQ("LFS=0,0 LF=1784220250,2971030570 LK=9064076660,9342689170", map["vdat_timers"]); + EXPECT_EQ("1", map["wpsw_cur"]); } TEST(NameValuePairsParser, DeletePairsWithValue) {
diff --git a/chromeos/system/statistics_provider.cc b/chromeos/system/statistics_provider.cc index 2a9a017b..70315b5 100644 --- a/chromeos/system/statistics_provider.cc +++ b/chromeos/system/statistics_provider.cc
@@ -43,12 +43,9 @@ namespace { -// Path to the tool used to get system info, and delimiters for the output -// format of the tool. +// Path to the tool used to get system info, and special values for the +// output of the tool. const char* kCrosSystemTool[] = {"/usr/bin/crossystem"}; -const char kCrosSystemEq[] = "="; -const char kCrosSystemDelim[] = "\n"; -const char kCrosSystemCommentDelim[] = "#"; const char kCrosSystemValueError[] = "(error)"; const char kHardwareClassCrosSystemKey[] = "hwid"; @@ -56,22 +53,10 @@ const char kIsVmCrosSystemKey[] = "inside_vm"; -// Key/value delimiters of machine hardware info file. machine-info is generated -// only for OOBE and enterprise enrollment and may not be present. See -// login-manager/init/machine-info.conf. -const char kMachineHardwareInfoEq[] = "="; -const char kMachineHardwareInfoDelim[] = " \n"; - // File to get ECHO coupon info from, and key/value delimiters of // the file. const char kEchoCouponFile[] = "/mnt/stateful_partition/unencrypted/cache/vpd/echo/vpd_echo.txt"; -const char kEchoCouponEq[] = "="; -const char kEchoCouponDelim[] = "\n"; - -// Key/value delimiters for VPD file. -const char kVpdEq[] = "="; -const char kVpdDelim[] = "\n"; // File to get regional data from. const char kCrosRegions[] = "/usr/share/misc/cros-regions.json"; @@ -508,8 +493,8 @@ if (base::SysInfo::IsRunningOnChromeOS()) { // Parse all of the key/value pairs from the crossystem tool. if (!parser.ParseNameValuePairsFromTool( - base::size(kCrosSystemTool), kCrosSystemTool, kCrosSystemEq, - kCrosSystemDelim, kCrosSystemCommentDelim)) { + base::size(kCrosSystemTool), kCrosSystemTool, + NameValuePairsFormat::kCrossystem)) { LOG(ERROR) << "Errors parsing output from: " << kCrosSystemTool; } // Drop useless "(error)" values so they don't displace valid values @@ -549,15 +534,17 @@ int bytes_written = base::WriteFile(vpd_path, stub_contents.c_str(), stub_contents.size()); if (bytes_written < static_cast<int>(stub_contents.size())) { - PLOG(ERROR) << "Error writing vpd stub " << vpd_path.value(); + PLOG(ERROR) << "Error writing VPD stub " << vpd_path.value(); } } - parser.GetNameValuePairsFromFile(machine_info_path, kMachineHardwareInfoEq, - kMachineHardwareInfoDelim); - parser.GetNameValuePairsFromFile(base::FilePath(kEchoCouponFile), - kEchoCouponEq, kEchoCouponDelim); - parser.GetNameValuePairsFromFile(vpd_path, kVpdEq, kVpdDelim); + // The machine-info file is generated only for OOBE and enterprise enrollment + // and may not be present. See login-manager/init/machine-info.conf. + parser.ParseNameValuePairsFromFile(machine_info_path, + NameValuePairsFormat::kMachineInfo); + parser.ParseNameValuePairsFromFile(base::FilePath(kEchoCouponFile), + NameValuePairsFormat::kVpdDump); + parser.ParseNameValuePairsFromFile(vpd_path, NameValuePairsFormat::kVpdDump); // Ensure that the hardware class key is present with the expected // key name, and if it couldn't be retrieved, that the value is "unknown".
diff --git a/chromeos/tast_control.gni b/chromeos/tast_control.gni index f5f7492..f156a52 100644 --- a/chromeos/tast_control.gni +++ b/chromeos/tast_control.gni
@@ -107,6 +107,12 @@ # https://crbug.com/1276337 "arc.IntentForward", "arc.IntentForward.vm", + + # b/207689782 + "crostini.Launcher.local_wayland_bullseye_stable", + "crostini.Launcher.local_wayland_buster_stable", + "crostini.Launcher.local_x11_bullseye_stable", + "crostini.Launcher.local_x11_buster_stable", ] # To disable a specific test in lacros_all_tast_tests, add it the following
diff --git a/components/BUILD.gn b/components/BUILD.gn index db29d590..21f03c61 100644 --- a/components/BUILD.gn +++ b/components/BUILD.gn
@@ -779,6 +779,7 @@ if (is_android) { sources += [ + "autofill_assistant/browser/js_flow_executor_unittest.cc", "autofill_assistant/browser/mock_script_executor_delegate.cc", "autofill_assistant/browser/mock_script_executor_delegate.h", "autofill_assistant/browser/web/web_controller_browsertest.cc",
diff --git a/components/autofill_assistant/browser/BUILD.gn b/components/autofill_assistant/browser/BUILD.gn index a308a87..87056a1 100644 --- a/components/autofill_assistant/browser/BUILD.gn +++ b/components/autofill_assistant/browser/BUILD.gn
@@ -168,6 +168,8 @@ "info_box.h", "intent_strings.cc", "intent_strings.h", + "js_flow_executor.cc", + "js_flow_executor.h", "metrics.cc", "metrics.h", "onboarding_result.h",
diff --git a/components/autofill_assistant/browser/devtools/BUILD.gn b/components/autofill_assistant/browser/devtools/BUILD.gn index 71fd674..3787786 100644 --- a/components/autofill_assistant/browser/devtools/BUILD.gn +++ b/components/autofill_assistant/browser/devtools/BUILD.gn
@@ -3,8 +3,12 @@ # found in the LICENSE file. devtools_domains = [ + "debugger", "dom", + "emulation", "input", + "network", + "page", "runtime", "target", ]
diff --git a/components/autofill_assistant/browser/devtools/devtools_client.cc b/components/autofill_assistant/browser/devtools/devtools_client.cc index 9173f2c9..4dea4f3 100644 --- a/components/autofill_assistant/browser/devtools/devtools_client.cc +++ b/components/autofill_assistant/browser/devtools/devtools_client.cc
@@ -27,6 +27,7 @@ dom_domain_(this), runtime_domain_(this), target_domain_(this), + page_domain_(this), next_message_id_(0), frame_tracker_(this) { browser_main_thread_ = content::GetUIThreadTaskRunner({}); @@ -55,6 +56,10 @@ return &target_domain_; } +page::ExperimentalDomain* DevtoolsClient::GetPage() { + return &page_domain_; +} + void DevtoolsClient::SendMessage( const char* method, std::unique_ptr<base::Value> params,
diff --git a/components/autofill_assistant/browser/devtools/devtools_client.h b/components/autofill_assistant/browser/devtools/devtools_client.h index 160e600..257c4c69 100644 --- a/components/autofill_assistant/browser/devtools/devtools_client.h +++ b/components/autofill_assistant/browser/devtools/devtools_client.h
@@ -21,6 +21,7 @@ #include "components/autofill_assistant/browser/devtools/devtools/domains/dom.h" #include "components/autofill_assistant/browser/devtools/devtools/domains/input.h" #include "components/autofill_assistant/browser/devtools/devtools/domains/network.h" +#include "components/autofill_assistant/browser/devtools/devtools/domains/page.h" #include "components/autofill_assistant/browser/devtools/devtools/domains/runtime.h" #include "components/autofill_assistant/browser/devtools/devtools/domains/target.h" #include "components/autofill_assistant/browser/devtools/message_dispatcher.h" @@ -43,6 +44,7 @@ dom::Domain* GetDOM(); runtime::Domain* GetRuntime(); target::ExperimentalDomain* GetTarget(); + page::ExperimentalDomain* GetPage(); // MessageDispatcher implementation: void SendMessage( @@ -158,6 +160,7 @@ dom::ExperimentalDomain dom_domain_; runtime::ExperimentalDomain runtime_domain_; target::ExperimentalDomain target_domain_; + page::ExperimentalDomain page_domain_; std::unordered_map<int, Callback> pending_messages_; EventHandlerMap event_handlers_; int next_message_id_;
diff --git a/components/autofill_assistant/browser/js_flow_executor.cc b/components/autofill_assistant/browser/js_flow_executor.cc new file mode 100644 index 0000000..6f9252c --- /dev/null +++ b/components/autofill_assistant/browser/js_flow_executor.cc
@@ -0,0 +1,308 @@ +// 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/autofill_assistant/browser/js_flow_executor.h" +#include "base/json/json_writer.h" +#include "base/logging.h" +#include "base/strings/strcat.h" +#include "components/autofill_assistant/browser/web/web_controller_util.h" + +namespace { + +// Initializes a |globalFlowState| variable on first run, and renews the +// promises that will let JS flows request native actions. +constexpr char kRunNativeAction[] = R"( + if (typeof globalFlowState === 'undefined') { + globalFlowState = {} + } + new Promise((fulfill, reject) => { + globalFlowState.runNativeAction = fulfill + globalFlowState.endNativeAction = reject + }) +)"; + +constexpr char kArrayGetNthElement[] = "function(index) { return this[index] }"; +constexpr char kFulfillActionPromise[] = R"( + function(status, result) { + this([status, result]) + } +)"; + +constexpr char kMainFrame[] = ""; + +} // namespace + +namespace autofill_assistant { + +JsFlowExecutor::JsFlowExecutor(content::WebContents* web_contents, + Delegate* delegate) + : delegate_(delegate), + devtools_client_(std::make_unique<DevtoolsClient>( + content::DevToolsAgentHost::GetOrCreateFor(web_contents))) {} + +JsFlowExecutor::~JsFlowExecutor() = default; + +void JsFlowExecutor::Start( + const std::string& js_flow, + base::OnceCallback<void(const ClientStatus&, std::unique_ptr<base::Value>)> + callback) { + if (callback_) { + LOG(ERROR) << "Invoked " << __func__ << " while already running"; + std::move(callback).Run(ClientStatus(INVALID_ACTION), nullptr); + return; + } + + js_flow_ = std::make_unique<std::string>(js_flow); + callback_ = std::move(callback); + if (isolated_world_context_id_ == -1) { + devtools_client_->GetPage()->GetFrameTree( + kMainFrame, base::BindOnce(&JsFlowExecutor::OnGetFrameTree, + weak_ptr_factory_.GetWeakPtr())); + } else { + InternalStart(); + } +} + +void JsFlowExecutor::OnGetFrameTree( + const DevtoolsClient::ReplyStatus& reply_status, + std::unique_ptr<page::GetFrameTreeResult> result) { + if (!result) { + LOG(ERROR) << "Failed to retrieve frame tree"; + std::move(callback_).Run( + JavaScriptErrorStatus(reply_status, __FILE__, __LINE__, nullptr), + nullptr); + return; + } + + devtools_client_->GetPage()->CreateIsolatedWorld( + page::CreateIsolatedWorldParams::Builder() + .SetFrameId(result->GetFrameTree()->GetFrame()->GetId()) + .Build(), + kMainFrame, + base::BindOnce(&JsFlowExecutor::IsolatedWorldCreated, + weak_ptr_factory_.GetWeakPtr())); +} + +void JsFlowExecutor::IsolatedWorldCreated( + const DevtoolsClient::ReplyStatus& reply_status, + std::unique_ptr<page::CreateIsolatedWorldResult> result) { + if (!result) { + LOG(ERROR) << "Failed to create isolated world"; + std::move(callback_).Run( + JavaScriptErrorStatus(reply_status, __FILE__, __LINE__, nullptr), + nullptr); + return; + } + + isolated_world_context_id_ = result->GetExecutionContextId(); + InternalStart(); +} + +void JsFlowExecutor::InternalStart() { + DCHECK(isolated_world_context_id_ != -1); + DCHECK(callback_); + + // Before running the flow in the sandbox, we define a promise that + // the flow may fulfill to request execution of a native action. + RefreshNativeActionPromise(); + + // Wrap the main js_flow in an async function containing a method to + // request native actions. This is essentially providing |js_flow| with a + // JS API to call native functionality. + // TODO(b/208420231): adjust linenumbers to account for the offset introduced + // by this wrapper, otherwise exception stacktraces will be hard to map to the + // original js source. + js_flow_ = std::make_unique<std::string>(base::StrCat({ + R"((async function() { + function runNativeAction(native_action) { + return new Promise( + (fulfill, reject) => { + globalFlowState.runNativeAction([native_action, fulfill]) + } + ) + } + )", + *js_flow_, " }) ()"})); + + // Run the wrapped js_flow in the sandbox and serve potential native action + // requests as they arrive. + devtools_client_->GetRuntime()->Evaluate( + runtime::EvaluateParams::Builder() + .SetExpression(*js_flow_) + .SetAwaitPromise(true) + .SetReturnByValue(true) + .SetContextId(isolated_world_context_id_) + .Build(), + kMainFrame, + base::BindOnce(&JsFlowExecutor::OnFlowFinished, + weak_ptr_factory_.GetWeakPtr())); +} + +void JsFlowExecutor::RefreshNativeActionPromise() { + devtools_client_->GetRuntime()->Evaluate( + runtime::EvaluateParams::Builder() + .SetExpression(kRunNativeAction) + .SetAwaitPromise(true) + .SetContextId(isolated_world_context_id_) + .Build(), + kMainFrame, + base::BindOnce(&JsFlowExecutor::OnNativeActionRequested, + weak_ptr_factory_.GetWeakPtr())); +} + +void JsFlowExecutor::OnNativeActionRequested( + const DevtoolsClient::ReplyStatus& reply_status, + std::unique_ptr<runtime::EvaluateResult> result) { + if (!CheckResultAndStopOnError(reply_status, result, __FILE__, __LINE__)) { + return; + } + + // We expect 2 arguments from JS: (1) the requested action(JSON-compatible + // value), and (2) the fulfill promise to call with the action result. + std::string js_array_object_id = result->GetResult()->GetObjectId(); + std::vector<std::unique_ptr<runtime::CallArgument>> arguments; + AddRuntimeCallArgument(/* index = */ 0, &arguments); + devtools_client_->GetRuntime()->CallFunctionOn( + runtime::CallFunctionOnParams::Builder() + .SetObjectId(js_array_object_id) + .SetArguments(std::move(arguments)) + .SetFunctionDeclaration(std::string(kArrayGetNthElement)) + .Build(), + kMainFrame, + base::BindOnce(&JsFlowExecutor::OnNativeActionRequestActionRetrieved, + weak_ptr_factory_.GetWeakPtr(), js_array_object_id)); +} + +void JsFlowExecutor::OnNativeActionRequestActionRetrieved( + const std::string& js_array_object_id, + const DevtoolsClient::ReplyStatus& reply_status, + std::unique_ptr<runtime::CallFunctionOnResult> result) { + if (!CheckResultAndStopOnError(reply_status, result, __FILE__, __LINE__)) { + return; + } + + auto* remote_object = result->GetResult(); + if (!remote_object->HasValue()) { + ClientStatus status(UNEXPECTED_JS_ERROR); + auto* details = status.mutable_details()->mutable_unexpected_error_info(); + details->set_source_file(__FILE__); + details->set_source_line_number(__LINE__); + + std::string stringified_result; + base::JSONWriter::Write(*remote_object->Serialize(), &stringified_result); + details->set_devtools_error_message( + base::StrCat({"runNativeAction expected single JSON-compatible " + "argument, but was called with ", + stringified_result})); + RunCallback(status, nullptr); + return; + } + + std::vector<std::unique_ptr<runtime::CallArgument>> arguments; + AddRuntimeCallArgument(/* index = */ 1, &arguments); + devtools_client_->GetRuntime()->CallFunctionOn( + runtime::CallFunctionOnParams::Builder() + .SetObjectId(js_array_object_id) + .SetArguments(std::move(arguments)) + .SetFunctionDeclaration(std::string(kArrayGetNthElement)) + .Build(), + kMainFrame, + base::BindOnce( + &JsFlowExecutor::OnNativeActionRequestFulfillPromiseRetrieved, + weak_ptr_factory_.GetWeakPtr(), remote_object->Serialize())); +} + +void JsFlowExecutor::OnNativeActionRequestFulfillPromiseRetrieved( + std::unique_ptr<base::Value> action_request, + const DevtoolsClient::ReplyStatus& reply_status, + std::unique_ptr<runtime::CallFunctionOnResult> result) { + if (!CheckResultAndStopOnError(reply_status, result, __FILE__, __LINE__)) { + return; + } + + auto* fulfill_promise_object = result->GetResult(); + DCHECK(fulfill_promise_object); + if (!fulfill_promise_object->HasObjectId()) { + // This should never happen, since the fulfill promise is programmatically + // provided. + ClientStatus status(OTHER_ACTION_STATUS); + auto* details = status.mutable_details()->mutable_unexpected_error_info(); + details->set_source_file(__FILE__); + details->set_source_line_number(__LINE__); + details->set_devtools_error_message( + "Native action requested, but no fulfill promise provided"); + RunCallback(status, nullptr); + return; + } + + delegate_->RunNativeAction( + std::move(action_request), + base::BindOnce(&JsFlowExecutor::OnNativeActionFinished, + weak_ptr_factory_.GetWeakPtr(), + fulfill_promise_object->GetObjectId())); +} + +void JsFlowExecutor::OnNativeActionFinished( + const std::string& fulfill_promise_object_id, + const ClientStatus& result_status, + std::unique_ptr<base::Value> result_value) { + if (!callback_) { + // No longer relevant. + return; + } + + // Refresh the native action request promise. + RefreshNativeActionPromise(); + + // Fulfill the promise and thus resume the JS flow. + std::vector<std::unique_ptr<runtime::CallArgument>> arguments; + AddRuntimeCallArgument(static_cast<int>(result_status.proto_status()), + &arguments); + auto result_arg = std::make_unique<base::Value>(); + if (result_value) { + result_arg = std::move(result_value); + } + arguments.emplace_back( + runtime::CallArgument::Builder().SetValue(std::move(result_arg)).Build()); + devtools_client_->GetRuntime()->CallFunctionOn( + runtime::CallFunctionOnParams::Builder() + .SetObjectId(fulfill_promise_object_id) + .SetArguments(std::move(arguments)) + .SetFunctionDeclaration(std::string(kFulfillActionPromise)) + .Build(), + kMainFrame, + base::BindOnce(&JsFlowExecutor::OnFlowResumed, + weak_ptr_factory_.GetWeakPtr())); +} + +void JsFlowExecutor::OnFlowResumed( + const DevtoolsClient::ReplyStatus& reply_status, + std::unique_ptr<runtime::CallFunctionOnResult> result) { + // This should never fail, but if it does, we need to catch it here to prevent + // the client from stalling indefinitely, waiting for the flow to resume. + if (!CheckResultAndStopOnError(reply_status, result, __FILE__, __LINE__)) { + return; + } +} + +void JsFlowExecutor::OnFlowFinished( + const DevtoolsClient::ReplyStatus& reply_status, + std::unique_ptr<runtime::EvaluateResult> result) { + // Note that the result is always serialized if available, not just if the + // flow was successful. In particular, this serializes exceptions. + RunCallback( + CheckJavaScriptResult(reply_status, result.get(), __FILE__, __LINE__), + (result != nullptr ? result->Serialize() : nullptr)); +} + +void JsFlowExecutor::RunCallback(const ClientStatus& status, + std::unique_ptr<base::Value> result_value) { + if (!status.ok() && result_value) { + DVLOG(1) << "Flow failed with " << status + << " and result: " << *result_value; + } + std::move(callback_).Run(status, std::move(result_value)); +} + +} // namespace autofill_assistant
diff --git a/components/autofill_assistant/browser/js_flow_executor.h b/components/autofill_assistant/browser/js_flow_executor.h new file mode 100644 index 0000000..0cef5be --- /dev/null +++ b/components/autofill_assistant/browser/js_flow_executor.h
@@ -0,0 +1,176 @@ +// 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_AUTOFILL_ASSISTANT_BROWSER_JS_FLOW_EXECUTOR_H_ +#define COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_JS_FLOW_EXECUTOR_H_ + +#include <memory> +#include <string> +#include "base/callback_forward.h" +#include "base/memory/weak_ptr.h" +#include "base/values.h" +#include "components/autofill_assistant/browser/client_status.h" +#include "components/autofill_assistant/browser/devtools/devtools_client.h" + +namespace autofill_assistant { + +// Executes a JS flow in a sandbox JS context. The flow may request additional +// native actions to be performed by its delegate. +class JsFlowExecutor { + public: + class Delegate { + public: + virtual ~Delegate() = default; + + // Asks the delegate to run |native_action| and call |finished_callback| + // when done. The contents of |native_action| depend on the JS script, but + // will typically be a serialized protobuffer. + virtual void RunNativeAction( + std::unique_ptr<base::Value> native_action, + base::OnceCallback<void(const ClientStatus& result_status, + std::unique_ptr<base::Value> result_value)> + finished_callback) = 0; + }; + + // |delegate| must outlive the JsFlowExecutor. + JsFlowExecutor(content::WebContents* web_contents, Delegate* delegate); + ~JsFlowExecutor(); + JsFlowExecutor(const JsFlowExecutor&) = delete; + JsFlowExecutor& operator=(const JsFlowExecutor&) = delete; + + // Starts executing |js_flow| in an isolated JS context. Once finished (or on + // error), |result_callback| is invoked with the final result. In the case of + // an uncaught exception during flow execution, the returned status may + // contain a stack trace and additional information (limited to the sandbox). + // Only one flow may run at a time. + // + // Flows may request additional native actions from the delegate, using the + // following syntax: + // + // let [status, result] = await runNativeAction('request') + // + // - |status| is an int corresponding to a ProcessedActionStatusProto. + // - [result] is a struct containing the result value, or an empty struct if + // no result was returned. The specific contents depend on the + // native action. + // - |runNativeAction| is provided automatically and takes a single argument. + // The type of the argument will depend on what the native + // delegate expects, but will typically be a serialized + // protobuffer. + // + // The flow result is one of the following, depending on the |js_flow|: + // (1) ACTION_APPLIED and a base::Value dictionary containing the 'result' key + // and value, as returned by the JS flow. Example for a one-liner js flow + // 'return 12345': + // { + // "result": { + // "description": "12345", + // "type": "number", + // "value": 12345 + // } + // } + // See the unit tests for further examples. Note: field names are + // auto-generated by base::Value serializers. + // + // (2) UNEXPECTED_JS_ERROR in case of an execution error, along with a + // base::Value dictionary containing the 'exceptionDetails' key and exception, + // if available. Note that this exception originates from the sandbox JS + // context. Example: + // JS flow: + // function doSomething(x) { + // console.log('foobar says: ' + x); + // throw new Error('Hello world!'); + // } + // function entrypoint() { + // doSomething('bla'); + // } + // entrypoint(); + // + // Returned exception details: + // "exceptionDetails": { + // "columnNumber": 0, + // "exception": { + // "className": "Error", + // "description": "Error: Hello world! + // at doSomething (<anonymous>:16:33) + // at entrypoint (<anonymous>:19:27) + // at <anonymous>:21:25 + // at <anonymous>:22:28", + // "objectId": "-3968045700143737919.4.2", + // "subtype": "error", + // "type": "object" + // }, + // "exceptionId": 2, + // "lineNumber": 0, + // "text": "Uncaught (in promise) Error: Hello world!" + // } + // + // (3) UNEXPECTED_JS_ERROR and null in case of internal errors during script + // execution (i.e., unrecoverable devtools errors). The status details may + // contain additional information. + void Start( + const std::string& js_flow, + base::OnceCallback<void(const ClientStatus&, + std::unique_ptr<base::Value>)> result_callback); + + private: + void InternalStart(); + void OnGetFrameTree(const DevtoolsClient::ReplyStatus& reply_status, + std::unique_ptr<page::GetFrameTreeResult> result); + void IsolatedWorldCreated( + const DevtoolsClient::ReplyStatus& reply_status, + std::unique_ptr<page::CreateIsolatedWorldResult> result); + void RefreshNativeActionPromise(); + void OnNativeActionRequested(const DevtoolsClient::ReplyStatus& reply_status, + std::unique_ptr<runtime::EvaluateResult> result); + void OnNativeActionRequestActionRetrieved( + const std::string& js_array_object_id, + const DevtoolsClient::ReplyStatus& reply_status, + std::unique_ptr<runtime::CallFunctionOnResult> result); + void OnNativeActionRequestFulfillPromiseRetrieved( + std::unique_ptr<base::Value> action_request, + const DevtoolsClient::ReplyStatus& reply_status, + std::unique_ptr<runtime::CallFunctionOnResult> result); + void OnNativeActionFinished(const std::string& fulfill_promise_object_id, + const ClientStatus& status, + std::unique_ptr<base::Value> result); + void OnFlowResumed(const DevtoolsClient::ReplyStatus& reply_status, + std::unique_ptr<runtime::CallFunctionOnResult> result); + void OnFlowFinished(const DevtoolsClient::ReplyStatus& reply_status, + std::unique_ptr<runtime::EvaluateResult> result); + void RunCallback(const ClientStatus& status, + std::unique_ptr<base::Value> result_value); + + // Returns true if |reply_status| and |result| are ok. Else, stops the flow + // and returns false. + template <typename T> + bool CheckResultAndStopOnError( + const DevtoolsClient::ReplyStatus& reply_status, + std::unique_ptr<T>& result, + const char* file, + int line) { + ClientStatus status = + CheckJavaScriptResult(reply_status, result.get(), file, line); + if (!status.ok()) { + RunCallback(status, (result != nullptr ? result->Serialize() : nullptr)); + return false; + } + return true; + } + + Delegate* const delegate_; + std::unique_ptr<DevtoolsClient> devtools_client_; + int isolated_world_context_id_ = -1; + + // Only set during a flow. + std::unique_ptr<std::string> js_flow_; + base::OnceCallback<void(const ClientStatus&, std::unique_ptr<base::Value>)> + callback_; + + base::WeakPtrFactory<JsFlowExecutor> weak_ptr_factory_{this}; +}; + +} // namespace autofill_assistant + +#endif // COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_JS_FLOW_EXECUTOR_H_
diff --git a/components/autofill_assistant/browser/js_flow_executor_unittest.cc b/components/autofill_assistant/browser/js_flow_executor_unittest.cc new file mode 100644 index 0000000..6760df259 --- /dev/null +++ b/components/autofill_assistant/browser/js_flow_executor_unittest.cc
@@ -0,0 +1,445 @@ +// 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/autofill_assistant/browser/js_flow_executor.h" +#include "base/callback.h" +#include "base/json/json_reader.h" +#include "base/strings/strcat.h" +#include "base/strings/string_number_conversions.h" +#include "base/test/gmock_callback_support.h" +#include "base/test/mock_callback.h" +#include "base/test/task_environment.h" +#include "base/time/tick_clock.h" +#include "content/public/test/browser_task_environment.h" +#include "content/public/test/browser_test.h" +#include "content/public/test/browser_test_utils.h" +#include "content/public/test/content_browser_test.h" +#include "content/public/test/content_browser_test_utils.h" +#include "content/public/test/test_browser_context.h" +#include "content/public/test/test_renderer_host.h" +#include "content/public/test/web_contents_tester.h" +#include "content/shell/browser/shell.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "third_party/abseil-cpp/absl/types/optional.h" +#include "third_party/blink/public/common/switches.h" + +namespace autofill_assistant { +namespace { + +using ::base::test::RunOnceCallback; +using ::testing::_; +using ::testing::ElementsAre; +using ::testing::Eq; +using ::testing::Ne; +using ::testing::NiceMock; +using ::testing::Pair; +using ::testing::Pointee; +using ::testing::Property; +using ::testing::SizeIs; +using ::testing::WithArg; + +// Parses |json| as a base::Value. No error handling - this will crash for +// invalid json inputs. +std::unique_ptr<base::Value> UniqueValueFromJson(const std::string& json) { + return std::make_unique<base::Value>( + std::move(*base::JSONReader::Read(json))); +} + +class MockJsFlowExecutorDelegate : public JsFlowExecutor::Delegate { + public: + MockJsFlowExecutorDelegate() = default; + ~MockJsFlowExecutorDelegate() override = default; + + MOCK_METHOD( + void, + RunNativeAction, + (std::unique_ptr<base::Value> native_action, + base::OnceCallback<void(const ClientStatus& result_status, + std::unique_ptr<base::Value> result_value)> + callback), + (override)); +}; + +class JsFlowExecutorTest : public content::ContentBrowserTest { + public: + JsFlowExecutorTest() {} + + void SetUpCommandLine(base::CommandLine* command_line) override { + command_line->AppendSwitch("site-per-process"); + // Necessary to avoid flakiness or failure due to input arriving + // before the first compositor commit. + command_line->AppendSwitch(blink::switches::kAllowPreCommitInput); + } + + void SetUpOnMainThread() override { + ContentBrowserTest::SetUpOnMainThread(); + + // Start a mock server for hosting an OOPIF. + http_server_iframe_ = std::make_unique<net::EmbeddedTestServer>( + net::EmbeddedTestServer::TYPE_HTTP); + http_server_iframe_->ServeFilesFromSourceDirectory( + "components/test/data/autofill_assistant/html_iframe"); + ASSERT_TRUE(http_server_iframe_->Start(8081)); + + // Start the main server hosting the test page. + http_server_ = std::make_unique<net::EmbeddedTestServer>( + net::EmbeddedTestServer::TYPE_HTTP); + http_server_->ServeFilesFromSourceDirectory( + "components/test/data/autofill_assistant/html"); + ASSERT_TRUE(http_server_->Start(8080)); + ASSERT_TRUE(NavigateToURL( + shell(), + http_server_->GetURL("/autofill_assistant_target_website.html"))); + + flow_executor_ = std::make_unique<JsFlowExecutor>(shell()->web_contents(), + &mock_delegate_); + } + + // Overload, ignore result value, just return the client status. + ClientStatus RunTest(const std::string& js_flow) { + std::unique_ptr<base::Value> ignored_result; + return RunTest(js_flow, ignored_result); + } + + ClientStatus RunTest(const std::string& js_flow, + std::unique_ptr<base::Value>& result_value) { + ClientStatus status; + base::RunLoop run_loop; + flow_executor_->Start( + js_flow, base::BindOnce(&JsFlowExecutorTest::OnFlowFinished, + base::Unretained(this), run_loop.QuitClosure(), + &status, std::ref(result_value))); + run_loop.Run(); + return status; + } + + void OnFlowFinished(base::OnceClosure done_callback, + ClientStatus* status_output, + std::unique_ptr<base::Value>& result_output, + const ClientStatus& status, + std::unique_ptr<base::Value> result_value) { + *status_output = status; + result_output = std::move(result_value); + std::move(done_callback).Run(); + } + + protected: + NiceMock<MockJsFlowExecutorDelegate> mock_delegate_; + std::unique_ptr<JsFlowExecutor> flow_executor_; + std::unique_ptr<net::EmbeddedTestServer> http_server_; + std::unique_ptr<net::EmbeddedTestServer> http_server_iframe_; +}; + +IN_PROC_BROWSER_TEST_F(JsFlowExecutorTest, SmokeTest) { + EXPECT_THAT(RunTest(std::string()), + Property(&ClientStatus::proto_status, ACTION_APPLIED)); +} + +IN_PROC_BROWSER_TEST_F(JsFlowExecutorTest, InvalidJs) { + EXPECT_THAT(RunTest("Not valid Javascript"), + Property(&ClientStatus::proto_status, UNEXPECTED_JS_ERROR)); +} + +IN_PROC_BROWSER_TEST_F(JsFlowExecutorTest, RunNativeActionWithReturnValue) { + std::unique_ptr<base::Value> native_return_value = + std::make_unique<base::Value>(std::move(*base::JSONReader::Read( + R"( + { + "keyA":12345, + "keyB":"Hello world", + "keyC": ["array", "of", "strings"], + "keyD": { + "keyE": "nested", + "keyF": true, + "keyG": 123.45, + "keyH": null + } + } + )"))); + + EXPECT_CALL(mock_delegate_, RunNativeAction) + .WillOnce([&](auto value, auto callback) { + EXPECT_EQ(*value, *UniqueValueFromJson(R"( + {"type":"string", + "value":"test"})")); + std::move(callback).Run(ClientStatus(ACTION_APPLIED), + std::move(native_return_value)); + }); + + std::unique_ptr<base::Value> js_return_value; + EXPECT_THAT(RunTest(R"( + let [status, value] = await runNativeAction('test'); + if (status != 2) { // ACTION_APPLIED + return status; + } + value.keyA += 3; + value.keyB += '!'; + value.keyD.keyF = false; + return value; + )", + js_return_value), + Property(&ClientStatus::proto_status, ACTION_APPLIED)); + EXPECT_EQ(*js_return_value, *base::JSONReader::Read(R"( + { + "result": { + "type": "object", + "value": { + "keyA": 12348, + "keyB": "Hello world!", + "keyC": [ "array", "of", "strings" ], + "keyD": { + "keyE": "nested", + "keyF": false, + "keyG": 123.45, + "keyH": null + } + } + } + } + )")); +} + +IN_PROC_BROWSER_TEST_F(JsFlowExecutorTest, RunMultipleNativeActions) { + EXPECT_CALL(mock_delegate_, RunNativeAction) + .WillOnce([&](auto value, auto callback) { + EXPECT_EQ(*value, *UniqueValueFromJson(R"( + {"type":"string", + "value":"test1"})")); + std::move(callback).Run(ClientStatus(ACTION_APPLIED), nullptr); + }) + .WillOnce([&](auto value, auto callback) { + EXPECT_EQ(*value, *UniqueValueFromJson(R"( + {"type":"string", + "value":"test2"})")); + std::move(callback).Run(ClientStatus(OTHER_ACTION_STATUS), nullptr); + }); + + // Note: the overall flow should report ACTION_APPLIED since the flow + // completed successfully, but the return value should hold + // OTHER_ACTION_STATUS, i.e., 3. + std::unique_ptr<base::Value> result; + EXPECT_THAT(RunTest(R"( + let [status, value] = await runNativeAction('test1'); + if (status == 2) { // ACTION_APPLIED + [status, value] = await runNativeAction('test2'); + } + return status; + )", + result), + Property(&ClientStatus::proto_status, ACTION_APPLIED)); + EXPECT_EQ(*result, *base::JSONReader::Read(R"( + { + "result": { + "description": "3", + "type": "number", + "value": 3 + } + } + )")); +} + +IN_PROC_BROWSER_TEST_F(JsFlowExecutorTest, ReturnInteger) { + std::unique_ptr<base::Value> result; + ClientStatus status = RunTest("return 12345;", result); + EXPECT_EQ(status.proto_status(), ACTION_APPLIED); + EXPECT_EQ(*result, *base::JSONReader::Read(R"( + { + "result": { + "description": "12345", + "type": "number", + "value": 12345 + } + } + )")); +} + +IN_PROC_BROWSER_TEST_F(JsFlowExecutorTest, ReturnString) { + std::unique_ptr<base::Value> result; + ClientStatus status = RunTest("return 'Hello world!';", result); + EXPECT_EQ(status.proto_status(), ACTION_APPLIED); + EXPECT_EQ(*result, *base::JSONReader::Read(R"( + { + "result": { + "type": "string", + "value": "Hello world!" + } + } + )")); +} + +IN_PROC_BROWSER_TEST_F(JsFlowExecutorTest, ReturnDictionary) { + std::unique_ptr<base::Value> result; + ClientStatus status = RunTest( + R"( + return { + "keyA":12345, + "keyB":"Hello world!", + "keyC": ["array", "of", "strings"], + "keyD": { + "keyE": "nested", + "keyF": true, + "keyG": 123.45, + "keyH": null + } + }; + )", + result); + EXPECT_EQ(status.proto_status(), ACTION_APPLIED); + EXPECT_EQ(*result, *base::JSONReader::Read(R"( + { + "result": { + "type": "object", + "value": { + "keyA": 12345, + "keyB": "Hello world!", + "keyC": ["array", "of", "strings"], + "keyD": { + "keyE": "nested", + "keyF": true, + "keyG": 123.45, + "keyH": null + } + } + } + } + )")); +} + +IN_PROC_BROWSER_TEST_F(JsFlowExecutorTest, ReturnNothing) { + std::unique_ptr<base::Value> result; + ClientStatus status = RunTest("", result); + EXPECT_EQ(status.proto_status(), ACTION_APPLIED); + EXPECT_EQ(*result, *base::JSONReader::Read(R"( + { + "result": { + "type": "undefined" + } + } + )")); +} + +IN_PROC_BROWSER_TEST_F(JsFlowExecutorTest, ReturnNull) { + std::unique_ptr<base::Value> result; + ClientStatus status = RunTest("return null;", result); + EXPECT_EQ(status.proto_status(), ACTION_APPLIED); + EXPECT_EQ(*result, *base::JSONReader::Read(R"( + { + "result": { + "subtype": "null", + "type": "object", + "value": null + } + } + )")); +} + +IN_PROC_BROWSER_TEST_F(JsFlowExecutorTest, ExceptionReporting) { + std::unique_ptr<base::Value> result; + ClientStatus status = RunTest("throw new Error('Hello world!');", result); + EXPECT_EQ(status.proto_status(), UNEXPECTED_JS_ERROR); + ASSERT_NE(result, nullptr); + + absl::optional<base::Value> exceptionDetails = + result->ExtractKey("exceptionDetails"); + ASSERT_NE(exceptionDetails, absl::nullopt); + + EXPECT_THAT(exceptionDetails->ExtractKey("text")->GetIfString(), + Pointee(Eq("Uncaught (in promise) Error: Hello world!"))); + + // We can't currently check the contents of the reported stack frames since + // they depend on the internal wrapper. For now, we simply test that this is + // not empty. For reference, at the time of writing, this was the full output: + // "exceptionDetails": { + // "columnNumber": 0, + // "exception": { + // "className": "Error", + // "description": "Error: Hello world! + // at <anonymous>:13:11 + // at <anonymous>:13:41", + // "objectId": "1450023673453216843.4.1", + // "subtype": "error", + // "type": "object" + // }, + // "exceptionId": 2, + // "lineNumber": 0, + // "text": "Uncaught (in promise) Error: Hello world!" + // } + EXPECT_THAT(exceptionDetails->ExtractKey("exception"), Ne(absl::nullopt)); +} + +IN_PROC_BROWSER_TEST_F(JsFlowExecutorTest, RunMultipleConsecutiveFlows) { + for (int i = 0; i < 10; ++i) { + std::unique_ptr<base::Value> result; + ClientStatus status = + RunTest(base::StrCat({"return ", base::NumberToString(i)}), result); + EXPECT_EQ(status.proto_status(), ACTION_APPLIED); + EXPECT_EQ(result->ExtractKey("result")->ExtractKey("value")->GetIfInt(), i); + } +} + +IN_PROC_BROWSER_TEST_F(JsFlowExecutorTest, + UnserializableRunNativeActionArgument) { + std::unique_ptr<base::Value> result; + EXPECT_CALL(mock_delegate_, RunNativeAction).Times(0); + ClientStatus status = RunTest( + R"( + function foo(){} + // foo cannot be serialized as a JSON object, so this should fail. + let [status, result] = await runNativeAction(foo); + return status; + )", + result); + EXPECT_EQ(result, nullptr); + EXPECT_EQ(status.proto_status(), UNEXPECTED_JS_ERROR); + EXPECT_TRUE(status.details().has_unexpected_error_info()); +} + +IN_PROC_BROWSER_TEST_F(JsFlowExecutorTest, StartWhileAlreadyRunningFails) { + EXPECT_CALL(mock_delegate_, RunNativeAction) + .WillOnce(WithArg<1>([&](auto callback) { + // Starting a second flow while the first one is running should fail. + EXPECT_EQ(RunTest(std::string()).proto_status(), INVALID_ACTION); + + // The first flow should be able to finish successfully. + std::move(callback).Run(ClientStatus(ACTION_APPLIED), nullptr); + })); + + std::unique_ptr<base::Value> result; + ClientStatus status = RunTest( + R"( + let [status, result] = await runNativeAction(''); + return status; + )", + result); + EXPECT_EQ(status.proto_status(), ACTION_APPLIED); + EXPECT_EQ(*result, *base::JSONReader::Read(R"( + { + "result": { + "description": "2", + "type": "number", + "value": 2 + } + } + )")); +} + +IN_PROC_BROWSER_TEST_F(JsFlowExecutorTest, EnvironmentIsPreservedBetweenRuns) { + EXPECT_EQ(RunTest("globalFlowState.i = 5;").proto_status(), ACTION_APPLIED); + + std::unique_ptr<base::Value> result; + EXPECT_EQ(RunTest("return globalFlowState.i;", result).proto_status(), + ACTION_APPLIED); + EXPECT_EQ(*result, *base::JSONReader::Read(R"( + { + "result": { + "description": "5", + "type": "number", + "value": 5 + } + } + )")); +} + +} // namespace +} // namespace autofill_assistant
diff --git a/components/autofill_assistant/browser/service.proto b/components/autofill_assistant/browser/service.proto index fb1d448..6c686d4 100644 --- a/components/autofill_assistant/browser/service.proto +++ b/components/autofill_assistant/browser/service.proto
@@ -41,7 +41,8 @@ optional string locale = 5; // country should be a country code as defined by ISO 3166-1-alpha-2. - // The intent is to communicate the client's location to the server. + // The intent is to communicate the client's location to the server. Location + // is extracted from Chrome Variations Service latest country. optional string country = 6; // Experiment ids string provided by the 'caller'.
diff --git a/components/exo/extended_drag_source.cc b/components/exo/extended_drag_source.cc index 3ea0e81e..1701b40 100644 --- a/components/exo/extended_drag_source.cc +++ b/components/exo/extended_drag_source.cc
@@ -7,6 +7,7 @@ #include <memory> #include <string> +#include "ash/drag_drop/drag_drop_tracker.h" #include "ash/public/cpp/window_properties.h" #include "ash/shell.h" #include "ash/wm/toplevel_window_event_handler.h" @@ -18,6 +19,7 @@ #include "components/exo/surface.h" #include "third_party/abseil-cpp/absl/types/optional.h" #include "ui/aura/client/aura_constants.h" +#include "ui/aura/env.h" #include "ui/aura/window.h" #include "ui/aura/window_delegate.h" #include "ui/aura/window_event_dispatcher.h" @@ -169,6 +171,51 @@ // Drag process will be started once OnDragStarted gets called. } +void DispatchGestureEndToWindow(aura::Window* window) { + if (window && window->delegate()) { + ui::GestureEventDetails details(ui::ET_GESTURE_END); + details.set_device_type(ui::GestureDeviceType::DEVICE_TOUCHSCREEN); + ui::GestureEvent gesture_end(0, 0, 0, ui::EventTimeForNow(), details); + window->delegate()->OnGestureEvent(&gesture_end); + } +} + +bool ExtendedDragSource::TakeCapture( + aura::Window* root_window, + aura::Window* source_window, + ash::ToplevelWindowDragDelegate::CancelDragDropCallback callback) { + if (!IsActive()) + return false; + + drag_drop_tracker_.reset(new ash::DragDropTracker(root_window, callback)); + // We need to transfer the current gesture sequence and the GR's touch event + // queue to the |drag_drop_tracker_|'s capture window so that when it takes + // capture, it still gets a valid gesture state. + aura::Env::GetInstance()->gesture_recognizer()->TransferEventsTo( + source_window, drag_drop_tracker_->capture_window(), + ui::TransferTouchesBehavior::kCancel); + // We also send a gesture end to the source window so it can clear state. + // TODO(varunjain): Remove this whole block when gesture sequence + // transferring is properly done in the GR (http://crbug.com/160558) + DispatchGestureEndToWindow(source_window); + drag_drop_tracker_->TakeCapture(); + return true; +} + +aura::Window* ExtendedDragSource::GetTarget(const ui::LocatedEvent& event) { + return drag_drop_tracker_->GetTarget(event); +} + +ui::LocatedEvent* ExtendedDragSource::ConvertEvent( + aura::Window* target, + const ui::LocatedEvent& event) { + return drag_drop_tracker_->ConvertEvent(target, event); +} + +aura::Window* ExtendedDragSource::capture_window() { + return drag_drop_tracker_->capture_window(); +} + bool ExtendedDragSource::IsActive() const { return !!source_; } @@ -324,6 +371,7 @@ } event_blocker_.reset(); drag_source_window_ = nullptr; + drag_drop_tracker_.reset(); UnlockCursor(); }
diff --git a/components/exo/extended_drag_source.h b/components/exo/extended_drag_source.h index 86cef84..75e19bb 100644 --- a/components/exo/extended_drag_source.h +++ b/components/exo/extended_drag_source.h
@@ -18,6 +18,10 @@ #include "ui/gfx/geometry/point.h" #include "ui/gfx/geometry/point_f.h" +namespace ash { +class DragDropTracker; +} + namespace aura { class Window; } @@ -80,6 +84,14 @@ ui::mojom::DragOperation OnToplevelWindowDragDropped() override; void OnToplevelWindowDragCancelled() override; void OnToplevelWindowDragEvent(ui::LocatedEvent* event) override; + bool TakeCapture(aura::Window* root_window, + aura::Window* source_window, + ash::ToplevelWindowDragDelegate::CancelDragDropCallback + callback) override; + aura::Window* GetTarget(const ui::LocatedEvent& event) override; + ui::LocatedEvent* ConvertEvent(aura::Window* target, + const ui::LocatedEvent& event) override; + aura::Window* capture_window() override; // DataSourceObserver: void OnDataSourceDestroying(DataSource* source) override; @@ -114,6 +126,8 @@ std::unique_ptr<aura::ScopedWindowEventTargetingBlocker> event_blocker_; aura::Window* drag_source_window_ = nullptr; + std::unique_ptr<ash::DragDropTracker> drag_drop_tracker_; + base::ObserverList<Observer>::Unchecked observers_; base::WeakPtrFactory<ExtendedDragSource> weak_factory_{this};
diff --git a/components/exo/server/wayland_server_controller.cc b/components/exo/server/wayland_server_controller.cc index d0eab295..3521873 100644 --- a/components/exo/server/wayland_server_controller.cc +++ b/components/exo/server/wayland_server_controller.cc
@@ -47,7 +47,11 @@ return g_instance; } -WaylandServerController::~WaylandServerController() {} +WaylandServerController::~WaylandServerController() { + // TODO(https://crbug.com/1124106): Investigate if we can eliminate Shutdown + // methods. + display_->Shutdown(); +} WaylandServerController::WaylandServerController( std::unique_ptr<DataExchangeDelegate> data_exchange_delegate,
diff --git a/components/exo/wayland/server.cc b/components/exo/wayland/server.cc index d931b8f..d370c6e 100644 --- a/components/exo/wayland/server.cc +++ b/components/exo/wayland/server.cc
@@ -439,7 +439,6 @@ // TODO(https://crbug.com/1124106): Investigate if we can eliminate Shutdown // methods. serial_tracker_->Shutdown(); - display_->Shutdown(); } // static
diff --git a/components/permissions/permission_request_manager.cc b/components/permissions/permission_request_manager.cc index ab4056f..7c2d6ad 100644 --- a/components/permissions/permission_request_manager.cc +++ b/components/permissions/permission_request_manager.cc
@@ -168,6 +168,12 @@ return; } + if (source_frame->IsNestedWithinFencedFrame()) { + request->Cancelled(); + request->RequestFinished(); + return; + } + if (is_notification_prompt_cooldown_active_ && request->GetContentSettingsType() == ContentSettingsType::NOTIFICATIONS) { // Short-circuit by canceling rather than denying to avoid creating a large
diff --git a/components/signin/core/browser/signin_header_helper_unittest.cc b/components/signin/core/browser/signin_header_helper_unittest.cc index 9638885..52df4a17 100644 --- a/components/signin/core/browser/signin_header_helper_unittest.cc +++ b/components/signin/core/browser/signin_header_helper_unittest.cc
@@ -329,7 +329,7 @@ // Mirror is always enabled on Android and iOS, so these tests are only relevant // on Desktop. -#if BUILDFLAG(ENABLE_DICE_SUPPORT) && !BUILDFLAG(IS_CHROMEOS_LACROS) +#if BUILDFLAG(ENABLE_DICE_SUPPORT) TEST_F(SigninHeaderHelperTest, TestMirrorRequestGaiaURL) { // No request when account consistency is disabled. @@ -636,7 +636,7 @@ } } -#endif // BUILDFLAG(ENABLE_DICE_SUPPORT) && !BUILDFLAG(IS_CHROMEOS_LACROS) +#endif // BUILDFLAG(ENABLE_DICE_SUPPORT) // Tests that the Mirror header request is returned normally when the redirect // URL is eligible.
diff --git a/components/signin/features.gni b/components/signin/features.gni index 5e0fe03..e253b1e4 100644 --- a/components/signin/features.gni +++ b/components/signin/features.gni
@@ -5,8 +5,7 @@ import("//build/config/chromeos/ui_mode.gni") # Dice is supported on the platform (but not necessarily enabled). -enable_dice_support = - is_linux || is_chromeos_lacros || is_mac || is_win || is_fuchsia +enable_dice_support = is_linux || is_mac || is_win || is_fuchsia # Mirror is enabled and other account consistency mechanisms are not available. -enable_mirror = is_android || is_chromeos_ash || is_ios +enable_mirror = is_android || is_chromeos_ash || is_chromeos_lacros || is_ios
diff --git a/components/signin/internal/identity_manager/profile_oauth2_token_service_builder.cc b/components/signin/internal/identity_manager/profile_oauth2_token_service_builder.cc index c17cc2e..55a8894 100644 --- a/components/signin/internal/identity_manager/profile_oauth2_token_service_builder.cc +++ b/components/signin/internal/identity_manager/profile_oauth2_token_service_builder.cc
@@ -52,7 +52,7 @@ signin_client, std::move(device_accounts_provider), account_tracker_service); } -#elif BUILDFLAG(IS_CHROMEOS_ASH) +#elif BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS) std::unique_ptr<ProfileOAuth2TokenServiceDelegate> CreateCrOsOAuthDelegate( AccountTrackerService* account_tracker_service, network::NetworkConnectionTracker* network_connection_tracker, @@ -64,18 +64,6 @@ } #elif BUILDFLAG(ENABLE_DICE_SUPPORT) -#if BUILDFLAG(IS_CHROMEOS_LACROS) -std::unique_ptr<ProfileOAuth2TokenServiceDelegate> CreateCrOsOAuthDelegate( - AccountTrackerService* account_tracker_service, - network::NetworkConnectionTracker* network_connection_tracker, - account_manager::AccountManagerFacade* account_manager_facade, - bool is_regular_profile) { - return std::make_unique<signin::ProfileOAuth2TokenServiceDelegateChromeOS>( - account_tracker_service, network_connection_tracker, - account_manager_facade, is_regular_profile); -} -#endif // BUILDFLAG(IS_CHROMEOS_LACROS) - std::unique_ptr<MutableProfileOAuth2TokenServiceDelegate> CreateMutableProfileOAuthDelegate( AccountTrackerService* account_tracker_service, @@ -133,25 +121,10 @@ return CreateIOSOAuthDelegate(signin_client, std::move(device_accounts_provider), account_tracker_service); -#elif BUILDFLAG(IS_CHROMEOS_ASH) +#elif BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS) return CreateCrOsOAuthDelegate(account_tracker_service, network_connection_tracker, account_manager_facade, is_regular_profile); -#elif BUILDFLAG(IS_CHROMEOS_LACROS) - // For the time being, Mirror is enabled only in the first / "Main" Profile in - // Lacros. - if (account_consistency == signin::AccountConsistencyMethod::kMirror) { - return CreateCrOsOAuthDelegate(account_tracker_service, - network_connection_tracker, - account_manager_facade, is_regular_profile); - } else { - // TODO(crbug.com/1198490): Remove this when we don't need DICE and - // `MutableProfileOAuth2TokenServiceDelegate` on Lacros anymore. - return CreateMutableProfileOAuthDelegate( - account_tracker_service, account_consistency, - delete_signin_cookies_on_exit, token_web_data, signin_client, - network_connection_tracker); - } #elif BUILDFLAG(ENABLE_DICE_SUPPORT) // Fall back to |MutableProfileOAuth2TokenServiceDelegate| on all platforms // other than Android, iOS, and Chrome OS (Ash and Lacros).
diff --git a/components/strings/components_strings_eu.xtb b/components/strings/components_strings_eu.xtb index d9b5aec3..4020f5c2 100644 --- a/components/strings/components_strings_eu.xtb +++ b/components/strings/components_strings_eu.xtb
@@ -645,6 +645,7 @@ <translation id="3087734570205094154">Behekoa</translation> <translation id="3095940652251934233">Statement</translation> <translation id="3096100844101284527">Gehitu jasotze-helbidea</translation> +<translation id="3098513225387949945">Ez ikusi egin zaio, desgaitzeko zerrendak * karakterearen berdina den eredu bat daukalako, eta hori gidalerroa desgaitzea bezala da.</translation> <translation id="3105172416063519923">Aktibo IDa:</translation> <translation id="3107591622054137333"><ph name="BEGIN_LINK" />DNS konfigurazio segurua egiaztatzen<ph name="END_LINK" /></translation> <translation id="3108943290502734357">Erdiko erretilua</translation> @@ -1548,6 +1549,7 @@ <translation id="6008122969617370890">N-tik 1erako ordena</translation> <translation id="6008256403891681546">JCB</translation> <translation id="6014801569448771146">Egiaztatu pasahitzak</translation> +<translation id="6014851866995737824">Ez ikusi egin zaio, gaitzeko edo desgaitzeko zerrenda falta delako.</translation> <translation id="6015796118275082299">Urtea</translation> <translation id="6017514345406065928">Berdea</translation> <translation id="6017850046339264347"><ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> webguneko erasotzaileek beste zerbaiten plantak egiten dituzten aplikazio iruzurtiak instala ditzakete edo datuak bil ditzakete zure jarraipena egiteko. <ph name="BEGIN_LEARN_MORE_LINK" />Lortu informazio gehiago<ph name="END_LEARN_MORE_LINK" /></translation>
diff --git a/components/strings/components_strings_km.xtb b/components/strings/components_strings_km.xtb index 700d270..0ace1d2f 100644 --- a/components/strings/components_strings_km.xtb +++ b/components/strings/components_strings_km.xtb
@@ -406,6 +406,7 @@ <translation id="2184405333245229118">{COUNT,plural, =1{អាសយដ្ឋាន 1}other{អាសយដ្ឋាន #}}</translation> <translation id="2187317261103489799">ស្វែងរក (លំនាំដើម)</translation> <translation id="2188375229972301266">ចោះច្រើនរន្ធខាងក្រោម</translation> +<translation id="2188852899391513400">ពាក្យសម្ងាត់ដែលអ្នកទើបតែប្រើត្រូវបានរកឃើញនៅក្នុងការបែកធ្លាយទិន្នន័យ។ ដើម្បីការពារសុវត្ថិភាពគណនីរបស់អ្នក កម្មវិធីគ្រប់គ្រងពាក្យសម្ងាត់ Google ណែនាំឱ្យផ្លាស់ប្ដូរពាក្យសម្ងាត់នេះឥឡូវនេះ រួចពិនិត្យមើលពាក្យសម្ងាត់ដែលអ្នកបានរក្សាទុក។</translation> <translation id="2202020181578195191">បញ្ចូលឆ្នាំផុតកំណត់ឲ្យបានត្រឹមត្រូវ</translation> <translation id="22081806969704220">ទម្រទី 3</translation> <translation id="2212735316055980242">គោលការណ៍មិនត្រូវបានរកឃើញទេ</translation> @@ -901,6 +902,7 @@ <translation id="3827112369919217609">ដាច់ខាត</translation> <translation id="3827475930221174051">ផ្អែកលើសកម្មភាពដែលពាក់ព័ន្ធនឹង "<ph name="SEARCH_TERM" />"</translation> <translation id="3828924085048779000">ឃ្លាសម្ងាត់ទទេមិនត្រូវបានអនុញ្ញាតទេ។</translation> +<translation id="3831065134033923230">ពិនិត្យមើលពាក្យសម្ងាត់ដែលអ្នកបានរក្សាទុក</translation> <translation id="3831915413245941253"><ph name="ENROLLMENT_DOMAIN" /> បានដំឡើងកម្មវិធីបន្ថែមសម្រាប់មុខងារបន្ថែម។ កម្មវិធីបន្ថែមមានសិទ្ធិចូលប្រើទិន្នន័យមួយចំនួនរបស់អ្នក។</translation> <translation id="3832522519263485449">ចោះច្រើនរន្ធខាងឆ្វេង</translation> <translation id="3835233591525155343">ការប្រើប្រាស់ឧបករណ៍របស់អ្នក</translation> @@ -1101,6 +1103,7 @@ <translation id="443673843213245140">ការប្រើប្រូកស៊ីត្រូវបានបិទដំណើរការ ប៉ុន្តែការកំណត់ប្រូកស៊ីដែលច្បាស់លាស់ត្រូវបានបញ្ជាក់។</translation> <translation id="4450893287417543264">កុំបង្ហាញម្ដងទៀត</translation> <translation id="4451135742916150903">អាចស្នើសុំភ្ជាប់ជាមួយឧបករណ៍ HID</translation> +<translation id="4452328064229197696">ពាក្យសម្ងាត់ដែលអ្នកទើបតែប្រើត្រូវបានរកឃើញនៅក្នុងការបែកធ្លាយទិន្នន័យ។ ដើម្បីការពារសុវត្ថិភាពគណនីរបស់អ្នក កម្មវិធីគ្រប់គ្រងពាក្យសម្ងាត់ Google ណែនាំឱ្យពិនិត្យមើលពាក្យសម្ងាត់ដែលអ្នកបានរក្សាទុក។</translation> <translation id="4455222631300069614">សូមប្ដូរពាក្យសម្ងាត់របស់អ្នកឥឡូវនេះ</translation> <translation id="4460315069258617173">អនុញ្ញាត រហូតទាល់តែអ្នកបិទផ្ទាំងសម្រាប់គេហទំព័រនេះ</translation> <translation id="4464826014807964867">គេហទំព័រដែលមានព័ត៌មានពីស្ថាប័នរបស់អ្នក</translation> @@ -1590,6 +1593,7 @@ <translation id="6051221802930200923">អ្នកមិនអាចចូលទៅកាន់ <ph name="SITE" /> ឥឡូវនេះបានទេ ដោយសារតែគេហទំព័រនេះប្រើការខ្ទាស់វិញ្ញាបនបត្រ។ ជាទូទៅបញ្ហាបណ្តាញ ឬការវាយប្រហារកើតឡើងជាបណ្តោះអាសន្ន ដូច្នេះទំព័រនេះនឹងដំណើរការល្អឡើងវិញនៅពេលក្រោយ។</translation> <translation id="6051898664905071243">ចំនួនទំព័រ៖</translation> <translation id="6052284303005792909">•</translation> +<translation id="6052319569711353666">ពាក្យសម្ងាត់ដែលអ្នកទើបតែប្រើត្រូវបានរកឃើញនៅក្នុងការបែកធ្លាយទិន្នន័យ។ កម្មវិធីគ្រប់គ្រងពាក្យសម្ងាត់ Google ណែនាំឱ្យផ្លាស់ប្ដូរពាក្យសម្ងាត់នេះឥឡូវនេះ។</translation> <translation id="6055888660316801977">សន្លឹកព័ត៌មានផ្ទៀងផ្ទាត់ការបង់ប្រាក់មានសុវត្ថិភាព ដែលគ្មានព័ត៌មានផ្ទៀងផ្ទាត់ត្រូវគ្នា</translation> <translation id="6058977677006700226">ប្រើបណ្ណរបស់អ្នកនៅលើឧបករណ៍ទាំងអស់របស់អ្នកឬ?</translation> <translation id="6059925163896151826">ឧបករណ៍ USB</translation>
diff --git a/components/strings/components_strings_ko.xtb b/components/strings/components_strings_ko.xtb index 10773c98..ae469de 100644 --- a/components/strings/components_strings_ko.xtb +++ b/components/strings/components_strings_ko.xtb
@@ -645,6 +645,7 @@ <translation id="3087734570205094154">맨 아래</translation> <translation id="3095940652251934233">Statement</translation> <translation id="3096100844101284527">수령 주소 추가</translation> +<translation id="3098513225387949945">사용 안함 목록에 정책을 사용 중지하는 것과 동일한 '*'와 동등한 패턴이 포함되어 있으므로 무시되었습니다.</translation> <translation id="3105172416063519923">애셋 ID:</translation> <translation id="3107591622054137333"><ph name="BEGIN_LINK" />보안 DNS 구성 확인<ph name="END_LINK" /></translation> <translation id="3108943290502734357">중간 트레이</translation> @@ -1552,6 +1553,7 @@ <translation id="6008122969617370890">N-to-1 순서</translation> <translation id="6008256403891681546">JCB</translation> <translation id="6014801569448771146">비밀번호 확인</translation> +<translation id="6014851866995737824">'사용' 또는 '사용 안함' 목록이 없으므로 무시되었습니다.</translation> <translation id="6015796118275082299">연도</translation> <translation id="6017514345406065928">녹색</translation> <translation id="6017850046339264347"><ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" />에 있는 공격자는 다른 앱인 것처럼 가장하거나 사용자를 추적하는 데 사용될 수 있는 데이터를 수집하는 사기성 앱을 설치할 수도 있습니다. <ph name="BEGIN_LEARN_MORE_LINK" />자세히 알아보기<ph name="END_LEARN_MORE_LINK" /></translation>
diff --git a/components/strings/components_strings_my.xtb b/components/strings/components_strings_my.xtb index 93d5887..74036b355 100644 --- a/components/strings/components_strings_my.xtb +++ b/components/strings/components_strings_my.xtb
@@ -2495,7 +2495,7 @@ <translation id="9141013498910525015">လိပ်စာများ စီမံရန်</translation> <translation id="9144951720726881238">သက်တမ်းကုန်ဆုံးရက်-</translation> <translation id="9148088599418889305">ပစ္စည်းပို့နည်းလမ်း ရွေးရန်</translation> -<translation id="9148507642005240123">&တည်းဖြတ်မှုကို တစ်ဆင့်နောက်ပြန်ရန်</translation> +<translation id="9148507642005240123">&တည်းဖြတ်မှု မလုပ်တော့ရန်</translation> <translation id="9150045010208374699">သင့်ကင်မရာအား အသုံးပြုမည်</translation> <translation id="9150685862434908345">သင့်စီမံခန့်ခွဲသူက သင်၏ဘရောင်ဇာ စနစ်ထည့်သွင်းမှုကို အဝေးမှ ပြောင်းလဲနိုင်ပါသည်။ ဤစက်ပေါ်ရှိ လုပ်ဆောင်ချက်များကိုလည်း Chrome ပြင်ပမှ စီမံခန့်ခွဲထားခြင်း ဖြစ်နိုင်သည်။ <ph name="BEGIN_LINK" />ပိုမိုလေ့လာရန်<ph name="END_LINK" /></translation> <translation id="9154194610265714752">မွမ်းမံပြီး</translation>
diff --git a/components/strings/components_strings_tr.xtb b/components/strings/components_strings_tr.xtb index 07aa32a..835f35b 100644 --- a/components/strings/components_strings_tr.xtb +++ b/components/strings/components_strings_tr.xtb
@@ -401,6 +401,7 @@ <translation id="2184405333245229118">{COUNT,plural, =1{1 adres}other{# adres}}</translation> <translation id="2187317261103489799">Algıla (varsayılan)</translation> <translation id="2188375229972301266">Altta çoklu zımba</translation> +<translation id="2188852899391513400">Az önce kullandığınız şifrenin bir veri ihlali sonucunda açığa çıktığı anlaşıldı. Google Şifre Yöneticisi, hesaplarınızın güvenliğini sağlamak için bu şifreyi hemen değiştirmenizi ve ardından kayıtlı şifrelerinizi kontrol etmenizi öneriyor.</translation> <translation id="2202020181578195191">Geçerli bir son kullanma yılı girin</translation> <translation id="22081806969704220">Tepsi 3</translation> <translation id="2212735316055980242">Politika bulunamadı</translation> @@ -895,6 +896,7 @@ <translation id="3827112369919217609">Mutlak</translation> <translation id="3827475930221174051">"<ph name="SEARCH_TERM" />" ile ilgili etkinliğe dayalı öğeler</translation> <translation id="3828924085048779000">Boş parolaya izin verilmez.</translation> +<translation id="3831065134033923230">Kayıtlı şifrelerinizi kontrol edin</translation> <translation id="3831915413245941253"><ph name="ENROLLMENT_DOMAIN" /> diğer işlevler için uzantılar yüklemiş. Söz konusu uzantıların bazı verilerinize erişimi bulunuyor.</translation> <translation id="3832522519263485449">Solda çoklu delik</translation> <translation id="3835233591525155343">Cihaz kullanımınız</translation> @@ -1090,6 +1092,7 @@ <translation id="443673843213245140">Proxy kullanımı devre dışı, ancak açık bir proxy yapılandırması belirtildi.</translation> <translation id="4450893287417543264">Bir daha gösterme</translation> <translation id="4451135742916150903">HID cihazlarına bağlanmak isteyebilir</translation> +<translation id="4452328064229197696">Az önce kullandığınız şifrenin bir veri ihlali sonucunda açığa çıktığı anlaşıldı. Google Şifre Yöneticisi, hesaplarınızın güvenliğini sağlamak için kayıtlı şifrelerinizi kontrol etmenizi öneriyor.</translation> <translation id="4455222631300069614">Şifrenizi hemen değiştirin</translation> <translation id="4460315069258617173">Bu siteye ait sekmeleri kapatana kadar izin verilir</translation> <translation id="4464826014807964867">Kuruluşunuzun gönderdiği bilgilerin olduğu web siteleri</translation> @@ -1579,6 +1582,7 @@ <translation id="6051221802930200923"><ph name="SITE" /> sitesi sertifika sabitleme yöntemi kullandığından siteyi şu anda ziyaret edemezsiniz. Ağ hataları ve saldırılar genellikle geçici olduğundan bu sayfa muhtemelen daha sonra çalışacaktır.</translation> <translation id="6051898664905071243">Sayfa sayısı:</translation> <translation id="6052284303005792909">•</translation> +<translation id="6052319569711353666">Az önce kullandığınız şifrenin bir veri ihlali sonucunda açığa çıktığı anlaşıldı. Google Şifre Yöneticisi bu şifreyi hemen değiştirmenizi öneriyor.</translation> <translation id="6055888660316801977">Eşleşmeyen güvenli ödeme kimlik bilgisi sayfası</translation> <translation id="6058977677006700226">Kartlarınızı tüm cihazlarınızda kullanmak istiyor musunuz?</translation> <translation id="6059925163896151826">USB cihazları</translation>
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn index 1aa915a..72ba223 100644 --- a/content/browser/BUILD.gn +++ b/content/browser/BUILD.gn
@@ -2396,7 +2396,7 @@ } if (!is_android) { - deps += [ "//content/browser/devtools:devtools_resources" ] + deps += [ "//content/browser/devtools:devtools_resources_extern" ] } if (enable_basic_printing) {
diff --git a/content/browser/accessibility/accessibility_action_browsertest.cc b/content/browser/accessibility/accessibility_action_browsertest.cc index 4f4d67ed..d55ac94 100644 --- a/content/browser/accessibility/accessibility_action_browsertest.cc +++ b/content/browser/accessibility/accessibility_action_browsertest.cc
@@ -474,7 +474,7 @@ target->CreateTextPositionAt(0); BrowserAccessibility::AXPosition end_of_line_1 = start_position->CreateNextLineEndPosition( - ui::AXBoundaryBehavior::CrossBoundary); + ui::AXBoundaryBehavior::kCrossBoundary); EXPECT_EQ(5, end_of_line_1->text_offset()); #endif } @@ -508,7 +508,7 @@ target->CreateTextPositionAt(0); BrowserAccessibility::AXPosition end_of_line_1 = start_position->CreateNextLineEndPosition( - ui::AXBoundaryBehavior::CrossBoundary); + ui::AXBoundaryBehavior::kCrossBoundary); EXPECT_EQ(5, end_of_line_1->text_offset()); #endif }
diff --git a/content/browser/accessibility/accessibility_auralinux_browsertest.cc b/content/browser/accessibility/accessibility_auralinux_browsertest.cc index 0ea4ace46..20fe460a 100644 --- a/content/browser/accessibility/accessibility_auralinux_browsertest.cc +++ b/content/browser/accessibility/accessibility_auralinux_browsertest.cc
@@ -460,7 +460,7 @@ } IN_PROC_BROWSER_TEST_F(AccessibilityAuraLinuxBrowserTest, - TestParagraphTextAtOffsetWithBoundarySentence) { + DISABLED_TestParagraphTextAtOffsetWithBoundarySentence) { LoadInitialAccessibilityTreeFromHtml(std::string( R"HTML(<!DOCTYPE html> <html>
diff --git a/content/browser/accessibility/accessibility_win_browsertest.cc b/content/browser/accessibility/accessibility_win_browsertest.cc index 4fdbe79..abd5bf58 100644 --- a/content/browser/accessibility/accessibility_win_browsertest.cc +++ b/content/browser/accessibility/accessibility_win_browsertest.cc
@@ -1774,7 +1774,7 @@ Microsoft::WRL::ComPtr<IAccessibleText> input_text; SetUpScrollableInputField(&input_text); - int contents_string_length = static_cast<int>(InputContentsString().size()); + LONG contents_string_length = static_cast<LONG>(InputContentsString().size()); constexpr LONG visible_characters_start = 21; LONG n_characters; ASSERT_HRESULT_SUCCEEDED(input_text->get_nCharacters(&n_characters)); @@ -1969,7 +1969,7 @@ Microsoft::WRL::ComPtr<IAccessibleText> input_text; SetUpScrollableInputTypeSearchField(&input_text); - int contents_string_length = static_cast<int>(InputContentsString().size()); + LONG contents_string_length = static_cast<LONG>(InputContentsString().size()); constexpr LONG visible_characters_start = 21; LONG n_characters; ASSERT_HRESULT_SUCCEEDED(input_text->get_nCharacters(&n_characters)); @@ -2618,7 +2618,7 @@ AccessibilityNotificationWaiter waiter( shell()->web_contents(), ui::kAXModeComplete, ax::mojom::Event::kTextSelectionChanged); - int contents_string_length = static_cast<int>(InputContentsString().size()); + LONG contents_string_length = static_cast<LONG>(InputContentsString().size()); start_offset = 0; end_offset = contents_string_length; EXPECT_HRESULT_FAILED(input_text->setSelection(1, start_offset, end_offset)); @@ -2654,7 +2654,7 @@ LONG n_ranges = 1; IA2Range* ranges = reinterpret_cast<IA2Range*>(CoTaskMemAlloc(sizeof(IA2Range))); - int contents_string_length = static_cast<int>(InputContentsString().size()); + LONG contents_string_length = static_cast<LONG>(InputContentsString().size()); ranges[0].anchor = ax_input.Get(); ranges[0].anchorOffset = -1; ranges[0].active = ax_input.Get(); @@ -2859,7 +2859,7 @@ // There is no selection, just a caret. EXPECT_EQ(E_INVALIDARG, hr); - int contents_string_length = static_cast<int>(InputContentsString().size()); + LONG contents_string_length = static_cast<LONG>(InputContentsString().size()); AccessibilityNotificationWaiter waiter( shell()->web_contents(), ui::kAXModeComplete, ax::mojom::Event::kTextSelectionChanged); @@ -2896,7 +2896,7 @@ Microsoft::WRL::ComPtr<IAccessible2_4> ax_textarea; ASSERT_HRESULT_SUCCEEDED(textarea_text.As(&ax_textarea)); - int contents_string_length = static_cast<int>(InputContentsString().size()); + LONG contents_string_length = static_cast<LONG>(InputContentsString().size()); LONG n_ranges = 1; IA2Range* ranges = reinterpret_cast<IA2Range*>(CoTaskMemAlloc(sizeof(IA2Range))); @@ -3471,7 +3471,7 @@ EXPECT_EQ(nullptr, text.Get()); hr = input_text->get_textAtOffset(invalid_offset, IA2_TEXT_BOUNDARY_SENTENCE, &start_offset, &end_offset, text.Receive()); - EXPECT_EQ(S_FALSE, hr); + EXPECT_EQ(E_INVALIDARG, hr); EXPECT_EQ(0, start_offset); EXPECT_EQ(0, end_offset); EXPECT_EQ(nullptr, text.Get()); @@ -3508,7 +3508,7 @@ hr = input_text->get_textAtOffset(IA2_TEXT_OFFSET_LENGTH, IA2_TEXT_BOUNDARY_SENTENCE, &start_offset, &end_offset, text.Receive()); - EXPECT_EQ(S_FALSE, hr); + EXPECT_EQ(E_INVALIDARG, hr); EXPECT_EQ(0, start_offset); EXPECT_EQ(0, end_offset); EXPECT_EQ(nullptr, text.Get()); @@ -3578,7 +3578,7 @@ hr = textarea_text->get_textAtOffset( invalid_offset, IA2_TEXT_BOUNDARY_SENTENCE, &start_offset, &end_offset, text.Receive()); - EXPECT_EQ(S_FALSE, hr); + EXPECT_EQ(E_INVALIDARG, hr); EXPECT_EQ(0, start_offset); EXPECT_EQ(0, end_offset); EXPECT_EQ(nullptr, text.Get()); @@ -3617,7 +3617,7 @@ hr = textarea_text->get_textAtOffset( IA2_TEXT_OFFSET_LENGTH, IA2_TEXT_BOUNDARY_SENTENCE, &start_offset, &end_offset, text.Receive()); - EXPECT_EQ(S_FALSE, hr); + EXPECT_EQ(E_INVALIDARG, hr); EXPECT_EQ(0, start_offset); EXPECT_EQ(0, end_offset); EXPECT_EQ(nullptr, text.Get()); @@ -3643,7 +3643,7 @@ Microsoft::WRL::ComPtr<IAccessibleText> input_text; SetUpInputField(&input_text); - int contents_string_length = static_cast<int>(InputContentsString().size()); + LONG contents_string_length = static_cast<LONG>(InputContentsString().size()); for (LONG offset = 0; offset < contents_string_length; ++offset) { std::wstring expected_text(1, InputContentsString()[offset]); LONG expected_start_offset = offset; @@ -3671,7 +3671,7 @@ Microsoft::WRL::ComPtr<IAccessibleText> textarea_text; SetUpTextareaField(&textarea_text); - int contents_string_length = InputContentsString().size(); + LONG contents_string_length = static_cast<LONG>(InputContentsString().size()); for (LONG offset = 0; offset < contents_string_length; ++offset) { std::wstring expected_text(1, TextAreaContentsString()[offset]); LONG expected_start_offset = offset; @@ -3859,7 +3859,7 @@ CheckTextAtOffset(input_text, 39, IA2_TEXT_BOUNDARY_WORD, 38, 40, L", "); // Trailing final punctuation should not be part of the last word. - int contents_string_length = static_cast<int>(InputContentsString().size()); + LONG contents_string_length = static_cast<LONG>(InputContentsString().size()); CheckTextAtOffset(input_text, 40, IA2_TEXT_BOUNDARY_WORD, 40, 44, L"like"); CheckTextAtOffset(input_text, 41, IA2_TEXT_BOUNDARY_WORD, 40, 44, L"like"); CheckTextAtOffset(input_text, 44, IA2_TEXT_BOUNDARY_WORD, 44, @@ -3924,7 +3924,7 @@ CheckTextAtOffset(textarea_text, 39, IA2_TEXT_BOUNDARY_WORD, 38, 40, L", "); // Trailing final punctuation should not be part of the last word. - int contents_string_length = static_cast<int>(InputContentsString().size()); + LONG contents_string_length = static_cast<LONG>(InputContentsString().size()); CheckTextAtOffset(textarea_text, 40, IA2_TEXT_BOUNDARY_WORD, 40, 44, L"like"); CheckTextAtOffset(textarea_text, 41, IA2_TEXT_BOUNDARY_WORD, 40, 44, L"like"); CheckTextAtOffset(textarea_text, 44, IA2_TEXT_BOUNDARY_WORD, 44, @@ -3967,33 +3967,78 @@ } IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, - TestTextAtOffsetWithBoundarySentence) { + DISABLED_TestTextAtOffsetWithBoundarySentence) { Microsoft::WRL::ComPtr<IAccessibleText> input_text; SetUpInputField(&input_text); - // Sentence navigation is not currently implemented. - LONG start_offset = 0; - LONG end_offset = 0; - base::win::ScopedBstr text; - HRESULT hr = - input_text->get_textAtOffset(5, IA2_TEXT_BOUNDARY_SENTENCE, &start_offset, - &end_offset, text.Receive()); - EXPECT_EQ(S_FALSE, hr); + const LONG contents_string_length = + static_cast<LONG>(InputContentsString().size()); + const std::wstring expected_text = base::SysUTF8ToWide(InputContentsString()); + for (LONG offset = 0; offset < contents_string_length; ++offset) { + CheckTextAtOffset(input_text, offset, IA2_TEXT_BOUNDARY_SENTENCE, 0, + contents_string_length, expected_text); + } + + // Test special offsets. + CheckTextAtOffset(input_text, IA2_TEXT_OFFSET_CARET, + IA2_TEXT_BOUNDARY_SENTENCE, 0, contents_string_length, + expected_text); + { + LONG start_offset = 0; + LONG end_offset = 0; + base::win::ScopedBstr text; + HRESULT hr = input_text->get_textAtOffset( + IA2_TEXT_OFFSET_LENGTH, IA2_TEXT_BOUNDARY_SENTENCE, &start_offset, + &end_offset, text.Receive()); + EXPECT_EQ(E_INVALIDARG, hr); + EXPECT_EQ(0, start_offset); + EXPECT_EQ(0, end_offset); + EXPECT_EQ(nullptr, text.Get()); + } } IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, - TestMultiLineTextAtOffsetWithBoundarySentence) { + DISABLED_TestMultiLineTextAtOffsetWithBoundarySentence) { Microsoft::WRL::ComPtr<IAccessibleText> textarea_text; SetUpTextareaField(&textarea_text); - // Sentence navigation is not currently implemented. - LONG start_offset = 0; - LONG end_offset = 0; - base::win::ScopedBstr text; - HRESULT hr = textarea_text->get_textAtOffset(25, IA2_TEXT_BOUNDARY_SENTENCE, - &start_offset, &end_offset, - text.Receive()); - EXPECT_EQ(S_FALSE, hr); + const LONG contents_string_length = + static_cast<LONG>(TextAreaContentsString().size()); + const std::vector<LONG> sentence_starts{0, 23, 24, 31, 32}; + const std::vector<LONG> sentence_ends{23, 24, 31, 32, contents_string_length}; + size_t sentence_index = 0; + for (LONG offset = 0; offset < contents_string_length && + sentence_index < sentence_starts.size(); + ++offset) { + if (offset == sentence_starts[sentence_index + 1]) + ++sentence_index; + LONG expected_start_offset = sentence_starts[sentence_index]; + LONG expected_end_offset = sentence_ends[sentence_index]; + const std::wstring expected_text = + base::SysUTF8ToWide(TextAreaContentsString().substr( + sentence_starts[sentence_index], + (sentence_ends[sentence_index] - sentence_starts[sentence_index]))); + CheckTextAtOffset(textarea_text, offset, IA2_TEXT_BOUNDARY_SENTENCE, + expected_start_offset, expected_end_offset, + expected_text); + } + + // Test special offsets. + CheckTextAtOffset(textarea_text, IA2_TEXT_OFFSET_CARET, + IA2_TEXT_BOUNDARY_SENTENCE, 32, contents_string_length, + base::SysUTF8ToWide(TextAreaContentsString().substr(32))); + { + LONG start_offset = 0; + LONG end_offset = 0; + base::win::ScopedBstr text; + HRESULT hr = textarea_text->get_textAtOffset( + IA2_TEXT_OFFSET_LENGTH, IA2_TEXT_BOUNDARY_SENTENCE, &start_offset, + &end_offset, text.Receive()); + EXPECT_EQ(E_INVALIDARG, hr); + EXPECT_EQ(0, start_offset); + EXPECT_EQ(0, end_offset); + EXPECT_EQ(nullptr, text.Get()); + } } IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, @@ -4002,7 +4047,7 @@ SetUpInputField(&input_text); // Single line text fields should return the whole text. - int contents_string_length = static_cast<int>(InputContentsString().size()); + LONG contents_string_length = static_cast<LONG>(InputContentsString().size()); CheckTextAtOffset(input_text, 0, IA2_TEXT_BOUNDARY_LINE, 0, contents_string_length, base::SysUTF8ToWide(InputContentsString())); @@ -4029,7 +4074,7 @@ L"WebKit \n"); // Last line does not have a trailing newline. - int contents_string_length = static_cast<int>(InputContentsString().size()); + LONG contents_string_length = static_cast<LONG>(InputContentsString().size()); CheckTextAtOffset(textarea_text, 32, IA2_TEXT_BOUNDARY_LINE, 32, contents_string_length, L"\"KHTML, like\"."); @@ -4067,8 +4112,8 @@ // Blink represents the blank line with a newline character, so in total there // should be two more newlines. The second newline is not part of the HTML // value attribute however. - int contents_string_length = - static_cast<int>(InputContentsString().size()) + 1; + LONG contents_string_length = + static_cast<LONG>(InputContentsString().size()) + 1; CheckTextAtOffset(textarea_text, 32, IA2_TEXT_BOUNDARY_LINE, 32, contents_string_length, L"\"KHTML, like\".\n"); CheckTextAtOffset(textarea_text, 46, IA2_TEXT_BOUNDARY_LINE, 32,
diff --git a/content/browser/accessibility/ax_platform_node_textrangeprovider_win_browsertest.cc b/content/browser/accessibility/ax_platform_node_textrangeprovider_win_browsertest.cc index da59ff1..24ab89a 100644 --- a/content/browser/accessibility/ax_platform_node_textrangeprovider_win_browsertest.cc +++ b/content/browser/accessibility/ax_platform_node_textrangeprovider_win_browsertest.cc
@@ -2758,7 +2758,7 @@ /*expected_count*/ -1); ASSERT_HRESULT_SUCCEEDED( text_range_provider->ExpandToEnclosingUnit(TextUnit_Word)); - EXPECT_UIA_TEXTRANGE_EQ(text_range_provider, L"\xA0\n"); + EXPECT_UIA_TEXTRANGE_EQ(text_range_provider, L"\xA0"); // Case 2: test on range that includes the whitespace and the following word. GetTextRangeProviderFromTextNode(*node, &text_range_provider); @@ -2771,7 +2771,7 @@ /*expected_count*/ 1); ASSERT_HRESULT_SUCCEEDED( text_range_provider->ExpandToEnclosingUnit(TextUnit_Word)); - EXPECT_UIA_TEXTRANGE_EQ(text_range_provider, L"\xA0\n"); + EXPECT_UIA_TEXTRANGE_EQ(text_range_provider, L"\xA0"); // Case 3: test on degenerate range after whitespace. node = FindNode(ax::mojom::Role::kStaticText, "3.14");
diff --git a/content/browser/accessibility/browser_accessibility.cc b/content/browser/accessibility/browser_accessibility.cc index 4f02ac6..da885b9a 100644 --- a/content/browser/accessibility/browser_accessibility.cc +++ b/content/browser/accessibility/browser_accessibility.cc
@@ -106,26 +106,6 @@ return nullptr; } -int GetBoundaryTextOffsetInsideBaseAnchor( - ax::mojom::MoveDirection direction, - const BrowserAccessibility::AXPosition& base, - const BrowserAccessibility::AXPosition& position) { - if (base->GetAnchor() == position->GetAnchor()) - return position->text_offset(); - - // If the position is outside the anchor of the base position, then return - // the first or last position in the same direction. - switch (direction) { - case ax::mojom::MoveDirection::kNone: - NOTREACHED(); - return position->text_offset(); - case ax::mojom::MoveDirection::kBackward: - return base->CreatePositionAtStartOfAnchor()->text_offset(); - case ax::mojom::MoveDirection::kForward: - return base->CreatePositionAtEndOfAnchor()->text_offset(); - } -} - } // namespace bool BrowserAccessibility::IsValid() const { @@ -1188,29 +1168,6 @@ return result; } -absl::optional<int> BrowserAccessibility::FindTextBoundary( - ax::mojom::TextBoundary boundary, - int offset, - ax::mojom::MoveDirection direction, - ax::mojom::TextAffinity affinity) const { - const AXPosition position = CreateTextPositionAt(offset, affinity); - - // On Windows and Linux ATK, searching for a text boundary should always stop - // at the boundary of the current object. - auto boundary_behavior = ui::AXBoundaryBehavior::StopAtAnchorBoundary; - // On Windows and Linux ATK, it is standard text navigation behavior to stop - // if we are searching in the backwards direction and the current position is - // already at the required text boundary. - DCHECK_NE(direction, ax::mojom::MoveDirection::kNone); - if (direction == ax::mojom::MoveDirection::kBackward) - boundary_behavior = ui::AXBoundaryBehavior::StopIfAlreadyAtBoundary; - - return GetBoundaryTextOffsetInsideBaseAnchor( - direction, position, - position->CreatePositionAtTextBoundary(boundary, direction, - boundary_behavior)); -} - const std::vector<gfx::NativeViewAccessible> BrowserAccessibility::GetUIADirectChildrenInRange( ui::AXPlatformNodeDelegate* start,
diff --git a/content/browser/accessibility/browser_accessibility.h b/content/browser/accessibility/browser_accessibility.h index 3d0c611..529eb09 100644 --- a/content/browser/accessibility/browser_accessibility.h +++ b/content/browser/accessibility/browser_accessibility.h
@@ -510,12 +510,6 @@ int GetIndexInParent() override; gfx::AcceleratedWidget GetTargetForNativeAccessibilityEvent() override; - absl::optional<int> FindTextBoundary( - ax::mojom::TextBoundary boundary, - int offset, - ax::mojom::MoveDirection direction, - ax::mojom::TextAffinity affinity) const override; - const std::vector<gfx::NativeViewAccessible> GetUIADirectChildrenInRange( ui::AXPlatformNodeDelegate* start, ui::AXPlatformNodeDelegate* end) override;
diff --git a/content/browser/accessibility/browser_accessibility_cocoa.mm b/content/browser/accessibility/browser_accessibility_cocoa.mm index befdae21..c9e3952 100644 --- a/content/browser/accessibility/browser_accessibility_cocoa.mm +++ b/content/browser/accessibility/browser_accessibility_cocoa.mm
@@ -2540,7 +2540,7 @@ if (position->IsNullPosition()) return nil; return CreateTextMarker(position->CreateNextCharacterPosition( - ui::AXBoundaryBehavior::CrossBoundary)); + ui::AXBoundaryBehavior::kCrossBoundary)); } if ([attribute @@ -2551,7 +2551,7 @@ if (position->IsNullPosition()) return nil; return CreateTextMarker(position->CreatePreviousCharacterPosition( - ui::AXBoundaryBehavior::CrossBoundary)); + ui::AXBoundaryBehavior::kCrossBoundary)); } if ([attribute @@ -2564,10 +2564,10 @@ BrowserAccessibility::AXPosition startWordPosition = endPosition->CreatePreviousWordStartPosition( - ui::AXBoundaryBehavior::StopAtAnchorBoundary); + ui::AXBoundaryBehavior::kStopAtAnchorBoundary); BrowserAccessibility::AXPosition endWordPosition = endPosition->CreatePreviousWordEndPosition( - ui::AXBoundaryBehavior::StopAtAnchorBoundary); + ui::AXBoundaryBehavior::kStopAtAnchorBoundary); BrowserAccessibility::AXPosition startPosition = *startWordPosition <= *endWordPosition ? std::move(endWordPosition) : std::move(startWordPosition); @@ -2586,10 +2586,10 @@ BrowserAccessibility::AXPosition endWordPosition = startPosition->CreateNextWordEndPosition( - ui::AXBoundaryBehavior::StopAtAnchorBoundary); + ui::AXBoundaryBehavior::kStopAtAnchorBoundary); BrowserAccessibility::AXPosition startWordPosition = startPosition->CreateNextWordStartPosition( - ui::AXBoundaryBehavior::StopAtAnchorBoundary); + ui::AXBoundaryBehavior::kStopAtAnchorBoundary); BrowserAccessibility::AXPosition endPosition = *startWordPosition <= *endWordPosition ? std::move(startWordPosition) : std::move(endWordPosition); @@ -2606,7 +2606,7 @@ if (position->IsNullPosition()) return nil; return CreateTextMarker(position->CreateNextWordEndPosition( - ui::AXBoundaryBehavior::CrossBoundary)); + ui::AXBoundaryBehavior::kCrossBoundary)); } if ([attribute @@ -2617,7 +2617,7 @@ if (position->IsNullPosition()) return nil; return CreateTextMarker(position->CreatePreviousWordStartPosition( - ui::AXBoundaryBehavior::CrossBoundary)); + ui::AXBoundaryBehavior::kCrossBoundary)); } if ([attribute isEqualToString: @@ -2654,10 +2654,10 @@ // Make sure that the line start position is really at the start of the // current line. lineStartPosition = lineStartPosition->CreatePreviousLineStartPosition( - ui::AXBoundaryBehavior::StopIfAlreadyAtBoundary); + ui::AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); BrowserAccessibility::AXPosition lineEndPosition = lineStartPosition->CreateNextLineEndPosition( - ui::AXBoundaryBehavior::StopAtAnchorBoundary); + ui::AXBoundaryBehavior::kStopAtAnchorBoundary); BrowserAccessibility::AXRange range(std::move(lineStartPosition), std::move(lineEndPosition)); return CreateTextMarkerRange(std::move(range)); @@ -2673,10 +2673,10 @@ BrowserAccessibility::AXPosition startLinePosition = endPosition->CreatePreviousLineStartPosition( - ui::AXBoundaryBehavior::StopAtLastAnchorBoundary); + ui::AXBoundaryBehavior::kStopAtLastAnchorBoundary); BrowserAccessibility::AXPosition endLinePosition = endPosition->CreatePreviousLineEndPosition( - ui::AXBoundaryBehavior::StopAtLastAnchorBoundary); + ui::AXBoundaryBehavior::kStopAtLastAnchorBoundary); BrowserAccessibility::AXPosition startPosition = *startLinePosition <= *endLinePosition ? std::move(endLinePosition) : std::move(startLinePosition); @@ -2695,10 +2695,10 @@ BrowserAccessibility::AXPosition startLinePosition = startPosition->CreateNextLineStartPosition( - ui::AXBoundaryBehavior::StopAtLastAnchorBoundary); + ui::AXBoundaryBehavior::kStopAtLastAnchorBoundary); BrowserAccessibility::AXPosition endLinePosition = startPosition->CreateNextLineEndPosition( - ui::AXBoundaryBehavior::StopAtLastAnchorBoundary); + ui::AXBoundaryBehavior::kStopAtLastAnchorBoundary); BrowserAccessibility::AXPosition endPosition = *startLinePosition <= *endLinePosition ? std::move(startLinePosition) : std::move(endLinePosition); @@ -2715,7 +2715,7 @@ if (position->IsNullPosition()) return nil; return CreateTextMarker(position->CreateNextLineEndPosition( - ui::AXBoundaryBehavior::CrossBoundary)); + ui::AXBoundaryBehavior::kCrossBoundary)); } if ([attribute @@ -2726,7 +2726,22 @@ if (position->IsNullPosition()) return nil; return CreateTextMarker(position->CreatePreviousLineStartPosition( - ui::AXBoundaryBehavior::CrossBoundary)); + ui::AXBoundaryBehavior::kCrossBoundary)); + } + + if ([attribute + isEqualToString: + NSAccessibilitySentenceTextMarkerRangeForTextMarkerParameterizedAttribute]) { + BrowserAccessibility::AXPosition position = + CreatePositionFromTextMarker(parameter); + if (position->IsNullPosition()) + return nil; + + BrowserAccessibility::AXRange range = + position->ExpandToEnclosingTextBoundary( + ax::mojom::TextBoundary::kSentenceStartOrEnd, + ui::AXRangeExpandBehavior::kLeftFirst); + return CreateTextMarkerRange(std::move(range)); } if ([attribute @@ -2737,14 +2752,10 @@ if (position->IsNullPosition()) return nil; - BrowserAccessibility::AXPosition startPosition = - position->CreatePreviousParagraphStartPosition( - ui::AXBoundaryBehavior::StopIfAlreadyAtBoundary); - BrowserAccessibility::AXPosition endPosition = - position->CreateNextParagraphEndPosition( - ui::AXBoundaryBehavior::StopIfAlreadyAtBoundary); - BrowserAccessibility::AXRange range(std::move(startPosition), - std::move(endPosition)); + BrowserAccessibility::AXRange range = + position->ExpandToEnclosingTextBoundary( + ax::mojom::TextBoundary::kParagraphStartOrEnd, + ui::AXRangeExpandBehavior::kLeftFirst); return CreateTextMarkerRange(std::move(range)); } @@ -2756,7 +2767,7 @@ if (position->IsNullPosition()) return nil; return CreateTextMarker(position->CreateNextParagraphEndPosition( - ui::AXBoundaryBehavior::CrossBoundary)); + ui::AXBoundaryBehavior::kCrossBoundary)); } if ([attribute @@ -2767,7 +2778,7 @@ if (position->IsNullPosition()) return nil; return CreateTextMarker(position->CreatePreviousParagraphStartPosition( - ui::AXBoundaryBehavior::CrossBoundary)); + ui::AXBoundaryBehavior::kCrossBoundary)); } if ([attribute @@ -2780,10 +2791,10 @@ BrowserAccessibility::AXPosition startPosition = position->CreatePreviousFormatStartPosition( - ui::AXBoundaryBehavior::StopIfAlreadyAtBoundary); + ui::AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); BrowserAccessibility::AXPosition endPosition = position->CreateNextFormatEndPosition( - ui::AXBoundaryBehavior::StopIfAlreadyAtBoundary); + ui::AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); BrowserAccessibility::AXRange range(std::move(startPosition), std::move(endPosition)); return CreateTextMarkerRange(std::move(range)); @@ -2867,10 +2878,10 @@ // Note that hard line breaks are on a line of their own. BrowserAccessibility::AXPosition startPosition = position->CreatePreviousLineStartPosition( - ui::AXBoundaryBehavior::StopIfAlreadyAtBoundary); + ui::AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); BrowserAccessibility::AXPosition endPosition = startPosition->CreateNextLineStartPosition( - ui::AXBoundaryBehavior::StopAtLastAnchorBoundary); + ui::AXBoundaryBehavior::kStopAtLastAnchorBoundary); BrowserAccessibility::AXRange range(std::move(startPosition), std::move(endPosition)); return CreateTextMarkerRange(std::move(range));
diff --git a/content/browser/accessibility/browser_accessibility_unittest.cc b/content/browser/accessibility/browser_accessibility_unittest.cc index 71832263..4c22f29 100644 --- a/content/browser/accessibility/browser_accessibility_unittest.cc +++ b/content/browser/accessibility/browser_accessibility_unittest.cc
@@ -829,7 +829,7 @@ BrowserAccessibility::AXPosition next_word_start = position->CreateNextWordStartPosition( - ui::AXBoundaryBehavior::CrossBoundary); + ui::AXBoundaryBehavior::kCrossBoundary); if (position->MaxTextOffset() == 0) { EXPECT_TRUE(next_word_start->IsNullPosition()); } else { @@ -841,7 +841,7 @@ BrowserAccessibility::AXPosition next_word_end = position->CreateNextWordEndPosition( - ui::AXBoundaryBehavior::CrossBoundary); + ui::AXBoundaryBehavior::kCrossBoundary); if (position->MaxTextOffset() == 0) { EXPECT_TRUE(next_word_end->IsNullPosition()); } else {
diff --git a/content/browser/accessibility/dump_accessibility_events_browsertest.cc b/content/browser/accessibility/dump_accessibility_events_browsertest.cc index e244cab..26974556 100644 --- a/content/browser/accessibility/dump_accessibility_events_browsertest.cc +++ b/content/browser/accessibility/dump_accessibility_events_browsertest.cc
@@ -493,8 +493,8 @@ RunEventTest(FILE_PATH_LITERAL("caret-move.html")); } -// Flaky on Windows: https://crbug.com/1186887 -#if defined(OS_WIN) +// Flaky on Windows, disabled on Linux: https://crbug.com/1186887 +#if defined(OS_LINUX) || defined(OS_WIN) #define MAYBE_AccessibilityEventsCaretMoveHiddenInput \ DISABLED_AccessibilityEventsCaretMoveHiddenInput #else
diff --git a/content/browser/devtools/BUILD.gn b/content/browser/devtools/BUILD.gn index 365a4dd..0661912 100644 --- a/content/browser/devtools/BUILD.gn +++ b/content/browser/devtools/BUILD.gn
@@ -11,6 +11,15 @@ # Android does not support DevTools front-end. if (!is_android) { + source_set("devtools_resources_extern") { + sources = [ "devtools_resources.cc" ] + + # This is to remove unnecessary dependency from compiles of targets depends + # on this target to grit action. + public = [] + + deps = [ ":devtools_resources" ] + } grit("devtools_resources") { source = "$root_gen_dir/$devtools_grd_location"
diff --git a/content/browser/devtools/devtools_frontend_host_impl.cc b/content/browser/devtools/devtools_frontend_host_impl.cc index 0b46252..1d38a1c 100644 --- a/content/browser/devtools/devtools_frontend_host_impl.cc +++ b/content/browser/devtools/devtools_frontend_host_impl.cc
@@ -11,11 +11,14 @@ #include "base/memory/ref_counted_memory.h" #include "build/build_config.h" #include "content/browser/bad_message.h" -#include "content/browser/devtools/grit/devtools_resources_map.h" #include "content/public/browser/render_frame_host.h" #include "content/public/browser/web_contents.h" #include "content/public/common/content_client.h" #include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h" +#include "ui/base/webui/resource_path.h" + +extern const webui::ResourcePath kDevtoolsResources[]; +extern const size_t kDevtoolsResourcesSize; namespace content {
diff --git a/content/browser/devtools/devtools_http_handler.cc b/content/browser/devtools/devtools_http_handler.cc index a19470a..3c2d16c 100644 --- a/content/browser/devtools/devtools_http_handler.cc +++ b/content/browser/devtools/devtools_http_handler.cc
@@ -55,7 +55,7 @@ #endif #if !defined(OS_ANDROID) && !defined(OS_FUCHSIA) -#include "content/browser/devtools/grit/devtools_resources.h" // nogncheck +extern const int kCcompressedProtocolJSON; #endif namespace content { @@ -661,7 +661,7 @@ NOTREACHED(); #else scoped_refptr<base::RefCountedMemory> bytes = - GetContentClient()->GetDataResourceBytes(COMPRESSED_PROTOCOL_JSON); + GetContentClient()->GetDataResourceBytes(kCcompressedProtocolJSON); std::string json_protocol(reinterpret_cast<const char*>(bytes->front()), bytes->size());
diff --git a/content/browser/devtools/devtools_resources.cc b/content/browser/devtools/devtools_resources.cc new file mode 100644 index 0000000..422a88f --- /dev/null +++ b/content/browser/devtools/devtools_resources.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 "content/browser/devtools/grit/devtools_resources.h" + +extern const int kCcompressedProtocolJSON = COMPRESSED_PROTOCOL_JSON;
diff --git a/content/browser/devtools/worker_devtools_agent_host.cc b/content/browser/devtools/worker_devtools_agent_host.cc index 9768c12..5daf2de 100644 --- a/content/browser/devtools/worker_devtools_agent_host.cc +++ b/content/browser/devtools/worker_devtools_agent_host.cc
@@ -100,9 +100,7 @@ } RenderProcessHost* WorkerDevToolsAgentHost::GetProcessHost() { - DedicatedWorkerHost* host = GetDedicatedWorkerHost(); - DCHECK(host); - return host->GetProcessHost(); + return RenderProcessHost::FromID(process_id_); } std::string WorkerDevToolsAgentHost::GetType() {
diff --git a/content/child/runtime_features.cc b/content/child/runtime_features.cc index 41bd824..5fc022b 100644 --- a/content/child/runtime_features.cc +++ b/content/child/runtime_features.cc
@@ -303,6 +303,10 @@ {wf::EnableVideoPlaybackQuality, features::kVideoPlaybackQuality}, {wf::EnableVideoWakeLockOptimisationHiddenMuted, media::kWakeLockOptimisationHiddenMuted}, + {wf::EnableWebBluetoothGetDevices, + features::kWebBluetoothNewPermissionsBackend, kSetOnlyIfOverridden}, + {wf::EnableWebBluetoothWatchAdvertisements, + features::kWebBluetoothNewPermissionsBackend, kSetOnlyIfOverridden}, {wf::EnableWebID, features::kFedCm}, #if defined(OS_ANDROID) {wf::EnableWebNfc, features::kWebNfc, kSetOnlyIfOverridden},
diff --git a/content/public/common/content_features.cc b/content/public/common/content_features.cc index a1738ab..a777f7f 100644 --- a/content/public/common/content_features.cc +++ b/content/public/common/content_features.cc
@@ -1004,7 +1004,8 @@ // Controls whether Web Bluetooth should use the new permissions backend. The // new permissions backend uses ChooserContextBase, which is used by other -// device APIs, such as WebUSB. +// device APIs, such as WebUSB. When enabled, WebBluetoothWatchAdvertisements +// and WebBluetoothGetDevices blink features are also enabled. const base::Feature kWebBluetoothNewPermissionsBackend{ "WebBluetoothNewPermissionsBackend", base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/content/shell/BUILD.gn b/content/shell/BUILD.gn index 36d36ad3..7183e197 100644 --- a/content/shell/BUILD.gn +++ b/content/shell/BUILD.gn
@@ -494,6 +494,10 @@ deps = [ ":content_shell_framework_bundle_data", ":content_shell_resources_bundle_data", + + # Despite its path, this does not add a //base dependency, see comments in + # the .cc file. + "//base/allocator:early_zone_registration_mac", "//sandbox/mac:seatbelt", ] info_plist_target = ":content_shell_plist" @@ -732,7 +736,10 @@ "CONTENT_SHELL_HELPER_SUFFIX=${invoker.helper_name_suffix}", "CONTENT_SHELL_HELPER_BUNDLE_ID_SUFFIX=${invoker.helper_bundle_id_suffix}", ] - deps = [ "//sandbox/mac:seatbelt" ] + deps = [ + "//base/allocator:early_zone_registration_mac", + "//sandbox/mac:seatbelt", + ] info_plist_target = ":content_shell_helper_plist"
diff --git a/content/shell/app/shell_main_mac.cc b/content/shell/app/shell_main_mac.cc index d72f77c..0097ce68 100644 --- a/content/shell/app/shell_main_mac.cc +++ b/content/shell/app/shell_main_mac.cc
@@ -12,6 +12,8 @@ #include <memory> +#include "base/allocator/early_zone_registration_mac.h" + #if defined(HELPER_EXECUTABLE) #include "sandbox/mac/seatbelt_exec.h" // nogncheck #endif // defined(HELPER_EXECUTABLE) @@ -23,6 +25,8 @@ } // namespace int main(int argc, char* argv[]) { + partition_alloc::EarlyMallocZoneRegistration(); + uint32_t exec_path_size = 0; int rv = _NSGetExecutablePath(NULL, &exec_path_size); if (rv != -1) {
diff --git a/content/test/data/gpu/webcodecs/encode-decode.html b/content/test/data/gpu/webcodecs/encode-decode.html index 531f1862..bb3f1861 100644 --- a/content/test/data/gpu/webcodecs/encode-decode.html +++ b/content/test/data/gpu/webcodecs/encode-decode.html
@@ -48,6 +48,8 @@ } }); + let timestamps = []; + const encoder_init = { output(chunk, metadata) { let config = metadata.decoderConfig; @@ -59,6 +61,12 @@ } decoder.decode(chunk); frames_encoded++; + + let expected_timestamp = timestamps.shift(); + TEST.assert( + chunk.timestamp == expected_timestamp, + `EncodedVideoChunk timestamp mismatch. Expected: ${ + expected_timestamp} got ${chunk.timestamp}`); }, error(e) { errors++; @@ -73,6 +81,7 @@ for (let i = 0; i < frames_to_encode; i++) { let frame = await source.getNextFrame(); let keyframe = (i % 10 == 0); + timestamps.push(frame.timestamp); encoder.encode(frame, { keyFrame: keyframe }); frame.close();
diff --git a/extensions/renderer/api/automation/automation_internal_custom_bindings.cc b/extensions/renderer/api/automation/automation_internal_custom_bindings.cc index 2017213..be326fad 100644 --- a/extensions/renderer/api/automation/automation_internal_custom_bindings.cc +++ b/extensions/renderer/api/automation/automation_internal_custom_bindings.cc
@@ -2853,7 +2853,7 @@ if (!head_pos->AtStartOfParagraph()) { ui::AXNodePosition::AXPositionInstance start_para_pos = head_pos->CreatePreviousParagraphStartPosition( - ui::AXBoundaryBehavior::StopAtLastAnchorBoundary); + ui::AXBoundaryBehavior::kStopAtLastAnchorBoundary); ui::AXRange<ui::AXPosition<ui::AXNodePosition, ui::AXNode>> pre_range( start_para_pos->Clone(), head_pos->Clone()); pre_str = pre_range.GetText(); @@ -2863,7 +2863,7 @@ // node to the end of the paragraph. ui::AXNodePosition::AXPositionInstance end_para_pos = head_pos->CreateNextParagraphEndPosition( - ui::AXBoundaryBehavior::StopAtLastAnchorBoundary); + ui::AXBoundaryBehavior::kStopAtLastAnchorBoundary); ui::AXRange<ui::AXPosition<ui::AXNodePosition, ui::AXNode>> post_range( head_pos->Clone(), end_para_pos->Clone()); post_str = post_range.GetText();
diff --git a/extensions/renderer/api/automation/automation_position.cc b/extensions/renderer/api/automation/automation_position.cc index a704805b..445862f 100644 --- a/extensions/renderer/api/automation/automation_position.cc +++ b/extensions/renderer/api/automation/automation_position.cc
@@ -273,118 +273,118 @@ void AutomationPosition::MoveToNextCharacterPosition( gin::Arguments* arguments) { position_ = position_->CreateNextCharacterPosition( - ui::AXBoundaryBehavior::CrossBoundary); + ui::AXBoundaryBehavior::kCrossBoundary); } void AutomationPosition::MoveToPreviousCharacterPosition( gin::Arguments* arguments) { position_ = position_->CreatePreviousCharacterPosition( - ui::AXBoundaryBehavior::CrossBoundary); + ui::AXBoundaryBehavior::kCrossBoundary); } void AutomationPosition::MoveToNextWordStartPosition( gin::Arguments* arguments) { position_ = position_->CreateNextWordStartPosition( - ui::AXBoundaryBehavior::CrossBoundary); + ui::AXBoundaryBehavior::kCrossBoundary); } void AutomationPosition::MoveToPreviousWordStartPosition( gin::Arguments* arguments) { position_ = position_->CreatePreviousWordStartPosition( - ui::AXBoundaryBehavior::CrossBoundary); + ui::AXBoundaryBehavior::kCrossBoundary); } void AutomationPosition::MoveToNextWordEndPosition(gin::Arguments* arguments) { position_ = position_->CreateNextWordEndPosition( - ui::AXBoundaryBehavior::CrossBoundary); + ui::AXBoundaryBehavior::kCrossBoundary); } void AutomationPosition::MoveToPreviousWordEndPosition( gin::Arguments* arguments) { position_ = position_->CreatePreviousWordEndPosition( - ui::AXBoundaryBehavior::CrossBoundary); + ui::AXBoundaryBehavior::kCrossBoundary); } void AutomationPosition::MoveToNextLineStartPosition( gin::Arguments* arguments) { position_ = position_->CreateNextLineStartPosition( - ui::AXBoundaryBehavior::CrossBoundary); + ui::AXBoundaryBehavior::kCrossBoundary); } void AutomationPosition::MoveToPreviousLineStartPosition( gin::Arguments* arguments) { position_ = position_->CreatePreviousLineStartPosition( - ui::AXBoundaryBehavior::CrossBoundary); + ui::AXBoundaryBehavior::kCrossBoundary); } void AutomationPosition::MoveToNextLineEndPosition(gin::Arguments* arguments) { position_ = position_->CreateNextLineEndPosition( - ui::AXBoundaryBehavior::CrossBoundary); + ui::AXBoundaryBehavior::kCrossBoundary); } void AutomationPosition::MoveToPreviousLineEndPosition( gin::Arguments* arguments) { position_ = position_->CreatePreviousLineEndPosition( - ui::AXBoundaryBehavior::CrossBoundary); + ui::AXBoundaryBehavior::kCrossBoundary); } void AutomationPosition::MoveToPreviousFormatStartPosition( gin::Arguments* arguments) { position_ = position_->CreatePreviousFormatStartPosition( - ui::AXBoundaryBehavior::CrossBoundary); + ui::AXBoundaryBehavior::kCrossBoundary); } void AutomationPosition::MoveToNextFormatEndPosition( gin::Arguments* arguments) { position_ = position_->CreateNextFormatEndPosition( - ui::AXBoundaryBehavior::CrossBoundary); + ui::AXBoundaryBehavior::kCrossBoundary); } void AutomationPosition::MoveToNextParagraphStartPosition( gin::Arguments* arguments) { position_ = position_->CreateNextParagraphStartPosition( - ui::AXBoundaryBehavior::CrossBoundary); + ui::AXBoundaryBehavior::kCrossBoundary); } void AutomationPosition::MoveToPreviousParagraphStartPosition( gin::Arguments* arguments) { position_ = position_->CreatePreviousParagraphStartPosition( - ui::AXBoundaryBehavior::CrossBoundary); + ui::AXBoundaryBehavior::kCrossBoundary); } void AutomationPosition::MoveToNextParagraphEndPosition( gin::Arguments* arguments) { position_ = position_->CreateNextParagraphEndPosition( - ui::AXBoundaryBehavior::CrossBoundary); + ui::AXBoundaryBehavior::kCrossBoundary); } void AutomationPosition::MoveToPreviousParagraphEndPosition( gin::Arguments* arguments) { position_ = position_->CreatePreviousParagraphEndPosition( - ui::AXBoundaryBehavior::CrossBoundary); + ui::AXBoundaryBehavior::kCrossBoundary); } void AutomationPosition::MoveToNextPageStartPosition( gin::Arguments* arguments) { position_ = position_->CreateNextPageStartPosition( - ui::AXBoundaryBehavior::CrossBoundary); + ui::AXBoundaryBehavior::kCrossBoundary); } void AutomationPosition::MoveToPreviousPageStartPosition( gin::Arguments* arguments) { position_ = position_->CreatePreviousPageStartPosition( - ui::AXBoundaryBehavior::CrossBoundary); + ui::AXBoundaryBehavior::kCrossBoundary); } void AutomationPosition::MoveToNextPageEndPosition(gin::Arguments* arguments) { position_ = position_->CreateNextPageEndPosition( - ui::AXBoundaryBehavior::CrossBoundary); + ui::AXBoundaryBehavior::kCrossBoundary); } void AutomationPosition::MoveToPreviousPageEndPosition( gin::Arguments* arguments) { position_ = position_->CreatePreviousPageEndPosition( - ui::AXBoundaryBehavior::CrossBoundary); + ui::AXBoundaryBehavior::kCrossBoundary); } void AutomationPosition::MoveToNextAnchorPosition(gin::Arguments* arguments) {
diff --git a/ios/chrome/app/strings/resources/ios_chromium_strings_en-GB.xtb b/ios/chrome/app/strings/resources/ios_chromium_strings_en-GB.xtb index 5d02894..b03d82e 100644 --- a/ios/chrome/app/strings/resources/ios_chromium_strings_en-GB.xtb +++ b/ios/chrome/app/strings/resources/ios_chromium_strings_en-GB.xtb
@@ -31,6 +31,7 @@ <translation id="2478931088402984578">Select <ph name="BEGIN_BOLD" />Chromium<ph name="END_BOLD" /></translation> <translation id="2567507405773541360">Get more done with a simple, secure and faster-than-ever Chromium</translation> <translation id="2590893390871230428">Sync your Chromium data</translation> +<translation id="259094968798709429">Passwords are saved to <ph name="BEGIN_LINK" />Password Manager<ph name="END_LINK" /> so that you can use them on any device.</translation> <translation id="2650312721222849884">To see your tabs from wherever you use Chromium, turn on sync</translation> <translation id="2684230048001240293">Set Chromium as default to sync your tabs, passwords and payment info on all your devices</translation> <translation id="2730884209570016437">Chromium can't use your camera because it's in use by another application</translation> @@ -44,6 +45,7 @@ <translation id="3639997914391704523">Chromium can check your passwords when you sign in with your Google Account.</translation> <translation id="3805899903892079518">Chromium does not have access to your photos or videos. Enable access in iOS Settings > Privacy > Photos.</translation> <translation id="3946918322491238254">You can still see all your bookmarks, history, passwords and other settings on this device. If you make changes, they won't sync to your account.</translation> +<translation id="4043291146360695975">Passwords are saved to Password Manager on this device only.</translation> <translation id="4099085513035183040">Not supported on Chromium beta</translation> <translation id="4555020257205549924">When this feature is turned on, Chromium will offer to translate pages written in other languages using Google Translate. <ph name="BEGIN_LINK" />Find out more<ph name="END_LINK" /></translation> <translation id="4585809515399340748">You can now use Chromium whenever you tap on links in messages, documents and other apps.</translation>
diff --git a/ios/chrome/app/strings/resources/ios_chromium_strings_km.xtb b/ios/chrome/app/strings/resources/ios_chromium_strings_km.xtb index c123bc2..a259f9b6 100644 --- a/ios/chrome/app/strings/resources/ios_chromium_strings_km.xtb +++ b/ios/chrome/app/strings/resources/ios_chromium_strings_km.xtb
@@ -81,6 +81,7 @@ <translation id="7099326575020694068">Chromium មិនអាចប្រើកាមេរ៉ារបស់អ្នកនៅក្នុងមុខងារបំបែកការមើលបានទេ</translation> <translation id="7108914401277488191">បំពេញកិច្ចការបានកាន់តែច្រើនតាមរយៈ Chromium</translation> <translation id="7118091470949186573">ឥឡូវនេះ អ្នកអាចប្រើ Chromium រាល់ពេលដែលអ្នកចុចលើតំណនៅក្នុងសារ ឯកសារ និងកម្មវិធីផ្សេងទៀត។</translation> +<translation id="7175400662502680481">ពាក្យសម្ងាត់របស់អ្នកត្រូវបានបញ្ចេញនៅក្នុងការបែកធ្លាយទិន្នន័យ។ កម្មវិធីគ្រប់គ្រងពាក្យសម្ងាត់ណែនាំឱ្យផ្លាស់ប្ដូរពាក្យសម្ងាត់នេះឥឡូវនេះ។</translation> <translation id="7208566199746267865">Chromium សន្សំពេលវេលាដោយប្រើគណនីរបស់អ្នកនៅលើបណ្តាញអ៊ីនធឺណិត។ អ្នកអាចបញ្ចូល ឬលុបគណនីនៅក្នុងការកំណត់។</translation> <translation id="725427773388857052">Chromium អាចជួយរក្សាសុវត្ថិភាពរបស់អ្នកពីការបែកធ្លាយទិន្នន័យ គេហទំព័រដែលគ្មានសុវត្ថិភាព និងអ្វីៗជាច្រើនទៀត។</translation> <translation id="7269362888766543920">កម្មវិធីបន្ថែមមួយចំនួនអាចធ្វើឲ្យ Chromium គាំង។ សូមសាកល្បងលុបការដំឡើងពួកវា។</translation>
diff --git a/ios/chrome/app/strings/resources/ios_chromium_strings_tr.xtb b/ios/chrome/app/strings/resources/ios_chromium_strings_tr.xtb index 50c94727..e06b22128 100644 --- a/ios/chrome/app/strings/resources/ios_chromium_strings_tr.xtb +++ b/ios/chrome/app/strings/resources/ios_chromium_strings_tr.xtb
@@ -81,6 +81,7 @@ <translation id="7099326575020694068">Chromium, kameranızı Bölünmüş Görünüm modunda kullanamıyor</translation> <translation id="7108914401277488191">Chromium İle Daha Fazlasını Yapın</translation> <translation id="7118091470949186573">Artık mesajlardaki, dokümanlardaki ve diğer uygulamalardaki bağlantıları tıkladığınızda Chromium'u kullanabilirsiniz.</translation> +<translation id="7175400662502680481">Şifreniz bir veri ihlali nedeniyle açığa çıktı. Şifre Yöneticisi bu şifreyi hemen değiştirmenizi öneriyor.</translation> <translation id="7208566199746267865">Chromium, hesaplarınızı web'e getirerek size zaman kazandırır. Hesapları Ayarlar'dan ekleyebilir veya kaldırabilirsiniz.</translation> <translation id="725427773388857052">Chromium; veri ihlallerine, güvenli olmayan web sitelerine ve diğer tehditlere karşı güvende kalmanıza yardımcı olabilir.</translation> <translation id="7269362888766543920">Bazı eklentiler Chromium'un kilitlenmesine neden oluyor. Lütfen bunların yüklemesini kaldırmayı deneyin.</translation>
diff --git a/ios/chrome/app/strings/resources/ios_chromium_strings_uz.xtb b/ios/chrome/app/strings/resources/ios_chromium_strings_uz.xtb index 9cdc5bfc..fe71290 100644 --- a/ios/chrome/app/strings/resources/ios_chromium_strings_uz.xtb +++ b/ios/chrome/app/strings/resources/ios_chromium_strings_uz.xtb
@@ -30,6 +30,7 @@ <translation id="2478931088402984578"><ph name="BEGIN_BOLD" />Chromium<ph name="END_BOLD" /> brauzerini tanlang.</translation> <translation id="2567507405773541360">Oddiy, xavfsiz va har qachongidan tezkor Chromium brauzerida koʻplab imkoniyatlar</translation> <translation id="2590893390871230428">Chromium axborotini sinxronlash</translation> +<translation id="259094968798709429">Parollar <ph name="BEGIN_LINK" />Parollar menejeriga<ph name="END_LINK" /> saqlanadi va ulardan istalgan qurilmada foydalanish mumkin.</translation> <translation id="2650312721222849884">Boshqa qurilmalardagi Chromium varaqlarini koʻrish uchun sinxronizatsiyani yoqing</translation> <translation id="2684230048001240293">Chromium brauzerini varaqlar, parollar va toʻlov axborotlarini barcha qurilmalarga sinxronlovchi asosiy ilova sifatida belgilash</translation> <translation id="2730884209570016437">Chromium kamerangizdan foydalana olmaydi, chunki boshqa ilova allaqachon kamerani ishlatmoqda.</translation> @@ -43,6 +44,7 @@ <translation id="3639997914391704523">Google hisobingizga kirsangiz, Chromium parollaringizni tekshira oladi.</translation> <translation id="3805899903892079518">Chromium rasm va videolaringizni ko‘ra olmayapti. “Sozlamalar > Maxfiylik > Rasmlar” oynasi orqali ruxsat bering.</translation> <translation id="3946918322491238254">Bu qurilmadagi bukmarklar, tarix, parollar va boshqa sozlamalarni koʻrishingiz mumkin. Oʻzgartirish kiritilsa, ular hisobingizga sinxronlanmaydi.</translation> +<translation id="4043291146360695975">Parollar faqat shu qurilmadagi Parollar menejeriga saqlanadi.</translation> <translation id="4099085513035183040">Chromium Beta nashrida ishlamaydi</translation> <translation id="4555020257205549924">Agar bu funksiya yoniq bo‘lsa, Chromium sahifalarni Google Tarjimon xizmati yordamida tarjima qilishni taklif qiladi. <ph name="BEGIN_LINK" />Batafsil...<ph name="END_LINK" /></translation> <translation id="4585809515399340748">Siz endi Chrome brauzerini har doim xabarlar, hujjatlar va boshqa ilovalardagi havolalar ustiga bosib ishga tushirishingiz mumkin.</translation>
diff --git a/ios/chrome/app/strings/resources/ios_google_chrome_strings_en-GB.xtb b/ios/chrome/app/strings/resources/ios_google_chrome_strings_en-GB.xtb index 3e7b33b..61a2d73 100644 --- a/ios/chrome/app/strings/resources/ios_google_chrome_strings_en-GB.xtb +++ b/ios/chrome/app/strings/resources/ios_google_chrome_strings_en-GB.xtb
@@ -50,6 +50,7 @@ <translation id="4214277427269650960">Sign in to this site and Chrome. You can turn on sync later.</translation> <translation id="424864128008805179">Sign out of Chrome?</translation> <translation id="4249068189593983585">Chrome tip. For more tab options, touch and hold the Show Tabs button in the toolbar, which is at the bottom or top of your screen.</translation> +<translation id="4267862249323750454">Passwords are saved to <ph name="BEGIN_LINK" />Google Password Manager<ph name="END_LINK" /> so that you can use them on any device.</translation> <translation id="4523886039239821078">Some add-ons cause Chrome to crash. Please uninstall:</translation> <translation id="4633328489441962921">Chrome can't check for updates</translation> <translation id="4698415050768537821">Chrome couldn't check all passwords. Try again tomorrow or <ph name="BEGIN_LINK" />check passwords in your Google Account.<ph name="END_LINK" /></translation> @@ -70,6 +71,7 @@ <translation id="6063091872902370735">Allow Chrome Sign-in</translation> <translation id="6181930887571472871">Switch to Chrome</translation> <translation id="6238746320622508509">Let Chrome lock your incognito tabs.</translation> +<translation id="6387994324662817823">Passwords are saved to Google Password Manager on this device only.</translation> <translation id="6427126399757991875">Your organisation is setting up Chrome…</translation> <translation id="6600954340915313787">Copied to Chrome</translation> <translation id="6634107063912726160">When you sign out, Chrome won't sync any new data to your Google Account. Data previously synced stays in the account.</translation>
diff --git a/ios/chrome/app/strings/resources/ios_google_chrome_strings_km.xtb b/ios/chrome/app/strings/resources/ios_google_chrome_strings_km.xtb index 44e9095..a6791dbd 100644 --- a/ios/chrome/app/strings/resources/ios_google_chrome_strings_km.xtb +++ b/ios/chrome/app/strings/resources/ios_google_chrome_strings_km.xtb
@@ -75,6 +75,7 @@ <translation id="6634107063912726160">នៅពេលអ្នកចេញពីគណនី Chrome នឹងមិនធ្វើសមកាលកម្មទិន្នន័យថ្មីទៅគណនី Google របស់អ្នកទេ។ ទិន្នន័យដែលបានធ្វើសមកាលកម្មពីមុនបន្តស្ថិតនៅក្នុងគណនី។</translation> <translation id="6648150602980899529">អ្នកកំពុងចូលដោយប្រើគណនីដែលគ្រប់គ្រងដោយ <ph name="DOMAIN" /> និងកំពុងផ្តល់ឲ្យអ្នកគ្រប់គ្រងរបស់វានូវលទ្ធភាពគ្រប់គ្រងទិន្នន័យ Chrome របស់អ្នក។ ទិន្នន័យរបស់អ្នកនឹងភ្ជាប់ជាមួយគណនីនេះជាអចិន្ត្រៃយ៍។ ការចេញពី Chrome នឹងលុបទិន្នន័យរបស់អ្នកចេញពីឧបករណ៍នេះ ប៉ុន្តែវានឹងនៅតែត្រូវបានរក្សាទុកក្នុងគណនី Google របស់អ្នកដដែល។</translation> <translation id="6676840375528380067">សម្អាតទិន្នន័យ Chrome របស់អ្នកពីឧបករណ៍នេះមែនទេ?</translation> +<translation id="6709398533399187136">ពាក្យសម្ងាត់របស់អ្នកត្រូវបានបញ្ចេញនៅក្នុងការបែកធ្លាយទិន្នន័យ។ កម្មវិធីគ្រប់គ្រងពាក្យសម្ងាត់ Google ណែនាំឱ្យផ្លាស់ប្ដូរពាក្យសម្ងាត់នេះឥឡូវនេះ។</translation> <translation id="6822673484890854830">Chrome មិនអាចពិនិត្យពាក្យសម្ងាត់ទាំងអស់បានទេ។ សូមព្យាយាមម្តងទៀតពេលក្រោយ។</translation> <translation id="6964931465519938134">ឥឡូវនេះ អ្នកអាចប្រើ Chrome រាល់ពេលដែលអ្នកចុចលើតំណនៅក្នុងសារ ឯកសារ និងកម្មវិធីផ្សេងទៀត។</translation> <translation id="7059914902409643750">កំណត់ឱ្យ Chrome ក្លាយជារបស់អ្នក</translation>
diff --git a/ios/chrome/app/strings/resources/ios_google_chrome_strings_tr.xtb b/ios/chrome/app/strings/resources/ios_google_chrome_strings_tr.xtb index 7dd480d..49a71b46 100644 --- a/ios/chrome/app/strings/resources/ios_google_chrome_strings_tr.xtb +++ b/ios/chrome/app/strings/resources/ios_google_chrome_strings_tr.xtb
@@ -75,6 +75,7 @@ <translation id="6634107063912726160">Oturumu kapattığınızda Chrome, hiçbir yeni veriyi Google Hesabınızla senkronize etmez. Önceden senkronize edilmiş veriler hesapta kalır.</translation> <translation id="6648150602980899529"><ph name="DOMAIN" /> tarafından yönetilen bir hesapla oturum açıyorsunuz ve yöneticiye tüm Chrome verileriniz üzerinde denetim olanağı veriyorsunuz. Verileriniz kalıcı olarak bu hesaba bağlanacaktır. Chrome'da oturumu kapattığınızda verileriniz bu cihazdan silinir ancak Google Hesabınızda kalmaya devam eder.</translation> <translation id="6676840375528380067">Chrome verileriniz bu cihazdan temizlensin mi?</translation> +<translation id="6709398533399187136">Şifreniz bir veri ihlali nedeniyle açığa çıktı. Google Şifre Yöneticisi bu şifreyi hemen değiştirmenizi öneriyor.</translation> <translation id="6822673484890854830">Chrome, şifrelerin tümünü kontrol edemedi. Daha sonra tekrar deneyin.</translation> <translation id="6964931465519938134">Artık mesajlardaki, dokümanlardaki ve diğer uygulamalardaki bağlantıları tıkladığınızda Chrome'u kullanabilirsiniz.</translation> <translation id="7059914902409643750">Chrome'u Kendinize Uyarlayın</translation>
diff --git a/ios/chrome/app/strings/resources/ios_google_chrome_strings_uz.xtb b/ios/chrome/app/strings/resources/ios_google_chrome_strings_uz.xtb index 2a189a3..2c57a497 100644 --- a/ios/chrome/app/strings/resources/ios_google_chrome_strings_uz.xtb +++ b/ios/chrome/app/strings/resources/ios_google_chrome_strings_uz.xtb
@@ -50,6 +50,7 @@ <translation id="4214277427269650960">Bu sayt va Chrome hisobiga kiring. Sinxronizatsiyani keyinroq yoqish mumkin.</translation> <translation id="424864128008805179">Chrome hisobidan chiqmoqchimisiz?</translation> <translation id="4249068189593983585">Chrome maslahati. Ko‘proq varaqlarni tanlash uchun ekranning quyi yoki yuqorisida joylashgan asboblar panelidan Varaqlarni ko‘rsatish tugmasi ustiga bosib turing.</translation> +<translation id="4267862249323750454">Parollar <ph name="BEGIN_LINK" />Google Parollar menejeriga<ph name="END_LINK" /> saqlanadi va ulardan istalgan qurilmada foydalanish mumkin.</translation> <translation id="4523886039239821078">Ba’zi kengaytmalar Chrome brauzerini ishdan chiqaradi. O‘chirilishi kerak bo‘lgan kengaytmalar:</translation> <translation id="4633328489441962921">Chrome yangilanishlar chiqqanini tekshira olmadi</translation> <translation id="4698415050768537821">Chrome barcha parollarni tekshira olmadi. Ertaga qaytadan urining yoki <ph name="BEGIN_LINK" />Google hisobingizdagi parollarni tekshiring.<ph name="END_LINK" /></translation> @@ -70,6 +71,7 @@ <translation id="6063091872902370735">Chrome hisobiga kirishga ruxsat berish</translation> <translation id="6181930887571472871">Chrome brauzeriga almashing</translation> <translation id="6238746320622508509">Chrome bilan Inkognito varaqlarni himoyalang.</translation> +<translation id="6387994324662817823">Parollar faqat shu qurilmadagi Google Parollar menejeriga saqlanadi.</translation> <translation id="6427126399757991875">Chrome tashkilotingiz tomonidan sozlanmoqda...</translation> <translation id="6600954340915313787">Chrome’ga nusxalandi</translation> <translation id="6634107063912726160">Hisobingizdan chiqsangiz, Chrome yangi maʼlumotlarni Google hisobingizga sinxronlamaydi. Oldin sinxronlangan maʼlumotlar hisobingizda qoladi.</translation>
diff --git a/ios/chrome/browser/passwords/well_known_change_password_tab_helper_unittest.mm b/ios/chrome/browser/passwords/well_known_change_password_tab_helper_unittest.mm index 6e50820..75c3ac2 100644 --- a/ios/chrome/browser/passwords/well_known_change_password_tab_helper_unittest.mm +++ b/ios/chrome/browser/passwords/well_known_change_password_tab_helper_unittest.mm
@@ -9,7 +9,7 @@ #include "base/test/bind.h" #import "base/test/ios/wait_util.h" #include "base/test/scoped_feature_list.h" -#include "components/password_manager/core/browser/site_affiliation/affiliation_service_impl.h" +#include "components/password_manager/core/browser/site_affiliation/mock_affiliation_service.h" #include "components/password_manager/core/browser/well_known_change_password_util.h" #include "components/password_manager/core/common/password_manager_features.h" #include "components/ukm/test_ukm_recorder.h" @@ -38,6 +38,7 @@ namespace { +using ::testing::NiceMock; using base::test::ios::WaitUntilConditionOrTimeout; using net::test_server::BasicHttpResponse; using net::test_server::EmbeddedTestServer; @@ -57,47 +58,6 @@ constexpr char kMockChangePasswordPath[] = "/change-password-override"; -class TestAffiliationService : public password_manager::AffiliationService { - public: - void PrefetchChangePasswordURLs( - const std::vector<GURL>& urls, - /*AffiliationService:*/ base::OnceClosure closure) override {} - void Clear() override {} - GURL GetChangePasswordURL(const GURL& url) const override { - if (override_available_) { - GURL::Replacements replacement; - replacement.SetPathStr(path_); - return url.ReplaceComponents(replacement); - } - return GURL(); - } - void GetAffiliationsAndBranding( - const FacetURI& facet_uri, - AffiliationService::StrategyOnCacheMiss cache_miss_strategy, - ResultCallback result_callback) override {} - void Prefetch(const FacetURI& facet_uri, - const base::Time& keep_fresh_until) override {} - void CancelPrefetch(const FacetURI& facet_uri, - const base::Time& keep_fresh_until) override {} - void TrimCacheForFacetURI(const FacetURI& facet_uri) override {} - void TrimUnusedCache(std::vector<FacetURI> facet_uris) override {} - void InjectAffiliationAndBrandingInformation( - std::vector<std::unique_ptr<password_manager::PasswordForm>> forms, - AffiliationService::StrategyOnCacheMiss strategy_on_cache_miss, - PasswordFormsCallback result_callback) override {} - - void SetOverrideAvailable(bool available, std::string path) { - override_available_ = available; - path_ = path; - } - - private: - bool override_available_ = false; - - // Path to change password url. - std::string path_; -}; - // Re-implementation of web::LoadUrl() that allows specifying a custom page // transition. void LoadUrlWithTransition(web::WebState* web_state, @@ -131,14 +91,16 @@ EXPECT_TRUE(test_server_->InitializeAndListen()); test_server_->StartAcceptingConnections(); - affiliation_service_ = static_cast<TestAffiliationService*>( - IOSChromeAffiliationServiceFactory::GetInstance() - ->SetTestingFactoryAndUse( - web_state()->GetBrowserState(), - base::BindRepeating([](web::BrowserState* browser_state) { - return std::unique_ptr<KeyedService>( - std::make_unique<TestAffiliationService>()); - }))); + affiliation_service_ = + static_cast<password_manager::MockAffiliationService*>( + IOSChromeAffiliationServiceFactory::GetInstance() + ->SetTestingFactoryAndUse( + web_state()->GetBrowserState(), + base::BindRepeating([](web::BrowserState* browser_state) { + return std::unique_ptr<KeyedService>( + std::make_unique<NiceMock< + password_manager::MockAffiliationService>>()); + }))); web_state()->SetDelegate(&delegate_); password_manager::WellKnownChangePasswordTabHelper::CreateForWebState( @@ -169,9 +131,10 @@ GURL GetNavigatedUrl() const; // Sets if change passwords URL can be obtained. - void SetOverrideAvailable(bool available, - std::string path = kMockChangePasswordPath) { - affiliation_service_->SetOverrideAvailable(available, path); + void SetChangePasswordURLForAffiliationService( + const GURL& change_password_url) { + EXPECT_CALL(*affiliation_service_, GetChangePasswordURL) + .WillRepeatedly(testing::Return(change_password_url)); } // Maps a path to a ServerResponse config object. @@ -189,7 +152,7 @@ base::test::ScopedFeatureList feature_list_; network::TestURLLoaderFactory test_url_loader_factory_; web::FakeWebStateDelegate delegate_; - TestAffiliationService* affiliation_service_ = nullptr; + password_manager::MockAffiliationService* affiliation_service_ = nullptr; }; GURL WellKnownChangePasswordTabHelperTest::GetNavigatedUrl() const { @@ -292,7 +255,8 @@ TEST_F(WellKnownChangePasswordTabHelperTest, NoSupportForChangePassword_WithOverride) { - SetOverrideAvailable(true); + SetChangePasswordURLForAffiliationService( + test_server_->GetURL(kMockChangePasswordPath)); path_response_map_[kWellKnownChangePasswordPath] = { net::HTTP_PERMANENT_REDIRECT, {std::make_pair("Location", "/not-found")}}; path_response_map_["/not-found"] = {net::HTTP_NOT_FOUND, {}}; @@ -320,7 +284,8 @@ TEST_F(WellKnownChangePasswordTabHelperTest, NoSupportForChangePassword_AffiliationServiceReturnsWellKnownUrl) { - SetOverrideAvailable(true, kWellKnownChangePasswordPath); + SetChangePasswordURLForAffiliationService( + test_server_->GetURL(kWellKnownChangePasswordPath)); path_response_map_[kWellKnownChangePasswordPath] = {net::HTTP_NOT_FOUND, {}}; path_response_map_["/"] = {net::HTTP_OK, {}}; SetUrlLoaderResponse(kWellKnownNotExistingResourcePath, net::HTTP_NOT_FOUND);
diff --git a/ios/chrome/browser/ui/settings/google_services/google_services_settings_egtest.mm b/ios/chrome/browser/ui/settings/google_services/google_services_settings_egtest.mm index 7af3aea..893856e4 100644 --- a/ios/chrome/browser/ui/settings/google_services/google_services_settings_egtest.mm +++ b/ios/chrome/browser/ui/settings/google_services/google_services_settings_egtest.mm
@@ -4,6 +4,7 @@ #import "base/strings/sys_string_conversions.h" #import "base/test/ios/wait_util.h" +#include "components/password_manager/core/common/password_manager_features.h" #include "components/password_manager/core/common/password_manager_pref_names.h" #import "components/policy/core/common/policy_loader_ios_constants.h" #import "components/policy/policy_constants.h" @@ -89,6 +90,13 @@ - (AppLaunchConfiguration)appConfigurationForTestCase { AppLaunchConfiguration config; config.relaunch_policy = ForceRelaunchByCleanShutdown; + + if ([self isRunningTest:@selector + (testTogglePasswordLeakCheckForSignedOutUser)]) { + config.features_enabled.push_back( + password_manager::features::kLeakDetectionUnauthenticated); + } + return config; } @@ -153,8 +161,8 @@ } // Tests that password leak detection can only be toggled if Safe Browsing is -// enabled. -- (void)testTogglePasswordLeakCheck { +// enabled for signed in user. +- (void)testTogglePasswordLeakCheckForSignedInUser { // Ensure that Safe Browsing and password leak detection opt-outs start in // their default (opted-in) state. [ChromeEarlGrey setBoolValue:YES forUserPref:prefs::kSafeBrowsingEnabled]; @@ -220,6 +228,71 @@ @"Failed to toggle-on password leak checks"); } +// Tests that password leak detection can only be toggled if Safe Browsing is +// enabled for signed out user. +- (void)testTogglePasswordLeakCheckForSignedOutUser { + // Ensure that Safe Browsing and password leak detection opt-outs start in + // their default (opted-in) state. + [ChromeEarlGrey setBoolValue:YES forUserPref:prefs::kSafeBrowsingEnabled]; + [ChromeEarlGrey + setBoolValue:YES + forUserPref:password_manager::prefs::kPasswordLeakDetectionEnabled]; + + // Open "Google Services" settings. + [self openGoogleServicesSettings]; + + // Check that Safe Browsing is enabled, and toggle it off. + [[self elementInteractionWithGreyMatcher: + chrome_test_util::SettingsSwitchCell( + kSafeBrowsingItemAccessibilityIdentifier, + /*is_toggled_on=*/YES, + /*enabled=*/YES)] + performAction:chrome_test_util::TurnSettingsSwitchOn(NO)]; + + // Check that the password leak check toggle is both toggled off and disabled. + [[self elementInteractionWithGreyMatcher: + chrome_test_util::SettingsSwitchCell( + kPasswordLeakCheckItemAccessibilityIdentifier, + /*is_toggled_on=*/NO, + /*enabled=*/NO)] assertWithMatcher:grey_notNil()]; + + // Toggle Safe Browsing on. + [[self elementInteractionWithGreyMatcher: + chrome_test_util::SettingsSwitchCell( + kSafeBrowsingItemAccessibilityIdentifier, + /*is_toggled_on=*/NO, + /*enabled=*/YES)] + performAction:chrome_test_util::TurnSettingsSwitchOn(YES)]; + + // Check that the password leak check toggle is enabled, and toggle it off. + [[self elementInteractionWithGreyMatcher: + chrome_test_util::SettingsSwitchCell( + kPasswordLeakCheckItemAccessibilityIdentifier, + /*is_toggled_on=*/YES, + /*enabled=*/YES)] + performAction:chrome_test_util::TurnSettingsSwitchOn(NO)]; + + // Check the underlying pref value. + GREYAssertFalse( + [ChromeEarlGrey userBooleanPref:password_manager::prefs:: + kPasswordLeakDetectionEnabled], + @"Failed to toggle-off password leak checks"); + + // Toggle password leak check detection back on. + [[self elementInteractionWithGreyMatcher: + chrome_test_util::SettingsSwitchCell( + kPasswordLeakCheckItemAccessibilityIdentifier, + /*is_toggled_on=*/NO, + /*enabled=*/YES)] + performAction:chrome_test_util::TurnSettingsSwitchOn(YES)]; + + // Check the underlying pref value. + GREYAssertTrue( + [ChromeEarlGrey userBooleanPref:password_manager::prefs:: + kPasswordLeakDetectionEnabled], + @"Failed to toggle-on password leak checks"); +} + // Tests that disabling the "Allow Chrome sign-in" > "Sign out" option blocks // the user from signing in to Chrome through the promo sign-in until it is // re-enabled.
diff --git a/ios/chrome/browser/ui/settings/google_services/google_services_settings_mediator.mm b/ios/chrome/browser/ui/settings/google_services/google_services_settings_mediator.mm index 64ff5de..fbbd0ba 100644 --- a/ios/chrome/browser/ui/settings/google_services/google_services_settings_mediator.mm +++ b/ios/chrome/browser/ui/settings/google_services/google_services_settings_mediator.mm
@@ -150,7 +150,7 @@ // The observable boolean that binds to the password leak check settings // state. @property(nonatomic, strong, readonly) - PrefBackedBoolean* passwordLeakCheckEnabled; + PrefBackedBoolean* passwordLeakCheckPreference; // The item related to the switch for the automatic password leak detection // setting. @property(nonatomic, strong, null_resettable) @@ -202,11 +202,11 @@ initWithPrefService:localPrefService prefName:metrics::prefs::kMetricsReportingEnabled]; _sendDataUsagePreference.observer = self; - _passwordLeakCheckEnabled = [[PrefBackedBoolean alloc] + _passwordLeakCheckPreference = [[PrefBackedBoolean alloc] initWithPrefService:userPrefService prefName:password_manager::prefs:: kPasswordLeakDetectionEnabled]; - _passwordLeakCheckEnabled.observer = self; + _passwordLeakCheckPreference.observer = self; _anonymizedDataCollectionPreference = [[PrefBackedBoolean alloc] initWithPrefService:userPrefService prefName:unified_consent::prefs:: @@ -444,7 +444,7 @@ passwordLeakCheckItem.on = [self passwordLeakCheckItemOnState]; passwordLeakCheckItem.accessibilityIdentifier = kPasswordLeakCheckItemAccessibilityIdentifier; - passwordLeakCheckItem.enabled = self.hasPrimaryIdentity; + passwordLeakCheckItem.enabled = [self isPasswordLeakCheckEnabled]; _passwordLeakCheckItem = passwordLeakCheckItem; } return _passwordLeakCheckItem; @@ -494,21 +494,30 @@ return managedItem; } +// Returns a boolean indicating whether leak detection feature is enabled. +- (BOOL)isPasswordLeakCheckEnabled { + return self.hasPrimaryIdentity || + base::FeatureList::IsEnabled( + password_manager::features::kLeakDetectionUnauthenticated); +} + // Returns a boolean indicating if the switch should appear as "On" or "Off" // based on the sync preference and the sign in status. - (BOOL)passwordLeakCheckItemOnState { return self.safeBrowsingPreference.value && - self.passwordLeakCheckEnabled.value && self.hasPrimaryIdentity; + self.passwordLeakCheckPreference.value && + [self isPasswordLeakCheckEnabled]; } // Updates the detail text and on state of the leak check item based on the // state. - (void)updateLeakCheckItem { self.passwordLeakCheckItem.enabled = - self.hasPrimaryIdentity && self.safeBrowsingPreference.value; + self.safeBrowsingPreference.value && [self isPasswordLeakCheckEnabled]; self.passwordLeakCheckItem.on = [self passwordLeakCheckItemOnState]; - if (!self.hasPrimaryIdentity && self.passwordLeakCheckEnabled.value) { + if (self.passwordLeakCheckPreference.value && + ![self isPasswordLeakCheckEnabled]) { // If the user is signed out and the sync preference is enabled, this // informs that it will be turned on on sign in. self.passwordLeakCheckItem.detailText = @@ -586,7 +595,7 @@ break; case PasswordLeakCheckSwitchItemType: // Update the pref. - self.passwordLeakCheckEnabled.value = value; + self.passwordLeakCheckPreference.value = value; // Update the item. [self updateLeakCheckItem]; break;
diff --git a/ios/chrome/browser/ui/table_view/cells/table_view_text_edit_item.mm b/ios/chrome/browser/ui/table_view/cells/table_view_text_edit_item.mm index cc2c4365..63ceff75 100644 --- a/ios/chrome/browser/ui/table_view/cells/table_view_text_edit_item.mm +++ b/ios/chrome/browser/ui/table_view/cells/table_view_text_edit_item.mm
@@ -23,6 +23,8 @@ const CGFloat kLabelAndFieldGap = 5; // Height/width of the edit icon. const CGFloat kEditIconLength = 18; +// Height/width of the error icon. +const CGFloat kErrorIconLength = 20; } // namespace @@ -299,6 +301,9 @@ #pragma mark Public - (void)setIcon:(TableViewTextEditItemIconType)iconType { + self.textFieldTrailingConstraint.constant = -kLabelAndFieldGap; + self.textLabelTrailingConstraint.constant = -kLabelAndFieldGap; + switch (iconType) { case TableViewTextEditItemIconTypeNone: self.iconView.hidden = YES; @@ -312,13 +317,15 @@ self.iconView.hidden = NO; [self.iconView setImage:[self editImage]]; self.iconView.tintColor = [UIColor colorNamed:kGrey400Color]; - [self setIconTypeNoneConstraints]; + + _editIconHeightConstraint.constant = kEditIconLength; break; case TableViewTextEditItemIconTypeError: self.iconView.hidden = NO; [self.iconView setImage:[self errorImage]]; self.iconView.tintColor = [UIColor colorNamed:kRedColor]; - [self setIconTypeNoneConstraints]; + + _editIconHeightConstraint.constant = kErrorIconLength; break; default: NOTREACHED(); @@ -419,14 +426,6 @@ } } -// Sets constraints when the icon needs to be hidden. -- (void)setIconTypeNoneConstraints { - self.textFieldTrailingConstraint.constant = -kLabelAndFieldGap; - self.textLabelTrailingConstraint.constant = -kLabelAndFieldGap; - - _editIconHeightConstraint.constant = kEditIconLength; -} - // Returns the edit icon image. - (UIImage*)editImage { return [[UIImage imageNamed:@"table_view_cell_edit_icon"]
diff --git a/ios/chrome/browser/ui/table_view/chrome_table_view_controller.mm b/ios/chrome/browser/ui/table_view/chrome_table_view_controller.mm index d4971ec..cc78cc64 100644 --- a/ios/chrome/browser/ui/table_view/chrome_table_view_controller.mm +++ b/ios/chrome/browser/ui/table_view/chrome_table_view_controller.mm
@@ -75,10 +75,13 @@ // TODO(crbug.com/1254652): Large titles appear collapsed in some case when // opening a tableView. e.g when opening History screen without entry. Remove -// this method when the issue is fixed. +// this method when iOS 14 is dropped. - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; - [self.navigationController.navigationBar sizeToFit]; + if (@available(iOS 15, *)) { + } else { + [self.navigationController.navigationBar sizeToFit]; + } } #pragma mark - Accessors
diff --git a/media/capture/video/chromeos/camera_hal_dispatcher_impl.cc b/media/capture/video/chromeos/camera_hal_dispatcher_impl.cc index 2a755b8..aef8354f 100644 --- a/media/capture/video/chromeos/camera_hal_dispatcher_impl.cc +++ b/media/capture/video/chromeos/camera_hal_dispatcher_impl.cc
@@ -53,6 +53,10 @@ "/run/camera/force_enable_hdrnet"; const base::FilePath::CharType kForceDisableHdrNetPath[] = "/run/camera/force_disable_hdrnet"; +const base::FilePath::CharType kForceEnableAutoFramingPath[] = + "/run/camera/force_enable_auto_framing"; +const base::FilePath::CharType kForceDisableAutoFramingPath[] = + "/run/camera/force_disable_auto_framing"; std::string GenerateRandomToken() { char random_bytes[16]; @@ -196,10 +200,12 @@ if (command_line->HasSwitch(media::switches::kForceControlFaceAe)) { if (command_line->GetSwitchValueASCII( media::switches::kForceControlFaceAe) == "enable") { - base::File file(enable_file_path, base::File::FLAG_CREATE_ALWAYS); + base::File file(enable_file_path, base::File::FLAG_CREATE_ALWAYS | + base::File::FLAG_WRITE); file.Close(); } else { - base::File file(disable_file_path, base::File::FLAG_CREATE_ALWAYS); + base::File file(disable_file_path, base::File::FLAG_CREATE_ALWAYS | + base::File::FLAG_WRITE); file.Close(); } } @@ -220,10 +226,38 @@ std::string value = command_line->GetSwitchValueASCII(switches::kHdrNetOverride); if (value == switches::kHdrNetForceEnabled) { - base::File file(enable_file_path, base::File::FLAG_CREATE_ALWAYS); + base::File file(enable_file_path, base::File::FLAG_CREATE_ALWAYS | + base::File::FLAG_WRITE); file.Close(); } else if (value == switches::kHdrNetForceDisabled) { - base::File file(disable_file_path, base::File::FLAG_CREATE_ALWAYS); + base::File file(disable_file_path, base::File::FLAG_CREATE_ALWAYS | + base::File::FLAG_WRITE); + file.Close(); + } + } + } + + { + base::FilePath enable_file_path(kForceEnableAutoFramingPath); + base::FilePath disable_file_path(kForceDisableAutoFramingPath); + if (!base::DeleteFile(enable_file_path)) { + LOG(WARNING) << "Could not delete " << kForceEnableAutoFramingPath; + } + if (!base::DeleteFile(disable_file_path)) { + LOG(WARNING) << "Could not delete " << kForceDisableAutoFramingPath; + } + const base::CommandLine* command_line = + base::CommandLine::ForCurrentProcess(); + if (command_line->HasSwitch(media::switches::kAutoFramingOverride)) { + std::string value = + command_line->GetSwitchValueASCII(switches::kAutoFramingOverride); + if (value == switches::kAutoFramingForceEnabled) { + base::File file(enable_file_path, base::File::FLAG_CREATE_ALWAYS | + base::File::FLAG_WRITE); + file.Close(); + } else if (value == switches::kAutoFramingForceDisabled) { + base::File file(disable_file_path, base::File::FLAG_CREATE_ALWAYS | + base::File::FLAG_WRITE); file.Close(); } }
diff --git a/media/capture/video/chromeos/video_capture_features_chromeos.cc b/media/capture/video/chromeos/video_capture_features_chromeos.cc index a892c838..82570b1 100644 --- a/media/capture/video/chromeos/video_capture_features_chromeos.cc +++ b/media/capture/video/chromeos/video_capture_features_chromeos.cc
@@ -10,6 +10,7 @@ const char kForceControlFaceAe[] = "force-control-face-ae"; const char kHdrNetOverride[] = "hdrnet-override"; +const char kAutoFramingOverride[] = "auto-framing-override"; } // namespace switches
diff --git a/media/capture/video/chromeos/video_capture_features_chromeos.h b/media/capture/video/chromeos/video_capture_features_chromeos.h index 4170ee3..61edf0c 100644 --- a/media/capture/video/chromeos/video_capture_features_chromeos.h +++ b/media/capture/video/chromeos/video_capture_features_chromeos.h
@@ -18,6 +18,10 @@ constexpr char kHdrNetForceEnabled[] = "force-enabled"; constexpr char kHdrNetForceDisabled[] = "force-disabled"; +CAPTURE_EXPORT extern const char kAutoFramingOverride[]; +constexpr char kAutoFramingForceEnabled[] = "force-enabled"; +constexpr char kAutoFramingForceDisabled[] = "force-disabled"; + } // namespace switches namespace features {
diff --git a/ppapi/proxy/plugin_var_tracker.cc b/ppapi/proxy/plugin_var_tracker.cc index b8fcd568..b558d86 100644 --- a/ppapi/proxy/plugin_var_tracker.cc +++ b/ppapi/proxy/plugin_var_tracker.cc
@@ -517,5 +517,5 @@ return false; } -} // namesace proxy +} // namespace proxy } // namespace ppapi
diff --git a/services/network/cors/cors_url_loader.cc b/services/network/cors/cors_url_loader.cc index 96f1e1f..75306f3 100644 --- a/services/network/cors/cors_url_loader.cc +++ b/services/network/cors/cors_url_loader.cc
@@ -8,7 +8,7 @@ #include "base/containers/contains.h" #include "base/containers/flat_set.h" #include "base/feature_list.h" -#include "base/metrics/histogram_macros.h" +#include "base/metrics/histogram_functions.h" #include "base/strings/string_split.h" #include "net/base/load_flags.h" #include "net/http/http_status_code.h" @@ -737,6 +737,41 @@ CloneClientSecurityState(), request_.url, status, is_warning); } +absl::optional<URLLoaderCompletionStatus> CorsURLLoader::ConvertPreflightResult( + int net_error, + absl::optional<CorsErrorStatus> status) { + if (net_error == net::OK) { + DCHECK(!status) << *status; + return absl::nullopt; + } + + // `kInvalidResponse` is never returned by the preflight controller, so we use + // it to record the case where there was a net error and no CORS error. + auto histogram_error = mojom::CorsError::kInvalidResponse; + if (status) { + DCHECK(status->cors_error != mojom::CorsError::kInvalidResponse); + histogram_error = status->cors_error; + } + + if (should_ignore_preflight_errors_) { + // Even if we ignore the error, record the warning in metrics and DevTools. + base::UmaHistogramEnumeration(kPreflightWarningHistogramName, + histogram_error); + if (status && devtools_observer_) { + ReportCorsErrorToDevTools(*status, /*is_warning=*/true); + } + + return absl::nullopt; + } + + base::UmaHistogramEnumeration(kPreflightErrorHistogramName, histogram_error); + if (status) { + return URLLoaderCompletionStatus(*std::move(status)); + } + + return URLLoaderCompletionStatus(net_error); +} + void CorsURLLoader::OnPreflightRequestComplete( int net_error, absl::optional<CorsErrorStatus> status, @@ -744,19 +779,13 @@ has_authorization_covered_by_wildcard_ = has_authorization_covered_by_wildcard; - if (net_error == net::OK) { - DCHECK(!status) << *status; - } else if (!should_ignore_preflight_errors_) { - HandleComplete(status.has_value() ? URLLoaderCompletionStatus(*status) - : URLLoaderCompletionStatus(net_error)); + absl::optional<URLLoaderCompletionStatus> completion_status = + ConvertPreflightResult(net_error, std::move(status)); + if (completion_status) { + HandleComplete(*std::move(completion_status)); return; } - // Even if we ignore the error, report it as a warning to DevTools. - if (devtools_observer_ && status) { - ReportCorsErrorToDevTools(*status, /*is_warning=*/true); - } - StartNetworkRequest(); }
diff --git a/services/network/cors/cors_url_loader.h b/services/network/cors/cors_url_loader.h index 017c2cba..6be0a31 100644 --- a/services/network/cors/cors_url_loader.h +++ b/services/network/cors/cors_url_loader.h
@@ -19,6 +19,7 @@ #include "services/network/public/mojom/devtools_observer.mojom.h" #include "services/network/public/mojom/fetch_api.mojom.h" #include "services/network/public/mojom/url_loader.mojom.h" +#include "services/network/public/mojom/url_loader_completion_status.mojom.h" #include "services/network/public/mojom/url_loader_factory.mojom.h" #include "third_party/abseil-cpp/absl/types/optional.h" #include "url/gurl.h" @@ -118,6 +119,12 @@ private: void StartRequest(); + + // Helper for `OnPreflightRequestComplete()`. + absl::optional<URLLoaderCompletionStatus> ConvertPreflightResult( + int net_error, + absl::optional<CorsErrorStatus> status); + void OnPreflightRequestComplete(int net_error, absl::optional<CorsErrorStatus> status, bool has_authorization_covered_by_wildcard); @@ -260,7 +267,7 @@ // sent the preflight, so we ignore them all. // // INVARIANT: if this is true, then - // `should_ignore_private_network_access_errors_` is also true. + // `ShouldIgnorePrivateNetworkAccessErrors()` is also true. // // TODO(https://crbug.com/1268378): Remove this along with // `should_ignore_private_network_access_errors_`.
diff --git a/services/network/cors/cors_url_loader_unittest.cc b/services/network/cors/cors_url_loader_unittest.cc index 22d06df0..6f5ffe7 100644 --- a/services/network/cors/cors_url_loader_unittest.cc +++ b/services/network/cors/cors_url_loader_unittest.cc
@@ -59,6 +59,8 @@ namespace { +using ::testing::ElementsAre; +using ::testing::IsEmpty; using ::testing::IsSupersetOf; using ::testing::Optional; using ::testing::Pair; @@ -67,6 +69,15 @@ constexpr char kTestCorsExemptHeader[] = "x-test-cors-exempt"; +constexpr char kPreflightErrorHistogramName[] = "Net.Cors.PreflightCheckError2"; +constexpr char kPreflightWarningHistogramName[] = + "Net.Cors.PreflightCheckWarning"; + +base::Bucket MakeBucket(mojom::CorsError error, + base::HistogramBase::Count count) { + return base::Bucket(static_cast<base::HistogramBase::Sample>(error), count); +} + class TestURLLoaderFactory : public mojom::URLLoaderFactory { public: TestURLLoaderFactory() {} @@ -610,8 +621,7 @@ EXPECT_TRUE(client().has_received_completion()); if (expect_allowed) { EXPECT_THAT(client().completion_status().error_code, net::test::IsOk()); - EXPECT_THAT(bad_message_helper.bad_message_reports(), - ::testing::IsEmptyMatcher()); + EXPECT_THAT(bad_message_helper.bad_message_reports(), IsEmpty()); } else { EXPECT_THAT(client().completion_status().error_code, net::test::IsError(net::ERR_INVALID_ARGUMENT)); @@ -3519,6 +3529,8 @@ mojom::PrivateNetworkRequestPolicy::kPreflightWarn) .Build(); + base::HistogramTester histogram_tester; + CreateLoaderAndStart(request); RunUntilCreateLoaderAndStartCalled(); NotifyLoaderClientOnComplete(CorsErrorStatus( @@ -3534,6 +3546,11 @@ RunUntilComplete(); EXPECT_EQ(client().completion_status().error_code, net::OK); + + EXPECT_THAT(histogram_tester.GetAllSamples(kPreflightErrorHistogramName), + IsEmpty()); + EXPECT_THAT(histogram_tester.GetAllSamples(kPreflightWarningHistogramName), + ElementsAre(MakeBucket(mojom::CorsError::kInvalidResponse, 1))); } // This test verifies that when: @@ -3561,6 +3578,8 @@ mojom::PrivateNetworkRequestPolicy::kPreflightWarn) .Build(); + base::HistogramTester histogram_tester; + CreateLoaderAndStart(request); RunUntilCreateLoaderAndStartCalled(); NotifyLoaderClientOnComplete(CorsErrorStatus( @@ -3568,8 +3587,7 @@ mojom::IPAddressSpace::kUnknown, mojom::IPAddressSpace::kPrivate)); RunUntilCreateLoaderAndStartCalled(); - NotifyLoaderClientOnComplete( - CorsErrorStatus(mojom::CorsError::kMissingAllowOriginHeader)); + NotifyLoaderClientOnReceiveResponse(); RunUntilCreateLoaderAndStartCalled(); NotifyLoaderClientOnReceiveResponse(); @@ -3577,6 +3595,12 @@ RunUntilComplete(); EXPECT_EQ(client().completion_status().error_code, net::OK); + + EXPECT_THAT(histogram_tester.GetAllSamples(kPreflightErrorHistogramName), + IsEmpty()); + EXPECT_THAT(histogram_tester.GetAllSamples(kPreflightWarningHistogramName), + ElementsAre(MakeBucket( + mojom::CorsError::kPreflightMissingAllowOriginHeader, 1))); } // This test verifies that when: @@ -3605,6 +3629,8 @@ mojom::PrivateNetworkRequestPolicy::kPreflightWarn) .Build(); + base::HistogramTester histogram_tester; + CreateLoaderAndStart(request); RunUntilCreateLoaderAndStartCalled(); NotifyLoaderClientOnComplete(CorsErrorStatus( @@ -3624,6 +3650,12 @@ RunUntilComplete(); EXPECT_EQ(client().completion_status().error_code, net::OK); + + EXPECT_THAT(histogram_tester.GetAllSamples(kPreflightErrorHistogramName), + IsEmpty()); + EXPECT_THAT(histogram_tester.GetAllSamples(kPreflightWarningHistogramName), + ElementsAre(MakeBucket( + mojom::CorsError::kPreflightMissingAllowPrivateNetwork, 1))); } // This test verifies that when: @@ -3652,6 +3684,8 @@ mojom::PrivateNetworkRequestPolicy::kPreflightWarn) .Build(); + base::HistogramTester histogram_tester; + CreateLoaderAndStart(request); RunUntilCreateLoaderAndStartCalled(); NotifyLoaderClientOnComplete(CorsErrorStatus( @@ -3672,6 +3706,12 @@ RunUntilComplete(); EXPECT_EQ(client().completion_status().error_code, net::OK); + + EXPECT_THAT(histogram_tester.GetAllSamples(kPreflightErrorHistogramName), + IsEmpty()); + EXPECT_THAT(histogram_tester.GetAllSamples(kPreflightWarningHistogramName), + ElementsAre(MakeBucket( + mojom::CorsError::kPreflightInvalidAllowPrivateNetwork, 1))); } // This test verifies that when: @@ -3699,6 +3739,8 @@ mojom::PrivateNetworkRequestPolicy::kPreflightWarn) .Build(); + base::HistogramTester histogram_tester; + CreateLoaderAndStart(request); RunUntilCreateLoaderAndStartCalled(); NotifyLoaderClientOnComplete(CorsErrorStatus( @@ -3710,6 +3752,11 @@ RunUntilComplete(); EXPECT_EQ(client().completion_status().error_code, net::ERR_INVALID_ARGUMENT); + + EXPECT_THAT(histogram_tester.GetAllSamples(kPreflightErrorHistogramName), + ElementsAre(MakeBucket(mojom::CorsError::kInvalidResponse, 1))); + EXPECT_THAT(histogram_tester.GetAllSamples(kPreflightWarningHistogramName), + IsEmpty()); } // This test verifies that when: @@ -3745,6 +3792,8 @@ .WithDevToolsObserver(devtools_observer.Bind()) .Build(); + base::HistogramTester histogram_tester; + CreateLoaderAndStart(request); RunUntilCreateLoaderAndStartCalled(); NotifyLoaderClientOnComplete(CorsErrorStatus( @@ -3752,21 +3801,27 @@ mojom::IPAddressSpace::kUnknown, mojom::IPAddressSpace::kPrivate)); RunUntilCreateLoaderAndStartCalled(); - NotifyLoaderClientOnComplete( - CorsErrorStatus(mojom::CorsError::kMissingAllowOriginHeader)); + NotifyLoaderClientOnReceiveResponse(); RunUntilComplete(); EXPECT_EQ(client().completion_status().error_code, net::ERR_FAILED); - EXPECT_THAT( - client().completion_status().cors_error_status, - Optional(CorsErrorStatus(mojom::CorsError::kMissingAllowOriginHeader))); + EXPECT_THAT(client().completion_status().cors_error_status, + Optional(CorsErrorStatus( + mojom::CorsError::kPreflightMissingAllowOriginHeader))); + + EXPECT_THAT(histogram_tester.GetAllSamples(kPreflightErrorHistogramName), + ElementsAre(MakeBucket( + mojom::CorsError::kPreflightMissingAllowOriginHeader, 1))); + EXPECT_THAT(histogram_tester.GetAllSamples(kPreflightWarningHistogramName), + IsEmpty()); devtools_observer.WaitUntilCorsError(); const MockDevToolsObserver::OnCorsErrorParams& error_params = *devtools_observer.cors_error_params(); - EXPECT_EQ(error_params.status, - CorsErrorStatus(mojom::CorsError::kMissingAllowOriginHeader)); + EXPECT_EQ( + error_params.status, + CorsErrorStatus(mojom::CorsError::kPreflightMissingAllowOriginHeader)); EXPECT_FALSE(error_params.is_warning); ASSERT_TRUE(error_params.client_security_state); EXPECT_TRUE(error_params.client_security_state->is_web_secure_context); @@ -3813,6 +3868,8 @@ // and warnings suppressed inside `PreflightController` are not observed. request.devtools_request_id = "devtools"; + base::HistogramTester histogram_tester; + CreateLoaderAndStart(request); RunUntilCreateLoaderAndStartCalled(); NotifyLoaderClientOnComplete(CorsErrorStatus( @@ -3833,6 +3890,12 @@ EXPECT_EQ(client().completion_status().error_code, net::OK); + EXPECT_THAT(histogram_tester.GetAllSamples(kPreflightErrorHistogramName), + IsEmpty()); + EXPECT_THAT(histogram_tester.GetAllSamples(kPreflightWarningHistogramName), + ElementsAre(MakeBucket( + mojom::CorsError::kPreflightMissingAllowPrivateNetwork, 1))); + devtools_observer.WaitUntilCorsError(); CorsErrorStatus expected_status( @@ -3881,6 +3944,8 @@ mojom::PrivateNetworkRequestPolicy::kPreflightBlock) .Build(); + base::HistogramTester histogram_tester; + CreateLoaderAndStart(request); RunUntilCreateLoaderAndStartCalled(); NotifyLoaderClientOnComplete(CorsErrorStatus( @@ -3892,6 +3957,11 @@ RunUntilComplete(); EXPECT_EQ(client().completion_status().error_code, net::ERR_INVALID_ARGUMENT); + + EXPECT_THAT(histogram_tester.GetAllSamples(kPreflightErrorHistogramName), + ElementsAre(MakeBucket(mojom::CorsError::kInvalidResponse, 1))); + EXPECT_THAT(histogram_tester.GetAllSamples(kPreflightWarningHistogramName), + IsEmpty()); } // This test verifies that when: @@ -3927,6 +3997,8 @@ .WithDevToolsObserver(devtools_observer.Bind()) .Build(); + base::HistogramTester histogram_tester; + CreateLoaderAndStart(request); RunUntilCreateLoaderAndStartCalled(); NotifyLoaderClientOnComplete(CorsErrorStatus( @@ -3934,21 +4006,27 @@ mojom::IPAddressSpace::kUnknown, mojom::IPAddressSpace::kPrivate)); RunUntilCreateLoaderAndStartCalled(); - NotifyLoaderClientOnComplete( - CorsErrorStatus(mojom::CorsError::kMissingAllowOriginHeader)); + NotifyLoaderClientOnReceiveResponse(); RunUntilComplete(); EXPECT_EQ(client().completion_status().error_code, net::ERR_FAILED); - EXPECT_THAT( - client().completion_status().cors_error_status, - Optional(CorsErrorStatus(mojom::CorsError::kMissingAllowOriginHeader))); + EXPECT_THAT(client().completion_status().cors_error_status, + Optional(CorsErrorStatus( + mojom::CorsError::kPreflightMissingAllowOriginHeader))); + + EXPECT_THAT(histogram_tester.GetAllSamples(kPreflightErrorHistogramName), + ElementsAre(MakeBucket( + mojom::CorsError::kPreflightMissingAllowOriginHeader, 1))); + EXPECT_THAT(histogram_tester.GetAllSamples(kPreflightWarningHistogramName), + IsEmpty()); devtools_observer.WaitUntilCorsError(); const MockDevToolsObserver::OnCorsErrorParams& error_params = *devtools_observer.cors_error_params(); - EXPECT_EQ(error_params.status, - CorsErrorStatus(mojom::CorsError::kMissingAllowOriginHeader)); + EXPECT_EQ( + error_params.status, + CorsErrorStatus(mojom::CorsError::kPreflightMissingAllowOriginHeader)); EXPECT_FALSE(error_params.is_warning); ASSERT_TRUE(error_params.client_security_state); EXPECT_TRUE(error_params.client_security_state->is_web_secure_context); @@ -3992,6 +4070,8 @@ .WithDevToolsObserver(devtools_observer.Bind()) .Build(); + base::HistogramTester histogram_tester; + CreateLoaderAndStart(request); RunUntilCreateLoaderAndStartCalled(); NotifyLoaderClientOnComplete(CorsErrorStatus( @@ -4014,6 +4094,12 @@ EXPECT_THAT(client().completion_status().cors_error_status, Optional(expected_status)); + EXPECT_THAT(histogram_tester.GetAllSamples(kPreflightErrorHistogramName), + ElementsAre(MakeBucket( + mojom::CorsError::kPreflightMissingAllowPrivateNetwork, 1))); + EXPECT_THAT(histogram_tester.GetAllSamples(kPreflightWarningHistogramName), + IsEmpty()); + devtools_observer.WaitUntilCorsError(); const MockDevToolsObserver::OnCorsErrorParams& error_params = @@ -4062,6 +4148,8 @@ .WithDevToolsObserver(devtools_observer.Bind()) .Build(); + base::HistogramTester histogram_tester; + CreateLoaderAndStart(request); RunUntilCreateLoaderAndStartCalled(); NotifyLoaderClientOnComplete(CorsErrorStatus( @@ -4085,6 +4173,12 @@ EXPECT_THAT(client().completion_status().cors_error_status, Optional(expected_status)); + EXPECT_THAT(histogram_tester.GetAllSamples(kPreflightErrorHistogramName), + ElementsAre(MakeBucket( + mojom::CorsError::kPreflightInvalidAllowPrivateNetwork, 1))); + EXPECT_THAT(histogram_tester.GetAllSamples(kPreflightWarningHistogramName), + IsEmpty()); + devtools_observer.WaitUntilCorsError(); const MockDevToolsObserver::OnCorsErrorParams& error_params =
diff --git a/services/network/cors/preflight_controller.cc b/services/network/cors/preflight_controller.cc index 879272b..a3e76ea 100644 --- a/services/network/cors/preflight_controller.cc +++ b/services/network/cors/preflight_controller.cc
@@ -9,6 +9,7 @@ #include "base/bind.h" #include "base/memory/raw_ptr.h" +#include "base/metrics/histogram_functions.h" #include "base/metrics/histogram_macros.h" #include "base/no_destructor.h" #include "base/strings/string_util.h" @@ -244,8 +245,6 @@ return absl::nullopt; } - UMA_HISTOGRAM_ENUMERATION("Net.Cors.PreflightCheckError", - error_status->cors_error); return error_status; } @@ -320,6 +319,9 @@ original_request.request_initiator, client_security_state.Clone(), original_request.url, *status, /*is_warning=*/true); } + + base::UmaHistogramEnumeration(kPreflightWarningHistogramName, + status->cors_error); } absl::optional<mojom::CorsError> error; @@ -352,6 +354,9 @@ } // namespace +const char kPreflightErrorHistogramName[] = "Net.Cors.PreflightCheckError2"; +const char kPreflightWarningHistogramName[] = "Net.Cors.PreflightCheckWarning"; + class PreflightController::PreflightLoader final { public: PreflightLoader(
diff --git a/services/network/cors/preflight_controller.h b/services/network/cors/preflight_controller.h index ba5e70dd..c7d2e8d9 100644 --- a/services/network/cors/preflight_controller.h +++ b/services/network/cors/preflight_controller.h
@@ -33,6 +33,12 @@ namespace cors { +// Name of a histogram that records preflight errors (CorsError values). +extern const char kPreflightErrorHistogramName[]; + +// Name of a histogram that records suppressed preflight errors, aka warnings. +extern const char kPreflightWarningHistogramName[]; + // A class to manage CORS-preflight, making a CORS-preflight request, checking // its result, and owning a CORS-preflight cache. class COMPONENT_EXPORT(NETWORK_SERVICE) PreflightController final {
diff --git a/services/strings/services_strings_km.xtb b/services/strings/services_strings_km.xtb index 4f5d133..f1b702c 100644 --- a/services/strings/services_strings_km.xtb +++ b/services/strings/services_strings_km.xtb
@@ -1,5 +1,6 @@ <?xml version="1.0" ?> <!DOCTYPE translationbundle> <translationbundle lang="km"> +<translation id="1553734813273230889">កម្មវិធីបំប្លែងប្រូកស៊ី WinHttp</translation> <translation id="8191453843330043793">កម្មវិធីដោះស្រាយប្រូកស៊ី V8</translation> </translationbundle> \ No newline at end of file
diff --git a/services/strings/services_strings_tr.xtb b/services/strings/services_strings_tr.xtb index 594c044..b5f848b 100644 --- a/services/strings/services_strings_tr.xtb +++ b/services/strings/services_strings_tr.xtb
@@ -1,5 +1,6 @@ <?xml version="1.0" ?> <!DOCTYPE translationbundle> <translationbundle lang="tr"> +<translation id="1553734813273230889">WinHttp Proxy Çözümleyici</translation> <translation id="8191453843330043793">V8 Proxy Çözümleyici</translation> </translationbundle> \ No newline at end of file
diff --git a/testing/buildbot/chromium.android.fyi.json b/testing/buildbot/chromium.android.fyi.json index 3df2ddf..872fd61 100644 --- a/testing/buildbot/chromium.android.fyi.json +++ b/testing/buildbot/chromium.android.fyi.json
@@ -5796,7 +5796,7 @@ { "cipd_package": "chromium/testing/weblayer-x86", "location": "weblayer_instrumentation_test_M96", - "revision": "version:96.0.4664.96" + "revision": "version:96.0.4664.97" }, { "cipd_package": "infra/tools/luci/logdog/butler/${platform}", @@ -5884,7 +5884,7 @@ { "cipd_package": "chromium/testing/weblayer-x86", "location": "weblayer_instrumentation_test_M97", - "revision": "version:97.0.4692.42" + "revision": "version:97.0.4692.43" }, { "cipd_package": "infra/tools/luci/logdog/butler/${platform}", @@ -6060,7 +6060,7 @@ { "cipd_package": "chromium/testing/weblayer-x86", "location": "weblayer_instrumentation_test_M96", - "revision": "version:96.0.4664.96" + "revision": "version:96.0.4664.97" }, { "cipd_package": "infra/tools/luci/logdog/butler/${platform}", @@ -6148,7 +6148,7 @@ { "cipd_package": "chromium/testing/weblayer-x86", "location": "weblayer_instrumentation_test_M97", - "revision": "version:97.0.4692.42" + "revision": "version:97.0.4692.43" }, { "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
diff --git a/testing/buildbot/chromium.android.json b/testing/buildbot/chromium.android.json index c559e05..4baacb1 100644 --- a/testing/buildbot/chromium.android.json +++ b/testing/buildbot/chromium.android.json
@@ -43665,7 +43665,7 @@ { "cipd_package": "chromium/testing/weblayer-x86", "location": "weblayer_instrumentation_test_M96", - "revision": "version:96.0.4664.96" + "revision": "version:96.0.4664.97" }, { "cipd_package": "infra/tools/luci/logdog/butler/${platform}", @@ -43753,7 +43753,7 @@ { "cipd_package": "chromium/testing/weblayer-x86", "location": "weblayer_instrumentation_test_M97", - "revision": "version:97.0.4692.42" + "revision": "version:97.0.4692.43" }, { "cipd_package": "infra/tools/luci/logdog/butler/${platform}", @@ -43929,7 +43929,7 @@ { "cipd_package": "chromium/testing/weblayer-x86", "location": "weblayer_instrumentation_test_M96", - "revision": "version:96.0.4664.96" + "revision": "version:96.0.4664.97" }, { "cipd_package": "infra/tools/luci/logdog/butler/${platform}", @@ -44017,7 +44017,7 @@ { "cipd_package": "chromium/testing/weblayer-x86", "location": "weblayer_instrumentation_test_M97", - "revision": "version:97.0.4692.42" + "revision": "version:97.0.4692.43" }, { "cipd_package": "infra/tools/luci/logdog/butler/${platform}", @@ -44268,7 +44268,7 @@ { "cipd_package": "chromium/testing/weblayer-x86", "location": "weblayer_instrumentation_test_M96", - "revision": "version:96.0.4664.96" + "revision": "version:96.0.4664.97" }, { "cipd_package": "infra/tools/luci/logdog/butler/${platform}", @@ -44356,7 +44356,7 @@ { "cipd_package": "chromium/testing/weblayer-x86", "location": "weblayer_instrumentation_test_M97", - "revision": "version:97.0.4692.42" + "revision": "version:97.0.4692.43" }, { "cipd_package": "infra/tools/luci/logdog/butler/${platform}", @@ -44532,7 +44532,7 @@ { "cipd_package": "chromium/testing/weblayer-x86", "location": "weblayer_instrumentation_test_M96", - "revision": "version:96.0.4664.96" + "revision": "version:96.0.4664.97" }, { "cipd_package": "infra/tools/luci/logdog/butler/${platform}", @@ -44620,7 +44620,7 @@ { "cipd_package": "chromium/testing/weblayer-x86", "location": "weblayer_instrumentation_test_M97", - "revision": "version:97.0.4692.42" + "revision": "version:97.0.4692.43" }, { "cipd_package": "infra/tools/luci/logdog/butler/${platform}", @@ -44871,7 +44871,7 @@ { "cipd_package": "chromium/testing/weblayer-x86", "location": "weblayer_instrumentation_test_M96", - "revision": "version:96.0.4664.96" + "revision": "version:96.0.4664.97" }, { "cipd_package": "infra/tools/luci/logdog/butler/${platform}", @@ -44959,7 +44959,7 @@ { "cipd_package": "chromium/testing/weblayer-x86", "location": "weblayer_instrumentation_test_M97", - "revision": "version:97.0.4692.42" + "revision": "version:97.0.4692.43" }, { "cipd_package": "infra/tools/luci/logdog/butler/${platform}", @@ -45135,7 +45135,7 @@ { "cipd_package": "chromium/testing/weblayer-x86", "location": "weblayer_instrumentation_test_M96", - "revision": "version:96.0.4664.96" + "revision": "version:96.0.4664.97" }, { "cipd_package": "infra/tools/luci/logdog/butler/${platform}", @@ -45223,7 +45223,7 @@ { "cipd_package": "chromium/testing/weblayer-x86", "location": "weblayer_instrumentation_test_M97", - "revision": "version:97.0.4692.42" + "revision": "version:97.0.4692.43" }, { "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
diff --git a/testing/buildbot/variants.pyl b/testing/buildbot/variants.pyl index e9f9c95..b9d5a6b 100644 --- a/testing/buildbot/variants.pyl +++ b/testing/buildbot/variants.pyl
@@ -363,7 +363,7 @@ { 'cipd_package': 'chromium/testing/weblayer-x86', 'location': 'weblayer_instrumentation_test_M97', - 'revision': 'version:97.0.4692.42', + 'revision': 'version:97.0.4692.43', } ], }, @@ -387,7 +387,7 @@ { 'cipd_package': 'chromium/testing/weblayer-x86', 'location': 'weblayer_instrumentation_test_M96', - 'revision': 'version:96.0.4664.96', + 'revision': 'version:96.0.4664.97', } ], }, @@ -435,7 +435,7 @@ { 'cipd_package': 'chromium/testing/weblayer-x86', 'location': 'weblayer_instrumentation_test_M97', - 'revision': 'version:97.0.4692.42', + 'revision': 'version:97.0.4692.43', } ], }, @@ -459,7 +459,7 @@ { 'cipd_package': 'chromium/testing/weblayer-x86', 'location': 'weblayer_instrumentation_test_M96', - 'revision': 'version:96.0.4664.96', + 'revision': 'version:96.0.4664.97', } ], }, @@ -507,7 +507,7 @@ { 'cipd_package': 'chromium/testing/weblayer-x86', 'location': 'weblayer_instrumentation_test_M97', - 'revision': 'version:97.0.4692.42', + 'revision': 'version:97.0.4692.43', } ], }, @@ -531,7 +531,7 @@ { 'cipd_package': 'chromium/testing/weblayer-x86', 'location': 'weblayer_instrumentation_test_M96', - 'revision': 'version:96.0.4664.96', + 'revision': 'version:96.0.4664.97', } ], },
diff --git a/testing/scripts/run_android_wpt.py b/testing/scripts/run_android_wpt.py index eb41717..44bbc92 100755 --- a/testing/scripts/run_android_wpt.py +++ b/testing/scripts/run_android_wpt.py
@@ -35,7 +35,7 @@ from wpt_android_lib import ( WPTWeblayerAdapter, WPTWebviewAdapter, WPTClankAdapter, - add_emulator_args, get_device) + add_emulator_args, get_devices) logger = logging.getLogger(__name__) @@ -65,13 +65,13 @@ from devil import devil_env -def _get_adapter(product, device): +def _get_adapter(product, devices): if product == ANDROID_WEBLAYER: - return WPTWeblayerAdapter(device) + return WPTWeblayerAdapter(devices) elif product == ANDROID_WEBVIEW: - return WPTWebviewAdapter(device) + return WPTWebviewAdapter(devices) else: - return WPTClankAdapter(device) + return WPTClankAdapter(devices) # This is not really a "script test" so does not need to manually add @@ -92,12 +92,12 @@ args, _ = product_parser.parse_known_args() product = args.product - with get_device(args) as device: - if not device: + with get_devices(args) as devices: + if not devices: logger.error('There are no devices attached to this host. Exiting...') return - adapter = _get_adapter(product, device) + adapter = _get_adapter(product, devices) if adapter.options.verbose: if adapter.options.verbose == 1: logger.setLevel(logging.INFO)
diff --git a/testing/scripts/wpt_android_lib.py b/testing/scripts/wpt_android_lib.py index 12da624..c1fbd8b 100644 --- a/testing/scripts/wpt_android_lib.py +++ b/testing/scripts/wpt_android_lib.py
@@ -67,11 +67,11 @@ class WPTAndroidAdapter(wpt_common.BaseWptScriptAdapter): - def __init__(self, device): + def __init__(self, devices): self.pass_through_wpt_args = [] self.pass_through_binary_args = [] self._metadata_dir = None - self._device = device + self._devices = devices super(WPTAndroidAdapter, self).__init__() # Arguments from add_extra_argumentsparse were added so # its safe to parse the arguments and set self._options @@ -101,7 +101,6 @@ rest_args.extend(['run', '--tests=' + wpt_common.EXTERNAL_WPT_TESTS_DIR, '--test-type=' + self.options.test_type, - '--device-serial', self._device.serial, '--webdriver-binary', self.options.webdriver_binary, '--symbols-path', @@ -122,6 +121,10 @@ '--binary-arg=--force-fieldtrial-params=DownloadServiceStudy.Enabled:' 'start_up_delay_ms/0', ]) + + for device in self._devices: + rest_args.extend(['--device-serial', device.serial]) + # if metadata was created then add the metadata directory # to the list of wpt arguments if self._metadata_dir: @@ -281,11 +284,11 @@ @contextlib.contextmanager def _install_apks(self): install_weblayer_shell_as_needed = _maybe_install_user_apk( - self._device, self.options.weblayer_shell, self.WEBLAYER_SHELL_PKG) + self._devices, self.options.weblayer_shell, self.WEBLAYER_SHELL_PKG) install_weblayer_support_as_needed = _maybe_install_user_apk( - self._device, self.options.weblayer_support, self.WEBLAYER_SUPPORT_PKG) + self._devices, self.options.weblayer_support, self.WEBLAYER_SUPPORT_PKG) install_webview_provider_as_needed = _maybe_install_webview_provider( - self._device, self.options.webview_provider) + self._devices, self.options.webview_provider) with install_weblayer_shell_as_needed, \ install_weblayer_support_as_needed, \ @@ -314,8 +317,8 @@ class WPTWebviewAdapter(WPTAndroidAdapter): - def __init__(self, device): - super(WPTWebviewAdapter, self).__init__(device) + def __init__(self, devices): + super(WPTWebviewAdapter, self).__init__(devices) if self.options.system_webview_shell is not None: self.system_webview_shell_pkg = apk_helper.GetPackageName( self.options.system_webview_shell) @@ -325,10 +328,10 @@ @contextlib.contextmanager def _install_apks(self): install_shell_as_needed = _maybe_install_user_apk( - self._device, self.options.system_webview_shell, + self._devices, self.options.system_webview_shell, self.system_webview_shell_pkg) install_webview_provider_as_needed = _maybe_install_webview_provider( - self._device, self.options.webview_provider) + self._devices, self.options.webview_provider) with install_shell_as_needed, install_webview_provider_as_needed: yield @@ -358,7 +361,7 @@ @contextlib.contextmanager def _install_apks(self): install_clank_as_needed = _maybe_install_user_apk( - self._device, self.options.chrome_apk) + self._devices, self.options.chrome_apk) with install_clank_as_needed: yield @@ -404,40 +407,59 @@ action='store_true', default=False, help='Enable graphical window display on the emulator.') - + parser.add_argument( + '-j', '--processes', dest='processes', + type=int, + default=1, + help='Number of emulator to run.') @contextlib.contextmanager def get_device(args): - instance = None + with get_devices(args) as devices: + yield None if not devices else devices[0] + +@contextlib.contextmanager +def get_devices(args): + instances = [] try: if args.avd_config: avd_config = avd.AvdConfig(args.avd_config) logger.warning('Install emulator from ' + args.avd_config) avd_config.Install() - instance = avd_config.CreateInstance() - instance.Start(writable_system=True, window=args.emulator_window) - device_utils.DeviceUtils(instance.serial).WaitUntilFullyBooted() + for _ in range(max(args.processes, 1)): + instance = avd_config.CreateInstance() + instance.Start(writable_system=True, window=args.emulator_window) + device_utils.DeviceUtils(instance.serial).WaitUntilFullyBooted() + instances.append(instance) #TODO(weizhong): when choose device, make sure abi matches with target devices = device_utils.DeviceUtils.HealthyDevices() if devices: - yield devices[0] + yield devices else: yield finally: - if instance: + for instance in instances: instance.Stop() -def _maybe_install_webview_provider(device, apk): +def _maybe_install_webview_provider(devices, apk): if apk: logger.info('Will install WebView apk at ' + apk) - return webview_app.UseWebViewProvider(device, apk) + + @contextlib.contextmanager + def use_webview_provider(devices, apk): + with contextlib.ExitStack() as stack: + for device in devices: + stack.enter_context(webview_app.UseWebViewProvider(device, apk)) + yield + + return use_webview_provider(devices, apk) else: return _no_op() -def _maybe_install_user_apk(device, apk, expected_pkg=None): +def _maybe_install_user_apk(devices, apk, expected_pkg=None): """contextmanager to install apk on device. Args: @@ -453,7 +475,7 @@ if expected_pkg and pkg != expected_pkg: raise ValueError('{} has incorrect package name: {}, expected {}.'.format( apk, pkg, expected_pkg)) - install_as_needed = _app_installed(device, apk, pkg) + install_as_needed = _app_installed(devices, apk, pkg) logger.info('Will install ' + pkg + ' at ' + apk) else: install_as_needed = _no_op() @@ -461,12 +483,14 @@ @contextlib.contextmanager -def _app_installed(device, apk, pkg): - device.Install(apk) +def _app_installed(devices, apk, pkg): + for device in devices: + device.Install(apk) try: yield finally: - device.Uninstall(pkg) + for device in devices: + device.Uninstall(pkg) # Dummy contextmanager to simplify multiple optional managers.
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json index 90a9f0e..6daecd1 100644 --- a/testing/variations/fieldtrial_testing_config.json +++ b/testing/variations/fieldtrial_testing_config.json
@@ -2103,6 +2103,21 @@ ] } ], + "ChromeOSWallpaperMigration": [ + { + "platforms": [ + "chromeos" + ], + "experiments": [ + { + "name": "Enabled", + "enable_features": [ + "WallpaperWebUI" + ] + } + ] + } + ], "ChromeShareLongScreenshot": [ { "platforms": [
diff --git a/third_party/blink/public/platform/web_runtime_features.h b/third_party/blink/public/platform/web_runtime_features.h index d0d5a12..e12df24 100644 --- a/third_party/blink/public/platform/web_runtime_features.h +++ b/third_party/blink/public/platform/web_runtime_features.h
@@ -171,10 +171,12 @@ BLINK_PLATFORM_EXPORT static void EnableWebAppManifestId(bool); BLINK_PLATFORM_EXPORT static void EnableWebAuth(bool); BLINK_PLATFORM_EXPORT static void EnableWebBluetooth(bool); - BLINK_PLATFORM_EXPORT static void - EnableWebBluetoothRemoteCharacteristicNewWriteValue(bool); + BLINK_PLATFORM_EXPORT static void EnableWebBluetoothGetDevices(bool); BLINK_PLATFORM_EXPORT static void EnableWebBluetoothManufacturerDataFilter( bool); + BLINK_PLATFORM_EXPORT static void + EnableWebBluetoothRemoteCharacteristicNewWriteValue(bool); + BLINK_PLATFORM_EXPORT static void EnableWebBluetoothWatchAdvertisements(bool); BLINK_PLATFORM_EXPORT static void EnableWebGLDeveloperExtensions(bool); BLINK_PLATFORM_EXPORT static void EnableWebGLDraftExtensions(bool); BLINK_PLATFORM_EXPORT static void EnableWebGLImageChromium(bool);
diff --git a/third_party/blink/renderer/core/inspector/inspector_page_agent.cc b/third_party/blink/renderer/core/inspector/inspector_page_agent.cc index fefedfbef..aa46f97 100644 --- a/third_party/blink/renderer/core/inspector/inspector_page_agent.cc +++ b/third_party/blink/renderer/core/inspector/inspector_page_agent.cc
@@ -1361,18 +1361,20 @@ std::unique_ptr<protocol::Page::Frame> InspectorPageAgent::BuildObjectForFrame( LocalFrame* frame) { DocumentLoader* loader = frame->Loader().GetDocumentLoader(); + // There are some rare cases where no DocumentLoader is set. We use an empty + // Url and MimeType in those cases. See e.g. https://crbug.com/1270184. + const KURL url = loader ? loader->Url() : KURL(); + const String mime_type = loader ? loader->MimeType() : String(); std::unique_ptr<protocol::Page::Frame> frame_object = protocol::Page::Frame::create() .setId(IdentifiersFactory::FrameId(frame)) .setLoaderId(IdentifiersFactory::LoaderId(loader)) - .setUrl(UrlWithoutFragment(loader->Url()).GetString()) + .setUrl(UrlWithoutFragment(url).GetString()) .setDomainAndRegistry(blink::network_utils::GetDomainAndRegistry( - loader->Url().Host(), - blink::network_utils::PrivateRegistryFilter:: - kIncludePrivateRegistries)) - .setMimeType(frame->Loader().GetDocumentLoader()->MimeType()) - .setSecurityOrigin( - SecurityOrigin::Create(loader->Url())->ToRawString()) + url.Host(), blink::network_utils::PrivateRegistryFilter:: + kIncludePrivateRegistries)) + .setMimeType(mime_type) + .setSecurityOrigin(SecurityOrigin::Create(url)->ToRawString()) .setSecureContextType(CreateProtocolSecureContextType( frame->DomWindow() ->GetSecurityContext() @@ -1381,8 +1383,8 @@ CreateProtocolCrossOriginIsolatedContextType(frame->DomWindow())) .setGatedAPIFeatures(CreateGatedAPIFeaturesArray(frame->DomWindow())) .build(); - if (loader->Url().HasFragmentIdentifier()) - frame_object->setUrlFragment("#" + loader->Url().FragmentIdentifier()); + if (url.HasFragmentIdentifier()) + frame_object->setUrlFragment("#" + url.FragmentIdentifier()); Frame* parent_frame = frame->Tree().Parent(); if (parent_frame) { frame_object->setParentId(IdentifiersFactory::FrameId(parent_frame));
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item.cc index 46e72ad..1ff328f3 100644 --- a/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item.cc +++ b/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item.cc
@@ -651,8 +651,7 @@ AffineTransform transform; transform.Rotate(svg_data.angle); - const SimpleFontData* font_data = - To<LayoutSVGInlineText>(GetLayoutObject())->ScaledFont().PrimaryFont(); + const SimpleFontData* font_data = ScaledFont().PrimaryFont(); // https://svgwg.org/svg2-draft/text.html#TextpathLayoutRules // The rotation should be about the center of the baseline. @@ -691,8 +690,7 @@ return BuildSvgTransformForTextPath(AffineTransform()); transform.Rotate(svg_data.angle); - const SimpleFontData* font_data = - To<LayoutSVGInlineText>(GetLayoutObject())->ScaledFont().PrimaryFont(); + const SimpleFontData* font_data = ScaledFont().PrimaryFont(); // https://svgwg.org/svg2-draft/text.html#TextElementRotateAttribute // > The supplemental rotation, in degrees, about the current text position //
diff --git a/third_party/blink/renderer/core/layout/ng/svg/ng_svg_text_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/svg/ng_svg_text_layout_algorithm.cc index 13ec466..ea76046 100644 --- a/third_party/blink/renderer/core/layout/ng/svg/ng_svg_text_layout_algorithm.cc +++ b/third_party/blink/renderer/core/layout/ng/svg/ng_svg_text_layout_algorithm.cc
@@ -137,9 +137,7 @@ const NGFragmentItem& item = *items[info.item_index]; const LogicalOffset logical_offset = items[info.item_index].offset; LayoutUnit ascent; - if (const auto* font_data = To<LayoutSVGInlineText>(item.GetLayoutObject()) - ->ScaledFont() - .PrimaryFont()) { + if (const auto* font_data = item.ScaledFont().PrimaryFont()) { ascent = font_data->GetFontMetrics().FixedAscent( item.Style().GetFontBaseline()); }
diff --git a/third_party/blink/renderer/core/paint/ng/ng_text_fragment_painter.cc b/third_party/blink/renderer/core/paint/ng/ng_text_fragment_painter.cc index 1d09a6c..8ff788f 100644 --- a/third_party/blink/renderer/core/paint/ng/ng_text_fragment_painter.cc +++ b/third_party/blink/renderer/core/paint/ng/ng_text_fragment_painter.cc
@@ -299,13 +299,10 @@ } // Set our font. - const Font& font = UNLIKELY(svg_inline_text) - ? svg_inline_text->ScaledFont() - : UNLIKELY(text_combine) - ? text_combine->UsesCompressedFont() - ? text_combine->CompressedFont() - : style.GetFont() - : style.GetFont(); + const Font& font = + UNLIKELY(text_combine && text_combine->UsesCompressedFont()) + ? text_combine->CompressedFont() + : text_item.ScaledFont(); const SimpleFontData* font_data = font.PrimaryFont(); DCHECK(font_data);
diff --git a/third_party/blink/renderer/extensions/chromeos/OWNERS b/third_party/blink/renderer/extensions/chromeos/OWNERS new file mode 100644 index 0000000..3ef627b6 --- /dev/null +++ b/third_party/blink/renderer/extensions/chromeos/OWNERS
@@ -0,0 +1,2 @@ +glenrob@chromium.org +ortuno@chromium.org
diff --git a/third_party/blink/renderer/modules/accessibility/inspector_accessibility_agent.cc b/third_party/blink/renderer/modules/accessibility/inspector_accessibility_agent.cc index a173053..f85b8c8 100644 --- a/third_party/blink/renderer/modules/accessibility/inspector_accessibility_agent.cc +++ b/third_party/blink/renderer/modules/accessibility/inspector_accessibility_agent.cc
@@ -654,6 +654,7 @@ if (parent) { protocol_node->setParentId(String::Number(parent->AXObjectID())); } else { + DCHECK(ax_object.GetDocument() && ax_object.GetDocument()->GetFrame()); auto& frame_token = ax_object.GetDocument()->GetFrame()->GetDevToolsFrameToken(); protocol_node->setFrameId(IdentifiersFactory::IdFromToken(frame_token)); @@ -918,7 +919,7 @@ AXID ax_id = in_id.ToUInt(); AXObject* ax_object = cache.ObjectFromAXID(ax_id); - if (!ax_object) + if (!ax_object || ax_object->IsDetached()) return Response::InvalidParams("Invalid ID"); *out_nodes = @@ -971,6 +972,9 @@ while (!reachable.IsEmpty()) { AXObject* descendant = reachable.back(); reachable.pop_back(); + if (descendant->IsDetached()) + continue; + // If the node is ignored or has no corresponding DOM node, we include // another layer of children. if (follow_ignored && @@ -1059,8 +1063,6 @@ } void InspectorAccessibilityAgent::RefreshFrontendNodes() { - if (!enabled_.Get()) - return; auto nodes = std::make_unique<protocol::Array<protocol::Accessibility::AXNode>>(); for (AXObject* changed_node : dirty_nodes_) { @@ -1075,6 +1077,8 @@ void InspectorAccessibilityAgent::AXEventFired(AXObject* ax_object, ax::mojom::blink::Event event) { + if (!enabled_.Get()) + return; DCHECK(ax_object->AccessibilityIsIncludedInTree()); switch (event) { case ax::mojom::blink::Event::kLoadComplete: @@ -1088,6 +1092,7 @@ break; default: AXObjectModified(ax_object, false); + RefreshFrontendNodes(); break; } } @@ -1100,6 +1105,8 @@ void InspectorAccessibilityAgent::AXObjectModified(AXObject* ax_object, bool subtree) { + if (!enabled_.Get()) + return; DCHECK(ax_object->AccessibilityIsIncludedInTree()); if (subtree) { HeapVector<Member<AXObject>> reachable; @@ -1117,7 +1124,6 @@ } else { MarkAXObjectDirty(ax_object); } - RefreshFrontendNodes(); } void InspectorAccessibilityAgent::EnableAndReset() {
diff --git a/third_party/blink/renderer/modules/mediastream/OWNERS b/third_party/blink/renderer/modules/mediastream/OWNERS index ac33cb15..b3fbc817 100644 --- a/third_party/blink/renderer/modules/mediastream/OWNERS +++ b/third_party/blink/renderer/modules/mediastream/OWNERS
@@ -3,7 +3,7 @@ hbos@chromium.org tommi@chromium.org -per-file media_stream_audio_processor*=olka@chromium.org +per-file *audio*=olka@chromium.org per-file low_latency_video*=kron@chromium.org per-file webmediaplayer_ms*=kron@chromium.org per-file video_renderer_algorithm*=kron@chromium.org
diff --git a/third_party/blink/renderer/modules/mediastream/media_stream_constraints_util_video_device.cc b/third_party/blink/renderer/modules/mediastream/media_stream_constraints_util_video_device.cc index f14f5ea2..53f3639 100644 --- a/third_party/blink/renderer/modules/mediastream/media_stream_constraints_util_video_device.cc +++ b/third_party/blink/renderer/modules/mediastream/media_stream_constraints_util_video_device.cc
@@ -10,8 +10,13 @@ #include <utility> #include "base/containers/contains.h" +#include "base/feature_list.h" +#include "base/strings/stringprintf.h" #include "media/base/limits.h" +#include "media/base/video_types.h" #include "media/mojo/mojom/display_media_information.mojom-blink.h" +#include "media/webrtc/webrtc_features.h" +#include "third_party/blink/public/platform/modules/webrtc/webrtc_logging.h" #include "third_party/blink/public/platform/web_string.h" #include "third_party/blink/public/web/modules/mediastream/media_stream_video_source.h" #include "third_party/blink/renderer/modules/mediastream/media_stream_constraints_util.h" @@ -732,6 +737,41 @@ VideoDeviceCaptureCapabilities& VideoDeviceCaptureCapabilities::operator=( VideoDeviceCaptureCapabilities&& other) = default; +// Enables debug logging of capabilities processing when picking a video. +// TODO(crbug.com/1275617): Remove this and calls once investigation is +// complete. +const base::Feature kMediaStreamCapabilitiesDebugLogging{ + "MediaStreamCapabilitiesDebugLogging", base::FEATURE_DISABLED_BY_DEFAULT}; + +// TODO(crbug.com/1275617): Remove this and calls once investigation is +// complete. +void MaybeLogDebugInfo(const std::string& message) { + if (base::FeatureList::IsEnabled(kMediaStreamCapabilitiesDebugLogging)) { + blink::WebRtcLogMessage("SelectSettingsVideoDeviceCapture(): " + message); + } +} + +// TODO(crbug.com/1275617): Remove this and calls once investigation is +// complete. +void MaybeLogDeviceCapabilities( + const Vector<VideoInputDeviceCapabilities>& device_capabilities) { + if (base::FeatureList::IsEnabled(kMediaStreamCapabilitiesDebugLogging)) { + std::string devices_string; + for (auto& device : device_capabilities) { + std::string formats_string; + for (auto& format : device.formats) { + formats_string += media::VideoCaptureFormat::ToString(format); + } + devices_string += base::StringPrintf( + "{device_id:%s, formats:[%s], facing_mode:%s},", + device.device_id.Utf8().c_str(), formats_string.c_str(), + ToWebString(device.facing_mode).Utf8().c_str()); + } + MaybeLogDebugInfo( + base::StringPrintf("Received devices %s", devices_string.c_str())); + } +} + VideoCaptureSettings SelectSettingsVideoDeviceCapture( const VideoDeviceCaptureCapabilities& capabilities, const MediaConstraints& constraints, @@ -744,6 +784,11 @@ // This function works only if infinity is defined for the double type. static_assert(std::numeric_limits<double>::has_infinity, "Requires infinity"); + // TODO(crbug.com/1275617): Remove once investigation is complete. + MaybeLogDebugInfo(base::StringPrintf("Media constraints %s", + constraints.ToString().Utf8().c_str())); + MaybeLogDeviceCapabilities(capabilities.device_capabilities); + // A distance vector contains: // a) For each advanced constraint set, a 0/Infinity value indicating if the // candidate satisfies the corresponding constraint set. @@ -765,6 +810,9 @@ for (auto& device : capabilities.device_capabilities) { if (!DeviceSatisfiesConstraintSet(device, constraints.Basic(), &failed_constraint_name)) { + MaybeLogDebugInfo(base::StringPrintf( + "Device %s rejected due to constraint %s", + device.device_id.Utf8().c_str(), failed_constraint_name)); continue; } @@ -779,6 +827,11 @@ CandidateFormat candidate_format(format); if (!candidate_format.ApplyConstraintSet(constraints.Basic(), &failed_constraint_name)) { + MaybeLogDebugInfo(base::StringPrintf( + "Device %s format %s rejected due to constraint %s", + device.device_id.Utf8().c_str(), + media::VideoCaptureFormat::ToString(format).c_str(), + failed_constraint_name)); continue; } @@ -853,9 +906,15 @@ } } - if (!result.HasValue()) + if (!result.HasValue()) { + MaybeLogDebugInfo(base::StringPrintf( + "No matching devices. Returning with failed constraint name %s", + failed_constraint_name)); return VideoCaptureSettings(failed_constraint_name); + } + MaybeLogDebugInfo(base::StringPrintf("Returning best matching result %s", + failed_constraint_name)); return result; }
diff --git a/third_party/blink/renderer/modules/mediastream/user_media_processor.cc b/third_party/blink/renderer/modules/mediastream/user_media_processor.cc index c807b277..acb95e0 100644 --- a/third_party/blink/renderer/modules/mediastream/user_media_processor.cc +++ b/third_party/blink/renderer/modules/mediastream/user_media_processor.cc
@@ -898,6 +898,7 @@ if (!settings.HasValue()) { String failed_constraint_name = String(settings.failed_constraint_name()); DCHECK(!failed_constraint_name.IsEmpty()); + GetUserMediaRequestFailed( MediaStreamRequestResult::CONSTRAINT_NOT_SATISFIED, failed_constraint_name);
diff --git a/third_party/blink/renderer/modules/webcodecs/video_encoder.cc b/third_party/blink/renderer/modules/webcodecs/video_encoder.cc index 74612a6..42485c9 100644 --- a/third_party/blink/renderer/modules/webcodecs/video_encoder.cc +++ b/third_party/blink/renderer/modules/webcodecs/video_encoder.cc
@@ -653,11 +653,18 @@ // HasPendingActivity() keeps the VideoEncoder alive long enough. auto blit_done_callback = [](VideoEncoder* self, bool keyframe, uint32_t reset_count, + base::TimeDelta timestamp, + media::VideoFrameMetadata metadata, media::VideoEncoder::StatusCB done_callback, scoped_refptr<media::VideoFrame> frame) { if (!self || self->reset_count_ != reset_count || !frame) return; + // CopyRGBATextureToVideoFrame() operates on mailboxes and not frames, + // so we must manually copy over properties relevant to the encoder. + frame->set_timestamp(timestamp); + frame->set_metadata(metadata); + DCHECK_CALLED_ON_VALID_SEQUENCE(self->sequence_checker_); --self->requested_encodes_; self->blocking_request_in_progress_ = false; @@ -697,7 +704,7 @@ format, frame->coded_size(), frame->ColorSpace(), origin, frame->mailbox_holder(0), dst_color_space, WTF::Bind(blit_done_callback, WrapWeakPersistent(this), keyframe, - reset_count_, + reset_count_, frame->timestamp(), frame->metadata(), ConvertToBaseOnceCallback(CrossThreadBindOnce( done_callback, WrapCrossThreadWeakPersistent(this), WrapCrossThreadPersistent(request)))))) {
diff --git a/third_party/blink/renderer/platform/exported/web_runtime_features.cc b/third_party/blink/renderer/platform/exported/web_runtime_features.cc index a562f5ad..38b38f224 100644 --- a/third_party/blink/renderer/platform/exported/web_runtime_features.cc +++ b/third_party/blink/renderer/platform/exported/web_runtime_features.cc
@@ -65,14 +65,22 @@ RuntimeEnabledFeatures::SetWebBluetoothEnabled(enable); } +void WebRuntimeFeatures::EnableWebBluetoothGetDevices(bool enable) { + RuntimeEnabledFeatures::SetWebBluetoothGetDevicesEnabled(enable); +} + +void WebRuntimeFeatures::EnableWebBluetoothManufacturerDataFilter(bool enable) { + RuntimeEnabledFeatures::SetWebBluetoothManufacturerDataFilterEnabled(enable); +} + void WebRuntimeFeatures::EnableWebBluetoothRemoteCharacteristicNewWriteValue( bool enable) { RuntimeEnabledFeatures:: SetWebBluetoothRemoteCharacteristicNewWriteValueEnabled(enable); } -void WebRuntimeFeatures::EnableWebBluetoothManufacturerDataFilter(bool enable) { - RuntimeEnabledFeatures::SetWebBluetoothManufacturerDataFilterEnabled(enable); +void WebRuntimeFeatures::EnableWebBluetoothWatchAdvertisements(bool enable) { + RuntimeEnabledFeatures::SetWebBluetoothWatchAdvertisementsEnabled(enable); } void WebRuntimeFeatures::EnableCompositeBGColorAnimation(bool enable) {
diff --git a/third_party/blink/renderer/platform/webrtc/webrtc_video_frame_adapter.cc b/third_party/blink/renderer/platform/webrtc/webrtc_video_frame_adapter.cc index 6440c03..a8009070 100644 --- a/third_party/blink/renderer/platform/webrtc/webrtc_video_frame_adapter.cc +++ b/third_party/blink/renderer/platform/webrtc/webrtc_video_frame_adapter.cc
@@ -265,6 +265,11 @@ return nullptr; } + // CopyRGBATextureToVideoFrame() operates on mailboxes and not frames, so we + // must manually copy over properties relevant to the encoder. + dst_frame->set_timestamp(source_frame->timestamp()); + dst_frame->set_metadata(source_frame->metadata()); + // TODO(crbug.com/1224279): We should remove this wait by internalizing // it into VideoFrame::Map(). raster_context_provider->RasterInterface()->Finish();
diff --git a/third_party/blink/tools/blinkpy/web_tests/lint_test_expectations.py b/third_party/blink/tools/blinkpy/web_tests/lint_test_expectations.py index 08b3bd1..d458c49 100644 --- a/third_party/blink/tools/blinkpy/web_tests/lint_test_expectations.py +++ b/third_party/blink/tools/blinkpy/web_tests/lint_test_expectations.py
@@ -315,7 +315,7 @@ failures, warnings = _check_test_existence( host, port, path, expectations) failures.extend(_check_directory_glob(host, port, path, expectations)) - warnings.extend(_check_not_slow_and_timeout(host, port, path, expectations)) + failures.extend(_check_not_slow_and_timeout(host, port, path, expectations)) failures.extend(_check_never_fix_tests(host, port, path, expectations)) if path in PRODUCTS_TO_EXPECTATION_FILE_PATHS.values(): failures.extend(_check_non_wpt_in_android_override(
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations index fb52742..e731149 100644 --- a/third_party/blink/web_tests/TestExpectations +++ b/third_party/blink/web_tests/TestExpectations
@@ -2987,7 +2987,6 @@ crbug.com/626703 [ Mac10.12 ] external/wpt/websockets/constructor/009.html?wpt_flags=h2 [ Crash Failure ] crbug.com/626703 [ Mac10.13 ] external/wpt/scroll-to-text-fragment/scroll-to-text-fragment-security.sub.html [ Timeout ] crbug.com/626703 [ Mac10.13 ] virtual/threaded/external/wpt/css/css-backgrounds/animations/background-color-animation-in-body.html [ Failure ] -crbug.com/626703 [ Mac10.14 ] virtual/fenced-frame-mparch/wpt_internal/fenced_frame/pointer-lock.https.html [ Timeout ] crbug.com/626703 [ Mac11 ] virtual/threaded/external/wpt/css/css-backgrounds/background-image-centered.html [ Failure ] crbug.com/626703 [ Mac10.13 ] external/wpt/css/selectors/invalidation/has-in-ancestor-position.html [ Failure Timeout ] crbug.com/626703 external/wpt/resource-timing/entries-for-network-errors.sub.https.html [ Timeout ] @@ -7118,6 +7117,10 @@ # Data URL navigation within Fenced Frames currently crashed in the MPArch implementation. crbug.com/1243568 virtual/fenced-frame-mparch/wpt_internal/fenced_frame/window-data-url-navigation.html [ Crash ] +# Gesture of WPT Test Driver doesn't work well in MPArch Fenced Frame on some Mac try bots. +crbug.com/1275997 [ Mac ] virtual/fenced-frame-mparch/wpt_internal/fenced_frame/pointer-lock.https.html [ Timeout Pass ] +crbug.com/1275997 [ Mac ] virtual/fenced-frame-mparch/wpt_internal/fenced_frame/web-bluetooth.https.html [ Timeout Pass ] + # Sheriff 2021-10-15 crbug.com/1256763 [ Linux ] virtual/gpu-rasterization/images/color-profile-image-pseudo-content.html [ Failure Pass ] crbug.com/1260393 [ Mac ] virtual/oopr-canvas2d/fast/canvas/color-space/canvas-createImageBitmap-p3.html [ Failure Pass ]
diff --git a/third_party/blink/web_tests/wpt_internal/fenced_frame/resources/utils.js b/third_party/blink/web_tests/wpt_internal/fenced_frame/resources/utils.js index 422d175..f4ed78d 100644 --- a/third_party/blink/web_tests/wpt_internal/fenced_frame/resources/utils.js +++ b/third_party/blink/web_tests/wpt_internal/fenced_frame/resources/utils.js
@@ -79,6 +79,8 @@ "referrer.value ACK" : "00000000-0000-0000-0000-000000000028", "bluetooth.requestDevice" : "00000000-0000-0000-0000-000000000029", + + "usb.requestDevice" : "00000000-0000-0000-0000-00000000002A", // Add keys above this list, incrementing the key UUID in hexadecimal } @@ -129,3 +131,25 @@ const serverUrl = `${origin}${STORE_URL}?key=${key}&value=${value}`; await fetch(serverUrl, {"mode": "no-cors"}); } + +// Simulates a user gesture and calls `callback` when `mouseup` happens. +function simulateGesture(callback) { + // Get or create the target element. + let target = document.getElementById('target'); + if (!target) { + target = document.createElement('button'); + target.textContent = '\u2573'; + target.id = 'target'; + document.body.appendChild(target); + } + target.addEventListener('mouseup', callback); + + requestAnimationFrame(() => { + if (eventSender) { + eventSender.mouseMoveTo(target.getBoundingClientRect().x, + target.getBoundingClientRect().y); + eventSender.mouseDown(); + eventSender.mouseUp(); + } + }); +}
diff --git a/third_party/blink/web_tests/wpt_internal/fenced_frame/resources/web-usb-inner.https.html b/third_party/blink/web_tests/wpt_internal/fenced_frame/resources/web-usb-inner.https.html new file mode 100644 index 0000000..64cd14e4 --- /dev/null +++ b/third_party/blink/web_tests/wpt_internal/fenced_frame/resources/web-usb-inner.https.html
@@ -0,0 +1,27 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-vendor.js"></script> +<script src="utils.js"></script> +<title>Fenced frame content to report the result of navigator.usb.requestDevice</title> + +<body> +<script> +window.onload = () => { + requestAnimationFrame(() => { + simulateGesture(async function() { + // This file is meant to be navigated to from a <fencedframe> element. It + // reports back to the page hosting the <fencedframe> whether or not + // `navigator.usb.requestDevice` is allowed. + const usb_request_device_key = KEYS['usb.requestDevice']; + try { + await navigator.usb.requestDevice({ filters: [{ vendorId: 0 }] }); + writeValueToServer(usb_request_device_key, 'permission granted'); + } catch (e) { + writeValueToServer(usb_request_device_key, `${e.name}: ${e.message}`); + } + }); + }); +}; +</script> +</body>
diff --git a/third_party/blink/web_tests/wpt_internal/fenced_frame/resources/web-usb-inner.https.html.headers b/third_party/blink/web_tests/wpt_internal/fenced_frame/resources/web-usb-inner.https.html.headers new file mode 100644 index 0000000..6247f6d --- /dev/null +++ b/third_party/blink/web_tests/wpt_internal/fenced_frame/resources/web-usb-inner.https.html.headers
@@ -0,0 +1 @@ +Supports-Loading-Mode: fenced-frame \ No newline at end of file
diff --git a/third_party/blink/web_tests/wpt_internal/fenced_frame/web-usb.https.html b/third_party/blink/web_tests/wpt_internal/fenced_frame/web-usb.https.html new file mode 100644 index 0000000..bc5d258 --- /dev/null +++ b/third_party/blink/web_tests/wpt_internal/fenced_frame/web-usb.https.html
@@ -0,0 +1,26 @@ +<!DOCTYPE html> +<title>Test WebUSB navigator.usb.requestDevice()</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/common/utils.js"></script> +<script src="resources/utils.js"></script> + +<body> +<script> +promise_test(async () => { + const usb_request_device_key = KEYS['usb.requestDevice']; + + attachFencedFrame('resources/web-usb-inner.https.html'); + // Get the result for the top-level fenced frame. + const fenced_frame_result = await nextValueFromServer(usb_request_device_key); + // It fails to request a USB device in a fenced frame. Refer to + // https://github.com/shivanigithub/fenced-frame#security-considerations. + assert_equals( + fenced_frame_result, + 'SecurityError: Failed to execute \'requestDevice\' on \'USB\': '+ + 'Access to the feature "usb" is disallowed by permissions policy.', + 'fenced frame has the right value' + + ' for usb.requestDevice'); +}, 'navigator.usb.requestDevice'); +</script> +</body>
diff --git a/third_party/fusejs/dist/fuse.d.ts b/third_party/fusejs/dist/fuse.d.ts new file mode 100644 index 0000000..43c13c7 --- /dev/null +++ b/third_party/fusejs/dist/fuse.d.ts
@@ -0,0 +1,293 @@ +// Type definitions for Fuse.js v6.4.1 +// TypeScript v3.9.5 + +export default Fuse +export as namespace Fuse + +declare class Fuse<T> { + constructor( + list: ReadonlyArray<T>, + options?: Fuse.IFuseOptions<T>, + index?: FuseIndex<T> + ) + /** + * Search function for the Fuse instance. + * + * ```typescript + * const list: MyType[] = [myType1, myType2, etc...] + + * const options: Fuse.IFuseOptions<MyType> = { + * keys: ['key1', 'key2'] + * } + * + * const myFuse = new Fuse(list, options) + * let result = myFuse.search('pattern') + * ``` + * + * @param pattern The pattern to search + * @param options `Fuse.FuseSearchOptions` + * @returns An array of search results + */ + search<R = T>( + pattern: string | Fuse.Expression, + options?: Fuse.FuseSearchOptions + ): Fuse.FuseResult<R>[] + + setCollection(docs: ReadonlyArray<T>, index?: FuseIndex<T>): void + + /** + * Adds a doc to the end the list. + */ + add(doc: T): void + + /** + * Removes all documents from the list which the predicate returns truthy for, + * and returns an array of the removed docs. + * The predicate is invoked with two arguments: (doc, index). + */ + remove(predicate: (doc: T, idx: number) => boolean): T[] + + /** + * Removes the doc at the specified index. + */ + removeAt(idx: number): void + + /** + * Returns the generated Fuse index + */ + getIndex(): FuseIndex<T> + + /** + * Return the current version. + */ + static version: string + + /** + * Use this method to pre-generate the index from the list, and pass it + * directly into the Fuse instance. + * + * _Note that Fuse will automatically index the table if one isn't provided + * during instantiation._ + * + * ```typescript + * const list: MyType[] = [myType1, myType2, etc...] + * + * const index = Fuse.createIndex<MyType>( + * keys: ['key1', 'key2'] + * list: list + * ) + * + * const options: Fuse.IFuseOptions<MyType> = { + * keys: ['key1', 'key2'] + * } + * + * const myFuse = new Fuse(list, options, index) + * ``` + * @param keys The keys to index + * @param list The list from which to create an index + * @param options? + * @returns An indexed list + */ + static createIndex<U>( + keys: Array<Fuse.FuseOptionKey>, + list: ReadonlyArray<U>, + options?: Fuse.FuseIndexOptions<U> + ): FuseIndex<U> + + static parseIndex<U>( + index: any, + options?: Fuse.FuseIndexOptions<U> + ): FuseIndex<U> +} + +declare class FuseIndex<T> { + constructor(options?: Fuse.FuseIndexOptions<T>) + setSources(docs: ReadonlyArray<T>): void + setKeys(keys: ReadonlyArray<string>): void + setIndexRecords(records: Fuse.FuseIndexRecords): void + create(): void + add(doc: T): void + toJSON(): { + keys: ReadonlyArray<string> + collection: Fuse.FuseIndexRecords + } +} + +declare namespace Fuse { + type FuseGetFunction<T> = ( + obj: T, + path: string | string[] + ) => ReadonlyArray<string> | string + + export type FuseIndexOptions<T> = { + getFn: FuseGetFunction<T> + } + + // { + // title: { '$': "Old Man's War" }, + // 'author.firstName': { '$': 'Codenar' } + // } + // + // OR + // + // { + // tags: [ + // { $: 'nonfiction', idx: 0 }, + // { $: 'web development', idx: 1 }, + // ] + // } + export type FuseSortFunctionItem = { + [key: string]: { $: string } | { $: string; idx: number }[] + } + + // { + // score: 0.001, + // key: 'author.firstName', + // value: 'Codenar', + // indices: [ [ 0, 3 ] ] + // } + export type FuseSortFunctionMatch = { + score: number + key: string + value: string + indices: ReadonlyArray<number>[] + } + + // { + // score: 0, + // key: 'tags', + // value: 'nonfiction', + // idx: 1, + // indices: [ [ 0, 9 ] ] + // } + export type FuseSortFunctionMatchList = FuseSortFunctionMatch & { + idx: number + } + + export type FuseSortFunctionArg = { + idx: number + item: FuseSortFunctionItem + score: number + matches?: (FuseSortFunctionMatch | FuseSortFunctionMatchList)[] + } + + export type FuseSortFunction = ( + a: FuseSortFunctionArg, + b: FuseSortFunctionArg + ) => number + + // title: { + // '$': "Old Man's War", + // 'n': 0.5773502691896258 + // } + type RecordEntryObject = { + v: string // The text value + n: number // The field-length norm + } + + // 'author.tags.name': [{ + // 'v': 'pizza lover', + // 'i': 2, + // 'n: 0.7071067811865475 + // } + type RecordEntryArrayItem = ReadonlyArray<RecordEntryObject & { i: number }> + + // TODO: this makes it difficult to infer the type. Need to think more about this + type RecordEntry = { [key: string]: RecordEntryObject | RecordEntryArrayItem } + + // { + // i: 0, + // '$': { + // '0': { v: "Old Man's War", n: 0.5773502691896258 }, + // '1': { v: 'Codenar', n: 1 }, + // '2': [ + // { v: 'pizza lover', i: 2, n: 0.7071067811865475 }, + // { v: 'helo wold', i: 1, n: 0.7071067811865475 }, + // { v: 'hello world', i: 0, n: 0.7071067811865475 } + // ] + // } + // } + type FuseIndexObjectRecord = { + i: number // The index of the record in the source list + $: RecordEntry + } + + // { + // keys: null, + // list: [ + // { v: 'one', i: 0, n: 1 }, + // { v: 'two', i: 1, n: 1 }, + // { v: 'three', i: 2, n: 1 } + // ] + // } + type FuseIndexStringRecord = { + i: number // The index of the record in the source list + v: string // The text value + n: number // The field-length norm + } + + type FuseIndexRecords = + | ReadonlyArray<FuseIndexObjectRecord> + | ReadonlyArray<FuseIndexStringRecord> + + // { + // name: 'title', + // weight: 0.7 + // } + export type FuseOptionKeyObject = { + name: string | string[] + weight: number + } + + export type FuseOptionKey = FuseOptionKeyObject | string | string[] + + export interface IFuseOptions<T> { + isCaseSensitive?: boolean + distance?: number + findAllMatches?: boolean + getFn?: FuseGetFunction<T> + ignoreLocation?: boolean + ignoreFieldNorm?: boolean + includeMatches?: boolean + includeScore?: boolean + keys?: Array<FuseOptionKey> + location?: number + minMatchCharLength?: number + shouldSort?: boolean + sortFn?: FuseSortFunction + threshold?: number + useExtendedSearch?: boolean + } + + // Denotes the start/end indices of a match + // start end + // ↓ ↓ + type RangeTuple = [number, number] + + export type FuseResultMatch = { + indices: ReadonlyArray<RangeTuple> + key?: string + refIndex?: number + value?: string + } + + export type FuseSearchOptions = { + limit: number + } + + export type FuseResult<T> = { + item: T + refIndex: number + score?: number + matches?: ReadonlyArray<FuseResultMatch> + } + + export type Expression = + | { [key: string]: string } + | { + $path: ReadonlyArray<string> + $val: string + } + | { $and?: Expression[] } + | { $or?: Expression[] } +}
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml index 0d8d19e..cf56095 100644 --- a/tools/metrics/histograms/enums.xml +++ b/tools/metrics/histograms/enums.xml
@@ -45059,6 +45059,21 @@ <int value="5" label="Third Party Unsafe Headers"/> </enum> +<enum name="IntentPickerDialogAction"> + <summary> + Defines actions taken by the user in the Intent Picker dialog + </summary> + <int value="0" label="Invalid"/> + <int value="1" label="Error"/> + <int value="2" label="Dialog deactivated"/> + <int value="3" label="Chrome selected"/> + <int value="4" label="Chrome selected and preferred"/> + <int value="5" label="ARC app selected"/> + <int value="6" label="ARC app selected and preferred"/> + <int value="7" label="PWA selected"/> + <int value="8" label="PWA selected and preferred"/> +</enum> + <enum name="InterceptionType"> <int value="0" label="Not intercepted"/> <int value="1" label="Other"/> @@ -51463,6 +51478,7 @@ <int value="-221649438" label="AllowTouchpadHapticClickSettings:disabled"/> <int value="-220599034" label="UsePdfCompositorServiceForPrint:enabled"/> <int value="-220298483" label="ImeOptionsInSettings:enabled"/> + <int value="-218843425" label="auto-framing-override"/> <int value="-217930860" label="EnableAutomaticSnooze:disabled"/> <int value="-217885320" label="CdmFactoryDaemon:disabled"/> <int value="-216219963" label="ash-shelf-color-scheme"/>
diff --git a/tools/metrics/histograms/metadata/chromeos/histograms.xml b/tools/metrics/histograms/metadata/chromeos/histograms.xml index 866a87ed..9bbd0104 100644 --- a/tools/metrics/histograms/metadata/chromeos/histograms.xml +++ b/tools/metrics/histograms/metadata/chromeos/histograms.xml
@@ -171,6 +171,9 @@ <histogram name="ChromeOS.Apps.IntentPickerAction" enum="ArcIntentHandlerAction" expires_after="2022-05-21"> + <obsolete> + Replaced by ChromeOS.Intents.IntentPickerAction in M98. + </obsolete> <owner>elijahtaylor@google.com</owner> <owner>dominickn@chromium.org</owner> <owner>shihuis@google.com</owner> @@ -186,10 +189,9 @@ <owner>dominickn@chromium.org</owner> <owner>shihuis@google.com</owner> <summary> - Records the app platform chosen (or Chrome if the user did not choose an - app) when the intent picker is shown to the user on Chrome OS. The - destination may be specified due to the user explicit selection or a - previously stored preference. + Records the app platform shown as the result of link intent handling. + Recorded when the user makes a choice in the Intent Picker dialog, and when + the result of a previous user preference is applied during a link click. </summary> </histogram> @@ -956,6 +958,34 @@ </token> </histogram> +<histogram name="ChromeOS.Intents.IntentPickerAction" + enum="IntentPickerDialogAction" expires_after="2022-06-01"> + <owner>tsergeant@chromium.org</owner> + <owner>chromeos-apps-foundation-team@google.com</owner> + <summary> + The intent picker dialog is shown (either automatically, or though a page + action button) when the user navigates to a page whose URL can be handled by + an installed app. This metric is recorded when the dialog is closed and + records the user action which caused it to close. + </summary> +</histogram> + +<histogram name="ChromeOS.Intents.IntentPickerAction.From{LaunchSource}" + enum="IntentPickerDialogAction" expires_after="2022-06-01"> + <owner>tsergeant@chromium.org</owner> + <owner>chromeos-apps-foundation-team@google.com</owner> + <summary> + The intent picker dialog is shown when the user navigates to a page whose + URL can be handled by an installed app. This metric is recorded when the + dialog is closed and records the user action which caused it to close. Only + recorded when the Intent Picker dialog is shown from {LaunchSource}. + </summary> + <token key="LaunchSource"> + <variant name="AutoPopOut"/> + <variant name="OmniboxIcon"/> + </token> +</histogram> + <histogram name="ChromeOS.IsLacrosBrowser" enum="Boolean" expires_after="never"> <!-- expires-never: Used to identify lacros binary in metrics backend. -->
diff --git a/tools/metrics/histograms/metadata/net/histograms.xml b/tools/metrics/histograms/metadata/net/histograms.xml index fa95b4c..12fa89b7 100644 --- a/tools/metrics/histograms/metadata/net/histograms.xml +++ b/tools/metrics/histograms/metadata/net/histograms.xml
@@ -504,11 +504,38 @@ <histogram name="Net.Cors.PreflightCheckError" enum="CorsAccessCheckError" expires_after="2022-05-01"> + <obsolete> + Superseded by Net.Cors.PreflightCheckError2 in 2021-12. + </obsolete> <owner>toyoshim@chromium.org</owner> <owner>yhirano@chromium.org</owner> <summary> The distribution of CORS error types on preflight requests. This reports - whenever CORS checks detect an error on the original network requests. + whenever CORS checks detect an error on a preflight request. + </summary> +</histogram> + +<histogram name="Net.Cors.PreflightCheckError2" enum="CorsAccessCheckError" + expires_after="2022-05-01"> + <owner>toyoshim@chromium.org</owner> + <owner>yhirano@chromium.org</owner> + <summary> + The distribution of CORS error types on preflight requests. This reports + whenever CORS checks detect an error on a preflight request. The + InvalidResponse case corresponds to net errors for which there was no CORS + error, e.g. CONNECTION_REFUSED, NX_DOMAIN, etc. + </summary> +</histogram> + +<histogram name="Net.Cors.PreflightCheckWarning" enum="CorsAccessCheckError" + expires_after="2022-05-01"> + <owner>titouan@chromium.org</owner> + <owner>clamy@chromium.org</owner> + <summary> + The distribution of CORS error types on preflight requests which are treated + as warnings. This reports whenever CORS checks detect an error on a + preflight request without failing the whole original network request, due to + being configured to ignore these failures. </summary> </histogram>
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json index f6ffa947..18c91a5 100644 --- a/tools/perf/core/perfetto_binary_roller/binary_deps.json +++ b/tools/perf/core/perfetto_binary_roller/binary_deps.json
@@ -5,20 +5,20 @@ "remote_path": "perfetto_binaries/trace_processor_shell/linux_arm/49b4b5dcbc312d8d2c3751cf29238b8efeb4e494/trace_processor_shell" }, "win": { - "hash": "ec72eaca6115386670b5c0c89a9d95cec663dffe", - "remote_path": "perfetto_binaries/trace_processor_shell/win/e9e81c01529610f02f63a87122c5bd878972ef7b/trace_processor_shell.exe" + "hash": "2c643ac4fd4700e47e8cc931af3d8d9f922f55ba", + "remote_path": "perfetto_binaries/trace_processor_shell/win/c949b5f8e83df73166dd8c9eb5edffba93e02992/trace_processor_shell.exe" }, "mac": { - "hash": "c437f7ebd7486156511b7cc4d9652296b2759143", - "remote_path": "perfetto_binaries/trace_processor_shell/mac/e9e81c01529610f02f63a87122c5bd878972ef7b/trace_processor_shell" + "hash": "c6d57066daae4390a3d60efa022bfa6286a3ffbb", + "remote_path": "perfetto_binaries/trace_processor_shell/mac/c949b5f8e83df73166dd8c9eb5edffba93e02992/trace_processor_shell" }, "linux_arm64": { "hash": "5074025a2898ec41a872e70a5719e417acb0a380", "remote_path": "perfetto_binaries/trace_processor_shell/linux_arm64/49b4b5dcbc312d8d2c3751cf29238b8efeb4e494/trace_processor_shell" }, "linux": { - "hash": "b3633b776416401a6a07a629baf4902bdd2ab169", - "remote_path": "perfetto_binaries/trace_processor_shell/linux/e9e81c01529610f02f63a87122c5bd878972ef7b/trace_processor_shell" + "hash": "644c4e0d22681534e2f74e3cae5ba145381ab0bc", + "remote_path": "perfetto_binaries/trace_processor_shell/linux/c949b5f8e83df73166dd8c9eb5edffba93e02992/trace_processor_shell" } }, "power_profile.sql": {
diff --git a/tools/style_variable_generator/colors_test_include_style_sheet_expected.ts b/tools/style_variable_generator/colors_test_include_style_sheet_expected.ts index ec9113b..39a272f 100644 --- a/tools/style_variable_generator/colors_test_include_style_sheet_expected.ts +++ b/tools/style_variable_generator/colors_test_include_style_sheet_expected.ts
@@ -11,11 +11,21 @@ /* SAFETY_BOILERPLATE */ export interface GetColorsCSSOptions { - lockTheme?: 'light'|'dark'; + /** The html selector the colors should be attached too. */ + target?: 'host' | 'html'; + /** + * Generate a css dump which sets variables to their dark mode values in light + * mode and vice versa. If true lockTheme is ignored. + */ + invert?: boolean; + /** + * Generate a css dump which sets variables to either their dark mode or light + * mode values and ignores the documents prefers-color-scheme. + */ + lockTheme?: 'light' | 'dark'; } const DEFAULT_CSS = ` -html:not(body) { --google-grey-900-rgb: 32, 33, 36; --google-grey-900: rgb(var(--google-grey-900-rgb)); @@ -31,11 +41,9 @@ --cros-disabled-opacity: 0.38; --cros-reference-opacity: var(--cros-disabled-opacity); -} `; const DARK_MODE_OVERRIDES_CSS = ` -html:not(body) { --cros-text-color-primary-rgb: 255, 255, 255; --cros-text-color-primary: rgb(var(--cros-text-color-primary-rgb)); @@ -46,9 +54,12 @@ --cros-bg-color-elevation-1: rgb(var(--cros-bg-color-elevation-1-rgb)); --cros-reference-opacity: 1; -} `; +const UNTYPED_CSS = ``; + +const TYPOGRAPHY_CSS = ``; + /** * Returns a string containing all semantic colors exported in this file as * css variables. This string an be used to construct a stylesheet which can be @@ -59,21 +70,51 @@ */ export function getColorsCSS(options?: GetColorsCSSOptions) { let cssString; - if (options?.lockTheme === 'light') { + if (options?.invert) { + cssString = /* SAFE */ (` + ${options?.target === 'host' ? ':host' : 'html:not(body)'} { + ${DEFAULT_CSS} + ${UNTYPED_CSS} + ${TYPOGRAPHY_CSS} + ${DARK_MODE_OVERRIDES_CSS} + } + + @media (prefers-color-scheme: dark) { + ${options?.target === 'host' ? ':host' : 'html:not(body)'} { + ${DEFAULT_CSS} + } + } + `); + } else if (options?.lockTheme === 'light') { // Tag strings which are safe with a special comment so copybara can add // the right safety wrappers whem moving this code into Google3. - cssString = /* SAFE */ (DEFAULT_CSS); + cssString = /* SAFE */ (` + ${options?.target === 'host' ? ':host' : 'html:not(body)'} { + ${DEFAULT_CSS} + ${UNTYPED_CSS} + ${TYPOGRAPHY_CSS} + } + `); } else if (options?.lockTheme === 'dark') { cssString = /* SAFE */ (` - ${DEFAULT_CSS} - ${DARK_MODE_OVERRIDES_CSS} + ${options?.target === 'host' ? ':host' : 'html:not(body)'} { + ${DEFAULT_CSS} + ${UNTYPED_CSS} + ${TYPOGRAPHY_CSS} + ${DARK_MODE_OVERRIDES_CSS} + } `); } else { cssString = /* SAFE */ (` - ${DEFAULT_CSS} - + ${options?.target === 'host' ? ':host' : 'html:not(body)'} { + ${DEFAULT_CSS} + ${UNTYPED_CSS} + ${TYPOGRAPHY_CSS} + } @media (prefers-color-scheme: dark) { - ${DARK_MODE_OVERRIDES_CSS} + ${options?.target === 'host' ? ':host' : 'html:not(body)'} { + ${DARK_MODE_OVERRIDES_CSS} + } } `); }
diff --git a/tools/style_variable_generator/colors_test_typography_and_untyped_css_expected.ts b/tools/style_variable_generator/colors_test_typography_and_untyped_css_expected.ts index b77dccab..aecddc4 100644 --- a/tools/style_variable_generator/colors_test_typography_and_untyped_css_expected.ts +++ b/tools/style_variable_generator/colors_test_typography_and_untyped_css_expected.ts
@@ -13,11 +13,21 @@ /* SAFETY_BOILERPLATE */ export interface GetColorsCSSOptions { - lockTheme?: 'light'|'dark'; + /** The html selector the colors should be attached too. */ + target?: 'host' | 'html'; + /** + * Generate a css dump which sets variables to their dark mode values in light + * mode and vice versa. If true lockTheme is ignored. + */ + invert?: boolean; + /** + * Generate a css dump which sets variables to either their dark mode or light + * mode values and ignores the documents prefers-color-scheme. + */ + lockTheme?: 'light' | 'dark'; } const DEFAULT_CSS = ` -html:not(body) { --google-grey-900-rgb: 32, 33, 36; --google-grey-900: rgb(var(--google-grey-900-rgb)); @@ -33,29 +43,9 @@ --cros-disabled-opacity: 0.38; --cros-reference-opacity: var(--cros-disabled-opacity); -} -html { - /* shadows */ - --cros-elevation-1-shadow: 0px 1px 2px rgba(0, 0, 0, 0.3), 0px 1px 3px rgba(0, 0, 0, 0.15); - --cros-elevation-2-shadow: 0px 1px 2px rgba(0, 0, 0, 0.3), 0px 2px 6px rgba(0, 0, 0, 0.15); - --cros-elevation-3-shadow: 0px 1px 3px rgba(0, 0, 0, 0.3), 0px 4px 8px rgba(0, 0, 0, 0.15); -} -html { - /* font families */ - --cros-font-family-test: 'Google Sans', 'Noto Sans', sans-serif; - --cros-font-family-other: Roboto, 'Noto Sans', sans-serif; - - /* typefaces */ - --cros-headline-1-font: 500 15px/22px var(--cros-font-family-test); - --cros-headline-1-font-family: var(--cros-font-family-test); - --cros-headline-1-font-size: 15px; - --cros-headline-1-font-weight: 500; - --cros-headline-1-line-height: 22px; -} `; const DARK_MODE_OVERRIDES_CSS = ` -html:not(body) { --cros-text-color-primary-rgb: 255, 255, 255; --cros-text-color-primary: rgb(var(--cros-text-color-primary-rgb)); @@ -66,7 +56,26 @@ --cros-bg-color-elevation-1: rgb(var(--cros-bg-color-elevation-1-rgb)); --cros-reference-opacity: 1; -} +`; + +const UNTYPED_CSS = ` + /* shadows */ + --cros-elevation-1-shadow: 0px 1px 2px rgba(0, 0, 0, 0.3), 0px 1px 3px rgba(0, 0, 0, 0.15); + --cros-elevation-2-shadow: 0px 1px 2px rgba(0, 0, 0, 0.3), 0px 2px 6px rgba(0, 0, 0, 0.15); + --cros-elevation-3-shadow: 0px 1px 3px rgba(0, 0, 0, 0.3), 0px 4px 8px rgba(0, 0, 0, 0.15); +`; + +const TYPOGRAPHY_CSS = ` + /* font families */ + --cros-font-family-test: 'Google Sans', 'Noto Sans', sans-serif; + --cros-font-family-other: Roboto, 'Noto Sans', sans-serif; + + /* typefaces */ + --cros-headline-1-font: 500 15px/22px var(--cros-font-family-test); + --cros-headline-1-font-family: var(--cros-font-family-test); + --cros-headline-1-font-size: 15px; + --cros-headline-1-font-weight: 500; + --cros-headline-1-line-height: 22px; `; /** @@ -79,21 +88,51 @@ */ export function getColorsCSS(options?: GetColorsCSSOptions) { let cssString; - if (options?.lockTheme === 'light') { + if (options?.invert) { + cssString = /* SAFE */ (` + ${options?.target === 'host' ? ':host' : 'html:not(body)'} { + ${DEFAULT_CSS} + ${UNTYPED_CSS} + ${TYPOGRAPHY_CSS} + ${DARK_MODE_OVERRIDES_CSS} + } + + @media (prefers-color-scheme: dark) { + ${options?.target === 'host' ? ':host' : 'html:not(body)'} { + ${DEFAULT_CSS} + } + } + `); + } else if (options?.lockTheme === 'light') { // Tag strings which are safe with a special comment so copybara can add // the right safety wrappers whem moving this code into Google3. - cssString = /* SAFE */ (DEFAULT_CSS); + cssString = /* SAFE */ (` + ${options?.target === 'host' ? ':host' : 'html:not(body)'} { + ${DEFAULT_CSS} + ${UNTYPED_CSS} + ${TYPOGRAPHY_CSS} + } + `); } else if (options?.lockTheme === 'dark') { cssString = /* SAFE */ (` - ${DEFAULT_CSS} - ${DARK_MODE_OVERRIDES_CSS} + ${options?.target === 'host' ? ':host' : 'html:not(body)'} { + ${DEFAULT_CSS} + ${UNTYPED_CSS} + ${TYPOGRAPHY_CSS} + ${DARK_MODE_OVERRIDES_CSS} + } `); } else { cssString = /* SAFE */ (` - ${DEFAULT_CSS} - + ${options?.target === 'host' ? ':host' : 'html:not(body)'} { + ${DEFAULT_CSS} + ${UNTYPED_CSS} + ${TYPOGRAPHY_CSS} + } @media (prefers-color-scheme: dark) { - ${DARK_MODE_OVERRIDES_CSS} + ${options?.target === 'host' ? ':host' : 'html:not(body)'} { + ${DARK_MODE_OVERRIDES_CSS} + } } `); }
diff --git a/tools/style_variable_generator/colors_test_typography_expected.ts b/tools/style_variable_generator/colors_test_typography_expected.ts index 1beca9c..17e23a95 100644 --- a/tools/style_variable_generator/colors_test_typography_expected.ts +++ b/tools/style_variable_generator/colors_test_typography_expected.ts
@@ -12,11 +12,21 @@ /* SAFETY_BOILERPLATE */ export interface GetColorsCSSOptions { - lockTheme?: 'light'|'dark'; + /** The html selector the colors should be attached too. */ + target?: 'host' | 'html'; + /** + * Generate a css dump which sets variables to their dark mode values in light + * mode and vice versa. If true lockTheme is ignored. + */ + invert?: boolean; + /** + * Generate a css dump which sets variables to either their dark mode or light + * mode values and ignores the documents prefers-color-scheme. + */ + lockTheme?: 'light' | 'dark'; } const DEFAULT_CSS = ` -html:not(body) { --google-grey-900-rgb: 32, 33, 36; --google-grey-900: rgb(var(--google-grey-900-rgb)); @@ -32,23 +42,9 @@ --cros-disabled-opacity: 0.38; --cros-reference-opacity: var(--cros-disabled-opacity); -} -html { - /* font families */ - --cros-font-family-test: 'Google Sans', 'Noto Sans', sans-serif; - --cros-font-family-other: Roboto, 'Noto Sans', sans-serif; - - /* typefaces */ - --cros-headline-1-font: 500 15px/22px var(--cros-font-family-test); - --cros-headline-1-font-family: var(--cros-font-family-test); - --cros-headline-1-font-size: 15px; - --cros-headline-1-font-weight: 500; - --cros-headline-1-line-height: 22px; -} `; const DARK_MODE_OVERRIDES_CSS = ` -html:not(body) { --cros-text-color-primary-rgb: 255, 255, 255; --cros-text-color-primary: rgb(var(--cros-text-color-primary-rgb)); @@ -59,7 +55,21 @@ --cros-bg-color-elevation-1: rgb(var(--cros-bg-color-elevation-1-rgb)); --cros-reference-opacity: 1; -} +`; + +const UNTYPED_CSS = ``; + +const TYPOGRAPHY_CSS = ` + /* font families */ + --cros-font-family-test: 'Google Sans', 'Noto Sans', sans-serif; + --cros-font-family-other: Roboto, 'Noto Sans', sans-serif; + + /* typefaces */ + --cros-headline-1-font: 500 15px/22px var(--cros-font-family-test); + --cros-headline-1-font-family: var(--cros-font-family-test); + --cros-headline-1-font-size: 15px; + --cros-headline-1-font-weight: 500; + --cros-headline-1-line-height: 22px; `; /** @@ -72,21 +82,51 @@ */ export function getColorsCSS(options?: GetColorsCSSOptions) { let cssString; - if (options?.lockTheme === 'light') { + if (options?.invert) { + cssString = /* SAFE */ (` + ${options?.target === 'host' ? ':host' : 'html:not(body)'} { + ${DEFAULT_CSS} + ${UNTYPED_CSS} + ${TYPOGRAPHY_CSS} + ${DARK_MODE_OVERRIDES_CSS} + } + + @media (prefers-color-scheme: dark) { + ${options?.target === 'host' ? ':host' : 'html:not(body)'} { + ${DEFAULT_CSS} + } + } + `); + } else if (options?.lockTheme === 'light') { // Tag strings which are safe with a special comment so copybara can add // the right safety wrappers whem moving this code into Google3. - cssString = /* SAFE */ (DEFAULT_CSS); + cssString = /* SAFE */ (` + ${options?.target === 'host' ? ':host' : 'html:not(body)'} { + ${DEFAULT_CSS} + ${UNTYPED_CSS} + ${TYPOGRAPHY_CSS} + } + `); } else if (options?.lockTheme === 'dark') { cssString = /* SAFE */ (` - ${DEFAULT_CSS} - ${DARK_MODE_OVERRIDES_CSS} + ${options?.target === 'host' ? ':host' : 'html:not(body)'} { + ${DEFAULT_CSS} + ${UNTYPED_CSS} + ${TYPOGRAPHY_CSS} + ${DARK_MODE_OVERRIDES_CSS} + } `); } else { cssString = /* SAFE */ (` - ${DEFAULT_CSS} - + ${options?.target === 'host' ? ':host' : 'html:not(body)'} { + ${DEFAULT_CSS} + ${UNTYPED_CSS} + ${TYPOGRAPHY_CSS} + } @media (prefers-color-scheme: dark) { - ${DARK_MODE_OVERRIDES_CSS} + ${options?.target === 'host' ? ':host' : 'html:not(body)'} { + ${DARK_MODE_OVERRIDES_CSS} + } } `); }
diff --git a/tools/style_variable_generator/colors_test_untyped_css_expected.ts b/tools/style_variable_generator/colors_test_untyped_css_expected.ts index e443f65..a2f0290 100644 --- a/tools/style_variable_generator/colors_test_untyped_css_expected.ts +++ b/tools/style_variable_generator/colors_test_untyped_css_expected.ts
@@ -12,11 +12,21 @@ /* SAFETY_BOILERPLATE */ export interface GetColorsCSSOptions { - lockTheme?: 'light'|'dark'; + /** The html selector the colors should be attached too. */ + target?: 'host' | 'html'; + /** + * Generate a css dump which sets variables to their dark mode values in light + * mode and vice versa. If true lockTheme is ignored. + */ + invert?: boolean; + /** + * Generate a css dump which sets variables to either their dark mode or light + * mode values and ignores the documents prefers-color-scheme. + */ + lockTheme?: 'light' | 'dark'; } const DEFAULT_CSS = ` -html:not(body) { --google-grey-900-rgb: 32, 33, 36; --google-grey-900: rgb(var(--google-grey-900-rgb)); @@ -32,17 +42,9 @@ --cros-disabled-opacity: 0.38; --cros-reference-opacity: var(--cros-disabled-opacity); -} -html { - /* shadows */ - --cros-elevation-1-shadow: 0px 1px 2px rgba(0, 0, 0, 0.3), 0px 1px 3px rgba(0, 0, 0, 0.15); - --cros-elevation-2-shadow: 0px 1px 2px rgba(0, 0, 0, 0.3), 0px 2px 6px rgba(0, 0, 0, 0.15); - --cros-elevation-3-shadow: 0px 1px 3px rgba(0, 0, 0, 0.3), 0px 4px 8px rgba(0, 0, 0, 0.15); -} `; const DARK_MODE_OVERRIDES_CSS = ` -html:not(body) { --cros-text-color-primary-rgb: 255, 255, 255; --cros-text-color-primary: rgb(var(--cros-text-color-primary-rgb)); @@ -53,9 +55,17 @@ --cros-bg-color-elevation-1: rgb(var(--cros-bg-color-elevation-1-rgb)); --cros-reference-opacity: 1; -} `; +const UNTYPED_CSS = ` + /* shadows */ + --cros-elevation-1-shadow: 0px 1px 2px rgba(0, 0, 0, 0.3), 0px 1px 3px rgba(0, 0, 0, 0.15); + --cros-elevation-2-shadow: 0px 1px 2px rgba(0, 0, 0, 0.3), 0px 2px 6px rgba(0, 0, 0, 0.15); + --cros-elevation-3-shadow: 0px 1px 3px rgba(0, 0, 0, 0.3), 0px 4px 8px rgba(0, 0, 0, 0.15); +`; + +const TYPOGRAPHY_CSS = ``; + /** * Returns a string containing all semantic colors exported in this file as * css variables. This string an be used to construct a stylesheet which can be @@ -66,21 +76,51 @@ */ export function getColorsCSS(options?: GetColorsCSSOptions) { let cssString; - if (options?.lockTheme === 'light') { + if (options?.invert) { + cssString = /* SAFE */ (` + ${options?.target === 'host' ? ':host' : 'html:not(body)'} { + ${DEFAULT_CSS} + ${UNTYPED_CSS} + ${TYPOGRAPHY_CSS} + ${DARK_MODE_OVERRIDES_CSS} + } + + @media (prefers-color-scheme: dark) { + ${options?.target === 'host' ? ':host' : 'html:not(body)'} { + ${DEFAULT_CSS} + } + } + `); + } else if (options?.lockTheme === 'light') { // Tag strings which are safe with a special comment so copybara can add // the right safety wrappers whem moving this code into Google3. - cssString = /* SAFE */ (DEFAULT_CSS); + cssString = /* SAFE */ (` + ${options?.target === 'host' ? ':host' : 'html:not(body)'} { + ${DEFAULT_CSS} + ${UNTYPED_CSS} + ${TYPOGRAPHY_CSS} + } + `); } else if (options?.lockTheme === 'dark') { cssString = /* SAFE */ (` - ${DEFAULT_CSS} - ${DARK_MODE_OVERRIDES_CSS} + ${options?.target === 'host' ? ':host' : 'html:not(body)'} { + ${DEFAULT_CSS} + ${UNTYPED_CSS} + ${TYPOGRAPHY_CSS} + ${DARK_MODE_OVERRIDES_CSS} + } `); } else { cssString = /* SAFE */ (` - ${DEFAULT_CSS} - + ${options?.target === 'host' ? ':host' : 'html:not(body)'} { + ${DEFAULT_CSS} + ${UNTYPED_CSS} + ${TYPOGRAPHY_CSS} + } @media (prefers-color-scheme: dark) { - ${DARK_MODE_OVERRIDES_CSS} + ${options?.target === 'host' ? ':host' : 'html:not(body)'} { + ${DARK_MODE_OVERRIDES_CSS} + } } `); }
diff --git a/tools/style_variable_generator/ts_generator.tmpl b/tools/style_variable_generator/ts_generator.tmpl index 6982cce..ea4a679 100644 --- a/tools/style_variable_generator/ts_generator.tmpl +++ b/tools/style_variable_generator/ts_generator.tmpl
@@ -24,33 +24,41 @@ /* SAFETY_BOILERPLATE */ export interface GetColorsCSSOptions { - lockTheme?: 'light'|'dark'; + /** The html selector the colors should be attached too. */ + target?: 'host' | 'html'; + /** + * Generate a css dump which sets variables to their dark mode values in light + * mode and vice versa. If true lockTheme is ignored. + */ + invert?: boolean; + /** + * Generate a css dump which sets variables to either their dark mode or light + * mode values and ignores the documents prefers-color-scheme. + */ + lockTheme?: 'light' | 'dark'; } const DEFAULT_CSS = ` -{# -The :not(body) adds extra selector specificity so that these colors 'win' -against paper-styles/color.html. -TODO(https://crbug.com/1062154): Remove once deprecated colors are removed from -Chrome OS pages. --#} -html:not(body) { {{- render_variables_as_css(Modes.DEFAULT) -}} -} +`; +const DARK_MODE_OVERRIDES_CSS = ` +{{- render_variables_as_css(Modes.DARK) -}} +`; + +const UNTYPED_CSS = ` {%- if untyped_css %} -html { {%- for group_name, vars in untyped_css.items() %} /* {{group_name}} */ {%- for name, value in vars.items() %} {{name | to_css_var_name}}: {{value}}; {%- endfor %} {%- endfor %} -} -{%- endif -%} +{% endif -%} +`; +const TYPOGRAPHY_CSS = ` {%- if typography.font_families or typography.typefaces %} -html { /* font families */ {%- for name, value in typography.font_families.items() %} {{name | to_css_var_name}}: {{value}}; @@ -65,14 +73,7 @@ {{name | to_css_var_name}}-line-height: {{typeface.line_height}}px; {%- endfor %} -} -{%- endif %} -`; - -const DARK_MODE_OVERRIDES_CSS = ` -html:not(body) { -{{- render_variables_as_css(Modes.DARK) -}} -} +{% endif -%} `; /** @@ -85,27 +86,58 @@ */ export function getColorsCSS(options?: GetColorsCSSOptions) { let cssString; - if (options?.lockTheme === 'light') { + if (options?.invert) { + cssString = /* SAFE */ (` + ${options?.target === 'host' ? ':host' : 'html:not(body)'} { + ${DEFAULT_CSS} + ${UNTYPED_CSS} + ${TYPOGRAPHY_CSS} + ${DARK_MODE_OVERRIDES_CSS} + } + + @media (prefers-color-scheme: dark) { + ${options?.target === 'host' ? ':host' : 'html:not(body)'} { + ${DEFAULT_CSS} + } + } + `); + } else if (options?.lockTheme === 'light') { // Tag strings which are safe with a special comment so copybara can add // the right safety wrappers whem moving this code into Google3. - cssString = /* SAFE */ (DEFAULT_CSS); + cssString = /* SAFE */ (` + ${options?.target === 'host' ? ':host' : 'html:not(body)'} { + ${DEFAULT_CSS} + ${UNTYPED_CSS} + ${TYPOGRAPHY_CSS} + } + `); } else if (options?.lockTheme === 'dark') { cssString = /* SAFE */ (` - ${DEFAULT_CSS} - ${DARK_MODE_OVERRIDES_CSS} + ${options?.target === 'host' ? ':host' : 'html:not(body)'} { + ${DEFAULT_CSS} + ${UNTYPED_CSS} + ${TYPOGRAPHY_CSS} + ${DARK_MODE_OVERRIDES_CSS} + } `); } else { cssString = /* SAFE */ (` - ${DEFAULT_CSS} - + ${options?.target === 'host' ? ':host' : 'html:not(body)'} { + ${DEFAULT_CSS} + ${UNTYPED_CSS} + ${TYPOGRAPHY_CSS} + } @media (prefers-color-scheme: dark) { - ${DARK_MODE_OVERRIDES_CSS} + ${options?.target === 'host' ? ':host' : 'html:not(body)'} { + ${DARK_MODE_OVERRIDES_CSS} + } } `); } return cssString; } + {%- endif %} {% for model_name, color in colors[Modes.DEFAULT].items() -%}
diff --git a/ui/accessibility/ax_computed_node_data.cc b/ui/accessibility/ax_computed_node_data.cc index 6e15815a..e175016 100644 --- a/ui/accessibility/ax_computed_node_data.cc +++ b/ui/accessibility/ax_computed_node_data.cc
@@ -280,9 +280,13 @@ sentence_starts_ = std::vector<int32_t>(); sentence_ends_ = std::vector<int32_t>(); - const std::u16string& text_content = GetOrComputeTextContentUTF16(); - if (text_content.empty()) + if (owner_->IsLineBreak()) return; + const std::u16string& text_content = GetOrComputeTextContentUTF16(); + if (text_content.empty() || + base::ContainsOnlyChars(text_content, base::kWhitespaceUTF16)) { + return; + } // Unlike in ICU, a sentence boundary is not valid in Blink if it falls within // some whitespace that is used to separate sentences. We therefore need to
diff --git a/ui/accessibility/ax_node_position_unittest.cc b/ui/accessibility/ax_node_position_unittest.cc index a7368e02..2876598 100644 --- a/ui/accessibility/ax_node_position_unittest.cc +++ b/ui/accessibility/ax_node_position_unittest.cc
@@ -388,6 +388,10 @@ inline_box1_.role = ax::mojom::Role::kInlineTextBox; inline_box1_.AddState(ax::mojom::State::kEditable); inline_box1_.SetName("Line 1"); + inline_box1_.AddIntListAttribute(ax::mojom::IntListAttribute::kSentenceStarts, + {0}); + inline_box1_.AddIntListAttribute(ax::mojom::IntListAttribute::kSentenceEnds, + {6}); inline_box1_.AddIntListAttribute(ax::mojom::IntListAttribute::kWordStarts, std::vector<int32_t>{0, 5}); inline_box1_.AddIntListAttribute(ax::mojom::IntListAttribute::kWordEnds, @@ -412,6 +416,10 @@ inline_box2_.role = ax::mojom::Role::kInlineTextBox; inline_box2_.AddState(ax::mojom::State::kEditable); inline_box2_.SetName("Line 2"); + inline_box2_.AddIntListAttribute(ax::mojom::IntListAttribute::kSentenceStarts, + {0}); + inline_box2_.AddIntListAttribute(ax::mojom::IntListAttribute::kSentenceEnds, + {6}); inline_box2_.AddIntListAttribute(ax::mojom::IntListAttribute::kWordStarts, std::vector<int32_t>{0, 5}); inline_box2_.AddIntListAttribute(ax::mojom::IntListAttribute::kWordEnds, @@ -3222,19 +3230,19 @@ ax::mojom::TextAffinity::kDownstream); paragraph_start_position = paragraph_start_position->CreateNextParagraphStartPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); ASSERT_TRUE(paragraph_start_position->IsTextPosition()); EXPECT_EQ(inline_text_data_b_1.id, paragraph_start_position->anchor_id()); EXPECT_EQ(0, paragraph_start_position->text_offset()); paragraph_start_position = paragraph_start_position->CreateNextParagraphStartPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); ASSERT_TRUE(paragraph_start_position->IsTextPosition()); EXPECT_EQ(inline_text_data_c.id, paragraph_start_position->anchor_id()); EXPECT_EQ(0, paragraph_start_position->text_offset()); paragraph_start_position = paragraph_start_position->CreateNextParagraphStartPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); EXPECT_TRUE(paragraph_start_position->IsNullPosition()); paragraph_start_position = AXNodePosition::CreateTextPosition( @@ -3242,25 +3250,25 @@ ax::mojom::TextAffinity::kDownstream); paragraph_start_position = paragraph_start_position->CreatePreviousParagraphStartPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); ASSERT_TRUE(paragraph_start_position->IsTextPosition()); EXPECT_EQ(inline_text_data_c.id, paragraph_start_position->anchor_id()); EXPECT_EQ(0, paragraph_start_position->text_offset()); paragraph_start_position = paragraph_start_position->CreatePreviousParagraphStartPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); ASSERT_TRUE(paragraph_start_position->IsTextPosition()); EXPECT_EQ(inline_text_data_b_1.id, paragraph_start_position->anchor_id()); EXPECT_EQ(0, paragraph_start_position->text_offset()); paragraph_start_position = paragraph_start_position->CreatePreviousParagraphStartPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); ASSERT_TRUE(paragraph_start_position->IsTextPosition()); EXPECT_EQ(inline_text_data_a.id, paragraph_start_position->anchor_id()); EXPECT_EQ(0, paragraph_start_position->text_offset()); paragraph_start_position = paragraph_start_position->CreatePreviousParagraphStartPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); EXPECT_TRUE(paragraph_start_position->IsNullPosition()); TestPositionType paragraph_end_position = AXNodePosition::CreateTextPosition( @@ -3268,28 +3276,28 @@ ax::mojom::TextAffinity::kDownstream); paragraph_end_position = paragraph_end_position->CreateNextParagraphEndPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); ASSERT_TRUE(paragraph_end_position->IsTextPosition()); EXPECT_EQ(inline_text_data_a.id, paragraph_end_position->anchor_id()); // "First paragraph<>". EXPECT_EQ(15, paragraph_end_position->text_offset()); paragraph_end_position = paragraph_end_position->CreateNextParagraphEndPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); ASSERT_TRUE(paragraph_end_position->IsTextPosition()); EXPECT_EQ(inline_text_data_b_3.id, paragraph_end_position->anchor_id()); // "paragraph<>". EXPECT_EQ(9, paragraph_end_position->text_offset()); paragraph_end_position = paragraph_end_position->CreateNextParagraphEndPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); ASSERT_TRUE(paragraph_end_position->IsTextPosition()); EXPECT_EQ(inline_text_data_c.id, paragraph_end_position->anchor_id()); // "Third paragraph<>". EXPECT_EQ(15, paragraph_end_position->text_offset()); paragraph_end_position = paragraph_end_position->CreateNextParagraphEndPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); EXPECT_TRUE(paragraph_end_position->IsNullPosition()); paragraph_end_position = AXNodePosition::CreateTextPosition( @@ -3297,21 +3305,21 @@ ax::mojom::TextAffinity::kDownstream); paragraph_end_position = paragraph_end_position->CreatePreviousParagraphEndPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); ASSERT_TRUE(paragraph_end_position->IsTextPosition()); EXPECT_EQ(inline_text_data_b_3.id, paragraph_end_position->anchor_id()); // "paragraph<>". EXPECT_EQ(9, paragraph_end_position->text_offset()); paragraph_end_position = paragraph_end_position->CreatePreviousParagraphEndPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); ASSERT_TRUE(paragraph_end_position->IsTextPosition()); EXPECT_EQ(inline_text_data_a.id, paragraph_end_position->anchor_id()); // "First paragraph<>". EXPECT_EQ(15, paragraph_end_position->text_offset()); paragraph_end_position = paragraph_end_position->CreatePreviousParagraphEndPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); EXPECT_TRUE(paragraph_end_position->IsNullPosition()); } @@ -3363,7 +3371,7 @@ ax::mojom::TextAffinity::kDownstream); test_position = test_position->CreatePreviousParagraphEndPosition( - AXBoundaryBehavior::StopAtAnchorBoundary); + AXBoundaryBehavior::kStopAtAnchorBoundary); EXPECT_TRUE(test_position->IsTextPosition()); EXPECT_EQ(root_data.id, test_position->anchor_id()); EXPECT_EQ(5, test_position->text_offset()); @@ -4516,9 +4524,9 @@ inline_box_data_1.SetName("One"); inline_box_data_1.AddState(ax::mojom::State::kIgnored); inline_box_data_1.AddIntListAttribute( - ax::mojom::IntListAttribute::kWordStarts, std::vector<int32_t>{0}); + ax::mojom::IntListAttribute::kWordStarts, {0}); inline_box_data_1.AddIntListAttribute(ax::mojom::IntListAttribute::kWordEnds, - std::vector<int32_t>{3}); + {3}); inline_box_data_1.AddIntAttribute(ax::mojom::IntAttribute::kNextOnLineId, kInlineBox2Id); @@ -4532,9 +4540,9 @@ inline_box_data_2.role = ax::mojom::Role::kInlineTextBox; inline_box_data_2.SetName("Two"); inline_box_data_2.AddIntListAttribute( - ax::mojom::IntListAttribute::kWordStarts, std::vector<int32_t>{0}); + ax::mojom::IntListAttribute::kWordStarts, {0}); inline_box_data_2.AddIntListAttribute(ax::mojom::IntListAttribute::kWordEnds, - std::vector<int32_t>{3}); + {3}); inline_box_data_2.AddIntAttribute(ax::mojom::IntAttribute::kPreviousOnLineId, kInlineBox1Id); inline_box_data_2.AddIntAttribute(ax::mojom::IntAttribute::kNextOnLineId, @@ -4550,9 +4558,9 @@ inline_box_data_3.role = ax::mojom::Role::kInlineTextBox; inline_box_data_3.SetName("Three"); inline_box_data_3.AddIntListAttribute( - ax::mojom::IntListAttribute::kWordStarts, std::vector<int32_t>{0}); + ax::mojom::IntListAttribute::kWordStarts, {0}); inline_box_data_3.AddIntListAttribute(ax::mojom::IntListAttribute::kWordEnds, - std::vector<int32_t>{5}); + {5}); inline_box_data_3.AddIntAttribute(ax::mojom::IntAttribute::kPreviousOnLineId, kInlineBox2Id); inline_box_data_3.AddIntAttribute(ax::mojom::IntAttribute::kNextOnLineId, @@ -4569,9 +4577,9 @@ inline_box_data_4.SetName("Four"); inline_box_data_4.AddState(ax::mojom::State::kIgnored); inline_box_data_3.AddIntListAttribute( - ax::mojom::IntListAttribute::kWordStarts, std::vector<int32_t>{0}); + ax::mojom::IntListAttribute::kWordStarts, {0}); inline_box_data_3.AddIntListAttribute(ax::mojom::IntListAttribute::kWordEnds, - std::vector<int32_t>{4}); + {4}); inline_box_data_3.AddIntAttribute(ax::mojom::IntAttribute::kPreviousOnLineId, kInlineBox3Id); @@ -4593,7 +4601,7 @@ ASSERT_FALSE(text_position->IsIgnored()); TestPositionType test_position = text_position->CreatePositionAtTextBoundary( ax::mojom::TextBoundary::kWordStart, ax::mojom::MoveDirection::kForward, - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); ASSERT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTextPosition()); EXPECT_EQ(inline_box_data_3.id, test_position->anchor_id()); @@ -4601,7 +4609,7 @@ EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity()); test_position = text_position->CreatePositionAtTextBoundary( ax::mojom::TextBoundary::kWordStart, ax::mojom::MoveDirection::kBackward, - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); ASSERT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTextPosition()); EXPECT_EQ(inline_box_data_2.id, test_position->anchor_id()); @@ -4614,7 +4622,7 @@ ASSERT_FALSE(text_position->IsIgnored()); test_position = text_position->CreatePositionAtTextBoundary( ax::mojom::TextBoundary::kWordStart, ax::mojom::MoveDirection::kForward, - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); ASSERT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTextPosition()); EXPECT_EQ(inline_box_data_3.id, test_position->anchor_id()); @@ -4622,7 +4630,7 @@ EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity()); test_position = text_position->CreatePositionAtTextBoundary( ax::mojom::TextBoundary::kWordStart, ax::mojom::MoveDirection::kBackward, - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); ASSERT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTextPosition()); EXPECT_EQ(inline_box_data_2.id, test_position->anchor_id()); @@ -4781,15 +4789,15 @@ ASSERT_NE(nullptr, null_position); TestPositionType test_position = null_position->CreatePreviousFormatStartPosition( - AXBoundaryBehavior::StopIfAlreadyAtBoundary); + AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsNullPosition()); test_position = null_position->CreatePreviousFormatStartPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsNullPosition()); test_position = null_position->CreatePreviousFormatStartPosition( - AXBoundaryBehavior::StopAtAnchorBoundary); + AXBoundaryBehavior::kStopAtAnchorBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsNullPosition()); } @@ -4804,45 +4812,46 @@ TestPositionType test_position = tree_position->CreatePreviousFormatStartPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTreePosition()); EXPECT_EQ(static_text1_.id, test_position->anchor_id()); EXPECT_EQ(0, test_position->child_index()); test_position = test_position->CreatePreviousFormatStartPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTreePosition()); EXPECT_EQ(check_box_.id, test_position->anchor_id()); EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index()); test_position = test_position->CreatePreviousFormatStartPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTreePosition()); EXPECT_EQ(button_.id, test_position->anchor_id()); EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index()); - // StopIfAlreadyAtBoundary shouldn't move, since it's already at a boundary. + // kStopAtAnchorBoundaryOrIfAlreadyAtBoundary shouldn't move, since it's + // already at a boundary. test_position = test_position->CreatePreviousFormatStartPosition( - AXBoundaryBehavior::StopIfAlreadyAtBoundary); + AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTreePosition()); EXPECT_EQ(button_.id, test_position->anchor_id()); EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index()); - // StopAtLastAnchorBoundary should stop at the start of the whole content - // while CrossBoundary should return a null position when crossing it. + // kStopAtLastAnchorBoundary should stop at the start of the whole content + // while kCrossBoundary should return a null position when crossing it. test_position = test_position->CreatePreviousFormatStartPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTreePosition()); EXPECT_EQ(button_.id, test_position->anchor_id()); EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index()); test_position = test_position->CreatePreviousFormatStartPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsNullPosition()); } @@ -4858,7 +4867,7 @@ TestPositionType test_position = text_position->CreatePreviousFormatStartPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTextPosition()); EXPECT_EQ(inline_box1_.id, test_position->anchor_id()); @@ -4866,38 +4875,39 @@ EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity()); test_position = test_position->CreatePreviousFormatStartPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTextPosition()); EXPECT_EQ(check_box_.id, test_position->anchor_id()); EXPECT_EQ(0, test_position->text_offset()); test_position = test_position->CreatePreviousFormatStartPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTextPosition()); EXPECT_EQ(button_.id, test_position->anchor_id()); EXPECT_EQ(0, test_position->text_offset()); - // StopIfAlreadyAtBoundary shouldn't move, since it's already at a boundary. + // kStopAtAnchorBoundaryOrIfAlreadyAtBoundary shouldn't move, since it's + // already at a boundary. test_position = test_position->CreatePreviousFormatStartPosition( - AXBoundaryBehavior::StopIfAlreadyAtBoundary); + AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTextPosition()); EXPECT_EQ(button_.id, test_position->anchor_id()); EXPECT_EQ(0, test_position->text_offset()); - // StopAtLastAnchorBoundary should stop at the start of the whole content - // while CrossBoundary should return a null position when crossing it. + // kStopAtLastAnchorBoundary should stop at the start of the whole content + // while kCrossBoundary should return a null position when crossing it. test_position = test_position->CreatePreviousFormatStartPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTextPosition()); EXPECT_EQ(button_.id, test_position->anchor_id()); EXPECT_EQ(0, test_position->text_offset()); test_position = test_position->CreatePreviousFormatStartPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsNullPosition()); } @@ -4906,11 +4916,11 @@ TestPositionType null_position = AXNodePosition::CreateNullPosition(); ASSERT_NE(nullptr, null_position); TestPositionType test_position = null_position->CreateNextFormatEndPosition( - AXBoundaryBehavior::StopIfAlreadyAtBoundary); + AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsNullPosition()); test_position = null_position->CreateNextFormatEndPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsNullPosition()); } @@ -4924,52 +4934,53 @@ ASSERT_TRUE(tree_position->IsTreePosition()); TestPositionType test_position = tree_position->CreateNextFormatEndPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTreePosition()); EXPECT_EQ(check_box_.id, test_position->anchor_id()); EXPECT_EQ(0, test_position->child_index()); test_position = test_position->CreateNextFormatEndPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTreePosition()); EXPECT_EQ(inline_box1_.id, test_position->anchor_id()); EXPECT_EQ(0, test_position->child_index()); test_position = test_position->CreateNextFormatEndPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTreePosition()); EXPECT_EQ(line_break_.id, test_position->anchor_id()); EXPECT_EQ(0, test_position->child_index()); test_position = test_position->CreateNextFormatEndPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTreePosition()); EXPECT_EQ(inline_box2_.id, test_position->anchor_id()); EXPECT_EQ(0, test_position->child_index()); - // StopIfAlreadyAtBoundary shouldn't move, since it's already at a boundary. + // kStopAtAnchorBoundaryOrIfAlreadyAtBoundary shouldn't move, since it's + // already at a boundary. test_position = test_position->CreateNextFormatEndPosition( - AXBoundaryBehavior::StopIfAlreadyAtBoundary); + AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTreePosition()); EXPECT_EQ(inline_box2_.id, test_position->anchor_id()); EXPECT_EQ(0, test_position->child_index()); - // StopAtLastAnchorBoundary should stop at the end of the whole content while - // CrossBoundary should return a null position when crossing it. + // kStopAtLastAnchorBoundary should stop at the end of the whole content while + // kCrossBoundary should return a null position when crossing it. test_position = test_position->CreateNextFormatEndPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTreePosition()); EXPECT_EQ(inline_box2_.id, test_position->anchor_id()); EXPECT_EQ(0, test_position->child_index()); test_position = test_position->CreateNextFormatEndPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsNullPosition()); } @@ -4984,59 +4995,60 @@ ASSERT_TRUE(text_position->IsTextPosition()); TestPositionType test_position = text_position->CreateNextFormatEndPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTextPosition()); EXPECT_EQ(button_.id, test_position->anchor_id()); EXPECT_EQ(1, test_position->text_offset()); test_position = test_position->CreateNextFormatEndPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTextPosition()); EXPECT_EQ(check_box_.id, test_position->anchor_id()); EXPECT_EQ(1, test_position->text_offset()); test_position = test_position->CreateNextFormatEndPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTextPosition()); EXPECT_EQ(inline_box1_.id, test_position->anchor_id()); EXPECT_EQ(6, test_position->text_offset()); test_position = test_position->CreateNextFormatEndPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTextPosition()); EXPECT_EQ(line_break_.id, test_position->anchor_id()); EXPECT_EQ(1, test_position->text_offset()); test_position = test_position->CreateNextFormatEndPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTextPosition()); EXPECT_EQ(inline_box2_.id, test_position->anchor_id()); EXPECT_EQ(6, test_position->text_offset()); - // StopIfAlreadyAtBoundary shouldn't move, since it's already at a boundary. + // kStopAtAnchorBoundaryOrIfAlreadyAtBoundary shouldn't move, since it's + // already at a boundary. test_position = test_position->CreateNextFormatEndPosition( - AXBoundaryBehavior::StopIfAlreadyAtBoundary); + AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTextPosition()); EXPECT_EQ(inline_box2_.id, test_position->anchor_id()); EXPECT_EQ(6, test_position->text_offset()); - // StopAtLastAnchorBoundary should stop at the end of the whole content while - // CrossBoundary should return a null position when crossing it. + // kStopAtLastAnchorBoundary should stop at the end of the whole content while + // kCrossBoundary should return a null position when crossing it. test_position = test_position->CreateNextFormatEndPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTextPosition()); EXPECT_EQ(inline_box2_.id, test_position->anchor_id()); EXPECT_EQ(6, test_position->text_offset()); test_position = test_position->CreateNextFormatEndPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsNullPosition()); } @@ -5194,7 +5206,7 @@ // is at the end of "heading 1". TestPositionType format_end_position = text_position->CreateNextFormatEndPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); ASSERT_NE(nullptr, format_end_position); EXPECT_TRUE(format_end_position->IsTextPosition()); EXPECT_EQ(inline_text_4.id, format_end_position->anchor_id()); @@ -5205,7 +5217,7 @@ // Move position to end of format at "heading 1|select, option 1|<>...", which // is at the end of embedded object <select, option 1> (popup_button_5). format_end_position = format_end_position->CreateNextFormatEndPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); ASSERT_NE(nullptr, format_end_position); EXPECT_TRUE(format_end_position->IsTextPosition()); EXPECT_EQ(popup_button_5.id, format_end_position->anchor_id()); @@ -5216,7 +5228,7 @@ // Move position to end of format at "...|select, option 1|heading 2<>...", // which is at the end of "heading 2". format_end_position = format_end_position->CreateNextFormatEndPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); ASSERT_NE(nullptr, format_end_position); EXPECT_TRUE(format_end_position->IsTextPosition()); EXPECT_EQ(inline_text_10.id, format_end_position->anchor_id()); @@ -5228,7 +5240,7 @@ // "...heading 2|select, option 2|<>|select, option 3|...", which is at the // end of embedded object <select, option 2> (popup_button_11). format_end_position = format_end_position->CreateNextFormatEndPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); ASSERT_NE(nullptr, format_end_position); EXPECT_TRUE(format_end_position->IsTextPosition()); EXPECT_EQ(popup_button_11.id, format_end_position->anchor_id()); @@ -5240,7 +5252,7 @@ // "...heading 2|select, option 2||select, option 3|<>...", which is at the // end of embedded object <select, option 3> (popup_button_14). format_end_position = format_end_position->CreateNextFormatEndPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); ASSERT_NE(nullptr, format_end_position); EXPECT_TRUE(format_end_position->IsTextPosition()); EXPECT_EQ(popup_button_14.id, format_end_position->anchor_id()); @@ -5251,7 +5263,7 @@ // Move position to end of format at "...|select, option 3|more text<>", which // is at the end of "more text". format_end_position = format_end_position->CreateNextFormatEndPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); ASSERT_NE(nullptr, format_end_position); EXPECT_TRUE(format_end_position->IsTextPosition()); EXPECT_EQ(inline_text_18.id, format_end_position->anchor_id()); @@ -5289,7 +5301,7 @@ ASSERT_NE(nullptr, text_position); TestPositionType test_position = text_position->CreatePreviousFormatStartPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTextPosition()); EXPECT_EQ(text_data.id, test_position->anchor_id()); @@ -5301,7 +5313,7 @@ ax::mojom::TextAffinity::kDownstream); ASSERT_NE(nullptr, text_position); test_position = text_position->CreateNextFormatEndPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTextPosition()); EXPECT_EQ(more_text_data.id, test_position->anchor_id()); @@ -5516,7 +5528,7 @@ EXPECT_EQ(6, text_position->text_offset()); text_position = text_position->CreateNextFormatEndPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); ASSERT_NE(nullptr, text_position); EXPECT_TRUE(text_position->IsTextPosition()); EXPECT_EQ(inline_box_11.id, text_position->anchor_id()); @@ -5532,7 +5544,7 @@ EXPECT_EQ(0, text_position->text_offset()); text_position = text_position->CreatePreviousFormatStartPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); ASSERT_NE(nullptr, text_position); EXPECT_TRUE(text_position->IsTextPosition()); EXPECT_EQ(inline_box_5.id, text_position->anchor_id()); @@ -5552,7 +5564,7 @@ EXPECT_EQ(7, text_position->text_offset()); text_position = text_position->CreateNextFormatEndPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); ASSERT_NE(nullptr, text_position); EXPECT_TRUE(text_position->IsTextPosition()); EXPECT_EQ(inline_box_13.id, text_position->anchor_id()); @@ -5568,7 +5580,7 @@ EXPECT_EQ(0, text_position->text_offset()); text_position = text_position->CreatePreviousFormatStartPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); ASSERT_NE(nullptr, text_position); EXPECT_TRUE(text_position->IsTextPosition()); EXPECT_EQ(inline_box_22.id, text_position->anchor_id()); @@ -5581,22 +5593,22 @@ ASSERT_NE(nullptr, null_position); TestPositionType test_position = null_position->CreatePreviousPageStartPosition( - AXBoundaryBehavior::StopIfAlreadyAtBoundary); + AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsNullPosition()); test_position = null_position->CreateNextPageStartPosition( - AXBoundaryBehavior::StopIfAlreadyAtBoundary); + AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsNullPosition()); test_position = null_position->CreatePreviousPageEndPosition( - AXBoundaryBehavior::StopIfAlreadyAtBoundary); + AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsNullPosition()); test_position = null_position->CreatePreviousPageStartPosition( - AXBoundaryBehavior::StopIfAlreadyAtBoundary); + AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsNullPosition()); } @@ -5614,23 +5626,24 @@ ASSERT_NE(nullptr, tree_position); ASSERT_TRUE(tree_position->IsTreePosition()); - // StopIfAlreadyAtBoundary shouldn't move at all since it's at a boundary. + // kStopAtAnchorBoundaryOrIfAlreadyAtBoundary shouldn't move at all since it's + // at a boundary. TestPositionType test_position = tree_position->CreateNextPageStartPosition( - AXBoundaryBehavior::StopIfAlreadyAtBoundary); + AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTreePosition()); EXPECT_EQ(page_1_data.id, test_position->anchor_id()); EXPECT_EQ(0, test_position->child_index()); test_position = tree_position->CreateNextPageStartPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTreePosition()); EXPECT_EQ(page_2_text_data.id, test_position->anchor_id()); EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index()); test_position = tree_position->CreateNextPageStartPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTreePosition()); EXPECT_EQ(page_2_text_data.id, test_position->anchor_id()); @@ -5638,36 +5651,36 @@ // Test CreateNextPageEndPosition until the end of content is reached. test_position = tree_position->CreateNextPageEndPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTreePosition()); EXPECT_EQ(page_1_data.id, test_position->anchor_id()); EXPECT_EQ(1, test_position->child_index()); test_position = test_position->CreateNextPageEndPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTreePosition()); EXPECT_EQ(page_2_text_data.id, test_position->anchor_id()); EXPECT_EQ(0, test_position->child_index()); test_position = test_position->CreateNextPageEndPosition( - AXBoundaryBehavior::StopIfAlreadyAtBoundary); + AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTreePosition()); EXPECT_EQ(page_2_text_data.id, test_position->anchor_id()); EXPECT_EQ(0, test_position->child_index()); - // StopAtLastAnchorBoundary shouldn't move past the end of the whole content. + // kStopAtLastAnchorBoundary shouldn't move past the end of the whole content. test_position = test_position->CreateNextPageStartPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTreePosition()); EXPECT_EQ(page_3_text_data.id, test_position->anchor_id()); EXPECT_EQ(0, test_position->child_index()); test_position = test_position->CreateNextPageEndPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTreePosition()); EXPECT_EQ(page_3_text_data.id, test_position->anchor_id()); @@ -5675,69 +5688,69 @@ // Moving forward past the end should return a null position. TestPositionType null_position = test_position->CreateNextPageStartPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); EXPECT_NE(nullptr, null_position); EXPECT_TRUE(null_position->IsNullPosition()); null_position = test_position->CreateNextPageEndPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); EXPECT_NE(nullptr, null_position); EXPECT_TRUE(null_position->IsNullPosition()); // Now move backward through the accessibility tree. tree_position = test_position->CreatePreviousPageEndPosition( - AXBoundaryBehavior::StopIfAlreadyAtBoundary); + AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); EXPECT_NE(nullptr, tree_position); EXPECT_TRUE(tree_position->IsTreePosition()); EXPECT_EQ(page_3_text_data.id, tree_position->anchor_id()); EXPECT_EQ(0, tree_position->child_index()); test_position = tree_position->CreatePreviousPageEndPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTreePosition()); EXPECT_EQ(page_2_text_data.id, test_position->anchor_id()); EXPECT_EQ(0, test_position->child_index()); test_position = tree_position->CreatePreviousPageEndPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTreePosition()); EXPECT_EQ(page_2_text_data.id, test_position->anchor_id()); EXPECT_EQ(0, test_position->child_index()); test_position = test_position->CreatePreviousPageStartPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTreePosition()); EXPECT_EQ(page_2_text_data.id, test_position->anchor_id()); EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index()); test_position = test_position->CreatePreviousPageStartPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTreePosition()); EXPECT_EQ(page_1_text_data.id, test_position->anchor_id()); EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index()); test_position = test_position->CreatePreviousPageStartPosition( - AXBoundaryBehavior::StopIfAlreadyAtBoundary); + AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTreePosition()); EXPECT_EQ(page_1_text_data.id, test_position->anchor_id()); EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index()); - // StopAtLastAnchorBoundary shouldn't move past the start of the whole + // kStopAtLastAnchorBoundary shouldn't move past the start of the whole // content. test_position = test_position->CreatePreviousPageStartPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTreePosition()); EXPECT_EQ(page_1_text_data.id, test_position->anchor_id()); EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index()); test_position = test_position->CreatePreviousPageEndPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTreePosition()); EXPECT_EQ(page_1_text_data.id, test_position->anchor_id()); @@ -5745,12 +5758,12 @@ // Moving before the start should return a null position. null_position = test_position->CreatePreviousPageStartPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); EXPECT_NE(nullptr, null_position); EXPECT_TRUE(null_position->IsNullPosition()); null_position = test_position->CreatePreviousPageEndPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); EXPECT_NE(nullptr, null_position); EXPECT_TRUE(null_position->IsNullPosition()); } @@ -5769,23 +5782,24 @@ ASSERT_NE(nullptr, text_position); ASSERT_TRUE(text_position->IsTextPosition()); - // StopIfAlreadyAtBoundary shouldn't move at all since it's at a boundary. + // kStopAtAnchorBoundaryOrIfAlreadyAtBoundary shouldn't move at all since it's + // at a boundary. TestPositionType test_position = text_position->CreateNextPageStartPosition( - AXBoundaryBehavior::StopIfAlreadyAtBoundary); + AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTextPosition()); EXPECT_EQ(page_1_text_data.id, test_position->anchor_id()); EXPECT_EQ(0, test_position->text_offset()); test_position = text_position->CreateNextPageStartPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTextPosition()); EXPECT_EQ(page_2_text_data.id, test_position->anchor_id()); EXPECT_EQ(0, test_position->text_offset()); test_position = text_position->CreateNextPageStartPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTextPosition()); EXPECT_EQ(page_2_text_data.id, test_position->anchor_id()); @@ -5793,36 +5807,36 @@ // Test CreateNextPageEndPosition until the end of content is reached. test_position = test_position->CreateNextPageEndPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTextPosition()); EXPECT_EQ(page_2_text_data.id, test_position->anchor_id()); EXPECT_EQ(19, test_position->text_offset()); test_position = test_position->CreateNextPageEndPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTextPosition()); EXPECT_EQ(page_3_text_data.id, test_position->anchor_id()); EXPECT_EQ(24, test_position->text_offset()); test_position = test_position->CreateNextPageEndPosition( - AXBoundaryBehavior::StopIfAlreadyAtBoundary); + AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTextPosition()); EXPECT_EQ(page_3_text_data.id, test_position->anchor_id()); EXPECT_EQ(24, test_position->text_offset()); - // StopAtLastAnchorBoundary shouldn't move past the end of the whole content. + // kStopAtLastAnchorBoundary shouldn't move past the end of the whole content. test_position = test_position->CreateNextPageStartPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTextPosition()); EXPECT_EQ(page_3_text_data.id, test_position->anchor_id()); EXPECT_EQ(24, test_position->text_offset()); test_position = test_position->CreateNextPageEndPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTextPosition()); EXPECT_EQ(page_3_text_data.id, test_position->anchor_id()); @@ -5830,69 +5844,69 @@ // Moving forward past the end should return a null position. TestPositionType null_position = test_position->CreateNextPageStartPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); EXPECT_NE(nullptr, null_position); EXPECT_TRUE(null_position->IsNullPosition()); null_position = test_position->CreateNextPageEndPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); EXPECT_NE(nullptr, null_position); EXPECT_TRUE(null_position->IsNullPosition()); // Now move backward through the accessibility tree. text_position = test_position->CreatePreviousPageEndPosition( - AXBoundaryBehavior::StopIfAlreadyAtBoundary); + AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); EXPECT_NE(nullptr, text_position); EXPECT_TRUE(text_position->IsTextPosition()); EXPECT_EQ(page_3_text_data.id, text_position->anchor_id()); EXPECT_EQ(24, text_position->text_offset()); test_position = text_position->CreatePreviousPageEndPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTextPosition()); EXPECT_EQ(page_2_text_data.id, test_position->anchor_id()); EXPECT_EQ(19, test_position->text_offset()); test_position = text_position->CreatePreviousPageEndPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTextPosition()); EXPECT_EQ(page_2_text_data.id, test_position->anchor_id()); EXPECT_EQ(19, test_position->text_offset()); test_position = test_position->CreatePreviousPageStartPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTextPosition()); EXPECT_EQ(page_2_text_data.id, test_position->anchor_id()); EXPECT_EQ(0, test_position->text_offset()); test_position = test_position->CreatePreviousPageStartPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTextPosition()); EXPECT_EQ(page_1_text_data.id, test_position->anchor_id()); EXPECT_EQ(0, test_position->text_offset()); test_position = test_position->CreatePreviousPageStartPosition( - AXBoundaryBehavior::StopIfAlreadyAtBoundary); + AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTextPosition()); EXPECT_EQ(page_1_text_data.id, test_position->anchor_id()); EXPECT_EQ(0, test_position->text_offset()); - // StopAtLastAnchorBoundary shouldn't move past the start of the whole + // kStopAtLastAnchorBoundary shouldn't move past the start of the whole // content. test_position = test_position->CreatePreviousPageStartPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTextPosition()); EXPECT_EQ(page_1_text_data.id, test_position->anchor_id()); EXPECT_EQ(0, test_position->text_offset()); test_position = test_position->CreatePreviousPageEndPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTextPosition()); EXPECT_EQ(page_1_text_data.id, test_position->anchor_id()); @@ -5900,19 +5914,24 @@ // Moving before the start should return a null position. null_position = test_position->CreatePreviousPageStartPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); EXPECT_NE(nullptr, null_position); EXPECT_TRUE(null_position->IsNullPosition()); null_position = test_position->CreatePreviousPageEndPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); EXPECT_NE(nullptr, null_position); EXPECT_TRUE(null_position->IsNullPosition()); } TEST_F(AXPositionTest, CreatePositionAtPageBoundaryWithNonPaginatedDocument) { + // We start from the second character in the whole content instead of the + // first, so that with `AXBoundaryBehavior::kCrossBoundary` we would be able + // to move back to the start of the page. Otherwise, if we had started from + // the first character, we would already be at the start of the page, and thus + // have gotten the null position. TestPositionType text_position = AXNodePosition::CreateTextPosition( - GetTreeID(), static_text1_.id, 0 /* text_offset */, + GetTreeID(), static_text1_.id, 1 /* text_offset */, ax::mojom::TextAffinity::kDownstream); ASSERT_NE(nullptr, text_position); @@ -5921,7 +5940,7 @@ // page) TestPositionType test_position = text_position->CreatePreviousPageStartPosition( - AXBoundaryBehavior::StopIfAlreadyAtBoundary); + AXBoundaryBehavior::kCrossBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTextPosition()); EXPECT_EQ(button_.id, test_position->anchor_id()); @@ -5930,21 +5949,21 @@ // Since there is no next page, CreateNextPageStartPosition should return a // null position test_position = text_position->CreateNextPageStartPosition( - AXBoundaryBehavior::StopIfAlreadyAtBoundary); + AXBoundaryBehavior::kCrossBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsNullPosition()); // Since there is no previous page, CreatePreviousPageEndPosition should // return a null position test_position = text_position->CreatePreviousPageEndPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsNullPosition()); // Since there are no distinct pages, CreateNextPageEndPosition should move // to the end of the whole content, as if it's one large page. test_position = text_position->CreateNextPageEndPosition( - AXBoundaryBehavior::StopIfAlreadyAtBoundary); + AXBoundaryBehavior::kCrossBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTextPosition()); EXPECT_EQ(inline_box2_.id, test_position->anchor_id()); @@ -5953,7 +5972,7 @@ // CreatePreviousPageStartPosition should move back to the beginning of the // whole content. test_position = test_position->CreatePreviousPageStartPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTextPosition()); EXPECT_EQ(button_.id, test_position->anchor_id()); @@ -9303,11 +9322,11 @@ TestPositionType null_position = AXNodePosition::CreateNullPosition(); ASSERT_NE(nullptr, null_position); TestPositionType test_position = null_position->CreateNextCharacterPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsNullPosition()); test_position = null_position->CreatePreviousCharacterPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsNullPosition()); } @@ -9337,14 +9356,14 @@ // Test basic cases with static MaxTextOffset TestPositionType test_position = text_position->CreateNextCharacterPosition( - AXBoundaryBehavior::StopIfAlreadyAtBoundary); + AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); EXPECT_TRUE(test_position->IsValid()); ASSERT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTextPosition()); EXPECT_EQ(text_data.id, test_position->anchor_id()); EXPECT_EQ(9, test_position->text_offset()); test_position = text_position->CreateNextCharacterPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); ASSERT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsNullPosition()); @@ -9369,14 +9388,14 @@ // Now repeat the prior tests and ensure that we can create next character // positions with the new, valid MaxTextOffset (8). test_position = text_position->CreateNextCharacterPosition( - AXBoundaryBehavior::StopIfAlreadyAtBoundary); + AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); EXPECT_TRUE(test_position->IsValid()); ASSERT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTextPosition()); EXPECT_EQ(text_data.id, test_position->anchor_id()); EXPECT_EQ(8, test_position->text_offset()); test_position = text_position->CreateNextCharacterPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); ASSERT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsNullPosition()); @@ -9477,25 +9496,25 @@ ASSERT_TRUE(text_position->IsTextPosition()); TestPositionType test_position = text_position->CreateNextCharacterPosition( - AXBoundaryBehavior::StopIfAlreadyAtBoundary); + AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTextPosition()); EXPECT_EQ(inline_box1_.id, test_position->anchor_id()); EXPECT_EQ(4, test_position->text_offset()); test_position = text_position->CreateNextCharacterPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTextPosition()); EXPECT_EQ(inline_box1_.id, test_position->anchor_id()); EXPECT_EQ(5, test_position->text_offset()); test_position = text_position->CreateNextCharacterPosition( - AXBoundaryBehavior::StopAtAnchorBoundary); + AXBoundaryBehavior::kStopAtAnchorBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTextPosition()); EXPECT_EQ(inline_box1_.id, test_position->anchor_id()); EXPECT_EQ(5, test_position->text_offset()); test_position = text_position->CreateNextCharacterPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTextPosition()); EXPECT_EQ(inline_box1_.id, test_position->anchor_id()); @@ -9508,25 +9527,25 @@ ASSERT_TRUE(text_position->IsTextPosition()); test_position = text_position->CreateNextCharacterPosition( - AXBoundaryBehavior::StopIfAlreadyAtBoundary); + AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTextPosition()); EXPECT_EQ(inline_box1_.id, test_position->anchor_id()); EXPECT_EQ(5, test_position->text_offset()); test_position = text_position->CreateNextCharacterPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTextPosition()); EXPECT_EQ(inline_box1_.id, test_position->anchor_id()); EXPECT_EQ(6, test_position->text_offset()); test_position = text_position->CreateNextCharacterPosition( - AXBoundaryBehavior::StopAtAnchorBoundary); + AXBoundaryBehavior::kStopAtAnchorBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTextPosition()); EXPECT_EQ(inline_box1_.id, test_position->anchor_id()); EXPECT_EQ(6, test_position->text_offset()); test_position = text_position->CreateNextCharacterPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTextPosition()); EXPECT_EQ(inline_box1_.id, test_position->anchor_id()); @@ -9539,25 +9558,25 @@ ASSERT_TRUE(text_position->IsTextPosition()); test_position = text_position->CreateNextCharacterPosition( - AXBoundaryBehavior::StopIfAlreadyAtBoundary); + AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTextPosition()); EXPECT_EQ(inline_box1_.id, test_position->anchor_id()); EXPECT_EQ(6, test_position->text_offset()); test_position = text_position->CreateNextCharacterPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTextPosition()); EXPECT_EQ(line_break_.id, test_position->anchor_id()); EXPECT_EQ(1, test_position->text_offset()); test_position = text_position->CreateNextCharacterPosition( - AXBoundaryBehavior::StopAtAnchorBoundary); + AXBoundaryBehavior::kStopAtAnchorBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTextPosition()); EXPECT_EQ(inline_box1_.id, test_position->anchor_id()); EXPECT_EQ(6, test_position->text_offset()); test_position = text_position->CreateNextCharacterPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTextPosition()); EXPECT_EQ(line_break_.id, test_position->anchor_id()); @@ -9570,23 +9589,23 @@ ASSERT_TRUE(text_position->IsTextPosition()); test_position = text_position->CreateNextCharacterPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsNullPosition()); test_position = text_position->CreateNextCharacterPosition( - AXBoundaryBehavior::StopIfAlreadyAtBoundary); + AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTextPosition()); EXPECT_EQ(inline_box2_.id, test_position->anchor_id()); EXPECT_EQ(6, test_position->text_offset()); test_position = text_position->CreateNextCharacterPosition( - AXBoundaryBehavior::StopAtAnchorBoundary); + AXBoundaryBehavior::kStopAtAnchorBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTextPosition()); EXPECT_EQ(inline_box2_.id, test_position->anchor_id()); EXPECT_EQ(6, test_position->text_offset()); test_position = text_position->CreateNextCharacterPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTextPosition()); EXPECT_EQ(inline_box2_.id, test_position->anchor_id()); @@ -9599,25 +9618,25 @@ ASSERT_TRUE(text_position->IsTextPosition()); test_position = text_position->CreateNextCharacterPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTextPosition()); EXPECT_EQ(inline_box1_.id, test_position->anchor_id()); EXPECT_EQ(1, test_position->text_offset()); test_position = text_position->CreateNextCharacterPosition( - AXBoundaryBehavior::StopIfAlreadyAtBoundary); + AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTextPosition()); EXPECT_EQ(check_box_.id, test_position->anchor_id()); EXPECT_EQ(0, test_position->text_offset()); test_position = text_position->CreateNextCharacterPosition( - AXBoundaryBehavior::StopAtAnchorBoundary); + AXBoundaryBehavior::kStopAtAnchorBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTextPosition()); EXPECT_EQ(check_box_.id, test_position->anchor_id()); EXPECT_EQ(0, test_position->text_offset()); test_position = text_position->CreateNextCharacterPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTextPosition()); EXPECT_EQ(inline_box1_.id, test_position->anchor_id()); @@ -9630,7 +9649,7 @@ ASSERT_TRUE(text_position->IsTextPosition()); test_position = text_position->CreateNextCharacterPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTextPosition()); EXPECT_EQ(text_field_.id, test_position->anchor_id()); @@ -9645,7 +9664,7 @@ ASSERT_TRUE(text_position->IsTextPosition()); test_position = text_position->CreateNextCharacterPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTextPosition()); EXPECT_EQ(text_field_.id, test_position->anchor_id()); @@ -9663,25 +9682,25 @@ TestPositionType test_position = text_position->CreatePreviousCharacterPosition( - AXBoundaryBehavior::StopIfAlreadyAtBoundary); + AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTextPosition()); EXPECT_EQ(inline_box2_.id, test_position->anchor_id()); EXPECT_EQ(5, test_position->text_offset()); test_position = text_position->CreatePreviousCharacterPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTextPosition()); EXPECT_EQ(inline_box2_.id, test_position->anchor_id()); EXPECT_EQ(4, test_position->text_offset()); test_position = text_position->CreatePreviousCharacterPosition( - AXBoundaryBehavior::StopAtAnchorBoundary); + AXBoundaryBehavior::kStopAtAnchorBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTextPosition()); EXPECT_EQ(inline_box2_.id, test_position->anchor_id()); EXPECT_EQ(4, test_position->text_offset()); test_position = text_position->CreatePreviousCharacterPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTextPosition()); EXPECT_EQ(inline_box2_.id, test_position->anchor_id()); @@ -9694,25 +9713,25 @@ ASSERT_TRUE(text_position->IsTextPosition()); test_position = text_position->CreatePreviousCharacterPosition( - AXBoundaryBehavior::StopIfAlreadyAtBoundary); + AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTextPosition()); EXPECT_EQ(inline_box2_.id, test_position->anchor_id()); EXPECT_EQ(1, test_position->text_offset()); test_position = text_position->CreatePreviousCharacterPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTextPosition()); EXPECT_EQ(inline_box2_.id, test_position->anchor_id()); EXPECT_EQ(0, test_position->text_offset()); test_position = text_position->CreatePreviousCharacterPosition( - AXBoundaryBehavior::StopAtAnchorBoundary); + AXBoundaryBehavior::kStopAtAnchorBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTextPosition()); EXPECT_EQ(inline_box2_.id, test_position->anchor_id()); EXPECT_EQ(0, test_position->text_offset()); test_position = text_position->CreatePreviousCharacterPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTextPosition()); EXPECT_EQ(inline_box2_.id, test_position->anchor_id()); @@ -9725,25 +9744,25 @@ ASSERT_TRUE(text_position->IsTextPosition()); test_position = text_position->CreatePreviousCharacterPosition( - AXBoundaryBehavior::StopIfAlreadyAtBoundary); + AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTextPosition()); EXPECT_EQ(inline_box2_.id, test_position->anchor_id()); EXPECT_EQ(0, test_position->text_offset()); test_position = text_position->CreatePreviousCharacterPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTextPosition()); EXPECT_EQ(line_break_.id, test_position->anchor_id()); EXPECT_EQ(0, test_position->text_offset()); test_position = text_position->CreatePreviousCharacterPosition( - AXBoundaryBehavior::StopAtAnchorBoundary); + AXBoundaryBehavior::kStopAtAnchorBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTextPosition()); EXPECT_EQ(inline_box2_.id, test_position->anchor_id()); EXPECT_EQ(0, test_position->text_offset()); test_position = text_position->CreatePreviousCharacterPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTextPosition()); EXPECT_EQ(line_break_.id, test_position->anchor_id()); @@ -9756,23 +9775,23 @@ ASSERT_TRUE(text_position->IsTextPosition()); test_position = text_position->CreatePreviousCharacterPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsNullPosition()); test_position = text_position->CreatePreviousCharacterPosition( - AXBoundaryBehavior::StopIfAlreadyAtBoundary); + AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTextPosition()); EXPECT_EQ(inline_box1_.id, test_position->anchor_id()); EXPECT_EQ(0, test_position->text_offset()); test_position = text_position->CreatePreviousCharacterPosition( - AXBoundaryBehavior::StopAtAnchorBoundary); + AXBoundaryBehavior::kStopAtAnchorBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTextPosition()); EXPECT_EQ(inline_box1_.id, test_position->anchor_id()); EXPECT_EQ(0, test_position->text_offset()); test_position = text_position->CreatePreviousCharacterPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTextPosition()); EXPECT_EQ(inline_box1_.id, test_position->anchor_id()); @@ -9785,23 +9804,23 @@ ASSERT_TRUE(text_position->IsTextPosition()); test_position = text_position->CreatePreviousCharacterPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsNullPosition()); test_position = text_position->CreatePreviousCharacterPosition( - AXBoundaryBehavior::StopIfAlreadyAtBoundary); + AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTextPosition()); EXPECT_EQ(check_box_.id, test_position->anchor_id()); EXPECT_EQ(0, test_position->text_offset()); test_position = text_position->CreatePreviousCharacterPosition( - AXBoundaryBehavior::StopAtAnchorBoundary); + AXBoundaryBehavior::kStopAtAnchorBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTextPosition()); EXPECT_EQ(check_box_.id, test_position->anchor_id()); EXPECT_EQ(0, test_position->text_offset()); test_position = text_position->CreatePreviousCharacterPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTextPosition()); EXPECT_EQ(check_box_.id, test_position->anchor_id()); @@ -9814,7 +9833,7 @@ ASSERT_TRUE(text_position->IsTextPosition()); test_position = text_position->CreatePreviousCharacterPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTextPosition()); EXPECT_EQ(text_field_.id, test_position->anchor_id()); @@ -9837,7 +9856,7 @@ ++iter) { const int text_offset = *iter; test_position = test_position->CreateNextCharacterPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); ASSERT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTextPosition()); @@ -9855,7 +9874,7 @@ GetTreeID(), GetTree()->root()->id(), 3 /* text_offset */, ax::mojom::TextAffinity::kDownstream); test_position = test_position->CreateNextCharacterPosition( - AXBoundaryBehavior::StopIfAlreadyAtBoundary); + AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); ASSERT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTextPosition()); EXPECT_EQ(GetTree()->root()->id(), test_position->anchor_id()); @@ -9866,7 +9885,7 @@ GetTreeID(), GetTree()->root()->id(), 4 /* text_offset */, ax::mojom::TextAffinity::kDownstream); test_position = test_position->CreateNextCharacterPosition( - AXBoundaryBehavior::StopIfAlreadyAtBoundary); + AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); ASSERT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTextPosition()); EXPECT_EQ(GetTree()->root()->id(), test_position->anchor_id()); @@ -9877,7 +9896,7 @@ GetTreeID(), GetTree()->root()->id(), 9 /* text_offset */, ax::mojom::TextAffinity::kUpstream); test_position = test_position->CreateNextCharacterPosition( - AXBoundaryBehavior::StopIfAlreadyAtBoundary); + AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); ASSERT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTextPosition()); EXPECT_EQ(GetTree()->root()->id(), test_position->anchor_id()); @@ -9888,7 +9907,7 @@ GetTreeID(), GetTree()->root()->id(), 10 /* text_offset */, ax::mojom::TextAffinity::kUpstream); test_position = test_position->CreateNextCharacterPosition( - AXBoundaryBehavior::StopIfAlreadyAtBoundary); + AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); ASSERT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTextPosition()); EXPECT_EQ(GetTree()->root()->id(), test_position->anchor_id()); @@ -9912,7 +9931,7 @@ ++iter) { const int text_offset = *iter; test_position = test_position->CreatePreviousCharacterPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); ASSERT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTextPosition()); @@ -9930,7 +9949,7 @@ GetTreeID(), GetTree()->root()->id(), 3 /* text_offset */, ax::mojom::TextAffinity::kDownstream); test_position = test_position->CreatePreviousCharacterPosition( - AXBoundaryBehavior::StopIfAlreadyAtBoundary); + AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); ASSERT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTextPosition()); EXPECT_EQ(GetTree()->root()->id(), test_position->anchor_id()); @@ -9941,7 +9960,7 @@ GetTreeID(), GetTree()->root()->id(), 4 /* text_offset */, ax::mojom::TextAffinity::kDownstream); test_position = test_position->CreatePreviousCharacterPosition( - AXBoundaryBehavior::StopIfAlreadyAtBoundary); + AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); ASSERT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTextPosition()); EXPECT_EQ(GetTree()->root()->id(), test_position->anchor_id()); @@ -9952,7 +9971,7 @@ GetTreeID(), GetTree()->root()->id(), 9 /* text_offset */, ax::mojom::TextAffinity::kUpstream); test_position = test_position->CreatePreviousCharacterPosition( - AXBoundaryBehavior::StopIfAlreadyAtBoundary); + AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); ASSERT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTextPosition()); EXPECT_EQ(GetTree()->root()->id(), test_position->anchor_id()); @@ -9963,7 +9982,7 @@ GetTreeID(), GetTree()->root()->id(), 10 /* text_offset */, ax::mojom::TextAffinity::kUpstream); test_position = test_position->CreatePreviousCharacterPosition( - AXBoundaryBehavior::StopIfAlreadyAtBoundary); + AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); ASSERT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTextPosition()); EXPECT_EQ(GetTree()->root()->id(), test_position->anchor_id()); @@ -9983,7 +10002,7 @@ while (!text_position->IsNullPosition()) { TestPositionType moved_position = text_position->CreateNextCharacterPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); ASSERT_NE(nullptr, moved_position); text_position = std::move(moved_position); @@ -10000,7 +10019,7 @@ while (!text_position->IsNullPosition()) { TestPositionType moved_position = text_position->CreatePreviousCharacterPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); ASSERT_NE(nullptr, moved_position); text_position = std::move(moved_position); @@ -10015,11 +10034,11 @@ TestPositionType null_position = AXNodePosition::CreateNullPosition(); ASSERT_NE(nullptr, null_position); TestPositionType test_position = null_position->CreateNextWordStartPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsNullPosition()); test_position = null_position->CreatePreviousWordStartPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsNullPosition()); } @@ -10028,11 +10047,11 @@ TestPositionType null_position = AXNodePosition::CreateNullPosition(); ASSERT_NE(nullptr, null_position); TestPositionType test_position = null_position->CreateNextWordEndPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsNullPosition()); test_position = null_position->CreatePreviousWordEndPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsNullPosition()); } @@ -11070,7 +11089,7 @@ TestPositionType next_line_start_position = text_position->CreateNextLineStartPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); ASSERT_NE(nullptr, next_line_start_position); EXPECT_TRUE(next_line_start_position->IsTextPosition()); EXPECT_EQ(inline_box3.id, next_line_start_position->anchor_id()); @@ -11078,7 +11097,7 @@ TestPositionType previous_line_start_position = text_position->CreatePreviousLineStartPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); ASSERT_NE(nullptr, previous_line_start_position); EXPECT_TRUE(previous_line_start_position->IsTextPosition()); EXPECT_EQ(inline_box1.id, previous_line_start_position->anchor_id()); @@ -11086,7 +11105,7 @@ TestPositionType next_line_end_position = text_position->CreateNextLineEndPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); ASSERT_NE(nullptr, next_line_end_position); EXPECT_TRUE(next_line_end_position->IsTextPosition()); EXPECT_EQ(inline_box3.id, next_line_end_position->anchor_id()); @@ -11094,7 +11113,7 @@ TestPositionType previous_line_end_position = text_position->CreatePreviousLineEndPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); ASSERT_NE(nullptr, previous_line_end_position); EXPECT_TRUE(previous_line_end_position->IsTextPosition()); EXPECT_EQ(inline_box1.id, previous_line_end_position->anchor_id()); @@ -11229,7 +11248,7 @@ // "1. <f>irst item\n2. second item" text_position = text_position->CreateNextWordStartPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); ASSERT_NE(nullptr, text_position); ASSERT_TRUE(text_position->IsTextPosition()); ASSERT_EQ(inline_box2.id, text_position->anchor_id()); @@ -11237,7 +11256,7 @@ // "1. first <i>tem\n2. second item" text_position = text_position->CreateNextWordStartPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); ASSERT_NE(nullptr, text_position); ASSERT_TRUE(text_position->IsTextPosition()); ASSERT_EQ(inline_box2.id, text_position->anchor_id()); @@ -11245,7 +11264,7 @@ // "1. first item\n<2>. second item" text_position = text_position->CreateNextWordStartPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); ASSERT_NE(nullptr, text_position); ASSERT_TRUE(text_position->IsTextPosition()); ASSERT_EQ(inline_box3.id, text_position->anchor_id()); @@ -11253,7 +11272,7 @@ // "1. first item\n2. <s>econd item" text_position = text_position->CreateNextWordStartPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); ASSERT_NE(nullptr, text_position); ASSERT_TRUE(text_position->IsTextPosition()); ASSERT_EQ(inline_box4.id, text_position->anchor_id()); @@ -11261,7 +11280,7 @@ // "1. first item\n2. second <i>tem" text_position = text_position->CreateNextWordStartPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); ASSERT_NE(nullptr, text_position); ASSERT_TRUE(text_position->IsTextPosition()); ASSERT_EQ(inline_box4.id, text_position->anchor_id()); @@ -11396,7 +11415,7 @@ // "1. first item\n2. second <i>tem" text_position = text_position->CreatePreviousWordStartPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); ASSERT_NE(nullptr, text_position); ASSERT_TRUE(text_position->IsTextPosition()); ASSERT_EQ(inline_box4.id, text_position->anchor_id()); @@ -11404,7 +11423,7 @@ // "1. first item\n2. <s>econd item" text_position = text_position->CreatePreviousWordStartPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); ASSERT_NE(nullptr, text_position); ASSERT_TRUE(text_position->IsTextPosition()); ASSERT_EQ(inline_box4.id, text_position->anchor_id()); @@ -11412,7 +11431,7 @@ // "1. first item\n<2>. second item" text_position = text_position->CreatePreviousWordStartPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); ASSERT_NE(nullptr, text_position); ASSERT_TRUE(text_position->IsTextPosition()); ASSERT_EQ(inline_box3.id, text_position->anchor_id()); @@ -11420,7 +11439,7 @@ // "1. first <i>tem\n2. <s>econd item" text_position = text_position->CreatePreviousWordStartPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); ASSERT_NE(nullptr, text_position); ASSERT_TRUE(text_position->IsTextPosition()); ASSERT_EQ(inline_box2.id, text_position->anchor_id()); @@ -11428,7 +11447,7 @@ // "1. <f>irst item\n2. second item" text_position = text_position->CreatePreviousWordStartPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); ASSERT_NE(nullptr, text_position); ASSERT_TRUE(text_position->IsTextPosition()); ASSERT_EQ(inline_box2.id, text_position->anchor_id()); @@ -11436,7 +11455,7 @@ // "<1>. first item\n2. second item" text_position = text_position->CreatePreviousWordStartPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); ASSERT_NE(nullptr, text_position); ASSERT_TRUE(text_position->IsTextPosition()); ASSERT_EQ(inline_box1.id, text_position->anchor_id()); @@ -11573,7 +11592,7 @@ ax::mojom::TextAffinity::kDownstream); TestPositionType result_position = - position->CreateNextWordStartPosition(AXBoundaryBehavior::CrossBoundary); + position->CreateNextWordStartPosition(AXBoundaryBehavior::kCrossBoundary); EXPECT_TRUE(result_position->IsTextPosition()); EXPECT_EQ(text_field_4.id, result_position->anchor_id()); EXPECT_EQ(0, result_position->text_offset()); @@ -11582,7 +11601,7 @@ position = std::move(result_position); result_position = - position->CreateNextWordStartPosition(AXBoundaryBehavior::CrossBoundary); + position->CreateNextWordStartPosition(AXBoundaryBehavior::kCrossBoundary); EXPECT_TRUE(result_position->IsTextPosition()); EXPECT_EQ(inline_box_7.id, result_position->anchor_id()); EXPECT_EQ(1, result_position->text_offset()); @@ -11592,7 +11611,7 @@ // CreatePreviousWordStartPosition tests. position = std::move(result_position); result_position = position->CreatePreviousWordStartPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); EXPECT_TRUE(result_position->IsTextPosition()); EXPECT_EQ(text_field_4.id, result_position->anchor_id()); EXPECT_EQ(0, result_position->text_offset()); @@ -11601,7 +11620,7 @@ position = std::move(result_position); result_position = position->CreatePreviousWordStartPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); EXPECT_TRUE(result_position->IsTextPosition()); EXPECT_EQ(inline_box_3.id, result_position->anchor_id()); EXPECT_EQ(0, result_position->text_offset()); @@ -11611,7 +11630,7 @@ // CreateNextWordEndPosition tests. position = std::move(result_position); result_position = - position->CreateNextWordEndPosition(AXBoundaryBehavior::CrossBoundary); + position->CreateNextWordEndPosition(AXBoundaryBehavior::kCrossBoundary); EXPECT_TRUE(result_position->IsTextPosition()); EXPECT_EQ(inline_box_3.id, result_position->anchor_id()); EXPECT_EQ(6, result_position->text_offset()); @@ -11620,7 +11639,7 @@ position = std::move(result_position); result_position = - position->CreateNextWordEndPosition(AXBoundaryBehavior::CrossBoundary); + position->CreateNextWordEndPosition(AXBoundaryBehavior::kCrossBoundary); EXPECT_TRUE(result_position->IsTextPosition()); // The position would be on `text_field_4` instead of on `generic_container_5` // because the latter is ignored, and by design we prefer not to create @@ -11632,7 +11651,7 @@ position = std::move(result_position); result_position = - position->CreateNextWordEndPosition(AXBoundaryBehavior::CrossBoundary); + position->CreateNextWordEndPosition(AXBoundaryBehavior::kCrossBoundary); EXPECT_TRUE(result_position->IsTextPosition()); EXPECT_EQ(inline_box_7.id, result_position->anchor_id()); EXPECT_EQ(6, result_position->text_offset()); @@ -11642,7 +11661,7 @@ // CreatePreviousWordEndPosition tests. position = std::move(result_position); result_position = position->CreatePreviousWordEndPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); EXPECT_TRUE(result_position->IsTextPosition()); // The position would be on `text_field_4` instead of on `generic_container_5` // because the latter is ignored, and by design we prefer not to create @@ -11654,7 +11673,7 @@ position = std::move(result_position); result_position = position->CreatePreviousWordEndPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); EXPECT_TRUE(result_position->IsTextPosition()); EXPECT_EQ(inline_box_3.id, result_position->anchor_id()); EXPECT_EQ(6, result_position->text_offset()); @@ -11719,7 +11738,7 @@ ASSERT_NE(nullptr, text_position); text_position = text_position->CreatePreviousFormatStartPosition( - AXBoundaryBehavior::StopIfAlreadyAtBoundary); + AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); ASSERT_NE(nullptr, text_position); EXPECT_TRUE(text_position->IsTextPosition()); EXPECT_EQ(generic_container_12.id, text_position->anchor_id()); @@ -11741,7 +11760,7 @@ ASSERT_NE(nullptr, text_position); text_position = text_position->CreateNextParagraphEndPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); ASSERT_NE(nullptr, text_position); EXPECT_TRUE(text_position->IsLeafTextPosition()); EXPECT_EQ(button_14.id, text_position->anchor_id()); @@ -11846,10 +11865,14 @@ inline_box_8.id = 8; root_1.role = ax::mojom::Role::kRootWebArea; + root_1.AddBoolAttribute(ax::mojom::BoolAttribute::kIsLineBreakingObject, + true); root_1.child_ids = {static_text_2.id, popup_button_4.id, static_text_7.id}; static_text_2.role = ax::mojom::Role::kStaticText; static_text_2.SetName("Hi"); + static_text_2.AddBoolAttribute( + ax::mojom::BoolAttribute::kIsLineBreakingObject, true); static_text_2.child_ids = {inline_box_3.id}; inline_box_3.role = ax::mojom::Role::kInlineTextBox; @@ -11868,9 +11891,13 @@ menu_list_option_6.role = ax::mojom::Role::kMenuListOption; menu_list_option_6.SetName("Option"); menu_list_option_6.SetNameFrom(ax::mojom::NameFrom::kContents); + menu_list_option_6.AddBoolAttribute( + ax::mojom::BoolAttribute::kIsLineBreakingObject, true); static_text_7.role = ax::mojom::Role::kStaticText; static_text_7.SetName("3.14"); + static_text_7.AddBoolAttribute( + ax::mojom::BoolAttribute::kIsLineBreakingObject, true); static_text_7.child_ids = {inline_box_8.id}; inline_box_8.role = ax::mojom::Role::kInlineTextBox; @@ -11889,13 +11916,13 @@ ASSERT_NE(nullptr, position); position = position->CreateNextParagraphStartPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); ASSERT_NE(nullptr, position); EXPECT_EQ(popup_button_4.id, position->anchor_id()); EXPECT_EQ(0, position->text_offset()); position = position->CreateNextParagraphStartPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); ASSERT_NE(nullptr, position); EXPECT_EQ(inline_box_8.id, position->anchor_id()); EXPECT_EQ(0, position->text_offset()); @@ -11906,15 +11933,15 @@ ASSERT_NE(nullptr, position); position = position->CreatePreviousParagraphEndPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); ASSERT_NE(nullptr, position); EXPECT_EQ(popup_button_4.id, position->anchor_id()); // The content of this popup button should be replaced with the empty object - // character of length 1. + // replacement character of length 1. EXPECT_EQ(1, position->text_offset()); position = position->CreatePreviousParagraphEndPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); ASSERT_NE(nullptr, position); EXPECT_EQ(inline_box_3.id, position->anchor_id()); EXPECT_EQ(2, position->text_offset()); @@ -11932,13 +11959,13 @@ ASSERT_NE(nullptr, position); position = position->CreateNextParagraphStartPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); ASSERT_NE(nullptr, position); EXPECT_EQ(menu_list_option_6.id, position->anchor_id()); EXPECT_EQ(0, position->text_offset()); position = position->CreateNextParagraphStartPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); ASSERT_NE(nullptr, position); EXPECT_EQ(inline_box_8.id, position->anchor_id()); EXPECT_EQ(0, position->text_offset()); @@ -11949,13 +11976,13 @@ ASSERT_NE(nullptr, position); position = position->CreatePreviousParagraphEndPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); ASSERT_NE(nullptr, position); EXPECT_EQ(menu_list_option_6.id, position->anchor_id()); - EXPECT_EQ(1, position->text_offset()); + EXPECT_EQ(6, position->text_offset()); position = position->CreatePreviousParagraphEndPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); ASSERT_NE(nullptr, position); EXPECT_EQ(inline_box_3.id, position->anchor_id()); EXPECT_EQ(2, position->text_offset()); @@ -12108,32 +12135,32 @@ ExpandToEnclosingTextBoundaryTestParam{ ax::mojom::TextBoundary::kCharacter, AXRangeExpandBehavior::kLeftFirst, - "TextPosition anchor_id=4 text_offset=6 affinity=downstream " - "annotated_text=Line 1<\n>Line 2", - "TextPosition anchor_id=4 text_offset=7 affinity=downstream " - "annotated_text=Line 1\n<L>ine 2"}, - ExpandToEnclosingTextBoundaryTestParam{ - ax::mojom::TextBoundary::kCharacter, - AXRangeExpandBehavior::kRightFirst, "TextPosition anchor_id=4 text_offset=7 affinity=downstream " "annotated_text=Line 1\n<L>ine 2", "TextPosition anchor_id=4 text_offset=8 affinity=downstream " "annotated_text=Line 1\nL<i>ne 2"}, ExpandToEnclosingTextBoundaryTestParam{ - ax::mojom::TextBoundary::kFormatEnd, - AXRangeExpandBehavior::kLeftFirst, + ax::mojom::TextBoundary::kCharacter, + AXRangeExpandBehavior::kRightFirst, "TextPosition anchor_id=4 text_offset=6 affinity=downstream " "annotated_text=Line 1<\n>Line 2", "TextPosition anchor_id=4 text_offset=7 affinity=downstream " "annotated_text=Line 1\n<L>ine 2"}, ExpandToEnclosingTextBoundaryTestParam{ ax::mojom::TextBoundary::kFormatEnd, - AXRangeExpandBehavior::kRightFirst, - "TextPosition anchor_id=4 text_offset=7 affinity=downstream " + AXRangeExpandBehavior::kLeftFirst, + "TextPosition anchor_id=4 text_offset=7 affinity=upstream " "annotated_text=Line 1\n<L>ine 2", "TextPosition anchor_id=4 text_offset=13 affinity=downstream " "annotated_text=Line 1\nLine 2<>"}, ExpandToEnclosingTextBoundaryTestParam{ + ax::mojom::TextBoundary::kFormatEnd, + AXRangeExpandBehavior::kRightFirst, + "TextPosition anchor_id=4 text_offset=6 affinity=downstream " + "annotated_text=Line 1<\n>Line 2", + "TextPosition anchor_id=4 text_offset=7 affinity=upstream " + "annotated_text=Line 1\n<L>ine 2"}, + ExpandToEnclosingTextBoundaryTestParam{ ax::mojom::TextBoundary::kLineEnd, AXRangeExpandBehavior::kLeftFirst, "TextPosition anchor_id=4 text_offset=6 affinity=downstream " @@ -12150,24 +12177,24 @@ ExpandToEnclosingTextBoundaryTestParam{ ax::mojom::TextBoundary::kLineStart, AXRangeExpandBehavior::kLeftFirst, + "TextPosition anchor_id=4 text_offset=7 affinity=downstream " + "annotated_text=Line 1\n<L>ine 2", + "TextPosition anchor_id=4 text_offset=13 affinity=downstream " + "annotated_text=Line 1\nLine 2<>"}, + ExpandToEnclosingTextBoundaryTestParam{ + ax::mojom::TextBoundary::kLineStart, + AXRangeExpandBehavior::kRightFirst, "TextPosition anchor_id=4 text_offset=0 affinity=downstream " "annotated_text=<L>ine 1\nLine 2", "TextPosition anchor_id=4 text_offset=7 affinity=downstream " "annotated_text=Line 1\n<L>ine 2"}, ExpandToEnclosingTextBoundaryTestParam{ - ax::mojom::TextBoundary::kLineStart, - AXRangeExpandBehavior::kRightFirst, - "TextPosition anchor_id=4 text_offset=7 affinity=downstream " - "annotated_text=Line 1\n<L>ine 2", - "TextPosition anchor_id=4 text_offset=13 affinity=downstream " - "annotated_text=Line 1\nLine 2<>"}, - ExpandToEnclosingTextBoundaryTestParam{ ax::mojom::TextBoundary::kLineStartOrEnd, AXRangeExpandBehavior::kLeftFirst, - "TextPosition anchor_id=4 text_offset=0 affinity=downstream " - "annotated_text=<L>ine 1\nLine 2", - "TextPosition anchor_id=4 text_offset=6 affinity=downstream " - "annotated_text=Line 1<\n>Line 2"}, + "TextPosition anchor_id=4 text_offset=7 affinity=downstream " + "annotated_text=Line 1\n<L>ine 2", + "TextPosition anchor_id=4 text_offset=13 affinity=downstream " + "annotated_text=Line 1\nLine 2<>"}, ExpandToEnclosingTextBoundaryTestParam{ ax::mojom::TextBoundary::kLineStartOrEnd, AXRangeExpandBehavior::kRightFirst, @@ -12205,81 +12232,122 @@ ExpandToEnclosingTextBoundaryTestParam{ ax::mojom::TextBoundary::kParagraphStart, AXRangeExpandBehavior::kLeftFirst, - "TextPosition anchor_id=4 text_offset=0 affinity=downstream " - "annotated_text=<L>ine 1\nLine 2", "TextPosition anchor_id=4 text_offset=7 affinity=downstream " - "annotated_text=Line 1\n<L>ine 2"}, + "annotated_text=Line 1\n<L>ine 2", + "TextPosition anchor_id=4 text_offset=13 affinity=downstream " + "annotated_text=Line 1\nLine 2<>"}, ExpandToEnclosingTextBoundaryTestParam{ ax::mojom::TextBoundary::kParagraphStart, AXRangeExpandBehavior::kRightFirst, - "TextPosition anchor_id=4 text_offset=7 affinity=downstream " - "annotated_text=Line 1\n<L>ine 2", - "TextPosition anchor_id=4 text_offset=13 affinity=downstream " - "annotated_text=Line 1\nLine 2<>"}, - ExpandToEnclosingTextBoundaryTestParam{ - ax::mojom::TextBoundary::kParagraphStartOrEnd, - AXRangeExpandBehavior::kLeftFirst, "TextPosition anchor_id=4 text_offset=0 affinity=downstream " "annotated_text=<L>ine 1\nLine 2", - "TextPosition anchor_id=4 text_offset=6 affinity=downstream " - "annotated_text=Line 1<\n>Line 2"}, - ExpandToEnclosingTextBoundaryTestParam{ - ax::mojom::TextBoundary::kParagraphStartOrEnd, - AXRangeExpandBehavior::kRightFirst, - "TextPosition anchor_id=4 text_offset=7 affinity=downstream " - "annotated_text=Line 1\n<L>ine 2", - "TextPosition anchor_id=4 text_offset=13 affinity=downstream " - "annotated_text=Line 1\nLine 2<>"}, - // TODO(accessibility): Add tests for sentence boundary. - ExpandToEnclosingTextBoundaryTestParam{ - ax::mojom::TextBoundary::kWebPage, - AXRangeExpandBehavior::kLeftFirst, - "TextPosition anchor_id=1 text_offset=0 affinity=downstream " - "annotated_text=<L>ine 1\nLine 2", - "TextPosition anchor_id=9 text_offset=6 affinity=downstream " - "annotated_text=Line 2<>"}, - ExpandToEnclosingTextBoundaryTestParam{ - ax::mojom::TextBoundary::kWebPage, - AXRangeExpandBehavior::kRightFirst, - "TextPosition anchor_id=1 text_offset=0 affinity=downstream " - "annotated_text=<L>ine 1\nLine 2", - "TextPosition anchor_id=9 text_offset=6 affinity=downstream " - "annotated_text=Line 2<>"}, - ExpandToEnclosingTextBoundaryTestParam{ - ax::mojom::TextBoundary::kWordEnd, - AXRangeExpandBehavior::kLeftFirst, - "TextPosition anchor_id=4 text_offset=6 affinity=downstream " - "annotated_text=Line 1<\n>Line 2", - "TextPosition anchor_id=4 text_offset=11 affinity=downstream " - "annotated_text=Line 1\nLine< >2"}, - ExpandToEnclosingTextBoundaryTestParam{ - ax::mojom::TextBoundary::kWordEnd, - AXRangeExpandBehavior::kRightFirst, - "TextPosition anchor_id=4 text_offset=6 affinity=downstream " - "annotated_text=Line 1<\n>Line 2", - "TextPosition anchor_id=4 text_offset=11 affinity=downstream " - "annotated_text=Line 1\nLine< >2"}, - ExpandToEnclosingTextBoundaryTestParam{ - ax::mojom::TextBoundary::kWordStart, - AXRangeExpandBehavior::kLeftFirst, - "TextPosition anchor_id=4 text_offset=5 affinity=downstream " - "annotated_text=Line <1>\nLine 2", "TextPosition anchor_id=4 text_offset=7 affinity=downstream " "annotated_text=Line 1\n<L>ine 2"}, ExpandToEnclosingTextBoundaryTestParam{ - ax::mojom::TextBoundary::kWordStart, + ax::mojom::TextBoundary::kParagraphStartOrEnd, + AXRangeExpandBehavior::kLeftFirst, + "TextPosition anchor_id=4 text_offset=7 affinity=downstream " + "annotated_text=Line 1\n<L>ine 2", + "TextPosition anchor_id=4 text_offset=13 affinity=downstream " + "annotated_text=Line 1\nLine 2<>"}, + ExpandToEnclosingTextBoundaryTestParam{ + ax::mojom::TextBoundary::kParagraphStartOrEnd, AXRangeExpandBehavior::kRightFirst, "TextPosition anchor_id=4 text_offset=7 affinity=downstream " "annotated_text=Line 1\n<L>ine 2", + "TextPosition anchor_id=4 text_offset=13 affinity=downstream " + "annotated_text=Line 1\nLine 2<>"}, + ExpandToEnclosingTextBoundaryTestParam{ + ax::mojom::TextBoundary::kSentenceEnd, + AXRangeExpandBehavior::kLeftFirst, + "TextPosition anchor_id=4 text_offset=6 affinity=downstream " + "annotated_text=Line 1<\n>Line 2", + "TextPosition anchor_id=4 text_offset=13 affinity=downstream " + "annotated_text=Line 1\nLine 2<>"}, + ExpandToEnclosingTextBoundaryTestParam{ + ax::mojom::TextBoundary::kSentenceEnd, + AXRangeExpandBehavior::kRightFirst, + "TextPosition anchor_id=4 text_offset=6 affinity=downstream " + "annotated_text=Line 1<\n>Line 2", + "TextPosition anchor_id=4 text_offset=13 affinity=downstream " + "annotated_text=Line 1\nLine 2<>"}, + ExpandToEnclosingTextBoundaryTestParam{ + ax::mojom::TextBoundary::kSentenceStart, + AXRangeExpandBehavior::kLeftFirst, + "TextPosition anchor_id=4 text_offset=7 affinity=downstream " + "annotated_text=Line 1\n<L>ine 2", + "TextPosition anchor_id=4 text_offset=13 affinity=downstream " + "annotated_text=Line 1\nLine 2<>"}, + ExpandToEnclosingTextBoundaryTestParam{ + ax::mojom::TextBoundary::kSentenceStart, + AXRangeExpandBehavior::kRightFirst, + "TextPosition anchor_id=4 text_offset=0 affinity=downstream " + "annotated_text=<L>ine 1\nLine 2", + "TextPosition anchor_id=4 text_offset=7 affinity=downstream " + "annotated_text=Line 1\n<L>ine 2"}, + ExpandToEnclosingTextBoundaryTestParam{ + ax::mojom::TextBoundary::kSentenceStartOrEnd, + AXRangeExpandBehavior::kLeftFirst, + "TextPosition anchor_id=4 text_offset=7 affinity=downstream " + "annotated_text=Line 1\n<L>ine 2", + "TextPosition anchor_id=4 text_offset=13 affinity=downstream " + "annotated_text=Line 1\nLine 2<>"}, + ExpandToEnclosingTextBoundaryTestParam{ + ax::mojom::TextBoundary::kSentenceStartOrEnd, + AXRangeExpandBehavior::kRightFirst, + "TextPosition anchor_id=4 text_offset=7 affinity=downstream " + "annotated_text=Line 1\n<L>ine 2", + "TextPosition anchor_id=4 text_offset=13 affinity=downstream " + "annotated_text=Line 1\nLine 2<>"}, + ExpandToEnclosingTextBoundaryTestParam{ + ax::mojom::TextBoundary::kWebPage, + AXRangeExpandBehavior::kLeftFirst, + "TextPosition anchor_id=1 text_offset=0 affinity=downstream " + "annotated_text=<L>ine 1\nLine 2", + "TextPosition anchor_id=9 text_offset=6 affinity=downstream " + "annotated_text=Line 2<>"}, + ExpandToEnclosingTextBoundaryTestParam{ + ax::mojom::TextBoundary::kWebPage, + AXRangeExpandBehavior::kRightFirst, + "TextPosition anchor_id=1 text_offset=0 affinity=downstream " + "annotated_text=<L>ine 1\nLine 2", + "TextPosition anchor_id=9 text_offset=6 affinity=downstream " + "annotated_text=Line 2<>"}, + ExpandToEnclosingTextBoundaryTestParam{ + ax::mojom::TextBoundary::kWordEnd, + AXRangeExpandBehavior::kLeftFirst, + "TextPosition anchor_id=4 text_offset=6 affinity=downstream " + "annotated_text=Line 1<\n>Line 2", + "TextPosition anchor_id=4 text_offset=11 affinity=downstream " + "annotated_text=Line 1\nLine< >2"}, + ExpandToEnclosingTextBoundaryTestParam{ + ax::mojom::TextBoundary::kWordEnd, + AXRangeExpandBehavior::kRightFirst, + "TextPosition anchor_id=4 text_offset=6 affinity=downstream " + "annotated_text=Line 1<\n>Line 2", + "TextPosition anchor_id=4 text_offset=11 affinity=downstream " + "annotated_text=Line 1\nLine< >2"}, + ExpandToEnclosingTextBoundaryTestParam{ + ax::mojom::TextBoundary::kWordStart, + AXRangeExpandBehavior::kLeftFirst, + "TextPosition anchor_id=4 text_offset=7 affinity=downstream " + "annotated_text=Line 1\n<L>ine 2", "TextPosition anchor_id=4 text_offset=12 affinity=downstream " "annotated_text=Line 1\nLine <2>"}, ExpandToEnclosingTextBoundaryTestParam{ - ax::mojom::TextBoundary::kWordStartOrEnd, - AXRangeExpandBehavior::kLeftFirst, + ax::mojom::TextBoundary::kWordStart, + AXRangeExpandBehavior::kRightFirst, "TextPosition anchor_id=4 text_offset=5 affinity=downstream " "annotated_text=Line <1>\nLine 2", - "TextPosition anchor_id=4 text_offset=6 affinity=downstream " - "annotated_text=Line 1<\n>Line 2"}, + "TextPosition anchor_id=4 text_offset=7 affinity=downstream " + "annotated_text=Line 1\n<L>ine 2"}, + ExpandToEnclosingTextBoundaryTestParam{ + ax::mojom::TextBoundary::kWordStartOrEnd, + AXRangeExpandBehavior::kLeftFirst, + "TextPosition anchor_id=4 text_offset=7 affinity=downstream " + "annotated_text=Line 1\n<L>ine 2", + "TextPosition anchor_id=4 text_offset=11 affinity=downstream " + "annotated_text=Line 1\nLine< >2"}, ExpandToEnclosingTextBoundaryTestParam{ ax::mojom::TextBoundary::kWordStartOrEnd, AXRangeExpandBehavior::kRightFirst, @@ -12288,7 +12356,7 @@ "TextPosition anchor_id=4 text_offset=11 affinity=downstream " "annotated_text=Line 1\nLine< >2"})); -// Only test with AXBoundaryBehavior::CrossBoundary for now. +// Only test with AXBoundaryBehavior::kCrossBoundary for now. // TODO(accessibility): Add more tests for other boundary behaviors if needed. INSTANTIATE_TEST_SUITE_P( CreatePositionAtTextBoundary, @@ -12297,165 +12365,1028 @@ CreatePositionAtTextBoundaryTestParam{ ax::mojom::TextBoundary::kCharacter, ax::mojom::MoveDirection::kBackward, - AXBoundaryBehavior::CrossBoundary, + AXBoundaryBehavior::kCrossBoundary, "TextPosition anchor_id=7 text_offset=0 affinity=downstream " "annotated_text=<\n>"}, CreatePositionAtTextBoundaryTestParam{ ax::mojom::TextBoundary::kCharacter, ax::mojom::MoveDirection::kForward, - AXBoundaryBehavior::CrossBoundary, + AXBoundaryBehavior::kCrossBoundary, "TextPosition anchor_id=8 text_offset=1 affinity=downstream " "annotated_text=L<i>ne 2"}, CreatePositionAtTextBoundaryTestParam{ ax::mojom::TextBoundary::kFormatStart, ax::mojom::MoveDirection::kBackward, - AXBoundaryBehavior::CrossBoundary, + AXBoundaryBehavior::kCrossBoundary, "TextPosition anchor_id=7 text_offset=0 affinity=downstream " "annotated_text=<\n>"}, CreatePositionAtTextBoundaryTestParam{ ax::mojom::TextBoundary::kFormatEnd, ax::mojom::MoveDirection::kForward, - AXBoundaryBehavior::CrossBoundary, + AXBoundaryBehavior::kCrossBoundary, "TextPosition anchor_id=8 text_offset=6 affinity=downstream " "annotated_text=Line 2<>"}, CreatePositionAtTextBoundaryTestParam{ ax::mojom::TextBoundary::kLineEnd, ax::mojom::MoveDirection::kBackward, - AXBoundaryBehavior::CrossBoundary, + AXBoundaryBehavior::kCrossBoundary, "TextPosition anchor_id=7 text_offset=0 affinity=downstream " "annotated_text=<\n>"}, CreatePositionAtTextBoundaryTestParam{ ax::mojom::TextBoundary::kLineEnd, ax::mojom::MoveDirection::kForward, - AXBoundaryBehavior::CrossBoundary, + AXBoundaryBehavior::kCrossBoundary, "TextPosition anchor_id=8 text_offset=6 affinity=downstream " "annotated_text=Line 2<>"}, CreatePositionAtTextBoundaryTestParam{ ax::mojom::TextBoundary::kLineStart, ax::mojom::MoveDirection::kBackward, - AXBoundaryBehavior::CrossBoundary, + AXBoundaryBehavior::kCrossBoundary, "TextPosition anchor_id=6 text_offset=0 affinity=downstream " "annotated_text=<L>ine 1"}, CreatePositionAtTextBoundaryTestParam{ ax::mojom::TextBoundary::kLineStart, ax::mojom::MoveDirection::kForward, - AXBoundaryBehavior::CrossBoundary, "NullPosition"}, + AXBoundaryBehavior::kCrossBoundary, "NullPosition"}, CreatePositionAtTextBoundaryTestParam{ ax::mojom::TextBoundary::kLineStartOrEnd, ax::mojom::MoveDirection::kBackward, - AXBoundaryBehavior::CrossBoundary, + AXBoundaryBehavior::kCrossBoundary, "TextPosition anchor_id=6 text_offset=0 affinity=downstream " "annotated_text=<L>ine 1"}, CreatePositionAtTextBoundaryTestParam{ ax::mojom::TextBoundary::kLineStartOrEnd, ax::mojom::MoveDirection::kForward, - AXBoundaryBehavior::CrossBoundary, + AXBoundaryBehavior::kCrossBoundary, "TextPosition anchor_id=8 text_offset=6 affinity=downstream " "annotated_text=Line 2<>"}, CreatePositionAtTextBoundaryTestParam{ ax::mojom::TextBoundary::kObject, ax::mojom::MoveDirection::kBackward, - AXBoundaryBehavior::CrossBoundary, + AXBoundaryBehavior::kCrossBoundary, "TextPosition anchor_id=8 text_offset=0 affinity=downstream " "annotated_text=<L>ine 2"}, CreatePositionAtTextBoundaryTestParam{ ax::mojom::TextBoundary::kObject, ax::mojom::MoveDirection::kForward, - AXBoundaryBehavior::CrossBoundary, + AXBoundaryBehavior::kCrossBoundary, "TextPosition anchor_id=8 text_offset=6 affinity=downstream " "annotated_text=Line 2<>"}, CreatePositionAtTextBoundaryTestParam{ ax::mojom::TextBoundary::kParagraphEnd, ax::mojom::MoveDirection::kBackward, - AXBoundaryBehavior::CrossBoundary, + AXBoundaryBehavior::kCrossBoundary, "TextPosition anchor_id=6 text_offset=6 affinity=downstream " "annotated_text=Line 1<>"}, CreatePositionAtTextBoundaryTestParam{ ax::mojom::TextBoundary::kParagraphEnd, ax::mojom::MoveDirection::kForward, - AXBoundaryBehavior::CrossBoundary, + AXBoundaryBehavior::kCrossBoundary, "TextPosition anchor_id=8 text_offset=6 affinity=downstream " "annotated_text=Line 2<>"}, CreatePositionAtTextBoundaryTestParam{ ax::mojom::TextBoundary::kParagraphStart, ax::mojom::MoveDirection::kBackward, - AXBoundaryBehavior::CrossBoundary, + AXBoundaryBehavior::kCrossBoundary, "TextPosition anchor_id=6 text_offset=0 affinity=downstream " "annotated_text=<L>ine 1"}, CreatePositionAtTextBoundaryTestParam{ ax::mojom::TextBoundary::kParagraphStart, ax::mojom::MoveDirection::kForward, - AXBoundaryBehavior::CrossBoundary, "NullPosition"}, + AXBoundaryBehavior::kCrossBoundary, "NullPosition"}, CreatePositionAtTextBoundaryTestParam{ ax::mojom::TextBoundary::kParagraphStartOrEnd, ax::mojom::MoveDirection::kBackward, - AXBoundaryBehavior::CrossBoundary, + AXBoundaryBehavior::kCrossBoundary, "TextPosition anchor_id=6 text_offset=0 affinity=downstream " "annotated_text=<L>ine 1"}, CreatePositionAtTextBoundaryTestParam{ ax::mojom::TextBoundary::kParagraphStartOrEnd, ax::mojom::MoveDirection::kForward, - AXBoundaryBehavior::CrossBoundary, + AXBoundaryBehavior::kCrossBoundary, "TextPosition anchor_id=8 text_offset=6 affinity=downstream " "annotated_text=Line 2<>"}, - // TODO(accessibility): Add tests for sentence boundary. + CreatePositionAtTextBoundaryTestParam{ + ax::mojom::TextBoundary::kSentenceEnd, + ax::mojom::MoveDirection::kBackward, + AXBoundaryBehavior::kCrossBoundary, + "TextPosition anchor_id=6 text_offset=6 affinity=downstream " + "annotated_text=Line 1<>"}, + CreatePositionAtTextBoundaryTestParam{ + ax::mojom::TextBoundary::kSentenceEnd, + ax::mojom::MoveDirection::kForward, + AXBoundaryBehavior::kCrossBoundary, + "TextPosition anchor_id=8 text_offset=6 affinity=downstream " + "annotated_text=Line 2<>"}, + CreatePositionAtTextBoundaryTestParam{ + ax::mojom::TextBoundary::kSentenceStart, + ax::mojom::MoveDirection::kBackward, + AXBoundaryBehavior::kCrossBoundary, + "TextPosition anchor_id=6 text_offset=0 affinity=downstream " + "annotated_text=<L>ine 1"}, + CreatePositionAtTextBoundaryTestParam{ + ax::mojom::TextBoundary::kSentenceStart, + ax::mojom::MoveDirection::kForward, + AXBoundaryBehavior::kCrossBoundary, "NullPosition"}, + CreatePositionAtTextBoundaryTestParam{ + ax::mojom::TextBoundary::kSentenceStartOrEnd, + ax::mojom::MoveDirection::kBackward, + AXBoundaryBehavior::kCrossBoundary, + "TextPosition anchor_id=6 text_offset=0 affinity=downstream " + "annotated_text=<L>ine 1"}, + CreatePositionAtTextBoundaryTestParam{ + ax::mojom::TextBoundary::kSentenceStartOrEnd, + ax::mojom::MoveDirection::kForward, + AXBoundaryBehavior::kCrossBoundary, + "TextPosition anchor_id=8 text_offset=6 affinity=downstream " + "annotated_text=Line 2<>"}, CreatePositionAtTextBoundaryTestParam{ ax::mojom::TextBoundary::kWebPage, ax::mojom::MoveDirection::kBackward, - AXBoundaryBehavior::CrossBoundary, + AXBoundaryBehavior::kCrossBoundary, "TextPosition anchor_id=1 text_offset=0 affinity=downstream " "annotated_text=<L>ine 1\nLine 2"}, CreatePositionAtTextBoundaryTestParam{ ax::mojom::TextBoundary::kWebPage, ax::mojom::MoveDirection::kForward, - AXBoundaryBehavior::CrossBoundary, + AXBoundaryBehavior::kCrossBoundary, "TextPosition anchor_id=9 text_offset=6 affinity=downstream " "annotated_text=Line 2<>"}, CreatePositionAtTextBoundaryTestParam{ ax::mojom::TextBoundary::kWordEnd, ax::mojom::MoveDirection::kBackward, - AXBoundaryBehavior::CrossBoundary, + AXBoundaryBehavior::kCrossBoundary, "TextPosition anchor_id=6 text_offset=6 affinity=downstream " "annotated_text=Line 1<>"}, CreatePositionAtTextBoundaryTestParam{ ax::mojom::TextBoundary::kWordEnd, ax::mojom::MoveDirection::kForward, - AXBoundaryBehavior::CrossBoundary, + AXBoundaryBehavior::kCrossBoundary, "TextPosition anchor_id=8 text_offset=4 affinity=downstream " "annotated_text=Line< >2"}, CreatePositionAtTextBoundaryTestParam{ ax::mojom::TextBoundary::kWordStart, ax::mojom::MoveDirection::kBackward, - AXBoundaryBehavior::CrossBoundary, + AXBoundaryBehavior::kCrossBoundary, "TextPosition anchor_id=6 text_offset=5 affinity=downstream " "annotated_text=Line <1>"}, CreatePositionAtTextBoundaryTestParam{ ax::mojom::TextBoundary::kWordStart, ax::mojom::MoveDirection::kForward, - AXBoundaryBehavior::CrossBoundary, + AXBoundaryBehavior::kCrossBoundary, "TextPosition anchor_id=8 text_offset=5 affinity=downstream " "annotated_text=Line <2>"}, CreatePositionAtTextBoundaryTestParam{ ax::mojom::TextBoundary::kWordStartOrEnd, ax::mojom::MoveDirection::kBackward, - AXBoundaryBehavior::CrossBoundary, + AXBoundaryBehavior::kCrossBoundary, "TextPosition anchor_id=6 text_offset=5 affinity=downstream " "annotated_text=Line <1>"}, CreatePositionAtTextBoundaryTestParam{ ax::mojom::TextBoundary::kWordStartOrEnd, ax::mojom::MoveDirection::kForward, - AXBoundaryBehavior::CrossBoundary, + AXBoundaryBehavior::kCrossBoundary, "TextPosition anchor_id=8 text_offset=4 affinity=downstream " "annotated_text=Line< >2"})); INSTANTIATE_TEST_SUITE_P( + CreateNextSentenceStartPositionWithBoundaryBehaviorCrossBoundary, + AXPositionTextNavigationTestWithParam, + ::testing::Values( + TextNavigationTestParam{ + base::BindRepeating([](const TestPositionType& position) { + return position->CreateNextSentenceStartPosition( + AXBoundaryBehavior::kCrossBoundary); + }), + ROOT_ID, + 0 /* text_offset */, + {"TextPosition anchor_id=1 text_offset=7 " + "affinity=downstream annotated_text=Line 1\n<L>ine 2", + "NullPosition"}}, + TextNavigationTestParam{ + base::BindRepeating([](const TestPositionType& position) { + return position->CreateNextSentenceStartPosition( + AXBoundaryBehavior::kCrossBoundary); + }), + TEXT_FIELD_ID, + 0 /* text_offset */, + {"TextPosition anchor_id=4 text_offset=7 " + "affinity=downstream annotated_text=Line 1\n<L>ine 2", + "NullPosition"}}, + TextNavigationTestParam{ + base::BindRepeating([](const TestPositionType& position) { + return position->CreateNextSentenceStartPosition( + AXBoundaryBehavior::kCrossBoundary); + }), + STATIC_TEXT1_ID, + 1 /* text_offset */, + {"TextPosition anchor_id=9 text_offset=0 " + "affinity=downstream annotated_text=<L>ine 2", + "NullPosition"}}, + TextNavigationTestParam{ + base::BindRepeating([](const TestPositionType& position) { + return position->CreateNextSentenceStartPosition( + AXBoundaryBehavior::kCrossBoundary); + }), + INLINE_BOX2_ID, + 4 /* text_offset */, + {"NullPosition"}})); + +INSTANTIATE_TEST_SUITE_P( + CreateNextSentenceStartPositionWithBoundaryBehaviorStopAtAnchorBoundary, + AXPositionTextNavigationTestWithParam, + ::testing::Values( + TextNavigationTestParam{ + base::BindRepeating([](const TestPositionType& position) { + return position->CreateNextSentenceStartPosition( + AXBoundaryBehavior::kStopAtAnchorBoundary); + }), + ROOT_ID, + 0 /* text_offset */, + {"TextPosition anchor_id=1 text_offset=7 " + "affinity=downstream annotated_text=Line 1\n<L>ine 2", + "TextPosition anchor_id=1 text_offset=13 " + "affinity=downstream annotated_text=Line 1\nLine 2<>", + "TextPosition anchor_id=1 text_offset=13 " + "affinity=downstream annotated_text=Line 1\nLine 2<>"}}, + TextNavigationTestParam{ + base::BindRepeating([](const TestPositionType& position) { + return position->CreateNextSentenceStartPosition( + AXBoundaryBehavior::kStopAtAnchorBoundary); + }), + TEXT_FIELD_ID, + 0 /* text_offset */, + {"TextPosition anchor_id=4 text_offset=7 " + "affinity=downstream annotated_text=Line 1\n<L>ine 2", + "TextPosition anchor_id=4 text_offset=13 " + "affinity=downstream annotated_text=Line 1\nLine 2<>", + "TextPosition anchor_id=4 text_offset=13 " + "affinity=downstream annotated_text=Line 1\nLine 2<>"}}, + TextNavigationTestParam{ + base::BindRepeating([](const TestPositionType& position) { + return position->CreateNextSentenceStartPosition( + AXBoundaryBehavior::kStopAtAnchorBoundary); + }), + STATIC_TEXT1_ID, + 1 /* text_offset */, + {"TextPosition anchor_id=5 text_offset=6 " + "affinity=downstream annotated_text=Line 1<>", + "TextPosition anchor_id=5 text_offset=6 " + "affinity=downstream annotated_text=Line 1<>"}}, + TextNavigationTestParam{ + base::BindRepeating([](const TestPositionType& position) { + return position->CreateNextSentenceStartPosition( + AXBoundaryBehavior::kStopAtAnchorBoundary); + }), + INLINE_BOX2_ID, + 4 /* text_offset */, + {"TextPosition anchor_id=9 text_offset=6 " + "affinity=downstream annotated_text=Line 2<>", + "TextPosition anchor_id=9 text_offset=6 " + "affinity=downstream annotated_text=Line 2<>"}})); + +INSTANTIATE_TEST_SUITE_P( + CreateNextSentenceStartPositionWithBoundaryBehaviorStopAtAnchorBoundaryOrIfAlreadyAtBoundary, + AXPositionTextNavigationTestWithParam, + ::testing::Values( + TextNavigationTestParam{ + base::BindRepeating([](const TestPositionType& position) { + return position->CreateNextSentenceStartPosition( + AXBoundaryBehavior:: + kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); + }), + ROOT_ID, + 0 /* text_offset */, + {"TextPosition anchor_id=1 text_offset=0 " + "affinity=downstream annotated_text=<L>ine 1\nLine 2", + "TextPosition anchor_id=1 text_offset=0 " + "affinity=downstream annotated_text=<L>ine 1\nLine 2"}}, + TextNavigationTestParam{ + base::BindRepeating([](const TestPositionType& position) { + return position->CreateNextSentenceStartPosition( + AXBoundaryBehavior:: + kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); + }), + TEXT_FIELD_ID, + 0 /* text_offset */, + {"TextPosition anchor_id=4 text_offset=0 " + "affinity=downstream annotated_text=<L>ine 1\nLine 2", + "TextPosition anchor_id=4 text_offset=0 " + "affinity=downstream annotated_text=<L>ine 1\nLine 2"}}, + TextNavigationTestParam{ + base::BindRepeating([](const TestPositionType& position) { + return position->CreateNextSentenceStartPosition( + AXBoundaryBehavior:: + kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); + }), + STATIC_TEXT1_ID, + 1 /* text_offset */, + {"TextPosition anchor_id=5 text_offset=6 affinity=downstream " + "annotated_text=Line 1<>", + "TextPosition anchor_id=5 text_offset=6 affinity=downstream " + "annotated_text=Line 1<>"}}, + TextNavigationTestParam{ + base::BindRepeating([](const TestPositionType& position) { + return position->CreateNextSentenceStartPosition( + AXBoundaryBehavior:: + kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); + }), + INLINE_BOX2_ID, + 4 /* text_offset */, + {"TextPosition anchor_id=9 text_offset=6 " + "affinity=downstream annotated_text=Line 2<>", + "TextPosition anchor_id=9 text_offset=6 " + "affinity=downstream annotated_text=Line 2<>"}})); + +INSTANTIATE_TEST_SUITE_P( + CreateNextSentenceStartPositionWithBoundaryBehaviorStopAtLastAnchorBoundary, + AXPositionTextNavigationTestWithParam, + ::testing::Values( + TextNavigationTestParam{ + base::BindRepeating([](const TestPositionType& position) { + return position->CreateNextSentenceStartPosition( + AXBoundaryBehavior::kStopAtLastAnchorBoundary); + }), + ROOT_ID, + 0 /* text_offset */, + {"TextPosition anchor_id=1 text_offset=7 " + "affinity=downstream annotated_text=Line 1\n<L>ine 2", + "TextPosition anchor_id=1 text_offset=13 " + "affinity=downstream annotated_text=Line 1\nLine 2<>", + "TextPosition anchor_id=1 text_offset=13 " + "affinity=downstream annotated_text=Line 1\nLine 2<>"}}, + TextNavigationTestParam{ + base::BindRepeating([](const TestPositionType& position) { + return position->CreateNextSentenceStartPosition( + AXBoundaryBehavior::kStopAtLastAnchorBoundary); + }), + TEXT_FIELD_ID, + 0 /* text_offset */, + {"TextPosition anchor_id=4 text_offset=7 " + "affinity=downstream annotated_text=Line 1\n<L>ine 2", + "TextPosition anchor_id=4 text_offset=13 " + "affinity=downstream annotated_text=Line 1\nLine 2<>", + "TextPosition anchor_id=4 text_offset=13 " + "affinity=downstream annotated_text=Line 1\nLine 2<>"}}, + TextNavigationTestParam{ + base::BindRepeating([](const TestPositionType& position) { + return position->CreateNextSentenceStartPosition( + AXBoundaryBehavior::kStopAtLastAnchorBoundary); + }), + STATIC_TEXT1_ID, + 1 /* text_offset */, + {"TextPosition anchor_id=9 text_offset=0 " + "affinity=downstream annotated_text=<L>ine 2", + "TextPosition anchor_id=9 text_offset=6 " + "affinity=downstream annotated_text=Line 2<>", + "TextPosition anchor_id=9 text_offset=6 " + "affinity=downstream annotated_text=Line 2<>"}}, + TextNavigationTestParam{ + base::BindRepeating([](const TestPositionType& position) { + return position->CreateNextSentenceStartPosition( + AXBoundaryBehavior::kStopAtLastAnchorBoundary); + }), + INLINE_BOX2_ID, + 4 /* text_offset */, + {"TextPosition anchor_id=9 text_offset=6 " + "affinity=downstream annotated_text=Line 2<>", + "TextPosition anchor_id=9 text_offset=6 " + "affinity=downstream annotated_text=Line 2<>"}})); + +INSTANTIATE_TEST_SUITE_P( + CreatePreviousSentenceStartPositionWithBoundaryBehaviorCrossBoundary, + AXPositionTextNavigationTestWithParam, + ::testing::Values( + TextNavigationTestParam{ + base::BindRepeating([](const TestPositionType& position) { + return position->CreatePreviousSentenceStartPosition( + AXBoundaryBehavior::kCrossBoundary); + }), + ROOT_ID, + 13 /* text_offset at end of root. */, + {"TextPosition anchor_id=1 text_offset=7 " + "affinity=downstream annotated_text=Line 1\n<L>ine 2", + "TextPosition anchor_id=1 text_offset=0 " + "affinity=downstream annotated_text=<L>ine 1\nLine 2", + "NullPosition"}}, + TextNavigationTestParam{ + base::BindRepeating([](const TestPositionType& position) { + return position->CreatePreviousSentenceStartPosition( + AXBoundaryBehavior::kCrossBoundary); + }), + TEXT_FIELD_ID, + 13 /* text_offset at end of text field */, + {"TextPosition anchor_id=4 text_offset=7 " + "affinity=downstream annotated_text=Line 1\n<L>ine 2", + "TextPosition anchor_id=4 text_offset=0 " + "affinity=downstream annotated_text=<L>ine 1\nLine 2", + "NullPosition"}}, + TextNavigationTestParam{ + base::BindRepeating([](const TestPositionType& position) { + return position->CreatePreviousSentenceStartPosition( + AXBoundaryBehavior::kCrossBoundary); + }), + STATIC_TEXT1_ID, + 5 /* text_offset */, + {"TextPosition anchor_id=5 text_offset=0 " + "affinity=downstream annotated_text=<L>ine 1", + "NullPosition"}}, + TextNavigationTestParam{ + base::BindRepeating([](const TestPositionType& position) { + return position->CreatePreviousSentenceStartPosition( + AXBoundaryBehavior::kCrossBoundary); + }), + INLINE_BOX2_ID, + 4 /* text_offset */, + {"TextPosition anchor_id=9 text_offset=0 " + "affinity=downstream annotated_text=<L>ine 2", + "TextPosition anchor_id=6 text_offset=0 " + "affinity=downstream annotated_text=<L>ine 1", + "NullPosition"}})); + +INSTANTIATE_TEST_SUITE_P( + CreatePreviousSentenceStartPositionWithBoundaryBehaviorStopAtAnchorBoundary, + AXPositionTextNavigationTestWithParam, + ::testing::Values( + TextNavigationTestParam{ + base::BindRepeating([](const TestPositionType& position) { + return position->CreatePreviousSentenceStartPosition( + AXBoundaryBehavior::kStopAtAnchorBoundary); + }), + ROOT_ID, + 13 /* text_offset at end of root. */, + {"TextPosition anchor_id=1 text_offset=7 " + "affinity=downstream annotated_text=Line 1\n<L>ine 2", + "TextPosition anchor_id=1 text_offset=0 " + "affinity=downstream annotated_text=<L>ine 1\nLine 2", + "TextPosition anchor_id=1 text_offset=0 " + "affinity=downstream annotated_text=<L>ine 1\nLine 2"}}, + TextNavigationTestParam{ + base::BindRepeating([](const TestPositionType& position) { + return position->CreatePreviousSentenceStartPosition( + AXBoundaryBehavior::kStopAtAnchorBoundary); + }), + TEXT_FIELD_ID, + 13 /* text_offset at end of text field */, + {"TextPosition anchor_id=4 text_offset=7 " + "affinity=downstream annotated_text=Line 1\n<L>ine 2", + "TextPosition anchor_id=4 text_offset=0 " + "affinity=downstream annotated_text=<L>ine 1\nLine 2", + "TextPosition anchor_id=4 text_offset=0 " + "affinity=downstream annotated_text=<L>ine 1\nLine 2"}}, + TextNavigationTestParam{ + base::BindRepeating([](const TestPositionType& position) { + return position->CreatePreviousSentenceStartPosition( + AXBoundaryBehavior::kStopAtAnchorBoundary); + }), + STATIC_TEXT1_ID, + 5 /* text_offset */, + {"TextPosition anchor_id=5 text_offset=0 " + "affinity=downstream annotated_text=<L>ine 1", + "TextPosition anchor_id=5 text_offset=0 " + "affinity=downstream annotated_text=<L>ine 1"}}, + TextNavigationTestParam{ + base::BindRepeating([](const TestPositionType& position) { + return position->CreatePreviousSentenceStartPosition( + AXBoundaryBehavior::kStopAtAnchorBoundary); + }), + INLINE_BOX2_ID, + 4 /* text_offset */, + {"TextPosition anchor_id=9 text_offset=0 " + "affinity=downstream annotated_text=<L>ine 2", + "TextPosition anchor_id=9 text_offset=0 " + "affinity=downstream annotated_text=<L>ine 2"}})); + +INSTANTIATE_TEST_SUITE_P( + CreatePreviousSentenceStartPositionWithBoundaryBehaviorStopAtAnchorBoundaryOrIfAlreadyAtBoundary, + AXPositionTextNavigationTestWithParam, + ::testing::Values( + TextNavigationTestParam{ + base::BindRepeating([](const TestPositionType& position) { + return position->CreatePreviousSentenceStartPosition( + AXBoundaryBehavior:: + kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); + }), + ROOT_ID, + 13 /* text_offset at end of root. */, + {"TextPosition anchor_id=1 text_offset=7 " + "affinity=downstream annotated_text=Line 1\n<L>ine 2", + "TextPosition anchor_id=1 text_offset=7 " + "affinity=downstream annotated_text=Line 1\n<L>ine 2"}}, + TextNavigationTestParam{ + base::BindRepeating([](const TestPositionType& position) { + return position->CreatePreviousSentenceStartPosition( + AXBoundaryBehavior:: + kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); + }), + TEXT_FIELD_ID, + 13 /* text_offset at end of text field */, + {"TextPosition anchor_id=4 text_offset=7 " + "affinity=downstream annotated_text=Line 1\n<L>ine 2", + "TextPosition anchor_id=4 text_offset=7 " + "affinity=downstream annotated_text=Line 1\n<L>ine 2"}}, + TextNavigationTestParam{ + base::BindRepeating([](const TestPositionType& position) { + return position->CreatePreviousSentenceStartPosition( + AXBoundaryBehavior:: + kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); + }), + STATIC_TEXT1_ID, + 5 /* text_offset */, + {"TextPosition anchor_id=5 text_offset=0 " + "affinity=downstream annotated_text=<L>ine 1", + "TextPosition anchor_id=5 text_offset=0 " + "affinity=downstream annotated_text=<L>ine 1"}}, + TextNavigationTestParam{ + base::BindRepeating([](const TestPositionType& position) { + return position->CreatePreviousSentenceStartPosition( + AXBoundaryBehavior:: + kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); + }), + INLINE_BOX2_ID, + 4 /* text_offset */, + {"TextPosition anchor_id=9 text_offset=0 " + "affinity=downstream annotated_text=<L>ine 2", + "TextPosition anchor_id=9 text_offset=0 " + "affinity=downstream annotated_text=<L>ine 2"}})); + +INSTANTIATE_TEST_SUITE_P( + CreatePreviousSentenceStartPositionWithBoundaryBehaviorStopAtLastAnchorBoundary, + AXPositionTextNavigationTestWithParam, + ::testing::Values( + TextNavigationTestParam{ + base::BindRepeating([](const TestPositionType& position) { + return position->CreatePreviousSentenceStartPosition( + AXBoundaryBehavior::kStopAtLastAnchorBoundary); + }), + ROOT_ID, + 13 /* text_offset */, + {"TextPosition anchor_id=1 text_offset=7 " + "affinity=downstream annotated_text=Line 1\n<L>ine 2", + "TextPosition anchor_id=1 text_offset=0 " + "affinity=downstream annotated_text=<L>ine 1\nLine 2", + "TextPosition anchor_id=1 text_offset=0 " + "affinity=downstream annotated_text=<L>ine 1\nLine 2"}}, + TextNavigationTestParam{ + base::BindRepeating([](const TestPositionType& position) { + return position->CreatePreviousSentenceStartPosition( + AXBoundaryBehavior::kStopAtLastAnchorBoundary); + }), + TEXT_FIELD_ID, + 13 /* text_offset */, + {"TextPosition anchor_id=4 text_offset=7 " + "affinity=downstream annotated_text=Line 1\n<L>ine 2", + "TextPosition anchor_id=4 text_offset=0 " + "affinity=downstream annotated_text=<L>ine 1\nLine 2", + "TextPosition anchor_id=4 text_offset=0 " + "affinity=downstream annotated_text=<L>ine 1\nLine 2"}}, + TextNavigationTestParam{ + base::BindRepeating([](const TestPositionType& position) { + return position->CreatePreviousSentenceStartPosition( + AXBoundaryBehavior::kStopAtLastAnchorBoundary); + }), + STATIC_TEXT1_ID, + 5 /* text_offset */, + {"TextPosition anchor_id=5 text_offset=0 " + "affinity=downstream annotated_text=<L>ine 1", + "TextPosition anchor_id=5 text_offset=0 " + "affinity=downstream annotated_text=<L>ine 1"}}, + TextNavigationTestParam{ + base::BindRepeating([](const TestPositionType& position) { + return position->CreatePreviousSentenceStartPosition( + AXBoundaryBehavior::kStopAtLastAnchorBoundary); + }), + INLINE_BOX2_ID, + 4 /* text_offset */, + {"TextPosition anchor_id=9 text_offset=0 " + "affinity=downstream annotated_text=<L>ine 2", + "TextPosition anchor_id=6 text_offset=0 " + "affinity=downstream annotated_text=<L>ine 1", + "TextPosition anchor_id=6 text_offset=0 " + "affinity=downstream annotated_text=<L>ine 1"}})); + +INSTANTIATE_TEST_SUITE_P( + CreateNextSentenceEndPositionWithBoundaryBehaviorCrossBoundary, + AXPositionTextNavigationTestWithParam, + ::testing::Values( + TextNavigationTestParam{ + base::BindRepeating([](const TestPositionType& position) { + return position->CreateNextSentenceEndPosition( + AXBoundaryBehavior::kCrossBoundary); + }), + ROOT_ID, + 0 /* text_offset */, + {"TextPosition anchor_id=1 text_offset=6 " + "affinity=downstream annotated_text=Line 1<\n>Line 2", + "TextPosition anchor_id=1 text_offset=13 " + "affinity=downstream annotated_text=Line 1\nLine 2<>", + "NullPosition"}}, + TextNavigationTestParam{ + base::BindRepeating([](const TestPositionType& position) { + return position->CreateNextSentenceEndPosition( + AXBoundaryBehavior::kCrossBoundary); + }), + TEXT_FIELD_ID, + 0 /* text_offset */, + {"TextPosition anchor_id=4 text_offset=6 " + "affinity=downstream annotated_text=Line 1<\n>Line 2", + "TextPosition anchor_id=4 text_offset=13 " + "affinity=downstream annotated_text=Line 1\nLine 2<>", + "NullPosition"}}, + TextNavigationTestParam{ + base::BindRepeating([](const TestPositionType& position) { + return position->CreateNextSentenceEndPosition( + AXBoundaryBehavior::kCrossBoundary); + }), + STATIC_TEXT1_ID, + 1 /* text_offset */, + {"TextPosition anchor_id=5 text_offset=6 " + "affinity=downstream annotated_text=Line 1<>", + "TextPosition anchor_id=9 text_offset=6 " + "affinity=downstream annotated_text=Line 2<>", + "NullPosition"}}, + TextNavigationTestParam{ + base::BindRepeating([](const TestPositionType& position) { + return position->CreateNextSentenceEndPosition( + AXBoundaryBehavior::kCrossBoundary); + }), + INLINE_BOX2_ID, + 4 /* text_offset */, + {"TextPosition anchor_id=9 text_offset=6 " + "affinity=downstream annotated_text=Line 2<>", + "NullPosition"}})); + +INSTANTIATE_TEST_SUITE_P( + CreateNextSentenceEndPositionWithBoundaryBehaviorStopAtAnchorBoundary, + AXPositionTextNavigationTestWithParam, + ::testing::Values( + TextNavigationTestParam{ + base::BindRepeating([](const TestPositionType& position) { + return position->CreateNextSentenceEndPosition( + AXBoundaryBehavior::kStopAtAnchorBoundary); + }), + ROOT_ID, + 0 /* text_offset */, + {"TextPosition anchor_id=1 text_offset=6 " + "affinity=downstream annotated_text=Line 1<\n>Line 2", + "TextPosition anchor_id=1 text_offset=13 " + "affinity=downstream annotated_text=Line 1\nLine 2<>", + "TextPosition anchor_id=1 text_offset=13 " + "affinity=downstream annotated_text=Line 1\nLine 2<>"}}, + TextNavigationTestParam{ + base::BindRepeating([](const TestPositionType& position) { + return position->CreateNextSentenceEndPosition( + AXBoundaryBehavior::kStopAtAnchorBoundary); + }), + TEXT_FIELD_ID, + 0 /* text_offset */, + {"TextPosition anchor_id=4 text_offset=6 " + "affinity=downstream annotated_text=Line 1<\n>Line 2", + "TextPosition anchor_id=4 text_offset=13 " + "affinity=downstream annotated_text=Line 1\nLine 2<>", + "TextPosition anchor_id=4 text_offset=13 " + "affinity=downstream annotated_text=Line 1\nLine 2<>"}}, + TextNavigationTestParam{ + base::BindRepeating([](const TestPositionType& position) { + return position->CreateNextSentenceEndPosition( + AXBoundaryBehavior::kStopAtAnchorBoundary); + }), + STATIC_TEXT1_ID, + 1 /* text_offset */, + {"TextPosition anchor_id=5 text_offset=6 " + "affinity=downstream annotated_text=Line 1<>", + "TextPosition anchor_id=5 text_offset=6 " + "affinity=downstream annotated_text=Line 1<>"}}, + TextNavigationTestParam{ + base::BindRepeating([](const TestPositionType& position) { + return position->CreateNextSentenceEndPosition( + AXBoundaryBehavior::kStopAtAnchorBoundary); + }), + INLINE_BOX2_ID, + 4 /* text_offset */, + {"TextPosition anchor_id=9 text_offset=6 " + "affinity=downstream annotated_text=Line 2<>", + "TextPosition anchor_id=9 text_offset=6 " + "affinity=downstream annotated_text=Line 2<>"}})); + +INSTANTIATE_TEST_SUITE_P( + CreateNextSentenceEndPositionWithBoundaryBehaviorStopAtAnchorBoundaryOrIfAlreadyAtBoundary, + AXPositionTextNavigationTestWithParam, + ::testing::Values( + TextNavigationTestParam{ + base::BindRepeating([](const TestPositionType& position) { + return position->CreateNextSentenceEndPosition( + AXBoundaryBehavior:: + kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); + }), + ROOT_ID, + 0 /* text_offset */, + {"TextPosition anchor_id=1 text_offset=6 " + "affinity=downstream annotated_text=Line 1<\n>Line 2", + "TextPosition anchor_id=1 text_offset=6 " + "affinity=downstream annotated_text=Line 1<\n>Line 2"}}, + TextNavigationTestParam{ + base::BindRepeating([](const TestPositionType& position) { + return position->CreateNextSentenceEndPosition( + AXBoundaryBehavior:: + kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); + }), + TEXT_FIELD_ID, + 0 /* text_offset */, + {"TextPosition anchor_id=4 text_offset=6 " + "affinity=downstream annotated_text=Line 1<\n>Line 2", + "TextPosition anchor_id=4 text_offset=6 " + "affinity=downstream annotated_text=Line 1<\n>Line 2"}}, + TextNavigationTestParam{ + base::BindRepeating([](const TestPositionType& position) { + return position->CreateNextSentenceEndPosition( + AXBoundaryBehavior:: + kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); + }), + STATIC_TEXT1_ID, + 1 /* text_offset */, + {"TextPosition anchor_id=5 text_offset=6 " + "affinity=downstream annotated_text=Line 1<>", + "TextPosition anchor_id=5 text_offset=6 " + "affinity=downstream annotated_text=Line 1<>"}}, + TextNavigationTestParam{ + base::BindRepeating([](const TestPositionType& position) { + return position->CreateNextSentenceEndPosition( + AXBoundaryBehavior:: + kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); + }), + INLINE_BOX2_ID, + 4 /* text_offset */, + {"TextPosition anchor_id=9 text_offset=6 " + "affinity=downstream annotated_text=Line 2<>", + "TextPosition anchor_id=9 text_offset=6 " + "affinity=downstream annotated_text=Line 2<>"}})); + +INSTANTIATE_TEST_SUITE_P( + CreateNextSentenceEndPositionWithBoundaryBehaviorStopAtLastAnchorBoundary, + AXPositionTextNavigationTestWithParam, + ::testing::Values( + TextNavigationTestParam{ + base::BindRepeating([](const TestPositionType& position) { + return position->CreateNextSentenceEndPosition( + AXBoundaryBehavior::kStopAtLastAnchorBoundary); + }), + ROOT_ID, + 0 /* text_offset */, + {"TextPosition anchor_id=1 text_offset=6 " + "affinity=downstream annotated_text=Line 1<\n>Line 2", + "TextPosition anchor_id=1 text_offset=13 " + "affinity=downstream annotated_text=Line 1\nLine 2<>", + "TextPosition anchor_id=1 text_offset=13 " + "affinity=downstream annotated_text=Line 1\nLine 2<>"}}, + TextNavigationTestParam{ + base::BindRepeating([](const TestPositionType& position) { + return position->CreateNextSentenceEndPosition( + AXBoundaryBehavior::kStopAtLastAnchorBoundary); + }), + TEXT_FIELD_ID, + 0 /* text_offset */, + {"TextPosition anchor_id=4 text_offset=6 " + "affinity=downstream annotated_text=Line 1<\n>Line 2", + "TextPosition anchor_id=4 text_offset=13 " + "affinity=downstream annotated_text=Line 1\nLine 2<>", + "TextPosition anchor_id=4 text_offset=13 " + "affinity=downstream annotated_text=Line 1\nLine 2<>"}}, + TextNavigationTestParam{ + base::BindRepeating([](const TestPositionType& position) { + return position->CreateNextSentenceEndPosition( + AXBoundaryBehavior::kStopAtLastAnchorBoundary); + }), + STATIC_TEXT1_ID, + 1 /* text_offset */, + {"TextPosition anchor_id=5 text_offset=6 " + "affinity=downstream annotated_text=Line 1<>", + "TextPosition anchor_id=9 text_offset=6 " + "affinity=downstream annotated_text=Line 2<>", + "TextPosition anchor_id=9 text_offset=6 " + "affinity=downstream annotated_text=Line 2<>"}}, + TextNavigationTestParam{ + base::BindRepeating([](const TestPositionType& position) { + return position->CreateNextSentenceEndPosition( + AXBoundaryBehavior::kStopAtLastAnchorBoundary); + }), + INLINE_BOX2_ID, + 4 /* text_offset */, + {"TextPosition anchor_id=9 text_offset=6 " + "affinity=downstream annotated_text=Line 2<>", + "TextPosition anchor_id=9 text_offset=6 " + "affinity=downstream annotated_text=Line 2<>"}})); + +INSTANTIATE_TEST_SUITE_P( + CreatePreviousSentenceEndPositionWithBoundaryBehaviorCrossBoundary, + AXPositionTextNavigationTestWithParam, + ::testing::Values( + TextNavigationTestParam{ + base::BindRepeating([](const TestPositionType& position) { + return position->CreatePreviousSentenceEndPosition( + AXBoundaryBehavior::kCrossBoundary); + }), + ROOT_ID, + 13 /* text_offset at end of root. */, + {"TextPosition anchor_id=1 text_offset=6 " + "affinity=downstream annotated_text=Line 1<\n>Line 2", + "NullPosition"}}, + TextNavigationTestParam{ + base::BindRepeating([](const TestPositionType& position) { + return position->CreatePreviousSentenceEndPosition( + AXBoundaryBehavior::kCrossBoundary); + }), + TEXT_FIELD_ID, + 13 /* text_offset at end of text field */, + {"TextPosition anchor_id=4 text_offset=6 " + "affinity=downstream annotated_text=Line 1<\n>Line 2", + "NullPosition"}}, + TextNavigationTestParam{ + base::BindRepeating([](const TestPositionType& position) { + return position->CreatePreviousSentenceEndPosition( + AXBoundaryBehavior::kCrossBoundary); + }), + STATIC_TEXT1_ID, + 5 /* text_offset */, + {"NullPosition"}}, + TextNavigationTestParam{ + base::BindRepeating([](const TestPositionType& position) { + return position->CreatePreviousSentenceEndPosition( + AXBoundaryBehavior::kCrossBoundary); + }), + INLINE_BOX2_ID, + 4 /* text_offset */, + {"TextPosition anchor_id=6 text_offset=6 affinity=downstream " + "annotated_text=Line 1<>", + "NullPosition"}})); + +INSTANTIATE_TEST_SUITE_P( + CreatePreviousSentenceEndPositionWithBoundaryBehaviorStopAtAnchorBoundary, + AXPositionTextNavigationTestWithParam, + ::testing::Values( + TextNavigationTestParam{ + base::BindRepeating([](const TestPositionType& position) { + return position->CreatePreviousSentenceEndPosition( + AXBoundaryBehavior::kStopAtAnchorBoundary); + }), + ROOT_ID, + 13 /* text_offset at end of root. */, + {"TextPosition anchor_id=1 text_offset=6 " + "affinity=downstream annotated_text=Line 1<\n>Line 2", + "TextPosition anchor_id=1 text_offset=0 " + "affinity=downstream annotated_text=<L>ine 1\nLine 2", + "TextPosition anchor_id=1 text_offset=0 " + "affinity=downstream annotated_text=<L>ine 1\nLine 2"}}, + TextNavigationTestParam{ + base::BindRepeating([](const TestPositionType& position) { + return position->CreatePreviousSentenceEndPosition( + AXBoundaryBehavior::kStopAtAnchorBoundary); + }), + TEXT_FIELD_ID, + 13 /* text_offset at end of text field */, + {"TextPosition anchor_id=4 text_offset=6 " + "affinity=downstream annotated_text=Line 1<\n>Line 2", + "TextPosition anchor_id=4 text_offset=0 " + "affinity=downstream annotated_text=<L>ine 1\nLine 2", + "TextPosition anchor_id=4 text_offset=0 " + "affinity=downstream annotated_text=<L>ine 1\nLine 2"}}, + TextNavigationTestParam{ + base::BindRepeating([](const TestPositionType& position) { + return position->CreatePreviousSentenceEndPosition( + AXBoundaryBehavior::kStopAtAnchorBoundary); + }), + STATIC_TEXT1_ID, + 5 /* text_offset */, + {"TextPosition anchor_id=5 text_offset=0 " + "affinity=downstream annotated_text=<L>ine 1", + "TextPosition anchor_id=5 text_offset=0 " + "affinity=downstream annotated_text=<L>ine 1"}}, + TextNavigationTestParam{ + base::BindRepeating([](const TestPositionType& position) { + return position->CreatePreviousSentenceEndPosition( + AXBoundaryBehavior::kStopAtAnchorBoundary); + }), + INLINE_BOX2_ID, + 4 /* text_offset */, + {"TextPosition anchor_id=9 text_offset=0 " + "affinity=downstream annotated_text=<L>ine 2", + "TextPosition anchor_id=9 text_offset=0 " + "affinity=downstream annotated_text=<L>ine 2"}})); + +INSTANTIATE_TEST_SUITE_P( + CreatePreviousSentenceEndPositionWithBoundaryBehaviorStopAtAnchorBoundaryOrIfAlreadyAtBoundary, + AXPositionTextNavigationTestWithParam, + ::testing::Values( + TextNavigationTestParam{ + base::BindRepeating([](const TestPositionType& position) { + return position->CreatePreviousSentenceEndPosition( + AXBoundaryBehavior:: + kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); + }), + ROOT_ID, + 13 /* text_offset at end of root. */, + {"TextPosition anchor_id=1 text_offset=13 " + "affinity=downstream annotated_text=Line 1\nLine 2<>", + "TextPosition anchor_id=1 text_offset=13 " + "affinity=downstream annotated_text=Line 1\nLine 2<>"}}, + TextNavigationTestParam{ + base::BindRepeating([](const TestPositionType& position) { + return position->CreatePreviousSentenceEndPosition( + AXBoundaryBehavior:: + kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); + }), + TEXT_FIELD_ID, + 13 /* text_offset at end of text field */, + {"TextPosition anchor_id=4 text_offset=13 " + "affinity=downstream annotated_text=Line 1\nLine 2<>", + "TextPosition anchor_id=4 text_offset=13 " + "affinity=downstream annotated_text=Line 1\nLine 2<>"}}, + TextNavigationTestParam{ + base::BindRepeating([](const TestPositionType& position) { + return position->CreatePreviousSentenceEndPosition( + AXBoundaryBehavior:: + kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); + }), + STATIC_TEXT1_ID, + 5 /* text_offset */, + {"TextPosition anchor_id=5 text_offset=0 " + "affinity=downstream annotated_text=<L>ine 1", + "TextPosition anchor_id=5 text_offset=0 " + "affinity=downstream annotated_text=<L>ine 1"}}, + TextNavigationTestParam{ + base::BindRepeating([](const TestPositionType& position) { + return position->CreatePreviousSentenceEndPosition( + AXBoundaryBehavior:: + kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); + }), + INLINE_BOX2_ID, + 4 /* text_offset */, + {"TextPosition anchor_id=9 text_offset=0 " + "affinity=downstream annotated_text=<L>ine 2", + "TextPosition anchor_id=9 text_offset=0 " + "affinity=downstream annotated_text=<L>ine 2"}})); + +INSTANTIATE_TEST_SUITE_P( + CreatePreviousSentenceEndPositionWithBoundaryBehaviorStopAtLastAnchorBoundary, + AXPositionTextNavigationTestWithParam, + ::testing::Values( + TextNavigationTestParam{ + base::BindRepeating([](const TestPositionType& position) { + return position->CreatePreviousSentenceEndPosition( + AXBoundaryBehavior::kStopAtLastAnchorBoundary); + }), + ROOT_ID, + 13 /* text_offset at end of root. */, + {"TextPosition anchor_id=1 text_offset=6 " + "affinity=downstream annotated_text=Line 1<\n>Line 2", + "TextPosition anchor_id=1 text_offset=0 " + "affinity=downstream annotated_text=<L>ine 1\nLine 2", + "TextPosition anchor_id=1 text_offset=0 " + "affinity=downstream annotated_text=<L>ine 1\nLine 2"}}, + TextNavigationTestParam{ + base::BindRepeating([](const TestPositionType& position) { + return position->CreatePreviousSentenceEndPosition( + AXBoundaryBehavior::kStopAtLastAnchorBoundary); + }), + TEXT_FIELD_ID, + 13 /* text_offset at end of text field */, + {"TextPosition anchor_id=4 text_offset=6 " + "affinity=downstream annotated_text=Line 1<\n>Line 2", + "TextPosition anchor_id=2 text_offset=0 " + "affinity=downstream annotated_text=<>", + "TextPosition anchor_id=2 text_offset=0 " + "affinity=downstream annotated_text=<>"}}, + TextNavigationTestParam{ + base::BindRepeating([](const TestPositionType& position) { + return position->CreatePreviousSentenceEndPosition( + AXBoundaryBehavior::kStopAtLastAnchorBoundary); + }), + STATIC_TEXT1_ID, + 5 /* text_offset */, + {"TextPosition anchor_id=2 text_offset=0 " + "affinity=downstream annotated_text=<>", + "TextPosition anchor_id=2 text_offset=0 " + "affinity=downstream annotated_text=<>"}}, + TextNavigationTestParam{ + base::BindRepeating([](const TestPositionType& position) { + return position->CreatePreviousSentenceEndPosition( + AXBoundaryBehavior::kStopAtLastAnchorBoundary); + }), + INLINE_BOX2_ID, + 4 /* text_offset */, + {"TextPosition anchor_id=6 text_offset=6 affinity=downstream " + "annotated_text=Line 1<>", + "TextPosition anchor_id=2 text_offset=0 " + "affinity=downstream annotated_text=<>"}})); + +INSTANTIATE_TEST_SUITE_P( CreateNextWordStartPositionWithBoundaryBehaviorCrossBoundary, AXPositionTextNavigationTestWithParam, ::testing::Values( TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreateNextWordStartPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); }), ROOT_ID, 0 /* text_offset */, @@ -12469,7 +13400,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreateNextWordStartPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); }), TEXT_FIELD_ID, 0 /* text_offset */, @@ -12483,7 +13414,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreateNextWordStartPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); }), STATIC_TEXT1_ID, 1 /* text_offset */, @@ -12497,7 +13428,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreateNextWordStartPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); }), INLINE_BOX2_ID, 4 /* text_offset */, @@ -12512,7 +13443,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreateNextWordStartPosition( - AXBoundaryBehavior::StopAtAnchorBoundary); + AXBoundaryBehavior::kStopAtAnchorBoundary); }), ROOT_ID, 0 /* text_offset */, @@ -12527,7 +13458,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreateNextWordStartPosition( - AXBoundaryBehavior::StopAtAnchorBoundary); + AXBoundaryBehavior::kStopAtAnchorBoundary); }), TEXT_FIELD_ID, 0 /* text_offset */, @@ -12542,7 +13473,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreateNextWordStartPosition( - AXBoundaryBehavior::StopAtAnchorBoundary); + AXBoundaryBehavior::kStopAtAnchorBoundary); }), STATIC_TEXT1_ID, 1 /* text_offset */, @@ -12553,7 +13484,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreateNextWordStartPosition( - AXBoundaryBehavior::StopAtAnchorBoundary); + AXBoundaryBehavior::kStopAtAnchorBoundary); }), INLINE_BOX2_ID, 4 /* text_offset */, @@ -12563,13 +13494,14 @@ "affinity=downstream annotated_text=Line 2<>"}})); INSTANTIATE_TEST_SUITE_P( - CreateNextWordStartPositionWithBoundaryBehaviorStopIfAlreadyAtBoundary, + CreateNextWordStartPositionWithBoundaryBehaviorStopAtAnchorBoundaryOrIfAlreadyAtBoundary, AXPositionTextNavigationTestWithParam, ::testing::Values( TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreateNextWordStartPosition( - AXBoundaryBehavior::StopIfAlreadyAtBoundary); + AXBoundaryBehavior:: + kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); }), ROOT_ID, 0 /* text_offset */, @@ -12580,7 +13512,8 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreateNextWordStartPosition( - AXBoundaryBehavior::StopIfAlreadyAtBoundary); + AXBoundaryBehavior:: + kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); }), TEXT_FIELD_ID, 0 /* text_offset */, @@ -12591,7 +13524,8 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreateNextWordStartPosition( - AXBoundaryBehavior::StopIfAlreadyAtBoundary); + AXBoundaryBehavior:: + kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); }), STATIC_TEXT1_ID, 1 /* text_offset */, @@ -12602,7 +13536,8 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreateNextWordStartPosition( - AXBoundaryBehavior::StopIfAlreadyAtBoundary); + AXBoundaryBehavior:: + kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); }), INLINE_BOX2_ID, 4 /* text_offset */, @@ -12618,7 +13553,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreateNextWordStartPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); }), ROOT_ID, 0 /* text_offset */, @@ -12635,7 +13570,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreateNextWordStartPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); }), TEXT_FIELD_ID, 0 /* text_offset */, @@ -12652,7 +13587,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreateNextWordStartPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); }), STATIC_TEXT1_ID, 1 /* text_offset */, @@ -12669,7 +13604,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreateNextWordStartPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); }), INLINE_BOX2_ID, 4 /* text_offset */, @@ -12687,7 +13622,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreatePreviousWordStartPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); }), ROOT_ID, 13 /* text_offset at end of root. */, @@ -12703,7 +13638,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreatePreviousWordStartPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); }), TEXT_FIELD_ID, 13 /* text_offset at end of text field */, @@ -12719,7 +13654,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreatePreviousWordStartPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); }), STATIC_TEXT1_ID, 5 /* text_offset */, @@ -12729,7 +13664,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreatePreviousWordStartPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); }), INLINE_BOX2_ID, 4 /* text_offset */, @@ -12748,7 +13683,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreatePreviousWordStartPosition( - AXBoundaryBehavior::StopAtAnchorBoundary); + AXBoundaryBehavior::kStopAtAnchorBoundary); }), ROOT_ID, 13 /* text_offset at end of root. */, @@ -12765,7 +13700,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreatePreviousWordStartPosition( - AXBoundaryBehavior::StopAtAnchorBoundary); + AXBoundaryBehavior::kStopAtAnchorBoundary); }), TEXT_FIELD_ID, 13 /* text_offset at end of text field */, @@ -12782,7 +13717,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreatePreviousWordStartPosition( - AXBoundaryBehavior::StopAtAnchorBoundary); + AXBoundaryBehavior::kStopAtAnchorBoundary); }), STATIC_TEXT1_ID, 5 /* text_offset */, @@ -12793,7 +13728,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreatePreviousWordStartPosition( - AXBoundaryBehavior::StopAtAnchorBoundary); + AXBoundaryBehavior::kStopAtAnchorBoundary); }), INLINE_BOX2_ID, 4 /* text_offset */, @@ -12803,13 +13738,14 @@ "affinity=downstream annotated_text=<L>ine 2"}})); INSTANTIATE_TEST_SUITE_P( - CreatePreviousWordStartPositionWithBoundaryBehaviorStopIfAlreadyAtBoundary, + CreatePreviousWordStartPositionWithBoundaryBehaviorStopAtAnchorBoundaryOrIfAlreadyAtBoundary, AXPositionTextNavigationTestWithParam, ::testing::Values( TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreatePreviousWordStartPosition( - AXBoundaryBehavior::StopIfAlreadyAtBoundary); + AXBoundaryBehavior:: + kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); }), ROOT_ID, 13 /* text_offset at end of root. */, @@ -12820,7 +13756,8 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreatePreviousWordStartPosition( - AXBoundaryBehavior::StopIfAlreadyAtBoundary); + AXBoundaryBehavior:: + kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); }), TEXT_FIELD_ID, 13 /* text_offset at end of text field */, @@ -12831,7 +13768,8 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreatePreviousWordStartPosition( - AXBoundaryBehavior::StopIfAlreadyAtBoundary); + AXBoundaryBehavior:: + kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); }), STATIC_TEXT1_ID, 5 /* text_offset */, @@ -12840,7 +13778,8 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreatePreviousWordStartPosition( - AXBoundaryBehavior::StopIfAlreadyAtBoundary); + AXBoundaryBehavior:: + kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); }), INLINE_BOX2_ID, 4 /* text_offset */, @@ -12856,7 +13795,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreatePreviousWordStartPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); }), ROOT_ID, 13 /* text_offset */, @@ -12873,7 +13812,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreatePreviousWordStartPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); }), TEXT_FIELD_ID, 13 /* text_offset */, @@ -12890,7 +13829,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreatePreviousWordStartPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); }), STATIC_TEXT1_ID, 5 /* text_offset */, @@ -12901,7 +13840,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreatePreviousWordStartPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); }), INLINE_BOX2_ID, 4 /* text_offset */, @@ -12921,7 +13860,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreateNextWordEndPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); }), ROOT_ID, 0 /* text_offset */, @@ -12937,7 +13876,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreateNextWordEndPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); }), TEXT_FIELD_ID, 0 /* text_offset */, @@ -12953,7 +13892,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreateNextWordEndPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); }), STATIC_TEXT1_ID, 1 /* text_offset */, @@ -12969,7 +13908,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreateNextWordEndPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); }), INLINE_BOX2_ID, 4 /* text_offset */, @@ -12984,7 +13923,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreateNextWordEndPosition( - AXBoundaryBehavior::StopAtAnchorBoundary); + AXBoundaryBehavior::kStopAtAnchorBoundary); }), ROOT_ID, 0 /* text_offset */, @@ -13001,7 +13940,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreateNextWordEndPosition( - AXBoundaryBehavior::StopAtAnchorBoundary); + AXBoundaryBehavior::kStopAtAnchorBoundary); }), TEXT_FIELD_ID, 0 /* text_offset */, @@ -13018,7 +13957,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreateNextWordEndPosition( - AXBoundaryBehavior::StopAtAnchorBoundary); + AXBoundaryBehavior::kStopAtAnchorBoundary); }), STATIC_TEXT1_ID, 1 /* text_offset */, @@ -13031,7 +13970,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreateNextWordEndPosition( - AXBoundaryBehavior::StopAtAnchorBoundary); + AXBoundaryBehavior::kStopAtAnchorBoundary); }), INLINE_BOX2_ID, 4 /* text_offset */, @@ -13041,13 +13980,14 @@ "affinity=downstream annotated_text=Line 2<>"}})); INSTANTIATE_TEST_SUITE_P( - CreateNextWordEndPositionWithBoundaryBehaviorStopIfAlreadyAtBoundary, + CreateNextWordEndPositionWithBoundaryBehaviorStopAtAnchorBoundaryOrIfAlreadyAtBoundary, AXPositionTextNavigationTestWithParam, ::testing::Values( TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreateNextWordEndPosition( - AXBoundaryBehavior::StopIfAlreadyAtBoundary); + AXBoundaryBehavior:: + kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); }), ROOT_ID, 0 /* text_offset */, @@ -13058,7 +13998,8 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreateNextWordEndPosition( - AXBoundaryBehavior::StopIfAlreadyAtBoundary); + AXBoundaryBehavior:: + kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); }), TEXT_FIELD_ID, 0 /* text_offset */, @@ -13069,7 +14010,8 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreateNextWordEndPosition( - AXBoundaryBehavior::StopIfAlreadyAtBoundary); + AXBoundaryBehavior:: + kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); }), STATIC_TEXT1_ID, 1 /* text_offset */, @@ -13080,7 +14022,8 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreateNextWordEndPosition( - AXBoundaryBehavior::StopIfAlreadyAtBoundary); + AXBoundaryBehavior:: + kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); }), INLINE_BOX2_ID, 4 /* text_offset */, @@ -13094,7 +14037,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreateNextWordEndPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); }), ROOT_ID, 0 /* text_offset */, @@ -13111,7 +14054,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreateNextWordEndPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); }), TEXT_FIELD_ID, 0 /* text_offset */, @@ -13128,7 +14071,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreateNextWordEndPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); }), STATIC_TEXT1_ID, 1 /* text_offset */, @@ -13145,7 +14088,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreateNextWordEndPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); }), INLINE_BOX2_ID, 4 /* text_offset */, @@ -13161,7 +14104,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreatePreviousWordEndPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); }), ROOT_ID, 13 /* text_offset at end of root. */, @@ -13175,7 +14118,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreatePreviousWordEndPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); }), TEXT_FIELD_ID, 13 /* text_offset at end of text field */, @@ -13189,7 +14132,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreatePreviousWordEndPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); }), STATIC_TEXT1_ID, 5 /* text_offset */, @@ -13199,12 +14142,12 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreatePreviousWordEndPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); }), INLINE_BOX2_ID, 4 /* text_offset */, - {"TextPosition anchor_id=6 text_offset=6 " - "affinity=downstream annotated_text=Line 1<>", + {"TextPosition anchor_id=6 text_offset=6 affinity=downstream " + "annotated_text=Line 1<>", "TextPosition anchor_id=6 text_offset=4 " "affinity=downstream annotated_text=Line< >1", "NullPosition"}})); @@ -13216,107 +14159,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreatePreviousWordEndPosition( - AXBoundaryBehavior::StopAtAnchorBoundary); - }), - ROOT_ID, - 13 /* text_offset at end of root. */, - { - "TextPosition anchor_id=1 text_offset=11 " - "affinity=downstream annotated_text=Line 1\nLine< >2", - "TextPosition anchor_id=1 text_offset=6 " - "affinity=downstream annotated_text=Line 1<\n>Line 2", - "TextPosition anchor_id=1 text_offset=4 " - "affinity=downstream annotated_text=Line< >1\nLine 2", - "TextPosition anchor_id=1 text_offset=0 " - "affinity=downstream annotated_text=<L>ine 1\nLine 2", - }}, - TextNavigationTestParam{ - base::BindRepeating([](const TestPositionType& position) { - return position->CreatePreviousWordEndPosition( - AXBoundaryBehavior::StopAtAnchorBoundary); - }), - TEXT_FIELD_ID, - 13 /* text_offset at end of text field */, - {"TextPosition anchor_id=4 text_offset=11 " - "affinity=downstream annotated_text=Line 1\nLine< >2", - "TextPosition anchor_id=4 text_offset=6 " - "affinity=downstream annotated_text=Line 1<\n>Line 2", - "TextPosition anchor_id=4 text_offset=4 " - "affinity=downstream annotated_text=Line< >1\nLine 2", - "TextPosition anchor_id=4 text_offset=0 " - "affinity=downstream annotated_text=<L>ine 1\nLine 2"}}, - TextNavigationTestParam{ - base::BindRepeating([](const TestPositionType& position) { - return position->CreatePreviousWordEndPosition( - AXBoundaryBehavior::StopAtAnchorBoundary); - }), - STATIC_TEXT1_ID, - 5 /* text_offset */, - {"TextPosition anchor_id=5 text_offset=4 " - "affinity=downstream annotated_text=Line< >1", - "TextPosition anchor_id=5 text_offset=0 " - "affinity=downstream annotated_text=<L>ine 1"}}, - TextNavigationTestParam{ - base::BindRepeating([](const TestPositionType& position) { - return position->CreatePreviousWordEndPosition( - AXBoundaryBehavior::StopAtAnchorBoundary); - }), - INLINE_BOX2_ID, - 4 /* text_offset */, - {"TextPosition anchor_id=9 text_offset=0 " - "affinity=downstream annotated_text=<L>ine 2"}})); - -INSTANTIATE_TEST_SUITE_P( - CreatePreviousWordEndPositionWithBoundaryBehaviorStopIfAlreadyAtBoundary, - AXPositionTextNavigationTestWithParam, - ::testing::Values( - TextNavigationTestParam{ - base::BindRepeating([](const TestPositionType& position) { - return position->CreatePreviousWordEndPosition( - AXBoundaryBehavior::StopIfAlreadyAtBoundary); - }), - ROOT_ID, - 13 /* text_offset at end of root. */, - {"TextPosition anchor_id=1 text_offset=13 " - "affinity=downstream annotated_text=Line 1\nLine 2<>"}}, - TextNavigationTestParam{ - base::BindRepeating([](const TestPositionType& position) { - return position->CreatePreviousWordEndPosition( - AXBoundaryBehavior::StopIfAlreadyAtBoundary); - }), - TEXT_FIELD_ID, - 13 /* text_offset at end of text field */, - {"TextPosition anchor_id=4 text_offset=13 " - "affinity=downstream annotated_text=Line 1\nLine 2<>"}}, - TextNavigationTestParam{ - base::BindRepeating([](const TestPositionType& position) { - return position->CreatePreviousWordEndPosition( - AXBoundaryBehavior::StopIfAlreadyAtBoundary); - }), - STATIC_TEXT1_ID, - 5 /* text_offset */, - {"TextPosition anchor_id=5 text_offset=4 " - "affinity=downstream annotated_text=Line< >1", - "TextPosition anchor_id=5 text_offset=4 " - "affinity=downstream annotated_text=Line< >1"}}, - TextNavigationTestParam{ - base::BindRepeating([](const TestPositionType& position) { - return position->CreatePreviousWordEndPosition( - AXBoundaryBehavior::StopIfAlreadyAtBoundary); - }), - INLINE_BOX2_ID, - 4 /* text_offset */, - {"TextPosition anchor_id=9 text_offset=4 " - "affinity=downstream annotated_text=Line< >2"}})); - -INSTANTIATE_TEST_SUITE_P( - CreatePreviousWordEndPositionWithBoundaryBehaviorStopAtLastAnchorBoundary, - AXPositionTextNavigationTestWithParam, - ::testing::Values( - TextNavigationTestParam{ - base::BindRepeating([](const TestPositionType& position) { - return position->CreatePreviousWordEndPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtAnchorBoundary); }), ROOT_ID, 13 /* text_offset at end of root. */, @@ -13333,7 +14176,111 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreatePreviousWordEndPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtAnchorBoundary); + }), + TEXT_FIELD_ID, + 13 /* text_offset at end of text field */, + {"TextPosition anchor_id=4 text_offset=11 " + "affinity=downstream annotated_text=Line 1\nLine< >2", + "TextPosition anchor_id=4 text_offset=6 " + "affinity=downstream annotated_text=Line 1<\n>Line 2", + "TextPosition anchor_id=4 text_offset=4 " + "affinity=downstream annotated_text=Line< >1\nLine 2", + "TextPosition anchor_id=4 text_offset=0 " + "affinity=downstream annotated_text=<L>ine 1\nLine 2"}}, + TextNavigationTestParam{ + base::BindRepeating([](const TestPositionType& position) { + return position->CreatePreviousWordEndPosition( + AXBoundaryBehavior::kStopAtAnchorBoundary); + }), + STATIC_TEXT1_ID, + 5 /* text_offset */, + {"TextPosition anchor_id=5 text_offset=4 " + "affinity=downstream annotated_text=Line< >1", + "TextPosition anchor_id=5 text_offset=0 " + "affinity=downstream annotated_text=<L>ine 1"}}, + TextNavigationTestParam{ + base::BindRepeating([](const TestPositionType& position) { + return position->CreatePreviousWordEndPosition( + AXBoundaryBehavior::kStopAtAnchorBoundary); + }), + INLINE_BOX2_ID, + 4 /* text_offset */, + {"TextPosition anchor_id=9 text_offset=0 " + "affinity=downstream annotated_text=<L>ine 2"}})); + +INSTANTIATE_TEST_SUITE_P( + CreatePreviousWordEndPositionWithBoundaryBehaviorStopAtAnchorBoundaryOrIfAlreadyAtBoundary, + AXPositionTextNavigationTestWithParam, + ::testing::Values( + TextNavigationTestParam{ + base::BindRepeating([](const TestPositionType& position) { + return position->CreatePreviousWordEndPosition( + AXBoundaryBehavior:: + kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); + }), + ROOT_ID, + 13 /* text_offset at end of root. */, + {"TextPosition anchor_id=1 text_offset=13 " + "affinity=downstream annotated_text=Line 1\nLine 2<>"}}, + TextNavigationTestParam{ + base::BindRepeating([](const TestPositionType& position) { + return position->CreatePreviousWordEndPosition( + AXBoundaryBehavior:: + kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); + }), + TEXT_FIELD_ID, + 13 /* text_offset at end of text field */, + {"TextPosition anchor_id=4 text_offset=13 " + "affinity=downstream annotated_text=Line 1\nLine 2<>"}}, + TextNavigationTestParam{ + base::BindRepeating([](const TestPositionType& position) { + return position->CreatePreviousWordEndPosition( + AXBoundaryBehavior:: + kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); + }), + STATIC_TEXT1_ID, + 5 /* text_offset */, + {"TextPosition anchor_id=5 text_offset=4 " + "affinity=downstream annotated_text=Line< >1", + "TextPosition anchor_id=5 text_offset=4 " + "affinity=downstream annotated_text=Line< >1"}}, + TextNavigationTestParam{ + base::BindRepeating([](const TestPositionType& position) { + return position->CreatePreviousWordEndPosition( + AXBoundaryBehavior:: + kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); + }), + INLINE_BOX2_ID, + 4 /* text_offset */, + {"TextPosition anchor_id=9 text_offset=4 " + "affinity=downstream annotated_text=Line< >2"}})); + +INSTANTIATE_TEST_SUITE_P( + CreatePreviousWordEndPositionWithBoundaryBehaviorStopAtLastAnchorBoundary, + AXPositionTextNavigationTestWithParam, + ::testing::Values( + TextNavigationTestParam{ + base::BindRepeating([](const TestPositionType& position) { + return position->CreatePreviousWordEndPosition( + AXBoundaryBehavior::kStopAtLastAnchorBoundary); + }), + ROOT_ID, + 13 /* text_offset at end of root. */, + {"TextPosition anchor_id=1 text_offset=11 " + "affinity=downstream annotated_text=Line 1\nLine< >2", + "TextPosition anchor_id=1 text_offset=6 " + "affinity=downstream annotated_text=Line 1<\n>Line 2", + "TextPosition anchor_id=1 text_offset=4 " + "affinity=downstream annotated_text=Line< >1\nLine 2", + "TextPosition anchor_id=1 text_offset=0 " + "affinity=downstream annotated_text=<L>ine 1\nLine 2", + "TextPosition anchor_id=1 text_offset=0 " + "affinity=downstream annotated_text=<L>ine 1\nLine 2"}}, + TextNavigationTestParam{ + base::BindRepeating([](const TestPositionType& position) { + return position->CreatePreviousWordEndPosition( + AXBoundaryBehavior::kStopAtLastAnchorBoundary); }), TEXT_FIELD_ID, 13 /* text_offset at end of text field */, @@ -13350,7 +14297,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreatePreviousWordEndPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); }), STATIC_TEXT1_ID, 5 /* text_offset */, @@ -13363,12 +14310,12 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreatePreviousWordEndPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); }), INLINE_BOX2_ID, 4 /* text_offset */, - {"TextPosition anchor_id=6 text_offset=6 " - "affinity=downstream annotated_text=Line 1<>", + {"TextPosition anchor_id=6 text_offset=6 affinity=downstream " + "annotated_text=Line 1<>", "TextPosition anchor_id=6 text_offset=4 " "affinity=downstream annotated_text=Line< >1", "TextPosition anchor_id=2 text_offset=0 " @@ -13383,7 +14330,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreateNextLineStartPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); }), ROOT_ID, 0 /* text_offset */, @@ -13393,7 +14340,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreateNextLineStartPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); }), TEXT_FIELD_ID, 0 /* text_offset */, @@ -13403,7 +14350,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreateNextLineStartPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); }), STATIC_TEXT1_ID, 1 /* text_offset */, @@ -13413,7 +14360,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreateNextLineStartPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); }), INLINE_BOX2_ID, 4 /* text_offset */, @@ -13426,7 +14373,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreateNextLineStartPosition( - AXBoundaryBehavior::StopAtAnchorBoundary); + AXBoundaryBehavior::kStopAtAnchorBoundary); }), ROOT_ID, 0 /* text_offset */, @@ -13437,7 +14384,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreateNextLineStartPosition( - AXBoundaryBehavior::StopAtAnchorBoundary); + AXBoundaryBehavior::kStopAtAnchorBoundary); }), TEXT_FIELD_ID, 0 /* text_offset */, @@ -13448,7 +14395,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreateNextLineStartPosition( - AXBoundaryBehavior::StopAtAnchorBoundary); + AXBoundaryBehavior::kStopAtAnchorBoundary); }), STATIC_TEXT1_ID, 1 /* text_offset */, @@ -13457,7 +14404,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreateNextLineStartPosition( - AXBoundaryBehavior::StopAtAnchorBoundary); + AXBoundaryBehavior::kStopAtAnchorBoundary); }), INLINE_BOX2_ID, 4 /* text_offset */, @@ -13465,13 +14412,14 @@ "affinity=downstream annotated_text=Line 2<>"}})); INSTANTIATE_TEST_SUITE_P( - CreateNextLineStartPositionWithBoundaryBehaviorStopIfAlreadyAtBoundary, + CreateNextLineStartPositionWithBoundaryBehaviorStopAtAnchorBoundaryOrIfAlreadyAtBoundary, AXPositionTextNavigationTestWithParam, ::testing::Values( TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreateNextLineStartPosition( - AXBoundaryBehavior::StopIfAlreadyAtBoundary); + AXBoundaryBehavior:: + kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); }), ROOT_ID, 0 /* text_offset */, @@ -13482,7 +14430,8 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreateNextLineStartPosition( - AXBoundaryBehavior::StopIfAlreadyAtBoundary); + AXBoundaryBehavior:: + kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); }), TEXT_FIELD_ID, 0 /* text_offset */, @@ -13493,22 +14442,25 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreateNextLineStartPosition( - AXBoundaryBehavior::StopIfAlreadyAtBoundary); + AXBoundaryBehavior:: + kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); }), STATIC_TEXT1_ID, 1 /* text_offset */, - {"TextPosition anchor_id=9 text_offset=0 " - "affinity=downstream annotated_text=<L>ine 2", - "TextPosition anchor_id=9 text_offset=0 " - "affinity=downstream annotated_text=<L>ine 2"}}, + {"TextPosition anchor_id=5 text_offset=6 affinity=downstream " + "annotated_text=Line 1<>", + "TextPosition anchor_id=5 text_offset=6 affinity=downstream " + "annotated_text=Line 1<>"}}, TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreateNextLineStartPosition( - AXBoundaryBehavior::StopIfAlreadyAtBoundary); + AXBoundaryBehavior:: + kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); }), INLINE_BOX2_ID, 4 /* text_offset */, - {"NullPosition"}})); + {"TextPosition anchor_id=9 text_offset=6 affinity=downstream " + "annotated_text=Line 2<>"}})); INSTANTIATE_TEST_SUITE_P( CreateNextLineStartPositionWithBoundaryBehaviorStopAtLastAnchorBoundary, @@ -13517,7 +14469,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreateNextLineStartPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); }), ROOT_ID, 0 /* text_offset */, @@ -13530,7 +14482,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreateNextLineStartPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); }), TEXT_FIELD_ID, 0 /* text_offset */, @@ -13543,7 +14495,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreateNextLineStartPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); }), STATIC_TEXT1_ID, 1 /* text_offset */, @@ -13556,7 +14508,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreateNextLineStartPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); }), INLINE_BOX2_ID, 4 /* text_offset */, @@ -13572,7 +14524,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreatePreviousLineStartPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); }), ROOT_ID, 13 /* text_offset at the end of root. */, @@ -13584,7 +14536,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreatePreviousLineStartPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); }), TEXT_FIELD_ID, 13 /* text_offset at end of text field */, @@ -13596,7 +14548,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreatePreviousLineStartPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); }), STATIC_TEXT1_ID, 5 /* text_offset */, @@ -13606,7 +14558,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreatePreviousLineStartPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); }), INLINE_BOX2_ID, 4 /* text_offset */, @@ -13623,7 +14575,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreatePreviousLineStartPosition( - AXBoundaryBehavior::StopAtAnchorBoundary); + AXBoundaryBehavior::kStopAtAnchorBoundary); }), ROOT_ID, 13 /* text_offset at the end of root. */, @@ -13636,7 +14588,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreatePreviousLineStartPosition( - AXBoundaryBehavior::StopAtAnchorBoundary); + AXBoundaryBehavior::kStopAtAnchorBoundary); }), TEXT_FIELD_ID, 13 /* text_offset at end of text field */, @@ -13649,7 +14601,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreatePreviousLineStartPosition( - AXBoundaryBehavior::StopAtAnchorBoundary); + AXBoundaryBehavior::kStopAtAnchorBoundary); }), STATIC_TEXT1_ID, 5 /* text_offset */, @@ -13660,7 +14612,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreatePreviousLineStartPosition( - AXBoundaryBehavior::StopAtAnchorBoundary); + AXBoundaryBehavior::kStopAtAnchorBoundary); }), INLINE_BOX2_ID, 4 /* text_offset */, @@ -13670,13 +14622,14 @@ "affinity=downstream annotated_text=<L>ine 2"}})); INSTANTIATE_TEST_SUITE_P( - CreatePreviousLineStartPositionWithBoundaryBehaviorStopIfAlreadyAtBoundary, + CreatePreviousLineStartPositionWithBoundaryBehaviorStopAtAnchorBoundaryOrIfAlreadyAtBoundary, AXPositionTextNavigationTestWithParam, ::testing::Values( TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreatePreviousLineStartPosition( - AXBoundaryBehavior::StopIfAlreadyAtBoundary); + AXBoundaryBehavior:: + kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); }), ROOT_ID, 13 /* text_offset at the end of root. */, @@ -13687,7 +14640,8 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreatePreviousLineStartPosition( - AXBoundaryBehavior::StopIfAlreadyAtBoundary); + AXBoundaryBehavior:: + kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); }), TEXT_FIELD_ID, 13 /* text_offset at end of text field */, @@ -13698,7 +14652,8 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreatePreviousLineStartPosition( - AXBoundaryBehavior::StopIfAlreadyAtBoundary); + AXBoundaryBehavior:: + kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); }), STATIC_TEXT1_ID, 5 /* text_offset */, @@ -13709,7 +14664,8 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreatePreviousLineStartPosition( - AXBoundaryBehavior::StopIfAlreadyAtBoundary); + AXBoundaryBehavior:: + kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); }), INLINE_BOX2_ID, 4 /* text_offset */, @@ -13725,7 +14681,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreatePreviousLineStartPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); }), ROOT_ID, 13 /* text_offset at the end of root. */, @@ -13738,7 +14694,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreatePreviousLineStartPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); }), TEXT_FIELD_ID, 13 /* text_offset at end of text field */, @@ -13751,7 +14707,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreatePreviousLineStartPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); }), STATIC_TEXT1_ID, 5 /* text_offset */, @@ -13762,7 +14718,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreatePreviousLineStartPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); }), INLINE_BOX2_ID, 4 /* text_offset */, @@ -13780,7 +14736,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreateNextLineEndPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); }), ROOT_ID, 0 /* text_offset */, @@ -13792,7 +14748,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreateNextLineEndPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); }), TEXT_FIELD_ID, 0 /* text_offset */, @@ -13804,7 +14760,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreateNextLineEndPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); }), STATIC_TEXT1_ID, 1 /* text_offset */, @@ -13816,7 +14772,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreateNextLineEndPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); }), INLINE_BOX2_ID, 4 /* text_offset */, @@ -13831,7 +14787,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreateNextLineEndPosition( - AXBoundaryBehavior::StopAtAnchorBoundary); + AXBoundaryBehavior::kStopAtAnchorBoundary); }), ROOT_ID, 0 /* text_offset */, @@ -13844,7 +14800,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreateNextLineEndPosition( - AXBoundaryBehavior::StopAtAnchorBoundary); + AXBoundaryBehavior::kStopAtAnchorBoundary); }), TEXT_FIELD_ID, 0 /* text_offset */, @@ -13857,7 +14813,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreateNextLineEndPosition( - AXBoundaryBehavior::StopAtAnchorBoundary); + AXBoundaryBehavior::kStopAtAnchorBoundary); }), STATIC_TEXT1_ID, 1 /* text_offset */, @@ -13868,7 +14824,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreateNextLineEndPosition( - AXBoundaryBehavior::StopAtAnchorBoundary); + AXBoundaryBehavior::kStopAtAnchorBoundary); }), INLINE_BOX2_ID, 4 /* text_offset */, @@ -13878,13 +14834,14 @@ "affinity=downstream annotated_text=Line 2<>"}})); INSTANTIATE_TEST_SUITE_P( - CreateNextLineEndPositionWithBoundaryBehaviorStopIfAlreadyAtBoundary, + CreateNextLineEndPositionWithBoundaryBehaviorStopAtAnchorBoundaryOrIfAlreadyAtBoundary, AXPositionTextNavigationTestWithParam, ::testing::Values( TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreateNextLineEndPosition( - AXBoundaryBehavior::StopIfAlreadyAtBoundary); + AXBoundaryBehavior:: + kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); }), ROOT_ID, 0 /* text_offset */, @@ -13895,7 +14852,8 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreateNextLineEndPosition( - AXBoundaryBehavior::StopIfAlreadyAtBoundary); + AXBoundaryBehavior:: + kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); }), TEXT_FIELD_ID, 0 /* text_offset */, @@ -13906,7 +14864,8 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreateNextLineEndPosition( - AXBoundaryBehavior::StopIfAlreadyAtBoundary); + AXBoundaryBehavior:: + kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); }), STATIC_TEXT1_ID, 1 /* text_offset */, @@ -13917,7 +14876,8 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreateNextLineEndPosition( - AXBoundaryBehavior::StopIfAlreadyAtBoundary); + AXBoundaryBehavior:: + kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); }), INLINE_BOX2_ID, 4 /* text_offset */, @@ -13933,7 +14893,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreateNextLineEndPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); }), ROOT_ID, 0 /* text_offset */, @@ -13946,7 +14906,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreateNextLineEndPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); }), TEXT_FIELD_ID, 0 /* text_offset */, @@ -13959,7 +14919,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreateNextLineEndPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); }), STATIC_TEXT1_ID, 1 /* text_offset */, @@ -13972,7 +14932,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreateNextLineEndPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); }), INLINE_BOX2_ID, 4 /* text_offset */, @@ -13988,7 +14948,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreatePreviousLineEndPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); }), ROOT_ID, 13 /* text_offset at end of root. */, @@ -13998,7 +14958,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreatePreviousLineEndPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); }), TEXT_FIELD_ID, 13 /* text_offset at end of text field */, @@ -14008,7 +14968,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreatePreviousLineEndPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); }), ROOT_ID, 5 /* text_offset on the last character of "Line 1". */, @@ -14016,7 +14976,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreatePreviousLineEndPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); }), TEXT_FIELD_ID, 5 /* text_offset on the last character of "Line 1". */, @@ -14024,7 +14984,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreatePreviousLineEndPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); }), INLINE_BOX2_ID, 4 /* text_offset */, @@ -14034,7 +14994,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreatePreviousLineEndPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); }), INLINE_BOX2_ID, 0 /* text_offset */, @@ -14049,7 +15009,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreatePreviousLineEndPosition( - AXBoundaryBehavior::StopAtAnchorBoundary); + AXBoundaryBehavior::kStopAtAnchorBoundary); }), ROOT_ID, 13 /* text_offset at end of root. */, @@ -14060,7 +15020,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreatePreviousLineEndPosition( - AXBoundaryBehavior::StopAtAnchorBoundary); + AXBoundaryBehavior::kStopAtAnchorBoundary); }), TEXT_FIELD_ID, 13 /* text_offset at end of text field */, @@ -14071,7 +15031,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreatePreviousLineEndPosition( - AXBoundaryBehavior::StopAtAnchorBoundary); + AXBoundaryBehavior::kStopAtAnchorBoundary); }), ROOT_ID, 5 /* text_offset on the last character of "Line 1". */, @@ -14082,7 +15042,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreatePreviousLineEndPosition( - AXBoundaryBehavior::StopAtAnchorBoundary); + AXBoundaryBehavior::kStopAtAnchorBoundary); }), TEXT_FIELD_ID, 5 /* text_offset on the last character of "Line 1". */, @@ -14093,7 +15053,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreatePreviousLineEndPosition( - AXBoundaryBehavior::StopAtAnchorBoundary); + AXBoundaryBehavior::kStopAtAnchorBoundary); }), INLINE_BOX2_ID, 4 /* text_offset */, @@ -14104,7 +15064,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreatePreviousLineEndPosition( - AXBoundaryBehavior::StopAtAnchorBoundary); + AXBoundaryBehavior::kStopAtAnchorBoundary); }), INLINE_BOX2_ID, 0 /* text_offset */, @@ -14114,13 +15074,14 @@ "affinity=downstream annotated_text=<L>ine 2"}})); INSTANTIATE_TEST_SUITE_P( - CreatePreviousLineEndPositionWithBoundaryBehaviorStopIfAlreadyAtBoundary, + CreatePreviousLineEndPositionWithBoundaryBehaviorStopAtAnchorBoundaryOrIfAlreadyAtBoundary, AXPositionTextNavigationTestWithParam, ::testing::Values( TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreatePreviousLineEndPosition( - AXBoundaryBehavior::StopIfAlreadyAtBoundary); + AXBoundaryBehavior:: + kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); }), ROOT_ID, 12 /* text_offset one before the end of root. */, @@ -14131,7 +15092,8 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreatePreviousLineEndPosition( - AXBoundaryBehavior::StopIfAlreadyAtBoundary); + AXBoundaryBehavior:: + kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); }), TEXT_FIELD_ID, 12 /* text_offset one before the end of text field */, @@ -14142,33 +15104,39 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreatePreviousLineEndPosition( - AXBoundaryBehavior::StopIfAlreadyAtBoundary); + AXBoundaryBehavior:: + kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); }), INLINE_BOX1_ID, 2 /* text_offset */, - {"NullPosition"}}, + {"TextPosition anchor_id=6 text_offset=0 affinity=downstream " + "annotated_text=<L>ine 1", + "TextPosition anchor_id=6 text_offset=0 affinity=downstream " + "annotated_text=<L>ine 1"}}, TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreatePreviousLineEndPosition( - AXBoundaryBehavior::StopIfAlreadyAtBoundary); + AXBoundaryBehavior:: + kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); }), INLINE_BOX2_ID, 4 /* text_offset */, - {"TextPosition anchor_id=6 text_offset=6 " - "affinity=downstream annotated_text=Line 1<>", - "TextPosition anchor_id=6 text_offset=6 " - "affinity=downstream annotated_text=Line 1<>"}}, + {"TextPosition anchor_id=9 text_offset=0 affinity=downstream " + "annotated_text=<L>ine 2", + "TextPosition anchor_id=9 text_offset=0 affinity=downstream " + "annotated_text=<L>ine 2"}}, TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreatePreviousLineEndPosition( - AXBoundaryBehavior::StopIfAlreadyAtBoundary); + AXBoundaryBehavior:: + kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); }), INLINE_BOX2_ID, 0 /* text_offset */, - {"TextPosition anchor_id=6 text_offset=6 " - "affinity=downstream annotated_text=Line 1<>", - "TextPosition anchor_id=6 text_offset=6 " - "affinity=downstream annotated_text=Line 1<>"}})); + {"TextPosition anchor_id=9 text_offset=0 affinity=downstream " + "annotated_text=<L>ine 2", + "TextPosition anchor_id=9 text_offset=0 affinity=downstream " + "annotated_text=<L>ine 2"}})); INSTANTIATE_TEST_SUITE_P( CreatePreviousLineEndPositionWithBoundaryBehaviorStopAtLastAnchorBoundary, @@ -14177,7 +15145,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreatePreviousLineEndPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); }), ROOT_ID, 13 /* text_offset at end of root. */, @@ -14190,7 +15158,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreatePreviousLineEndPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); }), TEXT_FIELD_ID, 13 /* text_offset at end of text field */, @@ -14203,7 +15171,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreatePreviousLineEndPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); }), ROOT_ID, 5 /* text_offset on the last character of "Line 1". */, @@ -14214,7 +15182,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreatePreviousLineEndPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); }), TEXT_FIELD_ID, 5 /* text_offset on the last character of "Line 1". */, @@ -14225,7 +15193,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreatePreviousLineEndPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); }), INLINE_BOX2_ID, 4 /* text_offset */, @@ -14238,7 +15206,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreatePreviousLineEndPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); }), INLINE_BOX2_ID, 0 /* text_offset */, @@ -14256,7 +15224,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreateNextParagraphStartPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); }), ROOT_ID, 0 /* text_offset */, @@ -14265,7 +15233,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreateNextParagraphStartPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); }), TEXT_FIELD_ID, 0 /* text_offset */, @@ -14274,7 +15242,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreateNextParagraphStartPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); }), STATIC_TEXT1_ID, 1 /* text_offset */, @@ -14283,7 +15251,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreateNextParagraphStartPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); }), INLINE_BOX2_ID, 4 /* text_offset */, @@ -14296,7 +15264,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreateNextParagraphStartPosition( - AXBoundaryBehavior::StopAtAnchorBoundary); + AXBoundaryBehavior::kStopAtAnchorBoundary); }), ROOT_ID, 0 /* text_offset */, @@ -14307,7 +15275,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreateNextParagraphStartPosition( - AXBoundaryBehavior::StopAtAnchorBoundary); + AXBoundaryBehavior::kStopAtAnchorBoundary); }), TEXT_FIELD_ID, 0 /* text_offset */, @@ -14318,7 +15286,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreateNextParagraphStartPosition( - AXBoundaryBehavior::StopAtAnchorBoundary); + AXBoundaryBehavior::kStopAtAnchorBoundary); }), STATIC_TEXT1_ID, 1 /* text_offset */, @@ -14327,7 +15295,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreateNextParagraphStartPosition( - AXBoundaryBehavior::StopAtAnchorBoundary); + AXBoundaryBehavior::kStopAtAnchorBoundary); }), INLINE_BOX2_ID, 4 /* text_offset */, @@ -14335,13 +15303,14 @@ "affinity=downstream annotated_text=Line 2<>"}})); INSTANTIATE_TEST_SUITE_P( - CreateNextParagraphStartPositionWithBoundaryBehaviorStopIfAlreadyAtBoundary, + CreateNextParagraphStartPositionWithBoundaryBehaviorStopAtAnchorBoundaryOrIfAlreadyAtBoundary, AXPositionTextNavigationTestWithParam, ::testing::Values( TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreateNextParagraphStartPosition( - AXBoundaryBehavior::StopIfAlreadyAtBoundary); + AXBoundaryBehavior:: + kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); }), ROOT_ID, 0 /* text_offset */, @@ -14352,7 +15321,8 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreateNextParagraphStartPosition( - AXBoundaryBehavior::StopIfAlreadyAtBoundary); + AXBoundaryBehavior:: + kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); }), TEXT_FIELD_ID, 0 /* text_offset */, @@ -14363,22 +15333,25 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreateNextParagraphStartPosition( - AXBoundaryBehavior::StopIfAlreadyAtBoundary); + AXBoundaryBehavior:: + kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); }), STATIC_TEXT1_ID, 1 /* text_offset */, - {"TextPosition anchor_id=9 text_offset=0 " - "affinity=downstream annotated_text=<L>ine 2", - "TextPosition anchor_id=9 text_offset=0 " - "affinity=downstream annotated_text=<L>ine 2"}}, + {"TextPosition anchor_id=5 text_offset=6 affinity=downstream " + "annotated_text=Line 1<>", + "TextPosition anchor_id=5 text_offset=6 affinity=downstream " + "annotated_text=Line 1<>"}}, TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreateNextParagraphStartPosition( - AXBoundaryBehavior::StopIfAlreadyAtBoundary); + AXBoundaryBehavior:: + kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); }), INLINE_BOX2_ID, 4 /* text_offset */, - {"NullPosition"}})); + {"TextPosition anchor_id=9 text_offset=6 affinity=downstream " + "annotated_text=Line 2<>"}})); INSTANTIATE_TEST_SUITE_P( CreateNextParagraphStartPositionWithBoundaryBehaviorStopAtLastAnchorBoundary, @@ -14387,7 +15360,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreateNextParagraphStartPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); }), ROOT_ID, 0 /* text_offset */, @@ -14400,7 +15373,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreateNextParagraphStartPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); }), TEXT_FIELD_ID, 0 /* text_offset */, @@ -14413,7 +15386,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreateNextParagraphStartPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); }), STATIC_TEXT1_ID, 1 /* text_offset */, @@ -14426,7 +15399,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreateNextParagraphStartPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); }), INLINE_BOX2_ID, 4 /* text_offset */, @@ -14442,7 +15415,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreatePreviousParagraphStartPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); }), ROOT_ID, 13 /* text_offset at the end of root. */, @@ -14454,7 +15427,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreatePreviousParagraphStartPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); }), TEXT_FIELD_ID, 13 /* text_offset at end of text field */, @@ -14466,7 +15439,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreatePreviousParagraphStartPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); }), STATIC_TEXT1_ID, 5 /* text_offset */, @@ -14476,7 +15449,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreatePreviousParagraphStartPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); }), INLINE_BOX2_ID, 4 /* text_offset */, @@ -14493,7 +15466,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreatePreviousParagraphStartPosition( - AXBoundaryBehavior::StopAtAnchorBoundary); + AXBoundaryBehavior::kStopAtAnchorBoundary); }), ROOT_ID, 13 /* text_offset at the end of root. */, @@ -14506,7 +15479,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreatePreviousParagraphStartPosition( - AXBoundaryBehavior::StopAtAnchorBoundary); + AXBoundaryBehavior::kStopAtAnchorBoundary); }), TEXT_FIELD_ID, 13 /* text_offset at end of text field */, @@ -14519,7 +15492,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreatePreviousParagraphStartPosition( - AXBoundaryBehavior::StopAtAnchorBoundary); + AXBoundaryBehavior::kStopAtAnchorBoundary); }), STATIC_TEXT1_ID, 5 /* text_offset */, @@ -14530,7 +15503,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreatePreviousParagraphStartPosition( - AXBoundaryBehavior::StopAtAnchorBoundary); + AXBoundaryBehavior::kStopAtAnchorBoundary); }), INLINE_BOX2_ID, 4 /* text_offset */, @@ -14540,13 +15513,14 @@ "affinity=downstream annotated_text=<L>ine 2"}})); INSTANTIATE_TEST_SUITE_P( - CreatePreviousParagraphStartPositionWithBoundaryBehaviorStopIfAlreadyAtBoundary, + CreatePreviousParagraphStartPositionWithBoundaryBehaviorStopAtAnchorBoundaryOrIfAlreadyAtBoundary, AXPositionTextNavigationTestWithParam, ::testing::Values( TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreatePreviousParagraphStartPosition( - AXBoundaryBehavior::StopIfAlreadyAtBoundary); + AXBoundaryBehavior:: + kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); }), ROOT_ID, 13 /* text_offset at the end of root. */, @@ -14557,7 +15531,8 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreatePreviousParagraphStartPosition( - AXBoundaryBehavior::StopIfAlreadyAtBoundary); + AXBoundaryBehavior:: + kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); }), TEXT_FIELD_ID, 13 /* text_offset at end of text field */, @@ -14568,7 +15543,8 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreatePreviousParagraphStartPosition( - AXBoundaryBehavior::StopIfAlreadyAtBoundary); + AXBoundaryBehavior:: + kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); }), STATIC_TEXT1_ID, 5 /* text_offset */, @@ -14579,7 +15555,8 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreatePreviousParagraphStartPosition( - AXBoundaryBehavior::StopIfAlreadyAtBoundary); + AXBoundaryBehavior:: + kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); }), INLINE_BOX2_ID, 4 /* text_offset */, @@ -14595,7 +15572,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreatePreviousParagraphStartPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); }), ROOT_ID, 13 /* text_offset at the end of root. */, @@ -14610,7 +15587,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreatePreviousParagraphStartPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); }), TEXT_FIELD_ID, 13 /* text_offset at end of text field */, @@ -14623,7 +15600,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreatePreviousParagraphStartPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); }), STATIC_TEXT1_ID, 5 /* text_offset */, @@ -14634,7 +15611,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreatePreviousParagraphStartPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); }), INLINE_BOX2_ID, 4 /* text_offset */, @@ -14652,7 +15629,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreateNextParagraphEndPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); }), ROOT_ID, 0 /* text_offset */, @@ -14664,7 +15641,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreateNextParagraphEndPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); }), TEXT_FIELD_ID, 0 /* text_offset */, @@ -14676,7 +15653,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreateNextParagraphEndPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); }), STATIC_TEXT1_ID, 1 /* text_offset */, @@ -14688,7 +15665,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreateNextParagraphEndPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); }), INLINE_BOX2_ID, 4 /* text_offset */, @@ -14703,7 +15680,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreateNextParagraphEndPosition( - AXBoundaryBehavior::StopAtAnchorBoundary); + AXBoundaryBehavior::kStopAtAnchorBoundary); }), ROOT_ID, 0 /* text_offset */, @@ -14718,7 +15695,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreateNextParagraphEndPosition( - AXBoundaryBehavior::StopAtAnchorBoundary); + AXBoundaryBehavior::kStopAtAnchorBoundary); }), TEXT_FIELD_ID, 0 /* text_offset */, @@ -14733,7 +15710,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreateNextParagraphEndPosition( - AXBoundaryBehavior::StopAtAnchorBoundary); + AXBoundaryBehavior::kStopAtAnchorBoundary); }), STATIC_TEXT1_ID, 1 /* text_offset */, @@ -14744,7 +15721,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreateNextParagraphEndPosition( - AXBoundaryBehavior::StopAtAnchorBoundary); + AXBoundaryBehavior::kStopAtAnchorBoundary); }), INLINE_BOX2_ID, 4 /* text_offset */, @@ -14754,13 +15731,14 @@ "affinity=downstream annotated_text=Line 2<>"}})); INSTANTIATE_TEST_SUITE_P( - CreateNextParagraphEndPositionWithBoundaryBehaviorStopIfAlreadyAtBoundary, + CreateNextParagraphEndPositionWithBoundaryBehaviorStopAtAnchorBoundaryOrIfAlreadyAtBoundary, AXPositionTextNavigationTestWithParam, ::testing::Values( TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreateNextParagraphEndPosition( - AXBoundaryBehavior::StopIfAlreadyAtBoundary); + AXBoundaryBehavior:: + kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); }), ROOT_ID, 0 /* text_offset */, @@ -14776,18 +15754,20 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreateNextParagraphEndPosition( - AXBoundaryBehavior::StopIfAlreadyAtBoundary); + AXBoundaryBehavior:: + kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); }), TEXT_FIELD_ID, 5 /* text_offset */, {"TextPosition anchor_id=4 text_offset=6 " "affinity=downstream annotated_text=Line 1<\n>Line 2", - "TextPosition anchor_id=4 text_offset=13 " - "affinity=downstream annotated_text=Line 1\nLine 2<>"}}, + "TextPosition anchor_id=4 text_offset=6 affinity=downstream " + "annotated_text=Line 1<\n>Line 2"}}, TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreateNextParagraphEndPosition( - AXBoundaryBehavior::StopIfAlreadyAtBoundary); + AXBoundaryBehavior:: + kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); }), STATIC_TEXT1_ID, 1 /* text_offset */, @@ -14798,7 +15778,8 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreateNextParagraphEndPosition( - AXBoundaryBehavior::StopIfAlreadyAtBoundary); + AXBoundaryBehavior:: + kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); }), INLINE_BOX2_ID, 4 /* text_offset */, @@ -14809,25 +15790,27 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreateNextParagraphEndPosition( - AXBoundaryBehavior::StopIfAlreadyAtBoundary); + AXBoundaryBehavior:: + kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); }), LINE_BREAK_ID, 0 /* text_offset */, - {"TextPosition anchor_id=9 text_offset=6 " - "affinity=downstream annotated_text=Line 2<>", - "TextPosition anchor_id=9 text_offset=6 " - "affinity=downstream annotated_text=Line 2<>"}}, + {"TextPosition anchor_id=7 text_offset=1 affinity=downstream " + "annotated_text=\n<>", + "TextPosition anchor_id=7 text_offset=1 affinity=downstream " + "annotated_text=\n<>"}}, TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreateNextParagraphEndPosition( - AXBoundaryBehavior::StopIfAlreadyAtBoundary); + AXBoundaryBehavior:: + kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); }), LINE_BREAK_ID, 1 /* text_offset */, - {"TextPosition anchor_id=9 text_offset=6 " - "affinity=downstream annotated_text=Line 2<>", - "TextPosition anchor_id=9 text_offset=6 " - "affinity=downstream annotated_text=Line 2<>"}})); + {"TextPosition anchor_id=7 text_offset=1 affinity=downstream " + "annotated_text=\n<>", + "TextPosition anchor_id=7 text_offset=1 affinity=downstream " + "annotated_text=\n<>"}})); INSTANTIATE_TEST_SUITE_P( CreateNextParagraphEndPositionWithBoundaryBehaviorStopAtLastAnchorBoundary, @@ -14836,7 +15819,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreateNextParagraphEndPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); }), ROOT_ID, 0 /* text_offset */, @@ -14849,7 +15832,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreateNextParagraphEndPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); }), TEXT_FIELD_ID, 0 /* text_offset */, @@ -14862,7 +15845,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreateNextParagraphEndPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); }), STATIC_TEXT1_ID, 1 /* text_offset */, @@ -14875,7 +15858,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreateNextParagraphEndPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); }), INLINE_BOX2_ID, 4 /* text_offset */, @@ -14891,7 +15874,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreatePreviousParagraphEndPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); }), ROOT_ID, 13 /* text_offset at end of root. */, @@ -14903,7 +15886,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreatePreviousParagraphEndPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); }), TEXT_FIELD_ID, 13 /* text_offset at end of text field */, @@ -14915,7 +15898,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreatePreviousParagraphEndPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); }), ROOT_ID, 5 /* text_offset on the last character of "Line 1". */, @@ -14925,7 +15908,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreatePreviousParagraphEndPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); }), TEXT_FIELD_ID, 5 /* text_offset on the last character of "Line 1". */, @@ -14935,7 +15918,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreatePreviousParagraphEndPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); }), INLINE_BOX2_ID, 4 /* text_offset */, @@ -14947,7 +15930,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreatePreviousParagraphEndPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); }), INLINE_BOX2_ID, 0 /* text_offset */, @@ -14964,7 +15947,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreatePreviousParagraphEndPosition( - AXBoundaryBehavior::StopAtAnchorBoundary); + AXBoundaryBehavior::kStopAtAnchorBoundary); }), ROOT_ID, 13 /* text_offset at end of root. */, @@ -14977,7 +15960,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreatePreviousParagraphEndPosition( - AXBoundaryBehavior::StopAtAnchorBoundary); + AXBoundaryBehavior::kStopAtAnchorBoundary); }), TEXT_FIELD_ID, 13 /* text_offset at end of text field */, @@ -14990,7 +15973,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreatePreviousParagraphEndPosition( - AXBoundaryBehavior::StopAtAnchorBoundary); + AXBoundaryBehavior::kStopAtAnchorBoundary); }), ROOT_ID, 5 /* text_offset on the last character of "Line 1". */, @@ -15001,7 +15984,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreatePreviousParagraphEndPosition( - AXBoundaryBehavior::StopAtAnchorBoundary); + AXBoundaryBehavior::kStopAtAnchorBoundary); }), TEXT_FIELD_ID, 5 /* text_offset on the last character of "Line 1". */, @@ -15012,7 +15995,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreatePreviousParagraphEndPosition( - AXBoundaryBehavior::StopAtAnchorBoundary); + AXBoundaryBehavior::kStopAtAnchorBoundary); }), INLINE_BOX2_ID, 4 /* text_offset */, @@ -15023,7 +16006,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreatePreviousParagraphEndPosition( - AXBoundaryBehavior::StopAtAnchorBoundary); + AXBoundaryBehavior::kStopAtAnchorBoundary); }), INLINE_BOX2_ID, 0 /* text_offset */, @@ -15033,13 +16016,14 @@ "annotated_text=<L>ine 2"}})); INSTANTIATE_TEST_SUITE_P( - CreatePreviousParagraphEndPositionWithBoundaryBehaviorStopIfAlreadyAtBoundary, + CreatePreviousParagraphEndPositionWithBoundaryBehaviorStopAtAnchorBoundaryOrIfAlreadyAtBoundary, AXPositionTextNavigationTestWithParam, ::testing::Values( TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreatePreviousParagraphEndPosition( - AXBoundaryBehavior::StopIfAlreadyAtBoundary); + AXBoundaryBehavior:: + kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); }), ROOT_ID, 12 /* text_offset one before the end of root. */, @@ -15050,7 +16034,8 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreatePreviousParagraphEndPosition( - AXBoundaryBehavior::StopIfAlreadyAtBoundary); + AXBoundaryBehavior:: + kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); }), TEXT_FIELD_ID, 12 /* text_offset one before the end of text field */, @@ -15061,58 +16046,63 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreatePreviousParagraphEndPosition( - AXBoundaryBehavior::StopIfAlreadyAtBoundary); + AXBoundaryBehavior:: + kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); }), INLINE_BOX1_ID, 2 /* text_offset */, - {"TextPosition anchor_id=3 text_offset=0 " - "affinity=downstream annotated_text=<>", - "TextPosition anchor_id=3 text_offset=0 " - "affinity=downstream annotated_text=<>"}}, + {"TextPosition anchor_id=6 text_offset=0 affinity=downstream " + "annotated_text=<L>ine 1", + "TextPosition anchor_id=6 text_offset=0 affinity=downstream " + "annotated_text=<L>ine 1"}}, TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreatePreviousParagraphEndPosition( - AXBoundaryBehavior::StopIfAlreadyAtBoundary); + AXBoundaryBehavior:: + kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); }), INLINE_BOX2_ID, 4 /* text_offset */, - {"TextPosition anchor_id=6 text_offset=6 " - "affinity=downstream annotated_text=Line 1<>", - "TextPosition anchor_id=6 text_offset=6 " - "affinity=downstream annotated_text=Line 1<>"}}, + {"TextPosition anchor_id=9 text_offset=0 affinity=downstream " + "annotated_text=<L>ine 2", + "TextPosition anchor_id=9 text_offset=0 affinity=downstream " + "annotated_text=<L>ine 2"}}, TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreatePreviousParagraphEndPosition( - AXBoundaryBehavior::StopIfAlreadyAtBoundary); + AXBoundaryBehavior:: + kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); }), INLINE_BOX2_ID, 0 /* text_offset */, - {"TextPosition anchor_id=6 text_offset=6 " - "affinity=downstream annotated_text=Line 1<>", - "TextPosition anchor_id=6 text_offset=6 " - "affinity=downstream annotated_text=Line 1<>"}}, + {"TextPosition anchor_id=9 text_offset=0 affinity=downstream " + "annotated_text=<L>ine 2", + "TextPosition anchor_id=9 text_offset=0 affinity=downstream " + "annotated_text=<L>ine 2"}}, TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreatePreviousParagraphEndPosition( - AXBoundaryBehavior::StopIfAlreadyAtBoundary); + AXBoundaryBehavior:: + kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); }), LINE_BREAK_ID, 0 /* text_offset */, - {"TextPosition anchor_id=6 text_offset=6 " - "affinity=downstream annotated_text=Line 1<>", - "TextPosition anchor_id=6 text_offset=6 " - "affinity=downstream annotated_text=Line 1<>"}}, + {"TextPosition anchor_id=7 text_offset=0 affinity=downstream " + "annotated_text=<\n>", + "TextPosition anchor_id=7 text_offset=0 affinity=downstream " + "annotated_text=<\n>"}}, TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreatePreviousParagraphEndPosition( - AXBoundaryBehavior::StopIfAlreadyAtBoundary); + AXBoundaryBehavior:: + kStopAtAnchorBoundaryOrIfAlreadyAtBoundary); }), LINE_BREAK_ID, 1 /* text_offset */, - {"TextPosition anchor_id=6 text_offset=6 " - "affinity=downstream annotated_text=Line 1<>", - "TextPosition anchor_id=6 text_offset=6 " - "affinity=downstream annotated_text=Line 1<>"}})); + {"TextPosition anchor_id=7 text_offset=0 affinity=downstream " + "annotated_text=<\n>", + "TextPosition anchor_id=7 text_offset=0 affinity=downstream " + "annotated_text=<\n>"}})); INSTANTIATE_TEST_SUITE_P( CreatePreviousParagraphEndPositionWithBoundaryBehaviorStopAtLastAnchorBoundary, @@ -15121,7 +16111,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreatePreviousParagraphEndPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); }), ROOT_ID, 13 /* text_offset at end of root. */, @@ -15134,7 +16124,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreatePreviousParagraphEndPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); }), TEXT_FIELD_ID, 13 /* text_offset at end of text field */, @@ -15147,7 +16137,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreatePreviousParagraphEndPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); }), ROOT_ID, 5 /* text_offset on the last character of "Line 1". */, @@ -15158,7 +16148,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreatePreviousParagraphEndPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); }), TEXT_FIELD_ID, 5 /* text_offset on the last character of "Line 1". */, @@ -15169,7 +16159,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreatePreviousParagraphEndPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); }), INLINE_BOX2_ID, 4 /* text_offset */, @@ -15182,7 +16172,7 @@ TextNavigationTestParam{ base::BindRepeating([](const TestPositionType& position) { return position->CreatePreviousParagraphEndPosition( - AXBoundaryBehavior::StopAtLastAnchorBoundary); + AXBoundaryBehavior::kStopAtLastAnchorBoundary); }), INLINE_BOX2_ID, 0 /* text_offset */,
diff --git a/ui/accessibility/ax_position.h b/ui/accessibility/ax_position.h index beb5dd2..186aba7 100644 --- a/ui/accessibility/ax_position.h +++ b/ui/accessibility/ax_position.h
@@ -47,13 +47,30 @@ enum class AXPositionKind { NULL_POSITION, TREE_POSITION, TEXT_POSITION }; // Defines how creating the next or previous position should behave whenever we -// are at or are crossing a boundary, such as at the start of an anchor, a word -// or a line. +// are at or are crossing a text boundary, (such as the start of a word or the +// end of a sentence), or whenever we are crossing the initial position's +// anchor. Note that the "anchor" is the node to which an AXPosition is attached +// to. It is provided when a position is created. enum class AXBoundaryBehavior { - CrossBoundary, - StopAtAnchorBoundary, - StopIfAlreadyAtBoundary, - StopAtLastAnchorBoundary + // Crosses all boundaries. If the bounds of the current window-like container, + // such as the current webpage, have been reached, returns a null position. + kCrossBoundary, + // Stops if the current anchor is crossed, regardless of how the resulting + // position has been computed. For example, even though in order to find the + // next or previous word start in a text field we need to descend to the leaf + // equivalent position, this behavior will only stop when the bounds of the + // original anchor, i.e. the text field, have been crossed. + kStopAtAnchorBoundary, + // Stops if the current anchor is crossed or if we are already at the + // requested boundary. For an example of the former, imagine a position inside + // a text field and the resulting position outside it. For an example of the + // latter, say we are moving to the previous word start position when we are + // already at the start of a word. + kStopAtAnchorBoundaryOrIfAlreadyAtBoundary, + // Stops if we have reached the start or the end of of a window-like + // container, such as a webpage, a PDF, a dialog, the browser's UI (AKA + // Views), or the whole desktop. + kStopAtLastAnchorBoundary }; // Describes in further detail what type of boundary a current position is on. @@ -198,7 +215,8 @@ base::RepeatingCallback<bool(const AXPositionInstance&)>; using BoundaryTextOffsetsFunc = - base::RepeatingCallback<std::vector<int32_t>(const AXPositionInstance&)>; + base::RepeatingCallback<const std::vector<int32_t>&( + const AXPositionInstance&)>; static const int BEFORE_TEXT = -1; static const int INVALID_INDEX = -2; @@ -343,7 +361,7 @@ } } - if (!IsTextPosition() || text_offset_ > MaxTextOffset()) + if (!IsTextPosition() || text_offset_ < 0 || text_offset_ > MaxTextOffset()) return str; const std::u16string& text = GetText(); @@ -558,7 +576,18 @@ } bool AtStartOfWord() const { - AXPositionInstance text_position = AsLeafTextPosition(); + AXPositionInstance text_position; + if (!AtEndOfAnchor()) { + // We could get a leaf text position at the end of its anchor, where word + // start offsets would surely not be present. In such cases, we need to + // normalize to the start of the next leaf anchor. We avoid making this + // change when we are at the end of our anchor because this could + // effectively shift the position forward. + text_position = AsLeafTextPositionBeforeCharacter(); + } else { + text_position = AsLeafTextPosition(); + } + switch (text_position->kind_) { case AXPositionKind::NULL_POSITION: return false; @@ -566,7 +595,7 @@ NOTREACHED(); return false; case AXPositionKind::TEXT_POSITION: { - const std::vector<int32_t> word_starts = + const std::vector<int32_t>& word_starts = text_position->GetWordStartOffsets(); return base::Contains(word_starts, int32_t{text_position->text_offset_}); @@ -575,7 +604,18 @@ } bool AtEndOfWord() const { - AXPositionInstance text_position = AsLeafTextPosition(); + AXPositionInstance text_position; + if (!AtStartOfAnchor()) { + // We could get a leaf text position at the start of its anchor, where + // word end offsets would surely not be present. In such cases, we need to + // normalize to the end of the previous leaf anchor. We avoid making this + // change when we are at the start of our anchor because this could + // effectively shift the position backward. + text_position = AsLeafTextPositionAfterCharacter(); + } else { + text_position = AsLeafTextPosition(); + } + switch (text_position->kind_) { case AXPositionKind::NULL_POSITION: return false; @@ -583,13 +623,71 @@ NOTREACHED(); return false; case AXPositionKind::TEXT_POSITION: { - const std::vector<int32_t> word_ends = + const std::vector<int32_t>& word_ends = text_position->GetWordEndOffsets(); return base::Contains(word_ends, int32_t{text_position->text_offset_}); } } } + bool AtStartOfSentence() const { + AXPositionInstance text_position; + if (!AtEndOfAnchor()) { + // We could get a leaf text position at the end of its anchor, where + // sentence start offsets would surely not be present. In such cases, we + // need to normalize to the start of the next leaf anchor. We avoid making + // this change when we are at the end of our anchor because this could + // effectively shift the position forward. + text_position = AsLeafTextPositionBeforeCharacter(); + } else { + text_position = AsLeafTextPosition(); + } + + switch (text_position->kind_) { + case AXPositionKind::NULL_POSITION: + return false; + case AXPositionKind::TREE_POSITION: + NOTREACHED(); + return false; + case AXPositionKind::TEXT_POSITION: { + const std::vector<int32_t>& sentence_starts = + text_position->GetAnchor()->GetIntListAttribute( + ax::mojom::IntListAttribute::kSentenceStarts); + return base::Contains(sentence_starts, + int32_t{text_position->text_offset_}); + } + } + } + + bool AtEndOfSentence() const { + AXPositionInstance text_position; + if (!AtStartOfAnchor()) { + // We could get a leaf text position at the start of its anchor, where + // sentence end offsets would surely not be present. In such cases, we + // need to normalize to the end of the previous leaf anchor. We avoid + // making this change when we are at the start of our anchor because this + // could effectively shift the position backward. + text_position = AsLeafTextPositionAfterCharacter(); + } else { + text_position = AsLeafTextPosition(); + } + + switch (text_position->kind_) { + case AXPositionKind::NULL_POSITION: + return false; + case AXPositionKind::TREE_POSITION: + NOTREACHED(); + return false; + case AXPositionKind::TEXT_POSITION: { + const std::vector<int32_t>& sentence_ends = + text_position->GetAnchor()->GetIntListAttribute( + ax::mojom::IntListAttribute::kSentenceEnds); + return base::Contains(sentence_ends, + int32_t{text_position->text_offset_}); + } + } + } + bool AtStartOfLine() const { AXPositionInstance text_position = AsLeafTextPosition(); switch (text_position->kind_) { @@ -1606,40 +1704,50 @@ // text boundary, and creates an AXRange that spans from the former to the // latter. The resulting AXRange is always a forward range: its anchor always // comes before its focus in document order. The resulting AXRange is bounded - // by the anchor of this position, i.e. the AXBoundaryBehavior is set to - // StopAtAnchorBoundary. The exception is ax::mojom::TextBoundary::kWebPage, - // where this behavior won't make sense. This behavior is based on current - // platform needs and might be relaxed if necessary in the future. + // by the anchor of this position and the requested boundary type, i.e. the + // AXBoundaryBehavior is set to + // `AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary`. The + // exception is `ax::mojom::TextBoundary::kWebPage`, where this behavior won't + // make sense. This behavior is based on current platform needs and might be + // relaxed if necessary in the future. // - // Please note that |expand_behavior| should have no effect for - // ax::mojom::TextBoundary::kObject and ax::mojom::TextBoundary::kWebPage + // Observe that `expand_behavior` has an effect only when this position is + // between text units, e.g. between words, lines, paragraphs, etc. Also, + // please note that `expand_behavior` should have no effect for + // `ax::mojom::TextBoundary::kObject` and `ax::mojom::TextBoundary::kWebPage` // because the range should be the same regardless if we first move left or // right. AXRangeType ExpandToEnclosingTextBoundary( ax::mojom::TextBoundary boundary, AXRangeExpandBehavior expand_behavior) const { - AXBoundaryBehavior boundary_behavior = - AXBoundaryBehavior::StopAtAnchorBoundary; - if (boundary == ax::mojom::TextBoundary::kWebPage) - boundary_behavior = AXBoundaryBehavior::CrossBoundary; + AXBoundaryBehavior left_boundary_behavior = + AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary; + AXBoundaryBehavior right_boundary_behavior = + AXBoundaryBehavior::kStopAtAnchorBoundary; + if (boundary == ax::mojom::TextBoundary::kWebPage) { + left_boundary_behavior = AXBoundaryBehavior::kCrossBoundary; + right_boundary_behavior = AXBoundaryBehavior::kCrossBoundary; + } switch (expand_behavior) { case AXRangeExpandBehavior::kLeftFirst: { AXPositionInstance left_position = CreatePositionAtTextBoundary( - boundary, ax::mojom::MoveDirection::kBackward, boundary_behavior); + boundary, ax::mojom::MoveDirection::kBackward, + left_boundary_behavior); AXPositionInstance right_position = left_position->CreatePositionAtTextBoundary( boundary, ax::mojom::MoveDirection::kForward, - boundary_behavior); + right_boundary_behavior); return AXRangeType(std::move(left_position), std::move(right_position)); } case AXRangeExpandBehavior::kRightFirst: { AXPositionInstance right_position = CreatePositionAtTextBoundary( - boundary, ax::mojom::MoveDirection::kForward, boundary_behavior); + boundary, ax::mojom::MoveDirection::kForward, + left_boundary_behavior); AXPositionInstance left_position = right_position->CreatePositionAtTextBoundary( boundary, ax::mojom::MoveDirection::kBackward, - boundary_behavior); + right_boundary_behavior); return AXRangeType(std::move(left_position), std::move(right_position)); } } @@ -1898,19 +2006,55 @@ break; case ax::mojom::TextBoundary::kSentenceEnd: - NOTREACHED() << "Sentence boundaries are not yet supported."; - return CreateNullPosition(); + switch (direction) { + case ax::mojom::MoveDirection::kNone: + NOTREACHED(); + break; + case ax::mojom::MoveDirection::kBackward: + resulting_position = + CreatePreviousSentenceEndPosition(boundary_behavior); + break; + case ax::mojom::MoveDirection::kForward: + resulting_position = + CreateNextSentenceEndPosition(boundary_behavior); + break; + } + break; case ax::mojom::TextBoundary::kSentenceStart: - NOTREACHED() << "Sentence boundaries are not yet supported."; - return CreateNullPosition(); + switch (direction) { + case ax::mojom::MoveDirection::kNone: + NOTREACHED(); + break; + case ax::mojom::MoveDirection::kBackward: + resulting_position = + CreatePreviousSentenceStartPosition(boundary_behavior); + break; + case ax::mojom::MoveDirection::kForward: + resulting_position = + CreateNextSentenceStartPosition(boundary_behavior); + break; + } + break; case ax::mojom::TextBoundary::kSentenceStartOrEnd: - NOTREACHED() << "Sentence boundaries are not yet supported."; - return CreateNullPosition(); + switch (direction) { + case ax::mojom::MoveDirection::kNone: + NOTREACHED(); + break; + case ax::mojom::MoveDirection::kBackward: + resulting_position = + CreatePreviousSentenceStartPosition(boundary_behavior); + break; + case ax::mojom::MoveDirection::kForward: + resulting_position = + CreateNextSentenceEndPosition(boundary_behavior); + break; + } + break; case ax::mojom::TextBoundary::kWebPage: - DCHECK_EQ(boundary_behavior, AXBoundaryBehavior::CrossBoundary) + DCHECK_EQ(boundary_behavior, AXBoundaryBehavior::kCrossBoundary) << "We can't reach the start of the whole contents if we are " "disallowed from crossing boundaries."; switch (direction) { @@ -2499,17 +2643,18 @@ // See also http://www.unicode.org/reports/tr29/#Grapheme_Cluster_Boundaries AXPositionInstance CreateNextCharacterPosition( AXBoundaryBehavior boundary_behavior) const { - if (boundary_behavior == AXBoundaryBehavior::StopAtAnchorBoundary && + if ((boundary_behavior == AXBoundaryBehavior::kStopAtAnchorBoundary || + boundary_behavior == + AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary) && AtEndOfAnchor()) { return Clone(); } AXPositionInstance text_position = AsLeafTextPositionBeforeCharacter(); if (text_position->IsNullPosition()) { - if (boundary_behavior == AXBoundaryBehavior::StopIfAlreadyAtBoundary || - boundary_behavior == AXBoundaryBehavior::StopAtLastAnchorBoundary) { + if (boundary_behavior != AXBoundaryBehavior::kCrossBoundary) text_position = Clone(); - } + return text_position; } @@ -2525,7 +2670,8 @@ // positions that have the same affinity, since // `AsLeafTextPositionBeforeCharacter` resets the affinity to downstream, // while the original affinity might have been upstream. - if (boundary_behavior == AXBoundaryBehavior::StopIfAlreadyAtBoundary && + if (boundary_behavior == + AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary && (AtEndOfAnchor() || *text_position == *CloneWithDownstreamAffinity())) { return Clone(); } @@ -2551,9 +2697,9 @@ if (GetAnchor() == common_anchor) { text_position = text_position->CreateAncestorPosition( common_anchor, ax::mojom::MoveDirection::kForward); - } else if (boundary_behavior == AXBoundaryBehavior::StopAtAnchorBoundary) { + } else if (boundary_behavior == AXBoundaryBehavior::kStopAtAnchorBoundary) { // If the next character position crosses the current anchor boundary - // with StopAtAnchorBoundary, snap to the end of the current anchor. + // with kStopAtAnchorBoundary, snap to the end of the current anchor. return CreatePositionAtEndOfAnchor(); } @@ -2575,17 +2721,18 @@ // grapheme cluster. AXPositionInstance CreatePreviousCharacterPosition( AXBoundaryBehavior boundary_behavior) const { - if (boundary_behavior == AXBoundaryBehavior::StopAtAnchorBoundary && + if ((boundary_behavior == AXBoundaryBehavior::kStopAtAnchorBoundary || + boundary_behavior == + AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary) && AtStartOfAnchor()) { return Clone(); } AXPositionInstance text_position = AsLeafTextPositionAfterCharacter(); if (text_position->IsNullPosition()) { - if (boundary_behavior == AXBoundaryBehavior::StopIfAlreadyAtBoundary || - boundary_behavior == AXBoundaryBehavior::StopAtLastAnchorBoundary) { + if (boundary_behavior != AXBoundaryBehavior::kCrossBoundary) text_position = Clone(); - } + return text_position; } @@ -2600,7 +2747,8 @@ // our current anchor. We also need to ignore any differences that might be // due to the affinity, because that should not be a determining factor as // to whether we would stop if we are already at boundary or not. - if (boundary_behavior == AXBoundaryBehavior::StopIfAlreadyAtBoundary && + if (boundary_behavior == + AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary && (AtStartOfAnchor() || *text_position == *CloneWithUpstreamAffinity() || *text_position == *CloneWithDownstreamAffinity())) { return Clone(); @@ -2625,7 +2773,7 @@ if (GetAnchor() == common_anchor) { text_position = text_position->CreateAncestorPosition( common_anchor, ax::mojom::MoveDirection::kBackward); - } else if (boundary_behavior == AXBoundaryBehavior::StopAtAnchorBoundary) { + } else if (boundary_behavior == AXBoundaryBehavior::kStopAtAnchorBoundary) { // If the previous character position crosses the current anchor boundary // with StopAtAnchorBoundary, snap to the start of the current anchor. return CreatePositionAtStartOfAnchor(); @@ -2839,18 +2987,30 @@ BoundaryConditionPredicate at_end_condition, BoundaryTextOffsetsFunc get_start_offsets = BoundaryTextOffsetsFunc()) const { - AXPositionInstance text_position = AsLeafTextPosition(); + AXPositionInstance text_position; + if (!AtEndOfAnchor()) { + // We could get a leaf text position at the end of its anchor, where + // boundary start offsets would surely not be present. In such cases, we + // need to normalize to the start of the next leaf anchor. We avoid making + // this change when we are at the end of our anchor because this could + // effectively shift the position forward. + text_position = AsLeafTextPositionBeforeCharacter(); + } else { + text_position = AsLeafTextPosition(); + } + if (text_position->IsNullPosition()) return text_position; - if (boundary_behavior != AXBoundaryBehavior::StopIfAlreadyAtBoundary) { + if (boundary_behavior != + AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary) { text_position = text_position->CreateAdjacentLeafTextPosition(move_direction); if (text_position->IsNullPosition()) { // There is no adjacent position to move to; in such case, CrossBoundary // behavior shall return a null position, while any other behavior shall // fallback to return the initial position. - if (boundary_behavior == AXBoundaryBehavior::CrossBoundary) + if (boundary_behavior == AXBoundaryBehavior::kCrossBoundary) return text_position; return Clone(); } @@ -2883,7 +3043,10 @@ } if (next_position->IsNullPosition()) { - if (boundary_behavior == AXBoundaryBehavior::StopAtAnchorBoundary) { + if (boundary_behavior == AXBoundaryBehavior::kStopAtAnchorBoundary || + boundary_behavior == + AXBoundaryBehavior:: + kStopAtAnchorBoundaryOrIfAlreadyAtBoundary) { switch (move_direction) { case ax::mojom::MoveDirection::kNone: NOTREACHED(); @@ -2898,7 +3061,7 @@ } if (boundary_behavior == - AXBoundaryBehavior::StopAtLastAnchorBoundary) { + AXBoundaryBehavior::kStopAtLastAnchorBoundary) { // We can't simply return the following position; break and after // this loop we'll try to do some adjustments to text_position. switch (move_direction) { @@ -2934,7 +3097,10 @@ if (GetAnchor() == common_anchor) { text_position = text_position->CreateAncestorPosition(common_anchor, move_direction); - } else if (boundary_behavior == AXBoundaryBehavior::StopAtAnchorBoundary) { + } else if (boundary_behavior == AXBoundaryBehavior::kStopAtAnchorBoundary || + boundary_behavior == + AXBoundaryBehavior:: + kStopAtAnchorBoundaryOrIfAlreadyAtBoundary) { switch (move_direction) { case ax::mojom::MoveDirection::kNone: NOTREACHED(); @@ -2955,14 +3121,13 @@ text_position = text_position->AsTreePosition(); AXPositionInstance unignored_position = text_position->AsUnignoredPosition( AXPositionAdjustmentBehavior::kMoveForward); - // If there are no unignored positions in |move_direction| then - // `text_position` is anchored in ignored content at the end of the whole - // content. For StopAtLastAnchorBoundary, try to adjust in the opposite - // direction to return a position within the whole content just before - // crossing into the ignored content. This will be the last unignored anchor - // boundary. + // If there are no unignored positions then `text_position` is anchored in + // ignored content at the end of the whole content. For + // `kStopAtLastAnchorBoundary`, try to adjust in the opposite direction to + // return a position within the whole content just before crossing into the + // ignored content. This will be the last unignored anchor boundary. if (unignored_position->IsNullPosition() && - boundary_behavior == AXBoundaryBehavior::StopAtLastAnchorBoundary) { + boundary_behavior == AXBoundaryBehavior::kStopAtLastAnchorBoundary) { unignored_position = text_position->AsUnignoredPosition( AXPositionAdjustmentBehavior::kMoveBackward); } @@ -2976,18 +3141,30 @@ BoundaryConditionPredicate at_end_condition, BoundaryTextOffsetsFunc get_end_offsets = BoundaryTextOffsetsFunc()) const { - AXPositionInstance text_position = AsLeafTextPosition(); + AXPositionInstance text_position; + if (!AtStartOfAnchor()) { + // We could get a leaf text position at the start of its anchor, where + // boundary end offsets would surely not be present. In such cases, we + // need to normalize to the end of the previous leaf anchor. We avoid + // making this change when we are at the start of our anchor because this + // could effectively shift the position backward. + text_position = AsLeafTextPositionAfterCharacter(); + } else { + text_position = AsLeafTextPosition(); + } + if (text_position->IsNullPosition()) return text_position; - if (boundary_behavior != AXBoundaryBehavior::StopIfAlreadyAtBoundary) { + if (boundary_behavior != + AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary) { text_position = text_position->CreateAdjacentLeafTextPosition(move_direction); if (text_position->IsNullPosition()) { // There is no adjacent position to move to; in such case, CrossBoundary // behavior shall return a null position, while any other behavior shall // fallback to return the initial position. - if (boundary_behavior == AXBoundaryBehavior::CrossBoundary) + if (boundary_behavior == AXBoundaryBehavior::kCrossBoundary) return text_position; return Clone(); } @@ -3023,7 +3200,10 @@ } if (next_position->IsNullPosition()) { - if (boundary_behavior == AXBoundaryBehavior::StopAtAnchorBoundary) { + if (boundary_behavior == AXBoundaryBehavior::kStopAtAnchorBoundary || + boundary_behavior == + AXBoundaryBehavior:: + kStopAtAnchorBoundaryOrIfAlreadyAtBoundary) { switch (move_direction) { case ax::mojom::MoveDirection::kNone: NOTREACHED(); @@ -3038,7 +3218,7 @@ } if (boundary_behavior == - AXBoundaryBehavior::StopAtLastAnchorBoundary) { + AXBoundaryBehavior::kStopAtLastAnchorBoundary) { // We can't simply return the following position; break and after // this loop we'll try to do some adjustments to text_position. switch (move_direction) { @@ -3074,7 +3254,10 @@ if (GetAnchor() == common_anchor) { text_position = text_position->CreateAncestorPosition(common_anchor, move_direction); - } else if (boundary_behavior == AXBoundaryBehavior::StopAtAnchorBoundary) { + } else if (boundary_behavior == AXBoundaryBehavior::kStopAtAnchorBoundary || + boundary_behavior == + AXBoundaryBehavior:: + kStopAtAnchorBoundaryOrIfAlreadyAtBoundary) { switch (move_direction) { case ax::mojom::MoveDirection::kNone: NOTREACHED(); @@ -3101,7 +3284,7 @@ text_position->CloneWithDownstreamAffinity(); if (downstream_position->AtStartOfAnchor() || downstream_position->AtEndOfAnchor() || - !at_start_condition.Run(downstream_position)) { + !downstream_position->AtStartOfLine()) { text_position->affinity_ = ax::mojom::TextAffinity::kDownstream; } } @@ -3110,21 +3293,54 @@ text_position = text_position->AsTreePosition(); AXPositionInstance unignored_position = text_position->AsUnignoredPosition( AXPositionAdjustmentBehavior::kMoveBackward); - // If there are no unignored positions in |move_direction| then - // |text_position| is anchored in ignored content at the start or end - // of the whole content. - // For StopAtLastAnchorBoundary, try to adjust in the opposite direction - // to return a position within the whole content just before crossing into - // the ignored content. This will be the last unignored anchor boundary. + // If there are no unignored positions then `text_position` is anchored in + // ignored content at the start or end of the whole content. For + // `kStopAtLastAnchorBoundary`, try to adjust in the opposite direction to + // return a position within the whole content just before crossing into the + // ignored content. This will be the last unignored anchor boundary. if (unignored_position->IsNullPosition() && - boundary_behavior == AXBoundaryBehavior::StopAtLastAnchorBoundary) { + boundary_behavior == AXBoundaryBehavior::kStopAtLastAnchorBoundary) { unignored_position = text_position->AsUnignoredPosition( AXPositionAdjustmentBehavior::kMoveForward); } return unignored_position; } - // TODO(nektar): Add sentence navigation methods. + AXPositionInstance CreateNextSentenceStartPosition( + AXBoundaryBehavior boundary_behavior) const { + return CreateBoundaryStartPosition( + boundary_behavior, ax::mojom::MoveDirection::kForward, + base::BindRepeating(&AtStartOfSentencePredicate), + base::BindRepeating(&AtEndOfSentencePredicate), + base::BindRepeating(&GetSentenceStartOffsetsFunc)); + } + + AXPositionInstance CreatePreviousSentenceStartPosition( + AXBoundaryBehavior boundary_behavior) const { + return CreateBoundaryStartPosition( + boundary_behavior, ax::mojom::MoveDirection::kBackward, + base::BindRepeating(&AtStartOfSentencePredicate), + base::BindRepeating(&AtEndOfSentencePredicate), + base::BindRepeating(&GetSentenceStartOffsetsFunc)); + } + + AXPositionInstance CreateNextSentenceEndPosition( + AXBoundaryBehavior boundary_behavior) const { + return CreateBoundaryEndPosition( + boundary_behavior, ax::mojom::MoveDirection::kForward, + base::BindRepeating(&AtStartOfSentencePredicate), + base::BindRepeating(&AtEndOfSentencePredicate), + base::BindRepeating(&GetSentenceEndOffsetsFunc)); + } + + AXPositionInstance CreatePreviousSentenceEndPosition( + AXBoundaryBehavior boundary_behavior) const { + return CreateBoundaryEndPosition( + boundary_behavior, ax::mojom::MoveDirection::kBackward, + base::BindRepeating(&AtStartOfSentencePredicate), + base::BindRepeating(&AtEndOfSentencePredicate), + base::BindRepeating(&GetSentenceEndOffsetsFunc)); + } // Uses depth-first pre-order traversal. AXPositionInstance CreateNextAnchorPosition() const { @@ -3649,13 +3865,13 @@ if (GetAnchor()->IsCollapsedMenuListPopUpButton()) return true; - // All anchor nodes that are empty leaf nodes or have only ignored - // descendants should be treated as empty objects. Empty leaf nodes do not - // expose their descendants to platform accessibility APIs, but may have - // unignored descendants. They do not have any text content, however, hence - // they are still empty from our perspective. For example, an empty text - // field may still have an unignored generic container inside it. - if (AnchorUnignoredChildCount() && !GetAnchor()->IsEmptyLeaf()) + // All anchor nodes that are empty leaf nodes should be treated as empty + // objects. Empty leaf nodes do not expose their descendants to platform + // accessibility APIs, but may have unignored descendants. They do not have + // any inner text, hence they are empty from our perspective. For example, + // an empty text field may still have an unignored generic container inside + // it. + if (!GetAnchor()->IsEmptyLeaf()) return false; // <embed> and <object> elements with non empty children should not be @@ -4092,35 +4308,55 @@ return current_anchor_text_attributes; } - std::vector<int32_t> GetWordStartOffsets() const { - if (IsNullPosition()) - return std::vector<int32_t>(); + const std::vector<int32_t>& GetWordStartOffsets() const { + if (IsNullPosition()) { + static const base::NoDestructor<std::vector<int32_t>> empty_word_starts; + return *empty_word_starts; + } DCHECK(GetAnchor()); - // Embedded object replacement characters are not represented in the - // "kWordStarts" attribute so we need to special case them here. - if (IsEmptyObjectReplacedByCharacter()) - return {0}; + // An embedded object replacement character is exposed in a node's text + // representation when a control, such as a text field, is empty. Since the + // control has no text, no word start offsets are present in the + // `ax::mojom::IntListAttribute::kWordStarts` attribute, so we need to + // special case them here. + if (IsEmptyObjectReplacedByCharacter()) { + // Using braces ensures that the vector will contain the given value, and + // not create a vector of size 0. + static const base::NoDestructor<std::vector<int32_t>> + embedded_word_starts({0}); + return *embedded_word_starts; + } return GetAnchor()->GetIntListAttribute( ax::mojom::IntListAttribute::kWordStarts); } - std::vector<int32_t> GetWordEndOffsets() const { - if (IsNullPosition()) - return std::vector<int32_t>(); + const std::vector<int32_t>& GetWordEndOffsets() const { + if (IsNullPosition()) { + static const base::NoDestructor<std::vector<int32_t>> empty_word_ends; + return *empty_word_ends; + } DCHECK(GetAnchor()); - // Embedded object replacement characters are not represented in the - // "kWordEnds" attribute so we need to special case them here. + // An embedded object replacement character is exposed in a node's text + // representation when a control, such as a text field, is empty. Since the + // control has no text, no word end offsets are present in the + // `ax::mojom::IntListAttribute::kWordEnds` attribute, so we need to special + // case them here. // // Since the whole text exposed inside of an embedded object is of // length 1 (the embedded object replacement character), the word end offset - // is positioned at 1. Because we want to treat the embedded object - // replacement characters as ordinary characters, it wouldn't be consistent - // to assume they have no length and return 0 instead of 1. - if (IsEmptyObjectReplacedByCharacter()) - return {1}; + // is positioned at 1. Because we want to treat embedded object replacement + // characters as ordinary characters, it wouldn't be consistent to assume + // they have no length and return 0 instead of 1. + if (IsEmptyObjectReplacedByCharacter()) { + // Using braces ensures that the vector will contain the given value, and + // not create a vector of size 1. + static const base::NoDestructor<std::vector<int32_t>> embedded_word_ends( + {1}); + return *embedded_word_ends; + } return GetAnchor()->GetIntListAttribute( ax::mojom::IntListAttribute::kWordEnds); @@ -4442,6 +4678,22 @@ return position->AtEndOfLine(); } + static bool AtStartOfSentencePredicate(const AXPositionInstance& position) { + // Sentence boundaries should be at specific text offsets that are "visible" + // to assistive software, hence not ignored. Ignored nodes are often used + // for additional layout information, such as line and paragraph boundaries. + // Their text is not currently processed. + return !position->IsIgnored() && position->AtStartOfSentence(); + } + + static bool AtEndOfSentencePredicate(const AXPositionInstance& position) { + // Sentence boundaries should be at specific text offsets that are "visible" + // to assistive software, hence not ignored. Ignored nodes are often used + // for additional layout information, such as line and paragraph boundaries. + // Their text is not currently processed. + return !position->IsIgnored() && position->AtEndOfSentence(); + } + static bool AtStartOfFormatPredicate(const AXPositionInstance& position) { return position->AtStartOfFormat(); } @@ -4667,12 +4919,35 @@ return false; } - static std::vector<int32_t> GetWordStartOffsetsFunc( + static const std::vector<int32_t>& GetSentenceStartOffsetsFunc( + const AXPositionInstance& position) { + if (position->IsNullPosition()) { + static const base::NoDestructor<std::vector<int32_t>> + empty_sentence_starts; + return *empty_sentence_starts; + } + DCHECK(position->GetAnchor()); + return position->GetAnchor()->GetIntListAttribute( + ax::mojom::IntListAttribute::kSentenceStarts); + } + + static const std::vector<int32_t>& GetSentenceEndOffsetsFunc( + const AXPositionInstance& position) { + if (position->IsNullPosition()) { + static const base::NoDestructor<std::vector<int32_t>> empty_sentence_ends; + return *empty_sentence_ends; + } + DCHECK(position->GetAnchor()); + return position->GetAnchor()->GetIntListAttribute( + ax::mojom::IntListAttribute::kSentenceEnds); + } + + static const std::vector<int32_t>& GetWordStartOffsetsFunc( const AXPositionInstance& position) { return position->GetWordStartOffsets(); } - static std::vector<int32_t> GetWordEndOffsetsFunc( + static const std::vector<int32_t>& GetWordEndOffsetsFunc( const AXPositionInstance& position) { return position->GetWordEndOffsets(); } @@ -4758,7 +5033,7 @@ return Clone(); AXPositionInstance text_position = AsTextPosition(); - const std::vector<int32_t> boundary_offsets = + const std::vector<int32_t>& boundary_offsets = get_offsets.Run(text_position); if (boundary_offsets.empty()) return text_position; @@ -4813,7 +5088,7 @@ return Clone(); AXPositionInstance text_position = AsTextPosition(); - const std::vector<int32_t> boundary_offsets = + const std::vector<int32_t>& boundary_offsets = get_offsets.Run(text_position); switch (move_direction) { case ax::mojom::MoveDirection::kNone: @@ -4846,15 +5121,16 @@ // // This method is the first step for CreateBoundary[Start|End]Position to // guarantee that the resulting position when using a boundary behavior other - // than `AXBoundaryBehavior::StopIfAlreadyAtBoundary` is not equivalent to the - // initial position. That's why ignored positions are also skipped. Otherwise, - // if a boundary is present on an ignored position, the search for the next or - // previous boundary would stop prematurely. Note that if there are multiple - // adjacent ignored positions and all of them create a boundary, we'll skip - // them all on purpose. For example, adjacent ignored paragraph boundaries - // could be created by using multiple aria-hidden divs next to one another. - // These should not contribute more than one paragraph boundary to the tree's - // text representation, otherwise this will create user confusion. + // than `AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary` is + // not equivalent to the initial position. That's why ignored positions are + // also skipped. Otherwise, if a boundary is present on an ignored position, + // the search for the next or previous boundary would stop prematurely. Note + // that if there are multiple adjacent ignored positions and all of them + // create a boundary, we'll skip them all on purpose. For example, adjacent + // ignored paragraph boundaries could be created by using multiple aria-hidden + // divs next to one another. These should not contribute more than one + // paragraph boundary to the tree's text representation, otherwise this will + // create user confusion. // // Note that using the `CompareTo` method with text positions does not take // into account position affinity or the order of their anchors in the tree:
diff --git a/ui/accessibility/platform/ax_platform_node_auralinux_unittest.cc b/ui/accessibility/platform/ax_platform_node_auralinux_unittest.cc index 909dcac..e2c5a8e3 100644 --- a/ui/accessibility/platform/ax_platform_node_auralinux_unittest.cc +++ b/ui/accessibility/platform/ax_platform_node_auralinux_unittest.cc
@@ -1300,8 +1300,7 @@ AXNodeData root; root.id = 1; root.role = ax::mojom::Role::kTextField; - root.AddStringAttribute(ax::mojom::StringAttribute::kValue, - "A decently long string \xE2\x98\xBA with an emoji."); + root.SetValue("A decently long string \xE2\x98\xBA with an emoji."); Init(root); AtkObject* root_obj(GetRootAtkObject()); @@ -1519,7 +1518,8 @@ } #if ATK_CHECK_VERSION(2, 10, 0) -TEST_F(AXPlatformNodeAuraLinuxTest, TestAtkTextParagraphGranularity) { +TEST_F(AXPlatformNodeAuraLinuxTest, DISABLED_TestAtkTextParagraphGranularity) { + // TODO(nektar): Enable navigating by paragraphs in plain text. AXNodeData root; root.id = 1; root.role = ax::mojom::Role::kTextField;
diff --git a/ui/accessibility/platform/ax_platform_node_base.cc b/ui/accessibility/platform/ax_platform_node_base.cc index d049c57..8776c85 100644 --- a/ui/accessibility/platform/ax_platform_node_base.cc +++ b/ui/accessibility/platform/ax_platform_node_base.cc
@@ -2004,17 +2004,27 @@ ax::mojom::MoveDirection direction, ax::mojom::TextAffinity affinity) const { DCHECK_NE(boundary, ax::mojom::TextBoundary::kNone); - if (boundary != ax::mojom::TextBoundary::kSentenceStart) { - absl::optional<int> boundary_offset = - GetDelegate()->FindTextBoundary(boundary, offset, direction, affinity); - if (boundary_offset.has_value()) - return *boundary_offset; + if (!delegate_) + return offset; // Unable to compute text boundary. + + const AXPosition position = delegate_->CreateTextPositionAt(offset, affinity); + // On Windows and Linux ATK, searching for a text boundary should always stop + // at the boundary of the current object. + auto boundary_behavior = AXBoundaryBehavior::kStopAtAnchorBoundary; + // On Windows and Linux ATK, it is standard text navigation behavior to stop + // if we are searching in the backwards direction and the current position is + // already at the required text boundary. + if (direction == ax::mojom::MoveDirection::kBackward) { + boundary_behavior = + AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary; } - std::vector<int32_t> unused_line_start_offsets; - return static_cast<int>( - FindAccessibleTextBoundary(GetHypertext(), unused_line_start_offsets, - boundary, offset, direction, affinity)); + const AXPosition boundary_position = position->CreatePositionAtTextBoundary( + boundary, direction, boundary_behavior); + if (boundary_position->IsNullPosition()) + return -1; + DCHECK_GE(boundary_position->text_offset(), 0); + return boundary_position->text_offset(); } AXPlatformNodeBase* AXPlatformNodeBase::NearestLeafToPoint(
diff --git a/ui/accessibility/platform/ax_platform_node_base.h b/ui/accessibility/platform/ax_platform_node_base.h index 3ad7323..01f92d8 100644 --- a/ui/accessibility/platform/ax_platform_node_base.h +++ b/ui/accessibility/platform/ax_platform_node_base.h
@@ -15,6 +15,7 @@ #include "build/build_config.h" #include "ui/accessibility/ax_enums.mojom-forward.h" #include "ui/accessibility/ax_node.h" +#include "ui/accessibility/ax_node_position.h" #include "ui/accessibility/ax_text_attributes.h" #include "ui/accessibility/platform/ax_platform_node.h" #include "ui/accessibility/platform/ax_platform_node_delegate.h" @@ -60,6 +61,8 @@ class AX_EXPORT AXPlatformNodeBase : public AXPlatformNode { public: + using AXPosition = AXNodePosition::AXPositionInstance; + ~AXPlatformNodeBase() override; AXPlatformNodeBase(const AXPlatformNodeBase&) = delete; AXPlatformNodeBase& operator=(const AXPlatformNodeBase&) = delete; @@ -348,6 +351,7 @@ // This method finds text boundaries in the text used for platform text APIs. // Implementations may use side-channel data such as line or word indices to // produce appropriate results. + // Returns -1 if the requested boundary has not been found. virtual int FindTextBoundary(ax::mojom::TextBoundary boundary, int offset, ax::mojom::MoveDirection direction,
diff --git a/ui/accessibility/platform/ax_platform_node_delegate.h b/ui/accessibility/platform/ax_platform_node_delegate.h index f17c867..ab273dc8 100644 --- a/ui/accessibility/platform/ax_platform_node_delegate.h +++ b/ui/accessibility/platform/ax_platform_node_delegate.h
@@ -469,21 +469,6 @@ virtual const AXUniqueId& GetUniqueId() const = 0; - // Finds the previous or next offset from the provided offset, that matches - // the provided boundary type. - // - // This method finds text boundaries in the text used for platform text APIs. - // Implementations may use side-channel data such as line or word indices to - // produce appropriate results. It may optionally return no value, indicating - // that the delegate does not have all the information required to calculate - // this value and it is the responsibility of the AXPlatformNode itself to - // to calculate it. - virtual absl::optional<int> FindTextBoundary( - ax::mojom::TextBoundary boundary, - int offset, - ax::mojom::MoveDirection direction, - ax::mojom::TextAffinity affinity) const = 0; - // Return a vector of all the descendants of this delegate's node. This method // is only meaningful for Windows UIA. virtual const std::vector<gfx::NativeViewAccessible>
diff --git a/ui/accessibility/platform/ax_platform_node_delegate_base.cc b/ui/accessibility/platform/ax_platform_node_delegate_base.cc index a97184a..44593eff 100644 --- a/ui/accessibility/platform/ax_platform_node_delegate_base.cc +++ b/ui/accessibility/platform/ax_platform_node_delegate_base.cc
@@ -265,7 +265,6 @@ AXNodePosition::AXPositionInstance AXPlatformNodeDelegateBase::CreatePositionAt( int offset, ax::mojom::TextAffinity affinity) const { - NOTIMPLEMENTED(); return AXNodePosition::CreateNullPosition(); } @@ -273,7 +272,6 @@ AXPlatformNodeDelegateBase::CreateTextPositionAt( int offset, ax::mojom::TextAffinity affinity) const { - NOTIMPLEMENTED(); return AXNodePosition::CreateNullPosition(); } @@ -943,14 +941,6 @@ return *dummy_unique_id; } -absl::optional<int> AXPlatformNodeDelegateBase::FindTextBoundary( - ax::mojom::TextBoundary boundary, - int offset, - ax::mojom::MoveDirection direction, - ax::mojom::TextAffinity affinity) const { - return absl::nullopt; -} - const std::vector<gfx::NativeViewAccessible> AXPlatformNodeDelegateBase::GetUIADirectChildrenInRange( ui::AXPlatformNodeDelegate* start,
diff --git a/ui/accessibility/platform/ax_platform_node_delegate_base.h b/ui/accessibility/platform/ax_platform_node_delegate_base.h index fde3016..f06fbc7 100644 --- a/ui/accessibility/platform/ax_platform_node_delegate_base.h +++ b/ui/accessibility/platform/ax_platform_node_delegate_base.h
@@ -275,12 +275,6 @@ const AXUniqueId& GetUniqueId() const override; - absl::optional<int> FindTextBoundary( - ax::mojom::TextBoundary boundary, - int offset, - ax::mojom::MoveDirection direction, - ax::mojom::TextAffinity affinity) const override; - const std::vector<gfx::NativeViewAccessible> GetUIADirectChildrenInRange( ui::AXPlatformNodeDelegate* start, ui::AXPlatformNodeDelegate* end) override;
diff --git a/ui/accessibility/platform/ax_platform_node_textprovider_win.cc b/ui/accessibility/platform/ax_platform_node_textprovider_win.cc index 5d7e546..b7fd12d 100644 --- a/ui/accessibility/platform/ax_platform_node_textprovider_win.cc +++ b/ui/accessibility/platform/ax_platform_node_textprovider_win.cc
@@ -146,7 +146,7 @@ auto current_line_start = start->Clone(); while (!current_line_start->IsNullPosition() && *current_line_start < *end) { auto current_line_end = current_line_start->CreateNextLineEndPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); if (current_line_end->IsNullPosition() || *current_line_end > *end) current_line_end = end->Clone(); @@ -163,7 +163,7 @@ } current_line_start = current_line_start->CreateNextLineStartPosition( - AXBoundaryBehavior::CrossBoundary); + AXBoundaryBehavior::kCrossBoundary); } base::win::ScopedSafearray scoped_visible_ranges(
diff --git a/ui/accessibility/platform/ax_platform_node_textrangeprovider_win.cc b/ui/accessibility/platform/ax_platform_node_textrangeprovider_win.cc index 77083e5..d58ac42 100644 --- a/ui/accessibility/platform/ax_platform_node_textrangeprovider_win.cc +++ b/ui/accessibility/platform/ax_platform_node_textrangeprovider_win.cc
@@ -219,14 +219,14 @@ // boundary, thus we only need to move the end position. AXPositionInstance end_backup = end()->Clone(); SetEnd(start()->CreateNextCharacterPosition( - AXBoundaryBehavior::CrossBoundary)); + AXBoundaryBehavior::kCrossBoundary)); if (end()->IsNullPosition()) { // The previous could fail if the start is at the end of the last anchor // of the tree, try expanding to the previous character instead. AXPositionInstance start_backup = start()->Clone(); SetStart(start()->CreatePreviousCharacterPosition( - AXBoundaryBehavior::CrossBoundary)); + AXBoundaryBehavior::kCrossBoundary)); if (start()->IsNullPosition()) { // Text representation is empty, undo everything and exit. @@ -235,7 +235,7 @@ return S_OK; } SetEnd(start()->CreateNextCharacterPosition( - AXBoundaryBehavior::CrossBoundary)); + AXBoundaryBehavior::kCrossBoundary)); DCHECK(!end()->IsNullPosition()); } @@ -248,39 +248,31 @@ } case TextUnit_Format: SetStart(start()->CreatePreviousFormatStartPosition( - AXBoundaryBehavior::StopIfAlreadyAtBoundary)); + AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary)); SetEnd(start()->CreateNextFormatEndPosition( - AXBoundaryBehavior::StopIfAlreadyAtBoundary)); + AXBoundaryBehavior::kStopAtLastAnchorBoundary)); break; case TextUnit_Word: { AXPositionInstance start_backup = start()->Clone(); SetStart(start()->CreatePreviousWordStartPosition( - AXBoundaryBehavior::StopIfAlreadyAtBoundary)); - // Since we use AXBoundaryBehavior::StopIfAlreadyAtBoundary, the only case - // possible where CreatePreviousWordStartPosition can return a - // NullPosition is when it's called on a node before the first word - // boundary. This can happen when the document starts with nodes that have - // no word boundaries, like whitespaces and punctuation. When it happens, - // move the position back to the start of the document. - if (start()->IsNullPosition()) - SetStart(start_backup->CreatePositionAtStartOfContent()); + AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary)); // Since start_ is already located at a word boundary, we need to cross it // in order to move to the next one. Because Windows ATs behave // undesirably when the start and end endpoints are not in the same anchor // (for character and word navigation), stop at anchor boundary. SetEnd(start()->CreateNextWordStartPosition( - AXBoundaryBehavior::StopAtAnchorBoundary)); + AXBoundaryBehavior::kStopAtAnchorBoundary)); break; } case TextUnit_Line: SetStart(start()->CreateBoundaryStartPosition( - AXBoundaryBehavior::StopIfAlreadyAtBoundary, + AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary, ax::mojom::MoveDirection::kBackward, base::BindRepeating(&AtStartOfLinePredicate), base::BindRepeating(&AtEndOfLinePredicate))); SetEnd(start()->CreateBoundaryEndPosition( - AXBoundaryBehavior::StopIfAlreadyAtBoundary, + AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary, ax::mojom::MoveDirection::kForward, base::BindRepeating(&AtStartOfLinePredicate), base::BindRepeating(&AtEndOfLinePredicate))); @@ -288,9 +280,9 @@ case TextUnit_Paragraph: SetStart( start()->CreatePreviousParagraphStartPositionSkippingEmptyParagraphs( - AXBoundaryBehavior::StopIfAlreadyAtBoundary)); + AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary)); SetEnd(start()->CreateNextParagraphStartPositionSkippingEmptyParagraphs( - AXBoundaryBehavior::StopAtLastAnchorBoundary)); + AXBoundaryBehavior::kStopAtLastAnchorBoundary)); break; case TextUnit_Page: { // Per UIA spec, if the document containing the current range doesn't @@ -298,9 +290,10 @@ const AXNode* common_anchor = start()->LowestCommonAnchor(*end()); if (common_anchor->tree()->HasPaginationSupport()) { SetStart(start()->CreatePreviousPageStartPosition( - ui::AXBoundaryBehavior::StopIfAlreadyAtBoundary)); + AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary)); SetEnd(start()->CreateNextPageEndPosition( - ui::AXBoundaryBehavior::StopIfAlreadyAtBoundary)); + ui::AXBoundaryBehavior:: + kStopAtAnchorBoundaryOrIfAlreadyAtBoundary)); break; } } @@ -1238,13 +1231,14 @@ do { AXPositionInstance next_endpoint = GetNextTextBoundaryPosition( current_endpoint, boundary_type, - AXBoundaryBehavior::StopAtLastAnchorBoundary, boundary_direction); + AXBoundaryBehavior::kStopAtLastAnchorBoundary, boundary_direction); DCHECK(next_endpoint->IsLeafTextPosition()); - // Since AXBoundaryBehavior::StopAtLastAnchorBoundary forces the next text - // boundary position to be different than the input position, the only - // case where these are equal is when they're already located at the last - // anchor boundary. In such case, there is no next position to move to. + // Since AXBoundaryBehavior::kStopAtLastAnchorBoundary forces the next + // text boundary position to be different than the input position, the + // only case where these are equal is when they're already located at the + // last anchor boundary. In such case, there is no next position to move + // to. if (next_endpoint->GetAnchor() == current_endpoint->GetAnchor() && *next_endpoint == *current_endpoint) { *units_moved = (count > 0) ? iteration : -iteration;
diff --git a/ui/accessibility/platform/ax_platform_node_unittest.cc b/ui/accessibility/platform/ax_platform_node_unittest.cc index a3aaa53..d427e38 100644 --- a/ui/accessibility/platform/ax_platform_node_unittest.cc +++ b/ui/accessibility/platform/ax_platform_node_unittest.cc
@@ -26,36 +26,37 @@ const ui::AXNodeData& node6 /* = ui::AXNodeData() */, const ui::AXNodeData& node7 /* = ui::AXNodeData() */, const ui::AXNodeData& node8 /* = ui::AXNodeData() */, - const ui::AXNodeData& node9 /* = ui::AXNodeData() */, - const ui::AXNodeData& node10 /* = ui::AXNodeData() */, - const ui::AXNodeData& node11 /* = ui::AXNodeData() */, - const ui::AXNodeData& node12 /* = ui::AXNodeData() */) { - static ui::AXNodeData empty_data; - int32_t no_id = empty_data.id; + const ui::AXNodeData& node9 /* = AXNodeData() */, + const ui::AXNodeData& node10 /* = AXNodeData() */, + const ui::AXNodeData& node11 /* = AXNodeData() */, + const ui::AXNodeData& node12 /* = AXNodeData() */) { AXTreeUpdate update; update.root_id = node1.id; + update.has_tree_data = true; + update.tree_data.tree_id = AXTreeID::CreateNewAXTreeID(); + update.tree_data.title = "Dialog title"; update.nodes.push_back(node1); - if (node2.id != no_id) + if (node2.id != kInvalidAXNodeID) update.nodes.push_back(node2); - if (node3.id != no_id) + if (node3.id != kInvalidAXNodeID) update.nodes.push_back(node3); - if (node4.id != no_id) + if (node4.id != kInvalidAXNodeID) update.nodes.push_back(node4); - if (node5.id != no_id) + if (node5.id != kInvalidAXNodeID) update.nodes.push_back(node5); - if (node6.id != no_id) + if (node6.id != kInvalidAXNodeID) update.nodes.push_back(node6); - if (node7.id != no_id) + if (node7.id != kInvalidAXNodeID) update.nodes.push_back(node7); - if (node8.id != no_id) + if (node8.id != kInvalidAXNodeID) update.nodes.push_back(node8); - if (node9.id != no_id) + if (node9.id != kInvalidAXNodeID) update.nodes.push_back(node9); - if (node10.id != no_id) + if (node10.id != kInvalidAXNodeID) update.nodes.push_back(node10); - if (node11.id != no_id) + if (node11.id != kInvalidAXNodeID) update.nodes.push_back(node11); - if (node12.id != no_id) + if (node12.id != kInvalidAXNodeID) update.nodes.push_back(node12); Init(update); }
diff --git a/ui/accessibility/platform/ax_platform_node_unittest.h b/ui/accessibility/platform/ax_platform_node_unittest.h index b71bc5a..298c6c4 100644 --- a/ui/accessibility/platform/ax_platform_node_unittest.h +++ b/ui/accessibility/platform/ax_platform_node_unittest.h
@@ -27,18 +27,18 @@ void Init(const AXTreeUpdate& initial_state); // Convenience functions to initialize directly from a few AXNodeData objects. - void Init(const ui::AXNodeData& node1, - const ui::AXNodeData& node2 = ui::AXNodeData(), - const ui::AXNodeData& node3 = ui::AXNodeData(), - const ui::AXNodeData& node4 = ui::AXNodeData(), - const ui::AXNodeData& node5 = ui::AXNodeData(), - const ui::AXNodeData& node6 = ui::AXNodeData(), - const ui::AXNodeData& node7 = ui::AXNodeData(), - const ui::AXNodeData& node8 = ui::AXNodeData(), - const ui::AXNodeData& node9 = ui::AXNodeData(), - const ui::AXNodeData& node10 = ui::AXNodeData(), - const ui::AXNodeData& node11 = ui::AXNodeData(), - const ui::AXNodeData& node12 = ui::AXNodeData()); + void Init(const AXNodeData& node1, + const AXNodeData& node2 = AXNodeData(), + const AXNodeData& node3 = AXNodeData(), + const AXNodeData& node4 = AXNodeData(), + const AXNodeData& node5 = AXNodeData(), + const AXNodeData& node6 = AXNodeData(), + const AXNodeData& node7 = AXNodeData(), + const AXNodeData& node8 = AXNodeData(), + const AXNodeData& node9 = AXNodeData(), + const AXNodeData& node10 = AXNodeData(), + const AXNodeData& node11 = AXNodeData(), + const AXNodeData& node12 = AXNodeData()); AXTreeUpdate BuildTextField(); AXTreeUpdate BuildTextFieldWithSelectionRange(int32_t start, int32_t stop);
diff --git a/ui/accessibility/platform/ax_platform_node_win.cc b/ui/accessibility/platform/ax_platform_node_win.cc index 4a51f2f..ddf9a441 100644 --- a/ui/accessibility/platform/ax_platform_node_win.cc +++ b/ui/accessibility/platform/ax_platform_node_win.cc
@@ -3606,12 +3606,6 @@ AXPlatformNode::NotifyAddAXModeFlags(kScreenReaderAndHTMLAccessibilityModes | AXMode::kInlineTextBoxes); - // https://accessibility.linuxfoundation.org/a11yspecs/ia2/docs/html/_accessible_text_8idl.html - // IA2_TEXT_BOUNDARY_SENTENCE is optional and we can let the screenreader - // handle it, the rest of the boundary types must be supported. - if (boundary_type == IA2_TEXT_BOUNDARY_SENTENCE) - return S_FALSE; - HandleSpecialTextOffset(&offset); if (offset < 0) return E_INVALIDARG; @@ -3634,7 +3628,6 @@ } LONG start, end; - switch (text_offset_type) { case TextOffsetType::kAtOffset: { end = FindBoundary(boundary_type, offset,
diff --git a/ui/file_manager/file_manager/foreground/css/file_manager.css b/ui/file_manager/file_manager/foreground/css/file_manager.css index eabd7a0..fc0349d 100644 --- a/ui/file_manager/file_manager/foreground/css/file_manager.css +++ b/ui/file_manager/file_manager/foreground/css/file_manager.css
@@ -2410,19 +2410,36 @@ margin-inline-start: 16px; } -body.files-ng .table-header-label .sort-icon { - --cr-icon-button-icon-size: 16px; - --cr-icon-button-size: 32px; +.table-header-label .sort-icon { --cr-icon-button-fill-color: var(--cros-icon-color-secondary); + --cr-icon-button-icon-size: 16px; + --cr-icon-button-hover-background-color: var(--cros-ripple-color); + --cr-icon-button-size: 32px; border-radius: 50%; cursor: default; } -html:not(.pointer-active) body.files-ng .table-header-label .sort-icon, -body.files-ng .table-header-label .sort-icon:active { +.table-header-label .sort-icon:active { + /* TODO(crbug.com/1275388): use blend() instead */ + background: linear-gradient(var(--cros-ripple-color), + var(--cros-ripple-color)), + var(--cros-ripple-color); +} + +html:not(.pointer-active) .table-header-label .sort-icon:hover, +.table-header-label .sort-icon:active { cursor: pointer; } +.table-header-label .not-sorted .sort-icon { + display: none; +} + +html.pointer-active .table-header-label .sort-icon:not(:active):hover, +html.col-resize .table-header-label .sort-icon:not(:active):hover { + --cr-icon-button-hover-background-color: none; +} + body.files-ng .table-label-container { align-items: center; display: flex; @@ -2436,15 +2453,6 @@ text-overflow: ellipsis; } -html:not(.pointer-active) body.files-ng .table-header-label .sorted -.sort-icon:hover { - background-color: rgba(0, 0, 0, 4%); -} - -body.files-ng .table-header-label .not-sorted .sort-icon { - display: none; -} - /** Size column is aligned to right. */ body.files-ng .table-header-label.size .table-label-container { justify-content: flex-end; @@ -2483,24 +2491,36 @@ width: auto; } -body.files-ng .table-header-splitter .splitter-icon { - --cr-icon-button-icon-size: 32px; - --cr-icon-button-size: 32px; - --cr-icon-button-margin-start: 0px; +.table-header-splitter .splitter-icon { /* * TODO(crbug.com/1268206): the icon is not in disabled state, a new css * variable needs to be created and applied here. */ --cr-icon-button-fill-color: var(--cros-icon-color-disabled); + --cr-icon-button-hover-background-color: var(--cros-ripple-color); + --cr-icon-button-icon-size: 32px; + --cr-icon-button-margin-start: 0px; + --cr-icon-button-size: 32px; cursor: default; height: 32px; } -html:not(.pointer-active) body.files-ng .table-header-splitter -.splitter-icon:hover { +.table-header-splitter .splitter-icon:active { + /* TODO(crbug.com/1275388): use blend() instead */ + background: linear-gradient(var(--cros-ripple-color), + var(--cros-ripple-color)), + var(--cros-ripple-color); +} + +html:not(.pointer-active) .table-header-splitter .splitter-icon:hover { cursor: col-resize; } +html.pointer-active .table-header-splitter .splitter-icon:not(:active):hover, +html.col-resize .table-header-splitter .splitter-icon:not(:active):hover { + --cr-icon-button-hover-background-color: none; +} + body.files-ng #list-container .table-row-cell .size { padding-inline-end: 5px; padding-inline-start: 22px;
diff --git a/ui/file_manager/file_manager/foreground/elements/files_metadata_box.html b/ui/file_manager/file_manager/foreground/elements/files_metadata_box.html index 388bb46d..8477580 100644 --- a/ui/file_manager/file_manager/foreground/elements/files_metadata_box.html +++ b/ui/file_manager/file_manager/foreground/elements/files_metadata_box.html
@@ -25,16 +25,8 @@ padding-top: 16px; } - :host(:not([files-ng])) .category { - color: #E6E6E6; - font-size: 108%; - font-weight: 500; - margin: 12px 32px; - vertical-align: bottom; - } - - :host([files-ng]) .category { - color: var(--google-grey-200); + .category { + color: var(--cros-text-color-primary); font-family: 'Roboto Medium'; font-size: 14px; line-height: 56px; @@ -42,13 +34,7 @@ } hr { - border-color: white; - margin-top: 31px; - opacity: 0.24; - } - - :host([files-ng]) hr { - border-color: rgba(255, 255, 255, 0.2); + border-color: var(--cros-separator-color); border-top-width: 1px !important; border-width: 0; margin-bottom: 8px;
diff --git a/ui/file_manager/file_manager/foreground/elements/files_metadata_entry.html b/ui/file_manager/file_manager/foreground/elements/files_metadata_entry.html index 6448234..f69f57e 100644 --- a/ui/file_manager/file_manager/foreground/elements/files_metadata_entry.html +++ b/ui/file_manager/file_manager/foreground/elements/files_metadata_entry.html
@@ -24,7 +24,7 @@ } #key { - color: white; + color: var(--cros-text-color-primary); font-weight: 500; margin-inline-end: 16px; margin-inline-start: 32px; @@ -34,7 +34,7 @@ } #value { - color: white; + color: var(--cros-text-color-primary); opacity: 0.8; overflow-wrap: break-word; vertical-align: bottom;
diff --git a/ui/file_manager/file_manager/foreground/elements/files_quick_view.css b/ui/file_manager/file_manager/foreground/elements/files_quick_view.css index cf369ba..a15f647 100644 --- a/ui/file_manager/file_manager/foreground/elements/files_quick_view.css +++ b/ui/file_manager/file_manager/foreground/elements/files_quick_view.css
@@ -63,14 +63,12 @@ margin: 0; padding: 0; position: absolute; - top: 48px; + /* The top value here should be the total height of the #toolbar including + the height and the border-width */ + top: 57px; width: 100%; } -:host([files-ng]) #mainPanel { - top: 56px; -} - #contentPanel { background-color: transparent; display: flex; @@ -89,8 +87,8 @@ } #innerContentPanel { - background-color: transparent; - color: white; + background-color: var(--cros-app-shield-80); + color: var(--cros-text-color-primary); display: flex; flex-direction: column; height: calc(100% - 64px); @@ -114,28 +112,22 @@ } .text-content { - background-color: white; + background-color: var(--cros-bg-color); } #toolbar { align-items: center; - background-color: rgb(40, 42, 45); - color: white; + background-color: var(--cros-bg-color); + border-bottom: 1px solid var(--cros-separator-color); + color: var(--cros-text-color-primary); display: flex; - font-size: 108%; - height: 48px; - margin: 0; - opacity: 0.9; - padding: 0; - z-index: 1; -} - -:host([files-ng]) #toolbar { - color: var(--google-grey-200); font-family: 'Roboto Medium'; font-size: 14px; height: 56px; + margin: 0; opacity: 1; + padding: 0; + z-index: 1; } #file-path { @@ -153,10 +145,11 @@ display: flex; } -:host([files-ng]) cr-button { - --hover-bg-color: rgba(255, 255, 255, 4%); - --ink-color: rgba(255, 255, 255, 88%); - --text-color: currentColor; +cr-button { + --hover-bg-color:var(--cros-ripple-color); + --ink-color: var(--cros-ripple-color); + --ripple-opacity: 100%; + --text-color: var(--cros-icon-color-primary); border: 2px solid transparent; border-radius: 50%; box-shadow: none; @@ -210,53 +203,34 @@ } #delete-button > .icon { - -webkit-mask-image: url(../images/files/ui/delete.svg); - background-color: currentColor; + -webkit-mask-image: url(../images/files/ui/delete_ng.svg); height: 16px; width: 16px; } -:host([files-ng]) #delete-button > .icon { - -webkit-mask-image: url(../images/files/ui/delete_ng.svg); +#info-button[aria-pressed=true] { + background-color: var(--cros-ripple-color); } -#metadata-button { - --files-toggle-ripple-bg-color: white; - --files-toggle-ripple-activated-opacity: 0.3; -} - -:host([files-ng]) #metadata-button { - display: none; -} - -:host([files-ng]) #info-button[aria-pressed=true] { - background-color: rgba(255, 255, 255, 12%); -} - -:host([files-ng]) #info-button > .icon { +#info-button > .icon { -webkit-mask-image: url(../images/files/ui/info.svg); } :host-context(html.focus-outline-visible.files-ng) cr-button:not(:active):focus { - border: 2px solid var(--google-blue-300); + border: 2px solid var(--cros-focus-ring-color); } #metadata-box { - background-color: rgba(20, 22, 24, 80%); + background-color: var(--cros-bg-color-dropped-elevation-1); bottom: 0; height: 100%; margin: 0; - opacity: 0.8; + opacity: 1; overflow-y: auto; position: absolute; right: 0; } -:host([files-ng]) #metadata-box { - background-color: var(--google-grey-900); - opacity: 1; -} - :host-context(html[dir='rtl']) #metadata-box { left: 0; right: auto;
diff --git a/ui/file_manager/file_manager/foreground/elements/files_quick_view.html b/ui/file_manager/file_manager/foreground/elements/files_quick_view.html index da628cb..5c7240c 100644 --- a/ui/file_manager/file_manager/foreground/elements/files_quick_view.html +++ b/ui/file_manager/file_manager/foreground/elements/files_quick_view.html
@@ -23,9 +23,6 @@ <cr-button id="delete-button" on-click="onDeleteButtonTap" hidden$="[[!shouldShowDeleteButton_(canDelete, isModal)]]" aria-label="$i18n{QUICK_VIEW_DELETE_BUTTON_LABEL}" has-tooltip invert-tooltip> <span class="icon"></span> </cr-button> - <!-- TODO(files-ng): this fancy Polymer icon toggle button is not used in files-ng. Remove it. --> - <files-icon-button toggles id="metadata-button" on-tap="onMetadataButtonTap_" active="{{metadataBoxActive}}" aria-label="$i18n{QUICK_VIEW_TOGGLE_METADATA_BOX_BUTTON_LABEL}" tabindex="0" has-tooltip invert-tooltip> - </files-icon-button> <cr-button id="info-button" on-click="onMetadataButtonTap_" aria-pressed="{{metadataBoxActive}}" aria-label="$i18n{QUICK_VIEW_TOGGLE_METADATA_BOX_BUTTON_LABEL}" has-tooltip invert-tooltip> <span class="icon"></span> </cr-button>
diff --git a/ui/file_manager/file_manager/foreground/elements/files_tooltip.html b/ui/file_manager/file_manager/foreground/elements/files_tooltip.html index 67e29a28..abbb9c7 100644 --- a/ui/file_manager/file_manager/foreground/elements/files_tooltip.html +++ b/ui/file_manager/file_manager/foreground/elements/files_tooltip.html
@@ -28,11 +28,6 @@ white-space: nowrap; } - #label[invert] { - background-color: rgba(255 255 255 / 0.8); - color: var(--google-grey-900); - } - :host(.card-tooltip) { background-color: var(--cros-bg-color-elevation-1); border-radius: 8px;
diff --git a/ui/views/widget/widget_interactive_uitest.cc b/ui/views/widget/widget_interactive_uitest.cc index 11b3d36..d07418d 100644 --- a/ui/views/widget/widget_interactive_uitest.cc +++ b/ui/views/widget/widget_interactive_uitest.cc
@@ -289,15 +289,14 @@ base::RepeatingTimer timer_; }; -// TODO(pbos): Rewrite this to return unique_ptr. -views::Textfield* CreateTextfield() { +std::unique_ptr<Textfield> CreateTextfield() { auto textfield = std::make_unique<Textfield>(); // TODO(crbug.com/1218186): Remove this, this is in place temporarily to be // able to submit accessibility checks, but this focusable View needs to // add a name so that the screen reader knows what to announce. Consider // adding bogus placeholder text here. textfield->SetProperty(views::kSkipAccessibilityPaintChecks, true); - return textfield.release(); + return textfield; } } // namespace @@ -933,26 +932,27 @@ WidgetAutoclosePtr widget(CreateTopLevelNativeWidget()); widget->SetBounds(gfx::Rect(0, 0, 200, 200)); - Textfield* textfield = CreateTextfield(); - textfield->SetBounds(0, 0, 200, 20); - textfield->SetText(u"some text"); - widget->GetRootView()->AddChildView(textfield); + std::unique_ptr<Textfield> textfield = CreateTextfield(); + auto* const textfield_ptr = textfield.get(); + textfield_ptr->SetBounds(0, 0, 200, 20); + textfield_ptr->SetText(u"some text"); + widget->GetRootView()->AddChildView(std::move(textfield)); widget->Show(); - textfield->RequestFocus(); - textfield->SelectAll(true); - TextfieldTestApi textfield_test_api(textfield); + textfield_ptr->RequestFocus(); + textfield_ptr->SelectAll(true); + TextfieldTestApi textfield_test_api(textfield_ptr); RunPendingMessages(); ui::test::EventGenerator generator(GetRootWindow(widget.get())); - generator.GestureTapAt(textfield->GetBoundsInScreen().origin() + + generator.GestureTapAt(textfield_ptr->GetBoundsInScreen().origin() + gfx::Vector2d(10, 10)); static_cast<TouchSelectionControllerImpl*>( textfield_test_api.touch_selection_controller()) ->ShowQuickMenuImmediatelyForTesting(); - EXPECT_TRUE(textfield->HasFocus()); + EXPECT_TRUE(textfield_ptr->HasFocus()); EXPECT_TRUE(widget->IsActive()); EXPECT_TRUE(ui::TouchSelectionMenuRunner::GetInstance()->IsRunning()); } @@ -1283,12 +1283,13 @@ // Create a top level desktop native widget. WidgetAutoclosePtr top_level(CreateTopLevelNativeWidget()); - Textfield* textfield = CreateTextfield(); - textfield->SetBounds(0, 0, 200, 20); - top_level->GetRootView()->AddChildView(textfield); + std::unique_ptr<Textfield> textfield = CreateTextfield(); + auto* const textfield_ptr = textfield.get(); + textfield_ptr->SetBounds(0, 0, 200, 20); + top_level->GetRootView()->AddChildView(std::move(textfield)); ShowSync(top_level.get()); - textfield->RequestFocus(); - EXPECT_TRUE(textfield->HasFocus()); + textfield_ptr->RequestFocus(); + EXPECT_TRUE(textfield_ptr->HasFocus()); // Create a modal dialog. // This instance will be destroyed when the dialog is destroyed. @@ -1297,25 +1298,26 @@ Widget* modal_dialog_widget = DialogDelegate::CreateDialogWidget( dialog_delegate.release(), nullptr, top_level->GetNativeView()); modal_dialog_widget->SetBounds(gfx::Rect(0, 0, 100, 10)); - Textfield* dialog_textfield = CreateTextfield(); - dialog_textfield->SetBounds(0, 0, 50, 5); - modal_dialog_widget->GetRootView()->AddChildView(dialog_textfield); + std::unique_ptr<Textfield> dialog_textfield = CreateTextfield(); + auto* const dialog_textfield_ptr = dialog_textfield.get(); + dialog_textfield_ptr->SetBounds(0, 0, 50, 5); + modal_dialog_widget->GetRootView()->AddChildView(std::move(dialog_textfield)); // Dialog widget doesn't need a ShowSync as it gains active status // synchronously. modal_dialog_widget->Show(); - dialog_textfield->RequestFocus(); - EXPECT_TRUE(dialog_textfield->HasFocus()); - EXPECT_FALSE(textfield->HasFocus()); + dialog_textfield_ptr->RequestFocus(); + EXPECT_TRUE(dialog_textfield_ptr->HasFocus()); + EXPECT_FALSE(textfield_ptr->HasFocus()); DeactivateSync(top_level.get()); - EXPECT_FALSE(dialog_textfield->HasFocus()); - EXPECT_FALSE(textfield->HasFocus()); + EXPECT_FALSE(dialog_textfield_ptr->HasFocus()); + EXPECT_FALSE(textfield_ptr->HasFocus()); // After deactivation and activation of top level widget, only modal dialog // should restore focused view. ActivateSync(top_level.get()); - EXPECT_TRUE(dialog_textfield->HasFocus()); - EXPECT_FALSE(textfield->HasFocus()); + EXPECT_TRUE(dialog_textfield_ptr->HasFocus()); + EXPECT_FALSE(textfield_ptr->HasFocus()); } #endif // (defined(OS_LINUX) || defined(OS_CHROMEOS)) && // BUILDFLAG(ENABLE_DESKTOP_AURA) @@ -1977,9 +1979,10 @@ // Test input method focus changes affected by top window activaction. TEST_F(WidgetInputMethodInteractiveTest, MAYBE_Activation) { WidgetAutoclosePtr widget(CreateTopLevelNativeWidget()); - Textfield* textfield = CreateTextfield(); - widget->GetRootView()->AddChildView(textfield); - textfield->RequestFocus(); + std::unique_ptr<Textfield> textfield = CreateTextfield(); + auto* const textfield_ptr = textfield.get(); + widget->GetRootView()->AddChildView(std::move(textfield)); + textfield_ptr->RequestFocus(); ShowSync(widget.get()); @@ -1995,19 +1998,21 @@ // Test input method focus changes affected by focus changes within 1 window. TEST_F(WidgetInputMethodInteractiveTest, OneWindow) { WidgetAutoclosePtr widget(CreateTopLevelNativeWidget()); - Textfield* textfield1 = CreateTextfield(); - Textfield* textfield2 = CreateTextfield(); - textfield2->SetTextInputType(ui::TEXT_INPUT_TYPE_PASSWORD); - widget->GetRootView()->AddChildView(textfield1); - widget->GetRootView()->AddChildView(textfield2); + std::unique_ptr<Textfield> textfield1 = CreateTextfield(); + auto* const textfield1_ptr = textfield1.get(); + std::unique_ptr<Textfield> textfield2 = CreateTextfield(); + auto* const textfield2_ptr = textfield2.get(); + textfield2_ptr->SetTextInputType(ui::TEXT_INPUT_TYPE_PASSWORD); + widget->GetRootView()->AddChildView(std::move(textfield1)); + widget->GetRootView()->AddChildView(std::move(textfield2)); ShowSync(widget.get()); - textfield1->RequestFocus(); + textfield1_ptr->RequestFocus(); EXPECT_EQ(ui::TEXT_INPUT_TYPE_TEXT, widget->GetInputMethod()->GetTextInputType()); - textfield2->RequestFocus(); + textfield2_ptr->RequestFocus(); EXPECT_EQ(ui::TEXT_INPUT_TYPE_PASSWORD, widget->GetInputMethod()->GetTextInputType()); @@ -2024,7 +2029,7 @@ widget->GetInputMethod()->GetTextInputType()); DeactivateSync(widget.get()); - textfield1->RequestFocus(); + textfield1_ptr->RequestFocus(); ActivateSync(widget.get()); EXPECT_TRUE(widget->IsActive()); EXPECT_EQ(ui::TEXT_INPUT_TYPE_TEXT, @@ -2042,20 +2047,22 @@ child->SetBounds(gfx::Rect(0, 0, 50, 50)); child->Show(); - Textfield* textfield_parent = CreateTextfield(); - Textfield* textfield_child = CreateTextfield(); + std::unique_ptr<Textfield> textfield_parent = CreateTextfield(); + auto* const textfield_parent_ptr = textfield_parent.get(); + std::unique_ptr<Textfield> textfield_child = CreateTextfield(); + auto* const textfield_child_ptr = textfield_child.get(); textfield_parent->SetTextInputType(ui::TEXT_INPUT_TYPE_PASSWORD); - parent->GetRootView()->AddChildView(textfield_parent); - child->GetRootView()->AddChildView(textfield_child); + parent->GetRootView()->AddChildView(std::move(textfield_parent)); + child->GetRootView()->AddChildView(std::move(textfield_child)); ShowSync(parent.get()); EXPECT_EQ(parent->GetInputMethod(), child->GetInputMethod()); - textfield_parent->RequestFocus(); + textfield_parent_ptr->RequestFocus(); EXPECT_EQ(ui::TEXT_INPUT_TYPE_PASSWORD, parent->GetInputMethod()->GetTextInputType()); - textfield_child->RequestFocus(); + textfield_child_ptr->RequestFocus(); EXPECT_EQ(ui::TEXT_INPUT_TYPE_TEXT, parent->GetInputMethod()->GetTextInputType()); @@ -2071,7 +2078,7 @@ EXPECT_EQ(ui::TEXT_INPUT_TYPE_TEXT, parent->GetInputMethod()->GetTextInputType()); - textfield_parent->RequestFocus(); + textfield_parent_ptr->RequestFocus(); DeactivateSync(parent.get()); EXPECT_EQ(ui::TEXT_INPUT_TYPE_NONE, parent->GetInputMethod()->GetTextInputType()); @@ -2085,25 +2092,26 @@ // Test input method focus changes affected by textfield's state changes. TEST_F(WidgetInputMethodInteractiveTest, TextField) { WidgetAutoclosePtr widget(CreateTopLevelNativeWidget()); - Textfield* textfield = CreateTextfield(); - widget->GetRootView()->AddChildView(textfield); + std::unique_ptr<Textfield> textfield = CreateTextfield(); + auto* const textfield_ptr = textfield.get(); + widget->GetRootView()->AddChildView(std::move(textfield)); ShowSync(widget.get()); EXPECT_EQ(ui::TEXT_INPUT_TYPE_NONE, widget->GetInputMethod()->GetTextInputType()); - textfield->SetTextInputType(ui::TEXT_INPUT_TYPE_PASSWORD); + textfield_ptr->SetTextInputType(ui::TEXT_INPUT_TYPE_PASSWORD); EXPECT_EQ(ui::TEXT_INPUT_TYPE_NONE, widget->GetInputMethod()->GetTextInputType()); - textfield->RequestFocus(); + textfield_ptr->RequestFocus(); EXPECT_EQ(ui::TEXT_INPUT_TYPE_PASSWORD, widget->GetInputMethod()->GetTextInputType()); - textfield->SetTextInputType(ui::TEXT_INPUT_TYPE_TEXT); + textfield_ptr->SetTextInputType(ui::TEXT_INPUT_TYPE_TEXT); EXPECT_EQ(ui::TEXT_INPUT_TYPE_TEXT, widget->GetInputMethod()->GetTextInputType()); - textfield->SetReadOnly(true); + textfield_ptr->SetReadOnly(true); EXPECT_EQ(ui::TEXT_INPUT_TYPE_NONE, widget->GetInputMethod()->GetTextInputType()); } @@ -2111,21 +2119,22 @@ // Test input method should not work for accelerator. TEST_F(WidgetInputMethodInteractiveTest, AcceleratorInTextfield) { WidgetAutoclosePtr widget(CreateTopLevelNativeWidget()); - Textfield* textfield = CreateTextfield(); - widget->GetRootView()->AddChildView(textfield); + std::unique_ptr<Textfield> textfield = CreateTextfield(); + auto* const textfield_ptr = textfield.get(); + widget->GetRootView()->AddChildView(std::move(textfield)); ShowSync(widget.get()); - textfield->SetTextInputType(ui::TEXT_INPUT_TYPE_TEXT); - textfield->RequestFocus(); + textfield_ptr->SetTextInputType(ui::TEXT_INPUT_TYPE_TEXT); + textfield_ptr->RequestFocus(); ui::KeyEvent key_event(ui::ET_KEY_PRESSED, ui::VKEY_F, ui::EF_ALT_DOWN); ui::Accelerator accelerator(key_event); widget->GetFocusManager()->RegisterAccelerator( - accelerator, ui::AcceleratorManager::kNormalPriority, textfield); + accelerator, ui::AcceleratorManager::kNormalPriority, textfield_ptr); widget->OnKeyEvent(&key_event); EXPECT_TRUE(key_event.stopped_propagation()); - widget->GetFocusManager()->UnregisterAccelerators(textfield); + widget->GetFocusManager()->UnregisterAccelerators(textfield_ptr); ui::KeyEvent key_event2(key_event); widget->OnKeyEvent(&key_event2);
diff --git a/ui/webui/resources/mojo/BUILD.gn b/ui/webui/resources/mojo/BUILD.gn index 8df01f26..d5e4a25 100644 --- a/ui/webui/resources/mojo/BUILD.gn +++ b/ui/webui/resources/mojo/BUILD.gn
@@ -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("//build/config/chromeos/ui_mode.gni") import("//tools/typescript/ts_library.gni") preprocessed_folder = "$root_gen_dir/ui/webui/resources/preprocessed/mojo" @@ -14,9 +15,10 @@ in_files = [ "mojo/public/mojom/base/big_buffer.mojom-webui.js", + "mojo/public/mojom/base/file_path.mojom-webui.js", "mojo/public/mojom/base/string16.mojom-webui.js", "mojo/public/mojom/base/time.mojom-webui.js", - "mojo/public/mojom/base/file_path.mojom-webui.js", + "mojo/public/mojom/base/token.mojom-webui.js", "skia/public/mojom/skcolor.mojom-webui.js", "ui/base/mojom/window_open_disposition.mojom-webui.js", "url/mojom/url.mojom-webui.js", @@ -31,6 +33,11 @@ "//ui/base/mojom:mojom_js__generator", "//url/mojom:url_mojom_gurl_js__generator", ] + + if (is_chromeos_ash) { + in_files += [ "ui/gfx/geometry/mojom/geometry.mojom-webui.js" ] + extra_deps += [ "//ui/gfx/geometry/mojom:mojom_js__generator" ] + } } # Copy bindings.d.ts file under the same location where other shared Mojo JS